Comparar commits
430 Commits
v2.0.2
..
v2.1.0-rc1
| Autor | SHA1 | Data | |
|---|---|---|---|
| b4f5b7f5f3 | |||
| 9e66a6bec6 | |||
| 17566febe9 | |||
| db5a86bc06 | |||
| 2d604cee25 | |||
| e3a846bb1f | |||
| aaa5c1bc5f | |||
| 1ad8e539aa | |||
| 29e2877d35 | |||
| a708f8d8ad | |||
| 8777982629 | |||
| f1435c86ed | |||
| ece164f679 | |||
| 698f47d5ad | |||
| 1aa570326e | |||
| 1cc9070a50 | |||
| 127c107094 | |||
| f1faf8745a | |||
| 60a51f8085 | |||
| df63579071 | |||
| 5216648d0b | |||
| c6a65c692e | |||
| b468767184 | |||
| 76ce9ff8c4 | |||
| 23ed68c8dd | |||
| 2982c79444 | |||
| 7e56408331 | |||
| bcfdcec3ee | |||
| f96d94f143 | |||
| adea301e5b | |||
| f9dc569b0a | |||
| f7932bb0c7 | |||
| f20e052ad7 | |||
| 02daca9cb9 | |||
| b52a3a415c | |||
| 33dcb0c8d9 | |||
| 709da37be2 | |||
| 2458f07ca1 | |||
| 6cf5fc7f7d | |||
| 10db6cee6c | |||
| 3b7927366a | |||
| a25f094c4c | |||
| bdf830f691 | |||
| bd72642a58 | |||
| b3f5885a50 | |||
| aa168ec8b9 | |||
| d89cb3e575 | |||
| 9e1f215f22 | |||
| ffd0b19660 | |||
| 0020211857 | |||
| 1a59b4e208 | |||
| 94e7c762bf | |||
| 421c6a92f3 | |||
| d6aa667971 | |||
| 575fc9acbd | |||
| d581550130 | |||
| 952a134745 | |||
| 887aa952fe | |||
| 5f715a7af7 | |||
| eea982fb14 | |||
| 5a7eebe16e | |||
| 55a96af7cc | |||
| 8abaf92083 | |||
| b0c29d5c66 | |||
| 41a6f6df84 | |||
| 4984da7e0d | |||
| be88e5a2c3 | |||
| e7ad7d405c | |||
| 62d26814b2 | |||
| f2d8143511 | |||
| 3c1a605f62 | |||
| 300c1c2055 | |||
| fb42183bc7 | |||
| 65ec8a9e94 | |||
| 60598c0d34 | |||
| 3d7fc711ca | |||
| 46dbca1bf5 | |||
| f5da95a5b7 | |||
| b37361e21c | |||
| 32a208a176 | |||
| 5ec4fd94e0 | |||
| c9ef4d5fa0 | |||
| 1cdf0e8597 | |||
| 7ba88cc9e3 | |||
| 914df32587 | |||
| e0f54428d0 | |||
| 52a11b0835 | |||
| ef17dc6482 | |||
| 9978dc3f6c | |||
| f618ed3dfb | |||
| ef915fb2e5 | |||
| 6e42405113 | |||
| 11ef07c74a | |||
| 486d7690c4 | |||
| b3c2c594bd | |||
| 8852911f67 | |||
| e38bc6eab8 | |||
| 459e200ac0 | |||
| c781155b60 | |||
| 87aa1de67a | |||
| ccb871c30b | |||
| 74ed0b4f09 | |||
| becbb7b284 | |||
| ff76a842d0 | |||
| a56926b8d9 | |||
| 0d21503ee5 | |||
| 631cb095dd | |||
| caba719950 | |||
| 786b602c26 | |||
| f4bfce153d | |||
| e1ab50b17c | |||
| 7e4c0bd515 | |||
| ddcec2971e | |||
| 72d119a05f | |||
| d423cf2c7f | |||
| 3d847b50cf | |||
| 731a13cfd1 | |||
| 81296fae9d | |||
| aa38f7a4f2 | |||
| bac69f9984 | |||
| c46547592c | |||
| 6123fab091 | |||
| fd4a5100a6 | |||
| ff4a8c9202 | |||
| c871d721fd | |||
| 05d1cc9a94 | |||
| 196ee05fcc | |||
| e50cfa4e1b | |||
| 3224a959a4 | |||
| 2ccb3648c7 | |||
| 01aa647527 | |||
| eb3388de68 | |||
| beb970646b | |||
| 25c177ca3b | |||
| 1d9c591c08 | |||
| f753960add | |||
| 5e98894a97 | |||
| 15fe3b569b | |||
| 0e0b6026fc | |||
| 17dd199cba | |||
| 8f7dbe71a1 | |||
| 4b5c3d8f09 | |||
| 9955b0756a | |||
| 76d9b9c0e2 | |||
| 175ad6fb77 | |||
| 51896902e3 | |||
| b55220905e | |||
| bd65eb32b7 | |||
| b29d1e94b5 | |||
| b74e812671 | |||
| aa27b5db14 | |||
| 81e3a62360 | |||
| b0dc264369 | |||
| ac3f179420 | |||
| d9af837974 | |||
| 4784b327e7 | |||
| 1a1541ecd6 | |||
| cf9fec73cf | |||
| a1551ef6ab | |||
| 0163839cfb | |||
| 914a942e33 | |||
| 5e482ad4f7 | |||
| 6901fc9e38 | |||
| 0070835330 | |||
| 6431a2aa46 | |||
| 572d9bdf1a | |||
| 4cf2422a83 | |||
| 806ec98eab | |||
| 20fd349e17 | |||
| dae724b21c | |||
| 3e4612a1f0 | |||
| 6052e49bcc | |||
| 90cbd461ab | |||
| 517f2ed03d | |||
| 5fccc25f36 | |||
| cb1571c6c5 | |||
| 99b0d659bd | |||
| 854264c3d2 | |||
| d2a6cae695 | |||
| be9ed2f6a9 | |||
| 3ee8beb8a3 | |||
| f816e5e2af | |||
| 448d8bff18 | |||
| b82ffb52c7 | |||
| 3bccfb8993 | |||
| 6fb4e59120 | |||
| 309be57a12 | |||
| 37098c96f9 | |||
| 03cc67a2b1 | |||
| cb4fba7658 | |||
| 0bcb13f02e | |||
| 42ef09e3bb | |||
| 1ab44655e0 | |||
| 89734b95c4 | |||
| 8f5658bc01 | |||
| a59c3ef278 | |||
| b600ac882a | |||
| 71849c4372 | |||
| 9545af0d43 | |||
| 6c6ee358d4 | |||
| 060f4f291b | |||
| 3fb43d2322 | |||
| 4895683bab | |||
| 43800e3d1c | |||
| 3b8e1dcd89 | |||
| 302d6b321e | |||
| 3f85694394 | |||
| 8c5ef2f1c3 | |||
| aa1a2d1247 | |||
| 3993a7f636 | |||
| aaf16ff0e8 | |||
| 16c078963b | |||
| 9279bcdba4 | |||
| 0c467ef5b4 | |||
| 49cd53ee44 | |||
| 0e6a463564 | |||
| d38b190317 | |||
| 29558cb7bb | |||
| d2cd237e25 | |||
| c7b814337a | |||
| ced51813c7 | |||
| dd8d02b8ef | |||
| c3cf6aef7d | |||
| 38a8e5ee03 | |||
| 46269dac4e | |||
| 8c0297f688 | |||
| bc5890d8b5 | |||
| ee65315520 | |||
| 12f7cfde87 | |||
| 6d80f3d756 | |||
| b32f752d31 | |||
| cf8be7de91 | |||
| 30a3498c22 | |||
| 239603e24c | |||
| 390daed3de | |||
| 918c06aba3 | |||
| 726be08917 | |||
| a127debc54 | |||
| 6aa26654f6 | |||
| 0fde7f0e6b | |||
| d9f8edd259 | |||
| 251679253a | |||
| 64756c5dce | |||
| 9788055147 | |||
| 4d7fde59c2 | |||
| 4737c16996 | |||
| c97dfbf60c | |||
| 496b1e907d | |||
| 566131209d | |||
| b7823dc648 | |||
| 6d28a1b645 | |||
| b6aa18bfbc | |||
| d91ffc216a | |||
| a6c9e8c5b4 | |||
| 9337927722 | |||
| c81b02c7d9 | |||
| 5ea09d2668 | |||
| b9fc4c5994 | |||
| f1b500d3e0 | |||
| dff37e11eb | |||
| b1387f801b | |||
| 60b2312ab6 | |||
| 0354289795 | |||
| c11c35c459 | |||
| 7c5e70ac3c | |||
| d2e5ba123d | |||
| 0c9568f6dc | |||
| f1d48a9356 | |||
| 89f2a9e6dc | |||
| a203da3919 | |||
| 1c1ef52cf1 | |||
| 40c82c5c36 | |||
| 81f0c6535e | |||
| 46558d79a5 | |||
| e86b4203b9 | |||
| 05dd9554f9 | |||
| 30a0423f81 | |||
| 26e1223f9a | |||
| 84a04de7be | |||
| 3ff7fa0092 | |||
| 3f2a2cb14b | |||
| fac00348d9 | |||
| 3c93fd4fb7 | |||
| 6b71273380 | |||
| dccf4e9c34 | |||
| 39289a3164 | |||
| 6611d878ea | |||
| c3fc711095 | |||
| 84f1bdbc87 | |||
| 2eb5715599 | |||
| c93defc82d | |||
| bd39c64798 | |||
| 42a6b242c7 | |||
| e5570c24f2 | |||
| 6d87bd15cd | |||
| d657c00b11 | |||
| 6fae06f1d0 | |||
| f18b40f7e7 | |||
| 6a0633083d | |||
| 899f52be4f | |||
| 91525a7d33 | |||
| 455c3ae57d | |||
| b5390b5aa2 | |||
| a608b4e9e0 | |||
| f6a543ada3 | |||
| c7bf09c3d4 | |||
| c8ae54d9e8 | |||
| daf6d8772f | |||
| c80e72da83 | |||
| ec351e00ad | |||
| cf242871ea | |||
| 597d36dcf2 | |||
| 9c388787bb | |||
| f739d8fdd3 | |||
| 8ff3055b47 | |||
| 71827549d6 | |||
| de5de7acc5 | |||
| c8590c4468 | |||
| 98b966d274 | |||
| 674b6f2373 | |||
| 407ff0a99d | |||
| 0b6d21e3d5 | |||
| 557b704069 | |||
| 4369e31a49 | |||
| 23b5a74c17 | |||
| ee69ab2021 | |||
| 374f29c4d3 | |||
| 67910e7d60 | |||
| c80b033466 | |||
| 671af9f8fe | |||
| 6ea05ff6e3 | |||
| 00485e133f | |||
| c520ee4eab | |||
| 5408ec79f7 | |||
| f8e68ae823 | |||
| 184412d88e | |||
| 82d1d04774 | |||
| 731d4b3d4d | |||
| ccec186b98 | |||
| c66c259447 | |||
| 4ad165ce26 | |||
| 5cac90b3eb | |||
| df135a0bb2 | |||
| 4a04dc1a3e | |||
| 0e97fbb730 | |||
| 40ab3ee751 | |||
| f95fea9866 | |||
| 03719334ea | |||
| 4441053b1c | |||
| a34b663828 | |||
| db1f4d4016 | |||
| 3ea944d1b3 | |||
| b293aa762c | |||
| b5e75afc17 | |||
| 027a865fbc | |||
| 132b5f5130 | |||
| c3754e1fdd | |||
| 963eb1f29b | |||
| c418d67920 | |||
| 6e09e3af86 | |||
| 67e9a06d30 | |||
| abd63035c1 | |||
| df2418d9c5 | |||
| 2fdae6d72f | |||
| 05471d0acd | |||
| 05eee16959 | |||
| a752eadd0f | |||
| 028dc8d6c3 | |||
| 4a7242c8f9 | |||
| efdb29d2f9 | |||
| 225da68832 | |||
| 51a2e6c580 | |||
| 7fe03c715d | |||
| 419d18c128 | |||
| 74a7755ad9 | |||
| c1ba927b37 | |||
| a8eb913535 | |||
| cb3a301f2c | |||
| 6d6903ef62 | |||
| f5daf50dc4 | |||
| b8ccbbc72a | |||
| f0e17fd9c0 | |||
| b09f1d591c | |||
| 8fd2b8d829 | |||
| d610693af1 | |||
| 7d1886684e | |||
| 95fc792745 | |||
| efefc2d986 | |||
| 9f8d109a7e | |||
| cf9e5ffb0b | |||
| 1383023b2e | |||
| afd081f40b | |||
| 3812fd0866 | |||
| 24c41ed0da | |||
| 36e8e9ebf5 | |||
| 12dc372b21 | |||
| 20ea9015ca | |||
| 0c148025a3 | |||
| a7cf1b04ad | |||
| a08a90a718 | |||
| 1cb518cb13 | |||
| cac219aca8 | |||
| a159dfc7ec | |||
| b7061618b1 | |||
| d823809021 | |||
| 0329a8be2e | |||
| 79f64abfc3 | |||
| 63636aca9b | |||
| fdfab07d07 | |||
| cc5f8e5122 | |||
| 21dbf97a02 | |||
| 39bff056a6 | |||
| 128d46e19a | |||
| 6d027ebd40 | |||
| 89f69209dd | |||
| 94a57fe8d5 | |||
| dcb687929f | |||
| 628957de21 | |||
| bcfc16c0f6 | |||
| 3ba5e27d02 | |||
| 950bc578d0 | |||
| ccfcdff190 | |||
| 45835f1cf9 | |||
| cf543d1eb0 | |||
| 9f24f10186 | |||
| bc37668e9f | |||
| 22d87218b7 | |||
| 62d64acd7b | |||
| c6ff73f3e5 | |||
| a87602af3f |
@@ -133,13 +133,9 @@ endif()
|
||||
#endif()
|
||||
|
||||
|
||||
set(USE_NEON TRUE)
|
||||
if(HAVE_QT5)
|
||||
message(STATUS "Using Qt ${Qt5Core_VERSION_MAJOR}.${Qt5Core_VERSION_MINOR}.x")
|
||||
if (${Qt5Core_VERSION_MAJOR} EQUAL "5")
|
||||
if (${Qt5Core_VERSION_MINOR} EQUAL "4" OR ${Qt5Core_VERSION_MINOR} GREATER 4)
|
||||
message(STATUS "We do not require Neon in this setup, compile without!")
|
||||
set(USE_NEON FALSE)
|
||||
else()
|
||||
message(STATUS "If possible compile me with Qt 5.4 or higher.")
|
||||
endif()
|
||||
@@ -148,9 +144,6 @@ else()
|
||||
message(STATUS "If possible compile me with Qt 5.4 or higher.")
|
||||
endif()
|
||||
|
||||
if (USE_NEON)
|
||||
find_package(Neon REQUIRED)
|
||||
endif(USE_NEON)
|
||||
find_package(OpenSSL 1.0.0 REQUIRED)
|
||||
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
|
||||
@@ -1,12 +1,62 @@
|
||||
ChangeLog
|
||||
=========
|
||||
version 2.1 (release 2015-12-01)
|
||||
* GUI: Added a display of server activities
|
||||
* GUI: Added a separate view for not synced items, ignores, errors.
|
||||
* GUI: Improve upload/download progress UI (#3403, #3569)
|
||||
* Allow sharing with ownCloud internal users and groups from Desktop
|
||||
* Consider .* files hidden on all platforms (#4023)
|
||||
* Reflect read-only permissions in filesystem (#3244)
|
||||
* Blacklist: Clear on successful chunk upload (#3934)
|
||||
* Improve reconnecting after network change/disconnect (#4167 #3969 ...)
|
||||
* Performance improvements in Windows file system discovery
|
||||
* libneon-based propagator removed.
|
||||
It was already disabled on OS X and Windows and only used on
|
||||
Linux when building with Qt < 5.4 and having bandwidth limiting enabled.
|
||||
So now you need Qt 5.4 on Linux if you want bandwidth limiting.
|
||||
* performance improvements in the logging functions
|
||||
* Handle local disk space problems gracefully (#2939)
|
||||
* Improved handling of checksums: transport validation, db (#3735)
|
||||
* For *eml-files don't reupload if size and checksum are unchanged (#3235)
|
||||
* Handle 403 reply code properly (File Firewall) (#3490)
|
||||
* Reduce number of PROPFIND requests to server(#3964)
|
||||
* GUI: Added Account toolbox widget to keep account actions (#4139)
|
||||
* Tray Menu: Recent Activity fixes (#4093, #3969)
|
||||
* FolderMan: Fix infinite wait on pause (#4093)
|
||||
* Rename env variables to include unit (#2939)
|
||||
* FolderStatusModel: attempt to detect removed undecided files (#3612)
|
||||
* SyncEngine: Don't whipe the white list if the sync was aborted (#4018)
|
||||
* Quota: handle special negative value for the quota (#3940)
|
||||
* State app name in update notification (#4020)
|
||||
* PropagateUpload: Fix double-emission of finished (#3844)
|
||||
* GUI: Allow to click on folder names which are excluded from sync.
|
||||
* Shell Integration: Dolphin integration prepared, works if upstream released
|
||||
* FolderStatusModel: reset also if a folder was renamed (#4011)
|
||||
* GUI: No padding for toolbar extension (#3795)
|
||||
* Use the term folder sync connection in more places (#3757)
|
||||
* AccountSettings: don't disable pause when offline (#4010)
|
||||
* Fix handling of hidden files (#3980)
|
||||
* Handle download errors while resuming as soft errors (#4000)
|
||||
* SocketAPI: don't trim the command (#3297)
|
||||
* Shutdown socket API before removing the db (#3824)
|
||||
* GUI: Make "Keep" default in the delete-all dialog (#3824)
|
||||
* owncloudcmd: Return code 0 for --version and --help
|
||||
* owncloudcmd: add --max-sync-retries (#4037)
|
||||
* owncloudcmd: Don't do a check that file are older than 2s (#4160)
|
||||
* Fix getting size for selective sync (#3986)
|
||||
* Re-added close button in the settings window (#3713)
|
||||
* Abililty to handle storage limitations gracefully (#3736)
|
||||
* Updated 3rdparty dependencies: sqlite version 3.9.1
|
||||
* Organize needed vendor Qt patches better.
|
||||
* plus a lot of unmentioned improvements and fixes.
|
||||
|
||||
version 2.0.2 (release 2015-10-21)
|
||||
* csync_file_stat_s: Save a bit of memory
|
||||
* Shibboleth: Add our base user agent to WebKit
|
||||
* SelectiveSync: Increase folder list timeout to 60
|
||||
* Propagation: Try another sync on 423 Locked (#3387)
|
||||
* Propagation: Make 423 Locked a soft error (#3387)
|
||||
* Propagation: Reset upload blacklist if a chunk suceeds
|
||||
* Propagation: Reset upload blacklist if a chunk succeeds
|
||||
* Application: Fix crash on early shutdown (#3898)
|
||||
* Linux: Don't show settings dialog always when launched twice (#3273, #3771, #3485)
|
||||
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. (#3813)
|
||||
@@ -36,12 +86,12 @@ version 2.0.2 (release 2015-10-21)
|
||||
* PropagateLocalRemove: remove entries from the DB even if there was an error.
|
||||
* Settings UI improvements (eg. #3713, #3721, #3619 and others)
|
||||
* Folder: Do not create the sync folder if it does not exist (#3692)
|
||||
* Shell integratioon: don't show share menu item for top level folders
|
||||
* Shell integration: don't show share menu item for top level folders
|
||||
* Tray: Hide while modifying menus (#3656, #3672)
|
||||
* AddFolder: Improve remote path selection error handling (#3573)
|
||||
* csync_update: Use excluded_traversal() to improve performance (#3638)
|
||||
* csync_excluded: Add fast _traversal() function (#3638)
|
||||
* csync_exclude: Speed up siginificantly (#3638)
|
||||
* csync_exclude: Speed up significantly (#3638)
|
||||
* AccountSettings: Adjust quota info design (#3644, #3651)
|
||||
* Adjust buttons on remove folder/account questions (#3654)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| client-build-matrix | [](https://ci.owncloud.org/job/client-build-matrix-linux/) |
|
||||
| client-test-matrix-linux-no-build | [](https://ci.owncloud.org/job/client-test-matrix-linux-no-build/) |
|
||||
|
||||
| coverity_scan | [](https://scan.coverity.com/projects/owncloud-mirall)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
set( MIRALL_VERSION_MAJOR 2 )
|
||||
set( MIRALL_VERSION_MINOR 0 )
|
||||
set( MIRALL_VERSION_PATCH 2 )
|
||||
set( MIRALL_VERSION_MINOR 1 )
|
||||
set( MIRALL_VERSION_PATCH 0 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
|
||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
set( MIRALL_VERSION_SUFFIX "") #e.g. beta1, beta2, rc1
|
||||
set( MIRALL_VERSION_SUFFIX "rc1") #e.g. beta1, beta2, rc1
|
||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
|
||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
|
||||
@@ -38,6 +38,8 @@ QT_PLUGINS = [
|
||||
'imageformats/libqgif.dylib',
|
||||
'imageformats/libqico.dylib',
|
||||
'imageformats/libqjpeg.dylib',
|
||||
'bearer/libqcorewlanbearer.dylib',
|
||||
'bearer/libqgenericbearer.dylib',
|
||||
'imageformats/libqsvg.dylib',
|
||||
'imageformats/libqmng.dylib',
|
||||
]
|
||||
@@ -144,6 +146,10 @@ def FindFramework(path):
|
||||
search_pathes = FRAMEWORK_SEARCH_PATH
|
||||
search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
|
||||
for search_path in search_pathes:
|
||||
# The following two lines are needed for a custom built Qt from version 5.5 on, possibly not for the one from the Qt SDK.
|
||||
# Looks like the upstream macdeployqt also had an issue there https://bugreports.qt.io/browse/QTBUG-47868
|
||||
if path.find( "\@rpath/"):
|
||||
path = path.replace("@rpath/", "")
|
||||
abs_path = os.path.join(search_path, path)
|
||||
if os.path.exists(abs_path):
|
||||
return abs_path
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
From ea4dcc5931d455e4ee3e958ffa54a9f54ab022c8 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 5 Jan 2015 10:45:25 +0100
|
||||
Subject: [PATCH 1/3] Fix crash on Mac OS if PAC URL contains non-URL legal
|
||||
chars
|
||||
|
||||
macQueryInternal() was retrieving the PAC URL string as-entered by
|
||||
the user in the 'Proxies' tab of the system network settings dialog
|
||||
and passing it to CFURLCreateWithString().
|
||||
|
||||
CFURLCreateWithString() returns null if the input string contains
|
||||
non-URL legal chars or is empty.
|
||||
|
||||
Change-Id: I9166d0433a62c7b2274b5435a7dea0a16997d10e
|
||||
Patch-By: Robert Knight
|
||||
Task-number: QTBUG-36787
|
||||
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
|
||||
Reviewed-by: Markus Goetz <markus@woboq.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_mac.cpp | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
index 7d26246..81bce0c 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
@@ -221,7 +221,11 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
|
||||
int enabled;
|
||||
if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
|
||||
// PAC is enabled
|
||||
- CFStringRef cfPacLocation = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
|
||||
+ // kSCPropNetProxiesProxyAutoConfigURLString returns the URL string
|
||||
+ // as entered in the system proxy configuration dialog
|
||||
+ CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
|
||||
+ QCFType<CFStringRef> cfPacLocation = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, pacLocationSetting, NULL, NULL,
|
||||
+ kCFStringEncodingUTF8);
|
||||
|
||||
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
|
||||
QCFType<CFDataRef> pacData;
|
||||
--
|
||||
1.8.3.4 (Apple Git-47)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
From a83e4d1d9dd90d4563ce60f27dfb7802a780e33e Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 5 Jan 2015 11:42:52 +0100
|
||||
Subject: [PATCH 2/3] Fix possible crash when passing an invalid PAC URL
|
||||
|
||||
This commit checks whether CFURLCreateWithString() succeeded.
|
||||
|
||||
It does not appear to be possible to enter an empty URL directly in the
|
||||
PAC configuration dialog but I can't rule out the possibility
|
||||
that it could find its way into the settings via some other means.
|
||||
|
||||
Change-Id: I6c2053d385503bf0330f5ae9fb1ec36a473d425d
|
||||
Patch-By: Robert Knight
|
||||
Task-number: QTBUG-36787
|
||||
Reviewed-by: Markus Goetz <markus@woboq.com>
|
||||
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_mac.cpp | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
index 81bce0c..6be032e 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
@@ -230,6 +230,10 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
|
||||
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
|
||||
QCFType<CFDataRef> pacData;
|
||||
QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
|
||||
+ if (!pacUrl) {
|
||||
+ qWarning("Invalid PAC URL \"%s\"", qPrintable(QCFString::toQString(cfPacLocation)));
|
||||
+ return result;
|
||||
+ }
|
||||
SInt32 errorCode;
|
||||
if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacUrl, &pacData, NULL, NULL, &errorCode)) {
|
||||
QString pacLocation = QCFString::toQString(cfPacLocation);
|
||||
--
|
||||
1.8.3.4 (Apple Git-47)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
From 83bd9393e5564ea9168fda90c0f44456633a483a Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 5 Jan 2015 15:22:57 +0100
|
||||
Subject: [PATCH 3/3] Fix crash if PAC script retrieval returns a null CFData
|
||||
instance
|
||||
|
||||
The documentation for CFURLCreateDataAndPropertiesFromResource()
|
||||
does not make this clear but from looking at the CFNetwork implementation
|
||||
and a user stacktrace it appears that this function can return true
|
||||
but not set the data argument under certain circumstances.
|
||||
|
||||
Change-Id: I48034a640d6f47a51cd5883bbafacad4bcbd0415
|
||||
Task-number: QTBUG-36787
|
||||
Patch-By: Robert Knight
|
||||
Reviewed-by: Markus Goetz <markus@woboq.com>
|
||||
Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_mac.cpp | 5 ++++-
|
||||
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
index 6be032e..a1ac349 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
|
||||
@@ -240,7 +240,10 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
|
||||
qWarning("Unable to get the PAC script at \"%s\" (%s)", qPrintable(pacLocation), cfurlErrorDescription(errorCode));
|
||||
return result;
|
||||
}
|
||||
-
|
||||
+ if (!pacData) {
|
||||
+ qWarning("\"%s\" returned an empty PAC script", qPrintable(QCFString::toQString(cfPacLocation)));
|
||||
+ return result;
|
||||
+ }
|
||||
QCFType<CFStringRef> pacScript = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1);
|
||||
if (!pacScript) {
|
||||
// This should never happen, but the documentation says it may return NULL if there was a problem creating the object.
|
||||
--
|
||||
1.8.3.4 (Apple Git-47)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
From 22f3d359350fd65e4bbe2e9420fcc4460e8a590a Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= <morten.sorvig@digia.com>
|
||||
Date: Tue, 10 Mar 2015 22:37:39 +0100
|
||||
Subject: [PATCH] Cocoa: Fix systray SVG icons.
|
||||
|
||||
Regression caused by f3699510.
|
||||
|
||||
Task-number: QTBUG-44686
|
||||
Change-Id: I546422a67d4da29fac196025b09bddcb45c1b641
|
||||
Reviewed-by: Timur Pocheptsov <Timur.Pocheptsov@digia.com>
|
||||
---
|
||||
src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index e449fd3..8a35705 100755
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -234,6 +234,10 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
}
|
||||
}
|
||||
|
||||
+ // Handle SVG icons, which do not return anything for availableSizes().
|
||||
+ if (!selectedSize.isValid())
|
||||
+ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode);
|
||||
+
|
||||
QPixmap pixmap = icon.pixmap(selectedSize, mode);
|
||||
|
||||
// Draw a low-resolution icon if there is not enough pixels for a retina
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
From ee7fea33383726f0bb72e8082a357820e3ee3675 Mon Sep 17 00:00:00 2001
|
||||
From: Jocelyn Turcotte <jturcotte@woboq.com>
|
||||
Date: Tue, 24 Feb 2015 17:02:02 +0100
|
||||
Subject: [PATCH] OSX Fix disapearing tray icon
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
It would happen together with an error:
|
||||
QPainter::begin: Paint device returned engine == 0
|
||||
and would be caused by the size provided to QIcon::pixmap being empty,
|
||||
itself caused by the availableSizes list being empty for the Selected
|
||||
mode.
|
||||
|
||||
This bug was most often hidden by the fact that the Selected icon mode
|
||||
was not triggered properly since we usually only set menuVisible after
|
||||
calling updateIcon, and most of the time when it did, we would overwrite
|
||||
it right after with a Normal mode icon.
|
||||
|
||||
Fix the issue by disabling the broken feature completely since the
|
||||
default Selected icon is grayed out while tray icons are now usually
|
||||
black (or white when selected). To support the dark menu bar mode on
|
||||
10.10 we'll need to use NSImage's setTemplate anyway and that
|
||||
knowing in advance if we can invert the colors ourselves would also
|
||||
better solve the menuVisible usecase.
|
||||
|
||||
Task-number: QTBUG-42910
|
||||
Change-Id: If9ec9659af28ecceb841bfc2f11721e6029fe891
|
||||
Reviewed-by: Morten Johan Sørvig <morten.sorvig@theqtcompany.com>
|
||||
---
|
||||
src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 17 +++--------------
|
||||
1 file changed, 3 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
index 8a35705..d366a3c 100755
|
||||
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
|
||||
@@ -102,7 +102,6 @@ QT_USE_NAMESPACE
|
||||
QCocoaSystemTrayIcon *systray;
|
||||
NSStatusItem *item;
|
||||
QCocoaMenu *menu;
|
||||
- bool menuVisible;
|
||||
QIcon icon;
|
||||
QT_MANGLE_NAMESPACE(QNSImageView) *imageCell;
|
||||
}
|
||||
@@ -202,8 +201,6 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
|
||||
m_sys->item->icon = icon;
|
||||
|
||||
- const bool menuVisible = m_sys->item->menu && m_sys->item->menuVisible;
|
||||
-
|
||||
// The reccomended maximum title bar icon height is 18 points
|
||||
// (device independent pixels). The menu height on past and
|
||||
// current OS X versions is 22 points. Provide some future-proofing
|
||||
@@ -218,9 +215,8 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
// devicePixelRatio for the "best" screen on the system.
|
||||
qreal devicePixelRatio = qApp->devicePixelRatio();
|
||||
const int maxPixmapHeight = maxImageHeight * devicePixelRatio;
|
||||
- const QIcon::Mode mode = menuVisible ? QIcon::Selected : QIcon::Normal;
|
||||
QSize selectedSize;
|
||||
- Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes(mode))) {
|
||||
+ Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes())) {
|
||||
// Select a pixmap based on the height. We want the largest pixmap
|
||||
// with a height smaller or equal to maxPixmapHeight. The pixmap
|
||||
// may rectangular; assume it has a reasonable size. If there is
|
||||
@@ -236,9 +232,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
|
||||
|
||||
// Handle SVG icons, which do not return anything for availableSizes().
|
||||
if (!selectedSize.isValid())
|
||||
- selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode);
|
||||
+ selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight));
|
||||
|
||||
- QPixmap pixmap = icon.pixmap(selectedSize, mode);
|
||||
+ QPixmap pixmap = icon.pixmap(selectedSize);
|
||||
|
||||
// Draw a low-resolution icon if there is not enough pixels for a retina
|
||||
// icon. This prevents showing a small icon on retina displays.
|
||||
@@ -385,9 +381,6 @@ QT_END_NAMESPACE
|
||||
Q_UNUSED(notification);
|
||||
down = NO;
|
||||
|
||||
- parent->systray->updateIcon(parent->icon);
|
||||
- parent->menuVisible = false;
|
||||
-
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
@@ -397,8 +390,6 @@ QT_END_NAMESPACE
|
||||
int clickCount = [mouseEvent clickCount];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
- parent->systray->updateIcon(parent->icon);
|
||||
-
|
||||
if (clickCount == 2) {
|
||||
[self menuTrackingDone:nil];
|
||||
[parent doubleClickSelector:self];
|
||||
@@ -454,7 +445,6 @@ QT_END_NAMESPACE
|
||||
if (self) {
|
||||
item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
|
||||
menu = 0;
|
||||
- menuVisible = false;
|
||||
systray = sys;
|
||||
imageCell = [[QNSImageView alloc] initWithParent:self];
|
||||
[item setView: imageCell];
|
||||
@@ -498,7 +488,6 @@ QT_END_NAMESPACE
|
||||
selector:@selector(menuTrackingDone:)
|
||||
name:NSMenuDidEndTrackingNotification
|
||||
object:m];
|
||||
- menuVisible = true;
|
||||
[item popUpStatusItemMenu: m];
|
||||
}
|
||||
}
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
From f3cd07c11e0b7327ffc629f48a89c8c457cdba75 Mon Sep 17 00:00:00 2001
|
||||
From: Jocelyn Turcotte <jturcotte@woboq.com>
|
||||
Date: Fri, 6 Mar 2015 16:12:37 +0100
|
||||
Subject: [PATCH] Fix -force-debug-info with macx-clang
|
||||
|
||||
---
|
||||
mkspecs/common/clang.conf | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/mkspecs/common/clang.conf b/mkspecs/common/clang.conf
|
||||
index 2c29bb8..110d380 100644
|
||||
--- a/mkspecs/common/clang.conf
|
||||
+++ b/mkspecs/common/clang.conf
|
||||
@@ -20,11 +20,13 @@ QMAKE_CFLAGS_ISYSTEM = -isystem
|
||||
QMAKE_CFLAGS_PRECOMPILE = -x c-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT}
|
||||
QMAKE_CFLAGS_USE_PRECOMPILE = -Xclang -include-pch -Xclang ${QMAKE_PCH_OUTPUT}
|
||||
QMAKE_CFLAGS_LTCG = -flto
|
||||
+QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_OPTIMIZE -g
|
||||
|
||||
QMAKE_CXXFLAGS_PRECOMPILE = -x c++-header -c ${QMAKE_PCH_INPUT} -o ${QMAKE_PCH_OUTPUT}
|
||||
QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE
|
||||
QMAKE_CXXFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG
|
||||
QMAKE_CXXFLAGS_CXX11 = -std=c++11
|
||||
+QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO = $$QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO
|
||||
|
||||
QMAKE_LFLAGS_CXX11 =
|
||||
QMAKE_LFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG
|
||||
--
|
||||
2.2.0
|
||||
|
||||
@@ -0,0 +1,691 @@
|
||||
From cff39fba10ffc10ee4dcfdc66ff6528eb26462d3 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Fri, 10 Apr 2015 14:09:53 +0200
|
||||
Subject: [PATCH] QNAM: Fix upload corruptions when server closes connection
|
||||
|
||||
This patch fixes several upload corruptions if the server closes the connection
|
||||
while/before we send data into it. They happen inside multiple places in the HTTP
|
||||
layer and are explained in the comments.
|
||||
Corruptions are:
|
||||
* The upload byte device has an in-flight signal with pending upload data, if
|
||||
it gets reset (because server closes the connection) then the re-send of the
|
||||
request was sometimes taking this stale in-flight pending upload data.
|
||||
* Because some signals were DirectConnection and some were QueuedConnection, there
|
||||
was a chance that a direct signal overtakes a queued signal. The state machine
|
||||
then sent data down the socket which was buffered there (and sent later) although
|
||||
it did not match the current state of the state machine when it was actually sent.
|
||||
* A socket was seen as being able to have requests sent even though it was not
|
||||
encrypted yet. This relates to the previous corruption where data is stored inside
|
||||
the socket's buffer and then sent later.
|
||||
|
||||
The included auto test produces all fixed corruptions, I detected no regressions
|
||||
via the other tests.
|
||||
This code also adds a bit of sanity checking to protect from possible further
|
||||
problems.
|
||||
|
||||
[ChangeLog][QtNetwork] Fix HTTP(s) upload corruption when server closes connection
|
||||
|
||||
Change-Id: I54c883925ec897050941498f139c4b523030432e
|
||||
Reviewed-by: Peter Hartmann <peter-qt@hartmann.tk>
|
||||
---
|
||||
src/corelib/io/qnoncontiguousbytedevice.cpp | 18 +++
|
||||
src/corelib/io/qnoncontiguousbytedevice_p.h | 4 +
|
||||
.../access/qhttpnetworkconnectionchannel.cpp | 35 ++++-
|
||||
.../access/qhttpnetworkconnectionchannel_p.h | 2 +
|
||||
src/network/access/qhttpprotocolhandler.cpp | 7 +
|
||||
src/network/access/qhttpthreaddelegate_p.h | 36 ++++-
|
||||
src/network/access/qnetworkreplyhttpimpl.cpp | 25 ++-
|
||||
src/network/access/qnetworkreplyhttpimpl_p.h | 7 +-
|
||||
.../access/qnetworkreply/tst_qnetworkreply.cpp | 175 ++++++++++++++++++++-
|
||||
9 files changed, 279 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/src/corelib/io/qnoncontiguousbytedevice.cpp b/src/corelib/io/qnoncontiguousbytedevice.cpp
|
||||
index 11510a8..760ca3d 100644
|
||||
--- a/src/corelib/io/qnoncontiguousbytedevice.cpp
|
||||
+++ b/src/corelib/io/qnoncontiguousbytedevice.cpp
|
||||
@@ -236,6 +236,11 @@ qint64 QNonContiguousByteDeviceByteArrayImpl::size()
|
||||
return byteArray->size();
|
||||
}
|
||||
|
||||
+qint64 QNonContiguousByteDeviceByteArrayImpl::pos()
|
||||
+{
|
||||
+ return currentPosition;
|
||||
+}
|
||||
+
|
||||
QNonContiguousByteDeviceRingBufferImpl::QNonContiguousByteDeviceRingBufferImpl(QSharedPointer<QRingBuffer> rb)
|
||||
: QNonContiguousByteDevice(), currentPosition(0)
|
||||
{
|
||||
@@ -273,6 +278,11 @@ bool QNonContiguousByteDeviceRingBufferImpl::atEnd()
|
||||
return currentPosition >= size();
|
||||
}
|
||||
|
||||
+qint64 QNonContiguousByteDeviceRingBufferImpl::pos()
|
||||
+{
|
||||
+ return currentPosition;
|
||||
+}
|
||||
+
|
||||
bool QNonContiguousByteDeviceRingBufferImpl::reset()
|
||||
{
|
||||
if (resetDisabled)
|
||||
@@ -406,6 +416,14 @@ qint64 QNonContiguousByteDeviceIoDeviceImpl::size()
|
||||
return device->size() - initialPosition;
|
||||
}
|
||||
|
||||
+qint64 QNonContiguousByteDeviceIoDeviceImpl::pos()
|
||||
+{
|
||||
+ if (device->isSequential())
|
||||
+ return -1;
|
||||
+
|
||||
+ return device->pos();
|
||||
+}
|
||||
+
|
||||
QByteDeviceWrappingIoDevice::QByteDeviceWrappingIoDevice(QNonContiguousByteDevice *bd) : QIODevice((QObject*)0)
|
||||
{
|
||||
byteDevice = bd;
|
||||
diff --git a/src/corelib/io/qnoncontiguousbytedevice_p.h b/src/corelib/io/qnoncontiguousbytedevice_p.h
|
||||
index c05ae11..4d7b7b0 100644
|
||||
--- a/src/corelib/io/qnoncontiguousbytedevice_p.h
|
||||
+++ b/src/corelib/io/qnoncontiguousbytedevice_p.h
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
virtual const char* readPointer(qint64 maximumLength, qint64 &len) = 0;
|
||||
virtual bool advanceReadPointer(qint64 amount) = 0;
|
||||
virtual bool atEnd() = 0;
|
||||
+ virtual qint64 pos() { return -1; }
|
||||
virtual bool reset() = 0;
|
||||
void disableReset();
|
||||
bool isResetDisabled() { return resetDisabled; }
|
||||
@@ -106,6 +107,7 @@ public:
|
||||
bool atEnd();
|
||||
bool reset();
|
||||
qint64 size();
|
||||
+ qint64 pos() Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QByteArray* byteArray;
|
||||
qint64 currentPosition;
|
||||
@@ -121,6 +123,7 @@ public:
|
||||
bool atEnd();
|
||||
bool reset();
|
||||
qint64 size();
|
||||
+ qint64 pos() Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QSharedPointer<QRingBuffer> ringBuffer;
|
||||
qint64 currentPosition;
|
||||
@@ -138,6 +141,7 @@ public:
|
||||
bool atEnd();
|
||||
bool reset();
|
||||
qint64 size();
|
||||
+ qint64 pos() Q_DECL_OVERRIDE;
|
||||
protected:
|
||||
QIODevice* device;
|
||||
QByteArray* currentReadBuffer;
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 9f63280..49c6793 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -106,15 +106,19 @@ void QHttpNetworkConnectionChannel::init()
|
||||
socket->setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
+ // We want all signals (except the interactive ones) be connected as QueuedConnection
|
||||
+ // because else we're falling into cases where we recurse back into the socket code
|
||||
+ // and mess up the state. Always going to the event loop (and expecting that when reading/writing)
|
||||
+ // is safer.
|
||||
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(_q_bytesWritten(qint64)),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
QObject::connect(socket, SIGNAL(connected()),
|
||||
this, SLOT(_q_connected()),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
QObject::connect(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(_q_readyRead()),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
|
||||
// The disconnected() and error() signals may already come
|
||||
// while calling connectToHost().
|
||||
@@ -143,13 +147,13 @@ void QHttpNetworkConnectionChannel::init()
|
||||
// won't be a sslSocket if encrypt is false
|
||||
QObject::connect(sslSocket, SIGNAL(encrypted()),
|
||||
this, SLOT(_q_encrypted()),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
this, SLOT(_q_sslErrors(QList<QSslError>)),
|
||||
Qt::DirectConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
|
||||
this, SLOT(_q_encryptedBytesWritten(qint64)),
|
||||
- Qt::DirectConnection);
|
||||
+ Qt::QueuedConnection);
|
||||
|
||||
if (ignoreAllSslErrors)
|
||||
sslSocket->ignoreSslErrors();
|
||||
@@ -186,8 +190,11 @@ void QHttpNetworkConnectionChannel::close()
|
||||
// pendingEncrypt must only be true in between connected and encrypted states
|
||||
pendingEncrypt = false;
|
||||
|
||||
- if (socket)
|
||||
+ if (socket) {
|
||||
+ // socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
|
||||
+ // there is no socket yet.
|
||||
socket->close();
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
@@ -353,6 +360,14 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
|
||||
}
|
||||
return false;
|
||||
}
|
||||
+
|
||||
+ // This code path for ConnectedState
|
||||
+ if (pendingEncrypt) {
|
||||
+ // Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up
|
||||
+ // and corrupt the things sent to the server.
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -659,6 +674,12 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
|
||||
void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
|
||||
{
|
||||
Q_UNUSED(bytes);
|
||||
+ if (ssl) {
|
||||
+ // In the SSL case we want to send data from encryptedBytesWritten signal since that one
|
||||
+ // is the one going down to the actual network, not only into some SSL buffer.
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
// bytes have been written to the socket. write even more of them :)
|
||||
if (isSocketWriting())
|
||||
sendRequest();
|
||||
@@ -734,7 +755,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
|
||||
|
||||
// ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
|
||||
//channels[i].reconnectAttempts = 2;
|
||||
- if (pendingEncrypt) {
|
||||
+ if (ssl || pendingEncrypt) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
|
||||
#ifndef QT_NO_SSL
|
||||
if (connection->sslContext().isNull()) {
|
||||
// this socket is making the 1st handshake for this connection,
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
index 692c0e6..231fe11 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
@@ -83,6 +83,8 @@ typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
|
||||
class QHttpNetworkConnectionChannel : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
+ // TODO: Refactor this to add an EncryptingState (and remove pendingEncrypt).
|
||||
+ // Also add an Unconnected state so IdleState does not have double meaning.
|
||||
enum ChannelState {
|
||||
IdleState = 0, // ready to send request
|
||||
ConnectingState = 1, // connecting to host
|
||||
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
|
||||
index 28e10f7..3357948 100644
|
||||
--- a/src/network/access/qhttpprotocolhandler.cpp
|
||||
+++ b/src/network/access/qhttpprotocolhandler.cpp
|
||||
@@ -368,6 +368,13 @@ bool QHttpProtocolHandler::sendRequest()
|
||||
// nothing to read currently, break the loop
|
||||
break;
|
||||
} else {
|
||||
+ if (m_channel->written != uploadByteDevice->pos()) {
|
||||
+ // Sanity check. This was useful in tracking down an upload corruption.
|
||||
+ qWarning() << "QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position" << m_channel->written << "but read device is at" << uploadByteDevice->pos();
|
||||
+ Q_ASSERT(m_channel->written == uploadByteDevice->pos());
|
||||
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
|
||||
+ return false;
|
||||
+ }
|
||||
qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize);
|
||||
if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
|
||||
// socket broke down
|
||||
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
|
||||
index 1661082..b553409 100644
|
||||
--- a/src/network/access/qhttpthreaddelegate_p.h
|
||||
+++ b/src/network/access/qhttpthreaddelegate_p.h
|
||||
@@ -187,6 +187,7 @@ protected:
|
||||
QByteArray m_dataArray;
|
||||
bool m_atEnd;
|
||||
qint64 m_size;
|
||||
+ qint64 m_pos; // to match calls of haveDataSlot with the expected position
|
||||
public:
|
||||
QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
|
||||
: QNonContiguousByteDevice(),
|
||||
@@ -194,7 +195,8 @@ public:
|
||||
m_amount(0),
|
||||
m_data(0),
|
||||
m_atEnd(aE),
|
||||
- m_size(s)
|
||||
+ m_size(s),
|
||||
+ m_pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -202,6 +204,11 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
+ qint64 pos() Q_DECL_OVERRIDE
|
||||
+ {
|
||||
+ return m_pos;
|
||||
+ }
|
||||
+
|
||||
const char* readPointer(qint64 maximumLength, qint64 &len)
|
||||
{
|
||||
if (m_amount > 0) {
|
||||
@@ -229,11 +236,10 @@ public:
|
||||
|
||||
m_amount -= a;
|
||||
m_data += a;
|
||||
+ m_pos += a;
|
||||
|
||||
- // To main thread to inform about our state
|
||||
- emit processedData(a);
|
||||
-
|
||||
- // FIXME possible optimization, already ask user thread for some data
|
||||
+ // To main thread to inform about our state. The m_pos will be sent as a sanity check.
|
||||
+ emit processedData(m_pos, a);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -250,10 +256,21 @@ public:
|
||||
{
|
||||
m_amount = 0;
|
||||
m_data = 0;
|
||||
+ m_dataArray.clear();
|
||||
+
|
||||
+ if (wantDataPending) {
|
||||
+ // had requested the user thread to send some data (only 1 in-flight at any moment)
|
||||
+ wantDataPending = false;
|
||||
+ }
|
||||
|
||||
// Communicate as BlockingQueuedConnection
|
||||
bool b = false;
|
||||
emit resetData(&b);
|
||||
+ if (b) {
|
||||
+ // the reset succeeded, we're at pos 0 again
|
||||
+ m_pos = 0;
|
||||
+ // the HTTP code will anyway abort the request if !b.
|
||||
+ }
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -264,8 +281,13 @@ public:
|
||||
|
||||
public slots:
|
||||
// From user thread:
|
||||
- void haveDataSlot(QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
|
||||
+ void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
|
||||
{
|
||||
+ if (pos != m_pos) {
|
||||
+ // Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the
|
||||
+ // user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk.
|
||||
+ return;
|
||||
+ }
|
||||
wantDataPending = false;
|
||||
|
||||
m_dataArray = dataArray;
|
||||
@@ -285,7 +307,7 @@ signals:
|
||||
|
||||
// to main thread:
|
||||
void wantData(qint64);
|
||||
- void processedData(qint64);
|
||||
+ void processedData(qint64 pos, qint64 amount);
|
||||
void resetData(bool *b);
|
||||
};
|
||||
|
||||
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
index 4ce7303..974a101 100644
|
||||
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
|
||||
@@ -424,6 +424,7 @@ QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
|
||||
, synchronous(false)
|
||||
, state(Idle)
|
||||
, statusCode(0)
|
||||
+ , uploadByteDevicePosition(false)
|
||||
, uploadDeviceChoking(false)
|
||||
, outgoingData(0)
|
||||
, bytesUploaded(-1)
|
||||
@@ -863,9 +864,9 @@ void QNetworkReplyHttpImplPrivate::postRequest()
|
||||
q, SLOT(uploadByteDeviceReadyReadSlot()),
|
||||
Qt::QueuedConnection);
|
||||
|
||||
- // From main thread to user thread:
|
||||
- QObject::connect(q, SIGNAL(haveUploadData(QByteArray,bool,qint64)),
|
||||
- forwardUploadDevice, SLOT(haveDataSlot(QByteArray,bool,qint64)), Qt::QueuedConnection);
|
||||
+ // From user thread to http thread:
|
||||
+ QObject::connect(q, SIGNAL(haveUploadData(qint64,QByteArray,bool,qint64)),
|
||||
+ forwardUploadDevice, SLOT(haveDataSlot(qint64,QByteArray,bool,qint64)), Qt::QueuedConnection);
|
||||
QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
|
||||
forwardUploadDevice, SIGNAL(readyRead()),
|
||||
Qt::QueuedConnection);
|
||||
@@ -873,8 +874,8 @@ void QNetworkReplyHttpImplPrivate::postRequest()
|
||||
// From http thread to user thread:
|
||||
QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
|
||||
q, SLOT(wantUploadDataSlot(qint64)));
|
||||
- QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
|
||||
- q, SLOT(sentUploadDataSlot(qint64)));
|
||||
+ QObject::connect(forwardUploadDevice,SIGNAL(processedData(qint64, qint64)),
|
||||
+ q, SLOT(sentUploadDataSlot(qint64,qint64)));
|
||||
QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
|
||||
q, SLOT(resetUploadDataSlot(bool*)),
|
||||
Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
|
||||
@@ -1268,12 +1269,22 @@ void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfig
|
||||
void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r)
|
||||
{
|
||||
*r = uploadByteDevice->reset();
|
||||
+ if (*r) {
|
||||
+ // reset our own position which is used for the inter-thread communication
|
||||
+ uploadByteDevicePosition = 0;
|
||||
+ }
|
||||
}
|
||||
|
||||
// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
|
||||
-void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount)
|
||||
+void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 pos, qint64 amount)
|
||||
{
|
||||
+ if (uploadByteDevicePosition + amount != pos) {
|
||||
+ // Sanity check, should not happen.
|
||||
+ error(QNetworkReply::UnknownNetworkError, "");
|
||||
+ return;
|
||||
+ }
|
||||
uploadByteDevice->advanceReadPointer(amount);
|
||||
+ uploadByteDevicePosition += amount;
|
||||
}
|
||||
|
||||
// Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
|
||||
@@ -1298,7 +1309,7 @@ void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize)
|
||||
QByteArray dataArray(data, currentUploadDataLength);
|
||||
|
||||
// Communicate back to HTTP thread
|
||||
- emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
|
||||
+ emit q->haveUploadData(uploadByteDevicePosition, dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
|
||||
}
|
||||
|
||||
void QNetworkReplyHttpImplPrivate::uploadByteDeviceReadyReadSlot()
|
||||
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
|
||||
index 77d9c5a..1940922 100644
|
||||
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
|
||||
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
|
||||
Q_PRIVATE_SLOT(d_func(), void resetUploadDataSlot(bool *r))
|
||||
Q_PRIVATE_SLOT(d_func(), void wantUploadDataSlot(qint64))
|
||||
- Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64))
|
||||
+ Q_PRIVATE_SLOT(d_func(), void sentUploadDataSlot(qint64,qint64))
|
||||
Q_PRIVATE_SLOT(d_func(), void uploadByteDeviceReadyReadSlot())
|
||||
Q_PRIVATE_SLOT(d_func(), void emitReplyUploadProgress(qint64, qint64))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose())
|
||||
@@ -144,7 +144,7 @@ signals:
|
||||
|
||||
void startHttpRequestSynchronously();
|
||||
|
||||
- void haveUploadData(QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
|
||||
+ void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
|
||||
};
|
||||
|
||||
class QNetworkReplyHttpImplPrivate: public QNetworkReplyPrivate
|
||||
@@ -195,6 +195,7 @@ public:
|
||||
// upload
|
||||
QNonContiguousByteDevice* createUploadByteDevice();
|
||||
QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
|
||||
+ qint64 uploadByteDevicePosition;
|
||||
bool uploadDeviceChoking; // if we couldn't readPointer() any data at the moment
|
||||
QIODevice *outgoingData;
|
||||
QSharedPointer<QRingBuffer> outgoingDataBuffer;
|
||||
@@ -283,7 +284,7 @@ public:
|
||||
// From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
|
||||
void resetUploadDataSlot(bool *r);
|
||||
void wantUploadDataSlot(qint64);
|
||||
- void sentUploadDataSlot(qint64);
|
||||
+ void sentUploadDataSlot(qint64, qint64);
|
||||
|
||||
// From user's QNonContiguousByteDevice
|
||||
void uploadByteDeviceReadyReadSlot();
|
||||
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
index 3ccedf6..d2edf67 100644
|
||||
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
@@ -457,6 +457,10 @@ private Q_SLOTS:
|
||||
|
||||
void putWithRateLimiting();
|
||||
|
||||
+#ifndef QT_NO_SSL
|
||||
+ void putWithServerClosingConnectionImmediately();
|
||||
+#endif
|
||||
+
|
||||
// NOTE: This test must be last!
|
||||
void parentingRepliesToTheApp();
|
||||
private:
|
||||
@@ -4718,18 +4722,22 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag()
|
||||
class SslServer : public QTcpServer {
|
||||
Q_OBJECT
|
||||
public:
|
||||
- SslServer() : socket(0) {};
|
||||
+ SslServer() : socket(0), m_ssl(true) {}
|
||||
void incomingConnection(qintptr socketDescriptor) {
|
||||
QSslSocket *serverSocket = new QSslSocket;
|
||||
serverSocket->setParent(this);
|
||||
|
||||
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
|
||||
+ connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
+ if (!m_ssl) {
|
||||
+ emit newPlainConnection(serverSocket);
|
||||
+ return;
|
||||
+ }
|
||||
QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
|
||||
if (testDataDir.isEmpty())
|
||||
testDataDir = QCoreApplication::applicationDirPath();
|
||||
|
||||
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(encryptedSlot()));
|
||||
- connect(serverSocket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
serverSocket->setProtocol(QSsl::AnyProtocol);
|
||||
connect(serverSocket, SIGNAL(sslErrors(QList<QSslError>)), serverSocket, SLOT(ignoreSslErrors()));
|
||||
serverSocket->setLocalCertificate(testDataDir + "/certs/server.pem");
|
||||
@@ -4740,11 +4748,12 @@ public:
|
||||
}
|
||||
}
|
||||
signals:
|
||||
- void newEncryptedConnection();
|
||||
+ void newEncryptedConnection(QSslSocket *s);
|
||||
+ void newPlainConnection(QSslSocket *s);
|
||||
public slots:
|
||||
void encryptedSlot() {
|
||||
socket = (QSslSocket*) sender();
|
||||
- emit newEncryptedConnection();
|
||||
+ emit newEncryptedConnection(socket);
|
||||
}
|
||||
void readyReadSlot() {
|
||||
// for the incoming sockets, not the server socket
|
||||
@@ -4753,6 +4762,7 @@ public slots:
|
||||
|
||||
public:
|
||||
QSslSocket *socket;
|
||||
+ bool m_ssl;
|
||||
};
|
||||
|
||||
// very similar to ioPostToHttpUploadProgress but for SSL
|
||||
@@ -4780,7 +4790,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress()
|
||||
QNetworkReplyPtr reply(manager.post(request, sourceFile));
|
||||
|
||||
QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
|
||||
- connect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
+ connect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), reply.data(), SLOT(ignoreSslErrors()));
|
||||
|
||||
// get the request started and the incoming socket connected
|
||||
@@ -4788,7 +4798,7 @@ void tst_QNetworkReply::ioPostToHttpsUploadProgress()
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QTcpSocket *incomingSocket = server.socket;
|
||||
QVERIFY(incomingSocket);
|
||||
- disconnect(&server, SIGNAL(newEncryptedConnection()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
+ disconnect(&server, SIGNAL(newEncryptedConnection(QSslSocket*)), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
|
||||
|
||||
incomingSocket->setReadBufferSize(1*1024);
|
||||
@@ -7905,6 +7915,159 @@ void tst_QNetworkReply::putWithRateLimiting()
|
||||
}
|
||||
|
||||
|
||||
+#ifndef QT_NO_SSL
|
||||
+
|
||||
+class PutWithServerClosingConnectionImmediatelyHandler: public QObject
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ bool m_parsedHeaders;
|
||||
+ QByteArray m_receivedData;
|
||||
+ QByteArray m_expectedData;
|
||||
+ QSslSocket *m_socket;
|
||||
+ PutWithServerClosingConnectionImmediatelyHandler(QSslSocket *s, QByteArray expected) :m_parsedHeaders(false), m_expectedData(expected), m_socket(s)
|
||||
+ {
|
||||
+ m_socket->setParent(this);
|
||||
+ connect(m_socket, SIGNAL(readyRead()), SLOT(readyReadSlot()));
|
||||
+ connect(m_socket, SIGNAL(disconnected()), SLOT(disconnectedSlot()));
|
||||
+ }
|
||||
+signals:
|
||||
+ void correctFileUploadReceived();
|
||||
+ void corruptFileUploadReceived();
|
||||
+
|
||||
+public slots:
|
||||
+ void closeDelayed() {
|
||||
+ m_socket->close();
|
||||
+ }
|
||||
+
|
||||
+ void readyReadSlot()
|
||||
+ {
|
||||
+ QByteArray data = m_socket->readAll();
|
||||
+ m_receivedData += data;
|
||||
+ if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) {
|
||||
+ m_parsedHeaders = true;
|
||||
+ QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency
|
||||
+ // This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout
|
||||
+ // In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload
|
||||
+ // This test catches that.
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ void disconnectedSlot()
|
||||
+ {
|
||||
+ if (m_parsedHeaders) {
|
||||
+ //qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n"));
|
||||
+ m_receivedData = m_receivedData.mid(m_receivedData.indexOf("\r\n\r\n")+4); // check only actual data
|
||||
+ }
|
||||
+ if (m_receivedData.length() > 0 && !m_expectedData.startsWith(m_receivedData)) {
|
||||
+ // We had received some data but it is corrupt!
|
||||
+ qDebug() << "CORRUPT" << m_receivedData.count();
|
||||
+
|
||||
+ // Use this to track down the pattern of the corruption and conclude the source
|
||||
+// QFile a("/tmp/corrupt");
|
||||
+// a.open(QIODevice::WriteOnly);
|
||||
+// a.write(m_receivedData);
|
||||
+// a.close();
|
||||
+
|
||||
+// QFile b("/tmp/correct");
|
||||
+// b.open(QIODevice::WriteOnly);
|
||||
+// b.write(m_expectedData);
|
||||
+// b.close();
|
||||
+ //exit(1);
|
||||
+ emit corruptFileUploadReceived();
|
||||
+ } else {
|
||||
+ emit correctFileUploadReceived();
|
||||
+ }
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+class PutWithServerClosingConnectionImmediatelyServer: public SslServer
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ int m_correctUploads;
|
||||
+ int m_corruptUploads;
|
||||
+ int m_repliesFinished;
|
||||
+ int m_expectedReplies;
|
||||
+ QByteArray m_expectedData;
|
||||
+ PutWithServerClosingConnectionImmediatelyServer() : SslServer(), m_correctUploads(0), m_corruptUploads(0), m_repliesFinished(0), m_expectedReplies(0)
|
||||
+ {
|
||||
+ QObject::connect(this, SIGNAL(newEncryptedConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*)));
|
||||
+ QObject::connect(this, SIGNAL(newPlainConnection(QSslSocket*)), this, SLOT(createHandlerForConnection(QSslSocket*)));
|
||||
+ }
|
||||
+
|
||||
+public slots:
|
||||
+ void createHandlerForConnection(QSslSocket* s) {
|
||||
+ PutWithServerClosingConnectionImmediatelyHandler *handler = new PutWithServerClosingConnectionImmediatelyHandler(s, m_expectedData);
|
||||
+ handler->setParent(this);
|
||||
+ QObject::connect(handler, SIGNAL(correctFileUploadReceived()), this, SLOT(increaseCorrect()));
|
||||
+ QObject::connect(handler, SIGNAL(corruptFileUploadReceived()), this, SLOT(increaseCorrupt()));
|
||||
+ }
|
||||
+ void increaseCorrect() {
|
||||
+ m_correctUploads++;
|
||||
+ }
|
||||
+ void increaseCorrupt() {
|
||||
+ m_corruptUploads++;
|
||||
+ }
|
||||
+ void replyFinished() {
|
||||
+ m_repliesFinished++;
|
||||
+ if (m_repliesFinished == m_expectedReplies) {
|
||||
+ QTestEventLoop::instance().exitLoop();
|
||||
+ }
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+
|
||||
+
|
||||
+void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
|
||||
+{
|
||||
+ const int numUploads = 40;
|
||||
+ qint64 wantedSize = 512*1024; // 512 kB
|
||||
+ QByteArray sourceFile;
|
||||
+ for (int i = 0; i < wantedSize; ++i) {
|
||||
+ sourceFile += (char)'a' +(i%26);
|
||||
+ }
|
||||
+ bool withSsl = false;
|
||||
+
|
||||
+ for (int s = 0; s <= 1; s++) {
|
||||
+ withSsl = (s == 1);
|
||||
+ // Test also needs to run several times because of 9c2ecf89
|
||||
+ for (int j = 0; j < 20; j++) {
|
||||
+ // emulate a minimal https server
|
||||
+ PutWithServerClosingConnectionImmediatelyServer server;
|
||||
+ server.m_ssl = withSsl;
|
||||
+ server.m_expectedData = sourceFile;
|
||||
+ server.m_expectedReplies = numUploads;
|
||||
+ server.listen(QHostAddress(QHostAddress::LocalHost), 0);
|
||||
+
|
||||
+ for (int i = 0; i < numUploads; i++) {
|
||||
+ // create the request
|
||||
+ QUrl url = QUrl(QString("http%1://127.0.0.1:%2/file=%3").arg(withSsl ? "s" : "").arg(server.serverPort()).arg(i));
|
||||
+ QNetworkRequest request(url);
|
||||
+ QNetworkReply *reply = manager.put(request, sourceFile);
|
||||
+ connect(reply, SIGNAL(sslErrors(QList<QSslError>)), reply, SLOT(ignoreSslErrors()));
|
||||
+ connect(reply, SIGNAL(finished()), &server, SLOT(replyFinished()));
|
||||
+ reply->setParent(&server);
|
||||
+ }
|
||||
+
|
||||
+ // get the request started and the incoming socket connected
|
||||
+ QTestEventLoop::instance().enterLoop(10);
|
||||
+
|
||||
+ //qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads;
|
||||
+
|
||||
+ // Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked
|
||||
+ QVERIFY(server.m_correctUploads > 5);
|
||||
+ // Because actually important is that we don't get any corruption:
|
||||
+ QCOMPARE(server.m_corruptUploads, 0);
|
||||
+
|
||||
+ server.close();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
|
||||
// NOTE: This test must be last testcase in tst_qnetworkreply!
|
||||
void tst_QNetworkReply::parentingRepliesToTheApp()
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,434 @@
|
||||
From eae0cb09f1310e755c2aff7c1112f7a6c09d7a53 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Fri, 19 Jun 2015 15:35:34 +0200
|
||||
Subject: [PATCH] Network: Fix up previous corruption patch
|
||||
|
||||
This is a fix-up for cff39fba10ffc10ee4dcfdc66ff6528eb26462d3.
|
||||
That patch lead to some internal state issues that lead to the QTBUG-47048
|
||||
or to QNetworkReply objects erroring with "Connection Closed" when
|
||||
the server closed the Keep-Alive connection.
|
||||
|
||||
This patch changes the QNAM socket slot connections to be DirectConnection.
|
||||
We don't close the socket anymore in slots where it is anyway in a closed state
|
||||
afterwards. This prevents event/stack recursions.
|
||||
We also flush QSslSocket/QTcpSocket receive buffers when receiving a disconnect
|
||||
so that the developer always gets the full decrypted data from the buffers.
|
||||
|
||||
[ChangeLog][QtNetwork] Fix HTTP issues with "Unknown Error" and "Connection Closed"
|
||||
[ChangeLog][QtNetwork][Sockets] Read OS/encrypted read buffers when connection
|
||||
closed by server.
|
||||
|
||||
Change-Id: Ib4d6a2d0d988317e3a5356f36e8dbcee4590beed
|
||||
Task-number: QTBUG-47048
|
||||
Reviewed-by: Kai Koehne <kai.koehne@theqtcompany.com>
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/access/qhttpnetworkconnection.cpp | 1 -
|
||||
.../access/qhttpnetworkconnectionchannel.cpp | 108 +++++++++++++--------
|
||||
.../access/qhttpnetworkconnectionchannel_p.h | 1 +
|
||||
src/network/access/qhttpnetworkreply.cpp | 2 +-
|
||||
src/network/access/qhttpprotocolhandler.cpp | 1 -
|
||||
src/network/socket/qabstractsocket.cpp | 7 +-
|
||||
src/network/ssl/qsslsocket.cpp | 8 ++
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 7 ++
|
||||
.../access/qnetworkreply/tst_qnetworkreply.cpp | 9 +-
|
||||
9 files changed, 94 insertions(+), 50 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
|
||||
index 365ce55..543c70e 100644
|
||||
--- a/src/network/access/qhttpnetworkconnection.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnection.cpp
|
||||
@@ -917,7 +917,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
|
||||
channels[i].resendCurrent = false;
|
||||
- channels[i].state = QHttpNetworkConnectionChannel::IdleState;
|
||||
|
||||
// if this is not possible, error will be emitted and connection terminated
|
||||
if (!channels[i].resetUploadData())
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 49c6793..e2f6307 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -58,6 +58,11 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
|
||||
|
||||
+// Because in-flight when sending a request, the server might close our connection (because the persistent HTTP
|
||||
+// connection times out)
|
||||
+// We use 3 because we can get a _q_error 3 times depending on the timing:
|
||||
+static const int reconnectAttemptsDefault = 3;
|
||||
+
|
||||
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
|
||||
: socket(0)
|
||||
, ssl(false)
|
||||
@@ -69,7 +74,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
|
||||
, resendCurrent(false)
|
||||
, lastStatus(0)
|
||||
, pendingEncrypt(false)
|
||||
- , reconnectAttempts(2)
|
||||
+ , reconnectAttempts(reconnectAttemptsDefault)
|
||||
, authMethod(QAuthenticatorPrivate::None)
|
||||
, proxyAuthMethod(QAuthenticatorPrivate::None)
|
||||
, authenticationCredentialsSent(false)
|
||||
@@ -106,19 +111,18 @@ void QHttpNetworkConnectionChannel::init()
|
||||
socket->setProxy(QNetworkProxy::NoProxy);
|
||||
#endif
|
||||
|
||||
- // We want all signals (except the interactive ones) be connected as QueuedConnection
|
||||
- // because else we're falling into cases where we recurse back into the socket code
|
||||
- // and mess up the state. Always going to the event loop (and expecting that when reading/writing)
|
||||
- // is safer.
|
||||
+ // After some back and forth in all the last years, this is now a DirectConnection because otherwise
|
||||
+ // the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
|
||||
+ // which behave slightly differently on Windows vs Linux
|
||||
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(_q_bytesWritten(qint64)),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(socket, SIGNAL(connected()),
|
||||
this, SLOT(_q_connected()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(socket, SIGNAL(readyRead()),
|
||||
this, SLOT(_q_readyRead()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
|
||||
// The disconnected() and error() signals may already come
|
||||
// while calling connectToHost().
|
||||
@@ -129,10 +133,10 @@ void QHttpNetworkConnectionChannel::init()
|
||||
qRegisterMetaType<QAbstractSocket::SocketError>();
|
||||
QObject::connect(socket, SIGNAL(disconnected()),
|
||||
this, SLOT(_q_disconnected()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||
this, SLOT(_q_error(QAbstractSocket::SocketError)),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
@@ -147,13 +151,13 @@ void QHttpNetworkConnectionChannel::init()
|
||||
// won't be a sslSocket if encrypt is false
|
||||
QObject::connect(sslSocket, SIGNAL(encrypted()),
|
||||
this, SLOT(_q_encrypted()),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
this, SLOT(_q_sslErrors(QList<QSslError>)),
|
||||
Qt::DirectConnection);
|
||||
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
|
||||
this, SLOT(_q_encryptedBytesWritten(qint64)),
|
||||
- Qt::QueuedConnection);
|
||||
+ Qt::DirectConnection);
|
||||
|
||||
if (ignoreAllSslErrors)
|
||||
sslSocket->ignoreSslErrors();
|
||||
@@ -397,7 +401,7 @@ void QHttpNetworkConnectionChannel::allDone()
|
||||
|
||||
// reset the reconnection attempts after we receive a complete reply.
|
||||
// in case of failures, each channel will attempt two reconnects before emitting error.
|
||||
- reconnectAttempts = 2;
|
||||
+ reconnectAttempts = reconnectAttemptsDefault;
|
||||
|
||||
// now the channel can be seen as free/idle again, all signal emissions for the reply have been done
|
||||
if (state != QHttpNetworkConnectionChannel::ClosingState)
|
||||
@@ -651,6 +655,15 @@ void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
|
||||
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
+void QHttpNetworkConnectionChannel::resendCurrentRequest()
|
||||
+{
|
||||
+ requeueCurrentlyPipelinedRequests();
|
||||
+ if (reply)
|
||||
+ resendCurrent = true;
|
||||
+ if (qobject_cast<QHttpNetworkConnection*>(connection))
|
||||
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
+}
|
||||
+
|
||||
bool QHttpNetworkConnectionChannel::isSocketBusy() const
|
||||
{
|
||||
return (state & QHttpNetworkConnectionChannel::BusyState);
|
||||
@@ -694,8 +707,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
|
||||
return;
|
||||
}
|
||||
|
||||
- // read the available data before closing
|
||||
- if (isSocketWaiting() || isSocketReading()) {
|
||||
+ // read the available data before closing (also done in _q_error for other codepaths)
|
||||
+ if ((isSocketWaiting() || isSocketReading()) && socket->bytesAvailable()) {
|
||||
if (reply) {
|
||||
state = QHttpNetworkConnectionChannel::ReadingState;
|
||||
_q_receiveReply();
|
||||
@@ -707,7 +720,8 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
|
||||
state = QHttpNetworkConnectionChannel::IdleState;
|
||||
|
||||
requeueCurrentlyPipelinedRequests();
|
||||
- close();
|
||||
+
|
||||
+ pendingEncrypt = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -789,11 +803,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
errorCode = QNetworkReply::ConnectionRefusedError;
|
||||
break;
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
- // try to reconnect/resend before sending an error.
|
||||
- // while "Reading" the _q_disconnected() will handle this.
|
||||
- if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
|
||||
+ // This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
|
||||
+ // Depending on timing it can also come three times in a row (first time when we try to write into a closing QSslSocket).
|
||||
+ // The reconnectAttempts handling catches the cases where we can re-send the request.
|
||||
+ if (!reply && state == QHttpNetworkConnectionChannel::IdleState) {
|
||||
+ // Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
|
||||
+ // is sent on them. No need to error the other replies below. Just bail out here.
|
||||
+ // The _q_disconnected will handle the possibly pipelined replies
|
||||
+ return;
|
||||
+ } else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
|
||||
+ // Try to reconnect/resend before sending an error.
|
||||
+ // While "Reading" the _q_disconnected() will handle this.
|
||||
if (reconnectAttempts-- > 0) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
} else {
|
||||
errorCode = QNetworkReply::RemoteHostClosedError;
|
||||
@@ -818,24 +840,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
// we can ignore the readbuffersize as the data is already
|
||||
// in memory and we will not receive more data on the socket.
|
||||
reply->setReadBufferSize(0);
|
||||
+ reply->setDownstreamLimited(false);
|
||||
_q_receiveReply();
|
||||
-#ifndef QT_NO_SSL
|
||||
- if (ssl) {
|
||||
- // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
|
||||
- // So we need to check this if the socket is a QSslSocket. When the socket is flushed
|
||||
- // it will force a decrypt of the encrypted data in the plainsocket.
|
||||
- QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
|
||||
- qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
|
||||
- while (sslSocket->encryptedBytesAvailable()) {
|
||||
- sslSocket->flush();
|
||||
- _q_receiveReply();
|
||||
- qint64 afterFlush = sslSocket->encryptedBytesAvailable();
|
||||
- if (afterFlush == beforeFlush)
|
||||
- break;
|
||||
- beforeFlush = afterFlush;
|
||||
- }
|
||||
+ if (!reply) {
|
||||
+ // No more reply assigned after the previous call? Then it had been finished successfully.
|
||||
+ requeueCurrentlyPipelinedRequests();
|
||||
+ state = QHttpNetworkConnectionChannel::IdleState;
|
||||
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
+ return;
|
||||
}
|
||||
-#endif
|
||||
}
|
||||
|
||||
errorCode = QNetworkReply::RemoteHostClosedError;
|
||||
@@ -846,7 +859,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
case QAbstractSocket::SocketTimeoutError:
|
||||
// try to reconnect/resend before sending an error.
|
||||
if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
}
|
||||
errorCode = QNetworkReply::TimeoutError;
|
||||
@@ -860,7 +873,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
case QAbstractSocket::ProxyConnectionClosedError:
|
||||
// try to reconnect/resend before sending an error.
|
||||
if (reconnectAttempts-- > 0) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
}
|
||||
errorCode = QNetworkReply::ProxyConnectionClosedError;
|
||||
@@ -868,7 +881,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
case QAbstractSocket::ProxyConnectionTimeoutError:
|
||||
// try to reconnect/resend before sending an error.
|
||||
if (reconnectAttempts-- > 0) {
|
||||
- closeAndResendCurrentRequest();
|
||||
+ resendCurrentRequest();
|
||||
return;
|
||||
}
|
||||
errorCode = QNetworkReply::ProxyTimeoutError;
|
||||
@@ -916,8 +929,18 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
// send the next request
|
||||
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
|
||||
- if (that) //signal emission triggered event loop
|
||||
- close();
|
||||
+ if (that) {
|
||||
+ //signal emission triggered event loop
|
||||
+ if (!socket)
|
||||
+ state = QHttpNetworkConnectionChannel::IdleState;
|
||||
+ else if (socket->state() == QAbstractSocket::UnconnectedState)
|
||||
+ state = QHttpNetworkConnectionChannel::IdleState;
|
||||
+ else
|
||||
+ state = QHttpNetworkConnectionChannel::ClosingState;
|
||||
+
|
||||
+ // pendingEncrypt must only be true in between connected and encrypted states
|
||||
+ pendingEncrypt = false;
|
||||
+ }
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
@@ -941,7 +964,8 @@ void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetwor
|
||||
|
||||
void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
|
||||
{
|
||||
- sendRequest();
|
||||
+ if (reply)
|
||||
+ sendRequest();
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
index 231fe11..a834b7d 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
|
||||
@@ -169,6 +169,7 @@ public:
|
||||
|
||||
void handleUnexpectedEOF();
|
||||
void closeAndResendCurrentRequest();
|
||||
+ void resendCurrentRequest();
|
||||
|
||||
bool isSocketBusy() const;
|
||||
bool isSocketWriting() const;
|
||||
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
|
||||
index 55863a3..8b71bd8 100644
|
||||
--- a/src/network/access/qhttpnetworkreply.cpp
|
||||
+++ b/src/network/access/qhttpnetworkreply.cpp
|
||||
@@ -191,7 +191,7 @@ QByteArray QHttpNetworkReply::readAny()
|
||||
return QByteArray();
|
||||
|
||||
// we'll take the last buffer, so schedule another read from http
|
||||
- if (d->downstreamLimited && d->responseData.bufferCount() == 1)
|
||||
+ if (d->downstreamLimited && d->responseData.bufferCount() == 1 && !isFinished())
|
||||
d->connection->d_func()->readMoreLater(this);
|
||||
return d->responseData.read();
|
||||
}
|
||||
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
|
||||
index 3357948..380aaac 100644
|
||||
--- a/src/network/access/qhttpprotocolhandler.cpp
|
||||
+++ b/src/network/access/qhttpprotocolhandler.cpp
|
||||
@@ -250,7 +250,6 @@ bool QHttpProtocolHandler::sendRequest()
|
||||
if (!m_reply) {
|
||||
// heh, how should that happen!
|
||||
qWarning() << "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply";
|
||||
- m_channel->state = QHttpNetworkConnectionChannel::IdleState;
|
||||
return false;
|
||||
}
|
||||
|
||||
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
|
||||
index 2666771..0e82d4a 100644
|
||||
--- a/src/network/socket/qabstractsocket.cpp
|
||||
+++ b/src/network/socket/qabstractsocket.cpp
|
||||
@@ -768,6 +768,7 @@ bool QAbstractSocketPrivate::canReadNotification()
|
||||
void QAbstractSocketPrivate::canCloseNotification()
|
||||
{
|
||||
Q_Q(QAbstractSocket);
|
||||
+ // Note that this method is only called on Windows. Other platforms close in the canReadNotification()
|
||||
|
||||
#if defined (QABSTRACTSOCKET_DEBUG)
|
||||
qDebug("QAbstractSocketPrivate::canCloseNotification()");
|
||||
@@ -777,7 +778,11 @@ void QAbstractSocketPrivate::canCloseNotification()
|
||||
if (isBuffered) {
|
||||
// Try to read to the buffer, if the read fail we can close the socket.
|
||||
newBytes = buffer.size();
|
||||
- if (!readFromSocket()) {
|
||||
+ qint64 oldReadBufferMaxSize = readBufferMaxSize;
|
||||
+ readBufferMaxSize = 0; // temporarily disable max read buffer, we want to empty the OS buffer
|
||||
+ bool hadReadFromSocket = readFromSocket();
|
||||
+ readBufferMaxSize = oldReadBufferMaxSize;
|
||||
+ if (!hadReadFromSocket) {
|
||||
q->disconnectFromHost();
|
||||
return;
|
||||
}
|
||||
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
|
||||
index c1fab94..2b9e923 100644
|
||||
--- a/src/network/ssl/qsslsocket.cpp
|
||||
+++ b/src/network/ssl/qsslsocket.cpp
|
||||
@@ -2294,6 +2294,14 @@ void QSslSocketPrivate::_q_errorSlot(QAbstractSocket::SocketError error)
|
||||
qCDebug(lcSsl) << "\tstate =" << q->state();
|
||||
qCDebug(lcSsl) << "\terrorString =" << q->errorString();
|
||||
#endif
|
||||
+ // this moves encrypted bytes from plain socket into our buffer
|
||||
+ if (plainSocket->bytesAvailable()) {
|
||||
+ qint64 tmpReadBufferMaxSize = readBufferMaxSize;
|
||||
+ readBufferMaxSize = 0; // reset temporarily so the plain sockets completely drained drained
|
||||
+ transmit();
|
||||
+ readBufferMaxSize = tmpReadBufferMaxSize;
|
||||
+ }
|
||||
+
|
||||
q->setSocketError(plainSocket->error());
|
||||
q->setErrorString(plainSocket->errorString());
|
||||
emit q->error(error);
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index ac4336a..94655fe 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -1419,6 +1419,13 @@ void QSslSocketBackendPrivate::disconnected()
|
||||
{
|
||||
if (plainSocket->bytesAvailable() <= 0)
|
||||
destroySslContext();
|
||||
+ else {
|
||||
+ // Move all bytes into the plain buffer
|
||||
+ qint64 tmpReadBufferMaxSize = readBufferMaxSize;
|
||||
+ readBufferMaxSize = 0; // reset temporarily so the plain socket buffer is completely drained
|
||||
+ transmit();
|
||||
+ readBufferMaxSize = tmpReadBufferMaxSize;
|
||||
+ }
|
||||
//if there is still buffered data in the plain socket, don't destroy the ssl context yet.
|
||||
//it will be destroyed when the socket is deleted.
|
||||
}
|
||||
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
index d2edf67..138f528 100644
|
||||
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
|
||||
@@ -1051,7 +1051,7 @@ protected:
|
||||
// clean up QAbstractSocket's residue:
|
||||
while (client->bytesToWrite() > 0) {
|
||||
qDebug() << "Still having" << client->bytesToWrite() << "bytes to write, doing that now";
|
||||
- if (!client->waitForBytesWritten(2000)) {
|
||||
+ if (!client->waitForBytesWritten(10000)) {
|
||||
qDebug() << "ERROR: FastSender:" << client->error() << "cleaning up residue";
|
||||
return;
|
||||
}
|
||||
@@ -1071,7 +1071,7 @@ protected:
|
||||
measuredSentBytes += writeNextData(client, bytesToWrite);
|
||||
|
||||
while (client->bytesToWrite() > 0) {
|
||||
- if (!client->waitForBytesWritten(2000)) {
|
||||
+ if (!client->waitForBytesWritten(10000)) {
|
||||
qDebug() << "ERROR: FastSender:" << client->error() << "during blocking write";
|
||||
return;
|
||||
}
|
||||
@@ -7946,7 +7946,7 @@ public slots:
|
||||
m_receivedData += data;
|
||||
if (!m_parsedHeaders && m_receivedData.contains("\r\n\r\n")) {
|
||||
m_parsedHeaders = true;
|
||||
- QTimer::singleShot(qrand()%10, this, SLOT(closeDelayed())); // simulate random network latency
|
||||
+ QTimer::singleShot(qrand()%60, this, SLOT(closeDelayed())); // simulate random network latency
|
||||
// This server simulates a web server connection closing, e.g. because of Apaches MaxKeepAliveRequests or KeepAliveTimeout
|
||||
// In this case QNAM needs to re-send the upload data but it had a bug which then corrupts the upload
|
||||
// This test catches that.
|
||||
@@ -8052,11 +8052,12 @@ void tst_QNetworkReply::putWithServerClosingConnectionImmediately()
|
||||
|
||||
// get the request started and the incoming socket connected
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
+ QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
//qDebug() << "correct=" << server.m_correctUploads << "corrupt=" << server.m_corruptUploads << "expected=" <<numUploads;
|
||||
|
||||
// Sanity check because ecause of 9c2ecf89 most replies will error out but we want to make sure at least some of them worked
|
||||
- QVERIFY(server.m_correctUploads > 5);
|
||||
+ QVERIFY(server.m_correctUploads > 2);
|
||||
// Because actually important is that we don't get any corruption:
|
||||
QCOMPARE(server.m_corruptUploads, 0);
|
||||
|
||||
--
|
||||
1.9.1
|
||||
@@ -0,0 +1,64 @@
|
||||
From bc32c0ebc0bc00db84ca2f28eb16ab2e5b53a1b6 Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Fri, 24 Jul 2015 09:53:20 +0200
|
||||
Subject: [PATCH] QNAM: Fix reply deadlocks on server closing connection
|
||||
|
||||
The _q_readyRead can also be called from readMoreLater() because we implemented
|
||||
it so that bandwidth limited reading can be implemented.
|
||||
This can lead to a race condition if the socket is closing at the specific moment
|
||||
and then deadlock the channel: It will stay unusable with a zombie request.
|
||||
The fix in QHttpProtocolaHandler checks if there is actually bytes available to read
|
||||
from the socket and only then continue.
|
||||
|
||||
The fix in the HTTP channel needs to be done to properly finish the reply in
|
||||
cases of a server replying with HTTP/1.0 or "Connection: close".
|
||||
The delayed incovation of _q_receiveReply will properly finish up the reply.
|
||||
|
||||
Change-Id: I19ce2ae595f91d56386cc7406ccacc9935672b6b
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/access/qhttpnetworkconnectionchannel.cpp | 4 ++++
|
||||
src/network/access/qhttpprotocolhandler.cpp | 7 ++++++-
|
||||
2 files changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 7428f9b..257aa13 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -829,11 +829,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
||||
|
||||
if (!reply->d_func()->expectContent()) {
|
||||
// No content expected, this is a valid way to have the connection closed by the server
|
||||
+ // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
|
||||
+ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
|
||||
// There was no content-length header and it's not chunked encoding,
|
||||
// so this is a valid way to have the connection closed by the server
|
||||
+ // We need to invoke this asynchronously to make sure the state() of the socket is on QAbstractSocket::UnconnectedState
|
||||
+ QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
// ok, we got a disconnect even though we did not expect it
|
||||
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
|
||||
index ab2e3da..a208315 100644
|
||||
--- a/src/network/access/qhttpprotocolhandler.cpp
|
||||
+++ b/src/network/access/qhttpprotocolhandler.cpp
|
||||
@@ -237,7 +237,12 @@ void QHttpProtocolHandler::_q_readyRead()
|
||||
}
|
||||
|
||||
if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) {
|
||||
- m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
|
||||
+ if (m_socket->bytesAvailable()) {
|
||||
+ // We might get a spurious call from readMoreLater()
|
||||
+ // call of the QHttpNetworkConnection even while the socket is disconnecting.
|
||||
+ // Therefore check if there is actually bytes available before changing the channel state.
|
||||
+ m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
|
||||
+ }
|
||||
if (m_reply)
|
||||
_q_receiveReply();
|
||||
}
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
From 63cf5d3d26a6f65938c3cdec1734eac9faaaf8cb Mon Sep 17 00:00:00 2001
|
||||
From: Markus Goetz <markus@woboq.com>
|
||||
Date: Tue, 22 Sep 2015 14:26:24 -0400
|
||||
Subject: [PATCH] QNAM: Assign proper channel before sslErrors() emission
|
||||
|
||||
There can be a race condition where another channel connects
|
||||
and gets the sslErrors() from the socket first. Then the
|
||||
QSslConfiguration from the wrong socket (the default
|
||||
channel 0's socket) was used.
|
||||
|
||||
Task-number: QTBUG-18722
|
||||
Change-Id: Ibbfa48c27f181563745daf540fa792a57cc09682
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/access/qhttpnetworkconnectionchannel.cpp | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 257aa13..477cba2 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -1066,6 +1066,8 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
|
||||
connection->d_func()->pauseConnection();
|
||||
if (pendingEncrypt && !reply)
|
||||
connection->d_func()->dequeueRequest(socket);
|
||||
+ if (reply) // a reply was actually dequeued.
|
||||
+ reply->d_func()->connectionChannel = this; // set correct channel like in sendRequest() and queueRequest();
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP) {
|
||||
if (reply)
|
||||
emit reply->sslErrors(errors);
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
From 0df5d079290b4c3b13e58e9397fabdc1dfdba96b Mon Sep 17 00:00:00 2001
|
||||
From: Ulf Hermann <ulf.hermann@theqtcompany.com>
|
||||
Date: Fri, 25 Sep 2015 13:23:46 +0200
|
||||
Subject: [PATCH] Don't let closed http sockets pass as valid connections
|
||||
|
||||
A QAbstractSocket can be close()'d at any time, independently of its
|
||||
current connection state. being closed means that we cannot use it to
|
||||
read or write data, but internally it might still have some data to
|
||||
send or receive, for example to an http server. We can even get a
|
||||
connected() signal after close()'ing the socket.
|
||||
|
||||
We need to catch this condition and mark any pending data not yet
|
||||
written to the socket for resending.
|
||||
|
||||
Task-number: QTBUG-48326
|
||||
Change-Id: I6f61c35f2c567f2a138f8cfe9ade7fd1ec039be6
|
||||
Reviewed-by: Simon Hausmann <simon.hausmann@theqtcompany.com>
|
||||
---
|
||||
.../access/qhttpnetworkconnectionchannel.cpp | 7 ++-
|
||||
.../tst_qhttpnetworkconnection.cpp | 54 ++++++++++++++++++++++
|
||||
2 files changed, 60 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
index 293909c..b4eda34 100644
|
||||
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
|
||||
@@ -272,7 +272,12 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
|
||||
QAbstractSocket::SocketState socketState = socket->state();
|
||||
|
||||
// resend this request after we receive the disconnected signal
|
||||
- if (socketState == QAbstractSocket::ClosingState) {
|
||||
+ // If !socket->isOpen() then we have already called close() on the socket, but there was still a
|
||||
+ // pending connectToHost() for which we hadn't seen a connected() signal, yet. The connected()
|
||||
+ // has now arrived (as indicated by socketState != ClosingState), but we cannot send anything on
|
||||
+ // such a socket anymore.
|
||||
+ if (socketState == QAbstractSocket::ClosingState ||
|
||||
+ (socketState != QAbstractSocket::UnconnectedState && !socket->isOpen())) {
|
||||
if (reply)
|
||||
resendCurrent = true;
|
||||
return false;
|
||||
diff --git a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
|
||||
index 5d072af..0d188a8 100644
|
||||
--- a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
|
||||
+++ b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "private/qhttpnetworkconnection_p.h"
|
||||
#include "private/qnoncontiguousbytedevice_p.h"
|
||||
#include <QAuthenticator>
|
||||
+#include <QTcpServer>
|
||||
|
||||
#include "../../../network-settings.h"
|
||||
|
||||
@@ -106,6 +107,8 @@ private Q_SLOTS:
|
||||
|
||||
void getAndThenDeleteObject();
|
||||
void getAndThenDeleteObject_data();
|
||||
+
|
||||
+ void overlappingCloseAndWrite();
|
||||
};
|
||||
|
||||
tst_QHttpNetworkConnection::tst_QHttpNetworkConnection()
|
||||
@@ -1112,6 +1115,57 @@ void tst_QHttpNetworkConnection::getAndThenDeleteObject()
|
||||
}
|
||||
}
|
||||
|
||||
+class TestTcpServer : public QTcpServer
|
||||
+{
|
||||
+ Q_OBJECT
|
||||
+public:
|
||||
+ TestTcpServer() : errorCodeReports(0)
|
||||
+ {
|
||||
+ connect(this, &QTcpServer::newConnection, this, &TestTcpServer::onNewConnection);
|
||||
+ QVERIFY(listen(QHostAddress::LocalHost));
|
||||
+ }
|
||||
+
|
||||
+ int errorCodeReports;
|
||||
+
|
||||
+public slots:
|
||||
+ void onNewConnection()
|
||||
+ {
|
||||
+ QTcpSocket *socket = nextPendingConnection();
|
||||
+ if (!socket)
|
||||
+ return;
|
||||
+ // close socket instantly!
|
||||
+ connect(socket, &QTcpSocket::readyRead, socket, &QTcpSocket::close);
|
||||
+ }
|
||||
+
|
||||
+ void onReply(QNetworkReply::NetworkError code)
|
||||
+ {
|
||||
+ QCOMPARE(code, QNetworkReply::RemoteHostClosedError);
|
||||
+ ++errorCodeReports;
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+void tst_QHttpNetworkConnection::overlappingCloseAndWrite()
|
||||
+{
|
||||
+ // server accepts connections, but closes the socket instantly
|
||||
+ TestTcpServer server;
|
||||
+ QNetworkAccessManager accessManager;
|
||||
+
|
||||
+ // ten requests are scheduled. All should result in an RemoteHostClosed...
|
||||
+ QUrl url;
|
||||
+ url.setScheme(QStringLiteral("http"));
|
||||
+ url.setHost(server.serverAddress().toString());
|
||||
+ url.setPort(server.serverPort());
|
||||
+ for (int i = 0; i < 10; ++i) {
|
||||
+ QNetworkRequest request(url);
|
||||
+ QNetworkReply *reply = accessManager.get(request);
|
||||
+ // Not using Qt5 connection syntax here because of overly baroque syntax to discern between
|
||||
+ // different error() methods.
|
||||
+ QObject::connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
|
||||
+ &server, SLOT(onReply(QNetworkReply::NetworkError)));
|
||||
+ }
|
||||
+
|
||||
+ QTRY_COMPARE(server.errorCodeReports, 10);
|
||||
+}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QHttpNetworkConnection)
|
||||
--
|
||||
1.9.1
|
||||
@@ -0,0 +1,113 @@
|
||||
From c056e63cea1915667997c982f48296ce5acdcc80 Mon Sep 17 00:00:00 2001
|
||||
From: Lorn Potter <lorn.potter@gmail.com>
|
||||
Date: Tue, 2 Jun 2015 13:22:23 +1000
|
||||
Subject: [PATCH] Make sure to report correct NetworkAccessibility
|
||||
|
||||
Task-number: QTBUG-46323
|
||||
Change-Id: Ibdeb3280091a97d785d4314340678a63e88fb219
|
||||
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
|
||||
---
|
||||
src/network/access/qnetworkaccessmanager.cpp | 25 +++++++++++++++++--------
|
||||
src/network/access/qnetworkaccessmanager_p.h | 2 ++
|
||||
2 files changed, 19 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
|
||||
index e878feb..84931cb 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager.cpp
|
||||
+++ b/src/network/access/qnetworkaccessmanager.cpp
|
||||
@@ -472,11 +472,11 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
||||
// the QNetworkSession's signals
|
||||
connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
|
||||
SLOT(_q_onlineStateChanged(bool)));
|
||||
- // we would need all active configurations to check for
|
||||
- // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
- // and potentially expensive. We can just check the configuration here
|
||||
- d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
}
|
||||
+ // we would need all active configurations to check for
|
||||
+ // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
+ // and potentially expensive. We can just check the configuration here
|
||||
+ d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -946,6 +946,7 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
|
||||
void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
+ d->defaultAccessControl = false;
|
||||
|
||||
if (d->networkAccessible != accessible) {
|
||||
NetworkAccessibility previous = networkAccessible();
|
||||
@@ -964,7 +965,6 @@ void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkA
|
||||
QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
-
|
||||
if (d->networkSessionRequired) {
|
||||
QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
|
||||
if (networkSession) {
|
||||
@@ -975,7 +975,13 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess
|
||||
return NotAccessible;
|
||||
} else {
|
||||
// Network accessibility is either disabled or unknown.
|
||||
- return (d->networkAccessible == NotAccessible) ? NotAccessible : UnknownAccessibility;
|
||||
+ if (d->defaultAccessControl) {
|
||||
+ if (d->online)
|
||||
+ return d->networkAccessible;
|
||||
+ else
|
||||
+ return NotAccessible;
|
||||
+ }
|
||||
+ return (d->networkAccessible);
|
||||
}
|
||||
} else {
|
||||
if (d->online)
|
||||
@@ -1568,7 +1574,7 @@ void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &co
|
||||
if (!networkSessionStrongRef) {
|
||||
online = false;
|
||||
|
||||
- if (networkAccessible == QNetworkAccessManager::NotAccessible)
|
||||
+ if (networkAccessible == QNetworkAccessManager::NotAccessible || !online)
|
||||
emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
|
||||
else
|
||||
emit q->networkAccessibleChanged(QNetworkAccessManager::UnknownAccessibility);
|
||||
@@ -1616,11 +1622,14 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
|
||||
if (online) {
|
||||
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
|
||||
online = false;
|
||||
- emit q->networkAccessibleChanged(QNetworkAccessManager::NotAccessible);
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
}
|
||||
} else {
|
||||
if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
|
||||
online = true;
|
||||
+ if (defaultAccessControl)
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
emit q->networkAccessibleChanged(networkAccessible);
|
||||
}
|
||||
}
|
||||
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
|
||||
index f513324..c715da0 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager_p.h
|
||||
+++ b/src/network/access/qnetworkaccessmanager_p.h
|
||||
@@ -84,6 +84,7 @@ public:
|
||||
initializeSession(true),
|
||||
#endif
|
||||
cookieJarCreated(false),
|
||||
+ defaultAccessControl(true),
|
||||
authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
|
||||
{ }
|
||||
~QNetworkAccessManagerPrivate();
|
||||
@@ -164,6 +165,7 @@ public:
|
||||
#endif
|
||||
|
||||
bool cookieJarCreated;
|
||||
+ bool defaultAccessControl;
|
||||
|
||||
// The cache with authorization data:
|
||||
QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
From bb281eea179d50a413f4ec1ff172d27ee48d3a41 Mon Sep 17 00:00:00 2001
|
||||
From: Lorn Potter <lorn.potter@gmail.com>
|
||||
Date: Fri, 17 Jul 2015 15:32:23 +1000
|
||||
Subject: [PATCH] Make sure networkAccessibilityChanged is emitted
|
||||
|
||||
Task-number: QTBUG-46323
|
||||
Change-Id: I8297072b62763136f457ca6ae15282d1c22244f4
|
||||
Reviewed-by: Timo Jyrinki <timo.jyrinki@canonical.com>
|
||||
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
|
||||
---
|
||||
src/network/access/qnetworkaccessmanager.cpp | 70 +++++++++++++++-------
|
||||
src/network/access/qnetworkaccessmanager_p.h | 14 ++++-
|
||||
.../tst_qnetworkaccessmanager.cpp | 31 +++++-----
|
||||
3 files changed, 77 insertions(+), 38 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
|
||||
index 84931cb..f9e9513 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager.cpp
|
||||
+++ b/src/network/access/qnetworkaccessmanager.cpp
|
||||
@@ -278,7 +278,8 @@ static void ensureInitialized()
|
||||
|
||||
\snippet code/src_network_access_qnetworkaccessmanager.cpp 4
|
||||
|
||||
- Network requests can be reenabled again by calling
|
||||
+ Network requests can be re-enabled again, and this property will resume to
|
||||
+ reflect the actual device state by calling
|
||||
|
||||
\snippet code/src_network_access_qnetworkaccessmanager.cpp 5
|
||||
|
||||
@@ -467,16 +468,12 @@ QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
||||
qRegisterMetaType<QSharedPointer<char> >();
|
||||
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
- if (!d->networkSessionRequired) {
|
||||
- // if a session is required, we track online state through
|
||||
- // the QNetworkSession's signals
|
||||
- connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
|
||||
- SLOT(_q_onlineStateChanged(bool)));
|
||||
- }
|
||||
- // we would need all active configurations to check for
|
||||
- // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
- // and potentially expensive. We can just check the configuration here
|
||||
- d->online = (d->networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
+ // if a session is required, we track online state through
|
||||
+ // the QNetworkSession's signals if a request is already made.
|
||||
+ // we need to track current accessibility state by default
|
||||
+ //
|
||||
+ connect(&d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)),
|
||||
+ SLOT(_q_onlineStateChanged(bool)));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -946,7 +943,8 @@ QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const
|
||||
void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
- d->defaultAccessControl = false;
|
||||
+
|
||||
+ d->defaultAccessControl = accessible == NotAccessible ? false : true;
|
||||
|
||||
if (d->networkAccessible != accessible) {
|
||||
NetworkAccessibility previous = networkAccessible();
|
||||
@@ -965,6 +963,10 @@ void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkA
|
||||
QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
+
|
||||
+ if (d->networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined))
|
||||
+ return UnknownAccessibility;
|
||||
+
|
||||
if (d->networkSessionRequired) {
|
||||
QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession());
|
||||
if (networkSession) {
|
||||
@@ -1622,32 +1624,56 @@ void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession
|
||||
if (online) {
|
||||
if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) {
|
||||
online = false;
|
||||
- networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
- emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ if (networkAccessible != QNetworkAccessManager::NotAccessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
}
|
||||
} else {
|
||||
if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) {
|
||||
online = true;
|
||||
if (defaultAccessControl)
|
||||
- networkAccessible = QNetworkAccessManager::Accessible;
|
||||
- emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ if (networkAccessible != QNetworkAccessManager::Accessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline)
|
||||
{
|
||||
- // if the user set a config, we only care whether this one is active.
|
||||
+ Q_Q(QNetworkAccessManager);
|
||||
+ // if the user set a config, we only care whether this one is active.
|
||||
// Otherwise, this QNAM is online if there is an online config.
|
||||
if (customNetworkConfiguration) {
|
||||
online = (networkConfiguration.state() & QNetworkConfiguration::Active);
|
||||
} else {
|
||||
- if (isOnline && online != isOnline) {
|
||||
- networkSessionStrongRef.clear();
|
||||
- networkSessionWeakRef.clear();
|
||||
+ if (online != isOnline) {
|
||||
+ if (isOnline) {
|
||||
+ networkSessionStrongRef.clear();
|
||||
+ networkSessionWeakRef.clear();
|
||||
+ }
|
||||
+ online = isOnline;
|
||||
+ }
|
||||
+ }
|
||||
+ if (online) {
|
||||
+ if (defaultAccessControl) {
|
||||
+ if (networkAccessible != QNetworkAccessManager::Accessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
+ }
|
||||
+ } else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined)) {
|
||||
+ if (networkAccessible != QNetworkAccessManager::UnknownAccessibility) {
|
||||
+ networkAccessible = QNetworkAccessManager::UnknownAccessibility;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
+ }
|
||||
+ } else {
|
||||
+ if (networkAccessible != QNetworkAccessManager::NotAccessible) {
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+ emit q->networkAccessibleChanged(networkAccessible);
|
||||
}
|
||||
-
|
||||
- online = isOnline;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
|
||||
index c715da0..54ae114 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager_p.h
|
||||
+++ b/src/network/access/qnetworkaccessmanager_p.h
|
||||
@@ -78,7 +78,6 @@ public:
|
||||
customNetworkConfiguration(false),
|
||||
networkSessionRequired(networkConfigurationManager.capabilities()
|
||||
& QNetworkConfigurationManager::NetworkSessionRequired),
|
||||
- networkAccessible(QNetworkAccessManager::Accessible),
|
||||
activeReplyCount(0),
|
||||
online(false),
|
||||
initializeSession(true),
|
||||
@@ -86,7 +85,18 @@ public:
|
||||
cookieJarCreated(false),
|
||||
defaultAccessControl(true),
|
||||
authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
|
||||
- { }
|
||||
+ {
|
||||
+#ifndef QT_NO_BEARERMANAGEMENT
|
||||
+ // we would need all active configurations to check for
|
||||
+ // d->networkConfigurationManager.isOnline(), which is asynchronous
|
||||
+ // and potentially expensive. We can just check the configuration here
|
||||
+ online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active));
|
||||
+ if (online)
|
||||
+ networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ else
|
||||
+ networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
+#endif
|
||||
+ }
|
||||
~QNetworkAccessManagerPrivate();
|
||||
|
||||
void _q_replyFinished();
|
||||
diff --git a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
|
||||
index b4e4b9c..8ecb57d 100644
|
||||
--- a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
|
||||
+++ b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
|
||||
@@ -74,6 +74,10 @@ void tst_QNetworkAccessManager::networkAccessible()
|
||||
// if there is no session, we cannot know in which state we are in
|
||||
QNetworkAccessManager::NetworkAccessibility initialAccessibility =
|
||||
manager.networkAccessible();
|
||||
+
|
||||
+ if (initialAccessibility == QNetworkAccessManager::UnknownAccessibility)
|
||||
+ QSKIP("Unknown accessibility", SkipAll);
|
||||
+
|
||||
QCOMPARE(manager.networkAccessible(), initialAccessibility);
|
||||
|
||||
manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible);
|
||||
@@ -94,29 +98,28 @@ void tst_QNetworkAccessManager::networkAccessible()
|
||||
QCOMPARE(manager.networkAccessible(), initialAccessibility);
|
||||
|
||||
QNetworkConfigurationManager configManager;
|
||||
- bool sessionRequired = (configManager.capabilities()
|
||||
- & QNetworkConfigurationManager::NetworkSessionRequired);
|
||||
QNetworkConfiguration defaultConfig = configManager.defaultConfiguration();
|
||||
if (defaultConfig.isValid()) {
|
||||
manager.setConfiguration(defaultConfig);
|
||||
|
||||
- // the accessibility has not changed if no session is required
|
||||
- if (sessionRequired) {
|
||||
+ QCOMPARE(spy.count(), 0);
|
||||
+
|
||||
+ if (defaultConfig.state().testFlag(QNetworkConfiguration::Active))
|
||||
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible);
|
||||
+ else
|
||||
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
|
||||
+
|
||||
+ manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible);
|
||||
+
|
||||
+ if (defaultConfig.state().testFlag(QNetworkConfiguration::Active)) {
|
||||
QCOMPARE(spy.count(), 1);
|
||||
- QCOMPARE(spy.takeFirst().at(0).value<QNetworkAccessManager::NetworkAccessibility>(),
|
||||
- QNetworkAccessManager::Accessible);
|
||||
+ QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()),
|
||||
+ QNetworkAccessManager::NotAccessible);
|
||||
} else {
|
||||
QCOMPARE(spy.count(), 0);
|
||||
}
|
||||
- QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::Accessible);
|
||||
-
|
||||
- manager.setNetworkAccessible(QNetworkAccessManager::NotAccessible);
|
||||
-
|
||||
- QCOMPARE(spy.count(), 1);
|
||||
- QCOMPARE(QNetworkAccessManager::NetworkAccessibility(spy.takeFirst().at(0).toInt()),
|
||||
- QNetworkAccessManager::NotAccessible);
|
||||
- QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
|
||||
}
|
||||
+ QCOMPARE(manager.networkAccessible(), QNetworkAccessManager::NotAccessible);
|
||||
#endif
|
||||
}
|
||||
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
From e996d68f6130847637ba287518cff1289cfa48e5 Mon Sep 17 00:00:00 2001
|
||||
From: Lorn Potter <lorn.potter@gmail.com>
|
||||
Date: Fri, 6 Nov 2015 14:22:44 +1000
|
||||
Subject: [PATCH] Make UnknownAccessibility not block requests
|
||||
|
||||
This allows requests to proceed without needing bearer plugins.
|
||||
|
||||
Task-number: QTBUG-49267
|
||||
Change-Id: Ie5ce188ddefebd14d666bb5846e8f93ee2925ed1
|
||||
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
---
|
||||
src/network/access/qnetworkaccessmanager.cpp | 3 +--
|
||||
src/network/access/qnetworkaccessmanager_p.h | 2 ++
|
||||
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
|
||||
index 086140f..0e5870a 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager.cpp
|
||||
+++ b/src/network/access/qnetworkaccessmanager.cpp
|
||||
@@ -976,7 +976,6 @@ QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccess
|
||||
else
|
||||
return NotAccessible;
|
||||
} else {
|
||||
- // Network accessibility is either disabled or unknown.
|
||||
if (d->defaultAccessControl) {
|
||||
if (d->online)
|
||||
return d->networkAccessible;
|
||||
@@ -1161,7 +1160,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
// Return a disabled network reply if network access is disabled.
|
||||
// Except if the scheme is empty or file://.
|
||||
- if (!d->networkAccessible && !isLocalFile) {
|
||||
+ if (d->networkAccessible == NotAccessible && !isLocalFile) {
|
||||
return new QDisabledNetworkReply(this, req, op);
|
||||
}
|
||||
|
||||
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
|
||||
index 54ae114..3fc33b5 100644
|
||||
--- a/src/network/access/qnetworkaccessmanager_p.h
|
||||
+++ b/src/network/access/qnetworkaccessmanager_p.h
|
||||
@@ -93,6 +93,8 @@ public:
|
||||
online = (networkConfiguration.state().testFlag(QNetworkConfiguration::Active));
|
||||
if (online)
|
||||
networkAccessible = QNetworkAccessManager::Accessible;
|
||||
+ else if (networkConfiguration.state().testFlag(QNetworkConfiguration::Undefined))
|
||||
+ networkAccessible = QNetworkAccessManager::UnknownAccessibility;
|
||||
else
|
||||
networkAccessible = QNetworkAccessManager::NotAccessible;
|
||||
#endif
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
From 06818f6d1c602aa3c4f9356324866432d2dd0195 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Mon, 16 Nov 2015 15:02:37 +0100
|
||||
Subject: [PATCH 1/2] Remove legacy platform code in QSslSocket for OS X < 10.5
|
||||
|
||||
This avoids manual symbol lookups and makes the code more readable.
|
||||
Mark identical code.
|
||||
|
||||
Also use smart pointers instead of manual memory management.
|
||||
|
||||
(Backport of d42d7781f1cd62c3c7c008859507f24a1ff5bb2a to Qt 5.4)
|
||||
|
||||
Change-Id: I62820313dce87de6623cdc87b6e1361200ed7822
|
||||
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
|
||||
Conflicts:
|
||||
src/network/ssl/qsslsocket_openssl.cpp
|
||||
---
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 83 +++++++++++-----------------------
|
||||
src/network/ssl/qsslsocket_p.h | 6 +--
|
||||
2 files changed, 28 insertions(+), 61 deletions(-)
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index 13fc534..7d0fe00 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -69,14 +69,19 @@
|
||||
#include <QtCore/qvarlengtharray.h>
|
||||
#include <QLibrary> // for loading the security lib for the CA store
|
||||
|
||||
+#include <string.h>
|
||||
+
|
||||
+#ifdef Q_OS_DARWIN
|
||||
+# include <private/qcore_mac_p.h>
|
||||
+#endif
|
||||
+
|
||||
+#ifdef Q_OS_OSX
|
||||
+# include <Security/Security.h>
|
||||
+#endif
|
||||
+
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
-#if defined(Q_OS_MACX)
|
||||
-#define kSecTrustSettingsDomainSystem 2 // so we do not need to include the header file
|
||||
- PtrSecCertificateCopyData QSslSocketPrivate::ptrSecCertificateCopyData = 0;
|
||||
- PtrSecTrustSettingsCopyCertificates QSslSocketPrivate::ptrSecTrustSettingsCopyCertificates = 0;
|
||||
- PtrSecTrustCopyAnchorCertificates QSslSocketPrivate::ptrSecTrustCopyAnchorCertificates = 0;
|
||||
-#elif defined(Q_OS_WIN)
|
||||
+#if defined(Q_OS_WIN)
|
||||
PtrCertOpenSystemStoreW QSslSocketPrivate::ptrCertOpenSystemStoreW = 0;
|
||||
PtrCertFindCertificateInStore QSslSocketPrivate::ptrCertFindCertificateInStore = 0;
|
||||
PtrCertCloseStore QSslSocketPrivate::ptrCertCloseStore = 0;
|
||||
@@ -482,23 +487,7 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
|
||||
|
||||
#ifndef QT_NO_LIBRARY
|
||||
//load symbols needed to receive certificates from system store
|
||||
-#if defined(Q_OS_MACX)
|
||||
- QLibrary securityLib("/System/Library/Frameworks/Security.framework/Versions/Current/Security");
|
||||
- if (securityLib.load()) {
|
||||
- ptrSecCertificateCopyData = (PtrSecCertificateCopyData) securityLib.resolve("SecCertificateCopyData");
|
||||
- if (!ptrSecCertificateCopyData)
|
||||
- qWarning("could not resolve symbols in security library"); // should never happen
|
||||
-
|
||||
- ptrSecTrustSettingsCopyCertificates = (PtrSecTrustSettingsCopyCertificates) securityLib.resolve("SecTrustSettingsCopyCertificates");
|
||||
- if (!ptrSecTrustSettingsCopyCertificates) { // method was introduced in Leopard, use legacy method if it's not there
|
||||
- ptrSecTrustCopyAnchorCertificates = (PtrSecTrustCopyAnchorCertificates) securityLib.resolve("SecTrustCopyAnchorCertificates");
|
||||
- if (!ptrSecTrustCopyAnchorCertificates)
|
||||
- qWarning("could not resolve symbols in security library"); // should never happen
|
||||
- }
|
||||
- } else {
|
||||
- qWarning("could not load security library");
|
||||
- }
|
||||
-#elif defined(Q_OS_WIN)
|
||||
+#if defined(Q_OS_WIN)
|
||||
HINSTANCE hLib = LoadLibraryW(L"Crypt32");
|
||||
if (hLib) {
|
||||
#if defined(Q_OS_WINCE)
|
||||
@@ -635,40 +624,22 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
timer.start();
|
||||
#endif
|
||||
QList<QSslCertificate> systemCerts;
|
||||
-#if defined(Q_OS_MACX)
|
||||
- CFArrayRef cfCerts;
|
||||
- OSStatus status = 1;
|
||||
-
|
||||
- CFDataRef SecCertificateCopyData (
|
||||
- SecCertificateRef certificate
|
||||
- );
|
||||
-
|
||||
- if (ptrSecCertificateCopyData) {
|
||||
- if (ptrSecTrustSettingsCopyCertificates)
|
||||
- status = ptrSecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
|
||||
- else if (ptrSecTrustCopyAnchorCertificates)
|
||||
- status = ptrSecTrustCopyAnchorCertificates(&cfCerts);
|
||||
- if (!status) {
|
||||
- CFIndex size = CFArrayGetCount(cfCerts);
|
||||
- for (CFIndex i = 0; i < size; ++i) {
|
||||
- SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
|
||||
- CFDataRef data;
|
||||
-
|
||||
- data = ptrSecCertificateCopyData(cfCert);
|
||||
-
|
||||
- if (data == NULL) {
|
||||
- qWarning("error retrieving a CA certificate from the system store");
|
||||
- } else {
|
||||
- QByteArray rawCert = QByteArray::fromRawData((const char *)CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
- systemCerts.append(QSslCertificate::fromData(rawCert, QSsl::Der));
|
||||
- CFRelease(data);
|
||||
- }
|
||||
+ // note: also check implementation in openssl_mac.cpp
|
||||
+#if defined(Q_OS_OSX)
|
||||
+ // SecTrustSettingsCopyCertificates is not defined on iOS.
|
||||
+ QCFType<CFArrayRef> cfCerts;
|
||||
+
|
||||
+ OSStatus status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
|
||||
+ if (status == noErr ) {
|
||||
+ const CFIndex size = CFArrayGetCount(cfCerts);
|
||||
+ for (CFIndex i = 0; i < size; ++i) {
|
||||
+ SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
|
||||
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
|
||||
+ if (derData == NULL) {
|
||||
+ qWarning("error retrieving a CA certificate from the system store");
|
||||
+ } else {
|
||||
+ systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
|
||||
}
|
||||
- CFRelease(cfCerts);
|
||||
- }
|
||||
- else {
|
||||
- // no detailed error handling here
|
||||
- qWarning("could not retrieve system CA certificates");
|
||||
}
|
||||
}
|
||||
#elif defined(Q_OS_WIN)
|
||||
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
|
||||
index 6e7a2c5..c1a6f05 100644
|
||||
--- a/src/network/ssl/qsslsocket_p.h
|
||||
+++ b/src/network/ssl/qsslsocket_p.h
|
||||
@@ -145,11 +145,7 @@ public:
|
||||
static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName);
|
||||
Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname);
|
||||
|
||||
-#if defined(Q_OS_MACX)
|
||||
- static PtrSecCertificateCopyData ptrSecCertificateCopyData;
|
||||
- static PtrSecTrustSettingsCopyCertificates ptrSecTrustSettingsCopyCertificates;
|
||||
- static PtrSecTrustCopyAnchorCertificates ptrSecTrustCopyAnchorCertificates;
|
||||
-#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||
+#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||
static PtrCertOpenSystemStoreW ptrCertOpenSystemStoreW;
|
||||
static PtrCertFindCertificateInStore ptrCertFindCertificateInStore;
|
||||
static PtrCertCloseStore ptrCertCloseStore;
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
From 6b9366e7748857f14d5b0f92ced70c08ab5235b7 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <danimo@owncloud.com>
|
||||
Date: Wed, 25 Nov 2015 12:37:27 +0100
|
||||
Subject: [PATCH 2/2] QSslSocket: evaluate CAs in all keychain categories
|
||||
|
||||
This will make sure that certs in the domainUser (login),
|
||||
and domainAdmin (per machine) keychain are being picked up
|
||||
in systemCaCertificates() in addition to the (usually immutable)
|
||||
DomainSystem keychain.
|
||||
|
||||
Also consider the trust settings on OS X: If a certificate
|
||||
is either fully trusted or trusted for the purpose of SSL,
|
||||
it will be accepted.
|
||||
|
||||
[ChangeLog][Platform Specific Changes] OS X now accepts trusted
|
||||
certificates from the login and system keychains.
|
||||
|
||||
(Backport of fe3a84138e266c425f11353f7d8dc28a588af89e to Qt 5.4)
|
||||
|
||||
Task-number: QTBUG-32898
|
||||
Change-Id: Ia23083d5af74388eeee31ba07239735cbbe64368
|
||||
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
|
||||
---
|
||||
src/network/ssl/qsslsocket.cpp | 4 +
|
||||
src/network/ssl/qsslsocket_mac_shared.cpp | 148 ++++++++++++++++++++++++++++++
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 30 +-----
|
||||
src/network/ssl/ssl.pri | 4 +-
|
||||
4 files changed, 158 insertions(+), 28 deletions(-)
|
||||
create mode 100644 src/network/ssl/qsslsocket_mac_shared.cpp
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
|
||||
index 8887f47..6347c20 100644
|
||||
--- a/src/network/ssl/qsslsocket.cpp
|
||||
+++ b/src/network/ssl/qsslsocket.cpp
|
||||
@@ -1446,6 +1446,10 @@ QList<QSslCertificate> QSslSocket::defaultCaCertificates()
|
||||
returned by defaultCaCertificates(). You can replace that database
|
||||
with your own with setDefaultCaCertificates().
|
||||
|
||||
+ \note: On OS X, only certificates that are either trusted for all
|
||||
+ purposes or trusted for the purpose of SSL in the keychain will be
|
||||
+ returned.
|
||||
+
|
||||
\sa caCertificates(), defaultCaCertificates(), setDefaultCaCertificates()
|
||||
*/
|
||||
QList<QSslCertificate> QSslSocket::systemCaCertificates()
|
||||
diff --git a/src/network/ssl/qsslsocket_mac_shared.cpp b/src/network/ssl/qsslsocket_mac_shared.cpp
|
||||
new file mode 100644
|
||||
index 0000000..60fea4c
|
||||
--- /dev/null
|
||||
+++ b/src/network/ssl/qsslsocket_mac_shared.cpp
|
||||
@@ -0,0 +1,148 @@
|
||||
+/****************************************************************************
|
||||
+**
|
||||
+** Copyright (C) 2015 The Qt Company Ltd.
|
||||
+** Copyright (C) 2015 ownCloud Inc
|
||||
+** Contact: http://www.qt.io/licensing/
|
||||
+**
|
||||
+** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
+**
|
||||
+** $QT_BEGIN_LICENSE:LGPL21$
|
||||
+** Commercial License Usage
|
||||
+** Licensees holding valid commercial Qt licenses may use this file in
|
||||
+** accordance with the commercial license agreement provided with the
|
||||
+** Software or, alternatively, in accordance with the terms contained in
|
||||
+** a written agreement between you and The Qt Company. For licensing terms
|
||||
+** and conditions see http://www.qt.io/terms-conditions. For further
|
||||
+** information use the contact form at http://www.qt.io/contact-us.
|
||||
+**
|
||||
+** GNU Lesser General Public License Usage
|
||||
+** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
+** General Public License version 2.1 or version 3 as published by the Free
|
||||
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
||||
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
||||
+** following information to ensure the GNU Lesser General Public License
|
||||
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
||||
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
+**
|
||||
+** As a special exception, The Qt Company gives you certain additional
|
||||
+** rights. These rights are described in The Qt Company LGPL Exception
|
||||
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
+**
|
||||
+** $QT_END_LICENSE$
|
||||
+**
|
||||
+****************************************************************************/
|
||||
+
|
||||
+//#define QSSLSOCKET_DEBUG
|
||||
+//#define QT_DECRYPT_SSL_TRAFFIC
|
||||
+
|
||||
+#include "qsslsocket.h"
|
||||
+
|
||||
+#ifndef QT_NO_OPENSSL
|
||||
+# include "qsslsocket_openssl_p.h"
|
||||
+# include "qsslsocket_openssl_symbols_p.h"
|
||||
+#endif
|
||||
+
|
||||
+#include "qsslcertificate_p.h"
|
||||
+
|
||||
+#ifdef Q_OS_DARWIN
|
||||
+# include <private/qcore_mac_p.h>
|
||||
+#endif
|
||||
+
|
||||
+#include <QtCore/qdebug.h>
|
||||
+
|
||||
+#ifdef Q_OS_OSX
|
||||
+# include <Security/Security.h>
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
+QT_BEGIN_NAMESPACE
|
||||
+
|
||||
+#ifdef Q_OS_OSX
|
||||
+namespace {
|
||||
+
|
||||
+bool hasTrustedSslServerPolicy(SecPolicyRef policy, CFDictionaryRef props) {
|
||||
+ QCFType<CFDictionaryRef> policyProps = SecPolicyCopyProperties(policy);
|
||||
+ // only accept certificates with policies for SSL server validation for now
|
||||
+ if (CFEqual(CFDictionaryGetValue(policyProps, kSecPolicyOid), kSecPolicyAppleSSL)) {
|
||||
+ CFBooleanRef policyClient;
|
||||
+ if (CFDictionaryGetValueIfPresent(policyProps, kSecPolicyClient, reinterpret_cast<const void**>(&policyClient)) &&
|
||||
+ CFEqual(policyClient, kCFBooleanTrue)) {
|
||||
+ return false; // no client certs
|
||||
+ }
|
||||
+ if (!CFDictionaryContainsKey(props, kSecTrustSettingsResult)) {
|
||||
+ // as per the docs, no trust settings result implies full trust
|
||||
+ return true;
|
||||
+ }
|
||||
+ CFNumberRef number = static_cast<CFNumberRef>(CFDictionaryGetValue(props, kSecTrustSettingsResult));
|
||||
+ SecTrustSettingsResult settingsResult;
|
||||
+ CFNumberGetValue(number, kCFNumberSInt32Type, &settingsResult);
|
||||
+ switch (settingsResult) {
|
||||
+ case kSecTrustSettingsResultTrustRoot:
|
||||
+ case kSecTrustSettingsResultTrustAsRoot:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+bool isCaCertificateTrusted(SecCertificateRef cfCert, int domain)
|
||||
+{
|
||||
+ QCFType<CFArrayRef> cfTrustSettings;
|
||||
+ OSStatus status = SecTrustSettingsCopyTrustSettings(cfCert, domain, &cfTrustSettings);
|
||||
+ if (status == noErr) {
|
||||
+ CFIndex size = CFArrayGetCount(cfTrustSettings);
|
||||
+ // if empty, trust for everything (as per the Security Framework documentation)
|
||||
+ if (size == 0) {
|
||||
+ return true;
|
||||
+ } else {
|
||||
+ for (CFIndex i = 0; i < size; ++i) {
|
||||
+ CFDictionaryRef props = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(cfTrustSettings, i));
|
||||
+ if (CFDictionaryContainsKey(props, kSecTrustSettingsPolicy)) {
|
||||
+ if (hasTrustedSslServerPolicy((SecPolicyRef)CFDictionaryGetValue(props, kSecTrustSettingsPolicy), props))
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ qWarning("Error receiving trust for a CA certificate");
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+} // anon namespace
|
||||
+#endif // Q_OS_OSX
|
||||
+
|
||||
+QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
+{
|
||||
+ ensureInitialized();
|
||||
+
|
||||
+ QList<QSslCertificate> systemCerts;
|
||||
+ // SecTrustSettingsCopyCertificates is not defined on iOS.
|
||||
+#ifdef Q_OS_OSX
|
||||
+ QCFType<CFArrayRef> cfCerts;
|
||||
+ // iterate through all enum members, order:
|
||||
+ // kSecTrustSettingsDomainUser, kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainSystem
|
||||
+ for (int dom = kSecTrustSettingsDomainUser; dom <= kSecTrustSettingsDomainSystem; dom++) {
|
||||
+ OSStatus status = SecTrustSettingsCopyCertificates(dom, &cfCerts);
|
||||
+ if (status == noErr) {
|
||||
+ const CFIndex size = CFArrayGetCount(cfCerts);
|
||||
+ for (CFIndex i = 0; i < size; ++i) {
|
||||
+ SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
|
||||
+ QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
|
||||
+ if (::isCaCertificateTrusted(cfCert, dom)) {
|
||||
+ if (derData == NULL) {
|
||||
+ qWarning("Error retrieving a CA certificate from the system store");
|
||||
+ } else {
|
||||
+ systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+#endif
|
||||
+ return systemCerts;
|
||||
+}
|
||||
+
|
||||
+QT_END_NAMESPACE
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index 7d0fe00..7415e32 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -71,14 +71,6 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
-#ifdef Q_OS_DARWIN
|
||||
-# include <private/qcore_mac_p.h>
|
||||
-#endif
|
||||
-
|
||||
-#ifdef Q_OS_OSX
|
||||
-# include <Security/Security.h>
|
||||
-#endif
|
||||
-
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
@@ -616,6 +608,7 @@ void QSslSocketPrivate::resetDefaultCiphers()
|
||||
setDefaultCiphers(defaultCiphers);
|
||||
}
|
||||
|
||||
+#ifndef Q_OS_DARWIN // Apple implementation in qsslsocket_mac_shared.cpp
|
||||
QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
{
|
||||
ensureInitialized();
|
||||
@@ -624,25 +617,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
timer.start();
|
||||
#endif
|
||||
QList<QSslCertificate> systemCerts;
|
||||
- // note: also check implementation in openssl_mac.cpp
|
||||
-#if defined(Q_OS_OSX)
|
||||
- // SecTrustSettingsCopyCertificates is not defined on iOS.
|
||||
- QCFType<CFArrayRef> cfCerts;
|
||||
-
|
||||
- OSStatus status = SecTrustSettingsCopyCertificates(kSecTrustSettingsDomainSystem, &cfCerts);
|
||||
- if (status == noErr ) {
|
||||
- const CFIndex size = CFArrayGetCount(cfCerts);
|
||||
- for (CFIndex i = 0; i < size; ++i) {
|
||||
- SecCertificateRef cfCert = (SecCertificateRef)CFArrayGetValueAtIndex(cfCerts, i);
|
||||
- QCFType<CFDataRef> derData = SecCertificateCopyData(cfCert);
|
||||
- if (derData == NULL) {
|
||||
- qWarning("error retrieving a CA certificate from the system store");
|
||||
- } else {
|
||||
- systemCerts << QSslCertificate(QByteArray::fromCFData(derData), QSsl::Der);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-#elif defined(Q_OS_WIN)
|
||||
+#if defined(Q_OS_WIN)
|
||||
if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
|
||||
HCERTSTORE hSystemStore;
|
||||
#if defined(Q_OS_WINCE)
|
||||
@@ -719,6 +694,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
|
||||
|
||||
return systemCerts;
|
||||
}
|
||||
+#endif // Q_OS_DARWIN
|
||||
|
||||
void QSslSocketBackendPrivate::startClientEncryption()
|
||||
{
|
||||
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
|
||||
index 384e149..9546f18 100644
|
||||
--- a/src/network/ssl/ssl.pri
|
||||
+++ b/src/network/ssl/ssl.pri
|
||||
@@ -45,7 +45,9 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
|
||||
ssl/qsslsocket_openssl.cpp \
|
||||
ssl/qsslsocket_openssl_symbols.cpp
|
||||
|
||||
-android:!android-no-sdk: SOURCES += ssl/qsslsocket_openssl_android.cpp
|
||||
+ darwin:SOURCES += ssl/qsslsocket_mac_shared.cpp
|
||||
+
|
||||
+ android:!android-no-sdk: SOURCES += ssl/qsslsocket_openssl_android.cpp
|
||||
|
||||
# Add optional SSL libs
|
||||
# Static linking of OpenSSL with msvc:
|
||||
--
|
||||
1.9.1
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
From ae9d3f4c6c1a732788cd1f24c6a928cee16c3991 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Tue, 27 Jan 2015 16:58:32 +0100
|
||||
Subject: [PATCH] Win32: Re-init system proxy if internet settings change
|
||||
|
||||
Because Proxy Auto Configuration performs DNS lookups,
|
||||
the proxy settings are being cached. For long-running
|
||||
programs this means that once users switch e.g. from or
|
||||
to company networks with a proxy, they instantly will
|
||||
lose connectivity because we cache the old setting.
|
||||
|
||||
To remedy this, we monitor the Registry (locations
|
||||
courtesy of Chromium's platform support) for changes
|
||||
in its settings, and requery for the current proxy in
|
||||
that case.
|
||||
|
||||
Task-number: QTBUG-3470
|
||||
Task-number: QTBUG-29990
|
||||
Change-Id: Id25a51387bcd232c5f879cea0371038986d0e2de
|
||||
Reviewed-by: Oliver Wolff <oliver.wolff@theqtcompany.com>
|
||||
---
|
||||
src/network/kernel/qnetworkproxy_win.cpp | 86 +++++++++++++++++++++++++++++++-
|
||||
1 file changed, 84 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
|
||||
index da2c020..f7741ce 100644
|
||||
--- a/src/network/kernel/qnetworkproxy_win.cpp
|
||||
+++ b/src/network/kernel/qnetworkproxy_win.cpp
|
||||
@@ -345,12 +345,66 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
|
||||
return removeDuplicateProxies(result);
|
||||
}
|
||||
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+namespace {
|
||||
+class QRegistryWatcher {
|
||||
+public:
|
||||
+ void addLocation(HKEY hive, const QString& path)
|
||||
+ {
|
||||
+ HKEY openedKey;
|
||||
+ if (RegOpenKeyEx(hive, reinterpret_cast<const wchar_t*>(path.utf16()), 0, KEY_READ, &openedKey) != ERROR_SUCCESS)
|
||||
+ return;
|
||||
+
|
||||
+ const DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
+ REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;
|
||||
+
|
||||
+ // Watch the registry key for a change of value.
|
||||
+ HANDLE handle = CreateEvent(NULL, true, false, NULL);
|
||||
+ if (RegNotifyChangeKeyValue(openedKey, true, filter, handle, true) != ERROR_SUCCESS) {
|
||||
+ CloseHandle(handle);
|
||||
+ return;
|
||||
+ }
|
||||
+ m_watchEvents.append(handle);
|
||||
+ m_registryHandles.append(openedKey);
|
||||
+ }
|
||||
+
|
||||
+ bool hasChanged() const {
|
||||
+ return !isEmpty() &&
|
||||
+ WaitForMultipleObjects(m_watchEvents.size(), m_watchEvents.data(), false, 0) < WAIT_OBJECT_0 + m_watchEvents.size();
|
||||
+ }
|
||||
+
|
||||
+ bool isEmpty() const {
|
||||
+ return m_watchEvents.isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ void clear() {
|
||||
+ foreach (HANDLE event, m_watchEvents)
|
||||
+ CloseHandle(event);
|
||||
+ foreach (HKEY key, m_registryHandles)
|
||||
+ RegCloseKey(key);
|
||||
+
|
||||
+ m_watchEvents.clear();
|
||||
+ m_registryHandles.clear();
|
||||
+ }
|
||||
+
|
||||
+ ~QRegistryWatcher() {
|
||||
+ clear();
|
||||
+ }
|
||||
+
|
||||
+private:
|
||||
+ QVector<HANDLE> m_watchEvents;
|
||||
+ QVector<HKEY> m_registryHandles;
|
||||
+};
|
||||
+} // namespace
|
||||
+#endif // !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+
|
||||
class QWindowsSystemProxy
|
||||
{
|
||||
public:
|
||||
QWindowsSystemProxy();
|
||||
~QWindowsSystemProxy();
|
||||
void init();
|
||||
+ void reset();
|
||||
|
||||
QMutex mutex;
|
||||
|
||||
@@ -361,7 +415,9 @@ public:
|
||||
QStringList proxyServerList;
|
||||
QStringList proxyBypass;
|
||||
QList<QNetworkProxy> defaultResult;
|
||||
-
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+ QRegistryWatcher proxySettingsWatcher;
|
||||
+#endif
|
||||
bool initialized;
|
||||
bool functional;
|
||||
bool isAutoConfig;
|
||||
@@ -381,16 +437,42 @@ QWindowsSystemProxy::~QWindowsSystemProxy()
|
||||
ptrWinHttpCloseHandle(hHttpSession);
|
||||
}
|
||||
|
||||
+void QWindowsSystemProxy::reset()
|
||||
+{
|
||||
+ autoConfigUrl.clear();
|
||||
+ proxyServerList.clear();
|
||||
+ proxyBypass.clear();
|
||||
+ defaultResult.clear();
|
||||
+ defaultResult << QNetworkProxy::NoProxy;
|
||||
+ functional = false;
|
||||
+ isAutoConfig = false;
|
||||
+}
|
||||
+
|
||||
void QWindowsSystemProxy::init()
|
||||
{
|
||||
- if (initialized)
|
||||
+ bool proxySettingsChanged = false;
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+ proxySettingsChanged = proxySettingsWatcher.hasChanged();
|
||||
+#endif
|
||||
+
|
||||
+ if (initialized && !proxySettingsChanged)
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
+ reset();
|
||||
+
|
||||
#ifdef Q_OS_WINCE
|
||||
// Windows CE does not have any of the following API
|
||||
return;
|
||||
#else
|
||||
+
|
||||
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
|
||||
+ proxySettingsWatcher.clear(); // needs reset to trigger a new detection
|
||||
+ proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
|
||||
+ proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
|
||||
+ proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Policies\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
|
||||
+#endif
|
||||
+
|
||||
// load the winhttp.dll library
|
||||
QSystemLibrary lib(L"winhttp");
|
||||
if (!lib.load())
|
||||
--
|
||||
1.9.1
|
||||
@@ -0,0 +1,32 @@
|
||||
From c1a67e7dc3a6f8876efa32cdbabbfde1c5a37bc6 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Molkentin <daniel@molkentin.de>
|
||||
Date: Tue, 31 Mar 2015 17:43:44 +0200
|
||||
Subject: [PATCH] Windows: Do not crash if SSL context is gone after root cert
|
||||
lookup
|
||||
|
||||
On Windows, we perform an extra certificate lookup for root CAs that
|
||||
are not in Windows' (minimal) root store. This check can take up to
|
||||
15 seconds. The SSL context can already be gone once we return. Hence
|
||||
we now check for a non-null SSL context on Windows before proceeding.
|
||||
|
||||
Change-Id: I1951569d9b17da33fa604f7c9d8b33255acf200d
|
||||
Reviewed-by: Richard J. Moore <rich@kde.org>
|
||||
---
|
||||
src/network/ssl/qsslsocket_openssl.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
index 0e1a3e5..b132aec 100644
|
||||
--- a/src/network/ssl/qsslsocket_openssl.cpp
|
||||
+++ b/src/network/ssl/qsslsocket_openssl.cpp
|
||||
@@ -1281,7 +1281,7 @@ void QSslSocketBackendPrivate::_q_caRootLoaded(QSslCertificate cert, QSslCertifi
|
||||
if (plainSocket)
|
||||
plainSocket->resume();
|
||||
paused = false;
|
||||
- if (checkSslErrors())
|
||||
+ if (checkSslErrors() && ssl)
|
||||
continueHandshake();
|
||||
}
|
||||
|
||||
--
|
||||
1.9.1
|
||||
@@ -0,0 +1,46 @@
|
||||
## Patches used
|
||||
|
||||
There are our patches on top of Qt 5.4.0, which we are currently
|
||||
using for our binary packages on Windows and Mac OS. Most of them
|
||||
have been sent upstream and are part of newer Qt releases.
|
||||
|
||||
All changes are designed to up upstream, and all those that are
|
||||
special hacks to Qt will bear a NOUPSTREAM in their name
|
||||
|
||||
The git-style numeration is ordered by order of creation, their
|
||||
purpose is outlined in each patches' front matter.
|
||||
|
||||
### Part of Qt v5.4.1 and later
|
||||
* 0001-Fix-crash-on-Mac-OS-if-PAC-URL-contains-non-URL-lega.patch
|
||||
* 0002-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
|
||||
* 0003-Fix-crash-if-PAC-script-retrieval-returns-a-null-CFD.patch
|
||||
|
||||
### Part of Qt v5.4.2 and later
|
||||
* 0004-Cocoa-Fix-systray-SVG-icons.patch
|
||||
* 0005-OSX-Fix-disapearing-tray-icon.patch
|
||||
* 0007-QNAM-Fix-upload-corruptions-when-server-closes-conne.patch
|
||||
* 0018-Windows-Do-not-crash-if-SSL-context-is-gone-after-ro.patch
|
||||
|
||||
### Part of Qt v5.5.0 and later
|
||||
* 0017-Win32-Re-init-system-proxy-if-internet-settings-chan.patch
|
||||
|
||||
### Part of Qt v5.5.1 and later
|
||||
* 0007-X-Network-Fix-up-previous-corruption-patch.patch
|
||||
* 0008-QNAM-Fix-reply-deadlocks-on-server-closing-connectio.patch
|
||||
* 0014-Fix-SNI-for-TlsV1_0OrLater-TlsV1_1OrLater-and-TlsV1_.patch
|
||||
|
||||
### Upstreamed but not in any release yet (as of 2015-11-16)
|
||||
* 0009-QNAM-Assign-proper-channel-before-sslErrors-emission.patch
|
||||
* 0010-Don-t-let-closed-http-sockets-pass-as-valid-connecti.patch
|
||||
* 0011-Make-sure-to-report-correct-NetworkAccessibility.patch
|
||||
* 0012-Make-sure-networkAccessibilityChanged-is-emitted.patch
|
||||
* 0013-Make-UnknownAccessibility-not-block-requests.patch
|
||||
* 0015-Remove-legacy-platform-code-in-QSslSocket-for-OS-X-1.patch
|
||||
* 0016-Fix-possible-crash-when-passing-an-invalid-PAC-URL.patch
|
||||
|
||||
### Not submitted to be part of any release:
|
||||
* 0006-Fix-force-debug-info-with-macx-clang_NOUPSTREAM.patch
|
||||
This is only needed if you intent to harvest debugging symbols
|
||||
for breakpad.
|
||||
|
||||
|
||||
@@ -32,7 +32,3 @@ SET(QT_MOC_EXECUTABLE ${MINGW_PREFIX}-moc)
|
||||
SET(QT_RCC_EXECUTABLE ${MINGW_PREFIX}-rcc)
|
||||
SET(QT_UIC_EXECUTABLE ${MINGW_PREFIX}-uic)
|
||||
SET(QT_LRELEASE_EXECUTABLE ${MINGW_PREFIX}-lrelease)
|
||||
|
||||
# neon config
|
||||
SET(NEON_CONFIG_EXECUTABLE ${CMAKE_FIND_ROOT_PATH}/bin/neon-config)
|
||||
# /usr/i686-w64-mingw32/sys-root/mingw/bin/neon-config
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
# Auto-generated - do not modify
|
||||
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Kiadási jegyzetek megtekintése"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "A következő folyamatot(okat) meg kell állítani ${APPLICATION_EXECUTABLE}.$\nSzeretné ha a telepítő program megállítani ezeket a folyamatokat?"
|
||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Folyamat ${APPLICATION_EXECUTABLE} kilövése."
|
||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Kilövésre szánt folyamat nem található."
|
||||
StrCpy $PageReinstall_NEW_Field_1 "Az ${APPLICATION_NAME} alkalmazás egy régebbi verziója telepítva van a rendszeren. Ajánlott a régi alkalmazás eltávolítása mielőtt a legfrissebb verziót telepítané. Válassza ki milyen műveletet szeretne végrehajtani, és nyomja meg a $\"Következő$\" gombot a folytatáshoz."
|
||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "${APPLICATION_EXECUTABLE} folyamat kilövése."
|
||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "A kilövésre szánt folyamat nem található!"
|
||||
StrCpy $PageReinstall_NEW_Field_1 "Az ${APPLICATION_NAME} alkalmazás egy régebbi verziója telepítve van a rendszeren. Ajánlott a régi alkalmazás eltávolítása mielőtt a legfrissebb verziót telepítené. Válassza ki milyen műveletet szeretne végrehajtani, és nyomja meg a $\"Következő$\" gombot a folytatáshoz."
|
||||
StrCpy $PageReinstall_NEW_Field_2 "Eltávolítás telepítés előtt"
|
||||
StrCpy $PageReinstall_NEW_Field_3 "Ne távolítsa el"
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Már telepítve"
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Válaszd ki, hogy szeretnéd telepíteni a következő alkalmazást ${APPLICATION_NAME}."
|
||||
StrCpy $PageReinstall_OLD_Field_1 "Az ${APPLICATION_NAME} alklamazás egy újabb verziója már megtalálható a rendszeren. Nem ajánlott egy régebbi verzió telepítése. Ha valóban szeretné a régebbi verziót telepíteni, akkor ajánlott a jelenleg telepített verzió eltávolítása. Válassza ki milyen műveletet szeretne végrehajtani, és nyomja meg a $\"Következő$\" gombot a folytatáshoz."
|
||||
StrCpy $PageReinstall_OLD_Field_1 "Az ${APPLICATION_NAME} alkalmazás egy újabb verziója már megtalálható a rendszeren. Nem ajánlott egy régebbi verzió telepítése. Ha valóban szeretné a régebbi verziót telepíteni, akkor ajánlott a jelenleg telepített verzió eltávolítása. Válassza ki milyen műveletet szeretne végrehajtani, és nyomja meg a $\"Következő$\" gombot a folytatáshoz."
|
||||
StrCpy $PageReinstall_SAME_Field_1 "Az ${APPLICATION_NAME} alkalmazás ${VERSION} verziója már telepítve van.$↩$\nKérjük válaszd ki milyen műveletet szeretnél végrehajtani, és nyomd meg a „Következő” gombot."
|
||||
StrCpy $PageReinstall_SAME_Field_2 "Komponens hozzáadása/újratelepítése"
|
||||
StrCpy $PageReinstall_SAME_Field_3 "${APPLICATION_NAME} eltávolítása"
|
||||
StrCpy $UNINSTALLER_APPDATA_TITLE "${APPLICATION_NAME} eltávolítása"
|
||||
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Kérem válassza ki milyen karbantartási műveletet szeretne elvégezni?"
|
||||
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Válassza ki milyen karbantartási műveletet szeretne elvégezni."
|
||||
StrCpy $SEC_APPLICATION_DETAILS "Az ${APPLICATION_NAME} alkalmazás lényeges komponenseinek telepítése."
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Windows Explorer Integráció"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Windows Explorer Integráció Telepítése"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Start Menü Parancsikonok"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Windows Explorer integráció"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Windows Explorer integráció telepítése"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_SECTION "Start Menü parancsikonok"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "A ${APPLICATION_NAME} parancsikon hozzáadása a Start Menühöz"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Asztali Parancsikon"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "Asztali Parancsikon Létrehozása"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Gyorsindítás Eszköztár Parancsikon"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Gyorsindítás Eszköztár Parancsikon Létrehozása"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Asztali parancsikon"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "Asztali parancsikon létrehozása"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Gyorsindító eszköztár parancsikon"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Gyorsindító eszköztár parancsikon létrehozása"
|
||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} lényeges komponensek."
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "${APPLICATION_NAME} parancsikon"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Asztali parancsikon a ${APPLICATION_NAME} alkalmazásnak."
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Gyorsindítás eszköztár parancsikon a ${APPLICATION_NAME} alkalmazásnak."
|
||||
StrCpy $UNINSTALLER_FILE_Detail "Program Eltávolító Írása"
|
||||
StrCpy $UNINSTALLER_FILE_Detail "Elltávolító írása"
|
||||
StrCpy $UNINSTALLER_REGISTRY_Detail "Telepítési registry kulcsok írása"
|
||||
StrCpy $UNINSTALLER_FINISHED_Detail "Befejezve"
|
||||
StrCpy $UNINSTALLER_FINISHED_Detail "Befejezve!"
|
||||
StrCpy $UNINSTALL_MESSAGEBOX "Nem sikerült az ${APPLICATION_NAME} alkalmazás telepítése a '$INSTDIR' könyvtárba.$\n$\nSzeretné mindenképpen folytatni (nem ajánlott)?"
|
||||
StrCpy $UNINSTALL_ABORT "Az eltávolítást egy felhasználó megszakította"
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "Gyorsindító Hivatkozás (N/A)"
|
||||
StrCpy $INIT_NO_DESKTOP "Asztali Hivatkozás (felülírja a meglévőt)"
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "Gyorsindító hivatkozás (N/A)"
|
||||
StrCpy $INIT_NO_DESKTOP "Asztali hivatkozás (felülírja a meglévőt)"
|
||||
StrCpy $UAC_ERROR_ELEVATE "Nem sikerült felemelni, hiba:"
|
||||
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "A telepítő futtatásához adminisztrátori hozzáférés szükséges, próbáld újra."
|
||||
StrCpy $INIT_INSTALLER_RUNNING "A telepítő már fut."
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Auto-generated - do not modify
|
||||
StrCpy $MUI_FINISHPAGE_SHOWREADME_TEXT_STRING "Mostrar notas de la versión"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Se encontrarion ${APPLICATION_EXECUTABLE} proceso(s) que debe/n ser detenidos.$\"$\\n$\"¿Quiere que el instalador lo haga por usted?"
|
||||
StrCpy $ConfirmEndProcess_KILLING_PROCESSES_TEXT "Parando el proceso ${APPLICATION_EXECUTABLE}."
|
||||
StrCpy $ConfirmEndProcess_KILL_NOT_FOUND_TEXT "Proceso a detener no encontrado!"
|
||||
StrCpy $PageReinstall_NEW_Field_1 "Una versión anterior de ${APPLICATION_NAME} esta instalada en el sistema. Es recomendado que quite esta versión antes de instalar. Elija la operación a realizar y seleccione Siguiente para continuar."
|
||||
@@ -8,36 +9,35 @@ StrCpy $PageReinstall_NEW_Field_3 "No des-instalar."
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_TITLE "Actualmente Instalado."
|
||||
StrCpy $PageReinstall_NEW_MUI_HEADER_TEXT_SUBTITLE "Elija como desea instalar ${APPLICATION_NAME}."
|
||||
StrCpy $PageReinstall_OLD_Field_1 "Una versión mas reciente de ${APPLICATION_NAME} esta actualmente instalada! No es recomendado que instale una versión antigua. Si realmente desea instalar esta versión obsoleta, es mejor que des-instale la versión actual primero. Seleccione la operación que desea realizar y presione en Siguiente para continuar. "
|
||||
StrCpy $PageReinstall_SAME_Field_1 "La ${APPLICATION_NAME} ${VERSION} ya está instalado.$\n$\nSeleccione la operación que desea realizar y haga click en Siguiente para continuar."
|
||||
StrCpy $PageReinstall_SAME_Field_2 "Agregar/Re-Instalar componentes"
|
||||
StrCpy $PageReinstall_SAME_Field_3 "Des-instalar ${APPLICATION_NAME}"
|
||||
StrCpy $UNINSTALLER_APPDATA_TITLE "Des-instalar ${APPLICATION_NAME}"
|
||||
StrCpy $PageReinstall_SAME_MUI_HEADER_TEXT_SUBTITLE "Elija la opción de mantenimiento a realizar."
|
||||
StrCpy $SEC_APPLICATION_DETAILS "Instalar esenciales ${APPLICATION_NAME}."
|
||||
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_START_MENU_SECTION "Acceso Directo en Menú de Programas"
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_DetailPrint "Agregando el Acceso Directo al Menú de Inicio para ${APPLICATION_NAME}."
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_SECTION "Acceso directo en Escritorio"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_DetailPrint "Creando Accesos Directos en Escritorio"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Atajo de Acceso Rápido"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Creando Atajo de Acceso Rápido"
|
||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} esencial."
|
||||
StrCpy $OPTION_SECTION_SC_START_MENU_Desc "Acceso directo de ${APPLICATION_NAME}"
|
||||
StrCpy $OPTION_SECTION_SC_DESKTOP_Desc "Acceso Directo al Escritorio para ${APPLICATION_NAME}."
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Atajo de Acceso Rápido para ${APPLICATION_NAME}."
|
||||
StrCpy $UNINSTALLER_FILE_Detail "Escribiendo Des-Instalador."
|
||||
StrCpy $UNINSTALLER_REGISTRY_Detail "Escribiendo claves de Registro del Instalador"
|
||||
StrCpy $UNINSTALLER_FINISHED_Detail "Terminado"
|
||||
StrCpy $UNINSTALL_MESSAGEBOX "Parece que ${APPLICATION_NAME} no esta instalado en el directorio '$INSTDIR'.$\n$\n¿Continuar de todos modos? (No recomendado)"
|
||||
StrCpy $UNINSTALL_ABORT "Des-instalación abortada por el usuario"
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "Atajo de Acceso Rápido (N/A)"
|
||||
StrCpy $INIT_NO_DESKTOP "Acceso Directo en Escritorio (Sobrescribe existentes)"
|
||||
StrCpy $UAC_ERROR_ELEVATE "No se ha podido elevar, error:"
|
||||
StrCpy $UAC_INSTALLER_REQUIRE_ADMIN "Este instalador requiere acceso Administrador, intente de nuevo. "
|
||||
StrCpy $INIT_INSTALLER_RUNNING "El instalador ya esta corriendo."
|
||||
StrCpy $UAC_UNINSTALLER_REQUIRE_ADMIN "Este des-instalador requiere acceso administrador, intente de nuevo"
|
||||
StrCpy $UAC_ERROR_LOGON_SERVICE "Servicio Inicio de sesión no se está ejecutando, abortando!"
|
||||
StrCpy $INIT_UNINSTALLER_RUNNING "El des-instalador ya esta corriendo"
|
||||
StrCpy $SectionGroup_Shortcuts "Accesos Directos"
|
||||
StrCpy $ConfirmEndProcess_MESSAGEBOX_TEXT "Found ${APPLICATION_EXECUTABLE} process(s) which need to be stopped.$\nDo you want the installer to stop these for you?"
|
||||
StrCpy $PageReinstall_SAME_Field_1 "${APPLICATION_NAME} ${VERSION} is already installed.$\r$\nSelect the operation you want to perform and click Next to continue."
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_SECTION "Integration for Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_SHELL_EXT_DetailPrint "Installing Integration for Windows Explorer"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_SECTION "Quick Launch Shortcut"
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_DetailPrint "Creating Quick Launch Shortcut"
|
||||
StrCpy $OPTION_SECTION_SC_APPLICATION_Desc "${APPLICATION_NAME} essentials."
|
||||
StrCpy $OPTION_SECTION_SC_QUICK_LAUNCH_Desc "Quick Launch shortcut for ${APPLICATION_NAME}."
|
||||
StrCpy $UNINSTALL_MESSAGEBOX "It does not appear that ${APPLICATION_NAME} is installed in the directory '$INSTDIR'.$\r$\nContinue anyway (not recommended)?"
|
||||
StrCpy $INIT_NO_QUICK_LAUNCH "Quick Launch Shortcut (N/A)"
|
||||
StrCpy $UAC_ERROR_ELEVATE "Unable to elevate, error:"
|
||||
StrCpy $UAC_ERROR_LOGON_SERVICE "Logon service is not running, aborting!"
|
||||
|
||||
@@ -20,5 +20,7 @@
|
||||
<file>resources/lock-https.png</file>
|
||||
<file>resources/lock-https@2x.png</file>
|
||||
<file>resources/account.png</file>
|
||||
<file>resources/more.png</file>
|
||||
<file>resources/delete.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
# - Try to find Neon
|
||||
# Once done this will define
|
||||
#
|
||||
# NEON_FOUND - system has Neon
|
||||
# NEON_INCLUDE_DIRS - the Neon include directory
|
||||
# NEON_LIBRARIES - Link these to use Neon
|
||||
# NEON_DEFINITIONS - Compiler switches required for using Neon
|
||||
#
|
||||
# Copyright (c) 2011-2013 Andreas Schneider <asn@cryptomilk.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
#
|
||||
|
||||
|
||||
find_package(PkgConfig)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(_NEON neon)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
find_path(NEON_INCLUDE_DIRS
|
||||
NAMES
|
||||
neon/ne_basic.h
|
||||
HINTS
|
||||
${_NEON_INCLUDEDIR}
|
||||
${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
|
||||
find_library(NEON_LIBRARIES
|
||||
NAMES
|
||||
neon neon-27
|
||||
HINTS
|
||||
${_NEON_LIBDIR}
|
||||
${CMAKE_INSTALL_LIBDIR}
|
||||
${CMAKE_INSTALL_PREFIX}/lib
|
||||
${CMAKE_INSTALL_PREFIX}/lib64
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Neon DEFAULT_MSG NEON_LIBRARIES NEON_INCLUDE_DIRS)
|
||||
|
||||
# show the NEON_INCLUDE_DIRS and NEON_LIBRARIES variables only in the advanced view
|
||||
mark_as_advanced(NEON_INCLUDE_DIRS NEON_LIBRARIES)
|
||||
|
||||
# Check if neon was compiled with LFS support, if so, the NE_LFS variable has to
|
||||
# be defined in the owncloud module.
|
||||
# If neon was not compiled with LFS its also ok since the underlying system
|
||||
# than probably supports large files anyway.
|
||||
IF( CMAKE_FIND_ROOT_PATH )
|
||||
FIND_PROGRAM( NEON_CONFIG_EXECUTABLE NAMES neon-config HINTS ${CMAKE_FIND_ROOT_PATH}/bin )
|
||||
ELSE( CMAKE_FIND_ROOT_PATH )
|
||||
FIND_PROGRAM( NEON_CONFIG_EXECUTABLE NAMES neon-config )
|
||||
ENDIF( CMAKE_FIND_ROOT_PATH )
|
||||
|
||||
IF ( NEON_CONFIG_EXECUTABLE )
|
||||
MESSAGE(STATUS "neon-config executable: ${NEON_CONFIG_EXECUTABLE}")
|
||||
# neon-config --support lfs
|
||||
EXECUTE_PROCESS( COMMAND ${NEON_CONFIG_EXECUTABLE} "--support" "lfs"
|
||||
RESULT_VARIABLE LFS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE )
|
||||
|
||||
IF (LFS EQUAL 0)
|
||||
MESSAGE(STATUS "libneon has been compiled with LFS support")
|
||||
SET(NEON_WITH_LFS 1)
|
||||
ELSE (LFS EQUAL 0)
|
||||
MESSAGE(STATUS "libneon has not been compiled with LFS support, rely on OS")
|
||||
ENDIF (LFS EQUAL 0)
|
||||
ELSE ( NEON_CONFIG_EXECUTABLE )
|
||||
MESSAGE(STATUS, "neon-config could not be found.")
|
||||
ENDIF ( NEON_CONFIG_EXECUTABLE )
|
||||
@@ -134,13 +134,7 @@ if(NOT DEFINED CMAKE_INSTALL_LIBDIR OR (_libdir_set
|
||||
AND NOT CMAKE_CROSSCOMPILING)
|
||||
if (EXISTS "/etc/debian_version") # is this a debian system ?
|
||||
if(CMAKE_LIBRARY_ARCHITECTURE)
|
||||
if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$")
|
||||
set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
|
||||
endif()
|
||||
if(DEFINED _GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX
|
||||
AND "${_GNUInstallDirs_LAST_CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/?$")
|
||||
set(__LAST_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
|
||||
endif()
|
||||
set(_LIBDIR_DEFAULT "lib/${CMAKE_LIBRARY_ARCHITECTURE}")
|
||||
endif()
|
||||
else() # not debian, rely on CMAKE_SIZEOF_VOID_P:
|
||||
if(NOT DEFINED CMAKE_SIZEOF_VOID_P)
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
!define QT_DLL_PATH "${MING_BIN}"
|
||||
!define ACCESSIBLE_DLL_PATH "${MING_LIB}/qt5/plugins/accessible"
|
||||
!define SQLITE_DLL_PATH "${MING_LIB}/qt5/plugins/sqldrivers"
|
||||
!define BEARER_DLL_PATH "${MING_LIB}/qt5/plugins/bearer"
|
||||
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
|
||||
!define PLATFORMS_DLL_PATH "${MING_LIB}/qt5/plugins/platforms"
|
||||
|
||||
@@ -406,6 +407,10 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
SetOutPath "$INSTDIR\sqldrivers"
|
||||
File "${SQLITE_DLL_PATH}\qsqlite.dll"
|
||||
|
||||
SetOutPath "$INSTDIR\bearer"
|
||||
File "${BEARER_DLL_PATH}\qgenericbearer.dll"
|
||||
File "${BEARER_DLL_PATH}\qnativewifibearer.dll"
|
||||
|
||||
SetOutPath "$INSTDIR"
|
||||
;License & release notes.
|
||||
File "@CPACK_RESOURCE_FILE_LICENSE@"
|
||||
|
||||
@@ -21,8 +21,6 @@ if( Qt5Core_FOUND )
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5WebKitWidgets REQUIRED)
|
||||
find_package(Qt5WebKit REQUIRED)
|
||||
find_package(Qt5PrintSupport REQUIRED)
|
||||
find_package(Qt5Quick REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
if(APPLE)
|
||||
find_package(Qt5MacExtras REQUIRED)
|
||||
@@ -31,7 +29,9 @@ if( Qt5Core_FOUND )
|
||||
|
||||
else( Qt5Core_FOUND )
|
||||
if(WIN32 OR APPLE)
|
||||
if (NOT BUILD_WITH_QT4)
|
||||
message(FATAL_ERROR "Qt 5 not found, but application depends on Qt5 on Windows and Mac OS X")
|
||||
endif ()
|
||||
endif(WIN32 OR APPLE)
|
||||
endif( Qt5Core_FOUND )
|
||||
|
||||
@@ -72,11 +72,16 @@ endif()
|
||||
endmacro()
|
||||
|
||||
if(NOT TOKEN_AUTH_ONLY)
|
||||
find_package(Qt5LinguistTools REQUIRED)
|
||||
macro(qt_add_translation)
|
||||
qt5_add_translation(${ARGN})
|
||||
endmacro()
|
||||
else()
|
||||
find_package(Qt5LinguistTools)
|
||||
if(Qt5LinguistTools_FOUND)
|
||||
macro(qt_add_translation)
|
||||
qt5_add_translation(${ARGN})
|
||||
endmacro()
|
||||
else()
|
||||
macro(qt_add_translation)
|
||||
endmacro()
|
||||
endif()
|
||||
else()
|
||||
macro(qt_add_translation)
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
@@ -23,8 +23,4 @@
|
||||
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
|
||||
#cmakedefine SHAREDIR "@SHAREDIR@"
|
||||
|
||||
#ifndef NEON_WITH_LFS
|
||||
#cmakedefine NEON_WITH_LFS "@NEON_WITH_LFS@"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,10 +24,6 @@
|
||||
#cmakedefine HAVE_ICONV 1
|
||||
#cmakedefine HAVE_ICONV_CONST 1
|
||||
|
||||
#ifndef NEON_WITH_LFS
|
||||
#cmakedefine NEON_WITH_LFS 1
|
||||
#endif
|
||||
|
||||
#cmakedefine HAVE___MINGW_ASPRINTF 1
|
||||
#cmakedefine HAVE_ASPRINTF 1
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
project(libcsync)
|
||||
|
||||
add_subdirectory(std)
|
||||
if(USE_NEON)
|
||||
add_subdirectory(httpbf)
|
||||
endif()
|
||||
|
||||
# Statically include sqlite
|
||||
|
||||
@@ -71,19 +68,6 @@ else()
|
||||
endif()
|
||||
|
||||
|
||||
if(USE_NEON)
|
||||
list(APPEND csync_SRCS
|
||||
csync_owncloud.c
|
||||
csync_owncloud_util.c
|
||||
)
|
||||
list(APPEND CSYNC_LINK_LIBRARIES
|
||||
${NEON_LIBRARIES}
|
||||
)
|
||||
include_directories(${NEON_INCLUDE_DIRS})
|
||||
add_definitions(-DUSE_NEON)
|
||||
endif(USE_NEON)
|
||||
|
||||
|
||||
configure_file(csync_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h)
|
||||
|
||||
set(csync_HDRS
|
||||
|
||||
@@ -58,12 +58,6 @@
|
||||
#include "csync_rename.h"
|
||||
#include "c_jhash.h"
|
||||
|
||||
|
||||
#ifdef USE_NEON
|
||||
// Breaking the abstraction for fun and profit.
|
||||
#include "csync_owncloud.h"
|
||||
#endif
|
||||
|
||||
static int _key_cmp(const void *key, const void *data) {
|
||||
uint64_t a;
|
||||
csync_file_stat_t *b;
|
||||
@@ -154,9 +148,6 @@ int csync_init(CSYNC *ctx) {
|
||||
|
||||
ctx->local.type = LOCAL_REPLICA;
|
||||
|
||||
#ifdef USE_NEON
|
||||
owncloud_init(ctx);
|
||||
#endif
|
||||
ctx->remote.type = REMOTE_REPLICA;
|
||||
|
||||
if (c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp) < 0) {
|
||||
@@ -216,14 +207,6 @@ int csync_update(CSYNC *ctx) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "No exclude file loaded or defined!");
|
||||
}
|
||||
|
||||
#ifdef USE_NEON
|
||||
/* This is not actually connecting, just setting the info for neon. The legacy propagator can use it.. */
|
||||
if (dav_connect( ctx, ctx->remote.uri ) < 0) {
|
||||
ctx->status_code = CSYNC_STATUS_CONNECT_ERROR;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* update detection for local replica */
|
||||
csync_gettime(&start);
|
||||
ctx->current = LOCAL_REPLICA;
|
||||
@@ -438,6 +421,8 @@ static int _csync_treewalk_visitor(void *obj, void *data) {
|
||||
trav.error_status = cur->error_status;
|
||||
trav.should_update_metadata = cur->should_update_metadata;
|
||||
trav.has_ignored_files = cur->has_ignored_files;
|
||||
trav.checksum = cur->checksum;
|
||||
trav.checksumTypeId = cur->checksumTypeId;
|
||||
|
||||
if( other_node ) {
|
||||
csync_file_stat_t *other_stat = (csync_file_stat_t*)other_node->data;
|
||||
@@ -646,10 +631,6 @@ int csync_destroy(CSYNC *ctx) {
|
||||
SAFE_FREE(ctx->remote.uri);
|
||||
SAFE_FREE(ctx->error_string);
|
||||
|
||||
#ifdef USE_NEON
|
||||
owncloud_destroy(ctx);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ICONV
|
||||
c_close_iconv();
|
||||
#endif
|
||||
@@ -672,21 +653,6 @@ void csync_clear_exclude_list(CSYNC *ctx)
|
||||
csync_exclude_clear(ctx);
|
||||
}
|
||||
|
||||
int csync_set_auth_callback(CSYNC *ctx, csync_auth_callback cb) {
|
||||
if (ctx == NULL || cb == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->status & CSYNC_STATUS_INIT) {
|
||||
ctx->status_code = CSYNC_STATUS_CSYNC_STATUS_ERROR;
|
||||
fprintf(stderr, "This function must be called before initialization.");
|
||||
return -1;
|
||||
}
|
||||
ctx->callbacks.auth_function = cb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *csync_get_userdata(CSYNC *ctx) {
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
@@ -778,17 +744,7 @@ void csync_file_stat_free(csync_file_stat_t *st)
|
||||
SAFE_FREE(st->directDownloadCookies);
|
||||
SAFE_FREE(st->etag);
|
||||
SAFE_FREE(st->destpath);
|
||||
SAFE_FREE(st->checksum);
|
||||
SAFE_FREE(st);
|
||||
}
|
||||
}
|
||||
|
||||
int csync_set_module_property(CSYNC* ctx, const char* key, void* value)
|
||||
{
|
||||
#ifdef USE_NEON
|
||||
return owncloud_set_property(ctx, key, value);
|
||||
#else
|
||||
(void)ctx, (void)key, (void)value;
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,8 @@ enum csync_status_codes_e {
|
||||
CYSNC_STATUS_FILE_LOCKED_OR_OPEN,
|
||||
CSYNC_STATUS_INDIVIDUAL_EXCLUDE_HIDDEN,
|
||||
CSYNC_STATUS_INVALID_CHARACTERS,
|
||||
CSYNC_STATUS_INDIVIDUAL_STAT_FAILED
|
||||
CSYNC_STATUS_INDIVIDUAL_STAT_FAILED,
|
||||
CSYNC_STATUS_FORBIDDEN
|
||||
};
|
||||
|
||||
typedef enum csync_status_codes_e CSYNC_STATUS;
|
||||
@@ -173,7 +174,7 @@ enum csync_vio_file_stat_fields_e {
|
||||
CSYNC_VIO_FILE_STAT_FIELDS_TYPE = 1 << 0,
|
||||
CSYNC_VIO_FILE_STAT_FIELDS_MODE = 1 << 1, // local POSIX mode
|
||||
CSYNC_VIO_FILE_STAT_FIELDS_FLAGS = 1 << 2,
|
||||
CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3,
|
||||
// CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3,
|
||||
CSYNC_VIO_FILE_STAT_FIELDS_INODE = 1 << 4,
|
||||
// CSYNC_VIO_FILE_STAT_FIELDS_LINK_COUNT = 1 << 5,
|
||||
CSYNC_VIO_FILE_STAT_FIELDS_SIZE = 1 << 6,
|
||||
@@ -212,7 +213,6 @@ struct csync_vio_file_stat_s {
|
||||
|
||||
mode_t mode;
|
||||
|
||||
dev_t device;
|
||||
uint64_t inode;
|
||||
|
||||
int fields; // actually enum csync_vio_file_stat_fields_e fields;
|
||||
@@ -262,6 +262,10 @@ struct csync_tree_walk_file_s {
|
||||
const char *remotePerm;
|
||||
char *directDownloadUrl;
|
||||
char *directDownloadCookies;
|
||||
|
||||
const char *checksum;
|
||||
uint32_t checksumTypeId;
|
||||
|
||||
struct {
|
||||
int64_t size;
|
||||
time_t modtime;
|
||||
@@ -301,6 +305,10 @@ typedef void (*csync_vio_closedir_hook) (csync_vio_handle_t *dhhandle,
|
||||
typedef int (*csync_vio_stat_hook) (csync_vio_handle_t *dhhandle,
|
||||
void *userdata);
|
||||
|
||||
/* Compute the checksum of the given \a checksumTypeId for \a path. */
|
||||
typedef const char* (*csync_checksum_hook) (
|
||||
const char *path, uint32_t checksumTypeId, void *userdata);
|
||||
|
||||
/**
|
||||
* @brief Allocate a csync context.
|
||||
*
|
||||
@@ -519,19 +527,6 @@ const char *csync_get_status_string(CSYNC *ctx);
|
||||
int csync_set_iconv_codec(const char *from);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set a property to module
|
||||
*
|
||||
* @param ctx The csync context.
|
||||
*
|
||||
* @param key The property key
|
||||
*
|
||||
* @param value An opaque pointer to the data.
|
||||
*
|
||||
* @return 0 on success, less than 0 if an error occured.
|
||||
*/
|
||||
int csync_set_module_property(CSYNC *ctx, const char *key, void *value);
|
||||
|
||||
/**
|
||||
* @brief Aborts the current sync run as soon as possible. Can be called from another thread.
|
||||
*
|
||||
|
||||
@@ -171,7 +171,6 @@ bool csync_is_windows_reserved_word(const char* filename) {
|
||||
|
||||
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const char *path, int filetype, bool check_leading_dirs) {
|
||||
size_t i = 0;
|
||||
const char *p = NULL;
|
||||
const char *bname = NULL;
|
||||
size_t blen = 0;
|
||||
char *conflict = NULL;
|
||||
@@ -179,22 +178,6 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
|
||||
CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED;
|
||||
|
||||
for (p = path; *p; p++) {
|
||||
switch (*p) {
|
||||
case '\\':
|
||||
case ':':
|
||||
case '?':
|
||||
case '*':
|
||||
case '"':
|
||||
case '>':
|
||||
case '<':
|
||||
case '|':
|
||||
return CSYNC_FILE_EXCLUDE_INVALID_CHAR;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* split up the path */
|
||||
bname = strrchr(path, '/');
|
||||
if (bname) {
|
||||
@@ -217,7 +200,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
// Windows cannot sync files ending in spaces (#2176). It also cannot
|
||||
// distinguish files ending in '.' from files without an ending,
|
||||
// as '.' is a separator that is not stored internally, so let's
|
||||
@@ -231,7 +214,26 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Filter out characters not allowed in a filename on windows
|
||||
const char *p = NULL;
|
||||
for (p = path; *p; p++) {
|
||||
switch (*p) {
|
||||
case '\\':
|
||||
case ':':
|
||||
case '?':
|
||||
case '*':
|
||||
case '"':
|
||||
case '>':
|
||||
case '<':
|
||||
case '|':
|
||||
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
|
||||
goto out;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
rc = csync_fnmatch(".owncloudsync.log*", bname, 0);
|
||||
if (rc == 0) {
|
||||
@@ -264,40 +266,41 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Build a list of path components to check. */
|
||||
c_strlist_t *path_components = c_strlist_new(32);
|
||||
char *path_split = strdup(path);
|
||||
size_t len = strlen(path_split);
|
||||
for (i = len; ; --i) {
|
||||
// read backwards until a path separator is found
|
||||
if (i != 0 && path_split[i-1] != '/') {
|
||||
continue;
|
||||
}
|
||||
c_strlist_t *path_components = NULL;
|
||||
if (check_leading_dirs) {
|
||||
/* Build a list of path components to check. */
|
||||
path_components = c_strlist_new(32);
|
||||
char *path_split = strdup(path);
|
||||
size_t len = strlen(path_split);
|
||||
for (i = len; ; --i) {
|
||||
// read backwards until a path separator is found
|
||||
if (i != 0 && path_split[i-1] != '/') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
|
||||
if (path_split[i] != 0) {
|
||||
c_strlist_add_grow(&path_components, path_split + i);
|
||||
}
|
||||
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
|
||||
if (path_split[i] != 0) {
|
||||
c_strlist_add_grow(&path_components, path_split + i);
|
||||
}
|
||||
|
||||
if (i == 0 || !check_leading_dirs) {
|
||||
break;
|
||||
}
|
||||
if (i == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
|
||||
path_split[i-1] = '\0';
|
||||
c_strlist_add_grow(&path_components, path_split);
|
||||
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
|
||||
path_split[i-1] = '\0';
|
||||
c_strlist_add_grow(&path_components, path_split);
|
||||
}
|
||||
SAFE_FREE(path_split);
|
||||
}
|
||||
SAFE_FREE(path_split);
|
||||
|
||||
/* Loop over all exclude patterns and evaluate the given path */
|
||||
for (i = 0; match == CSYNC_NOT_EXCLUDED && i < excludes->count; i++) {
|
||||
bool match_dirs_only = false;
|
||||
char *pattern_stored = c_strdup(excludes->vector[i]);
|
||||
char* pattern = pattern_stored;
|
||||
char *pattern = excludes->vector[i];
|
||||
|
||||
type = CSYNC_FILE_EXCLUDE_LIST;
|
||||
if (strlen(pattern) < 1) {
|
||||
SAFE_FREE(pattern_stored);
|
||||
if (!pattern[0]) { /* empty pattern */
|
||||
continue;
|
||||
}
|
||||
/* Excludes starting with ']' means it can be cleanup */
|
||||
@@ -309,6 +312,9 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
}
|
||||
/* Check if the pattern applies to pathes only. */
|
||||
if (pattern[strlen(pattern)-1] == '/') {
|
||||
if (!check_leading_dirs && filetype == CSYNC_FTW_TYPE_FILE) {
|
||||
continue;
|
||||
}
|
||||
match_dirs_only = true;
|
||||
pattern[strlen(pattern)-1] = '\0'; /* Cut off the slash */
|
||||
}
|
||||
@@ -326,7 +332,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
}
|
||||
|
||||
/* if still not excluded, check each component and leading directory of the path */
|
||||
if (match == CSYNC_NOT_EXCLUDED) {
|
||||
if (match == CSYNC_NOT_EXCLUDED && check_leading_dirs) {
|
||||
size_t j = 0;
|
||||
if (match_dirs_only && filetype == CSYNC_FTW_TYPE_FILE) {
|
||||
j = 1; // skip the first entry, which is bname
|
||||
@@ -338,8 +344,16 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (match == CSYNC_NOT_EXCLUDED && !check_leading_dirs) {
|
||||
rc = csync_fnmatch(pattern, bname, 0);
|
||||
if (rc == 0) {
|
||||
match = type;
|
||||
}
|
||||
}
|
||||
if (match_dirs_only) {
|
||||
/* restore the '/' */
|
||||
pattern[strlen(pattern)] = '/';
|
||||
}
|
||||
SAFE_FREE(pattern_stored);
|
||||
}
|
||||
c_strlist_destroy(path_components);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#define ERRNO_SERVICE_UNAVAILABLE CSYNC_CUSTOM_ERRNO_BASE+14
|
||||
#define ERRNO_USER_ABORT CSYNC_CUSTOM_ERRNO_BASE+16
|
||||
#define ERRNO_STORAGE_UNAVAILABLE CSYNC_CUSTOM_ERRNO_BASE+17
|
||||
#define ERRNO_FORBIDDEN CSYNC_CUSTOM_ERRNO_BASE+18
|
||||
|
||||
#endif /* _CSYNC_MACROS_H */
|
||||
/* vim: set ft=c.doxygen ts=8 sw=2 et cindent: */
|
||||
|
||||
@@ -57,20 +57,13 @@ int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
|
||||
#else /* HAVE_FNMATCH */
|
||||
|
||||
#include <shlwapi.h>
|
||||
int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
|
||||
wchar_t *pat = NULL;
|
||||
wchar_t *name = NULL;
|
||||
int csync_fnmatch(const char *pattern, const char *name, int flags) {
|
||||
BOOL match;
|
||||
|
||||
(void) __flags;
|
||||
(void) flags;
|
||||
|
||||
name = c_utf8_string_to_locale(__name);
|
||||
pat = c_utf8_string_to_locale(__pattern);
|
||||
match = PathMatchSpecA(name, pattern);
|
||||
|
||||
match = PathMatchSpecW(name, pat);
|
||||
|
||||
c_free_locale_string(pat);
|
||||
c_free_locale_string(name);
|
||||
if(match)
|
||||
return 0;
|
||||
else
|
||||
|
||||
@@ -1,617 +0,0 @@
|
||||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
|
||||
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "csync_owncloud.h"
|
||||
#include "csync_owncloud_private.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "csync_private.h"
|
||||
|
||||
#include "csync_version.h"
|
||||
|
||||
|
||||
/*
|
||||
* helper method to build up a user text for SSL problems, called from the
|
||||
* verify_sslcert callback.
|
||||
*/
|
||||
static void addSSLWarning( char *ptr, const char *warn, int len )
|
||||
{
|
||||
char *concatHere = ptr;
|
||||
int remainingLen = 0;
|
||||
|
||||
if( ! (warn && ptr )) return;
|
||||
remainingLen = len - strlen(ptr);
|
||||
if( remainingLen <= 0 ) return;
|
||||
concatHere = ptr + strlen(ptr); /* put the write pointer to the end. */
|
||||
strncpy( concatHere, warn, remainingLen );
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to verify the SSL certificate, called from libneon.
|
||||
* It analyzes the SSL problem, creates a user information text and passes
|
||||
* it to the csync callback to ask the user.
|
||||
*/
|
||||
#define LEN 4096
|
||||
static int ssl_callback_by_neon(void *userdata, int failures,
|
||||
const ne_ssl_certificate *certificate)
|
||||
{
|
||||
char problem[LEN];
|
||||
char buf[MAX(NE_SSL_DIGESTLEN, NE_ABUFSIZ)];
|
||||
int ret = -1;
|
||||
const ne_ssl_certificate *cert = certificate;
|
||||
csync_auth_callback authcb = NULL;
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
|
||||
|
||||
memset( problem, 0, LEN );
|
||||
|
||||
while( cert ) {
|
||||
|
||||
addSSLWarning( problem, "There are problems with the SSL certificate:\n", LEN );
|
||||
if( failures & NE_SSL_NOTYETVALID ) {
|
||||
addSSLWarning( problem, " * The certificate is not yet valid.\n", LEN );
|
||||
}
|
||||
if( failures & NE_SSL_EXPIRED ) {
|
||||
addSSLWarning( problem, " * The certificate has expired.\n", LEN );
|
||||
}
|
||||
|
||||
if( failures & NE_SSL_UNTRUSTED ) {
|
||||
addSSLWarning( problem, " * The certificate is not trusted!\n", LEN );
|
||||
}
|
||||
if( failures & NE_SSL_IDMISMATCH ) {
|
||||
addSSLWarning( problem, " * The hostname for which the certificate was "
|
||||
"issued does not match the hostname of the server\n", LEN );
|
||||
}
|
||||
if( failures & NE_SSL_BADCHAIN ) {
|
||||
addSSLWarning( problem, " * The certificate chain contained a certificate other than the server cert\n", LEN );
|
||||
}
|
||||
if( failures & NE_SSL_REVOKED ) {
|
||||
addSSLWarning( problem, " * The server certificate has been revoked by the issuing authority.\n", LEN );
|
||||
}
|
||||
|
||||
if (ne_ssl_cert_digest(cert, buf) == 0) {
|
||||
addSSLWarning( problem, "Certificate fingerprint: ", LEN );
|
||||
addSSLWarning( problem, buf, LEN );
|
||||
addSSLWarning( problem, "\n", LEN );
|
||||
}
|
||||
cert = ne_ssl_cert_signedby( cert );
|
||||
}
|
||||
addSSLWarning( problem, "Do you want to accept the certificate chain anyway?\nAnswer yes to do so and take the risk: ", LEN );
|
||||
|
||||
if( ctx->csync_ctx ) {
|
||||
authcb = csync_get_auth_callback( ctx->csync_ctx );
|
||||
}
|
||||
if( authcb ){
|
||||
/* call the csync callback */
|
||||
DEBUG_WEBDAV("Call the csync callback for SSL problems");
|
||||
memset( buf, 0, NE_ABUFSIZ );
|
||||
(*authcb) ( problem, buf, NE_ABUFSIZ-1, 1, 0, csync_get_userdata(ctx->csync_ctx) );
|
||||
if( buf[0] == 'y' || buf[0] == 'Y') {
|
||||
ret = 0;
|
||||
} else {
|
||||
DEBUG_WEBDAV("Authentication callback replied %s", buf );
|
||||
|
||||
}
|
||||
}
|
||||
DEBUG_WEBDAV("## VERIFY_SSL CERT: %d", ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Authentication callback. Is set by ne_set_server_auth to be called
|
||||
* from the neon lib to authenticate a request.
|
||||
*/
|
||||
static int authentication_callback_by_neon( void *userdata, const char *realm, int attempt,
|
||||
char *username, char *password)
|
||||
{
|
||||
char buf[NE_ABUFSIZ];
|
||||
csync_auth_callback authcb = NULL;
|
||||
int re = attempt;
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
|
||||
|
||||
(void) realm;
|
||||
|
||||
/* DEBUG_WEBDAV( "Authentication required %s", realm ); */
|
||||
if( username && password ) {
|
||||
DEBUG_WEBDAV( "Authentication required %s", username );
|
||||
if( ctx->dav_session.user ) {
|
||||
/* allow user without password */
|
||||
if( strlen( ctx->dav_session.user ) < NE_ABUFSIZ ) {
|
||||
strcpy( username, ctx->dav_session.user );
|
||||
}
|
||||
if( ctx->dav_session.pwd && strlen( ctx->dav_session.pwd ) < NE_ABUFSIZ ) {
|
||||
strcpy( password, ctx->dav_session.pwd );
|
||||
}
|
||||
} else {
|
||||
authcb = csync_get_auth_callback( ctx->csync_ctx );
|
||||
if( authcb != NULL ){
|
||||
/* call the csync callback */
|
||||
DEBUG_WEBDAV("Call the csync callback for %s", realm );
|
||||
memset( buf, 0, NE_ABUFSIZ );
|
||||
(*authcb) ("Enter your username: ", buf, NE_ABUFSIZ-1, 1, 0, csync_get_userdata(ctx->csync_ctx) );
|
||||
if( strlen(buf) < NE_ABUFSIZ ) {
|
||||
strcpy( username, buf );
|
||||
}
|
||||
memset( buf, 0, NE_ABUFSIZ );
|
||||
(*authcb) ("Enter your password: ", buf, NE_ABUFSIZ-1, 0, 0, csync_get_userdata(ctx->csync_ctx) );
|
||||
if( strlen(buf) < NE_ABUFSIZ) {
|
||||
strcpy( password, buf );
|
||||
}
|
||||
} else {
|
||||
re = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
/*
|
||||
* Authentication callback. Is set by ne_set_proxy_auth to be called
|
||||
* from the neon lib to authenticate against a proxy. The data to authenticate
|
||||
* against comes from mirall throught vio_module_init function.
|
||||
*/
|
||||
static int proxy_authentication_callback_by_neon( void *userdata, const char *realm, int attempt,
|
||||
char *username, char *password)
|
||||
{
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
|
||||
(void) realm;
|
||||
if( ctx->dav_session.proxy_user && strlen( ctx->dav_session.proxy_user ) < NE_ABUFSIZ) {
|
||||
strcpy( username, ctx->dav_session.proxy_user );
|
||||
if( ctx->dav_session.proxy_pwd && strlen( ctx->dav_session.proxy_pwd ) < NE_ABUFSIZ) {
|
||||
strcpy( password, ctx->dav_session.proxy_pwd );
|
||||
}
|
||||
}
|
||||
/* NTLM needs several attempts */
|
||||
return (attempt < 3) ? 0 : -1;
|
||||
}
|
||||
|
||||
/* Configure the proxy depending on the variables */
|
||||
static int configureProxy( csync_owncloud_ctx_t *ctx, ne_session *session )
|
||||
{
|
||||
int port = 8080;
|
||||
int re = -1;
|
||||
|
||||
if( ! session ) return -1;
|
||||
if( ! ctx->dav_session.proxy_type ) return 0; /* Go by NoProxy per default */
|
||||
|
||||
if( ctx->dav_session.proxy_port > 0 ) {
|
||||
port = ctx->dav_session.proxy_port;
|
||||
}
|
||||
|
||||
if( c_streq(ctx->dav_session.proxy_type, "NoProxy" )) {
|
||||
DEBUG_WEBDAV("No proxy configured.");
|
||||
re = 0;
|
||||
} else if( c_streq(ctx->dav_session.proxy_type, "DefaultProxy") ||
|
||||
c_streq(ctx->dav_session.proxy_type, "HttpProxy") ||
|
||||
c_streq(ctx->dav_session.proxy_type, "HttpCachingProxy") ||
|
||||
c_streq(ctx->dav_session.proxy_type, "Socks5Proxy")) {
|
||||
|
||||
if( ctx->dav_session.proxy_host ) {
|
||||
DEBUG_WEBDAV("%s at %s:%d", ctx->dav_session.proxy_type, ctx->dav_session.proxy_host, port );
|
||||
if (c_streq(ctx->dav_session.proxy_type, "Socks5Proxy")) {
|
||||
ne_session_socks_proxy(session, NE_SOCK_SOCKSV5, ctx->dav_session.proxy_host, port,
|
||||
ctx->dav_session.proxy_user, ctx->dav_session.proxy_pwd);
|
||||
} else {
|
||||
ne_session_proxy(session, ctx->dav_session.proxy_host, port );
|
||||
}
|
||||
re = 2;
|
||||
} else {
|
||||
DEBUG_WEBDAV("%s requested but no proxy host defined.", ctx->dav_session.proxy_type );
|
||||
/* we used to try ne_system_session_proxy here, but we should rather err out
|
||||
to behave exactly like the caller. */
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBDAV( "Unsupported Proxy: %s", ctx->dav_session.proxy_type );
|
||||
}
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
/*
|
||||
* This hook is called for with the response of a request. Here its checked
|
||||
* if a Set-Cookie header is there for the PHPSESSID. The key is stored into
|
||||
* the webdav session to be added to subsequent requests.
|
||||
*/
|
||||
static void post_request_hook(ne_request *req, void *userdata, const ne_status *status)
|
||||
{
|
||||
const char *set_cookie_header = NULL;
|
||||
const char *sc = NULL;
|
||||
char *key = NULL;
|
||||
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
|
||||
|
||||
if (ctx->dav_session.session_key)
|
||||
return; /* We already have a session cookie, and we should ignore other ones */
|
||||
|
||||
if(!(status && req)) return;
|
||||
if( status->klass == 2 || status->code == 401 ) {
|
||||
/* successful request */
|
||||
set_cookie_header = ne_get_response_header( req, "Set-Cookie" );
|
||||
if( set_cookie_header ) {
|
||||
DEBUG_WEBDAV(" Set-Cookie found: %s", set_cookie_header);
|
||||
/* try to find a ', ' sequence which is the separator of neon if multiple Set-Cookie
|
||||
* headers are there.
|
||||
* The following code parses a string like this:
|
||||
* Set-Cookie: 50ace6bd8a669=p537brtt048jh8srlp2tuep7em95nh9u98mj992fbqc47d1aecp1;
|
||||
*/
|
||||
sc = set_cookie_header;
|
||||
while(sc) {
|
||||
const char *sc_val = sc;
|
||||
const char *sc_end = sc_val;
|
||||
int cnt = 0;
|
||||
int len = strlen(sc_val); /* The length of the rest of the header string. */
|
||||
|
||||
while( cnt < len && *sc_end != ';' && *sc_end != ',') {
|
||||
cnt++;
|
||||
sc_end++;
|
||||
}
|
||||
if( cnt == len ) {
|
||||
/* exit: We are at the end. */
|
||||
sc = NULL;
|
||||
} else if( *sc_end == ';' ) {
|
||||
/* We are at the end of the session key. */
|
||||
int keylen = sc_end-sc_val;
|
||||
if( key ) {
|
||||
int oldlen = strlen(key);
|
||||
key = c_realloc(key, oldlen + 2 + keylen+1);
|
||||
strcpy(key + oldlen, "; ");
|
||||
strncpy(key + oldlen + 2, sc_val, keylen);
|
||||
key[oldlen + 2 + keylen] = '\0';
|
||||
} else {
|
||||
key = c_malloc(keylen+1);
|
||||
strncpy( key, sc_val, keylen );
|
||||
key[keylen] = '\0';
|
||||
}
|
||||
|
||||
/* now search for a ',' to find a potential other header entry */
|
||||
while(cnt < len && *sc_end != ',') {
|
||||
cnt++;
|
||||
sc_end++;
|
||||
}
|
||||
if( cnt < len )
|
||||
sc = sc_end+2; /* mind the space after the comma */
|
||||
else
|
||||
sc = NULL;
|
||||
} else if( *sc_end == ',' ) {
|
||||
/* A new entry is to check. */
|
||||
if( *(sc_end + 1) == ' ') {
|
||||
sc = sc_end+2;
|
||||
} else {
|
||||
/* error condition */
|
||||
sc = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBDAV("Request failed, don't take session header.");
|
||||
}
|
||||
if( key ) {
|
||||
DEBUG_WEBDAV("----> Session-key: %s", key);
|
||||
SAFE_FREE(ctx->dav_session.session_key);
|
||||
ctx->dav_session.session_key = key;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this hook is called just after a request has been created, before its sent.
|
||||
* Here it is used to set the proxy connection header if available.
|
||||
*/
|
||||
static void request_created_hook(ne_request *req, void *userdata,
|
||||
const char *method, const char *requri)
|
||||
{
|
||||
// FIXME Can possibly be merged with pre_send_hook
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
|
||||
(void) method;
|
||||
(void) requri;
|
||||
|
||||
if( !req ) return;
|
||||
|
||||
if(ctx->dav_session.proxy_type) {
|
||||
/* required for NTLM */
|
||||
ne_add_request_header(req, "Proxy-Connection", "Keep-Alive");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this hook is called just before a request has been sent.
|
||||
* Here it is used to set the session cookie if available.
|
||||
*/
|
||||
static void pre_send_hook(ne_request *req, void *userdata,
|
||||
ne_buffer *header)
|
||||
{
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
|
||||
|
||||
if( !req ) return;
|
||||
|
||||
if(ctx->dav_session.session_key) {
|
||||
ne_buffer_concat(header, "Cookie: ", ctx->dav_session.session_key, "\r\n", NULL);
|
||||
} else {
|
||||
DEBUG_WEBDAV("csync pre_send_hook We don't have a Auth Cookie (session_key), this is wrong!");
|
||||
}
|
||||
}
|
||||
|
||||
static int post_send_hook(ne_request *req, void *userdata,
|
||||
const ne_status *status)
|
||||
{
|
||||
const char *location;
|
||||
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
|
||||
(void) status;
|
||||
|
||||
location = ne_get_response_header(req, "Location");
|
||||
|
||||
if( !location ) return NE_OK;
|
||||
|
||||
if( ctx->dav_session.redir_callback ) {
|
||||
if( ctx->dav_session.redir_callback( ctx->csync_ctx, location ) ) {
|
||||
return NE_REDIRECT;
|
||||
} else {
|
||||
return NE_RETRY;
|
||||
}
|
||||
}
|
||||
|
||||
return NE_REDIRECT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Connect to a DAV server
|
||||
* This function sets the flag _connected if the connection is established
|
||||
* and returns if the flag is set, so calling it frequently is save.
|
||||
*/
|
||||
int dav_connect(CSYNC *csyncCtx, const char *base_url) {
|
||||
int useSSL = 0;
|
||||
int rc;
|
||||
char protocol[6] = {'\0'};
|
||||
char uaBuf[256];
|
||||
char *path = NULL;
|
||||
char *scheme = NULL;
|
||||
char *host = NULL;
|
||||
unsigned int port = 0;
|
||||
int proxystate = -1;
|
||||
csync_owncloud_ctx_t *ctx = csyncCtx->owncloud_context;
|
||||
struct csync_client_certs_s* clientCerts = csyncCtx->clientCerts;
|
||||
|
||||
if (ctx->_connected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = c_parse_uri( base_url, &scheme,
|
||||
&ctx->dav_session.user,
|
||||
&ctx->dav_session.pwd,
|
||||
&host, &port, &path );
|
||||
if( rc < 0 ) {
|
||||
DEBUG_WEBDAV("Failed to parse uri %s", base_url );
|
||||
goto out;
|
||||
}
|
||||
|
||||
DEBUG_WEBDAV("* scheme %s", scheme );
|
||||
DEBUG_WEBDAV("* host %s", host );
|
||||
DEBUG_WEBDAV("* port %u", port );
|
||||
DEBUG_WEBDAV("* path %s", path );
|
||||
|
||||
if( strcmp( scheme, "owncloud" ) == 0 || strcmp( scheme, "http" ) == 0 ) {
|
||||
strcpy( protocol, "http");
|
||||
} else if( strcmp( scheme, "ownclouds" ) == 0 || strcmp( scheme, "https") == 0 ) {
|
||||
strcpy( protocol, "https");
|
||||
useSSL = 1;
|
||||
} else {
|
||||
DEBUG_WEBDAV("Invalid scheme %s, go out here!", scheme );
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
DEBUG_WEBDAV("* user %s", ctx->dav_session.user ? ctx->dav_session.user : "");
|
||||
|
||||
if (port == 0) {
|
||||
port = ne_uri_defaultport(protocol);
|
||||
}
|
||||
|
||||
ctx->dav_session.ctx = ne_session_create( protocol, host, port);
|
||||
|
||||
if (ctx->dav_session.ctx == NULL) {
|
||||
DEBUG_WEBDAV("Session create with protocol %s failed", protocol );
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->dav_session.read_timeout != 0) {
|
||||
ne_set_read_timeout(ctx->dav_session.ctx, ctx->dav_session.read_timeout);
|
||||
DEBUG_WEBDAV("Timeout set to %u seconds", ctx->dav_session.read_timeout );
|
||||
}
|
||||
// Should never take more than some seconds, 30 is really a max.
|
||||
ne_set_connect_timeout(ctx->dav_session.ctx, 30);
|
||||
|
||||
snprintf( uaBuf, sizeof(uaBuf), "Mozilla/5.0 (%s) mirall/%s (csyncoC)",
|
||||
CSYNC_STRINGIFY( MIRALL_VERSION ), csync_owncloud_get_platform() );
|
||||
ne_set_useragent( ctx->dav_session.ctx, uaBuf);
|
||||
ne_set_server_auth(ctx->dav_session.ctx, authentication_callback_by_neon, ctx);
|
||||
|
||||
if( useSSL ) {
|
||||
if (!ne_has_support(NE_FEATURE_SSL)) {
|
||||
DEBUG_WEBDAV("Error: SSL is not enabled.");
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(clientCerts != NULL) {
|
||||
ne_ssl_client_cert *clicert;
|
||||
|
||||
DEBUG_WEBDAV("dav_connect: certificatePath and certificatePasswd are set, so we use it" );
|
||||
DEBUG_WEBDAV(" with certificatePath: %s", clientCerts->certificatePath );
|
||||
DEBUG_WEBDAV(" with certificatePasswd: %s", clientCerts->certificatePasswd );
|
||||
clicert = ne_ssl_clicert_read ( clientCerts->certificatePath );
|
||||
if ( clicert == NULL ) {
|
||||
DEBUG_WEBDAV ( "Error read certificate : %s", ne_get_error ( ctx->dav_session.ctx ) );
|
||||
} else {
|
||||
if ( ne_ssl_clicert_encrypted ( clicert ) ) {
|
||||
int rtn = ne_ssl_clicert_decrypt ( clicert, clientCerts->certificatePasswd );
|
||||
if ( !rtn ) {
|
||||
DEBUG_WEBDAV ( "Certificate was deciphered successfully." );
|
||||
ne_ssl_set_clicert ( ctx->dav_session.ctx, clicert );
|
||||
} else {
|
||||
DEBUG_WEBDAV ( "Errors while deciphering certificate: %s", ne_get_error ( ctx->dav_session.ctx ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DEBUG_WEBDAV("dav_connect: error with csync_client_certs_s* clientCerts");
|
||||
}
|
||||
ne_ssl_trust_default_ca( ctx->dav_session.ctx );
|
||||
ne_ssl_set_verify( ctx->dav_session.ctx, ssl_callback_by_neon, ctx);
|
||||
}
|
||||
|
||||
/* Hook called when a request is created. It sets the proxy connection header. */
|
||||
ne_hook_create_request( ctx->dav_session.ctx, request_created_hook, ctx );
|
||||
/* Hook called after response headers are read. It gets the Session ID. */
|
||||
ne_hook_post_headers( ctx->dav_session.ctx, post_request_hook, ctx );
|
||||
/* Hook called before a request is sent. It sets the cookies. */
|
||||
ne_hook_pre_send( ctx->dav_session.ctx, pre_send_hook, ctx );
|
||||
/* Hook called after request is dispatched. Used for handling possible redirections. */
|
||||
ne_hook_post_send( ctx->dav_session.ctx, post_send_hook, ctx );
|
||||
|
||||
/* Proxy support */
|
||||
proxystate = configureProxy( ctx, ctx->dav_session.ctx );
|
||||
if( proxystate < 0 ) {
|
||||
DEBUG_WEBDAV("Error: Proxy-Configuration failed.");
|
||||
} else if( proxystate > 0 ) {
|
||||
ne_set_proxy_auth( ctx->dav_session.ctx, proxy_authentication_callback_by_neon, ctx );
|
||||
}
|
||||
|
||||
ctx->_connected = 1;
|
||||
rc = 0;
|
||||
out:
|
||||
SAFE_FREE(path);
|
||||
SAFE_FREE(host);
|
||||
SAFE_FREE(scheme);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
char *owncloud_error_string(CSYNC* ctx)
|
||||
{
|
||||
return ctx->owncloud_context->dav_session.error_string;
|
||||
}
|
||||
|
||||
int owncloud_commit(CSYNC* ctx) {
|
||||
if (!ctx->owncloud_context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( ctx->owncloud_context->dav_session.ctx ) {
|
||||
ne_forget_auth(ctx->owncloud_context->dav_session.ctx);
|
||||
ne_session_destroy(ctx->owncloud_context->dav_session.ctx );
|
||||
ctx->owncloud_context->dav_session.ctx = 0;
|
||||
}
|
||||
|
||||
/* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */
|
||||
|
||||
ctx->owncloud_context->dav_session.ctx = 0;
|
||||
|
||||
// ne_sock_exit();
|
||||
ctx->owncloud_context->_connected = 0; /* triggers dav_connect to go through the whole neon setup */
|
||||
|
||||
SAFE_FREE( ctx->owncloud_context->dav_session.user );
|
||||
SAFE_FREE( ctx->owncloud_context->dav_session.pwd );
|
||||
SAFE_FREE( ctx->owncloud_context->dav_session.session_key);
|
||||
SAFE_FREE( ctx->owncloud_context->dav_session.error_string );
|
||||
return 0;
|
||||
}
|
||||
|
||||
void owncloud_destroy(CSYNC* ctx)
|
||||
{
|
||||
owncloud_commit(ctx);
|
||||
SAFE_FREE(ctx->owncloud_context);
|
||||
|
||||
if (ctx->clientCerts) {
|
||||
SAFE_FREE(ctx->clientCerts->certificatePasswd);
|
||||
SAFE_FREE(ctx->clientCerts->certificatePath);
|
||||
SAFE_FREE(ctx->clientCerts);
|
||||
}
|
||||
ne_sock_exit();
|
||||
}
|
||||
|
||||
int owncloud_set_property(CSYNC* ctx, const char *key, void *data) {
|
||||
#define READ_STRING_PROPERTY(P) \
|
||||
if (c_streq(key, #P)) { \
|
||||
SAFE_FREE(ctx->owncloud_context->dav_session.P); \
|
||||
ctx->owncloud_context->dav_session.P = c_strdup((const char*)data); \
|
||||
return 0; \
|
||||
}
|
||||
READ_STRING_PROPERTY(session_key)
|
||||
READ_STRING_PROPERTY(proxy_type)
|
||||
READ_STRING_PROPERTY(proxy_host)
|
||||
READ_STRING_PROPERTY(proxy_user)
|
||||
READ_STRING_PROPERTY(proxy_pwd)
|
||||
#undef READ_STRING_PROPERTY
|
||||
|
||||
if (c_streq(key, "proxy_port")) {
|
||||
ctx->owncloud_context->dav_session.proxy_port = *(int*)(data);
|
||||
return 0;
|
||||
}
|
||||
if (c_streq(key, "read_timeout") || c_streq(key, "timeout")) {
|
||||
ctx->owncloud_context->dav_session.read_timeout = *(int*)(data);
|
||||
return 0;
|
||||
}
|
||||
if( c_streq(key, "get_dav_session")) {
|
||||
/* Give the ne_session to the caller */
|
||||
*(ne_session**)data = ctx->owncloud_context->dav_session.ctx;
|
||||
return 0;
|
||||
}
|
||||
if( c_streq(key, "redirect_callback")) {
|
||||
if (data) {
|
||||
csync_owncloud_redirect_callback_t* cb_wrapper = data;
|
||||
ctx->owncloud_context->dav_session.redir_callback = *cb_wrapper;
|
||||
} else {
|
||||
ctx->owncloud_context->dav_session.redir_callback = NULL;
|
||||
}
|
||||
}
|
||||
if( c_streq(key, "SSLClientCerts")) {
|
||||
if(ctx->clientCerts != NULL) {
|
||||
SAFE_FREE(ctx->clientCerts->certificatePasswd);
|
||||
SAFE_FREE(ctx->clientCerts->certificatePath);
|
||||
SAFE_FREE(ctx->clientCerts);
|
||||
ctx->clientCerts = NULL;
|
||||
}
|
||||
if (data) {
|
||||
struct csync_client_certs_s* clientCerts = (struct csync_client_certs_s*) data;
|
||||
struct csync_client_certs_s* newCerts = c_malloc(sizeof(struct csync_client_certs_s));
|
||||
newCerts->certificatePath = c_strdup(clientCerts->certificatePath);
|
||||
newCerts->certificatePasswd = c_strdup(clientCerts->certificatePasswd);
|
||||
ctx->clientCerts = newCerts;
|
||||
} else {
|
||||
DEBUG_WEBDAV("error: in owncloud_set_property for 'SSLClientCerts'" );
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void owncloud_init(CSYNC* ctx) {
|
||||
|
||||
ne_sock_init();
|
||||
|
||||
ctx->owncloud_context = c_malloc( sizeof( struct csync_owncloud_ctx_s ));
|
||||
ctx->owncloud_context->csync_ctx = ctx; // back reference
|
||||
|
||||
}
|
||||
|
||||
/* vim: set ts=4 sw=4 et cindent: */
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
|
||||
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#ifndef CSYNC_OWNCLOUD_H
|
||||
#define CSYNC_OWNCLOUD_H
|
||||
|
||||
#include "csync.h"
|
||||
#include "vio/csync_vio.h"
|
||||
|
||||
// Public API used by csync
|
||||
int owncloud_commit(CSYNC* ctx);
|
||||
void owncloud_destroy(CSYNC* ctx);
|
||||
char *owncloud_error_string(CSYNC* ctx);
|
||||
int owncloud_set_property(CSYNC* ctx, const char *key, void *data);
|
||||
void owncloud_init(CSYNC* ctx);
|
||||
|
||||
int dav_connect(CSYNC* ctx, const char *base_url);
|
||||
|
||||
#endif /* CSYNC_OWNCLOUD_H */
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
|
||||
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef CSYNC_OWNCLOUD_PRIVATE_H
|
||||
#define CSYNC_OWNCLOUD_PRIVATE_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "config_csync.h"
|
||||
#ifdef NEON_WITH_LFS /* Switch on LFS in libneon. Never remove the NE_LFS! */
|
||||
#define NE_LFS
|
||||
#endif
|
||||
|
||||
#include <neon/ne_basic.h>
|
||||
#include <neon/ne_socket.h>
|
||||
#include <neon/ne_session.h>
|
||||
#include <neon/ne_request.h>
|
||||
#include <neon/ne_props.h>
|
||||
#include <neon/ne_auth.h>
|
||||
#include <neon/ne_dates.h>
|
||||
#include <neon/ne_compress.h>
|
||||
#include <neon/ne_redirect.h>
|
||||
|
||||
|
||||
#include "c_rbtree.h"
|
||||
|
||||
#include "c_lib.h"
|
||||
#include "csync.h"
|
||||
#include "csync_misc.h"
|
||||
#include "csync_macros.h"
|
||||
#include "c_private.h"
|
||||
|
||||
#include "vio/csync_vio.h"
|
||||
|
||||
#include "csync_log.h"
|
||||
|
||||
#include "csync_owncloud.h"
|
||||
|
||||
|
||||
#define DEBUG_WEBDAV(...) csync_log( 9, "oc_module", __VA_ARGS__);
|
||||
|
||||
typedef int (*csync_owncloud_redirect_callback_t)(CSYNC* ctx, const char* uri);
|
||||
|
||||
/* Struct with the WebDAV session */
|
||||
struct dav_session_s {
|
||||
ne_session *ctx;
|
||||
char *user;
|
||||
char *pwd;
|
||||
|
||||
char *proxy_type;
|
||||
char *proxy_host;
|
||||
int proxy_port;
|
||||
char *proxy_user;
|
||||
char *proxy_pwd;
|
||||
|
||||
char *session_key;
|
||||
|
||||
char *error_string;
|
||||
|
||||
int read_timeout;
|
||||
|
||||
csync_owncloud_redirect_callback_t redir_callback;
|
||||
};
|
||||
|
||||
struct csync_owncloud_ctx_s {
|
||||
CSYNC *csync_ctx;
|
||||
|
||||
// For the WebDAV connection
|
||||
struct dav_session_s dav_session; /* The DAV Session, initialised in dav_connect */
|
||||
int _connected; /* flag to indicate if a connection exists, ie.
|
||||
the dav_session is valid */
|
||||
};
|
||||
|
||||
typedef struct csync_owncloud_ctx_s csync_owncloud_ctx_t;
|
||||
//typedef csync_owncloud_ctx_t* csync_owncloud_ctx_p;
|
||||
|
||||
void set_errno_from_http_errcode( int err );
|
||||
void set_error_message( csync_owncloud_ctx_t *ctx, const char *msg );
|
||||
void set_errno_from_neon_errcode(csync_owncloud_ctx_t *ctx, int neon_code );
|
||||
int http_result_code_from_session(csync_owncloud_ctx_t *ctx);
|
||||
void set_errno_from_session(csync_owncloud_ctx_t *ctx);
|
||||
|
||||
time_t oc_httpdate_parse( const char *date );
|
||||
|
||||
const char* csync_owncloud_get_platform(void);
|
||||
|
||||
char *_cleanPath( const char* uri );
|
||||
|
||||
#endif // CSYNC_OWNCLOUD_PRIVATE_H
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
|
||||
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "csync_owncloud.h"
|
||||
#include "csync_owncloud_private.h"
|
||||
|
||||
#include "csync_misc.h"
|
||||
|
||||
void set_errno_from_http_errcode( int err ) {
|
||||
int new_errno = 0;
|
||||
|
||||
switch(err) {
|
||||
case 200: /* OK */
|
||||
case 201: /* Created */
|
||||
case 202: /* Accepted */
|
||||
case 203: /* Non-Authoritative Information */
|
||||
case 204: /* No Content */
|
||||
case 205: /* Reset Content */
|
||||
case 207: /* Multi-Status */
|
||||
case 304: /* Not Modified */
|
||||
new_errno = 0;
|
||||
break;
|
||||
case 401: /* Unauthorized */
|
||||
case 402: /* Payment Required */
|
||||
case 407: /* Proxy Authentication Required */
|
||||
case 405:
|
||||
new_errno = EPERM;
|
||||
break;
|
||||
case 301: /* Moved Permanently */
|
||||
case 303: /* See Other */
|
||||
case 404: /* Not Found */
|
||||
case 410: /* Gone */
|
||||
new_errno = ENOENT;
|
||||
break;
|
||||
case 408: /* Request Timeout */
|
||||
case 504: /* Gateway Timeout */
|
||||
new_errno = EAGAIN;
|
||||
break;
|
||||
case 423: /* Locked */
|
||||
new_errno = EACCES;
|
||||
break;
|
||||
case 400: /* Bad Request */
|
||||
case 403: /* Forbidden */
|
||||
case 409: /* Conflict */
|
||||
case 411: /* Length Required */
|
||||
case 412: /* Precondition Failed */
|
||||
case 414: /* Request-URI Too Long */
|
||||
case 415: /* Unsupported Media Type */
|
||||
case 424: /* Failed Dependency */
|
||||
case 501: /* Not Implemented */
|
||||
new_errno = EINVAL;
|
||||
break;
|
||||
case 507: /* Insufficient Storage */
|
||||
new_errno = ENOSPC;
|
||||
break;
|
||||
case 206: /* Partial Content */
|
||||
case 300: /* Multiple Choices */
|
||||
case 302: /* Found */
|
||||
case 305: /* Use Proxy */
|
||||
case 306: /* (Unused) */
|
||||
case 307: /* Temporary Redirect */
|
||||
case 406: /* Not Acceptable */
|
||||
case 416: /* Requested Range Not Satisfiable */
|
||||
case 417: /* Expectation Failed */
|
||||
case 422: /* Unprocessable Entity */
|
||||
case 500: /* Internal Server Error */
|
||||
case 502: /* Bad Gateway */
|
||||
case 505: /* HTTP Version Not Supported */
|
||||
new_errno = EIO;
|
||||
break;
|
||||
case 503: /* Service Unavailable */
|
||||
new_errno = ERRNO_SERVICE_UNAVAILABLE;
|
||||
break;
|
||||
case 413: /* Request Entity too Large */
|
||||
new_errno = EFBIG;
|
||||
break;
|
||||
default:
|
||||
new_errno = EIO;
|
||||
}
|
||||
|
||||
errno = new_errno;
|
||||
}
|
||||
|
||||
// as per http://sourceforge.net/p/predef/wiki/OperatingSystems/
|
||||
// extend as required
|
||||
const char* csync_owncloud_get_platform() {
|
||||
#if defined (_WIN32)
|
||||
return "Windows";
|
||||
#elif defined(__APPLE__)
|
||||
return "Macintosh";
|
||||
#elif defined(__gnu_linux__)
|
||||
return "Linux";
|
||||
#elif defined(__DragonFly__)
|
||||
/* might also define __FreeBSD__ */
|
||||
return "DragonFlyBSD";
|
||||
#elif defined(__FreeBSD__)
|
||||
return "FreeBSD";
|
||||
#elif defined(__NetBSD__)
|
||||
return "NetBSD";
|
||||
#elif defined(__OpenBSD__)
|
||||
return "OpenBSD";
|
||||
#elif defined(sun) || defined(__sun)
|
||||
return "Solaris";
|
||||
#else
|
||||
return "Unknown OS";
|
||||
#endif
|
||||
}
|
||||
@@ -77,9 +77,6 @@ enum csync_replica_e {
|
||||
|
||||
typedef struct csync_file_stat_s csync_file_stat_t;
|
||||
|
||||
struct csync_owncloud_ctx_s; // csync_owncloud.c
|
||||
|
||||
|
||||
/**
|
||||
* @brief csync public structure
|
||||
*/
|
||||
@@ -99,6 +96,11 @@ struct csync_s {
|
||||
csync_vio_readdir_hook remote_readdir_hook;
|
||||
csync_vio_closedir_hook remote_closedir_hook;
|
||||
void *vio_userdata;
|
||||
|
||||
/* hook for comparing checksums of files during discovery */
|
||||
csync_checksum_hook checksum_hook;
|
||||
void *checksum_userdata;
|
||||
|
||||
} callbacks;
|
||||
c_strlist_t *excludes;
|
||||
|
||||
@@ -169,9 +171,6 @@ struct csync_s {
|
||||
bool db_is_empty;
|
||||
|
||||
bool ignore_hidden_files;
|
||||
|
||||
struct csync_owncloud_ctx_s *owncloud_context;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -198,6 +197,9 @@ struct csync_file_stat_s {
|
||||
char *directDownloadCookies;
|
||||
char remotePerm[REMOTE_PERM_BUF_SIZE+1];
|
||||
|
||||
const char *checksum;
|
||||
uint32_t checksumTypeId;
|
||||
|
||||
CSYNC_STATUS error_status;
|
||||
|
||||
enum csync_instructions_e instruction; /* u32 */
|
||||
|
||||
@@ -226,6 +226,8 @@ int csync_statedb_close(CSYNC *ctx) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define METADATA_COLUMNS "phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote, contentChecksum, contentChecksumTypeId"
|
||||
|
||||
// This funciton parses a line from the metadata table into the given csync_file_stat
|
||||
// structure which it is also allocating.
|
||||
// Note that this function calls laso sqlite3_step to actually get the info from db and
|
||||
@@ -286,6 +288,11 @@ static int _csync_file_stat_from_metadata_table( csync_file_stat_t **st, sqlite3
|
||||
if(column_count > 13) {
|
||||
(*st)->has_ignored_files = sqlite3_column_int(stmt, 13);
|
||||
}
|
||||
if(column_count > 15 && sqlite3_column_int(stmt, 15)) {
|
||||
(*st)->checksum = c_strdup( (char*) sqlite3_column_text(stmt, 14));
|
||||
(*st)->checksumTypeId = sqlite3_column_int(stmt, 15);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if( rc != SQLITE_DONE ) {
|
||||
@@ -307,7 +314,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_hash(CSYNC *ctx,
|
||||
}
|
||||
|
||||
if( ctx->statedb.by_hash_stmt == NULL ) {
|
||||
const char *hash_query = "SELECT * FROM metadata WHERE phash=?1";
|
||||
const char *hash_query = "SELECT " METADATA_COLUMNS " FROM metadata WHERE phash=?1";
|
||||
|
||||
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, hash_query, strlen(hash_query), &ctx->statedb.by_hash_stmt, NULL));
|
||||
ctx->statedb.lastReturnValue = rc;
|
||||
@@ -350,7 +357,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_file_id(CSYNC *ctx,
|
||||
}
|
||||
|
||||
if( ctx->statedb.by_fileid_stmt == NULL ) {
|
||||
const char *query = "SELECT * FROM metadata WHERE fileid=?1";
|
||||
const char *query = "SELECT " METADATA_COLUMNS " FROM metadata WHERE fileid=?1";
|
||||
|
||||
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, query, strlen(query), &ctx->statedb.by_fileid_stmt, NULL));
|
||||
ctx->statedb.lastReturnValue = rc;
|
||||
@@ -390,7 +397,7 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
|
||||
}
|
||||
|
||||
if( ctx->statedb.by_inode_stmt == NULL ) {
|
||||
const char *inode_query = "SELECT * FROM metadata WHERE inode=?1";
|
||||
const char *inode_query = "SELECT " METADATA_COLUMNS " FROM metadata WHERE inode=?1";
|
||||
|
||||
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, inode_query, strlen(inode_query), &ctx->statedb.by_inode_stmt, NULL));
|
||||
ctx->statedb.lastReturnValue = rc;
|
||||
@@ -416,37 +423,10 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
|
||||
return st;
|
||||
}
|
||||
|
||||
/* Get the etag. */
|
||||
char *csync_statedb_get_etag( CSYNC *ctx, uint64_t jHash ) {
|
||||
char *ret = NULL;
|
||||
csync_file_stat_t *fs = NULL;
|
||||
|
||||
if( !ctx ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( ! csync_get_statedb_exists(ctx)) return ret;
|
||||
|
||||
fs = csync_statedb_get_stat_by_hash(ctx, jHash );
|
||||
if( fs ) {
|
||||
if( fs->etag ) {
|
||||
ret = c_strdup(fs->etag);
|
||||
}
|
||||
csync_file_stat_free(fs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define BELOW_PATH_QUERY "SELECT phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM metadata WHERE pathlen>? AND path LIKE(?)"
|
||||
|
||||
int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||
int rc;
|
||||
sqlite3_stmt *stmt = NULL;
|
||||
int64_t cnt = 0;
|
||||
char *likepath;
|
||||
int asp;
|
||||
int min_path_len;
|
||||
|
||||
if( !path ) {
|
||||
return -1;
|
||||
@@ -456,7 +436,12 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, BELOW_PATH_QUERY, -1, &stmt, NULL));
|
||||
/* Select the entries for anything that starts with (path+'/')
|
||||
* In other words, anything that is between path+'/' and path+'0',
|
||||
* (because '0' follows '/' in ascii)
|
||||
*/
|
||||
const char *below_path_query = "SELECT " METADATA_COLUMNS " FROM metadata WHERE path > (?||'/') AND path < (?||'0')";
|
||||
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL));
|
||||
ctx->statedb.lastReturnValue = rc;
|
||||
if( rc != SQLITE_OK ) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query.");
|
||||
@@ -467,15 +452,8 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
asp = asprintf( &likepath, "%s/%%%%", path);
|
||||
if (asp < 0) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
min_path_len = strlen(path);
|
||||
sqlite3_bind_int(stmt, 1, min_path_len);
|
||||
sqlite3_bind_text(stmt, 2, likepath, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC);
|
||||
sqlite3_bind_text(stmt, 2, path, -1, SQLITE_STATIC);
|
||||
|
||||
cnt = 0;
|
||||
|
||||
@@ -518,7 +496,6 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
SAFE_FREE(likepath);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -270,29 +270,32 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
|
||||
((int64_t) fs->mtime), ((int64_t) tmp->modtime),
|
||||
fs->etag, tmp->etag, (uint64_t) fs->inode, (uint64_t) tmp->inode,
|
||||
(uint64_t) fs->size, (uint64_t) tmp->size, fs->remotePerm, tmp->remotePerm, tmp->has_ignored_files );
|
||||
if( !fs->etag) {
|
||||
if (ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag)) {
|
||||
st->instruction = CSYNC_INSTRUCTION_EVAL;
|
||||
goto out;
|
||||
}
|
||||
if((ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag ))
|
||||
|| (ctx->current == LOCAL_REPLICA && (!_csync_mtime_equal(fs->mtime, tmp->modtime)
|
||||
// zero size in statedb can happen during migration
|
||||
|| (tmp->size != 0 && fs->size != tmp->size)
|
||||
#if 0
|
||||
|| fs->inode != tmp->inode
|
||||
#endif
|
||||
))) {
|
||||
/* Comparison of the local inode is disabled because people reported problems
|
||||
* on windows with flacky inode values, see github bug #779
|
||||
*
|
||||
* The inode needs to be observed because:
|
||||
* $> echo a > a.txt ; echo b > b.txt
|
||||
* both files have the same mtime
|
||||
* sync them.
|
||||
* $> rm a.txt && mv b.txt a.txt
|
||||
* makes b.txt appearing as a.txt yet a sync is not performed because
|
||||
* both have the same modtime as mv does not change that.
|
||||
*/
|
||||
if (ctx->current == LOCAL_REPLICA &&
|
||||
(!_csync_mtime_equal(fs->mtime, tmp->modtime)
|
||||
// zero size in statedb can happen during migration
|
||||
|| (tmp->size != 0 && fs->size != tmp->size))) {
|
||||
|
||||
if (fs->size == tmp->size && tmp->checksumTypeId) {
|
||||
if (ctx->callbacks.checksum_hook) {
|
||||
st->checksum = ctx->callbacks.checksum_hook(
|
||||
file, tmp->checksumTypeId,
|
||||
ctx->callbacks.checksum_userdata);
|
||||
}
|
||||
bool checksumIdentical = false;
|
||||
if (st->checksum) {
|
||||
st->checksumTypeId = tmp->checksumTypeId;
|
||||
checksumIdentical = strncmp(st->checksum, tmp->checksum, 1000) == 0;
|
||||
}
|
||||
if (checksumIdentical) {
|
||||
st->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
st->should_update_metadata = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
st->instruction = CSYNC_INSTRUCTION_EVAL;
|
||||
goto out;
|
||||
}
|
||||
@@ -577,6 +580,26 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* set the current item to an ignored state.
|
||||
* If the item is set to ignored, the update phase continues, ie. its not a hard error */
|
||||
static bool mark_current_item_ignored( CSYNC *ctx, csync_file_stat_t *previous_fs, CSYNC_STATUS status )
|
||||
{
|
||||
if(!ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ctx->current_fs) {
|
||||
ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
ctx->current_fs->error_status = status;
|
||||
/* If a directory has ignored files, put the flag on the parent directory as well */
|
||||
if( previous_fs ) {
|
||||
previous_fs->has_ignored_files = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* File tree walker */
|
||||
int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
unsigned int depth) {
|
||||
@@ -584,7 +607,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
char *d_name = NULL;
|
||||
csync_vio_handle_t *dh = NULL;
|
||||
csync_vio_file_stat_t *dirent = NULL;
|
||||
csync_vio_file_stat_t *fs = NULL;
|
||||
csync_file_stat_t *previous_fs = NULL;
|
||||
int read_from_db = 0;
|
||||
int rc = 0;
|
||||
@@ -630,13 +652,8 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
/* permission denied */
|
||||
ctx->status_code = csync_errno_to_status(errno, CSYNC_STATUS_OPENDIR_ERROR);
|
||||
if (errno == EACCES) {
|
||||
if (ctx->current_fs) {
|
||||
ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
ctx->current_fs->error_status = CSYNC_STATUS_PERMISSION_DENIED;
|
||||
/* If a directory has ignored files, put the flag on the parent directory as well */
|
||||
if( previous_fs ) {
|
||||
previous_fs->has_ignored_files = true;
|
||||
}
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Permission denied.");
|
||||
if (mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_PERMISSION_DENIED)) {
|
||||
goto done;
|
||||
}
|
||||
} else if(errno == ENOENT) {
|
||||
@@ -645,19 +662,22 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!");
|
||||
}
|
||||
}
|
||||
// 403 Forbidden can be sent by the server if the file firewall is active.
|
||||
// A file or directory should be ignored and sync must continue. See #3490
|
||||
else if(errno == ERRNO_FORBIDDEN) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Directory access Forbidden (File Firewall?)");
|
||||
if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_FORBIDDEN) ) {
|
||||
goto done;
|
||||
}
|
||||
/* if current_fs is not defined here, better throw an error */
|
||||
}
|
||||
// The server usually replies with the custom "503 Storage not available"
|
||||
// if some path is temporarily unavailable. But in some cases a standard 503
|
||||
// is returned too. Thus we can't distinguish the two and will treat any
|
||||
// 503 as request to ignore the folder. See #3113 #2884.
|
||||
else if(errno == ERRNO_STORAGE_UNAVAILABLE || errno == ERRNO_SERVICE_UNAVAILABLE) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_WARN, "Storage was not available!");
|
||||
if (ctx->current_fs) {
|
||||
ctx->current_fs->instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
ctx->current_fs->error_status = CSYNC_STATUS_STORAGE_UNAVAILABLE;
|
||||
/* If a directory has ignored files, put the flag on the parent directory as well */
|
||||
if( previous_fs ) {
|
||||
previous_fs->has_ignored_files = true;
|
||||
}
|
||||
if( mark_current_item_ignored(ctx, previous_fs, CSYNC_STATUS_STORAGE_UNAVAILABLE ) ) {
|
||||
goto done;
|
||||
}
|
||||
/* if current_fs is not defined here, better throw an error */
|
||||
@@ -688,8 +708,8 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
}
|
||||
|
||||
/* skip "." and ".." */
|
||||
if (d_name[0] == '.' && (d_name[1] == '\0'
|
||||
|| (d_name[1] == '.' && d_name[2] == '\0'))) {
|
||||
if ( (d_name[0] == '.' && d_name[1] == '\0')
|
||||
|| (d_name[0] == '.' && d_name[1] == '.' && d_name[2] == '\0')) {
|
||||
csync_vio_file_stat_destroy(dirent);
|
||||
dirent = NULL;
|
||||
continue;
|
||||
@@ -741,15 +761,21 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
|
||||
/* Only for the local replica we have to stat(), for the remote one we have all data already */
|
||||
if (ctx->replica == LOCAL_REPLICA) {
|
||||
fs = csync_vio_file_stat_new();
|
||||
res = csync_vio_stat(ctx, filename, fs);
|
||||
res = csync_vio_stat(ctx, filename, dirent);
|
||||
} else {
|
||||
fs = dirent;
|
||||
res = 0;
|
||||
}
|
||||
|
||||
/* if the filename starts with a . we consider it a hidden file
|
||||
* For windows, the hidden state is also discovered within the vio
|
||||
* local stat function.
|
||||
*/
|
||||
if( d_name[0] == '.' ) {
|
||||
dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
|
||||
}
|
||||
|
||||
if( res == 0) {
|
||||
switch (fs->type) {
|
||||
switch (dirent->type) {
|
||||
case CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK:
|
||||
flag = CSYNC_FTW_FLAG_SLINK;
|
||||
break;
|
||||
@@ -772,42 +798,12 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
flag = CSYNC_FTW_FLAG_NSTAT;
|
||||
}
|
||||
|
||||
if( ctx->current == LOCAL_REPLICA ) {
|
||||
char *etag = NULL;
|
||||
int len = strlen( path );
|
||||
uint64_t h = c_jhash64((uint8_t *) path, len, 0);
|
||||
etag = csync_statedb_get_etag( ctx, h );
|
||||
|
||||
if(_last_db_return_error(ctx)) {
|
||||
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
|
||||
SAFE_FREE(etag);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if( etag ) {
|
||||
SAFE_FREE(fs->etag);
|
||||
fs->etag = etag;
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ETAG;
|
||||
|
||||
if( c_streq(etag, "")) {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Uniq ID from Database is EMPTY: %s", path);
|
||||
} else {
|
||||
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Uniq ID from Database: %s -> %s", path, fs->etag ? fs->etag : "<NULL>" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
previous_fs = ctx->current_fs;
|
||||
|
||||
/* Call walker function for each file */
|
||||
rc = fn(ctx, filename, fs, flag);
|
||||
rc = fn(ctx, filename, dirent, flag);
|
||||
/* this function may update ctx->current and ctx->read_from_db */
|
||||
|
||||
/* Only for the local replica we have to destroy stat(), for the remote one it is a pointer to dirent */
|
||||
if (ctx->replica == LOCAL_REPLICA) {
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
if (CSYNC_STATUS_IS_OK(ctx->status_code)) {
|
||||
ctx->status_code = CSYNC_STATUS_UPDATE_ERROR;
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
project(httpbflib C)
|
||||
|
||||
find_package(Neon)
|
||||
|
||||
|
||||
set(HTTPBF_PUBLIC_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
${NEON_INCLUDE_DIRS}
|
||||
CACHE INTERNAL "httpbflib public include directories"
|
||||
)
|
||||
|
||||
set(HTTPBF_LIBRARY
|
||||
httpbf
|
||||
CACHE INTERNAL "httpbf library"
|
||||
)
|
||||
|
||||
set(HTTPBF_LINK_LIBRARIES
|
||||
${HTTPBF_LIBRARY}
|
||||
)
|
||||
|
||||
set(httpbf_SRCS
|
||||
src/httpbf.c
|
||||
)
|
||||
set(httpbf_HEADERS
|
||||
src/httpbf.h
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${HTTPBF_PUBLIC_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_library(${HTTPBF_LIBRARY} STATIC ${httpbf_SRCS})
|
||||
target_link_libraries(${HTTPBF_LIBRARY} ${NEON_LIBRARIES})
|
||||
|
||||
if(NOT WIN32)
|
||||
add_definitions( -fPIC )
|
||||
endif()
|
||||
|
||||
INSTALL(
|
||||
TARGETS
|
||||
${HTTPBF_LIBRARY}
|
||||
LIBRARY DESTINATION
|
||||
${LIB_INSTALL_DIR}
|
||||
ARCHIVE DESTINATION
|
||||
${LIB_INSTALL_DIR}
|
||||
RUNTIME DESTINATION
|
||||
${BIN_INSTALL_DIR}
|
||||
)
|
||||
|
||||
if (NOT APPLE)
|
||||
INSTALL(
|
||||
FILES
|
||||
${httpbf_HEADERS}
|
||||
DESTINATION
|
||||
${CMAKE_INSTALL_INCLUDEDIR}
|
||||
)
|
||||
endif (NOT APPLE)
|
||||
@@ -1,29 +0,0 @@
|
||||
This is a little code that does ownCloud file chunking.
|
||||
|
||||
Basically to put a local file to an url:
|
||||
(Also see the client example code in client dir.)
|
||||
|
||||
/* Initialize the transfer, get a transfer struct. */
|
||||
hbf_transfer_t *trans = hbf_init_transfer( url );
|
||||
|
||||
Hbf_State state;
|
||||
if( trans ) {
|
||||
int fd = open_local_file( file );
|
||||
|
||||
/* create a neon session to use for the transfer */
|
||||
ne_session *session = create_neon_session(uri);
|
||||
|
||||
if( session && fd > -1 ) {
|
||||
/* Prepare the list of chunks, ie. calculate chunks and write back to trans. */
|
||||
state = hbf_splitlist(trans, fd);
|
||||
|
||||
if( state == HBF_SUCCESS ) {
|
||||
/* Transfer all the chunks through the HTTP session using PUT. */
|
||||
state = hbf_transfer( session, trans, "PUT" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GET a large file:
|
||||
Do GET Range requests.
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
project(client C)
|
||||
|
||||
set(CLIENT_EXECUTABLE httpbfclient CACHE INTERNAL "httpbf client")
|
||||
|
||||
set(CLIENT_LINK_LIBRARIES ${NEON_LIBRARIES} ${HBF_LIBRARY})
|
||||
|
||||
set(HTTPBF_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src})
|
||||
|
||||
if(NOT LINUX)
|
||||
list(APPEND CLIENT_LINK_LIBRARIES ${ARGP_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set(client_SRCS
|
||||
httpbf_client.c
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${HTTPBF_INCLUDE_DIR}
|
||||
${HTTPBF_PUBLIC_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_executable(${CLIENT_EXECUTABLE} ${client_SRCS})
|
||||
|
||||
target_link_libraries(${CLIENT_EXECUTABLE} ${CLIENT_LINK_LIBRARIES})
|
||||
|
||||
set_target_properties(
|
||||
${CLIENT_EXECUTABLE}
|
||||
PROPERTIES
|
||||
OUTPUT_NAME
|
||||
httpbf
|
||||
)
|
||||
|
||||
# install( TARGETS ${CLIENT_EXECUTABLE} DESTINATION ${BIN_INSTALL_DIR} )
|
||||
install(TARGETS ${CLIENT_EXECUTABLE} DESTINATION bin)
|
||||
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* httpbf - send big files via http
|
||||
*
|
||||
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <neon/ne_auth.h>
|
||||
|
||||
#include "httpbf.h"
|
||||
|
||||
/* Program documentation. */
|
||||
static char doc[] = "Usage: httpbf [OPTION...] LOCAL REMOTEDIR\n\
|
||||
httpbf - command line client to upload big files via http.\n\
|
||||
\n\
|
||||
Transfer a big file to a remote directory on ownCloud.\n\
|
||||
\n\
|
||||
-?, --help Give this help list\n\
|
||||
--usage Give a short usage message\n\
|
||||
-V, --version Print program version\n\
|
||||
";
|
||||
|
||||
static char _user[NE_ABUFSIZ];
|
||||
static char _pwd[NE_ABUFSIZ];
|
||||
|
||||
/* The options we understand. */
|
||||
static const struct option long_options[] =
|
||||
{
|
||||
{"version", no_argument, 0, 'V' },
|
||||
{"usage", no_argument, 0, 'h' },
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
static const char* httpbf_version = "0.1";
|
||||
|
||||
static void print_version()
|
||||
{
|
||||
printf( "%s\n", httpbf_version );
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void print_help()
|
||||
{
|
||||
printf( "%s\n", doc );
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int ne_auth( void *userdata, const char *realm, int attempt,
|
||||
char *username, char *password)
|
||||
{
|
||||
(void) userdata;
|
||||
(void) realm;
|
||||
|
||||
if( username && password ) {
|
||||
if( _user ) {
|
||||
/* allow user without password */
|
||||
if( strlen( _user ) < NE_ABUFSIZ ) {
|
||||
strcpy( username, _user );
|
||||
}
|
||||
if( _pwd && strlen( _pwd ) < NE_ABUFSIZ ) {
|
||||
strcpy( password, _pwd );
|
||||
}
|
||||
}
|
||||
}
|
||||
return attempt;
|
||||
}
|
||||
|
||||
|
||||
static int parse_args(int argc, char **argv)
|
||||
{
|
||||
while(optind < argc) {
|
||||
int c = -1;
|
||||
struct option *opt = NULL;
|
||||
int result = getopt_long( argc, argv, "Vh", long_options, &c );
|
||||
|
||||
if( result == -1 ) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch(result) {
|
||||
case 'V':
|
||||
print_version();
|
||||
break;
|
||||
case 'h':
|
||||
print_help();
|
||||
break;
|
||||
case 0:
|
||||
opt = (struct option*)&(long_options[c]);
|
||||
if(strcmp(opt->name, "no-name-yet")) {
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "Argument: No idea what!\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return optind;
|
||||
}
|
||||
|
||||
static ne_session* create_neon_session( const char *url )
|
||||
{
|
||||
ne_uri uri;
|
||||
ne_session *sess = NULL;
|
||||
|
||||
memset( _user, 0, NE_ABUFSIZ );
|
||||
memset( _pwd, 0, NE_ABUFSIZ );
|
||||
|
||||
if( ne_uri_parse( url, &uri ) == 0 ) {
|
||||
unsigned int port = ne_uri_defaultport(uri.scheme);
|
||||
if( uri.userinfo ) {
|
||||
char *slash = NULL;
|
||||
strcpy( _user, uri.userinfo );
|
||||
slash = strchr( _user, ':');
|
||||
if( slash ) {
|
||||
strcpy( _pwd, slash+1);
|
||||
*slash = 0;
|
||||
}
|
||||
}
|
||||
sess = ne_session_create(uri.scheme, uri.host, port);
|
||||
ne_set_server_auth(sess, ne_auth, 0 );
|
||||
|
||||
ne_uri_free(&uri);
|
||||
}
|
||||
return sess;
|
||||
}
|
||||
|
||||
static int open_local_file( const char *file )
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
if( !file ) return -1;
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void transfer( const char* local, const char* uri )
|
||||
{
|
||||
if( !(local && uri )) return;
|
||||
char *whole_url;
|
||||
int len;
|
||||
char *filename = basename(local);
|
||||
if( ! filename ) {
|
||||
return;
|
||||
}
|
||||
|
||||
len = strlen(filename)+strlen(uri)+2;
|
||||
whole_url = malloc( len );
|
||||
strcpy(whole_url, uri);
|
||||
strcat(whole_url, "/");
|
||||
strcat(whole_url, filename);
|
||||
|
||||
hbf_transfer_t *trans = hbf_init_transfer( whole_url );
|
||||
Hbf_State state;
|
||||
|
||||
if( trans ) {
|
||||
ne_session *session = create_neon_session(uri);
|
||||
if( session ) {
|
||||
int fd = open_local_file( local );
|
||||
if( fd > -1 ) {
|
||||
state = hbf_splitlist(trans, fd );
|
||||
if( state == HBF_SUCCESS ) {
|
||||
state = hbf_transfer( session, trans, "PUT" );
|
||||
}
|
||||
}
|
||||
ne_session_destroy(session);
|
||||
}
|
||||
}
|
||||
|
||||
if( state != HBF_SUCCESS ) {
|
||||
printf("Upload failed: %s\n", hbf_error_string(state));
|
||||
printf(" HTTP result %d, Server Error: %s\n",
|
||||
trans->status_code, trans->error_string ? trans->error_string : "<empty>" );
|
||||
}
|
||||
/* Print the result of the recent transfer */
|
||||
hbf_free_transfer( trans );
|
||||
free(whole_url);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int rc = 0;
|
||||
char errbuf[256] = {0};
|
||||
|
||||
parse_args(argc, argv);
|
||||
/* two options must remain as source and target */
|
||||
/* printf("ARGC: %d -> optind: %d\n", argc, optind ); */
|
||||
if( argc - optind < 2 ) {
|
||||
print_help();
|
||||
}
|
||||
|
||||
transfer( argv[optind], argv[optind+1]);
|
||||
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=2 et cindent: */
|
||||
@@ -1,38 +0,0 @@
|
||||
project(httpbf C)
|
||||
|
||||
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
|
||||
|
||||
set(HTTPBF_PUBLIC_INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
CACHE INTERNAL "httpbf public include directories"
|
||||
)
|
||||
|
||||
set(HTTPBFLIB_PRIVATE_INCLUDE_DIRS
|
||||
)
|
||||
|
||||
set(HBF_LIBRARY
|
||||
httpbf
|
||||
CACHE INTERNAL "httpbflib library"
|
||||
)
|
||||
|
||||
set(HTTPBFLIB_LINK_LIBRARIES
|
||||
${HBF_LIBRARY}
|
||||
)
|
||||
|
||||
set(httpbflib_SRCS
|
||||
httpbf.c
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${NEON_INCLUDE_DIRS}
|
||||
${GLIB2_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
add_definitions( -fPIC )
|
||||
endif()
|
||||
|
||||
add_library(${HBF_LIBRARY} SHARED ${httpbflib_SRCS} )
|
||||
|
||||
target_link_libraries(${HBF_LIBRARY} ${NEON_LIBRARIES})
|
||||
|
||||
@@ -1,688 +0,0 @@
|
||||
/*
|
||||
* httpbf - send big files via http
|
||||
*
|
||||
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "httpbf.h"
|
||||
|
||||
#include <neon/ne_session.h>
|
||||
#include <neon/ne_request.h>
|
||||
#include <neon/ne_basic.h>
|
||||
|
||||
// #ifdef NDEBUG
|
||||
// #define DEBUG_HBF(...)
|
||||
// #else
|
||||
#define DEBUG_HBF(...) { if(transfer->log_cb) { \
|
||||
char buf[1024]; \
|
||||
snprintf(buf, 1024, __VA_ARGS__); \
|
||||
transfer->log_cb(__func__, buf, transfer->user_data); \
|
||||
} }
|
||||
|
||||
// #endif
|
||||
|
||||
#define DEFAULT_BLOCK_SIZE (10*1024*1024)
|
||||
|
||||
/* Platform specific defines go here. */
|
||||
#ifdef _WIN32
|
||||
#define _hbf_fstat _fstat64
|
||||
typedef struct stat64 hbf_stat_t;
|
||||
#else
|
||||
#define _hbf_fstat fstat
|
||||
typedef struct stat hbf_stat_t;
|
||||
#endif
|
||||
|
||||
static int transfer_id( hbf_stat_t *sb ) {
|
||||
struct timeval tp;
|
||||
int res;
|
||||
int r;
|
||||
|
||||
if( gettimeofday(&tp, 0) < 0 ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* build a Unique ID:
|
||||
* take the current epoch and shift 8 bits up to keep the least bits.
|
||||
* than add the milliseconds, again shift by 8
|
||||
* and finally add the least 8 bit of the inode of the file.
|
||||
*/
|
||||
res = tp.tv_sec; /* epoche value in seconds */
|
||||
res = res << 8;
|
||||
r = (sb->st_ino & 0xFF);
|
||||
res += r; /* least six bit of inode */
|
||||
res = res << sizeof(tp.tv_usec);
|
||||
res += tp.tv_usec; /* milliseconds */
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
hbf_transfer_t *hbf_init_transfer( const char *dest_uri ) {
|
||||
hbf_transfer_t * transfer = NULL;
|
||||
|
||||
transfer = malloc( sizeof(hbf_transfer_t) );
|
||||
memset(transfer, 0, sizeof(hbf_transfer_t));
|
||||
|
||||
/* store the target uri */
|
||||
transfer->url = strdup(dest_uri);
|
||||
transfer->status_code = 200;
|
||||
transfer->error_string = NULL;
|
||||
transfer->start_id = 0;
|
||||
transfer->block_size = DEFAULT_BLOCK_SIZE;
|
||||
transfer->threshold = transfer->block_size;
|
||||
transfer->modtime_accepted = 0;
|
||||
transfer->oc_header_modtime = 0;
|
||||
|
||||
return transfer;
|
||||
}
|
||||
|
||||
/* Create the splitlist of a given file descriptor */
|
||||
Hbf_State hbf_splitlist(hbf_transfer_t *transfer, int fd ) {
|
||||
hbf_stat_t sb;
|
||||
int64_t num_blocks;
|
||||
int64_t blk_size;
|
||||
int64_t remainder = 0;
|
||||
|
||||
if( ! transfer ) {
|
||||
return HBF_PARAM_FAIL;
|
||||
}
|
||||
|
||||
if( fd <= 0 ) {
|
||||
DEBUG_HBF("File descriptor is invalid.");
|
||||
return HBF_PARAM_FAIL;
|
||||
}
|
||||
|
||||
if( _hbf_fstat(fd, &sb) < 0 ) {
|
||||
DEBUG_HBF("Failed to stat the file descriptor: errno = %d", errno);
|
||||
return HBF_FILESTAT_FAIL;
|
||||
}
|
||||
|
||||
/* Store the file characteristics. */
|
||||
transfer->fd = fd;
|
||||
transfer->stat_size = sb.st_size;
|
||||
transfer->modtime = sb.st_mtime;
|
||||
transfer->previous_etag = NULL;
|
||||
#ifndef NDEBUG
|
||||
transfer->calc_size = 0;
|
||||
#endif
|
||||
|
||||
DEBUG_HBF("block_size: %" PRId64 " threshold: %" PRId64 " st_size: %" PRId64, transfer->block_size, transfer->threshold, sb.st_size );
|
||||
|
||||
|
||||
/* calc the number of blocks to split in */
|
||||
blk_size = transfer->block_size;
|
||||
if (sb.st_size < transfer->threshold) {
|
||||
blk_size = transfer->threshold;
|
||||
}
|
||||
|
||||
num_blocks = sb.st_size / blk_size;
|
||||
|
||||
/* there migth be a remainder. */
|
||||
remainder = sb.st_size - num_blocks * blk_size;
|
||||
|
||||
/* if there is a remainder, add one block */
|
||||
if( remainder > 0 ) {
|
||||
num_blocks++;
|
||||
}
|
||||
|
||||
/* The file has size 0. There still needs to be at least one block. */
|
||||
if( sb.st_size == 0 ) {
|
||||
num_blocks = 1;
|
||||
blk_size = 0;
|
||||
}
|
||||
|
||||
DEBUG_HBF("num_blocks: %" PRId64 " rmainder: %" PRId64 " blk_size: %" PRId64, num_blocks, remainder, blk_size );
|
||||
|
||||
|
||||
if( num_blocks ) {
|
||||
int cnt;
|
||||
int64_t overall = 0;
|
||||
/* create a datastructure for the transfer data */
|
||||
transfer->block_arr = calloc(num_blocks, sizeof(hbf_block_t*));
|
||||
transfer->block_cnt = num_blocks;
|
||||
transfer->transfer_id = transfer_id(&sb);
|
||||
transfer->start_id = 0;
|
||||
|
||||
for( cnt=0; cnt < num_blocks; cnt++ ) {
|
||||
/* allocate a block struct and fill */
|
||||
hbf_block_t *block = malloc( sizeof(hbf_block_t) );
|
||||
memset(block, 0, sizeof(hbf_block_t));
|
||||
|
||||
block->seq_number = cnt;
|
||||
if( cnt > 0 ) {
|
||||
block->start = cnt * blk_size;
|
||||
}
|
||||
block->size = blk_size;
|
||||
block->state = HBF_NOT_TRANSFERED;
|
||||
|
||||
/* consider the remainder if we're already at the end */
|
||||
if( cnt == num_blocks-1 && remainder > 0 ) {
|
||||
block->size = remainder;
|
||||
}
|
||||
overall += block->size;
|
||||
/* store the block data into the result array in the transfer */
|
||||
*((transfer->block_arr)+cnt) = block;
|
||||
|
||||
DEBUG_HBF("created block %d (start: %" PRId64 " size: %" PRId64 ")", cnt, block->start, block->size );
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
transfer->calc_size = overall;
|
||||
#endif
|
||||
}
|
||||
return HBF_SUCCESS;
|
||||
}
|
||||
|
||||
void hbf_free_transfer( hbf_transfer_t *transfer ) {
|
||||
int cnt;
|
||||
|
||||
if( !transfer ) return;
|
||||
|
||||
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
|
||||
hbf_block_t *block = transfer->block_arr[cnt];
|
||||
if( !block ) continue;
|
||||
if( block->http_error_msg ) free( block->http_error_msg );
|
||||
if( block->etag ) free( block->etag );
|
||||
}
|
||||
free( transfer->block_arr );
|
||||
free( transfer->url );
|
||||
free( transfer->file_id );
|
||||
if( transfer->error_string) free( (void*) transfer->error_string );
|
||||
|
||||
free( transfer );
|
||||
}
|
||||
|
||||
static char* get_transfer_url( hbf_transfer_t *transfer, int indx ) {
|
||||
char *res = NULL;
|
||||
|
||||
hbf_block_t *block = NULL;
|
||||
|
||||
if( ! transfer ) return NULL;
|
||||
if( indx >= transfer->block_cnt ) return NULL;
|
||||
|
||||
block = transfer->block_arr[indx];
|
||||
if( ! block ) return NULL;
|
||||
|
||||
if( transfer->block_cnt == 1 ) {
|
||||
/* Just one chunk. We send as an ordinary request without chunking. */
|
||||
res = strdup( transfer->url );
|
||||
} else {
|
||||
char trans_id_str[32];
|
||||
char trans_block_str[32];
|
||||
char indx_str[32];
|
||||
int len = 1; /* trailing zero. */
|
||||
int tlen = 0;
|
||||
|
||||
tlen = sprintf( trans_id_str, "%u", transfer->transfer_id );
|
||||
if( tlen < 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
len += tlen;
|
||||
|
||||
tlen = sprintf( trans_block_str, "%u", transfer->block_cnt );
|
||||
if( tlen < 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
len += tlen;
|
||||
|
||||
tlen = sprintf( indx_str, "%u", indx );
|
||||
if( tlen < 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
len += tlen;
|
||||
|
||||
len += strlen(transfer->url);
|
||||
len += strlen("-chunking---");
|
||||
|
||||
res = malloc(len);
|
||||
|
||||
/* Note: must be %u for unsigned because one does not want '--' */
|
||||
if( sprintf(res, "%s-chunking-%u-%u-%u", transfer->url, transfer->transfer_id,
|
||||
transfer->block_cnt, indx ) < 0 ) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* perform one transfer of one block.
|
||||
* returns HBF_TRANSFER_SUCCESS if the transfer of this block was a success
|
||||
* returns HBF_SUCCESS if the server aknoweldge that he received all the blocks
|
||||
*/
|
||||
static int _hbf_dav_request(hbf_transfer_t *transfer, ne_request *req, int fd, hbf_block_t *blk ) {
|
||||
Hbf_State state = HBF_TRANSFER_SUCCESS;
|
||||
int res;
|
||||
const ne_status *req_status = NULL;
|
||||
const char *etag = NULL;
|
||||
|
||||
(void) transfer;
|
||||
|
||||
if( ! (blk && req) ) return HBF_PARAM_FAIL;
|
||||
|
||||
ne_set_request_body_fd(req, fd, blk->start, blk->size);
|
||||
DEBUG_HBF("Block: %d , Start: %" PRId64 " and Size: %" PRId64 "", blk->seq_number, blk->start, blk->size );
|
||||
res = ne_request_dispatch(req);
|
||||
|
||||
req_status = ne_get_status( req );
|
||||
|
||||
switch(res) {
|
||||
case NE_OK:
|
||||
blk->state = HBF_TRANSFER_FAILED;
|
||||
state = HBF_FAIL;
|
||||
etag = 0;
|
||||
if( req_status->klass == 2 ) {
|
||||
state = HBF_TRANSFER_SUCCESS;
|
||||
blk->state = HBF_TRANSFER_SUCCESS;
|
||||
etag = ne_get_response_header(req, "ETag");
|
||||
if (etag && etag[0]) {
|
||||
/* When there is an etag, it means the transfer was complete */
|
||||
state = HBF_SUCCESS;
|
||||
|
||||
if( etag[0] == '"' && etag[ strlen(etag)-1] == '"') {
|
||||
int len = strlen( etag )-2;
|
||||
blk->etag = malloc( len+1 );
|
||||
strncpy( blk->etag, etag+1, len );
|
||||
blk->etag[len] = '\0';
|
||||
} else {
|
||||
blk->etag = strdup( etag );
|
||||
}
|
||||
} else {
|
||||
/* DEBUG_HBF("OOOOOOOO No etag returned!"); */
|
||||
}
|
||||
|
||||
/* check if the server was able to set the mtime already. */
|
||||
etag = ne_get_response_header(req, "X-OC-MTime");
|
||||
if( etag && strcmp(etag, "accepted") == 0 ) {
|
||||
/* the server acknowledged that the mtime was set. */
|
||||
transfer->modtime_accepted = 1;
|
||||
}
|
||||
|
||||
etag = ne_get_response_header(req, "OC-FileID");
|
||||
if( etag ) {
|
||||
transfer->file_id = strdup( etag );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NE_AUTH:
|
||||
state = HBF_AUTH_FAIL;
|
||||
blk->state = HBF_TRANSFER_FAILED;
|
||||
break;
|
||||
case NE_PROXYAUTH:
|
||||
state = HBF_PROXY_AUTH_FAIL;
|
||||
blk->state = HBF_TRANSFER_FAILED;
|
||||
break;
|
||||
case NE_CONNECT:
|
||||
state = HBF_CONNECT_FAIL;
|
||||
blk->state = HBF_TRANSFER_FAILED;
|
||||
break;
|
||||
case NE_TIMEOUT:
|
||||
state = HBF_TIMEOUT_FAIL;
|
||||
blk->state = HBF_TRANSFER_FAILED;
|
||||
break;
|
||||
case NE_ERROR:
|
||||
state = HBF_FAIL;
|
||||
blk->state = HBF_TRANSFER_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
blk->http_result_code = req_status->code;
|
||||
if( req_status->reason_phrase ) {
|
||||
blk->http_error_msg = strdup(req_status->reason_phrase);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
Hbf_State hbf_validate_source_file( hbf_transfer_t *transfer ) {
|
||||
Hbf_State state = HBF_SUCCESS;
|
||||
hbf_stat_t sb;
|
||||
|
||||
if( transfer == NULL ) {
|
||||
state = HBF_PARAM_FAIL;
|
||||
}
|
||||
|
||||
if( state == HBF_SUCCESS ) {
|
||||
if( transfer->fd <= 0 ) {
|
||||
state = HBF_PARAM_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if( state == HBF_SUCCESS ) {
|
||||
int rc = _hbf_fstat( transfer->fd, &sb );
|
||||
if( rc != 0 ) {
|
||||
state = HBF_STAT_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if( state == HBF_SUCCESS ) {
|
||||
if( sb.st_mtime != transfer->modtime || sb.st_size != transfer->stat_size ) {
|
||||
state = HBF_SOURCE_FILE_CHANGE;
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/* Get the HTTP error code for the last request */
|
||||
static int _hbf_http_error_code(ne_session *session) {
|
||||
const char *msg = ne_get_error( session );
|
||||
char *msg2;
|
||||
int err;
|
||||
err = strtol(msg, &msg2, 10);
|
||||
if (msg == msg2) {
|
||||
err = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static Hbf_State _hbf_transfer_no_chunk(ne_session *session, hbf_transfer_t *transfer, const char *verb) {
|
||||
int res;
|
||||
const ne_status* req_status;
|
||||
|
||||
ne_request *req = ne_request_create(session, verb ? verb : "PUT", transfer->url);
|
||||
if (!req)
|
||||
return HBF_MEMORY_FAIL;
|
||||
|
||||
ne_add_request_header( req, "Content-Type", "application/octet-stream");
|
||||
|
||||
ne_set_request_body_fd(req, transfer->fd, 0, transfer->stat_size);
|
||||
DEBUG_HBF("HBF: chunking not supported for %s", transfer->url);
|
||||
res = ne_request_dispatch(req);
|
||||
req_status = ne_get_status( req );
|
||||
|
||||
if (res == NE_OK && req_status->klass == 2) {
|
||||
ne_request_destroy(req);
|
||||
return HBF_SUCCESS;
|
||||
}
|
||||
|
||||
if( transfer->error_string ) free( transfer->error_string );
|
||||
transfer->error_string = strdup( ne_get_error(session) );
|
||||
transfer->status_code = req_status->code;
|
||||
ne_request_destroy(req);
|
||||
return HBF_FAIL;
|
||||
}
|
||||
|
||||
Hbf_State hbf_transfer( ne_session *session, hbf_transfer_t *transfer, const char *verb ) {
|
||||
Hbf_State state = HBF_TRANSFER_SUCCESS;
|
||||
int cnt;
|
||||
|
||||
if( ! session ) {
|
||||
state = HBF_SESSION_FAIL;
|
||||
}
|
||||
if( ! transfer ) {
|
||||
state = HBF_SPLITLIST_FAIL;
|
||||
}
|
||||
if( ! verb ) {
|
||||
state = HBF_PARAM_FAIL;
|
||||
}
|
||||
|
||||
if(state == HBF_TRANSFER_SUCCESS) {
|
||||
DEBUG_HBF("%s request to %s", verb, transfer->url);
|
||||
}
|
||||
|
||||
for( cnt=0; state == HBF_TRANSFER_SUCCESS && cnt < transfer->block_cnt; cnt++ ) {
|
||||
/* cnt goes from O to block_cnt, but block_id starts at start_id and wrap around
|
||||
* That way if we have not finished uploaded when we reach block_cnt, we re-upload
|
||||
* the beginning of the file that the server did not have in cache anymore.
|
||||
*/
|
||||
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
|
||||
hbf_block_t *block = transfer->block_arr[block_id];
|
||||
char *transfer_url = NULL;
|
||||
|
||||
if( ! block ) state = HBF_PARAM_FAIL;
|
||||
|
||||
if( transfer->abort_cb ) {
|
||||
int do_abort = (transfer->abort_cb)(transfer->user_data);
|
||||
if( do_abort ) {
|
||||
state = HBF_USER_ABORTED;
|
||||
transfer->start_id = block_id % transfer->block_cnt;
|
||||
}
|
||||
}
|
||||
|
||||
if( state == HBF_TRANSFER_SUCCESS ) {
|
||||
transfer_url = get_transfer_url( transfer, block_id );
|
||||
if( ! transfer_url ) {
|
||||
state = HBF_PARAM_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if( state == HBF_TRANSFER_SUCCESS ) {
|
||||
if( transfer->block_cnt > 1 && cnt > 0 ) {
|
||||
/* The block count is > 1, check size and mtime before transmitting. */
|
||||
state = hbf_validate_source_file(transfer);
|
||||
if( state == HBF_SOURCE_FILE_CHANGE ) {
|
||||
/* The source file has changed meanwhile */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( state == HBF_TRANSFER_SUCCESS || state == HBF_SUCCESS ) {
|
||||
ne_request *req = ne_request_create(session, verb, transfer_url);
|
||||
|
||||
if( req ) {
|
||||
char buf[21];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%"PRId64, transfer->stat_size);
|
||||
ne_add_request_header(req, "OC-Total-Length", buf);
|
||||
if( transfer->oc_header_modtime > 0 ) {
|
||||
snprintf(buf, sizeof(buf), "%"PRId64, transfer->oc_header_modtime);
|
||||
ne_add_request_header(req, "X-OC-Mtime", buf);
|
||||
}
|
||||
|
||||
if( transfer->previous_etag ) {
|
||||
ne_add_request_header(req, "If-Match", transfer->previous_etag);
|
||||
}
|
||||
|
||||
if( transfer->block_cnt > 1 ) {
|
||||
ne_add_request_header(req, "OC-Chunked", "1");
|
||||
snprintf(buf, sizeof(buf), "%"PRId64, transfer->threshold);
|
||||
ne_add_request_header(req, "OC-Chunk-Size", buf);
|
||||
}
|
||||
ne_add_request_header( req, "Content-Type", "application/octet-stream");
|
||||
|
||||
state = _hbf_dav_request(transfer, req, transfer->fd, block );
|
||||
|
||||
if( state != HBF_TRANSFER_SUCCESS && state != HBF_SUCCESS) {
|
||||
if( transfer->error_string ) free( transfer->error_string );
|
||||
transfer->error_string = strdup( ne_get_error(session) );
|
||||
transfer->start_id = block_id % transfer->block_cnt;
|
||||
/* Set the code of the last transmission. */
|
||||
state = HBF_FAIL;
|
||||
transfer->status_code = transfer->block_arr[block_id]->http_result_code;
|
||||
}
|
||||
ne_request_destroy(req);
|
||||
|
||||
if (transfer->block_cnt > 1 && state == HBF_SUCCESS && cnt == 0) {
|
||||
/* Success on the first chunk is suspicious.
|
||||
It could happen that the server did not support chunking */
|
||||
int rc = ne_delete(session, transfer_url);
|
||||
if (rc == NE_OK && _hbf_http_error_code(session) == 204) {
|
||||
/* If delete suceeded, it means some proxy strips the OC_CHUNKING header
|
||||
start again without chunking: */
|
||||
free( transfer_url );
|
||||
return _hbf_transfer_no_chunk(session, transfer, verb);
|
||||
}
|
||||
}
|
||||
|
||||
if (state == HBF_TRANSFER_SUCCESS && transfer->chunk_finished_cb) {
|
||||
transfer->chunk_finished_cb(transfer, block_id, transfer->user_data);
|
||||
}
|
||||
|
||||
} else {
|
||||
state = HBF_MEMORY_FAIL;
|
||||
}
|
||||
}
|
||||
free( transfer_url );
|
||||
}
|
||||
|
||||
/* do the source file validation finally (again). */
|
||||
if( state == HBF_TRANSFER_SUCCESS ) {
|
||||
/* This means that no etag was returned on one of the chunks to indicate
|
||||
* that the upload was finished. */
|
||||
state = HBF_TRANSFER_NOT_ACKED;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
int hbf_fail_http_code( hbf_transfer_t *transfer )
|
||||
{
|
||||
int cnt;
|
||||
|
||||
if( ! transfer ) return 0;
|
||||
|
||||
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
|
||||
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
|
||||
hbf_block_t *block = transfer->block_arr[block_id];
|
||||
|
||||
if( block->state != HBF_NOT_TRANSFERED && block->state != HBF_TRANSFER_SUCCESS ) {
|
||||
return block->http_result_code;
|
||||
}
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
|
||||
const char *hbf_transfer_etag( hbf_transfer_t *transfer )
|
||||
{
|
||||
int cnt;
|
||||
const char *etag = NULL;
|
||||
|
||||
if( ! transfer ) return 0;
|
||||
|
||||
/* Loop over all parts and do a assertion that there is only one etag. */
|
||||
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
|
||||
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
|
||||
hbf_block_t *block = transfer->block_arr[block_id];
|
||||
if( block->etag ) {
|
||||
if( etag && strcmp(etag, block->etag) != 0 ) {
|
||||
/* multiple etags in the transfer, not equal. */
|
||||
DEBUG_HBF( "WARN: etags are not equal in blocks of one single transfer." );
|
||||
}
|
||||
etag = block->etag;
|
||||
}
|
||||
}
|
||||
return etag;
|
||||
}
|
||||
|
||||
const char *hbf_transfer_file_id( hbf_transfer_t *transfer )
|
||||
{
|
||||
const char *re = NULL;
|
||||
if(transfer) {
|
||||
re = transfer->file_id;
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
const char *hbf_error_string(hbf_transfer_t *transfer, Hbf_State state)
|
||||
{
|
||||
const char *re;
|
||||
int cnt;
|
||||
switch( state ) {
|
||||
case HBF_SUCCESS:
|
||||
re = "Ok.";
|
||||
break;
|
||||
case HBF_NOT_TRANSFERED: /* never tried to transfer */
|
||||
re = "Block was not yet tried to transfer.";
|
||||
break;
|
||||
case HBF_TRANSFER: /* transfer currently running */
|
||||
re = "Block is currently transferred.";
|
||||
break;
|
||||
case HBF_TRANSFER_FAILED: /* transfer tried but failed */
|
||||
re = "Block transfer failed.";
|
||||
break;
|
||||
case HBF_TRANSFER_SUCCESS: /* transfer succeeded. */
|
||||
re = "Block transfer successful.";
|
||||
break;
|
||||
case HBF_SPLITLIST_FAIL: /* the file could not be split */
|
||||
re = "Splitlist could not be computed.";
|
||||
break;
|
||||
case HBF_SESSION_FAIL:
|
||||
re = "No valid session in transfer.";
|
||||
break;
|
||||
case HBF_FILESTAT_FAIL:
|
||||
re = "Source file could not be stat'ed.";
|
||||
break;
|
||||
case HBF_PARAM_FAIL:
|
||||
re = "Parameter fail.";
|
||||
break;
|
||||
case HBF_AUTH_FAIL:
|
||||
re = "Authentication fail.";
|
||||
break;
|
||||
case HBF_PROXY_AUTH_FAIL:
|
||||
re = "Proxy Authentication fail.";
|
||||
break;
|
||||
case HBF_CONNECT_FAIL:
|
||||
re = "Connection could not be established.";
|
||||
break;
|
||||
case HBF_TIMEOUT_FAIL:
|
||||
re = "Network timeout.";
|
||||
break;
|
||||
case HBF_MEMORY_FAIL:
|
||||
re = "Out of memory.";
|
||||
break;
|
||||
case HBF_STAT_FAIL:
|
||||
re = "Filesystem stat on file failed.";
|
||||
break;
|
||||
case HBF_SOURCE_FILE_CHANGE:
|
||||
re = "Source file changed too often during upload.";
|
||||
break;
|
||||
case HBF_USER_ABORTED:
|
||||
re = "Transmission aborted by user.";
|
||||
break;
|
||||
case HBF_TRANSFER_NOT_ACKED:
|
||||
re = "The server did not provide an Etag.";
|
||||
break;
|
||||
case HBF_FAIL:
|
||||
default:
|
||||
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
|
||||
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
|
||||
hbf_block_t *block = transfer->block_arr[block_id];
|
||||
|
||||
if( block->state != HBF_NOT_TRANSFERED && block->state != HBF_TRANSFER_SUCCESS
|
||||
&& block->http_error_msg != NULL) {
|
||||
return block->http_error_msg;
|
||||
}
|
||||
}
|
||||
re = "Unknown error.";
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
void hbf_set_abort_callback( hbf_transfer_t *transfer, hbf_abort_callback cb)
|
||||
{
|
||||
if( transfer ) {
|
||||
transfer->abort_cb = cb;
|
||||
}
|
||||
}
|
||||
|
||||
void hbf_set_log_callback(hbf_transfer_t* transfer, hbf_log_callback cb)
|
||||
{
|
||||
if( transfer ) {
|
||||
transfer->log_cb = cb;
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/**
|
||||
* http big file functions
|
||||
*
|
||||
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _HBF_SEND_H
|
||||
#define _HBF_SEND_H
|
||||
|
||||
#include "config_csync.h"
|
||||
#ifdef NEON_WITH_LFS /* Switch on LFS in libneon. Never remove the NE_LFS! */
|
||||
#define NE_LFS
|
||||
#endif
|
||||
|
||||
#include <neon/ne_session.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum hbf_state_e {
|
||||
HBF_SUCCESS,
|
||||
HBF_NOT_TRANSFERED, /* never tried to transfer */
|
||||
HBF_TRANSFER, /* transfer currently running */
|
||||
HBF_TRANSFER_FAILED, /* transfer tried but failed */
|
||||
HBF_TRANSFER_SUCCESS, /* block transfer succeeded. */
|
||||
HBF_SPLITLIST_FAIL, /* the file could not be split */
|
||||
HBF_SESSION_FAIL,
|
||||
HBF_FILESTAT_FAIL,
|
||||
HBF_PARAM_FAIL,
|
||||
HBF_AUTH_FAIL,
|
||||
HBF_PROXY_AUTH_FAIL,
|
||||
HBF_CONNECT_FAIL,
|
||||
HBF_TIMEOUT_FAIL,
|
||||
HBF_MEMORY_FAIL,
|
||||
HBF_STAT_FAIL,
|
||||
HBF_SOURCE_FILE_CHANGE,
|
||||
HBF_USER_ABORTED,
|
||||
HBF_TRANSFER_NOT_ACKED,
|
||||
HBF_FAIL
|
||||
};
|
||||
|
||||
typedef enum hbf_state_e Hbf_State;
|
||||
|
||||
typedef struct hbf_block_s hbf_block_t;
|
||||
|
||||
struct hbf_block_s {
|
||||
int seq_number;
|
||||
|
||||
int64_t start;
|
||||
int64_t size;
|
||||
|
||||
Hbf_State state;
|
||||
int http_result_code;
|
||||
char *http_error_msg;
|
||||
char *etag;
|
||||
|
||||
int tries;
|
||||
};
|
||||
|
||||
typedef struct hbf_transfer_s hbf_transfer_t;
|
||||
|
||||
/* Callback for to check on abort */
|
||||
typedef int (*hbf_abort_callback) (void *);
|
||||
typedef void (*hbf_log_callback) (const char *, const char *, void*);
|
||||
typedef void (*hbf_chunk_finished_callback) (hbf_transfer_t*,int, void*);
|
||||
|
||||
struct hbf_transfer_s {
|
||||
hbf_block_t **block_arr;
|
||||
int block_cnt;
|
||||
int fd;
|
||||
int transfer_id;
|
||||
char *url;
|
||||
int start_id;
|
||||
|
||||
int status_code;
|
||||
char *error_string;
|
||||
|
||||
int64_t stat_size;
|
||||
time_t modtime;
|
||||
time_t oc_header_modtime;
|
||||
int64_t block_size;
|
||||
int64_t threshold;
|
||||
|
||||
void *user_data;
|
||||
hbf_abort_callback abort_cb;
|
||||
hbf_log_callback log_cb;
|
||||
hbf_chunk_finished_callback chunk_finished_cb;
|
||||
int modtime_accepted;
|
||||
const char *previous_etag; /* etag send as the If-Match http header */
|
||||
char *file_id;
|
||||
|
||||
#ifndef NDEBUG
|
||||
int64_t calc_size;
|
||||
#endif
|
||||
};
|
||||
|
||||
hbf_transfer_t *hbf_init_transfer( const char *dest_uri );
|
||||
|
||||
Hbf_State hbf_transfer( ne_session *session, hbf_transfer_t *transfer, const char *verb );
|
||||
|
||||
Hbf_State hbf_splitlist( hbf_transfer_t *transfer, int fd );
|
||||
|
||||
void hbf_free_transfer( hbf_transfer_t *transfer );
|
||||
|
||||
const char *hbf_error_string(hbf_transfer_t* transfer, Hbf_State state);
|
||||
|
||||
const char *hbf_transfer_etag( hbf_transfer_t *transfer );
|
||||
|
||||
const char *hbf_transfer_file_id( hbf_transfer_t *transfer );
|
||||
|
||||
void hbf_set_abort_callback( hbf_transfer_t *transfer, hbf_abort_callback cb);
|
||||
void hbf_set_log_callback( hbf_transfer_t *transfer, hbf_log_callback cb);
|
||||
|
||||
/* returns an http (error) code of the transmission. If the transmission
|
||||
* succeeded, the code is 200. If it failed, its the error code of the
|
||||
* first part transmission that failed.
|
||||
*/
|
||||
int hbf_fail_http_code( hbf_transfer_t *transfer );
|
||||
|
||||
Hbf_State hbf_validate_source_file( hbf_transfer_t *transfer );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
project(hbf_test C )
|
||||
|
||||
add_definitions(-DUNIT_TESTING=1)
|
||||
|
||||
find_package(CMocka REQUIRED)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMOCKA_INCLUDE_DIRS}
|
||||
${NEON_INCLUDE_DIRS}
|
||||
${HTTPBF_PUBLIC_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
add_executable(send_test hbf_send_test.c)
|
||||
target_link_libraries(send_test ${CMOCKA_LIBRARIES} ${NEON_LIBRARIES} ${HBF_LIBRARY} )
|
||||
|
||||
|
||||
|
Antes Largura: | Altura: | Tamanho: 1.2 MiB |
@@ -1,162 +0,0 @@
|
||||
/*
|
||||
* httpbf - send big files via http
|
||||
*
|
||||
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE /* See feature_test_macros(7) */
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "config_csync.h"
|
||||
|
||||
#include <httpbf.h>
|
||||
|
||||
|
||||
// A test case that does nothing and succeeds.
|
||||
static void null_test_success(void **state) {
|
||||
(void) state;
|
||||
}
|
||||
|
||||
|
||||
static char* test_file( const char* name ) {
|
||||
if( ! name ) return 0;
|
||||
|
||||
char path[260];
|
||||
strcpy( path, TESTFILEDIR);
|
||||
if(path[strlen(TESTFILEDIR)-1] != '/')
|
||||
strcat( path, "/");
|
||||
strcat( path, name );
|
||||
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
static void test_get_transfer_url( void **state ) {
|
||||
const char *url = "http://example.org/owncloud";
|
||||
const char *turl = NULL;
|
||||
char res[256];
|
||||
int i;
|
||||
Hbf_State hbf_state;
|
||||
|
||||
hbf_transfer_t *list = NULL;
|
||||
list = hbf_init_transfer( url );
|
||||
assert_non_null( list );
|
||||
|
||||
/* open a file */
|
||||
int fd = open( test_file("church.jpg"), O_RDONLY );
|
||||
assert_true(fd >= 0);
|
||||
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
assert_true( hbf_state == HBF_SUCCESS);
|
||||
|
||||
for( i=0; i < list->block_cnt; i++ ) {
|
||||
turl = get_transfer_url( list, i );
|
||||
|
||||
sprintf(res, "%s-chunking-%d-%d-%d", url, list->transfer_id,
|
||||
list->block_cnt, i );
|
||||
printf( "XX: %s\n", res );
|
||||
assert_string_equal( turl, res );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void test_hbf_init_transfer( void **state ) {
|
||||
hbf_transfer_t *list = NULL;
|
||||
const char *url = "http://example.org/owncloud";
|
||||
|
||||
list = hbf_init_transfer( url );
|
||||
assert_non_null( list );
|
||||
assert_string_equal( url, list->url );
|
||||
}
|
||||
|
||||
/* test with a file size that is not a multiply of the slize size. */
|
||||
static void test_hbf_splitlist_odd( void **state ){
|
||||
|
||||
hbf_transfer_t *list = NULL;
|
||||
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/church.jpg";
|
||||
|
||||
/* open a file */
|
||||
int fd = open(test_file("church.jpg"), O_RDONLY);
|
||||
assert_true(fd >= 0);
|
||||
|
||||
int prev_id = 0;
|
||||
int i;
|
||||
|
||||
Hbf_State hbf_state;
|
||||
|
||||
/* do a smoke test for uniqueness */
|
||||
for( i=0; i < 10000; i++) {
|
||||
list = hbf_init_transfer(dest_url);
|
||||
assert_non_null(list);
|
||||
usleep(1);
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
|
||||
assert_int_not_equal(list->transfer_id, prev_id);
|
||||
prev_id = list->transfer_id;
|
||||
hbf_free_transfer(list);
|
||||
}
|
||||
|
||||
list = hbf_init_transfer(dest_url);
|
||||
assert_non_null(list);
|
||||
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
assert_non_null(list);
|
||||
assert_int_equal(list->calc_size, list->stat_size);
|
||||
assert_int_not_equal(list->block_cnt, 0);
|
||||
assert_true( hbf_state == HBF_SUCCESS);
|
||||
|
||||
/* checks on the block list */
|
||||
int seen_zero_seq = 0;
|
||||
int prev_seq = -1;
|
||||
int64_t prev_block_end = -1;
|
||||
|
||||
for( i=0; i < list->block_cnt; i++) {
|
||||
hbf_block_t *blk = list->block_arr[i];
|
||||
assert_non_null(blk);
|
||||
if( blk->seq_number == 0 ) seen_zero_seq++;
|
||||
|
||||
assert_int_equal(prev_seq, blk->seq_number -1 );
|
||||
prev_seq = blk->seq_number;
|
||||
|
||||
assert_true((prev_block_end+1) == (blk->start));
|
||||
prev_block_end = blk->start + blk->size;
|
||||
}
|
||||
/* Make sure we exactly saw blk->seq_number == 0 exactly one times */
|
||||
assert_int_equal( seen_zero_seq, 1 );
|
||||
|
||||
hbf_free_transfer( list );
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const UnitTest tests[] = {
|
||||
unit_test(null_test_success),
|
||||
unit_test(test_hbf_splitlist_odd),
|
||||
unit_test(test_hbf_init_transfer),
|
||||
unit_test(test_get_transfer_url)
|
||||
};
|
||||
return run_tests(tests);
|
||||
}
|
||||
@@ -36,9 +36,6 @@
|
||||
#define CSYNC_LOG_CATEGORY_NAME "csync.vio.main"
|
||||
|
||||
#include "csync_log.h"
|
||||
#if USE_NEON
|
||||
#include "csync_owncloud.h"
|
||||
#endif
|
||||
|
||||
csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {
|
||||
switch(ctx->replica) {
|
||||
@@ -132,8 +129,5 @@ char *csync_vio_get_status_string(CSYNC *ctx) {
|
||||
if(ctx->error_string) {
|
||||
return ctx->error_string;
|
||||
}
|
||||
#ifdef USE_NEON
|
||||
return owncloud_error_string(ctx);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -157,13 +157,6 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf->name = c_basename(uri);
|
||||
|
||||
if (buf->name == NULL) {
|
||||
csync_vio_file_stat_destroy(buf);
|
||||
c_free_locale_string(wuri);
|
||||
return -1;
|
||||
}
|
||||
buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
|
||||
|
||||
switch(sb.st_mode & S_IFMT) {
|
||||
@@ -210,9 +203,6 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
|
||||
#endif
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
|
||||
|
||||
buf->device = sb.st_dev;
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
|
||||
|
||||
buf->inode = sb.st_ino;
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
|
||||
|
||||
|
||||
@@ -116,10 +116,31 @@ int csync_vio_local_closedir(csync_vio_handle_t *dhandle) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
|
||||
{
|
||||
long long int t = filetime->dwHighDateTime;
|
||||
t <<= 32;
|
||||
t += (UINT32)filetime->dwLowDateTime;
|
||||
t -= 116444736000000000LL;
|
||||
if (t < 0)
|
||||
{
|
||||
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
|
||||
return -1 - ((-t - 1) / 10000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (remainder) *remainder = t % 10000000;
|
||||
return t / 10000000;
|
||||
}
|
||||
}
|
||||
|
||||
csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
|
||||
|
||||
dhandle_t *handle = NULL;
|
||||
csync_vio_file_stat_t *file_stat = NULL;
|
||||
ULARGE_INTEGER FileIndex;
|
||||
DWORD rem;
|
||||
|
||||
handle = (dhandle_t *) dhandle;
|
||||
|
||||
@@ -148,13 +169,43 @@ csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
|
||||
file_stat->name = c_utf8_from_locale(handle->ffd.cFileName);
|
||||
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
||||
} else {
|
||||
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
||||
}
|
||||
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT
|
||||
&& handle->ffd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) {
|
||||
file_stat->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
|
||||
file_stat->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
|
||||
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
||||
} else {
|
||||
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
||||
}
|
||||
|
||||
return file_stat;
|
||||
file_stat->flags = CSYNC_VIO_FILE_FLAGS_NONE;
|
||||
/* Check for the hidden flag */
|
||||
if( handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
|
||||
file_stat->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
|
||||
}
|
||||
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
||||
|
||||
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow;
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
|
||||
|
||||
file_stat->atime = FileTimeToUnixTime(&handle->ffd.ftLastAccessTime, &rem);
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
|
||||
|
||||
file_stat->mtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
|
||||
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
|
||||
|
||||
file_stat->ctime = FileTimeToUnixTime(&handle->ffd.ftCreationTime, &rem);
|
||||
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
|
||||
|
||||
return file_stat;
|
||||
|
||||
err:
|
||||
SAFE_FREE(file_stat);
|
||||
@@ -162,29 +213,16 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
|
||||
{
|
||||
long long int t = filetime->dwHighDateTime;
|
||||
t <<= 32;
|
||||
t += (UINT32)filetime->dwLowDateTime;
|
||||
t -= 116444736000000000LL;
|
||||
if (t < 0)
|
||||
{
|
||||
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
|
||||
return -1 - ((-t - 1) / 10000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (remainder) *remainder = t % 10000000;
|
||||
return t / 10000000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
|
||||
HANDLE h, hFind;
|
||||
FILETIME ftCreate, ftAccess, ftWrite;
|
||||
/* Almost nothing to do since csync_vio_local_readdir already filled up most of the information
|
||||
But we still need to fetch the file ID.
|
||||
Possible optimisation: only fetch the file id when we need it (for new files)
|
||||
*/
|
||||
|
||||
HANDLE h;
|
||||
BY_HANDLE_FILE_INFORMATION fileInfo;
|
||||
WIN32_FIND_DATAW FindFileData;
|
||||
ULARGE_INTEGER FileIndex;
|
||||
mbchar_t *wuri = c_utf8_path_to_locale( uri );
|
||||
|
||||
@@ -205,47 +243,6 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf->flags = CSYNC_VIO_FILE_FLAGS_NONE;
|
||||
do {
|
||||
// Check first if it is a symlink (code from c_islink)
|
||||
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
hFind = FindFirstFileW(wuri, &FindFileData );
|
||||
if (hFind != INVALID_HANDLE_VALUE) {
|
||||
if( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
|
||||
(FindFileData.dwReserved0 & IO_REPARSE_TAG_SYMLINK) ) {
|
||||
buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
|
||||
buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FindClose(hFind);
|
||||
}
|
||||
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|
||||
|| fileInfo.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|
||||
|| fileInfo.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
|
||||
buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
||||
break;
|
||||
}
|
||||
// fallthrough:
|
||||
buf->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
||||
break;
|
||||
} while (0);
|
||||
|
||||
/* Check for the hidden flag */
|
||||
if( fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
|
||||
buf->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
|
||||
}
|
||||
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
||||
|
||||
buf->device = fileInfo.dwVolumeSerialNumber;
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
|
||||
|
||||
/* Get the Windows file id as an inode replacement. */
|
||||
FileIndex.HighPart = fileInfo.nFileIndexHigh;
|
||||
FileIndex.LowPart = fileInfo.nFileIndexLow;
|
||||
@@ -253,28 +250,7 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
|
||||
/* printf("Index: %I64i\n", FileIndex.QuadPart); */
|
||||
buf->inode = FileIndex.QuadPart;
|
||||
|
||||
buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow;
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
|
||||
|
||||
/* Get the file time with a win32 call rather than through stat. See
|
||||
* http://www.codeproject.com/Articles/1144/Beating-the-Daylight-Savings-Time-bug-and-getting
|
||||
* for deeper explanation.
|
||||
*/
|
||||
if( GetFileTime(h, &ftCreate, &ftAccess, &ftWrite) ) {
|
||||
DWORD rem;
|
||||
buf->atime = FileTimeToUnixTime(&ftAccess, &rem);
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
|
||||
|
||||
buf->mtime = FileTimeToUnixTime(&ftWrite, &rem);
|
||||
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
|
||||
|
||||
buf->ctime = FileTimeToUnixTime(&ftCreate, &rem);
|
||||
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
|
||||
}
|
||||
|
||||
c_free_locale_string(wuri);
|
||||
CloseHandle(h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ include_directories(
|
||||
${CSTDLIB_PUBLIC_INCLUDE_DIRS}
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMOCKA_INCLUDE_DIR}
|
||||
${HTTPBF_PUBLIC_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
include_directories(${CHECK_INCLUDE_DIRS})
|
||||
@@ -54,10 +53,6 @@ add_cmocka_test(check_csync_update csync_tests/check_csync_update.c ${TEST_TARGE
|
||||
# encoding
|
||||
add_cmocka_test(check_encoding_functions encoding_tests/check_encoding.c ${TEST_TARGET_LIBRARIES})
|
||||
|
||||
# httpbf
|
||||
set(TEST_HTTPBF_LIBRARIES ${TEST_TARGET_LIBRARIES} ${NEON_LIBRARIES})
|
||||
add_cmocka_test(check_httpbf httpbf_tests/hbf_send_test.c ${TEST_HTTPBF_LIBRARIES} )
|
||||
|
||||
if(UNIT_TESTING)
|
||||
INSTALL( FILES "${CMOCKA_LIBRARIES}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif(UNIT_TESTING)
|
||||
|
||||
@@ -48,6 +48,12 @@ static void setup_init(void **state) {
|
||||
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes));
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
/* and add some unicode stuff */
|
||||
rc = _csync_exclude_add(&(csync->excludes), "*.💩");
|
||||
assert_int_equal(rc, 0);
|
||||
rc = _csync_exclude_add(&(csync->excludes), "пятницы.*");
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
*state = csync;
|
||||
}
|
||||
|
||||
@@ -81,7 +87,7 @@ static void check_csync_exclude_load(void **state)
|
||||
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes) );
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_string_equal(csync->excludes->vector[0], "*.filepart");
|
||||
assert_string_equal(csync->excludes->vector[0], "*~");
|
||||
assert_int_not_equal(csync->excludes->count, 0);
|
||||
}
|
||||
|
||||
@@ -145,6 +151,16 @@ static void check_csync_excluded(void **state)
|
||||
rc = csync_excluded(csync, ".netscape/cache", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
|
||||
|
||||
/* Not excluded */
|
||||
rc = csync_excluded(csync, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
|
||||
/* excluded */
|
||||
rc = csync_excluded(csync, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
|
||||
rc = csync_excluded(csync, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE);
|
||||
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void check_csync_excluded_traversal(void **state)
|
||||
|
||||
@@ -41,6 +41,12 @@ static void statedb_create_metadata_table(sqlite3 *db)
|
||||
"modtime INTEGER(8),"
|
||||
"type INTEGER,"
|
||||
"md5 VARCHAR(32),"
|
||||
"fileid VARCHAR(128),"
|
||||
"remotePerm VARCHAR(128),"
|
||||
"filesize BIGINT,"
|
||||
"ignoredChildrenRemote INT,"
|
||||
"contentChecksum TEXT,"
|
||||
"contentChecksumTypeId INTEGER,"
|
||||
"PRIMARY KEY(phash));";
|
||||
|
||||
rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
|
||||
@@ -201,7 +207,6 @@ static csync_vio_file_stat_t* create_fstat(const char *name,
|
||||
}
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
|
||||
|
||||
fs->device = 0;
|
||||
|
||||
fs->size = 157459;
|
||||
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
|
||||
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* libcsync -- a library to sync a directory with another
|
||||
*
|
||||
* Copyright (c) 2013 by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cmocka.h>
|
||||
|
||||
#include "config_test.h"
|
||||
|
||||
#if USE_NEON
|
||||
#include "httpbf.c"
|
||||
#endif
|
||||
|
||||
// A test case that does nothing and succeeds.
|
||||
static void null_test_success(void **state) {
|
||||
(void) state;
|
||||
}
|
||||
|
||||
#if USE_NEON
|
||||
|
||||
static char* test_file( const char* name ) {
|
||||
char path[260];
|
||||
|
||||
if( ! name ) return 0;
|
||||
|
||||
strcpy( path, TESTFILES_DIR);
|
||||
if(path[strlen(TESTFILES_DIR)-1] != '/')
|
||||
strcat( path, "/");
|
||||
strcat( path, name );
|
||||
|
||||
return strdup(path);
|
||||
}
|
||||
|
||||
static void test_get_transfer_url( void **state ) {
|
||||
const char *url = "http://example.org/owncloud";
|
||||
const char *turl = NULL;
|
||||
int fd;
|
||||
Hbf_State hbf_state;
|
||||
|
||||
hbf_transfer_t *list = NULL;
|
||||
|
||||
(void) state;
|
||||
list = hbf_init_transfer( url );
|
||||
assert_non_null( list );
|
||||
|
||||
/* open a file */
|
||||
fd = open( test_file("church.jpg"), O_RDONLY );
|
||||
assert_true(fd >= 0);
|
||||
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
assert_true( hbf_state == HBF_SUCCESS);
|
||||
assert_true( list->block_cnt == 1);
|
||||
|
||||
turl = get_transfer_url( list, 0 );
|
||||
assert_non_null( turl );
|
||||
assert_string_equal( url, turl );
|
||||
|
||||
hbf_free_transfer( list );
|
||||
}
|
||||
|
||||
|
||||
static void test_get_transfer_url_bigfile( void **state ) {
|
||||
const char *url = "http://example.org/big_file";
|
||||
const char *turl = NULL;
|
||||
char res[256];
|
||||
int i, fd;
|
||||
Hbf_State hbf_state;
|
||||
hbf_transfer_t *list = NULL;
|
||||
|
||||
(void) state;
|
||||
|
||||
list = hbf_init_transfer( url );
|
||||
assert_non_null( list );
|
||||
|
||||
list->threshold = list->block_size = (1024*1024); /* block size 1 MB */
|
||||
|
||||
/* open a file */
|
||||
fd = open( test_file("church.jpg"), O_RDONLY );
|
||||
assert_true(fd >= 0);
|
||||
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
assert_true( hbf_state == HBF_SUCCESS);
|
||||
assert_true( list->block_cnt == 2 );
|
||||
|
||||
for( i=0; i < list->block_cnt; i++ ) {
|
||||
turl = get_transfer_url( list, i );
|
||||
assert_non_null(turl);
|
||||
|
||||
sprintf(res, "%s-chunking-%u-%u-%u", url, list->transfer_id,
|
||||
list->block_cnt, i );
|
||||
/* printf( "XX: %s\n", res ); */
|
||||
assert_string_equal( turl, res );
|
||||
}
|
||||
hbf_free_transfer(list);
|
||||
}
|
||||
|
||||
static void test_hbf_init_transfer( void **state ) {
|
||||
hbf_transfer_t *list = NULL;
|
||||
const char *url = "http://example.org/owncloud";
|
||||
|
||||
(void) state;
|
||||
|
||||
list = hbf_init_transfer( url );
|
||||
assert_non_null( list );
|
||||
assert_string_equal( url, list->url );
|
||||
}
|
||||
|
||||
/* test with a file size that is not a multiply of the slize size. */
|
||||
static void test_hbf_splitlist_odd( void **state ){
|
||||
|
||||
hbf_transfer_t *list = NULL;
|
||||
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/church.jpg";
|
||||
int prev_id = 0;
|
||||
int i, fd;
|
||||
Hbf_State hbf_state;
|
||||
|
||||
(void) state;
|
||||
|
||||
/* open a file */
|
||||
fd = open(test_file("church.jpg"), O_RDONLY);
|
||||
assert_true(fd >= 0);
|
||||
|
||||
/* do a smoke test for uniqueness */
|
||||
for( i=0; i < 10000; i++) {
|
||||
list = hbf_init_transfer(dest_url);
|
||||
assert_non_null(list);
|
||||
usleep(1);
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
|
||||
assert_int_not_equal(list->transfer_id, prev_id);
|
||||
prev_id = list->transfer_id;
|
||||
hbf_free_transfer(list);
|
||||
}
|
||||
|
||||
list = hbf_init_transfer(dest_url);
|
||||
assert_non_null(list);
|
||||
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
assert_non_null(list);
|
||||
#ifndef NDEBUG
|
||||
assert_int_equal(list->calc_size, list->stat_size);
|
||||
#endif
|
||||
assert_int_not_equal(list->block_cnt, 0);
|
||||
assert_true( hbf_state == HBF_SUCCESS);
|
||||
|
||||
/* checks on the block list */
|
||||
if( 1 ) {
|
||||
int seen_zero_seq = 0;
|
||||
int prev_seq = -1;
|
||||
int64_t prev_block_end = -1;
|
||||
|
||||
for( i=0; i < list->block_cnt; i++) {
|
||||
hbf_block_t *blk = list->block_arr[i];
|
||||
assert_non_null(blk);
|
||||
if( blk->seq_number == 0 ) seen_zero_seq++;
|
||||
|
||||
assert_int_equal(prev_seq, blk->seq_number -1 );
|
||||
prev_seq = blk->seq_number;
|
||||
|
||||
assert_true((prev_block_end+1) == (blk->start));
|
||||
prev_block_end = blk->start + blk->size;
|
||||
}
|
||||
/* Make sure we exactly saw blk->seq_number == 0 exactly one times */
|
||||
assert_int_equal( seen_zero_seq, 1 );
|
||||
}
|
||||
hbf_free_transfer( list );
|
||||
}
|
||||
|
||||
/* test with a file size that is not a multiply of the slize size. */
|
||||
static void test_hbf_splitlist_zero( void **state ){
|
||||
|
||||
hbf_transfer_t *list = NULL;
|
||||
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/zerofile.txt";
|
||||
int fd;
|
||||
Hbf_State hbf_state;
|
||||
|
||||
(void) state;
|
||||
|
||||
/* open a file */
|
||||
fd = open(test_file("zerofile.txt"), O_RDONLY);
|
||||
assert_true(fd >= 0);
|
||||
|
||||
list = hbf_init_transfer(dest_url);
|
||||
assert_non_null(list);
|
||||
|
||||
hbf_state = hbf_splitlist(list, fd);
|
||||
assert_non_null(list);
|
||||
assert_int_equal(list->stat_size, 0);
|
||||
#ifndef NDEBUG
|
||||
assert_int_equal(list->calc_size, list->stat_size);
|
||||
#endif
|
||||
assert_int_equal(list->block_cnt, 1);
|
||||
|
||||
assert_true( hbf_state == HBF_SUCCESS);
|
||||
|
||||
hbf_free_transfer( list );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
int main(void) {
|
||||
const UnitTest tests[] = {
|
||||
unit_test(null_test_success),
|
||||
#if USE_NEON
|
||||
unit_test(test_hbf_splitlist_odd),
|
||||
unit_test(test_hbf_splitlist_zero),
|
||||
unit_test(test_hbf_init_transfer),
|
||||
unit_test(test_get_transfer_url),
|
||||
unit_test(test_get_transfer_url_bigfile)
|
||||
#endif
|
||||
};
|
||||
return run_tests(tests);
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ our %config;
|
||||
assertLocalDirs assertLocalAndRemoteDir glob_put put_to_dir
|
||||
putToDirLWP localDir remoteDir localCleanup createLocalFile md5OfFile
|
||||
remoteCleanup server initLocalDir initRemoteDir moveRemoteFile
|
||||
printInfo remoteFileId createShare removeShare assert
|
||||
printInfo remoteFileProp remoteFileId createShare removeShare assert
|
||||
configValue testDirUrl getToFileLWP getToFileCurl);
|
||||
|
||||
sub server
|
||||
@@ -679,28 +679,38 @@ sub printInfo($)
|
||||
$infoCnt++;
|
||||
}
|
||||
|
||||
sub remoteFileId($$)
|
||||
sub remoteFileProp($$)
|
||||
{
|
||||
my ($fromDir, $file) = @_;
|
||||
my $fromUrl = testDirUrl() . $fromDir;
|
||||
my $id;
|
||||
my $result;
|
||||
|
||||
if( my $r = $d->propfind( -url => $fromUrl, -depth => 1 ) ) {
|
||||
if ( $r->is_collection ) {
|
||||
# print "Collection\n";
|
||||
|
||||
foreach my $res ( $r->get_resourcelist->get_resources() ) {
|
||||
my $filename = $res->get_property("rel_uri");
|
||||
# print "OOOOOOOOOOOOOO $filename " . $res->get_property('id') . "\n";
|
||||
if( $file eq $filename || $filename eq $file . "/" ) {
|
||||
$id = $res->get_property('id') || "";
|
||||
}
|
||||
my $filename = $res->get_property("rel_uri");
|
||||
# print "OOOOOOOOOOOOOO $filename " . $res->get_property('id') . "\n";
|
||||
if( $file eq $filename || $filename eq $file . "/" ) {
|
||||
$result = $res;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# print "OOOOOOOOOOOOOOOOOOO " . $r->get_property("rel_uri");
|
||||
$id = $r->get_property('id') || "";
|
||||
$result = $r;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
sub remoteFileId($$)
|
||||
{
|
||||
my ($fromDir, $file) = @_;
|
||||
my $id;
|
||||
if( my $res = remoteFileProp($fromDir, $file) ) {
|
||||
$id = $res->get_property('id') || "";
|
||||
}
|
||||
print "## ID of $file: $id\n";
|
||||
return $id;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Test script for the ownCloud module of csync.
|
||||
# This script requires a running ownCloud instance accessible via HTTP.
|
||||
# It does quite some fancy tests and asserts the results.
|
||||
#
|
||||
# Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
|
||||
use lib ".";
|
||||
|
||||
use File::Copy;
|
||||
use ownCloud::Test;
|
||||
|
||||
use strict;
|
||||
|
||||
print "Hello, this is t9, a tester for content checksums.\n";
|
||||
|
||||
initTesting();
|
||||
|
||||
printInfo( "Add some files to local");
|
||||
my $locDir = localDir();
|
||||
copy( "testfiles/test.txt", "$locDir/test.txt");
|
||||
copy( "testfiles/test.txt", "$locDir/test.eml");
|
||||
|
||||
csync( );
|
||||
print "\nAssert local and remote dirs.\n";
|
||||
assertLocalAndRemoteDir( '', 0);
|
||||
|
||||
# Get file properties before syncing again
|
||||
my $txtpropbefore = remoteFileProp("", "test.txt");
|
||||
my $emlpropbefore = remoteFileProp("", "test.eml");
|
||||
assert($txtpropbefore);
|
||||
assert($emlpropbefore);
|
||||
|
||||
printInfo( "Touch local files");
|
||||
system( "touch $locDir/test.txt" );
|
||||
system( "touch $locDir/test.eml" );
|
||||
|
||||
csync( );
|
||||
|
||||
# Get file properties afterwards
|
||||
my $txtpropafter = remoteFileProp("", "test.txt");
|
||||
my $emlpropafter = remoteFileProp("", "test.eml");
|
||||
assert($txtpropafter);
|
||||
assert($emlpropafter);
|
||||
|
||||
# The txt file is uploaded normally, etag and mtime differ
|
||||
assert($txtpropafter->get_property( "getetag" ) ne
|
||||
$txtpropbefore->get_property( "getetag" ));
|
||||
assert($txtpropafter->get_property( "getlastmodified" ) ne
|
||||
$txtpropbefore->get_property( "getlastmodified" ));
|
||||
# The eml was not uploaded, nothing differs
|
||||
assert($emlpropafter->get_property( "getetag" ) eq
|
||||
$emlpropbefore->get_property( "getetag" ));
|
||||
assert($emlpropafter->get_property( "getlastmodified" ) eq
|
||||
$emlpropbefore->get_property( "getlastmodified" ));
|
||||
|
||||
printInfo( "Change content of eml file (but not size)");
|
||||
system( "sed -i -e 's/in/IN/' $locDir/test.eml" );
|
||||
|
||||
csync( );
|
||||
|
||||
# Get file properties afterwards
|
||||
my $emlpropchanged = remoteFileProp("", "test.eml");
|
||||
assert($emlpropchanged);
|
||||
assert($emlpropafter->get_property( "getetag" ) ne
|
||||
$emlpropchanged->get_property( "getetag" ));
|
||||
assert($emlpropafter->get_property( "getlastmodified" ) ne
|
||||
$emlpropchanged->get_property( "getlastmodified" ));
|
||||
|
||||
# ==================================================================
|
||||
|
||||
cleanup();
|
||||
|
||||
# --
|
||||
|
||||
@@ -73,15 +73,6 @@ static void setup_dir(void **state) {
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
static void setup_file(void **state) {
|
||||
int rc;
|
||||
|
||||
setup_dir(state);
|
||||
|
||||
rc = system("echo \"This is a test\" > /tmp/csync_test/file.txt");
|
||||
assert_int_equal(rc, 0);
|
||||
}
|
||||
|
||||
static void teardown(void **state) {
|
||||
CSYNC *csync = *state;
|
||||
int rc;
|
||||
@@ -164,46 +155,6 @@ static void check_csync_vio_readdir(void **state)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Test for general functions (stat, chmod, chown, ...)
|
||||
*/
|
||||
|
||||
static void check_csync_vio_stat_dir(void **state)
|
||||
{
|
||||
CSYNC *csync = *state;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = csync_vio_file_stat_new();
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = csync_vio_stat(csync, CSYNC_TEST_DIR, fs);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_string_equal(fs->name, "csync_test");
|
||||
assert_int_equal(fs->type, CSYNC_VIO_FILE_TYPE_DIRECTORY);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
static void check_csync_vio_stat_file(void **state)
|
||||
{
|
||||
CSYNC *csync = *state;
|
||||
csync_vio_file_stat_t *fs;
|
||||
int rc;
|
||||
|
||||
fs = csync_vio_file_stat_new();
|
||||
assert_non_null(fs);
|
||||
|
||||
rc = csync_vio_stat(csync, CSYNC_TEST_FILE, fs);
|
||||
assert_int_equal(rc, 0);
|
||||
|
||||
assert_string_equal(fs->name, "file.txt");
|
||||
assert_int_equal(fs->type, CSYNC_VIO_FILE_TYPE_REGULAR);
|
||||
|
||||
csync_vio_file_stat_destroy(fs);
|
||||
}
|
||||
|
||||
int torture_run_tests(void)
|
||||
{
|
||||
const UnitTest tests[] = {
|
||||
@@ -211,9 +162,6 @@ int torture_run_tests(void)
|
||||
unit_test_setup_teardown(check_csync_vio_opendir_perm, setup, teardown),
|
||||
unit_test(check_csync_vio_closedir_null),
|
||||
unit_test_setup_teardown(check_csync_vio_readdir, setup_dir, teardown),
|
||||
|
||||
unit_test_setup_teardown(check_csync_vio_stat_dir, setup_dir, teardown),
|
||||
unit_test_setup_teardown(check_csync_vio_stat_file, setup_file, teardown),
|
||||
};
|
||||
|
||||
return run_tests(tests);
|
||||
|
||||
@@ -11,8 +11,8 @@ the Updater will check for updates and notify you when a new version is
|
||||
available.
|
||||
|
||||
.. note:: Because of various technical issues, desktop sync clients older than
|
||||
1.7 will not be allowed to connect and sync with the ownCloud 8.1 server. It is
|
||||
highly recommended to keep your client updated.
|
||||
1.7 will not be allowed to connect and sync with the ownCloud 8.1+ server. It
|
||||
is highly recommended to keep your client updated.
|
||||
|
||||
Basic Workflow
|
||||
--------------
|
||||
@@ -73,8 +73,8 @@ To prevent automatic updates, but allow manual overrides:
|
||||
|
||||
1. Edit these Registry keys:
|
||||
|
||||
a. (32-bit) ``HKEY_LOCAL_MACHINE\Software\ownCloud\ownCloud``
|
||||
b. (64-bit) ``HKEY_LOCAL_MACHINE\Software\Wow6432Node\ownCloud\ownCloud``
|
||||
a. (32-bit-Windows) ``HKEY_LOCAL_MACHINE\Software\ownCloud\ownCloud``
|
||||
b. (64-bit-Windows) ``HKEY_LOCAL_MACHINE\Software\Wow6432Node\ownCloud\ownCloud``
|
||||
|
||||
2. Add the key ``skipUpdateCheck`` (of type DWORD).
|
||||
|
||||
@@ -89,7 +89,7 @@ To prevent automatic updates and disallow manual overrides:
|
||||
|
||||
1. Edit this Registry key:
|
||||
|
||||
HKEY_LOCAL_MACHINE\Software\Policies\ownCloud\ownCloud
|
||||
``HKEY_LOCAL_MACHINE\Software\Policies\ownCloud\ownCloud``
|
||||
|
||||
2. Add the key ``skipUpdateCheck`` (of type DWORD).
|
||||
|
||||
|
||||
@@ -48,9 +48,9 @@ copyright = u'2013, The ownCloud developers'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '@VERSION_MAJOR@.@VERSION_MINOR@'
|
||||
version = '@MIRALL_VERSION_MAJOR@.@MIRALL_VERSION_MINOR@'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '@VERSION@'
|
||||
release = '@MIRALL_VERSION@'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -189,7 +189,7 @@ latex_documents = [
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
latex_logo = '@LATEX_LOGO@'
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
|
||||
|
Antes Largura: | Altura: | Tamanho: 5.7 KiB Depois Largura: | Altura: | Tamanho: 5.5 KiB |
|
Antes Largura: | Altura: | Tamanho: 39 KiB |
|
Antes Largura: | Altura: | Tamanho: 41 KiB |
|
Antes Largura: | Altura: | Tamanho: 44 KiB |
|
Antes Largura: | Altura: | Tamanho: 59 KiB |
@@ -3,7 +3,8 @@ Installing the Desktop Synchronization Client
|
||||
=============================================
|
||||
|
||||
You can download the latest version of the ownCloud Desktop Synchronization
|
||||
Client from the `ownCloud Website <https://owncloud.org/install/#desktop>`_.
|
||||
Client from the `ownCloud download page
|
||||
<https://owncloud.org/install/#desktop>`_.
|
||||
There are clients for Linux, Mac OS X, and Microsoft Windows.
|
||||
|
||||
Installation on Mac OS X and Windows is the same as for any software
|
||||
@@ -21,6 +22,9 @@ will display a notification when an update is available.
|
||||
Linux users must also have a password manager enabled, such as GNOME Keyring or
|
||||
KWallet, so that the sync client can login automatically.
|
||||
|
||||
You will also find links to source code archives and older versions on the
|
||||
download page.
|
||||
|
||||
Installation Wizard
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -13,14 +13,15 @@ Your files are always automatically synchronized between your ownCloud server
|
||||
and local PC.
|
||||
|
||||
.. note:: Because of various technical issues, desktop sync clients older than
|
||||
1.7 will not allowed to connect and sync with the ownCloud 8.1 server. It is
|
||||
highly recommended to keep your client updated.
|
||||
1.7 will not allowed to connect and sync with the ownCloud 8.1+ server. It
|
||||
is highly recommended to keep your client updated.
|
||||
|
||||
Improvements and New Features
|
||||
-----------------------------
|
||||
|
||||
The 2.0 release of the ownCloud desktop sync client has many new features and
|
||||
improvements.
|
||||
improvements. (See the `complete changelog
|
||||
<https://owncloud.org/changelog/desktop/>`_.)
|
||||
|
||||
* Multi-account support
|
||||
* Many UI improvements
|
||||
|
||||
@@ -92,18 +92,27 @@ have the following features:
|
||||
removed synchronization on an account (see **Remove Sync** below).
|
||||
|
||||
The little button with three dots that sits to the right of the sync status bar
|
||||
offers three additional options:
|
||||
offers four additional options:
|
||||
|
||||
* Open Folder
|
||||
* Pause Sync
|
||||
* Choose What to Sync
|
||||
* Pause Sync / Resume Sync
|
||||
* Remove Sync
|
||||
|
||||
**Pause Sync** pauses sync operations without making any changes to your account.
|
||||
**Open Folder** opens a file explorer window displaying the client-side folder
|
||||
that is being synced.
|
||||
|
||||
**Remove Sync** suspends synchronization without removing the account, and it
|
||||
removes your folder sync selection. When you're ready to resume synchronization
|
||||
click the **Add Folder Sync Connection** button, and re-select the folders you
|
||||
want to sync.
|
||||
**Choose What to Sync** opens the folder sync tree view. From there you can
|
||||
choose to sync all or only some of the folders in the folder tree.
|
||||
|
||||
**Pause Sync** pauses sync operations for just this folder sync connection
|
||||
without making any changes to your account.
|
||||
**Resume Sync** resumes sync operations for this folder sync connection.
|
||||
|
||||
**Remove Sync** removes this folder sync connection without removing the
|
||||
account. If you want to synchronize the folder tree again then click the
|
||||
**Add Folder Sync Connection** button, and re-select the folder tree that
|
||||
you want to sync.
|
||||
|
||||
.. image:: images/client-7.png
|
||||
:alt: Extra options for sync operations
|
||||
@@ -122,12 +131,15 @@ Multi-Account Support
|
||||
|
||||
You may now configure multiple ownCloud accounts in your desktop sync client.
|
||||
Simply click the **Add an Account** button on the General tab, and follow the
|
||||
account creation wizard. The new account will appear as a new tab in the settings dialog, where you can adjust its settings at any time.
|
||||
account creation wizard. The new account will appear as a new tab in the
|
||||
settings dialog, where you can adjust its settings at any time.
|
||||
|
||||
Editing Ignored Files
|
||||
---------------------
|
||||
|
||||
The Ignored Files Editor can be opened by clicking on the button in the General tab of the settings dialog. The settings apply to all configured accounts. The :guilabel:`Ignored Files Editor` provides a list of files that are ignored
|
||||
The Ignored Files Editor can be opened by clicking on the button in the General
|
||||
tab of the settings dialog. The settings apply to all configured accounts. The
|
||||
:guilabel:`Ignored Files Editor` provides a list of files that are ignored
|
||||
(that is, not synchronized) by the client and server during synchronizations.
|
||||
You may add additional files or directories that you want to exclude from the
|
||||
synchronization process. In addition to using standard characters, the Ignored
|
||||
@@ -184,7 +196,8 @@ components of the path being checked.
|
||||
|
||||
.. note:: Custom entries are currently not validated for syntactical
|
||||
correctness by the editor, so you will not see any warnings for bad
|
||||
syntax. If your synchronization does not work as you expected, check your syntax.
|
||||
syntax. If your synchronization does not work as you expected, check your
|
||||
syntax.
|
||||
|
||||
Each pattern string in the list is preceded by a checkbox. When the check box
|
||||
contains a check mark, in addition to ignoring the file or directory component
|
||||
@@ -197,8 +210,8 @@ this list:
|
||||
- The ownCloud Client always excludes files containing characters that cannot
|
||||
be synchronized to other file systems.
|
||||
|
||||
- Files are removed that cause individual errors three times during a synchronization.
|
||||
However, the client provides the option of retrying a synchronization three additional
|
||||
times on files that produce errors.
|
||||
- Files are removed that cause individual errors three times during a
|
||||
synchronization. However, the client provides the option of retrying a
|
||||
synchronization three additional times on files that produce errors.
|
||||
|
||||
For more detailed information see :ref:`ignored-files-label`.
|
||||
|
||||
@@ -50,6 +50,12 @@ OPTIONS
|
||||
``--httpproxy http://[user@pass:]<server>:<port>``
|
||||
Uses ``server`` as HTTP proxy.
|
||||
|
||||
``--nonshib``
|
||||
Uses Non Shibboleth WebDAV Authentication
|
||||
|
||||
``--davpath [path]``
|
||||
Overrides the WebDAV Path with ``path``
|
||||
|
||||
Example
|
||||
=======
|
||||
To synchronize the ownCloud directory ``Music`` to the local directory ``media/music``
|
||||
|
||||
@@ -42,6 +42,12 @@ Other command line switches supported by ``owncloudcmd`` include the following:
|
||||
``--unsyncedfolders [file]``
|
||||
File containing list of folders to not sync
|
||||
|
||||
``--nonshib``
|
||||
Uses Non Shibboleth WebDAV Authentication
|
||||
|
||||
``--davpath [path]``
|
||||
Overrides the WebDAV Path with ``path``
|
||||
|
||||
Credential Handling
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ Where:
|
||||
* ``Connected to <ownCloud instance> as <user>``: Indicates the ownCloud server
|
||||
which the client is syncing with and the user account on that server.
|
||||
|
||||
* ``Add Folder...``: Provides the ability to add another folder to the sync
|
||||
(see ``Adding a folder``).
|
||||
* ``Add Folder Sync Connection...``: Provides the ability to add another folder to the sync
|
||||
(see ``Adding a folder sync connection``).
|
||||
* ``Pause/Resume``: Will pause the current sync or prevent the client from
|
||||
starting a new sync. Resume will resume the sync process.
|
||||
* ``Remove``: Will remove the selected folder from being synced. This is used,
|
||||
@@ -74,11 +74,11 @@ Where:
|
||||
.. image:: images/settings_account.png
|
||||
:scale: 50 %
|
||||
|
||||
Adding a Folder
|
||||
^^^^^^^^^^^^^^^
|
||||
Adding a Folder Sync Connection
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Adding a new sync is initiated by clicking ``Add Folder...`` in the ``Account``
|
||||
settings.
|
||||
Adding a new sync is initiated by clicking ``Add Folder Sync Connection`` in
|
||||
the ``Account`` settings.
|
||||
|
||||
..note:: To add a folder, you must not already sync a folder that contains this
|
||||
folder. By default, the wizard sets up the root folder of the ownCloud
|
||||
|
||||
|
Depois Largura: | Altura: | Tamanho: 243 B |
|
Depois Largura: | Altura: | Tamanho: 122 B |
@@ -1,7 +1,17 @@
|
||||
add_subdirectory(MacOSX)
|
||||
if (APPLE)
|
||||
add_subdirectory(MacOSX)
|
||||
endif()
|
||||
add_subdirectory(icons)
|
||||
|
||||
if( UNIX AND NOT APPLE )
|
||||
add_subdirectory(nautilus)
|
||||
endif()
|
||||
|
||||
find_package(ECM 1.2.0 CONFIG QUIET)
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
find_package(KF5 "5.16" COMPONENTS KIO)
|
||||
if(KF5_FOUND)
|
||||
add_subdirectory(dolphin)
|
||||
else()
|
||||
message("Dolhin plugin disabled: KDE Frameworks 5.16 not found")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
From 3a26dc77f8e988ea99b23c4d5a2c831ecc31c920 Mon Sep 17 00:00:00 2001
|
||||
From: Olivier Goffart <ogoffart@woboq.com>
|
||||
Date: Thu, 17 Jul 2014 13:26:56 +0200
|
||||
Subject: [PATCH] WIP: add KOverlayIconPlugin
|
||||
|
||||
---
|
||||
.../src/kitemviews/kfileitemmodelrolesupdater.cpp | 35 ++++++++++++-
|
||||
.../src/kitemviews/kfileitemmodelrolesupdater.h | 9 ++++
|
||||
lib/konq/CMakeLists.txt | 4 +-
|
||||
lib/konq/koverlayiconplugin.cpp | 30 ++++++++++++
|
||||
lib/konq/koverlayiconplugin.desktop | 4 ++
|
||||
lib/konq/koverlayiconplugin.h | 57 ++++++++++++++++++++++
|
||||
6 files changed, 137 insertions(+), 2 deletions(-)
|
||||
create mode 100644 lib/konq/koverlayiconplugin.cpp
|
||||
create mode 100644 lib/konq/koverlayiconplugin.desktop
|
||||
create mode 100644 lib/konq/koverlayiconplugin.h
|
||||
|
||||
diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
index 0865d40..840a65d 100644
|
||||
--- a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
+++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
@@ -28,9 +28,11 @@
|
||||
#include <KGlobal>
|
||||
#include <KIO/JobUiDelegate>
|
||||
#include <KIO/PreviewJob>
|
||||
+#include <KServiceTypeTrader>
|
||||
|
||||
#include "private/kpixmapmodifier.h"
|
||||
#include "private/kdirectorycontentscounter.h"
|
||||
+#include <koverlayiconplugin.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
@@ -129,6 +131,17 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
|
||||
m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this);
|
||||
connect(m_directoryContentsCounter, SIGNAL(result(QString,int)),
|
||||
this, SLOT(slotDirectoryContentsCountReceived(QString,int)));
|
||||
+
|
||||
+
|
||||
+ const KService::List pluginServices = KServiceTypeTrader::self()->query("KOverlayIconPlugin");
|
||||
+
|
||||
+ for (KService::List::ConstIterator it = pluginServices.constBegin(); it != pluginServices.constEnd(); ++it) {
|
||||
+ KOverlayIconPlugin* plugin = (*it)->createInstance<KOverlayIconPlugin>(this);
|
||||
+ if (plugin) {
|
||||
+ m_overlayIconsPlugin.append(plugin);
|
||||
+ connect(plugin, SIGNAL(overlaysChanged(KUrl,QStringList)), this, SLOT(slotOverlaysChanged(KUrl,QStringList)));
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
|
||||
@@ -1075,7 +1088,11 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
|
||||
data.insert("type", item.mimeComment());
|
||||
}
|
||||
|
||||
- data.insert("iconOverlays", item.overlays());
|
||||
+ QStringList overlays = item.overlays();
|
||||
+ foreach(KOverlayIconPlugin *it, m_overlayIconsPlugin) {
|
||||
+ overlays.append(it->getOverlays(item));
|
||||
+ }
|
||||
+ data.insert("iconOverlays", overlays);
|
||||
|
||||
#ifdef HAVE_BALOO
|
||||
if (m_balooFileMonitor) {
|
||||
@@ -1086,6 +1103,22 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
|
||||
return data;
|
||||
}
|
||||
|
||||
+void KFileItemModelRolesUpdater::slotOverlaysChanged(const KUrl& url, const QStringList &)
|
||||
+{
|
||||
+ KFileItem item = m_model->fileItem(url);
|
||||
+ if (item.isNull())
|
||||
+ return;
|
||||
+ int index = m_model->index(item);
|
||||
+ QHash <QByteArray, QVariant> data = m_model->data(index);
|
||||
+ QStringList overlays = item.overlays();
|
||||
+ foreach(KOverlayIconPlugin *it, m_overlayIconsPlugin) {
|
||||
+ overlays.append(it->getOverlays(item));
|
||||
+ }
|
||||
+ data.insert("iconOverlays", overlays);
|
||||
+ m_model->setData(index, data);
|
||||
+}
|
||||
+
|
||||
+
|
||||
void KFileItemModelRolesUpdater::updateAllPreviews()
|
||||
{
|
||||
if (m_state == Paused) {
|
||||
diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h
|
||||
index a9e979a..6d3add0 100644
|
||||
--- a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h
|
||||
+++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <QSize>
|
||||
#include <QStringList>
|
||||
|
||||
+class KOverlayIconPlugin;
|
||||
class KDirectoryContentsCounter;
|
||||
class KFileItemModel;
|
||||
class KJob;
|
||||
@@ -180,6 +181,12 @@ private slots:
|
||||
void slotPreviewJobFinished();
|
||||
|
||||
/**
|
||||
+ * Is invoked when one of the KOverlayIconPlugin emit the signal that an overlay has changed
|
||||
+ */
|
||||
+ void slotOverlaysChanged(const KUrl&, const QStringList&);
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
* Resolves the sort role of the next item in m_pendingSortRole, applies it
|
||||
* to the model, and invokes itself if there are any pending items left. If
|
||||
* that is not the case, \a startUpdating() is called.
|
||||
@@ -331,6 +338,8 @@ private:
|
||||
|
||||
KDirectoryContentsCounter* m_directoryContentsCounter;
|
||||
|
||||
+ QList<KOverlayIconPlugin*> m_overlayIconsPlugin;
|
||||
+
|
||||
#ifdef HAVE_BALOO
|
||||
Baloo::FileMonitor* m_balooFileMonitor;
|
||||
#endif
|
||||
diff --git a/lib/konq/CMakeLists.txt b/lib/konq/CMakeLists.txt
|
||||
index 8ecbfa9..7381caf 100644
|
||||
--- a/lib/konq/CMakeLists.txt
|
||||
+++ b/lib/konq/CMakeLists.txt
|
||||
@@ -22,6 +22,7 @@ set(konq_LIB_SRCS
|
||||
konq_historyprovider.cpp
|
||||
kversioncontrolplugin.cpp # used by dolphin and its version control plugins (deprecated)
|
||||
kversioncontrolplugin2.cpp # used by dolphin and its version control plugins
|
||||
+ koverlayiconplugin.cpp
|
||||
|
||||
konq_nameandurlinputdialog.cpp # deprecated (functionality has moved to kdelibs)
|
||||
knewmenu.cpp # deprecated (functionality has moved to kdelibs)
|
||||
@@ -67,8 +68,9 @@ install( FILES
|
||||
konq_fileitemcapabilities.h
|
||||
kversioncontrolplugin.h
|
||||
kversioncontrolplugin2.h
|
||||
+ koverlayiconplugin.h
|
||||
konq_historyprovider.h
|
||||
konq_historyentry.h
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT Devel
|
||||
)
|
||||
-install( FILES konqpopupmenuplugin.desktop konqdndpopupmenuplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} )
|
||||
+install( FILES konqpopupmenuplugin.desktop konqdndpopupmenuplugin.desktop koverlayiconplugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} )
|
||||
diff --git a/lib/konq/koverlayiconplugin.cpp b/lib/konq/koverlayiconplugin.cpp
|
||||
new file mode 100644
|
||||
index 0000000..6125040
|
||||
--- /dev/null
|
||||
+++ b/lib/konq/koverlayiconplugin.cpp
|
||||
@@ -0,0 +1,30 @@
|
||||
+/*****************************************************************************
|
||||
+ * Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com> *
|
||||
+ * *
|
||||
+ * This library is free software; you can redistribute it and/or *
|
||||
+ * modify it under the terms of the GNU Library General Public *
|
||||
+ * License version 2 as published by the Free Software Foundation. *
|
||||
+ * *
|
||||
+ * This library is distributed in the hope that it will be useful, *
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
+ * Library General Public License for more details. *
|
||||
+ * *
|
||||
+ * You should have received a copy of the GNU Library General Public License *
|
||||
+ * along with this library; see the file COPYING.LIB. If not, write to *
|
||||
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
|
||||
+ * Boston, MA 02110-1301, USA. *
|
||||
+ *****************************************************************************/
|
||||
+
|
||||
+#include "koverlayiconplugin.h"
|
||||
+#include <KFileItem>
|
||||
+
|
||||
+KOverlayIconPlugin::KOverlayIconPlugin(QObject* parent) : QObject(parent)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+KOverlayIconPlugin::~KOverlayIconPlugin()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+#include "koverlayiconplugin.moc"
|
||||
diff --git a/lib/konq/koverlayiconplugin.desktop b/lib/konq/koverlayiconplugin.desktop
|
||||
new file mode 100644
|
||||
index 0000000..65a1170
|
||||
--- /dev/null
|
||||
+++ b/lib/konq/koverlayiconplugin.desktop
|
||||
@@ -0,0 +1,4 @@
|
||||
+[Desktop Entry]
|
||||
+Type=ServiceType
|
||||
+X-KDE-ServiceType=KOverlayIconPlugin
|
||||
+Comment=Plugin to add overlay icons in Dolphin
|
||||
diff --git a/lib/konq/koverlayiconplugin.h b/lib/konq/koverlayiconplugin.h
|
||||
new file mode 100644
|
||||
index 0000000..bcdf31b
|
||||
--- /dev/null
|
||||
+++ b/lib/konq/koverlayiconplugin.h
|
||||
@@ -0,0 +1,57 @@
|
||||
+/*****************************************************************************
|
||||
+ * Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com> *
|
||||
+ * *
|
||||
+ * This library is free software; you can redistribute it and/or *
|
||||
+ * modify it under the terms of the GNU Library General Public *
|
||||
+ * License version 2 as published by the Free Software Foundation. *
|
||||
+ * *
|
||||
+ * This library is distributed in the hope that it will be useful, *
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
+ * Library General Public License for more details. *
|
||||
+ * *
|
||||
+ * You should have received a copy of the GNU Library General Public License *
|
||||
+ * along with this library; see the file COPYING.LIB. If not, write to *
|
||||
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
|
||||
+ * Boston, MA 02110-1301, USA. *
|
||||
+ *****************************************************************************/
|
||||
+
|
||||
+
|
||||
+#ifndef OverlayIconPlugin_H
|
||||
+#define OverlayIconPlugin_H
|
||||
+
|
||||
+#include <QtCore/QObject>
|
||||
+#include <libkonq_export.h>
|
||||
+
|
||||
+class KUrl;
|
||||
+class KFileItem;
|
||||
+
|
||||
+/**
|
||||
+ * @brief Base class for overlay icon plugins.
|
||||
+ *
|
||||
+ * Enables the file manager to show custom overlay icons on files.
|
||||
+ *
|
||||
+ * To write a custom plugin you need to create a .desktop file for your plugin with
|
||||
+ * KDE-ServiceTypes=KOverlayIconPlugin
|
||||
+ */
|
||||
+class LIBKONQ_EXPORT KOverlayIconPlugin : public QObject {
|
||||
+ Q_OBJECT
|
||||
+ void *d;
|
||||
+public:
|
||||
+ explicit KOverlayIconPlugin(QObject *parent = 0);
|
||||
+ ~KOverlayIconPlugin();
|
||||
+
|
||||
+ /**
|
||||
+ * Returns a list of overlay pixmap to add to a file
|
||||
+ * This can be a path to an icon, or the icon name
|
||||
+ */
|
||||
+ virtual QStringList getOverlays(const KFileItem &item) = 0;
|
||||
+signals:
|
||||
+
|
||||
+ /**
|
||||
+ * Emit this signal when the list of overlay icon changed for a given URL
|
||||
+ */
|
||||
+ void overlaysChanged(const KUrl &url, const QStringList &overlays);
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
--
|
||||
2.1.3
|
||||
|
||||
@@ -1,17 +1,46 @@
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(dolphin-owncloud)
|
||||
find_package(KDE4 REQUIRED)
|
||||
include(KDE4Defaults)
|
||||
include(MacroLibrary)
|
||||
find_package(LibKonq REQUIRED)
|
||||
|
||||
add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
include(FeatureSummary)
|
||||
set(QT_MIN_VERSION "5.3.0")
|
||||
set(KF5_MIN_VERSION "5.16.0")
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Network)
|
||||
|
||||
find_package(ECM 1.2.0 REQUIRED CONFIG)
|
||||
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS CoreAddons KIO)
|
||||
|
||||
set_package_properties(DolphinVcs PROPERTIES
|
||||
DESCRIPTION "the Dolphin plugin library"
|
||||
URL "http://dolphin.kde.org/"
|
||||
TYPE REQUIRED
|
||||
PURPOSE "Provides plugin interfaces for Dolphin."
|
||||
)
|
||||
|
||||
include(KDEInstallDirs)
|
||||
include(KDECMakeSettings)
|
||||
include(KDECompilerSettings)
|
||||
include(ECMMarkNonGuiExecutable)
|
||||
include(GenerateExportHeader)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS)
|
||||
include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} )
|
||||
include_directories( ${KDE4_INCLUDE_DIR} ${QT_INCLUDES} ${LIBKONQ_INCLUDE_DIR} )
|
||||
|
||||
kde4_add_plugin(ownclouddolphinplugin ownclouddolphinplugin.cpp)
|
||||
#---HELPER---
|
||||
add_library(ownclouddolphinpluginhelper SHARED ownclouddolphinpluginhelper.cpp)
|
||||
target_link_libraries(ownclouddolphinpluginhelper Qt5::Network)
|
||||
generate_export_header(ownclouddolphinpluginhelper)
|
||||
install(TARGETS ownclouddolphinpluginhelper LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
target_link_libraries(ownclouddolphinplugin ${KDE4_KIO_LIBS} ${LIBKONQ_LIBRARY})
|
||||
install(FILES ownclouddolphinplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR})
|
||||
install(TARGETS ownclouddolphinplugin DESTINATION ${PLUGIN_INSTALL_DIR})
|
||||
#---OVERLAY PLUGIN---
|
||||
kcoreaddons_add_plugin(ownclouddolphinoverlayplugin INSTALL_NAMESPACE "kf5/overlayicon"
|
||||
JSON ownclouddolphinoverlayplugin.json SOURCES ownclouddolphinoverlayplugin.cpp)
|
||||
target_link_libraries(ownclouddolphinoverlayplugin KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets ownclouddolphinpluginhelper)
|
||||
|
||||
#---ACTION PLUGIN---
|
||||
add_library(ownclouddolphinactionplugin MODULE ownclouddolphinactionplugin.cpp)
|
||||
target_link_libraries(ownclouddolphinactionplugin KF5::CoreAddons KF5::KIOCore KF5::KIOWidgets ownclouddolphinpluginhelper)
|
||||
install(FILES ownclouddolphinactionplugin.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR})
|
||||
install(TARGETS ownclouddolphinactionplugin DESTINATION ${KDE_INSTALL_PLUGINDIR})
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
|
||||
|
||||
- The patch 0001-KOverlayIconPlugin.patch should be applied to kde-baseapps git repository
|
||||
(It should apply to both KDE/4.14 or Applications/14.12 branches)
|
||||
|
||||
- Recompile and install dolphin
|
||||
- Recompile and install recent enough version of dolphin and kio (git from oct 2015)
|
||||
|
||||
- Build and install the plugin
|
||||
|
||||
- After installing, run
|
||||
kdeinit4 --noincremental
|
||||
- Make sure to set XDG_DATA_DIRS=$PREFIX/share, QT_PLUGIN_PATH=$PREFIX/lib64/plugins/
|
||||
|
||||
- To test that the plugin is well installed
|
||||
ktraderclient --servicetype KOverlayIconPlugin
|
||||
It should show the Owncloud plugin
|
||||
- After installing, run
|
||||
kdeinit5 --noincremental
|
||||
|
||||
- restart dolphin (make sure to kill all instances)
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
#include <KIOWidgets/kabstractfileitemactionplugin.h>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <KIOCore/kfileitem.h>
|
||||
#include <KIOCore/KFileItemListProperties>
|
||||
#include <QtWidgets/QAction>
|
||||
#include <QtCore/QTimer>
|
||||
#include "ownclouddolphinpluginhelper.h"
|
||||
|
||||
class OwncloudDolphinPluginAction : public KAbstractFileItemActionPlugin
|
||||
{
|
||||
public:
|
||||
explicit OwncloudDolphinPluginAction(QObject* parent, const QList<QVariant>&)
|
||||
: KAbstractFileItemActionPlugin(parent) { }
|
||||
|
||||
QList<QAction*> actions(const KFileItemListProperties& fileItemInfos, QWidget* parentWidget) Q_DECL_OVERRIDE
|
||||
{
|
||||
auto helper = OwncloudDolphinPluginHelper::instance();
|
||||
QList<QUrl> urls = fileItemInfos.urlList();
|
||||
if (urls.count() != 1 || !helper->isConnected())
|
||||
return {};
|
||||
|
||||
auto url = urls.first();
|
||||
if (!url.isLocalFile())
|
||||
return {};
|
||||
auto localFile = url.toLocalFile();
|
||||
|
||||
const auto paths = helper->paths();
|
||||
if (!std::any_of(paths.begin(), paths.end(), [&](const QString &s) {
|
||||
return localFile.startsWith(s);
|
||||
} ))
|
||||
return {};
|
||||
|
||||
auto act = new QAction(parentWidget);
|
||||
act->setText(helper->shareActionString());
|
||||
connect(act, &QAction::triggered, this, [localFile, helper] {
|
||||
helper->sendCommand(QByteArray("SHARE:"+localFile.toUtf8()+"\n"));
|
||||
} );
|
||||
return { act };
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
K_PLUGIN_FACTORY(OwncloudDolphinPluginActionFactory, registerPlugin<OwncloudDolphinPluginAction>();)
|
||||
K_EXPORT_PLUGIN(OwncloudDolphinPluginActionFactory("ownclouddolhpinpluginaction"))
|
||||
|
||||
#include "ownclouddolphinactionplugin.moc"
|
||||
@@ -0,0 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Type=Service
|
||||
Name=OwncloudAction
|
||||
ServiceTypes=KFileItemAction/Plugin
|
||||
MimeType=application/octet-stream;inode/directory;
|
||||
X-KDE-Library=ownclouddolphinactionplugin
|
||||
@@ -0,0 +1,101 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#include <KOverlayIconPlugin>
|
||||
#include <KPluginFactory>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <KIOCore/kfileitem.h>
|
||||
#include <QTimer>
|
||||
#include "ownclouddolphinpluginhelper.h"
|
||||
|
||||
class OwncloudDolphinPlugin : public KOverlayIconPlugin
|
||||
{
|
||||
Q_PLUGIN_METADATA(IID "com.owncloud.ovarlayiconplugin" FILE "ownclouddolphinoverlayplugin.json");
|
||||
Q_OBJECT
|
||||
|
||||
typedef QHash<QByteArray, QByteArray> StatusMap;
|
||||
StatusMap m_status;
|
||||
|
||||
public:
|
||||
|
||||
OwncloudDolphinPlugin() {
|
||||
auto helper = OwncloudDolphinPluginHelper::instance();
|
||||
QObject::connect(helper, &OwncloudDolphinPluginHelper::commandRecieved,
|
||||
this, &OwncloudDolphinPlugin::slotCommandRecieved);
|
||||
}
|
||||
|
||||
QStringList getOverlays(const QUrl& url) override {
|
||||
auto helper = OwncloudDolphinPluginHelper::instance();
|
||||
if (!helper->isConnected())
|
||||
return QStringList();
|
||||
if (!url.isLocalFile())
|
||||
return QStringList();
|
||||
const QByteArray localFile = url.toLocalFile().toUtf8();
|
||||
|
||||
helper->sendCommand(QByteArray("RETRIEVE_FILE_STATUS:" + localFile + "\n"));
|
||||
|
||||
StatusMap::iterator it = m_status.find(localFile);
|
||||
if (it != m_status.constEnd()) {
|
||||
return overlaysForString(*it);
|
||||
}
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
private:
|
||||
QStringList overlaysForString(const QByteArray &status) {
|
||||
QStringList r;
|
||||
if (status.startsWith("NOP"))
|
||||
return r;
|
||||
|
||||
if (status.startsWith("OK"))
|
||||
r << "ownCloud_ok";
|
||||
if (status.startsWith("SYNC") || status.startsWith("NEW"))
|
||||
r << "ownCloud_sync";
|
||||
if (status.startsWith("IGNORE") || status.startsWith("WARN"))
|
||||
r << "ownCloud_warn";
|
||||
if (status.startsWith("ERROR"))
|
||||
r << "ownCloud_error";
|
||||
|
||||
if (status.contains("+SWM"))
|
||||
r << "document-share";
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void slotCommandRecieved(const QByteArray &line) {
|
||||
|
||||
QList<QByteArray> tokens = line.split(':');
|
||||
if (tokens.count() != 3)
|
||||
return;
|
||||
if (tokens[0] != "STATUS" && tokens[0] != "BROADCAST")
|
||||
return;
|
||||
if (tokens[2].isEmpty())
|
||||
return;
|
||||
|
||||
const QByteArray name = tokens[2];
|
||||
QByteArray &status = m_status[name]; // reference to the item in the hash
|
||||
if (status == tokens[1])
|
||||
return;
|
||||
status = tokens[1];
|
||||
|
||||
emit overlaysChanged(QUrl::fromLocalFile(QString::fromUtf8(name)), overlaysForString(status));
|
||||
}
|
||||
};
|
||||
|
||||
#include "ownclouddolphinoverlayplugin.moc"
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Description": "Overlay icon for owncloud",
|
||||
"ServiceTypes": [
|
||||
"KOverlayIconPlugin"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#include <koverlayiconplugin.h>
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
#include <kdebug.h>
|
||||
#include <kfileitem.h>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
|
||||
class OwncloudDolphinPlugin : public KOverlayIconPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
QLocalSocket m_socket;
|
||||
typedef QHash<QByteArray, QByteArray> StatusMap;
|
||||
StatusMap m_status;
|
||||
QByteArray m_line;
|
||||
|
||||
public:
|
||||
explicit OwncloudDolphinPlugin(QObject* parent, const QList<QVariant>&) : KOverlayIconPlugin(parent) {
|
||||
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
|
||||
tryConnect();
|
||||
}
|
||||
|
||||
virtual QStringList getOverlays(const KFileItem& item) {
|
||||
KUrl url = item.url();
|
||||
if (!url.isLocalFile())
|
||||
return QStringList();
|
||||
const QByteArray localFile = url.toLocalFile().toUtf8();
|
||||
kDebug() << localFile;
|
||||
|
||||
tryConnect();
|
||||
if (m_socket.state() == QLocalSocket::ConnectingState) {
|
||||
if (!m_socket.waitForConnected(100)) {
|
||||
kWarning() << "not connected" << m_socket.errorString();
|
||||
}
|
||||
}
|
||||
if (m_socket.state() == QLocalSocket::ConnectedState) {
|
||||
m_socket.write("RETRIEVE_FILE_STATUS:");
|
||||
m_socket.write(localFile);
|
||||
m_socket.write("\n");
|
||||
}
|
||||
|
||||
StatusMap::iterator it = m_status.find(localFile);
|
||||
if (it != m_status.constEnd()) {
|
||||
return overlaysForString(*it);
|
||||
}
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void tryConnect() {
|
||||
if (m_socket.state() != QLocalSocket::UnconnectedState)
|
||||
return;
|
||||
QString runtimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
|
||||
QString socketPath = runtimeDir + "/" + "ownCloud" + "/socket";
|
||||
m_socket.connectToServer(socketPath);
|
||||
}
|
||||
|
||||
QStringList overlaysForString(const QByteArray status) {
|
||||
QStringList r;
|
||||
if (status.startsWith("NOP"))
|
||||
return r;
|
||||
|
||||
if (status.startsWith("OK"))
|
||||
r << "dialog-ok";
|
||||
if (status.startsWith("SYNC") || status.startsWith("NEW"))
|
||||
r << "view-refresh";
|
||||
|
||||
if (status.contains("+SWM"))
|
||||
r << "document-share";
|
||||
|
||||
kDebug() << status << r;
|
||||
return r;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void readyRead() {
|
||||
while (m_socket.bytesAvailable()) {
|
||||
m_line += m_socket.readLine();
|
||||
if (!m_line.endsWith("\n"))
|
||||
continue;
|
||||
QByteArray line;
|
||||
qSwap(line, m_line);
|
||||
line.chop(1);
|
||||
kDebug() << "got line " << line;
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
QList<QByteArray> tokens = line.split(':');
|
||||
if (tokens.count() != 3)
|
||||
continue;
|
||||
if (tokens[0] != "STATUS" && tokens[0] != "BROADCAST")
|
||||
continue;
|
||||
if (tokens[2].isEmpty())
|
||||
continue;
|
||||
|
||||
const QByteArray name = tokens[2];
|
||||
QByteArray &status = m_status[name]; // reference to the item in the hash
|
||||
if (status == tokens[1])
|
||||
continue;
|
||||
status = tokens[1];
|
||||
|
||||
emit this->overlaysChanged(KUrl::fromLocalFile(QString::fromUtf8(name)), overlaysForString(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_FACTORY(OwncloudDolphinPluginFactory, registerPlugin<OwncloudDolphinPlugin>();)
|
||||
K_EXPORT_PLUGIN(OwncloudDolphinPluginFactory("ownclouddolhpinplugin"))
|
||||
|
||||
|
||||
#include "ownclouddolphinplugin.moc"
|
||||
@@ -1,6 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Service
|
||||
Name=Owncloud
|
||||
X-KDE-ServiceTypes=KOverlayIconPlugin
|
||||
MimeType=text/plain;
|
||||
X-KDE-Library=ownclouddolphinplugin
|
||||
@@ -0,0 +1,98 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
#include <qcoreevent.h>
|
||||
#include <QFile>
|
||||
#include "ownclouddolphinpluginhelper.h"
|
||||
|
||||
OwncloudDolphinPluginHelper* OwncloudDolphinPluginHelper::instance()
|
||||
{
|
||||
static OwncloudDolphinPluginHelper self;
|
||||
return &self;
|
||||
}
|
||||
|
||||
OwncloudDolphinPluginHelper::OwncloudDolphinPluginHelper()
|
||||
{
|
||||
connect(&_socket, &QLocalSocket::connected, this, &OwncloudDolphinPluginHelper::slotConnected);
|
||||
connect(&_socket, &QLocalSocket::readyRead, this, &OwncloudDolphinPluginHelper::slotReadyRead);
|
||||
_connectTimer.start(45 * 1000, Qt::VeryCoarseTimer, this);
|
||||
tryConnect();
|
||||
}
|
||||
|
||||
void OwncloudDolphinPluginHelper::timerEvent(QTimerEvent *e)
|
||||
{
|
||||
if (e->timerId() == _connectTimer.timerId()) {
|
||||
tryConnect();
|
||||
return;
|
||||
}
|
||||
QObject::timerEvent(e);
|
||||
}
|
||||
|
||||
bool OwncloudDolphinPluginHelper::isConnected() const
|
||||
{
|
||||
return _socket.state() == QLocalSocket::ConnectedState;
|
||||
}
|
||||
|
||||
void OwncloudDolphinPluginHelper::sendCommand(const char* data)
|
||||
{
|
||||
_socket.write(data);
|
||||
_socket.flush();
|
||||
}
|
||||
|
||||
void OwncloudDolphinPluginHelper::slotConnected()
|
||||
{
|
||||
sendCommand("SHARE_MENU_TITLE:\n");
|
||||
}
|
||||
|
||||
void OwncloudDolphinPluginHelper::tryConnect()
|
||||
{
|
||||
if (_socket.state() != QLocalSocket::UnconnectedState) {
|
||||
return;
|
||||
}
|
||||
QString runtimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
|
||||
QString socketPath = runtimeDir + QLatin1String("/ownCloud/socket");
|
||||
_socket.connectToServer(socketPath);
|
||||
}
|
||||
|
||||
void OwncloudDolphinPluginHelper::slotReadyRead()
|
||||
{
|
||||
while (_socket.bytesAvailable()) {
|
||||
_line += _socket.readLine();
|
||||
if (!_line.endsWith("\n"))
|
||||
continue;
|
||||
QByteArray line;
|
||||
qSwap(line, _line);
|
||||
line.chop(1);
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
|
||||
if (line.startsWith("REGISTER_PATH:")) {
|
||||
auto col = line.indexOf(':');
|
||||
QString file = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1);
|
||||
_paths.append(file);
|
||||
continue;
|
||||
} else if (line.startsWith("SHARE_MENU_TITLE:")) {
|
||||
auto col = line.indexOf(':');
|
||||
_shareActionString = QString::fromUtf8(line.constData() + col + 1, line.size() - col - 1);
|
||||
continue;
|
||||
}
|
||||
emit commandRecieved(line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <QBasicTimer>
|
||||
#include <QLocalSocket>
|
||||
#include "ownclouddolphinpluginhelper_export.h"
|
||||
|
||||
class OWNCLOUDDOLPHINPLUGINHELPER_EXPORT OwncloudDolphinPluginHelper : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static OwncloudDolphinPluginHelper *instance();
|
||||
|
||||
QString shareActionString() const { return _shareActionString; }
|
||||
bool isConnected() const;
|
||||
void sendCommand(const char *data);
|
||||
QVector<QString> paths() const { return _paths; }
|
||||
|
||||
signals:
|
||||
void commandRecieved(const QByteArray &cmd);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent*) override;
|
||||
|
||||
private:
|
||||
OwncloudDolphinPluginHelper();
|
||||
void slotConnected();
|
||||
void slotReadyRead();
|
||||
void tryConnect();
|
||||
QLocalSocket _socket;
|
||||
QByteArray _line;
|
||||
QVector<QString> _paths;
|
||||
QString _shareActionString;
|
||||
QBasicTimer _connectTimer;
|
||||
};
|
||||
@@ -1,267 +0,0 @@
|
||||
From d452ed613a9e02ed81eec2f3226f28babff240c8 Mon Sep 17 00:00:00 2001
|
||||
From: Olivier Goffart <ogoffart@woboq.com>
|
||||
Date: Thu, 17 Jul 2014 13:26:56 +0200
|
||||
Subject: [PATCH] WIP: add KOverlayIconPlugin
|
||||
|
||||
Conflicts:
|
||||
dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
lib/konq/CMakeLists.txt
|
||||
---
|
||||
.../src/kitemviews/kfileitemmodelrolesupdater.cpp | 38 ++++++++++++++-
|
||||
.../src/kitemviews/kfileitemmodelrolesupdater.h | 9 ++++
|
||||
lib/konq/src/CMakeLists.txt | 4 +-
|
||||
lib/konq/src/koverlayiconplugin.cpp | 30 ++++++++++++
|
||||
lib/konq/src/koverlayiconplugin.desktop | 4 ++
|
||||
lib/konq/src/koverlayiconplugin.h | 57 ++++++++++++++++++++++
|
||||
6 files changed, 140 insertions(+), 2 deletions(-)
|
||||
create mode 100644 lib/konq/src/koverlayiconplugin.cpp
|
||||
create mode 100644 lib/konq/src/koverlayiconplugin.desktop
|
||||
create mode 100644 lib/konq/src/koverlayiconplugin.h
|
||||
|
||||
diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
index df521e2..4d94836 100644
|
||||
--- a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
+++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.cpp
|
||||
@@ -29,9 +29,11 @@
|
||||
#include <KJobWidgets>
|
||||
#include <KIO/JobUiDelegate>
|
||||
#include <KIO/PreviewJob>
|
||||
+#include <KServiceTypeTrader>
|
||||
|
||||
#include "private/kpixmapmodifier.h"
|
||||
#include "private/kdirectorycontentscounter.h"
|
||||
+#include <koverlayiconplugin.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPainter>
|
||||
@@ -129,6 +131,20 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO
|
||||
m_directoryContentsCounter = new KDirectoryContentsCounter(m_model, this);
|
||||
connect(m_directoryContentsCounter, &KDirectoryContentsCounter::result,
|
||||
this, &KFileItemModelRolesUpdater::slotDirectoryContentsCountReceived);
|
||||
+
|
||||
+
|
||||
+ const KService::List pluginServices = KServiceTypeTrader::self()->query("KOverlayIconPlugin");
|
||||
+
|
||||
+ for (KService::List::ConstIterator it = pluginServices.constBegin(); it != pluginServices.constEnd(); ++it) {
|
||||
+ QString error;
|
||||
+ KOverlayIconPlugin* plugin = (*it)->createInstance<KOverlayIconPlugin>(this, QVariantList(), &error);
|
||||
+ if (plugin) {
|
||||
+ m_overlayIconsPlugin.append(plugin);
|
||||
+ connect(plugin, &KOverlayIconPlugin::overlaysChanged, this, &KFileItemModelRolesUpdater::slotOverlaysChanged);
|
||||
+ } else {
|
||||
+ qWarning() << "Could not load plugin " << (*it)->name() << ":" << error;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
KFileItemModelRolesUpdater::~KFileItemModelRolesUpdater()
|
||||
@@ -1065,7 +1081,11 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
|
||||
data.insert("type", item.mimeComment());
|
||||
}
|
||||
|
||||
- data.insert("iconOverlays", item.overlays());
|
||||
+ QStringList overlays = item.overlays();
|
||||
+ foreach(KOverlayIconPlugin *it, m_overlayIconsPlugin) {
|
||||
+ overlays.append(it->getOverlays(item));
|
||||
+ }
|
||||
+ data.insert("iconOverlays", overlays);
|
||||
|
||||
#ifdef HAVE_BALOO
|
||||
if (m_balooFileMonitor) {
|
||||
@@ -1076,6 +1096,22 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte
|
||||
return data;
|
||||
}
|
||||
|
||||
+void KFileItemModelRolesUpdater::slotOverlaysChanged(const QUrl& url, const QStringList &)
|
||||
+{
|
||||
+ KFileItem item = m_model->fileItem(url);
|
||||
+ if (item.isNull())
|
||||
+ return;
|
||||
+ int index = m_model->index(item);
|
||||
+ QHash <QByteArray, QVariant> data = m_model->data(index);
|
||||
+ QStringList overlays = item.overlays();
|
||||
+ foreach(KOverlayIconPlugin *it, m_overlayIconsPlugin) {
|
||||
+ overlays.append(it->getOverlays(item));
|
||||
+ }
|
||||
+ data.insert("iconOverlays", overlays);
|
||||
+ m_model->setData(index, data);
|
||||
+}
|
||||
+
|
||||
+
|
||||
void KFileItemModelRolesUpdater::updateAllPreviews()
|
||||
{
|
||||
if (m_state == Paused) {
|
||||
diff --git a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h
|
||||
index 6c82dbe..1e5b98e 100644
|
||||
--- a/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h
|
||||
+++ b/dolphin/src/kitemviews/kfileitemmodelrolesupdater.h
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <QSize>
|
||||
#include <QStringList>
|
||||
|
||||
+class KOverlayIconPlugin;
|
||||
class KDirectoryContentsCounter;
|
||||
class KFileItemModel;
|
||||
class QPixmap;
|
||||
@@ -183,6 +184,12 @@ private slots:
|
||||
void slotPreviewJobFinished();
|
||||
|
||||
/**
|
||||
+ * Is invoked when one of the KOverlayIconPlugin emit the signal that an overlay has changed
|
||||
+ */
|
||||
+ void slotOverlaysChanged(const QUrl& url, const QStringList&);
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
* Resolves the sort role of the next item in m_pendingSortRole, applies it
|
||||
* to the model, and invokes itself if there are any pending items left. If
|
||||
* that is not the case, \a startUpdating() is called.
|
||||
@@ -333,6 +340,8 @@ private:
|
||||
|
||||
KDirectoryContentsCounter* m_directoryContentsCounter;
|
||||
|
||||
+ QList<KOverlayIconPlugin*> m_overlayIconsPlugin;
|
||||
+
|
||||
#ifdef HAVE_BALOO
|
||||
Baloo::FileMonitor* m_balooFileMonitor;
|
||||
#endif
|
||||
diff --git a/lib/konq/src/CMakeLists.txt b/lib/konq/src/CMakeLists.txt
|
||||
index 9c05b9f..0ac0526 100644
|
||||
--- a/lib/konq/src/CMakeLists.txt
|
||||
+++ b/lib/konq/src/CMakeLists.txt
|
||||
@@ -15,6 +15,7 @@ set(konq_LIB_SRCS
|
||||
konq_historyprovider.cpp # konqueror and konqueror/sidebar
|
||||
kversioncontrolplugin.cpp # used by dolphin and its version control plugins (deprecated)
|
||||
kversioncontrolplugin2.cpp # used by dolphin and its version control plugins
|
||||
+ koverlayiconplugin.cpp
|
||||
)
|
||||
|
||||
add_library(KF5Konq ${konq_LIB_SRCS})
|
||||
@@ -64,13 +65,14 @@ install(FILES
|
||||
konq_popupmenuplugin.h
|
||||
kversioncontrolplugin.h
|
||||
kversioncontrolplugin2.h
|
||||
+ koverlayiconplugin.h
|
||||
${LibKonq_BINARY_DIR}/src/libkonq_export.h
|
||||
|
||||
DESTINATION ${KF5_INCLUDE_INSTALL_DIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
-install(FILES konqpopupmenuplugin.desktop konqdndpopupmenuplugin.desktop
|
||||
+install(FILES konqpopupmenuplugin.desktop konqdndpopupmenuplugin.desktop koverlayiconplugin.desktop
|
||||
DESTINATION ${SERVICETYPES_INSTALL_DIR}
|
||||
)
|
||||
|
||||
diff --git a/lib/konq/src/koverlayiconplugin.cpp b/lib/konq/src/koverlayiconplugin.cpp
|
||||
new file mode 100644
|
||||
index 0000000..6125040
|
||||
--- /dev/null
|
||||
+++ b/lib/konq/src/koverlayiconplugin.cpp
|
||||
@@ -0,0 +1,30 @@
|
||||
+/*****************************************************************************
|
||||
+ * Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com> *
|
||||
+ * *
|
||||
+ * This library is free software; you can redistribute it and/or *
|
||||
+ * modify it under the terms of the GNU Library General Public *
|
||||
+ * License version 2 as published by the Free Software Foundation. *
|
||||
+ * *
|
||||
+ * This library is distributed in the hope that it will be useful, *
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
+ * Library General Public License for more details. *
|
||||
+ * *
|
||||
+ * You should have received a copy of the GNU Library General Public License *
|
||||
+ * along with this library; see the file COPYING.LIB. If not, write to *
|
||||
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
|
||||
+ * Boston, MA 02110-1301, USA. *
|
||||
+ *****************************************************************************/
|
||||
+
|
||||
+#include "koverlayiconplugin.h"
|
||||
+#include <KFileItem>
|
||||
+
|
||||
+KOverlayIconPlugin::KOverlayIconPlugin(QObject* parent) : QObject(parent)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+KOverlayIconPlugin::~KOverlayIconPlugin()
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+#include "koverlayiconplugin.moc"
|
||||
diff --git a/lib/konq/src/koverlayiconplugin.desktop b/lib/konq/src/koverlayiconplugin.desktop
|
||||
new file mode 100644
|
||||
index 0000000..65a1170
|
||||
--- /dev/null
|
||||
+++ b/lib/konq/src/koverlayiconplugin.desktop
|
||||
@@ -0,0 +1,4 @@
|
||||
+[Desktop Entry]
|
||||
+Type=ServiceType
|
||||
+X-KDE-ServiceType=KOverlayIconPlugin
|
||||
+Comment=Plugin to add overlay icons in Dolphin
|
||||
diff --git a/lib/konq/src/koverlayiconplugin.h b/lib/konq/src/koverlayiconplugin.h
|
||||
new file mode 100644
|
||||
index 0000000..bfdaa2f
|
||||
--- /dev/null
|
||||
+++ b/lib/konq/src/koverlayiconplugin.h
|
||||
@@ -0,0 +1,57 @@
|
||||
+/*****************************************************************************
|
||||
+ * Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com> *
|
||||
+ * *
|
||||
+ * This library is free software; you can redistribute it and/or *
|
||||
+ * modify it under the terms of the GNU Library General Public *
|
||||
+ * License version 2 as published by the Free Software Foundation. *
|
||||
+ * *
|
||||
+ * This library is distributed in the hope that it will be useful, *
|
||||
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
+ * Library General Public License for more details. *
|
||||
+ * *
|
||||
+ * You should have received a copy of the GNU Library General Public License *
|
||||
+ * along with this library; see the file COPYING.LIB. If not, write to *
|
||||
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
|
||||
+ * Boston, MA 02110-1301, USA. *
|
||||
+ *****************************************************************************/
|
||||
+
|
||||
+
|
||||
+#ifndef OverlayIconPlugin_H
|
||||
+#define OverlayIconPlugin_H
|
||||
+
|
||||
+#include <QtCore/QObject>
|
||||
+#include <libkonq_export.h>
|
||||
+
|
||||
+class KUrl;
|
||||
+class KFileItem;
|
||||
+
|
||||
+/**
|
||||
+ * @brief Base class for overlay icon plugins.
|
||||
+ *
|
||||
+ * Enables the file manager to show custom overlay icons on files.
|
||||
+ *
|
||||
+ * To write a custom plugin you need to create a .desktop file for your plugin with
|
||||
+ * KDE-ServiceTypes=KOverlayIconPlugin
|
||||
+ */
|
||||
+class LIBKONQ_EXPORT KOverlayIconPlugin : public QObject {
|
||||
+ Q_OBJECT
|
||||
+ void *d;
|
||||
+public:
|
||||
+ explicit KOverlayIconPlugin(QObject *parent = 0);
|
||||
+ ~KOverlayIconPlugin();
|
||||
+
|
||||
+ /**
|
||||
+ * Returns a list of overlay pixmap to add to a file
|
||||
+ * This can be a path to an icon, or the icon name
|
||||
+ */
|
||||
+ virtual QStringList getOverlays(const KFileItem &item) = 0;
|
||||
+signals:
|
||||
+
|
||||
+ /**
|
||||
+ * Emit this signal when the list of overlay icon changed for a given URL
|
||||
+ */
|
||||
+ void overlaysChanged(const QUrl &url, const QStringList &overlays);
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
--
|
||||
2.2.1
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
project(dolphin-owncloud)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
set(QT_MIN_VERSION "5.3.0")
|
||||
|
||||
find_package(ECM 1.2.0 REQUIRED CONFIG)
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
|
||||
|
||||
include(KDEInstallDirs)
|
||||
include(KDECMakeSettings)
|
||||
include(KDECompilerSettings)
|
||||
include(ECMInstallIcons)
|
||||
include(ECMSetupVersion)
|
||||
|
||||
find_package(Qt5 CONFIG REQUIRED Core DBus Test Widgets)
|
||||
find_package(KF5 REQUIRED Archive Bookmarks CoreAddons Config ConfigWidgets DBusAddons KIO KDELibs4Support Parts Activities)
|
||||
|
||||
|
||||
find_package(KF5Konq REQUIRED)
|
||||
|
||||
add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS)
|
||||
|
||||
add_library(ownclouddolphinplugin MODULE ownclouddolphinplugin.cpp)
|
||||
target_link_libraries(ownclouddolphinplugin KF5::Konq)
|
||||
install(FILES ownclouddolphinplugin.desktop DESTINATION ${SERVICES_INSTALL_DIR})
|
||||
install(TARGETS ownclouddolphinplugin DESTINATION ${PLUGIN_INSTALL_DIR})
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
|
||||
- The patch 0001-KOverlayIconPlugin.patch should be applied to kde-baseapps git repository
|
||||
(It should apply to frameworks branch)
|
||||
|
||||
- Recompile and install dolphin (frameworks branch)
|
||||
|
||||
- Build and install the plugin
|
||||
|
||||
- Make sure to set XDG_DATA_DIRS=$PREFIX/share, QT_PLUGIN_PATH=$PREFIX/lib64/plugins/
|
||||
|
||||
- After installing, run
|
||||
kdeinit5 --noincremental
|
||||
|
||||
- restart dolphin (make sure to kill all instances)
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2014 by Olivier Goffart <ogoffart@woboq.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#include <koverlayiconplugin.h>
|
||||
#include <KPluginFactory>
|
||||
#include <KPluginLoader>
|
||||
#include <kdebug.h>
|
||||
#include <kfileitem.h>
|
||||
#include <QtNetwork/QLocalSocket>
|
||||
|
||||
|
||||
class OwncloudDolphinPlugin : public KOverlayIconPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
QLocalSocket m_socket;
|
||||
typedef QHash<QByteArray, QByteArray> StatusMap;
|
||||
StatusMap m_status;
|
||||
QByteArray m_line;
|
||||
|
||||
public:
|
||||
explicit OwncloudDolphinPlugin(QObject* parent, const QList<QVariant>&) : KOverlayIconPlugin(parent) {
|
||||
connect(&m_socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
|
||||
tryConnect();
|
||||
}
|
||||
|
||||
virtual QStringList getOverlays(const KFileItem& item) {
|
||||
auto url = item.url();
|
||||
if (!url.isLocalFile())
|
||||
return QStringList();
|
||||
const QByteArray localFile = url.toLocalFile().toUtf8();
|
||||
kDebug() << localFile;
|
||||
|
||||
tryConnect();
|
||||
if (m_socket.state() == QLocalSocket::ConnectingState) {
|
||||
if (!m_socket.waitForConnected(100)) {
|
||||
kWarning() << "not connected" << m_socket.errorString();
|
||||
}
|
||||
}
|
||||
if (m_socket.state() == QLocalSocket::ConnectedState) {
|
||||
m_socket.write("RETRIEVE_FILE_STATUS:");
|
||||
m_socket.write(localFile);
|
||||
m_socket.write("\n");
|
||||
}
|
||||
|
||||
StatusMap::iterator it = m_status.find(localFile);
|
||||
if (it != m_status.constEnd()) {
|
||||
return overlaysForString(*it);
|
||||
}
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void tryConnect() {
|
||||
if (m_socket.state() != QLocalSocket::UnconnectedState)
|
||||
return;
|
||||
QString runtimeDir = QFile::decodeName(qgetenv("XDG_RUNTIME_DIR"));
|
||||
QString socketPath = runtimeDir + "/" + "ownCloud" + "/socket";
|
||||
m_socket.connectToServer(socketPath);
|
||||
}
|
||||
|
||||
QStringList overlaysForString(const QByteArray status) {
|
||||
QStringList r;
|
||||
if (status.startsWith("NOP"))
|
||||
return r;
|
||||
|
||||
if (status.startsWith("OK"))
|
||||
r << "dialog-ok";
|
||||
if (status.startsWith("SYNC") || status.startsWith("NEW"))
|
||||
r << "view-refresh";
|
||||
|
||||
if (status.contains("+SWM"))
|
||||
r << "document-share";
|
||||
|
||||
kDebug() << status << r;
|
||||
return r;
|
||||
}
|
||||
|
||||
private slots:
|
||||
void readyRead() {
|
||||
while (m_socket.bytesAvailable()) {
|
||||
m_line += m_socket.readLine();
|
||||
if (!m_line.endsWith("\n"))
|
||||
continue;
|
||||
QByteArray line;
|
||||
qSwap(line, m_line);
|
||||
line.chop(1);
|
||||
kDebug() << "got line " << line;
|
||||
if (line.isEmpty())
|
||||
continue;
|
||||
QList<QByteArray> tokens = line.split(':');
|
||||
if (tokens.count() != 3)
|
||||
continue;
|
||||
if (tokens[0] != "STATUS" && tokens[0] != "BROADCAST")
|
||||
continue;
|
||||
if (tokens[2].isEmpty())
|
||||
continue;
|
||||
|
||||
const QByteArray name = tokens[2];
|
||||
QByteArray &status = m_status[name]; // reference to the item in the hash
|
||||
if (status == tokens[1])
|
||||
continue;
|
||||
status = tokens[1];
|
||||
|
||||
emit this->overlaysChanged(QUrl::fromLocalFile(QString::fromUtf8(name)), overlaysForString(status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
K_PLUGIN_FACTORY(OwncloudDolphinPluginFactory, registerPlugin<OwncloudDolphinPlugin>();)
|
||||
K_EXPORT_PLUGIN(OwncloudDolphinPluginFactory("ownclouddolhpinplugin"))
|
||||
|
||||
|
||||
#include "ownclouddolphinplugin.moc"
|
||||
@@ -1,6 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Service
|
||||
Name=Owncloud
|
||||
X-KDE-ServiceTypes=KOverlayIconPlugin
|
||||
MimeType=text/plain;
|
||||
X-KDE-Library=ownclouddolphinplugin
|
||||