Compare commits
864 Commits
v1.3.0-bet
...
v1.5.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
18a58f73de | ||
|
|
d1451a3c90 | ||
|
|
0bef47b2f3 | ||
|
|
ccc05d6658 | ||
|
|
c15de69156 | ||
|
|
8bfb44fd28 | ||
|
|
79d3b84fad | ||
|
|
b97701586e | ||
|
|
b120345fe7 | ||
|
|
ec5c65a530 | ||
|
|
77e7a1fa8d | ||
|
|
f6d45b68ef | ||
|
|
24d76a0d75 | ||
|
|
c3326efe94 | ||
|
|
c0e056bb84 | ||
|
|
8a7df36701 | ||
|
|
39ec6b1bb2 | ||
|
|
30b2406533 | ||
|
|
076d5dda0e | ||
|
|
e78eab46ff | ||
|
|
19a08f8d5e | ||
|
|
c6319117fd | ||
|
|
4c10ed4ada | ||
|
|
b0ab3ca80b | ||
|
|
9f89a2fe76 | ||
|
|
e386bfb550 | ||
|
|
0189a3f1bd | ||
|
|
d3a333e03c | ||
|
|
04d3e282fb | ||
|
|
4be5f970d5 | ||
|
|
31ceff181e | ||
|
|
42abf4101c | ||
|
|
f09cae74fb | ||
|
|
13a6393ec5 | ||
|
|
3c2bb1e2bc | ||
|
|
24af9f38f4 | ||
|
|
ace57f9dd4 | ||
|
|
e5117a98c9 | ||
|
|
c1f7af7e6f | ||
|
|
b4c116e2a2 | ||
|
|
98efab83a1 | ||
|
|
a19a960b5e | ||
|
|
29d2094626 | ||
|
|
c3b82e6818 | ||
|
|
1a7c89326b | ||
|
|
3a1f04ac2d | ||
|
|
2c8e95a0f4 | ||
|
|
a9d94ef732 | ||
|
|
2756d17873 | ||
|
|
34d7f84fc2 | ||
|
|
cea9b389bf | ||
|
|
71b1e881d8 | ||
|
|
7ba47e9d44 | ||
|
|
be48cb646c | ||
|
|
47eb5ff1d9 | ||
|
|
b608f5a670 | ||
|
|
e81dc34c4e | ||
|
|
1603e627a3 | ||
|
|
bf978b3e0f | ||
|
|
0fdaf33288 | ||
|
|
367c4153c1 | ||
|
|
395c673a24 | ||
|
|
22de23c651 | ||
|
|
23926d2461 | ||
|
|
c03f31b6ba | ||
|
|
a1060a8538 | ||
|
|
24251bc223 | ||
|
|
f78749d2cd | ||
|
|
0995377d39 | ||
|
|
b9228e64ff | ||
|
|
c4084de716 | ||
|
|
3c667918e7 | ||
|
|
e55745cbcf | ||
|
|
e01ce20431 | ||
|
|
dce8cb83d9 | ||
|
|
8dc956c55b | ||
|
|
6c77921a32 | ||
|
|
55e4748f06 | ||
|
|
5ff9e02517 | ||
|
|
7f7ebc36f5 | ||
|
|
0e5bfc03ce | ||
|
|
b309d333a6 | ||
|
|
ee439382ed | ||
|
|
192212c682 | ||
|
|
148bdfdcd6 | ||
|
|
066c0ba189 | ||
|
|
e4128cd5d8 | ||
|
|
9651f1cddf | ||
|
|
5feb9b0806 | ||
|
|
44b9ee19e7 | ||
|
|
cc16d19bc4 | ||
|
|
b96c2de2b7 | ||
|
|
f882b80708 | ||
|
|
4d7c014b23 | ||
|
|
1f274699e5 | ||
|
|
697e355f07 | ||
|
|
f8e6326880 | ||
|
|
67c5f513aa | ||
|
|
0589bfc51b | ||
|
|
04f32bd397 | ||
|
|
e89bdfc422 | ||
|
|
c2cc9e62a7 | ||
|
|
1f2ae0d061 | ||
|
|
91a39588c6 | ||
|
|
a642f86d5b | ||
|
|
00cf290574 | ||
|
|
b59952b539 | ||
|
|
88b4ff9809 | ||
|
|
ddd0965a82 | ||
|
|
0a9491ff46 | ||
|
|
32b44e3d87 | ||
|
|
268004b4ff | ||
|
|
6c0f6ae62e | ||
|
|
094f2bb540 | ||
|
|
f4da7f1fb0 | ||
|
|
b058185d3b | ||
|
|
eecb981736 | ||
|
|
46b870e260 | ||
|
|
1ffab7337d | ||
|
|
82fb8c49cf | ||
|
|
ff2d59d32f | ||
|
|
0ced165b3e | ||
|
|
d3378c131a | ||
|
|
0300a85295 | ||
|
|
60a116f3e0 | ||
|
|
ff4d2d488f | ||
|
|
b0852b4cf5 | ||
|
|
c047b4d4de | ||
|
|
6655da9d24 | ||
|
|
cc6abfc366 | ||
|
|
4e3d4c3153 | ||
|
|
c323041040 | ||
|
|
95d600c5f1 | ||
|
|
52a47fbc67 | ||
|
|
4e7c069c1f | ||
|
|
f89ffa513a | ||
|
|
0086916b4d | ||
|
|
df5ef6fe67 | ||
|
|
c9e51dcfc1 | ||
|
|
c05adfd817 | ||
|
|
055410e38f | ||
|
|
6163de378e | ||
|
|
22938cd697 | ||
|
|
a493f81ec2 | ||
|
|
87cb2a7114 | ||
|
|
a875b46a80 | ||
|
|
704ba791fd | ||
|
|
c24d6bd71c | ||
|
|
9b319cf189 | ||
|
|
1a9eb19f0d | ||
|
|
1943cc60b6 | ||
|
|
761c05c358 | ||
|
|
b08c1ada02 | ||
|
|
dab17e381c | ||
|
|
114f66f297 | ||
|
|
f8878833de | ||
|
|
364d4340fd | ||
|
|
6226a6ee8f | ||
|
|
c49edeb09d | ||
|
|
7e794cd94f | ||
|
|
933a62de01 | ||
|
|
23f8e3b4f8 | ||
|
|
3502edf71b | ||
|
|
4d8a371e43 | ||
|
|
01fd3242c4 | ||
|
|
9c289334e9 | ||
|
|
002142539b | ||
|
|
e1d1c10fad | ||
|
|
577bc546d8 | ||
|
|
85a832fb7c | ||
|
|
a9f23a8331 | ||
|
|
5d24599546 | ||
|
|
07d9d3770b | ||
|
|
c9ddb12b5a | ||
|
|
0932ee6051 | ||
|
|
ee1b8465a3 | ||
|
|
26bd164168 | ||
|
|
00e819bd92 | ||
|
|
740d33b378 | ||
|
|
f0b284eda7 | ||
|
|
da370c8b36 | ||
|
|
203b9c7f6b | ||
|
|
7932ec3cc1 | ||
|
|
3f15e02881 | ||
|
|
6fb5c04bde | ||
|
|
578bcc3522 | ||
|
|
99dea76fd1 | ||
|
|
b680540adf | ||
|
|
67f57a443c | ||
|
|
2b3b4f9daf | ||
|
|
ba01a697e6 | ||
|
|
16d81db117 | ||
|
|
4ce92f1a98 | ||
|
|
21c63637bd | ||
|
|
18f764e4d5 | ||
|
|
5930ca8ac7 | ||
|
|
49be4a3be2 | ||
|
|
1e50620f53 | ||
|
|
5e82dc1841 | ||
|
|
33d76962a7 | ||
|
|
6ce1c17ee1 | ||
|
|
8d9b4d3669 | ||
|
|
fb79b8a7f8 | ||
|
|
b0236eaa24 | ||
|
|
19bbff708e | ||
|
|
1399ea13cc | ||
|
|
a25d9fd3b4 | ||
|
|
72d51e4667 | ||
|
|
f6e3838eb2 | ||
|
|
c1fdecae2d | ||
|
|
4104db65bb | ||
|
|
36cb827406 | ||
|
|
69a4558fe4 | ||
|
|
928eae419f | ||
|
|
50edac8ee8 | ||
|
|
945951cda5 | ||
|
|
fa95a638af | ||
|
|
3b6aeb1fc8 | ||
|
|
6543a01418 | ||
|
|
9724e52f1b | ||
|
|
9413a30a08 | ||
|
|
6a9a2559d2 | ||
|
|
78b6f4df01 | ||
|
|
b7e88aa2ef | ||
|
|
224fd21612 | ||
|
|
a6bf33c501 | ||
|
|
306a9421fb | ||
|
|
d5885daf0f | ||
|
|
52b3f7105d | ||
|
|
7f6dc291c0 | ||
|
|
eacb849353 | ||
|
|
367bc401ee | ||
|
|
92af3ea725 | ||
|
|
582ce4cfa0 | ||
|
|
43ae3dfce5 | ||
|
|
ce851a7a8b | ||
|
|
b5b7589b41 | ||
|
|
b638341c14 | ||
|
|
7274417f84 | ||
|
|
1967226c71 | ||
|
|
227ea8ed24 | ||
|
|
e705db8339 | ||
|
|
be65f78174 | ||
|
|
fd6a17f3e6 | ||
|
|
0353d7b6a6 | ||
|
|
a64724be0e | ||
|
|
a0d9d41455 | ||
|
|
84e8ab5b71 | ||
|
|
f5bbb12434 | ||
|
|
a07d2cddd2 | ||
|
|
f1878640c8 | ||
|
|
edf8147561 | ||
|
|
3a9ab3a86f | ||
|
|
8f912ca0c5 | ||
|
|
a827056d28 | ||
|
|
6c90989584 | ||
|
|
08acf5e9aa | ||
|
|
bca295183b | ||
|
|
a400a2e0bb | ||
|
|
8c15839753 | ||
|
|
810024f4c9 | ||
|
|
de7bcca5fe | ||
|
|
6552a48639 | ||
|
|
aee0f0c882 | ||
|
|
e353193fbb | ||
|
|
fb547e9100 | ||
|
|
44289c8781 | ||
|
|
d4a5ab252d | ||
|
|
a3b3c28694 | ||
|
|
976c41a3b8 | ||
|
|
da087292fd | ||
|
|
274f59f93b | ||
|
|
85d810d2cf | ||
|
|
47f151c594 | ||
|
|
7b5ef2186e | ||
|
|
8236dafb96 | ||
|
|
56e5627b6b | ||
|
|
38db0eddab | ||
|
|
710625e2a3 | ||
|
|
a5e7af6c1f | ||
|
|
5fe4d2db2b | ||
|
|
b2c587e2f8 | ||
|
|
40c2d891c1 | ||
|
|
d5ad3a8a70 | ||
|
|
c74382af4f | ||
|
|
0be0111724 | ||
|
|
f0d454b511 | ||
|
|
b79a45403e | ||
|
|
1cc60e755b | ||
|
|
7fb7cc8c46 | ||
|
|
1400889b23 | ||
|
|
44fa9bd141 | ||
|
|
992dffa032 | ||
|
|
5fa7e48c24 | ||
|
|
3b00dfebed | ||
|
|
b42c7e07e6 | ||
|
|
ab7bfabf12 | ||
|
|
ab72644ace | ||
|
|
218fa040c8 | ||
|
|
b8f783f104 | ||
|
|
ae2e3e7fb1 | ||
|
|
edd9d9aee3 | ||
|
|
8c66085621 | ||
|
|
40ab325a37 | ||
|
|
7ae95b14f4 | ||
|
|
5da6103fb5 | ||
|
|
6b5b9db20a | ||
|
|
eb39d144e4 | ||
|
|
cfaaf4a2c4 | ||
|
|
279a738aa6 | ||
|
|
10b55f11a2 | ||
|
|
25065c4151 | ||
|
|
d2657bc154 | ||
|
|
4fde3f4a65 | ||
|
|
e398cfb27c | ||
|
|
3cc670ec29 | ||
|
|
c8d9e8458a | ||
|
|
892419e880 | ||
|
|
13fb49cf39 | ||
|
|
eabe3f968e | ||
|
|
2ca5eaaab9 | ||
|
|
eed3deac67 | ||
|
|
a63863b65c | ||
|
|
748ff13bce | ||
|
|
6c7700c2e7 | ||
|
|
c8ccb014c8 | ||
|
|
f1bd14e8de | ||
|
|
024d01a192 | ||
|
|
b2d02ef0bd | ||
|
|
751d7deda6 | ||
|
|
fff795146e | ||
|
|
c6219581f6 | ||
|
|
d0c5fb2395 | ||
|
|
6c2c81dc83 | ||
|
|
bfdb0c0012 | ||
|
|
d870d6c326 | ||
|
|
c06410e726 | ||
|
|
ef03ebe086 | ||
|
|
a217e8f24c | ||
|
|
c164beb040 | ||
|
|
5171e5880d | ||
|
|
2ef62524d6 | ||
|
|
4bbb29c2b4 | ||
|
|
261776cc78 | ||
|
|
3008142b1b | ||
|
|
152e729768 | ||
|
|
e7c77df59e | ||
|
|
4ff1a13f32 | ||
|
|
b9b18d6120 | ||
|
|
4945ce3c8c | ||
|
|
19aa8c63c0 | ||
|
|
efd11b61c6 | ||
|
|
1facb1f95d | ||
|
|
3db0788a91 | ||
|
|
e068098046 | ||
|
|
f2d289326b | ||
|
|
ae57f27eb9 | ||
|
|
a1767b2f7f | ||
|
|
5e9fcf7537 | ||
|
|
8192cc7eea | ||
|
|
2b8e1f2504 | ||
|
|
9d01f80744 | ||
|
|
1a04c9da67 | ||
|
|
d35e1baee1 | ||
|
|
2f16e50c87 | ||
|
|
3a662f7afb | ||
|
|
903a78623c | ||
|
|
7cd2f39f82 | ||
|
|
949dd5db35 | ||
|
|
49a5c5bb8b | ||
|
|
48aa355eea | ||
|
|
644b2673e0 | ||
|
|
8eed62e639 | ||
|
|
04c8449e5f | ||
|
|
9dd776ff6c | ||
|
|
016868e95a | ||
|
|
5d9c664fba | ||
|
|
a662c85728 | ||
|
|
5c4b7d427d | ||
|
|
12cc8bfd95 | ||
|
|
11c6f20c90 | ||
|
|
c602ec310d | ||
|
|
60a4180dd6 | ||
|
|
8e42721959 | ||
|
|
4553fa1d09 | ||
|
|
e43ff398cd | ||
|
|
c0cd255ea3 | ||
|
|
a0375fd000 | ||
|
|
6d847cd5f9 | ||
|
|
6470a3f539 | ||
|
|
c6111d09ce | ||
|
|
d6012854a9 | ||
|
|
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
|
||||
@@ -4,6 +4,8 @@ project(mirall)
|
||||
set(PACKAGE "mirall")
|
||||
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
|
||||
|
||||
include(Warnings)
|
||||
|
||||
set(OEM_THEME_DIR "" CACHE STRING "Define directory containing a custom theme")
|
||||
if ( EXISTS ${OEM_THEME_DIR}/OEM.cmake )
|
||||
include ( ${OEM_THEME_DIR}/OEM.cmake )
|
||||
@@ -11,11 +13,27 @@ else ()
|
||||
include ( ${CMAKE_SOURCE_DIR}/OWNCLOUD.cmake )
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED APPLICATION_SHORTNAME)
|
||||
set ( APPLICATION_SHORTNAME ${APPLICATION_NAME} )
|
||||
endif()
|
||||
|
||||
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)
|
||||
@@ -32,10 +50,8 @@ if (${GIT_SHA1} STREQUAL "GITDIR-NOTFOUND")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## stupid, we should upstream this
|
||||
if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr" AND NOT CMAKE_INSTALL_SYSCONFDIR)
|
||||
set(CMAKE_INSTALL_SYSCONFDIR "/etc")
|
||||
endif()
|
||||
set(SYSCONFDIR ${SYSCONF_INSTALL_DIR})
|
||||
set(DATADIR ${DATA_INSTALL_DIR})
|
||||
|
||||
#####
|
||||
## handle BUILD_OWNCLOUD_OSX_BUNDLE
|
||||
@@ -54,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.6.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest REQUIRED )
|
||||
if( UNIX AND NOT APPLE ) # Fdo notifications
|
||||
find_package(Qt4 4.6.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()
|
||||
@@ -74,32 +85,12 @@ find_package(INotify)
|
||||
endif()
|
||||
find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
find_package(QtKeychain)
|
||||
|
||||
set(WITH_QTKEYCHAIN ${QTKEYCHAIN_FOUND})
|
||||
set(USE_INOTIFY ${INOTIFY_FOUND})
|
||||
|
||||
configure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
# hidden files
|
||||
"/\\\\..+$"
|
||||
# temporary files
|
||||
"\\\\.swp$"
|
||||
# backup files
|
||||
"~$"
|
||||
# others
|
||||
"\\\\.#"
|
||||
"/#"
|
||||
"/build/"
|
||||
"/_build/"
|
||||
# used before
|
||||
"\\\\.o$"
|
||||
"\\\\.lo$"
|
||||
"\\\\.la$"
|
||||
"Makefile\\\\.in$"
|
||||
)
|
||||
|
||||
include(OwnCloudCPack.cmake)
|
||||
|
||||
add_definitions(-DUNICODE)
|
||||
@@ -121,5 +112,5 @@ endif(UNIT_TESTING)
|
||||
if(BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
configure_file(sync-exclude.lst ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/sync-exclude.lst COPYONLY)
|
||||
else()
|
||||
install( FILES sync-exclude.lst DESTINATION ${CMAKE_INSTALL_SYSCONFDIR} )
|
||||
install( FILES sync-exclude.lst DESTINATION ${SYSCONFDIR}/${APPLICATION_SHORTNAME} )
|
||||
endif()
|
||||
|
||||
@@ -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@)
|
||||
|
||||
80
ChangeLog
@@ -1,5 +1,85 @@
|
||||
ChangeLog
|
||||
=========
|
||||
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
|
||||
* Transform folder Status Dialog into Account Settings, provide feedback via context menu
|
||||
* Add Bandwidth Control
|
||||
* Add a visual storage/quota indicator (context menu and account settings)
|
||||
* Add progress indication (context menu and account settings)
|
||||
* Introduce a sync history, persisting results across syncs
|
||||
* Move ability to switch to mono icons from a switch to a Settings option
|
||||
* Add "Launch on System Startup" GUI option
|
||||
* Add "Show Desktop Nofications"GUI option (enabled by default)
|
||||
top optionally disable sync notifications
|
||||
* Add Help item, pointing to online reference
|
||||
* Implement graphical selection of remote folders in FolderWizard
|
||||
* Allow custom ignore patterns
|
||||
* 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
|
||||
* Windows: Reduce priority of CSync thread
|
||||
* Documentation: Prem. updates to reflect UI changes
|
||||
* Significant code refactorings
|
||||
* Require Qt 4.7
|
||||
* Known issue: Under certain conditions, a file will only get uploaded after up to five minutes
|
||||
|
||||
version 1.3.0 (release 2013-06-25 ), csync 0.80.0 required
|
||||
|
||||
* Default proxy port to 8080
|
||||
* Don't lose proxy settings when changing passwords
|
||||
* Support SOCKS5 proxy (useful in combination with ssh *D)
|
||||
* Propagate proxy changes to csync at runtime
|
||||
* Improve proxy wizard
|
||||
* Display proxy errors
|
||||
* Solved problems with lock files
|
||||
* Warn if for some reason all files are scheduled for removal on either side
|
||||
* Avoid infinite loop if authentication fails in certain cases
|
||||
* Fix reading the password from the config in certain cases
|
||||
* Do not crash when configured sync target disappears
|
||||
* Make --help work on windows
|
||||
* Make sync feedback less ambiguous.
|
||||
* Fix icon tray tooltip sometimes showing repeated content
|
||||
* More use of native directory separators on Windows
|
||||
* Remove journal when reusing a directory that used to have a journal before
|
||||
* Visual clean up of status dialog items
|
||||
* Wizard: When changing the URL or user name, allow the user to push his data
|
||||
to the new location or wipe the folder and start from scratch
|
||||
* Wizard: Make setting a custom folder as a sync target work again
|
||||
* Fix application icon
|
||||
* User-Agent now contains "Mozilla/5.0" and the Platform name (for firewall/proxy compat)
|
||||
* Server side directory moves will be detected
|
||||
* New setup wizard, defaulting to root syncing (only for new setups)
|
||||
* Improved thread stop/termination
|
||||
|
||||
version 1.2.5 (release 2013-04-23 ), csync 0.70.7 required
|
||||
* [Fixes] NSIS installer fixes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
set( APPLICATION_SHORTNAME "owncloud" )
|
||||
set( APPLICATION_NAME "ownCloud" )
|
||||
set( APPLICATION_EXECUTABLE "owncloud" )
|
||||
set( APPLICATION_DOMAIN "owncloud.com" )
|
||||
set( APPLICATION_VENDOR "ownCloud, Inc" )
|
||||
set( THEME_CLASS "ownCloudTheme" )
|
||||
#set( APPLICATION_SHORTNAME ${APPLICATION_NAME} )
|
||||
set( APPLICATION_EXECUTABLE "owncloud" )
|
||||
set( APPLICATION_DOMAIN "owncloud.com" )
|
||||
set( APPLICATION_VENDOR "ownCloud, Inc" )
|
||||
set( THEME_CLASS "ownCloudTheme" )
|
||||
set( APPLICATION_REV_DOMAIN "com.owncloud.desktopclient" )
|
||||
set( WIN_SETUP_BITMAP_PATH "${CMAKE_SOURCE_DIR}/admin/win/nsi" )
|
||||
# set( THEME_INCLUDE "${OEM_THEME_DIR}/mytheme.h" )
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set( VERSION_MAJOR 1 )
|
||||
set( VERSION_MINOR 3 )
|
||||
set( VERSION_MINOR 5 )
|
||||
set( VERSION_PATCH 0 )
|
||||
set( VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX}beta4)
|
||||
set( VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX}beta1")
|
||||
set( SOVERSION 0 )
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ mount="/Volumes/$(basename $src_dmg|cut -d"-" -f1)"
|
||||
|
||||
test -e $tmp_dmg && rm -rf $tmp_dmg
|
||||
hdiutil convert $src_dmg -format UDRW -o $tmp_dmg
|
||||
open $tmp_dmg
|
||||
sleep 12s
|
||||
hdiutil attach $tmp_dmg
|
||||
pushd $mount
|
||||
codesign -s "$identity" $mount/*.app
|
||||
popd
|
||||
|
||||
@@ -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"
|
||||
|
||||
108
cmake/modules/DefineInstallationPaths.cmake
Normal file
@@ -0,0 +1,108 @@
|
||||
if (UNIX)
|
||||
# Suffix for Linux
|
||||
SET(LIB_SUFFIX
|
||||
CACHE STRING "Define suffix of directory name (32/64)"
|
||||
)
|
||||
|
||||
SET(EXEC_INSTALL_PREFIX
|
||||
"${CMAKE_INSTALL_PREFIX}"
|
||||
CACHE PATH "Base directory for executables and libraries"
|
||||
)
|
||||
SET(SHARE_INSTALL_PREFIX
|
||||
"${CMAKE_INSTALL_PREFIX}/share"
|
||||
CACHE PATH "Base directory for files which go to share/"
|
||||
)
|
||||
SET(DATA_INSTALL_PREFIX
|
||||
"${SHARE_INSTALL_PREFIX}/${APPLICATION_SHORT_NAME}"
|
||||
CACHE PATH "The parent directory where applications can install their data")
|
||||
|
||||
# The following are directories where stuff will be installed to
|
||||
SET(BIN_INSTALL_DIR
|
||||
"${EXEC_INSTALL_PREFIX}/bin"
|
||||
CACHE PATH "The ${APPLICATION_SHORT_NAME} binary install dir (default prefix/bin)"
|
||||
)
|
||||
SET(SBIN_INSTALL_DIR
|
||||
"${EXEC_INSTALL_PREFIX}/sbin"
|
||||
CACHE PATH "The ${APPLICATION_SHORT_NAME} sbin install dir (default prefix/sbin)"
|
||||
)
|
||||
SET(LIB_INSTALL_DIR
|
||||
"${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}"
|
||||
CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/lib)"
|
||||
)
|
||||
SET(LIBEXEC_INSTALL_DIR
|
||||
"${EXEC_INSTALL_PREFIX}/libexec"
|
||||
CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is prefix/libexec)"
|
||||
)
|
||||
SET(PLUGIN_INSTALL_DIR
|
||||
"${LIB_INSTALL_DIR}/${APPLICATION_SHORT_NAME}"
|
||||
CACHE PATH "The subdirectory relative to the install prefix where plugins will be installed (default is prefix/lib/${APPLICATION_SHORT_NAME})"
|
||||
)
|
||||
SET(INCLUDE_INSTALL_DIR
|
||||
"${CMAKE_INSTALL_PREFIX}/include"
|
||||
CACHE PATH "The subdirectory to the header prefix (default prefix/include)"
|
||||
)
|
||||
|
||||
SET(DATA_INSTALL_DIR
|
||||
"${DATA_INSTALL_PREFIX}"
|
||||
CACHE PATH "The parent directory where applications can install their data (default prefix/share/${APPLICATION_SHORT_NAME})"
|
||||
)
|
||||
SET(HTML_INSTALL_DIR
|
||||
"${DATA_INSTALL_PREFIX}/doc/HTML"
|
||||
CACHE PATH "The HTML install dir for documentation (default data/doc/html)"
|
||||
)
|
||||
SET(ICON_INSTALL_DIR
|
||||
"${DATA_INSTALL_PREFIX}/icons"
|
||||
CACHE PATH "The icon install dir (default data/icons/)"
|
||||
)
|
||||
SET(SOUND_INSTALL_DIR
|
||||
"${DATA_INSTALL_PREFIX}/sounds"
|
||||
CACHE PATH "The install dir for sound files (default data/sounds)"
|
||||
)
|
||||
|
||||
SET(LOCALE_INSTALL_DIR
|
||||
"${SHARE_INSTALL_PREFIX}/locale"
|
||||
CACHE PATH "The install dir for translations (default prefix/share/locale)"
|
||||
)
|
||||
|
||||
SET(XDG_APPS_DIR
|
||||
"${SHARE_INSTALL_PREFIX}/applications/"
|
||||
CACHE PATH "The XDG apps dir"
|
||||
)
|
||||
SET(XDG_DIRECTORY_DIR
|
||||
"${SHARE_INSTALL_PREFIX}/desktop-directories"
|
||||
CACHE PATH "The XDG directory"
|
||||
)
|
||||
|
||||
IF(NOT "${EXEC_INSTALL_PREFIX}" STREQUAL "/usr")
|
||||
SET(SYSCONFDIR_INSTALL_PREFIX "${EXEC_INSTALL_PREFIX}")
|
||||
endif()
|
||||
|
||||
SET(SYSCONF_INSTALL_DIR
|
||||
"${SYSCONFDIR_INSTALL_PREFIX}/etc"
|
||||
CACHE PATH "The ${APPLICATION_SHORT_NAME} sysconfig install dir (default prefix/etc)"
|
||||
)
|
||||
SET(MAN_INSTALL_DIR
|
||||
"${SHARE_INSTALL_PREFIX}/man"
|
||||
CACHE PATH "The ${APPLICATION_SHORT_NAME} man install dir (default prefix/man)"
|
||||
)
|
||||
SET(INFO_INSTALL_DIR
|
||||
"${SHARE_INSTALL_PREFIX}/info"
|
||||
CACHE PATH "The ${APPLICATION_SHORT_NAME} info install dir (default prefix/info)"
|
||||
)
|
||||
endif (UNIX)
|
||||
|
||||
if (WIN32)
|
||||
# Same same
|
||||
set(SHARE_INSTALL_PREFIX "share" CACHE PATH "-")
|
||||
set(BIN_INSTALL_DIR "." CACHE PATH "-")
|
||||
set(SBIN_INSTALL_DIR "." CACHE PATH "-")
|
||||
set(LIB_INSTALL_DIR "lib" CACHE PATH "-")
|
||||
set(INCLUDE_INSTALL_DIR "include" CACHE PATH "-")
|
||||
set(PLUGIN_INSTALL_DIR "plugins" CACHE PATH "-")
|
||||
set(HTML_INSTALL_DIR "doc/HTML" CACHE PATH "-")
|
||||
set(ICON_INSTALL_DIR "." CACHE PATH "-")
|
||||
set(SOUND_INSTALL_DIR "." CACHE PATH "-")
|
||||
set(LOCALE_INSTALL_DIR "lang" CACHE PATH "-")
|
||||
set(SYSCONF_INSTALL_DIR "config" CACHE PATH "-")
|
||||
set(MAN_INSTALL_DIR "man" CACHE PATH "-")
|
||||
endif (WIN32)
|
||||
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 )
|
||||
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)
|
||||
@@ -1,6 +1,6 @@
|
||||
;ownCloud installer script.
|
||||
|
||||
!define APPLICATION_SHORTNAME "@APPLICATION_SHORTNAME@"
|
||||
!define APPLICATION_SHORTNAME "@APPLICATION_EXECUTABLE@"
|
||||
!define APPLICATION_NAME "@APPLICATION_NAME@"
|
||||
!define APPLICATION_VENDOR "@APPLICATION_VENDOR@"
|
||||
!define APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@.exe"
|
||||
@@ -18,7 +18,6 @@
|
||||
!define OPTION_SECTION_SC_START_MENU
|
||||
!define OPTION_SECTION_SC_DESKTOP
|
||||
!define OPTION_SECTION_SC_QUICK_LAUNCH
|
||||
!define OPTION_SECTION_SC_AUTOSTART
|
||||
!define OPTION_FINISHPAGE
|
||||
!define OPTION_FINISHPAGE_LAUNCHER
|
||||
; !define OPTION_FINISHPAGE_RELEASE_NOTES
|
||||
@@ -40,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"
|
||||
@@ -283,90 +281,24 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
!ifndef INSTALL_PATH
|
||||
;Main executable.
|
||||
File "${BUILD_PATH}\bin\${APPLICATION_EXECUTABLE}"
|
||||
File "${BUILD_PATH}\src\libowncloudsync.dll"
|
||||
File "${BUILD_PATH}\src\lib${APPLICATION_SHORTNAME}sync.dll"
|
||||
|
||||
File "${BUILD_PATH}\src\mirall_ca.qm"
|
||||
File "${BUILD_PATH}\src\mirall_cs_CZ.qm"
|
||||
File "${BUILD_PATH}\src\mirall_da.qm"
|
||||
File "${BUILD_PATH}\src\mirall_de.qm"
|
||||
File "${BUILD_PATH}\src\mirall_el.qm"
|
||||
File "${BUILD_PATH}\src\mirall_en.qm"
|
||||
File "${BUILD_PATH}\src\mirall_eo.qm"
|
||||
File "${BUILD_PATH}\src\mirall_es.qm"
|
||||
File "${BUILD_PATH}\src\mirall_es_AR.qm"
|
||||
File "${BUILD_PATH}\src\mirall_et_EE.qm"
|
||||
File "${BUILD_PATH}\src\mirall_eu.qm"
|
||||
File "${BUILD_PATH}\src\mirall_fa.qm"
|
||||
File "${BUILD_PATH}\src\mirall_fi_FI.qm"
|
||||
File "${BUILD_PATH}\src\mirall_fr.qm"
|
||||
File "${BUILD_PATH}\src\mirall_gl.qm"
|
||||
File "${BUILD_PATH}\src\mirall_he.qm"
|
||||
File "${BUILD_PATH}\src\mirall_hr.qm"
|
||||
File "${BUILD_PATH}\src\mirall_hu_HU.qm"
|
||||
File "${BUILD_PATH}\src\mirall_it.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ja_JP.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ko.qm"
|
||||
File "${BUILD_PATH}\src\mirall_lb.qm"
|
||||
File "${BUILD_PATH}\src\mirall_lt_LT.qm"
|
||||
File "${BUILD_PATH}\src\mirall_lv.qm"
|
||||
File "${BUILD_PATH}\src\mirall_mk.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ms_MY.qm"
|
||||
File "${BUILD_PATH}\src\mirall_nb_NO.qm"
|
||||
File "${BUILD_PATH}\src\mirall_nl.qm"
|
||||
File "${BUILD_PATH}\src\mirall_oc.qm"
|
||||
File "${BUILD_PATH}\src\mirall_pl.qm"
|
||||
File "${BUILD_PATH}\src\mirall_pt_BR.qm"
|
||||
File "${BUILD_PATH}\src\mirall_pt_PT.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ro.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ru.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ru_RU.qm"
|
||||
File "${BUILD_PATH}\src\mirall_si_LK.qm"
|
||||
File "${BUILD_PATH}\src\mirall_sk_SK.qm"
|
||||
File "${BUILD_PATH}\src\mirall_sl.qm"
|
||||
File "${BUILD_PATH}\src\mirall_sr@latin.qm"
|
||||
File "${BUILD_PATH}\src\mirall_sv.qm"
|
||||
File "${BUILD_PATH}\src\mirall_ta_LK.qm"
|
||||
File "${BUILD_PATH}\src\mirall_th_TH.qm"
|
||||
File "${BUILD_PATH}\src\mirall_tr.qm"
|
||||
File "${BUILD_PATH}\src\mirall_uk.qm"
|
||||
File "${BUILD_PATH}\src\mirall_vi.qm"
|
||||
File "${BUILD_PATH}\src\mirall_zh_CN.qm"
|
||||
File "${BUILD_PATH}\src\mirall_zh_TW.qm"
|
||||
|
||||
#File "${MING_SHARE}\qt4\translations\qt_ar.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_cs.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_da.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_de.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_es.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_fa.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_fr.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_gl.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_he.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_hu.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_ja.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_ko.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_lt.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_pl.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_pt.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_ru.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_sk.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_sl.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_sv.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_uk.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_zh_CN.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_zh_TW.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_zh_TW.qm"
|
||||
|
||||
File "${MING_SHARE}\qt4\translations\qtkeychain_de.qm"
|
||||
File "${BUILD_PATH}\src\mirall_*.qm"
|
||||
; Make sure only to copy qt, not qt_help, etc
|
||||
File "${MING_SHARE}\qt4\translations\qt_??.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qt_??_??.qm"
|
||||
File "${MING_SHARE}\qt4\translations\qtkeychain_*.qm"
|
||||
|
||||
SetOutPath "$INSTDIR\accessible"
|
||||
File "${ACCESSIBLE_DLL_PATH}\qtaccessiblewidgets4.dll"
|
||||
SetOutPath "$INSTDIR\imageformats"
|
||||
File "${IMAGEFORMATS_DLL_PATH}\qgif4.dll"
|
||||
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
|
||||
|
||||
@@ -380,12 +312,14 @@ 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"
|
||||
@@ -404,7 +338,7 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
|
||||
; Other
|
||||
;File "${MING_BIN}\libpng15-15.dll"
|
||||
;File "${MING_BIN}\libjpeg-8.dll"
|
||||
File "${MING_BIN}\libjpeg-8.dll"
|
||||
File "${MING_BIN}\zlib1.dll"
|
||||
File "${MING_BIN}\libcrypto-10.dll"
|
||||
File "${MING_BIN}\libssl-10.dll"
|
||||
@@ -417,18 +351,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
|
||||
@@ -455,27 +384,15 @@ SectionGroup "Shortcuts"
|
||||
|
||||
SectionGroupEnd
|
||||
|
||||
!ifdef OPTION_SECTION_SC_AUTOSTART
|
||||
${MementoSection} "Autostart" SEC_AUTOSTART
|
||||
SectionIn 1 2
|
||||
SetDetailsPrint textonly
|
||||
DetailPrint "Creating Windows Start Entry"
|
||||
SetDetailsPrint listonly
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Run" \
|
||||
"${APPLICATION_NAME}" "$INSTDIR\${APPLICATION_EXECUTABLE}"
|
||||
${MementoSectionEnd}
|
||||
!endif
|
||||
|
||||
${MementoSectionDone}
|
||||
|
||||
; Installer section descriptions
|
||||
;--------------------------------
|
||||
!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_DESCRIPTION_TEXT ${SEC_AUTOSTART} "Register ${APPLICATION_NAME} to run on Windows startup."
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
||||
Section -post
|
||||
@@ -578,15 +495,10 @@ Section Uninstall
|
||||
|
||||
DeleteRegKey HKCR "${APPLICATION_NAME}"
|
||||
|
||||
;Windows Start entry
|
||||
!ifdef OPTION_SECTION_SC_AUTOSTART
|
||||
DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "${APPLICATION_NAME}"
|
||||
!endif
|
||||
|
||||
;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 )
|
||||
20
cmake/modules/Warnings.cmake
Normal file
@@ -0,0 +1,20 @@
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
|
||||
OUTPUT_VARIABLE GCC_VERSION)
|
||||
if(GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wpedantic -Wno-long-long")
|
||||
else(GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -Wno-long-long")
|
||||
endif(GCC_VERSION VERSION_GREATER 4.8 OR GCC_VERSION VERSION_EQUAL 4.8)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
if(CMAKE_CXX_COMPILER MATCHES "clang")
|
||||
set(CMAKE_CXX_FLAGS "-Wall -Wextra -pedantic -Wno-long-long")
|
||||
endif(CMAKE_CXX_COMPILER MATCHES "clang")
|
||||
# TODO: handle msvc compilers warnings?
|
||||
|
||||
if(DEFINED MIRALL_FATAL_WARNINGS)
|
||||
if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER MATCHES "clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
|
||||
endif (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER MATCHES "clang")
|
||||
# TODO: handle msvc compilers warnings?
|
||||
endif(DEFINED MIRALL_FATAL_WARNINGS)
|
||||
@@ -2,11 +2,17 @@
|
||||
#define CONFIG_H
|
||||
|
||||
#cmakedefine USE_INOTIFY 1
|
||||
#cmakedefine WITH_CSYNC 1
|
||||
#cmakedefine WITH_QTKEYCHAIN 1
|
||||
|
||||
#cmakedefine GIT_SHA1 "@GIT_SHA1@"
|
||||
#cmakedefine APPLICATION_DOMAIN @APPLICATION_DOMAIN@
|
||||
#cmakedefine THEME_CLASS @THEME_CLASS@
|
||||
#cmakedefine THEME_INCLUDE @THEME_INCLUDE@
|
||||
|
||||
#cmakedefine APPLICATION_NAME "@APPLICATION_NAME@"
|
||||
#cmakedefine APPLICATION_SHORTNAME "@APPLICATION_SHORTNAME@"
|
||||
#cmakedefine APPLICATION_EXECUTABLE "@APPLICATION_EXECUTABLE@"
|
||||
|
||||
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
|
||||
#cmakedefine DATADIR "@DATADIR@"
|
||||
#endif
|
||||
|
||||
@@ -22,16 +22,21 @@ 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
|
||||
add_custom_target(doc-latex ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b latex
|
||||
-d ${SPHINX_CACHE_DIR}
|
||||
-d ${SPHINX_CACHE_DIR}/latex
|
||||
-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)
|
||||
@@ -41,7 +46,7 @@ if(SPHINX_FOUND)
|
||||
if (EXISTS ${QT_QCOLLECTIONGENERATOR_EXECUTABLE})
|
||||
add_custom_target( doc-qch-sphinx ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b qthelp
|
||||
-d ${SPHINX_CACHE_DIR}
|
||||
-d ${SPHINX_CACHE_DIR}/qthelp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_QCH_DIR} )
|
||||
add_custom_target( doc-qch ${QT_QCOLLECTIONGENERATOR_EXECUTABLE}
|
||||
@@ -54,12 +59,24 @@ if(SPHINX_FOUND)
|
||||
endif()
|
||||
add_custom_target( doc-html ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b html
|
||||
-d ${SPHINX_CACHE_DIR}
|
||||
-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}
|
||||
-d ${SPHINX_CACHE_DIR}/man
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_MAN_DIR} )
|
||||
|
||||
@@ -71,7 +88,7 @@ if(SPHINX_FOUND)
|
||||
add_custom_target( doc-chm-sphinx ${SPHINX_EXECUTABLE}
|
||||
-q -c . -b htmlhelp
|
||||
-D html_theme=basic
|
||||
-d ${SPHINX_CACHE_DIR}
|
||||
-d ${SPHINX_CACHE_DIR}/htmlhelp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${SPHINX_HTMLHELP_DIR} )
|
||||
add_custom_target( doc-chm pushd ${SPHINX_HTMLHELP_DIR}; ${MSHTML_COMPILER} *.hhp; popd
|
||||
|
||||
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,8 +34,13 @@ 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.
|
||||
|
||||
Sync Direction and Strategies
|
||||
-----------------------------
|
||||
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
|
||||
------------------------
|
||||
.. index:: time stamps, file times, etag, unique id
|
||||
|
||||
Until the release of ownCloud 4.5 and ownCloud Client 1.1, ownCloud employed
|
||||
@@ -44,7 +49,7 @@ synced to the other repository: the files modification time.
|
||||
|
||||
The *modification timestamp* is part of the files metadata. It is available on
|
||||
every relevant filesystem and is the natural indicator for a file change.
|
||||
modification timestamps do not require special action to create and have
|
||||
Modification timestamps do not require special action to create and have
|
||||
a general meaning. One design goal of csync is to not require a special server
|
||||
component, that’s why it was chosen as the backend component.
|
||||
|
||||
@@ -57,29 +62,24 @@ 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. The client will store this number in a
|
||||
per-directory database, located in the application directory (version 1.1) or
|
||||
as a hidden file right in the directory to be synced (later versions).
|
||||
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.
|
||||
|
||||
.. todo:: describe what happens if both sides change
|
||||
.. 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.
|
||||
|
||||
If the per-directory database gets removed, oCC's CSync backend will fall back
|
||||
to a time-stamp based sync process to rebuild the database. Thus it should be
|
||||
made sure that both server and client synchronized to NTP time before
|
||||
restarting the client after a database removal. If time deviates, the sync
|
||||
process might create faux conflict files, which only differ in their time.
|
||||
Those need to be cleaned up manually later on and will not be synced back
|
||||
to the server. However, no files will get deleted in this process.
|
||||
|
||||
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
|
||||
recursive process, it significantly reduces the effort required for a sync
|
||||
cycle, because the client will only walk directories with a modified unique id.
|
||||
|
||||
.. 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.
|
||||
|
||||
This table outlines the different sync methods attempted depending
|
||||
on server/client combination:
|
||||
@@ -106,3 +106,56 @@ are involved and one of them is not in sync with NTP time.
|
||||
.. _`NTP time synchronisation`: http://en.wikipedia.org/wiki/Network_Time_Protocol
|
||||
.. _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
|
||||
before. The previous value is stored in the client's database. If it is not, it
|
||||
means that the file has been added to the local repository. Note that on
|
||||
the local side, the modificaton time a good attribute to detect changes because
|
||||
it does not depend on time shifts and such.
|
||||
|
||||
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.
|
||||
|
||||
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 a hidden file right in the directory
|
||||
to be synced.
|
||||
|
||||
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,10 +41,11 @@ 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 cmake, follow the `generic build instructions`_.
|
||||
|
||||
To build mirall and csync, follow the `generic build instructions`_.
|
||||
|
||||
.. note::
|
||||
You should not call ``make install`` at any time, since the product of the
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -1,17 +1,20 @@
|
||||
ownCloud Client supports the following command line switches:
|
||||
|
||||
``--logwindow``
|
||||
open a window to show log output at startup.
|
||||
open a window to show log output.
|
||||
|
||||
``--logfile`` `<file>`
|
||||
write log output to a single file
|
||||
``--logfile`` `<filename>`
|
||||
write log output to file <filename>.
|
||||
|
||||
``--logdir`` `<dir>`
|
||||
write log output to dir, one for each sync run.
|
||||
``--logdir`` `<name>`
|
||||
write each sync log output in a new file in directory <name>
|
||||
|
||||
``--flushlog``
|
||||
``--logexpire`` `<hours>`
|
||||
removes logs older than <hours> hours. (to be used with --logdir)
|
||||
|
||||
``--logflush``
|
||||
flush the log file after every write.
|
||||
|
||||
``--monoicons``
|
||||
Use black/white pictograms for systray.
|
||||
``--confdir`` `<dirname>`
|
||||
Use the given configuration directory.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -90,9 +98,21 @@ create a huge amount of data, as the log window has a limited buffer.
|
||||
To write logs to disk, start the client with ``--logfile <file>``, where
|
||||
``<file`` is the file you want to log to, or ``--logdir <dir>``, where ``<dir>``
|
||||
is an existing directory. In case of ``--logdir``, each sync run will create a
|
||||
new file.
|
||||
new file. To limit the amount of data that accumulates over time, there is another
|
||||
useful parameter: ``--logexpire <hours>```. If that is combined with ```--logdir```
|
||||
the client automatically erases log data in that directory that is older than the
|
||||
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
|
||||
@@ -101,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,55 +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
|
||||
----------------------
|
||||
|
||||
Use the ``Configure`` option. It will open the Connection 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 reliable on some Linux distributions, as only the ``http_proxy``
|
||||
variable gets parsed. You can configure a proxy different from your
|
||||
system default by choosing ``Configure proxy...`` from the menu.
|
||||
|
||||
By default, ownCloud expects a HTTP proxy. If you want to specify a SOCKS5
|
||||
proxy instead, tick the "Use as SOCKSv5 proxy" option.
|
||||
|
||||
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`` |
|
||||
+-----------+------------------------------+
|
||||
@@ -4,4 +4,5 @@ Type=Application
|
||||
Exec=@APPLICATION_EXECUTABLE@
|
||||
Name=@APPLICATION_NAME@ desktop sync client
|
||||
GenericName=Folder Sync
|
||||
Icon=@APPLICATION_SHORTNAME@
|
||||
Icon=@APPLICATION_EXECUTABLE@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing;
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
<file>resources/task-ongoing.png</file>
|
||||
<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>
|
||||
</RCC>
|
||||
|
||||
BIN
resources/activity.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
resources/network.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/settings.png
Normal file
|
After Width: | Height: | Size: 2.7 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,27 +1,17 @@
|
||||
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(mirall_UI
|
||||
mirall/folderwizardsourcepage.ui
|
||||
mirall/folderwizardtargetpage.ui
|
||||
mirall/statusdialog.ui
|
||||
mirall/owncloudsetuppage_ng.ui
|
||||
mirall/owncloudwizardresultpage.ui
|
||||
mirall/owncloudcredentialspage.ui
|
||||
mirall/sslerrordialog.ui
|
||||
mirall/proxydialog.ui
|
||||
mirall/fileitemdialog.ui
|
||||
)
|
||||
set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
|
||||
|
||||
set(3rdparty_SRC
|
||||
3rdparty/qtsingleapplication/qtsingleapplication.cpp
|
||||
@@ -39,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 )
|
||||
@@ -52,38 +43,74 @@ 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/credentialstore.cpp
|
||||
mirall/owncloudfolder.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/httpconfigfile.cpp
|
||||
creds/shibbolethcredentials.cpp
|
||||
creds/shibboleth/shibbolethaccessmanager.cpp
|
||||
creds/shibboleth/shibbolethcookiejar.cpp
|
||||
creds/shibboleth/shibbolethwebview.cpp
|
||||
creds/shibboleth/shibbolethrefresher.cpp
|
||||
creds/shibboleth/shibbolethconfigfile.cpp
|
||||
creds/credentialscommon.cpp
|
||||
3rdparty/qjson/json.cpp
|
||||
)
|
||||
set(libsync_HEADERS
|
||||
|
||||
set(libsync_HEADERS
|
||||
mirall/folderman.h
|
||||
mirall/folder.h
|
||||
mirall/folderwatcher.h
|
||||
mirall/owncloudfolder.h
|
||||
mirall/csyncthread.h
|
||||
mirall/owncloudinfo.h
|
||||
mirall/credentialstore.h
|
||||
mirall/owncloudpropagator.h
|
||||
mirall/syncjournaldb.h
|
||||
mirall/theme.h
|
||||
mirall/owncloudtheme.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/httpconfigfile.h
|
||||
creds/shibbolethcredentials.h
|
||||
creds/shibboleth/shibbolethaccessmanager.h
|
||||
creds/shibboleth/shibbolethcookiejar.h
|
||||
creds/shibboleth/shibbolethwebview.h
|
||||
creds/shibboleth/shibbolethrefresher.h
|
||||
creds/shibboleth/shibbolethconfigfile.h
|
||||
creds/credentialscommon.h
|
||||
3rdparty/qjson/json.h
|
||||
)
|
||||
|
||||
IF( INOTIFY_FOUND )
|
||||
@@ -92,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()
|
||||
@@ -101,59 +128,111 @@ IF( APPLE )
|
||||
ENDIF()
|
||||
|
||||
|
||||
qt4_wrap_cpp(syncMoc ${libsync_HEADERS})
|
||||
qt_wrap_cpp(syncMoc ${libsync_HEADERS})
|
||||
|
||||
|
||||
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()
|
||||
|
||||
add_library(owncloudsync SHARED ${libsync_SRCS} ${syncMoc})
|
||||
set_target_properties( owncloudsync PROPERTIES
|
||||
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}
|
||||
)
|
||||
|
||||
target_link_libraries(owncloudsync ${libsync_LINK_TARGETS} )
|
||||
target_link_libraries(${synclib_NAME} ${libsync_LINK_TARGETS} )
|
||||
|
||||
if ( APPLE )
|
||||
target_link_libraries(owncloudsync /System/Library/Frameworks/CoreServices.framework)
|
||||
target_link_libraries(${synclib_NAME} /System/Library/Frameworks/CoreServices.framework)
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
install(TARGETS owncloudsync
|
||||
install(TARGETS ${synclib_NAME}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
if(NOT WIN32)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/mirall.desktop.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_SHORTNAME}.desktop)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_SHORTNAME}.desktop DESTINATION share/applications )
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop DESTINATION share/applications )
|
||||
endif()
|
||||
else()
|
||||
install(TARGETS owncloudsync DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
|
||||
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/statusdialog.cpp
|
||||
mirall/owncloudwizard.cpp
|
||||
mirall/folderstatusmodel.cpp
|
||||
mirall/protocolwidget.cpp
|
||||
wizard/owncloudwizard.cpp
|
||||
wizard/owncloudsetuppage.cpp
|
||||
wizard/owncloudhttpcredspage.cpp
|
||||
wizard/owncloudwizardresultpage.cpp
|
||||
wizard/owncloudwizardcommon.cpp
|
||||
wizard/owncloudshibbolethcredspage.cpp
|
||||
wizard/owncloudadvancedsetuppage.cpp
|
||||
mirall/owncloudsetupwizard.cpp
|
||||
mirall/updatedetector.cpp
|
||||
mirall/occinfo.cpp
|
||||
mirall/sslerrordialog.cpp
|
||||
mirall/logbrowser.cpp
|
||||
mirall/proxydialog.cpp
|
||||
mirall/fileitemdialog.cpp
|
||||
mirall/settingsdialog.cpp
|
||||
mirall/generalsettings.cpp
|
||||
mirall/networksettings.cpp
|
||||
mirall/accountsettings.cpp
|
||||
mirall/ignorelisteditor.cpp
|
||||
mirall/owncloudgui.cpp
|
||||
mirall/socketapi.cpp
|
||||
)
|
||||
|
||||
set(mirall_HEADERS
|
||||
@@ -161,13 +240,25 @@ set(mirall_HEADERS
|
||||
mirall/systray.h
|
||||
mirall/folderwizard.h
|
||||
mirall/owncloudsetupwizard.h
|
||||
mirall/owncloudwizard.h
|
||||
mirall/statusdialog.h
|
||||
wizard/owncloudwizard.h
|
||||
wizard/owncloudsetuppage.h
|
||||
wizard/owncloudhttpcredspage.h
|
||||
wizard/owncloudwizardresultpage.h
|
||||
wizard/owncloudwizardcommon.h
|
||||
wizard/owncloudshibbolethcredspage.h
|
||||
wizard/owncloudadvancedsetuppage.h
|
||||
mirall/folderstatusmodel.h
|
||||
mirall/updatedetector.h
|
||||
mirall/sslerrordialog.h
|
||||
mirall/logbrowser.h
|
||||
mirall/proxydialog.h
|
||||
mirall/fileitemdialog.h
|
||||
mirall/settingsdialog.h
|
||||
mirall/generalsettings.h
|
||||
mirall/networksettings.h
|
||||
mirall/accountsettings.h
|
||||
mirall/ignorelisteditor.h
|
||||
mirall/protocolwidget.h
|
||||
mirall/owncloudgui.h
|
||||
mirall/socketapi.h
|
||||
)
|
||||
|
||||
if( UNIX AND NOT APPLE)
|
||||
@@ -177,12 +268,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}
|
||||
@@ -200,26 +291,41 @@ set( final_src
|
||||
include( AddAppIconMacro )
|
||||
set(ownCloud_old ${ownCloud})
|
||||
|
||||
kde4_add_app_icon( ownCloud "${theme_dir}/colored/${APPLICATION_SHORTNAME}-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)
|
||||
install(FILES
|
||||
${theme_dir}/colored/${APPLICATION_SHORTNAME}-icon-48.png
|
||||
DESTINATION share/icons/hicolor/48x48/apps/ RENAME ${APPLICATION_SHORTNAME}.png)
|
||||
file( GLOB _icons "${theme_dir}/colored/${ICON_APP_NAME}-icon-*.png" )
|
||||
foreach( _file ${_icons} )
|
||||
string( REPLACE "${theme_dir}/colored/${ICON_APP_NAME}-icon-" "" _res ${_file} )
|
||||
string( REPLACE ".png" "" _res ${_res} )
|
||||
install( FILES ${_file} RENAME ${ICON_APP_NAME}.png DESTINATION ${DATADIR}/icons/hicolor/${_res}x${_res}/apps )
|
||||
endforeach( _file )
|
||||
endif(NOT WIN32)
|
||||
|
||||
install(FILES ${mirall_I18N} DESTINATION share/mirall/i18n)
|
||||
install(FILES ${mirall_I18N} DESTINATION share/${APPLICATION_EXECUTABLE}/i18n)
|
||||
|
||||
# we may not add MACOSX_BUNDLE here, if not building one
|
||||
|
||||
# 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)
|
||||
@@ -229,13 +335,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})
|
||||
@@ -243,7 +343,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()
|
||||
|
||||
|
||||
@@ -251,7 +350,7 @@ set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY}
|
||||
)
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${QT_LIBRARIES} )
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} owncloudsync )
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${synclib_NAME} )
|
||||
target_link_libraries( ${APPLICATION_EXECUTABLE} ${CSYNC_LIBRARY} )
|
||||
|
||||
install(TARGETS ${APPLICATION_EXECUTABLE}
|
||||
@@ -265,7 +364,9 @@ 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})
|
||||
install_qt4_plugin(qsqlite ${OWNCLOUD_OSX_BUNDLE} 0 "")
|
||||
install_qt4_plugin(qtaccessiblewidgets ${OWNCLOUD_OSX_BUNDLE} 0 "")
|
||||
endif()
|
||||
|
||||
find_program(KRAZY2_EXECUTABLE krazy2)
|
||||
@@ -278,3 +379,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}
|
||||
)
|
||||
|
||||
54
src/creds/abstractcredentials.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_ABSTRACT_CREDENTIALS_H
|
||||
#define MIRALL_CREDS_ABSTRACT_CREDENTIALS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <csync.h>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class QNetworkReply;
|
||||
namespace Mirall
|
||||
{
|
||||
class Account;
|
||||
|
||||
class AbstractCredentials : public QObject
|
||||
{
|
||||
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 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();
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
78
src/creds/credentialscommon.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QList>
|
||||
#include <QRegExp>
|
||||
#include <QString>
|
||||
#include <QSslCertificate>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include "creds/credentialscommon.h"
|
||||
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/account.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
int handleNeonSSLProblems(const char* prompt,
|
||||
char* buf,
|
||||
size_t /*len*/,
|
||||
int /*echo*/,
|
||||
int /*verify*/,
|
||||
void* /*userdata*/)
|
||||
{
|
||||
int re = 0;
|
||||
const QString qPrompt = QString::fromLatin1( prompt ).trimmed();
|
||||
|
||||
if( qPrompt.startsWith( QLatin1String("There are problems with the SSL certificate:"))) {
|
||||
// SSL is requested. If the program came here, the SSL check was done by mirall
|
||||
// It needs to be checked if the chain is still equal to the one which
|
||||
// was verified by the user.
|
||||
const QRegExp regexp("fingerprint: ([\\w\\d:]+)");
|
||||
bool certOk = false;
|
||||
int pos = 0;
|
||||
// This is the set of certificates which QNAM accepted, so we should accept
|
||||
// them as well
|
||||
QList<QSslCertificate> certs = AccountManager::instance()->account()->certificateChain();
|
||||
|
||||
while (!certOk && (pos = regexp.indexIn(qPrompt, 1+pos)) != -1) {
|
||||
QString neon_fingerprint = regexp.cap(1);
|
||||
|
||||
foreach( const QSslCertificate& c, certs ) {
|
||||
QString verified_shasum = Utility::formatFingerprint(c.digest(QCryptographicHash::Sha1).toHex());
|
||||
qDebug() << "SSL Fingerprint from neon: " << neon_fingerprint << " compared to verified: " << verified_shasum;
|
||||
if( verified_shasum == neon_fingerprint ) {
|
||||
certOk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// certOk = false; DEBUG setting, keep disabled!
|
||||
if( !certOk ) { // Problem!
|
||||
qstrcpy( buf, "no" );
|
||||
re = -1;
|
||||
} else {
|
||||
qstrcpy( buf, "yes" ); // Certificate is fine!
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Unknown prompt: <" << prompt << ">";
|
||||
re = -1;
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
31
src/creds/credentialscommon.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_COMMON_H
|
||||
#define MIRALL_CREDS_COMMON_H
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
int handleNeonSSLProblems(const char* prompt,
|
||||
char* buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void* userdata);
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
43
src/creds/credentialsfactory.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "creds/httpcredentials.h"
|
||||
#include "creds/dummycredentials.h"
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace CredentialsFactory
|
||||
{
|
||||
|
||||
AbstractCredentials* create(const QString& type)
|
||||
{
|
||||
// empty string might happen for old version of configuration
|
||||
if (type == "http" || type == "") {
|
||||
return new HttpCredentials;
|
||||
} else if (type == "dummy") {
|
||||
return new DummyCredentials;
|
||||
} else if (type == "shibboleth") {
|
||||
return new ShibbolethCredentials;
|
||||
} else {
|
||||
qWarning("Unknown credentials type: %s", qPrintable(type));
|
||||
return new DummyCredentials;
|
||||
}
|
||||
}
|
||||
|
||||
} // ns CredentialsFactory
|
||||
|
||||
} // ns Mirall
|
||||
32
src/creds/credentialsfactory.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_CREDENTIALS_FACTORY_H
|
||||
#define MIRALL_CREDS_CREDENTIALS_FACTORY_H
|
||||
|
||||
class AbstractCredentials;
|
||||
class QString;
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace CredentialsFactory
|
||||
{
|
||||
|
||||
AbstractCredentials* create(const QString& type);
|
||||
|
||||
} // ns CredentialsFactory
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
73
src/creds/dummycredentials.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "creds/dummycredentials.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
void DummyCredentials::syncContextPreInit(CSYNC*)
|
||||
{}
|
||||
|
||||
void DummyCredentials::syncContextPreStart(CSYNC*)
|
||||
{}
|
||||
|
||||
bool DummyCredentials::changed(AbstractCredentials* credentials) const
|
||||
{
|
||||
DummyCredentials* dummy(dynamic_cast< DummyCredentials* >(credentials));
|
||||
|
||||
return dummy == 0;
|
||||
}
|
||||
|
||||
QString DummyCredentials::authType() const
|
||||
{
|
||||
return QString::fromLatin1("dummy");
|
||||
}
|
||||
|
||||
QString DummyCredentials::user() const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
|
||||
QNetworkAccessManager* DummyCredentials::getQNAM() const
|
||||
{
|
||||
return new MirallAccessManager;
|
||||
}
|
||||
|
||||
bool DummyCredentials::ready() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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::persist(Account*)
|
||||
{}
|
||||
|
||||
} // ns Mirall
|
||||
43
src/creds/dummycredentials.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_DUMMY_CREDENTIALS_H
|
||||
#define MIRALL_CREDS_DUMMY_CREDENTIALS_H
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class DummyCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
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
|
||||
|
||||
#endif
|
||||
81
src/creds/http/httpconfigfile.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "creds/http/httpconfigfile.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const char userC[] = "user";
|
||||
const char passwdC[] = "passwd";
|
||||
const char oldPasswdC[] = "password";
|
||||
|
||||
} // ns
|
||||
|
||||
QString HttpConfigFile::user() const
|
||||
{
|
||||
return retrieveData(QString(), QLatin1String(userC)).toString();
|
||||
}
|
||||
|
||||
void HttpConfigFile::setUser(const QString& user)
|
||||
{
|
||||
storeData(QString(), QLatin1String(userC), QVariant(user));
|
||||
}
|
||||
|
||||
QString HttpConfigFile::password() const
|
||||
{
|
||||
const QVariant passwd(retrieveData(QString(), QLatin1String(passwdC)));
|
||||
|
||||
if (passwd.isValid()) {
|
||||
return QString::fromUtf8(QByteArray::fromBase64(passwd.toByteArray()));
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
void HttpConfigFile::setPassword(const QString& password)
|
||||
{
|
||||
QByteArray pwdba = password.toUtf8();
|
||||
storeData( QString(), QLatin1String(passwdC), QVariant(pwdba.toBase64()) );
|
||||
removeOldPassword();
|
||||
}
|
||||
|
||||
bool HttpConfigFile::passwordExists() const
|
||||
{
|
||||
return dataExists(QString(), QLatin1String(passwdC));
|
||||
}
|
||||
|
||||
void HttpConfigFile::removePassword()
|
||||
{
|
||||
removeOldPassword();
|
||||
removeData(QString(), QLatin1String(passwdC));
|
||||
}
|
||||
|
||||
void HttpConfigFile::fixupOldPassword()
|
||||
{
|
||||
const QString old(QString::fromLatin1(oldPasswdC));
|
||||
|
||||
if (dataExists(QString(), old)) {
|
||||
setPassword(retrieveData(QString(), old).toString());
|
||||
}
|
||||
}
|
||||
|
||||
void HttpConfigFile::removeOldPassword()
|
||||
{
|
||||
removeData(QString(), QLatin1String(oldPasswdC));
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
40
src/creds/http/httpconfigfile.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_HTTP_CONFIG_FILE_H
|
||||
#define MIRALL_CREDS_HTTP_CONFIG_FILE_H
|
||||
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class HttpConfigFile : public MirallConfigFile
|
||||
{
|
||||
public:
|
||||
QString user() const;
|
||||
void setUser(const QString& user);
|
||||
|
||||
QString password() const;
|
||||
void setPassword(const QString& password);
|
||||
bool passwordExists() const;
|
||||
void removePassword();
|
||||
void fixupOldPassword();
|
||||
|
||||
private:
|
||||
void removeOldPassword();
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
314
src/creds/httpcredentials.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QMutex>
|
||||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QSettings>
|
||||
#include <QInputDialog>
|
||||
|
||||
#include <qtkeychain/keychain.h>
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
#include "creds/httpcredentials.h"
|
||||
|
||||
using namespace QKeychain;
|
||||
|
||||
Q_DECLARE_METATYPE(Mirall::Account*)
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int getauth(const char *prompt,
|
||||
char *buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void *userdata)
|
||||
{
|
||||
int re = 0;
|
||||
QMutex mutex;
|
||||
// ### safe?
|
||||
HttpCredentials* http_credentials = qobject_cast<HttpCredentials*>(AccountManager::instance()->account()->credentials());
|
||||
|
||||
if (!http_credentials) {
|
||||
qDebug() << "Not a HTTP creds instance!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString qPrompt = QString::fromLatin1( prompt ).trimmed();
|
||||
QString user = http_credentials->user();
|
||||
QString pwd = http_credentials->password();
|
||||
|
||||
if( qPrompt == QLatin1String("Enter your username:") ) {
|
||||
// qDebug() << "OOO Username requested!";
|
||||
QMutexLocker locker( &mutex );
|
||||
qstrncpy( buf, user.toUtf8().constData(), len );
|
||||
} else if( qPrompt == QLatin1String("Enter your password:") ) {
|
||||
QMutexLocker locker( &mutex );
|
||||
// qDebug() << "OOO Password requested!";
|
||||
qstrncpy( buf, pwd.toUtf8().constData(), len );
|
||||
} else {
|
||||
re = handleNeonSSLProblems(prompt, buf, len, echo, verify, userdata);
|
||||
}
|
||||
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)
|
||||
{}
|
||||
|
||||
HttpCredentials::HttpCredentials(const QString& user, const QString& password)
|
||||
: _user(user),
|
||||
_password(password),
|
||||
_ready(true)
|
||||
{
|
||||
}
|
||||
|
||||
void HttpCredentials::syncContextPreInit (CSYNC* ctx)
|
||||
{
|
||||
csync_set_auth_callback (ctx, getauth);
|
||||
}
|
||||
|
||||
void HttpCredentials::syncContextPreStart (CSYNC* ctx)
|
||||
{
|
||||
// TODO: This should not 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 remove this code and keep it in
|
||||
// csyncthread code (or folder code, git remembers).
|
||||
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
||||
QString cookiesAsString;
|
||||
|
||||
// Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
||||
// when https://github.com/owncloud/core/pull/4042 is merged.
|
||||
foreach(QNetworkCookie c, cookies) {
|
||||
cookiesAsString += c.name();
|
||||
cookiesAsString += '=';
|
||||
cookiesAsString += c.value();
|
||||
cookiesAsString += "; ";
|
||||
}
|
||||
|
||||
csync_set_module_property(ctx, "session_key", cookiesAsString.toLatin1().data());
|
||||
}
|
||||
|
||||
bool HttpCredentials::changed(AbstractCredentials* credentials) const
|
||||
{
|
||||
HttpCredentials* other(dynamic_cast< HttpCredentials* >(credentials));
|
||||
|
||||
if (!other || other->user() != this->user()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString HttpCredentials::authType() const
|
||||
{
|
||||
return QString::fromLatin1("http");
|
||||
}
|
||||
|
||||
QString HttpCredentials::user() const
|
||||
{
|
||||
return _user;
|
||||
}
|
||||
|
||||
QString HttpCredentials::password() const
|
||||
{
|
||||
return _password;
|
||||
}
|
||||
|
||||
QNetworkAccessManager* HttpCredentials::getQNAM() const
|
||||
{
|
||||
MirallAccessManager* qnam = new HttpCredentialsAccessManager(this);
|
||||
|
||||
connect( qnam, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
|
||||
this, SLOT(slotAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
|
||||
return qnam;
|
||||
}
|
||||
|
||||
bool HttpCredentials::ready() const
|
||||
{
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void HttpCredentials::fetch(Account *account)
|
||||
{
|
||||
_user = account->credentialSetting(QLatin1String(userC)).toString();
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
QString HttpCredentials::queryPassword(bool *ok)
|
||||
{
|
||||
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::invalidateToken(Account *account)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* authenticator)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
QString HttpCredentials::keychainKey(const QString &url, const QString &user)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
70
src/creds/httpcredentials.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_HTTP_CREDENTIALS_H
|
||||
#define MIRALL_CREDS_HTTP_CREDENTIALS_H
|
||||
|
||||
#include <QMap>
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
class QNetworkReply;
|
||||
class QAuthenticator;
|
||||
|
||||
namespace QKeychain {
|
||||
class Job;
|
||||
}
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class HttpCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
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(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 slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
void slotReadJobDone(QKeychain::Job*);
|
||||
void slotWriteJobDone(QKeychain::Job*);
|
||||
|
||||
private:
|
||||
static QString keychainKey(const QString &url, const QString &user);
|
||||
QString _user;
|
||||
QString _password;
|
||||
bool _ready;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
56
src/creds/shibboleth/shibbolethaccessmanager.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkCookieJar>
|
||||
|
||||
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ShibbolethAccessManager::ShibbolethAccessManager(const QNetworkCookie& cookie, QObject* parent)
|
||||
: MirallAccessManager (parent),
|
||||
_cookie(cookie)
|
||||
{}
|
||||
|
||||
QNetworkReply* ShibbolethAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
|
||||
{
|
||||
if (!_cookie.name().isEmpty()) {
|
||||
QNetworkCookieJar* jar(cookieJar());
|
||||
QUrl url(request.url());
|
||||
QList<QNetworkCookie> cookies;
|
||||
|
||||
Q_FOREACH(const QNetworkCookie& cookie, jar->cookiesForUrl(url)) {
|
||||
if (!cookie.name().startsWith("_shibsession_")) {
|
||||
cookies << cookie;
|
||||
}
|
||||
}
|
||||
|
||||
cookies << _cookie;
|
||||
jar->setCookiesFromUrl(cookies, url);
|
||||
}
|
||||
|
||||
qDebug() << "Creating a request to " << request.url().toString() << " with shibboleth cookie:" << _cookie.name();
|
||||
|
||||
return MirallAccessManager::createRequest (op, request, outgoingData);
|
||||
}
|
||||
|
||||
void ShibbolethAccessManager::setCookie(const QNetworkCookie& cookie)
|
||||
{
|
||||
qDebug() << "Got new shibboleth cookie:" << cookie.name();
|
||||
_cookie = cookie;
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
43
src/creds/shibboleth/shibbolethaccessmanager.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_WIZARD_SHIBBOLETH_ACCESS_MANAGER_H
|
||||
#define MIRALL_WIZARD_SHIBBOLETH_ACCESS_MANAGER_H
|
||||
|
||||
#include <QNetworkCookie>
|
||||
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethAccessManager : public MirallAccessManager
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethAccessManager(const QNetworkCookie& cookie, QObject* parent = 0);
|
||||
|
||||
public Q_SLOTS:
|
||||
void setCookie(const QNetworkCookie& cookie);
|
||||
|
||||
protected:
|
||||
QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0);
|
||||
|
||||
private:
|
||||
QNetworkCookie _cookie;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
104
src/creds/shibboleth/shibbolethconfigfile.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "creds/shibboleth/shibbolethconfigfile.h"
|
||||
#include "creds/shibboleth/shibbolethcookiejar.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const char otherCookiesC[] = "otherCookies";
|
||||
|
||||
} // ns
|
||||
|
||||
void ShibbolethConfigFile::storeCookies(const QMap<QUrl, QList<QNetworkCookie> >& cookiesForUrl)
|
||||
{
|
||||
if (cookiesForUrl.isEmpty()) {
|
||||
removeData(QString(), QString::fromLatin1(otherCookiesC));
|
||||
} else {
|
||||
QByteArray data;
|
||||
QTextStream stream(&data);
|
||||
|
||||
Q_FOREACH (const QUrl& url, cookiesForUrl.keys()) {
|
||||
const QList<QNetworkCookie>& cookies(cookiesForUrl[url]);
|
||||
|
||||
if (cookies.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
stream << "URL: " << url.toString().toUtf8() << "\n";
|
||||
qDebug() << "URL: " << url.toString().toUtf8();
|
||||
|
||||
Q_FOREACH (const QNetworkCookie& cookie, cookies) {
|
||||
stream << cookie.toRawForm(QNetworkCookie::NameAndValueOnly) << "\n";
|
||||
qDebug() << cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
|
||||
}
|
||||
}
|
||||
|
||||
stream.flush();
|
||||
|
||||
const QByteArray encodedCookies(data.toBase64());
|
||||
|
||||
qDebug() << "Raw cookies:\n" << data;
|
||||
qDebug() << "Encoded cookies: " << encodedCookies;
|
||||
|
||||
storeData(QString(), QString::fromLatin1(otherCookiesC), QVariant(encodedCookies));
|
||||
}
|
||||
}
|
||||
|
||||
ShibbolethCookieJar* ShibbolethConfigFile::createCookieJar() const
|
||||
{
|
||||
ShibbolethCookieJar* jar = new ShibbolethCookieJar();
|
||||
const QVariant variant(retrieveData(QString(), QString::fromLatin1(otherCookiesC)));
|
||||
|
||||
if (variant.isValid()) {
|
||||
QByteArray data(QByteArray::fromBase64(variant.toByteArray()));
|
||||
QTextStream stream (&data);
|
||||
const QString urlHeader(QString::fromLatin1("URL: "));
|
||||
QUrl currentUrl;
|
||||
QList<QNetworkCookie> currentCookies;
|
||||
|
||||
qDebug() << "Got valid cookies variant: " << data;
|
||||
|
||||
while (!stream.atEnd()) {
|
||||
const QString line(stream.readLine());
|
||||
|
||||
qDebug() << line;
|
||||
|
||||
if (line.startsWith(urlHeader)) {
|
||||
if (!currentUrl.isEmpty() && !currentCookies.isEmpty()) {
|
||||
jar->setCookiesFromUrl(currentCookies, currentUrl);
|
||||
currentCookies.clear();
|
||||
currentUrl.clear();
|
||||
}
|
||||
currentUrl = QUrl(line.mid(5));
|
||||
} else if (!currentUrl.isEmpty()) {
|
||||
const int equalPos(line.indexOf('='));
|
||||
|
||||
currentCookies << QNetworkCookie(line.left(equalPos).toUtf8(), line.mid(equalPos + 1).toUtf8());
|
||||
}
|
||||
}
|
||||
if (!currentUrl.isEmpty() && !currentCookies.isEmpty()) {
|
||||
jar->setCookiesFromUrl(currentCookies, currentUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return jar;
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
38
src/creds/shibboleth/shibbolethconfigfile.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_SHIBBOLETH_CONFIG_FILE_H
|
||||
#define MIRALL_CREDS_SHIBBOLETH_CONFIG_FILE_H
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QNetworkCookie>
|
||||
#include <QUrl>
|
||||
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethCookieJar;
|
||||
|
||||
class ShibbolethConfigFile : public MirallConfigFile
|
||||
{
|
||||
public:
|
||||
void storeCookies(const QMap<QUrl, QList<QNetworkCookie> >& cookies);
|
||||
ShibbolethCookieJar* createCookieJar() const;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
34
src/creds/shibboleth/shibbolethcookiejar.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include "creds/shibboleth/shibbolethcookiejar.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ShibbolethCookieJar::ShibbolethCookieJar (QObject* parent)
|
||||
: QNetworkCookieJar (parent)
|
||||
{}
|
||||
|
||||
bool ShibbolethCookieJar::setCookiesFromUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url)
|
||||
{
|
||||
if (QNetworkCookieJar::setCookiesFromUrl (cookieList, url)) {
|
||||
Q_EMIT newCookiesForUrl (cookieList, url);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
41
src/creds/shibboleth/shibbolethcookiejar.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_WIZARD_SHIBBOLETH_COOKIE_JAR_H
|
||||
#define MIRALL_WIZARD_SHIBBOLETH_COOKIE_JAR_H
|
||||
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QList>
|
||||
|
||||
class QUrl;
|
||||
class QNetworkCookie;
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethCookieJar : public QNetworkCookieJar
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethCookieJar (QObject* parent = 0);
|
||||
|
||||
virtual bool setCookiesFromUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
|
||||
Q_SIGNALS:
|
||||
void newCookiesForUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
55
src/creds/shibboleth/shibbolethrefresher.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "mirall/account.h"
|
||||
#include "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ShibbolethRefresher::ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
|
||||
: QObject(parent),
|
||||
_account(account),
|
||||
_creds(creds),
|
||||
_csync_ctx(csync_ctx)
|
||||
{}
|
||||
|
||||
void ShibbolethRefresher::refresh()
|
||||
{
|
||||
QEventLoop loop;
|
||||
|
||||
connect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
this, SLOT(onInvalidatedAndFetched(QByteArray)));
|
||||
connect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
QMetaObject::invokeMethod(_creds, "invalidateAndFetch",Qt::QueuedConnection,
|
||||
Q_ARG(Account*, _account));
|
||||
loop.exec();
|
||||
disconnect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
&loop, SLOT(quit()));
|
||||
}
|
||||
|
||||
void ShibbolethRefresher::onInvalidatedAndFetched(const QByteArray& cookies)
|
||||
{
|
||||
// "cookies" is const and its data() return const void*. We want just void*.
|
||||
QByteArray myCookies(cookies);
|
||||
|
||||
disconnect(_creds, SIGNAL(invalidatedAndFetched(QByteArray)),
|
||||
this, SLOT(onInvalidatedAndFetched(QByteArray)));
|
||||
csync_set_module_property(_csync_ctx, "session_key", myCookies.data());
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
49
src/creds/shibboleth/shibbolethrefresher.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_SHIBBOLETH_REFRESHER_H
|
||||
#define MIRALL_CREDS_SHIBBOLETH_REFRESHER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <csync.h>
|
||||
|
||||
class QByteArray;
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class Account;
|
||||
class ShibbolethCredentials;
|
||||
|
||||
class ShibbolethRefresher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethRefresher(Account *account, ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
|
||||
|
||||
void refresh();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onInvalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
Account* _account;
|
||||
ShibbolethCredentials* _creds;
|
||||
CSYNC* _csync_ctx;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
124
src/creds/shibboleth/shibbolethwebview.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#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(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);
|
||||
connect(jar, SIGNAL (newCookiesForUrl (QList<QNetworkCookie>, QUrl)),
|
||||
this, SLOT (onNewCookiesForUrl (QList<QNetworkCookie>, QUrl)));
|
||||
connect(page, SIGNAL(loadStarted()),
|
||||
this, SLOT(slotLoadStarted()));
|
||||
connect(page, SIGNAL(loadFinished(bool)),
|
||||
this, SLOT(slotLoadFinished(bool)));
|
||||
|
||||
nm->setCookieJar(jar);
|
||||
page->setNetworkAccessManager(nm);
|
||||
page->mainFrame()->load(account->url());
|
||||
this->setPage(page);
|
||||
setWindowTitle(tr("%1 - Authenticate").arg(Theme::instance()->appNameGUI()));
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(Account* account, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(account, new ShibbolethCookieJar(this));
|
||||
}
|
||||
|
||||
ShibbolethWebView::~ShibbolethWebView()
|
||||
{
|
||||
slotLoadFinished();
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(Account* account, ShibbolethCookieJar* jar, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(account, jar);
|
||||
}
|
||||
|
||||
void ShibbolethWebView::onNewCookiesForUrl (const QList<QNetworkCookie>& cookieList, const QUrl& url)
|
||||
{
|
||||
QList<QNetworkCookie> otherCookies;
|
||||
QNetworkCookie shibCookie;
|
||||
|
||||
Q_FOREACH (const QNetworkCookie& cookie, cookieList) {
|
||||
if (cookie.name().startsWith ("_shibsession_")) {
|
||||
if (shibCookie.name().isEmpty()) {
|
||||
shibCookie = cookie;
|
||||
} else {
|
||||
qWarning() << "Too many Shibboleth session cookies at once!";
|
||||
}
|
||||
} else {
|
||||
otherCookies << cookie;
|
||||
}
|
||||
}
|
||||
|
||||
if (!otherCookies.isEmpty()) {
|
||||
Q_EMIT otherCookiesReceived(otherCookies, url);
|
||||
}
|
||||
if (!shibCookie.name().isEmpty()) {
|
||||
Q_EMIT shibbolethCookieReceived(shibCookie);
|
||||
}
|
||||
}
|
||||
|
||||
void ShibbolethWebView::hideEvent(QHideEvent* event)
|
||||
{
|
||||
Q_EMIT viewHidden();
|
||||
QWebView::hideEvent(event);
|
||||
}
|
||||
|
||||
void ShibbolethWebView::slotLoadStarted()
|
||||
{
|
||||
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
||||
}
|
||||
|
||||
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
|
||||
57
src/creds/shibboleth/shibbolethwebview.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_WIZARD_SHIBBOLETH_WEB_VIEW_H
|
||||
#define MIRALL_WIZARD_SHIBBOLETH_WEB_VIEW_H
|
||||
|
||||
#include <QList>
|
||||
#include <QWebView>
|
||||
|
||||
class QNetworkCookie;
|
||||
class QUrl;
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethCookieJar;
|
||||
class Account;
|
||||
|
||||
class ShibbolethWebView : public QWebView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethWebView(Account *account, QWidget* parent = 0);
|
||||
ShibbolethWebView(Account *account, ShibbolethCookieJar* jar, QWidget* parent = 0);
|
||||
~ShibbolethWebView();
|
||||
|
||||
protected:
|
||||
void hideEvent(QHideEvent* event);
|
||||
|
||||
Q_SIGNALS:
|
||||
void shibbolethCookieReceived(const QNetworkCookie& cookie);
|
||||
void viewHidden();
|
||||
void otherCookiesReceived(const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onNewCookiesForUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url);
|
||||
void slotLoadStarted();
|
||||
void slotLoadFinished(bool success = true);
|
||||
|
||||
private:
|
||||
void setup(Account *account, ShibbolethCookieJar* jar);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
269
src/creds/shibbolethcredentials.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
* 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 <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/account.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int shibboleth_redirect_callback(CSYNC* csync_ctx,
|
||||
const char* uri)
|
||||
{
|
||||
if (!csync_ctx || !uri) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const QString qurl(QString::fromLatin1(uri));
|
||||
QRegExp shibbolethyWords ("SAML|wayf");
|
||||
|
||||
shibbolethyWords.setCaseSensitivity (Qt::CaseInsensitive);
|
||||
if (!qurl.contains(shibbolethyWords)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
QMutex mutex;
|
||||
QMutexLocker locker(&mutex);
|
||||
Account *account = AccountManager::instance()->account();
|
||||
ShibbolethCredentials* creds = qobject_cast<ShibbolethCredentials*>(account->credentials());
|
||||
|
||||
|
||||
if (!creds) {
|
||||
qDebug() << "Not a Shibboleth creds instance!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
ShibbolethRefresher refresher(account, creds, csync_ctx);
|
||||
|
||||
// blocks
|
||||
refresher.refresh();
|
||||
|
||||
return creds->ready() ? 0 : 1;
|
||||
}
|
||||
|
||||
} // ns
|
||||
|
||||
ShibbolethCredentials::ShibbolethCredentials()
|
||||
: AbstractCredentials(),
|
||||
_url(),
|
||||
_shibCookie(),
|
||||
_ready(false),
|
||||
_browser(0),
|
||||
_otherCookies()
|
||||
{}
|
||||
|
||||
ShibbolethCredentials::ShibbolethCredentials(const QNetworkCookie& cookie, const QMap<QUrl, QList<QNetworkCookie> >& otherCookies)
|
||||
: _shibCookie(cookie),
|
||||
_ready(true),
|
||||
_browser(0),
|
||||
_otherCookies(otherCookies)
|
||||
{}
|
||||
|
||||
void ShibbolethCredentials::syncContextPreInit(CSYNC* ctx)
|
||||
{
|
||||
csync_set_auth_callback (ctx, handleNeonSSLProblems);
|
||||
}
|
||||
|
||||
QByteArray ShibbolethCredentials::prepareCookieData() const
|
||||
{
|
||||
QString cookiesAsString;
|
||||
// TODO: This should not 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 just append shibboleth
|
||||
// cookies to the "session_key" value and set it in csync module.
|
||||
QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
|
||||
QMap<QString, QString> uniqueCookies;
|
||||
|
||||
cookies << _shibCookie;
|
||||
// Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
|
||||
// when https://github.com/owncloud/core/pull/4042 is merged.
|
||||
foreach(QNetworkCookie c, cookies) {
|
||||
const QString cookieName(c.name());
|
||||
|
||||
if (cookieName.startsWith("_shibsession_")) {
|
||||
continue;
|
||||
}
|
||||
uniqueCookies.insert(cookieName, c.value());
|
||||
}
|
||||
|
||||
if (!_shibCookie.name().isEmpty()) {
|
||||
uniqueCookies.insert(_shibCookie.name(), _shibCookie.value());
|
||||
}
|
||||
foreach(const QString& cookieName, uniqueCookies.keys()) {
|
||||
cookiesAsString += cookieName;
|
||||
cookiesAsString += '=';
|
||||
cookiesAsString += uniqueCookies[cookieName];
|
||||
cookiesAsString += "; ";
|
||||
}
|
||||
|
||||
return cookiesAsString.toLatin1();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::syncContextPreStart (CSYNC* ctx)
|
||||
{
|
||||
typedef int (*csync_owncloud_redirect_callback_t)(CSYNC* ctx, const char* uri);
|
||||
|
||||
csync_owncloud_redirect_callback_t cb = shibboleth_redirect_callback;
|
||||
|
||||
csync_set_module_property(ctx, "session_key", prepareCookieData().data());
|
||||
csync_set_module_property(ctx, "redirect_callback", &cb);
|
||||
}
|
||||
|
||||
bool ShibbolethCredentials::changed(AbstractCredentials* credentials) const
|
||||
{
|
||||
ShibbolethCredentials* other(dynamic_cast< ShibbolethCredentials* >(credentials));
|
||||
|
||||
if (!other || other->cookie() != this->cookie()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QNetworkAccessManager* ShibbolethCredentials::getQNAM() const
|
||||
{
|
||||
ShibbolethAccessManager* qnam(new ShibbolethAccessManager(_shibCookie));
|
||||
|
||||
connect(this, SIGNAL(newCookie(QNetworkCookie)),
|
||||
qnam, SLOT(setCookie(QNetworkCookie)));
|
||||
return qnam;
|
||||
}
|
||||
|
||||
bool ShibbolethCredentials::ready() const
|
||||
{
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::fetch(Account *account)
|
||||
{
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
ShibbolethConfigFile cfg;
|
||||
if (account) {
|
||||
_url = account->url();
|
||||
}
|
||||
_browser = new ShibbolethWebView(account, cfg.createCookieJar());
|
||||
connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
||||
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
||||
connect(_browser, SIGNAL(viewHidden()),
|
||||
this, SLOT(slotBrowserHidden()));
|
||||
_browser->show ();
|
||||
}
|
||||
}
|
||||
|
||||
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()),
|
||||
this, SLOT(slotBrowserHidden()));
|
||||
disconnect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
||||
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
||||
_browser->hide();
|
||||
_browser->deleteLater();
|
||||
_browser = 0;
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::onShibbolethCookieReceived(const QNetworkCookie& cookie)
|
||||
{
|
||||
disposeBrowser();
|
||||
_ready = true;
|
||||
_shibCookie = cookie;
|
||||
Q_EMIT newCookie(_shibCookie);
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::slotBrowserHidden()
|
||||
{
|
||||
disposeBrowser();
|
||||
_ready = false;
|
||||
_shibCookie = QNetworkCookie();
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::invalidateAndFetch(Account* account)
|
||||
{
|
||||
_ready = false;
|
||||
connect (this, SIGNAL(fetched()),
|
||||
this, SLOT(onFetched()));
|
||||
// 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()
|
||||
{
|
||||
disconnect (this, SIGNAL(fetched()),
|
||||
this, SLOT(onFetched()));
|
||||
|
||||
Q_EMIT invalidatedAndFetched(prepareCookieData());
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
77
src/creds/shibbolethcredentials.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifndef MIRALL_CREDS_SHIBBOLETH_CREDENTIALS_H
|
||||
#define MIRALL_CREDS_SHIBBOLETH_CREDENTIALS_H
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QNetworkCookie>
|
||||
#include <QUrl>
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
class ShibbolethWebView;
|
||||
|
||||
class ShibbolethCredentials : public AbstractCredentials
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethCredentials();
|
||||
ShibbolethCredentials(const QNetworkCookie& cookie, const QMap<QUrl, QList<QNetworkCookie> >& otherCookies);
|
||||
|
||||
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;
|
||||
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(Account *account);
|
||||
|
||||
private Q_SLOTS:
|
||||
void onShibbolethCookieReceived(const QNetworkCookie& cookie);
|
||||
void slotBrowserHidden();
|
||||
void onFetched();
|
||||
|
||||
Q_SIGNALS:
|
||||
void newCookie(const QNetworkCookie& cookie);
|
||||
void invalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
QUrl _url;
|
||||
QByteArray prepareCookieData() const;
|
||||
void disposeBrowser();
|
||||
|
||||
QNetworkCookie _shibCookie;
|
||||
bool _ready;
|
||||
ShibbolethWebView* _browser;
|
||||
QMap<QUrl, QList<QNetworkCookie> > _otherCookies;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
43
src/main.cpp
@@ -11,8 +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)
|
||||
{
|
||||
@@ -20,7 +36,14 @@ int main(int argc, char **argv)
|
||||
|
||||
Mirall::Application app(argc, argv);
|
||||
app.initialize();
|
||||
|
||||
#ifndef Q_OS_WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
if( app.giveHelp() ) {
|
||||
app.showHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if the application is already running, notify it.
|
||||
if( app.isRunning() ) {
|
||||
QStringList args = app.arguments();
|
||||
@@ -30,12 +53,20 @@ int main(int argc, char **argv)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// if help requested, show on command line and exit.
|
||||
if( ! app.giveHelp() ) {
|
||||
return app.exec();
|
||||
} else {
|
||||
app.showHelp();
|
||||
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);
|
||||
}
|
||||
if (!changes) {
|
||||
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
|
||||
810
src/mirall/accountsettings.cpp
Normal file
@@ -0,0 +1,810 @@
|
||||
/*
|
||||
* 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 "accountsettings.h"
|
||||
#include "ui_accountsettings.h"
|
||||
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/folderwizard.h"
|
||||
#include "mirall/folderstatusmodel.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/owncloudsetupwizard.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/ignorelisteditor.h"
|
||||
#include "mirall/account.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMessageBox>
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
#include <QIcon>
|
||||
#include <QVariant>
|
||||
|
||||
#include "mirall/account.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
static const char progressBarStyleC[] =
|
||||
"QProgressBar {"
|
||||
"border: 1px solid grey;"
|
||||
"border-radius: 5px;"
|
||||
"text-align: center;"
|
||||
"}"
|
||||
"QProgressBar::chunk {"
|
||||
"background-color: %1; width: 1px;"
|
||||
"}";
|
||||
|
||||
AccountSettings::AccountSettings(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::AccountSettings),
|
||||
_wasDisabledBefore(false),
|
||||
_account(AccountManager::instance()->account())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
_model = new FolderStatusModel;
|
||||
_model->setParent(this);
|
||||
FolderStatusDelegate *delegate = new FolderStatusDelegate;
|
||||
delegate->setParent(this);
|
||||
|
||||
ui->_folderList->setItemDelegate( delegate );
|
||||
ui->_folderList->setModel( _model );
|
||||
ui->_folderList->setMinimumWidth( 300 );
|
||||
ui->_folderList->setEditTriggers( QAbstractItemView::NoEditTriggers );
|
||||
|
||||
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);
|
||||
|
||||
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()));;
|
||||
|
||||
connect(ui->_folderList, SIGNAL(clicked(QModelIndex)), SLOT(slotFolderActivated(QModelIndex)));
|
||||
connect(ui->_folderList, SIGNAL(doubleClicked(QModelIndex)),SLOT(slotDoubleClicked(QModelIndex)));
|
||||
|
||||
QColor color = palette().highlight().color();
|
||||
ui->quotaProgressBar->setStyleSheet(QString::fromLatin1(progressBarStyleC).arg(color.name()));
|
||||
ui->connectLabel->setWordWrap(true);
|
||||
ui->connectLabel->setOpenExternalLinks(true);
|
||||
ui->quotaLabel->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());
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderActivated( const QModelIndex& indx )
|
||||
{
|
||||
bool state = indx.isValid();
|
||||
|
||||
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" ) );
|
||||
} else {
|
||||
ui->_buttonEnable->setText( tr( "Resume" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AccountSettings::slotAddFolder()
|
||||
{
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->setSyncEnabled(false); // do not start more syncs.
|
||||
|
||||
FolderWizard *folderWizard = new FolderWizard(this);
|
||||
Folder::Map folderMap = folderMan->map();
|
||||
folderWizard->setFolderMap( folderMap );
|
||||
|
||||
connect(folderWizard, SIGNAL(accepted()), SLOT(slotFolderWizardAccepted()));
|
||||
connect(folderWizard, SIGNAL(rejected()), SLOT(slotFolderWizardRejected()));
|
||||
folderWizard->open();
|
||||
}
|
||||
|
||||
|
||||
void AccountSettings::slotFolderWizardAccepted()
|
||||
{
|
||||
FolderWizard *folderWizard = qobject_cast<FolderWizard*>(sender());
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
qDebug() << "* Folder wizard completed";
|
||||
|
||||
QString alias = folderWizard->field(QLatin1String("alias")).toString();
|
||||
QString sourceFolder = folderWizard->field(QLatin1String("sourceFolder")).toString();
|
||||
QString targetPath = folderWizard->property("targetPath").toString();
|
||||
|
||||
if (!FolderMan::ensureJournalGone( sourceFolder ))
|
||||
return;
|
||||
folderMan->addFolderDefinition(alias, sourceFolder, targetPath );
|
||||
Folder *f = folderMan->setupFolderFromConfigFile( alias );
|
||||
slotAddFolder( f );
|
||||
folderMan->setSyncEnabled(true);
|
||||
if( f ) {
|
||||
folderMan->slotScheduleAllFolders();
|
||||
emit folderChanged();
|
||||
}
|
||||
slotButtonsSetEnabled();
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderWizardRejected()
|
||||
{
|
||||
qDebug() << "* Folder wizard cancelled";
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->setSyncEnabled(true);
|
||||
folderMan->slotScheduleAllFolders();
|
||||
}
|
||||
|
||||
void AccountSettings::slotOpenAccountWizard()
|
||||
{
|
||||
this->topLevelWidget()->close();
|
||||
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), 0);
|
||||
}
|
||||
|
||||
void AccountSettings::slotAddFolder( Folder *folder )
|
||||
{
|
||||
if( ! folder || folder->alias().isEmpty() ) return;
|
||||
|
||||
QStandardItem *item = new QStandardItem();
|
||||
folderToModelItem( item, folder );
|
||||
_model->appendRow( item );
|
||||
// in order to update the enabled state of the "Sync now" button
|
||||
connect(folder, SIGNAL(syncStateChange()), this, SLOT(slotButtonsSetEnabled()), Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AccountSettings::slotButtonsSetEnabled()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->currentIndex();
|
||||
bool isSelected = selected.isValid();
|
||||
if (isSelected) {
|
||||
slotFolderActivated(selected);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::setGeneralErrors( const QStringList& errors )
|
||||
{
|
||||
_generalErrors = errors;
|
||||
}
|
||||
|
||||
void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
{
|
||||
if( ! item || !f ) return;
|
||||
|
||||
item->setData( f->nativePath(), FolderStatusDelegate::FolderPathRole );
|
||||
item->setData( f->remotePath(), FolderStatusDelegate::FolderSecondPathRole );
|
||||
item->setData( f->alias(), FolderStatusDelegate::FolderAliasRole );
|
||||
item->setData( f->syncEnabled(), FolderStatusDelegate::FolderSyncEnabled );
|
||||
|
||||
SyncResult res = f->syncResult();
|
||||
SyncResult::Status status = res.status();
|
||||
|
||||
QStringList errorList = res.errorStrings();
|
||||
|
||||
Theme *theme = Theme::instance();
|
||||
item->setData( theme->statusHeaderText( status ), Qt::ToolTipRole );
|
||||
if( f->syncEnabled() ) {
|
||||
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
|
||||
_wasDisabledBefore = false;
|
||||
}
|
||||
item->setData( theme->statusHeaderText( status ), FolderStatusDelegate::FolderStatus );
|
||||
|
||||
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 ) {
|
||||
ongoing = true;
|
||||
}
|
||||
item->setData( ongoing, FolderStatusDelegate::SyncRunning);
|
||||
|
||||
}
|
||||
|
||||
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() ) {
|
||||
// remove from file system through folder man
|
||||
// _model->removeRow( selected.row() );
|
||||
int ret = QMessageBox::question( this, tr("Confirm Folder Remove"),
|
||||
tr("<p>Do you really want to stop syncing the folder <i>%1</i>?</p>"
|
||||
"<p><b>Note:</b> This will not remove the files from your client.</p>").arg(alias),
|
||||
QMessageBox::Yes|QMessageBox::No );
|
||||
|
||||
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 );
|
||||
_model->removeRow(row);
|
||||
|
||||
emit folderChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
void AccountSettings::slotResetCurrentFolder()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
||||
if( selected.isValid() ) {
|
||||
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
int ret = QMessageBox::question( 0, tr("Confirm Folder Reset"),
|
||||
tr("<p>Do you really want to reset folder <i>%1</i> and rebuild your client database?</p>"
|
||||
"<p><b>Note:</b> This function is designed for maintenance purposes only. "
|
||||
"No files will be removed, but this can cause significant data traffic and "
|
||||
"take several minutes or hours to complete, depending on the size of the folder. "
|
||||
"Only use this option if advised by your administrator.</p>").arg(alias),
|
||||
QMessageBox::Yes|QMessageBox::No );
|
||||
if( ret == QMessageBox::Yes ) {
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
Folder *f = folderMan->folder(alias);
|
||||
f->slotTerminateSync(true);
|
||||
f->wipe();
|
||||
folderMan->slotScheduleAllFolders();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotDoubleClicked( const QModelIndex& indx )
|
||||
{
|
||||
if( ! indx.isValid() ) return;
|
||||
QString alias = _model->data( indx, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
|
||||
emit openFolderAlias( alias );
|
||||
}
|
||||
|
||||
void AccountSettings::showConnectionLabel( const QString& message, const QString& tooltip )
|
||||
{
|
||||
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 {
|
||||
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 ) {
|
||||
slotAddFolder( f );
|
||||
}
|
||||
|
||||
QModelIndex idx = _model->index(0, 0);
|
||||
if (idx.isValid()) {
|
||||
ui->_folderList->setCurrentIndex(idx);
|
||||
}
|
||||
slotButtonsSetEnabled();
|
||||
|
||||
}
|
||||
|
||||
// move from Application
|
||||
void AccountSettings::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 AccountSettings::slotEnableCurrentFolder()
|
||||
{
|
||||
QModelIndex selected = ui->_folderList->selectionModel()->currentIndex();
|
||||
|
||||
if( selected.isValid() ) {
|
||||
QString alias = _model->data( selected, FolderStatusDelegate::FolderAliasRole ).toString();
|
||||
bool folderEnabled = _model->data( selected, FolderStatusDelegate::FolderSyncEnabled).toBool();
|
||||
qDebug() << "Toggle enabled/disabled Folder alias " << alias << " - current state: " << folderEnabled;
|
||||
if( !alias.isEmpty() ) {
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
|
||||
qDebug() << "Application: enable folder with alias " << alias;
|
||||
bool terminate = false;
|
||||
|
||||
// this sets the folder status to disabled but does not interrupt it.
|
||||
Folder *f = folderMan->folder( alias );
|
||||
if( f && folderEnabled ) {
|
||||
// check if a sync is still running and if so, ask if we should terminate.
|
||||
if( f->isBusy() ) { // its still running
|
||||
int reply = QMessageBox::question( 0, tr("Sync Running"),
|
||||
tr("The syncing operation is running.<br/>Do you want to terminate it?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes );
|
||||
if ( reply == QMessageBox::Yes )
|
||||
terminate = true;
|
||||
else
|
||||
return; // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ) {
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
int row = 0;
|
||||
|
||||
if( ! folder ) return;
|
||||
|
||||
item = _model->item( row );
|
||||
|
||||
while( item ) {
|
||||
if( item->data( FolderStatusDelegate::FolderAliasRole ) == folder->alias() ) {
|
||||
// its the item to update!
|
||||
break;
|
||||
}
|
||||
item = _model->item( ++row );
|
||||
}
|
||||
|
||||
if( item ) {
|
||||
folderToModelItem( item, folder );
|
||||
} else {
|
||||
// the dialog is not visible.
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
// 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
|
||||
// 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*)));
|
||||
//}
|
||||
|
||||
//void AccountSettings::slotOCInfoFail( QNetworkReply *reply)
|
||||
//{
|
||||
// QString errStr = tr("unknown problem.");
|
||||
// if( reply ) errStr = reply->errorString();
|
||||
|
||||
// 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()
|
||||
{
|
||||
if( _OCUrl.isValid() )
|
||||
QDesktopServices::openUrl( _OCUrl );
|
||||
}
|
||||
|
||||
QStandardItem* AccountSettings::itemForFolder(const QString& folder)
|
||||
{
|
||||
QStandardItem *item = NULL;
|
||||
|
||||
if( folder.isEmpty() ) {
|
||||
return item;
|
||||
}
|
||||
|
||||
int row = 0;
|
||||
|
||||
item = _model->item( row );
|
||||
|
||||
while( item ) {
|
||||
if( item->data( FolderStatusDelegate::FolderAliasRole ) == folder ) {
|
||||
// its the item to update!
|
||||
break;
|
||||
}
|
||||
item = _model->item( ++row );
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
QString AccountSettings::shortenFilename( const QString& folder, const QString& file ) const
|
||||
{
|
||||
// strip off the server prefix from the file name
|
||||
QString shortFile(file);
|
||||
if( shortFile.isEmpty() ) {
|
||||
return QString::null;
|
||||
}
|
||||
|
||||
if(shortFile.startsWith(QLatin1String("ownclouds://")) ||
|
||||
shortFile.startsWith(QLatin1String("owncloud://")) ) {
|
||||
// rip off the whole ownCloud URL.
|
||||
Folder *f = FolderMan::instance()->folder(folder);
|
||||
if( f ) {
|
||||
QString remotePathUrl = f->remoteUrl().toString();
|
||||
shortFile.remove(Utility::toCSyncScheme(remotePathUrl));
|
||||
}
|
||||
}
|
||||
return shortFile;
|
||||
}
|
||||
|
||||
void AccountSettings::slotProgressProblem(const QString& folder, const Progress::SyncProblem& problem)
|
||||
{
|
||||
Q_UNUSED(problem);
|
||||
|
||||
QStandardItem *item = itemForFolder( folder );
|
||||
if( !item ) return;
|
||||
|
||||
int warnCount = qvariant_cast<int>( item->data(FolderStatusDelegate::WarningCount) );
|
||||
warnCount++;
|
||||
item->setData( QVariant(warnCount), FolderStatusDelegate::WarningCount );
|
||||
}
|
||||
|
||||
void AccountSettings::slotSetProgress(const QString& folder, const Progress::Info &progress )
|
||||
{
|
||||
// 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;
|
||||
|
||||
if( item == NULL ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hotfix for a crash that I experienced in a very rare case/setup
|
||||
if (progress.kind == Mirall::Progress::Invalid) {
|
||||
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;
|
||||
|
||||
// stay with the previous kind-string for Context.
|
||||
if( progress.kind != Progress::Context ) {
|
||||
_kindContext = Progress::asActionString(progress.kind);
|
||||
} else {
|
||||
if( _kindContext.isEmpty() ) {
|
||||
// empty kind context means that the dialog was opened after the action
|
||||
// was started.
|
||||
Progress::Kind kind = ProgressDispatcher::instance()->currentFolderContext(progress.folder);
|
||||
if( kind != Progress::Invalid ) {
|
||||
_kindContext = Progress::asActionString(kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
QString kindString = _kindContext;
|
||||
|
||||
switch( progress.kind ) {
|
||||
case Progress::StartSync:
|
||||
item->setData( QVariant(0), FolderStatusDelegate::WarningCount );
|
||||
break;
|
||||
case Progress::StartDownload:
|
||||
case Progress::StartUpload:
|
||||
case Progress::StartDelete:
|
||||
case Progress::StartRename:
|
||||
syncFileProgressString = tr("Start");
|
||||
if( _hideProgressTimers.contains(item) ) {
|
||||
// The timer is still running.
|
||||
QTimer *t = _hideProgressTimers.take(item);
|
||||
t->stop();
|
||||
t->deleteLater();
|
||||
}
|
||||
break;
|
||||
case Progress::Context:
|
||||
syncFileProgressString = tr("Currently");
|
||||
break;
|
||||
case Progress::EndDownload:
|
||||
case Progress::EndUpload:
|
||||
case Progress::EndDelete:
|
||||
case Progress::EndRename:
|
||||
break;
|
||||
case Progress::EndSync:
|
||||
syncFileProgressString = tr("Completely");
|
||||
|
||||
// start a timer to stop the progress display
|
||||
QTimer *timer;
|
||||
if( _hideProgressTimers.contains(item) ) {
|
||||
timer = _hideProgressTimers[item];
|
||||
// there is already one timer running.
|
||||
} else {
|
||||
timer = new QTimer;
|
||||
connect(timer, SIGNAL(timeout()), this, SLOT(slotHideProgress()));
|
||||
timer->setSingleShot(true);
|
||||
_hideProgressTimers.insert(item, timer);
|
||||
}
|
||||
timer->start(5000);
|
||||
break;
|
||||
case Progress::Invalid:
|
||||
case Progress::Download:
|
||||
case Progress::Upload:
|
||||
case Progress::Inactive:
|
||||
case Progress::SoftError:
|
||||
case Progress::NormalError:
|
||||
case Progress::FatalError:
|
||||
break;
|
||||
}
|
||||
|
||||
QString fileProgressString;
|
||||
QString s1 = Utility::octetsToString( prog1 );
|
||||
QString s2 = Utility::octetsToString( prog2 );
|
||||
|
||||
// switch on extra space.
|
||||
item->setData( QVariant(true), FolderStatusDelegate::AddProgressSpace );
|
||||
|
||||
if( progress.kind != Progress::EndSync ) {
|
||||
// Example text: "Currently uploading foobar.png (1MB of 2MB)"
|
||||
fileProgressString = tr("%1 %2 %3 (%4 of %5)").arg(syncFileProgressString).arg(kindString).
|
||||
arg(itemFileName).arg(s1).arg(s2);
|
||||
} else {
|
||||
fileProgressString = tr("Completely finished.");
|
||||
}
|
||||
item->setData( fileProgressString,FolderStatusDelegate::SyncProgressItemString);
|
||||
|
||||
// overall progress
|
||||
s1 = Utility::octetsToString( progress.overall_current_bytes );
|
||||
s2 = Utility::octetsToString( progress.overall_transmission_size );
|
||||
QString overallSyncString = tr("%1 of %2, file %3 of %4").arg(s1).arg(s2)
|
||||
.arg(progress.current_file_no).arg(progress.overall_file_count);
|
||||
item->setData( overallSyncString, FolderStatusDelegate::SyncProgressOverallString );
|
||||
|
||||
int overallPercent = 0;
|
||||
if( progress.overall_transmission_size > 0 ) {
|
||||
overallPercent = qRound(double(progress.overall_current_bytes)/double(progress.overall_transmission_size) * 100.0);
|
||||
}
|
||||
item->setData( overallPercent, FolderStatusDelegate::SyncProgressOverallPercent);
|
||||
}
|
||||
|
||||
void AccountSettings::slotHideProgress()
|
||||
{
|
||||
QTimer *send_timer = qobject_cast<QTimer*>(this->sender());
|
||||
QHash<QStandardItem*, QTimer*>::const_iterator i = _hideProgressTimers.constBegin();
|
||||
while (i != _hideProgressTimers.constEnd()) {
|
||||
if( i.value() == send_timer ) {
|
||||
QStandardItem *item = i.key();
|
||||
|
||||
/* 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;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
send_timer->deleteLater();
|
||||
}
|
||||
|
||||
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
||||
{
|
||||
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()
|
||||
{
|
||||
if (_ignoreEditor.isNull()) {
|
||||
_ignoreEditor = new IgnoreListEditor(this);
|
||||
_ignoreEditor->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_ignoreEditor->open();
|
||||
} else {
|
||||
Utility::raiseDialog(_ignoreEditor);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotAccountStateChanged(int state)
|
||||
{
|
||||
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()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
106
src/mirall/accountsettings.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 ACCOUNTSETTINGS_H
|
||||
#define ACCOUNTSETTINGS_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QUrl>
|
||||
#include <QPointer>
|
||||
#include <QHash>
|
||||
#include <QTimer>
|
||||
#include <QStandardItem>
|
||||
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
class QStandardItemModel;
|
||||
class QModelIndex;
|
||||
class QStandardItem;
|
||||
class QNetworkReply;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
namespace Ui {
|
||||
class AccountSettings;
|
||||
}
|
||||
|
||||
class FolderMan;
|
||||
class IgnoreListEditor;
|
||||
class Account;
|
||||
|
||||
class AccountSettings : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AccountSettings(QWidget *parent = 0);
|
||||
~AccountSettings();
|
||||
|
||||
void setFolderList( const Folder::Map& );
|
||||
|
||||
signals:
|
||||
void folderChanged();
|
||||
void openProtocol();
|
||||
void openFolderAlias( const QString& );
|
||||
void infoFolderAlias( const QString& );
|
||||
|
||||
public slots:
|
||||
void slotFolderActivated( const QModelIndex& );
|
||||
void slotOpenOC();
|
||||
void slotUpdateFolderState( Folder* );
|
||||
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 slotResetCurrentFolder();
|
||||
void slotFolderWizardAccepted();
|
||||
void slotFolderWizardRejected();
|
||||
void slotOpenAccountWizard();
|
||||
void slotHideProgress();
|
||||
|
||||
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<IgnoreListEditor> _ignoreEditor;
|
||||
QStandardItemModel *_model;
|
||||
QUrl _OCUrl;
|
||||
QHash<QStandardItem*, QTimer*> _hideProgressTimers;
|
||||
QString _kindContext;
|
||||
QStringList _generalErrors;
|
||||
bool _wasDisabledBefore;
|
||||
Account *_account;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // ACCOUNTSETTINGS_H
|
||||
146
src/mirall/accountsettings.ui
Normal file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Mirall::AccountSettings</class>
|
||||
<widget class="QWidget" name="Mirall::AccountSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>607</width>
|
||||
<height>382</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="1" column="1">
|
||||
<widget class="QGroupBox" name="maintenanceGroupBox">
|
||||
<property name="title">
|
||||
<string>Account Maintenance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QPushButton" name="ignoredFilesButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Edit Ignored Files</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="modifyAccountButton">
|
||||
<property name="text">
|
||||
<string>Modify Account</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="syncStatusGroupBox">
|
||||
<property name="title">
|
||||
<string>Account to Synchronize</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="connectLabel">
|
||||
<property name="text">
|
||||
<string>Connected with <server> as <user></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QListView" name="_folderList"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonAdd">
|
||||
<property name="text">
|
||||
<string>Add Folder...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonEnable">
|
||||
<property name="text">
|
||||
<string>Pause</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttonRemove">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="storageGroupBox">
|
||||
<property name="title">
|
||||
<string>Storage Usage</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QProgressBar" name="quotaProgressBar">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<property name="textVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="quotaLabel">
|
||||
<property name="text">
|
||||
<string>Retrieving usage information...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<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>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -16,34 +16,25 @@
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QNetworkReply>
|
||||
#include <QSslError>
|
||||
#include <QPointer>
|
||||
#include <QQueue>
|
||||
|
||||
#include "qtsingleapplication.h"
|
||||
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/logbrowser.h"
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/fileitemdialog.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 FolderWatcher;
|
||||
class FolderWizard;
|
||||
class StatusDialog;
|
||||
class OwncloudSetupWizard;
|
||||
class ownCloudInfo;
|
||||
class Folder;
|
||||
class SslErrorDialog;
|
||||
class UpdateDetector;
|
||||
class SocketApi;
|
||||
|
||||
class Application : public SharedTools::QtSingleApplication
|
||||
{
|
||||
@@ -55,94 +46,63 @@ public:
|
||||
bool giveHelp();
|
||||
void showHelp();
|
||||
|
||||
signals:
|
||||
|
||||
protected slots:
|
||||
void slotAddFolder();
|
||||
void slotOpenStatus();
|
||||
void slotRemoveFolder( const QString& );
|
||||
void slotEnableFolder( const QString&, const bool );
|
||||
void slotInfoFolder( const QString& );
|
||||
void slotConfigure();
|
||||
void slotConfigureProxy();
|
||||
void slotParseOptions( const QString& );
|
||||
void slotShowTrayMessage(const QString&, const QString&);
|
||||
|
||||
void slotSyncStateChange( const QString& );
|
||||
public slots:
|
||||
// TODO: this should not be public
|
||||
void slotownCloudWizardDone(int);
|
||||
protected:
|
||||
|
||||
protected:
|
||||
void parseOptions(const QStringList& );
|
||||
void setupTranslations();
|
||||
void setupActions();
|
||||
void setupSystemTray();
|
||||
void setupContextMenu();
|
||||
void setupLogBrowser();
|
||||
void setupProxy();
|
||||
void setupLogging();
|
||||
void enterNextLogFile();
|
||||
|
||||
//folders have to be disabled while making config changes
|
||||
void computeOverallSyncStatus();
|
||||
bool checkConfigExists(bool openSettings);
|
||||
|
||||
// reimplemented
|
||||
#if defined(Q_WS_WIN)
|
||||
bool winEventFilter( MSG * message, long * result );
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void folderRemoved();
|
||||
void folderStateChanged(Folder*);
|
||||
|
||||
protected slots:
|
||||
void slotTrayClicked( QSystemTrayIcon::ActivationReason );
|
||||
void slotFolderOpenAction(const QString & );
|
||||
void slotOpenOwnCloud();
|
||||
void slotStartFolderSetup(int result = QDialog::Accepted); // defaulting to Accepted
|
||||
void slotOwnCloudFound( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotNoOwnCloudFound( QNetworkReply* );
|
||||
void slotCheckAuthentication();
|
||||
void slotAuthCheck( const QString& ,QNetworkReply* );
|
||||
void slotOpenLogBrowser();
|
||||
void slotAbout();
|
||||
void slotSSLFailed( QNetworkReply *reply, QList<QSslError> errors );
|
||||
void slotFetchCredentials();
|
||||
void slotCredentialsFetched( bool );
|
||||
void slotParseOptions( const QString& );
|
||||
void slotCheckConnection();
|
||||
void slotConnectionValidatorResult(ConnectionValidator::Status);
|
||||
void slotStartUpdateDetector();
|
||||
void slotSetupProxy();
|
||||
void slotUseMonoIconsChanged( bool );
|
||||
void slotCredentialsFetched();
|
||||
void slotLogin();
|
||||
void slotLogout();
|
||||
void slotCleanup();
|
||||
void slotAccountChanged(Account *newAccount, Account *oldAccount);
|
||||
|
||||
private:
|
||||
void setHelp();
|
||||
void raiseDialog( QWidget* );
|
||||
void runValidator();
|
||||
|
||||
// configuration file -> folder
|
||||
Systray *_tray;
|
||||
QAction *_actionQuit;
|
||||
QAction *_actionAddFolder;
|
||||
QAction *_actionOpenStatus;
|
||||
QAction *_actionConfigure;
|
||||
QAction *_actionOpenoC;
|
||||
QAction *_actionConfigureProxy;
|
||||
QAction *_actionAbout;
|
||||
QPointer<ownCloudGui> _gui;
|
||||
QPointer<SocketApi> _socketApi;
|
||||
|
||||
#if QT_VERSION >= 0x040700
|
||||
QNetworkConfigurationManager *_networkMgr;
|
||||
#endif
|
||||
ConnectionValidator *_conValidator;
|
||||
|
||||
FolderWizard *_folderWizard;
|
||||
QScopedPointer<OwncloudSetupWizard> _owncloudSetupWizard;
|
||||
SslErrorDialog *_sslErrorDialog;
|
||||
|
||||
// tray's menu
|
||||
QMenu *_contextMenu;
|
||||
StatusDialog *_statusDialog;
|
||||
FileItemDialog *_fileItemDialog;
|
||||
|
||||
FolderMan *_folderMan;
|
||||
Theme *_theme;
|
||||
QSignalMapper *_folderOpenActionMapper;
|
||||
UpdateDetector *_updateDetector;
|
||||
LogBrowser *_logBrowser;
|
||||
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/credentialstore.h"
|
||||
#include "mirall/account.h"
|
||||
#include "mirall/networkjobs.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ConnectionValidator::ConnectionValidator(QObject *parent) :
|
||||
QObject(parent)
|
||||
ConnectionValidator::ConnectionValidator(Account *account, QObject *parent)
|
||||
: QObject(parent),
|
||||
_account(account),
|
||||
_networkError(QNetworkReply::NoError)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ConnectionValidator::ConnectionValidator(const QString& connection, QObject *parent)
|
||||
:_connection(connection)
|
||||
{
|
||||
ownCloudInfo::instance()->setCustomConfigHandle(_connection);
|
||||
}
|
||||
|
||||
QStringList ConnectionValidator::errors() const
|
||||
@@ -38,156 +34,138 @@ QStringList ConnectionValidator::errors() const
|
||||
return _errors;
|
||||
}
|
||||
|
||||
QString ConnectionValidator::statusString( Status )
|
||||
bool ConnectionValidator::networkError() const
|
||||
{
|
||||
return QLatin1String("Get your street creds!");
|
||||
return _networkError;
|
||||
}
|
||||
|
||||
QString ConnectionValidator::statusString( Status stat ) const
|
||||
{
|
||||
QString re;
|
||||
|
||||
switch( stat ) {
|
||||
case Undefined:
|
||||
re = QLatin1String("Undefined");
|
||||
break;
|
||||
case Connected:
|
||||
re = QLatin1String("Connected");
|
||||
break;
|
||||
case NotConfigured:
|
||||
re = QLatin1String("NotConfigured");
|
||||
break;
|
||||
case ServerVersionMismatch:
|
||||
re = QLatin1String("Server Version Mismatch");
|
||||
break;
|
||||
case CredentialsTooManyAttempts:
|
||||
re = QLatin1String("Credentials Too Many Attempts");
|
||||
break;
|
||||
case CredentialError:
|
||||
re = QLatin1String("CredentialError");
|
||||
break;
|
||||
case CredentialsUserCanceled:
|
||||
re = QLatin1String("Credential User Canceled");
|
||||
break;
|
||||
case CredentialsWrong:
|
||||
re = QLatin1String("Credentials Wrong");
|
||||
break;
|
||||
case StatusNotFound:
|
||||
re = QLatin1String("Status not found");
|
||||
break;
|
||||
default:
|
||||
re = QLatin1String("status undeclared.");
|
||||
}
|
||||
return re;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QTimer::singleShot( 0, this, SLOT( slotFetchCredentials() ));
|
||||
QTimer::singleShot( 0, this, SLOT( slotCheckAuthentication() ));
|
||||
}
|
||||
|
||||
// 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::slotFetchCredentials()
|
||||
{
|
||||
if( _connection.isEmpty() ) {
|
||||
if( CredentialStore::instance()->canTryAgain() ) {
|
||||
connect( CredentialStore::instance(), SIGNAL(fetchCredentialsFinished(bool)),
|
||||
this, SLOT(slotCredentialsFetched(bool)) );
|
||||
CredentialStore::instance()->fetchCredentials();
|
||||
}
|
||||
|
||||
if( CredentialStore::instance()->state() == CredentialStore::TooManyAttempts ) {
|
||||
_errors << tr("Too many attempts to get a valid password.");
|
||||
emit connectionResult( CredentialsTooManyAttempts );
|
||||
}
|
||||
} else {
|
||||
// Pull credentials from Mirall config.
|
||||
slotCredentialsFetched( true );
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotCredentialsFetched( bool ok )
|
||||
{
|
||||
qDebug() << "Credentials successfully fetched: " << ok;
|
||||
disconnect( CredentialStore::instance(), SIGNAL(fetchCredentialsFinished(bool)) );
|
||||
|
||||
if( ! ok ) {
|
||||
Status stat;
|
||||
_errors << tr("Error: Could not retrieve the password!");
|
||||
|
||||
if( CredentialStore::instance()->state() == CredentialStore::UserCanceled ) {
|
||||
_errors << tr("Password dialog was canceled!");
|
||||
stat = CredentialsUserCanceled;
|
||||
} else {
|
||||
_errors << CredentialStore::instance()->errorMessage();
|
||||
stat = CredentialError;
|
||||
}
|
||||
|
||||
qDebug() << "Could not fetch credentials" << _errors;
|
||||
|
||||
emit connectionResult( stat );
|
||||
} else {
|
||||
QString user, pwd;
|
||||
if( _connection.isEmpty() ) {
|
||||
user = CredentialStore::instance()->user();
|
||||
pwd = CredentialStore::instance()->password();
|
||||
} else {
|
||||
// in case of reconfiguration, the _connection is set.
|
||||
MirallConfigFile cfg(_connection);
|
||||
user = cfg.ownCloudUser();
|
||||
pwd = cfg.ownCloudPasswd();
|
||||
}
|
||||
ownCloudInfo::instance()->setCredentials( user, pwd );
|
||||
|
||||
// Credential fetched ok.
|
||||
QTimer::singleShot( 0, this, SLOT( slotCheckAuthentication() ));
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotCheckAuthentication()
|
||||
{
|
||||
connect( ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this, SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
ownCloudInfo::instance()->getRequest(QLatin1String("/"), true ); // 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)
|
||||
{
|
||||
bool ok = true;
|
||||
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;
|
||||
ok = false;
|
||||
switch (_account->state()) {
|
||||
case Account::SignedOut:
|
||||
_account->setState(Account::SignedOut);
|
||||
break;
|
||||
default:
|
||||
_account->setState(Account::Disconnected);
|
||||
}
|
||||
|
||||
} else if( reply->error() != QNetworkReply::NoError ) {
|
||||
_errors << reply->errorString();
|
||||
}
|
||||
|
||||
// disconnect from ownCloud Info signals
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
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,30 +43,31 @@ public:
|
||||
};
|
||||
|
||||
QStringList errors() const;
|
||||
bool networkError() const;
|
||||
|
||||
void checkConnection();
|
||||
|
||||
QString statusString( Status );
|
||||
QString statusString( Status ) const;
|
||||
|
||||
signals:
|
||||
void connectionResult( ConnectionValidator::Status );
|
||||
void connectionAvailable();
|
||||
void connectionFailed();
|
||||
// void connectionAvailable();
|
||||
// void connectionFailed();
|
||||
|
||||
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 slotFetchCredentials();
|
||||
void slotCredentialsFetched( bool );
|
||||
void slotCheckAuthentication();
|
||||
void slotAuthCheck( const QString& ,QNetworkReply * );
|
||||
void slotAuthFailed(QNetworkReply *reply);
|
||||
void slotAuthSuccess();
|
||||
|
||||
private:
|
||||
QStringList _errors;
|
||||
QString _connection;
|
||||
Account *_account;
|
||||
bool _networkError;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,397 +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 "mirall/credentialstore.h"
|
||||
#include "mirall/mirallconfigfile.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;
|
||||
int CredentialStore::_tries = 0;
|
||||
#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;
|
||||
}
|
||||
|
||||
bool CredentialStore::canTryAgain()
|
||||
{
|
||||
if( _tries > MAX_LOGIN_ATTEMPTS ) {
|
||||
qDebug() << "canTryAgain: Max attempts reached.";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Since QtKeyChain is required now, it makes sense to only query once. */
|
||||
if( _state == NotFetched || _state == AsyncWriting ) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CredentialStore::fetchCredentials()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
if( ++_tries > MAX_LOGIN_ATTEMPTS ) {
|
||||
qDebug() << "Too many attempts to enter password!";
|
||||
_state = TooManyAttempts;
|
||||
emit( fetchCredentialsFinished(false) );
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
QString pwd;
|
||||
_user = cfgFile.ownCloudUser();
|
||||
_url = cfgFile.ownCloudUrl();
|
||||
if( !cfgFile.passwordStorageAllowed() ) {
|
||||
_type = CredentialStore::User;
|
||||
}
|
||||
|
||||
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::User: {
|
||||
/* Ask the user for the password */
|
||||
/* Fixme: Move user interaction out here. */
|
||||
_state = AsyncFetching;
|
||||
_inputDialog = new QInputDialog;
|
||||
_inputDialog->setWindowTitle(QApplication::translate("MirallConfigFile","Password Required") );
|
||||
_inputDialog->setLabelText( QApplication::translate("MirallConfigFile","Please enter your %1 password:")
|
||||
.arg(Theme::instance()->appNameGUI()));
|
||||
_inputDialog->setInputMode( QInputDialog::TextInput );
|
||||
_inputDialog->setTextEchoMode( QLineEdit::Password );
|
||||
|
||||
connect(_inputDialog, SIGNAL(finished(int)), SLOT(slotUserDialogDone(int)));
|
||||
_inputDialog->open();
|
||||
break;
|
||||
}
|
||||
case CredentialStore::Settings: {
|
||||
/* Read from config file. */
|
||||
_state = Fetching;
|
||||
pwd = cfgFile.ownCloudPasswd();
|
||||
ok = true;
|
||||
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::slotUserDialogDone( int result )
|
||||
{
|
||||
if( result == QDialog::Accepted ) {
|
||||
_passwd = _inputDialog->textValue();
|
||||
_state = Ok;
|
||||
} else {
|
||||
_state = UserCanceled;
|
||||
_passwd = QString::null;
|
||||
}
|
||||
_inputDialog->deleteLater();
|
||||
emit(fetchCredentialsFinished(_state == Ok));
|
||||
}
|
||||
|
||||
void CredentialStore::reset()
|
||||
{
|
||||
_state = NotFetched;
|
||||
_user = QString::null;
|
||||
_passwd = QString::null;
|
||||
_tries = 0;
|
||||
}
|
||||
|
||||
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::AccessDeniedByUser:
|
||||
_state = AccessDeniedByUser;
|
||||
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, bool allowToStore )
|
||||
{
|
||||
_passwd = pwd;
|
||||
_user = user;
|
||||
if( allowToStore ) {
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
_type = KeyChain;
|
||||
#else
|
||||
_type = Settings;
|
||||
#endif
|
||||
} else {
|
||||
_type = User;
|
||||
}
|
||||
_url = url;
|
||||
_state = Ok;
|
||||
}
|
||||
|
||||
void CredentialStore::saveCredentials( )
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
QString key = keyChainKey(_url);
|
||||
if( key.isNull() ) {
|
||||
qDebug() << "Error: Can not save credentials, URL is zero!";
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
WritePasswordJob *job = NULL;
|
||||
#endif
|
||||
|
||||
switch( _type ) {
|
||||
case CredentialStore::User:
|
||||
deleteKeyChainCredential( key );
|
||||
break;
|
||||
case CredentialStore::KeyChain:
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
// Set password in KeyChain
|
||||
job = new WritePasswordJob(Theme::instance()->appName());
|
||||
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.writePassword( _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.
|
||||
MirallConfigFile cfgFile;
|
||||
cfgFile.clearPasswordFromConfig();
|
||||
_state = NotFetched;
|
||||
_tries = 0;
|
||||
}
|
||||
} 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,147 +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!!
|
||||
* UserCanceled: The fetching involved user interaction and the user canceled
|
||||
* the operation. No valid credentials are there.
|
||||
* TooManyAttempts: The user tried to often to enter a password.
|
||||
* Fetching: The fetching is not yet finished.
|
||||
* 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,
|
||||
UserCanceled,
|
||||
Fetching,
|
||||
AsyncFetching,
|
||||
EntryNotFound,
|
||||
AccessDeniedByUser,
|
||||
AccessDenied,
|
||||
NoKeychainBackend,
|
||||
Error,
|
||||
AsyncWriting,
|
||||
TooManyAttempts };
|
||||
|
||||
enum CredentialType {
|
||||
User = 0,
|
||||
Settings,
|
||||
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
|
||||
* @param password - the password.
|
||||
*/
|
||||
void setCredentials( const QString&, const QString&, const QString&, bool );
|
||||
|
||||
void saveCredentials( );
|
||||
|
||||
QString errorMessage();
|
||||
|
||||
/**
|
||||
* @brief canTryAgain - check if another try to get credentials makes sense.
|
||||
*/
|
||||
bool canTryAgain();
|
||||
|
||||
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* );
|
||||
void slotUserDialogDone(int);
|
||||
|
||||
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 int _tries;
|
||||
static CredentialType _type;
|
||||
QInputDialog *_inputDialog;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CREDENTIALSTORE_H
|
||||
@@ -14,10 +14,14 @@
|
||||
*/
|
||||
|
||||
#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
|
||||
#include <windows.h>
|
||||
@@ -41,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()
|
||||
@@ -60,110 +76,163 @@ 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:
|
||||
case CSYNC_STATUS_PERMISSION_DENIED:
|
||||
errStr = tr("CSync failed due to not handled permission deniend.");
|
||||
break;
|
||||
case CSYNC_ERR_NOT_FOUND:
|
||||
errStr = tr("CSync failed to find a specific file.");
|
||||
case CSYNC_STATUS_NOT_FOUND:
|
||||
errStr = tr("CSync failed to access "); // filename gets added.
|
||||
break;
|
||||
case CSYNC_ERR_EXISTS:
|
||||
case CSYNC_STATUS_FILE_EXISTS:
|
||||
errStr = tr("CSync tried to create a directory that already exists.");
|
||||
break;
|
||||
case CSYNC_ERR_NOSPC:
|
||||
case CSYNC_STATUS_OUT_OF_SPACE:
|
||||
errStr = tr("CSync: No space on %1 server available.").arg(Theme::instance()->appNameGUI());
|
||||
break;
|
||||
case CSYNC_ERR_UNSPEC:
|
||||
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;
|
||||
|
||||
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() ) {
|
||||
if( entry._retryCount <= 0 ) {
|
||||
re = true;
|
||||
item->_blacklistedInDb = true;
|
||||
}
|
||||
|
||||
// if the retryCount is 0, but the etag has changed, it is tried again
|
||||
// note that if the retryCount is -1 we never try again.
|
||||
if( entry._retryCount == 0 ) {
|
||||
if( item->_etag.isEmpty() || entry._lastTryEtag.isEmpty() ) {
|
||||
// compare the mtimes.
|
||||
if(entry._lastTryModtime != item->_modtime) {
|
||||
re = false;
|
||||
qDebug() << item->_file << " is blacklisted, but has changed mtime!";
|
||||
|
||||
}
|
||||
} else {
|
||||
if( entry._lastTryEtag != item->_etag) {
|
||||
re = false;
|
||||
qDebug() << item->_file << " is blacklisted, but has changed etag!";
|
||||
}
|
||||
}
|
||||
}
|
||||
if( re ) {
|
||||
qDebug() << "Item is on blacklist: " << entry._file << "retries:" << entry._retryCount;
|
||||
item->_blacklistedInDb = true;
|
||||
item->_instruction = CSYNC_INSTRUCTION_IGNORE;
|
||||
item->_errorString = tr("The item is not synced because it is on the blacklist.");
|
||||
slotProgress( Progress::SoftError, *item );
|
||||
}
|
||||
}
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkLocal( TREE_WALK_FILE* file, void *data )
|
||||
{
|
||||
return static_cast<CSyncThread*>(data)->treewalkFile( file, false );
|
||||
@@ -174,23 +243,37 @@ 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);
|
||||
|
||||
// record the seen files to be able to clean the journal later
|
||||
_seenFiles[item._file] = QString();
|
||||
|
||||
if(file->error_string) {
|
||||
item._errorString = QString::fromUtf8(file->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;
|
||||
|
||||
SyncFileItem::Direction dir;
|
||||
|
||||
int re = 0;
|
||||
|
||||
// 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;
|
||||
@@ -198,27 +281,47 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
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:
|
||||
if (!_needsUpdate)
|
||||
_needsUpdate = true;
|
||||
_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:
|
||||
// No need to do anything.
|
||||
return re;
|
||||
if (item._isDirectory && remote) {
|
||||
// Because we want still to update etags of directories
|
||||
dir = SyncFileItem::None;
|
||||
} else {
|
||||
// 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 );
|
||||
if (item._isDirectory)
|
||||
_renamedFolders.insert(item._file, item._renameTarget);
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
dir = !remote ? SyncFileItem::Down : SyncFileItem::Up;
|
||||
break;
|
||||
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:
|
||||
@@ -226,71 +329,60 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
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;
|
||||
}
|
||||
|
||||
switch( file->type ) {
|
||||
case CSYNC_FTW_TYPE_DIR:
|
||||
item._type = SyncFileItem::Directory;
|
||||
break;
|
||||
case CSYNC_FTW_TYPE_FILE:
|
||||
item._type = SyncFileItem::File;
|
||||
break;
|
||||
case CSYNC_FTW_TYPE_SLINK:
|
||||
item._type = SyncFileItem::SoftLink;
|
||||
break;
|
||||
default:
|
||||
item._type = SyncFileItem::UnknownType;
|
||||
}
|
||||
|
||||
item._dir = dir;
|
||||
_mutex.lock();
|
||||
_syncedItems.append(item);
|
||||
_mutex.unlock();
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkError(TREE_WALK_FILE* file)
|
||||
{
|
||||
SyncFileItem item;
|
||||
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;
|
||||
_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()
|
||||
@@ -305,36 +397,105 @@ 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;
|
||||
|
||||
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();
|
||||
|
||||
if( fileRecordCount == -1 ) {
|
||||
qDebug() << "No way to create a sync journal!";
|
||||
emit csyncError(tr("Unable to initialize a sync journal."));
|
||||
|
||||
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.
|
||||
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.
|
||||
// QString cookiesAsString;
|
||||
// foreach(QNetworkCookie c, _lastAuthCookies) {
|
||||
// cookiesAsString += c.name();
|
||||
// cookiesAsString += '=';
|
||||
// cookiesAsString += c.value();
|
||||
// cookiesAsString += "; ";
|
||||
// }
|
||||
// csync_set_module_property(_csync_ctx, "session_key", cookiesAsString.to
|
||||
// }
|
||||
|
||||
// csync_set_auth_callback( _csync_ctx, getauth );
|
||||
csync_set_progress_callback( _csync_ctx, progress );
|
||||
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, "cysnc_reconcile");
|
||||
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;
|
||||
@@ -343,9 +504,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";
|
||||
@@ -356,33 +523,152 @@ 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.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;
|
||||
}
|
||||
|
||||
void CSyncThread::progress(const char *remote_url, enum csync_notify_type_e kind,
|
||||
long long o1, long long o2, void *userdata)
|
||||
void CSyncThread::abort()
|
||||
{
|
||||
(void) o1; (void) o2;
|
||||
if (kind == CSYNC_NOTIFY_FINISHED_DOWNLOAD) {
|
||||
QString path = QUrl::fromEncoded(remote_url).toString();
|
||||
CSyncThread *thread = static_cast<CSyncThread*>(userdata);
|
||||
thread->fileReceived(path);
|
||||
}
|
||||
QMutexLocker locker(&_mutex);
|
||||
csync_request_abort(_csync_ctx);
|
||||
_abortRequested = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} // ns Mirall
|
||||
|
||||
@@ -21,35 +21,55 @@
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
#include <qelapsedtimer.h>
|
||||
#include <qstack.h>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookie>
|
||||
|
||||
#include <csync.h>
|
||||
|
||||
#include "mirall/syncfileitem.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
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();
|
||||
|
||||
@@ -58,21 +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 progress(const char *remote_url,
|
||||
enum csync_notify_type_e kind,
|
||||
long long o1, long long o2,
|
||||
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* );
|
||||
|
||||
static int walkFinalize(TREE_WALK_FILE*, void* );
|
||||
|
||||
|
||||
bool checkBlacklisting( SyncFileItem *item );
|
||||
|
||||
static QMutex _mutex;
|
||||
static QMutex _syncMutex;
|
||||
@@ -80,10 +98,30 @@ 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;
|
||||
|
||||
friend class CSyncRunScopeHelper;
|
||||
QAtomicInt _abortRequested;
|
||||
|
||||
friend struct CSyncRunScopeHelper;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,304 +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 "mirall/fileitemdialog.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logger.h"
|
||||
|
||||
#define TYPE_SUCCESS 1
|
||||
#define TYPE_CONFLICT 2
|
||||
#define TYPE_NEW 3
|
||||
#define TYPE_DELETED 4
|
||||
#define TYPE_ERROR 5
|
||||
#define TYPE_RENAME 6
|
||||
#define TYPE_IGNORE 7
|
||||
|
||||
#define FILE_TYPE 100
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
FileItemDialog::FileItemDialog(Theme *theme, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
_theme(theme)
|
||||
{
|
||||
setupUi(this);
|
||||
connect(_dialogButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()),
|
||||
this, SLOT(accept()));
|
||||
|
||||
QStringList header;
|
||||
header << tr("Files");
|
||||
QString firstColString = tr("File Count");
|
||||
header << firstColString;
|
||||
_treeWidget->setHeaderLabels( header );
|
||||
|
||||
_treeWidget->setColumnWidth(0, 480);
|
||||
_timer.setInterval(1000);
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(slotSetFolderMessage()));
|
||||
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
|
||||
|
||||
QPushButton *copyBtn = _dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
|
||||
connect(copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
|
||||
|
||||
setWindowTitle(tr("Sync Protocol"));
|
||||
|
||||
}
|
||||
|
||||
void FileItemDialog::setSyncResult( const SyncResult& result )
|
||||
{
|
||||
QString folderMessage;
|
||||
|
||||
SyncResult::Status syncStatus = result.status();
|
||||
switch( syncStatus ) {
|
||||
case SyncResult::Undefined:
|
||||
folderMessage = tr( "Undefined Folder State" );
|
||||
break;
|
||||
case SyncResult::NotYetStarted:
|
||||
folderMessage = tr( "The folder waits to start syncing." );
|
||||
break;
|
||||
case SyncResult::SyncPrepare:
|
||||
folderMessage = tr( "Determining which files to sync." );
|
||||
break;
|
||||
case SyncResult::Unavailable:
|
||||
folderMessage = tr( "Server is currently not available." );
|
||||
break;
|
||||
case SyncResult::SyncRunning:
|
||||
folderMessage = tr("Sync is running.");
|
||||
break;
|
||||
case SyncResult::Success:
|
||||
folderMessage = tr("Last Sync was successful.");
|
||||
break;
|
||||
case SyncResult::Error:
|
||||
folderMessage = tr( "Syncing Error." );
|
||||
break;
|
||||
case SyncResult::SetupError:
|
||||
folderMessage = tr( "Setup Error." );
|
||||
break;
|
||||
default:
|
||||
folderMessage = tr( "Undefined Error State." );
|
||||
}
|
||||
|
||||
_folderMessage = folderMessage;
|
||||
_lastSyncTime = result.syncTime();
|
||||
|
||||
if( result.errorStrings().count() ) {
|
||||
_errorLabel->setVisible(true);
|
||||
_errorLabel->setTextFormat(Qt::RichText);
|
||||
QString errStr;
|
||||
foreach( QString err, result.errorStrings() ) {
|
||||
errStr.append(QString("<p>%1</p>").arg(err));
|
||||
}
|
||||
|
||||
_errorLabel->setText(errStr);
|
||||
} else {
|
||||
_errorLabel->setText(QString::null);
|
||||
_errorLabel->setVisible(false);
|
||||
}
|
||||
|
||||
slotSetFolderMessage();
|
||||
if( syncStatus == SyncResult::SyncRunning ) {
|
||||
_timer.stop();
|
||||
} else {
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
setSyncFileItems( result.syncFileItemVector() );
|
||||
|
||||
}
|
||||
|
||||
void FileItemDialog::slotSetFolderMessage()
|
||||
{
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
int secs = _lastSyncTime.secsTo(now);
|
||||
|
||||
if (secs < 60)
|
||||
_timelabel->setText(tr("%1 (last finished %n sec. ago)", "", secs).arg(_folderMessage));
|
||||
else
|
||||
_timelabel->setText(tr("%1 (last finished %n min. ago)", "", secs/60).arg(_folderMessage));
|
||||
}
|
||||
|
||||
void FileItemDialog::copyToClipboard()
|
||||
{
|
||||
QString text;
|
||||
QTextStream ts(&text);
|
||||
|
||||
int topLevelItems = _treeWidget->topLevelItemCount();
|
||||
for (int i = 0; i < topLevelItems; i++) {
|
||||
QTreeWidgetItem *item = _treeWidget->topLevelItem(i);
|
||||
ts << left << qSetFieldWidth(50)
|
||||
<< item->data(0, Qt::DisplayRole).toString()
|
||||
<< right << qSetFieldWidth(6)
|
||||
<< item->data(1, Qt::DisplayRole).toString()
|
||||
<< endl;
|
||||
int childItems = item->childCount();
|
||||
for (int j = 0; j < childItems; j++) {
|
||||
QTreeWidgetItem *child =item->child(j);
|
||||
ts << left << qSetFieldWidth(0) << QLatin1String(" ")
|
||||
<< child->data(0,Qt::DisplayRole).toString()
|
||||
<< QString::fromLatin1(" (%1)").arg(
|
||||
child->data(1, Qt::DisplayRole).toString()
|
||||
)
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
|
||||
QApplication::clipboard()->setText(text);
|
||||
emit guiLog(tr("Copied to clipboard"), tr("The sync protocol has been copied to the clipboard."));
|
||||
}
|
||||
|
||||
void FileItemDialog::accept()
|
||||
{
|
||||
_timer.stop();
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void FileItemDialog::setSyncFileItems( const SyncFileItemVector& list )
|
||||
{
|
||||
_treeWidget->clear();
|
||||
QStringList strings;
|
||||
QFont headerFont;
|
||||
headerFont.setWeight(QFont::Bold);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("Synced Files"));
|
||||
_syncedFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_SUCCESS );
|
||||
_syncedFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_syncedFileItem);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("New Files"));
|
||||
_newFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_NEW );
|
||||
_newFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_newFileItem);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("Deleted Files"));
|
||||
_deletedFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_DELETED );
|
||||
_deletedFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_deletedFileItem);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("Renamed Files"));
|
||||
_renamedFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_RENAME);
|
||||
_renamedFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_renamedFileItem);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("Ignored Files"));
|
||||
_ignoredFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_IGNORE);
|
||||
_ignoredFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_renamedFileItem);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("Errors"));
|
||||
_errorFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_ERROR );
|
||||
_errorFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_errorFileItem);
|
||||
|
||||
strings.clear();
|
||||
strings.append(tr("Conflicts"));
|
||||
_conflictFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_CONFLICT);
|
||||
_conflictFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
|
||||
_treeWidget->addTopLevelItem(_conflictFileItem);
|
||||
|
||||
QList<QTreeWidgetItem*> syncedItems;
|
||||
QList<QTreeWidgetItem*> renamedItems;
|
||||
QList<QTreeWidgetItem*> newItems;
|
||||
QList<QTreeWidgetItem*> deletedItems;
|
||||
QList<QTreeWidgetItem*> ignoredItems;
|
||||
QList<QTreeWidgetItem*> conflictItems;
|
||||
QList<QTreeWidgetItem*> errorItems;
|
||||
|
||||
quint64 overall_files = 0;
|
||||
|
||||
foreach( SyncFileItem item, list ) {
|
||||
overall_files++;
|
||||
|
||||
QString dir;
|
||||
QStringList str( item._file );
|
||||
if( item._dir == SyncFileItem::Up ) dir = tr("Uploaded");
|
||||
if( item._dir == SyncFileItem::Down ) dir = tr("Downloaded");
|
||||
str << dir;
|
||||
|
||||
switch( item._instruction ) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
// do nothing.
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_EVAL:
|
||||
// should not happen
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
case CSYNC_INSTRUCTION_DELETED:
|
||||
deletedItems.append( new QTreeWidgetItem(_deletedFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_RENAME:
|
||||
renamedItems.append( new QTreeWidgetItem(_renamedFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
newItems.append( new QTreeWidgetItem(_newFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_CONFLICT:
|
||||
conflictItems.append( new QTreeWidgetItem(_conflictFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
ignoredItems.append( new QTreeWidgetItem(_ignoredFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_SYNC:
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
syncedItems.append( new QTreeWidgetItem(_syncedFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_STAT_ERROR:
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
errorItems.append( new QTreeWidgetItem(_errorFileItem, str, FILE_TYPE) );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
formatHeaderItem( _syncedFileItem, syncedItems );
|
||||
formatHeaderItem( _newFileItem, newItems );
|
||||
formatHeaderItem( _deletedFileItem, deletedItems );
|
||||
formatHeaderItem( _renamedFileItem, renamedItems );
|
||||
formatHeaderItem( _errorFileItem, errorItems );
|
||||
formatHeaderItem( _conflictFileItem, conflictItems );
|
||||
formatHeaderItem( _ignoredFileItem, ignoredItems );
|
||||
|
||||
}
|
||||
|
||||
void FileItemDialog::formatHeaderItem( QTreeWidgetItem *header, const QList<QTreeWidgetItem*>& list )
|
||||
{
|
||||
if( !header ) return;
|
||||
|
||||
header->addChildren( list );
|
||||
int count = list.count();
|
||||
#if LEAVE_THAT_TO_DESIGNERS
|
||||
QColor col("#adc5d3");
|
||||
header->setBackgroundColor(0, col);
|
||||
header->setBackgroundColor(1, col);
|
||||
#endif
|
||||
header->setText(1, QString::number( count ));
|
||||
if( count ) {
|
||||
QFont font;
|
||||
font.setWeight( QFont::Bold );
|
||||
header->setFont(0, font);
|
||||
header->setFont(1, font);
|
||||
header->setExpanded(true);
|
||||
} else {
|
||||
header->setExpanded(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,68 +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 FILEITEMDIALOG_H
|
||||
#define FILEITEMDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include "mirall/syncfileitem.h"
|
||||
|
||||
#include "ui_fileitemdialog.h"
|
||||
|
||||
namespace Mirall {
|
||||
class Theme;
|
||||
class SyncResult;
|
||||
|
||||
|
||||
class FileItemDialog : public QDialog, public Ui::_fileItemDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FileItemDialog(Theme*, QWidget *parent = 0);
|
||||
void setSyncResult( const SyncResult& );
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
|
||||
protected slots:
|
||||
void slotSetFolderMessage();
|
||||
void copyToClipboard();
|
||||
|
||||
signals:
|
||||
void guiLog(const QString&, const QString&);
|
||||
|
||||
private:
|
||||
void setSyncFileItems( const SyncFileItemVector& list );
|
||||
void formatHeaderItem( QTreeWidgetItem *, const QList<QTreeWidgetItem*>& );
|
||||
|
||||
QTreeWidgetItem *_newFileItem;
|
||||
QTreeWidgetItem *_syncedFileItem;
|
||||
QTreeWidgetItem *_deletedFileItem;
|
||||
QTreeWidgetItem *_renamedFileItem;
|
||||
QTreeWidgetItem *_errorFileItem;
|
||||
QTreeWidgetItem *_conflictFileItem;
|
||||
QTreeWidgetItem *_ignoredFileItem;
|
||||
|
||||
Theme *_theme;
|
||||
QString _folderMessage;
|
||||
QDateTime _lastSyncTime;
|
||||
QTimer _timer;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // FILEITEMDIALOG_H
|
||||
@@ -1,87 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>_fileItemDialog</class>
|
||||
<widget class="QWidget" name="_fileItemDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>612</width>
|
||||
<height>543</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Detailed Sync Protocol</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeWidget" name="_treeWidget">
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="rootIsDecorated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">1</string>
|
||||
</property>
|
||||
</column>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string notr="true">2</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_timelabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="_errorLabel">
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="_dialogButtonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Close</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -25,7 +25,7 @@ class FileUtils
|
||||
public:
|
||||
enum SubFolderListOption {
|
||||
SubFolderNoOptions = 0x0,
|
||||
SubFolderRecursive = 0x1,
|
||||
SubFolderRecursive = 0x1
|
||||
};
|
||||
Q_DECLARE_FLAGS(SubFolderListOptions, SubFolderListOption)
|
||||
|
||||
|
||||