Merge pull request #135 from nextcloud/oc_up

Merge upstream changes
This commit is contained in:
Roeland Jago Douma 2018-01-25 20:31:35 +01:00 committed by GitHub
commit 040507fce6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
220 changed files with 5584 additions and 7260 deletions

View File

@ -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:

View File

@ -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()

View File

@ -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
View 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

View File

@ -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)

View File

@ -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
View 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
View 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/*

View 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()

View 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()

View File

@ -0,0 +1 @@
include(${CMAKE_CURRENT_LIST_DIR}/../modules/ECMFindModuleHelpers.cmake)

View 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"
)

View 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)

View File

@ -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)

View 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"

View File

@ -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

View File

@ -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)

View 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

View File

@ -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);
}
}

View File

@ -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

View File

@ -23,3 +23,6 @@ if( UNIX AND NOT APPLE )
add_subdirectory(libcloudproviders)
endif()
if(MSVC)
add_subdirectory(windows)
endif()

View File

@ -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.

View File

@ -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 };
}

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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}")

View File

@ -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")

View File

@ -0,0 +1,4 @@
add_subdirectory(OCContextMenu)
add_subdirectory(OCOverlays)
add_subdirectory(OCUtil)

View 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}
)

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View 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}
)

View 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}
)

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

@ -1 +1 @@
Subproject commit e8fffe61e7c94ce88e59b80579754c4a46da65ea
Subproject commit 7df66f72aac595295dffcf4dc8a536822008c51d

View File

@ -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")

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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)
*/

View File

@ -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

View File

@ -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 {
/**

View File

@ -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

View File

@ -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

View File

@ -23,7 +23,7 @@ namespace OCC {
SyncJournalFileRecord::SyncJournalFileRecord()
: _inode(0)
, _type(0)
, _type(ItemTypeSkip)
, _fileSize(0)
, _serverHasIgnoredFiles(false)
{

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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: */

View File

@ -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>

View File

@ -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();

View File

@ -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",

View File

@ -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
}

View File

@ -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 */

View File

@ -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));

View File

@ -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

View File

@ -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.
*

View File

@ -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)

View File

@ -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"

View File

@ -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;
}

View File

@ -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 */

View File

@ -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

View File

@ -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);
}

View File

@ -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);
/**
* }@

View File

@ -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);

View File

@ -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
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -151,6 +151,13 @@ void AccountState::signOutByUi()
setState(SignedOut);
}
void AccountState::freshConnectionAttempt()
{
if (isConnected())
setState(Disconnected);
checkConnectivity();
}
void AccountState::signIn()
{
if (_state == SignedOut) {

View File

@ -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();

View File

@ -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;

View File

@ -77,8 +77,6 @@ protected:
void parseOptions(const QStringList &);
void setupTranslations();
void setupLogging();
void enterNextLogFile();
bool checkConfigExists(bool openSettings);
signals:
void folderRemoved();

View File

@ -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.";

View File

@ -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;

View File

@ -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

View File

@ -178,7 +178,7 @@ public:
/** Queues all folders for syncing. */
void scheduleAllFolders();
void setDirtyProxy(bool value = true);
void setDirtyProxy();
void setDirtyNetworkLimits();
/**

View File

@ -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.

View File

@ -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
}
}
}

View File

@ -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);

View File

@ -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;
};
}

View File

@ -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