mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-08 03:46:49 +02:00
Compare commits
261 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80aac855e1 | ||
|
|
9b78da3028 | ||
|
|
c190646cca | ||
|
|
69b2e3409b | ||
|
|
ea3b33b169 | ||
|
|
6d15970431 | ||
|
|
edb35db983 | ||
|
|
52a5b5041d | ||
|
|
b6244c0721 | ||
|
|
d44b5ed676 | ||
|
|
013fbfca0e | ||
|
|
3d507e6df7 | ||
|
|
f3f5da0939 | ||
|
|
fdb4cea0f6 | ||
|
|
782b70585b | ||
|
|
e580503a27 | ||
|
|
44ea998d3e | ||
|
|
0f0ee76284 | ||
|
|
5b3a227351 | ||
|
|
e93a51b87d | ||
|
|
c99b8c897e | ||
|
|
0f3e32d854 | ||
|
|
fe645efb60 | ||
|
|
60dfc90376 | ||
|
|
e047bd3670 | ||
|
|
a3928ccb75 | ||
|
|
b1e71d9ea6 | ||
|
|
1fc95a6539 | ||
|
|
d97e8f44de | ||
|
|
b76e55fb85 | ||
|
|
2ad7d5d049 | ||
|
|
a8436ede11 | ||
|
|
a5968a0d59 | ||
|
|
a388047654 | ||
|
|
5a9286a68c | ||
|
|
23da2f3ada | ||
|
|
8e28f4349d | ||
|
|
6dc0cac7ca | ||
|
|
b59b14ba83 | ||
|
|
41d749f061 | ||
|
|
8d72aedb48 | ||
|
|
d1abebf5b3 | ||
|
|
f92cb4dcb7 | ||
|
|
0437e716f0 | ||
|
|
e86416fff7 | ||
|
|
3cd8a2be1c | ||
|
|
3b28e863c4 | ||
|
|
38478ead98 | ||
|
|
e4b40b0f49 | ||
|
|
f174576674 | ||
|
|
af052ea839 | ||
|
|
54e91a4ad5 | ||
|
|
3198ecf19b | ||
|
|
513848e93b | ||
|
|
3d0abdf5d0 | ||
|
|
a6dd3d0baf | ||
|
|
e9bc49b905 | ||
|
|
80bc9d873e | ||
|
|
27ff952a88 | ||
|
|
c4e1ed5bcd | ||
|
|
628c24209e | ||
|
|
2601ecf936 | ||
|
|
c8c2b3cf66 | ||
|
|
5ad4e093bd | ||
|
|
53f35239c8 | ||
|
|
c38eaf5c33 | ||
|
|
910402fa91 | ||
|
|
276e3d6d21 | ||
|
|
c9db5d234f | ||
|
|
4fd773d4ae | ||
|
|
5d67271acd | ||
|
|
f52cefaa94 | ||
|
|
2c127ae2d5 | ||
|
|
7db4455592 | ||
|
|
0d926f7632 | ||
|
|
383ba63c5a | ||
|
|
4e5e290ec2 | ||
|
|
01b3cde79b | ||
|
|
5b0fea195d | ||
|
|
f4d1a07b72 | ||
|
|
99f5580c37 | ||
|
|
f49985697b | ||
|
|
2a410217e2 | ||
|
|
2c0e820d9a | ||
|
|
2326ea77e6 | ||
|
|
e6984f4058 | ||
|
|
aeea27b57b | ||
|
|
e8f95743cc | ||
|
|
fd0b8f3583 | ||
|
|
2a52b087fd | ||
|
|
c70cdd61ca | ||
|
|
5979db1270 | ||
|
|
0edc29dc72 | ||
|
|
5e4aaab91b | ||
|
|
6a298d180d | ||
|
|
bc946053a3 | ||
|
|
cd5ad7db64 | ||
|
|
1332e7d66a | ||
|
|
949e8e5f51 | ||
|
|
1e78a14f1a | ||
|
|
4291ea47f7 | ||
|
|
b50706a7aa | ||
|
|
b20f87eef3 | ||
|
|
bc2e1a53cb | ||
|
|
e7d4d12e16 | ||
|
|
f348a7d4f4 | ||
|
|
9c82c36c71 | ||
|
|
480388133e | ||
|
|
ff79cc9107 | ||
|
|
e86b6b2246 | ||
|
|
18c39197e5 | ||
|
|
7eb8219419 | ||
|
|
2f915af690 | ||
|
|
272898a792 | ||
|
|
68ee08440e | ||
|
|
b02b99003a | ||
|
|
e3306abbcb | ||
|
|
61d05fa4d7 | ||
|
|
0fc3df45ce | ||
|
|
78dede2ac4 | ||
|
|
577a2715bd | ||
|
|
56de50de28 | ||
|
|
a48785ee5e | ||
|
|
3be73ad1ca | ||
|
|
c1d2a9ab92 | ||
|
|
87a7642381 | ||
|
|
9b4e00c3fc | ||
|
|
97a4302b58 | ||
|
|
bac5121221 | ||
|
|
3462b1d1a3 | ||
|
|
54cc1cdcf7 | ||
|
|
0fd678e9a6 | ||
|
|
580762b4e6 | ||
|
|
c09a828f7b | ||
|
|
c5a0ce5a43 | ||
|
|
6d74601cf3 | ||
|
|
557931f207 | ||
|
|
84e9400742 | ||
|
|
45f831da4c | ||
|
|
55dfbb9446 | ||
|
|
8e1422fb37 | ||
|
|
6a378a6362 | ||
|
|
879dadba78 | ||
|
|
5266720c62 | ||
|
|
dca2b67338 | ||
|
|
ea691aa2a0 | ||
|
|
9597c2b808 | ||
|
|
3817202b9c | ||
|
|
ed960e5a71 | ||
|
|
8b08ca2d00 | ||
|
|
b20adf2281 | ||
|
|
b02f724e7e | ||
|
|
6d2f77851e | ||
|
|
131055aefc | ||
|
|
0682f8793c | ||
|
|
fdf4a53283 | ||
|
|
ccc1db02a0 | ||
|
|
4e15e2055c | ||
|
|
f8c0e796df | ||
|
|
fd40113e48 | ||
|
|
ca8b961e60 | ||
|
|
b6e3709e74 | ||
|
|
fd97a09e64 | ||
|
|
42d2594071 | ||
|
|
ff70cf9b5f | ||
|
|
9d06e99b58 | ||
|
|
543e8a224c | ||
|
|
ea378fe837 | ||
|
|
de9ee295be | ||
|
|
3a8d6a6f16 | ||
|
|
5ac58d3b83 | ||
|
|
866991df4c | ||
|
|
7613938181 | ||
|
|
4eb4069c15 | ||
|
|
f097e472f4 | ||
|
|
c46b97ec37 | ||
|
|
1994cd7ba2 | ||
|
|
4af45394f9 | ||
|
|
c1c5a488d0 | ||
|
|
5e18d5cf4e | ||
|
|
8a639d39fc | ||
|
|
e43425cf16 | ||
|
|
26af3ce525 | ||
|
|
5f316f8b96 | ||
|
|
35060f4954 | ||
|
|
0b2f1dda48 | ||
|
|
1a555449ec | ||
|
|
5ba61e5399 | ||
|
|
0809703d2b | ||
|
|
55b423fbb5 | ||
|
|
e08ec11fd4 | ||
|
|
0c2f65a939 | ||
|
|
29e34bd2c0 | ||
|
|
6abc2bf72d | ||
|
|
a10fe84a83 | ||
|
|
7d9c4d052c | ||
|
|
f8d69dfe8e | ||
|
|
ce735d8d68 | ||
|
|
484c8397e3 | ||
|
|
b8de36c585 | ||
|
|
59ba292edd | ||
|
|
bc9c4fe17b | ||
|
|
7253f64d26 | ||
|
|
28e9c56a4b | ||
|
|
f1a35527ee | ||
|
|
039ff0ca8e | ||
|
|
85afa4788b | ||
|
|
3ca071c612 | ||
|
|
d78b84a12f | ||
|
|
5c2b185374 | ||
|
|
8339ff3c1c | ||
|
|
73e58fa090 | ||
|
|
4e70a14c29 | ||
|
|
14131cf020 | ||
|
|
dd5b36779d | ||
|
|
34bb950889 | ||
|
|
489efa24f7 | ||
|
|
9840ae9e96 | ||
|
|
d375e9b86c | ||
|
|
722918abd6 | ||
|
|
48fe8d1cb8 | ||
|
|
1000cdda09 | ||
|
|
d71b59b817 | ||
|
|
95b92c12ac | ||
|
|
a60370255e | ||
|
|
21909cae04 | ||
|
|
176ea20fef | ||
|
|
1a279ca158 | ||
|
|
0ed929f24b | ||
|
|
41eeed981f | ||
|
|
8e68e0321c | ||
|
|
881b32521b | ||
|
|
b98876e265 | ||
|
|
dbb5bcb784 | ||
|
|
d95b43f347 | ||
|
|
9ca9773e9d | ||
|
|
13b2568358 | ||
|
|
7a2d12bb47 | ||
|
|
2152bc5fca | ||
|
|
11aecf1af2 | ||
|
|
903d6c80fe | ||
|
|
585221c58c | ||
|
|
8ef11a38c9 | ||
|
|
f94a15b0fa | ||
|
|
9c83c57f1c | ||
|
|
1d0ef83078 | ||
|
|
4ab217ba04 | ||
|
|
4a1a5fa076 | ||
|
|
298684aaa0 | ||
|
|
005c027274 | ||
|
|
f4495c5c80 | ||
|
|
f862c626a1 | ||
|
|
6487bb071b | ||
|
|
e7296d03d3 | ||
|
|
139bb8f094 | ||
|
|
84ecbc4c89 | ||
|
|
4a010ce7cd | ||
|
|
40698c8fd0 | ||
|
|
3adaf44f7b | ||
|
|
3983fecaf1 | ||
|
|
7effcff245 |
@@ -176,7 +176,7 @@ if (USE_OUR_OWN_SQLITE3)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(ZLIB)
|
find_package(ZLIB REQUIRED)
|
||||||
|
|
||||||
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
|
|||||||
34
ChangeLog
34
ChangeLog
@@ -1,7 +1,39 @@
|
|||||||
ChangeLog
|
ChangeLog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
version 2.3.0 (2017-02-xx)
|
version 2.3.4 (2017-11-02)
|
||||||
|
* Checksums: Use addData function
|
||||||
|
* Packaging: Require ZLIB
|
||||||
|
|
||||||
|
version 2.3.3 (2017-08-29)
|
||||||
|
* Chunking NG: Don't use old chunking on new DAV endpoint (#5855)
|
||||||
|
* Selective Sync: Skip excluded folders when reading DB, don't let them show errors (#5772)
|
||||||
|
* Settings: Make window bigger so Qt version is always visible (#5760)
|
||||||
|
* Share links: Show warning that public link shares are public (#5786)
|
||||||
|
* Downloads: Re-trigger folder discovery on HTTP 404 (#5799)
|
||||||
|
* Notifications: Propagate "Dismiss" as DELETE to server (#5922)
|
||||||
|
* Overlay Icons: Fix potential hangs on Windows
|
||||||
|
* SyncJournalDB: Don't use ._ as filename pattern if that does not work because of SMB storage settings (#5844)
|
||||||
|
* SyncJournalDB: Log reason for sqlite3 opening errors
|
||||||
|
* Windows: Fix a memory leak in FileSystem::longWinPath
|
||||||
|
* Switch Linux build also to Qt 5.6.2 (#5470)
|
||||||
|
* Stopped maintaining Qt 4 buildability
|
||||||
|
|
||||||
|
version 2.3.2 (2017-05-08)
|
||||||
|
* Fix more crashes (thanks to everyone submitting to our crash reporter!)
|
||||||
|
* Improve compatibility with server 10.0 (#5691, OC-Total-Length)
|
||||||
|
* Share dialog: UI improvements, Bring to front on tray click
|
||||||
|
* owncloudcmd: Align process return value with sync return value (#3936)
|
||||||
|
* Fix disk free check on Windows when opening the local DB
|
||||||
|
|
||||||
|
version 2.3.1 (2017-03-21)
|
||||||
|
* Fix several crashes (thanks to everyone submitting to our crash reporter!)
|
||||||
|
* Improve HTTP redirect handling (#5555)
|
||||||
|
* Blacklist: Escalate repeated soft error to normal error (#5500)
|
||||||
|
* NTFS: Do not attempt to upload two existing files with similar casing (#5544)
|
||||||
|
* Fix URL for linking to application password generation for ownCloud 10.0 (#5605)
|
||||||
|
|
||||||
|
version 2.3.0 (2017-03-03)
|
||||||
* Decreased memory usage during sync
|
* Decreased memory usage during sync
|
||||||
* Overlay icons: Lower CPU usage
|
* Overlay icons: Lower CPU usage
|
||||||
* Allow to not sync the server's external storages by default
|
* Allow to not sync the server's external storages by default
|
||||||
|
|||||||
24
Jenkinsfile
vendored
24
Jenkinsfile
vendored
@@ -5,36 +5,20 @@ node('CLIENT') {
|
|||||||
checkout scm
|
checkout scm
|
||||||
sh '''git submodule update --init'''
|
sh '''git submodule update --init'''
|
||||||
|
|
||||||
stage 'Qt4'
|
|
||||||
sh '''rm -rf build
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake -DUNIT_TESTING=1 -DBUILD_WITH_QT4=ON ..
|
|
||||||
make
|
|
||||||
ctest --output-on-failure'''
|
|
||||||
|
|
||||||
stage 'Qt4 - clang'
|
|
||||||
sh '''rm -rf build
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUNIT_TESTING=1 -DBUILD_WITH_QT4=ON ..
|
|
||||||
make
|
|
||||||
ctest --output-on-failure'''
|
|
||||||
|
|
||||||
stage 'Qt5'
|
stage 'Qt5'
|
||||||
sh '''rm -rf build
|
sh '''rm -rf build
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DUNIT_TESTING=1 -DBUILD_WITH_QT4=OFF ..
|
cmake -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 -DWITH_TESTING=1 -DBUILD_WITH_QT4=OFF ..
|
||||||
make
|
make -j4
|
||||||
ctest --output-on-failure'''
|
ctest --output-on-failure'''
|
||||||
|
|
||||||
stage 'Qt5 - clang'
|
stage 'Qt5 - clang'
|
||||||
sh '''rm -rf build
|
sh '''rm -rf build
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUNIT_TESTING=1 -DBUILD_WITH_QT4=OFF ..
|
cmake -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUNIT_TESTING=1 -DWITH_TESTING=1 -DBUILD_WITH_QT4=OFF ..
|
||||||
make
|
make -j4
|
||||||
ctest --output-on-failure'''
|
ctest --output-on-failure'''
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
set( MIRALL_VERSION_MAJOR 2 )
|
set( MIRALL_VERSION_MAJOR 2 )
|
||||||
set( MIRALL_VERSION_MINOR 3 )
|
set( MIRALL_VERSION_MINOR 3 )
|
||||||
set( MIRALL_VERSION_PATCH 0 )
|
set( MIRALL_VERSION_PATCH 4 )
|
||||||
set( MIRALL_VERSION_YEAR 2016 )
|
set( MIRALL_VERSION_YEAR 2017 )
|
||||||
set( MIRALL_SOVERSION 0 )
|
set( MIRALL_SOVERSION 0 )
|
||||||
|
|
||||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||||
set( MIRALL_VERSION_SUFFIX "git") #e.g. beta1, beta2, rc1
|
set( MIRALL_VERSION_SUFFIX "") #e.g. beta1, beta2, rc1
|
||||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||||
|
|
||||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ StrCpy $PageReinstall_NEW_Field_3 "Ez desinstalatu"
|
|||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Dagoeneko Instalatuta"
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Dagoeneko Instalatuta"
|
||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Hautatu nola nahi duzun ${APPLICATION_NAME} instalatzea."
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Hautatu nola nahi duzun ${APPLICATION_NAME} instalatzea."
|
||||||
StrCpy $PageReinstall_OLD_Field_1 "${APPLICATION_NAME}ren bertsio berriago bat instalatuta dago! Ez da aholkatzen bertsio zaharrago bat instalatzea. Benetan bertsio zaharrago hau instalatu nahi baduzu, hobe da lehenengo bertsio berria desinstalatzea. Hautatu nahi duzun aukera eta sakatu Hurrengoa jarraitzeko."
|
StrCpy $PageReinstall_OLD_Field_1 "${APPLICATION_NAME}ren bertsio berriago bat instalatuta dago! Ez da aholkatzen bertsio zaharrago bat instalatzea. Benetan bertsio zaharrago hau instalatu nahi baduzu, hobe da lehenengo bertsio berria desinstalatzea. Hautatu nahi duzun aukera eta sakatu Hurrengoa jarraitzeko."
|
||||||
|
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} dagoeneko instalatuta dago.$\nHautatu zer operazio egin nahi duzu eta klikatu Hurrengoa jarraitzeko."
|
||||||
StrCpy $PageReinstall_SAME_Field_2 "Gehitu/Berrinstalatu osagaiak"
|
StrCpy $PageReinstall_SAME_Field_2 "Gehitu/Berrinstalatu osagaiak"
|
||||||
StrCpy $PageReinstall_SAME_Field_3 "Desinstalatu ${APPLICATION_NAME}"
|
StrCpy $PageReinstall_SAME_Field_3 "Desinstalatu ${APPLICATION_NAME}"
|
||||||
StrCpy $UNINSTALLER_APPDATA_TITLE "Desinstalatu ${APPLICATION_NAME}"
|
StrCpy $UNINSTALLER_APPDATA_TITLE "Desinstalatu ${APPLICATION_NAME}"
|
||||||
@@ -40,4 +41,3 @@ StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Desinstalatzaile honek administratzaile b
|
|||||||
StrCpy $UAC_ERROR_LOGON_SERVICE "Saioa hasteko zerbitzua ez dago martxan, bertan behera uzten!"
|
StrCpy $UAC_ERROR_LOGON_SERVICE "Saioa hasteko zerbitzua ez dago martxan, bertan behera uzten!"
|
||||||
StrCpy $INIT_UNINSTALLER_RUNNING "Desinstalatzailea dagoeneko martxan da."
|
StrCpy $INIT_UNINSTALLER_RUNNING "Desinstalatzailea dagoeneko martxan da."
|
||||||
StrCpy $SectionGroup_Shortcuts "Lasterbideak"
|
StrCpy $SectionGroup_Shortcuts "Lasterbideak"
|
||||||
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ StrCpy $PageReinstall_NEW_Field_3 "
|
|||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Juba paigaldatud"
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Juba paigaldatud"
|
||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Vali, kuidas sa soovid paigaldada ${APPLICATION_NAME}."
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Vali, kuidas sa soovid paigaldada ${APPLICATION_NAME}."
|
||||||
StrCpy $PageReinstall_OLD_Field_1 "Uuem versioon ${APPLICATION_NAME} on juba paigaldatud! Vanema versiooni paigaldus ei ole soovitatav. Kui tõesti tahad paigaldada vanemat versiooni, siis on parem esmalt eemaldada olemasolev. Vali tehtav toiming ning kliki Jätka."
|
StrCpy $PageReinstall_OLD_Field_1 "Uuem versioon ${APPLICATION_NAME} on juba paigaldatud! Vanema versiooni paigaldus ei ole soovitatav. Kui tõesti tahad paigaldada vanemat versiooni, siis on parem esmalt eemaldada olemasolev. Vali tehtav toiming ning kliki Jätka."
|
||||||
|
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} on juba paigaldatud.$\n$\nVali toiming, mida sa tahad sooritada ning kliki jätkamiseks nuppu Next."
|
||||||
StrCpy $PageReinstall_SAME_Field_2 "Lisa/Taaspaigalda komponente"
|
StrCpy $PageReinstall_SAME_Field_2 "Lisa/Taaspaigalda komponente"
|
||||||
StrCpy $PageReinstall_SAME_Field_3 "Desinstalli ${APPLICATION_NAME}"
|
StrCpy $PageReinstall_SAME_Field_3 "Desinstalli ${APPLICATION_NAME}"
|
||||||
StrCpy $UNINSTALLER_APPDATA_TITLE "Desinstalli ${APPLICATION_NAME}"
|
StrCpy $UNINSTALLER_APPDATA_TITLE "Desinstalli ${APPLICATION_NAME}"
|
||||||
@@ -40,4 +41,3 @@ StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "See desinstallija vajab admini ligip
|
|||||||
StrCpy $UAC_ERROR_LOGON_SERVICE "Sisselogimisteenus ei tööta, katkestamine!"
|
StrCpy $UAC_ERROR_LOGON_SERVICE "Sisselogimisteenus ei tööta, katkestamine!"
|
||||||
StrCpy $INIT_UNINSTALLER_RUNNING "See desinstallija on juba käimas."
|
StrCpy $INIT_UNINSTALLER_RUNNING "See desinstallija on juba käimas."
|
||||||
StrCpy $SectionGroup_Shortcuts "Otseteed"
|
StrCpy $SectionGroup_Shortcuts "Otseteed"
|
||||||
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Raccourci de lancement rapide"
|
|||||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Création d'un raccourci de lancement rapide"
|
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Création d'un raccourci de lancement rapide"
|
||||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "Essentiels de ${APPLICATION_NAME}."
|
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "Essentiels de ${APPLICATION_NAME}."
|
||||||
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "Raccourci de ${APPLICATION_NAME}"
|
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "Raccourci de ${APPLICATION_NAME}"
|
||||||
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Raccourci Bureau de ${APPLICATION_NAME}."
|
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Raccourci de bureau pour ${APPLICATION_NAME}."
|
||||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Raccourci de lancement rapide de ${APPLICATION_NAME}."
|
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Raccourci de lancement rapide de ${APPLICATION_NAME}."
|
||||||
StrCpy $UNINSTALLER_FILE_Detail "Écriture du désinstallateur"
|
StrCpy $UNINSTALLER_FILE_Detail "Écriture du désinstallateur"
|
||||||
StrCpy $UNINSTALLER_REGISTRY_Detail "Écriture des clefs de registre du désinstallateur"
|
StrCpy $UNINSTALLER_REGISTRY_Detail "Écriture des clefs de registre du désinstallateur"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ 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_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_KILLING_PROCESSES_TEXT "Terminerer ${APPLICATION_EXECUTABLE}-prosesser."
|
||||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Fant ikke prosess som skulle termineres!"
|
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_1 "En eldre versjon av ${APPLICATION_NAME} er installert på systemet ditt. Det anbefales at du avinstallerer 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_2 "Avinstaller før installering"
|
||||||
StrCpy $PageReinstall_NEW_Field_3 "Ikke avinstaller"
|
StrCpy $PageReinstall_NEW_Field_3 "Ikke avinstaller"
|
||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Allerede installert"
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Allerede installert"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ StrCpy $PageReinstall_NEW_Field_2 "Desinstalar antes de instalar"
|
|||||||
StrCpy $PageReinstall_NEW_Field_3 "Não desinstale"
|
StrCpy $PageReinstall_NEW_Field_3 "Não desinstale"
|
||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Já instalado"
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Já instalado"
|
||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Escolha como pretende instalar ${APPLICATION_NAME}."
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Escolha como pretende instalar ${APPLICATION_NAME}."
|
||||||
StrCpy $PageReinstall_OLD_Field_1 "Uma versão mais recente da aplicação ${APPLICATION_NAME} já está instalada! Não é recomendada a instalação de uma versão mais antiga. Se realmente deseja instalar esta versão, aconselha-se a desinstalação da versão atual primeiro. Selecione a operação que deseja executar e clique em Avançar para continuar."
|
StrCpy $PageReinstall_OLD_Field_1 "Já está instalada uma versão mais recente de ${APPLICATION_NAME}! Não é recomendada a instalação de uma versão mais antiga. Se realmente desejar instalar esta versão antiga, aconselha-se que desinstale primeiro a versão atual. Selecione a operação que deseja executar e clique em $\"Seguinte$\" para continuar."
|
||||||
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} já está instalada.$\nSelecione a operação que deseja realizar e clique em 'Seguinte' para continuar."
|
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} já está instalada.$\nSelecione a operação que deseja realizar e clique em 'Seguinte' para continuar."
|
||||||
StrCpy $PageReinstall_SAME_Field_2 "Adicionar/Reinstalar Componentes"
|
StrCpy $PageReinstall_SAME_Field_2 "Adicionar/Reinstalar Componentes"
|
||||||
StrCpy $PageReinstall_SAME_Field_3 "Desinstalar ${APPLICATION_NAME}"
|
StrCpy $PageReinstall_SAME_Field_3 "Desinstalar ${APPLICATION_NAME}"
|
||||||
@@ -17,8 +17,8 @@ StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Escolha a opção de manute
|
|||||||
StrCpy $SEC_APPLICATION_DETAILS "A instalar o essencial de ${APPLICATION_NAME}."
|
StrCpy $SEC_APPLICATION_DETAILS "A instalar o essencial de ${APPLICATION_NAME}."
|
||||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integração para Windows Explorer"
|
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integração para Windows Explorer"
|
||||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "A instalar integração para Windows Explorer"
|
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "A instalar integração para Windows Explorer"
|
||||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Atalho do progama no Menu Inicial"
|
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Atalho do progama no Menu Iniciar"
|
||||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "A adicionar o atalho de ${APPLICATION_NAME} ao Menu Inicial."
|
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "A adicionar o atalho de ${APPLICATION_NAME} no Menu Iniciar."
|
||||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Atalho da área de trabalho"
|
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Atalho da área de trabalho"
|
||||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "A criar atalhos na área de trabalho"
|
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "A criar atalhos na área de trabalho"
|
||||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Atalho de início rápido"
|
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Atalho de início rápido"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Auto-generated - do not modify
|
# Auto-generated - do not modify
|
||||||
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Mostrar las notas de la versión"
|
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Mostrar las notas de la versión"
|
||||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "El/los proceso/s ${APPLICATION_EXECUTABLE} debe/n ser detenidos.$\n¿Quiere que el instalador lo haga por usted?"
|
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "El/los proceso(s) ${APPLICATION_EXECUTABLE} debe(n) ser detenido(s).$\n¿Quiere que el instalador lo haga por usted?"
|
||||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Deteniendo el/los proceso/s ${APPLICATION_EXECUTABLE}."
|
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Deteniendo el/los proceso(s) ${APPLICATION_EXECUTABLE}."
|
||||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "¡Proceso a finalizar no encontrado!"
|
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "¡Proceso a finalizar no encontrado!"
|
||||||
StrCpy $PageReinstall_NEW_Field_1 "Una versión anterior de ${APPLICATION_NAME} se encuentra instalada en el sistema. Se recomienda de instalar la versión actual antes de instalar la nueva. Seleccione la operacion deseada y haga click en Siguiente para continuar."
|
StrCpy $PageReinstall_NEW_Field_1 "Una versión anterior de ${APPLICATION_NAME} se encuentra instalada en el sistema. Se recomienda desinstalar la versión actual antes de instalar la nueva. Seleccione la operacion deseada y haga click en Siguiente para continuar."
|
||||||
StrCpy $PageReinstall_NEW_Field_2 "Desinstalar antes de instalar"
|
StrCpy $PageReinstall_NEW_Field_2 "Desinstalar antes de instalar"
|
||||||
StrCpy $PageReinstall_NEW_Field_3 "No desinstalar"
|
StrCpy $PageReinstall_NEW_Field_3 "No desinstalar"
|
||||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Ya está instalado"
|
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Ya está instalado"
|
||||||
@@ -17,13 +17,13 @@ StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Elija la opcion de mantenim
|
|||||||
StrCpy $SEC_APPLICATION_DETAILS "Instalando ${APPLICATION_NAME} esenciales."
|
StrCpy $SEC_APPLICATION_DETAILS "Instalando ${APPLICATION_NAME} esenciales."
|
||||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integración para Windows Explorer"
|
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integración para Windows Explorer"
|
||||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Instalando la integración para Windows Explorer"
|
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Instalando la integración para Windows Explorer"
|
||||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Acceso directo al programa Menú de Inicio"
|
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Acceso directo al programa en Menú de Inicio"
|
||||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "Añadiendo accesos directos para ${APPLICATION_NAME} en el Menú de Inicio."
|
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "Añadiendo accesos directos para ${APPLICATION_NAME} en el Menú de Inicio."
|
||||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Acceso directo de Escritorio"
|
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Acceso directo de Escritorio"
|
||||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "Creando accesos directos de escritorio"
|
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "Creando accesos directos de escritorio"
|
||||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Atajo de accceso rápido"
|
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Atajo de acceso rápido"
|
||||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Creando un Acceso Directo al Lanzador Rápido"
|
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Creando un Acceso Directo al Lanzador Rápido"
|
||||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} esencial."
|
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} esenciales."
|
||||||
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "Acceso Directo de ${APPLICATION_NAME}"
|
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "Acceso Directo de ${APPLICATION_NAME}"
|
||||||
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Acceso Directo de Escritorio para ${APPLICATION_NAME}"
|
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Acceso Directo de Escritorio para ${APPLICATION_NAME}"
|
||||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Lanzador Rápido de Accesos Director para ${APPLICATION_NAME}."
|
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Lanzador Rápido de Accesos Director para ${APPLICATION_NAME}."
|
||||||
|
|||||||
2
binary
2
binary
Submodule binary updated: 741b49156b...1818b48380
@@ -9,9 +9,19 @@
|
|||||||
# QTKEYCHAIN_LIBRARIES - The libraries needed to use QtKeychain
|
# QTKEYCHAIN_LIBRARIES - The libraries needed to use QtKeychain
|
||||||
# QTKEYCHAIN_DEFINITIONS - Compiler switches required for using LibXml2
|
# QTKEYCHAIN_DEFINITIONS - Compiler switches required for using LibXml2
|
||||||
|
|
||||||
|
# When we build our own Qt we also need to build QtKeychain with it
|
||||||
|
# so that it doesn't pull a different Qt version. For that reason
|
||||||
|
# first look in the Qt lib directory for QtKeychain.
|
||||||
|
get_target_property(_QTCORE_LIB_PATH Qt5::Core IMPORTED_LOCATION_RELEASE)
|
||||||
|
|
||||||
|
# Use PATH here because Debian 7.0 has CMake 2.8.9 and DIRECTORY is only available from 2.8.12+
|
||||||
|
get_filename_component(QT_LIB_DIR "${_QTCORE_LIB_PATH}" PATH)
|
||||||
|
|
||||||
find_path(QTKEYCHAIN_INCLUDE_DIR
|
find_path(QTKEYCHAIN_INCLUDE_DIR
|
||||||
NAMES
|
NAMES
|
||||||
keychain.h
|
keychain.h
|
||||||
|
HINTS
|
||||||
|
${QT_LIB_DIR}/../include
|
||||||
PATH_SUFFIXES
|
PATH_SUFFIXES
|
||||||
qt5keychain
|
qt5keychain
|
||||||
)
|
)
|
||||||
@@ -20,6 +30,8 @@ find_library(QTKEYCHAIN_LIBRARY
|
|||||||
NAMES
|
NAMES
|
||||||
qt5keychain
|
qt5keychain
|
||||||
lib5qtkeychain
|
lib5qtkeychain
|
||||||
|
HINTS
|
||||||
|
${QT_LIB_DIR}
|
||||||
PATHS
|
PATHS
|
||||||
/usr/lib
|
/usr/lib
|
||||||
/usr/lib/${CMAKE_ARCH_TRIPLET}
|
/usr/lib/${CMAKE_ARCH_TRIPLET}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
# (c) 2014 Copyright ownCloud GmbH
|
|
||||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
|
||||||
# For details see the accompanying COPYING* file.
|
|
||||||
|
|
||||||
# - Try to find QtKeychain
|
|
||||||
# Once done this will define
|
|
||||||
# QTKEYCHAIN_FOUND - System has QtKeychain
|
|
||||||
# QTKEYCHAIN_INCLUDE_DIRS - The QtKeychain include directories
|
|
||||||
# QTKEYCHAIN_LIBRARIES - The libraries needed to use QtKeychain
|
|
||||||
# QTKEYCHAIN_DEFINITIONS - Compiler switches required for using LibXml2
|
|
||||||
|
|
||||||
find_path(QTKEYCHAIN_INCLUDE_DIR
|
|
||||||
NAMES
|
|
||||||
keychain.h
|
|
||||||
PATH_SUFFIXES
|
|
||||||
qtkeychain
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
find_library(QTKEYCHAIN_LIBRARY
|
|
||||||
NAMES
|
|
||||||
qtkeychain
|
|
||||||
libqtkeychain
|
|
||||||
PATHS
|
|
||||||
/usr/lib
|
|
||||||
/usr/lib/${CMAKE_ARCH_TRIPLET}
|
|
||||||
/usr/local/lib
|
|
||||||
/opt/local/lib
|
|
||||||
${CMAKE_LIBRARY_PATH}
|
|
||||||
${CMAKE_INSTALL_PREFIX}/lib
|
|
||||||
)
|
|
||||||
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
# handle the QUIETLY and REQUIRED arguments and set QTKEYCHAIN_FOUND to TRUE
|
|
||||||
# if all listed variables are TRUE
|
|
||||||
find_package_handle_standard_args(QtKeychain DEFAULT_MSG
|
|
||||||
QTKEYCHAIN_LIBRARY QTKEYCHAIN_INCLUDE_DIR)
|
|
||||||
|
|
||||||
mark_as_advanced(QTKEYCHAIN_INCLUDE_DIR QTKEYCHAIN_LIBRARY)
|
|
||||||
@@ -116,8 +116,9 @@ ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll"
|
|||||||
!define MUI_HEADERIMAGE
|
!define MUI_HEADERIMAGE
|
||||||
!define MUI_HEADERIMAGE_BITMAP ${WIN_SETUP_BITMAP_PATH}/page_header.bmp
|
!define MUI_HEADERIMAGE_BITMAP ${WIN_SETUP_BITMAP_PATH}/page_header.bmp
|
||||||
!define MUI_COMPONENTSPAGE_SMALLDESC
|
!define MUI_COMPONENTSPAGE_SMALLDESC
|
||||||
!define MUI_FINISHPAGE_LINK "${APPLICATION_DOMAIN}"
|
; We removed this, h1 issue 191687
|
||||||
!define MUI_FINISHPAGE_LINK_LOCATION "http://${APPLICATION_DOMAIN}"
|
;!define MUI_FINISHPAGE_LINK "${APPLICATION_DOMAIN}"
|
||||||
|
;!define MUI_FINISHPAGE_LINK_LOCATION "http://${APPLICATION_DOMAIN}"
|
||||||
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
|
!define MUI_FINISHPAGE_NOREBOOTSUPPORT
|
||||||
!ifdef OPTION_FINISHPAGE_RELEASE_NOTES
|
!ifdef OPTION_FINISHPAGE_RELEASE_NOTES
|
||||||
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
|
!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED
|
||||||
|
|||||||
@@ -235,6 +235,11 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
|||||||
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
rc = csync_fnmatch(".sync_*.db*", bname, 0);
|
||||||
|
if (rc == 0) {
|
||||||
|
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
rc = csync_fnmatch(".csync_journal.db*", bname, 0);
|
rc = csync_fnmatch(".csync_journal.db*", bname, 0);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
match = CSYNC_FILE_SILENTLY_EXCLUDED;
|
||||||
|
|||||||
@@ -439,7 +439,7 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
|||||||
* In other words, anything that is between path+'/' and path+'0',
|
* In other words, anything that is between path+'/' and path+'0',
|
||||||
* (because '0' follows '/' in ascii)
|
* (because '0' follows '/' in ascii)
|
||||||
*/
|
*/
|
||||||
const char *below_path_query = "SELECT " METADATA_COLUMNS " FROM metadata WHERE path > (?||'/') AND path < (?||'0')";
|
const char *below_path_query = "SELECT " METADATA_COLUMNS " FROM metadata WHERE path > (?||'/') AND path < (?||'0') ORDER BY path||'/' ASC";
|
||||||
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL));
|
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL));
|
||||||
ctx->statedb.lastReturnValue = rc;
|
ctx->statedb.lastReturnValue = rc;
|
||||||
if( rc != SQLITE_OK ) {
|
if( rc != SQLITE_OK ) {
|
||||||
@@ -462,6 +462,36 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
|||||||
|
|
||||||
rc = _csync_file_stat_from_metadata_table( &st, stmt);
|
rc = _csync_file_stat_from_metadata_table( &st, stmt);
|
||||||
if( st ) {
|
if( st ) {
|
||||||
|
/* When selective sync is used, the database may have subtrees with a parent
|
||||||
|
* whose etag (md5) is _invalid_. These are ignored and shall not appear in the
|
||||||
|
* remote tree.
|
||||||
|
* Sometimes folders that are not ignored by selective sync get marked as
|
||||||
|
* _invalid_, but that is not a problem as the next discovery will retrieve
|
||||||
|
* their correct etags again and we don't run into this case.
|
||||||
|
*/
|
||||||
|
if( c_streq(st->etag, "_invalid_") ) {
|
||||||
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded", st->path);
|
||||||
|
char *skipbase = c_strdup(st->path);
|
||||||
|
skipbase[st->pathlen] = '/';
|
||||||
|
int skiplen = st->pathlen + 1;
|
||||||
|
|
||||||
|
/* Skip over all entries with the same base path. Note that this depends
|
||||||
|
* strongly on the ordering of the retrieved items. */
|
||||||
|
do {
|
||||||
|
csync_file_stat_free(st);
|
||||||
|
rc = _csync_file_stat_from_metadata_table( &st, stmt);
|
||||||
|
if( st && strncmp(st->path, skipbase, skiplen) != 0 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CSYNC_LOG(CSYNC_LOG_PRIORITY_TRACE, "%s selective sync excluded because the parent is", st->path);
|
||||||
|
} while( rc == SQLITE_ROW );
|
||||||
|
|
||||||
|
/* End of data? */
|
||||||
|
if( rc != SQLITE_ROW || !st ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for exclusion from the tree.
|
/* Check for exclusion from the tree.
|
||||||
* Note that this is only a safety net in case the ignore list changes
|
* Note that this is only a safety net in case the ignore list changes
|
||||||
* without a full remote discovery being triggered. */
|
* without a full remote discovery being triggered. */
|
||||||
|
|||||||
@@ -157,7 +157,16 @@ static void check_csync_excluded(void **state)
|
|||||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||||
rc = csync_excluded_no_ctx(csync->excludes, "subdir/._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
|
rc = csync_excluded_no_ctx(csync->excludes, "subdir/._sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
|
||||||
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||||
|
|
||||||
|
rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
|
||||||
|
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||||
|
rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db.ctmp", CSYNC_FTW_TYPE_FILE);
|
||||||
|
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||||
|
rc = csync_excluded_no_ctx(csync->excludes, ".sync_5bdd60bdfcfa.db-shm", CSYNC_FTW_TYPE_FILE);
|
||||||
|
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||||
|
rc = csync_excluded_no_ctx(csync->excludes, "subdir/.sync_5bdd60bdfcfa.db", CSYNC_FTW_TYPE_FILE);
|
||||||
|
assert_int_equal(rc, CSYNC_FILE_SILENTLY_EXCLUDED);
|
||||||
|
|
||||||
|
|
||||||
/* pattern ]*.directory - ignore and remove */
|
/* pattern ]*.directory - ignore and remove */
|
||||||
rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE);
|
rc = csync_excluded_no_ctx(csync->excludes, "my.~directory", CSYNC_FTW_TYPE_FILE);
|
||||||
|
|||||||
@@ -16,20 +16,50 @@ format. You can overwrite changes using the ownCloud configuration dialog.
|
|||||||
.. note:: Use caution when making changes to the ownCloud Client configuration
|
.. note:: Use caution when making changes to the ownCloud Client configuration
|
||||||
file. Incorrect settings can produce unintended results.
|
file. Incorrect settings can produce unintended results.
|
||||||
|
|
||||||
You can change the following configuration settings in the ``[ownCloud]`` section:
|
Some interesting values that can be set on the configuration file are:
|
||||||
|
|
||||||
- ``remotePollInterval`` (default: ``30000``) -- Specifies the poll time for the remote repository in milliseconds.
|
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``[ownCloud]`` section |
|
||||||
|
+=================================+===============+========================================================================================================+
|
||||||
|
| Variable | Default | Meaning |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``remotePollInterval`` | ``30000`` | Specifies the poll time for the remote repository in milliseconds. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``forceSyncInterval`` | ``7200000`` | The duration of no activity after which a synchronization run shall be triggered automatically. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``notificationRefreshInterval`` | ``300000`` | Specifies the default interval of checking for new server notifications in milliseconds. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
- ``forceSyncInterval`` (default: ``7200000``) -- The duration of no activity after which a synchronization run shall be triggered automatically.
|
|
||||||
|
|
||||||
- ``notificationRefreshInterval`` (default: ``300000``) -- Specifies the default interval of checking for new server notifications in milliseconds.
|
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``[General]`` section |
|
||||||
|
+=================================+===============+========================================================================================================+
|
||||||
|
| Variable | Default | Meaning |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``chunkSize`` | ``5242880`` | Specifies the chunk size of uploaded files in bytes. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``promptDeleteAllFiles`` | ``true`` | If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``maxLogLines`` | ``20000`` | Specifies the maximum number of log lines displayed in the log window. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``timeout`` | ``300`` | The timeout for network connections in seconds. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
|
||||||
You can change the following configuration settings in the ``[General]`` section:
|
|
||||||
|
|
||||||
- ``chunkSize`` (default: ``5242880``) -- Specifies the chunk size of uploaded files in bytes.
|
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``[Proxy]`` section |
|
||||||
- ``promptDeleteAllFiles`` (default: ``true``) -- If a UI prompt should ask for confirmation if it was detected that all files and folders were deleted.
|
+=================================+===============+========================================================================================================+
|
||||||
|
| Variable | Default | Meaning |
|
||||||
- ``maxLogLines`` (default: ``20000``) -- Specifies the maximum number of log lines displayed in the log window.
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``host`` | ``127.0.0.1`` | The address of the proxy server. |
|
||||||
- ``timeout`` (default: ``300``) -- The timeout for network connections in seconds.
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``port`` | ``8080`` | The port were the proxy is listening. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
|
| ``type`` | ``2`` | ``0`` for System Proxy. |
|
||||||
|
+ + +--------------------------------------------------------------------------------------------------------+
|
||||||
|
| | | ``1`` for SOCKS5 Proxy. |
|
||||||
|
+ + +--------------------------------------------------------------------------------------------------------+
|
||||||
|
| | | ``2`` for No Proxy. |
|
||||||
|
+ + +--------------------------------------------------------------------------------------------------------+
|
||||||
|
| | | ``3`` for HTTP(S) Proxy. |
|
||||||
|
+---------------------------------+---------------+--------------------------------------------------------------------------------------------------------+
|
||||||
29
doc/faq.rst
29
doc/faq.rst
@@ -18,3 +18,32 @@ The sync client has been intentionally limited to sync no deeper than
|
|||||||
fifty sub-directories, to help prevent memory problems.
|
fifty sub-directories, to help prevent memory problems.
|
||||||
Unfortunately, it, *currently*, does not report an error when this occurs.
|
Unfortunately, it, *currently*, does not report an error when this occurs.
|
||||||
However, a UI notification is planned for a future release of ownCloud.
|
However, a UI notification is planned for a future release of ownCloud.
|
||||||
|
|
||||||
|
I want to move my local sync folder
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
|
The ownCloud desktop client does not provide a way to change the local sync directory.
|
||||||
|
However, it can be done, though it is a bit unorthodox.
|
||||||
|
Specifically, you have to:
|
||||||
|
|
||||||
|
1. Remove the existing connection which syncs to the wrong directory
|
||||||
|
2. Add a new connection which syncs to the desired directory
|
||||||
|
|
||||||
|
image:: images/setup/ownCloud-remove_existing_connection.png
|
||||||
|
|
||||||
|
To do so, in the client UI, which you can see above, click the "**Account**" drop-down menu and then click "Remove".
|
||||||
|
This will display a "**Confirm Account Removal**" dialog window.
|
||||||
|
|
||||||
|
image:: images/setup/ownCloud-remove_existing_connection_confirmation_dialog.png
|
||||||
|
|
||||||
|
If you're sure, click "**Remove connection**".
|
||||||
|
|
||||||
|
Then, click the Account drop-down menu again, and this time click "**Add new**".
|
||||||
|
|
||||||
|
image:: images/setup/ownCloud-replacement_connection_wizard.png
|
||||||
|
|
||||||
|
This opens the ownCloud Connection Wizard, which you can see above, *but* with an extra option.
|
||||||
|
This option provides the ability to either: keep the existing data (synced by the previous connection) or to start a clean sync (erasing the existing data).
|
||||||
|
|
||||||
|
Make your choice and click "**Connect...**".
|
||||||
|
This will then step you through the Connection Wizard, just as you did when you setup the previous sync connection, but giving you the opportunity to choose a new sync directory.
|
||||||
|
|||||||
BIN
doc/images/setup/ownCloud-remove_existing_connection.png
Normal file
BIN
doc/images/setup/ownCloud-remove_existing_connection.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
doc/images/setup/ownCloud-replacement_connection_wizard.png
Executable file
BIN
doc/images/setup/ownCloud-replacement_connection_wizard.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -60,10 +60,9 @@ Other command line switches supported by ``owncloudcmd`` include the following:
|
|||||||
Credential Handling
|
Credential Handling
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
``owncloudcmd`` uses the credentials of the GUI synchronization client.
|
``owncloudcmd`` requires the user to specify the username and password using the standard URL pattern, e.g.,
|
||||||
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::
|
|
||||||
|
|
||||||
$ owncloudcmd /home/user/my_sync_folder https://carla:secret@server/owncloud/remote.php/webdav/
|
$ owncloudcmd /home/user/my_sync_folder https://carla:secret@server/owncloud/remote.php/webdav/
|
||||||
|
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ Identifying Basic Functionality Problems
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
If you see this error message stop your client, delete the
|
If you see this error message stop your client, delete the
|
||||||
``.csync_journal.db`` file, and then restart your client.
|
``._sync_xxxxxxx.db`` file, and then restart your client.
|
||||||
There is a ``.csync_journal.db`` file inside the folder of every account
|
There is a hidden ``._sync_xxxxxxx.db`` file inside the folder of every account
|
||||||
configured on your client.
|
configured on your client.
|
||||||
|
|
||||||
.. NOTE::
|
.. NOTE::
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Please try to only report a bug if it happens with the latest version
|
Please try to only report a bug if it happens with the latest version
|
||||||
The latest version can be seen by checking the ChangeLog: https://owncloud.org/changelog/desktop/
|
The latest version can be seen by checking the ChangeLog: https://owncloud.org/changelog/desktop/
|
||||||
|
|
||||||
For support try: https://central.owncloud.org/c/help/desktop-file-sync
|
For support try: https://central.owncloud.org/c/desktop-client
|
||||||
--->
|
--->
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,481 +9,6 @@ Icon=@APPLICATION_EXECUTABLE@
|
|||||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||||
X-GNOME-Autostart-Delay=3
|
X-GNOME-Autostart-Delay=3
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
|
|
||||||
|
|
||||||
@@ -763,6 +288,10 @@ Comment[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
|
|||||||
GenericName[ar]=مزامنة المجلد
|
GenericName[ar]=مزامنة المجلد
|
||||||
Name[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
|
Name[ar]=@APPLICATION_NAME@ زبون مزامنة مكتبي
|
||||||
Icon[ar]=@APPLICATION_EXECUTABLE@
|
Icon[ar]=@APPLICATION_EXECUTABLE@
|
||||||
|
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
|
||||||
|
GenericName[bg_BG]=Синхронизиране на папката
|
||||||
|
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
|
||||||
|
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
|
||||||
Comment[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
Comment[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
||||||
GenericName[ca]=Sincronització de carpetes
|
GenericName[ca]=Sincronització de carpetes
|
||||||
Name[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
Name[ca]=Client de sincronització d'escriptori @APPLICATION_NAME@
|
||||||
@@ -795,17 +324,21 @@ Comment[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
|||||||
GenericName[de_DE]=Ordner-Synchronisation
|
GenericName[de_DE]=Ordner-Synchronisation
|
||||||
Name[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
Name[de_DE]=@APPLICATION_NAME@ Desktop-Synchronisationsclient
|
||||||
Icon[de_DE]=@APPLICATION_EXECUTABLE@
|
Icon[de_DE]=@APPLICATION_EXECUTABLE@
|
||||||
Comment[bg_BG]=@APPLICATION_NAME@ клиент за десктоп синхронизация
|
Comment[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
|
||||||
GenericName[bg_BG]=Синхронизиране на папката
|
GenericName[eu]=Karpetaren sinkronizazioa
|
||||||
Name[bg_BG]=@APPLICATION_NAME@ клиент десктоп синхронизация
|
Name[eu]=@APPLICATION_NAME@ mahaigaineko sinkronizazio bezeroa
|
||||||
Icon[bg_BG]=@APPLICATION_EXECUTABLE@
|
Icon[eu]=@APPLICATION_EXECUTABLE@
|
||||||
GenericName[fa]=همسان سازی پوشهها
|
GenericName[fa]=همسان سازی پوشهها
|
||||||
Name[fa]=@APPLICATION_EXECUTABLE@ نسخهی همسان سازی مشتری
|
Name[fa]=@APPLICATION_EXECUTABLE@ نسخهی همسان سازی مشتری
|
||||||
Icon[fa]=@APPLICATION_EXECUTABLE@
|
Icon[fa]=@APPLICATION_EXECUTABLE@
|
||||||
Comment[fr]=@APPLICATION_NAME@ synchronisation du client
|
Comment[fr]=Synchronisez vos dossiers avec un serveur @APPLICATION_NAME@
|
||||||
GenericName[fr]=Dossier de Synchronisation
|
GenericName[fr]=Synchronisation de dossier
|
||||||
Name[fr]=@APPLICATION_NAME@ synchronisation du client
|
Name[fr]=Client de synchronisation @APPLICATION_NAME@
|
||||||
Icon[fr]=@APPLICATION_EXECUTABLE@
|
Icon[fr]=@APPLICATION_EXECUTABLE@
|
||||||
|
Comment[gl]=@APPLICATION_NAME@ cliente de sincronización para escritorio
|
||||||
|
GenericName[gl]=Sincronizar Cartafol
|
||||||
|
Name[gl]=@APPLICATION_NAME@ cliente de sincronización para escritorio
|
||||||
|
Icon[gl]=@APPLICATION_EXECUTABLE@
|
||||||
Comment[he]=@APPLICATION_NAME@ לקוח סנכון שולחן עבודה
|
Comment[he]=@APPLICATION_NAME@ לקוח סנכון שולחן עבודה
|
||||||
GenericName[he]=סנכון תיקייה
|
GenericName[he]=סנכון תיקייה
|
||||||
Name[he]=@APPLICATION_NAME@ לקוח סנכרון שולחן עבודה
|
Name[he]=@APPLICATION_NAME@ לקוח סנכרון שולחן עבודה
|
||||||
@@ -833,6 +366,10 @@ Comment[hu_HU]=@APPLICATION_NAME@ asztali szinkronizációs kliens
|
|||||||
GenericName[hu_HU]=Könyvtár szinkronizálás
|
GenericName[hu_HU]=Könyvtár szinkronizálás
|
||||||
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkr. kliens
|
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkr. kliens
|
||||||
Icon[hu_HU]=@APPLICATION_EXECUTABLE@
|
Icon[hu_HU]=@APPLICATION_EXECUTABLE@
|
||||||
|
Comment[af_ZA]=@APPLICATION_NAME@ werkskermsinchroniseerkliënt
|
||||||
|
GenericName[af_ZA]=Vouersinchronisering
|
||||||
|
Name[af_ZA]=@APPLICATION_NAME@ werkskermsinchroniseerkliënt
|
||||||
|
Icon[af_ZA]=@APPLICATION_EXECUTABLE@
|
||||||
Comment[nl]=@APPLICATION_NAME@ desktop synchronisatie client
|
Comment[nl]=@APPLICATION_NAME@ desktop synchronisatie client
|
||||||
GenericName[nl]=Mappen sync
|
GenericName[nl]=Mappen sync
|
||||||
Name[nl]=@APPLICATION_NAME@ desktop sync client
|
Name[nl]=@APPLICATION_NAME@ desktop sync client
|
||||||
@@ -889,7 +426,15 @@ Comment[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端
|
|||||||
GenericName[zh_CN]=文件夹同步
|
GenericName[zh_CN]=文件夹同步
|
||||||
Name[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端
|
Name[zh_CN]=@APPLICATION_NAME@ 桌面同步客户端
|
||||||
Icon[zh_CN]=@APPLICATION_EXECUTABLE@
|
Icon[zh_CN]=@APPLICATION_EXECUTABLE@
|
||||||
|
Comment[zh_HK]=桌面版同步客户端
|
||||||
|
Comment[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||||
GenericName[zh_TW]=資料夾同步
|
GenericName[zh_TW]=資料夾同步
|
||||||
|
Name[zh_TW]=@APPLICATION_NAME@ 桌面同步客戶端
|
||||||
|
Icon[zh_TW]=@APPLICATION_EXECUTABLE@
|
||||||
|
Comment[es_AR]=Cliente de sincronización para escritorio @APPLICATION_NAME@
|
||||||
|
GenericName[es_AR]=Sincronización de directorio
|
||||||
|
Name[es_AR]=Cliente de sincronización para escritorio @APPLICATION_NAME@
|
||||||
|
Icon[es_AR]=@APPLICATION_EXECUTABLE@
|
||||||
Comment[lt_LT]=@APPLICATION_NAME@ darbalaukio sinchronizavimo programa
|
Comment[lt_LT]=@APPLICATION_NAME@ darbalaukio sinchronizavimo programa
|
||||||
GenericName[lt_LT]=Katalogo sinchnorizacija
|
GenericName[lt_LT]=Katalogo sinchnorizacija
|
||||||
Name[lt_LT]=@APPLICATION_NAME@ darbalaukio programa
|
Name[lt_LT]=@APPLICATION_NAME@ darbalaukio programa
|
||||||
|
|||||||
@@ -172,9 +172,10 @@ void RemotePathChecker::workerThreadLoop()
|
|||||||
|
|
||||||
|
|
||||||
RemotePathChecker::RemotePathChecker()
|
RemotePathChecker::RemotePathChecker()
|
||||||
: _watchedDirectories(make_shared<const vector<wstring>>())
|
: _stop(false)
|
||||||
|
, _watchedDirectories(make_shared<const vector<wstring>>())
|
||||||
, _connected(false)
|
, _connected(false)
|
||||||
, _newQueries(CreateEvent(NULL, true, true, NULL))
|
, _newQueries(CreateEvent(NULL, FALSE, FALSE, NULL))
|
||||||
, _thread([this]{ this->workerThreadLoop(); })
|
, _thread([this]{ this->workerThreadLoop(); })
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
// This is the number that will end up in the version window of the DLLs.
|
// This is the number that will end up in the version window of the DLLs.
|
||||||
// Increment this version before committing a new build if you are today's shell_integration build master.
|
// Increment this version before committing a new build if you are today's shell_integration build master.
|
||||||
#define OCEXT_BUILD_NUM 44
|
#define OCEXT_BUILD_NUM 45
|
||||||
|
|
||||||
#define STRINGIZE2(s) #s
|
#define STRINGIZE2(s) #s
|
||||||
#define STRINGIZE(s) STRINGIZE2(s)
|
#define STRINGIZE(s) STRINGIZE2(s)
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ restart_sync:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Cmd cmd;
|
Cmd cmd;
|
||||||
QString dbPath = options.source_dir + SyncJournalDb::makeDbName(credentialFreeUrl, folder, user);
|
QString dbPath = options.source_dir + SyncJournalDb::makeDbName(options.source_dir, credentialFreeUrl, folder, user);
|
||||||
SyncJournalDb db(dbPath);
|
SyncJournalDb db(dbPath);
|
||||||
|
|
||||||
if (!selectiveSyncList.empty()) {
|
if (!selectiveSyncList.empty()) {
|
||||||
@@ -478,7 +478,12 @@ restart_sync:
|
|||||||
|
|
||||||
SyncEngine engine(account, options.source_dir, folder, &db);
|
SyncEngine engine(account, options.source_dir, folder, &db);
|
||||||
engine.setIgnoreHiddenFiles(options.ignoreHiddenFiles);
|
engine.setIgnoreHiddenFiles(options.ignoreHiddenFiles);
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
QObject::connect(&engine, &SyncEngine::finished,
|
||||||
|
[&app](bool result) { app.exit(result ? EXIT_SUCCESS : EXIT_FAILURE); });
|
||||||
|
#else
|
||||||
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
|
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
|
||||||
|
#endif
|
||||||
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
|
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
|
||||||
|
|
||||||
|
|
||||||
@@ -505,7 +510,7 @@ restart_sync:
|
|||||||
// Have to be done async, else, an error before exec() does not terminate the event loop.
|
// Have to be done async, else, an error before exec() does not terminate the event loop.
|
||||||
QMetaObject::invokeMethod(&engine, "startSync", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(&engine, "startSync", Qt::QueuedConnection);
|
||||||
|
|
||||||
app.exec();
|
int resultCode = app.exec();
|
||||||
|
|
||||||
if (engine.isAnotherSyncNeeded() != NoFollowUpSync) {
|
if (engine.isAnotherSyncNeeded() != NoFollowUpSync) {
|
||||||
if (restartCount < options.restartTimes) {
|
if (restartCount < options.restartTimes) {
|
||||||
@@ -516,6 +521,6 @@ restart_sync:
|
|||||||
qWarning() << "Another sync is needed, but not done because restart count is exceeded" << restartCount;
|
qWarning() << "Another sync is needed, but not done because restart count is exceeded" << restartCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return resultCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -313,10 +313,9 @@ void AccountSettings::slotFolderWizardAccepted()
|
|||||||
tr("<p>Could not create local folder <i>%1</i>.")
|
tr("<p>Could not create local folder <i>%1</i>.")
|
||||||
.arg(QDir::toNativeSeparators(definition.localPath)));
|
.arg(QDir::toNativeSeparators(definition.localPath)));
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
FileSystem::setFolderMinimumPermissions(definition.localPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FileSystem::setFolderMinimumPermissions(definition.localPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* take the value from the definition of already existing folders. All folders have
|
/* take the value from the definition of already existing folders. All folders have
|
||||||
|
|||||||
@@ -281,9 +281,12 @@ void AccountState::slotCredentialsFetched(AbstractCredentials* credentials)
|
|||||||
|
|
||||||
_waitingForNewCredentials = false;
|
_waitingForNewCredentials = false;
|
||||||
|
|
||||||
// When new credentials become available we always want to restart the
|
if (_connectionValidator) {
|
||||||
// connection validation, even if it's currently running.
|
// When new credentials become available we always want to restart the
|
||||||
delete _connectionValidator;
|
// connection validation, even if it's currently running.
|
||||||
|
_connectionValidator->deleteLater();
|
||||||
|
_connectionValidator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
checkConnectivity();
|
checkConnectivity();
|
||||||
}
|
}
|
||||||
@@ -298,9 +301,12 @@ void AccountState::slotCredentialsAsked(AbstractCredentials* credentials)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// When new credentials become available we always want to restart the
|
if (_connectionValidator) {
|
||||||
// connection validation, even if it's currently running.
|
// When new credentials become available we always want to restart the
|
||||||
delete _connectionValidator;
|
// connection validation, even if it's currently running.
|
||||||
|
_connectionValidator->deleteLater();
|
||||||
|
_connectionValidator = 0;
|
||||||
|
}
|
||||||
|
|
||||||
checkConnectivity();
|
checkConnectivity();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@
|
|||||||
#include "activitydata.h"
|
#include "activitydata.h"
|
||||||
#include "activitylistmodel.h"
|
#include "activitylistmodel.h"
|
||||||
|
|
||||||
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
Q_DECLARE_METATYPE(QPointer<OCC::AccountState>)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
ActivityListModel::ActivityListModel(QWidget *parent)
|
ActivityListModel::ActivityListModel(QWidget *parent)
|
||||||
@@ -43,6 +47,8 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
a = _finalList.at(index.row());
|
a = _finalList.at(index.row());
|
||||||
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
|
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
|
||||||
|
if (!ast)
|
||||||
|
return QVariant();
|
||||||
QStringList list;
|
QStringList list;
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
@@ -122,7 +128,7 @@ void ActivityListModel::startFetchJob(AccountState* s)
|
|||||||
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
|
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
|
||||||
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
|
QObject::connect(job, SIGNAL(jsonReceived(QVariantMap, int)),
|
||||||
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
|
this, SLOT(slotActivitiesReceived(QVariantMap, int)));
|
||||||
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
|
job->setProperty("AccountStatePtr", QVariant::fromValue<QPointer<AccountState>>(s));
|
||||||
|
|
||||||
QList< QPair<QString,QString> > params;
|
QList< QPair<QString,QString> > params;
|
||||||
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
|
params.append(qMakePair(QString::fromLatin1("page"), QString::fromLatin1("0")));
|
||||||
@@ -139,7 +145,10 @@ void ActivityListModel::slotActivitiesReceived(const QVariantMap& json, int stat
|
|||||||
auto activities = json.value("ocs").toMap().value("data").toList();
|
auto activities = json.value("ocs").toMap().value("data").toList();
|
||||||
|
|
||||||
ActivityList list;
|
ActivityList list;
|
||||||
AccountState* ast = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
|
auto ast = qvariant_cast<QPointer<AccountState>>(sender()->property("AccountStatePtr"));
|
||||||
|
if (!ast)
|
||||||
|
return;
|
||||||
|
|
||||||
_currentlyFetching.remove(ast);
|
_currentlyFetching.remove(ast);
|
||||||
|
|
||||||
foreach( auto activ, activities ) {
|
foreach( auto activ, activities ) {
|
||||||
|
|||||||
@@ -438,14 +438,13 @@ void ActivityWidget::slotNotifyServerFinished( const QString& reply, int replyCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
endNotificationRequest(job->widget(), replyCode);
|
endNotificationRequest(job->widget(), replyCode);
|
||||||
// FIXME: remove the widget after a couple of seconds
|
|
||||||
qDebug() << Q_FUNC_INFO << "Server Notification reply code"<< replyCode << reply;
|
qDebug() << Q_FUNC_INFO << "Server Notification reply code"<< replyCode << reply;
|
||||||
|
|
||||||
// if the notification was successful start a timer that triggers
|
// if the notification was successful start a timer that triggers
|
||||||
// removal of the done widgets in a few seconds
|
// removal of the done widgets in a few seconds
|
||||||
// Add 200 millisecs to the predefined value to make sure that the timer in
|
// Add 200 millisecs to the predefined value to make sure that the timer in
|
||||||
// widget's method readyToClose() has elapsed.
|
// widget's method readyToClose() has elapsed.
|
||||||
if( replyCode == OCS_SUCCESS_STATUS_CODE ) {
|
if( replyCode == OCS_SUCCESS_STATUS_CODE || replyCode == OCS_SUCCESS_STATUS_CODE_V2 ) {
|
||||||
scheduleWidgetToRemove( job->widget() );
|
scheduleWidgetToRemove( job->widget() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "updater/ocupdater.h"
|
#include "updater/ocupdater.h"
|
||||||
#include "excludedfiles.h"
|
#include "excludedfiles.h"
|
||||||
#include "owncloudsetupwizard.h"
|
#include "owncloudsetupwizard.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@@ -52,6 +53,8 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
#include <openssl/crypto.h>
|
||||||
|
|
||||||
class QSocket;
|
class QSocket;
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
@@ -508,7 +511,13 @@ void Application::showVersion()
|
|||||||
stream << _theme->appName().toLatin1().constData()
|
stream << _theme->appName().toLatin1().constData()
|
||||||
<< QLatin1String(" version ")
|
<< QLatin1String(" version ")
|
||||||
<< _theme->version().toLatin1().constData() << endl;
|
<< _theme->version().toLatin1().constData() << endl;
|
||||||
stream << "Using Qt " << qVersion() << endl;
|
#ifdef GIT_SHA1
|
||||||
|
stream << "Git revision " << GIT_SHA1 << endl;
|
||||||
|
#endif
|
||||||
|
stream << "Using Qt " << qVersion() << ", built against Qt " << QT_VERSION_STR << endl;
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
stream << "Using '" << QSslSocket::sslLibraryVersionString() << "'" << endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
displayHelpText(helpText);
|
displayHelpText(helpText);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,13 +70,21 @@ void HttpCredentialsGui::askFromUserAsync()
|
|||||||
|
|
||||||
QString HttpCredentialsGui::requestAppPasswordText(const Account* account)
|
QString HttpCredentialsGui::requestAppPasswordText(const Account* account)
|
||||||
{
|
{
|
||||||
if (account->serverVersionInt() < 0x090100) {
|
int version = account->serverVersionInt();
|
||||||
// Older server than 9.1 does not have trhe feature to request App Password
|
QString path;
|
||||||
|
|
||||||
|
// Version may not be available before login on new servers!
|
||||||
|
if (!version || version >= Account::makeServerVersion(10, 0, 0)) {
|
||||||
|
path = QLatin1String("/index.php/settings/personal?sectionid=security#apppasswords");
|
||||||
|
} else if (version >= Account::makeServerVersion(9, 1, 0)) {
|
||||||
|
path = QLatin1String("/index.php/settings/personal?section=apppasswords");
|
||||||
|
} else {
|
||||||
|
// Older server than 9.1 does not have the feature to request App Password
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
return tr("<a href=\"%1\">Click here</a> to request an app password from the web interface.")
|
return tr("<a href=\"%1\">Click here</a> to request an app password from the web interface.")
|
||||||
.arg(account->url().toString() + QLatin1String("/index.php/settings/personal?section=apppasswords"));
|
.arg(account->url().toString() + path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -345,7 +345,9 @@ void Folder::showSyncResultPopup()
|
|||||||
if( _syncResult.firstConflictItem() ) {
|
if( _syncResult.firstConflictItem() ) {
|
||||||
createGuiLog( _syncResult.firstConflictItem()->_file, LogStatusConflict, _syncResult.numConflictItems() );
|
createGuiLog( _syncResult.firstConflictItem()->_file, LogStatusConflict, _syncResult.numConflictItems() );
|
||||||
}
|
}
|
||||||
createGuiLog( _syncResult.firstItemError()->_file, LogStatusError, _syncResult.numErrorItems() );
|
if (int errorCount = _syncResult.numErrorItems()) {
|
||||||
|
createGuiLog( _syncResult.firstItemError()->_file, LogStatusError, errorCount );
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
||||||
}
|
}
|
||||||
@@ -909,6 +911,7 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction dir, bool *cancel
|
|||||||
}
|
}
|
||||||
*cancel = msgBox.clickedButton() == keepBtn;
|
*cancel = msgBox.clickedButton() == keepBtn;
|
||||||
if (*cancel) {
|
if (*cancel) {
|
||||||
|
FileSystem::setFolderMinimumPermissions(path());
|
||||||
journalDb()->clearFileTable();
|
journalDb()->clearFileTable();
|
||||||
_lastEtag.clear();
|
_lastEtag.clear();
|
||||||
slotScheduleThisFolder();
|
slotScheduleThisFolder();
|
||||||
@@ -1000,7 +1003,7 @@ QString FolderDefinition::absoluteJournalPath() const
|
|||||||
|
|
||||||
QString FolderDefinition::defaultJournalPath(AccountPtr account)
|
QString FolderDefinition::defaultJournalPath(AccountPtr account)
|
||||||
{
|
{
|
||||||
return SyncJournalDb::makeDbName(account->url(), targetPath, account->credentials()->user());
|
return SyncJournalDb::makeDbName(localPath, account->url(), targetPath, account->credentials()->user());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace OCC
|
} // namespace OCC
|
||||||
|
|||||||
@@ -235,11 +235,22 @@ void FolderMan::setupFoldersHelper(QSettings &settings, AccountStatePtr account,
|
|||||||
foreach (const auto& folderAlias, settings.childGroups()) {
|
foreach (const auto& folderAlias, settings.childGroups()) {
|
||||||
FolderDefinition folderDefinition;
|
FolderDefinition folderDefinition;
|
||||||
if (FolderDefinition::load(settings, folderAlias, &folderDefinition)) {
|
if (FolderDefinition::load(settings, folderAlias, &folderDefinition)) {
|
||||||
|
auto defaultJournalPath = folderDefinition.defaultJournalPath(account->account());
|
||||||
|
|
||||||
// Migration: Old settings don't have journalPath
|
// Migration: Old settings don't have journalPath
|
||||||
if (folderDefinition.journalPath.isEmpty()) {
|
if (folderDefinition.journalPath.isEmpty()) {
|
||||||
folderDefinition.journalPath = folderDefinition.defaultJournalPath(account->account());
|
folderDefinition.journalPath = defaultJournalPath;
|
||||||
}
|
}
|
||||||
folderDefinition.defaultJournalPath(account->account());
|
|
||||||
|
// Migration: ._ files sometimes don't work
|
||||||
|
// So if the configured journalPath is the default one ("._sync_*.db")
|
||||||
|
// but the current default doesn't have the underscore, switch to the
|
||||||
|
// new default. See SyncJournalDb::makeDbName().
|
||||||
|
if (folderDefinition.journalPath.startsWith("._sync_")
|
||||||
|
&& defaultJournalPath.startsWith(".sync_")) {
|
||||||
|
folderDefinition.journalPath = defaultJournalPath;
|
||||||
|
}
|
||||||
|
|
||||||
// Migration: If an old db is found, move it to the new name.
|
// Migration: If an old db is found, move it to the new name.
|
||||||
if (backwardsCompatible) {
|
if (backwardsCompatible) {
|
||||||
SyncJournalDb::maybeMigrateDb(folderDefinition.localPath, folderDefinition.absoluteJournalPath());
|
SyncJournalDb::maybeMigrateDb(folderDefinition.localPath, folderDefinition.absoluteJournalPath());
|
||||||
|
|||||||
@@ -631,8 +631,8 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
|||||||
const auto permissionMap = job->property(propertyPermissionMap).toMap();
|
const auto permissionMap = job->property(propertyPermissionMap).toMap();
|
||||||
|
|
||||||
QStringList sortedSubfolders = list;
|
QStringList sortedSubfolders = list;
|
||||||
// skip the parent item (first in the list)
|
if (!sortedSubfolders.isEmpty())
|
||||||
sortedSubfolders.erase(sortedSubfolders.begin());
|
sortedSubfolders.removeFirst(); // skip the parent item (first in the list)
|
||||||
Utility::sortFilenames(sortedSubfolders);
|
Utility::sortFilenames(sortedSubfolders);
|
||||||
|
|
||||||
QVarLengthArray<int, 10> undecidedIndexes;
|
QVarLengthArray<int, 10> undecidedIndexes;
|
||||||
@@ -652,7 +652,7 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
|
|||||||
newInfo._size = job->_sizes.value(path);
|
newInfo._size = job->_sizes.value(path);
|
||||||
newInfo._isExternal = permissionMap.value(removeTrailingSlash(path)).toString().contains("M");
|
newInfo._isExternal = permissionMap.value(removeTrailingSlash(path)).toString().contains("M");
|
||||||
newInfo._path = relativePath;
|
newInfo._path = relativePath;
|
||||||
newInfo._name = relativePath.split('/', QString::SkipEmptyParts).last();
|
newInfo._name = removeTrailingSlash(relativePath).split('/').last();
|
||||||
|
|
||||||
if (relativePath.isEmpty())
|
if (relativePath.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ void FolderWatcherPrivate::slotReceivedNotification(int fd)
|
|||||||
// qDebug() << Q_FUNC_INFO << event->name;
|
// qDebug() << Q_FUNC_INFO << event->name;
|
||||||
if (fileName.startsWith("._sync_") ||
|
if (fileName.startsWith("._sync_") ||
|
||||||
fileName.startsWith(".csync_journal.db") ||
|
fileName.startsWith(".csync_journal.db") ||
|
||||||
fileName.startsWith(".owncloudsync.log")) {
|
fileName.startsWith(".owncloudsync.log") ||
|
||||||
|
fileName.startsWith(".sync_")) {
|
||||||
// qDebug() << "ignore journal";
|
// qDebug() << "ignore journal";
|
||||||
} else {
|
} else {
|
||||||
const QString p = _watches[event->wd] + '/' + fileName;
|
const QString p = _watches[event->wd] + '/' + fileName;
|
||||||
|
|||||||
@@ -152,6 +152,12 @@
|
|||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="aboutLabel">
|
<widget class="QLabel" name="aboutLabel">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>About</string>
|
<string>About</string>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
|
|||||||
"and cannot be modified in this view.")
|
"and cannot be modified in this view.")
|
||||||
.arg(QDir::toNativeSeparators(cfgFile.excludeFile(ConfigFile::SystemScope)));
|
.arg(QDir::toNativeSeparators(cfgFile.excludeFile(ConfigFile::SystemScope)));
|
||||||
|
|
||||||
|
addPattern(".csync_journal.db*", /*deletable=*/false, /*readonly=*/true);
|
||||||
|
addPattern("._sync_*.db*", /*deletable=*/false, /*readonly=*/true);
|
||||||
|
addPattern(".sync_*.db*", /*deletable=*/false, /*readonly=*/true);
|
||||||
readIgnoreFile(cfgFile.excludeFile(ConfigFile::SystemScope), true);
|
readIgnoreFile(cfgFile.excludeFile(ConfigFile::SystemScope), true);
|
||||||
readIgnoreFile(cfgFile.excludeFile(ConfigFile::UserScope), false);
|
readIgnoreFile(cfgFile.excludeFile(ConfigFile::UserScope), false);
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
|
|||||||
connect(ui->removePushButton, SIGNAL(clicked()), SLOT(slotRemoveCurrentItem()));
|
connect(ui->removePushButton, SIGNAL(clicked()), SLOT(slotRemoveCurrentItem()));
|
||||||
connect(ui->addPushButton, SIGNAL(clicked()), SLOT(slotAddPattern()));
|
connect(ui->addPushButton, SIGNAL(clicked()), SLOT(slotAddPattern()));
|
||||||
|
|
||||||
|
ui->tableWidget->resizeColumnsToContents();
|
||||||
ui->tableWidget->horizontalHeader()->setResizeMode(patternCol, QHeaderView::Stretch);
|
ui->tableWidget->horizontalHeader()->setResizeMode(patternCol, QHeaderView::Stretch);
|
||||||
ui->tableWidget->verticalHeader()->setVisible(false);
|
ui->tableWidget->verticalHeader()->setVisible(false);
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>438</width>
|
<width>516</width>
|
||||||
<height>463</height>
|
<height>546</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -103,9 +103,18 @@
|
|||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
|||||||
@@ -54,9 +54,7 @@ void NotificationConfirmJob::start()
|
|||||||
req.setRawHeader("Ocs-APIREQUEST", "true");
|
req.setRawHeader("Ocs-APIREQUEST", "true");
|
||||||
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
|
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
|
||||||
|
|
||||||
QIODevice *iodevice = 0;
|
sendRequest(_verb, _link, req);
|
||||||
setReply(davRequest(_verb, _link, req, iodevice));
|
|
||||||
setupConnections(reply());
|
|
||||||
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,8 +125,8 @@ void NotificationWidget::slotNotificationRequestFinished(int statusCode)
|
|||||||
|
|
||||||
QString timeStr = locale.toString(QTime::currentTime());
|
QString timeStr = locale.toString(QTime::currentTime());
|
||||||
|
|
||||||
// the ocs API returns stat code 100 if it succeeded.
|
// the ocs API returns stat code 100 or 200 inside the xml if it succeeded.
|
||||||
if( statusCode != OCS_SUCCESS_STATUS_CODE ) {
|
if( statusCode != OCS_SUCCESS_STATUS_CODE && statusCode != OCS_SUCCESS_STATUS_CODE_V2 ) {
|
||||||
qDebug() << Q_FUNC_INFO << "Notification Request to Server failed, leave button visible.";
|
qDebug() << Q_FUNC_INFO << "Notification Request to Server failed, leave button visible.";
|
||||||
for( i = 0; i < _buttons.count(); i++ ) {
|
for( i = 0; i < _buttons.count(); i++ ) {
|
||||||
_buttons.at(i)->setEnabled(true);
|
_buttons.at(i)->setEnabled(true);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ OcsJob::OcsJob(AccountPtr account)
|
|||||||
: AbstractNetworkJob(account, "")
|
: AbstractNetworkJob(account, "")
|
||||||
{
|
{
|
||||||
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE);
|
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE);
|
||||||
|
_passStatusCodes.append(OCS_SUCCESS_STATUS_CODE_V2);
|
||||||
setIgnoreCredentialFailure(true);
|
setIgnoreCredentialFailure(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,9 +94,7 @@ void OcsJob::start()
|
|||||||
queryItems.append(qMakePair(QByteArray("format"), QByteArray("json")));
|
queryItems.append(qMakePair(QByteArray("format"), QByteArray("json")));
|
||||||
url.setEncodedQueryItems(queryItems);
|
url.setEncodedQueryItems(queryItems);
|
||||||
|
|
||||||
setReply(davRequest(_verb, url, req, buffer));
|
sendRequest(_verb, url, req, buffer);
|
||||||
setupConnections(reply());
|
|
||||||
buffer->setParent(reply());
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#define OCS_SUCCESS_STATUS_CODE 100
|
#define OCS_SUCCESS_STATUS_CODE 100
|
||||||
|
// Apparantly the v2.php URLs can return that
|
||||||
|
#define OCS_SUCCESS_STATUS_CODE_V2 200
|
||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
|||||||
@@ -174,22 +174,31 @@ void ownCloudGui::slotTrayClicked( QSystemTrayIcon::ActivationReason reason )
|
|||||||
last_click.start();
|
last_click.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// A click on the tray icon should only open the status window on Win and
|
// Left click
|
||||||
// Linux, not on Mac. They want a menu entry.
|
|
||||||
#if !defined Q_OS_MAC
|
|
||||||
if( reason == QSystemTrayIcon::Trigger ) {
|
if( reason == QSystemTrayIcon::Trigger ) {
|
||||||
// Start settings if config is existing.
|
if (OwncloudSetupWizard::bringWizardToFrontIfVisible()) {
|
||||||
slotOpenSettingsDialog();
|
// brought wizard to front
|
||||||
}
|
} else if (_shareDialogs.size() > 0) {
|
||||||
|
// Share dialog(s) be hidden by other apps, bring them back
|
||||||
|
Q_FOREACH(const QPointer<ShareDialog> &shareDialog, _shareDialogs) {
|
||||||
|
Q_ASSERT(shareDialog.data());
|
||||||
|
raiseDialog(shareDialog);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
// on macOS, a left click always opens menu.
|
||||||
|
// However if the settings dialog is already visible but hidden
|
||||||
|
// by other applications, this will bring it to the front.
|
||||||
|
if (!_settingsDialog.isNull() && _settingsDialog->isVisible()) {
|
||||||
|
raiseDialog(_settingsDialog.data());
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
// On Mac, if the settings dialog is already visible but hidden
|
slotOpenSettingsDialog();
|
||||||
// by other applications, this will bring it to the front.
|
#endif
|
||||||
if( reason == QSystemTrayIcon::Trigger ) {
|
|
||||||
if (!_settingsDialog.isNull() && _settingsDialog->isVisible()) {
|
|
||||||
slotShowSettings();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
// FIXME: Also make sure that any auto updater dialogue https://github.com/owncloud/client/issues/5613
|
||||||
|
// or SSL error dialog also comes to front.
|
||||||
}
|
}
|
||||||
|
|
||||||
void ownCloudGui::slotSyncStateChange( Folder* folder )
|
void ownCloudGui::slotSyncStateChange( Folder* folder )
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
#include "accountmanager.h"
|
#include "accountmanager.h"
|
||||||
#include "clientproxy.h"
|
#include "clientproxy.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
#include "owncloudgui.h"
|
||||||
|
|
||||||
#include "creds/credentialsfactory.h"
|
#include "creds/credentialsfactory.h"
|
||||||
#include "creds/abstractcredentials.h"
|
#include "creds/abstractcredentials.h"
|
||||||
@@ -64,10 +65,10 @@ OwncloudSetupWizard::~OwncloudSetupWizard()
|
|||||||
_ocWizard->deleteLater();
|
_ocWizard->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QPointer<OwncloudSetupWizard> wiz = 0;
|
||||||
|
|
||||||
void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *parent)
|
void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *parent)
|
||||||
{
|
{
|
||||||
static QPointer<OwncloudSetupWizard> wiz;
|
|
||||||
|
|
||||||
if (!wiz.isNull()) {
|
if (!wiz.isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -78,6 +79,16 @@ void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *
|
|||||||
wiz->startWizard();
|
wiz->startWizard();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OwncloudSetupWizard::bringWizardToFrontIfVisible()
|
||||||
|
{
|
||||||
|
if (wiz.isNull()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ownCloudGui::raiseDialog(wiz->_ocWizard);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void OwncloudSetupWizard::startWizard()
|
void OwncloudSetupWizard::startWizard()
|
||||||
{
|
{
|
||||||
AccountPtr account = AccountManager::createAccount();
|
AccountPtr account = AccountManager::createAccount();
|
||||||
@@ -183,6 +194,8 @@ void OwncloudSetupWizard::slotOwnCloudFoundAuth(const QUrl& url, const QVariantM
|
|||||||
Utility::escape(CheckServerJob::versionString(info)),
|
Utility::escape(CheckServerJob::versionString(info)),
|
||||||
Utility::escape(serverVersion)));
|
Utility::escape(serverVersion)));
|
||||||
|
|
||||||
|
// Note with newer servers we get the version actually only later in capabilities
|
||||||
|
// https://github.com/owncloud/core/pull/27473/files
|
||||||
_ocWizard->account()->setServerVersion(serverVersion);
|
_ocWizard->account()->setServerVersion(serverVersion);
|
||||||
|
|
||||||
QString p = url.path();
|
QString p = url.path();
|
||||||
@@ -368,6 +381,7 @@ void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFo
|
|||||||
|
|
||||||
bool nextStep = true;
|
bool nextStep = true;
|
||||||
if( fi.exists() ) {
|
if( fi.exists() ) {
|
||||||
|
FileSystem::setFolderMinimumPermissions(localFolder);
|
||||||
// there is an existing local folder. If its non empty, it can only be synced if the
|
// there is an existing local folder. If its non empty, it can only be synced if the
|
||||||
// ownCloud is newly created.
|
// ownCloud is newly created.
|
||||||
_ocWizard->appendToConfigurationLog(
|
_ocWizard->appendToConfigurationLog(
|
||||||
@@ -594,9 +608,7 @@ DetermineAuthTypeJob::DetermineAuthTypeJob(AccountPtr account, QObject *parent)
|
|||||||
|
|
||||||
void DetermineAuthTypeJob::start()
|
void DetermineAuthTypeJob::start()
|
||||||
{
|
{
|
||||||
QNetworkReply *reply = getRequest(account()->davPath());
|
sendRequest("GET", account()->davUrl());
|
||||||
setReply(reply);
|
|
||||||
setupConnections(reply);
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,8 +625,7 @@ bool DetermineAuthTypeJob::finished()
|
|||||||
// do a new run
|
// do a new run
|
||||||
_redirects++;
|
_redirects++;
|
||||||
resetTimeout();
|
resetTimeout();
|
||||||
setReply(getRequest(redirection));
|
sendRequest("GET", redirection);
|
||||||
setupConnections(reply());
|
|
||||||
return false; // don't discard
|
return false; // don't discard
|
||||||
} else {
|
} else {
|
||||||
#ifndef NO_SHIBBOLETH
|
#ifndef NO_SHIBBOLETH
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class OwncloudSetupWizard : public QObject
|
|||||||
public:
|
public:
|
||||||
/** Run the wizard */
|
/** Run the wizard */
|
||||||
static void runWizard(QObject *obj, const char* amember, QWidget *parent = 0 );
|
static void runWizard(QObject *obj, const char* amember, QWidget *parent = 0 );
|
||||||
|
static bool bringWizardToFrontIfVisible();
|
||||||
signals:
|
signals:
|
||||||
// overall dialog close signal.
|
// overall dialog close signal.
|
||||||
void ownCloudWizardDone( int );
|
void ownCloudWizardDone( int );
|
||||||
|
|||||||
@@ -81,9 +81,14 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Find the responsible QNAM if possible.
|
// Find the responsible QNAM if possible.
|
||||||
QNetworkAccessManager* sending_qnam = qobject_cast<QNetworkAccessManager*>(sender());
|
QNetworkAccessManager* sending_qnam = 0;
|
||||||
|
QWeakPointer<QNetworkAccessManager> qnam_alive;
|
||||||
if (Account* account = qobject_cast<Account*>(sender())) {
|
if (Account* account = qobject_cast<Account*>(sender())) {
|
||||||
sending_qnam = account->networkAccessManager();
|
// Since we go into an event loop, it's possible for the account's qnam
|
||||||
|
// to be destroyed before we get back. We can use this to check for its
|
||||||
|
// liveness.
|
||||||
|
qnam_alive = account->sharedNetworkAccessManager();
|
||||||
|
sending_qnam = qnam_alive.data();
|
||||||
}
|
}
|
||||||
if (!sending_qnam) {
|
if (!sending_qnam) {
|
||||||
qDebug() << "Could not get the sending QNAM for" << sender();
|
qDebug() << "Could not get the sending QNAM for" << sender();
|
||||||
@@ -122,6 +127,7 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired(
|
|||||||
qDebug() << "got creds for" << _proxy;
|
qDebug() << "got creds for" << _proxy;
|
||||||
authenticator->setUser(_username);
|
authenticator->setUser(_username);
|
||||||
authenticator->setPassword(_password);
|
authenticator->setPassword(_password);
|
||||||
|
sending_qnam = qnam_alive.data();
|
||||||
if (sending_qnam) {
|
if (sending_qnam) {
|
||||||
_gaveCredentialsTo.insert(sending_qnam);
|
_gaveCredentialsTo.insert(sending_qnam);
|
||||||
connect(sending_qnam, SIGNAL(destroyed(QObject*)),
|
connect(sending_qnam, SIGNAL(destroyed(QObject*)),
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
namespace OCC
|
namespace OCC
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const QString notificationsPath = QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications");
|
||||||
|
|
||||||
ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
|
ServerNotificationHandler::ServerNotificationHandler(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
@@ -47,7 +49,7 @@ void ServerNotificationHandler::slotFetchNotifications(AccountState *ptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if the previous notification job has finished, start next.
|
// if the previous notification job has finished, start next.
|
||||||
_notificationJob = new JsonApiJob( ptr->account(), QLatin1String("ocs/v2.php/apps/notifications/api/v1/notifications"), this );
|
_notificationJob = new JsonApiJob( ptr->account(), notificationsPath, this );
|
||||||
QObject::connect(_notificationJob.data(), SIGNAL(jsonReceived(QVariantMap, int)),
|
QObject::connect(_notificationJob.data(), SIGNAL(jsonReceived(QVariantMap, int)),
|
||||||
this, SLOT(slotNotificationsReceived(QVariantMap, int)));
|
this, SLOT(slotNotificationsReceived(QVariantMap, int)));
|
||||||
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(ptr));
|
_notificationJob->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(ptr));
|
||||||
@@ -94,6 +96,16 @@ void ServerNotificationHandler::slotNotificationsReceived(const QVariantMap& jso
|
|||||||
|
|
||||||
a._links.append(al);
|
a._links.append(al);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add another action to dismiss notification on server
|
||||||
|
// https://github.com/owncloud/notifications/blob/master/docs/ocs-endpoint-v1.md#deleting-a-notification-for-a-user
|
||||||
|
ActivityLink al;
|
||||||
|
al._label = tr("Dismiss");
|
||||||
|
al._link = Utility::concatUrlPath(ai->account()->url(), notificationsPath + "/" + json.value("notification_id").toString()).toString();
|
||||||
|
al._verb = "DELETE";
|
||||||
|
al._isPrimary = false;
|
||||||
|
a._links.append(al);
|
||||||
|
|
||||||
list.append(a);
|
list.append(a);
|
||||||
}
|
}
|
||||||
emit newNotificationList( list );
|
emit newNotificationList( list );
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
namespace OCC {
|
namespace OCC {
|
||||||
|
|
||||||
|
static const int thumbnailSize = 40;
|
||||||
|
|
||||||
ShareDialog::ShareDialog(QPointer<AccountState> accountState,
|
ShareDialog::ShareDialog(QPointer<AccountState> accountState,
|
||||||
const QString &sharePath,
|
const QString &sharePath,
|
||||||
const QString &localPath,
|
const QString &localPath,
|
||||||
@@ -67,7 +69,7 @@ ShareDialog::ShareDialog(QPointer<AccountState> accountState,
|
|||||||
QFileInfo f_info(_localPath);
|
QFileInfo f_info(_localPath);
|
||||||
QFileIconProvider icon_provider;
|
QFileIconProvider icon_provider;
|
||||||
QIcon icon = icon_provider.icon(f_info);
|
QIcon icon = icon_provider.icon(f_info);
|
||||||
_ui->label_icon->setPixmap(icon.pixmap(40,40));
|
_ui->label_icon->setPixmap(icon.pixmap(thumbnailSize, thumbnailSize));
|
||||||
|
|
||||||
// Set filename
|
// Set filename
|
||||||
QFileInfo lPath(_localPath);
|
QFileInfo lPath(_localPath);
|
||||||
@@ -176,7 +178,7 @@ void ShareDialog::showSharingUi()
|
|||||||
// We only do user/group sharing from 8.2.0
|
// We only do user/group sharing from 8.2.0
|
||||||
bool userGroupSharing =
|
bool userGroupSharing =
|
||||||
theme->userGroupSharing()
|
theme->userGroupSharing()
|
||||||
&& _accountState->account()->serverVersionInt() >= ((8 << 16) + (2 << 8));
|
&& _accountState->account()->serverVersionInt() >= Account::makeServerVersion(8, 2, 0);
|
||||||
|
|
||||||
bool autoShare = !userGroupSharing;
|
bool autoShare = !userGroupSharing;
|
||||||
|
|
||||||
@@ -213,6 +215,7 @@ void ShareDialog::slotThumbnailFetched(const int &statusCode, const QByteArray &
|
|||||||
|
|
||||||
QPixmap p;
|
QPixmap p;
|
||||||
p.loadFromData(reply, "PNG");
|
p.loadFromData(reply, "PNG");
|
||||||
|
p = p.scaledToHeight(thumbnailSize, Qt::SmoothTransformation);
|
||||||
_ui->label_icon->setPixmap(p);
|
_ui->label_icon->setPixmap(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>408</width>
|
||||||
<height>300</height>
|
<height>281</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@@ -64,7 +64,24 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="shareWidgetsLayout"/>
|
<layout class="QVBoxLayout" name="shareWidgetsLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>40</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
|||||||
@@ -14,11 +14,20 @@
|
|||||||
<string>Share NewDocument.odt</string>
|
<string>Share NewDocument.odt</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<layout class="QGridLayout" name="gridLayout_3">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_shareLink">
|
<layout class="QHBoxLayout" name="horizontalLayout_shareLink">
|
||||||
<property name="topMargin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="checkBox_shareLink">
|
<widget class="QCheckBox" name="checkBox_shareLink">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@@ -28,7 +37,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<widget class="QLabel" name="errorLabel">
|
<widget class="QLabel" name="errorLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||||
@@ -81,19 +90,19 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0" colspan="2">
|
<item row="2" column="0" colspan="2">
|
||||||
<widget class="QWidget" name="widget_shareLink" native="true">
|
<widget class="QWidget" name="widget_shareLink" native="true">
|
||||||
<layout class="QGridLayout" name="gridLayout_2">
|
<layout class="QGridLayout" name="gridLayout_2">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>20</number>
|
<number>20</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
<property name="topMargin">
|
||||||
<number>1</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="rightMargin">
|
<property name="rightMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="3" column="0">
|
<item row="4" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>20</number>
|
<number>20</number>
|
||||||
@@ -120,7 +129,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="6" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@@ -141,7 +150,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item row="3" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_password">
|
<layout class="QHBoxLayout" name="horizontalLayout_password">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="checkBox_password">
|
<widget class="QCheckBox" name="checkBox_password">
|
||||||
@@ -171,7 +180,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="1" column="0">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetDefaultConstraint</enum>
|
<enum>QLayout::SetDefaultConstraint</enum>
|
||||||
@@ -214,7 +223,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="2" column="0">
|
||||||
<widget class="QWidget" name="widget_editing" native="true">
|
<widget class="QWidget" name="widget_editing" native="true">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_editing">
|
<layout class="QHBoxLayout" name="horizontalLayout_editing">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
@@ -252,10 +261,23 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="publicLinkWarningLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Anyone with the link has access to the file/folder</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="3" column="1">
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ QSharedPointer<LinkShare> ShareManager::parseLinkShare(const QVariantMap &data)
|
|||||||
// From ownCloud server 8.2 the url field is always set for public shares
|
// From ownCloud server 8.2 the url field is always set for public shares
|
||||||
if (data.contains("url")) {
|
if (data.contains("url")) {
|
||||||
url = QUrl(data.value("url").toString());
|
url = QUrl(data.value("url").toString());
|
||||||
} else if (_account->serverVersionInt() >= (8 << 16)) {
|
} else if (_account->serverVersionInt() >= Account::makeServerVersion(8, 0, 0)) {
|
||||||
// From ownCloud server version 8 on, a different share link scheme is used.
|
// From ownCloud server version 8 on, a different share link scheme is used.
|
||||||
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("index.php/s/") + data.value("token").toString())).toString();
|
url = QUrl(Utility::concatUrlPath(_account->url(), QLatin1String("index.php/s/") + data.value("token").toString())).toString();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ void ShareUserGroupWidget::slotCompleterActivated(const QModelIndex & index)
|
|||||||
* https://github.com/owncloud/client/issues/4996
|
* https://github.com/owncloud/client/issues/4996
|
||||||
*/
|
*/
|
||||||
if (sharee->type() == Sharee::Federated
|
if (sharee->type() == Sharee::Federated
|
||||||
&& _account->serverVersionInt() < 0x090100) {
|
&& _account->serverVersionInt() < Account::makeServerVersion(9, 1, 0)) {
|
||||||
int permissions = SharePermissionRead | SharePermissionUpdate;
|
int permissions = SharePermissionRead | SharePermissionUpdate;
|
||||||
if (!_isFile) {
|
if (!_isFile) {
|
||||||
permissions |= SharePermissionCreate | SharePermissionDelete;
|
permissions |= SharePermissionCreate | SharePermissionDelete;
|
||||||
@@ -280,9 +280,16 @@ void ShareUserGroupWidget::slotCompleterHighlighted(const QModelIndex & index)
|
|||||||
void ShareUserGroupWidget::displayError(int code, const QString& message)
|
void ShareUserGroupWidget::displayError(int code, const QString& message)
|
||||||
{
|
{
|
||||||
_pi_sharee.stopAnimation();
|
_pi_sharee.stopAnimation();
|
||||||
|
|
||||||
|
// Also remove the spinner in the widget list, if any
|
||||||
|
foreach (auto pi, _ui->scrollArea->findChildren<QProgressIndicator*>()) {
|
||||||
|
delete pi;
|
||||||
|
}
|
||||||
|
|
||||||
qDebug() << "Error from server" << code << message;
|
qDebug() << "Error from server" << code << message;
|
||||||
_ui->errorLabel->setText(message);
|
_ui->errorLabel->setText(message);
|
||||||
_ui->errorLabel->show();
|
_ui->errorLabel->show();
|
||||||
|
_ui->shareeLineEdit->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShareWidget::ShareWidget(QSharedPointer<Share> share,
|
ShareWidget::ShareWidget(QSharedPointer<Share> share,
|
||||||
@@ -324,6 +331,11 @@ ShareWidget::ShareWidget(QSharedPointer<Share> share,
|
|||||||
QIcon icon(QLatin1String(":/client/resources/more.png"));
|
QIcon icon(QLatin1String(":/client/resources/more.png"));
|
||||||
_ui->permissionToolButton->setIcon(icon);
|
_ui->permissionToolButton->setIcon(icon);
|
||||||
|
|
||||||
|
// If there's only a single entry in the detailed permission menu, hide it
|
||||||
|
if (menu->actions().size() == 1) {
|
||||||
|
_ui->permissionToolButton->hide();
|
||||||
|
}
|
||||||
|
|
||||||
// Set the permissions checkboxes
|
// Set the permissions checkboxes
|
||||||
displayPermissions();
|
displayPermissions();
|
||||||
|
|
||||||
@@ -343,7 +355,7 @@ ShareWidget::ShareWidget(QSharedPointer<Share> share,
|
|||||||
* https://github.com/owncloud/client/issues/4996
|
* https://github.com/owncloud/client/issues/4996
|
||||||
*/
|
*/
|
||||||
if (share->getShareType() == Share::TypeRemote
|
if (share->getShareType() == Share::TypeRemote
|
||||||
&& share->account()->serverVersionInt() < 0x090100) {
|
&& share->account()->serverVersionInt() < Account::makeServerVersion(9, 1, 0)) {
|
||||||
_ui->permissionShare->setVisible(false);
|
_ui->permissionShare->setVisible(false);
|
||||||
_ui->permissionToolButton->setVisible(false);
|
_ui->permissionToolButton->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,26 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>457</width>
|
<width>397</width>
|
||||||
<height>164</height>
|
<height>273</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Share NewDocument.odt</string>
|
<string>Share NewDocument.odt</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="shareeHorizontalLayout">
|
<layout class="QHBoxLayout" name="shareeHorizontalLayout">
|
||||||
<item>
|
<item>
|
||||||
@@ -94,8 +106,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>437</width>
|
<width>395</width>
|
||||||
<height>94</height>
|
<height>221</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
||||||
|
|||||||
@@ -404,7 +404,7 @@ void SocketApi::command_SHARE(const QString& localFile, SocketListener* listener
|
|||||||
listener->sendMessage(message);
|
listener->sendMessage(message);
|
||||||
} else if (!theme->linkSharing() && (
|
} else if (!theme->linkSharing() && (
|
||||||
!theme->userGroupSharing() ||
|
!theme->userGroupSharing() ||
|
||||||
shareFolder->accountState()->account()->serverVersionInt() < ((8 << 16) + (2 << 8)))) {
|
shareFolder->accountState()->account()->serverVersionInt() < Account::makeServerVersion(8, 2, 0))) {
|
||||||
const QString message = QLatin1String("SHARE:NOP:")+QDir::toNativeSeparators(localFile);
|
const QString message = QLatin1String("SHARE:NOP:")+QDir::toNativeSeparators(localFile);
|
||||||
listener->sendMessage(message);
|
listener->sendMessage(message);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ static QString addCertDetailsField(const QString &key, const QString &value)
|
|||||||
return QString();
|
return QString();
|
||||||
|
|
||||||
return QLatin1String("<tr><td style=\"vertical-align: top;\"><b>") + key
|
return QLatin1String("<tr><td style=\"vertical-align: top;\"><b>") + key
|
||||||
+ QLatin1String("</b></td><td style=\"vertical-align: bottom;\">)") + value
|
+ QLatin1String("</b></td><td style=\"vertical-align: bottom;\">") + value
|
||||||
+ QLatin1String("</td></tr>");
|
+ QLatin1String("</td></tr>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ ThumbnailJob::ThumbnailJob(const QString &path, AccountPtr account, QObject* par
|
|||||||
|
|
||||||
void ThumbnailJob::start()
|
void ThumbnailJob::start()
|
||||||
{
|
{
|
||||||
setReply(getRequest(path()));
|
sendRequest("GET", makeAccountUrl(path()));
|
||||||
setupConnections(reply());
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,40 +122,49 @@ QNetworkReply* AbstractNetworkJob::addTimer(QNetworkReply *reply)
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* AbstractNetworkJob::davRequest(const QByteArray &verb, const QString &relPath,
|
QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUrl &url,
|
||||||
QNetworkRequest req, QIODevice *data)
|
QNetworkRequest req, QIODevice *requestBody)
|
||||||
{
|
{
|
||||||
return addTimer(_account->davRequest(verb, relPath, req, data));
|
auto reply = _account->sendRequest(verb, url, req, requestBody);
|
||||||
|
_requestBody = requestBody;
|
||||||
|
if (_requestBody) {
|
||||||
|
_requestBody->setParent(reply);
|
||||||
|
}
|
||||||
|
addTimer(reply);
|
||||||
|
setReply(reply);
|
||||||
|
setupConnections(reply);
|
||||||
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *AbstractNetworkJob::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
QUrl AbstractNetworkJob::makeAccountUrl(const QString& relativePath) const
|
||||||
{
|
{
|
||||||
return addTimer(_account->davRequest(verb, url, req, data));
|
return Utility::concatUrlPath(_account->url(), relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply* AbstractNetworkJob::getRequest(const QString &relPath)
|
QUrl AbstractNetworkJob::makeDavUrl(const QString& relativePath) const
|
||||||
{
|
{
|
||||||
return addTimer(_account->getRequest(relPath));
|
return Utility::concatUrlPath(_account->davUrl(), relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *AbstractNetworkJob::getRequest(const QUrl &url)
|
QByteArray AbstractNetworkJob::requestVerb(QNetworkReply* reply)
|
||||||
{
|
{
|
||||||
return addTimer(_account->getRequest(url));
|
switch (reply->operation()) {
|
||||||
}
|
case QNetworkAccessManager::HeadOperation:
|
||||||
|
return "HEAD";
|
||||||
QNetworkReply *AbstractNetworkJob::headRequest(const QString &relPath)
|
case QNetworkAccessManager::GetOperation:
|
||||||
{
|
return "GET";
|
||||||
return addTimer(_account->headRequest(relPath));
|
case QNetworkAccessManager::PutOperation:
|
||||||
}
|
return "PUT";
|
||||||
|
case QNetworkAccessManager::PostOperation:
|
||||||
QNetworkReply *AbstractNetworkJob::headRequest(const QUrl &url)
|
return "POST";
|
||||||
{
|
case QNetworkAccessManager::DeleteOperation:
|
||||||
return addTimer(_account->headRequest(url));
|
return "DELETE";
|
||||||
}
|
case QNetworkAccessManager::CustomOperation:
|
||||||
|
return reply->request().attribute(QNetworkRequest::CustomVerbAttribute).toByteArray();
|
||||||
QNetworkReply *AbstractNetworkJob::deleteRequest(const QUrl &url)
|
case QNetworkAccessManager::UnknownOperation:
|
||||||
{
|
break;
|
||||||
return addTimer(_account->deleteRequest(url));
|
}
|
||||||
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AbstractNetworkJob::slotFinished()
|
void AbstractNetworkJob::slotFinished()
|
||||||
@@ -178,24 +187,36 @@ void AbstractNetworkJob::slotFinished()
|
|||||||
// get the Date timestamp from reply
|
// get the Date timestamp from reply
|
||||||
_responseTimestamp = _reply->rawHeader("Date");
|
_responseTimestamp = _reply->rawHeader("Date");
|
||||||
|
|
||||||
if (_followRedirects) {
|
QUrl requestedUrl = reply()->request().url();
|
||||||
// ### the qWarnings here should be exported via displayErrors() so they
|
QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
||||||
|
if (_followRedirects && !redirectUrl.isEmpty()) {
|
||||||
|
_redirectCount++;
|
||||||
|
|
||||||
|
// ### some of the qWarnings here should be exported via displayErrors() so they
|
||||||
// ### can be presented to the user if the job executor has a GUI
|
// ### can be presented to the user if the job executor has a GUI
|
||||||
QUrl requestedUrl = reply()->request().url();
|
QByteArray verb = requestVerb(reply());
|
||||||
QUrl redirectUrl = reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
|
if (requestedUrl.scheme() == QLatin1String("https") &&
|
||||||
if (!redirectUrl.isEmpty()) {
|
redirectUrl.scheme() == QLatin1String("http")) {
|
||||||
_redirectCount++;
|
qWarning() << this << "HTTPS->HTTP downgrade detected!";
|
||||||
if (requestedUrl.scheme() == QLatin1String("https") &&
|
} else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
|
||||||
redirectUrl.scheme() == QLatin1String("http")) {
|
qWarning() << this << "Redirect loop detected!";
|
||||||
qWarning() << this << "HTTPS->HTTP downgrade detected!";
|
} else if (_requestBody && _requestBody->isSequential()) {
|
||||||
} else if (requestedUrl == redirectUrl || _redirectCount >= maxRedirects()) {
|
qWarning() << this << "cannot redirect request with sequential body";
|
||||||
qWarning() << this << "Redirect loop detected!";
|
} else if (verb.isEmpty()) {
|
||||||
} else {
|
qWarning() << this << "cannot redirect request: could not detect original verb";
|
||||||
resetTimeout();
|
} else {
|
||||||
setReply(getRequest(redirectUrl));
|
// Create the redirected request and send it
|
||||||
setupConnections(reply());
|
qDebug() << "Redirecting" << verb << requestedUrl << redirectUrl;
|
||||||
return;
|
resetTimeout();
|
||||||
|
if (_requestBody) {
|
||||||
|
_requestBody->seek(0);
|
||||||
}
|
}
|
||||||
|
sendRequest(
|
||||||
|
verb,
|
||||||
|
redirectUrl,
|
||||||
|
reply()->request(),
|
||||||
|
_requestBody);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,17 +78,29 @@ signals:
|
|||||||
void networkActivity();
|
void networkActivity();
|
||||||
protected:
|
protected:
|
||||||
void setupConnections(QNetworkReply *reply);
|
void setupConnections(QNetworkReply *reply);
|
||||||
QNetworkReply* davRequest(const QByteArray& verb, const QString &relPath,
|
|
||||||
QNetworkRequest req = QNetworkRequest(),
|
/** Initiate a network request, returning a QNetworkReply.
|
||||||
QIODevice *data = 0);
|
*
|
||||||
QNetworkReply* davRequest(const QByteArray& verb, const QUrl &url,
|
* Calls setReply() and setupConnections() on it.
|
||||||
QNetworkRequest req = QNetworkRequest(),
|
*
|
||||||
QIODevice *data = 0);
|
* Takes ownership of the requestBody (to allow redirects).
|
||||||
QNetworkReply* getRequest(const QString &relPath);
|
*/
|
||||||
QNetworkReply* getRequest(const QUrl &url);
|
QNetworkReply* sendRequest(const QByteArray& verb, const QUrl &url,
|
||||||
QNetworkReply* headRequest(const QString &relPath);
|
QNetworkRequest req = QNetworkRequest(),
|
||||||
QNetworkReply* headRequest(const QUrl &url);
|
QIODevice *requestBody = 0);
|
||||||
QNetworkReply* deleteRequest(const QUrl &url);
|
|
||||||
|
// sendRequest does not take a relative path instead of an url,
|
||||||
|
// but the old API allowed that. We have this undefined overload
|
||||||
|
// to help catch usage errors
|
||||||
|
QNetworkReply* sendRequest(const QByteArray& verb, const QString &relativePath,
|
||||||
|
QNetworkRequest req = QNetworkRequest(),
|
||||||
|
QIODevice *requestBody = 0);
|
||||||
|
|
||||||
|
/// Creates a url for the account from a relative path
|
||||||
|
QUrl makeAccountUrl(const QString& relativePath) const;
|
||||||
|
|
||||||
|
/// Like makeAccountUrl() but uses the account's dav base path
|
||||||
|
QUrl makeDavUrl(const QString& relativePath) const;
|
||||||
|
|
||||||
int maxRedirects() const { return 10; }
|
int maxRedirects() const { return 10; }
|
||||||
virtual bool finished() = 0;
|
virtual bool finished() = 0;
|
||||||
@@ -99,12 +111,19 @@ protected:
|
|||||||
// GET requests that don't set up any HTTP body or other flags.
|
// GET requests that don't set up any HTTP body or other flags.
|
||||||
bool _followRedirects;
|
bool _followRedirects;
|
||||||
|
|
||||||
|
/** Helper to construct the HTTP verb used in the request
|
||||||
|
*
|
||||||
|
* Returns an empty QByteArray for UnknownOperation.
|
||||||
|
*/
|
||||||
|
static QByteArray requestVerb(QNetworkReply* reply);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void slotFinished();
|
void slotFinished();
|
||||||
virtual void slotTimeout();
|
virtual void slotTimeout();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AccountPtr _account;
|
AccountPtr _account;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QNetworkReply* addTimer(QNetworkReply *reply);
|
QNetworkReply* addTimer(QNetworkReply *reply);
|
||||||
bool _ignoreCredentialFailure;
|
bool _ignoreCredentialFailure;
|
||||||
@@ -112,6 +131,12 @@ private:
|
|||||||
QString _path;
|
QString _path;
|
||||||
QTimer _timer;
|
QTimer _timer;
|
||||||
int _redirectCount;
|
int _redirectCount;
|
||||||
|
|
||||||
|
// Set by the xyzRequest() functions and needed to be able to redirect
|
||||||
|
// requests, should it be required.
|
||||||
|
//
|
||||||
|
// Reparented to the currently running QNetworkReply.
|
||||||
|
QPointer<QIODevice> _requestBody;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -202,54 +202,28 @@ QNetworkAccessManager *Account::networkAccessManager()
|
|||||||
return _am.data();
|
return _am.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *Account::headRequest(const QString &relPath)
|
QSharedPointer<QNetworkAccessManager> Account::sharedNetworkAccessManager()
|
||||||
{
|
{
|
||||||
return headRequest(Utility::concatUrlPath(url(), relPath));
|
return _am;
|
||||||
}
|
}
|
||||||
|
|
||||||
QNetworkReply *Account::headRequest(const QUrl &url)
|
QNetworkReply *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
||||||
{
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
|
||||||
request.setSslConfiguration(this->getOrCreateSslConfig());
|
|
||||||
#endif
|
|
||||||
return _am->head(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *Account::getRequest(const QString &relPath)
|
|
||||||
{
|
|
||||||
return getRequest(Utility::concatUrlPath(url(), relPath));
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *Account::getRequest(const QUrl &url)
|
|
||||||
{
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
|
||||||
request.setSslConfiguration(this->getOrCreateSslConfig());
|
|
||||||
#endif
|
|
||||||
return _am->get(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *Account::deleteRequest( const QUrl &url)
|
|
||||||
{
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
|
||||||
request.setSslConfiguration(this->getOrCreateSslConfig());
|
|
||||||
#endif
|
|
||||||
return _am->deleteResource(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
|
|
||||||
{
|
|
||||||
return davRequest(verb, Utility::concatUrlPath(davUrl(), relPath), req, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
|
||||||
{
|
{
|
||||||
req.setUrl(url);
|
req.setUrl(url);
|
||||||
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
#if QT_VERSION > QT_VERSION_CHECK(4, 8, 4)
|
||||||
req.setSslConfiguration(this->getOrCreateSslConfig());
|
req.setSslConfiguration(this->getOrCreateSslConfig());
|
||||||
#endif
|
#endif
|
||||||
|
if (verb == "HEAD" && !data) {
|
||||||
|
return _am->head(req);
|
||||||
|
} else if (verb == "GET" && !data) {
|
||||||
|
return _am->get(req);
|
||||||
|
} else if (verb == "POST") {
|
||||||
|
return _am->post(req, data);
|
||||||
|
} else if (verb == "PUT") {
|
||||||
|
return _am->put(req, data);
|
||||||
|
} else if (verb == "DELETE" && !data) {
|
||||||
|
return _am->deleteResource(req);
|
||||||
|
}
|
||||||
return _am->sendCustomRequest(req, verb, data);
|
return _am->sendCustomRequest(req, verb, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,9 +401,14 @@ int Account::serverVersionInt() const
|
|||||||
{
|
{
|
||||||
// FIXME: Use Qt 5.5 QVersionNumber
|
// FIXME: Use Qt 5.5 QVersionNumber
|
||||||
auto components = serverVersion().split('.');
|
auto components = serverVersion().split('.');
|
||||||
return (components.value(0).toInt() << 16)
|
return makeServerVersion(components.value(0).toInt(),
|
||||||
+ (components.value(1).toInt() << 8)
|
components.value(1).toInt(),
|
||||||
+ components.value(2).toInt();
|
components.value(2).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
int Account::makeServerVersion(int majorVersion, int minorVersion, int patchVersion)
|
||||||
|
{
|
||||||
|
return (majorVersion << 16) + (minorVersion << 8) + patchVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Account::serverVersionUnsupported() const
|
bool Account::serverVersionUnsupported() const
|
||||||
@@ -438,7 +417,7 @@ bool Account::serverVersionUnsupported() const
|
|||||||
// not detected yet, assume it is fine.
|
// not detected yet, assume it is fine.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return serverVersionInt() < 0x070000;
|
return serverVersionInt() < makeServerVersion(7, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::setServerVersion(const QString& version)
|
void Account::setServerVersion(const QString& version)
|
||||||
@@ -454,7 +433,7 @@ void Account::setServerVersion(const QString& version)
|
|||||||
|
|
||||||
bool Account::rootEtagChangesNotOnlySubFolderEtags()
|
bool Account::rootEtagChangesNotOnlySubFolderEtags()
|
||||||
{
|
{
|
||||||
return (serverVersionInt() >= 0x080100);
|
return (serverVersionInt() >= makeServerVersion(8, 1, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Account::setNonShib(bool nonShib)
|
void Account::setNonShib(bool nonShib)
|
||||||
|
|||||||
@@ -106,14 +106,10 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
// For creating various network requests
|
// For creating various network requests
|
||||||
QNetworkReply* headRequest(const QString &relPath);
|
QNetworkReply* sendRequest(const QByteArray &verb,
|
||||||
QNetworkReply* headRequest(const QUrl &url);
|
const QUrl &url,
|
||||||
QNetworkReply* getRequest(const QString &relPath);
|
QNetworkRequest req = QNetworkRequest(),
|
||||||
QNetworkReply* getRequest(const QUrl &url);
|
QIODevice *data = 0);
|
||||||
QNetworkReply* deleteRequest( const QUrl &url);
|
|
||||||
QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
|
|
||||||
QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
|
|
||||||
|
|
||||||
|
|
||||||
/** The ssl configuration during the first connection */
|
/** The ssl configuration during the first connection */
|
||||||
QSslConfiguration getOrCreateSslConfig();
|
QSslConfiguration getOrCreateSslConfig();
|
||||||
@@ -149,9 +145,22 @@ public:
|
|||||||
const Capabilities &capabilities() const;
|
const Capabilities &capabilities() const;
|
||||||
void setCapabilities(const QVariantMap &caps);
|
void setCapabilities(const QVariantMap &caps);
|
||||||
|
|
||||||
/** Access the server version */
|
/** Access the server version
|
||||||
|
*
|
||||||
|
* For servers >= 10.0.0, this can be the empty string until capabilities
|
||||||
|
* have been received.
|
||||||
|
*/
|
||||||
QString serverVersion() const;
|
QString serverVersion() const;
|
||||||
|
|
||||||
|
/** Server version for easy comparison.
|
||||||
|
*
|
||||||
|
* Example: serverVersionInt() >= makeServerVersion(11, 2, 3)
|
||||||
|
*
|
||||||
|
* Will be 0 if the version is not available yet.
|
||||||
|
*/
|
||||||
int serverVersionInt() const;
|
int serverVersionInt() const;
|
||||||
|
|
||||||
|
static int makeServerVersion(int majorVersion, int minorVersion, int patchVersion);
|
||||||
void setServerVersion(const QString &version);
|
void setServerVersion(const QString &version);
|
||||||
|
|
||||||
/** Whether the server is too old.
|
/** Whether the server is too old.
|
||||||
@@ -176,6 +185,7 @@ public:
|
|||||||
|
|
||||||
void resetNetworkAccessManager();
|
void resetNetworkAccessManager();
|
||||||
QNetworkAccessManager* networkAccessManager();
|
QNetworkAccessManager* networkAccessManager();
|
||||||
|
QSharedPointer<QNetworkAccessManager> sharedNetworkAccessManager();
|
||||||
|
|
||||||
/// Called by network jobs on credential errors, emits invalidCredentials()
|
/// Called by network jobs on credential errors, emits invalidCredentials()
|
||||||
void handleInvalidCredentials();
|
void handleInvalidCredentials();
|
||||||
|
|||||||
@@ -114,26 +114,21 @@ void ConnectionValidator::slotCheckServerAndAuth()
|
|||||||
|
|
||||||
void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &info)
|
void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &info)
|
||||||
{
|
{
|
||||||
|
// Newer servers don't disclose any version in status.php anymore
|
||||||
|
// https://github.com/owncloud/core/pull/27473/files
|
||||||
|
// so this string can be empty.
|
||||||
|
QString serverVersion = CheckServerJob::version(info);
|
||||||
|
|
||||||
// status.php was found.
|
// status.php was found.
|
||||||
qDebug() << "** Application: ownCloud found: "
|
qDebug() << "** Application: ownCloud found: "
|
||||||
<< url << " with version "
|
<< url << " with version "
|
||||||
<< CheckServerJob::versionString(info)
|
<< CheckServerJob::versionString(info)
|
||||||
<< "(" << CheckServerJob::version(info) << ")";
|
<< "(" << serverVersion << ")";
|
||||||
|
|
||||||
QString version = CheckServerJob::version(info);
|
if (!serverVersion.isEmpty() && !setAndCheckServerVersion(serverVersion)) {
|
||||||
_account->setServerVersion(version);
|
|
||||||
|
|
||||||
// We cannot deal with servers < 5.0.0
|
|
||||||
if (version.contains('.') && version.split('.')[0].toInt() < 5) {
|
|
||||||
_errors.append( tr("The configured server for this client is too old") );
|
|
||||||
_errors.append( tr("Please update to the latest server and restart the client.") );
|
|
||||||
reportResult( ServerVersionMismatch );
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We attempt to work with servers >= 5.0.0 but warn users.
|
|
||||||
// Check usages of Account::serverVersionUnsupported() for details.
|
|
||||||
|
|
||||||
// now check the authentication
|
// now check the authentication
|
||||||
if (_account->credentials()->ready())
|
if (_account->credentials()->ready())
|
||||||
QTimer::singleShot( 0, this, SLOT( checkAuthentication() ));
|
QTimer::singleShot( 0, this, SLOT( checkAuthentication() ));
|
||||||
@@ -235,6 +230,13 @@ void ConnectionValidator::slotCapabilitiesRecieved(const QVariantMap &json)
|
|||||||
auto caps = json.value("ocs").toMap().value("data").toMap().value("capabilities");
|
auto caps = json.value("ocs").toMap().value("data").toMap().value("capabilities");
|
||||||
qDebug() << "Server capabilities" << caps;
|
qDebug() << "Server capabilities" << caps;
|
||||||
_account->setCapabilities(caps.toMap());
|
_account->setCapabilities(caps.toMap());
|
||||||
|
|
||||||
|
// New servers also report the version in the capabilities
|
||||||
|
QString serverVersion = caps.toMap()["core"].toMap()["status"].toMap()["version"].toString();
|
||||||
|
if (!serverVersion.isEmpty() && !setAndCheckServerVersion(serverVersion)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fetchUser();
|
fetchUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +249,26 @@ void ConnectionValidator::fetchUser()
|
|||||||
job->start();
|
job->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConnectionValidator::setAndCheckServerVersion(const QString& version)
|
||||||
|
{
|
||||||
|
qDebug() << _account->url() << "has server version" << version;
|
||||||
|
_account->setServerVersion(version);
|
||||||
|
|
||||||
|
// We cannot deal with servers < 5.0.0
|
||||||
|
if (_account->serverVersionInt()
|
||||||
|
&& _account->serverVersionInt() < Account::makeServerVersion(5, 0, 0)) {
|
||||||
|
_errors.append( tr("The configured server for this client is too old") );
|
||||||
|
_errors.append( tr("Please update to the latest server and restart the client.") );
|
||||||
|
reportResult( ServerVersionMismatch );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We attempt to work with servers >= 5.0.0 but warn users.
|
||||||
|
// Check usages of Account::serverVersionUnsupported() for details.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void ConnectionValidator::slotUserFetched(const QVariantMap &json)
|
void ConnectionValidator::slotUserFetched(const QVariantMap &json)
|
||||||
{
|
{
|
||||||
QString user = json.value("ocs").toMap().value("data").toMap().value("id").toString();
|
QString user = json.value("ocs").toMap().value("data").toMap().value("id").toString();
|
||||||
|
|||||||
@@ -125,6 +125,12 @@ private:
|
|||||||
void checkServerCapabilities();
|
void checkServerCapabilities();
|
||||||
void fetchUser();
|
void fetchUser();
|
||||||
|
|
||||||
|
/** Sets the account's server version
|
||||||
|
*
|
||||||
|
* Returns false and reports ServerVersionMismatch for very old servers.
|
||||||
|
*/
|
||||||
|
bool setAndCheckServerVersion(const QString& version);
|
||||||
|
|
||||||
QStringList _errors;
|
QStringList _errors;
|
||||||
AccountPtr _account;
|
AccountPtr _account;
|
||||||
bool _isCheckingServerAndAuth;
|
bool _isCheckingServerAndAuth;
|
||||||
|
|||||||
@@ -52,12 +52,14 @@ namespace OCC {
|
|||||||
|
|
||||||
QString FileSystem::longWinPath( const QString& inpath )
|
QString FileSystem::longWinPath( const QString& inpath )
|
||||||
{
|
{
|
||||||
QString path(inpath);
|
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
path = QString::fromWCharArray( static_cast<wchar_t*>( c_utf8_path_to_locale(inpath.toUtf8() ) ) );
|
const char *unc_str = c_path_to_UNC(inpath.toUtf8());
|
||||||
#endif
|
QString path = QString::fromUtf8(unc_str);
|
||||||
|
free((void*)unc_str);
|
||||||
return path;
|
return path;
|
||||||
|
#else
|
||||||
|
return inpath;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystem::fileEquals(const QString& fn1, const QString& fn2)
|
bool FileSystem::fileEquals(const QString& fn1, const QString& fn2)
|
||||||
@@ -514,20 +516,13 @@ QString FileSystem::fileSystemForPath(const QString & path)
|
|||||||
static QByteArray readToCrypto( const QString& filename, QCryptographicHash::Algorithm algo )
|
static QByteArray readToCrypto( const QString& filename, QCryptographicHash::Algorithm algo )
|
||||||
{
|
{
|
||||||
QFile file(filename);
|
QFile file(filename);
|
||||||
const qint64 bufSize = qMin(BUFSIZE, file.size() + 1);
|
|
||||||
QByteArray buf(bufSize, Qt::Uninitialized);
|
|
||||||
QByteArray arr;
|
QByteArray arr;
|
||||||
QCryptographicHash crypto( algo );
|
QCryptographicHash crypto( algo );
|
||||||
|
|
||||||
if (file.open(QIODevice::ReadOnly)) {
|
if (file.open(QIODevice::ReadOnly)) {
|
||||||
qint64 size;
|
if (crypto.addData(&file)) {
|
||||||
while (!file.atEnd()) {
|
arr = crypto.result().toHex();
|
||||||
size = file.read( buf.data(), bufSize );
|
|
||||||
if( size > 0 ) {
|
|
||||||
crypto.addData(buf.data(), size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
arr = crypto.result().toHex();
|
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ void RequestEtagJob::start()
|
|||||||
buf->setData(xml);
|
buf->setData(xml);
|
||||||
buf->open(QIODevice::ReadOnly);
|
buf->open(QIODevice::ReadOnly);
|
||||||
// assumes ownership
|
// assumes ownership
|
||||||
setReply(davRequest("PROPFIND", path(), req, buf));
|
sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
|
||||||
buf->setParent(reply());
|
|
||||||
setupConnections(reply());
|
|
||||||
|
|
||||||
if( reply()->error() != QNetworkReply::NoError ) {
|
if( reply()->error() != QNetworkReply::NoError ) {
|
||||||
qDebug() << "getting etag: request network error: " << reply()->errorString();
|
qDebug() << "getting etag: request network error: " << reply()->errorString();
|
||||||
@@ -122,10 +120,11 @@ void MkColJob::start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// assumes ownership
|
// assumes ownership
|
||||||
QNetworkReply *reply = _url.isValid() ? davRequest("MKCOL", _url, req)
|
if (_url.isValid()) {
|
||||||
: davRequest("MKCOL", path(), req);
|
sendRequest("MKCOL", _url, req);
|
||||||
setReply(reply);
|
} else {
|
||||||
setupConnections(reply);
|
sendRequest("MKCOL", makeDavUrl(path()), req);
|
||||||
|
}
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,11 +321,11 @@ void LsColJob::start()
|
|||||||
QBuffer *buf = new QBuffer(this);
|
QBuffer *buf = new QBuffer(this);
|
||||||
buf->setData(xml);
|
buf->setData(xml);
|
||||||
buf->open(QIODevice::ReadOnly);
|
buf->open(QIODevice::ReadOnly);
|
||||||
QNetworkReply *reply = _url.isValid() ? davRequest("PROPFIND", _url, req, buf)
|
if (_url.isValid()) {
|
||||||
: davRequest("PROPFIND", path(), req, buf);
|
sendRequest("PROPFIND", _url, req, buf);
|
||||||
buf->setParent(reply);
|
} else {
|
||||||
setReply(reply);
|
sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
|
||||||
setupConnections(reply);
|
}
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,8 +379,7 @@ CheckServerJob::CheckServerJob(AccountPtr account, QObject *parent)
|
|||||||
|
|
||||||
void CheckServerJob::start()
|
void CheckServerJob::start()
|
||||||
{
|
{
|
||||||
setReply(getRequest(path()));
|
sendRequest("GET", makeAccountUrl(path()));
|
||||||
setupConnections(reply());
|
|
||||||
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
|
connect(reply(), SIGNAL(metaDataChanged()), this, SLOT(metaDataChangedSlot()));
|
||||||
connect(reply(), SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
|
connect(reply(), SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
@@ -476,10 +474,7 @@ bool CheckServerJob::finished()
|
|||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
|
qDebug() << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
|
||||||
if( status.contains("installed")
|
if( status.contains("installed") ) {
|
||||||
&& status.contains("version")
|
|
||||||
&& status.contains("versionstring") ) {
|
|
||||||
|
|
||||||
emit instanceFound(reply()->url(), status);
|
emit instanceFound(reply()->url(), status);
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "No proper answer on " << reply()->url();
|
qDebug() << "No proper answer on " << reply()->url();
|
||||||
@@ -529,9 +524,7 @@ void PropfindJob::start()
|
|||||||
QBuffer *buf = new QBuffer(this);
|
QBuffer *buf = new QBuffer(this);
|
||||||
buf->setData(xml);
|
buf->setData(xml);
|
||||||
buf->open(QIODevice::ReadOnly);
|
buf->open(QIODevice::ReadOnly);
|
||||||
setReply(davRequest("PROPFIND", path(), req, buf));
|
sendRequest("PROPFIND", makeDavUrl(path()), req, buf);
|
||||||
buf->setParent(reply());
|
|
||||||
setupConnections(reply());
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,9 +625,7 @@ void ProppatchJob::start()
|
|||||||
QBuffer *buf = new QBuffer(this);
|
QBuffer *buf = new QBuffer(this);
|
||||||
buf->setData(xml);
|
buf->setData(xml);
|
||||||
buf->open(QIODevice::ReadOnly);
|
buf->open(QIODevice::ReadOnly);
|
||||||
setReply(davRequest("PROPPATCH", path(), req, buf));
|
sendRequest("PROPPATCH", makeDavUrl(path()), req, buf);
|
||||||
buf->setParent(reply());
|
|
||||||
setupConnections(reply());
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,8 +662,7 @@ EntityExistsJob::EntityExistsJob(AccountPtr account, const QString &path, QObjec
|
|||||||
|
|
||||||
void EntityExistsJob::start()
|
void EntityExistsJob::start()
|
||||||
{
|
{
|
||||||
setReply(headRequest(path()));
|
sendRequest("HEAD", makeAccountUrl(path()));
|
||||||
setupConnections(reply());
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,8 +690,7 @@ void JsonApiJob::start()
|
|||||||
QList<QPair<QString, QString> > params = _additionalParams;
|
QList<QPair<QString, QString> > params = _additionalParams;
|
||||||
params << qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"));
|
params << qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json"));
|
||||||
url.setQueryItems(params);
|
url.setQueryItems(params);
|
||||||
setReply(davRequest("GET", url, req));
|
sendRequest("GET", url, req);
|
||||||
setupConnections(reply());
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,32 +94,132 @@ int OwncloudPropagator::hardMaximumActiveJob()
|
|||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Updates, creates or removes a blacklist entry for the given item.
|
PropagateItemJob::~PropagateItemJob()
|
||||||
*
|
|
||||||
* Returns whether the error should be suppressed.
|
|
||||||
*/
|
|
||||||
static bool blacklistCheck(SyncJournalDb* journal, const SyncFileItem& item)
|
|
||||||
{
|
{
|
||||||
SyncJournalErrorBlacklistRecord oldEntry = journal->errorBlacklistEntry(item._file);
|
if (auto p = propagator()) {
|
||||||
SyncJournalErrorBlacklistRecord newEntry = SyncJournalErrorBlacklistRecord::update(oldEntry, item);
|
// Normally, every job should clean itself from the _activeJobList. So this should not be
|
||||||
|
// needed. But if a job has a bug or is deleted before the network jobs signal get received,
|
||||||
if (newEntry.isValid()) {
|
// we might risk end up with dangling pointer in the list which may cause crashes.
|
||||||
journal->updateErrorBlacklistEntry(newEntry);
|
p->_activeJobList.removeAll(this);
|
||||||
} else if (oldEntry.isValid()) {
|
|
||||||
journal->wipeErrorBlacklistEntry(item._file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// In some cases we add errors to the blacklist for tracking, but don't
|
|
||||||
// want to actively suppress them.
|
|
||||||
return newEntry.isValid() && newEntry._ignoreDuration > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorString)
|
static time_t getMinBlacklistTime()
|
||||||
{
|
{
|
||||||
|
return qMax(qgetenv("OWNCLOUD_BLACKLIST_TIME_MIN").toInt(),
|
||||||
|
25); // 25 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_t getMaxBlacklistTime()
|
||||||
|
{
|
||||||
|
int v = qgetenv("OWNCLOUD_BLACKLIST_TIME_MAX").toInt();
|
||||||
|
if (v > 0)
|
||||||
|
return v;
|
||||||
|
return 24*60*60; // 1 day
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a blacklist entry, possibly taking into account an old one.
|
||||||
|
*
|
||||||
|
* The old entry may be invalid, then a fresh entry is created.
|
||||||
|
*/
|
||||||
|
static SyncJournalErrorBlacklistRecord createBlacklistEntry(
|
||||||
|
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item)
|
||||||
|
{
|
||||||
|
SyncJournalErrorBlacklistRecord entry;
|
||||||
|
|
||||||
|
entry._errorString = item._errorString;
|
||||||
|
entry._lastTryModtime = item._modtime;
|
||||||
|
entry._lastTryEtag = item._etag;
|
||||||
|
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
||||||
|
entry._file = item._file;
|
||||||
|
entry._renameTarget = item._renameTarget;
|
||||||
|
|
||||||
|
entry._retryCount = old._retryCount + 1;
|
||||||
|
|
||||||
|
static time_t minBlacklistTime(getMinBlacklistTime());
|
||||||
|
static time_t maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
|
||||||
|
|
||||||
|
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
|
||||||
|
entry._ignoreDuration = old._ignoreDuration * 5;
|
||||||
|
|
||||||
|
if( item._httpErrorCode == 403 ) {
|
||||||
|
qDebug() << "Probably firewall error: " << item._httpErrorCode << ", blacklisting up to 1h only";
|
||||||
|
entry._ignoreDuration = qMin(entry._ignoreDuration, time_t(60*60));
|
||||||
|
|
||||||
|
} else if( item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
|
||||||
|
qDebug() << "Fatal Error condition" << item._httpErrorCode << ", maximum blacklist ignore time!";
|
||||||
|
entry._ignoreDuration = maxBlacklistTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry._ignoreDuration = qBound(minBlacklistTime, entry._ignoreDuration, maxBlacklistTime);
|
||||||
|
|
||||||
|
if( item._status == SyncFileItem::SoftError ) {
|
||||||
|
// Track these errors, but don't actively suppress them.
|
||||||
|
entry._ignoreDuration = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Updates, creates or removes a blacklist entry for the given item.
|
||||||
|
*
|
||||||
|
* May adjust the status or item._errorString.
|
||||||
|
*/
|
||||||
|
static void blacklistUpdate(SyncJournalDb* journal, SyncFileItem& item)
|
||||||
|
{
|
||||||
|
SyncJournalErrorBlacklistRecord oldEntry = journal->errorBlacklistEntry(item._file);
|
||||||
|
|
||||||
|
bool mayBlacklist =
|
||||||
|
item._errorMayBeBlacklisted // explicitly flagged for blacklisting
|
||||||
|
|| ((item._status == SyncFileItem::NormalError
|
||||||
|
|| item._status == SyncFileItem::SoftError)
|
||||||
|
&& item._httpErrorCode != 0 // or non-local error
|
||||||
|
);
|
||||||
|
|
||||||
|
// No new entry? Possibly remove the old one, then done.
|
||||||
|
if (!mayBlacklist) {
|
||||||
|
if (oldEntry.isValid()) {
|
||||||
|
journal->wipeErrorBlacklistEntry(item._file);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newEntry = createBlacklistEntry(oldEntry, item);
|
||||||
|
journal->updateErrorBlacklistEntry(newEntry);
|
||||||
|
|
||||||
|
// Suppress the error if it was and continues to be blacklisted.
|
||||||
|
// An ignoreDuration of 0 mean we're tracking the error, but not actively
|
||||||
|
// suppressing it.
|
||||||
|
if (item._hasBlacklistEntry && newEntry._ignoreDuration > 0) {
|
||||||
|
item._status = SyncFileItem::FileIgnored;
|
||||||
|
item._errorString.prepend(PropagateItemJob::tr("Continue blacklisting:") + " ");
|
||||||
|
|
||||||
|
qDebug() << "blacklisting " << item._file
|
||||||
|
<< " for " << newEntry._ignoreDuration
|
||||||
|
<< ", retry count " << newEntry._retryCount;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some soft errors might become louder on repeat occurrence
|
||||||
|
if (item._status == SyncFileItem::SoftError
|
||||||
|
&& newEntry._retryCount > 1) {
|
||||||
|
qDebug() << "escalating soft error on " << item._file
|
||||||
|
<< " to normal error, " << item._httpErrorCode;
|
||||||
|
item._status = SyncFileItem::NormalError;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropagateItemJob::done(SyncFileItem::Status statusArg, const QString &errorString)
|
||||||
|
{
|
||||||
|
_item->_status = statusArg;
|
||||||
|
|
||||||
_state = Finished;
|
_state = Finished;
|
||||||
if (_item->_isRestoration) {
|
if (_item->_isRestoration) {
|
||||||
if( status == SyncFileItem::Success || status == SyncFileItem::Conflict) {
|
if( _item->_status == SyncFileItem::Success
|
||||||
status = SyncFileItem::Restoration;
|
|| _item->_status == SyncFileItem::Conflict) {
|
||||||
|
_item->_status = SyncFileItem::Restoration;
|
||||||
} else {
|
} else {
|
||||||
_item->_errorString += tr("; Restoration Failed: %1").arg(errorString);
|
_item->_errorString += tr("; Restoration Failed: %1").arg(errorString);
|
||||||
}
|
}
|
||||||
@@ -130,24 +230,18 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( propagator()->_abortRequested.fetchAndAddRelaxed(0) &&
|
if( propagator()->_abortRequested.fetchAndAddRelaxed(0) &&
|
||||||
(status == SyncFileItem::NormalError || status == SyncFileItem::FatalError)) {
|
(_item->_status == SyncFileItem::NormalError
|
||||||
|
|| _item->_status == SyncFileItem::FatalError)) {
|
||||||
// an abort request is ongoing. Change the status to Soft-Error
|
// an abort request is ongoing. Change the status to Soft-Error
|
||||||
status = SyncFileItem::SoftError;
|
_item->_status = SyncFileItem::SoftError;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( status ) {
|
switch( _item->_status ) {
|
||||||
case SyncFileItem::SoftError:
|
case SyncFileItem::SoftError:
|
||||||
case SyncFileItem::FatalError:
|
case SyncFileItem::FatalError:
|
||||||
case SyncFileItem::NormalError:
|
case SyncFileItem::NormalError:
|
||||||
// For normal errors, we blacklist aggressively, otherwise only on
|
// Check the blacklist, possibly adjusting the item (including its status)
|
||||||
// explicit request.
|
blacklistUpdate(propagator()->_journal, *_item);
|
||||||
if ((status == SyncFileItem::NormalError || _item->_errorMayBeBlacklisted)
|
|
||||||
&& blacklistCheck(propagator()->_journal, *_item)
|
|
||||||
&& _item->_hasBlacklistEntry) {
|
|
||||||
// do not error if the item was, and continues to be, blacklisted
|
|
||||||
status = SyncFileItem::FileIgnored;
|
|
||||||
_item->_errorString.prepend(tr("Continue blacklisting:") + " ");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SyncFileItem::Success:
|
case SyncFileItem::Success:
|
||||||
case SyncFileItem::Restoration:
|
case SyncFileItem::Restoration:
|
||||||
@@ -167,10 +261,8 @@ void PropagateItemJob::done(SyncFileItem::Status status, const QString &errorStr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_item->_status = status;
|
|
||||||
|
|
||||||
emit itemCompleted(_item);
|
emit itemCompleted(_item);
|
||||||
emit finished(status);
|
emit finished(_item->_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -509,6 +601,36 @@ bool OwncloudPropagator::localFileNameClash( const QString& relFile )
|
|||||||
return re;
|
return re;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OwncloudPropagator::hasCaseClashAccessibilityProblem(const QString &relfile)
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
bool result = false;
|
||||||
|
const QString file( _localDir + relfile );
|
||||||
|
WIN32_FIND_DATA FindFileData;
|
||||||
|
HANDLE hFind;
|
||||||
|
|
||||||
|
hFind = FindFirstFileW(reinterpret_cast<const wchar_t*>(file.utf16()), &FindFileData);
|
||||||
|
if (hFind != INVALID_HANDLE_VALUE) {
|
||||||
|
QString firstFile = QString::fromWCharArray( FindFileData.cFileName );
|
||||||
|
if (FindNextFile(hFind, &FindFileData)) {
|
||||||
|
QString secondFile = QString::fromWCharArray( FindFileData.cFileName );
|
||||||
|
// This extra check shouldn't be necessary, but ensures that there
|
||||||
|
// are two different filenames that are identical when case is ignored.
|
||||||
|
if (firstFile != secondFile
|
||||||
|
&& QString::compare(firstFile, secondFile, Qt::CaseInsensitive) == 0) {
|
||||||
|
result = true;
|
||||||
|
qDebug() << "Found two filepaths that only differ in case: " << firstFile << secondFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FindClose(hFind);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
Q_UNUSED(relfile);
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
QString OwncloudPropagator::getFilePath(const QString& tmp_file_name) const
|
QString OwncloudPropagator::getFilePath(const QString& tmp_file_name) const
|
||||||
{
|
{
|
||||||
return _localDir + tmp_file_name;
|
return _localDir + tmp_file_name;
|
||||||
@@ -669,9 +791,11 @@ void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
|
|||||||
|
|
||||||
if (status == SyncFileItem::FatalError ||
|
if (status == SyncFileItem::FatalError ||
|
||||||
(wasFirstJob && status != SyncFileItem::Success && status != SyncFileItem::Restoration)) {
|
(wasFirstJob && status != SyncFileItem::Success && status != SyncFileItem::Restoration)) {
|
||||||
abort();
|
if (_state != Finished) {
|
||||||
_state = Finished;
|
abort();
|
||||||
emit finished(status);
|
_state = Finished;
|
||||||
|
emit finished(status);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
} else if (status == SyncFileItem::NormalError || status == SyncFileItem::SoftError) {
|
} else if (status == SyncFileItem::NormalError || status == SyncFileItem::SoftError) {
|
||||||
_hasError = status;
|
_hasError = status;
|
||||||
@@ -688,6 +812,9 @@ void PropagateDirectory::slotSubJobFinished(SyncFileItem::Status status)
|
|||||||
|
|
||||||
void PropagateDirectory::finalize()
|
void PropagateDirectory::finalize()
|
||||||
{
|
{
|
||||||
|
if (_state == Finished)
|
||||||
|
return;
|
||||||
|
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
if (!_item->isEmpty() && _hasError == SyncFileItem::NoStatus) {
|
if (!_item->isEmpty() && _hasError == SyncFileItem::NoStatus) {
|
||||||
if( !_item->_renameTarget.isEmpty() ) {
|
if( !_item->_renameTarget.isEmpty() ) {
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
PropagateItemJob(OwncloudPropagator* propagator, const SyncFileItemPtr &item)
|
PropagateItemJob(OwncloudPropagator* propagator, const SyncFileItemPtr &item)
|
||||||
: PropagatorJob(propagator), _item(item) {}
|
: PropagatorJob(propagator), _item(item) {}
|
||||||
|
~PropagateItemJob();
|
||||||
|
|
||||||
bool scheduleNextJob() Q_DECL_OVERRIDE {
|
bool scheduleNextJob() Q_DECL_OVERRIDE {
|
||||||
if (_state != NotYetStarted) {
|
if (_state != NotYetStarted) {
|
||||||
@@ -256,10 +257,7 @@ public:
|
|||||||
|
|
||||||
class OwncloudPropagator : public QObject {
|
class OwncloudPropagator : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
PropagateItemJob *createJob(const SyncFileItemPtr& item);
|
PropagateItemJob *createJob(const SyncFileItemPtr& item);
|
||||||
QScopedPointer<PropagateDirectory> _rootJob;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const QString _localDir; // absolute path to the local directory. ends with '/'
|
const QString _localDir; // absolute path to the local directory. ends with '/'
|
||||||
const QString _remoteFolder; // remote folder, ends with '/'
|
const QString _remoteFolder; // remote folder, ends with '/'
|
||||||
@@ -307,7 +305,23 @@ public:
|
|||||||
int hardMaximumActiveJob();
|
int hardMaximumActiveJob();
|
||||||
|
|
||||||
bool isInSharedDirectory(const QString& file);
|
bool isInSharedDirectory(const QString& file);
|
||||||
|
|
||||||
|
/** Check whether a download would clash with an existing file
|
||||||
|
* in filesystems that are only case-preserving.
|
||||||
|
*/
|
||||||
bool localFileNameClash(const QString& relfile);
|
bool localFileNameClash(const QString& relfile);
|
||||||
|
|
||||||
|
/** Check whether a file is properly accessible for upload.
|
||||||
|
*
|
||||||
|
* It is possible to create files with filenames that differ
|
||||||
|
* only by case in NTFS, but most operations such as stat and
|
||||||
|
* open only target one of these by default.
|
||||||
|
*
|
||||||
|
* When that happens, we want to avoid uploading incorrect data
|
||||||
|
* and give up on the file.
|
||||||
|
*/
|
||||||
|
bool hasCaseClashAccessibilityProblem(const QString& relfile);
|
||||||
|
|
||||||
QString getFilePath(const QString& tmp_file_name) const;
|
QString getFilePath(const QString& tmp_file_name) const;
|
||||||
|
|
||||||
void abort() {
|
void abort() {
|
||||||
@@ -369,6 +383,7 @@ signals:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
AccountPtr _account;
|
AccountPtr _account;
|
||||||
|
QScopedPointer<PropagateDirectory> _rootJob;
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||||
// access to signals which are protected in Qt4
|
// access to signals which are protected in Qt4
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ QString ownCloudTheme::about() const
|
|||||||
{
|
{
|
||||||
QString devString;
|
QString devString;
|
||||||
devString = trUtf8("<p>Version %2. For more information visit <a href=\"%3\">https://%4</a></p>"
|
devString = trUtf8("<p>Version %2. For more information visit <a href=\"%3\">https://%4</a></p>"
|
||||||
"<p>For known issues and help, please visit: <a href=\"https://central.owncloud.org/c/help/desktop-file-sync\">https://central.owncloud.org</a></p>"
|
"<p>For known issues and help, please visit: <a href=\"https://central.owncloud.org/c/desktop-client\">https://central.owncloud.org</a></p>"
|
||||||
"<p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, "
|
"<p><small>By Klaas Freitag, Daniel Molkentin, Olivier Goffart, Markus Götz, "
|
||||||
" Jan-Christoph Borchardt, and others.</small></p>"
|
" Jan-Christoph Borchardt, and others.</small></p>"
|
||||||
"<p>Copyright ownCloud GmbH</p>"
|
"<p>Copyright ownCloud GmbH</p>"
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
#include "ownsql.h"
|
#include "ownsql.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
@@ -55,6 +57,12 @@ bool SqlDatabase::openHelper( const QString& filename, int sqliteFlags )
|
|||||||
|
|
||||||
if( _errId != SQLITE_OK ) {
|
if( _errId != SQLITE_OK ) {
|
||||||
qDebug() << "Error:" << _error << "for" << filename;
|
qDebug() << "Error:" << _error << "for" << filename;
|
||||||
|
if (_errId == SQLITE_CANTOPEN) {
|
||||||
|
qDebug() << "CANTOPEN extended errcode: " << sqlite3_extended_errcode(_db);
|
||||||
|
#if SQLITE_VERSION_NUMBER >= 3012000
|
||||||
|
qDebug() << "CANTOPEN system errno: " << sqlite3_system_errno(_db);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -101,8 +109,8 @@ bool SqlDatabase::openOrCreateReadWrite( const QString& filename )
|
|||||||
|
|
||||||
if( !checkDb() ) {
|
if( !checkDb() ) {
|
||||||
// When disk space is low, checking the db may fail even though it's fine.
|
// When disk space is low, checking the db may fail even though it's fine.
|
||||||
qint64 freeSpace = Utility::freeDiskSpace(filename);
|
qint64 freeSpace = Utility::freeDiskSpace(QFileInfo(filename).dir().absolutePath());
|
||||||
if (freeSpace < 1000000) {
|
if (freeSpace != -1 && freeSpace < 1000000) {
|
||||||
qDebug() << "Consistency check failed, disk space is low, aborting" << freeSpace;
|
qDebug() << "Consistency check failed, disk space is low, aborting" << freeSpace;
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -100,12 +100,11 @@ void GETFileJob::start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_directDownloadUrl.isEmpty()) {
|
if (_directDownloadUrl.isEmpty()) {
|
||||||
setReply(davRequest("GET", path(), req));
|
sendRequest("GET", makeDavUrl(path()), req);
|
||||||
} else {
|
} else {
|
||||||
// Use direct URL
|
// Use direct URL
|
||||||
setReply(davRequest("GET", _directDownloadUrl, req));
|
sendRequest("GET", _directDownloadUrl, req);
|
||||||
}
|
}
|
||||||
setupConnections(reply());
|
|
||||||
|
|
||||||
reply()->setReadBufferSize(16 * 1024); // keep low so we can easier limit the bandwidth
|
reply()->setReadBufferSize(16 * 1024); // keep low so we can easier limit the bandwidth
|
||||||
qDebug() << Q_FUNC_INFO << _bandwidthManager << _bandwidthChoked << _bandwidthLimited;
|
qDebug() << Q_FUNC_INFO << _bandwidthManager << _bandwidthChoked << _bandwidthLimited;
|
||||||
@@ -232,6 +231,8 @@ qint64 GETFileJob::currentDownloadPosition()
|
|||||||
|
|
||||||
void GETFileJob::slotReadyRead()
|
void GETFileJob::slotReadyRead()
|
||||||
{
|
{
|
||||||
|
if (!reply())
|
||||||
|
return;
|
||||||
int bufferSize = qMin(1024*8ll , reply()->bytesAvailable());
|
int bufferSize = qMin(1024*8ll , reply()->bytesAvailable());
|
||||||
QByteArray buffer(bufferSize, Qt::Uninitialized);
|
QByteArray buffer(bufferSize, Qt::Uninitialized);
|
||||||
|
|
||||||
@@ -499,6 +500,11 @@ void PropagateDownloadFile::slotGetFinished()
|
|||||||
} else if (fileNotFound) {
|
} else if (fileNotFound) {
|
||||||
job->setErrorString(tr("File was deleted from server"));
|
job->setErrorString(tr("File was deleted from server"));
|
||||||
job->setErrorStatus(SyncFileItem::SoftError);
|
job->setErrorStatus(SyncFileItem::SoftError);
|
||||||
|
|
||||||
|
// As a precaution against bugs that cause our database and the
|
||||||
|
// reality on the server to diverge, rediscover this folder on the
|
||||||
|
// next sync run.
|
||||||
|
propagator()->_journal->avoidReadFromDbOnNextSync(_item->_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncFileItem::Status status = job->errorStatus();
|
SyncFileItem::Status status = job->errorStatus();
|
||||||
@@ -768,7 +774,7 @@ void PropagateDownloadFile::downloadFinished()
|
|||||||
// Apply the remote permissions
|
// Apply the remote permissions
|
||||||
// Older server versions sometimes provide empty remote permissions
|
// Older server versions sometimes provide empty remote permissions
|
||||||
// see #4450 - don't adjust the write permissions there.
|
// see #4450 - don't adjust the write permissions there.
|
||||||
const int serverVersionGoodRemotePerm = 0x070000; // 7.0.0
|
const int serverVersionGoodRemotePerm = Account::makeServerVersion(7, 0, 0);
|
||||||
if (propagator()->account()->serverVersionInt() >= serverVersionGoodRemotePerm) {
|
if (propagator()->account()->serverVersionInt() >= serverVersionGoodRemotePerm) {
|
||||||
FileSystem::setFileReadOnlyWeak(_tmpFile.fileName(),
|
FileSystem::setFileReadOnlyWeak(_tmpFile.fileName(),
|
||||||
!_item->_remotePerm.contains('W'));
|
!_item->_remotePerm.contains('W'));
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ DeleteJob::DeleteJob(AccountPtr account, const QUrl& url, QObject* parent)
|
|||||||
void DeleteJob::start()
|
void DeleteJob::start()
|
||||||
{
|
{
|
||||||
QNetworkRequest req;
|
QNetworkRequest req;
|
||||||
setReply(_url.isValid() ? davRequest("DELETE", _url, req) : davRequest("DELETE", path(), req));
|
if (_url.isValid()) {
|
||||||
setupConnections(reply());
|
sendRequest("DELETE", _url, req);
|
||||||
|
} else {
|
||||||
|
sendRequest("DELETE", makeDavUrl(path()), req);
|
||||||
|
}
|
||||||
|
|
||||||
if( reply()->error() != QNetworkReply::NoError ) {
|
if( reply()->error() != QNetworkReply::NoError ) {
|
||||||
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
|
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
|
||||||
|
|||||||
@@ -43,8 +43,11 @@ void MoveJob::start()
|
|||||||
for(auto it = _extraHeaders.constBegin(); it != _extraHeaders.constEnd(); ++it) {
|
for(auto it = _extraHeaders.constBegin(); it != _extraHeaders.constEnd(); ++it) {
|
||||||
req.setRawHeader(it.key(), it.value());
|
req.setRawHeader(it.key(), it.value());
|
||||||
}
|
}
|
||||||
setReply(_url.isValid() ? davRequest("MOVE", _url, req) : davRequest("MOVE", path(), req));
|
if (_url.isValid()) {
|
||||||
setupConnections(reply());
|
sendRequest("MOVE", _url, req);
|
||||||
|
} else {
|
||||||
|
sendRequest("MOVE", makeDavUrl(path()), req);
|
||||||
|
}
|
||||||
|
|
||||||
if( reply()->error() != QNetworkReply::NoError ) {
|
if( reply()->error() != QNetworkReply::NoError ) {
|
||||||
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
|
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
|
||||||
|
|||||||
@@ -73,9 +73,11 @@ void PUTFileJob::start() {
|
|||||||
req.setRawHeader(it.key(), it.value());
|
req.setRawHeader(it.key(), it.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
setReply(_url.isValid() ? davRequest("PUT", _url, req, _device.data())
|
if (_url.isValid()) {
|
||||||
: davRequest("PUT", path(), req, _device.data()));
|
sendRequest("PUT", _url, req, _device);
|
||||||
setupConnections(reply());
|
} else {
|
||||||
|
sendRequest("PUT", makeDavUrl(path()), req, _device);
|
||||||
|
}
|
||||||
|
|
||||||
if( reply()->error() != QNetworkReply::NoError ) {
|
if( reply()->error() != QNetworkReply::NoError ) {
|
||||||
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
|
qWarning() << Q_FUNC_INFO << " Network error: " << reply()->errorString();
|
||||||
@@ -89,10 +91,10 @@ void PUTFileJob::start() {
|
|||||||
// (workaround disabled on windows and mac because the binaries we ship have patched qt)
|
// (workaround disabled on windows and mac because the binaries we ship have patched qt)
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 7)
|
#if QT_VERSION < QT_VERSION_CHECK(4, 8, 7)
|
||||||
if (QLatin1String(qVersion()) < QLatin1String("4.8.7"))
|
if (QLatin1String(qVersion()) < QLatin1String("4.8.7"))
|
||||||
connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
|
connect(_device, 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
|
#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"))
|
if (QLatin1String(qVersion()) < QLatin1String("5.4.2"))
|
||||||
connect(_device.data(), SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
|
connect(_device, SIGNAL(wasReset()), this, SLOT(slotSoftAbort()));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
@@ -119,8 +121,7 @@ void PollJob::start()
|
|||||||
QUrl accountUrl = account()->url();
|
QUrl accountUrl = account()->url();
|
||||||
QUrl finalUrl = QUrl::fromUserInput(accountUrl.scheme() + QLatin1String("://") + accountUrl.authority()
|
QUrl finalUrl = QUrl::fromUserInput(accountUrl.scheme() + QLatin1String("://") + accountUrl.authority()
|
||||||
+ (path().startsWith('/') ? QLatin1String("") : QLatin1String("/")) + path());
|
+ (path().startsWith('/') ? QLatin1String("") : QLatin1String("/")) + path());
|
||||||
setReply(getRequest(finalUrl));
|
sendRequest("GET", finalUrl);
|
||||||
setupConnections(reply());
|
|
||||||
connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(resetTimeout()));
|
connect(reply(), SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(resetTimeout()));
|
||||||
AbstractNetworkJob::start();
|
AbstractNetworkJob::start();
|
||||||
}
|
}
|
||||||
@@ -198,6 +199,13 @@ void PropagateUploadFileCommon::start()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the specific file can be accessed
|
||||||
|
if( propagator()->hasCaseClashAccessibilityProblem(_item->_file) ) {
|
||||||
|
done( SyncFileItem::NormalError, tr("File %1 cannot be uploaded because another file with the same name, differing only in case, exists")
|
||||||
|
.arg(QDir::toNativeSeparators(_item->_file)) );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
propagator()->_activeJobList.append(this);
|
propagator()->_activeJobList.append(this);
|
||||||
|
|
||||||
if (!_deleteExisting) {
|
if (!_deleteExisting) {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class PUTFileJob : public AbstractNetworkJob {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QScopedPointer<QIODevice> _device;
|
QIODevice* _device;
|
||||||
QMap<QByteArray, QByteArray> _headers;
|
QMap<QByteArray, QByteArray> _headers;
|
||||||
QString _errorString;
|
QString _errorString;
|
||||||
QUrl _url;
|
QUrl _url;
|
||||||
@@ -95,11 +95,17 @@ public:
|
|||||||
// Takes ownership of the device
|
// Takes ownership of the device
|
||||||
explicit PUTFileJob(AccountPtr account, const QString& path, QIODevice *device,
|
explicit PUTFileJob(AccountPtr account, const QString& path, QIODevice *device,
|
||||||
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
|
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
|
||||||
: AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk) {}
|
: AbstractNetworkJob(account, path, parent), _device(device), _headers(headers), _chunk(chunk)
|
||||||
|
{
|
||||||
|
_device->setParent(this);
|
||||||
|
}
|
||||||
explicit PUTFileJob(AccountPtr account, const QUrl& url, QIODevice *device,
|
explicit PUTFileJob(AccountPtr account, const QUrl& url, QIODevice *device,
|
||||||
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
|
const QMap<QByteArray, QByteArray> &headers, int chunk, QObject* parent = 0)
|
||||||
: AbstractNetworkJob(account, QString(), parent), _device(device), _headers(headers)
|
: AbstractNetworkJob(account, QString(), parent), _device(device), _headers(headers)
|
||||||
, _url(url), _chunk(chunk) {}
|
, _url(url), _chunk(chunk)
|
||||||
|
{
|
||||||
|
_device->setParent(this);
|
||||||
|
}
|
||||||
~PUTFileJob();
|
~PUTFileJob();
|
||||||
|
|
||||||
int _chunk;
|
int _chunk;
|
||||||
|
|||||||
@@ -292,6 +292,8 @@ void PropagateUploadFileNG::startNextChunk()
|
|||||||
_transmissionChecksumType, _transmissionChecksum);
|
_transmissionChecksumType, _transmissionChecksum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
headers["OC-Total-Length"] = QByteArray::number(fileSize);
|
||||||
|
|
||||||
auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), "/.file"),
|
auto job = new MoveJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), "/.file"),
|
||||||
destination, headers, this);
|
destination, headers, this);
|
||||||
_jobs.append(job);
|
_jobs.append(job);
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ void PropagateUploadFileV1::startNextChunk()
|
|||||||
parallelChunkUpload = env != "false" && env != "0";
|
parallelChunkUpload = env != "false" && env != "0";
|
||||||
} else {
|
} else {
|
||||||
int versionNum = propagator()->account()->serverVersionInt();
|
int versionNum = propagator()->account()->serverVersionInt();
|
||||||
if (versionNum < 0x080003) {
|
if (versionNum < Account::makeServerVersion(8, 0, 3)) {
|
||||||
// Disable parallel chunk upload severs older than 8.0.3 to avoid too many
|
// Disable parallel chunk upload severs older than 8.0.3 to avoid too many
|
||||||
// internal sever errors (#2743, #2938)
|
// internal sever errors (#2743, #2938)
|
||||||
parallelChunkUpload = false;
|
parallelChunkUpload = false;
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ bool SyncEngine::checkErrorBlacklisting( SyncFileItem &item )
|
|||||||
|
|
||||||
// If duration has expired, it's not blacklisted anymore
|
// If duration has expired, it's not blacklisted anymore
|
||||||
time_t now = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
time_t now = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
||||||
if( now > entry._lastTryTime + entry._ignoreDuration ) {
|
if( now >= entry._lastTryTime + entry._ignoreDuration ) {
|
||||||
qDebug() << "blacklist entry for " << item._file << " has expired!";
|
qDebug() << "blacklist entry for " << item._file << " has expired!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -386,8 +386,10 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
|||||||
|
|
||||||
if (item->_instruction == CSYNC_INSTRUCTION_NONE
|
if (item->_instruction == CSYNC_INSTRUCTION_NONE
|
||||||
|| (item->_instruction == CSYNC_INSTRUCTION_IGNORE && instruction != CSYNC_INSTRUCTION_NONE)) {
|
|| (item->_instruction == CSYNC_INSTRUCTION_IGNORE && instruction != CSYNC_INSTRUCTION_NONE)) {
|
||||||
|
// Take values from side (local/remote) where instruction is not _NONE
|
||||||
item->_instruction = instruction;
|
item->_instruction = instruction;
|
||||||
item->_modtime = file->modtime;
|
item->_modtime = file->modtime;
|
||||||
|
item->_size = file->size;
|
||||||
} else {
|
} else {
|
||||||
if (instruction != CSYNC_INSTRUCTION_NONE) {
|
if (instruction != CSYNC_INSTRUCTION_NONE) {
|
||||||
qWarning() << "ERROR: Instruction" << item->_instruction << "vs" << instruction << "for" << fileUtf8;
|
qWarning() << "ERROR: Instruction" << item->_instruction << "vs" << instruction << "for" << fileUtf8;
|
||||||
@@ -517,7 +519,7 @@ int SyncEngine::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
|||||||
if (file->etag && file->etag[0]) {
|
if (file->etag && file->etag[0]) {
|
||||||
item->_etag = file->etag;
|
item->_etag = file->etag;
|
||||||
}
|
}
|
||||||
item->_size = file->size;
|
|
||||||
|
|
||||||
if (!item->_inode) {
|
if (!item->_inode) {
|
||||||
item->_inode = file->inode;
|
item->_inode = file->inode;
|
||||||
@@ -930,7 +932,7 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check for invalid character in old server version
|
// Check for invalid character in old server version
|
||||||
if (_account->serverVersionInt() < 0x080100) {
|
if (_account->serverVersionInt() < Account::makeServerVersion(8, 1, 0)) {
|
||||||
// Server version older than 8.1 don't support these character in filename.
|
// Server version older than 8.1 don't support these character in filename.
|
||||||
static const QRegExp invalidCharRx("[\\\\:?*\"<>|]");
|
static const QRegExp invalidCharRx("[\\\\:?*\"<>|]");
|
||||||
for (auto it = syncItems.begin(); it != syncItems.end(); ++it) {
|
for (auto it = syncItems.begin(); it != syncItems.end(); ++it) {
|
||||||
@@ -961,7 +963,8 @@ void SyncEngine::slotDiscoveryJobFinished(int discoveryResult)
|
|||||||
&& _discoveryMainThread->_dataFingerprint != databaseFingerprint) {
|
&& _discoveryMainThread->_dataFingerprint != databaseFingerprint) {
|
||||||
qDebug() << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryMainThread->_dataFingerprint;
|
qDebug() << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryMainThread->_dataFingerprint;
|
||||||
restoreOldFiles(syncItems);
|
restoreOldFiles(syncItems);
|
||||||
} else if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2 && _account->serverVersionInt() < 0x090100) {
|
} else if (!_hasForwardInTimeFiles && _backInTimeFiles >= 2
|
||||||
|
&& _account->serverVersionInt() < Account::makeServerVersion(9, 1, 0)) {
|
||||||
// The server before ownCloud 9.1 did not have the data-fingerprint property. So in that
|
// The server before ownCloud 9.1 did not have the data-fingerprint property. So in that
|
||||||
// case we use heuristics to detect restored backup. This is disabled with newer version
|
// case we use heuristics to detect restored backup. This is disabled with newer version
|
||||||
// because this causes troubles to the user and is not as reliable as the data-fingerprint.
|
// because this causes troubles to the user and is not as reliable as the data-fingerprint.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
#include "ownsql.h"
|
#include "ownsql.h"
|
||||||
|
|
||||||
@@ -41,7 +42,8 @@ SyncJournalDb::SyncJournalDb(const QString& dbFilePath, QObject *parent) :
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SyncJournalDb::makeDbName(const QUrl& remoteUrl,
|
QString SyncJournalDb::makeDbName(const QString& localPath,
|
||||||
|
const QUrl& remoteUrl,
|
||||||
const QString& remotePath,
|
const QString& remotePath,
|
||||||
const QString& user)
|
const QString& user)
|
||||||
{
|
{
|
||||||
@@ -56,6 +58,42 @@ QString SyncJournalDb::makeDbName(const QUrl& remoteUrl,
|
|||||||
journalPath.append( ba.left(6).toHex() );
|
journalPath.append( ba.left(6).toHex() );
|
||||||
journalPath.append(".db");
|
journalPath.append(".db");
|
||||||
|
|
||||||
|
// If the journal doesn't exist and we can't create a file
|
||||||
|
// at that location, try again with a journal name that doesn't
|
||||||
|
// have the ._ prefix.
|
||||||
|
//
|
||||||
|
// The disadvantage of that filename is that it will only be ignored
|
||||||
|
// by client versions >2.3.2.
|
||||||
|
//
|
||||||
|
// See #5633: "._*" is often forbidden on samba shared folders.
|
||||||
|
|
||||||
|
// If it exists already, the path is clearly usable
|
||||||
|
QFile file(QDir(localPath).filePath(journalPath));
|
||||||
|
if (file.exists()) {
|
||||||
|
return journalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to create a file there
|
||||||
|
if (file.open(QIODevice::ReadWrite)) {
|
||||||
|
// Ok, all good.
|
||||||
|
file.close();
|
||||||
|
file.remove();
|
||||||
|
return journalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can we create it if we drop the underscore?
|
||||||
|
QString alternateJournalPath = journalPath.mid(2).prepend(".");
|
||||||
|
QFile file2(QDir(localPath).filePath(alternateJournalPath));
|
||||||
|
if (file2.open(QIODevice::ReadWrite)) {
|
||||||
|
// The alternative worked, use it
|
||||||
|
qDebug() << "Using alternate database path" << alternateJournalPath;
|
||||||
|
file2.close();
|
||||||
|
file2.remove();
|
||||||
|
return alternateJournalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neither worked, just keep the original and throw errors later
|
||||||
|
qDebug() << "Could not find a writable database path" << file.fileName();
|
||||||
return journalPath;
|
return journalPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ public:
|
|||||||
virtual ~SyncJournalDb();
|
virtual ~SyncJournalDb();
|
||||||
|
|
||||||
/// Create a journal path for a specific configuration
|
/// Create a journal path for a specific configuration
|
||||||
static QString makeDbName(const QUrl& remoteUrl,
|
static QString makeDbName(const QString& localPath,
|
||||||
|
const QUrl& remoteUrl,
|
||||||
const QString& remotePath,
|
const QString& remotePath,
|
||||||
const QString& user);
|
const QString& user);
|
||||||
|
|
||||||
@@ -136,6 +137,14 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Make sure that on the next sync, fileName is not read from the DB but uses the PROPFIND to
|
* Make sure that on the next sync, fileName is not read from the DB but uses the PROPFIND to
|
||||||
* get the info from the server
|
* get the info from the server
|
||||||
|
*
|
||||||
|
* Specifically, this sets the md5 field of fileName and all its parents to _invalid_.
|
||||||
|
* That causes a metadata difference and a resulting discovery from the remote for the
|
||||||
|
* affected folders.
|
||||||
|
*
|
||||||
|
* Since folders in the selective sync list will not be rediscovered (csync_ftw,
|
||||||
|
* _csync_detect_update skip them), the _invalid_ marker will stay and it. And any
|
||||||
|
* child items in the db will be ignored when reading a remote tree from the database.
|
||||||
*/
|
*/
|
||||||
void avoidReadFromDbOnNextSync(const QString& fileName);
|
void avoidReadFromDbOnNextSync(const QString& fileName);
|
||||||
|
|
||||||
|
|||||||
@@ -103,20 +103,6 @@ SyncFileItem SyncJournalFileRecord::toSyncFileItem()
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static time_t getMinBlacklistTime()
|
|
||||||
{
|
|
||||||
return qMax(qgetenv("OWNCLOUD_BLACKLIST_TIME_MIN").toInt(),
|
|
||||||
25); // 25 seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_t getMaxBlacklistTime()
|
|
||||||
{
|
|
||||||
int v = qgetenv("OWNCLOUD_BLACKLIST_TIME_MAX").toInt();
|
|
||||||
if (v > 0)
|
|
||||||
return v;
|
|
||||||
return 24*60*60; // 1 day
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SyncJournalErrorBlacklistRecord::isValid() const
|
bool SyncJournalErrorBlacklistRecord::isValid() const
|
||||||
{
|
{
|
||||||
return ! _file.isEmpty()
|
return ! _file.isEmpty()
|
||||||
@@ -124,54 +110,6 @@ bool SyncJournalErrorBlacklistRecord::isValid() const
|
|||||||
&& _lastTryTime > 0;
|
&& _lastTryTime > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncJournalErrorBlacklistRecord SyncJournalErrorBlacklistRecord::update(
|
|
||||||
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item)
|
|
||||||
{
|
|
||||||
SyncJournalErrorBlacklistRecord entry;
|
|
||||||
bool mayBlacklist =
|
|
||||||
item._errorMayBeBlacklisted // explicitly flagged for blacklisting
|
|
||||||
|| (item._httpErrorCode != 0 // or non-local error
|
|
||||||
#ifdef OWNCLOUD_5XX_NO_BLACKLIST
|
|
||||||
&& item._httpErrorCode / 100 != 5 // In this configuration, never blacklist error 5xx
|
|
||||||
#endif
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!mayBlacklist) {
|
|
||||||
qDebug() << "This error is not blacklisted " << item._httpErrorCode << item._errorMayBeBlacklisted;
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
static time_t minBlacklistTime(getMinBlacklistTime());
|
|
||||||
static time_t maxBlacklistTime(qMax(getMaxBlacklistTime(), minBlacklistTime));
|
|
||||||
|
|
||||||
entry._retryCount = old._retryCount + 1;
|
|
||||||
entry._errorString = item._errorString;
|
|
||||||
entry._lastTryModtime = item._modtime;
|
|
||||||
entry._lastTryEtag = item._etag;
|
|
||||||
entry._lastTryTime = Utility::qDateTimeToTime_t(QDateTime::currentDateTime());
|
|
||||||
// The factor of 5 feels natural: 25s, 2 min, 10 min, ~1h, ~5h, ~24h
|
|
||||||
entry._ignoreDuration = old._ignoreDuration * 5;
|
|
||||||
entry._file = item._file;
|
|
||||||
entry._renameTarget = item._renameTarget;
|
|
||||||
|
|
||||||
if( item._httpErrorCode == 403 ) {
|
|
||||||
qDebug() << "Probably firewall error: " << item._httpErrorCode << ", blacklisting up to 1h only";
|
|
||||||
entry._ignoreDuration = qMin(entry._ignoreDuration, time_t(60*60));
|
|
||||||
|
|
||||||
} else if( item._httpErrorCode == 413 || item._httpErrorCode == 415 ) {
|
|
||||||
qDebug() << "Fatal Error condition" << item._httpErrorCode << ", maximum blacklist ignore time!";
|
|
||||||
entry._ignoreDuration = maxBlacklistTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry._ignoreDuration = qBound(minBlacklistTime, entry._ignoreDuration, maxBlacklistTime);
|
|
||||||
|
|
||||||
qDebug() << "blacklisting " << item._file
|
|
||||||
<< " for " << entry._ignoreDuration
|
|
||||||
<< ", retry count " << entry._retryCount;
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool operator==(const SyncJournalFileRecord & lhs,
|
bool operator==(const SyncJournalFileRecord & lhs,
|
||||||
const SyncJournalFileRecord & rhs)
|
const SyncJournalFileRecord & rhs)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -93,15 +93,6 @@ public:
|
|||||||
QString _renameTarget;
|
QString _renameTarget;
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
/** Takes an old blacklist entry and updates it for a new sync result.
|
|
||||||
*
|
|
||||||
* The old entry may be invalid, then a fresh entry is created.
|
|
||||||
* If the returned record is invalid, the file shall not be
|
|
||||||
* blacklisted.
|
|
||||||
*/
|
|
||||||
static SyncJournalErrorBlacklistRecord update(
|
|
||||||
const SyncJournalErrorBlacklistRecord& old, const SyncFileItem& item);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#ifndef TOKEN_AUTH_ONLY
|
#ifndef TOKEN_AUTH_ONLY
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
#endif
|
#endif
|
||||||
|
#include <QSslSocket>
|
||||||
|
|
||||||
#include "owncloudtheme.h"
|
#include "owncloudtheme.h"
|
||||||
|
|
||||||
@@ -297,8 +298,13 @@ QString Theme::gitSHA1() const
|
|||||||
" on %3, %4 using Qt %5, %6</small></p>")
|
" on %3, %4 using Qt %5, %6</small></p>")
|
||||||
.arg(githubPrefix+gitSha1).arg(gitSha1.left(6))
|
.arg(githubPrefix+gitSha1).arg(gitSha1.left(6))
|
||||||
.arg(__DATE__).arg(__TIME__)
|
.arg(__DATE__).arg(__TIME__)
|
||||||
.arg(QT_VERSION_STR)
|
.arg(QString::fromAscii(qVersion()))
|
||||||
.arg(QString::fromAscii(OPENSSL_VERSION_TEXT));
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||||
|
.arg(QSslSocket::sslLibraryVersionString());
|
||||||
|
#else
|
||||||
|
.arg(QCoreApplication::translate("ownCloudTheme::about()", "built with %1").arg(
|
||||||
|
QString::fromAscii(OPENSSL_VERSION_TEXT)));
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return devString;
|
return devString;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ QString Utility::octetsToString( qint64 octets )
|
|||||||
QString s;
|
QString s;
|
||||||
qreal value = octets;
|
qreal value = octets;
|
||||||
|
|
||||||
// Whether we care about decimals: only for GB and only
|
// Whether we care about decimals: only for GB/MB and only
|
||||||
// if it's less than 10 GB.
|
// if it's less than 10 units.
|
||||||
bool round = true;
|
bool round = true;
|
||||||
|
|
||||||
// do not display terra byte with the current units, as when
|
// do not display terra byte with the current units, as when
|
||||||
@@ -137,6 +137,7 @@ QString Utility::octetsToString( qint64 octets )
|
|||||||
} else if (octets >= mb) {
|
} else if (octets >= mb) {
|
||||||
s = QCoreApplication::translate("Utility", "%L1 MB");
|
s = QCoreApplication::translate("Utility", "%L1 MB");
|
||||||
value /= mb;
|
value /= mb;
|
||||||
|
round = false;
|
||||||
} else if (octets >= kb) {
|
} else if (octets >= kb) {
|
||||||
s = QCoreApplication::translate("Utility", "%L1 KB");
|
s = QCoreApplication::translate("Utility", "%L1 KB");
|
||||||
value /= kb;
|
value /= kb;
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ namespace Utility
|
|||||||
OWNCLOUDSYNC_EXPORT QByteArray userAgentString();
|
OWNCLOUDSYNC_EXPORT QByteArray userAgentString();
|
||||||
OWNCLOUDSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName);
|
OWNCLOUDSYNC_EXPORT bool hasLaunchOnStartup(const QString &appName);
|
||||||
OWNCLOUDSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString& guiName, bool launch);
|
OWNCLOUDSYNC_EXPORT void setLaunchOnStartup(const QString &appName, const QString& guiName, bool launch);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the amount of free space available.
|
||||||
|
*
|
||||||
|
* \a path must point to a directory
|
||||||
|
*/
|
||||||
OWNCLOUDSYNC_EXPORT qint64 freeDiskSpace(const QString &path);
|
OWNCLOUDSYNC_EXPORT qint64 freeDiskSpace(const QString &path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ list(APPEND FolderWatcher_SRC ../src/gui/socketapisocket_mac.mm)
|
|||||||
ENDIF()
|
ENDIF()
|
||||||
owncloud_add_test(CSyncSqlite "")
|
owncloud_add_test(CSyncSqlite "")
|
||||||
owncloud_add_test(NetrcParser ../src/cmd/netrcparser.cpp)
|
owncloud_add_test(NetrcParser ../src/cmd/netrcparser.cpp)
|
||||||
owncloud_add_test(OwnSql "")
|
|
||||||
owncloud_add_test(SyncJournalDB "")
|
owncloud_add_test(SyncJournalDB "")
|
||||||
owncloud_add_test(SyncFileItem "")
|
owncloud_add_test(SyncFileItem "")
|
||||||
owncloud_add_test(ConcatUrl "")
|
owncloud_add_test(ConcatUrl "")
|
||||||
@@ -35,6 +34,7 @@ owncloud_add_test(ChecksumValidator "")
|
|||||||
owncloud_add_test(ExcludedFiles "")
|
owncloud_add_test(ExcludedFiles "")
|
||||||
if(HAVE_QT5 AND NOT BUILD_WITH_QT4)
|
if(HAVE_QT5 AND NOT BUILD_WITH_QT4)
|
||||||
owncloud_add_test(FileSystem "")
|
owncloud_add_test(FileSystem "")
|
||||||
|
owncloud_add_test(OwnSql "")
|
||||||
owncloud_add_test(Utility "")
|
owncloud_add_test(Utility "")
|
||||||
owncloud_add_test(SyncEngine "syncenginetestutils.h")
|
owncloud_add_test(SyncEngine "syncenginetestutils.h")
|
||||||
owncloud_add_test(SyncFileStatusTracker "syncenginetestutils.h")
|
owncloud_add_test(SyncFileStatusTracker "syncenginetestutils.h")
|
||||||
|
|||||||
@@ -536,6 +536,7 @@ public:
|
|||||||
const FileInfo *fileInfo;
|
const FileInfo *fileInfo;
|
||||||
char payload;
|
char payload;
|
||||||
int size;
|
int size;
|
||||||
|
bool aborted = false;
|
||||||
|
|
||||||
FakeGetReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
|
FakeGetReply(FileInfo &remoteRootFileInfo, QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
|
||||||
: QNetworkReply{parent} {
|
: QNetworkReply{parent} {
|
||||||
@@ -551,6 +552,12 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_INVOKABLE void respond() {
|
Q_INVOKABLE void respond() {
|
||||||
|
if (aborted) {
|
||||||
|
setError(OperationCanceledError, "Operation Canceled");
|
||||||
|
emit metaDataChanged();
|
||||||
|
emit finished();
|
||||||
|
return;
|
||||||
|
}
|
||||||
payload = fileInfo->contentChar;
|
payload = fileInfo->contentChar;
|
||||||
size = fileInfo->size;
|
size = fileInfo->size;
|
||||||
setHeader(QNetworkRequest::ContentLengthHeader, size);
|
setHeader(QNetworkRequest::ContentLengthHeader, size);
|
||||||
@@ -564,8 +571,14 @@ public:
|
|||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void abort() override { }
|
void abort() override {
|
||||||
qint64 bytesAvailable() const override { return size + QIODevice::bytesAvailable(); }
|
aborted = true;
|
||||||
|
}
|
||||||
|
qint64 bytesAvailable() const override {
|
||||||
|
if (aborted)
|
||||||
|
return 0;
|
||||||
|
return size + QIODevice::bytesAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
qint64 readData(char *data, qint64 maxlen) override {
|
qint64 readData(char *data, qint64 maxlen) override {
|
||||||
qint64 len = std::min(qint64{size}, maxlen);
|
qint64 len = std::min(qint64{size}, maxlen);
|
||||||
@@ -666,8 +679,9 @@ class FakeErrorReply : public QNetworkReply
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
FakeErrorReply(QNetworkAccessManager::Operation op, const QNetworkRequest &request, QObject *parent)
|
FakeErrorReply(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
|
||||||
: QNetworkReply{parent} {
|
QObject *parent, int httpErrorCode)
|
||||||
|
: QNetworkReply{parent}, _httpErrorCode(httpErrorCode) {
|
||||||
setRequest(request);
|
setRequest(request);
|
||||||
setUrl(request.url());
|
setUrl(request.url());
|
||||||
setOperation(op);
|
setOperation(op);
|
||||||
@@ -676,7 +690,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
Q_INVOKABLE void respond() {
|
Q_INVOKABLE void respond() {
|
||||||
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 500);
|
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, _httpErrorCode);
|
||||||
setError(InternalServerError, "Internal Server Fake Error");
|
setError(InternalServerError, "Internal Server Fake Error");
|
||||||
emit metaDataChanged();
|
emit metaDataChanged();
|
||||||
emit finished();
|
emit finished();
|
||||||
@@ -684,18 +698,22 @@ public:
|
|||||||
|
|
||||||
void abort() override { }
|
void abort() override { }
|
||||||
qint64 readData(char *, qint64) override { return 0; }
|
qint64 readData(char *, qint64) override { return 0; }
|
||||||
|
|
||||||
|
int _httpErrorCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FakeQNAM : public QNetworkAccessManager
|
class FakeQNAM : public QNetworkAccessManager
|
||||||
{
|
{
|
||||||
FileInfo _remoteRootFileInfo;
|
FileInfo _remoteRootFileInfo;
|
||||||
FileInfo _uploadFileInfo;
|
FileInfo _uploadFileInfo;
|
||||||
QStringList _errorPaths;
|
// maps a path to an HTTP error
|
||||||
|
QHash<QString, int> _errorPaths;
|
||||||
public:
|
public:
|
||||||
FakeQNAM(FileInfo initialRoot) : _remoteRootFileInfo{std::move(initialRoot)} { }
|
FakeQNAM(FileInfo initialRoot) : _remoteRootFileInfo{std::move(initialRoot)} { }
|
||||||
FileInfo ¤tRemoteState() { return _remoteRootFileInfo; }
|
FileInfo ¤tRemoteState() { return _remoteRootFileInfo; }
|
||||||
FileInfo &uploadState() { return _uploadFileInfo; }
|
FileInfo &uploadState() { return _uploadFileInfo; }
|
||||||
QStringList &errorPaths() { return _errorPaths; }
|
|
||||||
|
QHash<QString, int> &errorPaths() { return _errorPaths; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
|
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
|
||||||
@@ -703,7 +721,7 @@ protected:
|
|||||||
const QString fileName = getFilePathFromUrl(request.url());
|
const QString fileName = getFilePathFromUrl(request.url());
|
||||||
Q_ASSERT(!fileName.isNull());
|
Q_ASSERT(!fileName.isNull());
|
||||||
if (_errorPaths.contains(fileName))
|
if (_errorPaths.contains(fileName))
|
||||||
return new FakeErrorReply{op, request, this};
|
return new FakeErrorReply{op, request, this, _errorPaths[fileName]};
|
||||||
|
|
||||||
bool isUpload = request.url().path().startsWith(sUploadUrl.path());
|
bool isUpload = request.url().path().startsWith(sUploadUrl.path());
|
||||||
FileInfo &info = isUpload ? _uploadFileInfo : _remoteRootFileInfo;
|
FileInfo &info = isUpload ? _uploadFileInfo : _remoteRootFileInfo;
|
||||||
@@ -712,13 +730,13 @@ protected:
|
|||||||
if (verb == QLatin1String("PROPFIND"))
|
if (verb == QLatin1String("PROPFIND"))
|
||||||
// Ignore outgoingData always returning somethign good enough, works for now.
|
// Ignore outgoingData always returning somethign good enough, works for now.
|
||||||
return new FakePropfindReply{info, op, request, this};
|
return new FakePropfindReply{info, op, request, this};
|
||||||
else if (verb == QLatin1String("GET"))
|
else if (verb == QLatin1String("GET") || op == QNetworkAccessManager::GetOperation)
|
||||||
return new FakeGetReply{info, op, request, this};
|
return new FakeGetReply{info, op, request, this};
|
||||||
else if (verb == QLatin1String("PUT"))
|
else if (verb == QLatin1String("PUT") || op == QNetworkAccessManager::PutOperation)
|
||||||
return new FakePutReply{info, op, request, outgoingData->readAll(), this};
|
return new FakePutReply{info, op, request, outgoingData->readAll(), this};
|
||||||
else if (verb == QLatin1String("MKCOL"))
|
else if (verb == QLatin1String("MKCOL"))
|
||||||
return new FakeMkcolReply{info, op, request, this};
|
return new FakeMkcolReply{info, op, request, this};
|
||||||
else if (verb == QLatin1String("DELETE"))
|
else if (verb == QLatin1String("DELETE") || op == QNetworkAccessManager::DeleteOperation)
|
||||||
return new FakeDeleteReply{info, op, request, this};
|
return new FakeDeleteReply{info, op, request, this};
|
||||||
else if (verb == QLatin1String("MOVE") && !isUpload)
|
else if (verb == QLatin1String("MOVE") && !isUpload)
|
||||||
return new FakeMoveReply{info, op, request, this};
|
return new FakeMoveReply{info, op, request, this};
|
||||||
@@ -798,7 +816,13 @@ public:
|
|||||||
FileInfo currentRemoteState() { return _fakeQnam->currentRemoteState(); }
|
FileInfo currentRemoteState() { return _fakeQnam->currentRemoteState(); }
|
||||||
FileInfo &uploadState() { return _fakeQnam->uploadState(); }
|
FileInfo &uploadState() { return _fakeQnam->uploadState(); }
|
||||||
|
|
||||||
QStringList &serverErrorPaths() { return _fakeQnam->errorPaths(); }
|
struct ErrorList {
|
||||||
|
FakeQNAM *_qnam;
|
||||||
|
void append(const QString &path, int error = 500)
|
||||||
|
{ _qnam->errorPaths().insert(path, error); }
|
||||||
|
void clear() { _qnam->errorPaths().clear(); }
|
||||||
|
};
|
||||||
|
ErrorList serverErrorPaths() { return {_fakeQnam}; }
|
||||||
|
|
||||||
QString localPath() const {
|
QString localPath() const {
|
||||||
// SyncEngine wants a trailing slash
|
// SyncEngine wants a trailing slash
|
||||||
|
|||||||
@@ -12,37 +12,18 @@
|
|||||||
|
|
||||||
using namespace OCC;
|
using namespace OCC;
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
const char testdbC[] = "/tmp/testdb.sqlite";
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestOwnSql : public QObject
|
class TestOwnSql : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
QTemporaryDir _tempDir;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase() {
|
|
||||||
QFileInfo fi( testdbC );
|
|
||||||
|
|
||||||
if( fi.exists() ) {
|
|
||||||
QFile::remove(testdbC);
|
|
||||||
}
|
|
||||||
fi.refresh();
|
|
||||||
QVERIFY(!fi.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanupTestCase() {
|
|
||||||
// QFile::remove(testdbC);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOpenDb() {
|
void testOpenDb() {
|
||||||
QFileInfo fi( testdbC );
|
QFileInfo fi( _tempDir.path() + "/testdb.sqlite" );
|
||||||
QVERIFY( !fi.exists() ); // must not exist
|
QVERIFY( !fi.exists() ); // must not exist
|
||||||
_db.openOrCreateReadWrite(testdbC);
|
_db.openOrCreateReadWrite(fi.filePath());
|
||||||
fi.refresh();
|
fi.refresh();
|
||||||
QVERIFY(fi.exists());
|
QVERIFY(fi.exists());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void testCreate() {
|
void testCreate() {
|
||||||
|
|||||||
@@ -213,6 +213,49 @@ private slots:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testSelectiveSyncBug() {
|
||||||
|
// issue owncloud/enterprise#1965: files from selective-sync ignored
|
||||||
|
// folders are uploaded anyway is some circumstances.
|
||||||
|
FakeFolder fakeFolder{FileInfo{ QString(), {
|
||||||
|
FileInfo { QStringLiteral("parentFolder"), {
|
||||||
|
FileInfo{ QStringLiteral("subFolder"), {
|
||||||
|
{ QStringLiteral("fileA.txt"), 400 },
|
||||||
|
{ QStringLiteral("fileB.txt"), 400, 'o' }
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}};
|
||||||
|
|
||||||
|
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||||
|
auto expectedServerState = fakeFolder.currentRemoteState();
|
||||||
|
|
||||||
|
// Remove subFolder with selectiveSync:
|
||||||
|
fakeFolder.syncEngine().journal()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList,
|
||||||
|
{"parentFolder/subFolder/"});
|
||||||
|
fakeFolder.syncEngine().journal()->avoidReadFromDbOnNextSync("parentFolder/subFolder/");
|
||||||
|
|
||||||
|
// But touch a local file before the next sync, such that the local folder
|
||||||
|
// can't be removed
|
||||||
|
fakeFolder.localModifier().setContents("parentFolder/subFolder/fileB.txt", 'n');
|
||||||
|
|
||||||
|
// Several follow-up syncs don't change the state at all,
|
||||||
|
// in particular the remote state doesn't change and fileB.txt
|
||||||
|
// isn't uploaded.
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
fakeFolder.syncOnce();
|
||||||
|
|
||||||
|
{
|
||||||
|
// Nothing changed on the server
|
||||||
|
QCOMPARE(fakeFolder.currentRemoteState(), expectedServerState);
|
||||||
|
// The local state should still have subFolderA
|
||||||
|
auto local = fakeFolder.currentLocalState();
|
||||||
|
QVERIFY(local.find("parentFolder/subFolder"));
|
||||||
|
QVERIFY(local.find("parentFolder/subFolder/fileA.txt"));
|
||||||
|
QVERIFY(local.find("parentFolder/subFolder/fileB.txt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void abortAfterFailedMkdir() {
|
void abortAfterFailedMkdir() {
|
||||||
QSKIP("Skip for 2.3");
|
QSKIP("Skip for 2.3");
|
||||||
FakeFolder fakeFolder{FileInfo{}};
|
FakeFolder fakeFolder{FileInfo{}};
|
||||||
@@ -225,6 +268,40 @@ private slots:
|
|||||||
QCOMPARE(finishedSpy.size(), 1);
|
QCOMPARE(finishedSpy.size(), 1);
|
||||||
QCOMPARE(finishedSpy.first().first().toBool(), false);
|
QCOMPARE(finishedSpy.first().first().toBool(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void testDirDownloadWithError() {
|
||||||
|
FakeFolder fakeFolder{FileInfo::A12_B12_C12_S12()};
|
||||||
|
QSignalSpy completeSpy(&fakeFolder.syncEngine(), SIGNAL(itemCompleted(const SyncFileItemPtr &)));
|
||||||
|
fakeFolder.remoteModifier().mkdir("Y");
|
||||||
|
fakeFolder.remoteModifier().mkdir("Y/Z");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d0");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d1");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d2");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d3");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d4");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d5");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d6");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d7");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d8");
|
||||||
|
fakeFolder.remoteModifier().insert("Y/Z/d9");
|
||||||
|
fakeFolder.serverErrorPaths().append("Y/Z/d2", 503); // 503 is a fatal error
|
||||||
|
fakeFolder.serverErrorPaths().append("Y/Z/d3", 503); // 503 is a fatal error
|
||||||
|
QVERIFY(!fakeFolder.syncOnce());
|
||||||
|
QCoreApplication::processEvents(); // should not crash
|
||||||
|
|
||||||
|
QSet<QString> seen;
|
||||||
|
for(const QList<QVariant> &args : completeSpy) {
|
||||||
|
auto item = args[0].value<SyncFileItemPtr>();
|
||||||
|
qDebug() << item->_file << item->_isDirectory << item->_status;
|
||||||
|
QVERIFY(!seen.contains(item->_file)); // signal only sent once per item
|
||||||
|
seen.insert(item->_file);
|
||||||
|
if (item->_file == "Y/Z/d2" || item->_file == "Y/Z/d3") {
|
||||||
|
QVERIFY(item->_status == SyncFileItem::FatalError);
|
||||||
|
}
|
||||||
|
QVERIFY(item->_file != "Y/Z/d9"); // we should have aborted the sync before d9 starts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QTEST_GUILESS_MAIN(TestSyncEngine)
|
QTEST_GUILESS_MAIN(TestSyncEngine)
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ class TestSyncJournalDB : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
QTemporaryDir _tempDir;
|
||||||
public:
|
public:
|
||||||
TestSyncJournalDB()
|
TestSyncJournalDB() : _db((_tempDir.path() + "/sync.db"))
|
||||||
: _db("/tmp/csync-test.db")
|
|
||||||
{
|
{
|
||||||
|
QVERIFY(_tempDir.isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
QDateTime dropMsecs(QDateTime time)
|
QDateTime dropMsecs(QDateTime time)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ private slots:
|
|||||||
QCOMPARE(octetsToString(10240) , QString("10 KB"));
|
QCOMPARE(octetsToString(10240) , QString("10 KB"));
|
||||||
|
|
||||||
QCOMPARE(octetsToString(123456) , QString("121 KB"));
|
QCOMPARE(octetsToString(123456) , QString("121 KB"));
|
||||||
QCOMPARE(octetsToString(1234567) , QString("1 MB"));
|
QCOMPARE(octetsToString(1234567) , QString("1.2 MB"));
|
||||||
QCOMPARE(octetsToString(12345678) , QString("12 MB"));
|
QCOMPARE(octetsToString(12345678) , QString("12 MB"));
|
||||||
QCOMPARE(octetsToString(123456789) , QString("118 MB"));
|
QCOMPARE(octetsToString(123456789) , QString("118 MB"));
|
||||||
QCOMPARE(octetsToString(1000LL*1000*1000 * 5) , QString("4.7 GB"));
|
QCOMPARE(octetsToString(1000LL*1000*1000 * 5) , QString("4.7 GB"));
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user