mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-23 05:25:50 +03:00
commit
040507fce6
220 changed files with 5584 additions and 7260 deletions
|
@ -81,7 +81,7 @@ pipeline:
|
|||
TESTS: qt-5.9
|
||||
|
||||
AppImage-5.9:
|
||||
image: nextcloudci/client-appimage-ci:client-appimage-ci-6
|
||||
image: nextcloudci/client-appimage-ci:client-appimage-ci-8
|
||||
commands:
|
||||
- /bin/bash -c "./admin/linux/build-appimage.sh"
|
||||
when:
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(VERSION 2.8.0)
|
||||
if(POLICY CMP0020)
|
||||
cmake_policy(SET CMP0020 NEW)
|
||||
endif()
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
|
||||
project(client)
|
||||
|
||||
|
@ -61,14 +57,6 @@ if(NOT WITH_CRASHREPORTER)
|
|||
message(STATUS "Build of crashreporter disabled.")
|
||||
endif()
|
||||
|
||||
#####
|
||||
## handle DBUS for Fdo notifications
|
||||
if( UNIX AND NOT APPLE )
|
||||
add_definitions( -DUSE_FDO_NOTIFICATIONS)
|
||||
set(WITH_DBUS ON)
|
||||
endif()
|
||||
####
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(DefineInstallationPaths)
|
||||
include(GenerateExportHeader)
|
||||
|
@ -77,6 +65,12 @@ include(GetGitRevisionDescription)
|
|||
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
|
||||
add_definitions(
|
||||
-DQT_USE_QSTRINGBUILDER
|
||||
-DQT_MESSAGELOGCONTEXT #enable function name and line number in debug output
|
||||
-DQT_DEPRECATED_WARNINGS
|
||||
)
|
||||
|
||||
# if we cannot get it from git, directly try .tag (packages)
|
||||
# this will work if the tar balls have been properly created
|
||||
# via git-archive.
|
||||
|
@ -190,15 +184,6 @@ if(BUILD_CLIENT)
|
|||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
# On some OS, we want to use our own, not the system sqlite
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
include_directories(BEFORE ${SQLITE3_INCLUDE_DIR})
|
||||
if (WIN32)
|
||||
add_definitions(-DSQLITE_API=__declspec\(dllimport\))
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(GLib2)
|
||||
find_package(Gio)
|
||||
|
@ -221,20 +206,6 @@ add_definitions( -D_WIN32_WINNT=0x0600)
|
|||
add_definitions( -DWINVER=0x0600)
|
||||
endif( WIN32 )
|
||||
|
||||
include(QtVersionAbstraction)
|
||||
setup_qt()
|
||||
if (${Qt5Core_VERSION_MAJOR} EQUAL "5")
|
||||
if (${Qt5Core_VERSION_MINOR} EQUAL "6" OR ${Qt5Core_VERSION_MINOR} GREATER 6)
|
||||
else()
|
||||
message(STATUS "If possible compile me with Qt 5.6 or higher.")
|
||||
endif()
|
||||
if (${Qt5Core_VERSION_MINOR} EQUAL "9" OR ${Qt5Core_VERSION_MINOR} GREATER 9)
|
||||
else()
|
||||
message(STATUS "For HTTP2 use Qt 5.9.2 or higher.")
|
||||
endif()
|
||||
endif()
|
||||
message("Qt ${Qt5Core_VERSION} at ${Qt5Core_INCLUDE_DIRS}")
|
||||
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
|
|
|
@ -2,7 +2,7 @@ ChangeLog
|
|||
=========
|
||||
|
||||
version 2.4.0 (2017-12-XX)
|
||||
* If you're using 2.4.0 alpha1, please upgrade as the alpha1 had an issue with hidden files!
|
||||
* If you're using 2.4.0 alpha1, please upgrade as previous alphas/rcs had an issue with hidden files and renames!
|
||||
* OAuth2 authentication support by opening external browser (#5668)
|
||||
* Shibboleth: Change to use OAuth2 if supported (#6198)
|
||||
* Sharing: Add support for multiple public link shares (#5655)
|
||||
|
@ -66,7 +66,7 @@ version 2.4.0 (2017-12-XX)
|
|||
* Sync: Upload conflict files if OWNCLOUD_UPLOAD_CONFLICT_FILES environment variable is set (#6038)
|
||||
* Sync: Blacklist: Don't let errors become warnings (#5516)
|
||||
* Sync: Check etag again after active sync (#4116)
|
||||
* Sync: Rename handling fixes: duplicate file ids (#6096)
|
||||
* Sync: Rename handling fixes: duplicate file ids (#6096, #6212)
|
||||
* Sync: Rename handling fixes: File size must be equal
|
||||
* Sync: Rename handling: Fix duplicate files on abort/resume sync (#5949)
|
||||
* Sync: Add capability for invalid filename regexes (#6092)
|
||||
|
@ -80,6 +80,7 @@ version 2.4.0 (2017-12-XX)
|
|||
* Crash fixes
|
||||
* Test improvements
|
||||
* Small UI layout fixes
|
||||
* Performance improvements
|
||||
* Maintenance Mode: Detect maintenance mode (#4485)
|
||||
* Maintenance Mode: Add a 1 to 5 min reconnection delay (#5872)
|
||||
* HTTP: Send a unique X-Request-ID with each request (#5853)
|
||||
|
@ -95,6 +96,7 @@ version 2.4.0 (2017-12-XX)
|
|||
* Compilation: Remove Qt 4 code (#6025, #5702, #5505)
|
||||
* Harmonize source code style with clang-format (#5732)
|
||||
* Switch over to Qt 5 function pointer signal/slot syntax (#6041)
|
||||
* Compile with stack-smashing protection
|
||||
* Updater: Rudimentary support for beta channel (#6048)
|
||||
|
||||
version 2.3.4 (2017-11-02)
|
||||
|
@ -110,6 +112,7 @@ version 2.3.3 (2017-08-29)
|
|||
* Overlay Icons: Fix potential hangs on Windows
|
||||
* SyncJournalDB: Don't use ._ as filename pattern if that does not work because of SMB storage settings (#5844)
|
||||
* SyncJournalDB: Log reason for sqlite3 opening errors
|
||||
* Notifications: Proapgate "Dismiss" button action to server (#5922)
|
||||
* Switch Linux build also to Qt 5.6.2 (#5470)
|
||||
* Stopped maintaining Qt 4 buildability
|
||||
|
||||
|
|
22
admin/linux/Dockerfile
Normal file
22
admin/linux/Dockerfile
Normal file
|
@ -0,0 +1,22 @@
|
|||
# This DockerFile is used to create the image used for Jenkins, the CI system (see Jenkinsfile)
|
||||
# It is not meant to be used to create the production packages.
|
||||
|
||||
# Distro with Qt 5.6
|
||||
FROM ubuntu:yakkety
|
||||
|
||||
RUN apt-get update -q && DEBIAN_FRONTEND=noninteractive apt-get install -q -y --no-install-recommends \
|
||||
locales \
|
||||
build-essential \
|
||||
clang \
|
||||
ninja-build \
|
||||
cmake \
|
||||
extra-cmake-modules \
|
||||
libsqlite3-dev \
|
||||
libssl-dev \
|
||||
libcmocka-dev \
|
||||
qt5-default \
|
||||
qttools5-dev-tools \
|
||||
libqt5webkit5-dev \
|
||||
qt5keychain-dev \
|
||||
kio-dev \
|
||||
&& apt-get clean
|
|
@ -27,8 +27,8 @@ SET(QT_MKSPECS_DIR ${CMAKE_FIND_ROOT_PATH}/share/qt5/mkspecs)
|
|||
SET(QT_QT_INCLUDE_DIR ${CMAKE_FIND_ROOT_PATH}/include)
|
||||
|
||||
# qt tools
|
||||
SET(QT_QMAKE_EXECUTABLE ${MINGW_PREFIX}-qmake )
|
||||
SET(QT_MOC_EXECUTABLE ${MINGW_PREFIX}-moc)
|
||||
SET(QT_RCC_EXECUTABLE ${MINGW_PREFIX}-rcc)
|
||||
SET(QT_UIC_EXECUTABLE ${MINGW_PREFIX}-uic)
|
||||
SET(QT_LRELEASE_EXECUTABLE ${MINGW_PREFIX}-lrelease)
|
||||
SET(QT_QMAKE_EXECUTABLE ${MINGW_PREFIX}-qmake-qt5)
|
||||
SET(QT_MOC_EXECUTABLE ${MINGW_PREFIX}-moc-qt5)
|
||||
SET(QT_RCC_EXECUTABLE ${MINGW_PREFIX}-rcc-qt5)
|
||||
SET(Qt5Widgets_UIC_EXECUTABLE ${MINGW_PREFIX}-uic-qt5)
|
||||
SET(QT_LRELEASE_EXECUTABLE ${MINGW_PREFIX}-lrelease-qt5)
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
FROM opensuse:42.1
|
||||
|
||||
MAINTAINER Daniel Molkentin <danimo@owncloud.com>
|
||||
|
||||
ENV TERM ansi
|
||||
ENV HOME /root
|
||||
|
||||
ENV REFRESHED_AT 20160421
|
||||
ENV REFRESHED_AT 20170113
|
||||
|
||||
RUN zypper --non-interactive --gpg-auto-import-keys refresh
|
||||
RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/windows:/mingw/openSUSE_Leap_42.1/windows:mingw.repo
|
||||
RUN zypper --non-interactive --gpg-auto-import-keys ar http://download.opensuse.org/repositories/isv:ownCloud:toolchains:mingw:win32:2.3/openSUSE_Leap_42.1/isv:ownCloud:toolchains:mingw:win32:2.3.repo
|
||||
RUN zypper --non-interactive --gpg-auto-import-keys install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
|
||||
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
|
||||
mingw32-headers mingw32-runtime site-config mingw32-libwebp \
|
||||
mingw32-headers mingw32-runtime site-config mingw32-libwebp mingw32-libssp0 \
|
||||
mingw32-cross-libqt5-qmake mingw32-cross-libqt5-qttools mingw32-libqt5* \
|
||||
mingw32-qt5keychain* mingw32-angleproject* \
|
||||
mingw32-cross-nsis mingw32-libopenssl* \
|
||||
mingw32-sqlite* kdewin-png2ico \
|
||||
mingw32-sqlite* png2ico \
|
||||
osslsigncode wget
|
||||
|
||||
# RPM depends on curl for installs from HTTP
|
||||
|
|
45
appveyor.ini
Normal file
45
appveyor.ini
Normal file
|
@ -0,0 +1,45 @@
|
|||
[General]
|
||||
Branch = master
|
||||
ShallowClone = True
|
||||
Command=craft
|
||||
|
||||
# Variables defined here override the default value
|
||||
# The variable names are casesensitive
|
||||
[Variables]
|
||||
#Values need to be overwritten to create a chache
|
||||
UseCache = True
|
||||
CreateCache = False
|
||||
|
||||
# Settings applicable for all Crafts matrices
|
||||
# Settings are Category/key=value
|
||||
# Category is case sensitive
|
||||
|
||||
[GeneralSettings]
|
||||
General/EMERGE_PKGDSTDIR=${Variables:APPVEYOR_BUILD_FOLDER}/binaries
|
||||
Paths/python = C:\Python36
|
||||
Paths/python27 = C:\Python27
|
||||
Paths/downloaddir = ${Variables:Root}\downloads
|
||||
ShortPath/Enabled = False
|
||||
ShortPath/EnableJunctions = True
|
||||
ShortPath/JunctionDir = C:\CM-SP\
|
||||
Packager/CacheDir = ${Variables:Root}\cache
|
||||
Packager/UseCache = ${Variables:UseCache}
|
||||
Packager/CreateCache = ${Variables:CreateCache}
|
||||
; Packager/RepositoryUrl = https://files.kde.org/craft/
|
||||
Packager/PackageType = PortablePackager
|
||||
Packager/RepositoryUrl = http://ftp.acc.umu.se/mirror/kde.org/files/craft/master/
|
||||
Compile/BuildType = RelWithDebInfo
|
||||
ContinuousIntegration/Enabled = True
|
||||
|
||||
[BlueprintSettings]
|
||||
# don't try to pip install on the ci
|
||||
python-modules.ignored = True
|
||||
|
||||
libs/qt5.version = 5.9.3
|
||||
craft/craft-core.version = master
|
||||
|
||||
[windows-msvc2017_64-cl]
|
||||
General/ABI = windows-msvc2017_64-cl
|
||||
|
||||
[windows-msvc2017_32-cl]
|
||||
General/ABI = windows-msvc2017_32-cl
|
50
appveyor.yml
Normal file
50
appveyor.yml
Normal file
|
@ -0,0 +1,50 @@
|
|||
version: '{build}-{branch}'
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
clone_depth: 50
|
||||
|
||||
|
||||
init:
|
||||
- ps: |
|
||||
function craft($target) {
|
||||
& C:\Python36\python.exe "C:\CraftMaster\CraftMaster\CraftMaster.py" --config "$env:APPVEYOR_BUILD_FOLDER\appveyor.ini" --variables "APPVEYOR_BUILD_FOLDER=$env:APPVEYOR_BUILD_FOLDER" --target $target -c $args
|
||||
if($LASTEXITCODE -ne 0) {exit $LASTEXITCODE}
|
||||
}
|
||||
|
||||
install:
|
||||
- ps: |
|
||||
#use cmd to silence powershell behaviour for stderr
|
||||
& cmd /C "git clone -q --depth=1 git://anongit.kde.org/craftmaster.git C:\CraftMaster\CraftMaster 2>&1"
|
||||
|
||||
craft $env:TARGET -i craft
|
||||
craft $env:TARGET --install-deps owncloud-client
|
||||
|
||||
build_script:
|
||||
- ps: |
|
||||
craft $env:TARGET --no-cache --src-dir $env:APPVEYOR_BUILD_FOLDER owncloud-client
|
||||
|
||||
after_build:
|
||||
- ps: |
|
||||
craft $env:TARGET --src-dir $env:APPVEYOR_BUILD_FOLDER --package owncloud-client
|
||||
|
||||
|
||||
on_finish:
|
||||
- ps: |
|
||||
Get-ChildItem $env:USERPROFILE\.craft\* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
|
||||
test_script:
|
||||
- ps: |
|
||||
craft $env:TARGET --src-dir $env:APPVEYOR_BUILD_FOLDER --test owncloud-client
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: windows-msvc2017_32-cl
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
- TARGET: windows-msvc2017_64-cl
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
|
||||
artifacts:
|
||||
- path: binaries/*
|
332
cmake/modules/ECMAddAppIcon.cmake
Normal file
332
cmake/modules/ECMAddAppIcon.cmake
Normal file
|
@ -0,0 +1,332 @@
|
|||
#.rst:
|
||||
# ECMAddAppIcon
|
||||
# -------------
|
||||
#
|
||||
# Add icons to executable files and packages.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_add_app_icon(<sources_var>
|
||||
# ICONS <icon> [<icon> [...]]
|
||||
# [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.4x
|
||||
# [OUTFILE_BASE <name>]) # Since 5.4x
|
||||
# )
|
||||
#
|
||||
# The given icons, whose names must match the pattern::
|
||||
#
|
||||
# <size>-<other_text>.png
|
||||
#
|
||||
# will be added to the executable target whose sources are specified by
|
||||
# ``<sources_var>`` on platforms that support it (Windows and Mac OS X).
|
||||
# Other icon files are ignored but on Mac SVG files can be supported and
|
||||
# it is thus possible to mix those with png files in a single macro call.
|
||||
#
|
||||
# ``<size>`` is a numeric pixel size (typically 16, 32, 48, 64, 128 or 256).
|
||||
# ``<other_text>`` can be any other text. See the platform notes below for any
|
||||
# recommendations about icon sizes.
|
||||
#
|
||||
# ``SIDEBAR_ICONS`` can be used to add Mac OS X sidebar
|
||||
# icons to the generated iconset. They are used when a folder monitored by the
|
||||
# application is dragged into Finder's sidebar. Since 5.4x.
|
||||
#
|
||||
# ``OUTFILE_BASE`` will be used as the basename for the icon file. If
|
||||
# you specify it, the icon file will be called ``<OUTFILE_BASE>.icns`` on Mac OS X
|
||||
# and ``<OUTFILE_BASE>.ico`` on Windows. If you don't specify it, it defaults
|
||||
# to ``<sources_var>.<ext>``. Since 5.4x.
|
||||
#
|
||||
#
|
||||
# Windows notes
|
||||
# * Icons are compiled into the executable using a resource file.
|
||||
# * Icons may not show up in Windows Explorer if the executable
|
||||
# target does not have the ``WIN32_EXECUTABLE`` property set.
|
||||
# * The tool png2ico is required. See :find-module:`FindPng2Ico`.
|
||||
# * Supported sizes: 16, 32, 48, 64, 128.
|
||||
#
|
||||
# Mac OS X notes
|
||||
# * The executable target must have the ``MACOSX_BUNDLE`` property set.
|
||||
# * Icons are added to the bundle.
|
||||
# * If the ksvg2icns tool from KIconThemes is available, .svg and .svgz
|
||||
# files are accepted; the first that is converted successfully to .icns
|
||||
# will provide the application icon. SVG files are ignored otherwise.
|
||||
# * The tool iconutil (provided by Apple) is required for bitmap icons.
|
||||
# * Supported sizes: 16, 32, 64, 128, 256 (and 512, 1024 after OS X 10.9).
|
||||
# * At least a 128x128px (or an SVG) icon is required.
|
||||
# * Larger sizes are automatically used to substitute for smaller sizes on
|
||||
# "Retina" (high-resolution) displays. For example, a 32px icon, if
|
||||
# provided, will be used as a 32px icon on standard-resolution displays,
|
||||
# and as a 16px-equivalent icon (with an "@2x" tag) on high-resolution
|
||||
# displays. That is why you should provide 64px and 1024px icons although
|
||||
# they are not supported anymore directly. Instead they will be used as
|
||||
# 32px@2x and 512px@2x. ksvg2icns handles this internally.
|
||||
# * This function sets the ``MACOSX_BUNDLE_ICON_FILE`` variable to the name
|
||||
# of the generated icns file, so that it will be used as the
|
||||
# ``MACOSX_BUNDLE_ICON_FILE`` target property when you call
|
||||
# ``add_executable``.
|
||||
# * Sidebar icons should typically provided in 16, 32, 64, 128 and 256px.
|
||||
#
|
||||
# Since 1.7.0.
|
||||
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
# Copyright 2014 Ralf Habacker <ralf.habacker@freenet.de>
|
||||
# Copyright 2006-2009 Alexander Neundorf, <neundorf@kde.org>
|
||||
# Copyright 2006, 2007, Laurent Montel, <montel@kde.org>
|
||||
# Copyright 2007 Matthias Kretz <kretz@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
function(ecm_add_app_icon appsources)
|
||||
set(options)
|
||||
set(oneValueArgs OUTFILE_BASE)
|
||||
set(multiValueArgs ICONS SIDEBAR_ICONS)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT ARG_ICONS)
|
||||
message(FATAL_ERROR "No ICONS argument given to ecm_add_app_icon")
|
||||
endif()
|
||||
if(ARG_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_add_app_icon: ${ARG_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
find_program(KSVG2ICNS NAMES ksvg2icns)
|
||||
foreach(icon ${ARG_ICONS})
|
||||
get_filename_component(icon_full ${icon} ABSOLUTE)
|
||||
get_filename_component(icon_type ${icon_full} EXT)
|
||||
# do we have ksvg2icns in the path and did we receive an svg (or compressed svg) icon?
|
||||
if(KSVG2ICNS AND (${icon_type} STREQUAL ".svg" OR ${icon_type} STREQUAL ".svgz"))
|
||||
# convert the svg icon to an icon resource
|
||||
execute_process(COMMAND ${KSVG2ICNS} "${icon_full}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE KSVG2ICNS_ERROR)
|
||||
if(${KSVG2ICNS_ERROR})
|
||||
message(AUTHOR_WARNING "ksvg2icns could not generate an OS X application icon from ${icon}")
|
||||
else()
|
||||
# install the icns file we just created
|
||||
get_filename_component(icon_name ${icon_full} NAME_WE)
|
||||
set(MACOSX_BUNDLE_ICON_FILE ${icon_name}.icns PARENT_SCOPE)
|
||||
set(${appsources} "${${appsources}};${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns" PARENT_SCOPE)
|
||||
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${icon_name}.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
# we're done now
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_ICONS}" "icons" "16;32;48;64;128;256;512;1024")
|
||||
if(ARG_SIDEBAR_ICONS)
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;18;32;36;64")
|
||||
endif()
|
||||
|
||||
set(mac_icons
|
||||
# Icons: https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
|
||||
${icons_at_16px}
|
||||
${icons_at_32px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px}
|
||||
${icons_at_256px}
|
||||
${icons_at_512px}
|
||||
${icons_at_1024px}
|
||||
|
||||
# Sidebar Icons: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
|
||||
${sidebar_icons_at_16px}
|
||||
${sidebar_icons_at_18px}
|
||||
${sidebar_icons_at_32px}
|
||||
${sidebar_icons_at_36px}
|
||||
${sidebar_icons_at_64px})
|
||||
if (NOT icons_at_128px)
|
||||
message(AUTHOR_WARNING "No 128px icon provided; this will not work on Mac OS X")
|
||||
endif()
|
||||
|
||||
|
||||
set(windows_icons ${icons_at_16px}
|
||||
${icons_at_32px}
|
||||
${icons_at_48px}
|
||||
${icons_at_64px}
|
||||
${icons_at_128px})
|
||||
if (NOT windows_icons)
|
||||
message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
|
||||
endif()
|
||||
|
||||
if (ARG_OUTFILE_BASE)
|
||||
set (_outfilebasename "${ARG_OUTFILE_BASE}")
|
||||
else()
|
||||
set (_outfilebasename "${appsources}")
|
||||
endif()
|
||||
set (_outfilename "${CMAKE_CURRENT_BINARY_DIR}/${_outfilebasename}")
|
||||
|
||||
if (WIN32 AND windows_icons)
|
||||
set(saved_CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_FIND_MODULE_DIR})
|
||||
find_package(Png2Ico)
|
||||
set(CMAKE_MODULE_PATH "${saved_CMAKE_MODULE_PATH}")
|
||||
|
||||
if (Png2Ico_FOUND)
|
||||
if (Png2Ico_HAS_RCFILE_ARGUMENT)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.rc" "${_outfilename}.ico"
|
||||
COMMAND Png2Ico::Png2Ico
|
||||
ARGS
|
||||
--rcfile "${_outfilename}.rc"
|
||||
"${_outfilename}.ico"
|
||||
${windows_icons}
|
||||
DEPENDS ${windows_icons}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
else()
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.ico"
|
||||
COMMAND Png2Ico::Png2Ico
|
||||
ARGS "${_outfilename}.ico" ${windows_icons}
|
||||
DEPENDS ${windows_icons}
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
# this bit's a little hacky to make the dependency stuff work
|
||||
file(WRITE "${_outfilename}.rc.in" "IDI_ICON1 ICON DISCARDABLE \"${_outfilename}.ico\"\n")
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.rc"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E copy "${_outfilename}.rc.in" "${_outfilename}.rc"
|
||||
DEPENDS "${_outfilename}.ico"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
endif()
|
||||
set(${appsources} "${${appsources}};${_outfilename}.rc" PARENT_SCOPE)
|
||||
else()
|
||||
message(WARNING "Unable to find the png2ico utility - application will not have an application icon!")
|
||||
endif()
|
||||
elseif (APPLE AND mac_icons)
|
||||
# first generate .iconset directory structure, then convert to .icns format using the Mac OS X "iconutil" utility,
|
||||
# to create retina compatible icon, you need png source files in pixel resolution 16x16, 32x32, 64x64, 128x128,
|
||||
# 256x256, 512x512, 1024x1024
|
||||
find_program(ICONUTIL_EXECUTABLE NAMES iconutil)
|
||||
if (ICONUTIL_EXECUTABLE)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.iconset"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E make_directory "${_outfilename}.iconset"
|
||||
)
|
||||
set(iconset_icons)
|
||||
macro(copy_icon filename sizename type)
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.iconset/${type}_${sizename}.png"
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
ARGS -E copy
|
||||
"${filename}"
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png"
|
||||
DEPENDS
|
||||
"${_outfilename}.iconset"
|
||||
"${filename}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
list(APPEND iconset_icons
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png")
|
||||
endmacro()
|
||||
foreach(size 16 32 128 256 512)
|
||||
math(EXPR double_size "2 * ${size}")
|
||||
foreach(file ${icons_at_${size}px})
|
||||
copy_icon("${file}" "${size}x${size}" "icon")
|
||||
endforeach()
|
||||
foreach(file ${icons_at_${double_size}px})
|
||||
copy_icon("${file}" "${size}x${size}@2x" "icon")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
foreach(size 16 18 32)
|
||||
math(EXPR double_size "2 * ${size}")
|
||||
foreach(file ${sidebar_icons_at_${size}px})
|
||||
copy_icon("${file}" "${size}x${size}" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_${double_size}px})
|
||||
copy_icon("${file}" "${size}x${size}@2x" "sidebar")
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# generate .icns icon file
|
||||
add_custom_command(
|
||||
OUTPUT "${_outfilename}.icns"
|
||||
COMMAND ${ICONUTIL_EXECUTABLE}
|
||||
ARGS
|
||||
--convert icns
|
||||
--output "${_outfilename}.icns"
|
||||
"${_outfilename}.iconset"
|
||||
DEPENDS "${iconset_icons}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
# This will register the icon into the bundle
|
||||
set(MACOSX_BUNDLE_ICON_FILE "${_outfilebasename}.icns" PARENT_SCOPE)
|
||||
|
||||
# Append the icns file to the sources list so it will be a dependency to the
|
||||
# main target
|
||||
set(${appsources} "${${appsources}};${_outfilename}.icns" PARENT_SCOPE)
|
||||
|
||||
# Install the icon into the Resources dir in the bundle
|
||||
set_source_files_properties("${_outfilename}.icns" PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
else()
|
||||
message(STATUS "Unable to find the iconutil utility - application will not have an application icon!")
|
||||
endif()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
macro(_ecm_add_app_icon_categorize_icons icons type known_sizes)
|
||||
set(_${type}_known_sizes)
|
||||
foreach(size ${known_sizes})
|
||||
set(${type}_at_${size}px)
|
||||
list(APPEND _${type}_known_sizes ${size})
|
||||
endforeach()
|
||||
|
||||
|
||||
foreach(icon ${icons})
|
||||
get_filename_component(icon_full ${icon} ABSOLUTE)
|
||||
if (NOT EXISTS "${icon_full}")
|
||||
message(AUTHOR_WARNING "${icon_full} does not exist, ignoring")
|
||||
else()
|
||||
get_filename_component(icon_name ${icon} NAME)
|
||||
string(REGEX MATCH "([0-9]+)\\-[^/]+\\.([a-z]+)$"
|
||||
_dummy "${icon_name}")
|
||||
set(size "${CMAKE_MATCH_1}")
|
||||
set(ext "${CMAKE_MATCH_2}")
|
||||
|
||||
if (NOT (ext STREQUAL "svg" OR ext STREQUAL "svgz"))
|
||||
if (NOT size)
|
||||
message(AUTHOR_WARNING "${icon_full} is not named correctly for ecm_add_app_icon - ignoring")
|
||||
elseif (NOT ext STREQUAL "png")
|
||||
message(AUTHOR_WARNING "${icon_full} is not a png file - ignoring")
|
||||
else()
|
||||
list(FIND _${type}_known_sizes ${size} offset)
|
||||
|
||||
if (offset GREATER -1)
|
||||
list(APPEND ${type}_at_${size}px "${icon_full}")
|
||||
elseif()
|
||||
message(STATUS "not found ${type}_at_${size}px ${icon_full}")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
endmacro()
|
297
cmake/modules/ECMFindModuleHelpers.cmake
Normal file
297
cmake/modules/ECMFindModuleHelpers.cmake
Normal file
|
@ -0,0 +1,297 @@
|
|||
#.rst:
|
||||
# ECMFindModuleHelpers
|
||||
# --------------------
|
||||
#
|
||||
# Helper macros for find modules: ecm_find_package_version_check(),
|
||||
# ecm_find_package_parse_components() and
|
||||
# ecm_find_package_handle_library_components().
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_version_check(<name>)
|
||||
#
|
||||
# Prints warnings if the CMake version or the project's required CMake version
|
||||
# is older than that required by extra-cmake-modules.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_parse_components(<name>
|
||||
# RESULT_VAR <variable>
|
||||
# KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
#
|
||||
# This macro will populate <variable> with a list of components found in
|
||||
# <name>_FIND_COMPONENTS, after checking that all those components are in the
|
||||
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
|
||||
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
|
||||
# return().
|
||||
#
|
||||
# The order of components in <variable> is guaranteed to match the order they
|
||||
# are listed in the KNOWN_COMPONENTS argument.
|
||||
#
|
||||
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
|
||||
# <name>_<component>_component_deps will be checked for dependent components.
|
||||
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||
# dependencies will also be added to <variable>.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_handle_library_components(<name>
|
||||
# COMPONENTS <component> [<component> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
# [SKIP_PKG_CONFIG])
|
||||
#
|
||||
# Creates an imported library target for each component. The operation of this
|
||||
# macro depends on the presence of a number of CMake variables.
|
||||
#
|
||||
# The <name>_<component>_lib variable should contain the name of this library,
|
||||
# and <name>_<component>_header variable should contain the name of a header
|
||||
# file associated with it (whatever relative path is normally passed to
|
||||
# '#include'). <name>_<component>_header_subdir variable can be used to specify
|
||||
# which subdirectory of the include path the headers will be found in.
|
||||
# ecm_find_package_components() will then search for the library
|
||||
# and include directory (creating appropriate cache variables) and create an
|
||||
# imported library target named <name>::<component>.
|
||||
#
|
||||
# Additional variables can be used to provide additional information:
|
||||
#
|
||||
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
|
||||
# pkg-config is found, the pkg-config module given by
|
||||
# <name>_<component>_pkg_config will be searched for and used to help locate the
|
||||
# library and header file. It will also be used to set
|
||||
# <name>_<component>_VERSION.
|
||||
#
|
||||
# Note that if version information is found via pkg-config,
|
||||
# <name>_<component>_FIND_VERSION can be set to require a particular version
|
||||
# for each component.
|
||||
#
|
||||
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
|
||||
# of the imported target for <component> will be set to contain the imported
|
||||
# targets for the components listed in <name>_<component>_component_deps.
|
||||
# <component>_FOUND will also be set to false if any of the compoments in
|
||||
# <name>_<component>_component_deps are not found. This requires the components
|
||||
# in <name>_<component>_component_deps to be listed before <component> in the
|
||||
# COMPONENTS argument.
|
||||
#
|
||||
# The following variables will be set:
|
||||
#
|
||||
# ``<name>_TARGETS``
|
||||
# the imported targets
|
||||
# ``<name>_LIBRARIES``
|
||||
# the found libraries
|
||||
# ``<name>_INCLUDE_DIRS``
|
||||
# the combined required include directories for the components
|
||||
# ``<name>_DEFINITIONS``
|
||||
# the "other" CFLAGS provided by pkg-config, if any
|
||||
# ``<name>_VERSION``
|
||||
# the value of ``<name>_<component>_VERSION`` for the first component that
|
||||
# has this variable set (note that components are searched for in the order
|
||||
# they are passed to the macro), although if it is already set, it will not
|
||||
# be altered
|
||||
#
|
||||
# Note that these variables are never cleared, so if
|
||||
# ecm_find_package_handle_library_components() is called multiple times with
|
||||
# different components (typically because of multiple find_package() calls) then
|
||||
# ``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||
# call (although no duplicates).
|
||||
#
|
||||
# Since pre-1.0.0.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
macro(ecm_find_package_version_check module_name)
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_parse_components module_name)
|
||||
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fppc_oneValueArgs RESULT_VAR)
|
||||
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPPC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_RESULT_VAR)
|
||||
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
|
||||
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if(${module_name}_FIND_COMPONENTS)
|
||||
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
|
||||
|
||||
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
|
||||
# Make sure deps are included
|
||||
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
|
||||
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
|
||||
if("${ecm_fppc_index}" STREQUAL "-1")
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
|
||||
endif()
|
||||
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Skipping dependency handling for ${module_name}")
|
||||
endif()
|
||||
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
|
||||
|
||||
# This makes sure components are listed in the same order as
|
||||
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
|
||||
set(${ECM_FPPC_RESULT_VAR})
|
||||
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
|
||||
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
|
||||
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
|
||||
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
|
||||
endif()
|
||||
endforeach()
|
||||
# if there are any left, they are unknown components
|
||||
if(ecm_fppc_requestedComps)
|
||||
set(ecm_fppc_msgType STATUS)
|
||||
if(${module_name}_FIND_REQUIRED)
|
||||
set(ecm_fppc_msgType FATAL_ERROR)
|
||||
endif()
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_handle_library_components module_name)
|
||||
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fpwc_oneValueArgs)
|
||||
set(ecm_fpwc_multiValueArgs COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPWC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPWC_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package(PkgConfig)
|
||||
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
|
||||
set(ecm_fpwc_dep_vars)
|
||||
set(ecm_fpwc_dep_targets)
|
||||
if(NOT SKIP_DEPENDENCY_HANDLING)
|
||||
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
|
||||
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
|
||||
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
|
||||
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
|
||||
${${module_name}_${ecm_fpwc_comp}_pkg_config})
|
||||
endif()
|
||||
|
||||
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
|
||||
)
|
||||
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
|
||||
if(NOT ${module_name}_VERSION)
|
||||
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
|
||||
FOUND_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_FOUND
|
||||
REQUIRED_VARS
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
${ecm_fpwc_dep_vars}
|
||||
VERSION_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_VERSION
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(${module_name}_${ecm_fpwc_comp}_FOUND)
|
||||
list(APPEND ${module_name}_LIBRARIES
|
||||
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
|
||||
list(APPEND ${module_name}_INCLUDE_DIRS
|
||||
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
|
||||
set(${module_name}_DEFINITIONS
|
||||
${${module_name}_DEFINITIONS}
|
||||
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
|
||||
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
|
||||
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
|
||||
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
|
||||
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${module_name}_TARGETS
|
||||
"${module_name}::${ecm_fpwc_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(${module_name}_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
|
||||
endif()
|
||||
if(${module_name}_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
|
||||
endif()
|
||||
if(${module_name}_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
|
||||
endif()
|
||||
if(${module_name}_TARGETS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
|
||||
endif()
|
||||
endmacro()
|
1
cmake/modules/ECMFindModuleHelpersStub.cmake
Normal file
1
cmake/modules/ECMFindModuleHelpersStub.cmake
Normal file
|
@ -0,0 +1 @@
|
|||
include(${CMAKE_CURRENT_LIST_DIR}/../modules/ECMFindModuleHelpers.cmake)
|
117
cmake/modules/FindPng2Ico.cmake
Normal file
117
cmake/modules/FindPng2Ico.cmake
Normal file
|
@ -0,0 +1,117 @@
|
|||
#.rst:
|
||||
# FindPng2Ico
|
||||
# -----------
|
||||
#
|
||||
# Try to find png2ico.
|
||||
#
|
||||
# If the png2ico executable is not in your PATH, you can provide
|
||||
# an alternative name or full path location with the ``Png2Ico_EXECUTABLE``
|
||||
# variable.
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# ``Png2Ico_FOUND``
|
||||
# True if png2ico is available.
|
||||
#
|
||||
# ``Png2Ico_EXECUTABLE``
|
||||
# The png2ico executable.
|
||||
#
|
||||
# If ``Png2Ico_FOUND`` is TRUE, it will also define the following imported
|
||||
# target:
|
||||
#
|
||||
# ``Png2Ico::Png2Ico``
|
||||
# The png2ico executable.
|
||||
#
|
||||
# and the following variables:
|
||||
#
|
||||
# ``Png2Ico_HAS_COLORS_ARGUMENT``
|
||||
# Whether png2ico accepts a ``--colors`` argument. `Matthias Benkmann's
|
||||
# tool <http://www.winterdrache.de/freeware/png2ico/>`_ does, while the
|
||||
# version of png2ico from the `"KDE On Windows" (kdewin)
|
||||
# <https://projects.kde.org/projects/kdesupport/kdewin>`_ project does not.
|
||||
#
|
||||
# ``Png2Ico_HAS_RCFILE_ARGUMENT``
|
||||
# Whether png2ico accepts an ``--rcfile`` argument. The version of png2ico
|
||||
# from the `"KDE On Windows" (kdewin)
|
||||
# <https://projects.kde.org/projects/kdesupport/kdewin>`_ project does,
|
||||
# while `Matthias Benkmann's tool
|
||||
# <http://www.winterdrache.de/freeware/png2ico/>`_ does not.
|
||||
#
|
||||
# Since 1.7.0.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#=============================================================================
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake)
|
||||
|
||||
ecm_find_package_version_check(Png2Ico)
|
||||
|
||||
# Find png2ico
|
||||
find_program(Png2Ico_EXECUTABLE NAMES png2ico)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Png2Ico
|
||||
FOUND_VAR
|
||||
Png2Ico_FOUND
|
||||
REQUIRED_VARS
|
||||
Png2Ico_EXECUTABLE
|
||||
)
|
||||
|
||||
mark_as_advanced(Png2Ico_EXECUTABLE)
|
||||
|
||||
if (Png2Ico_FOUND)
|
||||
execute_process(
|
||||
COMMAND "${Png2Ico_EXECUTABLE}" --help
|
||||
OUTPUT_VARIABLE _png2ico_help_text
|
||||
ERROR_VARIABLE _png2ico_help_text
|
||||
)
|
||||
if (_png2ico_help_text MATCHES ".*--rcfile .*")
|
||||
set(Png2Ico_HAS_RCFILE_ARGUMENT TRUE)
|
||||
else()
|
||||
set(Png2Ico_HAS_RCFILE_ARGUMENT FALSE)
|
||||
endif()
|
||||
if (_png2ico_help_text MATCHES ".*--colors .*")
|
||||
set(Png2Ico_HAS_COLORS_ARGUMENT TRUE)
|
||||
else()
|
||||
set(Png2Ico_HAS_COLORS_ARGUMENT FALSE)
|
||||
endif()
|
||||
unset(_png2ico_help_text)
|
||||
|
||||
if (NOT TARGET Png2Ico::Png2Ico)
|
||||
add_executable(Png2Ico::Png2Ico IMPORTED)
|
||||
set_target_properties(Png2Ico::Png2Ico PROPERTIES
|
||||
IMPORTED_LOCATION "${Png2Ico_EXECUTABLE}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(Png2Ico PROPERTIES
|
||||
URL "http://www.winterdrache.de/freeware/png2ico/ or https://projects.kde.org/projects/kdesupport/kdewin"
|
||||
DESCRIPTION "Executable that converts a collection of PNG files into a Windows icon file"
|
||||
)
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
# - MACRO_ADD_PLUGIN(name [WITH_PREFIX] file1 .. fileN)
|
||||
#
|
||||
# Create a plugin from the given source files.
|
||||
# If WITH_PREFIX is given, the resulting plugin will have the
|
||||
# prefix "lib", otherwise it won't.
|
||||
#
|
||||
# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
|
||||
# Copyright (c) 2006, Laurent Montel, <montel@kde.org>
|
||||
# Copyright (c) 2006, Andreas Schneider, <asn@cryptomilk.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
|
||||
macro (MACRO_ADD_PLUGIN _target_NAME _with_PREFIX)
|
||||
|
||||
if (${_with_PREFIX} STREQUAL "WITH_PREFIX")
|
||||
set(_first_SRC)
|
||||
else (${_with_PREFIX} STREQUAL "WITH_PREFIX")
|
||||
set(_first_SRC ${_with_PREFIX})
|
||||
endif (${_with_PREFIX} STREQUAL "WITH_PREFIX")
|
||||
|
||||
add_library(${_target_NAME} MODULE ${_first_SRC} ${ARGN})
|
||||
|
||||
if (_first_SRC)
|
||||
set_target_properties(${_target_NAME} PROPERTIES PREFIX "")
|
||||
endif (_first_SRC)
|
||||
|
||||
endmacro (MACRO_ADD_PLUGIN _name _sources)
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
# (c) 2014 Copyright ownCloud GmbH
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING* file.
|
||||
|
||||
# - macro_copy_file(_src _dst)
|
||||
# Copies a file to ${_dst} only if ${_src} is different (newer) than ${_dst}
|
||||
#
|
||||
# Example:
|
||||
# macro_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/icon.png ${CMAKE_CURRENT_BINARY_DIR}/.)
|
||||
# Copies file icon.png to ${CMAKE_CURRENT_BINARY_DIR} directory
|
||||
#
|
||||
# Copyright (c) 2006-2007 Wengo
|
||||
# Copyright (c) 2006-2008 Andreas Schneider <asn@cryptomilk.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING file.
|
||||
|
||||
|
||||
macro (macro_copy_file _src _dst)
|
||||
# Removes all path containing .svn or CVS or CMakeLists.txt during the copy
|
||||
if (NOT ${_src} MATCHES ".*\\.svn|CVS|CMakeLists\\.txt.*")
|
||||
|
||||
if (CMAKE_VERBOSE_MAKEFILE)
|
||||
message(STATUS "Copy file from ${_src} to ${_dst}")
|
||||
endif (CMAKE_VERBOSE_MAKEFILE)
|
||||
|
||||
# Creates directory if necessary
|
||||
get_filename_component(_path ${_dst} PATH)
|
||||
file(MAKE_DIRECTORY ${_path})
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${CMAKE_COMMAND} -E copy_if_different ${_src} ${_dst}
|
||||
OUTPUT_QUIET
|
||||
)
|
||||
endif (NOT ${_src} MATCHES ".*\\.svn|CVS|CMakeLists\\.txt.*")
|
||||
endmacro (macro_copy_file)
|
|
@ -461,6 +461,7 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
|||
File "${MING_BIN}\libgcc_s_sjlj-1.dll"
|
||||
File "${MING_BIN}\libstdc++-6.dll"
|
||||
File "${MING_BIN}\libwinpthread-1.dll"
|
||||
File "${MING_BIN}\libssp-0.dll"
|
||||
|
||||
;CSync configs
|
||||
File "${SOURCE_PATH}/sync-exclude.lst"
|
||||
|
|
|
@ -1,104 +0,0 @@
|
|||
# (c) 2014 Copyright ownCloud GmbH
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING* file.
|
||||
|
||||
include (MacroOptionalFindPackage)
|
||||
include (MacroLogFeature)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
find_package(Qt5Concurrent REQUIRED)
|
||||
if(UNIT_TESTING)
|
||||
find_package(Qt5Test REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
if(APPLE)
|
||||
find_package(Qt5MacExtras REQUIRED)
|
||||
endif(APPLE)
|
||||
|
||||
if(NOT NO_SHIBBOLETH)
|
||||
find_package(Qt5WebKitWidgets)
|
||||
find_package(Qt5WebKit)
|
||||
if(NOT Qt5WebKitWidgets_FOUND)
|
||||
message(FATAL_ERROR "Qt5WebKit required for Shibboleth. Use -DNO_SHIBBOLETH=1 to disable it.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# We need this to find the paths to qdbusxml2cpp and co
|
||||
if (WITH_DBUS)
|
||||
find_package(Qt5DBus REQUIRED)
|
||||
include_directories(${Qt5DBus_INCLUDES})
|
||||
add_definitions(${Qt5DBus_DEFINITIONS})
|
||||
endif (WITH_DBUS)
|
||||
include_directories(${Qt5Core_INCLUDES})
|
||||
add_definitions(${Qt5Core_DEFINITIONS})
|
||||
if (NOT WIN32) #implied on Win32
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
endif(NOT WIN32)
|
||||
# set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
|
||||
if(APPLE AND NOT TOKEN_AUTH_ONLY)
|
||||
include_directories(${Qt5MacExtras_INCLUDE_DIRS})
|
||||
add_definitions(${Qt5MacExtras_DEFINITIONS})
|
||||
set (QT_LIBRARIES ${QT_LIBRARIES} ${Qt5MacExtras_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_LIBRARIES_ONLY)
|
||||
macro(qt_wrap_ui)
|
||||
qt5_wrap_ui(${ARGN})
|
||||
endmacro()
|
||||
else()
|
||||
# hack
|
||||
SET(QT_UIC_EXECUTABLE "")
|
||||
endif()
|
||||
|
||||
macro(qt_add_resources)
|
||||
qt5_add_resources(${ARGN})
|
||||
endmacro()
|
||||
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5LinguistTools)
|
||||
if(Qt5LinguistTools_FOUND)
|
||||
macro(qt_add_translation)
|
||||
qt5_add_translation(${ARGN})
|
||||
endmacro()
|
||||
else()
|
||||
macro(qt_add_translation)
|
||||
endmacro()
|
||||
endif()
|
||||
else()
|
||||
macro(qt_add_translation)
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
macro(qt_add_dbus_interface)
|
||||
qt5_add_dbus_interface(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_adaptor)
|
||||
qt5_add_dbus_adaptor(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_cpp)
|
||||
qt5_wrap_cpp(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(install_qt_executable)
|
||||
install_qt5_executable(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(setup_qt)
|
||||
endmacro()
|
||||
|
||||
set(QT_RCC_EXECUTABLE "${Qt5Core_RCC_EXECUTABLE}")
|
||||
|
||||
#Enable deprecated symbols
|
||||
add_definitions("-DQT_DISABLE_DEPRECATED_BEFORE=0")
|
||||
add_definitions("-DQT_DEPRECATED_WARNINGS")
|
||||
add_definitions("-DQT_USE_QSTRINGBUILDER") #optimize string concatenation
|
||||
add_definitions("-DQT_MESSAGELOGCONTEXT") #enable function name and line number in debug output
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# - macro_asciidoc2man(inputfile outputfile)
|
||||
#
|
||||
# Create a manpage with asciidoc.
|
||||
# Example: macro_asciidoc2man(foo.txt foo.1)
|
||||
#
|
||||
# Copyright (c) 2006, Andreas Schneider, <asn@cryptomilk.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
include(MacroCopyFile)
|
||||
|
||||
macro(MACRO_ASCIIDOC2MAN _a2m_input _a2m_output)
|
||||
find_program(A2X
|
||||
NAMES
|
||||
a2x
|
||||
)
|
||||
#message("+++ A2X: ${A2X}")
|
||||
|
||||
if (A2X)
|
||||
|
||||
#message("+++ ${A2X} --doctype=manpage --format=manpage --destination-dir=${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${_a2m_input}")
|
||||
macro_copy_file(${CMAKE_CURRENT_SOURCE_DIR}/${_a2m_input} ${CMAKE_CURRENT_BINARY_DIR}/${_a2m_input})
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
${A2X} --doctype=manpage --format=manpage ${_a2m_input}
|
||||
WORKING_DIRECTORY
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
RESULT_VARIABLE
|
||||
A2M_MAN_GENERATED
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
#message("+++ A2M_MAN_GENERATED: ${A2M_MAN_GENERATED}")
|
||||
if (A2M_MAN_GENERATED EQUAL 0)
|
||||
find_file(A2M_MAN_FILE
|
||||
NAME
|
||||
${_a2m_output}
|
||||
PATHS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
|
||||
if (A2M_MAN_FILE)
|
||||
get_filename_component(A2M_MAN_CATEGORY ${A2M_MAN_FILE} EXT)
|
||||
string(SUBSTRING ${A2M_MAN_CATEGORY} 1 1 A2M_MAN_CATEGORY)
|
||||
install(
|
||||
FILES
|
||||
${A2M_MAN_FILE}
|
||||
DESTINATION
|
||||
${MAN_INSTALL_DIR}/man${A2M_MAN_CATEGORY}
|
||||
)
|
||||
endif (A2M_MAN_FILE)
|
||||
endif (A2M_MAN_GENERATED EQUAL 0)
|
||||
|
||||
endif (A2X)
|
||||
endmacro(MACRO_ASCIIDOC2MAN _a2m_input _a2m_file)
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-long-long -Wno-gnu-zero-variadic-macro-arguments")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
|
||||
|
||||
# Fix sqlite compilation on macOS
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-incompatible-pointer-types-discards-qualifiers")
|
||||
# Fix sqlite compilation on MinGW
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-discarded-qualifiers")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
|
|
|
@ -1,460 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include "csync_update.cpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "torture.h"
|
||||
|
||||
#define TESTDB "/tmp/check_csync/journal.db"
|
||||
|
||||
static int firstrun = 1;
|
||||
|
||||
static void statedb_create_metadata_table(sqlite3 *db)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if( db ) {
|
||||
const char *sql = "CREATE TABLE IF NOT EXISTS metadata("
|
||||
"phash INTEGER(8),"
|
||||
"pathlen INTEGER,"
|
||||
"path VARCHAR(4096),"
|
||||
"inode INTEGER,"
|
||||
"uid INTEGER,"
|
||||
"gid INTEGER,"
|
||||
"mode INTEGER,"
|
||||
"modtime INTEGER(8),"
|
||||
"type INTEGER,"
|
||||
"md5 VARCHAR(32),"
|
||||
"fileid VARCHAR(128),"
|
||||
"remotePerm VARCHAR(128),"
|
||||
"filesize BIGINT,"
|
||||
"ignoredChildrenRemote INT,"
|
||||
"contentChecksum TEXT,"
|
||||
"contentChecksumTypeId INTEGER,"
|
||||
"PRIMARY KEY(phash));";
|
||||
|
||||
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||
//const char *msg = sqlite3_errmsg(db);
|
||||
assert_int_equal( rc, SQLITE_OK );
|
||||
|
||||
sql = "CREATE TABLE IF NOT EXISTS checksumtype("
|
||||
"id INTEGER PRIMARY KEY,"
|
||||
"name TEXT UNIQUE"
|
||||
");";
|
||||
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||
assert_int_equal( rc, SQLITE_OK );
|
||||
}
|
||||
}
|
||||
|
||||
static void statedb_insert_metadata(sqlite3 *db)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if( db ) {
|
||||
char *stmt = sqlite3_mprintf("INSERT INTO metadata"
|
||||
"(phash, pathlen, path, inode, uid, gid, mode, modtime,type,md5) VALUES"
|
||||
"(%lld, %d, '%q', %d, %d, %d, %d, %lld, %d, '%q');",
|
||||
(long long signed int)42,
|
||||
42,
|
||||
"I_was_wurst_before_I_became_wurstsalat",
|
||||
619070,
|
||||
42,
|
||||
42,
|
||||
42,
|
||||
(long long signed int)42,
|
||||
0,
|
||||
"4711");
|
||||
|
||||
char *errmsg;
|
||||
rc = sqlite3_exec(db, stmt, NULL, NULL, &errmsg);
|
||||
sqlite3_free(stmt);
|
||||
assert_int_equal( rc, SQLITE_OK );
|
||||
}
|
||||
}
|
||||
|
||||
static int setup(void **state)
|
||||
{
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
unlink(TESTDB);
|
||||
rc = system("mkdir -p /tmp/check_csync");
|
||||
assert_int_equal(rc, 0);
|
||||
rc = system("mkdir -p /tmp/check_csync1");
|
||||
assert_int_equal(rc, 0);
|
||||
csync_create(&csync, "/tmp/check_csync1");
|
||||
csync_init(csync, TESTDB);
|
||||
|
||||
/* Create a new db with metadata */
|
||||
sqlite3 *db;
|
||||
csync->statedb.file = c_strdup(TESTDB);
|
||||
rc = sqlite3_open(csync->statedb.file, &db);
|
||||
statedb_create_metadata_table(db);
|
||||
if( firstrun ) {
|
||||
statedb_insert_metadata(db);
|
||||
firstrun = 0;
|
||||
}
|
||||
sqlite3_close(db);
|
||||
|
||||
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_ftw(void **state)
|
||||
{
|
||||
CSYNC *csync;
|
||||
int rc;
|
||||
|
||||
rc = system("mkdir -p /tmp/check_csync");
|
||||
assert_int_equal(rc, 0);
|
||||
rc = system("mkdir -p /tmp/check_csync1");
|
||||
assert_int_equal(rc, 0);
|
||||
csync_create(&csync, "/tmp");
|
||||
csync_init(csync, TESTDB);
|
||||
|
||||
sqlite3 *db = NULL;
|
||||
rc = sqlite3_open_v2(TESTDB, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
|
||||
assert_int_equal(rc, SQLITE_OK);
|
||||
statedb_create_metadata_table(db);
|
||||
rc = sqlite3_close(db);
|
||||
assert_int_equal(rc, SQLITE_OK);
|
||||
|
||||
rc = csync_statedb_load(csync, TESTDB, &csync->statedb.db);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
csync->statedb.file = c_strdup( TESTDB );
|
||||
*state = csync;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int teardown(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
int rc;
|
||||
|
||||
unlink( csync->statedb.file);
|
||||
rc = csync_destroy(csync);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int teardown_rm(void **state) {
|
||||
int rc;
|
||||
|
||||
teardown(state);
|
||||
|
||||
rc = system("rm -rf /tmp/check_csync");
|
||||
assert_int_equal(rc, 0);
|
||||
rc = system("rm -rf /tmp/check_csync1");
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create a file stat, caller must free memory */
|
||||
static csync_vio_file_stat_t* create_fstat(const char *name,
|
||||
ino_t inode,
|
||||
time_t mtime)
|
||||
{
|
||||
csync_vio_file_stat_t *fs = NULL;
|
||||
time_t t;
|
||||
|
||||
fs = csync_vio_file_stat_new();
|
||||
if (fs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (name && *name) {
|
||||
fs->name = c_strdup(name);
|
||||
} else {
|
||||
fs->name = c_strdup("file.txt");
|
||||
}
|
||||
|
||||
if (fs->name == NULL) {
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
|
||||
|
||||
fs->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
||||
|
||||
|
||||
if (inode == 0) {
|
||||
fs->inode = 619070;
|
||||
} else {
|
||||
fs->inode = inode;
|
||||
}
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
|
||||
|
||||
|
||||
fs->size = 157459;
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
|
||||
|
||||
|
||||
|
||||
if (mtime == 0) {
|
||||
fs->atime = fs->ctime = fs->mtime = time(&t);
|
||||
} else {
|
||||
fs->atime = fs->ctime = fs->mtime = mtime;
|
||||
}
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
static int failing_fn(CSYNC *ctx,
|
||||
const char *file,
|
||||
const csync_vio_file_stat_t *fs,
|
||||
int flag)
|
||||
{
|
||||
(void) ctx;
|
||||
(void) file;
|
||||
(void) fs;
|
||||
(void) flag;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* detect a new file */
|
||||
static void check_csync_detect_update(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
csync_file_stat_t *st;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = create_fstat("file.txt", 0, 1217597845);
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
"/tmp/check_csync1/file.txt",
|
||||
fs,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
/* the instruction should be set to new */
|
||||
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
|
||||
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
|
||||
|
||||
/* create a statedb */
|
||||
csync_set_status(csync, 0xFFFF);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
/* Test behaviour in case no db is there. For that its important that the
|
||||
* test before this one uses teardown_rm.
|
||||
*/
|
||||
static void check_csync_detect_update_db_none(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
csync_file_stat_t *st;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = create_fstat("file.txt", 0, 1217597845);
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
"/tmp/check_csync1/file.txt",
|
||||
fs,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
/* the instruction should be set to new */
|
||||
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
|
||||
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
|
||||
|
||||
|
||||
/* create a statedb */
|
||||
csync_set_status(csync, 0xFFFF);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
static void check_csync_detect_update_db_eval(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
csync_file_stat_t *st;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = create_fstat("file.txt", 0, 42);
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
"/tmp/check_csync1/file.txt",
|
||||
fs,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
/* the instruction should be set to new */
|
||||
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
|
||||
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
|
||||
|
||||
/* create a statedb */
|
||||
csync_set_status(csync, 0xFFFF);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
|
||||
static void check_csync_detect_update_db_rename(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
// csync_file_stat_t *st;
|
||||
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc = 0;
|
||||
|
||||
fs = create_fstat("wurst.txt", 0, 42);
|
||||
assert_non_null(fs);
|
||||
csync_set_statedb_exists(csync, 1);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
"/tmp/check_csync1/wurst.txt",
|
||||
fs,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
/* the instruction should be set to rename */
|
||||
/*
|
||||
* temporarily broken.
|
||||
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
|
||||
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_RENAME);
|
||||
|
||||
st->instruction = CSYNC_INSTRUCTION_UPDATED;
|
||||
*/
|
||||
/* create a statedb */
|
||||
csync_set_status(csync, 0xFFFF);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
static void check_csync_detect_update_db_new(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
csync_file_stat_t *st;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = create_fstat("file.txt", 42000, 0);
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
"/tmp/check_csync1/file.txt",
|
||||
fs,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
/* the instruction should be set to new */
|
||||
st = (csync_file_stat_t*)c_rbtree_node_data(csync->local.tree->root);
|
||||
assert_int_equal(st->instruction, CSYNC_INSTRUCTION_NEW);
|
||||
|
||||
|
||||
/* create a statedb */
|
||||
csync_set_status(csync, 0xFFFF);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
static void check_csync_detect_update_null(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = create_fstat("file.txt", 0, 0);
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
NULL,
|
||||
fs,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, -1);
|
||||
|
||||
rc = _csync_detect_update(csync,
|
||||
"/tmp/check_csync1/file.txt",
|
||||
NULL,
|
||||
CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, -1);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
static void check_csync_ftw(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
int rc;
|
||||
|
||||
rc = csync_ftw(csync, "/tmp", csync_walker, MAX_DEPTH);
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
static void check_csync_ftw_empty_uri(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
int rc;
|
||||
|
||||
rc = csync_ftw(csync, "", csync_walker, MAX_DEPTH);
|
||||
assert_int_equal(rc, -1);
|
||||
}
|
||||
|
||||
static void check_csync_ftw_failing_fn(void **state)
|
||||
{
|
||||
CSYNC *csync = (CSYNC*)*state;
|
||||
int rc;
|
||||
|
||||
rc = csync_ftw(csync, "/tmp", failing_fn, MAX_DEPTH);
|
||||
assert_int_equal(rc, -1);
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update, setup, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_none, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_eval, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_rename, setup, teardown),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_db_new, setup, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_detect_update_null, setup, teardown_rm),
|
||||
|
||||
cmocka_unit_test_setup_teardown(check_csync_ftw, setup_ftw, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_ftw_empty_uri, setup_ftw, teardown_rm),
|
||||
cmocka_unit_test_setup_teardown(check_csync_ftw_failing_fn, setup_ftw, teardown_rm),
|
||||
};
|
||||
|
||||
return cmocka_run_group_tests(tests, NULL, NULL);
|
||||
}
|
||||
|
||||
}
|
|
@ -77,6 +77,123 @@ X-GNOME-Autostart-Delay=3
|
|||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
GenericName[oc]=Dorsièr de Sincronizacion
|
||||
|
@ -160,6 +277,7 @@ Icon[it]=@APPLICATION_EXECUTABLE@
|
|||
Comment[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
GenericName[ko]=폴더 동기화
|
||||
Name[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
Icon[ko]=@APPLICATION_EXECUTABLE@
|
||||
Comment[hu_HU]=@APPLICATION_NAME@ asztali szinkronizációs kliens
|
||||
GenericName[hu_HU]=Könyvtár szinkronizálás
|
||||
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkr. kliens
|
||||
|
|
|
@ -23,3 +23,6 @@ if( UNIX AND NOT APPLE )
|
|||
add_subdirectory(libcloudproviders)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
add_subdirectory(windows)
|
||||
endif()
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
if(APPLE)
|
||||
# Contrary to popular belief, this is called like this no matter what theme/OEM.
|
||||
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/ownCloud.icns")
|
||||
set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns")
|
||||
|
||||
# The bundle identifier and application group need to have compatible values with the client
|
||||
# to be able to open a Mach port across the extension's sandbox boundary.
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
#include <KCoreAddons/KPluginFactory>
|
||||
#include <KCoreAddons/KPluginLoader>
|
||||
#include <KIOWidgets/kabstractfileitemactionplugin.h>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <KIOCore/kfileitem.h>
|
||||
|
@ -27,10 +27,12 @@
|
|||
#include <QtWidgets/QMenu>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QEventLoop>
|
||||
#include "ownclouddolphinpluginhelper.h"
|
||||
|
||||
class OwncloudDolphinPluginAction : public KAbstractFileItemActionPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OwncloudDolphinPluginAction(QObject* parent, const QList<QVariant>&)
|
||||
: KAbstractFileItemActionPlugin(parent) { }
|
||||
|
@ -38,22 +40,72 @@ public:
|
|||
QList<QAction*> actions(const KFileItemListProperties& fileItemInfos, QWidget* parentWidget) Q_DECL_OVERRIDE
|
||||
{
|
||||
auto helper = OwncloudDolphinPluginHelper::instance();
|
||||
QList<QUrl> urls = fileItemInfos.urlList();
|
||||
if (urls.count() != 1 || !helper->isConnected())
|
||||
if (!helper->isConnected() || !fileItemInfos.isLocal())
|
||||
return {};
|
||||
|
||||
auto url = urls.first();
|
||||
if (!url.isLocalFile())
|
||||
return {};
|
||||
QDir localPath(url.toLocalFile());
|
||||
auto localFile = localPath.canonicalPath();
|
||||
|
||||
// If any of the url is outside of a sync folder, return an empty menu.
|
||||
const QList<QUrl> urls = fileItemInfos.urlList();
|
||||
const auto paths = helper->paths();
|
||||
if (!std::any_of(paths.begin(), paths.end(), [&](const QString &s) {
|
||||
return localFile.startsWith(s);
|
||||
} ))
|
||||
return {};
|
||||
QByteArray files;
|
||||
for (const auto &url : urls) {
|
||||
QDir localPath(url.toLocalFile());
|
||||
auto localFile = localPath.canonicalPath();
|
||||
if (!std::any_of(paths.begin(), paths.end(), [&](const QString &s) {
|
||||
return localFile.startsWith(s);
|
||||
}))
|
||||
return {};
|
||||
|
||||
if (!files.isEmpty())
|
||||
files += '\x1e'; // Record separator
|
||||
files += localFile.toUtf8();
|
||||
}
|
||||
|
||||
if (helper->version() < "1.1") { // in this case, lexicographic order works
|
||||
return legacyActions(fileItemInfos, parentWidget);
|
||||
}
|
||||
|
||||
auto menu = new QMenu(parentWidget);
|
||||
QEventLoop loop;
|
||||
auto con = connect(helper, &OwncloudDolphinPluginHelper::commandRecieved, this, [&](const QByteArray &cmd) {
|
||||
if (cmd.startsWith("GET_MENU_ITEMS:END")) {
|
||||
loop.quit();
|
||||
} else if (cmd.startsWith("MENU_ITEM:")) {
|
||||
auto args = QString::fromUtf8(cmd).split(QLatin1Char(':'));
|
||||
if (args.size() < 4)
|
||||
return;
|
||||
auto action = menu->addAction(args.mid(3).join(QLatin1Char(':')));
|
||||
if (args.value(2).contains(QLatin1Char('d')))
|
||||
action->setDisabled(true);
|
||||
auto call = args.value(1).toLatin1();
|
||||
connect(action, &QAction::triggered, [helper, call, files] {
|
||||
helper->sendCommand(QByteArray(call + ":" + files + "\n"));
|
||||
});
|
||||
}
|
||||
});
|
||||
QTimer::singleShot(100, &loop, SLOT(quit())); // add a timeout to be sure we don't freeze dolphin
|
||||
helper->sendCommand(QByteArray("GET_MENU_ITEMS:" + files + "\n"));
|
||||
loop.exec(QEventLoop::ExcludeUserInputEvents);
|
||||
disconnect(con);
|
||||
if (menu->actions().isEmpty()) {
|
||||
delete menu;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto menuaction = new QAction(parentWidget);
|
||||
menuaction->setText(helper->contextMenuTitle());
|
||||
menuaction->setMenu(menu);
|
||||
return { menuaction };
|
||||
}
|
||||
|
||||
|
||||
QList<QAction *> legacyActions(const KFileItemListProperties &fileItemInfos, QWidget *parentWidget)
|
||||
{
|
||||
QList<QUrl> urls = fileItemInfos.urlList();
|
||||
if (urls.count() != 1)
|
||||
return {};
|
||||
QDir localPath(urls.first().toLocalFile());
|
||||
auto localFile = localPath.canonicalPath();
|
||||
auto helper = OwncloudDolphinPluginHelper::instance();
|
||||
auto menuaction = new QAction(parentWidget);
|
||||
menuaction->setText(helper->contextMenuTitle());
|
||||
auto menu = new QMenu(parentWidget);
|
||||
|
@ -61,8 +113,8 @@ public:
|
|||
|
||||
auto shareAction = menu->addAction(helper->shareActionTitle());
|
||||
connect(shareAction, &QAction::triggered, this, [localFile, helper] {
|
||||
helper->sendCommand(QByteArray("SHARE:"+localFile.toUtf8()+"\n"));
|
||||
} );
|
||||
helper->sendCommand(QByteArray("SHARE:" + localFile.toUtf8() + "\n"));
|
||||
});
|
||||
|
||||
if (!helper->copyPrivateLinkTitle().isEmpty()) {
|
||||
auto copyPrivateLinkAction = menu->addAction(helper->copyPrivateLinkTitle());
|
||||
|
@ -77,7 +129,6 @@ public:
|
|||
helper->sendCommand(QByteArray("EMAIL_PRIVATE_LINK:" + localFile.toUtf8() + "\n"));
|
||||
});
|
||||
}
|
||||
|
||||
return { menuaction };
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ void OwncloudDolphinPluginHelper::sendCommand(const char* data)
|
|||
|
||||
void OwncloudDolphinPluginHelper::slotConnected()
|
||||
{
|
||||
sendCommand("VERSION:\n");
|
||||
sendCommand("GET_STRINGS:\n");
|
||||
}
|
||||
|
||||
|
@ -98,6 +99,16 @@ void OwncloudDolphinPluginHelper::slotReadyRead()
|
|||
_strings[args[1]] = args.mid(2).join(QLatin1Char(':'));
|
||||
}
|
||||
continue;
|
||||
} else if (line.startsWith("VERSION:")) {
|
||||
auto args = line.split(':');
|
||||
auto version = args.value(2);
|
||||
_version = version;
|
||||
if (!version.startsWith("1.")) {
|
||||
// Incompatible version, disconnect forever
|
||||
_connectTimer.stop();
|
||||
_socket.disconnectFromServer();
|
||||
return;
|
||||
}
|
||||
}
|
||||
emit commandRecieved(line);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <QObject>
|
||||
#include <QBasicTimer>
|
||||
#include <QLocalSocket>
|
||||
#include <QRegularExpression>
|
||||
#include "ownclouddolphinpluginhelper_export.h"
|
||||
|
||||
class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QObject {
|
||||
|
@ -44,6 +45,8 @@ public:
|
|||
QString copyPrivateLinkTitle() const { return _strings["COPY_PRIVATE_LINK_MENU_TITLE"]; }
|
||||
QString emailPrivateLinkTitle() const { return _strings["EMAIL_PRIVATE_LINK_MENU_TITLE"]; }
|
||||
|
||||
QByteArray version() { return _version; }
|
||||
|
||||
signals:
|
||||
void commandRecieved(const QByteArray &cmd);
|
||||
|
||||
|
@ -61,4 +64,5 @@ private:
|
|||
QBasicTimer _connectTimer;
|
||||
|
||||
QMap<QString, QString> _strings;
|
||||
QByteArray _version;
|
||||
};
|
||||
|
|
|
@ -41,7 +41,7 @@ macro(libcloudproviders_add_config _sources)
|
|||
endmacro(libcloudproviders_add_config _sources)
|
||||
|
||||
|
||||
IF (UNIX AND WITH_DBUS AND LIBCLOUDPROVIDERS_FOUND)
|
||||
IF (UNIX AND Qt5DBus_FOUND AND LIBCLOUDPROVIDERS_FOUND)
|
||||
STRING(TOLOWER "${APPLICATION_VENDOR}" DBUS_VENDOR)
|
||||
STRING(REGEX REPLACE "[^A-z0-9]" "" DBUS_VENDOR "${DBUS_VENDOR}")
|
||||
STRING(REGEX REPLACE "[^A-z0-9]" "" DBUS_APPLICATION_NAME "${APPLICATION_SHORTNAME}")
|
||||
|
|
|
@ -15,10 +15,16 @@
|
|||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# for more details.
|
||||
|
||||
import sys
|
||||
python3 = sys.version_info[0] >= 3
|
||||
|
||||
import os
|
||||
import urllib
|
||||
if python3:
|
||||
import urllib.parse
|
||||
import socket
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from gi.repository import GObject, Nautilus
|
||||
|
||||
|
@ -30,11 +36,11 @@ appname = 'ownCloud'
|
|||
|
||||
print("Initializing "+appname+"-client-nautilus extension")
|
||||
|
||||
|
||||
def get_local_path(url):
|
||||
if url[0:7] == 'file://':
|
||||
url = url[7:]
|
||||
return urllib.unquote(url)
|
||||
unquote = urllib.parse.unquote if python3 else urllib.unquote
|
||||
return unquote(url)
|
||||
|
||||
def get_runtime_dir():
|
||||
"""Returns the value of $XDG_RUNTIME_DIR, a directory path.
|
||||
|
@ -55,8 +61,9 @@ class SocketConnect(GObject.GObject):
|
|||
self.registered_paths = {}
|
||||
self._watch_id = 0
|
||||
self._sock = None
|
||||
self._listeners = [self._update_registered_paths]
|
||||
self._remainder = ''
|
||||
self._listeners = [self._update_registered_paths, self._get_version]
|
||||
self._remainder = ''.encode()
|
||||
self.protocolVersion = '1.0'
|
||||
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
||||
# all over the other objects.
|
||||
|
||||
|
@ -74,7 +81,7 @@ class SocketConnect(GObject.GObject):
|
|||
# print("Server command: " + cmd)
|
||||
if self.connected:
|
||||
try:
|
||||
self._sock.send(cmd)
|
||||
self._sock.send(cmd.encode())
|
||||
except:
|
||||
print("Sending failed.")
|
||||
self.reconnect()
|
||||
|
@ -96,6 +103,7 @@ class SocketConnect(GObject.GObject):
|
|||
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
|
||||
print("Socket watch id: " + str(self._watch_id))
|
||||
|
||||
self.sendCommand('VERSION:\n')
|
||||
self.sendCommand('GET_STRINGS:\n')
|
||||
|
||||
return False # Don't run again
|
||||
|
@ -107,29 +115,43 @@ class SocketConnect(GObject.GObject):
|
|||
|
||||
return True # Run again, if enabled via timeout_add()
|
||||
|
||||
# Reads data that becomes available.
|
||||
# New responses can be accessed with get_available_responses().
|
||||
# Returns false if no data was received within timeout
|
||||
def read_socket_data_with_timeout(self, timeout):
|
||||
self._sock.settimeout(timeout)
|
||||
try:
|
||||
self._remainder += self._sock.recv(1024)
|
||||
except socket.timeout:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
finally:
|
||||
self._sock.settimeout(None)
|
||||
|
||||
# Parses response lines out of collected data, returns list of strings
|
||||
def get_available_responses(self):
|
||||
end = self._remainder.rfind('\n'.encode())
|
||||
if end == -1:
|
||||
return []
|
||||
data = self._remainder[:end]
|
||||
self._remainder = self._remainder[end+1:]
|
||||
return data.decode().split('\n')
|
||||
|
||||
# Notify is the raw answer from the socket
|
||||
def _handle_notify(self, source, condition):
|
||||
data = source.recv(1024)
|
||||
# Prepend the remaining data from last call
|
||||
if len(self._remainder) > 0:
|
||||
data = self._remainder + data
|
||||
self._remainder = ''
|
||||
# Blocking is ok since we're notified of available data
|
||||
self._remainder += self._sock.recv(1024)
|
||||
|
||||
if len(data) > 0:
|
||||
# Remember the remainder for next round
|
||||
lastNL = data.rfind('\n');
|
||||
if lastNL > -1 and lastNL < len(data):
|
||||
self._remainder = data[lastNL+1:]
|
||||
data = data[:lastNL]
|
||||
|
||||
for l in data.split('\n'):
|
||||
self._handle_server_response(l)
|
||||
else:
|
||||
if len(self._remainder) == 0:
|
||||
return False
|
||||
|
||||
for line in self.get_available_responses():
|
||||
self.handle_server_response(line)
|
||||
|
||||
return True # Run again
|
||||
|
||||
def _handle_server_response(self, line):
|
||||
def handle_server_response(self, line):
|
||||
print("Server response: " + line)
|
||||
parts = line.split(':')
|
||||
action = parts[0]
|
||||
|
@ -149,6 +171,10 @@ class SocketConnect(GObject.GObject):
|
|||
if not self.registered_paths:
|
||||
self.reconnect()
|
||||
|
||||
def _get_version(self, action, args):
|
||||
if action == 'VERSION':
|
||||
self.protocolVersion = args[1]
|
||||
|
||||
socketConnect = SocketConnect()
|
||||
|
||||
|
||||
|
@ -178,29 +204,89 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
|||
|
||||
def get_file_items(self, window, files):
|
||||
# Show the menu extension to share a file or folder
|
||||
#
|
||||
# Show if file is OK.
|
||||
# Ignore top level folders.
|
||||
# Also show extension for folders
|
||||
# if there is a OK or SYNC underneath.
|
||||
# This is only
|
||||
|
||||
if len(files) != 1:
|
||||
return
|
||||
file = files[0]
|
||||
# Get usable file paths from the uris
|
||||
all_internal_files = True
|
||||
for i, file_uri in enumerate(files):
|
||||
filename = get_local_path(file_uri.get_uri())
|
||||
|
||||
filename = get_local_path(file.get_uri())
|
||||
# Check if its a folder (ends with an /), if yes add a "/"
|
||||
# otherwise it will not find the entry in the table
|
||||
isDir = os.path.isdir(filename + os.sep)
|
||||
if isDir:
|
||||
filename += os.sep
|
||||
# Check if its a folder (ends with an /), if yes add a "/"
|
||||
# otherwise it will not find the entry in the table
|
||||
isDir = os.path.isdir(filename + os.sep)
|
||||
if isDir:
|
||||
filename += os.sep
|
||||
|
||||
# Check if toplevel folder, we need to ignore those as they cannot be shared
|
||||
topLevelFolder, internalFile = self.check_registered_paths(filename)
|
||||
if topLevelFolder or not internalFile:
|
||||
# Check if toplevel folder, we need to ignore those as they cannot be shared
|
||||
topLevelFolder, internalFile = self.check_registered_paths(filename)
|
||||
if not internalFile:
|
||||
all_internal_files = False
|
||||
|
||||
files[i] = filename
|
||||
|
||||
# Don't show a context menu if some selected files aren't in a sync folder
|
||||
if not all_internal_files:
|
||||
return []
|
||||
|
||||
if socketConnect.protocolVersion >= '1.1': # lexicographic!
|
||||
return self.ask_for_menu_items(files)
|
||||
else:
|
||||
return self.legacy_menu_items(files)
|
||||
|
||||
def ask_for_menu_items(self, files):
|
||||
record_separator = '\x1e'
|
||||
filesstring = record_separator.join(files)
|
||||
socketConnect.sendCommand('GET_MENU_ITEMS:{}\n'.format(filesstring))
|
||||
|
||||
done = False
|
||||
start = time.time()
|
||||
timeout = 0.1 # 100ms
|
||||
menu_items = []
|
||||
while not done:
|
||||
dt = time.time() - start
|
||||
if dt >= timeout:
|
||||
break
|
||||
if not socketConnect.read_socket_data_with_timeout(timeout - dt):
|
||||
break
|
||||
for line in socketConnect.get_available_responses():
|
||||
# Process lines we don't care about
|
||||
if done or not (line.startswith('GET_MENU_ITEMS:') or line.startswith('MENU_ITEM:')):
|
||||
socketConnect.handle_server_response(line)
|
||||
continue
|
||||
if line == 'GET_MENU_ITEMS:END':
|
||||
done = True
|
||||
# don't break - we'd discard other responses
|
||||
if line.startswith('MENU_ITEM:'):
|
||||
args = line.split(':')
|
||||
if len(args) < 4:
|
||||
continue
|
||||
menu_items.append([args[1], 'd' not in args[2], ':'.join(args[3:])])
|
||||
|
||||
if not done:
|
||||
return self.legacy_menu_items(files)
|
||||
|
||||
if len(menu_items) == 0:
|
||||
return []
|
||||
|
||||
# Set up the 'ownCloud...' submenu
|
||||
item_owncloud = Nautilus.MenuItem(
|
||||
name='IntegrationMenu', label=self.strings.get('CONTEXT_MENU_TITLE', appname))
|
||||
menu = Nautilus.Menu()
|
||||
item_owncloud.set_submenu(menu)
|
||||
|
||||
for action, enabled, label in menu_items:
|
||||
item = Nautilus.MenuItem(name=action, label=label, sensitive=enabled)
|
||||
item.connect("activate", self.context_menu_action, action, filesstring)
|
||||
menu.append_item(item)
|
||||
|
||||
return [item_owncloud]
|
||||
|
||||
|
||||
def legacy_menu_items(self, files):
|
||||
# No legacy menu for a selection of several files
|
||||
if len(files) != 1:
|
||||
return []
|
||||
filename = files[0]
|
||||
|
||||
entry = socketConnect.nautilusVFSFile_table.get(filename)
|
||||
if not entry:
|
||||
return []
|
||||
|
@ -235,7 +321,7 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
|||
item = Nautilus.MenuItem(
|
||||
name='NautilusPython::ShareItem',
|
||||
label=self.strings.get('SHARE_MENU_TITLE', 'Share...'))
|
||||
item.connect("activate", self.context_menu_action, 'SHARE', file)
|
||||
item.connect("activate", self.context_menu_action, 'SHARE', filename)
|
||||
menu.append_item(item)
|
||||
|
||||
# Add permalink menu options, but hide these options for older clients
|
||||
|
@ -243,20 +329,19 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
|||
if 'COPY_PRIVATE_LINK_MENU_TITLE' in self.strings:
|
||||
item_copyprivatelink = Nautilus.MenuItem(
|
||||
name='CopyPrivateLink', label=self.strings.get('COPY_PRIVATE_LINK_MENU_TITLE', 'Copy private link to clipboard'))
|
||||
item_copyprivatelink.connect("activate", self.context_menu_action, 'COPY_PRIVATE_LINK', file)
|
||||
item_copyprivatelink.connect("activate", self.context_menu_action, 'COPY_PRIVATE_LINK', filename)
|
||||
menu.append_item(item_copyprivatelink)
|
||||
|
||||
if 'EMAIL_PRIVATE_LINK_MENU_TITLE' in self.strings:
|
||||
item_emailprivatelink = Nautilus.MenuItem(
|
||||
name='EmailPrivateLink', label=self.strings.get('EMAIL_PRIVATE_LINK_MENU_TITLE', 'Send private link by email...'))
|
||||
item_emailprivatelink.connect("activate", self.context_menu_action, 'EMAIL_PRIVATE_LINK', file)
|
||||
item_emailprivatelink.connect("activate", self.context_menu_action, 'EMAIL_PRIVATE_LINK', filename)
|
||||
menu.append_item(item_emailprivatelink)
|
||||
|
||||
return [item_owncloud]
|
||||
|
||||
|
||||
def context_menu_action(self, menu, action, file):
|
||||
filename = get_local_path(file.get_uri())
|
||||
def context_menu_action(self, menu, action, filename):
|
||||
print("Context menu: " + action + ' ' + filename)
|
||||
socketConnect.sendCommand(action + ":" + filename + "\n")
|
||||
|
||||
|
|
4
shell_integration/windows/CMakeLists.txt
Normal file
4
shell_integration/windows/CMakeLists.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
add_subdirectory(OCContextMenu)
|
||||
add_subdirectory(OCOverlays)
|
||||
add_subdirectory(OCUtil)
|
||||
|
18
shell_integration/windows/OCContextMenu/CMakeLists.txt
Normal file
18
shell_integration/windows/OCContextMenu/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
add_library(OCContextMenu MODULE
|
||||
dllmain.cpp
|
||||
OCClientInterface.cpp
|
||||
OCContextMenu.cpp
|
||||
OCContextMenuFactory.cpp
|
||||
OCContextMenuRegHandler.cpp
|
||||
stdafx.cpp
|
||||
OCContextMenu.rc
|
||||
OCContextMenu.def
|
||||
)
|
||||
|
||||
target_link_libraries(OCContextMenu
|
||||
OCUtil)
|
||||
|
||||
install(TARGETS OCContextMenu
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
|
@ -34,7 +34,7 @@ using namespace std;
|
|||
#define PIPE_TIMEOUT 5*1000 //ms
|
||||
#define SOCK_BUFFER 4096
|
||||
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo(const std::wstring &files)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
|
@ -45,7 +45,8 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
|||
if (!socket.Connect(pipename)) {
|
||||
return {};
|
||||
}
|
||||
socket.SendMsg(L"GET_STRINGS\n");
|
||||
socket.SendMsg(L"GET_STRINGS:CONTEXT_MENU_TITLE\n");
|
||||
socket.SendMsg((L"GET_MENU_ITEMS:" + files + L"\n").data());
|
||||
|
||||
ContextMenuInfo info;
|
||||
std::wstring response;
|
||||
|
@ -60,16 +61,14 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
|||
wstring stringName, stringValue;
|
||||
if (!StringUtil::extractChunks(response, stringName, stringValue))
|
||||
continue;
|
||||
if (stringName == L"SHARE_MENU_TITLE")
|
||||
info.shareMenuTitle = move(stringValue);
|
||||
else if (stringName == L"CONTEXT_MENU_TITLE")
|
||||
if (stringName == L"CONTEXT_MENU_TITLE")
|
||||
info.contextMenuTitle = move(stringValue);
|
||||
else if (stringName == L"COPY_PRIVATE_LINK_MENU_TITLE")
|
||||
info.copyLinkMenuTitle = move(stringValue);
|
||||
else if (stringName == L"EMAIL_PRIVATE_LINK_MENU_TITLE")
|
||||
info.emailLinkMenuTitle = move(stringValue);
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"GET_STRINGS:END"))) {
|
||||
} else if (StringUtil::begins_with(response, wstring(L"MENU_ITEM:"))) {
|
||||
wstring commandName, flags, title;
|
||||
if (!StringUtil::extractChunks(response, commandName, flags, title))
|
||||
continue;
|
||||
info.menuItems.push_back({ commandName, flags, title });
|
||||
} else if (StringUtil::begins_with(response, wstring(L"GET_MENU_ITEMS:END"))) {
|
||||
break; // Stop once we completely received the last sent request
|
||||
}
|
||||
}
|
||||
|
@ -81,22 +80,7 @@ OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo()
|
|||
return info;
|
||||
}
|
||||
|
||||
void OCClientInterface::RequestShare(const std::wstring &path)
|
||||
{
|
||||
SendRequest(L"SHARE", path);
|
||||
}
|
||||
|
||||
void OCClientInterface::RequestCopyLink(const std::wstring &path)
|
||||
{
|
||||
SendRequest(L"COPY_PRIVATE_LINK", path);
|
||||
}
|
||||
|
||||
void OCClientInterface::RequestEmailLink(const std::wstring &path)
|
||||
{
|
||||
SendRequest(L"EMAIL_PRIVATE_LINK", path);
|
||||
}
|
||||
|
||||
void OCClientInterface::SendRequest(wchar_t *verb, const std::wstring &path)
|
||||
void OCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &path)
|
||||
{
|
||||
auto pipename = CommunicationSocket::DefaultPipePath();
|
||||
|
||||
|
|
|
@ -46,18 +46,14 @@ public:
|
|||
struct ContextMenuInfo {
|
||||
std::vector<std::wstring> watchedDirectories;
|
||||
std::wstring contextMenuTitle;
|
||||
std::wstring shareMenuTitle;
|
||||
std::wstring copyLinkMenuTitle;
|
||||
std::wstring emailLinkMenuTitle;
|
||||
struct MenuItem
|
||||
{
|
||||
std::wstring command, flags, title;
|
||||
};
|
||||
std::vector<MenuItem> menuItems;
|
||||
};
|
||||
static ContextMenuInfo FetchInfo();
|
||||
|
||||
static void RequestShare(const std::wstring &path);
|
||||
static void RequestCopyLink(const std::wstring &path);
|
||||
static void RequestEmailLink(const std::wstring &path);
|
||||
|
||||
private:
|
||||
static void SendRequest(wchar_t *verb, const std::wstring &path);
|
||||
static ContextMenuInfo FetchInfo(const std::wstring &files);
|
||||
static void SendRequest(const wchar_t *verb, const std::wstring &path);
|
||||
};
|
||||
|
||||
#endif //ABSTRACTSOCKETHANDLER_H
|
||||
|
|
|
@ -22,13 +22,8 @@
|
|||
#include <shellapi.h>
|
||||
#include <StringUtil.h>
|
||||
|
||||
extern HINSTANCE g_hInst;
|
||||
extern long g_cDllRef;
|
||||
|
||||
#define IDM_SHARE 0
|
||||
#define IDM_COPYLINK 1
|
||||
#define IDM_EMAILLINK 2
|
||||
|
||||
OCContextMenu::OCContextMenu(void)
|
||||
: m_cRef(1)
|
||||
{
|
||||
|
@ -40,23 +35,6 @@ OCContextMenu::~OCContextMenu(void)
|
|||
InterlockedDecrement(&g_cDllRef);
|
||||
}
|
||||
|
||||
|
||||
void OCContextMenu::OnVerbShare(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::RequestShare(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
void OCContextMenu::OnVerbCopyLink(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::RequestCopyLink(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
void OCContextMenu::OnVerbEmailLink(HWND hWnd)
|
||||
{
|
||||
OCClientInterface::RequestEmailLink(std::wstring(m_szSelectedFile));
|
||||
}
|
||||
|
||||
|
||||
#pragma region IUnknown
|
||||
|
||||
// Query to the interface the component supported.
|
||||
|
@ -97,12 +75,12 @@ IFACEMETHODIMP_(ULONG) OCContextMenu::Release()
|
|||
IFACEMETHODIMP OCContextMenu::Initialize(
|
||||
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID)
|
||||
{
|
||||
m_selectedFiles.clear();
|
||||
|
||||
if (!pDataObj) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||
STGMEDIUM stm;
|
||||
|
||||
|
@ -110,14 +88,19 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
|||
// Get an HDROP handle.
|
||||
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
|
||||
if (hDrop) {
|
||||
// Ignore multi-selections
|
||||
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
|
||||
if (nFiles == 1) {
|
||||
for (int i = 0; i < nFiles; ++i) {
|
||||
// Get the path of the file.
|
||||
if (0 != DragQueryFile(hDrop, 0, m_szSelectedFile, ARRAYSIZE(m_szSelectedFile)))
|
||||
{
|
||||
hr = S_OK;
|
||||
wchar_t buffer[MAX_PATH];
|
||||
|
||||
if (!DragQueryFile(hDrop, i, buffer, ARRAYSIZE(buffer))) {
|
||||
m_selectedFiles.clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (i)
|
||||
m_selectedFiles += L'\x1e';
|
||||
m_selectedFiles += buffer;
|
||||
}
|
||||
|
||||
GlobalUnlock(stm.hGlobal);
|
||||
|
@ -128,7 +111,7 @@ IFACEMETHODIMP OCContextMenu::Initialize(
|
|||
|
||||
// If any value other than S_OK is returned from the method, the context
|
||||
// menu item is not displayed.
|
||||
return hr;
|
||||
return m_selectedFiles.empty() ? E_FAIL : S_OK;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
@ -153,17 +136,8 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
|
|||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
OCClientInterface::ContextMenuInfo info = OCClientInterface::FetchInfo();
|
||||
bool skip = true;
|
||||
size_t selectedFileLength = wcslen(m_szSelectedFile);
|
||||
for (const std::wstring path : info.watchedDirectories) {
|
||||
if (StringUtil::isDescendantOf(m_szSelectedFile, selectedFileLength, path)) {
|
||||
skip = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (skip) {
|
||||
m_info = OCClientInterface::FetchInfo(m_selectedFiles);
|
||||
if (m_info.menuItems.empty()) {
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
|
||||
}
|
||||
|
||||
|
@ -175,7 +149,7 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
|
|||
mii.fMask = MIIM_SUBMENU | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.hSubMenu = hSubmenu;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.contextMenuTitle[0];
|
||||
mii.dwTypeData = &m_info.contextMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hMenu, indexMenu++, TRUE, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
|
@ -183,133 +157,59 @@ IFACEMETHODIMP OCContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu, UINT
|
|||
InsertSeperator(hMenu, indexMenu++);
|
||||
|
||||
UINT indexSubMenu = 0;
|
||||
{
|
||||
assert(!info.shareMenuTitle.empty());
|
||||
for (auto &item : m_info.menuItems) {
|
||||
bool disabled = item.flags.find(L'd') != std::string::npos;
|
||||
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.wID = idCmdFirst + IDM_SHARE;
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING | MIIM_STATE;
|
||||
mii.wID = indexSubMenu;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.shareMenuTitle[0];
|
||||
mii.dwTypeData = &item.title[0];
|
||||
mii.fState = disabled ? MFS_DISABLED : MFS_ENABLED;
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu, true, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
indexSubMenu++;
|
||||
}
|
||||
{
|
||||
assert(!info.copyLinkMenuTitle.empty());
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.wID = idCmdFirst + IDM_COPYLINK;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.copyLinkMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
{
|
||||
assert(!info.emailLinkMenuTitle.empty());
|
||||
MENUITEMINFO mii = { sizeof(mii) };
|
||||
mii.fMask = MIIM_ID | MIIM_FTYPE | MIIM_STRING;
|
||||
mii.wID = idCmdFirst + IDM_EMAILLINK;
|
||||
mii.fType = MFT_STRING;
|
||||
mii.dwTypeData = &info.emailLinkMenuTitle[0];
|
||||
|
||||
if (!InsertMenuItem(hSubmenu, indexSubMenu++, TRUE, &mii))
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
|
||||
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
|
||||
// Set the code value to the offset of the largest command identifier
|
||||
// that was assigned, plus one (1).
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_EMAILLINK + 1));
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(indexSubMenu));
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
std::wstring command;
|
||||
|
||||
// For the Unicode case, if the high-order word is not zero, the
|
||||
// command's verb string is in lpcmi->lpVerbW.
|
||||
if (HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
|
||||
{
|
||||
// Is the verb supported by this context menu extension?
|
||||
if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocshare") == 0) {
|
||||
OnVerbShare(pici->hwnd);
|
||||
}
|
||||
else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"occopylink") == 0) {
|
||||
OnVerbCopyLink(pici->hwnd);
|
||||
}
|
||||
else if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, L"ocemaillink") == 0) {
|
||||
OnVerbEmailLink(pici->hwnd);
|
||||
}
|
||||
else {
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
command = ((CMINVOKECOMMANDINFOEX *)pici)->lpVerbW;
|
||||
} else {
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
|
||||
auto offset = LOWORD(pici->lpVerb);
|
||||
if (offset < m_info.menuItems.size())
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// If the command cannot be identified through the verb string, then
|
||||
// check the identifier offset.
|
||||
else
|
||||
{
|
||||
// Is the command identifier offset supported by this context menu
|
||||
// extension?
|
||||
if (LOWORD(pici->lpVerb) == IDM_SHARE) {
|
||||
OnVerbShare(pici->hwnd);
|
||||
}
|
||||
else if (LOWORD(pici->lpVerb) == IDM_COPYLINK) {
|
||||
OnVerbCopyLink(pici->hwnd);
|
||||
}
|
||||
else if (LOWORD(pici->lpVerb) == IDM_EMAILLINK) {
|
||||
OnVerbEmailLink(pici->hwnd);
|
||||
}
|
||||
else {
|
||||
// If the verb is not recognized by the context menu handler, it
|
||||
// must return E_FAIL to allow it to be passed on to the other
|
||||
// context menu handlers that might implement that verb.
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
command = m_info.menuItems[offset].command;
|
||||
}
|
||||
|
||||
OCClientInterface::SendRequest(command.data(), m_selectedFiles);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP OCContextMenu::GetCommandString(UINT_PTR idCommand,
|
||||
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
|
||||
{
|
||||
HRESULT hr = E_INVALIDARG;
|
||||
|
||||
switch (idCommand) {
|
||||
case IDM_SHARE:
|
||||
if (uFlags == GCS_VERBW) {
|
||||
// GCS_VERBW is an optional feature that enables a caller to
|
||||
// discover the canonical name for the verb passed in through
|
||||
// idCommand.
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
L"OCShareViaOC");
|
||||
}
|
||||
break;
|
||||
case IDM_COPYLINK:
|
||||
if (uFlags == GCS_VERBW) {
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
L"OCCopyLink");
|
||||
}
|
||||
break;
|
||||
case IDM_EMAILLINK:
|
||||
if (uFlags == GCS_VERBW) {
|
||||
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
L"OCEmailLink");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (idCommand < m_info.menuItems.size() && uFlags == GCS_VERBW) {
|
||||
return StringCchCopyW(reinterpret_cast<PWSTR>(pszName), cchMax,
|
||||
m_info.menuItems[idCommand].command.data());
|
||||
}
|
||||
|
||||
// If the idCommand or uFlags is not supported by this context menu
|
||||
// extension handler, return E_INVALIDARG.
|
||||
|
||||
return hr;
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
#pragma endregion
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#pragma once
|
||||
#include <shlobj.h> // For IShellExtInit and IContextMenu
|
||||
#include <string>
|
||||
#include "OCClientInterface.h"
|
||||
|
||||
class OCContextMenu : public IShellExtInit, public IContextMenu
|
||||
{
|
||||
|
@ -43,21 +45,9 @@ private:
|
|||
// Reference count of component.
|
||||
long m_cRef;
|
||||
|
||||
// The name of the selected file.
|
||||
wchar_t m_szSelectedFile[MAX_PATH];
|
||||
|
||||
// The method that handles the "ocshare" verb.
|
||||
void OnVerbShare(HWND hWnd);
|
||||
void OnVerbCopyLink(HWND hWnd);
|
||||
void OnVerbEmailLink(HWND hWnd);
|
||||
|
||||
PWSTR m_pszMenuText;
|
||||
PCSTR m_pszVerb;
|
||||
PCWSTR m_pwszVerb;
|
||||
PCSTR m_pszVerbCanonicalName;
|
||||
PCWSTR m_pwszVerbCanonicalName;
|
||||
PCSTR m_pszVerbHelpText;
|
||||
PCWSTR m_pwszVerbHelpText;
|
||||
// The name of the selected files (separated by '\x1e')
|
||||
std::wstring m_selectedFiles;
|
||||
OCClientInterface::ContextMenuInfo m_info;
|
||||
};
|
||||
|
||||
#endif //OCCONTEXTMENU_H
|
||||
|
|
Binary file not shown.
17
shell_integration/windows/OCOverlays/CMakeLists.txt
Normal file
17
shell_integration/windows/OCOverlays/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
add_library(OCOverlays MODULE
|
||||
DllMain.cpp
|
||||
OCOverlay.cpp
|
||||
OCOverlayFactory.cpp
|
||||
OCOverlayRegistrationHandler.cpp
|
||||
stdafx.cpp
|
||||
OCOverlay.rc
|
||||
OCOverlays.def
|
||||
)
|
||||
|
||||
target_link_libraries(OCOverlays
|
||||
OCUtil)
|
||||
|
||||
install(TARGETS OCOverlays
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
Binary file not shown.
17
shell_integration/windows/OCUtil/CMakeLists.txt
Normal file
17
shell_integration/windows/OCUtil/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
add_library(OCUtil SHARED
|
||||
CommunicationSocket.cpp
|
||||
FileUtil.cpp
|
||||
RegistryUtil.cpp
|
||||
RemotePathChecker.cpp
|
||||
stdafx.cpp
|
||||
StringUtil.cpp
|
||||
)
|
||||
|
||||
target_include_directories(OCUtil
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
install(TARGETS OCUtil
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
|
@ -1,74 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
#include "OCMessage.h"
|
||||
#include "ParserUtil.h"
|
||||
#include "UtilConstants.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
OCMessage::OCMessage(void)
|
||||
{
|
||||
_command = new wstring();
|
||||
_value = new wstring();
|
||||
}
|
||||
|
||||
OCMessage::~OCMessage(void)
|
||||
{
|
||||
}
|
||||
|
||||
bool OCMessage::InitFromMessage(const wstring* message)
|
||||
{
|
||||
if(message->length() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ParserUtil::GetItem(COMMAND, message, _command))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ParserUtil::GetItem(VALUE, message, _value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring* OCMessage::GetCommand()
|
||||
{
|
||||
return _command;
|
||||
}
|
||||
|
||||
std::wstring* OCMessage::GetValue()
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
void OCMessage::SetCommand(std::wstring* command)
|
||||
{
|
||||
_command = command;
|
||||
}
|
||||
|
||||
void OCMessage::SetValue(std::wstring* value)
|
||||
{
|
||||
_value = value;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
#ifndef OCMESSAGE_H
|
||||
#define OCMESSAGE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#pragma once
|
||||
|
||||
class __declspec(dllexport) OCMessage
|
||||
{
|
||||
public:
|
||||
OCMessage(void);
|
||||
~OCMessage(void);
|
||||
|
||||
bool InitFromMessage(const std::wstring*);
|
||||
|
||||
std::wstring* GetCommand();
|
||||
std::wstring* GetValue();
|
||||
|
||||
void SetCommand(std::wstring*);
|
||||
void SetValue(std::wstring*);
|
||||
|
||||
private:
|
||||
|
||||
std::wstring* _command;
|
||||
std::wstring* _value;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,389 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; either version 2.1 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*/
|
||||
|
||||
#include "ParserUtil.h"
|
||||
#include "UtilConstants.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool ParserUtil::GetItem(const wchar_t* item, const wstring* message, wstring* result)
|
||||
{
|
||||
size_t start = message->find(item, 0);
|
||||
|
||||
if(start == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t end = message->find(COLON, start);
|
||||
|
||||
if(end == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Move to next character after :
|
||||
end += 1;
|
||||
|
||||
wchar_t c = message->at(end);
|
||||
|
||||
//Move to the next character, which is the start of the value
|
||||
end += 1;
|
||||
|
||||
if(c == '[')
|
||||
{
|
||||
return GetList(end - 1, message, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetValue(end, message, result);
|
||||
}
|
||||
}
|
||||
|
||||
bool ParserUtil::GetList(size_t start, const wstring* message, wstring* result)
|
||||
{
|
||||
size_t end = start + 1;
|
||||
|
||||
int openBraceCount = 1;
|
||||
|
||||
while(openBraceCount > 0)
|
||||
{
|
||||
size_t closeBraceLocation = message->find(CLOSE_BRACE, end);
|
||||
size_t openBraceLocation = message->find(OPEN_BRACE, end);
|
||||
|
||||
if(closeBraceLocation < openBraceLocation)
|
||||
{
|
||||
openBraceCount--;
|
||||
end = closeBraceLocation + 1;
|
||||
}
|
||||
else if(openBraceLocation < closeBraceLocation)
|
||||
{
|
||||
openBraceCount++;
|
||||
end = openBraceLocation + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
|
||||
return GetString(start, end, message, result);
|
||||
}
|
||||
|
||||
size_t ParserUtil::GetNextStringItemInList(const wstring* message, size_t start, wstring* result)
|
||||
{
|
||||
size_t end = string::npos;
|
||||
size_t commaLocation = message->find(COMMA, start);
|
||||
|
||||
if(commaLocation == string::npos)
|
||||
{
|
||||
end = message->find(CLOSE_BRACE, start);
|
||||
if(end == string::npos)
|
||||
{
|
||||
end = message->length();
|
||||
}
|
||||
else
|
||||
{
|
||||
end = end - 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
end = commaLocation - 1;
|
||||
}
|
||||
|
||||
if(!GetString(start + 2, end, message, result))
|
||||
{
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
return end + 2;
|
||||
}
|
||||
|
||||
size_t ParserUtil::GetNextOCItemInList(const wstring* message, size_t start, wstring* result)
|
||||
{
|
||||
size_t end = message->find(OPEN_CURLY_BRACE, start) + 1;
|
||||
|
||||
int openBraceCount = 1;
|
||||
|
||||
while(openBraceCount > 0)
|
||||
{
|
||||
size_t closeBraceLocation = message->find(CLOSE_CURLY_BRACE, end);
|
||||
size_t openBraceLocation = message->find(OPEN_CURLY_BRACE, end);
|
||||
|
||||
if(closeBraceLocation < openBraceLocation)
|
||||
{
|
||||
openBraceCount--;
|
||||
end = closeBraceLocation + 1;
|
||||
}
|
||||
else if(openBraceLocation < closeBraceLocation)
|
||||
{
|
||||
openBraceCount++;
|
||||
end = openBraceLocation + 1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
|
||||
if(!GetString(start, end, message, result))
|
||||
{
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
bool ParserUtil::GetValue(size_t start, const wstring* message, wstring* result)
|
||||
{
|
||||
if(message->at(start - 1) == '\"')
|
||||
{
|
||||
size_t end = message->find(QUOTE, start);
|
||||
return GetString(start, end, message, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
start = start - 1;
|
||||
|
||||
size_t end = message->find(COMMA, start);
|
||||
|
||||
result->append(message->substr(start, end-start));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::GetString(size_t start, size_t end, const wstring* message, wstring* result)
|
||||
{
|
||||
if(end == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t length = end - start;
|
||||
|
||||
if(length > 0)
|
||||
{
|
||||
result->append(message->substr(start, length));
|
||||
}
|
||||
else
|
||||
{
|
||||
result->append(L"");
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::IsList(wstring* message)
|
||||
{
|
||||
wchar_t c = message->at(0);
|
||||
|
||||
if(c == '[')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParserUtil::ParseJsonList(wstring* message, vector<wstring*>* items)
|
||||
{
|
||||
|
||||
size_t currentLocation = message->find(OPEN_BRACE, 0);
|
||||
|
||||
while(currentLocation < message->size())
|
||||
{
|
||||
wstring* item = new wstring();
|
||||
|
||||
currentLocation = ParserUtil::GetNextStringItemInList(message, currentLocation, item);
|
||||
|
||||
if(currentLocation == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
items->push_back(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::ParseOCList(wstring* message, vector<wstring*>* items)
|
||||
{
|
||||
|
||||
size_t currentLocation = message->find(OPEN_CURLY_BRACE, 0);
|
||||
|
||||
while(currentLocation < message->size())
|
||||
{
|
||||
wstring* item = new wstring();
|
||||
|
||||
currentLocation = ParserUtil::GetNextOCItemInList(message, currentLocation, item);
|
||||
|
||||
if(currentLocation == string::npos)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
items->push_back(item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::ParseOCMessageList(wstring* message, vector<OCMessage*>* messages)
|
||||
{
|
||||
vector<wstring*>* items = new vector<wstring*>();
|
||||
|
||||
if(!ParseOCList(message, items))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(vector<wstring*>::iterator it = items->begin(); it != items->end(); it++)
|
||||
{
|
||||
wstring* temp = *it;
|
||||
|
||||
OCMessage* message = new OCMessage();
|
||||
message->InitFromMessage(temp);
|
||||
|
||||
messages->push_back(message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::SerializeList(std::vector<std::wstring>* list, std::wstring* result, bool escapeQuotes)
|
||||
{
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(OPEN_BRACE);
|
||||
|
||||
for(vector<wstring>::iterator it = list->begin(); it != list->end(); it++)
|
||||
{
|
||||
wstring value = *it;
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(value.c_str());
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(COMMA);
|
||||
}
|
||||
|
||||
//Erase last comma
|
||||
result->erase(result->size() - 1, 1);
|
||||
|
||||
result->append(CLOSE_BRACE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::SerializeMessage(std::map<std::wstring*, std::wstring*>* arguments, std::wstring* result, bool escapeQuotes)
|
||||
{
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(OPEN_CURLY_BRACE);
|
||||
|
||||
for(map<wstring*, wstring*>::iterator it = arguments->begin(); it != arguments->end(); it++)
|
||||
{
|
||||
wstring key = *it->first;
|
||||
wstring value = *it->second;
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(key.c_str());
|
||||
|
||||
if(escapeQuotes)
|
||||
{
|
||||
result->append(BACK_SLASH);
|
||||
}
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(COLON);
|
||||
result->append(value.c_str());
|
||||
result->append(COMMA);
|
||||
}
|
||||
|
||||
//Erase last comma
|
||||
result->erase(result->size() - 1, 1);
|
||||
|
||||
result->append(CLOSE_CURLY_BRACE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ParserUtil::SerializeMessage(OCMessage* OCMessage, std::wstring* result)
|
||||
{
|
||||
if(result == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result->append(OPEN_CURLY_BRACE);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(COMMAND);
|
||||
result->append(QUOTE);
|
||||
|
||||
result->append(COLON);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(OCMessage->GetCommand()->c_str());
|
||||
result->append(QUOTE);
|
||||
|
||||
result->append(COMMA);
|
||||
|
||||
result->append(QUOTE);
|
||||
result->append(VALUE);
|
||||
result->append(QUOTE);
|
||||
|
||||
result->append(COLON);
|
||||
|
||||
if(!IsList(OCMessage->GetValue()))
|
||||
{
|
||||
result->append(QUOTE);
|
||||
}
|
||||
|
||||
result->append(OCMessage->GetValue()->c_str());
|
||||
|
||||
if(!IsList(OCMessage->GetValue()))
|
||||
{
|
||||
result->append(QUOTE);
|
||||
}
|
||||
|
||||
result->append(CLOSE_CURLY_BRACE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -61,6 +61,30 @@ public:
|
|||
thirdChunk = source.substr(statusEnd + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool extractChunks(const std::wstring &source, std::wstring &secondChunk, std::wstring &thirdChunk, std::wstring &forthChunk)
|
||||
{
|
||||
auto statusBegin = source.find(L':', 0);
|
||||
assert(statusBegin != std::wstring::npos);
|
||||
|
||||
auto statusEnd = source.find(L':', statusBegin + 1);
|
||||
if (statusEnd == std::wstring::npos) {
|
||||
// the command do not contains two colon?
|
||||
return false;
|
||||
}
|
||||
|
||||
auto thirdColon = source.find(L':', statusEnd + 1);
|
||||
if (statusEnd == std::wstring::npos) {
|
||||
// the command do not contains three colon?
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assume the caller extracted the chunk before the first colon.
|
||||
secondChunk = source.substr(statusBegin + 1, statusEnd - statusBegin - 1);
|
||||
thirdChunk = source.substr(statusEnd + 1, thirdColon - statusEnd - 1);
|
||||
forthChunk = source.substr(thirdColon + 1);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // STRINGUTIL_H
|
||||
|
|
2
src/3rdparty/libcrashreporter-qt
vendored
2
src/3rdparty/libcrashreporter-qt
vendored
|
@ -1 +1 @@
|
|||
Subproject commit e8fffe61e7c94ce88e59b80579754c4a46da65ea
|
||||
Subproject commit 7df66f72aac595295dffcf4dc8a536822008c51d
|
|
@ -4,12 +4,17 @@ endif()
|
|||
|
||||
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
|
||||
|
||||
find_package(Qt5 5.6 COMPONENTS Core Network Xml Concurrent REQUIRED)
|
||||
if (Qt5Core_VERSION VERSION_LESS 5.9.0)
|
||||
message(STATUS "For HTTP/2 support, compile with Qt 5.9 or higher.")
|
||||
endif()
|
||||
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5Keychain REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT MSVC)
|
||||
if(NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)"))
|
||||
if(NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "^(alpha|parisc|hppa)"))
|
||||
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9))
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector --param=ssp-buffer-size=4")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector --param=ssp-buffer-size=4")
|
||||
|
|
|
@ -7,17 +7,7 @@ set(cmd_SRC
|
|||
simplesslerrorhandler.cpp
|
||||
netrcparser.cpp
|
||||
)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/libsync
|
||||
${CMAKE_BINARY_DIR}/src/libsync
|
||||
)
|
||||
|
||||
# csync is required.
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/csync
|
||||
${CMAKE_BINARY_DIR}/src/csync
|
||||
)
|
||||
|
||||
# Need tokenizer for netrc parser
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/qtokenizer)
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
|
||||
|
@ -31,13 +21,15 @@ endif()
|
|||
|
||||
if(NOT BUILD_LIBRARIES_ONLY)
|
||||
add_executable(${cmd_NAME} ${cmd_SRC})
|
||||
qt5_use_modules(${cmd_NAME} Network )
|
||||
set_target_properties(${cmd_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
set_target_properties(${cmd_NAME} PROPERTIES
|
||||
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE};${CMAKE_INSTALL_RPATH}" )
|
||||
|
||||
target_link_libraries(${cmd_NAME} ${synclib_NAME})
|
||||
target_link_libraries(${cmd_NAME} ocsync ${synclib_NAME} Qt5::Core Qt5::Network)
|
||||
|
||||
# Need tokenizer for netrc parser
|
||||
target_include_directories(${cmd_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src/3rdparty/qtokenizer)
|
||||
endif()
|
||||
|
||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
|
@ -49,7 +41,6 @@ elseif(NOT BUILD_LIBRARIES_ONLY)
|
|||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
|
||||
# FIXME: The following lines are dup in src/gui and src/cmd because it needs to be done after both are installed
|
||||
#FIXME: find a nice solution to make the second if(BUILD_OWNCLOUD_OSX_BUNDLE) unnecessary
|
||||
# currently it needs to be done because the code right above needs to be executed no matter
|
||||
|
|
|
@ -331,7 +331,6 @@ int main(int argc, char **argv)
|
|||
|
||||
parseOptions(app.arguments(), &options);
|
||||
|
||||
csync_set_log_level(options.silent ? 1 : 11);
|
||||
if (options.silent) {
|
||||
qInstallMessageHandler(nullMessageHandler);
|
||||
} else {
|
||||
|
@ -420,6 +419,30 @@ int main(int argc, char **argv)
|
|||
folder.chop(1);
|
||||
}
|
||||
|
||||
if (!options.proxy.isNull()) {
|
||||
QString host;
|
||||
int port = 0;
|
||||
bool ok;
|
||||
|
||||
QStringList pList = options.proxy.split(':');
|
||||
if (pList.count() == 3) {
|
||||
// http: //192.168.178.23 : 8080
|
||||
// 0 1 2
|
||||
host = pList.at(1);
|
||||
if (host.startsWith("//"))
|
||||
host.remove(0, 2);
|
||||
|
||||
port = pList.at(2).toInt(&ok);
|
||||
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port));
|
||||
} else {
|
||||
qFatal("Could not read httpproxy. The proxy should have the format \"http://hostname:port\".");
|
||||
}
|
||||
} else {
|
||||
clientProxy.setupQtProxyFromConfig();
|
||||
}
|
||||
|
||||
SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler;
|
||||
|
||||
HttpCredentialsText *cred = new HttpCredentialsText(user, password);
|
||||
|
@ -457,36 +480,8 @@ int main(int argc, char **argv)
|
|||
int restartCount = 0;
|
||||
restart_sync:
|
||||
|
||||
|
||||
opts = &options;
|
||||
|
||||
if (!options.proxy.isNull()) {
|
||||
QString host;
|
||||
int port = 0;
|
||||
bool ok;
|
||||
|
||||
QStringList pList = options.proxy.split(':');
|
||||
if (pList.count() == 3) {
|
||||
// http: //192.168.178.23 : 8080
|
||||
// 0 1 2
|
||||
host = pList.at(1);
|
||||
if (host.startsWith("//"))
|
||||
host.remove(0, 2);
|
||||
|
||||
port = pList.at(2).toInt(&ok);
|
||||
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port));
|
||||
}
|
||||
} else {
|
||||
clientProxy.setupQtProxyFromConfig();
|
||||
QString url(options.target_url);
|
||||
if (url.startsWith("owncloud")) {
|
||||
url.remove(0, 8);
|
||||
url = QString("http%1").arg(url);
|
||||
}
|
||||
}
|
||||
|
||||
QStringList selectiveSyncList;
|
||||
if (!options.unsyncedfolders.isEmpty()) {
|
||||
QFile f(options.unsyncedfolders);
|
||||
|
|
|
@ -207,6 +207,7 @@ static inline uint64_t c_jhash64(const uint8_t *k, uint64_t length, uint64_t int
|
|||
/* handle the last 23 bytes */
|
||||
c += length;
|
||||
switch(len) {
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
case 23: c+=((uint64_t)k[22]<<56);
|
||||
case 22: c+=((uint64_t)k[21]<<48);
|
||||
case 21: c+=((uint64_t)k[20]<<40);
|
||||
|
|
|
@ -403,27 +403,6 @@ QByteArray FileSystem::calcAdler32(const QString &filename)
|
|||
}
|
||||
#endif
|
||||
|
||||
QString FileSystem::makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||
{
|
||||
QString conflictFileName(fn);
|
||||
// Add _conflict-XXXX before the extension.
|
||||
int dotLocation = conflictFileName.lastIndexOf('.');
|
||||
// If no extension, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
|
||||
if (dotLocation <= conflictFileName.lastIndexOf('/') + 1) {
|
||||
dotLocation = conflictFileName.size();
|
||||
}
|
||||
QString timeString = dt.toString("yyyyMMdd-hhmmss");
|
||||
|
||||
// Additional marker
|
||||
QByteArray conflictFileUserName = qgetenv("CSYNC_CONFLICT_FILE_USERNAME");
|
||||
if (conflictFileUserName.isEmpty())
|
||||
conflictFileName.insert(dotLocation, "_conflict-" + timeString);
|
||||
else
|
||||
conflictFileName.insert(dotLocation, "_conflict_" + QString::fromUtf8(conflictFileUserName) + "-" + timeString);
|
||||
|
||||
return conflictFileName;
|
||||
}
|
||||
|
||||
bool FileSystem::remove(const QString &fileName, QString *errorString)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
|
|
|
@ -131,11 +131,6 @@ namespace FileSystem {
|
|||
QByteArray OCSYNC_EXPORT calcAdler32(const QString &fileName);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns a file name based on \a fn that's suitable for a conflict.
|
||||
*/
|
||||
QString OCSYNC_EXPORT makeConflictFileName(const QString &fn, const QDateTime &dt);
|
||||
|
||||
/**
|
||||
* Returns true when a file is locked. (Windows only)
|
||||
*/
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "ownsql.h"
|
||||
#include "common/utility.h"
|
||||
#include "common/asserts.h"
|
||||
#include <sqlite3.h>
|
||||
|
||||
#define SQLITE_SLEEP_TIME_USEC 100000
|
||||
#define SQLITE_REPEAT_COUNT 20
|
||||
|
|
|
@ -19,13 +19,14 @@
|
|||
#ifndef OWNSQL_H
|
||||
#define OWNSQL_H
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
#include "ocsynclib.h"
|
||||
|
||||
struct sqlite3;
|
||||
struct sqlite3_stmt;
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QElapsedTimer>
|
||||
#include <QUrl>
|
||||
#include <QDir>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "common/syncjournaldb.h"
|
||||
#include "version.h"
|
||||
|
@ -47,7 +48,7 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que
|
|||
rec._path = query.baValue(0);
|
||||
rec._inode = query.int64Value(1);
|
||||
rec._modtime = query.int64Value(2);
|
||||
rec._type = query.intValue(3);
|
||||
rec._type = static_cast<ItemType>(query.intValue(3));
|
||||
rec._etag = query.baValue(4);
|
||||
rec._fileId = query.baValue(5);
|
||||
rec._remotePerm = RemotePermissions(query.baValue(6).constData());
|
||||
|
@ -389,6 +390,7 @@ bool SyncJournalDb::checkConnect()
|
|||
"errorcount INTEGER,"
|
||||
"size INTEGER(8),"
|
||||
"modtime INTEGER(8),"
|
||||
"contentChecksum TEXT,"
|
||||
"PRIMARY KEY(path)"
|
||||
");");
|
||||
|
||||
|
@ -437,7 +439,7 @@ bool SyncJournalDb::checkConnect()
|
|||
return sqlFail("Create table version", createQuery);
|
||||
}
|
||||
|
||||
// create the checksumtype table.
|
||||
// create the datafingerprint table.
|
||||
createQuery.prepare("CREATE TABLE IF NOT EXISTS datafingerprint("
|
||||
"fingerprint TEXT UNIQUE"
|
||||
");");
|
||||
|
@ -445,6 +447,17 @@ bool SyncJournalDb::checkConnect()
|
|||
return sqlFail("Create table datafingerprint", createQuery);
|
||||
}
|
||||
|
||||
// create the conflicts table.
|
||||
createQuery.prepare("CREATE TABLE IF NOT EXISTS conflicts("
|
||||
"path TEXT PRIMARY KEY,"
|
||||
"baseFileId TEXT,"
|
||||
"baseEtag TEXT,"
|
||||
"baseModtime INTEGER"
|
||||
");");
|
||||
if (!createQuery.exec()) {
|
||||
return sqlFail("Create table conflicts", createQuery);
|
||||
}
|
||||
|
||||
createQuery.prepare("CREATE TABLE IF NOT EXISTS version("
|
||||
"major INTEGER(8),"
|
||||
"minor INTEGER(8),"
|
||||
|
@ -611,15 +624,15 @@ bool SyncJournalDb::checkConnect()
|
|||
}
|
||||
|
||||
_getUploadInfoQuery.reset(new SqlQuery(_db));
|
||||
if (_getUploadInfoQuery->prepare("SELECT chunk, transferid, errorcount, size, modtime FROM "
|
||||
if (_getUploadInfoQuery->prepare("SELECT chunk, transferid, errorcount, size, modtime, contentChecksum FROM "
|
||||
"uploadinfo WHERE path=?1")) {
|
||||
return sqlFail("prepare _getUploadInfoQuery", *_getUploadInfoQuery);
|
||||
}
|
||||
|
||||
_setUploadInfoQuery.reset(new SqlQuery(_db));
|
||||
if (_setUploadInfoQuery->prepare("INSERT OR REPLACE INTO uploadinfo "
|
||||
"(path, chunk, transferid, errorcount, size, modtime) "
|
||||
"VALUES ( ?1 , ?2, ?3 , ?4 , ?5, ?6 )")) {
|
||||
"(path, chunk, transferid, errorcount, size, modtime, contentChecksum) "
|
||||
"VALUES ( ?1 , ?2, ?3 , ?4 , ?5, ?6 , ?7 )")) {
|
||||
return sqlFail("prepare _setUploadInfoQuery", *_setUploadInfoQuery);
|
||||
}
|
||||
|
||||
|
@ -692,6 +705,23 @@ bool SyncJournalDb::checkConnect()
|
|||
return sqlFail("prepare _setDataFingerprintQuery2", *_setDataFingerprintQuery2);
|
||||
}
|
||||
|
||||
_getConflictRecordQuery.reset(new SqlQuery(_db));
|
||||
if (_getConflictRecordQuery->prepare("SELECT baseFileId, baseModtime, baseEtag FROM conflicts WHERE path=?1;")) {
|
||||
return sqlFail("prepare _getConflictRecordQuery", *_getConflictRecordQuery);
|
||||
}
|
||||
|
||||
_setConflictRecordQuery.reset(new SqlQuery(_db));
|
||||
if (_setConflictRecordQuery->prepare("INSERT OR REPLACE INTO conflicts "
|
||||
"(path, baseFileId, baseModtime, baseEtag) "
|
||||
"VALUES (?1, ?2, ?3, ?4);")) {
|
||||
return sqlFail("prepare _setConflictRecordQuery", *_setConflictRecordQuery);
|
||||
}
|
||||
|
||||
_deleteConflictRecordQuery.reset(new SqlQuery(_db));
|
||||
if (_deleteConflictRecordQuery->prepare("DELETE FROM conflicts WHERE path=?1;")) {
|
||||
return sqlFail("prepare _deleteConflictRecordQuery", *_deleteConflictRecordQuery);
|
||||
}
|
||||
|
||||
// don't start a new transaction now
|
||||
commitInternal(QString("checkConnect End"), false);
|
||||
|
||||
|
@ -740,6 +770,9 @@ void SyncJournalDb::close()
|
|||
_getDataFingerprintQuery.reset(0);
|
||||
_setDataFingerprintQuery1.reset(0);
|
||||
_setDataFingerprintQuery2.reset(0);
|
||||
_getConflictRecordQuery.reset(0);
|
||||
_setConflictRecordQuery.reset(0);
|
||||
_deleteConflictRecordQuery.reset(0);
|
||||
|
||||
_db.close();
|
||||
_avoidReadFromDbOnNextSyncFilter.clear();
|
||||
|
@ -849,6 +882,16 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
|||
commitInternal("update database structure: add contentChecksumTypeId col");
|
||||
}
|
||||
|
||||
if (!tableColumns("uploadinfo").contains("contentChecksum")) {
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE uploadinfo ADD COLUMN contentChecksum TEXT;");
|
||||
if (!query.exec()) {
|
||||
sqlFail("updateMetadataTableStructure: add contentChecksum column", query);
|
||||
re = false;
|
||||
}
|
||||
commitInternal("update database structure: add contentChecksum col for uploadinfo");
|
||||
}
|
||||
|
||||
|
||||
return re;
|
||||
}
|
||||
|
@ -1472,6 +1515,7 @@ SyncJournalDb::UploadInfo SyncJournalDb::getUploadInfo(const QString &file)
|
|||
res._errorCount = _getUploadInfoQuery->intValue(2);
|
||||
res._size = _getUploadInfoQuery->int64Value(3);
|
||||
res._modtime = _getUploadInfoQuery->int64Value(4);
|
||||
res._contentChecksum = _getUploadInfoQuery->baValue(5);
|
||||
res._valid = ok;
|
||||
}
|
||||
}
|
||||
|
@ -1494,6 +1538,7 @@ void SyncJournalDb::setUploadInfo(const QString &file, const SyncJournalDb::Uplo
|
|||
_setUploadInfoQuery->bindValue(4, i._errorCount);
|
||||
_setUploadInfoQuery->bindValue(5, i._size);
|
||||
_setUploadInfoQuery->bindValue(6, i._modtime);
|
||||
_setUploadInfoQuery->bindValue(7, i._contentChecksum);
|
||||
|
||||
if (!_setUploadInfoQuery->exec()) {
|
||||
return;
|
||||
|
@ -1827,7 +1872,7 @@ void SyncJournalDb::avoidReadFromDbOnNextSync(const QByteArray &fileName)
|
|||
|
||||
SqlQuery query(_db);
|
||||
// This query will match entries for which the path is a prefix of fileName
|
||||
// Note: CSYNC_FTW_TYPE_DIR == 2
|
||||
// Note: ItemTypeDirectory == 2
|
||||
query.prepare("UPDATE metadata SET md5='_invalid_' WHERE ?1 LIKE(path||'/%') AND type == 2;");
|
||||
query.bindValue(1, fileName);
|
||||
query.exec();
|
||||
|
@ -1938,6 +1983,72 @@ void SyncJournalDb::setDataFingerprint(const QByteArray &dataFingerprint)
|
|||
_setDataFingerprintQuery2->exec();
|
||||
}
|
||||
|
||||
void SyncJournalDb::setConflictRecord(const ConflictRecord &record)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return;
|
||||
|
||||
auto &query = *_setConflictRecordQuery;
|
||||
query.reset_and_clear_bindings();
|
||||
query.bindValue(1, record.path);
|
||||
query.bindValue(2, record.baseFileId);
|
||||
query.bindValue(3, record.baseModtime);
|
||||
query.bindValue(4, record.baseEtag);
|
||||
ASSERT(query.exec());
|
||||
}
|
||||
|
||||
ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path)
|
||||
{
|
||||
ConflictRecord entry;
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return entry;
|
||||
|
||||
auto &query = *_getConflictRecordQuery;
|
||||
query.reset_and_clear_bindings();
|
||||
query.bindValue(1, path);
|
||||
ASSERT(query.exec());
|
||||
if (!query.next())
|
||||
return entry;
|
||||
|
||||
entry.path = path;
|
||||
entry.baseFileId = query.baValue(0);
|
||||
entry.baseModtime = query.int64Value(1);
|
||||
entry.baseEtag = query.baValue(2);
|
||||
return entry;
|
||||
}
|
||||
|
||||
void SyncJournalDb::deleteConflictRecord(const QByteArray &path)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return;
|
||||
|
||||
auto &query = *_deleteConflictRecordQuery;
|
||||
query.reset_and_clear_bindings();
|
||||
query.bindValue(1, path);
|
||||
ASSERT(query.exec());
|
||||
}
|
||||
|
||||
QByteArrayList SyncJournalDb::conflictRecordPaths()
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if (!checkConnect())
|
||||
return {};
|
||||
|
||||
SqlQuery query(_db);
|
||||
query.prepare("SELECT path FROM conflicts");
|
||||
ASSERT(query.exec());
|
||||
|
||||
QByteArrayList paths;
|
||||
while (query.next())
|
||||
paths.append(query.baValue(0));
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
void SyncJournalDb::clearFileTable()
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
|
@ -2001,7 +2112,8 @@ bool operator==(const SyncJournalDb::UploadInfo &lhs,
|
|||
&& lhs._modtime == rhs._modtime
|
||||
&& lhs._valid == rhs._valid
|
||||
&& lhs._size == rhs._size
|
||||
&& lhs._transferid == rhs._transferid;
|
||||
&& lhs._transferid == rhs._transferid
|
||||
&& lhs._contentChecksum == rhs._contentChecksum;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
|
|
@ -112,6 +112,7 @@ public:
|
|||
qint64 _modtime;
|
||||
int _errorCount;
|
||||
bool _valid;
|
||||
QByteArray _contentChecksum;
|
||||
};
|
||||
|
||||
struct PollInfo
|
||||
|
@ -206,6 +207,22 @@ public:
|
|||
void setDataFingerprint(const QByteArray &dataFingerprint);
|
||||
QByteArray dataFingerprint();
|
||||
|
||||
|
||||
// Conflict record functions
|
||||
|
||||
/// Store a new or updated record in the database
|
||||
void setConflictRecord(const ConflictRecord &record);
|
||||
|
||||
/// Retrieve a conflict record by path of the _conflict- file
|
||||
ConflictRecord conflictRecord(const QByteArray &path);
|
||||
|
||||
/// Delete a conflict record by path of the _conflict- file
|
||||
void deleteConflictRecord(const QByteArray &path);
|
||||
|
||||
/// Return all paths of _conflict- files with records in the db
|
||||
QByteArrayList conflictRecordPaths();
|
||||
|
||||
|
||||
/**
|
||||
* Delete any file entry. This will force the next sync to re-sync everything as if it was new,
|
||||
* restoring everyfile on every remote. If a file is there both on the client and server side,
|
||||
|
@ -265,6 +282,9 @@ private:
|
|||
QScopedPointer<SqlQuery> _getDataFingerprintQuery;
|
||||
QScopedPointer<SqlQuery> _setDataFingerprintQuery1;
|
||||
QScopedPointer<SqlQuery> _setDataFingerprintQuery2;
|
||||
QScopedPointer<SqlQuery> _getConflictRecordQuery;
|
||||
QScopedPointer<SqlQuery> _setConflictRecordQuery;
|
||||
QScopedPointer<SqlQuery> _deleteConflictRecordQuery;
|
||||
|
||||
/* This is the list of paths we called avoidReadFromDbOnNextSync on.
|
||||
* It means that they should not be written to the DB in any case since doing
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace OCC {
|
|||
|
||||
SyncJournalFileRecord::SyncJournalFileRecord()
|
||||
: _inode(0)
|
||||
, _type(0)
|
||||
, _type(ItemTypeSkip)
|
||||
, _fileSize(0)
|
||||
, _serverHasIgnoredFiles(false)
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QString>
|
||||
#include <QDateTime>
|
||||
|
||||
#include "csync.h"
|
||||
#include "ocsynclib.h"
|
||||
#include "remotepermissions.h"
|
||||
#include "common/utility.h"
|
||||
|
@ -56,7 +57,7 @@ public:
|
|||
QByteArray _path;
|
||||
quint64 _inode;
|
||||
qint64 _modtime;
|
||||
int _type;
|
||||
ItemType _type;
|
||||
QByteArray _etag;
|
||||
QByteArray _fileId;
|
||||
qint64 _fileSize;
|
||||
|
@ -110,6 +111,43 @@ public:
|
|||
|
||||
bool isValid() const;
|
||||
};
|
||||
|
||||
/** Represents a conflict in the conflicts table.
|
||||
*
|
||||
* In the following the "conflict file" is the file with the "_conflict-"
|
||||
* tag and the base file is the file that its a conflict for. So if
|
||||
* a/foo.txt is the base file, its conflict file could be
|
||||
* a/foo_conflict-1234.txt.
|
||||
*/
|
||||
class OCSYNC_EXPORT ConflictRecord
|
||||
{
|
||||
public:
|
||||
/** Path to the _conflict- file
|
||||
*
|
||||
* So if a/foo.txt has a conflict, this path would point to
|
||||
* a/foo_conflict-1234.txt.
|
||||
*
|
||||
* The path is sync-folder relative.
|
||||
*/
|
||||
QByteArray path;
|
||||
|
||||
/// File id of the base file
|
||||
QByteArray baseFileId;
|
||||
|
||||
/** Modtime of the base file
|
||||
*
|
||||
* may not be available and be -1
|
||||
*/
|
||||
qint64 baseModtime = -1;
|
||||
|
||||
/** Etag of the base file
|
||||
*
|
||||
* may not be available and empty
|
||||
*/
|
||||
QByteArray baseEtag;
|
||||
|
||||
bool isValid() const { return !path.isEmpty(); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif // SYNCJOURNALFILERECORD_H
|
||||
|
|
|
@ -542,6 +542,21 @@ QUrl Utility::concatUrlPath(const QUrl &url, const QString &concatPath,
|
|||
return tmpUrl;
|
||||
}
|
||||
|
||||
QString Utility::makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||
{
|
||||
QString conflictFileName(fn);
|
||||
// Add _conflict-XXXX before the extension.
|
||||
int dotLocation = conflictFileName.lastIndexOf('.');
|
||||
// If no extension, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
|
||||
if (dotLocation <= conflictFileName.lastIndexOf('/') + 1) {
|
||||
dotLocation = conflictFileName.size();
|
||||
}
|
||||
QString timeString = dt.toString("yyyyMMdd-hhmmss");
|
||||
|
||||
conflictFileName.insert(dotLocation, "_conflict-" + timeString);
|
||||
return conflictFileName;
|
||||
}
|
||||
|
||||
bool Utility::isConflictFile(const char *name)
|
||||
{
|
||||
const char *bname = std::strrchr(name, '/');
|
||||
|
@ -551,32 +566,33 @@ bool Utility::isConflictFile(const char *name)
|
|||
bname = name;
|
||||
}
|
||||
|
||||
if (std::strstr(bname, "_conflict-"))
|
||||
return true;
|
||||
|
||||
if (shouldUploadConflictFiles()) {
|
||||
// For uploads, we want to consider files with any kind of username tag
|
||||
// as conflict files. (pattern *_conflict_*-)
|
||||
const char *startOfMarker = std::strstr(bname, "_conflict_");
|
||||
if (startOfMarker && std::strchr(startOfMarker, '-'))
|
||||
return true;
|
||||
} else {
|
||||
// Old behavior: optionally, files with the specific string in the env variable
|
||||
// appended are also considered conflict files.
|
||||
static auto conflictFileUsername = qgetenv("CSYNC_CONFLICT_FILE_USERNAME");
|
||||
static auto usernameConflictId = QByteArray("_conflict_" + conflictFileUsername + "-");
|
||||
if (!conflictFileUsername.isEmpty() && std::strstr(bname, usernameConflictId.constData())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::strstr(bname, "_conflict-");
|
||||
}
|
||||
|
||||
bool Utility::shouldUploadConflictFiles()
|
||||
bool Utility::isConflictFile(const QString &name)
|
||||
{
|
||||
static bool uploadConflictFiles = qEnvironmentVariableIntValue("OWNCLOUD_UPLOAD_CONFLICT_FILES") != 0;
|
||||
return uploadConflictFiles;
|
||||
auto bname = name.midRef(name.lastIndexOf('/') + 1);
|
||||
return bname.contains("_conflict-", Utility::fsCasePreserving() ? Qt::CaseInsensitive : Qt::CaseSensitive);
|
||||
}
|
||||
|
||||
QByteArray Utility::conflictFileBaseName(const QByteArray &conflictName)
|
||||
{
|
||||
// This function must be able to deal with conflict files for conflict files.
|
||||
// To do this, we scan backwards, for the outermost conflict marker and
|
||||
// strip only that to generate the conflict file base name.
|
||||
int from = conflictName.size();
|
||||
while (from != -1) {
|
||||
auto start = conflictName.lastIndexOf("_conflict-", from);
|
||||
if (start == -1)
|
||||
return "";
|
||||
from = start - 1;
|
||||
|
||||
auto end = conflictName.indexOf('.', start);
|
||||
if (end == -1)
|
||||
end = conflictName.size();
|
||||
return conflictName.left(start) + conflictName.mid(end);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
|
|
@ -182,17 +182,23 @@ namespace Utility {
|
|||
with the given parent. If no parent is specified, the caller must destroy the settings */
|
||||
OCSYNC_EXPORT std::unique_ptr<QSettings> settingsWithGroup(const QString &group, QObject *parent = 0);
|
||||
|
||||
/** Returns a file name based on \a fn that's suitable for a conflict.
|
||||
*/
|
||||
OCSYNC_EXPORT QString makeConflictFileName(const QString &fn, const QDateTime &dt);
|
||||
|
||||
/** Returns whether a file name indicates a conflict file
|
||||
*
|
||||
* See FileSystem::makeConflictFileName.
|
||||
*/
|
||||
OCSYNC_EXPORT bool isConflictFile(const char *name);
|
||||
OCSYNC_EXPORT bool isConflictFile(const QString &name);
|
||||
|
||||
/** Returns whether conflict files should be uploaded.
|
||||
/** Find the base name for a conflict file name
|
||||
*
|
||||
* Experimental! Real feature planned for 2.5.
|
||||
* Will return an empty string if it's not a conflict file.
|
||||
*
|
||||
* Prefer to use the data from the conflicts table in the journal to determine
|
||||
* a conflict's base file.
|
||||
*/
|
||||
OCSYNC_EXPORT bool shouldUploadConflictFiles();
|
||||
OCSYNC_EXPORT QByteArray conflictFileBaseName(const QByteArray &conflictName);
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
OCSYNC_EXPORT QVariant registryGetKeyValue(HKEY hRootKey, const QString &subKey, const QString &valueName);
|
||||
|
|
|
@ -57,7 +57,7 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName,
|
|||
QString desktopFileLocation = userAutoStartPath + appName + QLatin1String(".desktop");
|
||||
if (enable) {
|
||||
if (!QDir().exists(userAutoStartPath) && !QDir().mkpath(userAutoStartPath)) {
|
||||
qCWarning(lcUtility) << "Could not create autostart folder";
|
||||
qCWarning(lcUtility) << "Could not create autostart folder" << userAutoStartPath;
|
||||
return;
|
||||
}
|
||||
QFile iniFile(desktopFileLocation);
|
||||
|
|
|
@ -4,10 +4,6 @@ cmake_policy(SET CMP0017 NEW)
|
|||
list(APPEND crashreporter_SOURCES main.cpp)
|
||||
list(APPEND crashreporter_RC resources.qrc)
|
||||
|
||||
qt_wrap_ui( crashreporter_UI_HEADERS ${crashreporter_UI} )
|
||||
qt_add_resources( crashreporter_RC_RCC ${crashreporter_RC} )
|
||||
|
||||
|
||||
# TODO: differentiate release channel
|
||||
# if(BUILD_RELEASE)
|
||||
# set(CRASHREPORTER_RELEASE_CHANNEL "release")
|
||||
|
@ -19,10 +15,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CrashReporterConfig.h.in
|
|||
${CMAKE_CURRENT_BINARY_DIR}/CrashReporterConfig.h)
|
||||
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}
|
||||
"../3rdparty/libcrashreporter-qt/src/"
|
||||
)
|
||||
|
||||
|
||||
if(NOT BUILD_LIBRARIES_ONLY)
|
||||
add_executable( ${CRASHREPORTER_EXECUTABLE} WIN32
|
||||
|
@ -32,14 +24,15 @@ if(NOT BUILD_LIBRARIES_ONLY)
|
|||
${crashreporter_RC_RCC}
|
||||
)
|
||||
|
||||
qt5_use_modules(${CRASHREPORTER_EXECUTABLE} Widgets Network)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Widgets)
|
||||
|
||||
target_include_directories(${CRASHREPORTER_EXECUTABLE} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set_target_properties(${CRASHREPORTER_EXECUTABLE} PROPERTIES AUTOMOC ON)
|
||||
set_target_properties(${CRASHREPORTER_EXECUTABLE} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
set_target_properties(${CRASHREPORTER_EXECUTABLE} PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}" )
|
||||
target_link_libraries(${CRASHREPORTER_EXECUTABLE}
|
||||
crashreporter-gui
|
||||
${QT_LIBRARIES}
|
||||
Qt5::Core Qt5::Widgets
|
||||
)
|
||||
|
||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
|
|
|
@ -14,49 +14,15 @@ include(DefineOptions.cmake)
|
|||
|
||||
include(DefineInstallationPaths)
|
||||
|
||||
# add macros
|
||||
include(MacroAddPlugin)
|
||||
include(MacroCopyFile)
|
||||
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
include(../common/common.cmake)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (MEM_NULL_TESTS)
|
||||
add_definitions(-DCSYNC_MEM_NULL_TESTS)
|
||||
endif (MEM_NULL_TESTS)
|
||||
|
||||
add_subdirectory(std)
|
||||
|
||||
# Statically include sqlite
|
||||
|
||||
set(CSYNC_PUBLIC_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}
|
||||
CACHE INTERNAL "csync public include directories"
|
||||
)
|
||||
|
||||
set(CSYNC_PRIVATE_INCLUDE_DIRS
|
||||
${SQLITE3_INCLUDE_DIRS}
|
||||
${CSTDLIB_PUBLIC_INCLUDE_DIRS}
|
||||
${CMAKE_BINARY_DIR}
|
||||
)
|
||||
|
||||
set(CSYNC_LIBRARY
|
||||
ocsync
|
||||
CACHE INTERNAL "ocsync library"
|
||||
)
|
||||
|
||||
set(CSYNC_LINK_LIBRARIES
|
||||
${CSTDLIB_LIBRARY}
|
||||
${CSYNC_REQUIRED_LIBRARIES}
|
||||
${SQLITE3_LIBRARIES}
|
||||
)
|
||||
|
||||
# Specific option for builds tied to servers that do not support renaming extensions
|
||||
set(NO_RENAME_EXTENSION 0 CACHE BOOL "Do not issue rename if the extension changes")
|
||||
if(NO_RENAME_EXTENSION)
|
||||
|
@ -66,8 +32,6 @@ endif()
|
|||
set(csync_SRCS
|
||||
csync.cpp
|
||||
csync_exclude.cpp
|
||||
csync_log.cpp
|
||||
csync_time.c
|
||||
csync_util.cpp
|
||||
csync_misc.cpp
|
||||
|
||||
|
@ -77,6 +41,12 @@ set(csync_SRCS
|
|||
csync_rename.cpp
|
||||
|
||||
vio/csync_vio.cpp
|
||||
|
||||
std/c_alloc.c
|
||||
std/c_string.c
|
||||
std/c_time.c
|
||||
std/c_utf8.cpp
|
||||
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
@ -89,52 +59,48 @@ else()
|
|||
)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_ASPRINTF AND NOT HAVE___MINGW_ASPRINTF)
|
||||
list(APPEND csync_SRCS std/asprintf.c)
|
||||
endif()
|
||||
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
list(APPEND csync_SRCS ${SQLITE3_SOURCE})
|
||||
endif()
|
||||
|
||||
configure_file(csync_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h)
|
||||
|
||||
set(csync_HDRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/csync_version.h
|
||||
csync.h
|
||||
vio/csync_vio.h
|
||||
vio/csync_vio_method.h
|
||||
vio/csync_vio_module.h
|
||||
set(CSYNC_LIBRARY ocsync)
|
||||
add_library(${CSYNC_LIBRARY} SHARED ${common_SOURCES} ${csync_SRCS})
|
||||
|
||||
target_include_directories(
|
||||
${CSYNC_LIBRARY}
|
||||
PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/std
|
||||
)
|
||||
|
||||
# Statically include sqlite
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
list(APPEND csync_SRCS ${SQLITE3_SOURCE})
|
||||
if (WIN32)
|
||||
# We want to export sqlite symbols from the ocsync DLL without
|
||||
# having to patch both sqlite3.h and the amalgation sqlite3.c,
|
||||
# so do the import/export magic manually through the build system.
|
||||
remove_definitions(-DSQLITE_API=__declspec\(dllimport\))
|
||||
add_definitions(-DSQLITE_API=__declspec\(dllexport\))
|
||||
endif()
|
||||
# make sure that the path for the local sqlite3 is before the system one
|
||||
target_include_directories(${CSYNC_LIBRARY} BEFORE PRIVATE ${SQLITE3_INCLUDE_DIR})
|
||||
else()
|
||||
target_include_directories(${CSYNC_LIBRARY} PRIVATE ${SQLITE3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${CSYNC_PUBLIC_INCLUDE_DIRS}
|
||||
${CSYNC_PRIVATE_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_library(${CSYNC_LIBRARY} SHARED ${common_SOURCES} ${csync_SRCS})
|
||||
#add_library(${CSYNC_LIBRARY}_static STATIC ${csync_SRCS})
|
||||
|
||||
generate_export_header( ${CSYNC_LIBRARY}
|
||||
generate_export_header(${CSYNC_LIBRARY}
|
||||
EXPORT_MACRO_NAME OCSYNC_EXPORT
|
||||
EXPORT_FILE_NAME ocsynclib.h
|
||||
)
|
||||
|
||||
target_link_libraries(${CSYNC_LIBRARY} ${CSYNC_LINK_LIBRARIES})
|
||||
#target_link_libraries(${CSYNC_LIBRARY}_static ${CSYNC_LINK_LIBRARIES})
|
||||
target_link_libraries(${CSYNC_LIBRARY}
|
||||
${CSYNC_REQUIRED_LIBRARIES}
|
||||
${SQLITE3_LIBRARIES}
|
||||
Qt5::Core Qt5::Concurrent
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
target_link_libraries(${CSYNC_LIBRARY} ${ZLIB_LIBRARIES})
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${CSYNC_LIBRARY} ZLIB::ZLIB)
|
||||
endif(ZLIB_FOUND)
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
qt5_use_modules(${CSYNC_LIBRARY} Core Concurrent)
|
||||
|
||||
# For src/common/utility_mac.cpp
|
||||
if (APPLE)
|
||||
|
@ -169,11 +135,11 @@ else()
|
|||
TARGETS
|
||||
${CSYNC_LIBRARY}
|
||||
LIBRARY DESTINATION
|
||||
${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}
|
||||
${CMAKE_INSTALL_LIBDIR}/${APPLICATION_EXECUTABLE}
|
||||
ARCHIVE DESTINATION
|
||||
${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}
|
||||
${CMAKE_INSTALL_LIBDIR}/${APPLICATION_EXECUTABLE}
|
||||
RUNTIME DESTINATION
|
||||
${BIN_INSTALL_DIR}/${APPLICATION_EXECUTABLE}
|
||||
${CMAKE_INSTALL_BINDIR}/${APPLICATION_EXECUTABLE}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -25,16 +25,6 @@ if (NOT LINUX)
|
|||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} )
|
||||
endif (NOT LINUX)
|
||||
|
||||
check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME)
|
||||
if (HAVE_LIBRT OR HAVE_CLOCK_GETTIME)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} rt)
|
||||
endif (HAVE_LIBRT OR HAVE_CLOCK_GETTIME)
|
||||
|
||||
check_library_exists(dl dlopen "" HAVE_LIBDL)
|
||||
if (HAVE_LIBDL)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} dl)
|
||||
endif (HAVE_LIBDL)
|
||||
|
||||
check_function_exists(asprintf HAVE_ASPRINTF)
|
||||
|
||||
check_function_exists(fnmatch HAVE_FNMATCH)
|
||||
|
|
|
@ -6,10 +6,6 @@
|
|||
#cmakedefine BINARYDIR "${BINARYDIR}"
|
||||
#cmakedefine SOURCEDIR "${SOURCEDIR}"
|
||||
|
||||
#cmakedefine HAVE_CLOCK_GETTIME
|
||||
|
||||
#cmakedefine WITH_LOG4C 1
|
||||
|
||||
#cmakedefine HAVE_ARGP_H 1
|
||||
|
||||
#cmakedefine HAVE_TIMEGM 1
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include "c_lib.h"
|
||||
#include "csync_private.h"
|
||||
#include "csync_exclude.h"
|
||||
#include "csync_time.h"
|
||||
#include "csync_util.h"
|
||||
#include "csync_misc.h"
|
||||
#include "std/c_private.h"
|
||||
|
@ -46,9 +45,11 @@
|
|||
|
||||
#include "vio/csync_vio.h"
|
||||
|
||||
#include "csync_log.h"
|
||||
#include "csync_rename.h"
|
||||
#include "common/c_jhash.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(lcCSync, "sync.csync.csync", QtInfoMsg)
|
||||
|
||||
|
||||
csync_s::csync_s(const char *localUri, OCC::SyncJournalDb *statedb)
|
||||
|
@ -65,7 +66,6 @@ csync_s::csync_s(const char *localUri, OCC::SyncJournalDb *statedb)
|
|||
|
||||
int csync_update(CSYNC *ctx) {
|
||||
int rc = -1;
|
||||
struct timespec start, finish;
|
||||
|
||||
if (ctx == NULL) {
|
||||
errno = EBADF;
|
||||
|
@ -78,13 +78,16 @@ int csync_update(CSYNC *ctx) {
|
|||
csync_memstat_check();
|
||||
|
||||
if (!ctx->exclude_traversal_fn) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "No exclude file loaded or defined!");
|
||||
qCInfo(lcCSync, "No exclude file loaded or defined!");
|
||||
}
|
||||
|
||||
/* update detection for local replica */
|
||||
csync_gettime(&start);
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
ctx->current = LOCAL_REPLICA;
|
||||
|
||||
qCInfo(lcCSync, "## Starting local discovery ##");
|
||||
|
||||
rc = csync_ftw(ctx, ctx->local.uri, csync_walker, MAX_DEPTH);
|
||||
if (rc < 0) {
|
||||
if(ctx->status_code == CSYNC_STATUS_OK) {
|
||||
|
@ -93,17 +96,16 @@ int csync_update(CSYNC *ctx) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
csync_gettime(&finish);
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
||||
"Update detection for local replica took %.2f seconds walking %zu files.",
|
||||
c_secdiff(finish, start), ctx->local.files.size());
|
||||
qCInfo(lcCSync) << "Update detection for local replica took" << timer.elapsed() / 1000.
|
||||
<< "seconds walking" << ctx->local.files.size() << "files";
|
||||
csync_memstat_check();
|
||||
|
||||
/* update detection for remote replica */
|
||||
csync_gettime(&start);
|
||||
timer.restart();
|
||||
ctx->current = REMOTE_REPLICA;
|
||||
|
||||
qCInfo(lcCSync, "## Starting remote discovery ##");
|
||||
|
||||
rc = csync_ftw(ctx, "", csync_walker, MAX_DEPTH);
|
||||
if (rc < 0) {
|
||||
if(ctx->status_code == CSYNC_STATUS_OK) {
|
||||
|
@ -112,12 +114,9 @@ int csync_update(CSYNC *ctx) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
csync_gettime(&finish);
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
||||
"Update detection for remote replica took %.2f seconds "
|
||||
"walking %zu files.",
|
||||
c_secdiff(finish, start), ctx->remote.files.size());
|
||||
qCInfo(lcCSync) << "Update detection for remote replica took" << timer.elapsed() / 1000.
|
||||
<< "seconds walking" << ctx->remote.files.size() << "files";
|
||||
csync_memstat_check();
|
||||
|
||||
ctx->status |= CSYNC_STATUS_UPDATE;
|
||||
|
@ -128,7 +127,6 @@ int csync_update(CSYNC *ctx) {
|
|||
|
||||
int csync_reconcile(CSYNC *ctx) {
|
||||
int rc = -1;
|
||||
struct timespec start, finish;
|
||||
|
||||
if (ctx == NULL) {
|
||||
errno = EBADF;
|
||||
|
@ -137,17 +135,15 @@ int csync_reconcile(CSYNC *ctx) {
|
|||
ctx->status_code = CSYNC_STATUS_OK;
|
||||
|
||||
/* Reconciliation for local replica */
|
||||
csync_gettime(&start);
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
ctx->current = LOCAL_REPLICA;
|
||||
|
||||
rc = csync_reconcile_updates(ctx);
|
||||
|
||||
csync_gettime(&finish);
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
||||
"Reconciliation for local replica took %.2f seconds visiting %zu files.",
|
||||
c_secdiff(finish, start), ctx->local.files.size());
|
||||
qCInfo(lcCSync) << "Reconciliation for local replica took " << timer.elapsed() / 1000.
|
||||
<< "seconds visiting " << ctx->local.files.size() << " files.";
|
||||
|
||||
if (rc < 0) {
|
||||
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
|
||||
|
@ -157,17 +153,14 @@ int csync_reconcile(CSYNC *ctx) {
|
|||
}
|
||||
|
||||
/* Reconciliation for remote replica */
|
||||
csync_gettime(&start);
|
||||
timer.restart();
|
||||
|
||||
ctx->current = REMOTE_REPLICA;
|
||||
|
||||
rc = csync_reconcile_updates(ctx);
|
||||
|
||||
csync_gettime(&finish);
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG,
|
||||
"Reconciliation for remote replica took %.2f seconds visiting %zu files.",
|
||||
c_secdiff(finish, start), ctx->remote.files.size());
|
||||
qCInfo(lcCSync) << "Reconciliation for remote replica took " << timer.elapsed() / 1000.
|
||||
<< "seconds visiting " << ctx->remote.files.size() << " files.";
|
||||
|
||||
if (rc < 0) {
|
||||
if (!CSYNC_STATUS_IS_OK(ctx->status_code)) {
|
||||
|
@ -402,3 +395,19 @@ int csync_abort_requested(CSYNC *ctx)
|
|||
return (1 == 0);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<csync_file_stat_t> csync_file_stat_s::fromSyncJournalFileRecord(const OCC::SyncJournalFileRecord &rec)
|
||||
{
|
||||
std::unique_ptr<csync_file_stat_t> st(new csync_file_stat_t);
|
||||
st->path = rec._path;
|
||||
st->inode = rec._inode;
|
||||
st->modtime = rec._modtime;
|
||||
st->type = static_cast<ItemType>(rec._type);
|
||||
st->etag = rec._etag;
|
||||
st->file_id = rec._fileId;
|
||||
st->remotePerm = rec._remotePerm;
|
||||
st->size = rec._fileSize;
|
||||
st->has_ignored_files = rec._serverHasIgnoredFiles;
|
||||
st->checksumHeader = rec._checksumHeader;
|
||||
return st;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
#include "std/c_private.h"
|
||||
#include "ocsynclib.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -45,9 +44,16 @@
|
|||
#include <QByteArray>
|
||||
#include "common/remotepermissions.h"
|
||||
|
||||
namespace OCC {
|
||||
class SyncJournalFileRecord;
|
||||
}
|
||||
|
||||
#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG) && (__GNUC__ * 100 + __GNUC_MINOR__ < 408)
|
||||
// openSuse 12.3 didn't like enum bitfields.
|
||||
#define BITFIELD(size)
|
||||
#elif defined(Q_CC_MSVC)
|
||||
// MSVC stores enum and bool as signed, so we need to add a bit for the sign
|
||||
#define BITFIELD(size) :(size+1)
|
||||
#else
|
||||
#define BITFIELD(size) :size
|
||||
#endif
|
||||
|
@ -102,7 +108,6 @@ enum csync_status_codes_e {
|
|||
CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS,
|
||||
CSYNC_STATUS_INDIVIDUAL_TRAILING_SPACE,
|
||||
CSYNC_STATUS_INDIVIDUAL_EXCLUDE_LONG_FILENAME,
|
||||
CYSNC_STATUS_FILE_LOCKED_OR_OPEN,
|
||||
CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN,
|
||||
CSYNC_STATUS_INVALID_CHARACTERS,
|
||||
CSYNC_STATUS_INDIVIDUAL_STAT_FAILED,
|
||||
|
@ -148,11 +153,13 @@ enum csync_instructions_e {
|
|||
but without any propagation (UPDATE|RECONCILE) */
|
||||
};
|
||||
|
||||
enum csync_ftw_type_e {
|
||||
CSYNC_FTW_TYPE_FILE,
|
||||
CSYNC_FTW_TYPE_SLINK,
|
||||
CSYNC_FTW_TYPE_DIR,
|
||||
CSYNC_FTW_TYPE_SKIP
|
||||
// This enum is used with BITFIELD(3) and BITFIELD(4) in several places.
|
||||
// Also, this value is stored in the database, so beware of value changes.
|
||||
enum ItemType {
|
||||
ItemTypeFile = 0,
|
||||
ItemTypeSoftLink = 1,
|
||||
ItemTypeDirectory = 2,
|
||||
ItemTypeSkip = 3
|
||||
};
|
||||
|
||||
|
||||
|
@ -169,7 +176,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
|
|||
uint64_t inode;
|
||||
|
||||
OCC::RemotePermissions remotePerm;
|
||||
enum csync_ftw_type_e type BITFIELD(4);
|
||||
ItemType type BITFIELD(4);
|
||||
bool child_modified BITFIELD(1);
|
||||
bool has_ignored_files BITFIELD(1); // Specify that a directory, or child directory contains ignored files.
|
||||
bool is_hidden BITFIELD(1); // Not saved in the DB, only used during discovery for local files.
|
||||
|
@ -196,7 +203,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
|
|||
: modtime(0)
|
||||
, size(0)
|
||||
, inode(0)
|
||||
, type(CSYNC_FTW_TYPE_SKIP)
|
||||
, type(ItemTypeSkip)
|
||||
, child_modified(false)
|
||||
, has_ignored_files(false)
|
||||
, is_hidden(false)
|
||||
|
@ -204,21 +211,7 @@ struct OCSYNC_EXPORT csync_file_stat_s {
|
|||
, instruction(CSYNC_INSTRUCTION_NONE)
|
||||
{ }
|
||||
|
||||
static std::unique_ptr<csync_file_stat_t> fromSyncJournalFileRecord(const OCC::SyncJournalFileRecord &rec)
|
||||
{
|
||||
std::unique_ptr<csync_file_stat_t> st(new csync_file_stat_t);
|
||||
st->path = rec._path;
|
||||
st->inode = rec._inode;
|
||||
st->modtime = rec._modtime;
|
||||
st->type = static_cast<csync_ftw_type_e>(rec._type);
|
||||
st->etag = rec._etag;
|
||||
st->file_id = rec._fileId;
|
||||
st->remotePerm = rec._remotePerm;
|
||||
st->size = rec._fileSize;
|
||||
st->has_ignored_files = rec._serverHasIgnoredFiles;
|
||||
st->checksumHeader = rec._checksumHeader;
|
||||
return st;
|
||||
}
|
||||
static std::unique_ptr<csync_file_stat_t> fromSyncJournalFileRecord(const OCC::SyncJournalFileRecord &rec);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -229,10 +222,6 @@ typedef struct csync_s CSYNC;
|
|||
typedef int (*csync_auth_callback) (const char *prompt, char *buf, size_t len,
|
||||
int echo, int verify, void *userdata);
|
||||
|
||||
typedef void (*csync_log_callback) (int verbosity,
|
||||
const char *function,
|
||||
const char *buffer);
|
||||
|
||||
typedef void (*csync_update_callback) (bool local,
|
||||
const char *dirUrl,
|
||||
void *userdata);
|
||||
|
@ -310,39 +299,6 @@ csync_auth_callback OCSYNC_EXPORT csync_get_auth_callback(CSYNC *ctx);
|
|||
*/
|
||||
int OCSYNC_EXPORT csync_set_auth_callback(CSYNC *ctx, csync_auth_callback cb);
|
||||
|
||||
/**
|
||||
* @brief Set the log level.
|
||||
*
|
||||
* @param[in] level The log verbosity.
|
||||
*
|
||||
* @return 0 on success, < 0 if an error occurred.
|
||||
*/
|
||||
int OCSYNC_EXPORT csync_set_log_level(int level);
|
||||
|
||||
/**
|
||||
* @brief Get the log verbosity
|
||||
*
|
||||
* @return The log verbosity, -1 on error.
|
||||
*/
|
||||
int OCSYNC_EXPORT csync_get_log_level(void);
|
||||
|
||||
/**
|
||||
* @brief Get the logging callback set.
|
||||
*
|
||||
* @return The logging callback set or NULL if an error
|
||||
* occurred.
|
||||
*/
|
||||
csync_log_callback OCSYNC_EXPORT csync_get_log_callback(void);
|
||||
|
||||
/**
|
||||
* @brief Set the logging callback.
|
||||
*
|
||||
* @param cb The logging callback.
|
||||
*
|
||||
* @return 0 on success, less than 0 if an error occurred.
|
||||
*/
|
||||
int OCSYNC_EXPORT csync_set_log_callback(csync_log_callback cb);
|
||||
|
||||
/* Used for special modes or debugging */
|
||||
CSYNC_STATUS OCSYNC_EXPORT csync_get_status(CSYNC *ctx);
|
||||
|
||||
|
|
|
@ -23,11 +23,6 @@
|
|||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "c_lib.h"
|
||||
#include "c_private.h"
|
||||
|
@ -42,125 +37,43 @@
|
|||
#include <QString>
|
||||
#include <QFileInfo>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.exclude"
|
||||
#include "csync_log.h"
|
||||
|
||||
/** Expands C-like escape sequences.
|
||||
*
|
||||
* The returned string is heap-allocated and owned by the caller.
|
||||
/** Expands C-like escape sequences (in place)
|
||||
*/
|
||||
static const char *csync_exclude_expand_escapes(const char * input)
|
||||
static void csync_exclude_expand_escapes(QByteArray &input)
|
||||
{
|
||||
size_t i_len = strlen(input) + 1;
|
||||
char *out = (char*)c_malloc(i_len); // out can only be shorter
|
||||
|
||||
size_t i = 0;
|
||||
size_t o = 0;
|
||||
for (; i < i_len; ++i) {
|
||||
if (input[i] == '\\') {
|
||||
char *line = input.data();
|
||||
auto len = input.size();
|
||||
for (int i = 0; i < len; ++i) {
|
||||
if (line[i] == '\\') {
|
||||
// at worst input[i+1] is \0
|
||||
switch (input[i+1]) {
|
||||
case '\'': out[o++] = '\''; break;
|
||||
case '"': out[o++] = '"'; break;
|
||||
case '?': out[o++] = '?'; break;
|
||||
case '#': out[o++] = '#'; break;
|
||||
case 'a': out[o++] = '\a'; break;
|
||||
case 'b': out[o++] = '\b'; break;
|
||||
case 'f': out[o++] = '\f'; break;
|
||||
case 'n': out[o++] = '\n'; break;
|
||||
case 'r': out[o++] = '\r'; break;
|
||||
case 't': out[o++] = '\t'; break;
|
||||
case 'v': out[o++] = '\v'; break;
|
||||
switch (line[i+1]) {
|
||||
case '\'': line[o++] = '\''; break;
|
||||
case '"': line[o++] = '"'; break;
|
||||
case '?': line[o++] = '?'; break;
|
||||
case '#': line[o++] = '#'; break;
|
||||
case 'a': line[o++] = '\a'; break;
|
||||
case 'b': line[o++] = '\b'; break;
|
||||
case 'f': line[o++] = '\f'; break;
|
||||
case 'n': line[o++] = '\n'; break;
|
||||
case 'r': line[o++] = '\r'; break;
|
||||
case 't': line[o++] = '\t'; break;
|
||||
case 'v': line[o++] = '\v'; break;
|
||||
default:
|
||||
// '\*' '\?' '\[' '\\' will be processed during regex translation
|
||||
// '\\' is intentionally not expanded here (to avoid '\\*' and '\*'
|
||||
// ending up meaning the same thing)
|
||||
out[o++] = input[i];
|
||||
out[o++] = input[i+1];
|
||||
line[o++] = line[i];
|
||||
line[o++] = line[i + 1];
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
out[o++] = input[i];
|
||||
line[o++] = line[i];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Loads patterns from a file and adds them to excludes */
|
||||
int csync_exclude_load(const char *fname, QList<QByteArray> *excludes) {
|
||||
int fd = -1;
|
||||
int i = 0;
|
||||
int rc = -1;
|
||||
int64_t size;
|
||||
char *buf = NULL;
|
||||
char *entry = NULL;
|
||||
mbchar_t *w_fname;
|
||||
|
||||
if (fname == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
_fmode = _O_BINARY;
|
||||
#endif
|
||||
|
||||
w_fname = c_utf8_path_to_locale(fname);
|
||||
if (w_fname == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = _topen(w_fname, O_RDONLY);
|
||||
c_free_locale_string(w_fname);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size = lseek(fd, 0, SEEK_END);
|
||||
if (size < 0) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
if (size == 0) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
buf = (char*)c_malloc(size + 1);
|
||||
if (read(fd, buf, size) != size) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
buf[size] = '\0';
|
||||
|
||||
/* FIXME: Use fgets and don't add duplicates */
|
||||
entry = buf;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (buf[i] == '\n' || buf[i] == '\r') {
|
||||
if (entry != buf + i) {
|
||||
buf[i] = '\0';
|
||||
if (*entry != '#') {
|
||||
const char *unescaped = csync_exclude_expand_escapes(entry);
|
||||
excludes->append(unescaped);
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", unescaped);
|
||||
SAFE_FREE(unescaped);
|
||||
}
|
||||
}
|
||||
entry = buf + i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
SAFE_FREE(buf);
|
||||
close(fd);
|
||||
return rc;
|
||||
input.resize(o);
|
||||
}
|
||||
|
||||
// See http://support.microsoft.com/kb/74496 and
|
||||
|
@ -218,7 +131,7 @@ bool csync_is_windows_reserved_word(const char *filename)
|
|||
return false;
|
||||
}
|
||||
|
||||
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const char *path)
|
||||
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const char *path, bool excludeConflictFiles)
|
||||
{
|
||||
const char *bname = NULL;
|
||||
size_t blen = 0;
|
||||
|
@ -313,11 +226,9 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const char *path)
|
|||
}
|
||||
}
|
||||
|
||||
if (!OCC::Utility::shouldUploadConflictFiles()) {
|
||||
if (OCC::Utility::isConflictFile(bname)) {
|
||||
match = CSYNC_FILE_EXCLUDE_CONFLICT;
|
||||
goto out;
|
||||
}
|
||||
if (excludeConflictFiles && OCC::Utility::isConflictFile(bname)) {
|
||||
match = CSYNC_FILE_EXCLUDE_CONFLICT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -330,6 +241,8 @@ using namespace OCC;
|
|||
|
||||
ExcludedFiles::ExcludedFiles()
|
||||
{
|
||||
// Windows used to use PathMatchSpec which allows *foo to match abc/deffoo.
|
||||
_wildcardsMatchSlash = Utility::isWindows();
|
||||
}
|
||||
|
||||
ExcludedFiles::~ExcludedFiles()
|
||||
|
@ -341,6 +254,11 @@ void ExcludedFiles::addExcludeFilePath(const QString &path)
|
|||
_excludeFiles.insert(path);
|
||||
}
|
||||
|
||||
void ExcludedFiles::setExcludeConflictFiles(bool onoff)
|
||||
{
|
||||
_excludeConflictFiles = onoff;
|
||||
}
|
||||
|
||||
void ExcludedFiles::addManualExclude(const QByteArray &expr)
|
||||
{
|
||||
_manualExcludes.append(expr);
|
||||
|
@ -354,13 +272,29 @@ void ExcludedFiles::clearManualExcludes()
|
|||
reloadExcludeFiles();
|
||||
}
|
||||
|
||||
void ExcludedFiles::setWildcardsMatchSlash(bool onoff)
|
||||
{
|
||||
_wildcardsMatchSlash = onoff;
|
||||
prepare();
|
||||
}
|
||||
|
||||
bool ExcludedFiles::reloadExcludeFiles()
|
||||
{
|
||||
_allExcludes.clear();
|
||||
bool success = true;
|
||||
foreach (const QString &file, _excludeFiles) {
|
||||
if (csync_exclude_load(file.toUtf8(), &_allExcludes) < 0)
|
||||
QFile f(file);
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
while (!f.atEnd()) {
|
||||
QByteArray line = f.readLine().trimmed();
|
||||
if (line.isEmpty() || line.startsWith('#'))
|
||||
continue;
|
||||
csync_exclude_expand_escapes(line);
|
||||
_allExcludes.append(line);
|
||||
}
|
||||
}
|
||||
_allExcludes.append(_manualExcludes);
|
||||
prepare();
|
||||
|
@ -393,9 +327,9 @@ bool ExcludedFiles::isExcluded(
|
|||
}
|
||||
|
||||
QFileInfo fi(filePath);
|
||||
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
|
||||
ItemType type = ItemTypeFile;
|
||||
if (fi.isDir()) {
|
||||
type = CSYNC_FTW_TYPE_DIR;
|
||||
type = ItemTypeDirectory;
|
||||
}
|
||||
|
||||
QString relativePath = filePath.mid(basePath.size());
|
||||
|
@ -406,9 +340,9 @@ bool ExcludedFiles::isExcluded(
|
|||
return fullPatternMatch(relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED;
|
||||
}
|
||||
|
||||
CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, int filetype) const
|
||||
CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, ItemType filetype) const
|
||||
{
|
||||
auto match = _csync_excluded_common(path);
|
||||
auto match = _csync_excluded_common(path, _excludeConflictFiles);
|
||||
if (match != CSYNC_NOT_EXCLUDED)
|
||||
return match;
|
||||
if (_allExcludes.isEmpty())
|
||||
|
@ -426,35 +360,40 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, int fi
|
|||
QString bnameStr = QString::fromUtf8(bname);
|
||||
|
||||
QRegularExpressionMatch m;
|
||||
if (filetype == CSYNC_FTW_TYPE_DIR) {
|
||||
m = _bnameActivationRegexDir.match(bnameStr);
|
||||
if (filetype == ItemTypeDirectory) {
|
||||
m = _bnameTraversalRegexDir.match(bnameStr);
|
||||
} else {
|
||||
m = _bnameActivationRegexFile.match(bnameStr);
|
||||
m = _bnameTraversalRegexFile.match(bnameStr);
|
||||
}
|
||||
if (!m.hasMatch())
|
||||
return match;
|
||||
|
||||
// Now run the full match
|
||||
return CSYNC_NOT_EXCLUDED;
|
||||
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
|
||||
return CSYNC_FILE_EXCLUDE_LIST;
|
||||
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
|
||||
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
||||
}
|
||||
|
||||
// third capture: full path matching is triggered
|
||||
QString pathStr = QString::fromUtf8(path);
|
||||
if (filetype == CSYNC_FTW_TYPE_DIR) {
|
||||
m = _fullRegexDir.match(pathStr);
|
||||
|
||||
if (filetype == ItemTypeDirectory) {
|
||||
m = _fullTraversalRegexDir.match(pathStr);
|
||||
} else {
|
||||
m = _fullRegexFile.match(pathStr);
|
||||
m = _fullTraversalRegexFile.match(pathStr);
|
||||
}
|
||||
if (m.hasMatch()) {
|
||||
if (!m.captured(1).isEmpty()) {
|
||||
match = CSYNC_FILE_EXCLUDE_LIST;
|
||||
} else if (!m.captured(2).isEmpty()) {
|
||||
match = CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
||||
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
|
||||
return CSYNC_FILE_EXCLUDE_LIST;
|
||||
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
|
||||
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
return CSYNC_NOT_EXCLUDED;
|
||||
}
|
||||
|
||||
CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, int filetype) const
|
||||
CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, ItemType filetype) const
|
||||
{
|
||||
auto match = _csync_excluded_common(path);
|
||||
auto match = _csync_excluded_common(path, _excludeConflictFiles);
|
||||
if (match != CSYNC_NOT_EXCLUDED)
|
||||
return match;
|
||||
if (_allExcludes.isEmpty())
|
||||
|
@ -462,15 +401,15 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, int filetyp
|
|||
|
||||
QString p = QString::fromUtf8(path);
|
||||
QRegularExpressionMatch m;
|
||||
if (filetype == CSYNC_FTW_TYPE_DIR) {
|
||||
if (filetype == ItemTypeDirectory) {
|
||||
m = _fullRegexDir.match(p);
|
||||
} else {
|
||||
m = _fullRegexFile.match(p);
|
||||
}
|
||||
if (m.hasMatch()) {
|
||||
if (!m.captured(1).isEmpty()) {
|
||||
if (m.capturedStart(QStringLiteral("exclude")) != -1) {
|
||||
return CSYNC_FILE_EXCLUDE_LIST;
|
||||
} else if (!m.captured(2).isEmpty()) {
|
||||
} else if (m.capturedStart(QStringLiteral("excluderemove")) != -1) {
|
||||
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
||||
}
|
||||
}
|
||||
|
@ -478,12 +417,17 @@ CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, int filetyp
|
|||
}
|
||||
|
||||
auto ExcludedFiles::csyncTraversalMatchFun() const
|
||||
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, int filetype)>
|
||||
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>
|
||||
{
|
||||
return [this](const char *path, int filetype) { return this->traversalPatternMatch(path, filetype); };
|
||||
return [this](const char *path, ItemType filetype) { return this->traversalPatternMatch(path, filetype); };
|
||||
}
|
||||
|
||||
static QString convertToRegexpSyntax(QString exclude)
|
||||
/**
|
||||
* On linux we used to use fnmatch with FNM_PATHNAME, but the windows function we used
|
||||
* didn't have that behavior. wildcardsMatchSlash can be used to control which behavior
|
||||
* the resulting regex shall use.
|
||||
*/
|
||||
static QString convertToRegexpSyntax(QString exclude, bool wildcardsMatchSlash)
|
||||
{
|
||||
// Translate *, ?, [...] to their regex variants.
|
||||
// The escape sequences \*, \?, \[. \\ have a special meaning,
|
||||
|
@ -507,11 +451,19 @@ static QString convertToRegexpSyntax(QString exclude)
|
|||
switch (exclude[i].unicode()) {
|
||||
case '*':
|
||||
flush();
|
||||
regex.append("[^/]*");
|
||||
if (wildcardsMatchSlash) {
|
||||
regex.append(".*");
|
||||
} else {
|
||||
regex.append("[^/]*");
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
flush();
|
||||
regex.append("[^/]");
|
||||
if (wildcardsMatchSlash) {
|
||||
regex.append(".");
|
||||
} else {
|
||||
regex.append("[^/]");
|
||||
}
|
||||
break;
|
||||
case '[': {
|
||||
flush();
|
||||
|
@ -565,20 +517,56 @@ static QString convertToRegexpSyntax(QString exclude)
|
|||
return regex;
|
||||
}
|
||||
|
||||
static QString extractBnameTrigger(const QString &exclude, bool wildcardsMatchSlash)
|
||||
{
|
||||
// We can definitely drop everything to the left of a / - that will never match
|
||||
// any bname.
|
||||
QString pattern = exclude.mid(exclude.lastIndexOf('/') + 1);
|
||||
|
||||
// Easy case, nothing else can match a slash, so that's it.
|
||||
if (!wildcardsMatchSlash)
|
||||
return pattern;
|
||||
|
||||
// Otherwise it's more complicated. Examples:
|
||||
// - "foo*bar" can match "fooX/Xbar", pattern is "*bar"
|
||||
// - "foo*bar*" can match "fooX/XbarX", pattern is "*bar*"
|
||||
// - "foo?bar" can match "foo/bar" but also "fooXbar", pattern is "*bar"
|
||||
|
||||
auto isWildcard = [](QChar c) { return c == QLatin1Char('*') || c == QLatin1Char('?'); };
|
||||
|
||||
// First, skip wildcards on the very right of the pattern
|
||||
int i = pattern.size() - 1;
|
||||
while (i >= 0 && isWildcard(pattern[i]))
|
||||
--i;
|
||||
|
||||
// Then scan further until the next wildcard that could match a /
|
||||
while (i >= 0 && !isWildcard(pattern[i]))
|
||||
--i;
|
||||
|
||||
// Everything to the right is part of the pattern
|
||||
pattern = pattern.mid(i + 1);
|
||||
|
||||
// And if there was a wildcard, it starts with a *
|
||||
if (i >= 0)
|
||||
pattern.prepend('*');
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
void ExcludedFiles::prepare()
|
||||
{
|
||||
// Build regular expressions for the different cases.
|
||||
//
|
||||
// To compose the _bnameActivationRegex and _fullRegex patterns we
|
||||
// collect several subgroups of patterns here.
|
||||
// To compose the _bnameTraversalRegex, _fullTraversalRegex and _fullRegex
|
||||
// patterns we collect several subgroups of patterns here.
|
||||
//
|
||||
// * The "full" group will contain all patterns that contain a non-trailing
|
||||
// slash. They only make sense in the fullRegex.
|
||||
// slash. They only make sense in the fullRegex and fullTraversalRegex.
|
||||
// * The "bname" group contains all patterns without a non-trailing slash.
|
||||
// These need separate handling in the _fullRegex (slash-containing
|
||||
// patterns must be anchored to the front, these don't need it)
|
||||
// * The "bnameTrigger" group contains the bname part of all patterns in the
|
||||
// "full" group. These and the "bname" group become _bnameActivationRegex.
|
||||
// "full" group. These and the "bname" group become _bnameTraversalRegex.
|
||||
//
|
||||
// To complicate matters, the exclude patterns have two binary attributes
|
||||
// meaning we'll end up with 4 variants:
|
||||
|
@ -630,15 +618,15 @@ void ExcludedFiles::prepare()
|
|||
auto &fullFileDir = removeExcluded ? fullFileDirRemove : fullFileDirKeep;
|
||||
auto &fullDir = removeExcluded ? fullDirRemove : fullDirKeep;
|
||||
|
||||
auto regexExclude = convertToRegexpSyntax(QString::fromUtf8(exclude));
|
||||
auto regexExclude = convertToRegexpSyntax(QString::fromUtf8(exclude), _wildcardsMatchSlash);
|
||||
if (!fullPath) {
|
||||
regexAppend(bnameFileDir, bnameDir, regexExclude, matchDirOnly);
|
||||
} else {
|
||||
regexAppend(fullFileDir, fullDir, regexExclude, matchDirOnly);
|
||||
|
||||
// for activation, trigger on the 'bname' part of the full pattern
|
||||
auto bnameExclude = exclude.mid(exclude.lastIndexOf('/') + 1);
|
||||
auto regexBname = convertToRegexpSyntax(bnameExclude);
|
||||
// For activation, trigger on the 'bname' part of the full pattern.
|
||||
QString bnameExclude = extractBnameTrigger(exclude, _wildcardsMatchSlash);
|
||||
auto regexBname = convertToRegexpSyntax(bnameExclude, true);
|
||||
regexAppend(bnameTriggerFileDir, bnameTriggerDir, regexBname, matchDirOnly);
|
||||
}
|
||||
}
|
||||
|
@ -661,22 +649,40 @@ void ExcludedFiles::prepare()
|
|||
emptyMatchNothing(bnameTriggerFileDir);
|
||||
emptyMatchNothing(bnameTriggerDir);
|
||||
|
||||
// The bname activation regexe is applied to the bname only, so must be
|
||||
// anchored in the beginning and in the end. It has the explicit triggers
|
||||
// plus the bname-only patterns. Here we don't care about the remove/keep
|
||||
// distinction.
|
||||
_bnameActivationRegexFile.setPattern(
|
||||
"^(?:" + bnameFileDirKeep + "|" + bnameFileDirRemove + "|" + bnameTriggerFileDir + ")$");
|
||||
_bnameActivationRegexDir.setPattern(
|
||||
"^(?:" + bnameFileDirKeep + "|" + bnameFileDirRemove
|
||||
+ "|" + bnameDirKeep + "|" + bnameFileDirRemove
|
||||
+ "|" + bnameTriggerFileDir + "|" + bnameTriggerDir + ")$");
|
||||
// The bname regex is applied to the bname only, so it must be
|
||||
// anchored in the beginning and in the end. It has the structure:
|
||||
// (exclude)|(excluderemove)|(bname triggers).
|
||||
// If the third group matches, the fullActivatedRegex needs to be applied
|
||||
// to the full path.
|
||||
_bnameTraversalRegexFile.setPattern(
|
||||
"^(?P<exclude>" + bnameFileDirKeep + ")$|"
|
||||
+ "^(?P<excluderemove>" + bnameFileDirRemove + ")$|"
|
||||
+ "^(?P<trigger>" + bnameTriggerFileDir + ")$");
|
||||
_bnameTraversalRegexDir.setPattern(
|
||||
"^(?P<exclude>" + bnameFileDirKeep + "|" + bnameDirKeep + ")$|"
|
||||
+ "^(?P<excluderemove>" + bnameFileDirRemove + "|" + bnameDirRemove + ")$|"
|
||||
+ "^(?P<trigger>" + bnameTriggerFileDir + "|" + bnameTriggerDir + ")$");
|
||||
|
||||
// The full regex has two captures, it's basic form is "(...)|(...)". The first
|
||||
// capture has the keep/exclude-only patterns, the second the remove/exclude-and-remove
|
||||
// patterns.
|
||||
// The full traveral regex is applied to the full path if the trigger capture of
|
||||
// the bname regex matches. Its basic form is (exclude)|(excluderemove)".
|
||||
// This pattern can be much simpler than fullRegex since we can assume a traversal
|
||||
// situation and doesn't need to look for bname patterns in parent paths.
|
||||
_fullTraversalRegexFile.setPattern(
|
||||
QLatin1String("")
|
||||
// Full patterns are anchored to the beginning
|
||||
+ "^(?P<exclude>" + fullFileDirKeep + ")(?:$|/)"
|
||||
+ "|"
|
||||
+ "^(?P<excluderemove>" + fullFileDirRemove + ")(?:$|/)");
|
||||
_fullTraversalRegexDir.setPattern(
|
||||
QLatin1String("")
|
||||
+ "^(?P<exclude>" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)"
|
||||
+ "|"
|
||||
+ "^(?P<excluderemove>" + fullFileDirRemove + "|" + fullDirRemove + ")(?:$|/)");
|
||||
|
||||
// The full regex is applied to the full path and incorporates both bname and
|
||||
// full-path patterns. It has the form "(exclude)|(excluderemove)".
|
||||
_fullRegexFile.setPattern(
|
||||
QLatin1String("(")
|
||||
QLatin1String("(?P<exclude>")
|
||||
// Full patterns are anchored to the beginning
|
||||
+ "^(?:" + fullFileDirKeep + ")(?:$|/)" + "|"
|
||||
// Simple bname patterns can be any path component
|
||||
|
@ -684,16 +690,20 @@ void ExcludedFiles::prepare()
|
|||
// When checking a file for exclusion we must check all parent paths
|
||||
// against the dir-only patterns as well.
|
||||
+ "(?:^|/)(?:" + bnameDirKeep + ")/"
|
||||
+ ")|("
|
||||
+ ")"
|
||||
+ "|"
|
||||
+ "(?P<excluderemove>"
|
||||
+ "^(?:" + fullFileDirRemove + ")(?:$|/)" + "|"
|
||||
+ "(?:^|/)(?:" + bnameFileDirRemove + ")(?:$|/)" + "|"
|
||||
+ "(?:^|/)(?:" + bnameDirRemove + ")/"
|
||||
+ ")");
|
||||
_fullRegexDir.setPattern(
|
||||
QLatin1String("(")
|
||||
QLatin1String("(?P<exclude>")
|
||||
+ "^(?:" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)" + "|"
|
||||
+ "(?:^|/)(?:" + bnameFileDirKeep + "|" + bnameDirKeep + ")(?:$|/)"
|
||||
+ ")|("
|
||||
+ ")"
|
||||
+ "|"
|
||||
+ "(?P<excluderemove>"
|
||||
+ "^(?:" + fullFileDirRemove + "|" + fullDirRemove + ")(?:$|/)" + "|"
|
||||
+ "(?:^|/)(?:" + bnameFileDirRemove + "|" + bnameDirRemove + ")(?:$|/)"
|
||||
+ ")");
|
||||
|
@ -701,10 +711,14 @@ void ExcludedFiles::prepare()
|
|||
QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption;
|
||||
if (OCC::Utility::fsCasePreserving())
|
||||
patternOptions |= QRegularExpression::CaseInsensitiveOption;
|
||||
_bnameActivationRegexFile.setPatternOptions(patternOptions);
|
||||
_bnameActivationRegexFile.optimize();
|
||||
_bnameActivationRegexDir.setPatternOptions(patternOptions);
|
||||
_bnameActivationRegexDir.optimize();
|
||||
_bnameTraversalRegexFile.setPatternOptions(patternOptions);
|
||||
_bnameTraversalRegexFile.optimize();
|
||||
_bnameTraversalRegexDir.setPatternOptions(patternOptions);
|
||||
_bnameTraversalRegexDir.optimize();
|
||||
_fullTraversalRegexFile.setPatternOptions(patternOptions);
|
||||
_fullTraversalRegexFile.optimize();
|
||||
_fullTraversalRegexDir.setPatternOptions(patternOptions);
|
||||
_fullTraversalRegexDir.optimize();
|
||||
_fullRegexFile.setPatternOptions(patternOptions);
|
||||
_fullRegexFile.optimize();
|
||||
_fullRegexDir.setPatternOptions(patternOptions);
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include <QString>
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include <functional>
|
||||
|
||||
enum csync_exclude_type_e {
|
||||
CSYNC_NOT_EXCLUDED = 0,
|
||||
CSYNC_FILE_SILENTLY_EXCLUDED,
|
||||
|
@ -74,6 +76,13 @@ public:
|
|||
*/
|
||||
void addExcludeFilePath(const QString &path);
|
||||
|
||||
/**
|
||||
* Whether conflict files shall be excluded.
|
||||
*
|
||||
* Defaults to true.
|
||||
*/
|
||||
void setExcludeConflictFiles(bool onoff);
|
||||
|
||||
/**
|
||||
* Checks whether a file or directory should be excluded.
|
||||
*
|
||||
|
@ -100,6 +109,11 @@ public:
|
|||
*/
|
||||
void clearManualExcludes();
|
||||
|
||||
/**
|
||||
* Adjusts behavior of wildcards. Only used for testing.
|
||||
*/
|
||||
void setWildcardsMatchSlash(bool onoff);
|
||||
|
||||
/**
|
||||
* Generate a hook for traversal exclude pattern matching
|
||||
* that csync can use.
|
||||
|
@ -108,7 +122,7 @@ public:
|
|||
* ExcludedFiles instance stays alive.
|
||||
*/
|
||||
auto csyncTraversalMatchFun() const
|
||||
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, int filetype)>;
|
||||
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)>;
|
||||
|
||||
public slots:
|
||||
/**
|
||||
|
@ -125,7 +139,7 @@ private:
|
|||
* Note that this only matches patterns. It does not check whether the file
|
||||
* or directory pointed to is hidden (or whether it even exists).
|
||||
*/
|
||||
CSYNC_EXCLUDE_TYPE fullPatternMatch(const char *path, int filetype) const;
|
||||
CSYNC_EXCLUDE_TYPE fullPatternMatch(const char *path, ItemType filetype) const;
|
||||
|
||||
/**
|
||||
* @brief Check if the given path should be excluded in a traversal situation.
|
||||
|
@ -142,7 +156,7 @@ private:
|
|||
* Note that this only matches patterns. It does not check whether the file
|
||||
* or directory pointed to is hidden (or whether it even exists).
|
||||
*/
|
||||
CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, int filetype) const;
|
||||
CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, ItemType filetype) const;
|
||||
|
||||
/**
|
||||
* Generate optimized regular expressions for the exclude patterns.
|
||||
|
@ -165,8 +179,9 @@ private:
|
|||
* full("a/b/c/d") == traversal("a") || traversal("a/b") || traversal("a/b/c")
|
||||
*
|
||||
* The traversal matcher can be extremely fast because it has a fast early-out
|
||||
* case: It checks the bname part of the path against _bnameActivationRegex
|
||||
* and only runs the full regex if the bname activation was triggered.
|
||||
* case: It checks the bname part of the path against _bnameTraversalRegex
|
||||
* and only runs a simplified _fullTraversalRegex on the whole path if bname
|
||||
* activation for it was triggered.
|
||||
*
|
||||
* Note: The traversal matcher will return not-excluded on some paths that the
|
||||
* full matcher would exclude. Example: "b" is excluded. traversal("b/c")
|
||||
|
@ -184,11 +199,23 @@ private:
|
|||
QList<QByteArray> _allExcludes;
|
||||
|
||||
/// see prepare()
|
||||
QRegularExpression _bnameActivationRegexFile;
|
||||
QRegularExpression _bnameActivationRegexDir;
|
||||
QRegularExpression _bnameTraversalRegexFile;
|
||||
QRegularExpression _bnameTraversalRegexDir;
|
||||
QRegularExpression _fullTraversalRegexFile;
|
||||
QRegularExpression _fullTraversalRegexDir;
|
||||
QRegularExpression _fullRegexFile;
|
||||
QRegularExpression _fullRegexDir;
|
||||
|
||||
bool _excludeConflictFiles = true;
|
||||
|
||||
/**
|
||||
* Whether * and ? in patterns can match a /
|
||||
*
|
||||
* Unfortunately this was how matching was done on Windows so
|
||||
* it continues to be enabled there.
|
||||
*/
|
||||
bool _wildcardsMatchSlash = false;
|
||||
|
||||
friend class ExcludedFilesTest;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config_csync.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "csync_private.h"
|
||||
#include "csync_log.h"
|
||||
|
||||
CSYNC_THREAD int csync_log_level;
|
||||
CSYNC_THREAD csync_log_callback csync_log_cb;
|
||||
|
||||
void csync_log(int verbosity,
|
||||
const char *function,
|
||||
const char *format, ...)
|
||||
{
|
||||
csync_log_callback log_fn = csync_get_log_callback();
|
||||
if (log_fn && verbosity <= csync_get_log_level()) {
|
||||
char buffer[1024];
|
||||
va_list va;
|
||||
|
||||
va_start(va, format);
|
||||
vsnprintf(buffer, sizeof(buffer), format, va);
|
||||
va_end(va);
|
||||
|
||||
log_fn(verbosity, function, buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int csync_set_log_level(int level) {
|
||||
if (level < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
csync_log_level = level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int csync_get_log_level(void) {
|
||||
return csync_log_level;
|
||||
}
|
||||
|
||||
int csync_set_log_callback(csync_log_callback cb) {
|
||||
if (cb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
csync_log_cb = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
csync_log_callback csync_get_log_callback(void) {
|
||||
return csync_log_cb;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file csync_log.h
|
||||
*
|
||||
* @brief Logging interface of csync
|
||||
*
|
||||
* @defgroup csyncLogInternals csync logging internals
|
||||
* @ingroup csyncInternalAPI
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _CSYNC_LOG_H
|
||||
#define _CSYNC_LOG_H
|
||||
|
||||
/* GCC have printf type attribute check. */
|
||||
#ifndef PRINTF_ATTRIBUTE
|
||||
#ifdef __GNUC__
|
||||
#ifdef _WIN32
|
||||
#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__gnu_printf__, a, b)))
|
||||
#else
|
||||
#define PRINTF_ATTRIBUTE(a,b) __attribute__ ((__format__ (__printf__, a, b)))
|
||||
#endif
|
||||
#else
|
||||
#define PRINTF_ATTRIBUTE(a,b)
|
||||
#endif /* __GNUC__ */
|
||||
#endif /* ndef PRINTF_ATTRIBUTE */
|
||||
|
||||
enum csync_log_priority_e {
|
||||
CSYNC_LOG_PRIORITY_NOLOG = 0,
|
||||
CSYNC_LOG_PRIORITY_FATAL,
|
||||
CSYNC_LOG_PRIORITY_ALERT,
|
||||
CSYNC_LOG_PRIORITY_CRIT,
|
||||
CSYNC_LOG_PRIORITY_ERROR,
|
||||
CSYNC_LOG_PRIORITY_WARN,
|
||||
CSYNC_LOG_PRIORITY_NOTICE,
|
||||
CSYNC_LOG_PRIORITY_INFO,
|
||||
CSYNC_LOG_PRIORITY_DEBUG,
|
||||
CSYNC_LOG_PRIORITY_TRACE,
|
||||
CSYNC_LOG_PRIORITY_NOTSET,
|
||||
CSYNC_LOG_PRIORITY_UNKNOWN,
|
||||
};
|
||||
|
||||
#define CSYNC_LOG(priority, ...) \
|
||||
csync_log(priority, __func__, __VA_ARGS__)
|
||||
|
||||
void csync_log(int verbosity,
|
||||
const char *function,
|
||||
const char *format, ...) PRINTF_ATTRIBUTE(3, 4);
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
#endif /* _CSYNC_LOG_H */
|
||||
|
||||
/* vim: set ft=c.doxygen ts=4 sw=4 et cindent: */
|
|
@ -44,7 +44,6 @@
|
|||
#include "c_lib.h"
|
||||
#include "csync_misc.h"
|
||||
#include "csync_macros.h"
|
||||
#include "csync_log.h"
|
||||
|
||||
#ifdef HAVE_FNMATCH
|
||||
#include <fnmatch.h>
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#include <QHash>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sqlite3.h>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
@ -153,7 +152,7 @@ struct OCSYNC_EXPORT csync_s {
|
|||
*
|
||||
* See ExcludedFiles in csync_exclude.
|
||||
*/
|
||||
std::function<CSYNC_EXCLUDE_TYPE(const char *path, int filetype)> exclude_traversal_fn;
|
||||
std::function<CSYNC_EXCLUDE_TYPE(const char *path, ItemType filetype)> exclude_traversal_fn;
|
||||
|
||||
struct {
|
||||
std::unordered_map<ByteArrayRef, QByteArray, ByteArrayRefHash> folder_renamed_to; // map from->to
|
||||
|
@ -203,6 +202,8 @@ struct OCSYNC_EXPORT csync_s {
|
|||
|
||||
bool ignore_hidden_files = true;
|
||||
|
||||
bool upload_conflict_files = false;
|
||||
|
||||
csync_s(const char *localUri, OCC::SyncJournalDb *statedb);
|
||||
~csync_s();
|
||||
int reinitialize();
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "csync_rename.h"
|
||||
#include "common/c_jhash.h"
|
||||
#include "common/asserts.h"
|
||||
#include "common/syncjournalfilerecord.h"
|
||||
|
||||
#include <QLoggingCategory>
|
||||
Q_LOGGING_CATEGORY(lcReconcile, "sync.csync.reconciler", QtInfoMsg)
|
||||
|
@ -177,7 +178,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
// other is found as well?
|
||||
qCDebug(lcReconcile, "Other has already been renamed to %s",
|
||||
other->rename_path.constData());
|
||||
} else if (cur->type == CSYNC_FTW_TYPE_DIR
|
||||
} else if (cur->type == ItemTypeDirectory
|
||||
// The local replica is reconciled first, so the remote tree would
|
||||
// have either NONE or UPDATE_METADATA if the remote file is safe to
|
||||
// move.
|
||||
|
@ -278,26 +279,16 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
/* If the file already exist on the other side, we have a conflict.
|
||||
Abort the rename and consider it is a new file. */
|
||||
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
/* fall trough */
|
||||
/* fall through */
|
||||
/* file on current replica is changed or new */
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
// This operation is usually a no-op and will by default return false
|
||||
if (csync_file_locked_or_open(ctx->local.uri, cur->path)) {
|
||||
qCDebug(lcReconcile, "[Reconciler] IGNORING file %s/%s since it is locked / open", ctx->local.uri, cur->path.constData());
|
||||
cur->instruction = CSYNC_INSTRUCTION_ERROR;
|
||||
if (cur->error_status == CSYNC_STATUS_OK) // don't overwrite error
|
||||
cur->error_status = CYSNC_STATUS_FILE_LOCKED_OR_OPEN;
|
||||
break;
|
||||
} else {
|
||||
//qCDebug(lcReconcile, "[Reconciler] not ignoring file %s/%s", ctx->local.uri, cur->path);
|
||||
}
|
||||
switch (other->instruction) {
|
||||
/* file on other replica is changed or new */
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
if (other->type == CSYNC_FTW_TYPE_DIR &&
|
||||
cur->type == CSYNC_FTW_TYPE_DIR) {
|
||||
if (other->type == ItemTypeDirectory &&
|
||||
cur->type == ItemTypeDirectory) {
|
||||
// Folders of the same path are always considered equals
|
||||
is_conflict = false;
|
||||
} else {
|
||||
|
@ -316,6 +307,35 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
(ctx->current == REMOTE_REPLICA ? cur->checksumHeader : other->checksumHeader);
|
||||
if (!remoteChecksumHeader.isEmpty()) {
|
||||
is_conflict = true;
|
||||
|
||||
// Do we have an UploadInfo for this?
|
||||
// Maybe the Upload was completed, but the connection was broken just before
|
||||
// we recieved the etag (Issue #5106)
|
||||
auto up = ctx->statedb->getUploadInfo(cur->path);
|
||||
if (up._valid && up._contentChecksum == remoteChecksumHeader) {
|
||||
// Solve the conflict into an upload, or nothing
|
||||
auto remoteNode = ctx->current == REMOTE_REPLICA ? cur : other;
|
||||
auto localNode = ctx->current == REMOTE_REPLICA ? other : cur;
|
||||
remoteNode->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
localNode->instruction = up._modtime == localNode->modtime ? CSYNC_INSTRUCTION_UPDATE_METADATA : CSYNC_INSTRUCTION_SYNC;
|
||||
|
||||
// Update the etag and other server metadata in the journal already
|
||||
// (We can't use a typical CSYNC_INSTRUCTION_UPDATE_METADATA because
|
||||
// we must not store the size/modtime from the file system)
|
||||
OCC::SyncJournalFileRecord rec;
|
||||
if (ctx->statedb->getFileRecord(remoteNode->path, &rec)) {
|
||||
rec._path = remoteNode->path;
|
||||
rec._etag = remoteNode->etag;
|
||||
rec._fileId = remoteNode->file_id;
|
||||
rec._modtime = remoteNode->modtime;
|
||||
rec._type = remoteNode->type;
|
||||
rec._fileSize = remoteNode->size;
|
||||
rec._remotePerm = remoteNode->remotePerm;
|
||||
rec._checksumHeader = remoteNode->checksumHeader;
|
||||
ctx->statedb->setFileRecordMetadata(rec);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SO: If there is no checksum, we can have !is_conflict here
|
||||
|
@ -345,7 +365,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
// needs to delete the other entity first.
|
||||
cur->instruction = CSYNC_INSTRUCTION_TYPE_CHANGE;
|
||||
other->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else if (cur->type == CSYNC_FTW_TYPE_DIR) {
|
||||
} else if (cur->type == ItemTypeDirectory) {
|
||||
cur->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
other->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else {
|
||||
|
@ -378,7 +398,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
const char *repo = ctx->current == REMOTE_REPLICA ? "server" : "client";
|
||||
if(cur->instruction ==CSYNC_INSTRUCTION_NONE)
|
||||
{
|
||||
if(cur->type == CSYNC_FTW_TYPE_DIR)
|
||||
if(cur->type == ItemTypeDirectory)
|
||||
{
|
||||
qCDebug(lcReconcile,
|
||||
"%-30s %s dir: %s",
|
||||
|
@ -397,7 +417,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
|||
}
|
||||
else
|
||||
{
|
||||
if(cur->type == CSYNC_FTW_TYPE_DIR)
|
||||
if(cur->type == ItemTypeDirectory)
|
||||
{
|
||||
qCInfo(lcReconcile,
|
||||
"%-30s %s dir: %s",
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "config_csync.h"
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "csync_time.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.time"
|
||||
#include "csync_log.h"
|
||||
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
# ifdef _POSIX_MONOTONIC_CLOCK
|
||||
# define CSYNC_CLOCK CLOCK_MONOTONIC
|
||||
# else
|
||||
# define CSYNC_CLOCK CLOCK_REALTIME
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
int csync_gettime(struct timespec *tp)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
__int64 wintime;
|
||||
GetSystemTimeAsFileTime((FILETIME*)&wintime);
|
||||
wintime -= 116444736000000000ll; //1jan1601 to 1jan1970
|
||||
tp->tv_sec = wintime / 10000000ll; //seconds
|
||||
tp->tv_nsec = wintime % 10000000ll * 100; //nano-seconds
|
||||
#elif defined(HAVE_CLOCK_GETTIME)
|
||||
return clock_gettime(CSYNC_CLOCK, tp);
|
||||
#else
|
||||
struct timeval tv;
|
||||
|
||||
if (gettimeofday(&tv, NULL) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
tp->tv_sec = tv.tv_sec;
|
||||
tp->tv_nsec = tv.tv_usec * 1000;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef CSYNC_CLOCK
|
||||
|
||||
void csync_sleep(unsigned int msecs)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
Sleep(msecs);
|
||||
#else
|
||||
usleep(msecs * 1000);
|
||||
#endif
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _CSYNC_TIME_H
|
||||
#define _CSYNC_TIME_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
|
||||
int csync_gettime(struct timespec *tp);
|
||||
void csync_sleep(unsigned int msecs);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CSYNC_TIME_H */
|
|
@ -117,7 +117,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (fs->type == CSYNC_FTW_TYPE_SKIP) {
|
||||
if (fs->type == ItemTypeSkip) {
|
||||
excluded =CSYNC_FILE_EXCLUDE_STAT_FAILED;
|
||||
} else {
|
||||
/* Check if file is excluded */
|
||||
|
@ -151,21 +151,29 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
}
|
||||
}
|
||||
|
||||
if (ctx->current == REMOTE_REPLICA && QTextCodec::codecForLocale()->mibEnum() != 106) {
|
||||
auto localCodec = QTextCodec::codecForLocale();
|
||||
if (ctx->current == REMOTE_REPLICA && localCodec->mibEnum() != 106) {
|
||||
/* If the locale codec is not UTF-8, we must check that the filename from the server can
|
||||
* be encoded in the local file system. */
|
||||
if (!QTextCodec::codecForLocale()->canEncode(QString::fromUtf8(fs->path))) {
|
||||
* be encoded in the local file system.
|
||||
*
|
||||
* We cannot use QTextCodec::canEncode() since that can incorrectly return true, see
|
||||
* https://bugreports.qt.io/browse/QTBUG-6925.
|
||||
*/
|
||||
QTextEncoder encoder(localCodec, QTextCodec::ConvertInvalidToNull);
|
||||
if (encoder.fromUnicode(QString::fromUtf8(fs->path)).contains('\0')) {
|
||||
qCDebug(lcUpdate, "cannot encode %s to local encoding %d",
|
||||
fs->path.constData(), localCodec->mibEnum());
|
||||
excluded = CSYNC_FILE_EXCLUDE_CANNOT_ENCODE;
|
||||
}
|
||||
}
|
||||
|
||||
if (fs->type == CSYNC_FTW_TYPE_FILE ) {
|
||||
if (fs->type == ItemTypeFile ) {
|
||||
if (fs->modtime == 0) {
|
||||
qCDebug(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
||||
}
|
||||
}
|
||||
|
||||
if (excluded > CSYNC_NOT_EXCLUDED || fs->type == CSYNC_FTW_TYPE_SLINK) {
|
||||
if (excluded > CSYNC_NOT_EXCLUDED || fs->type == ItemTypeSoftLink) {
|
||||
fs->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
if (ctx->current_fs) {
|
||||
ctx->current_fs->has_ignored_files = true;
|
||||
|
@ -238,7 +246,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
bool metadata_differ = (ctx->current == REMOTE_REPLICA && (fs->file_id != base._fileId
|
||||
|| fs->remotePerm != base._remotePerm))
|
||||
|| (ctx->current == LOCAL_REPLICA && fs->inode != base._inode);
|
||||
if (fs->type == CSYNC_FTW_TYPE_DIR && ctx->current == REMOTE_REPLICA
|
||||
if (fs->type == ItemTypeDirectory && ctx->current == REMOTE_REPLICA
|
||||
&& !metadata_differ && ctx->read_remote_from_db) {
|
||||
/* If both etag and file id are equal for a directory, read all contents from
|
||||
* the database.
|
||||
|
@ -277,7 +285,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
|
||||
bool isRename =
|
||||
base.isValid() && base._type == fs->type
|
||||
&& ((base._modtime == fs->modtime && base._fileSize == fs->size) || fs->type == CSYNC_FTW_TYPE_DIR)
|
||||
&& ((base._modtime == fs->modtime && base._fileSize == fs->size) || fs->type == ItemTypeDirectory)
|
||||
#ifdef NO_RENAME_EXTENSION
|
||||
&& _csync_sameextension(base._path, fs->path)
|
||||
#endif
|
||||
|
@ -286,7 +294,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
|
||||
// Verify the checksum where possible
|
||||
if (isRename && !base._checksumHeader.isEmpty() && ctx->callbacks.checksum_hook
|
||||
&& fs->type == CSYNC_FTW_TYPE_FILE) {
|
||||
&& fs->type == ItemTypeFile) {
|
||||
fs->checksumHeader = ctx->callbacks.checksum_hook(
|
||||
_rel_to_abs(ctx, fs->path), base._checksumHeader,
|
||||
ctx->callbacks.checksum_userdata);
|
||||
|
@ -300,7 +308,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
qCDebug(lcUpdate, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
/* inode found so the file has been renamed */
|
||||
fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
|
||||
if (fs->type == CSYNC_FTW_TYPE_DIR) {
|
||||
if (fs->type == ItemTypeDirectory) {
|
||||
csync_rename_record(ctx, base._path, fs->path);
|
||||
}
|
||||
}
|
||||
|
@ -325,7 +333,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
done = true;
|
||||
return;
|
||||
}
|
||||
if (fs->type != CSYNC_FTW_TYPE_DIR && base._etag != fs->etag) {
|
||||
if (fs->type != ItemTypeDirectory && base._etag != fs->etag) {
|
||||
/* File with different etag, don't do a rename, but download the file again */
|
||||
qCWarning(lcUpdate, "file etag different, not a rename");
|
||||
done = true;
|
||||
|
@ -333,7 +341,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
}
|
||||
|
||||
// Record directory renames
|
||||
if (fs->type == CSYNC_FTW_TYPE_DIR) {
|
||||
if (fs->type == ItemTypeDirectory) {
|
||||
// If the same folder was already renamed by a different entry,
|
||||
// skip to the next candidate
|
||||
if (ctx->renames.folder_renamed_to.count(base._path) > 0) {
|
||||
|
@ -354,7 +362,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
|||
}
|
||||
|
||||
if (fs->instruction == CSYNC_INSTRUCTION_NEW
|
||||
&& fs->type == CSYNC_FTW_TYPE_DIR
|
||||
&& fs->type == ItemTypeDirectory
|
||||
&& ctx->current == REMOTE_REPLICA
|
||||
&& ctx->callbacks.checkSelectiveSyncNewFolderHook) {
|
||||
if (ctx->callbacks.checkSelectiveSyncNewFolderHook(ctx->callbacks.update_callback_userdata, fs->path, fs->remotePerm)) {
|
||||
|
@ -369,7 +377,7 @@ out:
|
|||
|
||||
/* Set the ignored error string. */
|
||||
if (fs->instruction == CSYNC_INSTRUCTION_IGNORE) {
|
||||
if( fs->type == CSYNC_FTW_TYPE_SLINK ) {
|
||||
if( fs->type == ItemTypeSoftLink ) {
|
||||
fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK; /* Symbolic links are ignored. */
|
||||
} else {
|
||||
if (excluded == CSYNC_FILE_EXCLUDE_LIST) {
|
||||
|
@ -394,13 +402,13 @@ out:
|
|||
if (fs->instruction != CSYNC_INSTRUCTION_NONE
|
||||
&& fs->instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& fs->instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
|
||||
&& fs->type != CSYNC_FTW_TYPE_DIR) {
|
||||
&& fs->type != ItemTypeDirectory) {
|
||||
fs->child_modified = true;
|
||||
}
|
||||
|
||||
// If conflict files are uploaded, they won't be marked as IGNORE / CSYNC_FILE_EXCLUDE_CONFLICT
|
||||
// but we still want them marked!
|
||||
if (OCC::Utility::shouldUploadConflictFiles()) {
|
||||
if (ctx->upload_conflict_files) {
|
||||
if (OCC::Utility::isConflictFile(fs->path.constData())) {
|
||||
fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE;
|
||||
}
|
||||
|
@ -436,21 +444,21 @@ int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
|
|||
}
|
||||
|
||||
switch (fs->type) {
|
||||
case CSYNC_FTW_TYPE_FILE:
|
||||
case ItemTypeFile:
|
||||
if (ctx->current == REMOTE_REPLICA) {
|
||||
qCDebug(lcUpdate, "file: %s [file_id=%s size=%" PRIu64 "]", fs->path.constData(), fs->file_id.constData(), fs->size);
|
||||
} else {
|
||||
qCDebug(lcUpdate, "file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", fs->path.constData(), fs->inode, fs->size);
|
||||
}
|
||||
break;
|
||||
case CSYNC_FTW_TYPE_DIR: /* enter directory */
|
||||
case ItemTypeDirectory: /* enter directory */
|
||||
if (ctx->current == REMOTE_REPLICA) {
|
||||
qCDebug(lcUpdate, "directory: %s [file_id=%s]", fs->path.constData(), fs->file_id.constData());
|
||||
} else {
|
||||
qCDebug(lcUpdate, "directory: %s [inode=%" PRIu64 "]", fs->path.constData(), fs->inode);
|
||||
}
|
||||
break;
|
||||
case CSYNC_FTW_TYPE_SLINK:
|
||||
case ItemTypeSoftLink:
|
||||
qCDebug(lcUpdate, "symlink: %s - not supported", fs->path.constData());
|
||||
break;
|
||||
default:
|
||||
|
@ -503,7 +511,7 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
|
|||
if (ctx->exclude_traversal_fn)
|
||||
excluded = ctx->exclude_traversal_fn(st->path, st->type);
|
||||
if (excluded != CSYNC_NOT_EXCLUDED) {
|
||||
qDebug(lcUpdate, "%s excluded (%d)", st->path.constData(), excluded);
|
||||
qInfo(lcUpdate, "%s excluded from db read (%d)", st->path.constData(), excluded);
|
||||
|
||||
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE
|
||||
|| excluded == CSYNC_FILE_SILENTLY_EXCLUDED) {
|
||||
|
@ -522,7 +530,7 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
|
|||
ctx->status_code = CSYNC_STATUS_STATEDB_LOAD_ERROR;
|
||||
return false;
|
||||
}
|
||||
qDebug(lcUpdate, "%" PRId64 " entries read below path %s from db.", count, uri);
|
||||
qInfo(lcUpdate, "%" PRId64 " entries read below path %s from db.", count, uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -690,7 +698,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
|||
}
|
||||
|
||||
previous_fs = ctx->current_fs;
|
||||
bool recurse = dirent->type == CSYNC_FTW_TYPE_DIR;
|
||||
bool recurse = dirent->type == ItemTypeDirectory;
|
||||
|
||||
/* Call walker function for each file */
|
||||
rc = fn(ctx, std::move(dirent));
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
#include "csync_util.h"
|
||||
#include "vio/csync_vio.h"
|
||||
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.util"
|
||||
#include "csync_log.h"
|
||||
Q_LOGGING_CATEGORY(lcCSyncUtils, "sync.csync.utils", QtInfoMsg)
|
||||
|
||||
|
||||
typedef struct {
|
||||
const char *instr_str;
|
||||
|
@ -102,29 +102,8 @@ void csync_memstat_check(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "Memory: %dK total size, %dK resident, %dK shared",
|
||||
m.size * 4, m.resident * 4, m.shared * 4);
|
||||
}
|
||||
|
||||
bool (*csync_file_locked_or_open_ext) (const char*) = 0; // filled in by library user
|
||||
void set_csync_file_locked_or_open_ext(bool (*f) (const char*));
|
||||
void set_csync_file_locked_or_open_ext(bool (*f) (const char*)) {
|
||||
csync_file_locked_or_open_ext = f;
|
||||
}
|
||||
|
||||
bool csync_file_locked_or_open( const char *dir, const char *fname) {
|
||||
char *tmp_uri = NULL;
|
||||
bool ret;
|
||||
if (!csync_file_locked_or_open_ext) {
|
||||
return false;
|
||||
}
|
||||
if (asprintf(&tmp_uri, "%s/%s", dir, fname) < 0) {
|
||||
return -1;
|
||||
}
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "csync_file_locked_or_open %s", tmp_uri);
|
||||
ret = csync_file_locked_or_open_ext(tmp_uri);
|
||||
SAFE_FREE(tmp_uri);
|
||||
return ret;
|
||||
qCInfo(lcCSyncUtils, "Memory: %dK total size, %dK resident, %dK shared",
|
||||
m.size * 4, m.resident * 4, m.shared * 4);
|
||||
}
|
||||
|
||||
#ifndef HAVE_TIMEGM
|
||||
|
|
|
@ -30,8 +30,6 @@ const char OCSYNC_EXPORT *csync_instruction_str(enum csync_instructions_e instr)
|
|||
|
||||
void OCSYNC_EXPORT csync_memstat_check(void);
|
||||
|
||||
bool OCSYNC_EXPORT csync_file_locked_or_open( const char *dir, const char *fname);
|
||||
|
||||
/* Returns true if we're reasonably certain that hash equality
|
||||
* for the header means content equality.
|
||||
*
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
project(cstdlib)
|
||||
|
||||
set(CSTDLIB_PUBLIC_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
CACHE INTERNAL "cstdlib public include directories"
|
||||
)
|
||||
|
||||
set(CSTDLIB_LIBRARY
|
||||
cstdlib
|
||||
CACHE INTERNAL "cstdlib library"
|
||||
)
|
||||
|
||||
set(CSTDLIB_LINK_LIBRARIES
|
||||
${CSTDLIB_LIBRARY}
|
||||
)
|
||||
|
||||
set(cstdlib_SRCS
|
||||
c_alloc.c
|
||||
c_path.c
|
||||
c_string.c
|
||||
c_time.c
|
||||
c_utf8.cpp
|
||||
)
|
||||
|
||||
if(NOT HAVE_ASPRINTF AND NOT HAVE___MINGW_ASPRINTF)
|
||||
list(APPEND cstdlib_SRCS
|
||||
asprintf.c
|
||||
)
|
||||
endif()
|
||||
|
||||
include_directories(
|
||||
${CSTDLIB_PUBLIC_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_library(${CSTDLIB_LIBRARY} STATIC ${cstdlib_SRCS})
|
||||
if(NOT WIN32)
|
||||
add_definitions( -fPIC )
|
||||
endif()
|
||||
qt5_use_modules(${CSTDLIB_LIBRARY} Core)
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
#include "c_macro.h"
|
||||
#include "c_alloc.h"
|
||||
#include "c_path.h"
|
||||
#include "c_string.h"
|
||||
#include "c_time.h"
|
||||
#include "c_private.h"
|
||||
|
|
|
@ -1,392 +0,0 @@
|
|||
/*
|
||||
* cynapses libc functions
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "c_private.h"
|
||||
#include "c_alloc.h"
|
||||
#include "c_path.h"
|
||||
#include "c_string.h"
|
||||
#include "c_utf8.h"
|
||||
|
||||
/*
|
||||
* dirname - parse directory component.
|
||||
*/
|
||||
char *c_dirname (const char *path) {
|
||||
char *newbuf = NULL;
|
||||
unsigned int len;
|
||||
|
||||
if (path == NULL || *path == '\0') {
|
||||
return c_strdup(".");
|
||||
}
|
||||
|
||||
len = strlen(path);
|
||||
|
||||
/* Remove trailing slashes */
|
||||
while(len > 0 && path[len - 1] == '/') --len;
|
||||
|
||||
/* We have only slashes */
|
||||
if (len == 0) {
|
||||
return c_strdup("/");
|
||||
}
|
||||
|
||||
/* goto next slash */
|
||||
while(len > 0 && path[len - 1] != '/') --len;
|
||||
|
||||
if (len == 0) {
|
||||
return c_strdup(".");
|
||||
} else if (len == 1) {
|
||||
return c_strdup("/");
|
||||
}
|
||||
|
||||
/* Remove slashes again */
|
||||
while(len > 0 && path[len - 1] == '/') --len;
|
||||
|
||||
newbuf = c_malloc(len + 1);
|
||||
|
||||
strncpy(newbuf, path, len);
|
||||
newbuf[len] = '\0';
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
char *c_basename (const char *path) {
|
||||
char *newbuf = NULL;
|
||||
const char *s;
|
||||
unsigned int len;
|
||||
|
||||
if (path == NULL || *path == '\0') {
|
||||
return c_strdup(".");
|
||||
}
|
||||
|
||||
len = strlen(path);
|
||||
/* Remove trailing slashes */
|
||||
while(len > 0 && path[len - 1] == '/') --len;
|
||||
|
||||
/* We have only slashes */
|
||||
if (len == 0) {
|
||||
return c_strdup("/");
|
||||
}
|
||||
|
||||
while(len > 0 && path[len - 1] != '/') --len;
|
||||
|
||||
if (len > 0) {
|
||||
s = path + len;
|
||||
len = strlen(s);
|
||||
|
||||
while(len > 0 && s[len - 1] == '/') --len;
|
||||
} else {
|
||||
return c_strdup(path);
|
||||
}
|
||||
|
||||
newbuf = c_malloc(len + 1);
|
||||
|
||||
strncpy(newbuf, s, len);
|
||||
newbuf[len] = '\0';
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
int c_parse_uri(const char *uri,
|
||||
char **scheme,
|
||||
char **user, char **passwd,
|
||||
char **host, unsigned int *port,
|
||||
char **path) {
|
||||
const char *p, *z;
|
||||
|
||||
if (uri == NULL || *uri == '\0') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p = z = uri;
|
||||
|
||||
/* check for valid scheme; git+ssh, pop3 */
|
||||
while (isalpha((int) *p) || isdigit((int) *p) ||
|
||||
*p == '+' || *p == '-') {
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p++;
|
||||
}
|
||||
|
||||
/* get scheme */
|
||||
if (*p == ':') {
|
||||
if (scheme != NULL) {
|
||||
*scheme = c_strndup(z, p - z);
|
||||
|
||||
if (*scheme == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
p++;
|
||||
z = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p = z;
|
||||
|
||||
/* do we have a hostname */
|
||||
if (p[0] == '/' && p[1] == '/') {
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
z += 2;
|
||||
p = z;
|
||||
|
||||
/* check for user and passwd */
|
||||
while (*p && *p != '@' && *p != '/') {
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^ or ^
|
||||
* z = ^
|
||||
*/
|
||||
p++;
|
||||
}
|
||||
|
||||
/* check for user and password */
|
||||
if (*p == '@') {
|
||||
const char *q;
|
||||
|
||||
q = p;
|
||||
|
||||
/* check if we have a password */
|
||||
while (q > z && *q != ':') {
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
* q = ^
|
||||
*/
|
||||
q--;
|
||||
}
|
||||
|
||||
/* password found */
|
||||
if (*q == ':') {
|
||||
if (user != NULL) {
|
||||
*user = c_strndup(z, q - z);
|
||||
if (*user == NULL) {
|
||||
errno = ENOMEM;
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (passwd != NULL) {
|
||||
*passwd = c_strndup(q + 1, p - (q + 1));
|
||||
if (*passwd == NULL) {
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
if (user != NULL) SAFE_FREE(*user);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* user only */
|
||||
if (user != NULL) {
|
||||
*user = c_strndup(z, p - z);
|
||||
if( *user == NULL) {
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p++;
|
||||
z = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p = z;
|
||||
|
||||
/* check for IPv6 address */
|
||||
if (*p == '[') {
|
||||
/*
|
||||
* uri = scheme://user:password@[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p++;
|
||||
|
||||
/* check if we have a valid IPv6 address */
|
||||
while (*p && (isxdigit((int) *p) || *p == '.' || *p == ':')) {
|
||||
/*
|
||||
* uri = scheme://user:password@[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p++;
|
||||
}
|
||||
|
||||
/* valid IPv6 address found */
|
||||
if (*p == ']') {
|
||||
/*
|
||||
* uri = scheme://user:password@[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
z++;
|
||||
|
||||
if (host != NULL) {
|
||||
*host = c_strndup(z, p - z);
|
||||
if (*host == NULL) {
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
if (user != NULL) SAFE_FREE(*user);
|
||||
if (passwd != NULL) SAFE_FREE(*passwd);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* uri = scheme://user:password@[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
p++;
|
||||
} else {
|
||||
/* invalid IPv6 address, assume a hostname */
|
||||
p = z;
|
||||
|
||||
while (*p && *p != ':' && *p != '/') {
|
||||
p++;
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^ or ^
|
||||
* z = ^
|
||||
*/
|
||||
}
|
||||
|
||||
if (host != NULL) {
|
||||
*host = c_strndup(z, p - z);
|
||||
if (*host == NULL) {
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
if (user != NULL) SAFE_FREE(*user);
|
||||
if (passwd != NULL) SAFE_FREE(*passwd);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* check for hostname */
|
||||
while (*p && *p != ':' && *p != '/') {
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^ ^
|
||||
* z = ^
|
||||
*/
|
||||
p++;
|
||||
}
|
||||
|
||||
if (host != NULL) {
|
||||
*host = c_strndup(z, p - z);
|
||||
if (*host == NULL) {
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
if (user != NULL) SAFE_FREE(*user);
|
||||
if (passwd != NULL) SAFE_FREE(*passwd);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check for port */
|
||||
if (*p == ':') {
|
||||
char **e = NULL;
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
z = ++p;
|
||||
|
||||
/* get only the digits */
|
||||
while (isdigit((int) *p)) {
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
* z = ^
|
||||
*/
|
||||
e = (char **) &p;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (port != NULL) {
|
||||
*port = strtoul(z, e, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* uri = scheme://user:password@host:port/path
|
||||
* p = ^
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* get the path with the leading slash */
|
||||
if (*p == '/') {
|
||||
if (path != NULL) {
|
||||
*path = c_strdup(p);
|
||||
if (*path == NULL) {
|
||||
if (scheme != NULL) SAFE_FREE(*scheme);
|
||||
if (user != NULL) SAFE_FREE(*user);
|
||||
if (passwd != NULL) SAFE_FREE(*passwd);
|
||||
if (host != NULL) SAFE_FREE(*host);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* cynapses libc functions
|
||||
*
|
||||
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file c_path.h
|
||||
*
|
||||
* @brief Interface of the cynapses libc path functions
|
||||
*
|
||||
* @defgroup cynPathInternals cynapses libc path functions
|
||||
* @ingroup cynLibraryAPI
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef _C_PATH_H
|
||||
#define _C_PATH_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "c_macro.h"
|
||||
#include "c_private.h"
|
||||
|
||||
/**
|
||||
* @brief Parse directory component.
|
||||
*
|
||||
* dirname breaks a null-terminated pathname string into a directory component.
|
||||
* In the usual case, c_dirname() returns the string up to, but not including,
|
||||
* the final '/'. Trailing '/' characters are not counted as part of the
|
||||
* pathname. The caller must free the memory.
|
||||
*
|
||||
* @param path The path to parse.
|
||||
*
|
||||
* @return The dirname of path or NULL if we can't allocate memory. If path
|
||||
* does not contain a slash, c_dirname() returns the string ".". If
|
||||
* path is the string "/", it returns the string "/". If path is
|
||||
* NULL or an empty string, "." is returned.
|
||||
*/
|
||||
char *c_dirname(const char *path);
|
||||
|
||||
/**
|
||||
* @brief basename - parse filename component.
|
||||
*
|
||||
* basename breaks a null-terminated pathname string into a filename component.
|
||||
* c_basename() returns the component following the final '/'. Trailing '/'
|
||||
* characters are not counted as part of the pathname.
|
||||
*
|
||||
* @param path The path to parse.
|
||||
*
|
||||
* @return The filename of path or NULL if we can't allocate memory. If path
|
||||
* is a the string "/", basename returns the string "/". If path is
|
||||
* NULL or an empty string, "." is returned.
|
||||
*/
|
||||
char *c_basename (const char *path);
|
||||
|
||||
/**
|
||||
* @brief parse a uri and split it into components.
|
||||
*
|
||||
* parse_uri parses an uri in the format
|
||||
*
|
||||
* [<scheme>:][//[<user>[:<password>]@]<host>[:<port>]]/[<path>]
|
||||
*
|
||||
* into its compoments. If you only want a special component,
|
||||
* pass NULL for all other components. All components will be allocated if they have
|
||||
* been found.
|
||||
*
|
||||
* @param uri The uri to parse.
|
||||
* @param scheme String for the scheme component
|
||||
* @param user String for the username component
|
||||
* @param passwd String for the password component
|
||||
* @param host String for the password component
|
||||
* @param port Integer for the port
|
||||
* @param path String for the path component with a leading slash.
|
||||
*
|
||||
* @return 0 on success, < 0 on error.
|
||||
*/
|
||||
int c_parse_uri(const char *uri, char **scheme, char **user, char **passwd,
|
||||
char **host, unsigned int *port, char **path);
|
||||
|
||||
/**
|
||||
* @brief Parts of a path.
|
||||
*
|
||||
* @param directory '\0' terminated path including the final '/'
|
||||
*
|
||||
* @param filename '\0' terminated string
|
||||
*
|
||||
* @param extension '\0' terminated string
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char * directory;
|
||||
char * filename;
|
||||
char * extension;
|
||||
} C_PATHINFO;
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _C_PATH_H */
|
|
@ -102,10 +102,6 @@ typedef struct stat csync_stat_t;
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRERROR_R
|
||||
#define strerror_r(errnum, buf, buflen) snprintf(buf, buflen, "%s", strerror(errnum))
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LSTAT
|
||||
#define lstat _stat
|
||||
#endif
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
#include <wchar.h>
|
||||
|
||||
#include "c_string.h"
|
||||
#include "c_path.h"
|
||||
#include "c_alloc.h"
|
||||
#include "c_macro.h"
|
||||
|
||||
|
@ -64,106 +63,3 @@ int c_streq(const char *a, const char *b) {
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
c_strlist_t *c_strlist_new(size_t size) {
|
||||
c_strlist_t *strlist = NULL;
|
||||
|
||||
if (size == 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlist = c_malloc(sizeof(c_strlist_t));
|
||||
if (strlist == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlist->vector = (char **) c_malloc(size * sizeof(char *));
|
||||
strlist->count = 0;
|
||||
strlist->size = size;
|
||||
|
||||
return strlist;
|
||||
}
|
||||
|
||||
c_strlist_t *c_strlist_expand(c_strlist_t *strlist, size_t size) {
|
||||
if (strlist == NULL || size == 0) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlist->size >= size) {
|
||||
return strlist;
|
||||
}
|
||||
|
||||
strlist->vector = (char **) c_realloc(strlist->vector, size * sizeof(char *));
|
||||
if (strlist->vector == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strlist->size = size;
|
||||
|
||||
return strlist;
|
||||
}
|
||||
|
||||
int c_strlist_add(c_strlist_t *strlist, const char *string) {
|
||||
if (strlist == NULL || string == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlist->count < strlist->size) {
|
||||
strlist->vector[strlist->count] = c_strdup(string);
|
||||
if (strlist->vector[strlist->count] == NULL) {
|
||||
return -1;
|
||||
}
|
||||
strlist->count++;
|
||||
} else {
|
||||
errno = ENOBUFS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int c_strlist_add_grow(c_strlist_t **strlist, const char *string) {
|
||||
if (*strlist == NULL) {
|
||||
*strlist = c_strlist_new(32);
|
||||
if (*strlist == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*strlist)->count == (*strlist)->size) {
|
||||
c_strlist_t *list = c_strlist_expand(*strlist, 2 * (*strlist)->size);
|
||||
if (list == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*strlist = list;
|
||||
}
|
||||
|
||||
return c_strlist_add(*strlist, string);
|
||||
}
|
||||
|
||||
void c_strlist_clear(c_strlist_t *strlist) {
|
||||
size_t i = 0;
|
||||
|
||||
if (strlist == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < strlist->count; i++) {
|
||||
SAFE_FREE(strlist->vector[i]);
|
||||
}
|
||||
|
||||
strlist->count = 0;
|
||||
}
|
||||
|
||||
void c_strlist_destroy(c_strlist_t *strlist) {
|
||||
|
||||
if (strlist == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
c_strlist_clear(strlist);
|
||||
SAFE_FREE(strlist->vector);
|
||||
SAFE_FREE(strlist);
|
||||
}
|
||||
|
|
|
@ -41,28 +41,6 @@ extern "C" {
|
|||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct c_strlist_s; typedef struct c_strlist_s c_strlist_t;
|
||||
|
||||
/**
|
||||
* @brief Structure for a stringlist
|
||||
*
|
||||
* Using a for loop you can access the strings saved in the vector.
|
||||
*
|
||||
* c_strlist_t strlist;
|
||||
* int i;
|
||||
* for (i = 0; i < strlist->count; i++) {
|
||||
* printf("value: %s", strlist->vector[i];
|
||||
* }
|
||||
*/
|
||||
struct c_strlist_s {
|
||||
/** The string vector */
|
||||
char **vector;
|
||||
/** The count of the strings saved in the vector */
|
||||
size_t count;
|
||||
/** Size of strings allocated */
|
||||
size_t size;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compare to strings case insensitively.
|
||||
*
|
||||
|
@ -84,68 +62,6 @@ int c_strncasecmp(const char *a, const char *b, size_t n);
|
|||
*/
|
||||
int c_streq(const char *a, const char *b);
|
||||
|
||||
/**
|
||||
* @brief Create a new stringlist.
|
||||
*
|
||||
* @param size Size to allocate.
|
||||
*
|
||||
* @return Pointer to the newly allocated stringlist. NULL if an error occurred.
|
||||
*/
|
||||
c_strlist_t *c_strlist_new(size_t size);
|
||||
|
||||
/**
|
||||
* @brief Expand the stringlist
|
||||
*
|
||||
* @param strlist Stringlist to expand
|
||||
* @param size New size of the strlinglist to expand
|
||||
*
|
||||
* @return Pointer to the expanded stringlist. NULL if an error occurred.
|
||||
*/
|
||||
c_strlist_t *c_strlist_expand(c_strlist_t *strlist, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Add a string to the stringlist.
|
||||
*
|
||||
* Duplicates the string and stores it in the stringlist.
|
||||
*
|
||||
* @param strlist Stringlist to add the string.
|
||||
* @param string String to add.
|
||||
*
|
||||
* @return 0 on success, less than 0 and errno set if an error occurred.
|
||||
* ENOBUFS if the list is full.
|
||||
*/
|
||||
int c_strlist_add(c_strlist_t *strlist, const char *string);
|
||||
|
||||
/**
|
||||
* @brief Add a string to the stringlist, growing it if necessary
|
||||
*
|
||||
* Duplicates the string and stores it in the stringlist.
|
||||
* It also initializes the stringlist if it starts out as null.
|
||||
*
|
||||
* @param strlist Stringlist to add the string.
|
||||
* @param string String to add.
|
||||
*
|
||||
* @return 0 on success, less than 0 and errno set if an error occurred.
|
||||
*/
|
||||
int c_strlist_add_grow(c_strlist_t **strlist, const char *string);
|
||||
|
||||
/**
|
||||
* @brief Removes all strings from the list.
|
||||
*
|
||||
* Frees the strings.
|
||||
*
|
||||
* @param strlist Stringlist to clear
|
||||
*/
|
||||
void c_strlist_clear(c_strlist_t *strlist);
|
||||
|
||||
/**
|
||||
* @brief Destroy the memory of the stringlist.
|
||||
*
|
||||
* Frees the strings and the stringlist.
|
||||
*
|
||||
* @param strlist Stringlist to destroy
|
||||
*/
|
||||
void c_strlist_destroy(c_strlist_t *strlist);
|
||||
|
||||
/**
|
||||
* }@
|
||||
|
|
|
@ -22,52 +22,9 @@
|
|||
#include "c_private.h"
|
||||
#include "c_string.h"
|
||||
|
||||
#include "c_path.h"
|
||||
#include "c_time.h"
|
||||
#include "c_utf8.h"
|
||||
|
||||
struct timespec c_tspecdiff(struct timespec time1, struct timespec time0) {
|
||||
struct timespec ret;
|
||||
int xsec = 0;
|
||||
int sign = 1;
|
||||
|
||||
if (time0.tv_nsec > time1.tv_nsec) {
|
||||
xsec = (int) ((time0.tv_nsec - time1.tv_nsec) / (1E9 + 1));
|
||||
time0.tv_nsec -= (long int) (1E9 * xsec);
|
||||
time0.tv_sec += xsec;
|
||||
}
|
||||
|
||||
if ((time1.tv_nsec - time0.tv_nsec) > 1E9) {
|
||||
xsec = (int) ((time1.tv_nsec - time0.tv_nsec) / 1E9);
|
||||
time0.tv_nsec += (long int) (1E9 * xsec);
|
||||
time0.tv_sec -= xsec;
|
||||
}
|
||||
|
||||
ret.tv_sec = time1.tv_sec - time0.tv_sec;
|
||||
ret.tv_nsec = time1.tv_nsec - time0.tv_nsec;
|
||||
|
||||
if (time1.tv_sec < time0.tv_sec) {
|
||||
sign = -1;
|
||||
}
|
||||
|
||||
ret.tv_sec = ret.tv_sec * sign;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
double c_secdiff(struct timespec clock1, struct timespec clock0) {
|
||||
double ret;
|
||||
struct timespec diff;
|
||||
|
||||
diff = c_tspecdiff(clock1, clock0);
|
||||
|
||||
ret = diff.tv_sec;
|
||||
ret += (double) diff.tv_nsec / (double) 1E9;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_UTIMES
|
||||
int c_utimes(const char *uri, const struct timeval *times) {
|
||||
mbchar_t *wuri = c_utf8_path_to_locale(uri);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#ifndef _C_TIME_H
|
||||
#define _C_TIME_H
|
||||
|
||||
#include "ocsynclib.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -31,34 +33,7 @@ extern "C" {
|
|||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Calculate time difference
|
||||
*
|
||||
* The c_tspecdiff function returns the time elapsed between time time1 and time
|
||||
* time0 represented as timespec.
|
||||
*
|
||||
* @param time1 The time.
|
||||
* @param time0 The time.
|
||||
*
|
||||
* @return time elapsed between time1 and time0.
|
||||
*/
|
||||
struct timespec c_tspecdiff(struct timespec time1, struct timespec time0);
|
||||
|
||||
/**
|
||||
* @brief Calculate time difference.
|
||||
*
|
||||
* The function returns the time elapsed between time clock1 and time
|
||||
* clock0 represented as double (in seconds and milliseconds).
|
||||
*
|
||||
* @param clock1 The time.
|
||||
* @param clock0 The time.
|
||||
*
|
||||
* @return time elapsed between clock1 and clock0 in seconds and
|
||||
* milliseconds.
|
||||
*/
|
||||
double c_secdiff(struct timespec clock1, struct timespec clock0);
|
||||
|
||||
int c_utimes(const char *uri, const struct timeval *times);
|
||||
OCSYNC_EXPORT int c_utimes(const char *uri, const struct timeval *times);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -31,11 +31,12 @@
|
|||
#include "c_string.h"
|
||||
#include "c_utf8.h"
|
||||
#include "csync_util.h"
|
||||
#include "csync_log.h"
|
||||
#include "csync_vio.h"
|
||||
|
||||
#include "vio/csync_vio_local.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(lcCSyncVIOLocal, "sync.csync.vio_local", QtInfoMsg)
|
||||
|
||||
/*
|
||||
* directory functions
|
||||
*/
|
||||
|
@ -105,8 +106,7 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
|||
QByteArray fullPath = QByteArray() % const_cast<const char *>(handle->path) % '/' % QByteArray() % const_cast<const char *>(dirent->d_name);
|
||||
if (file_stat->path.isNull()) {
|
||||
file_stat->original_path = fullPath;
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Invalid characters in file/directory name, please rename: \"%s\" (%s)",
|
||||
dirent->d_name, handle->path);
|
||||
qCWarning(lcCSyncVIOLocal) << "Invalid characters in file/directory name, please rename:" << dirent->d_name << handle->path;
|
||||
}
|
||||
|
||||
/* Check for availability of d_type, see manpage. */
|
||||
|
@ -120,9 +120,9 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
|||
case DT_DIR:
|
||||
case DT_REG:
|
||||
if (dirent->d_type == DT_DIR) {
|
||||
file_stat->type = CSYNC_FTW_TYPE_DIR;
|
||||
file_stat->type = ItemTypeDirectory;
|
||||
} else {
|
||||
file_stat->type = CSYNC_FTW_TYPE_FILE;
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -135,7 +135,7 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
|||
|
||||
if (_csync_vio_local_stat_mb(fullPath.constData(), file_stat.get()) < 0) {
|
||||
// Will get excluded by _csync_detect_update.
|
||||
file_stat->type = CSYNC_FTW_TYPE_SKIP;
|
||||
file_stat->type = ItemTypeSkip;
|
||||
}
|
||||
return file_stat;
|
||||
}
|
||||
|
@ -160,17 +160,17 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf
|
|||
|
||||
switch (sb.st_mode & S_IFMT) {
|
||||
case S_IFDIR:
|
||||
buf->type = CSYNC_FTW_TYPE_DIR;
|
||||
buf->type = ItemTypeDirectory;
|
||||
break;
|
||||
case S_IFREG:
|
||||
buf->type = CSYNC_FTW_TYPE_FILE;
|
||||
buf->type = ItemTypeFile;
|
||||
break;
|
||||
case S_IFLNK:
|
||||
case S_IFSOCK:
|
||||
buf->type = CSYNC_FTW_TYPE_SLINK;
|
||||
buf->type = ItemTypeSoftLink;
|
||||
break;
|
||||
default:
|
||||
buf->type = CSYNC_FTW_TYPE_SKIP;
|
||||
buf->type = ItemTypeSkip;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@
|
|||
#include "c_lib.h"
|
||||
#include "c_utf8.h"
|
||||
#include "csync_util.h"
|
||||
#include "csync_log.h"
|
||||
#include "csync_vio.h"
|
||||
|
||||
#include "vio/csync_vio_local.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(lcCSyncVIOLocal, "sync.csync.vio_local", QtInfoMsg)
|
||||
|
||||
/*
|
||||
* directory functions
|
||||
|
@ -172,21 +172,21 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
|||
// Detect symlinks, and treat junctions as symlinks too.
|
||||
if (handle->ffd.dwReserved0 == IO_REPARSE_TAG_SYMLINK
|
||||
|| handle->ffd.dwReserved0 == IO_REPARSE_TAG_MOUNT_POINT) {
|
||||
file_stat->type = CSYNC_FTW_TYPE_SLINK;
|
||||
file_stat->type = ItemTypeSoftLink;
|
||||
} else {
|
||||
// The SIS and DEDUP reparse points should be treated as
|
||||
// regular files. We don't know about the other ones yet,
|
||||
// but will also treat them normally for now.
|
||||
file_stat->type = CSYNC_FTW_TYPE_FILE;
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
file_stat->type = CSYNC_FTW_TYPE_SKIP;
|
||||
file_stat->type = ItemTypeSkip;
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
file_stat->type = CSYNC_FTW_TYPE_DIR;
|
||||
file_stat->type = ItemTypeDirectory;
|
||||
} else {
|
||||
file_stat->type = CSYNC_FTW_TYPE_FILE;
|
||||
file_stat->type = ItemTypeFile;
|
||||
}
|
||||
|
||||
/* Check for the hidden flag */
|
||||
|
@ -204,7 +204,7 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
|||
|
||||
if (_csync_vio_local_stat_mb(fullPath.data(), file_stat.get()) < 0) {
|
||||
// Will get excluded by _csync_detect_update.
|
||||
file_stat->type = CSYNC_FTW_TYPE_SKIP;
|
||||
file_stat->type = ItemTypeSkip;
|
||||
}
|
||||
|
||||
return file_stat;
|
||||
|
@ -235,13 +235,13 @@ static int _csync_vio_local_stat_mb(const mbchar_t *wuri, csync_file_stat_t *buf
|
|||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
|
||||
NULL );
|
||||
if( h == INVALID_HANDLE_VALUE ) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "CreateFileW failed on %ls", wuri);
|
||||
qCCritical(lcCSyncVIOLocal, "CreateFileW failed on %ls", wuri);
|
||||
errno = GetLastError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!GetFileInformationByHandle( h, &fileInfo ) ) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_CRIT, "GetFileInformationByHandle failed on %ls", wuri);
|
||||
qCCritical(lcCSyncVIOLocal, "GetFileInformationByHandle failed on %ls", wuri);
|
||||
errno = GetLastError();
|
||||
CloseHandle(h);
|
||||
return -1;
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
project(gui)
|
||||
find_package(Qt5 REQUIRED COMPONENTS Widgets)
|
||||
set(CMAKE_AUTOMOC TRUE)
|
||||
|
||||
set(CMAKE_AUTOUIC TRUE)
|
||||
set(CMAKE_AUTORCC TRUE)
|
||||
add_subdirectory(updater)
|
||||
|
||||
#TODO Move resources files
|
||||
qt_add_resources(MIRALL_RC_SRC ../../client.qrc)
|
||||
set(MIRALL_RC_SRC ../../client.qrc)
|
||||
if (EXISTS "${OEM_THEME_DIR}/theme.qrc")
|
||||
qt_add_resources(MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
list(APPEND MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
set(theme_dir ${OEM_THEME_DIR}/theme)
|
||||
else()
|
||||
qt_add_resources(MIRALL_RC_SRC ../../theme.qrc)
|
||||
list(APPEND MIRALL_RC_SRC ../../theme.qrc)
|
||||
set(theme_dir ${CMAKE_SOURCE_DIR}/theme)
|
||||
endif()
|
||||
|
||||
set(client_UI
|
||||
set(client_UI_SRCS
|
||||
accountsettings.ui
|
||||
folderwizardsourcepage.ui
|
||||
folderwizardtargetpage.ui
|
||||
|
@ -41,8 +43,6 @@ set(client_UI
|
|||
wizard/owncloudwizardresultpage.ui
|
||||
)
|
||||
|
||||
qt_wrap_ui(client_UI_SRCS ${client_UI})
|
||||
|
||||
set(client_SRCS
|
||||
accountmanager.cpp
|
||||
accountsettings.cpp
|
||||
|
@ -173,16 +173,7 @@ else()
|
|||
list(APPEND 3rdparty_SRC ../3rdparty/qtlockedfile/qtlockedfile_win.cpp )
|
||||
endif()
|
||||
|
||||
set(3rdparty_INC
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
|
||||
)
|
||||
|
||||
include_directories(${3rdparty_INC})
|
||||
|
||||
IF( NOT WIN32 AND NOT APPLE AND WITH_DBUS AND LIBCLOUDPROVIDERS_FOUND)
|
||||
IF( NOT WIN32 AND NOT APPLE AND LIBCLOUDPROVIDERS_FOUND)
|
||||
message("Building with libcloudproviderssupport")
|
||||
add_definitions(-DWITH_LIBCLOUDPROVIDERS)
|
||||
set(client_SRCS ${client_SRCS} cloudproviders/cloudprovidermanager.cpp)
|
||||
|
@ -193,20 +184,10 @@ IF( NOT WIN32 AND NOT APPLE AND WITH_DBUS AND LIBCLOUDPROVIDERS_FOUND)
|
|||
include_directories(${LIBCLOUDPROVIDERS_INCLUDE_DIR})
|
||||
ENDIF()
|
||||
|
||||
|
||||
|
||||
|
||||
# csync is required.
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/csync
|
||||
${CMAKE_BINARY_DIR}/src/csync
|
||||
)
|
||||
include_directories(../libsync ${CMAKE_CURRENT_BINARY_DIR}/../libsync)
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
|
||||
qt_add_translation(client_I18N ${TRANSLATIONS})
|
||||
find_package(Qt5LinguistTools)
|
||||
if(Qt5LinguistTools_FOUND)
|
||||
qt5_add_translation(client_I18N ${TRANSLATIONS})
|
||||
endif()
|
||||
|
||||
IF( WIN32 )
|
||||
configure_file(
|
||||
|
@ -237,8 +218,11 @@ if(QTKEYCHAIN_FOUND OR QT5KEYCHAIN_FOUND)
|
|||
endif()
|
||||
|
||||
# add executable icon on windows and osx
|
||||
include( AddAppIconMacro )
|
||||
set(ownCloud_old ${ownCloud})
|
||||
|
||||
# UPSTREAM our ECMAddAppIcon.cmake then require that version here
|
||||
# find_package(ECM 1.7.0 REQUIRED NO_MODULE)
|
||||
# list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
|
||||
include(ECMAddAppIcon)
|
||||
|
||||
# For historical reasons we can not use the application_shortname
|
||||
# for ownCloud but must rather set it manually.
|
||||
|
@ -246,16 +230,12 @@ if (NOT DEFINED APPLICATION_ICON_NAME)
|
|||
set(APPLICATION_ICON_NAME ${APPLICATION_SHORTNAME})
|
||||
endif()
|
||||
|
||||
kde4_add_app_icon( ownCloud "${theme_dir}/colored/${APPLICATION_ICON_NAME}-*.png")
|
||||
list(APPEND final_src ${ownCloud})
|
||||
set(ownCloud ${ownCloud_old})
|
||||
|
||||
if (WITH_DBUS)
|
||||
set(ADDITIONAL_APP_MODULES DBus)
|
||||
endif(WITH_DBUS)
|
||||
if (NOT NO_SHIBBOLETH)
|
||||
list(APPEND ADDITIONAL_APP_MODULES WebKitWidgets)
|
||||
file(GLOB_RECURSE OWNCLOUD_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-icon*")
|
||||
if(APPLE)
|
||||
file(GLOB_RECURSE OWNCLOUD_SIDEBAR_ICONS "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-sidebar*")
|
||||
MESSAGE(STATUS "OWNCLOUD_SIDEBAR_ICONS: ${APPLICATION_ICON_NAME}: ${OWNCLOUD_SIDEBAR_ICONS}")
|
||||
endif()
|
||||
ecm_add_app_icon(final_src ICONS "${OWNCLOUD_ICONS}" SIDEBAR_ICONS "${OWNCLOUD_SIDEBAR_ICONS}" OUTFILE_BASE "${APPLICATION_ICON_NAME}")
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE")
|
||||
|
@ -268,14 +248,13 @@ if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
|||
endif()
|
||||
|
||||
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
|
||||
if(NOT WIN32)
|
||||
file( GLOB _icons "${theme_dir}/colored/${APPLICATION_ICON_NAME}-icon-*.png" )
|
||||
foreach( _file ${_icons} )
|
||||
string( REPLACE "${theme_dir}/colored/${APPLICATION_ICON_NAME}-icon-" "" _res ${_file} )
|
||||
string( REPLACE ".png" "" _res ${_res} )
|
||||
install( FILES ${_file} RENAME ${APPLICATION_ICON_NAME}.png DESTINATION ${DATADIR}/icons/hicolor/${_res}x${_res}/apps )
|
||||
endforeach( _file )
|
||||
file(GLOB _icons "${theme_dir}/colored/*-${APPLICATION_ICON_NAME}-icon.png")
|
||||
foreach(_file ${_icons})
|
||||
string(REPLACE "${theme_dir}/colored/" "" _res ${_file})
|
||||
string(REPLACE "-${APPLICATION_ICON_NAME}-icon.png" "" _res ${_res})
|
||||
install(FILES ${_file} RENAME ${APPLICATION_ICON_NAME}.png DESTINATION ${DATADIR}/icons/hicolor/${_res}x${_res}/apps)
|
||||
endforeach(_file)
|
||||
endif(NOT WIN32)
|
||||
|
||||
install(FILES ${client_I18N} DESTINATION ${SHAREDIR}/${APPLICATION_EXECUTABLE}/i18n)
|
||||
|
@ -284,14 +263,12 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
|||
|
||||
# add_executable( ${APPLICATION_EXECUTABLE} main.cpp ${final_src})
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml Sql ${ADDITIONAL_APP_MODULES})
|
||||
else()
|
||||
# set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf.
|
||||
set(MACOSX_BUNDLE_ICON_FILE "ownCloud.icns")
|
||||
|
||||
# we must add MACOSX_BUNDLE only if building a bundle
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml Sql ${ADDITIONAL_APP_MODULES})
|
||||
|
||||
set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
|
||||
install(FILES ${client_I18N} DESTINATION ${QM_DIR})
|
||||
|
@ -308,18 +285,18 @@ else()
|
|||
install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
|
||||
endif()
|
||||
|
||||
add_library(updater STATIC ${updater_SRCS} ${updaterMoc})
|
||||
target_link_libraries(updater ${synclib_NAME})
|
||||
qt5_use_modules(updater Widgets Network Xml)
|
||||
add_library(updater STATIC ${updater_SRCS})
|
||||
target_link_libraries(updater ${synclib_NAME} Qt5::Widgets Qt5::Network Qt5::Xml)
|
||||
target_include_directories(updater PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
|
||||
)
|
||||
# Only relevant for Linux? On OS X it by default properly checks in the bundle directory next to the exe
|
||||
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
||||
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE};${CMAKE_INSTALL_RPATH}" )
|
||||
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE};${CMAKE_INSTALL_RPATH}" )
|
||||
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${QT_LIBRARIES} )
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::Widgets Qt5::Network Qt5::Xml)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${synclib_NAME} )
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} updater )
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${OS_SPECIFIC_LINK_LIBRARIES} )
|
||||
|
@ -338,9 +315,33 @@ IF( LIBCLOUDPROVIDERS_FOUND )
|
|||
ENDIF()
|
||||
|
||||
|
||||
target_include_directories(${APPLICATION_EXECUTABLE} PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/QProgressIndicator
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtlockedfile
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtmacgoodies/src
|
||||
${CMAKE_SOURCE_DIR}/src/3rdparty/qtsingleapplication
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
## handle DBUS for Fdo notifications
|
||||
if( UNIX AND NOT APPLE )
|
||||
find_package(Qt5 COMPONENTS DBus)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::DBus)
|
||||
target_compile_definitions(${APPLICATION_EXECUTABLE} PRIVATE "USE_FDO_NOTIFICATIONS")
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
find_package(Qt5 COMPONENTS MacExtras)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::MacExtras)
|
||||
endif()
|
||||
|
||||
if (NOT NO_SHIBBOLETH)
|
||||
find_package(Qt5 COMPONENTS WebKitWidgets)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::WebKitWidgets)
|
||||
endif()
|
||||
|
||||
if(WITH_CRASHREPORTER)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} crashreporter-handler)
|
||||
include_directories( "../3rdparty/libcrashreporter-qt/src/" )
|
||||
target_link_libraries(${APPLICATION_EXECUTABLE} crashreporter-handler)
|
||||
|
||||
if(UNIX AND NOT MAC)
|
||||
find_package(Threads REQUIRED)
|
||||
|
@ -348,6 +349,10 @@ if(WITH_CRASHREPORTER)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# application.cpp still uses QDesktopServices::storageLocation
|
||||
target_compile_definitions(${APPLICATION_EXECUTABLE} PRIVATE "QT_DISABLE_DEPRECATED_BEFORE=0")
|
||||
|
||||
|
||||
install(TARGETS ${APPLICATION_EXECUTABLE}
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
|
|
|
@ -151,6 +151,13 @@ void AccountState::signOutByUi()
|
|||
setState(SignedOut);
|
||||
}
|
||||
|
||||
void AccountState::freshConnectionAttempt()
|
||||
{
|
||||
if (isConnected())
|
||||
setState(Disconnected);
|
||||
checkConnectivity();
|
||||
}
|
||||
|
||||
void AccountState::signIn()
|
||||
{
|
||||
if (_state == SignedOut) {
|
||||
|
|
|
@ -105,6 +105,16 @@ public:
|
|||
* for the account and forgets the password. */
|
||||
void signOutByUi();
|
||||
|
||||
/** Tries to connect from scratch.
|
||||
*
|
||||
* Does nothing for signed out accounts.
|
||||
* Connected accounts will be disconnected and try anew.
|
||||
* Disconnected accounts will go to checkConnectivity().
|
||||
*
|
||||
* Useful for when network settings (proxy) change.
|
||||
*/
|
||||
void freshConnectionAttempt();
|
||||
|
||||
/// Move from SignedOut state to Disconnected (attempting to connect)
|
||||
void signIn();
|
||||
|
||||
|
|
|
@ -128,7 +128,15 @@ Application::Application(int &argc, char **argv)
|
|||
if (!QFileInfo(confDir).exists()) {
|
||||
// Migrate from version <= 2.4
|
||||
setApplicationName(_theme->appNameGUI());
|
||||
#ifndef QT_WARNING_DISABLE_DEPRECATED // Was added in Qt 5.9
|
||||
#define QT_WARNING_DISABLE_DEPRECATED QT_WARNING_DISABLE_GCC("-Wdeprecated-declarations")
|
||||
#endif
|
||||
QT_WARNING_PUSH
|
||||
QT_WARNING_DISABLE_DEPRECATED
|
||||
// We need to use the deprecated QDesktopServices::storageLocation because of its Qt4
|
||||
// behavior of adding "data" to the path
|
||||
QString oldDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||
QT_WARNING_POP
|
||||
setApplicationName(_theme->appName());
|
||||
if (QFileInfo(oldDir).isDir()) {
|
||||
qCInfo(lcApplication) << "Migrating old config from" << oldDir << "to" << confDir;
|
||||
|
|
|
@ -77,8 +77,6 @@ protected:
|
|||
void parseOptions(const QStringList &);
|
||||
void setupTranslations();
|
||||
void setupLogging();
|
||||
void enterNextLogFile();
|
||||
bool checkConfigExists(bool openSettings);
|
||||
|
||||
signals:
|
||||
void folderRemoved();
|
||||
|
|
|
@ -52,7 +52,6 @@ Folder::Folder(const FolderDefinition &definition,
|
|||
, _accountState(accountState)
|
||||
, _definition(definition)
|
||||
, _csyncUnavail(false)
|
||||
, _proxyDirty(true)
|
||||
, _lastSyncDuration(0)
|
||||
, _consecutiveFailingSyncs(0)
|
||||
, _consecutiveFollowUpSyncs(0)
|
||||
|
@ -116,7 +115,6 @@ Folder::~Folder()
|
|||
_engine.reset();
|
||||
}
|
||||
|
||||
|
||||
void Folder::checkLocalPath()
|
||||
{
|
||||
const QFileInfo fi(_definition.localPath);
|
||||
|
@ -600,22 +598,9 @@ bool Folder::reloadExcludes()
|
|||
return _engine->excludedFiles().reloadExcludeFiles();
|
||||
}
|
||||
|
||||
void Folder::setProxyDirty(bool value)
|
||||
{
|
||||
_proxyDirty = value;
|
||||
}
|
||||
|
||||
bool Folder::proxyDirty()
|
||||
{
|
||||
return _proxyDirty;
|
||||
}
|
||||
|
||||
void Folder::startSync(const QStringList &pathList)
|
||||
{
|
||||
Q_UNUSED(pathList)
|
||||
if (proxyDirty()) {
|
||||
setProxyDirty(false);
|
||||
}
|
||||
|
||||
if (isBusy()) {
|
||||
qCCritical(lcFolder) << "ERROR csync is still running and new sync requested.";
|
||||
|
|
|
@ -171,12 +171,6 @@ public:
|
|||
*/
|
||||
SyncResult syncResult() const;
|
||||
|
||||
/**
|
||||
* set the config file name.
|
||||
*/
|
||||
void setConfigFile(const QString &);
|
||||
QString configFile();
|
||||
|
||||
/**
|
||||
* This is called if the sync folder definition is removed. Do cleanups here.
|
||||
*/
|
||||
|
@ -276,9 +270,6 @@ public slots:
|
|||
*/
|
||||
void startSync(const QStringList &pathList = QStringList());
|
||||
|
||||
void setProxyDirty(bool value);
|
||||
bool proxyDirty();
|
||||
|
||||
int slotDiscardDownloadProgress();
|
||||
int downloadInfoCount();
|
||||
int slotWipeErrorBlacklist();
|
||||
|
@ -351,7 +342,6 @@ private:
|
|||
SyncResult _syncResult;
|
||||
QScopedPointer<SyncEngine> _engine;
|
||||
bool _csyncUnavail;
|
||||
bool _proxyDirty;
|
||||
QPointer<RequestEtagJob> _requestEtagJob;
|
||||
QString _lastEtag;
|
||||
QElapsedTimer _timeSinceLastSyncDone;
|
||||
|
|
|
@ -1085,12 +1085,10 @@ bool FolderMan::startFromScratch(const QString &localFolder)
|
|||
return true;
|
||||
}
|
||||
|
||||
void FolderMan::setDirtyProxy(bool value)
|
||||
void FolderMan::setDirtyProxy()
|
||||
{
|
||||
foreach (Folder *f, _folderMap.values()) {
|
||||
if (f) {
|
||||
f->setProxyDirty(value);
|
||||
|
||||
if (f->accountState() && f->accountState()->account()
|
||||
&& f->accountState()->account()->networkAccessManager()) {
|
||||
// Need to do this so we do not use the old determined system proxy
|
||||
|
|
|
@ -178,7 +178,7 @@ public:
|
|||
/** Queues all folders for syncing. */
|
||||
void scheduleAllFolders();
|
||||
|
||||
void setDirtyProxy(bool value = true);
|
||||
void setDirtyProxy();
|
||||
void setDirtyNetworkLimits();
|
||||
|
||||
/**
|
||||
|
|
|
@ -298,7 +298,7 @@ bool FolderStatusModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
// also check all the children
|
||||
for (int i = 0; i < info->_subs.count(); ++i) {
|
||||
if (info->_subs[i]._checked != Qt::Checked) {
|
||||
setData(index.child(i, 0), Qt::Checked, Qt::CheckStateRole);
|
||||
setData(this->index(i, 0, index), Qt::Checked, Qt::CheckStateRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +313,7 @@ bool FolderStatusModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
// Uncheck all the children
|
||||
for (int i = 0; i < info->_subs.count(); ++i) {
|
||||
if (info->_subs[i]._checked != Qt::Unchecked) {
|
||||
setData(index.child(i, 0), Qt::Unchecked, Qt::CheckStateRole);
|
||||
setData(this->index(i, 0, index), Qt::Unchecked, Qt::CheckStateRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -703,7 +703,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
|||
}
|
||||
|
||||
for (auto it = undecidedIndexes.begin(); it != undecidedIndexes.end(); ++it) {
|
||||
suggestExpand(idx.child(*it, 0));
|
||||
suggestExpand(index(*it, 0, idx));
|
||||
}
|
||||
|
||||
/* We need lambda function for the following code.
|
||||
|
|
|
@ -568,10 +568,12 @@ void FolderWizard::resizeEvent(QResizeEvent *event)
|
|||
QWizard::resizeEvent(event);
|
||||
|
||||
// workaround for QTBUG-22819: when the error label word wrap, the minimum height is not adjusted
|
||||
int hfw = currentPage()->heightForWidth(currentPage()->width());
|
||||
if (currentPage()->height() < hfw) {
|
||||
currentPage()->setMinimumSize(currentPage()->minimumSizeHint().width(), hfw);
|
||||
setTitleFormat(titleFormat()); // And another workaround for QTBUG-3396
|
||||
if (auto page = currentPage()) {
|
||||
int hfw = page->heightForWidth(page->width());
|
||||
if (page->height() < hfw) {
|
||||
page->setMinimumSize(page->minimumSizeHint().width(), hfw);
|
||||
setTitleFormat(titleFormat()); // And another workaround for QTBUG-3396
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,12 @@
|
|||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* If more issues are reported than this they will not show up
|
||||
* to avoid performance issues around sorting this many issues.
|
||||
*/
|
||||
static const int maxIssueCount = 50000;
|
||||
|
||||
IssuesWidget::IssuesWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, _ui(new Ui::IssuesWidget)
|
||||
|
@ -100,6 +106,14 @@ IssuesWidget::IssuesWidget(QWidget *parent)
|
|||
#if defined(Q_OS_MAC)
|
||||
_ui->_treeWidget->setMinimumWidth(400);
|
||||
#endif
|
||||
|
||||
_reenableSorting.setInterval(5000);
|
||||
connect(&_reenableSorting, &QTimer::timeout, this,
|
||||
[this]() { _ui->_treeWidget->setSortingEnabled(true); });
|
||||
|
||||
_ui->_tooManyIssuesWarning->hide();
|
||||
connect(this, &IssuesWidget::issueCountUpdated, this,
|
||||
[this](int count) { _ui->_tooManyIssuesWarning->setVisible(count >= maxIssueCount); });
|
||||
}
|
||||
|
||||
IssuesWidget::~IssuesWidget()
|
||||
|
@ -140,7 +154,7 @@ void IssuesWidget::cleanItems(const QString &folder)
|
|||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||
for (int cnt = itemCnt - 1; cnt >= 0; cnt--) {
|
||||
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
|
||||
QString itemFolder = item->data(2, Qt::UserRole).toString();
|
||||
QString itemFolder = ProtocolItem::folderName(item);
|
||||
if (itemFolder == folder) {
|
||||
delete item;
|
||||
}
|
||||
|
@ -157,11 +171,17 @@ void IssuesWidget::addItem(QTreeWidgetItem *item)
|
|||
if (!item)
|
||||
return;
|
||||
|
||||
int insertLoc = 0;
|
||||
int count = _ui->_treeWidget->topLevelItemCount();
|
||||
if (count >= maxIssueCount)
|
||||
return;
|
||||
|
||||
_ui->_treeWidget->setSortingEnabled(false);
|
||||
_reenableSorting.start();
|
||||
|
||||
// Insert item specific errors behind the others
|
||||
int insertLoc = 0;
|
||||
if (!item->text(1).isEmpty()) {
|
||||
for (int i = 0; i < _ui->_treeWidget->topLevelItemCount(); ++i) {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (_ui->_treeWidget->topLevelItem(i)->text(1).isEmpty()) {
|
||||
insertLoc = i + 1;
|
||||
} else {
|
||||
|
@ -177,11 +197,8 @@ void IssuesWidget::addItem(QTreeWidgetItem *item)
|
|||
|
||||
void IssuesWidget::slotOpenFile(QTreeWidgetItem *item, int)
|
||||
{
|
||||
QString folderName = item->data(2, Qt::UserRole).toString();
|
||||
QString fileName = item->text(1);
|
||||
|
||||
Folder *folder = FolderMan::instance()->folder(folderName);
|
||||
if (folder) {
|
||||
if (Folder *folder = ProtocolItem::folder(item)) {
|
||||
// folder->path() always comes back with trailing path
|
||||
QString fullPath = folder->path() + fileName;
|
||||
if (QFile(fullPath).exists()) {
|
||||
|
@ -200,7 +217,7 @@ void IssuesWidget::slotProgressInfo(const QString &folder, const ProgressInfo &p
|
|||
|
||||
void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
|
||||
{
|
||||
if (!item->hasErrorStatus())
|
||||
if (!item->showInIssuesTab())
|
||||
return;
|
||||
QTreeWidgetItem *line = ProtocolItem::create(folder, *item);
|
||||
if (!line)
|
||||
|
@ -268,13 +285,13 @@ bool IssuesWidget::shouldBeVisible(QTreeWidgetItem *item, AccountState *filterAc
|
|||
const QString &filterFolderAlias) const
|
||||
{
|
||||
bool visible = true;
|
||||
auto status = item->data(3, Qt::UserRole);
|
||||
auto status = ProtocolItem::status(item);
|
||||
visible &= (_ui->showIgnores->isChecked() || status != SyncFileItem::FileIgnored);
|
||||
visible &= (_ui->showWarnings->isChecked()
|
||||
|| (status != SyncFileItem::SoftError
|
||||
&& status != SyncFileItem::Restoration));
|
||||
|
||||
auto folderalias = item->data(2, Qt::UserRole).toString();
|
||||
auto folderalias = ProtocolItem::folderName(item);
|
||||
if (filterAccount) {
|
||||
auto folder = FolderMan::instance()->folder(folderalias);
|
||||
visible &= folder && folder->accountState() == filterAccount;
|
||||
|
@ -423,7 +440,7 @@ void IssuesWidget::addErrorWidget(QTreeWidgetItem *item, const QString &message,
|
|||
|
||||
auto button = new QPushButton("Retry all uploads", widget);
|
||||
button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
|
||||
auto folderAlias = item->data(2, Qt::UserRole).toString();
|
||||
auto folderAlias = ProtocolItem::folderName(item);
|
||||
connect(button, &QPushButton::clicked,
|
||||
this, [this, folderAlias]() { retryInsufficentRemoteStorageErrors(folderAlias); });
|
||||
layout->addWidget(button);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <QDialog>
|
||||
#include <QDateTime>
|
||||
#include <QLocale>
|
||||
#include <QTimer>
|
||||
|
||||
#include "progressdispatcher.h"
|
||||
#include "owncloudgui.h"
|
||||
|
@ -85,6 +86,9 @@ private:
|
|||
/// Wipes all insufficient remote storgage blacklist entries
|
||||
void retryInsufficentRemoteStorageErrors(const QString &folderAlias);
|
||||
|
||||
/// Each insert disables sorting, this timer reenables it
|
||||
QTimer _reenableSorting;
|
||||
|
||||
Ui::IssuesWidget *_ui;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -127,6 +127,13 @@
|
|||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_tooManyIssuesWarning">
|
||||
<property name="text">
|
||||
<string>There were too many issues. Not all will be visible here.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue