mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-04 12:11:33 +02:00
Compare commits
216 Commits
v1.8.1-bet
...
v1.8.3-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42513af536 | ||
|
|
676a2ff8ca | ||
|
|
1e56349838 | ||
|
|
33b8064042 | ||
|
|
1f5170253c | ||
|
|
ad6f2e03c9 | ||
|
|
4c3daf7927 | ||
|
|
4936f70d20 | ||
|
|
ecd2da185d | ||
|
|
7a7bf4c561 | ||
|
|
827e07700d | ||
|
|
d142c554d1 | ||
|
|
69f2b2da7c | ||
|
|
4000993200 | ||
|
|
2c6c21d753 | ||
|
|
34384e3613 | ||
|
|
a42856ef84 | ||
|
|
e7f01b9a95 | ||
|
|
fc2ceb88fb | ||
|
|
36f62f7fd4 | ||
|
|
ee08b3759d | ||
|
|
c73110e3c7 | ||
|
|
9ced5fb9a3 | ||
|
|
f9659d80c9 | ||
|
|
553e831121 | ||
|
|
e9ff471e1c | ||
|
|
eb6d29a223 | ||
|
|
3df26ef189 | ||
|
|
25767bee0b | ||
|
|
0f7773ff99 | ||
|
|
2ed329e025 | ||
|
|
e04882cc7c | ||
|
|
40818920d1 | ||
|
|
1bec652822 | ||
|
|
4c10f1e40c | ||
|
|
53154bcd1e | ||
|
|
1d600ceecc | ||
|
|
e1a36b53dc | ||
|
|
5f91abd7c6 | ||
|
|
75b38d1a2f | ||
|
|
7f597e5dd2 | ||
|
|
e3be1a176d | ||
|
|
b1658cb1cf | ||
|
|
6ba3324035 | ||
|
|
e2a26f0eaf | ||
|
|
8cfb17bca6 | ||
|
|
211e5ec82e | ||
|
|
2435ba7e7d | ||
|
|
be77e4f57e | ||
|
|
f344b42aa9 | ||
|
|
90f4a20a50 | ||
|
|
793130a533 | ||
|
|
1ddb162338 | ||
|
|
070d9d3147 | ||
|
|
9b73308edd | ||
|
|
b46a8239e2 | ||
|
|
e9d0f5a022 | ||
|
|
5721d8eb64 | ||
|
|
e708c145fa | ||
|
|
713beeb2a5 | ||
|
|
6c46713701 | ||
|
|
3754e6c781 | ||
|
|
6401b1cfc3 | ||
|
|
6b9e123816 | ||
|
|
fb2295fcec | ||
|
|
0af2f7e5ed | ||
|
|
1c84d01584 | ||
|
|
3fcce08a22 | ||
|
|
289800c1ba | ||
|
|
49fb37fefc | ||
|
|
9a02a0f3a8 | ||
|
|
4e79093f84 | ||
|
|
d2fff2c3e3 | ||
|
|
8f277e46d6 | ||
|
|
c33d962712 | ||
|
|
3870915118 | ||
|
|
b05ca526a4 | ||
|
|
4e28a24af3 | ||
|
|
aebadfcda2 | ||
|
|
c975954a9a | ||
|
|
dec3bd4a02 | ||
|
|
64ce0cd7a2 | ||
|
|
34593cccb6 | ||
|
|
5b5a636cc1 | ||
|
|
5c6a6529a6 | ||
|
|
68fa190cf7 | ||
|
|
d148464efe | ||
|
|
6a7f2089e8 | ||
|
|
17fe4c3b29 | ||
|
|
dd6c97abb6 | ||
|
|
ddfe3fa7ab | ||
|
|
1f7274c2f2 | ||
|
|
4d87f30434 | ||
|
|
8b371c42b7 | ||
|
|
c7f759fedf | ||
|
|
830daa40d1 | ||
|
|
f016d25b4c | ||
|
|
e18fd62f34 | ||
|
|
3701fbcbfe | ||
|
|
823f9fa0d1 | ||
|
|
67d38bc87b | ||
|
|
b36ff1ed1d | ||
|
|
ec83295b99 | ||
|
|
e36252a845 | ||
|
|
d0d8de9f2f | ||
|
|
9693048f78 | ||
|
|
101d2268ff | ||
|
|
2fcad760b9 | ||
|
|
52eb6c95cf | ||
|
|
35169e3de4 | ||
|
|
650b201b33 | ||
|
|
f595fc2f9c | ||
|
|
06c889630c | ||
|
|
d6dbabfbc4 | ||
|
|
bcae146444 | ||
|
|
8a39748654 | ||
|
|
3556ed416c | ||
|
|
e5e2ce2b22 | ||
|
|
39d103adf7 | ||
|
|
afd1406e61 | ||
|
|
a4c411af99 | ||
|
|
bcc896fb6e | ||
|
|
76166c6252 | ||
|
|
dd5a49bc78 | ||
|
|
cdfafa2180 | ||
|
|
6b16e18eb8 | ||
|
|
c2dacd03a5 | ||
|
|
505dba5b23 | ||
|
|
af5a7063c9 | ||
|
|
9e7779a476 | ||
|
|
625e61516f | ||
|
|
41614ec851 | ||
|
|
4d3a0ed250 | ||
|
|
cac15988f0 | ||
|
|
1e131f4732 | ||
|
|
22c35c4d15 | ||
|
|
9507bb4be6 | ||
|
|
e1c370a9a2 | ||
|
|
b9eafaaf24 | ||
|
|
909368025f | ||
|
|
15bfa46023 | ||
|
|
0359c775e0 | ||
|
|
1053153ec4 | ||
|
|
79ac61684c | ||
|
|
441eca86c4 | ||
|
|
f07d3d069e | ||
|
|
e300e3c744 | ||
|
|
b9df8290c9 | ||
|
|
73e2254a80 | ||
|
|
352c2957b2 | ||
|
|
23b6426dfa | ||
|
|
9ef8658122 | ||
|
|
0a67719f2f | ||
|
|
04d820f9cf | ||
|
|
931dd59844 | ||
|
|
a6500d8068 | ||
|
|
0d5d2c578d | ||
|
|
77679790db | ||
|
|
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 |
@@ -165,13 +165,13 @@ endif()
|
||||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
|
||||
|
||||
find_package(SQLite3 3.8.0 REQUIRED)
|
||||
# On some OS, we want to use our own, not the system sqlite
|
||||
if (USE_OUR_OWN_SQLITE3)
|
||||
include_directories(BEFORE ${SQLITE3_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
find_package(ZLIB)
|
||||
|
||||
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
|
||||
92
ChangeLog
92
ChangeLog
@@ -1,6 +1,96 @@
|
||||
ChangeLog
|
||||
=========
|
||||
version 1.8.0 (release 2015-03-xx)
|
||||
version 1.8.3 (release 2015-06-??)
|
||||
* Fix a bug in the Windows Installer that could crash explorer (#3320)
|
||||
* Reduce 'Connection closed' errors (#3318, #3313, #3298)
|
||||
|
||||
ChangeLog
|
||||
=========
|
||||
version 1.8.2 (release 2015-06-08)
|
||||
* Improve reporting of server error messages (#3220)
|
||||
* Discovery: Ignore folders with any 503 (#3113)
|
||||
* Wizard: Show server error message if possible (#3220)
|
||||
* QNAM: Fix handling of mitm cert changes (#3283)
|
||||
* Win32: Installer translations added (#3277)
|
||||
* Win32: Allow concurrent OEM (un-)installers (#3272)
|
||||
* Win32: Make Setup/Update Mutex theme-unique (#3272)
|
||||
* HTTP: Add the branding name to the UserAgent string
|
||||
* ConnectonValidator: Always run with new credentials (#3266)
|
||||
* Recall Feature: Admins can trigger an upload of a file from
|
||||
client to server again (#3246)
|
||||
* Propagator: Add 'Content-Length: 0' header to MKCOL request (#3256)
|
||||
* Switch on checksum verification through branding or config
|
||||
* Add ability for checksum verification of up and download
|
||||
* Fix opening external links for some labels (#3135)
|
||||
* AccountState: Run only a single validator, allow error message
|
||||
overriding (#3236, #3153)
|
||||
* SyncJournalDB: Minor fixes and simplificatons
|
||||
* SyncEngine: Force re-read of folder Etags for upgrades from
|
||||
1.8.0 and 1.8.1
|
||||
* Propagator: Limit length of temporary file name (#2789)
|
||||
* ShareDialog: Password ui fixes (#3189)
|
||||
* Fix startup hang by removing QSettings lock file (#3175)
|
||||
* Wizard: Allow SSL cert dialog to show twice (#3168)
|
||||
* ProtocolWidget: Fix rename message (#3210)
|
||||
* Discovery: Test better, treat invalid hrefs as error (#3176)
|
||||
* Propagator: Overwrite local data only if unchanged (#3156)
|
||||
* ShareDialog: Improve error reporting for share API fails
|
||||
* OSX Updater: Only allow updates only if in /Applications (#2931)
|
||||
* Wizard: Fix lock icon (#1447)
|
||||
* Fix compilation with GCC 5
|
||||
* Treat any 503 error as temporary (#3113)
|
||||
* Work around for the Qt PUT corruption bug (#2425)
|
||||
* OSX Shell integration: Optimizations
|
||||
* Windows Shell integration: Optimizations
|
||||
.. more than 250 commits since 1.8.1
|
||||
|
||||
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,10 +1,10 @@
|
||||
set( MIRALL_VERSION_MAJOR 1 )
|
||||
set( MIRALL_VERSION_MINOR 8 )
|
||||
set( MIRALL_VERSION_PATCH 1 )
|
||||
set( MIRALL_VERSION_PATCH 3 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
|
||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
set( MIRALL_VERSION_SUFFIX "beta1") #e.g. beta1, beta2, rc1
|
||||
set( MIRALL_VERSION_SUFFIX "rc2") #e.g. beta1, beta2, rc1
|
||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
|
||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,24 +10,25 @@ minimum_perc = 5
|
||||
|
||||
# simple one-to-one language mappings
|
||||
trans.ca = pofiles/ca.po
|
||||
trans.el = pofiles/el.po
|
||||
trans.es = pofiles/es.po
|
||||
trans.es_AR = pofiles/es_AR.po
|
||||
trans.eu = pofiles/eu.po
|
||||
trans.fa = pofiles/fa.po
|
||||
trans.fr = pofiles/fr.po
|
||||
trans.gl = pofiles/gl.po
|
||||
trans.it = pofiles/it.po
|
||||
trans.nl = pofiles/nl.po
|
||||
trans.pl = pofiles/pl.po
|
||||
trans.pt_BR = pofiles/pt_BR.po
|
||||
trans.ru = pofiles/ru.po
|
||||
trans.el = pofiles/el.po
|
||||
trans.es_AR = pofiles/es_AR.po
|
||||
trans.es = pofiles/es.po
|
||||
trans.eu = pofiles/eu.po
|
||||
trans.fa = pofiles/fa.po
|
||||
trans.fr = pofiles/fr.po
|
||||
trans.gl = pofiles/gl.po
|
||||
trans.it = pofiles/it.po
|
||||
trans.nb_NO = pofiles/nb_NO.po
|
||||
trans.nl = pofiles/nl.po
|
||||
trans.pl = pofiles/pl.po
|
||||
trans.pt_BR = pofiles/pt_BR.po
|
||||
trans.ru = pofiles/ru.po
|
||||
trans.sl = pofiles/sl.po
|
||||
trans.sv = pofiles/sv.po
|
||||
trans.tr = pofiles/tr.po
|
||||
trans.uk = pofiles/uk.po
|
||||
trans.zh_TW = pofiles/zh_TW.po
|
||||
trans.zh_CN = pofiles/zh_CN.po
|
||||
trans.uk = pofiles/uk.po
|
||||
trans.zh_CN = pofiles/zh_CN.po
|
||||
trans.zh_TW = pofiles/zh_TW.po
|
||||
|
||||
# special handling below
|
||||
|
||||
@@ -35,14 +36,13 @@ trans.zh_CN = pofiles/zh_CN.po
|
||||
trans.de_DE = pofiles/de.po
|
||||
|
||||
# choose one of the given translations on transifex as default
|
||||
trans.pt_PT = pofiles/pt.po
|
||||
trans.pt_PT = pofiles/pt.po
|
||||
|
||||
# choose a special language as more generic default
|
||||
trans.cs_CZ = pofiles/cs.po
|
||||
trans.et_EE = pofiles/et.po
|
||||
trans.fi_FI = pofiles/fi.po
|
||||
trans.ja_JP = pofiles/ja.po
|
||||
trans.hu_HU = pofiles/hu.po
|
||||
trans.sk_SK = pofiles/sk.po
|
||||
trans.th_TH = pofiles/th.po
|
||||
|
||||
trans.cs_CZ = pofiles/cs.po
|
||||
trans.et_EE = pofiles/et.po
|
||||
trans.fi_FI = pofiles/fi.po
|
||||
trans.hu_HU = pofiles/hu.po
|
||||
trans.ja_JP = pofiles/ja.po
|
||||
trans.sk_SK = pofiles/sk.po
|
||||
trans.th_TH = pofiles/th.po
|
||||
|
||||
@@ -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"
|
||||
|
||||
46
admin/win/nsi/l10n/Norwegian.nsh
Normal file
46
admin/win/nsi/l10n/Norwegian.nsh
Normal file
@@ -0,0 +1,46 @@
|
||||
# Auto-generated - do not modify
|
||||
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Vis versjonsmerknader"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Fant ${APPLICATION_EXECUTABLE}-prosess(er) som må stoppes.$\nVil du at installasjonsprogrammet skal stoppe dem for deg?"
|
||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Terminerer ${APPLICATION_EXECUTABLE}-prosesser."
|
||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Fant ikke prosess som skulle termineres!"
|
||||
StrCpy $PageReinstall_NEW_Field_1 "En eldre versjon av ${APPLICATION_NAME} er installert på systemet ditt. Det anbefales at du avnistallerer den versjonen før installering av ny versjon. Velg hva du vil gjøre og klikk Neste for å fortsette."
|
||||
StrCpy $PageReinstall_NEW_Field_2 "Avinstaller før installering"
|
||||
StrCpy $PageReinstall_NEW_Field_3 "Ikke avinstaller"
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Allerede installert"
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Velg hvordan du vil installere ${APPLICATION_NAME}."
|
||||
StrCpy $PageReinstall_OLD_Field_1 "En nyere versjon av ${APPLICATION_NAME} er allerede installert! Det anbefales ikke at du installerer en eldre versjon. Hvis du virkelig ønsker å installere denne eldre versjonen, er det bedre å avinstallere gjeldende versjon først. Velg hva du vil gjøre og klikk Neste for å fortsette."
|
||||
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} er allerede installert.\nVelg hva du vil gjøre og klikk Neste for å fortsette."
|
||||
StrCpy $PageReinstall_SAME_Field_2 "Legg til/installer komponenter på nytt"
|
||||
StrCpy $PageReinstall_SAME_Field_3 "Avinstaller ${APPLICATION_NAME}"
|
||||
StrCpy $UNINSTALLER_APPDATA_TITLE "Avinstaller ${APPLICATION_NAME}"
|
||||
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Velg hva slags vedlikehold som skal utføres."
|
||||
StrCpy $SEC_APPLICATION_DETAILS "Installerer ${APPLICATION_NAME} grunnleggende."
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integrering med Windows Utforsker"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Installerer integrering med Windows Utforsker"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Snarvei i Start-menyen"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "Legger til snarvei for ${APPLICATION_NAME} i Start-menyen."
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Snarvei på skrivebordet"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "Oppretter snarveier på skrivebordet"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Snarvei i Hurtigstart"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Oppretter snarvei i Hurtigstart"
|
||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} grunnleggende."
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "${APPLICATION_NAME}-snarvei."
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Skrivebordssnarvei for ${APPLICATION_NAME}."
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Hurtigstart-snarvei for ${APPLICATION_NAME}."
|
||||
StrCpy $UNINSTALLER_APPDATA_SUBTITLE "Fjern ${APPLICATION_NAME} sin datamappe fra datamaskinen."
|
||||
StrCpy $UNINSTALLER_APPDATA_LABEL_1 "Ønsker du å slette ${APPLICATION_NAME} sin datamappe?"
|
||||
StrCpy $UNINSTALLER_APPDATA_LABEL_2 "Ikke kryss av dersom du vil beholde datamappen for senere bruk. Kryss av for å slette mappen."
|
||||
StrCpy $UNINSTALLER_APPDATA_CHECKBOX "Ja, slett denne datamappen."
|
||||
StrCpy $UNINSTALLER_FILE_Detail "Skriver Avinstallasjonsprogram."
|
||||
StrCpy $UNINSTALLER_REGISTRY_Detail "Skriver registernøkler for installasjonsprogrammet"
|
||||
StrCpy $UNINSTALLER_FINISHED_Detail "Ferdig"
|
||||
StrCpy $UNINSTALL_MESSAGEBOX "Det ser ikke ut som ${APPLICATION_NAME} er installert i mappe '$INSTDIR'.$\n$\nFortsett likevel (ikke anbefalt)?"
|
||||
StrCpy $UNINSTALL_ABORT "Avinstallering avbrutt av bruker"
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "Hurtigstart-snarvei (I/T)"
|
||||
StrCpy $INIT_NO_DESKTOP "Snarvei på skrivebordet (skriver over eksisterende)"
|
||||
StrCpy $UAC_ERROR_ELEVATE "Klarte ikke å heve tilgangsnivå. Feil: "
|
||||
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Dette installasjonsprogrammet krever administrasjonstilgang. Prøv igjen"
|
||||
StrCpy $INIT_INSTALLER_RUNNING "Installasjonsprogrammet kjører allerede."
|
||||
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Avinstallasjonsprogrammet krever administrasjonstilgang. Prøv igjen"
|
||||
StrCpy $INIT_UNINSTALLER_RUNNING "Avinstallasjonsprogrammet kjører allerede."
|
||||
StrCpy $SectionGroup_Shortcuts "Snarveier"
|
||||
46
admin/win/nsi/l10n/Thai.nsh
Normal file
46
admin/win/nsi/l10n/Thai.nsh
Normal file
@@ -0,0 +1,46 @@
|
||||
# Auto-generated - do not modify
|
||||
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "แสดงบันทึกประจำรุ่น"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "พบว่ากระบวนการ ${APPLICATION_EXECUTABLE} จะต้องหยุดทำงาน\nคุณต้องการติดตั้งเพื่อหยุดการทำงานเหล่านี้ของคุณ?"
|
||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "ฆ่ากระบวนการทำงาน ${APPLICATION_EXECUTABLE}"
|
||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "ไม่พบการฆ่ากระบวนการ!"
|
||||
StrCpy $PageReinstall_NEW_Field_1 "รุ่นเก่าของ ${APPLICATION_NAME} มีการติดตั้งในระบบของคุณ ขอแนะนำให้คุณถอนการติดตั้งรุ่นปัจจุบันออกก่อน เลือกการดำเนินการที่คุณต้องการที่จะดำเนินการและคลิกถัดไปเพื่อดำเนินการต่อ"
|
||||
StrCpy $PageReinstall_NEW_Field_2 "ถอนการติดตั้งก่อนการติดตั้ง"
|
||||
StrCpy $PageReinstall_NEW_Field_3 "อย่าถอนการติดตั้ง"
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "ติดตั้งแล้ว"
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "เลือกวิธีที่คุณต้องการติดตั้ง ${APPLICATION_NAME}"
|
||||
StrCpy $PageReinstall_OLD_Field_1 "รุ่นใหม่ของ ${APPLICATION_NAME} ถูกติดตั้งแล้ว! เราไม่แนะนำให้คุณติดตั้งรุ่นเก่า ถ้าคุณอยากจะติดตั้งรุ่นเก่าก็สามารถสอนการติดตั้งได้"
|
||||
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} ได้ถูกติดตั้งแล้ว เลือกการดำเนินการที่คุณต้องการที่จะดำเนินการและคลิกถัดไปเพื่อดำเนินการต่อ"
|
||||
StrCpy $PageReinstall_SAME_Field_2 "ส่วนประกอบ เพิ่ม/ติดตั้งใหม่ "
|
||||
StrCpy $PageReinstall_SAME_Field_3 "ถอนการติดตั้ง ${APPLICATION_NAME}"
|
||||
StrCpy $UNINSTALLER_APPDATA_TITLE "ถอนการติดตั้ง ${APPLICATION_NAME}"
|
||||
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "เลือกตัวเลือกการบำรุงรักษาเพื่อดำเนินการ"
|
||||
StrCpy $SEC_APPLICATION_DETAILS "กำลังติดตั้งข้อมูลที่เป็นเป็นของ ${APPLICATION_NAME}"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "บูรณาการสำหรับ Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "บูรณาการกำลังติดตั้งสำหรับ Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "โปรแกรมทางลัดเมนูเริ่มต้น"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "เพิ่มทางลัดสำหรับ ${APPLICATION_NAME} ที่เมนูเริ่มต้น"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "ทางลัดพื้นโต๊ะ"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "สร้างทางลัดบนเดสก์ทอป"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "ทางลัดเร่งด่วน"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "สร้างทางลัดเร่งด่วน"
|
||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} ที่จำเป็น"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "ทางลัด ${APPLICATION_NAME}"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "ทางลัดบนเดสก์ทอปสำหรับ ${APPLICATION_NAME}"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "ทางลัดเร่งด่วนสำหรับ ${APPLICATION_NAME}"
|
||||
StrCpy $UNINSTALLER_APPDATA_SUBTITLE "ลบ ${APPLICATION_NAME} ของโฟลเดอร์ข้อมูลจากคอมพิวเตอร์ของคุณ"
|
||||
StrCpy $UNINSTALLER_APPDATA_LABEL_1 "คุณต้องการลบ ${APPLICATION_NAME} ของโฟลเดอร์ข้อมูล?"
|
||||
StrCpy $UNINSTALLER_APPDATA_LABEL_2 "ปล่อยให้ตรวจสอบสำหรับใช้โฟลเดอร์ข้อมูลในภายหลังหรือตรวจสอบเพื่อลบโฟลเดอร์ข้อมูล"
|
||||
StrCpy $UNINSTALLER_APPDATA_CHECKBOX "ใช่ลบโฟลเดอร์ข้อมูลนี้"
|
||||
StrCpy $UNINSTALLER_FILE_Detail "กำลังถอนการติดตั้ง"
|
||||
StrCpy $UNINSTALLER_REGISTRY_Detail "กำลังติดตั้งรหัสรีจิสทรี"
|
||||
StrCpy $UNINSTALLER_FINISHED_Detail "เสร็จสิ้น"
|
||||
StrCpy $UNINSTALL_MESSAGEBOX "มันจะไม่ปรากฏ ${APPLICATION_NAME} ที่มีการติดตั้งในไดเรกทอรี '$INSTDIR' ดำเนินการต่อไป (ไม่แนะนำ)?"
|
||||
StrCpy $UNINSTALL_ABORT "ถอนการติดตั้งถูกยกเลิกโดยผู้ใช้"
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "ทางลัดเร่งด่วน (N/A)"
|
||||
StrCpy $INIT_NO_DESKTOP "ทางลัดเดสก์ทอป (จะเขียนทับหากมีอยู่แล้ว)"
|
||||
StrCpy $UAC_ERROR_ELEVATE "ไม่สามารถที่จะยกระดับข้อผิดพลาด:"
|
||||
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "การติดตั้งนี้จะต้องมีการเข้าถึงผู้ดูแลระบบ กรุณาลองอีกครั้ง"
|
||||
StrCpy $INIT_INSTALLER_RUNNING "กำลังทำการติดตั้ง"
|
||||
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "การถอนการติดตั้งนี้จะต้องมีการเข้าถึงส่วนผู้ดูแลระบบ กรุณาลองอีกครั้ง"
|
||||
StrCpy $INIT_UNINSTALLER_RUNNING "กำลังทำการถอนการติดตั้ง"
|
||||
StrCpy $SectionGroup_Shortcuts "ทางลัด"
|
||||
@@ -67,7 +67,7 @@ localeToName = {
|
||||
"mg" : "Malagasy",
|
||||
"ms" : "Malay",
|
||||
"mn" : "Mongolian",
|
||||
"nb" : "Norwegian",
|
||||
"nb_NO" : "Norwegian",
|
||||
"nn" : "NorwegianNynorsk",
|
||||
"ps" : "Pashto",
|
||||
"pl" : "Polish",
|
||||
|
||||
@@ -44,5 +44,6 @@ iconv -t CP1252 -o Finnish.nsh Finnish.nsh
|
||||
iconv -t CP932 -o Japanese.nsh Japanese.nsh
|
||||
iconv -t CP1250 -o Slovak.nsh Slovak.nsh
|
||||
iconv -t CP1254 -o Turkish.nsh Turkish.nsh
|
||||
iconv -t CP1252 -o Norwegian.nsh Norwegian.nsh
|
||||
|
||||
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
!insertmacro MUI_LANGUAGE "Hungarian"
|
||||
!insertmacro MUI_LANGUAGE "Ukrainian"
|
||||
!insertmacro MUI_LANGUAGE "French"
|
||||
!insertmacro MUI_LANGUAGE "Catalan"
|
||||
!insertmacro MUI_LANGUAGE "Norwegian"
|
||||
!insertmacro MUI_LANGUAGE "Russian"
|
||||
!insertmacro MUI_LANGUAGE "Thai"
|
||||
!insertmacro MUI_LANGUAGE "Finnish"
|
||||
!insertmacro MUI_LANGUAGE "Basque"
|
||||
!insertmacro MUI_LANGUAGE "Greek"
|
||||
!insertmacro MUI_LANGUAGE "SimpChinese"
|
||||
!insertmacro MUI_LANGUAGE "PortugueseBR"
|
||||
!insertmacro MUI_LANGUAGE "Catalan"
|
||||
!insertmacro MUI_LANGUAGE "Italian"
|
||||
!insertmacro MUI_LANGUAGE "Portuguese"
|
||||
!insertmacro MUI_LANGUAGE "German"
|
||||
|
||||
2
binary
2
binary
Submodule binary updated: 1fb9ddfa9a...8b72648a93
@@ -175,10 +175,11 @@ VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${VERSION}"
|
||||
StrCmp $LANGUAGE ${LANG_ITALIAN} Italian 0
|
||||
StrCmp $LANGUAGE ${LANG_ESTONIAN} Estonian 0
|
||||
StrCmp $LANGUAGE ${LANG_GREEK} Greek 0
|
||||
StrCmp $LANGUAGE ${LANG_GREEK} Basque 0
|
||||
StrCmp $LANGUAGE ${LANG_GREEK} Galician 0
|
||||
StrCmp $LANGUAGE ${LANG_GREEK} Slovak 0
|
||||
StrCmp $LANGUAGE ${LANG_GREEK} Turkish 0
|
||||
StrCmp $LANGUAGE ${LANG_BASQUE} Basque 0
|
||||
StrCmp $LANGUAGE ${LANG_GALICIAN} Galician 0
|
||||
StrCmp $LANGUAGE ${LANG_SLOVAC} Slovak 0
|
||||
StrCmp $LANGUAGE ${LANG_TURKISH} Turkish 0
|
||||
StrCmp $LANGUAGE ${LANG_NORWEGIAN} Norwegian 0
|
||||
StrCmp $LANGUAGE ${LANG_PORTUGUESEBR} Brazilian EndLanguageCmp
|
||||
German:
|
||||
!include "${source_path}/admin/win/nsi/l10n\German.nsh"
|
||||
@@ -221,6 +222,9 @@ VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${VERSION}"
|
||||
Goto EndLanguageCmp
|
||||
Brazilian:
|
||||
!include "${source_path}/admin/win/nsi/l10n\PortugueseBR.nsh"
|
||||
Goto EndLanguageCmp
|
||||
Norwegian:
|
||||
!include "${source_path}/admin/win/nsi/l10n\Norwegian.nsh"
|
||||
EndLanguageCmp:
|
||||
|
||||
FunctionEnd
|
||||
@@ -475,6 +479,7 @@ SectionEnd
|
||||
CreateDirectory "$INSTDIR\shellext"
|
||||
!define LIBRARY_COM
|
||||
!define LIBRARY_SHELL_EXTENSION
|
||||
!define LIBRARY_IGNORE_VERSION
|
||||
${If} ${RunningX64}
|
||||
!define LIBRARY_X64
|
||||
!insertmacro InstallLib DLL NOTSHARED REBOOT_PROTECTED "${SOURCE_PATH}\binary\shell_integration\windows\Release\x64\OCUtil_x64.dll" "$INSTDIR\shellext\OCUtil_x64.dll" "$INSTDIR\shellext"
|
||||
@@ -488,6 +493,7 @@ SectionEnd
|
||||
${Endif}
|
||||
!undef LIBRARY_COM
|
||||
!undef LIBRARY_SHELL_EXTENSION
|
||||
!undef LIBRARY_IGNORE_VERSION
|
||||
${MementoSectionEnd}
|
||||
!endif
|
||||
|
||||
@@ -646,6 +652,7 @@ Section Uninstall
|
||||
!ifdef OPTION_SECTION_SC_SHELL_EXT
|
||||
!define LIBRARY_COM
|
||||
!define LIBRARY_SHELL_EXTENSION
|
||||
!define LIBRARY_IGNORE_VERSION
|
||||
${If} ${HasSection} SEC_SHELL_EXT
|
||||
DetailPrint "Uninstalling x64 overlay DLLs"
|
||||
!define LIBRARY_X64
|
||||
@@ -661,6 +668,7 @@ Section Uninstall
|
||||
${EndIf}
|
||||
!undef LIBRARY_COM
|
||||
!undef LIBRARY_SHELL_EXTENSION
|
||||
!undef LIBRARY_IGNORE_VERSION
|
||||
!endif
|
||||
|
||||
;Start menu shortcut
|
||||
@@ -777,7 +785,7 @@ Function .onInit
|
||||
goto UAC_Elevate
|
||||
|
||||
;Prevent multiple instances.
|
||||
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "owncloudInstaller") i .r1 ?e'
|
||||
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "${APPLICATION_SHORTNAME}Installer") i .r1 ?e'
|
||||
Pop $R0
|
||||
StrCmp $R0 0 +3
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION $INIT_INSTALLER_RUNNING
|
||||
@@ -842,7 +850,7 @@ Function un.onInit
|
||||
goto UAC_Elevate
|
||||
|
||||
;Prevent multiple instances.
|
||||
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "owncloudUninstaller") i .r1 ?e'
|
||||
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "${APPLICATION_SHORTNAME}Uninstaller") i .r1 ?e'
|
||||
Pop $R0
|
||||
StrCmp $R0 0 +3
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION $INIT_UNINSTALLER_RUNNING
|
||||
|
||||
@@ -17,6 +17,7 @@ if( Qt5Core_FOUND )
|
||||
message(STATUS "Found Qt5 core, checking for further dependencies...")
|
||||
find_package(Qt5Network REQUIRED)
|
||||
find_package(Qt5Xml REQUIRED)
|
||||
find_package(Qt5Concurrent REQUIRED)
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
find_package(Qt5WebKit REQUIRED)
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#cmakedefine APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@"
|
||||
#cmakedefine APPLICATION_UPDATE_URL "@APPLICATION_UPDATE_URL@"
|
||||
|
||||
#cmakedefine ZLIB_FOUND @ZLIB_FOUND@
|
||||
|
||||
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
|
||||
#cmakedefine DATADIR "@DATADIR@"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -386,6 +386,20 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
|
||||
SAFE_FREE(renamed_path);
|
||||
}
|
||||
|
||||
if (!other_node) {
|
||||
/* Check the source path as well. */
|
||||
int len;
|
||||
uint64_t h = 0;
|
||||
char *renamed_path = csync_rename_adjust_path_source(ctx, cur->path);
|
||||
|
||||
if (!c_streq(renamed_path, cur->path)) {
|
||||
len = strlen( renamed_path );
|
||||
h = c_jhash64((uint8_t *) renamed_path, len, 0);
|
||||
other_node = c_rbtree_find(other_tree, &h);
|
||||
}
|
||||
SAFE_FREE(renamed_path);
|
||||
}
|
||||
|
||||
if (obj == NULL || data == NULL) {
|
||||
ctx->status_code = CSYNC_STATUS_PARAM_ERROR;
|
||||
return -1;
|
||||
|
||||
@@ -40,8 +40,6 @@
|
||||
#include <sys/types.h>
|
||||
#include <config_csync.h>
|
||||
|
||||
#include "csync_version.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -233,6 +233,7 @@ CSYNC_EXCLUDE_TYPE csync_excluded_no_ctx(c_strlist_t *excludes, const char *path
|
||||
}
|
||||
|
||||
// check the strlen and ignore the file if its name is longer than 254 chars.
|
||||
// whenever changing this also check createDownloadTmpFileName
|
||||
if (strlen(bname) > 254) {
|
||||
match = CSYNC_FILE_EXCLUDE_LONG_FILENAME;
|
||||
SAFE_FREE(bname);
|
||||
|
||||
@@ -61,7 +61,7 @@ enum csync_log_priority_e {
|
||||
};
|
||||
|
||||
#define CSYNC_LOG(priority, ...) \
|
||||
csync_log(priority, __FUNCTION__, __VA_ARGS__)
|
||||
csync_log(priority, __func__, __VA_ARGS__)
|
||||
|
||||
void csync_log(int verbosity,
|
||||
const char *function,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ struct csync_rename_s {
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> folder_renamed_to; // map from->to
|
||||
std::map<std::string, std::string> folder_renamed_from; // map to->from
|
||||
|
||||
struct renameop {
|
||||
csync_file_stat_t *st;
|
||||
@@ -63,6 +64,7 @@ void csync_rename_destroy(CSYNC* ctx)
|
||||
void csync_rename_record(CSYNC* ctx, const char* from, const char* to)
|
||||
{
|
||||
csync_rename_s::get(ctx)->folder_renamed_to[from] = to;
|
||||
csync_rename_s::get(ctx)->folder_renamed_from[to] = from;
|
||||
}
|
||||
|
||||
char* csync_rename_adjust_path(CSYNC* ctx, const char* path)
|
||||
@@ -78,4 +80,18 @@ char* csync_rename_adjust_path(CSYNC* ctx, const char* path)
|
||||
return c_strdup(path);
|
||||
}
|
||||
|
||||
char* csync_rename_adjust_path_source(CSYNC* ctx, const char* path)
|
||||
{
|
||||
csync_rename_s* d = csync_rename_s::get(ctx);
|
||||
for (std::string p = _parentDir(path); !p.empty(); p = _parentDir(p)) {
|
||||
std::map< std::string, std::string >::iterator it = d->folder_renamed_from.find(p);
|
||||
if (it != d->folder_renamed_from.end()) {
|
||||
std::string rep = it->second + (path + p.length());
|
||||
return c_strdup(rep.c_str());
|
||||
}
|
||||
}
|
||||
return c_strdup(path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Return the final destination path of a given patch in case of renames */
|
||||
char *csync_rename_adjust_path(CSYNC *ctx, const char *path);
|
||||
/* Return the source of a given path in case of renames */
|
||||
char *csync_rename_adjust_path_source(CSYNC *ctx, const char *path);
|
||||
void csync_rename_destroy(CSYNC *ctx);
|
||||
void csync_rename_record(CSYNC *ctx, const char *from, const char *to);
|
||||
|
||||
|
||||
@@ -621,7 +621,12 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
if (asp < 0) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!");
|
||||
}
|
||||
} else if(errno == ERRNO_STORAGE_UNAVAILABLE) {
|
||||
}
|
||||
// The server usually replies with the custom "503 Storage not available"
|
||||
// if some path is temporarily unavailable. But in some cases a standard 503
|
||||
// is returned too. Thus we can't distinguish the two and will treat any
|
||||
// 503 as request to ignore the folder. See #3113 #2884.
|
||||
else if(errno == ERRNO_STORAGE_UNAVAILABLE || errno == ERRNO_SERVICE_UNAVAILABLE) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Storage was not available!");
|
||||
if (ctx->current_fs) {
|
||||
ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#define DEBUG_HBF(...) { if(transfer->log_cb) { \
|
||||
char buf[1024]; \
|
||||
snprintf(buf, 1024, __VA_ARGS__); \
|
||||
transfer->log_cb(__FUNCTION__, buf, transfer->user_data); \
|
||||
transfer->log_cb(__func__, buf, transfer->user_data); \
|
||||
} }
|
||||
|
||||
// #endif
|
||||
|
||||
@@ -131,7 +131,7 @@ static void check_logging(void **state)
|
||||
rc = csync_set_log_callback(check_log_callback);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
csync_log(1, __FUNCTION__, "rc = %d", rc);
|
||||
csync_log(1, __func__, "rc = %d", rc);
|
||||
|
||||
rc = _tstat(path, &sb);
|
||||
|
||||
|
||||
@@ -124,7 +124,8 @@ sub initTesting(;$)
|
||||
$ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0
|
||||
}
|
||||
|
||||
$d = HTTP::DAV->new();
|
||||
my $ua = HTTP::DAV::UserAgent->new(keep_alive => 1 );
|
||||
$d = HTTP::DAV->new(-useragent => $ua);
|
||||
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $user,
|
||||
@@ -191,7 +192,6 @@ sub removeRemoteDir($;$)
|
||||
my ($dir, $optionsRef) = @_;
|
||||
|
||||
my $url = testDirUrl() . $dir;
|
||||
|
||||
if( $optionsRef && $optionsRef->{user} && $optionsRef->{passwd} ) {
|
||||
$d->credentials( -url=> $owncloud, -realm=>"ownCloud",
|
||||
-user=> $optionsRef->{user},
|
||||
@@ -326,11 +326,11 @@ sub assertLocalDirs( $$ )
|
||||
|
||||
opendir(my $dh, $dir1 ) || die;
|
||||
while(readdir $dh) {
|
||||
assert( -e "$dir2/$_" );
|
||||
assert( -e "$dir2/$_", " $dir2/$_ do not exist" );
|
||||
next if( -d "$dir1/$_"); # don't compare directory sizes.
|
||||
my $s1 = -s "$dir1/$_";
|
||||
my $s2 = -s "$dir2/$_";
|
||||
assert( $s1 == $s2, "$dir1/$_ <-> $dir2/$_" );
|
||||
assert( $s1 == $s2, "$dir1/$_ <-> $dir2/$_ size not equal ($s1 != $s2)" );
|
||||
}
|
||||
closedir $dh;
|
||||
}
|
||||
@@ -524,7 +524,9 @@ sub put_to_dir( $$;$ )
|
||||
|
||||
my $filename = $file;
|
||||
$filename =~ s/^.*\///;
|
||||
$filename =~ s/#/%23/g; # poor man's URI encoder
|
||||
my $puturl = $targetUrl . $dir. $filename;
|
||||
|
||||
print "put_to_dir puts to $puturl\n";
|
||||
unless ($d->put( -local => $file, -url => $puturl )) {
|
||||
print " ### FAILED to put a single file!\n";
|
||||
|
||||
85
csync/tests/ownCloud/t_recall.pl
Executable file
85
csync/tests/ownCloud/t_recall.pl
Executable file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Test script for the ownCloud module of csync.
|
||||
# This script requires a running ownCloud instance accessible via HTTP.
|
||||
# It does quite some fancy tests and asserts the results.
|
||||
#
|
||||
# Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
|
||||
use lib ".";
|
||||
|
||||
|
||||
use File::Copy;
|
||||
use ownCloud::Test;
|
||||
|
||||
use strict;
|
||||
|
||||
print "Hello, this is t_recall, a tester for the recall feature\n";
|
||||
|
||||
initTesting();
|
||||
|
||||
printInfo( "Syncing two files with the same name that differ with case" );
|
||||
|
||||
#create some files
|
||||
my $tmpdir = "/tmp/t_recall/";
|
||||
mkdir($tmpdir);
|
||||
createLocalFile( $tmpdir . "file1.dat", 100 );
|
||||
createLocalFile( $tmpdir . "file2.dat", 150 );
|
||||
createLocalFile( $tmpdir . "file3.dat", 110 );
|
||||
createLocalFile( $tmpdir . "file4.dat", 170 );
|
||||
|
||||
#put them in some directories
|
||||
createRemoteDir( "dir" );
|
||||
glob_put( "$tmpdir/*", "dir" );
|
||||
|
||||
csync();
|
||||
|
||||
assertLocalAndRemoteDir( '', 0);
|
||||
|
||||
|
||||
|
||||
printInfo( "Testing with a .sys.admin#recall#" );
|
||||
system("echo 'dir/file2.dat' > ". $tmpdir . ".sys.admin\#recall\#");
|
||||
system("echo 'dir/file3.dat' >> ". $tmpdir . ".sys.admin\#recall\#");
|
||||
glob_put( "$tmpdir/.sys.admin\#recall\#", "" );
|
||||
|
||||
csync();
|
||||
|
||||
#test that the recall files have been created
|
||||
assert( -e glob(localDir().'dir/file2_.sys.admin#recall#-*.dat' ) );
|
||||
assert( -e glob(localDir().'dir/file3_.sys.admin#recall#-*.dat' ) );
|
||||
|
||||
#Remove the recall file
|
||||
unlink(localDir() . ".sys.admin#recall#");
|
||||
|
||||
# 2 sync necessary for the recall to be uploaded
|
||||
csync();
|
||||
|
||||
assertLocalAndRemoteDir( '', 0);
|
||||
|
||||
printInfo( "Testing with a dir/.sys.admin#recall#" );
|
||||
system("echo 'file4.dat' > ". $tmpdir . ".sys.admin\#recall\#");
|
||||
glob_put( "$tmpdir/.sys.admin\#recall\#", "dir" );
|
||||
|
||||
csync();
|
||||
assert( -e glob(localDir().'dir/file4_.sys.admin#recall#-*.dat' ) );
|
||||
|
||||
|
||||
cleanup();
|
||||
system("rm -r " . $tmpdir);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
==============
|
||||
Advanced Usage
|
||||
==============
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ The process of synchronization keeps files in two separate repositories the
|
||||
same. When synchronized:
|
||||
|
||||
- If a file is added to one repository it is copied to the other synchronized repository.
|
||||
- When a file is changed in one repository, the change is propagated to any
|
||||
synchronized other repositories- If a file is deleted in one repository, it
|
||||
is deleted in any other.
|
||||
- When a file is changed in one repository, the change is propagated to any other
|
||||
synchronized repository.
|
||||
- If a file is deleted in one repository, it is deleted in any other.
|
||||
|
||||
It is important to note that the ownCloud synchronization process does not use
|
||||
a typical client/server system where the server is always master. This is a
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -214,6 +214,8 @@ To build the most up to date version of the client:
|
||||
1. Clone the latest versions of the client from Git_ as follows:
|
||||
|
||||
``git clone git://github.com/owncloud/client.git``
|
||||
``git submodule init``
|
||||
``git submodule update``
|
||||
|
||||
2. Create the build directory:
|
||||
|
||||
|
||||
@@ -35,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
|
||||
|
||||
|
||||
|
||||
@@ -61,12 +61,12 @@ Other issues can affect synchronization of your ownCloud files:
|
||||
ensure that the folder to which you are synchronizing is not shared with
|
||||
other synchronization applications.
|
||||
|
||||
- Synchronizing the same directory with ownCloud and other
|
||||
synchronization software such as Unison, rsync, Microsoft Windows Offline
|
||||
Folders, or other cloud services such as DropBox or Microsoft SkyDrive is not
|
||||
supported and should not be attempted. In the worst case, it is possible that
|
||||
synchronizing folders or files using ownCloud and other synchronization
|
||||
software or services can result in data loss.
|
||||
- Synchronizing the same directory with ownCloud and other synchronization
|
||||
software such as Unison, rsync, Microsoft Windows Offline Folders, or other
|
||||
cloud services such as DropBox or Microsoft SkyDrive is not supported and
|
||||
should not be attempted. In the worst case, it is possible that synchronizing
|
||||
folders or files using ownCloud and other synchronization software or
|
||||
services can result in data loss.
|
||||
|
||||
- If you find that only specific files are not synrchronized, the
|
||||
synchronization protocol might be having an effect. Some files are
|
||||
@@ -80,9 +80,13 @@ Other issues can affect synchronization of your ownCloud files:
|
||||
|
||||
.. note:: The data directory on the server is exclusive to ownCloud and must not be modified manually.
|
||||
|
||||
If you are using a different file backend on the server, you can try to exclude a bug in the
|
||||
- If you are using a different file backend on the server, you can try to exclude a bug in the
|
||||
backend by reverting to the built-in backend.
|
||||
|
||||
- If you are experiencing slow upload/download speed or similar performance issues
|
||||
be aware that those could be caused by on-access virus scanning solutions, either
|
||||
on the server (like the files_antivirus app) or the client.
|
||||
|
||||
Log Files
|
||||
---------
|
||||
|
||||
@@ -125,9 +129,9 @@ mentioned above to save the log to a file.
|
||||
.. note:: You can also open a log window for an already running session, by
|
||||
restarting the client using the following command:
|
||||
|
||||
* Windows: ``C:\Program Files (x86)\ownCloud\owncloud.exe --logwindow``
|
||||
* Mac OS X: ``/Applications/owncloud.app/Contents/MacOS/owncloud --logwindow``
|
||||
* Linux: ``owncloud --logwindow``
|
||||
* Windows: ``C:\Program Files (x86)\ownCloud\owncloud.exe --logwindow``
|
||||
* Mac OS X: ``/Applications/owncloud.app/Contents/MacOS/owncloud --logwindow``
|
||||
* Linux: ``owncloud --logwindow``
|
||||
|
||||
Saving Files Directly
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
@interface ContentManager : NSObject
|
||||
{
|
||||
NSMutableDictionary* _fileNamesCache;
|
||||
NSMutableDictionary* _oldFileNamesCache;
|
||||
BOOL _fileIconsEnabled;
|
||||
BOOL _hasChangedContent;
|
||||
|
||||
@@ -35,10 +36,9 @@
|
||||
- (void)enableFileIcons:(BOOL)enable;
|
||||
- (NSNumber*)iconByPath:(NSString*)path isDirectory:(BOOL)isDir;
|
||||
- (void)removeAllIcons;
|
||||
- (void)removeIcons:(NSArray*)paths;
|
||||
- (void)setIcons:(NSDictionary*)iconDictionary filterByFolder:(NSString*)filterFolder;
|
||||
- (void)setResultForPath:(NSString*)path result:(NSString*)result;
|
||||
- (void)clearFileNameCacheForPath:(NSString*)path;
|
||||
- (void)clearFileNameCache;
|
||||
- (void)reFetchFileNameCacheForPath:(NSString*)path;
|
||||
- (void)repaintAllWindows;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ static ContentManager* sharedInstance = nil;
|
||||
if (self)
|
||||
{
|
||||
_fileNamesCache = [[NSMutableDictionary alloc] init];
|
||||
_oldFileNamesCache = [[NSMutableDictionary alloc] init];
|
||||
_fileIconsEnabled = TRUE;
|
||||
_hasChangedContent = TRUE;
|
||||
}
|
||||
@@ -41,6 +42,7 @@ static ContentManager* sharedInstance = nil;
|
||||
{
|
||||
[self removeAllIcons];
|
||||
[_fileNamesCache release];
|
||||
[_oldFileNamesCache release];
|
||||
sharedInstance = nil;
|
||||
|
||||
[super dealloc];
|
||||
@@ -148,84 +150,48 @@ static ContentManager* sharedInstance = nil;
|
||||
|
||||
if( result == nil ) {
|
||||
// start the async call
|
||||
NSNumber *askState = [[RequestManager sharedInstance] askForIcon:normalizedPath isDirectory:isDir];
|
||||
[_fileNamesCache setObject:askState forKey:normalizedPath];
|
||||
|
||||
[[RequestManager sharedInstance] askForIcon:normalizedPath isDirectory:isDir];
|
||||
result = [NSNumber numberWithInt:0];
|
||||
} else if( [result intValue] == -1 ) {
|
||||
// the socket call is underways.
|
||||
result = [NSNumber numberWithInt:0];
|
||||
} else {
|
||||
// there is a proper icon index
|
||||
// Set 0 into the cache, meaning "don't have an icon, but already requested it"
|
||||
[_fileNamesCache setObject:result forKey:normalizedPath];
|
||||
}
|
||||
// NSLog(@"iconByPath return value %d", [result intValue]);
|
||||
if ([result intValue] == 0) {
|
||||
// Show the old state while we wait for the new one
|
||||
NSNumber* oldResult = [_oldFileNamesCache objectForKey:normalizedPath];
|
||||
if (oldResult)
|
||||
result = oldResult;
|
||||
}
|
||||
// NSLog(@"iconByPath return value %d", [result intValue]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// called as a result of an UPDATE_VIEW message.
|
||||
// it clears the entries from the hash to make it call again home to the desktop client.
|
||||
- (void)clearFileNameCacheForPath:(NSString*)path
|
||||
// Clears the entries from the hash to make it call again home to the desktop client.
|
||||
- (void)clearFileNameCache
|
||||
{
|
||||
//NSLog(@"%@", NSStringFromSelector(_cmd));
|
||||
NSMutableArray *keysToDelete = [NSMutableArray array];
|
||||
|
||||
if( path != nil ) {
|
||||
for (id p in [_fileNamesCache keyEnumerator]) {
|
||||
//do stuff with obj
|
||||
if ( [p hasPrefix:path] ) {
|
||||
[keysToDelete addObject:p];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// clear the entire fileNameCache
|
||||
[_fileNamesCache release];
|
||||
_fileNamesCache = [[NSMutableDictionary alloc] init];
|
||||
return;
|
||||
}
|
||||
|
||||
if( [keysToDelete count] > 0 ) {
|
||||
NSLog( @"Entries to delete: %lu", (unsigned long)[keysToDelete count]);
|
||||
[_fileNamesCache removeObjectsForKeys:keysToDelete];
|
||||
}
|
||||
[_fileNamesCache release];
|
||||
_fileNamesCache = [[NSMutableDictionary alloc] init];
|
||||
[_oldFileNamesCache removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)reFetchFileNameCacheForPath:(NSString*)path
|
||||
{
|
||||
NSLog(@"%@", NSStringFromSelector(_cmd));
|
||||
//NSLog(@"%@", NSStringFromSelector(_cmd));
|
||||
|
||||
for (id p in [_fileNamesCache keyEnumerator]) {
|
||||
if ( path && [p hasPrefix:path] ) {
|
||||
[[RequestManager sharedInstance] askForIcon:p isDirectory:false]; // FIXME isDirectory parameter
|
||||
//[_fileNamesCache setObject:askState forKey:p]; We don't do this since we want to keep the old icon meanwhile
|
||||
//NSLog(@"%@ %@", NSStringFromSelector(_cmd), p);
|
||||
}
|
||||
}
|
||||
// We won't request the new state if if finds the path in _fileNamesCache
|
||||
// Move all entries to _oldFileNamesCache so that the get re-requested, but
|
||||
// still available while we refill the cache
|
||||
[_oldFileNamesCache addEntriesFromDictionary:_fileNamesCache];
|
||||
[_fileNamesCache removeAllObjects];
|
||||
|
||||
// Ask for directory itself
|
||||
if ([path hasSuffix:@"/"]) {
|
||||
path = [path substringToIndex:path.length - 1];
|
||||
}
|
||||
[[RequestManager sharedInstance] askForIcon:path isDirectory:true];
|
||||
//NSLog(@"%@ %@", NSStringFromSelector(_cmd), path);
|
||||
[self repaintAllWindows];
|
||||
}
|
||||
|
||||
|
||||
- (void)removeAllIcons
|
||||
{
|
||||
[_fileNamesCache removeAllObjects];
|
||||
|
||||
[self repaintAllWindows];
|
||||
}
|
||||
|
||||
- (void)removeIcons:(NSArray*)paths
|
||||
{
|
||||
for (NSString* path in paths)
|
||||
{
|
||||
NSString* normalizedPath = [path decomposedStringWithCanonicalMapping];
|
||||
|
||||
[_fileNamesCache removeObjectForKey:normalizedPath];
|
||||
}
|
||||
[_oldFileNamesCache removeAllObjects];
|
||||
|
||||
[self repaintAllWindows];
|
||||
}
|
||||
@@ -361,6 +327,7 @@ static ContentManager* sharedInstance = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_oldFileNamesCache removeObjectForKey:normalizedPath];
|
||||
[_fileNamesCache setObject:iconId forKey:normalizedPath];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
NSMutableArray* _requestQueue;
|
||||
NSMutableDictionary* _registeredPathes;
|
||||
NSMutableSet* _requestedPaths;
|
||||
|
||||
NSString *_shareMenuTitle;
|
||||
|
||||
@@ -34,7 +35,7 @@
|
||||
|
||||
- (BOOL)isRegisteredPath:(NSString*)path isDirectory:(BOOL)isDir;
|
||||
- (void)askOnSocket:(NSString*)path query:(NSString*)verb;
|
||||
- (NSNumber*)askForIcon:(NSString*)path isDirectory:(BOOL)isDir;
|
||||
- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir;
|
||||
- (void)menuItemClicked:(NSDictionary*)actionDictionary;
|
||||
- (void)start;
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ static RequestManager* sharedInstance = nil;
|
||||
_isConnected = NO;
|
||||
|
||||
_registeredPathes = [[NSMutableDictionary alloc] init];
|
||||
_requestedPaths = [[NSMutableSet alloc] init];
|
||||
|
||||
_shareMenuTitle = nil;
|
||||
|
||||
@@ -101,28 +102,23 @@ static RequestManager* sharedInstance = nil;
|
||||
return registered;
|
||||
}
|
||||
|
||||
- (NSNumber*)askForIcon:(NSString*)path isDirectory:(BOOL)isDir
|
||||
- (void)askForIcon:(NSString*)path isDirectory:(BOOL)isDir
|
||||
{
|
||||
NSString *verb = @"RETRIEVE_FILE_STATUS";
|
||||
NSNumber *res = [NSNumber numberWithInt:0];
|
||||
|
||||
if( [self isRegisteredPath:path isDirectory:isDir] ) {
|
||||
[_requestedPaths addObject:path];
|
||||
if( _isConnected ) {
|
||||
if(isDir) {
|
||||
verb = @"RETRIEVE_FOLDER_STATUS";
|
||||
}
|
||||
|
||||
[self askOnSocket:path query:verb];
|
||||
|
||||
NSNumber *res_minus_one = [NSNumber numberWithInt:0];
|
||||
|
||||
return res_minus_one;
|
||||
} else {
|
||||
[_requestQueue addObject:path];
|
||||
[self start]; // try again to connect
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,9 +143,13 @@ static RequestManager* sharedInstance = nil;
|
||||
path, [chunks objectAtIndex:i+1] ];
|
||||
}
|
||||
}
|
||||
[contentman setResultForPath:path result:[chunks objectAtIndex:1]];
|
||||
// The client will broadcast all changes, do not fill the cache for paths that Finder didn't ask for.
|
||||
if ([_requestedPaths containsObject:path]) {
|
||||
[contentman setResultForPath:path result:[chunks objectAtIndex:1]];
|
||||
}
|
||||
} else if( [[chunks objectAtIndex:0] isEqualToString:@"UPDATE_VIEW"] ) {
|
||||
NSString *path = [chunks objectAtIndex:1];
|
||||
[_requestedPaths removeAllObjects];
|
||||
[contentman reFetchFileNameCacheForPath:path];
|
||||
} else if( [[chunks objectAtIndex:0 ] isEqualToString:@"REGISTER_PATH"] ) {
|
||||
NSNumber *one = [NSNumber numberWithInt:1];
|
||||
@@ -198,10 +198,11 @@ static RequestManager* sharedInstance = nil;
|
||||
for( NSString *path in _requestQueue ) {
|
||||
[self askOnSocket:path query:@"RETRIEVE_FILE_STATUS"];
|
||||
}
|
||||
[_requestQueue removeAllObjects];
|
||||
}
|
||||
|
||||
ContentManager *contentman = [ContentManager sharedInstance];
|
||||
[contentman clearFileNameCacheForPath:nil];
|
||||
[contentman clearFileNameCache];
|
||||
[contentman repaintAllWindows];
|
||||
|
||||
// Read for the UPDATE_VIEW requests
|
||||
@@ -218,10 +219,11 @@ static RequestManager* sharedInstance = nil;
|
||||
// clear the registered pathes.
|
||||
[_registeredPathes release];
|
||||
_registeredPathes = [[NSMutableDictionary alloc] init];
|
||||
[_requestedPaths removeAllObjects];
|
||||
|
||||
// clear the caches in conent manager
|
||||
ContentManager *contentman = [ContentManager sharedInstance];
|
||||
[contentman clearFileNameCacheForPath:nil];
|
||||
[contentman clearFileNameCache];
|
||||
[contentman repaintAllWindows];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/bin/sh
|
||||
# osascript $HOME/owncloud.com/client/shell_integration/MacOSX/unload.scpt
|
||||
SELFPATH=`dirname $0`
|
||||
# osascript $SELFPATH/unload.scpt
|
||||
|
||||
sudo rm -rf /Library/ScriptingAdditions/SyncStateFinder.osax
|
||||
# Klaas' machine
|
||||
@@ -12,6 +13,6 @@ OSAXDIR=$HOME/Library/Developer/Xcode/DerivedData/OwnCloud-*/Build/Products/Debu
|
||||
|
||||
sudo killall Finder
|
||||
sleep 1
|
||||
osascript $HOME/owncloud.com/client/shell_integration/MacOSX/load.scpt
|
||||
osascript $HOME/owncloud.com/client/shell_integration/MacOSX/check.scpt
|
||||
osascript $SELFPATH/load.scpt
|
||||
osascript $SELFPATH/check.scpt
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ void RemotePathChecker::workerThreadLoop()
|
||||
++it;
|
||||
}
|
||||
}
|
||||
// Assume that we won't need this at this point, UNREGISTER_PATH is rare
|
||||
_oldCache.clear();
|
||||
}
|
||||
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||
} else if (StringUtil::begins_with(response, wstring(L"STATUS:")) ||
|
||||
@@ -115,13 +117,16 @@ void RemotePathChecker::workerThreadLoop()
|
||||
auto responseStatus = response.substr(statusBegin+1, statusEnd - statusBegin-1);
|
||||
auto responsePath = response.substr(statusEnd+1);
|
||||
auto state = _StrToFileState(responseStatus);
|
||||
auto erased = asked.erase(responsePath);
|
||||
bool wasAsked = asked.erase(responsePath) > 0;
|
||||
|
||||
bool changed = false;
|
||||
{ std::unique_lock<std::mutex> lock(_mutex);
|
||||
auto &it = _cache[responsePath];
|
||||
changed = (it != state);
|
||||
it = state;
|
||||
bool wasCached = _cache.find(responsePath) != _cache.end();
|
||||
if (wasAsked || wasCached) {
|
||||
auto &it = _cache[responsePath];
|
||||
changed = (it != state);
|
||||
it = state;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, responsePath.data(), NULL);
|
||||
@@ -129,20 +134,25 @@ void RemotePathChecker::workerThreadLoop()
|
||||
}
|
||||
else if (StringUtil::begins_with(response, wstring(L"UPDATE_VIEW"))) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
auto cache = _cache; // Make a copy of the cache under the mutex
|
||||
// Keep the old states to continue having something to display while the new state is
|
||||
// requested from the client, triggered by clearing _cache.
|
||||
_oldCache.insert(_cache.cbegin(), _cache.cend());
|
||||
|
||||
// Swap to make a copy of the cache under the mutex and clear the one stored.
|
||||
std::unordered_map<std::wstring, FileState> cache;
|
||||
swap(cache, _cache);
|
||||
lock.unlock();
|
||||
// Request a status for all the items in the cache.
|
||||
for (auto it = cache.begin(); it != cache.end(); ++it) {
|
||||
if (!socket.SendMsg(wstring(L"RETRIEVE_FILE_STATUS:" + it->first + L'\n').data())) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Let explorer know about the invalidated cache entries, it will re-request the ones it needs.
|
||||
for (auto it = cache.begin(); it != cache.end(); ++it) {
|
||||
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, it->first.data(), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (socket.Event() == INVALID_HANDLE_VALUE) {
|
||||
std::unique_lock<std::mutex> lock(_mutex);
|
||||
_cache.clear();
|
||||
_oldCache.clear();
|
||||
_watchedDirectories.clear();
|
||||
_connected = connected = false;
|
||||
}
|
||||
@@ -195,11 +205,17 @@ bool RemotePathChecker::IsMonitoredPath(const wchar_t* filePath, int* state)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Re-request the status while we display what we have in _oldCache
|
||||
_pending.push(filePath);
|
||||
|
||||
it = _oldCache.find(path);
|
||||
bool foundInOldCache = it != _oldCache.end();
|
||||
if (foundInOldCache)
|
||||
*state = it->second;
|
||||
|
||||
lock.unlock();
|
||||
SetEvent(_newQueries);
|
||||
return false;
|
||||
|
||||
return foundInOldCache;
|
||||
}
|
||||
|
||||
RemotePathChecker::FileState RemotePathChecker::_StrToFileState(const std::wstring &str)
|
||||
|
||||
@@ -52,6 +52,7 @@ private:
|
||||
std::queue<std::wstring> _pending;
|
||||
|
||||
std::unordered_map<std::wstring, FileState> _cache;
|
||||
std::unordered_map<std::wstring, FileState> _oldCache;
|
||||
std::vector<std::wstring> _watchedDirectories;
|
||||
bool _connected;
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "qtlocalpeer.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDataStream>
|
||||
#include <QTime>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
@@ -28,9 +28,6 @@ public:
|
||||
* @return the list of migrated folder definitions
|
||||
*/
|
||||
QStringList migrateFolderDefinitons();
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ void AccountSettings::slotAddFolder( Folder *folder )
|
||||
if( ! folder || folder->alias().isEmpty() ) return;
|
||||
|
||||
QStandardItem *item = new QStandardItem();
|
||||
folderToModelItem( item, folder, _accountState && _accountState->isConnectedOrMaintenance());
|
||||
folderToModelItem( item, folder, _accountState && _accountState->isConnectedOrTemporarilyUnavailable());
|
||||
_model->appendRow( item );
|
||||
// in order to update the enabled state of the "Sync now" button
|
||||
connect(folder, SIGNAL(syncStateChange()), this, SLOT(slotFolderSyncStateChange()), Qt::UniqueConnection);
|
||||
@@ -537,7 +537,7 @@ void AccountSettings::slotUpdateFolderState( Folder *folder )
|
||||
}
|
||||
|
||||
if( item ) {
|
||||
folderToModelItem( item, folder, _accountState->isConnectedOrMaintenance() );
|
||||
folderToModelItem( item, folder, _accountState->isConnectedOrTemporarilyUnavailable() );
|
||||
} else {
|
||||
// the dialog is not visible.
|
||||
}
|
||||
@@ -794,7 +794,7 @@ void AccountSettings::slotAccountStateChanged(int state)
|
||||
foreach (Folder *folder, folderMan->map().values()) {
|
||||
slotUpdateFolderState(folder);
|
||||
}
|
||||
if (state == AccountState::Connected || state == AccountState::ServerMaintenance) {
|
||||
if (state == AccountState::Connected || state == AccountState::ServiceUnavailable) {
|
||||
QString user;
|
||||
if (AbstractCredentials *cred = account->credentials()) {
|
||||
user = cred->user();
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
<property name="text">
|
||||
<string>Connected with <server> as <user></string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
||||
@@ -114,9 +114,10 @@ void AccountState::setState(State state)
|
||||
} else if (oldState == SignedOut && _state == Disconnected) {
|
||||
checkConnectivity();
|
||||
}
|
||||
|
||||
emit stateChanged(_state);
|
||||
}
|
||||
|
||||
// might not have changed but the underlying _connectionErrors might have
|
||||
emit stateChanged(_state);
|
||||
}
|
||||
|
||||
QString AccountState::stateString(State state)
|
||||
@@ -129,8 +130,8 @@ QString AccountState::stateString(State state)
|
||||
return QLatin1String("Disconnected");
|
||||
case Connected:
|
||||
return QLatin1String("Connected");
|
||||
case ServerMaintenance:
|
||||
return QLatin1String("ServerMaintenance");
|
||||
case ServiceUnavailable:
|
||||
return QLatin1String("ServiceUnavailable");
|
||||
case NetworkError:
|
||||
return QLatin1String("NetworkError");
|
||||
case ConfigurationError:
|
||||
@@ -158,9 +159,9 @@ bool AccountState::isConnected() const
|
||||
return _state == Connected;
|
||||
}
|
||||
|
||||
bool AccountState::isConnectedOrMaintenance() const
|
||||
bool AccountState::isConnectedOrTemporarilyUnavailable() const
|
||||
{
|
||||
return isConnected() || _state == ServerMaintenance;
|
||||
return isConnected() || _state == ServiceUnavailable;
|
||||
}
|
||||
|
||||
QuotaInfo *AccountState::quotaInfo()
|
||||
@@ -174,7 +175,12 @@ void AccountState::checkConnectivity()
|
||||
return;
|
||||
}
|
||||
|
||||
if (_connectionValidator) {
|
||||
qDebug() << "ConnectionValidator already running, ignoring";
|
||||
return;
|
||||
}
|
||||
ConnectionValidator * conValidator = new ConnectionValidator(account());
|
||||
_connectionValidator = conValidator;
|
||||
connect(conValidator, SIGNAL(connectionResult(ConnectionValidator::Status,QStringList)),
|
||||
SLOT(slotConnectionValidatorResult(ConnectionValidator::Status,QStringList)));
|
||||
if (isConnected()) {
|
||||
@@ -202,6 +208,7 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
|
||||
return;
|
||||
}
|
||||
|
||||
auto oldStatus = _connectionStatus;
|
||||
if (_connectionStatus != status) {
|
||||
qDebug() << "AccountState connection status change: "
|
||||
<< connectionStatusString(_connectionStatus) << "->"
|
||||
@@ -213,7 +220,9 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
|
||||
switch (status)
|
||||
{
|
||||
case ConnectionValidator::Connected:
|
||||
setState(Connected);
|
||||
if (oldStatus != ConnectionValidator::Connected) {
|
||||
setState(Connected);
|
||||
}
|
||||
break;
|
||||
case ConnectionValidator::Undefined:
|
||||
case ConnectionValidator::NotConfigured:
|
||||
@@ -234,8 +243,8 @@ void AccountState::slotConnectionValidatorResult(ConnectionValidator::Status sta
|
||||
case ConnectionValidator::UserCanceledCredentials:
|
||||
setState(SignedOut);
|
||||
break;
|
||||
case ConnectionValidator::ServerMaintenance:
|
||||
setState(ServerMaintenance);
|
||||
case ConnectionValidator::ServiceUnavailable:
|
||||
setState(ServiceUnavailable);
|
||||
break;
|
||||
case ConnectionValidator::Timeout:
|
||||
setState(NetworkError);
|
||||
@@ -263,6 +272,12 @@ void AccountState::slotCredentialsFetched(AbstractCredentials* credentials)
|
||||
return;
|
||||
}
|
||||
|
||||
// When new credentials become available we always want to restart the
|
||||
// connection validation, even if it's currently running.
|
||||
if (_connectionValidator) {
|
||||
delete _connectionValidator;
|
||||
}
|
||||
|
||||
checkConnectivity();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define ACCOUNTINFO_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QPointer>
|
||||
#include "utility.h"
|
||||
#include "connectionvalidator.h"
|
||||
|
||||
@@ -67,9 +68,9 @@ public:
|
||||
/// The account is successfully talking to the server.
|
||||
Connected,
|
||||
|
||||
/// The account is talking to the server, but the server is in
|
||||
/// maintenance mode.
|
||||
ServerMaintenance,
|
||||
/// There's a temporary problem with talking to the server,
|
||||
/// don't bother the user too much and try again.
|
||||
ServiceUnavailable,
|
||||
|
||||
/// Could not communicate with the server for some reason.
|
||||
/// We assume this may resolve itself over time and will try
|
||||
@@ -100,7 +101,7 @@ public:
|
||||
void setSignedOut(bool signedOut);
|
||||
|
||||
bool isConnected() const;
|
||||
bool isConnectedOrMaintenance() const;
|
||||
bool isConnectedOrTemporarilyUnavailable() const;
|
||||
|
||||
QuotaInfo *quotaInfo();
|
||||
|
||||
@@ -128,6 +129,7 @@ private:
|
||||
ConnectionStatus _connectionStatus;
|
||||
QStringList _connectionErrors;
|
||||
bool _waitingForNewCredentials;
|
||||
QPointer<ConnectionValidator> _connectionValidator;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -110,6 +110,15 @@ Application::Application(int &argc, char **argv) :
|
||||
if (isRunning())
|
||||
return;
|
||||
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) && QT_VERSION < QT_VERSION_CHECK(5, 4, 2)
|
||||
// Workaround for QTBUG-44576: Make sure a stale QSettings lock file
|
||||
// is deleted. (Introduced in Qt 5.4.0 and fixed in Qt 5.4.2)
|
||||
{
|
||||
QString lockFilePath = ConfigFile().configFile() + QLatin1String(".lock");
|
||||
QLockFile(lockFilePath).removeStaleLockFile();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_CRASHREPORTER)
|
||||
if (ConfigFile().crashReporter())
|
||||
_crashHandler.reset(new CrashReporter::Handler( QDir::tempPath(), true, CRASHREPORTER_EXECUTABLE ));
|
||||
@@ -270,7 +279,7 @@ void Application::slotAccountStateChanged(int state)
|
||||
folderMan->setSyncEnabled(true);
|
||||
folderMan->slotScheduleAllFolders();
|
||||
break;
|
||||
case AccountState::ServerMaintenance:
|
||||
case AccountState::ServiceUnavailable:
|
||||
case AccountState::SignedOut:
|
||||
case AccountState::ConfigurationError:
|
||||
case AccountState::NetworkError:
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -32,6 +32,7 @@ class SelectiveSyncTreeView;
|
||||
class ownCloudInfo;
|
||||
|
||||
class FormatWarningsWizardPage : public QWizardPage {
|
||||
Q_OBJECT
|
||||
protected:
|
||||
QString formatWarnings(const QStringList &warnings) const;
|
||||
};
|
||||
|
||||
@@ -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*)));
|
||||
|
||||
@@ -221,7 +221,7 @@ void ownCloudGui::slotComputeOverallSyncStatus()
|
||||
_tray->setToolTip(tr("Please sign in"));
|
||||
return;
|
||||
}
|
||||
if (!a->isConnectedOrMaintenance()) {
|
||||
if (!a->isConnectedOrTemporarilyUnavailable()) {
|
||||
_tray->setIcon(Theme::instance()->folderOfflineIcon(true));
|
||||
_tray->setToolTip(tr("Disconnected from server"));
|
||||
return;
|
||||
|
||||
@@ -189,6 +189,10 @@ void OwncloudSetupWizard::slotNoOwnCloudFoundAuth(QNetworkReply *reply)
|
||||
.arg(Theme::instance()->appNameGUI(),
|
||||
reply->url().toString(),
|
||||
reply->errorString()), checkDowngradeAdvised(reply));
|
||||
|
||||
// Allow the credentials dialog to pop up again for the same URL.
|
||||
// Maybe the user just clicked 'Cancel' by accident or changed his mind.
|
||||
_ocWizard->account()->resetSslCertErrorState();
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotNoOwnCloudFoundAuthTimeout(const QUrl&url)
|
||||
@@ -219,7 +223,6 @@ void OwncloudSetupWizard::testOwnCloudConnect()
|
||||
job->setProperties(QList<QByteArray>() << "getlastmodified");
|
||||
connect(job, SIGNAL(result(QVariantMap)), _ocWizard, SLOT(successfulStep()));
|
||||
connect(job, SIGNAL(finishedWithError()), this, SLOT(slotAuthError()));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotAuthNetworkError(QNetworkReply*)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
@@ -232,10 +235,11 @@ void OwncloudSetupWizard::slotAuthError()
|
||||
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 = job->reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
QUrl redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||
if (!redirectUrl.isEmpty()) {
|
||||
qDebug() << "authed request was redirected to" << redirectUrl.toString();
|
||||
|
||||
@@ -250,18 +254,37 @@ void OwncloudSetupWizard::slotAuthError()
|
||||
_ocWizard->account()->setUrl(redirectUrl);
|
||||
testOwnCloudConnect();
|
||||
return;
|
||||
} else {
|
||||
errorMsg = tr("The authenticated request to the server was redirected to "
|
||||
"'%1'. The URL is bad, the server is misconfigured.")
|
||||
.arg(redirectUrl.toString());
|
||||
}
|
||||
}
|
||||
errorMsg = tr("The authenticated request to the server was redirected to "
|
||||
"'%1'. The URL is bad, the server is misconfigured.")
|
||||
.arg(redirectUrl.toString());
|
||||
|
||||
if (errorMsg.isEmpty()) {
|
||||
// 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) {
|
||||
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());
|
||||
} else {
|
||||
errorMsg = errorMessage(reply->errorString(), reply->readAll());
|
||||
}
|
||||
|
||||
// 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->displayError(errorMsg, false);
|
||||
|
||||
_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)
|
||||
@@ -287,29 +310,6 @@ bool OwncloudSetupWizard::checkDowngradeAdvised(QNetworkReply* reply)
|
||||
return true;
|
||||
}
|
||||
|
||||
void OwncloudSetupWizard::slotAuthNetworkError(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;
|
||||
|
||||
@@ -62,7 +62,6 @@ private slots:
|
||||
void slotNoOwnCloudFoundAuthTimeout(const QUrl&url);
|
||||
|
||||
void slotConnectToOCUrl(const QString&);
|
||||
void slotAuthNetworkError(QNetworkReply*);
|
||||
void slotAuthError();
|
||||
|
||||
void slotCreateLocalAndRemoteFolders(const QString&, const QString&);
|
||||
|
||||
@@ -206,7 +206,7 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
|
||||
const QString longTimeStr = timeString(timestamp, QLocale::LongFormat);
|
||||
|
||||
columns << timeStr;
|
||||
columns << fixupFilename(item._file);
|
||||
columns << fixupFilename(item._originalFile);
|
||||
columns << folder;
|
||||
|
||||
// If the error string is set, it's prefered because it is a useful user message.
|
||||
|
||||
@@ -144,6 +144,15 @@ void ShareDialog::done( int r ) {
|
||||
QDialog::done(r);
|
||||
}
|
||||
|
||||
static int getJsonReturnCode(const QVariantMap &json, QString &message)
|
||||
{
|
||||
//TODO proper checking
|
||||
int code = json.value("ocs").toMap().value("meta").toMap().value("statuscode").toInt();
|
||||
message = json.value("ocs").toMap().value("meta").toMap().value("message").toString();
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void ShareDialog::setExpireDate(const QDate &date)
|
||||
{
|
||||
if( _public_share_id == 0 ) {
|
||||
@@ -162,16 +171,14 @@ void ShareDialog::setExpireDate(const QDate &date)
|
||||
|
||||
OcsShareJob *job = new OcsShareJob("PUT", url, _account, this);
|
||||
job->setPostParams(postParams);
|
||||
connect(job, SIGNAL(jobFinished(QString)), this, SLOT(slotExpireSet(QString)));
|
||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotExpireSet(QVariantMap)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
void ShareDialog::slotExpireSet(const QString &reply)
|
||||
void ShareDialog::slotExpireSet(const QVariantMap &reply)
|
||||
{
|
||||
QString message;
|
||||
int code = checkJsonReturnCode(reply, message);
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "Status code: " << code;
|
||||
int code = getJsonReturnCode(reply, message);
|
||||
if (code != 100) {
|
||||
displayError(code);
|
||||
}
|
||||
@@ -234,18 +241,15 @@ void ShareDialog::setPassword(const QString &password)
|
||||
}
|
||||
OcsShareJob *job = new OcsShareJob(verb, url, _account, this);
|
||||
job->setPostParams(requestParams);
|
||||
connect(job, SIGNAL(jobFinished(QString)), this, SLOT(slotPasswordSet(QString)));
|
||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotPasswordSet(QVariantMap)));
|
||||
job->start();
|
||||
_passwordJobRunning = true;
|
||||
}
|
||||
|
||||
void ShareDialog::slotPasswordSet(const QString &reply)
|
||||
void ShareDialog::slotPasswordSet(const QVariantMap &reply)
|
||||
{
|
||||
QString message;
|
||||
int code = checkJsonReturnCode(reply, message);
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "Status code: " << code;
|
||||
|
||||
int code = getJsonReturnCode(reply, message);
|
||||
if (code != 100) {
|
||||
displayError(code);
|
||||
}
|
||||
@@ -267,23 +271,20 @@ void ShareDialog::getShares()
|
||||
params.append(qMakePair(QString::fromLatin1("path"), _sharePath));
|
||||
url.setQueryItems(params);
|
||||
OcsShareJob *job = new OcsShareJob("GET", url, _account, this);
|
||||
connect(job, SIGNAL(jobFinished(QString)), this, SLOT(slotSharesFetched(QString)));
|
||||
job->addPassStatusCode(404); // don't report error if share doesn't exist yet
|
||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotSharesFetched(QVariantMap)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
void ShareDialog::slotSharesFetched(const QString &reply)
|
||||
void ShareDialog::slotSharesFetched(const QVariantMap &reply)
|
||||
{
|
||||
QString message;
|
||||
int code = checkJsonReturnCode(reply, message);
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "Status code: " << code;
|
||||
int code = getJsonReturnCode(reply, message);
|
||||
if (code != 100 && code != 404) {
|
||||
displayError(code);
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
QVariantMap json = QtJson::parse(reply, success).toMap();
|
||||
ShareDialog::_shares = json.value("ocs").toMap().value("data").toList();
|
||||
ShareDialog::_shares = reply.value("ocs").toMap().value("data").toList();
|
||||
const QString versionString = AccountManager::instance()->account()->serverVersion();
|
||||
|
||||
Q_FOREACH(auto share, ShareDialog::_shares) {
|
||||
@@ -291,6 +292,7 @@ void ShareDialog::slotSharesFetched(const QString &reply)
|
||||
|
||||
if (data.value("share_type").toInt() == SHARETYPE_PUBLIC) {
|
||||
_public_share_id = data.value("id").toULongLong();
|
||||
_ui->pushButton_copy->show();
|
||||
|
||||
_ui->widget_shareLink->show();
|
||||
_ui->checkBox_shareLink->setChecked(true);
|
||||
@@ -384,12 +386,10 @@ void ShareDialog::setShareLink( const QString& url )
|
||||
|
||||
}
|
||||
|
||||
void ShareDialog::slotDeleteShareFetched(const QString &reply)
|
||||
void ShareDialog::slotDeleteShareFetched(const QVariantMap &reply)
|
||||
{
|
||||
QString message;
|
||||
int code = checkJsonReturnCode(reply, message);
|
||||
|
||||
qDebug() << Q_FUNC_INFO << "Status code: " << code;
|
||||
int code = getJsonReturnCode(reply, message);
|
||||
if (code != 100) {
|
||||
displayError(code);
|
||||
}
|
||||
@@ -423,29 +423,31 @@ void ShareDialog::slotCheckBoxShareLinkClicked()
|
||||
postParams.append(qMakePair(QString::fromLatin1("shareType"), QString::number(SHARETYPE_PUBLIC)));
|
||||
OcsShareJob *job = new OcsShareJob("POST", url, _account, this);
|
||||
job->setPostParams(postParams);
|
||||
connect(job, SIGNAL(jobFinished(QString)), this, SLOT(slotCreateShareFetched(QString)));
|
||||
job->addPassStatusCode(403); // "password required" is not an error
|
||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotCreateShareFetched(QVariantMap)));
|
||||
job->start();
|
||||
} else {
|
||||
_pi_link->startAnimation();
|
||||
QUrl url = Account::concatUrlPath(_account->url(), QString("ocs/v1.php/apps/files_sharing/api/v1/shares/%1").arg(_public_share_id));
|
||||
OcsShareJob *job = new OcsShareJob("DELETE", url, _account, this);
|
||||
connect(job, SIGNAL(jobFinished(QString)), this, SLOT(slotDeleteShareFetched(QString)));
|
||||
connect(job, SIGNAL(jobFinished(QVariantMap)), this, SLOT(slotDeleteShareFetched(QVariantMap)));
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
void ShareDialog::slotCreateShareFetched(const QString &reply)
|
||||
void ShareDialog::slotCreateShareFetched(const QVariantMap &reply)
|
||||
{
|
||||
QString message;
|
||||
int code = checkJsonReturnCode(reply, message);
|
||||
int code = getJsonReturnCode(reply, message);
|
||||
_pi_link->stopAnimation();
|
||||
|
||||
if (code == 403) {
|
||||
// there needs to be a password
|
||||
_ui->checkBox_password->setChecked(true);
|
||||
_ui->checkBox_password->setVisible(false);
|
||||
_ui->checkBox_password->setText(tr("Public shå requires a password:"));
|
||||
_ui->checkBox_password->setEnabled(false);
|
||||
_ui->checkBox_password->setText(tr("Public shå requires a password"));
|
||||
_ui->lineEdit_password->setFocus();
|
||||
_ui->pushButton_copy->hide();
|
||||
_ui->widget_shareLink->show();
|
||||
|
||||
slotCheckBoxPasswordClicked();
|
||||
@@ -455,9 +457,8 @@ void ShareDialog::slotCreateShareFetched(const QString &reply)
|
||||
return;
|
||||
}
|
||||
|
||||
bool success;
|
||||
QVariantMap json = QtJson::parse(reply, success).toMap();
|
||||
_public_share_id = json.value("ocs").toMap().values("data")[0].toMap().value("id").toULongLong();
|
||||
_public_share_id = reply.value("ocs").toMap().values("data")[0].toMap().value("id").toULongLong();
|
||||
_ui->pushButton_copy->show();
|
||||
getShares();
|
||||
}
|
||||
|
||||
@@ -466,7 +467,7 @@ void ShareDialog::slotCheckBoxPasswordClicked()
|
||||
if (_ui->checkBox_password->checkState() == Qt::Checked) {
|
||||
_ui->lineEdit_password->show();
|
||||
_ui->pushButton_setPassword->show();
|
||||
_ui->lineEdit_password->setPlaceholderText(tr("Choose a password for the public link"));
|
||||
_ui->lineEdit_password->setPlaceholderText(tr("Password"));
|
||||
_ui->lineEdit_password->setFocus();
|
||||
} else {
|
||||
ShareDialog::setPassword(QString());
|
||||
@@ -500,22 +501,6 @@ void ShareDialog::slotPushButtonCopyLinkPressed()
|
||||
clipboard->setText(_shareUrl);
|
||||
}
|
||||
|
||||
int ShareDialog::checkJsonReturnCode(const QString &reply, QString &message)
|
||||
{
|
||||
bool success;
|
||||
QVariantMap json = QtJson::parse(reply, success).toMap();
|
||||
|
||||
if (!success) {
|
||||
qDebug() << Q_FUNC_INFO << "Failed to parse reply";
|
||||
}
|
||||
|
||||
//TODO proper checking
|
||||
int code = json.value("ocs").toMap().value("meta").toMap().value("statuscode").toInt();
|
||||
message = json.value("ocs").toMap().value("meta").toMap().value("message").toString();
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void ShareDialog::setShareCheckBoxTitle(bool haveShares)
|
||||
{
|
||||
const QString noSharesTitle(tr("&Share link"));
|
||||
@@ -672,6 +657,7 @@ OcsShareJob::OcsShareJob(const QByteArray &verb, const QUrl &url, AccountPtr acc
|
||||
_verb(verb),
|
||||
_url(url)
|
||||
{
|
||||
_passStatusCodes.append(100);
|
||||
setIgnoreCredentialFailure(true);
|
||||
}
|
||||
|
||||
@@ -680,6 +666,11 @@ void OcsShareJob::setPostParams(const QList<QPair<QString, QString> >& postParam
|
||||
_postParams = postParams;
|
||||
}
|
||||
|
||||
void OcsShareJob::addPassStatusCode(int code)
|
||||
{
|
||||
_passStatusCodes.append(code);
|
||||
}
|
||||
|
||||
void OcsShareJob::start()
|
||||
{
|
||||
QNetworkRequest req;
|
||||
@@ -711,7 +702,23 @@ void OcsShareJob::start()
|
||||
|
||||
bool OcsShareJob::finished()
|
||||
{
|
||||
emit jobFinished(reply()->readAll());
|
||||
const QString replyData = reply()->readAll();
|
||||
|
||||
bool success;
|
||||
QVariantMap json = QtJson::parse(replyData, success).toMap();
|
||||
if (!success) {
|
||||
qDebug() << "Could not parse reply to" << _verb << _url << _postParams
|
||||
<< ":" << replyData;
|
||||
}
|
||||
|
||||
QString message;
|
||||
const int statusCode = getJsonReturnCode(json, message);
|
||||
if (!_passStatusCodes.contains(statusCode)) {
|
||||
qDebug() << "Reply to" << _verb << _url << _postParams
|
||||
<< "has unexpected status code:" << statusCode << replyData;
|
||||
}
|
||||
|
||||
emit jobFinished(json);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,17 +29,19 @@ public:
|
||||
explicit OcsShareJob(const QByteArray& verb, const QUrl& url, AccountPtr account, QObject* parent = 0);
|
||||
|
||||
void setPostParams(const QList<QPair<QString, QString> >& postParams);
|
||||
void addPassStatusCode(int code);
|
||||
|
||||
public slots:
|
||||
void start() Q_DECL_OVERRIDE;
|
||||
signals:
|
||||
void jobFinished(QString reply);
|
||||
void jobFinished(QVariantMap reply);
|
||||
private slots:
|
||||
virtual bool finished() Q_DECL_OVERRIDE;
|
||||
private:
|
||||
QByteArray _verb;
|
||||
QUrl _url;
|
||||
QList<QPair<QString, QString> > _postParams;
|
||||
QVector<int> _passStatusCodes;
|
||||
};
|
||||
|
||||
|
||||
@@ -49,7 +51,6 @@ class ShareDialog;
|
||||
|
||||
class AbstractCredentials;
|
||||
class QuotaInfo;
|
||||
class MirallAccessManager;
|
||||
class SyncResult;
|
||||
|
||||
class ShareDialog : public QDialog
|
||||
@@ -63,11 +64,11 @@ public:
|
||||
void getShares();
|
||||
|
||||
private slots:
|
||||
void slotSharesFetched(const QString &reply);
|
||||
void slotCreateShareFetched(const QString &reply);
|
||||
void slotDeleteShareFetched(const QString &reply);
|
||||
void slotPasswordSet(const QString &reply);
|
||||
void slotExpireSet(const QString &reply);
|
||||
void slotSharesFetched(const QVariantMap &reply);
|
||||
void slotCreateShareFetched(const QVariantMap &reply);
|
||||
void slotDeleteShareFetched(const QVariantMap &reply);
|
||||
void slotPasswordSet(const QVariantMap &reply);
|
||||
void slotExpireSet(const QVariantMap &reply);
|
||||
void slotCalendarClicked(const QDate &date);
|
||||
void slotCheckBoxShareLinkClicked();
|
||||
void slotCheckBoxPasswordClicked();
|
||||
@@ -102,7 +103,6 @@ private:
|
||||
qulonglong _public_share_id;
|
||||
void setPassword(const QString &password);
|
||||
void setExpireDate(const QDate &date);
|
||||
int checkJsonReturnCode(const QString &reply, QString &message);
|
||||
|
||||
QProgressIndicator *_pi_link;
|
||||
QProgressIndicator *_pi_password;
|
||||
|
||||
@@ -82,8 +82,8 @@ private:
|
||||
Q_INVOKABLE void command_SHARE_MENU_TITLE(const QString& argument, QLocalSocket* socket);
|
||||
QString buildRegisterPathMessage(const QString& path);
|
||||
|
||||
QLocalServer _localServer;
|
||||
QList<QLocalSocket*> _listeners;
|
||||
QLocalServer _localServer;
|
||||
c_strlist_t *_excludes;
|
||||
QHash<Folder*, SqlQuery*> _dbQueries;
|
||||
QHash<Folder*, SqlDatabase*> _openDbs;
|
||||
|
||||
@@ -111,15 +111,33 @@ SparkleUpdater::~SparkleUpdater()
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
||||
bool autoUpdaterAllowed()
|
||||
{
|
||||
// See https://github.com/owncloud/client/issues/2931
|
||||
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
|
||||
NSString *expectedPath = [NSString stringWithFormat:@"/Applications/%@", [bundlePath lastPathComponent]];
|
||||
if ([expectedPath isEqualTo:bundlePath]) {
|
||||
return true;
|
||||
}
|
||||
qWarning() << "ERROR: We are not in /Applications, won't check for update!";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SparkleUpdater::checkForUpdate()
|
||||
{
|
||||
[d->updater checkForUpdates: NSApp];
|
||||
if (autoUpdaterAllowed()) {
|
||||
[d->updater checkForUpdates: NSApp];
|
||||
}
|
||||
}
|
||||
|
||||
void SparkleUpdater::backgroundCheckForUpdate()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "launching background check";
|
||||
[d->updater checkForUpdatesInBackground];
|
||||
if (autoUpdaterAllowed()) {
|
||||
[d->updater checkForUpdatesInBackground];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -57,6 +57,9 @@
|
||||
<property name="text">
|
||||
<string>Error Label</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
|
||||
@@ -61,6 +61,7 @@ OwncloudSetupPage::OwncloudSetupPage(QWidget *parent)
|
||||
|
||||
setupCustomization();
|
||||
|
||||
slotUrlChanged(QLatin1String("")); // don't jitter UI
|
||||
connect(_ui.leUrl, SIGNAL(textChanged(QString)), SLOT(slotUrlChanged(QString)));
|
||||
connect(_ui.leUrl, SIGNAL(editingFinished()), SLOT(slotUrlEditFinished()));
|
||||
|
||||
@@ -115,7 +116,7 @@ void OwncloudSetupPage::slotUrlChanged(const QString& url)
|
||||
_ui.leUrl->setText(newUrl);
|
||||
}
|
||||
|
||||
if (url.startsWith(QLatin1String("http://"))) {
|
||||
if (!url.startsWith(QLatin1String("https://"))) {
|
||||
_ui.urlLabel->setPixmap(QPixmap(Theme::hidpiFileName(":/client/resources/lock-http.png")));
|
||||
_ui.urlLabel->setToolTip(tr("This url is NOT secure as it is not encrypted.\n"
|
||||
"It is not advisable to use it."));
|
||||
|
||||
@@ -61,6 +61,7 @@ set(libsync_SRCS
|
||||
theme.cpp
|
||||
utility.cpp
|
||||
ownsql.cpp
|
||||
transmissionchecksumvalidator.cpp
|
||||
creds/dummycredentials.cpp
|
||||
creds/abstractcredentials.cpp
|
||||
creds/credentialsfactory.cpp
|
||||
@@ -141,6 +142,11 @@ if(NEON_FOUND)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
list(APPEND libsync_LINK_TARGETS ${ZLIB_LIBRARIES})
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
endif(ZLIB_FOUND)
|
||||
|
||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
||||
GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
||||
BASE_NAME ${synclib_NAME}
|
||||
@@ -151,9 +157,9 @@ GENERATE_EXPORT_HEADER( ${synclib_NAME}
|
||||
|
||||
|
||||
if(TOKEN_AUTH_ONLY)
|
||||
qt5_use_modules(${synclib_NAME} Network)
|
||||
qt5_use_modules(${synclib_NAME} Network Concurrent)
|
||||
else()
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network WebKitWidgets)
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network WebKitWidgets Concurrent)
|
||||
endif()
|
||||
|
||||
set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
|
||||
@@ -321,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);
|
||||
}
|
||||
|
||||
@@ -333,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);
|
||||
}
|
||||
|
||||
@@ -396,6 +400,11 @@ void Account::addApprovedCerts(const QList<QSslCertificate> certs)
|
||||
_approvedCerts += certs;
|
||||
}
|
||||
|
||||
void Account::resetSslCertErrorState()
|
||||
{
|
||||
_treatSslErrorsAsFailure = false;
|
||||
}
|
||||
|
||||
void Account::setSslErrorHandler(AbstractSslErrorHandler *handler)
|
||||
{
|
||||
_sslErrorHandler.reset(handler);
|
||||
@@ -494,6 +503,11 @@ void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
_treatSslErrorsAsFailure = true;
|
||||
// if during normal operation, a new certificate was MITM'ed, and the user does not
|
||||
// ACK it, the running request must be aborted and the QNAM must be reset, to not
|
||||
// treat the new cert as granted. See bug #3283
|
||||
reply->abort();
|
||||
resetNetworkAccessManager();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,11 @@ public:
|
||||
void setApprovedCerts(const QList<QSslCertificate> certs);
|
||||
void addApprovedCerts(const QList<QSslCertificate> certs);
|
||||
|
||||
// Usually when a user explicitly rejects a certificate we don't
|
||||
// ask again. After this call, a dialog will again be shown when
|
||||
// the next unknown certificate is encountered.
|
||||
void resetSslCertErrorState();
|
||||
|
||||
// pluggable handler
|
||||
void setSslErrorHandler(AbstractSslErrorHandler *handler);
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ BandwidthManager::~BandwidthManager()
|
||||
|
||||
void BandwidthManager::registerUploadDevice(UploadDevice *p)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << p;
|
||||
//qDebug() << Q_FUNC_INFO << p;
|
||||
_absoluteUploadDeviceList.append(p);
|
||||
_relativeUploadDeviceList.append(p);
|
||||
QObject::connect(p, SIGNAL(destroyed(QObject*)), this, SLOT(unregisterUploadDevice(QObject*)));
|
||||
@@ -117,7 +117,7 @@ void BandwidthManager::unregisterUploadDevice(QObject *o)
|
||||
|
||||
void BandwidthManager::unregisterUploadDevice(UploadDevice* p)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << p;
|
||||
//qDebug() << Q_FUNC_INFO << p;
|
||||
_absoluteUploadDeviceList.removeAll(p);
|
||||
_relativeUploadDeviceList.removeAll(p);
|
||||
if (p == _relativeLimitCurrentMeasuredDevice) {
|
||||
@@ -128,7 +128,7 @@ void BandwidthManager::unregisterUploadDevice(UploadDevice* p)
|
||||
|
||||
void BandwidthManager::registerDownloadJob(GETFileJob* j)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << j;
|
||||
//qDebug() << Q_FUNC_INFO << j;
|
||||
_downloadJobList.append(j);
|
||||
QObject::connect(j, SIGNAL(destroyed(QObject*)), this, SLOT(unregisterDownloadJob(QObject*)));
|
||||
|
||||
@@ -176,19 +176,19 @@ void BandwidthManager::relativeUploadMeasuringTimerExpired()
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _relativeUploadDeviceList.count() << "Starting Delay";
|
||||
// qDebug() << Q_FUNC_INFO << _relativeUploadDeviceList.count() << "Starting Delay";
|
||||
|
||||
qint64 relativeLimitProgressMeasured = (_relativeLimitCurrentMeasuredDevice->_readWithProgress
|
||||
+ _relativeLimitCurrentMeasuredDevice->_read) / 2;
|
||||
qint64 relativeLimitProgressDifference = relativeLimitProgressMeasured - _relativeUploadLimitProgressAtMeasuringRestart;
|
||||
qDebug() << Q_FUNC_INFO << _relativeUploadLimitProgressAtMeasuringRestart
|
||||
<< relativeLimitProgressMeasured << relativeLimitProgressDifference;
|
||||
// qDebug() << Q_FUNC_INFO << _relativeUploadLimitProgressAtMeasuringRestart
|
||||
// << relativeLimitProgressMeasured << relativeLimitProgressDifference;
|
||||
|
||||
qint64 speedkBPerSec = (relativeLimitProgressDifference / relativeLimitMeasuringTimerIntervalMsec*1000.0) / 1024.0;
|
||||
qDebug() << Q_FUNC_INFO << relativeLimitProgressDifference/1024 <<"kB =>" << speedkBPerSec << "kB/sec on full speed ("
|
||||
<< _relativeLimitCurrentMeasuredDevice->_readWithProgress << _relativeLimitCurrentMeasuredDevice->_read
|
||||
<< qAbs(_relativeLimitCurrentMeasuredDevice->_readWithProgress
|
||||
- _relativeLimitCurrentMeasuredDevice->_read) << ")";
|
||||
// qint64 speedkBPerSec = (relativeLimitProgressDifference / relativeLimitMeasuringTimerIntervalMsec*1000.0) / 1024.0;
|
||||
// qDebug() << Q_FUNC_INFO << relativeLimitProgressDifference/1024 <<"kB =>" << speedkBPerSec << "kB/sec on full speed ("
|
||||
// << _relativeLimitCurrentMeasuredDevice->_readWithProgress << _relativeLimitCurrentMeasuredDevice->_read
|
||||
// << qAbs(_relativeLimitCurrentMeasuredDevice->_readWithProgress
|
||||
// - _relativeLimitCurrentMeasuredDevice->_read) << ")";
|
||||
|
||||
qint64 uploadLimitPercent = -_currentUploadLimit;
|
||||
// don't use too extreme values
|
||||
@@ -197,9 +197,9 @@ void BandwidthManager::relativeUploadMeasuringTimerExpired()
|
||||
qint64 wholeTimeMsec = (100.0 / uploadLimitPercent) * relativeLimitMeasuringTimerIntervalMsec;
|
||||
qint64 waitTimeMsec = wholeTimeMsec - relativeLimitMeasuringTimerIntervalMsec;
|
||||
qint64 realWaitTimeMsec = waitTimeMsec + wholeTimeMsec;
|
||||
qDebug() << Q_FUNC_INFO << waitTimeMsec << " - "<< realWaitTimeMsec <<
|
||||
" msec for " << uploadLimitPercent << "%";
|
||||
qDebug() << Q_FUNC_INFO << "XXXX" << uploadLimitPercent << relativeLimitMeasuringTimerIntervalMsec;
|
||||
// qDebug() << Q_FUNC_INFO << waitTimeMsec << " - "<< realWaitTimeMsec <<
|
||||
// " msec for " << uploadLimitPercent << "%";
|
||||
// qDebug() << Q_FUNC_INFO << "XXXX" << uploadLimitPercent << relativeLimitMeasuringTimerIntervalMsec;
|
||||
|
||||
// We want to wait twice as long since we want to give all
|
||||
// devices the same quota we used now since we don't want
|
||||
@@ -209,12 +209,12 @@ void BandwidthManager::relativeUploadMeasuringTimerExpired()
|
||||
|
||||
int deviceCount = _relativeUploadDeviceList.count();
|
||||
qint64 quotaPerDevice = relativeLimitProgressDifference * (uploadLimitPercent / 100.0) / deviceCount + 1.0;
|
||||
qDebug() << Q_FUNC_INFO << "YYYY" << relativeLimitProgressDifference << uploadLimitPercent << deviceCount;
|
||||
// qDebug() << Q_FUNC_INFO << "YYYY" << relativeLimitProgressDifference << uploadLimitPercent << deviceCount;
|
||||
Q_FOREACH(UploadDevice *ud, _relativeUploadDeviceList) {
|
||||
ud->setBandwidthLimited(true);
|
||||
ud->setChoked(false);
|
||||
ud->giveBandwidthQuota(quotaPerDevice);
|
||||
qDebug() << Q_FUNC_INFO << "Gave" << quotaPerDevice/1024.0 << "kB to" << ud;
|
||||
// qDebug() << Q_FUNC_INFO << "Gave" << quotaPerDevice/1024.0 << "kB to" << ud;
|
||||
}
|
||||
_relativeLimitCurrentMeasuredDevice = 0;
|
||||
}
|
||||
@@ -232,7 +232,7 @@ void BandwidthManager::relativeUploadDelayTimerExpired()
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _relativeUploadDeviceList.count() << "Starting measuring";
|
||||
// qDebug() << Q_FUNC_INFO << _relativeUploadDeviceList.count() << "Starting measuring";
|
||||
|
||||
// Take first device and then append it again (= we round robin all devices)
|
||||
_relativeLimitCurrentMeasuredDevice = _relativeUploadDeviceList.takeFirst();
|
||||
@@ -270,16 +270,16 @@ void BandwidthManager::relativeDownloadMeasuringTimerExpired()
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _downloadJobList.count() << "Starting Delay";
|
||||
// qDebug() << Q_FUNC_INFO << _downloadJobList.count() << "Starting Delay";
|
||||
|
||||
qint64 relativeLimitProgressMeasured = _relativeLimitCurrentMeasuredJob->currentDownloadPosition();
|
||||
qint64 relativeLimitProgressDifference = relativeLimitProgressMeasured - _relativeDownloadLimitProgressAtMeasuringRestart;
|
||||
qDebug() << Q_FUNC_INFO << _relativeDownloadLimitProgressAtMeasuringRestart
|
||||
<< relativeLimitProgressMeasured << relativeLimitProgressDifference;
|
||||
|
||||
qint64 speedkBPerSec = (relativeLimitProgressDifference / relativeLimitMeasuringTimerIntervalMsec*1000.0) / 1024.0;
|
||||
qDebug() << Q_FUNC_INFO << relativeLimitProgressDifference/1024 <<"kB =>" << speedkBPerSec << "kB/sec on full speed ("
|
||||
<< _relativeLimitCurrentMeasuredJob->currentDownloadPosition() ;
|
||||
// qint64 speedkBPerSec = (relativeLimitProgressDifference / relativeLimitMeasuringTimerIntervalMsec*1000.0) / 1024.0;
|
||||
// qDebug() << Q_FUNC_INFO << relativeLimitProgressDifference/1024 <<"kB =>" << speedkBPerSec << "kB/sec on full speed ("
|
||||
// << _relativeLimitCurrentMeasuredJob->currentDownloadPosition() ;
|
||||
|
||||
qint64 downloadLimitPercent = -_currentDownloadLimit;
|
||||
// don't use too extreme values
|
||||
@@ -288,9 +288,9 @@ void BandwidthManager::relativeDownloadMeasuringTimerExpired()
|
||||
qint64 wholeTimeMsec = (100.0 / downloadLimitPercent) * relativeLimitMeasuringTimerIntervalMsec;
|
||||
qint64 waitTimeMsec = wholeTimeMsec - relativeLimitMeasuringTimerIntervalMsec;
|
||||
qint64 realWaitTimeMsec = waitTimeMsec + wholeTimeMsec;
|
||||
qDebug() << Q_FUNC_INFO << waitTimeMsec << " - "<< realWaitTimeMsec <<
|
||||
" msec for " << downloadLimitPercent << "%";
|
||||
qDebug() << Q_FUNC_INFO << "XXXX" << downloadLimitPercent << relativeLimitMeasuringTimerIntervalMsec;
|
||||
// qDebug() << Q_FUNC_INFO << waitTimeMsec << " - "<< realWaitTimeMsec <<
|
||||
// " msec for " << downloadLimitPercent << "%";
|
||||
// qDebug() << Q_FUNC_INFO << "XXXX" << downloadLimitPercent << relativeLimitMeasuringTimerIntervalMsec;
|
||||
|
||||
// We want to wait twice as long since we want to give all
|
||||
// devices the same quota we used now since we don't want
|
||||
@@ -305,12 +305,12 @@ void BandwidthManager::relativeDownloadMeasuringTimerExpired()
|
||||
// quota -= 20*1024;
|
||||
// }
|
||||
qint64 quotaPerJob = quota / jobCount + 1.0;
|
||||
qDebug() << Q_FUNC_INFO << "YYYY" << relativeLimitProgressDifference << downloadLimitPercent << jobCount;
|
||||
// qDebug() << Q_FUNC_INFO << "YYYY" << relativeLimitProgressDifference << downloadLimitPercent << jobCount;
|
||||
Q_FOREACH(GETFileJob *gfj, _downloadJobList) {
|
||||
gfj->setBandwidthLimited(true);
|
||||
gfj->setChoked(false);
|
||||
gfj->giveBandwidthQuota(quotaPerJob);
|
||||
qDebug() << Q_FUNC_INFO << "Gave" << quotaPerJob/1024.0 << "kB to" << gfj;
|
||||
// qDebug() << Q_FUNC_INFO << "Gave" << quotaPerJob/1024.0 << "kB to" << gfj;
|
||||
}
|
||||
_relativeLimitCurrentMeasuredDevice = 0;
|
||||
}
|
||||
@@ -329,7 +329,7 @@ void BandwidthManager::relativeDownloadDelayTimerExpired()
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << Q_FUNC_INFO << _downloadJobList.count() << "Starting measuring";
|
||||
// qDebug() << Q_FUNC_INFO << _downloadJobList.count() << "Starting measuring";
|
||||
|
||||
// Take first device and then append it again (= we round robin all devices)
|
||||
_relativeLimitCurrentMeasuredJob = _downloadJobList.takeFirst();
|
||||
@@ -393,18 +393,18 @@ void BandwidthManager::absoluteLimitTimerExpired()
|
||||
{
|
||||
if (usingAbsoluteUploadLimit() && _absoluteUploadDeviceList.count() > 0) {
|
||||
qint64 quotaPerDevice = _currentUploadLimit / qMax(1, _absoluteUploadDeviceList.count());
|
||||
qDebug() << Q_FUNC_INFO << quotaPerDevice << _absoluteUploadDeviceList.count() << _currentUploadLimit;
|
||||
// qDebug() << Q_FUNC_INFO << quotaPerDevice << _absoluteUploadDeviceList.count() << _currentUploadLimit;
|
||||
Q_FOREACH(UploadDevice *device, _absoluteUploadDeviceList) {
|
||||
device->giveBandwidthQuota(quotaPerDevice);
|
||||
qDebug() << Q_FUNC_INFO << "Gave " << quotaPerDevice/1024.0 << " kB to" << device;
|
||||
// qDebug() << Q_FUNC_INFO << "Gave " << quotaPerDevice/1024.0 << " kB to" << device;
|
||||
}
|
||||
}
|
||||
if (usingAbsoluteDownloadLimit() && _downloadJobList.count() > 0) {
|
||||
qint64 quotaPerJob = _currentDownloadLimit / qMax(1, _downloadJobList.count());
|
||||
qDebug() << Q_FUNC_INFO << quotaPerJob << _downloadJobList.count() << _currentDownloadLimit;
|
||||
// qDebug() << Q_FUNC_INFO << quotaPerJob << _downloadJobList.count() << _currentDownloadLimit;
|
||||
Q_FOREACH(GETFileJob *j, _downloadJobList) {
|
||||
j->giveBandwidthQuota(quotaPerJob);
|
||||
qDebug() << Q_FUNC_INFO << "Gave " << quotaPerJob/1024.0 << " kB to" << j;
|
||||
// qDebug() << Q_FUNC_INFO << "Gave " << quotaPerJob/1024.0 << " kB to" << j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +163,7 @@ SystemProxyRunnable::SystemProxyRunnable(const QUrl &url) : QObject(), QRunnable
|
||||
void SystemProxyRunnable::run()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << "Starting system proxy lookup";
|
||||
qRegisterMetaType<QNetworkProxy>("QNetworkProxy");
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::systemProxyForQuery(QNetworkProxyQuery(_url));
|
||||
|
||||
if (proxies.isEmpty()) {
|
||||
|
||||
@@ -49,6 +49,7 @@ static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications"
|
||||
static const char skipUpdateCheckC[] = "skipUpdateCheck";
|
||||
static const char geometryC[] = "geometry";
|
||||
static const char timeoutC[] = "timeout";
|
||||
static const char transmissionChecksumC[] = "transmissionChecksum";
|
||||
|
||||
static const char proxyHostC[] = "Proxy/host";
|
||||
static const char proxyTypeC[] = "Proxy/type";
|
||||
@@ -118,6 +119,20 @@ int ConfigFile::timeout() const
|
||||
return settings.value(QLatin1String(timeoutC), 300).toInt(); // default to 5 min
|
||||
}
|
||||
|
||||
QString ConfigFile::transmissionChecksum() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
||||
QString checksum = settings.value(QLatin1String(transmissionChecksumC), QString()).toString();
|
||||
|
||||
if( checksum.isEmpty() ) {
|
||||
// if the config file setting is empty, maybe the Branding requires it.
|
||||
checksum = Theme::instance()->transmissionChecksum();
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
void ConfigFile::setOptionalDesktopNotifications(bool show)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
||||
@@ -103,6 +103,12 @@ public:
|
||||
|
||||
int timeout() const;
|
||||
|
||||
// send a checksum as a header along with the transmission or not.
|
||||
// possible values:
|
||||
// empty: no checksum calculated or expected.
|
||||
// or "Adler32", "MD5", "SHA1"
|
||||
QString transmissionChecksum() const;
|
||||
|
||||
void saveGeometry(QWidget *w);
|
||||
void restoreGeometry(QWidget *w);
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ QString ConnectionValidator::statusString( Status stat )
|
||||
return QLatin1String("Status not found");
|
||||
case UserCanceledCredentials:
|
||||
return QLatin1String("User canceled credentials");
|
||||
case ServerMaintenance:
|
||||
return QLatin1String("Server in maintenance mode");
|
||||
case ServiceUnavailable:
|
||||
return QLatin1String("Service unavailable");
|
||||
case Timeout:
|
||||
return QLatin1String("Timeout");
|
||||
}
|
||||
@@ -187,18 +187,13 @@ void ConnectionValidator::slotAuthFailed(QNetworkReply *reply)
|
||||
stat = CredentialsWrong;
|
||||
|
||||
} else if( reply->error() != QNetworkReply::NoError ) {
|
||||
_errors << reply->errorString();
|
||||
_errors << errorMessage(reply->errorString(), reply->readAll());
|
||||
|
||||
const int httpStatus =
|
||||
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if ( httpStatus == 503 ) {
|
||||
// Is this a maintenance mode reply from the server
|
||||
// or a regular 503 from somewhere else?
|
||||
QByteArray body = reply->readAll();
|
||||
if ( body.contains("Sabre\\DAV\\Exception\\ServiceUnavailable") ) {
|
||||
_errors.clear();
|
||||
stat = ServerMaintenance;
|
||||
}
|
||||
_errors.clear();
|
||||
stat = ServiceUnavailable;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ public:
|
||||
CredentialsWrong,
|
||||
StatusNotFound,
|
||||
UserCanceledCredentials,
|
||||
ServerMaintenance,
|
||||
ServiceUnavailable,
|
||||
// actually also used for other errors on the authed request
|
||||
Timeout
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <QFile>
|
||||
#include <QDateTime>
|
||||
#include <QNetworkCookie>
|
||||
#include <QDataStream>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
|
||||
@@ -75,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 */
|
||||
@@ -316,7 +306,7 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r)
|
||||
// Default keep at EIO, see above
|
||||
}
|
||||
|
||||
emit finishedWithError(errnoCode, msg);
|
||||
emit finishedWithError(errnoCode == 0 ? EIO : errnoCode, msg);
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
#include "utility.h"
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#ifdef ZLIB_FOUND
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <qabstractfileengine.h>
|
||||
@@ -155,7 +161,50 @@ bool FileSystem::rename(const QString &originFileName,
|
||||
return success;
|
||||
}
|
||||
|
||||
bool FileSystem::renameReplace(const QString& originFileName, const QString& destinationFileName, QString* errorString)
|
||||
bool FileSystem::fileChanged(const QString& fileName,
|
||||
qint64 previousSize,
|
||||
time_t previousMtime)
|
||||
{
|
||||
return getSize(fileName) != previousSize
|
||||
|| getModTime(fileName) != previousMtime;
|
||||
}
|
||||
|
||||
bool FileSystem::verifyFileUnchanged(const QString& fileName,
|
||||
qint64 previousSize,
|
||||
time_t previousMtime)
|
||||
{
|
||||
const qint64 actualSize = getSize(fileName);
|
||||
const time_t actualMtime = getModTime(fileName);
|
||||
if (actualSize != previousSize || actualMtime != previousMtime) {
|
||||
qDebug() << "File" << fileName << "has changed:"
|
||||
<< "size: " << previousSize << "<->" << actualSize
|
||||
<< ", mtime: " << previousMtime << "<->" << actualMtime;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileSystem::renameReplace(const QString& originFileName,
|
||||
const QString& destinationFileName,
|
||||
qint64 destinationSize,
|
||||
time_t destinationMtime,
|
||||
QString* errorString)
|
||||
{
|
||||
if (fileExists(destinationFileName)
|
||||
&& fileChanged(destinationFileName, destinationSize, destinationMtime)) {
|
||||
if (errorString) {
|
||||
*errorString = qApp->translate("FileSystem",
|
||||
"The destination file has an unexpected size or modification time");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return uncheckedRenameReplace(originFileName, destinationFileName, errorString);
|
||||
}
|
||||
|
||||
bool FileSystem::uncheckedRenameReplace(const QString& originFileName,
|
||||
const QString& destinationFileName,
|
||||
QString* errorString)
|
||||
{
|
||||
#ifndef Q_OS_WIN
|
||||
bool success;
|
||||
@@ -350,4 +399,58 @@ QString FileSystem::fileSystemForPath(const QString & path)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define BUFSIZE 1024*1024*10
|
||||
|
||||
static QByteArray readToCrypto( const QString& filename, QCryptographicHash::Algorithm algo )
|
||||
{
|
||||
const qint64 bufSize = BUFSIZE;
|
||||
QByteArray buf(bufSize,0);
|
||||
QByteArray arr;
|
||||
QCryptographicHash crypto( algo );
|
||||
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
qint64 size;
|
||||
while (!file.atEnd()) {
|
||||
size = file.read( buf.data(), bufSize );
|
||||
if( size > 0 ) {
|
||||
crypto.addData(buf.data(), size);
|
||||
}
|
||||
}
|
||||
arr = crypto.result().toHex();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
QByteArray FileSystem::calcMd5( const QString& filename )
|
||||
{
|
||||
return readToCrypto( filename, QCryptographicHash::Md5 );
|
||||
}
|
||||
|
||||
QByteArray FileSystem::calcSha1( const QString& filename )
|
||||
{
|
||||
return readToCrypto( filename, QCryptographicHash::Sha1 );
|
||||
}
|
||||
|
||||
#ifdef ZLIB_FOUND
|
||||
QByteArray FileSystem::calcAdler32( const QString& filename )
|
||||
{
|
||||
unsigned int adler = adler32(0L, Z_NULL, 0);
|
||||
const qint64 bufSize = BUFSIZE;
|
||||
QByteArray buf(bufSize, 0);
|
||||
|
||||
QFile file(filename);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
qint64 size;
|
||||
while (!file.atEnd()) {
|
||||
size = file.read(buf.data(), bufSize);
|
||||
if( size > 0 )
|
||||
adler = adler32(adler, (const Bytef*) buf.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
return QByteArray::number( adler, 16 );
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <QString>
|
||||
#include <ctime>
|
||||
#include <QCryptographicHash>
|
||||
|
||||
#include <owncloudlib.h>
|
||||
|
||||
@@ -67,13 +70,44 @@ bool OWNCLOUDSYNC_EXPORT fileExists(const QString& filename);
|
||||
bool OWNCLOUDSYNC_EXPORT rename(const QString& originFileName,
|
||||
const QString& destinationFileName,
|
||||
QString* errorString = NULL);
|
||||
|
||||
/**
|
||||
* Rename the file \a originFileName to \a destinationFileName, and overwrite the destination if it
|
||||
* already exists
|
||||
* Returns true if the file's mtime or size are not what is expected.
|
||||
* Nonexisting files are covered through mtime: they have an mtime of -1.
|
||||
*/
|
||||
bool renameReplace(const QString &originFileName, const QString &destinationFileName,
|
||||
bool fileChanged(const QString& fileName,
|
||||
qint64 previousSize,
|
||||
time_t previousMtime);
|
||||
|
||||
/**
|
||||
* Like !fileChanged() but with verbose logging if the file *did* change.
|
||||
*/
|
||||
bool verifyFileUnchanged(const QString& fileName,
|
||||
qint64 previousSize,
|
||||
time_t previousMtime);
|
||||
|
||||
/**
|
||||
* Rename the file \a originFileName to \a destinationFileName, and
|
||||
* overwrite the destination if it already exists - as long as the
|
||||
* destination file has the expected \a destinationSize and
|
||||
* \a destinationMtime.
|
||||
* If the destination file does not exist, the given size and mtime are
|
||||
* ignored.
|
||||
*/
|
||||
bool renameReplace(const QString &originFileName,
|
||||
const QString &destinationFileName,
|
||||
qint64 destinationSize,
|
||||
time_t destinationMtime,
|
||||
QString *errorString);
|
||||
|
||||
/**
|
||||
* Rename the file \a originFileName to \a destinationFileName, and
|
||||
* overwrite the destination if it already exists - without extra checks.
|
||||
*/
|
||||
bool uncheckedRenameReplace(const QString &originFileName,
|
||||
const QString &destinationFileName,
|
||||
QString *errorString);
|
||||
|
||||
/**
|
||||
* Replacement for QFile::open(ReadOnly) followed by a seek().
|
||||
* This version sets a more permissive sharing mode on Windows.
|
||||
@@ -90,4 +124,10 @@ bool openAndSeekFileSharedRead(QFile* file, QString* error, qint64 seek);
|
||||
QString fileSystemForPath(const QString & path);
|
||||
#endif
|
||||
|
||||
QByteArray OWNCLOUDSYNC_EXPORT calcMd5( const QString& fileName );
|
||||
QByteArray OWNCLOUDSYNC_EXPORT calcSha1( const QString& fileName );
|
||||
#ifdef ZLIB_FOUND
|
||||
QByteArray OWNCLOUDSYNC_EXPORT calcAdler32( const QString& fileName );
|
||||
#endif
|
||||
|
||||
}}
|
||||
|
||||
@@ -43,7 +43,6 @@ namespace OCC {
|
||||
|
||||
AbstractNetworkJob::AbstractNetworkJob(AccountPtr account, const QString &path, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _duration(0)
|
||||
, _timedout(false)
|
||||
, _followRedirects(false)
|
||||
, _ignoreCredentialFailure(false)
|
||||
@@ -307,8 +306,12 @@ MkColJob::MkColJob(AccountPtr account, const QString &path, QObject *parent)
|
||||
|
||||
void MkColJob::start()
|
||||
{
|
||||
// assumes ownership
|
||||
QNetworkReply *reply = davRequest("MKCOL", path());
|
||||
// add 'Content-Length: 0' header (see https://github.com/owncloud/client/issues/3256)
|
||||
QNetworkRequest req;
|
||||
req.setRawHeader("Content-Length", "0");
|
||||
|
||||
// assumes ownership
|
||||
QNetworkReply *reply = davRequest("MKCOL", path(), req);
|
||||
setReply(reply);
|
||||
setupConnections(reply);
|
||||
AbstractNetworkJob::start();
|
||||
@@ -350,7 +353,7 @@ LsColXMLParser::LsColXMLParser()
|
||||
|
||||
}
|
||||
|
||||
bool LsColXMLParser::parse( const QByteArray& xml, QHash<QString, qint64> *sizes)
|
||||
bool LsColXMLParser::parse( const QByteArray& xml, QHash<QString, qint64> *sizes, const QString& expectedPath)
|
||||
{
|
||||
// Parse DAV response
|
||||
QXmlStreamReader reader(xml);
|
||||
@@ -371,7 +374,14 @@ bool LsColXMLParser::parse( const QByteArray& xml, QHash<QString, qint64> *sizes
|
||||
// Start elements with DAV:
|
||||
if (type == QXmlStreamReader::StartElement && reader.namespaceUri() == QLatin1String("DAV:")) {
|
||||
if (name == QLatin1String("href")) {
|
||||
currentHref = QUrl::fromPercentEncoding(reader.readElementText().toUtf8());
|
||||
// We don't use URL encoding in our request URL (which is the expected path) (QNAM will do it for us)
|
||||
// but the result will have URL encoding..
|
||||
QString hrefString = QString::fromUtf8(QByteArray::fromPercentEncoding(reader.readElementText().toUtf8()));
|
||||
if (!hrefString.startsWith(expectedPath)) {
|
||||
qDebug() << "Invalid href" << hrefString << "expected starting with" << expectedPath;
|
||||
return false;
|
||||
}
|
||||
currentHref = hrefString;
|
||||
} else if (name == QLatin1String("response")) {
|
||||
} else if (name == QLatin1String("propstat")) {
|
||||
insidePropstat = true;
|
||||
@@ -520,7 +530,8 @@ bool LsColJob::finished()
|
||||
connect( &parser, SIGNAL(finishedWithoutError()),
|
||||
this, SIGNAL(finishedWithoutError()) );
|
||||
|
||||
if( !parser.parse( reply()->readAll(), &_sizes ) ) {
|
||||
QString expectedPath = reply()->request().url().path(); // something like "/owncloud/remote.php/webdav/folder"
|
||||
if( !parser.parse( reply()->readAll(), &_sizes, expectedPath ) ) {
|
||||
// XML parse error
|
||||
emit finishedWithError(reply());
|
||||
}
|
||||
@@ -840,7 +851,31 @@ bool JsonApiJob::finished()
|
||||
return true;
|
||||
}
|
||||
|
||||
QString extractErrorMessage(const QByteArray& errorResponse)
|
||||
{
|
||||
QXmlStreamReader reader(errorResponse);
|
||||
reader.readNextStartElement();
|
||||
if (reader.name() != "error") {
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
while (!reader.atEnd() && reader.error() == QXmlStreamReader::NoError) {
|
||||
reader.readNextStartElement();
|
||||
if (reader.name() == QLatin1String("message")) {
|
||||
return reader.readElementText();
|
||||
}
|
||||
}
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
QString errorMessage(const QString& baseError, const QByteArray& body)
|
||||
{
|
||||
QString msg = baseError;
|
||||
QString extra = extractErrorMessage(body);
|
||||
if (!extra.isEmpty()) {
|
||||
msg += QString::fromLatin1(" (%1)").arg(extra);
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -137,7 +137,7 @@ class OWNCLOUDSYNC_EXPORT LsColXMLParser : public QObject {
|
||||
public:
|
||||
explicit LsColXMLParser();
|
||||
|
||||
bool parse(const QByteArray &xml, QHash<QString, qint64> *sizes);
|
||||
bool parse(const QByteArray &xml, QHash<QString, qint64> *sizes, const QString& expectedPath);
|
||||
|
||||
signals:
|
||||
void directoryListingSubfolders(const QStringList &items);
|
||||
@@ -314,6 +314,17 @@ signals:
|
||||
void jsonRecieved(const QVariantMap &json);
|
||||
};
|
||||
|
||||
/** Gets the SabreDAV-style error message from an error response.
|
||||
*
|
||||
* This assumes the response is XML with a 'error' tag that has a
|
||||
* 'message' tag that contains the data to extract.
|
||||
*
|
||||
* Returns a null string if no message was found.
|
||||
*/
|
||||
QString OWNCLOUDSYNC_EXPORT extractErrorMessage(const QByteArray& errorResponse);
|
||||
|
||||
/** Builds a error message based on the error and the reply body. */
|
||||
QString OWNCLOUDSYNC_EXPORT errorMessage(const QString& baseError, const QByteArray& body);
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
|
||||
@@ -259,6 +259,21 @@ void OwncloudPropagator::start(const SyncFileItemVector& items)
|
||||
{
|
||||
Q_ASSERT(std::is_sorted(items.begin(), items.end()));
|
||||
|
||||
/* Check and log the transmission checksum type */
|
||||
ConfigFile cfg;
|
||||
const QString checksumType = cfg.transmissionChecksum().toUpper();
|
||||
|
||||
/* if the checksum type is empty, it is not send. No error */
|
||||
if( !checksumType.isEmpty() ) {
|
||||
if( checksumType == checkSumAdlerUpperC ||
|
||||
checksumType == checkSumMD5C ||
|
||||
checksumType == checkSumSHA1C ) {
|
||||
qDebug() << "Client sends and expects transmission checksum type" << checksumType;
|
||||
} else {
|
||||
qWarning() << "Unknown transmission checksum type from config" << checksumType;
|
||||
}
|
||||
}
|
||||
|
||||
/* This builds all the job needed for the propagation.
|
||||
* Each directories is a PropagateDirectory job, which contains the files in it.
|
||||
* In order to do that we loop over the items. (which are sorted by destination)
|
||||
|
||||
@@ -345,7 +345,7 @@ QString SqlQuery::lastQuery() const
|
||||
|
||||
int SqlQuery::numRowsAffected()
|
||||
{
|
||||
return 1;
|
||||
return sqlite3_changes(_db);
|
||||
}
|
||||
|
||||
void SqlQuery::finish()
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "owncloudpropagator_p.h"
|
||||
#include "propagatedownload.h"
|
||||
#include "networkjobs.h"
|
||||
@@ -21,6 +22,8 @@
|
||||
#include "utility.h"
|
||||
#include "filesystem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
|
||||
#include <json.h>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QFileInfo>
|
||||
@@ -30,6 +33,30 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
// Always coming in with forward slashes.
|
||||
// In csync_excluded_no_ctx we ignore all files with longer than 254 chars
|
||||
// This function also adds a dot at the begining of the filename to hide the file on OS X and Linux
|
||||
QString OWNCLOUDSYNC_EXPORT createDownloadTmpFileName(const QString &previous) {
|
||||
QString tmpFileName;
|
||||
QString tmpPath;
|
||||
int slashPos = previous.lastIndexOf('/');
|
||||
// work with both pathed filenames and only filenames
|
||||
if (slashPos == -1) {
|
||||
tmpFileName = previous;
|
||||
tmpPath = QString();
|
||||
} else {
|
||||
tmpFileName = previous.mid(slashPos+1);
|
||||
tmpPath = previous.left(slashPos);
|
||||
}
|
||||
int overhead = 1 + 1 + 2 + 8; // slash dot dot-tilde ffffffff"
|
||||
int spaceForFileName = qMin(254, tmpFileName.length() + overhead) - overhead;
|
||||
if (tmpPath.length() > 0) {
|
||||
return tmpPath + '/' + '.' + tmpFileName.left(spaceForFileName) + ".~" + (QString::number(uint(qrand() % 0xFFFFFFFF), 16));
|
||||
} else {
|
||||
return '.' + tmpFileName.left(spaceForFileName) + ".~" + (QString::number(uint(qrand() % 0xFFFFFFFF), 16));
|
||||
}
|
||||
}
|
||||
|
||||
// DOES NOT take owncership of the device.
|
||||
GETFileJob::GETFileJob(AccountPtr account, const QString& path, QFile *device,
|
||||
const QMap<QByteArray, QByteArray> &headers, const QByteArray &expectedEtagForResume,
|
||||
@@ -307,12 +334,7 @@ void PropagateDownloadFileQNAM::start()
|
||||
}
|
||||
|
||||
if (tmpFileName.isEmpty()) {
|
||||
tmpFileName = _item._file;
|
||||
//add a dot at the begining of the filename to hide the file.
|
||||
int slashPos = tmpFileName.lastIndexOf('/');
|
||||
tmpFileName.insert(slashPos+1, '.');
|
||||
//add the suffix
|
||||
tmpFileName += ".~" + QString::number(uint(qrand()), 16);
|
||||
tmpFileName = createDownloadTmpFileName(_item._file);
|
||||
}
|
||||
|
||||
_tmpFile.setFileName(_propagator->getFilePath(tmpFileName));
|
||||
@@ -339,6 +361,7 @@ void PropagateDownloadFileQNAM::start()
|
||||
if (startSize > 0) {
|
||||
if (startSize == _item._size) {
|
||||
qDebug() << "File is already complete, no need to download";
|
||||
_tmpFile.close();
|
||||
downloadFinished();
|
||||
return;
|
||||
}
|
||||
@@ -463,7 +486,21 @@ void PropagateDownloadFileQNAM::slotGetFinished()
|
||||
return;
|
||||
}
|
||||
|
||||
downloadFinished();
|
||||
// Do checksum validation for the download. If there is no checksum header, the validator
|
||||
// will also emit the validated() signal to continue the flow in slot downloadFinished()
|
||||
// as this is (still) also correct.
|
||||
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(_tmpFile.fileName(), this);
|
||||
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(downloadFinished()));
|
||||
connect(validator, SIGNAL(validationFailed(QString)), this, SLOT(slotChecksumFail(QString)));
|
||||
validator->downloadValidation(job->reply()->rawHeader(checkSumHeaderC));
|
||||
|
||||
}
|
||||
|
||||
void PropagateDownloadFileQNAM::slotChecksumFail( const QString& errMsg )
|
||||
{
|
||||
_tmpFile.remove();
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, errMsg ); // tr("The file downloaded with a broken checksum, will be redownloaded."));
|
||||
}
|
||||
|
||||
QString makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||
@@ -487,9 +524,54 @@ QString makeConflictFileName(const QString &fn, const QDateTime &dt)
|
||||
return conflictFileName;
|
||||
}
|
||||
|
||||
|
||||
namespace { // Anonymous namespace for the recall feature
|
||||
static QString makeRecallFileName(const QString &fn)
|
||||
{
|
||||
QString recallFileName(fn);
|
||||
// Add _recall-XXXX before the extention.
|
||||
int dotLocation = recallFileName.lastIndexOf('.');
|
||||
// If no extention, add it at the end (take care of cases like foo/.hidden or foo.bar/file)
|
||||
if (dotLocation <= recallFileName.lastIndexOf('/') + 1) {
|
||||
dotLocation = recallFileName.size();
|
||||
}
|
||||
|
||||
QString timeString = QDateTime::currentDateTime().toString("yyyyMMdd-hhmmss");
|
||||
recallFileName.insert(dotLocation, "_.sys.admin#recall#-" + timeString);
|
||||
|
||||
return recallFileName;
|
||||
}
|
||||
|
||||
static void handleRecallFile(const QString &fn)
|
||||
{
|
||||
qDebug() << "handleRecallFile: " << fn;
|
||||
|
||||
FileSystem::setFileHidden(fn, true);
|
||||
|
||||
QFile file(fn);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qWarning() << "Could not open recall file" << file.errorString();
|
||||
return;
|
||||
}
|
||||
QFileInfo existingFile(fn);
|
||||
QDir thisDir = existingFile.dir();
|
||||
|
||||
while (!file.atEnd()) {
|
||||
QByteArray line = file.readLine();
|
||||
line.chop(1); // remove trailing \n
|
||||
QString fpath = thisDir.filePath(line);
|
||||
QString rpath = makeRecallFileName(fpath);
|
||||
|
||||
// if previously recalled file exists then remove it (copy will not overwrite it)
|
||||
QFile(rpath).remove();
|
||||
qDebug() << "Copy recall file: " << fpath << " -> " << rpath;
|
||||
QFile::copy(fpath,rpath);
|
||||
}
|
||||
}
|
||||
} // end namespace
|
||||
|
||||
void PropagateDownloadFileQNAM::downloadFinished()
|
||||
{
|
||||
|
||||
QString fn = _propagator->getFilePath(_item._file);
|
||||
|
||||
// In case of file name clash, report an error
|
||||
@@ -512,11 +594,7 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
done(SyncFileItem::SoftError, renameError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QFileInfo existingFile(fn);
|
||||
if(FileSystem::fileExists(fn) && existingFile.permissions() != _tmpFile.permissions()) {
|
||||
_tmpFile.setPermissions(existingFile.permissions());
|
||||
qDebug() << "Created conflict file" << fn << "->" << conflictFileName;
|
||||
}
|
||||
|
||||
FileSystem::setModTime(_tmpFile.fileName(), _item._modtime);
|
||||
@@ -524,11 +602,32 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
// Accuracy, and we really need the time from the file system. (#3103)
|
||||
_item._modtime = FileSystem::getModTime(_tmpFile.fileName());
|
||||
|
||||
if (FileSystem::fileExists(fn)) {
|
||||
// Preserve the existing file permissions.
|
||||
QFileInfo existingFile(fn);
|
||||
if (existingFile.permissions() != _tmpFile.permissions()) {
|
||||
_tmpFile.setPermissions(existingFile.permissions());
|
||||
}
|
||||
|
||||
// Check whether the existing file has changed since the discovery
|
||||
// phase by comparing size and mtime to the previous values. This
|
||||
// is necessary to avoid overwriting user changes that happened between
|
||||
// the discovery phase and now.
|
||||
const qint64 expectedSize = _item.log._other_size;
|
||||
const time_t expectedMtime = _item.log._other_modtime;
|
||||
if (! FileSystem::verifyFileUnchanged(fn, expectedSize, expectedMtime)) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("File has changed since discovery"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QString error;
|
||||
_propagator->addTouchedFile(fn);
|
||||
FileSystem::setFileHidden(_tmpFile.fileName(), false);
|
||||
if (!FileSystem::renameReplace(_tmpFile.fileName(), fn, &error)) {
|
||||
// The fileChanged() check is done above to generate better error messages.
|
||||
if (!FileSystem::uncheckedRenameReplace(_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
|
||||
// put the downloaded file in its place, we are in a bad spot:
|
||||
// If we do nothing the next sync run will assume the user deleted
|
||||
@@ -540,10 +639,12 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
_propagator->_journal->deleteFileRecord(fn);
|
||||
_propagator->_journal->commit("download finished");
|
||||
}
|
||||
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, error);
|
||||
return;
|
||||
}
|
||||
FileSystem::setFileHidden(fn, false);
|
||||
|
||||
// Maybe we downloaded a newer version of the file than we thought we would...
|
||||
// Get up to date information for the journal.
|
||||
@@ -553,6 +654,11 @@ void PropagateDownloadFileQNAM::downloadFinished()
|
||||
_propagator->_journal->setDownloadInfo(_item._file, SyncJournalDb::DownloadInfo());
|
||||
_propagator->_journal->commit("download file start2");
|
||||
done(isConflict ? SyncFileItem::Conflict : SyncFileItem::Success);
|
||||
|
||||
// handle the special recall file
|
||||
if(_item._file == QLatin1String(".sys.admin#recall#") || _item._file.endsWith("/.sys.admin#recall#")) {
|
||||
handleRecallFile(fn);
|
||||
}
|
||||
}
|
||||
|
||||
void PropagateDownloadFileQNAM::slotDownloadProgress(qint64 received, qint64)
|
||||
|
||||
@@ -101,19 +101,21 @@ private slots:
|
||||
|
||||
class PropagateDownloadFileQNAM : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
QPointer<GETFileJob> _job;
|
||||
|
||||
// QFile *_file;
|
||||
QFile _tmpFile;
|
||||
public:
|
||||
PropagateDownloadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
|
||||
: PropagateItemJob(propagator, item) {}
|
||||
void start() Q_DECL_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void slotGetFinished();
|
||||
void abort() Q_DECL_OVERRIDE;
|
||||
void downloadFinished();
|
||||
void slotDownloadProgress(qint64,qint64);
|
||||
void slotChecksumFail( const QString& errMsg );
|
||||
|
||||
private:
|
||||
QPointer<GETFileJob> _job;
|
||||
QFile _tmpFile;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "propagateupload.h"
|
||||
#include "owncloudpropagator_p.h"
|
||||
#include "networkjobs.h"
|
||||
@@ -21,6 +22,8 @@
|
||||
#include "utility.h"
|
||||
#include "filesystem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
|
||||
#include <json.h>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QFileInfo>
|
||||
@@ -32,6 +35,12 @@
|
||||
#include "propagator_legacy.h"
|
||||
#endif
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2)
|
||||
namespace {
|
||||
const char owncloudShouldSoftCancelPropertyName[] = "owncloud-should-soft-cancel";
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
@@ -86,6 +95,17 @@ void PUTFileJob::start() {
|
||||
connect(reply(), SIGNAL(uploadProgress(qint64,qint64)), this, SIGNAL(uploadProgress(qint64,qint64)));
|
||||
connect(this, SIGNAL(networkActivity()), account().data(), SIGNAL(propagatorNetworkActivity()));
|
||||
|
||||
// For Qt versions not including https://codereview.qt-project.org/110150
|
||||
// Also do the runtime check if compiled with an old Qt but running with fixed one.
|
||||
// (workaround disabled on windows and mac because the binaries we ship have patched qt)
|
||||
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 7)
|
||||
if (QLatin1String(qVersion()) < QLatin1String("4.8.7"))
|
||||
connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
|
||||
#elif QT_VERSION > QT_VERSION_CHECK(5, 0, 0) && QT_VERSION < QT_VERSION_CHECK(5, 4, 2) && !defined Q_OS_WIN && !defined Q_OS_MAC
|
||||
if (QLatin1String(qVersion()) < QLatin1String("5.4.2"))
|
||||
connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
|
||||
#endif
|
||||
|
||||
AbstractNetworkJob::start();
|
||||
}
|
||||
|
||||
@@ -94,6 +114,13 @@ void PUTFileJob::slotTimeout() {
|
||||
reply()->abort();
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2)
|
||||
void PUTFileJob::slotSoftAbort() {
|
||||
reply()->setProperty(owncloudShouldSoftCancelPropertyName, true);
|
||||
reply()->abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
void PollJob::start()
|
||||
{
|
||||
setTimeout(120 * 1000);
|
||||
@@ -167,22 +194,51 @@ bool PollJob::finished()
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PropagateUploadFileQNAM::start()
|
||||
{
|
||||
if (_propagator->_abortRequested.fetchAndAddRelaxed(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString filePath = _propagator->getFilePath(_item._file);
|
||||
|
||||
// remember the modtime before checksumming to be able to detect a file
|
||||
// change during the checksum calculation
|
||||
_item._modtime = FileSystem::getModTime(filePath);
|
||||
|
||||
_stopWatch.start();
|
||||
|
||||
// do whatever is needed to add a checksum to the http upload request.
|
||||
// in any case, the validator will emit signal startUpload to let the flow
|
||||
// continue in slotStartUpload here.
|
||||
TransmissionChecksumValidator *validator = new TransmissionChecksumValidator(filePath, this);
|
||||
connect(validator, SIGNAL(validated(QByteArray)), this, SLOT(slotStartUpload(QByteArray)));
|
||||
validator->uploadValidation();
|
||||
}
|
||||
|
||||
void PropagateUploadFileQNAM::slotStartUpload(const QByteArray& checksum)
|
||||
{
|
||||
const QString fullFilePath(_propagator->getFilePath(_item._file));
|
||||
|
||||
_item._checksum = checksum;
|
||||
|
||||
if (!FileSystem::fileExists(fullFilePath)) {
|
||||
done(SyncFileItem::SoftError, tr("File Removed"));
|
||||
return;
|
||||
}
|
||||
_stopWatch.addLapTime(QLatin1String("Checksum"));
|
||||
|
||||
time_t prevModtime = _item._modtime; // the _item value was set in PropagateUploadFileQNAM::start()
|
||||
// but a potential checksum calculation could have taken some time during which the file could
|
||||
// have been changed again, so better check again here.
|
||||
|
||||
// Update the mtime and size, it might have changed since discovery.
|
||||
_item._modtime = FileSystem::getModTime(fullFilePath);
|
||||
if( prevModtime != _item._modtime ) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("Local file changed during syncing. It will be resumed."));
|
||||
return;
|
||||
}
|
||||
|
||||
quint64 fileSize = FileSystem::getSize(fullFilePath);
|
||||
_item._size = fileSize;
|
||||
|
||||
@@ -369,6 +425,18 @@ void PropagateUploadFileQNAM::startNextChunk()
|
||||
headers["OC-Chunk-Size"]= QByteArray::number(quint64(chunkSize()));
|
||||
headers["Content-Type"] = "application/octet-stream";
|
||||
headers["X-OC-Mtime"] = QByteArray::number(qint64(_item._modtime));
|
||||
|
||||
if(_item._file.contains(".sys.admin#recall#")) {
|
||||
// This is a file recall triggered by the admin. Note: the
|
||||
// recall list file created by the admin and downloaded by the
|
||||
// client (.sys.admin#recall#) also falls into this category
|
||||
// (albeit users are not supposed to mess up with it)
|
||||
|
||||
// We use a special tag header so that the server may decide to store this file away in some admin stage area
|
||||
// And not directly in the user's area (what would trigger redownloads etc).
|
||||
headers["OC-Tag"] = ".sys.admin#recall#";
|
||||
}
|
||||
|
||||
if (!_item._etag.isEmpty() && _item._etag != "empty_etag" &&
|
||||
_item._instruction != CSYNC_INSTRUCTION_NEW // On new files never send a If-Match
|
||||
) {
|
||||
@@ -397,6 +465,14 @@ void PropagateUploadFileQNAM::startNextChunk()
|
||||
if( currentChunkSize == 0 ) { // if the last chunk pretents to be 0, its actually the full chunk size.
|
||||
currentChunkSize = chunkSize();
|
||||
}
|
||||
if( !_item._checksum.isEmpty() ) {
|
||||
headers[checkSumHeaderC] = _item._checksum;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// checksum if its only one chunk
|
||||
if( !_item._checksum.isEmpty() ) {
|
||||
headers[checkSumHeaderC] = _item._checksum;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -471,6 +547,18 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||
}
|
||||
|
||||
QNetworkReply::NetworkError err = job->reply()->error();
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2)
|
||||
if (err == QNetworkReply::OperationCanceledError && job->reply()->property(owncloudShouldSoftCancelPropertyName).isValid()) {
|
||||
// Abort the job and try again later.
|
||||
// This works around a bug in QNAM wich might reuse a non-empty buffer for the next request.
|
||||
qDebug() << "Forcing job abort on HTTP connection reset with Qt < 5.4.2.";
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
done(SyncFileItem::SoftError, tr("Forcing job abort on HTTP connection reset with Qt < 5.4.2."));
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (err != QNetworkReply::NoError) {
|
||||
_item._httpErrorCode = job->reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if(checkForProblemsWithShared(_item._httpErrorCode,
|
||||
@@ -478,14 +566,9 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||
"It is restored and your edit is in the conflict file."))) {
|
||||
return;
|
||||
}
|
||||
QString errorString = job->errorString();
|
||||
|
||||
QByteArray replyContent = job->reply()->readAll();
|
||||
qDebug() << replyContent; // display the XML error in the debug
|
||||
QRegExp rx("<s:message>(.*)</s:message>"); // Issue #1366: display server exception
|
||||
if (rx.indexIn(QString::fromUtf8(replyContent)) != -1) {
|
||||
errorString += QLatin1String(" (") + rx.cap(1) + QLatin1Char(')');
|
||||
}
|
||||
QString errorString = errorMessage(job->errorString(), replyContent);
|
||||
|
||||
if (job->reply()->hasRawHeader("OC-ErrorString")) {
|
||||
errorString = job->reply()->rawHeader("OC-ErrorString");
|
||||
@@ -538,15 +621,8 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||
}
|
||||
}
|
||||
|
||||
// compare expected and real modification time of the file and size
|
||||
const time_t new_mtime = FileSystem::getModTime(fullFilePath);
|
||||
const quint64 new_size = static_cast<quint64>(FileSystem::getSize(fullFilePath));
|
||||
QFileInfo fi(_propagator->getFilePath(_item._file));
|
||||
if (new_mtime != _item._modtime || new_size != _item._size) {
|
||||
qDebug() << "The local file has changed during upload:"
|
||||
<< "mtime: " << _item._modtime << "<->" << new_mtime
|
||||
<< ", size: " << _item._size << "<->" << new_size
|
||||
<< ", QFileInfo: " << Utility::qDateTimeToTime_t(fi.lastModified()) << fi.lastModified();
|
||||
// Check whether the file changed since discovery.
|
||||
if (! FileSystem::verifyFileUnchanged(fullFilePath, _item._size, _item._modtime)) {
|
||||
_propagator->_anotherSyncNeeded = true;
|
||||
if( !finished ) {
|
||||
abortWithError(SyncFileItem::SoftError, tr("Local file changed during sync."));
|
||||
@@ -612,6 +688,11 @@ void PropagateUploadFileQNAM::slotPutFinished()
|
||||
// Well, the mtime was not set
|
||||
#endif
|
||||
}
|
||||
|
||||
// performance logging
|
||||
_item._requestDuration = _stopWatch.stop();
|
||||
qDebug() << "*==* duration UPLOAD" << _item._size << _stopWatch.durationOfLap(QLatin1String("Checksum")) << _item._requestDuration;
|
||||
|
||||
finalize(_item);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
|
||||
namespace OCC {
|
||||
class BandwidthManager;
|
||||
|
||||
@@ -40,11 +41,21 @@ public:
|
||||
bool isSequential() const Q_DECL_OVERRIDE;
|
||||
bool seek ( qint64 pos ) Q_DECL_OVERRIDE;
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 4, 2)
|
||||
bool reset() Q_DECL_OVERRIDE { emit wasReset(); return QIODevice::reset(); }
|
||||
#endif
|
||||
|
||||
void setBandwidthLimited(bool);
|
||||
bool isBandwidthLimited() { return _bandwidthLimited; }
|
||||
void setChoked(bool);
|
||||
bool isChoked() { return _choked; }
|
||||
void giveBandwidthQuota(qint64 bwq);
|
||||
|
||||
signals:
|
||||
#if QT_VERSION < 0x050402
|
||||
void wasReset();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// The file data
|
||||
@@ -65,6 +76,8 @@ protected slots:
|
||||
|
||||
class PUTFileJob : public AbstractNetworkJob {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QScopedPointer<QIODevice> _device;
|
||||
QMap<QByteArray, QByteArray> _headers;
|
||||
QString _errorString;
|
||||
@@ -95,6 +108,11 @@ public:
|
||||
signals:
|
||||
void finishedSignal();
|
||||
void uploadProgress(qint64,qint64);
|
||||
|
||||
private slots:
|
||||
#if QT_VERSION < 0x050402
|
||||
void slotSoftAbort();
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -131,6 +149,7 @@ signals:
|
||||
class PropagateUploadFileQNAM : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
/**
|
||||
* That's the start chunk that was stored in the database for resuming.
|
||||
* In the non-resuming case it is 0.
|
||||
@@ -148,6 +167,10 @@ class PropagateUploadFileQNAM : public PropagateItemJob {
|
||||
QElapsedTimer _duration;
|
||||
QVector<PUTFileJob*> _jobs; /// network jobs that are currently in transit
|
||||
bool _finished; // Tells that all the jobs have been finished
|
||||
|
||||
// measure the performance of checksum calc and upload
|
||||
Utility::StopWatch _stopWatch;
|
||||
|
||||
public:
|
||||
PropagateUploadFileQNAM(OwncloudPropagator* propagator,const SyncFileItem& item)
|
||||
: PropagateItemJob(propagator, item), _startChunk(0), _currentChunk(0), _chunkCount(0), _transferId(0), _finished(false) {}
|
||||
@@ -160,6 +183,8 @@ private slots:
|
||||
void startNextChunk();
|
||||
void finalize(const SyncFileItem&);
|
||||
void slotJobDestroyed(QObject *job);
|
||||
void slotStartUpload(const QByteArray &checksum);
|
||||
|
||||
private:
|
||||
void startPollJob(const QString& path);
|
||||
void abortWithError(SyncFileItem::Status status, const QString &error);
|
||||
|
||||
@@ -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.
|
||||
@@ -713,7 +714,11 @@ void PropagateDownloadFileLegacy::start()
|
||||
|
||||
QString error;
|
||||
_propagator->addTouchedFile(fn);
|
||||
if (!FileSystem::renameReplace(tmpFile.fileName(), fn, &error)) {
|
||||
const qint64 expectedFileSize = _item.log._other_size;
|
||||
const time_t expectedFileMtime = _item.log._other_modtime;
|
||||
if (!FileSystem::renameReplace(tmpFile.fileName(), fn,
|
||||
expectedFileSize, expectedFileMtime,
|
||||
&error)) {
|
||||
done(SyncFileItem::NormalError, error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,6 +119,7 @@ void PropagateLocalMkdir::start()
|
||||
done( SyncFileItem::NormalError, tr("Attention, possible case sensitivity clash with %1").arg(newDirStr) );
|
||||
return;
|
||||
}
|
||||
_propagator->addTouchedFile(newDirStr);
|
||||
QDir localDir(_propagator->_localDir);
|
||||
if (!localDir.mkpath(_item._file)) {
|
||||
done( SyncFileItem::NormalError, tr("could not create directory %1").arg(newDirStr) );
|
||||
|
||||
@@ -21,6 +21,22 @@
|
||||
|
||||
namespace OCC {
|
||||
|
||||
/**
|
||||
* Tags for checksum headers.
|
||||
* They are here for being shared between Upload- and Download Job
|
||||
*/
|
||||
|
||||
// the header itself
|
||||
static const char checkSumHeaderC[] = "OC-Checksum";
|
||||
// ...and it's values
|
||||
static const char checkSumMD5C[] = "MD5";
|
||||
static const char checkSumSHA1C[] = "SHA1";
|
||||
static const char checkSumAdlerC[] = "Adler32";
|
||||
static const char checkSumAdlerUpperC[] = "ADLER32";
|
||||
|
||||
/**
|
||||
* Declaration of the other propagation jobs
|
||||
*/
|
||||
class PropagateLocalRemove : public PropagateItemJob {
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
@@ -75,7 +75,7 @@ SyncEngine::SyncEngine(AccountPtr account, CSYNC *ctx, const QString& localPath,
|
||||
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
|
||||
qRegisterMetaType<Progress::Info>("Progress::Info");
|
||||
|
||||
_thread.setObjectName("CSync_Neon_Thread");
|
||||
_thread.setObjectName("SyncEngine_Thread");
|
||||
_thread.start();
|
||||
}
|
||||
|
||||
@@ -595,7 +595,6 @@ void SyncEngine::startSync()
|
||||
#endif
|
||||
|
||||
fileRecordCount = _journal->getFileRecordCount(); // this creates the DB if it does not exist yet
|
||||
bool isUpdateFrom_1_5 = _journal->isUpdateFrom_1_5();
|
||||
|
||||
if( fileRecordCount == -1 ) {
|
||||
qDebug() << "No way to create a sync journal!";
|
||||
@@ -605,13 +604,7 @@ void SyncEngine::startSync()
|
||||
// database creation error!
|
||||
}
|
||||
|
||||
if (fileRecordCount >= 1 && isUpdateFrom_1_5) {
|
||||
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_ctx->read_remote_from_db = false;
|
||||
} else {
|
||||
_csync_ctx->read_remote_from_db = 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.
|
||||
@@ -999,8 +992,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");
|
||||
|
||||
@@ -150,6 +150,7 @@ public:
|
||||
quint64 _inode;
|
||||
QByteArray _fileId;
|
||||
QByteArray _remotePerm;
|
||||
QByteArray _checksum;
|
||||
QString _directDownloadUrl;
|
||||
QString _directDownloadCookies;
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
_dbFile = path;
|
||||
@@ -272,19 +272,20 @@ bool SyncJournalDb::checkConnect()
|
||||
return sqlFail("Create table version", createQuery);
|
||||
}
|
||||
|
||||
_possibleUpgradeFromMirall_1_5 = false;
|
||||
bool forceRemoteDiscovery = 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
|
||||
if (!isNewDb) {
|
||||
qDebug() << Q_FUNC_INFO << "_possibleUpgradeFromMirall_1_5 detected!";
|
||||
_possibleUpgradeFromMirall_1_5 = true;
|
||||
qDebug() << Q_FUNC_INFO << "possibleUpgradeFromMirall_1_5 detected!";
|
||||
forceRemoteDiscovery = true;
|
||||
}
|
||||
createQuery.prepare("INSERT INTO version VALUES (?1, ?2, ?3, ?4);");
|
||||
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 +293,10 @@ bool SyncJournalDb::checkConnect()
|
||||
int minor = versionQuery.intValue(1);
|
||||
int patch = versionQuery.intValue(2);
|
||||
|
||||
if( major == 1 && minor == 8 && (patch == 0 || patch == 1) ) {
|
||||
qDebug() << Q_FUNC_INFO << "possibleUpgradeFromMirall_1_8_0_or_1 detected!";
|
||||
forceRemoteDiscovery = 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 "
|
||||
@@ -317,6 +322,25 @@ bool SyncJournalDb::checkConnect()
|
||||
qDebug() << "WARN: Failed to update the database structure!";
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are upgrading from a client version older than 1.5 is found,
|
||||
* we cannot read from the database because we need to fetch the files id and etags.
|
||||
*
|
||||
* If 1.8.0 caused missing data in the local tree, so we also don't read from DB
|
||||
* to get back the files that were gone.
|
||||
* In 1.8.1 we had a fix to re-get the data, but this one here is better
|
||||
*/
|
||||
if (forceRemoteDiscovery) {
|
||||
qDebug() << "Forcing remote re-discovery by deleting folder Etags";
|
||||
SqlQuery deleteRemoteFolderEtagsQuery(_db);
|
||||
deleteRemoteFolderEtagsQuery.prepare("UPDATE metadata SET md5='_invalid_' WHERE type=2;");
|
||||
if( !deleteRemoteFolderEtagsQuery.exec() ) {
|
||||
qDebug() << "ERROR: Query failed" << deleteRemoteFolderEtagsQuery.error();
|
||||
} else {
|
||||
qDebug() << "Cleared" << deleteRemoteFolderEtagsQuery.numRowsAffected() << "folder ETags";
|
||||
}
|
||||
}
|
||||
|
||||
_getFileRecordQuery.reset(new SqlQuery(_db));
|
||||
_getFileRecordQuery->prepare("SELECT path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize FROM "
|
||||
"metadata WHERE phash=?1" );
|
||||
@@ -403,7 +427,6 @@ void SyncJournalDb::close()
|
||||
_deleteFileRecordRecursively.reset(0);
|
||||
_getErrorBlacklistQuery.reset(0);
|
||||
_setErrorBlacklistQuery.reset(0);
|
||||
_possibleUpgradeFromMirall_1_5 = false;
|
||||
|
||||
_db.close();
|
||||
_avoidReadFromDbOnNextSyncFilter.clear();
|
||||
@@ -749,10 +772,6 @@ bool SyncJournalDb::postSyncCleanup(const QSet<QString>& filepathsToKeep,
|
||||
// Incoroporate results back into main DB
|
||||
walCheckpoint();
|
||||
|
||||
if (_possibleUpgradeFromMirall_1_5) {
|
||||
_possibleUpgradeFromMirall_1_5 = false; // should be handled now
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1145,12 +1164,11 @@ void SyncJournalDb::wipeErrorBlacklistEntry( const QString& file )
|
||||
|
||||
void SyncJournalDb::updateErrorBlacklistEntry( const SyncJournalErrorBlacklistRecord& item )
|
||||
{
|
||||
QMutexLocker locker(&_mutex);
|
||||
if( !checkConnect() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
QMutexLocker locker(&_mutex);
|
||||
|
||||
_setErrorBlacklistQuery->bindValue(1, item._file);
|
||||
_setErrorBlacklistQuery->bindValue(2, item._lastTryEtag);
|
||||
_setErrorBlacklistQuery->bindValue(3, QString::number(item._lastTryModtime));
|
||||
@@ -1315,13 +1333,6 @@ bool SyncJournalDb::isConnected()
|
||||
return checkConnect();
|
||||
}
|
||||
|
||||
bool SyncJournalDb::isUpdateFrom_1_5()
|
||||
{
|
||||
QMutexLocker lock(&_mutex);
|
||||
checkConnect();
|
||||
return _possibleUpgradeFromMirall_1_5;
|
||||
}
|
||||
|
||||
bool operator==(const SyncJournalDb::DownloadInfo & lhs,
|
||||
const SyncJournalDb::DownloadInfo & rhs)
|
||||
{
|
||||
|
||||
@@ -113,12 +113,6 @@ public:
|
||||
*/
|
||||
bool isConnected();
|
||||
|
||||
/**
|
||||
* Tell the sync engine if we need to disable the fetch from db to be sure that the fileid
|
||||
* are updated.
|
||||
*/
|
||||
bool isUpdateFrom_1_5();
|
||||
|
||||
private:
|
||||
bool updateDatabaseStructure();
|
||||
bool updateMetadataTableStructure();
|
||||
@@ -134,7 +128,6 @@ private:
|
||||
QString _dbFile;
|
||||
QMutex _mutex; // Public functions are protected with the mutex.
|
||||
int _transaction;
|
||||
bool _possibleUpgradeFromMirall_1_5;
|
||||
QScopedPointer<SqlQuery> _getFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _setFileRecordQuery;
|
||||
QScopedPointer<SqlQuery> _getDownloadInfoQuery;
|
||||
|
||||
@@ -241,12 +241,17 @@ QString Theme::updateCheckUrl() const
|
||||
return QLatin1String("https://updates.owncloud.com/client/");
|
||||
}
|
||||
|
||||
QString Theme::transmissionChecksum() const
|
||||
{
|
||||
return QString::null; // No transmission by default.
|
||||
}
|
||||
|
||||
QString Theme::gitSHA1() const
|
||||
{
|
||||
QString devString;
|
||||
#ifdef GIT_SHA1
|
||||
const QString githubPrefix(QLatin1String(
|
||||
"https://github.com/owncloud/mirall/commit/"));
|
||||
"https://github.com/owncloud/client/commit/"));
|
||||
const QString gitSha1(QLatin1String(GIT_SHA1));
|
||||
devString = QCoreApplication::translate("ownCloudTheme::about()",
|
||||
"<p><small>Built from Git revision <a href=\"%1\">%2</a>"
|
||||
@@ -389,5 +394,5 @@ bool Theme::wizardSelectiveSyncDefaultNothing() const
|
||||
}
|
||||
|
||||
|
||||
} // end namespace mirall
|
||||
} // end namespace client
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef _THEME_H
|
||||
#define _THEME_H
|
||||
|
||||
#include <QObject>
|
||||
#include "syncresult.h"
|
||||
|
||||
|
||||
@@ -188,12 +189,19 @@ public:
|
||||
*/
|
||||
virtual QString updateCheckUrl() const;
|
||||
|
||||
|
||||
/**
|
||||
* When true, the setup wizard will show the selective sync dialog by default and default
|
||||
* to nothing selected
|
||||
*/
|
||||
virtual bool wizardSelectiveSyncDefaultNothing() const;
|
||||
/**
|
||||
* @brief Add an additional checksum header to PUT requests and compare them
|
||||
* if they come with GET requests.
|
||||
* This value sets the checksum type (SHA1, MD5 or Adler32) or is left empty
|
||||
* if no checksumming is wanted. In that case it can still be overwritten in
|
||||
* the client config file.
|
||||
*/
|
||||
virtual QString transmissionChecksum() const;
|
||||
|
||||
protected:
|
||||
#ifndef TOKEN_AUTH_ONLY
|
||||
|
||||
151
src/libsync/transmissionchecksumvalidator.cpp
Normal file
151
src/libsync/transmissionchecksumvalidator.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "filesystem.h"
|
||||
#include "transmissionchecksumvalidator.h"
|
||||
#include "syncfileitem.h"
|
||||
#include "propagatorjobs.h"
|
||||
#include "configfile.h"
|
||||
|
||||
#include <qtconcurrentrun.h>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
TransmissionChecksumValidator::TransmissionChecksumValidator(const QString& filePath, QObject *parent)
|
||||
:QObject(parent),
|
||||
_filePath(filePath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::setChecksumType( const QByteArray& type )
|
||||
{
|
||||
_checksumType = type;
|
||||
}
|
||||
|
||||
QString TransmissionChecksumValidator::checksumType() const
|
||||
{
|
||||
QString checksumType = _checksumType;
|
||||
if( checksumType.isEmpty() ) {
|
||||
ConfigFile cfg;
|
||||
checksumType = cfg.transmissionChecksum();
|
||||
}
|
||||
|
||||
return checksumType;
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::uploadValidation()
|
||||
{
|
||||
const QString csType = checksumType();
|
||||
|
||||
if( csType.isEmpty() ) {
|
||||
// if there is no checksum defined, continue to upload
|
||||
emit validated(QByteArray());
|
||||
} else {
|
||||
// Calculate the checksum in a different thread first.
|
||||
|
||||
connect( &_watcher, SIGNAL(finished()),
|
||||
this, SLOT(slotUploadChecksumCalculated()));
|
||||
if( csType == checkSumMD5C ) {
|
||||
_checksumHeader = checkSumMD5C;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
|
||||
|
||||
} else if( csType == checkSumSHA1C ) {
|
||||
_checksumHeader = checkSumSHA1C;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run( FileSystem::calcSha1, _filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( csType == checkSumAdlerC) {
|
||||
_checksumHeader = checkSumAdlerC;
|
||||
_checksumHeader += ":";
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
// for an unknown checksum, continue to upload
|
||||
emit validated(QByteArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::slotUploadChecksumCalculated( )
|
||||
{
|
||||
QByteArray checksum = _watcher.future().result();
|
||||
|
||||
if( !checksum.isEmpty() ) {
|
||||
checksum.prepend( _checksumHeader );
|
||||
}
|
||||
|
||||
emit validated(checksum);
|
||||
}
|
||||
|
||||
|
||||
void TransmissionChecksumValidator::downloadValidation( const QByteArray& checksumHeader )
|
||||
{
|
||||
// if the incoming header is empty, there was no checksum header, and
|
||||
// no validation can happen. Just continue.
|
||||
const QString csType = checksumType();
|
||||
|
||||
// for empty checksum type, everything is valid.
|
||||
if( csType.isEmpty() ) {
|
||||
emit validated(QByteArray());
|
||||
return;
|
||||
}
|
||||
|
||||
int indx = checksumHeader.indexOf(':');
|
||||
if( indx < 0 ) {
|
||||
qDebug() << "Checksum header malformed:" << checksumHeader;
|
||||
emit validationFailed(tr("The checksum header is malformed.")); // show must go on - even not validated.
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray type = checksumHeader.left(indx).toUpper();
|
||||
_expectedHash = checksumHeader.mid(indx+1);
|
||||
|
||||
connect( &_watcher, SIGNAL(finished()), this, SLOT(slotDownloadChecksumCalculated()) );
|
||||
|
||||
// start the calculation in different thread
|
||||
if( type == checkSumMD5C ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcMd5, _filePath));
|
||||
} else if( type == checkSumSHA1C ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcSha1, _filePath));
|
||||
}
|
||||
#ifdef ZLIB_FOUND
|
||||
else if( type == checkSumAdlerUpperC ) {
|
||||
_watcher.setFuture(QtConcurrent::run(FileSystem::calcAdler32, _filePath));
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
qDebug() << "Unknown checksum type" << type;
|
||||
emit validationFailed(tr("The checksum header is malformed."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TransmissionChecksumValidator::slotDownloadChecksumCalculated()
|
||||
{
|
||||
const QByteArray hash = _watcher.future().result();
|
||||
|
||||
if( hash != _expectedHash ) {
|
||||
emit validationFailed(tr("The downloaded file does not match the checksum, it will be resumed."));
|
||||
} else {
|
||||
// qDebug() << "Checksum checked and matching: " << _expectedHash;
|
||||
emit validated(hash);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
76
src/libsync/transmissionchecksumvalidator.h
Normal file
76
src/libsync/transmissionchecksumvalidator.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "owncloudlib.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QFutureWatcher>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
class OWNCLOUDSYNC_EXPORT TransmissionChecksumValidator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TransmissionChecksumValidator(const QString& filePath, QObject *parent = 0);
|
||||
|
||||
/**
|
||||
* method to prepare a checksum for transmission and save it to the _checksum
|
||||
* member of the SyncFileItem *item.
|
||||
* The kind of requested checksum is taken from config. No need to set from outside.
|
||||
*
|
||||
* In any case of processing (checksum set, no checksum required and also unusual error)
|
||||
* the object will emit the signal validated(). The item->_checksum is than either
|
||||
* set to a proper value or empty.
|
||||
*/
|
||||
void uploadValidation();
|
||||
|
||||
/**
|
||||
* method to verify the checksum coming with requests in a checksum header. The required
|
||||
* checksum method is read from config.
|
||||
*
|
||||
* If no checksum is there, or if a correct checksum is there, the signal validated()
|
||||
* will be emitted. In case of any kind of error, the signal validationFailed() will
|
||||
* be emitted.
|
||||
*/
|
||||
void downloadValidation( const QByteArray& checksumHeader );
|
||||
|
||||
// This is only used in test cases (by now). This class reads the required
|
||||
// test case from the config file.
|
||||
void setChecksumType(const QByteArray &type );
|
||||
QString checksumType() const;
|
||||
|
||||
signals:
|
||||
void validated(const QByteArray& checksum);
|
||||
void validationFailed( const QString& errMsg );
|
||||
|
||||
private slots:
|
||||
void slotUploadChecksumCalculated();
|
||||
void slotDownloadChecksumCalculated();
|
||||
|
||||
private:
|
||||
QByteArray _checksumType;
|
||||
QByteArray _expectedHash;
|
||||
QByteArray _checksumHeader;
|
||||
|
||||
QString _filePath;
|
||||
|
||||
// watcher for the checksum calculation thread
|
||||
QFutureWatcher<QByteArray> _watcher;
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user