Compare commits
529 Commits
v1.2.4
...
v1.4.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24251bc223 | ||
|
|
f78749d2cd | ||
|
|
0995377d39 | ||
|
|
b9228e64ff | ||
|
|
c4084de716 | ||
|
|
3c667918e7 | ||
|
|
e55745cbcf | ||
|
|
e01ce20431 | ||
|
|
dce8cb83d9 | ||
|
|
8dc956c55b | ||
|
|
6c77921a32 | ||
|
|
55e4748f06 | ||
|
|
5ff9e02517 | ||
|
|
7f7ebc36f5 | ||
|
|
0e5bfc03ce | ||
|
|
b309d333a6 | ||
|
|
ee439382ed | ||
|
|
192212c682 | ||
|
|
148bdfdcd6 | ||
|
|
066c0ba189 | ||
|
|
9651f1cddf | ||
|
|
5feb9b0806 | ||
|
|
44b9ee19e7 | ||
|
|
cc16d19bc4 | ||
|
|
b96c2de2b7 | ||
|
|
f882b80708 | ||
|
|
4d7c014b23 | ||
|
|
1f274699e5 | ||
|
|
697e355f07 | ||
|
|
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 | ||
|
|
b206a3b8e2 | ||
|
|
3bff5a061b | ||
|
|
0bc9b6f44e | ||
|
|
905f70a186 | ||
|
|
a8707b681d | ||
|
|
5d8f9f5346 | ||
|
|
e43ff398cd | ||
|
|
a441b1d562 | ||
|
|
6e2042cd55 | ||
|
|
bb8b58dc66 | ||
|
|
9cd099056b | ||
|
|
0adbc032ae | ||
|
|
22a679fb8c | ||
|
|
35a67fab0a | ||
|
|
fdc8117211 | ||
|
|
24208e6137 | ||
|
|
c0cd255ea3 | ||
|
|
a0375fd000 | ||
|
|
6d847cd5f9 | ||
|
|
6470a3f539 | ||
|
|
c6111d09ce | ||
|
|
d6012854a9 | ||
|
|
46c7026726 | ||
|
|
01ad3c4d81 | ||
|
|
4ac98bde73 | ||
|
|
f42a6d6ef6 | ||
|
|
9055c6ade7 | ||
|
|
1356a5bbaa | ||
|
|
3c320c2736 | ||
|
|
969757199e | ||
|
|
60f1c65a48 | ||
|
|
b87b0e16e6 | ||
|
|
8ed0b1be55 | ||
|
|
91b5f1076f | ||
|
|
8ec2457965 | ||
|
|
82d79b1188 | ||
|
|
e33601becd | ||
|
|
334443adbb | ||
|
|
99579e8a2a | ||
|
|
89438f7ace | ||
|
|
d323ec5dd9 | ||
|
|
bb5cf37330 | ||
|
|
4b0bdd648c | ||
|
|
5588fbe695 | ||
|
|
12ea381205 | ||
|
|
99fbf25fb2 | ||
|
|
b37645e14d | ||
|
|
1ec5a1aaa2 | ||
|
|
3eb7acde25 | ||
|
|
e53e39cfad | ||
|
|
1a17f40233 | ||
|
|
10094a997a | ||
|
|
2af38b093f | ||
|
|
b03c168175 | ||
|
|
1c6bc84d2d | ||
|
|
541239c17b | ||
|
|
74b4ade15a | ||
|
|
205502fd3b | ||
|
|
54e4217216 | ||
|
|
d2579a7754 | ||
|
|
76580840dd | ||
|
|
779e59156c | ||
|
|
b0f0d0b1cd | ||
|
|
858dcb53bd | ||
|
|
9d7db88fcb | ||
|
|
2099b7c6a0 | ||
|
|
4442564ad2 | ||
|
|
12148b5c9b | ||
|
|
d7d77a49fc | ||
|
|
b70c2f5c20 | ||
|
|
033249423f | ||
|
|
0c959e8661 | ||
|
|
79785241ea | ||
|
|
0090862313 | ||
|
|
a4a68c6622 | ||
|
|
49b4c341ae | ||
|
|
7c1f91abdd | ||
|
|
1f2ba7e254 | ||
|
|
8014bcb7c4 | ||
|
|
b1c8bf5954 | ||
|
|
0eb6740bac | ||
|
|
96531b548a | ||
|
|
f3371360ed | ||
|
|
c1b92f9fed | ||
|
|
b028ca0738 | ||
|
|
a9aab30d55 | ||
|
|
efbad3a3c8 | ||
|
|
b6ed959f98 | ||
|
|
477953f965 | ||
|
|
aaf59cece5 | ||
|
|
e88b7e8ec2 | ||
|
|
078b09b5f4 | ||
|
|
0d06fe764a | ||
|
|
94f8c39a9e | ||
|
|
7d1bd9da86 | ||
|
|
ed8cdf6910 | ||
|
|
7ee83847c8 | ||
|
|
52eca1362f | ||
|
|
eec1af09c4 | ||
|
|
c651c8cbf9 | ||
|
|
da6a398624 | ||
|
|
1418512a9f | ||
|
|
dd2859880b | ||
|
|
f38d8ba2e4 | ||
|
|
fdd52a2520 | ||
|
|
e2c0792043 | ||
|
|
e8889530b0 | ||
|
|
23f67d950a | ||
|
|
0bd1f507ab | ||
|
|
2bcfc143f6 | ||
|
|
9dc21bf8a3 | ||
|
|
70f627ba06 | ||
|
|
0683c26d41 | ||
|
|
dbc085d4d4 | ||
|
|
804eaa700e | ||
|
|
646922665d | ||
|
|
62ad2f19e3 | ||
|
|
14f55420f9 | ||
|
|
d4ea0ba6ba | ||
|
|
c06f7b7ecb | ||
|
|
3a50eece9e | ||
|
|
44f0fb0242 | ||
|
|
c210e2b72f | ||
|
|
7b5079f043 | ||
|
|
be4027fb36 | ||
|
|
0f03b47a46 | ||
|
|
c923e0803e | ||
|
|
bd74dbbfc5 | ||
|
|
a0599e75b7 | ||
|
|
f334b6349b | ||
|
|
2ff9fcfa1d | ||
|
|
aa23e4698b | ||
|
|
302fdeb1f9 | ||
|
|
eea8542fc0 | ||
|
|
a36d79e993 | ||
|
|
be5c1b2072 | ||
|
|
f0bd1ea48e | ||
|
|
90d0460a42 | ||
|
|
c15ce2b20b | ||
|
|
b51f210bfa | ||
|
|
105c76c055 | ||
|
|
6b15b7a58f | ||
|
|
fee0aae975 | ||
|
|
b33b638d2d | ||
|
|
1e677a5f0f | ||
|
|
3f3f6c0f39 | ||
|
|
5dd53b48ef | ||
|
|
a215e11b20 | ||
|
|
7d71f9dd2d | ||
|
|
183f1574ca | ||
|
|
504b651bb9 | ||
|
|
c848ff4bdb | ||
|
|
5aa8f5bc01 | ||
|
|
6c5d5a543c | ||
|
|
a8bc849b5d | ||
|
|
5f535b9661 | ||
|
|
eaf865539c | ||
|
|
75448c41d4 | ||
|
|
afc96e2021 | ||
|
|
d7bce80c7b | ||
|
|
7e07d20d31 | ||
|
|
238111be09 | ||
|
|
6f0820a3ed | ||
|
|
1d0d1c249b | ||
|
|
1b5ed68375 | ||
|
|
ce996b3fc8 | ||
|
|
2789522948 | ||
|
|
55a56cc275 | ||
|
|
20101527b2 | ||
|
|
311bd39100 | ||
|
|
fab77a15f0 | ||
|
|
09e7f5c618 | ||
|
|
c5a674b1df | ||
|
|
f83453cfc7 | ||
|
|
1ca8ebebd9 | ||
|
|
7cc73ed46c | ||
|
|
01b4694b47 | ||
|
|
bcc6d59c6b | ||
|
|
b4e2e5f586 | ||
|
|
43ed874789 | ||
|
|
8928c87fc3 | ||
|
|
e3f8a918d6 | ||
|
|
668bb94270 | ||
|
|
89e0543e0a | ||
|
|
6695b50965 | ||
|
|
5c02c85126 | ||
|
|
de99c0c7e7 | ||
|
|
9f0348979b | ||
|
|
5b5cd310b9 | ||
|
|
ea85d68a26 | ||
|
|
fb0a883f7b | ||
|
|
64a0efe29a | ||
|
|
1f361b7260 | ||
|
|
763b06477d | ||
|
|
cd7d2a0778 | ||
|
|
a4be3f304d | ||
|
|
88184d14d6 | ||
|
|
844d8eba47 | ||
|
|
5e65945a53 | ||
|
|
428040762f | ||
|
|
30e0a13bc5 | ||
|
|
275c2b1a6b | ||
|
|
e409814cb6 | ||
|
|
acdb624713 | ||
|
|
3cae8e7124 | ||
|
|
2595b9eae7 | ||
|
|
46a8561925 | ||
|
|
bb74cab887 | ||
|
|
dd7f58b9b9 | ||
|
|
8165b83a27 | ||
|
|
fa62c270e6 | ||
|
|
c03199e78a | ||
|
|
f346503534 | ||
|
|
1315ca2a57 | ||
|
|
2f0474a467 |
@@ -4,7 +4,10 @@ project(mirall)
|
||||
set(PACKAGE "mirall")
|
||||
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
|
||||
|
||||
if ( DEFINED OEM_THEME_DIR AND EXISTS ${OEM_THEME_DIR}/OEM.cmake )
|
||||
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 )
|
||||
else ()
|
||||
include ( ${CMAKE_SOURCE_DIR}/OWNCLOUD.cmake )
|
||||
@@ -12,7 +15,7 @@ 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}/src/mirall/")
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/src/mirall/")
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -53,9 +56,19 @@ 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 )
|
||||
find_package(Csync)
|
||||
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(Csync REQUIRED)
|
||||
if(UNIX)
|
||||
find_package(INotify REQUIRED)
|
||||
else()
|
||||
@@ -65,7 +78,6 @@ find_package(Sphinx)
|
||||
find_package(PdfLatex)
|
||||
find_package(QtKeychain)
|
||||
|
||||
set(WITH_CSYNC CSYNC_FOUND)
|
||||
set(WITH_QTKEYCHAIN ${QTKEYCHAIN_FOUND})
|
||||
set(USE_INOTIFY ${INOTIFY_FOUND})
|
||||
|
||||
@@ -92,12 +104,6 @@ set(CPACK_SOURCE_IGNORE_FILES
|
||||
|
||||
include(OwnCloudCPack.cmake)
|
||||
|
||||
#
|
||||
# This cmake builds two targets (aka apps), mirall and owncloud. For the owncloud
|
||||
# target, OWNCLOUD_CLIENT needs to be a compile flag. It is set in src/CMakeLists.txt
|
||||
# but if that fails because cmake is too old, uncomment this here if you want to build
|
||||
# owncloud.
|
||||
# add_definitions(-DOWNCLOUD_CLIENT)
|
||||
add_definitions(-DUNICODE)
|
||||
add_definitions(-D_UNICODE)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ If you have questions about how to use the ownCloud Desktop Client, please
|
||||
direct these to the [mailing list][mailinglist] or our [forum][forum].
|
||||
We are also available on [IRC][irc].
|
||||
|
||||
### Guidelines
|
||||
* Report the issue using our [template][template], it includes all the
|
||||
### Bug Reporting Guidelines
|
||||
* Important: Report the issue using our [template][template], it includes all the
|
||||
informations we need to track down the issue.
|
||||
* This repository is *only* for issues within the ownCloud desktop client.
|
||||
Issues in other compontents should be reported in their own repositores:
|
||||
|
||||
67
ChangeLog
@@ -1,5 +1,70 @@
|
||||
ChangeLog
|
||||
=========
|
||||
version 1.4.0beta1 (release 2013-08-08 ), csync 0.81.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
|
||||
* 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
|
||||
* [Fixes] Fix crash race by making certificateChain() thread safe
|
||||
* [Fixes] Build with older CMake versions (CentOS/RHEL 6)
|
||||
* [Fixes] Wording in GUI
|
||||
* [Fixes] Silently ignore "installed = true" status.php
|
||||
* Set log verbosity before calling csync_init.
|
||||
* GUI feedback for the statistics copy action
|
||||
* Safer approach for detecting duplicate sync runs
|
||||
|
||||
version 1.2.4 (release 2013-04-11 ), csync 0.70.6 required
|
||||
* [Fixes] Clarify string in folder wizard
|
||||
@@ -10,11 +75,9 @@ version 1.2.4 (release 2013-04-11 ), csync 0.70.6 required
|
||||
* [Fixes] Allow setting the configuration directory on command line
|
||||
|
||||
version 1.2.3 (release 2013-04-02 ), csync 0.70.5 required
|
||||
|
||||
* [Fixes] Unbreak self-signed certificate handling
|
||||
|
||||
version 1.2.2 (release 2013-04-02 ), csync 0.70.5 required
|
||||
|
||||
* [Fixes] Do not crash when local file tree contains symlinks
|
||||
* [Fixes] Correctly handle locked files on Windows
|
||||
* [Fixes] Display errors in all members of the SSL chain
|
||||
|
||||
61
README.md
@@ -2,50 +2,12 @@
|
||||
|
||||
## Introduction
|
||||
|
||||
Mirall synchronizes your folders with another computer.
|
||||
|
||||
The ultimate goals of Mirall are:
|
||||
|
||||
* Network location aware: should not try to sync against your NAS if you are
|
||||
not in the home network
|
||||
* It is a zero-interaction tool. So forget about resolving conflicts.
|
||||
* It should work silently and realiably.
|
||||
|
||||
Mirall is in early stages of development, and may still eat your
|
||||
files or hang your computer.
|
||||
|
||||
* Network location awareness not implemented yet
|
||||
* Current version supports local and remote (sftp and smb) folders.
|
||||
* It is powered by csync (http://www.csync.org), however
|
||||
the user does not know and other tools will be incorporated to provide other
|
||||
functionality.
|
||||
|
||||
## Current issues
|
||||
|
||||
* No sane way to backup conflicting versions yet, this should be solved
|
||||
in a near csync release (--conflictcopy, available in Jann's branch).
|
||||
Right now the newest copy wins.
|
||||
* You can't remove folder configurations
|
||||
Workaround: delete ~/.local/share/data/Mirall/folders/$alias and restart
|
||||
* Some tasks block the GUI (initial setup of watchers)
|
||||
* May be some concurrency issues
|
||||
|
||||
## Roadmap
|
||||
|
||||
* Improve robustness to minimize user interaction
|
||||
* Improve feedback and sync results
|
||||
* Add support for other folder types: tarsnap, duplicity, git (SparkleShare)
|
||||
|
||||
## Requirements
|
||||
|
||||
* Linux (currently it uses inotify to detect file changes)
|
||||
* unison installed in the local and remote machine
|
||||
(you should not care if you got Mirall with your favorite
|
||||
distribution)
|
||||
Mirall is a tool to synchronize files from ownCloud Server with your computer.
|
||||
It uses OCSync as its syncing backend.
|
||||
|
||||
## Download
|
||||
|
||||
### openSUSE
|
||||
### Binary packages
|
||||
|
||||
* Refer to the download page http://owncloud.org/sync-clients/
|
||||
|
||||
@@ -55,23 +17,12 @@ files or hang your computer.
|
||||
|
||||
## Building the source code
|
||||
|
||||
You need Qt 4.7 and cmake:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
|
||||
To generate a tarball:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make package_source
|
||||
Please refer to [Building the Client](http://doc.owncloud.org/desktop/1.2/building.html)
|
||||
in the ownCloud client manual.
|
||||
|
||||
## Authors
|
||||
|
||||
* Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Duncan Mac-Vicar P. <duncan@kde.org> (original Mirall author)
|
||||
* Klaas Freitag <freitag@owncloud.com>
|
||||
* Daniel Molkentin <danimo@owncloud.com>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set( VERSION_MAJOR 1 )
|
||||
set( VERSION_MINOR 2 )
|
||||
set( VERSION_PATCH 4 )
|
||||
set( VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX})
|
||||
set( VERSION_MINOR 4 )
|
||||
set( VERSION_PATCH 0 )
|
||||
set( VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX}beta2")
|
||||
set( SOVERSION 0 )
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -267,7 +266,7 @@ FunctionEnd
|
||||
# INSTALLER SECTIONS #
|
||||
# #
|
||||
##############################################################################
|
||||
Section "${APPLICATION_NAME}" SEC_OWNCLOUD
|
||||
Section "${APPLICATION_NAME}" SEC_APPLICATION
|
||||
SectionIn 1 2 3 RO
|
||||
SetDetailsPrint listonly
|
||||
|
||||
@@ -283,84 +282,20 @@ Section "${APPLICATION_NAME}" SEC_OWNCLOUD
|
||||
!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
|
||||
@@ -374,13 +309,14 @@ Section "${APPLICATION_NAME}" SEC_OWNCLOUD
|
||||
|
||||
;License & release notes.
|
||||
File "@CPACK_RESOURCE_FILE_LICENSE@"
|
||||
File /oname=NOTES.txt ${NSI_PATH}\RELEASE_NOTES.txt
|
||||
;File /oname=NOTES.txt ${NSI_PATH}\RELEASE_NOTES.txt
|
||||
|
||||
;QT stuff:
|
||||
File "${QT_DLL_PATH}\QtCore4.dll"
|
||||
File "${QT_DLL_PATH}\QtGui4.dll"
|
||||
File "${QT_DLL_PATH}\QtNetwork4.dll"
|
||||
File "${QT_DLL_PATH}\QtXml4.dll"
|
||||
File "${QT_DLL_PATH}\QtWebKit4.dll"
|
||||
|
||||
;QtKeyChain stuff
|
||||
File "${MING_BIN}\libqtkeychain.dll"
|
||||
@@ -404,7 +340,7 @@ Section "${APPLICATION_NAME}" SEC_OWNCLOUD
|
||||
|
||||
; 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"
|
||||
@@ -425,9 +361,9 @@ SectionGroup "Shortcuts"
|
||||
SetShellVarContext all
|
||||
RMDir /r "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
CreateDirectory "$SMPROGRAMS\${APPLICATION_NAME}"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\LICENSE.lnk" "$INSTDIR\LICENSE.txt"
|
||||
;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}\Release notes.lnk" "$INSTDIR\NOTES.txt"
|
||||
CreateShortCut "$SMPROGRAMS\${APPLICATION_NAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
SetShellVarContext current
|
||||
${MementoSectionEnd}
|
||||
@@ -455,17 +391,6 @@ 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
|
||||
@@ -475,7 +400,6 @@ ${MementoSectionDone}
|
||||
!insertmacro MUI_DESCRIPTION_TEXT ${SEC_START_MENU} "${APPLICATION_NAME} program group."
|
||||
!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_QUICK_AUTOSTART} "Register ${APPLICATION_NAME} to run on Windows startup."
|
||||
!insertmacro MUI_FUNCTION_DESCRIPTION_END
|
||||
|
||||
Section -post
|
||||
@@ -578,11 +502,6 @@ 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
|
||||
|
||||
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,7 +2,6 @@
|
||||
#define CONFIG_H
|
||||
|
||||
#cmakedefine USE_INOTIFY 1
|
||||
#cmakedefine WITH_CSYNC 1
|
||||
#cmakedefine WITH_QTKEYCHAIN 1
|
||||
|
||||
#cmakedefine GIT_SHA1 "@GIT_SHA1@"
|
||||
|
||||
@@ -27,7 +27,7 @@ if(SPHINX_FOUND)
|
||||
# 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} )
|
||||
@@ -41,7 +41,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 +54,12 @@ 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} )
|
||||
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 +71,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
|
||||
|
||||
@@ -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 oCC checks both repositories for changes frequently after a certain time
|
||||
span. That is refered to as a sync run. In between the local repository is
|
||||
monitored by a file system monitor system that starts a sync run immediately
|
||||
if something was edited, added or removed.
|
||||
|
||||
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,22 @@ 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:: 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.
|
||||
|
||||
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 +104,39 @@ 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.
|
||||
|
||||
So what happens if a file has changed on both, the local and the remote repository
|
||||
since the last sync run? That means it can not easily be decided which version
|
||||
of the file is the one that should be used. Moreover, changes to any side must
|
||||
not be lost. That is called the conflict case and the client solves it by creating
|
||||
a conflict file of the older of the two files and save the newer one under the
|
||||
original file name. Conflict files are always created on the client and never on
|
||||
the server. The conflict file has the same name as the original file appended
|
||||
with the timestamp of the conflict detection.
|
||||
|
||||
The Sync Journal
|
||||
----------------
|
||||
The client stores the ETag number in a per-directory database, called the journal.
|
||||
It is located in the application directory (until version 1.1) or as a hidden file
|
||||
right in the directory to be synced (later versions).
|
||||
|
||||
If the journal database gets removed, oCC's CSync backend will rebuild the database
|
||||
by comparing the files and their modification times. Thus it should be made sure
|
||||
that both server and client synchronized to NTP time before restarting the client
|
||||
after a database removal.
|
||||
|
||||
The oCC also provides a button in the Settings Dialog that allows to "reset" the
|
||||
journal. That can be used to recreate the journal database.
|
||||
@@ -20,20 +20,24 @@ Linux
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
Follow the `generic build instructions`_.
|
||||
Next to XCode (and the command line tools!), you will need some
|
||||
extra dependencies.
|
||||
|
||||
You can install the missing dependencies via MacPorts_ or Homebrew_.
|
||||
You can install these dependencies via MacPorts_ or Homebrew_.
|
||||
This is only needed on the build machine, since non-standard libs
|
||||
will be deployed in the app bundle.
|
||||
|
||||
The only exception to this rule is libiniparser_, which lacks a decent
|
||||
build system. If you are using Homebrew_, you can just add it::
|
||||
The tested and preferred way is to use HomeBrew_. The ownCloud team has
|
||||
its own repository which contains non-standard recipes. Add it with::
|
||||
|
||||
brew tap dschmidt/owncloud
|
||||
brew install iniparser
|
||||
brew tap owncloud/owncloud
|
||||
|
||||
Otherwise, you need to copy the header and lib files to
|
||||
``/usr/local/include`` and ``/usr/local/lib`` respectively.
|
||||
Next, install the missing dependencies::
|
||||
|
||||
brew install $(brew deps ocsync)
|
||||
brew install $(brew deps mirall)
|
||||
|
||||
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
|
||||
@@ -51,18 +55,22 @@ 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)::
|
||||
|
||||
http://download.opensuse.org/repositories/isv:/ownCloud:/devel:/mingw:/win32/openSUSE_12.1/
|
||||
http://download.opensuse.org/repositories/windows:/mingw/openSUSE_12.1/
|
||||
http://download.opensuse.org/repositories/windows:/mingw:/win32/openSUSE_12.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
|
||||
|
||||
Next, install the cross-compiler packages and the cross-compiled dependencies::
|
||||
|
||||
zypper si -d mingw32-csync
|
||||
zypper install kdewin-png2ico mingw32-libqt4 mingw32-libqt4-devel \
|
||||
mingw32-libgcrypt mingw32-libgnutls mingw32-libneon \
|
||||
mingw32-libbeecrypt mingw32-libopenssl mingw32-openssl \
|
||||
mingw32-libpng-devel mingw32-libsqlite mingw32-qtkeychain \
|
||||
mingw32-qtkeychain-devel mingw32-iniparser mingw32-dlfcn
|
||||
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
|
||||
|
||||
For the installer, the NSIS installer package is also required::
|
||||
|
||||
@@ -76,18 +84,18 @@ For the installer, the NSIS installer package is also required::
|
||||
You will also need to manually download and install the following files with
|
||||
``rpm -ivh <package>`` (They will also work with OpenSUSE 12.2)::
|
||||
|
||||
http://pmbs.links2linux.org/download/mingw:/32/openSUSE_12.1/x86_64/mingw32-cross-nsis-plugin-processes-0-1.1.x86_64.rpm
|
||||
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://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
|
||||
|
||||
Now, follow the `generic build instructions`_, but pay attention to
|
||||
the following differences:
|
||||
|
||||
1. For building ``libocsync``, you need to use ``mingw32-cmake`` instead
|
||||
of cmake.
|
||||
2. Also, you need to specify *absolute pathes* for ``CSYNC_LIBRARY_PATH``
|
||||
and ``CSYNC_LIBRARY_PATH`` when running ``cmake`` on mirall.
|
||||
3. for building ``mirall``, you need to use ``cmake`` again, but make sure
|
||||
2. for building ``mirall``, you need to use ``cmake`` again, but make sure
|
||||
to append the following parameter::
|
||||
3. Also, you need to specify *absolute pathes* for ``CSYNC_LIBRARY_PATH``
|
||||
and ``CSYNC_LIBRARY_PATH`` when running ``cmake`` on mirall.
|
||||
|
||||
``-DCMAKE_TOOLCHAIN_FILE=../mirall/admin/win/Toolchain-mingw32-openSUSE.cmake``
|
||||
|
||||
@@ -147,7 +155,7 @@ directories. If this succeeds, call ``make``. The owncloud binary should appear
|
||||
in the ``bin`` directory. You can also run ``make install`` to install the client to
|
||||
``/usr/local/bin``.
|
||||
|
||||
To build in installer (requires the mingw32-cross-nsis packages)::
|
||||
To build an installer/app bundle (requires the mingw32-cross-nsis packages on Windows)::
|
||||
|
||||
make package
|
||||
|
||||
@@ -161,4 +169,3 @@ Known cmake parameters:
|
||||
.. _Git: http://git-scm.com
|
||||
.. _MacPorts: http://www.macports.org
|
||||
.. _Homebrew: http://mxcl.github.com/homebrew/
|
||||
.. _libiniparser: http://ndevilla.free.fr/iniparser/
|
||||
|
||||
@@ -41,7 +41,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'ownCloud Client Manual'
|
||||
copyright = u'2012, The ownCloud developers'
|
||||
copyright = u'2013, The ownCloud developers'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -215,8 +215,6 @@ latex_documents = [
|
||||
man_pages = [
|
||||
('owncloud.1', 'owncloud', u'File synchronisation desktop utility.',
|
||||
[u'The ownCloud developers'], 1),
|
||||
('mirall.1', 'mirall', u'File synchronisation desktop utility.',
|
||||
[u'The ownCloud developers'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@@ -250,7 +248,7 @@ texinfo_documents = [
|
||||
epub_title = u'ownCloud Client Manual'
|
||||
epub_author = u'The ownCloud developers'
|
||||
epub_publisher = u'The ownCloud developers'
|
||||
epub_copyright = u'2012, The ownCloud developers'
|
||||
epub_copyright = u'2013, The ownCloud developers'
|
||||
|
||||
# The language of the text. It defaults to the language option
|
||||
# or en if the language is not set.
|
||||
|
||||
@@ -16,8 +16,6 @@ It contains settings in the ini file format known from Windows.
|
||||
|
||||
.. note:: Changes may be overwritten by using ownCloud's configuration dialog.
|
||||
|
||||
.. note:: The new version is less precise in this regard.
|
||||
|
||||
These are config settings that may be changed:
|
||||
|
||||
``remotePollinterval`` (default: ``30000``)
|
||||
@@ -26,6 +24,3 @@ These are config settings that may be changed:
|
||||
``maxLogLines`` (default: ``20000``)
|
||||
Maximum count of log lines shown in the log window
|
||||
|
||||
``remotePollinterval``
|
||||
The frequency used for polling for remote changes on the ownCloud Server.
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Mac OS X
|
||||
Installing the ownCloud client on your Mac follows the normal app installation
|
||||
pattern:
|
||||
|
||||
1. Download the installation file Click ownCloud-1.1.1.dmg, a window with the
|
||||
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
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
mirall(1)
|
||||
---------
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
*mirall* [`OPTIONS`...]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
mirall is a file synchronisation desktop utility.
|
||||
It synchronizes files on your local machine with an ownCloud Server. If you
|
||||
make a change to the files on one computer, it will flow across the others
|
||||
using this desktop sync clients.
|
||||
|
||||
Normally you start the client by click on the desktop icon or start from the
|
||||
application menu. After starting an ownCloud icon appears in the system tray.
|
||||
|
||||
Options
|
||||
=======
|
||||
.. include:: options.rst
|
||||
|
||||
Config File
|
||||
===========
|
||||
.. include:: conffile.rst
|
||||
|
||||
BUGS
|
||||
====
|
||||
|
||||
Please report bugs at https://github.com/owncloud/core/issues.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
`csync(1)`, `mirall(1)`
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
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`` `<filename>`
|
||||
write log output to file.
|
||||
write log output to file <filename>.
|
||||
|
||||
``--flushlog``
|
||||
``--logdir`` `<name>`
|
||||
write each sync log output in a new file in directory <name>
|
||||
|
||||
``--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.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
owncloud is a file synchronisation desktop utility it is based on mirall.
|
||||
ownCloud is a file synchronisation desktop utility based on mirall.
|
||||
It synchronizes files on your local machine with an ownCloud Server. If you
|
||||
make a change to the files on one computer, it will flow across the others
|
||||
using this desktop sync clients.
|
||||
@@ -33,5 +33,5 @@ Please report bugs at https://github.com/owncloud/core/issues.
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
`csync(1)`, `mirall(1)`
|
||||
`csync(1)`
|
||||
|
||||
|
||||
@@ -4,9 +4,12 @@ Doc Build Convenience Scripts
|
||||
* ``htmlhelp.sh``: A script to install Microsoft HTML Workshop on Linux or Mac OS using Wine, along with some dependencies.
|
||||
* ``htmlhelp.reg``: Registry file to override some DLLs with their native version and set the right Windows version.
|
||||
|
||||
Those files have been taken from the HTML Help Project (http://code.google.com/p/htmlhelp/wiki/HHW4Wine).
|
||||
Those files have been taken from the `HTML Help Project`_.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The HTML Help Project has licensed its software under LGPLv3 terms.
|
||||
The HTML Help Project has licensed_ its software under LGPLv2.1 terms
|
||||
|
||||
.. _HTML Help Project: http://code.google.com/p/htmlhelp/wiki/HHW4Wine
|
||||
.. _licensed: https://code.google.com/p/htmlhelp/source/browse/trunk/pyhtmlhelp/COPYING
|
||||
|
||||
@@ -1,42 +1,123 @@
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
:All of my desktop clients fail to connect to ownCloud:
|
||||
If the client fails to start syncing it basically can have two
|
||||
basic reasons: Either the server setup has a problem or the client
|
||||
has a bug. When reporting bugs, it is crucial to find out what part
|
||||
of the system causes the problem.
|
||||
|
||||
Identifying basic functionality problems
|
||||
----------------------------------------
|
||||
|
||||
:Perform a general ownCloud Server test:
|
||||
A very first check is to verify that you can log on to ownClouds web
|
||||
application. Assuming your ownCloud instance is installed at
|
||||
``http://yourserver.com/owncloud``, type
|
||||
``http://yourserver.com/owncloud/`` into your browsers address bar.
|
||||
|
||||
If you are not prompted to enter your user name and password, or if you
|
||||
see a red warning box on the page, your server setup is not correct or needs
|
||||
fixes. Please verify that your server installation is working correctly.
|
||||
|
||||
:Ensure the WebDAV API is working:
|
||||
If all desktop clients fail to connect to ownCloud, but the access via the
|
||||
web interface works, the problem often is a mis-configuration of the WebDAV
|
||||
API.
|
||||
|
||||
The ownCloud client uses the built-in WebDAV access of the server content.
|
||||
Verify that you can log on to ownClouds WebDAV server. Assuming your ownCloud
|
||||
instance is installed at ``http://yourserver.com/owncloud``, type
|
||||
``http://yourserver.com/owncloud/remote.php/webdav`` into your browsers
|
||||
address bar.
|
||||
|
||||
If you are not prompted to enter your user name and password, please verify
|
||||
that your server installation is working correctly.
|
||||
|
||||
If you are prompted, but the authentication fails even though the credentials
|
||||
your provided are correct, please ensure that your authentication backend
|
||||
is configured properly.
|
||||
|
||||
:Use a WebDAV command line tool to test:
|
||||
A more sophisticated test is to use a WebDAV command line client and log
|
||||
into the ownCloud WebDAV server, such as a little app called cadaver, available
|
||||
on Linux. I can be used to further verify that the WebDAV server is running
|
||||
properly, for example by performing PROPFIND calls:
|
||||
into the ownCloud WebDAV server, such as a little app called cadaver,
|
||||
available on Linux. It can be used to further verify that the WebDAV server is
|
||||
running properly, for example by performing PROPFIND calls:
|
||||
|
||||
``propget .`` called within cadaver will return some properties of the current
|
||||
directory and thus be a successful WebDAV connect.
|
||||
|
||||
:The desktop client fails for an unknown reason:
|
||||
Start the client with ``--logwindow``. You can also open a log window for an
|
||||
already running session, by simply starting the client again with this
|
||||
parameter. Syntax:
|
||||
Isolating other issues
|
||||
----------------------
|
||||
|
||||
If the sync result is unreliable, please ensure that the folder synced with
|
||||
ownCloud is not shared with other syncing apps.
|
||||
|
||||
.. note:: Syncing the same directory with ownCloud and other sync software such
|
||||
as Unison, rsync, Microsoft Windows Offline Folders or cloud services
|
||||
such as DropBox or Microsoft SkyDrive is not supported and should
|
||||
not be attempted. In the worst case, doing so can result in data
|
||||
loss.
|
||||
|
||||
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.
|
||||
|
||||
.. note:: The data directory on the server is exclusive to ownCloud and must
|
||||
not be modified manually.
|
||||
|
||||
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:
|
||||
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.
|
||||
|
||||
You can also open a log window for an already running session, by simply
|
||||
starting the client again with this parameter. Syntax:
|
||||
|
||||
* Windows: ``C:\Program Files (x86)\ownCloud\owncloud.exe --logwindow``
|
||||
* Mac OS X: ``/Applications/owncloud.app/Contents/MacOS/owncloud --logwindow``
|
||||
* Linux: ``owncloud --logwindow``
|
||||
|
||||
The log output can help you with tracking down problem, and if you report
|
||||
a bug, it's useful to include the output.
|
||||
It is also possible to directly log to a directory, which is an useful option
|
||||
in case the problem only happens ocassionally. In that case it is better to
|
||||
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. 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:
|
||||
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
|
||||
``Info``.
|
||||
|
||||
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:
|
||||
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
|
||||
problems. A file called ``access_log`` usually records all requests handled
|
||||
by the server. Especially the access_log is a very good debugging tool as the
|
||||
log line contains a lot of information of every request and it's result.
|
||||
|
||||
More information about the apache logging can be found at
|
||||
``http://httpd.apache.org/docs/current/logs.html``.
|
||||
|
||||
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
|
||||
problems. A file called ``access_log`` usually records all requests handled
|
||||
by the server. More information about the apache logging can be found at
|
||||
``http://httpd.apache.org/docs/current/logs.html``.
|
||||
@@ -7,11 +7,82 @@ application menu. In the system tray, an ownCloud icon appears.
|
||||
|
||||
.. index:: start application
|
||||
|
||||
A left click on the tray icon open a status dialog which gives an overview on
|
||||
the configured sync folders and allows to add and remove more sync folder
|
||||
connections as well as pausing a sync connection.
|
||||
Overview
|
||||
--------
|
||||
|
||||
A right click on the tray icon gives other configuration options.
|
||||
ownCloud is represented by an icon in the Desktop's system tray, also known
|
||||
as notification area.
|
||||
|
||||
The clients menu is accessed with a right click (Windows, Linux) or left click
|
||||
(Mac OS).
|
||||
|
||||
The status of the current sync can be observed in the Status dialog, available
|
||||
trough the ``Open status...`` option. On Windows, a left click on the tray icon
|
||||
also opens the status dialog.
|
||||
|
||||
.. note:: Until the intial setup has finished, the Connection Wizard will be
|
||||
shown instead when left-clicking on Windows.
|
||||
|
||||
The dialog provides an overview on the configured sync folders and allows to add
|
||||
and remove more sync folder connections as well as pausing a sync connection.
|
||||
|
||||
Changing Your Password and Account Settings
|
||||
-------------------------------------------
|
||||
|
||||
In the ``Settings`` Dialog, choose ``Account`` -> ``Modify Account``. It will open
|
||||
Setup Wizard, which next to reconfiguring your connection to use a different
|
||||
user or server also will allow to change the password for the local account,
|
||||
or to switch from HTTP to HTTPS.
|
||||
|
||||
Setting up a Proxy
|
||||
------------------
|
||||
|
||||
By default, the configured system proxy will be picked up. This may not be
|
||||
working reliably on some Linux distributions, as only the ``http_proxy``
|
||||
variable gets picked up. You can configure a proxy different from your
|
||||
system default in the ``Network`` section of the ``Settings`` dialog.
|
||||
|
||||
The default settings assume an HTTP proxy, which is the typical use case.
|
||||
If you require SOCKS 5 proxy, pick ``SOCKS5 proxy`` instead of ``HTTP(S) proxy``
|
||||
from the drop down menu. SOCKS 5 proxies are typically provided by some
|
||||
SSH implementations, for instance OpenSSH's ``-D`` parameter. This is
|
||||
useful for scenarios where SSH is employed to securely tunnel a client
|
||||
to the network running the ownCloud server.
|
||||
|
||||
Limiting Bandwidth
|
||||
------------------
|
||||
|
||||
Starting with Version 1.4, the Client provides bandwidth limiter.
|
||||
This option can be found in the ``Network`` section of the
|
||||
``Settings Dialog``.
|
||||
|
||||
You will find two settings for ``Download Bandwidth`` and
|
||||
``Upload Bandwidth``.
|
||||
|
||||
Upload Bandwidth
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
The default is to automatically limit the upload. The rationale
|
||||
for this default is that typically, Computers and laptops are
|
||||
not directly connected to the server, but via a Cable Modems
|
||||
or DSL lines, which provide significantly more downstream than
|
||||
upstream bandwith. Sataurating the upstream bandwidth would
|
||||
interfere with other applications, especially Voice-Over-IP or
|
||||
Games.
|
||||
|
||||
The automatic limiter will throttle the speed to about 75%
|
||||
of the available upstream bandwidth. If you are communicating
|
||||
with the server via a fast, symetric connection, you can set the
|
||||
Limiter to ``No Limit`` instead. If want a stronger limitation,
|
||||
choose ``Limit to`` and specify a limit manually.
|
||||
|
||||
|
||||
Download Bandwidth
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Because the download bandwidth is usually no concern, it is not
|
||||
automatically limited. Should you find that the Client is taking
|
||||
up too much bandwidth, you can manually specify a limit (in KB).
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
@@ -20,6 +20,8 @@ PHP version:
|
||||
|
||||
ownCloud version:
|
||||
|
||||
Storage backend:
|
||||
|
||||
### Client configuration
|
||||
Client version:
|
||||
|
||||
@@ -31,18 +33,14 @@ Installation path of client:
|
||||
|
||||
### Logs
|
||||
|
||||
#### output of `owncloud --logwindow` or `owncloud --logfile log.txt`
|
||||
```
|
||||
Insert your log output here
|
||||
```
|
||||
Please use Gist (https://gist.github.com/) or a similar code paster for longer
|
||||
logs.
|
||||
|
||||
#### Web server error log
|
||||
```
|
||||
Insert your webserver log here
|
||||
```
|
||||
```Template for output < 10 lines```
|
||||
|
||||
#### ownCloud log (data/owncloud.log)
|
||||
```
|
||||
Insert your ownCloud log here
|
||||
```
|
||||
1. Output of `owncloud --logwindow` or `owncloud --logfile log.txt`
|
||||
|
||||
2. Web server error log:
|
||||
|
||||
3. ownCloud log (data/owncloud.log):
|
||||
|
||||
|
||||
@@ -5,3 +5,4 @@ Exec=@APPLICATION_EXECUTABLE@
|
||||
Name=@APPLICATION_NAME@ desktop sync client
|
||||
GenericName=Folder Sync
|
||||
Icon=@APPLICATION_SHORTNAME@
|
||||
Keywords=@APPLICATION_NAME@;syncing;file;sharing
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
<file>resources/folder-remote.png</file>
|
||||
<file>resources/folder-sync.png</file>
|
||||
<file>resources/folder-grey.png</file>
|
||||
<file>resources/mirall-32.png</file>
|
||||
<file>resources/mirall-128.png</file>
|
||||
<file>resources/mirall-48.png</file>
|
||||
<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/network.png</file>
|
||||
<file>resources/owncloud_logo_blue.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 698 B |
|
Before Width: | Height: | Size: 991 B |
BIN
resources/network.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/owncloud_logo_blue.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
resources/settings.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
resources/warning-16.png
Normal file
|
After Width: | Height: | Size: 596 B |
22
src/3rdparty/LGPL_EXCEPTION.txt
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
Digia Qt LGPL Exception version 1.1
|
||||
|
||||
As an additional permission to the GNU Lesser General Public License version
|
||||
2.1, the object code form of a "work that uses the Library" may incorporate
|
||||
material from a header file that is part of the Library. You may distribute
|
||||
such object code under terms of your choice, provided that:
|
||||
(i) the header files of the Library have not been modified; and
|
||||
(ii) the incorporated material is limited to numerical parameters, data
|
||||
structure layouts, accessors, macros, inline functions and
|
||||
templates; and
|
||||
(iii) you comply with the terms of Section 6 of the GNU Lesser General
|
||||
Public License version 2.1.
|
||||
|
||||
Moreover, you may apply this exception to a modified version of the Library,
|
||||
provided that such modification does not involve copying material from the
|
||||
Library into the modified Library's header files unless such material is
|
||||
limited to (i) numerical parameters; (ii) data structure layouts;
|
||||
(iii) accessors; and (iv) small macros, templates and inline functions of
|
||||
five lines or less in length.
|
||||
|
||||
Furthermore, you are not required to apply this additional permission to a
|
||||
modified version of the Library.
|
||||
165
src/3rdparty/QProgressIndicator/LICENSE
vendored
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
137
src/3rdparty/QProgressIndicator/QProgressIndicator.cpp
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
*
|
||||
* This file is part of QProgressIndicator,
|
||||
* an open-source recent files menu widget
|
||||
*
|
||||
* Copyright (C) 2009 - 2010 Morgan Leborgne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with QRecentFilesMenu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "QProgressIndicator.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
QProgressIndicator::QProgressIndicator(QWidget* parent)
|
||||
: QWidget(parent),
|
||||
m_angle(0),
|
||||
m_timerId(-1),
|
||||
m_delay(40),
|
||||
m_displayedWhenStopped(false),
|
||||
m_color(Qt::black)
|
||||
{
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
|
||||
bool QProgressIndicator::isAnimated () const
|
||||
{
|
||||
return (m_timerId != -1);
|
||||
}
|
||||
|
||||
void QProgressIndicator::setDisplayedWhenStopped(bool state)
|
||||
{
|
||||
m_displayedWhenStopped = state;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
bool QProgressIndicator::isDisplayedWhenStopped() const
|
||||
{
|
||||
return m_displayedWhenStopped;
|
||||
}
|
||||
|
||||
void QProgressIndicator::startAnimation()
|
||||
{
|
||||
m_angle = 0;
|
||||
|
||||
if (m_timerId == -1)
|
||||
m_timerId = startTimer(m_delay);
|
||||
}
|
||||
|
||||
void QProgressIndicator::stopAnimation()
|
||||
{
|
||||
if (m_timerId != -1)
|
||||
killTimer(m_timerId);
|
||||
|
||||
m_timerId = -1;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void QProgressIndicator::setAnimationDelay(int delay)
|
||||
{
|
||||
if (m_timerId != -1)
|
||||
killTimer(m_timerId);
|
||||
|
||||
m_delay = delay;
|
||||
|
||||
if (m_timerId != -1)
|
||||
m_timerId = startTimer(m_delay);
|
||||
}
|
||||
|
||||
void QProgressIndicator::setColor(const QColor & color)
|
||||
{
|
||||
m_color = color;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
QSize QProgressIndicator::sizeHint() const
|
||||
{
|
||||
return QSize(20,20);
|
||||
}
|
||||
|
||||
int QProgressIndicator::heightForWidth(int w) const
|
||||
{
|
||||
return w;
|
||||
}
|
||||
|
||||
void QProgressIndicator::timerEvent(QTimerEvent * /*event*/)
|
||||
{
|
||||
m_angle = (m_angle+30)%360;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void QProgressIndicator::paintEvent(QPaintEvent * /*event*/)
|
||||
{
|
||||
if (!m_displayedWhenStopped && !isAnimated())
|
||||
return;
|
||||
|
||||
int width = qMin(this->width(), this->height());
|
||||
|
||||
QPainter p(this);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
int outerRadius = (width-1)*0.5;
|
||||
int innerRadius = (width-1)*0.5*0.38;
|
||||
|
||||
int capsuleHeight = outerRadius - innerRadius;
|
||||
int capsuleWidth = (width > 32 ) ? capsuleHeight *.23 : capsuleHeight *.35;
|
||||
int capsuleRadius = capsuleWidth/2;
|
||||
|
||||
for (int i=0; i<12; i++)
|
||||
{
|
||||
QColor color = m_color;
|
||||
color.setAlphaF(1.0f - (i/12.0f));
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(color);
|
||||
p.save();
|
||||
p.translate(rect().center());
|
||||
p.rotate(m_angle - i*30.0f);
|
||||
p.drawRoundedRect(-capsuleWidth*0.5, -(innerRadius+capsuleHeight), capsuleWidth, capsuleHeight, capsuleRadius, capsuleRadius);
|
||||
p.restore();
|
||||
}
|
||||
}
|
||||
108
src/3rdparty/QProgressIndicator/QProgressIndicator.h
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
*
|
||||
* This file is part of QProgressIndicator,
|
||||
* an open-source recent files menu widget
|
||||
*
|
||||
* Copyright (C) 2009 - 2010 Morgan Leborgne
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with QRecentFilesMenu. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#ifndef QPROGRESSINDICATOR_H
|
||||
#define QPROGRESSINDICATOR_H
|
||||
|
||||
#include <QWidget>
|
||||
#include <QColor>
|
||||
|
||||
/*!
|
||||
\class QProgressIndicator
|
||||
\brief The QProgressIndicator class lets an application display a progress indicator to show that a lengthy task is under way.
|
||||
|
||||
Progress indicators are indeterminate and do nothing more than spin to show that the application is busy.
|
||||
\sa QProgressBar
|
||||
*/
|
||||
class QProgressIndicator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(int delay READ animationDelay WRITE setAnimationDelay)
|
||||
Q_PROPERTY(bool displayedWhenStopped READ isDisplayedWhenStopped WRITE setDisplayedWhenStopped)
|
||||
Q_PROPERTY(QColor color READ color WRITE setColor)
|
||||
public:
|
||||
QProgressIndicator(QWidget* parent = 0);
|
||||
|
||||
/*! Returns the delay between animation steps.
|
||||
\return The number of milliseconds between animation steps. By default, the animation delay is set to 40 milliseconds.
|
||||
\sa setAnimationDelay
|
||||
*/
|
||||
int animationDelay() const { return m_delay; }
|
||||
|
||||
/*! Returns a Boolean value indicating whether the component is currently animated.
|
||||
\return Animation state.
|
||||
\sa startAnimation stopAnimation
|
||||
*/
|
||||
bool isAnimated () const;
|
||||
|
||||
/*! Returns a Boolean value indicating whether the receiver shows itself even when it is not animating.
|
||||
\return Return true if the progress indicator shows itself even when it is not animating. By default, it returns false.
|
||||
\sa setDisplayedWhenStopped
|
||||
*/
|
||||
bool isDisplayedWhenStopped() const;
|
||||
|
||||
/*! Returns the color of the component.
|
||||
\sa setColor
|
||||
*/
|
||||
const QColor & color() const { return m_color; }
|
||||
|
||||
virtual QSize sizeHint() const;
|
||||
int heightForWidth(int w) const;
|
||||
public slots:
|
||||
/*! Starts the spin animation.
|
||||
\sa stopAnimation isAnimated
|
||||
*/
|
||||
void startAnimation();
|
||||
|
||||
/*! Stops the spin animation.
|
||||
\sa startAnimation isAnimated
|
||||
*/
|
||||
void stopAnimation();
|
||||
|
||||
/*! Sets the delay between animation steps.
|
||||
Setting the \a delay to a value larger than 40 slows the animation, while setting the \a delay to a smaller value speeds it up.
|
||||
\param delay The delay, in milliseconds.
|
||||
\sa animationDelay
|
||||
*/
|
||||
void setAnimationDelay(int delay);
|
||||
|
||||
/*! Sets whether the component hides itself when it is not animating.
|
||||
\param state The animation state. Set false to hide the progress indicator when it is not animating; otherwise true.
|
||||
\sa isDisplayedWhenStopped
|
||||
*/
|
||||
void setDisplayedWhenStopped(bool state);
|
||||
|
||||
/*! Sets the color of the components to the given color.
|
||||
\sa color
|
||||
*/
|
||||
void setColor(const QColor & color);
|
||||
protected:
|
||||
virtual void timerEvent(QTimerEvent * event);
|
||||
virtual void paintEvent(QPaintEvent * event);
|
||||
private:
|
||||
int m_angle;
|
||||
int m_timerId;
|
||||
int m_delay;
|
||||
bool m_displayedWhenStopped;
|
||||
QColor m_color;
|
||||
};
|
||||
|
||||
#endif // QPROGRESSINDICATOR_H
|
||||
14
src/3rdparty/QProgressIndicator/README.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
## Description
|
||||
|
||||
The QProgressIndicator class lets an application display a progress indicator to show that a lengthy task is under way.
|
||||
Will work at any size.
|
||||
|
||||
<img src="https://raw.github.com/mojocorp/QProgressIndicator/master/screen-capture-1.png" >
|
||||
<img src="https://raw.github.com/mojocorp/QProgressIndicator/master/screen-capture-2.png" >
|
||||
|
||||
## Dependency
|
||||
Qt 4.4.x.
|
||||
|
||||
## License
|
||||
|
||||
LGPL
|
||||
353
src/3rdparty/fancylineedit/fancylineedit.cpp
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "fancylineedit.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QDebug>
|
||||
#include <QString>
|
||||
#include <QPropertyAnimation>
|
||||
#include <QApplication>
|
||||
#include <QMenu>
|
||||
#include <QMouseEvent>
|
||||
#include <QLabel>
|
||||
#include <QAbstractButton>
|
||||
#include <QPainter>
|
||||
#include <QStyle>
|
||||
#include <QPaintEvent>
|
||||
#include <QDesktopWidget>
|
||||
|
||||
/*! Opens a menu at the specified widget position.
|
||||
* This functions computes the position where to show the menu, and opens it with
|
||||
* QMenu::exec().
|
||||
* \param menu The menu to open
|
||||
* \param widget The widget next to which to open the menu
|
||||
*/
|
||||
static void execMenuAtWidget(QMenu *menu, QWidget *widget)
|
||||
{
|
||||
QPoint p;
|
||||
QRect screen = qApp->desktop()->availableGeometry(widget);
|
||||
QSize sh = menu->sizeHint();
|
||||
QRect rect = widget->rect();
|
||||
if (widget->isRightToLeft()) {
|
||||
if (widget->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height())
|
||||
p = widget->mapToGlobal(rect.bottomRight());
|
||||
else
|
||||
p = widget->mapToGlobal(rect.topRight() - QPoint(0, sh.height()));
|
||||
p.rx() -= sh.width();
|
||||
} else {
|
||||
if (widget->mapToGlobal(QPoint(0, rect.bottom())).y() + sh.height() <= screen.height())
|
||||
p = widget->mapToGlobal(rect.bottomLeft());
|
||||
else
|
||||
p = widget->mapToGlobal(rect.topLeft() - QPoint(0, sh.height()));
|
||||
}
|
||||
p.rx() = qMax(screen.left(), qMin(p.x(), screen.right() - sh.width()));
|
||||
p.ry() += 1;
|
||||
|
||||
menu->exec(p);
|
||||
}
|
||||
|
||||
/*!
|
||||
\class Utils::FancyLineEdit
|
||||
|
||||
\brief A line edit with an embedded pixmap on one side that is connected to
|
||||
a menu.
|
||||
|
||||
Additionally, it can display a grayed hintText (like "Type Here to")
|
||||
when not focused and empty. When connecting to the changed signals and
|
||||
querying text, one has to be aware that the text is set to that hint
|
||||
text if isShowingHintText() returns true (that is, does not contain
|
||||
valid user input).
|
||||
*/
|
||||
|
||||
enum { margin = 6 };
|
||||
|
||||
#define ICONBUTTON_HEIGHT 18
|
||||
#define FADE_TIME 160
|
||||
|
||||
namespace Utils {
|
||||
|
||||
// --------- FancyLineEditPrivate
|
||||
class FancyLineEditPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
explicit FancyLineEditPrivate(FancyLineEdit *parent);
|
||||
|
||||
virtual bool eventFilter(QObject *obj, QEvent *event);
|
||||
|
||||
FancyLineEdit *m_lineEdit;
|
||||
QPixmap m_pixmap[2];
|
||||
QMenu *m_menu[2];
|
||||
bool m_menuTabFocusTrigger[2];
|
||||
IconButton *m_iconbutton[2];
|
||||
bool m_iconEnabled[2];
|
||||
};
|
||||
|
||||
|
||||
FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) :
|
||||
QObject(parent), m_lineEdit(parent)
|
||||
{
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
m_menu[i] = 0;
|
||||
m_menuTabFocusTrigger[i] = false;
|
||||
m_iconbutton[i] = new IconButton(parent);
|
||||
m_iconbutton[i]->installEventFilter(this);
|
||||
m_iconbutton[i]->hide();
|
||||
m_iconbutton[i]->setAutoHide(false);
|
||||
m_iconEnabled[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
|
||||
{
|
||||
int buttonIndex = -1;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (obj == m_iconbutton[i]) {
|
||||
buttonIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buttonIndex == -1)
|
||||
return QObject::eventFilter(obj, event);
|
||||
switch (event->type()) {
|
||||
case QEvent::FocusIn:
|
||||
if (m_menuTabFocusTrigger[buttonIndex] && m_menu[buttonIndex]) {
|
||||
m_lineEdit->setFocus();
|
||||
execMenuAtWidget(m_menu[buttonIndex], m_iconbutton[buttonIndex]);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QObject::eventFilter(obj, event);
|
||||
}
|
||||
|
||||
|
||||
// --------- FancyLineEdit
|
||||
FancyLineEdit::FancyLineEdit(QWidget *parent) :
|
||||
QLineEdit(parent),
|
||||
d(new FancyLineEditPrivate(this))
|
||||
{
|
||||
ensurePolished();
|
||||
updateMargins();
|
||||
|
||||
connect(this, SIGNAL(textChanged(QString)), this, SLOT(checkButtons(QString)));
|
||||
connect(d->m_iconbutton[Left], SIGNAL(clicked()), this, SLOT(iconClicked()));
|
||||
connect(d->m_iconbutton[Right], SIGNAL(clicked()), this, SLOT(iconClicked()));
|
||||
}
|
||||
|
||||
void FancyLineEdit::checkButtons(const QString &text)
|
||||
{
|
||||
if (m_oldText.isEmpty() || text.isEmpty()) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (d->m_iconbutton[i]->hasAutoHide())
|
||||
d->m_iconbutton[i]->animateShow(!text.isEmpty());
|
||||
}
|
||||
m_oldText = text;
|
||||
}
|
||||
}
|
||||
|
||||
FancyLineEdit::~FancyLineEdit()
|
||||
{
|
||||
}
|
||||
|
||||
void FancyLineEdit::setButtonVisible(Side side, bool visible)
|
||||
{
|
||||
d->m_iconbutton[side]->setVisible(visible);
|
||||
d->m_iconEnabled[side] = visible;
|
||||
updateMargins();
|
||||
}
|
||||
|
||||
bool FancyLineEdit::isButtonVisible(Side side) const
|
||||
{
|
||||
return d->m_iconEnabled[side];
|
||||
}
|
||||
|
||||
void FancyLineEdit::iconClicked()
|
||||
{
|
||||
IconButton *button = qobject_cast<IconButton *>(sender());
|
||||
int index = -1;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
if (d->m_iconbutton[i] == button)
|
||||
index = i;
|
||||
if (index == -1)
|
||||
return;
|
||||
if (d->m_menu[index]) {
|
||||
execMenuAtWidget(d->m_menu[index], button);
|
||||
} else {
|
||||
emit buttonClicked((Side)index);
|
||||
if (index == Left)
|
||||
emit leftButtonClicked();
|
||||
else if (index == Right)
|
||||
emit rightButtonClicked();
|
||||
}
|
||||
}
|
||||
|
||||
void FancyLineEdit::updateMargins()
|
||||
{
|
||||
bool leftToRight = (layoutDirection() == Qt::LeftToRight);
|
||||
Side realLeft = (leftToRight ? Left : Right);
|
||||
Side realRight = (leftToRight ? Right : Left);
|
||||
|
||||
int leftMargin = d->m_iconbutton[realLeft]->pixmap().width() + 8;
|
||||
int rightMargin = d->m_iconbutton[realRight]->pixmap().width() + 8;
|
||||
// Note KDE does not reserve space for the highlight color
|
||||
if (style()->inherits("OxygenStyle")) {
|
||||
leftMargin = qMax(24, leftMargin);
|
||||
rightMargin = qMax(24, rightMargin);
|
||||
}
|
||||
|
||||
QMargins margins((d->m_iconEnabled[realLeft] ? leftMargin : 0), 0,
|
||||
(d->m_iconEnabled[realRight] ? rightMargin : 0), 0);
|
||||
|
||||
setTextMargins(margins);
|
||||
}
|
||||
|
||||
void FancyLineEdit::updateButtonPositions()
|
||||
{
|
||||
QRect contentRect = rect();
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Side iconpos = (Side)i;
|
||||
if (layoutDirection() == Qt::RightToLeft)
|
||||
iconpos = (iconpos == Left ? Right : Left);
|
||||
|
||||
if (iconpos == FancyLineEdit::Right) {
|
||||
const int iconoffset = textMargins().right() + 4;
|
||||
d->m_iconbutton[i]->setGeometry(contentRect.adjusted(width() - iconoffset, 0, 0, 0));
|
||||
} else {
|
||||
const int iconoffset = textMargins().left() + 4;
|
||||
d->m_iconbutton[i]->setGeometry(contentRect.adjusted(0, 0, -width() + iconoffset, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyLineEdit::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
updateButtonPositions();
|
||||
}
|
||||
|
||||
void FancyLineEdit::setButtonPixmap(Side side, const QPixmap &buttonPixmap)
|
||||
{
|
||||
d->m_iconbutton[side]->setPixmap(buttonPixmap);
|
||||
updateMargins();
|
||||
updateButtonPositions();
|
||||
update();
|
||||
}
|
||||
|
||||
QPixmap FancyLineEdit::buttonPixmap(Side side) const
|
||||
{
|
||||
return d->m_pixmap[side];
|
||||
}
|
||||
|
||||
void FancyLineEdit::setButtonMenu(Side side, QMenu *buttonMenu)
|
||||
{
|
||||
d->m_menu[side] = buttonMenu;
|
||||
d->m_iconbutton[side]->setIconOpacity(1.0);
|
||||
}
|
||||
|
||||
QMenu *FancyLineEdit::buttonMenu(Side side) const
|
||||
{
|
||||
return d->m_menu[side];
|
||||
}
|
||||
|
||||
bool FancyLineEdit::hasMenuTabFocusTrigger(Side side) const
|
||||
{
|
||||
return d->m_menuTabFocusTrigger[side];
|
||||
}
|
||||
|
||||
void FancyLineEdit::setMenuTabFocusTrigger(Side side, bool v)
|
||||
{
|
||||
if (d->m_menuTabFocusTrigger[side] == v)
|
||||
return;
|
||||
|
||||
d->m_menuTabFocusTrigger[side] = v;
|
||||
d->m_iconbutton[side]->setFocusPolicy(v ? Qt::TabFocus : Qt::NoFocus);
|
||||
}
|
||||
|
||||
bool FancyLineEdit::hasAutoHideButton(Side side) const
|
||||
{
|
||||
return d->m_iconbutton[side]->hasAutoHide();
|
||||
}
|
||||
|
||||
void FancyLineEdit::setAutoHideButton(Side side, bool h)
|
||||
{
|
||||
d->m_iconbutton[side]->setAutoHide(h);
|
||||
if (h)
|
||||
d->m_iconbutton[side]->setIconOpacity(text().isEmpty() ? 0.0 : 1.0);
|
||||
else
|
||||
d->m_iconbutton[side]->setIconOpacity(1.0);
|
||||
}
|
||||
|
||||
void FancyLineEdit::setButtonToolTip(Side side, const QString &tip)
|
||||
{
|
||||
d->m_iconbutton[side]->setToolTip(tip);
|
||||
}
|
||||
|
||||
void FancyLineEdit::setButtonFocusPolicy(Side side, Qt::FocusPolicy policy)
|
||||
{
|
||||
d->m_iconbutton[side]->setFocusPolicy(policy);
|
||||
}
|
||||
|
||||
// IconButton - helper class to represent a clickable icon
|
||||
|
||||
IconButton::IconButton(QWidget *parent)
|
||||
: QAbstractButton(parent), m_autoHide(false)
|
||||
{
|
||||
setCursor(Qt::ArrowCursor);
|
||||
setFocusPolicy(Qt::NoFocus);
|
||||
}
|
||||
|
||||
void IconButton::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
QRect pixmapRect = QRect(0, 0, m_pixmap.width(), m_pixmap.height());
|
||||
pixmapRect.moveCenter(rect().center());
|
||||
|
||||
if (m_autoHide)
|
||||
painter.setOpacity(m_iconOpacity);
|
||||
|
||||
painter.drawPixmap(pixmapRect, m_pixmap);
|
||||
}
|
||||
|
||||
void IconButton::animateShow(bool visible)
|
||||
{
|
||||
if (visible) {
|
||||
QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
|
||||
animation->setDuration(FADE_TIME);
|
||||
animation->setEndValue(1.0);
|
||||
animation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
} else {
|
||||
QPropertyAnimation *animation = new QPropertyAnimation(this, "iconOpacity");
|
||||
animation->setDuration(FADE_TIME);
|
||||
animation->setEndValue(0.0);
|
||||
animation->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Utils
|
||||
121
src/3rdparty/fancylineedit/fancylineedit.h
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef FANCYLINEEDIT_H
|
||||
#define FANCYLINEEDIT_H
|
||||
|
||||
#include <QLineEdit>
|
||||
#include <QAbstractButton>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class FancyLineEditPrivate;
|
||||
|
||||
class IconButton: public QAbstractButton
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(float iconOpacity READ iconOpacity WRITE setIconOpacity)
|
||||
Q_PROPERTY(bool autoHide READ hasAutoHide WRITE setAutoHide)
|
||||
Q_PROPERTY(QPixmap pixmap READ pixmap WRITE setPixmap)
|
||||
public:
|
||||
explicit IconButton(QWidget *parent = 0);
|
||||
void paintEvent(QPaintEvent *event);
|
||||
void setPixmap(const QPixmap &pixmap) { m_pixmap = pixmap; update(); }
|
||||
QPixmap pixmap() const { return m_pixmap; }
|
||||
float iconOpacity() { return m_iconOpacity; }
|
||||
void setIconOpacity(float value) { m_iconOpacity = value; update(); }
|
||||
void animateShow(bool visible);
|
||||
|
||||
void setAutoHide(bool hide) { m_autoHide = hide; }
|
||||
bool hasAutoHide() const { return m_autoHide; }
|
||||
|
||||
private:
|
||||
float m_iconOpacity;
|
||||
bool m_autoHide;
|
||||
QPixmap m_pixmap;
|
||||
};
|
||||
|
||||
class FancyLineEdit : public QLineEdit
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_ENUMS(Side)
|
||||
|
||||
public:
|
||||
enum Side {Left = 0, Right = 1};
|
||||
|
||||
explicit FancyLineEdit(QWidget *parent = 0);
|
||||
~FancyLineEdit();
|
||||
|
||||
QPixmap buttonPixmap(Side side) const;
|
||||
void setButtonPixmap(Side side, const QPixmap &pixmap);
|
||||
|
||||
QMenu *buttonMenu(Side side) const;
|
||||
void setButtonMenu(Side side, QMenu *menu);
|
||||
|
||||
void setButtonVisible(Side side, bool visible);
|
||||
bool isButtonVisible(Side side) const;
|
||||
|
||||
void setButtonToolTip(Side side, const QString &);
|
||||
void setButtonFocusPolicy(Side side, Qt::FocusPolicy policy);
|
||||
|
||||
// Set whether tabbing in will trigger the menu.
|
||||
void setMenuTabFocusTrigger(Side side, bool v);
|
||||
bool hasMenuTabFocusTrigger(Side side) const;
|
||||
|
||||
// Set if icon should be hidden when text is empty
|
||||
void setAutoHideButton(Side side, bool h);
|
||||
bool hasAutoHideButton(Side side) const;
|
||||
#if QT_VERSION <= 0x040700
|
||||
void setPlaceholderText( const QString& ) { }
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void buttonClicked(Utils::FancyLineEdit::Side side);
|
||||
void leftButtonClicked();
|
||||
void rightButtonClicked();
|
||||
|
||||
private slots:
|
||||
void checkButtons(const QString &);
|
||||
void iconClicked();
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *e);
|
||||
|
||||
private:
|
||||
void updateMargins();
|
||||
void updateButtonPositions();
|
||||
friend class Utils::FancyLineEditPrivate;
|
||||
|
||||
FancyLineEditPrivate *d;
|
||||
QString m_oldText;
|
||||
};
|
||||
|
||||
} // namespace Utils
|
||||
|
||||
#endif // FANCYLINEEDIT_H
|
||||
@@ -3,7 +3,7 @@ include(${QT_USE_FILE})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ../mirall.qrc)
|
||||
if ( DEFINED OEM_THEME_DIR)
|
||||
if ( IS_DIRECTORY ${OEM_THEME_DIR} )
|
||||
QT4_ADD_RESOURCES ( MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
|
||||
set(theme_dir ${OEM_THEME_DIR}/theme)
|
||||
else()
|
||||
@@ -11,39 +11,39 @@ else()
|
||||
set(theme_dir ${CMAKE_CURRENT_SOURCE_DIR}/../theme)
|
||||
endif()
|
||||
|
||||
set(synclib_NAME ${APPLICATION_SHORTNAME}sync)
|
||||
|
||||
set(mirall_UI
|
||||
mirall/folderwizardsourcepage.ui
|
||||
mirall/folderwizardtargetpage.ui
|
||||
mirall/folderwizardnetworkpage.ui
|
||||
mirall/folderwizardowncloudpage.ui
|
||||
mirall/statusdialog.ui
|
||||
mirall/owncloudwizardselecttypepage.ui
|
||||
mirall/owncloudsetuppage.ui
|
||||
mirall/createanowncloudpage.ui
|
||||
mirall/owncloudftpaccesspage.ui
|
||||
mirall/owncloudwizardresultpage.ui
|
||||
mirall/owncloudcredentialspage.ui
|
||||
mirall/sslerrordialog.ui
|
||||
mirall/proxydialog.ui
|
||||
mirall/fileitemdialog.ui
|
||||
mirall/settingsdialog.ui
|
||||
mirall/generalsettings.ui
|
||||
mirall/networksettings.ui
|
||||
mirall/accountsettings.ui
|
||||
mirall/ignorelisteditor.ui
|
||||
mirall/itemprogressdialog.ui
|
||||
wizard/owncloudsetupnocredspage.ui
|
||||
wizard/owncloudhttpcredspage.ui
|
||||
wizard/owncloudwizardresultpage.ui
|
||||
wizard/owncloudadvancedsetuppage.ui
|
||||
)
|
||||
|
||||
set(3rdparty_SRC
|
||||
3rdparty/qtsingleapplication/qtsingleapplication.cpp
|
||||
3rdparty/qtsingleapplication/qtlocalpeer.h
|
||||
3rdparty/qtsingleapplication/qtlocalpeer.cpp
|
||||
3rdparty/qtsingleapplication/qtsingleapplication.h
|
||||
3rdparty/qtsingleapplication/qtsinglecoreapplication.h
|
||||
3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
|
||||
|
||||
3rdparty/qtlockedfile/qtlockedfile.h
|
||||
3rdparty/qtlockedfile/qtlockedfile.cpp
|
||||
3rdparty/fancylineedit/fancylineedit.cpp
|
||||
3rdparty/QProgressIndicator/QProgressIndicator.cpp
|
||||
)
|
||||
|
||||
set(3rdparty_HEADER
|
||||
3rdparty/qtsingleapplication/qtlocalpeer.h
|
||||
3rdparty/qtsingleapplication/qtsingleapplication.h
|
||||
3rdparty/qtsingleapplication/qtsinglecoreapplication.h
|
||||
3rdparty/fancylineedit/fancylineedit.h
|
||||
3rdparty/QProgressIndicator/QProgressIndicator.h
|
||||
)
|
||||
qt4_wrap_cpp(3rdparty_MOC ${3rdparty_HEADER})
|
||||
|
||||
@@ -56,6 +56,8 @@ endif()
|
||||
set(3rdparty_INC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtlockedfile
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtsingleapplication
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QProgressIndicator
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/fancylineedit
|
||||
)
|
||||
|
||||
qt4_wrap_ui(mirall_UI_SRCS ${mirall_UI})
|
||||
@@ -65,32 +67,57 @@ set(libsync_SRCS
|
||||
mirall/folder.cpp
|
||||
mirall/folderwatcher.cpp
|
||||
mirall/syncresult.cpp
|
||||
mirall/unisonfolder.cpp
|
||||
mirall/networklocation.cpp
|
||||
mirall/mirallconfigfile.cpp
|
||||
mirall/credentialstore.cpp
|
||||
mirall/csyncfolder.cpp
|
||||
mirall/owncloudfolder.cpp
|
||||
mirall/csyncthread.cpp
|
||||
mirall/fileutils.cpp
|
||||
mirall/theme.cpp
|
||||
mirall/owncloudtheme.cpp
|
||||
mirall/miralltheme.cpp
|
||||
mirall/owncloudinfo.cpp
|
||||
mirall/logger.cpp
|
||||
mirall/utility.cpp
|
||||
mirall/connectionvalidator.cpp
|
||||
mirall/progressdispatcher.cpp
|
||||
mirall/mirallaccessmanager.cpp
|
||||
creds/dummycredentials.cpp
|
||||
creds/httpcredentials.cpp
|
||||
creds/credentialsfactory.cpp
|
||||
creds/http/credentialstore.cpp
|
||||
creds/http/httpconfigfile.cpp
|
||||
creds/shibbolethcredentials.cpp
|
||||
creds/shibboleth/shibbolethaccessmanager.cpp
|
||||
creds/shibboleth/shibbolethcookiejar.cpp
|
||||
creds/shibboleth/shibbolethwebview.cpp
|
||||
creds/shibboleth/shibbolethrefresher.cpp
|
||||
creds/shibboleth/shibbolethconfigfile.cpp
|
||||
creds/credentialscommon.cpp
|
||||
)
|
||||
set(libsync_HEADERS
|
||||
|
||||
set(libsync_HEADERS
|
||||
mirall/folderman.h
|
||||
mirall/folder.h
|
||||
mirall/folderwatcher.h
|
||||
mirall/unisonfolder.h
|
||||
mirall/csyncfolder.h
|
||||
mirall/owncloudfolder.h
|
||||
mirall/csyncthread.h
|
||||
mirall/theme.h
|
||||
mirall/owncloudtheme.h
|
||||
mirall/owncloudinfo.h
|
||||
mirall/credentialstore.h
|
||||
mirall/logger.h
|
||||
mirall/connectionvalidator.h
|
||||
mirall/progressdispatcher.h
|
||||
mirall/mirallaccessmanager.h
|
||||
creds/abstractcredentials.h
|
||||
creds/dummycredentials.h
|
||||
creds/httpcredentials.h
|
||||
creds/credentialsfactory.h
|
||||
creds/http/credentialstore.h
|
||||
creds/http/httpconfigfile.h
|
||||
creds/shibbolethcredentials.h
|
||||
creds/shibboleth/shibbolethaccessmanager.h
|
||||
creds/shibboleth/shibbolethcookiejar.h
|
||||
creds/shibboleth/shibbolethwebview.h
|
||||
creds/shibboleth/shibbolethrefresher.h
|
||||
creds/shibboleth/shibbolethconfigfile.h
|
||||
creds/credentialscommon.h
|
||||
)
|
||||
|
||||
IF( INOTIFY_FOUND )
|
||||
@@ -121,59 +148,80 @@ if(QTKEYCHAIN_FOUND)
|
||||
include_directories(${QTKEYCHAIN_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
add_library(owncloudsync SHARED ${libsync_SRCS} ${syncMoc})
|
||||
set_target_properties( owncloudsync PROPERTIES COMPILE_DEFINITIONS OWNCLOUD_CLIENT)
|
||||
set_target_properties( owncloudsync PROPERTIES
|
||||
add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
|
||||
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
|
||||
${APPLICATION_SHORTNAME}.desktop)
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_SHORTNAME}.desktop)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_SHORTNAME}.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_SRCS
|
||||
mirall/application.cpp
|
||||
mirall/systray.cpp
|
||||
mirall/folderwizard.cpp
|
||||
mirall/statusdialog.cpp
|
||||
mirall/owncloudwizard.cpp
|
||||
mirall/folderstatusmodel.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/itemprogressdialog.cpp
|
||||
)
|
||||
|
||||
set(mirall_HEADERS
|
||||
mirall/application.h
|
||||
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/itemprogressdialog.h
|
||||
)
|
||||
|
||||
if( UNIX AND NOT APPLE)
|
||||
@@ -183,7 +231,7 @@ if( UNIX AND NOT APPLE)
|
||||
endif()
|
||||
|
||||
# csync is required.
|
||||
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CMAKE_BINARY_DIR})
|
||||
include_directories(${CSYNC_INCLUDE_DIR}/csync ${CSYNC_INCLUDE_DIR} ${CSYNC_BUILD_PATH}/src)
|
||||
include_directories(${3rdparty_INC})
|
||||
|
||||
qt4_wrap_cpp(mirallMoc ${mirall_HEADERS})
|
||||
@@ -214,35 +262,22 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
|
||||
if(NOT WIN32)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/resources/mirall-48.png
|
||||
DESTINATION share/icons/hicolor/48x48/apps/ RENAME mirall.png)
|
||||
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/${APPLICATION_SHORTNAME}-icon-*.png" )
|
||||
foreach( _file ${_icons} )
|
||||
string( REPLACE "${theme_dir}/colored/${APPLICATION_SHORTNAME}-icon-" "" _res ${_file} )
|
||||
string( REPLACE ".png" "" _res ${_res} )
|
||||
install( FILES ${_file} RENAME ${APPLICATION_SHORTNAME}.png DESTINATION
|
||||
${CMAKE_INSTALL_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_SHORTNAME}/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})
|
||||
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES COMPILE_DEFINITIONS OWNCLOUD_CLIENT)
|
||||
|
||||
# mirall is not built for the bundle
|
||||
add_executable( mirall WIN32 main.cpp ${final_src})
|
||||
|
||||
target_link_libraries(mirall ${QT_LIBRARIES} )
|
||||
target_link_libraries(mirall owncloudsync)
|
||||
target_link_libraries(mirall ${CSYNC_LIBRARY})
|
||||
|
||||
set_target_properties( mirall PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${BIN_OUTPUT_DIRECTORY} )
|
||||
install(TARGETS mirall
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
else()
|
||||
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
|
||||
include(DeployQt4)
|
||||
@@ -252,7 +287,6 @@ else()
|
||||
|
||||
# we must add MACOSX_BUNDLE only if building a bundle
|
||||
add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
|
||||
set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES COMPILE_DEFINITIONS OWNCLOUD_CLIENT)
|
||||
|
||||
#FIXME: hardcoded path
|
||||
if ( EXISTS ${CSYNC_BINARY_DIR}/modules/ocsync_owncloud.so )
|
||||
@@ -275,7 +309,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}
|
||||
|
||||
47
src/creds/abstractcredentials.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
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 QNetworkAccessManager* getQNAM() const = 0;
|
||||
virtual bool ready() const = 0;
|
||||
virtual void fetch() = 0;
|
||||
virtual void persistForUrl(const QString& url) = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void fetched();
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
77
src/creds/credentialscommon.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* 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 "creds/credentialscommon.h"
|
||||
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/owncloudinfo.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 = ownCloudInfo::instance()->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
|
||||
32
src/creds/credentialscommon.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* 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
|
||||
56
src/creds/dummycredentials.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 "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");
|
||||
}
|
||||
|
||||
QNetworkAccessManager* DummyCredentials::getQNAM() const
|
||||
{
|
||||
return new MirallAccessManager;
|
||||
}
|
||||
|
||||
bool DummyCredentials::ready() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void DummyCredentials::fetch()
|
||||
{
|
||||
Q_EMIT(fetched());
|
||||
}
|
||||
|
||||
void DummyCredentials::persistForUrl(const QString&)
|
||||
{}
|
||||
|
||||
} // ns Mirall
|
||||
39
src/creds/dummycredentials.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "mirall/credentialstore.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "creds/http/credentialstore.h"
|
||||
#include "creds/http/httpconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
@@ -35,7 +35,6 @@ 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
|
||||
@@ -67,51 +66,14 @@ CredentialStore::CredState CredentialStore::state()
|
||||
return _state;
|
||||
}
|
||||
|
||||
bool CredentialStore::canTryAgain()
|
||||
{
|
||||
bool canDoIt = false;
|
||||
|
||||
if( _tries > MAX_LOGIN_ATTEMPTS ) {
|
||||
qDebug() << "canTryAgain: Max attempts reached.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if( _state == NotFetched ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch( _type ) {
|
||||
case CredentialStore::User:
|
||||
canDoIt = true;
|
||||
break;
|
||||
case CredentialStore::Settings:
|
||||
break;
|
||||
case CredentialStore::KeyChain:
|
||||
canDoIt = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return canDoIt;
|
||||
}
|
||||
|
||||
void CredentialStore::fetchCredentials()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
if( ++_tries > MAX_LOGIN_ATTEMPTS ) {
|
||||
qDebug() << "Too many attempts to enter password!";
|
||||
_state = TooManyAttempts;
|
||||
emit( fetchCredentialsFinished(false) );
|
||||
return;
|
||||
}
|
||||
HttpConfigFile cfgFile;
|
||||
|
||||
bool ok = false;
|
||||
QString pwd;
|
||||
_user = cfgFile.ownCloudUser();
|
||||
_user = cfgFile.user();
|
||||
_url = cfgFile.ownCloudUrl();
|
||||
if( !cfgFile.passwordStorageAllowed() ) {
|
||||
_type = CredentialStore::User;
|
||||
}
|
||||
|
||||
QString key = keyChainKey(_url);
|
||||
|
||||
@@ -123,31 +85,22 @@ void CredentialStore::fetchCredentials()
|
||||
}
|
||||
|
||||
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->exec();
|
||||
break;
|
||||
}
|
||||
case CredentialStore::Settings: {
|
||||
/* Read from config file. */
|
||||
_state = Fetching;
|
||||
pwd = cfgFile.ownCloudPasswd();
|
||||
ok = true;
|
||||
cfgFile.fixupOldPassword();
|
||||
if( cfgFile.passwordExists() ) {
|
||||
pwd = cfgFile.password();
|
||||
ok = true;
|
||||
} else {
|
||||
ok = false;
|
||||
_state = EntryNotFound;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CredentialStore::KeyChain: {
|
||||
// If the credentials are here already, return.
|
||||
if( _state == Ok ) {
|
||||
if( _state == Ok || _state == AsyncWriting ) {
|
||||
emit(fetchCredentialsFinished(true));
|
||||
return;
|
||||
}
|
||||
@@ -189,25 +142,11 @@ void CredentialStore::fetchCredentials()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -254,9 +193,6 @@ void CredentialStore::slotKeyChainReadFinished(QKeychain::Job* job)
|
||||
case QKeychain::CouldNotDeleteEntry:
|
||||
_state = Error;
|
||||
break;
|
||||
case QKeychain::AccessDeniedByUser:
|
||||
_state = AccessDeniedByUser;
|
||||
break;
|
||||
case QKeychain::AccessDenied:
|
||||
_state = AccessDenied;
|
||||
break;
|
||||
@@ -305,53 +241,48 @@ QString CredentialStore::errorMessage()
|
||||
}
|
||||
|
||||
void CredentialStore::setCredentials( const QString& url, const QString& user,
|
||||
const QString& pwd, bool allowToStore )
|
||||
const QString& pwd )
|
||||
{
|
||||
_passwd = pwd;
|
||||
_user = user;
|
||||
if( allowToStore ) {
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
_type = KeyChain;
|
||||
_type = KeyChain;
|
||||
#else
|
||||
_type = Settings;
|
||||
_type = Settings;
|
||||
#endif
|
||||
} else {
|
||||
_type = User;
|
||||
}
|
||||
_url = url;
|
||||
_state = Ok;
|
||||
}
|
||||
|
||||
void CredentialStore::saveCredentials( )
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
HttpConfigFile 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
|
||||
|
||||
cfgFile.setUser(_user);
|
||||
switch( _type ) {
|
||||
case CredentialStore::User:
|
||||
deleteKeyChainCredential( key );
|
||||
break;
|
||||
case CredentialStore::KeyChain:
|
||||
case CredentialStore::KeyChain: {
|
||||
#ifdef WITH_QTKEYCHAIN
|
||||
WritePasswordJob *job = new WritePasswordJob(Theme::instance()->appName());
|
||||
// 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 );
|
||||
cfgFile.setPassword( _passwd );
|
||||
reset();
|
||||
break;
|
||||
default:
|
||||
@@ -371,17 +302,22 @@ void CredentialStore::slotKeyChainWriteFinished( QKeychain::Job *job )
|
||||
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();
|
||||
HttpConfigFile cfgFile;
|
||||
cfgFile.removePassword();
|
||||
_state = NotFetched;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "Error: KeyChain Write Password Job failed!";
|
||||
_state = Error;
|
||||
}
|
||||
#else
|
||||
(void) job;
|
||||
@@ -34,10 +34,8 @@ namespace Mirall {
|
||||
* 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.
|
||||
* EntryNotFound: No password entry found in the storage.
|
||||
* Error: A general error happened.
|
||||
* After fetching has finished, signal fetchCredentialsFinished(bool) is emitted.
|
||||
* The result can be retrieved with state() and password() and user() methods.
|
||||
@@ -49,19 +47,16 @@ class CredentialStore : public QObject
|
||||
public:
|
||||
enum CredState { NotFetched = 0,
|
||||
Ok,
|
||||
UserCanceled,
|
||||
Fetching,
|
||||
AsyncFetching,
|
||||
EntryNotFound,
|
||||
AccessDeniedByUser,
|
||||
AccessDenied,
|
||||
NoKeychainBackend,
|
||||
Error,
|
||||
TooManyAttempts };
|
||||
AsyncWriting };
|
||||
|
||||
enum CredentialType {
|
||||
User = 0,
|
||||
Settings,
|
||||
Settings = 0,
|
||||
KeyChain
|
||||
};
|
||||
|
||||
@@ -96,19 +91,13 @@ public:
|
||||
* 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 setCredentials( const QString& url, const QString& user, const QString& pwd);
|
||||
|
||||
void saveCredentials( );
|
||||
|
||||
QString errorMessage();
|
||||
|
||||
/**
|
||||
* @brief canTryAgain - check if another try to get credentials makes sense.
|
||||
*/
|
||||
bool canTryAgain();
|
||||
|
||||
void reset();
|
||||
signals:
|
||||
/**
|
||||
@@ -124,7 +113,6 @@ signals:
|
||||
protected slots:
|
||||
void slotKeyChainReadFinished( QKeychain::Job* );
|
||||
void slotKeyChainWriteFinished( QKeychain::Job* );
|
||||
void slotUserDialogDone(int);
|
||||
|
||||
private:
|
||||
explicit CredentialStore(QObject *parent = 0);
|
||||
@@ -137,9 +125,7 @@ private:
|
||||
static QString _user;
|
||||
static QString _url;
|
||||
static QString _errorMsg;
|
||||
static int _tries;
|
||||
static CredentialType _type;
|
||||
QInputDialog *_inputDialog;
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
218
src/creds/httpcredentials.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* 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 "creds/httpcredentials.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "creds/http/credentialstore.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int getauth(const char *prompt,
|
||||
char *buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void *userdata)
|
||||
{
|
||||
int re = 0;
|
||||
QMutex mutex;
|
||||
MirallConfigFile cfg;
|
||||
HttpCredentials* http_credentials = dynamic_cast< HttpCredentials* > (cfg.getCredentials());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
} // ns
|
||||
|
||||
HttpCredentials::HttpCredentials()
|
||||
: _user(),
|
||||
_password(),
|
||||
_ready(false),
|
||||
_attempts()
|
||||
{}
|
||||
|
||||
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(ownCloudInfo::instance()->getLastAuthCookies());
|
||||
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 MirallAccessManager;
|
||||
|
||||
connect( qnam, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
|
||||
this, SLOT(slotAuthentication(QNetworkReply*,QAuthenticator*)));
|
||||
|
||||
return qnam;
|
||||
}
|
||||
|
||||
bool HttpCredentials::ready() const
|
||||
{
|
||||
return _ready;
|
||||
}
|
||||
|
||||
void HttpCredentials::fetch()
|
||||
{
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
// TODO: merge CredentialStore into HttpCredentials?
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
connect(store, SIGNAL(fetchCredentialsFinished(bool)),
|
||||
this, SLOT(slotCredentialsFetched(bool)));
|
||||
store->fetchCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
void HttpCredentials::persistForUrl(const QString& url)
|
||||
{
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
store->setCredentials(url, _user, _password);
|
||||
store->saveCredentials();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotCredentialsFetched(bool ok)
|
||||
{
|
||||
_ready = ok;
|
||||
if (_ready) {
|
||||
CredentialStore* store(CredentialStore::instance());
|
||||
_user = store->user();
|
||||
_password = store->password();
|
||||
}
|
||||
Q_EMIT fetched();
|
||||
}
|
||||
|
||||
void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* authenticator)
|
||||
{
|
||||
if( !(authenticator && reply) ) return;
|
||||
|
||||
qDebug() << "Authenticating request for " << reply->url();
|
||||
|
||||
if (_attempts.contains(reply)) {
|
||||
++_attempts[reply];
|
||||
} else {
|
||||
connect(reply, SIGNAL(finished()),
|
||||
this, SLOT(slotReplyFinished()));
|
||||
_attempts[reply] = 1;
|
||||
}
|
||||
// TODO: Replace it with something meaningful...
|
||||
//if( reply->url().toString().startsWith( webdavUrl( _connection ) ) ) {
|
||||
if (_attempts[reply] > 1) {
|
||||
qDebug() << "Too many attempts to authenticate. Stop request.";
|
||||
reply->close();
|
||||
} else {
|
||||
authenticator->setUser( _user );
|
||||
authenticator->setPassword( _password );
|
||||
}
|
||||
//} else {
|
||||
// qDebug() << "WRN: attempt to authenticate to different url - closing.";
|
||||
// reply->close();
|
||||
//}
|
||||
}
|
||||
|
||||
void HttpCredentials::slotReplyFinished()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast< QNetworkReply* >(sender());
|
||||
|
||||
disconnect(reply, SIGNAL(finished()),
|
||||
this, SLOT(slotReplyFinished()));
|
||||
_attempts.remove (reply);
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
64
src/creds/httpcredentials.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
* Copyright (C) by Krzesimir Nowak <krzesimir@endocode.com>
|
||||
*
|
||||
* 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 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();
|
||||
void persistForUrl(const QString& url);
|
||||
|
||||
QString user() const;
|
||||
QString password() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotCredentialsFetched(bool);
|
||||
void slotAuthentication(QNetworkReply*, QAuthenticator*);
|
||||
void slotReplyFinished();
|
||||
|
||||
private:
|
||||
QString _user;
|
||||
QString _password;
|
||||
bool _ready;
|
||||
QMap<QNetworkReply*, int> _attempts;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
55
src/creds/shibboleth/shibbolethaccessmanager.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 <QDebug>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
#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
|
||||
52
src/creds/shibboleth/shibbolethrefresher.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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 "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibbolethcredentials.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
ShibbolethRefresher::ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent)
|
||||
: QObject(parent),
|
||||
_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);
|
||||
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
|
||||
47
src/creds/shibboleth/shibbolethrefresher.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 ShibbolethCredentials;
|
||||
|
||||
class ShibbolethRefresher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethRefresher(ShibbolethCredentials* creds, CSYNC* csync_ctx, QObject* parent = 0);
|
||||
|
||||
void refresh();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onInvalidatedAndFetched(const QByteArray& cookieData);
|
||||
|
||||
private:
|
||||
ShibbolethCredentials* _creds;
|
||||
CSYNC* _csync_ctx;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
104
src/creds/shibboleth/shibbolethwebview.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 <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QNetworkCookie>
|
||||
#include <QWebFrame>
|
||||
#include <QWebPage>
|
||||
|
||||
#include "creds/shibboleth/shibbolethcookiejar.h"
|
||||
#include "creds/shibboleth/shibbolethwebview.h"
|
||||
#include "mirall/mirallaccessmanager.h"
|
||||
|
||||
namespace Mirall
|
||||
{
|
||||
|
||||
void ShibbolethWebView::setup(const QUrl& url, ShibbolethCookieJar* jar)
|
||||
{
|
||||
MirallAccessManager* nm = new MirallAccessManager(this);
|
||||
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()));
|
||||
|
||||
nm->setCookieJar(jar);
|
||||
page->setNetworkAccessManager(nm);
|
||||
page->mainFrame ()->load (url);
|
||||
this->setPage (page);
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(const QUrl& url, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(url, new ShibbolethCookieJar(this));
|
||||
}
|
||||
|
||||
ShibbolethWebView::~ShibbolethWebView()
|
||||
{
|
||||
slotLoadFinished();
|
||||
}
|
||||
|
||||
ShibbolethWebView::ShibbolethWebView(const QUrl& url, ShibbolethCookieJar* jar, QWidget* parent)
|
||||
: QWebView(parent)
|
||||
{
|
||||
setup(url, 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()
|
||||
{
|
||||
QApplication::restoreOverrideCursor();
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
56
src/creds/shibboleth/shibbolethwebview.h
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.
|
||||
*/
|
||||
|
||||
#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 ShibbolethWebView : public QWebView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ShibbolethWebView(const QUrl& url, QWidget* parent = 0);
|
||||
ShibbolethWebView(const QUrl& url, 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();
|
||||
|
||||
private:
|
||||
void setup(const QUrl& url, ShibbolethCookieJar* jar);
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
231
src/creds/shibbolethcredentials.cpp
Normal file
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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 "creds/shibbolethcredentials.h"
|
||||
#include "creds/shibboleth/shibbolethaccessmanager.h"
|
||||
#include "creds/shibboleth/shibbolethwebview.h"
|
||||
#include "creds/shibboleth/shibbolethrefresher.h"
|
||||
#include "creds/shibboleth/shibbolethconfigfile.h"
|
||||
#include "creds/credentialscommon.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
|
||||
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);
|
||||
MirallConfigFile cfg;
|
||||
ShibbolethCredentials* creds = dynamic_cast< ShibbolethCredentials* > (cfg.getCredentials());
|
||||
|
||||
if (!creds) {
|
||||
qDebug() << "Not a Shibboleth creds instance!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
ShibbolethRefresher refresher(creds, csync_ctx);
|
||||
|
||||
// blocks
|
||||
refresher.refresh();
|
||||
|
||||
return creds->ready() ? 0 : 1;
|
||||
}
|
||||
|
||||
} // ns
|
||||
|
||||
ShibbolethCredentials::ShibbolethCredentials()
|
||||
: _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(ownCloudInfo::instance()->getLastAuthCookies());
|
||||
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");
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if (_ready) {
|
||||
Q_EMIT fetched();
|
||||
} else {
|
||||
ShibbolethConfigFile cfg;
|
||||
|
||||
_browser = new ShibbolethWebView(QUrl(cfg.ownCloudUrl()), cfg.createCookieJar());
|
||||
connect(_browser, SIGNAL(shibbolethCookieReceived(QNetworkCookie)),
|
||||
this, SLOT(onShibbolethCookieReceived(QNetworkCookie)));
|
||||
connect(_browser, SIGNAL(viewHidden()),
|
||||
this, SLOT(slotBrowserHidden()));
|
||||
_browser->show ();
|
||||
}
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::persistForUrl(const QString& /*url*/)
|
||||
{
|
||||
ShibbolethConfigFile cfg;
|
||||
|
||||
cfg.storeCookies(_otherCookies);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
_ready = false;
|
||||
connect (this, SIGNAL(fetched()),
|
||||
this, SLOT(onFetched()));
|
||||
fetch();
|
||||
}
|
||||
|
||||
void ShibbolethCredentials::onFetched()
|
||||
{
|
||||
disconnect (this, SIGNAL(fetched()),
|
||||
this, SLOT(onFetched()));
|
||||
|
||||
Q_EMIT invalidatedAndFetched(prepareCookieData());
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
72
src/creds/shibbolethcredentials.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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;
|
||||
QNetworkAccessManager* getQNAM() const;
|
||||
bool ready() const;
|
||||
void fetch();
|
||||
void persistForUrl(const QString& url);
|
||||
|
||||
QNetworkCookie cookie() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void invalidateAndFetch();
|
||||
|
||||
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:
|
||||
QByteArray prepareCookieData() const;
|
||||
void disposeBrowser();
|
||||
|
||||
QNetworkCookie _shibCookie;
|
||||
bool _ready;
|
||||
ShibbolethWebView* _browser;
|
||||
QMap<QUrl, QList<QNetworkCookie> > _otherCookies;
|
||||
};
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
#endif
|
||||
24
src/main.cpp
@@ -13,6 +13,9 @@
|
||||
*/
|
||||
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@@ -20,7 +23,12 @@ int main(int argc, char **argv)
|
||||
|
||||
Mirall::Application app(argc, argv);
|
||||
app.initialize();
|
||||
|
||||
|
||||
if( app.giveHelp() ) {
|
||||
app.showHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if the application is already running, notify it.
|
||||
if( app.isRunning() ) {
|
||||
QStringList args = app.arguments();
|
||||
@@ -30,12 +38,16 @@ 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();
|
||||
if (!QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
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()));
|
||||
}
|
||||
}
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
|
||||
679
src/mirall/accountsettings.cpp
Normal file
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
* 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/owncloudinfo.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/itemprogressdialog.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QListWidgetItem>
|
||||
#include <QMessageBox>
|
||||
#include <QAction>
|
||||
#include <QKeySequence>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
AccountSettings::AccountSettings(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::AccountSettings),
|
||||
_item(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
_model = new FolderStatusModel;
|
||||
_model->setParent(this);
|
||||
FolderStatusDelegate *delegate = new FolderStatusDelegate;
|
||||
|
||||
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->_ButtonInfo->setEnabled(false);
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
|
||||
QAction *resetFolderAction = new QAction(this);
|
||||
resetFolderAction->setShortcut(QKeySequence(Qt::Key_F5));
|
||||
connect(resetFolderAction, SIGNAL(triggered()), SLOT(slotResetCurrentFolder()));
|
||||
addAction(resetFolderAction);
|
||||
|
||||
connect(ui->_ButtonRemove, SIGNAL(clicked()), this, SLOT(slotRemoveCurrentFolder()));
|
||||
connect(ui->_ButtonEnable, SIGNAL(clicked()), this, SLOT(slotEnableCurrentFolder()));
|
||||
connect(ui->_ButtonInfo, SIGNAL(clicked()), this, SLOT(slotInfoAboutCurrentFolder()));
|
||||
connect(ui->_ButtonAdd, SIGNAL(clicked()), this, SLOT(slotAddFolder()));
|
||||
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)));
|
||||
|
||||
ownCloudInfo *ocInfo = ownCloudInfo::instance();
|
||||
slotUpdateQuota(ocInfo->lastQuotaTotalBytes(), ocInfo->lastQuotaUsedBytes());
|
||||
connect(ocInfo, SIGNAL(quotaUpdated(qint64,qint64)), SLOT(slotUpdateQuota(qint64,qint64)));
|
||||
|
||||
ui->connectLabel->setWordWrap( true );
|
||||
|
||||
setFolderList(FolderMan::instance()->map());
|
||||
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
void AccountSettings::slotFolderActivated( const QModelIndex& indx )
|
||||
{
|
||||
bool state = indx.isValid();
|
||||
|
||||
ui->_ButtonRemove->setEnabled( state );
|
||||
ui->_ButtonEnable->setEnabled( state );
|
||||
ui->_ButtonInfo->setEnabled( state );
|
||||
|
||||
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();
|
||||
}
|
||||
buttonsSetEnabled();
|
||||
}
|
||||
|
||||
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 );
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AccountSettings::buttonsSetEnabled()
|
||||
{
|
||||
bool haveFolders = ui->_folderList->model()->rowCount() > 0;
|
||||
|
||||
ui->_ButtonRemove->setEnabled(false);
|
||||
if( Theme::instance()->singleSyncFolder() ) {
|
||||
// only one folder synced folder allowed.
|
||||
ui->_ButtonAdd->setVisible(!haveFolders);
|
||||
} else {
|
||||
ui->_ButtonAdd->setVisible(true);
|
||||
ui->_ButtonAdd->setEnabled(true);
|
||||
}
|
||||
|
||||
QModelIndex selected = ui->_folderList->currentIndex();
|
||||
bool isSelected = selected.isValid();
|
||||
|
||||
ui->_ButtonEnable->setEnabled(isSelected);
|
||||
ui->_ButtonRemove->setEnabled(isSelected);
|
||||
ui->_ButtonInfo->setEnabled(isSelected);
|
||||
}
|
||||
|
||||
void AccountSettings::setListWidgetItem( QListWidgetItem *item )
|
||||
{
|
||||
_item = item;
|
||||
}
|
||||
|
||||
void AccountSettings::folderToModelItem( QStandardItem *item, Folder *f )
|
||||
{
|
||||
if( ! item || !f ) return;
|
||||
|
||||
item->setData( f->nativePath(), FolderStatusDelegate::FolderPathRole );
|
||||
item->setData( f->secondPath(), FolderStatusDelegate::FolderSecondPathRole );
|
||||
item->setData( f->alias(), FolderStatusDelegate::FolderAliasRole );
|
||||
item->setData( f->syncEnabled(), FolderStatusDelegate::FolderSyncEnabled );
|
||||
|
||||
SyncResult res = f->syncResult();
|
||||
SyncResult::Status status = res.status();
|
||||
|
||||
QString errors = res.errorStrings().join(QLatin1String("<br/>"));
|
||||
|
||||
Theme *theme = Theme::instance();
|
||||
item->setData( theme->statusHeaderText( status ), Qt::ToolTipRole );
|
||||
if( f->syncEnabled() ) {
|
||||
item->setData( theme->syncStateIcon( status ), FolderStatusDelegate::FolderStatusIconRole );
|
||||
} else {
|
||||
item->setData( theme->folderDisabledIcon( ), FolderStatusDelegate::FolderStatusIconRole ); // size 48 before
|
||||
}
|
||||
item->setData( theme->statusHeaderText( status ), FolderStatusDelegate::FolderStatus );
|
||||
item->setData( errors, 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() ) {
|
||||
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;
|
||||
}
|
||||
FolderMan *folderMan = FolderMan::instance();
|
||||
folderMan->slotRemoveFolder( alias );
|
||||
setFolderList(folderMan->map());
|
||||
emit folderChanged();
|
||||
slotCheckConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
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();
|
||||
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::slotCheckConnection()
|
||||
{
|
||||
if( ownCloudInfo::instance()->isConfigured() ) {
|
||||
connect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
connect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
|
||||
ui->connectLabel->setText( tr("Checking %1 connection...").arg(Theme::instance()->appNameGUI()));
|
||||
qDebug() << "Check status.php from statusdialog.";
|
||||
ownCloudInfo::instance()->checkInstallation();
|
||||
} else {
|
||||
// ownCloud is not yet configured.
|
||||
ui->connectLabel->setText( tr("No %1 connection configured.").arg(Theme::instance()->appNameGUI()));
|
||||
ui->_ButtonAdd->setEnabled( false);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::setFolderList( const Folder::Map &folders )
|
||||
{
|
||||
_model->clear();
|
||||
foreach( Folder *f, folders ) {
|
||||
qDebug() << "Folder: " << f;
|
||||
slotAddFolder( f );
|
||||
}
|
||||
|
||||
QModelIndex idx = _model->index(0, 0);
|
||||
if (idx.isValid()) {
|
||||
ui->_folderList->setCurrentIndex(idx);
|
||||
}
|
||||
buttonsSetEnabled();
|
||||
|
||||
}
|
||||
|
||||
// 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 )
|
||||
folderMan->terminateSyncProcess( alias );
|
||||
|
||||
folderMan->slotEnableFolder( alias, !folderEnabled );
|
||||
slotUpdateFolderState (f);
|
||||
// set the button text accordingly.
|
||||
slotFolderActivated( selected );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
slotCheckConnection();
|
||||
}
|
||||
|
||||
void AccountSettings::slotOCInfo( const QString& url, const QString& versionStr, const QString& version, const QString& )
|
||||
{
|
||||
#ifdef Q_OS_WIN32
|
||||
// work around a bug in QDesktopServices on Win32, see i-net
|
||||
QString filePath = url;
|
||||
|
||||
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);
|
||||
ui->connectLabel->setText( tr("Connected to <a href=\"%1\">%1</a>.").arg(url) );
|
||||
ui->connectLabel->setToolTip( 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();
|
||||
|
||||
ui->connectLabel->setText( tr("<p>Failed to connect to %1: <tt>%2</tt></p>").arg(Theme::instance()->appNameGUI()).arg(errStr) );
|
||||
ui->_ButtonAdd->setEnabled( false);
|
||||
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(const QString&, const QString&, const QString&, const QString&)),
|
||||
this, SLOT(slotOCInfo( const QString&, const QString&, const QString&, const QString& )));
|
||||
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
this, SLOT(slotOCInfoFail(QNetworkReply*)));
|
||||
|
||||
}
|
||||
|
||||
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 = ownCloudInfo::instance()->webdavUrl() + QLatin1Char('/') + f->secondPath();
|
||||
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 << " file " << file << ": "<< p1;
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
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:
|
||||
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::Error:
|
||||
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();
|
||||
item->setData( QVariant(false), FolderStatusDelegate::AddProgressSpace );
|
||||
item->setData( QVariant(QString::null), FolderStatusDelegate::SyncProgressOverallString );
|
||||
item->setData( QVariant(QString::null), FolderStatusDelegate::SyncProgressItemString );
|
||||
item->setData( 0, FolderStatusDelegate::SyncProgressOverallPercent );
|
||||
|
||||
ui->_folderList->repaint();
|
||||
_hideProgressTimers.remove(item);
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
send_timer->deleteLater();
|
||||
}
|
||||
|
||||
void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
|
||||
{
|
||||
ui->quotaProgressBar->setEnabled(true);
|
||||
// workaround the label only accepting ints (which may be only 32 bit wide)
|
||||
ui->quotaProgressBar->setMaximum(100);
|
||||
ui->quotaProgressBar->setValue(round(used/(double)total * 100));
|
||||
QString usedStr = Utility::octetsToString(used);
|
||||
QString totalStr = Utility::octetsToString(total);
|
||||
ui->quotaLabel->setText(tr("%1 of %2 in use.").arg(usedStr, totalStr));
|
||||
}
|
||||
|
||||
void AccountSettings::slotIgnoreFilesEditor()
|
||||
{
|
||||
if (_ignoreEditor.isNull()) {
|
||||
_ignoreEditor = new IgnoreListEditor(this);
|
||||
_ignoreEditor->setAttribute( Qt::WA_DeleteOnClose, true );
|
||||
_ignoreEditor->open();
|
||||
} else {
|
||||
Utility::raiseDialog(_ignoreEditor);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountSettings::slotInfoAboutCurrentFolder()
|
||||
{
|
||||
emit(openProgressDialog());
|
||||
}
|
||||
|
||||
AccountSettings::~AccountSettings()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
107
src/mirall/accountsettings.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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"
|
||||
#include "mirall/itemprogressdialog.h"
|
||||
|
||||
class QStandardItemModel;
|
||||
class QModelIndex;
|
||||
class QStandardItem;
|
||||
class QNetworkReply;
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
namespace Ui {
|
||||
class AccountSettings;
|
||||
}
|
||||
|
||||
class FolderMan;
|
||||
class ItemProgressDialog;
|
||||
class IgnoreListEditor;
|
||||
|
||||
class AccountSettings : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit AccountSettings(QWidget *parent = 0);
|
||||
~AccountSettings();
|
||||
|
||||
void setFolderList( const Folder::Map& );
|
||||
void buttonsSetEnabled();
|
||||
void setListWidgetItem(QListWidgetItem* item);
|
||||
|
||||
signals:
|
||||
void folderChanged();
|
||||
void openProgressDialog();
|
||||
void openFolderAlias( const QString& );
|
||||
void infoFolderAlias( const QString& );
|
||||
|
||||
public slots:
|
||||
void slotFolderActivated( const QModelIndex& );
|
||||
void slotOpenOC();
|
||||
void slotUpdateFolderState( Folder* );
|
||||
void slotCheckConnection();
|
||||
void slotOCInfo( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotOCInfoFail( QNetworkReply* );
|
||||
void slotDoubleClicked( const QModelIndex& );
|
||||
void slotFolderOpenAction( const QString& );
|
||||
void slotSetProgress(const QString&, const Progress::Info& progress);
|
||||
void slotProgressProblem(const QString& folder, const Progress::SyncProblem& problem);
|
||||
|
||||
void slotUpdateQuota( qint64,qint64 );
|
||||
void slotIgnoreFilesEditor();
|
||||
|
||||
protected slots:
|
||||
void slotAddFolder();
|
||||
void slotAddFolder( Folder* );
|
||||
void slotEnableCurrentFolder();
|
||||
void slotRemoveCurrentFolder();
|
||||
void slotInfoAboutCurrentFolder();
|
||||
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& );
|
||||
|
||||
Ui::AccountSettings *ui;
|
||||
QPointer<ItemProgressDialog> _fileItemDialog;
|
||||
QPointer<IgnoreListEditor> _ignoreEditor;
|
||||
QStandardItemModel *_model;
|
||||
QListWidgetItem *_item;
|
||||
QUrl _OCUrl;
|
||||
QHash<QStandardItem*, QTimer*> _hideProgressTimers;
|
||||
QString _kindContext;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // ACCOUNTSETTINGS_H
|
||||
163
src/mirall/accountsettings.ui
Normal file
@@ -0,0 +1,163 @@
|
||||
<?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>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QGroupBox" name="syncStatusGroupBox">
|
||||
<property name="title">
|
||||
<string>Sync Status</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>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_ButtonInfo">
|
||||
<property name="text">
|
||||
<string>Info...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="quotaLabel">
|
||||
<property name="text">
|
||||
<string>Retrieving usage information...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<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,17 +16,18 @@
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <QApplication>
|
||||
#include <QSystemTrayIcon>
|
||||
#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/connectionvalidator.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
@@ -37,13 +38,13 @@ class QNetworkReply;
|
||||
|
||||
namespace Mirall {
|
||||
class Theme;
|
||||
class Folder;
|
||||
class FolderWatcher;
|
||||
class FolderWizard;
|
||||
class StatusDialog;
|
||||
class OwncloudSetupWizard;
|
||||
class ownCloudInfo;
|
||||
class SslErrorDialog;
|
||||
class UpdateDetector;
|
||||
class SettingsDialog;
|
||||
class ItemProgressDialog;
|
||||
|
||||
class Application : public SharedTools::QtSingleApplication
|
||||
{
|
||||
@@ -55,84 +56,92 @@ 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 enterNextLogFile();
|
||||
bool checkConfigExists(bool openSettings);
|
||||
|
||||
//folders have to be disabled while making config changes
|
||||
void computeOverallSyncStatus();
|
||||
|
||||
// reimplemented
|
||||
#if defined(Q_WS_WIN)
|
||||
bool winEventFilter( MSG * message, long * result );
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void folderRemoved();
|
||||
void folderStateChanged(Folder*);
|
||||
|
||||
protected slots:
|
||||
void slotFoldersChanged();
|
||||
void slotSettings();
|
||||
void slotItemProgressDialog();
|
||||
void slotParseOptions( const QString& );
|
||||
void slotShowTrayMessage(const QString&, const QString&);
|
||||
void slotShowOptionalTrayMessage(const QString&, const QString&);
|
||||
void slotShowGuiMessage(const QString& title, const QString& message);
|
||||
void slotCheckConnection();
|
||||
void slotConnectionValidatorResult(ConnectionValidator::Status);
|
||||
void slotSyncStateChange( const QString& );
|
||||
void slotTrayClicked( QSystemTrayIcon::ActivationReason );
|
||||
void slotFolderOpenAction(const QString & );
|
||||
void slotOpenOwnCloud();
|
||||
void 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 slotStartUpdateDetector();
|
||||
|
||||
void slotSetupProxy();
|
||||
void slotRefreshQuotaDisplay( qint64 total, qint64 used );
|
||||
void slotUseMonoIconsChanged( bool );
|
||||
void slotUpdateProgress(const QString&, const Progress::Info&);
|
||||
void slotProgressSyncProblem(const QString& folder, const Progress::SyncProblem &problem);
|
||||
void slotDisplayIdle();
|
||||
void slotHelp();
|
||||
void slotCredentialsFetched();
|
||||
private:
|
||||
void setHelp();
|
||||
void raiseDialog( QWidget* );
|
||||
void rebuildRecentMenus();
|
||||
void runValidator();
|
||||
|
||||
// configuration file -> folder
|
||||
QSystemTrayIcon *_tray;
|
||||
QAction *_actionQuit;
|
||||
QAction *_actionAddFolder;
|
||||
QAction *_actionOpenStatus;
|
||||
QAction *_actionConfigure;
|
||||
Systray *_tray;
|
||||
QAction *_actionOpenoC;
|
||||
QAction *_actionConfigureProxy;
|
||||
QAction *_actionAbout;
|
||||
QAction *_actionSettings;
|
||||
QAction *_actionQuota;
|
||||
QAction *_actionStatus;
|
||||
QAction *_actionRecent;
|
||||
QAction *_actionHelp;
|
||||
QAction *_actionQuit;
|
||||
|
||||
#if QT_VERSION >= 0x040700
|
||||
QNetworkConfigurationManager *_networkMgr;
|
||||
#endif
|
||||
|
||||
FolderWizard *_folderWizard;
|
||||
OwncloudSetupWizard *_owncloudSetupWizard;
|
||||
QPointer<FolderWizard> _folderWizard;
|
||||
SslErrorDialog *_sslErrorDialog;
|
||||
ConnectionValidator *_conValidator;
|
||||
|
||||
// tray's menu
|
||||
QMenu *_contextMenu;
|
||||
StatusDialog *_statusDialog;
|
||||
FileItemDialog *_fileItemDialog;
|
||||
QMenu *_recentActionsMenu;
|
||||
|
||||
FolderMan *_folderMan;
|
||||
Theme *_theme;
|
||||
QSignalMapper *_folderOpenActionMapper;
|
||||
UpdateDetector *_updateDetector;
|
||||
QMap<QString, QString> _overallStatusStrings;
|
||||
LogBrowser *_logBrowser;
|
||||
QPointer<SettingsDialog> _settingsDialog;
|
||||
QPointer<ItemProgressDialog> _progressDialog;
|
||||
|
||||
QString _logFile;
|
||||
QString _logDirectory;
|
||||
|
||||
int _logExpire;
|
||||
bool _showLogWindow;
|
||||
bool _logFlush;
|
||||
bool _helpOnly;
|
||||
|
||||
166
src/mirall/connectionvalidator.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 <QtCore>
|
||||
|
||||
#include "mirall/connectionvalidator.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
ConnectionValidator::ConnectionValidator(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ConnectionValidator::ConnectionValidator(const QString& connection, QObject *parent)
|
||||
: QObject(parent),
|
||||
_connection(connection)
|
||||
{
|
||||
ownCloudInfo::instance()->setCustomConfigHandle(_connection);
|
||||
}
|
||||
|
||||
QStringList ConnectionValidator::errors() const
|
||||
{
|
||||
return _errors;
|
||||
}
|
||||
|
||||
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();
|
||||
} else {
|
||||
emit connectionResult( NotConfigured );
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotStatusFound( const QString& url, const QString& versionStr, const QString& version, const QString& /*edition*/)
|
||||
{
|
||||
// status.php was found.
|
||||
qDebug() << "** Application: ownCloud found: " << url << " with version " << versionStr << "(" << version << ")";
|
||||
// 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>"));
|
||||
emit connectionResult( ServerVersionMismatch );
|
||||
return;
|
||||
}
|
||||
|
||||
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*)));
|
||||
|
||||
_errors.append( reply->errorString() );
|
||||
emit connectionResult( StatusNotFound );
|
||||
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotCheckAuthentication()
|
||||
{
|
||||
connect( ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this, SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
qDebug() << "# checking for authentication settings.";
|
||||
ownCloudInfo::instance()->getWebDAVPath(QLatin1String("/") ); // this call needs to be authenticated.
|
||||
// simply GET the webdav root, will fail if credentials are wrong.
|
||||
// continue in slotAuthCheck here :-)
|
||||
}
|
||||
|
||||
void ConnectionValidator::slotAuthCheck( const QString& ,QNetworkReply *reply )
|
||||
{
|
||||
Status stat = Connected;
|
||||
|
||||
if( reply->error() == QNetworkReply::AuthenticationRequiredError ||
|
||||
reply->error() == QNetworkReply::OperationCanceledError ) { // returned if the user is wrong.
|
||||
qDebug() << "******** Password is wrong!";
|
||||
_errors << "The provided credentials are wrong.";
|
||||
stat = CredentialsWrong;
|
||||
}
|
||||
|
||||
// disconnect from ownCloud Info signals
|
||||
disconnect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
|
||||
|
||||
emit connectionResult( stat );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
71
src/mirall/connectionvalidator.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 CONNECTIONVALIDATOR_H
|
||||
#define CONNECTIONVALIDATOR_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
class QNetworkReply;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class ConnectionValidator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConnectionValidator(QObject *parent = 0);
|
||||
explicit ConnectionValidator(const QString& connection, QObject *parent = 0);
|
||||
|
||||
enum Status {
|
||||
Undefined,
|
||||
Connected,
|
||||
NotConfigured,
|
||||
ServerVersionMismatch,
|
||||
CredentialsTooManyAttempts,
|
||||
CredentialError,
|
||||
CredentialsUserCanceled,
|
||||
CredentialsWrong,
|
||||
StatusNotFound
|
||||
|
||||
};
|
||||
|
||||
QStringList errors() const;
|
||||
|
||||
void checkConnection();
|
||||
|
||||
QString statusString( Status ) const;
|
||||
|
||||
signals:
|
||||
void connectionResult( ConnectionValidator::Status );
|
||||
// void connectionAvailable();
|
||||
// void connectionFailed();
|
||||
|
||||
public slots:
|
||||
|
||||
protected slots:
|
||||
void slotStatusFound( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotNoStatusFound(QNetworkReply *);
|
||||
|
||||
void slotCheckAuthentication();
|
||||
void slotAuthCheck( const QString& ,QNetworkReply * );
|
||||
|
||||
private:
|
||||
QStringList _errors;
|
||||
QString _connection;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CONNECTIONVALIDATOR_H
|
||||
@@ -1,213 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CreateAnOwncloudPage</class>
|
||||
<widget class="QWidget" name="CreateAnOwncloudPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>539</width>
|
||||
<height>346</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../mirall.qrc">:/mirall/resources/mirall-32.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Create a new ownCloud</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_17">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Where do you want to create your ownCloud?</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="createLocalRadioBtn">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>create ownCloud on this computer</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#585858;">installs the ownCloud on this computer. Other people will not be able to access your data by default.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="createPerFTPRadioBtn">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
<underline>false</underline>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>create ownCloud on my internet domain</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>22</width>
|
||||
<height>13</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'Sans Serif'; font-size:11pt; font-weight:400; font-style:normal;">
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#585858;">installs ownCloud on a domain you control. You need the FTP credentials for the installation.</span></p>
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; color:#585858;"></p>
|
||||
<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#585858;">This ownCloud will be accessible from the internet.</span></p></body></html></string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::AutoText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Domain:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>myDomainEdit</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QLineEdit" name="myDomainEdit">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>388</width>
|
||||
<height>72</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../mirall.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
*
|
||||
* 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 "mirall/csyncfolder.h"
|
||||
#include "mirall/csyncthread.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
|
||||
#include <csync.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QMutexLocker>
|
||||
#include <QThread>
|
||||
#include <QStringList>
|
||||
#include <QTextStream>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
CSyncFolder::CSyncFolder(const QString &alias,
|
||||
const QString &path,
|
||||
const QString &secondPath,
|
||||
QObject *parent)
|
||||
: Folder(alias, path, secondPath, parent)
|
||||
, _csync(0)
|
||||
, _thread(0)
|
||||
, _csyncError(false)
|
||||
|
||||
{
|
||||
}
|
||||
|
||||
CSyncFolder::~CSyncFolder()
|
||||
{
|
||||
}
|
||||
|
||||
bool CSyncFolder::isBusy() const
|
||||
{
|
||||
return (_csync && _thread && _thread->isRunning() );
|
||||
}
|
||||
|
||||
void CSyncFolder::startSync(const QStringList &pathList)
|
||||
{
|
||||
if (_thread && _thread->isRunning()) {
|
||||
qCritical() << "* ERROR csync is still running and new sync requested.";
|
||||
return;
|
||||
}
|
||||
delete _csync;
|
||||
delete _thread;
|
||||
_errors.clear();
|
||||
_csyncError = false;
|
||||
_syncResult.setStatus( SyncResult::SyncRunning );
|
||||
emit syncStateChange();
|
||||
|
||||
_thread = new QThread(this);
|
||||
_csync = new CSyncThread( path(), secondPath() );
|
||||
connect(_csync, SIGNAL(started()), SLOT(slotCSyncStarted()), Qt::QueuedConnection);
|
||||
connect(_csync, SIGNAL(finished()), SLOT(slotCSyncFinished()), Qt::QueuedConnection);
|
||||
connect(_csync, SIGNAL(csyncError(QString)), SLOT(slotCSyncError(QString)), Qt::QueuedConnection);
|
||||
_csync->moveToThread(_thread);
|
||||
_thread->start();
|
||||
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void CSyncFolder::slotTerminateSync()
|
||||
{
|
||||
if( _thread ) {
|
||||
_thread->terminate();
|
||||
}
|
||||
}
|
||||
|
||||
void CSyncFolder::slotCSyncStarted()
|
||||
{
|
||||
qDebug() << " * csync thread started";
|
||||
emit syncStarted();
|
||||
}
|
||||
|
||||
void CSyncFolder::slotCSyncFinished()
|
||||
{
|
||||
SyncResult res(SyncResult::Success);
|
||||
if( _csyncError ) {
|
||||
res.setStatus( SyncResult::Error );
|
||||
res.setErrorString( _errors.join(QLatin1String("\n")));
|
||||
}
|
||||
emit syncFinished( res );
|
||||
}
|
||||
|
||||
void CSyncFolder::slotCSyncError( const QString& errorStr )
|
||||
{
|
||||
_errors.append( errorStr );
|
||||
_csyncError = true;
|
||||
}
|
||||
|
||||
} // ns
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
@@ -43,30 +43,14 @@
|
||||
namespace Mirall {
|
||||
|
||||
/* static variables to hold the credentials */
|
||||
QString CSyncThread::_user;
|
||||
QString CSyncThread::_passwd;
|
||||
QNetworkProxy CSyncThread::_proxy;
|
||||
|
||||
QString CSyncThread::_csyncConfigDir; // to be able to remove the lock file.
|
||||
|
||||
QMutex CSyncThread::_mutex;
|
||||
QMutex CSyncThread::_syncMutex;
|
||||
|
||||
void csyncLogCatcher(CSYNC *ctx,
|
||||
int verbosity,
|
||||
const char *function,
|
||||
const char *buffer,
|
||||
void *userdata)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
CSyncThread::CSyncThread(const QString &source, const QString &target)
|
||||
: _source(source)
|
||||
, _target(target)
|
||||
|
||||
CSyncThread::CSyncThread(CSYNC *csync)
|
||||
{
|
||||
_mutex.lock();
|
||||
if( ! _source.endsWith(QLatin1Char('/'))) _source.append(QLatin1Char('/'));
|
||||
_csync_ctx = csync;
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
@@ -75,6 +59,8 @@ 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 errStr;
|
||||
@@ -122,9 +108,7 @@ QString CSyncThread::csyncErrorToString( CSYNC_ERROR_CODE err, const char *errSt
|
||||
errStr = tr("CSync processing step propagate failed.");
|
||||
break;
|
||||
case CSYNC_ERR_ACCESS_FAILED:
|
||||
errStr = tr("<p>The target directory %1 does not exist.</p><p>Please check the sync setup.</p>").arg(_target);
|
||||
// this is critical. The database has to be removed.
|
||||
emit wipeDb();
|
||||
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:
|
||||
@@ -181,26 +165,6 @@ QString CSyncThread::csyncErrorToString( CSYNC_ERROR_CODE err, const char *errSt
|
||||
|
||||
}
|
||||
|
||||
const char* CSyncThread::proxyTypeToCStr(QNetworkProxy::ProxyType type)
|
||||
{
|
||||
switch (type) {
|
||||
case QNetworkProxy::NoProxy:
|
||||
return "NoProxy";
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
return "DefaultProxy";
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
return "Socks5Proxy";
|
||||
case QNetworkProxy::HttpProxy:
|
||||
return "HttpProxy";
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
return "HttpCachingProxy";
|
||||
case QNetworkProxy::FtpCachingProxy:
|
||||
return "FtpCachingProxy";
|
||||
default:
|
||||
return "NoProxy";
|
||||
}
|
||||
}
|
||||
|
||||
int CSyncThread::treewalkLocal( TREE_WALK_FILE* file, void *data )
|
||||
{
|
||||
return static_cast<CSyncThread*>(data)->treewalkFile( file, false );
|
||||
@@ -228,6 +192,11 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
|
||||
int re = 0;
|
||||
|
||||
if (file->instruction != CSYNC_INSTRUCTION_IGNORE
|
||||
&& file->instruction != CSYNC_INSTRUCTION_REMOVE) {
|
||||
_hasFiles = true;
|
||||
}
|
||||
|
||||
switch(file->instruction) {
|
||||
case CSYNC_INSTRUCTION_NONE:
|
||||
case CSYNC_INSTRUCTION_IGNORE:
|
||||
@@ -264,6 +233,20 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
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);
|
||||
@@ -274,7 +257,7 @@ int CSyncThread::treewalkFile( TREE_WALK_FILE *file, bool remote )
|
||||
|
||||
int CSyncThread::treewalkError(TREE_WALK_FILE* file)
|
||||
{
|
||||
SyncFileItem item;
|
||||
SyncFileItem item; // only used for search.
|
||||
item._file= QString::fromUtf8(file->path);
|
||||
int indx = _syncedItems.indexOf(item);
|
||||
|
||||
@@ -282,10 +265,11 @@ int CSyncThread::treewalkError(TREE_WALK_FILE* file)
|
||||
return 0;
|
||||
|
||||
if( file &&
|
||||
file->instruction == CSYNC_INSTRUCTION_STAT_ERROR ||
|
||||
file->instruction == CSYNC_INSTRUCTION_ERROR ) {
|
||||
(file->instruction == CSYNC_INSTRUCTION_STAT_ERROR ||
|
||||
file->instruction == CSYNC_INSTRUCTION_ERROR) ) {
|
||||
_mutex.lock();
|
||||
_syncedItems[indx]._instruction = file->instruction;
|
||||
_syncedItems[indx]._errorString = QString::fromUtf8(file->error_string);
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
@@ -293,20 +277,21 @@ int CSyncThread::treewalkError(TREE_WALK_FILE* file)
|
||||
}
|
||||
|
||||
struct CSyncRunScopeHelper {
|
||||
CSyncRunScopeHelper(CSYNC *_ctx, CSyncThread *_parent)
|
||||
: ctx(_ctx), parent(_parent)
|
||||
CSyncRunScopeHelper(CSYNC *ctx, CSyncThread *parent)
|
||||
: _ctx(ctx), _parent(parent)
|
||||
{
|
||||
t.start();
|
||||
_t.start();
|
||||
}
|
||||
~CSyncRunScopeHelper() {
|
||||
csync_destroy(ctx);
|
||||
csync_commit(_ctx);
|
||||
|
||||
qDebug() << "CSync run took " << t.elapsed() << " Milliseconds";
|
||||
emit(parent->finished());
|
||||
qDebug() << "CSync run took " << _t.elapsed() << " Milliseconds";
|
||||
emit(_parent->finished());
|
||||
_parent->_syncMutex.unlock();
|
||||
}
|
||||
CSYNC *ctx;
|
||||
QTime t;
|
||||
CSyncThread *parent;
|
||||
CSYNC *_ctx;
|
||||
QTime _t;
|
||||
CSyncThread *_parent;
|
||||
};
|
||||
|
||||
void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
|
||||
@@ -326,194 +311,195 @@ void CSyncThread::handleSyncError(CSYNC *ctx, const char *state) {
|
||||
|
||||
void CSyncThread::startSync()
|
||||
{
|
||||
if (!_syncMutex.tryLock()) {
|
||||
qDebug() << Q_FUNC_INFO << "WARNING: Another sync seems to be running. Not starting a new one.";
|
||||
return;
|
||||
}
|
||||
|
||||
if( ! _csync_ctx ) {
|
||||
qDebug() << "XXXXXXXXXXXXXXXX FAIL: do not have csync_ctx!";
|
||||
}
|
||||
qDebug() << Q_FUNC_INFO << "Sync started";
|
||||
|
||||
static int syncsRunning = 0;
|
||||
syncsRunning++;
|
||||
assert(syncsRunning == 1);
|
||||
|
||||
qDebug() << "starting to sync " << qApp->thread() << QThread::currentThread();
|
||||
CSYNC *csync;
|
||||
int proxyPort = _proxy.port();
|
||||
|
||||
_mutex.lock();
|
||||
_syncedItems.clear();
|
||||
_needsUpdate = false;
|
||||
_mutex.unlock();
|
||||
|
||||
if( csync_create(&csync,
|
||||
_source.toUtf8().data(),
|
||||
_target.toUtf8().data()) < 0 ) {
|
||||
emit csyncError( tr("CSync create failed.") );
|
||||
}
|
||||
|
||||
MirallConfigFile cfg;
|
||||
csync_set_config_dir( csync, cfg.configPath().toUtf8() );
|
||||
|
||||
_mutex.lock();
|
||||
_csyncConfigDir = cfg.configPath();
|
||||
_mutex.unlock();
|
||||
|
||||
csync_enable_conflictcopys(csync);
|
||||
QString excludeList = cfg.excludeFile();
|
||||
if( !excludeList.isEmpty() ) {
|
||||
qDebug() << "==== added CSync exclude List: " << excludeList.toUtf8();
|
||||
csync_add_exclude_list( csync, excludeList.toUtf8() );
|
||||
}
|
||||
|
||||
// cleans up behind us and emits finished() to ease error handling
|
||||
CSyncRunScopeHelper helper(csync, this);
|
||||
CSyncRunScopeHelper helper(_csync_ctx, this);
|
||||
|
||||
csync_set_userdata(csync, this);
|
||||
// maybe move this somewhere else where it can influence a running sync?
|
||||
MirallConfigFile cfg;
|
||||
|
||||
csync_set_log_callback( csync, csyncLogCatcher );
|
||||
csync_set_auth_callback( csync, getauth );
|
||||
csync_set_progress_callback( csync, progress );
|
||||
|
||||
if( csync_init(csync) < 0 ) {
|
||||
handleSyncError(csync, "csync_init");
|
||||
return;
|
||||
int downloadLimit = 0;
|
||||
if (cfg.useDownloadLimit()) {
|
||||
downloadLimit = cfg.downloadLimit() * 1000;
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "bandwidth_limit_download", &downloadLimit);
|
||||
|
||||
int uploadLimit = -75; // 75%
|
||||
int useUpLimit = cfg.useUploadLimit();
|
||||
if ( useUpLimit >= 1) {
|
||||
uploadLimit = cfg.uploadLimit() * 1000;
|
||||
} else if (useUpLimit == 0) {
|
||||
uploadLimit = 0;
|
||||
}
|
||||
csync_set_module_property(_csync_ctx, "bandwidth_limit_upload", &uploadLimit);
|
||||
|
||||
csync_set_progress_callback( _csync_ctx, cb_progress );
|
||||
|
||||
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
|
||||
csync_set_userdata(_csync_ctx, this);
|
||||
|
||||
// TODO: This should be a part of this method, but we don't have
|
||||
// any way to get "session_key" module property from csync. Had we
|
||||
// have it, then we could keep this code and remove it from
|
||||
// AbstractCredentials implementations.
|
||||
cfg.getCredentials()->syncContextPreStart(_csync_ctx);
|
||||
// if (_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 );
|
||||
|
||||
|
||||
// set module properties, mainly the proxy information.
|
||||
// do not use QLatin1String here because that has to be real const char* for C.
|
||||
csync_set_log_verbosity(csync, 11);
|
||||
csync_set_module_property(csync, "csync_context", csync);
|
||||
csync_set_module_property(csync, "proxy_type", (char*) proxyTypeToCStr(_proxy.type()) );
|
||||
csync_set_module_property(csync, "proxy_host", _proxy.hostName().toUtf8().data() );
|
||||
csync_set_module_property(csync, "proxy_port", &proxyPort );
|
||||
csync_set_module_property(csync, "proxy_user", _proxy.user().toUtf8().data() );
|
||||
csync_set_module_property(csync, "proxy_pwd" , _proxy.password().toUtf8().data() );
|
||||
|
||||
qDebug() << "#### Update start #################################################### >>";
|
||||
if( csync_update(csync) < 0 ) {
|
||||
handleSyncError(csync, "csync_update");
|
||||
if( csync_update(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "csync_update");
|
||||
return;
|
||||
}
|
||||
qDebug() << "<<#### Update end ###########################################################";
|
||||
|
||||
if( csync_reconcile(csync) < 0 ) {
|
||||
handleSyncError(csync, "cysnc_reconcile");
|
||||
if( csync_reconcile(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "csync_reconcile");
|
||||
return;
|
||||
}
|
||||
|
||||
_hasFiles = false;
|
||||
bool walkOk = true;
|
||||
if( csync_walk_local_tree(csync, &treewalkLocal, 0) < 0 ) {
|
||||
if( csync_walk_local_tree(_csync_ctx, &treewalkLocal, 0) < 0 ) {
|
||||
qDebug() << "Error in local treewalk.";
|
||||
walkOk = false;
|
||||
}
|
||||
if( walkOk && csync_walk_remote_tree(csync, &treewalkRemote, 0) < 0 ) {
|
||||
if( walkOk && csync_walk_remote_tree(_csync_ctx, &treewalkRemote, 0) < 0 ) {
|
||||
qDebug() << "Error in remote treewalk.";
|
||||
}
|
||||
|
||||
if (!_hasFiles && !_syncedItems.isEmpty()) {
|
||||
qDebug() << Q_FUNC_INFO << "All the files are going to be removed, asking the user";
|
||||
bool cancel = true;
|
||||
emit aboutToRemoveAllFiles(_syncedItems.first()._dir, &cancel);
|
||||
if (cancel) {
|
||||
qDebug() << Q_FUNC_INFO << "Abort sync";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_needsUpdate)
|
||||
emit(started());
|
||||
|
||||
if( csync_propagate(csync) < 0 ) {
|
||||
handleSyncError(csync, "cysnc_reconcile");
|
||||
if( csync_propagate(_csync_ctx) < 0 ) {
|
||||
handleSyncError(_csync_ctx, "cysnc_reconcile");
|
||||
return;
|
||||
}
|
||||
|
||||
if( walkOk ) {
|
||||
if( csync_walk_local_tree(csync, &walkFinalize, 0) < 0 ||
|
||||
csync_walk_remote_tree( csync, &walkFinalize, 0 ) < 0 ) {
|
||||
qDebug() << "Error in finalize treewalk.";
|
||||
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);
|
||||
}
|
||||
}
|
||||
syncsRunning--;
|
||||
qDebug() << Q_FUNC_INFO << "Sync finished";
|
||||
}
|
||||
|
||||
void CSyncThread::setConnectionDetails( const QString &user, const QString &passwd, const QNetworkProxy &proxy )
|
||||
Progress::Kind CSyncThread::csyncToProgressKind( enum csync_notify_type_e kind )
|
||||
{
|
||||
_mutex.lock();
|
||||
_user = user;
|
||||
_passwd = passwd;
|
||||
_proxy = proxy;
|
||||
_mutex.unlock();
|
||||
}
|
||||
Progress::Kind pKind = Progress::Invalid;
|
||||
|
||||
QString CSyncThread::csyncConfigDir()
|
||||
{
|
||||
return _csyncConfigDir;
|
||||
}
|
||||
|
||||
int CSyncThread::getauth(const char *prompt,
|
||||
char *buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void *userdata
|
||||
)
|
||||
{
|
||||
int re = 0;
|
||||
|
||||
QString qPrompt = QString::fromLatin1( prompt ).trimmed();
|
||||
|
||||
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, _passwd.toUtf8().constData(), len );
|
||||
} else {
|
||||
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.
|
||||
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 = ownCloudInfo::instance()->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;
|
||||
}
|
||||
switch(kind) {
|
||||
case CSYNC_NOTIFY_INVALID:
|
||||
pKind = Progress::Invalid;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_SYNC_SEQUENCE:
|
||||
pKind = Progress::StartSync;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_DOWNLOAD:
|
||||
pKind = Progress::StartDownload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_UPLOAD:
|
||||
pKind = Progress::StartUpload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_PROGRESS:
|
||||
pKind = Progress::Context;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_DOWNLOAD:
|
||||
pKind = Progress::EndDownload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_UPLOAD:
|
||||
pKind = Progress::EndUpload;
|
||||
break;
|
||||
case CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE:
|
||||
pKind = Progress::EndSync;
|
||||
break;
|
||||
case CSYNC_NOTIFY_START_DELETE:
|
||||
pKind = Progress::StartDelete;
|
||||
break;
|
||||
case CSYNC_NOTIFY_END_DELETE:
|
||||
pKind = Progress::EndDelete;
|
||||
break;
|
||||
case CSYNC_NOTIFY_ERROR:
|
||||
pKind = Progress::Error;
|
||||
break;
|
||||
default:
|
||||
pKind = Progress::Invalid;
|
||||
break;
|
||||
}
|
||||
return re;
|
||||
return pKind;
|
||||
}
|
||||
|
||||
|
||||
void CSyncThread::progress(const char *remote_url, enum csync_notify_type_e kind,
|
||||
long long o1, long long o2, void *userdata)
|
||||
void CSyncThread::cb_progress( CSYNC_PROGRESS *progress, void *userdata )
|
||||
{
|
||||
(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);
|
||||
if( !progress ) {
|
||||
qDebug() << "No progress block in progress callback found!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
if( !userdata ) {
|
||||
qDebug() << "No thread given in progress callback!";
|
||||
return;
|
||||
}
|
||||
Progress::Info pInfo;
|
||||
CSyncThread *thread = static_cast<CSyncThread*>(userdata);
|
||||
|
||||
pInfo.kind = thread->csyncToProgressKind( progress->kind );
|
||||
pInfo.current_file = QUrl::fromEncoded( progress->path ).toString();
|
||||
pInfo.file_size = progress->file_size;
|
||||
pInfo.current_file_bytes = progress->curr_bytes;
|
||||
|
||||
pInfo.overall_file_count = progress->overall_file_count;
|
||||
pInfo.current_file_no = progress->current_file_no;
|
||||
pInfo.overall_transmission_size = progress->overall_transmission_size;
|
||||
pInfo.overall_current_bytes = progress->current_overall_bytes;
|
||||
pInfo.timestamp = QDateTime::currentDateTime();
|
||||
|
||||
// Connect to something in folder!
|
||||
thread->transmissionProgress( pInfo );
|
||||
|
||||
}
|
||||
|
||||
} // ns Mirall
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
#include <QThread>
|
||||
#include <QString>
|
||||
#include <QNetworkProxy>
|
||||
#include <QNetworkCookie>
|
||||
|
||||
#include <csync.h>
|
||||
|
||||
#include "mirall/syncfileitem.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
|
||||
class QProcess;
|
||||
|
||||
@@ -35,14 +37,10 @@ class CSyncThread : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CSyncThread(const QString &source, const QString &target);
|
||||
CSyncThread(CSYNC *);
|
||||
~CSyncThread();
|
||||
|
||||
static void setConnectionDetails( const QString&, const QString&, const QNetworkProxy& );
|
||||
static QString csyncConfigDir();
|
||||
|
||||
const char* proxyTypeToCStr(QNetworkProxy::ProxyType);
|
||||
QString csyncErrorToString( CSYNC_ERROR_CODE, const char * );
|
||||
static QString csyncErrorToString( CSYNC_ERROR_CODE, const char * );
|
||||
|
||||
Q_INVOKABLE void startSync();
|
||||
|
||||
@@ -54,48 +52,40 @@ signals:
|
||||
void csyncUnavailable();
|
||||
void treeWalkResult(const SyncFileItemVector&);
|
||||
|
||||
void transmissionProgress( const Progress::Info& progress );
|
||||
void csyncStateDbFile( const QString& );
|
||||
void wipeDb();
|
||||
|
||||
void finished();
|
||||
void started();
|
||||
|
||||
void aboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel);
|
||||
|
||||
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);
|
||||
|
||||
static void cb_progress( CSYNC_PROGRESS *progress, void *userdata );
|
||||
|
||||
static int treewalkLocal( TREE_WALK_FILE*, void *);
|
||||
static int treewalkRemote( TREE_WALK_FILE*, void *);
|
||||
int treewalkFile( TREE_WALK_FILE*, bool );
|
||||
int treewalkError( TREE_WALK_FILE* );
|
||||
|
||||
Progress::Kind csyncToProgressKind( enum csync_notify_type_e kind );
|
||||
static int walkFinalize(TREE_WALK_FILE*, void* );
|
||||
|
||||
static int getauth(const char *prompt,
|
||||
char *buf,
|
||||
size_t len,
|
||||
int echo,
|
||||
int verify,
|
||||
void *userdata
|
||||
);
|
||||
|
||||
|
||||
static QMutex _mutex;
|
||||
static QString _user;
|
||||
static QString _passwd;
|
||||
static QNetworkProxy _proxy;
|
||||
|
||||
static QString _csyncConfigDir;
|
||||
|
||||
static QMutex _syncMutex;
|
||||
SyncFileItemVector _syncedItems;
|
||||
|
||||
QString _source;
|
||||
QString _target;
|
||||
CSYNC *_csync_ctx;
|
||||
bool _needsUpdate;
|
||||
|
||||
friend class CSyncRunScopeHelper;
|
||||
bool _hasFiles; // true if there is at least one file that is not ignored or removed
|
||||
|
||||
friend struct CSyncRunScopeHelper;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,295 +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"
|
||||
|
||||
#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()));
|
||||
|
||||
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::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);
|
||||
|
||||
_timelabel->setText(tr("%1 (finished %2 sec. ago)").arg(_folderMessage).arg(secs));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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("Up");
|
||||
if( item._dir == SyncFileItem::Down ) dir = tr("Down");
|
||||
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,65 +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();
|
||||
|
||||
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
|
||||
@@ -25,7 +25,7 @@ class FileUtils
|
||||
public:
|
||||
enum SubFolderListOption {
|
||||
SubFolderNoOptions = 0x0,
|
||||
SubFolderRecursive = 0x1,
|
||||
SubFolderRecursive = 0x1
|
||||
};
|
||||
Q_DECLARE_FLAGS(SubFolderListOptions, SubFolderListOption)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.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
|
||||
@@ -17,66 +19,108 @@
|
||||
#include "mirall/folderwatcher.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/logger.h"
|
||||
#include "mirall/owncloudinfo.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "folderman.h"
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
void csyncLogCatcher(CSYNC */*ctx*/,
|
||||
int /*verbosity*/,
|
||||
const char */*function*/,
|
||||
const char *buffer,
|
||||
void */*userdata*/)
|
||||
{
|
||||
Logger::instance()->csyncLog( QString::fromUtf8(buffer) );
|
||||
}
|
||||
|
||||
Folder::Folder(const QString &alias, const QString &path, const QString& secondPath, QObject *parent)
|
||||
: QObject(parent),
|
||||
_errorCount(0),
|
||||
_path(path),
|
||||
_secondPath(secondPath),
|
||||
_pollTimer(new QTimer(this)),
|
||||
_alias(alias),
|
||||
_onlyOnlineEnabled(false),
|
||||
_onlyThisLANEnabled(false),
|
||||
_online(false),
|
||||
_enabled(true)
|
||||
: QObject(parent)
|
||||
, _path(path)
|
||||
, _secondPath(secondPath)
|
||||
, _alias(alias)
|
||||
, _enabled(true)
|
||||
, _thread(0)
|
||||
, _csync(0)
|
||||
, _csyncError(false)
|
||||
, _csyncUnavail(false)
|
||||
, _csync_ctx(0)
|
||||
{
|
||||
qsrand(QTime::currentTime().msec());
|
||||
MirallConfigFile cfgFile;
|
||||
_timeSinceLastSync.start();
|
||||
|
||||
_pollTimer->setSingleShot(true);
|
||||
int polltime = cfgFile.remotePollInterval()- 2000 + (int)( 4000.0*qrand()/(RAND_MAX+1.0));
|
||||
qDebug() << "setting remote poll timer interval to" << polltime << "msec for folder " << alias;
|
||||
_pollTimer->setInterval( polltime );
|
||||
|
||||
QObject::connect(_pollTimer, SIGNAL(timeout()), this, SLOT(slotPollTimerTimeout()));
|
||||
_pollTimer->start();
|
||||
|
||||
_watcher = new Mirall::FolderWatcher(path, this);
|
||||
_watcher = new FolderWatcher(path, this);
|
||||
|
||||
MirallConfigFile cfg;
|
||||
|
||||
_watcher->setIgnoreListFile( cfg.excludeFile() );
|
||||
_watcher->addIgnoreListFile( cfg.excludeFile(MirallConfigFile::SystemScope) );
|
||||
_watcher->addIgnoreListFile( cfg.excludeFile(MirallConfigFile::UserScope) );
|
||||
|
||||
QObject::connect(_watcher, SIGNAL(folderChanged(const QStringList &)),
|
||||
SLOT(slotChanged(const QStringList &)));
|
||||
QObject::connect(this, SIGNAL(syncStarted()),
|
||||
SLOT(slotSyncStarted()));
|
||||
QObject::connect(this, SIGNAL(syncFinished(const SyncResult &)),
|
||||
SLOT(slotSyncFinished(const SyncResult &)));
|
||||
|
||||
#if QT_VERSION >= 0x040700
|
||||
_online = _networkMgr.isOnline();
|
||||
QObject::connect(&_networkMgr, SIGNAL(onlineStateChanged(bool)), SLOT(slotOnlineChanged(bool)));
|
||||
#else
|
||||
_online = true;
|
||||
#endif
|
||||
|
||||
_syncResult.setStatus( SyncResult::NotYetStarted );
|
||||
|
||||
// check if the local path exists
|
||||
checkLocalPath();
|
||||
|
||||
int polltime = cfg.remotePollInterval();
|
||||
qDebug() << "setting remote poll timer interval to" << polltime << "msec";
|
||||
_pollTimer.setInterval( polltime );
|
||||
QObject::connect(&_pollTimer, SIGNAL(timeout()), this, SLOT(slotPollTimerTimeout()));
|
||||
_pollTimer.start();
|
||||
|
||||
_syncResult.setFolder(alias);
|
||||
}
|
||||
|
||||
bool Folder::init()
|
||||
{
|
||||
QString url = Utility::toCSyncScheme(ownCloudInfo::instance()->webdavUrl() + secondPath());
|
||||
QString localpath = path();
|
||||
|
||||
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url.toUtf8().data() ) < 0 ) {
|
||||
qDebug() << "Unable to create csync-context!";
|
||||
slotCSyncError(tr("Unable to create csync-context"));
|
||||
_csync_ctx = 0;
|
||||
} else {
|
||||
csync_set_log_callback( _csync_ctx, csyncLogCatcher );
|
||||
csync_set_log_verbosity(_csync_ctx, 11);
|
||||
|
||||
MirallConfigFile cfgFile;
|
||||
csync_set_config_dir( _csync_ctx, cfgFile.configPath().toUtf8() );
|
||||
|
||||
csync_enable_conflictcopys(_csync_ctx);
|
||||
setIgnoredFiles();
|
||||
cfgFile.getCredentials()->syncContextPreInit(_csync_ctx);
|
||||
|
||||
if( csync_init( _csync_ctx ) < 0 ) {
|
||||
qDebug() << "Could not initialize csync!" << csync_get_error(_csync_ctx) << csync_get_error_string(_csync_ctx);
|
||||
slotCSyncError(CSyncThread::csyncErrorToString(csync_get_error(_csync_ctx), csync_get_error_string(_csync_ctx)));
|
||||
csync_destroy(_csync_ctx);
|
||||
_csync_ctx = 0;
|
||||
}
|
||||
}
|
||||
return _csync_ctx;
|
||||
}
|
||||
Folder::~Folder()
|
||||
{
|
||||
if( _thread ) {
|
||||
_thread->quit();
|
||||
csync_request_abort(_csync_ctx);
|
||||
_thread->wait();
|
||||
}
|
||||
delete _csync;
|
||||
// Destroy csync here.
|
||||
csync_destroy(_csync_ctx);
|
||||
}
|
||||
|
||||
void Folder::checkLocalPath()
|
||||
@@ -129,6 +173,11 @@ QString Folder::path() const
|
||||
return p;
|
||||
}
|
||||
|
||||
bool Folder::isBusy() const
|
||||
{
|
||||
return ( _thread && _thread->isRunning() );
|
||||
}
|
||||
|
||||
QString Folder::secondPath() const
|
||||
{
|
||||
return _secondPath;
|
||||
@@ -139,11 +188,6 @@ QString Folder::nativePath() const
|
||||
return QDir::toNativeSeparators(_path);
|
||||
}
|
||||
|
||||
QString Folder::nativeSecondPath() const
|
||||
{
|
||||
return secondPath();
|
||||
}
|
||||
|
||||
bool Folder::syncEnabled() const
|
||||
{
|
||||
return _enabled;
|
||||
@@ -153,9 +197,6 @@ void Folder::setSyncEnabled( bool doit )
|
||||
{
|
||||
_enabled = doit;
|
||||
_watcher->setEventsEnabled( doit );
|
||||
if( doit && ! _pollTimer->isActive() ) {
|
||||
_pollTimer->start();
|
||||
}
|
||||
|
||||
qDebug() << "setSyncEnabled - ############################ " << doit;
|
||||
if( doit ) {
|
||||
@@ -168,85 +209,22 @@ void Folder::setSyncEnabled( bool doit )
|
||||
}
|
||||
}
|
||||
|
||||
bool Folder::onlyOnlineEnabled() const
|
||||
{
|
||||
return _onlyOnlineEnabled;
|
||||
}
|
||||
|
||||
void Folder::setOnlyOnlineEnabled(bool enabled)
|
||||
{
|
||||
_onlyOnlineEnabled = enabled;
|
||||
}
|
||||
|
||||
bool Folder::onlyThisLANEnabled() const
|
||||
{
|
||||
return _onlyThisLANEnabled;
|
||||
}
|
||||
|
||||
void Folder::setOnlyThisLANEnabled(bool enabled)
|
||||
{
|
||||
_onlyThisLANEnabled = enabled;
|
||||
}
|
||||
|
||||
int Folder::pollInterval() const
|
||||
{
|
||||
return _pollTimer->interval();
|
||||
}
|
||||
|
||||
void Folder::setSyncState(SyncResult::Status state)
|
||||
{
|
||||
_syncResult.setStatus(state);
|
||||
}
|
||||
|
||||
void Folder::setPollInterval(int milliseconds)
|
||||
{
|
||||
_pollTimer->setInterval( milliseconds );
|
||||
}
|
||||
|
||||
int Folder::errorCount()
|
||||
{
|
||||
return _errorCount;
|
||||
}
|
||||
|
||||
void Folder::resetErrorCount()
|
||||
{
|
||||
_errorCount = 0;
|
||||
}
|
||||
|
||||
void Folder::incrementErrorCount()
|
||||
{
|
||||
// if the error count gets higher than three, the interval timer
|
||||
// of the watcher is doubled.
|
||||
_errorCount++;
|
||||
if( _errorCount > 1 ) {
|
||||
int interval = _watcher->eventInterval();
|
||||
int newInt = 2*interval;
|
||||
qDebug() << "Set new watcher interval to " << newInt;
|
||||
_watcher->setEventInterval( newInt );
|
||||
_errorCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
SyncResult Folder::syncResult() const
|
||||
{
|
||||
return _syncResult;
|
||||
}
|
||||
|
||||
void Folder::evaluateSync(const QStringList &pathList)
|
||||
void Folder::evaluateSync(const QStringList &/*pathList*/)
|
||||
{
|
||||
if( !_enabled ) {
|
||||
qDebug() << "*" << alias() << "sync skipped, disabled!";
|
||||
return;
|
||||
}
|
||||
if (!_online && onlyOnlineEnabled()) {
|
||||
qDebug() << "*" << alias() << "sync skipped, not online";
|
||||
return;
|
||||
}
|
||||
|
||||
// stop the poll timer here. Its started again in the slot of
|
||||
// sync finished.
|
||||
qDebug() << "* " << alias() << "Poll timer disabled";
|
||||
_pollTimer->stop();
|
||||
|
||||
_syncResult.setStatus( SyncResult::NotYetStarted );
|
||||
emit scheduleToSync( alias() );
|
||||
@@ -255,15 +233,36 @@ void Folder::evaluateSync(const QStringList &pathList)
|
||||
|
||||
void Folder::slotPollTimerTimeout()
|
||||
{
|
||||
qDebug() << "* Polling" << alias() << "for changes. Ignoring all pending events until now";
|
||||
_watcher->clearPendingEvents();
|
||||
evaluateSync(QStringList());
|
||||
qDebug() << "* Polling" << alias() << "for changes. (time since next sync:" << (_timeSinceLastSync.elapsed() / 1000) << "s)";
|
||||
|
||||
if (quint64(_timeSinceLastSync.elapsed()) > MirallConfigFile().forceSyncInterval()) {
|
||||
qDebug() << "* Force Sync now";
|
||||
evaluateSync(QStringList());
|
||||
} else {
|
||||
RequestEtagJob* job = new RequestEtagJob(secondPath(), this);
|
||||
// check if the etag is different
|
||||
QObject::connect(job, SIGNAL(etagRetreived(QString)), this, SLOT(etagRetreived(QString)));
|
||||
QObject::connect(job, SIGNAL(networkError()), this, SLOT(slotNetworkUnavailable()));
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::slotOnlineChanged(bool online)
|
||||
void Folder::etagRetreived(const QString& etag)
|
||||
{
|
||||
qDebug() << "* " << alias() << "is" << (online ? "now online" : "no longer online");
|
||||
_online = online;
|
||||
qDebug() << "* Compare etag with previous etag: " << (_lastEtag != etag);
|
||||
|
||||
// re-enable sync if it was disabled because network was down
|
||||
FolderMan::instance()->setSyncEnabled(true);
|
||||
|
||||
if (_lastEtag != etag) {
|
||||
_lastEtag = etag;
|
||||
evaluateSync(QStringList());
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::slotNetworkUnavailable()
|
||||
{
|
||||
_syncResult.setStatus(SyncResult::Unavailable);
|
||||
emit syncStateChange();
|
||||
}
|
||||
|
||||
void Folder::slotChanged(const QStringList &pathList)
|
||||
@@ -272,36 +271,100 @@ void Folder::slotChanged(const QStringList &pathList)
|
||||
evaluateSync(pathList);
|
||||
}
|
||||
|
||||
void Folder::slotSyncStarted()
|
||||
void Folder::bubbleUpSyncResult()
|
||||
{
|
||||
// disable events until syncing is done
|
||||
_watcher->setEventsEnabled(false);
|
||||
}
|
||||
// count new, removed and updated items
|
||||
int newItems = 0;
|
||||
int removedItems = 0;
|
||||
int updatedItems = 0;
|
||||
int ignoredItems = 0;
|
||||
|
||||
void Folder::slotSyncFinished(const SyncResult &result)
|
||||
{
|
||||
_watcher->setEventsEnabledDelayed(2000);
|
||||
SyncFileItem firstItemNew;
|
||||
SyncFileItem firstItemDeleted;
|
||||
SyncFileItem firstItemUpdated;
|
||||
|
||||
qDebug() << "OO folder slotSyncFinished: result: " << int(result.status()) << " local: " << result.localRunOnly();
|
||||
emit syncStateChange();
|
||||
foreach (const SyncFileItem &item, _syncResult.syncFileItemVector() ) {
|
||||
if( item._instruction == CSYNC_INSTRUCTION_ERROR ) {
|
||||
slotCSyncError( tr("File %1: %2").arg(item._file).arg(item._errorString) );
|
||||
} else {
|
||||
if (item._dir == SyncFileItem::Down) {
|
||||
switch (item._instruction) {
|
||||
case CSYNC_INSTRUCTION_NEW:
|
||||
newItems++;
|
||||
if (firstItemNew.isEmpty())
|
||||
firstItemNew = item;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_REMOVE:
|
||||
removedItems++;
|
||||
if (firstItemDeleted.isEmpty())
|
||||
firstItemDeleted = item;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_UPDATED:
|
||||
updatedItems++;
|
||||
if (firstItemUpdated.isEmpty())
|
||||
firstItemUpdated = item;
|
||||
break;
|
||||
case CSYNC_INSTRUCTION_ERROR:
|
||||
qDebug() << "Got Instruction ERROR. " << _syncResult.errorString();
|
||||
break;
|
||||
default:
|
||||
// nothing.
|
||||
break;
|
||||
}
|
||||
} else if( item._dir == SyncFileItem::None ) { // ignored files counting.
|
||||
if( item._instruction == CSYNC_INSTRUCTION_IGNORE ) {
|
||||
ignoredItems++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reenable the poll timer if folder is sync enabled
|
||||
if( syncEnabled() ) {
|
||||
qDebug() << "* " << alias() << "Poll timer enabled with " << _pollTimer->interval() << "milliseconds";
|
||||
_pollTimer->start();
|
||||
} else {
|
||||
qDebug() << "* Not enabling poll timer for " << alias();
|
||||
_pollTimer->stop();
|
||||
_syncResult.setWarnCount(ignoredItems);
|
||||
|
||||
Logger *logger = Logger::instance();
|
||||
|
||||
qDebug() << "OO folder slotSyncFinished: result: " << int(_syncResult.status());
|
||||
if (newItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemNew._file);
|
||||
if (newItems == 1)
|
||||
logger->postGuiLog(tr("New file available"), tr("'%1' has been synced to this machine.").arg(file));
|
||||
else
|
||||
logger->postGuiLog(tr("New files available"), tr("'%1' and %n other file(s) have been synced to this machine.",
|
||||
"", newItems-1).arg(file));
|
||||
}
|
||||
if (removedItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemDeleted._file);
|
||||
if (removedItems == 1)
|
||||
logger->postGuiLog(tr("File removed"), tr("'%1' has been removed.").arg(file));
|
||||
else
|
||||
logger->postGuiLog(tr("Files removed"), tr("'%1' and %n other file(s) have been removed.",
|
||||
"", removedItems-1).arg(file));
|
||||
}
|
||||
if (updatedItems > 0) {
|
||||
QString file = QDir::toNativeSeparators(firstItemUpdated._file);
|
||||
if (updatedItems == 1)
|
||||
logger->postGuiLog(tr("File updated"), tr("'%1' has been updated.").arg(file));
|
||||
else
|
||||
logger->postGuiLog(tr("Files updated"), tr("'%1' and %n other file(s) have been updated.",
|
||||
"", updatedItems-1).arg(file));
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::slotLocalPathChanged( const QString& dir )
|
||||
{
|
||||
QDir notifiedDir(dir);
|
||||
QDir localPath(_path );
|
||||
QDir localPath( path() );
|
||||
|
||||
if( notifiedDir == localPath ) {
|
||||
if( notifiedDir.absolutePath() == localPath.absolutePath() ) {
|
||||
if( !localPath.exists() ) {
|
||||
qDebug() << "XXXXXXX The sync folder root was removed!!";
|
||||
if( _thread && _thread->isRunning() ) {
|
||||
qDebug() << "CSync currently running, set wipe flag!!";
|
||||
} else {
|
||||
qDebug() << "CSync not running, wipe it now!!";
|
||||
wipe();
|
||||
}
|
||||
|
||||
qDebug() << "ALARM: The local path was DELETED!";
|
||||
}
|
||||
}
|
||||
@@ -317,20 +380,303 @@ QString Folder::configFile()
|
||||
return _configFile;
|
||||
}
|
||||
|
||||
void Folder::setBackend( const QString& b )
|
||||
void Folder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
|
||||
{
|
||||
_backend = b;
|
||||
_syncResult.setSyncFileItemVector(items);
|
||||
}
|
||||
|
||||
QString Folder::backend() const
|
||||
void Folder::slotCatchWatcherError(const QString& error)
|
||||
{
|
||||
return _backend;
|
||||
Logger::instance()->postGuiLog(tr("Error"), error);
|
||||
}
|
||||
|
||||
void Folder::slotTerminateSync()
|
||||
{
|
||||
qDebug() << "folder " << alias() << " Terminating!";
|
||||
MirallConfigFile cfg;
|
||||
QString configDir = cfg.configPath();
|
||||
qDebug() << "csync's Config Dir: " << configDir;
|
||||
|
||||
if( _thread && _csync ) {
|
||||
csync_request_abort(_csync_ctx);
|
||||
_thread->quit();
|
||||
_thread->wait();
|
||||
_csync->deleteLater();
|
||||
delete _thread;
|
||||
_csync = 0;
|
||||
_thread = 0;
|
||||
csync_resume(_csync_ctx);
|
||||
}
|
||||
|
||||
if( ! configDir.isEmpty() ) {
|
||||
QFile file( configDir + QLatin1String("/lock"));
|
||||
if( file.exists() ) {
|
||||
qDebug() << "After termination, lock file exists and gets removed.";
|
||||
file.remove();
|
||||
}
|
||||
}
|
||||
|
||||
_errors.append( tr("The CSync thread terminated.") );
|
||||
_csyncError = true;
|
||||
qDebug() << "-> CSync Terminated!";
|
||||
slotCSyncFinished();
|
||||
}
|
||||
|
||||
// This removes the csync File database if the sync folder definition is removed
|
||||
// permanentely. This is needed to provide a clean startup again in case another
|
||||
// local folder is synced to the same ownCloud.
|
||||
// See http://bugs.owncloud.org/thebuggenie/owncloud/issues/oc-788
|
||||
void Folder::wipe()
|
||||
{
|
||||
QString stateDbFile = path()+QLatin1String(".csync_journal.db");
|
||||
|
||||
QFile file(stateDbFile);
|
||||
if( file.exists() ) {
|
||||
if( !file.remove()) {
|
||||
qDebug() << "WRN: Failed to remove existing csync StateDB " << stateDbFile;
|
||||
} else {
|
||||
qDebug() << "wipe: Removed csync StateDB " << stateDbFile;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "WRN: statedb is empty, can not remove.";
|
||||
}
|
||||
// Check if the tmp database file also exists
|
||||
QString ctmpName = path() + QLatin1String(".csync_journal.db.ctmp");
|
||||
QFile ctmpFile( ctmpName );
|
||||
if( ctmpFile.exists() ) {
|
||||
ctmpFile.remove();
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::setIgnoredFiles()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
csync_clear_exclude_list( _csync_ctx );
|
||||
QString excludeList = cfgFile.excludeFile( MirallConfigFile::SystemScope );
|
||||
if( !excludeList.isEmpty() ) {
|
||||
qDebug() << "==== added system ignore list to csync:" << excludeList.toUtf8();
|
||||
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
|
||||
}
|
||||
excludeList = cfgFile.excludeFile( MirallConfigFile::UserScope );
|
||||
if( !excludeList.isEmpty() ) {
|
||||
qDebug() << "==== added user defined ignore list to csync:" << excludeList.toUtf8();
|
||||
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::setProxy()
|
||||
{
|
||||
if( _csync_ctx ) {
|
||||
/* Store proxy */
|
||||
QUrl proxyUrl(ownCloudInfo::instance()->webdavUrl());
|
||||
QList<QNetworkProxy> proxies = QNetworkProxyFactory::proxyForQuery(proxyUrl);
|
||||
// We set at least one in Application
|
||||
Q_ASSERT(proxies.count() > 0);
|
||||
QNetworkProxy proxy = proxies.first();
|
||||
if (proxy.type() == QNetworkProxy::NoProxy) {
|
||||
qDebug() << "Passing NO proxy to csync for" << proxyUrl;
|
||||
} else {
|
||||
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
|
||||
<< " to csync for" << proxyUrl;
|
||||
}
|
||||
int proxyPort = proxy.port();
|
||||
|
||||
csync_set_module_property(_csync_ctx, "proxy_type", (char*) proxyTypeToCStr(proxy.type()) );
|
||||
csync_set_module_property(_csync_ctx, "proxy_host", proxy.hostName().toUtf8().data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_port", &proxyPort );
|
||||
csync_set_module_property(_csync_ctx, "proxy_user", proxy.user().toUtf8().data() );
|
||||
csync_set_module_property(_csync_ctx, "proxy_pwd" , proxy.password().toUtf8().data() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char* Folder::proxyTypeToCStr(QNetworkProxy::ProxyType type)
|
||||
{
|
||||
switch (type) {
|
||||
case QNetworkProxy::NoProxy:
|
||||
return "NoProxy";
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
return "DefaultProxy";
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
return "Socks5Proxy";
|
||||
case QNetworkProxy::HttpProxy:
|
||||
return "HttpProxy";
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
return "HttpCachingProxy";
|
||||
case QNetworkProxy::FtpCachingProxy:
|
||||
return "FtpCachingProxy";
|
||||
default:
|
||||
return "NoProxy";
|
||||
}
|
||||
}
|
||||
|
||||
void Folder::startSync(const QStringList &pathList)
|
||||
{
|
||||
Q_UNUSED(pathList)
|
||||
if (!_csync_ctx) {
|
||||
// no _csync_ctx yet, initialize it.
|
||||
init();
|
||||
setProxy();
|
||||
|
||||
if (!_csync_ctx) {
|
||||
qDebug() << Q_FUNC_INFO << "init failed.";
|
||||
// the error should already be set
|
||||
QMetaObject::invokeMethod(this, "slotCSyncFinished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_thread && _thread->isRunning()) {
|
||||
qCritical() << "* ERROR csync is still running and new sync requested.";
|
||||
return;
|
||||
}
|
||||
if (_thread)
|
||||
_thread->quit();
|
||||
delete _csync;
|
||||
delete _thread;
|
||||
_errors.clear();
|
||||
_csyncError = false;
|
||||
_csyncUnavail = false;
|
||||
|
||||
_syncResult.clearErrors();
|
||||
_syncResult.setStatus( SyncResult::SyncPrepare );
|
||||
emit syncStateChange();
|
||||
|
||||
|
||||
qDebug() << "*** Start syncing";
|
||||
_thread = new QThread(this);
|
||||
_thread->setPriority(QThread::LowPriority);
|
||||
setIgnoredFiles();
|
||||
_csync = new CSyncThread( _csync_ctx );
|
||||
_csync->moveToThread(_thread);
|
||||
|
||||
|
||||
qRegisterMetaType<SyncFileItemVector>("SyncFileItemVector");
|
||||
qRegisterMetaType<SyncFileItem::Direction>("SyncFileItem::Direction");
|
||||
|
||||
connect( _csync, SIGNAL(treeWalkResult(const SyncFileItemVector&)),
|
||||
this, SLOT(slotThreadTreeWalkResult(const SyncFileItemVector&)), Qt::QueuedConnection);
|
||||
|
||||
connect(_csync, SIGNAL(started()), SLOT(slotCSyncStarted()), Qt::QueuedConnection);
|
||||
connect(_csync, SIGNAL(finished()), SLOT(slotCSyncFinished()), Qt::QueuedConnection);
|
||||
connect(_csync, SIGNAL(csyncError(QString)), SLOT(slotCSyncError(QString)), Qt::QueuedConnection);
|
||||
connect(_csync, SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
|
||||
|
||||
//blocking connection so the message box happens in this thread, but block the csync thread.
|
||||
connect(_csync, SIGNAL(aboutToRemoveAllFiles(SyncFileItem::Direction,bool*)),
|
||||
SLOT(slotAboutToRemoveAllFiles(SyncFileItem::Direction,bool*)), Qt::BlockingQueuedConnection);
|
||||
connect(_csync, SIGNAL(transmissionProgress(Progress::Info)), this, SLOT(slotTransmissionProgress(Progress::Info)));
|
||||
|
||||
_thread->start();
|
||||
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
|
||||
|
||||
// disable events until syncing is done
|
||||
_watcher->setEventsEnabled(false);
|
||||
_pollTimer.stop();
|
||||
emit syncStarted();
|
||||
}
|
||||
|
||||
void Folder::slotCSyncError(const QString& err)
|
||||
{
|
||||
_errors.append( err );
|
||||
_csyncError = true;
|
||||
}
|
||||
|
||||
void Folder::slotCSyncStarted()
|
||||
{
|
||||
qDebug() << " * csync thread started";
|
||||
_syncResult.setStatus(SyncResult::SyncRunning);
|
||||
emit syncStateChange();
|
||||
}
|
||||
|
||||
void Folder::slotCsyncUnavailable()
|
||||
{
|
||||
_csyncUnavail = true;
|
||||
}
|
||||
|
||||
void Folder::slotCSyncFinished()
|
||||
{
|
||||
qDebug() << "-> CSync Finished slot with error " << _csyncError;
|
||||
_watcher->setEventsEnabledDelayed(2000);
|
||||
_pollTimer.start();
|
||||
_timeSinceLastSync.restart();
|
||||
|
||||
bubbleUpSyncResult();
|
||||
|
||||
if (_csyncError) {
|
||||
_syncResult.setStatus(SyncResult::Error);
|
||||
qDebug() << " ** error Strings: " << _errors;
|
||||
_syncResult.setErrorStrings( _errors );
|
||||
qDebug() << " * owncloud csync thread finished with error";
|
||||
} else if (_csyncUnavail) {
|
||||
_syncResult.setStatus(SyncResult::Unavailable);
|
||||
} else if( _syncResult.warnCount() > 0 ) {
|
||||
// there have been warnings on the way.
|
||||
_syncResult.setStatus(SyncResult::Problem);
|
||||
} else {
|
||||
_syncResult.setStatus(SyncResult::Success);
|
||||
}
|
||||
|
||||
if( _thread && _thread->isRunning() ) {
|
||||
_thread->quit();
|
||||
}
|
||||
emit syncStateChange();
|
||||
ownCloudInfo::instance()->getQuotaRequest("/");
|
||||
emit syncFinished( _syncResult );
|
||||
}
|
||||
|
||||
void Folder::slotTransmissionProgress(const Progress::Info& progress)
|
||||
{
|
||||
Progress::Info newInfo = progress;
|
||||
newInfo.folder = alias();
|
||||
|
||||
if(newInfo.current_file.startsWith(QLatin1String("ownclouds://")) ||
|
||||
newInfo.current_file.startsWith(QLatin1String("owncloud://")) ) {
|
||||
// rip off the whole ownCloud URL.
|
||||
QString remotePathUrl = ownCloudInfo::instance()->webdavUrl() + secondPath();
|
||||
newInfo.current_file.remove(Utility::toCSyncScheme(remotePathUrl));
|
||||
}
|
||||
QString localPath = path();
|
||||
if( newInfo.current_file.startsWith(localPath) ) {
|
||||
// remove the local dir.
|
||||
newInfo.current_file = newInfo.current_file.right( newInfo.current_file.length() - localPath.length());
|
||||
}
|
||||
|
||||
// remember problems happening to set the correct Sync status in slot slotCSyncFinished.
|
||||
if( newInfo.kind == Progress::StartSync ) {
|
||||
_syncResult.setWarnCount(0);
|
||||
}
|
||||
if( newInfo.kind == Progress::Error ) {
|
||||
_syncResult.setWarnCount( _syncResult.warnCount()+1 );
|
||||
}
|
||||
|
||||
ProgressDispatcher::instance()->setProgressInfo(alias(), newInfo);
|
||||
}
|
||||
|
||||
void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction direction, bool *cancel)
|
||||
{
|
||||
QString msg = direction == SyncFileItem::Down ?
|
||||
tr("This sync would remove all the files in the local sync folder '%1'.\n"
|
||||
"If you or your administrator have reset your account on the server, choose "
|
||||
"\"Keep files\". If you want your data to be removed, choose \"Remove all files\".") :
|
||||
tr("This sync would remove all the files in the sync folder '%1'.\n"
|
||||
"This might be because the folder was silently reconfigured, or that all "
|
||||
"the file were manually removed.\n"
|
||||
"Are you sure you want to perform this operation?");
|
||||
QMessageBox msgBox(QMessageBox::Warning, tr("Remove All Files?"),
|
||||
msg.arg(alias()));
|
||||
msgBox.addButton(tr("Remove all files"), QMessageBox::DestructiveRole);
|
||||
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::ActionRole);
|
||||
if (msgBox.exec() == -1) {
|
||||
*cancel = true;
|
||||
return;
|
||||
}
|
||||
*cancel = msgBox.clickedButton() == keepBtn;
|
||||
if (*cancel) {
|
||||
wipe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
|
||||
* Copyright (C) by Daniel Molkentin <danimo@owncloud.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
|
||||
@@ -15,38 +17,61 @@
|
||||
#ifndef MIRALL_FOLDER_H
|
||||
#define MIRALL_FOLDER_H
|
||||
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QHash>
|
||||
#include <QTimer>
|
||||
|
||||
#if QT_VERSION >= 0x040700
|
||||
#include <QNetworkConfigurationManager>
|
||||
#endif
|
||||
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/progressdispatcher.h"
|
||||
#include "mirall/csyncthread.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkProxy>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <qelapsedtimer.h>
|
||||
|
||||
class QAction;
|
||||
class QIcon;
|
||||
class QFileSystemWatcher;
|
||||
class QThread;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class FolderWatcher;
|
||||
|
||||
typedef enum SyncFileStatus_s {
|
||||
STATUS_NONE,
|
||||
STATUS_EVAL,
|
||||
STATUS_REMOVE,
|
||||
STATUS_RENAME,
|
||||
STATUS_NEW,
|
||||
STATUS_CONFLICT,
|
||||
STATUS_IGNORE,
|
||||
STATUS_SYNC,
|
||||
STATUS_STAT_ERROR,
|
||||
STATUS_ERROR,
|
||||
STATUS_UPDATED
|
||||
} SyncFileStatus;
|
||||
|
||||
class Folder : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
protected:
|
||||
friend class FolderMan;
|
||||
Folder(const QString&, const QString&, const QString& , QObject*parent = 0L);
|
||||
virtual ~Folder();
|
||||
|
||||
public:
|
||||
~Folder();
|
||||
|
||||
typedef QHash<QString, Folder*> Map;
|
||||
typedef QHashIterator<QString, Folder*> MapIterator;
|
||||
|
||||
/**
|
||||
* Get status about a single file.
|
||||
*/
|
||||
SyncFileStatus fileStatus( const QString& );
|
||||
|
||||
/**
|
||||
* alias or nickname
|
||||
*/
|
||||
@@ -56,13 +81,16 @@ public:
|
||||
* local folder path
|
||||
*/
|
||||
QString path() const;
|
||||
virtual QString secondPath() const;
|
||||
/**
|
||||
* remote folder path
|
||||
*/
|
||||
QString secondPath() const;
|
||||
|
||||
/**
|
||||
* local folder path with native separators
|
||||
*/
|
||||
QString nativePath() const;
|
||||
virtual QString nativeSecondPath() const;
|
||||
|
||||
/**
|
||||
* switch sync on or off
|
||||
* If the sync is switched off, the startSync method is not going to
|
||||
@@ -72,69 +100,17 @@ public:
|
||||
|
||||
bool syncEnabled() const;
|
||||
|
||||
/**
|
||||
* Starts a sync operation
|
||||
*
|
||||
* If the list of changed files is known, it is passed.
|
||||
*
|
||||
* If the list of changed files is empty, the folder
|
||||
* implementation should figure it by itself of
|
||||
* perform a full scan of changes
|
||||
*/
|
||||
virtual void startSync(const QStringList &pathList) = 0;
|
||||
|
||||
/**
|
||||
* True if the folder is busy and can't initiate
|
||||
* a synchronization
|
||||
*/
|
||||
virtual bool isBusy() const = 0;
|
||||
|
||||
/**
|
||||
* only sync when online in the network
|
||||
*/
|
||||
bool onlyOnlineEnabled() const;
|
||||
|
||||
/**
|
||||
* @see onlyOnlineEnabled
|
||||
*/
|
||||
void setOnlyOnlineEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* only sync when online in the same LAN
|
||||
* as the one used during setup
|
||||
*/
|
||||
bool onlyThisLANEnabled() const;
|
||||
|
||||
/**
|
||||
* @see onlyThisLANEnabled
|
||||
*/
|
||||
void setOnlyThisLANEnabled(bool enabled);
|
||||
|
||||
|
||||
/**
|
||||
* error counter, stop syncing after the counter reaches a certain
|
||||
* number.
|
||||
*/
|
||||
int errorCount();
|
||||
|
||||
void resetErrorCount();
|
||||
|
||||
void incrementErrorCount();
|
||||
virtual bool isBusy() const;
|
||||
|
||||
/**
|
||||
* return the last sync result with error message and status
|
||||
*/
|
||||
SyncResult syncResult() const;
|
||||
|
||||
/**
|
||||
* set the backend description string.
|
||||
*/
|
||||
void setBackend( const QString& );
|
||||
/**
|
||||
* get the backend description string.
|
||||
*/
|
||||
QString backend() const;
|
||||
|
||||
/**
|
||||
* set the config file name.
|
||||
*/
|
||||
@@ -146,9 +122,6 @@ public:
|
||||
*/
|
||||
virtual void wipe();
|
||||
|
||||
QIcon icon( int size ) const;
|
||||
QTimer *_pollTimer;
|
||||
|
||||
signals:
|
||||
void syncStateChange();
|
||||
void syncStarted();
|
||||
@@ -156,7 +129,6 @@ signals:
|
||||
void scheduleToSync( const QString& );
|
||||
|
||||
public slots:
|
||||
void slotSyncFinished(const SyncResult &);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -166,43 +138,47 @@ public slots:
|
||||
/**
|
||||
* terminate the current sync run
|
||||
*/
|
||||
virtual void slotTerminateSync() = 0;
|
||||
void slotTerminateSync();
|
||||
|
||||
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
|
||||
|
||||
|
||||
/**
|
||||
* Sets minimum amounts of milliseconds that will separate
|
||||
* poll intervals
|
||||
* Starts a sync operation
|
||||
*
|
||||
* If the list of changed files is known, it is passed.
|
||||
*/
|
||||
void setPollInterval( int );
|
||||
void startSync(const QStringList &pathList = QStringList());
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The minimum amounts of seconds to wait before
|
||||
* doing a full sync to see if the remote changed
|
||||
*/
|
||||
int pollInterval() const;
|
||||
void setSyncState(SyncResult::Status state);
|
||||
private slots:
|
||||
void slotCSyncStarted();
|
||||
void slotCSyncError(const QString& );
|
||||
void slotCsyncUnavailable();
|
||||
void slotCSyncFinished();
|
||||
|
||||
FolderWatcher *_watcher;
|
||||
int _errorCount;
|
||||
SyncResult _syncResult;
|
||||
|
||||
protected slots:
|
||||
|
||||
void slotOnlineChanged(bool online);
|
||||
void slotTransmissionProgress(const Progress::Info& progress);
|
||||
|
||||
void slotPollTimerTimeout();
|
||||
|
||||
/* called when the watcher detect a list of changed
|
||||
paths */
|
||||
|
||||
void slotSyncStarted();
|
||||
void etagRetreived(const QString &);
|
||||
void slotNetworkUnavailable();
|
||||
|
||||
/**
|
||||
* Triggered by a file system watcher on the local sync dir
|
||||
*/
|
||||
virtual void slotLocalPathChanged( const QString& );
|
||||
void slotLocalPathChanged( const QString& );
|
||||
void slotThreadTreeWalkResult(const SyncFileItemVector& );
|
||||
void slotCatchWatcherError( const QString& );
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool init();
|
||||
|
||||
void setSyncState(SyncResult::Status state);
|
||||
|
||||
void setIgnoredFiles();
|
||||
void setProxy();
|
||||
const char* proxyTypeToCStr(QNetworkProxy::ProxyType type);
|
||||
|
||||
void bubbleUpSyncResult();
|
||||
|
||||
/**
|
||||
* Starts a sync (calling startSync)
|
||||
@@ -210,23 +186,28 @@ private:
|
||||
*/
|
||||
void evaluateSync(const QStringList &pathList);
|
||||
|
||||
virtual void checkLocalPath();
|
||||
void checkLocalPath();
|
||||
|
||||
QString _path;
|
||||
QString _secondPath;
|
||||
QString _alias;
|
||||
bool _onlyOnlineEnabled;
|
||||
bool _onlyThisLANEnabled;
|
||||
QString _configFile;
|
||||
|
||||
QFileSystemWatcher *_pathWatcher;
|
||||
|
||||
#if QT_VERSION >= 0x040700
|
||||
QNetworkConfigurationManager _networkMgr;
|
||||
#endif
|
||||
bool _online;
|
||||
bool _enabled;
|
||||
QString _backend;
|
||||
FolderWatcher *_watcher;
|
||||
SyncResult _syncResult;
|
||||
QThread *_thread;
|
||||
CSyncThread *_csync;
|
||||
QStringList _errors;
|
||||
bool _csyncError;
|
||||
bool _csyncUnavail;
|
||||
bool _wipeDb;
|
||||
Progress::Kind _progressKind;
|
||||
QTimer _pollTimer;
|
||||
QString _lastEtag;
|
||||
QElapsedTimer _timeSinceLastSync;
|
||||
|
||||
CSYNC *_csync_ctx;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
|
||||
#include "mirall/folderman.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/unisonfolder.h"
|
||||
#include "mirall/csyncfolder.h"
|
||||
#include "mirall/owncloudfolder.h"
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/syncresult.h"
|
||||
#include "mirall/inotify.h"
|
||||
#include "mirall/theme.h"
|
||||
#include "owncloudinfo.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include <CoreServices/CoreServices.h>
|
||||
@@ -29,10 +28,13 @@
|
||||
#endif
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QMessageBox>
|
||||
#include <QtCore>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
FolderMan* FolderMan::_instance = 0;
|
||||
|
||||
FolderMan::FolderMan(QObject *parent) :
|
||||
QObject(parent),
|
||||
_syncEnabled( true )
|
||||
@@ -49,11 +51,17 @@ FolderMan::FolderMan(QObject *parent) :
|
||||
this, SIGNAL(folderSyncStateChange(const QString &)));
|
||||
}
|
||||
|
||||
FolderMan *FolderMan::instance()
|
||||
{
|
||||
if(!_instance)
|
||||
_instance = new FolderMan;
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
FolderMan::~FolderMan()
|
||||
{
|
||||
foreach (Folder *folder, _folderMap) {
|
||||
delete folder;
|
||||
}
|
||||
qDeleteAll(_folderMap);
|
||||
}
|
||||
|
||||
Mirall::Folder::Map FolderMan::map()
|
||||
@@ -65,15 +73,7 @@ Mirall::Folder::Map FolderMan::map()
|
||||
int FolderMan::setupFolders()
|
||||
{
|
||||
// setup a handler to look for configuration changes
|
||||
#ifdef CHECK_FOR_SETUP_CHANGES
|
||||
_configFolderWatcher = new FolderWatcher( _folderConfigPath );
|
||||
_configFolderWatcher->setEventInterval(20000);
|
||||
connect(_configFolderWatcher, SIGNAL(folderChanged(const QStringList &)),
|
||||
this, SLOT( slotReparseConfiguration()) );
|
||||
#endif
|
||||
int cnt = setupKnownFolders();
|
||||
|
||||
return cnt;
|
||||
return setupKnownFolders();
|
||||
}
|
||||
|
||||
void FolderMan::slotReparseConfiguration()
|
||||
@@ -81,20 +81,25 @@ void FolderMan::slotReparseConfiguration()
|
||||
setupKnownFolders();
|
||||
}
|
||||
|
||||
int FolderMan::unloadAllFolders()
|
||||
{
|
||||
int cnt = 0;
|
||||
|
||||
// clear the list of existing folders.
|
||||
Folder::MapIterator i(_folderMap);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
delete _folderMap.take( i.key() );
|
||||
cnt++;
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
int FolderMan::setupKnownFolders()
|
||||
{
|
||||
qDebug() << "* Setup folders from " << _folderConfigPath;
|
||||
|
||||
// first terminate sync jobs.
|
||||
terminateCurrentSync();
|
||||
|
||||
// clear the list of existing folders.
|
||||
Folder::MapIterator i(_folderMap);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
delete _folderMap.take( i.key() );
|
||||
}
|
||||
unloadAllFolders();
|
||||
|
||||
QDir dir( _folderConfigPath );
|
||||
dir.setFilter(QDir::Files);
|
||||
@@ -119,6 +124,25 @@ void FolderMan::wipeAllJournals()
|
||||
}
|
||||
}
|
||||
|
||||
bool FolderMan::ensureJournalGone(const QString &localPath)
|
||||
{
|
||||
|
||||
// remove old .csync_journal file
|
||||
QString stateDbFile = localPath+QLatin1String("/.csync_journal.db");
|
||||
while (QFile::exists(stateDbFile) && !QFile::remove(stateDbFile)) {
|
||||
int ret = QMessageBox::warning(0, tr("Could not reset folder state"),
|
||||
tr("An old sync journal '%1' was found, "
|
||||
"but could not be removed. Please make sure "
|
||||
"that no application is currently using it.")
|
||||
.arg(QDir::fromNativeSeparators(QDir::cleanPath(stateDbFile))),
|
||||
QMessageBox::Retry|QMessageBox::Abort);
|
||||
if (ret == QMessageBox::Abort) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FolderMan::terminateCurrentSync()
|
||||
{
|
||||
if( !_currentSyncFolder.isEmpty() ) {
|
||||
@@ -220,57 +244,28 @@ Folder* FolderMan::setupFolderFromConfigFile(const QString &file) {
|
||||
// QString connection = settings.value( QLatin1String("connection") ).toString();
|
||||
QString alias = unescapeAlias( escapedAlias );
|
||||
|
||||
if (!backend.isEmpty()) {
|
||||
|
||||
if (backend == QLatin1String("unison")) {
|
||||
folder = new UnisonFolder(alias, path, targetPath, this );
|
||||
} else if (backend == QLatin1String("csync")) {
|
||||
#ifdef WITH_CSYNC
|
||||
folder = new CSyncFolder(alias, path, targetPath, this );
|
||||
#else
|
||||
qCritical() << "* csync support not enabled!! ignoring:" << file;
|
||||
#endif
|
||||
} else if( backend == QLatin1String("owncloud") ) {
|
||||
#ifdef WITH_CSYNC
|
||||
|
||||
MirallConfigFile cfgFile;
|
||||
|
||||
// assemble the owncloud url to pass to csync, incl. webdav
|
||||
QString oCUrl = cfgFile.ownCloudUrl( QString::null, true );
|
||||
|
||||
// cut off the leading slash, oCUrl always has a trailing.
|
||||
if( targetPath.startsWith(QLatin1Char('/')) ) {
|
||||
|
||||
targetPath.remove(0,1);
|
||||
}
|
||||
|
||||
folder = new ownCloudFolder( alias, path, oCUrl + targetPath, this );
|
||||
folder->setConfigFile(file);
|
||||
#else
|
||||
qCritical() << "* owncloud support not enabled!! ignoring:" << file;
|
||||
#endif
|
||||
} else {
|
||||
qWarning() << "unknown backend" << backend;
|
||||
return NULL;
|
||||
}
|
||||
if (backend.isEmpty() || backend != QLatin1String("owncloud")) {
|
||||
qWarning() << "obsolete configuration of type" << backend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( folder ) {
|
||||
folder->setBackend( backend );
|
||||
// folder->setOnlyOnlineEnabled(settings.value("folder/onlyOnline", false).toBool());
|
||||
folder->setOnlyThisLANEnabled(settings.value(QLatin1String("folder/onlyThisLAN"), false).toBool());
|
||||
|
||||
_folderMap[alias] = folder;
|
||||
|
||||
qDebug() << "Adding folder to Folder Map " << folder;
|
||||
/* Use a signal mapper to connect the signals to the alias */
|
||||
connect(folder, SIGNAL(scheduleToSync(const QString&)), SLOT(slotScheduleSync(const QString&)));
|
||||
connect(folder, SIGNAL(syncStateChange()), _folderChangeSignalMapper, SLOT(map()));
|
||||
connect(folder, SIGNAL(syncStarted()), SLOT(slotFolderSyncStarted()));
|
||||
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
|
||||
|
||||
_folderChangeSignalMapper->setMapping( folder, folder->alias() );
|
||||
// cut off the leading slash, oCUrl always has a trailing.
|
||||
if( targetPath.startsWith(QLatin1Char('/')) ) {
|
||||
targetPath.remove(0,1);
|
||||
}
|
||||
|
||||
folder = new Folder( alias, path, targetPath, this );
|
||||
folder->setConfigFile(file);
|
||||
qDebug() << "Adding folder to Folder Map " << folder;
|
||||
_folderMap[alias] = folder;
|
||||
|
||||
/* Use a signal mapper to connect the signals to the alias */
|
||||
connect(folder, SIGNAL(scheduleToSync(const QString&)), SLOT(slotScheduleSync(const QString&)));
|
||||
connect(folder, SIGNAL(syncStateChange()), _folderChangeSignalMapper, SLOT(map()));
|
||||
connect(folder, SIGNAL(syncStarted()), SLOT(slotFolderSyncStarted()));
|
||||
connect(folder, SIGNAL(syncFinished(SyncResult)), SLOT(slotFolderSyncFinished(SyncResult)));
|
||||
|
||||
_folderChangeSignalMapper->setMapping( folder, folder->alias() );
|
||||
return folder;
|
||||
}
|
||||
|
||||
@@ -291,12 +286,18 @@ void FolderMan::slotEnableFolder( const QString& alias, bool enable )
|
||||
// csync still remains in a stable state, regardless of that.
|
||||
void FolderMan::terminateSyncProcess( const QString& alias )
|
||||
{
|
||||
Folder *f = _folderMap[alias];
|
||||
if( f ) {
|
||||
f->slotTerminateSync();
|
||||
QString folderAlias = alias;
|
||||
if( alias.isEmpty() ) {
|
||||
folderAlias = _currentSyncFolder;
|
||||
}
|
||||
if( ! folderAlias.isEmpty() ) {
|
||||
Folder *f = _folderMap[folderAlias];
|
||||
if( f ) {
|
||||
f->slotTerminateSync();
|
||||
|
||||
if(_currentSyncFolder == alias )
|
||||
_currentSyncFolder = QString::null;
|
||||
if(_currentSyncFolder == folderAlias )
|
||||
_currentSyncFolder.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,19 +313,21 @@ Folder *FolderMan::folder( const QString& alias )
|
||||
|
||||
SyncResult FolderMan::syncResult( const QString& alias )
|
||||
{
|
||||
SyncResult res;
|
||||
Folder *f = folder( alias );
|
||||
return syncResult(f);
|
||||
}
|
||||
|
||||
if( f ) {
|
||||
res = f->syncResult();
|
||||
}
|
||||
return res;
|
||||
SyncResult FolderMan::syncResult( Folder *f )
|
||||
{
|
||||
return f ? f->syncResult() : SyncResult();
|
||||
}
|
||||
|
||||
void FolderMan::slotScheduleAllFolders()
|
||||
{
|
||||
foreach( Folder *f, _folderMap.values() ) {
|
||||
slotScheduleSync( f->alias() );
|
||||
if (f->syncEnabled()) {
|
||||
slotScheduleSync( f->alias() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,17 +346,19 @@ void FolderMan::slotScheduleSync( const QString& alias )
|
||||
}
|
||||
|
||||
if( ! _scheduleQueue.contains(alias )) {
|
||||
_scheduleQueue.append(alias);
|
||||
_scheduleQueue.enqueue(alias);
|
||||
} else {
|
||||
qDebug() << " II> Sync for folder " << alias << " already scheduled, do not enqueue!";
|
||||
}
|
||||
|
||||
slotScheduleFolderSync();
|
||||
|
||||
}
|
||||
|
||||
void FolderMan::setSyncEnabled( bool enabled )
|
||||
{
|
||||
if (!_syncEnabled && enabled && !_scheduleQueue.isEmpty()) {
|
||||
// We have things in our queue that were waiting the the connection to go back on.
|
||||
QTimer::singleShot(200, this, SLOT(slotScheduleFolderSync()));
|
||||
}
|
||||
_syncEnabled = enabled;
|
||||
}
|
||||
|
||||
@@ -376,11 +381,14 @@ void FolderMan::slotScheduleFolderSync()
|
||||
|
||||
qDebug() << "XX slotScheduleFolderSync: folderQueue size: " << _scheduleQueue.count();
|
||||
if( ! _scheduleQueue.isEmpty() ) {
|
||||
const QString alias = _scheduleQueue.takeFirst();
|
||||
const QString alias = _scheduleQueue.dequeue();
|
||||
if( _folderMap.contains( alias ) ) {
|
||||
ownCloudInfo::instance()->getQuotaRequest("/");
|
||||
Folder *f = _folderMap[alias];
|
||||
_currentSyncFolder = alias;
|
||||
f->startSync( QStringList() );
|
||||
if (f->syncEnabled()) {
|
||||
f->startSync( QStringList() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,28 +410,17 @@ void FolderMan::slotFolderSyncFinished( const SyncResult& )
|
||||
QTimer::singleShot(200, this, SLOT(slotScheduleFolderSync()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a folder definition to the config
|
||||
* Params:
|
||||
* QString backend
|
||||
* QString alias
|
||||
* QString sourceFolder on local machine
|
||||
* QString targetPath on remote
|
||||
* bool onlyThisLAN, currently unused.
|
||||
*/
|
||||
void FolderMan::addFolderDefinition( const QString& backend, const QString& alias,
|
||||
const QString& sourceFolder, const QString& targetPath,
|
||||
bool onlyThisLAN )
|
||||
void FolderMan::addFolderDefinition(const QString& alias, const QString& sourceFolder, const QString& targetPath )
|
||||
{
|
||||
QString escapedAlias = escapeAlias(alias);
|
||||
// Create a settings file named after the alias
|
||||
QSettings settings( _folderConfigPath + QLatin1Char('/') + escapedAlias, QSettings::IniFormat);
|
||||
|
||||
settings.setValue(QString::fromLatin1("%1/localPath").arg(escapedAlias), sourceFolder );
|
||||
settings.setValue(QString::fromLatin1("%1/targetPath").arg(escapedAlias), targetPath );
|
||||
settings.setValue(QString::fromLatin1("%1/backend").arg(escapedAlias), backend );
|
||||
settings.setValue(QString::fromLatin1("%1/connection").arg(escapedAlias), Theme::instance()->appName());
|
||||
settings.setValue(QString::fromLatin1("%1/onlyThisLAN").arg(escapedAlias), onlyThisLAN );
|
||||
settings.beginGroup(escapedAlias);
|
||||
settings.setValue(QLatin1String("localPath"), sourceFolder );
|
||||
settings.setValue(QLatin1String("targetPath"), targetPath );
|
||||
// for compat reasons
|
||||
settings.setValue(QLatin1String("backend"), "owncloud" );
|
||||
settings.setValue(QLatin1String("connection"), Theme::instance()->appName());
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
@@ -453,6 +450,8 @@ void FolderMan::removeFolder( const QString& alias )
|
||||
{
|
||||
Folder *f = 0;
|
||||
|
||||
_scheduleQueue.removeAll(alias);
|
||||
|
||||
if( _folderMap.contains( alias )) {
|
||||
qDebug() << "Removing " << alias;
|
||||
f = _folderMap.take( alias );
|
||||
@@ -471,4 +470,131 @@ void FolderMan::removeFolder( const QString& alias )
|
||||
}
|
||||
}
|
||||
|
||||
QString FolderMan::getBackupName( const QString& fullPathName ) const
|
||||
{
|
||||
if( fullPathName.isEmpty() ) return QString::null;
|
||||
|
||||
QString newName = fullPathName + QLatin1String(".oC_bak");
|
||||
QFileInfo fi( newName );
|
||||
int cnt = 1;
|
||||
do {
|
||||
if( fi.exists() ) {
|
||||
newName = fullPathName + QString( ".oC_bak_%1").arg(cnt++);
|
||||
fi.setFile(newName);
|
||||
}
|
||||
} while( fi.exists() );
|
||||
|
||||
return newName;
|
||||
}
|
||||
|
||||
bool FolderMan::startFromScratch( const QString& localFolder )
|
||||
{
|
||||
if( localFolder.isEmpty() ) return false;
|
||||
|
||||
QFileInfo fi( localFolder );
|
||||
if( fi.exists() && fi.isDir() ) {
|
||||
QDir file = fi.dir();
|
||||
|
||||
// check if there are files in the directory.
|
||||
if( file.count() == 0 ) {
|
||||
// directory is existing, but its empty. Use it.
|
||||
qDebug() << "startFromScratch: Directory is empty!";
|
||||
return true;
|
||||
}
|
||||
QString newName = getBackupName( fi.absoluteFilePath() );
|
||||
|
||||
if( file.rename( fi.absoluteFilePath(), newName )) {
|
||||
if( file.mkdir( fi.absoluteFilePath() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
|
||||
{
|
||||
SyncResult overallResult(SyncResult::Undefined);
|
||||
|
||||
foreach ( Folder *folder, folders ) {
|
||||
SyncResult folderResult = folder->syncResult();
|
||||
SyncResult::Status syncStatus = folderResult.status();
|
||||
|
||||
switch( syncStatus ) {
|
||||
case SyncResult::Undefined:
|
||||
if ( overallResult.status() != SyncResult::Error )
|
||||
overallResult.setStatus(SyncResult::Error);
|
||||
break;
|
||||
case SyncResult::NotYetStarted:
|
||||
overallResult.setStatus( SyncResult::NotYetStarted );
|
||||
break;
|
||||
case SyncResult::SyncPrepare:
|
||||
overallResult.setStatus( SyncResult::SyncPrepare );
|
||||
break;
|
||||
case SyncResult::SyncRunning:
|
||||
overallResult.setStatus( SyncResult::SyncRunning );
|
||||
break;
|
||||
case SyncResult::Unavailable:
|
||||
overallResult.setStatus( SyncResult::Unavailable );
|
||||
break;
|
||||
case SyncResult::Success:
|
||||
if( overallResult.status() == SyncResult::Undefined )
|
||||
overallResult.setStatus( SyncResult::Success );
|
||||
break;
|
||||
case SyncResult::Error:
|
||||
overallResult.setStatus( SyncResult::Error );
|
||||
break;
|
||||
case SyncResult::SetupError:
|
||||
if ( overallResult.status() != SyncResult::Error )
|
||||
overallResult.setStatus( SyncResult::SetupError );
|
||||
break;
|
||||
case SyncResult::Problem:
|
||||
if ( overallResult.status() != SyncResult::Problem )
|
||||
overallResult.setStatus( SyncResult::Problem );
|
||||
break;
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
}
|
||||
return overallResult;
|
||||
}
|
||||
|
||||
QString FolderMan::statusToString( SyncResult syncStatus, bool enabled ) const
|
||||
{
|
||||
QString folderMessage;
|
||||
switch( syncStatus.status() ) {
|
||||
case SyncResult::Undefined:
|
||||
folderMessage = tr( "Undefined State." );
|
||||
break;
|
||||
case SyncResult::NotYetStarted:
|
||||
folderMessage = tr( "Waits to start syncing." );
|
||||
break;
|
||||
case SyncResult::SyncPrepare:
|
||||
folderMessage = tr( "Preparing for sync." );
|
||||
break;
|
||||
case SyncResult::SyncRunning:
|
||||
folderMessage = tr( "Sync is running." );
|
||||
break;
|
||||
case SyncResult::Unavailable:
|
||||
folderMessage = tr( "Server is currently not available." );
|
||||
break;
|
||||
case SyncResult::Success:
|
||||
folderMessage = tr( "Last Sync was successful." );
|
||||
break;
|
||||
case SyncResult::Error:
|
||||
break;
|
||||
case SyncResult::Problem:
|
||||
folderMessage = tr( "Last Sync was successful, but with warnings on individual files.");
|
||||
break;
|
||||
case SyncResult::SetupError:
|
||||
folderMessage = tr( "Setup Error." );
|
||||
break;
|
||||
// no default case on purpose, check compiler warnings
|
||||
}
|
||||
if( !enabled ) {
|
||||
// sync is disabled.
|
||||
folderMessage = tr( "%1 (Sync is paused)" ).arg(folderMessage);
|
||||
}
|
||||
return folderMessage;
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QQueue>
|
||||
#include <QList>
|
||||
|
||||
#include "mirall/folder.h"
|
||||
#include "mirall/folderwatcher.h"
|
||||
@@ -25,15 +26,15 @@
|
||||
|
||||
class QSignalMapper;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class SyncResult;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class FolderMan : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FolderMan(QObject *parent = 0);
|
||||
static FolderMan* instance();
|
||||
~FolderMan();
|
||||
|
||||
int setupFolders();
|
||||
@@ -43,39 +44,44 @@ public:
|
||||
/**
|
||||
* Add a folder definition to the config
|
||||
* Params:
|
||||
* QString backend
|
||||
* QString alias
|
||||
* QString sourceFolder on local machine
|
||||
* QString targetPath on remote
|
||||
* bool onlyThisLAN, currently unused.
|
||||
*/
|
||||
void addFolderDefinition( const QString&, const QString&, const QString&, const QString&, bool );
|
||||
void addFolderDefinition(const QString&, const QString&, const QString& );
|
||||
|
||||
/**
|
||||
* return the folder by alias or NULL if no folder with the alias exists.
|
||||
*/
|
||||
/** Returns the folder by alias or NULL if no folder with the alias exists. */
|
||||
Folder *folder( const QString& );
|
||||
|
||||
/**
|
||||
* return the last sync result by alias
|
||||
*/
|
||||
/** Returns the last sync result by alias */
|
||||
SyncResult syncResult( const QString& );
|
||||
|
||||
/**
|
||||
* creates a folder for a specific configuration, identified by alias.
|
||||
*/
|
||||
/** Returns the last sync result by Folder */
|
||||
SyncResult syncResult( Folder* );
|
||||
|
||||
/** Creates a folder for a specific configuration, identified by alias. */
|
||||
Folder* setupFolderFromConfigFile(const QString & );
|
||||
|
||||
/**
|
||||
* wipes all folder defintions. No way back!
|
||||
*/
|
||||
/** Wipes all folder defintions. No way back! */
|
||||
void removeAllFolderDefinitions();
|
||||
|
||||
/**
|
||||
* Removes csync journals from all folders.
|
||||
*/
|
||||
/** Removes csync journals from all folders. */
|
||||
void wipeAllJournals();
|
||||
|
||||
/**
|
||||
* Ensures that a given directory does not contain a .csync_journal.
|
||||
*
|
||||
* @returns false if the journal could not be removed, true otherwise.
|
||||
*/
|
||||
static bool ensureJournalGone(const QString &path);
|
||||
|
||||
/** Creates a new and empty local directory. */
|
||||
bool startFromScratch( const QString& );
|
||||
|
||||
QString statusToString( SyncResult, bool enabled ) const;
|
||||
|
||||
static SyncResult accountStatus( const QList<Folder*> &folders );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* signal to indicate a folder named by alias has changed its sync state.
|
||||
@@ -92,7 +98,10 @@ public slots:
|
||||
|
||||
void slotReparseConfiguration();
|
||||
|
||||
void terminateSyncProcess( const QString& );
|
||||
void terminateSyncProcess( const QString& alias = QString::null );
|
||||
|
||||
/* delete all folder objects */
|
||||
int unloadAllFolders();
|
||||
|
||||
// if enabled is set to false, no new folders will start to sync.
|
||||
// the current one will finish.
|
||||
@@ -112,6 +121,7 @@ private:
|
||||
// and create the folders
|
||||
int setupKnownFolders();
|
||||
void terminateCurrentSync();
|
||||
QString getBackupName( const QString& ) const;
|
||||
|
||||
// Escaping of the alias which is used in QSettings AND the file
|
||||
// system, thus need to be escaped.
|
||||
@@ -120,14 +130,17 @@ private:
|
||||
|
||||
void removeFolder( const QString& );
|
||||
|
||||
FolderWatcher *_configFolderWatcher;
|
||||
Folder::Map _folderMap;
|
||||
QString _folderConfigPath;
|
||||
QSignalMapper *_folderChangeSignalMapper;
|
||||
QString _currentSyncFolder;
|
||||
QStringList _scheduleQueue;
|
||||
bool _syncEnabled;
|
||||
QQueue<QString> _scheduleQueue;
|
||||
|
||||
explicit FolderMan(QObject *parent = 0);
|
||||
static FolderMan *_instance;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace Mirall
|
||||
#endif // FOLDERMAN_H
|
||||
|
||||
304
src/mirall/folderstatusmodel.cpp
Normal file
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
*
|
||||
* 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 "mirall/folderstatusmodel.h"
|
||||
#include "mirall/utility.h"
|
||||
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
FolderStatusModel::FolderStatusModel()
|
||||
:QStandardItemModel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Qt::ItemFlags FolderStatusModel::flags ( const QModelIndex& ) const
|
||||
{
|
||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
||||
}
|
||||
|
||||
QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::EditRole)
|
||||
return QVariant();
|
||||
else
|
||||
return QStandardItemModel::data(index,role);
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
|
||||
FolderStatusDelegate::FolderStatusDelegate()
|
||||
:QStyledItemDelegate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FolderStatusDelegate::~FolderStatusDelegate()
|
||||
{
|
||||
// TODO Auto-generated destructor stub
|
||||
}
|
||||
|
||||
//alocate each item size in listview.
|
||||
QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem & option ,
|
||||
const QModelIndex & index) const
|
||||
{
|
||||
Q_UNUSED(option)
|
||||
QFont aliasFont = option.font;
|
||||
QFont font = option.font;
|
||||
aliasFont.setPointSize( font.pointSize() +2 );
|
||||
|
||||
QFontMetrics fm(font);
|
||||
QFontMetrics aliasFm(aliasFont);
|
||||
|
||||
int aliasMargin = aliasFm.height()/2;
|
||||
int margin = fm.height()/4;
|
||||
|
||||
// calc height
|
||||
|
||||
int h = aliasMargin; // margin to top
|
||||
h += aliasFm.height(); // alias
|
||||
h += margin; // between alias and local path
|
||||
h += fm.height(); // local path
|
||||
h += margin; // between local and remote path
|
||||
h += fm.height(); // remote path
|
||||
h += aliasMargin; // bottom margin
|
||||
|
||||
// add some space to show an error condition.
|
||||
if( ! qvariant_cast<QString>(index.data(FolderErrorMsg)).isEmpty() ) {
|
||||
h += aliasMargin*2+fm.height();
|
||||
}
|
||||
|
||||
if( qvariant_cast<bool>(index.data(AddProgressSpace)) ) {
|
||||
int margin = fm.height()/4;
|
||||
h += (5 * margin); // All the margins
|
||||
h += 2* fm.boundingRect(tr("File")).height();
|
||||
}
|
||||
|
||||
return QSize( 0, h);
|
||||
}
|
||||
|
||||
void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QStyledItemDelegate::paint(painter,option,index);
|
||||
|
||||
painter->save();
|
||||
|
||||
QFont aliasFont = option.font;
|
||||
QFont subFont = option.font;
|
||||
QFont errorFont = subFont;
|
||||
QFont progressFont = subFont;
|
||||
|
||||
progressFont.setPointSize( subFont.pointSize()-1);
|
||||
//font.setPixelSize(font.weight()+);
|
||||
aliasFont.setBold(true);
|
||||
aliasFont.setPointSize( subFont.pointSize()+2 );
|
||||
|
||||
QFontMetrics subFm( subFont );
|
||||
QFontMetrics aliasFm( aliasFont );
|
||||
QFontMetrics progressFm( progressFont );
|
||||
|
||||
int aliasMargin = aliasFm.height()/2;
|
||||
int margin = subFm.height()/4;
|
||||
|
||||
QIcon statusIcon = qvariant_cast<QIcon>(index.data(FolderStatusIconRole));
|
||||
QString aliasText = qvariant_cast<QString>(index.data(FolderAliasRole));
|
||||
QString pathText = qvariant_cast<QString>(index.data(FolderPathRole));
|
||||
QString remotePath = qvariant_cast<QString>(index.data(FolderSecondPathRole));
|
||||
QString errorText = qvariant_cast<QString>(index.data(FolderErrorMsg));
|
||||
|
||||
int overallPercent = qvariant_cast<int>(index.data(SyncProgressOverallPercent));
|
||||
QString overallString = qvariant_cast<QString>(index.data(SyncProgressOverallString));
|
||||
QString itemString = qvariant_cast<QString>(index.data(SyncProgressItemString));
|
||||
int warningCount = qvariant_cast<int>(index.data(WarningCount));
|
||||
bool syncOngoing = qvariant_cast<bool>(index.data(SyncRunning));
|
||||
// QString statusText = qvariant_cast<QString>(index.data(FolderStatus));
|
||||
bool syncEnabled = index.data(FolderSyncEnabled).toBool();
|
||||
// QString syncStatus = syncEnabled? tr( "Enabled" ) : tr( "Disabled" );
|
||||
|
||||
QRect iconRect = option.rect;
|
||||
QRect aliasRect = option.rect;
|
||||
|
||||
iconRect.setLeft( aliasMargin );
|
||||
iconRect.setTop( iconRect.top() + aliasMargin ); // (iconRect.height()-iconsize.height())/2);
|
||||
|
||||
// alias box
|
||||
aliasRect.setTop(aliasRect.top() + aliasMargin );
|
||||
aliasRect.setBottom(aliasRect.top() + aliasFm.height());
|
||||
aliasRect.setRight(aliasRect.right() - aliasMargin );
|
||||
|
||||
// remote directory box
|
||||
QRect remotePathRect = aliasRect;
|
||||
remotePathRect.setTop(aliasRect.bottom() + margin );
|
||||
remotePathRect.setBottom(remotePathRect.top() + subFm.height());
|
||||
|
||||
// local directory box
|
||||
QRect localPathRect = remotePathRect;
|
||||
localPathRect.setTop( remotePathRect.bottom() + margin );
|
||||
localPathRect.setBottom( localPathRect.top() + subFm.height());
|
||||
|
||||
iconRect.setBottom(localPathRect.bottom());
|
||||
iconRect.setWidth(iconRect.height());
|
||||
|
||||
int nextToIcon = iconRect.right()+aliasMargin;
|
||||
aliasRect.setLeft(nextToIcon);
|
||||
localPathRect.setLeft(nextToIcon);
|
||||
remotePathRect.setLeft(nextToIcon);
|
||||
|
||||
int iconSize = iconRect.width();
|
||||
|
||||
QPixmap pm = statusIcon.pixmap(iconSize, iconSize, syncEnabled ? QIcon::Normal : QIcon::Disabled );
|
||||
painter->drawPixmap(QPoint(iconRect.left(), iconRect.top()), pm);
|
||||
|
||||
// only show the warning icon if the sync is running. Otherwise its
|
||||
// encoded in the status icon.
|
||||
if( warningCount > 0 && syncOngoing) {
|
||||
QRect warnRect;
|
||||
warnRect.setLeft(iconRect.left());
|
||||
warnRect.setTop(iconRect.bottom()-17);
|
||||
warnRect.setWidth(16);
|
||||
warnRect.setHeight(16);
|
||||
|
||||
QIcon warnIcon(":/mirall/resources/warning-16");
|
||||
QPixmap pm = warnIcon.pixmap(16,16, syncEnabled ? QIcon::Normal : QIcon::Disabled );
|
||||
painter->drawPixmap(QPoint(warnRect.left(), warnRect.top()),pm );
|
||||
}
|
||||
|
||||
if ((option.state & QStyle::State_Selected)
|
||||
&& (option.state & QStyle::State_Active)
|
||||
// Hack: Windows Vista's light blue is not contrasting enough for white
|
||||
&& !qApp->style()->inherits("QWindowsVistaStyle")) {
|
||||
painter->setPen(option.palette.color(QPalette::HighlightedText));
|
||||
} else {
|
||||
painter->setPen(option.palette.color(QPalette::Text));
|
||||
}
|
||||
QString elidedAlias = aliasFm.elidedText(aliasText, Qt::ElideRight, aliasRect.width());
|
||||
painter->setFont(aliasFont);
|
||||
painter->drawText(aliasRect, elidedAlias);
|
||||
|
||||
painter->setFont(subFont);
|
||||
QString elidedRemotePathText;
|
||||
|
||||
if (remotePath.isEmpty() || remotePath == QLatin1String("/")) {
|
||||
elidedRemotePathText = subFm.elidedText(tr("Syncing all files in your account with"),
|
||||
Qt::ElideRight, remotePathRect.width());
|
||||
} else {
|
||||
elidedRemotePathText = subFm.elidedText(tr("Remote path: %1").arg(remotePath),
|
||||
Qt::ElideMiddle, remotePathRect.width());
|
||||
}
|
||||
painter->drawText(remotePathRect, elidedRemotePathText);
|
||||
|
||||
QString elidedPathText = subFm.elidedText(pathText, Qt::ElideMiddle, localPathRect.width());
|
||||
painter->drawText(localPathRect, elidedPathText);
|
||||
|
||||
// paint an error overlay if there is an error string
|
||||
|
||||
int h = iconRect.bottom();
|
||||
if( !errorText.isEmpty() ) {
|
||||
h += aliasMargin;
|
||||
QRect errorRect = localPathRect;
|
||||
errorRect.setLeft( iconRect.left());
|
||||
errorRect.setTop( h );
|
||||
errorRect.setHeight(subFm.height()+aliasMargin);
|
||||
errorRect.setRight( option.rect.right()-aliasMargin );
|
||||
|
||||
painter->setBrush( QColor(0xbb, 0x4d, 0x4d) );
|
||||
painter->setPen( QColor(0xaa, 0xaa, 0xaa));
|
||||
painter->drawRoundedRect( errorRect, 4, 4 );
|
||||
|
||||
painter->setPen( Qt::white );
|
||||
painter->setFont(errorFont);
|
||||
QRect errorTextRect = errorRect;
|
||||
errorTextRect.setLeft( errorTextRect.left()+aliasMargin +16);
|
||||
errorTextRect.setTop( errorTextRect.top()+aliasMargin/2 );
|
||||
|
||||
int linebreak = errorText.indexOf(QLatin1String("<br"));
|
||||
QString eText = errorText;
|
||||
if(linebreak) {
|
||||
eText = errorText.left(linebreak);
|
||||
}
|
||||
painter->drawText(errorTextRect, eText);
|
||||
|
||||
h = errorRect.bottom();
|
||||
}
|
||||
h += aliasMargin;
|
||||
|
||||
// Sync File Progress Bar: Show it if syncFile is not empty.
|
||||
if( !overallString.isEmpty()) {
|
||||
int fileNameTextHeight = subFm.boundingRect(tr("File")).height();
|
||||
int barHeight = fileNameTextHeight;
|
||||
int overallWidth = option.rect.width()-2*aliasMargin;
|
||||
|
||||
painter->save();
|
||||
|
||||
// Sizes-Text
|
||||
QRect octetRect = subFm.boundingRect( overallString );
|
||||
int progressTextWidth = octetRect.width();
|
||||
|
||||
// Overall Progress Bar.
|
||||
QRect pBRect;
|
||||
pBRect.setTop( h );
|
||||
pBRect.setLeft( iconRect.left());
|
||||
pBRect.setHeight(barHeight);
|
||||
pBRect.setWidth( overallWidth - progressTextWidth - margin );
|
||||
|
||||
QStyleOptionProgressBarV2 pBarOpt;
|
||||
pBarOpt.state = option.state | QStyle::State_Horizontal;
|
||||
pBarOpt.minimum = 0;
|
||||
pBarOpt.maximum = 100;
|
||||
pBarOpt.progress = overallPercent;
|
||||
pBarOpt.orientation = Qt::Horizontal;
|
||||
pBarOpt.palette = option.palette;
|
||||
pBarOpt.rect = pBRect;
|
||||
|
||||
QApplication::style()->drawControl( QStyle::CE_ProgressBar, &pBarOpt, painter );
|
||||
|
||||
// Overall Progress Text
|
||||
QRect overallProgressRect;
|
||||
overallProgressRect.setTop( pBRect.top() );
|
||||
overallProgressRect.setHeight( pBRect.height() );
|
||||
overallProgressRect.setLeft( pBRect.right()+margin);
|
||||
overallProgressRect.setWidth( progressTextWidth );
|
||||
painter->setFont(progressFont);
|
||||
|
||||
QString elidedText = progressFm.elidedText(overallString, Qt::ElideLeft, overallProgressRect.width());
|
||||
painter->drawText( overallProgressRect, Qt::AlignRight+Qt::AlignVCenter, elidedText);
|
||||
|
||||
// Individual File Progress
|
||||
QRect fileRect;
|
||||
fileRect.setTop( pBRect.bottom() + margin);
|
||||
fileRect.setLeft( iconRect.left());
|
||||
fileRect.setWidth(overallWidth);
|
||||
fileRect.setHeight(fileNameTextHeight);
|
||||
elidedText = progressFm.elidedText(itemString, Qt::ElideLeft, fileRect.width());
|
||||
|
||||
painter->drawText( fileRect, Qt::AlignLeft+Qt::AlignVCenter, elidedText);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
bool FolderStatusDelegate::editorEvent ( QEvent * /*event*/, QAbstractItemModel * /*model*/, const QStyleOptionViewItem & /*option*/, const QModelIndex & /*index*/ )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
66
src/mirall/folderstatusmodel.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) by Klaas Freitag <freitag@kde.org>
|
||||
*
|
||||
* 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 FOLDERSTATUSMODEL_H
|
||||
#define FOLDERSTATUSMODEL_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QStandardItemModel>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class FolderStatusModel : public QStandardItemModel
|
||||
{
|
||||
public:
|
||||
FolderStatusModel();
|
||||
virtual Qt::ItemFlags flags( const QModelIndex& ) const;
|
||||
QVariant data(const QModelIndex &index, int role) const;
|
||||
|
||||
};
|
||||
|
||||
class FolderStatusDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FolderStatusDelegate();
|
||||
virtual ~FolderStatusDelegate();
|
||||
|
||||
enum datarole { FolderAliasRole = Qt::UserRole + 100,
|
||||
FolderPathRole,
|
||||
FolderSecondPathRole,
|
||||
FolderRemotePath,
|
||||
FolderStatus,
|
||||
FolderErrorMsg,
|
||||
FolderSyncEnabled,
|
||||
FolderStatusIconRole,
|
||||
|
||||
SyncProgressOverallPercent,
|
||||
SyncProgressOverallString,
|
||||
SyncProgressItemString,
|
||||
AddProgressSpace,
|
||||
WarningCount,
|
||||
SyncRunning
|
||||
};
|
||||
void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const;
|
||||
QSize sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const;
|
||||
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
|
||||
const QModelIndex& index );
|
||||
|
||||
private:
|
||||
bool _addProgressSpace;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // FOLDERSTATUSMODEL_H
|
||||
@@ -69,7 +69,7 @@ QString FolderWatcher::root() const
|
||||
return _root;
|
||||
}
|
||||
|
||||
void FolderWatcher::setIgnoreListFile( const QString& file )
|
||||
void FolderWatcher::addIgnoreListFile( const QString& file )
|
||||
{
|
||||
if( file.isEmpty() ) return;
|
||||
|
||||
@@ -79,18 +79,12 @@ void FolderWatcher::setIgnoreListFile( const QString& file )
|
||||
|
||||
while (!infile.atEnd()) {
|
||||
QString line = QString::fromLocal8Bit( infile.readLine() ).trimmed();
|
||||
if( !line.startsWith( QLatin1Char('#') )) {
|
||||
addIgnore(line);
|
||||
if( !line.startsWith( QLatin1Char('#') ) && line.isEmpty() ) {
|
||||
_ignores.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWatcher::addIgnore(const QString &pattern)
|
||||
{
|
||||
if( pattern.isEmpty() ) return;
|
||||
_ignores.append(pattern);
|
||||
}
|
||||
|
||||
QStringList FolderWatcher::ignores() const
|
||||
{
|
||||
return _ignores;
|
||||
|
||||
@@ -43,7 +43,7 @@ class FolderWatcherPrivate;
|
||||
*/
|
||||
class FolderWatcher : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @param root Path of the root of the folder
|
||||
@@ -58,16 +58,11 @@ public:
|
||||
|
||||
/**
|
||||
* Set a file name to load a file with ignore patterns.
|
||||
*
|
||||
* Valid entries do not start with a hash sign (#)
|
||||
* and may contain wildcards
|
||||
*/
|
||||
void setIgnoreListFile( const QString& );
|
||||
|
||||
/**
|
||||
* Add an ignore pattern that will not be
|
||||
* notified
|
||||
*
|
||||
* You can use wildcards
|
||||
*/
|
||||
void addIgnore(const QString &pattern);
|
||||
void addIgnoreListFile( const QString& );
|
||||
|
||||
/**
|
||||
* If true, folderChanged() events are sent
|
||||
@@ -109,10 +104,10 @@ public slots:
|
||||
void setEventsEnabledDelayed( int );
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted when one of the paths is changed
|
||||
*/
|
||||
/** Emitted when one of the paths is changed */
|
||||
void folderChanged(const QStringList &pathList);
|
||||
/** Emitted if an error occurs */
|
||||
void error(const QString& error);
|
||||
|
||||
protected:
|
||||
void setProcessTimer();
|
||||
|
||||
@@ -38,10 +38,10 @@ FolderWatcherPrivate::FolderWatcherPrivate(FolderWatcher *p)
|
||||
|
||||
{
|
||||
_inotify = new INotify(this, standard_event_mask);
|
||||
slotAddFolderRecursive(_parent->root());
|
||||
QObject::connect(_inotify, SIGNAL(notifyEvent(int, int, const QString &)),
|
||||
this, SLOT(slotINotifyEvent(int, int, const QString &)));
|
||||
|
||||
QMetaObject::invokeMethod(this, "slotAddFolderRecursive", Q_ARG(QString, _parent->root()));
|
||||
}
|
||||
|
||||
void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path)
|
||||
@@ -49,7 +49,12 @@ void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path)
|
||||
int subdirs = 0;
|
||||
qDebug() << "(+) Watcher:" << path;
|
||||
|
||||
_inotify->addPath(path);
|
||||
if (!_inotify->addPath(path)) {
|
||||
FolderWatcher *fw = qobject_cast<FolderWatcher*>(parent());
|
||||
emit fw->error(tr("Could not monitor directories due to system limitations.\n"
|
||||
"The application will not work reliably. Please check the\n"
|
||||
"documentation for possible fixes."));
|
||||
}
|
||||
QStringList watchedFolders(_inotify->directories());
|
||||
// qDebug() << "currently watching " << watchedFolders;
|
||||
QStringListIterator subfoldersIt(FileUtils::subFoldersList(path, FileUtils::SubFolderRecursive));
|
||||
@@ -78,7 +83,7 @@ void FolderWatcherPrivate::slotAddFolderRecursive(const QString &path)
|
||||
qDebug() << " `-> and" << subdirs << "subdirectories";
|
||||
}
|
||||
|
||||
void FolderWatcherPrivate::slotINotifyEvent(int mask, int cookie, const QString &path)
|
||||
void FolderWatcherPrivate::slotINotifyEvent(int mask, int /*cookie*/, const QString &path)
|
||||
{
|
||||
int lastMask = _lastMask;
|
||||
QString lastPath = _lastPath;
|
||||
|
||||
@@ -26,6 +26,8 @@ class FolderWatcherPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
FolderWatcherPrivate(FolderWatcher *p);
|
||||
signals:
|
||||
void error(const QString& error);
|
||||
private slots:
|
||||
void slotAddFolderRecursive(const QString &path);
|
||||
void slotINotifyEvent(int mask, int cookie, const QString &path);
|
||||
|
||||
@@ -19,12 +19,15 @@
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
#include <QFileInfo>
|
||||
#include <QFileIconProvider>
|
||||
#include <QInputDialog>
|
||||
#include <QUrl>
|
||||
#include <QValidator>
|
||||
#include <QWizardPage>
|
||||
#include <QDir>
|
||||
#include <QTreeWidget>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -32,19 +35,16 @@ namespace Mirall
|
||||
{
|
||||
|
||||
FolderWizardSourcePage::FolderWizardSourcePage()
|
||||
:_folderMap(0)
|
||||
: QWizardPage()
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
registerField(QLatin1String("sourceFolder*"), _ui.localFolderLineEdit);
|
||||
_ui.localFolderLineEdit->setText( QString::fromLatin1( "%1/%2").arg( QDir::homePath() ).arg(Theme::instance()->appName() ) );
|
||||
QString defaultPath = QString::fromLatin1( "%1/%2").arg( QDir::homePath() ).arg(Theme::instance()->appName() );
|
||||
_ui.localFolderLineEdit->setText( QDir::toNativeSeparators( defaultPath ) );
|
||||
registerField(QLatin1String("alias*"), _ui.aliasLineEdit);
|
||||
_ui.aliasLineEdit->setText( Theme::instance()->appNameGUI() );
|
||||
|
||||
_ui.warnLabel->hide();
|
||||
#if QT_VERSION >= 0x040700
|
||||
_ui.localFolderLineEdit->setPlaceholderText(QApplication::translate("FolderWizardSourcePage", "/home/local1", 0, QApplication::UnicodeUTF8));
|
||||
_ui.aliasLineEdit->setPlaceholderText(QApplication::translate("FolderWizardSourcePage", "Music", 0, QApplication::UnicodeUTF8));
|
||||
#endif
|
||||
}
|
||||
|
||||
FolderWizardSourcePage::~FolderWizardSourcePage()
|
||||
@@ -64,22 +64,27 @@ void FolderWizardSourcePage::cleanupPage()
|
||||
|
||||
bool FolderWizardSourcePage::isComplete() const
|
||||
{
|
||||
QFileInfo selFile( _ui.localFolderLineEdit->text() );
|
||||
QFileInfo selFile( QDir::fromNativeSeparators(_ui.localFolderLineEdit->text()) );
|
||||
QString userInput = selFile.canonicalFilePath();
|
||||
|
||||
QString warnString;
|
||||
|
||||
bool isOk = selFile.isDir();
|
||||
if( !isOk ) {
|
||||
warnString = tr("No local directory selected!");
|
||||
warnString = tr("No local folder selected!");
|
||||
}
|
||||
|
||||
if (isOk && !selFile.isWritable()) {
|
||||
isOk = false;
|
||||
warnString += tr("You have no permission to write to the selected folder!");
|
||||
}
|
||||
|
||||
// check if the local directory isn't used yet in another ownCloud sync
|
||||
Folder::Map *map = _folderMap;
|
||||
if( ! map ) return false;
|
||||
Folder::Map map = _folderMap;
|
||||
|
||||
if( isOk ) {
|
||||
Folder::Map::const_iterator i = map->constBegin();
|
||||
while( isOk && i != map->constEnd() ) {
|
||||
Folder::Map::const_iterator i = map.constBegin();
|
||||
while( isOk && i != map.constEnd() ) {
|
||||
Folder *f = static_cast<Folder*>(i.value());
|
||||
QString folderDir = QDir( f->path() ).canonicalPath();
|
||||
if( folderDir.isEmpty() )
|
||||
@@ -94,7 +99,8 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
qDebug() << "Checking local path: " << folderDir << " <-> " << userInput;
|
||||
if( QFileInfo( f->path() ) == userInput ) {
|
||||
isOk = false;
|
||||
warnString.append( tr("The local path %1 is already an upload folder.<br/>Please pick another one!").arg(userInput) );
|
||||
warnString.append( tr("The local path %1 is already an upload folder.<br/>Please pick another one!")
|
||||
.arg(QDir::toNativeSeparators(userInput)) );
|
||||
}
|
||||
if( isOk && folderDir.startsWith( userInput )) {
|
||||
qDebug() << "A already configured folder is child of the current selected";
|
||||
@@ -117,10 +123,10 @@ bool FolderWizardSourcePage::isComplete() const
|
||||
isOk = false;
|
||||
}
|
||||
|
||||
Folder::Map::const_iterator i = map->constBegin();
|
||||
Folder::Map::const_iterator i = map.constBegin();
|
||||
bool goon = true;
|
||||
while( goon && i != map->constEnd() ) {
|
||||
Folder *f = static_cast<Folder*>(i.value());
|
||||
while( goon && i != map.constEnd() ) {
|
||||
Folder *f = i.value();
|
||||
qDebug() << "Checking local alias: " << f->alias();
|
||||
if( f ) {
|
||||
if( f->alias() == alias ) {
|
||||
@@ -148,7 +154,7 @@ void FolderWizardSourcePage::on_localFolderChooseBtn_clicked()
|
||||
tr("Select the source folder"),
|
||||
QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
|
||||
if (!dir.isEmpty()) {
|
||||
_ui.localFolderLineEdit->setText(dir);
|
||||
_ui.localFolderLineEdit->setText(QDir::toNativeSeparators(dir));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,76 +166,38 @@ void FolderWizardSourcePage::on_localFolderLineEdit_textChanged()
|
||||
|
||||
// =================================================================================
|
||||
FolderWizardTargetPage::FolderWizardTargetPage()
|
||||
: _dirChecked( false ),
|
||||
_warnWasVisible(false)
|
||||
: _warnWasVisible(false)
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
_ui.warnFrame->hide();
|
||||
|
||||
registerField(QLatin1String("local?"), _ui.localFolderRadioBtn);
|
||||
registerField(QLatin1String("remote?"), _ui.urlFolderRadioBtn);
|
||||
registerField(QLatin1String("OC?"), _ui.OCRadioBtn);
|
||||
registerField(QLatin1String("targetLocalFolder"), _ui.localFolder2LineEdit);
|
||||
registerField(QLatin1String("targetURLFolder"), _ui.urlFolderLineEdit);
|
||||
registerField(QLatin1String("targetOCFolder"), _ui.OCFolderLineEdit);
|
||||
|
||||
connect( _ui.OCFolderLineEdit, SIGNAL(textChanged(QString)),
|
||||
SLOT(slotFolderTextChanged(QString)));
|
||||
|
||||
_timer = new QTimer(this);
|
||||
_timer->setSingleShot( true );
|
||||
connect( _timer, SIGNAL(timeout()), SLOT(slotTimerFires()));
|
||||
|
||||
#if QT_Version >= 0x040700
|
||||
_ui.OCFolderLineEdit->setPlaceholderText(QApplication::translate("FolderWizardTargetPage", "root", 0, QApplication::UnicodeUTF8));
|
||||
_ui.localFolder2LineEdit->setPlaceholderText(QApplication::translate("FolderWizardTargetPage", "/home/local", 0, QApplication::UnicodeUTF8));
|
||||
_ui.urlFolderLineEdit->setPlaceholderText(QApplication::translate("FolderWizardTargetPage", "scp://john@host.com//myfolder", 0, QApplication::UnicodeUTF8));
|
||||
#endif
|
||||
connect(_ui.addFolderButton, SIGNAL(clicked()), SLOT(slotAddRemoteFolder()));
|
||||
connect(_ui.refreshButton, SIGNAL(clicked()), SLOT(slotRefreshFolders()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemActivated(QTreeWidgetItem*,int)), SIGNAL(completeChanged()));
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotFolderTextChanged( const QString& t)
|
||||
void FolderWizardTargetPage::slotAddRemoteFolder()
|
||||
{
|
||||
_dirChecked = false;
|
||||
emit completeChanged();
|
||||
QTreeWidgetItem *current = _ui.folderTreeWidget->currentItem();
|
||||
|
||||
if( t.isEmpty() ) {
|
||||
_timer->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if( _timer->isActive() ) _timer->stop();
|
||||
_timer->start(500);
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotTimerFires()
|
||||
{
|
||||
const QString folder = _ui.OCFolderLineEdit->text();
|
||||
qDebug() << "Querying folder " << folder;
|
||||
ownCloudInfo::instance()->getWebDAVPath( folder );
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotDirCheckReply(const QString &url, QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << "Got reply from owncloud dir check: " << url << " :" << reply->error();
|
||||
_dirChecked = (reply->error() == QNetworkReply::NoError);
|
||||
if( _dirChecked ) {
|
||||
showWarn();
|
||||
} else {
|
||||
showWarn( tr("The folder is not available on your %1.<br/>Click to create it." )
|
||||
.arg( Theme::instance()->appNameGUI() ), true );
|
||||
QString parent('/');
|
||||
if (current) {
|
||||
parent = current->data(0, Qt::UserRole).toString();
|
||||
}
|
||||
|
||||
emit completeChanged();
|
||||
QInputDialog *dlg = new QInputDialog(this);
|
||||
|
||||
dlg->setWindowTitle(tr("Add Remote Folder"));
|
||||
dlg->setLabelText(tr("Enter the name of the new folder:"));
|
||||
dlg->setTextValue(parent);
|
||||
dlg->open(this, SLOT(slotCreateRemoteFolder(QString)));
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolder()
|
||||
void FolderWizardTargetPage::slotCreateRemoteFolder(QString folder)
|
||||
{
|
||||
_ui.OCFolderLineEdit->setEnabled( false );
|
||||
|
||||
const QString folder = _ui.OCFolderLineEdit->text();
|
||||
if( folder.isEmpty() ) return;
|
||||
|
||||
qDebug() << "creating folder on ownCloud: " << folder;
|
||||
ownCloudInfo::instance()->mkdirRequest( folder );
|
||||
}
|
||||
|
||||
@@ -237,43 +205,106 @@ void FolderWizardTargetPage::slotCreateRemoteFolderFinished( QNetworkReply::Netw
|
||||
{
|
||||
qDebug() << "** webdav mkdir request finished " << error;
|
||||
|
||||
_ui.OCFolderLineEdit->setEnabled( true );
|
||||
// the webDAV server seems to return a 202 even if mkdir was successful.
|
||||
if( error == QNetworkReply::NoError ||
|
||||
error == QNetworkReply::ContentOperationNotPermittedError) {
|
||||
showWarn( tr("Folder was successfully created on %1.").arg( Theme::instance()->appNameGUI() ), false );
|
||||
slotTimerFires();
|
||||
showWarn( tr("Folder was successfully created on %1.").arg( Theme::instance()->appNameGUI() ) );
|
||||
slotRefreshFolders();
|
||||
} else {
|
||||
showWarn( tr("Failed to create the folder on %1.<br/>Please check manually.").arg( Theme::instance()->appNameGUI() ), false );
|
||||
showWarn( tr("Failed to create the folder on %1.<br/>Please check manually.").arg( Theme::instance()->appNameGUI() ) );
|
||||
}
|
||||
}
|
||||
|
||||
static QTreeWidgetItem* findFirstChild(QTreeWidgetItem *parent, const QString& text)
|
||||
{
|
||||
for (int i = 0; i < parent->childCount(); ++i) {
|
||||
QTreeWidgetItem *child = parent->child(i);
|
||||
if (child->text(0) == text) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void recursiveInsert(QTreeWidgetItem *parent, QStringList pathTrail, QString path)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
if (pathTrail.size() == 0) {
|
||||
if (path.endsWith('/')) {
|
||||
path.chop(1);
|
||||
}
|
||||
parent->setToolTip(0, path);
|
||||
parent->setData(0, Qt::UserRole, path);
|
||||
} else {
|
||||
QTreeWidgetItem *item = findFirstChild(parent, pathTrail.first());
|
||||
if (!item) {
|
||||
item = new QTreeWidgetItem(parent);
|
||||
item->setIcon(0, folderIcon);
|
||||
item->setText(0, pathTrail.first());
|
||||
item->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator);
|
||||
}
|
||||
|
||||
pathTrail.removeFirst();
|
||||
recursiveInsert(item, pathTrail, path);
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotUpdateDirectories(QStringList list)
|
||||
{
|
||||
QFileIconProvider prov;
|
||||
QIcon folderIcon = prov.icon(QFileIconProvider::Folder);
|
||||
|
||||
QString webdavFolder = QUrl(ownCloudInfo::instance()->webdavUrl()).path();
|
||||
connect(_ui.folderTreeWidget, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(slotItemExpanded(QTreeWidgetItem*)));
|
||||
|
||||
QTreeWidgetItem *root = _ui.folderTreeWidget->topLevelItem(0);
|
||||
if (!root) {
|
||||
root = new QTreeWidgetItem(_ui.folderTreeWidget);
|
||||
root->setText(0, tr("Root (\"/\")", "root folder"));
|
||||
root->setIcon(0, folderIcon);
|
||||
root->setToolTip(0, tr("Choose this to sync the entire account"));
|
||||
root->setData(0, Qt::UserRole, "/");
|
||||
}
|
||||
foreach (QString path, list) {
|
||||
path.remove(webdavFolder);
|
||||
QStringList paths = path.split('/');
|
||||
if (paths.last().isEmpty()) paths.removeLast();
|
||||
recursiveInsert(root, paths, path);
|
||||
}
|
||||
root->setExpanded(true);
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotRefreshFolders()
|
||||
{
|
||||
ownCloudInfo::instance()->getDirectoryListing("/");
|
||||
_ui.folderTreeWidget->clear();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotItemExpanded(QTreeWidgetItem *item)
|
||||
{
|
||||
ownCloudInfo::instance()->getDirectoryListing(item->text(0));
|
||||
}
|
||||
|
||||
FolderWizardTargetPage::~FolderWizardTargetPage()
|
||||
{
|
||||
}
|
||||
|
||||
bool FolderWizardTargetPage::isComplete() const
|
||||
{
|
||||
if (_ui.localFolderRadioBtn->isChecked()) {
|
||||
return QFileInfo(_ui.localFolder2LineEdit->text()).isDir();
|
||||
} else if (_ui.urlFolderRadioBtn->isChecked()) {
|
||||
QUrl url(_ui.urlFolderLineEdit->text());
|
||||
return url.isValid() && (url.scheme() == QLatin1String("sftp")
|
||||
|| url.scheme() == QLatin1String("smb"));
|
||||
} else if( _ui.OCRadioBtn->isChecked()) {
|
||||
/* owncloud selected */
|
||||
QString dir = _ui.OCFolderLineEdit->text();
|
||||
if( dir.isEmpty() ) {
|
||||
showWarn( tr("Better do not use the remote root directory.<br/>If you do, you can <b>not</b> mirror another local folder."), false);
|
||||
if (!_ui.folderTreeWidget->currentItem())
|
||||
return false;
|
||||
|
||||
QString dir = _ui.folderTreeWidget->currentItem()->data(0, Qt::UserRole).toString();
|
||||
wizard()->setProperty("targetPath", dir);
|
||||
|
||||
if( dir == QLatin1String("/") ) {
|
||||
showWarn( tr("If you sync the root folder, you can <b>not</b> configure another sync directory."));
|
||||
return true;
|
||||
} else {
|
||||
showWarn();
|
||||
return true;
|
||||
} else {
|
||||
if( _dirChecked ) {
|
||||
showWarn();
|
||||
}
|
||||
return _dirChecked;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::cleanupPage()
|
||||
@@ -283,64 +314,24 @@ void FolderWizardTargetPage::cleanupPage()
|
||||
|
||||
void FolderWizardTargetPage::initializePage()
|
||||
{
|
||||
slotToggleItems();
|
||||
showWarn();
|
||||
|
||||
/* check the owncloud configuration file and query the ownCloud */
|
||||
ownCloudInfo *ocInfo = ownCloudInfo::instance();
|
||||
if( ocInfo->isConfigured() ) {
|
||||
connect( ocInfo, SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
|
||||
SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
|
||||
connect( ocInfo, SIGNAL(noOwncloudFound(QNetworkReply*)),
|
||||
SLOT(slotNoOwnCloudFound(QNetworkReply*)));
|
||||
connect( ocInfo, SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
SLOT(slotDirCheckReply(QString,QNetworkReply*)));
|
||||
connect( ocInfo, SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
SLOT(slotCreateRemoteFolderFinished( QNetworkReply::NetworkError )));
|
||||
connect( ocInfo, SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
|
||||
SLOT(slotDirCheckReply(QString,QNetworkReply*)));
|
||||
connect( ocInfo, SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
|
||||
SLOT(slotCreateRemoteFolderFinished( QNetworkReply::NetworkError )));
|
||||
connect( ocInfo, SIGNAL(directoryListingUpdated(QStringList)),
|
||||
SLOT(slotUpdateDirectories(QStringList)));
|
||||
|
||||
connect(_ui._buttCreateFolder, SIGNAL(clicked()), SLOT(slotCreateRemoteFolder()));
|
||||
ocInfo->checkInstallation();
|
||||
|
||||
} else {
|
||||
_ui.OCRadioBtn->setEnabled( false );
|
||||
_ui.OCFolderLineEdit->setEnabled( false );
|
||||
}
|
||||
|
||||
QString dir = _ui.OCFolderLineEdit->text();
|
||||
if( !dir.isEmpty() ) {
|
||||
slotFolderTextChanged( dir );
|
||||
slotRefreshFolders();
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotOwnCloudFound( const QString& url, const QString& infoStr, const QString& version, const QString& edition)
|
||||
void FolderWizardTargetPage::showWarn( const QString& msg ) const
|
||||
{
|
||||
Q_UNUSED(version);
|
||||
Q_UNUSED(edition);
|
||||
|
||||
if( infoStr.isEmpty() ) {
|
||||
} else {
|
||||
_ui.OCLabel->setText( tr("to your <a href=\"%1\">%2</a> (version %3)").arg(url)
|
||||
.arg(Theme::instance()->appNameGUI()).arg(infoStr));
|
||||
_ui.OCFolderLineEdit->setEnabled( true );
|
||||
_ui.OCRadioBtn->setEnabled( true );
|
||||
qDebug() << "ownCloud found on " << url << " with version: " << infoStr;
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotNoOwnCloudFound( QNetworkReply* error )
|
||||
{
|
||||
qDebug() << "No ownCloud configured: " << error->error();
|
||||
_ui.OCLabel->setText( tr("no configured %1 found!").arg(Theme::instance()->appNameGUI()) );
|
||||
showWarn( tr("%1 could not be reached:<br/><tt>%2</tt>")
|
||||
.arg(Theme::instance()->appNameGUI()).arg(error->errorString()));
|
||||
_ui.OCRadioBtn->setEnabled( false );
|
||||
_ui.OCFolderLineEdit->setEnabled( false );
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::showWarn( const QString& msg, bool showCreateButton ) const
|
||||
{
|
||||
_ui._buttCreateFolder->setVisible( showCreateButton && !msg.isEmpty() );
|
||||
|
||||
if( msg.isEmpty() ) {
|
||||
_ui.warnFrame->hide();
|
||||
|
||||
@@ -350,146 +341,35 @@ void FolderWizardTargetPage::showWarn( const QString& msg, bool showCreateButton
|
||||
}
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::on_localFolderRadioBtn_toggled()
|
||||
{
|
||||
slotToggleItems();
|
||||
emit completeChanged();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::on_urlFolderRadioBtn_toggled()
|
||||
{
|
||||
slotToggleItems();
|
||||
emit completeChanged();
|
||||
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::on_localFolder2LineEdit_textChanged()
|
||||
{
|
||||
emit completeChanged();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::on_urlFolderLineEdit_textChanged()
|
||||
{
|
||||
emit completeChanged();
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::slotToggleItems()
|
||||
{
|
||||
|
||||
bool enabled = _ui.localFolderRadioBtn->isChecked();
|
||||
_ui.localFolder2LineEdit->setEnabled(enabled);
|
||||
_ui.localFolder2ChooseBtn->setEnabled(enabled);
|
||||
if( enabled ) {
|
||||
_warnWasVisible = _ui.warnFrame->isVisible();
|
||||
_ui.warnFrame->hide();
|
||||
}
|
||||
|
||||
enabled = _ui.urlFolderRadioBtn->isChecked();
|
||||
_ui.urlFolderLineEdit->setEnabled(enabled);
|
||||
if( enabled ) {
|
||||
_warnWasVisible = _ui.warnFrame->isVisible();
|
||||
_ui.warnFrame->hide();
|
||||
}
|
||||
|
||||
enabled = _ui.OCRadioBtn->isChecked();
|
||||
_ui.OCFolderLineEdit->setEnabled(enabled);
|
||||
if( enabled ) _ui.warnFrame->setVisible( _warnWasVisible );
|
||||
}
|
||||
|
||||
void FolderWizardTargetPage::on_localFolder2ChooseBtn_clicked()
|
||||
{
|
||||
QString dir = QFileDialog::getExistingDirectory(this,
|
||||
tr("Select the target folder"),
|
||||
QDesktopServices::storageLocation(QDesktopServices::HomeLocation));
|
||||
if (!dir.isEmpty()) {
|
||||
_ui.localFolder2LineEdit->setText(dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ====================================================================================
|
||||
|
||||
FolderWizardNetworkPage::FolderWizardNetworkPage()
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
registerField(QLatin1String("onlyNetwork*"), _ui.checkBoxOnlyOnline);
|
||||
registerField(QLatin1String("onlyLocalNetwork*"), _ui.checkBoxOnlyThisLAN );
|
||||
}
|
||||
|
||||
FolderWizardNetworkPage::~FolderWizardNetworkPage()
|
||||
{
|
||||
}
|
||||
|
||||
bool FolderWizardNetworkPage::isComplete() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
FolderWizardOwncloudPage::FolderWizardOwncloudPage()
|
||||
{
|
||||
_ui.setupUi(this);
|
||||
registerField(QLatin1String("OCUrl*"), _ui.lineEditOCUrl);
|
||||
registerField(QLatin1String("OCUser*"), _ui.lineEditOCUser );
|
||||
registerField(QLatin1String("OCPasswd"), _ui.lineEditOCPasswd);
|
||||
registerField(QLatin1String("OCSiteAlias*"), _ui.lineEditOCAlias);
|
||||
}
|
||||
|
||||
FolderWizardOwncloudPage::~FolderWizardOwncloudPage()
|
||||
{
|
||||
}
|
||||
|
||||
void FolderWizardOwncloudPage::initializePage()
|
||||
{
|
||||
_ui.lineEditOCAlias->setText( QLatin1String("ownCloud") );
|
||||
_ui.lineEditOCUrl->setText( QLatin1String("http://localhost/owncloud") );
|
||||
QString user = QString::fromLocal8Bit(qgetenv("USER"));
|
||||
_ui.lineEditOCUser->setText( user );
|
||||
}
|
||||
|
||||
bool FolderWizardOwncloudPage::isComplete() const
|
||||
{
|
||||
|
||||
bool hasAlias = !(_ui.lineEditOCAlias->text().isEmpty());
|
||||
QUrl u( _ui.lineEditOCUrl->text() );
|
||||
bool hasUrl = u.isValid();
|
||||
|
||||
return hasAlias && hasUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Folder wizard itself
|
||||
*/
|
||||
|
||||
FolderWizard::FolderWizard( QWidget *parent )
|
||||
: QWizard(parent),
|
||||
_folderWizardSourcePage(0),
|
||||
_folderWizardSourcePage(new FolderWizardSourcePage),
|
||||
_folderWizardTargetPage(0)
|
||||
{
|
||||
_folderWizardSourcePage = new FolderWizardSourcePage();
|
||||
setPage(Page_Source, _folderWizardSourcePage );
|
||||
setPage(Page_Source, _folderWizardSourcePage );
|
||||
if (!Theme::instance()->singleSyncFolder()) {
|
||||
_folderWizardTargetPage = new FolderWizardTargetPage();
|
||||
setPage(Page_Target, _folderWizardTargetPage );
|
||||
}
|
||||
|
||||
setWindowTitle( tr( "%1 Folder Wizard" ).arg( Theme::instance()->appNameGUI() ) );
|
||||
#ifdef Q_WS_MAC
|
||||
setWizardStyle( QWizard::ModernStyle );
|
||||
#endif
|
||||
setWindowTitle( tr("Add Folder") );
|
||||
setOptions(QWizard::CancelButtonOnLeft);
|
||||
setButtonText(QWizard::FinishButton, tr("Add Folder"));
|
||||
}
|
||||
|
||||
FolderWizard::~FolderWizard()
|
||||
{
|
||||
delete _folderWizardSourcePage;
|
||||
if( _folderWizardTargetPage )
|
||||
delete _folderWizardTargetPage;
|
||||
}
|
||||
|
||||
void FolderWizard::setFolderMap( Folder::Map *fm)
|
||||
void FolderWizard::setFolderMap( const Folder::Map& fm)
|
||||
{
|
||||
if( _folderWizardSourcePage ) {
|
||||
_folderWizardSourcePage->setFolderMap( fm );
|
||||
}
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
@@ -23,14 +23,10 @@
|
||||
|
||||
#include "ui_folderwizardsourcepage.h"
|
||||
#include "ui_folderwizardtargetpage.h"
|
||||
#include "ui_folderwizardnetworkpage.h"
|
||||
#include "ui_folderwizardowncloudpage.h"
|
||||
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
class ownCloudInfo;
|
||||
class ownCloudDirCheck;
|
||||
|
||||
/**
|
||||
* page to ask for the local source folder
|
||||
@@ -46,14 +42,14 @@ public:
|
||||
void initializePage();
|
||||
void cleanupPage();
|
||||
|
||||
void setFolderMap( Folder::Map *fm ) { _folderMap = fm; }
|
||||
void setFolderMap( const Folder::Map &fm ) { _folderMap = fm; }
|
||||
protected slots:
|
||||
void on_localFolderChooseBtn_clicked();
|
||||
void on_localFolderLineEdit_textChanged();
|
||||
|
||||
private:
|
||||
Ui_FolderWizardSourcePage _ui;
|
||||
Folder::Map *_folderMap;
|
||||
Folder::Map _folderMap;
|
||||
};
|
||||
|
||||
|
||||
@@ -74,65 +70,21 @@ public:
|
||||
virtual void cleanupPage();
|
||||
|
||||
protected slots:
|
||||
void slotToggleItems();
|
||||
void on_localFolder2ChooseBtn_clicked();
|
||||
|
||||
void on_localFolderRadioBtn_toggled();
|
||||
void on_urlFolderRadioBtn_toggled();
|
||||
|
||||
void on_localFolder2LineEdit_textChanged();
|
||||
void on_urlFolderLineEdit_textChanged();
|
||||
|
||||
void slotOwnCloudFound( const QString&, const QString&, const QString&, const QString& );
|
||||
void slotNoOwnCloudFound(QNetworkReply*);
|
||||
|
||||
void slotFolderTextChanged( const QString& );
|
||||
void slotTimerFires();
|
||||
void slotDirCheckReply( const QString&, QNetworkReply* );
|
||||
void showWarn( const QString& = QString(), bool showCreateButton = false ) const;
|
||||
void slotCreateRemoteFolder();
|
||||
void showWarn( const QString& = QString() ) const;
|
||||
void slotAddRemoteFolder();
|
||||
void slotCreateRemoteFolder(QString);
|
||||
void slotCreateRemoteFolderFinished( QNetworkReply::NetworkError error );
|
||||
|
||||
void slotUpdateDirectories(QStringList);
|
||||
void slotRefreshFolders();
|
||||
void slotItemExpanded(QTreeWidgetItem*);
|
||||
private:
|
||||
Ui_FolderWizardTargetPage _ui;
|
||||
QTimer *_timer;
|
||||
ownCloudInfo *_ownCloudDirCheck;
|
||||
bool _dirChecked;
|
||||
bool _warnWasVisible;
|
||||
};
|
||||
|
||||
class FolderWizardNetworkPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FolderWizardNetworkPage();
|
||||
~FolderWizardNetworkPage();
|
||||
|
||||
virtual bool isComplete() const;
|
||||
|
||||
protected slots:
|
||||
|
||||
private:
|
||||
Ui_FolderWizardNetworkPage _ui;
|
||||
};
|
||||
|
||||
class FolderWizardOwncloudPage : public QWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
FolderWizardOwncloudPage();
|
||||
~FolderWizardOwncloudPage();
|
||||
|
||||
virtual bool isComplete() const;
|
||||
void initializePage();
|
||||
|
||||
protected slots:
|
||||
|
||||
private:
|
||||
Ui_FolderWizardOwncloudPage _ui;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -143,14 +95,12 @@ public:
|
||||
|
||||
enum {
|
||||
Page_Source,
|
||||
Page_Target,
|
||||
Page_Network,
|
||||
Page_Owncloud
|
||||
Page_Target
|
||||
};
|
||||
|
||||
FolderWizard(QWidget *parent = 0);
|
||||
~FolderWizard();
|
||||
void setFolderMap( Folder::Map* );
|
||||
void setFolderMap( const Folder::Map &map );
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FolderWizardNetworkPage</class>
|
||||
<widget class="QWidget" name="FolderWizardNetworkPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>435</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Network Settings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Set networking options:</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxOnlyOnline">
|
||||
<property name="text">
|
||||
<string>Only enable if network is available</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="checkBoxOnlyThisLAN">
|
||||
<property name="text">
|
||||
<string>restrict to this local network</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>172</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>FolderWizardOwncloudPage</class>
|
||||
<widget class="QWidget" name="FolderWizardOwncloudPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>435</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>ownCloud Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="lineEditOCUrl"/>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>User:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="lineEditOCUser"/>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="lineEditOCPasswd">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEditOCAlias"/>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Alias:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>172</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>ownCloud</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -7,29 +7,30 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>423</width>
|
||||
<height>226</height>
|
||||
<height>155</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Sync Directory</string>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>349</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Pick a local folder on your computer to sync:</string>
|
||||
<string>Pick a local folder on your computer to sync</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
@@ -66,7 +67,7 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="warnLabel">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
@@ -152,19 +153,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>349</width>
|
||||
<height>83</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
||||
@@ -7,269 +7,14 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>367</height>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Synchronization Target</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_17">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Select a destination folder for your data</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_4">
|
||||
<item row="0" column="1">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="OCLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>on your &ownCloud server</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>OCRadioBtn</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Remote folder:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="OCFolderLineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap>:/mirall/resources/folder-grey-32.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="LocalFolderLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>to a &local folder</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>localFolderRadioBtn</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Folder name:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLineEdit" name="localFolder2LineEdit"/>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QPushButton" name="localFolder2ChooseBtn">
|
||||
<property name="text">
|
||||
<string>C&hoose..</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0" rowspan="2">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../mirall.qrc">:/mirall/resources/folder-remote-32.png</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QLabel" name="OCLabel_3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>to a &remote folder (URL)</string>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>urlFolderRadioBtn</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLineEdit" name="urlFolderLineEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="OCRadioBtn">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="localFolderRadioBtn">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QRadioButton" name="urlFolderRadioBtn">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QFrame" name="warnFrame">
|
||||
<property name="palette">
|
||||
<palette>
|
||||
@@ -367,30 +112,77 @@
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="_buttCreateFolder">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>create</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="0" column="0">
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Select a destination folder</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="addFolderButton">
|
||||
<property name="text">
|
||||
<string>Add Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="refreshButton">
|
||||
<property name="text">
|
||||
<string>Refresh</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="4">
|
||||
<widget class="QTreeWidget" name="folderTreeWidget">
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="headerHidden">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<column>
|
||||
<property name="text">
|
||||
<string>Folders</string>
|
||||
</property>
|
||||
</column>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
@@ -398,15 +190,13 @@
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>102</height>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../mirall.qrc"/>
|
||||
</resources>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
97
src/mirall/generalsettings.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 "generalsettings.h"
|
||||
#include "ui_generalsettings.h"
|
||||
|
||||
#include "mirall/theme.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
#include "mirall/application.h"
|
||||
#include "mirall/utility.h"
|
||||
#include "mirall/mirallconfigfile.h"
|
||||
|
||||
#include <QNetworkProxy>
|
||||
#include <QDir>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
GeneralSettings::GeneralSettings(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
_ui(new Ui::GeneralSettings)
|
||||
{
|
||||
_ui->setupUi(this);
|
||||
|
||||
connect(_ui->desktopNotificationsCheckBox, SIGNAL(toggled(bool)),
|
||||
SLOT(slotToggleOptionalDesktopNotifications(bool)));
|
||||
|
||||
_ui->autostartCheckBox->setChecked(Utility::hasLaunchOnStartup(Theme::instance()->appName()));
|
||||
connect(_ui->autostartCheckBox, SIGNAL(toggled(bool)), SLOT(slotToggleLaunchOnStartup(bool)));
|
||||
|
||||
// setup about section
|
||||
QString about = Theme::instance()->about();
|
||||
if (about.isEmpty()) {
|
||||
_ui->aboutGroupBox->hide();
|
||||
} else {
|
||||
_ui->aboutLabel->setText(about);
|
||||
_ui->aboutLabel->setOpenExternalLinks(true);
|
||||
}
|
||||
|
||||
loadMiscSettings();
|
||||
|
||||
// misc
|
||||
connect(_ui->monoIconsCheckBox, SIGNAL(toggled(bool)), SLOT(saveMiscSettings()));
|
||||
|
||||
// OEM themes are not obliged to ship mono icons, so there
|
||||
// is no point in offering an option
|
||||
QString themeDir = QString::fromLatin1(":/mirall/theme/1%1/")
|
||||
.arg(Theme::instance()->systrayIconFlavor(true));
|
||||
if (QDir(themeDir).exists())
|
||||
{
|
||||
_ui->monoIconsCheckBox->setVisible(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GeneralSettings::~GeneralSettings()
|
||||
{
|
||||
delete _ui;
|
||||
}
|
||||
|
||||
void GeneralSettings::loadMiscSettings()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
_ui->monoIconsCheckBox->setChecked(cfgFile.monoIcons());
|
||||
_ui->desktopNotificationsCheckBox->setChecked(cfgFile.optionalDesktopNotifications());
|
||||
}
|
||||
|
||||
void GeneralSettings::saveMiscSettings()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
bool isChecked = _ui->monoIconsCheckBox->isChecked();
|
||||
cfgFile.setMonoIcons(isChecked);
|
||||
Theme::instance()->setSystrayUseMonoIcons(isChecked);
|
||||
}
|
||||
|
||||
void GeneralSettings::slotToggleLaunchOnStartup(bool enable)
|
||||
{
|
||||
Theme *theme = Theme::instance();
|
||||
Utility::setLaunchOnStartup(theme->appName(), theme->appNameGUI(), enable);
|
||||
}
|
||||
|
||||
void GeneralSettings::slotToggleOptionalDesktopNotifications(bool enable)
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
cfgFile.setOptionalDesktopNotifications(enable);
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
50
src/mirall/generalsettings.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 MIRALL_GENERALSETTINGS_H
|
||||
#define MIRALL_GENERALSETTINGS_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
namespace Ui {
|
||||
class GeneralSettings;
|
||||
}
|
||||
|
||||
class GeneralSettings : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GeneralSettings(QWidget *parent = 0);
|
||||
~GeneralSettings();
|
||||
|
||||
signals:
|
||||
void proxySettingsChanged();
|
||||
|
||||
private slots:
|
||||
void saveMiscSettings();
|
||||
void slotToggleLaunchOnStartup(bool);
|
||||
void slotToggleOptionalDesktopNotifications(bool);
|
||||
|
||||
private:
|
||||
void loadMiscSettings();
|
||||
|
||||
Ui::GeneralSettings *_ui;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Mirall
|
||||
#endif // MIRALL_GENERALSETTINGS_H
|
||||
83
src/mirall/generalsettings.ui
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Mirall::GeneralSettings</class>
|
||||
<widget class="QWidget" name="Mirall::GeneralSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>468</width>
|
||||
<height>169</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="generalGroupBox">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="autostartCheckBox">
|
||||
<property name="text">
|
||||
<string>Launch on System Startup</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="desktopNotificationsCheckBox">
|
||||
<property name="text">
|
||||
<string>Show Desktop Notifications</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QCheckBox" name="monoIconsCheckBox">
|
||||
<property name="text">
|
||||
<string>Use Monochrome Icons</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="aboutGroupBox">
|
||||
<property name="title">
|
||||
<string>About</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="aboutLabel">
|
||||
<property name="text">
|
||||
<string>About</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
<buttongroups>
|
||||
<buttongroup name="proxyButtonGroup"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
167
src/mirall/ignorelisteditor.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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/mirallconfigfile.h"
|
||||
|
||||
#include "ignorelisteditor.h"
|
||||
#include "ui_ignorelisteditor.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QListWidget>
|
||||
#include <QListWidgetItem>
|
||||
#include <QColorGroup>
|
||||
#include <QMessageBox>
|
||||
#include <QInputDialog>
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
IgnoreListEditor::IgnoreListEditor(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::IgnoreListEditor)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->descriptionLabel->setText(tr("Files or directories matching a pattern will not be synchronized.\n\n"
|
||||
"Checked items will also be deleted if they prevent a directory from "
|
||||
"being removed. This is useful for meta data."));
|
||||
|
||||
MirallConfigFile cfgFile;
|
||||
readIgnoreFile(cfgFile.excludeFile(MirallConfigFile::SystemScope), true);
|
||||
readIgnoreFile(cfgFile.excludeFile(MirallConfigFile::UserScope), false);
|
||||
|
||||
connect(this, SIGNAL(accepted()), SLOT(slotUpdateLocalIgnoreList()));
|
||||
ui->removePushButton->setEnabled(false);
|
||||
connect(ui->listWidget, SIGNAL(itemSelectionChanged()), SLOT(slotItemSelectionChanged()));
|
||||
connect(ui->listWidget, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(slotItemChanged(QListWidgetItem*)));
|
||||
connect(ui->removePushButton, SIGNAL(clicked()), SLOT(slotRemoveCurrentItem()));
|
||||
connect(ui->addPushButton, SIGNAL(clicked()), SLOT(slotAddPattern()));
|
||||
connect(ui->listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(slotEditPattern(QListWidgetItem*)));
|
||||
}
|
||||
|
||||
static void setupItemFlags(QListWidgetItem* item)
|
||||
{
|
||||
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsSelectable|Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
}
|
||||
|
||||
IgnoreListEditor::~IgnoreListEditor()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void IgnoreListEditor::slotItemSelectionChanged()
|
||||
{
|
||||
QListWidgetItem *item = ui->listWidget->currentItem();
|
||||
if (!item) {
|
||||
ui->removePushButton->setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool enable = item->flags() & Qt::ItemIsEnabled;
|
||||
ui->removePushButton->setEnabled(enable);
|
||||
}
|
||||
|
||||
void IgnoreListEditor::slotRemoveCurrentItem()
|
||||
{
|
||||
delete ui->listWidget->currentItem();
|
||||
}
|
||||
|
||||
void IgnoreListEditor::slotUpdateLocalIgnoreList()
|
||||
{
|
||||
MirallConfigFile cfgFile;
|
||||
QString ignoreFile = cfgFile.excludeFile(MirallConfigFile::UserScope);
|
||||
QFile ignores(ignoreFile);
|
||||
if (ignores.open(QIODevice::WriteOnly)) {
|
||||
for(int i = 0; i < ui->listWidget->count(); ++i) {
|
||||
QListWidgetItem *item = ui->listWidget->item(i);
|
||||
if (item->flags() & Qt::ItemIsEnabled) {
|
||||
QByteArray prepend;
|
||||
if (item->checkState() == Qt::Checked) {
|
||||
prepend = "]";
|
||||
}
|
||||
ignores.write(prepend+item->text().toUtf8()+'\n');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Could not open file"),
|
||||
tr("Cannot write changes to '%1'.").arg(ignoreFile));
|
||||
}
|
||||
}
|
||||
|
||||
void IgnoreListEditor::slotAddPattern()
|
||||
{
|
||||
bool okClicked;
|
||||
QString pattern = QInputDialog::getText(this, tr("Add Ignore Pattern"),
|
||||
tr("Add a new ignore pattern:"),
|
||||
QLineEdit::Normal, QString(), &okClicked);
|
||||
|
||||
if (!okClicked || pattern.isEmpty())
|
||||
return;
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem;
|
||||
setupItemFlags(item);
|
||||
if (pattern.startsWith("]")) {
|
||||
pattern = pattern.mid(1);
|
||||
item->setCheckState(Qt::Checked);
|
||||
}
|
||||
item->setText(pattern);
|
||||
ui->listWidget->addItem(item);
|
||||
ui->listWidget->scrollToItem(item);
|
||||
}
|
||||
|
||||
void IgnoreListEditor::slotEditPattern(QListWidgetItem *item)
|
||||
{
|
||||
if (!(item->flags() & Qt::ItemIsEnabled))
|
||||
return;
|
||||
|
||||
QString pattern = QInputDialog::getText(this, tr("Add Ignore Pattern"),
|
||||
tr("Add a new ignore pattern:"),
|
||||
QLineEdit::Normal, item->text());
|
||||
if (!pattern.isEmpty()) {
|
||||
item->setText(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
void IgnoreListEditor::readIgnoreFile(const QString &file, bool readOnly)
|
||||
{
|
||||
|
||||
MirallConfigFile cfgFile;
|
||||
const QString disabledTip(tr("This entry is provided by the system at '%1' "
|
||||
"and cannot be modified in this view.")
|
||||
.arg(QDir::toNativeSeparators(cfgFile.excludeFile(MirallConfigFile::SystemScope))));
|
||||
|
||||
QFile ignores(file);
|
||||
if (ignores.open(QIODevice::ReadOnly)) {
|
||||
while (!ignores.atEnd()) {
|
||||
QString line = QString::fromUtf8(ignores.readLine());
|
||||
line.chop(1);
|
||||
if (!line.isEmpty() && !line.startsWith("#")) {
|
||||
QListWidgetItem *item = new QListWidgetItem;
|
||||
setupItemFlags(item);
|
||||
if (line.startsWith("]")) {
|
||||
line = line.mid(1);
|
||||
item->setCheckState(Qt::Checked);
|
||||
}
|
||||
item->setText(line);
|
||||
if (readOnly) {
|
||||
item->setFlags(item->flags() ^ Qt::ItemIsEnabled);
|
||||
item->setToolTip(disabledTip);
|
||||
}
|
||||
ui->listWidget->addItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Mirall
|
||||
49
src/mirall/ignorelisteditor.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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 IGNORELISTEDITOR_H
|
||||
#define IGNORELISTEDITOR_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class QListWidgetItem;
|
||||
|
||||
namespace Mirall {
|
||||
|
||||
namespace Ui {
|
||||
class IgnoreListEditor;
|
||||
}
|
||||
|
||||
class IgnoreListEditor : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit IgnoreListEditor(QWidget *parent = 0);
|
||||
~IgnoreListEditor();
|
||||
|
||||
private slots:
|
||||
void slotItemSelectionChanged();
|
||||
void slotRemoveCurrentItem();
|
||||
void slotUpdateLocalIgnoreList();
|
||||
void slotAddPattern();
|
||||
void slotEditPattern(QListWidgetItem*);
|
||||
|
||||
private:
|
||||
void readIgnoreFile(const QString& file, bool readOnly);
|
||||
Ui::IgnoreListEditor *ui;
|
||||
};
|
||||
|
||||
} // namespace Mirall
|
||||
|
||||
#endif // IGNORELISTEDITOR_H
|
||||
117
src/mirall/ignorelisteditor.ui
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Mirall::IgnoreListEditor</class>
|
||||
<widget class="QDialog" name="Mirall::IgnoreListEditor">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>471</width>
|
||||
<height>359</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Ignored Files Editor</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>213</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="3">
|
||||
<widget class="QListWidget" name="listWidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QPushButton" name="addPushButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="removePushButton">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QLabel" name="descriptionLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Mirall::IgnoreListEditor</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>348</x>
|
||||
<y>333</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>361</x>
|
||||
<y>355</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Mirall::IgnoreListEditor</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>210</x>
|
||||
<y>333</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>385</x>
|
||||
<y>297</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||