mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-04 20:34:17 +02:00
Compare commits
1 Commits
v2.5.0-oem
...
v2.5.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
107baf2947 |
@@ -5,15 +5,48 @@ project(client)
|
||||
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/THEME.cmake")
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
|
||||
set(OEM_THEME_DIR "" CACHE STRING "Define directory containing a custom theme")
|
||||
if ( EXISTS ${OEM_THEME_DIR}/OEM.cmake )
|
||||
include ( ${OEM_THEME_DIR}/OEM.cmake )
|
||||
else ()
|
||||
include ( ${CMAKE_SOURCE_DIR}/OWNCLOUD.cmake )
|
||||
endif()
|
||||
|
||||
set(synclib_NAME "${APPLICATION_EXECUTABLE}sync")
|
||||
set(csync_NAME "${APPLICATION_EXECUTABLE}_csync")
|
||||
# Default suffix if the theme doesn't define one
|
||||
if(NOT DEFINED APPLICATION_VIRTUALFILE_SUFFIX)
|
||||
set(APPLICATION_VIRTUALFILE_SUFFIX "${APPLICATION_SHORTNAME}_virtual" CACHE STRING "Virtual file suffix (not including the .)")
|
||||
endif()
|
||||
|
||||
# need this logic to not mess with re/uninstallations via macosx.pkgproj
|
||||
if(${APPLICATION_REV_DOMAIN} STREQUAL "com.owncloud.desktopclient")
|
||||
set(APPLICATION_REV_DOMAIN_INSTALLER "com.ownCloud.client")
|
||||
else()
|
||||
set(APPLICATION_REV_DOMAIN_INSTALLER ${APPLICATION_REV_DOMAIN})
|
||||
endif()
|
||||
|
||||
# For usage in XML files we preprocess
|
||||
string(REPLACE "&" "&" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME}")
|
||||
string(REPLACE "<" "<" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME_XML_ESCAPED}")
|
||||
string(REPLACE ">" ">" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME_XML_ESCAPED}")
|
||||
|
||||
if (NOT DEFINED LINUX_PACKAGE_SHORTNAME)
|
||||
set(LINUX_PACKAGE_SHORTNAME "${APPLICATION_SHORTNAME}")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED PACKAGE)
|
||||
set(PACKAGE "${LINUX_PACKAGE_SHORTNAME}-client")
|
||||
endif()
|
||||
|
||||
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
|
||||
|
||||
if(NOT CRASHREPORTER_EXECUTABLE)
|
||||
set(CRASHREPORTER_EXECUTABLE "${APPLICATION_EXECUTABLE}_crash_reporter")
|
||||
endif()
|
||||
|
||||
include(Warnings)
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/VERSION.cmake)
|
||||
# For config.h
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
# Allows includes based on src/ like #include "common/utility.h" or #include "csync/csync.h"
|
||||
|
||||
131
ChangeLog
131
ChangeLog
@@ -1,58 +1,29 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
version 2.5.0 (2018-09-xx)
|
||||
version 2.5.0 (2018-06-xx)
|
||||
* Local discovery: Speed up by skipping directories without changes reported by the file system watcher.
|
||||
* Experimental option to create virtual files (e.g. my_document.txt.owncloud) and download contents on demand ("placeholders")
|
||||
* Windows: Add sync folders to Explorer's navigation pane (#5295)
|
||||
* Config: Client configuration in roaming profile on Windows, XDG conform on Linux (#684, #2245)
|
||||
* Experimental option to upload conflict files (#4557)
|
||||
* Conflicts: Change conflict file naming scheme
|
||||
* Conflicts: Add user name to conflict file name (#6325)
|
||||
* Conflicts: Better comparison when connection broke (#6626)
|
||||
* Conflicts: Deal with file/folder conflicts (#6312)
|
||||
* Conflicts: Change tray icon for unresolved conflicts (#6277)
|
||||
* Conflicts: Add documentation link to conflicts listing (#6396)
|
||||
* Conflicts: Change tags to be more user friendly (#6365)
|
||||
* Share dialog: Allow opening it if the file's contents are still syncing (#4608)
|
||||
* Share dialog: Don't hide account settings when opening it (#6185)
|
||||
* Share dialog: Remove odd grey square on OSX (#5774)
|
||||
* Share dialog: Preserve the entered password when unrelated changes are done (#6512)
|
||||
* Share dialog: Fix Re-shares not not showing up (#6666)
|
||||
* Sharing: Add "copy public link" to menu (#6356)
|
||||
* Share link: Update permission wording (#6192)
|
||||
* Private links: improve legacy fileid derivation (#6745)
|
||||
* User shares: Show avatars
|
||||
* OAuth2: Remove the timeout (#6612)
|
||||
* ConnectionValidator: change the minimum server version to 7.0
|
||||
* ConnectionValidator: Warn when the server version is less than 9.1
|
||||
* Experimental option to create virtual files and download contents on demand ("placeholders")
|
||||
* Experimental option to upload conflict files (#4557)
|
||||
* Wizard: Remove the "Skip folder config" button and instead add a radio button (#3664)
|
||||
* Wizard: Fix for back button in OAuth2 (#6574)
|
||||
* Wizard: add a context menu to copy the OAuth2 link (enterprise
|
||||
* Wizard: Put errors into a scroll area (#6546)
|
||||
* Wizard: show a message when the URL is invalid
|
||||
* Wizard: pre-select the right radio button (#6685)
|
||||
* Selective Sync: Do not abort applying selective sync if one folder has an error (#6675)
|
||||
* Sharing: Add "copy public link" to menu (#6356)
|
||||
* Protocol: Introduce context menu with "open in browser" (#6121)
|
||||
* Protocol: Correct sorting by size (#6326)
|
||||
* Issues tab: Invalidate issues selectively (#6226)
|
||||
* Issues tab: Don't allow two issues for the same file/folder
|
||||
* Issues tab: addItem performance improvement
|
||||
* Activities: Remove the text that a server does not support activities when the account is removed (#6679)
|
||||
* Activities: Handle the fact that the username can contain a '@' (#6728)
|
||||
* Notifications: Lower hiding timeout
|
||||
* Notifications: Also have clickable link (#6236)
|
||||
* Shell integration: Add "Open in browser" entry in the explorer menu (#5903)
|
||||
* Sync journal: Fix crash when unmounting a drive while a sync is running (#6049)
|
||||
* Client certs: Improve error message (#6128)
|
||||
* User shares: Show avatars
|
||||
* Settings: Hide selective sync buttons while disconnected (#5809)
|
||||
* Settings: Show account page when account created
|
||||
* Settings: Move "About" to a dialog (#6075)
|
||||
* Settings: Force sync should wipe the blacklist (#6757)
|
||||
* Excludes: Optimize further the matching of exclude files using regular expression
|
||||
* Don't use Qt deprecated API now that we required Qt 5.6
|
||||
* Windows: Update Overlay Icon naming
|
||||
* Windows: Release handle/fd when file open fails (#6699)
|
||||
* SyncEngine: Recover when the PUT reply (or chunkin's MOVE) is lost (#5106)
|
||||
* Config: Look for exclude file in a relative path.
|
||||
* Config: Versionize settings
|
||||
* Settings: Fix rename migration issue on old macOS
|
||||
* Credentials: Re-try on Linux if daemon not running (#4274, #6522)
|
||||
* Windows: Fixed MSVC build and compiler bugs
|
||||
* Proxy: Hostname validation and reconnection on setting change (#6140)
|
||||
@@ -60,86 +31,60 @@ version 2.5.0 (2018-09-xx)
|
||||
* Exclude regex: Restore old matching on Windows (#6245)
|
||||
* Build system: Modernize the CMakeLists.txt files
|
||||
* Use standard png2ico
|
||||
* Sync: When detecting a local move, keep the local mtime (#6629)
|
||||
* Sync: Better error handling for local directory parsing (#6610)
|
||||
* Sync: Error if properties are missing (#6317)
|
||||
* Sync: Recover when the PUT reply (or chunkin's MOVE) is lost (#5106)
|
||||
* Sync: Do not abort a sync if the server closes the connection (#6516)
|
||||
* Sync: Increase the timeout for the last MOVE/PUT for huge files (#6527)
|
||||
* Sync: Fix renames making hierarchy inversion (#6694)
|
||||
* Sync: RemotePermissions: Fix empty vs null (#4608)
|
||||
* Sync: Fix the "direction" of the "all file delted" message when the server is reset (#6317)
|
||||
* Data-Fingerprint: Fix backup detection when fingerprint is empty
|
||||
* propagateuploadv1: Fixed an assert with ownCloud 5
|
||||
* Download: Use the <s:message> from the reply in the error message (#6459, #6459)
|
||||
* Sync: Deal with file/folder conflicts (#6312)
|
||||
* Protocol: Correct sorting by size (#6326)
|
||||
* SocketAPI: dynamic action menu
|
||||
* Hidden option to move remote-deleted files to trash (#6265)
|
||||
* Tray: Change icon for unresolved conflicts (#6277)
|
||||
* FolderStatusModel: Refresh folders on Problem sync (#6337)
|
||||
* SyncJournal: Clear etag filter before sync
|
||||
* SyncEngine: Use separate state for two unicode conversions
|
||||
* Conflicts: Add documentation link to conflicts listing (#6396)
|
||||
* owncloudcmd: Do not read the proxy settings from the gui's config file
|
||||
* Discovery: Error if properties are missing (#6317)
|
||||
* ProgressInfo: Add information for local vs remote discovery
|
||||
* Issues tab: Invalidate issues selectively (#6226)
|
||||
* SyncResult: Make sure the number of conflicts is correct (#6226)
|
||||
* IssuesWidget: Don't allow two issues for the same file/folder
|
||||
* IssuesWidget: addItem performance improvement
|
||||
* Remove the "CSync" wording from the error messages
|
||||
* Apply branding to crashreporter resources file
|
||||
* Dolphin plugin: fall back if $XDG_RUNTIME_DIR is empty
|
||||
* SslButton: Add HTTP/2 info (#3146)
|
||||
* SslButton: Improve speed (especially on macOS) (#6031)
|
||||
* propagateuploadv1: Fixed an assert with ownCloud 5
|
||||
* Folder: normalize the local path. (#4424)
|
||||
* Folder: Fix checking if the folder can be used as sync folder (#6654)
|
||||
* SslButton: Improve speed (especially on macOS) (#6031)
|
||||
* Blacklisting must prevent parent etag updates (#6411)
|
||||
* FolderStatusModel: fix potential assert
|
||||
* Nautilus integration: Not a ColumnProvider
|
||||
* Nautilus integration: Fix python3 compatibility (#6406, #6643)
|
||||
* Nautilus: Guard against None state (#6643)
|
||||
* Dolphin plugin: fall back if $XDG_RUNTIME_DIR is empty
|
||||
* Nautilus integration: Fix python3 compatibility (#6406)
|
||||
* Conflicts: Change tags to be more user friendly (#6365)
|
||||
* Notify if an explicitly excluded folder is created (#6222)
|
||||
* Theme: unify ownCloudTheme and Theme classes
|
||||
* Share link: Update permission wording (#6192)
|
||||
* SyncJournalDb::setSelectiveSyncList: Always use a transaction (#6431)
|
||||
* Folders: Use "Problem" icon for unresolved conflicts (#6277)
|
||||
* macOS: Unload the Finder extension on exit (#5382, #3819)
|
||||
* Logging: Go to new file on Problem/Abort too (#6442)
|
||||
* Logging: Compress log when switching files (#6442)
|
||||
* LogDir: Go to new file on Problem/Abort too (#6442)
|
||||
* LogDir: Compress log when switching files (#6442)
|
||||
* Logging: Add persistent auto-logdir option (#6442)
|
||||
* Logging: .owncloudsynclog: Allow 10 MB of size (#6420)
|
||||
* Logging: .owncloudsynclog: Persist X-Request-ID for correlation with server (#6420)
|
||||
* .owncloudsynclog: Allow 10 MB of size (#6420)
|
||||
* .owncloudsynclog: Persist X-Request-ID for correlation with server (#6420)
|
||||
* Notifications: Lower hiding timeout
|
||||
* Download: Use the <s:message> from the reply in the error message (#6459)
|
||||
* Notifications: Also have clickable link (#6236)
|
||||
* UI: High-DPI layout fixes
|
||||
* Network settings: Better warnings about bad configuration (#5885)
|
||||
* Share dialog: Allow opening it if the file's contents are still syncing (#4608)
|
||||
* Share dialog: Don't hide account settings when opening it (#6185)
|
||||
* Share dialog: Remove odd grey square on OSX (#5774)
|
||||
* Share dialog: Preserve the entered password when unrelated changes are done (#6512)
|
||||
* Folder watcher: Show a notification if it becomes unreliable (#6119)
|
||||
* Ignore editor: Preserve comments in the exclude list file
|
||||
* Updater: Support EXE->MSI upgrade (different code than 2.4)
|
||||
* Updater: Remove unused installers before copying new ones into the appdata dir (#6690)
|
||||
* ConnectionValidator: change the minimum server version to 7.0
|
||||
* ConnectionValidator: Warn when the server version is less than 10.0
|
||||
* Valgrind: Refactorings to avoid errors
|
||||
* Crash fixes (#6562 and more)
|
||||
* Windows: Fix missing company name in our DLLs
|
||||
* Windows: Appveyor/craft changes
|
||||
* Linux: More tray workarounds (#6545)
|
||||
* libocsync: Rename to ${APPLICATION_EXECUTABLE}_csync
|
||||
* Don't use Qt deprecated API now that we required Qt 5.6
|
||||
|
||||
version 2.4.3 (2018-08-13)
|
||||
* Windows: Don't ignore files with FILE_ATTRIBUTE_TEMPORARY (#6696, #6610)
|
||||
* OAuth2: Fix infinite loop when the refresh token is expired
|
||||
* Windows MSI: Fix crash in the auto updater
|
||||
* Nautilus: Guard against None state (#6643)
|
||||
|
||||
version 2.4.2 (2018-07-18)
|
||||
* Linux: Tray workarounds (#6545)
|
||||
* Fix nautilus/nemo shell issues (#6393, #6406)
|
||||
* Updater: Add update channel feature (#6259)
|
||||
* Updater: Support EXE->MSI upgrade
|
||||
* SyncJournal: Fixes for sync folders on removable media (#6049, #6049)
|
||||
* SslButton: Add HTTP/2 info (#3146)
|
||||
* Fix assert when using ownCloud server 5 (which you should not) (#6403)
|
||||
* Normalize local path (#4424)
|
||||
* Blacklisting must prevent parent etag updates (#6411)
|
||||
* macdeployqt: Adjust minimum version based on our Qt (#5932)
|
||||
* macOS: Unload the Finder extension on exit (#5382, #3819)
|
||||
* Upload: Adjust timeout for final job based on file size (#6527)
|
||||
* Sync: When detecting a local move, keep the local mtime (#6629)
|
||||
* Credentials: Retry fetching from the keychain in case the keychain is still starting (#4274, #6522)
|
||||
* OAuth2: Try to refresh the token even if the credentials weren't ready (#6522)
|
||||
* Propagation: Do not abort a sync if the server closes the connection (#6516)
|
||||
* Propagation: Increase the timeout for the last MOVE/PUT for huge files (#6527)
|
||||
* Update bundled sqlite version to 3.23.1
|
||||
* Auto Updater: Drop down menu to switch update channels
|
||||
|
||||
version 2.4.1 (2018-03-05)
|
||||
* Ignore files with file names that can't be encoded for the filesystem (#6287, #5676, #5719)
|
||||
|
||||
72
Jenkinsfile
vendored
Normal file
72
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
#!groovy
|
||||
|
||||
//
|
||||
// We now run the tests in Debug mode so that ASSERTs are triggered.
|
||||
// Ideally we should run the tests in both Debug and Release so we catch
|
||||
// all possible error combinations.
|
||||
// See also the top comment in syncenginetestutils.h
|
||||
//
|
||||
// We are building "Linux - GCC" with "make" and "Linux - Clang" with ninja,
|
||||
// the combinations are more or less arbitrarily chosen. We just want to
|
||||
// check that both compilers and both CMake generators work. It's
|
||||
// unlikely that a specific generator only breaks with a specific
|
||||
// compiler.
|
||||
|
||||
// Constructed from the DockerFile in admin/linux/DockerFile
|
||||
def linux = docker.image('dominikschmidt/docker-owncloud-client-linux:latest')
|
||||
// Constructed from the DockerFile in admin/win/docker/DockerFile
|
||||
def win32 = docker.image('dominikschmidt/docker-owncloud-client-win32-cross:latest')
|
||||
|
||||
node('CLIENT') {
|
||||
stage 'Checkout'
|
||||
checkout scm
|
||||
sh '''git submodule update --init'''
|
||||
|
||||
stage 'Linux - Pull Docker Image'
|
||||
linux.pull()
|
||||
|
||||
stage 'Linux - GCC'
|
||||
linux.inside {
|
||||
sh '''
|
||||
export HOME="$(pwd)/home"
|
||||
rm -rf build home
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 ..
|
||||
make -j4
|
||||
LC_ALL=C.UTF-8 ctest -V --output-on-failure
|
||||
'''
|
||||
}
|
||||
|
||||
stage 'Linux - Clang'
|
||||
linux.inside {
|
||||
sh '''
|
||||
export HOME="$(pwd)/home"
|
||||
rm -rf build home
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 ..
|
||||
ninja -j4
|
||||
LC_ALL=C.UTF-8 ctest -V --output-on-failure
|
||||
'''
|
||||
}
|
||||
|
||||
stage 'Win32 Cross - Pull Docker Image'
|
||||
win32.pull()
|
||||
|
||||
stage 'Win32 Cross'
|
||||
win32.inside {
|
||||
sh '''
|
||||
rm -rf build-win32
|
||||
mkdir build-win32
|
||||
cd build-win32
|
||||
../admin/win/download_runtimes.sh
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=../admin/win/Toolchain-mingw32-openSUSE.cmake -DWITH_CRASHREPORTER=ON
|
||||
make -j4
|
||||
make package
|
||||
ctest .
|
||||
'''
|
||||
}
|
||||
|
||||
// Stage 'macOS' TODO
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# ownCloud Desktop Client
|
||||
|
||||
[](https://drone.owncloud.com/owncloud/client) [](https://ci.appveyor.com/project/ownclouders/client/branch/master)
|
||||
[](https://jenkins.owncloud.org/job/owncloud-client/job/client/job/master/) [](https://ci.appveyor.com/project/ownclouders/client/branch/master)
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
38
THEME.cmake
38
THEME.cmake
@@ -1,38 +0,0 @@
|
||||
set(OEM_THEME_DIR "" CACHE STRING "Define directory containing a custom theme")
|
||||
if (EXISTS "${OEM_THEME_DIR}/OEM.cmake")
|
||||
include("${OEM_THEME_DIR}/OEM.cmake")
|
||||
else()
|
||||
include ("${CMAKE_CURRENT_LIST_DIR}/OWNCLOUD.cmake")
|
||||
endif()
|
||||
|
||||
# Default suffix if the theme doesn't define one
|
||||
if(NOT DEFINED APPLICATION_VIRTUALFILE_SUFFIX)
|
||||
set(APPLICATION_VIRTUALFILE_SUFFIX "${APPLICATION_SHORTNAME}_virtual" CACHE STRING "Virtual file suffix (not including the .)")
|
||||
endif()
|
||||
|
||||
# need this logic to not mess with re/uninstallations via macosx.pkgproj
|
||||
if(${APPLICATION_REV_DOMAIN} STREQUAL "com.owncloud.desktopclient")
|
||||
set(APPLICATION_REV_DOMAIN_INSTALLER "com.ownCloud.client")
|
||||
else()
|
||||
set(APPLICATION_REV_DOMAIN_INSTALLER ${APPLICATION_REV_DOMAIN})
|
||||
endif()
|
||||
|
||||
# For usage in XML files we preprocess
|
||||
string(REPLACE "&" "&" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME}")
|
||||
string(REPLACE "<" "<" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME_XML_ESCAPED}")
|
||||
string(REPLACE ">" ">" APPLICATION_NAME_XML_ESCAPED "${APPLICATION_NAME_XML_ESCAPED}")
|
||||
|
||||
if (NOT DEFINED LINUX_PACKAGE_SHORTNAME)
|
||||
set(LINUX_PACKAGE_SHORTNAME "${APPLICATION_SHORTNAME}")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED PACKAGE)
|
||||
set(PACKAGE "${LINUX_PACKAGE_SHORTNAME}-client")
|
||||
endif()
|
||||
|
||||
if(NOT CRASHREPORTER_EXECUTABLE)
|
||||
set(CRASHREPORTER_EXECUTABLE "${APPLICATION_EXECUTABLE}_crash_reporter")
|
||||
endif()
|
||||
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/VERSION.cmake")
|
||||
@@ -1,11 +1,11 @@
|
||||
set( MIRALL_VERSION_MAJOR 2 )
|
||||
set( MIRALL_VERSION_MINOR 5 )
|
||||
set( MIRALL_VERSION_PATCH 1 )
|
||||
set( MIRALL_VERSION_PATCH 0 )
|
||||
set( MIRALL_VERSION_YEAR 2018 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
|
||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
set( MIRALL_VERSION_SUFFIX "git") #e.g. beta1, beta2, rc1
|
||||
set( MIRALL_VERSION_SUFFIX "alpha1") #e.g. beta1, beta2, rc1
|
||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
|
||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
|
||||
@@ -24,9 +24,6 @@ import sys
|
||||
from glob import glob
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
bundle_dir = sys.argv[1]
|
||||
qmake_path = sys.argv[2]
|
||||
|
||||
def QueryQMake(attrib):
|
||||
return subprocess.check_output([qmake_path, '-query', attrib]).rstrip('\n')
|
||||
|
||||
@@ -35,9 +32,7 @@ FRAMEWORK_SEARCH_PATH=[
|
||||
os.path.join(os.environ['HOME'], 'Library/Frameworks')
|
||||
]
|
||||
|
||||
LIBRARY_SEARCH_PATH=[
|
||||
'.'
|
||||
]
|
||||
LIBRARY_SEARCH_PATH=['/usr/local/lib', '.']
|
||||
|
||||
QT_PLUGINS = [
|
||||
'sqldrivers/libqsqlite.dylib',
|
||||
@@ -49,12 +44,10 @@ QT_PLUGINS = [
|
||||
]
|
||||
|
||||
QT_PLUGINS_SEARCH_PATH=[
|
||||
# os.path.join(os.environ['QTDIR'], 'plugins'),
|
||||
'/usr/local/Cellar/qt/5.2.1/plugins',
|
||||
]
|
||||
|
||||
# Package libraries from these paths even if they are also a system library
|
||||
SYSTEM_LIBRARY_BLACKLIST = [
|
||||
QueryQMake('QT_INSTALL_LIBS')
|
||||
]
|
||||
|
||||
class Error(Exception):
|
||||
pass
|
||||
@@ -85,6 +78,8 @@ if len(sys.argv) < 3:
|
||||
def is_exe(fpath):
|
||||
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
|
||||
|
||||
bundle_dir = sys.argv[1]
|
||||
qmake_path = sys.argv[2]
|
||||
|
||||
bundle_name = os.path.basename(bundle_dir).split('.')[0]
|
||||
|
||||
@@ -203,7 +198,7 @@ def FixFramework(path):
|
||||
FixLibraryInstallPath(library, new_path)
|
||||
|
||||
def FixLibrary(path):
|
||||
if path in fixed_libraries or FindSystemLibrary(path) is not None:
|
||||
if path in fixed_libraries or FindSystemLibrary(os.path.basename(path)) is not None:
|
||||
return
|
||||
else:
|
||||
fixed_libraries.append(path)
|
||||
@@ -240,7 +235,6 @@ def FixBinary(path):
|
||||
FixLibraryInstallPath(library, path)
|
||||
|
||||
def CopyLibrary(path):
|
||||
print "CopyLibrary:", path
|
||||
new_path = os.path.join(binary_dir, os.path.basename(path))
|
||||
args = ['ditto', '--arch=x86_64', path, new_path]
|
||||
commands.append(args)
|
||||
@@ -249,7 +243,6 @@ def CopyLibrary(path):
|
||||
return new_path
|
||||
|
||||
def CopyPlugin(path, subdir):
|
||||
print "CopyPlugin:", path, subdir
|
||||
new_path = os.path.join(plugins_dir, subdir, os.path.basename(path))
|
||||
args = ['mkdir', '-p', os.path.dirname(new_path)]
|
||||
commands.append(args)
|
||||
@@ -260,8 +253,8 @@ def CopyPlugin(path, subdir):
|
||||
return new_path
|
||||
|
||||
def CopyFramework(source_dylib):
|
||||
print "CopyFramework:", source_dylib
|
||||
parts = source_dylib.split(os.sep)
|
||||
print "CopyFramework:", source_dylib
|
||||
for i, part in enumerate(parts):
|
||||
matchObj = re.match(r'(\w+\.framework)', part)
|
||||
if matchObj:
|
||||
@@ -308,12 +301,7 @@ def FixInstallPath(library_path, library, new_path):
|
||||
args = ['install_name_tool', '-change', library_path, new_path, library]
|
||||
commands.append(args)
|
||||
|
||||
def FindSystemLibrary(library_path):
|
||||
for item in SYSTEM_LIBRARY_BLACKLIST:
|
||||
if library_path.startswith(item):
|
||||
return None
|
||||
|
||||
library_name = os.path.basename(library_path)
|
||||
def FindSystemLibrary(library_name):
|
||||
for path in ['/lib', '/usr/lib']:
|
||||
full_path = os.path.join(path, library_name)
|
||||
if os.path.exists(full_path):
|
||||
@@ -321,7 +309,7 @@ def FindSystemLibrary(library_path):
|
||||
return None
|
||||
|
||||
def FixLibraryInstallPath(library_path, library):
|
||||
system_library = FindSystemLibrary(library_path)
|
||||
system_library = FindSystemLibrary(os.path.basename(library_path))
|
||||
if system_library is None:
|
||||
new_path = '@executable_path/../MacOS/%s' % os.path.basename(library_path)
|
||||
FixInstallPath(library_path, library, new_path)
|
||||
|
||||
14
appveyor.ini
14
appveyor.ini
@@ -9,18 +9,16 @@ Command=craft
|
||||
#Values need to be overwritten to create a chache
|
||||
UseCache = True
|
||||
CreateCache = False
|
||||
QtVersion = 5.11.1
|
||||
OpenSslVersion = 1.1.0i
|
||||
CacheVersion = Qt_${Variables:QtVersion}
|
||||
QtVersion = 5.10.1
|
||||
OpenSslVersion = 1.1.0h
|
||||
CacheVersion = Qt_${Variables:QtVersion}-1
|
||||
|
||||
# Settings applicable for all Crafts matrices
|
||||
# Settings are Category/key=value
|
||||
# Category is case sensitive
|
||||
|
||||
[GeneralSettings]
|
||||
Version/ConfigVersion = 6
|
||||
|
||||
Packager/Destination=${Variables:APPVEYOR_BUILD_FOLDER}/binaries
|
||||
General/EMERGE_PKGDSTDIR=${Variables:APPVEYOR_BUILD_FOLDER}/binaries
|
||||
Paths/python = C:\Python36
|
||||
Paths/python27 = C:\Python27
|
||||
Paths/downloaddir = ${Variables:Root}\downloads
|
||||
@@ -33,15 +31,13 @@ Packager/CreateCache = ${Variables:CreateCache}
|
||||
Packager/CacheVersion = ${Variables:CacheVersion}
|
||||
; Packager/RepositoryUrl = https://files.kde.org/craft/
|
||||
Packager/PackageType = PortablePackager
|
||||
Packager/Whitelist = *.pdb;*.sym;symbols
|
||||
Packager/RepositoryUrl = https://attic.owncloud.com/org/mirror/craft/
|
||||
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
|
||||
binary/mysql.ignored = True
|
||||
|
||||
libs/qt5.version = ${Variables:QtVersion}
|
||||
win32libs/openssl.version = ${Variables:OpenSslVersion}
|
||||
|
||||
@@ -3,7 +3,6 @@ version: '{build}-{branch}'
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- 2.5
|
||||
|
||||
clone_depth: 50
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#
|
||||
# ecm_add_app_icon(<sources_var>
|
||||
# ICONS <icon> [<icon> [...]]
|
||||
# [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.49
|
||||
# [OUTFILE_BASENAME <name>]) # Since 5.49
|
||||
# [SIDEBAR_ICONS <icon> [<icon> [...]] # Since 5.4x
|
||||
# [OUTFILE_BASE <name>]) # Since 5.4x
|
||||
# )
|
||||
#
|
||||
# The given icons, whose names must match the pattern::
|
||||
@@ -27,21 +27,20 @@
|
||||
#
|
||||
# ``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.49.
|
||||
# application is dragged into Finder's sidebar. Since 5.4x.
|
||||
#
|
||||
# ``OUTFILE_BASENAME`` will be used as the basename for the icon file. If
|
||||
# you specify it, the icon file will be called ``<OUTFILE_BASENAME>.icns`` on Mac OS X
|
||||
# and ``<OUTFILE_BASENAME>.ico`` on Windows. If you don't specify it, it defaults
|
||||
# to ``<sources_var>.<ext>``. Since 5.49.
|
||||
# ``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.
|
||||
# * One of the tools png2ico (See :find-module:`FindPng2Ico`) or
|
||||
# icotool (see :find-module:`FindIcoTool`) is required.
|
||||
# * Supported sizes: 16, 24, 32, 48, 64, 128, 256, 512 and 1024.
|
||||
# * 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.
|
||||
@@ -102,7 +101,7 @@ include(CMakeParseArguments)
|
||||
|
||||
function(ecm_add_app_icon appsources)
|
||||
set(options)
|
||||
set(oneValueArgs OUTFILE_BASENAME)
|
||||
set(oneValueArgs OUTFILE_BASE)
|
||||
set(multiValueArgs ICONS SIDEBAR_ICONS)
|
||||
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
@@ -141,7 +140,7 @@ function(ecm_add_app_icon appsources)
|
||||
|
||||
_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;32;64;128;256")
|
||||
_ecm_add_app_icon_categorize_icons("${ARG_SIDEBAR_ICONS}" "sidebar_icons" "16;18;32;36;64")
|
||||
endif()
|
||||
|
||||
set(mac_icons
|
||||
@@ -180,8 +179,8 @@ function(ecm_add_app_icon appsources)
|
||||
message(AUTHOR_WARNING "No icons suitable for use on Windows provided")
|
||||
endif()
|
||||
|
||||
if (ARG_OUTFILE_BASENAME)
|
||||
set (_outfilebasename "${ARG_OUTFILE_BASENAME}")
|
||||
if (ARG_OUTFILE_BASE)
|
||||
set (_outfilebasename "${ARG_OUTFILE_BASE}")
|
||||
else()
|
||||
set (_outfilebasename "${appsources}")
|
||||
endif()
|
||||
@@ -214,7 +213,7 @@ function(ecm_add_app_icon appsources)
|
||||
endfunction()
|
||||
|
||||
if (IcoTool_FOUND)
|
||||
list(APPEND icotool_args "-c" "-o" "${_outfilename}.ico")
|
||||
set(icotool_args "-c -o \"${_outfilename}.ico\"")
|
||||
|
||||
# According to https://stackoverflow.com/a/40851713/2886832
|
||||
# Windows always chooses the first icon above 255px, all other ones will be ignored
|
||||
@@ -298,9 +297,6 @@ function(ecm_add_app_icon appsources)
|
||||
list(APPEND iconset_icons
|
||||
"${_outfilename}.iconset/${type}_${sizename}.png")
|
||||
endmacro()
|
||||
|
||||
# List of supported sizes and filenames taken from:
|
||||
# https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html#//apple_ref/doc/uid/TP40012302-CH7-SW4
|
||||
foreach(size 16 32 128 256 512)
|
||||
math(EXPR double_size "2 * ${size}")
|
||||
foreach(file ${icons_at_${size}px})
|
||||
@@ -311,25 +307,14 @@ function(ecm_add_app_icon appsources)
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
# List of supported sizes and filenames taken from:
|
||||
# https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/Finder.html#//apple_ref/doc/uid/TP40014214-CH15-SW15
|
||||
foreach(file ${sidebar_icons_at_16px})
|
||||
copy_icon("${file}" "16x16" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_32px})
|
||||
copy_icon("${file}" "16x16@2x" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_32px})
|
||||
copy_icon("${file}" "18x18" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_64px})
|
||||
copy_icon("${file}" "18x18@2x" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_128px})
|
||||
copy_icon("${file}" "32x32" "sidebar")
|
||||
endforeach()
|
||||
foreach(file ${sidebar_icons_at_256px})
|
||||
copy_icon("${file}" "32x32@2x" "sidebar")
|
||||
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
|
||||
|
||||
@@ -26,7 +26,6 @@ find_path(SQLITE3_INCLUDE_DIR
|
||||
sqlite3.h
|
||||
PATHS
|
||||
${_SQLITE3_INCLUDEDIR}
|
||||
${SQLITE3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
find_library(SQLITE3_LIBRARY
|
||||
@@ -34,7 +33,6 @@ find_library(SQLITE3_LIBRARY
|
||||
sqlite3 sqlite3-0
|
||||
PATHS
|
||||
${_SQLITE3_LIBDIR}
|
||||
${SQLITE3_LIBRARIES}
|
||||
)
|
||||
|
||||
set(SQLITE3_INCLUDE_DIRS
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#
|
||||
# Once done this will define
|
||||
# SPARKLE_FOUND - system has Sparkle
|
||||
# SPARKLE_LIBRARY - The framework needed to use Sparkle
|
||||
# SPARKLE_INCLUDE_DIR - the Sparkle include directory
|
||||
# SPARKLE_LIBRARY - The library needed to use Sparkle
|
||||
# Copyright (c) 2009, Vittorio Giovara <vittorio.giovara@gmail.com>
|
||||
#
|
||||
# Distributed under the OSI-approved BSD License (the "License");
|
||||
@@ -14,8 +15,9 @@
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_path(SPARKLE_INCLUDE_DIR Sparkle.h)
|
||||
find_library(SPARKLE_LIBRARY NAMES Sparkle)
|
||||
|
||||
find_package_handle_standard_args(Sparkle DEFAULT_MSG SPARKLE_LIBRARY)
|
||||
mark_as_advanced(SPARKLE_LIBRARY)
|
||||
find_package_handle_standard_args(Sparkle DEFAULT_MSG SPARKLE_INCLUDE_DIR SPARKLE_LIBRARY)
|
||||
mark_as_advanced(SPARKLE_INCLUDE_DIR SPARKLE_LIBRARY)
|
||||
|
||||
|
||||
@@ -389,7 +389,7 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${BUILD_PATH}\bin\${APPLICATION_EXECUTABLE}"
|
||||
File "${BUILD_PATH}\bin\${APPLICATION_CMD_EXECUTABLE}"
|
||||
File "${BUILD_PATH}\bin\lib${APPLICATION_SHORTNAME}sync.dll"
|
||||
File "${BUILD_PATH}\bin\lib${APPLICATION_SHORTNAME}_csync.dll"
|
||||
File "${BUILD_PATH}\bin\libocsync.dll"
|
||||
|
||||
File "${BUILD_PATH}\src\gui\client*.qm"
|
||||
; Make sure only to copy qt, not qt_help, etc
|
||||
|
||||
34
doc/faq.rst
34
doc/faq.rst
@@ -21,40 +21,6 @@ When a deeply nested directory is excluded from synchronization it will be
|
||||
listed with other ignored files and directories in the "Not synced" tab of
|
||||
the "Activity" pane.
|
||||
|
||||
I See a Warning Message for Unsupported Versions.
|
||||
-------------------------------------------------
|
||||
|
||||
Keeping software up to date is crucial for file integrity and security – if
|
||||
software is outdated, there can be unfixed bugs. That’s why you should always
|
||||
upgrade your software when there is a new version.
|
||||
|
||||
The ownCloud Desktop Client talks to a server, e.g. the ownCloud server – so
|
||||
you don’t only have to upgrade your client when there is a new version for it,
|
||||
also the server has to be kept up-to-date by your sysadmin.
|
||||
|
||||
Starting with version 2.5.0, the client will show a warning message if you
|
||||
connect to an outdated or unsupported server:
|
||||
|
||||
.. image:: https://owncloud.org/wp-content/uploads/2018/09/ownCloud-unsupported-version-warning-message.png
|
||||
|
||||
**Because earlier versions are not maintained anymore, only ownCloud 10.0.0 or
|
||||
higher is supported.** So if you encounter such a message, you should ask your
|
||||
administrator to upgrade ownCloud to a secure version.
|
||||
|
||||
An important feature of the ownCloud Client is checksumming – each time you
|
||||
download or upload a file, the client and the server both check if the file was
|
||||
corrupted during the sync. This way you can be sure that you don’t lose any
|
||||
files.
|
||||
|
||||
There are servers out there which don’t have checksumming implemented on their
|
||||
side, or which are not tested by ownCloud’s QA team. They can’t ensure file
|
||||
integrity, they have potential security issues, and we can’t guarantee that
|
||||
they are compatible with the ownCloud Desktop Client.
|
||||
|
||||
**We care about your data and want it to be safe.** That’s why you see this warning
|
||||
message, so you can evaluate your data security. Don’t worry – you can still
|
||||
use the client with an unsupported server, but do so at your own risk.
|
||||
|
||||
There Was A Warning About Changes In Synchronized Folders Not Being Tracked Reliably.
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -2,21 +2,30 @@
|
||||
Installing the Desktop Synchronization Client
|
||||
=============================================
|
||||
|
||||
You can download the latest version of the ownCloud Desktop Synchronization Client from the `ownCloud download page`_.
|
||||
There are clients for *Linux*, *macOS*, and *Microsoft Windows*.
|
||||
You can download the latest version of the ownCloud Desktop Synchronization
|
||||
Client from the `ownCloud download page`_.
|
||||
There are clients for Linux, Mac OS X, and Microsoft Windows.
|
||||
|
||||
Installation on Mac OS X and Windows is the same as for any software application: download the program and then double-click it to launch the installation, and then follow the installation wizard.
|
||||
After it is installed and configured the sync client will automatically keep itself updated; see :doc:`autoupdate` for more information.
|
||||
Installation on Mac OS X and Windows is the same as for any software
|
||||
application: download the program and then double-click it to launch the
|
||||
installation, and then follow the installation wizard. After it is installed and
|
||||
configured the sync client will automatically keep itself updated; see
|
||||
:doc:`autoupdate` for more information.
|
||||
|
||||
Linux users must follow the instructions on the download page to add the appropriate repository for their Linux distribution, install the signing key, and then use their package managers to install the desktop sync client.
|
||||
Linux users will also update their sync clients via package manager, and the client will display a notification when an update is available.
|
||||
Linux users must follow the instructions on the download page to add the
|
||||
appropriate repository for their Linux distribution, install the signing key,
|
||||
and then use their package managers to install the desktop sync client. Linux
|
||||
users will also update their sync clients via package manager, and the client
|
||||
will display a notification when an update is available.
|
||||
|
||||
Linux users must also have a password manager enabled, such as `GNOME Keyring`_ or `KWallet`_, so that the sync client can login automatically.
|
||||
Linux users must also have a password manager enabled, such as GNOME Keyring or
|
||||
KWallet, so that the sync client can login automatically.
|
||||
|
||||
You will also find links to source code archives and older versions on the download page.
|
||||
You will also find links to source code archives and older versions on the
|
||||
download page.
|
||||
|
||||
System Requirements
|
||||
-------------------
|
||||
----------------------------------
|
||||
|
||||
- Windows 7+
|
||||
- Mac OS X 10.7+ (**64-bit only**)
|
||||
@@ -29,176 +38,36 @@ System Requirements
|
||||
.. note::
|
||||
For Linux distributions, we support, if technically feasible, the latest 2 versions per platform and the previous Ubuntu `LTS`_.
|
||||
|
||||
Customizing the Windows installation
|
||||
------------------------------------
|
||||
|
||||
If you just want to install ownCloud Desktop Synchronization Client on your local system, you can simply launch the .msi file and configure it in the wizard that pops up.
|
||||
|
||||
Features
|
||||
^^^^^^^^
|
||||
|
||||
The MSI installer provides several features that can be installed or removed individually, which you can also control via command-line, if you are automating the installation, then run the following command:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi
|
||||
|
||||
The command will install the ownCloud Desktop Synchronization Client into the default location with the default features enabled.
|
||||
If you want to disable, e.g., desktop shortcut icons you can simply change the above command to the following:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi REMOVE=DesktopShortcut
|
||||
|
||||
See the following table for a list of available features:
|
||||
|
||||
+--------------------+--------------------+----------------------------------+-----------------------------+
|
||||
| Feature | Enabled by default | Description |Property to disable |
|
||||
+====================+====================+==================================+=============================+
|
||||
| Client | Yes, required | The actual client | |
|
||||
+--------------------+--------------------+----------------------------------+-----------------------------+
|
||||
| DesktopShortcut | Yes | Adds a shortcut to the desktop |``NO_DESKTOP_SHORTCUT`` |
|
||||
+--------------------+--------------------+----------------------------------+-----------------------------+
|
||||
| StartMenuShortcuts | Yes | Adds shortcuts to the start menu |``NO_START_MENU_SHORTCUTS``|
|
||||
+--------------------+--------------------+----------------------------------+-----------------------------+
|
||||
| ShellExtensions | Yes | Adds Explorer integration |``NO_SHELL_EXTENSIONS`` |
|
||||
+--------------------+--------------------+----------------------------------+-----------------------------+
|
||||
|
||||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
You can also choose to only install the client itself by using the following command:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi ADDDEFAULT=Client
|
||||
|
||||
If you for instance want to install everything but the ``DesktopShortcut`` and the ``ShellExtensions`` feature, you have two possibilities:
|
||||
|
||||
1. You explicitly name all the features you actually want to install (whitelist) where ``Client`` is always installed anyway:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi ADDDEFAULT=StartMenuShortcuts
|
||||
|
||||
2. You pass the ``NO_DESKTOP_SHORTCUT`` and ``NO_SHELL_EXTENSIONS`` properties:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi NO_DESKTOP_SHORTCUT="1" NO_SHELL_EXTENSIONS="1"
|
||||
|
||||
.. note:: The ownCloud .msi remembers these properties, so you don't need to specify them on upgrades.
|
||||
|
||||
.. note:: You cannot use these to change the installed features, if you want to do that, see the next section.
|
||||
|
||||
Changing Installed Features
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can change the installed features later by using ``REMOVE`` and ``ADDDEFAULT`` properties.
|
||||
|
||||
1. If you want to add the desktop shortcut later, run the following command:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi ADDDEFAULT="DesktopShortcut"
|
||||
|
||||
2. If you want to remove it, simply run the following command:
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi REMOVE="DesktopShortcut"
|
||||
|
||||
Windows keeps track of the installed features and using ``REMOVE`` or ``ADDDEFAULT`` will only affect the mentioned features.
|
||||
|
||||
Compare `REMOVE <https://msdn.microsoft.com/en-us/library/windows/desktop/aa371194(v=vs.85).aspx>`_ and `ADDDEFAULT <https://msdn.microsoft.com/en-us/library/windows/desktop/aa367518(v=vs.85).aspx>`_ on the Windows Installer Guide.
|
||||
|
||||
.. note:: You cannot specify `REMOVE` on initial installation as it will disable all features.
|
||||
|
||||
Installation Folder
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
You can adjust the installation folder by specifying the ``INSTALLDIR`` property like this
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi INSTALLDIR="C:\Program Files (x86)\Non Standard ownCloud Client Folder"
|
||||
|
||||
Be careful when using PowerShell instead of ``cmd.exe``, it can be tricky to get the whitespace escaping right there.
|
||||
Specifying the ``INSTALLDIR`` like this only works on first installation, you cannot simply re-invoke the .msi with a different path.
|
||||
If you still need to change it, uninstall it first and reinstall it with the new path.
|
||||
|
||||
Disabling Automatic Updates
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To disable automatic updates, you can pass the ``SKIPAUTOUPDATE`` property.
|
||||
|
||||
::
|
||||
|
||||
msiexec /passive /i ownCloud-x.y.z.msi SKIPAUTOUPDATE="1"
|
||||
|
||||
Launch After Installation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To launch the client automatically after installation, you can pass the ``LAUNCH`` property.
|
||||
|
||||
::
|
||||
|
||||
msiexec /i ownCloud-x.y.z.msi LAUNCH="1"
|
||||
|
||||
This option also removes the checkbox to let users decide if they want to launch the client for non passive/quiet mode.
|
||||
|
||||
.. note:: This option does not have any effect without GUI.
|
||||
|
||||
No Reboot After Installation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ownCloud Client schedules a reboot after installation to make sure the Explorer extension is correctly (un)loaded.
|
||||
If you're taking care of the reboot yourself, you can set the ``REBOOT`` property
|
||||
|
||||
::
|
||||
|
||||
msiexec /i ownCloud-x.y.z.msi REBOOT=ReallySuppress
|
||||
|
||||
This will make `msiexec` exit with error `ERROR_SUCCESS_REBOOT_REQUIRED` (3010).
|
||||
If your deployment tooling interprets this as an actual error and you want to avoid that, you may want to set the ``DO_NOT_SCHEDULE_REBOOT`` instead
|
||||
|
||||
::
|
||||
|
||||
msiexec /i ownCloud-x.y.z.msi DO_NOT_SCHEDULE_REBOOT="1"
|
||||
|
||||
Installation Wizard
|
||||
-------------------
|
||||
|
||||
The installation wizard takes you step-by-step through configuration options and account setup.
|
||||
First you need to enter the URL of your ownCloud server.
|
||||
The installation wizard takes you step-by-step through configuration options and
|
||||
account setup. First you need to enter the URL of your ownCloud server.
|
||||
|
||||
.. image:: images/client-1.png
|
||||
:alt: form for entering ownCloud server URL
|
||||
|
||||
|
||||
Enter your ownCloud login on the next screen.
|
||||
|
||||
.. image:: images/client-2.png
|
||||
:alt: form for entering your ownCloud login
|
||||
|
||||
On the *"Local Folder Option"* screen you may sync all of your files on the ownCloud server, or select individual folders.
|
||||
The default local sync folder is ``ownCloud``, in your home directory.
|
||||
You may change this as well.
|
||||
On the Local Folder Option screen you may sync
|
||||
all of your files on the ownCloud server, or select individual folders. The
|
||||
default local sync folder is ``ownCloud``, in your home directory. You may
|
||||
change this as well.
|
||||
|
||||
.. image:: images/client-3.png
|
||||
:alt: Select which remote folders to sync, and which local folder to store
|
||||
:alt: Select which remote folders to sync, and which local folder to store
|
||||
them in.
|
||||
|
||||
When you have completed selecting your sync folders, click the *"Connect"* button at the bottom right.
|
||||
The client will attempt to connect to your ownCloud server, and when it is successful you'll see two buttons:
|
||||
|
||||
- one to connect to your ownCloud Web GUI
|
||||
- one to open your local folder
|
||||
|
||||
It will also start synchronizing your files.
|
||||
|
||||
When you have completed selecting your sync folders, click the Connect button
|
||||
at the bottom right. The client will attempt to connect to your ownCloud
|
||||
server, and when it is successful you'll see two buttons: one to connect to
|
||||
your ownCloud Web GUI, and one to open your local folder. It will also start
|
||||
synchronizing your files.
|
||||
|
||||
.. Links
|
||||
|
||||
|
||||
.. _ownCloud download page: https://owncloud.com/download/#desktop-clients
|
||||
.. _LTS: https://wiki.ubuntu.com/LTS
|
||||
.. _GNOME Keyring: https://wiki.gnome.org/Projects/GnomeKeyring/
|
||||
.. _KWallet: https://utils.kde.org/projects/kwalletmanager/
|
||||
.. _LTS: https://wiki.ubuntu.com/LTS
|
||||
@@ -24,9 +24,7 @@ The other options are:
|
||||
``--logflush``
|
||||
Clears (flushes) the log file after each write action.
|
||||
|
||||
``--logdebug``
|
||||
Also output debug-level messages in the log (equivalent to setting the env var QT_LOGGING_RULES="qt.*=true;*.debug=true").
|
||||
)
|
||||
|
||||
``--confdir`` `<dirname>`
|
||||
Uses the specified configuration directory.
|
||||
|
||||
|
||||
|
||||
@@ -8,205 +8,199 @@ GenericName=Folder Sync
|
||||
Icon=@APPLICATION_EXECUTABLE@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||
X-GNOME-Autostart-Delay=3
|
||||
MimeType=application/vnd.@APPLICATION_EXECUTABLE@;
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
MimeType=application/x-@APPLICATION_EXECUTABLE@;
|
||||
|
||||
# Translations
|
||||
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
Icon[oc]=@APPLICATION_EXECUTABLE@
|
||||
Name[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
GenericName[oc]=Dorsièr de Sincronizacion
|
||||
Name[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
Icon[oc]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
|
||||
Icon[ar]=@APPLICATION_EXECUTABLE@
|
||||
Name[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
|
||||
GenericName[ar]=مزامنة المجلد
|
||||
Name[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
|
||||
Icon[ar]=@APPLICATION_EXECUTABLE@
|
||||
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
|
||||
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
|
||||
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
|
||||
GenericName[bg_BG]=Синхронизиране на папката
|
||||
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
|
||||
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
||||
Icon[ca]=@APPLICATION_EXECUTABLE@
|
||||
Name[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
||||
GenericName[ca]=Sincronització de carpetes
|
||||
Name[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
||||
Icon[ca]=@APPLICATION_EXECUTABLE@
|
||||
Comment[da]=@APPLICATION_NAME@ skrivebordsklient til synkronisering
|
||||
Icon[da]=@APPLICATION_EXECUTABLE@
|
||||
Name[da]=@APPLICATION_NAME@ skrivebordsklient til synk
|
||||
GenericName[da]=Mappesynkronisering
|
||||
Name[da]=@APPLICATION_NAME@ skrivebordsklient til synk
|
||||
Icon[da]=@APPLICATION_EXECUTABLE@
|
||||
Comment[de]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||
Icon[de]=@APPLICATION_EXECUTABLE@
|
||||
Name[de]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||
GenericName[de]=Ordner-Synchronisation
|
||||
Name[de]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||
Icon[de]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ja_JP]=@APPLICATION_NAME@ デスクトップ同期クライアント
|
||||
Icon[ja_JP]=@APPLICATION_EXECUTABLE@
|
||||
Name[ja_JP]=@APPLICATION_NAME@ デスクトップ同期クライアント
|
||||
GenericName[ja_JP]=フォルダー同期
|
||||
Name[ja_JP]=@APPLICATION_NAME@ デスクトップ同期クライアント
|
||||
Icon[ja_JP]=@APPLICATION_EXECUTABLE@
|
||||
Comment[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη
|
||||
Icon[el]=@APPLICATION_EXECUTABLE@
|
||||
Name[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη
|
||||
GenericName[el]=Συγχρονισμός φακέλου
|
||||
Name[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη
|
||||
Icon[el]=@APPLICATION_EXECUTABLE@
|
||||
Comment[en_GB]=@APPLICATION_NAME@ desktop synchronisation client
|
||||
Icon[en_GB]=@APPLICATION_EXECUTABLE@
|
||||
Name[en_GB]=@APPLICATION_NAME@ desktop sync client
|
||||
GenericName[en_GB]=Folder Sync
|
||||
Name[en_GB]=@APPLICATION_NAME@ desktop sync client
|
||||
Icon[en_GB]=@APPLICATION_EXECUTABLE@
|
||||
Comment[es]=@APPLICATION_NAME@ cliente de sincronización de escritorio
|
||||
Icon[es]=@APPLICATION_EXECUTABLE@
|
||||
Name[es]=@APPLICATION_NAME@ cliente de sincronización de escritorio
|
||||
GenericName[es]=Sincronización de carpeta
|
||||
Name[es]=@APPLICATION_NAME@ cliente de sincronización de escritorio
|
||||
Icon[es]=@APPLICATION_EXECUTABLE@
|
||||
Comment[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||
Icon[de_DE]=@APPLICATION_EXECUTABLE@
|
||||
Name[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||
GenericName[de_DE]=Ordner-Synchronisation
|
||||
Name[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||
Icon[de_DE]=@APPLICATION_EXECUTABLE@
|
||||
Comment[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
|
||||
Icon[eu]=@APPLICATION_EXECUTABLE@
|
||||
Name[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
|
||||
GenericName[eu]=Karpetaren sinkronizazioa
|
||||
Icon[fa]=@APPLICATION_EXECUTABLE@
|
||||
Name[fa]=@APPLICATION_EXECUTABLE@ نسخهی همسان سازی مشتری
|
||||
Name[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
|
||||
Icon[eu]=@APPLICATION_EXECUTABLE@
|
||||
GenericName[fa]=همسان سازی پوشهها
|
||||
Name[fa]=@APPLICATION_EXECUTABLE@ نسخهی همسان سازی مشتری
|
||||
Icon[fa]=@APPLICATION_EXECUTABLE@
|
||||
Comment[fr]=Client de synchronisation @APPLICATION_NAME@
|
||||
Icon[fr]=@APPLICATION_EXECUTABLE@
|
||||
Name[fr]=Client de synchronisation @APPLICATION_NAME@
|
||||
GenericName[fr]=Synchronisation de dossier
|
||||
Name[fr]=Client de synchronisation @APPLICATION_NAME@
|
||||
Icon[fr]=@APPLICATION_EXECUTABLE@
|
||||
Comment[gl]=@APPLICATION_NAME@ cliente de sincronización para escritorio
|
||||
Icon[gl]=@APPLICATION_EXECUTABLE@
|
||||
Name[gl]=@APPLICATION_NAME@ cliente de sincronización para escritorio
|
||||
GenericName[gl]=Sincronizar Cartafol
|
||||
Name[gl]=@APPLICATION_NAME@ cliente de sincronización para escritorio
|
||||
Icon[gl]=@APPLICATION_EXECUTABLE@
|
||||
Comment[he]=@APPLICATION_NAME@ לקוח סנכון שולחן עבודה
|
||||
Icon[he]=@APPLICATION_EXECUTABLE@
|
||||
Name[he]=@APPLICATION_NAME@ לקוח סנכרון שולחן עבודה
|
||||
GenericName[he]=סנכון תיקייה
|
||||
Name[he]=@APPLICATION_NAME@ לקוח סנכרון שולחן עבודה
|
||||
Icon[he]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ia]=@APPLICATION_NAME@ cliente de synchronisation pro scriptorio
|
||||
Icon[ia]=@APPLICATION_EXECUTABLE@
|
||||
Name[ia]=@APPLICATION_NAME@ cliente de synchronisation pro scriptorio
|
||||
GenericName[ia]=Synchronisar Dossier
|
||||
Name[ia]=@APPLICATION_NAME@ cliente de synchronisation pro scriptorio
|
||||
Icon[ia]=@APPLICATION_EXECUTABLE@
|
||||
Comment[id]=Klien sinkronisasi desktop @APPLICATION_NAME@
|
||||
Icon[id]=@APPLICATION_EXECUTABLE@
|
||||
Name[id]=Klien sync desktop @APPLICATION_NAME@
|
||||
GenericName[id]=Folder Sync
|
||||
Name[id]=Klien sync desktop @APPLICATION_NAME@
|
||||
Icon[id]=@APPLICATION_EXECUTABLE@
|
||||
Comment[is]=@APPLICATION_NAME@ skjáborðsforrit samstillingar
|
||||
Icon[is]=@APPLICATION_EXECUTABLE@
|
||||
Name[is]=@APPLICATION_NAME@ skjáborðsforrit samstillingar
|
||||
GenericName[is]=Samstilling möppu
|
||||
Name[is]=@APPLICATION_NAME@ skjáborðsforrit samstillingar
|
||||
Icon[is]=@APPLICATION_EXECUTABLE@
|
||||
Comment[it]=Client di sincronizzazione del desktop di @APPLICATION_NAME@
|
||||
Icon[it]=@APPLICATION_EXECUTABLE@
|
||||
Name[it]=Client di sincronizzazione del desktop di @APPLICATION_NAME@
|
||||
GenericName[it]=Sincronizzazione cartella
|
||||
Name[it]=Client di sincronizzazione del desktop di @APPLICATION_NAME@
|
||||
Icon[it]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
Icon[ko]=@APPLICATION_EXECUTABLE@
|
||||
Name[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
GenericName[ko]=폴더 동기화
|
||||
Name[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
Icon[ko]=@APPLICATION_EXECUTABLE@
|
||||
Comment[lo]=@APPLICATION_NAME@ ການປະສານຂໍ້ມູນຄອມພິວເຕີລູກຂ່າຍ
|
||||
Icon[lo]=@APPLICATION_EXECUTABLE@
|
||||
Name[lo]=@APPLICATION_NAME@ ຊິງຄອມພິວເຕີລູກຂ່າຍ
|
||||
GenericName[lo]=ໂຟນເດີຊິງ
|
||||
Name[lo]=@APPLICATION_NAME@ ຊິງຄອມພິວເຕີລູກຂ່າຍ
|
||||
Icon[lo]=@APPLICATION_EXECUTABLE@
|
||||
Comment[mk]=@APPLICATION_NAME@ десктор клиент за синхронизација
|
||||
Icon[mk]=@APPLICATION_EXECUTABLE@
|
||||
Name[mk]=@APPLICATION_NAME@ десктор клиент за синхронизација
|
||||
GenericName[mk]=Папка за синхронизација
|
||||
Name[mk]=@APPLICATION_NAME@ десктор клиент за синхронизација
|
||||
Icon[mk]=@APPLICATION_EXECUTABLE@
|
||||
Comment[hu_HU]=@APPLICATION_NAME@ asztali szinkronizációs kliens
|
||||
Icon[hu_HU]=@APPLICATION_EXECUTABLE@
|
||||
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkronizációs kliens
|
||||
GenericName[hu_HU]=Mappaszinkronizálás
|
||||
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkronizációs kliens
|
||||
Icon[hu_HU]=@APPLICATION_EXECUTABLE@
|
||||
Comment[af_ZA]=@APPLICATION_NAME@ werkskermsinchroniseerkliënt
|
||||
Icon[af_ZA]=@APPLICATION_EXECUTABLE@
|
||||
Name[af_ZA]=@APPLICATION_NAME@ werkskermsinchroniseerkliënt
|
||||
GenericName[af_ZA]=Vouersinchronisering
|
||||
Name[af_ZA]=@APPLICATION_NAME@ werkskermsinchroniseerkliënt
|
||||
Icon[af_ZA]=@APPLICATION_EXECUTABLE@
|
||||
Comment[nl]=@APPLICATION_NAME@ desktop synchronisatie client
|
||||
Icon[nl]=@APPLICATION_EXECUTABLE@
|
||||
Name[nl]=@APPLICATION_NAME@ desktop sync client
|
||||
GenericName[nl]=Mappen sync
|
||||
Name[nl]=@APPLICATION_NAME@ desktop sync client
|
||||
Icon[nl]=@APPLICATION_EXECUTABLE@
|
||||
Comment[et_EE]=@APPLICATION_NAME@ sünkroonimise klient töölauale
|
||||
Icon[et_EE]=@APPLICATION_EXECUTABLE@
|
||||
Name[et_EE]=@APPLICATION_NAME@ sünkroonimise klient töölauale
|
||||
GenericName[et_EE]=Kaustade sünkroonimine
|
||||
Name[et_EE]=@APPLICATION_NAME@ sünkroonimise klient töölauale
|
||||
Icon[et_EE]=@APPLICATION_EXECUTABLE@
|
||||
Comment[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
|
||||
Icon[pl]=@APPLICATION_EXECUTABLE@
|
||||
Name[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
|
||||
GenericName[pl]=Folder Synchronizacji
|
||||
Name[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
|
||||
Icon[pl]=@APPLICATION_EXECUTABLE@
|
||||
Comment[pt_BR]=@APPLICATION_NAME@ cliente de sincronização do computador
|
||||
Icon[pt_BR]=@APPLICATION_EXECUTABLE@
|
||||
Name[pt_BR]=@APPLICATION_NAME@ cliente de sincronização de desktop
|
||||
GenericName[pt_BR]=Sincronização de Pasta
|
||||
Name[pt_BR]=@APPLICATION_NAME@ cliente de sincronização de desktop
|
||||
Icon[pt_BR]=@APPLICATION_EXECUTABLE@
|
||||
Comment[cs_CZ]=@APPLICATION_NAME@ počítačový synchronizační klient
|
||||
Icon[cs_CZ]=@APPLICATION_EXECUTABLE@
|
||||
Name[cs_CZ]=@APPLICATION_NAME@ počítačový synchronizační klient
|
||||
GenericName[cs_CZ]=Synchronizace adresáře
|
||||
Name[cs_CZ]=@APPLICATION_NAME@ počítačový synchronizační klient
|
||||
Icon[cs_CZ]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ru]=Настольный клиент синхронизации @APPLICATION_NAME@
|
||||
Icon[ru]=@APPLICATION_EXECUTABLE@
|
||||
Name[ru]=Настольный клиент синхронизации @APPLICATION_NAME@
|
||||
GenericName[ru]=Синхронизация каталогов
|
||||
Name[ru]=Настольный клиент синхронизации @APPLICATION_NAME@
|
||||
Icon[ru]=@APPLICATION_EXECUTABLE@
|
||||
Comment[sl]=@APPLICATION_NAME@ ‒ Program za usklajevanje datotek z namizjem
|
||||
Icon[sl]=@APPLICATION_EXECUTABLE@
|
||||
Name[sl]=@APPLICATION_NAME@ ‒ Program za usklajevanje datotek z namizjem
|
||||
GenericName[sl]=Usklajevanje map
|
||||
Name[sl]=@APPLICATION_NAME@ ‒ Program za usklajevanje datotek z namizjem
|
||||
Icon[sl]=@APPLICATION_EXECUTABLE@
|
||||
Comment[sq]=Klient njëkohësimesh @APPLICATION_NAME@ për desktop
|
||||
Icon[sq]=@APPLICATION_EXECUTABLE@
|
||||
Name[sq]=Klient njëkohësimesh @APPLICATION_NAME@ për desktop
|
||||
GenericName[sq]=Njëkohësim Dosjesh
|
||||
Name[sq]=Klient njëkohësimesh @APPLICATION_NAME@ për desktop
|
||||
Icon[sq]=@APPLICATION_EXECUTABLE@
|
||||
Comment[fi_FI]=@APPLICATION_NAME@ työpöytäsynkronointisovellus
|
||||
Icon[fi_FI]=@APPLICATION_EXECUTABLE@
|
||||
Name[fi_FI]=@APPLICATION_NAME@ työpöytäsynkronointisovellus
|
||||
GenericName[fi_FI]=Kansion synkronointi
|
||||
Name[fi_FI]=@APPLICATION_NAME@ työpöytäsynkronointisovellus
|
||||
Icon[fi_FI]=@APPLICATION_EXECUTABLE@
|
||||
Comment[sv]=@APPLICATION_NAME@ desktop synkroniseringsklient
|
||||
Icon[sv]=@APPLICATION_EXECUTABLE@
|
||||
Name[sv]=@APPLICATION_NAME@ desktop synk-klient
|
||||
GenericName[sv]=Mappsynk
|
||||
Name[sv]=@APPLICATION_NAME@ desktop synk-klient
|
||||
Icon[sv]=@APPLICATION_EXECUTABLE@
|
||||
Comment[tr]=@APPLICATION_NAME@ masaüstü eşitleme istemcisi
|
||||
Icon[tr]=@APPLICATION_EXECUTABLE@
|
||||
Name[tr]=@APPLICATION_NAME@ masaüstü eşitleme istemcisi
|
||||
GenericName[tr]=Dosya Eşitleme
|
||||
Name[tr]=@APPLICATION_NAME@ masaüstü eşitleme istemcisi
|
||||
Icon[tr]=@APPLICATION_EXECUTABLE@
|
||||
Comment[uk]=Настільний клієнт синхронізації @APPLICATION_NAME@
|
||||
Icon[uk]=@APPLICATION_EXECUTABLE@
|
||||
Name[uk]=Настільний клієнт синхронізації @APPLICATION_NAME@
|
||||
GenericName[uk]=Синхронізація теки
|
||||
Name[uk]=Настільний клієнт синхронізації @APPLICATION_NAME@
|
||||
Icon[uk]=@APPLICATION_EXECUTABLE@
|
||||
Comment[ro]=@APPLICATION_NAME@ client de sincronizare pe desktop
|
||||
Icon[ro]=@APPLICATION_EXECUTABLE@
|
||||
Name[ro]=@APPLICATION_NAME@ client de sincronizare pe desktop
|
||||
GenericName[ro]=Sincronizare director
|
||||
Name[ro]=@APPLICATION_NAME@ client de sincronizare pe desktop
|
||||
Icon[ro]=@APPLICATION_EXECUTABLE@
|
||||
Comment[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端
|
||||
Icon[zh_CN]=@APPLICATION_EXECUTABLE@
|
||||
Name[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端
|
||||
GenericName[zh_CN]=文件夹同步
|
||||
Name[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端
|
||||
Icon[zh_CN]=@APPLICATION_EXECUTABLE@
|
||||
Comment[zh_HK]=桌面版同步客户端
|
||||
Comment[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||
Icon[zh_TW]=@APPLICATION_EXECUTABLE@
|
||||
Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||
GenericName[zh_TW]=資料夾同步
|
||||
Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||
Icon[zh_TW]=@APPLICATION_EXECUTABLE@
|
||||
Comment[es_AR]=Cliente de sincronización para escritorio @APPLICATION_NAME@
|
||||
Icon[es_AR]=@APPLICATION_EXECUTABLE@
|
||||
Name[es_AR]=Cliente de sincronización para escritorio @APPLICATION_NAME@
|
||||
GenericName[es_AR]=Sincronización de directorio
|
||||
Name[es_AR]=Cliente de sincronización para escritorio @APPLICATION_NAME@
|
||||
Icon[es_AR]=@APPLICATION_EXECUTABLE@
|
||||
Comment[lt_LT]=@APPLICATION_NAME@ darbalaukio sinchronizavimo programa
|
||||
Icon[lt_LT]=@APPLICATION_EXECUTABLE@
|
||||
Name[lt_LT]=@APPLICATION_NAME@ darbalaukio programa
|
||||
GenericName[lt_LT]=Katalogo sinchnorizacija
|
||||
Name[lt_LT]=@APPLICATION_NAME@ darbalaukio programa
|
||||
Icon[lt_LT]=@APPLICATION_EXECUTABLE@
|
||||
Comment[th_TH]=@APPLICATION_NAME@ ประสานข้อมูลด้วยโปรแกรมบนเดสก์ท็อป
|
||||
Icon[th_TH]=@APPLICATION_EXECUTABLE@
|
||||
Name[th_TH]= @APPLICATION_NAME@ ประสานข้อมูลด้วยโปรแกรมบนเดสก์ท็อป
|
||||
GenericName[th_TH]=ประสานข้อมูลโฟลเดอร์
|
||||
Name[th_TH]= @APPLICATION_NAME@ ประสานข้อมูลด้วยโปรแกรมบนเดสก์ท็อป
|
||||
Icon[th_TH]=@APPLICATION_EXECUTABLE@
|
||||
Comment[es_MX]=Cliente de escritorio para sincronziación de @APPLICATION_NAME@
|
||||
Icon[es_MX]=@APPLICATION_EXECUTABLE@
|
||||
Name[es_MX]=Cliente de escritorio para sincronziación de @APPLICATION_NAME@
|
||||
GenericName[es_MX]=Sincronización de Carpetas
|
||||
Name[es_MX]=Cliente de escritorio para sincronziación de @APPLICATION_NAME@
|
||||
Icon[es_MX]=@APPLICATION_EXECUTABLE@
|
||||
Comment[nb_NO]=@APPLICATION_NAME@ skrivebordssynkroniseringsklient
|
||||
Icon[nb_NO]=@APPLICATION_EXECUTABLE@
|
||||
Name[nb_NO]=@APPLICATION_NAME@ skrivebordssynkroniseringsklient
|
||||
GenericName[nb_NO]=Mappesynkronisering
|
||||
Name[nb_NO]=@APPLICATION_NAME@ skrivebordssynkroniseringsklient
|
||||
Icon[nb_NO]=@APPLICATION_EXECUTABLE@
|
||||
Comment[nn_NO]=@APPLICATION_NAME@ klient for å synkronisera frå skrivebord
|
||||
Icon[nn_NO]=@APPLICATION_EXECUTABLE@
|
||||
Name[nn_NO]=@APPLICATION_NAME@ klient for å synkronisera frå skrivebord
|
||||
GenericName[nn_NO]=Mappe synkronisering
|
||||
Name[nn_NO]=@APPLICATION_NAME@ klient for å synkronisera frå skrivebord
|
||||
Icon[nn_NO]=@APPLICATION_EXECUTABLE@
|
||||
Comment[pt_PT]=@APPLICATION_NAME@ - Cliente de Sincronização para PC
|
||||
Icon[pt_PT]=@APPLICATION_EXECUTABLE@
|
||||
Name[pt_PT]=@APPLICATION_NAME@ - Cliente de Sincronização para PC
|
||||
GenericName[pt_PT]=Sincronizar Pasta
|
||||
Name[pt_PT]=@APPLICATION_NAME@ - Cliente de Sincronização para PC
|
||||
Icon[pt_PT]=@APPLICATION_EXECUTABLE@
|
||||
Icon[km]=@APPLICATION_EXECUTABLE@
|
||||
Comment[lb]=@APPLICATION_NAME@ Desktop Synchronisatioun Client
|
||||
Icon[lb]=@APPLICATION_EXECUTABLE@
|
||||
Name[lb]=@APPLICATION_NAME@ Desktop Sync Client
|
||||
GenericName[lb]=Dossier Dync
|
||||
Name[lb]=@APPLICATION_NAME@ Desktop Sync Client
|
||||
Icon[lb]=@APPLICATION_EXECUTABLE@
|
||||
|
||||
@@ -3,6 +3,4 @@
|
||||
# this script replaces the line
|
||||
# appname = 'ownCloud'
|
||||
# with the correct branding name in the syncstate.py script
|
||||
# It also replaces the occurences in the class name so several
|
||||
# branding can be loaded (see #6524)
|
||||
sed -i.org -e "s/ownCloud/$1/g" syncstate.py
|
||||
sed -i.org -e 's/appname\s*=\s*'"'"'ownCloud'"'/appname = '$1'/" syncstate.py
|
||||
|
||||
@@ -28,8 +28,10 @@ import time
|
||||
|
||||
from gi.repository import GObject, Nautilus
|
||||
|
||||
# Note: setappname.sh will search and replace 'ownCloud' on this file to update this line and other
|
||||
# occurrences of the name
|
||||
# Please do not touch the following line.
|
||||
# The reason is that we use a script to adopt this file for branding
|
||||
# by replacing this line with the branding app name. If the following
|
||||
# line is changed, the script can not match the pattern and fails.
|
||||
appname = 'ownCloud'
|
||||
|
||||
print("Initializing "+appname+"-client-nautilus extension")
|
||||
@@ -176,7 +178,7 @@ class SocketConnect(GObject.GObject):
|
||||
socketConnect = SocketConnect()
|
||||
|
||||
|
||||
class MenuExtension_ownCloud(GObject.GObject, Nautilus.MenuProvider):
|
||||
class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
@@ -233,7 +235,7 @@ class MenuExtension_ownCloud(GObject.GObject, Nautilus.MenuProvider):
|
||||
def ask_for_menu_items(self, files):
|
||||
record_separator = '\x1e'
|
||||
filesstring = record_separator.join(files)
|
||||
socketConnect.sendCommand(u'GET_MENU_ITEMS:{}\n'.format(filesstring))
|
||||
socketConnect.sendCommand('GET_MENU_ITEMS:{}\n'.format(filesstring))
|
||||
|
||||
done = False
|
||||
start = time.time()
|
||||
@@ -293,8 +295,8 @@ class MenuExtension_ownCloud(GObject.GObject, Nautilus.MenuProvider):
|
||||
# and we definitely don't want to show them for IGNORED.
|
||||
shareable = False
|
||||
state = entry['state']
|
||||
state_ok = state and state.startswith('OK')
|
||||
state_sync = state and state.startswith('SYNC')
|
||||
state_ok = state.startswith('OK')
|
||||
state_sync = state.startswith('SYNC')
|
||||
if state_ok:
|
||||
shareable = True
|
||||
elif state_sync and isDir:
|
||||
@@ -344,7 +346,7 @@ class MenuExtension_ownCloud(GObject.GObject, Nautilus.MenuProvider):
|
||||
socketConnect.sendCommand(action + ":" + filename + "\n")
|
||||
|
||||
|
||||
class SyncStateExtension_ownCloud(GObject.GObject, Nautilus.InfoProvider):
|
||||
class SyncStateExtension(GObject.GObject, Nautilus.InfoProvider):
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
using namespace std;
|
||||
|
||||
#define PIPE_TIMEOUT 5*1000 //ms
|
||||
#define SOCK_BUFFER 4096
|
||||
|
||||
OCClientInterface::ContextMenuInfo OCClientInterface::FetchInfo(const std::wstring &files)
|
||||
{
|
||||
@@ -91,5 +92,9 @@ void OCClientInterface::SendRequest(const wchar_t *verb, const std::wstring &pat
|
||||
return;
|
||||
}
|
||||
|
||||
socket.SendMsg((verb + (L":" + path + L"\n")).data());
|
||||
wchar_t msg[SOCK_BUFFER] = { 0 };
|
||||
if (SUCCEEDED(StringCchPrintf(msg, SOCK_BUFFER, L"%s:%s\n", verb, path.c_str())))
|
||||
{
|
||||
socket.SendMsg(msg);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -5,7 +5,6 @@ add_library(OCUtil SHARED
|
||||
RemotePathChecker.cpp
|
||||
stdafx.cpp
|
||||
StringUtil.cpp
|
||||
OCUtil.rc
|
||||
)
|
||||
|
||||
target_include_directories(OCUtil
|
||||
|
||||
Binary file not shown.
@@ -169,11 +169,8 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StringUtil.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="OCUtil.rc" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,14 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by OCContextMenu.rc
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 101
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
||||
2
src/3rdparty/libcrashreporter-qt
vendored
2
src/3rdparty/libcrashreporter-qt
vendored
Submodule src/3rdparty/libcrashreporter-qt updated: 293bdfb92c...3f92d83acf
8865
src/3rdparty/sqlite3/sqlite3.c
vendored
8865
src/3rdparty/sqlite3/sqlite3.c
vendored
File diff suppressed because it is too large
Load Diff
275
src/3rdparty/sqlite3/sqlite3.h
vendored
275
src/3rdparty/sqlite3/sqlite3.h
vendored
@@ -123,9 +123,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.24.0"
|
||||
#define SQLITE_VERSION_NUMBER 3024000
|
||||
#define SQLITE_SOURCE_ID "2018-06-04 19:24:41 c7ee0833225bfd8c5ec2f9bf62b97c4e04d03bd9566366d5221ac8fb199a87ca"
|
||||
#define SQLITE_VERSION "3.23.1"
|
||||
#define SQLITE_VERSION_NUMBER 3023001
|
||||
#define SQLITE_SOURCE_ID "2018-04-10 17:39:29 4bb2294022060e61de7da5c227a69ccd846ba330e31626ebcd59a94efd148b3b"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@@ -504,7 +504,6 @@ SQLITE_API int sqlite3_exec(
|
||||
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
|
||||
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
|
||||
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
|
||||
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
|
||||
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
|
||||
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
|
||||
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
|
||||
@@ -512,7 +511,6 @@ SQLITE_API int sqlite3_exec(
|
||||
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
|
||||
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
|
||||
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
|
||||
#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
|
||||
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
|
||||
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
|
||||
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
|
||||
@@ -1932,22 +1930,6 @@ struct sqlite3_mem_methods {
|
||||
** I/O required to support statement rollback.
|
||||
** The default value for this setting is controlled by the
|
||||
** [SQLITE_STMTJRNL_SPILL] compile-time option.
|
||||
**
|
||||
** [[SQLITE_CONFIG_SORTERREF_SIZE]]
|
||||
** <dt>SQLITE_CONFIG_SORTERREF_SIZE
|
||||
** <dd>The SQLITE_CONFIG_SORTERREF_SIZE option accepts a single parameter
|
||||
** of type (int) - the new value of the sorter-reference size threshold.
|
||||
** Usually, when SQLite uses an external sort to order records according
|
||||
** to an ORDER BY clause, all fields required by the caller are present in the
|
||||
** sorted records. However, if SQLite determines based on the declared type
|
||||
** of a table column that its values are likely to be very large - larger
|
||||
** than the configured sorter-reference size threshold - then a reference
|
||||
** is stored in each sorted record and the required column values loaded
|
||||
** from the database as records are returned in sorted order. The default
|
||||
** value for this option is to never use this optimization. Specifying a
|
||||
** negative value for this option restores the default behaviour.
|
||||
** This option is only available if SQLite is compiled with the
|
||||
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
|
||||
@@ -1977,7 +1959,6 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
|
||||
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
|
||||
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
|
||||
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
@@ -2114,21 +2095,6 @@ struct sqlite3_mem_methods {
|
||||
** 0 or 1 to indicate whether output-for-triggers has been disabled - 0 if
|
||||
** it is not disabled, 1 if it is.
|
||||
** </dd>
|
||||
**
|
||||
** <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
|
||||
** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
|
||||
** [VACUUM] in order to reset a database back to an empty database
|
||||
** with no schema and no content. The following process works even for
|
||||
** a badly corrupted database file:
|
||||
** <ol>
|
||||
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 1, 0);
|
||||
** <li> [sqlite3_exec](db, "[VACUUM]", 0, 0, 0);
|
||||
** <li> sqlite3_db_config(db, SQLITE_DBCONFIG_RESET_DATABASE, 0, 0);
|
||||
** </ol>
|
||||
** Because resetting a database is destructive and irreversible, the
|
||||
** process requires the use of this obscure API and multiple steps to help
|
||||
** ensure that it does not happen by accident.
|
||||
** </dd>
|
||||
** </dl>
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
@@ -2140,8 +2106,7 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */
|
||||
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
|
||||
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */
|
||||
#define SQLITE_DBCONFIG_MAX 1008 /* Largest DBCONFIG */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||
@@ -5527,41 +5492,6 @@ SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory;
|
||||
*/
|
||||
SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Win32 Specific Interface
|
||||
**
|
||||
** These interfaces are available only on Windows. The
|
||||
** [sqlite3_win32_set_directory] interface is used to set the value associated
|
||||
** with the [sqlite3_temp_directory] or [sqlite3_data_directory] variable, to
|
||||
** zValue, depending on the value of the type parameter. The zValue parameter
|
||||
** should be NULL to cause the previous value to be freed via [sqlite3_free];
|
||||
** a non-NULL value will be copied into memory obtained from [sqlite3_malloc]
|
||||
** prior to being used. The [sqlite3_win32_set_directory] interface returns
|
||||
** [SQLITE_OK] to indicate success, [SQLITE_ERROR] if the type is unsupported,
|
||||
** or [SQLITE_NOMEM] if memory could not be allocated. The value of the
|
||||
** [sqlite3_data_directory] variable is intended to act as a replacement for
|
||||
** the current directory on the sub-platforms of Win32 where that concept is
|
||||
** not present, e.g. WinRT and UWP. The [sqlite3_win32_set_directory8] and
|
||||
** [sqlite3_win32_set_directory16] interfaces behave exactly the same as the
|
||||
** sqlite3_win32_set_directory interface except the string parameter must be
|
||||
** UTF-8 or UTF-16, respectively.
|
||||
*/
|
||||
SQLITE_API int sqlite3_win32_set_directory(
|
||||
unsigned long type, /* Identifier for directory being set or reset */
|
||||
void *zValue /* New value for directory being set or reset */
|
||||
);
|
||||
SQLITE_API int sqlite3_win32_set_directory8(unsigned long type, const char *zValue);
|
||||
SQLITE_API int sqlite3_win32_set_directory16(unsigned long type, const void *zValue);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Win32 Directory Types
|
||||
**
|
||||
** These macros are only available on Windows. They define the allowed values
|
||||
** for the type argument to the [sqlite3_win32_set_directory] interface.
|
||||
*/
|
||||
#define SQLITE_WIN32_DATA_DIRECTORY_TYPE 1
|
||||
#define SQLITE_WIN32_TEMP_DIRECTORY_TYPE 2
|
||||
|
||||
/*
|
||||
** CAPI3REF: Test For Auto-Commit Mode
|
||||
** KEYWORDS: {autocommit mode}
|
||||
@@ -6294,10 +6224,6 @@ struct sqlite3_index_info {
|
||||
|
||||
/*
|
||||
** CAPI3REF: Virtual Table Scan Flags
|
||||
**
|
||||
** Virtual table implementations are allowed to set the
|
||||
** [sqlite3_index_info].idxFlags field to some combination of
|
||||
** these bits.
|
||||
*/
|
||||
#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
|
||||
|
||||
@@ -7073,7 +6999,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_ALWAYS 13
|
||||
#define SQLITE_TESTCTRL_RESERVE 14
|
||||
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
|
||||
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
|
||||
#define SQLITE_TESTCTRL_ISKEYWORD 16
|
||||
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
|
||||
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
|
||||
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
|
||||
@@ -7087,189 +7013,6 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
||||
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26
|
||||
#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQL Keyword Checking
|
||||
**
|
||||
** These routines provide access to the set of SQL language keywords
|
||||
** recognized by SQLite. Applications can uses these routines to determine
|
||||
** whether or not a specific identifier needs to be escaped (for example,
|
||||
** by enclosing in double-quotes) so as not to confuse the parser.
|
||||
**
|
||||
** The sqlite3_keyword_count() interface returns the number of distinct
|
||||
** keywords understood by SQLite.
|
||||
**
|
||||
** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
|
||||
** makes *Z point to that keyword expressed as UTF8 and writes the number
|
||||
** of bytes in the keyword into *L. The string that *Z points to is not
|
||||
** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
|
||||
** SQLITE_OK if N is within bounds and SQLITE_ERROR if not. If either Z
|
||||
** or L are NULL or invalid pointers then calls to
|
||||
** sqlite3_keyword_name(N,Z,L) result in undefined behavior.
|
||||
**
|
||||
** The sqlite3_keyword_check(Z,L) interface checks to see whether or not
|
||||
** the L-byte UTF8 identifier that Z points to is a keyword, returning non-zero
|
||||
** if it is and zero if not.
|
||||
**
|
||||
** The parser used by SQLite is forgiving. It is often possible to use
|
||||
** a keyword as an identifier as long as such use does not result in a
|
||||
** parsing ambiguity. For example, the statement
|
||||
** "CREATE TABLE BEGIN(REPLACE,PRAGMA,END);" is accepted by SQLite, and
|
||||
** creates a new table named "BEGIN" with three columns named
|
||||
** "REPLACE", "PRAGMA", and "END". Nevertheless, best practice is to avoid
|
||||
** using keywords as identifiers. Common techniques used to avoid keyword
|
||||
** name collisions include:
|
||||
** <ul>
|
||||
** <li> Put all identifier names inside double-quotes. This is the official
|
||||
** SQL way to escape identifier names.
|
||||
** <li> Put identifier names inside [...]. This is not standard SQL,
|
||||
** but it is what SQL Server does and so lots of programmers use this
|
||||
** technique.
|
||||
** <li> Begin every identifier with the letter "Z" as no SQL keywords start
|
||||
** with "Z".
|
||||
** <li> Include a digit somewhere in every identifier name.
|
||||
** </ul>
|
||||
**
|
||||
** Note that the number of keywords understood by SQLite can depend on
|
||||
** compile-time options. For example, "VACUUM" is not a keyword if
|
||||
** SQLite is compiled with the [-DSQLITE_OMIT_VACUUM] option. Also,
|
||||
** new keywords may be added to future releases of SQLite.
|
||||
*/
|
||||
SQLITE_API int sqlite3_keyword_count(void);
|
||||
SQLITE_API int sqlite3_keyword_name(int,const char**,int*);
|
||||
SQLITE_API int sqlite3_keyword_check(const char*,int);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Dynamic String Object
|
||||
** KEYWORDS: {dynamic string}
|
||||
**
|
||||
** An instance of the sqlite3_str object contains a dynamically-sized
|
||||
** string under construction.
|
||||
**
|
||||
** The lifecycle of an sqlite3_str object is as follows:
|
||||
** <ol>
|
||||
** <li> ^The sqlite3_str object is created using [sqlite3_str_new()].
|
||||
** <li> ^Text is appended to the sqlite3_str object using various
|
||||
** methods, such as [sqlite3_str_appendf()].
|
||||
** <li> ^The sqlite3_str object is destroyed and the string it created
|
||||
** is returned using the [sqlite3_str_finish()] interface.
|
||||
** </ol>
|
||||
*/
|
||||
typedef struct sqlite3_str sqlite3_str;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Create A New Dynamic String Object
|
||||
** CONSTRUCTOR: sqlite3_str
|
||||
**
|
||||
** ^The [sqlite3_str_new(D)] interface allocates and initializes
|
||||
** a new [sqlite3_str] object. To avoid memory leaks, the object returned by
|
||||
** [sqlite3_str_new()] must be freed by a subsequent call to
|
||||
** [sqlite3_str_finish(X)].
|
||||
**
|
||||
** ^The [sqlite3_str_new(D)] interface always returns a pointer to a
|
||||
** valid [sqlite3_str] object, though in the event of an out-of-memory
|
||||
** error the returned object might be a special singleton that will
|
||||
** silently reject new text, always return SQLITE_NOMEM from
|
||||
** [sqlite3_str_errcode()], always return 0 for
|
||||
** [sqlite3_str_length()], and always return NULL from
|
||||
** [sqlite3_str_finish(X)]. It is always safe to use the value
|
||||
** returned by [sqlite3_str_new(D)] as the sqlite3_str parameter
|
||||
** to any of the other [sqlite3_str] methods.
|
||||
**
|
||||
** The D parameter to [sqlite3_str_new(D)] may be NULL. If the
|
||||
** D parameter in [sqlite3_str_new(D)] is not NULL, then the maximum
|
||||
** length of the string contained in the [sqlite3_str] object will be
|
||||
** the value set for [sqlite3_limit](D,[SQLITE_LIMIT_LENGTH]) instead
|
||||
** of [SQLITE_MAX_LENGTH].
|
||||
*/
|
||||
SQLITE_API sqlite3_str *sqlite3_str_new(sqlite3*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Finalize A Dynamic String
|
||||
** DESTRUCTOR: sqlite3_str
|
||||
**
|
||||
** ^The [sqlite3_str_finish(X)] interface destroys the sqlite3_str object X
|
||||
** and returns a pointer to a memory buffer obtained from [sqlite3_malloc64()]
|
||||
** that contains the constructed string. The calling application should
|
||||
** pass the returned value to [sqlite3_free()] to avoid a memory leak.
|
||||
** ^The [sqlite3_str_finish(X)] interface may return a NULL pointer if any
|
||||
** errors were encountered during construction of the string. ^The
|
||||
** [sqlite3_str_finish(X)] interface will also return a NULL pointer if the
|
||||
** string in [sqlite3_str] object X is zero bytes long.
|
||||
*/
|
||||
SQLITE_API char *sqlite3_str_finish(sqlite3_str*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Add Content To A Dynamic String
|
||||
** METHOD: sqlite3_str
|
||||
**
|
||||
** These interfaces add content to an sqlite3_str object previously obtained
|
||||
** from [sqlite3_str_new()].
|
||||
**
|
||||
** ^The [sqlite3_str_appendf(X,F,...)] and
|
||||
** [sqlite3_str_vappendf(X,F,V)] interfaces uses the [built-in printf]
|
||||
** functionality of SQLite to append formatted text onto the end of
|
||||
** [sqlite3_str] object X.
|
||||
**
|
||||
** ^The [sqlite3_str_append(X,S,N)] method appends exactly N bytes from string S
|
||||
** onto the end of the [sqlite3_str] object X. N must be non-negative.
|
||||
** S must contain at least N non-zero bytes of content. To append a
|
||||
** zero-terminated string in its entirety, use the [sqlite3_str_appendall()]
|
||||
** method instead.
|
||||
**
|
||||
** ^The [sqlite3_str_appendall(X,S)] method appends the complete content of
|
||||
** zero-terminated string S onto the end of [sqlite3_str] object X.
|
||||
**
|
||||
** ^The [sqlite3_str_appendchar(X,N,C)] method appends N copies of the
|
||||
** single-byte character C onto the end of [sqlite3_str] object X.
|
||||
** ^This method can be used, for example, to add whitespace indentation.
|
||||
**
|
||||
** ^The [sqlite3_str_reset(X)] method resets the string under construction
|
||||
** inside [sqlite3_str] object X back to zero bytes in length.
|
||||
**
|
||||
** These methods do not return a result code. ^If an error occurs, that fact
|
||||
** is recorded in the [sqlite3_str] object and can be recovered by a
|
||||
** subsequent call to [sqlite3_str_errcode(X)].
|
||||
*/
|
||||
SQLITE_API void sqlite3_str_appendf(sqlite3_str*, const char *zFormat, ...);
|
||||
SQLITE_API void sqlite3_str_vappendf(sqlite3_str*, const char *zFormat, va_list);
|
||||
SQLITE_API void sqlite3_str_append(sqlite3_str*, const char *zIn, int N);
|
||||
SQLITE_API void sqlite3_str_appendall(sqlite3_str*, const char *zIn);
|
||||
SQLITE_API void sqlite3_str_appendchar(sqlite3_str*, int N, char C);
|
||||
SQLITE_API void sqlite3_str_reset(sqlite3_str*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Status Of A Dynamic String
|
||||
** METHOD: sqlite3_str
|
||||
**
|
||||
** These interfaces return the current status of an [sqlite3_str] object.
|
||||
**
|
||||
** ^If any prior errors have occurred while constructing the dynamic string
|
||||
** in sqlite3_str X, then the [sqlite3_str_errcode(X)] method will return
|
||||
** an appropriate error code. ^The [sqlite3_str_errcode(X)] method returns
|
||||
** [SQLITE_NOMEM] following any out-of-memory error, or
|
||||
** [SQLITE_TOOBIG] if the size of the dynamic string exceeds
|
||||
** [SQLITE_MAX_LENGTH], or [SQLITE_OK] if there have been no errors.
|
||||
**
|
||||
** ^The [sqlite3_str_length(X)] method returns the current length, in bytes,
|
||||
** of the dynamic string under construction in [sqlite3_str] object X.
|
||||
** ^The length returned by [sqlite3_str_length(X)] does not include the
|
||||
** zero-termination byte.
|
||||
**
|
||||
** ^The [sqlite3_str_value(X)] method returns a pointer to the current
|
||||
** content of the dynamic string under construction in X. The value
|
||||
** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
|
||||
** and might be freed or altered by any subsequent method on the same
|
||||
** [sqlite3_str] object. Applications must not used the pointer returned
|
||||
** [sqlite3_str_value(X)] after any subsequent method call on the same
|
||||
** object. ^Applications may change the content of the string returned
|
||||
** by [sqlite3_str_value(X)] as long as they do not write into any bytes
|
||||
** outside the range of 0 to [sqlite3_str_length(X)] and do not read or
|
||||
** write any byte after any subsequent sqlite3_str method call.
|
||||
*/
|
||||
SQLITE_API int sqlite3_str_errcode(sqlite3_str*);
|
||||
SQLITE_API int sqlite3_str_length(sqlite3_str*);
|
||||
SQLITE_API char *sqlite3_str_value(sqlite3_str*);
|
||||
|
||||
/*
|
||||
** CAPI3REF: SQLite Runtime Status
|
||||
**
|
||||
@@ -8539,11 +8282,11 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
|
||||
** method of a [virtual table], then it returns true if and only if the
|
||||
** column is being fetched as part of an UPDATE operation during which the
|
||||
** column value will not change. Applications might use this to substitute
|
||||
** a return value that is less expensive to compute and that the corresponding
|
||||
** [xUpdate] method understands as a "no-change" value.
|
||||
** a lighter-weight value to return that the corresponding [xUpdate] method
|
||||
** understands as a "no-change" value.
|
||||
**
|
||||
** If the [xColumn] method calls sqlite3_vtab_nochange() and finds that
|
||||
** the column is not changed by the UPDATE statement, then the xColumn
|
||||
** the column is not changed by the UPDATE statement, they the xColumn
|
||||
** method can optionally return without setting a result, without calling
|
||||
** any of the [sqlite3_result_int|sqlite3_result_xxxxx() interfaces].
|
||||
** In that case, [sqlite3_value_nochange(X)] will return true for the
|
||||
@@ -9038,7 +8781,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
|
||||
** been a prior call to [sqlite3_deserialize(D,S,...)] with the same
|
||||
** values of D and S.
|
||||
** The size of the database is written into *P even if the
|
||||
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
|
||||
** SQLITE_SERIALIZE_NOCOPY bit is set but no contigious copy
|
||||
** of the database exists.
|
||||
**
|
||||
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
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()
|
||||
get_target_property (QT_QMAKE_EXECUTABLE Qt5::qmake IMPORTED_LOCATION)
|
||||
message(STATUS "Using Qt ${Qt5Core_VERSION} (${QT_QMAKE_EXECUTABLE})")
|
||||
|
||||
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5Keychain REQUIRED)
|
||||
|
||||
@@ -16,11 +16,13 @@ if(UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_LIBRARIES_ONLY)
|
||||
add_executable(${cmd_NAME} ${cmd_SRC})
|
||||
set_target_properties(${cmd_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
add_executable(${cmd_NAME} ${cmd_SRC})
|
||||
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} "${csync_NAME}" "${synclib_NAME}" Qt5::Core Qt5::Network)
|
||||
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)
|
||||
|
||||
@@ -272,12 +272,10 @@ bool FileSystem::openAndSeekFileSharedRead(QFile *file, QString *errorOrNull, qi
|
||||
int fd = _open_osfhandle((intptr_t)fileHandle, _O_RDONLY);
|
||||
if (fd == -1) {
|
||||
error = "could not make fd from handle";
|
||||
CloseHandle(fileHandle);
|
||||
return false;
|
||||
}
|
||||
if (!file->open(fd, QIODevice::ReadOnly, QFile::AutoCloseHandle)) {
|
||||
error = file->errorString();
|
||||
_close(fd); // implicitly closes fileHandle
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ static const char letters[] = " WDNVCKRSMm";
|
||||
template <typename Char>
|
||||
void RemotePermissions::fromArray(const Char *p)
|
||||
{
|
||||
_value = notNullMask;
|
||||
_value = p ? notNullMask : 0;
|
||||
if (!p)
|
||||
return;
|
||||
while (*p) {
|
||||
@@ -37,7 +37,17 @@ void RemotePermissions::fromArray(const Char *p)
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray RemotePermissions::toDbValue() const
|
||||
RemotePermissions::RemotePermissions(const char *p)
|
||||
{
|
||||
fromArray(p);
|
||||
}
|
||||
|
||||
RemotePermissions::RemotePermissions(const QString &s)
|
||||
{
|
||||
fromArray(s.isEmpty() ? nullptr : s.utf16());
|
||||
}
|
||||
|
||||
QByteArray RemotePermissions::toString() const
|
||||
{
|
||||
QByteArray result;
|
||||
if (isNull())
|
||||
@@ -54,25 +64,4 @@ QByteArray RemotePermissions::toDbValue() const
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray RemotePermissions::toString() const
|
||||
{
|
||||
return toDbValue();
|
||||
}
|
||||
|
||||
RemotePermissions RemotePermissions::fromDbValue(const QByteArray &value)
|
||||
{
|
||||
if (value.isEmpty())
|
||||
return RemotePermissions();
|
||||
RemotePermissions perm;
|
||||
perm.fromArray(value.constData());
|
||||
return perm;
|
||||
}
|
||||
|
||||
RemotePermissions RemotePermissions::fromServerString(const QString &value)
|
||||
{
|
||||
RemotePermissions perm;
|
||||
perm.fromArray(value.utf16());
|
||||
return perm;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -57,22 +57,11 @@ public:
|
||||
// (by setting forceRemoteDiscovery in SyncJournalDb::checkConnect)
|
||||
PermissionsCount = IsMountedSub
|
||||
};
|
||||
|
||||
/// null permissions
|
||||
RemotePermissions() = default;
|
||||
explicit RemotePermissions(const char *);
|
||||
explicit RemotePermissions(const QString &);
|
||||
|
||||
/// array with one character per permission, "" is null, " " is non-null but empty
|
||||
QByteArray toDbValue() const;
|
||||
|
||||
/// output for display purposes, no defined format (same as toDbValue in practice)
|
||||
QByteArray toString() const;
|
||||
|
||||
/// read value that was written with toDbValue()
|
||||
static RemotePermissions fromDbValue(const QByteArray &);
|
||||
|
||||
/// read a permissions string received from the server, never null
|
||||
static RemotePermissions fromServerString(const QString &);
|
||||
|
||||
bool hasPermission(Permissions p) const
|
||||
{
|
||||
return _value & (1 << static_cast<int>(p));
|
||||
|
||||
@@ -58,7 +58,7 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que
|
||||
rec._type = static_cast<ItemType>(query.intValue(3));
|
||||
rec._etag = query.baValue(4);
|
||||
rec._fileId = query.baValue(5);
|
||||
rec._remotePerm = RemotePermissions::fromDbValue(query.baValue(6));
|
||||
rec._remotePerm = RemotePermissions(query.baValue(6).constData());
|
||||
rec._fileSize = query.int64Value(7);
|
||||
rec._serverHasIgnoredFiles = (query.intValue(8) > 0);
|
||||
rec._checksumHeader = query.baValue(9);
|
||||
@@ -717,15 +717,6 @@ bool SyncJournalDb::updateMetadataTableStructure()
|
||||
commitInternal("update database structure: add contentChecksum col for uploadinfo");
|
||||
}
|
||||
|
||||
if (!tableColumns("conflicts").contains("basePath")) {
|
||||
SqlQuery query(_db);
|
||||
query.prepare("ALTER TABLE conflicts ADD COLUMN basePath TEXT;");
|
||||
if (!query.exec()) {
|
||||
sqlFail("updateMetadataTableStructure: add basePath column", query);
|
||||
re = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return re;
|
||||
}
|
||||
@@ -1904,14 +1895,13 @@ void SyncJournalDb::setConflictRecord(const ConflictRecord &record)
|
||||
auto &query = _setConflictRecordQuery;
|
||||
ASSERT(query.initOrReset(QByteArrayLiteral(
|
||||
"INSERT OR REPLACE INTO conflicts "
|
||||
"(path, baseFileId, baseModtime, baseEtag, basePath) "
|
||||
"VALUES (?1, ?2, ?3, ?4, ?5);"),
|
||||
"(path, baseFileId, baseModtime, baseEtag) "
|
||||
"VALUES (?1, ?2, ?3, ?4);"),
|
||||
_db));
|
||||
query.bindValue(1, record.path);
|
||||
query.bindValue(2, record.baseFileId);
|
||||
query.bindValue(3, record.baseModtime);
|
||||
query.bindValue(4, record.baseEtag);
|
||||
query.bindValue(5, record.initialBasePath);
|
||||
ASSERT(query.exec());
|
||||
}
|
||||
|
||||
@@ -1923,7 +1913,7 @@ ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path)
|
||||
if (!checkConnect())
|
||||
return entry;
|
||||
auto &query = _getConflictRecordQuery;
|
||||
ASSERT(query.initOrReset(QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag, basePath FROM conflicts WHERE path=?1;"), _db));
|
||||
ASSERT(query.initOrReset(QByteArrayLiteral("SELECT baseFileId, baseModtime, baseEtag FROM conflicts WHERE path=?1;"), _db));
|
||||
query.bindValue(1, path);
|
||||
ASSERT(query.exec());
|
||||
if (!query.next())
|
||||
@@ -1933,7 +1923,6 @@ ConflictRecord SyncJournalDb::conflictRecord(const QByteArray &path)
|
||||
entry.baseFileId = query.baValue(0);
|
||||
entry.baseModtime = query.int64Value(1);
|
||||
entry.baseEtag = query.baValue(2);
|
||||
entry.initialBasePath = query.baValue(3);
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
}
|
||||
int _chunk;
|
||||
int _transferid;
|
||||
qint64 _size;
|
||||
quint64 _size; //currently unused
|
||||
qint64 _modtime;
|
||||
int _errorCount;
|
||||
bool _valid;
|
||||
|
||||
@@ -29,20 +29,9 @@ SyncJournalFileRecord::SyncJournalFileRecord()
|
||||
{
|
||||
}
|
||||
|
||||
QByteArray SyncJournalFileRecord::legacyDeriveNumericFileId() const
|
||||
QByteArray SyncJournalFileRecord::numericFileId() const
|
||||
{
|
||||
// The id property which is stored in _fileId is
|
||||
// leftpad_with_zero(fileid, 8) + instanceid
|
||||
// so if it starts with a 0 we know the first 8 bytes
|
||||
// can be taken.
|
||||
if (_fileId.startsWith('0')) {
|
||||
return _fileId.left(8);
|
||||
}
|
||||
|
||||
// Otherwise we don't know exactly how long it is,
|
||||
// use every digit until the first letter. The instanceid of
|
||||
// oc >= 6 starts with "oc". This will break for older instances
|
||||
// that have a digit as the first character of the instance id.
|
||||
// Use the id up until the first non-numeric character
|
||||
for (int i = 0; i < _fileId.size(); ++i) {
|
||||
if (_fileId[i] < '0' || _fileId[i] > '9') {
|
||||
return _fileId.left(i);
|
||||
|
||||
@@ -45,18 +45,13 @@ public:
|
||||
return !_path.isEmpty();
|
||||
}
|
||||
|
||||
/** Returns a guess for the numeric part of the full id in _fileId.
|
||||
/** Returns the numeric part of the full id in _fileId.
|
||||
*
|
||||
* On the server this is sometimes known as the internal file id, the "fileid" DAV property.
|
||||
* On the server this is sometimes known as the internal file id.
|
||||
*
|
||||
* It is used in the fallback construction of private links.
|
||||
*
|
||||
* New code should not use this function: Request the fileid property from the server.
|
||||
* In the future we might store the fileid instead of the id, but the migration is complex
|
||||
* if we don't want to store both.
|
||||
* It is used in the construction of private links.
|
||||
*/
|
||||
QByteArray legacyDeriveNumericFileId() const;
|
||||
|
||||
QByteArray numericFileId() const;
|
||||
QDateTime modDateTime() const { return Utility::qDateTimeFromTime_t(_modtime); }
|
||||
|
||||
QByteArray _path;
|
||||
@@ -151,17 +146,6 @@ public:
|
||||
*/
|
||||
QByteArray baseEtag;
|
||||
|
||||
/**
|
||||
* The path of the original file at the time the conflict was created
|
||||
*
|
||||
* Note that in nearly all cases one should query the db by baseFileId and
|
||||
* thus retrieve the *current* base path instead!
|
||||
*
|
||||
* maybe be empty if not available
|
||||
*/
|
||||
QByteArray initialBasePath;
|
||||
|
||||
|
||||
bool isValid() const { return !path.isEmpty(); }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ if(NOT BUILD_LIBRARIES_ONLY)
|
||||
set_target_properties(${CRASHREPORTER_EXECUTABLE} PROPERTIES AUTOMOC ON)
|
||||
set_target_properties(${CRASHREPORTER_EXECUTABLE} PROPERTIES AUTORCC 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
|
||||
Qt5::Core Qt5::Widgets
|
||||
|
||||
@@ -69,35 +69,36 @@ endif()
|
||||
|
||||
configure_file(csync_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h)
|
||||
|
||||
add_library("${csync_NAME}" SHARED ${common_SOURCES} ${csync_SRCS})
|
||||
set(CSYNC_LIBRARY ocsync)
|
||||
add_library(${CSYNC_LIBRARY} SHARED ${common_SOURCES} ${csync_SRCS})
|
||||
|
||||
target_include_directories(
|
||||
"${csync_NAME}"
|
||||
${CSYNC_LIBRARY}
|
||||
PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/std
|
||||
)
|
||||
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
# make sure that the path for the local sqlite3 is before the system one
|
||||
target_include_directories("${csync_NAME}" BEFORE PRIVATE ${SQLITE3_INCLUDE_DIR})
|
||||
target_include_directories(${CSYNC_LIBRARY} BEFORE PRIVATE ${SQLITE3_INCLUDE_DIR})
|
||||
else()
|
||||
target_include_directories("${csync_NAME}" PRIVATE ${SQLITE3_INCLUDE_DIR})
|
||||
target_include_directories(${CSYNC_LIBRARY} PRIVATE ${SQLITE3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
|
||||
generate_export_header("${csync_NAME}"
|
||||
generate_export_header(${CSYNC_LIBRARY}
|
||||
EXPORT_MACRO_NAME OCSYNC_EXPORT
|
||||
EXPORT_FILE_NAME ocsynclib.h
|
||||
)
|
||||
|
||||
target_link_libraries("${csync_NAME}"
|
||||
target_link_libraries(${CSYNC_LIBRARY}
|
||||
${CSYNC_REQUIRED_LIBRARIES}
|
||||
${SQLITE3_LIBRARIES}
|
||||
Qt5::Core Qt5::Concurrent
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
target_link_libraries("${csync_NAME}" ZLIB::ZLIB)
|
||||
target_link_libraries(${CSYNC_LIBRARY} ZLIB::ZLIB)
|
||||
endif(ZLIB_FOUND)
|
||||
|
||||
|
||||
@@ -105,11 +106,11 @@ endif(ZLIB_FOUND)
|
||||
if (APPLE)
|
||||
find_library(FOUNDATION_LIBRARY NAMES Foundation)
|
||||
find_library(CORESERVICES_LIBRARY NAMES CoreServices)
|
||||
target_link_libraries("${csync_NAME}" ${FOUNDATION_LIBRARY} ${CORESERVICES_LIBRARY})
|
||||
target_link_libraries(${CSYNC_LIBRARY} ${FOUNDATION_LIBRARY} ${CORESERVICES_LIBRARY})
|
||||
endif()
|
||||
|
||||
set_target_properties(
|
||||
"${csync_NAME}"
|
||||
${CSYNC_LIBRARY}
|
||||
PROPERTIES
|
||||
VERSION
|
||||
${LIBRARY_VERSION}
|
||||
@@ -121,7 +122,7 @@ set_target_properties(
|
||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
INSTALL(
|
||||
TARGETS
|
||||
"${csync_NAME}"
|
||||
${CSYNC_LIBRARY}
|
||||
LIBRARY DESTINATION
|
||||
${LIB_INSTALL_DIR}
|
||||
ARCHIVE DESTINATION
|
||||
@@ -132,13 +133,13 @@ if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
else()
|
||||
INSTALL(
|
||||
TARGETS
|
||||
"${csync_NAME}"
|
||||
${CSYNC_LIBRARY}
|
||||
LIBRARY DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
${CMAKE_INSTALL_LIBDIR}/${APPLICATION_EXECUTABLE}
|
||||
ARCHIVE DESTINATION
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
${CMAKE_INSTALL_LIBDIR}/${APPLICATION_EXECUTABLE}
|
||||
RUNTIME DESTINATION
|
||||
${CMAKE_INSTALL_BINDIR}
|
||||
${CMAKE_INSTALL_BINDIR}/${APPLICATION_EXECUTABLE}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ int csync_s::reinitialize() {
|
||||
renames.folder_renamed_to.clear();
|
||||
|
||||
status = CSYNC_STATUS_INIT;
|
||||
error_string.clear();
|
||||
SAFE_FREE(error_string);
|
||||
|
||||
rc = 0;
|
||||
return rc;
|
||||
@@ -252,6 +252,7 @@ int csync_s::reinitialize() {
|
||||
|
||||
csync_s::~csync_s() {
|
||||
SAFE_FREE(local.uri);
|
||||
SAFE_FREE(error_string);
|
||||
}
|
||||
|
||||
void *csync_get_userdata(CSYNC *ctx) {
|
||||
@@ -297,6 +298,11 @@ CSYNC_STATUS csync_get_status(CSYNC *ctx) {
|
||||
return ctx->status_code;
|
||||
}
|
||||
|
||||
const char *csync_get_status_string(CSYNC *ctx)
|
||||
{
|
||||
return csync_vio_get_status_string(ctx);
|
||||
}
|
||||
|
||||
void csync_request_abort(CSYNC *ctx)
|
||||
{
|
||||
if (ctx != NULL) {
|
||||
|
||||
@@ -307,6 +307,15 @@ int OCSYNC_EXPORT csync_walk_local_tree(CSYNC *ctx, const csync_treewalk_visit_f
|
||||
*/
|
||||
int OCSYNC_EXPORT csync_walk_remote_tree(CSYNC *ctx, const csync_treewalk_visit_func &visitor);
|
||||
|
||||
/**
|
||||
* @brief Get the csync status string.
|
||||
*
|
||||
* @param ctx The csync context.
|
||||
*
|
||||
* @return A const pointer to a string with more precise status info.
|
||||
*/
|
||||
const char OCSYNC_EXPORT *csync_get_status_string(CSYNC *ctx);
|
||||
|
||||
/**
|
||||
* @brief Aborts the current sync run as soon as possible. Can be called from another thread.
|
||||
*
|
||||
|
||||
@@ -180,10 +180,7 @@ struct OCSYNC_EXPORT csync_s {
|
||||
/* csync error code */
|
||||
enum csync_status_codes_e status_code = CSYNC_STATUS_OK;
|
||||
|
||||
/* Some additional string information which is added to the error message corresponding to the error code in errno.
|
||||
* Usually a filename
|
||||
*/
|
||||
QString error_string;
|
||||
char *error_string = nullptr;
|
||||
|
||||
int status = CSYNC_STATUS_INIT;
|
||||
volatile bool abort = false;
|
||||
|
||||
@@ -210,7 +210,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
/* First, check that the file is NOT in our tree (another file with the same name was added) */
|
||||
if (our_tree->findFile(basePath)) {
|
||||
other = nullptr;
|
||||
qCInfo(lcReconcile, "Origin found in our tree : %s", basePath.constData());
|
||||
qCDebug(lcReconcile, "Origin found in our tree : %s", basePath.constData());
|
||||
} else {
|
||||
/* Find the potential rename source file in the other tree.
|
||||
* If the renamed file could not be found in the opposite tree, that is because it
|
||||
@@ -218,7 +218,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
* The journal is cleaned up later after propagation.
|
||||
*/
|
||||
other = other_tree->findFile(basePath);
|
||||
qCInfo(lcReconcile, "Rename origin in other tree (%s) %s",
|
||||
qCDebug(lcReconcile, "Rename origin in other tree (%s) %s",
|
||||
basePath.constData(), other ? "found" : "not found");
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
// Some other EVAL_RENAME already claimed other.
|
||||
// We do nothing: maybe a different candidate for
|
||||
// other is found as well?
|
||||
qCInfo(lcReconcile, "Other has already been renamed to %s",
|
||||
qCDebug(lcReconcile, "Other has already been renamed to %s",
|
||||
other->rename_path.constData());
|
||||
} else if (cur->type == ItemTypeDirectory
|
||||
// The local replica is reconciled first, so the remote tree would
|
||||
@@ -241,17 +241,13 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
|| other->instruction == CSYNC_INSTRUCTION_NONE
|
||||
|| other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA
|
||||
|| other->instruction == CSYNC_INSTRUCTION_REMOVE) {
|
||||
qCInfo(lcReconcile, "Switching %s to RENAME to %s",
|
||||
qCDebug(lcReconcile, "Switching %s to RENAME to %s",
|
||||
other->path.constData(), cur->path.constData());
|
||||
other->instruction = CSYNC_INSTRUCTION_RENAME;
|
||||
other->rename_path = cur->path;
|
||||
if( !cur->file_id.isEmpty() ) {
|
||||
other->file_id = cur->file_id;
|
||||
}
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
// Keep the local mtime.
|
||||
other->modtime = cur->modtime;
|
||||
}
|
||||
other->inode = cur->inode;
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
// We have consumed 'other': exit this loop to not consume another one.
|
||||
@@ -265,7 +261,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
// Local: The remote reconcile will be able to deal with this.
|
||||
// Remote: The local replica has already dealt with this.
|
||||
// See the EVAL_RENAME case when other was found directly.
|
||||
qCInfo(lcReconcile, "File in a renamed directory, other side's instruction: %d",
|
||||
qCDebug(lcReconcile, "File in a renamed directory, other side's instruction: %d",
|
||||
other->instruction);
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else {
|
||||
@@ -273,7 +269,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
// and the instruction in the local tree is NEW while cur has EVAL_RENAME
|
||||
// due to a remote move of the same file. In these scenarios we just
|
||||
// want the instruction to stay NEW.
|
||||
qCInfo(lcReconcile, "Other already has instruction %d",
|
||||
qCDebug(lcReconcile, "Other already has instruction %d",
|
||||
other->instruction);
|
||||
}
|
||||
};
|
||||
@@ -281,7 +277,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
/* use the old name to find the "other" node */
|
||||
OCC::SyncJournalFileRecord base;
|
||||
qCInfo(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
||||
qCDebug(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
||||
cur->inode);
|
||||
ctx->statedb->getFileRecordByInode(cur->inode, &base);
|
||||
renameCandidateProcessing(base._path);
|
||||
@@ -294,7 +290,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
// line.
|
||||
auto basePath = csync_rename_adjust_full_path_source(ctx, cur->path);
|
||||
if (basePath != cur->path) {
|
||||
qCInfo(lcReconcile, "Trying rename origin by csync_rename mapping %s",
|
||||
qCDebug(lcReconcile, "Trying rename origin by csync_rename mapping %s",
|
||||
basePath.constData());
|
||||
// We go through getFileRecordsByFileId to ensure the basePath
|
||||
// computed in this way also has the expected fileid.
|
||||
@@ -307,7 +303,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
|
||||
// Also feed all the other files with the same fileid if necessary
|
||||
if (!processedRename) {
|
||||
qCInfo(lcReconcile, "Finding rename origin through file ID %s",
|
||||
qCDebug(lcReconcile, "Finding rename origin through file ID %s",
|
||||
cur->file_id.constData());
|
||||
ctx->statedb->getFileRecordsByFileId(cur->file_id,
|
||||
[&](const OCC::SyncJournalFileRecord &base) { renameCandidateProcessing(base._path); });
|
||||
@@ -374,8 +370,7 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
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 && up._size == localNode->size ?
|
||||
CSYNC_INSTRUCTION_UPDATE_METADATA : CSYNC_INSTRUCTION_SYNC;
|
||||
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
|
||||
@@ -465,16 +460,6 @@ static void _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// We *never* propagate a local NEW virtual file to the server
|
||||
// This can happen if we had EVAL_RENAME but something else happened on the remote
|
||||
// and the rename doesn't go through.
|
||||
if ((cur->type == ItemTypeVirtualFile || cur->type == ItemTypeVirtualFileDownload)
|
||||
&& ctx->current == LOCAL_REPLICA
|
||||
&& cur->instruction == CSYNC_INSTRUCTION_NEW) {
|
||||
qCInfo(lcReconcile) << "Downgrading NEW to IGNORE for local virtual file" << cur->path;
|
||||
cur->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
}
|
||||
|
||||
//hide instruction NONE messages when log level is set to debug,
|
||||
//only show these messages on log level trace
|
||||
const char *repo = ctx->current == REMOTE_REPLICA ? "server" : "client";
|
||||
|
||||
@@ -126,12 +126,12 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
* This code should probably be in csync_exclude, but it does not have the fs parameter.
|
||||
* Keep it here for now */
|
||||
if (ctx->ignore_hidden_files && (fs->is_hidden)) {
|
||||
qCInfo(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
|
||||
qCDebug(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
|
||||
excluded = CSYNC_FILE_EXCLUDE_HIDDEN;
|
||||
}
|
||||
} else {
|
||||
/* File is ignored because it's matched by a user- or system exclude pattern. */
|
||||
qCInfo(lcUpdate, "%s excluded (%d)", fs->path.constData(), excluded);
|
||||
qCDebug(lcUpdate, "%s excluded (%d)", fs->path.constData(), excluded);
|
||||
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
|
||||
return 1;
|
||||
}
|
||||
@@ -156,7 +156,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
*/
|
||||
QTextEncoder encoder(localCodec, QTextCodec::ConvertInvalidToNull);
|
||||
if (encoder.fromUnicode(QString::fromUtf8(fs->path)).contains('\0')) {
|
||||
qCInfo(lcUpdate, "cannot encode %s to local encoding %d",
|
||||
qCDebug(lcUpdate, "cannot encode %s to local encoding %d",
|
||||
fs->path.constData(), localCodec->mibEnum());
|
||||
excluded = CSYNC_FILE_EXCLUDE_CANNOT_ENCODE;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
|
||||
if (fs->type == ItemTypeFile ) {
|
||||
if (fs->modtime == 0) {
|
||||
qCInfo(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
||||
qCDebug(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
checksumIdentical = fs->checksumHeader == base._checksumHeader;
|
||||
}
|
||||
if (checksumIdentical) {
|
||||
qCInfo(lcUpdate, "NOTE: Checksums are identical, file did not actually change: %s", fs->path.constData());
|
||||
qCDebug(lcUpdate, "NOTE: Checksums are identical, file did not actually change: %s", fs->path.constData());
|
||||
fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
goto out;
|
||||
}
|
||||
@@ -293,7 +293,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
* The metadata comparison ensure that we fetch all the file id or permission when
|
||||
* upgrading owncloud
|
||||
*/
|
||||
qCInfo(lcUpdate, "Reading from database: %s", fs->path.constData());
|
||||
qCDebug(lcUpdate, "Reading from database: %s", fs->path.constData());
|
||||
ctx->remote.read_from_db = true;
|
||||
}
|
||||
/* If it was remembered in the db that the remote dir has ignored files, store
|
||||
@@ -304,7 +304,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
}
|
||||
if (metadata_differ) {
|
||||
/* file id or permissions has changed. Which means we need to update them in the DB. */
|
||||
qCInfo(lcUpdate, "Need to update metadata for: %s", fs->path.constData());
|
||||
qCDebug(lcUpdate, "Need to update metadata for: %s", fs->path.constData());
|
||||
fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
} else {
|
||||
fs->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
@@ -312,7 +312,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
} else {
|
||||
/* check if it's a file and has been renamed */
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
qCInfo(lcUpdate, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
qCDebug(lcUpdate, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
|
||||
OCC::SyncJournalFileRecord base;
|
||||
if(!ctx->statedb->getFileRecordByInode(fs->inode, &base)) {
|
||||
@@ -324,15 +324,10 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
fs->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
|
||||
bool isRename =
|
||||
base.isValid()
|
||||
&& (base._type == fs->type
|
||||
// The file could be scheduled for download and renamed
|
||||
|| (base._type == ItemTypeVirtualFileDownload && fs->type == ItemTypeVirtualFile))
|
||||
&& ((base._modtime == fs->modtime && base._fileSize == fs->size)
|
||||
// Directories and virtual files don't need size/mtime equality
|
||||
|| fs->type == ItemTypeDirectory || fs->type == ItemTypeVirtualFile)
|
||||
base.isValid() && base._type == fs->type
|
||||
&& ((base._modtime == fs->modtime && base._fileSize == fs->size) || fs->type == ItemTypeDirectory)
|
||||
#ifdef NO_RENAME_EXTENSION
|
||||
&& _csync_sameextension(base._path, fs->path)
|
||||
&& _csync_sameextension(base._path, fs->path)
|
||||
#endif
|
||||
;
|
||||
|
||||
@@ -344,69 +339,22 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
_rel_to_abs(ctx, fs->path), base._checksumHeader,
|
||||
ctx->callbacks.checksum_userdata);
|
||||
if (!fs->checksumHeader.isEmpty()) {
|
||||
qCInfo(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), base._checksumHeader.constData());
|
||||
qCDebug(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), base._checksumHeader.constData());
|
||||
isRename = fs->checksumHeader == base._checksumHeader;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRename) {
|
||||
qCInfo(lcUpdate, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
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 == ItemTypeDirectory) {
|
||||
csync_rename_record(ctx, base._path, fs->path);
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fs->type == ItemTypeVirtualFile) {
|
||||
// It's allowed to rename "abc" -> "abc.owncloud": In that case the file
|
||||
// becomes a placeholder by being drained of contents.
|
||||
if (base.isValid()
|
||||
&& base._type == ItemTypeFile
|
||||
&& fs->size == base._fileSize
|
||||
&& fs->modtime == base._modtime
|
||||
&& fs->path.left(fs->path.size() - ctx->virtual_file_suffix.size()) == base._path) {
|
||||
qCInfo(lcUpdate) << "Base file was renamed to virtual file:" << fs->path;
|
||||
|
||||
// Wipe the base db entry to make sure the base file isn't removed
|
||||
// from the server later!
|
||||
// WARNING: Side effect on database during update phase:
|
||||
// Makes local/remote discovery order dependent!
|
||||
ctx->statedb->deleteFileRecord(base._path);
|
||||
|
||||
// Wipe the placeholder file such that the remote discovery will
|
||||
// note it must be recreated with the right size and flags.
|
||||
QFile::remove(QString::fromUtf8(ctx->local.uri) + "/" + fs->path);
|
||||
|
||||
// Also set up the expected db entry
|
||||
// WARNING: Side effect on database during update phase again
|
||||
base._path = fs->path;
|
||||
base._type = ItemTypeVirtualFile;
|
||||
ctx->statedb->setFileRecord(base);
|
||||
|
||||
return 0; // Don't add as entry into local files
|
||||
}
|
||||
|
||||
// Remove the spurious file if it looks like a placeholder file
|
||||
// (we know placeholder files contain " ")
|
||||
if (fs->size <= 1) {
|
||||
QFile::remove(QString::fromUtf8(ctx->local.uri) + "/" + fs->path);
|
||||
qCWarning(lcUpdate) << "Wiping virtual file without db entry for" << fs->path;
|
||||
return 0; // Don't add as entry into local files
|
||||
} else {
|
||||
fs->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
qCWarning(lcUpdate) << "Virtual file without db entry for" << fs->path
|
||||
<< "but looks odd, keeping";
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
} else {
|
||||
qCInfo(lcUpdate, "Checking for rename based on fileid %s", fs->file_id.constData());
|
||||
|
||||
/* Remote Replica Rename check */
|
||||
fs->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
|
||||
@@ -460,7 +408,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
csync_rename_record(ctx, base._path, fs->path);
|
||||
}
|
||||
|
||||
qCInfo(lcUpdate, "remote rename detected based on fileid %s --> %s", base._path.constData(), fs->path.constData());
|
||||
qCDebug(lcUpdate, "remote rename detected based on fileid %s --> %s", base._path.constData(), fs->path.constData());
|
||||
fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
|
||||
done = true;
|
||||
};
|
||||
@@ -562,22 +510,13 @@ int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
|
||||
}
|
||||
|
||||
switch (fs->type) {
|
||||
case ItemTypeFile:
|
||||
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 ItemTypeVirtualFile:
|
||||
// This can happen when locally a virtual file is seen that doen't have
|
||||
// a matching database entry.
|
||||
if (ctx->current == REMOTE_REPLICA) {
|
||||
qCDebug(lcUpdate, "virtual file: %s [file_id=%s size=%" PRIu64 "]", fs->path.constData(), fs->file_id.constData(), fs->size);
|
||||
} else {
|
||||
qCDebug(lcUpdate, "virtual file: %s [inode=%" PRIu64 " size=%" PRIu64 "]", fs->path.constData(), fs->inode, fs->size);
|
||||
}
|
||||
break;
|
||||
case ItemTypeDirectory: /* enter directory */
|
||||
if (ctx->current == REMOTE_REPLICA) {
|
||||
qCDebug(lcUpdate, "directory: %s [file_id=%s]", fs->path.constData(), fs->file_id.constData());
|
||||
@@ -586,11 +525,11 @@ int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
|
||||
}
|
||||
break;
|
||||
case ItemTypeSoftLink:
|
||||
qCInfo(lcUpdate, "symlink: %s - not supported", fs->path.constData());
|
||||
qCDebug(lcUpdate, "symlink: %s - not supported", fs->path.constData());
|
||||
break;
|
||||
default:
|
||||
qCInfo(lcUpdate, "item: %s - item type %d not iterated", fs->path.constData(), fs->type);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = _csync_detect_update(ctx, std::move(fs));
|
||||
@@ -613,7 +552,7 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri, bool singleFile = fal
|
||||
* their correct etags again and we don't run into this case.
|
||||
*/
|
||||
if (rec._etag == "_invalid_") {
|
||||
qCInfo(lcUpdate, "%s selective sync excluded", rec._path.constData());
|
||||
qCDebug(lcUpdate, "%s selective sync excluded", rec._path.constData());
|
||||
skipbase = rec._path;
|
||||
skipbase += '/';
|
||||
return;
|
||||
@@ -738,6 +677,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
ctx->status_code = CSYNC_STATUS_ABORTED;
|
||||
goto error;
|
||||
}
|
||||
int asp = 0;
|
||||
/* permission denied */
|
||||
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR);
|
||||
if (errno == EACCES) {
|
||||
@@ -746,7 +686,8 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
return 0;
|
||||
}
|
||||
} else if(errno == ENOENT) {
|
||||
ctx->error_string = QString::fromUtf8(uri);
|
||||
asp = asprintf( &ctx->error_string, "%s", uri);
|
||||
ASSERT(asp >= 0);
|
||||
}
|
||||
// 403 Forbidden can be sent by the server if the file firewall is active.
|
||||
// A file or directory should be ignored and sync must continue. See #3490
|
||||
@@ -773,25 +714,11 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
goto error;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// Get the next item in the directory
|
||||
errno = 0;
|
||||
dirent = csync_vio_readdir(ctx, dh);
|
||||
if (!dirent) {
|
||||
if (errno != 0) {
|
||||
// Note: Windows vio converts any error into EACCES
|
||||
qCWarning(lcUpdate, "readdir failed for file in %s - errno %d", uri, errno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Normal case: End of items in directory
|
||||
break;
|
||||
}
|
||||
|
||||
while ((dirent = csync_vio_readdir(ctx, dh))) {
|
||||
/* Conversion error */
|
||||
if (dirent->path.isEmpty() && !dirent->original_path.isEmpty()) {
|
||||
ctx->status_code = CSYNC_STATUS_INVALID_CHARACTERS;
|
||||
ctx->error_string = QString::fromUtf8(dirent->original_path);
|
||||
ctx->error_string = c_strdup(dirent->original_path);
|
||||
dirent->original_path.clear();
|
||||
goto error;
|
||||
}
|
||||
@@ -821,12 +748,12 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
&& filename.endsWith(ctx->virtual_file_suffix)) {
|
||||
QByteArray db_uri = fullpath.mid(strlen(ctx->local.uri) + 1);
|
||||
|
||||
if (fill_tree_from_db(ctx, db_uri.constData(), true))
|
||||
continue;
|
||||
if( ! fill_tree_from_db(ctx, db_uri.constData(), true) ) {
|
||||
qCWarning(lcUpdate) << "Virtual file without db entry for" << filename;
|
||||
QFile::remove(fullpath);
|
||||
}
|
||||
|
||||
// We didn't find this file in the database: Let this proceed into
|
||||
// rename detection as a virtual file.
|
||||
dirent->type = ItemTypeVirtualFile;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if the filename starts with a . we consider it a hidden file
|
||||
@@ -896,7 +823,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
}
|
||||
|
||||
csync_vio_closedir(ctx, dh);
|
||||
qCInfo(lcUpdate, " <= Closing walk for %s with read_from_db %d", uri, read_from_db);
|
||||
qCDebug(lcUpdate, " <= Closing walk for %s with read_from_db %d", uri, read_from_db);
|
||||
|
||||
return rc;
|
||||
|
||||
|
||||
@@ -90,3 +90,9 @@ std::unique_ptr<csync_file_stat_t> csync_vio_readdir(CSYNC *ctx, csync_vio_handl
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *csync_vio_get_status_string(CSYNC *ctx) {
|
||||
if(ctx->error_string) {
|
||||
return ctx->error_string;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -35,4 +35,8 @@ typedef struct fhandle_s {
|
||||
csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name);
|
||||
int csync_vio_closedir(CSYNC *ctx, csync_vio_handle_t *dhandle);
|
||||
std::unique_ptr<csync_file_stat_t> csync_vio_readdir(CSYNC *ctx, csync_vio_handle_t *dhandle);
|
||||
|
||||
char *csync_vio_get_status_string(CSYNC *ctx);
|
||||
|
||||
|
||||
#endif /* _CSYNC_VIO_H */
|
||||
|
||||
@@ -156,7 +156,6 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
||||
// might be error, check!
|
||||
int dwError = GetLastError();
|
||||
if (dwError != ERROR_NO_MORE_FILES) {
|
||||
qCWarning(lcCSyncVIOLocal, "FindNextFile error %d", dwError);
|
||||
errno = EACCES; // no more files is fine. Otherwise EACCESS
|
||||
}
|
||||
return nullptr;
|
||||
@@ -182,7 +181,7 @@ std::unique_ptr<csync_file_stat_t> csync_vio_local_readdir(csync_vio_handle_t *d
|
||||
}
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
) {
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
file_stat->type = ItemTypeSkip;
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
file_stat->type = ItemTypeDirectory;
|
||||
|
||||
@@ -137,16 +137,9 @@ IF( APPLE )
|
||||
list(APPEND client_SRCS systray.mm)
|
||||
|
||||
if(SPARKLE_FOUND)
|
||||
# Define this, we need to check in updater.cpp
|
||||
add_definitions(-DHAVE_SPARKLE)
|
||||
list(APPEND updater_SRCS updater/sparkleupdater_mac.mm updater/sparkleupdater.h)
|
||||
list(APPEND updater_DEPS ${SPARKLE_LIBRARY})
|
||||
|
||||
# Sparkle.framework is installed from here because macdeployqt's CopyFramework breaks on this bundle
|
||||
# as its logic is tightly tailored around Qt5 frameworks
|
||||
install(DIRECTORY "${SPARKLE_LIBRARY}"
|
||||
DESTINATION "${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks" USE_SOURCE_PERMISSIONS)
|
||||
|
||||
# Define this, we need to check in updater.cpp
|
||||
add_definitions( -DHAVE_SPARKLE )
|
||||
list(APPEND updater_SRCS updater/sparkleupdater_mac.mm updater/sparkleupdater.h)
|
||||
endif()
|
||||
ENDIF()
|
||||
|
||||
@@ -228,7 +221,7 @@ 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_BASENAME "${APPLICATION_ICON_NAME}")
|
||||
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")
|
||||
@@ -275,12 +268,15 @@ else()
|
||||
endif()
|
||||
|
||||
add_library(updater STATIC ${updater_SRCS})
|
||||
target_link_libraries(updater ${synclib_NAME} ${updater_DEPS} Qt5::Widgets Qt5::Network Qt5::Xml)
|
||||
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}" )
|
||||
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} Qt5::Widgets Qt5::Network Qt5::Xml)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${synclib_NAME} )
|
||||
|
||||
@@ -96,7 +96,7 @@ protected:
|
||||
auto pos = folderList->mapFromGlobal(QCursor::pos());
|
||||
auto index = folderList->indexAt(pos);
|
||||
if (model->classify(index) == FolderStatusModel::RootFolder
|
||||
&& (FolderStatusDelegate::errorsListRect(folderList->visualRect(index), index).contains(pos)
|
||||
&& (FolderStatusDelegate::errorsListRect(folderList->visualRect(index)).contains(pos)
|
||||
|| FolderStatusDelegate::optionsButtonRect(folderList->visualRect(index),folderList->layoutDirection()).contains(pos))) {
|
||||
shape = Qt::PointingHandCursor;
|
||||
}
|
||||
@@ -162,7 +162,7 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
|
||||
|
||||
QAction *syncNowWithRemoteDiscovery = new QAction(this);
|
||||
syncNowWithRemoteDiscovery->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F6));
|
||||
connect(syncNowWithRemoteDiscovery, &QAction::triggered, this, &AccountSettings::slotScheduleCurrentFolderForceFullDiscovery);
|
||||
connect(syncNowWithRemoteDiscovery, &QAction::triggered, this, &AccountSettings::slotScheduleCurrentFolderForceRemoteDiscovery);
|
||||
addAction(syncNowWithRemoteDiscovery);
|
||||
|
||||
|
||||
@@ -287,9 +287,6 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
bool folderPaused = _model->data(index, FolderStatusDelegate::FolderSyncPaused).toBool();
|
||||
bool folderConnected = _model->data(index, FolderStatusDelegate::FolderAccountConnected).toBool();
|
||||
auto folderMan = FolderMan::instance();
|
||||
QPointer<Folder> folder = folderMan->folder(alias);
|
||||
if (!folder)
|
||||
return;
|
||||
|
||||
QMenu *menu = new QMenu(tv);
|
||||
|
||||
@@ -298,7 +295,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||
QAction *ac = menu->addAction(tr("Open folder"));
|
||||
connect(ac, &QAction::triggered, this, &AccountSettings::slotOpenCurrentFolder);
|
||||
|
||||
if (!ui->_folderList->isExpanded(index) && !folder->useVirtualFiles()) {
|
||||
if (!ui->_folderList->isExpanded(index)) {
|
||||
ac = menu->addAction(tr("Choose what to sync"));
|
||||
ac->setEnabled(folderConnected);
|
||||
connect(ac, &QAction::triggered, this, &AccountSettings::doExpand);
|
||||
@@ -343,7 +340,7 @@ void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
||||
slotCustomContextMenuRequested(pos);
|
||||
return;
|
||||
}
|
||||
if (FolderStatusDelegate::errorsListRect(tv->visualRect(indx), indx).contains(pos)) {
|
||||
if (FolderStatusDelegate::errorsListRect(tv->visualRect(indx)).contains(pos)) {
|
||||
emit showIssuesList(_model->data(indx, FolderStatusDelegate::FolderAliasRole).toString());
|
||||
return;
|
||||
}
|
||||
@@ -362,7 +359,6 @@ void AccountSettings::slotAddFolder()
|
||||
folderMan->setSyncEnabled(false); // do not start more syncs.
|
||||
|
||||
FolderWizard *folderWizard = new FolderWizard(_accountState->account(), this);
|
||||
folderWizard->setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
connect(folderWizard, &QDialog::accepted, this, &AccountSettings::slotFolderWizardAccepted);
|
||||
connect(folderWizard, &QDialog::rejected, this, &AccountSettings::slotFolderWizardRejected);
|
||||
@@ -567,12 +563,10 @@ void AccountSettings::slotScheduleCurrentFolder()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotScheduleCurrentFolderForceFullDiscovery()
|
||||
void AccountSettings::slotScheduleCurrentFolderForceRemoteDiscovery()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
if (auto folder = folderMan->folder(selectedFolderAlias())) {
|
||||
folder->slotWipeErrorBlacklist();
|
||||
folder->slotNextSyncFullLocalDiscovery();
|
||||
folder->journalDb()->forceRemoteDiscoveryNextSync();
|
||||
folderMan->scheduleFolder(folder);
|
||||
}
|
||||
@@ -588,8 +582,6 @@ void AccountSettings::slotForceSyncCurrentFolder()
|
||||
folderMan->scheduleFolder(current);
|
||||
}
|
||||
|
||||
selectedFolder->slotWipeErrorBlacklist(); // issue #6757
|
||||
|
||||
// Insert the selected folder at the front of the queue
|
||||
folderMan->scheduleFolderNext(selectedFolder);
|
||||
}
|
||||
@@ -659,7 +651,7 @@ void AccountSettings::slotAccountStateChanged()
|
||||
if (state == AccountState::Connected) {
|
||||
QStringList errors;
|
||||
if (account->serverVersionUnsupported()) {
|
||||
errors << tr("The server version %1 is unsupported! Proceed at your own risk.").arg(account->serverVersion());
|
||||
errors << tr("The server version %1 is old and unsupported! Proceed at your own risk.").arg(account->serverVersion());
|
||||
}
|
||||
showConnectionLabel(tr("Connected to %1.").arg(serverWithUser), errors);
|
||||
} else if (state == AccountState::ServiceUnavailable) {
|
||||
@@ -703,6 +695,9 @@ void AccountSettings::slotAccountStateChanged()
|
||||
if (ui->_folderList->isExpanded(_model->index(i)))
|
||||
ui->_folderList->setExpanded(_model->index(i), false);
|
||||
}
|
||||
} else if (_model->isDirty()) {
|
||||
// If we connect and have pending changes, show the list.
|
||||
doExpand();
|
||||
}
|
||||
|
||||
// Disabling expansion of folders might require hiding the selective
|
||||
@@ -761,6 +756,8 @@ AccountSettings::~AccountSettings()
|
||||
|
||||
void AccountSettings::refreshSelectiveSyncStatus()
|
||||
{
|
||||
bool shouldBeVisible = _model->isDirty() && _accountState->isConnected();
|
||||
|
||||
QString msg;
|
||||
int cnt = 0;
|
||||
foreach (Folder *folder, FolderMan::instance()->map().values()) {
|
||||
@@ -791,20 +788,10 @@ void AccountSettings::refreshSelectiveSyncStatus()
|
||||
}
|
||||
}
|
||||
|
||||
// Some selective sync ui (either normal editing or big folder) will show
|
||||
// if this variable ends up true.
|
||||
bool shouldBeVisible = false;
|
||||
|
||||
if (msg.isEmpty()) {
|
||||
// Show the ui if the model is dirty only
|
||||
shouldBeVisible = _model->isDirty() && _accountState->isConnected();
|
||||
|
||||
ui->selectiveSyncButtons->setVisible(true);
|
||||
ui->bigFolderUi->setVisible(false);
|
||||
} else {
|
||||
// There's a reason the big folder ui should be shown
|
||||
shouldBeVisible = _accountState->isConnected();
|
||||
|
||||
ConfigFile cfg;
|
||||
QString info = !cfg.confirmExternalStorage()
|
||||
? tr("There are folders that were not synchronized because they are too big: ")
|
||||
@@ -815,6 +802,7 @@ void AccountSettings::refreshSelectiveSyncStatus()
|
||||
ui->selectiveSyncNotification->setText(info + msg);
|
||||
ui->selectiveSyncButtons->setVisible(false);
|
||||
ui->bigFolderUi->setVisible(true);
|
||||
shouldBeVisible = true;
|
||||
}
|
||||
|
||||
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
|
||||
@@ -824,7 +812,6 @@ void AccountSettings::refreshSelectiveSyncStatus()
|
||||
if (shouldBeVisible) {
|
||||
ui->selectiveSyncStatus->setMaximumHeight(0);
|
||||
ui->selectiveSyncStatus->setVisible(true);
|
||||
doExpand();
|
||||
}
|
||||
auto anim = new QPropertyAnimation(ui->selectiveSyncStatus, "maximumHeight", ui->selectiveSyncStatus);
|
||||
anim->setEndValue(shouldBeVisible ? hint.height() : 0);
|
||||
|
||||
@@ -73,7 +73,7 @@ protected slots:
|
||||
void slotAddFolder();
|
||||
void slotEnableCurrentFolder();
|
||||
void slotScheduleCurrentFolder();
|
||||
void slotScheduleCurrentFolderForceFullDiscovery();
|
||||
void slotScheduleCurrentFolderForceRemoteDiscovery();
|
||||
void slotForceSyncCurrentFolder();
|
||||
void slotRemoveCurrentFolder();
|
||||
void slotOpenCurrentFolder(); // sync folder
|
||||
|
||||
@@ -314,10 +314,10 @@ void AccountState::slotInvalidCredentials()
|
||||
|
||||
if (account()->credentials()->ready()) {
|
||||
account()->credentials()->invalidateToken();
|
||||
}
|
||||
if (auto creds = qobject_cast<HttpCredentials *>(account()->credentials())) {
|
||||
if (creds->refreshAccessToken())
|
||||
return;
|
||||
if (auto creds = qobject_cast<HttpCredentials *>(account()->credentials())) {
|
||||
if (creds->refreshAccessToken())
|
||||
return;
|
||||
}
|
||||
}
|
||||
account()->credentials()->askFromUser();
|
||||
}
|
||||
|
||||
@@ -135,6 +135,11 @@ void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
const QString elidedAction = fm.elidedText(actionText, Qt::ElideRight, actionTextBox.width());
|
||||
painter->drawText(actionTextBox, elidedAction);
|
||||
|
||||
int atPos = accountRole.indexOf(QLatin1Char('@'));
|
||||
if (atPos > -1) {
|
||||
accountRole.remove(0, atPos + 1);
|
||||
}
|
||||
|
||||
QString timeStr;
|
||||
if (accountOnline) {
|
||||
timeStr = tr("%1 on %2").arg(timeText, accountRole);
|
||||
|
||||
@@ -84,11 +84,6 @@ ActivityWidget::ActivityWidget(QWidget *parent)
|
||||
connect(_model, &ActivityListModel::activityJobStatusCode,
|
||||
this, &ActivityWidget::slotAccountActivityStatus);
|
||||
|
||||
connect(AccountManager::instance(), &AccountManager::accountRemoved, this, [this](AccountState *ast) {
|
||||
if (_accountsWithoutActivities.remove(ast->account()->displayName()))
|
||||
showLabels();
|
||||
});
|
||||
|
||||
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
|
||||
_copyBtn->setToolTip(tr("Copy the activity list to the clipboard."));
|
||||
connect(_copyBtn, &QAbstractButton::clicked, this, &ActivityWidget::copyToClipboard);
|
||||
@@ -295,6 +290,11 @@ void ActivityWidget::slotBuildNotificationDisplay(const ActivityList &list)
|
||||
QString host = activity._accName;
|
||||
// store the name of the account that sends the notification to be
|
||||
// able to add it to the tray notification
|
||||
// remove the user name from the account as that is not accurate here.
|
||||
int indx = host.indexOf(QChar('@'));
|
||||
if (indx > -1) {
|
||||
host.remove(0, 1 + indx);
|
||||
}
|
||||
if (!host.isEmpty()) {
|
||||
if (accNotified.contains(host)) {
|
||||
accNotified[host] = accNotified[host] + 1;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace {
|
||||
" --logexpire <hours> : removes logs older than <hours> hours.\n"
|
||||
" (to be used with --logdir)\n"
|
||||
" --logflush : flush the log file after every write.\n"
|
||||
" --logdebug : also output debug-level messages in the log.\n"
|
||||
" --logdebug : also output debug-level messages in the log (equivalent to setting the env var QT_LOGGING_RULES=\"qt.*=true;*.debug=true\").\n"
|
||||
" --confdir <dirname> : Use the given configuration folder.\n";
|
||||
|
||||
QString applicationTrPath()
|
||||
@@ -186,8 +186,7 @@ Application::Application(int &argc, char **argv)
|
||||
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||
|
||||
auto confDir = ConfigFile().configPath();
|
||||
if (confDir.endsWith('/')) confDir.chop(1); // macOS 10.11.x does not like trailing slash for rename/move.
|
||||
if (!QFileInfo(confDir).isDir()) {
|
||||
if (!QFileInfo(confDir).exists()) {
|
||||
// Migrate from version <= 2.4
|
||||
setApplicationName(_theme->appNameGUI());
|
||||
#ifndef QT_WARNING_DISABLE_DEPRECATED // Was added in Qt 5.9
|
||||
@@ -198,7 +197,6 @@ Application::Application(int &argc, char **argv)
|
||||
// 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);
|
||||
if (oldDir.endsWith('/')) oldDir.chop(1); // macOS 10.11.x does not like trailing slash for rename/move.
|
||||
QT_WARNING_POP
|
||||
setApplicationName(_theme->appName());
|
||||
if (QFileInfo(oldDir).isDir()) {
|
||||
@@ -542,12 +540,20 @@ void Application::parseOptions(const QStringList &options)
|
||||
|
||||
// Helpers for displaying messages. Note that there is no console on Windows.
|
||||
#ifdef Q_OS_WIN
|
||||
static void displayHelpText(const QString &t) // No console on Windows.
|
||||
// Format as <pre> HTML
|
||||
static inline void toHtml(QString &t)
|
||||
{
|
||||
QString spaces(80, ' '); // Add a line of non-wrapped space to make the messagebox wide enough.
|
||||
QString text = QLatin1String("<qt><pre style='white-space:pre-wrap'>")
|
||||
+ t.toHtmlEscaped() + QLatin1String("</pre><pre>") + spaces + QLatin1String("</pre></qt>");
|
||||
QMessageBox::information(0, Theme::instance()->appNameGUI(), text);
|
||||
t.replace(QLatin1Char('&'), QLatin1String("&"));
|
||||
t.replace(QLatin1Char('<'), QLatin1String("<"));
|
||||
t.replace(QLatin1Char('>'), QLatin1String(">"));
|
||||
t.insert(0, QLatin1String("<html><pre>"));
|
||||
t.append(QLatin1String("</pre></html>"));
|
||||
}
|
||||
|
||||
static void displayHelpText(QString t) // No console on Windows.
|
||||
{
|
||||
toHtml(t);
|
||||
QMessageBox::information(0, Theme::instance()->appNameGUI(), t);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <QJsonDocument>
|
||||
#include "theme.h"
|
||||
#include "networkjobs.h"
|
||||
#include "creds/httpcredentials.h"
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -86,8 +85,6 @@ void OAuth::start()
|
||||
QString basicAuth = QString("%1:%2").arg(
|
||||
Theme::instance()->oauthClientId(), Theme::instance()->oauthClientSecret());
|
||||
req.setRawHeader("Authorization", "Basic " + basicAuth.toUtf8().toBase64());
|
||||
// We just added the Authorization header, don't let HttpCredentialsAccessManager tamper with it
|
||||
req.setAttribute(HttpCredentials::DontAddCredentialsAttribute, true);
|
||||
|
||||
auto requestBody = new QBuffer;
|
||||
QUrlQuery arguments(QString(
|
||||
@@ -153,6 +150,7 @@ void OAuth::start()
|
||||
});
|
||||
}
|
||||
});
|
||||
QTimer::singleShot(5 * 60 * 1000, this, [this] { result(Error); });
|
||||
}
|
||||
|
||||
QUrl OAuth::authorisationLink() const
|
||||
|
||||
@@ -297,9 +297,6 @@ public slots:
|
||||
*/
|
||||
void downloadVirtualFile(const QString &relativepath);
|
||||
|
||||
/** Ensures that the next sync performs a full local discovery. */
|
||||
void slotNextSyncFullLocalDiscovery();
|
||||
|
||||
private slots:
|
||||
void slotSyncStarted();
|
||||
void slotSyncFinished(bool);
|
||||
@@ -328,6 +325,9 @@ private slots:
|
||||
*/
|
||||
void slotScheduleThisFolder();
|
||||
|
||||
/** Ensures that the next sync performs a full local discovery. */
|
||||
void slotNextSyncFullLocalDiscovery();
|
||||
|
||||
/** Adjust sync result based on conflict data from IssuesWidget.
|
||||
*
|
||||
* This is pretty awkward, but IssuesWidget just keeps better track
|
||||
|
||||
@@ -1306,45 +1306,25 @@ QString FolderMan::trayTooltipStatusString(
|
||||
return folderMessage;
|
||||
}
|
||||
|
||||
static QString checkPathValidityRecursive(const QString &path)
|
||||
QString FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl, bool forNewDirectory) const
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return FolderMan::tr("No valid folder selected!");
|
||||
return tr("No valid folder selected!");
|
||||
}
|
||||
|
||||
QFileInfo selFile(path);
|
||||
|
||||
if (!selFile.exists()) {
|
||||
return checkPathValidityRecursive(selFile.dir().path());
|
||||
return checkPathValidityForNewFolder(selFile.dir().path(), serverUrl, true);
|
||||
}
|
||||
|
||||
if (!selFile.isDir()) {
|
||||
return FolderMan::tr("The selected path is not a folder!");
|
||||
return tr("The selected path is not a folder!");
|
||||
}
|
||||
|
||||
if (!selFile.isWritable()) {
|
||||
return FolderMan::tr("You have no permission to write to the selected folder!");
|
||||
return tr("You have no permission to write to the selected folder!");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
// QFileInfo::canonicalPath returns an empty string if the file does not exist.
|
||||
// This function also works with files that does not exist and resolve the symlinks in the
|
||||
// parent directories.
|
||||
static QString canonicalPath(const QString &path)
|
||||
{
|
||||
QFileInfo selFile(path);
|
||||
if (!selFile.exists()) {
|
||||
return canonicalPath(selFile.dir().path()) + '/' + selFile.fileName();
|
||||
}
|
||||
return selFile.canonicalFilePath();
|
||||
}
|
||||
|
||||
QString FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl) const
|
||||
{
|
||||
QString recursiveValidity = checkPathValidityRecursive(path);
|
||||
if (!recursiveValidity.isEmpty())
|
||||
return recursiveValidity;
|
||||
|
||||
// check if the local directory isn't used yet in another ownCloud sync
|
||||
Qt::CaseSensitivity cs = Qt::CaseSensitive;
|
||||
@@ -1352,28 +1332,57 @@ QString FolderMan::checkPathValidityForNewFolder(const QString &path, const QUrl
|
||||
cs = Qt::CaseInsensitive;
|
||||
}
|
||||
|
||||
const QString userDir = QDir::cleanPath(canonicalPath(path)) + '/';
|
||||
for (auto i = _folderMap.constBegin(); i != _folderMap.constEnd(); ++i) {
|
||||
Folder *f = static_cast<Folder *>(i.value());
|
||||
QString folderDir = QDir::cleanPath(canonicalPath(f->path())) + '/';
|
||||
QString folderDir = QDir(f->path()).canonicalPath();
|
||||
if (folderDir.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (!folderDir.endsWith(QLatin1Char('/'), cs))
|
||||
folderDir.append(QLatin1Char('/'));
|
||||
|
||||
bool differentPaths = QString::compare(folderDir, userDir, cs) != 0;
|
||||
if (differentPaths && folderDir.startsWith(userDir, cs)) {
|
||||
const QString folderDirClean = QDir::cleanPath(folderDir) + '/';
|
||||
const QString userDirClean = QDir::cleanPath(path) + '/';
|
||||
|
||||
// folderDir follows sym links, path not.
|
||||
bool differentPathes = !Utility::fileNamesEqual(QDir::cleanPath(folderDir), QDir::cleanPath(path));
|
||||
|
||||
if (!forNewDirectory && differentPathes && folderDirClean.startsWith(userDirClean, cs)) {
|
||||
return tr("The local folder %1 already contains a folder used in a folder sync connection. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
if (differentPaths && userDir.startsWith(folderDir, cs)) {
|
||||
// QDir::cleanPath keeps links
|
||||
// canonicalPath() remove symlinks and uses the symlink targets.
|
||||
QString absCleanUserFolder = QDir::cleanPath(QDir(path).canonicalPath()) + '/';
|
||||
|
||||
if ((forNewDirectory || differentPathes) && userDirClean.startsWith(folderDirClean, cs)) {
|
||||
return tr("The local folder %1 is already contained in a folder used in a folder sync connection. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
// both follow symlinks.
|
||||
bool cleanUserEqualsCleanFolder = Utility::fileNamesEqual(absCleanUserFolder, folderDirClean);
|
||||
if (differentPathes && absCleanUserFolder.startsWith(folderDirClean, cs) && !cleanUserEqualsCleanFolder) {
|
||||
return tr("The local folder %1 is a symbolic link. "
|
||||
"The link target is already contained in a folder used in a folder sync connection. "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
if (differentPathes && folderDirClean.startsWith(absCleanUserFolder, cs) && !cleanUserEqualsCleanFolder && !forNewDirectory) {
|
||||
return tr("The local folder %1 contains a symbolic link. "
|
||||
"The link target contains an already synced folder "
|
||||
"Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(path));
|
||||
}
|
||||
|
||||
// if both pathes are equal, the server url needs to be different
|
||||
// otherwise it would mean that a new connection from the same local folder
|
||||
// to the same account is added which is not wanted. The account must differ.
|
||||
if (serverUrl.isValid() && !differentPaths) {
|
||||
if (serverUrl.isValid() && Utility::fileNamesEqual(absCleanUserFolder, folderDir)) {
|
||||
QUrl folderUrl = f->accountState()->account()->url();
|
||||
QString user = f->accountState()->account()->credentials()->user();
|
||||
folderUrl.setUserName(user);
|
||||
|
||||
@@ -138,9 +138,11 @@ public:
|
||||
*
|
||||
* Note that different accounts are allowed to sync to the same folder.
|
||||
*
|
||||
* \a forNewDirectory is internal and is used for recursion.
|
||||
*
|
||||
* @returns an empty string if it is allowed, or an error if it is not allowed
|
||||
*/
|
||||
QString checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl = QUrl()) const;
|
||||
QString checkPathValidityForNewFolder(const QString &path, const QUrl &serverUrl = QUrl(), bool forNewDirectory = false) const;
|
||||
|
||||
/**
|
||||
* Attempts to find a non-existing, acceptable path for creating a new sync folder.
|
||||
|
||||
@@ -77,13 +77,16 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||
int h = rootFolderHeightWithoutErrors(fm, aliasFm);
|
||||
// this already includes the bottom margin
|
||||
|
||||
// add some space for the message boxes.
|
||||
// add some space to show an conflict indicator.
|
||||
int margin = fm.height() / 4;
|
||||
for (auto role : {FolderConflictMsg, FolderErrorMsg, FolderInfoMsg}) {
|
||||
auto msgs = qvariant_cast<QStringList>(index.data(role));
|
||||
if (!msgs.isEmpty()) {
|
||||
h += margin + 2 * margin + msgs.count() * fm.height();
|
||||
}
|
||||
if (!qvariant_cast<QStringList>(index.data(FolderConflictMsg)).isEmpty()) {
|
||||
QStringList msgs = qvariant_cast<QStringList>(index.data(FolderConflictMsg));
|
||||
h += margin + 2 * margin + msgs.count() * fm.height();
|
||||
}
|
||||
// add some space to show an error condition.
|
||||
if (!qvariant_cast<QStringList>(index.data(FolderErrorMsg)).isEmpty()) {
|
||||
QStringList errMsgs = qvariant_cast<QStringList>(index.data(FolderErrorMsg));
|
||||
h += margin + 2 * margin + errMsgs.count() * fm.height();
|
||||
}
|
||||
|
||||
return QSize(0, h);
|
||||
@@ -153,7 +156,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
QString remotePath = qvariant_cast<QString>(index.data(FolderSecondPathRole));
|
||||
QStringList conflictTexts = qvariant_cast<QStringList>(index.data(FolderConflictMsg));
|
||||
QStringList errorTexts = qvariant_cast<QStringList>(index.data(FolderErrorMsg));
|
||||
QStringList infoTexts = qvariant_cast<QStringList>(index.data(FolderInfoMsg));
|
||||
|
||||
int overallPercent = qvariant_cast<int>(index.data(SyncProgressOverallPercent));
|
||||
QString overallString = qvariant_cast<QString>(index.data(SyncProgressOverallString));
|
||||
@@ -288,8 +290,6 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
drawTextBox(conflictTexts, QColor(0xba, 0xba, 0x4d));
|
||||
if (!errorTexts.isEmpty())
|
||||
drawTextBox(errorTexts, QColor(0xbb, 0x4d, 0x4d));
|
||||
if (!infoTexts.isEmpty())
|
||||
drawTextBox(infoTexts, QColor(0x4d, 0xba, 0x4d));
|
||||
|
||||
// Sync File Progress Bar: Show it if syncFile is not empty.
|
||||
if (showProgess) {
|
||||
@@ -375,22 +375,13 @@ QRect FolderStatusDelegate::optionsButtonRect(QRect within, Qt::LayoutDirection
|
||||
return QStyle::visualRect(direction, within, r);
|
||||
}
|
||||
|
||||
QRect FolderStatusDelegate::errorsListRect(QRect within, const QModelIndex &index)
|
||||
QRect FolderStatusDelegate::errorsListRect(QRect within)
|
||||
{
|
||||
QFont font = QFont();
|
||||
QFont aliasFont = makeAliasFont(font);
|
||||
QFontMetrics fm(font);
|
||||
QFontMetrics aliasFm(aliasFont);
|
||||
within.setTop(within.top() + FolderStatusDelegate::rootFolderHeightWithoutErrors(fm, aliasFm));
|
||||
int margin = fm.height() / 4;
|
||||
int h = 0;
|
||||
for (auto role : {FolderConflictMsg, FolderErrorMsg}) {
|
||||
auto msgs = qvariant_cast<QStringList>(index.data(role));
|
||||
if (!msgs.isEmpty()) {
|
||||
h += margin + 2 * margin + msgs.count() * fm.height();
|
||||
}
|
||||
}
|
||||
within.setHeight(h);
|
||||
return within;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ public:
|
||||
FolderSecondPathRole,
|
||||
FolderConflictMsg,
|
||||
FolderErrorMsg,
|
||||
FolderInfoMsg,
|
||||
FolderSyncPaused,
|
||||
FolderStatusIconRole,
|
||||
FolderAccountConnected,
|
||||
@@ -58,7 +57,7 @@ public:
|
||||
* return the position of the option button within the item
|
||||
*/
|
||||
static QRect optionsButtonRect(QRect within, Qt::LayoutDirection direction);
|
||||
static QRect errorsListRect(QRect within, const QModelIndex &);
|
||||
static QRect errorsListRect(QRect within);
|
||||
static int rootFolderHeightWithoutErrors(const QFontMetrics &fm, const QFontMetrics &aliasFm);
|
||||
|
||||
private:
|
||||
|
||||
@@ -210,10 +210,6 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
|
||||
: QStringList();
|
||||
case FolderStatusDelegate::FolderErrorMsg:
|
||||
return f->syncResult().errorStrings();
|
||||
case FolderStatusDelegate::FolderInfoMsg:
|
||||
return f->useVirtualFiles()
|
||||
? QStringList(tr("New files are being created as virtual files."))
|
||||
: QStringList();
|
||||
case FolderStatusDelegate::SyncRunning:
|
||||
return f->syncResult().status() == SyncResult::SyncRunning;
|
||||
case FolderStatusDelegate::HeaderRole:
|
||||
@@ -362,8 +358,6 @@ int FolderStatusModel::rowCount(const QModelIndex &parent) const
|
||||
auto info = infoForIndex(parent);
|
||||
if (!info)
|
||||
return 0;
|
||||
if (info->_folder && info->_folder->useVirtualFiles())
|
||||
return 0;
|
||||
if (info->hasLabel())
|
||||
return 1;
|
||||
return info->_subs.count();
|
||||
@@ -519,9 +513,6 @@ bool FolderStatusModel::hasChildren(const QModelIndex &parent) const
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
if (info->_folder && info->_folder->useVirtualFiles())
|
||||
return false;
|
||||
|
||||
if (!info->_fetched)
|
||||
return true;
|
||||
|
||||
@@ -547,10 +538,6 @@ bool FolderStatusModel::canFetchMore(const QModelIndex &parent) const
|
||||
// Keep showing the error to the user, it will be hidden when the account reconnects
|
||||
return false;
|
||||
}
|
||||
if (info->_folder && info->_folder->useVirtualFiles()) {
|
||||
// Selective sync is hidden in that case
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -753,11 +740,10 @@ void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply *r)
|
||||
if (parentInfo) {
|
||||
qCDebug(lcFolderStatus) << r->errorString();
|
||||
parentInfo->_lastErrorString = r->errorString();
|
||||
auto error = r->error();
|
||||
|
||||
parentInfo->resetSubs(this, idx);
|
||||
|
||||
if (error == QNetworkReply::ContentNotFoundError) {
|
||||
if (r->error() == QNetworkReply::ContentNotFoundError) {
|
||||
parentInfo->_fetched = true;
|
||||
} else {
|
||||
ASSERT(!parentInfo->hasLabel());
|
||||
@@ -823,7 +809,7 @@ void FolderStatusModel::slotApplySelectiveSync()
|
||||
auto oldBlackList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok);
|
||||
if (!ok) {
|
||||
qCWarning(lcFolderStatus) << "Could not read selective sync list from db.";
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
QStringList blackList = createBlackList(&_folders[i], oldBlackList);
|
||||
folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
|
||||
@@ -856,8 +842,6 @@ void FolderStatusModel::slotApplySelectiveSync()
|
||||
foreach (const auto &it, changes) {
|
||||
folder->journalDb()->avoidReadFromDbOnNextSync(it);
|
||||
}
|
||||
// Also make sure we see the local file that had been ignored before
|
||||
folder->slotNextSyncFullLocalDiscovery();
|
||||
FolderMan::instance()->scheduleFolder(folder);
|
||||
}
|
||||
}
|
||||
@@ -1171,8 +1155,6 @@ void FolderStatusModel::slotSyncAllPendingBigFolders()
|
||||
foreach (const auto &it, undecidedList) {
|
||||
folder->journalDb()->avoidReadFromDbOnNextSync(it);
|
||||
}
|
||||
// Also make sure we see the local file that had been ignored before
|
||||
folder->slotNextSyncFullLocalDiscovery();
|
||||
FolderMan::instance()->scheduleFolder(folder);
|
||||
}
|
||||
|
||||
@@ -1244,11 +1226,7 @@ bool FolderStatusModel::SubFolderInfo::hasLabel() const
|
||||
void FolderStatusModel::SubFolderInfo::resetSubs(FolderStatusModel *model, QModelIndex index)
|
||||
{
|
||||
_fetched = false;
|
||||
if (_fetchingJob) {
|
||||
disconnect(_fetchingJob, nullptr, model, nullptr);
|
||||
_fetchingJob->deleteLater();
|
||||
_fetchingJob.clear();
|
||||
}
|
||||
delete _fetchingJob;
|
||||
if (hasLabel()) {
|
||||
model->beginRemoveRows(index, 0, 0);
|
||||
_fetchingLabel = false;
|
||||
|
||||
@@ -67,10 +67,8 @@ FolderWizardLocalPath::FolderWizardLocalPath(const AccountPtr &account)
|
||||
connect(_ui.localFolderChooseBtn, &QAbstractButton::clicked, this, &FolderWizardLocalPath::slotChooseLocalFolder);
|
||||
_ui.localFolderChooseBtn->setToolTip(tr("Click to select a local folder to sync."));
|
||||
|
||||
QUrl serverUrl = _account->url();
|
||||
serverUrl.setUserName(_account->credentials()->user());
|
||||
QString defaultPath = QDir::homePath() + QLatin1Char('/') + Theme::instance()->appName();
|
||||
defaultPath = FolderMan::instance()->findGoodPathForNewSyncFolder(defaultPath, serverUrl);
|
||||
defaultPath = FolderMan::instance()->findGoodPathForNewSyncFolder(defaultPath, account->url());
|
||||
_ui.localFolderLineEdit->setText(QDir::toNativeSeparators(defaultPath));
|
||||
_ui.localFolderLineEdit->setToolTip(tr("Enter the path to the local folder."));
|
||||
|
||||
@@ -485,12 +483,9 @@ FolderWizardSelectiveSync::FolderWizardSelectiveSync(const AccountPtr &account)
|
||||
_selectiveSync = new SelectiveSyncWidget(account, this);
|
||||
layout->addWidget(_selectiveSync);
|
||||
|
||||
if (Theme::instance()->showVirtualFilesOption()) {
|
||||
if (ConfigFile().showExperimentalOptions()) {
|
||||
_virtualFilesCheckBox = new QCheckBox(tr("Use virtual files instead of downloading content immediately (experimental)"));
|
||||
connect(_virtualFilesCheckBox, &QCheckBox::clicked, this, &FolderWizardSelectiveSync::virtualFilesCheckboxClicked);
|
||||
connect(_virtualFilesCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
_selectiveSync->setEnabled(state == Qt::Unchecked);
|
||||
});
|
||||
layout->addWidget(_virtualFilesCheckBox);
|
||||
}
|
||||
}
|
||||
@@ -519,9 +514,8 @@ void FolderWizardSelectiveSync::initializePage()
|
||||
|
||||
bool FolderWizardSelectiveSync::validatePage()
|
||||
{
|
||||
bool useVirtualFiles = _virtualFilesCheckBox && _virtualFilesCheckBox->isChecked();
|
||||
wizard()->setProperty("selectiveSyncBlackList", useVirtualFiles ? QVariant() : QVariant(_selectiveSync->createBlackList()));
|
||||
wizard()->setProperty("useVirtualFiles", QVariant(useVirtualFiles));
|
||||
wizard()->setProperty("selectiveSyncBlackList", QVariant(_selectiveSync->createBlackList()));
|
||||
wizard()->setProperty("useVirtualFiles", QVariant(_virtualFilesCheckBox && _virtualFilesCheckBox->isChecked()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,17 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
||||
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
|
||||
connect(_ui->autostartCheckBox, &QAbstractButton::toggled, this, &GeneralSettings::slotToggleLaunchOnStartup);
|
||||
|
||||
// setup about section
|
||||
QString about = Theme::instance()->about();
|
||||
if (about.isEmpty()) {
|
||||
_ui->aboutGroupBox->hide();
|
||||
} else {
|
||||
_ui->aboutLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction);
|
||||
_ui->aboutLabel->setText("<qt><style> p{padding-bottom:0; margin-bottom:0;padding-top:2; margin-top: 2; font-size:smaller;};</style>"+about+"</qt>");
|
||||
_ui->aboutLabel->setWordWrap(true);
|
||||
_ui->aboutLabel->setOpenExternalLinks(true);
|
||||
}
|
||||
|
||||
loadMiscSettings();
|
||||
slotUpdateInfo();
|
||||
|
||||
@@ -90,18 +101,6 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
||||
|
||||
// accountAdded means the wizard was finished and the wizard might change some options.
|
||||
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
|
||||
|
||||
// Only our standard brandings currently support beta channel
|
||||
Theme *theme = Theme::instance();
|
||||
if (theme->appName() != QLatin1String("ownCloud") && theme->appName() != QLatin1String("testpilotcloud") ) {
|
||||
#ifdef Q_OS_MAC
|
||||
// Because we don't have any statusString from the SparkleUpdater anyway we can hide the whole thing
|
||||
_ui->updatesGroupBox->hide();
|
||||
#else
|
||||
_ui->updateChannelLabel->hide();
|
||||
_ui->updateChannel->hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
GeneralSettings::~GeneralSettings()
|
||||
@@ -149,7 +148,7 @@ void GeneralSettings::slotUpdateInfo()
|
||||
_ui->restartButton->setVisible(ocupdater->downloadState() == OCUpdater::DownloadComplete);
|
||||
|
||||
}
|
||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||
#ifdef Q_OS_MAC
|
||||
else if (SparkleUpdater *sparkleUpdater = qobject_cast<SparkleUpdater *>(Updater::instance())) {
|
||||
_ui->updateStateLabel->setText(sparkleUpdater->statusString());
|
||||
_ui->restartButton->setVisible(false);
|
||||
@@ -158,13 +157,12 @@ void GeneralSettings::slotUpdateInfo()
|
||||
|
||||
// Channel selection
|
||||
_ui->updateChannel->setCurrentIndex(ConfigFile().updateChannel() == "beta" ? 1 : 0);
|
||||
connect(_ui->updateChannel, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
connect(_ui->updateChannel, &QComboBox::currentTextChanged,
|
||||
this, &GeneralSettings::slotUpdateChannelChanged, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void GeneralSettings::slotUpdateChannelChanged(int index)
|
||||
void GeneralSettings::slotUpdateChannelChanged(const QString &channel)
|
||||
{
|
||||
QString channel = index == 0 ? QStringLiteral("stable") : QStringLiteral("beta");
|
||||
if (channel == ConfigFile().updateChannel())
|
||||
return;
|
||||
|
||||
@@ -194,7 +192,7 @@ void GeneralSettings::slotUpdateChannelChanged(int index)
|
||||
updater->setUpdateUrl(Updater::updateUrl());
|
||||
updater->checkForUpdate();
|
||||
}
|
||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||
#ifdef Q_OS_MAC
|
||||
else if (SparkleUpdater *updater = qobject_cast<SparkleUpdater *>(Updater::instance())) {
|
||||
updater->setUpdateUrl(Updater::updateUrl());
|
||||
updater->checkForUpdate();
|
||||
|
||||
@@ -45,7 +45,7 @@ private slots:
|
||||
void slotToggleOptionalDesktopNotifications(bool);
|
||||
void slotShowInExplorerNavigationPane(bool);
|
||||
void slotUpdateInfo();
|
||||
void slotUpdateChannelChanged(int index);
|
||||
void slotUpdateChannelChanged(const QString &channel);
|
||||
void slotIgnoreFilesEditor();
|
||||
void loadMiscSettings();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>785</width>
|
||||
<height>390</height>
|
||||
<height>533</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -48,6 +48,28 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="aboutGroupBox">
|
||||
<property name="title">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="aboutLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QGroupBox" name="updatesGroupBox">
|
||||
<property name="title">
|
||||
<string>Updates</string>
|
||||
@@ -138,7 +160,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
|
||||
@@ -140,7 +140,6 @@ void IgnoreListEditor::slotUpdateLocalIgnoreList()
|
||||
// ignored (because the remote etag did not change) (issue #3172)
|
||||
foreach (Folder *folder, folderMan->map()) {
|
||||
folder->journalDb()->forceRemoteDiscoveryNextSync();
|
||||
folder->slotNextSyncFullLocalDiscovery();
|
||||
folderMan->scheduleFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,10 +190,8 @@ void IssuesWidget::addItem(QTreeWidgetItem *item)
|
||||
return;
|
||||
|
||||
int count = _ui->_treeWidget->topLevelItemCount();
|
||||
if (count >= maxIssueCount) {
|
||||
delete item;
|
||||
if (count >= maxIssueCount)
|
||||
return;
|
||||
}
|
||||
|
||||
_ui->_treeWidget->setSortingEnabled(false);
|
||||
_reenableSorting.start();
|
||||
|
||||
@@ -25,7 +25,6 @@ OcsShareJob::OcsShareJob(AccountPtr account)
|
||||
: OcsJob(account)
|
||||
{
|
||||
setPath("ocs/v1.php/apps/files_sharing/api/v1/shares");
|
||||
addParam(QString::fromLatin1("reshares"), QString::fromLatin1("true"));
|
||||
connect(this, &OcsJob::jobFinished, this, &OcsShareJob::jobDone);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/vnd.@APPLICATION_EXECUTABLE@">
|
||||
<mime-type type="application/x-@APPLICATION_EXECUTABLE@">
|
||||
<comment>@APPLICATION_NAME@ virtual files</comment>
|
||||
<glob pattern="*.@APPLICATION_VIRTUALFILE_SUFFIX@"/>
|
||||
</mime-type>
|
||||
|
||||
@@ -38,8 +38,6 @@
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QDialog>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#if defined(Q_OS_X11)
|
||||
#include <QX11Info>
|
||||
@@ -52,13 +50,18 @@ const char propertyAccountC[] = "oc_account";
|
||||
ownCloudGui::ownCloudGui(Application *parent)
|
||||
: QObject(parent)
|
||||
, _tray(0)
|
||||
,
|
||||
#if defined(Q_OS_MAC)
|
||||
, _settingsDialog(new SettingsDialogMac(this))
|
||||
_settingsDialog(new SettingsDialogMac(this))
|
||||
,
|
||||
#else
|
||||
, _settingsDialog(new SettingsDialog(this))
|
||||
_settingsDialog(new SettingsDialog(this))
|
||||
,
|
||||
#endif
|
||||
, _logBrowser(0)
|
||||
_logBrowser(0)
|
||||
, _contextMenuVisibleOsx(false)
|
||||
, _recentActionsMenu(0)
|
||||
, _qdbusmenuWorkaround(false)
|
||||
, _app(parent)
|
||||
{
|
||||
_tray = new Systray();
|
||||
@@ -114,7 +117,7 @@ void ownCloudGui::slotOpenSettingsDialog()
|
||||
|
||||
void ownCloudGui::slotTrayClicked(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
if (_workaroundFakeDoubleClick) {
|
||||
if (_qdbusmenuWorkaround) {
|
||||
static QElapsedTimer last_click;
|
||||
if (last_click.isValid() && last_click.elapsed() < 200) {
|
||||
return;
|
||||
@@ -196,7 +199,7 @@ void ownCloudGui::slotTrayMessageIfServerUnsupported(Account *account)
|
||||
if (account->serverVersionUnsupported()) {
|
||||
slotShowTrayMessage(
|
||||
tr("Unsupported Server Version"),
|
||||
tr("The server on account %1 runs an unsupported version %2. "
|
||||
tr("The server on account %1 runs an old and unsupported version %2. "
|
||||
"Using this client with unsupported server versions is untested and "
|
||||
"potentially dangerous. Proceed at your own risk.")
|
||||
.arg(account->displayName(), account->serverVersion()));
|
||||
@@ -399,19 +402,17 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
|
||||
|
||||
void ownCloudGui::slotContextMenuAboutToShow()
|
||||
{
|
||||
_contextMenuVisibleManual = true;
|
||||
// For some reason on OS X _contextMenu->isVisible returns always false
|
||||
_contextMenuVisibleOsx = true;
|
||||
|
||||
// Update icon in sys tray, as it might change depending on the context menu state
|
||||
slotComputeOverallSyncStatus();
|
||||
|
||||
if (!_workaroundNoAboutToShowUpdate) {
|
||||
updateContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotContextMenuAboutToHide()
|
||||
{
|
||||
_contextMenuVisibleManual = false;
|
||||
// For some reason on OS X _contextMenu->isVisible returns always false
|
||||
_contextMenuVisibleOsx = false;
|
||||
|
||||
// Update icon in sys tray, as it might change depending on the context menu state
|
||||
slotComputeOverallSyncStatus();
|
||||
@@ -419,11 +420,11 @@ void ownCloudGui::slotContextMenuAboutToHide()
|
||||
|
||||
bool ownCloudGui::contextMenuVisible() const
|
||||
{
|
||||
// On some platforms isVisible doesn't work and always returns false,
|
||||
// elsewhere aboutToHide is unreliable.
|
||||
if (_workaroundManualVisibility)
|
||||
return _contextMenuVisibleManual;
|
||||
#ifdef Q_OS_MAC
|
||||
return _contextMenuVisibleOsx;
|
||||
#else
|
||||
return _contextMenu->isVisible();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool minimalTrayMenu()
|
||||
@@ -446,36 +447,12 @@ static bool updateWhileVisible()
|
||||
}
|
||||
}
|
||||
|
||||
static QByteArray envForceQDBusTrayWorkaround()
|
||||
static QByteArray forceQDBusTrayWorkaround()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_QDBUS_TRAY_WORKAROUND");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundShowAndHideTray()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_SHOW_HIDE");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundNoAboutToShowUpdate()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_NO_ABOUT_TO_SHOW");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundFakeDoubleClick()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_FAKE_DOUBLE_CLICK");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundManualVisibility()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_MANUAL_VISIBILITY");
|
||||
return var;
|
||||
}
|
||||
|
||||
void ownCloudGui::setupContextMenu()
|
||||
{
|
||||
if (_contextMenu) {
|
||||
@@ -494,73 +471,55 @@ void ownCloudGui::setupContextMenu()
|
||||
// The tray menu is surprisingly problematic. Being able to switch to
|
||||
// a minimal version of it is a useful workaround and testing tool.
|
||||
if (minimalTrayMenu()) {
|
||||
if (! Theme::instance()->about().isEmpty()) {
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionAbout);
|
||||
}
|
||||
_contextMenu->addAction(_actionQuit);
|
||||
return;
|
||||
}
|
||||
|
||||
auto applyEnvVariable = [](bool *sw, const QByteArray &value) {
|
||||
if (value == "1")
|
||||
*sw = true;
|
||||
if (value == "0")
|
||||
*sw = false;
|
||||
};
|
||||
|
||||
// This is an old compound flag that people might still depend on
|
||||
bool qdbusmenuWorkarounds = false;
|
||||
applyEnvVariable(&qdbusmenuWorkarounds, envForceQDBusTrayWorkaround());
|
||||
if (qdbusmenuWorkarounds) {
|
||||
_workaroundFakeDoubleClick = true;
|
||||
_workaroundNoAboutToShowUpdate = true;
|
||||
_workaroundShowAndHideTray = true;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// https://bugreports.qt.io/browse/QTBUG-54633
|
||||
_workaroundNoAboutToShowUpdate = true;
|
||||
_workaroundManualVisibility = true;
|
||||
#endif
|
||||
|
||||
// Enables workarounds for bugs introduced in Qt 5.5.0
|
||||
// In particular QTBUG-47863 #3672 (tray menu fails to update and
|
||||
// becomes unresponsive) and QTBUG-48068 #3722 (click signal is
|
||||
// emitted several times)
|
||||
// The Qt version check intentionally uses 5.0.0 (where platformMenu()
|
||||
// was introduced) instead of 5.5.0 to avoid issues where the Qt
|
||||
// version used to build is different from the one used at runtime.
|
||||
// If we build with 5.6.1 or newer, we can skip this because the
|
||||
// bugs should be fixed there.
|
||||
#ifdef Q_OS_LINUX
|
||||
// For KDE sessions if the platform plugin is missing,
|
||||
// neither aboutToShow() updates nor the isVisible() call
|
||||
// work. At least aboutToHide is reliable.
|
||||
// https://github.com/owncloud/client/issues/6545
|
||||
static QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
|
||||
static QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
|
||||
bool isKde =
|
||||
xdgCurrentDesktop.contains("KDE")
|
||||
|| desktopSession.contains("plasma")
|
||||
|| desktopSession.contains("kde");
|
||||
QObject *platformMenu = reinterpret_cast<QObject *>(_tray->contextMenu()->platformMenu());
|
||||
if (isKde && platformMenu && platformMenu->metaObject()->className() == QLatin1String("QDBusPlatformMenu")) {
|
||||
_workaroundManualVisibility = true;
|
||||
_workaroundNoAboutToShowUpdate = true;
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
|
||||
if (qVersion() == QByteArray("5.5.0")) {
|
||||
QObject *platformMenu = reinterpret_cast<QObject *>(_tray->contextMenu()->platformMenu());
|
||||
if (platformMenu
|
||||
&& platformMenu->metaObject()->className() == QLatin1String("QDBusPlatformMenu")) {
|
||||
_qdbusmenuWorkaround = true;
|
||||
qCWarning(lcApplication) << "Enabled QDBusPlatformMenu workaround";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
applyEnvVariable(&_workaroundNoAboutToShowUpdate, envForceWorkaroundNoAboutToShowUpdate());
|
||||
applyEnvVariable(&_workaroundFakeDoubleClick, envForceWorkaroundFakeDoubleClick());
|
||||
applyEnvVariable(&_workaroundShowAndHideTray, envForceWorkaroundShowAndHideTray());
|
||||
applyEnvVariable(&_workaroundManualVisibility, envForceWorkaroundManualVisibility());
|
||||
if (forceQDBusTrayWorkaround() == "1") {
|
||||
_qdbusmenuWorkaround = true;
|
||||
} else if (forceQDBusTrayWorkaround() == "0") {
|
||||
_qdbusmenuWorkaround = false;
|
||||
}
|
||||
|
||||
qCInfo(lcApplication) << "Tray menu workarounds:"
|
||||
<< "noabouttoshow:" << _workaroundNoAboutToShowUpdate
|
||||
<< "fakedoubleclick:" << _workaroundFakeDoubleClick
|
||||
<< "showhide:" << _workaroundShowAndHideTray
|
||||
<< "manualvisibility:" << _workaroundManualVisibility;
|
||||
|
||||
|
||||
connect(&_delayedTrayUpdateTimer, &QTimer::timeout, this, &ownCloudGui::updateContextMenu);
|
||||
_delayedTrayUpdateTimer.setInterval(2 * 1000);
|
||||
_delayedTrayUpdateTimer.setSingleShot(true);
|
||||
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToShow()), SLOT(slotContextMenuAboutToShow()));
|
||||
// unfortunately aboutToHide is unreliable, it seems to work on OSX though
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToHide()), SLOT(slotContextMenuAboutToHide()));
|
||||
// When the qdbusmenuWorkaround is necessary, we can't do on-demand updates
|
||||
// because the workaround is to hide and show the tray icon.
|
||||
if (_qdbusmenuWorkaround) {
|
||||
connect(&_workaroundBatchTrayUpdate, &QTimer::timeout, this, &ownCloudGui::updateContextMenu);
|
||||
_workaroundBatchTrayUpdate.setInterval(30 * 1000);
|
||||
_workaroundBatchTrayUpdate.setSingleShot(true);
|
||||
} else {
|
||||
// Update the context menu whenever we're about to show it
|
||||
// to the user.
|
||||
#ifdef Q_OS_MAC
|
||||
// https://bugreports.qt.io/browse/QTBUG-54633
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToShow()), SLOT(slotContextMenuAboutToShow()));
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToHide()), SLOT(slotContextMenuAboutToHide()));
|
||||
#else
|
||||
connect(_contextMenu.data(), &QMenu::aboutToShow, this, &ownCloudGui::updateContextMenu);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Populate the context menu now.
|
||||
updateContextMenu();
|
||||
@@ -572,21 +531,13 @@ void ownCloudGui::updateContextMenu()
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's visible, we can't update live, and it won't be updated lazily: reschedule
|
||||
if (contextMenuVisible() && !updateWhileVisible() && _workaroundNoAboutToShowUpdate) {
|
||||
if (!_delayedTrayUpdateTimer.isActive()) {
|
||||
_delayedTrayUpdateTimer.start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_workaroundShowAndHideTray) {
|
||||
if (_qdbusmenuWorkaround) {
|
||||
// To make tray menu updates work with these bugs (see setupContextMenu)
|
||||
// we need to hide and show the tray icon. We don't want to do that
|
||||
// while it's visible!
|
||||
if (contextMenuVisible()) {
|
||||
if (!_delayedTrayUpdateTimer.isActive()) {
|
||||
_delayedTrayUpdateTimer.start();
|
||||
if (!_workaroundBatchTrayUpdate.isActive()) {
|
||||
_workaroundBatchTrayUpdate.start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -699,38 +650,37 @@ void ownCloudGui::updateContextMenu()
|
||||
}
|
||||
_contextMenu->addAction(_actionLogin);
|
||||
}
|
||||
|
||||
if (! Theme::instance()->about().isEmpty()) {
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionAbout);
|
||||
}
|
||||
|
||||
_contextMenu->addAction(_actionQuit);
|
||||
|
||||
if (_workaroundShowAndHideTray) {
|
||||
if (_qdbusmenuWorkaround) {
|
||||
_tray->show();
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::updateContextMenuNeeded()
|
||||
{
|
||||
// if it's visible and we can update live: update now
|
||||
if (contextMenuVisible() && updateWhileVisible()) {
|
||||
// Note: don't update while visible on OSX
|
||||
// https://bugreports.qt.io/browse/QTBUG-54845
|
||||
updateContextMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
// if we can't lazily update: update later
|
||||
if (_workaroundNoAboutToShowUpdate) {
|
||||
// Note: don't update immediately even in the invisible case
|
||||
// as that can lead to extremely frequent menu updates
|
||||
if (!_delayedTrayUpdateTimer.isActive()) {
|
||||
_delayedTrayUpdateTimer.start();
|
||||
// For the workaround case updating while visible is impossible. Instead
|
||||
// occasionally update the menu when it's invisible.
|
||||
if (_qdbusmenuWorkaround) {
|
||||
if (!_workaroundBatchTrayUpdate.isActive()) {
|
||||
_workaroundBatchTrayUpdate.start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// https://bugreports.qt.io/browse/QTBUG-54845
|
||||
// We cannot update on demand or while visible -> update when invisible.
|
||||
if (!contextMenuVisible()) {
|
||||
updateContextMenu();
|
||||
}
|
||||
#else
|
||||
if (updateWhileVisible() && contextMenuVisible())
|
||||
updateContextMenu();
|
||||
#endif
|
||||
|
||||
// If no update was done here, we might update it on-demand due to
|
||||
// the aboutToShow() signal.
|
||||
}
|
||||
|
||||
void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg)
|
||||
@@ -787,8 +737,6 @@ void ownCloudGui::setupActions()
|
||||
QObject::connect(_actionNewAccountWizard, &QAction::triggered, this, &ownCloudGui::slotNewAccountWizard);
|
||||
_actionHelp = new QAction(tr("Help"), this);
|
||||
QObject::connect(_actionHelp, &QAction::triggered, this, &ownCloudGui::slotHelp);
|
||||
_actionAbout = new QAction(tr("About %1").arg(Theme::instance()->appNameGUI()), this);
|
||||
QObject::connect(_actionAbout, &QAction::triggered, this, &ownCloudGui::slotAbout);
|
||||
_actionQuit = new QAction(tr("Quit %1").arg(Theme::instance()->appNameGUI()), this);
|
||||
QObject::connect(_actionQuit, SIGNAL(triggered(bool)), _app, SLOT(quit()));
|
||||
|
||||
@@ -1125,7 +1073,7 @@ void ownCloudGui::slotShowShareDialog(const QString &sharePath, const QString &l
|
||||
w = _shareDialogs[localPath];
|
||||
} else {
|
||||
qCInfo(lcApplication) << "Opening share dialog" << sharePath << localPath << maxSharingPermissions;
|
||||
w = new ShareDialog(accountState, sharePath, localPath, maxSharingPermissions, fileRecord.legacyDeriveNumericFileId(), startPage);
|
||||
w = new ShareDialog(accountState, sharePath, localPath, maxSharingPermissions, fileRecord.numericFileId(), startPage);
|
||||
w->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
|
||||
_shareDialogs[localPath] = w;
|
||||
@@ -1145,28 +1093,5 @@ void ownCloudGui::slotRemoveDestroyedShareDialogs()
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotAbout()
|
||||
{
|
||||
QString title = tr("About %1").arg(Theme::instance()->appNameGUI());
|
||||
QString about = Theme::instance()->about();
|
||||
QMessageBox *msgBox = new QMessageBox(this->_settingsDialog);
|
||||
#ifdef Q_OS_MAC
|
||||
// From Qt doc: "On macOS, the window title is ignored (as required by the macOS Guidelines)."
|
||||
msgBox->setText(title);
|
||||
#else
|
||||
msgBox->setWindowTitle(title);
|
||||
#endif
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
msgBox->setTextFormat(Qt::RichText);
|
||||
msgBox->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
||||
msgBox->setInformativeText("<qt>"+about+"</qt>");
|
||||
msgBox->setStandardButtons(QMessageBox::Ok);
|
||||
QIcon appIcon = Theme::instance()->applicationIcon();
|
||||
// Assume icon is always small enough to fit an about dialog?
|
||||
qDebug() << appIcon.availableSizes().last();
|
||||
msgBox->setIconPixmap(appIcon.pixmap(appIcon.availableSizes().last()));
|
||||
msgBox->show();
|
||||
}
|
||||
|
||||
|
||||
} // end namespace
|
||||
|
||||
@@ -108,7 +108,6 @@ private slots:
|
||||
void slotUnpauseAllFolders();
|
||||
void slotPauseAllFolders();
|
||||
void slotNewAccountWizard();
|
||||
void slotAbout();
|
||||
|
||||
private:
|
||||
void setPauseOnAllFoldersHelper(bool pause);
|
||||
@@ -125,19 +124,14 @@ private:
|
||||
// tray's menu
|
||||
QScopedPointer<QMenu> _contextMenu;
|
||||
|
||||
// Manually tracking whether the context menu is visible via aboutToShow
|
||||
// and aboutToHide. Unfortunately aboutToHide isn't reliable everywhere
|
||||
// so this only gets used with _workaroundManualVisibility (when the tray's
|
||||
// isVisible() is unreliable)
|
||||
bool _contextMenuVisibleManual = false;
|
||||
// Manually tracking whether the context menu is visible, but only works
|
||||
// on OSX because aboutToHide is not reliable everywhere.
|
||||
bool _contextMenuVisibleOsx;
|
||||
|
||||
QMenu *_recentActionsMenu;
|
||||
QVector<QMenu *> _accountMenus;
|
||||
bool _workaroundShowAndHideTray = false;
|
||||
bool _workaroundNoAboutToShowUpdate = false;
|
||||
bool _workaroundFakeDoubleClick = false;
|
||||
bool _workaroundManualVisibility = false;
|
||||
QTimer _delayedTrayUpdateTimer;
|
||||
bool _qdbusmenuWorkaround;
|
||||
QTimer _workaroundBatchTrayUpdate;
|
||||
QMap<QString, QPointer<ShareDialog>> _shareDialogs;
|
||||
|
||||
QAction *_actionLogin;
|
||||
@@ -149,7 +143,6 @@ private:
|
||||
QAction *_actionEstimate;
|
||||
QAction *_actionRecent;
|
||||
QAction *_actionHelp;
|
||||
QAction *_actionAbout;
|
||||
QAction *_actionQuit;
|
||||
QAction *_actionCrash;
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ void ProtocolItem::openContextMenu(QPoint globalPos, QTreeWidgetItem *item, QWid
|
||||
// "Open in Browser" action
|
||||
auto openInBrowser = menu->addAction(ProtocolWidget::tr("Open in browser"));
|
||||
QObject::connect(openInBrowser, &QAction::triggered, parent, [parent, account, rec]() {
|
||||
fetchPrivateLinkUrl(account, rec._path, rec.legacyDeriveNumericFileId(), parent,
|
||||
fetchPrivateLinkUrl(account, rec._path, rec.numericFileId(), parent,
|
||||
[parent](const QString &url) {
|
||||
Utility::openBrowser(url, parent);
|
||||
});
|
||||
|
||||
@@ -492,8 +492,6 @@ void SelectiveSyncDialog::accept()
|
||||
foreach (const auto &it, changes) {
|
||||
_folder->journalDb()->avoidReadFromDbOnNextSync(it);
|
||||
}
|
||||
// Also make sure we see the local file that had been ignored before
|
||||
_folder->slotNextSyncFullLocalDiscovery();
|
||||
|
||||
folderMan->scheduleFolder(_folder);
|
||||
}
|
||||
|
||||
@@ -229,7 +229,6 @@ void SettingsDialog::accountAdded(AccountState *s)
|
||||
_actionGroup->addAction(accountAction);
|
||||
_actionGroupWidgets.insert(accountAction, accountSettings);
|
||||
_actionForAccount.insert(s->account().data(), accountAction);
|
||||
accountAction->trigger();
|
||||
|
||||
connect(accountSettings, &AccountSettings::folderChanged, _gui, &ownCloudGui::slotFoldersChanged);
|
||||
connect(accountSettings, &AccountSettings::openFolderAlias,
|
||||
|
||||
@@ -166,7 +166,6 @@ void SettingsDialogMac::accountAdded(AccountState *s)
|
||||
connect(s, &AccountState::isConnectedChanged, this, &SettingsDialogMac::slotRefreshActivityAccountStateSender);
|
||||
|
||||
slotRefreshActivity(s);
|
||||
setCurrentPanelIndex(0);
|
||||
}
|
||||
|
||||
void SettingsDialogMac::accountRemoved(AccountState *s)
|
||||
|
||||
@@ -539,7 +539,7 @@ void ShareLinkWidget::emailShareLink(const QUrl &url)
|
||||
{
|
||||
QString fileName = _sharePath.mid(_sharePath.lastIndexOf('/') + 1);
|
||||
Utility::openEmailComposer(
|
||||
tr("I shared %1 with you").arg(fileName),
|
||||
QString("I shared %1 with you").arg(fileName),
|
||||
url.toString(),
|
||||
this);
|
||||
}
|
||||
|
||||
@@ -580,7 +580,7 @@ void SocketApi::fetchPrivateLinkUrlHelper(const QString &localFile, const std::f
|
||||
fetchPrivateLinkUrl(
|
||||
fileData.folder->accountState()->account(),
|
||||
fileData.serverRelativePath,
|
||||
record.legacyDeriveNumericFileId(),
|
||||
record.numericFileId(),
|
||||
this,
|
||||
targetFun);
|
||||
}
|
||||
@@ -621,41 +621,6 @@ void SocketApi::command_DOWNLOAD_VIRTUAL_FILE(const QString &filesArg, SocketLis
|
||||
}
|
||||
}
|
||||
|
||||
/* Go over all the files ans replace them by a virtual file */
|
||||
void SocketApi::command_REPLACE_VIRTUAL_FILE(const QString &filesArg, SocketListener *)
|
||||
{
|
||||
QStringList files = filesArg.split(QLatin1Char('\x1e')); // Record Separator
|
||||
auto suffix = QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX);
|
||||
|
||||
for (const auto &file : files) {
|
||||
QString relativePath;
|
||||
auto folder = FolderMan::instance()->folderForPath(file, &relativePath);
|
||||
if (!folder)
|
||||
continue;
|
||||
if (file.endsWith(suffix))
|
||||
continue;
|
||||
QFileInfo fi(file);
|
||||
if (fi.isDir()) {
|
||||
folder->journalDb()->getFilesBelowPath(relativePath.toUtf8(), [&](const SyncJournalFileRecord &rec) {
|
||||
if (rec._type != ItemTypeFile || rec._path.endsWith(APPLICATION_DOTVIRTUALFILE_SUFFIX))
|
||||
return;
|
||||
QString file = folder->path() + '/' + QString::fromUtf8(rec._path);
|
||||
if (!FileSystem::rename(file, file + suffix)) {
|
||||
qCWarning(lcSocketApi) << "Unable to rename " << file;
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
SyncJournalFileRecord record;
|
||||
if (!folder->journalDb()->getFileRecord(relativePath, &record) || !record.isValid())
|
||||
continue;
|
||||
if (!FileSystem::rename(file, file + suffix)) {
|
||||
qCWarning(lcSocketApi) << "Unable to rename " << file;
|
||||
}
|
||||
FolderMan::instance()->scheduleFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::emailPrivateLink(const QString &link)
|
||||
{
|
||||
Utility::openEmailComposer(
|
||||
@@ -798,23 +763,12 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe
|
||||
if (folder) {
|
||||
auto virtualFileSuffix = QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX);
|
||||
bool hasVirtualFile = false;
|
||||
bool hasNormalFiles = false;
|
||||
bool hasDir = false;
|
||||
for (const auto &file : files) {
|
||||
if (QFileInfo(file).isDir()) {
|
||||
hasDir = true;
|
||||
} else if (file.endsWith(virtualFileSuffix)) {
|
||||
if (file.endsWith(virtualFileSuffix) || (folder->useVirtualFiles() && QFileInfo(file).isDir()))
|
||||
hasVirtualFile = true;
|
||||
} else if (!hasNormalFiles) {
|
||||
bool isOnTheServer = FileData::get(file).journalRecord().isValid();
|
||||
hasNormalFiles = isOnTheServer;
|
||||
}
|
||||
}
|
||||
if (hasVirtualFile || (hasDir && folder->useVirtualFiles()))
|
||||
if (hasVirtualFile)
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:DOWNLOAD_VIRTUAL_FILE::") + tr("Download file(s)", "", files.size()));
|
||||
|
||||
if ((hasNormalFiles || hasDir) && folder->useVirtualFiles())
|
||||
listener->sendMessage(QLatin1String("MENU_ITEM:REPLACE_VIRTUAL_FILE::") + tr("Replace file(s) by virtual file", "", files.size()));
|
||||
}
|
||||
|
||||
listener->sendMessage(QString("GET_MENU_ITEMS:END"));
|
||||
|
||||
@@ -108,7 +108,6 @@ private:
|
||||
Q_INVOKABLE void command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
|
||||
Q_INVOKABLE void command_OPEN_PRIVATE_LINK(const QString &localFile, SocketListener *listener);
|
||||
Q_INVOKABLE void command_DOWNLOAD_VIRTUAL_FILE(const QString &filesArg, SocketListener *listener);
|
||||
Q_INVOKABLE void command_REPLACE_VIRTUAL_FILE(const QString &filesArg, SocketListener *listener);
|
||||
|
||||
// Fetch the private link and call targetFun
|
||||
void fetchPrivateLinkUrlHelper(const QString &localFile, const std::function<void(const QString &url)> &targetFun);
|
||||
|
||||
@@ -192,30 +192,8 @@ void OCUpdater::slotStartInstaller()
|
||||
settings.setValue(autoUpdateAttemptedC, true);
|
||||
settings.sync();
|
||||
qCInfo(lcUpdater) << "Running updater" << updateFile;
|
||||
|
||||
if(updateFile.endsWith(".exe")) {
|
||||
QProcess::startDetached(updateFile, QStringList() << "/S"
|
||||
<< "/launch");
|
||||
} else if(updateFile.endsWith(".msi")) {
|
||||
// When MSIs are installed without gui they cannot launch applications
|
||||
// as they lack the user context. That is why we need to run the client
|
||||
// manually here. We wrap the msiexec and client invocation in a powershell
|
||||
// script because owncloud.exe will be shut down for installation.
|
||||
// | Out-Null forces powershell to wait for msiexec to finish.
|
||||
auto preparePathForPowershell = [](QString path) {
|
||||
path.replace("'", "''");
|
||||
|
||||
return QDir::toNativeSeparators(path);
|
||||
};
|
||||
|
||||
QString msiLogFile = cfg.configPath() + "msi.log";
|
||||
QString command = QString("&{msiexec /norestart /passive /i '%1' /L*V '%2'| Out-Null ; &'%3'}")
|
||||
.arg(preparePathForPowershell(updateFile))
|
||||
.arg(preparePathForPowershell(msiLogFile))
|
||||
.arg(preparePathForPowershell(QCoreApplication::applicationFilePath()));
|
||||
|
||||
QProcess::startDetached("powershell.exe", QStringList{"-Command", command});
|
||||
}
|
||||
QProcess::startDetached(updateFile, QStringList() << "/S"
|
||||
<< "/launch");
|
||||
}
|
||||
|
||||
void OCUpdater::checkForUpdate()
|
||||
@@ -296,19 +274,11 @@ void NSISUpdater::slotDownloadFinished()
|
||||
|
||||
QUrl url(reply->url());
|
||||
_file->close();
|
||||
|
||||
ConfigFile cfg;
|
||||
QSettings settings(cfg.configFile(), QSettings::IniFormat);
|
||||
|
||||
// remove previously downloaded but not used installer
|
||||
QFile oldTargetFile(settings.value(updateAvailableC).toString());
|
||||
if (oldTargetFile.exists()) {
|
||||
oldTargetFile.remove();
|
||||
}
|
||||
|
||||
QFile::copy(_file->fileName(), _targetFile);
|
||||
setDownloadState(DownloadComplete);
|
||||
qCInfo(lcUpdater) << "Downloaded" << url.toString() << "to" << _targetFile;
|
||||
ConfigFile cfg;
|
||||
QSettings settings(cfg.configFile(), QSettings::IniFormat);
|
||||
settings.setValue(updateTargetVersionC, updateInfo().version());
|
||||
settings.setValue(updateAvailableC, _targetFile);
|
||||
}
|
||||
@@ -333,7 +303,7 @@ void NSISUpdater::versionInfoArrived(const UpdateInfo &info)
|
||||
showDialog(info);
|
||||
}
|
||||
if (!url.isEmpty()) {
|
||||
_targetFile = cfg.configPath() + url.mid(url.lastIndexOf('/')+1);
|
||||
_targetFile = cfg.configPath() + url.mid(url.lastIndexOf('/'));
|
||||
if (QFile(_targetFile).exists()) {
|
||||
setDownloadState(DownloadComplete);
|
||||
} else {
|
||||
|
||||
@@ -115,7 +115,6 @@ signals:
|
||||
void requestRestart();
|
||||
|
||||
public slots:
|
||||
// FIXME Maybe this should be in the NSISUpdater which should have been called WindowsUpdater
|
||||
void slotStartInstaller();
|
||||
|
||||
protected slots:
|
||||
|
||||
@@ -57,10 +57,6 @@ QUrl Updater::updateUrl()
|
||||
urlQuery.addQueryItem(QLatin1String("sparkle"), QLatin1String("true"));
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
urlQuery.addQueryItem(QLatin1String("msi"), QLatin1String("true"));
|
||||
#endif
|
||||
|
||||
updateBaseUrl.setQuery(urlQuery);
|
||||
|
||||
return updateBaseUrl;
|
||||
@@ -130,10 +126,9 @@ Updater *Updater::create()
|
||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||
return new SparkleUpdater(url);
|
||||
#elif defined(Q_OS_WIN32)
|
||||
// Also for MSI
|
||||
// the best we can do is notify about updates
|
||||
return new NSISUpdater(url);
|
||||
#else
|
||||
// the best we can do is notify about updates
|
||||
return new PassiveUpdateNotifier(url);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ OwncloudAdvancedSetupPage::OwncloudAdvancedSetupPage()
|
||||
|
||||
registerField(QLatin1String("OCSyncFromScratch"), _ui.cbSyncFromScratch);
|
||||
|
||||
_ui.errorScrollContents->layout()->addWidget(_progressIndi);
|
||||
_ui.resultLayout->addWidget(_progressIndi);
|
||||
stopSpinner();
|
||||
setupCustomization();
|
||||
|
||||
@@ -105,12 +105,7 @@ void OwncloudAdvancedSetupPage::initializePage()
|
||||
{
|
||||
WizardCommon::initErrorLabel(_ui.errorLabel);
|
||||
|
||||
auto labelSizeHint = _ui.errorLabel->minimumSizeHint();
|
||||
_ui.errorScroll->setMinimumSize(
|
||||
labelSizeHint.width(),
|
||||
qMax<int>(1.3 * labelSizeHint.height(), _progressIndi->height()));
|
||||
|
||||
if (!Theme::instance()->showVirtualFilesOption()) {
|
||||
if (!ConfigFile().showExperimentalOptions()) {
|
||||
// If the layout were wrapped in a widget, the auto-grouping of the
|
||||
// radio buttons no longer works and there are surprising margins.
|
||||
// Just manually hide the button and remove the layout.
|
||||
@@ -142,7 +137,6 @@ void OwncloudAdvancedSetupPage::initializePage()
|
||||
|
||||
if (Theme::instance()->wizardSelectiveSyncDefaultNothing()) {
|
||||
_selectiveSyncBlacklist = QStringList("/");
|
||||
setRadioChecked(_ui.rSelectiveSync);
|
||||
QTimer::singleShot(0, this, &OwncloudAdvancedSetupPage::slotSelectiveSyncClicked);
|
||||
}
|
||||
|
||||
@@ -187,6 +181,8 @@ void OwncloudAdvancedSetupPage::updateStatus()
|
||||
}
|
||||
|
||||
_ui.syncModeLabel->setText(t);
|
||||
_ui.syncModeLabel->setFixedHeight(_ui.syncModeLabel->sizeHint().height());
|
||||
wizard()->resize(wizard()->sizeHint());
|
||||
setErrorString(errorStr);
|
||||
emit completeChanged();
|
||||
}
|
||||
@@ -199,12 +195,14 @@ bool OwncloudAdvancedSetupPage::dataChanged()
|
||||
|
||||
void OwncloudAdvancedSetupPage::startSpinner()
|
||||
{
|
||||
_ui.resultLayout->setEnabled(true);
|
||||
_progressIndi->setVisible(true);
|
||||
_progressIndi->startAnimation();
|
||||
}
|
||||
|
||||
void OwncloudAdvancedSetupPage::stopSpinner()
|
||||
{
|
||||
_ui.resultLayout->setEnabled(false);
|
||||
_progressIndi->setVisible(false);
|
||||
_progressIndi->stopAnimation();
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>917</width>
|
||||
<height>604</height>
|
||||
<width>912</width>
|
||||
<height>635</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@@ -454,6 +454,13 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="syncModeLabel">
|
||||
<property name="text">
|
||||
@@ -471,54 +478,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QScrollArea" name="errorScroll">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="errorScrollContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>899</width>
|
||||
<height>68</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="resultLayout"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="bottomLabel">
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
*/
|
||||
|
||||
#include <QVariant>
|
||||
#include <QMenu>
|
||||
#include <QClipboard>
|
||||
|
||||
#include "wizard/owncloudoauthcredspage.h"
|
||||
#include "theme.h"
|
||||
@@ -47,20 +45,9 @@ OwncloudOAuthCredsPage::OwncloudOAuthCredsPage()
|
||||
|
||||
connect(_ui.openLinkButton, &QCommandLinkButton::clicked, [this] {
|
||||
_ui.errorLabel->hide();
|
||||
qobject_cast<OwncloudWizard *>(wizard())->account()->clearCookieJar(); // #6574
|
||||
if (_asyncAuth)
|
||||
_asyncAuth->openBrowser();
|
||||
});
|
||||
_ui.openLinkButton->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
QObject::connect(_ui.openLinkButton, &QWidget::customContextMenuRequested, [this](const QPoint &pos) {
|
||||
auto menu = new QMenu(_ui.openLinkButton);
|
||||
menu->addAction(tr("Copy link to clipboard"), this, [this] {
|
||||
if (_asyncAuth)
|
||||
QApplication::clipboard()->setText(_asyncAuth->authorisationLink().toString(QUrl::FullyEncoded));
|
||||
});
|
||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||
menu->popup(_ui.openLinkButton->mapToGlobal(pos));
|
||||
});
|
||||
}
|
||||
|
||||
void OwncloudOAuthCredsPage::initializePage()
|
||||
|
||||
@@ -221,19 +221,12 @@ QString OwncloudSetupPage::url() const
|
||||
bool OwncloudSetupPage::validatePage()
|
||||
{
|
||||
if (!_authTypeKnown) {
|
||||
QString u = url();
|
||||
QUrl qurl(u);
|
||||
if (!qurl.isValid() || qurl.host().isEmpty()) {
|
||||
setErrorString(tr("Invalid URL"), false);
|
||||
return false;
|
||||
}
|
||||
|
||||
setErrorString(QString(), false);
|
||||
_checking = true;
|
||||
startSpinner();
|
||||
emit completeChanged();
|
||||
|
||||
emit determineAuthType(u);
|
||||
emit determineAuthType(url());
|
||||
return false;
|
||||
} else {
|
||||
// connecting is running
|
||||
|
||||
@@ -62,6 +62,9 @@ namespace WizardCommon {
|
||||
|
||||
errorLabel->setStyleSheet(style);
|
||||
errorLabel->setWordWrap(true);
|
||||
auto sizePolicy = errorLabel->sizePolicy();
|
||||
sizePolicy.setRetainSizeWhenHidden(true);
|
||||
errorLabel->setSizePolicy(sizePolicy);
|
||||
errorLabel->setVisible(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD|NetBSD|OpenBSD")
|
||||
)
|
||||
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD|NetBSD|OpenBSD")
|
||||
|
||||
if(SPARKLE_FOUND AND NOT BUILD_LIBRARIES_ONLY)
|
||||
list (APPEND OS_SPECIFIC_LINK_LIBRARIES ${SPARKLE_LIBRARY})
|
||||
endif()
|
||||
|
||||
set(libsync_SRCS
|
||||
account.cpp
|
||||
bandwidthmanager.cpp
|
||||
@@ -86,7 +90,7 @@ ENDIF(NOT APPLE)
|
||||
|
||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS})
|
||||
target_link_libraries(${synclib_NAME}
|
||||
"${csync_NAME}"
|
||||
ocsync
|
||||
${OS_SPECIFIC_LINK_LIBRARIES}
|
||||
Qt5::Core Qt5::Network
|
||||
)
|
||||
@@ -118,6 +122,8 @@ set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
SOVERSION ${MIRALL_SOVERSION}
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
|
||||
)
|
||||
set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE};${CMAKE_INSTALL_RPATH}" )
|
||||
|
||||
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
install(TARGETS ${synclib_NAME}
|
||||
@@ -127,6 +133,10 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
)
|
||||
else()
|
||||
install(TARGETS ${synclib_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
|
||||
if (SPARKLE_FOUND)
|
||||
install(DIRECTORY "${SPARKLE_LIBRARY}"
|
||||
DESTINATION "${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks" USE_SOURCE_PERMISSIONS)
|
||||
endif (SPARKLE_FOUND)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -463,8 +463,7 @@ bool Account::serverVersionUnsupported() const
|
||||
// not detected yet, assume it is fine.
|
||||
return false;
|
||||
}
|
||||
// Older version which is not "end of life" according to https://github.com/owncloud/core/wiki/Maintenance-and-Release-Schedule
|
||||
return serverVersionInt() < makeServerVersion(10, 0, 0) || serverVersion().endsWith("Nextcloud");
|
||||
return serverVersionInt() < makeServerVersion(9, 1, 0);
|
||||
}
|
||||
|
||||
void Account::setServerVersion(const QString &version)
|
||||
|
||||
@@ -379,12 +379,11 @@ bool HttpCredentials::refreshAccessToken()
|
||||
QJsonParseError jsonParseError;
|
||||
QJsonObject json = QJsonDocument::fromJson(jsonData, &jsonParseError).object();
|
||||
QString accessToken = json["access_token"].toString();
|
||||
if (jsonParseError.error != QJsonParseError::NoError || json.isEmpty()) {
|
||||
// Invalid or empty JSON: Network error maybe?
|
||||
if (reply->error() != QNetworkReply::NoError || jsonParseError.error != QJsonParseError::NoError || json.isEmpty()) {
|
||||
// Network error maybe?
|
||||
qCWarning(lcHttpCredentials) << "Error while refreshing the token" << reply->errorString() << jsonData << jsonParseError.errorString();
|
||||
} else if (accessToken.isEmpty()) {
|
||||
// If the json was valid, but the reply did not contain an access token, the token
|
||||
// is considered expired. (Usually the HTTP reply code is 400)
|
||||
// The token is no longer valid.
|
||||
qCDebug(lcHttpCredentials) << "Expired refresh token. Logging out";
|
||||
_refreshToken.clear();
|
||||
} else {
|
||||
|
||||
@@ -90,7 +90,7 @@ int DiscoveryJob::isInSelectiveSyncBlackListCallback(void *data, const QByteArra
|
||||
|
||||
bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, RemotePermissions remotePerm)
|
||||
{
|
||||
if (_syncOptions._confirmExternalStorage && !_syncOptions._newFilesAreVirtual
|
||||
if (_syncOptions._confirmExternalStorage
|
||||
&& remotePerm.hasPermission(RemotePermissions::IsMounted)) {
|
||||
// external storage.
|
||||
|
||||
@@ -113,7 +113,7 @@ bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString &path, RemotePermis
|
||||
}
|
||||
|
||||
auto limit = _syncOptions._newBigFolderSizeLimit;
|
||||
if (limit < 0 || _syncOptions._newFilesAreVirtual) {
|
||||
if (limit < 0) {
|
||||
// no limit, everything is allowed;
|
||||
return false;
|
||||
}
|
||||
@@ -331,7 +331,7 @@ static void propertyMapToFileStat(const QMap<QString, QString> &map, csync_file_
|
||||
} else if (property == "dDC") {
|
||||
file_stat->directDownloadCookies = value.toUtf8();
|
||||
} else if (property == "permissions") {
|
||||
file_stat->remotePerm = RemotePermissions::fromServerString(value);
|
||||
file_stat->remotePerm = RemotePermissions(value);
|
||||
} else if (property == "checksums") {
|
||||
file_stat->checksumHeader = findBestChecksum(value.toUtf8());
|
||||
} else if (property == "share-types" && !value.isEmpty()) {
|
||||
@@ -355,16 +355,12 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
|
||||
// The first entry is for the folder itself, we should process it differently.
|
||||
_ignoredFirst = true;
|
||||
if (map.contains("permissions")) {
|
||||
auto perm = RemotePermissions::fromServerString(map.value("permissions"));
|
||||
RemotePermissions perm(map.value("permissions"));
|
||||
emit firstDirectoryPermissions(perm);
|
||||
_isExternalStorage = perm.hasPermission(RemotePermissions::IsMounted);
|
||||
}
|
||||
if (map.contains("data-fingerprint")) {
|
||||
_dataFingerprint = map.value("data-fingerprint").toUtf8();
|
||||
if (_dataFingerprint.isEmpty()) {
|
||||
// Placeholder that means that the server supports the feature even if it did not set one.
|
||||
_dataFingerprint = "[empty]";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Remove <webDAV-Url>/folder/ from <webDAV-Url>/folder/subfile.txt
|
||||
@@ -381,11 +377,13 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
|
||||
std::unique_ptr<csync_file_stat_t> file_stat(new csync_file_stat_t);
|
||||
file_stat->path = file.toUtf8();
|
||||
file_stat->size = -1;
|
||||
file_stat->modtime = -1;
|
||||
propertyMapToFileStat(map, file_stat.get());
|
||||
if (file_stat->type == ItemTypeDirectory)
|
||||
file_stat->size = 0;
|
||||
if (file_stat->type == ItemTypeSkip
|
||||
|| file_stat->size == -1
|
||||
|| file_stat->modtime == -1
|
||||
|| file_stat->remotePerm.isNull()
|
||||
|| file_stat->etag.isEmpty()
|
||||
|| file_stat->file_id.isEmpty()) {
|
||||
@@ -665,7 +663,7 @@ csync_vio_handle_t *DiscoveryJob::remote_vio_opendir_hook(const char *url,
|
||||
qCDebug(lcDiscovery) << directoryResult->code << "when opening" << url << "msg=" << directoryResult->msg;
|
||||
errno = directoryResult->code;
|
||||
// save the error string to the context
|
||||
discoveryJob->_csync_ctx->error_string = directoryResult->msg;
|
||||
discoveryJob->_csync_ctx->error_string = qstrdup(directoryResult->msg.toUtf8().constData());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ void CheckServerJob::onTimedOut()
|
||||
|
||||
QString CheckServerJob::version(const QJsonObject &info)
|
||||
{
|
||||
return info.value(QLatin1String("version")).toString() + "-" + info.value(QLatin1String("productname")).toString();
|
||||
return info.value(QLatin1String("version")).toString();
|
||||
}
|
||||
|
||||
QString CheckServerJob::versionString(const QJsonObject &info)
|
||||
|
||||
@@ -695,7 +695,6 @@ bool OwncloudPropagator::createConflict(const SyncFileItemPtr &item,
|
||||
ConflictRecord conflictRecord;
|
||||
conflictRecord.path = conflictFileName.toUtf8();
|
||||
conflictRecord.baseModtime = item->_previousModtime;
|
||||
conflictRecord.initialBasePath = item->_file.toUtf8();
|
||||
|
||||
SyncJournalFileRecord baseRecord;
|
||||
if (_journal->getFileRecord(item->_originalFile, &baseRecord) && baseRecord.isValid()) {
|
||||
|
||||
@@ -36,9 +36,6 @@ public:
|
||||
QPixmap wizardHeaderLogo() const Q_DECL_OVERRIDE;
|
||||
#endif
|
||||
|
||||
// For owncloud-brandings *do* show the virtual files option.
|
||||
bool showVirtualFilesOption() const override { return true; }
|
||||
|
||||
private:
|
||||
};
|
||||
}
|
||||
|
||||
@@ -147,7 +147,6 @@ void GETFileJob::newReplyHook(QNetworkReply *reply)
|
||||
|
||||
connect(reply, &QNetworkReply::metaDataChanged, this, &GETFileJob::slotMetaDataChanged);
|
||||
connect(reply, &QIODevice::readyRead, this, &GETFileJob::slotReadyRead);
|
||||
connect(reply, &QNetworkReply::finished, this, &GETFileJob::slotReadyRead);
|
||||
connect(reply, &QNetworkReply::downloadProgress, this, &GETFileJob::downloadProgress);
|
||||
}
|
||||
|
||||
@@ -166,9 +165,6 @@ void GETFileJob::slotMetaDataChanged()
|
||||
// If the status code isn't 2xx, don't write the reply body to the file.
|
||||
// For any error: handle it when the job is finished, not here.
|
||||
if (httpStatus / 100 != 2) {
|
||||
// Disable the buffer limit, as we don't limit the bandwidth for error messages.
|
||||
// (We are only going to do a readAll() at the end.)
|
||||
reply()->setReadBufferSize(0);
|
||||
_device->close();
|
||||
return;
|
||||
}
|
||||
@@ -272,7 +268,7 @@ void GETFileJob::slotReadyRead()
|
||||
int bufferSize = qMin(1024 * 8ll, reply()->bytesAvailable());
|
||||
QByteArray buffer(bufferSize, Qt::Uninitialized);
|
||||
|
||||
while (reply()->bytesAvailable() > 0 && _saveBodyToFile) {
|
||||
while (reply()->bytesAvailable() > 0) {
|
||||
if (_bandwidthChoked) {
|
||||
qCWarning(lcGetJob) << "Download choked";
|
||||
break;
|
||||
@@ -296,7 +292,7 @@ void GETFileJob::slotReadyRead()
|
||||
return;
|
||||
}
|
||||
|
||||
if (_device->isOpen()) {
|
||||
if (_device->isOpen() && _saveBodyToFile) {
|
||||
qint64 w = _device->write(buffer.constData(), r);
|
||||
if (w != r) {
|
||||
_errorString = _device->errorString();
|
||||
@@ -308,7 +304,7 @@ void GETFileJob::slotReadyRead()
|
||||
}
|
||||
}
|
||||
|
||||
if (reply()->isFinished() && (reply()->bytesAvailable() == 0 || !_saveBodyToFile)) {
|
||||
if (reply()->isFinished() && reply()->bytesAvailable() == 0) {
|
||||
qCDebug(lcGetJob) << "Actually finished!";
|
||||
if (_bandwidthManager) {
|
||||
_bandwidthManager->unregisterDownloadJob(this);
|
||||
@@ -355,13 +351,10 @@ void PropagateDownloadFile::start()
|
||||
if (_item->_type == ItemTypeVirtualFile) {
|
||||
auto fn = propagator()->getFilePath(_item->_file);
|
||||
qCDebug(lcPropagateDownload) << "creating virtual file" << fn;
|
||||
|
||||
// NOTE: Other places might depend on contents of placeholder files (like csync_update)
|
||||
QFile file(fn);
|
||||
file.open(QFile::ReadWrite | QFile::Truncate);
|
||||
file.write(" ");
|
||||
file.close();
|
||||
FileSystem::setModTime(fn, _item->_modtime);
|
||||
updateMetadata(false);
|
||||
return;
|
||||
}
|
||||
@@ -667,11 +660,10 @@ void PropagateDownloadFile::slotGetFinished()
|
||||
// the database yet!)
|
||||
if (job->reply()->rawHeader("OC-Conflict") == "1") {
|
||||
_conflictRecord.path = _item->_file.toUtf8();
|
||||
_conflictRecord.initialBasePath = job->reply()->rawHeader("OC-ConflictInitialBasePath");
|
||||
_conflictRecord.baseFileId = job->reply()->rawHeader("OC-ConflictBaseFileId");
|
||||
_conflictRecord.baseEtag = job->reply()->rawHeader("OC-ConflictBaseEtag");
|
||||
_conflictRecord.baseEtag = _job->reply()->rawHeader("OC-ConflictBaseEtag");
|
||||
|
||||
auto mtimeHeader = job->reply()->rawHeader("OC-ConflictBaseMtime");
|
||||
auto mtimeHeader = _job->reply()->rawHeader("OC-ConflictBaseMtime");
|
||||
if (!mtimeHeader.isEmpty())
|
||||
_conflictRecord.baseModtime = mtimeHeader.toLongLong();
|
||||
|
||||
|
||||
@@ -88,20 +88,11 @@ void PropagateRemoteMove::start()
|
||||
return;
|
||||
}
|
||||
|
||||
QString source = propagator()->_remoteFolder + _item->_file;
|
||||
QString destination = QDir::cleanPath(propagator()->account()->url().path() + QLatin1Char('/')
|
||||
+ propagator()->account()->davPath() + propagator()->_remoteFolder + _item->_renameTarget);
|
||||
if (_item->_type == ItemTypeVirtualFile || _item->_type == ItemTypeVirtualFileDownload) {
|
||||
auto suffix = propagator()->syncOptions()._virtualFileSuffix;
|
||||
ASSERT(source.endsWith(suffix) && destination.endsWith(suffix));
|
||||
if (source.endsWith(suffix) && destination.endsWith(suffix)) {
|
||||
source.chop(suffix.size());
|
||||
destination.chop(suffix.size());
|
||||
}
|
||||
}
|
||||
qCDebug(lcPropagateRemoteMove) << source << destination;
|
||||
|
||||
_job = new MoveJob(propagator()->account(), source, destination, this);
|
||||
_job = new MoveJob(propagator()->account(),
|
||||
propagator()->_remoteFolder + _item->_file,
|
||||
destination, this);
|
||||
connect(_job.data(), &MoveJob::finishedSignal, this, &PropagateRemoteMove::slotMoveJobFinished);
|
||||
propagator()->_activeJobList.append(this);
|
||||
_job->start();
|
||||
|
||||
@@ -564,8 +564,6 @@ void PropagateUploadFileCommon::slotJobDestroyed(QObject *job)
|
||||
// This function is used whenever there is an error occuring and jobs might be in progress
|
||||
void PropagateUploadFileCommon::abortWithError(SyncFileItem::Status status, const QString &error)
|
||||
{
|
||||
if (_aborting)
|
||||
return;
|
||||
abort(AbortType::Synchronous);
|
||||
done(status, error);
|
||||
}
|
||||
@@ -601,8 +599,6 @@ QMap<QByteArray, QByteArray> PropagateUploadFileCommon::headers()
|
||||
auto conflictRecord = propagator()->_journal->conflictRecord(_item->_file.toUtf8());
|
||||
if (conflictRecord.isValid()) {
|
||||
headers["OC-Conflict"] = "1";
|
||||
if (!conflictRecord.initialBasePath.isEmpty())
|
||||
headers["OC-ConflictInitialBasePath"] = conflictRecord.initialBasePath;
|
||||
if (!conflictRecord.baseFileId.isEmpty())
|
||||
headers["OC-ConflictBaseFileId"] = conflictRecord.baseFileId;
|
||||
if (conflictRecord.baseModtime != -1)
|
||||
@@ -638,10 +634,6 @@ void PropagateUploadFileCommon::abortNetworkJobs(
|
||||
PropagatorJob::AbortType abortType,
|
||||
const std::function<bool(AbstractNetworkJob *)> &mayAbortJob)
|
||||
{
|
||||
if (_aborting)
|
||||
return;
|
||||
_aborting = true;
|
||||
|
||||
// Count the number of jobs that need aborting, and emit the overall
|
||||
// abort signal when they're all done.
|
||||
QSharedPointer<int> runningCount(new int(0));
|
||||
|
||||
@@ -209,14 +209,6 @@ protected:
|
||||
QVector<AbstractNetworkJob *> _jobs; /// network jobs that are currently in transit
|
||||
bool _finished BITFIELD(1); /// Tells that all the jobs have been finished
|
||||
bool _deleteExisting BITFIELD(1);
|
||||
|
||||
/** Whether an abort is currently ongoing.
|
||||
*
|
||||
* Important to avoid duplicate aborts since each finishing PUTFileJob might
|
||||
* trigger an abort on error.
|
||||
*/
|
||||
bool _aborting BITFIELD(1);
|
||||
|
||||
QByteArray _transmissionChecksumHeader;
|
||||
|
||||
public:
|
||||
@@ -224,7 +216,6 @@ public:
|
||||
: PropagateItemJob(propagator, item)
|
||||
, _finished(false)
|
||||
, _deleteExisting(false)
|
||||
, _aborting(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -83,8 +83,7 @@ void PropagateUploadFileNG::doStartUpload()
|
||||
propagator()->_activeJobList.append(this);
|
||||
|
||||
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
|
||||
if (progressInfo._valid && progressInfo.isChunked() && progressInfo._modtime == _item->_modtime
|
||||
&& progressInfo._size == qint64(_item->_size)) {
|
||||
if (progressInfo._valid && progressInfo.isChunked() && progressInfo._modtime == _item->_modtime) {
|
||||
_transferId = progressInfo._transferid;
|
||||
auto url = chunkUrl();
|
||||
auto job = new LsColJob(propagator()->account(), url, this);
|
||||
@@ -240,7 +239,6 @@ void PropagateUploadFileNG::startNewUpload()
|
||||
pi._transferid = _transferId;
|
||||
pi._modtime = _item->_modtime;
|
||||
pi._contentChecksum = _item->_checksumHeader;
|
||||
pi._size = _item->_size;
|
||||
propagator()->_journal->setUploadInfo(_item->_file, pi);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
QMap<QByteArray, QByteArray> headers;
|
||||
|
||||
@@ -43,7 +43,7 @@ void PropagateUploadFileV1::doStartUpload()
|
||||
|
||||
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
|
||||
|
||||
if (progressInfo._valid && progressInfo.isChunked() && progressInfo._modtime == _item->_modtime && progressInfo._size == qint64(_item->_size)
|
||||
if (progressInfo._valid && progressInfo.isChunked() && progressInfo._modtime == _item->_modtime
|
||||
&& (progressInfo._contentChecksum == _item->_checksumHeader || progressInfo._contentChecksum.isEmpty() || _item->_checksumHeader.isEmpty())) {
|
||||
_startChunk = progressInfo._chunk;
|
||||
_transferId = progressInfo._transferid;
|
||||
@@ -59,7 +59,6 @@ void PropagateUploadFileV1::doStartUpload()
|
||||
pi._modtime = _item->_modtime;
|
||||
pi._errorCount = 0;
|
||||
pi._contentChecksum = _item->_checksumHeader;
|
||||
pi._size = _item->_size;
|
||||
propagator()->_journal->setUploadInfo(_item->_file, pi);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
}
|
||||
@@ -286,7 +285,6 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
pi._modtime = _item->_modtime;
|
||||
pi._errorCount = 0; // successful chunk upload resets
|
||||
pi._contentChecksum = _item->_checksumHeader;
|
||||
pi._size = _item->_size;
|
||||
propagator()->_journal->setUploadInfo(_item->_file, pi);
|
||||
propagator()->_journal->commit("Upload info");
|
||||
startNextChunk();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user