Compare commits
538 Commits
v1.4.0-rc1
...
v1.5.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cf7ba91c1 | ||
|
|
f8478ac27b | ||
|
|
b092a8cee3 | ||
|
|
bb2f221edf | ||
|
|
dd323bc296 | ||
|
|
31989d23a6 | ||
|
|
1291ffaf02 | ||
|
|
89d1e2d924 | ||
|
|
857ef33056 | ||
|
|
f5e8efd435 | ||
|
|
e5edb8e2c7 | ||
|
|
93ac78fd75 | ||
|
|
5f31d13236 | ||
|
|
ae69a24f5c | ||
|
|
fc986b0ab8 | ||
|
|
c5e435f9bd | ||
|
|
599451d250 | ||
|
|
af78f1996c | ||
|
|
41d5445980 | ||
|
|
e5afd8b901 | ||
|
|
6fad20d5fc | ||
|
|
7662a60111 | ||
|
|
dff17ec08e | ||
|
|
df5a72eb38 | ||
|
|
fa9d9c9909 | ||
|
|
a0d1dc8807 | ||
|
|
9e2f6c0258 | ||
|
|
ee2efbb071 | ||
|
|
8bc532705f | ||
|
|
c3dc84c58f | ||
|
|
72b0f4e573 | ||
|
|
861de89e9c | ||
|
|
a6dfc0a83e | ||
|
|
859f776440 | ||
|
|
4b7f75059a | ||
|
|
b3620e320b | ||
|
|
883576cb95 | ||
|
|
62ec7aa9c5 | ||
|
|
99fc570267 | ||
|
|
1f5c4bde14 | ||
|
|
046d180d2f | ||
|
|
278e76b774 | ||
|
|
09d850bfaa | ||
|
|
f0a6047ecf | ||
|
|
e8ffb17b3b | ||
|
|
09d4fa2127 | ||
|
|
f521301c51 | ||
|
|
de5161137f | ||
|
|
1abfd4ba44 | ||
|
|
ecf45856e1 | ||
|
|
148b22f53d | ||
|
|
ec075a6d2a | ||
|
|
6923f26597 | ||
|
|
206c62a171 | ||
|
|
c03b2bbc87 | ||
|
|
2f708c0877 | ||
|
|
58eb000163 | ||
|
|
f5f56e45c0 | ||
|
|
951ac79a68 | ||
|
|
ce47adb482 | ||
|
|
ef8fe11f5a | ||
|
|
30db533cea | ||
|
|
99eeaa0db5 | ||
|
|
80a01ecff3 | ||
|
|
5f3ce25ccf | ||
|
|
0d85810c23 | ||
|
|
4626ef1ffb | ||
|
|
23007613a9 | ||
|
|
0fc51704f5 | ||
|
|
46a403eb02 | ||
|
|
e719e80409 | ||
|
|
310278f580 | ||
|
|
59072a81f1 | ||
|
|
d55bffb319 | ||
|
|
232cbc45b5 | ||
|
|
a88d45bff4 | ||
|
|
090e474d70 | ||
|
|
4f7546768b | ||
|
|
7a3c086be2 | ||
|
|
5c8b6ed902 | ||
|
|
cb6918f3d5 | ||
|
|
489cc8aa29 | ||
|
|
d8a8d5da63 | ||
|
|
0d85dcdd9e | ||
|
|
7eddff39e7 | ||
|
|
81e47b0896 | ||
|
|
ef0c7348ad | ||
|
|
050bb55f1e | ||
|
|
fa715ce135 | ||
|
|
911e0bdd6e | ||
|
|
fa0f773fcb | ||
|
|
ac8296fb94 | ||
|
|
f47ce2fea6 | ||
|
|
33ff6b3934 | ||
|
|
ca3d8ab193 | ||
|
|
d85009a2e9 | ||
|
|
72d2ac09e3 | ||
|
|
6b7da798b8 | ||
|
|
2e4043b498 | ||
|
|
dc29046d61 | ||
|
|
0c6dca25c4 | ||
|
|
1a3f246c46 | ||
|
|
ad6c42b031 | ||
|
|
9ddedf81ac | ||
|
|
685c13dead | ||
|
|
c25c7daca7 | ||
|
|
2a17a2a102 | ||
|
|
4e22fff427 | ||
|
|
6165c38289 | ||
|
|
f554cca3d6 | ||
|
|
67132326d2 | ||
|
|
ea2b5fb29c | ||
|
|
0a2861a731 | ||
|
|
6f17131e3c | ||
|
|
ca3885de2a | ||
|
|
5d6700c68d | ||
|
|
37d6f6eeab | ||
|
|
fd1552f7a0 | ||
|
|
055a8d7e74 | ||
|
|
1964e60eb0 | ||
|
|
11acfde55a | ||
|
|
3a1c6429ab | ||
|
|
ecb2444923 | ||
|
|
65bd4be16e | ||
|
|
39e48d3d01 | ||
|
|
c0b3672f32 | ||
|
|
8d2950f66c | ||
|
|
55e82ee4c1 | ||
|
|
aa17be40cc | ||
|
|
97c661c909 | ||
|
|
2767e7084a | ||
|
|
5900b1ad25 | ||
|
|
20b9ae757d | ||
|
|
aff2dd9f44 | ||
|
|
e30c484a7a | ||
|
|
0f6dd8748f | ||
|
|
a342f63fdf | ||
|
|
5c4d240c66 | ||
|
|
e551e92e13 | ||
|
|
b98d97a96d | ||
|
|
f30ac49264 | ||
|
|
49ba252fff | ||
|
|
69269f8f75 | ||
|
|
e73730cb94 | ||
|
|
098e04c13f | ||
|
|
b0f6628584 | ||
|
|
42f6867329 | ||
|
|
0a9a3d8f04 | ||
|
|
ec850e83b9 | ||
|
|
08665d6ac2 | ||
|
|
4194a078d5 | ||
|
|
6758c89130 | ||
|
|
c15a1eedd1 | ||
|
|
bf6e1f10ce | ||
|
|
c22f8a47f1 | ||
|
|
858facb5e0 | ||
|
|
b610dd2754 | ||
|
|
b3972a5ba8 | ||
|
|
36e8273da0 | ||
|
|
2f4de3cc48 | ||
|
|
b22ef9f8fa | ||
|
|
e20f39f040 | ||
|
|
63188667bb | ||
|
|
c2eaf5e627 | ||
|
|
7ba8983f0a | ||
|
|
088aa6ebdd | ||
|
|
767ec4ed59 | ||
|
|
b12b8c981d | ||
|
|
b499a62593 | ||
|
|
fa0a2764a4 | ||
|
|
a537a98f03 | ||
|
|
ed5b0973dd | ||
|
|
e8e27b61f6 | ||
|
|
336e22233d | ||
|
|
578431c791 | ||
|
|
ead0f8d029 | ||
|
|
f7aa2aa348 | ||
|
|
ccd254abba | ||
|
|
56de183155 | ||
|
|
c8256eea0e | ||
|
|
b026bb308d | ||
|
|
6bd4d367d4 | ||
|
|
d401a62fd9 | ||
|
|
8f61cc4041 | ||
|
|
21c9fc2d35 | ||
|
|
171572e400 | ||
|
|
475a4f2676 | ||
|
|
52e01b39d6 | ||
|
|
af1dcfd179 | ||
|
|
233a6908dd | ||
|
|
ae83c12f05 | ||
|
|
878ae56a71 | ||
|
|
2ff4ebc72f | ||
|
|
ca79d3b437 | ||
|
|
14081a197b | ||
|
|
9fe2549938 | ||
|
|
e0a50d4bb9 | ||
|
|
ad1c1f4130 | ||
|
|
121b18bf70 | ||
|
|
22ed29a30b | ||
|
|
64b59f8679 | ||
|
|
406ed5a0c0 | ||
|
|
d5081f4328 | ||
|
|
95d7afb0d0 | ||
|
|
2b2987d962 | ||
|
|
2eb77445be | ||
|
|
12b1b38351 | ||
|
|
b0abb6362a | ||
|
|
a458ffd472 | ||
|
|
60b6f520e7 | ||
|
|
8d0d5b4077 | ||
|
|
5c9a6afb82 | ||
|
|
162acc1cc2 | ||
|
|
64ef43d918 | ||
|
|
33c3d2a7d0 | ||
|
|
1238ab4f69 | ||
|
|
a42ff5a07c | ||
|
|
aa4b6bd4ea | ||
|
|
478ba9c5ef | ||
|
|
c58e9d17a8 | ||
|
|
41ccbc0334 | ||
|
|
bd46ad56fc | ||
|
|
bde5e86c50 | ||
|
|
ec0f01fd7c | ||
|
|
fe4c1cc35a | ||
|
|
b6152c19d6 | ||
|
|
800abbf8b7 | ||
|
|
3af622d535 | ||
|
|
ee4cbf52dc | ||
|
|
5cd2be058d | ||
|
|
3a21edca2b | ||
|
|
c6a926842a | ||
|
|
440b5164ad | ||
|
|
cc5f17a7d2 | ||
|
|
85d5b82811 | ||
|
|
f0a1ac4346 | ||
|
|
bdc39f9cc2 | ||
|
|
d3ae2f42a7 | ||
|
|
df39ab0b2f | ||
|
|
28833ee5ac | ||
|
|
c66a1d1895 | ||
|
|
cdee0dc1cf | ||
|
|
a43c5fcfe8 | ||
|
|
5c67f39476 | ||
|
|
fb47657b1f | ||
|
|
76d46af4b7 | ||
|
|
86af2848dd | ||
|
|
4ca310b63b | ||
|
|
59bc1d8966 | ||
|
|
14c2ff44f3 | ||
|
|
b56fcd8ebd | ||
|
|
e38d6d974b | ||
|
|
9500c5ffab | ||
|
|
b079cedbf5 | ||
|
|
6e088e28f5 | ||
|
|
01e2743bae | ||
|
|
75ffa787a6 | ||
|
|
4ad9f34807 | ||
|
|
a91799a11c | ||
|
|
44fd03c058 | ||
|
|
083e75998f | ||
|
|
adc47948a5 | ||
|
|
6e886e28e9 | ||
|
|
e63fc184a5 | ||
|
|
84a40dcb59 | ||
|
|
8e90782107 | ||
|
|
68ba99b7f0 | ||
|
|
2fefc428a8 | ||
|
|
17220f2604 | ||
|
|
7a68961b25 | ||
|
|
aa2baa45fb | ||
|
|
eda5feb82c | ||
|
|
d2b445c80c | ||
|
|
b4621e22e6 | ||
|
|
38679f79b5 | ||
|
|
a927caf2b0 | ||
|
|
29c846a764 | ||
|
|
804c9fbd6f | ||
|
|
2edebdef08 | ||
|
|
a91ba0fd48 | ||
|
|
a25d55a265 | ||
|
|
c99f75b247 | ||
|
|
0efbfb10aa | ||
|
|
cb8006b89f | ||
|
|
660469cbf5 | ||
|
|
a06e551469 | ||
|
|
746b86a1dd | ||
|
|
1c594b6a8d | ||
|
|
2b652422b9 | ||
|
|
313832de8d | ||
|
|
250f281189 | ||
|
|
2c63f7a24d | ||
|
|
10fba886dc | ||
|
|
046d955f5c | ||
|
|
d0d362664b | ||
|
|
f841450dae | ||
|
|
a3927c5c2c | ||
|
|
89cfa387cd | ||
|
|
1fccb23442 | ||
|
|
bfd50ffcd0 | ||
|
|
d2301c811e | ||
|
|
4f2a171913 | ||
|
|
005d70a73c | ||
|
|
592291cbcb | ||
|
|
7236bd7dd4 | ||
|
|
c02d5f41a5 | ||
|
|
5a7cd815ab | ||
|
|
006be43c73 | ||
|
|
c2c01bccfc | ||
|
|
2240039442 | ||
|
|
fb4728c7ee | ||
|
|
f34621578e | ||
|
|
5e50b1f1fd | ||
|
|
3c95d342ee | ||
|
|
53ac5427a8 | ||
|
|
1ed8afba09 | ||
|
|
ef81a8a2ad | ||
|
|
fa9d1614e7 | ||
|
|
fe46b588af | ||
|
|
290de7c430 | ||
|
|
05fbfb520f | ||
|
|
805e1330ad | ||
|
|
7c6fcf688c | ||
|
|
127055dd70 | ||
|
|
f4929e849e | ||
|
|
ba9ac03b0b | ||
|
|
0257f7e169 | ||
|
|
763cb43e46 | ||
|
|
8ed2588cdf | ||
|
|
d8d2d36638 | ||
|
|
afc13e70b9 | ||
|
|
baa9ba089c | ||
|
|
3495b822a5 | ||
|
|
be88d425fc | ||
|
|
94a06cec5b | ||
|
|
4008f6b309 | ||
|
|
daac6886a0 | ||
|
|
e304dfd5b9 | ||
|
|
d0af48c417 | ||
|
|
2e3aabf99b | ||
|
|
59bf8740a0 | ||
|
|
21cd57228e | ||
|
|
4501c64e61 | ||
|
|
273105e78b | ||
|
|
2707116350 | ||
|
|
c7d30bae98 | ||
|
|
be328581a7 | ||
|
|
c32bc27b3e | ||
|
|
0fef88a9b9 | ||
|
|
3e0fc56495 | ||
|
|
7ea3fc1533 | ||
|
|
396ec4f888 | ||
|
|
79ea7c3eed | ||
|
|
b18810f381 | ||
|
|
51d9cf099c | ||
|
|
339ed20abc | ||
|
|
44ed577992 | ||
|
|
f6685accc3 | ||
|
|
2c2e79c13d | ||
|
|
81961068a2 | ||
|
|
4e91a6450c | ||
|
|
3dc2547bb5 | ||
|
|
47f299f0ee | ||
|
|
d135aab86c | ||
|
|
e2a2b882bb | ||
|
|
d8309a64cb | ||
|
|
55722099fa | ||
|
|
48abe62151 | ||
|
|
2149814428 | ||
|
|
ea1c951006 | ||
|
|
4a96f9a5c9 | ||
|
|
fcc4151810 | ||
|
|
82cd79c004 | ||
|
|
74983af3b4 | ||
|
|
f5bcb11fe9 | ||
|
|
f6f1c638a5 | ||
|
|
e250672e4a | ||
|
|
13093f5de3 | ||
|
|
9b62104f30 | ||
|
|
df36ebf308 | ||
|
|
0d0c6a15fb | ||
|
|
86e42a9cf0 | ||
|
|
b4911cc5ce | ||
|
|
3e31d86596 | ||
|
|
ab007e2bb2 | ||
|
|
de9dcbd231 | ||
|
|
00fff7f793 | ||
|
|
5cf13dfa32 | ||
|
|
27b9a5aed9 | ||
|
|
c45d55b94b | ||
|
|
7048bd8cc9 | ||
|
|
cae6ca987f | ||
|
|
65171cfb3a | ||
|
|
7ec636aeb6 | ||
|
|
c96980a57d | ||
|
|
0959ceeff2 | ||
|
|
1988c9d8c4 | ||
|
|
1eaa1e47fe | ||
|
|
31923b314e | ||
|
|
ee840b62e5 | ||
|
|
4de99d2540 | ||
|
|
363efaa408 | ||
|
|
5b60522e5e | ||
|
|
b0beeea95f | ||
|
|
d0a4650d69 | ||
|
|
ec5a84cf01 | ||
|
|
a057eb8590 | ||
|
|
6a8753b119 | ||
|
|
b334c82fdd | ||
|
|
23fab90a31 | ||
|
|
8fdf9cac6b | ||
|
|
525d12f5a2 | ||
|
|
15d247a708 | ||
|
|
9ed4aa4111 | ||
|
|
a1d0b85277 | ||
|
|
5e5a77b040 | ||
|
|
e420ae6942 | ||
|
|
0be7c0273e | ||
|
|
41b1b53c76 | ||
|
|
60ef5535c6 | ||
|
|
e38d0807c9 | ||
|
|
864b7cd41e | ||
|
|
78c8f13645 | ||
|
|
c87c456ae0 | ||
|
|
94d9b8fb39 | ||
|
|
7045a6c239 | ||
|
|
2b1602872c | ||
|
|
b488ad12c2 | ||
|
|
2dad027001 | ||
|
|
f97bb240d7 | ||
|
|
574e029254 | ||
|
|
91759c4a27 | ||
|
|
ce5934461e | ||
|
|
80332fb6ab | ||
|
|
287ec2f3df | ||
|
|
eb5824f713 | ||
|
|
eded901f69 | ||
|
|
6f637e40d8 | ||
|
|
ec06663dee | ||
|
|
3c5c432e1d | ||
|
|
927f7549d4 | ||
|
|
177114f8d9 | ||
|
|
6bfdfd1af0 | ||
|
|
2d2e843804 | ||
|
|
fd30df82cb | ||
|
|
55f2fcb4c6 | ||
|
|
d8888432c3 | ||
|
|
59425741b6 | ||
|
|
3577ba2981 | ||
|
|
d4477b0d65 | ||
|
|
48b4c57d92 | ||
|
|
51712fea53 | ||
|
|
f597f99198 | ||
|
|
00d09763af | ||
|
|
5639572ef3 | ||
|
|
1e7716abb4 | ||
|
|
751181d14f | ||
|
|
013b87f9c5 | ||
|
|
6da2f6bbaa | ||
|
|
198cb43ad6 | ||
|
|
db648a0268 | ||
|
|
7cea2225de | ||
|
|
51a7cbeb55 | ||
|
|
171a9e1575 | ||
|
|
76deabe4df | ||
|
|
74ec37f3c7 | ||
|
|
6b9950a9a0 | ||
|
|
c6ed7dc586 | ||
|
|
35ac6610c7 | ||
|
|
121efd8ff1 | ||
|
|
23d8f01012 | ||
|
|
05178f0fbf | ||
|
|
573aaec9fd | ||
|
|
8fe102662d | ||
|
|
b676ffe208 | ||
|
|
625b21152e | ||
|
|
385759f3c9 | ||
|
|
9ce09713b6 | ||
|
|
4fa6faf608 | ||
|
|
a24ae13d9c | ||
|
|
64dd0d1b45 | ||
|
|
da63b1223c | ||
|
|
36f32c2aef | ||
|
|
063acadc3b | ||
|
|
81c720c05b | ||
|
|
edcdcb945a | ||
|
|
5df6430bb2 | ||
|
|
9a0096d07c | ||
|
|
25d33d6057 | ||
|
|
ab4c6247c7 | ||
|
|
1b69dbb38b | ||
|
|
bfdd488b00 | ||
|
|
54c8809bf4 | ||
|
|
0873665bec | ||
|
|
02b3033ca3 | ||
|
|
a27c8ad90c | ||
|
|
bf6d0a521c | ||
|
|
336bbb2403 | ||
|
|
73da086964 | ||
|
|
58f4f3623f | ||
|
|
3c2bb1e2bc | ||
|
|
24af9f38f4 | ||
|
|
98efab83a1 | ||
|
|
e4128cd5d8 | ||
|
|
f8e6326880 | ||
|
|
67c5f513aa | ||
|
|
0589bfc51b | ||
|
|
04f32bd397 | ||
|
|
5bb4c3be43 | ||
|
|
07ce6cfa79 | ||
|
|
9ce47c9675 | ||
|
|
40255d643c | ||
|
|
b31200a6f2 | ||
|
|
98cbb599b8 | ||
|
|
ecd314cef5 | ||
|
|
0b1ecd0ac6 | ||
|
|
852e30ef07 | ||
|
|
2193da0ab5 | ||
|
|
7cd12e7dc5 | ||
|
|
685bf395be | ||
|
|
0636ae6f28 | ||
|
|
7f8eba3700 | ||
|
|
02957aba45 | ||
|
|
946258ca59 | ||
|
|
2e53e2e4e0 | ||
|
|
721a8f79ab | ||
|
|
ecaf66db5d | ||
|
|
0c775aba6d | ||
|
|
71b238031f | ||
|
|
662bd4e5fe | ||
|
|
5118054fa3 | ||
|
|
c956d11183 | ||
|
|
7df23a1b19 | ||
|
|
72580d7213 | ||
|
|
264cc73806 | ||
|
|
c041076c95 | ||
|
|
f6ff189f35 | ||
|
|
e1e8842548 |
5
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.gitmodules
|
||||
*build*/
|
||||
*flymake*
|
||||
CMakeLists.txt.user*
|
||||
@@ -5,3 +6,7 @@ CMakeLists.txt.user*
|
||||
*~
|
||||
*.autosave
|
||||
doc/_build/*
|
||||
*~
|
||||
*.kate-swp
|
||||
*.kdev4
|
||||
win/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "doc/ocdoc"]
|
||||
path = doc/ocdoc
|
||||
url = https://github.com/owncloud/documentation
|
||||
@@ -21,8 +21,20 @@ include(${CMAKE_SOURCE_DIR}/VERSION.cmake)
|
||||
configure_file( ${CMAKE_SOURCE_DIR}/src/mirall/version.h.in "${CMAKE_CURRENT_BINARY_DIR}/src/mirall/version.h" )
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/src/mirall/")
|
||||
|
||||
#####
|
||||
## handle DBUS for Fdo notifications
|
||||
if( UNIX AND NOT APPLE )
|
||||
add_definitions( -DUSE_FDO_NOTIFICATIONS)
|
||||
set(WITH_DBUS ON)
|
||||
endif()
|
||||
####
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(DefineInstallationPaths)
|
||||
include(QtVersionAbstraction)
|
||||
|
||||
setup_qt()
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
|
||||
@@ -58,19 +70,14 @@ else()
|
||||
endif()
|
||||
#####
|
||||
|
||||
#####
|
||||
## handle DBUS for Fdo notifications
|
||||
if( UNIX AND NOT APPLE )
|
||||
add_definitions( -DUSE_FDO_NOTIFICATIONS)
|
||||
endif()
|
||||
####
|
||||
|
||||
#### find libs
|
||||
find_package(Qt4 4.7.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest QtWebkit REQUIRED )
|
||||
if( UNIX AND NOT APPLE ) # Fdo notifications
|
||||
find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED )
|
||||
endif()
|
||||
#find_package(Qt4 4.7.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest QtWebkit REQUIRED )
|
||||
#if( UNIX AND NOT APPLE ) # Fdo notifications
|
||||
# find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED )
|
||||
#endif()
|
||||
find_package(Neon REQUIRED)
|
||||
find_package(Csync REQUIRED)
|
||||
find_package(QtKeychain REQUIRED)
|
||||
if(UNIX)
|
||||
find_package(INotify REQUIRED)
|
||||
else()
|
||||
@@ -78,7 +85,6 @@ find_package(INotify)
|
||||
endif()
|
||||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
find_package(QtKeychain)
|
||||
|
||||
set(WITH_QTKEYCHAIN ${QTKEYCHAIN_FOUND})
|
||||
set(USE_INOTIFY ${INOTIFY_FOUND})
|
||||
|
||||
@@ -21,11 +21,9 @@ set( CSYNC_BINARY_DIR @CSYNC_BINARY_DIR@ )
|
||||
set( MINGW_ROOT @CMAKE_FIND_ROOT_PATH@ )
|
||||
if(CSYNC_BINARY_DIR)
|
||||
set( CSYNC_LIBRARY_DIR "${CSYNC_BINARY_DIR}/src" )
|
||||
set( CSYNC_PLUGIN_DIR "${CSYNC_BINARY_DIR}/modules" )
|
||||
set( CSYNC_CONFIG_DIR "${CSYNC_BINARY_DIR}/config" )
|
||||
else()
|
||||
set( CSYNC_LIBRARY_DIR "${MINGW_ROOT}/bin" )
|
||||
set( CSYNC_PLUGIN_DIR "${MINGW_ROOT}/plugins-0" ) #FIXME: whatever it is
|
||||
set( CSYNC_CONFIG_DIR "${MINGW_ROOT}/etc/ocsync" )
|
||||
endif()
|
||||
set( BUILD_OWNCLOUD_OSX_BUNDLE @BUILD_OWNCLOUD_OSX_BUNDLE@)
|
||||
|
||||
43
ChangeLog
@@ -1,6 +1,44 @@
|
||||
ChangeLog
|
||||
=========
|
||||
version 1.4.0beta1 (release 2013-08-08 ), csync 0.81.0 required
|
||||
|
||||
version 1.4.2 (release 2013-10-18 ), csync 0.90.4 required
|
||||
|
||||
* Do not show the warning icon in the tray (#944)
|
||||
* Fix manual proxy support when switching (#1016)
|
||||
* Add folder column to detailed sync protocol (#1037)
|
||||
* Fix possible endless loop in inotify (#1041)
|
||||
* Do not elide the progress text (#1049)
|
||||
* Fix high CPU load (#1073)
|
||||
* Reconnect if network is unavailable after startup (#1080)
|
||||
* Ensure paused folder stays paused when syncing with more than one folder (#1083)
|
||||
* Don't show desktop notification when the user doesn't want to (#1093)
|
||||
* System tray: Avoid quick flickering up of the ok-icon for the sync prepare state
|
||||
* Progress: Do not show progress if nothing is transmitted
|
||||
* Progress: Show number of deletes.
|
||||
|
||||
version 1.4.1 (release 2013-09-24 ), csync 0.90.1 required
|
||||
|
||||
* Translation and documentation fixes.
|
||||
* Fixed error display in settings/status dialog, displays multi
|
||||
line error messages now correctly.
|
||||
* Wait up to 30 secs before complaining about missing systray
|
||||
Fixes bug #949
|
||||
* Fixed utf8 issues with basic auth authentication, fixes bug #941
|
||||
* Fixed remote folder selector, avoid recursive syncing, fixes bug #962
|
||||
* Handle and display network problems at startup correctly.
|
||||
* Enable and disable the folder watcher during syncs correctly.
|
||||
* Fix setting of thread priority.
|
||||
* Fixed file size display.
|
||||
* Fixed various folder wizard issues, bug #992
|
||||
* Made "Sync started" message optional, fixes bug #934
|
||||
* Fixed shutdown, avoid crashed config on win32, fixes bug #945
|
||||
* Pop up config wizard if no server url is configured, fixes bug #1018
|
||||
* Settings: calculate sidebar width dynamically, fixes bug #1020
|
||||
* Fixed a crash if sync folders were removed, fixes bug #713
|
||||
* Do proper resync after network disconnect, fixes bug #1007
|
||||
* Various minor code fixes
|
||||
|
||||
version 1.4.0 (release 2013-09-04 ), csync 0.90.0 required
|
||||
|
||||
* New Scheduler: Only sync when there are actual changes in the server
|
||||
* Add a Settings Dialog, move Proxy Settings there
|
||||
@@ -19,6 +57,9 @@ version 1.4.0beta1 (release 2013-08-08 ), csync 0.81.0 required
|
||||
* Add an editor for ingore patterns
|
||||
* ALlow to flag certain ignore patterns as discardable
|
||||
* Ensure to ship with all valid translations
|
||||
* Progress Dialog now preserves the last syncned items across sync runs
|
||||
* Split Setup Wizard into multiple pages again
|
||||
* Implement "--logfile -" to log to stdout
|
||||
* Add preliminary support for Shibboleth authentication
|
||||
* Linux: Provide more icon sizes
|
||||
* Linux: Do not trigger notifier on ignored files
|
||||
|
||||
@@ -9,10 +9,11 @@ else ()
|
||||
endif()
|
||||
|
||||
include( VERSION.cmake )
|
||||
set( CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR} )
|
||||
set( CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR} )
|
||||
set( CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH} )
|
||||
set( CPACK_PACKAGE_VERSION ${VERSION} )
|
||||
set( CPACK_PACKAGE_VERSION_MAJOR ${MIRALL_VERSION_MAJOR} )
|
||||
set( CPACK_PACKAGE_VERSION_MINOR ${MIRALL_VERSION_MINOR} )
|
||||
set( CPACK_PACKAGE_VERSION_PATCH ${MIRALL_VERSION_PATCH} )
|
||||
set( CPACK_PACKAGE_VERSION_BUILD ${MIRALL_VERSION_BUILD} )
|
||||
set( CPACK_PACKAGE_VERSION ${MIRALL_VERSION_FULL}${MIRALL_VERSION_SUFFIX} )
|
||||
|
||||
if(APPLE)
|
||||
set( CPACK_GENERATOR "DragNDrop" )
|
||||
|
||||
@@ -1,6 +1,26 @@
|
||||
set( VERSION_MAJOR 1 )
|
||||
set( VERSION_MINOR 4 )
|
||||
set( VERSION_PATCH 0 )
|
||||
set( VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX}rc1")
|
||||
set( SOVERSION 0 )
|
||||
set( MIRALL_VERSION_MAJOR 1 )
|
||||
set( MIRALL_VERSION_MINOR 5 )
|
||||
set( MIRALL_VERSION_PATCH 0 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
|
||||
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
set( MIRALL_VERSION_SUFFIX "beta3" ) #e.g. beta1, beta2, rc1
|
||||
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
|
||||
|
||||
if( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
set( MIRALL_VERSION_BUILD "0" ) # Integer ID. Generated by the build system
|
||||
endif( NOT DEFINED MIRALL_VERSION_BUILD )
|
||||
|
||||
# Composite defines
|
||||
# Used e.g. for libraries Keep at x.y.z.
|
||||
set( MIRALL_VERSION "${MIRALL_VERSION_MAJOR}.${MIRALL_VERSION_MINOR}.${MIRALL_VERSION_PATCH}" )
|
||||
# Version with Build ID. Used in the installer
|
||||
set( MIRALL_VERSION_FULL ${MIRALL_VERSION} )
|
||||
set( MIRALL_VERSION_STRING ${MIRALL_VERSION} )
|
||||
set( MIRALL_VERSION_FULL "${MIRALL_VERSION_FULL}.${MIRALL_VERSION_BUILD}" )
|
||||
|
||||
set( MIRALL_VERSION_STRING "${MIRALL_VERSION}${MIRALL_VERSION_SUFFIX}" )
|
||||
|
||||
if( MIRALL_VERSION_BUILD )
|
||||
set( MIRALL_VERSION_STRING "${MIRALL_VERSION_STRING} (build ${MIRALL_VERSION_BUILD})" )
|
||||
endif( MIRALL_VERSION_BUILD )
|
||||
|
||||
@@ -5,7 +5,6 @@ This sample uses the registry plugin, so you need to download that if you don't
|
||||
|
||||
!define APPNAME "UAC_RealWorldFullyLoadedDualMode"
|
||||
!define ELEVATIONTITLE "${APPNAME}: Elevate" ;displayed during elevation on our custom page
|
||||
!define SMSUBDIR $StartMenuFolder ;"${APPNAME}"
|
||||
!define UNINSTALLER_NAME "Uninstall ${APPNAME}.exe"
|
||||
!define UNINSTALLER_REGSECTION "${APPNAME}"
|
||||
!define RegPath.MSUninstall "Software\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
@@ -200,10 +199,7 @@ UAC::Exec "" "Notepad.exe" "$Windir\Win.INI" "$InstDir"
|
||||
FunctionEnd
|
||||
|
||||
Function CreateSMShortcuts
|
||||
StrCpy ${SMSUBDIR} $9 ;stupid sync
|
||||
CreateDirectory "$SMPrograms\${SMSUBDIR}"
|
||||
CreateShortcut "$SMPrograms\${SMSUBDIR}\${APPNAME}.lnk" "$Windir\Notepad.exe"
|
||||
CreateShortcut "$SMPrograms\${SMSUBDIR}\Uninstall ${APPNAME}.lnk" "$InstDir\${UNINSTALLER_NAME}"
|
||||
CreateShortcut "$SMPrograms\${APPNAME}.lnk" "$Windir\Notepad.exe"
|
||||
FunctionEnd
|
||||
Function CreateDeskShortcuts
|
||||
CreateShortcut "$Desktop\${APPNAME}.lnk" "$Windir\Notepad.exe"
|
||||
@@ -222,7 +218,6 @@ ${registry::Unload}
|
||||
SectionEnd
|
||||
|
||||
Section "Startmenu Shortcuts"
|
||||
StrCpy $9 ${SMSUBDIR} ;this is stupid as hell, we need correct ${SMSUBDIR} in the outer process, this is the only way (plugins cannot enum "custom" var's AFAIK)
|
||||
${UAC.CallFunctionAsUser} CreateSMShortcuts
|
||||
SectionEnd
|
||||
Section "Desktop Shortcut"
|
||||
@@ -231,9 +226,7 @@ SectionEnd
|
||||
|
||||
Section Uninstall
|
||||
Delete "$InstDir\${UNINSTALLER_NAME}"
|
||||
Delete "$SMPrograms\${SMSUBDIR}\${APPNAME}.lnk"
|
||||
Delete "$SMPrograms\${SMSUBDIR}\Uninstall ${APPNAME}.lnk"
|
||||
RMDir "$SMPrograms\${SMSUBDIR}"
|
||||
Delete "$SMPrograms\${APPNAME}.lnk"
|
||||
Delete "$Desktop\${APPNAME}.lnk"
|
||||
|
||||
RMDir "$InstDir"
|
||||
|
||||
73
cmake/modules/FindNeon.cmake
Normal file
@@ -0,0 +1,73 @@
|
||||
# - 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 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
|
||||
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 PARENT_SCOPE)
|
||||
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 )
|
||||
@@ -17,15 +17,15 @@
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>@APPLICATION_NAME@ @VERSION@</string>
|
||||
<string>@APPLICATION_NAME@ @MIRALL_VERSION_STRING@</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>@VERSION@</string>
|
||||
<string>@VERSION_FULL@</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@VERSION@</string>
|
||||
<string>@VERSION_FULL@</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>(C) 2012 @APPLICATION_VENDOR@</string>
|
||||
</dict>
|
||||
|
||||
157
cmake/modules/MacroLogFeature.cmake
Normal file
@@ -0,0 +1,157 @@
|
||||
# This file defines the Feature Logging macros.
|
||||
#
|
||||
# MACRO_LOG_FEATURE(VAR FEATURE DESCRIPTION URL [REQUIRED [MIN_VERSION [COMMENTS]]])
|
||||
# Logs the information so that it can be displayed at the end
|
||||
# of the configure run
|
||||
# VAR : TRUE or FALSE, indicating whether the feature is supported
|
||||
# FEATURE: name of the feature, e.g. "libjpeg"
|
||||
# DESCRIPTION: description what this feature provides
|
||||
# URL: home page
|
||||
# REQUIRED: TRUE or FALSE, indicating whether the featue is required
|
||||
# MIN_VERSION: minimum version number. empty string if unneeded
|
||||
# COMMENTS: More info you may want to provide. empty string if unnecessary
|
||||
#
|
||||
# MACRO_DISPLAY_FEATURE_LOG()
|
||||
# Call this to display the collected results.
|
||||
# Exits CMake with a FATAL error message if a required feature is missing
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# INCLUDE(MacroLogFeature)
|
||||
#
|
||||
# FIND_PACKAGE(JPEG)
|
||||
# MACRO_LOG_FEATURE(JPEG_FOUND "libjpeg" "Support JPEG images" "http://www.ijg.org" TRUE "3.2a" "")
|
||||
# ...
|
||||
# MACRO_DISPLAY_FEATURE_LOG()
|
||||
|
||||
# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
|
||||
# Copyright (c) 2006, Allen Winter, <winter@kde.org>
|
||||
# Copyright (c) 2009, Sebastian Trueg, <trueg@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
IF (NOT _macroLogFeatureAlreadyIncluded)
|
||||
SET(_file ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
|
||||
IF (EXISTS ${_file})
|
||||
FILE(REMOVE ${_file})
|
||||
ENDIF (EXISTS ${_file})
|
||||
|
||||
SET(_file ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
|
||||
IF (EXISTS ${_file})
|
||||
FILE(REMOVE ${_file})
|
||||
ENDIF (EXISTS ${_file})
|
||||
|
||||
SET(_file ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
|
||||
IF (EXISTS ${_file})
|
||||
FILE(REMOVE ${_file})
|
||||
ENDIF (EXISTS ${_file})
|
||||
|
||||
SET(_macroLogFeatureAlreadyIncluded TRUE)
|
||||
|
||||
INCLUDE(FeatureSummary)
|
||||
|
||||
ENDIF (NOT _macroLogFeatureAlreadyIncluded)
|
||||
|
||||
|
||||
MACRO(MACRO_LOG_FEATURE _var _package _description _url ) # _required _minvers _comments)
|
||||
|
||||
STRING(TOUPPER "${ARGV4}" _required)
|
||||
SET(_minvers "${ARGV5}")
|
||||
SET(_comments "${ARGV6}")
|
||||
|
||||
IF (${_var})
|
||||
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
|
||||
ELSE (${_var})
|
||||
IF ("${_required}" STREQUAL "TRUE")
|
||||
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
|
||||
ELSE ("${_required}" STREQUAL "TRUE")
|
||||
SET(_LOGFILENAME ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
|
||||
ENDIF ("${_required}" STREQUAL "TRUE")
|
||||
ENDIF (${_var})
|
||||
|
||||
SET(_logtext " * ${_package}")
|
||||
|
||||
IF (NOT ${_var})
|
||||
IF (${_minvers} MATCHES ".*")
|
||||
SET(_logtext "${_logtext} (${_minvers} or higher)")
|
||||
ENDIF (${_minvers} MATCHES ".*")
|
||||
SET(_logtext "${_logtext} <${_url}>\n ")
|
||||
ELSE (NOT ${_var})
|
||||
SET(_logtext "${_logtext} - ")
|
||||
ENDIF (NOT ${_var})
|
||||
|
||||
SET(_logtext "${_logtext}${_description}")
|
||||
|
||||
IF (NOT ${_var})
|
||||
IF (${_comments} MATCHES ".*")
|
||||
SET(_logtext "${_logtext}\n ${_comments}")
|
||||
ENDIF (${_comments} MATCHES ".*")
|
||||
# SET(_logtext "${_logtext}\n") #double-space missing features?
|
||||
ENDIF (NOT ${_var})
|
||||
|
||||
FILE(APPEND "${_LOGFILENAME}" "${_logtext}\n")
|
||||
|
||||
IF(COMMAND SET_PACKAGE_INFO) # in FeatureSummary.cmake since CMake 2.8.3
|
||||
SET_PACKAGE_INFO("${_package}" "\"${_description}\"" "${_url}" "\"${_comments}\"")
|
||||
ENDIF(COMMAND SET_PACKAGE_INFO)
|
||||
|
||||
ENDMACRO(MACRO_LOG_FEATURE)
|
||||
|
||||
|
||||
MACRO(MACRO_DISPLAY_FEATURE_LOG)
|
||||
IF(COMMAND FEATURE_SUMMARY) # in FeatureSummary.cmake since CMake 2.8.3
|
||||
FEATURE_SUMMARY(FILENAME ${CMAKE_CURRENT_BINARY_DIR}/FindPackageLog.txt
|
||||
WHAT ALL)
|
||||
ENDIF(COMMAND FEATURE_SUMMARY)
|
||||
|
||||
SET(_missingFile ${CMAKE_BINARY_DIR}/MissingRequirements.txt)
|
||||
SET(_enabledFile ${CMAKE_BINARY_DIR}/EnabledFeatures.txt)
|
||||
SET(_disabledFile ${CMAKE_BINARY_DIR}/DisabledFeatures.txt)
|
||||
|
||||
IF (EXISTS ${_missingFile} OR EXISTS ${_enabledFile} OR EXISTS ${_disabledFile})
|
||||
SET(_printSummary TRUE)
|
||||
ENDIF (EXISTS ${_missingFile} OR EXISTS ${_enabledFile} OR EXISTS ${_disabledFile})
|
||||
|
||||
IF(_printSummary)
|
||||
SET(_missingDeps 0)
|
||||
IF (EXISTS ${_enabledFile})
|
||||
FILE(READ ${_enabledFile} _enabled)
|
||||
FILE(REMOVE ${_enabledFile})
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following external packages were located on your system.\n-- This installation will have the extra features provided by these packages.\n-----------------------------------------------------------------------------\n${_enabled}")
|
||||
ENDIF (EXISTS ${_enabledFile})
|
||||
|
||||
|
||||
IF (EXISTS ${_disabledFile})
|
||||
SET(_missingDeps 1)
|
||||
FILE(READ ${_disabledFile} _disabled)
|
||||
FILE(REMOVE ${_disabledFile})
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following OPTIONAL packages could NOT be located on your system.\n-- Consider installing them to enable more features from this software.\n-----------------------------------------------------------------------------\n${_disabled}")
|
||||
ENDIF (EXISTS ${_disabledFile})
|
||||
|
||||
|
||||
IF (EXISTS ${_missingFile})
|
||||
SET(_missingDeps 1)
|
||||
FILE(READ ${_missingFile} _requirements)
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- The following REQUIRED packages could NOT be located on your system.\n-- You must install these packages before continuing.\n-----------------------------------------------------------------------------\n${_requirements}")
|
||||
FILE(REMOVE ${_missingFile})
|
||||
SET(_haveMissingReq 1)
|
||||
ENDIF (EXISTS ${_missingFile})
|
||||
|
||||
|
||||
IF (NOT ${_missingDeps})
|
||||
SET(_summary "${_summary}\n-----------------------------------------------------------------------------\n-- Congratulations! All external packages have been found.")
|
||||
ENDIF (NOT ${_missingDeps})
|
||||
|
||||
|
||||
MESSAGE(${_summary})
|
||||
MESSAGE("-----------------------------------------------------------------------------\n")
|
||||
|
||||
|
||||
IF(_haveMissingReq)
|
||||
MESSAGE(FATAL_ERROR "Exiting: Missing Requirements")
|
||||
ENDIF(_haveMissingReq)
|
||||
|
||||
ENDIF(_printSummary)
|
||||
|
||||
ENDMACRO(MACRO_DISPLAY_FEATURE_LOG)
|
||||
47
cmake/modules/MacroOptionalFindPackage.cmake
Normal file
@@ -0,0 +1,47 @@
|
||||
# - MACRO_OPTIONAL_FIND_PACKAGE() combines FIND_PACKAGE() with an OPTION()
|
||||
# MACRO_OPTIONAL_FIND_PACKAGE( <name> [QUIT] )
|
||||
# This macro is a combination of OPTION() and FIND_PACKAGE(), it
|
||||
# works like FIND_PACKAGE(), but additionally it automatically creates
|
||||
# an option name WITH_<name>, which can be disabled via the cmake GUI.
|
||||
# or via -DWITH_<name>=OFF
|
||||
# The standard <name>_FOUND variables can be used in the same way
|
||||
# as when using the normal FIND_PACKAGE()
|
||||
|
||||
# Copyright (c) 2006-2010 Alexander Neundorf, <neundorf@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
# This is just a helper macro to set a bunch of variables empty.
|
||||
# We don't know whether the package uses UPPERCASENAME or CamelCaseName, so we try both:
|
||||
macro(_MOFP_SET_EMPTY_IF_DEFINED _name _var)
|
||||
if(DEFINED ${_name}_${_var})
|
||||
set(${_name}_${_var} "")
|
||||
endif(DEFINED ${_name}_${_var})
|
||||
|
||||
string(TOUPPER ${_name} _nameUpper)
|
||||
if(DEFINED ${_nameUpper}_${_var})
|
||||
set(${_nameUpper}_${_var} "")
|
||||
endif(DEFINED ${_nameUpper}_${_var})
|
||||
endmacro(_MOFP_SET_EMPTY_IF_DEFINED _package _var)
|
||||
|
||||
|
||||
macro (MACRO_OPTIONAL_FIND_PACKAGE _name )
|
||||
option(WITH_${_name} "Search for ${_name} package" ON)
|
||||
if (WITH_${_name})
|
||||
find_package(${_name} ${ARGN})
|
||||
else (WITH_${_name})
|
||||
string(TOUPPER ${_name} _nameUpper)
|
||||
set(${_name}_FOUND FALSE)
|
||||
set(${_nameUpper}_FOUND FALSE)
|
||||
|
||||
_mofp_set_empty_if_defined(${_name} INCLUDE_DIRS)
|
||||
_mofp_set_empty_if_defined(${_name} INCLUDE_DIR)
|
||||
_mofp_set_empty_if_defined(${_name} INCLUDES)
|
||||
_mofp_set_empty_if_defined(${_name} LIBRARY)
|
||||
_mofp_set_empty_if_defined(${_name} LIBRARIES)
|
||||
_mofp_set_empty_if_defined(${_name} LIBS)
|
||||
_mofp_set_empty_if_defined(${_name} FLAGS)
|
||||
_mofp_set_empty_if_defined(${_name} DEFINITIONS)
|
||||
endif (WITH_${_name})
|
||||
endmacro (MACRO_OPTIONAL_FIND_PACKAGE)
|
||||
@@ -39,7 +39,6 @@
|
||||
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt4/plugins/imageformats"
|
||||
|
||||
!define CSYNC_LIBRARY_DIR "@CSYNC_LIBRARY_DIR@"
|
||||
!define CSYNC_PLUGIN_DIR "@CSYNC_PLUGIN_DIR@"
|
||||
!define CSYNC_CONFIG_DIR "@CSYNC_CONFIG_DIR@"
|
||||
|
||||
!define NSI_PATH "${source_path}/admin/win/nsi"
|
||||
@@ -50,7 +49,8 @@
|
||||
|
||||
!define VER_MAJOR "@CPACK_PACKAGE_VERSION_MAJOR@"
|
||||
!define VER_MINOR "@CPACK_PACKAGE_VERSION_MINOR@"
|
||||
!define VER_BUILD "@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
!define VER_PATCH "@CPACK_PACKAGE_VERSION_PATCH@"
|
||||
!define VER_BUILD "@CPACK_PACKAGE_VERSION_BUILD@"
|
||||
!define VERSION "@CPACK_PACKAGE_VERSION@"
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
@@ -206,7 +206,10 @@ Function PageReinstall
|
||||
IntCmp $R0 ${VER_MAJOR} minor_check new_version older_version
|
||||
minor_check:
|
||||
ReadRegDWORD $R0 HKLM "Software\${APPLICATION_NAME}" "VersionMinor"
|
||||
IntCmp $R0 ${VER_MINOR} build_check new_version older_version
|
||||
IntCmp $R0 ${VER_MINOR} rev_check new_version older_version
|
||||
rev_check:
|
||||
ReadRegDWORD $R0 HKLM "Software\${APPLICATION_NAME}" "VersionRevision"
|
||||
IntCmp $R0 ${VER_PATCH} build_check new_version older_version
|
||||
build_check:
|
||||
ReadRegDWORD $R0 HKLM "Software\${APPLICATION_NAME}" "VersionBuild"
|
||||
IntCmp $R0 ${VER_BUILD} same_version new_version older_version
|
||||
@@ -297,11 +300,9 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${IMAGEFORMATS_DLL_PATH}\qjpeg4.dll"
|
||||
File "${IMAGEFORMATS_DLL_PATH}\qico4.dll"
|
||||
|
||||
SetOutPath "$INSTDIR\modules"
|
||||
; FIXME: fix installation dir of module, currently needs manual copying to
|
||||
; /usr/i686-w64-mingw32/sys-root/mingw/bin/csync_modules/
|
||||
SetOutPath "$INSTDIR\sqldrivers"
|
||||
File "${SQLITE_DLL_PATH}\qsqlite4.dll"
|
||||
|
||||
File "${CSYNC_PLUGIN_DIR}/ocsync_owncloud.dll"
|
||||
SetOutPath "$INSTDIR"
|
||||
!endif
|
||||
|
||||
@@ -315,16 +316,15 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
File "${QT_DLL_PATH}\QtCore4.dll"
|
||||
File "${QT_DLL_PATH}\QtGui4.dll"
|
||||
File "${QT_DLL_PATH}\QtNetwork4.dll"
|
||||
File "${QT_DLL_PATH}\QtSql4.dll"
|
||||
File "${QT_DLL_PATH}\QtXml4.dll"
|
||||
File "${QT_DLL_PATH}\QtWebKit4.dll"
|
||||
|
||||
;QtKeyChain stuff
|
||||
File "${MING_BIN}\libqtkeychain.dll"
|
||||
|
||||
File "${CSYNC_LIBRARY_DIR}/libocsync.dll"
|
||||
File "${CSYNC_LIBRARY_DIR}\libocsync.dll"
|
||||
File "${MING_BIN}\libsqlite3-0.dll"
|
||||
File "${MING_BIN}\libiniparser.dll"
|
||||
File "${MING_BIN}\libdl.dll"
|
||||
File "${MING_BIN}\libpng15-15.dll"
|
||||
|
||||
; ownCloud plugin
|
||||
@@ -353,18 +353,13 @@ SectionEnd
|
||||
SectionGroup "Shortcuts"
|
||||
|
||||
!ifdef OPTION_SECTION_SC_START_MENU
|
||||
${MementoSection} "Start Menu Program Group" SEC_START_MENU
|
||||
${MementoSection} "Start Menu Program Shortcut" SEC_START_MENU
|
||||
SectionIn 1 2 3
|
||||
SetDetailsPrint textonly
|
||||
DetailPrint "Adding shortcuts for the ${APPLICATION_NAME} program group to the Start Menu."
|
||||
DetailPrint "Adding shortcut for ${APPLICATION_NAME} to the Start Menu."
|
||||
SetDetailsPrint listonly
|
||||
SetShellVarContext all
|
||||
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
CreateDirectory "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
;CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\LICENSE.lnk" "$INSTDIR\LICENSE.txt"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_EXECUTABLE}"
|
||||
;CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Release notes.lnk" "$INSTDIR\NOTES.txt"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}.lnk" "$INSTDIR\${APPLICATION_EXECUTABLE}"
|
||||
SetShellVarContext current
|
||||
${MementoSectionEnd}
|
||||
!endif
|
||||
@@ -397,7 +392,7 @@ ${MementoSectionDone}
|
||||
;--------------------------------
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_APPLICATION} "${APPLICATION_NAME} essentials."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} program group."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} shortcut."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_DESKTOP} "Desktop shortcut for ${APPLICATION_NAME}."
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_QUICK_LAUNCH} "Quick Launch shortcut for ${APPLICATION_NAME}."
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
@@ -419,7 +414,7 @@ Section -post
|
||||
WriteRegStr HKLM "Software\${APPLICATION_NAME}" "" $INSTDIR
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionMajor" "${VER_MAJOR}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionMinor" "${VER_MINOR}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionRevision" "${REVISION}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionRevision" "${VER_PATCH}"
|
||||
WriteRegDWORD HKLM "Software\${APPLICATION_NAME}" "VersionBuild" "${VER_BUILD}"
|
||||
|
||||
;Add or Remove Programs entry.
|
||||
@@ -505,7 +500,7 @@ Section Uninstall
|
||||
;Start menu shortcuts.
|
||||
!ifdef OPTION_SECTION_SC_START_MENU
|
||||
SetShellVarContext all
|
||||
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
Delete "$SMPROGRAMS\${APPLICATION_NAME}.lnk"
|
||||
SetShellVarContext current
|
||||
!endif
|
||||
|
||||
|
||||
131
cmake/modules/QtVersionAbstraction.cmake
Normal file
@@ -0,0 +1,131 @@
|
||||
include (MacroOptionalFindPackage)
|
||||
include (MacroLogFeature)
|
||||
|
||||
|
||||
option(BUILD_WITH_QT4 "Build with Qt4 no matter if Qt5 was found" ON)
|
||||
|
||||
if( NOT BUILD_WITH_QT4 )
|
||||
find_package(Qt5Core QUIET)
|
||||
if( Qt5Core_DIR )
|
||||
find_package(Qt5Widgets QUIET)
|
||||
find_package(Qt5Quick QUIET)
|
||||
find_package(Qt5PrintSupport QUIET)
|
||||
find_package(Qt5WebKit QUIET)
|
||||
find_package(Qt5Location QUIET)
|
||||
find_package(Qt5Network QUIET)
|
||||
find_package(Qt5Sensors QUIET)
|
||||
find_package(Qt5Xml QUIET)
|
||||
# find_package(Qt5WebKitWidgets QUIET)
|
||||
|
||||
message(STATUS "Using Qt 5!")
|
||||
|
||||
# We need this to find the paths to qdbusxml2cpp and co
|
||||
if (WITH_DBUS)
|
||||
find_package(Qt5DBus REQUIRED)
|
||||
include_directories(${Qt5DBus_INCLUDES})
|
||||
add_definitions(${Qt5DBus_DEFINITIONS})
|
||||
endif (WITH_DBUS)
|
||||
|
||||
include_directories(${Qt5Widgets_INCLUDES})
|
||||
add_definitions(${Qt5Widgets_DEFINITIONS})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
# set(CMAKE_CXX_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
|
||||
|
||||
|
||||
macro(qt_wrap_ui)
|
||||
qt5_wrap_ui(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_resources)
|
||||
qt5_add_resources(${ARGN})
|
||||
endmacro()
|
||||
|
||||
# find_package(Qt5LinguistTools REQUIRED)
|
||||
macro(qt_add_translation)
|
||||
# qt5_add_translation(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_interface)
|
||||
qt5_add_dbus_interface(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_adaptor)
|
||||
qt5_add_dbus_adaptor(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_cpp)
|
||||
qt5_wrap_cpp(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(install_qt_executable)
|
||||
install_qt5_executable(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(setup_qt)
|
||||
endmacro()
|
||||
|
||||
set(QT_RCC_EXECUTABLE "${Qt5Core_RCC_EXECUTABLE}")
|
||||
|
||||
#Enable deprecated symbols
|
||||
add_definitions("-DQT_DISABLE_DEPRECATED_BEFORE=0")
|
||||
endif()
|
||||
endif()
|
||||
if( NOT Qt5Core_DIR )
|
||||
message(STATUS "Could not find Qt5, searching for Qt4 instead...")
|
||||
|
||||
set(NEEDED_QT4_COMPONENTS "QtCore" "QtXml" "QtNetwork" "QtGui" "QtWebkit")
|
||||
if( BUILD_TESTS )
|
||||
list(APPEND NEEDED_QT4_COMPONENTS "QtTest")
|
||||
endif()
|
||||
|
||||
macro_optional_find_package(Qt4 4.7.0 COMPONENTS ${NEEDED_QT4_COMPONENTS} )
|
||||
macro_log_feature(QT4_FOUND "Qt" "A cross-platform application and UI framework" "http://qt.nokia.com" TRUE "" "If you see this, although libqt4-devel is installed, check whether the \n qtwebkit-devel package and whatever contains QtUiTools is installed too")
|
||||
|
||||
macro(qt5_use_modules)
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_ui)
|
||||
qt4_wrap_ui(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_resources)
|
||||
qt4_add_resources(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_translation)
|
||||
qt4_add_translation(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_interface)
|
||||
qt4_add_dbus_interface(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_add_dbus_adaptor)
|
||||
qt4_add_dbus_adaptor(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(qt_wrap_cpp)
|
||||
qt4_wrap_cpp(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(install_qt_executable)
|
||||
install_qt4_executable(${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(setup_qt)
|
||||
set(QT_USE_QTGUI TRUE)
|
||||
set(QT_USE_QTSQL TRUE)
|
||||
set(QT_USE_QTNETWORK TRUE)
|
||||
set(QT_USE_QTXML TRUE)
|
||||
set(QT_USE_QTWEBKIT TRUE)
|
||||
set(QT_USE_QTDBUS TRUE)
|
||||
|
||||
include( ${QT_USE_FILE} )
|
||||
endmacro()
|
||||
endif()
|
||||
|
||||
if( Qt5Core_DIR )
|
||||
set( HAVE_QT5 TRUE )
|
||||
else( Qt5Core_DIR )
|
||||
set( HAVE_QT5 FALSE )
|
||||
endif( Qt5Core_DIR )
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#cmakedefine APPLICATION_NAME "@APPLICATION_NAME@"
|
||||
#cmakedefine APPLICATION_SHORTNAME "@APPLICATION_SHORTNAME@"
|
||||
#cmakedefine APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@"
|
||||
|
||||
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
|
||||
#cmakedefine DATADIR "@DATADIR@"
|
||||
|
||||
@@ -22,6 +22,11 @@ if(SPHINX_FOUND)
|
||||
add_custom_target(doc DEPENDS doc-html doc-man COMMENT "Building documentation...")
|
||||
endif(WITH_DOC)
|
||||
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ocdoc/_shared_assets")
|
||||
add_dependencies(doc doc-html-org)
|
||||
add_dependencies(doc doc-html-com)
|
||||
endif()
|
||||
|
||||
if(PDFLATEX_FOUND)
|
||||
# if this still fails on Debian/Ubuntu, run
|
||||
# apt-get install texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended
|
||||
@@ -31,7 +36,7 @@ if(SPHINX_FOUND)
|
||||
-D latex_logo=${LATEX_LOGO}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_PDF_DIR} )
|
||||
add_custom_target(doc-pdf make -C ${SPHINX_PDF_DIR} all-pdf
|
||||
add_custom_target(doc-pdf $(MAKE) -C ${SPHINX_PDF_DIR} all-pdf
|
||||
DEPENDS doc-latex )
|
||||
add_dependencies(doc doc-pdf)
|
||||
if (WITH_DOC)
|
||||
@@ -56,7 +61,19 @@ if(SPHINX_FOUND)
|
||||
-q -c . -b html
|
||||
-d ${SPHINX_CACHE_DIR}/html
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_HTML_DIR} )
|
||||
${SPHINX_HTML_DIR}/unthemed )
|
||||
add_custom_target( doc-html-org ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b html
|
||||
-d ${SPHINX_CACHE_DIR}/html
|
||||
-D html_theme=owncloud_org
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_HTML_DIR}/org )
|
||||
add_custom_target( doc-html-com ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b html
|
||||
-d ${SPHINX_CACHE_DIR}/html
|
||||
-D html_theme=owncloud_com
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_HTML_DIR}/com )
|
||||
add_custom_target( doc-man ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b man
|
||||
-d ${SPHINX_CACHE_DIR}/man
|
||||
|
||||
153
doc/Makefile
@@ -1,153 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = _build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
# the i18n builder cannot share the environment and doctrees with the others
|
||||
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " texinfo to make Texinfo files"
|
||||
@echo " info to make Texinfo files and run them through makeinfo"
|
||||
@echo " gettext to make PO message catalogs"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
|
||||
html:
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml:
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml:
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle:
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json:
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp:
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OwncloudDocumentation.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OwncloudDocumentation.qhc"
|
||||
|
||||
devhelp:
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/OwncloudDocumentation"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OwncloudDocumentation"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub:
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf:
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
$(MAKE) -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text:
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man:
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
texinfo:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo
|
||||
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
|
||||
@echo "Run \`make' in that directory to run these through makeinfo" \
|
||||
"(use \`make info' here to do that automatically)."
|
||||
|
||||
info:
|
||||
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
|
||||
@echo "Running Texinfo files through makeinfo..."
|
||||
make -C $(BUILDDIR)/texinfo info
|
||||
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
|
||||
|
||||
gettext:
|
||||
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
|
||||
@echo
|
||||
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
|
||||
|
||||
changes:
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck:
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest:
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
37
doc/accountsetup.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
Setting up an Account
|
||||
=====================
|
||||
|
||||
If no account has been configured, ownCloud Client will automatically assist
|
||||
you in connecting to your ownCloud Server after the application has been
|
||||
started.
|
||||
|
||||
As a first step, specify the URL to your Server, just
|
||||
like you would when you open your ownCloud instance inside a browser.
|
||||
|
||||
.. image:: images/wizard_url.png
|
||||
:scale: 50 %
|
||||
|
||||
.. note:: Make sure to use ``https://`` if the server supports it. Otherwise,
|
||||
your password and all data will be transferred to the server unencrypted.
|
||||
This makes it easy for third parties to intercept your communication, and
|
||||
getting hold of your password!
|
||||
|
||||
Next, you are prompted for your username and password. Again, use the same
|
||||
credentials that you would use to log on via the web interface.
|
||||
|
||||
.. image:: images/wizard_user.png
|
||||
:scale: 50 %
|
||||
|
||||
Finally, choose the folder that ownCloud Client is supposed to sync the
|
||||
contents of your ownCloud account with. By default, this is a folder
|
||||
called `ownCloud`, which will reside in your home directory.
|
||||
|
||||
.. image:: images/wizard_targetfolder.png
|
||||
:scale: 50 %
|
||||
|
||||
After pressing `Connect`, ownCloud Client will commence with the syncing
|
||||
process. The next screen will give you the opportunity to review your
|
||||
settings:
|
||||
|
||||
.. image:: images/wizard_overview.png
|
||||
:scale: 50 %
|
||||
14
doc/advancedusage.rst
Normal file
@@ -0,0 +1,14 @@
|
||||
Advanced Usage
|
||||
==============
|
||||
|
||||
.. index:: Advanced Usage
|
||||
|
||||
Options
|
||||
-------
|
||||
.. index:: command line switches, command line, options, parameters
|
||||
.. include:: options.rst
|
||||
|
||||
Config File
|
||||
-----------
|
||||
.. index:: config file
|
||||
.. include:: conffile.rst
|
||||
@@ -1,5 +1,6 @@
|
||||
Architecture
|
||||
============
|
||||
Appendix B: Architecture
|
||||
========================
|
||||
|
||||
.. index:: architecture
|
||||
|
||||
The ownCloud project provides desktop sync clients to synchronize the
|
||||
@@ -11,11 +12,10 @@ csync was written to synchronize with ownCloud’s built-in WebDAV server.
|
||||
|
||||
The ownCloud sync client is based on a tool called mirall initially written by
|
||||
Duncan Mac Vicar. Later Klaas Freitag joined the project and enhanced it to work
|
||||
with ownCloud server. Both mirall and ownCloud Client (oCC) build from the same
|
||||
source, currently hosted in the ownCloud source repo on gitorious.
|
||||
with ownCloud server.
|
||||
|
||||
oCC is written in C++ using the `Qt Framework`_. As a result oCC runs on the
|
||||
three important platforms Linux, Windows and MacOS.
|
||||
ownCloud Client is written in C++ using the `Qt Framework`_. As a result, the
|
||||
ownCloud Client runs on the three important platforms Linux, Windows and MacOS.
|
||||
|
||||
.. _csync: http://www.csync.org
|
||||
.. _`Qt Framework`: http://www.qt-project.org
|
||||
@@ -23,8 +23,8 @@ three important platforms Linux, Windows and MacOS.
|
||||
The Sync Process
|
||||
----------------
|
||||
|
||||
First it is important to recall what syncing is. Syncing tries to keep the files
|
||||
on both repositories the same. That means if a file is added to one repository
|
||||
First it is important to recall what syncing is: It tries to keep the files
|
||||
on two repositories the same. That means if a file is added to one repository
|
||||
it is going to be copied to the other repository. If a file is changed on one
|
||||
repository, the change is propagated to the other repository. Also, if a file
|
||||
is deleted on one side, it is deleted on the other. As a matter of fact, in
|
||||
@@ -34,10 +34,10 @@ server is always master.
|
||||
This is the major difference to other systems like a file backup where just
|
||||
changes and new files are propagated but files never get deleted.
|
||||
|
||||
The oCC checks both repositories for changes frequently after a certain time
|
||||
span. That is refered to as a sync run. In between the local repository is
|
||||
monitored by a file system monitor system that starts a sync run immediately
|
||||
if something was edited, added or removed.
|
||||
The ownCloud Client checks both repositories for changes frequently after a
|
||||
certain time span. That is refered to as a sync run. In between the local
|
||||
repository is monitored by a file system monitor system that starts a sync run
|
||||
immediately if something was edited, added or removed.
|
||||
|
||||
Sync by Time versus ETag
|
||||
------------------------
|
||||
@@ -62,16 +62,18 @@ machines.
|
||||
Since this strategy is rather fragile without NTP, ownCloud 4.5 introduced a
|
||||
unique number, which changes whenever the file changes. Although it is a unique
|
||||
value, it is not a hash of the file, but a randomly chosen number, which it will
|
||||
transmit in the Etag_ field. Since the file number is guaranteed to change if the
|
||||
file changes, it can now be used to determine if one of the files has changed.
|
||||
transmit in the Etag_ field. Since the file number is guaranteed to change if
|
||||
the file changes, it can now be used to determine if one of the files has
|
||||
changed.
|
||||
|
||||
.. note:: oCC 1.1 and newer require file ID capabilities on the ownCloud server,
|
||||
hence using them with a server earlier than 4.5.0 is not supported.
|
||||
.. note:: ownCloud Client 1.1 and newer require file ID capabilities on the
|
||||
ownCloud server, hence using them with a server earlier than 4.5.0 is
|
||||
not supported.
|
||||
|
||||
Before the 1.3.0 release of the client the sync process might create faux conflict
|
||||
files if time deviates. The original and the conflict files only differed in the
|
||||
timestamp, but not in content. This behaviour was changed towards a binary check
|
||||
if the files are different.
|
||||
Before the 1.3.0 release of the client the sync process might create faux
|
||||
conflict files if time deviates. The original and the conflict files only
|
||||
differed in the timestamp, but not in content. This behaviour was changed
|
||||
towards a binary check if the files are different.
|
||||
|
||||
Just like files, directories also hold a unique id, which changes whenever
|
||||
one of the contained files or directories gets modified. Since this is a
|
||||
@@ -105,7 +107,8 @@ are involved and one of them is not in sync with NTP time.
|
||||
.. _Etag: http://en.wikipedia.org/wiki/HTTP_ETag
|
||||
|
||||
Comparison and Conflict Cases
|
||||
----------------------------
|
||||
-----------------------------
|
||||
|
||||
In a sync run the client first has to detect if one of the two repositories have
|
||||
changed files. On the local repository, the client traverses the file
|
||||
tree and compares the modification time of each file with the value it was
|
||||
@@ -118,25 +121,41 @@ For the remote (ie. ownCloud) repository, the client compares the ETag of each
|
||||
file with it's previous value. Again the previous value is queried from the
|
||||
database. If the ETag is still the same, the file has not changed.
|
||||
|
||||
So what happens if a file has changed on both, the local and the remote repository
|
||||
since the last sync run? That means it can not easily be decided which version
|
||||
of the file is the one that should be used. Moreover, changes to any side must
|
||||
not be lost. That is called the conflict case and the client solves it by creating
|
||||
a conflict file of the older of the two files and save the newer one under the
|
||||
original file name. Conflict files are always created on the client and never on
|
||||
the server. The conflict file has the same name as the original file appended
|
||||
with the timestamp of the conflict detection.
|
||||
In case a file has changed on both, the local and the remote repository since
|
||||
the last sync run, it can not easily be decided which version of the file is
|
||||
the one that should be used. However, changes to any side must not be lost.
|
||||
|
||||
That is called a **conflict case**. The client solves it by creating a conflict
|
||||
file of the older of the two files and save the newer one under the original
|
||||
file name. Conflict files are always created on the client and never on the
|
||||
server. The conflict file has the same name as the original file appended with
|
||||
the timestamp of the conflict detection.
|
||||
|
||||
|
||||
.. _ignored-files-label:
|
||||
|
||||
Ignored Files
|
||||
-------------
|
||||
|
||||
ownCloud Client will refuse to sync the following files:
|
||||
|
||||
* Files matched by one of the pattern in :ref:`ignoredFilesEditor-label`
|
||||
* Files containing characters that do not work on certain file systems.
|
||||
Currently, these characters are: `\, :, ?, *, ", >, <, |`
|
||||
* Files starting in ``.csync_journal.db`` (reserved for journalling)
|
||||
|
||||
The Sync Journal
|
||||
----------------
|
||||
The client stores the ETag number in a per-directory database, called the journal.
|
||||
It is located in the application directory (until version 1.1) or as a hidden file
|
||||
right in the directory to be synced (later versions).
|
||||
|
||||
If the journal database gets removed, oCC's CSync backend will rebuild the database
|
||||
by comparing the files and their modification times. Thus it should be made sure
|
||||
that both server and client synchronized to NTP time before restarting the client
|
||||
after a database removal.
|
||||
The client stores the ETag number in a per-directory database,
|
||||
called the journal. It is a hidden file right in the directory
|
||||
to be synced.
|
||||
|
||||
The oCC also provides a button in the Settings Dialog that allows to "reset" the
|
||||
journal. That can be used to recreate the journal database.
|
||||
If the journal database gets removed, ownCloud Client's CSync backend will
|
||||
rebuild the database by comparing the files and their modification times. Thus
|
||||
it should be made sure that both server and client synchronized with NTP time
|
||||
before restarting the client after a database removal.
|
||||
|
||||
Pressing ``F5`` in the Account Settings Dialog that allows to "reset" the
|
||||
journal. That can be used to recreate the journal database. Use this only
|
||||
if advised to do so by the developer or support staff.
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
Building the Client
|
||||
===================
|
||||
.. _building-label:
|
||||
|
||||
Appendix A: Building the Client
|
||||
===============================
|
||||
|
||||
This section explains how to build the ownCloud Client from source
|
||||
for all major platforms. You should read this section if you want
|
||||
to development on the desktop client.
|
||||
|
||||
Note that the building instruction are subject to change as development
|
||||
proceeds. It is important to check the version which is to built.
|
||||
|
||||
This instructions were updated to work with ownCloud Client 1.5.
|
||||
|
||||
Linux
|
||||
-----
|
||||
|
||||
@@ -34,9 +41,10 @@ its own repository which contains non-standard recipes. Add it with::
|
||||
|
||||
Next, install the missing dependencies::
|
||||
|
||||
brew install $(brew deps ocsync)
|
||||
brew install $(brew deps ocsync)
|
||||
brew install $(brew deps mirall)
|
||||
|
||||
|
||||
To build mirall and csync, follow the `generic build instructions`_.
|
||||
|
||||
.. note::
|
||||
@@ -49,13 +57,12 @@ Windows (cross-compile)
|
||||
|
||||
Due to the amount of dependencies that csync entails, building the client
|
||||
for Windows is **currently only supported on openSUSE**, by using the MinGW
|
||||
cross compiler. You can set up openSUSE 12.1 or 12.2 in a virtual machine
|
||||
cross compiler. You can set up openSUSE 12.1, 12.2 or 13.1 in a virtual machine
|
||||
if you do not have it installed already.
|
||||
|
||||
In order to cross-compile, the following repositories need to be added
|
||||
via YaST or ``zypper ar`` (adjust when using openSUSE 12.2)::
|
||||
via YaST or ``zypper ar`` (adjust when using openSUSE 12.2 or 13.1)::
|
||||
|
||||
zypper ar http://download.opensuse.org/repositories/isv:/ownCloud:/devel:/mingw:/win32/openSUSE_12.1/isv:ownCloud:devel:mingw:win32.repo
|
||||
zypper ar http://download.opensuse.org/repositories/windows:/mingw:/win32/openSUSE_12.1/windows:mingw:win32.repo
|
||||
zypper ar http://download.opensuse.org/repositories/windows:/mingw/openSUSE_12.1/windows:mingw.repo
|
||||
|
||||
@@ -63,14 +70,16 @@ Next, install the cross-compiler packages and the cross-compiled dependencies::
|
||||
|
||||
zypper install cmake make mingw32-cross-binutils mingw32-cross-cpp mingw32-cross-gcc \
|
||||
mingw32-cross-gcc-c++ mingw32-cross-pkg-config mingw32-filesystem \
|
||||
mingw32-headers mingw32-runtime site-config mingw32-iniparser-devel \
|
||||
mingw32-libsqlite-devel mingw32-dlfcn-devel mingw32-libssh2-devel \
|
||||
kdewin-png2ico mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
|
||||
mingw32-libgnutls mingw32-libneon mingw32-libneon-devel mingw32-libbeecrypt \
|
||||
mingw32-libopenssl mingw32-openssl mingw32-libpng-devel mingw32-libsqlite \
|
||||
mingw32-qtkeychain mingw32-qtkeychain-devel mingw32-iniparser mingw32-dlfcn \
|
||||
mingw32-libintl-devel mingw32-libneon-devel mingw32-libopenssl-devel \
|
||||
mingw32-libproxy-devel mingw32-libxml2-devel mingw32-zlib-devel
|
||||
mingw32-headers mingw32-runtime site-config mingw32-libqt4-sql \
|
||||
mingw32-libqt4-sql-sqlite mingw32-libsqlite-devel \
|
||||
mingw32-dlfcn-devel mingw32-libssh2-devel kdewin-png2ico \
|
||||
mingw32-libqt4 mingw32-libqt4-devel mingw32-libgcrypt \
|
||||
mingw32-libgnutls mingw32-libneon-openssl mingw32-libneon-devel \
|
||||
mingw32-libbeecrypt mingw32-libopenssl mingw32-openssl \
|
||||
mingw32-libpng-devel mingw32-libsqlite mingw32-qtkeychain \
|
||||
mingw32-qtkeychain-devel mingw32-dlfcn mingw32-libintl-devel \
|
||||
mingw32-libneon-devel mingw32-libopenssl-devel mingw32-libproxy-devel \
|
||||
mingw32-libxml2-devel mingw32-zlib-devel
|
||||
|
||||
For the installer, the NSIS installer package is also required::
|
||||
|
||||
@@ -82,10 +91,10 @@ For the installer, the NSIS installer package is also required::
|
||||
mingw32-cross-nsis-plugin-processes mingw32-cross-nsis-plugin-uac
|
||||
|
||||
You will also need to manually download and install the following files with
|
||||
``rpm -ivh <package>`` (They will also work with OpenSUSE 12.2)::
|
||||
``rpm -ivh <package>`` (They will also work with openSUSE 12.2 and newer)::
|
||||
|
||||
rpm -ihv http://pmbs.links2linux.org/download/mingw:/32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-processes-0-1.1.x86_64.rpm
|
||||
rpm -ihv http://pmbs.links2linux.org/download/mingw:/32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-uac-0-3.1.x86_64.rpm
|
||||
rpm -ihv http://download.tomahawk-player.org/packman/mingw:32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-processes-0-1.1.x86_64.rpm
|
||||
rpm -ihv http://download.tomahawk-player.org/packman/mingw:32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-uac-0-3.1.x86_64.rpm
|
||||
|
||||
Now, follow the `generic build instructions`_, but pay attention to
|
||||
the following differences:
|
||||
@@ -115,7 +124,7 @@ CMake and Mirall can be downloaded at ownCloud's `Client Download Page`_.
|
||||
If you want to build the leading edge version of the client, you should
|
||||
use the latest versions of Mirall and CSync via Git_, like so::
|
||||
|
||||
git clone git://git.csync.org/users/freitag/csync.git ocsync
|
||||
git clone git://git.csync.org/users/owncloud/csync.git ocsync
|
||||
git clone git://github.com/owncloud/mirall.git
|
||||
|
||||
Next, create build directories::
|
||||
@@ -124,11 +133,11 @@ Next, create build directories::
|
||||
mkdir mirall-build
|
||||
|
||||
This guide assumes that all directories are residing next to each other.
|
||||
Next, make sure to check out the 'dav' branch in the newly checked out
|
||||
Next, make sure to check out the branch called 'ocsync' in the newly checked out
|
||||
`ocsync` directory::
|
||||
|
||||
cd ocsync
|
||||
git checkout dav
|
||||
git checkout ocsync
|
||||
|
||||
The first package to build is CSync::
|
||||
|
||||
@@ -137,9 +146,10 @@ The first package to build is CSync::
|
||||
make
|
||||
|
||||
You probably have to satisfy some dependencies. Make sure to install all the
|
||||
needed development packages. You will need ``iniparser``, ``sqlite3`` as well as
|
||||
``neon`` for the ownCloud module. Take special care about ``neon``. If that is
|
||||
missing, the cmake run will succeed but silently not build the ownCloud module.
|
||||
needed development packages. You will need ``sqlite3`` as well as ``neon`` for
|
||||
the ownCloud module. Take special care about ``neon``. If that is missing, the
|
||||
cmake run will succeed but silently not build the ownCloud module.
|
||||
|
||||
``libssh`` and ``libsmbclient`` are optional and not required for the client
|
||||
to work. If you want to install the client, run ``make install`` as a final step.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import sys, os
|
||||
extensions = ['sphinx.ext.todo']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
#templates_path = ['templates']
|
||||
templates_path = ['@CMAKE_CURRENT_SOURCE_DIR@/ocdoc/_shared_assets/templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
@@ -64,7 +64,7 @@ release = '@VERSION@'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build']
|
||||
exclude_patterns = ['_build','scripts/*']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
@@ -95,7 +95,7 @@ pygments_style = 'sphinx'
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
#html_theme_path = ['themes']
|
||||
html_theme_path = ['@CMAKE_CURRENT_SOURCE_DIR@/ocdoc/_shared_assets/themes']
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
@@ -120,7 +120,7 @@ html_short_title = "Client Manual"
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
#html_static_path = ['static']
|
||||
html_static_path = ['@CMAKE_CURRENT_SOURCE_DIR@/ocdoc/_shared_assets/static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
@@ -215,6 +215,8 @@ latex_documents = [
|
||||
man_pages = [
|
||||
('owncloud.1', 'owncloud', u'File synchronisation desktop utility.',
|
||||
[u'The ownCloud developers'], 1),
|
||||
('owncloudcmd.1', 'owncloudcmd', u'Command line ownCloud client tool.',
|
||||
[u'The ownCloud developers'], 1),
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
|
||||
@@ -4,8 +4,8 @@ Glossary
|
||||
.. glossary::
|
||||
:sorted:
|
||||
|
||||
ownCloud Sync Client
|
||||
ownCloud Client
|
||||
oCC
|
||||
Name of the official ownCloud syncing client for desktop, which runs on
|
||||
Windows, Mac OS X and Linux. It is based Mirall, and uses the CSync
|
||||
sync engine for synchronization with the ownCloud server.
|
||||
@@ -23,6 +23,7 @@ Glossary
|
||||
exist in the client directory.
|
||||
|
||||
unique id
|
||||
ETag
|
||||
ID assigned to every file starting with ownCloud server 4.5 and submitted
|
||||
via the HTTP ``Etag``. Used to check if files on client and server have
|
||||
changed.
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB |
BIN
doc/images/icon.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
doc/images/ignored_files_editor.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
doc/images/menu.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
doc/images/settings_account.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
doc/images/settings_general.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
doc/images/settings_network.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
doc/images/sync_protocol.png
Normal file
|
After Width: | Height: | Size: 168 KiB |
BIN
doc/images/wizard_overview.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
doc/images/wizard_targetfolder.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
doc/images/wizard_url.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
doc/images/wizard_user.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
@@ -6,8 +6,11 @@ Contents
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
install
|
||||
usage
|
||||
introduction
|
||||
accountsetup
|
||||
visualtour
|
||||
advancedusage
|
||||
|
||||
building
|
||||
architecture
|
||||
troubleshooting
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
The latest version of ownCloud client can be obtained at
|
||||
http://owncloud.org/sync-clients/.
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
ownCloud client for Windows is provided as a NSIS-based setup file for
|
||||
machine-wide install.
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
Installing the ownCloud client on your Mac follows the normal app installation
|
||||
pattern:
|
||||
|
||||
1. Download the installation file Click ownCloud-x.y.z.dmg, a window with the
|
||||
2. ownCloud icon opens In that window, drag the ownCloud application into the
|
||||
3. ‘Applications’ folder on the right hand side From ‘Applications’, choose
|
||||
ownCloud
|
||||
|
||||
Linux
|
||||
------
|
||||
|
||||
The ownCloud client is provided as in a convenient repository for a wide range
|
||||
of popular Linux distributions. If you want to build the sources instead.
|
||||
|
||||
Supported distributions are CentOS/RHEL, Fedora, SLES, openSUSE, Ubuntu and
|
||||
Debian.
|
||||
|
||||
To support other distributions, a source build is required.
|
||||
37
doc/introduction.rst
Normal file
@@ -0,0 +1,37 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
This is the documentation for the ownCloud Sync Client, also referred to as
|
||||
the ownCloud Client.
|
||||
|
||||
The ownCloud Sync Client is a desktop program you install on your computer.
|
||||
Specify one ore more directories on the local machine to sync your ownCloud
|
||||
server, and always have your latest files wherever you are. Make a change to the
|
||||
files on one computer, it will flow across the others using these desktop sync
|
||||
clients.
|
||||
|
||||
ownCloud Client is available for Windows, Mac OS X and various Linux
|
||||
distributions. See below for details on how to obtain the Client.
|
||||
|
||||
Obtaining the Client
|
||||
--------------------
|
||||
|
||||
The latest version of the ownCloud Client can be obtained at
|
||||
http://owncloud.org/sync-clients/.
|
||||
|
||||
ownCloud client for **Windows** is provided as a NSIS-based setup file for
|
||||
machine-wide install. Installing the ownCloud client on **Mac OS** follows
|
||||
the normal app bundle installation pattern:
|
||||
|
||||
1. Download the installation file: Click ``ownCloud-x.y.z.dmg``, a window with
|
||||
the ownCloud icon opens.
|
||||
2. In that window, drag the ownCloud application into the ``Applications``
|
||||
folder.
|
||||
3. On the right hand side From ``Applications``, choose ``ownCloud``.
|
||||
|
||||
The ownCloud Client is also provided as in a convenient repository for a wide
|
||||
range of popular **Linux distributions**. If you want to build the sources
|
||||
instead.
|
||||
|
||||
Supported distributions are Fedora, openSUSE, Ubuntu and Debian.
|
||||
To support other distributions, a is required, see :ref:`building-label`
|
||||
1
doc/ocdoc
Submodule
@@ -15,9 +15,6 @@ ownCloud Client supports the following command line switches:
|
||||
``--logflush``
|
||||
flush the log file after every write.
|
||||
|
||||
``--monoicons``
|
||||
Use black/white pictograms for systray.
|
||||
|
||||
``--confdir`` `<dirname>`
|
||||
Use the given configuration directory.
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
:orphan:
|
||||
|
||||
owncloud(1)
|
||||
-----------
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
*owncloud* [`OPTIONS`...]
|
||||
@@ -28,10 +29,10 @@ Config File
|
||||
BUGS
|
||||
====
|
||||
|
||||
Please report bugs at https://github.com/owncloud/core/issues.
|
||||
Please report bugs at https://github.com/owncloud/mirall/issues.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
`csync(1)`
|
||||
:manpage:`owncloudcmd(1)`
|
||||
|
||||
|
||||
34
doc/owncloudcmd.1.rst
Normal file
@@ -0,0 +1,34 @@
|
||||
:orphan:
|
||||
|
||||
owncloudcmd(1)
|
||||
--------------
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
*owncloudcmd* [`OPTIONS`...] sourcedir owncloudurl
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
owncloudcmd is the command line tool for the ownCloud file synchronisation
|
||||
desktop utility, based on mirall.
|
||||
|
||||
Contrary to the :manpage:`owncloud(1)` GUI client, `owncloudcmd` will only
|
||||
perform a single sync run and then exit. It thus replaces the `ocsync` binary
|
||||
used for the same purpose in earlier releases.
|
||||
|
||||
A sync run will sync a single local directory with a WebDAV share on a
|
||||
remote ownCloud server.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
``--confdir`` `PATH`
|
||||
The configuration dir where `csync.conf` is located
|
||||
|
||||
BUGS
|
||||
====
|
||||
Please report bugs at https://github.com/owncloud/mirall/issues.
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
:manpage:`owncloud(1)`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Troubleshooting
|
||||
===============
|
||||
Appendix C: Troubleshooting
|
||||
===========================
|
||||
|
||||
If the client fails to start syncing it basically can have two
|
||||
basic reasons: Either the server setup has a problem or the client
|
||||
@@ -55,6 +55,12 @@ ownCloud is not shared with other syncing apps.
|
||||
not be attempted. In the worst case, doing so can result in data
|
||||
loss.
|
||||
|
||||
If some files do not get take a look at the sync protocol. Some files are
|
||||
automatically automatically being ignored because they are system files,
|
||||
others get ignored because their file name contains characters that cannot
|
||||
be represented on certain file systems. See :ref:`_ignored-files-label` for
|
||||
details.
|
||||
|
||||
If you are operating your own server and use the local storage backend (the
|
||||
default), make sure that ownCloud has exclusive access to the directory.
|
||||
|
||||
@@ -65,13 +71,15 @@ If you are using a different backend, you can try to exclude a bug in the
|
||||
backend by reverting to the local backend.
|
||||
|
||||
Logfiles
|
||||
========
|
||||
--------
|
||||
|
||||
Doing effective debugging requires to provide as much as relevant logs as
|
||||
possible. The log output can help you with tracking down problem, and if you
|
||||
report a bug, you're advised to include the output.
|
||||
|
||||
:Client Logfile:
|
||||
Client Logfile
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
Start the client with ``--logwindow``. That opens a window providing a view
|
||||
on the current log. It provides a Save button to let you save the log to a
|
||||
file.
|
||||
@@ -97,11 +105,14 @@ given expiry period.
|
||||
|
||||
For example, for a long running test where you intend to keep the log data of the
|
||||
last two days, this would be the command line:
|
||||
|
||||
```
|
||||
owncloud --logdir /tmp/owncloud_logs --logexpire 48
|
||||
```
|
||||
|
||||
:ownCloud server Logfile:
|
||||
ownCloud server Logfile
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ownCloud server maintains an ownCloud specific logfile as well. It can and
|
||||
must be enabled through the ownCloud Administration page. There you can adjust
|
||||
the loglevel. It is advisable to set it to a verbose level like ``Debug`` or
|
||||
@@ -110,7 +121,9 @@ the loglevel. It is advisable to set it to a verbose level like ``Debug`` or
|
||||
The logfile can be viewed either in the web interface or can be found in the
|
||||
filesystem in the ownCloud server data dir.
|
||||
|
||||
:Webserver Logfiles:
|
||||
Webserver Logfiles
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Also, please take a look at your webservers error log file to check if there
|
||||
are problems. For Apache on Linux, the error logs usually can be found at
|
||||
``/var/log/apache2``. A file called ``error_log`` shows errors like PHP code
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
Usage
|
||||
=====
|
||||
.. index:: usage, client sync usage
|
||||
|
||||
To start ownCloud Client, click on the desktop icon or start it from the
|
||||
application menu. In the system tray, an ownCloud icon appears.
|
||||
|
||||
.. index:: start application
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
ownCloud is represented by an icon in the Desktop's system tray, also known
|
||||
as notification area.
|
||||
|
||||
The clients menu is accessed with a right click (Windows, Linux) or left click
|
||||
(Mac OS).
|
||||
|
||||
The status of the current sync can be observed in the Status dialog, available
|
||||
trough the ``Open status...`` option. On Windows, a left click on the tray icon
|
||||
also opens the status dialog.
|
||||
|
||||
.. note:: Until the intial setup has finished, the Connection Wizard will be
|
||||
shown instead when left-clicking on Windows.
|
||||
|
||||
The dialog provides an overview on the configured sync folders and allows to add
|
||||
and remove more sync folder connections as well as pausing a sync connection.
|
||||
|
||||
Changing Your Password and Account Settings
|
||||
-------------------------------------------
|
||||
|
||||
In the ``Settings`` Dialog, choose ``Account`` -> ``Modify Account``. It will open
|
||||
Setup Wizard, which next to reconfiguring your connection to use a different
|
||||
user or server also will allow to change the password for the local account,
|
||||
or to switch from HTTP to HTTPS.
|
||||
|
||||
Setting up a Proxy
|
||||
------------------
|
||||
|
||||
By default, the configured system proxy will be picked up. This may not be
|
||||
working reliably on some Linux distributions, as only the ``http_proxy``
|
||||
variable gets picked up. You can configure a proxy different from your
|
||||
system default in the ``Network`` section of the ``Settings`` dialog.
|
||||
|
||||
The default settings assume an HTTP proxy, which is the typical use case.
|
||||
If you require SOCKS 5 proxy, pick ``SOCKS5 proxy`` instead of ``HTTP(S) proxy``
|
||||
from the drop down menu. SOCKS 5 proxies are typically provided by some
|
||||
SSH implementations, for instance OpenSSH's ``-D`` parameter. This is
|
||||
useful for scenarios where SSH is employed to securely tunnel a client
|
||||
to the network running the ownCloud server.
|
||||
|
||||
Limiting Bandwidth
|
||||
------------------
|
||||
|
||||
Starting with Version 1.4, the Client provides bandwidth limiter.
|
||||
This option can be found in the ``Network`` section of the
|
||||
``Settings Dialog``.
|
||||
|
||||
You will find two settings for ``Download Bandwidth`` and
|
||||
``Upload Bandwidth``.
|
||||
|
||||
Upload Bandwidth
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The default is to automatically limit the upload. The rationale
|
||||
for this default is that typically, Computers and laptops are
|
||||
not directly connected to the server, but via a Cable Modems
|
||||
or DSL lines, which provide significantly more downstream than
|
||||
upstream bandwith. Sataurating the upstream bandwidth would
|
||||
interfere with other applications, especially Voice-Over-IP or
|
||||
Games.
|
||||
|
||||
The automatic limiter will throttle the speed to about 75%
|
||||
of the available upstream bandwidth. If you are communicating
|
||||
with the server via a fast, symetric connection, you can set the
|
||||
Limiter to ``No Limit`` instead. If want a stronger limitation,
|
||||
choose ``Limit to`` and specify a limit manually.
|
||||
|
||||
|
||||
Download Bandwidth
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Because the download bandwidth is usually no concern, it is not
|
||||
automatically limited. Should you find that the Client is taking
|
||||
up too much bandwidth, you can manually specify a limit (in KB).
|
||||
|
||||
Options
|
||||
-------
|
||||
.. index:: command line switches, command line, options, parameters
|
||||
.. include:: options.rst
|
||||
|
||||
Config File
|
||||
-----------
|
||||
.. index:: config file
|
||||
.. include:: conffile.rst
|
||||
189
doc/visualtour.rst
Normal file
@@ -0,0 +1,189 @@
|
||||
Visual Tour
|
||||
===========
|
||||
|
||||
.. index:: visual tour, usage
|
||||
|
||||
ownCloud Client stays in the background, and is visible as an
|
||||
icon in your system tray (Windows, KDE), status bar (Mac OS X)
|
||||
or notification area (Ubuntu), like so:
|
||||
|
||||
.. image:: images/icon.png
|
||||
|
||||
If a setup is still required, it will open the setup. Otherwise, the
|
||||
main menu is opened, which provides several options and displays
|
||||
progress information:
|
||||
|
||||
.. image:: images/menu.png
|
||||
|
||||
Here is an explanation of the individual items in the menu:
|
||||
|
||||
* ``Open ownCloud in browser``: Opens the ownCloud web interface
|
||||
* ``Open folder 'ownCloud'``: Opens the local folder. If you have
|
||||
defined multiple sync targets, you should see multiple entries
|
||||
here.
|
||||
* **Disk space indicator**: Shows how much space is used up on the server.
|
||||
* Operation indicator: Shows the status of the current sync process, or
|
||||
``Up to date`` if server and client are in sync.
|
||||
* **Recent Changes**: shows the last six files modified by sync operations,
|
||||
and provides access to the Sync status, which lists all changes
|
||||
since the last restart of ownCloud Client.
|
||||
* ``Settings...``: provides access to the settings menu.
|
||||
* ``Help``: Opens a browser to display this help.
|
||||
* ``Quit ownCloud``: Quits ownCloud, ending a currently running sync run.
|
||||
|
||||
The settings dialog is split up in three categories: ``Account Settings``,
|
||||
``General Settings`` and ``Network Settings``:
|
||||
|
||||
Account Settings
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. index:: account settings, user, password, Server URL
|
||||
|
||||
The ``Account Settings`` tab provides an executive summary about the synced
|
||||
folders in your account and allows to modify them. It also provides a more
|
||||
detailed report about the storage usage. Finally, it allows to change
|
||||
the files that ownCloud Client should ignore (for details, see the
|
||||
``Ignored Files Editor`` section below), and to modify various aspects
|
||||
of the current account settings, such as user name, password and server URL.
|
||||
|
||||
.. image:: images/settings_account.png
|
||||
:scale: 50 %
|
||||
|
||||
General Settings
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. index:: general settings, auto start, startup, desktop notifications
|
||||
|
||||
The tab provides several useful options:
|
||||
|
||||
.. image:: images/settings_general.png
|
||||
:scale: 50 %
|
||||
|
||||
* **Launch on System Startup**: This option is automatically activated
|
||||
once a user has conimaged his account. Unchecking the box will cause
|
||||
ownCloud client to not launch on startup for a particular user.
|
||||
* **Show Desktop Nofications**: Do not show bubble notifications whenever
|
||||
a set of sync operations has been performed.
|
||||
* **Use Monochrome Icons**: Use less obstrusive icons. Especially useful
|
||||
on Mac OS.
|
||||
|
||||
The acout menu provides information about authors as well as detailed
|
||||
information about the build conditions. Those are particularly valuable
|
||||
when filing a bug report.
|
||||
|
||||
Network Settings
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
.. index:: proxy settings, SOCKS, bandwith, throttling, limiting
|
||||
|
||||
This tab consollidates ``Proxy Settings`` and ``Bandwith Limiting``:
|
||||
|
||||
.. image:: images/settings_network.png
|
||||
:scale: 50 %
|
||||
|
||||
Proxy Settings
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
* ``No Proxy``: Check this if ownCloud Client should circumvent the default
|
||||
proxy conimaged on the system.
|
||||
* ``Use system proxy``: Default, will follow the systems proxy settings.
|
||||
On Linux, this will only pick up the value of the variable ``http_proxy``.
|
||||
* ``Specify proxy manually as``: Allows to specify custom proxy settings.
|
||||
If you require to go through a HTTP(S) proxy server such as Squid or Microsoft
|
||||
Forefront TMG, pick ``HTTP(S)``. ``SOCKSv5`` on the other hand is particulary
|
||||
useful in special company LAN setups, or in combination with the OpenSSH
|
||||
dynamic application level forwarding feature (see ``ssh -D``).
|
||||
* ``Host``: Enter the host name or IP address of your proxy server, followed
|
||||
by the port number. HTTP proxies usually listen on Ports 8080 (default) or
|
||||
3128. SOCKS server usually listen on port 1080.
|
||||
* ``Proxy Server requires authentication``: Should be checked if the proxy
|
||||
server does not allow anonymous usage. If you check this option, you must
|
||||
provide username and password in the fields below, or ownless Cloud will no
|
||||
longer be able to connect successfully.
|
||||
|
||||
Bandwidth Limiting
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ``Download Bandwidth`` (i.e. the bandwidth available for data flowing
|
||||
from the ownCloud Server to the client) can be either ``Unlimited``
|
||||
(the default), or limited to a custom value, specified in bytes
|
||||
|
||||
The ``Upload Bandwith`` (i.e. the bandwith available for data flowing
|
||||
from the ownCloud Client to the server) additionally has the option
|
||||
to ``Limit automatically``: When this option is checked, the ownCloud
|
||||
Client will surrender available upstream bandwith to other applications.
|
||||
Use this option if you expirience problems with real time communication,
|
||||
such as Skype or other VoIP software, in conjunction with ownCloud Client.
|
||||
This is commonly the case with asymmetric internet connection, such as
|
||||
certain DSL lines with very limited upstream capacity.
|
||||
|
||||
ownCloud Client will pick up changes immediately, but ongoing operations
|
||||
will finish using the old settings.
|
||||
|
||||
The Sync Status Display
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. index:: sync status
|
||||
|
||||
The ``Sync Status`` window, which can be invoked from either from the main
|
||||
menu (``Recent Changes`` -> ``Details...``) or the ``Account Settings``
|
||||
(``Info`` button), will provide you with an in-depth summary of the recent
|
||||
sync activity. It will also show files that have not been synched (ignored
|
||||
files). Those are ignored either because they are listed in the ignored
|
||||
files list (see ``Ignored Files Editor`` section below), or because they
|
||||
cannot be synced in a cross-platform manner because they contain special
|
||||
characters that cannot be stored on certain file systems.
|
||||
|
||||
.. image:: images/sync_protocol.png
|
||||
:scale: 50 %
|
||||
|
||||
.. _ignoredFilesEditor-label:
|
||||
|
||||
The Ignored Files Editor
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. index:: ignored files, exclude files, pattern
|
||||
|
||||
The ignored files editor allows adding patterns for files or directories
|
||||
that should be excluded from the sync process. Next to normal characters,
|
||||
wildcards can be used to match an arbitrary number of characters, designated
|
||||
by an asterisk (``*``) or a single character, designated by a question mark
|
||||
(``?``).
|
||||
|
||||
Global defaults cannot be directly modified within the editor. Hovering
|
||||
with the mouse will reveal the location of the global exclude definition
|
||||
file.
|
||||
|
||||
In addition to this list, ownCloud Client always excludes files with
|
||||
characters that cannot be synched down to other file systems,
|
||||
see :ref:`ignored-files-label`.
|
||||
|
||||
.. note:: Modifying the global exclude definition file might render the
|
||||
client unusable or cause undesired behavior.
|
||||
|
||||
.. note:: Custom entries are currently not validated for syntactical
|
||||
correctness by the editor, but might fail to load correctly.
|
||||
|
||||
.. image:: images/ignored_files_editor.png
|
||||
:scale: 50%
|
||||
|
||||
Pattern Matching
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
To match file names against the exclude patterns, the unix standard C
|
||||
library function fnmatch is used. It checks the filename against the pattern
|
||||
using standard shell wildcard pattern matching. Check `The opengroup website
|
||||
<http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13_01>`
|
||||
for the gory details.
|
||||
|
||||
The path that is checked is the relative path unter the sync root directory.
|
||||
|
||||
Examples:
|
||||
^^^^^^^^^
|
||||
+-----------+------------------------------+
|
||||
| Pattern | Matches |
|
||||
+===========+==============================+
|
||||
| ``~$*`` | ``~$foo``, ``~$example.doc`` |
|
||||
+-----------+------------------------------+
|
||||
| ``fl?p`` | ``flip``, ``flap`` |
|
||||
+-----------+------------------------------+
|
||||
@@ -5,4 +5,4 @@ Exec=@APPLICATION_EXECUTABLE@
|
||||
Name=@APPLICATION_NAME@ desktop sync client
|
||||
GenericName=Folder Sync
|
||||
Icon=@APPLICATION_EXECUTABLE@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<file>resources/view-refresh.png</file>
|
||||
<file>resources/warning-16.png</file>
|
||||
<file>resources/settings.png</file>
|
||||
<file>resources/activity.png</file>
|
||||
<file>resources/network.png</file>
|
||||
<file>resources/owncloud_logo_blue.png</file>
|
||||
</qresource>
|
||||
|
||||
BIN
resources/activity.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
245
src/3rdparty/csync/c_jhash.h
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* c_jhash.c Jenkins Hash
|
||||
*
|
||||
* Copyright (c) 1997 Bob Jenkins <bob_jenkins@burtleburtle.net>
|
||||
*
|
||||
* lookup8.c, by Bob Jenkins, January 4 1997, Public Domain.
|
||||
* hash(), hash2(), hash3, and _c_mix() are externally useful functions.
|
||||
* Routines to test the hash are included if SELF_TEST is defined.
|
||||
* You can use this free for any purpose. It has no warranty.
|
||||
*
|
||||
* See http://burtleburtle.net/bob/hash/evahash.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file c_jhash.h
|
||||
*
|
||||
* @brief Interface of the cynapses jhash implementation
|
||||
*
|
||||
* @defgroup cynJHashInternals cynapses libc jhash function
|
||||
* @ingroup cynLibraryAPI
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
#ifndef _C_JHASH_H
|
||||
#define _C_JHASH_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define c_hashsize(n) ((uint8_t) 1 << (n))
|
||||
#define c_hashmask(n) (xhashsize(n) - 1)
|
||||
|
||||
/**
|
||||
* _c_mix -- Mix 3 32-bit values reversibly.
|
||||
*
|
||||
* For every delta with one or two bit set, and the deltas of all three
|
||||
* high bits or all three low bits, whether the original value of a,b,c
|
||||
* is almost all zero or is uniformly distributed,
|
||||
* If _c_mix() is run forward or backward, at least 32 bits in a,b,c
|
||||
* have at least 1/4 probability of changing.
|
||||
* If _c_mix() is run forward, every bit of c will change between 1/3 and
|
||||
* 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
|
||||
* _c_mix() was built out of 36 single-cycle latency instructions in a
|
||||
* structure that could supported 2x parallelism, like so:
|
||||
* a -= b;
|
||||
* a -= c; x = (c>>13);
|
||||
* b -= c; a ^= x;
|
||||
* b -= a; x = (a<<8);
|
||||
* c -= a; b ^= x;
|
||||
* c -= b; x = (b>>13);
|
||||
* ...
|
||||
*
|
||||
* Unfortunately, superscalar Pentiums and Sparcs can't take advantage
|
||||
* of that parallelism. They've also turned some of those single-cycle
|
||||
* latency instructions into multi-cycle latency instructions. Still,
|
||||
* this is the fastest good hash I could find. There were about 2^^68
|
||||
* to choose from. I only looked at a billion or so.
|
||||
*/
|
||||
#define _c_mix(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>13); \
|
||||
b -= c; b -= a; b ^= (a<<8); \
|
||||
c -= a; c -= b; c ^= (b>>13); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<16); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>3); \
|
||||
b -= c; b -= a; b ^= (a<<10); \
|
||||
c -= a; c -= b; c ^= (b>>15); \
|
||||
}
|
||||
|
||||
/**
|
||||
* _c_mix64 -- Mix 3 64-bit values reversibly.
|
||||
*
|
||||
* _c_mix64() takes 48 machine instructions, but only 24 cycles on a superscalar
|
||||
* machine (like Intel's new MMX architecture). It requires 4 64-bit
|
||||
* registers for 4::2 parallelism.
|
||||
* All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of
|
||||
* (a,b,c), and all deltas of bottom bits were tested. All deltas were
|
||||
* tested both on random keys and on keys that were nearly all zero.
|
||||
* These deltas all cause every bit of c to change between 1/3 and 2/3
|
||||
* of the time (well, only 113/400 to 287/400 of the time for some
|
||||
* 2-bit delta). These deltas all cause at least 80 bits to change
|
||||
* among (a,b,c) when the _c_mix is run either forward or backward (yes it
|
||||
* is reversible).
|
||||
* This implies that a hash using _c_mix64 has no funnels. There may be
|
||||
* characteristics with 3-bit deltas or bigger, I didn't test for
|
||||
* those.
|
||||
*/
|
||||
#define _c_mix64(a,b,c) \
|
||||
{ \
|
||||
a -= b; a -= c; a ^= (c>>43); \
|
||||
b -= c; b -= a; b ^= (a<<9); \
|
||||
c -= a; c -= b; c ^= (b>>8); \
|
||||
a -= b; a -= c; a ^= (c>>38); \
|
||||
b -= c; b -= a; b ^= (a<<23); \
|
||||
c -= a; c -= b; c ^= (b>>5); \
|
||||
a -= b; a -= c; a ^= (c>>35); \
|
||||
b -= c; b -= a; b ^= (a<<49); \
|
||||
c -= a; c -= b; c ^= (b>>11); \
|
||||
a -= b; a -= c; a ^= (c>>12); \
|
||||
b -= c; b -= a; b ^= (a<<18); \
|
||||
c -= a; c -= b; c ^= (b>>22); \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief hash a variable-length key into a 32-bit value
|
||||
*
|
||||
* The best hash table sizes are powers of 2. There is no need to do
|
||||
* mod a prime (mod is sooo slow!). If you need less than 32 bits,
|
||||
* use a bitmask. For example, if you need only 10 bits, do
|
||||
* h = (h & hashmask(10));
|
||||
* In which case, the hash table should have hashsize(10) elements.
|
||||
*
|
||||
* Use for hash table lookup, or anything where one collision in 2^32 is
|
||||
* acceptable. Do NOT use for cryptographic purposes.
|
||||
*
|
||||
* @param k The key (the unaligned variable-length array of bytes).
|
||||
*
|
||||
* @param length The length of the key, counting by bytes.
|
||||
*
|
||||
* @param initval Initial value, can be any 4-byte value.
|
||||
*
|
||||
* @return Returns a 32-bit value. Every bit of the key affects every bit
|
||||
* of the return value. Every 1-bit and 2-bit delta achieves
|
||||
* avalanche. About 36+6len instructions.
|
||||
*/
|
||||
static inline uint32_t c_jhash(const uint8_t *k, uint32_t length, uint32_t initval) {
|
||||
uint32_t a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
|
||||
c = initval; /* the previous hash value */
|
||||
|
||||
while (len >= 12) {
|
||||
a += (k[0] +((uint32_t)k[1]<<8) +((uint32_t)k[2]<<16) +((uint32_t)k[3]<<24));
|
||||
b += (k[4] +((uint32_t)k[5]<<8) +((uint32_t)k[6]<<16) +((uint32_t)k[7]<<24));
|
||||
c += (k[8] +((uint32_t)k[9]<<8) +((uint32_t)k[10]<<16)+((uint32_t)k[11]<<24));
|
||||
_c_mix(a,b,c);
|
||||
k += 12; len -= 12;
|
||||
}
|
||||
|
||||
/* handle the last 11 bytes */
|
||||
c += length;
|
||||
/* all the case statements fall through */
|
||||
switch(len) {
|
||||
case 11: c+=((uint32_t)k[10]<<24);
|
||||
case 10: c+=((uint32_t)k[9]<<16);
|
||||
case 9 : c+=((uint32_t)k[8]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 8 : b+=((uint32_t)k[7]<<24);
|
||||
case 7 : b+=((uint32_t)k[6]<<16);
|
||||
case 6 : b+=((uint32_t)k[5]<<8);
|
||||
case 5 : b+=k[4];
|
||||
case 4 : a+=((uint32_t)k[3]<<24);
|
||||
case 3 : a+=((uint32_t)k[2]<<16);
|
||||
case 2 : a+=((uint32_t)k[1]<<8);
|
||||
case 1 : a+=k[0];
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
_c_mix(a,b,c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief hash a variable-length key into a 64-bit value
|
||||
*
|
||||
* The best hash table sizes are powers of 2. There is no need to do
|
||||
* mod a prime (mod is sooo slow!). If you need less than 64 bits,
|
||||
* use a bitmask. For example, if you need only 10 bits, do
|
||||
* h = (h & hashmask(10));
|
||||
* In which case, the hash table should have hashsize(10) elements.
|
||||
*
|
||||
* Use for hash table lookup, or anything where one collision in 2^^64
|
||||
* is acceptable. Do NOT use for cryptographic purposes.
|
||||
*
|
||||
* @param k The key (the unaligned variable-length array of bytes).
|
||||
* @param length The length of the key, counting by bytes.
|
||||
* @param intval Initial value, can be any 8-byte value.
|
||||
*
|
||||
* @return A 64-bit value. Every bit of the key affects every bit of
|
||||
* the return value. No funnels. Every 1-bit and 2-bit delta
|
||||
* achieves avalanche. About 41+5len instructions.
|
||||
*/
|
||||
static inline uint64_t c_jhash64(const uint8_t *k, uint64_t length, uint64_t intval) {
|
||||
uint64_t a,b,c,len;
|
||||
|
||||
/* Set up the internal state */
|
||||
len = length;
|
||||
a = b = intval; /* the previous hash value */
|
||||
c = 0x9e3779b97f4a7c13LL; /* the golden ratio; an arbitrary value */
|
||||
|
||||
/* handle most of the key */
|
||||
while (len >= 24)
|
||||
{
|
||||
a += (k[0] +((uint64_t)k[ 1]<< 8)+((uint64_t)k[ 2]<<16)+((uint64_t)k[ 3]<<24)
|
||||
+((uint64_t)k[4 ]<<32)+((uint64_t)k[ 5]<<40)+((uint64_t)k[ 6]<<48)+((uint64_t)k[ 7]<<56));
|
||||
b += (k[8] +((uint64_t)k[ 9]<< 8)+((uint64_t)k[10]<<16)+((uint64_t)k[11]<<24)
|
||||
+((uint64_t)k[12]<<32)+((uint64_t)k[13]<<40)+((uint64_t)k[14]<<48)+((uint64_t)k[15]<<56));
|
||||
c += (k[16] +((uint64_t)k[17]<< 8)+((uint64_t)k[18]<<16)+((uint64_t)k[19]<<24)
|
||||
+((uint64_t)k[20]<<32)+((uint64_t)k[21]<<40)+((uint64_t)k[22]<<48)+((uint64_t)k[23]<<56));
|
||||
_c_mix64(a,b,c);
|
||||
k += 24; len -= 24;
|
||||
}
|
||||
|
||||
/* handle the last 23 bytes */
|
||||
c += length;
|
||||
switch(len) {
|
||||
case 23: c+=((uint64_t)k[22]<<56);
|
||||
case 22: c+=((uint64_t)k[21]<<48);
|
||||
case 21: c+=((uint64_t)k[20]<<40);
|
||||
case 20: c+=((uint64_t)k[19]<<32);
|
||||
case 19: c+=((uint64_t)k[18]<<24);
|
||||
case 18: c+=((uint64_t)k[17]<<16);
|
||||
case 17: c+=((uint64_t)k[16]<<8);
|
||||
/* the first byte of c is reserved for the length */
|
||||
case 16: b+=((uint64_t)k[15]<<56);
|
||||
case 15: b+=((uint64_t)k[14]<<48);
|
||||
case 14: b+=((uint64_t)k[13]<<40);
|
||||
case 13: b+=((uint64_t)k[12]<<32);
|
||||
case 12: b+=((uint64_t)k[11]<<24);
|
||||
case 11: b+=((uint64_t)k[10]<<16);
|
||||
case 10: b+=((uint64_t)k[ 9]<<8);
|
||||
case 9: b+=((uint64_t)k[ 8]);
|
||||
case 8: a+=((uint64_t)k[ 7]<<56);
|
||||
case 7: a+=((uint64_t)k[ 6]<<48);
|
||||
case 6: a+=((uint64_t)k[ 5]<<40);
|
||||
case 5: a+=((uint64_t)k[ 4]<<32);
|
||||
case 4: a+=((uint64_t)k[ 3]<<24);
|
||||
case 3: a+=((uint64_t)k[ 2]<<16);
|
||||
case 2: a+=((uint64_t)k[ 1]<<8);
|
||||
case 1: a+=((uint64_t)k[ 0]);
|
||||
/* case 0: nothing left to add */
|
||||
}
|
||||
_c_mix64(a,b,c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* }@
|
||||
*/
|
||||
#endif /* _C_JHASH_H */
|
||||
|
||||
3
src/3rdparty/qjson/AUTHORS
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
Eeli Reilin <eeli@emicode.fi>
|
||||
Luis Gustavo S. Barreto <gustavosbarreto@gmail.com>
|
||||
Stephen Kockentiedt <Stephen@Kockentiedt.name>
|
||||
26
src/3rdparty/qjson/LICENSE
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
Copyright 2011 Eeli Reilin. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation
|
||||
are those of the authors and should not be interpreted as representing
|
||||
official policies, either expressed or implied, of Eeli Reilin.
|
||||
96
src/3rdparty/qjson/README
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
########################################################################
|
||||
1. INTRODUCTION
|
||||
|
||||
The Json class is a simple class for parsing JSON data into a QVariant
|
||||
hierarchies. Now, we can also reverse the process and serialize
|
||||
QVariant hierarchies into valid JSON data.
|
||||
|
||||
|
||||
########################################################################
|
||||
2. HOW TO USE
|
||||
|
||||
The parser is really easy to use. Let's say we have the following
|
||||
QString of JSON data:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
{
|
||||
"encoding" : "UTF-8",
|
||||
"plug-ins" : [
|
||||
"python",
|
||||
"c++",
|
||||
"ruby"
|
||||
],
|
||||
"indent" : {
|
||||
"length" : 3,
|
||||
"use_space" : true
|
||||
}
|
||||
}
|
||||
------------------------------------------------------------------------
|
||||
|
||||
We would first call the parse-method:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
//Say that we're using the QtJson namespace
|
||||
using namespace QtJson;
|
||||
bool ok;
|
||||
//json is a QString containing the JSON data
|
||||
QVariantMap result = Json::parse(json, ok).toMap();
|
||||
|
||||
if(!ok) {
|
||||
qFatal("An error occurred during parsing");
|
||||
exit(1);
|
||||
}
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Assuming the parsing process completed without errors, we would then
|
||||
go through the hierarchy:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
qDebug() << "encoding:" << result["encoding"].toString();
|
||||
qDebug() << "plugins:";
|
||||
|
||||
foreach(QVariant plugin, result["plug-ins"].toList()) {
|
||||
qDebug() << "\t-" << plugin.toString();
|
||||
}
|
||||
|
||||
QVariantMap nestedMap = result["indent"].toMap();
|
||||
qDebug() << "length:" << nestedMap["length"].toInt();
|
||||
qDebug() << "use_space:" << nestedMap["use_space"].toBool();
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The previous code would print out the following:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
encoding: "UTF-8"
|
||||
plugins:
|
||||
- "python"
|
||||
- "c++"
|
||||
- "ruby"
|
||||
length: 3
|
||||
use_space: true
|
||||
------------------------------------------------------------------------
|
||||
|
||||
To write JSON data from Qt object is as simple as parsing:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
QVariantMap map;
|
||||
map["name"] = "Name";
|
||||
map["age"] = 22;
|
||||
|
||||
QByteArray data = Json::serialize(map);
|
||||
------------------------------------------------------------------------
|
||||
|
||||
The byte array 'data' contains valid JSON data:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
{
|
||||
name: "Luis Gustavo",
|
||||
age: 22,
|
||||
}
|
||||
------------------------------------------------------------------------
|
||||
|
||||
|
||||
########################################################################
|
||||
4. CONTRIBUTING
|
||||
|
||||
The code is available to download at GitHub. Contribute if you dare!
|
||||
653
src/3rdparty/qjson/json.cpp
vendored
Normal file
@@ -0,0 +1,653 @@
|
||||
/* Copyright 2011 Eeli Reilin. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation
|
||||
* are those of the authors and should not be interpreted as representing
|
||||
* official policies, either expressed or implied, of Eeli Reilin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file json.cpp
|
||||
*/
|
||||
|
||||
#include "json.h"
|
||||
|
||||
namespace QtJson
|
||||
{
|
||||
|
||||
|
||||
static QString sanitizeString(QString str);
|
||||
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep);
|
||||
static QVariant parseValue(const QString &json, int &index, bool &success);
|
||||
static QVariant parseObject(const QString &json, int &index, bool &success);
|
||||
static QVariant parseArray(const QString &json, int &index, bool &success);
|
||||
static QVariant parseString(const QString &json, int &index, bool &success);
|
||||
static QVariant parseNumber(const QString &json, int &index);
|
||||
static int lastIndexOfNumber(const QString &json, int index);
|
||||
static void eatWhitespace(const QString &json, int &index);
|
||||
static int lookAhead(const QString &json, int index);
|
||||
static int nextToken(const QString &json, int &index);
|
||||
|
||||
|
||||
|
||||
|
||||
/***** public *****/
|
||||
|
||||
|
||||
/**
|
||||
* parse
|
||||
*/
|
||||
QVariant parse(const QString &json)
|
||||
{
|
||||
bool success = true;
|
||||
return parse(json, success);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse
|
||||
*/
|
||||
QVariant parse(const QString &json, bool &success)
|
||||
{
|
||||
success = true;
|
||||
|
||||
//Return an empty QVariant if the JSON data is either null or empty
|
||||
if(!json.isNull() || !json.isEmpty())
|
||||
{
|
||||
QString data = json;
|
||||
//We'll start from index 0
|
||||
int index = 0;
|
||||
|
||||
//Parse the first value
|
||||
QVariant value = parseValue(data, index, success);
|
||||
|
||||
//Return the parsed value
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Return the empty QVariant
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray serialize(const QVariant &data)
|
||||
{
|
||||
bool success = true;
|
||||
return serialize(data, success);
|
||||
}
|
||||
|
||||
QByteArray serialize(const QVariant &data, bool &success)
|
||||
{
|
||||
QByteArray str;
|
||||
success = true;
|
||||
|
||||
if(!data.isValid()) // invalid or null?
|
||||
{
|
||||
str = "null";
|
||||
}
|
||||
else if((data.type() == QVariant::List) || (data.type() == QVariant::StringList)) // variant is a list?
|
||||
{
|
||||
QList<QByteArray> values;
|
||||
const QVariantList list = data.toList();
|
||||
Q_FOREACH(const QVariant& v, list)
|
||||
{
|
||||
QByteArray serializedValue = serialize(v);
|
||||
if(serializedValue.isNull())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
values << serializedValue;
|
||||
}
|
||||
|
||||
str = "[ " + join( values, ", " ) + " ]";
|
||||
}
|
||||
else if(data.type() == QVariant::Hash) // variant is a hash?
|
||||
{
|
||||
const QVariantHash vhash = data.toHash();
|
||||
QHashIterator<QString, QVariant> it( vhash );
|
||||
str = "{ ";
|
||||
QList<QByteArray> pairs;
|
||||
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
QByteArray serializedValue = serialize(it.value());
|
||||
|
||||
if(serializedValue.isNull())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue;
|
||||
}
|
||||
|
||||
str += join(pairs, ", ");
|
||||
str += " }";
|
||||
}
|
||||
else if(data.type() == QVariant::Map) // variant is a map?
|
||||
{
|
||||
const QVariantMap vmap = data.toMap();
|
||||
QMapIterator<QString, QVariant> it( vmap );
|
||||
str = "{ ";
|
||||
QList<QByteArray> pairs;
|
||||
while(it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
QByteArray serializedValue = serialize(it.value());
|
||||
if(serializedValue.isNull())
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
pairs << sanitizeString(it.key()).toUtf8() + " : " + serializedValue;
|
||||
}
|
||||
str += join(pairs, ", ");
|
||||
str += " }";
|
||||
}
|
||||
else if((data.type() == QVariant::String) || (data.type() == QVariant::ByteArray)) // a string or a byte array?
|
||||
{
|
||||
str = sanitizeString(data.toString()).toUtf8();
|
||||
}
|
||||
else if(data.type() == QVariant::Double) // double?
|
||||
{
|
||||
str = QByteArray::number(data.toDouble(), 'g', 20);
|
||||
if(!str.contains(".") && ! str.contains("e"))
|
||||
{
|
||||
str += ".0";
|
||||
}
|
||||
}
|
||||
else if (data.type() == QVariant::Bool) // boolean value?
|
||||
{
|
||||
str = data.toBool() ? "true" : "false";
|
||||
}
|
||||
else if (data.type() == QVariant::ULongLong) // large unsigned number?
|
||||
{
|
||||
str = QByteArray::number(data.value<qulonglong>());
|
||||
}
|
||||
else if ( data.canConvert<qlonglong>() ) // any signed number?
|
||||
{
|
||||
str = QByteArray::number(data.value<qlonglong>());
|
||||
}
|
||||
else if (data.canConvert<long>())
|
||||
{
|
||||
str = QString::number(data.value<long>()).toUtf8();
|
||||
}
|
||||
else if (data.canConvert<QString>()) // can value be converted to string?
|
||||
{
|
||||
// this will catch QDate, QDateTime, QUrl, ...
|
||||
str = sanitizeString(data.toString()).toUtf8();
|
||||
}
|
||||
else
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***** private *****/
|
||||
|
||||
|
||||
/**
|
||||
* \enum JsonToken
|
||||
*/
|
||||
enum JsonToken
|
||||
{
|
||||
JsonTokenNone = 0,
|
||||
JsonTokenCurlyOpen = 1,
|
||||
JsonTokenCurlyClose = 2,
|
||||
JsonTokenSquaredOpen = 3,
|
||||
JsonTokenSquaredClose = 4,
|
||||
JsonTokenColon = 5,
|
||||
JsonTokenComma = 6,
|
||||
JsonTokenString = 7,
|
||||
JsonTokenNumber = 8,
|
||||
JsonTokenTrue = 9,
|
||||
JsonTokenFalse = 10,
|
||||
JsonTokenNull = 11
|
||||
};
|
||||
|
||||
static QString sanitizeString(QString str)
|
||||
{
|
||||
str.replace(QLatin1String("\\"), QLatin1String("\\\\"));
|
||||
str.replace(QLatin1String("\""), QLatin1String("\\\""));
|
||||
str.replace(QLatin1String("\b"), QLatin1String("\\b"));
|
||||
str.replace(QLatin1String("\f"), QLatin1String("\\f"));
|
||||
str.replace(QLatin1String("\n"), QLatin1String("\\n"));
|
||||
str.replace(QLatin1String("\r"), QLatin1String("\\r"));
|
||||
str.replace(QLatin1String("\t"), QLatin1String("\\t"));
|
||||
return QString(QLatin1String("\"%1\"")).arg(str);
|
||||
}
|
||||
|
||||
static QByteArray join(const QList<QByteArray> &list, const QByteArray &sep)
|
||||
{
|
||||
QByteArray res;
|
||||
Q_FOREACH(const QByteArray &i, list)
|
||||
{
|
||||
if(!res.isEmpty())
|
||||
{
|
||||
res += sep;
|
||||
}
|
||||
res += i;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* parseValue
|
||||
*/
|
||||
static QVariant parseValue(const QString &json, int &index, bool &success)
|
||||
{
|
||||
//Determine what kind of data we should parse by
|
||||
//checking out the upcoming token
|
||||
switch(lookAhead(json, index))
|
||||
{
|
||||
case JsonTokenString:
|
||||
return parseString(json, index, success);
|
||||
case JsonTokenNumber:
|
||||
return parseNumber(json, index);
|
||||
case JsonTokenCurlyOpen:
|
||||
return parseObject(json, index, success);
|
||||
case JsonTokenSquaredOpen:
|
||||
return parseArray(json, index, success);
|
||||
case JsonTokenTrue:
|
||||
nextToken(json, index);
|
||||
return QVariant(true);
|
||||
case JsonTokenFalse:
|
||||
nextToken(json, index);
|
||||
return QVariant(false);
|
||||
case JsonTokenNull:
|
||||
nextToken(json, index);
|
||||
return QVariant();
|
||||
case JsonTokenNone:
|
||||
break;
|
||||
}
|
||||
|
||||
//If there were no tokens, flag the failure and return an empty QVariant
|
||||
success = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
/**
|
||||
* parseObject
|
||||
*/
|
||||
static QVariant parseObject(const QString &json, int &index, bool &success)
|
||||
{
|
||||
QVariantMap map;
|
||||
int token;
|
||||
|
||||
//Get rid of the whitespace and increment index
|
||||
nextToken(json, index);
|
||||
|
||||
//Loop through all of the key/value pairs of the object
|
||||
bool done = false;
|
||||
while(!done)
|
||||
{
|
||||
//Get the upcoming token
|
||||
token = lookAhead(json, index);
|
||||
|
||||
if(token == JsonTokenNone)
|
||||
{
|
||||
success = false;
|
||||
return QVariantMap();
|
||||
}
|
||||
else if(token == JsonTokenComma)
|
||||
{
|
||||
nextToken(json, index);
|
||||
}
|
||||
else if(token == JsonTokenCurlyClose)
|
||||
{
|
||||
nextToken(json, index);
|
||||
return map;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Parse the key/value pair's name
|
||||
QString name = parseString(json, index, success).toString();
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
//Get the next token
|
||||
token = nextToken(json, index);
|
||||
|
||||
//If the next token is not a colon, flag the failure
|
||||
//return an empty QVariant
|
||||
if(token != JsonTokenColon)
|
||||
{
|
||||
success = false;
|
||||
return QVariant(QVariantMap());
|
||||
}
|
||||
|
||||
//Parse the key/value pair's value
|
||||
QVariant value = parseValue(json, index, success);
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
//Assign the value to the key in the map
|
||||
map[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
//Return the map successfully
|
||||
return QVariant(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseArray
|
||||
*/
|
||||
static QVariant parseArray(const QString &json, int &index, bool &success)
|
||||
{
|
||||
QVariantList list;
|
||||
|
||||
nextToken(json, index);
|
||||
|
||||
bool done = false;
|
||||
while(!done)
|
||||
{
|
||||
int token = lookAhead(json, index);
|
||||
|
||||
if(token == JsonTokenNone)
|
||||
{
|
||||
success = false;
|
||||
return QVariantList();
|
||||
}
|
||||
else if(token == JsonTokenComma)
|
||||
{
|
||||
nextToken(json, index);
|
||||
}
|
||||
else if(token == JsonTokenSquaredClose)
|
||||
{
|
||||
nextToken(json, index);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
QVariant value = parseValue(json, index, success);
|
||||
|
||||
if(!success)
|
||||
{
|
||||
return QVariantList();
|
||||
}
|
||||
|
||||
list.push_back(value);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseString
|
||||
*/
|
||||
static QVariant parseString(const QString &json, int &index, bool &success)
|
||||
{
|
||||
QString s;
|
||||
QChar c;
|
||||
|
||||
eatWhitespace(json, index);
|
||||
|
||||
c = json[index++];
|
||||
|
||||
bool complete = false;
|
||||
while(!complete)
|
||||
{
|
||||
if(index == json.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
c = json[index++];
|
||||
|
||||
if(c == '\"')
|
||||
{
|
||||
complete = true;
|
||||
break;
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
if(index == json.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
c = json[index++];
|
||||
|
||||
if(c == '\"')
|
||||
{
|
||||
s.append('\"');
|
||||
}
|
||||
else if(c == '\\')
|
||||
{
|
||||
s.append('\\');
|
||||
}
|
||||
else if(c == '/')
|
||||
{
|
||||
s.append('/');
|
||||
}
|
||||
else if(c == 'b')
|
||||
{
|
||||
s.append('\b');
|
||||
}
|
||||
else if(c == 'f')
|
||||
{
|
||||
s.append('\f');
|
||||
}
|
||||
else if(c == 'n')
|
||||
{
|
||||
s.append('\n');
|
||||
}
|
||||
else if(c == 'r')
|
||||
{
|
||||
s.append('\r');
|
||||
}
|
||||
else if(c == 't')
|
||||
{
|
||||
s.append('\t');
|
||||
}
|
||||
else if(c == 'u')
|
||||
{
|
||||
int remainingLength = json.size() - index;
|
||||
|
||||
if(remainingLength >= 4)
|
||||
{
|
||||
QString unicodeStr = json.mid(index, 4);
|
||||
|
||||
int symbol = unicodeStr.toInt(0, 16);
|
||||
|
||||
s.append(QChar(symbol));
|
||||
|
||||
index += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if(!complete)
|
||||
{
|
||||
success = false;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return QVariant(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* parseNumber
|
||||
*/
|
||||
static QVariant parseNumber(const QString &json, int &index)
|
||||
{
|
||||
eatWhitespace(json, index);
|
||||
|
||||
int lastIndex = lastIndexOfNumber(json, index);
|
||||
int charLength = (lastIndex - index) + 1;
|
||||
QString numberStr;
|
||||
|
||||
numberStr = json.mid(index, charLength);
|
||||
|
||||
index = lastIndex + 1;
|
||||
|
||||
if (numberStr.contains('.')) {
|
||||
return QVariant(numberStr.toDouble(NULL));
|
||||
} else if (numberStr.startsWith('-')) {
|
||||
return QVariant(numberStr.toLongLong(NULL));
|
||||
} else {
|
||||
return QVariant(numberStr.toULongLong(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lastIndexOfNumber
|
||||
*/
|
||||
static int lastIndexOfNumber(const QString &json, int index)
|
||||
{
|
||||
int lastIndex;
|
||||
|
||||
for(lastIndex = index; lastIndex < json.size(); lastIndex++)
|
||||
{
|
||||
if(QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lastIndex -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* eatWhitespace
|
||||
*/
|
||||
static void eatWhitespace(const QString &json, int &index)
|
||||
{
|
||||
for(; index < json.size(); index++)
|
||||
{
|
||||
if(QString(" \t\n\r").indexOf(json[index]) == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lookAhead
|
||||
*/
|
||||
static int lookAhead(const QString &json, int index)
|
||||
{
|
||||
int saveIndex = index;
|
||||
return nextToken(json, saveIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* nextToken
|
||||
*/
|
||||
static int nextToken(const QString &json, int &index)
|
||||
{
|
||||
eatWhitespace(json, index);
|
||||
|
||||
if(index == json.size())
|
||||
{
|
||||
return JsonTokenNone;
|
||||
}
|
||||
|
||||
QChar c = json[index];
|
||||
index++;
|
||||
switch(c.toLatin1())
|
||||
{
|
||||
case '{': return JsonTokenCurlyOpen;
|
||||
case '}': return JsonTokenCurlyClose;
|
||||
case '[': return JsonTokenSquaredOpen;
|
||||
case ']': return JsonTokenSquaredClose;
|
||||
case ',': return JsonTokenComma;
|
||||
case '"': return JsonTokenString;
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
case '-': return JsonTokenNumber;
|
||||
case ':': return JsonTokenColon;
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
int remainingLength = json.size() - index;
|
||||
|
||||
//True
|
||||
if(remainingLength >= 4)
|
||||
{
|
||||
if (json[index] == 't' && json[index + 1] == 'r' &&
|
||||
json[index + 2] == 'u' && json[index + 3] == 'e')
|
||||
{
|
||||
index += 4;
|
||||
return JsonTokenTrue;
|
||||
}
|
||||
}
|
||||
|
||||
//False
|
||||
if (remainingLength >= 5)
|
||||
{
|
||||
if (json[index] == 'f' && json[index + 1] == 'a' &&
|
||||
json[index + 2] == 'l' && json[index + 3] == 's' &&
|
||||
json[index + 4] == 'e')
|
||||
{
|
||||
index += 5;
|
||||
return JsonTokenFalse;
|
||||
}
|
||||
}
|
||||
|
||||
//Null
|
||||
if (remainingLength >= 4)
|
||||
{
|
||||
if (json[index] == 'n' && json[index + 1] == 'u' &&
|
||||
json[index + 2] == 'l' && json[index + 3] == 'l')
|
||||
{
|
||||
index += 4;
|
||||
return JsonTokenNull;
|
||||
}
|
||||
}
|
||||
|
||||
return JsonTokenNone;
|
||||
}
|
||||
|
||||
|
||||
} //end namespace
|
||||
85
src/3rdparty/qjson/json.h
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/* Copyright 2011 Eeli Reilin. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL EELI REILIN OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||||
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation
|
||||
* are those of the authors and should not be interpreted as representing
|
||||
* official policies, either expressed or implied, of Eeli Reilin.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file json.h
|
||||
*/
|
||||
|
||||
#ifndef JSON_H
|
||||
#define JSON_H
|
||||
|
||||
#include <QVariant>
|
||||
#include <QString>
|
||||
|
||||
|
||||
/**
|
||||
* \namespace QtJson
|
||||
* \brief A JSON data parser
|
||||
*
|
||||
* Json parses a JSON data into a QVariant hierarchy.
|
||||
*/
|
||||
namespace QtJson
|
||||
{
|
||||
|
||||
/**
|
||||
* Parse a JSON string
|
||||
*
|
||||
* \param json The JSON data
|
||||
*/
|
||||
QVariant parse(const QString &json);
|
||||
|
||||
/**
|
||||
* Parse a JSON string
|
||||
*
|
||||
* \param json The JSON data
|
||||
* \param success The success of the parsing
|
||||
*/
|
||||
QVariant parse(const QString &json, bool &success);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
*
|
||||
* \return QByteArray Textual JSON representation
|
||||
*/
|
||||
QByteArray serialize(const QVariant &data);
|
||||
|
||||
/**
|
||||
* This method generates a textual JSON representation
|
||||
*
|
||||
* \param data The JSON data generated by the parser.
|
||||
* \param success The success of the serialization
|
||||
*
|
||||
* \return QByteArray Textual JSON representation
|
||||
*/
|
||||
QByteArray serialize(const QVariant &data, bool &success);
|
||||
|
||||
} //end namespace
|
||||
|
||||
#endif //JSON_H
|
||||
@@ -61,11 +61,13 @@ QtSingleApplication::QtSingleApplication(const QString &appId, int &argc, char *
|
||||
}
|
||||
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
QtSingleApplication::QtSingleApplication(int &argc, char **argv, Type type)
|
||||
: QApplication(argc, argv, type)
|
||||
{
|
||||
sysInit();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(Q_WS_X11)
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
**
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _SHAREDTOOLS_SINGLEAPPLICATION
|
||||
#define _SHAREDTOOLS_SINGLEAPPLICATION
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
namespace SharedTools {
|
||||
@@ -41,7 +44,9 @@ class QtSingleApplication : public QApplication
|
||||
public:
|
||||
QtSingleApplication(int &argc, char **argv, bool GUIenabled = true);
|
||||
QtSingleApplication(const QString &id, int &argc, char **argv);
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
|
||||
QtSingleApplication(int &argc, char **argv, Type type);
|
||||
#endif
|
||||
#if defined(Q_WS_X11)
|
||||
explicit QtSingleApplication(Display *dpy, Qt::HANDLE visual = 0, Qt::HANDLE colormap = 0);
|
||||
QtSingleApplication(Display *dpy, int &argc, char **argv, Qt::HANDLE visual = 0, Qt::HANDLE cmap = 0);
|
||||
@@ -83,3 +88,4 @@ private:
|
||||
};
|
||||
|
||||
} // namespace SharedTools
|
||||
#endif // _SHAREDTOOLS_SINGLEAPPLICATION
|
||||
|
||||
@@ -1,34 +1,18 @@
|
||||
include(${QT_USE_FILE})
|
||||
#add_subdirectory(integration)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ../mirall.qrc)
|
||||
qt_add_resources(MIRALL_RC_SRC ../mirall.qrc)
|
||||
if ( IS_DIRECTORY ${OEM_THEME_DIR} )
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
qt_add_resources(MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
set(theme_dir ${OEM_THEME_DIR}/theme)
|
||||
else()
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ../theme.qrc)
|
||||
qt_add_resources(MIRALL_RC_SRC ../theme.qrc)
|
||||
set(theme_dir ${CMAKE_CURRENT_SOURCE_DIR}/../theme)
|
||||
endif()
|
||||
|
||||
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
|
||||
|
||||
set(mirall_UI
|
||||
mirall/folderwizardsourcepage.ui
|
||||
mirall/folderwizardtargetpage.ui
|
||||
mirall/sslerrordialog.ui
|
||||
mirall/settingsdialog.ui
|
||||
mirall/generalsettings.ui
|
||||
mirall/networksettings.ui
|
||||
mirall/accountsettings.ui
|
||||
mirall/ignorelisteditor.ui
|
||||
mirall/itemprogressdialog.ui
|
||||
wizard/owncloudsetupnocredspage.ui
|
||||
wizard/owncloudhttpcredspage.ui
|
||||
wizard/owncloudwizardresultpage.ui
|
||||
wizard/owncloudadvancedsetuppage.ui
|
||||
)
|
||||
|
||||
set(3rdparty_SRC
|
||||
3rdparty/qtsingleapplication/qtsingleapplication.cpp
|
||||
3rdparty/qtsingleapplication/qtlocalpeer.cpp
|
||||
@@ -45,9 +29,10 @@ set(3rdparty_HEADER
|
||||
3rdparty/fancylineedit/fancylineedit.h
|
||||
3rdparty/QProgressIndicator/QProgressIndicator.h
|
||||
)
|
||||
qt4_wrap_cpp(3rdparty_MOC ${3rdparty_HEADER})
|
||||
|
||||
if(NOT WIN32)
|
||||
qt_wrap_cpp(3rdparty_MOC ${3rdparty_HEADER})
|
||||
|
||||
if(NOT WIN32)
|
||||
list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
|
||||
else()
|
||||
list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_win.cpp )
|
||||
@@ -58,31 +43,34 @@ set(3rdparty_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtsingleapplication
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QProgressIndicator
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/fancylineedit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/csync
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qjson
|
||||
)
|
||||
|
||||
qt4_wrap_ui(mirall_UI_SRCS ${mirall_UI})
|
||||
|
||||
set(libsync_SRCS
|
||||
mirall/folderman.cpp
|
||||
mirall/folder.cpp
|
||||
mirall/folderwatcher.cpp
|
||||
mirall/syncresult.cpp
|
||||
mirall/networklocation.cpp
|
||||
mirall/mirallconfigfile.cpp
|
||||
mirall/csyncthread.cpp
|
||||
mirall/owncloudpropagator.cpp
|
||||
mirall/syncjournalfilerecord.cpp
|
||||
mirall/syncjournaldb.cpp
|
||||
mirall/fileutils.cpp
|
||||
mirall/theme.cpp
|
||||
mirall/owncloudtheme.cpp
|
||||
mirall/owncloudinfo.cpp
|
||||
mirall/logger.cpp
|
||||
mirall/utility.cpp
|
||||
mirall/connectionvalidator.cpp
|
||||
mirall/progressdispatcher.cpp
|
||||
mirall/mirallaccessmanager.cpp
|
||||
mirall/networkjobs.cpp
|
||||
mirall/account.cpp
|
||||
mirall/quotainfo.cpp
|
||||
creds/dummycredentials.cpp
|
||||
creds/httpcredentials.cpp
|
||||
creds/credentialsfactory.cpp
|
||||
creds/http/credentialstore.cpp
|
||||
creds/http/httpconfigfile.cpp
|
||||
creds/shibbolethcredentials.cpp
|
||||
creds/shibboleth/shibbolethaccessmanager.cpp
|
||||
@@ -91,6 +79,7 @@ set(libsync_SRCS
|
||||
creds/shibboleth/shibbolethrefresher.cpp
|
||||
creds/shibboleth/shibbolethconfigfile.cpp
|
||||
creds/credentialscommon.cpp
|
||||
3rdparty/qjson/json.cpp
|
||||
)
|
||||
|
||||
set(libsync_HEADERS
|
||||
@@ -98,18 +87,21 @@ set(libsync_HEADERS
|
||||
mirall/folder.h
|
||||
mirall/folderwatcher.h
|
||||
mirall/csyncthread.h
|
||||
mirall/owncloudpropagator.h
|
||||
mirall/syncjournaldb.h
|
||||
mirall/theme.h
|
||||
mirall/owncloudtheme.h
|
||||
mirall/owncloudinfo.h
|
||||
mirall/logger.h
|
||||
mirall/connectionvalidator.h
|
||||
mirall/progressdispatcher.h
|
||||
mirall/mirallaccessmanager.h
|
||||
mirall/networkjobs.h
|
||||
mirall/account.h
|
||||
mirall/quotainfo.h
|
||||
creds/abstractcredentials.h
|
||||
creds/dummycredentials.h
|
||||
creds/httpcredentials.h
|
||||
creds/credentialsfactory.h
|
||||
creds/http/credentialstore.h
|
||||
creds/http/httpconfigfile.h
|
||||
creds/shibbolethcredentials.h
|
||||
creds/shibboleth/shibbolethaccessmanager.h
|
||||
@@ -118,6 +110,7 @@ set(libsync_HEADERS
|
||||
creds/shibboleth/shibbolethrefresher.h
|
||||
creds/shibboleth/shibbolethconfigfile.h
|
||||
creds/credentialscommon.h
|
||||
3rdparty/qjson/json.h
|
||||
)
|
||||
|
||||
IF( INOTIFY_FOUND )
|
||||
@@ -126,7 +119,7 @@ IF( INOTIFY_FOUND )
|
||||
set(libsync_HEADERS ${libsync_HEADERS} mirall/inotify.h)
|
||||
set(libsync_HEADERS ${libsync_HEADERS} mirall/folderwatcher_inotify.h)
|
||||
ENDIF()
|
||||
IF( WIN32 )
|
||||
IF( WIN32 )
|
||||
set(libsync_SRCS ${libsync_SRCS} mirall/folderwatcher_win.cpp)
|
||||
set(libsync_HEADERS ${libsync_HEADERS} mirall/folderwatcher_win.h)
|
||||
ENDIF()
|
||||
@@ -135,23 +128,75 @@ IF( APPLE )
|
||||
ENDIF()
|
||||
|
||||
|
||||
qt4_wrap_cpp(syncMoc ${libsync_HEADERS})
|
||||
qt_wrap_cpp(syncMoc ${libsync_HEADERS})
|
||||
|
||||
# These headers are installed for libowncloudsync to be used by 3rd party apps
|
||||
set(owncloudsync_HEADERS
|
||||
mirall/account.h
|
||||
mirall/csyncthread.h
|
||||
mirall/folder.h
|
||||
mirall/folderman.h
|
||||
mirall/folderwatcher.h
|
||||
mirall/mirallconfigfile.h
|
||||
mirall/networkjobs.h
|
||||
mirall/progressdispatcher.h
|
||||
mirall/syncfileitem.h
|
||||
mirall/syncjournaldb.h
|
||||
mirall/syncresult.h
|
||||
)
|
||||
|
||||
set(creds_HEADERS
|
||||
creds/abstractcredentials.h
|
||||
creds/httpcredentials.h
|
||||
)
|
||||
|
||||
IF (NOT APPLE)
|
||||
INSTALL(
|
||||
FILES ${owncloudsync_HEADERS}
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/owncloudsync/mirall
|
||||
)
|
||||
INSTALL(
|
||||
FILES ${creds_HEADERS}
|
||||
DESTINATION ${INCLUDE_INSTALL_DIR}/owncloudsync/creds
|
||||
)
|
||||
ENDIF(NOT APPLE)
|
||||
|
||||
IF( DEFINED CSYNC_BUILD_PATH )
|
||||
SET(HTTPBF_LIBRARY ${CSYNC_BUILD_PATH}/src/httpbf/libhttpbf.a)
|
||||
ELSE()
|
||||
FIND_LIBRARY(HTTPBF_LIBRARY NAMES httpbf HINTS $ENV{CSYNC_DIR})
|
||||
ENDIF()
|
||||
|
||||
|
||||
list(APPEND libsync_LINK_TARGETS
|
||||
${QT_LIBRARIES}
|
||||
${CSYNC_LIBRARY}
|
||||
dl
|
||||
${HTTPBF_LIBRARY}
|
||||
)
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
list(APPEND libsync_LINK_TARGETS
|
||||
inotify
|
||||
)
|
||||
ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
|
||||
if(QTKEYCHAIN_FOUND)
|
||||
list(APPEND libsync_LINK_TARGETS ${QTKEYCHAIN_LIBRARY})
|
||||
include_directories(${QTKEYCHAIN_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if(NEON_FOUND)
|
||||
list(APPEND libsync_LINK_TARGETS ${NEON_LIBRARIES})
|
||||
include_directories(${NEON_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
||||
|
||||
qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets Sql)
|
||||
|
||||
set_target_properties( ${synclib_NAME} PROPERTIES
|
||||
VERSION ${VERSION}
|
||||
SOVERSION ${SOVERSION}
|
||||
VERSION ${MIRALL_VERSION}
|
||||
SOVERSION ${MIRALL_SOVERSION}
|
||||
)
|
||||
|
||||
target_link_libraries(${synclib_NAME} ${libsync_LINK_TARGETS} )
|
||||
@@ -175,11 +220,30 @@ else()
|
||||
install(TARGETS ${synclib_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
|
||||
endif()
|
||||
|
||||
set(mirall_UI
|
||||
mirall/folderwizardsourcepage.ui
|
||||
mirall/folderwizardtargetpage.ui
|
||||
mirall/sslerrordialog.ui
|
||||
mirall/settingsdialog.ui
|
||||
mirall/generalsettings.ui
|
||||
mirall/networksettings.ui
|
||||
mirall/accountsettings.ui
|
||||
mirall/ignorelisteditor.ui
|
||||
mirall/protocolwidget.ui
|
||||
wizard/owncloudsetupnocredspage.ui
|
||||
wizard/owncloudhttpcredspage.ui
|
||||
wizard/owncloudwizardresultpage.ui
|
||||
wizard/owncloudadvancedsetuppage.ui
|
||||
)
|
||||
|
||||
qt_wrap_ui(mirall_UI_SRCS ${mirall_UI})
|
||||
|
||||
set(mirall_SRCS
|
||||
mirall/application.cpp
|
||||
mirall/systray.cpp
|
||||
mirall/folderwizard.cpp
|
||||
mirall/folderstatusmodel.cpp
|
||||
mirall/protocolwidget.cpp
|
||||
wizard/owncloudwizard.cpp
|
||||
wizard/owncloudsetuppage.cpp
|
||||
wizard/owncloudhttpcredspage.cpp
|
||||
@@ -197,7 +261,8 @@ set(mirall_SRCS
|
||||
mirall/networksettings.cpp
|
||||
mirall/accountsettings.cpp
|
||||
mirall/ignorelisteditor.cpp
|
||||
mirall/itemprogressdialog.cpp
|
||||
mirall/owncloudgui.cpp
|
||||
mirall/socketapi.cpp
|
||||
)
|
||||
|
||||
set(mirall_HEADERS
|
||||
@@ -221,7 +286,9 @@ set(mirall_HEADERS
|
||||
mirall/networksettings.h
|
||||
mirall/accountsettings.h
|
||||
mirall/ignorelisteditor.h
|
||||
mirall/itemprogressdialog.h
|
||||
mirall/protocolwidget.h
|
||||
mirall/owncloudgui.h
|
||||
mirall/socketapi.h
|
||||
)
|
||||
|
||||
if( UNIX AND NOT APPLE)
|
||||
@@ -231,12 +298,12 @@ if( UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
# csync is required.
|
||||
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CSYNC_BUILD_PATH}/src)
|
||||
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CSYNC_INCLUDE_DIR}/httpbf/src ${CSYNC_BUILD_PATH}/src)
|
||||
include_directories(${3rdparty_INC})
|
||||
|
||||
qt4_wrap_cpp(mirallMoc ${mirall_HEADERS})
|
||||
qt_wrap_cpp(mirallMoc ${mirall_HEADERS})
|
||||
|
||||
qt4_add_translation(mirall_I18N ${TRANSLATIONS})
|
||||
qt_add_translation(mirall_I18N ${TRANSLATIONS})
|
||||
|
||||
set( final_src
|
||||
${mirall_HEADERS}
|
||||
@@ -254,19 +321,31 @@ set( final_src
|
||||
include( AddAppIconMacro )
|
||||
set(ownCloud_old ${ownCloud})
|
||||
|
||||
kde4_add_app_icon( ownCloud "${theme_dir}/colored/${APPLICATION_EXECUTABLE}-icon*.png")
|
||||
# set an icon_app_name. For historical reasons we can not use the
|
||||
# application_shortname for ownCloud but must rather set it manually.
|
||||
if ( EXISTS ${OEM_THEME_DIR}/OEM.cmake )
|
||||
set(ICON_APP_NAME ${APPLICATION_SHORTNAME})
|
||||
else()
|
||||
set(ICON_APP_NAME "owncloud")
|
||||
endif()
|
||||
|
||||
kde4_add_app_icon( ownCloud "${theme_dir}/colored/${ICON_APP_NAME}-icon*.png")
|
||||
list(APPEND final_src ${ownCloud})
|
||||
set(ownCloud ${ownCloud_old})
|
||||
|
||||
if (WITH_DBUS)
|
||||
set(ADDITIONAL_APP_MODULES DBus)
|
||||
endif(WITH_DBUS)
|
||||
|
||||
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
if(NOT WIN32)
|
||||
file( GLOB _icons "${theme_dir}/colored/${APPLICATION_EXECUTABLE}-icon-*.png" )
|
||||
file( GLOB _icons "${theme_dir}/colored/${ICON_APP_NAME}-icon-*.png" )
|
||||
foreach( _file ${_icons} )
|
||||
string( REPLACE "${theme_dir}/colored/${APPLICATION_EXECUTABLE}-icon-" "" _res ${_file} )
|
||||
string( REPLACE "${theme_dir}/colored/${ICON_APP_NAME}-icon-" "" _res ${_file} )
|
||||
string( REPLACE ".png" "" _res ${_res} )
|
||||
install( FILES ${_file} RENAME ${APPLICATION_EXECUTABLE}.png DESTINATION ${DATADIR}/icons/hicolor/${_res}x${_res}/apps )
|
||||
install( FILES ${_file} RENAME ${ICON_APP_NAME}.png DESTINATION ${DATADIR}/icons/hicolor/${_res}x${_res}/apps )
|
||||
endforeach( _file )
|
||||
endif(NOT WIN32)
|
||||
|
||||
@@ -276,7 +355,7 @@ endif(NOT WIN32)
|
||||
|
||||
# add_executable( ${APPLICATION_EXECUTABLE} main.cpp ${final_src})
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
|
||||
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
||||
else()
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
include(DeployQt4)
|
||||
@@ -286,13 +365,7 @@ else()
|
||||
|
||||
# we must add MACOSX_BUNDLE only if building a bundle
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
|
||||
|
||||
#FIXME: hardcoded path
|
||||
if ( EXISTS ${CSYNC_BINARY_DIR}/modules/ocsync_owncloud.so )
|
||||
install(FILES ${CSYNC_BINARY_DIR}/modules/ocsync_owncloud.so DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins)
|
||||
else()
|
||||
install(FILES /usr/local/lib/ocsync-0/ocsync_owncloud.so DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins)
|
||||
endif()
|
||||
qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
|
||||
|
||||
set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
|
||||
install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
|
||||
@@ -300,7 +373,6 @@ else()
|
||||
install(FILES ${qt_I18N} DESTINATION ${QM_DIR})
|
||||
file(GLOB qtkeychain_I18N ${QT_TRANSLATIONS_DIR}/qtkeychain*.qm)
|
||||
install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
|
||||
list(APPEND dirs "/usr/local/lib")
|
||||
endif()
|
||||
|
||||
|
||||
@@ -322,7 +394,7 @@ install(TARGETS ${APPLICATION_EXECUTABLE}
|
||||
# currently it needs to be done because the code right above needs to be executed no matter
|
||||
# if building a bundle or not and the install_qt4_executable needs to be called afterwards
|
||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "" "${OWNCLOUD_OSX_BUNDLE}/Contents/Plugins/ocsync_owncloud.so" ${dirs})
|
||||
install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite")
|
||||
endif()
|
||||
|
||||
find_program(KRAZY2_EXECUTABLE krazy2)
|
||||
@@ -335,3 +407,17 @@ if(KRAZY2_EXECUTABLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
set(owncloudcmd_NAME ${APPLICATION_EXECUTABLE}cmd)
|
||||
set(OWNCLOUDCMD_SRC owncloudcmd/owncloudcmd.cpp)
|
||||
add_executable(${owncloudcmd_NAME} ${OWNCLOUDCMD_SRC})
|
||||
qt5_use_modules(${owncloudcmd_NAME} Network Sql)
|
||||
set_target_properties(${owncloudcmd_NAME} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
target_link_libraries(${owncloudcmd_NAME} ${synclib_NAME})
|
||||
target_link_libraries(${owncloudcmd_NAME} ${CSYNC_LIBRARY})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mirall)
|
||||
install(TARGETS ${owncloudcmd_NAME}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
@@ -19,27 +19,34 @@
|
||||
#include <csync.h>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
|
||||
class QNetworkReply;
|
||||
namespace Mirall
|
||||
{
|
||||
class Account;
|
||||
|
||||
class AbstractCredentials : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
// No need for virtual destructor - QObject already has one.
|
||||
virtual void syncContextPreInit(CSYNC* ctx) = 0;
|
||||
virtual void syncContextPreStart(CSYNC* ctx) = 0;
|
||||
virtual bool changed(AbstractCredentials* credentials) const = 0;
|
||||
virtual QString authType() const = 0;
|
||||
virtual QNetworkAccessManager* getQNAM() const = 0;
|
||||
virtual bool ready() const = 0;
|
||||
virtual void fetch() = 0;
|
||||
virtual void persistForUrl(const QString& url) = 0;
|
||||
// No need for virtual destructor - QObject already has one.
|
||||
virtual void syncContextPreInit(CSYNC* ctx) = 0;
|
||||
virtual void syncContextPreStart(CSYNC* ctx) = 0;
|
||||
virtual bool changed(AbstractCredentials* credentials) const = 0;
|
||||
virtual QString authType() const = 0;
|
||||
virtual QString user() const = 0;
|
||||
virtual QNetworkAccessManager* getQNAM() const = 0;
|
||||
virtual bool ready() const = 0;
|
||||
virtual void fetch(Account *account) = 0;
|
||||
virtual bool stillValid(QNetworkReply *reply) = 0;
|
||||
virtual bool fetchFromUser(Account *account) = 0;
|
||||
virtual void persist(Account *account) = 0;
|
||||
/** Invalidates auth token, or password for basic auth */
|
||||
virtual void invalidateToken(Account *account) = 0;
|
||||
|
||||
|
||||
Q_SIGNALS:
|
||||
void fetched();
|
||||
void fetched();
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
@@ -19,10 +18,12 @@
|
||||
#include <QString>
|
||||
#include <QSslCertificate>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "creds/credentialscommon.h"
|
||||
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
@@ -46,7 +47,7 @@ int handleNeonSSLProblems(const char* prompt,
|
||||
int pos = 0;
|
||||
// This is the set of certificates which QNAM accepted, so we should accept
|
||||
// them as well
|
||||
QList<QSslCertificate> certs = ownCloudInfo::instance()->certificateChain();
|
||||
QList<QSslCertificate> certs = AccountManager::instance()->account()->certificateChain();
|
||||
|
||||
while (!certOk && (pos = regexp.indexIn(qPrompt, 1+pos)) != -1) {
|
||||
QString neon_fingerprint = regexp.cap(1);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
|
||||
@@ -35,6 +35,11 @@ QString DummyCredentials::authType() const
|
||||
return QString::fromLatin1("dummy");
|
||||
}
|
||||
|
||||
QString DummyCredentials::user() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QNetworkAccessManager* DummyCredentials::getQNAM() const
|
||||
{
|
||||
return new MirallAccessManager;
|
||||
@@ -45,12 +50,24 @@ bool DummyCredentials::ready() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void DummyCredentials::fetch()
|
||||
bool DummyCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
Q_UNUSED(reply)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DummyCredentials::fetchFromUser(Account *account)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
return false;
|
||||
}
|
||||
|
||||
void DummyCredentials::fetch(Account*)
|
||||
{
|
||||
Q_EMIT(fetched());
|
||||
}
|
||||
|
||||
void DummyCredentials::persistForUrl(const QString&)
|
||||
void DummyCredentials::persist(Account*)
|
||||
{}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -21,17 +21,21 @@ namespace Mirall
|
||||
|
||||
class DummyCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QString user() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
bool stillValid(QNetworkReply *reply);
|
||||
bool fetchFromUser(Account *account);
|
||||
void fetch(Account*);
|
||||
void persist(Account*);
|
||||
void invalidateToken(Account *) {}
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -1,338 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QtGui>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "creds/http/credentialstore.h"
|
||||
#include "creds/http/httpconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
#include <qtkeychain/keychain.h>
|
||||
using namespace QKeychain;
|
||||
#endif
|
||||
|
||||
#define MAX_LOGIN_ATTEMPTS 3
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
CredentialStore *CredentialStore::_instance=0;
|
||||
CredentialStore::CredState CredentialStore::_state = NotFetched;
|
||||
QString CredentialStore::_passwd = QString::null;
|
||||
QString CredentialStore::_user = QString::null;
|
||||
QString CredentialStore::_url = QString::null;
|
||||
QString CredentialStore::_errorMsg = QString::null;
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
CredentialStore::CredentialType CredentialStore::_type = KeyChain;
|
||||
#else
|
||||
CredentialStore::CredentialType CredentialStore::_type = Settings;
|
||||
#endif
|
||||
|
||||
CredentialStore::CredentialStore(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
CredentialStore *CredentialStore::instance()
|
||||
{
|
||||
if( !CredentialStore::_instance ) CredentialStore::_instance = new CredentialStore;
|
||||
return CredentialStore::_instance;
|
||||
}
|
||||
|
||||
QString CredentialStore::password() const
|
||||
{
|
||||
return _passwd;
|
||||
}
|
||||
QString CredentialStore::user() const
|
||||
{
|
||||
return _user;
|
||||
}
|
||||
|
||||
CredentialStore::CredState CredentialStore::state()
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
void CredentialStore::fetchCredentials()
|
||||
{
|
||||
HttpConfigFile cfgFile;
|
||||
|
||||
bool ok = false;
|
||||
QString pwd;
|
||||
_user = cfgFile.user();
|
||||
_url = cfgFile.ownCloudUrl();
|
||||
|
||||
QString key = keyChainKey(_url);
|
||||
|
||||
if( key.isNull() ) {
|
||||
qDebug() << "Can not fetch credentials, url is zero!";
|
||||
_state = Error;
|
||||
emit( fetchCredentialsFinished(false) );
|
||||
return;
|
||||
}
|
||||
|
||||
switch( _type ) {
|
||||
case CredentialStore::Settings: {
|
||||
/* Read from config file. */
|
||||
_state = Fetching;
|
||||
cfgFile.fixupOldPassword();
|
||||
if( cfgFile.passwordExists() ) {
|
||||
pwd = cfgFile.password();
|
||||
ok = true;
|
||||
} else {
|
||||
ok = false;
|
||||
_state = EntryNotFound;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CredentialStore::KeyChain: {
|
||||
// If the credentials are here already, return.
|
||||
if( _state == Ok || _state == AsyncWriting ) {
|
||||
emit(fetchCredentialsFinished(true));
|
||||
return;
|
||||
}
|
||||
// otherwise fetch asynchronious.
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
_state = AsyncFetching;
|
||||
if( !_user.isEmpty() ) {
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
job->setKey( key );
|
||||
|
||||
connect( job, SIGNAL(finished(QKeychain::Job*)), this,
|
||||
SLOT(slotKeyChainReadFinished(QKeychain::Job*)));
|
||||
job->start();
|
||||
}
|
||||
#else
|
||||
qDebug() << "QtKeyChain: Not yet implemented!";
|
||||
_state = Error;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( _state == Fetching ) { // ...but not AsyncFetching
|
||||
if( ok ) {
|
||||
_passwd = pwd;
|
||||
_state = Ok;
|
||||
}
|
||||
if( !ok && _state == Fetching ) {
|
||||
_state = Error;
|
||||
}
|
||||
|
||||
emit( fetchCredentialsFinished(ok) );
|
||||
} else {
|
||||
// in case of AsyncFetching nothing happens here. The finished-Slot
|
||||
// will emit the finish signal.
|
||||
}
|
||||
}
|
||||
|
||||
void CredentialStore::reset()
|
||||
{
|
||||
_state = NotFetched;
|
||||
_user = QString::null;
|
||||
_passwd = QString::null;
|
||||
}
|
||||
|
||||
QString CredentialStore::keyChainKey( const QString& url ) const
|
||||
{
|
||||
QString u(url);
|
||||
if( u.isEmpty() ) {
|
||||
qDebug() << "Empty url in keyChain, error!";
|
||||
return QString::null;
|
||||
}
|
||||
if( _user.isEmpty() ) {
|
||||
qDebug() << "Error: User is emty!";
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
if( !u.endsWith(QChar('/')) ) {
|
||||
u.append(QChar('/'));
|
||||
}
|
||||
|
||||
QString key = _user+QLatin1Char(':')+u;
|
||||
return key;
|
||||
}
|
||||
|
||||
void CredentialStore::slotKeyChainReadFinished(QKeychain::Job* job)
|
||||
{
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
ReadPasswordJob *pwdJob = static_cast<ReadPasswordJob*>(job);
|
||||
if( pwdJob ) {
|
||||
switch( pwdJob->error() ) {
|
||||
case QKeychain::NoError:
|
||||
_passwd = pwdJob->textData();
|
||||
#ifdef Q_OS_LINUX
|
||||
// Currently there is a bug in the keychain on linux that if no
|
||||
// entry is there, an empty password comes back, but no error.
|
||||
if( _passwd.isEmpty() ) {
|
||||
_state = EntryNotFound;
|
||||
_errorMsg = tr("No password entry found in keychain. Please reconfigure.");
|
||||
} else
|
||||
#endif
|
||||
_state = Ok;
|
||||
break;
|
||||
case QKeychain::EntryNotFound:
|
||||
_state = EntryNotFound;
|
||||
break;
|
||||
case QKeychain::CouldNotDeleteEntry:
|
||||
_state = Error;
|
||||
break;
|
||||
case QKeychain::AccessDenied:
|
||||
_state = AccessDenied;
|
||||
break;
|
||||
case QKeychain::NoBackendAvailable:
|
||||
_state = NoKeychainBackend;
|
||||
break;
|
||||
case QKeychain::NotImplemented:
|
||||
_state = NoKeychainBackend;
|
||||
break;
|
||||
case QKeychain::OtherError:
|
||||
default:
|
||||
_state = Error;
|
||||
|
||||
}
|
||||
/* In case there is no backend, tranparentely switch to Settings file. */
|
||||
if( _state == NoKeychainBackend ) {
|
||||
qDebug() << "No Storage Backend, falling back to Settings mode.";
|
||||
_type = CredentialStore::Settings;
|
||||
fetchCredentials();
|
||||
return;
|
||||
}
|
||||
|
||||
if( _state == EntryNotFound ) {
|
||||
// try to migrate.
|
||||
}
|
||||
|
||||
if( _state != Ok ) {
|
||||
qDebug() << "Error with keychain: " << pwdJob->errorString();
|
||||
if(_errorMsg.isEmpty()) _errorMsg = pwdJob->errorString();
|
||||
} else {
|
||||
_errorMsg = QString::null;
|
||||
}
|
||||
} else {
|
||||
_state = Error;
|
||||
qDebug() << "Error: KeyChain Read Password Job failed!";
|
||||
}
|
||||
emit(fetchCredentialsFinished(_state == Ok));
|
||||
#else
|
||||
(void) job;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString CredentialStore::errorMessage()
|
||||
{
|
||||
return _errorMsg;
|
||||
}
|
||||
|
||||
void CredentialStore::setCredentials( const QString& url, const QString& user,
|
||||
const QString& pwd )
|
||||
{
|
||||
_passwd = pwd;
|
||||
_user = user;
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
_type = KeyChain;
|
||||
#else
|
||||
_type = Settings;
|
||||
#endif
|
||||
_url = url;
|
||||
_state = Ok;
|
||||
}
|
||||
|
||||
void CredentialStore::saveCredentials( )
|
||||
{
|
||||
HttpConfigFile cfgFile;
|
||||
QString key = keyChainKey(_url);
|
||||
if( key.isNull() ) {
|
||||
qDebug() << "Error: Can not save credentials, URL is zero!";
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
#endif
|
||||
|
||||
cfgFile.setUser(_user);
|
||||
switch( _type ) {
|
||||
case CredentialStore::KeyChain: {
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
// Set password in KeyChain
|
||||
job->setKey( key );
|
||||
job->setTextData(_passwd);
|
||||
|
||||
connect( job, SIGNAL(finished(QKeychain::Job*)), this,
|
||||
SLOT(slotKeyChainWriteFinished(QKeychain::Job*)));
|
||||
_state = AsyncWriting;
|
||||
job->start();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case CredentialStore::Settings:
|
||||
cfgFile.setPassword( _passwd );
|
||||
reset();
|
||||
break;
|
||||
default:
|
||||
// unsupported.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CredentialStore::slotKeyChainWriteFinished( QKeychain::Job *job )
|
||||
{
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
WritePasswordJob *pwdJob = static_cast<WritePasswordJob*>(job);
|
||||
if( pwdJob ) {
|
||||
QKeychain::Error err = pwdJob->error();
|
||||
|
||||
if( err != QKeychain::NoError ) {
|
||||
qDebug() << "Error with keychain: " << pwdJob->errorString();
|
||||
if( err == NoBackendAvailable || err == NotImplemented ||
|
||||
pwdJob->errorString().contains(QLatin1String("Could not open wallet"))) {
|
||||
_state = NoKeychainBackend;
|
||||
_type = Settings;
|
||||
saveCredentials();
|
||||
} else {
|
||||
_state = Error;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Successfully stored password for user " << _user;
|
||||
// Try to remove password formerly stored in the config file.
|
||||
HttpConfigFile cfgFile;
|
||||
cfgFile.removePassword();
|
||||
_state = NotFetched;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Error: KeyChain Write Password Job failed!";
|
||||
_state = Error;
|
||||
}
|
||||
#else
|
||||
(void) job;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Called if a user chooses to not store the password locally.
|
||||
void CredentialStore::deleteKeyChainCredential( const QString& key )
|
||||
{
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
// Start the remove job, do not care so much about the result.
|
||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||
job->setKey( key );
|
||||
job->start();
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef CREDENTIALSTORE_H
|
||||
#define CREDENTIALSTORE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QInputDialog>
|
||||
|
||||
namespace QKeychain {
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
/*
|
||||
* This object holds the credential information of the ownCloud connection. It
|
||||
* is implemented as a singleton.
|
||||
* At startup of the client, at first the fetchCredentials() method must be called
|
||||
* which tries to get credentials from one of the supported backends. To determine
|
||||
* which backend should be used, MirallConfigFile::credentialType() is called as
|
||||
* the backend is configured in the config file.
|
||||
*
|
||||
* The fetchCredentials() call changes the internal state of the credential store
|
||||
* to one of
|
||||
* Ok: There are credentials. Note that it's unknown if they are correct!!
|
||||
* Fetching: The fetching is not yet finished.
|
||||
* EntryNotFound: No password entry found in the storage.
|
||||
* Error: A general error happened.
|
||||
* After fetching has finished, signal fetchCredentialsFinished(bool) is emitted.
|
||||
* The result can be retrieved with state() and password() and user() methods.
|
||||
*/
|
||||
|
||||
class CredentialStore : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum CredState { NotFetched = 0,
|
||||
Ok,
|
||||
Fetching,
|
||||
AsyncFetching,
|
||||
EntryNotFound,
|
||||
AccessDenied,
|
||||
NoKeychainBackend,
|
||||
Error,
|
||||
AsyncWriting };
|
||||
|
||||
enum CredentialType {
|
||||
Settings = 0,
|
||||
KeyChain
|
||||
};
|
||||
|
||||
QString password( ) const;
|
||||
QString user( ) const;
|
||||
|
||||
/**
|
||||
* @brief state
|
||||
* @return the state of the Credentialstore.
|
||||
*/
|
||||
CredState state();
|
||||
|
||||
/**
|
||||
* @brief fetchCredentials - start to retrieve user credentials.
|
||||
*
|
||||
* This method must be called first to retrieve the credentials.
|
||||
* At the end, this method emits the fetchKeyChainFinished() signal.
|
||||
*/
|
||||
void fetchCredentials();
|
||||
|
||||
/**
|
||||
* @brief instance - singleton pointer.
|
||||
* @return the singleton pointer to access the object.
|
||||
*/
|
||||
static CredentialStore *instance();
|
||||
|
||||
/**
|
||||
* @brief setCredentials - sets the user credentials.
|
||||
*
|
||||
* This function is called from the setup wizard to set the credentials
|
||||
* int this store. Note that it does not store the password.
|
||||
* The function also sets the state to ok.
|
||||
* @param url - the connection url
|
||||
* @param user - the user name
|
||||
*/
|
||||
void setCredentials( const QString& url, const QString& user, const QString& pwd);
|
||||
|
||||
void saveCredentials( );
|
||||
|
||||
QString errorMessage();
|
||||
|
||||
void reset();
|
||||
signals:
|
||||
/**
|
||||
* @brief fetchCredentialsFinished
|
||||
*
|
||||
* emitted as soon as the fetching of the credentials has finished.
|
||||
* If the parameter is true, there is a password and user. This does
|
||||
* however, not say if the credentials are valid log in data.
|
||||
* If false, the user pressed cancel.
|
||||
*/
|
||||
void fetchCredentialsFinished(bool);
|
||||
|
||||
protected slots:
|
||||
void slotKeyChainReadFinished( QKeychain::Job* );
|
||||
void slotKeyChainWriteFinished( QKeychain::Job* );
|
||||
|
||||
private:
|
||||
explicit CredentialStore(QObject *parent = 0);
|
||||
void deleteKeyChainCredential( const QString& );
|
||||
QString keyChainKey( const QString& ) const;
|
||||
|
||||
static CredentialStore *_instance;
|
||||
static CredState _state;
|
||||
static QString _passwd;
|
||||
static QString _user;
|
||||
static QString _url;
|
||||
static QString _errorMsg;
|
||||
static CredentialType _type;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREDENTIALSTORE_H
|
||||
@@ -1,5 +1,4 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
@@ -15,14 +14,23 @@
|
||||
*/
|
||||
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QSettings>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include "creds/httpcredentials.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include <qtkeychain/keychain.h>
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "creds/http/credentialstore.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
#include "creds/httpcredentials.h"
|
||||
|
||||
using namespace QKeychain;
|
||||
|
||||
Q_DECLARE_METATYPE(Mirall::Account*)
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
@@ -39,8 +47,8 @@ int getauth(const char *prompt,
|
||||
{
|
||||
int re = 0;
|
||||
QMutex mutex;
|
||||
MirallConfigFile cfg;
|
||||
HttpCredentials* http_credentials = dynamic_cast< HttpCredentials* > (cfg.getCredentials());
|
||||
// ### safe?
|
||||
HttpCredentials* http_credentials = qobject_cast<HttpCredentials*>(AccountManager::instance()->account()->credentials());
|
||||
|
||||
if (!http_credentials) {
|
||||
qDebug() << "Not a HTTP creds instance!";
|
||||
@@ -65,20 +73,38 @@ int getauth(const char *prompt,
|
||||
return re;
|
||||
}
|
||||
|
||||
const char userC[] = "user";
|
||||
|
||||
} // ns
|
||||
|
||||
class HttpCredentialsAccessManager : public MirallAccessManager {
|
||||
public:
|
||||
HttpCredentialsAccessManager(const HttpCredentials *cred, QObject* parent = 0)
|
||||
: MirallAccessManager(parent), _cred(cred) {}
|
||||
protected:
|
||||
QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) {
|
||||
QByteArray credHash = QByteArray(_cred->user().toUtf8()+":"+_cred->password().toUtf8()).toBase64();
|
||||
QNetworkRequest req(request);
|
||||
req.setRawHeader(QByteArray("Authorization"), QByteArray("Basic ") + credHash);
|
||||
//qDebug() << "Request for " << req.url() << "with authorization" << QByteArray::fromBase64(credHash);
|
||||
return MirallAccessManager::createRequest(op, req, outgoingData);
|
||||
}
|
||||
private:
|
||||
const HttpCredentials *_cred;
|
||||
};
|
||||
|
||||
HttpCredentials::HttpCredentials()
|
||||
: _user(),
|
||||
_password(),
|
||||
_ready(false),
|
||||
_attempts()
|
||||
_ready(false)
|
||||
{}
|
||||
|
||||
HttpCredentials::HttpCredentials(const QString& user, const QString& password)
|
||||
: _user(user),
|
||||
_password(password),
|
||||
_ready(true)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
void HttpCredentials::syncContextPreInit (CSYNC* ctx)
|
||||
{
|
||||
@@ -91,7 +117,7 @@ void HttpCredentials::syncContextPreStart (CSYNC* ctx)
|
||||
// any way to get "session_key" module property from csync. Had we
|
||||
// have it, then we could remove this code and keep it in
|
||||
// csyncthread code (or folder code, git remembers).
|
||||
QList<QNetworkCookie> cookies(ownCloudInfo::instance()->getLastAuthCookies());
|
||||
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
||||
QString cookiesAsString;
|
||||
|
||||
// Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
||||
@@ -110,7 +136,7 @@ bool HttpCredentials::changed(AbstractCredentials* credentials) const
|
||||
{
|
||||
HttpCredentials* other(dynamic_cast< HttpCredentials* >(credentials));
|
||||
|
||||
if (!other || other->user() != this->user()) {
|
||||
if (!other || (other->user() != this->user())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -134,7 +160,7 @@ QString HttpCredentials::password() const
|
||||
|
||||
QNetworkAccessManager* HttpCredentials::getQNAM() const
|
||||
{
|
||||
MirallAccessManager* qnam = new MirallAccessManager;
|
||||
MirallAccessManager* qnam = new HttpCredentialsAccessManager(this);
|
||||
|
||||
connect( qnam, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
|
||||
this, SLOT(slotAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
@@ -147,72 +173,142 @@ bool HttpCredentials::ready() const
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void HttpCredentials::fetch()
|
||||
void HttpCredentials::fetch(Account *account)
|
||||
{
|
||||
_user = account->credentialSetting(QLatin1String(userC)).toString();
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
// TODO: merge CredentialStore into HttpCredentials?
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
connect(store, SIGNAL(fetchCredentialsFinished(bool)),
|
||||
this, SLOT(slotCredentialsFetched(bool)));
|
||||
store->fetchCredentials();
|
||||
ReadPasswordJob *job = new ReadPasswordJob(Theme::instance()->appName());
|
||||
job->setSettings(account->settingsWithGroup(Theme::instance()->appName()));
|
||||
job->setInsecureFallback(true);
|
||||
job->setKey(keychainKey(account->url().toString(), _user));
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotReadJobDone(QKeychain::Job*)));
|
||||
job->setProperty("account", QVariant::fromValue(account));
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
bool HttpCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
return ((reply->error() != QNetworkReply::AuthenticationRequiredError)
|
||||
// returned if user or password is incorrect
|
||||
&& (reply->error() != QNetworkReply::OperationCanceledError));
|
||||
}
|
||||
|
||||
bool HttpCredentials::fetchFromUser(Account *account)
|
||||
{
|
||||
bool ok = false;
|
||||
QString password = queryPassword(&ok);
|
||||
if (ok) {
|
||||
_password = password;
|
||||
_ready = true;
|
||||
persist(account);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
|
||||
{
|
||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob*>(job);
|
||||
delete readJob->settings();
|
||||
_password = readJob->textData();
|
||||
QKeychain::Error error = job->error();
|
||||
switch (error) {
|
||||
case NoError:
|
||||
_ready = true;
|
||||
Q_EMIT fetched();
|
||||
break;
|
||||
default:
|
||||
if (!_user.isEmpty()) {
|
||||
bool ok;
|
||||
QString pwd = queryPassword(&ok);
|
||||
if (ok) {
|
||||
_password = pwd;
|
||||
_ready = true;
|
||||
persist(qvariant_cast<Account*>(readJob->property("account")));
|
||||
Q_EMIT fetched();
|
||||
break;
|
||||
}
|
||||
}
|
||||
qDebug() << "Error while reading password" << job->errorString();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpCredentials::persistForUrl(const QString& url)
|
||||
QString HttpCredentials::queryPassword(bool *ok)
|
||||
{
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
store->setCredentials(url, _user, _password);
|
||||
store->saveCredentials();
|
||||
qDebug() << AccountManager::instance()->account()->state();
|
||||
if (ok) {
|
||||
QString str = QInputDialog::getText(0, tr("Enter Password"),
|
||||
tr("Please enter %1 password for user '%2':")
|
||||
.arg(Theme::instance()->appNameGUI(), _user),
|
||||
QLineEdit::Password, QString(), ok);
|
||||
qDebug() << AccountManager::instance()->account()->state();
|
||||
return str;
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpCredentials::slotCredentialsFetched(bool ok)
|
||||
void HttpCredentials::invalidateToken(Account *account)
|
||||
{
|
||||
_ready = ok;
|
||||
if (_ready) {
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
_user = store->user();
|
||||
_password = store->password();
|
||||
_password = QString();
|
||||
DeletePasswordJob *job = new DeletePasswordJob(Theme::instance()->appName());
|
||||
job->setKey(keychainKey(account->url().toString(), _user));
|
||||
job->start();
|
||||
_ready = false;
|
||||
}
|
||||
|
||||
void HttpCredentials::persist(Account *account)
|
||||
{
|
||||
account->setCredentialSetting(QLatin1String(userC), _user);
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
job->setSettings(account->settingsWithGroup(Theme::instance()->appName()));
|
||||
job->setInsecureFallback(true);
|
||||
connect(job, SIGNAL(finished(QKeychain::Job*)), SLOT(slotWriteJobDone(QKeychain::Job*)));
|
||||
job->setKey(keychainKey(account->url().toString(), _user));
|
||||
job->setTextData(_password);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotWriteJobDone(QKeychain::Job *job)
|
||||
{
|
||||
delete job->settings();
|
||||
switch (job->error()) {
|
||||
case NoError:
|
||||
break;
|
||||
default:
|
||||
qDebug() << "Error while writing password" << job->errorString();
|
||||
}
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* authenticator)
|
||||
{
|
||||
if( !(authenticator && reply) ) return;
|
||||
|
||||
qDebug() << "Authenticating request for " << reply->url();
|
||||
|
||||
if (_attempts.contains(reply)) {
|
||||
++_attempts[reply];
|
||||
} else {
|
||||
connect(reply, SIGNAL(finished()),
|
||||
this, SLOT(slotReplyFinished()));
|
||||
_attempts[reply] = 1;
|
||||
}
|
||||
// TODO: Replace it with something meaningful...
|
||||
//if( reply->url().toString().startsWith( webdavUrl( _connection ) ) ) {
|
||||
if (_attempts[reply] > 1) {
|
||||
qDebug() << "Too many attempts to authenticate. Stop request.";
|
||||
reply->close();
|
||||
} else {
|
||||
authenticator->setUser( _user );
|
||||
authenticator->setPassword( _password );
|
||||
}
|
||||
//} else {
|
||||
// qDebug() << "WRN: attempt to authenticate to different url - closing.";
|
||||
// reply->close();
|
||||
//}
|
||||
Q_UNUSED(authenticator)
|
||||
// we cannot use QAuthenticator, because it sends username and passwords with latin1
|
||||
// instead of utf8 encoding. Instead, we send it manually. Thus, if we reach this signal,
|
||||
// those credentials were invalid and we terminate.
|
||||
qDebug() << "Stop request: Authentication failed for " << reply->url().toString();
|
||||
reply->close();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReplyFinished()
|
||||
QString HttpCredentials::keychainKey(const QString &url, const QString &user)
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast< QNetworkReply* >(sender());
|
||||
QString u(url);
|
||||
if( u.isEmpty() ) {
|
||||
qDebug() << "Empty url in keyChain, error!";
|
||||
return QString::null;
|
||||
}
|
||||
if( user.isEmpty() ) {
|
||||
qDebug() << "Error: User is emty!";
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
disconnect(reply, SIGNAL(finished()),
|
||||
this, SLOT(slotReplyFinished()));
|
||||
_attempts.remove (reply);
|
||||
if( !u.endsWith(QChar('/')) ) {
|
||||
u.append(QChar('/'));
|
||||
}
|
||||
|
||||
QString key = user+QLatin1Char(':')+u;
|
||||
return key;
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
@@ -24,39 +23,46 @@
|
||||
class QNetworkReply;
|
||||
class QAuthenticator;
|
||||
|
||||
namespace QKeychain {
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class HttpCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HttpCredentials();
|
||||
HttpCredentials(const QString& user, const QString& password);
|
||||
HttpCredentials();
|
||||
HttpCredentials(const QString& user, const QString& password);
|
||||
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
|
||||
QString user() const;
|
||||
QString password() const;
|
||||
void syncContextPreInit(CSYNC* ctx);
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch(Account *account);
|
||||
bool stillValid(QNetworkReply *reply);
|
||||
bool fetchFromUser(Account *account);
|
||||
void persist(Account *account);
|
||||
QString user() const;
|
||||
QString password() const;
|
||||
QString queryPassword(bool *ok);
|
||||
void invalidateToken(Account *account);
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotCredentialsFetched(bool);
|
||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
void slotReplyFinished();
|
||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
void slotReadJobDone(QKeychain::Job*);
|
||||
void slotWriteJobDone(QKeychain::Job*);
|
||||
|
||||
private:
|
||||
QString _user;
|
||||
QString _password;
|
||||
bool _ready;
|
||||
QMap<QNetworkReply*, int> _attempts;
|
||||
static QString keychainKey(const QString &url, const QString &user);
|
||||
QString _user;
|
||||
QString _password;
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkCookieJar>
|
||||
|
||||
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
||||
|
||||
|
||||
@@ -13,14 +13,16 @@
|
||||
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ShibbolethRefresher::ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
|
||||
ShibbolethRefresher::ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
|
||||
: QObject(parent),
|
||||
_account(account),
|
||||
_creds(creds),
|
||||
_csync_ctx(csync_ctx)
|
||||
{}
|
||||
@@ -33,7 +35,8 @@ void ShibbolethRefresher::refresh()
|
||||
this, SLOT(onInvalidatedAndFetched(QByteArray)));
|
||||
connect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
QMetaObject::invokeMethod(_creds, "invalidateAndFetch", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(_creds, "invalidateAndFetch",Qt::QueuedConnection,
|
||||
Q_ARG(Account*, _account));
|
||||
loop.exec();
|
||||
disconnect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
|
||||
@@ -23,6 +23,7 @@ class QByteArray;
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class Account;
|
||||
class ShibbolethCredentials;
|
||||
|
||||
class ShibbolethRefresher : public QObject
|
||||
@@ -30,7 +31,7 @@ class ShibbolethRefresher : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
|
||||
ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
|
||||
|
||||
void refresh();
|
||||
|
||||
@@ -38,6 +39,7 @@ private Q_SLOTS:
|
||||
void onInvalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
Account* _account;
|
||||
ShibbolethCredentials* _creds;
|
||||
CSYNC* _csync_ctx;
|
||||
};
|
||||
|
||||
@@ -16,17 +16,25 @@
|
||||
#include <QNetworkCookie>
|
||||
#include <QWebFrame>
|
||||
#include <QWebPage>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "creds/shibboleth/shibbolethcookiejar.h"
|
||||
#include "creds/shibboleth/shibbolethwebview.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
void ShibbolethWebView::setup(const QUrl& url, ShibbolethCookieJar* jar)
|
||||
void ShibbolethWebView::setup(Account *account, ShibbolethCookieJar* jar)
|
||||
{
|
||||
MirallAccessManager* nm = new MirallAccessManager(this);
|
||||
// we need our own QNAM, but the we offload the SSL error handling to
|
||||
// the account object, which already can do this
|
||||
connect(nm, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
|
||||
account, SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
|
||||
|
||||
QWebPage* page = new QWebPage(this);
|
||||
|
||||
jar->setParent(this);
|
||||
@@ -35,18 +43,19 @@ void ShibbolethWebView::setup(const QUrl& url, ShibbolethCookieJar* jar)
|
||||
connect(page, SIGNAL(loadStarted()),
|
||||
this, SLOT(slotLoadStarted()));
|
||||
connect(page, SIGNAL(loadFinished(bool)),
|
||||
this, SLOT(slotLoadFinished()));
|
||||
this, SLOT(slotLoadFinished(bool)));
|
||||
|
||||
nm->setCookieJar(jar);
|
||||
page->setNetworkAccessManager(nm);
|
||||
page->mainFrame ()->load (url);
|
||||
this->setPage (page);
|
||||
page->mainFrame()->load(account->url());
|
||||
this->setPage(page);
|
||||
setWindowTitle(tr("%1 - Authenticate").arg(Theme::instance()->appNameGUI()));
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(const QUrl& url, QWidget* parent)
|
||||
ShibbolethWebView::ShibbolethWebView(Account* account, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(url, new ShibbolethCookieJar(this));
|
||||
setup(account, new ShibbolethCookieJar(this));
|
||||
}
|
||||
|
||||
ShibbolethWebView::~ShibbolethWebView()
|
||||
@@ -54,10 +63,10 @@ ShibbolethWebView::~ShibbolethWebView()
|
||||
slotLoadFinished();
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(const QUrl& url, ShibbolethCookieJar* jar, QWidget* parent)
|
||||
ShibbolethWebView::ShibbolethWebView(Account* account, ShibbolethCookieJar* jar, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(url, jar);
|
||||
setup(account, jar);
|
||||
}
|
||||
|
||||
void ShibbolethWebView::onNewCookiesForUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url)
|
||||
@@ -96,9 +105,20 @@ void ShibbolethWebView::slotLoadStarted()
|
||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||
}
|
||||
|
||||
void ShibbolethWebView::slotLoadFinished()
|
||||
void ShibbolethWebView::slotLoadFinished(bool success)
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
|
||||
if (!title().isNull()) {
|
||||
setWindowTitle(tr("%1 - %2").arg(Theme::instance()->appNameGUI(), title()));
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
QMessageBox::critical(this, tr("Error loading IdP login page"),
|
||||
tr("Could not load Shibboleth login page to log you in.\n"
|
||||
"Please ensure that your network connection is working."));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -24,14 +24,15 @@ namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethCookieJar;
|
||||
class Account;
|
||||
|
||||
class ShibbolethWebView : public QWebView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethWebView(const QUrl& url, QWidget* parent = 0);
|
||||
ShibbolethWebView(const QUrl& url, ShibbolethCookieJar* jar, QWidget* parent = 0);
|
||||
ShibbolethWebView(Account *account, QWidget* parent = 0);
|
||||
ShibbolethWebView(Account *account, ShibbolethCookieJar* jar, QWidget* parent = 0);
|
||||
~ShibbolethWebView();
|
||||
|
||||
protected:
|
||||
@@ -45,10 +46,10 @@ Q_SIGNALS:
|
||||
private Q_SLOTS:
|
||||
void onNewCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
void slotLoadStarted();
|
||||
void slotLoadFinished();
|
||||
void slotLoadFinished(bool success = true);
|
||||
|
||||
private:
|
||||
void setup(const QUrl& url, ShibbolethCookieJar* jar);
|
||||
void setup(Account *account, ShibbolethCookieJar* jar);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -12,14 +12,16 @@
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMutex>
|
||||
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
||||
#include "creds/shibboleth/shibbolethwebview.h"
|
||||
#include "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibboleth/shibbolethconfigfile.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
@@ -44,15 +46,16 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx,
|
||||
|
||||
QMutex mutex;
|
||||
QMutexLocker locker(&mutex);
|
||||
MirallConfigFile cfg;
|
||||
ShibbolethCredentials* creds = dynamic_cast< ShibbolethCredentials* > (cfg.getCredentials());
|
||||
Account *account = AccountManager::instance()->account();
|
||||
ShibbolethCredentials* creds = qobject_cast<ShibbolethCredentials*>(account->credentials());
|
||||
|
||||
|
||||
if (!creds) {
|
||||
qDebug() << "Not a Shibboleth creds instance!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
ShibbolethRefresher refresher(creds, csync_ctx);
|
||||
ShibbolethRefresher refresher(account, creds, csync_ctx);
|
||||
|
||||
// blocks
|
||||
refresher.refresh();
|
||||
@@ -63,7 +66,9 @@ int shibboleth_redirect_callback(CSYNC* csync_ctx,
|
||||
} // ns
|
||||
|
||||
ShibbolethCredentials::ShibbolethCredentials()
|
||||
: _shibCookie(),
|
||||
: AbstractCredentials(),
|
||||
_url(),
|
||||
_shibCookie(),
|
||||
_ready(false),
|
||||
_browser(0),
|
||||
_otherCookies()
|
||||
@@ -88,7 +93,7 @@ QByteArray ShibbolethCredentials::prepareCookieData() const
|
||||
// have any way to get "session_key" module property from
|
||||
// csync. Had we have it, then we could just append shibboleth
|
||||
// cookies to the "session_key" value and set it in csync module.
|
||||
QList<QNetworkCookie> cookies(ownCloudInfo::instance()->getLastAuthCookies());
|
||||
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
||||
QMap<QString, QString> uniqueCookies;
|
||||
|
||||
cookies << _shibCookie;
|
||||
@@ -142,6 +147,14 @@ QString ShibbolethCredentials::authType() const
|
||||
return QString::fromLatin1("shibboleth");
|
||||
}
|
||||
|
||||
QString ShibbolethCredentials::user() const
|
||||
{
|
||||
// ### TODO: If we had a way to extract the currently authenticated user
|
||||
// somehow, we could return its id token (email) here (stored in REMOTE_USER)
|
||||
// The server doesn't return it by default
|
||||
return QString();
|
||||
}
|
||||
|
||||
QNetworkCookie ShibbolethCredentials::cookie() const
|
||||
{
|
||||
return _shibCookie;
|
||||
@@ -161,14 +174,16 @@ bool ShibbolethCredentials::ready() const
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::fetch()
|
||||
void ShibbolethCredentials::fetch(Account *account)
|
||||
{
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
ShibbolethConfigFile cfg;
|
||||
|
||||
_browser = new ShibbolethWebView(QUrl(cfg.ownCloudUrl()), cfg.createCookieJar());
|
||||
if (account) {
|
||||
_url = account->url();
|
||||
}
|
||||
_browser = new ShibbolethWebView(account, cfg.createCookieJar());
|
||||
connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
||||
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
||||
connect(_browser, SIGNAL(viewHidden()),
|
||||
@@ -177,13 +192,33 @@ void ShibbolethCredentials::fetch()
|
||||
}
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::persistForUrl(const QString& /*url*/)
|
||||
bool ShibbolethCredentials::stillValid(QNetworkReply *reply)
|
||||
{
|
||||
Q_UNUSED(reply)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ShibbolethCredentials::fetchFromUser(Account *account)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::persist(Account* /*account*/)
|
||||
{
|
||||
ShibbolethConfigFile cfg;
|
||||
|
||||
cfg.storeCookies(_otherCookies);
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::invalidateToken(Account *account)
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
_shibCookie.setValue("");
|
||||
// ### access to ctx missing, but might not be required at all
|
||||
//csync_set_module_property(ctx, "session_key", "");
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::disposeBrowser()
|
||||
{
|
||||
disconnect(_browser, SIGNAL(viewHidden()),
|
||||
@@ -212,12 +247,15 @@ void ShibbolethCredentials::slotBrowserHidden()
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::invalidateAndFetch()
|
||||
void ShibbolethCredentials::invalidateAndFetch(Account* account)
|
||||
{
|
||||
_ready = false;
|
||||
connect (this, SIGNAL(fetched()),
|
||||
this, SLOT(onFetched()));
|
||||
fetch();
|
||||
// small hack to support the ShibbolethRefresher hack
|
||||
// we already rand fetch() with a valid account object,
|
||||
// and hence know the url on refresh
|
||||
fetch(account);
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::onFetched()
|
||||
|
||||
@@ -38,15 +38,19 @@ public:
|
||||
void syncContextPreStart(CSYNC* ctx);
|
||||
bool changed(AbstractCredentials* credentials) const;
|
||||
QString authType() const;
|
||||
QString user() const;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
void fetch(Account *account);
|
||||
bool stillValid(QNetworkReply *reply);
|
||||
virtual bool fetchFromUser(Account *account);
|
||||
void persist(Account *account);
|
||||
void invalidateToken(Account *account);
|
||||
|
||||
QNetworkCookie cookie() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void invalidateAndFetch();
|
||||
void invalidateAndFetch(Account *account);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onShibbolethCookieReceived(const QNetworkCookie& cookie);
|
||||
@@ -58,6 +62,7 @@ Q_SIGNALS:
|
||||
void invalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
QUrl _url;
|
||||
QByteArray prepareCookieData() const;
|
||||
void disposeBrowser();
|
||||
|
||||
|
||||
35
src/main.cpp
@@ -11,11 +11,24 @@
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
#include <signal.h>
|
||||
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/utility.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
|
||||
void warnSystray()
|
||||
{
|
||||
QMessageBox::critical(0, qApp->translate("main.cpp", "System Tray not available"),
|
||||
qApp->translate("main.cpp", "%1 requires on a working system tray. "
|
||||
"If you are running XFCE, please follow "
|
||||
"<a href=\"http://docs.xfce.org/xfce/xfce4-panel/systray\">these instructions</a>. "
|
||||
"Otherwise, please install a system tray application such as 'trayer' and try again.")
|
||||
.arg(Mirall::Theme::instance()->appNameGUI()));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@@ -23,7 +36,9 @@ int main(int argc, char **argv)
|
||||
|
||||
Mirall::Application app(argc, argv);
|
||||
app.initialize();
|
||||
|
||||
#ifndef Q_OS_WIN
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
if( app.giveHelp() ) {
|
||||
app.showHelp();
|
||||
return 0;
|
||||
@@ -39,13 +54,17 @@ int main(int argc, char **argv)
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable() && qgetenv("DESKTOP_SESSION") != "ubuntu") {
|
||||
QMessageBox::critical(0, qApp->translate("main.cpp", "System Tray not available"),
|
||||
qApp->translate("main.cpp", "%1 requires on a working system tray. "
|
||||
"If you are running XFCE, please follow "
|
||||
"<a href=\"http://docs.xfce.org/xfce/xfce4-panel/systray\">these instructions</a>. "
|
||||
"Otherwise, please install a system tray application such as 'trayer' and try again.")
|
||||
.arg(Mirall::Theme::instance()->appNameGUI()));
|
||||
int attempts = 0;
|
||||
forever {
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable() && qgetenv("DESKTOP_SESSION") != "ubuntu") {
|
||||
Mirall::Utility::sleep(1);
|
||||
attempts++;
|
||||
if (attempts < 30) continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
warnSystray();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return app.exec();
|
||||
|
||||
326
src/mirall/account.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
#include "creds/credentialsfactory.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QMutex>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QSslSocket>
|
||||
#include <QNetworkCookieJar>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
static const char urlC[] = "url";
|
||||
static const char authTypeC[] = "authType";
|
||||
static const char userC[] = "user";
|
||||
static const char httpUserC[] = "http_user";
|
||||
|
||||
AccountManager *AccountManager::_instance = 0;
|
||||
|
||||
AccountManager *AccountManager::instance()
|
||||
{
|
||||
static QMutex mutex;
|
||||
if (!_instance)
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (!_instance) {
|
||||
_instance = new AccountManager;
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void AccountManager::setAccount(Account *account)
|
||||
{
|
||||
emit accountAboutToChange(account, _account);
|
||||
std::swap(_account, account);
|
||||
emit accountChanged(_account, account);
|
||||
}
|
||||
|
||||
|
||||
Account::Account(AbstractSslErrorHandler *sslErrorHandler, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _sslErrorHandler(sslErrorHandler)
|
||||
, _am(0)
|
||||
, _credentials(0)
|
||||
, _treatSslErrorsAsFailure(false)
|
||||
, _state(Account::Disconnected)
|
||||
{
|
||||
}
|
||||
|
||||
Account::~Account()
|
||||
{
|
||||
}
|
||||
|
||||
void Account::save()
|
||||
{
|
||||
QScopedPointer<QSettings> settings(settingsWithGroup(Theme::instance()->appName()));
|
||||
settings->setValue(QLatin1String(urlC), _url.toString());
|
||||
if (_credentials) {
|
||||
_credentials->persist(this);
|
||||
Q_FOREACH(QString key, _settingsMap.keys()) {
|
||||
settings->setValue(key, _settingsMap.value(key));
|
||||
}
|
||||
settings->setValue(QLatin1String(authTypeC), _credentials->authType());
|
||||
|
||||
// HACK: Save http_user also as user
|
||||
if (_settingsMap.contains(httpUserC))
|
||||
settings->setValue(userC, _settingsMap.value(httpUserC));
|
||||
}
|
||||
settings->sync();
|
||||
|
||||
// ### TODO port away from MirallConfigFile
|
||||
MirallConfigFile cfg;
|
||||
qDebug() << "Saving " << approvedCerts().count() << " unknown certs.";
|
||||
QByteArray certs;
|
||||
Q_FOREACH( const QSslCertificate& cert, approvedCerts() ) {
|
||||
certs += cert.toPem() + '\n';
|
||||
}
|
||||
if (!certs.isEmpty()) {
|
||||
cfg.setCaCerts( certs );
|
||||
}
|
||||
}
|
||||
|
||||
Account* Account::restore()
|
||||
{
|
||||
QScopedPointer<QSettings> settings(settingsWithGroup(Theme::instance()->appName()));
|
||||
if (!settings->childKeys().isEmpty()) {
|
||||
Account *acc = new Account;
|
||||
MirallConfigFile cfg;
|
||||
acc->setApprovedCerts(QSslCertificate::fromData(cfg.caCerts()));
|
||||
acc->setUrl(settings->value(QLatin1String(urlC)).toUrl());
|
||||
acc->setCredentials(CredentialsFactory::create(settings->value(QLatin1String(authTypeC)).toString()));
|
||||
|
||||
// We want to only restore settings for that auth type and the user value
|
||||
acc->_settingsMap.insert(QLatin1String(userC), settings->value(userC));
|
||||
QString authTypePrefix = settings->value(authTypeC).toString() + "_";
|
||||
Q_FOREACH(QString key, settings->childKeys()) {
|
||||
if (!key.startsWith(authTypePrefix))
|
||||
continue;
|
||||
acc->_settingsMap.insert(key, settings->value(key));
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool isEqualExceptProtocol(const QUrl &url1, const QUrl &url2)
|
||||
{
|
||||
return (url1.host() != url2.host() ||
|
||||
url1.port() != url2.port() ||
|
||||
url1.path() != url2.path());
|
||||
}
|
||||
|
||||
bool Account::changed(Account *other, bool ignoreUrlProtocol) const
|
||||
{
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
bool changes = false;
|
||||
if (ignoreUrlProtocol) {
|
||||
changes = isEqualExceptProtocol(_url, other->_url);
|
||||
} else {
|
||||
changes = (_url == other->_url);
|
||||
}
|
||||
|
||||
changes |= _credentials->changed(other->_credentials);
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
AbstractCredentials *Account::credentials() const
|
||||
{
|
||||
return _credentials;
|
||||
}
|
||||
|
||||
void Account::setCredentials(AbstractCredentials *cred)
|
||||
{
|
||||
_credentials = cred;
|
||||
// set active credential manager
|
||||
if (_am) {
|
||||
_am->deleteLater();
|
||||
}
|
||||
_am = _credentials->getQNAM();
|
||||
connect(_am, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
|
||||
SLOT(slotHandleErrors(QNetworkReply*,QList<QSslError>)));
|
||||
}
|
||||
|
||||
QUrl Account::davUrl() const
|
||||
{
|
||||
return concatUrlPath(url(), davPath());
|
||||
}
|
||||
|
||||
QList<QNetworkCookie> Account::lastAuthCookies() const
|
||||
{
|
||||
return _am->cookieJar()->cookiesForUrl(_url);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::headRequest(const QString &relPath)
|
||||
{
|
||||
return headRequest(concatUrlPath(url(), relPath));
|
||||
}
|
||||
|
||||
QNetworkReply *Account::headRequest(const QUrl &url)
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
return _am->head(request);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::getRequest(const QString &relPath)
|
||||
{
|
||||
return getRequest(concatUrlPath(url(), relPath));
|
||||
}
|
||||
|
||||
QNetworkReply *Account::getRequest(const QUrl &url)
|
||||
{
|
||||
QNetworkRequest request(url);
|
||||
return _am->get(request);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
return davRequest(verb, concatUrlPath(davUrl(), relPath), req, data);
|
||||
}
|
||||
|
||||
QNetworkReply *Account::davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
|
||||
{
|
||||
req.setUrl(url);
|
||||
return _am->sendCustomRequest(req, verb, data);
|
||||
}
|
||||
|
||||
void Account::setCertificateChain(const QList<QSslCertificate> &certs)
|
||||
{
|
||||
_certificateChain = certs;
|
||||
}
|
||||
|
||||
void Account::setApprovedCerts(const QList<QSslCertificate> certs)
|
||||
{
|
||||
_approvedCerts = certs;
|
||||
}
|
||||
|
||||
void Account::addApprovedCerts(const QList<QSslCertificate> certs)
|
||||
{
|
||||
_approvedCerts += certs;
|
||||
}
|
||||
|
||||
void Account::setSslErrorHandler(AbstractSslErrorHandler *handler)
|
||||
{
|
||||
_sslErrorHandler.reset(handler);
|
||||
}
|
||||
|
||||
void Account::setUrl(const QUrl &url)
|
||||
{
|
||||
_url = url;
|
||||
}
|
||||
|
||||
QUrl Account::concatUrlPath(const QUrl &url, const QString &concatPath)
|
||||
{
|
||||
QUrl tmpUrl = url;
|
||||
QString path = tmpUrl.path();
|
||||
// avoid '//'
|
||||
if (path.endsWith('/') && concatPath.startsWith('/')) {
|
||||
path.chop(1);
|
||||
} // avoid missing '/'
|
||||
else if (!path.endsWith('/') && !concatPath.startsWith('/')) {
|
||||
path += QLatin1Char('/');
|
||||
}
|
||||
path += concatPath;
|
||||
tmpUrl.setPath(path);
|
||||
return tmpUrl;
|
||||
}
|
||||
|
||||
QString Account::_configFileName;
|
||||
|
||||
QSettings *Account::settingsWithGroup(const QString& group)
|
||||
{
|
||||
if (_configFileName.isEmpty()) {
|
||||
// cache file name
|
||||
MirallConfigFile cfg;
|
||||
_configFileName = cfg.configFile();
|
||||
}
|
||||
QSettings *settings = new QSettings(_configFileName, QSettings::IniFormat);
|
||||
settings->beginGroup(group);
|
||||
return settings;
|
||||
}
|
||||
|
||||
QVariant Account::credentialSetting(const QString &key) const
|
||||
{
|
||||
if (_credentials) {
|
||||
QString prefix = _credentials->authType();
|
||||
QString value = _settingsMap.value(prefix+"_"+key).toString();
|
||||
if (value.isEmpty()) {
|
||||
value = _settingsMap.value(key).toString();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void Account::setCredentialSetting(const QString &key, const QVariant &value)
|
||||
{
|
||||
if (_credentials) {
|
||||
QString prefix = _credentials->authType();
|
||||
_settingsMap.insert(prefix+"_"+key, value);
|
||||
}
|
||||
}
|
||||
|
||||
int Account::state() const
|
||||
{
|
||||
return _state;
|
||||
}
|
||||
|
||||
void Account::setState(int state)
|
||||
{
|
||||
if (_state != state) {
|
||||
_state = state;
|
||||
emit stateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
void Account::slotHandleErrors(QNetworkReply *reply , QList<QSslError> errors)
|
||||
{
|
||||
qDebug() << "SSL-Warnings happened for url " << reply->url().toString();
|
||||
|
||||
if( _treatSslErrorsAsFailure ) {
|
||||
// User decided once not to trust. Honor this decision.
|
||||
qDebug() << "Certs not trusted by user decision, returning.";
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QSslCertificate> approvedCerts;
|
||||
if (_sslErrorHandler.isNull() ) {
|
||||
qDebug() << Q_FUNC_INFO << "called without valid SSL error handler for account" << url();
|
||||
} else {
|
||||
if (_sslErrorHandler->handleErrors(errors, &approvedCerts, this)) {
|
||||
QSslSocket::addDefaultCaCertificates(approvedCerts);
|
||||
addApprovedCerts(approvedCerts);
|
||||
// all ssl certs are known and accepted. We can ignore the problems right away.
|
||||
qDebug() << "Certs are already known and trusted, Warnings are not valid.";
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
_treatSslErrorsAsFailure = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
164
src/mirall/account.h
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SERVERCONNECTION_H
|
||||
#define SERVERCONNECTION_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QUrl>
|
||||
#include <QNetworkCookie>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSslCertificate>
|
||||
#include <QSslError>
|
||||
|
||||
class QSettings;
|
||||
class QNetworkReply;
|
||||
class QUrl;
|
||||
class QNetworkAccessManager;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class AbstractCredentials;
|
||||
class Account;
|
||||
|
||||
class AccountManager : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
static AccountManager *instance();
|
||||
~AccountManager() {}
|
||||
|
||||
void setAccount(Account *account);
|
||||
Account *account() { return _account; }
|
||||
|
||||
Q_SIGNALS:
|
||||
void accountChanged(Account *newAccount, Account *oldAccount);
|
||||
void accountAboutToChange(Account *newAccount, Account *oldAccount);
|
||||
|
||||
private:
|
||||
AccountManager() : _account(0) {}
|
||||
Account *_account;
|
||||
static AccountManager *_instance;
|
||||
};
|
||||
|
||||
/* Reimplement this to handle SSL errors */
|
||||
class AbstractSslErrorHandler {
|
||||
public:
|
||||
virtual ~AbstractSslErrorHandler() {}
|
||||
virtual bool handleErrors(QList<QSslError>, QList<QSslCertificate>*, Account*) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class represents an account on an ownCloud Server
|
||||
*/
|
||||
class Account : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State { Disconnected = 0, /// no network connection
|
||||
Connected, /// account is online
|
||||
SignedOut /// Disconnected + credential token has been discarded
|
||||
};
|
||||
|
||||
static QString davPath() { return "remote.php/webdav/"; }
|
||||
|
||||
Account(AbstractSslErrorHandler *sslErrorHandler = 0, QObject *parent = 0);
|
||||
~Account();
|
||||
|
||||
/**
|
||||
* Saves the account to a given settings file
|
||||
*/
|
||||
void save();
|
||||
|
||||
/**
|
||||
* Creates an account object from from a given settings file.
|
||||
*/
|
||||
static Account* restore();
|
||||
/**
|
||||
* @brief Creates a minimal account object
|
||||
*
|
||||
* This will set up a ssl error handler
|
||||
*
|
||||
* @return A new Account object
|
||||
*/
|
||||
static Account* create(const QUrl &url);
|
||||
|
||||
/**
|
||||
* @brief Checks the Account instance is different from \param other
|
||||
*
|
||||
* @returns true, if credentials or url have changed, false otherwise
|
||||
*/
|
||||
bool changed(Account *other, bool ignoreUrlProtocol) const;
|
||||
|
||||
/** Holds the accounts credentials */
|
||||
AbstractCredentials* credentials() const;
|
||||
void setCredentials(AbstractCredentials *cred);
|
||||
|
||||
/** Server url of the account */
|
||||
void setUrl(const QUrl &url);
|
||||
QUrl url() const { return _url; }
|
||||
|
||||
/** Returns webdav entry URL, based on url() */
|
||||
QUrl davUrl() const;
|
||||
|
||||
QList<QNetworkCookie> lastAuthCookies() const;
|
||||
|
||||
QNetworkReply* headRequest(const QString &relPath);
|
||||
QNetworkReply* headRequest(const QUrl &url);
|
||||
QNetworkReply* getRequest(const QString &relPath);
|
||||
QNetworkReply* getRequest(const QUrl &url);
|
||||
QNetworkReply* davRequest(const QByteArray &verb, const QString &relPath, QNetworkRequest req, QIODevice *data = 0);
|
||||
QNetworkReply* davRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data = 0);
|
||||
|
||||
/** The certificates of the account */
|
||||
QList<QSslCertificate> certificateChain() const { return _certificateChain; }
|
||||
void setCertificateChain(const QList<QSslCertificate> &certs);
|
||||
/** The certificates of the account */
|
||||
QList<QSslCertificate> approvedCerts() const { return _approvedCerts; }
|
||||
void setApprovedCerts(const QList<QSslCertificate> certs);
|
||||
void addApprovedCerts(const QList<QSslCertificate> certs);
|
||||
|
||||
// pluggable handler
|
||||
void setSslErrorHandler(AbstractSslErrorHandler *handler);
|
||||
|
||||
// static helper function
|
||||
static QUrl concatUrlPath(const QUrl &url, const QString &concatPath);
|
||||
static QSettings* settingsWithGroup(const QString &group);
|
||||
|
||||
// to be called by credentials only
|
||||
QVariant credentialSetting(const QString& key) const;
|
||||
void setCredentialSetting(const QString& key, const QVariant &value);
|
||||
|
||||
int state() const;
|
||||
void setState(int state);
|
||||
signals:
|
||||
void stateChanged(int state);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotHandleErrors(QNetworkReply*,QList<QSslError>);
|
||||
|
||||
private:
|
||||
QMap<QString, QVariant> _settingsMap;
|
||||
QUrl _url;
|
||||
QList<QSslCertificate> _approvedCerts;
|
||||
QList<QSslCertificate> _certificateChain;
|
||||
QScopedPointer<AbstractSslErrorHandler> _sslErrorHandler;
|
||||
QNetworkAccessManager *_am;
|
||||
AbstractCredentials* _credentials;
|
||||
bool _treatSslErrorsAsFailure;
|
||||
int _state;
|
||||
static QString _configFileName;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //SERVERCONNECTION_H
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/folderwizard.h"
|
||||
#include "mirall/folderstatusmodel.h"
|
||||
#include "mirall/utility.h"
|
||||
@@ -25,7 +24,8 @@
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/ignorelisteditor.h"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
#include "mirall/account.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
@@ -35,6 +35,10 @@
|
||||
#include <QMessageBox>
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
#include <QIcon>
|
||||
#include <QVariant>
|
||||
|
||||
#include "mirall/account.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
@@ -51,7 +55,8 @@ static const char progressBarStyleC[] =
|
||||
AccountSettings::AccountSettings(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::AccountSettings),
|
||||
_item(0)
|
||||
_wasDisabledBefore(false),
|
||||
_account(AccountManager::instance()->account())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
@@ -65,20 +70,23 @@ AccountSettings::AccountSettings(QWidget *parent) :
|
||||
ui->_folderList->setMinimumWidth( 300 );
|
||||
ui->_folderList->setEditTriggers( QAbstractItemView::NoEditTriggers );
|
||||
|
||||
ui->_ButtonRemove->setEnabled(false);
|
||||
ui->_ButtonEnable->setEnabled(false);
|
||||
ui->_ButtonInfo->setEnabled(false);
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
ui->_buttonRemove->setEnabled(false);
|
||||
ui->_buttonEnable->setEnabled(false);
|
||||
ui->_buttonAdd->setEnabled(true);
|
||||
|
||||
QAction *resetFolderAction = new QAction(this);
|
||||
resetFolderAction->setShortcut(QKeySequence(Qt::Key_F5));
|
||||
connect(resetFolderAction, SIGNAL(triggered()), SLOT(slotResetCurrentFolder()));
|
||||
addAction(resetFolderAction);
|
||||
|
||||
connect(ui->_ButtonRemove, SIGNAL(clicked()), this, SLOT(slotRemoveCurrentFolder()));
|
||||
connect(ui->_ButtonEnable, SIGNAL(clicked()), this, SLOT(slotEnableCurrentFolder()));
|
||||
connect(ui->_ButtonInfo, SIGNAL(clicked()), this, SLOT(slotInfoAboutCurrentFolder()));
|
||||
connect(ui->_ButtonAdd, SIGNAL(clicked()), this, SLOT(slotAddFolder()));
|
||||
QAction *syncNowAction = new QAction(this);
|
||||
syncNowAction->setShortcut(QKeySequence(Qt::Key_F6));
|
||||
connect(syncNowAction, SIGNAL(triggered()), SLOT(slotSyncCurrentFolderNow()));
|
||||
addAction(syncNowAction);
|
||||
|
||||
connect(ui->_buttonRemove, SIGNAL(clicked()), this, SLOT(slotRemoveCurrentFolder()));
|
||||
connect(ui->_buttonEnable, SIGNAL(clicked()), this, SLOT(slotEnableCurrentFolder()));
|
||||
connect(ui->_buttonAdd, SIGNAL(clicked()), this, SLOT(slotAddFolder()));
|
||||
connect(ui->modifyAccountButton, SIGNAL(clicked()), SLOT(slotOpenAccountWizard()));
|
||||
connect(ui->ignoredFilesButton, SIGNAL(clicked()), SLOT(slotIgnoreFilesEditor()));;
|
||||
|
||||
@@ -87,32 +95,43 @@ AccountSettings::AccountSettings(QWidget *parent) :
|
||||
|
||||
QColor color = palette().highlight().color();
|
||||
ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
|
||||
ownCloudInfo *ocInfo = ownCloudInfo::instance();
|
||||
slotUpdateQuota(ocInfo->lastQuotaTotalBytes(), ocInfo->lastQuotaUsedBytes());
|
||||
connect(ocInfo, SIGNAL(quotaUpdated(qint64,qint64)), SLOT(slotUpdateQuota(qint64,qint64)));
|
||||
ui->connectLabel->setWordWrap(true);
|
||||
ui->connectLabel->setOpenExternalLinks(true);
|
||||
ui->quotaLabel->setWordWrap(true);
|
||||
|
||||
ui->connectLabel->setWordWrap( true );
|
||||
ui->connectLabel->setText(tr("No account configured."));
|
||||
ui->_buttonAdd->setEnabled(false);
|
||||
if (_account) {
|
||||
connect(_account, SIGNAL(stateChanged(int)), SLOT(slotAccountStateChanged(int)));
|
||||
slotAccountStateChanged(_account->state());
|
||||
}
|
||||
|
||||
setFolderList(FolderMan::instance()->map());
|
||||
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderActivated( const QModelIndex& indx )
|
||||
{
|
||||
bool state = indx.isValid();
|
||||
|
||||
ui->_ButtonRemove->setEnabled( state );
|
||||
ui->_ButtonEnable->setEnabled( state );
|
||||
ui->_ButtonInfo->setEnabled( state );
|
||||
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
|
||||
|
||||
ui->_buttonRemove->setEnabled(state);
|
||||
if( Theme::instance()->singleSyncFolder() ) {
|
||||
// only one folder synced folder allowed.
|
||||
ui->_buttonAdd->setVisible(!haveFolders);
|
||||
} else {
|
||||
ui->_buttonAdd->setVisible(true);
|
||||
ui->_buttonAdd->setEnabled( state );
|
||||
}
|
||||
ui->_buttonEnable->setEnabled( state );
|
||||
|
||||
if ( state ) {
|
||||
bool folderEnabled = _model->data( indx, FolderStatusDelegate::FolderSyncEnabled).toBool();
|
||||
qDebug() << "folder is sync enabled: " << folderEnabled;
|
||||
if ( folderEnabled ) {
|
||||
ui->_ButtonEnable->setText( tr( "Pause" ) );
|
||||
ui->_buttonEnable->setText( tr( "Pause" ) );
|
||||
} else {
|
||||
ui->_ButtonEnable->setText( tr( "Resume" ) );
|
||||
ui->_buttonEnable->setText( tr( "Resume" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,7 +174,7 @@ void AccountSettings::slotFolderWizardAccepted()
|
||||
folderMan->slotScheduleAllFolders();
|
||||
emit folderChanged();
|
||||
}
|
||||
buttonsSetEnabled();
|
||||
slotButtonsSetEnabled();
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderWizardRejected()
|
||||
@@ -179,35 +198,24 @@ void AccountSettings::slotAddFolder( Folder *folder )
|
||||
QStandardItem *item = new QStandardItem();
|
||||
folderToModelItem( item, folder );
|
||||
_model->appendRow( item );
|
||||
slotCheckConnection();
|
||||
// in order to update the enabled state of the "Sync now" button
|
||||
connect(folder, SIGNAL(syncStateChange()), this, SLOT(slotButtonsSetEnabled()), Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AccountSettings::buttonsSetEnabled()
|
||||
void AccountSettings::slotButtonsSetEnabled()
|
||||
{
|
||||
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
|
||||
|
||||
ui->_ButtonRemove->setEnabled(false);
|
||||
if( Theme::instance()->singleSyncFolder() ) {
|
||||
// only one folder synced folder allowed.
|
||||
ui->_ButtonAdd->setVisible(!haveFolders);
|
||||
} else {
|
||||
ui->_ButtonAdd->setVisible(true);
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
}
|
||||
|
||||
QModelIndex selected = ui->_folderList->currentIndex();
|
||||
bool isSelected = selected.isValid();
|
||||
|
||||
ui->_ButtonEnable->setEnabled(isSelected);
|
||||
ui->_ButtonRemove->setEnabled(isSelected);
|
||||
ui->_ButtonInfo->setEnabled(isSelected);
|
||||
if (isSelected) {
|
||||
slotFolderActivated(selected);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::setListWidgetItem( QListWidgetItem *item )
|
||||
void AccountSettings::setGeneralErrors( const QStringList& errors )
|
||||
{
|
||||
_item = item;
|
||||
_generalErrors = errors;
|
||||
}
|
||||
|
||||
void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
@@ -215,7 +223,7 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
if( ! item || !f ) return;
|
||||
|
||||
item->setData( f->nativePath(), FolderStatusDelegate::FolderPathRole );
|
||||
item->setData( f->secondPath(), FolderStatusDelegate::FolderSecondPathRole );
|
||||
item->setData( f->remotePath(), FolderStatusDelegate::FolderSecondPathRole );
|
||||
item->setData( f->alias(), FolderStatusDelegate::FolderAliasRole );
|
||||
item->setData( f->syncEnabled(), FolderStatusDelegate::FolderSyncEnabled );
|
||||
|
||||
@@ -223,27 +231,40 @@ void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
SyncResult::Status status = res.status();
|
||||
|
||||
QStringList errorList = res.errorStrings();
|
||||
QString errors;
|
||||
if( ! errorList.isEmpty() ) {
|
||||
errors = res.errorStrings().join(QLatin1String("<br/>"));
|
||||
}
|
||||
|
||||
Theme *theme = Theme::instance();
|
||||
item->setData( theme->statusHeaderText( status ), Qt::ToolTipRole );
|
||||
if( f->syncEnabled() ) {
|
||||
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
|
||||
if( status == SyncResult::SyncPrepare ) {
|
||||
if( _wasDisabledBefore ) {
|
||||
// if the folder was disabled before, set the sync icon
|
||||
item->setData( theme->syncStateIcon( SyncResult::SyncRunning), FolderStatusDelegate::FolderStatusIconRole );
|
||||
} // we keep the previous icon for the SyncPrepare state.
|
||||
} else {
|
||||
// kepp the previous icon for the prepare phase.
|
||||
if( status == SyncResult::Problem) {
|
||||
item->setData( theme->syncStateIcon( SyncResult::Success), FolderStatusDelegate::FolderStatusIconRole );
|
||||
} else {
|
||||
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
item->setData( theme->folderDisabledIcon( ), FolderStatusDelegate::FolderStatusIconRole ); // size 48 before
|
||||
item->setData( theme->folderDisabledIcon( ), FolderStatusDelegate::FolderStatusIconRole ); // size 48 before
|
||||
_wasDisabledBefore = false;
|
||||
}
|
||||
item->setData( theme->statusHeaderText( status ), FolderStatusDelegate::FolderStatus );
|
||||
item->setData( errors, FolderStatusDelegate::FolderErrorMsg );
|
||||
item->setData( theme->statusHeaderText( status ), FolderStatusDelegate::FolderStatus );
|
||||
|
||||
if( errors.isEmpty() && (status == SyncResult::Error ||
|
||||
status == SyncResult::SetupError ||
|
||||
status == SyncResult::Unavailable )) {
|
||||
item->setData( theme->statusHeaderText(status), FolderStatusDelegate::FolderErrorMsg);
|
||||
if( errorList.isEmpty() ) {
|
||||
if( (status == SyncResult::Error ||
|
||||
status == SyncResult::SetupError ||
|
||||
status == SyncResult::SyncAbortRequested ||
|
||||
status == SyncResult::Unavailable)) {
|
||||
errorList << theme->statusHeaderText(status);
|
||||
}
|
||||
}
|
||||
|
||||
item->setData( errorList, FolderStatusDelegate::FolderErrorMsg);
|
||||
|
||||
bool ongoing = false;
|
||||
item->setData( QVariant(res.warnCount()), FolderStatusDelegate::WarningCount );
|
||||
if( status == SyncResult::SyncRunning ) {
|
||||
@@ -257,6 +278,8 @@ void AccountSettings::slotRemoveCurrentFolder()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
||||
if( selected.isValid() ) {
|
||||
int row = selected.row();
|
||||
|
||||
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
qDebug() << "Remove Folder alias " << alias;
|
||||
if( !alias.isEmpty() ) {
|
||||
@@ -270,11 +293,23 @@ void AccountSettings::slotRemoveCurrentFolder()
|
||||
if( ret == QMessageBox::No ) {
|
||||
return;
|
||||
}
|
||||
/* Remove the selected item from the timer hash. */
|
||||
QStandardItem *item = NULL;
|
||||
if( selected.isValid() )
|
||||
item = _model->itemFromIndex(selected);
|
||||
|
||||
if( selected.isValid() && item && _hideProgressTimers.contains(item) ) {
|
||||
QTimer *t = _hideProgressTimers[item];
|
||||
t->stop();
|
||||
_hideProgressTimers.remove(item);
|
||||
delete(t);
|
||||
}
|
||||
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->slotRemoveFolder( alias );
|
||||
setFolderList(folderMan->map());
|
||||
_model->removeRow(row);
|
||||
|
||||
emit folderChanged();
|
||||
slotCheckConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -293,7 +328,7 @@ void AccountSettings::slotResetCurrentFolder()
|
||||
if( ret == QMessageBox::Yes ) {
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder *f = folderMan->folder(alias);
|
||||
f->slotTerminateSync();
|
||||
f->slotTerminateSync(true);
|
||||
f->wipe();
|
||||
folderMan->slotScheduleAllFolders();
|
||||
}
|
||||
@@ -308,29 +343,33 @@ void AccountSettings::slotDoubleClicked( const QModelIndex& indx )
|
||||
emit openFolderAlias( alias );
|
||||
}
|
||||
|
||||
void AccountSettings::slotCheckConnection()
|
||||
void AccountSettings::showConnectionLabel( const QString& message, const QString& tooltip )
|
||||
{
|
||||
if( ownCloudInfo::instance()->isConfigured() ) {
|
||||
connect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
connect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
|
||||
ui->connectLabel->setText( tr("Checking %1 connection...").arg(Theme::instance()->appNameGUI()));
|
||||
qDebug() << "Check status.php from statusdialog.";
|
||||
ownCloudInfo::instance()->checkInstallation();
|
||||
const QString errStyle = QLatin1String("color:#ffffff; background-color:#bb4d4d;padding:5px;"
|
||||
"border-width: 1px; border-style: solid; border-color: #aaaaaa;"
|
||||
"border-radius:5px;");
|
||||
if( _generalErrors.isEmpty() ) {
|
||||
ui->connectLabel->setText( message );
|
||||
ui->connectLabel->setToolTip(tooltip);
|
||||
} else {
|
||||
// ownCloud is not yet configured.
|
||||
ui->connectLabel->setText( tr("No %1 connection configured.").arg(Theme::instance()->appNameGUI()));
|
||||
ui->_ButtonAdd->setEnabled( false);
|
||||
const QString msg = _generalErrors.join(QLatin1String("\n"));
|
||||
ui->connectLabel->setText( msg );
|
||||
ui->connectLabel->setToolTip(QString());
|
||||
ui->connectLabel->setStyleSheet(errStyle);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::setFolderList( const Folder::Map &folders )
|
||||
{
|
||||
_model->clear();
|
||||
|
||||
foreach(QTimer *t, _hideProgressTimers) {
|
||||
t->stop();
|
||||
delete t;
|
||||
}
|
||||
_hideProgressTimers.clear();
|
||||
|
||||
foreach( Folder *f, folders ) {
|
||||
qDebug() << "Folder: " << f;
|
||||
slotAddFolder( f );
|
||||
}
|
||||
|
||||
@@ -338,7 +377,7 @@ void AccountSettings::setFolderList( const Folder::Map &folders )
|
||||
if (idx.isValid()) {
|
||||
ui->_folderList->setCurrentIndex(idx);
|
||||
}
|
||||
buttonsSetEnabled();
|
||||
slotButtonsSetEnabled();
|
||||
|
||||
}
|
||||
|
||||
@@ -351,7 +390,7 @@ void AccountSettings::slotFolderOpenAction( const QString& alias )
|
||||
QUrl url(f->path(), QUrl::TolerantMode);
|
||||
url.setScheme( QLatin1String("file") );
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
#ifdef Q_OS_WIN
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = f->path();
|
||||
|
||||
@@ -395,10 +434,15 @@ void AccountSettings::slotEnableCurrentFolder()
|
||||
|
||||
// message box can return at any time while the thread keeps running,
|
||||
// so better check again after the user has responded.
|
||||
if ( f->isBusy() && terminate )
|
||||
folderMan->terminateSyncProcess( alias );
|
||||
if ( f->isBusy() && terminate ) {
|
||||
f->slotTerminateSync(false);
|
||||
}
|
||||
|
||||
folderMan->slotEnableFolder( alias, !folderEnabled );
|
||||
|
||||
// keep state for the icon setting.
|
||||
if( !folderEnabled ) _wasDisabledBefore = true;
|
||||
|
||||
slotUpdateFolderState (f);
|
||||
// set the button text accordingly.
|
||||
slotFolderActivated( selected );
|
||||
@@ -406,6 +450,20 @@ void AccountSettings::slotEnableCurrentFolder()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotSyncCurrentFolderNow()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
||||
if( !selected.isValid() )
|
||||
return;
|
||||
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder *f = folderMan->folder( alias );
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
f->evaluateSync(QStringList());
|
||||
}
|
||||
|
||||
void AccountSettings::slotUpdateFolderState( Folder *folder )
|
||||
{
|
||||
QStandardItem *item = 0;
|
||||
@@ -428,52 +486,45 @@ void AccountSettings::slotUpdateFolderState( Folder *folder )
|
||||
} else {
|
||||
// the dialog is not visible.
|
||||
}
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
void AccountSettings::slotOCInfo( const QString& url, const QString& versionStr, const QString& version, const QString& )
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = url;
|
||||
//void AccountSettings::slotOCInfo( const QString& url, const QString& versionStr, const QString& version, const QString& )
|
||||
//{
|
||||
//#ifdef Q_OS_WIN
|
||||
// // work around a bug in QDesktopServices on Win32, see i-net
|
||||
// QString filePath = url;
|
||||
|
||||
if (filePath.startsWith("\\\\") || filePath.startsWith("//"))
|
||||
_OCUrl.setUrl(QDir::toNativeSeparators(filePath));
|
||||
else
|
||||
_OCUrl = QUrl::fromLocalFile(filePath);
|
||||
#else
|
||||
_OCUrl = QUrl::fromLocalFile(url);
|
||||
#endif
|
||||
// if (filePath.startsWith("\\\\") || filePath.startsWith("//"))
|
||||
// _OCUrl.setUrl(QDir::toNativeSeparators(filePath));
|
||||
// else
|
||||
// _OCUrl = QUrl::fromLocalFile(filePath);
|
||||
//#else
|
||||
// _OCUrl = QUrl::fromLocalFile(url);
|
||||
//#endif
|
||||
|
||||
qDebug() << "#-------# oC found on " << url;
|
||||
/* enable the open button */
|
||||
ui->connectLabel->setOpenExternalLinks(true);
|
||||
QUrl safeUrl(url);
|
||||
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
ui->connectLabel->setText( tr("Connected to <a href=\"%1\">%2</a>.").arg(url, safeUrl.toString()) );
|
||||
ui->connectLabel->setToolTip( tr("Version: %1 (%2)").arg(versionStr).arg(version));
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
// qDebug() << "#-------# oC found on " << url;
|
||||
// /* enable the open button */
|
||||
// ui->connectLabel->setOpenExternalLinks(true);
|
||||
// QUrl safeUrl(url);
|
||||
// safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
// showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a>.").arg(url, safeUrl.toString()),
|
||||
// tr("Version: %1 (%2)").arg(versionStr).arg(version) );
|
||||
// ui->_buttonAdd->setEnabled(true);
|
||||
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
}
|
||||
// disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
// this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
// disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
// this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
//}
|
||||
|
||||
void AccountSettings::slotOCInfoFail( QNetworkReply *reply)
|
||||
{
|
||||
QString errStr = tr("unknown problem.");
|
||||
if( reply ) errStr = reply->errorString();
|
||||
//void AccountSettings::slotOCInfoFail( QNetworkReply *reply)
|
||||
//{
|
||||
// QString errStr = tr("unknown problem.");
|
||||
// if( reply ) errStr = reply->errorString();
|
||||
|
||||
ui->connectLabel->setText( tr("<p>Failed to connect to %1: <tt>%2</tt></p>").arg(Theme::instance()->appNameGUI()).arg(errStr) );
|
||||
ui->_ButtonAdd->setEnabled( false);
|
||||
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
|
||||
}
|
||||
// showConnectionLabel( tr("<p>Failed to connect to %1: <tt>%2</tt></p>").arg(Theme::instance()->appNameGUI()).arg(errStr) );
|
||||
// ui->_buttonAdd->setEnabled( false);
|
||||
//}
|
||||
|
||||
void AccountSettings::slotOpenOC()
|
||||
{
|
||||
@@ -516,9 +567,8 @@ QString AccountSettings::shortenFilename( const QString& folder, const QString&
|
||||
// rip off the whole ownCloud URL.
|
||||
Folder *f = FolderMan::instance()->folder(folder);
|
||||
if( f ) {
|
||||
QString remotePathUrl = ownCloudInfo::instance()->webdavUrl() + QLatin1Char('/') + f->secondPath();
|
||||
QString remotePathUrl = f->remoteUrl().toString();
|
||||
shortFile.remove(Utility::toCSyncScheme(remotePathUrl));
|
||||
|
||||
}
|
||||
}
|
||||
return shortFile;
|
||||
@@ -538,7 +588,7 @@ void AccountSettings::slotProgressProblem(const QString& folder, const Progress:
|
||||
|
||||
void AccountSettings::slotSetProgress(const QString& folder, const Progress::Info &progress )
|
||||
{
|
||||
// qDebug() << "================================> Progress for folder " << folder << " file " << file << ": "<< p1;
|
||||
// qDebug() << "================================> Progress for folder " << folder << " progress " << Progress::asResultString(progress.kind);
|
||||
QStandardItem *item = itemForFolder( folder );
|
||||
qint64 prog1 = progress.current_file_bytes;
|
||||
qint64 prog2 = progress.file_size;
|
||||
@@ -552,6 +602,11 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
qDebug() << "================================> INVALID Progress for folder " << folder;
|
||||
return;
|
||||
}
|
||||
if( (progress.kind == Progress::StartSync)
|
||||
&& progress.overall_file_count == 0 ) {
|
||||
// do not show progress if nothing is transmitted.
|
||||
return;
|
||||
}
|
||||
|
||||
QString itemFileName = shortenFilename(folder, progress.current_file);
|
||||
QString syncFileProgressString;
|
||||
@@ -578,6 +633,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
case Progress::StartDownload:
|
||||
case Progress::StartUpload:
|
||||
case Progress::StartDelete:
|
||||
case Progress::StartRename:
|
||||
syncFileProgressString = tr("Start");
|
||||
if( _hideProgressTimers.contains(item) ) {
|
||||
// The timer is still running.
|
||||
@@ -592,6 +648,7 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
case Progress::EndDownload:
|
||||
case Progress::EndUpload:
|
||||
case Progress::EndDelete:
|
||||
case Progress::EndRename:
|
||||
break;
|
||||
case Progress::EndSync:
|
||||
syncFileProgressString = tr("Completely");
|
||||
@@ -613,7 +670,9 @@ void AccountSettings::slotSetProgress(const QString& folder, const Progress::Inf
|
||||
case Progress::Download:
|
||||
case Progress::Upload:
|
||||
case Progress::Inactive:
|
||||
case Progress::Error:
|
||||
case Progress::SoftError:
|
||||
case Progress::NormalError:
|
||||
case Progress::FatalError:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -654,12 +713,21 @@ void AccountSettings::slotHideProgress()
|
||||
while (i != _hideProgressTimers.constEnd()) {
|
||||
if( i.value() == send_timer ) {
|
||||
QStandardItem *item = i.key();
|
||||
item->setData( QVariant(false), FolderStatusDelegate::AddProgressSpace );
|
||||
item->setData( QVariant(QString::null), FolderStatusDelegate::SyncProgressOverallString );
|
||||
item->setData( QVariant(QString::null), FolderStatusDelegate::SyncProgressItemString );
|
||||
item->setData( 0, FolderStatusDelegate::SyncProgressOverallPercent );
|
||||
|
||||
ui->_folderList->repaint();
|
||||
/* Check if this item is still existing */
|
||||
bool ok = false;
|
||||
for( int r = 0; !ok && r < _model->rowCount(); r++) {
|
||||
if( item == _model->item(r,0) ) {
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( ok ) {
|
||||
item->setData( QVariant(false), FolderStatusDelegate::AddProgressSpace );
|
||||
item->setData( QVariant(QString::null), FolderStatusDelegate::SyncProgressOverallString );
|
||||
item->setData( QVariant(QString::null), FolderStatusDelegate::SyncProgressItemString );
|
||||
item->setData( 0, FolderStatusDelegate::SyncProgressOverallPercent );
|
||||
}
|
||||
_hideProgressTimers.remove(item);
|
||||
break;
|
||||
}
|
||||
@@ -671,15 +739,25 @@ void AccountSettings::slotHideProgress()
|
||||
|
||||
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
||||
{
|
||||
ui->quotaProgressBar->setEnabled(true);
|
||||
// workaround the label only accepting ints (which may be only 32 bit wide)
|
||||
ui->quotaProgressBar->setMaximum(100);
|
||||
int qVal = qRound(used/(double)total * 100);
|
||||
if( qVal > 100 ) qVal = 100;
|
||||
ui->quotaProgressBar->setValue(qVal);
|
||||
QString usedStr = Utility::octetsToString(used);
|
||||
QString totalStr = Utility::octetsToString(total);
|
||||
ui->quotaLabel->setText(tr("%1 of %2 in use.").arg(usedStr, totalStr));
|
||||
if( total > 0 ) {
|
||||
ui->quotaProgressBar->setVisible(true);
|
||||
ui->quotaInfoLabel->setVisible(true);
|
||||
ui->quotaProgressBar->setEnabled(true);
|
||||
// workaround the label only accepting ints (which may be only 32 bit wide)
|
||||
ui->quotaProgressBar->setMaximum(100);
|
||||
int qVal = qRound(used/(double)total * 100);
|
||||
if( qVal > 100 ) qVal = 100;
|
||||
ui->quotaProgressBar->setValue(qVal);
|
||||
QString usedStr = Utility::octetsToString(used);
|
||||
QString totalStr = Utility::octetsToString(total);
|
||||
double percent = used/(double)total*100;
|
||||
QString percentStr = Utility::compactFormatDouble(percent, 1);
|
||||
ui->quotaLabel->setText(tr("%1 of %2 (%3%) in use.").arg(usedStr, totalStr, percentStr));
|
||||
} else {
|
||||
ui->quotaProgressBar->setVisible(false);
|
||||
ui->quotaInfoLabel->setVisible(false);
|
||||
ui->quotaLabel->setText(tr("Currently there is no storage usage information available."));
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotIgnoreFilesEditor()
|
||||
@@ -693,9 +771,35 @@ void AccountSettings::slotIgnoreFilesEditor()
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotInfoAboutCurrentFolder()
|
||||
void AccountSettings::slotAccountStateChanged(int state)
|
||||
{
|
||||
emit(openProgressDialog());
|
||||
if (_account) {
|
||||
QUrl safeUrl(_account->url());
|
||||
safeUrl.setPassword(QString()); // Remove the password from the URL to avoid showing it in the UI
|
||||
ui->_buttonAdd->setEnabled(state == Account::Connected);
|
||||
if (state == Account::Connected) {
|
||||
QString user;
|
||||
if (AbstractCredentials *cred = _account->credentials()) {
|
||||
user = cred->user();
|
||||
}
|
||||
if (user.isEmpty()) {
|
||||
showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a>.").arg(_account->url().toString(), safeUrl.toString())
|
||||
/*, tr("Version: %1 (%2)").arg(versionStr).arg(version) */ );
|
||||
} else {
|
||||
showConnectionLabel( tr("Connected to <a href=\"%1\">%2</a> as <i>%3</i>.").arg(_account->url().toString(), safeUrl.toString(), user)
|
||||
/*, tr("Version: %1 (%2)").arg(versionStr).arg(version) */ );
|
||||
}
|
||||
} else {
|
||||
showConnectionLabel( tr("No connection to %1 at <a href=\"%1\">%2</a>.")
|
||||
.arg(Theme::instance()->appNameGUI(),
|
||||
_account->url().toString(),
|
||||
safeUrl.toString()) );
|
||||
}
|
||||
} else {
|
||||
// ownCloud is not yet configured.
|
||||
showConnectionLabel( tr("No %1 connection configured.").arg(Theme::instance()->appNameGUI()) );
|
||||
ui->_buttonAdd->setEnabled( false);
|
||||
}
|
||||
}
|
||||
|
||||
AccountSettings::~AccountSettings()
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
|
||||
class QStandardItemModel;
|
||||
class QModelIndex;
|
||||
@@ -38,8 +37,8 @@ class AccountSettings;
|
||||
}
|
||||
|
||||
class FolderMan;
|
||||
class ItemProgressDialog;
|
||||
class IgnoreListEditor;
|
||||
class Account;
|
||||
|
||||
class AccountSettings : public QWidget
|
||||
{
|
||||
@@ -50,12 +49,10 @@ public:
|
||||
~AccountSettings();
|
||||
|
||||
void setFolderList( const Folder::Map& );
|
||||
void buttonsSetEnabled();
|
||||
void setListWidgetItem(QListWidgetItem* item);
|
||||
|
||||
signals:
|
||||
void folderChanged();
|
||||
void openProgressDialog();
|
||||
void openProtocol();
|
||||
void openFolderAlias( const QString& );
|
||||
void infoFolderAlias( const QString& );
|
||||
|
||||
@@ -63,23 +60,24 @@ public slots:
|
||||
void slotFolderActivated( const QModelIndex& );
|
||||
void slotOpenOC();
|
||||
void slotUpdateFolderState( Folder* );
|
||||
void slotCheckConnection();
|
||||
void slotOCInfo( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotOCInfoFail( QNetworkReply* );
|
||||
void slotDoubleClicked( const QModelIndex& );
|
||||
void slotFolderOpenAction( const QString& );
|
||||
void slotSetProgress(const QString&, const Progress::Info& progress);
|
||||
void slotProgressProblem(const QString& folder, const Progress::SyncProblem& problem);
|
||||
void slotButtonsSetEnabled();
|
||||
|
||||
void slotUpdateQuota( qint64,qint64 );
|
||||
void slotIgnoreFilesEditor();
|
||||
void slotAccountStateChanged(int state);
|
||||
|
||||
void setGeneralErrors( const QStringList& errors );
|
||||
|
||||
protected slots:
|
||||
void slotAddFolder();
|
||||
void slotAddFolder( Folder* );
|
||||
void slotEnableCurrentFolder();
|
||||
void slotSyncCurrentFolderNow();
|
||||
void slotRemoveCurrentFolder();
|
||||
void slotInfoAboutCurrentFolder();
|
||||
void slotResetCurrentFolder();
|
||||
void slotFolderWizardAccepted();
|
||||
void slotFolderWizardRejected();
|
||||
@@ -90,16 +88,17 @@ private:
|
||||
QString shortenFilename( const QString& folder, const QString& file ) const;
|
||||
void folderToModelItem( QStandardItem *, Folder * );
|
||||
QStandardItem* itemForFolder(const QString& );
|
||||
void showConnectionLabel( const QString& message, const QString& tooltip = QString() );
|
||||
|
||||
Ui::AccountSettings *ui;
|
||||
QPointer<ItemProgressDialog> _fileItemDialog;
|
||||
QPointer<IgnoreListEditor> _ignoreEditor;
|
||||
QStandardItemModel *_model;
|
||||
QListWidgetItem *_item;
|
||||
QUrl _OCUrl;
|
||||
QHash<QStandardItem*, QTimer*> _hideProgressTimers;
|
||||
QString _kindContext;
|
||||
|
||||
QStringList _generalErrors;
|
||||
bool _wasDisabledBefore;
|
||||
Account *_account;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="syncStatusGroupBox">
|
||||
<property name="title">
|
||||
<string>Sync Status</string>
|
||||
<string>Account to Synchronize</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
@@ -59,21 +59,21 @@
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="_ButtonAdd">
|
||||
<widget class="QPushButton" name="_buttonAdd">
|
||||
<property name="text">
|
||||
<string>Add Folder...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_ButtonEnable">
|
||||
<widget class="QPushButton" name="_buttonEnable">
|
||||
<property name="text">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_ButtonRemove">
|
||||
<widget class="QPushButton" name="_buttonRemove">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
@@ -92,13 +92,6 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_ButtonInfo">
|
||||
<property name="text">
|
||||
<string>Info...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@@ -121,6 +114,9 @@
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@@ -131,7 +127,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<widget class="QLabel" name="quotaInfoLabel">
|
||||
<property name="text">
|
||||
<string><b>Note:</b> Some folders, including network mounted or shared folders, might have different limits.</string>
|
||||
</property>
|
||||
|
||||
@@ -19,25 +19,18 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/systray.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/folderwatcher.h"
|
||||
#include "mirall/folderwizard.h"
|
||||
#include "mirall/networklocation.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/sslerrordialog.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/updatedetector.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/settingsdialog.h"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/inotify.h"
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/socketapi.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
@@ -45,26 +38,14 @@
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <QHash>
|
||||
#include <QHashIterator>
|
||||
#include <QUrl>
|
||||
#include <QDesktopServices>
|
||||
#include <QTranslator>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkProxyFactory>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
// application logging handler.
|
||||
void mirallLogCatcher(QtMsgType type, const char *msg)
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
// qDebug() exports to local8Bit, which is not always UTF-8
|
||||
Logger::instance()->mirallLog( QString::fromLocal8Bit(msg) );
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
static const char optionsC[] =
|
||||
@@ -83,12 +64,12 @@ static const char optionsC[] =
|
||||
QString applicationTrPath()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
return QString::fromLatin1(DATADIR"/i18n/");
|
||||
return QString::fromLatin1(DATADIR"/"APPLICATION_EXECUTABLE"/i18n/");
|
||||
#endif
|
||||
#ifdef Q_OS_MAC
|
||||
return QApplication::applicationDirPath()+QLatin1String("/../Resources/Translations"); // path defaults to app dir.
|
||||
#endif
|
||||
#ifdef Q_OS_WIN32
|
||||
#ifdef Q_OS_WIN
|
||||
return QApplication::applicationDirPath();
|
||||
#endif
|
||||
}
|
||||
@@ -98,17 +79,14 @@ QString applicationTrPath()
|
||||
|
||||
Application::Application(int &argc, char **argv) :
|
||||
SharedTools::QtSingleApplication(argc, argv),
|
||||
_tray(0),
|
||||
_networkMgr(new QNetworkConfigurationManager(this)),
|
||||
_sslErrorDialog(0),
|
||||
_contextMenu(0),
|
||||
_recentActionsMenu(0),
|
||||
_gui(0),
|
||||
_theme(Theme::instance()),
|
||||
_logBrowser(0),
|
||||
_logExpire(0),
|
||||
_helpOnly(false),
|
||||
_startupNetworkError(false),
|
||||
_showLogWindow(false),
|
||||
_logExpire(0),
|
||||
_logFlush(false),
|
||||
_helpOnly(false)
|
||||
_userTriggeredConnect(false)
|
||||
{
|
||||
setApplicationName( _theme->appNameGUI() );
|
||||
setWindowIcon( _theme->applicationIcon() );
|
||||
@@ -117,54 +95,42 @@ Application::Application(int &argc, char **argv) :
|
||||
//no need to waste time;
|
||||
if ( _helpOnly ) return;
|
||||
|
||||
setupLogBrowser();
|
||||
setupLogging();
|
||||
setupTranslations();
|
||||
|
||||
connect( this, SIGNAL(messageReceived(QString)), SLOT(slotParseOptions(QString)));
|
||||
connect( Logger::instance(), SIGNAL(guiLog(QString,QString)),
|
||||
this, SLOT(slotShowTrayMessage(QString,QString)));
|
||||
connect( Logger::instance(), SIGNAL(optionalGuiLog(QString,QString)),
|
||||
this, SLOT(slotShowOptionalTrayMessage(QString,QString)));
|
||||
connect( Logger::instance(), SIGNAL(guiMessage(QString,QString)),
|
||||
this, SLOT(slotShowGuiMessage(QString,QString)));
|
||||
|
||||
ProgressDispatcher *pd = ProgressDispatcher::instance();
|
||||
connect( pd, SIGNAL(progressInfo(QString,Progress::Info)), this,
|
||||
SLOT(slotUpdateProgress(QString,Progress::Info)) );
|
||||
connect( pd, SIGNAL(progressSyncProblem(QString,Progress::SyncProblem)),
|
||||
SLOT(slotProgressSyncProblem(QString,Progress::SyncProblem)));
|
||||
Account *account = Account::restore();
|
||||
if (account) {
|
||||
account->setSslErrorHandler(new SslDialogErrorHandler);
|
||||
AccountManager::instance()->setAccount(account);
|
||||
}
|
||||
|
||||
// create folder manager for sync folder management
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
connect( folderMan, SIGNAL(folderSyncStateChange(QString)),
|
||||
this,SLOT(slotSyncStateChange(QString)));
|
||||
folderMan->setSyncEnabled(false);
|
||||
|
||||
/* use a signal mapper to map the open requests to the alias names */
|
||||
_folderOpenActionMapper = new QSignalMapper(this);
|
||||
connect(_folderOpenActionMapper, SIGNAL(mapped(const QString &)),
|
||||
this, SLOT(slotFolderOpenAction(const QString &)));
|
||||
FolderMan::instance()->setSyncEnabled(false);
|
||||
|
||||
setQuitOnLastWindowClosed(false);
|
||||
|
||||
qRegisterMetaType<Progress::Kind>("Progress::Kind");
|
||||
qRegisterMetaType<Progress::Info>("Progress::Info");
|
||||
#if 0
|
||||
qDebug() << "* Network is" << (_networkMgr->isOnline() ? "online" : "offline");
|
||||
foreach (const QNetworkConfiguration& netCfg, _networkMgr->allConfigurations(QNetworkConfiguration::Active)) {
|
||||
//qDebug() << "Network:" << netCfg.identifier();
|
||||
}
|
||||
#endif
|
||||
qRegisterMetaType<Progress::SyncProblem>("Progress::SyncProblem");
|
||||
|
||||
MirallConfigFile cfg;
|
||||
_theme->setSystrayUseMonoIcons(cfg.monoIcons());
|
||||
connect (_theme, SIGNAL(systrayUseMonoIconsChanged(bool)), SLOT(slotUseMonoIconsChanged(bool)));
|
||||
|
||||
setupActions();
|
||||
setupSystemTray();
|
||||
slotSetupProxy();
|
||||
FolderMan::instance()->setupFolders();
|
||||
slotSetupProxy(); // folders have to be defined first.
|
||||
|
||||
folderMan->setupFolders();
|
||||
_gui = new ownCloudGui(this);
|
||||
if( _showLogWindow ) {
|
||||
_gui->slotToggleLogBrowser(); // _showLogWindow is set in parseOptions.
|
||||
}
|
||||
connect( _gui, SIGNAL(setupProxy()), SLOT(slotSetupProxy()));
|
||||
if (account) {
|
||||
connect(account, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
|
||||
}
|
||||
connect(AccountManager::instance(), SIGNAL(accountChanged(Account*,Account*)),
|
||||
this, SLOT(slotAccountChanged(Account*,Account*)));
|
||||
|
||||
// startup procedure.
|
||||
QTimer::singleShot( 0, this, SLOT( slotCheckConnection() ));
|
||||
@@ -173,25 +139,61 @@ Application::Application(int &argc, char **argv) :
|
||||
QTimer::singleShot( 3000, this, SLOT( slotStartUpdateDetector() ));
|
||||
}
|
||||
|
||||
connect( ownCloudInfo::instance(), SIGNAL(sslFailed(QNetworkReply*, QList<QSslError>)),
|
||||
this,SLOT(slotSSLFailed(QNetworkReply*, QList<QSslError>)));
|
||||
connect (this, SIGNAL(aboutToQuit()), SLOT(slotCleanup()));
|
||||
|
||||
connect( ownCloudInfo::instance(), SIGNAL(quotaUpdated(qint64,qint64)),
|
||||
SLOT(slotRefreshQuotaDisplay(qint64, qint64)));
|
||||
_socketApi = new SocketApi(this, cfg.configPathWithAppName().append(QLatin1String("socket")));
|
||||
|
||||
qDebug() << "Network Location: " << NetworkLocation::currentLocation().encoded();
|
||||
}
|
||||
|
||||
Application::~Application()
|
||||
{
|
||||
if (_settingsDialog) {
|
||||
delete _settingsDialog.data();
|
||||
// qDebug() << "* Mirall shutdown";
|
||||
}
|
||||
|
||||
void Application::slotLogin()
|
||||
{
|
||||
Account *a = AccountManager::instance()->account();
|
||||
if (a) {
|
||||
FolderMan::instance()->setupFolders();
|
||||
_userTriggeredConnect = true;
|
||||
slotCheckConnection();
|
||||
}
|
||||
}
|
||||
|
||||
delete _logBrowser;
|
||||
delete _tray; // needed, see ctor
|
||||
void Application::slotLogout()
|
||||
{
|
||||
Account *a = AccountManager::instance()->account();
|
||||
if (a) {
|
||||
// invalidate & forget token/password
|
||||
a->credentials()->invalidateToken(a);
|
||||
// terminate all syncs and unload folders
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->setSyncEnabled(false);
|
||||
folderMan->terminateSyncProcess();
|
||||
folderMan->unloadAllFolders();
|
||||
a->setState(Account::SignedOut);
|
||||
// show result
|
||||
_gui->slotComputeOverallSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "* Mirall shutdown";
|
||||
void Application::slotAccountChanged(Account *newAccount, Account *oldAccount)
|
||||
{
|
||||
disconnect(oldAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
|
||||
connect(newAccount, SIGNAL(stateChanged(int)), _gui, SLOT(slotAccountStateChanged()));
|
||||
}
|
||||
|
||||
|
||||
void Application::slotCleanup()
|
||||
{
|
||||
// explicitly close windows. This is somewhat of a hack to ensure
|
||||
// that saving the geometries happens ASAP during a OS shutdown
|
||||
Account *account = AccountManager::instance()->account();
|
||||
if (account) {
|
||||
account->save();
|
||||
}
|
||||
_gui->slotShutdown();
|
||||
_gui->deleteLater();
|
||||
}
|
||||
|
||||
void Application::slotStartUpdateDetector()
|
||||
@@ -202,36 +204,35 @@ void Application::slotStartUpdateDetector()
|
||||
|
||||
void Application::slotCheckConnection()
|
||||
{
|
||||
if( checkConfigExists(false) ) {
|
||||
MirallConfigFile cfg;
|
||||
AbstractCredentials* credentials(cfg.getCredentials());
|
||||
Account *account = AccountManager::instance()->account();
|
||||
|
||||
if( account ) {
|
||||
AbstractCredentials* credentials(account->credentials());
|
||||
|
||||
if (! credentials->ready()) {
|
||||
connect( credentials, SIGNAL(fetched()),
|
||||
this, SLOT(slotCredentialsFetched()));
|
||||
credentials->fetch();
|
||||
credentials->fetch(account);
|
||||
} else {
|
||||
runValidator();
|
||||
}
|
||||
} else {
|
||||
// the call to checkConfigExists opens the setup wizard
|
||||
// if the config does not exist. Nothing to do here.
|
||||
// let gui open the setup wizard
|
||||
_gui->slotOpenSettingsDialog( true );
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotCredentialsFetched()
|
||||
{
|
||||
MirallConfigFile cfg;
|
||||
AbstractCredentials* credentials(cfg.getCredentials());
|
||||
|
||||
disconnect(credentials, SIGNAL(fetched()),
|
||||
Account *account = AccountManager::instance()->account();
|
||||
disconnect(account->credentials(), SIGNAL(fetched()),
|
||||
this, SLOT(slotCredentialsFetched()));
|
||||
runValidator();
|
||||
}
|
||||
|
||||
void Application::runValidator()
|
||||
{
|
||||
_conValidator = new ConnectionValidator();
|
||||
_conValidator = new ConnectionValidator(AccountManager::instance()->account());
|
||||
connect( _conValidator, SIGNAL(connectionResult(ConnectionValidator::Status)),
|
||||
this, SLOT(slotConnectionValidatorResult(ConnectionValidator::Status)) );
|
||||
_conValidator->checkConnection();
|
||||
@@ -240,66 +241,38 @@ void Application::runValidator()
|
||||
void Application::slotConnectionValidatorResult(ConnectionValidator::Status status)
|
||||
{
|
||||
qDebug() << "Connection Validator Result: " << _conValidator->statusString(status);
|
||||
QStringList startupFails;
|
||||
|
||||
if( status == ConnectionValidator::Connected ) {
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
qDebug() << "######## Connection and Credentials are ok!";
|
||||
folderMan->setSyncEnabled(true);
|
||||
_tray->setIcon( _theme->syncStateIcon( SyncResult::NotYetStarted, true ) );
|
||||
_tray->show();
|
||||
|
||||
int cnt = folderMan->map().size();
|
||||
slotShowTrayMessage(tr("%1 Sync Started").arg(_theme->appNameGUI()),
|
||||
tr("Sync started for %n configured sync folder(s).","", cnt));
|
||||
|
||||
// queue up the sync for all folders.
|
||||
folderMan->slotScheduleAllFolders();
|
||||
|
||||
computeOverallSyncStatus();
|
||||
|
||||
setupContextMenu();
|
||||
} else if( status == ConnectionValidator::NotConfigured ) {
|
||||
// this can not happen, it should be caught in first step of startup.
|
||||
} else {
|
||||
// What else?
|
||||
}
|
||||
_conValidator->deleteLater();
|
||||
}
|
||||
|
||||
void Application::slotSSLFailed( QNetworkReply *reply, QList<QSslError> errors )
|
||||
{
|
||||
qDebug() << "SSL-Warnings happened for url " << reply->url().toString();
|
||||
|
||||
if( ownCloudInfo::instance()->certsUntrusted() ) {
|
||||
// User decided once to untrust. Honor this decision.
|
||||
qDebug() << "Untrusted by user decision, returning.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString configHandle = ownCloudInfo::instance()->configHandle(reply);
|
||||
|
||||
// make the ssl dialog aware of the custom config. It loads known certs.
|
||||
if( ! _sslErrorDialog ) {
|
||||
_sslErrorDialog = new SslErrorDialog;
|
||||
}
|
||||
_sslErrorDialog->setCustomConfigHandle( configHandle );
|
||||
|
||||
if( _sslErrorDialog->setErrorList( errors ) ) {
|
||||
// all ssl certs are known and accepted. We can ignore the problems right away.
|
||||
qDebug() << "Certs are already known and trusted, Warnings are not valid.";
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
if( _sslErrorDialog->exec() == QDialog::Accepted ) {
|
||||
if( _sslErrorDialog->trustConnection() ) {
|
||||
reply->ignoreSslErrors();
|
||||
} else {
|
||||
// User does not want to trust.
|
||||
ownCloudInfo::instance()->setCertsUntrusted(true);
|
||||
}
|
||||
} else {
|
||||
ownCloudInfo::instance()->setCertsUntrusted(true);
|
||||
if(!_connectionMsgBox.isNull()) {
|
||||
_connectionMsgBox->close();
|
||||
}
|
||||
|
||||
} else {
|
||||
// if we have problems here, it's unlikely that syncing will work.
|
||||
FolderMan::instance()->setSyncEnabled(false);
|
||||
|
||||
startupFails = _conValidator->errors();
|
||||
_startupNetworkError = _conValidator->networkError();
|
||||
if (_userTriggeredConnect) {
|
||||
if(_connectionMsgBox.isNull()) {
|
||||
_connectionMsgBox = new QMessageBox(QMessageBox::Warning, tr("Connection failed"),
|
||||
_conValidator->errors().join(". ").append('.'), QMessageBox::Ok, 0);
|
||||
_connectionMsgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
_connectionMsgBox->open();
|
||||
_userTriggeredConnect = false;
|
||||
}
|
||||
}
|
||||
QTimer::singleShot(30*1000, this, SLOT(slotCheckConnection()));
|
||||
}
|
||||
_gui->startupConnected( (status == ConnectionValidator::Connected), startupFails);
|
||||
|
||||
_conValidator->deleteLater();
|
||||
}
|
||||
|
||||
void Application::slotownCloudWizardDone( int res )
|
||||
@@ -320,126 +293,15 @@ void Application::slotownCloudWizardDone( int res )
|
||||
|
||||
}
|
||||
|
||||
void Application::setupActions()
|
||||
{
|
||||
_actionOpenoC = new QAction(tr("Open %1 in browser").arg(_theme->appNameGUI()), this);
|
||||
QObject::connect(_actionOpenoC, SIGNAL(triggered(bool)), SLOT(slotOpenOwnCloud()));
|
||||
_actionQuota = new QAction(tr("Calculating quota..."), this);
|
||||
_actionQuota->setEnabled( false );
|
||||
_actionStatus = new QAction(tr("Unknown status"), this);
|
||||
_actionStatus->setEnabled( false );
|
||||
_actionSettings = new QAction(tr("Settings..."), this);
|
||||
_actionRecent = new QAction(tr("Details..."), this);
|
||||
_actionRecent->setEnabled( true );
|
||||
|
||||
QObject::connect(_actionRecent, SIGNAL(triggered(bool)), SLOT(slotItemProgressDialog()));
|
||||
QObject::connect(_actionSettings, SIGNAL(triggered(bool)), SLOT(slotSettings()));
|
||||
_actionHelp = new QAction(tr("Help"), this);
|
||||
QObject::connect(_actionHelp, SIGNAL(triggered(bool)), SLOT(slotHelp()));
|
||||
_actionQuit = new QAction(tr("Quit %1").arg(_theme->appNameGUI()), this);
|
||||
QObject::connect(_actionQuit, SIGNAL(triggered(bool)), SLOT(quit()));
|
||||
}
|
||||
|
||||
void Application::setupSystemTray()
|
||||
{
|
||||
// Setting a parent heres will crash on X11 since by the time qapp runs
|
||||
// its childrens dtors, the X11->screen variable queried for is gone -> crash
|
||||
_tray = new Systray();
|
||||
_tray->setIcon( _theme->syncStateIcon( SyncResult::NotYetStarted, true ) );
|
||||
|
||||
connect(_tray,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
SLOT(slotTrayClicked(QSystemTrayIcon::ActivationReason)));
|
||||
|
||||
setupContextMenu();
|
||||
|
||||
_tray->show();
|
||||
}
|
||||
|
||||
void Application::setupContextMenu()
|
||||
{
|
||||
bool isConfigured = ownCloudInfo::instance()->isConfigured();
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
_actionOpenoC->setEnabled(isConfigured);
|
||||
|
||||
if( _contextMenu ) {
|
||||
_contextMenu->clear();
|
||||
_recentActionsMenu->clear();
|
||||
_recentActionsMenu->addAction(tr("None."));
|
||||
_recentActionsMenu->addAction(_actionRecent);
|
||||
} else {
|
||||
_contextMenu = new QMenu();
|
||||
_recentActionsMenu = _contextMenu->addMenu(tr("Recent Changes"));
|
||||
// this must be called only once after creating the context menu, or
|
||||
// it will trigger a bug in Ubuntu's SNI bridge patch (11.10, 12.04).
|
||||
_tray->setContextMenu(_contextMenu);
|
||||
}
|
||||
_contextMenu->setTitle(_theme->appNameGUI() );
|
||||
_contextMenu->addAction(_actionOpenoC);
|
||||
|
||||
int folderCnt = folderMan->map().size();
|
||||
// add open actions for all sync folders to the tray menu
|
||||
if( _theme->singleSyncFolder() ) {
|
||||
// there should be exactly one folder. No sync-folder add action will be shown.
|
||||
QStringList li = folderMan->map().keys();
|
||||
if( li.size() == 1 ) {
|
||||
Folder *folder = folderMan->map().value(li.first());
|
||||
if( folder ) {
|
||||
// if there is singleFolder mode, a generic open action is displayed.
|
||||
QAction *action = new QAction( tr("Open %1 folder").arg(_theme->appNameGUI()), this);
|
||||
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
|
||||
_folderOpenActionMapper->setMapping( action, folder->alias() );
|
||||
|
||||
_contextMenu->addAction(action);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// show a grouping with more than one folder.
|
||||
if ( folderCnt > 1) {
|
||||
_contextMenu->addAction(tr("Managed Folders:"))->setDisabled(true);
|
||||
}
|
||||
foreach (Folder *folder, folderMan->map() ) {
|
||||
QAction *action = new QAction( tr("Open folder '%1'").arg(folder->alias()), this );
|
||||
connect( action, SIGNAL(triggered()),_folderOpenActionMapper,SLOT(map()));
|
||||
_folderOpenActionMapper->setMapping( action, folder->alias() );
|
||||
|
||||
_contextMenu->addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionQuota);
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionStatus);
|
||||
_contextMenu->addMenu(_recentActionsMenu);
|
||||
_contextMenu->addSeparator();
|
||||
_contextMenu->addAction(_actionSettings);
|
||||
if (!_theme->helpUrl().isEmpty()) {
|
||||
_contextMenu->addAction(_actionHelp);
|
||||
}
|
||||
_contextMenu->addSeparator();
|
||||
|
||||
_contextMenu->addAction(_actionQuit);
|
||||
}
|
||||
|
||||
void Application::setupLogBrowser()
|
||||
void Application::setupLogging()
|
||||
{
|
||||
// might be called from second instance
|
||||
if (!_logBrowser) {
|
||||
// init the log browser.
|
||||
qInstallMsgHandler( mirallLogCatcher );
|
||||
_logBrowser = new LogBrowser;
|
||||
// ## TODO: allow new log name maybe?
|
||||
if (!_logDirectory.isEmpty()) {
|
||||
enterNextLogFile();
|
||||
} else if (!_logFile.isEmpty()) {
|
||||
qDebug() << "Logging into logfile: " << _logFile << " with flush " << _logFlush;
|
||||
_logBrowser->setLogFile( _logFile, _logFlush );
|
||||
}
|
||||
}
|
||||
Logger::instance()->setLogFile(_logFile);
|
||||
Logger::instance()->setLogDir(_logDir);
|
||||
Logger::instance()->setLogExpire(_logExpire);
|
||||
Logger::instance()->setLogFlush(_logFlush);
|
||||
|
||||
if (_showLogWindow)
|
||||
slotOpenLogBrowser();
|
||||
Logger::instance()->enterNextLogFile();
|
||||
|
||||
qDebug() << QString::fromLatin1( "################## %1 %2 (%3) %4").arg(_theme->appName())
|
||||
.arg( QLocale::system().name() )
|
||||
@@ -448,37 +310,6 @@ void Application::setupLogBrowser()
|
||||
|
||||
}
|
||||
|
||||
void Application::enterNextLogFile()
|
||||
{
|
||||
if (_logBrowser && !_logDirectory.isEmpty()) {
|
||||
QDir dir(_logDirectory);
|
||||
if (!dir.exists()) {
|
||||
dir.mkpath(".");
|
||||
}
|
||||
|
||||
// Find out what is the file with the highest nymber if any
|
||||
QStringList files = dir.entryList(QStringList("owncloud.log.*"),
|
||||
QDir::Files);
|
||||
QRegExp rx("owncloud.log.(\\d+)");
|
||||
uint maxNumber = 0;
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
foreach(const QString &s, files) {
|
||||
if (rx.exactMatch(s)) {
|
||||
maxNumber = qMax(maxNumber, rx.cap(1).toUInt());
|
||||
if (_logExpire > 0) {
|
||||
QFileInfo fileInfo = dir.absoluteFilePath(s);
|
||||
if (fileInfo.lastModified().addSecs(60*60 * _logExpire) < now) {
|
||||
dir.remove(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString filename = _logDirectory + "/owncloud.log." + QString::number(maxNumber+1);
|
||||
_logBrowser->setLogFile(filename , _logFlush);
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkProxy proxyFromConfig(const MirallConfigFile& cfg)
|
||||
{
|
||||
QNetworkProxy proxy;
|
||||
@@ -503,6 +334,7 @@ void Application::slotSetupProxy()
|
||||
|
||||
switch(proxyType) {
|
||||
case QNetworkProxy::NoProxy:
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy);
|
||||
break;
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
@@ -510,10 +342,12 @@ void Application::slotSetupProxy()
|
||||
break;
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
break;
|
||||
case QNetworkProxy::HttpProxy:
|
||||
proxy.setType(QNetworkProxy::HttpProxy);
|
||||
QNetworkProxyFactory::setUseSystemConfiguration(false);
|
||||
QNetworkProxy::setApplicationProxy(proxy);
|
||||
break;
|
||||
default:
|
||||
@@ -524,238 +358,16 @@ void Application::slotSetupProxy()
|
||||
FolderMan::instance()->slotScheduleAllFolders();
|
||||
}
|
||||
|
||||
void Application::slotRefreshQuotaDisplay( qint64 total, qint64 used )
|
||||
{
|
||||
if (total == 0) {
|
||||
_actionQuota->setText(tr("Quota n/a"));
|
||||
return;
|
||||
}
|
||||
|
||||
double percent = used/(double)total*100;
|
||||
QString percentFormatted = Utility::compactFormatDouble(percent, 1);
|
||||
QString totalFormatted = Utility::octetsToString(total);
|
||||
_actionQuota->setText(tr("%1% of %2 in use").arg(percentFormatted).arg(totalFormatted));
|
||||
}
|
||||
|
||||
void Application::slotUseMonoIconsChanged(bool)
|
||||
{
|
||||
computeOverallSyncStatus();
|
||||
}
|
||||
|
||||
void Application::slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem& problem)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
Q_UNUSED(problem);
|
||||
|
||||
// display a warn icon if warnings happend.
|
||||
QIcon warnIcon(":/mirall/resources/warning-16");
|
||||
_actionRecent->setIcon(warnIcon);
|
||||
|
||||
rebuildRecentMenus();
|
||||
}
|
||||
|
||||
void Application::rebuildRecentMenus()
|
||||
{
|
||||
_recentActionsMenu->clear();
|
||||
const QList<Progress::Info>& progressInfoList = ProgressDispatcher::instance()->recentChangedItems(5);
|
||||
|
||||
if( progressInfoList.size() == 0 ) {
|
||||
_recentActionsMenu->addAction(tr("No items synced recently"));
|
||||
} else {
|
||||
QListIterator<Progress::Info> i(progressInfoList);
|
||||
|
||||
while(i.hasNext()) {
|
||||
Progress::Info info = i.next();
|
||||
QString kindStr = Progress::asResultString(info.kind);
|
||||
QString timeStr = info.timestamp.toString("hh:mm");
|
||||
|
||||
QString actionText = tr("%1 (%2, %3)").arg(info.current_file).arg(kindStr).arg(timeStr);
|
||||
_recentActionsMenu->addAction( actionText );
|
||||
}
|
||||
}
|
||||
// add a more... entry.
|
||||
_recentActionsMenu->addAction(_actionRecent);
|
||||
}
|
||||
|
||||
void Application::slotUpdateProgress(const QString &folder, const Progress::Info& progress)
|
||||
{
|
||||
Q_UNUSED(folder);
|
||||
|
||||
// shows an entry in the context menu.
|
||||
QString curAmount = Utility::octetsToString(progress.overall_current_bytes);
|
||||
QString totalAmount = Utility::octetsToString(progress.overall_transmission_size);
|
||||
_actionStatus->setText(tr("Syncing %1 of %2 (%3 of %4) ").arg(progress.current_file_no)
|
||||
.arg(progress.overall_file_count).arg(curAmount, totalAmount));
|
||||
|
||||
// wipe the problem list at start of sync.
|
||||
if( progress.kind == Progress::StartSync ) {
|
||||
_actionRecent->setIcon( QIcon() ); // Fixme: Set a "in-progress"-item eventually.
|
||||
}
|
||||
|
||||
// If there was a change in the file list, redo the progress menu.
|
||||
if( progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload ||
|
||||
progress.kind == Progress::EndDelete ) {
|
||||
rebuildRecentMenus();
|
||||
}
|
||||
|
||||
if (progress.kind == Progress::EndSync) {
|
||||
rebuildRecentMenus(); // show errors.
|
||||
QTimer::singleShot(2000, this, SLOT(slotDisplayIdle()));
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotDisplayIdle()
|
||||
{
|
||||
_actionStatus->setText(tr("Up to date"));
|
||||
}
|
||||
|
||||
void Application::slotHelp()
|
||||
{
|
||||
QDesktopServices::openUrl(QUrl(_theme->helpUrl()));
|
||||
}
|
||||
|
||||
/*
|
||||
* open the folder with the given Alais
|
||||
*/
|
||||
void Application::slotFolderOpenAction( const QString& alias )
|
||||
{
|
||||
Folder *f = FolderMan::instance()->folder(alias);
|
||||
qDebug() << "opening local url " << f->path();
|
||||
if( f ) {
|
||||
QUrl url(f->path(), QUrl::TolerantMode);
|
||||
url.setScheme( QLatin1String("file") );
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = f->path();
|
||||
|
||||
if (filePath.startsWith(QLatin1String("\\\\")) || filePath.startsWith(QLatin1String("//")))
|
||||
url.setUrl(QDir::toNativeSeparators(filePath));
|
||||
else
|
||||
url = QUrl::fromLocalFile(filePath);
|
||||
#endif
|
||||
QDesktopServices::openUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotOpenOwnCloud()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
|
||||
QString url = cfgFile.ownCloudUrl();
|
||||
QDesktopServices::openUrl( url );
|
||||
}
|
||||
|
||||
void Application::slotTrayClicked( QSystemTrayIcon::ActivationReason reason )
|
||||
{
|
||||
// A click on the tray icon should only open the status window on Win and
|
||||
// Linux, not on Mac. They want a menu entry.
|
||||
#if defined Q_WS_WIN || defined Q_WS_X11
|
||||
if( reason == QSystemTrayIcon::Trigger ) {
|
||||
checkConfigExists(true); // start settings if config is existing.
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Application::checkConfigExists(bool openSettings)
|
||||
{
|
||||
// if no config file is there, start the configuration wizard.
|
||||
MirallConfigFile cfgFile;
|
||||
|
||||
if( cfgFile.exists() ) {
|
||||
if( openSettings ) {
|
||||
slotSettings();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
qDebug() << "No configured folders yet, starting setup wizard";
|
||||
OwncloudSetupWizard::runWizard(this, SLOT(slotownCloudWizardDone(int)));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Application::slotOpenLogBrowser()
|
||||
{
|
||||
_logBrowser->show();
|
||||
_logBrowser->raise();
|
||||
}
|
||||
|
||||
// slot hit when a folder gets changed in the settings dialog.
|
||||
void Application::slotFoldersChanged()
|
||||
{
|
||||
computeOverallSyncStatus();
|
||||
setupContextMenu();
|
||||
}
|
||||
|
||||
void Application::slotSettings()
|
||||
{
|
||||
if (_settingsDialog.isNull()) {
|
||||
_settingsDialog = new SettingsDialog(this);
|
||||
_settingsDialog->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_settingsDialog->show();
|
||||
}
|
||||
Utility::raiseDialog(_settingsDialog);
|
||||
}
|
||||
|
||||
void Application::slotItemProgressDialog()
|
||||
{
|
||||
if (_progressDialog.isNull()) {
|
||||
_progressDialog = new ItemProgressDialog(this);
|
||||
_progressDialog->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_progressDialog->setupList();
|
||||
_progressDialog->show();
|
||||
}
|
||||
Utility::raiseDialog(_progressDialog);
|
||||
_gui->slotComputeOverallSyncStatus();
|
||||
}
|
||||
|
||||
void Application::slotParseOptions(const QString &opts)
|
||||
{
|
||||
QStringList options = opts.split(QLatin1Char('|'));
|
||||
parseOptions(options);
|
||||
setupLogBrowser();
|
||||
}
|
||||
|
||||
void Application::slotShowTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
if( _tray )
|
||||
_tray->showMessage(title, msg);
|
||||
else
|
||||
qDebug() << "Tray not ready: " << msg;
|
||||
}
|
||||
|
||||
void Application::slotShowOptionalTrayMessage(const QString &title, const QString &msg)
|
||||
{
|
||||
MirallConfigFile cfg;
|
||||
if (cfg.optionalDesktopNotifications())
|
||||
slotShowTrayMessage(title, msg);
|
||||
}
|
||||
|
||||
void Application::slotShowGuiMessage(const QString &title, const QString &message)
|
||||
{
|
||||
QMessageBox *msgBox = new QMessageBox;
|
||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
msgBox->setText(message);
|
||||
msgBox->setWindowTitle(title);
|
||||
msgBox->setIcon(QMessageBox::Information);
|
||||
msgBox->open();
|
||||
}
|
||||
|
||||
void Application::slotSyncStateChange( const QString& alias )
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
const SyncResult& result = folderMan->syncResult( alias );
|
||||
emit folderStateChanged( folderMan->folder(alias) );
|
||||
|
||||
computeOverallSyncStatus();
|
||||
|
||||
qDebug() << "Sync state changed for folder " << alias << ": " << result.statusString();
|
||||
|
||||
if (result.status() == SyncResult::Success || result.status() == SyncResult::Error) {
|
||||
enterNextLogFile();
|
||||
}
|
||||
if( _progressDialog ) {
|
||||
_progressDialog->setSyncResult(result);
|
||||
}
|
||||
setupLogging();
|
||||
}
|
||||
|
||||
void Application::parseOptions(const QStringList &options)
|
||||
@@ -771,17 +383,17 @@ void Application::parseOptions(const QStringList &options)
|
||||
setHelp();
|
||||
break;
|
||||
} else if (option == QLatin1String("--logwindow") ||
|
||||
option == QLatin1String("-l")) {
|
||||
option == QLatin1String("-l")) {
|
||||
_showLogWindow = true;
|
||||
} else if (option == QLatin1String("--logfile")) {
|
||||
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
|
||||
_logFile = it.next();
|
||||
_logFile = it.next();
|
||||
} else {
|
||||
setHelp();
|
||||
}
|
||||
} else if (option == QLatin1String("--logdir")) {
|
||||
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
|
||||
_logDirectory = it.next();
|
||||
_logDir = it.next();
|
||||
} else {
|
||||
setHelp();
|
||||
}
|
||||
@@ -804,35 +416,6 @@ void Application::parseOptions(const QStringList &options)
|
||||
setHelp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::computeOverallSyncStatus()
|
||||
{
|
||||
|
||||
// display the info of the least successful sync (eg. not just display the result of the latest sync
|
||||
QString trayMessage;
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder::Map map = folderMan->map();
|
||||
SyncResult overallResult = FolderMan::accountStatus(map.values());
|
||||
|
||||
// create the tray blob message, check if we have an defined state
|
||||
if( overallResult.status() != SyncResult::Undefined ) {
|
||||
QStringList allStatusStrings;
|
||||
foreach(Folder* folder, map.values()) {
|
||||
qDebug() << "Folder in overallStatus Message: " << folder << " with name " << folder->alias();
|
||||
QString folderMessage = folderMan->statusToString(folder->syncResult().status(), folder->syncEnabled());
|
||||
allStatusStrings += tr("Folder %1: %2").arg(folder->alias(), folderMessage);
|
||||
}
|
||||
|
||||
if( ! allStatusStrings.isEmpty() )
|
||||
trayMessage = allStatusStrings.join(QLatin1String("\n"));
|
||||
else
|
||||
trayMessage = tr("No sync folders configured.");
|
||||
|
||||
QIcon statusIcon = _theme->syncStateIcon( overallResult.status(), true);
|
||||
_tray->setIcon( statusIcon );
|
||||
_tray->setToolTip(trayMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,7 +458,7 @@ void Application::showHelp()
|
||||
<< QLatin1String(optionsC);
|
||||
|
||||
if (_theme->appName() == QLatin1String("ownCloud"))
|
||||
stream << endl << "For more information, see http://www.owncloud.org" << endl;
|
||||
stream << endl << "For more information, see http://www.owncloud.org" << endl << endl;
|
||||
|
||||
displayHelpText(helpText);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslError>
|
||||
#include <QPointer>
|
||||
#include <QQueue>
|
||||
|
||||
@@ -25,26 +23,18 @@
|
||||
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logbrowser.h"
|
||||
#include "mirall/systray.h"
|
||||
#include "mirall/owncloudgui.h"
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QMessageBox;
|
||||
class QSystemTrayIcon;
|
||||
class QNetworkConfigurationManager;
|
||||
class QSignalMapper;
|
||||
class QNetworkReply;
|
||||
|
||||
namespace Mirall {
|
||||
class Theme;
|
||||
class Folder;
|
||||
class FolderWatcher;
|
||||
class FolderWizard;
|
||||
class ownCloudInfo;
|
||||
class SslErrorDialog;
|
||||
class SettingsDialog;
|
||||
class ItemProgressDialog;
|
||||
class SocketApi;
|
||||
|
||||
class Application : public SharedTools::QtSingleApplication
|
||||
{
|
||||
@@ -63,16 +53,10 @@ public slots:
|
||||
protected:
|
||||
void parseOptions(const QStringList& );
|
||||
void setupTranslations();
|
||||
void setupActions();
|
||||
void setupSystemTray();
|
||||
void setupContextMenu();
|
||||
void setupLogBrowser();
|
||||
void setupLogging();
|
||||
void enterNextLogFile();
|
||||
bool checkConfigExists(bool openSettings);
|
||||
|
||||
//folders have to be disabled while making config changes
|
||||
void computeOverallSyncStatus();
|
||||
|
||||
// reimplemented
|
||||
#if defined(Q_WS_WIN)
|
||||
bool winEventFilter( MSG * message, long * result );
|
||||
@@ -83,68 +67,42 @@ signals:
|
||||
void folderStateChanged(Folder*);
|
||||
|
||||
protected slots:
|
||||
void slotFoldersChanged();
|
||||
void slotSettings();
|
||||
void slotItemProgressDialog();
|
||||
void slotParseOptions( const QString& );
|
||||
void slotShowTrayMessage(const QString&, const QString&);
|
||||
void slotShowOptionalTrayMessage(const QString&, const QString&);
|
||||
void slotShowGuiMessage(const QString& title, const QString& message);
|
||||
void slotCheckConnection();
|
||||
void slotConnectionValidatorResult(ConnectionValidator::Status);
|
||||
void slotSyncStateChange( const QString& );
|
||||
void slotTrayClicked( QSystemTrayIcon::ActivationReason );
|
||||
void slotFolderOpenAction(const QString & );
|
||||
void slotOpenOwnCloud();
|
||||
void slotOpenLogBrowser();
|
||||
void slotSSLFailed( QNetworkReply *reply, QList<QSslError> errors );
|
||||
void slotStartUpdateDetector();
|
||||
void slotSetupProxy();
|
||||
void slotRefreshQuotaDisplay( qint64 total, qint64 used );
|
||||
void slotUseMonoIconsChanged( bool );
|
||||
void slotUpdateProgress(const QString&, const Progress::Info&);
|
||||
void slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem &problem);
|
||||
void slotDisplayIdle();
|
||||
void slotHelp();
|
||||
void slotCredentialsFetched();
|
||||
void slotLogin();
|
||||
void slotLogout();
|
||||
void slotCleanup();
|
||||
void slotAccountChanged(Account *newAccount, Account *oldAccount);
|
||||
|
||||
private:
|
||||
void setHelp();
|
||||
void raiseDialog( QWidget* );
|
||||
void rebuildRecentMenus();
|
||||
void runValidator();
|
||||
|
||||
Systray *_tray;
|
||||
QAction *_actionOpenoC;
|
||||
QAction *_actionSettings;
|
||||
QAction *_actionQuota;
|
||||
QAction *_actionStatus;
|
||||
QAction *_actionRecent;
|
||||
QAction *_actionHelp;
|
||||
QAction *_actionQuit;
|
||||
QPointer<ownCloudGui> _gui;
|
||||
QPointer<SocketApi> _socketApi;
|
||||
|
||||
QNetworkConfigurationManager *_networkMgr;
|
||||
|
||||
QPointer<FolderWizard> _folderWizard;
|
||||
SslErrorDialog *_sslErrorDialog;
|
||||
ConnectionValidator *_conValidator;
|
||||
|
||||
// tray's menu
|
||||
QMenu *_contextMenu;
|
||||
QMenu *_recentActionsMenu;
|
||||
|
||||
Theme *_theme;
|
||||
QSignalMapper *_folderOpenActionMapper;
|
||||
LogBrowser *_logBrowser;
|
||||
QPointer<SettingsDialog> _settingsDialog;
|
||||
QPointer<ItemProgressDialog> _progressDialog;
|
||||
|
||||
QString _logFile;
|
||||
QString _logDirectory;
|
||||
|
||||
int _logExpire;
|
||||
bool _showLogWindow;
|
||||
bool _logFlush;
|
||||
bool _helpOnly;
|
||||
bool _startupNetworkError;
|
||||
|
||||
// options from command line:
|
||||
bool _showLogWindow;
|
||||
QString _logFile;
|
||||
QString _logDir;
|
||||
int _logExpire;
|
||||
bool _logFlush;
|
||||
bool _userTriggeredConnect;
|
||||
QPointer<QMessageBox> _connectionMsgBox;
|
||||
|
||||
friend class ownCloudGui; // for _startupNetworkError
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -12,25 +12,21 @@
|
||||
*/
|
||||
|
||||
#include <QtCore>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ConnectionValidator::ConnectionValidator(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ConnectionValidator::ConnectionValidator(const QString& connection, QObject *parent)
|
||||
ConnectionValidator::ConnectionValidator(Account *account, QObject *parent)
|
||||
: QObject(parent),
|
||||
_connection(connection)
|
||||
_account(account),
|
||||
_networkError(QNetworkReply::NoError)
|
||||
{
|
||||
ownCloudInfo::instance()->setCustomConfigHandle(_connection);
|
||||
}
|
||||
|
||||
QStringList ConnectionValidator::errors() const
|
||||
@@ -38,6 +34,11 @@ QStringList ConnectionValidator::errors() const
|
||||
return _errors;
|
||||
}
|
||||
|
||||
bool ConnectionValidator::networkError() const
|
||||
{
|
||||
return _networkError;
|
||||
}
|
||||
|
||||
QString ConnectionValidator::statusString( Status stat ) const
|
||||
{
|
||||
QString re;
|
||||
@@ -79,38 +80,30 @@ QString ConnectionValidator::statusString( Status stat ) const
|
||||
|
||||
void ConnectionValidator::checkConnection()
|
||||
{
|
||||
if( ownCloudInfo::instance()->isConfigured() ) {
|
||||
connect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
SLOT(slotStatusFound(QString,QString,QString,QString)));
|
||||
|
||||
connect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
|
||||
// checks for status.php
|
||||
ownCloudInfo::instance()->checkInstallation();
|
||||
if( _account ) {
|
||||
CheckServerJob *checkJob = new CheckServerJob(_account, false, this);
|
||||
checkJob->setIgnoreCredentialFailure(true);
|
||||
connect(checkJob, SIGNAL(instanceFound(QUrl,QVariantMap)), SLOT(slotStatusFound(QUrl,QVariantMap)));
|
||||
connect(checkJob, SIGNAL(networkError(QNetworkReply*)), SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
checkJob->start();
|
||||
} else {
|
||||
_errors << tr("No ownCloud account configured");
|
||||
emit connectionResult( NotConfigured );
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotStatusFound( const QString& url, const QString& versionStr, const QString& version, const QString& /*edition*/)
|
||||
void ConnectionValidator::slotStatusFound(const QUrl&url, const QVariantMap &info)
|
||||
{
|
||||
// status.php was found.
|
||||
qDebug() << "** Application: ownCloud found: " << url << " with version " << versionStr << "(" << version << ")";
|
||||
qDebug() << "** Application: ownCloud found: "
|
||||
<< url << " with version "
|
||||
<< CheckServerJob::versionString(info)
|
||||
<< "(" << CheckServerJob::version(info) << ")";
|
||||
// now check the authentication
|
||||
MirallConfigFile cfgFile(_connection);
|
||||
|
||||
cfgFile.setOwnCloudVersion( version );
|
||||
// disconnect from ownCloudInfo
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
this, SLOT(slotStatusFound(QString,QString,QString,QString)));
|
||||
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
|
||||
if( version.startsWith("4.0") ) {
|
||||
_errors.append( tr("<p>The configured server for this client is too old.</p>"
|
||||
"<p>Please update to the latest server and restart the client.</p>"));
|
||||
if( CheckServerJob::version(info).startsWith("4.0") ) {
|
||||
_errors.append( tr("The configured server for this client is too old") );
|
||||
_errors.append( tr("Please update to the latest server and restart the client.") );
|
||||
emit connectionResult( ServerVersionMismatch );
|
||||
return;
|
||||
}
|
||||
@@ -121,46 +114,58 @@ void ConnectionValidator::slotStatusFound( const QString& url, const QString& ve
|
||||
// status.php could not be loaded.
|
||||
void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
|
||||
{
|
||||
// disconnect from ownCloudInfo
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
this, SLOT(slotStatusFound(QString,QString,QString,QString)));
|
||||
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotNoStatusFound(QNetworkReply*)));
|
||||
_account->setState(Account::Disconnected);
|
||||
|
||||
// ### TODO
|
||||
_errors.append(tr("Unable to connect to %1").arg(_account->url().toString()));
|
||||
_errors.append( reply->errorString() );
|
||||
_networkError = (reply->error() != QNetworkReply::NoError);
|
||||
emit connectionResult( StatusNotFound );
|
||||
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotCheckAuthentication()
|
||||
{
|
||||
connect( ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this, SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
ownCloudInfo::instance()->getWebDAVPath(QLatin1String("/") ); // this call needs to be authenticated.
|
||||
// simply GET the webdav root, will fail if credentials are wrong.
|
||||
// continue in slotAuthCheck here :-)
|
||||
PropfindJob *job = new PropfindJob(_account, "/", this);
|
||||
job->setIgnoreCredentialFailure(true);
|
||||
job->setProperties(QList<QByteArray>() << "getlastmodified");
|
||||
connect(job, SIGNAL(result(QVariantMap)), SLOT(slotAuthSuccess()));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotAuthFailed(QNetworkReply*)));
|
||||
job->start();
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotAuthCheck( const QString& ,QNetworkReply *reply )
|
||||
void ConnectionValidator::slotAuthFailed(QNetworkReply *reply)
|
||||
{
|
||||
Status stat = Connected;
|
||||
Status stat = StatusNotFound;
|
||||
|
||||
if( reply->error() == QNetworkReply::AuthenticationRequiredError ||
|
||||
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user is wrong.
|
||||
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user/pwd is wrong.
|
||||
qDebug() << reply->error() << reply->errorString();
|
||||
qDebug() << "******** Password is wrong!";
|
||||
_errors << "The provided credentials are wrong.";
|
||||
_errors << tr("The provided credentials are not correct");
|
||||
stat = CredentialsWrong;
|
||||
}
|
||||
switch (_account->state()) {
|
||||
case Account::SignedOut:
|
||||
_account->setState(Account::SignedOut);
|
||||
break;
|
||||
default:
|
||||
_account->setState(Account::Disconnected);
|
||||
}
|
||||
|
||||
// disconnect from ownCloud Info signals
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
} else if( reply->error() != QNetworkReply::NoError ) {
|
||||
_errors << reply->errorString();
|
||||
}
|
||||
|
||||
emit connectionResult( stat );
|
||||
}
|
||||
|
||||
|
||||
void ConnectionValidator::slotAuthSuccess()
|
||||
{
|
||||
_account->setState(Account::Connected);
|
||||
emit connectionResult(Connected);
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -16,17 +16,18 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class QNetworkReply;
|
||||
#include <QVariantMap>
|
||||
#include <QNetworkReply>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class Account;
|
||||
|
||||
class ConnectionValidator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConnectionValidator(QObject *parent = 0);
|
||||
explicit ConnectionValidator(const QString& connection, QObject *parent = 0);
|
||||
explicit ConnectionValidator(Account *account, QObject *parent = 0);
|
||||
|
||||
enum Status {
|
||||
Undefined,
|
||||
@@ -42,6 +43,7 @@ public:
|
||||
};
|
||||
|
||||
QStringList errors() const;
|
||||
bool networkError() const;
|
||||
|
||||
void checkConnection();
|
||||
|
||||
@@ -55,15 +57,17 @@ signals:
|
||||
public slots:
|
||||
|
||||
protected slots:
|
||||
void slotStatusFound( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotNoStatusFound(QNetworkReply *);
|
||||
void slotStatusFound(const QUrl&url, const QVariantMap &info);
|
||||
void slotNoStatusFound(QNetworkReply *reply);
|
||||
|
||||
void slotCheckAuthentication();
|
||||
void slotAuthCheck( const QString& ,QNetworkReply * );
|
||||
void slotAuthFailed(QNetworkReply *reply);
|
||||
void slotAuthSuccess();
|
||||
|
||||
private:
|
||||
QStringList _errors;
|
||||
QString _connection;
|
||||
Account *_account;
|
||||
bool _networkError;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -14,10 +14,13 @@
|
||||
*/
|
||||
|
||||
#include "mirall/csyncthread.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "owncloudpropagator.h"
|
||||
#include "syncjournaldb.h"
|
||||
#include "syncjournalfilerecord.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
@@ -42,16 +45,28 @@
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
/* static variables to hold the credentials */
|
||||
void csyncLogCatcher(int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
/* static variables to hold the credentials */
|
||||
QMutex CSyncThread::_mutex;
|
||||
QMutex CSyncThread::_syncMutex;
|
||||
|
||||
CSyncThread::CSyncThread(CSYNC *csync)
|
||||
CSyncThread::CSyncThread(CSYNC *csync, const QString &localPath, const QString &remotePath, SyncJournalDb *journal)
|
||||
{
|
||||
_mutex.lock();
|
||||
_localPath = localPath;
|
||||
_remotePath = remotePath;
|
||||
_csync_ctx = csync;
|
||||
_journal = journal;
|
||||
_mutex.unlock();
|
||||
qRegisterMetaType<SyncFileItem>("SyncFileItem");
|
||||
qRegisterMetaType<SyncFileItem::Status>("SyncFileItem::Status");
|
||||
}
|
||||
|
||||
CSyncThread::~CSyncThread()
|
||||
@@ -61,110 +76,173 @@ CSyncThread::~CSyncThread()
|
||||
|
||||
//Convert an error code from csync to a user readable string.
|
||||
// Keep that function thread safe as it can be called from the sync thread or the main thread
|
||||
QString CSyncThread::csyncErrorToString( CSYNC_ERROR_CODE err, const char *errString )
|
||||
QString CSyncThread::csyncErrorToString(CSYNC_STATUS err)
|
||||
{
|
||||
QString errStr;
|
||||
|
||||
switch( err ) {
|
||||
case CSYNC_ERR_NONE:
|
||||
case CSYNC_STATUS_OK:
|
||||
errStr = tr("Success.");
|
||||
break;
|
||||
case CSYNC_ERR_LOG:
|
||||
errStr = tr("CSync Logging setup failed.");
|
||||
break;
|
||||
case CSYNC_ERR_LOCK:
|
||||
case CSYNC_STATUS_NO_LOCK:
|
||||
errStr = tr("CSync failed to create a lock file.");
|
||||
break;
|
||||
case CSYNC_ERR_STATEDB_LOAD:
|
||||
case CSYNC_STATUS_STATEDB_LOAD_ERROR:
|
||||
errStr = tr("CSync failed to load the state db.");
|
||||
break;
|
||||
case CSYNC_ERR_MODULE:
|
||||
case CSYNC_STATUS_STATEDB_WRITE_ERROR:
|
||||
errStr = tr("CSync failed to write the state db.");
|
||||
break;
|
||||
case CSYNC_STATUS_NO_MODULE:
|
||||
errStr = tr("<p>The %1 plugin for csync could not be loaded.<br/>Please verify the installation!</p>").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_ERR_TIMESKEW:
|
||||
case CSYNC_STATUS_TIMESKEW:
|
||||
errStr = tr("The system time on this client is different than the system time on the server. "
|
||||
"Please use a time synchronization service (NTP) on the server and client machines "
|
||||
"so that the times remain the same.");
|
||||
break;
|
||||
case CSYNC_ERR_FILESYSTEM:
|
||||
case CSYNC_STATUS_FILESYSTEM_UNKNOWN:
|
||||
errStr = tr("CSync could not detect the filesystem type.");
|
||||
break;
|
||||
case CSYNC_ERR_TREE:
|
||||
case CSYNC_STATUS_TREE_ERROR:
|
||||
errStr = tr("CSync got an error while processing internal trees.");
|
||||
break;
|
||||
case CSYNC_ERR_MEM:
|
||||
case CSYNC_STATUS_MEMORY_ERROR:
|
||||
errStr = tr("CSync failed to reserve memory.");
|
||||
break;
|
||||
case CSYNC_ERR_PARAM:
|
||||
case CSYNC_STATUS_PARAM_ERROR:
|
||||
errStr = tr("CSync fatal parameter error.");
|
||||
break;
|
||||
case CSYNC_ERR_UPDATE:
|
||||
case CSYNC_STATUS_UPDATE_ERROR:
|
||||
errStr = tr("CSync processing step update failed.");
|
||||
break;
|
||||
case CSYNC_ERR_RECONCILE:
|
||||
case CSYNC_STATUS_RECONCILE_ERROR:
|
||||
errStr = tr("CSync processing step reconcile failed.");
|
||||
break;
|
||||
case CSYNC_ERR_PROPAGATE:
|
||||
case CSYNC_STATUS_PROPAGATE_ERROR:
|
||||
errStr = tr("CSync processing step propagate failed.");
|
||||
break;
|
||||
case CSYNC_ERR_ACCESS_FAILED:
|
||||
case CSYNC_STATUS_REMOTE_ACCESS_ERROR:
|
||||
errStr = tr("<p>The target directory does not exist.</p><p>Please check the sync setup.</p>");
|
||||
break;
|
||||
case CSYNC_ERR_REMOTE_CREATE:
|
||||
case CSYNC_ERR_REMOTE_STAT:
|
||||
case CSYNC_STATUS_REMOTE_CREATE_ERROR:
|
||||
case CSYNC_STATUS_REMOTE_STAT_ERROR:
|
||||
errStr = tr("A remote file can not be written. Please check the remote access.");
|
||||
break;
|
||||
case CSYNC_ERR_LOCAL_CREATE:
|
||||
case CSYNC_ERR_LOCAL_STAT:
|
||||
case CSYNC_STATUS_LOCAL_CREATE_ERROR:
|
||||
case CSYNC_STATUS_LOCAL_STAT_ERROR:
|
||||
errStr = tr("The local filesystem can not be written. Please check permissions.");
|
||||
break;
|
||||
case CSYNC_ERR_PROXY:
|
||||
case CSYNC_STATUS_PROXY_ERROR:
|
||||
errStr = tr("CSync failed to connect through a proxy.");
|
||||
break;
|
||||
case CSYNC_ERR_LOOKUP:
|
||||
case CSYNC_STATUS_PROXY_AUTH_ERROR:
|
||||
errStr = tr("CSync could not authenticate at the proxy.");
|
||||
break;
|
||||
case CSYNC_STATUS_LOOKUP_ERROR:
|
||||
errStr = tr("CSync failed to lookup proxy or server.");
|
||||
break;
|
||||
case CSYNC_ERR_AUTH_SERVER:
|
||||
case CSYNC_STATUS_SERVER_AUTH_ERROR:
|
||||
errStr = tr("CSync failed to authenticate at the %1 server.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_ERR_AUTH_PROXY:
|
||||
errStr = tr("CSync failed to authenticate at the proxy.");
|
||||
break;
|
||||
case CSYNC_ERR_CONNECT:
|
||||
case CSYNC_STATUS_CONNECT_ERROR:
|
||||
errStr = tr("CSync failed to connect to the network.");
|
||||
break;
|
||||
case CSYNC_ERR_TIMEOUT:
|
||||
case CSYNC_STATUS_TIMEOUT:
|
||||
errStr = tr("A network connection timeout happend.");
|
||||
break;
|
||||
case CSYNC_ERR_HTTP:
|
||||
case CSYNC_STATUS_HTTP_ERROR:
|
||||
errStr = tr("A HTTP transmission error happened.");
|
||||
break;
|
||||
case CSYNC_ERR_PERM:
|
||||
errStr = tr("CSync: Permission deniend.");
|
||||
case CSYNC_STATUS_PERMISSION_DENIED:
|
||||
errStr = tr("CSync failed due to not handled permission deniend.");
|
||||
break;
|
||||
case CSYNC_ERR_NOT_FOUND:
|
||||
errStr = tr("CSync: File not found.");
|
||||
case CSYNC_STATUS_NOT_FOUND:
|
||||
errStr = tr("CSync failed to access "); // filename gets added.
|
||||
break;
|
||||
case CSYNC_ERR_EXISTS:
|
||||
errStr = tr("CSync: Directory already exists.");
|
||||
case CSYNC_STATUS_FILE_EXISTS:
|
||||
errStr = tr("CSync tried to create a directory that already exists.");
|
||||
break;
|
||||
case CSYNC_ERR_NOSPC:
|
||||
errStr = tr("CSync: No space left on %1 server.").arg(Theme::instance()->appNameGUI());
|
||||
case CSYNC_STATUS_OUT_OF_SPACE:
|
||||
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_STATUS_QUOTA_EXCEEDED:
|
||||
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_STATUS_UNSUCCESSFUL:
|
||||
errStr = tr("CSync unspecified error.");
|
||||
break;
|
||||
case CSYNC_STATUS_ABORTED:
|
||||
errStr = tr("Aborted by the user");
|
||||
break;
|
||||
case CSYNC_ERR_UNSPEC:
|
||||
errStr = tr("CSync: unspecified error.");
|
||||
|
||||
default:
|
||||
errStr = tr("An internal error number %1 happend.").arg( (int) err );
|
||||
}
|
||||
|
||||
if( errString ) {
|
||||
errStr += tr("<br/>Backend Message: ")+QString::fromUtf8(errString);
|
||||
}
|
||||
return errStr;
|
||||
|
||||
}
|
||||
|
||||
bool CSyncThread::checkBlacklisting( SyncFileItem *item )
|
||||
{
|
||||
bool re = false;
|
||||
|
||||
if( !_journal ) {
|
||||
qWarning() << "Journal is undefined!";
|
||||
return false;
|
||||
}
|
||||
|
||||
SyncJournalBlacklistRecord entry = _journal->blacklistEntry(item->_file);
|
||||
item->_blacklistedInDb = false;
|
||||
|
||||
// if there is a valid entry in the blacklist table and the retry count is
|
||||
// already null or smaller than 0, the file is blacklisted.
|
||||
if( entry.isValid() ) {
|
||||
item->_blacklistedInDb = true;
|
||||
|
||||
if( entry._retryCount <= 0 ) {
|
||||
re = true;
|
||||
}
|
||||
|
||||
// if the retryCount is 0, but the etag for downloads or the mtime for uploads
|
||||
// has changed, it is tried again
|
||||
// note that if the retryCount is -1 we never try again.
|
||||
if( entry._retryCount == 0 ) {
|
||||
if( item->_dir == SyncFileItem::Up ) { // check the modtime
|
||||
if(item->_modtime == 0 || entry._lastTryModtime == 0) {
|
||||
re = false;
|
||||
} else {
|
||||
if( item->_modtime != entry._lastTryModtime ) {
|
||||
re = false;
|
||||
qDebug() << item->_file << " is blacklisted, but has changed mtime!";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// download, check the etag.
|
||||
if( item->_etag.isEmpty() || entry._lastTryEtag.isEmpty() ) {
|
||||
qDebug() << item->_file << "one ETag is empty, no blacklisting";
|
||||
return false;
|
||||
} else {
|
||||
if( item->_etag != entry._lastTryEtag ) {
|
||||
re = false;
|
||||
qDebug() << item->_file << " is blacklisted, but has changed etag!";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( re ) {
|
||||
qDebug() << "Item is on blacklist: " << entry._file << "retries:" << entry._retryCount;
|
||||
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
item->_errorString = tr("The item is not synced because of previous errors.");
|
||||
slotProgress( Progress::SoftError, *item );
|
||||
}
|
||||
}
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkLocal( TREE_WALK_FILE* file, void *data )
|
||||
{
|
||||
return static_cast<CSyncThread*>(data)->treewalkFile( file, false );
|
||||
@@ -175,64 +253,41 @@ int CSyncThread::treewalkRemote( TREE_WALK_FILE* file, void *data )
|
||||
return static_cast<CSyncThread*>(data)->treewalkFile( file, true );
|
||||
}
|
||||
|
||||
int CSyncThread::walkFinalize(TREE_WALK_FILE* file, void *data )
|
||||
{
|
||||
return static_cast<CSyncThread*>(data)->treewalkError( file);
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
{
|
||||
if( ! file ) return -1;
|
||||
SyncFileItem item;
|
||||
item._file = QString::fromUtf8( file->path );
|
||||
item._originalFile = item._file;
|
||||
item._instruction = file->instruction;
|
||||
item._dir = SyncFileItem::None;
|
||||
item._fileId = QString::fromUtf8(file->file_id);
|
||||
|
||||
SyncFileItem::Direction dir;
|
||||
// record the seen files to be able to clean the journal later
|
||||
_seenFiles[item._file] = QString();
|
||||
|
||||
int re = 0;
|
||||
|
||||
if (file->instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& file->instruction != CSYNC_INSTRUCTION_REMOVE) {
|
||||
_hasFiles = true;
|
||||
}
|
||||
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
switch(file->error_status) {
|
||||
case CSYNC_STATUS_OK:
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IS_SYMLINK:
|
||||
item._errorString = tr("Symbolic links are not supported in syncing.");
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IGNORE_LIST:
|
||||
item._errorString = tr("File is listed on the ignore list.");
|
||||
break;
|
||||
case CSYNC_STATUS_INDIVIDUAL_IS_INVALID_CHARS:
|
||||
item._errorString = tr("File contains invalid characters that can not be synced cross platform.");
|
||||
break;
|
||||
default:
|
||||
if (!_needsUpdate)
|
||||
_needsUpdate = true;
|
||||
}
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
// No need to do anything.
|
||||
return re;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
item._renameTarget = QString::fromUtf8( file->rename_path );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
dir = SyncFileItem::None;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
case CSYNC_INSTRUCTION_DELETED:
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
default:
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
Q_ASSERT("Non handled error-status");
|
||||
/* No error string */
|
||||
}
|
||||
|
||||
item._isDirectory = file->type == CSYNC_FTW_TYPE_DIR;
|
||||
item._modtime = file->modtime;
|
||||
item._etag = file->etag;
|
||||
item._size = file->size;
|
||||
item._should_update_etag = file->should_update_etag;
|
||||
switch( file->type ) {
|
||||
case CSYNC_FTW_TYPE_DIR:
|
||||
item._type = SyncFileItem::Directory;
|
||||
@@ -247,66 +302,110 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
item._type = SyncFileItem::UnknownType;
|
||||
}
|
||||
|
||||
SyncFileItem::Direction dir;
|
||||
|
||||
int re = 0;
|
||||
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
_progressInfo.overall_file_count++;
|
||||
_progressInfo.overall_transmission_size += file->size;
|
||||
//fall trough
|
||||
default:
|
||||
_needsUpdate = true;
|
||||
}
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
// We need to update the database.
|
||||
_journal->setFileRecord(SyncJournalFileRecord(item, _localPath + item._file));
|
||||
item._instruction = CSYNC_INSTRUCTION_NONE;
|
||||
// fall trough
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
if (item._isDirectory && remote) {
|
||||
// Because we want still to update etags of directories
|
||||
dir = SyncFileItem::None;
|
||||
} else {
|
||||
// No need to do anything.
|
||||
_hasFiles = true;
|
||||
|
||||
return re;
|
||||
}
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
item._renameTarget = QString::fromUtf8( file->rename_path );
|
||||
if (item._isDirectory)
|
||||
_renamedFolders.insert(item._file, item._renameTarget);
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
//
|
||||
slotProgress(Progress::SoftError, item, 0, 0);
|
||||
dir = SyncFileItem::None;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
case CSYNC_INSTRUCTION_DELETED:
|
||||
default:
|
||||
dir = remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
}
|
||||
|
||||
item._dir = dir;
|
||||
_mutex.lock();
|
||||
// check for blacklisting of this item.
|
||||
// if the item is on blacklist, the instruction was set to IGNORE
|
||||
checkBlacklisting( &item );
|
||||
|
||||
if (file->instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& file->instruction != CSYNC_INSTRUCTION_REMOVE) {
|
||||
_hasFiles = true;
|
||||
}
|
||||
_syncedItems.append(item);
|
||||
_mutex.unlock();
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkError(TREE_WALK_FILE* file)
|
||||
{
|
||||
SyncFileItem item; // only used for search.
|
||||
item._file= QString::fromUtf8(file->path);
|
||||
int indx = _syncedItems.indexOf(item);
|
||||
|
||||
if ( indx == -1 )
|
||||
return 0;
|
||||
|
||||
if( file &&
|
||||
(file->instruction == CSYNC_INSTRUCTION_STAT_ERROR ||
|
||||
file->instruction == CSYNC_INSTRUCTION_ERROR) ) {
|
||||
_mutex.lock();
|
||||
_syncedItems[indx]._instruction = file->instruction;
|
||||
_syncedItems[indx]._errorString = QString::fromUtf8(file->error_string);
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct CSyncRunScopeHelper {
|
||||
CSyncRunScopeHelper(CSYNC *ctx, CSyncThread *parent)
|
||||
: _ctx(ctx), _parent(parent)
|
||||
{
|
||||
_t.start();
|
||||
}
|
||||
~CSyncRunScopeHelper() {
|
||||
csync_commit(_ctx);
|
||||
|
||||
qDebug() << "CSync run took " << _t.elapsed() << " Milliseconds";
|
||||
emit(_parent->finished());
|
||||
_parent->_syncMutex.unlock();
|
||||
}
|
||||
CSYNC *_ctx;
|
||||
QTime _t;
|
||||
CSyncThread *_parent;
|
||||
};
|
||||
|
||||
void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
|
||||
CSYNC_ERROR_CODE err = csync_get_error( ctx );
|
||||
const char *errMsg = csync_get_error_string( ctx );
|
||||
QString errStr = csyncErrorToString(err, errMsg);
|
||||
CSYNC_STATUS err = csync_get_status( ctx );
|
||||
const char *errMsg = csync_get_status_string( ctx );
|
||||
QString errStr = csyncErrorToString(err);
|
||||
if( errMsg ) {
|
||||
if( !errStr.endsWith(" ")) {
|
||||
errStr.append(" ");
|
||||
}
|
||||
errStr += QString::fromUtf8(errMsg);
|
||||
}
|
||||
|
||||
// if there is csyncs url modifier in the error message, replace it.
|
||||
if( errStr.contains("ownclouds://") ) errStr.replace("ownclouds://", "https://");
|
||||
if( errStr.contains("owncloud://") ) errStr.replace("owncloud://", "http://");
|
||||
|
||||
qDebug() << " #### ERROR during "<< state << ": " << errStr;
|
||||
switch (err) {
|
||||
case CSYNC_ERR_SERVICE_UNAVAILABLE:
|
||||
case CSYNC_ERR_CONNECT:
|
||||
|
||||
if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_ABORTED) ) {
|
||||
qDebug() << "Update phase was aborted by user!";
|
||||
} else if( CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_SERVICE_UNAVAILABLE ) ||
|
||||
CSYNC_STATUS_IS_EQUAL( err, CSYNC_STATUS_CONNECT_ERROR )) {
|
||||
emit csyncUnavailable();
|
||||
break;
|
||||
default:
|
||||
} else {
|
||||
emit csyncError(errStr);
|
||||
}
|
||||
csync_commit(_csync_ctx);
|
||||
emit finished();
|
||||
_syncMutex.unlock();
|
||||
thread()->quit();
|
||||
}
|
||||
|
||||
void CSyncThread::startSync()
|
||||
@@ -321,44 +420,63 @@ void CSyncThread::startSync()
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "Sync started";
|
||||
|
||||
|
||||
qDebug() << "starting to sync " << qApp->thread() << QThread::currentThread();
|
||||
_syncedItems.clear();
|
||||
|
||||
_mutex.lock();
|
||||
_syncedItems.clear();
|
||||
_needsUpdate = false;
|
||||
if (!_abortRequested.fetchAndAddRelease(0)) {
|
||||
csync_resume(_csync_ctx);
|
||||
}
|
||||
_mutex.unlock();
|
||||
|
||||
// cleans up behind us and emits finished() to ease error handling
|
||||
CSyncRunScopeHelper helper(_csync_ctx, this);
|
||||
|
||||
// maybe move this somewhere else where it can influence a running sync?
|
||||
MirallConfigFile cfg;
|
||||
|
||||
int downloadLimit = 0;
|
||||
if (cfg.useDownloadLimit()) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "bandwidth_limit_download", &downloadLimit);
|
||||
if (!_journal->exists()) {
|
||||
qDebug() << "=====sync looks new (no DB exists), activating recursive PROPFIND if csync supports it";
|
||||
bool no_recursive_propfind = false;
|
||||
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
||||
} else {
|
||||
// retrieve the file count from the db and close it afterwards because
|
||||
// csync_update also opens the database.
|
||||
int fileRecordCount = 0;
|
||||
fileRecordCount = _journal->getFileRecordCount();
|
||||
_journal->close();
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "bandwidth_limit_upload", &uploadLimit);
|
||||
if( fileRecordCount == -1 ) {
|
||||
qDebug() << "No way to create a sync journal!";
|
||||
emit csyncError(tr("Unable to initialize a sync journal."));
|
||||
|
||||
csync_set_progress_callback( _csync_ctx, cb_progress );
|
||||
csync_commit(_csync_ctx);
|
||||
emit finished();
|
||||
_syncMutex.unlock();
|
||||
thread()->quit();
|
||||
|
||||
return;
|
||||
// database creation error!
|
||||
} else if ( fileRecordCount < 50 ) {
|
||||
qDebug() << "=====sync DB has only" << fileRecordCount << "items, enable recursive PROPFIND if csync supports it";
|
||||
bool no_recursive_propfind = false;
|
||||
csync_set_module_property(_csync_ctx, "no_recursive_propfind", &no_recursive_propfind);
|
||||
} else {
|
||||
qDebug() << "=====sync with existing DB";
|
||||
}
|
||||
}
|
||||
|
||||
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
|
||||
csync_set_userdata(_csync_ctx, this);
|
||||
|
||||
// TODO: This should be a part of this method, but we don't have
|
||||
// any way to get "session_key" module property from csync. Had we
|
||||
// have it, then we could keep this code and remove it from
|
||||
// AbstractCredentials implementations.
|
||||
cfg.getCredentials()->syncContextPreStart(_csync_ctx);
|
||||
if (Account *account = AccountManager::instance()->account()) {
|
||||
account->credentials()->syncContextPreStart(_csync_ctx);
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << "No default Account object, huh?";
|
||||
}
|
||||
// if (_lastAuthCookies.length() > 0) {
|
||||
// // Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
||||
// // when https://github.com/owncloud/core/pull/4042 is merged.
|
||||
@@ -373,23 +491,34 @@ void CSyncThread::startSync()
|
||||
// }
|
||||
|
||||
// csync_set_auth_callback( _csync_ctx, getauth );
|
||||
csync_set_log_callback( csyncLogCatcher );
|
||||
csync_set_log_level( 11 );
|
||||
|
||||
_syncTime.start();
|
||||
|
||||
|
||||
QElapsedTimer updateTime;
|
||||
updateTime.start();
|
||||
qDebug() << "#### Update start #################################################### >>";
|
||||
|
||||
if( csync_update(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "csync_update");
|
||||
return;
|
||||
}
|
||||
qDebug() << "<<#### Update end ###########################################################";
|
||||
qDebug() << "<<#### Update end #################################################### " << updateTime.elapsed();
|
||||
|
||||
if( csync_reconcile(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "csync_reconcile");
|
||||
return;
|
||||
}
|
||||
|
||||
slotProgress(Progress::StartSync, SyncFileItem(), 0, 0);
|
||||
|
||||
_progressInfo = Progress::Info();
|
||||
|
||||
_hasFiles = false;
|
||||
bool walkOk = true;
|
||||
_seenFiles.clear();
|
||||
|
||||
if( csync_walk_local_tree(_csync_ctx, &treewalkLocal, 0) < 0 ) {
|
||||
qDebug() << "Error in local treewalk.";
|
||||
walkOk = false;
|
||||
@@ -398,9 +527,15 @@ void CSyncThread::startSync()
|
||||
qDebug() << "Error in remote treewalk.";
|
||||
}
|
||||
|
||||
// Adjust the paths for the renames.
|
||||
for (SyncFileItemVector::iterator it = _syncedItems.begin();
|
||||
it != _syncedItems.end(); ++it) {
|
||||
it->_file = adjustRenamedPath(it->_file);
|
||||
}
|
||||
|
||||
if (!_hasFiles && !_syncedItems.isEmpty()) {
|
||||
qDebug() << Q_FUNC_INFO << "All the files are going to be removed, asking the user";
|
||||
bool cancel = true;
|
||||
bool cancel = false;
|
||||
emit aboutToRemoveAllFiles(_syncedItems.first()._dir, &cancel);
|
||||
if (cancel) {
|
||||
qDebug() << Q_FUNC_INFO << "Abort sync";
|
||||
@@ -411,95 +546,153 @@ void CSyncThread::startSync()
|
||||
if (_needsUpdate)
|
||||
emit(started());
|
||||
|
||||
if( csync_propagate(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "cysnc_reconcile");
|
||||
ne_session_s *session = 0;
|
||||
// that call to set property actually is a get which will return the session
|
||||
// FIXME add a csync_get_module_property to csync
|
||||
|
||||
csync_set_module_property(_csync_ctx, "get_dav_session", &session);
|
||||
Q_ASSERT(session);
|
||||
|
||||
_propagator.reset(new OwncloudPropagator (session, _localPath, _remotePath,
|
||||
_journal, &_abortRequested));
|
||||
connect(_propagator.data(), SIGNAL(completed(SyncFileItem)),
|
||||
this, SLOT(transferCompleted(SyncFileItem)), Qt::QueuedConnection);
|
||||
connect(_propagator.data(), SIGNAL(progress(Progress::Kind,SyncFileItem,quint64,quint64)),
|
||||
this, SLOT(slotProgress(Progress::Kind,SyncFileItem,quint64,quint64)));
|
||||
connect(_propagator.data(), SIGNAL(finished()), this, SLOT(slotFinished()));
|
||||
|
||||
int downloadLimit = 0;
|
||||
if (cfg.useDownloadLimit()) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
}
|
||||
_propagator->_downloadLimit = downloadLimit;
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
_propagator->_uploadLimit = uploadLimit;
|
||||
|
||||
_propagator->start(_syncedItems);
|
||||
}
|
||||
|
||||
void CSyncThread::transferCompleted(const SyncFileItem &item)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << item._file << item._status << item._errorString;
|
||||
|
||||
/* Update the _syncedItems vector */
|
||||
int idx = _syncedItems.indexOf(item);
|
||||
if (idx >= 0) {
|
||||
_syncedItems[idx]._instruction = item._instruction;
|
||||
_syncedItems[idx]._errorString = item._errorString;
|
||||
_syncedItems[idx]._status = item._status;
|
||||
|
||||
} else {
|
||||
qWarning() << Q_FUNC_INFO << "Could not find index in synced items!";
|
||||
}
|
||||
|
||||
if (item._status == SyncFileItem::FatalError) {
|
||||
emit csyncError(item._errorString);
|
||||
}
|
||||
}
|
||||
|
||||
void CSyncThread::slotFinished()
|
||||
{
|
||||
// emit the treewalk results.
|
||||
if( ! _journal->postSyncCleanup( _seenFiles ) ) {
|
||||
qDebug() << "Cleaning of synced ";
|
||||
}
|
||||
_journal->commit("All Finished.", false);
|
||||
emit treeWalkResult(_syncedItems);
|
||||
|
||||
csync_commit(_csync_ctx);
|
||||
|
||||
qDebug() << "CSync run took " << _syncTime.elapsed() << " Milliseconds";
|
||||
slotProgress(Progress::EndSync,SyncFileItem(), 0 , 0);
|
||||
emit finished();
|
||||
_propagator.reset(0);
|
||||
_syncMutex.unlock();
|
||||
thread()->quit();
|
||||
}
|
||||
|
||||
void CSyncThread::progressProblem(Progress::Kind kind, const SyncFileItem& item)
|
||||
{
|
||||
Progress::SyncProblem problem;
|
||||
|
||||
problem.kind = kind;
|
||||
problem.current_file = item._file;
|
||||
problem.error_message = item._errorString;
|
||||
problem.error_code = item._httpErrorCode;
|
||||
problem.timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// connected to something in folder.
|
||||
emit transmissionProblem( problem );
|
||||
}
|
||||
|
||||
void CSyncThread::slotProgress(Progress::Kind kind, const SyncFileItem& item, quint64 curr, quint64 total)
|
||||
{
|
||||
if( Progress::isErrorKind(kind) ) {
|
||||
progressProblem(kind, item);
|
||||
return;
|
||||
}
|
||||
|
||||
if( walkOk ) {
|
||||
if( csync_walk_local_tree(_csync_ctx, &walkFinalize, 0) < 0 ||
|
||||
csync_walk_remote_tree(_csync_ctx, &walkFinalize, 0 ) < 0 ) {
|
||||
qDebug() << "Error in finalize treewalk.";
|
||||
} else {
|
||||
// emit the treewalk results.
|
||||
emit treeWalkResult(_syncedItems);
|
||||
if( kind == Progress::StartSync ) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_currentFileNo = 0;
|
||||
_lastOverallBytes = 0;
|
||||
}
|
||||
if( kind == Progress::StartDelete ||
|
||||
kind == Progress::StartDownload ||
|
||||
kind == Progress::StartRename ||
|
||||
kind == Progress::StartUpload ) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_currentFileNo += 1;
|
||||
}
|
||||
|
||||
if( kind == Progress::EndUpload ||
|
||||
kind == Progress::EndDownload ||
|
||||
kind == Progress::EndRename ||
|
||||
kind == Progress::EndDelete ) {
|
||||
QMutexLocker lock(&_mutex);
|
||||
_lastOverallBytes += total;
|
||||
curr = 0;
|
||||
}
|
||||
|
||||
Progress::Info pInfo(_progressInfo);
|
||||
pInfo.kind = kind;
|
||||
pInfo.current_file = item._file;
|
||||
pInfo.rename_target = item._renameTarget;
|
||||
pInfo.file_size = total;
|
||||
pInfo.current_file_bytes = curr;
|
||||
pInfo.current_file_no = _currentFileNo;
|
||||
pInfo.timestamp = QDateTime::currentDateTime();
|
||||
pInfo.overall_current_bytes = _lastOverallBytes + curr;
|
||||
// Connect to something in folder!
|
||||
emit transmissionProgress( pInfo );
|
||||
}
|
||||
|
||||
/* Given a path on the remote, give the path as it is when the rename is done */
|
||||
QString CSyncThread::adjustRenamedPath(const QString& original)
|
||||
{
|
||||
int slashPos = original.size();
|
||||
while ((slashPos = original.lastIndexOf('/' , slashPos - 1)) > 0) {
|
||||
QHash< QString, QString >::const_iterator it = _renamedFolders.constFind(original.left(slashPos));
|
||||
if (it != _renamedFolders.constEnd()) {
|
||||
return *it + original.mid(slashPos);
|
||||
}
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "Sync finished";
|
||||
return original;
|
||||
}
|
||||
|
||||
Progress::Kind CSyncThread::csyncToProgressKind( enum csync_notify_type_e kind )
|
||||
void CSyncThread::abort()
|
||||
{
|
||||
Progress::Kind pKind = Progress::Invalid;
|
||||
|
||||
switch(kind) {
|
||||
case CSYNC_NOTIFY_INVALID:
|
||||
pKind = Progress::Invalid;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_SYNC_SEQUENCE:
|
||||
pKind = Progress::StartSync;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_DOWNLOAD:
|
||||
pKind = Progress::StartDownload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_UPLOAD:
|
||||
pKind = Progress::StartUpload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_PROGRESS:
|
||||
pKind = Progress::Context;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_DOWNLOAD:
|
||||
pKind = Progress::EndDownload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_UPLOAD:
|
||||
pKind = Progress::EndUpload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE:
|
||||
pKind = Progress::EndSync;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_DELETE:
|
||||
pKind = Progress::StartDelete;
|
||||
break;
|
||||
case CSYNC_NOTIFY_END_DELETE:
|
||||
pKind = Progress::EndDelete;
|
||||
break;
|
||||
case CSYNC_NOTIFY_ERROR:
|
||||
pKind = Progress::Error;
|
||||
break;
|
||||
default:
|
||||
pKind = Progress::Invalid;
|
||||
break;
|
||||
}
|
||||
return pKind;
|
||||
QMutexLocker locker(&_mutex);
|
||||
csync_request_abort(_csync_ctx);
|
||||
_abortRequested = true;
|
||||
}
|
||||
|
||||
void CSyncThread::cb_progress( CSYNC_PROGRESS *progress, void *userdata )
|
||||
{
|
||||
if( !progress ) {
|
||||
qDebug() << "No progress block in progress callback found!";
|
||||
return;
|
||||
}
|
||||
if( !userdata ) {
|
||||
qDebug() << "No thread given in progress callback!";
|
||||
return;
|
||||
}
|
||||
Progress::Info pInfo;
|
||||
CSyncThread *thread = static_cast<CSyncThread*>(userdata);
|
||||
|
||||
pInfo.kind = thread->csyncToProgressKind( progress->kind );
|
||||
pInfo.current_file = QUrl::fromEncoded( progress->path ).toString();
|
||||
pInfo.file_size = progress->file_size;
|
||||
pInfo.current_file_bytes = progress->curr_bytes;
|
||||
|
||||
pInfo.overall_file_count = progress->overall_file_count;
|
||||
pInfo.current_file_no = progress->current_file_no;
|
||||
pInfo.overall_transmission_size = progress->overall_transmission_size;
|
||||
pInfo.overall_current_bytes = progress->current_overall_bytes;
|
||||
pInfo.timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// Connect to something in folder!
|
||||
thread->transmissionProgress( pInfo );
|
||||
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
#include <qelapsedtimer.h>
|
||||
#include <qstack.h>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookie>
|
||||
|
||||
@@ -33,26 +35,41 @@ class QProcess;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SyncJournalFileRecord;
|
||||
|
||||
class SyncJournalDb;
|
||||
|
||||
class OwncloudPropagator;
|
||||
|
||||
void csyncLogCatcher(int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/);
|
||||
|
||||
class CSyncThread : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CSyncThread(CSYNC *);
|
||||
CSyncThread(CSYNC *, const QString &localPath, const QString &remotePath, SyncJournalDb *journal);
|
||||
~CSyncThread();
|
||||
|
||||
static QString csyncErrorToString( CSYNC_ERROR_CODE, const char * );
|
||||
static QString csyncErrorToString( CSYNC_STATUS);
|
||||
|
||||
Q_INVOKABLE void startSync();
|
||||
|
||||
/* Abort the sync. Called from the main thread */
|
||||
void abort();
|
||||
|
||||
signals:
|
||||
void fileReceived( const QString& );
|
||||
void fileRemoved( const QString& );
|
||||
void csyncError( const QString& );
|
||||
void csyncWarning( const QString& );
|
||||
void csyncUnavailable();
|
||||
void treeWalkResult(const SyncFileItemVector&);
|
||||
|
||||
void transmissionProgress( const Progress::Info& progress );
|
||||
void transmissionProblem( const Progress::SyncProblem& problem );
|
||||
|
||||
void csyncStateDbFile( const QString& );
|
||||
void wipeDb();
|
||||
|
||||
@@ -61,20 +78,19 @@ signals:
|
||||
|
||||
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
|
||||
|
||||
private slots:
|
||||
void transferCompleted(const SyncFileItem& item);
|
||||
void slotFinished();
|
||||
void slotProgress(Progress::Kind kind, const SyncFileItem &item, quint64 curr = 0, quint64 total = 0);
|
||||
|
||||
private:
|
||||
void handleSyncError(CSYNC *ctx, const char *state);
|
||||
|
||||
static void cb_progress( CSYNC_PROGRESS *progress, void *userdata );
|
||||
void progressProblem(Progress::Kind kind, const SyncFileItem& item);
|
||||
|
||||
static int treewalkLocal( TREE_WALK_FILE*, void *);
|
||||
static int treewalkRemote( TREE_WALK_FILE*, void *);
|
||||
int treewalkFile( TREE_WALK_FILE*, bool );
|
||||
int treewalkError( TREE_WALK_FILE* );
|
||||
|
||||
Progress::Kind csyncToProgressKind( enum csync_notify_type_e kind );
|
||||
static int walkFinalize(TREE_WALK_FILE*, void* );
|
||||
|
||||
|
||||
bool checkBlacklisting( SyncFileItem *item );
|
||||
|
||||
static QMutex _mutex;
|
||||
static QMutex _syncMutex;
|
||||
@@ -82,8 +98,28 @@ private:
|
||||
|
||||
CSYNC *_csync_ctx;
|
||||
bool _needsUpdate;
|
||||
QString _localPath;
|
||||
QString _remotePath;
|
||||
SyncJournalDb *_journal;
|
||||
QScopedPointer <OwncloudPropagator> _propagator;
|
||||
QElapsedTimer _syncTime;
|
||||
QString _lastDeleted; // if the last item was a path and it has been deleted
|
||||
QHash <QString, QString> _seenFiles;
|
||||
|
||||
|
||||
// maps the origin and the target of the folders that have been renamed
|
||||
QHash<QString, QString> _renamedFolders;
|
||||
QString adjustRenamedPath(const QString &original);
|
||||
|
||||
bool _hasFiles; // true if there is at least one file that is not ignored or removed
|
||||
Progress::Info _progressInfo;
|
||||
int _downloadLimit;
|
||||
int _uploadLimit;
|
||||
qint64 _currentFileNo;
|
||||
qint64 _overallFileCount;
|
||||
quint64 _lastOverallBytes;
|
||||
|
||||
QAtomicInt _abortRequested;
|
||||
|
||||
friend struct CSyncRunScopeHelper;
|
||||
};
|
||||
|
||||
@@ -15,16 +15,35 @@
|
||||
*/
|
||||
#include "config.h"
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/folderwatcher.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "mirall/syncjournalfilerecord.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "folderman.h"
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
enum csync_exclude_type_e {
|
||||
CSYNC_NOT_EXCLUDED = 0,
|
||||
CSYNC_FILE_SILENTLY_EXCLUDED,
|
||||
CSYNC_FILE_EXCLUDE_AND_REMOVE,
|
||||
CSYNC_FILE_EXCLUDE_LIST,
|
||||
CSYNC_FILE_EXCLUDE_INVALID_CHAR
|
||||
};
|
||||
typedef enum csync_exclude_type_e CSYNC_EXCLUDE_TYPE;
|
||||
|
||||
CSYNC_EXCLUDE_TYPE csync_excluded(CSYNC *ctx, const char *path, int filetype);
|
||||
|
||||
}
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
@@ -35,25 +54,19 @@
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
void csyncLogCatcher(CSYNC */*ctx*/,
|
||||
int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
Folder::Folder(const QString &alias, const QString &path, const QString& secondPath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, _path(path)
|
||||
, _secondPath(secondPath)
|
||||
, _remotePath(secondPath)
|
||||
, _alias(alias)
|
||||
, _enabled(true)
|
||||
, _thread(0)
|
||||
, _csync(0)
|
||||
, _csyncError(false)
|
||||
, _csyncUnavail(false)
|
||||
, _wipeDb(false)
|
||||
, _proxyDirty(true)
|
||||
, _journal(path)
|
||||
, _csync_ctx(0)
|
||||
{
|
||||
qsrand(QTime::currentTime().msec());
|
||||
@@ -84,7 +97,7 @@ Folder::Folder(const QString &alias, const QString &path, const QString& secondP
|
||||
|
||||
bool Folder::init()
|
||||
{
|
||||
QString url = Utility::toCSyncScheme(ownCloudInfo::instance()->webdavUrl() + secondPath());
|
||||
QString url = Utility::toCSyncScheme(remoteUrl().toString());
|
||||
QString localpath = path();
|
||||
|
||||
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url.toUtf8().data() ) < 0 ) {
|
||||
@@ -92,30 +105,40 @@ bool Folder::init()
|
||||
slotCSyncError(tr("Unable to create csync-context"));
|
||||
_csync_ctx = 0;
|
||||
} else {
|
||||
csync_set_log_callback( _csync_ctx, csyncLogCatcher );
|
||||
csync_set_log_verbosity(_csync_ctx, 11);
|
||||
csync_set_log_callback( csyncLogCatcher );
|
||||
csync_set_log_level( 11 );
|
||||
|
||||
MirallConfigFile cfgFile;
|
||||
csync_set_config_dir( _csync_ctx, cfgFile.configPath().toUtf8() );
|
||||
|
||||
csync_enable_conflictcopys(_csync_ctx);
|
||||
setIgnoredFiles();
|
||||
cfgFile.getCredentials()->syncContextPreInit(_csync_ctx);
|
||||
if (Account *account = AccountManager::instance()->account()) {
|
||||
account->credentials()->syncContextPreInit(_csync_ctx);
|
||||
} else {
|
||||
qDebug() << Q_FUNC_INFO << "No default Account object, huh?";
|
||||
}
|
||||
|
||||
if( csync_init( _csync_ctx ) < 0 ) {
|
||||
qDebug() << "Could not initialize csync!" << csync_get_error(_csync_ctx) << csync_get_error_string(_csync_ctx);
|
||||
slotCSyncError(CSyncThread::csyncErrorToString(csync_get_error(_csync_ctx), csync_get_error_string(_csync_ctx)));
|
||||
qDebug() << "Could not initialize csync!" << csync_get_status(_csync_ctx) << csync_get_status_string(_csync_ctx);
|
||||
QString errStr = CSyncThread::csyncErrorToString(CSYNC_STATUS(csync_get_status(_csync_ctx)));
|
||||
const char *errMsg = csync_get_status_string(_csync_ctx);
|
||||
if( errMsg ) {
|
||||
errStr += QLatin1String("<br/>");
|
||||
errStr += QString::fromUtf8(errMsg);
|
||||
}
|
||||
slotCSyncError(errStr);
|
||||
csync_destroy(_csync_ctx);
|
||||
_csync_ctx = 0;
|
||||
}
|
||||
}
|
||||
return _csync_ctx;
|
||||
}
|
||||
|
||||
Folder::~Folder()
|
||||
{
|
||||
if( _thread ) {
|
||||
_thread->quit();
|
||||
csync_request_abort(_csync_ctx);
|
||||
_csync->abort();
|
||||
_thread->wait();
|
||||
}
|
||||
delete _csync;
|
||||
@@ -178,9 +201,23 @@ bool Folder::isBusy() const
|
||||
return ( _thread && _thread->isRunning() );
|
||||
}
|
||||
|
||||
QString Folder::secondPath() const
|
||||
QString Folder::remotePath() const
|
||||
{
|
||||
return _secondPath;
|
||||
return _remotePath;
|
||||
}
|
||||
|
||||
QUrl Folder::remoteUrl() const
|
||||
{
|
||||
Account *account = AccountManager::instance()->account();
|
||||
QUrl url = account->davUrl();
|
||||
QString path = url.path();
|
||||
if (!path.endsWith('/')) {
|
||||
path.append('/');
|
||||
}
|
||||
path.append(_remotePath);
|
||||
url.setPath(path);
|
||||
qDebug() << url;
|
||||
return url;
|
||||
}
|
||||
|
||||
QString Folder::nativePath() const
|
||||
@@ -196,15 +233,13 @@ bool Folder::syncEnabled() const
|
||||
void Folder::setSyncEnabled( bool doit )
|
||||
{
|
||||
_enabled = doit;
|
||||
_watcher->setEventsEnabled( doit );
|
||||
|
||||
qDebug() << "setSyncEnabled - ############################ " << doit;
|
||||
if( doit ) {
|
||||
// undefined until next sync
|
||||
_syncResult.setStatus( SyncResult::NotYetStarted);
|
||||
_syncResult.clearErrors();
|
||||
evaluateSync( QStringList() );
|
||||
// qDebug() << "Syncing enabled on folder " << name();
|
||||
} else {
|
||||
// do not stop or start the watcher here, that is done internally by
|
||||
// folder class. Even if the watcher fires, the folder does not
|
||||
// schedule itself because it checks the var. _enabled before.
|
||||
_pollTimer.stop();
|
||||
}
|
||||
}
|
||||
@@ -227,22 +262,25 @@ void Folder::evaluateSync(const QStringList &/*pathList*/)
|
||||
}
|
||||
|
||||
_syncResult.setStatus( SyncResult::NotYetStarted );
|
||||
_syncResult.clearErrors();
|
||||
emit scheduleToSync( alias() );
|
||||
|
||||
}
|
||||
|
||||
void Folder::slotPollTimerTimeout()
|
||||
{
|
||||
qDebug() << "* Polling" << alias() << "for changes. (time since next sync:" << (_timeSinceLastSync.elapsed() / 1000) << "s)";
|
||||
qDebug() << "* Polling" << alias() << "for changes. (time since last sync:" << (_timeSinceLastSync.elapsed() / 1000) << "s)";
|
||||
|
||||
if (quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval()) {
|
||||
qDebug() << "* Force Sync now";
|
||||
if (quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval() ||
|
||||
_syncResult.status() != SyncResult::Success ) {
|
||||
qDebug() << "** Force Sync now";
|
||||
evaluateSync(QStringList());
|
||||
} else {
|
||||
RequestEtagJob* job = new RequestEtagJob(secondPath(), this);
|
||||
RequestEtagJob* job = new RequestEtagJob(AccountManager::instance()->account(), remotePath(), this);
|
||||
// check if the etag is different
|
||||
QObject::connect(job, SIGNAL(etagRetreived(QString)), this, SLOT(etagRetreived(QString)));
|
||||
QObject::connect(job, SIGNAL(networkError()), this, SLOT(slotNetworkUnavailable()));
|
||||
QObject::connect(job, SIGNAL(networkError(QNetworkReply*)), this, SLOT(slotNetworkUnavailable()));
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,6 +299,7 @@ void Folder::etagRetreived(const QString& etag)
|
||||
|
||||
void Folder::slotNetworkUnavailable()
|
||||
{
|
||||
AccountManager::instance()->account()->setState(Account::Disconnected);
|
||||
_syncResult.setStatus(SyncResult::Unavailable);
|
||||
emit syncStateChange();
|
||||
}
|
||||
@@ -278,17 +317,18 @@ void Folder::bubbleUpSyncResult()
|
||||
int removedItems = 0;
|
||||
int updatedItems = 0;
|
||||
int ignoredItems = 0;
|
||||
int renamedItems = 0;
|
||||
|
||||
SyncFileItem firstItemNew;
|
||||
SyncFileItem firstItemDeleted;
|
||||
SyncFileItem firstItemUpdated;
|
||||
|
||||
SyncFileItem firstItemRenamed;
|
||||
Logger *logger = Logger::instance();
|
||||
|
||||
foreach (const SyncFileItem &item, _syncResult.syncFileItemVector() ) {
|
||||
if( item._instruction == CSYNC_INSTRUCTION_ERROR ) {
|
||||
if( item._status == SyncFileItem::FatalError || item._status == SyncFileItem::NormalError ) {
|
||||
slotCSyncError( tr("File %1: %2").arg(item._file).arg(item._errorString) );
|
||||
logger->postGuiLog(tr("File %1").arg(item._file), item._errorString);
|
||||
logger->postOptionalGuiLog(tr("File %1").arg(item._file), item._errorString);
|
||||
|
||||
} else {
|
||||
if (item._dir == SyncFileItem::Down) {
|
||||
@@ -313,7 +353,8 @@ void Folder::bubbleUpSyncResult()
|
||||
}
|
||||
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
updatedItems++;
|
||||
if (firstItemUpdated.isEmpty())
|
||||
firstItemUpdated = item;
|
||||
@@ -321,6 +362,12 @@ void Folder::bubbleUpSyncResult()
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
qDebug() << "Got Instruction ERROR. " << _syncResult.errorString();
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
if (firstItemRenamed.isEmpty()) {
|
||||
firstItemRenamed = item;
|
||||
}
|
||||
renamedItems++;
|
||||
break;
|
||||
default:
|
||||
// nothing.
|
||||
break;
|
||||
@@ -335,33 +382,50 @@ void Folder::bubbleUpSyncResult()
|
||||
|
||||
_syncResult.setWarnCount(ignoredItems);
|
||||
|
||||
|
||||
createGuiLog( firstItemNew._file, tr("downloaded"), newItems );
|
||||
createGuiLog( firstItemDeleted._file, tr("removed"), removedItems );
|
||||
createGuiLog( firstItemUpdated._file, tr("updated"), updatedItems );
|
||||
|
||||
if( !firstItemRenamed.isEmpty() ) {
|
||||
QString renameVerb = tr("renamed");
|
||||
// if the path changes it's rather a move
|
||||
QDir renTarget = QFileInfo(firstItemRenamed._renameTarget).dir();
|
||||
QDir renSource = QFileInfo(firstItemRenamed._file).dir();
|
||||
if(renTarget != renSource) {
|
||||
renameVerb = tr("moved");
|
||||
}
|
||||
createGuiLog( firstItemRenamed._file, tr("%1 to %2").arg(renameVerb).arg(firstItemRenamed._renameTarget), renamedItems );
|
||||
}
|
||||
|
||||
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
||||
if (newItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemNew._file);
|
||||
if (newItems == 1)
|
||||
logger->postGuiLog(tr("New file available"), tr("'%1' has been synced to this machine.").arg(file));
|
||||
else
|
||||
logger->postGuiLog(tr("New files available"), tr("'%1' and %n other file(s) have been synced to this machine.",
|
||||
"", newItems-1).arg(file));
|
||||
}
|
||||
if (removedItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemDeleted._file);
|
||||
if (removedItems == 1)
|
||||
logger->postGuiLog(tr("File removed"), tr("'%1' has been removed.").arg(file));
|
||||
else
|
||||
logger->postGuiLog(tr("Files removed"), tr("'%1' and %n other file(s) have been removed.",
|
||||
"", removedItems-1).arg(file));
|
||||
}
|
||||
if (updatedItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemUpdated._file);
|
||||
if (updatedItems == 1)
|
||||
logger->postGuiLog(tr("File updated"), tr("'%1' has been updated.").arg(file));
|
||||
else
|
||||
logger->postGuiLog(tr("Files updated"), tr("'%1' and %n other file(s) have been updated.",
|
||||
"", updatedItems-1).arg(file));
|
||||
}
|
||||
|
||||
void Folder::createGuiLog( const QString& filename, const QString& verb, int count )
|
||||
{
|
||||
if(count > 0) {
|
||||
Logger *logger = Logger::instance();
|
||||
|
||||
QString file = QDir::toNativeSeparators(filename);
|
||||
if (count == 1) {
|
||||
logger->postOptionalGuiLog(tr("File %1").arg(verb), tr("'%1' has been %2.").arg(file).arg(verb));
|
||||
} else {
|
||||
logger->postOptionalGuiLog(tr("Files %1").arg(verb),
|
||||
tr("'%1' and %2 other files have been %3.").arg(file).arg(count-1).arg(verb));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Folder::blackListEntryCount()
|
||||
{
|
||||
return _journal.blackListEntryCount();
|
||||
}
|
||||
|
||||
int Folder::slotWipeBlacklist()
|
||||
{
|
||||
return _journal.wipeBlacklist();
|
||||
}
|
||||
|
||||
void Folder::slotLocalPathChanged( const QString& dir )
|
||||
{
|
||||
QDir notifiedDir(dir);
|
||||
@@ -399,39 +463,31 @@ void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
|
||||
|
||||
void Folder::slotCatchWatcherError(const QString& error)
|
||||
{
|
||||
Logger::instance()->postGuiLog(tr("Error"), error);
|
||||
Logger::instance()->postOptionalGuiLog(tr("Error"), error);
|
||||
}
|
||||
|
||||
void Folder::slotTerminateSync()
|
||||
void Folder::slotTerminateSync(bool block)
|
||||
{
|
||||
qDebug() << "folder " << alias() << " Terminating!";
|
||||
MirallConfigFile cfg;
|
||||
QString configDir = cfg.configPath();
|
||||
qDebug() << "csync's Config Dir: " << configDir;
|
||||
|
||||
if( _thread && _csync ) {
|
||||
csync_request_abort(_csync_ctx);
|
||||
_thread->quit();
|
||||
_csync->abort();
|
||||
|
||||
// Do not display an error message, user knows his own actions.
|
||||
// _errors.append( tr("The CSync thread terminated.") );
|
||||
// _csyncError = true;
|
||||
if (!block) {
|
||||
setSyncState(SyncResult::SyncAbortRequested);
|
||||
return;
|
||||
}
|
||||
|
||||
_thread->wait();
|
||||
_csync->deleteLater();
|
||||
delete _thread;
|
||||
_csync = 0;
|
||||
_thread = 0;
|
||||
csync_resume(_csync_ctx);
|
||||
slotCSyncFinished();
|
||||
}
|
||||
|
||||
if( ! configDir.isEmpty() ) {
|
||||
QFile file( configDir + QLatin1String("/lock"));
|
||||
if( file.exists() ) {
|
||||
qDebug() << "After termination, lock file exists and gets removed.";
|
||||
file.remove();
|
||||
}
|
||||
}
|
||||
|
||||
_errors.append( tr("The CSync thread terminated.") );
|
||||
_csyncError = true;
|
||||
qDebug() << "-> CSync Terminated!";
|
||||
slotCSyncFinished();
|
||||
setSyncEnabled(false);
|
||||
}
|
||||
|
||||
// This removes the csync File database if the sync folder definition is removed
|
||||
@@ -442,6 +498,8 @@ void Folder::wipe()
|
||||
{
|
||||
QString stateDbFile = path()+QLatin1String(".csync_journal.db");
|
||||
|
||||
_journal.close(); // close the sync journal
|
||||
|
||||
QFile file(stateDbFile);
|
||||
if( file.exists() ) {
|
||||
if( !file.remove()) {
|
||||
@@ -478,31 +536,37 @@ void Folder::setIgnoredFiles()
|
||||
|
||||
void Folder::setProxy()
|
||||
{
|
||||
if( _csync_ctx ) {
|
||||
/* Store proxy */
|
||||
QUrl proxyUrl(ownCloudInfo::instance()->webdavUrl());
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(proxyUrl);
|
||||
// We set at least one in Application
|
||||
Q_ASSERT(proxies.count() > 0);
|
||||
QNetworkProxy proxy = proxies.first();
|
||||
if (proxy.type() == QNetworkProxy::NoProxy) {
|
||||
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
|
||||
} else {
|
||||
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
|
||||
<< " to csync for" << proxyUrl;
|
||||
}
|
||||
int proxyPort = proxy.port();
|
||||
|
||||
csync_set_module_property(_csync_ctx, "proxy_type", (char*) proxyTypeToCStr(proxy.type()) );
|
||||
csync_set_module_property(_csync_ctx, "proxy_host", proxy.hostName().toUtf8().data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_port", &proxyPort );
|
||||
csync_set_module_property(_csync_ctx, "proxy_user", proxy.user().toUtf8().data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_pwd" , proxy.password().toUtf8().data() );
|
||||
|
||||
FolderMan::instance()->setDirtyProxy(false);
|
||||
/* Store proxy */
|
||||
QUrl proxyUrl(AccountManager::instance()->account()->url());
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(QNetworkProxyQuery(proxyUrl));
|
||||
// We set at least one in Application
|
||||
Q_ASSERT(proxies.count() > 0);
|
||||
QNetworkProxy proxy = proxies.first();
|
||||
if (proxy.type() == QNetworkProxy::NoProxy) {
|
||||
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
|
||||
} else {
|
||||
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
|
||||
<< " to csync for" << proxyUrl;
|
||||
}
|
||||
_proxy_type = proxyTypeToCStr(proxy.type());
|
||||
_proxy_host = proxy.hostName().toUtf8();
|
||||
_proxy_port = proxy.port();
|
||||
_proxy_user = proxy.user().toUtf8();
|
||||
_proxy_pwd = proxy.password().toUtf8();
|
||||
|
||||
setProxyDirty(false);
|
||||
}
|
||||
|
||||
void Folder::setProxyDirty(bool value)
|
||||
{
|
||||
_proxyDirty = value;
|
||||
}
|
||||
|
||||
bool Folder::proxyDirty()
|
||||
{
|
||||
return _proxyDirty;
|
||||
}
|
||||
|
||||
const char* Folder::proxyTypeToCStr(QNetworkProxy::ProxyType type)
|
||||
{
|
||||
@@ -530,7 +594,6 @@ void Folder::startSync(const QStringList &pathList)
|
||||
if (!_csync_ctx) {
|
||||
// no _csync_ctx yet, initialize it.
|
||||
init();
|
||||
setProxy();
|
||||
|
||||
if (!_csync_ctx) {
|
||||
qDebug() << Q_FUNC_INFO << "init failed.";
|
||||
@@ -538,9 +601,15 @@ void Folder::startSync(const QStringList &pathList)
|
||||
QMetaObject::invokeMethod(this, "slotCSyncFinished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
} else if (FolderMan::instance()->isDirtyProxy()) {
|
||||
setProxy();
|
||||
} else if (proxyDirty()) {
|
||||
setProxy();
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "proxy_type", const_cast<char*>(_proxy_type) );
|
||||
csync_set_module_property(_csync_ctx, "proxy_host", _proxy_host.data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_port", &_proxy_port );
|
||||
csync_set_module_property(_csync_ctx, "proxy_user", _proxy_user.data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_pwd", _proxy_pwd.data() );
|
||||
|
||||
if (_thread && _thread->isRunning()) {
|
||||
qCritical() << "* ERROR csync is still running and new sync requested.";
|
||||
@@ -561,12 +630,10 @@ void Folder::startSync(const QStringList &pathList)
|
||||
|
||||
qDebug() << "*** Start syncing";
|
||||
_thread = new QThread(this);
|
||||
_thread->setPriority(QThread::LowPriority);
|
||||
setIgnoredFiles();
|
||||
_csync = new CSyncThread( _csync_ctx );
|
||||
_csync = new CSyncThread( _csync_ctx, path(), remoteUrl().path(), &_journal);
|
||||
_csync->moveToThread(_thread);
|
||||
|
||||
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
@@ -582,8 +649,11 @@ void Folder::startSync(const QStringList &pathList)
|
||||
connect(_csync, SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), Qt::BlockingQueuedConnection);
|
||||
connect(_csync, SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
|
||||
connect(_csync, SIGNAL(transmissionProblem(Progress::SyncProblem)), this, SLOT(slotTransmissionProblem(Progress::SyncProblem)));
|
||||
|
||||
_thread->start();
|
||||
_thread->setPriority(QThread::LowPriority);
|
||||
|
||||
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
|
||||
|
||||
// disable events until syncing is done
|
||||
@@ -612,7 +682,7 @@ void Folder::slotCsyncUnavailable()
|
||||
|
||||
void Folder::slotCSyncFinished()
|
||||
{
|
||||
qDebug() << "-> CSync Finished slot with error " << _csyncError;
|
||||
qDebug() << "-> CSync Finished slot with error " << _csyncError << "warn count" << _syncResult.warnCount();
|
||||
_watcher->setEventsEnabledDelayed(2000);
|
||||
_pollTimer.start();
|
||||
_timeSinceLastSync.restart();
|
||||
@@ -637,10 +707,35 @@ void Folder::slotCSyncFinished()
|
||||
_thread->quit();
|
||||
}
|
||||
emit syncStateChange();
|
||||
ownCloudInfo::instance()->getQuotaRequest("/");
|
||||
emit syncFinished( _syncResult );
|
||||
}
|
||||
|
||||
// the problem comes without a folder and the valid path set. Add that here
|
||||
// and hand the result over to the progress dispatcher.
|
||||
void Folder::slotTransmissionProblem( const Progress::SyncProblem& problem )
|
||||
{
|
||||
Progress::SyncProblem newProb = problem;
|
||||
newProb.folder = alias();
|
||||
|
||||
if(newProb.current_file.startsWith(QLatin1String("ownclouds://")) ||
|
||||
newProb.current_file.startsWith(QLatin1String("owncloud://")) ) {
|
||||
// rip off the whole ownCloud URL.
|
||||
newProb.current_file.remove(Utility::toCSyncScheme(remoteUrl().toString()));
|
||||
}
|
||||
QString localPath = path();
|
||||
if( newProb.current_file.startsWith(localPath) ) {
|
||||
// remove the local dir.
|
||||
newProb.current_file = newProb.current_file.right( newProb.current_file.length() - localPath.length());
|
||||
}
|
||||
|
||||
// Count all error conditions.
|
||||
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
|
||||
|
||||
ProgressDispatcher::instance()->setProgressProblem(alias(), newProb);
|
||||
}
|
||||
|
||||
// the progress comes without a folder and the valid path set. Add that here
|
||||
// and hand the result over to the progress dispatcher.
|
||||
void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
{
|
||||
Progress::Info newInfo = progress;
|
||||
@@ -649,8 +744,7 @@ void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
if(newInfo.current_file.startsWith(QLatin1String("ownclouds://")) ||
|
||||
newInfo.current_file.startsWith(QLatin1String("owncloud://")) ) {
|
||||
// rip off the whole ownCloud URL.
|
||||
QString remotePathUrl = ownCloudInfo::instance()->webdavUrl() + secondPath();
|
||||
newInfo.current_file.remove(Utility::toCSyncScheme(remotePathUrl));
|
||||
newInfo.current_file.remove(Utility::toCSyncScheme(remoteUrl().toString()));
|
||||
}
|
||||
QString localPath = path();
|
||||
if( newInfo.current_file.startsWith(localPath) ) {
|
||||
@@ -662,9 +756,6 @@ void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
if( newInfo.kind == Progress::StartSync ) {
|
||||
_syncResult.setWarnCount(0);
|
||||
}
|
||||
if( newInfo.kind == Progress::Error ) {
|
||||
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
|
||||
}
|
||||
|
||||
ProgressDispatcher::instance()->setProgressInfo(alias(), newInfo);
|
||||
}
|
||||
@@ -693,6 +784,65 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *
|
||||
}
|
||||
}
|
||||
|
||||
SyncFileStatus Folder::fileStatus( const QString& fileName )
|
||||
{
|
||||
/*
|
||||
STATUS_NONE,
|
||||
+ STATUS_EVAL,
|
||||
STATUS_REMOVE, (invalid for this case because it asks for local files)
|
||||
STATUS_RENAME,
|
||||
+ STATUS_NEW,
|
||||
STATUS_CONFLICT,(probably also invalid as we know the conflict only with server involvement)
|
||||
+ STATUS_IGNORE,
|
||||
+ STATUS_SYNC,
|
||||
+ STATUS_STAT_ERROR,
|
||||
STATUS_ERROR,
|
||||
STATUS_UPDATED
|
||||
*/
|
||||
|
||||
// FIXME: Find a way for STATUS_ERROR
|
||||
SyncFileStatus stat = FILE_STATUS_NONE;
|
||||
|
||||
QString file = path() + fileName;
|
||||
QFileInfo fi(file);
|
||||
|
||||
if( !fi.exists() ) {
|
||||
stat = FILE_STATUS_STAT_ERROR; // not really possible.
|
||||
}
|
||||
|
||||
// file is ignored?
|
||||
if( fi.isSymLink() ) {
|
||||
stat = FILE_STATUS_IGNORE;
|
||||
}
|
||||
int type = CSYNC_FTW_TYPE_FILE;
|
||||
if( fi.isDir() ) {
|
||||
type = CSYNC_FTW_TYPE_DIR;
|
||||
}
|
||||
|
||||
if( stat == FILE_STATUS_NONE ) {
|
||||
CSYNC_EXCLUDE_TYPE excl = csync_excluded(_csync_ctx, file.toUtf8(), type);
|
||||
|
||||
if( excl != CSYNC_NOT_EXCLUDED ) {
|
||||
stat = FILE_STATUS_IGNORE;
|
||||
}
|
||||
}
|
||||
|
||||
SyncJournalFileRecord rec = _journal.getFileRecord(fileName);
|
||||
if( stat == FILE_STATUS_NONE && !rec.isValid() ) {
|
||||
stat = FILE_STATUS_NEW;
|
||||
}
|
||||
|
||||
// file was locally modified.
|
||||
if( stat == FILE_STATUS_NONE && fi.lastModified() != rec._modtime ) {
|
||||
stat = FILE_STATUS_EVAL;
|
||||
}
|
||||
|
||||
if( stat == FILE_STATUS_NONE ) {
|
||||
stat = FILE_STATUS_SYNC;
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/csyncthread.h"
|
||||
#include "mirall/syncjournaldb.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
@@ -40,28 +41,27 @@ namespace Mirall {
|
||||
class FolderWatcher;
|
||||
|
||||
typedef enum SyncFileStatus_s {
|
||||
STATUS_NONE,
|
||||
STATUS_EVAL,
|
||||
STATUS_REMOVE,
|
||||
STATUS_RENAME,
|
||||
STATUS_NEW,
|
||||
STATUS_CONFLICT,
|
||||
STATUS_IGNORE,
|
||||
STATUS_SYNC,
|
||||
STATUS_STAT_ERROR,
|
||||
STATUS_ERROR,
|
||||
STATUS_UPDATED
|
||||
FILE_STATUS_NONE,
|
||||
FILE_STATUS_EVAL,
|
||||
FILE_STATUS_REMOVE,
|
||||
FILE_STATUS_RENAME,
|
||||
FILE_STATUS_NEW,
|
||||
FILE_STATUS_CONFLICT,
|
||||
FILE_STATUS_IGNORE,
|
||||
FILE_STATUS_SYNC,
|
||||
FILE_STATUS_STAT_ERROR,
|
||||
FILE_STATUS_ERROR,
|
||||
FILE_STATUS_UPDATED
|
||||
} SyncFileStatus;
|
||||
|
||||
|
||||
class Folder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected:
|
||||
friend class FolderMan;
|
||||
public:
|
||||
Folder(const QString&, const QString&, const QString& , QObject*parent = 0L);
|
||||
|
||||
public:
|
||||
~Folder();
|
||||
|
||||
typedef QHash<QString, Folder*> Map;
|
||||
@@ -81,10 +81,16 @@ public:
|
||||
* local folder path
|
||||
*/
|
||||
QString path() const;
|
||||
|
||||
/**
|
||||
* remote folder path
|
||||
*/
|
||||
QString secondPath() const;
|
||||
QString remotePath() const;
|
||||
|
||||
/**
|
||||
* remote folder path with server url
|
||||
*/
|
||||
QUrl remoteUrl() const;
|
||||
|
||||
/**
|
||||
* local folder path with native separators
|
||||
@@ -137,8 +143,10 @@ public slots:
|
||||
|
||||
/**
|
||||
* terminate the current sync run
|
||||
*
|
||||
* If block is true, this will block synchroniously for the sync thread to finish.
|
||||
*/
|
||||
void slotTerminateSync();
|
||||
void slotTerminateSync(bool block);
|
||||
|
||||
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
|
||||
|
||||
@@ -150,6 +158,18 @@ public slots:
|
||||
*/
|
||||
void startSync(const QStringList &pathList = QStringList());
|
||||
|
||||
/**
|
||||
* Starts a sync (calling startSync)
|
||||
* if the policies allow for it
|
||||
*/
|
||||
void evaluateSync(const QStringList &pathList);
|
||||
|
||||
void setProxyDirty(bool value);
|
||||
bool proxyDirty();
|
||||
|
||||
int slotWipeBlacklist();
|
||||
int blackListEntryCount();
|
||||
|
||||
private slots:
|
||||
void slotCSyncStarted();
|
||||
void slotCSyncError(const QString& );
|
||||
@@ -157,6 +177,7 @@ private slots:
|
||||
void slotCSyncFinished();
|
||||
|
||||
void slotTransmissionProgress(const Progress::Info& progress);
|
||||
void slotTransmissionProblem( const Progress::SyncProblem& problem );
|
||||
|
||||
void slotPollTimerTimeout();
|
||||
void etagRetreived(const QString &);
|
||||
@@ -169,7 +190,7 @@ private slots:
|
||||
void slotThreadTreeWalkResult(const SyncFileItemVector& );
|
||||
void slotCatchWatcherError( const QString& );
|
||||
|
||||
protected:
|
||||
private:
|
||||
bool init();
|
||||
|
||||
void setSyncState(SyncResult::Status state);
|
||||
@@ -180,16 +201,12 @@ protected:
|
||||
|
||||
void bubbleUpSyncResult();
|
||||
|
||||
/**
|
||||
* Starts a sync (calling startSync)
|
||||
* if the policies allow for it
|
||||
*/
|
||||
void evaluateSync(const QStringList &pathList);
|
||||
|
||||
void checkLocalPath();
|
||||
|
||||
void createGuiLog( const QString& filename, const QString& verb, int count );
|
||||
|
||||
QString _path;
|
||||
QString _secondPath;
|
||||
QString _remotePath;
|
||||
QString _alias;
|
||||
QString _configFile;
|
||||
QFileSystemWatcher *_pathWatcher;
|
||||
@@ -202,13 +219,22 @@ protected:
|
||||
bool _csyncError;
|
||||
bool _csyncUnavail;
|
||||
bool _wipeDb;
|
||||
bool _proxyDirty;
|
||||
Progress::Kind _progressKind;
|
||||
QTimer _pollTimer;
|
||||
QString _lastEtag;
|
||||
QElapsedTimer _timeSinceLastSync;
|
||||
|
||||
SyncJournalDb _journal;
|
||||
|
||||
CSYNC *_csync_ctx;
|
||||
|
||||
const char *_proxy_type;
|
||||
QByteArray _proxy_host;
|
||||
int _proxy_port;
|
||||
QByteArray _proxy_user;
|
||||
QByteArray _proxy_pwd;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/inotify.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "owncloudinfo.h"
|
||||
|
||||
#include <neon/ne_socket.h>
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <CoreServices/CoreServices.h>
|
||||
@@ -37,8 +38,7 @@ FolderMan* FolderMan::_instance = 0;
|
||||
|
||||
FolderMan::FolderMan(QObject *parent) :
|
||||
QObject(parent),
|
||||
_syncEnabled( true ),
|
||||
_dirtyProxy( true )
|
||||
_syncEnabled( true )
|
||||
{
|
||||
// if QDir::mkpath would not be so stupid, I would not need to have this
|
||||
// duplication of folderConfigPath() here
|
||||
@@ -54,8 +54,10 @@ FolderMan::FolderMan(QObject *parent) :
|
||||
|
||||
FolderMan *FolderMan::instance()
|
||||
{
|
||||
if(!_instance)
|
||||
if(!_instance) {
|
||||
_instance = new FolderMan;
|
||||
ne_sock_init();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
@@ -63,6 +65,7 @@ FolderMan *FolderMan::instance()
|
||||
FolderMan::~FolderMan()
|
||||
{
|
||||
qDeleteAll(_folderMap);
|
||||
ne_sock_exit();
|
||||
}
|
||||
|
||||
Mirall::Folder::Map FolderMan::map()
|
||||
@@ -70,18 +73,6 @@ Mirall::Folder::Map FolderMan::map()
|
||||
return _folderMap;
|
||||
}
|
||||
|
||||
|
||||
int FolderMan::setupFolders()
|
||||
{
|
||||
// setup a handler to look for configuration changes
|
||||
return setupKnownFolders();
|
||||
}
|
||||
|
||||
void FolderMan::slotReparseConfiguration()
|
||||
{
|
||||
setupKnownFolders();
|
||||
}
|
||||
|
||||
int FolderMan::unloadAllFolders()
|
||||
{
|
||||
int cnt = 0;
|
||||
@@ -93,10 +84,11 @@ int FolderMan::unloadAllFolders()
|
||||
delete _folderMap.take( i.key() );
|
||||
cnt++;
|
||||
}
|
||||
_currentSyncFolder.clear();
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int FolderMan::setupKnownFolders()
|
||||
int FolderMan::setupFolders()
|
||||
{
|
||||
qDebug() << "* Setup folders from " << _folderConfigPath;
|
||||
|
||||
@@ -226,7 +218,7 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
return folder;
|
||||
}
|
||||
|
||||
QSettings settings( cfgFile.filePath(), QSettings::IniFormat);
|
||||
QSettings settings( _folderConfigPath + QLatin1Char('/') + escapedAlias, QSettings::IniFormat);
|
||||
qDebug() << " -> file path: " << settings.fileName();
|
||||
|
||||
// Check if the filename is equal to the group setting. If not, use the group
|
||||
@@ -239,9 +231,10 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
|
||||
settings.beginGroup( escapedAlias ); // read the group with the same name as the file which is the folder alias
|
||||
|
||||
QString path = settings.value(QLatin1String("localpath")).toString();
|
||||
QString path = settings.value(QLatin1String("localPath")).toString();
|
||||
QString backend = settings.value(QLatin1String("backend")).toString();
|
||||
QString targetPath = settings.value( QLatin1String("targetPath") ).toString();
|
||||
QString targetPath = settings.value( QLatin1String("targetPath")).toString();
|
||||
bool paused = settings.value( QLatin1String("paused"), false).toBool();
|
||||
// QString connection = settings.value( QLatin1String("connection") ).toString();
|
||||
QString alias = unescapeAlias( escapedAlias );
|
||||
|
||||
@@ -259,6 +252,9 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
folder->setConfigFile(file);
|
||||
qDebug() << "Adding folder to Folder Map " << folder;
|
||||
_folderMap[alias] = folder;
|
||||
if (paused) {
|
||||
_disabledFolders.insert(folder);
|
||||
}
|
||||
|
||||
/* Use a signal mapper to connect the signals to the alias */
|
||||
connect(folder, SIGNAL(scheduleToSync(const QString&)), SLOT(slotScheduleSync(const QString&)));
|
||||
@@ -280,6 +276,17 @@ void FolderMan::slotEnableFolder( const QString& alias, bool enable )
|
||||
Folder *f = _folderMap[alias];
|
||||
if( f ) {
|
||||
f->setSyncEnabled(enable);
|
||||
f->evaluateSync(QStringList());
|
||||
|
||||
QSettings settings(_folderConfigPath + QLatin1Char('/') + f->configFile(), QSettings::IniFormat);
|
||||
settings.beginGroup(escapeAlias(f->alias()));
|
||||
if (enable) {
|
||||
settings.remove("paused");
|
||||
_disabledFolders.remove(f);
|
||||
} else {
|
||||
settings.setValue("paused", true);
|
||||
_disabledFolders.insert(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,8 +301,7 @@ void FolderMan::terminateSyncProcess( const QString& alias )
|
||||
if( ! folderAlias.isEmpty() ) {
|
||||
Folder *f = _folderMap[folderAlias];
|
||||
if( f ) {
|
||||
f->slotTerminateSync();
|
||||
|
||||
f->slotTerminateSync(true);
|
||||
if(_currentSyncFolder == folderAlias )
|
||||
_currentSyncFolder.clear();
|
||||
}
|
||||
@@ -342,7 +348,7 @@ void FolderMan::slotScheduleSync( const QString& alias )
|
||||
|
||||
qDebug() << "Schedule folder " << alias << " to sync!";
|
||||
if( _currentSyncFolder == alias ) {
|
||||
// the current folder is currently syncing.
|
||||
qDebug() << " the current folder is currently syncing.";
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -361,6 +367,10 @@ void FolderMan::setSyncEnabled( bool enabled )
|
||||
QTimer::singleShot(200, this, SLOT(slotScheduleFolderSync()));
|
||||
}
|
||||
_syncEnabled = enabled;
|
||||
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
f->setSyncEnabled(enabled && !_disabledFolders.contains(f));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -384,10 +394,9 @@ void FolderMan::slotScheduleFolderSync()
|
||||
if( ! _scheduleQueue.isEmpty() ) {
|
||||
const QString alias = _scheduleQueue.dequeue();
|
||||
if( _folderMap.contains( alias ) ) {
|
||||
ownCloudInfo::instance()->getQuotaRequest("/");
|
||||
Folder *f = _folderMap[alias];
|
||||
_currentSyncFolder = alias;
|
||||
if (f->syncEnabled()) {
|
||||
if( f->syncEnabled() ) {
|
||||
_currentSyncFolder = alias;
|
||||
f->startSync( QStringList() );
|
||||
}
|
||||
}
|
||||
@@ -425,6 +434,23 @@ void FolderMan::addFolderDefinition(const QString& alias, const QString& sourceF
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
Folder *FolderMan::folderForPath(const QUrl &path)
|
||||
{
|
||||
QString absolutePath = path.toLocalFile();
|
||||
absolutePath.append("/");
|
||||
|
||||
foreach(Folder* folder, map().values())
|
||||
{
|
||||
if(absolutePath.startsWith(folder->path()))
|
||||
{
|
||||
qDebug() << "found folder: " << folder->path() << " for " << absolutePath;
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FolderMan::removeAllFolderDefinitions()
|
||||
{
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
@@ -467,7 +493,10 @@ void FolderMan::removeFolder( const QString& alias )
|
||||
qDebug() << "Remove folder config file " << file.fileName();
|
||||
file.remove();
|
||||
}
|
||||
f->deleteLater();
|
||||
// FIXME: this is a temporar dirty fix against a crash happening because
|
||||
// the csync owncloud module still has static components. Activate the
|
||||
// delete once the module is fixed.
|
||||
// f->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,6 +542,14 @@ bool FolderMan::startFromScratch( const QString& localFolder )
|
||||
return false;
|
||||
}
|
||||
|
||||
void FolderMan::setDirtyProxy(bool value)
|
||||
{
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
f->setProxyDirty(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
|
||||
{
|
||||
SyncResult overallResult(SyncResult::Undefined);
|
||||
@@ -538,6 +575,7 @@ SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
|
||||
case SyncResult::Unavailable:
|
||||
overallResult.setStatus( SyncResult::Unavailable );
|
||||
break;
|
||||
case SyncResult::Problem: // don't show the problem icon in tray.
|
||||
case SyncResult::Success:
|
||||
if( overallResult.status() == SyncResult::Undefined )
|
||||
overallResult.setStatus( SyncResult::Success );
|
||||
@@ -549,11 +587,9 @@ SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
|
||||
if ( overallResult.status() != SyncResult::Error )
|
||||
overallResult.setStatus( SyncResult::SetupError );
|
||||
break;
|
||||
case SyncResult::Problem:
|
||||
if ( overallResult.status() != SyncResult::Problem )
|
||||
overallResult.setStatus( SyncResult::Problem );
|
||||
case SyncResult::SyncAbortRequested:
|
||||
break;
|
||||
// no default case on purpose, check compiler warnings
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
}
|
||||
return overallResult;
|
||||
@@ -589,6 +625,9 @@ QString FolderMan::statusToString( SyncResult syncStatus, bool enabled ) const
|
||||
case SyncResult::SetupError:
|
||||
folderMessage = tr( "Setup Error." );
|
||||
break;
|
||||
case SyncResult::SyncAbortRequested:
|
||||
folderMessage = tr( "User Abort." );
|
||||
break;
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
if( !enabled ) {
|
||||
|
||||
@@ -50,6 +50,9 @@ public:
|
||||
*/
|
||||
void addFolderDefinition(const QString&, const QString&, const QString& );
|
||||
|
||||
/** Returns the folder which the file or directory stored in path is in */
|
||||
Folder* folderForPath(const QUrl& path);
|
||||
|
||||
/** Returns the folder by alias or NULL if no folder with the alias exists. */
|
||||
Folder *folder( const QString& );
|
||||
|
||||
@@ -96,8 +99,6 @@ public slots:
|
||||
void slotFolderSyncStarted();
|
||||
void slotFolderSyncFinished( const SyncResult& );
|
||||
|
||||
void slotReparseConfiguration();
|
||||
|
||||
void terminateSyncProcess( const QString& alias = QString::null );
|
||||
|
||||
/* delete all folder objects */
|
||||
@@ -109,8 +110,7 @@ public slots:
|
||||
|
||||
void slotScheduleAllFolders();
|
||||
|
||||
bool isDirtyProxy() { return _dirtyProxy; }
|
||||
void setDirtyProxy(bool value = true) { _dirtyProxy = value; }
|
||||
void setDirtyProxy(bool value = true);
|
||||
|
||||
private slots:
|
||||
// slot to add a folder to the syncing queue
|
||||
@@ -122,7 +122,6 @@ private slots:
|
||||
private:
|
||||
// finds all folder configuration files
|
||||
// and create the folders
|
||||
int setupKnownFolders();
|
||||
void terminateCurrentSync();
|
||||
QString getBackupName( const QString& ) const;
|
||||
|
||||
@@ -133,6 +132,7 @@ private:
|
||||
|
||||
void removeFolder( const QString& );
|
||||
|
||||
QSet<Folder*> _disabledFolders;
|
||||
Folder::Map _folderMap;
|
||||
QString _folderConfigPath;
|
||||
QSignalMapper *_folderChangeSignalMapper;
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
|
||||
#include <QtWidgets>
|
||||
#endif
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
@@ -81,8 +84,9 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem & option ,
|
||||
h += aliasMargin; // bottom margin
|
||||
|
||||
// add some space to show an error condition.
|
||||
if( ! qvariant_cast<QString>(index.data(FolderErrorMsg)).isEmpty() ) {
|
||||
h += aliasMargin*2+fm.height();
|
||||
if( ! qvariant_cast<QStringList>(index.data(FolderErrorMsg)).isEmpty() ) {
|
||||
QStringList errMsgs = qvariant_cast<QStringList>(index.data(FolderErrorMsg));
|
||||
h += aliasMargin*2 + errMsgs.count()*fm.height();
|
||||
}
|
||||
|
||||
if( qvariant_cast<bool>(index.data(AddProgressSpace)) ) {
|
||||
@@ -122,7 +126,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
QString aliasText = qvariant_cast<QString>(index.data(FolderAliasRole));
|
||||
QString pathText = qvariant_cast<QString>(index.data(FolderPathRole));
|
||||
QString remotePath = qvariant_cast<QString>(index.data(FolderSecondPathRole));
|
||||
QString errorText = qvariant_cast<QString>(index.data(FolderErrorMsg));
|
||||
QStringList errorTexts= qvariant_cast<QStringList>(index.data(FolderErrorMsg));
|
||||
|
||||
int overallPercent = qvariant_cast<int>(index.data(SyncProgressOverallPercent));
|
||||
QString overallString = qvariant_cast<QString>(index.data(SyncProgressOverallString));
|
||||
@@ -211,12 +215,12 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
// paint an error overlay if there is an error string
|
||||
|
||||
int h = iconRect.bottom();
|
||||
if( !errorText.isEmpty() ) {
|
||||
if( !errorTexts.isEmpty() ) {
|
||||
h += aliasMargin;
|
||||
QRect errorRect = localPathRect;
|
||||
errorRect.setLeft( iconRect.left());
|
||||
errorRect.setTop( h );
|
||||
errorRect.setHeight(subFm.height()+aliasMargin);
|
||||
errorRect.setHeight(errorTexts.count() * subFm.height()+aliasMargin);
|
||||
errorRect.setRight( option.rect.right()-aliasMargin );
|
||||
|
||||
painter->setBrush( QColor(0xbb, 0x4d, 0x4d) );
|
||||
@@ -226,15 +230,16 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
painter->setPen( Qt::white );
|
||||
painter->setFont(errorFont);
|
||||
QRect errorTextRect = errorRect;
|
||||
errorTextRect.setLeft( errorTextRect.left()+aliasMargin +16);
|
||||
errorTextRect.setLeft( errorTextRect.left()+aliasMargin );
|
||||
errorTextRect.setTop( errorTextRect.top()+aliasMargin/2 );
|
||||
|
||||
int linebreak = errorText.indexOf(QLatin1String("<br"));
|
||||
QString eText = errorText;
|
||||
if(linebreak) {
|
||||
eText = errorText.left(linebreak);
|
||||
int x = errorTextRect.left();
|
||||
int y = errorTextRect.top()+aliasMargin/2 + subFm.height()/2;
|
||||
|
||||
foreach( QString eText, errorTexts ) {
|
||||
painter->drawText(x, y, subFm.elidedText( eText, Qt::ElideLeft, errorTextRect.width()-2*aliasMargin));
|
||||
y += subFm.height();
|
||||
}
|
||||
painter->drawText(errorTextRect, eText);
|
||||
|
||||
h = errorRect.bottom();
|
||||
}
|
||||
@@ -249,8 +254,8 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
painter->save();
|
||||
|
||||
// Sizes-Text
|
||||
QRect octetRect = subFm.boundingRect( overallString );
|
||||
int progressTextWidth = octetRect.width();
|
||||
QRect octetRect = progressFm.boundingRect( overallString );
|
||||
int progressTextWidth = octetRect.width() + 2;
|
||||
|
||||
// Overall Progress Bar.
|
||||
QRect pBRect;
|
||||
@@ -279,8 +284,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
overallProgressRect.setWidth( progressTextWidth );
|
||||
painter->setFont(progressFont);
|
||||
|
||||
QString elidedText = progressFm.elidedText(overallString, Qt::ElideLeft, overallProgressRect.width());
|
||||
painter->drawText( overallProgressRect, Qt::AlignRight+Qt::AlignVCenter, elidedText);
|
||||
painter->drawText( overallProgressRect, Qt::AlignRight+Qt::AlignVCenter, overallString);
|
||||
// painter->drawRect(overallProgressRect);
|
||||
|
||||
// Individual File Progress
|
||||
@@ -289,7 +293,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
|
||||
fileRect.setLeft( iconRect.left());
|
||||
fileRect.setWidth(overallWidth);
|
||||
fileRect.setHeight(fileNameTextHeight);
|
||||
elidedText = progressFm.elidedText(itemString, Qt::ElideLeft, fileRect.width());
|
||||
QString elidedText = progressFm.elidedText(itemString, Qt::ElideLeft, fileRect.width());
|
||||
|
||||
painter->drawText( fileRect, Qt::AlignLeft+Qt::AlignVCenter, elidedText);
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ void FolderWatcher::addIgnoreListFile( const QString& file )
|
||||
|
||||
while (!infile.atEnd()) {
|
||||
QString line = QString::fromLocal8Bit( infile.readLine() ).trimmed();
|
||||
if( !line.startsWith( QLatin1Char('#') ) && line.isEmpty() ) {
|
||||
if( !(line.startsWith( QLatin1Char('#') ) || line.isEmpty()) ) {
|
||||
_ignores.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path)
|
||||
qDebug() << "(+) Watcher:" << path;
|
||||
|
||||
if (!_inotify->addPath(path)) {
|
||||
FolderWatcher *fw = qobject_cast<FolderWatcher*>(parent());
|
||||
emit fw->error(tr("Could not monitor directories due to system limitations.\n"
|
||||
emit _parent->error(tr("Could not monitor directories due to system limitations.\n"
|
||||
"The application will not work reliably. Please check the\n"
|
||||
"documentation for possible fixes."));
|
||||
}
|
||||
@@ -133,7 +132,16 @@ void FolderWatcherPrivate::slotINotifyEvent(int mask, int /*cookie*/, const QStr
|
||||
//qDebug() << cookie << " OTHER " << mask << " :" << path;
|
||||
}
|
||||
|
||||
foreach (const QString& pattern, _parent->ignores()) {
|
||||
QStringList ignores = _parent->ignores();
|
||||
|
||||
if( path.endsWith(".csync_journal.db.ctmp") ||
|
||||
path.endsWith(".csync_journal.db.ctmp-journal") ||
|
||||
path.endsWith(".csync_journal.db")) {
|
||||
qDebug() << " ** Inotify ignored for " <<path;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (const QString& pattern, ignores) {
|
||||
QRegExp regexp(pattern);
|
||||
regexp.setPatternSyntax(QRegExp::Wildcard);
|
||||
|
||||
|
||||
@@ -13,9 +13,11 @@
|
||||
*/
|
||||
|
||||
#include "mirall/folderwizard.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
@@ -34,8 +36,24 @@
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
QString FormatWarningsWizardPage::formatWarnings(const QStringList &warnings) const
|
||||
{
|
||||
QString ret;
|
||||
if (warnings.count() == 1) {
|
||||
ret = tr("<b>Warning:</b> ") + warnings.first();
|
||||
} else if (warnings.count() > 1) {
|
||||
ret = tr("<b>Warning:</b> ") + "<ul>";
|
||||
Q_FOREACH(QString warning, warnings) {
|
||||
ret += QString::fromLatin1("<li>%1</li>").arg(warning);
|
||||
}
|
||||
ret += "</ul>";
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
FolderWizardSourcePage::FolderWizardSourcePage()
|
||||
: QWizardPage()
|
||||
: FormatWarningsWizardPage()
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
registerField(QLatin1String("sourceFolder*"), _ui.localFolderLineEdit);
|
||||
@@ -43,7 +61,7 @@ FolderWizardSourcePage::FolderWizardSourcePage()
|
||||
_ui.localFolderLineEdit->setText( QDir::toNativeSeparators( defaultPath ) );
|
||||
registerField(QLatin1String("alias*"), _ui.aliasLineEdit);
|
||||
_ui.aliasLineEdit->setText( Theme::instance()->appNameGUI() );
|
||||
|
||||
_ui.warnLabel->setTextFormat(Qt::RichText);
|
||||
_ui.warnLabel->hide();
|
||||
}
|
||||
|
||||
@@ -67,16 +85,16 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
QFileInfo selFile( QDir::fromNativeSeparators(_ui.localFolderLineEdit->text()) );
|
||||
QString userInput = selFile.canonicalFilePath();
|
||||
|
||||
QString warnString;
|
||||
QStringList warnStrings;
|
||||
|
||||
bool isOk = selFile.isDir();
|
||||
if( !isOk ) {
|
||||
warnString = tr("No local folder selected!");
|
||||
warnStrings.append(tr("No valid local folder selected!"));
|
||||
}
|
||||
|
||||
if (isOk && !selFile.isWritable()) {
|
||||
isOk = false;
|
||||
warnString += tr("You have no permission to write to the selected folder!");
|
||||
warnStrings.append(tr("You have no permission to write to the selected folder!"));
|
||||
}
|
||||
|
||||
// check if the local directory isn't used yet in another ownCloud sync
|
||||
@@ -97,21 +115,40 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
if( ! folderDir.endsWith(QLatin1Char('/')) ) folderDir.append(QLatin1Char('/'));
|
||||
|
||||
qDebug() << "Checking local path: " << folderDir << " <-> " << userInput;
|
||||
if( QFileInfo( f->path() ) == userInput ) {
|
||||
if( QDir::cleanPath(f->path()) == QDir::cleanPath(userInput) &&
|
||||
QDir::cleanPath(QDir(f->path()).canonicalPath()) == QDir(userInput).canonicalPath() ) {
|
||||
isOk = false;
|
||||
warnString.append( tr("The local path %1 is already an upload folder.<br/>Please pick another one!")
|
||||
warnStrings.append( tr("The local path %1 is already an upload folder. Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(userInput)) );
|
||||
}
|
||||
if( isOk && folderDir.startsWith( userInput )) {
|
||||
if( isOk && QDir::cleanPath(folderDir).startsWith(QDir::cleanPath(userInput)+'/') ) {
|
||||
qDebug() << "A already configured folder is child of the current selected";
|
||||
warnString.append( tr("An already configured folder is contained in the current entry."));
|
||||
warnStrings.append( tr("An already configured folder is contained in the current entry."));
|
||||
isOk = false;
|
||||
}
|
||||
if( isOk && userInput.startsWith( folderDir ) ) {
|
||||
|
||||
QString absCleanUserFolder = QDir::cleanPath(QDir(userInput).canonicalPath())+'/';
|
||||
if( isOk && QDir::cleanPath(folderDir).startsWith(absCleanUserFolder) ) {
|
||||
qDebug() << "A already configured folder is child of the current selected";
|
||||
warnStrings.append( tr("The selected folder is a symbolic link. An already configured"
|
||||
"folder is contained in the folder this link is pointing to."));
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
if( isOk && QDir::cleanPath(QString(userInput+'/')).startsWith( QDir::cleanPath(folderDir)) ) {
|
||||
qDebug() << "An already configured folder is parent of the current selected";
|
||||
warnString.append( tr("An already configured folder contains the currently entered directory."));
|
||||
warnStrings.append( tr("An already configured folder contains the currently entered folder."));
|
||||
isOk = false;
|
||||
}
|
||||
if( isOk && absCleanUserFolder.startsWith( QDir::cleanPath(folderDir)) ) {
|
||||
qDebug() << "The selected folder is a symbolic link. An already configured folder is\n"
|
||||
"the parent of the current selected contains the folder this link is pointing to.";
|
||||
warnStrings.append( tr("The selected folder is a symbolic link. An already configured folder "
|
||||
"is the parent of the current selected contains the folder this link is "
|
||||
"pointing to."));
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
@@ -119,7 +156,7 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
// check if the alias is unique.
|
||||
QString alias = _ui.aliasLineEdit->text();
|
||||
if( alias.isEmpty() ) {
|
||||
warnString.append( tr("The alias can not be empty. Please provide a descriptive alias word.") );
|
||||
warnStrings.append( tr("The alias can not be empty. Please provide a descriptive alias word.") );
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
@@ -130,7 +167,7 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
qDebug() << "Checking local alias: " << f->alias();
|
||||
if( f ) {
|
||||
if( f->alias() == alias ) {
|
||||
warnString.append( tr("<br/>The alias <i>%1</i> is already in use. Please pick another alias.").arg(alias) );
|
||||
warnStrings.append( tr("The alias <i>%1</i> is already in use. Please pick another alias.").arg(alias) );
|
||||
isOk = false;
|
||||
goon = false;
|
||||
}
|
||||
@@ -138,12 +175,14 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
i++;
|
||||
}
|
||||
|
||||
_ui.warnLabel->setWordWrap(true);
|
||||
if( isOk ) {
|
||||
_ui.warnLabel->hide();
|
||||
_ui.warnLabel->setText( QString::null );
|
||||
} else {
|
||||
_ui.warnLabel->show();
|
||||
_ui.warnLabel->setText( warnString );
|
||||
QString warnings = formatWarnings(warnStrings);
|
||||
_ui.warnLabel->setText( warnings );
|
||||
}
|
||||
return isOk;
|
||||
}
|
||||
@@ -166,7 +205,8 @@ void FolderWizardSourcePage::on_localFolderLineEdit_textChanged()
|
||||
|
||||
// =================================================================================
|
||||
FolderWizardTargetPage::FolderWizardTargetPage()
|
||||
: _warnWasVisible(false)
|
||||
: FormatWarningsWizardPage()
|
||||
,_warnWasVisible(false)
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
_ui.warnFrame->hide();
|
||||
@@ -175,6 +215,8 @@ FolderWizardTargetPage::FolderWizardTargetPage()
|
||||
connect(_ui.refreshButton, SIGNAL(clicked()), SLOT(slotRefreshFolders()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotAddRemoteFolder()
|
||||
@@ -195,24 +237,32 @@ void FolderWizardTargetPage::slotAddRemoteFolder()
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolder(QString folder)
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolder(const QString &folder)
|
||||
{
|
||||
if( folder.isEmpty() ) return;
|
||||
ownCloudInfo::instance()->mkdirRequest( folder );
|
||||
|
||||
MkColJob *job = new MkColJob(AccountManager::instance()->account(), folder, this);
|
||||
/* check the owncloud configuration file and query the ownCloud */
|
||||
connect(job, SIGNAL(finished(QNetworkReply::NetworkError)),
|
||||
SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
|
||||
connect(job, SIGNAL(networkError(QNetworkReply*)), SLOT(slotHandleNetworkError(QNetworkReply*)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolderFinished( QNetworkReply::NetworkError error )
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error)
|
||||
{
|
||||
qDebug() << "** webdav mkdir request finished " << error;
|
||||
if (error == QNetworkReply::NoError) {
|
||||
qDebug() << "** webdav mkdir request finished";
|
||||
showWarn(tr("Folder was successfully created on %1.").arg(Theme::instance()->appNameGUI()));
|
||||
slotRefreshFolders();
|
||||
}
|
||||
}
|
||||
|
||||
// the webDAV server seems to return a 202 even if mkdir was successful.
|
||||
if( error == QNetworkReply::NoError ||
|
||||
error == QNetworkReply::ContentOperationNotPermittedError) {
|
||||
showWarn( tr("Folder was successfully created on %1.").arg( Theme::instance()->appNameGUI() ) );
|
||||
slotRefreshFolders();
|
||||
} else {
|
||||
showWarn( tr("Failed to create the folder on %1.<br/>Please check manually.").arg( Theme::instance()->appNameGUI() ) );
|
||||
}
|
||||
void FolderWizardTargetPage::slotHandleNetworkError(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << "** webdav mkdir request failed:" << reply->error();
|
||||
showWarn(tr("Failed to create the folder on %1. Please check manually.")
|
||||
.arg(Theme::instance()->appNameGUI()));
|
||||
}
|
||||
|
||||
static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& text)
|
||||
@@ -226,11 +276,11 @@ static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& t
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path)
|
||||
void FolderWizardTargetPage::recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
if (pathTrail.size() == 0) {
|
||||
if (pathTrail.size() == 0) {
|
||||
if (path.endsWith('/')) {
|
||||
path.chop(1);
|
||||
}
|
||||
@@ -242,6 +292,7 @@ static void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QStr
|
||||
item = new QTreeWidgetItem(parent);
|
||||
item->setIcon(0, folderIcon);
|
||||
item->setText(0, pathTrail.first());
|
||||
item->setData(0, Qt::UserRole, pathTrail.first());
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
}
|
||||
|
||||
@@ -250,19 +301,15 @@ static void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QStr
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotUpdateDirectories(QStringList list)
|
||||
void FolderWizardTargetPage::slotUpdateDirectories(const QStringList &list)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
|
||||
QString webdavFolder = QUrl(ownCloudInfo::instance()->webdavUrl()).path();
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
QString webdavFolder = QUrl(AccountManager::instance()->account()->davUrl()).path();
|
||||
|
||||
QTreeWidgetItem *root = _ui.folderTreeWidget->topLevelItem(0);
|
||||
if (!root) {
|
||||
root = new QTreeWidgetItem(_ui.folderTreeWidget);
|
||||
root->setText(0, tr("Root (\"/\")", "root folder"));
|
||||
root->setIcon(0, folderIcon);
|
||||
root->setText(0, Theme::instance()->appNameGUI());
|
||||
root->setIcon(0, Theme::instance()->applicationIcon());
|
||||
root->setToolTip(0, tr("Choose this to sync the entire account"));
|
||||
root->setData(0, Qt::UserRole, "/");
|
||||
}
|
||||
@@ -277,13 +324,20 @@ void FolderWizardTargetPage::slotUpdateDirectories(QStringList list)
|
||||
|
||||
void FolderWizardTargetPage::slotRefreshFolders()
|
||||
{
|
||||
ownCloudInfo::instance()->getDirectoryListing("/");
|
||||
LsColJob *job = new LsColJob(AccountManager::instance()->account(), "/", this);
|
||||
connect(job, SIGNAL(directoryListing(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
job->start();
|
||||
_ui.folderTreeWidget->clear();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotItemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
ownCloudInfo::instance()->getDirectoryListing(item->text(0));
|
||||
QString dir = item->data(0, Qt::UserRole).toString();
|
||||
LsColJob *job = new LsColJob(AccountManager::instance()->account(), dir, this);
|
||||
connect(job, SIGNAL(directoryListing(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
job->start();
|
||||
}
|
||||
|
||||
FolderWizardTargetPage::~FolderWizardTargetPage()
|
||||
@@ -295,16 +349,36 @@ bool FolderWizardTargetPage::isComplete() const
|
||||
if (!_ui.folderTreeWidget->currentItem())
|
||||
return false;
|
||||
|
||||
QStringList warnStrings;
|
||||
QString dir = _ui.folderTreeWidget->currentItem()->data(0, Qt::UserRole).toString();
|
||||
if (!dir.startsWith(QLatin1Char('/'))) {
|
||||
dir.prepend(QLatin1Char('/'));
|
||||
}
|
||||
wizard()->setProperty("targetPath", dir);
|
||||
|
||||
if( dir == QLatin1String("/") ) {
|
||||
showWarn( tr("If you sync the root folder, you can <b>not</b> configure another sync directory."));
|
||||
return true;
|
||||
} else {
|
||||
showWarn();
|
||||
return true;
|
||||
Folder::Map map = _folderMap;
|
||||
Folder::Map::const_iterator i = map.constBegin();
|
||||
for(i = map.constBegin();i != map.constEnd(); i++ ) {
|
||||
Folder *f = static_cast<Folder*>(i.value());
|
||||
QString curDir = f->remotePath();
|
||||
if (!curDir.startsWith(QLatin1Char('/'))) {
|
||||
curDir.prepend(QLatin1Char('/'));
|
||||
}
|
||||
if (QDir::cleanPath(dir) == QDir::cleanPath(curDir)) {
|
||||
warnStrings.append(tr("This folder is already being synced."));
|
||||
} else if (dir.startsWith(curDir + QLatin1Char('/'))) {
|
||||
warnStrings.append(tr("You are already syncing <i>%1</i>, which is a parent folder of <i>%2</i>.").arg(curDir).arg(dir));
|
||||
}
|
||||
|
||||
if (curDir == QLatin1String("/")) {
|
||||
warnStrings.append(tr("You are already syncing all your files. Syncing another folder is <b>not</b> supported. "
|
||||
"If you want to sync multiple folders, please remove the currently configured "
|
||||
"root folder sync."));
|
||||
}
|
||||
}
|
||||
|
||||
showWarn(formatWarnings(warnStrings));
|
||||
return warnStrings.isEmpty();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::cleanupPage()
|
||||
@@ -315,19 +389,7 @@ void FolderWizardTargetPage::cleanupPage()
|
||||
void FolderWizardTargetPage::initializePage()
|
||||
{
|
||||
showWarn();
|
||||
|
||||
/* check the owncloud configuration file and query the ownCloud */
|
||||
ownCloudInfo *ocInfo = ownCloudInfo::instance();
|
||||
if( ocInfo->isConfigured() ) {
|
||||
connect( ocInfo, SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
SLOT(slotDirCheckReply(QString,QNetworkReply*)));
|
||||
connect( ocInfo, SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
SLOT(slotCreateRemoteFolderFinished( QNetworkReply::NetworkError )));
|
||||
connect( ocInfo, SIGNAL(directoryListingUpdated(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
|
||||
slotRefreshFolders();
|
||||
}
|
||||
slotRefreshFolders();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::showWarn( const QString& msg ) const
|
||||
@@ -352,6 +414,7 @@ FolderWizard::FolderWizard( QWidget *parent )
|
||||
_folderWizardSourcePage(new FolderWizardSourcePage),
|
||||
_folderWizardTargetPage(0)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
setPage(Page_Source, _folderWizardSourcePage );
|
||||
if (!Theme::instance()->singleSyncFolder()) {
|
||||
_folderWizardTargetPage = new FolderWizardTargetPage();
|
||||
@@ -370,6 +433,7 @@ FolderWizard::~FolderWizard()
|
||||
void FolderWizard::setFolderMap( const Folder::Map& fm)
|
||||
{
|
||||
_folderWizardSourcePage->setFolderMap( fm );
|
||||
_folderWizardTargetPage->setFolderMap( fm );
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
@@ -28,10 +28,15 @@ namespace Mirall {
|
||||
|
||||
class ownCloudInfo;
|
||||
|
||||
class FormatWarningsWizardPage : public QWizardPage {
|
||||
protected:
|
||||
QString formatWarnings(const QStringList &warnings) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* page to ask for the local source folder
|
||||
*/
|
||||
class FolderWizardSourcePage : public QWizardPage
|
||||
class FolderWizardSourcePage : public FormatWarningsWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -57,7 +62,7 @@ private:
|
||||
* page to ask for the target folder
|
||||
*/
|
||||
|
||||
class FolderWizardTargetPage : public QWizardPage
|
||||
class FolderWizardTargetPage : public FormatWarningsWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -69,20 +74,25 @@ public:
|
||||
virtual void initializePage();
|
||||
virtual void cleanupPage();
|
||||
|
||||
void setFolderMap( const Folder::Map &fm ) { _folderMap = fm; }
|
||||
|
||||
protected slots:
|
||||
|
||||
void showWarn( const QString& = QString() ) const;
|
||||
void slotAddRemoteFolder();
|
||||
void slotCreateRemoteFolder(QString);
|
||||
void slotCreateRemoteFolderFinished( QNetworkReply::NetworkError error );
|
||||
void slotUpdateDirectories(QStringList);
|
||||
void slotCreateRemoteFolder(const QString&);
|
||||
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError error);
|
||||
void slotHandleNetworkError(QNetworkReply*);
|
||||
void slotUpdateDirectories(const QStringList&);
|
||||
void slotRefreshFolders();
|
||||
void slotItemExpanded(QTreeWidgetItem*);
|
||||
private:
|
||||
void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path);
|
||||
Ui_FolderWizardTargetPage _ui;
|
||||
ownCloudInfo *_ownCloudDirCheck;
|
||||
bool _dirChecked;
|
||||
bool _warnWasVisible;
|
||||
Folder::Map _folderMap;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -145,9 +145,6 @@
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
|
||||
@@ -109,9 +109,6 @@
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
||||
@@ -53,12 +53,9 @@ GeneralSettings::GeneralSettings(QWidget *parent) :
|
||||
|
||||
// OEM themes are not obliged to ship mono icons, so there
|
||||
// is no point in offering an option
|
||||
QString themeDir = QString::fromLatin1(":/mirall/theme/1%1/")
|
||||
QString themeDir = QString::fromLatin1(":/mirall/theme/%1/")
|
||||
.arg(Theme::instance()->systrayIconFlavor(true));
|
||||
if (QDir(themeDir).exists())
|
||||
{
|
||||
_ui->monoIconsCheckBox->setVisible(false);
|
||||
}
|
||||
_ui->monoIconsCheckBox->setVisible(QDir(themeDir).exists());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<item>
|
||||
<widget class="QGroupBox" name="generalGroupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
<string>General Setttings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
@@ -77,7 +77,4 @@
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="proxyButtonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <QDir>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QColorGroup>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
|
||||
@@ -30,6 +29,7 @@ IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::IgnoreListEditor)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->descriptionLabel->setText(tr("Files or directories matching a pattern will not be synchronized.\n\n"
|
||||
|
||||
@@ -88,6 +88,7 @@ void INotify::slotActivated(int /*fd*/)
|
||||
// with the help of watch descriptor, retrieve, corresponding INotify
|
||||
if (event == NULL) {
|
||||
qDebug() << "NULL event";
|
||||
i += sizeof(struct inotify_event);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||