Comparar commits

...

92 Commits

Autor SHA1 Mensagem Data
Olivier Goffart 172689d35c Merge remote-tracking branch 'origin/2.3' 2017-02-14 15:08:10 +01:00
Jocelyn Turcotte 60a4a742a3 Disable tests uncompatible with Qt4 for that build 2017-02-14 14:32:41 +01:00
Jocelyn Turcotte b26db062d2 Fix the UNIT_TESTING build on Windows
- Put all tests in the bin directory so that DLLs can be loaded
- Add missing exports
- Skip tests that use code depending on zlib
- The "GMT" timezone is named differently, use the int constructor instead

5 tests are still failing, it's not really worth fixing at the moment
since no developper is currently using Windows as its main platform.
2017-02-14 14:32:41 +01:00
Jocelyn Turcotte 611f633ba8 Fix FolderManTest on macOS
On macOS /var is a symlink to /private/var and we have to make sure that we
use the canonical path before and after it enters the code to make sure we
compare paths correctly.
2017-02-14 14:32:41 +01:00
Jocelyn Turcotte 3265948458 Fix ExcludedFilesTest on macOS
Use the same logic to find sync-exclude as check_csync_exclude.c
2017-02-14 14:32:41 +01:00
Jocelyn Turcotte 0e3e9d5991 Fix FileSystemTest on macOS 2017-02-14 14:32:41 +01:00
Jocelyn Turcotte 0be6cd3bf7 Fix FolderWatcherTest on macOS and Windows
- We need to use a QGuiApplication on macOS or else we don't get notifications
- Switch to use QSignalSpy rather than lists and sleeps
- Use system() for all modifications since we pass kFSEventStreamCreateFlagIgnoreSelf
- Keep using the local process on Windows since it catches its own events
2017-02-14 14:32:41 +01:00
Jocelyn Turcotte 867b78052d Fix UtilityTest on macOS 2017-02-14 14:32:41 +01:00
Olivier Goffart 058f7df635 Folder: remove unused variable 2017-02-14 10:10:07 +01:00
Jenkins for ownCloud c1552d7984 [tx-robot] updated from transifex 2017-02-14 02:18:27 +01:00
Markus Goetz 575b981542 Modify Jenkinsfile for 2.3 2017-02-13 14:47:51 +01:00
Jenkins for ownCloud 0d0af95956 [tx-robot] updated from transifex 2017-02-13 02:18:28 +01:00
Jenkins for ownCloud 6c5d873647 [tx-robot] updated from transifex 2017-02-12 02:18:28 +01:00
Jenkins for ownCloud 71187e1744 [tx-robot] updated from transifex 2017-02-11 02:18:34 +01:00
Christian Kamm 006ce854a6 Doc: For conflicts, the local file is renamed #5521 2017-02-10 12:50:35 +01:00
Jenkins for ownCloud 697178bab6 [tx-robot] updated from transifex 2017-02-10 02:18:29 +01:00
Jenkins for ownCloud d8a86da377 [tx-robot] updated from transifex 2017-02-09 02:18:33 +01:00
Jocelyn Turcotte d1dace9e7f Remove unneeded Q_DECLARE_METATYPE 2017-02-08 17:57:20 +01:00
Jocelyn Turcotte 1cec2ca13d Fix TestSyncEngine::abortAfterFailedMkdir
It could be possible that _firstJob is marked as finished if
aborted before its parent PropagateDirectory was marked as finished,
allowing a posted scheduleNextJob call to schedule the child job
in-between.
2017-02-08 17:32:04 +01:00
Jocelyn Turcotte 1d9e08d109 Skip TestSyncEngine::abortAfterFailedMkdir 2017-02-08 17:25:32 +01:00
ckamm 7879c470b3 Merge pull request #5518 from ckamm/asserts
Improve usage of asserts
2017-02-08 15:25:02 +01:00
Christian Kamm 3c7545a45f CMake: Don't force-enable asserts #5429
Should be safer now that we've gone through asserts and assured that
one ones that should be fatal continue to exist in release builds
independently of QT_NO_DEBUG.
2017-02-08 15:15:19 +01:00
Christian Kamm 4c1fdf1dee Double check usage of asserts #5429
A few are supposed to be fatal.
2017-02-08 15:15:19 +01:00
Christian Kamm 18e25122db Performance: reserve() _remotePerm #5496 2017-02-08 15:06:09 +01:00
Olivier Goffart 439e688906 Folder: Change the wording for the massive deletion message
Issue #5503
2017-02-08 14:30:14 +01:00
ckamm 4198d9f420 Settings: Don't migrate settings on access error #5499 (#5523)
Previously, we'd try migrating from legacy settings if reading
the settings failed with an error. Now, we try again after a
couple of seconds and eventually give up.
2017-02-08 14:28:50 +01:00
Olivier Goffart 06579a5c70 AccountManager: Fix small leak in the account migration code
The QSettings object was not deleted in every possible code paths
2017-02-08 12:31:07 +01:00
Markus Goetz df773ea8bb CentOS: More build fixes 2017-02-08 11:39:28 +01:00
Markus Goetz e7be4faac8 CentOS: Another attempt 2017-02-08 11:15:23 +01:00
Jenkins for ownCloud a184c6bec1 [tx-robot] updated from transifex 2017-02-08 02:18:27 +01:00
Markus Goetz 1102ac20ac Improve previous fix
After comment on https://github.com/owncloud/client/commit/78a798eef3eb8d04360c6d9542db55469f59cacd
2017-02-07 19:40:32 +01:00
Markus Goetz 6a4ae63f14 CentOS: Another fix for old compiler version (2) 2017-02-07 19:22:04 +01:00
Markus Goetz 6c6961e4d9 ChangeLog for 2.3 additions 2017-02-07 19:04:00 +01:00
Markus Goetz 78a798eef3 Old Qt: Another build fix 2017-02-07 18:54:40 +01:00
Markus Goetz 19dd656989 CentOS: Another fix for old compiler version 2017-02-07 18:32:34 +01:00
Markus Goetz d9fac50e9b CentOS: Fix build for old compiler version 2017-02-07 18:05:29 +01:00
Markus Goetz 9614a94035 Revert "Disable RTTI"
This reverts commit 85d3de1589.
This breaks our old Qt on CentOS.

We can re-introduce this with #5470
2017-02-07 17:35:04 +01:00
Jenkins for ownCloud 5002964546 [tx-robot] updated from transifex 2017-02-07 02:18:27 +01:00
Jenkins for ownCloud 4cca1ba55f [tx-robot] updated from transifex 2017-02-06 02:18:27 +01:00
Jenkins for ownCloud 3e99c32582 [tx-robot] updated from transifex 2017-02-05 02:18:27 +01:00
Jenkins for ownCloud 74ce597f8d [tx-robot] updated from transifex 2017-02-04 02:18:27 +01:00
Jenkins for ownCloud a27b2d94b3 [tx-robot] updated from transifex 2017-02-03 02:18:27 +01:00
Jenkins for ownCloud 890e771ec1 [tx-robot] updated from transifex 2017-02-02 02:18:27 +01:00
Jenkins for ownCloud 5df10cb8f8 [tx-robot] updated from transifex 2017-02-01 02:18:28 +01:00
Jenkins for ownCloud 9acc974268 [tx-robot] updated from transifex 2017-01-31 02:18:26 +01:00
Jenkins for ownCloud 42e64026ae [tx-robot] updated from transifex 2017-01-30 02:18:28 +01:00
Jenkins for ownCloud 78e7d13f9f [tx-robot] updated from transifex 2017-01-29 02:18:27 +01:00
Jenkins for ownCloud 2d55332a45 [tx-robot] updated from transifex 2017-01-28 02:18:34 +01:00
Jenkins for ownCloud 72cd9abd4a [tx-robot] updated from transifex 2017-01-28 01:15:15 +01:00
Olivier Goffart 0d16cf41fe Merge remote-tracking branch 'origin/master' into 2.3
We can do that because the only changes that were in master but not in 2.3 were the
translations change and documentation change, and the support for the 'M' permission
which we want in 2.3.
2017-01-27 17:45:05 +01:00
Olivier Goffart 65e4afedc4 Account Wizard: don't whitelist everything on the first sync if we still want to confirm big folder
Issue: https://github.com/owncloud/client/pull/5340#issuecomment-274564441
2017-01-27 15:59:59 +01:00
Olivier Goffart b76a9654cc General Settings: Make sure to reload the settings when an account is added
The Size limit, or confirmation checkboxes might have changed.

We need to guard against saving if the control changes while we are loading

Issue: https://github.com/owncloud/client/pull/5340#issuecomment-274878023
2017-01-27 15:59:59 +01:00
Olivier Goffart 38cf459b3e Confirm External Storage: adjust the notification message
We need to forward the information that the folder is an external storage
for the notification message.

Issue: https://github.com/owncloud/client/pull/5340#issuecomment-274878023
2017-01-27 15:59:59 +01:00
Olivier Goffart ed7416098e Wizard: Increase the size
The advanced page has become quite complex and does not fit on the screen
anymore if the fonts are too big
2017-01-27 15:59:59 +01:00
Olivier Goffart 517623e457 FolderStatusModel: Different icon for external storages
Issue: https://github.com/owncloud/client/pull/5340#issuecomment-274564441
2017-01-27 15:59:59 +01:00
Olivier Goffart f89bc09fd1 Discovery: Filter 'M' out of permissions for non root storage
The sync engine rely on the 'M' in premission to ask for confirmation
(As requested in issue #5340)
But we only want to ask the premission for the 'root' of the mounting point and not
for every subfolders within it.
So we change the discovery phase in a way that it does not keep the 'M' for
children within the external storage.
2017-01-27 15:59:59 +01:00
Olivier Goffart f854c5263b Wizard: Add options to ask confirmation for external storage
Added two checkboxes in the Account Wizard in the advanced page to change the first options.
Also added a checkbox in the general settings to ask for confirmation for external storages.

Theme options allow to hide the checkboxes in the wizard.

As described in issue #5340
2017-01-27 15:59:59 +01:00
Olivier Goffart 70da607e06 Discovery: Add branding option to disable default sync of 'M' directories
Issue #5331 and https://github.com/owncloud/enterprise/issues/1594
2017-01-27 15:59:59 +01:00
Jenkins for ownCloud 4f28b2daad [tx-robot] updated from transifex 2017-01-27 02:18:28 +01:00
Jocelyn Turcotte 8884fe7236 Remove PropagateDirectory::_runningNow
This variable isn't used anymore except in a stray Q_ASSERT.
2017-01-26 17:13:54 +01:00
Jocelyn Turcotte bb3efc5988 Clear SyncEngine::_remotePerms after the sync
Also make reuse the SyncFileItem::_remotePerm QByteArray allocation
instead of redoing the copy from the csync char*.
2017-01-26 17:13:54 +01:00
Jocelyn Turcotte f6665ccc81 Pack boolean members in PropagateUploadFileCommon #5457 2017-01-26 17:13:54 +01:00
Jocelyn Turcotte f1e9be4fa8 Don't use the StopWatch for each job in production builds #5456
This accounted for more than 10% of the peak memory usage while only
providing value through the debugging log.
2017-01-26 17:13:54 +01:00
Jocelyn Turcotte 9db23d4df1 Remove SyncFileItem::_requestDuration #5456
This will save a QElapsedTimer in each job that was used only for
.owncloudsync.log (for which the request duration doesn't bring much
value).
2017-01-26 17:13:54 +01:00
Jocelyn Turcotte 9d16ad844c Don't reallocate a QByteArray for each contentChecksumType call #5481 2017-01-26 17:13:54 +01:00
Jocelyn Turcotte b0700ebbab Remove duplicate SyncFileItem entries for the log
The accuracy of that log isn't as important as the few bytes those
fields take as hostage for the whole sync.
2017-01-26 17:13:54 +01:00
Markus Goetz 2bda55be81 Don't create legacy 'folders/' subdirectory 2017-01-26 12:29:58 +01:00
Markus Goetz e859d220be Cookies: Use different DB for different accounts (#5490)
This is a follow up to #5469
2017-01-26 10:54:03 +01:00
Piotr Mrówczyński c3ae5123cb Delete finished propagation jobs in PropagateDirectory #5269 (#5400) 2017-01-26 10:03:22 +01:00
Jenkins for ownCloud a1a5111a8a [tx-robot] updated from transifex 2017-01-26 02:18:40 +01:00
Jocelyn Turcotte ee211d7609 Release SyncFileItem objects with their job
We now delete subjobs as their propagation is complete. This allows us
to also release the item by making sure that nothing else is holding a
reference to it.

Remove the stored SyncFileItemVector from SyncEngine and SyncResult
and instead gather the needed info progressively as each itemCompleted
signal is emitted.

This frees some holes on the heap as propagation goes, allowing many
memory allocations without the need of requesting more virtual memory
from the OS, preventing the memory usage from increasingly growing.
2017-01-25 23:26:23 +01:00
Jocelyn Turcotte 1fc5a76622 Pass the SyncFileItem as SyncFileItemPtr in itemCompleted
This will allow us to keep a reference on the items in connected slots.
2017-01-25 23:26:23 +01:00
Jocelyn Turcotte a764d7eb86 Don't pass the PropagatorJob in itemCompleted
This was to catch duplicate emissions for PropagateDirectory but we
don't emit this signal anymore from there.
This fixes a warning about PropagatorJob not being a registered metatype.

This reverts commit fe42c1a818.
2017-01-25 23:26:23 +01:00
Jocelyn Turcotte 605a18ff73 Tests: Add a large sync benchmark
This simulates a ~50k files sync that can be used to measure memory
usage without having to wait for a server.
2017-01-25 23:26:23 +01:00
Jocelyn Turcotte 92e86641d1 Bring back the error message if the sync-exclude.lst file is missing
We unconditionnally add the file on startup, but don't check for errors.
During sync we check for errors, but only try loading the file if it
exists.
2017-01-25 23:26:23 +01:00
Markus Goetz c8cfb3160e Merge remote-tracking branch 'origin/selective-sync-big-folder' into 2.3 2017-01-25 19:45:20 +01:00
Olivier Goffart 268fc97a71 Merge pull request #5480 from owncloud/chunking-ng-cleanup
Chunking ng cleanup
2017-01-25 19:19:48 +01:00
Jenkins for ownCloud de2458d892 [tx-robot] updated from transifex 2017-01-25 02:18:34 +01:00
Matthew Setter d75b838897 Add extra note about deleting .csync_journal.db
This is added as per the recommendation from @guruz in #5252.
2017-01-24 16:18:21 +01:00
Carla Schroder 4df7cab72a add note about deleting .csync_journal.db for "CSync unknown error" 2017-01-24 16:18:21 +01:00
Christian Kamm 59c1fdbe05 Shib: Use different keychain entry per account #5469
Previously shib multiaccount didn't work at all because the
session cookie was stored in the same keychain entry.
2017-01-24 13:14:11 +01:00
Christian Kamm 26234dbf6c Folders: Adjust ui when account is disconnected #5477
Previously if you paused/unpaused a folder for a disconnected account
they would prepare to sync and thus display the 'Waiting...' text. With
this change, folders that can't possibly sync don't show text like
this.

When account connectivity changes, all unpaused folders will be
scheduled anyway.
2017-01-24 10:26:03 +01:00
Jenkins for ownCloud 05a254b527 [tx-robot] updated from transifex 2017-01-24 02:18:28 +01:00
Jenkins for ownCloud d282a8380e [tx-robot] updated from transifex 2017-01-23 11:11:23 +01:00
Jenkins for ownCloud 83dfbb933b [tx-robot] updated from transifex 2017-01-22 02:18:27 +01:00
Jenkins for ownCloud 100603fdc0 [tx-robot] updated from transifex 2017-01-21 02:18:28 +01:00
Olivier Goffart 8a70d22af7 ChunkingNG: Add some tests
- Test that we recover correctly if the chunks were removed on the server.
 - Test changing the file localy while uploading.
2017-01-20 14:48:53 +01:00
Olivier Goffart a63d970e5e ChunkingNG: remove stale chunks when cleaning the uploadInfo table
Stale chunks might be there because a file was removed or would just not
be uploaded, for any reason.
We just start the DeleteJob but we don't care if it success or not.

Relates to https://github.com/owncloud/core/issues/26981

One of the test is testing the case where the file is modified on the server
during the upload. So this test the precondition failed error.
The FakeGetReply logic was modified because resizing a 150MB big QByteArray
by increment of 16k just did not scale when downloading a big file.
2017-01-20 14:48:53 +01:00
Olivier Goffart f6c77fad17 ChunkingNG: delete stale chunks if the file was changed locally
Relates to https://github.com/owncloud/core/issues/26981

We do not track the success or error of the DeleteJob because it does not
matter. If it fails, it might be because the chunks were already removed.
If not, the chunks will be stale, but the server must anyway do a few
cleanup from time to time because we do not always remove the chunks
2017-01-20 14:48:53 +01:00
Olivier Goffart 0249a68420 SelectiveSync: adjust the position of the "Loading" label
It should be parented to the treeview to be sure that is shows inside of it.
2017-01-13 10:55:45 +01:00
Christian Kamm aa6f041c36 wizardSelectiveSyncDefaultNothing applies to folder wizard #4581
Previous the Theme option only applied to the account setup wizard.
2017-01-13 10:52:39 +01:00
Christian Kamm dce3f8c4f6 Selective sync: Adjust widget common to folder/account wizards 2017-01-13 10:52:10 +01:00
133 arquivos alterados com 11835 adições e 9577 exclusões
+2 -3
Ver Arquivo
@@ -7,6 +7,8 @@ endif()
project(client)
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(OEM_THEME_DIR "" CACHE STRING "Define directory containing a custom theme")
if ( EXISTS ${OEM_THEME_DIR}/OEM.cmake )
include ( ${OEM_THEME_DIR}/OEM.cmake )
@@ -58,9 +60,6 @@ if( UNIX AND NOT APPLE )
endif()
####
# Enable Q_ASSERT etc. in all builds
add_definitions( -DQT_FORCE_ASSERTS )
include(GNUInstallDirs)
include(DefineInstallationPaths)
include(GenerateExportHeader)
+5 -4
Ver Arquivo
@@ -1,10 +1,11 @@
ChangeLog
=========
version 2.3.0 (2017-0x-xx)
* WiP!
* WiP Switch Windows and OS X build to 5.6.2
* WiP Performance improvements for exclude detection
version 2.3.0 (2017-02-xx)
* Decreased memory usage during sync
* Overlay icons: Lower CPU usage
* Allow to not sync the server's external storages by default
* Switch Windows and OS X build to 5.6.2
* Switch to new ownCloud server WebDAV endpoint
* Chunking NG: New file upload chunking algorithmn for ownCloud server 9.2
* Allow to sync a folder to multiple different servers (Filename change from .csync_journal.db to _sync_$HASH.db)
+1 -1
Ver Arquivo
@@ -39,7 +39,7 @@ node('CLIENT') {
stage 'Win32'
def win32 = docker.image('deepdiver/docker-owncloud-client-win32:latest')
def win32 = docker.image('guruz/docker-owncloud-client-win32:latest')
win32.pull() // make sure we have the latest available from Docker Hub
win32.inside {
sh '''
+1 -1
Ver Arquivo
@@ -3,7 +3,7 @@ StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Показати примітки
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Знайдено процес(и) ${APPLICATION_EXECUTABLE}, які необхідно зупинити.$\nХочете щоб програма установки зробила це самостійно?"
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Завершення процесів ${APPLICATION_EXECUTABLE}."
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Не знайдено процеси, які необхідно зупинити!"
StrCpy $PageReinstall_NEW_Field_1 "Знайдено застарілу версію програми ${APPLICATION_NAME}. Рекомендуємо її спочатку видалити. Оберіть подальшу дію та натисніть $\"Далі$\"."
StrCpy $PageReinstall_NEW_Field_1 "У вашої системі встановлена застаріла версія додатку ${APPLICATION_NAME}. Рекомендуємо видалити її перед початком встановлення поточної версії. Оберіть подальшу дію та натисніть $\"Далі$\"."
StrCpy $PageReinstall_NEW_Field_2 "Видалити перед установкою"
StrCpy $PageReinstall_NEW_Field_3 "Не видаляти"
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Установлено"
+1 -2
Ver Arquivo
@@ -28,7 +28,6 @@ include(ConfigureChecks.cmake)
set(SOURCE_DIR ${CMAKE_SOURCE_DIR})
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
include_directories(${CMAKE_CURRENT_BINARY_DIR})
@@ -41,7 +40,7 @@ endif (MEM_NULL_TESTS)
add_subdirectory(src)
if (UNIT_TESTING)
set(WITH_UNIT_TESTING ON)
set(WITH_TESTING ON)
find_package(CMocka)
if (CMOCKA_FOUND)
+1 -1
Ver Arquivo
@@ -26,4 +26,4 @@
#cmakedefine HAVE___MINGW_ASPRINTF 1
#cmakedefine HAVE_ASPRINTF 1
#cmakedefine WITH_UNIT_TESTING 1
#cmakedefine WITH_TESTING 1
+1 -1
Ver Arquivo
@@ -45,7 +45,7 @@
#define CSYNC_LOG_CATEGORY_NAME "csync.exclude"
#include "csync_log.h"
#ifndef WITH_UNIT_TESTING
#ifndef WITH_TESTING
static
#endif
int _csync_exclude_add(c_strlist_t **inList, const char *string) {
+1 -1
Ver Arquivo
@@ -36,7 +36,7 @@ enum csync_exclude_type_e {
};
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
#ifdef WITH_UNIT_TESTING
#ifdef WITH_TESTING
int OCSYNC_EXPORT _csync_exclude_add(c_strlist_t **inList, const char *string);
#endif
+2 -2
Ver Arquivo
@@ -89,7 +89,7 @@ struct csync_s {
/* hooks for checking the white list (uses the update_callback_userdata) */
int (*checkSelectiveSyncBlackListHook)(void*, const char*);
int (*checkSelectiveSyncNewFolderHook)(void*, const char*);
int (*checkSelectiveSyncNewFolderHook)(void*, const char* /* path */, const char* /* remotePerm */);
csync_vio_opendir_hook remote_opendir_hook;
@@ -207,7 +207,7 @@ __attribute__ ((packed))
#endif
;
void csync_file_stat_free(csync_file_stat_t *st);
OCSYNC_EXPORT void csync_file_stat_free(csync_file_stat_t *st);
/*
* context for the treewalk function
+5 -7
Ver Arquivo
@@ -56,17 +56,15 @@ int csync_get_statedb_exists(CSYNC *ctx);
*
* @return 0 on success, less than 0 if an error occurred with errno set.
*/
int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb);
OCSYNC_EXPORT int csync_statedb_load(CSYNC *ctx, const char *statedb, sqlite3 **pdb);
int csync_statedb_close(CSYNC *ctx);
OCSYNC_EXPORT int csync_statedb_close(CSYNC *ctx);
csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx, uint64_t phash);
OCSYNC_EXPORT csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx, uint64_t phash);
csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx, uint64_t inode);
OCSYNC_EXPORT csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx, uint64_t inode);
csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx, const char *file_id);
char *csync_statedb_get_etag(CSYNC *ctx, uint64_t jHash);
OCSYNC_EXPORT csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx, const char *file_id);
/**
* @brief Query all files metadata inside and below a path.
+1 -1
Ver Arquivo
@@ -436,7 +436,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
st->instruction = CSYNC_INSTRUCTION_NEW;
if (fs->type == CSYNC_VIO_FILE_TYPE_DIRECTORY && ctx->current == REMOTE_REPLICA && ctx->callbacks.checkSelectiveSyncNewFolderHook) {
if (ctx->callbacks.checkSelectiveSyncNewFolderHook(ctx->callbacks.update_callback_userdata, path)) {
if (ctx->callbacks.checkSelectiveSyncNewFolderHook(ctx->callbacks.update_callback_userdata, path, fs->remotePerm)) {
csync_file_stat_free(st);
return 1;
}
+10 -5
Ver Arquivo
@@ -132,11 +132,16 @@ changed and no synchronization occurs.
In the event a file has changed on both the local and the remote repository
since the last sync run, it can not easily be decided which version of the file
is the one that should be used. However, changes to any side will not be lost. Instead,
a *conflict case* is created. The client resolves this conflict by creating a
conflict file of the older of the two files and saving the newer file under the
original file name. Conflict files are always created on the client and never
on the server. The conflict file uses the same name as the original file, but
is appended with the timestamp of the conflict detection.
a *conflict case* is created. The client resolves this conflict by renaming the
local file, appending a conflict label and timestamp, and saving the remote file
under the original file name.
Example: Assume there is a conflict in message.txt because its contents have
changed both locally and remotely since the last sync run. The local file with
the local changes will be renamed to message_conflict-20160101-153110.txt and
the remote file will be downloaded and saved as message.txt.
Conflict files are always created on the client and never on the server.
.. _ignored-files-label:
+15
Ver Arquivo
@@ -50,6 +50,21 @@ Identifying Basic Functionality Problems
As an example, after installing the ``cadaver`` app, you can issue the
``propget`` command to obtain various properties pertaining to the current
directory and also verify WebDAV server connection.
"CSync unknown error"
---------------------
If you see this error message stop your client, delete the
``.csync_journal.db`` file, and then restart your client.
There is a ``.csync_journal.db`` file inside the folder of every account
configured on your client.
.. NOTE::
Please note that this will also erase some of your settings about which
files to download.
See https://github.com/owncloud/client/issues/5226 for more discussion of this
issue.
Isolating other issues
+86 -8
Ver Arquivo
@@ -646,6 +646,75 @@ 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
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
GenericName[oc]=Dorsièr de Sincronizacion
@@ -671,7 +740,9 @@ Comment[ja_JP]=@APPLICATION_NAME@ デスクトップ同期クライアント
GenericName[ja_JP]=フォルダー同期
Name[ja_JP]=@APPLICATION_NAME@ デスクトップ同期クライアント
Icon[ja_JP]=@APPLICATION_EXECUTABLE@
Comment[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη
GenericName[el]=Συγχρονισμός φακέλου
Name[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη
Icon[el]=@APPLICATION_EXECUTABLE@
Comment[en_GB]=@APPLICATION_NAME@ desktop synchronisation client
GenericName[en_GB]=Folder Sync
@@ -685,10 +756,13 @@ Comment[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
GenericName[de_DE]=Ordner-Synchronisation
Name[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
Icon[de_DE]=@APPLICATION_EXECUTABLE@
Comment[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
GenericName[pl]=Folder Synchronizacji
Name[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
Icon[pl]=@APPLICATION_EXECUTABLE@
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
GenericName[bg_BG]=Синхронизиране на папката
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
GenericName[fa]=همسان سازی پوشه‌ها
Name[fa]=@APPLICATION_EXECUTABLE@ نسخه‌ی همسان سازی مشتری
Icon[fa]=@APPLICATION_EXECUTABLE@
Comment[fr]=@APPLICATION_NAME@ synchronisation du client
GenericName[fr]=Dossier de Synchronisation
Name[fr]=@APPLICATION_NAME@ synchronisation du client
@@ -697,6 +771,10 @@ Comment[he]=@APPLICATION_NAME@ לקוח סנכון שולחן עבודה
GenericName[he]=סנכון תיקייה
Name[he]=@APPLICATION_NAME@ לקוח סנכרון שולחן עבודה
Icon[he]=@APPLICATION_EXECUTABLE@
Comment[ia]=@APPLICATION_NAME@ cliente de synchronisation pro scriptorio
GenericName[ia]=Synchronisar Dossier
Name[ia]=@APPLICATION_NAME@ cliente de synchronisation pro scriptorio
Icon[ia]=@APPLICATION_EXECUTABLE@
Comment[id]=Klien sinkronisasi desktop @APPLICATION_NAME@
GenericName[id]=Folder Sync
Name[id]=Klien sync desktop @APPLICATION_NAME@
@@ -724,10 +802,10 @@ Comment[et_EE]=@APPLICATION_NAME@ sünkroonimise klient töölauale
GenericName[et_EE]=Kaustade sünkroonimine
Name[et_EE]=@APPLICATION_NAME@ sünkroonimise klient töölauale
Icon[et_EE]=@APPLICATION_EXECUTABLE@
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
GenericName[bg_BG]=Синхронизиране на папката
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
Comment[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
GenericName[pl]=Folder Synchronizacji
Name[pl]=@APPLICATION_NAME@ klient synchronizacji dla komputerów stacjonarnych
Icon[pl]=@APPLICATION_EXECUTABLE@
Comment[pt_BR]=@APPLICATION_NAME@ cliente de sincronização do computador
GenericName[pt_BR]=Sincronização de Pasta
Name[pt_BR]=@APPLICATION_NAME@ cliente de sincronização de desktop
-1
Ver Arquivo
@@ -1,7 +1,6 @@
# TODO: OSX and LIB_ONLY seem to require this to go to binary dir only
if(NOT TOKEN_AUTH_ONLY)
endif()
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
-2
Ver Arquivo
@@ -1,8 +1,6 @@
project(cmd)
set(CMAKE_AUTOMOC TRUE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(cmd_NAME ${APPLICATION_EXECUTABLE}cmd)
set(cmd_SRC
cmd.cpp
-2
Ver Arquivo
@@ -3,8 +3,6 @@ set(CMAKE_AUTOMOC TRUE)
add_subdirectory(updater)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
#TODO Move resources files
qt_add_resources(MIRALL_RC_SRC ../../client.qrc)
if ( IS_DIRECTORY ${OEM_THEME_DIR} )
+16 -7
Ver Arquivo
@@ -46,11 +46,17 @@ AccountManager *AccountManager::instance()
bool AccountManager::restore()
{
auto settings = Utility::settingsWithGroup(QLatin1String(accountsC));
if (settings->status() != QSettings::NoError) {
qDebug() << "Could not read settings from" << settings->fileName()
<< settings->status();
return false;
}
// If there are no accounts, check the old format.
if (settings->childGroups().isEmpty()
&& !settings->contains(QLatin1String(versionC))) {
return restoreFromLegacySettings();
restoreFromLegacySettings();
return true;
}
foreach (const auto& accountId, settings->childGroups()) {
@@ -69,6 +75,9 @@ bool AccountManager::restore()
bool AccountManager::restoreFromLegacySettings()
{
qDebug() << "Migrate: restoreFromLegacySettings, checking settings group"
<< Theme::instance()->appName();
// try to open the correctly themed settings
auto settings = Utility::settingsWithGroup(Theme::instance()->appName());
@@ -86,7 +95,7 @@ bool AccountManager::restoreFromLegacySettings()
QFileInfo fi( oCCfgFile );
if( fi.isReadable() ) {
QSettings *oCSettings = new QSettings(oCCfgFile, QSettings::IniFormat);
std::unique_ptr<QSettings> oCSettings(new QSettings(oCCfgFile, QSettings::IniFormat));
oCSettings->beginGroup(QLatin1String("ownCloud"));
// Check the theme url to see if it is the same url that the oC config was for
@@ -101,9 +110,7 @@ bool AccountManager::restoreFromLegacySettings()
qDebug() << "Migrate oC config if " << oCUrl << " == " << overrideUrl << ":"
<< (oCUrl == overrideUrl ? "Yes" : "No");
if( oCUrl == overrideUrl ) {
settings.reset( oCSettings );
} else {
delete oCSettings;
settings = std::move(oCSettings);
}
}
}
@@ -196,8 +203,8 @@ void AccountManager::saveAccountHelper(Account* acc, QSettings& settings, bool s
if (acc->_am) {
CookieJar* jar = qobject_cast<CookieJar*>(acc->_am->cookieJar());
if (jar) {
qDebug() << "Saving cookies.";
jar->save();
qDebug() << "Saving cookies." << acc->cookieJarPath();
jar->save(acc->cookieJarPath());
}
}
}
@@ -289,6 +296,8 @@ void AccountManager::deleteAccount(AccountState* account)
auto copy = *it; // keep a reference to the shared pointer so it does not delete it just yet
_accounts.erase(it);
QFile::remove(account->account()->cookieJarPath());
auto settings = Utility::settingsWithGroup(QLatin1String(accountsC));
settings->remove(account->account()->id());
+3 -1
Ver Arquivo
@@ -36,7 +36,9 @@ public:
/**
* Creates account objects from a given settings file.
* return true if the account was restored
*
* Returns false if there was an error reading the settings,
* but note that settings not existing is not an error.
*/
bool restore();
+7 -2
Ver Arquivo
@@ -675,8 +675,13 @@ void AccountSettings::refreshSelectiveSyncStatus()
ui->selectiveSyncButtons->setVisible(true);
ui->bigFolderUi->setVisible(false);
} else {
QString wholeMsg = tr("There are new folders that were not synchronized because they are too big: ") + msg;
ui->selectiveSyncNotification->setText(wholeMsg);
ConfigFile cfg;
QString info =
!cfg.confirmExternalStorage() ? tr("There are folders that were not synchronized because they are too big: ") :
!cfg.newBigFolderSizeLimit().first ? tr("There are folders that were not synchronized because they are external storages: ") :
tr("There are folders that were not synchronized because they are too big or external storages: ");
ui->selectiveSyncNotification->setText(info + msg);
ui->selectiveSyncButtons->setVisible(false);
ui->bigFolderUi->setVisible(true);
shouldBeVisible = true;
+17 -1
Ver Arquivo
@@ -156,7 +156,23 @@ Application::Application(int &argc, char **argv) :
connect(this, SIGNAL(messageReceived(QString, QObject*)), SLOT(slotParseMessage(QString, QObject*)));
AccountManager::instance()->restore();
if (!AccountManager::instance()->restore()) {
// If there is an error reading the account settings, try again
// after a couple of seconds, if that fails, give up.
// (non-existence is not an error)
Utility::sleep(5);
if (!AccountManager::instance()->restore()) {
qDebug() << "Could not read the account settings, quitting";
QMessageBox::critical(
0,
tr("Error accessing the configuration file"),
tr("There was an error while accessing the configuration "
"file at %1.").arg(ConfigFile().configFile()),
tr("Quit ownCloud"));
QTimer::singleShot(0, qApp, SLOT(quit()));
return;
}
}
FolderMan::instance()->setSyncEnabled(true);
+3 -3
Ver Arquivo
@@ -130,7 +130,7 @@ void ShibbolethCredentials::fetchFromKeychain()
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
job->setSettings(Utility::settingsWithGroup(Theme::instance()->appName(), job).release());
job->setInsecureFallback(false);
job->setKey(keychainKey(_account->url().toString(), "shibAssertion"));
job->setKey(keychainKey(_account->url().toString(), user()));
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
job->start();
}
@@ -309,7 +309,7 @@ void ShibbolethCredentials::storeShibCookie(const QNetworkCookie &cookie)
job->setSettings(Utility::settingsWithGroup(Theme::instance()->appName(), job).release());
// we don't really care if it works...
//connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
job->setKey(keychainKey(_account->url().toString(), "shibAssertion"));
job->setKey(keychainKey(_account->url().toString(), user()));
job->setTextData(QString::fromUtf8(cookie.toRawForm()));
job->start();
}
@@ -318,7 +318,7 @@ void ShibbolethCredentials::removeShibCookie()
{
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
job->setSettings(Utility::settingsWithGroup(Theme::instance()->appName(), job).release());
job->setKey(keychainKey(_account->url().toString(), "shibAssertion"));
job->setKey(keychainKey(_account->url().toString(), user()));
job->start();
}
+70 -167
Ver Arquivo
@@ -52,9 +52,7 @@ Folder::Folder(const FolderDefinition& definition,
: QObject(parent)
, _accountState(accountState)
, _definition(definition)
, _csyncError(false)
, _csyncUnavail(false)
, _wipeDb(false)
, _proxyDirty(true)
, _lastSyncDuration(0)
, _consecutiveFailingSyncs(0)
@@ -87,8 +85,6 @@ Folder::Folder(const FolderDefinition& definition,
connect(_accountState.data(), SIGNAL(isConnectedChanged()), this, SIGNAL(canSyncChanged()));
connect(_engine.data(), SIGNAL(rootEtag(QString)), this, SLOT(etagRetreivedFromSyncEngine(QString)));
connect(_engine.data(), SIGNAL(treeWalkResult(const SyncFileItemVector&)),
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
@@ -102,9 +98,10 @@ Folder::Folder(const FolderDefinition& definition,
SLOT(slotAboutToRestoreBackup(bool*)));
connect(_engine.data(), SIGNAL(folderDiscovered(bool,QString)), this, SLOT(slotFolderDiscovered(bool,QString)));
connect(_engine.data(), SIGNAL(transmissionProgress(ProgressInfo)), this, SLOT(slotTransmissionProgress(ProgressInfo)));
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_engine.data(), SIGNAL(newBigFolder(QString)), this, SLOT(slotNewBigFolderDiscovered(QString)));
connect(_engine.data(), SIGNAL(itemCompleted(const SyncFileItemPtr &)),
this, SLOT(slotItemCompleted(const SyncFileItemPtr &)));
connect(_engine.data(), SIGNAL(newBigFolder(QString,bool)),
this, SLOT(slotNewBigFolderDiscovered(QString,bool)));
connect(_engine.data(), SIGNAL(seenLockedFile(QString)), FolderMan::instance(), SLOT(slotSyncOnceFileUnlocks(QString)));
connect(_engine.data(), SIGNAL(aboutToPropagate(SyncFileItemVector&)),
SLOT(slotLogPropagationStart()));
@@ -138,13 +135,13 @@ void Folder::checkLocalPath()
} else {
// Check directory again
if( !FileSystem::fileExists(_definition.localPath, fi) ) {
_syncResult.setErrorString(tr("Local folder %1 does not exist.").arg(_definition.localPath));
_syncResult.appendErrorString(tr("Local folder %1 does not exist.").arg(_definition.localPath));
_syncResult.setStatus( SyncResult::SetupError );
} else if( !fi.isDir() ) {
_syncResult.setErrorString(tr("%1 should be a folder but is not.").arg(_definition.localPath));
_syncResult.appendErrorString(tr("%1 should be a folder but is not.").arg(_definition.localPath));
_syncResult.setStatus( SyncResult::SetupError );
} else if( !fi.isReadable() ) {
_syncResult.setErrorString(tr("%1 is not readable.").arg(_definition.localPath));
_syncResult.appendErrorString(tr("%1 is not readable.").arg(_definition.localPath));
_syncResult.setStatus( SyncResult::SetupError );
}
}
@@ -267,8 +264,8 @@ SyncResult Folder::syncResult() const
void Folder::prepareToSync()
{
_syncResult.reset();
_syncResult.setStatus( SyncResult::NotYetStarted );
_syncResult.clearErrors();
}
void Folder::slotRunEtagJob()
@@ -322,120 +319,33 @@ void Folder::etagRetreivedFromSyncEngine(const QString& etag)
}
void Folder::bubbleUpSyncResult()
void Folder::showSyncResultPopup()
{
// count new, removed and updated items
int newItems = 0;
int removedItems = 0;
int updatedItems = 0;
int ignoredItems = 0;
int renamedItems = 0;
int conflictItems = 0;
int errorItems = 0;
SyncFileItemPtr firstItemNew;
SyncFileItemPtr firstItemDeleted;
SyncFileItemPtr firstItemUpdated;
SyncFileItemPtr firstItemRenamed;
SyncFileItemPtr firstConflictItem;
SyncFileItemPtr firstItemError;
QElapsedTimer timer;
timer.start();
foreach (const SyncFileItemPtr &item, _syncResult.syncFileItemVector() ) {
// Process the item to the gui
if( item->_status == SyncFileItem::FatalError || item->_status == SyncFileItem::NormalError ) {
//: this displays an error string (%2) for a file %1
slotSyncError( tr("%1: %2").arg(item->_file, item->_errorString) );
errorItems++;
if (!firstItemError) {
firstItemError = item;
}
} else if( item->_status == SyncFileItem::FileIgnored ) {
// ignored files don't show up in notifications
continue;
} else if( item->_status == SyncFileItem::Conflict ) {
conflictItems++;
if (!firstConflictItem) {
firstConflictItem = item;
}
} else {
// add new directories or remove gone away dirs to the watcher
if (item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_NEW ) {
FolderMan::instance()->addMonitorPath( alias(), path()+item->_file );
}
if (item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_REMOVE ) {
FolderMan::instance()->removeMonitorPath( alias(), path()+item->_file );
}
if (!item->hasErrorStatus() && item->_direction == SyncFileItem::Down) {
switch (item->_instruction) {
case CSYNC_INSTRUCTION_NEW:
case CSYNC_INSTRUCTION_TYPE_CHANGE:
newItems++;
if (!firstItemNew)
firstItemNew = item;
break;
case CSYNC_INSTRUCTION_REMOVE:
removedItems++;
if (!firstItemDeleted)
firstItemDeleted = item;
break;
case CSYNC_INSTRUCTION_SYNC:
updatedItems++;
if (!firstItemUpdated)
firstItemUpdated = item;
break;
case CSYNC_INSTRUCTION_ERROR:
qDebug() << "Got Instruction ERROR. " << _syncResult.errorString();
break;
case CSYNC_INSTRUCTION_RENAME:
if (!firstItemRenamed) {
firstItemRenamed = item;
}
renamedItems++;
break;
default:
// nothing.
break;
}
} else if( item->_direction == SyncFileItem::None ) { // ignored files counting.
if( item->_instruction == CSYNC_INSTRUCTION_IGNORE ) {
ignoredItems++;
}
}
}
if( _syncResult.firstItemNew() ) {
createGuiLog( _syncResult.firstItemNew()->_file, LogStatusNew, _syncResult.numNewItems() );
}
if( _syncResult.firstItemDeleted() ) {
createGuiLog( _syncResult.firstItemDeleted()->_file, LogStatusRemove, _syncResult.numRemovedItems() );
}
if( _syncResult.firstItemUpdated() ) {
createGuiLog( _syncResult.firstItemUpdated()->_file, LogStatusUpdated, _syncResult.numUpdatedItems() );
}
qDebug() << "Processing result list and logging took " << timer.elapsed() << " Milliseconds.";
_syncResult.setWarnCount(ignoredItems);
if( firstItemNew ) {
createGuiLog( firstItemNew->_file, LogStatusNew, newItems );
}
if( firstItemDeleted ) {
createGuiLog( firstItemDeleted->_file, LogStatusRemove, removedItems );
}
if( firstItemUpdated ) {
createGuiLog( firstItemUpdated->_file, LogStatusUpdated, updatedItems );
}
if( firstItemRenamed ) {
if( _syncResult.firstItemRenamed() ) {
LogStatus status(LogStatusRename);
// if the path changes it's rather a move
QDir renTarget = QFileInfo(firstItemRenamed->_renameTarget).dir();
QDir renSource = QFileInfo(firstItemRenamed->_file).dir();
QDir renTarget = QFileInfo(_syncResult.firstItemRenamed()->_renameTarget).dir();
QDir renSource = QFileInfo(_syncResult.firstItemRenamed()->_file).dir();
if(renTarget != renSource) {
status = LogStatusMove;
}
createGuiLog( firstItemRenamed->_originalFile, status, renamedItems, firstItemRenamed->_renameTarget );
createGuiLog( _syncResult.firstItemRenamed()->_originalFile, status, _syncResult.numRenamedItems(), _syncResult.firstItemRenamed()->_renameTarget );
}
if( firstConflictItem ) {
createGuiLog( firstConflictItem->_file, LogStatusConflict, conflictItems );
if( _syncResult.firstConflictItem() ) {
createGuiLog( _syncResult.firstConflictItem()->_file, LogStatusConflict, _syncResult.numConflictItems() );
}
createGuiLog( firstItemError->_file, LogStatusError, errorItems );
createGuiLog( _syncResult.firstItemError()->_file, LogStatusError, _syncResult.numErrorItems() );
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
}
@@ -574,12 +484,6 @@ void Folder::slotWatchedPathChanged(const QString& path)
scheduleThisFolderSoon();
}
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
_syncResult.setSyncFileItemVector(items);
}
void Folder::saveToSettings() const
{
// Remove first to make sure we don't get duplicates
@@ -642,9 +546,6 @@ void Folder::slotTerminateSync()
if( _engine->isSyncRunning() ) {
_engine->abort();
// Do not display an error message, user knows his own actions.
// _errors.append( tr("The CSync thread terminated.") );
// _csyncError = true;
setSyncState(SyncResult::SyncAbortRequested);
}
}
@@ -692,10 +593,9 @@ bool Folder::setIgnoredFiles()
// a QSet of files to load.
ConfigFile cfg;
QString systemList = cfg.excludeFile(ConfigFile::SystemScope);
if( QFile::exists(systemList) ) {
qDebug() << "==== adding system ignore list to csync:" << systemList;
_engine->excludedFiles().addExcludeFilePath(systemList);
}
qDebug() << "==== adding system ignore list to csync:" << systemList;
_engine->excludedFiles().addExcludeFilePath(systemList);
QString userList = cfg.excludeFile(ConfigFile::UserScope);
if( QFile::exists(userList) ) {
qDebug() << "==== adding user defined ignore list to csync:" << userList;
@@ -726,19 +626,17 @@ void Folder::startSync(const QStringList &pathList)
qCritical() << "* ERROR csync is still running and new sync requested.";
return;
}
_errors.clear();
_csyncError = false;
_csyncUnavail = false;
_timeSinceLastSyncStart.restart();
_syncResult.clearErrors();
_syncResult.setStatus( SyncResult::SyncPrepare );
_syncResult.setSyncFileItemVector(SyncFileItemVector());
emit syncStateChange();
qDebug() << "*** Start syncing " << remoteUrl().toString() << " - client version"
<< qPrintable(Theme::instance()->version());
_fileLog->start(path());
if (!setIgnoredFiles())
{
slotSyncError(tr("Could not read system exclude file"));
@@ -748,15 +646,15 @@ void Folder::startSync(const QStringList &pathList)
setDirtyNetworkLimits();
SyncOptions opt;
ConfigFile cfgFile;
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
quint64 limit = newFolderLimit.first ? newFolderLimit.second * 1000 * 1000 : -1; // convert from MB to B
_engine->setNewBigFolderSizeLimit(limit);
opt._newBigFolderSizeLimit = newFolderLimit.first ? newFolderLimit.second * 1000LL * 1000LL : -1; // convert from MB to B
opt._confirmExternalStorage = cfgFile.confirmExternalStorage();
_engine->setSyncOptions(opt);
_engine->setIgnoreHiddenFiles(_definition.ignoreHiddenFiles);
_fileLog->start(path());
QMetaObject::invokeMethod(_engine.data(), "startSync", Qt::QueuedConnection);
emit syncStarted();
@@ -787,8 +685,7 @@ void Folder::setDirtyNetworkLimits()
void Folder::slotSyncError(const QString& err)
{
_errors.append( err );
_csyncError = true;
_syncResult.appendErrorString(err);
}
void Folder::slotSyncStarted()
@@ -810,29 +707,26 @@ void Folder::slotSyncFinished(bool success)
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
<< " SSL " << QSslSocket::sslLibraryVersionString().toUtf8().data()
#endif
;
;
if( _csyncError ) {
qDebug() << "-> SyncEngine finished with ERROR, warn count is" << _syncResult.warnCount();
bool syncError = !_syncResult.errorStrings().isEmpty();
if( syncError ) {
qDebug() << "-> SyncEngine finished with ERROR";
} else {
qDebug() << "-> SyncEngine finished without problem.";
}
_fileLog->finish();
bubbleUpSyncResult();
showSyncResultPopup();
auto anotherSyncNeeded = _engine->isAnotherSyncNeeded();
if (_csyncError) {
if (syncError) {
_syncResult.setStatus(SyncResult::Error);
qDebug() << " ** error Strings: " << _errors;
_syncResult.setErrorStrings( _errors );
qDebug() << " * owncloud csync thread finished with error";
} else if (_csyncUnavail) {
_syncResult.setStatus(SyncResult::Error);
qDebug() << " ** csync not available.";
} else if( _syncResult.warnCount() > 0 ) {
// there have been warnings on the way.
} else if( _syncResult.foundFilesNotSynced() ) {
_syncResult.setStatus(SyncResult::Problem);
} else if( _definition.paused ) {
// Maybe the sync was terminated because the user paused the folder
@@ -909,26 +803,28 @@ void Folder::slotFolderDiscovered(bool, QString folderName)
// and hand the result over to the progress dispatcher.
void Folder::slotTransmissionProgress(const ProgressInfo &pi)
{
if( !pi.isUpdatingEstimates() ) {
// this is the beginning of a sync, set the warning level to 0
_syncResult.setWarnCount(0);
}
emit progressInfo(pi);
ProgressDispatcher::instance()->setProgressInfo(alias(), pi);
}
// a item is completed: count the errors and forward to the ProgressDispatcher
void Folder::slotItemCompleted(const SyncFileItem &item, const PropagatorJob& job)
void Folder::slotItemCompleted(const SyncFileItemPtr &item)
{
if (Progress::isWarningKind(item._status)) {
// Count all error conditions.
_syncResult.setWarnCount(_syncResult.warnCount()+1);
// add new directories or remove gone away dirs to the watcher
if (item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_NEW ) {
FolderMan::instance()->addMonitorPath( alias(), path()+item->_file );
}
_fileLog->logItem(item);
emit ProgressDispatcher::instance()->itemCompleted(alias(), item, job);
if (item->_isDirectory && item->_instruction == CSYNC_INSTRUCTION_REMOVE ) {
FolderMan::instance()->removeMonitorPath( alias(), path()+item->_file );
}
_syncResult.processCompletedItem(item);
_fileLog->logItem(*item);
emit ProgressDispatcher::instance()->itemCompleted(alias(), item);
}
void Folder::slotNewBigFolderDiscovered(const QString &newF)
void Folder::slotNewBigFolderDiscovered(const QString &newF, bool isExternal)
{
auto newFolder = newF;
if (!newFolder.endsWith(QLatin1Char('/'))) {
@@ -953,9 +849,11 @@ void Folder::slotNewBigFolderDiscovered(const QString &newF)
journal->setSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList, undecidedList);
emit newBigFolderDiscovered(newFolder);
}
QString message = tr("A new folder larger than %1 MB has been added: %2.\n"
"Please go in the settings to select it if you wish to download it.")
.arg(ConfigFile().newBigFolderSizeLimit().second).arg(newF);
QString message = !isExternal ?
(tr("A new folder larger than %1 MB has been added: %2.\n")
.arg(ConfigFile().newBigFolderSizeLimit().second).arg(newF))
: (tr("A folder from an external storage has been added.\n"));
message += tr("Please go in the settings to select it if you wish to download it.");
auto logger = Logger::instance();
logger->postOptionalGuiLog(Theme::instance()->appNameGUI(), message);
@@ -984,17 +882,22 @@ void Folder::setSaveBackwardsCompatible(bool save)
_saveBackwardsCompatible = save;
}
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel)
{
ConfigFile cfgFile;
if (!cfgFile.promptDeleteFiles())
return;
QString msg =
tr("This sync would remove all the files in the sync folder '%1'.\n"
"This might be because the folder was silently reconfigured, or that all "
"the files were manually removed.\n"
"Are you sure you want to perform this operation?");
QString msg = dir == SyncFileItem::Down ?
tr("All files in the sync folder '%1' folder were deleted on the server.\n"
"These deletes will be synchronized to your local sync folder, making such files "
"unavailable unless you have a right to restore. \n"
"If you decide to keep the files, they will be re-synced with the server if you have rights to do so.\n"
"If you decide to delete the files, they will be unavailable to you, unless you are the owner.") :
tr("All the files in your local sync folder '%1' were deleted. These deletes will be "
"synchronized with your server, making such files unavailable unless restored.\n"
"Are you sure you want to sync those actions with the server?\n"
"If this was an accident and you decide to keep your files, they will be re-synced from the server.");
QMessageBox msgBox(QMessageBox::Warning, tr("Remove All Files?"),
msg.arg(shortGuiLocalPath()));
msgBox.addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
+3 -8
Ver Arquivo
@@ -280,17 +280,15 @@ private slots:
void slotFolderDiscovered(bool local, QString folderName);
void slotTransmissionProgress(const ProgressInfo& pi);
void slotItemCompleted(const SyncFileItem&, const PropagatorJob&);
void slotItemCompleted(const SyncFileItemPtr&);
void slotRunEtagJob();
void etagRetreived(const QString &);
void etagRetreivedFromSyncEngine(const QString &);
void slotThreadTreeWalkResult(const SyncFileItemVector& ); // after sync is done
void slotEmitFinishedDelayed();
void slotNewBigFolderDiscovered(const QString &);
void slotNewBigFolderDiscovered(const QString &, bool isExternal);
void slotLogPropagationStart();
@@ -302,7 +300,7 @@ private slots:
private:
bool setIgnoredFiles();
void bubbleUpSyncResult();
void showSyncResultPopup();
void checkLocalPath();
@@ -325,10 +323,7 @@ private:
SyncResult _syncResult;
QScopedPointer<SyncEngine> _engine;
QStringList _errors;
bool _csyncError;
bool _csyncUnavail;
bool _wipeDb;
bool _proxyDirty;
QPointer<RequestEtagJob> _requestEtagJob;
QString _lastEtag;
+8 -7
Ver Arquivo
@@ -23,6 +23,7 @@
#include "accountmanager.h"
#include "filesystem.h"
#include "lockwatcher.h"
#include "asserts.h"
#include <syncengine.h>
#ifdef Q_OS_MAC
@@ -48,7 +49,7 @@ FolderMan::FolderMan(QObject *parent) :
_lockWatcher(new LockWatcher),
_appRestartRequired(false)
{
Q_ASSERT(!_instance);
ASSERT(!_instance);
_instance = this;
_socketApi.reset(new SocketApi);
@@ -133,12 +134,13 @@ int FolderMan::unloadAndDeleteAllFolders()
delete f;
cnt++;
}
ASSERT(_folderMap.isEmpty());
_lastSyncFolder = 0;
_currentSyncFolder = 0;
_scheduledFolders.clear();
emit scheduleQueueChanged();
Q_ASSERT(_folderMap.count() == 0);
return cnt;
}
@@ -260,7 +262,6 @@ int FolderMan::setupFoldersMigration()
{
ConfigFile cfg;
QDir storageDir(cfg.configPath());
storageDir.mkpath(QLatin1String("folders"));
_folderConfigPath = cfg.configPath() + QLatin1String("folders");
qDebug() << "* Setup folders from " << _folderConfigPath << "(migration)";
@@ -463,7 +464,7 @@ void FolderMan::slotFolderSyncPaused( Folder *f, bool paused )
void FolderMan::slotFolderCanSyncChanged()
{
Folder *f = qobject_cast<Folder*>(sender());
Q_ASSERT(f);
ASSERT(f);
if (f->canSync()) {
_socketApi->slotRegisterPath(f->alias());
} else {
@@ -1121,7 +1122,7 @@ void FolderMan::setDirtyNetworkLimits()
SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
{
SyncResult overallResult(SyncResult::Undefined);
SyncResult overallResult;
int cnt = folders.count();
@@ -1235,10 +1236,10 @@ SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
return overallResult;
}
QString FolderMan::statusToString( SyncResult syncStatus, bool paused ) const
QString FolderMan::statusToString( SyncResult::Status syncStatus, bool paused ) const
{
QString folderMessage;
switch( syncStatus.status() ) {
switch( syncStatus ) {
case SyncResult::Undefined:
folderMessage = tr( "Undefined State." );
break;
+1 -1
Ver Arquivo
@@ -106,7 +106,7 @@ public:
/** Creates a new and empty local directory. */
bool startFromScratch( const QString& );
QString statusToString(SyncResult, bool paused ) const;
QString statusToString(SyncResult::Status, bool paused ) const;
static SyncResult accountStatus( const QList<Folder*> &folders );
+45 -23
Ver Arquivo
@@ -16,6 +16,7 @@
#include "folderman.h"
#include "accountstate.h"
#include "utility.h"
#include "asserts.h"
#include <theme.h>
#include <account.h>
#include "folderstatusdelegate.h"
@@ -29,6 +30,14 @@ Q_DECLARE_METATYPE(QPersistentModelIndex)
namespace OCC {
static const char propertyParentIndexC[] = "oc_parentIndex";
static const char propertyPermissionMap[] = "oc_permissionMap";
static QString removeTrailingSlash(const QString &s) {
if (s.endsWith('/')) {
return s.left(s.size() - 1);
}
return s;
}
FolderStatusModel::FolderStatusModel(QObject *parent)
:QAbstractItemModel(parent), _accountState(0), _dirty(false)
@@ -162,7 +171,7 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
case Qt::CheckStateRole:
return x._checked;
case Qt::DecorationRole:
return QFileIconProvider().icon(QFileIconProvider::Folder);
return QFileIconProvider().icon(x._isExternal ? QFileIconProvider::Network : QFileIconProvider::Folder);
case Qt::ForegroundRole:
if (x._isUndecided) {
return QColor(Qt::red);
@@ -368,6 +377,9 @@ FolderStatusModel::SubFolderInfo* FolderStatusModel::infoForIndex(const QModelIn
if (parentInfo->hasLabel()) {
return 0;
}
if (index.row() >= parentInfo->_subs.size()) {
return 0;
}
return &parentInfo->_subs[index.row()];
} else {
if (index.row() >= _folders.count()) {
@@ -469,14 +481,14 @@ QModelIndex FolderStatusModel::parent(const QModelIndex& child) const
}
auto pathIdx = static_cast<SubFolderInfo*>(child.internalPointer())->_pathIdx;
int i = 1;
Q_ASSERT(pathIdx.at(0) < _folders.count());
ASSERT(pathIdx.at(0) < _folders.count());
if (pathIdx.count() == 1) {
return createIndex(pathIdx.at(0), 0/*, nullptr*/);
}
const SubFolderInfo *info = &_folders[pathIdx.at(0)];
while (i < pathIdx.count() - 1) {
Q_ASSERT(pathIdx.at(i) < info->_subs.count());
ASSERT(pathIdx.at(i) < info->_subs.count());
info = &info->_subs[pathIdx.at(i)];
++i;
}
@@ -537,12 +549,15 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
path += info->_path;
}
LsColJob *job = new LsColJob(_accountState->account(), path, this);
job->setProperties(QList<QByteArray>() << "resourcetype" << "http://owncloud.org/ns:size");
job->setProperties(QList<QByteArray>() << "resourcetype" << "http://owncloud.org/ns:size" << "http://owncloud.org/ns:permissions");
job->setTimeout(60 * 1000);
connect(job, SIGNAL(directoryListingSubfolders(QStringList)),
SLOT(slotUpdateDirectories(QStringList)));
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
connect(job, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
this, SLOT(slotGatherPermissions(const QString&, const QMap<QString,QString>&)));
job->start();
QPersistentModelIndex persistentIndex(parent);
@@ -553,10 +568,24 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
QTimer::singleShot(1000, this, SLOT(slotShowFetchProgress()));
}
void FolderStatusModel::slotGatherPermissions(const QString &href, const QMap<QString,QString> &map)
{
auto it = map.find("permissions");
if (it == map.end())
return;
auto job = sender();
auto permissionMap = job->property(propertyPermissionMap).toMap();
job->setProperty(propertyPermissionMap, QVariant()); // avoid a detach of the map while it is modified
ASSERT(!href.endsWith(QLatin1Char('/')), "LsColXMLParser::parse should remove the trailing slash before calling us.");
permissionMap[href] = *it;
job->setProperty(propertyPermissionMap, permissionMap);
}
void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
{
auto job = qobject_cast<LsColJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
QModelIndex idx = qvariant_cast<QPersistentModelIndex>(job->property(propertyParentIndexC));
auto parentInfo = infoForIndex(idx);
if (!parentInfo) {
@@ -598,6 +627,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
selectiveSyncUndecidedSet.insert(str);
}
}
const auto permissionMap = job->property(propertyPermissionMap).toMap();
QStringList sortedSubfolders = list;
// skip the parent item (first in the list)
@@ -618,8 +648,8 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
newInfo._folder = parentInfo->_folder;
newInfo._pathIdx = parentInfo->_pathIdx;
newInfo._pathIdx << newSubs.size();
auto size = job ? job->_sizes.value(path) : 0;
newInfo._size = size;
newInfo._size = job->_sizes.value(path);
newInfo._isExternal = permissionMap.value(removeTrailingSlash(path)).toString().contains("M");
newInfo._path = relativePath;
newInfo._name = relativePath.split('/', QString::SkipEmptyParts).last();
@@ -686,7 +716,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
{
auto job = qobject_cast<LsColJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
QModelIndex idx = qvariant_cast<QPersistentModelIndex>(job->property(propertyParentIndexC));
if (!idx.isValid()) {
return;
@@ -701,7 +731,7 @@ void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
if (r->error() == QNetworkReply::ContentNotFoundError) {
parentInfo->_fetched = true;
} else {
Q_ASSERT(!parentInfo->hasLabel());
ASSERT(!parentInfo->hasLabel());
beginInsertRows(idx, 0, 0);
parentInfo->_hasError = true;
endInsertRows();
@@ -981,7 +1011,7 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
auto& pi = _folders[folderIndex]._progress;
SyncResult::Status state = f->syncResult().status();
if (f->syncPaused()) {
if (!f->canSync()) {
// Reset progress info.
pi = SubFolderInfo::Progress();
} else if (state == SyncResult::NotYetStarted) {
@@ -1012,18 +1042,10 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
// update the icon etc. now
slotUpdateFolderState(f);
if (state == SyncResult::Success) {
foreach (const SyncFileItemPtr &i, f->syncResult().syncFileItemVector()) {
if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW
|| i->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE
|| i->_instruction == CSYNC_INSTRUCTION_RENAME)) {
// There is a new or a removed folder. reset all data
auto & info = _folders[folderIndex];
info.resetSubs(this, index(folderIndex));
return;
}
}
if (state == SyncResult::Success && f->syncResult().folderStructureWasChanged()) {
// There is a new or a removed folder. reset all data
auto & info = _folders[folderIndex];
info.resetSubs(this, index(folderIndex));
}
}
@@ -1114,7 +1136,7 @@ void FolderStatusModel::slotSyncNoPendingBigFolders()
void FolderStatusModel::slotNewBigFolder()
{
auto f = qobject_cast<Folder *>(sender());
Q_ASSERT(f);
ASSERT(f);
int folderIndex = -1;
for (int i = 0; i < _folders.count(); ++i) {
+3 -1
Ver Arquivo
@@ -51,7 +51,7 @@ public:
struct SubFolderInfo {
SubFolderInfo()
: _folder(0), _size(0), _fetched(false), _fetching(false),
: _folder(0), _size(0), _isExternal(false), _fetched(false), _fetching(false),
_hasError(false), _fetchingLabel(false), _isUndecided(false), _checked(Qt::Checked) {}
Folder *_folder;
QString _name;
@@ -59,6 +59,7 @@ public:
QVector<int> _pathIdx;
QVector<SubFolderInfo> _subs;
qint64 _size;
bool _isExternal;
bool _fetched; // If we did the LSCOL for this folder already
bool _fetching; // Whether a LSCOL job is currently running
@@ -113,6 +114,7 @@ public slots:
private slots:
void slotUpdateDirectories(const QStringList &);
void slotGatherPermissions(const QString &name, const QMap<QString,QString> &properties);
void slotLscolFinishedWithError(QNetworkReply *r);
void slotFolderSyncStateChange(Folder* f);
void slotFolderScheduleQueueChanged();
+9 -6
Ver Arquivo
@@ -482,9 +482,8 @@ void FolderWizardRemotePath::showWarn( const QString& msg ) const
FolderWizardSelectiveSync::FolderWizardSelectiveSync(const AccountPtr& account)
{
QVBoxLayout *layout = new QVBoxLayout(this);
_treeView = new SelectiveSyncTreeView(account, this);
layout->addWidget(new QLabel(tr("Choose What to Sync: You can optionally deselect remote subfolders you do not wish to synchronize.")));
layout->addWidget(_treeView);
_selectiveSync = new SelectiveSyncWidget(account, this);
layout->addWidget(_selectiveSync);
}
FolderWizardSelectiveSync::~FolderWizardSelectiveSync()
@@ -501,13 +500,17 @@ void FolderWizardSelectiveSync::initializePage()
QString alias = QFileInfo(targetPath).fileName();
if (alias.isEmpty())
alias = Theme::instance()->appName();
_treeView->setFolderInfo(targetPath, alias);
QStringList initialBlacklist;
if (Theme::instance()->wizardSelectiveSyncDefaultNothing()) {
initialBlacklist = QStringList("/");
}
_selectiveSync->setFolderInfo(targetPath, alias, initialBlacklist);
QWizardPage::initializePage();
}
bool FolderWizardSelectiveSync::validatePage()
{
wizard()->setProperty("selectiveSyncBlackList", QVariant(_treeView->createBlackList()));
wizard()->setProperty("selectiveSyncBlackList", QVariant(_selectiveSync->createBlackList()));
return true;
}
@@ -517,7 +520,7 @@ void FolderWizardSelectiveSync::cleanupPage()
QString alias = QFileInfo(targetPath).fileName();
if (alias.isEmpty())
alias = Theme::instance()->appName();
_treeView->setFolderInfo(targetPath, alias);
_selectiveSync->setFolderInfo(targetPath, alias);
QWizardPage::cleanupPage();
}
+2 -2
Ver Arquivo
@@ -27,7 +27,7 @@
namespace OCC {
class SelectiveSyncTreeView;
class SelectiveSyncWidget;
class ownCloudInfo;
@@ -127,7 +127,7 @@ public:
virtual void cleanupPage() Q_DECL_OVERRIDE;
private:
SelectiveSyncTreeView *_treeView;
SelectiveSyncWidget *_selectiveSync;
};
+18 -1
Ver Arquivo
@@ -32,12 +32,14 @@
#include <QNetworkProxy>
#include <QDir>
#include <QScopedValueRollback>
namespace OCC {
GeneralSettings::GeneralSettings(QWidget *parent) :
QWidget(parent),
_ui(new Ui::GeneralSettings)
_ui(new Ui::GeneralSettings),
_currentlyLoading(false)
{
_ui->setupUi(this);
@@ -66,6 +68,7 @@ GeneralSettings::GeneralSettings(QWidget *parent) :
connect(_ui->crashreporterCheckBox, SIGNAL(toggled(bool)), SLOT(saveMiscSettings()));
connect(_ui->newFolderLimitCheckBox, SIGNAL(toggled(bool)), SLOT(saveMiscSettings()));
connect(_ui->newFolderLimitSpinBox, SIGNAL(valueChanged(int)), SLOT(saveMiscSettings()));
connect(_ui->newExternalStorage, SIGNAL(toggled(bool)), SLOT(saveMiscSettings()));
#ifndef WITH_CRASHREPORTER
_ui->crashreporterCheckBox->setVisible(false);
@@ -85,6 +88,9 @@ GeneralSettings::GeneralSettings(QWidget *parent) :
_ui->monoIconsCheckBox->setVisible(QDir(themeDir).exists());
connect(_ui->ignoredFilesButton, SIGNAL(clicked()), SLOT(slotIgnoreFilesEditor()));
// accountAdded means the wizard was finished and the wizard might change some options.
connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState*)), this, SLOT(loadMiscSettings()));
}
GeneralSettings::~GeneralSettings()
@@ -99,6 +105,12 @@ QSize GeneralSettings::sizeHint() const {
void GeneralSettings::loadMiscSettings()
{
#if QT_VERSION < QT_VERSION_CHECK( 5, 4, 0 )
QScopedValueRollback<bool> scope(_currentlyLoading);
_currentlyLoading = true;
#else
QScopedValueRollback<bool> scope(_currentlyLoading, true);
#endif
ConfigFile cfgFile;
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
_ui->desktopNotificationsCheckBox->setChecked(cfgFile.optionalDesktopNotifications());
@@ -106,6 +118,8 @@ void GeneralSettings::loadMiscSettings()
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
_ui->newFolderLimitCheckBox->setChecked(newFolderLimit.first);
_ui->newFolderLimitSpinBox->setValue(newFolderLimit.second);
_ui->newExternalStorage->setChecked(cfgFile.confirmExternalStorage());
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
}
void GeneralSettings::slotUpdateInfo()
@@ -130,6 +144,8 @@ void GeneralSettings::slotUpdateInfo()
void GeneralSettings::saveMiscSettings()
{
if (_currentlyLoading)
return;
ConfigFile cfgFile;
bool isChecked = _ui->monoIconsCheckBox->isChecked();
cfgFile.setMonoIcons(isChecked);
@@ -138,6 +154,7 @@ void GeneralSettings::saveMiscSettings()
cfgFile.setNewBigFolderSizeLimit(_ui->newFolderLimitCheckBox->isChecked(),
_ui->newFolderLimitSpinBox->value());
cfgFile.setConfirmExternalStorage(_ui->newExternalStorage->isChecked());
}
void GeneralSettings::slotToggleLaunchOnStartup(bool enable)
+2 -1
Ver Arquivo
@@ -45,13 +45,14 @@ private slots:
void slotToggleOptionalDesktopNotifications(bool);
void slotUpdateInfo();
void slotIgnoreFilesEditor();
void loadMiscSettings();
private:
void loadMiscSettings();
Ui::GeneralSettings *_ui;
QPointer<IgnoreListEditor> _ignoreEditor;
QPointer<SyncLogDialog> _syncLogDialog;
bool _currentlyLoading;
};
+36 -42
Ver Arquivo
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>706</width>
<width>785</width>
<height>523</height>
</rect>
</property>
@@ -52,33 +52,37 @@
<property name="title">
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QPushButton" name="ignoredFilesButton">
<property name="text">
<string>Edit &amp;Ignored Files</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="ignoredFilesButton">
<property name="text">
<string>Edit &amp;Ignored Files</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>555</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="1" colspan="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>555</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="3">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="newFolderLimitCheckBox">
<property name="text">
<string>Ask &amp;confirmation before downloading folders larger than</string>
<string>Ask for confirmation before synchronizing folders larger than</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -98,7 +102,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>MB</string>
<string extracomment="Trailing part of &quot;Ask confirmation before syncing folder larger than&quot; ">MB</string>
</property>
</widget>
</item>
@@ -117,7 +121,14 @@
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<item>
<widget class="QCheckBox" name="newExternalStorage">
<property name="text">
<string>Ask for confirmation before synchronizing external storages</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="crashreporterCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
@@ -130,23 +141,6 @@
</property>
</widget>
</item>
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
+2 -1
Ver Arquivo
@@ -15,6 +15,7 @@
#include "notificationwidget.h"
#include "QProgressIndicator.h"
#include "utility.h"
#include "asserts.h"
#include <QPushButton>
@@ -33,8 +34,8 @@ void NotificationWidget::setActivity(const Activity& activity)
{
_myActivity = activity;
Q_ASSERT( !activity._accName.isEmpty() );
_accountName = activity._accName;
ASSERT(!_accountName.isEmpty());
// _ui._headerLabel->setText( );
_ui._subjectLabel->setVisible( !activity._subject.isEmpty() );
+4 -2
Ver Arquivo
@@ -533,9 +533,11 @@ void OwncloudSetupWizard::slotAssistantFinished( int result )
if (f) {
f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
_ocWizard->selectiveSyncBlacklist());
// The user already accepted the selective sync dialog. everything is in the white list
f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList,
if (!_ocWizard->isConfirmBigFolderChecked()) {
// The user already accepted the selective sync dialog. everything is in the white list
f->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList,
QStringList() << QLatin1String("/"));
}
}
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
}
+5 -10
Ver Arquivo
@@ -27,7 +27,6 @@
#include "syncfileitem.h"
#include "folder.h"
#include "openfilemanager.h"
#include "owncloudpropagator.h"
#include "activityitemdelegate.h"
#include "ui_protocolwidget.h"
@@ -45,8 +44,8 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) :
connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString,ProgressInfo)),
this, SLOT(slotProgressInfo(QString,ProgressInfo)));
connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString,SyncFileItem,PropagatorJob)),
this, SLOT(slotItemCompleted(QString,SyncFileItem,PropagatorJob)));
connect(ProgressDispatcher::instance(), SIGNAL(itemCompleted(QString,SyncFileItemPtr)),
this, SLOT(slotItemCompleted(QString,SyncFileItemPtr)));
connect(_ui->_treeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SLOT(slotOpenFile(QTreeWidgetItem*,int)));
@@ -222,15 +221,11 @@ void ProtocolWidget::slotProgressInfo( const QString& folder, const ProgressInfo
}
}
void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItem &item, const PropagatorJob &job)
void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItemPtr &item)
{
if (qobject_cast<const PropagateDirectory*>(&job)) {
return;
}
QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, item);
QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, *item);
if(line) {
if( item.hasErrorStatus() ) {
if( item->hasErrorStatus() ) {
_issueItemView->insertTopLevelItem(0, line);
emit issueItemCountUpdated(_issueItemView->topLevelItemCount());
} else {
+1 -1
Ver Arquivo
@@ -52,7 +52,7 @@ public:
public slots:
void slotProgressInfo( const QString& folder, const ProgressInfo& progress );
void slotItemCompleted( const QString& folder, const SyncFileItem& item, const PropagatorJob& job);
void slotItemCompleted( const QString& folder, const SyncFileItemPtr& item);
void slotOpenFile( QTreeWidgetItem* item, int );
protected:
+71 -55
Ver Arquivo
@@ -18,6 +18,7 @@
#include "networkjobs.h"
#include "theme.h"
#include "folderman.h"
#include "configfile.h"
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include <QTreeWidget>
@@ -29,6 +30,7 @@
#include <QScopedValueRollback>
#include <QTreeWidgetItem>
#include <QLabel>
#include <QVBoxLayout>
namespace OCC {
@@ -54,32 +56,48 @@ private:
}
};
SelectiveSyncTreeView::SelectiveSyncTreeView(AccountPtr account, QWidget* parent)
: QTreeWidget(parent), _inserting(false), _account(account)
SelectiveSyncWidget::SelectiveSyncWidget(AccountPtr account, QWidget *parent)
: QWidget(parent)
, _account(account)
, _inserting(false)
, _folderTree(new QTreeWidget(this))
{
_loading = new QLabel(tr("Loading ..."), this);
connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(slotItemExpanded(QTreeWidgetItem*)));
connect(this, SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(slotItemChanged(QTreeWidgetItem*,int)));
setSortingEnabled(true);
sortByColumn(0, Qt::AscendingOrder);
setColumnCount(2);
_loading = new QLabel(tr("Loading ..."), _folderTree);
auto layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
auto header = new QLabel(this);
header->setText(tr("Deselect remote folders you do not wish to synchronize."));
header->setWordWrap(true);
layout->addWidget(header);
layout->addWidget(_folderTree);
connect(_folderTree, SIGNAL(itemExpanded(QTreeWidgetItem*)),
SLOT(slotItemExpanded(QTreeWidgetItem*)));
connect(_folderTree, SIGNAL(itemChanged(QTreeWidgetItem*,int)),
SLOT(slotItemChanged(QTreeWidgetItem*,int)));
_folderTree->setSortingEnabled(true);
_folderTree->sortByColumn(0, Qt::AscendingOrder);
_folderTree->setColumnCount(2);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
header()->setSectionResizeMode(0, QHeaderView::QHeaderView::ResizeToContents);
header()->setSectionResizeMode(1, QHeaderView::QHeaderView::ResizeToContents);
_folderTree->header()->setSectionResizeMode(0, QHeaderView::QHeaderView::ResizeToContents);
_folderTree->header()->setSectionResizeMode(1, QHeaderView::QHeaderView::ResizeToContents);
#else
header()->resizeSection(0, sizeHint().width()/2);
_folderTree->header()->resizeSection(0, sizeHint().width()/2);
#endif
header()->setStretchLastSection(true);
headerItem()->setText(0, tr("Name"));
headerItem()->setText(1, tr("Size"));
_folderTree->header()->setStretchLastSection(true);
_folderTree->headerItem()->setText(0, tr("Name"));
_folderTree->headerItem()->setText(1, tr("Size"));
}
QSize SelectiveSyncTreeView::sizeHint() const
QSize SelectiveSyncWidget::sizeHint() const
{
return QTreeView::sizeHint().expandedTo(QSize(400, 400));
return QWidget::sizeHint().expandedTo(QSize(600, 600));
}
void SelectiveSyncTreeView::refreshFolders()
void SelectiveSyncWidget::refreshFolders()
{
LsColJob *job = new LsColJob(_account, _folderPath, this);
job->setProperties(QList<QByteArray>() << "resourcetype" << "http://owncloud.org/ns:size");
@@ -88,12 +106,12 @@ void SelectiveSyncTreeView::refreshFolders()
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
job->start();
clear();
_folderTree->clear();
_loading->show();
_loading->move(10,header()->height() + 10);
_loading->move(10, _folderTree->header()->height() + 10);
}
void SelectiveSyncTreeView::setFolderInfo(const QString& folderPath, const QString& rootName, const QStringList& oldBlackList)
void SelectiveSyncWidget::setFolderInfo(const QString& folderPath, const QString& rootName, const QStringList& oldBlackList)
{
_folderPath = folderPath;
if (_folderPath.startsWith(QLatin1Char('/'))) {
@@ -116,7 +134,7 @@ static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& t
return 0;
}
void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size)
void SelectiveSyncWidget::recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size)
{
QFileIconProvider prov;
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
@@ -159,13 +177,13 @@ void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList
}
}
void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
void SelectiveSyncWidget::slotUpdateDirectories(QStringList list)
{
auto job = qobject_cast<LsColJob *>(sender());
QScopedValueRollback<bool> isInserting(_inserting);
_inserting = true;
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(topLevelItem(0));
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(_folderTree->topLevelItem(0));
QUrl url = _account->davUrl();
QString pathToRemove = url.path();
@@ -206,15 +224,11 @@ void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
}
if (!root) {
root = new SelectiveSyncTreeViewItem(this);
root = new SelectiveSyncTreeViewItem(_folderTree);
root->setText(0, _rootName);
root->setIcon(0, Theme::instance()->applicationIcon());
root->setData(0, Qt::UserRole, QString());
if (_oldBlackList.isEmpty()) {
root->setCheckState(0, Qt::Checked);
} else {
root->setCheckState(0, Qt::PartiallyChecked);
}
root->setCheckState(0, Qt::Checked);
qint64 size = job ? job->_sizes.value(pathToRemove, -1) : -1;
if (size >= 0) {
root->setText(1, Utility::octetsToString(size));
@@ -236,10 +250,19 @@ void SelectiveSyncTreeView::slotUpdateDirectories(QStringList list)
recursiveInsert(root, paths, path, size);
}
// Root is partially checked if any children are not checked
for (int i = 0; i < root->childCount(); ++i) {
const auto child = root->child(i);
if (child->checkState(0) != Qt::Checked) {
root->setCheckState(0, Qt::PartiallyChecked);
break;
}
}
root->setExpanded(true);
}
void SelectiveSyncTreeView::slotLscolFinishedWithError(QNetworkReply *r)
void SelectiveSyncWidget::slotLscolFinishedWithError(QNetworkReply *r)
{
if (r->error() == QNetworkReply::ContentNotFoundError) {
_loading->setText(tr("No subfolders currently on the server."));
@@ -249,7 +272,7 @@ void SelectiveSyncTreeView::slotLscolFinishedWithError(QNetworkReply *r)
_loading->resize(_loading->sizeHint()); // because it's not in a layout
}
void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
void SelectiveSyncWidget::slotItemExpanded(QTreeWidgetItem *item)
{
QString dir = item->data(0, Qt::UserRole).toString();
if (dir.isEmpty()) return;
@@ -264,7 +287,7 @@ void SelectiveSyncTreeView::slotItemExpanded(QTreeWidgetItem *item)
job->start();
}
void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
void SelectiveSyncWidget::slotItemChanged(QTreeWidgetItem *item, int col)
{
if (col != 0 || _inserting)
return;
@@ -322,10 +345,10 @@ void SelectiveSyncTreeView::slotItemChanged(QTreeWidgetItem *item, int col)
}
}
QStringList SelectiveSyncTreeView::createBlackList(QTreeWidgetItem* root) const
QStringList SelectiveSyncWidget::createBlackList(QTreeWidgetItem* root) const
{
if (!root) {
root = topLevelItem(0);
root = _folderTree->topLevelItem(0);
}
if (!root) return QStringList();
@@ -354,15 +377,15 @@ QStringList SelectiveSyncTreeView::createBlackList(QTreeWidgetItem* root) const
return result;
}
QStringList SelectiveSyncTreeView::oldBlackList() const
QStringList SelectiveSyncWidget::oldBlackList() const
{
return _oldBlackList;
}
qint64 SelectiveSyncTreeView::estimatedSize(QTreeWidgetItem* root)
qint64 SelectiveSyncWidget::estimatedSize(QTreeWidgetItem* root)
{
if (!root) {
root = topLevelItem(0);
root = _folderTree->topLevelItem(0);
}
if (!root) return -1;
@@ -396,10 +419,10 @@ SelectiveSyncDialog::SelectiveSyncDialog(AccountPtr account, Folder* folder, QWi
_okButton(0) // defined in init()
{
bool ok;
init(account, tr("Unchecked folders will be <b>removed</b> from your local file system and will not be synchronized to this computer anymore"));
init(account);
QStringList selectiveSyncList = _folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &ok);
if( ok ) {
_treeView->setFolderInfo(_folder->remotePath(), _folder->alias(),selectiveSyncList);
_selectiveSync->setFolderInfo(_folder->remotePath(), _folder->alias(),selectiveSyncList);
} else {
_okButton->setEnabled(false);
}
@@ -411,22 +434,16 @@ SelectiveSyncDialog::SelectiveSyncDialog(AccountPtr account, const QString &fold
const QStringList& blacklist, QWidget* parent, Qt::WindowFlags f)
: QDialog(parent, f), _folder(0)
{
init(account,
Theme::instance()->wizardSelectiveSyncDefaultNothing() ?
tr("Choose What to Sync: Select remote subfolders you wish to synchronize.") :
tr("Choose What to Sync: Deselect remote subfolders you do not wish to synchronize."));
_treeView->setFolderInfo(folder, folder, blacklist);
init(account);
_selectiveSync->setFolderInfo(folder, folder, blacklist);
}
void SelectiveSyncDialog::init(const AccountPtr &account, const QString &labelText)
void SelectiveSyncDialog::init(const AccountPtr &account)
{
setWindowTitle(tr("Choose What to Sync"));
QVBoxLayout *layout = new QVBoxLayout(this);
_treeView = new SelectiveSyncTreeView(account, this);
auto label = new QLabel(labelText);
label->setWordWrap(true);
layout->addWidget(label);
layout->addWidget(_treeView);
_selectiveSync = new SelectiveSyncWidget(account, this);
layout->addWidget(_selectiveSync);
QDialogButtonBox *buttonBox = new QDialogButtonBox(Qt::Horizontal);
_okButton = buttonBox->addButton(QDialogButtonBox::Ok);
connect(_okButton, SIGNAL(clicked()), this, SLOT(accept()));
@@ -444,7 +461,7 @@ void SelectiveSyncDialog::accept()
if( ! ok ) {
return;
}
QStringList blackList = _treeView->createBlackList();
QStringList blackList = _selectiveSync->createBlackList();
_folder->journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, blackList);
FolderMan *folderMan = FolderMan::instance();
@@ -467,19 +484,18 @@ void SelectiveSyncDialog::accept()
QStringList SelectiveSyncDialog::createBlackList() const
{
return _treeView->createBlackList();
return _selectiveSync->createBlackList();
}
QStringList SelectiveSyncDialog::oldBlackList() const
{
return _treeView->oldBlackList();
return _selectiveSync->oldBlackList();
}
qint64 SelectiveSyncDialog::estimatedSize()
{
return _treeView->estimatedSize();
return _selectiveSync->estimatedSize();
}
}
+17 -7
Ver Arquivo
@@ -26,40 +26,50 @@ namespace OCC {
class Folder;
/**
* @brief The SelectiveSyncTreeView class
* @brief The SelectiveSyncWidget contains a folder tree with labels
* @ingroup gui
*/
class SelectiveSyncTreeView : public QTreeWidget {
class SelectiveSyncWidget : public QWidget {
Q_OBJECT
public:
explicit SelectiveSyncTreeView(AccountPtr account, QWidget* parent = 0);
explicit SelectiveSyncWidget(AccountPtr account, QWidget* parent = 0);
/// Returns a list of blacklisted paths, each including the trailing /
QStringList createBlackList(QTreeWidgetItem* root = 0) const;
/** Returns the oldBlackList passed into setFolderInfo(), except that
* a "/" entry is expanded to all top-level folder names.
*/
QStringList oldBlackList() const;
// Estimates the total size of checked items (recursively)
qint64 estimatedSize(QTreeWidgetItem *root = 0);
void refreshFolders();
// oldBlackList is a list of excluded paths, each including a trailing /
void setFolderInfo(const QString &folderPath, const QString &rootName,
const QStringList &oldBlackList = QStringList());
QSize sizeHint() const Q_DECL_OVERRIDE;
private slots:
void slotUpdateDirectories(QStringList);
void slotItemExpanded(QTreeWidgetItem *);
void slotItemChanged(QTreeWidgetItem*,int);
void slotLscolFinishedWithError(QNetworkReply*);
private:
void refreshFolders();
void recursiveInsert(QTreeWidgetItem* parent, QStringList pathTrail, QString path, qint64 size);
AccountPtr _account;
QString _folderPath;
QString _rootName;
QStringList _oldBlackList;
bool _inserting; // set to true when we are inserting new items on the list
AccountPtr _account;
QLabel *_loading;
QTreeWidget *_folderTree;
};
/**
@@ -85,9 +95,9 @@ public:
private:
void init(const AccountPtr &account, const QString &label);
void init(const AccountPtr &account);
SelectiveSyncTreeView *_treeView;
SelectiveSyncWidget *_selectiveSync;
Folder *_folder;
QPushButton *_okButton;
+1 -1
Ver Arquivo
@@ -229,7 +229,7 @@ void ShareLinkWidget::slotSharesFetched(const QList<QSharedPointer<Share>> &shar
Q_FOREACH(auto share, shares) {
if (share->getShareType() == Share::TypeLink) {
_share = qSharedPointerObjectCast<LinkShare>(share);
_share = qSharedPointerDynamicCast<LinkShare>(share);
_ui->pushButton_copy->show();
_ui->pushButton_mail->show();
+5 -7
Ver Arquivo
@@ -31,6 +31,7 @@
#include "accountstate.h"
#include "account.h"
#include "capabilities.h"
#include "asserts.h"
#include <QBitArray>
#include <QDebug>
@@ -60,9 +61,6 @@
#define DEBUG qDebug() << "SocketApi: "
Q_DECLARE_METATYPE(OCC::SocketListener)
static inline QString removeTrailingSlash(QString path)
{
Q_ASSERT(path.endsWith(QLatin1Char('/')));
@@ -113,7 +111,7 @@ class SocketListener {
public:
QIODevice* socket;
SocketListener(QIODevice* socket = nullptr) : socket(socket) { }
SocketListener(QIODevice* socket = 0) : socket(socket) { }
void sendMessage(const QString& message, bool doWait = false) const
{
@@ -216,7 +214,7 @@ SocketApi::~SocketApi()
DEBUG << "dtor";
_localServer.close();
// All remaining sockets will be destroyed with _localServer, their parent
Q_ASSERT(_listeners.isEmpty() || _listeners.first().socket->parent() == &_localServer);
ASSERT(_listeners.isEmpty() || _listeners.first().socket->parent() == &_localServer);
_listeners.clear();
}
@@ -231,7 +229,7 @@ void SocketApi::slotNewConnection()
connect(socket, SIGNAL(readyRead()), this, SLOT(slotReadSocket()));
connect(socket, SIGNAL(disconnected()), this, SLOT(onLostConnection()));
connect(socket, SIGNAL(destroyed(QObject*)), this, SLOT(slotSocketDestroyed(QObject*)));
Q_ASSERT(socket->readAll().isEmpty());
ASSERT(socket->readAll().isEmpty());
_listeners.append(SocketListener(socket));
SocketListener &listener = _listeners.last();
@@ -259,7 +257,7 @@ void SocketApi::slotSocketDestroyed(QObject* obj)
void SocketApi::slotReadSocket()
{
QIODevice* socket = qobject_cast<QIODevice*>(sender());
Q_ASSERT(socket);
ASSERT(socket);
SocketListener *listener = &*std::find_if(_listeners.begin(), _listeners.end(), ListenerHasSocketPred(socket));
while(socket->canReadLine()) {
+7 -7
Ver Arquivo
@@ -145,18 +145,18 @@ void SyncRunFileLog::logItem( const SyncFileItem& item )
const QChar L = QLatin1Char('|');
_out << ts << L;
_out << QString::number(item._requestDuration) << L;
if( item.log._instruction != CSYNC_INSTRUCTION_RENAME ) {
_out << L;
if( item._instruction != CSYNC_INSTRUCTION_RENAME ) {
_out << item._file << L;
} else {
_out << item._file << QLatin1String(" -> ") << item._renameTarget << L;
}
_out << instructionToStr( item.log._instruction ) << L;
_out << instructionToStr( item._instruction ) << L;
_out << directionToStr( item._direction ) << L;
_out << QString::number(item.log._modtime) << L;
_out << item.log._etag << L;
_out << QString::number(item.log._size) << L;
_out << item.log._fileId << L;
_out << QString::number(item._modtime) << L;
_out << item._etag << L;
_out << QString::number(item._size) << L;
_out << item._fileId << L;
_out << item._status << L;
_out << item._errorString << L;
_out << QString::number(item._httpErrorCode) << L;
+27
Ver Arquivo
@@ -68,6 +68,15 @@ OwncloudAdvancedSetupPage::OwncloudAdvancedSetupPage()
_ui.lServerIcon->setPixmap(appIcon.pixmap(48));
_ui.lLocalIcon->setText(QString());
_ui.lLocalIcon->setPixmap(QPixmap(Theme::hidpiFileName(":/client/resources/folder-sync.png")));
if (theme->wizardHideExternalStorageConfirmationCheckbox()) {
_ui.confCheckBoxExternal->hide();
}
if (theme->wizardHideFolderSizeLimitCheckbox()) {
_ui.confCheckBoxSize->hide();
_ui.confSpinBox->hide();
_ui.confTraillingSizeLabel->hide();
}
}
void OwncloudAdvancedSetupPage::setupCustomization()
@@ -118,6 +127,12 @@ void OwncloudAdvancedSetupPage::initializePage()
_selectiveSyncBlacklist = QStringList("/");
QTimer::singleShot(0, this, SLOT(slotSelectiveSyncClicked()));
}
ConfigFile cfgFile;
auto newFolderLimit = cfgFile.newBigFolderSizeLimit();
_ui.confCheckBoxSize->setChecked(newFolderLimit.first);
_ui.confSpinBox->setValue(newFolderLimit.second);
_ui.confCheckBoxExternal->setChecked(cfgFile.confirmExternalStorage());
}
// Called if the user changes the user- or url field. Adjust the texts and
@@ -200,6 +215,11 @@ QStringList OwncloudAdvancedSetupPage::selectiveSyncBlacklist() const
return _selectiveSyncBlacklist;
}
bool OwncloudAdvancedSetupPage::isConfirmBigFolderChecked() const
{
return _ui.rSyncEverything->isChecked() && _ui.confCheckBoxSize->isChecked();
}
bool OwncloudAdvancedSetupPage::validatePage()
{
if(!_created) {
@@ -208,6 +228,13 @@ bool OwncloudAdvancedSetupPage::validatePage()
startSpinner();
emit completeChanged();
if (_ui.rSyncEverything->isChecked()) {
ConfigFile cfgFile;
cfgFile.setNewBigFolderSizeLimit(_ui.confCheckBoxSize->isChecked(),
_ui.confSpinBox->value());
cfgFile.setConfirmExternalStorage(_ui.confCheckBoxExternal->isChecked());
}
emit createLocalAndRemoteFolders(localFolder(), _remoteFolder);
return false;
} else {
+1
Ver Arquivo
@@ -41,6 +41,7 @@ public:
bool validatePage() Q_DECL_OVERRIDE;
QString localFolder() const;
QStringList selectiveSyncBlacklist() const;
bool isConfirmBigFolderChecked() const;
void setRemoteFolder( const QString& remoteFolder);
void setMultipleFoldersExist( bool exist );
void directoriesCreated();
+133 -7
Ver Arquivo
@@ -6,12 +6,12 @@
<rect>
<x>0</x>
<y>0</y>
<width>917</width>
<height>493</height>
<width>912</width>
<height>633</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
@@ -226,11 +226,14 @@
<item row="0" column="1" colspan="2">
<widget class="QWidget" name="widget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="rSyncEverything">
<property name="text">
@@ -263,6 +266,64 @@
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<property name="horizontalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="confCheckBoxSize">
<property name="text">
<string>Ask for confirmation before synchroni&amp;zing folders larger than</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="confSpinBox">
<property name="maximum">
<number>999999</number>
</property>
<property name="value">
<number>99</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="confTraillingSizeLabel">
<property name="text">
<string extracomment="Trailing part of &quot;Ask confirmation before syncing folder larger than&quot; ">MB</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="confCheckBoxExternal">
<property name="text">
<string>Ask for confirmation before synchronizing e&amp;xternal storages</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
@@ -345,5 +406,70 @@
</layout>
</widget>
<resources/>
<connections/>
<connections>
<connection>
<sender>rSyncEverything</sender>
<signal>toggled(bool)</signal>
<receiver>confCheckBoxSize</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>217</x>
<y>78</y>
</hint>
<hint type="destinationlabel">
<x>298</x>
<y>126</y>
</hint>
</hints>
</connection>
<connection>
<sender>rSyncEverything</sender>
<signal>toggled(bool)</signal>
<receiver>confSpinBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>311</x>
<y>83</y>
</hint>
<hint type="destinationlabel">
<x>952</x>
<y>134</y>
</hint>
</hints>
</connection>
<connection>
<sender>rSyncEverything</sender>
<signal>toggled(bool)</signal>
<receiver>confTraillingSizeLabel</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>277</x>
<y>76</y>
</hint>
<hint type="destinationlabel">
<x>1076</x>
<y>136</y>
</hint>
</hints>
</connection>
<connection>
<sender>rSyncEverything</sender>
<signal>toggled(bool)</signal>
<receiver>confCheckBoxExternal</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>181</x>
<y>78</y>
</hint>
<hint type="destinationlabel">
<x>382</x>
<y>174</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+7 -4
Ver Arquivo
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>602</width>
<height>193</height>
<width>506</width>
<height>515</height>
</rect>
</property>
<property name="sizePolicy">
@@ -80,7 +80,7 @@
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Server &amp;Address</string>
<string>Ser&amp;ver Address</string>
</property>
<property name="buddy">
<cstring>leUrl</cstring>
@@ -166,10 +166,13 @@
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<height>200</height>
</size>
</property>
</spacer>
@@ -44,6 +44,7 @@ void OwncloudShibbolethCredsPage::setupBrowser()
// i.e. if someone presses "back"
QNetworkAccessManager *qnam = account->networkAccessManager();
CookieJar *jar = new CookieJar;
jar->restore(account->cookieJarPath());
// Implicitly deletes the old cookie jar, and reparents the jar
qnam->setCookieJar(jar);
+4 -1
Ver Arquivo
@@ -86,7 +86,6 @@ OwncloudWizard::OwncloudWizard(QWidget *parent)
setTitleFormat(Qt::RichText);
setSubTitleFormat(Qt::RichText);
setButtonText(QWizard::CustomButton1, tr("Skip folders configuration"));
}
void OwncloudWizard::setAccount(AccountPtr account)
@@ -109,6 +108,10 @@ QStringList OwncloudWizard::selectiveSyncBlacklist() const
return _advancedSetupPage->selectiveSyncBlacklist();
}
bool OwncloudWizard::isConfirmBigFolderChecked() const
{
return _advancedSetupPage->isConfirmBigFolderChecked();
}
QString OwncloudWizard::ocUrl() const
{
+1
Ver Arquivo
@@ -59,6 +59,7 @@ public:
QString ocUrl() const;
QString localFolder() const;
QStringList selectiveSyncBlacklist() const;
bool isConfirmBigFolderChecked() const;
void enableFinishOnResultWidget(bool enable);
-2
Ver Arquivo
@@ -1,8 +1,6 @@
project(libsync)
set(CMAKE_AUTOMOC TRUE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
configure_file( version.h.in "${CMAKE_CURRENT_BINARY_DIR}/version.h" )
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
-8
Ver Arquivo
@@ -172,7 +172,6 @@ void AbstractNetworkJob::slotFinished()
// get the Date timestamp from reply
_responseTimestamp = _reply->rawHeader("Date");
_duration = _durationTimer.elapsed();
if (_followRedirects) {
// ### the qWarnings here should be exported via displayErrors() so they
@@ -206,11 +205,6 @@ void AbstractNetworkJob::slotFinished()
}
}
quint64 AbstractNetworkJob::duration()
{
return _duration;
}
QByteArray AbstractNetworkJob::responseTimestamp()
{
return _responseTimestamp;
@@ -224,8 +218,6 @@ AbstractNetworkJob::~AbstractNetworkJob()
void AbstractNetworkJob::start()
{
_timer.start();
_durationTimer.start();
_duration = 0;
const QUrl url = account()->url();
const QString displayUrl = QString( "%1://%2%3").arg(url.scheme()).arg(url.host()).arg(url.path());
-3
Ver Arquivo
@@ -55,7 +55,6 @@ public:
bool ignoreCredentialFailure() const { return _ignoreCredentialFailure; }
QByteArray responseTimestamp();
quint64 duration();
qint64 timeoutMsec() { return _timer.interval(); }
@@ -82,8 +81,6 @@ protected:
int maxRedirects() const { return 10; }
virtual bool finished() = 0;
QByteArray _responseTimestamp;
QElapsedTimer _durationTimer;
quint64 _duration;
bool _timedout; // set to true when the timeout slot is received
// Automatically follows redirects. Note that this only works for
+10 -2
Ver Arquivo
@@ -20,6 +20,7 @@
#include "creds/abstractcredentials.h"
#include "capabilities.h"
#include "theme.h"
#include "asserts.h"
#include <QSettings>
#include <QMutex>
@@ -153,8 +154,9 @@ QUrl Account::davUrl() const
*/
void Account::clearCookieJar()
{
Q_ASSERT(qobject_cast<CookieJar*>(_am->cookieJar()));
static_cast<CookieJar*>(_am->cookieJar())->setAllCookies(QList<QNetworkCookie>());
auto jar = qobject_cast<CookieJar*>(_am->cookieJar());
ASSERT(jar);
jar->setAllCookies(QList<QNetworkCookie>());
emit wantsAccountSaved(this);
}
@@ -169,6 +171,12 @@ void Account::lendCookieJarTo(QNetworkAccessManager *guest)
jar->setParent(oldParent); // takes it back
}
QString Account::cookieJarPath()
{
ConfigFile cfg;
return cfg.configPath() + "/cookies" + id() + ".db";
}
void Account::resetNetworkAccessManager()
{
if (!_credentials || !_am) {
+1
Ver Arquivo
@@ -172,6 +172,7 @@ public:
void clearCookieJar();
void lendCookieJarTo(QNetworkAccessManager *guest);
QString cookieJarPath();
void resetNetworkAccessManager();
QNetworkAccessManager* networkAccessManager();
+52
Ver Arquivo
@@ -0,0 +1,52 @@
#ifndef OWNCLOUD_ASSERTS_H
#define OWNCLOUD_ASSERTS_H
#include <qglobal.h>
#if defined(QT_FORCE_ASSERTS) || !defined(QT_NO_DEBUG)
#define OC_ASSERT_MSG qFatal
#else
#define OC_ASSERT_MSG qWarning
#endif
// For overloading macros by argument count
// See stackoverflow.com/questions/16683146/can-macros-be-overloaded-by-number-of-arguments
#define OC_ASSERT_CAT(A, B) A ## B
#define OC_ASSERT_SELECT(NAME, NUM) OC_ASSERT_CAT(NAME ## _, NUM)
#define OC_ASSERT_GET_COUNT(_1, _2, _3, COUNT, ...) COUNT
#define OC_ASSERT_VA_SIZE(...) OC_ASSERT_GET_COUNT(__VA_ARGS__, 3, 2, 1, 0)
#define OC_ASSERT_OVERLOAD(NAME, ...) OC_ASSERT_SELECT(NAME, OC_ASSERT_VA_SIZE(__VA_ARGS__))(__VA_ARGS__)
// Default assert: If the condition is false in debug builds, terminate.
//
// Prints a message on failure, even in release builds.
#define ASSERT(...) OC_ASSERT_OVERLOAD(ASSERT, __VA_ARGS__)
#define ASSERT_1(cond) \
if (!(cond)) { \
OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \
} else {}
#define ASSERT_2(cond, message) \
if (!(cond)) { \
OC_ASSERT_MSG("ASSERT: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \
} else {}
// Enforce condition to be true, even in release builds.
//
// Prints 'message' and aborts execution if 'cond' is false.
#define ENFORCE(...) OC_ASSERT_OVERLOAD(ENFORCE, __VA_ARGS__)
#define ENFORCE_1(cond) \
if (!(cond)) { \
qFatal("ENFORCE: \"%s\" in file %s, line %d", #cond, __FILE__, __LINE__); \
} else {}
#define ENFORCE_2(cond, message) \
if (!(cond)) { \
qFatal("ENFORCE: \"%s\" in file %s, line %d with message: %s", #cond, __FILE__, __LINE__, message); \
} else {}
// An assert that is only present in debug builds: typically used for
// asserts that are too expensive for release mode.
//
// Q_ASSERT
#endif
+3 -3
Ver Arquivo
@@ -111,10 +111,10 @@ bool uploadChecksumEnabled()
QByteArray contentChecksumType()
{
static QByteArray type = qgetenv("OWNCLOUD_CONTENT_CHECKSUM_TYPE");
if (!type.isNull()) { // can set to "" to disable checksumming
return type;
if (type.isNull()) { // can set to "" to disable checksumming
type = "SHA1";
}
return "SHA1";
return type;
}
ComputeChecksum::ComputeChecksum(QObject* parent)
+21 -9
Ver Arquivo
@@ -17,6 +17,7 @@
#include "configfile.h"
#include "theme.h"
#include "utility.h"
#include "asserts.h"
#include "creds/abstractcredentials.h"
@@ -67,6 +68,7 @@ static const char downloadLimitC[] = "BWLimit/downloadLimit";
static const char newBigFolderSizeLimitC[] = "newBigFolderSizeLimit";
static const char useNewBigFolderSizeLimitC[] = "useNewBigFolderSizeLimit";
static const char confirmExternalStorageC[] = "confirmExternalStorage";
static const char maxLogLinesC[] = "Logging/maxLogLines";
@@ -138,7 +140,7 @@ void ConfigFile::setOptionalDesktopNotifications(bool show)
void ConfigFile::saveGeometry(QWidget *w)
{
#ifndef TOKEN_AUTH_ONLY
Q_ASSERT(!w->objectName().isNull());
ASSERT(!w->objectName().isNull());
QSettings settings(configFile(), QSettings::IniFormat);
settings.beginGroup(w->objectName());
settings.setValue(QLatin1String(geometryC), w->saveGeometry());
@@ -157,7 +159,7 @@ void ConfigFile::saveGeometryHeader(QHeaderView *header)
{
#ifndef TOKEN_AUTH_ONLY
if(!header) return;
Q_ASSERT(!header->objectName().isEmpty());
ASSERT(!header->objectName().isEmpty());
QSettings settings(configFile(), QSettings::IniFormat);
settings.beginGroup(header->objectName());
@@ -170,7 +172,7 @@ void ConfigFile::restoreGeometryHeader(QHeaderView *header)
{
#ifndef TOKEN_AUTH_ONLY
if(!header) return;
Q_ASSERT(!header->objectName().isNull());
ASSERT(!header->objectName().isNull());
QSettings settings(configFile(), QSettings::IniFormat);
settings.beginGroup(header->objectName());
@@ -229,8 +231,8 @@ QString ConfigFile::excludeFile(Scope scope) const
// directories.
QFileInfo fi;
if (scope != SystemScope) {
QFileInfo fi;
switch (scope) {
case UserScope:
fi.setFile( configPath(), exclFile );
if( ! fi.isReadable() ) {
@@ -240,12 +242,12 @@ QString ConfigFile::excludeFile(Scope scope) const
fi.setFile( configPath(), exclFile );
}
return fi.absoluteFilePath();
} else if (scope != UserScope) {
case SystemScope:
return ConfigFile::excludeFileFromSystem();
} else {
Q_ASSERT(false);
return QString(); // unreachable
}
ASSERT(false);
return QString();
}
QString ConfigFile::excludeFileFromSystem()
@@ -596,6 +598,16 @@ void ConfigFile::setNewBigFolderSizeLimit(bool isChecked, quint64 mbytes)
setValue(useNewBigFolderSizeLimitC, isChecked);
}
bool ConfigFile::confirmExternalStorage() const
{
return getValue(confirmExternalStorageC, QString(), true).toBool();
}
void ConfigFile::setConfirmExternalStorage(bool isChecked)
{
setValue(confirmExternalStorageC, isChecked);
}
bool ConfigFile::promptDeleteFiles() const
{
QSettings settings(configFile(), QSettings::IniFormat);
+2
Ver Arquivo
@@ -105,6 +105,8 @@ public:
/** [checked, size in MB] **/
QPair<bool, quint64> newBigFolderSizeLimit() const;
void setNewBigFolderSizeLimit(bool isChecked, quint64 mbytes);
bool confirmExternalStorage() const;
void setConfirmExternalStorage(bool);
static bool setConfDir(const QString &value);
+5 -12
Ver Arquivo
@@ -68,7 +68,6 @@ QDataStream &operator>>(QDataStream &stream, QList<QNetworkCookie> &list)
CookieJar::CookieJar(QObject *parent) :
QNetworkCookieJar(parent)
{
restore();
}
CookieJar::~CookieJar()
@@ -97,21 +96,21 @@ void CookieJar::clearSessionCookies()
setAllCookies(removeExpired(allCookies()));
}
void CookieJar::save()
void CookieJar::save(const QString &fileName)
{
QFile file;
file.setFileName(storagePath());
qDebug() << storagePath();
file.setFileName(fileName);
qDebug() << fileName;
file.open(QIODevice::WriteOnly);
QDataStream stream(&file);
stream << removeExpired(allCookies());
file.close();
}
void CookieJar::restore()
void CookieJar::restore(const QString &fileName)
{
QFile file;
file.setFileName(storagePath());
file.setFileName(fileName);
file.open(QIODevice::ReadOnly);
QDataStream stream(&file);
QList<QNetworkCookie> list;
@@ -131,10 +130,4 @@ QList<QNetworkCookie> CookieJar::removeExpired(const QList<QNetworkCookie> &cook
return updatedList;
}
QString CookieJar::storagePath() const
{
ConfigFile cfg;
return cfg.configPath() + "/cookies.db";
}
} // namespace OCC
+2 -4
Ver Arquivo
@@ -39,15 +39,13 @@ public:
using QNetworkCookieJar::setAllCookies;
using QNetworkCookieJar::allCookies;
void save();
void save(const QString &fileName);
void restore(const QString &fileName);
signals:
void newCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);
private:
void restore();
QList<QNetworkCookie> removeExpired(const QList<QNetworkCookie> &cookies);
QString storagePath() const;
};
} // namespace OCC
+2 -2
Ver Arquivo
@@ -12,10 +12,10 @@
* for more details.
*/
#include <QString>
#include <QDebug>
#include "asserts.h"
#include "creds/abstractcredentials.h"
namespace OCC
@@ -28,7 +28,7 @@ AbstractCredentials::AbstractCredentials()
void AbstractCredentials::setAccount(Account *account)
{
Q_ASSERT(!_account);
ENFORCE(!_account, "should only setAccount once");
_account = account;
}
+43 -11
Ver Arquivo
@@ -13,13 +13,19 @@
*/
#include "discoveryphase.h"
#include "account.h"
#include "theme.h"
#include "asserts.h"
#include <csync_private.h>
#include <csync_rename.h>
#include <qdebug.h>
#include <qdebug.h>
#include <QUrl>
#include "account.h"
#include <QFileInfo>
#include <cstring>
namespace OCC {
@@ -81,14 +87,32 @@ int DiscoveryJob::isInSelectiveSyncBlackListCallback(void *data, const char *pat
return static_cast<DiscoveryJob*>(data)->isInSelectiveSyncBlackList(path);
}
bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString& path)
bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString& path, const char *remotePerm)
{
// If this path or the parent is in the white list, then we do not block this file
if (_syncOptions._confirmExternalStorage && std::strchr(remotePerm, 'M')) {
// 'M' in the permission means external storage.
/* Note: DiscoverySingleDirectoryJob::directoryListingIteratedSlot make sure that only the
* root of a mounted storage has 'M', all sub entries have 'm' */
// Only allow it if the white list contains exactly this path (not parents)
// We want to ask confirmation for external storage even if the parents where selected
if (_selectiveSyncWhiteList.contains(path + QLatin1Char('/'))) {
return false;
}
emit newBigFolder(path, true);
return true;
}
// If this path or the parent is in the white list, then we do not block this file
if (findPathInList(_selectiveSyncWhiteList, path)) {
return false;
}
if (_newBigFolderSizeLimit < 0) {
auto limit = _syncOptions._newBigFolderSizeLimit;
if (limit < 0) {
// no limit, everything is allowed;
return false;
}
@@ -102,10 +126,9 @@ bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString& path)
_vioWaitCondition.wait(&_vioMutex);
}
auto limit = _newBigFolderSizeLimit;
if (result >= limit) {
// we tell the UI there is a new folder
emit newBigFolder(path);
emit newBigFolder(path, false);
return true;
} else {
// it is not too big, put it in the white list (so we will not do more query for the children)
@@ -119,9 +142,9 @@ bool DiscoveryJob::checkSelectiveSyncNewFolder(const QString& path)
}
}
int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const char *path)
int DiscoveryJob::checkSelectiveSyncNewFolderCallback(void *data, const char *path, const char *remotePerm)
{
return static_cast<DiscoveryJob*>(data)->checkSelectiveSyncNewFolder(QString::fromUtf8(path));
return static_cast<DiscoveryJob*>(data)->checkSelectiveSyncNewFolder(QString::fromUtf8(path), remotePerm);
}
@@ -224,7 +247,7 @@ int get_errno_from_http_errcode( int err, const QString & reason ) {
DiscoverySingleDirectoryJob::DiscoverySingleDirectoryJob(const AccountPtr &account, const QString &path, QObject *parent)
: QObject(parent), _subPath(path), _account(account), _ignoredFirst(false), _isRootPath(false)
: QObject(parent), _subPath(path), _account(account), _ignoredFirst(false), _isRootPath(false), _isExternalStorage(false)
{
}
@@ -321,7 +344,9 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
// The first entry is for the folder itself, we should process it differently.
_ignoredFirst = true;
if (map.contains("permissions")) {
emit firstDirectoryPermissions(map.value("permissions"));
auto perm = map.value("permissions");
emit firstDirectoryPermissions(perm);
_isExternalStorage = perm.contains(QLatin1Char('M'));
}
if (map.contains("data-fingerprint")) {
_dataFingerprint = map.value("data-fingerprint").toUtf8();
@@ -344,6 +369,13 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file, con
if (!file_stat->etag || strlen(file_stat->etag) == 0) {
qDebug() << "WARNING: etag of" << file_stat->name << "is" << file_stat->etag << " This must not happen.";
}
if (_isExternalStorage) {
/* All the entries in a external storage have 'M' in their permission. However, for all
purposes in the desktop client, we only need to know about the mount points.
So replace the 'M' by a 'm' for every sub entries in an external storage */
std::replace(file_stat->remotePerm, file_stat->remotePerm + strlen(file_stat->remotePerm),
'M', 'm');
}
QStringRef fileRef(&file);
int slashPos = file.lastIndexOf(QLatin1Char('/'));
+17 -5
Ver Arquivo
@@ -34,6 +34,16 @@ class Account;
* if the files are new, or changed.
*/
struct SyncOptions {
SyncOptions() : _newBigFolderSizeLimit(-1), _confirmExternalStorage(false) {}
/** 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;
};
/**
* @brief The FileStatPointer class
* @ingroup libsync
@@ -107,6 +117,8 @@ private:
bool _ignoredFirst;
// Set to true if this is the root path and we need to check the data-fingerprint
bool _isRootPath;
// If this directory is an external storage (The first item has 'M' in its permission)
bool _isExternalStorage;
QPointer<LsColJob> _lsColJob;
public:
@@ -176,8 +188,8 @@ class DiscoveryJob : public QObject {
*/
bool isInSelectiveSyncBlackList(const char* path) const;
static int isInSelectiveSyncBlackListCallback(void *, const char *);
bool checkSelectiveSyncNewFolder(const QString &path);
static int checkSelectiveSyncNewFolderCallback(void*, const char*);
bool checkSelectiveSyncNewFolder(const QString &path, const char *remotePerm);
static int checkSelectiveSyncNewFolderCallback(void* data, const char* path, const char* remotePerm);
// Just for progress
static void update_job_update_callback (bool local,
@@ -197,7 +209,7 @@ class DiscoveryJob : public QObject {
public:
explicit DiscoveryJob(CSYNC *ctx, QObject* parent = 0)
: QObject(parent), _csync_ctx(ctx), _newBigFolderSizeLimit(-1) {
: QObject(parent), _csync_ctx(ctx) {
// We need to forward the log property as csync uses thread local
// and updates run in another thread
_log_callback = csync_get_log_callback();
@@ -207,7 +219,7 @@ public:
QStringList _selectiveSyncBlackList;
QStringList _selectiveSyncWhiteList;
qint64 _newBigFolderSizeLimit;
SyncOptions _syncOptions;
Q_INVOKABLE void start();
signals:
void finished(int result);
@@ -218,7 +230,7 @@ signals:
void doGetSizeSignal(const QString &path, qint64 *result);
// A new folder was discovered and was not synced because of the confirmation feature
void newBigFolder(const QString &folder);
void newBigFolder(const QString &folder, bool isExternal);
};
}
+1 -1
Ver Arquivo
@@ -47,7 +47,7 @@ void ExcludedFiles::addExcludeFilePath(const QString& path)
_excludeFiles.insert(path);
}
#ifdef WITH_UNIT_TESTING
#ifdef WITH_TESTING
void ExcludedFiles::addExcludeExpr(const QString &expr)
{
_csync_exclude_add(_excludesPtr, expr.toLatin1().constData());
+1 -1
Ver Arquivo
@@ -58,7 +58,7 @@ public:
const QString& basePath,
bool excludeHidden) const;
#ifdef WITH_UNIT_TESTING
#ifdef WITH_TESTING
void addExcludeExpr(const QString &expr);
#endif
+1 -1
Ver Arquivo
@@ -84,7 +84,7 @@ QString OWNCLOUDSYNC_EXPORT longWinPath( const QString& inpath );
*/
time_t OWNCLOUDSYNC_EXPORT getModTime(const QString& filename);
bool setModTime(const QString &filename, time_t modTime);
bool OWNCLOUDSYNC_EXPORT setModTime(const QString &filename, time_t modTime);
/**
* @brief Get the size for a file
+28 -32
Ver Arquivo
@@ -25,6 +25,7 @@
#include "configfile.h"
#include "utility.h"
#include "account.h"
#include "asserts.h"
#include <json.h>
#ifdef Q_OS_WIN
@@ -172,7 +173,7 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
_item->_status = status;
emit itemCompleted(*_item, *this);
emit itemCompleted(_item);
emit finished(status);
}
@@ -221,7 +222,7 @@ bool PropagateItemJob::checkForProblemsWithShared(int httpStatusCode, const QStr
if( newJob ) {
newJob->setRestoreJobMsg(msg);
_restoreJob.reset(newJob);
connect(_restoreJob.data(), SIGNAL(itemCompleted(const SyncFileItemPtr &, const PropagatorJob &)),
connect(_restoreJob.data(), SIGNAL(itemCompleted(const SyncFileItemPtr &)),
this, SLOT(slotRestoreJobCompleted(const SyncFileItemPtr &)));
QMetaObject::invokeMethod(newJob, "start");
}
@@ -403,8 +404,8 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
_rootJob->append(it);
}
connect(_rootJob.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_rootJob.data(), SIGNAL(itemCompleted(const SyncFileItemPtr &)),
this, SIGNAL(itemCompleted(const SyncFileItemPtr &)));
connect(_rootJob.data(), SIGNAL(progress(const SyncFileItem &,quint64)), this, SIGNAL(progress(const SyncFileItem &,quint64)));
connect(_rootJob.data(), SIGNAL(finished(SyncFileItem::Status)), this, SLOT(emitFinished(SyncFileItem::Status)));
connect(_rootJob.data(), SIGNAL(ready()), this, SLOT(scheduleNextJob()), Qt::QueuedConnection);
@@ -588,17 +589,12 @@ OwncloudPropagator *PropagatorJob::propagator() const
PropagatorJob::JobParallelism PropagateDirectory::parallelism()
{
// If any of the non-finished sub jobs is not parallel, we have to wait
// FIXME! we should probably cache this result
if (_firstJob && _firstJob->_state != Finished) {
if (_firstJob->parallelism() != FullParallelism)
return WaitForFinished;
if (_firstJob && _firstJob->parallelism() != FullParallelism) {
return WaitForFinished;
}
// FIXME: use the cached value of finished job
for (int i = 0; i < _subJobs.count(); ++i) {
if (_subJobs.at(i)->_state != Finished && _subJobs.at(i)->parallelism() != FullParallelism) {
if (_subJobs.at(i)->parallelism() != FullParallelism) {
return WaitForFinished;
}
}
@@ -629,15 +625,8 @@ bool PropagateDirectory::scheduleNextJob()
return false;
}
// cache the value of first unfinished subjob
bool stopAtDirectory = false;
int i = _firstUnfinishedSubJob;
int subJobsCount = _subJobs.count();
while (i < subJobsCount && _subJobs.at(i)->_state == Finished) {
_firstUnfinishedSubJob = ++i;
}
for (int i = _firstUnfinishedSubJob; i < subJobsCount; ++i) {
for (int i = 0; i < _subJobs.size(); ++i) {
if (_subJobs.at(i)->_state == Finished) {
continue;
}
@@ -650,7 +639,7 @@ bool PropagateDirectory::scheduleNextJob()
return true;
}
Q_ASSERT(_subJobs.at(i)->_state == Running);
ASSERT(_subJobs.at(i)->_state == Running);
auto paral = _subJobs.at(i)->parallelism();
if (paral == WaitForFinished) {
@@ -665,8 +654,23 @@ bool PropagateDirectory::scheduleNextJob()
void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
{
PropagatorJob *subJob = static_cast<PropagatorJob *>(sender());
ASSERT(subJob);
// Delete the job and remove it from our list of jobs.
subJob->deleteLater();
bool wasFirstJob = false;
if (subJob == _firstJob.data()) {
wasFirstJob = true;
_firstJob.take();
} else {
int i = _subJobs.indexOf(subJob);
ASSERT(i >= 0);
_subJobs.remove(i);
}
if (status == SyncFileItem::FatalError ||
(sender() == _firstJob.data() && status != SyncFileItem::Success && status != SyncFileItem::Restoration)) {
(wasFirstJob && status != SyncFileItem::Success && status != SyncFileItem::Restoration)) {
abort();
_state = Finished;
emit finished(status);
@@ -674,18 +678,10 @@ void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
} else if (status == SyncFileItem::NormalError || status == SyncFileItem::SoftError) {
_hasError = status;
}
_runningNow--;
_jobsFinished++;
int totalJobs = _subJobs.count();
if (_firstJob) {
totalJobs++;
}
// We finished processing all the jobs
// check if we finished
if (_jobsFinished >= totalJobs) {
Q_ASSERT(!_runningNow); // how can we be finished if there are still jobs running now
if (!_firstJob && _subJobs.isEmpty()) {
finalize();
} else {
emit ready();
@@ -765,7 +761,7 @@ void CleanupPollsJob::start()
void CleanupPollsJob::slotPollFinished()
{
PollJob *job = qobject_cast<PollJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
if (job->_item->_status == SyncFileItem::FatalError) {
emit aborted(job->_item->_errorString);
deleteLater();
+5 -10
Ver Arquivo
@@ -114,7 +114,7 @@ signals:
/**
* Emitted when one item has been completed within a job.
*/
void itemCompleted(const SyncFileItem &, const PropagatorJob &);
void itemCompleted(const SyncFileItemPtr &);
/**
* Emitted when all the sub-jobs have been finished and
@@ -192,14 +192,11 @@ public:
SyncFileItemPtr _item;
int _jobsFinished; // number of jobs that have completed
int _runningNow; // number of subJobs running right now
SyncFileItem::Status _hasError; // NoStatus, or NormalError / SoftError if there was an error
int _firstUnfinishedSubJob;
explicit PropagateDirectory(OwncloudPropagator *propagator, const SyncFileItemPtr &item = SyncFileItemPtr(new SyncFileItem))
: PropagatorJob(propagator)
, _firstJob(0), _item(item), _jobsFinished(0), _runningNow(0), _hasError(SyncFileItem::NoStatus), _firstUnfinishedSubJob(0)
, _item(item), _hasError(SyncFileItem::NoStatus)
{ }
virtual ~PropagateDirectory() {
@@ -230,12 +227,10 @@ public:
private slots:
bool possiblyRunNextJob(PropagatorJob *next) {
if (next->_state == NotYetStarted) {
connect(next, SIGNAL(finished(SyncFileItem::Status)), this, SLOT(slotSubJobFinished(SyncFileItem::Status)), Qt::QueuedConnection);
connect(next, SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(next, SIGNAL(finished(SyncFileItem::Status)), this, SLOT(slotSubJobFinished(SyncFileItem::Status)));
connect(next, SIGNAL(itemCompleted(const SyncFileItemPtr &)), this, SIGNAL(itemCompleted(const SyncFileItemPtr &)));
connect(next, SIGNAL(progress(const SyncFileItem &,quint64)), this, SIGNAL(progress(const SyncFileItem &,quint64)));
connect(next, SIGNAL(ready()), this, SIGNAL(ready()));
_runningNow++;
}
return next->scheduleNextJob();
}
@@ -356,7 +351,7 @@ private slots:
void scheduleNextJob();
signals:
void itemCompleted(const SyncFileItem &, const PropagatorJob &);
void itemCompleted(const SyncFileItemPtr &);
void progress(const SyncFileItem&, quint64 bytes);
void finished(bool success);
+56 -59
Ver Arquivo
@@ -20,6 +20,7 @@
#include "ownsql.h"
#include "utility.h"
#include "asserts.h"
#define SQLITE_SLEEP_TIME_USEC 100000
#define SQLITE_REPEAT_COUNT 20
@@ -147,10 +148,8 @@ void SqlDatabase::close()
{
if( _db ) {
SQLITE_DO(sqlite3_close(_db) );
if (_errId != SQLITE_OK) {
qWarning() << "ERROR When closing DB" << _error;
Q_ASSERT(!"SQLite Close Error");
}
// Fatal because reopening an unclosed db might be problematic.
ENFORCE(_errId == SQLITE_OK, "Error when closing DB");
_db = 0;
}
}
@@ -223,11 +222,7 @@ int SqlQuery::prepare( const QString& sql, bool allow_failure )
if( _errId != SQLITE_OK ) {
_error = QString::fromUtf8(sqlite3_errmsg(_db));
qWarning() << "Sqlite prepare statement error:" << _error << "in" <<_sql;
if (!allow_failure) {
qFatal("SQLITE Prepare error: %s in %s",
_error.toLocal8Bit().data(),
sql.toLocal8Bit().data());
}
ENFORCE(allow_failure, "SQLITE Prepare error");
}
}
return _errId;
@@ -284,61 +279,63 @@ bool SqlQuery::next()
void SqlQuery::bindValue(int pos, const QVariant& value)
{
int res = -1;
Q_ASSERT(_stmt);
if( _stmt ) {
switch (value.type()) {
case QVariant::Int:
case QVariant::Bool:
res = sqlite3_bind_int(_stmt, pos, value.toInt());
break;
case QVariant::Double:
res = sqlite3_bind_double(_stmt, pos, value.toDouble());
break;
case QVariant::UInt:
case QVariant::LongLong:
res = sqlite3_bind_int64(_stmt, pos, value.toLongLong());
break;
case QVariant::DateTime: {
const QDateTime dateTime = value.toDateTime();
const QString str = dateTime.toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz"));
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
break;
}
case QVariant::Time: {
const QTime time = value.toTime();
const QString str = time.toString(QLatin1String("hh:mm:ss.zzz"));
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
break;
}
case QVariant::String: {
if( !value.toString().isNull() ) {
// lifetime of string == lifetime of its qvariant
const QString *str = static_cast<const QString*>(value.constData());
res = sqlite3_bind_text16(_stmt, pos, str->utf16(),
(str->size()) * sizeof(QChar), SQLITE_TRANSIENT);
} else {
res = sqlite3_bind_null(_stmt, pos);
}
break; }
case QVariant::ByteArray: {
auto ba = value.toByteArray();
res = sqlite3_bind_text(_stmt, pos, ba.constData(), ba.size(), SQLITE_TRANSIENT);
break;
}
default: {
QString str = value.toString();
// SQLITE_TRANSIENT makes sure that sqlite buffers the data
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
(str.size()) * sizeof(QChar), SQLITE_TRANSIENT);
break; }
if (!_stmt) {
ASSERT(false);
return;
}
switch (value.type()) {
case QVariant::Int:
case QVariant::Bool:
res = sqlite3_bind_int(_stmt, pos, value.toInt());
break;
case QVariant::Double:
res = sqlite3_bind_double(_stmt, pos, value.toDouble());
break;
case QVariant::UInt:
case QVariant::LongLong:
res = sqlite3_bind_int64(_stmt, pos, value.toLongLong());
break;
case QVariant::DateTime: {
const QDateTime dateTime = value.toDateTime();
const QString str = dateTime.toString(QLatin1String("yyyy-MM-ddThh:mm:ss.zzz"));
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
break;
}
case QVariant::Time: {
const QTime time = value.toTime();
const QString str = time.toString(QLatin1String("hh:mm:ss.zzz"));
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
str.size() * sizeof(ushort), SQLITE_TRANSIENT);
break;
}
case QVariant::String: {
if( !value.toString().isNull() ) {
// lifetime of string == lifetime of its qvariant
const QString *str = static_cast<const QString*>(value.constData());
res = sqlite3_bind_text16(_stmt, pos, str->utf16(),
(str->size()) * sizeof(QChar), SQLITE_TRANSIENT);
} else {
res = sqlite3_bind_null(_stmt, pos);
}
break; }
case QVariant::ByteArray: {
auto ba = value.toByteArray();
res = sqlite3_bind_text(_stmt, pos, ba.constData(), ba.size(), SQLITE_TRANSIENT);
break;
}
default: {
QString str = value.toString();
// SQLITE_TRANSIENT makes sure that sqlite buffers the data
res = sqlite3_bind_text16(_stmt, pos, str.utf16(),
(str.size()) * sizeof(QChar), SQLITE_TRANSIENT);
break; }
}
if (res != SQLITE_OK) {
qDebug() << Q_FUNC_INFO << "ERROR" << value << res;
}
Q_ASSERT( res == SQLITE_OK );
ASSERT( res == SQLITE_OK );
}
bool SqlQuery::nullValue(int index)
+1 -5
Ver Arquivo
@@ -28,8 +28,6 @@
namespace OCC {
class PropagatorJob;
/**
* @brief The ProgressInfo class
* @ingroup libsync
@@ -252,9 +250,7 @@ signals:
/**
* @brief: the item was completed by a job
*/
void itemCompleted(const QString &folder,
const SyncFileItem & item,
const PropagatorJob & job);
void itemCompleted(const QString &folder, const SyncFileItemPtr & item);
protected:
void setProgressInfo(const QString& folder, const ProgressInfo& progress);
+2 -2
Ver Arquivo
@@ -23,6 +23,7 @@
#include "filesystem.h"
#include "propagatorjobs.h"
#include "checksums.h"
#include "asserts.h"
#include <json.h>
#include <QNetworkAccessManager>
@@ -441,7 +442,7 @@ void PropagateDownloadFile::slotGetFinished()
propagator()->_activeJobList.removeOne(this);
GETFileJob *job = qobject_cast<GETFileJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
qDebug() << Q_FUNC_INFO << job->reply()->request().url() << "FINISHED WITH STATUS"
<< job->reply()->error()
@@ -520,7 +521,6 @@ void PropagateDownloadFile::slotGetFinished()
// so make sure we have the up-to-date time
_item->_modtime = job->lastModified();
}
_item->_requestDuration = job->duration();
_item->_responseTimeStamp = job->responseTimestamp();
_tmpFile.close();
+2 -2
Ver Arquivo
@@ -15,6 +15,7 @@
#include "propagateremotedelete.h"
#include "owncloudpropagator_p.h"
#include "account.h"
#include "asserts.h"
namespace OCC {
@@ -81,7 +82,7 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
{
propagator()->_activeJobList.removeOne(this);
Q_ASSERT(_job);
ASSERT(_job);
qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS"
<< _job->reply()->error()
@@ -104,7 +105,6 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
return;
}
_item->_requestDuration = _job->duration();
_item->_responseTimeStamp = _job->responseTimestamp();
// A 404 reply is also considered a success here: We want to make sure
+2 -2
Ver Arquivo
@@ -17,6 +17,7 @@
#include "account.h"
#include "syncjournalfilerecord.h"
#include "propagateremotedelete.h"
#include "asserts.h"
#include <QFile>
namespace OCC {
@@ -70,7 +71,7 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
{
propagator()->_activeJobList.removeOne(this);
Q_ASSERT(_job);
ASSERT(_job);
qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS"
<< _job->reply()->error()
@@ -99,7 +100,6 @@ void PropagateRemoteMkdir::slotMkcolJobFinished()
return;
}
_item->_requestDuration = _job->duration();
_item->_responseTimeStamp = _job->responseTimestamp();
_item->_fileId = _job->reply()->rawHeader("OC-FileId");
+4 -4
Ver Arquivo
@@ -18,6 +18,7 @@
#include "account.h"
#include "syncjournalfilerecord.h"
#include "filesystem.h"
#include "asserts.h"
#include <QFile>
#include <QStringList>
#include <QDir>
@@ -123,7 +124,7 @@ void PropagateRemoteMove::slotMoveJobFinished()
{
propagator()->_activeJobList.removeOne(this);
Q_ASSERT(_job);
ASSERT(_job);
qDebug() << Q_FUNC_INFO << _job->reply()->request().url() << "FINISHED WITH STATUS"
<< _job->reply()->error()
@@ -145,7 +146,6 @@ void PropagateRemoteMove::slotMoveJobFinished()
return;
}
_item->_requestDuration = _job->duration();
_item->_responseTimeStamp = _job->responseTimestamp();
if (_item->_httpErrorCode != 201 ) {
@@ -208,8 +208,8 @@ bool PropagateRemoteMove::adjustSelectiveSync(SyncJournalDb *journal, const QStr
return false;
bool changed = false;
Q_ASSERT(!from_.endsWith(QLatin1String("/")));
Q_ASSERT(!to_.endsWith(QLatin1String("/")));
ASSERT(!from_.endsWith(QLatin1String("/")));
ASSERT(!to_.endsWith(QLatin1String("/")));
QString from = from_ + QLatin1String("/");
QString to = to_ + QLatin1String("/");
+9 -3
Ver Arquivo
@@ -25,6 +25,7 @@
#include "checksums.h"
#include "syncengine.h"
#include "propagateremotedelete.h"
#include "asserts.h"
#include <json.h>
#include <QNetworkAccessManager>
@@ -224,7 +225,9 @@ void PropagateUploadFileCommon::slotComputeContentChecksum()
// change during the checksum calculation
_item->_modtime = FileSystem::getModTime(filePath);
#ifdef WITH_TESTING
_stopWatch.start();
#endif
QByteArray checksumType = contentChecksumType();
@@ -251,8 +254,10 @@ void PropagateUploadFileCommon::slotComputeTransmissionChecksum(const QByteArray
_item->_contentChecksum = contentChecksum;
_item->_contentChecksumType = contentChecksumType;
#ifdef WITH_TESTING
_stopWatch.addLapTime(QLatin1String("ContentChecksum"));
_stopWatch.start();
#endif
// Reuse the content checksum as the transmission checksum if possible
const auto supportedTransmissionChecksums =
@@ -299,7 +304,9 @@ void PropagateUploadFileCommon::slotStartUpload(const QByteArray& transmissionCh
done(SyncFileItem::SoftError, tr("File Removed"));
return;
}
#ifdef WITH_TESTING
_stopWatch.addLapTime(QLatin1String("TransmissionChecksum"));
#endif
time_t prevModtime = _item->_modtime; // the _item value was set in PropagateUploadFile::start()
// but a potential checksum calculation could have taken some time during which the file could
@@ -369,7 +376,7 @@ bool UploadDevice::prepareAndOpen(const QString& fileName, qint64 start, qint64
qint64 UploadDevice::writeData(const char* , qint64 ) {
Q_ASSERT(!"write to read only device");
ASSERT(false, "write to read only device");
return 0;
}
@@ -480,7 +487,7 @@ void PropagateUploadFileCommon::startPollJob(const QString& path)
void PropagateUploadFileCommon::slotPollFinished()
{
PollJob *job = qobject_cast<PollJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
propagator()->_activeJobList.removeOne(this);
@@ -569,7 +576,6 @@ QMap<QByteArray, QByteArray> PropagateUploadFileCommon::headers()
void PropagateUploadFileCommon::finalize()
{
_item->_requestDuration = _duration.elapsed();
_finished = true;
if (!propagator()->_journal->setFileRecord(SyncJournalFileRecord(*_item, propagator()->getFilePath(_item->_file)))) {
+4 -3
Ver Arquivo
@@ -183,13 +183,14 @@ class PropagateUploadFileCommon : public PropagateItemJob {
Q_OBJECT
protected:
QElapsedTimer _duration;
QVector<AbstractNetworkJob*> _jobs; /// network jobs that are currently in transit
bool _finished; /// Tells that all the jobs have been finished
bool _deleteExisting;
bool _finished BITFIELD(1); /// Tells that all the jobs have been finished
bool _deleteExisting BITFIELD(1);
// measure the performance of checksum calc and upload
#ifdef WITH_TESTING
Utility::StopWatch _stopWatch;
#endif
QByteArray _transmissionChecksum;
QByteArray _transmissionChecksumType;
+19 -10
Ver Arquivo
@@ -25,7 +25,7 @@
#include "syncengine.h"
#include "propagateremotemove.h"
#include "propagateremotedelete.h"
#include "asserts.h"
#include <QNetworkAccessManager>
#include <QFileInfo>
@@ -80,7 +80,6 @@ QUrl PropagateUploadFileNG::chunkUrl(int chunk)
void PropagateUploadFileNG::doStartUpload()
{
_duration.start();
propagator()->_activeJobList.append(this);
const SyncJournalDb::UploadInfo progressInfo = propagator()->_journal->getUploadInfo(_item->_file);
@@ -98,6 +97,12 @@ void PropagateUploadFileNG::doStartUpload()
this, SLOT(slotPropfindIterate(QString,QMap<QString,QString>)));
job->start();
return;
} else if (progressInfo._valid) {
// The upload info is stale. remove the stale chunks on the server
_transferId = progressInfo._transferid;
// Fire and forget. Any error will be ignored.
(new DeleteJob(propagator()->account(), chunkUrl(), this))->start();
// startNewUpload will reset the _transferId and the UploadInfo in the db.
}
startNewUpload();
@@ -183,7 +188,7 @@ void PropagateUploadFileNG::slotPropfindFinishedWithError()
void PropagateUploadFileNG::slotDeleteJobFinished()
{
auto job = qobject_cast<DeleteJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
_jobs.remove(_jobs.indexOf(job));
QNetworkReply::NetworkError err = job->reply()->error();
@@ -215,7 +220,7 @@ void PropagateUploadFileNG::slotDeleteJobFinished()
void PropagateUploadFileNG::startNewUpload()
{
Q_ASSERT(propagator()->_activeJobList.count(this) == 1);
ASSERT(propagator()->_activeJobList.count(this) == 1);
_transferId = qrand() ^ _item->_modtime ^ (_item->_size << 16) ^ qHash(_item->_file);
_sent = 0;
_currentChunk = 0;
@@ -265,11 +270,12 @@ void PropagateUploadFileNG::startNextChunk()
return;
quint64 fileSize = _item->_size;
Q_ASSERT(fileSize >= _sent);
ENFORCE(fileSize >= _sent, "Sent data exceeds file size");
quint64 currentChunkSize = qMin(chunkSize(), fileSize - _sent);
if (currentChunkSize == 0) {
Q_ASSERT(_jobs.isEmpty()); // There should be no running job anymore
ASSERT(_jobs.isEmpty());
_finished = true;
// Finish with a MOVE
QString destination = QDir::cleanPath(propagator()->account()->url().path() + QLatin1Char('/')
@@ -338,7 +344,8 @@ void PropagateUploadFileNG::startNextChunk()
void PropagateUploadFileNG::slotPutFinished()
{
PUTFileJob *job = qobject_cast<PUTFileJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
slotJobDestroyed(job); // remove it from the _jobs list
qDebug() << job->reply()->request().url() << "FINISHED WITH STATUS"
@@ -386,7 +393,7 @@ void PropagateUploadFileNG::slotPutFinished()
return;
}
Q_ASSERT(_sent <= _item->_size);
ENFORCE(_sent <= _item->_size, "can't send more than size");
bool finished = _sent == _item->_size;
// Check if the file still exists
@@ -478,14 +485,16 @@ void PropagateUploadFileNG::slotMoveJobFinished()
}
_item->_responseTimeStamp = job->responseTimestamp();
#ifdef WITH_TESTING
// performance logging
_item->_requestDuration = _stopWatch.stop();
quint64 duration = _stopWatch.stop();
qDebug() << "*==* duration UPLOAD" << _item->_size
<< _stopWatch.durationOfLap(QLatin1String("ContentChecksum"))
<< _stopWatch.durationOfLap(QLatin1String("TransmissionChecksum"))
<< _item->_requestDuration;
<< duration;
// The job might stay alive for the whole sync, release this tiny bit of memory.
_stopWatch.reset();
#endif
finalize();
}
+7 -4
Ver Arquivo
@@ -25,6 +25,7 @@
#include "checksums.h"
#include "syncengine.h"
#include "propagateremotedelete.h"
#include "asserts.h"
#include <json.h>
#include <QNetworkAccessManager>
@@ -49,7 +50,6 @@ void PropagateUploadFileV1::doStartUpload()
}
_currentChunk = 0;
_duration.start();
emit progress(*_item, 0);
startNextChunk();
@@ -172,7 +172,8 @@ void PropagateUploadFileV1::startNextChunk()
void PropagateUploadFileV1::slotPutFinished()
{
PUTFileJob *job = qobject_cast<PUTFileJob *>(sender());
Q_ASSERT(job);
ASSERT(job);
slotJobDestroyed(job); // remove it from the _jobs list
qDebug() << Q_FUNC_INFO << job->reply()->request().url() << "FINISHED WITH STATUS"
@@ -340,14 +341,16 @@ void PropagateUploadFileV1::slotPutFinished()
done(SyncFileItem::SoftError, "Server does not support X-OC-MTime");
}
#ifdef WITH_TESTING
// performance logging
_item->_requestDuration = _stopWatch.stop();
quint64 duration = _stopWatch.stop();
qDebug() << "*==* duration UPLOAD" << _item->_size
<< _stopWatch.durationOfLap(QLatin1String("ContentChecksum"))
<< _stopWatch.durationOfLap(QLatin1String("TransmissionChecksum"))
<< _item->_requestDuration;
<< duration;
// The job might stay alive for the whole sync, release this tiny bit of memory.
_stopWatch.reset();
#endif
finalize();
}
+76 -64
Ver Arquivo
@@ -23,6 +23,8 @@
#include "syncfilestatus.h"
#include "csync_private.h"
#include "filesystem.h"
#include "propagateremotedelete.h"
#include "asserts.h"
#ifdef Q_OS_WIN
#include <windows.h>
@@ -71,18 +73,18 @@ SyncEngine::SyncEngine(AccountPtr account, const QString& localPath,
, _backInTimeFiles(0)
, _uploadLimit(0)
, _downloadLimit(0)
, _newBigFolderSizeLimit(-1)
, _checksum_hook(journal)
, _anotherSyncNeeded(NoFollowUpSync)
{
qRegisterMetaType<SyncFileItem>("SyncFileItem");
qRegisterMetaType<SyncFileItemPtr>("SyncFileItemPtr");
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
qRegisterMetaType<SyncFileStatus>("SyncFileStatus");
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
// Everything in the SyncEngine expects a trailing slash for the localPath.
Q_ASSERT(localPath.endsWith(QLatin1Char('/')));
ASSERT(localPath.endsWith(QLatin1Char('/')));
csync_create(&_csync_ctx, localPath.toUtf8().data());
@@ -266,11 +268,11 @@ bool SyncEngine::checkErrorBlacklisting( SyncFileItem &item )
return true;
}
void SyncEngine::deleteStaleDownloadInfos()
void SyncEngine::deleteStaleDownloadInfos(const SyncFileItemVector &syncItems)
{
// Find all downloadinfo paths that we want to preserve.
QSet<QString> download_file_paths;
foreach(const SyncFileItemPtr &it, _syncedItems) {
foreach(const SyncFileItemPtr &it, syncItems) {
if (it->_direction == SyncFileItem::Down
&& it->_type == SyncFileItem::File)
{
@@ -288,11 +290,11 @@ void SyncEngine::deleteStaleDownloadInfos()
}
}
void SyncEngine::deleteStaleUploadInfos()
void SyncEngine::deleteStaleUploadInfos(const SyncFileItemVector &syncItems)
{
// Find all blacklisted paths that we want to preserve.
QSet<QString> upload_file_paths;
foreach(const SyncFileItemPtr &it, _syncedItems) {
foreach(const SyncFileItemPtr &it, syncItems) {
if (it->_direction == SyncFileItem::Up
&& it->_type == SyncFileItem::File)
{
@@ -301,14 +303,23 @@ void SyncEngine::deleteStaleUploadInfos()
}
// Delete from journal.
_journal->deleteStaleUploadInfos(upload_file_paths);
auto ids = _journal->deleteStaleUploadInfos(upload_file_paths);
// Delete the stales chunk on the server.
if (account()->capabilities().chunkingNg()) {
foreach (uint transferId, ids) {
QUrl url = Utility::concatUrlPath(account()->url(), QLatin1String("remote.php/dav/uploads/")
+ account()->davUser() + QLatin1Char('/') + QString::number(transferId));
(new DeleteJob(account(), url, this))->start();
}
}
}
void SyncEngine::deleteStaleErrorBlacklistEntries()
void SyncEngine::deleteStaleErrorBlacklistEntries(const SyncFileItemVector &syncItems)
{
// Find all blacklisted paths that we want to preserve.
QSet<QString> blacklist_file_paths;
foreach(const SyncFileItemPtr &it, _syncedItems) {
foreach(const SyncFileItemPtr &it, syncItems) {
if (it->_hasBlacklistEntry)
blacklist_file_paths.insert(it->_file);
}
@@ -343,7 +354,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
QTextCodec::ConverterState utf8State;
static QTextCodec *codec = QTextCodec::codecForName("UTF-8");
Q_ASSERT(codec);
ASSERT(codec);
QString fileUtf8 = codec->toUnicode(file->path, qstrlen(file->path), &utf8State);
QString renameTarget;
QString key = fileUtf8;
@@ -379,9 +390,11 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
item->_modtime = file->modtime;
} else {
if (instruction != CSYNC_INSTRUCTION_NONE) {
qDebug() << "ERROR: Instruction" << item->_instruction << "vs" << instruction << "for" << fileUtf8;
Q_ASSERT(!"Instructions are both unequal NONE");
return -1;
qWarning() << "ERROR: Instruction" << item->_instruction << "vs" << instruction << "for" << fileUtf8;
ASSERT(false);
// Set instruction to NONE for safety.
file->instruction = item->_instruction = instruction = CSYNC_INSTRUCTION_NONE;
return -1; // should lead to treewalk error
}
}
@@ -396,6 +409,8 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
}
if (file->remotePerm && file->remotePerm[0]) {
item->_remotePerm = QByteArray(file->remotePerm);
if (remote)
_remotePerms[item->_file] = item->_remotePerm;
}
/* The flag "serverHasIgnoredFiles" is true if item in question is a directory
@@ -426,10 +441,6 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
_seenFiles.insert(renameTarget);
}
if (remote && file->remotePerm && file->remotePerm[0]) {
_remotePerms[item->_file] = file->remotePerm;
}
switch(file->error_status) {
case CSYNC_STATUS_OK:
break;
@@ -491,7 +502,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
item->_status = SyncFileItem::SoftError;
break;
default:
Q_ASSERT("Non handled error-status");
ASSERT(false, "Non handled error-status");
/* No error string */
}
@@ -642,12 +653,6 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
_needsUpdate = true;
item->log._etag = file->etag;
item->log._fileId = file->file_id;
item->log._instruction = file->instruction;
item->log._modtime = file->modtime;
item->log._size = file->size;
item->log._other_etag = file->other.etag;
item->log._other_fileId = file->other.file_id;
item->log._other_instruction = file->other.instruction;
@@ -707,8 +712,11 @@ void SyncEngine::startSync()
}
}
Q_ASSERT(!s_anySyncRunning);
Q_ASSERT(!_syncRunning);
if (s_anySyncRunning || _syncRunning) {
ASSERT(false);
return;
}
s_anySyncRunning = true;
_syncRunning = true;
_anotherSyncNeeded = NoFollowUpSync;
@@ -743,7 +751,6 @@ void SyncEngine::startSync()
qDebug() << "Could not determine free space available at" << _localPath;
}
_syncedItems.clear();
_syncItemMap.clear();
_needsUpdate = false;
@@ -830,14 +837,14 @@ void SyncEngine::startSync()
return;
}
discoveryJob->_newBigFolderSizeLimit = _newBigFolderSizeLimit;
discoveryJob->_syncOptions = _syncOptions;
discoveryJob->moveToThread(&_thread);
connect(discoveryJob, SIGNAL(finished(int)), this, SLOT(slotDiscoveryJobFinished(int)));
connect(discoveryJob, SIGNAL(folderDiscovered(bool,QString)),
this, SIGNAL(folderDiscovered(bool,QString)));
connect(discoveryJob, SIGNAL(newBigFolder(QString)),
this, SIGNAL(newBigFolder(QString)));
connect(discoveryJob, SIGNAL(newBigFolder(QString,bool)),
this, SIGNAL(newBigFolder(QString,bool)));
// This is used for the DiscoveryJob to be able to request the main thread/
@@ -890,6 +897,8 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
_hasForwardInTimeFiles = false;
_backInTimeFiles = 0;
bool walkOk = true;
_remotePerms.clear();
_remotePerms.reserve(c_rbtree_size(_csync_ctx->remote.tree));
_seenFiles.clear();
_temporarilyUnavailablePaths.clear();
_renamedFolders.clear();
@@ -911,12 +920,12 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
csync_commit(_csync_ctx);
// The map was used for merging trees, convert it to a list:
_syncedItems = _syncItemMap.values().toVector();
SyncFileItemVector syncItems = _syncItemMap.values().toVector();
_syncItemMap.clear(); // free memory
// Adjust the paths for the renames.
for (SyncFileItemVector::iterator it = _syncedItems.begin();
it != _syncedItems.end(); ++it) {
for (SyncFileItemVector::iterator it = syncItems.begin();
it != syncItems.end(); ++it) {
(*it)->_file = adjustRenamedPath((*it)->_file);
}
@@ -924,7 +933,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
if (_account->serverVersionInt() < 0x080100) {
// Server version older than 8.1 don't support these character in filename.
static const QRegExp invalidCharRx("[\\\\:?*\"<>|]");
for (auto it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
for (auto it = syncItems.begin(); it != syncItems.end(); ++it) {
if ((*it)->_direction == SyncFileItem::Up &&
(*it)->destination().contains(invalidCharRx)) {
(*it)->_errorString = tr("File name contains at least one invalid character");
@@ -936,7 +945,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
if (!_hasNoneFiles && _hasRemoveFile) {
qDebug() << Q_FUNC_INFO << "All the files are going to be changed, asking the user";
bool cancel = false;
emit aboutToRemoveAllFiles(_syncedItems.first()->_direction, &cancel);
emit aboutToRemoveAllFiles(syncItems.first()->_direction, &cancel);
if (cancel) {
qDebug() << Q_FUNC_INFO << "Abort sync";
finalize(false);
@@ -951,7 +960,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
if (!databaseFingerprint.isNull()
&& _discoveryMainThread->_dataFingerprint != databaseFingerprint) {
qDebug() << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryMainThread->_dataFingerprint;
restoreOldFiles();
restoreOldFiles(syncItems);
} else if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2 && _account->serverVersionInt() < 0x090100) {
// The server before ownCloud 9.1 did not have the data-fingerprint property. So in that
// case we use heuristics to detect restored backup. This is disabled with newer version
@@ -961,18 +970,18 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
bool restore = false;
emit aboutToRestoreBackup(&restore);
if (restore) {
restoreOldFiles();
restoreOldFiles(syncItems);
}
}
// Sort items per destination
std::sort(_syncedItems.begin(), _syncedItems.end());
std::sort(syncItems.begin(), syncItems.end());
// make sure everything is allowed
checkForPermission();
checkForPermission(syncItems);
// To announce the beginning of the sync
emit aboutToPropagate(_syncedItems);
emit aboutToPropagate(syncItems);
// it's important to do this before ProgressInfo::start(), to announce start of new sync
emit transmissionProgress(*_progressInfo);
_progressInfo->startEstimateUpdates();
@@ -994,8 +1003,8 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
_propagator = QSharedPointer<OwncloudPropagator>(
new OwncloudPropagator (_account, _localPath, _remotePath, _journal));
connect(_propagator.data(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)),
this, SLOT(slotItemCompleted(const SyncFileItem &, const PropagatorJob &)));
connect(_propagator.data(), SIGNAL(itemCompleted(const SyncFileItemPtr &)),
this, SLOT(slotItemCompleted(const SyncFileItemPtr &)));
connect(_propagator.data(), SIGNAL(progress(const SyncFileItem &,quint64)),
this, SLOT(slotProgress(const SyncFileItem &,quint64)));
connect(_propagator.data(), SIGNAL(finished(bool)), this, SLOT(slotFinished(bool)), Qt::QueuedConnection);
@@ -1005,16 +1014,16 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
// apply the network limits to the propagator
setNetworkLimits(_uploadLimit, _downloadLimit);
deleteStaleDownloadInfos();
deleteStaleUploadInfos();
deleteStaleErrorBlacklistEntries();
deleteStaleDownloadInfos(syncItems);
deleteStaleUploadInfos(syncItems);
deleteStaleErrorBlacklistEntries(syncItems);
_journal->commit("post stale entry removal");
// Emit the started signal only after the propagator has been set up.
if (_needsUpdate)
emit(started());
_propagator->start(_syncedItems);
_propagator->start(syncItems);
qDebug() << "<<#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QLatin1String("Post-Reconcile Finished"));
}
@@ -1051,19 +1060,19 @@ void SyncEngine::setNetworkLimits(int upload, int download)
}
}
void SyncEngine::slotItemCompleted(const SyncFileItem &item, const PropagatorJob &job)
void SyncEngine::slotItemCompleted(const SyncFileItemPtr &item)
{
const char * instruction_str = csync_instruction_str(item._instruction);
qDebug() << Q_FUNC_INFO << item._file << instruction_str << item._status << item._errorString;
const char * instruction_str = csync_instruction_str(item->_instruction);
qDebug() << Q_FUNC_INFO << item->_file << instruction_str << item->_status << item->_errorString;
_progressInfo->setProgressComplete(item);
_progressInfo->setProgressComplete(*item);
if (item._status == SyncFileItem::FatalError) {
emit csyncError(item._errorString);
if (item->_status == SyncFileItem::FatalError) {
emit csyncError(item->_errorString);
}
emit transmissionProgress(*_progressInfo);
emit itemCompleted(item, job);
emit itemCompleted(item);
}
void SyncEngine::slotFinished(bool success)
@@ -1087,7 +1096,6 @@ void SyncEngine::slotFinished(bool success)
// files needed propagation
emit transmissionProgress(*_progressInfo);
emit treeWalkResult(_syncedItems);
finalize(success);
}
@@ -1108,6 +1116,10 @@ void SyncEngine::finalize(bool success)
// Delete the propagator only after emitting the signal.
_propagator.clear();
_remotePerms.clear();
_seenFiles.clear();
_temporarilyUnavailablePaths.clear();
_renamedFolders.clear();
_clearTouchedFilesTimer.start();
}
@@ -1137,13 +1149,13 @@ QString SyncEngine::adjustRenamedPath(const QString& original)
* Make sure that we are allowed to do what we do by checking the permissions and the selective sync list
*
*/
void SyncEngine::checkForPermission()
void SyncEngine::checkForPermission(SyncFileItemVector &syncItems)
{
bool selectiveListOk;
auto selectiveSyncBlackList = _journal->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList, &selectiveListOk);
std::sort(selectiveSyncBlackList.begin(), selectiveSyncBlackList.end());
for (SyncFileItemVector::iterator it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
for (SyncFileItemVector::iterator it = syncItems.begin(); it != syncItems.end(); ++it) {
if ((*it)->_direction != SyncFileItem::Up) {
// Currently we only check server-side permissions
continue;
@@ -1160,7 +1172,7 @@ void SyncEngine::checkForPermission()
(*it)->_errorString = tr("Ignored because of the \"choose what to sync\" blacklist");
if ((*it)->_isDirectory) {
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
for (SyncFileItemVector::iterator it_next = it + 1; it_next != syncItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
it = it_next;
(*it)->_instruction = CSYNC_INSTRUCTION_IGNORE;
(*it)->_status = SyncFileItem::FileIgnored;
@@ -1185,7 +1197,7 @@ void SyncEngine::checkForPermission()
(*it)->_status = SyncFileItem::NormalError;
(*it)->_errorString = tr("Not allowed because you don't have permission to add subfolders to that folder");
for (SyncFileItemVector::iterator it_next = it + 1; it_next != _syncedItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
for (SyncFileItemVector::iterator it_next = it + 1; it_next != syncItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
it = it_next;
if ((*it)->_instruction == CSYNC_INSTRUCTION_RENAME) {
// The file was most likely moved in this directory.
@@ -1245,7 +1257,7 @@ void SyncEngine::checkForPermission()
if ((*it)->_isDirectory) {
// restore all sub items
for (SyncFileItemVector::iterator it_next = it + 1;
it_next != _syncedItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
it_next != syncItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
it = it_next;
if ((*it)->_instruction != CSYNC_INSTRUCTION_REMOVE) {
@@ -1271,12 +1283,12 @@ void SyncEngine::checkForPermission()
// underneath, propagator sees that.
if( (*it)->_isDirectory ) {
// put a more descriptive message if a top level share dir really is removed.
if( it == _syncedItems.begin() || !(path.startsWith((*(it-1))->_file)) ) {
if( it == syncItems.begin() || !(path.startsWith((*(it-1))->_file)) ) {
(*it)->_errorString = tr("Local files and share folder removed.");
}
for (SyncFileItemVector::iterator it_next = it + 1;
it_next != _syncedItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
it_next != syncItems.end() && (*it_next)->_file.startsWith(path); ++it_next) {
it = it_next;
}
}
@@ -1355,7 +1367,7 @@ void SyncEngine::checkForPermission()
if ((*it)->_isDirectory) {
for (SyncFileItemVector::iterator it_next = it + 1;
it_next != _syncedItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
it_next != syncItems.end() && (*it_next)->destination().startsWith(path); ++it_next) {
it = it_next;
(*it)->_instruction = CSYNC_INSTRUCTION_ERROR;
(*it)->_status = SyncFileItem::NormalError;
@@ -1384,7 +1396,7 @@ QByteArray SyncEngine::getPermissions(const QString& file) const
return _remotePerms.value(file);
}
void SyncEngine::restoreOldFiles()
void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems)
{
/* When the server is trying to send us lots of file in the past, this means that a backup
was restored in the server. In that case, we should not simply overwrite the newer file
@@ -1392,7 +1404,7 @@ void SyncEngine::restoreOldFiles()
upload the client file. But we still downloaded the old file in a conflict file just in case
*/
for (auto it = _syncedItems.begin(); it != _syncedItems.end(); ++it) {
for (auto it = syncItems.begin(); it != syncItems.end(); ++it) {
if ((*it)->_direction != SyncFileItem::Down)
continue;
+10 -22
Ver Arquivo
@@ -47,7 +47,6 @@ namespace OCC {
class SyncJournalFileRecord;
class SyncJournalDb;
class OwncloudPropagator;
class PropagatorJob;
enum AnotherSyncNeeded
{
@@ -78,10 +77,7 @@ public:
bool isSyncRunning() const { return _syncRunning; }
/* Set the maximum size a folder can have without asking for confirmation
* -1 means infinite
*/
void setNewBigFolderSizeLimit(qint64 limit) { _newBigFolderSizeLimit = limit; }
void setSyncOptions(const SyncOptions &options) { _syncOptions = options; }
bool ignoreHiddenFiles() const { return _csync_ctx->ignore_hidden_files; }
void setIgnoreHiddenFiles(bool ignore) { _csync_ctx->ignore_hidden_files = ignore; }
@@ -122,10 +118,7 @@ signals:
void aboutToPropagate(SyncFileItemVector&);
// after each item completed by a job (successful or not)
void itemCompleted(const SyncFileItem&, const PropagatorJob&);
// after sync is done
void treeWalkResult(const SyncFileItemVector&);
void itemCompleted(const SyncFileItemPtr&);
void transmissionProgress( const ProgressInfo& progress );
@@ -146,7 +139,7 @@ signals:
void aboutToRestoreBackup(bool *restore);
// A new folder was discovered and was not synced because of the confirmation feature
void newBigFolder(const QString &folder);
void newBigFolder(const QString &folder, bool isExternal);
/** Emitted when propagation has problems with a locked file.
*
@@ -156,7 +149,7 @@ signals:
private slots:
void slotRootEtagReceived(const QString &);
void slotItemCompleted(const SyncFileItem& item, const PropagatorJob & job);
void slotItemCompleted(const SyncFileItemPtr& item);
void slotFinished(bool success);
void slotProgress(const SyncFileItem& item, quint64 curent);
void slotDiscoveryJobFinished(int updateResult);
@@ -180,13 +173,13 @@ private:
// Cleans up unnecessary downloadinfo entries in the journal as well
// as their temporary files.
void deleteStaleDownloadInfos();
void deleteStaleDownloadInfos(const SyncFileItemVector &syncItems);
// Removes stale uploadinfos from the journal.
void deleteStaleUploadInfos();
void deleteStaleUploadInfos(const SyncFileItemVector &syncItems);
// Removes stale error blacklist entries from the journal.
void deleteStaleErrorBlacklistEntries();
void deleteStaleErrorBlacklistEntries(const SyncFileItemVector &syncItems);
// cleanup and emit the finished signal
void finalize(bool success);
@@ -196,10 +189,6 @@ private:
// Must only be acessed during update and reconcile
QMap<QString, SyncFileItemPtr> _syncItemMap;
// should be called _syncItems (present tense). It's the items from the _syncItemMap but
// sorted and re-adjusted based on permissions.
SyncFileItemVector _syncedItems;
AccountPtr _account;
CSYNC *_csync_ctx;
bool _needsUpdate;
@@ -241,13 +230,13 @@ private:
* check if we are allowed to propagate everything, and if we are not, adjust the instructions
* to recover
*/
void checkForPermission();
void checkForPermission(SyncFileItemVector &syncItems);
QByteArray getPermissions(const QString& file) const;
/**
* Instead of downloading files from the server, upload the files to the server
*/
void restoreOldFiles();
void restoreOldFiles(SyncFileItemVector &syncItems);
bool _hasNoneFiles; // true if there is at least one file which was not changed on the server
bool _hasRemoveFile; // true if there is at leasr one file with instruction REMOVE
@@ -257,8 +246,7 @@ private:
int _uploadLimit;
int _downloadLimit;
/* maximum size a folder can have without asking for confirmation: -1 means infinite */
qint64 _newBigFolderSizeLimit;
SyncOptions _syncOptions;
// hash containing the permissions on the remote directory
QHash<QString, QByteArray> _remotePerms;
+2 -7
Ver Arquivo
@@ -68,7 +68,7 @@ public:
_serverHasIgnoredFiles(false), _hasBlacklistEntry(false),
_errorMayBeBlacklisted(false), _status(NoStatus),
_isRestoration(false),
_httpErrorCode(0), _requestDuration(0), _affectedItems(1),
_httpErrorCode(0), _affectedItems(1),
_instruction(CSYNC_INSTRUCTION_NONE), _modtime(0), _size(0), _inode(0)
{
}
@@ -160,7 +160,6 @@ public:
quint16 _httpErrorCode;
QString _errorString; // Contains a string only in case of error
QByteArray _responseTimeStamp;
quint64 _requestDuration;
quint32 _affectedItems; // the number of affected items by the operation on this item.
// usually this value is 1, but for removes on dirs, it might be much higher.
@@ -179,15 +178,10 @@ public:
QString _directDownloadCookies;
struct {
quint64 _size;
time_t _modtime;
QByteArray _etag;
QByteArray _fileId;
quint64 _other_size;
time_t _other_modtime;
QByteArray _other_etag;
QByteArray _other_fileId;
enum csync_instructions_e _instruction BITFIELD(16);
enum csync_instructions_e _other_instruction BITFIELD(16);
} log;
};
@@ -202,5 +196,6 @@ typedef QVector<SyncFileItemPtr> SyncFileItemVector;
}
Q_DECLARE_METATYPE(OCC::SyncFileItem)
Q_DECLARE_METATYPE(OCC::SyncFileItemPtr)
#endif // SYNCFILEITEM_H
+24 -23
Ver Arquivo
@@ -17,6 +17,7 @@
#include "syncengine.h"
#include "syncjournaldb.h"
#include "syncjournalfilerecord.h"
#include "asserts.h"
namespace OCC {
@@ -77,8 +78,8 @@ SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine)
{
connect(syncEngine, SIGNAL(aboutToPropagate(SyncFileItemVector&)),
SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(syncEngine, SIGNAL(itemCompleted(const SyncFileItem&, const PropagatorJob&)),
SLOT(slotItemCompleted(const SyncFileItem&)));
connect(syncEngine, SIGNAL(itemCompleted(const SyncFileItemPtr&)),
SLOT(slotItemCompleted(const SyncFileItemPtr&)));
connect(syncEngine, SIGNAL(finished(bool)), SLOT(slotSyncFinished()));
connect(syncEngine, SIGNAL(started()), SLOT(slotSyncEngineRunningChanged()));
connect(syncEngine, SIGNAL(finished(bool)), SLOT(slotSyncEngineRunningChanged()));
@@ -86,7 +87,7 @@ SyncFileStatusTracker::SyncFileStatusTracker(SyncEngine *syncEngine)
SyncFileStatus SyncFileStatusTracker::fileStatus(const QString& relativePath)
{
Q_ASSERT(!relativePath.endsWith(QLatin1Char('/')));
ASSERT(!relativePath.endsWith(QLatin1Char('/')));
if (relativePath.isEmpty()) {
// This is the root sync folder, it doesn't have an entry in the database and won't be walked by csync, so resolve manually.
@@ -121,8 +122,8 @@ SyncFileStatus SyncFileStatusTracker::fileStatus(const QString& relativePath)
void SyncFileStatusTracker::slotPathTouched(const QString& fileName)
{
QString folderPath = _syncEngine->localPath();
Q_ASSERT(fileName.startsWith(folderPath));
ASSERT(fileName.startsWith(folderPath));
QString localPath = fileName.mid(folderPath.size());
_dirtyPaths.insert(localPath);
@@ -141,7 +142,7 @@ void SyncFileStatusTracker::incSyncCountAndEmitStatusChanged(const QString &rela
// We passed from OK to SYNC, increment the parent to keep it marked as
// SYNC while we propagate ourselves and our own children.
Q_ASSERT(!relativePath.endsWith('/'));
ASSERT(!relativePath.endsWith('/'));
int lastSlashIndex = relativePath.lastIndexOf('/');
if (lastSlashIndex != -1)
incSyncCountAndEmitStatusChanged(relativePath.left(lastSlashIndex), UnknownShared);
@@ -163,7 +164,7 @@ void SyncFileStatusTracker::decSyncCountAndEmitStatusChanged(const QString &rela
emit fileStatusChanged(getSystemDestination(relativePath), status);
// We passed from SYNC to OK, decrement our parent.
Q_ASSERT(!relativePath.endsWith('/'));
ASSERT(!relativePath.endsWith('/'));
int lastSlashIndex = relativePath.lastIndexOf('/');
if (lastSlashIndex != -1)
decSyncCountAndEmitStatusChanged(relativePath.left(lastSlashIndex), UnknownShared);
@@ -174,7 +175,7 @@ void SyncFileStatusTracker::decSyncCountAndEmitStatusChanged(const QString &rela
void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
{
Q_ASSERT(_syncCount.isEmpty());
ASSERT(_syncCount.isEmpty());
std::map<QString, SyncFileStatus::SyncFileStatusTag> oldProblems;
std::swap(_syncProblems, oldProblems);
@@ -223,28 +224,28 @@ void SyncFileStatusTracker::slotAboutToPropagate(SyncFileItemVector& items)
}
}
void SyncFileStatusTracker::slotItemCompleted(const SyncFileItem &item)
void SyncFileStatusTracker::slotItemCompleted(const SyncFileItemPtr &item)
{
// qDebug() << Q_FUNC_INFO << item.destination() << item._status << item._instruction;
if (showErrorInSocketApi(item)) {
_syncProblems[item._file] = SyncFileStatus::StatusError;
invalidateParentPaths(item.destination());
} else if (showWarningInSocketApi(item)) {
_syncProblems[item._file] = SyncFileStatus::StatusWarning;
if (showErrorInSocketApi(*item)) {
_syncProblems[item->_file] = SyncFileStatus::StatusError;
invalidateParentPaths(item->destination());
} else if (showWarningInSocketApi(*item)) {
_syncProblems[item->_file] = SyncFileStatus::StatusWarning;
} else {
_syncProblems.erase(item._file);
_syncProblems.erase(item->_file);
}
SharedFlag sharedFlag = item._remotePerm.contains("S") ? Shared : NotShared;
if (item._instruction != CSYNC_INSTRUCTION_NONE
&& item._instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
&& item._instruction != CSYNC_INSTRUCTION_IGNORE
&& item._instruction != CSYNC_INSTRUCTION_ERROR) {
SharedFlag sharedFlag = item->_remotePerm.contains("S") ? Shared : NotShared;
if (item->_instruction != CSYNC_INSTRUCTION_NONE
&& item->_instruction != CSYNC_INSTRUCTION_UPDATE_METADATA
&& item->_instruction != CSYNC_INSTRUCTION_IGNORE
&& item->_instruction != CSYNC_INSTRUCTION_ERROR) {
// decSyncCount calls *must* be symetric with incSyncCount calls in slotAboutToPropagate
decSyncCountAndEmitStatusChanged(item.destination(), sharedFlag);
decSyncCountAndEmitStatusChanged(item->destination(), sharedFlag);
} else {
emit fileStatusChanged(getSystemDestination(item.destination()), resolveSyncAndErrorStatus(item.destination(), sharedFlag));
emit fileStatusChanged(getSystemDestination(item->destination()), resolveSyncAndErrorStatus(item->destination(), sharedFlag));
}
}
@@ -277,8 +278,8 @@ SyncFileStatus SyncFileStatusTracker::resolveSyncAndErrorStatus(const QString &r
status.set(problemStatus);
}
// The shared status needs to have been fetched from a SyncFileItem or the DB at this point.
Q_ASSERT(sharedFlag != UnknownShared);
ASSERT(sharedFlag != UnknownShared,
"The shared status needs to have been fetched from a SyncFileItem or the DB at this point.");
if (sharedFlag == Shared)
status.setSharedWithMe(true);
+1 -1
Ver Arquivo
@@ -46,7 +46,7 @@ signals:
private slots:
void slotAboutToPropagate(SyncFileItemVector& items);
void slotItemCompleted(const SyncFileItem& item);
void slotItemCompleted(const SyncFileItemPtr& item);
void slotSyncFinished();
void slotSyncEngineRunningChanged();
+11 -7
Ver Arquivo
@@ -27,6 +27,7 @@
#include "utility.h"
#include "version.h"
#include "filesystem.h"
#include "asserts.h"
#include "../../csync/src/std/c_jhash.h"
@@ -178,7 +179,7 @@ bool SyncJournalDb::sqlFail( const QString& log, const SqlQuery& query )
{
commitTransaction();
qWarning() << "SQL Error" << log << query.error();
Q_ASSERT(!"SQL ERROR");
ASSERT(false);
_db.close();
return false;
}
@@ -1370,21 +1371,22 @@ void SyncJournalDb::setUploadInfo(const QString& file, const SyncJournalDb::Uplo
}
}
bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
QVector<uint> SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
{
QMutexLocker locker(&_mutex);
QVector<uint> ids;
if (!checkConnect()) {
return false;
return ids;
}
SqlQuery query(_db);
query.prepare("SELECT path FROM uploadinfo");
query.prepare("SELECT path,transferid FROM uploadinfo");
if (!query.exec()) {
QString err = query.error();
qDebug() << "Error creating prepared statement: " << query.lastQuery() << ", Error:" << err;
return false;
return ids;
}
QStringList superfluousPaths;
@@ -1393,10 +1395,12 @@ bool SyncJournalDb::deleteStaleUploadInfos(const QSet<QString> &keep)
const QString file = query.stringValue(0);
if (!keep.contains(file)) {
superfluousPaths.append(file);
ids.append(query.intValue(1));
}
}
return deleteBatch(*_deleteUploadInfoQuery, superfluousPaths, "uploadinfo");
deleteBatch(*_deleteUploadInfoQuery, superfluousPaths, "uploadinfo");
return ids;
}
SyncJournalErrorBlacklistRecord SyncJournalDb::errorBlacklistEntry( const QString& file )
@@ -1604,7 +1608,7 @@ void SyncJournalDb::setPollInfo(const SyncJournalDb::PollInfo& info)
QStringList SyncJournalDb::getSelectiveSyncList(SyncJournalDb::SelectiveSyncListType type, bool *ok )
{
QStringList result;
Q_ASSERT(ok);
ASSERT(ok);
QMutexLocker locker(&_mutex);
if( !checkConnect() ) {
+2 -1
Ver Arquivo
@@ -105,7 +105,8 @@ public:
UploadInfo getUploadInfo(const QString &file);
void setUploadInfo(const QString &file, const UploadInfo &i);
bool deleteStaleUploadInfos(const QSet<QString>& keep);
// Return the list of transfer ids that were removed.
QVector<uint> deleteStaleUploadInfos(const QSet<QString>& keep);
SyncJournalErrorBlacklistRecord errorBlacklistEntry( const QString& );
bool deleteStaleErrorBlacklistEntries(const QSet<QString>& keep);
+77 -34
Ver Arquivo
@@ -13,19 +13,22 @@
*/
#include "syncresult.h"
#include "progressdispatcher.h"
namespace OCC
{
SyncResult::SyncResult()
: _status( Undefined ),
_warnCount(0)
{
}
: _status( Undefined )
, _foundFilesNotSynced(false)
, _folderStructureWasChanged(false)
, _numNewItems(0)
, _numRemovedItems(0)
, _numUpdatedItems(0)
, _numRenamedItems(0)
, _numConflictItems(0)
, _numErrorItems(0)
SyncResult::SyncResult(SyncResult::Status status )
: _status(status),
_warnCount(0)
{
}
@@ -34,6 +37,11 @@ SyncResult::Status SyncResult::status() const
return _status;
}
void SyncResult::reset()
{
*this = SyncResult();
}
QString SyncResult::statusString() const
{
QString re;
@@ -80,42 +88,17 @@ void SyncResult::setStatus( Status stat )
_syncTime = QDateTime::currentDateTime();
}
void SyncResult::setSyncFileItemVector( const SyncFileItemVector& items )
{
_syncItems = items;
}
SyncFileItemVector SyncResult::syncFileItemVector() const
{
return _syncItems;
}
QDateTime SyncResult::syncTime() const
{
return _syncTime;
}
void SyncResult::setWarnCount(int wc)
{
_warnCount = wc;
}
int SyncResult::warnCount() const
{
return _warnCount;
}
void SyncResult::setErrorStrings( const QStringList& list )
{
_errors = list;
}
QStringList SyncResult::errorStrings() const
{
return _errors;
}
void SyncResult::setErrorString( const QString& err )
void SyncResult::appendErrorString( const QString& err )
{
_errors.append( err );
}
@@ -141,8 +124,68 @@ QString SyncResult::folder() const
return _folder;
}
SyncResult::~SyncResult()
void SyncResult::processCompletedItem(const SyncFileItemPtr &item)
{
if (Progress::isWarningKind(item->_status)) {
// Count any error conditions, error strings will have priority anyway.
_foundFilesNotSynced = true;
}
if (item->_isDirectory && (item->_instruction == CSYNC_INSTRUCTION_NEW
|| item->_instruction == CSYNC_INSTRUCTION_TYPE_CHANGE
|| item->_instruction == CSYNC_INSTRUCTION_REMOVE
|| item->_instruction == CSYNC_INSTRUCTION_RENAME)) {
_folderStructureWasChanged = true;
}
// Process the item to the gui
if( item->_status == SyncFileItem::FatalError || item->_status == SyncFileItem::NormalError ) {
//: this displays an error string (%2) for a file %1
appendErrorString( QObject::tr("%1: %2").arg(item->_file, item->_errorString) );
_numErrorItems++;
if (!_firstItemError) {
_firstItemError = item;
}
} else if( item->_status == SyncFileItem::Conflict ) {
_numConflictItems++;
if (!_firstConflictItem) {
_firstConflictItem = item;
}
} else {
if (!item->hasErrorStatus() && item->_status != SyncFileItem::FileIgnored && item->_direction == SyncFileItem::Down) {
switch (item->_instruction) {
case CSYNC_INSTRUCTION_NEW:
case CSYNC_INSTRUCTION_TYPE_CHANGE:
_numNewItems++;
if (!_firstItemNew)
_firstItemNew = item;
break;
case CSYNC_INSTRUCTION_REMOVE:
_numRemovedItems++;
if (!_firstItemDeleted)
_firstItemDeleted = item;
break;
case CSYNC_INSTRUCTION_SYNC:
_numUpdatedItems++;
if (!_firstItemUpdated)
_firstItemUpdated = item;
break;
case CSYNC_INSTRUCTION_RENAME:
if (!_firstItemRenamed) {
_firstItemRenamed = item;
}
_numRenamedItems++;
break;
default:
// nothing.
break;
}
} else if( item->_direction == SyncFileItem::None ) {
if( item->_instruction == CSYNC_INSTRUCTION_IGNORE ) {
_foundFilesNotSynced = true;
}
}
}
}
+39 -11
Ver Arquivo
@@ -47,20 +47,13 @@ public:
};
SyncResult();
SyncResult( Status status );
~SyncResult();
void setErrorString( const QString& );
void setErrorStrings( const QStringList& );
void reset();
void appendErrorString( const QString& );
QString errorString() const;
QStringList errorStrings() const;
int warnCount() const;
void setWarnCount(int wc);
void clearErrors();
// handle a list of changed items.
void setSyncFileItemVector( const SyncFileItemVector& );
SyncFileItemVector syncFileItemVector() const;
void setStatus( Status );
Status status() const;
QString statusString() const;
@@ -68,6 +61,25 @@ public:
void setFolder(const QString& folder);
QString folder() const;
bool foundFilesNotSynced() const { return _foundFilesNotSynced; }
bool folderStructureWasChanged() const { return _folderStructureWasChanged; }
int numNewItems() const { return _numNewItems; }
int numRemovedItems() const { return _numRemovedItems; }
int numUpdatedItems() const { return _numUpdatedItems; }
int numRenamedItems() const { return _numRenamedItems; }
int numConflictItems() const { return _numConflictItems; }
int numErrorItems() const { return _numErrorItems; }
const SyncFileItemPtr& firstItemNew() const { return _firstItemNew; }
const SyncFileItemPtr& firstItemDeleted() const { return _firstItemDeleted; }
const SyncFileItemPtr& firstItemUpdated() const { return _firstItemUpdated; }
const SyncFileItemPtr& firstItemRenamed() const { return _firstItemRenamed; }
const SyncFileItemPtr& firstConflictItem() const { return _firstConflictItem; }
const SyncFileItemPtr& firstItemError() const { return _firstItemError; }
void processCompletedItem(const SyncFileItemPtr &item);
private:
Status _status;
SyncFileItemVector _syncItems;
@@ -77,7 +89,23 @@ private:
* when the sync tool support this...
*/
QStringList _errors;
int _warnCount;
bool _foundFilesNotSynced;
bool _folderStructureWasChanged;
// count new, removed and updated items
int _numNewItems;
int _numRemovedItems;
int _numUpdatedItems;
int _numRenamedItems;
int _numConflictItems;
int _numErrorItems;
SyncFileItemPtr _firstItemNew;
SyncFileItemPtr _firstItemDeleted;
SyncFileItemPtr _firstItemUpdated;
SyncFileItemPtr _firstItemRenamed;
SyncFileItemPtr _firstConflictItem;
SyncFileItemPtr _firstItemError;
};
}
+12 -1
Ver Arquivo
@@ -275,6 +275,15 @@ qint64 Theme::newBigFolderSizeLimit() const
return 500;
}
bool Theme::wizardHideExternalStorageConfirmationCheckbox() const
{
return false;
}
bool Theme::wizardHideFolderSizeLimitCheckbox() const
{
return false;
}
QString Theme::gitSHA1() const
{
@@ -412,7 +421,7 @@ QPixmap Theme::wizardHeaderBanner() const
if (!c.isValid())
return QPixmap();
QPixmap pix(QSize(600, 78));
QPixmap pix(QSize(750, 78));
pix.fill(wizardHeaderBackgroundColor());
return pix;
}
@@ -478,4 +487,6 @@ QString Theme::quotaBaseFolder() const
{
return QLatin1String("/");
}
} // end namespace client
+12
Ver Arquivo
@@ -219,6 +219,17 @@ public:
**/
virtual qint64 newBigFolderSizeLimit() const;
/**
* Hide the checkbox that says "Ask for confirmation before synchronizing folders larger than X MB"
* in the account wizard
*/
virtual bool wizardHideFolderSizeLimitCheckbox() const;
/**
* Hide the checkbox that says "Ask for confirmation before synchronizing external storages"
* in the account wizard
*/
virtual bool wizardHideExternalStorageConfirmationCheckbox() const;
/**
* Alternative path on the server that provides access to the webdav capabilities
*
@@ -302,6 +313,7 @@ public:
*/
virtual QString quotaBaseFolder() const;
protected:
#ifndef TOKEN_AUTH_ONLY
QIcon themeIcon(const QString& name, bool sysTray = false, bool sysTrayMenuVisible = false) const;
+7 -8
Ver Arquivo
@@ -273,14 +273,13 @@ void Utility::usleep(int usec)
bool Utility::fsCasePreserving()
{
bool re = false;
if( isWindows() || isMac() ) {
re = true;
} else {
bool isTest = qgetenv("OWNCLOUD_TEST_CASE_PRESERVING").toInt();
re = isTest;
}
return re;
#ifndef WITH_TESTING
QByteArray env = qgetenv("OWNCLOUD_TEST_CASE_PRESERVING");
if (!env.isEmpty())
return env.toInt();
#endif
return isWindows() || isMac();
}
bool Utility::fileNamesEqual( const QString& fn1, const QString& fn2)
+9 -15
Ver Arquivo
@@ -2,7 +2,6 @@ include_directories(${CMAKE_BINARY_DIR}/csync ${CMAKE_BINARY_DIR}/csync/src ${CM
include_directories(${CMAKE_SOURCE_DIR}/csync/src/)
include_directories(${CMAKE_SOURCE_DIR}/csync/src/std ${CMAKE_SOURCE_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/qtokenizer)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
include(QtVersionAbstraction)
setup_qt()
@@ -10,7 +9,6 @@ setup_qt()
include(owncloud_add_test.cmake)
owncloud_add_test(OwncloudPropagator "")
owncloud_add_test(Utility "")
owncloud_add_test(Updater "")
SET(FolderWatcher_SRC ../src/gui/folderwatcher.cpp)
@@ -25,31 +23,30 @@ IF( APPLE )
list(APPEND FolderWatcher_SRC ../src/gui/folderwatcher_mac.cpp)
list(APPEND FolderWatcher_SRC ../src/gui/socketapisocket_mac.mm)
ENDIF()
owncloud_add_test(FolderWatcher "${FolderWatcher_SRC}")
if( UNIX AND NOT APPLE )
if(HAVE_QT5 AND NOT BUILD_WITH_QT4)
owncloud_add_test(InotifyWatcher "${FolderWatcher_SRC}")
endif(HAVE_QT5 AND NOT BUILD_WITH_QT4)
endif(UNIX AND NOT APPLE)
owncloud_add_test(CSyncSqlite "")
owncloud_add_test(NetrcParser ../src/cmd/netrcparser.cpp)
owncloud_add_test(OwnSql "")
owncloud_add_test(SyncJournalDB "")
owncloud_add_test(SyncFileItem "")
owncloud_add_test(ConcatUrl "")
owncloud_add_test(XmlParse "")
owncloud_add_test(FileSystem "")
owncloud_add_test(ChecksumValidator "")
owncloud_add_test(ExcludedFiles "")
if(HAVE_QT5 AND NOT BUILD_WITH_QT4)
owncloud_add_test(FileSystem "")
owncloud_add_test(Utility "")
owncloud_add_test(SyncEngine "syncenginetestutils.h")
owncloud_add_test(SyncFileStatusTracker "syncenginetestutils.h")
owncloud_add_test(ChunkingNg "syncenginetestutils.h")
owncloud_add_test(UploadReset "syncenginetestutils.h")
owncloud_add_test(FolderWatcher "${FolderWatcher_SRC}")
if( UNIX AND NOT APPLE )
owncloud_add_test(InotifyWatcher "${FolderWatcher_SRC}")
endif(UNIX AND NOT APPLE)
owncloud_add_benchmark(LargeSync "syncenginetestutils.h")
endif(HAVE_QT5 AND NOT BUILD_WITH_QT4)
SET(FolderMan_SRC ../src/gui/folderman.cpp)
@@ -60,8 +57,5 @@ list(APPEND FolderMan_SRC ../src/gui/syncrunfilelog.cpp )
list(APPEND FolderMan_SRC ../src/gui/lockwatcher.cpp )
list(APPEND FolderMan_SRC ${FolderWatcher_SRC})
list(APPEND FolderMan_SRC stub.cpp )
#include_directories(${QTKEYCHAIN_INCLUDE_DIR})
#include_directories(${CMAKE_BINARY_DIR}/src/gui)
#include_directories(${CMAKE_SOURCE_DIR}/src/3rdparty/qjson)
owncloud_add_test(FolderMan "${FolderMan_SRC}")
+43
Ver Arquivo
@@ -0,0 +1,43 @@
/*
* This software is in the public domain, furnished "as is", without technical
* support, and with no warranty, express or implied, as to its usefulness for
* any purpose.
*
*/
#include "syncenginetestutils.h"
#include <syncengine.h>
using namespace OCC;
int numDirs = 0;
int numFiles = 0;
template<int filesPerDir, int dirPerDir, int maxDepth>
void addBunchOfFiles(int depth, const QString &path, FileModifier &fi) {
for (int fileNum = 1; fileNum <= filesPerDir; ++fileNum) {
QString name = QStringLiteral("file") + QString::number(fileNum);
fi.insert(path.isEmpty() ? name : path + "/" + name);
numFiles++;
}
if (depth >= maxDepth)
return;
for (char dirNum = 1; dirNum <= dirPerDir; ++dirNum) {
QString name = QStringLiteral("dir") + QString::number(dirNum);
QString subPath = path.isEmpty() ? name : path + "/" + name;
fi.mkdir(subPath);
numDirs++;
addBunchOfFiles<filesPerDir, dirPerDir, maxDepth>(depth + 1, subPath, fi);
}
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
FakeFolder fakeFolder{FileInfo{}};
addBunchOfFiles<10, 8, 4>(0, "", fakeFolder.localModifier());
qDebug() << "NUMFILES" << numFiles;
qDebug() << "NUMDIRS" << numDirs;
return fakeFolder.syncOnce() ? 0 : -1;
}
+29
Ver Arquivo
@@ -12,6 +12,7 @@ macro(owncloud_add_test test_class additional_cpp)
add_executable(${OWNCLOUD_TEST_CLASS}Test test${OWNCLOUD_TEST_CLASS_LOWERCASE}.cpp ${additional_cpp})
qt5_use_modules(${OWNCLOUD_TEST_CLASS}Test Test Sql Xml Network)
set_target_properties(${OWNCLOUD_TEST_CLASS}Test PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY})
target_link_libraries(${OWNCLOUD_TEST_CLASS}Test
updater
@@ -24,3 +25,31 @@ macro(owncloud_add_test test_class additional_cpp)
add_definitions(-DOWNCLOUD_BIN_PATH=${CMAKE_BINARY_DIR}/bin)
add_test(NAME ${OWNCLOUD_TEST_CLASS}Test COMMAND ${OWNCLOUD_TEST_CLASS}Test)
endmacro()
macro(owncloud_add_benchmark test_class additional_cpp)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
${QT_INCLUDES}
"${PROJECT_SOURCE_DIR}/src/gui"
"${PROJECT_SOURCE_DIR}/src/libsync"
"${CMAKE_BINARY_DIR}/src/libsync"
"${CMAKE_CURRENT_BINARY_DIR}"
)
set(CMAKE_AUTOMOC TRUE)
set(OWNCLOUD_TEST_CLASS ${test_class})
string(TOLOWER "${OWNCLOUD_TEST_CLASS}" OWNCLOUD_TEST_CLASS_LOWERCASE)
add_executable(${OWNCLOUD_TEST_CLASS}Bench benchmarks/bench${OWNCLOUD_TEST_CLASS_LOWERCASE}.cpp ${additional_cpp})
qt5_use_modules(${OWNCLOUD_TEST_CLASS}Bench Test Sql Xml Network)
set_target_properties(${OWNCLOUD_TEST_CLASS}Bench PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY})
target_link_libraries(${OWNCLOUD_TEST_CLASS}Bench
updater
${APPLICATION_EXECUTABLE}sync
${QT_QTTEST_LIBRARY}
${QT_QTCORE_LIBRARY}
)
add_definitions(-DOWNCLOUD_TEST)
add_definitions(-DOWNCLOUD_BIN_PATH=${CMAKE_BINARY_DIR}/bin)
endmacro()
+53 -27
Ver Arquivo
@@ -226,7 +226,7 @@ public:
etag = file->etag;
return file;
}
return nullptr;
return 0;
}
FileInfo *createDir(const QString &relativePath) {
@@ -267,6 +267,15 @@ public:
return (parentPath.isEmpty() ? QString() : (parentPath + '/')) + name;
}
void fixupParentPathRecursively() {
auto p = path();
for (auto it = children.begin(); it != children.end(); ++it) {
Q_ASSERT(it.key() == it->name);
it->parentPath = p;
it->fixupParentPathRecursively();
}
}
QString name;
bool isDir = true;
bool isShared = false;
@@ -285,15 +294,6 @@ private:
return find(pathComponents, true);
}
void fixupParentPathRecursively() {
auto p = path();
for (auto it = children.begin(); it != children.end(); ++it) {
Q_ASSERT(it.key() == it->name);
it->parentPath = p;
it->fixupParentPathRecursively();
}
}
friend inline QDebug operator<<(QDebug dbg, const FileInfo& fi) {
return dbg << "{ " << fi.path() << ": " << fi.children;
}
@@ -315,7 +315,10 @@ public:
QString fileName = getFilePathFromUrl(request.url());
Q_ASSERT(!fileName.isNull()); // for root, it should be empty
const FileInfo *fileInfo = remoteRootFileInfo.find(fileName);
Q_ASSERT(fileInfo);
if (!fileInfo) {
QMetaObject::invokeMethod(this, "respond404", Qt::QueuedConnection);
return;
}
QString prefix = request.url().path().left(request.url().path().size() - fileName.size());
// Don't care about the request and just return a full propfind
@@ -342,7 +345,7 @@ public:
} else
xml.writeEmptyElement(davUri, QStringLiteral("resourcetype"));
auto gmtDate = fileInfo.lastModified.toTimeZone(QTimeZone("GMT"));
auto gmtDate = fileInfo.lastModified.toTimeZone(QTimeZone(0));
auto stringDate = gmtDate.toString("ddd, dd MMM yyyy HH:mm:ss 'GMT'");
xml.writeTextElement(davUri, QStringLiteral("getlastmodified"), stringDate);
xml.writeTextElement(davUri, QStringLiteral("getcontentlength"), QString::number(fileInfo.size));
@@ -375,6 +378,13 @@ public:
emit finished();
}
Q_INVOKABLE void respond404() {
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 404);
setError(InternalServerError, "Not Found");
emit metaDataChanged();
emit finished();
}
void abort() override { }
qint64 bytesAvailable() const override { return payload.size() + QIODevice::bytesAvailable(); }
@@ -524,7 +534,8 @@ class FakeGetReply : public QNetworkReply
Q_OBJECT
public:
const FileInfo *fileInfo;
QByteArray payload;
char payload;
int size;
FakeGetReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
: QNetworkReply{parent} {
@@ -540,8 +551,9 @@ public:
}
Q_INVOKABLE void respond() {
payload.fill(fileInfo->contentChar, fileInfo->size);
setHeader(QNetworkRequest::ContentLengthHeader, payload.size());
payload = fileInfo->contentChar;
size = fileInfo->size;
setHeader(QNetworkRequest::ContentLengthHeader, size);
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
setRawHeader("OC-ETag", fileInfo->etag.toLatin1());
setRawHeader("ETag", fileInfo->etag.toLatin1());
@@ -553,12 +565,12 @@ public:
}
void abort() override { }
qint64 bytesAvailable() const override { return payload.size() + QIODevice::bytesAvailable(); }
qint64 bytesAvailable() const override { return size + QIODevice::bytesAvailable(); }
qint64 readData(char *data, qint64 maxlen) override {
qint64 len = std::min(qint64{payload.size()}, maxlen);
strncpy(data, payload.constData(), len);
payload.remove(0, len);
qint64 len = std::min(qint64{size}, maxlen);
std::fill_n(data, len, payload);
size -= len;
return len;
}
};
@@ -586,7 +598,7 @@ public:
Q_ASSERT(sourceFolder->isDir);
int count = 0;
int size = 0;
char payload = '*';
char payload = '\0';
do {
QString chunkName = QString::number(count).rightJustified(8, '0');
@@ -596,6 +608,7 @@ public:
Q_ASSERT(!x.isDir);
Q_ASSERT(x.size > 0); // There should not be empty chunks
size += x.size;
Q_ASSERT(!payload || payload == x.contentChar);
payload = x.contentChar;
++count;
} while(true);
@@ -607,7 +620,12 @@ public:
Q_ASSERT(!fileName.isEmpty());
if ((fileInfo = remoteRootFileInfo.find(fileName))) {
QCOMPARE(request.rawHeader("If"), QByteArray("<" + request.rawHeader("Destination") + "> ([\"" + fileInfo->etag.toLatin1() + "\"])"));
QVERIFY(request.hasRawHeader("If")); // The client should put this header
if (request.rawHeader("If") != QByteArray("<" + request.rawHeader("Destination") +
"> ([\"" + fileInfo->etag.toLatin1() + "\"])")) {
QMetaObject::invokeMethod(this, "respondPreconditionFailed", Qt::QueuedConnection);
return;
}
fileInfo->size = size;
fileInfo->contentChar = payload;
} else {
@@ -632,6 +650,13 @@ public:
emit finished();
}
Q_INVOKABLE void respondPreconditionFailed() {
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 412);
setError(InternalServerError, "Precondition Failed");
emit metaDataChanged();
emit finished();
}
void abort() override { }
qint64 readData(char *, qint64) override { return 0; }
};
@@ -766,6 +791,7 @@ public:
QDir rootDir{_tempDir.path()};
FileInfo rootTemplate;
fromDisk(rootDir, rootTemplate);
rootTemplate.fixupParentPathRecursively();
return rootTemplate;
}
@@ -792,15 +818,15 @@ public:
}
void execUntilItemCompleted(const QString &relativePath) {
QSignalSpy spy(_syncEngine.get(), SIGNAL(itemCompleted(const SyncFileItem &, const PropagatorJob &)));
QSignalSpy spy(_syncEngine.get(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
QElapsedTimer t;
t.start();
while (t.elapsed() < 5000) {
spy.clear();
QVERIFY(spy.wait());
for(const QList<QVariant> &args : spy) {
auto item = args[0].value<OCC::SyncFileItem>();
if (item.destination() == relativePath)
auto item = args[0].value<OCC::SyncFileItemPtr>();
if (item->destination() == relativePath)
return;
}
}
@@ -809,7 +835,7 @@ public:
bool execUntilFinished() {
QSignalSpy spy(_syncEngine.get(), SIGNAL(finished(bool)));
bool ok = spy.wait(60000);
bool ok = spy.wait(3600000);
Q_ASSERT(ok && "Sync timed out");
return spy[0][0].toBool();
}
@@ -842,8 +868,8 @@ private:
if (diskChild.isDir()) {
QDir subDir = dir;
subDir.cd(diskChild.fileName());
templateFi.children.insert(diskChild.fileName(), FileInfo{diskChild.fileName()});
fromDisk(subDir, templateFi.children.last());
FileInfo &subFi = templateFi.children[diskChild.fileName()] = FileInfo{diskChild.fileName()};
fromDisk(subDir, subFi);
} else {
QFile f{diskChild.filePath()};
f.open(QFile::ReadOnly);
+8 -3
Ver Arquivo
@@ -55,7 +55,6 @@ using namespace OCC;
private slots:
void initTestCase() {
qDebug() << Q_FUNC_INFO;
_root = QDir::tempPath() + "/" + "test_" + QString::number(qrand());
QDir rootDir(_root);
@@ -65,7 +64,9 @@ using namespace OCC;
}
void testUploadChecksummingAdler() {
#ifndef ZLIB_FOUND
QSKIP("ZLIB not found.", SkipSingle);
#else
ComputeChecksum *vali = new ComputeChecksum(this);
_expectedType = "Adler32";
vali->setChecksumType(_expectedType);
@@ -81,6 +82,7 @@ using namespace OCC;
loop.exec();
delete vali;
#endif
}
void testUploadChecksummingMd5() {
@@ -119,7 +121,9 @@ using namespace OCC;
}
void testDownloadChecksummingAdler() {
#ifndef ZLIB_FOUND
QSKIP("ZLIB not found.", SkipSingle);
#else
QByteArray adler = checkSumAdlerC;
adler.append(":");
adler.append(FileSystem::calcAdler32( _testfile ));
@@ -143,6 +147,7 @@ using namespace OCC;
QTRY_VERIFY(_errorSeen);
delete vali;
#endif
}
+188 -23
Ver Arquivo
@@ -11,6 +11,36 @@
using namespace OCC;
/* Upload a 1/3 of a file of given size.
* fakeFolder needs to be synchronized */
static void partialUpload(FakeFolder &fakeFolder, const QString &name, int size)
{
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.uploadState().children.count(), 0); // The state should be clean
fakeFolder.localModifier().insert(name, size);
// Abort when the upload is at 1/3
int sizeWhenAbort = -1;
auto con = QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::transmissionProgress,
[&](const ProgressInfo &progress) {
if (progress.completedSize() > (progress.totalSize() /3 )) {
sizeWhenAbort = progress.completedSize();
fakeFolder.syncEngine().abort();
}
});
QVERIFY(!fakeFolder.syncOnce()); // there should have been an error
QObject::disconnect(con);
QVERIFY(sizeWhenAbort > 0);
QVERIFY(sizeWhenAbort < size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // the transfer was done with chunking
auto upStateChildren = fakeFolder.uploadState().children.first().children;
QCOMPARE(sizeWhenAbort, std::accumulate(upStateChildren.cbegin(), upStateChildren.cend(), 0,
[](int s, const FileInfo &i) { return s + i.size; }));
}
class TestChunkingNG : public QObject
{
Q_OBJECT
@@ -40,38 +70,173 @@ private slots:
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ {"chunking", "1.0"} } } });
const int size = 300 * 1000 * 1000; // 300 MB
fakeFolder.localModifier().insert("A/a0", size);
// Abort when the upload is at 1/3
int sizeWhenAbort = -1;
auto con = QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::transmissionProgress,
[&](const ProgressInfo &progress) {
if (progress.completedSize() > (progress.totalSize() /3 )) {
sizeWhenAbort = progress.completedSize();
fakeFolder.syncEngine().abort();
}
});
QVERIFY(!fakeFolder.syncOnce()); // there should have been an error
QObject::disconnect(con);
QVERIFY(sizeWhenAbort > 0);
QVERIFY(sizeWhenAbort < size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // the transfer was done with chunking
auto upStateChildren = fakeFolder.uploadState().children.first().children;
QCOMPARE(sizeWhenAbort, std::accumulate(upStateChildren.cbegin(), upStateChildren.cend(), 0,
[](int s, const FileInfo &i) { return s + i.size; }));
partialUpload(fakeFolder, "A/a0", size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
// Add a fake file to make sure it gets deleted
fakeFolder.uploadState().children.first().insert("10000", size);
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
// The same chunk id was re-used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QCOMPARE(fakeFolder.uploadState().children.first().name, chunkingId);
}
// We modify the file locally after it has been partially uploaded
void testRemoveStale1() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ {"chunking", "1.0"} } } });
const int size = 300 * 1000 * 1000; // 300 MB
partialUpload(fakeFolder, "A/a0", size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
fakeFolder.localModifier().setContents("A/a0", 'B');
fakeFolder.localModifier().appendByte("A/a0");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // The same chunk id was re-used
QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size + 1);
// A different chunk id was used, and the previous one is removed
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QVERIFY(fakeFolder.uploadState().children.first().name != chunkingId);
}
// We remove the file locally after it has been partially uploaded
void testRemoveStale2() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ {"chunking", "1.0"} } } });
const int size = 300 * 1000 * 1000; // 300 MB
partialUpload(fakeFolder, "A/a0", size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
fakeFolder.localModifier().remove("A/a0");
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.uploadState().children.count(), 0);
}
void testCreateConflictWhileSyncing() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ {"chunking", "1.0"} } } });
const int size = 150 * 1000 * 1000; // 150 MB
// Put a file on the server and download it.
fakeFolder.remoteModifier().insert("A/a0", size);
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
// Modify the file localy and start the upload
fakeFolder.localModifier().setContents("A/a0", 'B');
fakeFolder.localModifier().appendByte("A/a0");
// But in the middle of the sync, modify the file on the server
QMetaObject::Connection con = QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::transmissionProgress,
[&](const ProgressInfo &progress) {
if (progress.completedSize() > (progress.totalSize() / 2 )) {
fakeFolder.remoteModifier().setContents("A/a0", 'C');
QObject::disconnect(con);
}
});
QVERIFY(!fakeFolder.syncOnce());
// There was a precondition failed error, this means wen need to sync again
QCOMPARE(fakeFolder.syncEngine().isAnotherSyncNeeded(), ImmediateFollowUp);
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // We did not clean the chunks at this point
// Now we will download the server file and create a conflict
QVERIFY(fakeFolder.syncOnce());
auto localState = fakeFolder.currentLocalState();
// A0 is the one from the server
QCOMPARE(localState.find("A/a0")->size, size);
QCOMPARE(localState.find("A/a0")->contentChar, 'C');
// There is a conflict file with our version
auto &stateAChildren = localState.find("A")->children;
auto it = std::find_if(stateAChildren.cbegin(), stateAChildren.cend(), [&](const FileInfo &fi) {
return fi.name.startsWith("a0_conflict");
});
QVERIFY(it != stateAChildren.cend());
QCOMPARE(it->contentChar, 'B');
QCOMPARE(it->size, size+1);
// Remove the conflict file so the comparison works!
fakeFolder.localModifier().remove("A/" + it->name);
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.uploadState().children.count(), 0); // The last sync cleaned the chunks
}
void testModifyLocalFileWhileUploading() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ {"chunking", "1.0"} } } });
const int size = 150 * 1000 * 1000; // 150 MB
fakeFolder.localModifier().insert("A/a0", size);
// middle of the sync, modify the file
QMetaObject::Connection con = QObject::connect(&fakeFolder.syncEngine(), &SyncEngine::transmissionProgress,
[&](const ProgressInfo &progress) {
if (progress.completedSize() > (progress.totalSize() / 2 )) {
fakeFolder.localModifier().setContents("A/a0", 'B');
fakeFolder.localModifier().appendByte("A/a0");
QObject::disconnect(con);
}
});
QVERIFY(!fakeFolder.syncOnce());
// There should be a followup sync
QCOMPARE(fakeFolder.syncEngine().isAnotherSyncNeeded(), ImmediateFollowUp);
QCOMPARE(fakeFolder.uploadState().children.count(), 1); // We did not clean the chunks at this point
auto chunkingId = fakeFolder.uploadState().children.first().name;
// Now we make a new sync which should upload the file for good.
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size+1);
// A different chunk id was used, and the previous one is removed
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QVERIFY(fakeFolder.uploadState().children.first().name != chunkingId);
}
void testResumeServerDeletedChunks() {
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
fakeFolder.syncEngine().account()->setCapabilities({ { "dav", QVariantMap{ {"chunking", "1.0"} } } });
const int size = 300 * 1000 * 1000; // 300 MB
partialUpload(fakeFolder, "A/a0", size);
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
auto chunkingId = fakeFolder.uploadState().children.first().name;
// Delete the chunks on the server
fakeFolder.uploadState().children.clear();
QVERIFY(fakeFolder.syncOnce());
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
QCOMPARE(fakeFolder.currentRemoteState().find("A/a0")->size, size);
// A different chunk id was used
QCOMPARE(fakeFolder.uploadState().children.count(), 1);
QVERIFY(fakeFolder.uploadState().children.first().name != chunkingId);
}
};
QTEST_GUILESS_MAIN(TestChunkingNG)
+2 -6
Ver Arquivo
@@ -11,9 +11,7 @@
using namespace OCC;
#define STR_(X) #X
#define STR(X) STR_(X)
#define BIN_PATH STR(OWNCLOUD_BIN_PATH)
#define EXCLUDE_LIST_FILE SOURCEDIR"/../sync-exclude.lst"
class TestExcludedFiles: public QObject
{
@@ -31,9 +29,7 @@ private slots:
QVERIFY(!excluded.isExcluded("/a/.b", "/a", keepHidden));
QVERIFY(excluded.isExcluded("/a/.b", "/a", excludeHidden));
QString path(BIN_PATH);
path.append("/sync-exclude.lst");
excluded.addExcludeFilePath(path);
excluded.addExcludeFilePath(EXCLUDE_LIST_FILE);
excluded.reloadExcludes();
QVERIFY(!excluded.isExcluded("/a/b", "/a", keepHidden));
+23 -38
Ver Arquivo
@@ -17,7 +17,7 @@ class TestFileSystem : public QObject
{
Q_OBJECT
QString _root;
QTemporaryDir _root;
QByteArray shellSum( const QByteArray& cmd, const QString& file )
@@ -38,51 +38,36 @@ class TestFileSystem : public QObject
}
private slots:
void initTestCase() {
qsrand(QTime::currentTime().msec());
QString subdir("test_"+QString::number(qrand()));
_root = QDir::tempPath() + "/" + subdir;
QDir dir("/tmp");
dir.mkdir(subdir);
qDebug() << "creating test directory " << _root;
}
void cleanupTestCase()
{
if( !_root.isEmpty() )
system(QString("rm -rf "+_root).toUtf8());
}
void testMd5Calc()
{
QString file( _root+"/file_a.bin");
writeRandomFile(file);
QFileInfo fi(file);
QVERIFY(fi.exists());
QByteArray sum = calcMd5(file);
QString file( _root.path() + "/file_a.bin");
QVERIFY(writeRandomFile(file));
QFileInfo fi(file);
QVERIFY(fi.exists());
QByteArray sum = calcMd5(file);
QByteArray sSum = shellSum("/usr/bin/md5sum", file);
qDebug() << "calculated" << sum << "versus md5sum:"<< sSum;
QVERIFY(!sSum.isEmpty());
QVERIFY(!sum.isEmpty());
QVERIFY(sSum == sum );
QByteArray sSum = shellSum("md5sum", file);
if (sSum.isEmpty())
QSKIP("Couldn't execute md5sum to calculate checksum, executable missing?", SkipSingle);
QVERIFY(!sum.isEmpty());
QCOMPARE(sSum, sum);
}
void testSha1Calc()
{
QString file( _root+"/file_b.bin");
writeRandomFile(file);
QFileInfo fi(file);
QVERIFY(fi.exists());
QByteArray sum = calcSha1(file);
QString file( _root.path() + "/file_b.bin");
writeRandomFile(file);
QFileInfo fi(file);
QVERIFY(fi.exists());
QByteArray sum = calcSha1(file);
QByteArray sSum = shellSum("/usr/bin/sha1sum", file);
qDebug() << "calculated" << sum << "versus sha1sum:"<< sSum;
QVERIFY(!sSum.isEmpty());
QVERIFY(!sum.isEmpty());
QVERIFY(sSum == sum );
QByteArray sSum = shellSum("sha1sum", file);
if (sSum.isEmpty())
QSKIP("Couldn't execute sha1sum to calculate checksum, executable missing?", SkipSingle);
QVERIFY(!sum.isEmpty());
QCOMPARE(sSum, sum);
}
};
+33 -32
Ver Arquivo
@@ -63,6 +63,7 @@ private slots:
f.open(QFile::WriteOnly);
f.write("hello");
}
QString dirPath = dir2.canonicalPath();
AccountPtr account = Account::create();
QUrl url("http://example.de");
@@ -73,22 +74,22 @@ private slots:
AccountStatePtr newAccountState(new AccountState(account));
FolderMan *folderman = FolderMan::instance();
QCOMPARE(folderman, &_fm);
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dir.path() + "/sub/ownCloud1")));
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dir.path() + "/ownCloud2")));
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dirPath + "/sub/ownCloud1")));
QVERIFY(folderman->addFolder(newAccountState.data(), folderDefinition(dirPath + "/ownCloud2")));
// those should be allowed
// QString FolderMan::checkPathValidityForNewFolder(const QString& path, const QUrl &serverUrl, bool forNewDirectory)
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/free"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/free2/"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/sub/free"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/free2/"), QString());
// Not an existing directory -> Ok
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/bliblablu"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/free/bliblablu"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/bliblablu/some/more"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/sub/bliblablu"), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/sub/free/bliblablu"), QString());
// QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/sub/bliblablu/some/more"), QString());
// A file -> Error
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/file.txt").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/sub/file.txt").isNull());
// There are folders configured in those folders, url needs to be taken into account: -> ERROR
QUrl url2(url);
@@ -96,51 +97,51 @@ private slots:
url2.setUserName(user);
// The following both fail because they refer to the same account (user and url)
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1", url2).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/", url2).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/sub/ownCloud1", url2).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/ownCloud2/", url2).isNull());
// Now it will work because the account is different
QUrl url3("http://anotherexample.org");
url3.setUserName("dummy");
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1", url3), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/", url3), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/sub/ownCloud1", url3), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/ownCloud2/", url3), QString());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path()).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/folder").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/folder/f").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/sub/ownCloud1/folder").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/sub/ownCloud1/folder/f").isNull());
// make a bunch of links
QVERIFY(QFile::link(dir.path() + "/sub/free", dir.path() + "/link1"));
QVERIFY(QFile::link(dir.path() + "/sub", dir.path() + "/link2"));
QVERIFY(QFile::link(dir.path() + "/sub/ownCloud1", dir.path() + "/link3"));
QVERIFY(QFile::link(dir.path() + "/sub/ownCloud1/folder", dir.path() + "/link4"));
QVERIFY(QFile::link(dirPath + "/sub/free", dirPath + "/link1"));
QVERIFY(QFile::link(dirPath + "/sub", dirPath + "/link2"));
QVERIFY(QFile::link(dirPath + "/sub/ownCloud1", dirPath + "/link3"));
QVERIFY(QFile::link(dirPath + "/sub/ownCloud1/folder", dirPath + "/link4"));
// Ok
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/link1").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/link2/free").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + "/link1").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + "/link2/free").isNull());
// Not Ok
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link2").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/link2").isNull());
// link 3 points to an existing sync folder. To make it fail, the account must be the same
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3", url2).isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/link3", url2).isNull());
// while with a different account, this is fine
QCOMPARE(folderman->checkPathValidityForNewFolder(dir.path() + "/link3", url3), QString());
QCOMPARE(folderman->checkPathValidityForNewFolder(dirPath + "/link3", url3), QString());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link4").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3/folder").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/link4").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/link3/folder").isNull());
// test some non existing sub path (error)
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/some/sub/path").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/ownCloud2/blublu").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/sub/ownCloud1/folder/g/h").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dir.path() + "/link3/folder/neu_folder").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/sub/ownCloud1/some/sub/path").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/ownCloud2/blublu").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/sub/ownCloud1/folder/g/h").isNull());
QVERIFY(!folderman->checkPathValidityForNewFolder(dirPath + "/link3/folder/neu_folder").isNull());
// Subfolder of links
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/link1/subfolder").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dir.path() + "/link2/free/subfolder").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + "/link1/subfolder").isNull());
QVERIFY(folderman->checkPathValidityForNewFolder(dirPath + "/link2/free/subfolder").isNull());
// Invalid paths
QVERIFY(!folderman->checkPathValidityForNewFolder("").isNull());
+124 -117
Ver Arquivo
@@ -10,174 +10,181 @@
#include "folderwatcher.h"
#include "utility.h"
void touch(const QString &file)
{
#ifdef Q_OS_WIN
OCC::Utility::writeRandomFile(file);
#else
QString cmd;
cmd = QString("touch %1").arg(file);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
#endif
}
void mkdir(const QString &file)
{
#ifdef Q_OS_WIN
QDir dir;
dir.mkdir(file);
#else
QString cmd = QString("mkdir %1").arg(file);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
#endif
}
void rmdir(const QString &file)
{
#ifdef Q_OS_WIN
QDir dir;
dir.rmdir(file);
#else
QString cmd = QString("rmdir %1").arg(file);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
#endif
}
void rm(const QString &file)
{
#ifdef Q_OS_WIN
QFile::remove(file);
#else
QString cmd = QString("rm %1").arg(file);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
#endif
}
void mv(const QString &file1, const QString &file2)
{
#ifdef Q_OS_WIN
QFile::rename(file1, file2);
#else
QString cmd = QString("mv %1 %2").arg(file1, file2);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
#endif
}
using namespace OCC;
class TestFolderWatcher : public QObject
{
Q_OBJECT
public slots:
void slotFolderChanged( const QString& path ) {
if (_skipNotifications.contains(path)) {
return;
}
if (_requiredNotifications.contains(path)) {
_receivedNotifications.insert(path);
}
}
QTemporaryDir _root;
QString _rootPath;
QScopedPointer<FolderWatcher> _watcher;
QScopedPointer<QSignalSpy> _pathChangedSpy;
void slotEnd() { // in case something goes wrong...
_loop.quit();
QVERIFY2(1 == 0, "Loop hang!");
}
private:
QString _root;
FolderWatcher *_watcher;
QEventLoop _loop;
QTimer _timer;
QSet<QString> _requiredNotifications;
QSet<QString> _receivedNotifications;
QSet<QString> _skipNotifications;
void processAndWait()
bool waitForPathChanged(const QString &path)
{
_loop.processEvents();
Utility::usleep(200000);
_loop.processEvents();
QElapsedTimer t;
t.start();
while (t.elapsed() < 5000) {
// Check if it was already reported as changed by the watcher
for (int i = 0; i < _pathChangedSpy->size(); ++i) {
const auto &args = _pathChangedSpy->at(i);
if (args.first().toString() == path)
return true;
}
// Wait a bit and test again (don't bother checking if we timed out or not)
_pathChangedSpy->wait(200);
}
return false;
}
public:
TestFolderWatcher() {
qsrand(QTime::currentTime().msec());
QDir rootDir(_root.path());
_rootPath = rootDir.canonicalPath();
qDebug() << "creating test directory tree in " << _rootPath;
rootDir.mkpath("a1/b1/c1");
rootDir.mkpath("a1/b1/c2");
rootDir.mkpath("a1/b2/c1");
rootDir.mkpath("a1/b3/c3");
rootDir.mkpath("a2/b3/c3");
Utility::writeRandomFile( _rootPath+"/a1/random.bin");
Utility::writeRandomFile( _rootPath+"/a1/b2/todelete.bin");
Utility::writeRandomFile( _rootPath+"/a2/renamefile");
Utility::writeRandomFile( _rootPath+"/a1/movefile");
_watcher.reset(new FolderWatcher(_rootPath));
_pathChangedSpy.reset(new QSignalSpy(_watcher.data(), SIGNAL(pathChanged(QString))));
}
private slots:
void initTestCase() {
qsrand(QTime::currentTime().msec());
_root = QDir::tempPath() + "/" + "test_" + QString::number(qrand());
qDebug() << "creating test directory tree in " << _root;
QDir rootDir(_root);
rootDir.mkpath(_root + "/a1/b1/c1");
rootDir.mkpath(_root + "/a1/b1/c2");
rootDir.mkpath(_root + "/a1/b2/c1");
rootDir.mkpath(_root + "/a1/b3/c3");
rootDir.mkpath(_root + "/a2/b3/c3");
Utility::writeRandomFile( _root+"/a1/random.bin");
Utility::writeRandomFile( _root+"/a1/b2/todelete.bin");
Utility::writeRandomFile( _root+"/a2/renamefile");
Utility::writeRandomFile( _root+"/a1/movefile");
_watcher = new FolderWatcher(_root);
QObject::connect(_watcher, SIGNAL(pathChanged(QString)), this, SLOT(slotFolderChanged(QString)));
_timer.singleShot(5000, this, SLOT(slotEnd()));
}
void init()
{
_receivedNotifications.clear();
_requiredNotifications.clear();
_skipNotifications.clear();
}
void checkNotifications()
{
processAndWait();
QCOMPARE(_receivedNotifications, _requiredNotifications);
_pathChangedSpy->clear();
}
void testACreate() { // create a new file
QString file(_root + "/foo.txt");
QString file(_rootPath + "/foo.txt");
QString cmd;
_requiredNotifications.insert(file);
cmd = QString("echo \"xyz\" > %1").arg(file);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
checkNotifications();
QVERIFY(waitForPathChanged(file));
}
void testATouch() { // touch an existing file.
QString file(_root + "/a1/random.bin");
_requiredNotifications.insert(file);
#ifdef Q_OS_WIN
Utility::writeRandomFile(QString("%1/a1/random.bin").arg(_root));
#else
QString cmd;
cmd = QString("touch %1").arg(file);
qDebug() << "Command: " << cmd;
system(cmd.toLocal8Bit());
#endif
checkNotifications();
QString file(_rootPath + "/a1/random.bin");
touch(file);
QVERIFY(waitForPathChanged(file));
}
void testCreateADir() {
QString file(_root+"/a1/b1/new_dir");
_requiredNotifications.insert(file);
//_skipNotifications.insert(_root + "/a1/b1/new_dir");
QDir dir;
dir.mkdir(file);
QVERIFY(QFile::exists(file));
checkNotifications();
QString file(_rootPath+"/a1/b1/new_dir");
mkdir(file);
QVERIFY(waitForPathChanged(file));
}
void testRemoveADir() {
QString file(_root+"/a1/b3/c3");
_requiredNotifications.insert(file);
QDir dir;
QVERIFY(dir.rmdir(file));
checkNotifications();
QString file(_rootPath+"/a1/b3/c3");
rmdir(file);
QVERIFY(waitForPathChanged(file));
}
void testRemoveAFile() {
QString file(_root+"/a1/b2/todelete.bin");
_requiredNotifications.insert(file);
QString file(_rootPath+"/a1/b2/todelete.bin");
QVERIFY(QFile::exists(file));
QFile::remove(file);
rm(file);
QVERIFY(!QFile::exists(file));
checkNotifications();
QVERIFY(waitForPathChanged(file));
}
void testRenameAFile() {
QString file1(_root+"/a2/renamefile");
QString file2(_root+"/a2/renamefile.renamed");
_requiredNotifications.insert(file1);
_requiredNotifications.insert(file2);
QString file1(_rootPath+"/a2/renamefile");
QString file2(_rootPath+"/a2/renamefile.renamed");
QVERIFY(QFile::exists(file1));
QFile::rename(file1, file2);
mv(file1, file2);
QVERIFY(QFile::exists(file2));
checkNotifications();
QVERIFY(waitForPathChanged(file1));
QVERIFY(waitForPathChanged(file2));
}
void testMoveAFile() {
QString old_file(_root+"/a1/movefile");
QString new_file(_root+"/a2/movefile.renamed");
_requiredNotifications.insert(old_file);
_requiredNotifications.insert(new_file);
QString old_file(_rootPath+"/a1/movefile");
QString new_file(_rootPath+"/a2/movefile.renamed");
QVERIFY(QFile::exists(old_file));
QFile::rename(old_file, new_file);
mv(old_file, new_file);
QVERIFY(QFile::exists(new_file));
checkNotifications();
}
void cleanupTestCase() {
if( _root.startsWith(QDir::tempPath() )) {
system( QString("rm -rf %1").arg(_root).toLocal8Bit() );
}
delete _watcher;
QVERIFY(waitForPathChanged(old_file));
QVERIFY(waitForPathChanged(new_file));
}
};
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
// Qt4 does not have QTEST_GUILESS_MAIN, so we simulate it.
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
TestFolderWatcher tc;
return QTest::qExec(&tc, argc, argv);
}
#ifdef Q_OS_MAC
QTEST_MAIN(TestFolderWatcher)
#else
QTEST_GUILESS_MAIN(TestFolderWatcher)
#endif

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais