mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-04 20:34:17 +02:00
Compare commits
170 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee71024496 | ||
|
|
b1f326054f | ||
|
|
bceb40ed80 | ||
|
|
fd684eda52 | ||
|
|
9e3c3353bd | ||
|
|
07ffff3d77 | ||
|
|
9b34427a1c | ||
|
|
5fba476076 | ||
|
|
d63abef718 | ||
|
|
cdba8a7f2f | ||
|
|
21967a130b | ||
|
|
08e78d5d6f | ||
|
|
fe68e1e82c | ||
|
|
25ac3bfdb7 | ||
|
|
4700c604b1 | ||
|
|
356fa737c5 | ||
|
|
520e2eb392 | ||
|
|
e0d2bd4830 | ||
|
|
1dc05f99bf | ||
|
|
b2b176bcd0 | ||
|
|
fde5ccd0df | ||
|
|
d1fe25cc31 | ||
|
|
60c18f75b5 | ||
|
|
c7f3791f3d | ||
|
|
ee9d5e6bf0 | ||
|
|
96fa3a3a1a | ||
|
|
7c4deec800 | ||
|
|
78e82eb920 | ||
|
|
3c91a1ace4 | ||
|
|
3a52db46ad | ||
|
|
137bce6dd0 | ||
|
|
f000e6ce6a | ||
|
|
7c1281dd06 | ||
|
|
60729f2bbd | ||
|
|
0b0ecfcbe4 | ||
|
|
1fe86bced2 | ||
|
|
c6a62a497d | ||
|
|
8be6881093 | ||
|
|
99c8118229 | ||
|
|
6c5ca055c4 | ||
|
|
a6ec8f3090 | ||
|
|
b039c2ce86 | ||
|
|
5a6d286c41 | ||
|
|
ba65187ad3 | ||
|
|
a91f54f0a8 | ||
|
|
d77c1f3e4a | ||
|
|
7390ddbd98 | ||
|
|
32a4b40f0a | ||
|
|
bc1bc2a4f8 | ||
|
|
155c965866 | ||
|
|
6c73f25747 | ||
|
|
2518fd7059 | ||
|
|
56edae6958 | ||
|
|
d8275cd4e1 | ||
|
|
c3dca7a288 | ||
|
|
96ff0076c7 | ||
|
|
8bb4af067a | ||
|
|
df0df76b51 | ||
|
|
458f336405 | ||
|
|
3f3f27d4d3 | ||
|
|
7c9cffa5ae | ||
|
|
996223197c | ||
|
|
b8e7555977 | ||
|
|
ed80a712ab | ||
|
|
2866e56c51 | ||
|
|
2074bdbb19 | ||
|
|
d212ac7d16 | ||
|
|
d2bae21b14 | ||
|
|
4283ab3b44 | ||
|
|
c579069071 | ||
|
|
88488c695c | ||
|
|
21594e9aa9 | ||
|
|
9ffacd4ecd | ||
|
|
9d5f5ea3bc | ||
|
|
2dbd27af76 | ||
|
|
0634a4d0c6 | ||
|
|
fa80a006b8 | ||
|
|
9d88ef5432 | ||
|
|
7b99877c68 | ||
|
|
ec81cdefb0 | ||
|
|
454d5b575c | ||
|
|
785b59e6d1 | ||
|
|
9d8fc4aa4d | ||
|
|
57ac1d9ea2 | ||
|
|
d9ea6936ab | ||
|
|
c917251e9e | ||
|
|
c805c5d6e9 | ||
|
|
adcf40afc3 | ||
|
|
d986011067 | ||
|
|
5a83636f81 | ||
|
|
d475628c70 | ||
|
|
4a890eae38 | ||
|
|
760e11bc5d | ||
|
|
96ebf2b519 | ||
|
|
4a6f4919d7 | ||
|
|
b98040c7d5 | ||
|
|
1240a8163d | ||
|
|
e15b9b5358 | ||
|
|
1617c9d482 | ||
|
|
16600fe86a | ||
|
|
50ba73860c | ||
|
|
750cdc1910 | ||
|
|
f4e2c84111 | ||
|
|
4f27750711 | ||
|
|
71d9f23068 | ||
|
|
9f7aed7602 | ||
|
|
7f2213416a | ||
|
|
9b9f0bdd4d | ||
|
|
ee0aec514f | ||
|
|
c2fd7d6ebd | ||
|
|
4a893c5267 | ||
|
|
2473183f19 | ||
|
|
7d7142d7d8 | ||
|
|
cb57cda87a | ||
|
|
f2fa812b0b | ||
|
|
ef89582d7e | ||
|
|
ae74a21305 | ||
|
|
32af63764c | ||
|
|
61ff90409d | ||
|
|
ba0c3256fa | ||
|
|
b6fe5d2cff | ||
|
|
9123fac545 | ||
|
|
c6bc388001 | ||
|
|
ea985a85af | ||
|
|
2578832002 | ||
|
|
cfada67aa6 | ||
|
|
5483682281 | ||
|
|
2a8c23aac3 | ||
|
|
88bc96fc4c | ||
|
|
588129d852 | ||
|
|
0889991c38 | ||
|
|
5c87a35fe4 | ||
|
|
418185af9a | ||
|
|
4b56cc6e08 | ||
|
|
3cef771868 | ||
|
|
59b20de7cd | ||
|
|
e0ae34f01b | ||
|
|
a9a24c96fc | ||
|
|
a202203325 | ||
|
|
83c3d76966 | ||
|
|
99734f8d72 | ||
|
|
570a0bb586 | ||
|
|
450a815d61 | ||
|
|
dafc2d2b73 | ||
|
|
0bd2edd33a | ||
|
|
807267cfdb | ||
|
|
48c9222578 | ||
|
|
b5ea56df73 | ||
|
|
2fdf6dd8f2 | ||
|
|
5b8c9eb16f | ||
|
|
9c05150c12 | ||
|
|
c7eb85ef78 | ||
|
|
4df455f2e0 | ||
|
|
653a00d63d | ||
|
|
3c9acdf724 | ||
|
|
e81d1ab9b8 | ||
|
|
ad5620efb5 | ||
|
|
f455c71338 | ||
|
|
033f2cd231 | ||
|
|
c9eccd729d | ||
|
|
b1100cd9e5 | ||
|
|
8f728ddfb2 | ||
|
|
4ef0ce112c | ||
|
|
57c14a0eba | ||
|
|
89f831e7d4 | ||
|
|
917b8409ae | ||
|
|
86fd39e3a9 | ||
|
|
04db332051 | ||
|
|
06a2f58c51 | ||
|
|
b87d55758b |
48
ChangeLog
48
ChangeLog
@@ -1,6 +1,52 @@
|
||||
ChangeLog
|
||||
=========
|
||||
version 1.8.0 (release 2015-03-xx)
|
||||
version 1.8.1 (release 2015-05-07)
|
||||
* Make "operation canceled" error a soft error
|
||||
* Do not throw an error for files that are scheduled to be removed,
|
||||
but can not be found on the server. #2919
|
||||
* Windows: Reset QNAM to proper function after hibernation. #2899 #2895 #2973
|
||||
* Fix argument verification of --confdir #2453
|
||||
* Fix a crash when accessing a dangling UploadDevice pointer #2984
|
||||
* Add-folder wizard: Make sure there is a scrollbar if folder names
|
||||
are too long #2962
|
||||
* Add-folder Wizard: Select the newly created folder
|
||||
* Activity: Correctly restore column sizes #3005
|
||||
* SSL Button: do not crash on empty certificate chain
|
||||
* SSL Button: Make menu creation lazy #3007 #2990
|
||||
* Lookup system proxy async to avoid hangs #2993 #2802
|
||||
* ShareDialog: Some GUI refinements
|
||||
* ShareDialog: On creation of a share always retrieve the share
|
||||
This makes sure that if a default expiration date is set this is reflected
|
||||
in the dialog. #2889
|
||||
* ShareDialog: Only show share dialog if we are connected.
|
||||
* HttpCreds: Fill pw dialog with previous password. #2848 #2879
|
||||
* HttpCreds: Delete password from old location. #2186
|
||||
* Do not store Session Cookies in the client cookie storage
|
||||
* CookieJar: Don't accidentally overwrite cookies. #2808
|
||||
* ProtocolWidget: Always add seconds to the DateTime locale. #2535
|
||||
* Updater: Give context as to which app is about to be updated #3040
|
||||
* Windows: Add version information for owncloud.exe. This should help us know
|
||||
what version or build number a crash report was generated with.
|
||||
* Fix a crash on shutdown in ~SocketApi #3057
|
||||
* SyncEngine: Show more timing measurements #3064
|
||||
* Discovery: Add warning if returned etag is 0
|
||||
* Fix a crash caused by an invalid DiscoveryDirectoryResult::iterator #3051
|
||||
* Sync: Fix sync of deletions during 503. #2894
|
||||
* Handle redirect of auth request. #3082
|
||||
* Discovery: Fix parsing of broken XML replies, which fixes local file disappearing #3102
|
||||
* Migration: Silently restore files that were deleted locally by bug #3102
|
||||
* Sort folder sizes SelectiveSyncTreeView numerically #3112
|
||||
* Sync: PropagateDownload: Read the mtime from the file system after writing it #3103
|
||||
* Sync: Propagate download: Fix restoring files for which the conflict file exists #3106
|
||||
* Use identical User Agents and version for csync and the Qt parts
|
||||
* Prevent another crash in ~SocketApi #3118
|
||||
* Windows: Fix rename of finished file. #3073
|
||||
* AccountWizard: Fix auth error handling. #3155
|
||||
* Documentation fixes
|
||||
* Infrastructure/build fixes
|
||||
* Win32/OS X: Apply patch from OpenSSL to handle oudated intermediates gracefully #3087
|
||||
|
||||
version 1.8.0 (release 2015-03-17)
|
||||
* Mac OS: HIDPI support
|
||||
* Support Sharing from desktop: Added a share dialog that can be
|
||||
opened by context menu in the file managers (Win, Mac, Nautilus)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set( MIRALL_VERSION_MAJOR 1 )
|
||||
set( MIRALL_VERSION_MINOR 8 )
|
||||
set( MIRALL_VERSION_PATCH 0 )
|
||||
set( MIRALL_VERSION_PATCH 1 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
|
||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
|
||||
@@ -50,7 +50,7 @@ if [ ! -z "$identity" ]; then
|
||||
echo "Will try to sign the installer"
|
||||
pushd $install_path
|
||||
productsign --sign "$identity" "$installer_file" "$installer_file.new"
|
||||
mv "$installer_file".new $installer_file
|
||||
mv "$installer_file".new "$installer_file"
|
||||
popd
|
||||
else
|
||||
echo "No certificate given, will not sign the pkg"
|
||||
|
||||
@@ -260,12 +260,18 @@ def CopyFramework(path):
|
||||
commands.append(args)
|
||||
args = ['chmod', 'u+w', os.path.join(full_path, parts[-1])]
|
||||
commands.append(args)
|
||||
args = ['chmod', 'u+w', os.path.join(frameworks_dir, framework, "Resources")]
|
||||
resources_dir = os.path.join(frameworks_dir, framework, "Resources")
|
||||
|
||||
args = ['mkdir', resources_dir]
|
||||
commands.append(args)
|
||||
args = ['chmod', 'u+w', resources_dir]
|
||||
commands.append(args)
|
||||
|
||||
info_plist = os.path.join(os.path.split(path)[0], '..', '..', 'Contents', 'Info.plist')
|
||||
if not os.path.exists(info_plist):
|
||||
info_plist = os.path.join(os.path.split(path)[0], 'Resources', 'Info.plist')
|
||||
if os.path.exists(info_plist):
|
||||
args = ['cp', '-r', info_plist, os.path.join(frameworks_dir, framework, "Resources")]
|
||||
args = ['cp', '-r', info_plist, resources_dir]
|
||||
commands.append(args)
|
||||
return os.path.join(full_path, parts[-1])
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh -x
|
||||
#!/bin/sh -xe
|
||||
|
||||
[ "$#" -lt 2 ] && echo "Usage: sign_app.sh <app> <identity>" && exit
|
||||
|
||||
@@ -7,6 +7,6 @@ identity="$2"
|
||||
|
||||
codesign -s "$identity" --force --verbose=4 --deep "$src_app"
|
||||
|
||||
# Just for our debug purposes:
|
||||
# Verify the signature
|
||||
spctl -a -t exec -vv $src_app
|
||||
codesign -dv $src_app
|
||||
|
||||
19
admin/win/docker/build.sh
Executable file
19
admin/win/docker/build.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $(basename $0) directory_relative_to_home [uid]"
|
||||
exit
|
||||
fi
|
||||
|
||||
useradd user -u ${2:-1000}
|
||||
su - user << EOF
|
||||
cd /home/user/$1
|
||||
rm -rf build-win32
|
||||
mkdir build-win32
|
||||
cd build-win32
|
||||
../admin/win/download_runtimes.sh
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=../admin/win/Toolchain-mingw32-openSUSE.cmake -DWITH_CRASHREPORTER=ON
|
||||
make -j4
|
||||
make package
|
||||
ctest .
|
||||
EOF
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#VS2013
|
||||
base_url=http://download.microsoft.com/download/2/E/6/2E61CFA4-993B-4DD4-91DA-3737CD5CD6E3
|
||||
tmp_path=/tmp/.vcredist
|
||||
tmp_path=${1:-/tmp/.vcredist}
|
||||
|
||||
mkdir -p $tmp_path
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ StrCpy $PageReinstall_SAME_Field_3 "Desinstal.lar ${APPLICATION_NAME}"
|
||||
StrCpy $UNINSTALLER_APPDATA_TITLE "Desinstal.lar ${APPLICATION_NAME}"
|
||||
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Escolliu l'opció de manteniment per executar-ho."
|
||||
StrCpy $SEC_APPLICATION_DETAILS "Instal·lant ${APPLICATION_NAME} essencial."
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integració per Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Instal·lant integració per Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Accés directe del programa al menú d'inici"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "Afegint la drecera per ${APPLICATION_NAME} al menú d'inici."
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Drecera a l'escriptori"
|
||||
@@ -42,5 +44,3 @@ StrCpy $INIT_INSTALLER_RUNNING "L'instal·lador ja s'està executant."
|
||||
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Aquest desinstal·lador requereix accés d'administrador, intenteu-ho de nou."
|
||||
StrCpy $INIT_UNINSTALLER_RUNNING "El desinstal·lador ja s'està executant."
|
||||
StrCpy $SectionGroup_Shortcuts "Dreceres"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integration for Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Installing Integration for Windows Explorer"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Auto-generated - do not modify
|
||||
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Afficher les notes de version"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Le(s) processus en cours d’exécution ${APPLICATION_EXECUTABLE} doit (doivent) être stoppé(s) afin de poursuivre.$\nVoulez-vous que le programme d’installation s’en charge pour vous ?"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Les processus ${APPLICATION_EXECUTABLE} en cours d’exécution doivent être stoppés avant de poursuivre.$\nVoulez-vous que le programme d’installation s’en charge pour vous ?"
|
||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Fermeture des processus ${APPLICATION_EXECUTABLE}."
|
||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Le processus à stopper n'a pas été trouvé !"
|
||||
StrCpy $PageReinstall_NEW_Field_1 "Une ancienne version de ${APPLICATION_NAME} est installée sur votre système. Il est recommandé de désinstaller cette version avant de continuer. Sélectionnez l'opération que vous voulez exécuter et cliquez sur Suivant pour continuer."
|
||||
|
||||
@@ -39,8 +39,8 @@ StrCpy $UNINSTALL_ABORT "A desinstalaci
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "Acceso de inicio rápido (n/d)"
|
||||
StrCpy $INIT_NO_DESKTOP "Atallo no escritorio (sobrescribe o existente)"
|
||||
StrCpy $UAC_ERROR_ELEVATE "Non foi posíbel elevalo, erro:"
|
||||
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Este instalador require acceso de administrador, tenteo de novo"
|
||||
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Este instalador require acceso de administrador, ténteo de novo"
|
||||
StrCpy $INIT_INSTALLER_RUNNING "O instalador xa está en execución."
|
||||
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Este desinstalador require acceso de administrador, tenteo de novo"
|
||||
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Este desinstalador require acceso de administrador, ténteo de novo"
|
||||
StrCpy $INIT_UNINSTALLER_RUNNING "O desinstalador xa está en execución."
|
||||
StrCpy $SectionGroup_Shortcuts "Atallos"
|
||||
|
||||
2
binary
2
binary
Submodule binary updated: 01d73965dc...1fb9ddfa9a
@@ -3,13 +3,7 @@
|
||||
# global needed variables
|
||||
set(APPLICATION_NAME "ocsync")
|
||||
|
||||
set(APPLICATION_VERSION_MAJOR "0")
|
||||
set(APPLICATION_VERSION_MINOR "91")
|
||||
set(APPLICATION_VERSION_PATCH "5")
|
||||
|
||||
set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}")
|
||||
|
||||
set(LIBRARY_VERSION "0.2.1")
|
||||
set(LIBRARY_VERSION ${MIRALL_VERSION})
|
||||
set(LIBRARY_SOVERSION "0")
|
||||
|
||||
# add definitions
|
||||
|
||||
@@ -576,7 +576,8 @@ int csync_commit(CSYNC *ctx) {
|
||||
_csync_clean_ctx(ctx);
|
||||
|
||||
ctx->remote.read_from_db = 0;
|
||||
ctx->read_from_db_disabled = 0;
|
||||
ctx->read_remote_from_db = true;
|
||||
ctx->db_is_empty = false;
|
||||
|
||||
|
||||
/* Create new trees */
|
||||
@@ -773,10 +774,3 @@ int csync_set_module_property(CSYNC* ctx, const char* key, void* value)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int csync_set_read_from_db(CSYNC* ctx, int enabled)
|
||||
{
|
||||
ctx->read_from_db_disabled = !enabled;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <config_csync.h>
|
||||
|
||||
#include "csync_version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@@ -550,11 +548,6 @@ void csync_resume(CSYNC *ctx);
|
||||
*/
|
||||
int csync_abort_requested(CSYNC *ctx);
|
||||
|
||||
/**
|
||||
* Specify if it is allowed to read the remote tree from the DB (default to enabled)
|
||||
*/
|
||||
int csync_set_read_from_db(CSYNC* ctx, int enabled);
|
||||
|
||||
char *csync_normalize_etag(const char *);
|
||||
time_t oc_httpdate_parse( const char *date );
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include "csync_private.h"
|
||||
|
||||
#include "csync_version.h"
|
||||
|
||||
|
||||
/*
|
||||
* helper method to build up a user text for SSL problems, called from the
|
||||
@@ -437,8 +439,8 @@ int dav_connect(CSYNC *csyncCtx, const char *base_url) {
|
||||
// Should never take more than some seconds, 30 is really a max.
|
||||
ne_set_connect_timeout(ctx->dav_session.ctx, 30);
|
||||
|
||||
snprintf( uaBuf, sizeof(uaBuf), "Mozilla/5.0 (%s) csyncoC/%s",
|
||||
csync_owncloud_get_platform(), CSYNC_STRINGIFY( LIBCSYNC_VERSION ));
|
||||
snprintf( uaBuf, sizeof(uaBuf), "Mozilla/5.0 (%s) mirall/%s (csyncoC)",
|
||||
CSYNC_STRINGIFY( MIRALL_VERSION ), csync_owncloud_get_platform() );
|
||||
ne_set_useragent( ctx->dav_session.ctx, uaBuf);
|
||||
ne_set_server_auth(ctx->dav_session.ctx, authentication_callback_by_neon, ctx);
|
||||
|
||||
|
||||
@@ -151,7 +151,17 @@ struct csync_s {
|
||||
int status;
|
||||
volatile int abort;
|
||||
void *rename_info;
|
||||
int read_from_db_disabled;
|
||||
|
||||
/**
|
||||
* Specify if it is allowed to read the remote tree from the DB (default to enabled)
|
||||
*/
|
||||
bool read_remote_from_db;
|
||||
|
||||
/**
|
||||
* If true, the DB is considered empty and all reads are skipped. (default is false)
|
||||
* This is useful during the initial local discovery as it speeds it up significantly.
|
||||
*/
|
||||
bool db_is_empty;
|
||||
|
||||
struct csync_owncloud_ctx_s *owncloud_context;
|
||||
|
||||
|
||||
@@ -156,8 +156,8 @@ static int _csync_merge_algorithm_visitor(void *obj, void *data) {
|
||||
}
|
||||
|
||||
if( tmp ) {
|
||||
if( tmp->path ) {
|
||||
len = strlen( tmp->path );
|
||||
len = strlen( tmp->path );
|
||||
if( len > 0 ) {
|
||||
h = c_jhash64((uint8_t *) tmp->path, len, 0);
|
||||
/* First, check that the file is NOT in our tree (another file with the same name was added) */
|
||||
node = c_rbtree_find(ctx->current == REMOTE_REPLICA ? ctx->remote.tree : ctx->local.tree, &h);
|
||||
|
||||
@@ -298,7 +298,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx,
|
||||
csync_file_stat_t *st = NULL;
|
||||
int rc;
|
||||
|
||||
if( !ctx ) {
|
||||
if( !ctx || ctx->db_is_empty ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( !ctx ) {
|
||||
if( !ctx || ctx->db_is_empty ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( !ctx ) {
|
||||
if( !ctx || ctx->db_is_empty ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -448,7 +448,7 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( !ctx ) {
|
||||
if( !ctx || ctx->db_is_empty ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||
|| !c_streq(fs->remotePerm, tmp->remotePerm)))
|
||||
|| (ctx->current == LOCAL_REPLICA && fs->inode != tmp->inode);
|
||||
if (type == CSYNC_FTW_TYPE_DIR && ctx->current == REMOTE_REPLICA
|
||||
&& !metadata_differ && !ctx->read_from_db_disabled) {
|
||||
&& !metadata_differ && ctx->read_remote_from_db) {
|
||||
/* If both etag and file id are equal for a directory, read all contents from
|
||||
* the database.
|
||||
* The metadata comparison ensure that we fetch all the file id or permission when
|
||||
|
||||
@@ -28,22 +28,7 @@ extern "C" {
|
||||
#define CSYNC_STRINGIFY(s) CSYNC_TOSTRING(s)
|
||||
#define CSYNC_TOSTRING(s) #s
|
||||
|
||||
/* csync version macros */
|
||||
#define CSYNC_VERSION_INT(a, b, c) ((a) << 16 | (b) << 8 | (c))
|
||||
#define CSYNC_VERSION_DOT(a, b, c) a ##.## b ##.## c
|
||||
#define CSYNC_VERSION(a, b, c) CSYNC_VERSION_DOT(a, b, c)
|
||||
|
||||
/* csync version */
|
||||
#define LIBCSYNC_VERSION_MAJOR @APPLICATION_VERSION_MAJOR@
|
||||
#define LIBCSYNC_VERSION_MINOR @APPLICATION_VERSION_MINOR@
|
||||
#define LIBCSYNC_VERSION_MICRO @APPLICATION_VERSION_PATCH@
|
||||
|
||||
#define LIBCSYNC_VERSION_INT CSYNC_VERSION_INT(LIBCSYNC_VERSION_MAJOR, \
|
||||
LIBCSYNC_VERSION_MINOR, \
|
||||
LIBCSYNC_VERSION_MICRO)
|
||||
#define LIBCSYNC_VERSION CSYNC_VERSION(LIBCSYNC_VERSION_MAJOR, \
|
||||
LIBCSYNC_VERSION_MINOR, \
|
||||
LIBCSYNC_VERSION_MICRO)
|
||||
#define MIRALL_VERSION @MIRALL_VERSION@
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "c_lib.h"
|
||||
#include "csync.h"
|
||||
#include "csync_log.h"
|
||||
|
||||
csync_vio_file_stat_t *csync_vio_file_stat_new(void) {
|
||||
csync_vio_file_stat_t *file_stat = (csync_vio_file_stat_t *) c_malloc(sizeof(csync_vio_file_stat_t));
|
||||
@@ -30,7 +31,9 @@ csync_vio_file_stat_t *csync_vio_file_stat_new(void) {
|
||||
csync_vio_file_stat_t* csync_vio_file_stat_copy(csync_vio_file_stat_t *file_stat) {
|
||||
csync_vio_file_stat_t *file_stat_cpy = csync_vio_file_stat_new();
|
||||
memcpy(file_stat_cpy, file_stat, sizeof(csync_vio_file_stat_t));
|
||||
file_stat_cpy->etag = c_strdup(file_stat_cpy->etag);
|
||||
if (file_stat_cpy->fields & CSYNC_VIO_FILE_STAT_FIELDS_ETAG) {
|
||||
file_stat_cpy->etag = c_strdup(file_stat_cpy->etag);
|
||||
}
|
||||
if (file_stat_cpy->directDownloadCookies) {
|
||||
file_stat_cpy->directDownloadCookies = c_strdup(file_stat_cpy->directDownloadCookies);
|
||||
}
|
||||
@@ -68,6 +71,7 @@ void csync_vio_file_stat_set_file_id( csync_vio_file_stat_t *dst, const char* sr
|
||||
void csync_vio_set_file_id( char* dst, const char *src ) {
|
||||
if( src && dst ) {
|
||||
if( strlen(src) > FILE_ID_BUF_SIZE ) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "Ignoring file_id because it is too long: %s", src);
|
||||
strcpy(dst, "");
|
||||
} else {
|
||||
strcpy(dst, src);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
==============
|
||||
Advanced Usage
|
||||
==============
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
=====================
|
||||
The Automatic Updater
|
||||
=====================
|
||||
|
||||
@@ -9,11 +10,15 @@ users only need to use their normal package managers. However, on Linux systems
|
||||
the Updater will check for updates and notify you when a new version is
|
||||
available.
|
||||
|
||||
.. note:: Because of various technical issues, desktop sync clients older than
|
||||
1.7 will not be allowed to connect and sync with the ownCloud 8.1 server. It is
|
||||
highly recommended to keep your client updated.
|
||||
|
||||
Basic Workflow
|
||||
--------------
|
||||
|
||||
The following sections describe how to use the Automatic Updater on different
|
||||
operating systems:
|
||||
operating systems.
|
||||
|
||||
Windows
|
||||
^^^^^^^
|
||||
@@ -26,6 +31,9 @@ If an update is available, and has been successfully downloaded, the ownCloud
|
||||
client starts a silent update prior to its next launch and then restarts
|
||||
itself. Should the silent update fail, the client offers a manual download.
|
||||
|
||||
When you upgrade from 1.7 you should restart Windows to ensure that all the new
|
||||
features in 1.8 are enabled.
|
||||
|
||||
.. note:: Administrative privileges are required to perform the update.
|
||||
|
||||
Mac OS X
|
||||
|
||||
@@ -18,6 +18,9 @@ and then use their package managers to install the desktop sync client. Linux
|
||||
users will also update their sync clients via package manager, and the client
|
||||
will display a notification when an update is available.
|
||||
|
||||
Linux users must also have a password manager enabled, such as GNOME Keyring or
|
||||
KWallet, so that the sync client can login automatically.
|
||||
|
||||
Improvements and New Features
|
||||
-----------------------------
|
||||
|
||||
@@ -32,6 +35,9 @@ including:
|
||||
and Nautilus on Linux.
|
||||
* Faster uploads and downloads.
|
||||
|
||||
.. note:: When you upgrade from 1.7, restart Windows to ensure that all new
|
||||
features are visible.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
|
||||
@@ -11,3 +11,6 @@ Desktop Sync client enables you to:
|
||||
Your files are always automatically synchronized between your ownCloud server
|
||||
and local PC.
|
||||
|
||||
.. note:: Because of various technical issues, desktop sync clients older than
|
||||
1.7 will not allowed to connect and sync with the ownCloud 8.1 server. It is
|
||||
highly recommended to keep your client updated.
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
When invoking the client from the command line, the following options are supported:
|
||||
You have the option of starting your ownCloud desktop client with the
|
||||
``owncloud`` command. The following options are supported:
|
||||
|
||||
``-h``, ``--help``
|
||||
``owncloud -h`` or ``owncloud --help``
|
||||
Displays all command options.
|
||||
|
||||
The other options are:
|
||||
|
||||
``--logwindow``
|
||||
Opens a window displaying log output.
|
||||
|
||||
``--logfile`` `<filename>`
|
||||
Write log output to the file specified. To write to stdout, specify `-` as the filename.
|
||||
Write log output to the file specified. To write to stdout, specify `-`
|
||||
as the filename.
|
||||
|
||||
``--logdir`` `<name>`
|
||||
Writes each synchronization log output in a new file in the specified directory.
|
||||
|
||||
Writes each synchronization log output in a new file in the specified
|
||||
directory.
|
||||
|
||||
``--logexpire`` `<hours>`
|
||||
Removes logs older than the value specified (in hours). This command is used with ``--logdir``.
|
||||
Removes logs older than the value specified (in hours). This command is
|
||||
used with ``--logdir``.
|
||||
|
||||
``--logflush``
|
||||
Clears (flushes) the log file after each write action.
|
||||
|
||||
``--confdir`` `<dirname>`
|
||||
Uses the specified configuration directory.
|
||||
|
||||
Uses the specified configuration directory.
|
||||
@@ -1,16 +1,14 @@
|
||||
The ownCloud Client packages contain a command line client that can be used to
|
||||
synchronize ownCloud files to client machines. The command line client is
|
||||
called ``owncloudcmd``.
|
||||
The ownCloud Client packages contain a command line client, ``owncloudcmd``, that can
|
||||
be used to synchronize ownCloud files to client machines.
|
||||
|
||||
owncloudcmd performs a single *sync run* and then exits the synchronization
|
||||
process. In this manner, owncloudcmd processes the differences between client
|
||||
and server directories and propagates the files to bring both repositories to
|
||||
the same state. Contrary to the GUI-based client, ``owncloudcmd`` does not
|
||||
repeat
|
||||
synchronizations on its own. It also does not monitor for file system changes.
|
||||
``owncloudcmd`` performs a single *sync run* and then exits the synchronization
|
||||
process. In this manner, ``owncloudcmd`` processes the differences between
|
||||
client and server directories and propagates the files to bring both
|
||||
repositories to the same state. Contrary to the GUI-based client,
|
||||
``owncloudcmd`` does not repeat synchronizations on its own. It also does not
|
||||
monitor for file system changes.
|
||||
|
||||
To invoke ``owncloudcmd``, you must provide the local and the remote
|
||||
repository
|
||||
To invoke ``owncloudcmd``, you must provide the local and the remote repository
|
||||
URL using the following command::
|
||||
|
||||
owncloudcmd [OPTIONS...] sourcedir owncloudurl
|
||||
@@ -18,45 +16,41 @@ URL using the following command::
|
||||
where ``sourcedir`` is the local directory and ``owncloudurl`` is
|
||||
the server URL.
|
||||
|
||||
.. note:: Prior to the 1.6 version of owncloudcmd, the tool only accepted
|
||||
``owncloud://`` or ``ownclouds://`` in place of ``http://`` and ``https://`` as
|
||||
a scheme. See ``Examples`` for details.
|
||||
|
||||
Other comand line switches supported by owncloudcmd include the following:
|
||||
Other command line switches supported by ``owncloudcmd`` include the following:
|
||||
|
||||
``--user``, ``-u`` ``[user]``
|
||||
Use ``user`` as the login name.
|
||||
Specify the user's login name.
|
||||
|
||||
``--password``, ``-p`` ``[password]``
|
||||
Use ``password`` as the password.
|
||||
Specify the user's password.
|
||||
|
||||
``-n``
|
||||
Use ``netrc (5)`` for login.
|
||||
Use ``netrc (5)`` for login.
|
||||
|
||||
``--non-interactive``
|
||||
Do not prompt for questions.
|
||||
Do not prompt for questions.
|
||||
|
||||
``--silent``, ``-s``
|
||||
Inhibits verbose log output.
|
||||
Inhibits verbose log output.
|
||||
|
||||
``--trust``
|
||||
Trust any SSL certificate, including invalid ones.
|
||||
Trust any SSL certificate, including invalid ones.
|
||||
|
||||
``--httpproxy http://[user@pass:]<server>:<port>``
|
||||
Uses the specified ``server`` as the HTTP proxy.
|
||||
|
||||
``--unsyncedfolders [file]``
|
||||
File containing list of folders to not sync
|
||||
|
||||
Credential Handling
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default, ``owncloudcmd`` reads the client configuration and uses the
|
||||
credentials of the GUI synchronization client. If no client is configured, or if you choose
|
||||
to use a different user to synchronize, you can specify the user password
|
||||
setting with the usual URL pattern. For example::
|
||||
credentials of the GUI synchronization client. If no client is configured, or if
|
||||
you choose to use a different user to synchronize, you can specify the user
|
||||
password setting with the usual URL pattern. For example::
|
||||
|
||||
https://user:secret@192.168.178.2/remote.php/webdav
|
||||
|
||||
Example
|
||||
~~~~~~~
|
||||
$ owncloudcmd / https://carla:secret@server/owncloud/remote.php/webdav/
|
||||
|
||||
To synchronize the ownCloud directory ``Music`` to the local directory
|
||||
``media/music``, through a proxy listening on port ``8080``, and on a gateway
|
||||
@@ -66,13 +60,5 @@ machine using IP address ``192.168.178.1``, the command line would be::
|
||||
$HOME/media/music \
|
||||
https://server/owncloud/remote.php/webdav/Music
|
||||
|
||||
``owncloudcmd`` will enquire user name and password, unless they have
|
||||
``owncloudcmd`` will prompt for the user name and password, unless they have
|
||||
been specified on the command line or ``-n`` has been passed.
|
||||
|
||||
Using the legacy scheme, the command line would be::
|
||||
|
||||
$ owncloudcmd --httpproxy http://192.168.178.1:8080 \
|
||||
$HOME/media/music \
|
||||
ownclouds://server/owncloud/remote.php/webdav/Music
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class SocketConnect(GObject.GObject):
|
||||
print("Sending failed.")
|
||||
self.reconnect()
|
||||
else:
|
||||
print "Cannot send, not connected!"
|
||||
print("Cannot send, not connected!")
|
||||
|
||||
def addListener(self, listener):
|
||||
self._listeners.append(listener)
|
||||
@@ -85,7 +85,7 @@ class SocketConnect(GObject.GObject):
|
||||
self.connected = True
|
||||
print("Setting connected to %r" % self.connected )
|
||||
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
|
||||
print "Socket watch id: "+str(self._watch_id)
|
||||
print("Socket watch id: "+str(self._watch_id))
|
||||
return False # don't run again
|
||||
except Exception as e:
|
||||
print("Could not connect to unix socket." + str(e))
|
||||
@@ -175,7 +175,7 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||
|
||||
def menu_share(self, menu, file):
|
||||
filename = get_local_path(file.get_uri())
|
||||
print "Share file "+filename
|
||||
print("Share file "+filename)
|
||||
socketConnect.sendCommand("SHARE:"+filename+"\n")
|
||||
|
||||
|
||||
|
||||
@@ -83,7 +83,6 @@
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;OCCONTEXTMENU_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<AdditionalIncludeDirectories>..\OCUtil;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
@@ -101,8 +100,8 @@
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;OCCONTEXTMENU_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>..\OCUtil;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -132,6 +131,7 @@
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\OCUtil;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalLibraryDirectories>..\$(Configuration)\$(Platform);</AdditionalLibraryDirectories>
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\OCUtil;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@@ -145,6 +146,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>..\OCUtil;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
||||
@@ -120,6 +120,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>NDEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@@ -136,6 +137,7 @@
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PreprocessorDefinitions>NDEBUG;_USING_V110_SDK71_;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "qtlocalpeer.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDataStream>
|
||||
#include <QTime>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
@@ -194,6 +194,9 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
|
||||
if (options->target_url.startsWith("http"))
|
||||
options->target_url.replace(0, 4, "owncloud");
|
||||
options->source_dir = args.takeLast();
|
||||
if (!options->source_dir.endsWith('/')) {
|
||||
options->source_dir.append('/');
|
||||
}
|
||||
if( !QFile::exists( options->source_dir )) {
|
||||
std::cerr << "Source dir '" << qPrintable(options->source_dir) << "' does not exist." << std::endl;
|
||||
exit(1);
|
||||
@@ -442,7 +445,9 @@ restart_sync:
|
||||
if (!f.open(QFile::ReadOnly)) {
|
||||
qCritical() << "Could not open file containing the list of unsynced folders: " << options.unsyncedfolders;
|
||||
} else {
|
||||
selectiveSyncList = QString::fromUtf8(f.readAll()).split('\n');
|
||||
// filter out empty lines and comments
|
||||
selectiveSyncList = QString::fromUtf8(f.readAll()).split('\n').filter(QRegExp("\\S+")).filter(QRegExp("^[^#]"));
|
||||
|
||||
for (int i = 0; i < selectiveSyncList.count(); ++i) {
|
||||
if (!selectiveSyncList.at(i).endsWith(QLatin1Char('/'))) {
|
||||
selectiveSyncList[i].append(QLatin1Char('/'));
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
#include "account.h"
|
||||
#include "simplesslerrorhandler.h"
|
||||
|
||||
bool SimpleSslErrorHandler::handleErrors(QList<QSslError> errors, QList<QSslCertificate> *certs, OCC::AccountPtr account)
|
||||
bool SimpleSslErrorHandler::handleErrors(QList<QSslError> errors, const QSslConfiguration &conf, QList<QSslCertificate> *certs, OCC::AccountPtr account)
|
||||
{
|
||||
(void) account;
|
||||
(void) conf;
|
||||
|
||||
if (!certs) {
|
||||
qDebug() << "Certs parameter required but is NULL!";
|
||||
|
||||
@@ -20,7 +20,7 @@ class QSslCertificate;
|
||||
|
||||
class SimpleSslErrorHandler : public OCC::AbstractSslErrorHandler {
|
||||
public:
|
||||
bool handleErrors(QList<QSslError> errors, QList<QSslCertificate> *certs, OCC::AccountPtr) Q_DECL_OVERRIDE;
|
||||
bool handleErrors(QList<QSslError> errors, const QSslConfiguration &conf, QList<QSslCertificate> *certs, OCC::AccountPtr) Q_DECL_OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // SIMPLESSLERRORHANDLER_H
|
||||
|
||||
@@ -154,10 +154,19 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}
|
||||
|
||||
qt_add_translation(client_I18N ${TRANSLATIONS})
|
||||
|
||||
IF( WIN32 )
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/version.rc
|
||||
@ONLY)
|
||||
set(client_version ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
ENDIF()
|
||||
|
||||
set( final_src
|
||||
${MIRALL_RC_SRC}
|
||||
${client_SRCS}
|
||||
${client_UI_SRCS}
|
||||
${client_version}
|
||||
${guiMoc}
|
||||
${client_I18N}
|
||||
${3rdparty_SRC}
|
||||
|
||||
@@ -28,9 +28,6 @@ public:
|
||||
* @return the list of migrated folder definitions
|
||||
*/
|
||||
QStringList migrateFolderDefinitons();
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -43,7 +43,9 @@ void AccountStateManager::setAccountState(AccountState *accountState)
|
||||
emit accountStateRemoved(_accountState);
|
||||
}
|
||||
_accountState = accountState;
|
||||
emit accountStateAdded(accountState);
|
||||
if (accountState) {
|
||||
emit accountStateAdded(accountState);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountStateManager::slotAccountAdded(AccountPtr account)
|
||||
@@ -54,13 +56,15 @@ void AccountStateManager::slotAccountAdded(AccountPtr account)
|
||||
AccountState::AccountState(AccountPtr account)
|
||||
: QObject(account.data())
|
||||
, _account(account)
|
||||
, _quotaInfo(new QuotaInfo(this))
|
||||
, _quotaInfo(0)
|
||||
, _state(AccountState::Disconnected)
|
||||
, _connectionStatus(ConnectionValidator::Undefined)
|
||||
, _waitingForNewCredentials(false)
|
||||
{
|
||||
qRegisterMetaType<AccountState*>("AccountState*");
|
||||
|
||||
_quotaInfo = new QuotaInfo(this); // Need to be initialized when 'this' is fully initialized
|
||||
|
||||
connect(account.data(), SIGNAL(invalidCredentials()),
|
||||
SLOT(slotInvalidCredentials()));
|
||||
connect(account.data(), SIGNAL(credentialsFetched(AbstractCredentials*)),
|
||||
@@ -179,6 +183,15 @@ void AccountState::checkConnectivity()
|
||||
conValidator->checkAuthentication();
|
||||
} else {
|
||||
// Check the server and then the auth.
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// There seems to be a bug in Qt on Windows where QNAM sometimes stops
|
||||
// working correctly after the computer woke up from sleep. See #2895 #2899
|
||||
// and #2973.
|
||||
// As an attempted workaround, reset the QNAM regularly if the account is
|
||||
// disconnected.
|
||||
account()->resetNetworkAccessManager();
|
||||
#endif
|
||||
conValidator->checkServerAndAuth();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +180,8 @@ Application::Application(int &argc, char **argv) :
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
// qDebug() << "* OCC shutdown";
|
||||
// Remove the account from the account manager so it can be deleted.
|
||||
AccountManager::instance()->setAccount(AccountPtr());
|
||||
}
|
||||
|
||||
void Application::slotLogin()
|
||||
@@ -403,7 +404,10 @@ void Application::parseOptions(const QStringList &options)
|
||||
} else if (option == QLatin1String("--confdir")) {
|
||||
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
|
||||
QString confDir = it.next();
|
||||
ConfigFile::setConfDir( confDir );
|
||||
if (!ConfigFile::setConfDir( confDir )) {
|
||||
std::cerr << "Invalid path passed to --confdir" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
} else {
|
||||
showHelp();
|
||||
}
|
||||
|
||||
@@ -772,6 +772,7 @@ void Folder::startSync(const QStringList &pathList)
|
||||
_timeSinceLastSyncStart.restart();
|
||||
_syncResult.clearErrors();
|
||||
_syncResult.setStatus( SyncResult::SyncPrepare );
|
||||
_syncResult.setSyncFileItemVector(SyncFileItemVector());
|
||||
emit syncStateChange();
|
||||
|
||||
qDebug() << "*** Start syncing - client version"
|
||||
|
||||
@@ -873,6 +873,13 @@ void FolderMan::setDirtyProxy(bool value)
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
if(f) {
|
||||
f->setProxyDirty(value);
|
||||
|
||||
if (f->accountState() && f->accountState()->account()
|
||||
&& f->accountState()->account()->networkAccessManager()) {
|
||||
// Need to do this have us not use the old determined system proxy
|
||||
f->accountState()->account()->networkAccessManager()->setProxy(
|
||||
QNetworkProxy(QNetworkProxy::DefaultProxy));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,6 +246,12 @@ FolderWizardRemotePath::FolderWizardRemotePath(AccountPtr account)
|
||||
_lscolTimer.setInterval(500);
|
||||
_lscolTimer.setSingleShot(true);
|
||||
connect(&_lscolTimer, SIGNAL(timeout()), SLOT(slotLsColFolderEntry()));
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
_ui.folderTreeWidget->header()->setSectionResizeMode(0,QHeaderView::ResizeToContents);
|
||||
// Make sure that there will be a scrollbar when the contents is too wide
|
||||
_ui.folderTreeWidget->header()->setStretchLastSection(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FolderWizardRemotePath::slotAddRemoteFolder()
|
||||
@@ -291,6 +297,8 @@ void FolderWizardRemotePath::slotCreateRemoteFolderFinished(QNetworkReply::Netwo
|
||||
qDebug() << "** webdav mkdir request finished";
|
||||
showWarn(tr("Folder was successfully created on %1.").arg(Theme::instance()->appNameGUI()));
|
||||
slotRefreshFolders();
|
||||
_ui.folderEntry->setText(static_cast<MkColJob *>(sender())->path());
|
||||
slotLsColFolderEntry();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
|
||||
connect(this, SIGNAL(accepted()), SLOT(slotUpdateLocalIgnoreList()));
|
||||
ui->removePushButton->setEnabled(false);
|
||||
connect(ui->listWidget, SIGNAL(itemSelectionChanged()), SLOT(slotItemSelectionChanged()));
|
||||
connect(ui->listWidget, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(slotItemChanged(QListWidgetItem*)));
|
||||
connect(ui->removePushButton, SIGNAL(clicked()), SLOT(slotRemoveCurrentItem()));
|
||||
connect(ui->addPushButton, SIGNAL(clicked()), SLOT(slotAddPattern()));
|
||||
connect(ui->listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(slotEditPattern(QListWidgetItem*)));
|
||||
|
||||
@@ -145,6 +145,9 @@ void OwncloudSetupWizard::slotDetermineAuthType(const QString &urlString)
|
||||
}
|
||||
AccountPtr account = _ocWizard->account();
|
||||
account->setUrl(url);
|
||||
// Reset the proxy which might had been determined previously in ConnectionValidator::checkServerAndAuth()
|
||||
// when there was a previous account.
|
||||
account->networkAccessManager()->setProxy(QNetworkProxy(QNetworkProxy::DefaultProxy));
|
||||
// Set fake credentials beforfe we check what credential it actually is.
|
||||
account->setCredentials(CredentialsFactory::create("dummy"));
|
||||
CheckServerJob *job = new CheckServerJob(_ocWizard->account(), this);
|
||||
@@ -214,11 +217,71 @@ void OwncloudSetupWizard::testOwnCloudConnect()
|
||||
auto *job = new PropfindJob(account, "/", this);
|
||||
job->setIgnoreCredentialFailure(true);
|
||||
job->setProperties(QList<QByteArray>() << "getlastmodified");
|
||||
connect(job, SIGNAL(result(QVariantMap)), _ocWizard, SLOT(successfulStep()));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotConnectionCheck(QNetworkReply*)));
|
||||
connect(job, SIGNAL(result(QVariantMap)), _ocWizard, SLOT(successfulStep()));
|
||||
connect(job, SIGNAL(finishedWithError()), this, SLOT(slotAuthError()));
|
||||
job->start();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotAuthError()
|
||||
{
|
||||
QString errorMsg;
|
||||
|
||||
PropfindJob* job = qobject_cast<PropfindJob*>(sender());
|
||||
if (!job) {
|
||||
qWarning() << "Can't check for authed redirects. This slot should be invoked from PropfindJob!";
|
||||
return;
|
||||
}
|
||||
QNetworkReply* reply = job->reply();
|
||||
|
||||
// If there were redirects on the *authed* requests, also store
|
||||
// the updated server URL, similar to redirects on status.php.
|
||||
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
if (!redirectUrl.isEmpty()) {
|
||||
qDebug() << "authed request was redirected to" << redirectUrl.toString();
|
||||
|
||||
// strip the expected path
|
||||
QString path = redirectUrl.path();
|
||||
static QString expectedPath = "/" + _ocWizard->account()->davPath();
|
||||
if (path.endsWith(expectedPath)) {
|
||||
path.chop(expectedPath.size());
|
||||
redirectUrl.setPath(path);
|
||||
|
||||
qDebug() << "setting account url to" << redirectUrl.toString();
|
||||
_ocWizard->account()->setUrl(redirectUrl);
|
||||
testOwnCloudConnect();
|
||||
return;
|
||||
}
|
||||
errorMsg = tr("The authenticated request to the server was redirected to "
|
||||
"'%1'. The URL is bad, the server is misconfigured.")
|
||||
.arg(redirectUrl.toString());
|
||||
|
||||
// A 404 is actually a success: we were authorized to know that the folder does
|
||||
// not exist. It will be created later...
|
||||
} else if (reply->error() == QNetworkReply::ContentNotFoundError) {
|
||||
_ocWizard->successfulStep();
|
||||
return;
|
||||
|
||||
// Provide messages for other errors, such as invalid credentials.
|
||||
} else if (reply->error() != QNetworkReply::NoError) {
|
||||
errorMsg = reply->errorString();
|
||||
if (!_ocWizard->account()->credentials()->stillValid(reply)) {
|
||||
errorMsg = tr("Access forbidden by server. To verify that you have proper access, "
|
||||
"<a href=\"%1\">click here</a> to access the service with your browser.")
|
||||
.arg(_ocWizard->account()->url().toString());
|
||||
}
|
||||
|
||||
// Something else went wrong, maybe the response was 200 but with invalid data.
|
||||
} else {
|
||||
errorMsg = tr("There was an invalid response to an authenticated webdav request");
|
||||
}
|
||||
|
||||
_ocWizard->show();
|
||||
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds) {
|
||||
_ocWizard->back();
|
||||
}
|
||||
_ocWizard->displayError(errorMsg, _ocWizard->currentId() == WizardCommon::Page_ServerSetup && checkDowngradeAdvised(reply));
|
||||
}
|
||||
|
||||
bool OwncloudSetupWizard::checkDowngradeAdvised(QNetworkReply* reply)
|
||||
{
|
||||
if(reply->url().scheme() != QLatin1String("https")) {
|
||||
@@ -242,29 +305,6 @@ bool OwncloudSetupWizard::checkDowngradeAdvised(QNetworkReply* reply)
|
||||
return true;
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotConnectionCheck(QNetworkReply* reply)
|
||||
{
|
||||
QString msg = reply->errorString();
|
||||
switch (reply->error()) {
|
||||
case QNetworkReply::NoError:
|
||||
case QNetworkReply::ContentNotFoundError:
|
||||
_ocWizard->successfulStep();
|
||||
break;
|
||||
default:
|
||||
if (!_ocWizard->account()->credentials()->stillValid(reply)) {
|
||||
msg = tr("Access forbidden by server. To verify that you have proper access, "
|
||||
"<a href=\"%1\">click here</a> to access the service with your browser.")
|
||||
.arg(_ocWizard->account()->url().toString());
|
||||
}
|
||||
_ocWizard->show();
|
||||
if (_ocWizard->currentId() == WizardCommon::Page_ShibbolethCreds) {
|
||||
_ocWizard->back();
|
||||
}
|
||||
_ocWizard->displayError(msg, _ocWizard->currentId() == WizardCommon::Page_ServerSetup && checkDowngradeAdvised(reply));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFolder, const QString& remoteFolder)
|
||||
{
|
||||
qDebug() << "Setup local sync folder for new oC connection " << localFolder;
|
||||
@@ -291,7 +331,7 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFo
|
||||
}
|
||||
if (nextStep) {
|
||||
EntityExistsJob *job = new EntityExistsJob(_ocWizard->account(), _ocWizard->account()->davPath() + remoteFolder, this);
|
||||
connect(job, SIGNAL(exists(QNetworkReply*)), SLOT(slotAuthCheckReply(QNetworkReply*)));
|
||||
connect(job, SIGNAL(exists(QNetworkReply*)), SLOT(slotRemoteFolderExists(QNetworkReply*)));
|
||||
job->start();
|
||||
} else {
|
||||
finalizeSetup( false );
|
||||
@@ -299,7 +339,7 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFo
|
||||
}
|
||||
|
||||
// ### TODO move into EntityExistsJob once we decide if/how to return gui strings from jobs
|
||||
void OwncloudSetupWizard::slotAuthCheckReply(QNetworkReply *reply)
|
||||
void OwncloudSetupWizard::slotRemoteFolderExists(QNetworkReply *reply)
|
||||
{
|
||||
bool ok = true;
|
||||
QString error;
|
||||
@@ -519,8 +559,10 @@ bool DetermineAuthTypeJob::finished()
|
||||
} else if (redirection.toString().endsWith(account()->davPath())) {
|
||||
// do a new run
|
||||
_redirects++;
|
||||
resetTimeout();
|
||||
setReply(getRequest(redirection));
|
||||
setupConnections(reply());
|
||||
return false; // don't discard
|
||||
} else {
|
||||
QRegExp shibbolethyWords("SAML|wayf");
|
||||
|
||||
|
||||
@@ -62,10 +62,10 @@ private slots:
|
||||
void slotNoOwnCloudFoundAuthTimeout(const QUrl&url);
|
||||
|
||||
void slotConnectToOCUrl(const QString&);
|
||||
void slotConnectionCheck(QNetworkReply*);
|
||||
void slotAuthError();
|
||||
|
||||
void slotCreateLocalAndRemoteFolders(const QString&, const QString&);
|
||||
void slotAuthCheckReply(QNetworkReply*);
|
||||
void slotRemoteFolderExists(QNetworkReply*);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError);
|
||||
void slotAssistantFinished( int );
|
||||
void slotSkipFolderConfiguration();
|
||||
|
||||
@@ -73,16 +73,10 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) :
|
||||
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
|
||||
_copyBtn->setEnabled(false);
|
||||
connect(_copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
|
||||
|
||||
ConfigFile cfg;
|
||||
cfg.restoreGeometryHeader(_ui->_treeWidget->header());
|
||||
}
|
||||
|
||||
ProtocolWidget::~ProtocolWidget()
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.saveGeometryHeader(_ui->_treeWidget->header() );
|
||||
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
@@ -137,6 +131,20 @@ void ProtocolWidget::slotRetrySync()
|
||||
folderMan->slotScheduleAllFolders();
|
||||
}
|
||||
|
||||
void ProtocolWidget::showEvent(QShowEvent *ev)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.restoreGeometryHeader(_ui->_treeWidget->header());
|
||||
QWidget::showEvent(ev);
|
||||
}
|
||||
|
||||
void ProtocolWidget::hideEvent(QHideEvent *ev)
|
||||
{
|
||||
ConfigFile cfg;
|
||||
cfg.saveGeometryHeader(_ui->_treeWidget->header() );
|
||||
QWidget::hideEvent(ev);
|
||||
}
|
||||
|
||||
void ProtocolWidget::cleanIgnoreItems(const QString& folder)
|
||||
{
|
||||
int itemCnt = _ui->_treeWidget->topLevelItemCount();
|
||||
@@ -159,16 +167,11 @@ void ProtocolWidget::cleanIgnoreItems(const QString& folder)
|
||||
|
||||
QString ProtocolWidget::timeString(QDateTime dt, QLocale::FormatType format) const
|
||||
{
|
||||
QLocale loc = QLocale::system();
|
||||
QString timeStr;
|
||||
|
||||
if( format == QLocale::NarrowFormat ) {
|
||||
timeStr = loc.toString(dt, QLocale::NarrowFormat);
|
||||
} else {
|
||||
timeStr = loc.toString(dt, format);
|
||||
}
|
||||
|
||||
return timeStr;
|
||||
const QLocale loc = QLocale::system();
|
||||
QString dtFormat = loc.dateTimeFormat(format);
|
||||
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
|
||||
dtFormat.replace(re, "\\1:mm:ss");
|
||||
return loc.toString(dt, dtFormat);
|
||||
}
|
||||
|
||||
void ProtocolWidget::slotOpenFile( QTreeWidgetItem *item, int )
|
||||
|
||||
@@ -39,8 +39,6 @@ public:
|
||||
explicit ProtocolWidget(QWidget *parent = 0);
|
||||
~ProtocolWidget();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void slotProgressInfo( const QString& folder, const Progress::Info& progress );
|
||||
void slotOpenFile( QTreeWidgetItem* item, int );
|
||||
@@ -49,6 +47,10 @@ protected slots:
|
||||
void copyToClipboard();
|
||||
void slotRetrySync();
|
||||
|
||||
protected:
|
||||
void showEvent(QShowEvent *);
|
||||
void hideEvent(QHideEvent *);
|
||||
|
||||
signals:
|
||||
void guiLog(const QString&, const QString&);
|
||||
|
||||
|
||||
@@ -26,10 +26,33 @@
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QScopedValueRollback>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QLabel>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
|
||||
class SelectiveSyncTreeViewItem : public QTreeWidgetItem {
|
||||
public:
|
||||
SelectiveSyncTreeViewItem(int type = QTreeWidgetItem::Type)
|
||||
: QTreeWidgetItem(type) { }
|
||||
SelectiveSyncTreeViewItem(const QStringList &strings, int type = QTreeWidgetItem::Type)
|
||||
: QTreeWidgetItem(strings, type) { }
|
||||
SelectiveSyncTreeViewItem(QTreeWidget *view, int type = QTreeWidgetItem::Type)
|
||||
: QTreeWidgetItem(view, type) { }
|
||||
SelectiveSyncTreeViewItem(QTreeWidgetItem *parent, int type = QTreeWidgetItem::Type)
|
||||
: QTreeWidgetItem(parent, type) { }
|
||||
|
||||
private:
|
||||
bool operator<(const QTreeWidgetItem &other)const {
|
||||
int column = treeWidget()->sortColumn();
|
||||
if (column == 1) {
|
||||
return data(1, Qt::UserRole).toLongLong() < other.data(1, Qt::UserRole).toLongLong();
|
||||
}
|
||||
return QTreeWidgetItem::operator <(other);
|
||||
}
|
||||
};
|
||||
|
||||
SelectiveSyncTreeView::SelectiveSyncTreeView(AccountPtr account, QWidget* parent)
|
||||
: QTreeWidget(parent), _inserting(false), _account(account)
|
||||
{
|
||||
@@ -101,9 +124,9 @@ void SelectiveSyncTreeView::recursiveInsert(QTreeWidgetItem* parent, QStringList
|
||||
parent->setToolTip(0, path);
|
||||
parent->setData(0, Qt::UserRole, path);
|
||||
} else {
|
||||
QTreeWidgetItem *item = findFirstChild(parent, pathTrail.first());
|
||||
SelectiveSyncTreeViewItem *item = static_cast<SelectiveSyncTreeViewItem*>(findFirstChild(parent, pathTrail.first()));
|
||||
if (!item) {
|
||||
item = new QTreeWidgetItem(parent);
|
||||
item = new SelectiveSyncTreeViewItem(parent);
|
||||
if (parent->checkState(0) == Qt::Checked
|
||||
|| parent->checkState(0) == Qt::PartiallyChecked) {
|
||||
item->setCheckState(0, Qt::Checked);
|
||||
@@ -138,7 +161,7 @@ void SelectiveSyncTreeView::slotUpdateDirectories(const QStringList&list)
|
||||
QScopedValueRollback<bool> isInserting(_inserting);
|
||||
_inserting = true;
|
||||
|
||||
QTreeWidgetItem *root = topLevelItem(0);
|
||||
SelectiveSyncTreeViewItem *root = static_cast<SelectiveSyncTreeViewItem*>(topLevelItem(0));
|
||||
|
||||
if (!root && list.size() <= 1) {
|
||||
_loading->setText(tr("No subfolders currently on the server."));
|
||||
@@ -149,7 +172,7 @@ void SelectiveSyncTreeView::slotUpdateDirectories(const QStringList&list)
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
root = new QTreeWidgetItem(this);
|
||||
root = new SelectiveSyncTreeViewItem(this);
|
||||
root->setText(0, _rootName);
|
||||
root->setIcon(0, Theme::instance()->applicationIcon());
|
||||
root->setData(0, Qt::UserRole, QString());
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "folder.h"
|
||||
#include "theme.h"
|
||||
#include "syncresult.h"
|
||||
#include "configfile.h"
|
||||
|
||||
#include "QProgressIndicator.h"
|
||||
#include <QBuffer>
|
||||
@@ -44,6 +45,8 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
|
||||
_resharingAllowed(resharingAllowed)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setObjectName("SharingDialog"); // required as group for saveGeometry call
|
||||
|
||||
_ui->setupUi(this);
|
||||
_ui->pushButton_copy->setIcon(QIcon::fromTheme("edit-copy"));
|
||||
_ui->pushButton_copy->setEnabled(false);
|
||||
@@ -84,24 +87,31 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
|
||||
QIcon icon = icon_provider.icon(f_info);
|
||||
_ui->label_icon->setPixmap(icon.pixmap(40,40));
|
||||
|
||||
QString name;
|
||||
if( f_info.isDir() ) {
|
||||
name = tr("Share Directory");
|
||||
} else {
|
||||
name = tr("Share File");
|
||||
}
|
||||
_ui->groupBox->setTitle(name);
|
||||
|
||||
QString lPath(_localPath);
|
||||
if( lPath.length() > 50) {
|
||||
lPath = QLatin1String("...")+lPath.right(50);
|
||||
}
|
||||
_ui->label_name->setText(tr("Local path: %1").arg(lPath));
|
||||
QFileInfo lPath(_localPath);
|
||||
QString fileName = lPath.fileName();
|
||||
_ui->label_name->setText(tr("%1").arg(fileName));
|
||||
QFont f( _ui->label_name->font());
|
||||
f.setPointSize( f.pointSize() * 1.4 );
|
||||
_ui->label_name->setFont( f );
|
||||
|
||||
_ui->label_sharePath->setWordWrap(true);
|
||||
_ui->label_sharePath->setText(tr("%1 path: %2").arg(Theme::instance()->appNameGUI()).arg(_sharePath));
|
||||
QString ocDir(_sharePath);
|
||||
ocDir.truncate(ocDir.length()-fileName.length());
|
||||
|
||||
if( ocDir == QLatin1String("/")) {
|
||||
_ui->label_sharePath->setText(QString());
|
||||
} else {
|
||||
if( ocDir.startsWith(QLatin1Char('/')) ) {
|
||||
ocDir = ocDir.mid(1, -1);
|
||||
}
|
||||
if( ocDir.endsWith(QLatin1Char('/')) ) {
|
||||
ocDir.chop(1);
|
||||
}
|
||||
_ui->label_sharePath->setText(tr("Folder: %2").arg(ocDir));
|
||||
}
|
||||
|
||||
this->setWindowTitle(tr("%1 Sharing").arg(Theme::instance()->appNameGUI()));
|
||||
_ui->label_password->setText(tr("Set p&assword"));
|
||||
_ui->checkBox_password->setText(tr("P&assword protect"));
|
||||
// check if the file is already inside of a synced folder
|
||||
if( sharePath.isEmpty() ) {
|
||||
// The file is not yet in an ownCloud synced folder. We could automatically
|
||||
@@ -128,6 +138,12 @@ ShareDialog::ShareDialog(AccountPtr account, const QString &sharePath, const QSt
|
||||
_ui->errorLabel->hide();
|
||||
}
|
||||
|
||||
void ShareDialog::done( int r ) {
|
||||
ConfigFile cfg;
|
||||
cfg.saveGeometry(this);
|
||||
QDialog::done(r);
|
||||
}
|
||||
|
||||
void ShareDialog::setExpireDate(const QDate &date)
|
||||
{
|
||||
if( _public_share_id == 0 ) {
|
||||
@@ -331,19 +347,40 @@ void ShareDialog::slotSharesFetched(const QString &reply)
|
||||
}
|
||||
}
|
||||
|
||||
void ShareDialog::resizeEvent(QResizeEvent *e)
|
||||
{
|
||||
QDialog::resizeEvent(e);
|
||||
redrawElidedUrl();
|
||||
}
|
||||
|
||||
void ShareDialog::redrawElidedUrl()
|
||||
{
|
||||
QString u;
|
||||
|
||||
if( !_shareUrl.isEmpty() ) {
|
||||
QFontMetrics fm( _ui->_labelShareLink->font() );
|
||||
int linkLengthPixel = _ui->_labelShareLink->width();
|
||||
|
||||
const QUrl realUrl(_shareUrl);
|
||||
QString elidedUrl = fm.elidedText(_shareUrl, Qt::ElideRight, linkLengthPixel);
|
||||
|
||||
u = QString("<a href=\"%1\">%2</a>").arg(realUrl.toString(QUrl::None)).arg(elidedUrl);
|
||||
}
|
||||
_ui->_labelShareLink->setText(u);
|
||||
}
|
||||
|
||||
void ShareDialog::setShareLink( const QString& url )
|
||||
{
|
||||
// FIXME: shorten the url for output.
|
||||
const QUrl realUrl(url);
|
||||
if( realUrl.isValid() ) {
|
||||
const QString u = QString("<a href=\"%1\">%2</a>").arg(realUrl.toString(QUrl::None)).arg(url);
|
||||
_ui->_labelShareLink->setText(u);
|
||||
_shareUrl = url;
|
||||
_ui->pushButton_copy->setEnabled(true);
|
||||
} else {
|
||||
_shareUrl.clear();
|
||||
_ui->_labelShareLink->setText(QString::null);
|
||||
}
|
||||
redrawElidedUrl();
|
||||
|
||||
}
|
||||
|
||||
@@ -407,7 +444,7 @@ void ShareDialog::slotCreateShareFetched(const QString &reply)
|
||||
// there needs to be a password
|
||||
_ui->checkBox_password->setChecked(true);
|
||||
_ui->checkBox_password->setVisible(false);
|
||||
_ui->label_password->setText(tr("Public shå requires a password:"));
|
||||
_ui->checkBox_password->setText(tr("Public shå requires a password:"));
|
||||
_ui->lineEdit_password->setFocus();
|
||||
_ui->widget_shareLink->show();
|
||||
|
||||
@@ -421,13 +458,7 @@ void ShareDialog::slotCreateShareFetched(const QString &reply)
|
||||
bool success;
|
||||
QVariantMap json = QtJson::parse(reply, success).toMap();
|
||||
_public_share_id = json.value("ocs").toMap().values("data")[0].toMap().value("id").toULongLong();
|
||||
QString url = json.value("ocs").toMap().values("data")[0].toMap().value("url").toString();
|
||||
|
||||
setShareLink(url);
|
||||
|
||||
setShareCheckBoxTitle(true);
|
||||
|
||||
_ui->widget_shareLink->show();
|
||||
getShares();
|
||||
}
|
||||
|
||||
void ShareDialog::slotCheckBoxPasswordClicked()
|
||||
@@ -487,8 +518,8 @@ int ShareDialog::checkJsonReturnCode(const QString &reply, QString &message)
|
||||
|
||||
void ShareDialog::setShareCheckBoxTitle(bool haveShares)
|
||||
{
|
||||
const QString noSharesTitle(tr("Check to &share by public link"));
|
||||
const QString haveSharesTitle(tr("&Shared by public link (uncheck to delete share)"));
|
||||
const QString noSharesTitle(tr("&Share link"));
|
||||
const QString haveSharesTitle(tr("&Share link"));
|
||||
|
||||
if( haveShares ) {
|
||||
_ui->checkBox_shareLink->setText( haveSharesTitle );
|
||||
|
||||
@@ -49,7 +49,6 @@ class ShareDialog;
|
||||
|
||||
class AbstractCredentials;
|
||||
class QuotaInfo;
|
||||
class MirallAccessManager;
|
||||
class SyncResult;
|
||||
|
||||
class ShareDialog : public QDialog
|
||||
@@ -76,11 +75,15 @@ private slots:
|
||||
void slotPasswordChanged(const QString& newText);
|
||||
void slotPushButtonCopyLinkPressed();
|
||||
void slotThumbnailFetched(const int &statusCode, const QByteArray &reply);
|
||||
|
||||
void done( int r );
|
||||
private:
|
||||
void setShareCheckBoxTitle(bool haveShares);
|
||||
void displayError(int code);
|
||||
void displayError(const QString& errMsg);
|
||||
void setShareLink( const QString& url );
|
||||
void resizeEvent(QResizeEvent *e);
|
||||
void redrawElidedUrl();
|
||||
|
||||
Ui::ShareDialog *_ui;
|
||||
AccountPtr _account;
|
||||
|
||||
@@ -6,23 +6,71 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>403</width>
|
||||
<height>296</height>
|
||||
<width>372</width>
|
||||
<height>241</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Share NewDocument.odt</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="label_icon">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_name">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>share label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="2">
|
||||
<widget class="QProgressIndicator" name="pi_share" native="true"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_sharePath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ownCloud Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_shareLink">
|
||||
<property name="topMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_shareLink">
|
||||
<property name="text">
|
||||
@@ -34,8 +82,14 @@
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QWidget" name="widget_shareLink" native="true">
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
@@ -68,7 +122,7 @@
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_copy">
|
||||
<property name="text">
|
||||
<string>Copy &Link</string>
|
||||
<string>Copy &link</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -79,30 +133,36 @@
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_password">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
<string>Set password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_password">
|
||||
<property name="text">
|
||||
<string>Set p&assword</string>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>checkBox_password</cstring>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_password">
|
||||
<property name="echoMode">
|
||||
@@ -112,15 +172,24 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton_setPassword">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set &Password</string>
|
||||
<string>Set &password </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkBox_expire">
|
||||
<property name="text">
|
||||
@@ -140,80 +209,6 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Share Info</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="label_icon">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="label_name">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>share label</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="2">
|
||||
<widget class="QProgressIndicator" name="pi_share" native="true"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_sharePath">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ownCloud Path:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -227,7 +222,37 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="errorLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>errorLabel</zorder>
|
||||
<zorder>widget_shareLink</zorder>
|
||||
<zorder>buttonBox</zorder>
|
||||
<zorder>checkBox_password</zorder>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<customwidgets>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) by Dominik Schmidt <dev@dominik-schmidt.de>
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "syncfileitem.h"
|
||||
#include "filesystem.h"
|
||||
#include "version.h"
|
||||
#include "accountstate.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
@@ -136,7 +138,9 @@ SocketApi::~SocketApi()
|
||||
{
|
||||
DEBUG << "dtor";
|
||||
_localServer.close();
|
||||
qDeleteAll(_listeners);
|
||||
// All remaining sockets will be destroyed with _localServer, their parent
|
||||
Q_ASSERT(_listeners.isEmpty() || _listeners.first()->parent() == &_localServer);
|
||||
_listeners.clear();
|
||||
slotClearExcludesList();
|
||||
}
|
||||
|
||||
@@ -163,7 +167,7 @@ void SocketApi::slotReadExcludes()
|
||||
|
||||
void SocketApi::slotNewConnection()
|
||||
{
|
||||
SocketType* socket = _localServer.nextPendingConnection();
|
||||
QLocalSocket* socket = _localServer.nextPendingConnection();
|
||||
|
||||
if( ! socket ) {
|
||||
return;
|
||||
@@ -195,7 +199,7 @@ void SocketApi::onLostConnection()
|
||||
{
|
||||
DEBUG << "Lost connection " << sender();
|
||||
|
||||
SocketType* socket = qobject_cast<SocketType*>(sender());
|
||||
QLocalSocket* socket = qobject_cast<QLocalSocket*>(sender());
|
||||
_listeners.removeAll(socket);
|
||||
socket->deleteLater();
|
||||
}
|
||||
@@ -203,7 +207,7 @@ void SocketApi::onLostConnection()
|
||||
|
||||
void SocketApi::slotReadSocket()
|
||||
{
|
||||
SocketType* socket = qobject_cast<SocketType*>(sender());
|
||||
QLocalSocket* socket = qobject_cast<QLocalSocket*>(sender());
|
||||
Q_ASSERT(socket);
|
||||
|
||||
while(socket->canReadLine()) {
|
||||
@@ -211,12 +215,12 @@ void SocketApi::slotReadSocket()
|
||||
QString command = line.split(":").first();
|
||||
QString function = QString(QLatin1String("command_")).append(command);
|
||||
|
||||
QString functionWithArguments = function + QLatin1String("(QString,SocketType*)");
|
||||
QString functionWithArguments = function + QLatin1String("(QString,QLocalSocket*)");
|
||||
int indexOfMethod = this->metaObject()->indexOfMethod(functionWithArguments.toAscii());
|
||||
|
||||
QString argument = line.remove(0, command.length()+1).trimmed();
|
||||
if(indexOfMethod != -1) {
|
||||
QMetaObject::invokeMethod(this, function.toAscii(), Q_ARG(QString, argument), Q_ARG(SocketType*, socket));
|
||||
QMetaObject::invokeMethod(this, function.toAscii(), Q_ARG(QString, argument), Q_ARG(QLocalSocket*, socket));
|
||||
} else {
|
||||
DEBUG << "The command is not supported by this version of the client:" << command << "with argument:" << argument;
|
||||
}
|
||||
@@ -228,7 +232,7 @@ void SocketApi::slotRegisterPath( const QString& alias )
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
if (f) {
|
||||
QString message = buildRegisterPathMessage(f->path());
|
||||
foreach(SocketType *socket, _listeners) {
|
||||
foreach(QLocalSocket *socket, _listeners) {
|
||||
sendMessage(socket, message);
|
||||
}
|
||||
}
|
||||
@@ -333,7 +337,7 @@ void SocketApi::slotSyncItemDiscovered(const QString &folder, const SyncFileItem
|
||||
|
||||
|
||||
|
||||
void SocketApi::sendMessage(SocketType *socket, const QString& message, bool doWait)
|
||||
void SocketApi::sendMessage(QLocalSocket *socket, const QString& message, bool doWait)
|
||||
{
|
||||
DEBUG << "Sending message: " << message;
|
||||
QString localMessage = message;
|
||||
@@ -368,12 +372,12 @@ void SocketApi::broadcastMessage( const QString& verb, const QString& path, cons
|
||||
|
||||
// sendMessage already has a debug output
|
||||
//DEBUG << "Broadcasting to" << _listeners.count() << "listeners: " << msg;
|
||||
foreach(SocketType *socket, _listeners) {
|
||||
foreach(QLocalSocket *socket, _listeners) {
|
||||
sendMessage(socket, msg, doWait);
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketType* socket)
|
||||
void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, QLocalSocket* socket)
|
||||
{
|
||||
// This command is the same as RETRIEVE_FILE_STATUS
|
||||
|
||||
@@ -381,7 +385,7 @@ void SocketApi::command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketTy
|
||||
command_RETRIEVE_FILE_STATUS(argument, socket);
|
||||
}
|
||||
|
||||
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType* socket)
|
||||
void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, QLocalSocket* socket)
|
||||
{
|
||||
if( !socket ) {
|
||||
qDebug() << "No valid socket object.";
|
||||
@@ -409,7 +413,7 @@ void SocketApi::command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType
|
||||
sendMessage(socket, message);
|
||||
}
|
||||
|
||||
void SocketApi::command_SHARE(const QString& localFile, SocketType* socket)
|
||||
void SocketApi::command_SHARE(const QString& localFile, QLocalSocket* socket)
|
||||
{
|
||||
if (!socket) {
|
||||
qDebug() << Q_FUNC_INFO << "No valid socket object.";
|
||||
@@ -423,6 +427,10 @@ void SocketApi::command_SHARE(const QString& localFile, SocketType* socket)
|
||||
const QString message = QLatin1String("SHARE:NOP:")+QDir::toNativeSeparators(localFile);
|
||||
// files that are not within a sync folder are not synced.
|
||||
sendMessage(socket, message);
|
||||
} else if (!shareFolder->accountState()->isConnected()) {
|
||||
const QString message = QLatin1String("SHARE:NOTCONNECTED:")+QDir::toNativeSeparators(localFile);
|
||||
// if the folder isn't connected, don't open the share dialog
|
||||
sendMessage(socket, message);
|
||||
} else {
|
||||
const QString folderForPath = shareFolder->path();
|
||||
const QString remotePath = shareFolder->remotePath() + localFile.right(localFile.count()-folderForPath.count()+1);
|
||||
@@ -443,12 +451,12 @@ void SocketApi::command_SHARE(const QString& localFile, SocketType* socket)
|
||||
}
|
||||
}
|
||||
|
||||
void SocketApi::command_VERSION(const QString&, SocketType* socket)
|
||||
void SocketApi::command_VERSION(const QString&, QLocalSocket* socket)
|
||||
{
|
||||
sendMessage(socket, QLatin1String("VERSION:" MIRALL_VERSION_STRING ":" MIRALL_SOCKET_API_VERSION));
|
||||
}
|
||||
|
||||
void SocketApi::command_SHARE_MENU_TITLE(const QString &, SocketType* socket)
|
||||
void SocketApi::command_SHARE_MENU_TITLE(const QString &, QLocalSocket* socket)
|
||||
{
|
||||
sendMessage(socket, QLatin1String("SHARE_MENU_TITLE:") + tr("Share with %1", "parameter is ownCloud").arg(Theme::instance()->appNameGUI()));
|
||||
}
|
||||
|
||||
@@ -37,8 +37,6 @@ class QStringList;
|
||||
|
||||
namespace OCC {
|
||||
|
||||
typedef QLocalSocket SocketType;
|
||||
|
||||
class SyncFileStatus;
|
||||
class Folder;
|
||||
|
||||
@@ -72,24 +70,20 @@ private:
|
||||
SyncJournalFileRecord dbFileRecord_capi( Folder *folder, QString fileName );
|
||||
SqlQuery *getSqlQuery( Folder *folder );
|
||||
|
||||
void sendMessage(SocketType* socket, const QString& message, bool doWait = false);
|
||||
void sendMessage(QLocalSocket* socket, const QString& message, bool doWait = false);
|
||||
void broadcastMessage(const QString& verb, const QString &path, const QString &status = QString::null, bool doWait = false);
|
||||
|
||||
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, SocketType* socket);
|
||||
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString& argument, SocketType* socket);
|
||||
Q_INVOKABLE void command_SHARE(const QString& localFile, SocketType* socket);
|
||||
Q_INVOKABLE void command_RETRIEVE_FOLDER_STATUS(const QString& argument, QLocalSocket* socket);
|
||||
Q_INVOKABLE void command_RETRIEVE_FILE_STATUS(const QString& argument, QLocalSocket* socket);
|
||||
Q_INVOKABLE void command_SHARE(const QString& localFile, QLocalSocket* socket);
|
||||
|
||||
Q_INVOKABLE void command_VERSION(const QString& argument, SocketType* socket);
|
||||
Q_INVOKABLE void command_VERSION(const QString& argument, QLocalSocket* socket);
|
||||
|
||||
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString& argument, SocketType* socket);
|
||||
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString& argument, QLocalSocket* socket);
|
||||
QString buildRegisterPathMessage(const QString& path);
|
||||
|
||||
#ifdef SOCKETAPI_TCP
|
||||
QTcpServer _localServer;
|
||||
#else
|
||||
QList<QLocalSocket*> _listeners;
|
||||
QLocalServer _localServer;
|
||||
#endif
|
||||
QList<SocketType*> _listeners;
|
||||
c_strlist_t *_excludes;
|
||||
QHash<Folder*, SqlQuery*> _dbQueries;
|
||||
QHash<Folder*, SqlDatabase*> _openDbs;
|
||||
|
||||
@@ -31,6 +31,10 @@ SslButton::SslButton(QWidget *parent) :
|
||||
{
|
||||
setPopupMode(QToolButton::InstantPopup);
|
||||
setAutoRaise(true);
|
||||
|
||||
setMenu(new QMenu(this));
|
||||
QObject::connect(menu(), SIGNAL(aboutToShow()),
|
||||
this, SLOT(slotUpdateMenu()));
|
||||
}
|
||||
|
||||
QString SslButton::protoToString(QSsl::SslProtocol proto)
|
||||
@@ -178,19 +182,39 @@ void SslButton::updateAccountState(AccountState *accountState)
|
||||
} else {
|
||||
setVisible(true);
|
||||
}
|
||||
AccountPtr account = accountState->account();
|
||||
if(QMenu *oldMenu = menu()) {
|
||||
oldMenu->hide(); // Need to be hidden because the QToolButton would be left in invalid state if the menu is deleted while it is visible
|
||||
setMenu(0);
|
||||
oldMenu->deleteLater(); // setMenu do not delete the previous menu.
|
||||
}
|
||||
_accountState = accountState;
|
||||
|
||||
AccountPtr account = _accountState->account();
|
||||
if (account->url().scheme() == QLatin1String("https")) {
|
||||
setIcon(QIcon(QPixmap(Theme::hidpiFileName(":/client/resources/lock-https.png"))));
|
||||
QPixmap pm(Theme::hidpiFileName(":/client/resources/lock-https.png"));
|
||||
setIcon(QIcon(pm));
|
||||
QSslCipher cipher = account->sslConfiguration().sessionCipher();
|
||||
setToolTip(tr("This connection is encrypted using %1 bit %2.\n").arg(cipher.usedBits()).arg(cipher.name()));
|
||||
QMenu *menu = new QMenu(this);
|
||||
} else {
|
||||
setIcon(QIcon(QPixmap(Theme::hidpiFileName(":/client/resources/lock-http.png"))));
|
||||
setToolTip(tr("This connection is NOT secure as it is not encrypted.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void SslButton::slotUpdateMenu() {
|
||||
menu()->clear();
|
||||
|
||||
if (!_accountState) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccountPtr account = _accountState->account();
|
||||
|
||||
if (account->url().scheme() == QLatin1String("https")) {
|
||||
|
||||
QList<QSslCertificate> chain = account->sslConfiguration().peerCertificateChain();
|
||||
menu->addAction(tr("Certificate information:"))->setEnabled(false);
|
||||
|
||||
if (chain.isEmpty()) {
|
||||
qWarning() << "empty certificate chain";
|
||||
return;
|
||||
}
|
||||
|
||||
menu()->addAction(tr("Certificate information:"))->setEnabled(false);
|
||||
|
||||
QList<QSslCertificate> tmpChain;
|
||||
foreach(QSslCertificate cert, chain) {
|
||||
@@ -213,13 +237,9 @@ void SslButton::updateAccountState(AccountState *accountState)
|
||||
it.toBack();
|
||||
int i = 0;
|
||||
while (it.hasPrevious()) {
|
||||
menu->addMenu(buildCertMenu(menu, it.previous(), account->approvedCerts(), i));
|
||||
menu()->addMenu(buildCertMenu(menu(), it.previous(), account->approvedCerts(), i));
|
||||
i++;
|
||||
}
|
||||
setMenu(menu);
|
||||
} else {
|
||||
setIcon(QIcon(QPixmap(Theme::hidpiFileName(":/client/resources/lock-http.png"))));
|
||||
setToolTip(tr("This connection is NOT secure as it is not encrypted.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ class QSslConfiguration;
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class Account;
|
||||
class AccountState;
|
||||
|
||||
class SslButton : public QToolButton
|
||||
@@ -35,9 +34,13 @@ public:
|
||||
QString protoToString(QSsl::SslProtocol proto);
|
||||
void updateAccountState(AccountState *accountState);
|
||||
|
||||
public slots:
|
||||
void slotUpdateMenu();
|
||||
|
||||
private:
|
||||
QMenu* buildCertMenu(QMenu *parent, const QSslCertificate& cert,
|
||||
const QList<QSslCertificate>& userApproved, int pos);
|
||||
QPointer<AccountState> _accountState;
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -34,8 +34,9 @@ namespace Utility {
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SslDialogErrorHandler::handleErrors(QList<QSslError> errors, QList<QSslCertificate> *certs, AccountPtr account)
|
||||
bool SslDialogErrorHandler::handleErrors(QList<QSslError> errors, const QSslConfiguration &conf, QList<QSslCertificate> *certs, AccountPtr account)
|
||||
{
|
||||
(void) conf;
|
||||
if (!certs) {
|
||||
qDebug() << "Certs parameter required but is NULL!";
|
||||
return false;
|
||||
|
||||
@@ -33,7 +33,7 @@ class SslErrorDialog;
|
||||
|
||||
class SslDialogErrorHandler : public AbstractSslErrorHandler {
|
||||
public:
|
||||
bool handleErrors(QList<QSslError> errors, QList<QSslCertificate> *certs, AccountPtr) Q_DECL_OVERRIDE;
|
||||
bool handleErrors(QList<QSslError> errors, const QSslConfiguration &conf, QList<QSslCertificate> *certs, AccountPtr) Q_DECL_OVERRIDE;
|
||||
};
|
||||
|
||||
class SslErrorDialog : public QDialog
|
||||
|
||||
@@ -52,9 +52,10 @@ bool OCUpdater::performUpdate()
|
||||
QSettings settings(cfg.configFile(), QSettings::IniFormat);
|
||||
QString updateFile = settings.value(updateAvailableC).toString();
|
||||
if (!updateFile.isEmpty() && QFile(updateFile).exists()) {
|
||||
if (QMessageBox::information(0, tr("New Update Ready"),
|
||||
tr("A new update is about to be installed. The updater may ask\n"
|
||||
"for additional privileges during the process."), QMessageBox::Ok)) {
|
||||
const QString name = Theme::instance()->appNameGUI();
|
||||
if (QMessageBox::information(0, tr("New %1 Update Ready").arg(name),
|
||||
tr("A new update for %1 is about to be installed. The updater may ask\n"
|
||||
"for additional privileges during the process.").arg(name), QMessageBox::Ok)) {
|
||||
slotStartInstaller();
|
||||
return true;
|
||||
}
|
||||
|
||||
25
src/gui/version.rc.in
Normal file
25
src/gui/version.rc.in
Normal file
@@ -0,0 +1,25 @@
|
||||
#include "winresrc.h"
|
||||
|
||||
#define VER_FILEVERSION @MIRALL_VERSION_MAJOR@,@MIRALL_VERSION_MINOR@,@MIRALL_VERSION_PATCH@,@MIRALL_VERSION_BUILD@
|
||||
#define VER_FILEVERSION_STR "@MIRALL_VERSION_MAJOR@.@MIRALL_VERSION_MINOR@.@MIRALL_VERSION_PATCH@.@MIRALL_VERSION_BUILD@\0"
|
||||
|
||||
#define VER_PRODUCTVERSION @MIRALL_VERSION_MAJOR@,@MIRALL_VERSION_MINOR@,@MIRALL_VERSION_PATCH@,@MIRALL_VERSION_BUILD@
|
||||
#define VER_PRODUCTVERSION_STR "@MIRALL_VERSION_MAJOR@.@MIRALL_VERSION_MINOR@.@MIRALL_VERSION_PATCH@.@MIRALL_VERSION_BUILD@\0"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VER_FILEVERSION
|
||||
PRODUCTVERSION VER_PRODUCTVERSION
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "080904b0"
|
||||
BEGIN
|
||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x809, 1200
|
||||
END
|
||||
END
|
||||
@@ -64,7 +64,6 @@ set(libsync_SRCS
|
||||
creds/dummycredentials.cpp
|
||||
creds/abstractcredentials.cpp
|
||||
creds/credentialsfactory.cpp
|
||||
creds/http/httpconfigfile.cpp
|
||||
creds/credentialscommon.cpp
|
||||
../3rdparty/qjson/json.cpp
|
||||
../3rdparty/certificates/p12topem.cpp
|
||||
|
||||
@@ -63,7 +63,9 @@ void AccountManager::setAccount(AccountPtr account)
|
||||
emit accountRemoved(_account);
|
||||
}
|
||||
_account = account;
|
||||
emit accountAdded(account);
|
||||
if (account) {
|
||||
emit accountAdded(account);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +131,15 @@ void Account::save()
|
||||
if (!certs.isEmpty()) {
|
||||
settings->setValue( QLatin1String(caCertsKeyC), certs );
|
||||
}
|
||||
|
||||
// Save cookies.
|
||||
if (_am) {
|
||||
CookieJar* jar = qobject_cast<CookieJar*>(_am->cookieJar());
|
||||
if (jar) {
|
||||
qDebug() << "Saving cookies.";
|
||||
jar->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccountPtr Account::restore()
|
||||
@@ -271,6 +282,21 @@ void Account::clearCookieJar()
|
||||
_am->setCookieJar(new CookieJar);
|
||||
}
|
||||
|
||||
void Account::resetNetworkAccessManager()
|
||||
{
|
||||
if (!_credentials || !_am) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Resetting QNAM";
|
||||
QNetworkCookieJar* jar = _am->cookieJar();
|
||||
_am->deleteLater();
|
||||
_am = _credentials->getQNAM();
|
||||
_am->setCookieJar(jar); // takes ownership of the old cookie jar
|
||||
connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
|
||||
SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
|
||||
}
|
||||
|
||||
QNetworkAccessManager *Account::networkAccessManager()
|
||||
{
|
||||
return _am;
|
||||
@@ -295,7 +321,9 @@ QNetworkReply *Account::getRequest(const QString &relPath)
|
||||
QNetworkReply *Account::getRequest(const QUrl &url)
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
||||
request.setSslConfiguration(this->createSslConfig());
|
||||
#endif
|
||||
return _am->get(request);
|
||||
}
|
||||
|
||||
@@ -307,7 +335,9 @@ QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPat
|
||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
req.setUrl(url);
|
||||
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
||||
req.setSslConfiguration(this->createSslConfig());
|
||||
#endif
|
||||
return _am->sendCustomRequest(req, verb, data);
|
||||
}
|
||||
|
||||
@@ -460,7 +490,7 @@ void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_sslErrorHandler->handleErrors(errors, &approvedCerts, sharedFromThis())) {
|
||||
if (_sslErrorHandler->handleErrors(errors, reply->sslConfiguration(), &approvedCerts, sharedFromThis())) {
|
||||
QSslSocket::addDefaultCaCertificates(approvedCerts);
|
||||
addApprovedCerts(approvedCerts);
|
||||
// all ssl certs are known and accepted. We can ignore the problems right away.
|
||||
|
||||
@@ -62,7 +62,7 @@ private:
|
||||
class AbstractSslErrorHandler {
|
||||
public:
|
||||
virtual ~AbstractSslErrorHandler() {}
|
||||
virtual bool handleErrors(QList<QSslError>, QList<QSslCertificate>*, AccountPtr) = 0;
|
||||
virtual bool handleErrors(QList<QSslError>, const QSslConfiguration &conf, QList<QSslCertificate>*, AccountPtr) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -156,6 +156,7 @@ public:
|
||||
|
||||
void clearCookieJar();
|
||||
|
||||
void resetNetworkAccessManager();
|
||||
QNetworkAccessManager* networkAccessManager();
|
||||
|
||||
/// Called by network jobs on credential errors.
|
||||
@@ -185,7 +186,6 @@ private:
|
||||
QNetworkAccessManager *_am;
|
||||
AbstractCredentials* _credentials;
|
||||
bool _treatSslErrorsAsFailure;
|
||||
int _state;
|
||||
static QString _configFileName;
|
||||
QByteArray _pemCertificate;
|
||||
QString _pemPrivateKey;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include "configfile.h"
|
||||
#include <QUrl>
|
||||
#include <QThreadPool>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -23,7 +24,7 @@ ClientProxy::ClientProxy(QObject *parent) :
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkProxy ClientProxy::proxyFromConfig(const ConfigFile& cfg)
|
||||
static QNetworkProxy proxyFromConfig(const ConfigFile& cfg)
|
||||
{
|
||||
QNetworkProxy proxy;
|
||||
|
||||
@@ -39,6 +40,22 @@ QNetworkProxy ClientProxy::proxyFromConfig(const ConfigFile& cfg)
|
||||
return proxy;
|
||||
}
|
||||
|
||||
bool ClientProxy::isUsingSystemDefault() {
|
||||
OCC::ConfigFile cfg;
|
||||
|
||||
// if there is no config file, default to system proxy.
|
||||
if( cfg.exists() ) {
|
||||
return cfg.proxyType() == QNetworkProxy::DefaultProxy;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString printQNetworkProxy(const QNetworkProxy &proxy)
|
||||
{
|
||||
return QString("%1://%2:%3").arg(proxy.type()).arg(proxy.hostName()).arg(proxy.port());
|
||||
}
|
||||
|
||||
void ClientProxy::setupQtProxyFromConfig()
|
||||
{
|
||||
OCC::ConfigFile cfg;
|
||||
@@ -53,19 +70,23 @@ void ClientProxy::setupQtProxyFromConfig()
|
||||
|
||||
switch(proxyType) {
|
||||
case QNetworkProxy::NoProxy:
|
||||
qDebug() << "Set proxy configuration to use NO proxy";
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
|
||||
break;
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
qDebug() << "Set proxy configuration to use system configuration";
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(true);
|
||||
break;
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
qDebug() << "Set proxy configuration to SOCKS5" << printQNetworkProxy(proxy);
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
break;
|
||||
case QNetworkProxy::HttpProxy:
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
qDebug() << "Set proxy configuration to HTTP" << printQNetworkProxy(proxy);
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
break;
|
||||
@@ -96,6 +117,7 @@ const char* ClientProxy::proxyTypeToCStr(QNetworkProxy::ProxyType type)
|
||||
|
||||
void ClientProxy::setCSyncProxy( const QUrl& url, CSYNC *csync_ctx )
|
||||
{
|
||||
#ifdef USE_NEON
|
||||
/* Store proxy */
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(QNetworkProxyQuery(url));
|
||||
// We set at least one in Application
|
||||
@@ -118,7 +140,39 @@ void ClientProxy::setCSyncProxy( const QUrl& url, CSYNC *csync_ctx )
|
||||
csync_set_module_property( csync_ctx, "proxy_port", &proxy_port );
|
||||
csync_set_module_property( csync_ctx, "proxy_user", proxy.user().toUtf8().data());
|
||||
csync_set_module_property( csync_ctx, "proxy_pwd", proxy.password().toUtf8().data());
|
||||
#else
|
||||
Q_UNUSED(url);
|
||||
Q_UNUSED(csync_ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ClientProxy::lookupSystemProxyAsync(const QUrl &url, QObject *dst, const char *slot)
|
||||
{
|
||||
SystemProxyRunnable *runnable = new SystemProxyRunnable(url);
|
||||
QObject::connect(runnable, SIGNAL(systemProxyLookedUp(QNetworkProxy)), dst, slot);
|
||||
QThreadPool::globalInstance()->start(runnable); // takes ownership and deletes
|
||||
}
|
||||
|
||||
SystemProxyRunnable::SystemProxyRunnable(const QUrl &url) : QObject(), QRunnable(), _url(url)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SystemProxyRunnable::run()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "Starting system proxy lookup";
|
||||
qRegisterMetaType<QNetworkProxy>("QNetworkProxy");
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(_url));
|
||||
|
||||
if (proxies.isEmpty()) {
|
||||
emit systemProxyLookedUp(QNetworkProxy(QNetworkProxy::NoProxy));
|
||||
} else {
|
||||
emit systemProxyLookedUp(proxies.first());
|
||||
// FIXME Would we really ever return more?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkProxy>
|
||||
#include <QRunnable>
|
||||
#include <QUrl>
|
||||
|
||||
#include <csync.h>
|
||||
#include "utility.h"
|
||||
@@ -30,17 +32,31 @@ class OWNCLOUDSYNC_EXPORT ClientProxy : public QObject
|
||||
public:
|
||||
explicit ClientProxy(QObject *parent = 0);
|
||||
|
||||
signals:
|
||||
static bool isUsingSystemDefault();
|
||||
static void lookupSystemProxyAsync(const QUrl &url, QObject *dst, const char *slot);
|
||||
|
||||
public slots:
|
||||
void setCSyncProxy( const QUrl& url, CSYNC *csync_ctx );
|
||||
void setupQtProxyFromConfig();
|
||||
|
||||
private:
|
||||
QNetworkProxy proxyFromConfig(const ConfigFile& cfg);
|
||||
const char* proxyTypeToCStr(QNetworkProxy::ProxyType type);
|
||||
};
|
||||
|
||||
class SystemProxyRunnable : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
SystemProxyRunnable(const QUrl &url);
|
||||
void run();
|
||||
signals:
|
||||
void systemProxyLookedUp(const QNetworkProxy &url);
|
||||
private:
|
||||
QUrl _url;
|
||||
};
|
||||
|
||||
QString printQNetworkProxy(const QNetworkProxy &proxy);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // CLIENTPROXY_H
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
static const char caCertsKeyC[] = "CaCertificates";
|
||||
//static const char caCertsKeyC[] = "CaCertificates"; only used from account.cpp
|
||||
static const char remotePollIntervalC[] = "remotePollInterval";
|
||||
static const char forceSyncIntervalC[] = "forceSyncInterval";
|
||||
static const char monoIconsC[] = "monoIcons";
|
||||
@@ -85,10 +85,10 @@ ConfigFile::ConfigFile()
|
||||
// qDebug() << Q_FUNC_INFO << "Loading config: " << config << " (URL is " << settings.value("url").toString() << ")";
|
||||
}
|
||||
|
||||
void ConfigFile::setConfDir(const QString &value)
|
||||
bool ConfigFile::setConfDir(const QString &value)
|
||||
{
|
||||
QString dirPath = value;
|
||||
if( dirPath.isEmpty() ) return;
|
||||
if( dirPath.isEmpty() ) return false;
|
||||
|
||||
QFileInfo fi(dirPath);
|
||||
if ( !fi.exists() && !fi.isAbsolute() ) {
|
||||
@@ -101,7 +101,9 @@ void ConfigFile::setConfDir(const QString &value)
|
||||
dirPath = fi.absoluteFilePath();
|
||||
qDebug() << "** Using custom config dir " << dirPath;
|
||||
_confDir=dirPath;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigFile::optionalDesktopNotifications() const
|
||||
@@ -145,7 +147,7 @@ void ConfigFile::saveGeometryHeader(QHeaderView *header)
|
||||
{
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
if(!header) return;
|
||||
Q_ASSERT(!header->objectName().isNull());
|
||||
Q_ASSERT(!header->objectName().isEmpty());
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.beginGroup(header->objectName());
|
||||
@@ -162,7 +164,7 @@ void ConfigFile::restoreGeometryHeader(QHeaderView *header)
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.beginGroup(header->objectName());
|
||||
header->restoreState(getValue(geometryC, header->objectName()).toByteArray());
|
||||
header->restoreState(settings.value(geometryC).toByteArray());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -315,20 +317,6 @@ bool ConfigFile::dataExists(const QString& group, const QString& key) const
|
||||
return settings.contains(key);
|
||||
}
|
||||
|
||||
QByteArray ConfigFile::caCerts( )
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
return settings.value( QLatin1String(caCertsKeyC) ).toByteArray();
|
||||
}
|
||||
|
||||
void ConfigFile::setCaCerts( const QByteArray & certs )
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
||||
settings.setValue( QLatin1String(caCertsKeyC), certs );
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
int ConfigFile::remotePollInterval( const QString& connection ) const
|
||||
{
|
||||
QString con( connection );
|
||||
|
||||
@@ -96,7 +96,7 @@ public:
|
||||
void setUploadLimit(int kbytes);
|
||||
void setDownloadLimit(int kbytes);
|
||||
|
||||
static void setConfDir(const QString &value);
|
||||
static bool setConfDir(const QString &value);
|
||||
|
||||
bool optionalDesktopNotifications() const;
|
||||
void setOptionalDesktopNotifications(bool show);
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
|
||||
#include <QtCore>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkProxyFactory>
|
||||
|
||||
#include "connectionvalidator.h"
|
||||
#include "theme.h"
|
||||
#include "account.h"
|
||||
#include "networkjobs.h"
|
||||
#include "clientproxy.h"
|
||||
#include <creds/abstractcredentials.h>
|
||||
|
||||
namespace OCC {
|
||||
@@ -61,8 +63,38 @@ void ConnectionValidator::checkServerAndAuth()
|
||||
reportResult( NotConfigured );
|
||||
return;
|
||||
}
|
||||
qDebug() << "Checking server and authentication";
|
||||
|
||||
_isCheckingServerAndAuth = true;
|
||||
|
||||
// Lookup system proxy in a thread https://github.com/owncloud/client/issues/2993
|
||||
if (ClientProxy::isUsingSystemDefault()) {
|
||||
qDebug() << "Trying to look up system proxy";
|
||||
ClientProxy::lookupSystemProxyAsync(_account->url(),
|
||||
this, SLOT(systemProxyLookupDone(QNetworkProxy)));
|
||||
} else {
|
||||
// We want to reset the QNAM proxy so that the global proxy settings are used (via ClientProxy settings)
|
||||
_account->networkAccessManager()->setProxy(QNetworkProxy(QNetworkProxy::DefaultProxy));
|
||||
// use a queued invocation so we're as asynchronous as with the other code path
|
||||
QMetaObject::invokeMethod(this, "slotCheckServerAndAuth", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::systemProxyLookupDone(const QNetworkProxy &proxy) {
|
||||
if (!_account) {
|
||||
qDebug() << "Bailing out, Account had been deleted";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "Setting QNAM proxy to be system proxy" << printQNetworkProxy(proxy);
|
||||
_account->networkAccessManager()->setProxy(proxy);
|
||||
|
||||
slotCheckServerAndAuth();
|
||||
}
|
||||
|
||||
// The actual check
|
||||
void ConnectionValidator::slotCheckServerAndAuth()
|
||||
{
|
||||
CheckServerJob *checkJob = new CheckServerJob(_account, this);
|
||||
checkJob->setIgnoreCredentialFailure(true);
|
||||
connect(checkJob, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotStatusFound(QUrl,QVariantMap)));
|
||||
@@ -98,12 +130,16 @@ void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &inf
|
||||
// Fetch them now! Once fetched, a new connectivity check will be
|
||||
// initiated anyway.
|
||||
creds->fetch();
|
||||
|
||||
// no result is reported
|
||||
deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
// status.php could not be loaded (network or server issue!).
|
||||
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << reply->error() << reply->errorString();
|
||||
if( reply && ! _account->credentials()->stillValid(reply)) {
|
||||
_errors.append(tr("Authentication error: Either username or password are wrong."));
|
||||
} else {
|
||||
|
||||
@@ -36,6 +36,8 @@ namespace OCC {
|
||||
|
||||
|
||||
*---> checkServerAndAuth (check status.php)
|
||||
Will asynchronously check for system proxy (if using system proxy)
|
||||
And then invoke slotCheckServerAndAuth
|
||||
CheckServerJob
|
||||
|
|
||||
+-> slotNoStatusFound --> X
|
||||
@@ -85,6 +87,7 @@ public:
|
||||
public slots:
|
||||
/// Checks the server and the authentication.
|
||||
void checkServerAndAuth();
|
||||
void systemProxyLookupDone(const QNetworkProxy &proxy);
|
||||
|
||||
/// Checks authentication only.
|
||||
void checkAuthentication();
|
||||
@@ -93,6 +96,8 @@ signals:
|
||||
void connectionResult( ConnectionValidator::Status status, QStringList errors );
|
||||
|
||||
protected slots:
|
||||
void slotCheckServerAndAuth();
|
||||
|
||||
void slotStatusFound(const QUrl&url, const QVariantMap &info);
|
||||
void slotNoStatusFound(QNetworkReply *reply);
|
||||
void slotJobTimeout(const QUrl& url);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <QFile>
|
||||
#include <QDateTime>
|
||||
#include <QNetworkCookie>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -71,7 +72,6 @@ CookieJar::CookieJar(QObject *parent) :
|
||||
|
||||
CookieJar::~CookieJar()
|
||||
{
|
||||
save();
|
||||
}
|
||||
|
||||
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url)
|
||||
@@ -103,7 +103,7 @@ void CookieJar::save()
|
||||
qDebug() << storagePath();
|
||||
file.open(QIODevice::WriteOnly);
|
||||
QDataStream stream(&file);
|
||||
stream << allCookies();
|
||||
stream << removeExpired(allCookies());
|
||||
file.close();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,10 +34,11 @@ public:
|
||||
using QNetworkCookieJar::setAllCookies;
|
||||
using QNetworkCookieJar::allCookies;
|
||||
|
||||
void save();
|
||||
|
||||
signals:
|
||||
void newCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
private:
|
||||
void save();
|
||||
void restore();
|
||||
QList<QNetworkCookie> removeExpired(const QList<QNetworkCookie> &cookies);
|
||||
QString storagePath() const;
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "creds/http/httpconfigfile.h"
|
||||
|
||||
namespace OCC
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const char userC[] = "user";
|
||||
const char passwdC[] = "passwd";
|
||||
const char oldPasswdC[] = "password";
|
||||
|
||||
} // ns
|
||||
|
||||
QString HttpConfigFile::user() const
|
||||
{
|
||||
return retrieveData(QString(), QLatin1String(userC)).toString();
|
||||
}
|
||||
|
||||
void HttpConfigFile::setUser(const QString& user)
|
||||
{
|
||||
storeData(QString(), QLatin1String(userC), QVariant(user));
|
||||
}
|
||||
|
||||
QString HttpConfigFile::password() const
|
||||
{
|
||||
const QVariant passwd(retrieveData(QString(), QLatin1String(passwdC)));
|
||||
|
||||
if (passwd.isValid()) {
|
||||
return QString::fromUtf8(QByteArray::fromBase64(passwd.toByteArray()));
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void HttpConfigFile::setPassword(const QString& password)
|
||||
{
|
||||
QByteArray pwdba = password.toUtf8();
|
||||
storeData( QString(), QLatin1String(passwdC), QVariant(pwdba.toBase64()) );
|
||||
removeOldPassword();
|
||||
}
|
||||
|
||||
bool HttpConfigFile::passwordExists() const
|
||||
{
|
||||
return dataExists(QString(), QLatin1String(passwdC));
|
||||
}
|
||||
|
||||
void HttpConfigFile::removePassword()
|
||||
{
|
||||
removeOldPassword();
|
||||
removeData(QString(), QLatin1String(passwdC));
|
||||
}
|
||||
|
||||
void HttpConfigFile::fixupOldPassword()
|
||||
{
|
||||
const QString old(QString::fromLatin1(oldPasswdC));
|
||||
|
||||
if (dataExists(QString(), old)) {
|
||||
setPassword(retrieveData(QString(), old).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void HttpConfigFile::removeOldPassword()
|
||||
{
|
||||
removeData(QString(), QLatin1String(oldPasswdC));
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_HTTP_CONFIG_FILE_H
|
||||
#define MIRALL_CREDS_HTTP_CONFIG_FILE_H
|
||||
|
||||
#include "configfile.h"
|
||||
|
||||
namespace OCC
|
||||
{
|
||||
|
||||
class HttpConfigFile : public ConfigFile
|
||||
{
|
||||
public:
|
||||
QString user() const;
|
||||
void setUser(const QString& user);
|
||||
|
||||
QString password() const;
|
||||
void setPassword(const QString& password);
|
||||
bool passwordExists() const;
|
||||
void removePassword();
|
||||
void fixupOldPassword();
|
||||
QString certificatePath() const;
|
||||
void setCertificatePath(const QString& cPath);
|
||||
QString certificatePasswd() const;
|
||||
void setCertificatePasswd(const QString& cPasswd);
|
||||
|
||||
private:
|
||||
void removeOldPassword();
|
||||
};
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
#endif
|
||||
@@ -313,6 +313,9 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||
|
||||
void HttpCredentials::invalidateToken()
|
||||
{
|
||||
if (! _password.isEmpty()) {
|
||||
_previousPassword = _password;
|
||||
}
|
||||
_password = QString();
|
||||
_ready = false;
|
||||
|
||||
@@ -333,6 +336,14 @@ void HttpCredentials::invalidateToken()
|
||||
job->setKey(kck);
|
||||
job->start();
|
||||
|
||||
// Also ensure the password is deleted from the deprecated place
|
||||
// otherwise we'd possibly read and use it again and again.
|
||||
DeletePasswordJob *job2 = new DeletePasswordJob(Theme::instance()->appName());
|
||||
// no job2->setSettings() call here, to make it use the deprecated location.
|
||||
job2->setInsecureFallback(true);
|
||||
job2->setKey(kck);
|
||||
job2->start();
|
||||
|
||||
_account->clearCookieJar();
|
||||
}
|
||||
|
||||
@@ -387,7 +398,7 @@ QString HttpCredentialsGui::queryPassword(bool *ok)
|
||||
QString str = QInputDialog::getText(0, tr("Enter Password"),
|
||||
tr("Please enter %1 password for user '%2':")
|
||||
.arg(Theme::instance()->appNameGUI(), _user),
|
||||
QLineEdit::Password, QString(), ok);
|
||||
QLineEdit::Password, _previousPassword, ok);
|
||||
return str;
|
||||
} else {
|
||||
return QString();
|
||||
|
||||
@@ -64,6 +64,7 @@ private Q_SLOTS:
|
||||
protected:
|
||||
QString _user;
|
||||
QString _password;
|
||||
QString _previousPassword;
|
||||
|
||||
private:
|
||||
QString _certificatePath;
|
||||
@@ -74,6 +75,7 @@ private:
|
||||
};
|
||||
|
||||
class OWNCLOUDSYNC_EXPORT HttpCredentialsGui : public HttpCredentials {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit HttpCredentialsGui() : HttpCredentials() {}
|
||||
HttpCredentialsGui(const QString& user, const QString& password, const QString& certificatePath, const QString& certificatePasswd) : HttpCredentials(user, password, certificatePath, certificatePasswd) {}
|
||||
|
||||
@@ -83,21 +83,11 @@ void DiscoveryJob::update_job_update_callback (bool local,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Only use for error cases! It will always set an error errno
|
||||
int get_errno_from_http_errcode( int err, const QString & reason ) {
|
||||
int new_errno = 0;
|
||||
int new_errno = EIO;
|
||||
|
||||
switch(err) {
|
||||
case 200: /* OK */
|
||||
case 201: /* Created */
|
||||
case 202: /* Accepted */
|
||||
case 203: /* Non-Authoritative Information */
|
||||
case 204: /* No Content */
|
||||
case 205: /* Reset Content */
|
||||
case 207: /* Multi-Status */
|
||||
case 304: /* Not Modified */
|
||||
new_errno = 0;
|
||||
break;
|
||||
case 401: /* Unauthorized */
|
||||
case 402: /* Payment Required */
|
||||
case 407: /* Proxy Authentication Required */
|
||||
@@ -268,8 +258,11 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file,QMap
|
||||
}
|
||||
|
||||
|
||||
csync_vio_file_stat_t *file_stat = propertyMapToFileStat(map);
|
||||
FileStatPointer file_stat(propertyMapToFileStat(map));
|
||||
file_stat->name = strdup(file.toUtf8());
|
||||
if (!file_stat->etag || strlen(file_stat->etag) == 0) {
|
||||
qDebug() << "WARNING: etag of" << file_stat->name << "is" << file_stat->etag << " This must not happen.";
|
||||
}
|
||||
//qDebug() << "!!!!" << file_stat << file_stat->name << file_stat->file_id << map.count();
|
||||
_results.append(file_stat);
|
||||
}
|
||||
@@ -282,6 +275,13 @@ void DiscoverySingleDirectoryJob::directoryListingIteratedSlot(QString file,QMap
|
||||
|
||||
void DiscoverySingleDirectoryJob::lsJobFinishedWithoutErrorSlot()
|
||||
{
|
||||
if (!_ignoredFirst) {
|
||||
// This is a sanity check, if we haven't _ignoredFirst then it means we never received any directoryListingIteratedSlot
|
||||
// which means somehow the server XML was bogus
|
||||
emit finishedWithError(ERRNO_WRONG_CONTENT, QLatin1String("Server error: PROPFIND reply is not XML formatted!"));
|
||||
deleteLater();
|
||||
return;
|
||||
}
|
||||
emit etagConcatenation(_etagConcatenation);
|
||||
emit finishedWithResult(_results);
|
||||
deleteLater();
|
||||
@@ -293,7 +293,7 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r)
|
||||
int httpCode = r->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
QString httpReason = r->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
QString msg = r->errorString();
|
||||
int errnoCode = 0;
|
||||
int errnoCode = EIO; // Something went wrong
|
||||
qDebug() << Q_FUNC_INFO << r->errorString() << httpCode << r->error();
|
||||
if (httpCode != 0 && httpCode != 207) {
|
||||
errnoCode = get_errno_from_http_errcode(httpCode, httpReason);
|
||||
@@ -302,9 +302,11 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r)
|
||||
} else if (!contentType.contains("application/xml; charset=utf-8")) {
|
||||
msg = QLatin1String("Server error: PROPFIND reply is not XML formatted!");
|
||||
errnoCode = ERRNO_WRONG_CONTENT;
|
||||
} else {
|
||||
// Default keep at EIO, see above
|
||||
}
|
||||
|
||||
emit finishedWithError(errnoCode, msg);
|
||||
emit finishedWithError(errnoCode == 0 ? EIO : errnoCode, msg);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
@@ -316,15 +318,6 @@ void DiscoveryMainThread::setupHooks(DiscoveryJob *discoveryJob, const QString &
|
||||
connect(discoveryJob, SIGNAL(doOpendirSignal(QString,DiscoveryDirectoryResult*)),
|
||||
this, SLOT(doOpendirSlot(QString,DiscoveryDirectoryResult*)),
|
||||
Qt::QueuedConnection);
|
||||
connect(discoveryJob, SIGNAL(doClosedirSignal(QString)),
|
||||
this, SLOT(doClosedirSlot(QString)),
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void DiscoveryMainThread::doClosedirSlot(QString path)
|
||||
{
|
||||
//qDebug() << Q_FUNC_INFO << "Invalidating" << path;
|
||||
deleteCacheEntry(path);
|
||||
}
|
||||
|
||||
// Coming from owncloud_opendir -> DiscoveryJob::vio_opendir_hook -> doOpendirSignal
|
||||
@@ -349,8 +342,8 @@ void DiscoveryMainThread::doOpendirSlot(QString subPath, DiscoveryDirectoryResul
|
||||
|
||||
// Schedule the DiscoverySingleDirectoryJob
|
||||
_singleDirJob = new DiscoverySingleDirectoryJob(_account, fullPath, this);
|
||||
QObject::connect(_singleDirJob, SIGNAL(finishedWithResult(QLinkedList<csync_vio_file_stat_t *>)),
|
||||
this, SLOT(singleDirectoryJobResultSlot(QLinkedList<csync_vio_file_stat_t*>)));
|
||||
QObject::connect(_singleDirJob, SIGNAL(finishedWithResult(const QList<FileStatPointer> &)),
|
||||
this, SLOT(singleDirectoryJobResultSlot(const QList<FileStatPointer> &)));
|
||||
QObject::connect(_singleDirJob, SIGNAL(finishedWithError(int,QString)),
|
||||
this, SLOT(singleDirectoryJobFinishedWithErrorSlot(int,QString)));
|
||||
QObject::connect(_singleDirJob, SIGNAL(firstDirectoryPermissions(QString)),
|
||||
@@ -361,7 +354,7 @@ void DiscoveryMainThread::doOpendirSlot(QString subPath, DiscoveryDirectoryResul
|
||||
}
|
||||
|
||||
|
||||
void DiscoveryMainThread::singleDirectoryJobResultSlot(QLinkedList<csync_vio_file_stat_t *> result)
|
||||
void DiscoveryMainThread::singleDirectoryJobResultSlot(const QList<FileStatPointer> & result)
|
||||
{
|
||||
if (!_currentDiscoveryDirectoryResult) {
|
||||
return; // possibly aborted
|
||||
@@ -369,11 +362,9 @@ void DiscoveryMainThread::singleDirectoryJobResultSlot(QLinkedList<csync_vio_fil
|
||||
qDebug() << Q_FUNC_INFO << "Have" << result.count() << "results for " << _currentDiscoveryDirectoryResult->path;
|
||||
|
||||
|
||||
_directoryContents.insert(_currentDiscoveryDirectoryResult->path, result);
|
||||
|
||||
_currentDiscoveryDirectoryResult->list = result;
|
||||
_currentDiscoveryDirectoryResult->code = 0;
|
||||
_currentDiscoveryDirectoryResult->iterator = _currentDiscoveryDirectoryResult->list.begin();
|
||||
_currentDiscoveryDirectoryResult->listIndex = 0;
|
||||
_currentDiscoveryDirectoryResult = 0; // the sync thread owns it now
|
||||
|
||||
_discoveryJob->_vioMutex.lock();
|
||||
@@ -411,7 +402,7 @@ void DiscoveryMainThread::abort() {
|
||||
if (_singleDirJob) {
|
||||
_singleDirJob->disconnect(SIGNAL(finishedWithError(int,QString)), this);
|
||||
_singleDirJob->disconnect(SIGNAL(firstDirectoryPermissions(QString)), this);
|
||||
_singleDirJob->disconnect(SIGNAL(finishedWithResult(QLinkedList<csync_vio_file_stat_t*>)), this);
|
||||
_singleDirJob->disconnect(SIGNAL(finishedWithResult(const QList<FileStatPointer> &)), this);
|
||||
_singleDirJob->abort();
|
||||
}
|
||||
if (_currentDiscoveryDirectoryResult) {
|
||||
@@ -466,9 +457,8 @@ csync_vio_file_stat_t* DiscoveryJob::remote_vio_readdir_hook (csync_vio_handle_t
|
||||
DiscoveryJob *discoveryJob = static_cast<DiscoveryJob*>(userdata);
|
||||
if (discoveryJob) {
|
||||
DiscoveryDirectoryResult *directoryResult = static_cast<DiscoveryDirectoryResult*>(dhandle);
|
||||
if (directoryResult->iterator != directoryResult->list.end()) {
|
||||
csync_vio_file_stat_t *file_stat = *(directoryResult->iterator);
|
||||
directoryResult->iterator++;
|
||||
if (directoryResult->listIndex < directoryResult->list.size()) {
|
||||
csync_vio_file_stat_t *file_stat = directoryResult->list.at(directoryResult->listIndex++).data();
|
||||
// Make a copy, csync_update will delete the copy
|
||||
return csync_vio_file_stat_copy(file_stat);
|
||||
}
|
||||
@@ -484,7 +474,6 @@ void DiscoveryJob::remote_vio_closedir_hook (csync_vio_handle_t *dhandle, void
|
||||
QString path = directoryResult->path;
|
||||
qDebug() << Q_FUNC_INFO << discoveryJob << path;
|
||||
delete directoryResult; // just deletes the struct and the iterator, the data itself is owned by the SyncEngine/DiscoveryMainThread
|
||||
emit discoveryJob->doClosedirSignal(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,12 +34,36 @@ class Account;
|
||||
* if the files are new, or changed.
|
||||
*/
|
||||
|
||||
class FileStatPointer {
|
||||
public:
|
||||
FileStatPointer(csync_vio_file_stat_t *stat)
|
||||
: _stat(stat)
|
||||
{ }
|
||||
FileStatPointer(const FileStatPointer &other)
|
||||
: _stat(csync_vio_file_stat_copy(other._stat))
|
||||
{ }
|
||||
~FileStatPointer() {
|
||||
csync_vio_file_stat_destroy(_stat);
|
||||
}
|
||||
FileStatPointer &operator=(const FileStatPointer &other) {
|
||||
csync_vio_file_stat_destroy(_stat);
|
||||
_stat = csync_vio_file_stat_copy(other._stat);
|
||||
return *this;
|
||||
}
|
||||
inline csync_vio_file_stat_t *data() const { return _stat; }
|
||||
inline csync_vio_file_stat_t *operator->() const { return _stat; }
|
||||
|
||||
private:
|
||||
csync_vio_file_stat_t *_stat;
|
||||
};
|
||||
|
||||
struct DiscoveryDirectoryResult {
|
||||
QString path;
|
||||
QString msg;
|
||||
int code;
|
||||
QLinkedList<csync_vio_file_stat_t*>::iterator iterator;
|
||||
QLinkedList<csync_vio_file_stat_t *> list;
|
||||
QList<FileStatPointer> list;
|
||||
int listIndex;
|
||||
DiscoveryDirectoryResult() : code(EIO), listIndex(0) { }
|
||||
};
|
||||
|
||||
// Run in the main thread, reporting to the DiscoveryJobMainThread object
|
||||
@@ -53,14 +77,14 @@ public:
|
||||
signals:
|
||||
void firstDirectoryPermissions(const QString &);
|
||||
void etagConcatenation(const QString &);
|
||||
void finishedWithResult(QLinkedList<csync_vio_file_stat_t*>);
|
||||
void finishedWithResult(const QList<FileStatPointer> &);
|
||||
void finishedWithError(int csyncErrnoCode, QString msg);
|
||||
private slots:
|
||||
void directoryListingIteratedSlot(QString,QMap<QString,QString>);
|
||||
void lsJobFinishedWithoutErrorSlot();
|
||||
void lsJobFinishedWithErrorSlot(QNetworkReply*);
|
||||
private:
|
||||
QLinkedList<csync_vio_file_stat_t*> _results;
|
||||
QList<FileStatPointer> _results;
|
||||
QString _subPath;
|
||||
QString _etagConcatenation;
|
||||
AccountPtr _account;
|
||||
@@ -73,11 +97,6 @@ class DiscoveryJob;
|
||||
class DiscoveryMainThread : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
// For non-recursive and recursive
|
||||
// If it is not in this map it needs to be requested
|
||||
QMap<QString, QLinkedList<csync_vio_file_stat_t*> > _directoryContents;
|
||||
|
||||
|
||||
QPointer<DiscoveryJob> _discoveryJob;
|
||||
QPointer<DiscoverySingleDirectoryJob> _singleDirJob;
|
||||
QString _pathPrefix;
|
||||
@@ -87,22 +106,6 @@ class DiscoveryMainThread : public QObject {
|
||||
public:
|
||||
DiscoveryMainThread(AccountPtr account) : QObject(), _account(account), _currentDiscoveryDirectoryResult(0) {
|
||||
|
||||
}
|
||||
void deleteCacheEntry(QString path) {
|
||||
//qDebug() << path << _directoryContents.value(path).count();
|
||||
foreach (csync_vio_file_stat_t* stat, _directoryContents.value(path)) {
|
||||
csync_vio_file_stat_destroy(stat);
|
||||
}
|
||||
_directoryContents.remove(path);
|
||||
}
|
||||
|
||||
~DiscoveryMainThread() {
|
||||
// Delete the _contents_ of the list-map explicitly:
|
||||
foreach (const QLinkedList<csync_vio_file_stat_t*> & list, _directoryContents) {
|
||||
foreach (csync_vio_file_stat_t* stat, list) {
|
||||
csync_vio_file_stat_destroy(stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
void abort();
|
||||
|
||||
@@ -110,10 +113,9 @@ public:
|
||||
public slots:
|
||||
// From DiscoveryJob:
|
||||
void doOpendirSlot(QString url, DiscoveryDirectoryResult* );
|
||||
void doClosedirSlot(QString path);
|
||||
|
||||
// From Job:
|
||||
void singleDirectoryJobResultSlot(QLinkedList<csync_vio_file_stat_t*>);
|
||||
void singleDirectoryJobResultSlot(const QList<FileStatPointer> &);
|
||||
void singleDirectoryJobFinishedWithErrorSlot(int csyncErrnoCode, QString msg);
|
||||
void singleDirectoryJobFirstDirectoryPermissionsSlot(QString);
|
||||
signals:
|
||||
@@ -175,8 +177,6 @@ signals:
|
||||
|
||||
// After the discovery job has been woken up again (_vioWaitCondition)
|
||||
void doOpendirSignal(QString url, DiscoveryDirectoryResult*);
|
||||
// to tell the main thread to invalidate its directory data
|
||||
void doClosedirSignal(QString path);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -68,11 +68,12 @@ AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path,
|
||||
|
||||
void AbstractNetworkJob::setReply(QNetworkReply *reply)
|
||||
{
|
||||
if (_reply) {
|
||||
_reply->deleteLater();
|
||||
}
|
||||
reply->setProperty("doNotHandleAuth", true);
|
||||
if (reply)
|
||||
reply->setProperty("doNotHandleAuth", true);
|
||||
|
||||
QNetworkReply *old = _reply;
|
||||
_reply = reply;
|
||||
delete old;
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::setTimeout(qint64 msec)
|
||||
@@ -213,9 +214,7 @@ QByteArray AbstractNetworkJob::responseTimestamp()
|
||||
|
||||
AbstractNetworkJob::~AbstractNetworkJob()
|
||||
{
|
||||
if (_reply) {
|
||||
_reply->deleteLater();
|
||||
}
|
||||
setReply(0);
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::start()
|
||||
@@ -224,7 +223,10 @@ void AbstractNetworkJob::start()
|
||||
_durationTimer.start();
|
||||
_duration = 0;
|
||||
|
||||
qDebug() << "!!!" << metaObject()->className() << "created for" << account()->url() << "querying" << path();
|
||||
const QUrl url = account()->url();
|
||||
const QString displayUrl = QString( "%1://%2%3").arg(url.scheme()).arg(url.host()).arg(url.path());
|
||||
|
||||
qDebug() << "!!!" << metaObject()->className() << "created for" << displayUrl << "+" << path();
|
||||
}
|
||||
|
||||
void AbstractNetworkJob::slotTimeout()
|
||||
@@ -318,6 +320,131 @@ bool MkColJob::finished()
|
||||
return true;
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
// supposed to read <D:collection> when pointing to <D:resourcetype><D:collection></D:resourcetype>..
|
||||
static QString readContentsAsString(QXmlStreamReader &reader) {
|
||||
QString result;
|
||||
int level = 0;
|
||||
do {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement) {
|
||||
level++;
|
||||
result += "<" + reader.name().toString() + ">";
|
||||
} else if (type == QXmlStreamReader::Characters) {
|
||||
result += reader.text();
|
||||
} else if (type == QXmlStreamReader::EndElement) {
|
||||
level--;
|
||||
if (level < 0) {
|
||||
break;
|
||||
}
|
||||
result += "</" + reader.name().toString() + ">";
|
||||
}
|
||||
|
||||
} while (!reader.atEnd());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
LsColXMLParser::LsColXMLParser()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool LsColXMLParser::parse( const QByteArray& xml, QHash<QString, qint64> *sizes)
|
||||
{
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(xml);
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
|
||||
QStringList folders;
|
||||
QString currentHref;
|
||||
QMap<QString, QString> currentTmpProperties;
|
||||
QMap<QString, QString> currentHttp200Properties;
|
||||
bool currentPropsHaveHttp200 = false;
|
||||
bool insidePropstat = false;
|
||||
bool insideProp = false;
|
||||
bool insideMultiStatus = false;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
QString name = reader.name().toString();
|
||||
// Start elements with DAV:
|
||||
if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if (name == QLatin1String("href")) {
|
||||
currentHref = QUrl::fromPercentEncoding(reader.readElementText().toUtf8());
|
||||
} else if (name == QLatin1String("response")) {
|
||||
} else if (name == QLatin1String("propstat")) {
|
||||
insidePropstat = true;
|
||||
} else if (name == QLatin1String("status") && insidePropstat) {
|
||||
QString httpStatus = reader.readElementText();
|
||||
if (httpStatus.startsWith("HTTP/1.1 200")) {
|
||||
currentPropsHaveHttp200 = true;
|
||||
} else {
|
||||
currentPropsHaveHttp200 = false;
|
||||
}
|
||||
} else if (name == QLatin1String("prop")) {
|
||||
insideProp = true;
|
||||
continue;
|
||||
} else if (name == QLatin1String("multistatus")) {
|
||||
insideMultiStatus = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == QXmlStreamReader::StartElement && insidePropstat && insideProp) {
|
||||
// All those elements are properties
|
||||
QString propertyContent = readContentsAsString(reader);
|
||||
if (name == QLatin1String("resourcetype") && propertyContent.contains("collection")) {
|
||||
folders.append(currentHref);
|
||||
} else if (name == QLatin1String("quota-used-bytes")) {
|
||||
bool ok = false;
|
||||
auto s = propertyContent.toLongLong(&ok);
|
||||
if (ok && sizes) {
|
||||
sizes->insert(currentHref, s);
|
||||
}
|
||||
}
|
||||
currentTmpProperties.insert(reader.name().toString(), propertyContent);
|
||||
}
|
||||
|
||||
// End elements with DAV:
|
||||
if (type == QXmlStreamReader::EndElement) {
|
||||
if (reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if (reader.name() == "response") {
|
||||
if (currentHref.endsWith('/')) {
|
||||
currentHref.chop(1);
|
||||
}
|
||||
emit directoryListingIterated(currentHref, currentHttp200Properties);
|
||||
currentHref.clear();
|
||||
currentHttp200Properties.clear();
|
||||
} else if (reader.name() == "propstat") {
|
||||
insidePropstat = false;
|
||||
if (currentPropsHaveHttp200) {
|
||||
currentHttp200Properties = QMap<QString,QString>(currentTmpProperties);
|
||||
}
|
||||
currentTmpProperties.clear();
|
||||
currentPropsHaveHttp200 = false;
|
||||
} else if (reader.name() == "prop") {
|
||||
insideProp = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (reader.hasError()) {
|
||||
// XML Parser error? Whatever had been emitted before will come as directoryListingIterated
|
||||
qDebug() << "ERROR" << reader.errorString() << xml;
|
||||
return false;
|
||||
} else if (!insideMultiStatus) {
|
||||
qDebug() << "ERROR no WebDAV response?" << xml;
|
||||
return false;
|
||||
} else {
|
||||
emit directoryListingSubfolders(folders);
|
||||
emit finishedWithoutError();
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/*********************************************************************************************/
|
||||
|
||||
LsColJob::LsColJob(AccountPtr account, const QString &path, QObject *parent)
|
||||
@@ -375,117 +502,36 @@ void LsColJob::start()
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
// supposed to read <D:collection> when pointing to <D:resourcetype><D:collection></D:resourcetype>..
|
||||
static QString readContentsAsString(QXmlStreamReader &reader) {
|
||||
QString result;
|
||||
int level = 0;
|
||||
do {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
if (type == QXmlStreamReader::StartElement) {
|
||||
level++;
|
||||
result += "<" + reader.name().toString() + ">";
|
||||
} else if (type == QXmlStreamReader::Characters) {
|
||||
result += reader.text();
|
||||
} else if (type == QXmlStreamReader::EndElement) {
|
||||
level--;
|
||||
if (level < 0) {
|
||||
break;
|
||||
}
|
||||
result += "</" + reader.name().toString() + ">";
|
||||
}
|
||||
|
||||
} while (!reader.atEnd());
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Instead of doing all in this slot, we should iteratively parse in readyRead(). This
|
||||
// would allow us to be more asynchronous in processing while data is coming from the network,
|
||||
// not in all in one big blobb at the end.
|
||||
bool LsColJob::finished()
|
||||
{
|
||||
QString contentType = reply()->header(QNetworkRequest::ContentTypeHeader).toString();
|
||||
int httpCode = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (httpCode == 207 && contentType.contains("application/xml; charset=utf-8")) {
|
||||
// Parse DAV response
|
||||
QByteArray xml = reply()->readAll();
|
||||
QXmlStreamReader reader(xml);
|
||||
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
|
||||
LsColXMLParser parser;
|
||||
connect( &parser, SIGNAL(directoryListingSubfolders(const QStringList&)),
|
||||
this, SIGNAL(directoryListingSubfolders(const QStringList&)) );
|
||||
connect( &parser, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
|
||||
this, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)) );
|
||||
connect( &parser, SIGNAL(finishedWithError(QNetworkReply *)),
|
||||
this, SIGNAL(finishedWithError(QNetworkReply *)) );
|
||||
connect( &parser, SIGNAL(finishedWithoutError()),
|
||||
this, SIGNAL(finishedWithoutError()) );
|
||||
|
||||
QStringList folders;
|
||||
QString currentHref;
|
||||
QMap<QString, QString> currentTmpProperties;
|
||||
QMap<QString, QString> currentHttp200Properties;
|
||||
bool currentPropsHaveHttp200 = false;
|
||||
bool insidePropstat = false;
|
||||
bool insideProp = false;
|
||||
|
||||
while (!reader.atEnd()) {
|
||||
QXmlStreamReader::TokenType type = reader.readNext();
|
||||
QString name = reader.name().toString();
|
||||
// Start elements with DAV:
|
||||
if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if (name == QLatin1String("href")) {
|
||||
currentHref = QUrl::fromPercentEncoding(reader.readElementText().toUtf8());
|
||||
} else if (name == QLatin1String("response")) {
|
||||
} else if (name == QLatin1String("propstat")) {
|
||||
insidePropstat = true;
|
||||
} else if (name == QLatin1String("status") && insidePropstat) {
|
||||
QString httpStatus = reader.readElementText();
|
||||
if (httpStatus.startsWith("HTTP/1.1 200")) {
|
||||
currentPropsHaveHttp200 = true;
|
||||
} else {
|
||||
currentPropsHaveHttp200 = false;
|
||||
}
|
||||
} else if (name == QLatin1String("prop")) {
|
||||
insideProp = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == QXmlStreamReader::StartElement && insidePropstat && insideProp) {
|
||||
// All those elements are properties
|
||||
QString propertyContent = readContentsAsString(reader);
|
||||
if (name == QLatin1String("resourcetype") && propertyContent.contains("collection")) {
|
||||
folders.append(currentHref);
|
||||
} else if (name == QLatin1String("quota-used-bytes")) {
|
||||
bool ok = false;
|
||||
auto s = propertyContent.toLongLong(&ok);
|
||||
if (ok) {
|
||||
_sizes[currentHref] = s;
|
||||
}
|
||||
}
|
||||
currentTmpProperties.insert(reader.name().toString(), propertyContent);
|
||||
}
|
||||
|
||||
// End elements with DAV:
|
||||
if (type == QXmlStreamReader::EndElement) {
|
||||
if (reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if (reader.name() == "response") {
|
||||
if (currentHref.endsWith('/')) {
|
||||
currentHref.chop(1);
|
||||
}
|
||||
emit directoryListingIterated(currentHref, currentHttp200Properties);
|
||||
currentHref.clear();
|
||||
currentHttp200Properties.clear();
|
||||
} else if (reader.name() == "propstat") {
|
||||
insidePropstat = false;
|
||||
if (currentPropsHaveHttp200) {
|
||||
currentHttp200Properties = QMap<QString,QString>(currentTmpProperties);
|
||||
}
|
||||
currentTmpProperties.clear();
|
||||
currentPropsHaveHttp200 = false;
|
||||
} else if (reader.name() == "prop") {
|
||||
insideProp = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !parser.parse( reply()->readAll(), &_sizes ) ) {
|
||||
// XML parse error
|
||||
emit finishedWithError(reply());
|
||||
}
|
||||
emit directoryListingSubfolders(folders);
|
||||
emit finishedWithoutError();
|
||||
} else if (httpCode == 207) {
|
||||
// wrong content type
|
||||
emit finishedWithError(reply());
|
||||
} else {
|
||||
// wrong HTTP code
|
||||
// wrong HTTP code or any other network error
|
||||
emit finishedWithError(reply());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -648,8 +694,9 @@ bool PropfindJob::finished()
|
||||
if (type == QXmlStreamReader::StartElement) {
|
||||
if (!curElement.isEmpty() && curElement.top() == QLatin1String("prop")) {
|
||||
items.insert(reader.name().toString(), reader.readElementText());
|
||||
} else {
|
||||
curElement.push(reader.name().toString());
|
||||
}
|
||||
curElement.push(reader.name().toString());
|
||||
}
|
||||
if (type == QXmlStreamReader::EndElement) {
|
||||
if(curElement.top() == reader.name()) {
|
||||
|
||||
@@ -94,6 +94,9 @@ protected:
|
||||
QElapsedTimer _durationTimer;
|
||||
quint64 _duration;
|
||||
bool _timedout; // set to true when the timeout slot is recieved
|
||||
|
||||
// Automatically follows redirects. Note that this only works for
|
||||
// GET requests that don't set up any HTTP body or other flags.
|
||||
bool _followRedirects;
|
||||
|
||||
private slots:
|
||||
@@ -129,6 +132,21 @@ private slots:
|
||||
/**
|
||||
* @brief The LsColJob class
|
||||
*/
|
||||
class OWNCLOUDSYNC_EXPORT LsColXMLParser : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LsColXMLParser();
|
||||
|
||||
bool parse(const QByteArray &xml, QHash<QString, qint64> *sizes);
|
||||
|
||||
signals:
|
||||
void directoryListingSubfolders(const QStringList &items);
|
||||
void directoryListingIterated(const QString &name, const QMap<QString,QString> &properties);
|
||||
void finishedWithError(QNetworkReply *reply);
|
||||
void finishedWithoutError();
|
||||
|
||||
};
|
||||
|
||||
class OWNCLOUDSYNC_EXPORT LsColJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#endif
|
||||
#include "configfile.h"
|
||||
#include "utility.h"
|
||||
#include "account.h"
|
||||
#include <json.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -40,9 +41,13 @@
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QTimerEvent>
|
||||
#include <QDebug>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
OwncloudPropagator::~OwncloudPropagator()
|
||||
{}
|
||||
|
||||
/* The maximum number of active job in parallel */
|
||||
int OwncloudPropagator::maximumActiveJob()
|
||||
{
|
||||
@@ -631,6 +636,9 @@ void PropagateDirectory::finalize()
|
||||
emit finished(_hasError == SyncFileItem::NoStatus ? SyncFileItem::Success : _hasError);
|
||||
}
|
||||
|
||||
CleanupPollsJob::~CleanupPollsJob()
|
||||
{}
|
||||
|
||||
void CleanupPollsJob::start()
|
||||
{
|
||||
if (_pollInfos.empty()) {
|
||||
|
||||
@@ -270,6 +270,8 @@ public:
|
||||
, _account(account)
|
||||
{ }
|
||||
|
||||
~OwncloudPropagator();
|
||||
|
||||
void start(const SyncFileItemVector &_syncedItems);
|
||||
|
||||
QAtomicInt _downloadLimit;
|
||||
@@ -359,6 +361,8 @@ public:
|
||||
SyncJournalDb *journal, const QString &localPath, QObject* parent = 0)
|
||||
: QObject(parent), _pollInfos(pollInfos), _account(account), _journal(journal), _localPath(localPath) {}
|
||||
|
||||
~CleanupPollsJob();
|
||||
|
||||
void start();
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QNetworkReply>
|
||||
#include <QDebug>
|
||||
#include "syncfileitem.h"
|
||||
|
||||
namespace OCC {
|
||||
@@ -33,9 +34,14 @@ inline QByteArray parseEtag(const char *header) {
|
||||
|
||||
inline QByteArray getEtagFromReply(QNetworkReply *reply)
|
||||
{
|
||||
QByteArray ret = parseEtag(reply->rawHeader("OC-ETag"));
|
||||
QByteArray ocEtag = parseEtag(reply->rawHeader("OC-ETag"));
|
||||
QByteArray etag = parseEtag(reply->rawHeader("ETag"));
|
||||
QByteArray ret = ocEtag;
|
||||
if (ret.isEmpty()) {
|
||||
ret = parseEtag(reply->rawHeader("ETag"));
|
||||
ret = etag;
|
||||
}
|
||||
if (ocEtag.length() > 0 && ocEtag != etag) {
|
||||
qDebug() << "Quite peculiar, we have an etag != OC-Etag [no problem!]" << etag << ocEtag;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <cmath>
|
||||
|
||||
namespace OCC {
|
||||
@@ -338,6 +339,7 @@ void PropagateDownloadFileQNAM::start()
|
||||
if (startSize > 0) {
|
||||
if (startSize == _item._size) {
|
||||
qDebug() << "File is already complete, no need to download";
|
||||
_tmpFile.close();
|
||||
downloadFinished();
|
||||
return;
|
||||
}
|
||||
@@ -368,6 +370,7 @@ void PropagateDownloadFileQNAM::start()
|
||||
_job->start();
|
||||
}
|
||||
|
||||
const char owncloudCustomSoftErrorStringC[] = "owncloud-custom-soft-error-string";
|
||||
void PropagateDownloadFileQNAM::slotGetFinished()
|
||||
{
|
||||
_propagator->_activeJobs--;
|
||||
@@ -407,7 +410,18 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
||||
return;
|
||||
}
|
||||
|
||||
// This gives a custom QNAM (by the user of libowncloudsync) to abort() a QNetworkReply in its metaDataChanged() slot and
|
||||
// set a custom error string to make this a soft error. In contrast to the default hard error this won't bring down
|
||||
// the whole sync and allows for a custom error message.
|
||||
QNetworkReply *reply = job->reply();
|
||||
if (err == QNetworkReply::OperationCanceledError && reply->property(owncloudCustomSoftErrorStringC).isValid()) {
|
||||
job->setErrorString(reply->property(owncloudCustomSoftErrorStringC).toString());
|
||||
job->setErrorStatus(SyncFileItem::SoftError);
|
||||
}
|
||||
|
||||
SyncFileItem::Status status = job->errorStatus();
|
||||
|
||||
|
||||
if (status == SyncFileItem::NoStatus) {
|
||||
status = classifyError(err, _item._httpErrorCode);
|
||||
}
|
||||
@@ -476,7 +490,6 @@ QString makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||
|
||||
void PropagateDownloadFileQNAM::downloadFinished()
|
||||
{
|
||||
|
||||
QString fn = _propagator->getFilePath(_item._file);
|
||||
|
||||
// In case of file name clash, report an error
|
||||
@@ -506,10 +519,14 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
_tmpFile.setPermissions(existingFile.permissions());
|
||||
}
|
||||
|
||||
FileSystem::setFileHidden(_tmpFile.fileName(), false);
|
||||
FileSystem::setModTime(_tmpFile.fileName(), _item._modtime);
|
||||
// We need to fetch the time again because some file system such as FAT have a less than a second
|
||||
// Accuracy, and we really need the time from the file system. (#3103)
|
||||
_item._modtime = FileSystem::getModTime(_tmpFile.fileName());
|
||||
|
||||
QString error;
|
||||
_propagator->addTouchedFile(fn);
|
||||
FileSystem::setFileHidden(_tmpFile.fileName(), false);
|
||||
if (!FileSystem::renameReplace(_tmpFile.fileName(), fn, &error)) {
|
||||
qDebug() << Q_FUNC_INFO << QString("Rename failed: %1 => %2").arg(_tmpFile.fileName()).arg(fn);
|
||||
// If we moved away the original file due to a conflict but can't
|
||||
@@ -530,7 +547,6 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
|
||||
// Maybe we downloaded a newer version of the file than we thought we would...
|
||||
// Get up to date information for the journal.
|
||||
FileSystem::setModTime(fn, _item._modtime);
|
||||
_item._size = FileSystem::getSize(fn);
|
||||
|
||||
_propagator->_journal->setFileRecord(SyncJournalFileRecord(_item, fn));
|
||||
|
||||
@@ -78,8 +78,10 @@ public:
|
||||
qint64 currentDownloadPosition();
|
||||
|
||||
QString errorString() const;
|
||||
void setErrorString(const QString& s) { _errorString = s; }
|
||||
|
||||
SyncFileItem::Status errorStatus() { return _errorStatus; }
|
||||
void setErrorStatus(const SyncFileItem::Status & s) { _errorStatus = s; }
|
||||
|
||||
virtual void slotTimeout() Q_DECL_OVERRIDE;
|
||||
|
||||
|
||||
@@ -85,9 +85,10 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
|
||||
<< (_job->reply()->error() == QNetworkReply::NoError ? QLatin1String("") : _job->reply()->errorString());
|
||||
|
||||
QNetworkReply::NetworkError err = _job->reply()->error();
|
||||
_item._httpErrorCode = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
const int httpStatus = _job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
_item._httpErrorCode = httpStatus;
|
||||
|
||||
if (err != QNetworkReply::NoError) {
|
||||
if (err != QNetworkReply::NoError && err != QNetworkReply::ContentNotFoundError) {
|
||||
|
||||
if( checkForProblemsWithShared(_item._httpErrorCode,
|
||||
tr("The file has been removed from a read only share. It was restored.")) ) {
|
||||
@@ -102,7 +103,11 @@ void PropagateRemoteDelete::slotDeleteJobFinished()
|
||||
_item._requestDuration = _job->duration();
|
||||
_item._responseTimeStamp = _job->responseTimestamp();
|
||||
|
||||
if (_item._httpErrorCode != 204 ) {
|
||||
// A 404 reply is also considered a success here: We want to make sure
|
||||
// a file is gone from the server. It not being there in the first place
|
||||
// is ok. This will happen for files that are in the DB but not on
|
||||
// the server or the local file system.
|
||||
if (httpStatus != 204 && httpStatus != 404) {
|
||||
// Normaly we expect "204 No Content"
|
||||
// If it is not the case, it might be because of a proxy or gateway intercepting the request, so we must
|
||||
// throw an error.
|
||||
|
||||
@@ -64,6 +64,12 @@ static qint64 chunkSize() {
|
||||
return chunkSize;
|
||||
}
|
||||
|
||||
PUTFileJob::~PUTFileJob()
|
||||
{
|
||||
// Make sure that we destroy the QNetworkReply before our _device of which it keeps an internal pointer.
|
||||
setReply(0);
|
||||
}
|
||||
|
||||
void PUTFileJob::start() {
|
||||
QNetworkRequest req;
|
||||
for(QMap<QByteArray, QByteArray>::const_iterator it = _headers.begin(); it != _headers.end(); ++it) {
|
||||
@@ -518,8 +524,8 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||
// But if the upload is ongoing, because not all chunks were uploaded
|
||||
// yet, the upload can be stopped and an error can be displayed, because
|
||||
// the server hasn't registered the new file yet.
|
||||
bool finished = job->reply()->hasRawHeader("ETag")
|
||||
|| job->reply()->hasRawHeader("OC-ETag");
|
||||
QByteArray etag = getEtagFromReply(job->reply());
|
||||
bool finished = etag.length() > 0;
|
||||
|
||||
// Check if the file still exists
|
||||
const QString fullFilePath(_propagator->getFilePath(_item._file));
|
||||
@@ -589,7 +595,6 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||
_item._fileId = fid;
|
||||
}
|
||||
|
||||
QByteArray etag = getEtagFromReply(job->reply());
|
||||
_item._etag = etag;
|
||||
|
||||
_item._responseTimeStamp = job->responseTimestamp();
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
explicit PUTFileJob(AccountPtr account, const QString& path, QIODevice *device,
|
||||
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
|
||||
: AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk) {}
|
||||
~PUTFileJob();
|
||||
|
||||
int _chunk;
|
||||
|
||||
|
||||
@@ -695,7 +695,8 @@ void PropagateDownloadFileLegacy::start()
|
||||
&& !FileSystem::fileEquals(fn, tmpFile.fileName()); // compare the files to see if there was an actual conflict.
|
||||
//In case of conflict, make a backup of the old file
|
||||
if (isConflict) {
|
||||
QString conflictFileName = makeConflictFileName(fn, Utility::qDateTimeFromTime_t(_item._modtime));
|
||||
auto conflictDate = FileSystem::fileExists(fn) ? FileSystem::getModTime(fn) : _item._modtime;
|
||||
QString conflictFileName = makeConflictFileName(fn, Utility::qDateTimeFromTime_t(conflictDate));
|
||||
QString renameError;
|
||||
if (!FileSystem::rename(fn, conflictFileName, &renameError)) {
|
||||
//If the rename fails, don't replace it.
|
||||
|
||||
@@ -384,6 +384,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
case CSYNC_STATUS_STORAGE_UNAVAILABLE:
|
||||
item._errorString = QLatin1String("Directory temporarily not available on server.");
|
||||
item._status = SyncFileItem::SoftError;
|
||||
_temporarilyUnavailablePaths.insert(item._file);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT("Non handled error-status");
|
||||
@@ -484,9 +485,11 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
}
|
||||
|
||||
item._direction = dir;
|
||||
// check for blacklisting of this item.
|
||||
// if the item is on blacklist, the instruction was set to IGNORE
|
||||
checkErrorBlacklisting( &item );
|
||||
if (instruction != CSYNC_INSTRUCTION_NONE) {
|
||||
// check for blacklisting of this item.
|
||||
// if the item is on blacklist, the instruction was set to ERROR
|
||||
checkErrorBlacklisting( &item );
|
||||
}
|
||||
|
||||
if (!item._isDirectory) {
|
||||
_progressInfo._totalFileCount++;
|
||||
@@ -602,14 +605,25 @@ void SyncEngine::startSync()
|
||||
// database creation error!
|
||||
}
|
||||
|
||||
if (fileRecordCount >= 1 && isUpdateFrom_1_5) {
|
||||
bool isUpdateFrom_1_8 = _journal->isUpdateFrom_1_8_0();
|
||||
|
||||
/*
|
||||
* If 1.8.0 caused missing data in the local tree, this patch gets it
|
||||
* back. For that, the usage of the journal for remote repository is
|
||||
* disabled at the first start.
|
||||
*/
|
||||
if (fileRecordCount >= 1 && (isUpdateFrom_1_5 || isUpdateFrom_1_8)) {
|
||||
qDebug() << "detected update from 1.5" << fileRecordCount << isUpdateFrom_1_5;
|
||||
// Disable the read from DB to be sure to re-read all the fileid and etags.
|
||||
csync_set_read_from_db(_csync_ctx, false);
|
||||
_csync_ctx->read_remote_from_db = false;
|
||||
} else {
|
||||
csync_set_read_from_db(_csync_ctx, true);
|
||||
_csync_ctx->read_remote_from_db = true;
|
||||
}
|
||||
|
||||
// This tells csync to never read from the DB if it is empty
|
||||
// thereby speeding up the initial discovery significantly.
|
||||
_csync_ctx->db_is_empty = (fileRecordCount == 0);
|
||||
|
||||
bool usingSelectiveSync = (!_selectiveSyncBlackList.isEmpty());
|
||||
qDebug() << (usingSelectiveSync ? "====Using Selective Sync" : "====NOT Using Selective Sync");
|
||||
if (fileRecordCount >= 0 && fileRecordCount < 50 && !usingSelectiveSync) {
|
||||
@@ -690,7 +704,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||
return;
|
||||
}
|
||||
|
||||
_stopWatch.addLapTime(QLatin1String("Reconcile Finished"));
|
||||
qDebug() << "<<#### Reconcile end #################################################### " << _stopWatch.addLapTime(QLatin1String("Reconcile Finished"));
|
||||
|
||||
_progressInfo = Progress::Info();
|
||||
|
||||
@@ -698,6 +712,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||
_hasRemoveFile = false;
|
||||
bool walkOk = true;
|
||||
_seenFiles.clear();
|
||||
_temporarilyUnavailablePaths.clear();
|
||||
|
||||
if( csync_walk_local_tree(_csync_ctx, &treewalkLocal, 0) < 0 ) {
|
||||
qDebug() << "Error in local treewalk.";
|
||||
@@ -791,6 +806,8 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
||||
emit(started());
|
||||
|
||||
_propagator->start(_syncedItems);
|
||||
|
||||
qDebug() << "<<#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QLatin1String("Post-Reconcile Finished"));
|
||||
}
|
||||
|
||||
void SyncEngine::slotCleanPollsJobAborted(const QString &error)
|
||||
@@ -859,7 +876,7 @@ void SyncEngine::slotFinished()
|
||||
_anotherSyncNeeded = _anotherSyncNeeded || _propagator->_anotherSyncNeeded;
|
||||
|
||||
// emit the treewalk results.
|
||||
if( ! _journal->postSyncCleanup( _seenFiles ) ) {
|
||||
if( ! _journal->postSyncCleanup( _seenFiles, _temporarilyUnavailablePaths ) ) {
|
||||
qDebug() << "Cleaning of synced ";
|
||||
}
|
||||
|
||||
@@ -989,8 +1006,8 @@ void SyncEngine::checkForPermission()
|
||||
it->_direction = SyncFileItem::Down;
|
||||
it->_isRestoration = true;
|
||||
// take the things to write to the db from the "other" node (i.e: info from server)
|
||||
// ^^ FIXME This might not be needed anymore since we merge the info in treewalkFile
|
||||
it->_modtime = it->log._other_modtime;
|
||||
it->_size = it->log._other_size;
|
||||
it->_fileId = it->log._other_fileId;
|
||||
it->_etag = it->log._other_etag;
|
||||
it->_errorString = tr("Not allowed to upload this file because it is read-only on the server, restoring");
|
||||
|
||||
@@ -159,7 +159,21 @@ private:
|
||||
QPointer<DiscoveryMainThread> _discoveryMainThread;
|
||||
QSharedPointer <OwncloudPropagator> _propagator;
|
||||
QString _lastDeleted; // if the last item was a path and it has been deleted
|
||||
|
||||
// After a sync, only the syncdb entries whose filenames appear in this
|
||||
// set will be kept. See _temporarilyUnavailablePaths.
|
||||
QSet<QString> _seenFiles;
|
||||
|
||||
// Some paths might be temporarily unavailable on the server, for
|
||||
// example due to 503 Storage not available. Deleting information
|
||||
// about the files from the database in these cases would lead to
|
||||
// incorrect synchronization.
|
||||
// Therefore all syncdb entries whose filename starts with one of
|
||||
// the paths in this set will be kept.
|
||||
// The specific case that fails otherwise is deleting a local file
|
||||
// while the remote says storage not available.
|
||||
QSet<QString> _temporarilyUnavailablePaths;
|
||||
|
||||
QThread _thread;
|
||||
|
||||
Progress::Info _progressInfo;
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
namespace OCC {
|
||||
|
||||
SyncJournalDb::SyncJournalDb(const QString& path, QObject *parent) :
|
||||
QObject(parent), _transaction(0), _possibleUpgradeFromMirall_1_5(false)
|
||||
QObject(parent), _transaction(0), _possibleUpgradeFromMirall_1_5(false), _possibleUpgradeFromMirall_1_8_0(false)
|
||||
{
|
||||
|
||||
_dbFile = path;
|
||||
@@ -273,6 +273,8 @@ bool SyncJournalDb::checkConnect()
|
||||
}
|
||||
|
||||
_possibleUpgradeFromMirall_1_5 = false;
|
||||
_possibleUpgradeFromMirall_1_8_0 = false;
|
||||
|
||||
SqlQuery versionQuery("SELECT major, minor, patch FROM version;", _db);
|
||||
if (!versionQuery.next()) {
|
||||
// If there was no entry in the table, it means we are likely upgrading from 1.5
|
||||
@@ -284,7 +286,7 @@ bool SyncJournalDb::checkConnect()
|
||||
createQuery.bindValue(1, MIRALL_VERSION_MAJOR);
|
||||
createQuery.bindValue(2, MIRALL_VERSION_MINOR);
|
||||
createQuery.bindValue(3, MIRALL_VERSION_PATCH);
|
||||
createQuery.bindValue(3, MIRALL_VERSION_BUILD);
|
||||
createQuery.bindValue(4, MIRALL_VERSION_BUILD);
|
||||
createQuery.exec();
|
||||
|
||||
} else {
|
||||
@@ -292,6 +294,10 @@ bool SyncJournalDb::checkConnect()
|
||||
int minor = versionQuery.intValue(1);
|
||||
int patch = versionQuery.intValue(2);
|
||||
|
||||
if( major == 1 && minor == 8 && patch == 0 ) {
|
||||
qDebug() << Q_FUNC_INFO << "_possibleUpgradeFromMirall_1_8_0 detected!";
|
||||
_possibleUpgradeFromMirall_1_8_0 = true;
|
||||
}
|
||||
// Not comparing the BUILD id here, correct?
|
||||
if( !(major == MIRALL_VERSION_MAJOR && minor == MIRALL_VERSION_MINOR && patch == MIRALL_VERSION_PATCH) ) {
|
||||
createQuery.prepare("UPDATE version SET major=?1, minor=?2, patch =?3, custom=?4 "
|
||||
@@ -698,7 +704,8 @@ SyncJournalFileRecord SyncJournalDb::getFileRecord( const QString& filename )
|
||||
return rec;
|
||||
}
|
||||
|
||||
bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
|
||||
bool SyncJournalDb::postSyncCleanup(const QSet<QString>& filepathsToKeep,
|
||||
const QSet<QString>& prefixesToKeep)
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
@@ -719,8 +726,16 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
|
||||
|
||||
while(query.next()) {
|
||||
const QString file = query.stringValue(1);
|
||||
bool contained = items.contains(file);
|
||||
if( !contained ) {
|
||||
bool keep = filepathsToKeep.contains(file);
|
||||
if( !keep ) {
|
||||
foreach( const QString & prefix, prefixesToKeep ) {
|
||||
if( file.startsWith(prefix) ) {
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( !keep ) {
|
||||
superfluousItems.append(query.stringValue(0));
|
||||
}
|
||||
}
|
||||
@@ -744,6 +759,10 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString> &items )
|
||||
_possibleUpgradeFromMirall_1_5 = false; // should be handled now
|
||||
}
|
||||
|
||||
if (_possibleUpgradeFromMirall_1_8_0) {
|
||||
_possibleUpgradeFromMirall_1_8_0 = false; // should be handled now
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1313,6 +1332,13 @@ bool SyncJournalDb::isUpdateFrom_1_5()
|
||||
return _possibleUpgradeFromMirall_1_5;
|
||||
}
|
||||
|
||||
bool SyncJournalDb::isUpdateFrom_1_8_0()
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
checkConnect();
|
||||
return _possibleUpgradeFromMirall_1_8_0;
|
||||
}
|
||||
|
||||
bool operator==(const SyncJournalDb::DownloadInfo & lhs,
|
||||
const SyncJournalDb::DownloadInfo & rhs)
|
||||
{
|
||||
|
||||
@@ -97,7 +97,8 @@ public:
|
||||
*/
|
||||
void avoidReadFromDbOnNextSync(const QString& fileName);
|
||||
|
||||
bool postSyncCleanup( const QSet<QString>& items );
|
||||
bool postSyncCleanup(const QSet<QString>& filepathsToKeep,
|
||||
const QSet<QString>& prefixesToKeep);
|
||||
|
||||
/* Because sqlite transactions is really slow, we encapsulate everything in big transactions
|
||||
* Commit will actually commit the transaction and create a new one.
|
||||
@@ -117,6 +118,7 @@ public:
|
||||
* are updated.
|
||||
*/
|
||||
bool isUpdateFrom_1_5();
|
||||
bool isUpdateFrom_1_8_0();
|
||||
|
||||
private:
|
||||
bool updateDatabaseStructure();
|
||||
@@ -134,6 +136,7 @@ private:
|
||||
QMutex _mutex; // Public functions are protected with the mutex.
|
||||
int _transaction;
|
||||
bool _possibleUpgradeFromMirall_1_5;
|
||||
bool _possibleUpgradeFromMirall_1_8_0;
|
||||
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
||||
|
||||
@@ -122,11 +122,14 @@ QIcon Theme::themeIcon( const QString& name, bool sysTray ) const
|
||||
flavor = QLatin1String("colored");
|
||||
}
|
||||
|
||||
QIcon icon;
|
||||
if( QIcon::hasThemeIcon( name )) {
|
||||
// use from theme
|
||||
icon = QIcon::fromTheme( name );
|
||||
} else {
|
||||
return QIcon::fromTheme( name );
|
||||
}
|
||||
|
||||
QString key = name + "," + flavor;
|
||||
QIcon & cached = _iconCache[key];
|
||||
if (cached.isNull()) {
|
||||
QList<int> sizes;
|
||||
sizes <<16 << 22 << 32 << 48 << 64 << 128 << 256;
|
||||
foreach (int size, sizes) {
|
||||
@@ -140,19 +143,20 @@ QIcon Theme::themeIcon( const QString& name, bool sysTray ) const
|
||||
p.setPen(QColor("#dfdbd2"));
|
||||
p.drawPixmap(px.rect(), mask, mask.rect());
|
||||
}
|
||||
icon.addPixmap(px);
|
||||
cached.addPixmap(px);
|
||||
}
|
||||
}
|
||||
if (icon.isNull()) {
|
||||
if (cached.isNull()) {
|
||||
foreach (int size, sizes) {
|
||||
QString pixmapName = QString::fromLatin1(":/client/resources/%1-%2.png").arg(name).arg(size);
|
||||
if (QFile::exists(pixmapName)) {
|
||||
icon.addFile(pixmapName);
|
||||
cached.addFile(pixmapName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return icon;
|
||||
|
||||
return cached;
|
||||
}
|
||||
|
||||
QString Theme::hidpiFileName(const QString &fileName, QPaintDevice *dev)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef _THEME_H
|
||||
#define _THEME_H
|
||||
|
||||
#include <QObject>
|
||||
#include "syncresult.h"
|
||||
|
||||
|
||||
@@ -196,7 +197,9 @@ public:
|
||||
virtual bool wizardSelectiveSyncDefaultNothing() const;
|
||||
|
||||
protected:
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
QIcon themeIcon(const QString& name, bool sysTray = false) const;
|
||||
#endif
|
||||
Theme();
|
||||
|
||||
signals:
|
||||
@@ -208,7 +211,9 @@ private:
|
||||
|
||||
static Theme* _instance;
|
||||
bool _mono;
|
||||
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
mutable QHash<QString, QIcon> _iconCache;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -32,5 +32,5 @@ owncloud_add_test(SyncJournalDB "")
|
||||
owncloud_add_test(SyncFileItem "")
|
||||
owncloud_add_test(ConcatUrl "")
|
||||
|
||||
|
||||
owncloud_add_test(XmlParse "")
|
||||
|
||||
|
||||
245
test/testxmlparse.h
Normal file
245
test/testxmlparse.h
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.
|
||||
* */
|
||||
|
||||
#ifndef MIRALL_TESTXMLPARSE_H
|
||||
#define MIRALL_TESTXMLPARSE_H
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
#include "networkjobs.h"
|
||||
|
||||
using namespace OCC;
|
||||
|
||||
class TestXmlParse : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
bool _success;
|
||||
QStringList _subdirs;
|
||||
QStringList _items;
|
||||
|
||||
public slots:
|
||||
void slotDirectoryListingSubFolders(const QStringList& list)
|
||||
{
|
||||
qDebug() << "subfolders: " << list;
|
||||
_subdirs.append(list);
|
||||
}
|
||||
|
||||
void slotDirectoryListingIterated(const QString& item, const QMap<QString,QString>& )
|
||||
{
|
||||
qDebug() << " item: " << item;
|
||||
_items.append(item);
|
||||
}
|
||||
|
||||
void slotFinishedSuccessfully()
|
||||
{
|
||||
_success = true;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void init() {
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
_success = false;
|
||||
_subdirs.clear();
|
||||
_items.clear();
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
}
|
||||
|
||||
|
||||
void testParser1() {
|
||||
const QByteArray testXml = "<?xml version='1.0' encoding='utf-8'?>"
|
||||
"<d:multistatus xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\">"
|
||||
"<d:response>"
|
||||
"<d:href>/oc/remote.php/webdav/sharefolder/</d:href>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<oc:id>00004213ocobzus5kn6s</oc:id>"
|
||||
"<oc:permissions>RDNVCK</oc:permissions>"
|
||||
"<oc:size>121780</oc:size>"
|
||||
"<d:getetag>\"5527beb0400b0\"</d:getetag>"
|
||||
"<d:resourcetype>"
|
||||
"<d:collection/>"
|
||||
"</d:resourcetype>"
|
||||
"<d:getlastmodified>Fri, 06 Feb 2015 13:49:55 GMT</d:getlastmodified>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 200 OK</d:status>"
|
||||
"</d:propstat>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<d:getcontentlength/>"
|
||||
"<oc:downloadURL/>"
|
||||
"<oc:dDC/>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 404 Not Found</d:status>"
|
||||
"</d:propstat>"
|
||||
"</d:response>"
|
||||
"<d:response>"
|
||||
"<d:href>/oc/remote.php/webdav/sharefolder/quitte.pdf</d:href>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<oc:id>00004215ocobzus5kn6s</oc:id>"
|
||||
"<oc:permissions>RDNVW</oc:permissions>"
|
||||
"<d:getetag>\"2fa2f0d9ed49ea0c3e409d49e652dea0\"</d:getetag>"
|
||||
"<d:resourcetype/>"
|
||||
"<d:getlastmodified>Fri, 06 Feb 2015 13:49:55 GMT</d:getlastmodified>"
|
||||
"<d:getcontentlength>121780</d:getcontentlength>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 200 OK</d:status>"
|
||||
"</d:propstat>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<oc:downloadURL/>"
|
||||
"<oc:dDC/>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 404 Not Found</d:status>"
|
||||
"</d:propstat>"
|
||||
"</d:response>"
|
||||
"</d:multistatus>";
|
||||
|
||||
|
||||
LsColXMLParser parser;
|
||||
|
||||
connect( &parser, SIGNAL(directoryListingSubfolders(const QStringList&)),
|
||||
this, SLOT(slotDirectoryListingSubFolders(const QStringList&)) );
|
||||
connect( &parser, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
|
||||
this, SLOT(slotDirectoryListingIterated(const QString&, const QMap<QString,QString>&)) );
|
||||
connect( &parser, SIGNAL(finishedWithoutError()),
|
||||
this, SLOT(slotFinishedSuccessfully()) );
|
||||
|
||||
QHash <QString, qint64> sizes;
|
||||
QVERIFY(parser.parse( testXml, &sizes ));
|
||||
|
||||
QVERIFY(_success);
|
||||
QVERIFY(sizes.size() == 0 ); // No quota info in the XML
|
||||
|
||||
QVERIFY(_items.contains("/oc/remote.php/webdav/sharefolder/quitte.pdf"));
|
||||
QVERIFY(_items.contains("/oc/remote.php/webdav/sharefolder"));
|
||||
QVERIFY(_items.size() == 2 );
|
||||
|
||||
QVERIFY(_subdirs.contains("/oc/remote.php/webdav/sharefolder/"));
|
||||
QVERIFY(_subdirs.size() == 1);
|
||||
}
|
||||
|
||||
void testParserBrokenXml() {
|
||||
const QByteArray testXml = "X<?xml version='1.0' encoding='utf-8'?>"
|
||||
"<d:multistatus xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\" xmlns:oc=\"http://owncloud.org/ns\">"
|
||||
"<d:response>"
|
||||
"<d:href>/oc/remote.php/webdav/sharefolder/</d:href>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<oc:id>00004213ocobzus5kn6s</oc:id>"
|
||||
"<oc:permissions>RDNVCK</oc:permissions>"
|
||||
"<oc:size>121780</oc:size>"
|
||||
"<d:getetag>\"5527beb0400b0\"</d:getetag>"
|
||||
"<d:resourcetype>"
|
||||
"<d:collection/>"
|
||||
"</d:resourcetype>"
|
||||
"<d:getlastmodified>Fri, 06 Feb 2015 13:49:55 GMT</d:getlastmodified>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 200 OK</d:status>"
|
||||
"</d:propstat>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<d:getcontentlength/>"
|
||||
"<oc:downloadURL/>"
|
||||
"<oc:dDC/>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 404 Not Found</d:status>"
|
||||
"</d:propstat>"
|
||||
"</d:response>"
|
||||
"<d:response>"
|
||||
"<d:href>/oc/remote.php/webdav/sharefolder/quitte.pdf</d:href>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<oc:id>00004215ocobzus5kn6s</oc:id>"
|
||||
"<oc:permissions>RDNVW</oc:permissions>"
|
||||
"<d:getetag>\"2fa2f0d9ed49ea0c3e409d49e652dea0\"</d:getetag>"
|
||||
"<d:resourcetype/>"
|
||||
"<d:getlastmodified>Fri, 06 Feb 2015 13:49:55 GMT</d:getlastmodified>"
|
||||
"<d:getcontentlength>121780</d:getcontentlength>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 200 OK</d:status>"
|
||||
"</d:propstat>"
|
||||
"<d:propstat>"
|
||||
"<d:prop>"
|
||||
"<oc:downloadURL/>"
|
||||
"<oc:dDC/>"
|
||||
"</d:prop>"
|
||||
"<d:status>HTTP/1.1 404 Not Found</d:status>"
|
||||
"</d:propstat>"
|
||||
"</d:response>"
|
||||
"</d:multistatus>";
|
||||
|
||||
|
||||
LsColXMLParser parser;
|
||||
|
||||
connect( &parser, SIGNAL(directoryListingSubfolders(const QStringList&)),
|
||||
this, SLOT(slotDirectoryListingSubFolders(const QStringList&)) );
|
||||
connect( &parser, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
|
||||
this, SLOT(slotDirectoryListingIterated(const QString&, const QMap<QString,QString>&)) );
|
||||
connect( &parser, SIGNAL(finishedWithoutError()),
|
||||
this, SLOT(slotFinishedSuccessfully()) );
|
||||
|
||||
QHash <QString, qint64> sizes;
|
||||
QVERIFY(false == parser.parse( testXml, &sizes )); // verify false
|
||||
|
||||
QVERIFY(!_success);
|
||||
QVERIFY(sizes.size() == 0 ); // No quota info in the XML
|
||||
|
||||
QVERIFY(_items.size() == 0 ); // FIXME: We should change the parser to not emit during parsing but at the end
|
||||
|
||||
QVERIFY(_subdirs.size() == 0);
|
||||
}
|
||||
|
||||
void testParserEmptyXmlNoDav() {
|
||||
const QByteArray testXml = "<html><body>I am under construction</body></html>";
|
||||
|
||||
LsColXMLParser parser;
|
||||
|
||||
connect( &parser, SIGNAL(directoryListingSubfolders(const QStringList&)),
|
||||
this, SLOT(slotDirectoryListingSubFolders(const QStringList&)) );
|
||||
connect( &parser, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
|
||||
this, SLOT(slotDirectoryListingIterated(const QString&, const QMap<QString,QString>&)) );
|
||||
connect( &parser, SIGNAL(finishedWithoutError()),
|
||||
this, SLOT(slotFinishedSuccessfully()) );
|
||||
|
||||
QHash <QString, qint64> sizes;
|
||||
QVERIFY(false == parser.parse( testXml, &sizes )); // verify false
|
||||
|
||||
QVERIFY(!_success);
|
||||
QVERIFY(sizes.size() == 0 ); // No quota info in the XML
|
||||
|
||||
QVERIFY(_items.size() == 0 ); // FIXME: We should change the parser to not emit during parsing but at the end
|
||||
QVERIFY(_subdirs.size() == 0);
|
||||
}
|
||||
|
||||
void testParserEmptyXml() {
|
||||
const QByteArray testXml = "";
|
||||
|
||||
LsColXMLParser parser;
|
||||
|
||||
connect( &parser, SIGNAL(directoryListingSubfolders(const QStringList&)),
|
||||
this, SLOT(slotDirectoryListingSubFolders(const QStringList&)) );
|
||||
connect( &parser, SIGNAL(directoryListingIterated(const QString&, const QMap<QString,QString>&)),
|
||||
this, SLOT(slotDirectoryListingIterated(const QString&, const QMap<QString,QString>&)) );
|
||||
connect( &parser, SIGNAL(finishedWithoutError()),
|
||||
this, SLOT(slotFinishedSuccessfully()) );
|
||||
|
||||
QHash <QString, qint64> sizes;
|
||||
QVERIFY(false == parser.parse( testXml, &sizes )); // verify false
|
||||
|
||||
QVERIFY(!_success);
|
||||
QVERIFY(sizes.size() == 0 ); // No quota info in the XML
|
||||
|
||||
QVERIFY(_items.size() == 0 ); // FIXME: We should change the parser to not emit during parsing but at the end
|
||||
QVERIFY(_subdirs.size() == 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,7 @@ trans.pt_BR = client_pt_BR.ts
|
||||
trans.ru = client_ru.ts
|
||||
trans.sl = client_sl.ts
|
||||
trans.sv = client_sv.ts
|
||||
trans.sr = client_sr.ts
|
||||
trans.tr = client_tr.ts
|
||||
trans.uk = client_uk.ts
|
||||
trans.zh_TW = client_zh_TW.ts
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user