mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-24 14:05:58 +03:00
commit
09fa5966da
143 changed files with 5287 additions and 4780 deletions
|
@ -246,6 +246,7 @@ set(WITH_TESTING ${UNIT_TESTING})
|
||||||
if(BUILD_CLIENT)
|
if(BUILD_CLIENT)
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
if(NOT BUILD_LIBRARIES_ONLY)
|
if(NOT BUILD_LIBRARIES_ONLY)
|
||||||
|
add_subdirectory(man)
|
||||||
add_subdirectory(doc)
|
add_subdirectory(doc)
|
||||||
add_subdirectory(doc/dev)
|
add_subdirectory(doc/dev)
|
||||||
if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/admin)
|
if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/admin)
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
ChangeLog
|
ChangeLog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
version 2.4.0 (2017-11-XX)
|
version 2.4.0 (2017-12-XX)
|
||||||
* If you're using 2.4.0 alpha1, please upgrade as the alpha1 had an issue with hidden files!
|
* If you're using 2.4.0 alpha1, please upgrade as the alpha1 had an issue with hidden files!
|
||||||
* OAuth2 authentication support by opening external browser (#5668)
|
* OAuth2 authentication support by opening external browser (#5668)
|
||||||
|
* Shibboleth: Change to use OAuth2 if supported (#6198)
|
||||||
* Sharing: Add support for multiple public link shares (#5655)
|
* Sharing: Add support for multiple public link shares (#5655)
|
||||||
* Sharing: Add option to copy/email private links (#5023, #5627)
|
* Sharing: Add option to copy/email private links (#5023, #5627)
|
||||||
* Sharing: Add option "show file listing" (#5837)
|
* Sharing: Add option "show file listing" (#5837)
|
||||||
|
@ -61,6 +62,7 @@ version 2.4.0 (2017-11-XX)
|
||||||
* Sync: Introduce overall errors that are not tied to a file (#5746)
|
* Sync: Introduce overall errors that are not tied to a file (#5746)
|
||||||
* Sync: Better messaging for 507 Insufficient Storage (#5537)
|
* Sync: Better messaging for 507 Insufficient Storage (#5537)
|
||||||
* Sync: Create conflicts by comparing the hash of files with identical mtime/size (#5589)
|
* Sync: Create conflicts by comparing the hash of files with identical mtime/size (#5589)
|
||||||
|
* Sync: Avoid downloads by comparing the hash of files with identical mtime/size (#6153)
|
||||||
* Sync: Upload conflict files if OWNCLOUD_UPLOAD_CONFLICT_FILES environment variable is set (#6038)
|
* Sync: Upload conflict files if OWNCLOUD_UPLOAD_CONFLICT_FILES environment variable is set (#6038)
|
||||||
* Sync: Blacklist: Don't let errors become warnings (#5516)
|
* Sync: Blacklist: Don't let errors become warnings (#5516)
|
||||||
* Sync: Check etag again after active sync (#4116)
|
* Sync: Check etag again after active sync (#4116)
|
||||||
|
|
|
@ -98,7 +98,7 @@ set(QT_RCC_EXECUTABLE "${Qt5Core_RCC_EXECUTABLE}")
|
||||||
|
|
||||||
#Enable deprecated symbols
|
#Enable deprecated symbols
|
||||||
add_definitions("-DQT_DISABLE_DEPRECATED_BEFORE=0")
|
add_definitions("-DQT_DISABLE_DEPRECATED_BEFORE=0")
|
||||||
|
add_definitions("-DQT_DEPRECATED_WARNINGS")
|
||||||
add_definitions("-DQT_USE_QSTRINGBUILDER") #optimize string concatenation
|
add_definitions("-DQT_USE_QSTRINGBUILDER") #optimize string concatenation
|
||||||
add_definitions("-DQT_MESSAGELOGCONTEXT") #enable function name and line number in debug output
|
add_definitions("-DQT_MESSAGELOGCONTEXT") #enable function name and line number in debug output
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ if(SPHINX_FOUND)
|
||||||
set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
|
set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
|
||||||
# HTML output directory
|
# HTML output directory
|
||||||
set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html")
|
set(SPHINX_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/html")
|
||||||
set(SPHINX_MAN_DIR "${CMAKE_CURRENT_BINARY_DIR}/man1")
|
|
||||||
set(SPHINX_PDF_DIR "${CMAKE_CURRENT_BINARY_DIR}/latex")
|
set(SPHINX_PDF_DIR "${CMAKE_CURRENT_BINARY_DIR}/latex")
|
||||||
set(SPHINX_QCH_DIR "${CMAKE_CURRENT_BINARY_DIR}/qthelp")
|
set(SPHINX_QCH_DIR "${CMAKE_CURRENT_BINARY_DIR}/qthelp")
|
||||||
set(SPHINX_HTMLHELP_DIR "${CMAKE_CURRENT_BINARY_DIR}/htmlhelp")
|
set(SPHINX_HTMLHELP_DIR "${CMAKE_CURRENT_BINARY_DIR}/htmlhelp")
|
||||||
|
@ -17,8 +16,6 @@ if(SPHINX_FOUND)
|
||||||
install(DIRECTORY ${SPHINX_PDF_DIR} DESTINATION ${APPLICATION_DOC_DIR} OPTIONAL)
|
install(DIRECTORY ${SPHINX_PDF_DIR} DESTINATION ${APPLICATION_DOC_DIR} OPTIONAL)
|
||||||
install(DIRECTORY ${SPHINX_QCH_DIR} DESTINATION ${APPLICATION_DOC_DIR} OPTIONAL)
|
install(DIRECTORY ${SPHINX_QCH_DIR} DESTINATION ${APPLICATION_DOC_DIR} OPTIONAL)
|
||||||
|
|
||||||
install(DIRECTORY ${SPHINX_MAN_DIR} DESTINATION ${CMAKE_INSTALL_MANDIR} OPTIONAL)
|
|
||||||
|
|
||||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" conf.py @ONLY)
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/conf.py.in" conf.py @ONLY)
|
||||||
|
|
||||||
if(WITH_DOC)
|
if(WITH_DOC)
|
||||||
|
@ -79,11 +76,6 @@ if(SPHINX_FOUND)
|
||||||
-D html_theme=owncloud_com
|
-D html_theme=owncloud_com
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${SPHINX_HTML_DIR}/com )
|
${SPHINX_HTML_DIR}/com )
|
||||||
add_custom_target( doc-man ${SPHINX_EXECUTABLE}
|
|
||||||
-q -c . -b man
|
|
||||||
-d ${SPHINX_CACHE_DIR}/man
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
${SPHINX_MAN_DIR} )
|
|
||||||
|
|
||||||
## Building CHM files requires HTML Help Workshop. Since it requires wine
|
## Building CHM files requires HTML Help Workshop. Since it requires wine
|
||||||
## with special dependencies, it's impossible to write a cmake check for it.
|
## with special dependencies, it's impossible to write a cmake check for it.
|
||||||
|
@ -98,4 +90,4 @@ if(SPHINX_FOUND)
|
||||||
${SPHINX_HTMLHELP_DIR} )
|
${SPHINX_HTMLHELP_DIR} )
|
||||||
add_custom_target( doc-chm pushd ${SPHINX_HTMLHELP_DIR}; ${MSHTML_COMPILER} *.hhp; popd
|
add_custom_target( doc-chm pushd ${SPHINX_HTMLHELP_DIR}; ${MSHTML_COMPILER} *.hhp; popd
|
||||||
DEPENDS doc-chm-sphinx )
|
DEPENDS doc-chm-sphinx )
|
||||||
endif(SPHINX_FOUND)
|
endif(SPHINX_FOUND)
|
|
@ -119,7 +119,7 @@ Preventing Automatic Updates in Linux Environments
|
||||||
|
|
||||||
Because the Linux client does not provide automatic updating functionality, there is no
|
Because the Linux client does not provide automatic updating functionality, there is no
|
||||||
need to remove the automatic-update check. However, if you want to disable it edit your desktop
|
need to remove the automatic-update check. However, if you want to disable it edit your desktop
|
||||||
client configuration file, ``$HOME/.local/share/data/ownCloud/owncloud.cfg``.
|
client configuration file, ``$HOME/.config/ownCloud/owncloud.cfg``.
|
||||||
Add this line to the [General] section::
|
Add this line to the [General] section::
|
||||||
|
|
||||||
skipUpdateCheck=true
|
skipUpdateCheck=true
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
The ownCloud Client reads a configuration file. You can locate this configuration file as follows:
|
The ownCloud Client reads a configuration file. You can locate this configuration file as follows:
|
||||||
|
|
||||||
On Linux distributions:
|
On Linux distributions:
|
||||||
``$HOME/.local/share/data/ownCloud/owncloud.cfg``
|
``$HOME/.config/ownCloud/owncloud.cfg``
|
||||||
|
|
||||||
On Microsoft Windows systems:
|
On Microsoft Windows systems:
|
||||||
``%LOCALAPPDATA%\ownCloud\owncloud.cfg``
|
``%APPDATA%\ownCloud\owncloud.cfg``
|
||||||
|
|
||||||
On MAC OS X systems:
|
On MAC OS X systems:
|
||||||
``$HOME/Library/Application Support/ownCloud/owncloud.cfg``
|
``$HOME/Library/Preferences/ownCloud/owncloud.cfg``
|
||||||
|
|
||||||
|
|
||||||
The configuration file contains settings using the Microsoft Windows .ini file
|
The configuration file contains settings using the Microsoft Windows .ini file
|
||||||
|
|
14
man/CMakeLists.txt
Normal file
14
man/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
if(SPHINX_FOUND)
|
||||||
|
|
||||||
|
# Sphinx cache with pickled ReST documents
|
||||||
|
set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
|
||||||
|
|
||||||
|
# HTML output directory
|
||||||
|
set(SPHINX_MAN_DIR "${CMAKE_CURRENT_BINARY_DIR}/man1")
|
||||||
|
install(DIRECTORY ${SPHINX_MAN_DIR} DESTINATION ${CMAKE_INSTALL_MANDIR} OPTIONAL)
|
||||||
|
add_custom_target( doc-man ${SPHINX_EXECUTABLE}
|
||||||
|
-c ${CMAKE_SOURCE_DIR}/doc -b man
|
||||||
|
-d ${SPHINX_CACHE_DIR}/man
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${SPHINX_MAN_DIR} )
|
||||||
|
endif(SPHINX_FOUND)
|
1
man/index.rst
Normal file
1
man/index.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
33
man/owncloud.1.rst
Normal file
33
man/owncloud.1.rst
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
:orphan:
|
||||||
|
|
||||||
|
owncloud(1)
|
||||||
|
————
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
========
|
||||||
|
*owncloud* [`OPTIONS`...]
|
||||||
|
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
===========
|
||||||
|
The ownCloud Client is a file synchronization desktop utility. It synchronizes files on your local computer, tablet, or handheld device with an ownCloud Server. If you make a change to the files on one device, the change is propagated to all other synchronized devices using the desktop synchronization clients.
|
||||||
|
|
||||||
|
Normally, you start the client by clicking on the desktop icon or by starting it from the client application menu. After starting, an ownCloud icon appears in the computer system tray or on your tablet or handheld device.
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
.. include:: ../doc/options.rst
|
||||||
|
|
||||||
|
Config File
|
||||||
|
===========
|
||||||
|
.. include:: ../doc/conffile.rst
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
====
|
||||||
|
|
||||||
|
Please report bugs at https://github.com/owncloud/client/issues.
|
||||||
|
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
========
|
||||||
|
:manpage:`owncloudcmd(1)`
|
97
man/owncloudcmd.1.rst
Normal file
97
man/owncloudcmd.1.rst
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
:orphan:
|
||||||
|
|
||||||
|
owncloudcmd(1)
|
||||||
|
—————
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
========
|
||||||
|
*owncloudcmd* [`OPTIONS`...] sourcedir owncloudurl
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
===========
|
||||||
|
owncloudcmd is the command line tool used for the ownCloud file synchronization
|
||||||
|
desktop utility.
|
||||||
|
|
||||||
|
Contrary to the :manpage:`owncloud(1)` GUI client, `owncloudcmd` only performs
|
||||||
|
a single sync run and then exits. In so doing, `owncloudcmd` replaces the
|
||||||
|
`ocsync` binary used for the same purpose in earlier releases.
|
||||||
|
|
||||||
|
A *sync run* synchronizes a single local directory using a WebDAV share on a
|
||||||
|
remote ownCloud server.
|
||||||
|
|
||||||
|
To invoke the command line client, provide the local and the remote repository:
|
||||||
|
The first parameter is the local directory. The second parameter is
|
||||||
|
the server URL.
|
||||||
|
|
||||||
|
.. note:: Prior to the 1.6 release of owncloudcmd, the tool only accepted
|
||||||
|
``owncloud://`` or ``ownclouds://`` in place of ``http://`` and ``https://`` as
|
||||||
|
a scheme. See ``Examples`` for details.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
=======
|
||||||
|
``—user``, ``-u`` ``[user]``
|
||||||
|
Use ``user`` as the login name.
|
||||||
|
|
||||||
|
``—password``, ``-p`` ``[password]``
|
||||||
|
Use ``password`` as the password.
|
||||||
|
|
||||||
|
``-n``
|
||||||
|
Use ``netrc (5)`` for login.
|
||||||
|
|
||||||
|
``—non-interactive``
|
||||||
|
Do not prompt for questions.
|
||||||
|
|
||||||
|
``—silent``, ``—s``
|
||||||
|
Inhibits verbose log output.
|
||||||
|
|
||||||
|
``—trust``
|
||||||
|
Trust any SSL certificate, including invalid ones.
|
||||||
|
|
||||||
|
``—httpproxy http://[user@pass:]<server>:<port>``
|
||||||
|
Uses ``server`` as HTTP proxy.
|
||||||
|
|
||||||
|
``—nonshib``
|
||||||
|
Uses Non Shibboleth WebDAV Authentication
|
||||||
|
|
||||||
|
``—davpath [path]``
|
||||||
|
Overrides the WebDAV Path with ``path``
|
||||||
|
|
||||||
|
``—exclude [file]``
|
||||||
|
Exclude list file
|
||||||
|
|
||||||
|
``—unsyncedfolders [file]``
|
||||||
|
File containing the list of unsynced folders (selective sync)
|
||||||
|
|
||||||
|
``—max-sync-retries [n]``
|
||||||
|
Retries maximum n times (defaults to 3)
|
||||||
|
|
||||||
|
``-h``
|
||||||
|
Sync hidden files,do not ignore them
|
||||||
|
|
||||||
|
Example
|
||||||
|
=======
|
||||||
|
To synchronize the ownCloud directory ``Music`` to the local directory ``media/music``
|
||||||
|
through a proxy listening on port ``8080`` on the gateway machine ``192.168.178.1``,
|
||||||
|
the command line would be::
|
||||||
|
|
||||||
|
$ owncloudcmd —httpproxy http://192.168.178.1:8080 \
|
||||||
|
$HOME/media/music \
|
||||||
|
https://server/owncloud/remote.php/webdav/Music
|
||||||
|
|
||||||
|
``owncloudcmd`` will enquire user name and password, unless they have
|
||||||
|
been specified on the command line or ``-n`` (see `netrc(5)`) has been passed.
|
||||||
|
|
||||||
|
Using the legacy scheme, it would be::
|
||||||
|
|
||||||
|
$ owncloudcmd —httpproxy http://192.168.178.1:8080 \
|
||||||
|
$HOME/media/music \
|
||||||
|
ownclouds://server/owncloud/remote.php/webdav/Music
|
||||||
|
|
||||||
|
|
||||||
|
BUGS
|
||||||
|
====
|
||||||
|
Please report bugs at https://github.com/owncloud/client/issues.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
========
|
||||||
|
:manpage:`owncloud(1)`
|
|
@ -8,253 +8,6 @@ GenericName=Folder Sync
|
||||||
Icon=@APPLICATION_EXECUTABLE@
|
Icon=@APPLICATION_EXECUTABLE@
|
||||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||||
X-GNOME-Autostart-Delay=3
|
X-GNOME-Autostart-Delay=3
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -22,7 +22,7 @@
|
||||||
#define OVERLAY_GUID_WARNING L"{0960F096-F328-48A3-B746-276B1E3C3722}"
|
#define OVERLAY_GUID_WARNING L"{0960F096-F328-48A3-B746-276B1E3C3722}"
|
||||||
#define OVERLAY_GUID_WARNING_SHARED L"{0960F097-F328-48A3-B746-276B1E3C3722}"
|
#define OVERLAY_GUID_WARNING_SHARED L"{0960F097-F328-48A3-B746-276B1E3C3722}"
|
||||||
|
|
||||||
#define OVERLAY_GENERIC_NAME L"OC Overlay Handler"
|
#define OVERLAY_GENERIC_NAME L"ownCloud overlay handler"
|
||||||
|
|
||||||
// two spaces to put us ahead of the competition :/
|
// two spaces to put us ahead of the competition :/
|
||||||
#define OVERLAY_NAME_ERROR L" OCError"
|
#define OVERLAY_NAME_ERROR L" OCError"
|
||||||
|
@ -47,4 +47,4 @@
|
||||||
|
|
||||||
#define GET_FILE_OVERLAY_ID L"getFileIconId"
|
#define GET_FILE_OVERLAY_ID L"getFileIconId"
|
||||||
|
|
||||||
#define PORT 34001
|
#define PORT 34001
|
||||||
|
|
2
src/3rdparty/sqlite3/sqlite3.c
vendored
2
src/3rdparty/sqlite3/sqlite3.c
vendored
|
@ -180331,7 +180331,7 @@ static int sessionBindRow(
|
||||||
** iterator pIter points to to the SELECT and attempts to seek to the table
|
** iterator pIter points to to the SELECT and attempts to seek to the table
|
||||||
** entry. If a row is found, the SELECT statement left pointing at the row
|
** entry. If a row is found, the SELECT statement left pointing at the row
|
||||||
** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
|
** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
|
||||||
** has occured, the statement is reset and SQLITE_OK is returned. If an
|
** has occurred, the statement is reset and SQLITE_OK is returned. If an
|
||||||
** error occurs, the statement is reset and an SQLite error code is returned.
|
** error occurs, the statement is reset and an SQLite error code is returned.
|
||||||
**
|
**
|
||||||
** If this function returns SQLITE_ROW, the caller must eventually reset()
|
** If this function returns SQLITE_ROW, the caller must eventually reset()
|
||||||
|
|
|
@ -8,20 +8,29 @@ if(NOT TOKEN_AUTH_ONLY)
|
||||||
find_package(Qt5Keychain REQUIRED)
|
find_package(Qt5Keychain REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(NOT MSVC)
|
||||||
# Enable DEP & ASLR
|
if(NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "^(parisc|hppa)"))
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
|
if((CMAKE_CXX_COMPILER_ID MATCHES "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9))
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector --param=ssp-buffer-size=4")
|
||||||
elseif(UNIX AND NOT APPLE)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector --param=ssp-buffer-size=4")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong")
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-protector-strong")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
|
string(TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_LOWER)
|
||||||
if(CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)")
|
if(CMAKE_BUILD_TYPE_LOWER MATCHES "(release|relwithdebinfo|minsizerel)")
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_FORTIFY_SOURCE=2")
|
||||||
endif()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
# Enable DEP & ASLR
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
|
||||||
|
elseif(UNIX AND NOT APPLE)
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now")
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -31,7 +31,7 @@ endif()
|
||||||
|
|
||||||
if(NOT BUILD_LIBRARIES_ONLY)
|
if(NOT BUILD_LIBRARIES_ONLY)
|
||||||
add_executable(${cmd_NAME} ${cmd_SRC})
|
add_executable(${cmd_NAME} ${cmd_SRC})
|
||||||
qt5_use_modules(${cmd_NAME} Network Sql)
|
qt5_use_modules(${cmd_NAME} Network )
|
||||||
set_target_properties(${cmd_NAME} PROPERTIES
|
set_target_properties(${cmd_NAME} PROPERTIES
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||||
set_target_properties(${cmd_NAME} PROPERTIES
|
set_target_properties(${cmd_NAME} PROPERTIES
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
using namespace OCC;
|
using namespace OCC;
|
||||||
|
|
||||||
|
|
||||||
static void nullMessageHandler(QtMsgType, const char *)
|
static void nullMessageHandler(QtMsgType, const QMessageLogContext &, const QString &)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +333,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
csync_set_log_level(options.silent ? 1 : 11);
|
csync_set_log_level(options.silent ? 1 : 11);
|
||||||
if (options.silent) {
|
if (options.silent) {
|
||||||
qInstallMsgHandler(nullMessageHandler);
|
qInstallMessageHandler(nullMessageHandler);
|
||||||
} else {
|
} else {
|
||||||
qSetMessagePattern("%{time MM-dd hh:mm:ss:zzz} [ %{type} %{category} ]%{if-debug}\t[ %{function} ]%{endif}:\t%{message}");
|
qSetMessagePattern("%{time MM-dd hh:mm:ss:zzz} [ %{type} %{category} ]%{if-debug}\t[ %{function} ]%{endif}:\t%{message}");
|
||||||
}
|
}
|
||||||
|
@ -534,7 +534,7 @@ restart_sync:
|
||||||
engine.excludedFiles().addExcludeFilePath(systemExcludeFile);
|
engine.excludedFiles().addExcludeFilePath(systemExcludeFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!engine.excludedFiles().reloadExcludes()) {
|
if (!engine.excludedFiles().reloadExcludeFiles()) {
|
||||||
qFatal("Cannot load system exclude list or list supplied via --exclude");
|
qFatal("Cannot load system exclude list or list supplied via --exclude");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
#include "configfile.h"
|
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "simplesslerrorhandler.h"
|
#include "simplesslerrorhandler.h"
|
||||||
|
|
|
@ -344,6 +344,7 @@ void SqlQuery::bindValue(int pos, const QVariant &value)
|
||||||
break;
|
break;
|
||||||
case QVariant::UInt:
|
case QVariant::UInt:
|
||||||
case QVariant::LongLong:
|
case QVariant::LongLong:
|
||||||
|
case QVariant::ULongLong:
|
||||||
res = sqlite3_bind_int64(_stmt, pos, value.toLongLong());
|
res = sqlite3_bind_int64(_stmt, pos, value.toLongLong());
|
||||||
break;
|
break;
|
||||||
case QVariant::DateTime: {
|
case QVariant::DateTime: {
|
||||||
|
|
|
@ -45,7 +45,7 @@ Q_LOGGING_CATEGORY(lcDb, "sync.database", QtInfoMsg)
|
||||||
static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &query)
|
static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &query)
|
||||||
{
|
{
|
||||||
rec._path = query.baValue(0);
|
rec._path = query.baValue(0);
|
||||||
rec._inode = query.intValue(1);
|
rec._inode = query.int64Value(1);
|
||||||
rec._modtime = query.int64Value(2);
|
rec._modtime = query.int64Value(2);
|
||||||
rec._type = query.intValue(3);
|
rec._type = query.intValue(3);
|
||||||
rec._etag = query.baValue(4);
|
rec._etag = query.baValue(4);
|
||||||
|
|
|
@ -296,9 +296,7 @@ namespace {
|
||||||
|
|
||||||
QString description(quint64 value) const
|
QString description(quint64 value) const
|
||||||
{
|
{
|
||||||
return QCoreApplication::translate(
|
return QCoreApplication::translate("Utility", name, 0, value);
|
||||||
"Utility", name, 0, QCoreApplication::UnicodeUTF8,
|
|
||||||
value);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// QTBUG-3945 and issue #4855: QT_TRANSLATE_NOOP does not work with plural form because lupdate
|
// QTBUG-3945 and issue #4855: QT_TRANSLATE_NOOP does not work with plural form because lupdate
|
||||||
|
@ -520,11 +518,11 @@ void Utility::sortFilenames(QStringList &fileNames)
|
||||||
QCollator collator;
|
QCollator collator;
|
||||||
collator.setNumericMode(true);
|
collator.setNumericMode(true);
|
||||||
collator.setCaseSensitivity(Qt::CaseInsensitive);
|
collator.setCaseSensitivity(Qt::CaseInsensitive);
|
||||||
qSort(fileNames.begin(), fileNames.end(), collator);
|
std::sort(fileNames.begin(), fileNames.end(), collator);
|
||||||
}
|
}
|
||||||
|
|
||||||
QUrl Utility::concatUrlPath(const QUrl &url, const QString &concatPath,
|
QUrl Utility::concatUrlPath(const QUrl &url, const QString &concatPath,
|
||||||
const QList<QPair<QString, QString>> &queryItems)
|
const QUrlQuery &queryItems)
|
||||||
{
|
{
|
||||||
QString path = url.path();
|
QString path = url.path();
|
||||||
if (!concatPath.isEmpty()) {
|
if (!concatPath.isEmpty()) {
|
||||||
|
@ -540,9 +538,7 @@ QUrl Utility::concatUrlPath(const QUrl &url, const QString &concatPath,
|
||||||
|
|
||||||
QUrl tmpUrl = url;
|
QUrl tmpUrl = url;
|
||||||
tmpUrl.setPath(path);
|
tmpUrl.setPath(path);
|
||||||
if (queryItems.size() > 0) {
|
tmpUrl.setQuery(queryItems);
|
||||||
tmpUrl.setQueryItems(queryItems);
|
|
||||||
}
|
|
||||||
return tmpUrl;
|
return tmpUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QUrlQuery>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ namespace Utility {
|
||||||
* @param unit an optional unit that is appended if present.
|
* @param unit an optional unit that is appended if present.
|
||||||
* @return the formatted string.
|
* @return the formatted string.
|
||||||
*/
|
*/
|
||||||
OCSYNC_EXPORT QString compactFormatDouble(double value, int prec, const QString &unit = QString::null);
|
OCSYNC_EXPORT QString compactFormatDouble(double value, int prec, const QString &unit = QString());
|
||||||
|
|
||||||
// porting methods
|
// porting methods
|
||||||
OCSYNC_EXPORT QString escape(const QString &);
|
OCSYNC_EXPORT QString escape(const QString &);
|
||||||
|
@ -175,7 +176,7 @@ namespace Utility {
|
||||||
/** Appends concatPath and queryItems to the url */
|
/** Appends concatPath and queryItems to the url */
|
||||||
OCSYNC_EXPORT QUrl concatUrlPath(
|
OCSYNC_EXPORT QUrl concatUrlPath(
|
||||||
const QUrl &url, const QString &concatPath,
|
const QUrl &url, const QString &concatPath,
|
||||||
const QList<QPair<QString, QString>> &queryItems = (QList<QPair<QString, QString>>()));
|
const QUrlQuery &queryItems = {});
|
||||||
|
|
||||||
/** Returns a new settings pre-set in a specific group. The Settings will be created
|
/** Returns a new settings pre-set in a specific group. The Settings will be created
|
||||||
with the given parent. If no parent is specified, the caller must destroy the settings */
|
with the given parent. If no parent is specified, the caller must destroy the settings */
|
||||||
|
|
|
@ -58,7 +58,7 @@ bool hasLaunchOnStartup_private(const QString &)
|
||||||
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
||||||
CFURLRef itemUrlRef = NULL;
|
CFURLRef itemUrlRef = NULL;
|
||||||
|
|
||||||
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr) {
|
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
|
||||||
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
||||||
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
||||||
returnValue = true;
|
returnValue = true;
|
||||||
|
@ -100,7 +100,7 @@ void setLaunchOnStartup_private(const QString &appName, const QString &guiName,
|
||||||
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(itemsArray, i);
|
||||||
CFURLRef itemUrlRef = NULL;
|
CFURLRef itemUrlRef = NULL;
|
||||||
|
|
||||||
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr) {
|
if (LSSharedFileListItemResolve(item, 0, &itemUrlRef, NULL) == noErr && itemUrlRef) {
|
||||||
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
CFStringRef itemUrlString = CFURLGetString(itemUrlRef);
|
||||||
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
if (CFStringCompare(itemUrlString, appUrlRefString, 0) == kCFCompareEqualTo) {
|
||||||
LSSharedFileListItemRemove(loginItems, item); // remove it!
|
LSSharedFileListItemRemove(loginItems, item); // remove it!
|
||||||
|
|
|
@ -77,7 +77,7 @@ int csync_update(CSYNC *ctx) {
|
||||||
|
|
||||||
csync_memstat_check();
|
csync_memstat_check();
|
||||||
|
|
||||||
if (!ctx->excludes) {
|
if (!ctx->exclude_traversal_fn) {
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "No exclude file loaded or defined!");
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_INFO, "No exclude file loaded or defined!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,14 +211,14 @@ static int _csync_treewalk_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||||
|
|
||||||
if (other_file_it == other_tree->cend()) {
|
if (other_file_it == other_tree->cend()) {
|
||||||
/* Check the renamed path as well. */
|
/* Check the renamed path as well. */
|
||||||
QByteArray renamed_path = csync_rename_adjust_path(ctx, cur->path);
|
QByteArray renamed_path = csync_rename_adjust_parent_path(ctx, cur->path);
|
||||||
if (renamed_path != cur->path)
|
if (renamed_path != cur->path)
|
||||||
other_file_it = other_tree->find(renamed_path);
|
other_file_it = other_tree->find(renamed_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (other_file_it == other_tree->cend()) {
|
if (other_file_it == other_tree->cend()) {
|
||||||
/* Check the source path as well. */
|
/* Check the source path as well. */
|
||||||
QByteArray renamed_path = csync_rename_adjust_path_source(ctx, cur->path);
|
QByteArray renamed_path = csync_rename_adjust_parent_path_source(ctx, cur->path);
|
||||||
if (renamed_path != cur->path)
|
if (renamed_path != cur->path)
|
||||||
other_file_it = other_tree->find(renamed_path);
|
other_file_it = other_tree->find(renamed_path);
|
||||||
}
|
}
|
||||||
|
@ -314,6 +314,9 @@ int csync_s::reinitialize() {
|
||||||
local.files.clear();
|
local.files.clear();
|
||||||
remote.files.clear();
|
remote.files.clear();
|
||||||
|
|
||||||
|
renames.folder_renamed_from.clear();
|
||||||
|
renames.folder_renamed_to.clear();
|
||||||
|
|
||||||
local_discovery_style = LocalDiscoveryStyle::FilesystemOnly;
|
local_discovery_style = LocalDiscoveryStyle::FilesystemOnly;
|
||||||
locally_touched_dirs.clear();
|
locally_touched_dirs.clear();
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,8 @@ enum csync_status_codes_e {
|
||||||
CSYNC_STATUS_INDIVIDUAL_STAT_FAILED,
|
CSYNC_STATUS_INDIVIDUAL_STAT_FAILED,
|
||||||
CSYNC_STATUS_FORBIDDEN,
|
CSYNC_STATUS_FORBIDDEN,
|
||||||
CSYNC_STATUS_INDIVIDUAL_TOO_DEEP,
|
CSYNC_STATUS_INDIVIDUAL_TOO_DEEP,
|
||||||
CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE
|
CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE,
|
||||||
|
CSYNC_STATUS_INDIVIDUAL_CANNOT_ENCODE
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum csync_status_codes_e CSYNC_STATUS;
|
typedef enum csync_status_codes_e CSYNC_STATUS;
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
@ -50,25 +51,6 @@
|
||||||
#define CSYNC_LOG_CATEGORY_NAME "csync.exclude"
|
#define CSYNC_LOG_CATEGORY_NAME "csync.exclude"
|
||||||
#include "csync_log.h"
|
#include "csync_log.h"
|
||||||
|
|
||||||
#ifndef WITH_TESTING
|
|
||||||
static
|
|
||||||
#endif
|
|
||||||
int _csync_exclude_add(c_strlist_t **inList, const char *string) {
|
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
// We never want duplicates, so check whether the string is already
|
|
||||||
// in the list first.
|
|
||||||
if (*inList) {
|
|
||||||
for (i = 0; i < (*inList)->count; ++i) {
|
|
||||||
char *pattern = (*inList)->vector[i];
|
|
||||||
if (c_streq(pattern, string)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c_strlist_add_grow(inList, string);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Expands C-like escape sequences.
|
/** Expands C-like escape sequences.
|
||||||
*
|
*
|
||||||
* The returned string is heap-allocated and owned by the caller.
|
* The returned string is heap-allocated and owned by the caller.
|
||||||
|
@ -87,7 +69,6 @@ static const char *csync_exclude_expand_escapes(const char * input)
|
||||||
case '\'': out[o++] = '\''; break;
|
case '\'': out[o++] = '\''; break;
|
||||||
case '"': out[o++] = '"'; break;
|
case '"': out[o++] = '"'; break;
|
||||||
case '?': out[o++] = '?'; break;
|
case '?': out[o++] = '?'; break;
|
||||||
case '\\': out[o++] = '\\'; break;
|
|
||||||
case '#': out[o++] = '#'; break;
|
case '#': out[o++] = '#'; break;
|
||||||
case 'a': out[o++] = '\a'; break;
|
case 'a': out[o++] = '\a'; break;
|
||||||
case 'b': out[o++] = '\b'; break;
|
case 'b': out[o++] = '\b'; break;
|
||||||
|
@ -97,6 +78,9 @@ static const char *csync_exclude_expand_escapes(const char * input)
|
||||||
case 't': out[o++] = '\t'; break;
|
case 't': out[o++] = '\t'; break;
|
||||||
case 'v': out[o++] = '\v'; break;
|
case 'v': out[o++] = '\v'; break;
|
||||||
default:
|
default:
|
||||||
|
// '\*' '\?' '\[' '\\' will be processed during regex translation
|
||||||
|
// '\\' is intentionally not expanded here (to avoid '\\*' and '\*'
|
||||||
|
// ending up meaning the same thing)
|
||||||
out[o++] = input[i];
|
out[o++] = input[i];
|
||||||
out[o++] = input[i+1];
|
out[o++] = input[i+1];
|
||||||
break;
|
break;
|
||||||
|
@ -109,7 +93,8 @@ static const char *csync_exclude_expand_escapes(const char * input)
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int csync_exclude_load(const char *fname, c_strlist_t **list) {
|
/** Loads patterns from a file and adds them to excludes */
|
||||||
|
int csync_exclude_load(const char *fname, QList<QByteArray> *excludes) {
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
@ -162,14 +147,9 @@ int csync_exclude_load(const char *fname, c_strlist_t **list) {
|
||||||
buf[i] = '\0';
|
buf[i] = '\0';
|
||||||
if (*entry != '#') {
|
if (*entry != '#') {
|
||||||
const char *unescaped = csync_exclude_expand_escapes(entry);
|
const char *unescaped = csync_exclude_expand_escapes(entry);
|
||||||
rc = _csync_exclude_add(list, unescaped);
|
excludes->append(unescaped);
|
||||||
if( rc == 0 ) {
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", unescaped);
|
||||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "Adding entry: %s", unescaped);
|
|
||||||
}
|
|
||||||
SAFE_FREE(unescaped);
|
SAFE_FREE(unescaped);
|
||||||
if (rc < 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entry = buf + i + 1;
|
entry = buf + i + 1;
|
||||||
|
@ -193,6 +173,11 @@ static const char *win_reserved_words_4[] = {
|
||||||
};
|
};
|
||||||
static const char *win_reserved_words_n[] = { "CLOCK$", "$Recycle.Bin" };
|
static const char *win_reserved_words_n[] = { "CLOCK$", "$Recycle.Bin" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if filename is considered reserved by Windows
|
||||||
|
* @param file_name filename
|
||||||
|
* @return true if file is reserved, false otherwise
|
||||||
|
*/
|
||||||
bool csync_is_windows_reserved_word(const char *filename)
|
bool csync_is_windows_reserved_word(const char *filename)
|
||||||
{
|
{
|
||||||
size_t len_filename = strlen(filename);
|
size_t len_filename = strlen(filename);
|
||||||
|
@ -233,14 +218,12 @@ bool csync_is_windows_reserved_word(const char *filename)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const char *path, int filetype, bool check_leading_dirs) {
|
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(const char *path)
|
||||||
size_t i = 0;
|
{
|
||||||
const char *bname = NULL;
|
const char *bname = NULL;
|
||||||
size_t blen = 0;
|
size_t blen = 0;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
|
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
|
||||||
CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED;
|
|
||||||
c_strlist_t *path_components = NULL;
|
|
||||||
|
|
||||||
/* split up the path */
|
/* split up the path */
|
||||||
bname = strrchr(path, '/');
|
bname = strrchr(path, '/');
|
||||||
|
@ -322,10 +305,12 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* We create a desktop.ini on Windows for the sidebar icon, make sure we don't sync them. */
|
/* We create a desktop.ini on Windows for the sidebar icon, make sure we don't sync them. */
|
||||||
rc = csync_fnmatch("Desktop.ini", bname, 0);
|
if (blen == 11) {
|
||||||
if (rc == 0) {
|
rc = csync_fnmatch("Desktop.ini", bname, 0);
|
||||||
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
if (rc == 0) {
|
||||||
goto out;
|
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!OCC::Utility::shouldUploadConflictFiles()) {
|
if (!OCC::Utility::shouldUploadConflictFiles()) {
|
||||||
|
@ -335,192 +320,393 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( ! excludes ) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_leading_dirs) {
|
|
||||||
/* Build a list of path components to check. */
|
|
||||||
path_components = c_strlist_new(32);
|
|
||||||
char *path_split = strdup(path);
|
|
||||||
size_t len = strlen(path_split);
|
|
||||||
for (i = len; ; --i) {
|
|
||||||
// read backwards until a path separator is found
|
|
||||||
if (i != 0 && path_split[i-1] != '/') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
|
|
||||||
if (path_split[i] != 0) {
|
|
||||||
c_strlist_add_grow(&path_components, path_split + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
|
|
||||||
path_split[i-1] = '\0';
|
|
||||||
c_strlist_add_grow(&path_components, path_split);
|
|
||||||
}
|
|
||||||
SAFE_FREE(path_split);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loop over all exclude patterns and evaluate the given path */
|
|
||||||
for (i = 0; match == CSYNC_NOT_EXCLUDED && i < excludes->count; i++) {
|
|
||||||
bool match_dirs_only = false;
|
|
||||||
char *pattern = excludes->vector[i];
|
|
||||||
|
|
||||||
type = CSYNC_FILE_EXCLUDE_LIST;
|
|
||||||
if (!pattern[0]) { /* empty pattern */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* Excludes starting with ']' means it can be cleanup */
|
|
||||||
if (pattern[0] == ']') {
|
|
||||||
++pattern;
|
|
||||||
if (filetype == CSYNC_FTW_TYPE_FILE) {
|
|
||||||
type = CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Check if the pattern applies to pathes only. */
|
|
||||||
if (pattern[strlen(pattern)-1] == '/') {
|
|
||||||
if (!check_leading_dirs && filetype == CSYNC_FTW_TYPE_FILE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
match_dirs_only = true;
|
|
||||||
pattern[strlen(pattern)-1] = '\0'; /* Cut off the slash */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if the pattern contains a / and if, compare to the whole path */
|
|
||||||
if (strchr(pattern, '/')) {
|
|
||||||
rc = csync_fnmatch(pattern, path, FNM_PATHNAME);
|
|
||||||
if( rc == 0 ) {
|
|
||||||
match = type;
|
|
||||||
}
|
|
||||||
/* if the pattern requires a dir, but path is not, its still not excluded. */
|
|
||||||
if (match_dirs_only && filetype != CSYNC_FTW_TYPE_DIR) {
|
|
||||||
match = CSYNC_NOT_EXCLUDED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if still not excluded, check each component and leading directory of the path */
|
|
||||||
if (match == CSYNC_NOT_EXCLUDED && check_leading_dirs) {
|
|
||||||
size_t j = 0;
|
|
||||||
if (match_dirs_only && filetype == CSYNC_FTW_TYPE_FILE) {
|
|
||||||
j = 1; // skip the first entry, which is bname
|
|
||||||
}
|
|
||||||
for (; j < path_components->count; ++j) {
|
|
||||||
rc = csync_fnmatch(pattern, path_components->vector[j], 0);
|
|
||||||
if (rc == 0) {
|
|
||||||
match = type;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (match == CSYNC_NOT_EXCLUDED && !check_leading_dirs) {
|
|
||||||
rc = csync_fnmatch(pattern, bname, 0);
|
|
||||||
if (rc == 0) {
|
|
||||||
match = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (match_dirs_only) {
|
|
||||||
/* restore the '/' */
|
|
||||||
pattern[strlen(pattern)] = '/';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c_strlist_destroy(path_components);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Only for bnames (not paths) */
|
|
||||||
static QString convertToBnameRegexpSyntax(QString exclude)
|
using namespace OCC;
|
||||||
|
|
||||||
|
ExcludedFiles::ExcludedFiles()
|
||||||
{
|
{
|
||||||
QString s = QRegularExpression::escape(exclude).replace("\\*", ".*").replace("\\?", ".");
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void csync_exclude_traversal_prepare(CSYNC *ctx)
|
ExcludedFiles::~ExcludedFiles()
|
||||||
{
|
{
|
||||||
ctx->parsed_traversal_excludes.prepare(ctx->excludes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void csync_s::TraversalExcludes::prepare(c_strlist_t *excludes)
|
void ExcludedFiles::addExcludeFilePath(const QString &path)
|
||||||
{
|
{
|
||||||
c_strlist_destroy(list_patterns_fnmatch);
|
_excludeFiles.insert(path);
|
||||||
list_patterns_fnmatch = nullptr;
|
}
|
||||||
|
|
||||||
// Start out with regexes that would match nothing
|
void ExcludedFiles::addManualExclude(const QByteArray &expr)
|
||||||
QString exclude_only = "a^";
|
{
|
||||||
QString exclude_and_remove = "a^";
|
_manualExcludes.append(expr);
|
||||||
|
_allExcludes.append(expr);
|
||||||
|
prepare();
|
||||||
|
}
|
||||||
|
|
||||||
size_t exclude_count = excludes ? excludes->count : 0;
|
void ExcludedFiles::clearManualExcludes()
|
||||||
for (unsigned int i = 0; i < exclude_count; i++) {
|
{
|
||||||
char *exclude = excludes->vector[i];
|
_manualExcludes.clear();
|
||||||
QString *builderToUse = & exclude_only;
|
reloadExcludeFiles();
|
||||||
if (exclude[0] == '\n') continue; // empty line
|
}
|
||||||
if (exclude[0] == '\r') continue; // empty line
|
|
||||||
|
|
||||||
/* If an exclude entry contains some fnmatch-ish characters, we use the C-style codepath without QRegularEpression */
|
bool ExcludedFiles::reloadExcludeFiles()
|
||||||
if (strchr(exclude, '/') || strchr(exclude, '[') || strchr(exclude, '{') || strchr(exclude, '\\')) {
|
{
|
||||||
_csync_exclude_add(&list_patterns_fnmatch, exclude);
|
_allExcludes.clear();
|
||||||
continue;
|
bool success = true;
|
||||||
}
|
foreach (const QString &file, _excludeFiles) {
|
||||||
|
if (csync_exclude_load(file.toUtf8(), &_allExcludes) < 0)
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
_allExcludes.append(_manualExcludes);
|
||||||
|
prepare();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/* Those will attempt to use QRegularExpression */
|
bool ExcludedFiles::isExcluded(
|
||||||
if (exclude[0] == ']'){
|
const QString &filePath,
|
||||||
exclude++;
|
const QString &basePath,
|
||||||
builderToUse = &exclude_and_remove;
|
bool excludeHidden) const
|
||||||
}
|
{
|
||||||
if (builderToUse->size() > 0) {
|
if (!filePath.startsWith(basePath, Utility::fsCasePreserving() ? Qt::CaseInsensitive : Qt::CaseSensitive)) {
|
||||||
builderToUse->append("|");
|
// Mark paths we're not responsible for as excluded...
|
||||||
}
|
return true;
|
||||||
builderToUse->append(convertToBnameRegexpSyntax(exclude));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString pattern = "^(" + exclude_only + ")$|^(" + exclude_and_remove + ")$";
|
if (excludeHidden) {
|
||||||
regexp_exclude.setPattern(pattern);
|
QString path = filePath;
|
||||||
QRegularExpression::PatternOptions patternOptions = QRegularExpression::OptimizeOnFirstUsageOption;
|
// Check all path subcomponents, but to *not* check the base path:
|
||||||
if (OCC::Utility::fsCasePreserving())
|
// We do want to be able to sync with a hidden folder as the target.
|
||||||
patternOptions |= QRegularExpression::CaseInsensitiveOption;
|
while (path.size() > basePath.size()) {
|
||||||
regexp_exclude.setPatternOptions(patternOptions);
|
QFileInfo fi(path);
|
||||||
regexp_exclude.optimize();
|
if (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.'))) {
|
||||||
}
|
return true;
|
||||||
|
|
||||||
CSYNC_EXCLUDE_TYPE csync_excluded_traversal(CSYNC *ctx, const char *path, int filetype) {
|
|
||||||
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
|
|
||||||
|
|
||||||
/* Check only static patterns and only with the reduced list which is empty usually */
|
|
||||||
match = _csync_excluded_common(ctx->parsed_traversal_excludes.list_patterns_fnmatch, path, filetype, false);
|
|
||||||
if (match != CSYNC_NOT_EXCLUDED) {
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx->excludes) {
|
|
||||||
/* Now check with our optimized regexps */
|
|
||||||
const char *bname = NULL;
|
|
||||||
/* split up the path */
|
|
||||||
bname = strrchr(path, '/');
|
|
||||||
if (bname) {
|
|
||||||
bname += 1; // don't include the /
|
|
||||||
} else {
|
|
||||||
bname = path;
|
|
||||||
}
|
|
||||||
QString p = QString::fromUtf8(bname);
|
|
||||||
auto m = ctx->parsed_traversal_excludes.regexp_exclude.match(p);
|
|
||||||
if (m.hasMatch()) {
|
|
||||||
if (!m.captured(1).isEmpty()) {
|
|
||||||
match = CSYNC_FILE_EXCLUDE_LIST;
|
|
||||||
} else if (!m.captured(2).isEmpty()) {
|
|
||||||
match = CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the parent path
|
||||||
|
path = fi.absolutePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFileInfo fi(filePath);
|
||||||
|
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
|
||||||
|
if (fi.isDir()) {
|
||||||
|
type = CSYNC_FTW_TYPE_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString relativePath = filePath.mid(basePath.size());
|
||||||
|
if (relativePath.endsWith(QLatin1Char('/'))) {
|
||||||
|
relativePath.chop(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullPatternMatch(relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSYNC_EXCLUDE_TYPE ExcludedFiles::traversalPatternMatch(const char *path, int filetype) const
|
||||||
|
{
|
||||||
|
auto match = _csync_excluded_common(path);
|
||||||
|
if (match != CSYNC_NOT_EXCLUDED)
|
||||||
|
return match;
|
||||||
|
if (_allExcludes.isEmpty())
|
||||||
|
return CSYNC_NOT_EXCLUDED;
|
||||||
|
|
||||||
|
// Check the bname part of the path to see whether the full
|
||||||
|
// regex should be run.
|
||||||
|
|
||||||
|
const char *bname = strrchr(path, '/');
|
||||||
|
if (bname) {
|
||||||
|
bname += 1; // don't include the /
|
||||||
|
} else {
|
||||||
|
bname = path;
|
||||||
|
}
|
||||||
|
QString bnameStr = QString::fromUtf8(bname);
|
||||||
|
|
||||||
|
QRegularExpressionMatch m;
|
||||||
|
if (filetype == CSYNC_FTW_TYPE_DIR) {
|
||||||
|
m = _bnameActivationRegexDir.match(bnameStr);
|
||||||
|
} else {
|
||||||
|
m = _bnameActivationRegexFile.match(bnameStr);
|
||||||
|
}
|
||||||
|
if (!m.hasMatch())
|
||||||
|
return match;
|
||||||
|
|
||||||
|
// Now run the full match
|
||||||
|
|
||||||
|
QString pathStr = QString::fromUtf8(path);
|
||||||
|
if (filetype == CSYNC_FTW_TYPE_DIR) {
|
||||||
|
m = _fullRegexDir.match(pathStr);
|
||||||
|
} else {
|
||||||
|
m = _fullRegexFile.match(pathStr);
|
||||||
|
}
|
||||||
|
if (m.hasMatch()) {
|
||||||
|
if (!m.captured(1).isEmpty()) {
|
||||||
|
match = CSYNC_FILE_EXCLUDE_LIST;
|
||||||
|
} else if (!m.captured(2).isEmpty()) {
|
||||||
|
match = CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path, int filetype) {
|
CSYNC_EXCLUDE_TYPE ExcludedFiles::fullPatternMatch(const char *path, int filetype) const
|
||||||
return _csync_excluded_common(excludes, path, filetype, true);
|
{
|
||||||
|
auto match = _csync_excluded_common(path);
|
||||||
|
if (match != CSYNC_NOT_EXCLUDED)
|
||||||
|
return match;
|
||||||
|
if (_allExcludes.isEmpty())
|
||||||
|
return CSYNC_NOT_EXCLUDED;
|
||||||
|
|
||||||
|
QString p = QString::fromUtf8(path);
|
||||||
|
QRegularExpressionMatch m;
|
||||||
|
if (filetype == CSYNC_FTW_TYPE_DIR) {
|
||||||
|
m = _fullRegexDir.match(p);
|
||||||
|
} else {
|
||||||
|
m = _fullRegexFile.match(p);
|
||||||
|
}
|
||||||
|
if (m.hasMatch()) {
|
||||||
|
if (!m.captured(1).isEmpty()) {
|
||||||
|
return CSYNC_FILE_EXCLUDE_LIST;
|
||||||
|
} else if (!m.captured(2).isEmpty()) {
|
||||||
|
return CSYNC_FILE_EXCLUDE_AND_REMOVE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CSYNC_NOT_EXCLUDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto ExcludedFiles::csyncTraversalMatchFun() const
|
||||||
|
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, int filetype)>
|
||||||
|
{
|
||||||
|
return [this](const char *path, int filetype) { return this->traversalPatternMatch(path, filetype); };
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString convertToRegexpSyntax(QString exclude)
|
||||||
|
{
|
||||||
|
// Translate *, ?, [...] to their regex variants.
|
||||||
|
// The escape sequences \*, \?, \[. \\ have a special meaning,
|
||||||
|
// the other ones have already been expanded before
|
||||||
|
// (like "\\n" being replaced by "\n").
|
||||||
|
//
|
||||||
|
// QString being UTF-16 makes unicode-correct escaping tricky.
|
||||||
|
// If we escaped each UTF-16 code unit we'd end up splitting 4-byte
|
||||||
|
// code points. To avoid problems we delegate as much work as possible to
|
||||||
|
// QRegularExpression::escape(): It always receives as long a sequence
|
||||||
|
// as code units as possible.
|
||||||
|
QString regex;
|
||||||
|
int i = 0;
|
||||||
|
int charsToEscape = 0;
|
||||||
|
auto flush = [&]() {
|
||||||
|
regex.append(QRegularExpression::escape(exclude.mid(i - charsToEscape, charsToEscape)));
|
||||||
|
charsToEscape = 0;
|
||||||
|
};
|
||||||
|
auto len = exclude.size();
|
||||||
|
for (; i < len; ++i) {
|
||||||
|
switch (exclude[i].unicode()) {
|
||||||
|
case '*':
|
||||||
|
flush();
|
||||||
|
regex.append("[^/]*");
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
flush();
|
||||||
|
regex.append("[^/]");
|
||||||
|
break;
|
||||||
|
case '[': {
|
||||||
|
flush();
|
||||||
|
// Find the end of the bracket expression
|
||||||
|
auto j = i + 1;
|
||||||
|
for (; j < len; ++j) {
|
||||||
|
if (exclude[j] == ']')
|
||||||
|
break;
|
||||||
|
if (j != len - 1 && exclude[j] == '\\' && exclude[j + 1] == ']')
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
if (j == len) {
|
||||||
|
// no matching ], just insert the escaped [
|
||||||
|
regex.append("\\[");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Translate [! to [^
|
||||||
|
QString bracketExpr = exclude.mid(i, j - i + 1);
|
||||||
|
if (bracketExpr.startsWith("[!"))
|
||||||
|
bracketExpr[1] = '^';
|
||||||
|
regex.append(bracketExpr);
|
||||||
|
i = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '\\':
|
||||||
|
flush();
|
||||||
|
if (i == len - 1) {
|
||||||
|
regex.append("\\\\");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// '\*' -> '\*', but '\z' -> '\\z'
|
||||||
|
switch (exclude[i + 1].unicode()) {
|
||||||
|
case '*':
|
||||||
|
case '?':
|
||||||
|
case '[':
|
||||||
|
case '\\':
|
||||||
|
regex.append(QRegularExpression::escape(exclude.mid(i + 1, 1)));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
charsToEscape += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
++charsToEscape;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flush();
|
||||||
|
return regex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExcludedFiles::prepare()
|
||||||
|
{
|
||||||
|
// Build regular expressions for the different cases.
|
||||||
|
//
|
||||||
|
// To compose the _bnameActivationRegex and _fullRegex patterns we
|
||||||
|
// collect several subgroups of patterns here.
|
||||||
|
//
|
||||||
|
// * The "full" group will contain all patterns that contain a non-trailing
|
||||||
|
// slash. They only make sense in the fullRegex.
|
||||||
|
// * The "bname" group contains all patterns without a non-trailing slash.
|
||||||
|
// These need separate handling in the _fullRegex (slash-containing
|
||||||
|
// patterns must be anchored to the front, these don't need it)
|
||||||
|
// * The "bnameTrigger" group contains the bname part of all patterns in the
|
||||||
|
// "full" group. These and the "bname" group become _bnameActivationRegex.
|
||||||
|
//
|
||||||
|
// To complicate matters, the exclude patterns have two binary attributes
|
||||||
|
// meaning we'll end up with 4 variants:
|
||||||
|
// * "]" patterns mean "EXCLUDE_AND_REMOVE", they get collected in the
|
||||||
|
// pattern strings ending in "Remove". The others go to "Keep".
|
||||||
|
// * trailing-slash patterns match directories only. They get collected
|
||||||
|
// in the pattern strings saying "Dir", the others go into "FileDir"
|
||||||
|
// because they match files and directories.
|
||||||
|
|
||||||
|
QString fullFileDirKeep;
|
||||||
|
QString fullFileDirRemove;
|
||||||
|
QString fullDirKeep;
|
||||||
|
QString fullDirRemove;
|
||||||
|
|
||||||
|
QString bnameFileDirKeep;
|
||||||
|
QString bnameFileDirRemove;
|
||||||
|
QString bnameDirKeep;
|
||||||
|
QString bnameDirRemove;
|
||||||
|
|
||||||
|
QString bnameTriggerFileDir;
|
||||||
|
QString bnameTriggerDir;
|
||||||
|
|
||||||
|
auto regexAppend = [](QString &fileDirPattern, QString &dirPattern, const QString &appendMe, bool dirOnly) {
|
||||||
|
QString &pattern = dirOnly ? dirPattern : fileDirPattern;
|
||||||
|
if (!pattern.isEmpty())
|
||||||
|
pattern.append("|");
|
||||||
|
pattern.append(appendMe);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto exclude : _allExcludes) {
|
||||||
|
if (exclude[0] == '\n')
|
||||||
|
continue; // empty line
|
||||||
|
if (exclude[0] == '\r')
|
||||||
|
continue; // empty line
|
||||||
|
|
||||||
|
bool matchDirOnly = exclude.endsWith('/');
|
||||||
|
if (matchDirOnly)
|
||||||
|
exclude = exclude.left(exclude.size() - 1);
|
||||||
|
|
||||||
|
bool removeExcluded = (exclude[0] == ']');
|
||||||
|
if (removeExcluded)
|
||||||
|
exclude = exclude.mid(1);
|
||||||
|
|
||||||
|
bool fullPath = exclude.contains('/');
|
||||||
|
|
||||||
|
/* Use QRegularExpression, append to the right pattern */
|
||||||
|
auto &bnameFileDir = removeExcluded ? bnameFileDirRemove : bnameFileDirKeep;
|
||||||
|
auto &bnameDir = removeExcluded ? bnameDirRemove : bnameDirKeep;
|
||||||
|
auto &fullFileDir = removeExcluded ? fullFileDirRemove : fullFileDirKeep;
|
||||||
|
auto &fullDir = removeExcluded ? fullDirRemove : fullDirKeep;
|
||||||
|
|
||||||
|
auto regexExclude = convertToRegexpSyntax(QString::fromUtf8(exclude));
|
||||||
|
if (!fullPath) {
|
||||||
|
regexAppend(bnameFileDir, bnameDir, regexExclude, matchDirOnly);
|
||||||
|
} else {
|
||||||
|
regexAppend(fullFileDir, fullDir, regexExclude, matchDirOnly);
|
||||||
|
|
||||||
|
// for activation, trigger on the 'bname' part of the full pattern
|
||||||
|
auto bnameExclude = exclude.mid(exclude.lastIndexOf('/') + 1);
|
||||||
|
auto regexBname = convertToRegexpSyntax(bnameExclude);
|
||||||
|
regexAppend(bnameTriggerFileDir, bnameTriggerDir, regexBname, matchDirOnly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The empty pattern would match everything - change it to match-nothing
|
||||||
|
auto emptyMatchNothing = [](QString &pattern) {
|
||||||
|
if (pattern.isEmpty())
|
||||||
|
pattern = "a^";
|
||||||
|
};
|
||||||
|
emptyMatchNothing(fullFileDirKeep);
|
||||||
|
emptyMatchNothing(fullFileDirRemove);
|
||||||
|
emptyMatchNothing(fullDirKeep);
|
||||||
|
emptyMatchNothing(fullDirRemove);
|
||||||
|
|
||||||
|
emptyMatchNothing(bnameFileDirKeep);
|
||||||
|
emptyMatchNothing(bnameFileDirRemove);
|
||||||
|
emptyMatchNothing(bnameDirKeep);
|
||||||
|
emptyMatchNothing(bnameDirRemove);
|
||||||
|
|
||||||
|
emptyMatchNothing(bnameTriggerFileDir);
|
||||||
|
emptyMatchNothing(bnameTriggerDir);
|
||||||
|
|
||||||
|
// The bname activation regexe is applied to the bname only, so must be
|
||||||
|
// anchored in the beginning and in the end. It has the explicit triggers
|
||||||
|
// plus the bname-only patterns. Here we don't care about the remove/keep
|
||||||
|
// distinction.
|
||||||
|
_bnameActivationRegexFile.setPattern(
|
||||||
|
"^(?:" + bnameFileDirKeep + "|" + bnameFileDirRemove + "|" + bnameTriggerFileDir + ")$");
|
||||||
|
_bnameActivationRegexDir.setPattern(
|
||||||
|
"^(?:" + bnameFileDirKeep + "|" + bnameFileDirRemove
|
||||||
|
+ "|" + bnameDirKeep + "|" + bnameFileDirRemove
|
||||||
|
+ "|" + bnameTriggerFileDir + "|" + bnameTriggerDir + ")$");
|
||||||
|
|
||||||
|
// The full regex has two captures, it's basic form is "(...)|(...)". The first
|
||||||
|
// capture has the keep/exclude-only patterns, the second the remove/exclude-and-remove
|
||||||
|
// patterns.
|
||||||
|
_fullRegexFile.setPattern(
|
||||||
|
QLatin1String("(")
|
||||||
|
// Full patterns are anchored to the beginning
|
||||||
|
+ "^(?:" + fullFileDirKeep + ")(?:$|/)" + "|"
|
||||||
|
// Simple bname patterns can be any path component
|
||||||
|
+ "(?:^|/)(?:" + bnameFileDirKeep + ")(?:$|/)" + "|"
|
||||||
|
// When checking a file for exclusion we must check all parent paths
|
||||||
|
// against the dir-only patterns as well.
|
||||||
|
+ "(?:^|/)(?:" + bnameDirKeep + ")/"
|
||||||
|
+ ")|("
|
||||||
|
+ "^(?:" + fullFileDirRemove + ")(?:$|/)" + "|"
|
||||||
|
+ "(?:^|/)(?:" + bnameFileDirRemove + ")(?:$|/)" + "|"
|
||||||
|
+ "(?:^|/)(?:" + bnameDirRemove + ")/"
|
||||||
|
+ ")");
|
||||||
|
_fullRegexDir.setPattern(
|
||||||
|
QLatin1String("(")
|
||||||
|
+ "^(?:" + fullFileDirKeep + "|" + fullDirKeep + ")(?:$|/)" + "|"
|
||||||
|
+ "(?:^|/)(?:" + bnameFileDirKeep + "|" + bnameDirKeep + ")(?:$|/)"
|
||||||
|
+ ")|("
|
||||||
|
+ "^(?:" + fullFileDirRemove + "|" + fullDirRemove + ")(?:$|/)" + "|"
|
||||||
|
+ "(?:^|/)(?:" + bnameFileDirRemove + "|" + bnameDirRemove + ")(?:$|/)"
|
||||||
|
+ ")");
|
||||||
|
|
||||||
|
QRegularExpression::PatternOptions patternOptions = QRegularExpression::NoPatternOption;
|
||||||
|
if (OCC::Utility::fsCasePreserving())
|
||||||
|
patternOptions |= QRegularExpression::CaseInsensitiveOption;
|
||||||
|
_bnameActivationRegexFile.setPatternOptions(patternOptions);
|
||||||
|
_bnameActivationRegexFile.optimize();
|
||||||
|
_bnameActivationRegexDir.setPatternOptions(patternOptions);
|
||||||
|
_bnameActivationRegexDir.optimize();
|
||||||
|
_fullRegexFile.setPatternOptions(patternOptions);
|
||||||
|
_fullRegexFile.optimize();
|
||||||
|
_fullRegexDir.setPatternOptions(patternOptions);
|
||||||
|
_fullRegexDir.optimize();
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,13 @@
|
||||||
|
|
||||||
#include "ocsynclib.h"
|
#include "ocsynclib.h"
|
||||||
|
|
||||||
|
#include "csync.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRegularExpression>
|
||||||
|
|
||||||
enum csync_exclude_type_e {
|
enum csync_exclude_type_e {
|
||||||
CSYNC_NOT_EXCLUDED = 0,
|
CSYNC_NOT_EXCLUDED = 0,
|
||||||
CSYNC_FILE_SILENTLY_EXCLUDED,
|
CSYNC_FILE_SILENTLY_EXCLUDED,
|
||||||
|
@ -33,67 +40,156 @@ enum csync_exclude_type_e {
|
||||||
CSYNC_FILE_EXCLUDE_LONG_FILENAME,
|
CSYNC_FILE_EXCLUDE_LONG_FILENAME,
|
||||||
CSYNC_FILE_EXCLUDE_HIDDEN,
|
CSYNC_FILE_EXCLUDE_HIDDEN,
|
||||||
CSYNC_FILE_EXCLUDE_STAT_FAILED,
|
CSYNC_FILE_EXCLUDE_STAT_FAILED,
|
||||||
CSYNC_FILE_EXCLUDE_CONFLICT
|
CSYNC_FILE_EXCLUDE_CONFLICT,
|
||||||
|
CSYNC_FILE_EXCLUDE_CANNOT_ENCODE
|
||||||
};
|
};
|
||||||
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
|
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
|
||||||
|
|
||||||
#ifdef WITH_TESTING
|
class ExcludedFilesTest;
|
||||||
int OCSYNC_EXPORT _csync_exclude_add(c_strlist_t **inList, const char *string);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Load exclude list
|
* Manages file/directory exclusion.
|
||||||
*
|
*
|
||||||
* @param ctx The context of the synchronizer.
|
* Most commonly exclude patterns are loaded from files. See
|
||||||
* @param fname The filename to load.
|
* addExcludeFilePath() and reloadExcludeFiles().
|
||||||
*
|
*
|
||||||
* @return 0 on success, -1 if an error occurred with errno set.
|
* Excluded files are primarily relevant for sync runs, and for
|
||||||
|
* file watcher filtering.
|
||||||
|
*
|
||||||
|
* Excluded files and ignored files are the same thing. But the
|
||||||
|
* selective sync blacklist functionality is a different thing
|
||||||
|
* entirely.
|
||||||
*/
|
*/
|
||||||
int OCSYNC_EXPORT csync_exclude_load(const char *fname, c_strlist_t **list);
|
class OCSYNC_EXPORT ExcludedFiles : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
ExcludedFiles();
|
||||||
|
~ExcludedFiles();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief When all list loads and list are done
|
* Adds a new path to a file containing exclude patterns.
|
||||||
*
|
*
|
||||||
* Used to initialize internal data structures that build upon the loaded excludes.
|
* Does not load the file. Use reloadExcludeFiles() afterwards.
|
||||||
*
|
*/
|
||||||
* @param ctx
|
void addExcludeFilePath(const QString &path);
|
||||||
*/
|
|
||||||
void OCSYNC_EXPORT csync_exclude_traversal_prepare(CSYNC *ctx);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Check if the given path should be excluded in a traversal situation.
|
* Checks whether a file or directory should be excluded.
|
||||||
*
|
*
|
||||||
* It does only part of the work that csync_excluded does because it's assumed
|
* @param filePath the absolute path to the file
|
||||||
* that all leading directories have been run through csync_excluded_traversal()
|
* @param basePath folder path from which to apply exclude rules, ends with a /
|
||||||
* before. This can be significantly faster.
|
*/
|
||||||
*
|
bool isExcluded(
|
||||||
* That means for '/foo/bar/file' only ('/foo/bar/file', 'file') is checked
|
const QString &filePath,
|
||||||
* against the exclude patterns.
|
const QString &basePath,
|
||||||
*
|
bool excludeHidden) const;
|
||||||
* @param ctx The synchronizer context.
|
|
||||||
* @param path The patch to check.
|
/**
|
||||||
*
|
* Adds an exclude pattern.
|
||||||
* @return 2 if excluded and needs cleanup, 1 if excluded, 0 if not.
|
*
|
||||||
*/
|
* Primarily used in tests. Patterns added this way are preserved when
|
||||||
CSYNC_EXCLUDE_TYPE OCSYNC_EXPORT csync_excluded_traversal(CSYNC *ctx, const char *path, int filetype);
|
* reloadExcludeFiles() is called.
|
||||||
|
*/
|
||||||
|
void addManualExclude(const QByteArray &expr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all manually added exclude patterns.
|
||||||
|
*
|
||||||
|
* Primarily used in tests.
|
||||||
|
*/
|
||||||
|
void clearManualExcludes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a hook for traversal exclude pattern matching
|
||||||
|
* that csync can use.
|
||||||
|
*
|
||||||
|
* Careful: The function will only be valid for as long as this
|
||||||
|
* ExcludedFiles instance stays alive.
|
||||||
|
*/
|
||||||
|
auto csyncTraversalMatchFun() const
|
||||||
|
-> std::function<CSYNC_EXCLUDE_TYPE(const char *path, int filetype)>;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
/**
|
||||||
|
* Reloads the exclude patterns from the registered paths.
|
||||||
|
*/
|
||||||
|
bool reloadExcludeFiles();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief Match the exclude pattern against the full path.
|
||||||
|
*
|
||||||
|
* @param Path is folder-relative, should not start with a /.
|
||||||
|
*
|
||||||
|
* Note that this only matches patterns. It does not check whether the file
|
||||||
|
* or directory pointed to is hidden (or whether it even exists).
|
||||||
|
*/
|
||||||
|
CSYNC_EXCLUDE_TYPE fullPatternMatch(const char *path, int filetype) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if the given path should be excluded in a traversal situation.
|
||||||
|
*
|
||||||
|
* It does only part of the work that full() does because it's assumed
|
||||||
|
* that all leading directories have been run through traversal()
|
||||||
|
* before. This can be significantly faster.
|
||||||
|
*
|
||||||
|
* That means for 'foo/bar/file' only ('foo/bar/file', 'file') is checked
|
||||||
|
* against the exclude patterns.
|
||||||
|
*
|
||||||
|
* @param Path is folder-relative, should not start with a /.
|
||||||
|
*
|
||||||
|
* Note that this only matches patterns. It does not check whether the file
|
||||||
|
* or directory pointed to is hidden (or whether it even exists).
|
||||||
|
*/
|
||||||
|
CSYNC_EXCLUDE_TYPE traversalPatternMatch(const char *path, int filetype) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate optimized regular expressions for the exclude patterns.
|
||||||
|
*
|
||||||
|
* The optimization works in two steps: First, all supported patterns are put
|
||||||
|
* into _fullRegexFile/_fullRegexDir. These regexes can be applied to the full
|
||||||
|
* path to determine whether it is excluded or not.
|
||||||
|
*
|
||||||
|
* The second is a performance optimization. The particularly common use
|
||||||
|
* case for excludes during a sync run is "traversal": Instead of checking
|
||||||
|
* the full path every time, we check each parent path with the traversal
|
||||||
|
* function incrementally.
|
||||||
|
*
|
||||||
|
* Example: When the sync run eventually arrives at "a/b/c it can assume
|
||||||
|
* that the traversal matching has already been run on "a", "a/b"
|
||||||
|
* and just needs to run the traversal matcher on "a/b/c".
|
||||||
|
*
|
||||||
|
* The full matcher is equivalent to or-combining the traversal match results
|
||||||
|
* of all parent paths:
|
||||||
|
* full("a/b/c/d") == traversal("a") || traversal("a/b") || traversal("a/b/c")
|
||||||
|
*
|
||||||
|
* The traversal matcher can be extremely fast because it has a fast early-out
|
||||||
|
* case: It checks the bname part of the path against _bnameActivationRegex
|
||||||
|
* and only runs the full regex if the bname activation was triggered.
|
||||||
|
*
|
||||||
|
* Note: The traversal matcher will return not-excluded on some paths that the
|
||||||
|
* full matcher would exclude. Example: "b" is excluded. traversal("b/c")
|
||||||
|
* returns not-excluded because "c" isn't a bname activation pattern.
|
||||||
|
*/
|
||||||
|
void prepare();
|
||||||
|
|
||||||
|
/// Files to load excludes from
|
||||||
|
QSet<QString> _excludeFiles;
|
||||||
|
|
||||||
|
/// Exclude patterns added with addManualExclude()
|
||||||
|
QList<QByteArray> _manualExcludes;
|
||||||
|
|
||||||
|
/// List of all active exclude patterns
|
||||||
|
QList<QByteArray> _allExcludes;
|
||||||
|
|
||||||
|
/// see prepare()
|
||||||
|
QRegularExpression _bnameActivationRegexFile;
|
||||||
|
QRegularExpression _bnameActivationRegexDir;
|
||||||
|
QRegularExpression _fullRegexFile;
|
||||||
|
QRegularExpression _fullRegexDir;
|
||||||
|
|
||||||
|
friend class ExcludedFilesTest;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks all path components if the whole path should be excluded
|
|
||||||
*
|
|
||||||
* @param excludes
|
|
||||||
* @param path
|
|
||||||
* @param filetype
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
CSYNC_EXCLUDE_TYPE OCSYNC_EXPORT csync_excluded_no_ctx(c_strlist_t *excludes, const char *path, int filetype);
|
|
||||||
#endif /* _CSYNC_EXCLUDE_H */
|
#endif /* _CSYNC_EXCLUDE_H */
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Checks if filename is considered reserved by Windows
|
|
||||||
* @param file_name filename
|
|
||||||
* @return true if file is reserved, false otherwise
|
|
||||||
*/
|
|
||||||
bool csync_is_windows_reserved_word(const char *file_name);
|
|
||||||
|
|
||||||
|
|
||||||
/* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */
|
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "common/syncjournaldb.h"
|
#include "common/syncjournaldb.h"
|
||||||
#include "config_csync.h"
|
#include "config_csync.h"
|
||||||
|
@ -46,11 +47,9 @@
|
||||||
#include "std/c_private.h"
|
#include "std/c_private.h"
|
||||||
#include "csync.h"
|
#include "csync.h"
|
||||||
#include "csync_misc.h"
|
#include "csync_misc.h"
|
||||||
|
#include "csync_exclude.h"
|
||||||
#include "csync_macros.h"
|
#include "csync_macros.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How deep to scan directories.
|
* How deep to scan directories.
|
||||||
*/
|
*/
|
||||||
|
@ -148,17 +147,13 @@ struct OCSYNC_EXPORT csync_s {
|
||||||
|
|
||||||
OCC::SyncJournalDb *statedb;
|
OCC::SyncJournalDb *statedb;
|
||||||
|
|
||||||
c_strlist_t *excludes = nullptr; /* list of individual patterns collected from all exclude files */
|
/**
|
||||||
struct TraversalExcludes {
|
* Function used to determine whether an item is excluded
|
||||||
~TraversalExcludes() {
|
* during the update phase.
|
||||||
c_strlist_destroy(list_patterns_fnmatch);
|
*
|
||||||
}
|
* See ExcludedFiles in csync_exclude.
|
||||||
void prepare(c_strlist_t *excludes);
|
*/
|
||||||
|
std::function<CSYNC_EXCLUDE_TYPE(const char *path, int filetype)> exclude_traversal_fn;
|
||||||
QRegularExpression regexp_exclude;
|
|
||||||
c_strlist_t *list_patterns_fnmatch = nullptr;
|
|
||||||
|
|
||||||
} parsed_traversal_excludes;
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
std::unordered_map<ByteArrayRef, QByteArray, ByteArrayRefHash> folder_renamed_to; // map from->to
|
std::unordered_map<ByteArrayRef, QByteArray, ByteArrayRefHash> folder_renamed_to; // map from->to
|
||||||
|
|
|
@ -113,7 +113,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||||
|
|
||||||
if (!other) {
|
if (!other) {
|
||||||
/* Check the renamed path as well. */
|
/* Check the renamed path as well. */
|
||||||
other = other_tree->findFile(csync_rename_adjust_path(ctx, cur->path));
|
other = other_tree->findFile(csync_rename_adjust_parent_path(ctx, cur->path));
|
||||||
}
|
}
|
||||||
if (!other) {
|
if (!other) {
|
||||||
/* Check if it is ignored */
|
/* Check if it is ignored */
|
||||||
|
@ -147,24 +147,25 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||||
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
cur->instruction = CSYNC_INSTRUCTION_NEW;
|
||||||
|
|
||||||
bool processedRename = false;
|
bool processedRename = false;
|
||||||
auto renameCandidateProcessing = [&](const OCC::SyncJournalFileRecord &base) {
|
auto renameCandidateProcessing = [&](const QByteArray &basePath) {
|
||||||
if (processedRename)
|
if (processedRename)
|
||||||
return;
|
return;
|
||||||
if (!base.isValid())
|
if (basePath.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* First, check that the file is NOT in our tree (another file with the same name was added) */
|
/* First, check that the file is NOT in our tree (another file with the same name was added) */
|
||||||
if (our_tree->findFile(base._path)) {
|
if (our_tree->findFile(basePath)) {
|
||||||
qCDebug(lcReconcile, "Origin found in our tree : %s", base._path.constData());
|
other = nullptr;
|
||||||
|
qCDebug(lcReconcile, "Origin found in our tree : %s", basePath.constData());
|
||||||
} else {
|
} else {
|
||||||
/* Find the potential rename source file in the other tree.
|
/* 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
|
* If the renamed file could not be found in the opposite tree, that is because it
|
||||||
* is not longer existing there, maybe because it was renamed or deleted.
|
* is not longer existing there, maybe because it was renamed or deleted.
|
||||||
* The journal is cleaned up later after propagation.
|
* The journal is cleaned up later after propagation.
|
||||||
*/
|
*/
|
||||||
other = other_tree->findFile(base._path);
|
other = other_tree->findFile(basePath);
|
||||||
qCDebug(lcReconcile, "Rename origin in other tree (%s) %s",
|
qCDebug(lcReconcile, "Rename origin in other tree (%s) %s",
|
||||||
base._path.constData(), other ? "found" : "not found");
|
basePath.constData(), other ? "found" : "not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!other) {
|
if(!other) {
|
||||||
|
@ -197,7 +198,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||||
// We have consumed 'other': exit this loop to not consume another one.
|
// We have consumed 'other': exit this loop to not consume another one.
|
||||||
processedRename = true;
|
processedRename = true;
|
||||||
} else if (our_tree->findFile(csync_rename_adjust_path(ctx, other->path)) == cur) {
|
} else if (our_tree->findFile(csync_rename_adjust_parent_path(ctx, other->path)) == cur) {
|
||||||
// If we're here, that means that the other side's reconcile will be able
|
// If we're here, that means that the other side's reconcile will be able
|
||||||
// to work against cur: The filename itself didn't change, only a parent
|
// to work against cur: The filename itself didn't change, only a parent
|
||||||
// directory was renamed! In that case it's safe to ignore the rename
|
// directory was renamed! In that case it's safe to ignore the rename
|
||||||
|
@ -225,12 +226,34 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||||
qCDebug(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
qCDebug(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
||||||
cur->inode);
|
cur->inode);
|
||||||
ctx->statedb->getFileRecordByInode(cur->inode, &base);
|
ctx->statedb->getFileRecordByInode(cur->inode, &base);
|
||||||
renameCandidateProcessing(base);
|
renameCandidateProcessing(base._path);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(ctx->current == REMOTE_REPLICA);
|
ASSERT(ctx->current == REMOTE_REPLICA);
|
||||||
qCDebug(lcReconcile, "Finding rename origin through file ID %s",
|
|
||||||
cur->file_id.constData());
|
// The update phase has already mapped out all dir->dir renames, check the
|
||||||
ctx->statedb->getFileRecordsByFileId(cur->file_id, renameCandidateProcessing);
|
// path that is consistent with that first. Otherwise update mappings and
|
||||||
|
// reconcile mappings might disagree, leading to odd situations down the
|
||||||
|
// line.
|
||||||
|
auto basePath = csync_rename_adjust_full_path_source(ctx, cur->path);
|
||||||
|
if (basePath != cur->path) {
|
||||||
|
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.
|
||||||
|
ctx->statedb->getFileRecordsByFileId(cur->file_id,
|
||||||
|
[&](const OCC::SyncJournalFileRecord &base) {
|
||||||
|
if (base._path == basePath)
|
||||||
|
renameCandidateProcessing(basePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also feed all the other files with the same fileid if necessary
|
||||||
|
if (!processedRename) {
|
||||||
|
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); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -36,7 +36,7 @@ void csync_rename_record(CSYNC* ctx, const QByteArray &from, const QByteArray &t
|
||||||
ctx->renames.folder_renamed_from[to] = from;
|
ctx->renames.folder_renamed_from[to] = from;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray csync_rename_adjust_path(CSYNC* ctx, const QByteArray &path)
|
QByteArray csync_rename_adjust_parent_path(CSYNC *ctx, const QByteArray &path)
|
||||||
{
|
{
|
||||||
if (ctx->renames.folder_renamed_to.empty())
|
if (ctx->renames.folder_renamed_to.empty())
|
||||||
return path;
|
return path;
|
||||||
|
@ -50,11 +50,25 @@ QByteArray csync_rename_adjust_path(CSYNC* ctx, const QByteArray &path)
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray csync_rename_adjust_path_source(CSYNC* ctx, const QByteArray &path)
|
QByteArray csync_rename_adjust_parent_path_source(CSYNC *ctx, const QByteArray &path)
|
||||||
{
|
{
|
||||||
if (ctx->renames.folder_renamed_from.empty())
|
if (ctx->renames.folder_renamed_from.empty())
|
||||||
return path;
|
return path;
|
||||||
for (auto p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
|
for (ByteArrayRef p = _parentDir(path); !p.isEmpty(); p = _parentDir(p)) {
|
||||||
|
auto it = ctx->renames.folder_renamed_from.find(p);
|
||||||
|
if (it != ctx->renames.folder_renamed_from.end()) {
|
||||||
|
QByteArray rep = it->second + path.mid(p.length());
|
||||||
|
return rep;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray csync_rename_adjust_full_path_source(CSYNC *ctx, const QByteArray &path)
|
||||||
|
{
|
||||||
|
if (ctx->renames.folder_renamed_from.empty())
|
||||||
|
return path;
|
||||||
|
for (ByteArrayRef p = path; !p.isEmpty(); p = _parentDir(p)) {
|
||||||
auto it = ctx->renames.folder_renamed_from.find(p);
|
auto it = ctx->renames.folder_renamed_from.find(p);
|
||||||
if (it != ctx->renames.folder_renamed_from.end()) {
|
if (it != ctx->renames.folder_renamed_from.end()) {
|
||||||
QByteArray rep = it->second + path.mid(p.length());
|
QByteArray rep = it->second + path.mid(p.length());
|
||||||
|
|
|
@ -22,10 +22,19 @@
|
||||||
|
|
||||||
#include "csync.h"
|
#include "csync.h"
|
||||||
|
|
||||||
/* Return the final destination path of a given patch in case of renames */
|
/* Return the final destination path of a given patch in case of renames
|
||||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_path(CSYNC *ctx, const QByteArray &path);
|
*
|
||||||
|
* Does only map the parent directories. If the directory "A" is renamed to
|
||||||
|
* "B" then this function will not map "A" to "B". Only "A/foo" -> "B/foo".
|
||||||
|
*/
|
||||||
|
QByteArray OCSYNC_EXPORT csync_rename_adjust_parent_path(CSYNC *ctx, const QByteArray &path);
|
||||||
|
|
||||||
/* Return the source of a given path in case of renames */
|
/* Return the source of a given path in case of renames */
|
||||||
QByteArray OCSYNC_EXPORT csync_rename_adjust_path_source(CSYNC *ctx, const QByteArray &path);
|
QByteArray OCSYNC_EXPORT csync_rename_adjust_parent_path_source(CSYNC *ctx, const QByteArray &path);
|
||||||
|
|
||||||
|
/* like the parent_path variant, but applying to the full path */
|
||||||
|
QByteArray OCSYNC_EXPORT csync_rename_adjust_full_path_source(CSYNC *ctx, const QByteArray &path);
|
||||||
|
|
||||||
void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const QByteArray &from, const QByteArray &to);
|
void OCSYNC_EXPORT csync_rename_record(CSYNC *ctx, const QByteArray &from, const QByteArray &to);
|
||||||
/* Return the amount of renamed item recorded */
|
/* Return the amount of renamed item recorded */
|
||||||
bool OCSYNC_EXPORT csync_rename_count(CSYNC *ctx);
|
bool OCSYNC_EXPORT csync_rename_count(CSYNC *ctx);
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
#include "common/asserts.h"
|
#include "common/asserts.h"
|
||||||
|
|
||||||
|
#include <QtCore/QTextCodec>
|
||||||
|
|
||||||
// Needed for PRIu64 on MinGW in C++ mode.
|
// Needed for PRIu64 on MinGW in C++ mode.
|
||||||
#define __STDC_FORMAT_MACROS
|
#define __STDC_FORMAT_MACROS
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
@ -107,7 +109,7 @@ static bool _csync_mtime_equal(time_t a, time_t b)
|
||||||
*/
|
*/
|
||||||
static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
|
static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
|
||||||
OCC::SyncJournalFileRecord base;
|
OCC::SyncJournalFileRecord base;
|
||||||
CSYNC_EXCLUDE_TYPE excluded;
|
CSYNC_EXCLUDE_TYPE excluded = CSYNC_NOT_EXCLUDED;
|
||||||
|
|
||||||
if (fs == NULL) {
|
if (fs == NULL) {
|
||||||
errno = EINVAL;
|
errno = EINVAL;
|
||||||
|
@ -118,8 +120,9 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||||
if (fs->type == CSYNC_FTW_TYPE_SKIP) {
|
if (fs->type == CSYNC_FTW_TYPE_SKIP) {
|
||||||
excluded =CSYNC_FILE_EXCLUDE_STAT_FAILED;
|
excluded =CSYNC_FILE_EXCLUDE_STAT_FAILED;
|
||||||
} else {
|
} else {
|
||||||
/* Check if file is excluded */
|
/* Check if file is excluded */
|
||||||
excluded = csync_excluded_traversal(ctx, fs->path, fs->type);
|
if (ctx->exclude_traversal_fn)
|
||||||
|
excluded = ctx->exclude_traversal_fn(fs->path, fs->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( excluded == CSYNC_NOT_EXCLUDED ) {
|
if( excluded == CSYNC_NOT_EXCLUDED ) {
|
||||||
|
@ -148,6 +151,14 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ctx->current == REMOTE_REPLICA && QTextCodec::codecForLocale()->mibEnum() != 106) {
|
||||||
|
/* If the locale codec is not UTF-8, we must check that the filename from the server can
|
||||||
|
* be encoded in the local file system. */
|
||||||
|
if (!QTextCodec::codecForLocale()->canEncode(QString::fromUtf8(fs->path))) {
|
||||||
|
excluded = CSYNC_FILE_EXCLUDE_CANNOT_ENCODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fs->type == CSYNC_FTW_TYPE_FILE ) {
|
if (fs->type == CSYNC_FTW_TYPE_FILE ) {
|
||||||
if (fs->modtime == 0) {
|
if (fs->modtime == 0) {
|
||||||
qCDebug(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
qCDebug(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
||||||
|
@ -375,6 +386,8 @@ out:
|
||||||
fs->error_status = CSYNC_STATUS_INDIVIDUAL_STAT_FAILED;
|
fs->error_status = CSYNC_STATUS_INDIVIDUAL_STAT_FAILED;
|
||||||
} else if (excluded == CSYNC_FILE_EXCLUDE_CONFLICT) {
|
} else if (excluded == CSYNC_FILE_EXCLUDE_CONFLICT) {
|
||||||
fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE;
|
fs->error_status = CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE;
|
||||||
|
} else if (excluded == CSYNC_FILE_EXCLUDE_CANNOT_ENCODE) {
|
||||||
|
fs->error_status = CSYNC_STATUS_INDIVIDUAL_CANNOT_ENCODE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -486,7 +499,9 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
|
||||||
/* Check for exclusion from the tree.
|
/* Check for exclusion from the tree.
|
||||||
* Note that this is only a safety net in case the ignore list changes
|
* Note that this is only a safety net in case the ignore list changes
|
||||||
* without a full remote discovery being triggered. */
|
* without a full remote discovery being triggered. */
|
||||||
CSYNC_EXCLUDE_TYPE excluded = csync_excluded_traversal(ctx, st->path, st->type);
|
CSYNC_EXCLUDE_TYPE excluded = CSYNC_NOT_EXCLUDED;
|
||||||
|
if (ctx->exclude_traversal_fn)
|
||||||
|
excluded = ctx->exclude_traversal_fn(st->path, st->type);
|
||||||
if (excluded != CSYNC_NOT_EXCLUDED) {
|
if (excluded != CSYNC_NOT_EXCLUDED) {
|
||||||
qDebug(lcUpdate, "%s excluded (%d)", st->path.constData(), excluded);
|
qDebug(lcUpdate, "%s excluded (%d)", st->path.constData(), excluded);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ int c_streq(const char *a, const char *b);
|
||||||
*
|
*
|
||||||
* @param size Size to allocate.
|
* @param size Size to allocate.
|
||||||
*
|
*
|
||||||
* @return Pointer to the newly allocated stringlist. NULL if an error occured.
|
* @return Pointer to the newly allocated stringlist. NULL if an error occurred.
|
||||||
*/
|
*/
|
||||||
c_strlist_t *c_strlist_new(size_t size);
|
c_strlist_t *c_strlist_new(size_t size);
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ c_strlist_t *c_strlist_new(size_t size);
|
||||||
* @param strlist Stringlist to expand
|
* @param strlist Stringlist to expand
|
||||||
* @param size New size of the strlinglist to expand
|
* @param size New size of the strlinglist to expand
|
||||||
*
|
*
|
||||||
* @return Pointer to the expanded stringlist. NULL if an error occured.
|
* @return Pointer to the expanded stringlist. NULL if an error occurred.
|
||||||
*/
|
*/
|
||||||
c_strlist_t *c_strlist_expand(c_strlist_t *strlist, size_t size);
|
c_strlist_t *c_strlist_expand(c_strlist_t *strlist, size_t size);
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ c_strlist_t *c_strlist_expand(c_strlist_t *strlist, size_t size);
|
||||||
* @param strlist Stringlist to add the string.
|
* @param strlist Stringlist to add the string.
|
||||||
* @param string String to add.
|
* @param string String to add.
|
||||||
*
|
*
|
||||||
* @return 0 on success, less than 0 and errno set if an error occured.
|
* @return 0 on success, less than 0 and errno set if an error occurred.
|
||||||
* ENOBUFS if the list is full.
|
* ENOBUFS if the list is full.
|
||||||
*/
|
*/
|
||||||
int c_strlist_add(c_strlist_t *strlist, const char *string);
|
int c_strlist_add(c_strlist_t *strlist, const char *string);
|
||||||
|
@ -125,7 +125,7 @@ int c_strlist_add(c_strlist_t *strlist, const char *string);
|
||||||
* @param strlist Stringlist to add the string.
|
* @param strlist Stringlist to add the string.
|
||||||
* @param string String to add.
|
* @param string String to add.
|
||||||
*
|
*
|
||||||
* @return 0 on success, less than 0 and errno set if an error occured.
|
* @return 0 on success, less than 0 and errno set if an error occurred.
|
||||||
*/
|
*/
|
||||||
int c_strlist_add_grow(c_strlist_t **strlist, const char *string);
|
int c_strlist_add_grow(c_strlist_t **strlist, const char *string);
|
||||||
|
|
||||||
|
|
|
@ -263,7 +263,6 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_model->classify(index) == FolderStatusModel::SubFolder) {
|
if (_model->classify(index) == FolderStatusModel::SubFolder) {
|
||||||
QTreeView *tv = ui->_folderList;
|
|
||||||
QMenu *menu = new QMenu(tv);
|
QMenu *menu = new QMenu(tv);
|
||||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
|
@ -275,8 +274,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||||
ac->setEnabled(false);
|
ac->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
menu->exec(QCursor::pos());
|
menu->popup(tv->mapToGlobal(pos));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,6 +289,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||||
auto folderMan = FolderMan::instance();
|
auto folderMan = FolderMan::instance();
|
||||||
|
|
||||||
QMenu *menu = new QMenu(tv);
|
QMenu *menu = new QMenu(tv);
|
||||||
|
|
||||||
menu->setAttribute(Qt::WA_DeleteOnClose);
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
QAction *ac = menu->addAction(tr("Open folder"));
|
QAction *ac = menu->addAction(tr("Open folder"));
|
||||||
|
@ -316,7 +315,7 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
|
||||||
|
|
||||||
ac = menu->addAction(tr("Remove folder sync connection"));
|
ac = menu->addAction(tr("Remove folder sync connection"));
|
||||||
connect(ac, &QAction::triggered, this, &AccountSettings::slotRemoveCurrentFolder);
|
connect(ac, &QAction::triggered, this, &AccountSettings::slotRemoveCurrentFolder);
|
||||||
menu->exec(tv->mapToGlobal(pos));
|
menu->popup(tv->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
void AccountSettings::slotFolderListClicked(const QModelIndex &indx)
|
||||||
|
@ -688,14 +687,22 @@ void AccountSettings::slotAccountStateChanged()
|
||||||
/* Allow to expand the item if the account is connected. */
|
/* Allow to expand the item if the account is connected. */
|
||||||
ui->_folderList->setItemsExpandable(state == AccountState::Connected);
|
ui->_folderList->setItemsExpandable(state == AccountState::Connected);
|
||||||
|
|
||||||
/* check if there are expanded root items, if so, close them, if the state is different from being Connected. */
|
|
||||||
if (state != AccountState::Connected) {
|
if (state != AccountState::Connected) {
|
||||||
|
/* check if there are expanded root items, if so, close them */
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < _model->rowCount(); ++i) {
|
for (i = 0; i < _model->rowCount(); ++i) {
|
||||||
if (ui->_folderList->isExpanded(_model->index(i)))
|
if (ui->_folderList->isExpanded(_model->index(i)))
|
||||||
ui->_folderList->setExpanded(_model->index(i), false);
|
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
|
||||||
|
// sync user interface buttons.
|
||||||
|
refreshSelectiveSyncStatus();
|
||||||
|
|
||||||
/* set the correct label for the Account toolbox button */
|
/* set the correct label for the Account toolbox button */
|
||||||
if (_accountState) {
|
if (_accountState) {
|
||||||
if (_accountState->isSignedOut()) {
|
if (_accountState->isSignedOut()) {
|
||||||
|
@ -748,7 +755,7 @@ AccountSettings::~AccountSettings()
|
||||||
|
|
||||||
void AccountSettings::refreshSelectiveSyncStatus()
|
void AccountSettings::refreshSelectiveSyncStatus()
|
||||||
{
|
{
|
||||||
bool shouldBeVisible = _model->isDirty();
|
bool shouldBeVisible = _model->isDirty() && _accountState->isConnected();
|
||||||
|
|
||||||
QString msg;
|
QString msg;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
|
@ -128,9 +128,9 @@ void ActivityListModel::startFetchJob(AccountState *s)
|
||||||
this, &ActivityListModel::slotActivitiesReceived);
|
this, &ActivityListModel::slotActivitiesReceived);
|
||||||
job->setProperty("AccountStatePtr", QVariant::fromValue<QPointer<AccountState>>(s));
|
job->setProperty("AccountStatePtr", QVariant::fromValue<QPointer<AccountState>>(s));
|
||||||
|
|
||||||
QList<QPair<QString, QString>> params;
|
QUrlQuery params;
|
||||||
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
|
params.addQueryItem(QLatin1String("page"), QLatin1String("0"));
|
||||||
params.append(qMakePair(QString::fromLatin1("pagesize"), QString::fromLatin1("100")));
|
params.addQueryItem(QLatin1String("pagesize"), QLatin1String("100"));
|
||||||
job->addQueryParams(params);
|
job->addQueryParams(params);
|
||||||
|
|
||||||
_currentlyFetching.insert(s);
|
_currentlyFetching.insert(s);
|
||||||
|
|
|
@ -429,14 +429,13 @@ void ActivityWidget::slotNotifyServerFinished(const QString &reply, int replyCod
|
||||||
}
|
}
|
||||||
|
|
||||||
endNotificationRequest(job->widget(), replyCode);
|
endNotificationRequest(job->widget(), replyCode);
|
||||||
// FIXME: remove the widget after a couple of seconds
|
|
||||||
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
|
qCInfo(lcActivity) << "Server Notification reply code" << replyCode << reply;
|
||||||
|
|
||||||
// if the notification was successful start a timer that triggers
|
// if the notification was successful start a timer that triggers
|
||||||
// removal of the done widgets in a few seconds
|
// removal of the done widgets in a few seconds
|
||||||
// Add 200 millisecs to the predefined value to make sure that the timer in
|
// Add 200 millisecs to the predefined value to make sure that the timer in
|
||||||
// widget's method readyToClose() has elapsed.
|
// widget's method readyToClose() has elapsed.
|
||||||
if (replyCode == OCS_SUCCESS_STATUS_CODE) {
|
if (replyCode == OCS_SUCCESS_STATUS_CODE || replyCode == OCS_SUCCESS_STATUS_CODE_V2) {
|
||||||
scheduleWidgetToRemove(job->widget());
|
scheduleWidgetToRemove(job->widget());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
#include "accountmanager.h"
|
#include "accountmanager.h"
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
#include "updater/ocupdater.h"
|
#include "updater/ocupdater.h"
|
||||||
#include "excludedfiles.h"
|
|
||||||
#include "owncloudsetupwizard.h"
|
#include "owncloudsetupwizard.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
@ -52,6 +51,7 @@
|
||||||
#include <QTranslator>
|
#include <QTranslator>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
|
||||||
class QSocket;
|
class QSocket;
|
||||||
|
|
||||||
|
@ -120,10 +120,29 @@ Application::Application(int &argc, char **argv)
|
||||||
// TODO: Can't set this without breaking current config paths
|
// TODO: Can't set this without breaking current config paths
|
||||||
// setOrganizationName(QLatin1String(APPLICATION_VENDOR));
|
// setOrganizationName(QLatin1String(APPLICATION_VENDOR));
|
||||||
setOrganizationDomain(QLatin1String(APPLICATION_REV_DOMAIN));
|
setOrganizationDomain(QLatin1String(APPLICATION_REV_DOMAIN));
|
||||||
setApplicationName(_theme->appNameGUI());
|
setApplicationName(_theme->appName());
|
||||||
setWindowIcon(_theme->applicationIcon());
|
setWindowIcon(_theme->applicationIcon());
|
||||||
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
setAttribute(Qt::AA_UseHighDpiPixmaps, true);
|
||||||
|
|
||||||
|
auto confDir = ConfigFile().configPath();
|
||||||
|
if (!QFileInfo(confDir).exists()) {
|
||||||
|
// Migrate from version <= 2.4
|
||||||
|
setApplicationName(_theme->appNameGUI());
|
||||||
|
QString oldDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
||||||
|
setApplicationName(_theme->appName());
|
||||||
|
if (QFileInfo(oldDir).isDir()) {
|
||||||
|
qCInfo(lcApplication) << "Migrating old config from" << oldDir << "to" << confDir;
|
||||||
|
if (!QFile::rename(oldDir, confDir)) {
|
||||||
|
qCWarning(lcApplication) << "Failed to move the old config file to its new location (" << oldDir << "to" << confDir << ")";
|
||||||
|
} else {
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
// Create a symbolic link so a downgrade of the client would still find the config.
|
||||||
|
QFile::link(confDir, oldDir);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parseOptions(arguments());
|
parseOptions(arguments());
|
||||||
//no need to waste time;
|
//no need to waste time;
|
||||||
if (_helpOnly || _versionOnly)
|
if (_helpOnly || _versionOnly)
|
||||||
|
@ -140,13 +159,10 @@ Application::Application(int &argc, char **argv)
|
||||||
setupLogging();
|
setupLogging();
|
||||||
setupTranslations();
|
setupTranslations();
|
||||||
|
|
||||||
// Setup global excludes
|
// The timeout is initialized with an environment variable, if not, override with the value from the config
|
||||||
qCInfo(lcApplication) << "Loading global exclude list";
|
|
||||||
ConfigFile cfg;
|
ConfigFile cfg;
|
||||||
ExcludedFiles &excludes = ExcludedFiles::instance();
|
if (!AbstractNetworkJob::httpTimeout)
|
||||||
excludes.addExcludeFilePath(cfg.excludeFile(ConfigFile::SystemScope));
|
AbstractNetworkJob::httpTimeout = cfg.timeout();
|
||||||
excludes.addExcludeFilePath(cfg.excludeFile(ConfigFile::UserScope));
|
|
||||||
excludes.reloadExcludes();
|
|
||||||
|
|
||||||
_folderManager.reset(new FolderMan);
|
_folderManager.reset(new FolderMan);
|
||||||
|
|
||||||
|
|
|
@ -156,12 +156,13 @@ void OAuth::start()
|
||||||
QUrl OAuth::authorisationLink() const
|
QUrl OAuth::authorisationLink() const
|
||||||
{
|
{
|
||||||
Q_ASSERT(_server.isListening());
|
Q_ASSERT(_server.isListening());
|
||||||
QUrl url = Utility::concatUrlPath(_account->url(), QLatin1String("/index.php/apps/oauth2/authorize"),
|
QUrlQuery query;
|
||||||
{ { QLatin1String("response_type"), QLatin1String("code") },
|
query.setQueryItems({ { QLatin1String("response_type"), QLatin1String("code") },
|
||||||
{ QLatin1String("client_id"), Theme::instance()->oauthClientId() },
|
{ QLatin1String("client_id"), Theme::instance()->oauthClientId() },
|
||||||
{ QLatin1String("redirect_uri"), QLatin1String("http://localhost:") + QString::number(_server.serverPort()) } });
|
{ QLatin1String("redirect_uri"), QLatin1String("http://localhost:") + QString::number(_server.serverPort()) } });
|
||||||
if (!_expectedUser.isNull())
|
if (!_expectedUser.isNull())
|
||||||
url.addQueryItem("user", _expectedUser);
|
query.addQueryItem("user", _expectedUser);
|
||||||
|
QUrl url = Utility::concatUrlPath(_account->url(), QLatin1String("/index.php/apps/oauth2/authorize"), query);
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "creds/shibbolethcredentials.h"
|
#include "creds/shibbolethcredentials.h"
|
||||||
#include "shibboleth/shibbolethuserjob.h"
|
#include "shibboleth/shibbolethuserjob.h"
|
||||||
#include "creds/credentialscommon.h"
|
#include "creds/credentialscommon.h"
|
||||||
|
#include "creds/httpcredentialsgui.h"
|
||||||
|
|
||||||
#include "accessmanager.h"
|
#include "accessmanager.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
|
@ -151,7 +152,31 @@ void ShibbolethCredentials::fetchFromKeychainHelper()
|
||||||
|
|
||||||
void ShibbolethCredentials::askFromUser()
|
void ShibbolethCredentials::askFromUser()
|
||||||
{
|
{
|
||||||
showLoginWindow();
|
// First, we do a DetermineAuthTypeJob to make sure that the server is still using shibboleth and did not upgrade to oauth
|
||||||
|
DetermineAuthTypeJob *job = new DetermineAuthTypeJob(_account->sharedFromThis(), this);
|
||||||
|
connect(job, &DetermineAuthTypeJob::authType, [this, job](DetermineAuthTypeJob::AuthType type) {
|
||||||
|
if (type == DetermineAuthTypeJob::Shibboleth) {
|
||||||
|
// Normal case, still shibboleth
|
||||||
|
showLoginWindow();
|
||||||
|
} else if (type == DetermineAuthTypeJob::OAuth) {
|
||||||
|
// Hack: upgrade to oauth
|
||||||
|
auto newCred = new HttpCredentialsGui;
|
||||||
|
job->setParent(0);
|
||||||
|
job->deleteLater();
|
||||||
|
auto account = this->_account;
|
||||||
|
auto user = this->_user;
|
||||||
|
account->setCredentials(newCred); // delete this
|
||||||
|
account->setCredentialSetting(QLatin1String("user"), user);
|
||||||
|
newCred->fetchUser();
|
||||||
|
newCred->askFromUser();
|
||||||
|
} else {
|
||||||
|
// Basic auth or unkown. Since it may be unkown it might be a temporary failure, don't replace the credentials here
|
||||||
|
// Still show the login window in that case not to break the flow.
|
||||||
|
showLoginWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShibbolethCredentials::stillValid(QNetworkReply *reply)
|
bool ShibbolethCredentials::stillValid(QNetworkReply *reply)
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include "socketapi.h"
|
#include "socketapi.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
#include "excludedfiles.h"
|
|
||||||
|
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
|
|
||||||
|
@ -79,7 +78,8 @@ Folder::Folder(const FolderDefinition &definition,
|
||||||
// pass the setting if hidden files are to be ignored, will be read in csync_update
|
// pass the setting if hidden files are to be ignored, will be read in csync_update
|
||||||
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
|
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
|
||||||
|
|
||||||
if (!setIgnoredFiles())
|
ConfigFile::setupDefaultExcludeFilePaths(_engine->excludedFiles());
|
||||||
|
if (!reloadExcludes())
|
||||||
qCWarning(lcFolder, "Could not read system exclude file");
|
qCWarning(lcFolder, "Could not read system exclude file");
|
||||||
|
|
||||||
connect(_accountState.data(), &AccountState::isConnectedChanged, this, &Folder::canSyncChanged);
|
connect(_accountState.data(), &AccountState::isConnectedChanged, this, &Folder::canSyncChanged);
|
||||||
|
@ -595,24 +595,9 @@ void Folder::wipe()
|
||||||
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
|
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Folder::setIgnoredFiles()
|
bool Folder::reloadExcludes()
|
||||||
{
|
{
|
||||||
// Note: Doing this on each sync run and on Folder construction is
|
return _engine->excludedFiles().reloadExcludeFiles();
|
||||||
// unnecessary, because _engine->excludedFiles() persists between
|
|
||||||
// sync runs. This is not a big problem because ExcludedFiles maintains
|
|
||||||
// a QSet of files to load.
|
|
||||||
ConfigFile cfg;
|
|
||||||
QString systemList = cfg.excludeFile(ConfigFile::SystemScope);
|
|
||||||
qCInfo(lcFolder) << "Adding system ignore list to csync:" << systemList;
|
|
||||||
_engine->excludedFiles().addExcludeFilePath(systemList);
|
|
||||||
|
|
||||||
QString userList = cfg.excludeFile(ConfigFile::UserScope);
|
|
||||||
if (QFile::exists(userList)) {
|
|
||||||
qCInfo(lcFolder) << "Adding user defined ignore list to csync:" << userList;
|
|
||||||
_engine->excludedFiles().addExcludeFilePath(userList);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _engine->excludedFiles().reloadExcludes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Folder::setProxyDirty(bool value)
|
void Folder::setProxyDirty(bool value)
|
||||||
|
@ -647,7 +632,7 @@ void Folder::startSync(const QStringList &pathList)
|
||||||
|
|
||||||
_fileLog->start(path());
|
_fileLog->start(path());
|
||||||
|
|
||||||
if (!setIgnoredFiles()) {
|
if (!reloadExcludes()) {
|
||||||
slotSyncError(tr("Could not read system exclude file"));
|
slotSyncError(tr("Could not read system exclude file"));
|
||||||
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection, Q_ARG(bool, false));
|
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection, Q_ARG(bool, false));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -323,7 +323,7 @@ private slots:
|
||||||
void slotNextSyncFullLocalDiscovery();
|
void slotNextSyncFullLocalDiscovery();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool setIgnoredFiles();
|
bool reloadExcludes();
|
||||||
|
|
||||||
void showSyncResultPopup();
|
void showSyncResultPopup();
|
||||||
|
|
||||||
|
@ -342,7 +342,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
void createGuiLog(const QString &filename, LogStatus status, int count,
|
void createGuiLog(const QString &filename, LogStatus status, int count,
|
||||||
const QString &renameTarget = QString::null);
|
const QString &renameTarget = QString());
|
||||||
|
|
||||||
AccountStatePtr _accountState;
|
AccountStatePtr _accountState;
|
||||||
FolderDefinition _definition;
|
FolderDefinition _definition;
|
||||||
|
|
|
@ -1018,7 +1018,7 @@ QString FolderMan::getBackupName(QString fullPathName) const
|
||||||
fullPathName.chop(1);
|
fullPathName.chop(1);
|
||||||
|
|
||||||
if (fullPathName.isEmpty())
|
if (fullPathName.isEmpty())
|
||||||
return QString::null;
|
return QString();
|
||||||
|
|
||||||
QString newName = fullPathName + tr(" (backup)");
|
QString newName = fullPathName + tr(" (backup)");
|
||||||
QFileInfo fi(newName);
|
QFileInfo fi(newName);
|
||||||
|
|
|
@ -59,7 +59,7 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||||
auto classif = static_cast<const FolderStatusModel *>(index.model())->classify(index);
|
auto classif = static_cast<const FolderStatusModel *>(index.model())->classify(index);
|
||||||
if (classif == FolderStatusModel::AddButton) {
|
if (classif == FolderStatusModel::AddButton) {
|
||||||
const int margins = aliasFm.height(); // same as 2*aliasMargin of paint
|
const int margins = aliasFm.height(); // same as 2*aliasMargin of paint
|
||||||
QFontMetrics fm(option.font);
|
QFontMetrics fm(qApp->font("QPushButton"));
|
||||||
QStyleOptionButton opt;
|
QStyleOptionButton opt;
|
||||||
static_cast<QStyleOption &>(opt) = option;
|
static_cast<QStyleOption &>(opt) = option;
|
||||||
opt.text = addFolderText();
|
opt.text = addFolderText();
|
||||||
|
@ -138,7 +138,10 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||||
opt.rect.setWidth(qMin(opt.rect.width(), hint.width()));
|
opt.rect.setWidth(qMin(opt.rect.width(), hint.width()));
|
||||||
opt.rect.adjust(0, aliasMargin, 0, -aliasMargin);
|
opt.rect.adjust(0, aliasMargin, 0, -aliasMargin);
|
||||||
opt.rect = QStyle::visualRect(option.direction, option.rect, opt.rect);
|
opt.rect = QStyle::visualRect(option.direction, option.rect, opt.rect);
|
||||||
|
painter->save();
|
||||||
|
painter->setFont(qApp->font("QPushButton"));
|
||||||
QApplication::style()->drawControl(QStyle::CE_PushButton, &opt, painter, option.widget);
|
QApplication::style()->drawControl(QStyle::CE_PushButton, &opt, painter, option.widget);
|
||||||
|
painter->restore();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,7 @@ void FolderStatusModel::setAccountState(const AccountState *accountState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by header text
|
// Sort by header text
|
||||||
qSort(_folders.begin(), _folders.end(), sortByFolderHeader);
|
std::sort(_folders.begin(), _folders.end(), sortByFolderHeader);
|
||||||
|
|
||||||
// Set the root _pathIdx after the sorting
|
// Set the root _pathIdx after the sorting
|
||||||
for (int i = 0; i < _folders.size(); ++i) {
|
for (int i = 0; i < _folders.size(); ++i) {
|
||||||
|
@ -951,8 +951,7 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress)
|
||||||
//: Example text: "download 24Kb/s" (%1 is replaced by 24Kb (translated))
|
//: Example text: "download 24Kb/s" (%1 is replaced by 24Kb (translated))
|
||||||
fileProgressString.append(tr("download %1/s").arg(Utility::octetsToString(estimatedDownBw)));
|
fileProgressString.append(tr("download %1/s").arg(Utility::octetsToString(estimatedDownBw)));
|
||||||
#else
|
#else
|
||||||
fileProgressString.append(trUtf8("\u2193"
|
fileProgressString.append(tr("\u2193 %1/s")
|
||||||
" %1/s")
|
|
||||||
.arg(Utility::octetsToString(estimatedDownBw)));
|
.arg(Utility::octetsToString(estimatedDownBw)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -962,8 +961,7 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress)
|
||||||
//: Example text: "upload 24Kb/s" (%1 is replaced by 24Kb (translated))
|
//: Example text: "upload 24Kb/s" (%1 is replaced by 24Kb (translated))
|
||||||
fileProgressString.append(tr("upload %1/s").arg(Utility::octetsToString(estimatedUpBw)));
|
fileProgressString.append(tr("upload %1/s").arg(Utility::octetsToString(estimatedUpBw)));
|
||||||
#else
|
#else
|
||||||
fileProgressString.append(trUtf8("\u2191"
|
fileProgressString.append(tr("\u2191 %1/s")
|
||||||
" %1/s")
|
|
||||||
.arg(Utility::octetsToString(estimatedUpBw)));
|
.arg(Utility::octetsToString(estimatedUpBw)));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#include "folderwatcher_linux.h"
|
#include "folderwatcher_linux.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "excludedfiles.h"
|
|
||||||
#include "folder.h"
|
#include "folder.h"
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <QWizardPage>
|
#include <QWizardPage>
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QEvent>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
@ -106,7 +107,7 @@ bool FolderWizardLocalPath::isComplete() const
|
||||||
_ui.warnLabel->setWordWrap(true);
|
_ui.warnLabel->setWordWrap(true);
|
||||||
if (isOk) {
|
if (isOk) {
|
||||||
_ui.warnLabel->hide();
|
_ui.warnLabel->hide();
|
||||||
_ui.warnLabel->setText(QString::null);
|
_ui.warnLabel->setText(QString());
|
||||||
} else {
|
} else {
|
||||||
_ui.warnLabel->show();
|
_ui.warnLabel->show();
|
||||||
QString warnings = formatWarnings(warnStrings);
|
QString warnings = formatWarnings(warnStrings);
|
||||||
|
@ -117,7 +118,7 @@ bool FolderWizardLocalPath::isComplete() const
|
||||||
|
|
||||||
void FolderWizardLocalPath::slotChooseLocalFolder()
|
void FolderWizardLocalPath::slotChooseLocalFolder()
|
||||||
{
|
{
|
||||||
QString sf = QDesktopServices::storageLocation(QDesktopServices::HomeLocation);
|
QString sf = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
|
||||||
QDir d(sf);
|
QDir d(sf);
|
||||||
|
|
||||||
// open the first entry of the home dir. Otherwise the dir picker comes
|
// open the first entry of the home dir. Otherwise the dir picker comes
|
||||||
|
@ -536,9 +537,11 @@ FolderWizard::FolderWizard(AccountPtr account, QWidget *parent)
|
||||||
{
|
{
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
setPage(Page_Source, _folderWizardSourcePage);
|
setPage(Page_Source, _folderWizardSourcePage);
|
||||||
|
_folderWizardSourcePage->installEventFilter(this);
|
||||||
if (!Theme::instance()->singleSyncFolder()) {
|
if (!Theme::instance()->singleSyncFolder()) {
|
||||||
_folderWizardTargetPage = new FolderWizardRemotePath(account);
|
_folderWizardTargetPage = new FolderWizardRemotePath(account);
|
||||||
setPage(Page_Target, _folderWizardTargetPage);
|
setPage(Page_Target, _folderWizardTargetPage);
|
||||||
|
_folderWizardTargetPage->installEventFilter(this);
|
||||||
}
|
}
|
||||||
setPage(Page_SelectiveSync, _folderWizardSelectiveSyncPage);
|
setPage(Page_SelectiveSync, _folderWizardSelectiveSyncPage);
|
||||||
|
|
||||||
|
@ -551,5 +554,25 @@ FolderWizard::~FolderWizard()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FolderWizard::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
if (event->type() == QEvent::LayoutRequest) {
|
||||||
|
// Workaround QTBUG-3396: forces QWizardPrivate::updateLayout()
|
||||||
|
QTimer::singleShot(0, this, [this] { setTitleFormat(titleFormat()); });
|
||||||
|
}
|
||||||
|
return QWizard::eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderWizard::resizeEvent(QResizeEvent *event)
|
||||||
|
{
|
||||||
|
QWizard::resizeEvent(event);
|
||||||
|
|
||||||
|
// workaround for QTBUG-22819: when the error label word wrap, the minimum height is not adjusted
|
||||||
|
int hfw = currentPage()->heightForWidth(currentPage()->width());
|
||||||
|
if (currentPage()->height() < hfw) {
|
||||||
|
currentPage()->setMinimumSize(currentPage()->minimumSizeHint().width(), hfw);
|
||||||
|
setTitleFormat(titleFormat()); // And another workaround for QTBUG-3396
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
|
@ -149,6 +149,9 @@ public:
|
||||||
explicit FolderWizard(AccountPtr account, QWidget *parent = 0);
|
explicit FolderWizard(AccountPtr account, QWidget *parent = 0);
|
||||||
~FolderWizard();
|
~FolderWizard();
|
||||||
|
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
void resizeEvent(QResizeEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FolderWizardLocalPath *_folderWizardSourcePage;
|
FolderWizardLocalPath *_folderWizardSourcePage;
|
||||||
FolderWizardRemotePath *_folderWizardTargetPage;
|
FolderWizardRemotePath *_folderWizardTargetPage;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QUrlQuery>
|
||||||
|
|
||||||
using namespace OCC;
|
using namespace OCC;
|
||||||
|
|
||||||
|
@ -45,8 +46,10 @@ bool Utility::openBrowser(const QUrl &url, QWidget *errorWidgetParent)
|
||||||
bool Utility::openEmailComposer(const QString &subject, const QString &body, QWidget *errorWidgetParent)
|
bool Utility::openEmailComposer(const QString &subject, const QString &body, QWidget *errorWidgetParent)
|
||||||
{
|
{
|
||||||
QUrl url(QLatin1String("mailto:"));
|
QUrl url(QLatin1String("mailto:"));
|
||||||
url.setQueryItems({ { QLatin1String("subject"), subject },
|
QUrlQuery query;
|
||||||
|
query.setQueryItems({ { QLatin1String("subject"), subject },
|
||||||
{ QLatin1String("body"), body } });
|
{ QLatin1String("body"), body } });
|
||||||
|
url.setQuery(query);
|
||||||
|
|
||||||
if (!QDesktopServices::openUrl(url)) {
|
if (!QDesktopServices::openUrl(url)) {
|
||||||
if (errorWidgetParent) {
|
if (errorWidgetParent) {
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "ignorelisteditor.h"
|
#include "ignorelisteditor.h"
|
||||||
#include "folderman.h"
|
#include "folderman.h"
|
||||||
#include "ui_ignorelisteditor.h"
|
#include "ui_ignorelisteditor.h"
|
||||||
#include "excludedfiles.h"
|
|
||||||
|
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -61,7 +60,7 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent)
|
||||||
connect(ui->addPushButton, &QAbstractButton::clicked, this, &IgnoreListEditor::slotAddPattern);
|
connect(ui->addPushButton, &QAbstractButton::clicked, this, &IgnoreListEditor::slotAddPattern);
|
||||||
|
|
||||||
ui->tableWidget->resizeColumnsToContents();
|
ui->tableWidget->resizeColumnsToContents();
|
||||||
ui->tableWidget->horizontalHeader()->setResizeMode(patternCol, QHeaderView::Stretch);
|
ui->tableWidget->horizontalHeader()->setSectionResizeMode(patternCol, QHeaderView::Stretch);
|
||||||
ui->tableWidget->verticalHeader()->setVisible(false);
|
ui->tableWidget->verticalHeader()->setVisible(false);
|
||||||
|
|
||||||
ui->syncHiddenFilesCheckBox->setChecked(!FolderMan::instance()->ignoreHiddenFiles());
|
ui->syncHiddenFilesCheckBox->setChecked(!FolderMan::instance()->ignoreHiddenFiles());
|
||||||
|
@ -135,8 +134,6 @@ void IgnoreListEditor::slotUpdateLocalIgnoreList()
|
||||||
folder->journalDb()->forceRemoteDiscoveryNextSync();
|
folder->journalDb()->forceRemoteDiscoveryNextSync();
|
||||||
folderMan->scheduleFolder(folder);
|
folderMan->scheduleFolder(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExcludedFiles::instance().reloadExcludes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IgnoreListEditor::slotAddPattern()
|
void IgnoreListEditor::slotAddPattern()
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "common/syncjournalfilerecord.h"
|
#include "common/syncjournalfilerecord.h"
|
||||||
#include "elidedlabel.h"
|
#include "elidedlabel.h"
|
||||||
|
|
||||||
|
|
||||||
#include "ui_issueswidget.h"
|
#include "ui_issueswidget.h"
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
@ -54,6 +55,9 @@ IssuesWidget::IssuesWidget(QWidget *parent)
|
||||||
connect(_ui->_treeWidget, &QTreeWidget::itemActivated, this, &IssuesWidget::slotOpenFile);
|
connect(_ui->_treeWidget, &QTreeWidget::itemActivated, this, &IssuesWidget::slotOpenFile);
|
||||||
connect(_ui->copyIssuesButton, &QAbstractButton::clicked, this, &IssuesWidget::copyToClipboard);
|
connect(_ui->copyIssuesButton, &QAbstractButton::clicked, this, &IssuesWidget::copyToClipboard);
|
||||||
|
|
||||||
|
_ui->_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(_ui->_treeWidget, &QTreeWidget::customContextMenuRequested, this, &IssuesWidget::slotItemContextMenu);
|
||||||
|
|
||||||
connect(_ui->showIgnores, &QAbstractButton::toggled, this, &IssuesWidget::slotRefreshIssues);
|
connect(_ui->showIgnores, &QAbstractButton::toggled, this, &IssuesWidget::slotRefreshIssues);
|
||||||
connect(_ui->showWarnings, &QAbstractButton::toggled, this, &IssuesWidget::slotRefreshIssues);
|
connect(_ui->showWarnings, &QAbstractButton::toggled, this, &IssuesWidget::slotRefreshIssues);
|
||||||
connect(_ui->filterAccount, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &IssuesWidget::slotRefreshIssues);
|
connect(_ui->filterAccount, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &IssuesWidget::slotRefreshIssues);
|
||||||
|
@ -85,7 +89,7 @@ IssuesWidget::IssuesWidget(QWidget *parent)
|
||||||
_ui->_treeWidget->setHeaderLabels(header);
|
_ui->_treeWidget->setHeaderLabels(header);
|
||||||
int timestampColumnWidth =
|
int timestampColumnWidth =
|
||||||
ActivityItemDelegate::rowHeight() // icon
|
ActivityItemDelegate::rowHeight() // icon
|
||||||
+ _ui->_treeWidget->fontMetrics().width(ProtocolWidget::timeString(QDateTime::currentDateTime()))
|
+ _ui->_treeWidget->fontMetrics().width(ProtocolItem::timeString(QDateTime::currentDateTime()))
|
||||||
+ timestampColumnExtra;
|
+ timestampColumnExtra;
|
||||||
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
||||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||||
|
@ -198,7 +202,7 @@ void IssuesWidget::slotItemCompleted(const QString &folder, const SyncFileItemPt
|
||||||
{
|
{
|
||||||
if (!item->hasErrorStatus())
|
if (!item->hasErrorStatus())
|
||||||
return;
|
return;
|
||||||
QTreeWidgetItem *line = ProtocolWidget::createCompletedTreewidgetItem(folder, *item);
|
QTreeWidgetItem *line = ProtocolItem::create(folder, *item);
|
||||||
if (!line)
|
if (!line)
|
||||||
return;
|
return;
|
||||||
addItem(line);
|
addItem(line);
|
||||||
|
@ -233,6 +237,15 @@ void IssuesWidget::slotAccountRemoved(AccountState *account)
|
||||||
updateAccountChoiceVisibility();
|
updateAccountChoiceVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IssuesWidget::slotItemContextMenu(const QPoint &pos)
|
||||||
|
{
|
||||||
|
auto item = _ui->_treeWidget->itemAt(pos);
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
auto globalPos = _ui->_treeWidget->viewport()->mapToGlobal(pos);
|
||||||
|
ProtocolItem::openContextMenu(globalPos, item, this);
|
||||||
|
}
|
||||||
|
|
||||||
void IssuesWidget::updateAccountChoiceVisibility()
|
void IssuesWidget::updateAccountChoiceVisibility()
|
||||||
{
|
{
|
||||||
bool visible = _ui->filterAccount->count() > 2;
|
bool visible = _ui->filterAccount->count() > 2;
|
||||||
|
@ -373,8 +386,8 @@ void IssuesWidget::addError(const QString &folderAlias, const QString &message,
|
||||||
|
|
||||||
QStringList columns;
|
QStringList columns;
|
||||||
QDateTime timestamp = QDateTime::currentDateTime();
|
QDateTime timestamp = QDateTime::currentDateTime();
|
||||||
const QString timeStr = ProtocolWidget::timeString(timestamp);
|
const QString timeStr = ProtocolItem::timeString(timestamp);
|
||||||
const QString longTimeStr = ProtocolWidget::timeString(timestamp, QLocale::LongFormat);
|
const QString longTimeStr = ProtocolItem::timeString(timestamp, QLocale::LongFormat);
|
||||||
|
|
||||||
columns << timeStr;
|
columns << timeStr;
|
||||||
columns << ""; // no "File" entry
|
columns << ""; // no "File" entry
|
||||||
|
@ -383,7 +396,7 @@ void IssuesWidget::addError(const QString &folderAlias, const QString &message,
|
||||||
|
|
||||||
QIcon icon = Theme::instance()->syncStateIcon(SyncResult::Error);
|
QIcon icon = Theme::instance()->syncStateIcon(SyncResult::Error);
|
||||||
|
|
||||||
QTreeWidgetItem *twitem = new SortedTreeWidgetItem(columns);
|
QTreeWidgetItem *twitem = new ProtocolItem(columns);
|
||||||
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
|
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
|
||||||
twitem->setData(0, Qt::UserRole, timestamp);
|
twitem->setData(0, Qt::UserRole, timestamp);
|
||||||
twitem->setIcon(0, icon);
|
twitem->setIcon(0, icon);
|
||||||
|
|
|
@ -68,6 +68,7 @@ private slots:
|
||||||
void slotUpdateFolderFilters();
|
void slotUpdateFolderFilters();
|
||||||
void slotAccountAdded(AccountState *account);
|
void slotAccountAdded(AccountState *account);
|
||||||
void slotAccountRemoved(AccountState *account);
|
void slotAccountRemoved(AccountState *account);
|
||||||
|
void slotItemContextMenu(const QPoint &pos);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateAccountChoiceVisibility();
|
void updateAccountChoiceVisibility();
|
||||||
|
|
|
@ -128,8 +128,8 @@ void NotificationWidget::slotNotificationRequestFinished(int statusCode)
|
||||||
|
|
||||||
QString timeStr = locale.toString(QTime::currentTime());
|
QString timeStr = locale.toString(QTime::currentTime());
|
||||||
|
|
||||||
// the ocs API returns stat code 100 if it succeeded.
|
// the ocs API returns stat code 100 or 200 inside the xml if it succeeded.
|
||||||
if (statusCode != OCS_SUCCESS_STATUS_CODE) {
|
if (statusCode != OCS_SUCCESS_STATUS_CODE && statusCode != OCS_SUCCESS_STATUS_CODE_V2) {
|
||||||
qCWarning(lcNotifications) << "Notification Request to Server failed, leave button visible.";
|
qCWarning(lcNotifications) << "Notification Request to Server failed, leave button visible.";
|
||||||
for (i = 0; i < _buttons.count(); i++) {
|
for (i = 0; i < _buttons.count(); i++) {
|
||||||
_buttons.at(i)->setEnabled(true);
|
_buttons.at(i)->setEnabled(true);
|
||||||
|
|
|
@ -28,6 +28,7 @@ OcsJob::OcsJob(AccountPtr account)
|
||||||
: AbstractNetworkJob(account, "")
|
: AbstractNetworkJob(account, "")
|
||||||
{
|
{
|
||||||
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE);
|
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE);
|
||||||
|
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE_V2);
|
||||||
setIgnoreCredentialFailure(true);
|
setIgnoreCredentialFailure(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,15 +52,16 @@ void OcsJob::appendPath(const QString &id)
|
||||||
setPath(path() + QLatin1Char('/') + id);
|
setPath(path() + QLatin1Char('/') + id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<QPair<QByteArray, QByteArray>>
|
static QUrlQuery percentEncodeQueryItems(
|
||||||
percentEncodeQueryItems(
|
|
||||||
const QList<QPair<QString, QString>> &items)
|
const QList<QPair<QString, QString>> &items)
|
||||||
{
|
{
|
||||||
QList<QPair<QByteArray, QByteArray>> result;
|
QUrlQuery result;
|
||||||
|
// Note: QUrlQuery::setQueryItems() does not fully percent encode
|
||||||
|
// the query items, see #5042
|
||||||
foreach (const auto &item, items) {
|
foreach (const auto &item, items) {
|
||||||
result.append(qMakePair(
|
result.addQueryItem(
|
||||||
QUrl::toPercentEncoding(item.first),
|
QUrl::toPercentEncoding(item.first),
|
||||||
QUrl::toPercentEncoding(item.second)));
|
QUrl::toPercentEncoding(item.second));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -70,13 +72,11 @@ void OcsJob::start()
|
||||||
req.setRawHeader("Ocs-APIREQUEST", "true");
|
req.setRawHeader("Ocs-APIREQUEST", "true");
|
||||||
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
|
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
QUrl url = Utility::concatUrlPath(account()->url(), path());
|
|
||||||
QBuffer *buffer = new QBuffer;
|
QBuffer *buffer = new QBuffer;
|
||||||
|
|
||||||
|
QUrlQuery queryItems;
|
||||||
if (_verb == "GET") {
|
if (_verb == "GET") {
|
||||||
// Note: QUrl::setQueryItems() does not fully percent encode
|
queryItems = percentEncodeQueryItems(_params);
|
||||||
// the query items, see #5042
|
|
||||||
url.setEncodedQueryItems(percentEncodeQueryItems(_params));
|
|
||||||
} else if (_verb == "POST" || _verb == "PUT") {
|
} else if (_verb == "POST" || _verb == "PUT") {
|
||||||
// Url encode the _postParams and put them in a buffer.
|
// Url encode the _postParams and put them in a buffer.
|
||||||
QByteArray postData;
|
QByteArray postData;
|
||||||
|
@ -90,12 +90,8 @@ void OcsJob::start()
|
||||||
}
|
}
|
||||||
buffer->setData(postData);
|
buffer->setData(postData);
|
||||||
}
|
}
|
||||||
|
queryItems.addQueryItem(QLatin1String("format"), QLatin1String("json"));
|
||||||
//We want json data
|
QUrl url = Utility::concatUrlPath(account()->url(), path(), queryItems);
|
||||||
auto queryItems = url.encodedQueryItems();
|
|
||||||
queryItems.append(qMakePair(QByteArray("format"), QByteArray("json")));
|
|
||||||
url.setEncodedQueryItems(queryItems);
|
|
||||||
|
|
||||||
sendRequest(_verb, url, req, buffer);
|
sendRequest(_verb, url, req, buffer);
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#define OCS_SUCCESS_STATUS_CODE 100
|
#define OCS_SUCCESS_STATUS_CODE 100
|
||||||
|
// Apparantly the v2.php URLs can return that
|
||||||
|
#define OCS_SUCCESS_STATUS_CODE_V2 200
|
||||||
|
|
||||||
class QJsonDocument;
|
class QJsonDocument;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
#include "folder.h"
|
#include "folder.h"
|
||||||
#include "openfilemanager.h"
|
#include "openfilemanager.h"
|
||||||
#include "activityitemdelegate.h"
|
#include "activityitemdelegate.h"
|
||||||
|
#include "guiutility.h"
|
||||||
|
#include "accountstate.h"
|
||||||
|
|
||||||
#include "ui_protocolwidget.h"
|
#include "ui_protocolwidget.h"
|
||||||
|
|
||||||
|
@ -32,7 +34,115 @@
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
bool SortedTreeWidgetItem::operator<(const QTreeWidgetItem &other) const
|
QString ProtocolItem::timeString(QDateTime dt, QLocale::FormatType format)
|
||||||
|
{
|
||||||
|
const QLocale loc = QLocale::system();
|
||||||
|
QString dtFormat = loc.dateTimeFormat(format);
|
||||||
|
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
|
||||||
|
dtFormat.replace(re, "\\1:mm:ss");
|
||||||
|
return loc.toString(dt, dtFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolItem *ProtocolItem::create(const QString &folder, const SyncFileItem &item)
|
||||||
|
{
|
||||||
|
auto f = FolderMan::instance()->folder(folder);
|
||||||
|
if (!f) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList columns;
|
||||||
|
QDateTime timestamp = QDateTime::currentDateTime();
|
||||||
|
const QString timeStr = timeString(timestamp);
|
||||||
|
const QString longTimeStr = timeString(timestamp, QLocale::LongFormat);
|
||||||
|
|
||||||
|
columns << timeStr;
|
||||||
|
columns << Utility::fileNameForGuiUse(item._originalFile);
|
||||||
|
columns << f->shortGuiLocalPath();
|
||||||
|
|
||||||
|
// If the error string is set, it's prefered because it is a useful user message.
|
||||||
|
QString message = item._errorString;
|
||||||
|
if (message.isEmpty()) {
|
||||||
|
message = Progress::asResultString(item);
|
||||||
|
}
|
||||||
|
columns << message;
|
||||||
|
|
||||||
|
QIcon icon;
|
||||||
|
if (item._status == SyncFileItem::NormalError
|
||||||
|
|| item._status == SyncFileItem::FatalError
|
||||||
|
|| item._status == SyncFileItem::DetailError
|
||||||
|
|| item._status == SyncFileItem::BlacklistedError) {
|
||||||
|
icon = Theme::instance()->syncStateIcon(SyncResult::Error);
|
||||||
|
} else if (Progress::isWarningKind(item._status)) {
|
||||||
|
icon = Theme::instance()->syncStateIcon(SyncResult::Problem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProgressInfo::isSizeDependent(item)) {
|
||||||
|
columns << Utility::octetsToString(item._size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolItem *twitem = new ProtocolItem(columns);
|
||||||
|
// Warning: The data and tooltips on the columns define an implicit
|
||||||
|
// interface and can only be changed with care.
|
||||||
|
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
|
||||||
|
twitem->setData(0, Qt::UserRole, timestamp);
|
||||||
|
twitem->setIcon(0, icon);
|
||||||
|
twitem->setToolTip(0, longTimeStr);
|
||||||
|
twitem->setToolTip(1, item._file);
|
||||||
|
twitem->setData(2, Qt::UserRole, folder);
|
||||||
|
twitem->setToolTip(3, message);
|
||||||
|
twitem->setData(3, Qt::UserRole, item._status);
|
||||||
|
return twitem;
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncJournalFileRecord ProtocolItem::syncJournalRecord(QTreeWidgetItem *item)
|
||||||
|
{
|
||||||
|
SyncJournalFileRecord rec;
|
||||||
|
auto f = folder(item);
|
||||||
|
if (!f)
|
||||||
|
return rec;
|
||||||
|
f->journalDb()->getFileRecord(item->toolTip(1), &rec);
|
||||||
|
return rec;
|
||||||
|
}
|
||||||
|
|
||||||
|
Folder *ProtocolItem::folder(QTreeWidgetItem *item)
|
||||||
|
{
|
||||||
|
return FolderMan::instance()->folder(item->data(2, Qt::UserRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProtocolItem::openContextMenu(QPoint globalPos, QTreeWidgetItem *item, QWidget *parent)
|
||||||
|
{
|
||||||
|
auto f = ProtocolItem::folder(item);
|
||||||
|
if (!f)
|
||||||
|
return;
|
||||||
|
AccountPtr account = f->accountState()->account();
|
||||||
|
auto rec = ProtocolItem::syncJournalRecord(item);
|
||||||
|
// rec might not be valid
|
||||||
|
|
||||||
|
auto menu = new QMenu(parent);
|
||||||
|
|
||||||
|
if (rec.isValid()) {
|
||||||
|
// "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.numericFileId(), parent,
|
||||||
|
[parent](const QString &url) {
|
||||||
|
Utility::openBrowser(url, parent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// More actions will be conditionally added to the context menu here later
|
||||||
|
|
||||||
|
if (menu->actions().isEmpty()) {
|
||||||
|
delete menu;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
menu->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
menu->popup(globalPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ProtocolItem::operator<(const QTreeWidgetItem &other) const
|
||||||
{
|
{
|
||||||
int column = treeWidget()->sortColumn();
|
int column = treeWidget()->sortColumn();
|
||||||
if (column != 0) {
|
if (column != 0) {
|
||||||
|
@ -56,6 +166,9 @@ ProtocolWidget::ProtocolWidget(QWidget *parent)
|
||||||
|
|
||||||
connect(_ui->_treeWidget, &QTreeWidget::itemActivated, this, &ProtocolWidget::slotOpenFile);
|
connect(_ui->_treeWidget, &QTreeWidget::itemActivated, this, &ProtocolWidget::slotOpenFile);
|
||||||
|
|
||||||
|
_ui->_treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
connect(_ui->_treeWidget, &QTreeWidget::customContextMenuRequested, this, &ProtocolWidget::slotItemContextMenu);
|
||||||
|
|
||||||
// Adjust copyToClipboard() when making changes here!
|
// Adjust copyToClipboard() when making changes here!
|
||||||
QStringList header;
|
QStringList header;
|
||||||
header << tr("Time");
|
header << tr("Time");
|
||||||
|
@ -71,7 +184,7 @@ ProtocolWidget::ProtocolWidget(QWidget *parent)
|
||||||
|
|
||||||
_ui->_treeWidget->setHeaderLabels(header);
|
_ui->_treeWidget->setHeaderLabels(header);
|
||||||
int timestampColumnWidth =
|
int timestampColumnWidth =
|
||||||
_ui->_treeWidget->fontMetrics().width(timeString(QDateTime::currentDateTime()))
|
_ui->_treeWidget->fontMetrics().width(ProtocolItem::timeString(QDateTime::currentDateTime()))
|
||||||
+ timestampColumnExtra;
|
+ timestampColumnExtra;
|
||||||
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
_ui->_treeWidget->setColumnWidth(0, timestampColumnWidth);
|
||||||
_ui->_treeWidget->setColumnWidth(1, 180);
|
_ui->_treeWidget->setColumnWidth(1, 180);
|
||||||
|
@ -119,14 +232,13 @@ void ProtocolWidget::hideEvent(QHideEvent *ev)
|
||||||
QWidget::hideEvent(ev);
|
QWidget::hideEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProtocolWidget::slotItemContextMenu(const QPoint &pos)
|
||||||
QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format)
|
|
||||||
{
|
{
|
||||||
const QLocale loc = QLocale::system();
|
auto item = _ui->_treeWidget->itemAt(pos);
|
||||||
QString dtFormat = loc.dateTimeFormat(format);
|
if (!item)
|
||||||
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
|
return;
|
||||||
dtFormat.replace(re, "\\1:mm:ss");
|
auto globalPos = _ui->_treeWidget->viewport()->mapToGlobal(pos);
|
||||||
return loc.toString(dt, dtFormat);
|
ProtocolItem::openContextMenu(globalPos, item, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProtocolWidget::slotOpenFile(QTreeWidgetItem *item, int)
|
void ProtocolWidget::slotOpenFile(QTreeWidgetItem *item, int)
|
||||||
|
@ -144,60 +256,11 @@ void ProtocolWidget::slotOpenFile(QTreeWidgetItem *item, int)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QTreeWidgetItem *ProtocolWidget::createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item)
|
|
||||||
{
|
|
||||||
auto f = FolderMan::instance()->folder(folder);
|
|
||||||
if (!f) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList columns;
|
|
||||||
QDateTime timestamp = QDateTime::currentDateTime();
|
|
||||||
const QString timeStr = timeString(timestamp);
|
|
||||||
const QString longTimeStr = timeString(timestamp, QLocale::LongFormat);
|
|
||||||
|
|
||||||
columns << timeStr;
|
|
||||||
columns << Utility::fileNameForGuiUse(item._originalFile);
|
|
||||||
columns << f->shortGuiLocalPath();
|
|
||||||
|
|
||||||
// If the error string is set, it's prefered because it is a useful user message.
|
|
||||||
QString message = item._errorString;
|
|
||||||
if (message.isEmpty()) {
|
|
||||||
message = Progress::asResultString(item);
|
|
||||||
}
|
|
||||||
columns << message;
|
|
||||||
|
|
||||||
QIcon icon;
|
|
||||||
if (item._status == SyncFileItem::NormalError
|
|
||||||
|| item._status == SyncFileItem::FatalError
|
|
||||||
|| item._status == SyncFileItem::DetailError
|
|
||||||
|| item._status == SyncFileItem::BlacklistedError) {
|
|
||||||
icon = Theme::instance()->syncStateIcon(SyncResult::Error);
|
|
||||||
} else if (Progress::isWarningKind(item._status)) {
|
|
||||||
icon = Theme::instance()->syncStateIcon(SyncResult::Problem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ProgressInfo::isSizeDependent(item)) {
|
|
||||||
columns << Utility::octetsToString(item._size);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTreeWidgetItem *twitem = new SortedTreeWidgetItem(columns);
|
|
||||||
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
|
|
||||||
twitem->setData(0, Qt::UserRole, timestamp);
|
|
||||||
twitem->setIcon(0, icon);
|
|
||||||
twitem->setToolTip(0, longTimeStr);
|
|
||||||
twitem->setToolTip(1, item._file);
|
|
||||||
twitem->setData(2, Qt::UserRole, folder);
|
|
||||||
twitem->setToolTip(3, message);
|
|
||||||
twitem->setData(3, Qt::UserRole, item._status);
|
|
||||||
return twitem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
|
void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
|
||||||
{
|
{
|
||||||
if (item->hasErrorStatus())
|
if (item->hasErrorStatus())
|
||||||
return;
|
return;
|
||||||
QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, *item);
|
QTreeWidgetItem *line = ProtocolItem::create(folder, *item);
|
||||||
if (line) {
|
if (line) {
|
||||||
// Limit the number of items
|
// Limit the number of items
|
||||||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||||
|
|
|
@ -35,16 +35,25 @@ namespace Ui {
|
||||||
class Application;
|
class Application;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A QTreeWidgetItem with special sorting.
|
* The items used in the protocol and issue QTreeWidget
|
||||||
*
|
*
|
||||||
* It allows items for global entries to be moved to the top if the
|
* Special sorting: It allows items for global entries to be moved to the top if the
|
||||||
* sorting section is the "Time" column.
|
* sorting section is the "Time" column.
|
||||||
*/
|
*/
|
||||||
class SortedTreeWidgetItem : public QTreeWidgetItem
|
class ProtocolItem : public QTreeWidgetItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using QTreeWidgetItem::QTreeWidgetItem;
|
using QTreeWidgetItem::QTreeWidgetItem;
|
||||||
|
|
||||||
|
// Shared with IssueWidget
|
||||||
|
static ProtocolItem *create(const QString &folder, const SyncFileItem &item);
|
||||||
|
static QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat);
|
||||||
|
|
||||||
|
static SyncJournalFileRecord syncJournalRecord(QTreeWidgetItem *item);
|
||||||
|
static Folder *folder(QTreeWidgetItem *item);
|
||||||
|
|
||||||
|
static void openContextMenu(QPoint globalPos, QTreeWidgetItem *item, QWidget *parent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool operator<(const QTreeWidgetItem &other) const override;
|
bool operator<(const QTreeWidgetItem &other) const override;
|
||||||
};
|
};
|
||||||
|
@ -63,10 +72,6 @@ public:
|
||||||
|
|
||||||
void storeSyncActivity(QTextStream &ts);
|
void storeSyncActivity(QTextStream &ts);
|
||||||
|
|
||||||
// Shared with IssueWidget
|
|
||||||
static QTreeWidgetItem *createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item);
|
|
||||||
static QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat);
|
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
void slotItemCompleted(const QString &folder, const SyncFileItemPtr &item);
|
||||||
void slotOpenFile(QTreeWidgetItem *item, int);
|
void slotOpenFile(QTreeWidgetItem *item, int);
|
||||||
|
@ -75,6 +80,9 @@ protected:
|
||||||
void showEvent(QShowEvent *);
|
void showEvent(QShowEvent *);
|
||||||
void hideEvent(QHideEvent *);
|
void hideEvent(QHideEvent *);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void slotItemContextMenu(const QPoint &pos);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void copyToClipboard();
|
void copyToClipboard();
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "selectivesyncdialog.h"
|
#include "selectivesyncdialog.h"
|
||||||
#include "folder.h"
|
#include "folder.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "excludedfiles.h"
|
|
||||||
#include "networkjobs.h"
|
#include "networkjobs.h"
|
||||||
#include "theme.h"
|
#include "theme.h"
|
||||||
#include "folderman.h"
|
#include "folderman.h"
|
||||||
|
@ -95,6 +94,9 @@ SelectiveSyncWidget::SelectiveSyncWidget(AccountPtr account, QWidget *parent)
|
||||||
_folderTree->header()->setStretchLastSection(true);
|
_folderTree->header()->setStretchLastSection(true);
|
||||||
_folderTree->headerItem()->setText(0, tr("Name"));
|
_folderTree->headerItem()->setText(0, tr("Name"));
|
||||||
_folderTree->headerItem()->setText(1, tr("Size"));
|
_folderTree->headerItem()->setText(1, tr("Size"));
|
||||||
|
|
||||||
|
ConfigFile::setupDefaultExcludeFilePaths(_excludedFiles);
|
||||||
|
_excludedFiles.reloadExcludeFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
QSize SelectiveSyncWidget::sizeHint() const
|
QSize SelectiveSyncWidget::sizeHint() const
|
||||||
|
@ -204,7 +206,7 @@ void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
|
||||||
QMutableListIterator<QString> it(list);
|
QMutableListIterator<QString> it(list);
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
it.next();
|
it.next();
|
||||||
if (ExcludedFiles::instance().isExcluded(it.value(), pathToRemove, FolderMan::instance()->ignoreHiddenFiles()))
|
if (_excludedFiles.isExcluded(it.value(), pathToRemove, FolderMan::instance()->ignoreHiddenFiles()))
|
||||||
it.remove();
|
it.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
#include "accountfwd.h"
|
#include "accountfwd.h"
|
||||||
|
|
||||||
|
#include "csync_exclude.h"
|
||||||
|
|
||||||
class QTreeWidgetItem;
|
class QTreeWidgetItem;
|
||||||
class QTreeWidget;
|
class QTreeWidget;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
|
@ -72,6 +74,10 @@ private:
|
||||||
QLabel *_loading;
|
QLabel *_loading;
|
||||||
|
|
||||||
QTreeWidget *_folderTree;
|
QTreeWidget *_folderTree;
|
||||||
|
|
||||||
|
// During account setup we want to filter out excluded folders from the
|
||||||
|
// view without having a Folder.SyncEngine.ExcludedFiles instance.
|
||||||
|
ExcludedFiles _excludedFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,6 +24,8 @@ namespace OCC {
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcServerNotification, "gui.servernotification", QtInfoMsg)
|
Q_LOGGING_CATEGORY(lcServerNotification, "gui.servernotification", QtInfoMsg)
|
||||||
|
|
||||||
|
const QString notificationsPath = QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications");
|
||||||
|
|
||||||
ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
|
ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
|
@ -47,7 +49,7 @@ void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the previous notification job has finished, start next.
|
// if the previous notification job has finished, start next.
|
||||||
_notificationJob = new JsonApiJob(ptr->account(), QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications"), this);
|
_notificationJob = new JsonApiJob(ptr->account(), notificationsPath, this);
|
||||||
QObject::connect(_notificationJob.data(), &JsonApiJob::jsonReceived,
|
QObject::connect(_notificationJob.data(), &JsonApiJob::jsonReceived,
|
||||||
this, &ServerNotificationHandler::slotNotificationsReceived);
|
this, &ServerNotificationHandler::slotNotificationsReceived);
|
||||||
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState *>(ptr));
|
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState *>(ptr));
|
||||||
|
@ -94,6 +96,16 @@ void ServerNotificationHandler::slotNotificationsReceived(const QJsonDocument &j
|
||||||
|
|
||||||
a._links.append(al);
|
a._links.append(al);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add another action to dismiss notification on server
|
||||||
|
// https://github.com/owncloud/notifications/blob/master/docs/ocs-endpoint-v1.md#deleting-a-notification-for-a-user
|
||||||
|
ActivityLink al;
|
||||||
|
al._label = tr("Dismiss");
|
||||||
|
al._link = Utility::concatUrlPath(ai->account()->url(), notificationsPath + "/" + json.value("notification_id").toString()).toString();
|
||||||
|
al._verb = "DELETE";
|
||||||
|
al._isPrimary = false;
|
||||||
|
a._links.append(al);
|
||||||
|
|
||||||
list.append(a);
|
list.append(a);
|
||||||
}
|
}
|
||||||
emit newNotificationList(list);
|
emit newNotificationList(list);
|
||||||
|
|
|
@ -57,23 +57,6 @@ namespace OCC {
|
||||||
|
|
||||||
#include "settingsdialogcommon.cpp"
|
#include "settingsdialogcommon.cpp"
|
||||||
|
|
||||||
static QIcon circleMask(const QImage &avatar)
|
|
||||||
{
|
|
||||||
int dim = avatar.width();
|
|
||||||
|
|
||||||
QPixmap fixedImage(dim, dim);
|
|
||||||
fixedImage.fill(Qt::transparent);
|
|
||||||
|
|
||||||
QPainter imgPainter(&fixedImage);
|
|
||||||
QPainterPath clip;
|
|
||||||
clip.addEllipse(0, 0, dim, dim);
|
|
||||||
imgPainter.setClipPath(clip);
|
|
||||||
imgPainter.drawImage(0, 0, avatar);
|
|
||||||
imgPainter.end();
|
|
||||||
|
|
||||||
return QIcon(fixedImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
|
// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
|
||||||
//
|
//
|
||||||
|
@ -232,7 +215,7 @@ void SettingsDialog::accountAdded(AccountState *s)
|
||||||
accountAction = createColorAwareAction(QLatin1String(":/client/resources/account.png"),
|
accountAction = createColorAwareAction(QLatin1String(":/client/resources/account.png"),
|
||||||
actionText);
|
actionText);
|
||||||
} else {
|
} else {
|
||||||
QIcon icon = circleMask(avatar);
|
QIcon icon(QPixmap::fromImage(AvatarJob::makeCircularAvatar(avatar)));
|
||||||
accountAction = createActionWithIcon(icon, actionText);
|
accountAction = createActionWithIcon(icon, actionText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +248,7 @@ void SettingsDialog::slotAccountAvatarChanged()
|
||||||
if (action) {
|
if (action) {
|
||||||
QImage pix = account->avatar();
|
QImage pix = account->avatar();
|
||||||
if (!pix.isNull()) {
|
if (!pix.isNull()) {
|
||||||
action->setIcon(circleMask(pix));
|
action->setIcon(QPixmap::fromImage(AvatarJob::makeCircularAvatar(pix)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,7 +308,7 @@ void SettingsDialog::customizeStyle()
|
||||||
QString altBase(palette().alternateBase().color().name());
|
QString altBase(palette().alternateBase().color().name());
|
||||||
QString dark(palette().dark().color().name());
|
QString dark(palette().dark().color().name());
|
||||||
QString background(palette().base().color().name());
|
QString background(palette().base().color().name());
|
||||||
_toolBar->setStyleSheet(QString::fromAscii(TOOLBAR_CSS).arg(background, dark, highlightColor, altBase));
|
_toolBar->setStyleSheet(QString::fromLatin1(TOOLBAR_CSS).arg(background, dark, highlightColor, altBase));
|
||||||
|
|
||||||
Q_FOREACH (QAction *a, _actionGroup->actions()) {
|
Q_FOREACH (QAction *a, _actionGroup->actions()) {
|
||||||
QIcon icon = createColorAwareIcon(a->property("iconPath").toString());
|
QIcon icon = createColorAwareIcon(a->property("iconPath").toString());
|
||||||
|
|
|
@ -81,8 +81,9 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
|
||||||
connect(_ui->pushButton_setPassword, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPasswordReturnPressed);
|
connect(_ui->pushButton_setPassword, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPasswordReturnPressed);
|
||||||
connect(_ui->checkBox_expire, &QAbstractButton::clicked, this, &ShareLinkWidget::slotCheckBoxExpireClicked);
|
connect(_ui->checkBox_expire, &QAbstractButton::clicked, this, &ShareLinkWidget::slotCheckBoxExpireClicked);
|
||||||
connect(_ui->calendar, &QDateTimeEdit::dateChanged, this, &ShareLinkWidget::slotExpireDateChanged);
|
connect(_ui->calendar, &QDateTimeEdit::dateChanged, this, &ShareLinkWidget::slotExpireDateChanged);
|
||||||
connect(_ui->checkBox_editing, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPermissionsCheckboxClicked);
|
connect(_ui->radio_readOnly, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPermissionsClicked);
|
||||||
connect(_ui->checkBox_fileListing, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPermissionsCheckboxClicked);
|
connect(_ui->radio_readWrite, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPermissionsClicked);
|
||||||
|
connect(_ui->radio_uploadOnly, &QAbstractButton::clicked, this, &ShareLinkWidget::slotPermissionsClicked);
|
||||||
|
|
||||||
_ui->errorLabel->hide();
|
_ui->errorLabel->hide();
|
||||||
|
|
||||||
|
@ -149,7 +150,7 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
|
||||||
// File can't have public upload set; we also hide it if the capability isn't there
|
// File can't have public upload set; we also hide it if the capability isn't there
|
||||||
_ui->widget_editing->setVisible(
|
_ui->widget_editing->setVisible(
|
||||||
!_isFile && _account->capabilities().sharePublicLinkAllowUpload());
|
!_isFile && _account->capabilities().sharePublicLinkAllowUpload());
|
||||||
_ui->checkBox_fileListing->setVisible(
|
_ui->radio_uploadOnly->setVisible(
|
||||||
_account->capabilities().sharePublicLinkSupportsUploadOnly());
|
_account->capabilities().sharePublicLinkSupportsUploadOnly());
|
||||||
|
|
||||||
|
|
||||||
|
@ -158,12 +159,12 @@ ShareLinkWidget::ShareLinkWidget(AccountPtr account,
|
||||||
_linkContextMenu = new QMenu(this);
|
_linkContextMenu = new QMenu(this);
|
||||||
connect(_linkContextMenu, &QMenu::triggered,
|
connect(_linkContextMenu, &QMenu::triggered,
|
||||||
this, &ShareLinkWidget::slotLinkContextMenuActionTriggered);
|
this, &ShareLinkWidget::slotLinkContextMenuActionTriggered);
|
||||||
_deleteLinkAction = _linkContextMenu->addAction(tr("Delete"));
|
|
||||||
_openLinkAction = _linkContextMenu->addAction(tr("Open link in browser"));
|
_openLinkAction = _linkContextMenu->addAction(tr("Open link in browser"));
|
||||||
_copyLinkAction = _linkContextMenu->addAction(tr("Copy link to clipboard"));
|
_copyLinkAction = _linkContextMenu->addAction(tr("Copy link to clipboard"));
|
||||||
_copyDirectLinkAction = _linkContextMenu->addAction(tr("Copy link to clipboard (direct download)"));
|
_copyDirectLinkAction = _linkContextMenu->addAction(tr("Copy link to clipboard (direct download)"));
|
||||||
_emailLinkAction = _linkContextMenu->addAction(tr("Send link by email"));
|
_emailLinkAction = _linkContextMenu->addAction(tr("Send link by email"));
|
||||||
_emailDirectLinkAction = _linkContextMenu->addAction(tr("Send link by email (direct download)"));
|
_emailDirectLinkAction = _linkContextMenu->addAction(tr("Send link by email (direct download)"));
|
||||||
|
_deleteLinkAction = _linkContextMenu->addAction(tr("Delete"));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create the share manager and connect it properly
|
* Create the share manager and connect it properly
|
||||||
|
@ -282,7 +283,9 @@ void ShareLinkWidget::slotShareSelectionChanged()
|
||||||
auto share = selectedShare();
|
auto share = selectedShare();
|
||||||
if (!share) {
|
if (!share) {
|
||||||
_ui->shareProperties->setEnabled(false);
|
_ui->shareProperties->setEnabled(false);
|
||||||
_ui->checkBox_editing->setChecked(false);
|
_ui->radio_readOnly->setChecked(false);
|
||||||
|
_ui->radio_readWrite->setChecked(false);
|
||||||
|
_ui->radio_uploadOnly->setChecked(false);
|
||||||
_ui->checkBox_expire->setChecked(false);
|
_ui->checkBox_expire->setChecked(false);
|
||||||
_ui->checkBox_password->setChecked(false);
|
_ui->checkBox_password->setChecked(false);
|
||||||
return;
|
return;
|
||||||
|
@ -292,8 +295,11 @@ void ShareLinkWidget::slotShareSelectionChanged()
|
||||||
|
|
||||||
_ui->checkBox_password->setEnabled(!_passwordRequired);
|
_ui->checkBox_password->setEnabled(!_passwordRequired);
|
||||||
_ui->checkBox_expire->setEnabled(!_expiryRequired);
|
_ui->checkBox_expire->setEnabled(!_expiryRequired);
|
||||||
_ui->checkBox_editing->setEnabled(
|
_ui->widget_editing->setEnabled(true);
|
||||||
_account->capabilities().sharePublicLinkAllowUpload());
|
if (!_account->capabilities().sharePublicLinkAllowUpload()) {
|
||||||
|
_ui->radio_readWrite->setEnabled(false);
|
||||||
|
_ui->radio_uploadOnly->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
// Password state
|
// Password state
|
||||||
_ui->checkBox_password->setText(tr("P&assword protect"));
|
_ui->checkBox_password->setText(tr("P&assword protect"));
|
||||||
|
@ -324,9 +330,15 @@ void ShareLinkWidget::slotShareSelectionChanged()
|
||||||
|
|
||||||
// Public upload state (box is hidden for files)
|
// Public upload state (box is hidden for files)
|
||||||
if (!_isFile) {
|
if (!_isFile) {
|
||||||
_ui->checkBox_editing->setChecked(share->getPublicUpload());
|
if (share->getPublicUpload()) {
|
||||||
_ui->checkBox_fileListing->setChecked(share->getShowFileListing());
|
if (share->getShowFileListing()) {
|
||||||
_ui->checkBox_fileListing->setEnabled(share->getPublicUpload());
|
_ui->radio_readWrite->setChecked(true);
|
||||||
|
} else {
|
||||||
|
_ui->radio_uploadOnly->setChecked(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_ui->radio_readOnly->setChecked(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +468,7 @@ void ShareLinkWidget::slotCreateShareRequiresPassword(const QString &message)
|
||||||
_ui->checkBox_password->setEnabled(false);
|
_ui->checkBox_password->setEnabled(false);
|
||||||
_ui->checkBox_password->setText(tr("Public shå requires a password"));
|
_ui->checkBox_password->setText(tr("Public shå requires a password"));
|
||||||
_ui->checkBox_expire->setEnabled(false);
|
_ui->checkBox_expire->setEnabled(false);
|
||||||
_ui->checkBox_editing->setEnabled(false);
|
_ui->widget_editing->setEnabled(false);
|
||||||
if (!message.isEmpty()) {
|
if (!message.isEmpty()) {
|
||||||
_ui->errorLabel->setText(message);
|
_ui->errorLabel->setText(message);
|
||||||
_ui->errorLabel->show();
|
_ui->errorLabel->show();
|
||||||
|
@ -579,19 +591,18 @@ void ShareLinkWidget::slotDeleteShareClicked()
|
||||||
confirmAndDeleteShare(share);
|
confirmAndDeleteShare(share);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareLinkWidget::slotPermissionsCheckboxClicked()
|
void ShareLinkWidget::slotPermissionsClicked()
|
||||||
{
|
{
|
||||||
if (auto current = selectedShare()) {
|
if (auto current = selectedShare()) {
|
||||||
_ui->checkBox_editing->setEnabled(false);
|
_ui->widget_editing->setEnabled(false);
|
||||||
_ui->checkBox_fileListing->setEnabled(false);
|
|
||||||
_pi_editing->startAnimation();
|
_pi_editing->startAnimation();
|
||||||
_ui->errorLabel->hide();
|
_ui->errorLabel->hide();
|
||||||
|
|
||||||
SharePermissions perm = SharePermissionRead;
|
SharePermissions perm = SharePermissionRead;
|
||||||
if (_ui->checkBox_editing->isChecked() && _ui->checkBox_fileListing->isChecked()) {
|
if (_ui->radio_readWrite->isChecked()) {
|
||||||
perm = SharePermissionRead | SharePermissionCreate
|
perm = SharePermissionRead | SharePermissionCreate
|
||||||
| SharePermissionUpdate | SharePermissionDelete;
|
| SharePermissionUpdate | SharePermissionDelete;
|
||||||
} else if (_ui->checkBox_editing->isChecked() && !_ui->checkBox_fileListing->isChecked()) {
|
} else if (_ui->radio_uploadOnly->isChecked()) {
|
||||||
perm = SharePermissionCreate;
|
perm = SharePermissionCreate;
|
||||||
}
|
}
|
||||||
current->setPermissions(perm);
|
current->setPermissions(perm);
|
||||||
|
|
|
@ -65,7 +65,7 @@ private slots:
|
||||||
void slotCheckBoxPasswordClicked();
|
void slotCheckBoxPasswordClicked();
|
||||||
void slotCheckBoxExpireClicked();
|
void slotCheckBoxExpireClicked();
|
||||||
void slotPasswordReturnPressed();
|
void slotPasswordReturnPressed();
|
||||||
void slotPermissionsCheckboxClicked();
|
void slotPermissionsClicked();
|
||||||
void slotExpireDateChanged(const QDate &date);
|
void slotExpireDateChanged(const QDate &date);
|
||||||
void slotPasswordChanged(const QString &newText);
|
void slotPasswordChanged(const QString &newText);
|
||||||
void slotNameEdited(QTableWidgetItem *item);
|
void slotNameEdited(QTableWidgetItem *item);
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>376</width>
|
<width>394</width>
|
||||||
<height>493</height>
|
<height>534</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -48,7 +48,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableWidget" name="linkShares">
|
<widget class="QTableWidget" name="linkShares">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
<verstretch>0</verstretch>
|
<verstretch>0</verstretch>
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
|
@ -191,36 +191,110 @@
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="bottomMargin">
|
<property name="verticalSpacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QCheckBox" name="checkBox_editing">
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Allow editing</string>
|
<string>Users can view and download contents.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="2" column="0">
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeHint" stdset="0">
|
<property name="sizeHint" stdset="0">
|
||||||
<size>
|
<size>
|
||||||
<width>40</width>
|
<width>20</width>
|
||||||
<height>20</height>
|
<height>6</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="6" column="0">
|
||||||
<widget class="QCheckBox" name="checkBox_fileListing">
|
<widget class="QRadioButton" name="radio_uploadOnly">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show file listing</string>
|
<string>Upload only (File Drop)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Receive files from others without revealing the contents of the folder.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QRadioButton" name="radio_readOnly">
|
||||||
|
<property name="text">
|
||||||
|
<string>Read only</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QRadioButton" name="radio_readWrite">
|
||||||
|
<property name="text">
|
||||||
|
<string>Read && Write</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Users can view, download, edit and upload contents.</string>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Fixed</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>6</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -287,7 +361,6 @@
|
||||||
<tabstop>nameLineEdit</tabstop>
|
<tabstop>nameLineEdit</tabstop>
|
||||||
<tabstop>createShareButton</tabstop>
|
<tabstop>createShareButton</tabstop>
|
||||||
<tabstop>linkShares</tabstop>
|
<tabstop>linkShares</tabstop>
|
||||||
<tabstop>checkBox_editing</tabstop>
|
|
||||||
<tabstop>checkBox_password</tabstop>
|
<tabstop>checkBox_password</tabstop>
|
||||||
<tabstop>lineEdit_password</tabstop>
|
<tabstop>lineEdit_password</tabstop>
|
||||||
<tabstop>pushButton_setPassword</tabstop>
|
<tabstop>pushButton_setPassword</tabstop>
|
||||||
|
|
|
@ -379,9 +379,9 @@ QSharedPointer<LinkShare> ShareManager::parseLinkShare(const QJsonObject &data)
|
||||||
// From ownCloud server version 8 on, a different share link scheme is used.
|
// From ownCloud server version 8 on, a different share link scheme is used.
|
||||||
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("index.php/s/") + data.value("token").toString())).toString();
|
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("index.php/s/") + data.value("token").toString())).toString();
|
||||||
} else {
|
} else {
|
||||||
QList<QPair<QString, QString>> queryArgs;
|
QUrlQuery queryArgs;
|
||||||
queryArgs.append(qMakePair(QString("service"), QString("files")));
|
queryArgs.addQueryItem(QLatin1String("service"), QLatin1String("files"));
|
||||||
queryArgs.append(qMakePair(QString("t"), data.value("token").toString()));
|
queryArgs.addQueryItem(QLatin1String("t"), data.value("token").toString());
|
||||||
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("public.php"), queryArgs).toString());
|
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("public.php"), queryArgs).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,9 @@
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QColor>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -178,31 +181,34 @@ void ShareUserGroupWidget::slotSharesFetched(const QList<QSharedPointer<Share>>
|
||||||
|
|
||||||
auto newViewPort = new QWidget(scrollArea);
|
auto newViewPort = new QWidget(scrollArea);
|
||||||
auto layout = new QVBoxLayout(newViewPort);
|
auto layout = new QVBoxLayout(newViewPort);
|
||||||
|
layout->setMargin(0);
|
||||||
|
layout->setSpacing(0);
|
||||||
|
|
||||||
QSize minimumSize = newViewPort->sizeHint();
|
QSize minimumSize = newViewPort->sizeHint();
|
||||||
int x = 0;
|
int x = 0;
|
||||||
|
|
||||||
if (shares.isEmpty()) {
|
foreach (const auto &share, shares) {
|
||||||
|
// We don't handle link shares
|
||||||
|
if (share->getShareType() == Share::TypeLink) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShareUserLine *s = new ShareUserLine(share, _maxSharingPermissions, _isFile, _ui->scrollArea);
|
||||||
|
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
|
||||||
|
connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares);
|
||||||
|
s->setBackgroundRole(layout->count() % 2 == 0 ? QPalette::Base : QPalette::AlternateBase);
|
||||||
|
layout->addWidget(s);
|
||||||
|
|
||||||
|
x++;
|
||||||
|
if (x <= 3) {
|
||||||
|
minimumSize = newViewPort->sizeHint();
|
||||||
|
} else {
|
||||||
|
minimumSize.rwidth() = qMax(newViewPort->sizeHint().width(), minimumSize.width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (layout->isEmpty()) {
|
||||||
layout->addWidget(new QLabel(tr("The item is not shared with any users or groups")));
|
layout->addWidget(new QLabel(tr("The item is not shared with any users or groups")));
|
||||||
} else {
|
} else {
|
||||||
foreach (const auto &share, shares) {
|
|
||||||
// We don't handle link shares
|
|
||||||
if (share->getShareType() == Share::TypeLink) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShareUserLine *s = new ShareUserLine(share, _maxSharingPermissions, _isFile, _ui->scrollArea);
|
|
||||||
connect(s, &ShareUserLine::resizeRequested, this, &ShareUserGroupWidget::slotAdjustScrollWidgetSize);
|
|
||||||
connect(s, &ShareUserLine::visualDeletionDone, this, &ShareUserGroupWidget::getShares);
|
|
||||||
layout->addWidget(s);
|
|
||||||
|
|
||||||
x++;
|
|
||||||
if (x <= 3) {
|
|
||||||
minimumSize = newViewPort->sizeHint();
|
|
||||||
} else {
|
|
||||||
minimumSize.rwidth() = qMax(newViewPort->sizeHint().width(), minimumSize.width());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
layout->addStretch(1);
|
layout->addStretch(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +422,65 @@ ShareUserLine::ShareUserLine(QSharedPointer<Share> share,
|
||||||
if (!share->account()->capabilities().shareResharing()) {
|
if (!share->account()->capabilities().shareResharing()) {
|
||||||
_ui->permissionShare->hide();
|
_ui->permissionShare->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadAvatar();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareUserLine::loadAvatar()
|
||||||
|
{
|
||||||
|
const int avatarSize = 36;
|
||||||
|
|
||||||
|
// Set size of the placeholder
|
||||||
|
_ui->avatar->setMinimumHeight(avatarSize);
|
||||||
|
_ui->avatar->setMinimumWidth(avatarSize);
|
||||||
|
_ui->avatar->setMaximumHeight(avatarSize);
|
||||||
|
_ui->avatar->setMaximumWidth(avatarSize);
|
||||||
|
_ui->avatar->setAlignment(Qt::AlignCenter);
|
||||||
|
|
||||||
|
/* Create the fallback avatar.
|
||||||
|
*
|
||||||
|
* This will be shown until the avatar image data arrives.
|
||||||
|
*/
|
||||||
|
const QByteArray hash = QCryptographicHash::hash(_ui->sharedWith->text().toUtf8(), QCryptographicHash::Md5);
|
||||||
|
double hue = static_cast<quint8>(hash[0]) / 255.;
|
||||||
|
|
||||||
|
// See core/js/placeholder.js for details on colors and styling
|
||||||
|
const QColor bg = QColor::fromHslF(hue, 0.7, 0.68);
|
||||||
|
const QString style = QString(R"(* {
|
||||||
|
color: #fff;
|
||||||
|
background-color: %1;
|
||||||
|
border-radius: %2px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: %2px;
|
||||||
|
font-size: %2px;
|
||||||
|
})").arg(bg.name(), QString::number(avatarSize / 2));
|
||||||
|
_ui->avatar->setStyleSheet(style);
|
||||||
|
|
||||||
|
// The avatar label is the first character of the user name.
|
||||||
|
const QString text = _share->getShareWith()->displayName();
|
||||||
|
_ui->avatar->setText(text.at(0).toUpper());
|
||||||
|
|
||||||
|
/* Start the network job to fetch the avatar data.
|
||||||
|
*
|
||||||
|
* Currently only regular users can have avatars.
|
||||||
|
*/
|
||||||
|
if (_share->getShareWith()->type() == Sharee::User) {
|
||||||
|
AvatarJob *job = new AvatarJob(_share->account(), _share->getShareWith()->shareWith(), avatarSize, this);
|
||||||
|
connect(job, &AvatarJob::avatarPixmap, this, &ShareUserLine::slotAvatarLoaded);
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShareUserLine::slotAvatarLoaded(QImage avatar)
|
||||||
|
{
|
||||||
|
if (avatar.isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
avatar = AvatarJob::makeCircularAvatar(avatar);
|
||||||
|
_ui->avatar->setPixmap(QPixmap::fromImage(avatar));
|
||||||
|
|
||||||
|
// Remove the stylesheet for the fallback avatar
|
||||||
|
_ui->avatar->setStyleSheet("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShareUserLine::on_deleteShareButton_clicked()
|
void ShareUserLine::on_deleteShareButton_clicked()
|
||||||
|
|
|
@ -131,8 +131,11 @@ private slots:
|
||||||
void slotShareDeleted();
|
void slotShareDeleted();
|
||||||
void slotPermissionsSet();
|
void slotPermissionsSet();
|
||||||
|
|
||||||
|
void slotAvatarLoaded(QImage avatar);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void displayPermissions();
|
void displayPermissions();
|
||||||
|
void loadAvatar();
|
||||||
|
|
||||||
Ui::ShareUserLine *_ui;
|
Ui::ShareUserLine *_ui;
|
||||||
QSharedPointer<Share> _share;
|
QSharedPointer<Share> _share;
|
||||||
|
|
|
@ -10,27 +10,25 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>468</width>
|
<width>468</width>
|
||||||
<height>64</height>
|
<height>46</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="autoFillBackground">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<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>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="avatar">
|
||||||
|
<property name="text">
|
||||||
|
<string>TextLabel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="sharedWith">
|
<widget class="QLabel" name="sharedWith">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -55,36 +53,24 @@
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QFrame" name="frame">
|
<widget class="QCheckBox" name="permissionShare">
|
||||||
<property name="frameShape">
|
<property name="text">
|
||||||
<enum>QFrame::StyledPanel</enum>
|
<string>can share</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShadow">
|
</widget>
|
||||||
<enum>QFrame::Raised</enum>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="permissionsEdit">
|
||||||
|
<property name="text">
|
||||||
|
<string>can edit</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QToolButton" name="permissionToolButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>...</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="permissionsEdit">
|
|
||||||
<property name="text">
|
|
||||||
<string>can edit</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="permissionShare">
|
|
||||||
<property name="text">
|
|
||||||
<string>can share</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QToolButton" name="permissionToolButton">
|
|
||||||
<property name="text">
|
|
||||||
<string>...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
|
|
|
@ -65,7 +65,7 @@ static inline QString removeTrailingSlash(QString path)
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString buildMessage(const QString &verb, const QString &path, const QString &status = QString::null)
|
static QString buildMessage(const QString &verb, const QString &path, const QString &status = QString())
|
||||||
{
|
{
|
||||||
QString msg(verb);
|
QString msg(verb);
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ void SocketApi::slotReadSocket()
|
||||||
QString line = QString::fromUtf8(socket->readLine()).normalized(QString::NormalizationForm_C);
|
QString line = QString::fromUtf8(socket->readLine()).normalized(QString::NormalizationForm_C);
|
||||||
line.chop(1); // remove the '\n'
|
line.chop(1); // remove the '\n'
|
||||||
qCInfo(lcSocketApi) << "Received SocketAPI message <--" << line << "from" << socket;
|
qCInfo(lcSocketApi) << "Received SocketAPI message <--" << line << "from" << socket;
|
||||||
QByteArray command = line.split(":").value(0).toAscii();
|
QByteArray command = line.split(":").value(0).toLatin1();
|
||||||
QByteArray functionWithArguments = "command_" + command + "(QString,SocketListener*)";
|
QByteArray functionWithArguments = "command_" + command + "(QString,SocketListener*)";
|
||||||
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
int indexOfMethod = staticMetaObject.indexOfMethod(functionWithArguments);
|
||||||
|
|
||||||
|
@ -307,7 +307,7 @@ void SocketApi::slotUnregisterPath(const QString &alias)
|
||||||
|
|
||||||
Folder *f = FolderMan::instance()->folder(alias);
|
Folder *f = FolderMan::instance()->folder(alias);
|
||||||
if (f)
|
if (f)
|
||||||
broadcastMessage(buildMessage(QLatin1String("UNREGISTER_PATH"), removeTrailingSlash(f->path()), QString::null), true);
|
broadcastMessage(buildMessage(QLatin1String("UNREGISTER_PATH"), removeTrailingSlash(f->path()), QString()), true);
|
||||||
|
|
||||||
_registeredAliases.remove(alias);
|
_registeredAliases.remove(alias);
|
||||||
}
|
}
|
||||||
|
@ -492,7 +492,7 @@ void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketListener *listen
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetches the private link url asynchronously and then calls the target slot
|
// Fetches the private link url asynchronously and then calls the target slot
|
||||||
void fetchPrivateLinkUrl(const QString &localFile, SocketApi *target, void (SocketApi::*targetFun)(const QString &url) const)
|
static void fetchPrivateLinkUrlHelper(const QString &localFile, SocketApi *target, void (SocketApi::*targetFun)(const QString &url) const)
|
||||||
{
|
{
|
||||||
Folder *shareFolder = FolderMan::instance()->folderForPath(localFile);
|
Folder *shareFolder = FolderMan::instance()->folderForPath(localFile);
|
||||||
if (!shareFolder) {
|
if (!shareFolder) {
|
||||||
|
@ -505,45 +505,23 @@ void fetchPrivateLinkUrl(const QString &localFile, SocketApi *target, void (Sock
|
||||||
|
|
||||||
AccountPtr account = shareFolder->accountState()->account();
|
AccountPtr account = shareFolder->accountState()->account();
|
||||||
|
|
||||||
// Generate private link ourselves: used as a fallback
|
|
||||||
SyncJournalFileRecord rec;
|
SyncJournalFileRecord rec;
|
||||||
if (!shareFolder->journalDb()->getFileRecord(file, &rec) || !rec.isValid())
|
if (!shareFolder->journalDb()->getFileRecord(file, &rec) || !rec.isValid())
|
||||||
return;
|
return;
|
||||||
const QString oldUrl =
|
|
||||||
account->deprecatedPrivateLinkUrl(rec.numericFileId()).toString(QUrl::FullyEncoded);
|
|
||||||
|
|
||||||
// Retrieve the new link or numeric file id by PROPFIND
|
fetchPrivateLinkUrl(account, file, rec.numericFileId(), target, [=](const QString &url) {
|
||||||
PropfindJob *job = new PropfindJob(account, file, target);
|
(target->*targetFun)(url);
|
||||||
job->setProperties(
|
|
||||||
QList<QByteArray>()
|
|
||||||
<< "http://owncloud.org/ns:fileid" // numeric file id for fallback private link generation
|
|
||||||
<< "http://owncloud.org/ns:privatelink");
|
|
||||||
job->setTimeout(10 * 1000);
|
|
||||||
QObject::connect(job, &PropfindJob::result, target, [=](const QVariantMap &result) {
|
|
||||||
auto privateLinkUrl = result["privatelink"].toString();
|
|
||||||
auto numericFileId = result["fileid"].toByteArray();
|
|
||||||
if (!privateLinkUrl.isEmpty()) {
|
|
||||||
(target->*targetFun)(privateLinkUrl);
|
|
||||||
} else if (!numericFileId.isEmpty()) {
|
|
||||||
(target->*targetFun)(account->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded));
|
|
||||||
} else {
|
|
||||||
(target->*targetFun)(oldUrl);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
QObject::connect(job, &PropfindJob::finishedWithError, target, [=](QNetworkReply *) {
|
|
||||||
(target->*targetFun)(oldUrl);
|
|
||||||
});
|
|
||||||
job->start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *)
|
void SocketApi::command_COPY_PRIVATE_LINK(const QString &localFile, SocketListener *)
|
||||||
{
|
{
|
||||||
fetchPrivateLinkUrl(localFile, this, &SocketApi::copyPrivateLinkToClipboard);
|
fetchPrivateLinkUrlHelper(localFile, this, &SocketApi::copyPrivateLinkToClipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *)
|
void SocketApi::command_EMAIL_PRIVATE_LINK(const QString &localFile, SocketListener *)
|
||||||
{
|
{
|
||||||
fetchPrivateLinkUrl(localFile, this, &SocketApi::emailPrivateLink);
|
fetchPrivateLinkUrlHelper(localFile, this, &SocketApi::emailPrivateLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SocketApi::copyPrivateLinkToClipboard(const QString &link) const
|
void SocketApi::copyPrivateLinkToClipboard(const QString &link) const
|
||||||
|
|
|
@ -39,21 +39,6 @@ SslButton::SslButton(QWidget *parent)
|
||||||
this, &SslButton::slotUpdateMenu);
|
this, &SslButton::slotUpdateMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SslButton::protoToString(QSsl::SslProtocol proto)
|
|
||||||
{
|
|
||||||
switch (proto) {
|
|
||||||
break;
|
|
||||||
case QSsl::SslV2:
|
|
||||||
return QLatin1String("SSL v2");
|
|
||||||
case QSsl::SslV3:
|
|
||||||
return QLatin1String("SSL v3");
|
|
||||||
case QSsl::TlsV1:
|
|
||||||
return QLatin1String("TLS");
|
|
||||||
default:
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString addCertDetailsField(const QString &key, const QString &value)
|
static QString addCertDetailsField(const QString &key, const QString &value)
|
||||||
{
|
{
|
||||||
if (value.isEmpty())
|
if (value.isEmpty())
|
||||||
|
@ -92,7 +77,7 @@ QMenu *SslButton::buildCertMenu(QMenu *parent, const QSslCertificate &cert,
|
||||||
QString serial = QString::fromUtf8(cert.serialNumber());
|
QString serial = QString::fromUtf8(cert.serialNumber());
|
||||||
QString effectiveDate = cert.effectiveDate().date().toString();
|
QString effectiveDate = cert.effectiveDate().date().toString();
|
||||||
QString expiryDate = cert.expiryDate().date().toString();
|
QString expiryDate = cert.expiryDate().date().toString();
|
||||||
QString sna = QStringList(cert.alternateSubjectNames().values()).join(" ");
|
QString sna = QStringList(cert.subjectAlternativeNames().values()).join(" ");
|
||||||
|
|
||||||
QString details;
|
QString details;
|
||||||
QTextStream stream(&details);
|
QTextStream stream(&details);
|
||||||
|
@ -144,7 +129,7 @@ QMenu *SslButton::buildCertMenu(QMenu *parent, const QSslCertificate &cert,
|
||||||
|
|
||||||
QString certId = cn.isEmpty() ? ou : cn;
|
QString certId = cn.isEmpty() ? ou : cn;
|
||||||
|
|
||||||
if (QSslSocket::systemCaCertificates().contains(cert)) {
|
if (QSslConfiguration::systemCaCertificates().contains(cert)) {
|
||||||
txt += certId;
|
txt += certId;
|
||||||
} else {
|
} else {
|
||||||
if (isSelfSigned(cert)) {
|
if (isSelfSigned(cert)) {
|
||||||
|
@ -224,16 +209,18 @@ void SslButton::slotUpdateMenu()
|
||||||
|
|
||||||
_menu->addAction(tr("Certificate information:"))->setEnabled(false);
|
_menu->addAction(tr("Certificate information:"))->setEnabled(false);
|
||||||
|
|
||||||
|
const auto systemCerts = QSslConfiguration::systemCaCertificates();
|
||||||
|
|
||||||
QList<QSslCertificate> tmpChain;
|
QList<QSslCertificate> tmpChain;
|
||||||
foreach (QSslCertificate cert, chain) {
|
foreach (QSslCertificate cert, chain) {
|
||||||
tmpChain << cert;
|
tmpChain << cert;
|
||||||
if (QSslSocket::systemCaCertificates().contains(cert))
|
if (systemCerts.contains(cert))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
chain = tmpChain;
|
chain = tmpChain;
|
||||||
|
|
||||||
// find trust anchor (informational only, verification is done by QSslSocket!)
|
// find trust anchor (informational only, verification is done by QSslSocket!)
|
||||||
foreach (QSslCertificate rootCA, QSslSocket::systemCaCertificates()) {
|
for (const QSslCertificate &rootCA : systemCerts) {
|
||||||
if (rootCA.issuerInfo(QSslCertificate::CommonName) == chain.last().issuerInfo(QSslCertificate::CommonName)
|
if (rootCA.issuerInfo(QSslCertificate::CommonName) == chain.last().issuerInfo(QSslCertificate::CommonName)
|
||||||
&& rootCA.issuerInfo(QSslCertificate::Organization) == chain.last().issuerInfo(QSslCertificate::Organization)) {
|
&& rootCA.issuerInfo(QSslCertificate::Organization) == chain.last().issuerInfo(QSslCertificate::Organization)) {
|
||||||
chain.append(rootCA);
|
chain.append(rootCA);
|
||||||
|
|
|
@ -36,7 +36,6 @@ class SslButton : public QToolButton
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit SslButton(QWidget *parent = 0);
|
explicit SslButton(QWidget *parent = 0);
|
||||||
QString protoToString(QSsl::SslProtocol proto);
|
|
||||||
void updateAccountState(AccountState *accountState);
|
void updateAccountState(AccountState *accountState);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
|
@ -136,7 +136,7 @@ void SyncRunFileLog::logItem(const SyncFileItem &item)
|
||||||
if (item._direction == SyncFileItem::None) {
|
if (item._direction == SyncFileItem::None) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
QString ts = QString::fromAscii(item._responseTimeStamp);
|
QString ts = QString::fromLatin1(item._responseTimeStamp);
|
||||||
if (ts.length() > 6) {
|
if (ts.length() > 6) {
|
||||||
QRegExp rx("(\\d\\d:\\d\\d:\\d\\d)");
|
QRegExp rx("(\\d\\d:\\d\\d:\\d\\d)");
|
||||||
if (ts.contains(rx)) {
|
if (ts.contains(rx)) {
|
||||||
|
|
|
@ -41,7 +41,7 @@ Updater *Updater::instance()
|
||||||
|
|
||||||
QUrl Updater::addQueryParams(const QUrl &url)
|
QUrl Updater::addQueryParams(const QUrl &url)
|
||||||
{
|
{
|
||||||
QUrl paramUrl = url;
|
QUrlQuery query;
|
||||||
Theme *theme = Theme::instance();
|
Theme *theme = Theme::instance();
|
||||||
QString platform = QLatin1String("stranger");
|
QString platform = QLatin1String("stranger");
|
||||||
if (Utility::isLinux()) {
|
if (Utility::isLinux()) {
|
||||||
|
@ -56,23 +56,25 @@ QUrl Updater::addQueryParams(const QUrl &url)
|
||||||
|
|
||||||
QString sysInfo = getSystemInfo();
|
QString sysInfo = getSystemInfo();
|
||||||
if (!sysInfo.isEmpty()) {
|
if (!sysInfo.isEmpty()) {
|
||||||
paramUrl.addQueryItem(QLatin1String("client"), sysInfo);
|
query.addQueryItem(QLatin1String("client"), sysInfo);
|
||||||
}
|
}
|
||||||
paramUrl.addQueryItem(QLatin1String("version"), clientVersion());
|
query.addQueryItem(QLatin1String("version"), clientVersion());
|
||||||
paramUrl.addQueryItem(QLatin1String("platform"), platform);
|
query.addQueryItem(QLatin1String("platform"), platform);
|
||||||
paramUrl.addQueryItem(QLatin1String("oem"), theme->appName());
|
query.addQueryItem(QLatin1String("oem"), theme->appName());
|
||||||
|
|
||||||
QString suffix = QString::fromLatin1(MIRALL_STRINGIFY(MIRALL_VERSION_SUFFIX));
|
QString suffix = QString::fromLatin1(MIRALL_STRINGIFY(MIRALL_VERSION_SUFFIX));
|
||||||
paramUrl.addQueryItem(QLatin1String("versionsuffix"), suffix);
|
query.addQueryItem(QLatin1String("versionsuffix"), suffix);
|
||||||
if (suffix.startsWith("nightly")
|
if (suffix.startsWith("nightly")
|
||||||
|| suffix.startsWith("alpha")
|
|| suffix.startsWith("alpha")
|
||||||
|| suffix.startsWith("rc")
|
|| suffix.startsWith("rc")
|
||||||
|| suffix.startsWith("beta")) {
|
|| suffix.startsWith("beta")) {
|
||||||
paramUrl.addQueryItem(QLatin1String("channel"), "beta");
|
query.addQueryItem(QLatin1String("channel"), "beta");
|
||||||
// FIXME: Provide a checkbox in UI to enable regular versions to switch
|
// FIXME: Provide a checkbox in UI to enable regular versions to switch
|
||||||
// to beta channel
|
// to beta channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QUrl paramUrl = url;
|
||||||
|
paramUrl.setQuery(query);
|
||||||
return paramUrl;
|
return paramUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ QString Updater::getSystemInfo()
|
||||||
|
|
||||||
return QString::fromLocal8Bit(output.toBase64());
|
return QString::fromLocal8Bit(output.toBase64());
|
||||||
#else
|
#else
|
||||||
return QString::null;
|
return QString();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -230,7 +230,7 @@ bool OwncloudAdvancedSetupPage::isConfirmBigFolderChecked() const
|
||||||
bool OwncloudAdvancedSetupPage::validatePage()
|
bool OwncloudAdvancedSetupPage::validatePage()
|
||||||
{
|
{
|
||||||
if (!_created) {
|
if (!_created) {
|
||||||
setErrorString(QString::null);
|
setErrorString(QString());
|
||||||
_checking = true;
|
_checking = true;
|
||||||
startSpinner();
|
startSpinner();
|
||||||
emit completeChanged();
|
emit completeChanged();
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="errorLabel">
|
<widget class="QLabel" name="errorLabel">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>An error occured while connecting. Please try again.</string>
|
<string>An error occurred while connecting. Please try again.</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="textFormat">
|
<property name="textFormat">
|
||||||
<enum>Qt::PlainText</enum>
|
<enum>Qt::PlainText</enum>
|
||||||
|
|
|
@ -221,7 +221,7 @@ QString OwncloudSetupPage::url() const
|
||||||
bool OwncloudSetupPage::validatePage()
|
bool OwncloudSetupPage::validatePage()
|
||||||
{
|
{
|
||||||
if (!_authTypeKnown) {
|
if (!_authTypeKnown) {
|
||||||
setErrorString(QString::null, false);
|
setErrorString(QString(), false);
|
||||||
_checking = true;
|
_checking = true;
|
||||||
startSpinner();
|
startSpinner();
|
||||||
emit completeChanged();
|
emit completeChanged();
|
||||||
|
|
|
@ -195,7 +195,7 @@ void OwncloudWizard::slotCurrentPageChanged(int id)
|
||||||
if (id == WizardCommon::Page_Result) {
|
if (id == WizardCommon::Page_Result) {
|
||||||
disconnect(this, &QDialog::finished, this, &OwncloudWizard::basicSetupFinished);
|
disconnect(this, &QDialog::finished, this, &OwncloudWizard::basicSetupFinished);
|
||||||
emit basicSetupFinished(QDialog::Accepted);
|
emit basicSetupFinished(QDialog::Accepted);
|
||||||
appendToConfigurationLog(QString::null);
|
appendToConfigurationLog(QString());
|
||||||
// Immediately close on show, we currently don't want this page anymore
|
// Immediately close on show, we currently don't want this page anymore
|
||||||
done(Accepted);
|
done(Accepted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ bool OwncloudWizardResultPage::isComplete() const
|
||||||
|
|
||||||
void OwncloudWizardResultPage::initializePage()
|
void OwncloudWizardResultPage::initializePage()
|
||||||
{
|
{
|
||||||
_ui.localFolderLabel->setText(QString::null);
|
_ui.localFolderLabel->setText(QString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OwncloudWizardResultPage::setRemoteFolder(const QString &remoteFolder)
|
void OwncloudWizardResultPage::setRemoteFolder(const QString &remoteFolder)
|
||||||
|
@ -81,7 +81,7 @@ void OwncloudWizardResultPage::setRemoteFolder(const QString &remoteFolder)
|
||||||
void OwncloudWizardResultPage::setupCustomization()
|
void OwncloudWizardResultPage::setupCustomization()
|
||||||
{
|
{
|
||||||
// set defaults for the customize labels.
|
// set defaults for the customize labels.
|
||||||
_ui.topLabel->setText(QString::null);
|
_ui.topLabel->setText(QString());
|
||||||
_ui.topLabel->hide();
|
_ui.topLabel->hide();
|
||||||
|
|
||||||
QVariant variant = Theme::instance()->customMedia(Theme::oCSetupResultTop);
|
QVariant variant = Theme::instance()->customMedia(Theme::oCSetupResultTop);
|
||||||
|
|
|
@ -56,7 +56,6 @@ set(libsync_SRCS
|
||||||
syncfilestatustracker.cpp
|
syncfilestatustracker.cpp
|
||||||
syncresult.cpp
|
syncresult.cpp
|
||||||
theme.cpp
|
theme.cpp
|
||||||
excludedfiles.cpp
|
|
||||||
creds/dummycredentials.cpp
|
creds/dummycredentials.cpp
|
||||||
creds/abstractcredentials.cpp
|
creds/abstractcredentials.cpp
|
||||||
creds/credentialscommon.cpp
|
creds/credentialscommon.cpp
|
||||||
|
|
|
@ -40,6 +40,9 @@ namespace OCC {
|
||||||
|
|
||||||
Q_LOGGING_CATEGORY(lcNetworkJob, "sync.networkjob", QtInfoMsg)
|
Q_LOGGING_CATEGORY(lcNetworkJob, "sync.networkjob", QtInfoMsg)
|
||||||
|
|
||||||
|
// If not set, it is overwritten by the Application constructor with the value from the config
|
||||||
|
int AbstractNetworkJob::httpTimeout = qEnvironmentVariableIntValue("OWNCLOUD_TIMEOUT");
|
||||||
|
|
||||||
AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent)
|
AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, _timedout(false)
|
, _timedout(false)
|
||||||
|
@ -51,7 +54,7 @@ AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path,
|
||||||
, _redirectCount(0)
|
, _redirectCount(0)
|
||||||
{
|
{
|
||||||
_timer.setSingleShot(true);
|
_timer.setSingleShot(true);
|
||||||
_timer.setInterval(OwncloudPropagator::httpTimeout() * 1000); // default to 5 minutes.
|
_timer.setInterval((httpTimeout ? httpTimeout : 300) * 1000); // default to 5 minutes.
|
||||||
connect(&_timer, &QTimer::timeout, this, &AbstractNetworkJob::slotTimeout);
|
connect(&_timer, &QTimer::timeout, this, &AbstractNetworkJob::slotTimeout);
|
||||||
|
|
||||||
connect(this, &AbstractNetworkJob::networkActivity, this, &AbstractNetworkJob::resetTimeout);
|
connect(this, &AbstractNetworkJob::networkActivity, this, &AbstractNetworkJob::resetTimeout);
|
||||||
|
@ -326,7 +329,7 @@ QString extractErrorMessage(const QByteArray &errorResponse)
|
||||||
QXmlStreamReader reader(errorResponse);
|
QXmlStreamReader reader(errorResponse);
|
||||||
reader.readNextStartElement();
|
reader.readNextStartElement();
|
||||||
if (reader.name() != "error") {
|
if (reader.name() != "error") {
|
||||||
return QString::null;
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString exception;
|
QString exception;
|
||||||
|
|
|
@ -88,6 +88,10 @@ public:
|
||||||
*/
|
*/
|
||||||
QString errorStringParsingBody(QByteArray *body = 0);
|
QString errorStringParsingBody(QByteArray *body = 0);
|
||||||
|
|
||||||
|
/** static variable the HTTP timeout (in seconds). If set to 0, the default will be used
|
||||||
|
*/
|
||||||
|
static int httpTimeout;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setTimeout(qint64 msec);
|
void setTimeout(qint64 msec);
|
||||||
void resetTimeout();
|
void resetTimeout();
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "cookiejar.h"
|
#include "cookiejar.h"
|
||||||
#include "networkjobs.h"
|
#include "networkjobs.h"
|
||||||
#include "configfile.h"
|
|
||||||
#include "accessmanager.h"
|
#include "accessmanager.h"
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
|
@ -33,6 +32,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QSslKey>
|
#include <QSslKey>
|
||||||
#include <QAuthenticator>
|
#include <QAuthenticator>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -93,6 +93,7 @@ void Account::setDavUser(const QString &newDavUser)
|
||||||
_davUser = newDavUser;
|
_davUser = newDavUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
QImage Account::avatar() const
|
QImage Account::avatar() const
|
||||||
{
|
{
|
||||||
return _avatarImg;
|
return _avatarImg;
|
||||||
|
@ -102,6 +103,7 @@ void Account::setAvatar(const QImage &img)
|
||||||
_avatarImg = img;
|
_avatarImg = img;
|
||||||
emit accountChangedAvatar();
|
emit accountChangedAvatar();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
QString Account::displayName() const
|
QString Account::displayName() const
|
||||||
{
|
{
|
||||||
|
@ -204,8 +206,7 @@ void Account::lendCookieJarTo(QNetworkAccessManager *guest)
|
||||||
|
|
||||||
QString Account::cookieJarPath()
|
QString Account::cookieJarPath()
|
||||||
{
|
{
|
||||||
ConfigFile cfg;
|
return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/cookies" + id() + ".db";
|
||||||
return cfg.configPath() + "/cookies" + id() + ".db";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::resetNetworkAccessManager()
|
void Account::resetNetworkAccessManager()
|
||||||
|
|
|
@ -26,7 +26,10 @@
|
||||||
#include <QSslCipher>
|
#include <QSslCipher>
|
||||||
#include <QSslError>
|
#include <QSslError>
|
||||||
#include <QSharedPointer>
|
#include <QSharedPointer>
|
||||||
|
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
#include <QPixmap>
|
#include <QPixmap>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -86,8 +89,10 @@ public:
|
||||||
QString davDisplayName() const;
|
QString davDisplayName() const;
|
||||||
void setDavDisplayName(const QString &newDisplayName);
|
void setDavDisplayName(const QString &newDisplayName);
|
||||||
|
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
QImage avatar() const;
|
QImage avatar() const;
|
||||||
void setAvatar(const QImage &img);
|
void setAvatar(const QImage &img);
|
||||||
|
#endif
|
||||||
|
|
||||||
/// The name of the account as shown in the toolbar
|
/// The name of the account as shown in the toolbar
|
||||||
QString displayName() const;
|
QString displayName() const;
|
||||||
|
@ -266,7 +271,9 @@ private:
|
||||||
QString _id;
|
QString _id;
|
||||||
QString _davUser;
|
QString _davUser;
|
||||||
QString _displayName;
|
QString _displayName;
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
QImage _avatarImg;
|
QImage _avatarImg;
|
||||||
|
#endif
|
||||||
QMap<QString, QVariant> _settingsMap;
|
QMap<QString, QVariant> _settingsMap;
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
#include "capabilities.h"
|
#include "capabilities.h"
|
||||||
|
|
||||||
#include "configfile.h"
|
|
||||||
|
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
|
@ -21,10 +21,11 @@
|
||||||
|
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
|
|
||||||
|
#include "csync_exclude.h"
|
||||||
|
|
||||||
#ifndef TOKEN_AUTH_ONLY
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QDesktopServices>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QNetworkProxy>
|
#include <QNetworkProxy>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
#define DEFAULT_REMOTE_POLL_INTERVAL 30000 // default remote poll time in milliseconds
|
#define DEFAULT_REMOTE_POLL_INTERVAL 30000 // default remote poll time in milliseconds
|
||||||
#define DEFAULT_FULL_LOCAL_DISCOVERY_INTERVAL (60 * 60 * 1000) // 1 hour
|
#define DEFAULT_FULL_LOCAL_DISCOVERY_INTERVAL (60 * 60 * 1000) // 1 hour
|
||||||
|
@ -82,7 +84,7 @@ static const char maxLogLinesC[] = "Logging/maxLogLines";
|
||||||
|
|
||||||
const char certPath[] = "http_certificatePath";
|
const char certPath[] = "http_certificatePath";
|
||||||
const char certPasswd[] = "http_certificatePasswd";
|
const char certPasswd[] = "http_certificatePasswd";
|
||||||
QString ConfigFile::_confDir = QString::null;
|
QString ConfigFile::_confDir = QString();
|
||||||
bool ConfigFile::_askedUser = false;
|
bool ConfigFile::_askedUser = false;
|
||||||
|
|
||||||
ConfigFile::ConfigFile()
|
ConfigFile::ConfigFile()
|
||||||
|
@ -250,13 +252,11 @@ QVariant ConfigFile::getPolicySetting(const QString &setting, const QVariant &de
|
||||||
|
|
||||||
QString ConfigFile::configPath() const
|
QString ConfigFile::configPath() const
|
||||||
{
|
{
|
||||||
#ifndef TOKEN_AUTH_ONLY
|
|
||||||
if (_confDir.isEmpty()) {
|
if (_confDir.isEmpty()) {
|
||||||
// Qt 5's QStandardPaths::writableLocation gives us wrong results (without /data/),
|
// On Unix, use the AppConfigLocation for the settings, that's configurable with the XDG_CONFIG_HOME env variable.
|
||||||
// so we'll have to use the deprecated version for now
|
// On Windows, use AppDataLocation, that's where the roaming data is and where we should store the config file
|
||||||
_confDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
|
_confDir = QStandardPaths::writableLocation(Utility::isWindows() ? QStandardPaths::AppDataLocation : QStandardPaths::AppConfigLocation);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
QString dir = _confDir;
|
QString dir = _confDir;
|
||||||
|
|
||||||
if (!dir.endsWith(QLatin1Char('/')))
|
if (!dir.endsWith(QLatin1Char('/')))
|
||||||
|
@ -264,12 +264,6 @@ QString ConfigFile::configPath() const
|
||||||
return dir;
|
return dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ConfigFile::configPathWithAppName() const
|
|
||||||
{
|
|
||||||
//HACK
|
|
||||||
return QFileInfo(configFile()).dir().absolutePath().append("/");
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QLatin1String exclFile("sync-exclude.lst");
|
static const QLatin1String exclFile("sync-exclude.lst");
|
||||||
|
|
||||||
QString ConfigFile::excludeFile(Scope scope) const
|
QString ConfigFile::excludeFile(Scope scope) const
|
||||||
|
@ -607,12 +601,12 @@ QString ConfigFile::proxyPassword() const
|
||||||
|
|
||||||
int ConfigFile::useUploadLimit() const
|
int ConfigFile::useUploadLimit() const
|
||||||
{
|
{
|
||||||
return getValue(useUploadLimitC, QString::null, 0).toInt();
|
return getValue(useUploadLimitC, QString(), 0).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ConfigFile::useDownloadLimit() const
|
int ConfigFile::useDownloadLimit() const
|
||||||
{
|
{
|
||||||
return getValue(useDownloadLimitC, QString::null, 0).toInt();
|
return getValue(useDownloadLimitC, QString(), 0).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigFile::setUseUploadLimit(int val)
|
void ConfigFile::setUseUploadLimit(int val)
|
||||||
|
@ -627,12 +621,12 @@ void ConfigFile::setUseDownloadLimit(int val)
|
||||||
|
|
||||||
int ConfigFile::uploadLimit() const
|
int ConfigFile::uploadLimit() const
|
||||||
{
|
{
|
||||||
return getValue(uploadLimitC, QString::null, 10).toInt();
|
return getValue(uploadLimitC, QString(), 10).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
int ConfigFile::downloadLimit() const
|
int ConfigFile::downloadLimit() const
|
||||||
{
|
{
|
||||||
return getValue(downloadLimitC, QString::null, 80).toInt();
|
return getValue(downloadLimitC, QString(), 80).toInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigFile::setUploadLimit(int kbytes)
|
void ConfigFile::setUploadLimit(int kbytes)
|
||||||
|
@ -748,4 +742,17 @@ std::unique_ptr<QSettings> ConfigFile::settingsWithGroup(const QString &group, Q
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigFile::setupDefaultExcludeFilePaths(ExcludedFiles &excludedFiles)
|
||||||
|
{
|
||||||
|
ConfigFile cfg;
|
||||||
|
QString systemList = cfg.excludeFile(ConfigFile::SystemScope);
|
||||||
|
qCInfo(lcConfigFile) << "Adding system ignore list to csync:" << systemList;
|
||||||
|
excludedFiles.addExcludeFilePath(systemList);
|
||||||
|
|
||||||
|
QString userList = cfg.excludeFile(ConfigFile::UserScope);
|
||||||
|
if (QFile::exists(userList)) {
|
||||||
|
qCInfo(lcConfigFile) << "Adding user defined ignore list to csync:" << userList;
|
||||||
|
excludedFiles.addExcludeFilePath(userList);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
|
|
||||||
class QWidget;
|
class QWidget;
|
||||||
class QHeaderView;
|
class QHeaderView;
|
||||||
|
class ExcludedFiles;
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -42,7 +43,6 @@ public:
|
||||||
SystemScope };
|
SystemScope };
|
||||||
|
|
||||||
QString configPath() const;
|
QString configPath() const;
|
||||||
QString configPathWithAppName() const;
|
|
||||||
QString configFile() const;
|
QString configFile() const;
|
||||||
QString excludeFile(Scope scope) const;
|
QString excludeFile(Scope scope) const;
|
||||||
static QString excludeFileFromSystem(); // doesn't access config dir
|
static QString excludeFileFromSystem(); // doesn't access config dir
|
||||||
|
@ -55,7 +55,7 @@ public:
|
||||||
QByteArray caCerts();
|
QByteArray caCerts();
|
||||||
void setCaCerts(const QByteArray &);
|
void setCaCerts(const QByteArray &);
|
||||||
|
|
||||||
bool passwordStorageAllowed(const QString &connection = QString::null);
|
bool passwordStorageAllowed(const QString &connection = QString());
|
||||||
|
|
||||||
// max count of lines in the log window
|
// max count of lines in the log window
|
||||||
int maxLogLines() const;
|
int maxLogLines() const;
|
||||||
|
@ -153,6 +153,9 @@ public:
|
||||||
with the given parent. If no parent is specified, the caller must destroy the settings */
|
with the given parent. If no parent is specified, the caller must destroy the settings */
|
||||||
static std::unique_ptr<QSettings> settingsWithGroup(const QString &group, QObject *parent = 0);
|
static std::unique_ptr<QSettings> settingsWithGroup(const QString &group, QObject *parent = 0);
|
||||||
|
|
||||||
|
/// Add the system and user exclude file path to the ExcludedFiles instance.
|
||||||
|
static void setupDefaultExcludeFilePaths(ExcludedFiles &excludedFiles);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QVariant getPolicySetting(const QString &policy, const QVariant &defaultValue = QVariant()) const;
|
QVariant getPolicySetting(const QString &policy, const QVariant &defaultValue = QVariant()) const;
|
||||||
void storeData(const QString &group, const QString &key, const QVariant &value);
|
void storeData(const QString &group, const QString &key, const QVariant &value);
|
||||||
|
@ -161,7 +164,7 @@ protected:
|
||||||
bool dataExists(const QString &group, const QString &key) const;
|
bool dataExists(const QString &group, const QString &key) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVariant getValue(const QString ¶m, const QString &group = QString::null,
|
QVariant getValue(const QString ¶m, const QString &group = QString(),
|
||||||
const QVariant &defaultValue = QVariant()) const;
|
const QVariant &defaultValue = QVariant()) const;
|
||||||
void setValue(const QString &key, const QVariant &value);
|
void setValue(const QString &key, const QVariant &value);
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkProxyFactory>
|
#include <QNetworkProxyFactory>
|
||||||
#include <QPixmap>
|
|
||||||
#include <QXmlStreamReader>
|
#include <QXmlStreamReader>
|
||||||
|
|
||||||
#include "connectionvalidator.h"
|
#include "connectionvalidator.h"
|
||||||
|
@ -333,24 +332,28 @@ void ConnectionValidator::slotUserFetched(const QJsonDocument &json)
|
||||||
QString user = json.object().value("ocs").toObject().value("data").toObject().value("id").toString();
|
QString user = json.object().value("ocs").toObject().value("data").toObject().value("id").toString();
|
||||||
if (!user.isEmpty()) {
|
if (!user.isEmpty()) {
|
||||||
_account->setDavUser(user);
|
_account->setDavUser(user);
|
||||||
|
|
||||||
AvatarJob *job = new AvatarJob(_account, this);
|
|
||||||
job->setTimeout(20 * 1000);
|
|
||||||
QObject::connect(job, &AvatarJob::avatarPixmap, this, &ConnectionValidator::slotAvatarImage);
|
|
||||||
|
|
||||||
job->start();
|
|
||||||
}
|
}
|
||||||
QString displayName = json.object().value("ocs").toObject().value("data").toObject().value("display-name").toString();
|
QString displayName = json.object().value("ocs").toObject().value("data").toObject().value("display-name").toString();
|
||||||
if (!displayName.isEmpty()) {
|
if (!displayName.isEmpty()) {
|
||||||
_account->setDavDisplayName(displayName);
|
_account->setDavDisplayName(displayName);
|
||||||
}
|
}
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
|
AvatarJob *job = new AvatarJob(_account, _account->davUser(), 128, this);
|
||||||
|
job->setTimeout(20 * 1000);
|
||||||
|
QObject::connect(job, &AvatarJob::avatarPixmap, this, &ConnectionValidator::slotAvatarImage);
|
||||||
|
job->start();
|
||||||
|
#else
|
||||||
|
reportResult(Connected);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
void ConnectionValidator::slotAvatarImage(const QImage &img)
|
void ConnectionValidator::slotAvatarImage(const QImage &img)
|
||||||
{
|
{
|
||||||
_account->setAvatar(img);
|
_account->setAvatar(img);
|
||||||
reportResult(Connected);
|
reportResult(Connected);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ConnectionValidator::reportResult(Status status)
|
void ConnectionValidator::reportResult(Status status)
|
||||||
{
|
{
|
||||||
|
|
|
@ -123,7 +123,9 @@ protected slots:
|
||||||
|
|
||||||
void slotCapabilitiesRecieved(const QJsonDocument &);
|
void slotCapabilitiesRecieved(const QJsonDocument &);
|
||||||
void slotUserFetched(const QJsonDocument &);
|
void slotUserFetched(const QJsonDocument &);
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
void slotAvatarImage(const QImage &img);
|
void slotAvatarImage(const QImage &img);
|
||||||
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reportResult(Status status);
|
void reportResult(Status status);
|
||||||
|
|
|
@ -40,11 +40,11 @@ QString AbstractCredentials::keychainKey(const QString &url, const QString &user
|
||||||
QString u(url);
|
QString u(url);
|
||||||
if (u.isEmpty()) {
|
if (u.isEmpty()) {
|
||||||
qCWarning(lcCredentials) << "Empty url in keyChain, error!";
|
qCWarning(lcCredentials) << "Empty url in keyChain, error!";
|
||||||
return QString::null;
|
return QString();
|
||||||
}
|
}
|
||||||
if (user.isEmpty()) {
|
if (user.isEmpty()) {
|
||||||
qCWarning(lcCredentials) << "Error: User is empty!";
|
qCWarning(lcCredentials) << "Error: User is empty!";
|
||||||
return QString::null;
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!u.endsWith(QChar('/'))) {
|
if (!u.endsWith(QChar('/'))) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ QString TokenCredentials::password() const
|
||||||
return _password;
|
return _password;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkAccessManager *TokenCredentials::getQNAM() const
|
QNetworkAccessManager *TokenCredentials::createQNAM() const
|
||||||
{
|
{
|
||||||
AccessManager *qnam = new TokenCredentialsAccessManager(this);
|
AccessManager *qnam = new TokenCredentialsAccessManager(this);
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ public:
|
||||||
TokenCredentials(const QString &user, const QString &password, const QString &token);
|
TokenCredentials(const QString &user, const QString &password, const QString &token);
|
||||||
|
|
||||||
QString authType() const Q_DECL_OVERRIDE;
|
QString authType() const Q_DECL_OVERRIDE;
|
||||||
QNetworkAccessManager *getQNAM() const Q_DECL_OVERRIDE;
|
QNetworkAccessManager *createQNAM() const Q_DECL_OVERRIDE;
|
||||||
bool ready() const Q_DECL_OVERRIDE;
|
bool ready() const Q_DECL_OVERRIDE;
|
||||||
void askFromUser() Q_DECL_OVERRIDE;
|
void askFromUser() Q_DECL_OVERRIDE;
|
||||||
void fetchFromKeychain() Q_DECL_OVERRIDE;
|
void fetchFromKeychain() Q_DECL_OVERRIDE;
|
||||||
|
|
|
@ -75,7 +75,7 @@ bool DiscoveryJob::isInSelectiveSyncBlackList(const QByteArray &path) const
|
||||||
|
|
||||||
// Also try to adjust the path if there was renames
|
// Also try to adjust the path if there was renames
|
||||||
if (csync_rename_count(_csync_ctx)) {
|
if (csync_rename_count(_csync_ctx)) {
|
||||||
QByteArray adjusted = csync_rename_adjust_path_source(_csync_ctx, path);
|
QByteArray adjusted = csync_rename_adjust_parent_path_source(_csync_ctx, path);
|
||||||
if (adjusted != path) {
|
if (adjusted != path) {
|
||||||
return findPathInList(_selectiveSyncBlackList, QString::fromUtf8(adjusted));
|
return findPathInList(_selectiveSyncBlackList, QString::fromUtf8(adjusted));
|
||||||
}
|
}
|
||||||
|
@ -698,8 +698,6 @@ void DiscoveryJob::start()
|
||||||
_csync_ctx->callbacks.remote_closedir_hook = remote_vio_closedir_hook;
|
_csync_ctx->callbacks.remote_closedir_hook = remote_vio_closedir_hook;
|
||||||
_csync_ctx->callbacks.vio_userdata = this;
|
_csync_ctx->callbacks.vio_userdata = this;
|
||||||
|
|
||||||
csync_exclude_traversal_prepare(_csync_ctx); // Converts the flat exclude list to optimized regexps
|
|
||||||
|
|
||||||
csync_set_log_callback(_log_callback);
|
csync_set_log_callback(_log_callback);
|
||||||
csync_set_log_level(_log_level);
|
csync_set_log_level(_log_level);
|
||||||
_lastUpdateProgressCallbackCall.invalidate();
|
_lastUpdateProgressCallbackCall.invalidate();
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <QWaitCondition>
|
#include <QWaitCondition>
|
||||||
#include <QLinkedList>
|
#include <QLinkedList>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include "syncoptions.h"
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -35,52 +36,6 @@ class Account;
|
||||||
* if the files are new, or changed.
|
* if the files are new, or changed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct SyncOptions
|
|
||||||
{
|
|
||||||
SyncOptions()
|
|
||||||
: _newBigFolderSizeLimit(-1)
|
|
||||||
, _confirmExternalStorage(false)
|
|
||||||
, _initialChunkSize(10 * 1000 * 1000) // 10 MB
|
|
||||||
, _minChunkSize(1 * 1000 * 1000) // 1 MB
|
|
||||||
, _maxChunkSize(100 * 1000 * 1000) // 100 MB
|
|
||||||
, _targetChunkUploadDuration(60 * 1000) // 1 minute
|
|
||||||
, _parallelNetworkJobs(true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Maximum size (in Bytes) a folder can have without asking for confirmation.
|
|
||||||
* -1 means infinite */
|
|
||||||
qint64 _newBigFolderSizeLimit;
|
|
||||||
|
|
||||||
/** If a confirmation should be asked for external storages */
|
|
||||||
bool _confirmExternalStorage;
|
|
||||||
|
|
||||||
/** The initial un-adjusted chunk size in bytes for chunked uploads, both
|
|
||||||
* for old and new chunking algorithm, which classifies the item to be chunked
|
|
||||||
*
|
|
||||||
* In chunkingNG, when dynamic chunk size adjustments are done, this is the
|
|
||||||
* starting value and is then gradually adjusted within the
|
|
||||||
* minChunkSize / maxChunkSize bounds.
|
|
||||||
*/
|
|
||||||
quint64 _initialChunkSize;
|
|
||||||
|
|
||||||
/** The minimum chunk size in bytes for chunked uploads */
|
|
||||||
quint64 _minChunkSize;
|
|
||||||
|
|
||||||
/** The maximum chunk size in bytes for chunked uploads */
|
|
||||||
quint64 _maxChunkSize;
|
|
||||||
|
|
||||||
/** The target duration of chunk uploads for dynamic chunk sizing.
|
|
||||||
*
|
|
||||||
* Set to 0 it will disable dynamic chunk sizing.
|
|
||||||
*/
|
|
||||||
quint64 _targetChunkUploadDuration;
|
|
||||||
|
|
||||||
/** Whether parallel network jobs are allowed. */
|
|
||||||
bool _parallelNetworkJobs;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct DiscoveryDirectoryResult
|
struct DiscoveryDirectoryResult
|
||||||
{
|
{
|
||||||
QString path;
|
QString path;
|
||||||
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) by Christian Kamm <mail@ckamm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "excludedfiles.h"
|
|
||||||
#include "common/utility.h"
|
|
||||||
|
|
||||||
#include <QFileInfo>
|
|
||||||
|
|
||||||
#include "std/c_string.h"
|
|
||||||
#include "csync.h"
|
|
||||||
#include "csync_exclude.h"
|
|
||||||
|
|
||||||
using namespace OCC;
|
|
||||||
|
|
||||||
ExcludedFiles::ExcludedFiles(c_strlist_t **excludesPtr)
|
|
||||||
: _excludesPtr(excludesPtr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ExcludedFiles::~ExcludedFiles()
|
|
||||||
{
|
|
||||||
c_strlist_destroy(*_excludesPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExcludedFiles &ExcludedFiles::instance()
|
|
||||||
{
|
|
||||||
static c_strlist_t *globalExcludes;
|
|
||||||
static ExcludedFiles inst(&globalExcludes);
|
|
||||||
return inst;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExcludedFiles::addExcludeFilePath(const QString &path)
|
|
||||||
{
|
|
||||||
_excludeFiles.insert(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef WITH_TESTING
|
|
||||||
void ExcludedFiles::addExcludeExpr(const QString &expr)
|
|
||||||
{
|
|
||||||
_csync_exclude_add(_excludesPtr, expr.toLatin1().constData());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool ExcludedFiles::reloadExcludes()
|
|
||||||
{
|
|
||||||
c_strlist_destroy(*_excludesPtr);
|
|
||||||
*_excludesPtr = NULL;
|
|
||||||
|
|
||||||
bool success = true;
|
|
||||||
foreach (const QString &file, _excludeFiles) {
|
|
||||||
if (csync_exclude_load(file.toUtf8(), _excludesPtr) < 0)
|
|
||||||
success = false;
|
|
||||||
}
|
|
||||||
// The csync_exclude_traversal_prepare is called implicitely at sync start.
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ExcludedFiles::isExcluded(
|
|
||||||
const QString &filePath,
|
|
||||||
const QString &basePath,
|
|
||||||
bool excludeHidden) const
|
|
||||||
{
|
|
||||||
if (!filePath.startsWith(basePath, Utility::fsCasePreserving() ? Qt::CaseInsensitive : Qt::CaseSensitive)) {
|
|
||||||
// Mark paths we're not responsible for as excluded...
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludeHidden) {
|
|
||||||
QString path = filePath;
|
|
||||||
// Check all path subcomponents, but to *not* check the base path:
|
|
||||||
// We do want to be able to sync with a hidden folder as the target.
|
|
||||||
while (path.size() > basePath.size()) {
|
|
||||||
QFileInfo fi(path);
|
|
||||||
if (fi.isHidden() || fi.fileName().startsWith(QLatin1Char('.'))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the parent path
|
|
||||||
path = fi.absolutePath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QFileInfo fi(filePath);
|
|
||||||
csync_ftw_type_e type = CSYNC_FTW_TYPE_FILE;
|
|
||||||
if (fi.isDir()) {
|
|
||||||
type = CSYNC_FTW_TYPE_DIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString relativePath = filePath.mid(basePath.size());
|
|
||||||
if (relativePath.endsWith(QLatin1Char('/'))) {
|
|
||||||
relativePath.chop(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return csync_excluded_no_ctx(*_excludesPtr, relativePath.toUtf8(), type) != CSYNC_NOT_EXCLUDED;
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) by Christian Kamm <mail@ckamm.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "owncloudlib.h"
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
#include "std/c_string.h"
|
|
||||||
#include "csync.h"
|
|
||||||
#include "csync_exclude.h" // for CSYNC_EXCLUDE_TYPE
|
|
||||||
|
|
||||||
namespace OCC {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages the global system and user exclude lists.
|
|
||||||
*/
|
|
||||||
class OWNCLOUDSYNC_EXPORT ExcludedFiles : public QObject
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
static ExcludedFiles &instance();
|
|
||||||
|
|
||||||
ExcludedFiles(c_strlist_t **excludesPtr);
|
|
||||||
~ExcludedFiles();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new path to a file containing exclude patterns.
|
|
||||||
*
|
|
||||||
* Does not load the file. Use reloadExcludes() afterwards.
|
|
||||||
*/
|
|
||||||
void addExcludeFilePath(const QString &path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks whether a file or directory should be excluded.
|
|
||||||
*
|
|
||||||
* @param filePath the absolute path to the file
|
|
||||||
* @param basePath folder path from which to apply exclude rules
|
|
||||||
*/
|
|
||||||
bool isExcluded(
|
|
||||||
const QString &filePath,
|
|
||||||
const QString &basePath,
|
|
||||||
bool excludeHidden) const;
|
|
||||||
|
|
||||||
#ifdef WITH_TESTING
|
|
||||||
void addExcludeExpr(const QString &expr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
/**
|
|
||||||
* Reloads the exclude patterns from the registered paths.
|
|
||||||
*/
|
|
||||||
bool reloadExcludes();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// This is a pointer to the csync exclude list, its is owned by this class
|
|
||||||
// but the pointer can be in a csync_context so that it can itself also query the list.
|
|
||||||
c_strlist_t **_excludesPtr;
|
|
||||||
QSet<QString> _excludeFiles;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace OCC
|
|
|
@ -27,9 +27,9 @@
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QPixmap>
|
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
#include "networkjobs.h"
|
#include "networkjobs.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
|
@ -626,10 +626,11 @@ bool PropfindJob::finished()
|
||||||
|
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
|
||||||
AvatarJob::AvatarJob(AccountPtr account, QObject *parent)
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
|
AvatarJob::AvatarJob(AccountPtr account, const QString &userId, int size, QObject *parent)
|
||||||
: AbstractNetworkJob(account, QString(), parent)
|
: AbstractNetworkJob(account, QString(), parent)
|
||||||
{
|
{
|
||||||
_avatarUrl = Utility::concatUrlPath(account->url(), QString("remote.php/dav/avatars/%1/128.png").arg(account->davUser()));
|
_avatarUrl = Utility::concatUrlPath(account->url(), QString("remote.php/dav/avatars/%1/%2.png").arg(userId, QString::number(size)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarJob::start()
|
void AvatarJob::start()
|
||||||
|
@ -639,6 +640,26 @@ void AvatarJob::start()
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage AvatarJob::makeCircularAvatar(const QImage &baseAvatar)
|
||||||
|
{
|
||||||
|
int dim = baseAvatar.width();
|
||||||
|
|
||||||
|
QImage avatar(dim, dim, baseAvatar.format());
|
||||||
|
avatar.fill(Qt::transparent);
|
||||||
|
|
||||||
|
QPainter painter(&avatar);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
|
||||||
|
QPainterPath path;
|
||||||
|
path.addEllipse(0, 0, dim, dim);
|
||||||
|
painter.setClipPath(path);
|
||||||
|
|
||||||
|
painter.drawImage(0, 0, baseAvatar);
|
||||||
|
painter.end();
|
||||||
|
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
bool AvatarJob::finished()
|
bool AvatarJob::finished()
|
||||||
{
|
{
|
||||||
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
@ -656,6 +677,7 @@ bool AvatarJob::finished()
|
||||||
emit(avatarPixmap(avImage));
|
emit(avatarPixmap(avImage));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
|
||||||
|
@ -758,7 +780,7 @@ JsonApiJob::JsonApiJob(const AccountPtr &account, const QString &path, QObject *
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void JsonApiJob::addQueryParams(QList<QPair<QString, QString>> params)
|
void JsonApiJob::addQueryParams(const QUrlQuery ¶ms)
|
||||||
{
|
{
|
||||||
_additionalParams = params;
|
_additionalParams = params;
|
||||||
}
|
}
|
||||||
|
@ -767,10 +789,9 @@ void JsonApiJob::start()
|
||||||
{
|
{
|
||||||
QNetworkRequest req;
|
QNetworkRequest req;
|
||||||
req.setRawHeader("OCS-APIREQUEST", "true");
|
req.setRawHeader("OCS-APIREQUEST", "true");
|
||||||
QUrl url = Utility::concatUrlPath(account()->url(), path());
|
auto query = _additionalParams;
|
||||||
QList<QPair<QString, QString>> params = _additionalParams;
|
query.addQueryItem(QLatin1String("format"), QLatin1String("json"));
|
||||||
params << qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"));
|
QUrl url = Utility::concatUrlPath(account()->url(), path(), query);
|
||||||
url.setQueryItems(params);
|
|
||||||
sendRequest("GET", url, req);
|
sendRequest("GET", url, req);
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
@ -903,4 +924,36 @@ bool SimpleNetworkJob::finished()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fetchPrivateLinkUrl(AccountPtr account, const QString &remotePath,
|
||||||
|
const QByteArray &numericFileId, QObject *target,
|
||||||
|
std::function<void(const QString &url)> targetFun)
|
||||||
|
{
|
||||||
|
QString oldUrl;
|
||||||
|
if (!numericFileId.isEmpty())
|
||||||
|
oldUrl = account->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded);
|
||||||
|
|
||||||
|
// Retrieve the new link by PROPFIND
|
||||||
|
PropfindJob *job = new PropfindJob(account, remotePath, target);
|
||||||
|
job->setProperties(
|
||||||
|
QList<QByteArray>()
|
||||||
|
<< "http://owncloud.org/ns:fileid" // numeric file id for fallback private link generation
|
||||||
|
<< "http://owncloud.org/ns:privatelink");
|
||||||
|
job->setTimeout(10 * 1000);
|
||||||
|
QObject::connect(job, &PropfindJob::result, target, [=](const QVariantMap &result) {
|
||||||
|
auto privateLinkUrl = result["privatelink"].toString();
|
||||||
|
auto numericFileId = result["fileid"].toByteArray();
|
||||||
|
if (!privateLinkUrl.isEmpty()) {
|
||||||
|
targetFun(privateLinkUrl);
|
||||||
|
} else if (!numericFileId.isEmpty()) {
|
||||||
|
targetFun(account->deprecatedPrivateLinkUrl(numericFileId).toString(QUrl::FullyEncoded));
|
||||||
|
} else {
|
||||||
|
targetFun(oldUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
QObject::connect(job, &PropfindJob::finishedWithError, target, [=](QNetworkReply *) {
|
||||||
|
targetFun(oldUrl);
|
||||||
|
});
|
||||||
|
job->start();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
#define NETWORKJOBS_H
|
#define NETWORKJOBS_H
|
||||||
|
|
||||||
#include "abstractnetworkjob.h"
|
#include "abstractnetworkjob.h"
|
||||||
|
#include <QUrlQuery>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
class QUrl;
|
class QUrl;
|
||||||
class QJsonObject;
|
class QJsonObject;
|
||||||
|
@ -133,11 +135,9 @@ private:
|
||||||
QList<QByteArray> _properties;
|
QList<QByteArray> _properties;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
/**
|
/**
|
||||||
* @brief The AvatarJob class
|
* @brief Retrieves the account users avatar from the server using a GET request.
|
||||||
*
|
|
||||||
* Retrieves the account users avatar from the server using a GET request.
|
|
||||||
*
|
*
|
||||||
* If the server does not have the avatar, the result Pixmap is empty.
|
* If the server does not have the avatar, the result Pixmap is empty.
|
||||||
*
|
*
|
||||||
|
@ -147,15 +147,23 @@ class OWNCLOUDSYNC_EXPORT AvatarJob : public AbstractNetworkJob
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit AvatarJob(AccountPtr account, QObject *parent = 0);
|
/**
|
||||||
|
* @param userId The user for which to obtain the avatar
|
||||||
|
* @param size The size of the avatar (square so size*size)
|
||||||
|
*/
|
||||||
|
explicit AvatarJob(AccountPtr account, const QString &userId, int size, QObject *parent = 0);
|
||||||
|
|
||||||
void start() Q_DECL_OVERRIDE;
|
void start() Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
/** The retrieved avatar images don't have the circle shape by default */
|
||||||
|
static QImage makeCircularAvatar(const QImage &baseAvatar);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
/**
|
||||||
* @brief avatarPixmap - returns either a valid pixmap or not.
|
* @brief avatarPixmap - returns either a valid pixmap or not.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void avatarPixmap(QImage);
|
void avatarPixmap(const QImage &);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
virtual bool finished() Q_DECL_OVERRIDE;
|
virtual bool finished() Q_DECL_OVERRIDE;
|
||||||
|
@ -163,6 +171,7 @@ private slots:
|
||||||
private:
|
private:
|
||||||
QUrl _avatarUrl;
|
QUrl _avatarUrl;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send a Proppatch request
|
* @brief Send a Proppatch request
|
||||||
|
@ -331,7 +340,7 @@ public:
|
||||||
*
|
*
|
||||||
* This function needs to be called before start() obviously.
|
* This function needs to be called before start() obviously.
|
||||||
*/
|
*/
|
||||||
void addQueryParams(QList<QPair<QString, QString>> params);
|
void addQueryParams(const QUrlQuery ¶ms);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void start() Q_DECL_OVERRIDE;
|
void start() Q_DECL_OVERRIDE;
|
||||||
|
@ -348,7 +357,7 @@ signals:
|
||||||
void jsonReceived(const QJsonDocument &json, int statusCode);
|
void jsonReceived(const QJsonDocument &json, int statusCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QList<QPair<QString, QString>> _additionalParams;
|
QUrlQuery _additionalParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -402,6 +411,23 @@ private slots:
|
||||||
bool finished() Q_DECL_OVERRIDE;
|
bool finished() Q_DECL_OVERRIDE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Runs a PROPFIND to figure out the private link url
|
||||||
|
*
|
||||||
|
* The numericFileId is used only to build the deprecatedPrivateLinkUrl
|
||||||
|
* locally as a fallback. If it's empty and the PROPFIND fails, targetFun
|
||||||
|
* will be called with an empty string.
|
||||||
|
*
|
||||||
|
* The job and signal connections are parented to the target QObject.
|
||||||
|
*
|
||||||
|
* Note: targetFun is guaranteed to be called only through the event
|
||||||
|
* loop and never directly.
|
||||||
|
*/
|
||||||
|
void OWNCLOUDSYNC_EXPORT fetchPrivateLinkUrl(
|
||||||
|
AccountPtr account, const QString &remotePath,
|
||||||
|
const QByteArray &numericFileId, QObject *target,
|
||||||
|
std::function<void(const QString &url)> targetFun);
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|
||||||
#endif // NETWORKJOBS_H
|
#endif // NETWORKJOBS_H
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#include "propagateremotemove.h"
|
#include "propagateremotemove.h"
|
||||||
#include "propagateremotemkdir.h"
|
#include "propagateremotemkdir.h"
|
||||||
#include "propagatorjobs.h"
|
#include "propagatorjobs.h"
|
||||||
#include "configfile.h"
|
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
#include "account.h"
|
#include "account.h"
|
||||||
#include "common/asserts.h"
|
#include "common/asserts.h"
|
||||||
|
@ -554,19 +553,6 @@ bool OwncloudPropagator::isInSharedDirectory(const QString &file)
|
||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OwncloudPropagator::httpTimeout()
|
|
||||||
{
|
|
||||||
static int timeout = 0;
|
|
||||||
if (!timeout) {
|
|
||||||
timeout = qgetenv("OWNCLOUD_TIMEOUT").toUInt();
|
|
||||||
if (timeout == 0) {
|
|
||||||
ConfigFile cfg;
|
|
||||||
timeout = cfg.timeout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OwncloudPropagator::localFileNameClash(const QString &relFile)
|
bool OwncloudPropagator::localFileNameClash(const QString &relFile)
|
||||||
{
|
{
|
||||||
bool re = false;
|
bool re = false;
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
#include "common/syncjournaldb.h"
|
#include "common/syncjournaldb.h"
|
||||||
#include "bandwidthmanager.h"
|
#include "bandwidthmanager.h"
|
||||||
#include "accountfwd.h"
|
#include "accountfwd.h"
|
||||||
#include "discoveryphase.h"
|
#include "syncoptions.h"
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
@ -464,9 +464,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// timeout in seconds
|
|
||||||
static int httpTimeout();
|
|
||||||
|
|
||||||
AccountPtr account() const;
|
AccountPtr account() const;
|
||||||
|
|
||||||
enum DiskSpaceResult {
|
enum DiskSpaceResult {
|
||||||
|
|
|
@ -89,7 +89,9 @@ SyncEngine::SyncEngine(AccountPtr account, const QString &localPath,
|
||||||
|
|
||||||
_csync_ctx.reset(new CSYNC(localPath.toUtf8().data(), journal));
|
_csync_ctx.reset(new CSYNC(localPath.toUtf8().data(), journal));
|
||||||
|
|
||||||
_excludedFiles.reset(new ExcludedFiles(&_csync_ctx->excludes));
|
_excludedFiles.reset(new ExcludedFiles);
|
||||||
|
_csync_ctx->exclude_traversal_fn = _excludedFiles->csyncTraversalMatchFun();
|
||||||
|
|
||||||
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));
|
_syncFileStatusTracker.reset(new SyncFileStatusTracker(this));
|
||||||
|
|
||||||
_clearTouchedFilesTimer.setSingleShot(true);
|
_clearTouchedFilesTimer.setSingleShot(true);
|
||||||
|
@ -476,6 +478,9 @@ int SyncEngine::treewalkFile(csync_file_stat_t *file, csync_file_stat_t *other,
|
||||||
case CSYNC_STATUS_INDIVIDUAL_TOO_DEEP:
|
case CSYNC_STATUS_INDIVIDUAL_TOO_DEEP:
|
||||||
item->_errorString = tr("Folder hierarchy is too deep");
|
item->_errorString = tr("Folder hierarchy is too deep");
|
||||||
break;
|
break;
|
||||||
|
case CSYNC_STATUS_INDIVIDUAL_CANNOT_ENCODE:
|
||||||
|
item->_errorString = tr("The filename cannot be encoded on your file system.");
|
||||||
|
break;
|
||||||
case CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE:
|
case CSYNC_STATUS_INDIVIDUAL_IS_CONFLICT_FILE:
|
||||||
item->_status = SyncFileItem::Conflict;
|
item->_status = SyncFileItem::Conflict;
|
||||||
if (Utility::shouldUploadConflictFiles()) {
|
if (Utility::shouldUploadConflictFiles()) {
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
// when do we go away with this private/public separation?
|
// when do we go away with this private/public separation?
|
||||||
#include <csync_private.h>
|
#include <csync_private.h>
|
||||||
|
|
||||||
#include "excludedfiles.h"
|
|
||||||
#include "syncfileitem.h"
|
#include "syncfileitem.h"
|
||||||
#include "progressdispatcher.h"
|
#include "progressdispatcher.h"
|
||||||
#include "common/utility.h"
|
#include "common/utility.h"
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue