1
0
mirror of https://github.com/chylex/Nextcloud-Desktop.git synced 2026-04-03 18:11:32 +02:00

Compare commits

..

309 Commits

Author SHA1 Message Date
Daniel Molkentin
24251bc223 Add "Keyword" to the desktop template
Fixes #876
2013-08-16 12:29:11 +02:00
Jenkins for ownCloud
f78749d2cd [tx-robot] updated from transifex 2013-08-15 16:57:02 -04:00
Daniel Molkentin
0995377d39 1.4.0 beta2 2013-08-15 17:32:46 +02:00
Daniel Molkentin
b9228e64ff Make Folder Reset an expert option (F5)
This feature should not be required, but might be helpful
in the debugging process.
2013-08-15 17:00:07 +02:00
Olivier Goffart
c4084de716 Remove the 'Remove Account' button
It is not doing anything right now, and we have no plans to implement it
in a short future

Fix Issue #866
2013-08-15 16:42:08 +02:00
Olivier Goffart
3c667918e7 Fix Reconfiguration keeps sync folder definition
Issue #875
2013-08-15 16:35:03 +02:00
Olivier Goffart
e55745cbcf Remove connection from unexisting signal to unexisting slot 2013-08-15 16:16:22 +02:00
Daniel Molkentin
e01ce20431 Wizard: Do not use FormLayout for URL line edit
Looks ugly on Mac

Fixes #870
2013-08-15 15:58:20 +02:00
Olivier Goffart
dce8cb83d9 Do not forget the config on the second page of the wizzard
Fixes mirral issue #874 where certificate was not stored on the first
page
2013-08-15 15:45:16 +02:00
Olivier Goffart
8dc956c55b Put all the debug in the log window
Don't emit debug before we installed the hook and created he log window
Also no debug if we use --help
2013-08-15 15:45:16 +02:00
Daniel Molkentin
6c77921a32 Admendment of last commit 2013-08-15 13:09:20 +02:00
Daniel Molkentin
55e4748f06 allow relative directories for --confdir 2013-08-15 13:01:05 +02:00
Daniel Molkentin
5ff9e02517 Implement --logfile - (log to stdout) 2013-08-15 12:24:52 +02:00
Daniel Molkentin
7f7ebc36f5 Fix showing help when app is running 2013-08-15 12:24:52 +02:00
Klaas Freitag
0e5bfc03ce Removed label on request of Jan. 2013-08-15 12:17:01 +02:00
Klaas Freitag
b309d333a6 Minor wording and margin fixes. 2013-08-15 11:30:48 +02:00
Daniel Molkentin
ee439382ed Logger: always log to log widget 2013-08-15 10:56:16 +02:00
Klaas Freitag
192212c682 Handle quota exceeding as error. 2013-08-15 08:15:43 +02:00
Klaas Freitag
148bdfdcd6 Some minor cleanups. 2013-08-15 08:15:43 +02:00
hefee
066c0ba189 Make building documantation parallelable.
sphinx will fail in a race condition, if every sphinx target
uses the same CACHE dir:

Exception occurred:
	File "/usr/lib/pymodules/python2.7/sphinx/cmdline.py", line 189, in main
app.build(force_all, filenames)
	File "/usr/lib/pymodules/python2.7/sphinx/application.py", line 204, in build
self.builder.build_update()
	File "/usr/lib/pymodules/python2.7/sphinx/builders/__init__.py", line 196, in build_update
	'out of date' % len(to_build))
	File "/usr/lib/pymodules/python2.7/sphinx/builders/__init__.py", line 238, in build
self.env.topickle(path.join(self.doctreedir, ENV_PICKLE_FILENAME))
	File "/usr/lib/pymodules/python2.7/sphinx/environment.py", line 351, in topickle
	movefile(filename + '.tmp', filename)
	File "/usr/lib/pymodules/python2.7/sphinx/util/osutil.py", line 104, in movefile
os.rename(source, dest)
	OSError: [Errno 2] No such file or directory
2013-08-15 02:36:44 +02:00
Daniel Molkentin
9651f1cddf Folder Wizard: Decode percent encoding 2013-08-14 19:24:02 +02:00
Daniel Molkentin
5feb9b0806 folderwizard: layout fixes 2013-08-14 19:24:02 +02:00
Klaas Freitag
44b9ee19e7 Flatten the itemprogressdialog and show a flat list. 2013-08-14 18:24:06 +02:00
Klaas Freitag
cc16d19bc4 Removed unused defines. 2013-08-14 18:24:06 +02:00
Klaas Freitag
b96c2de2b7 Cleaned folder class, removed a couple of signals and helper classes. 2013-08-14 18:24:06 +02:00
Klaas Freitag
f882b80708 Fix wording to more consolidated style, Jan request. 2013-08-14 18:24:06 +02:00
Daniel Molkentin
4d7c014b23 Resize dialog 2013-08-14 17:47:10 +02:00
Daniel Molkentin
1f274699e5 Pressing F12 in Settings now open the log browser 2013-08-14 17:46:58 +02:00
Daniel Molkentin
697e355f07 Hide mono icon options if the resource has no mono icons 2013-08-14 16:35:42 +02:00
Olivier Goffart
e89bdfc422 Make the Pause button stop the sync 2013-08-14 14:57:56 +02:00
Olivier Goffart
c2cc9e62a7 Fix the brandwith limit
The unit in mirall are in kbytes/s,  but csync expect  byte/s
2013-08-14 14:57:56 +02:00
Daniel Molkentin
1f2ae0d061 Folder Wizard UI clean up
Input from Jan
2013-08-14 14:01:33 +02:00
Olivier Goffart
91a39588c6 re-enable the sync if the syncing was disabled because the connection is down at startup 2013-08-14 12:59:56 +02:00
Olivier Goffart
a642f86d5b Detect connection failure from the polling 2013-08-14 12:34:03 +02:00
Olivier Goffart
00cf290574 reduce a bit the log output 2013-08-14 12:34:03 +02:00
Daniel Molkentin
b59952b539 Wrap custom message label 2013-08-14 11:58:03 +02:00
Daniel Molkentin
88b4ff9809 Wording changes proposed by Jan 2013-08-14 11:57:19 +02:00
Daniel Molkentin
ddd0965a82 Fix include after class rename 2013-08-14 11:11:47 +02:00
Klaas Freitag
0a9491ff46 Improved timestamp formatting in item sync progress dialog. 2013-08-14 08:04:46 +02:00
Klaas Freitag
32b44e3d87 Removed unused dialog class. 2013-08-14 08:04:46 +02:00
Klaas Freitag
268004b4ff Get correct action kind from progress Dispatcher in case of Context
kind.
2013-08-14 08:04:46 +02:00
Daniel Molkentin
6c0f6ae62e Make copy action copy all fields again
Fixes #863
2013-08-13 15:39:28 +02:00
Jenkins for ownCloud
094f2bb540 [tx-robot] updated from transifex 2013-08-13 08:12:11 -04:00
Daniel Molkentin
f4da7f1fb0 Merge pull request #857 from Arakmar/hiddenFix
Don't add empty entries in hidden files editor with an empty string or when cancel is clicked
2013-08-13 03:43:02 -07:00
Yoann Laissus
b058185d3b Don't add empty entries in hidden files editor with an empty string
or when cancel is clicked
2013-08-12 13:10:16 +02:00
Jenkins for ownCloud
eecb981736 [tx-robot] updated from transifex 2013-08-11 07:07:58 -04:00
Daniel Molkentin
46b870e260 Revert "run tests verbosely"
This reverts commit ff2d59d32f.
2013-08-09 14:45:49 +02:00
Olivier Goffart
1ffab7337d add the temporary downloaded files to the exclude list 2013-08-09 14:10:21 +02:00
Olivier Goffart
82fb8c49cf Do not clear the pending event in the polling anymore
This is useless,  and can even cause problems now since polling only do
a sync when the server has actually changed
2013-08-09 14:10:21 +02:00
Daniel Molkentin
ff2d59d32f run tests verbosely 2013-08-09 13:27:05 +02:00
Daniel Molkentin
0ced165b3e Add tests for Utility namespace 2013-08-09 13:09:41 +02:00
Daniel Molkentin
d3378c131a Fix formatting for 1 byte size in octetsToString() 2013-08-09 13:02:24 +02:00
Jenkins for ownCloud
0300a85295 [tx-robot] updated from transifex 2013-08-09 07:01:08 -04:00
Daniel Molkentin
60a116f3e0 Add tests for Utility namespace 2013-08-09 12:56:43 +02:00
Daniel Molkentin
ff4d2d488f Add cancel button to wizard on Mac
Fixes #848
2013-08-09 01:08:46 +02:00
Daniel Molkentin
b0852b4cf5 No ... for entires that do not spawn a dialog 2013-08-08 20:27:16 +02:00
Daniel Molkentin
c047b4d4de Seems autowrapping doesn't work everywhere. Enforce it 2013-08-08 17:52:09 +02:00
Olivier Goffart
6655da9d24 Fix warning 2013-08-08 16:03:57 +02:00
Daniel Molkentin
cc6abfc366 ChangeLog: 1.4.0beta1 2013-08-08 15:51:48 +02:00
Daniel Molkentin
4e3d4c3153 Credits: Add Jan 2013-08-08 15:51:36 +02:00
Daniel Molkentin
c323041040 NSIS: install libjpeg for qjpeg4.dll 2013-08-08 15:39:34 +02:00
Daniel Molkentin
95d600c5f1 Fix proxy documentation, explain bandwidth limiter 2013-08-08 14:54:52 +02:00
Olivier Goffart
52a47fbc67 Force a sync run after 5 minutes 2013-08-08 14:32:14 +02:00
Klaas Freitag
4e7c069c1f Some adds to the architecture documentation. 2013-08-08 14:19:13 +02:00
Klaas Freitag
f89ffa513a Bump version to 1.4.0beta1 2013-08-08 13:08:23 +02:00
Daniel Molkentin
0086916b4d Retain all old settings when running the setup wizard
Fixes #825
Fixes #759
2013-08-08 13:04:05 +02:00
Klaas Freitag
df5ef6fe67 Added new information icon. 2013-08-08 11:16:28 +02:00
Klaas Freitag
c9e51dcfc1 Display properly elided text in progress. 2013-08-08 11:16:28 +02:00
Daniel Molkentin
c05adfd817 Open Top-Level-Dialogs modal, not semi-modal 2013-08-08 10:36:09 +02:00
Daniel Molkentin
055410e38f Setup proxy connection before every sync 2013-08-08 00:46:23 +02:00
Daniel Molkentin
6163de378e Merge pull request #840 from owncloud/network-icon
Proper network icon for the nice new settings dialog.
2013-08-07 13:27:00 -07:00
Jan-Christoph Borchardt
22938cd697 Proper network icon for the nice new settings dialog. 2013-08-07 21:04:27 +02:00
Daniel Molkentin
a493f81ec2 Windows Installer: Add jpeg,gif,ico image formats 2013-08-07 16:24:10 +02:00
Krzesimir Nowak
87cb2a7114 Set busy cursor when doing page loading in browser. 2013-08-07 15:05:42 +02:00
Krzesimir Nowak
a875b46a80 Add cookie handling for Shibboleth credentials.
Which cookies are going to be stored are determined only when setting
up or modifying an account. Cookie handling allow us to avoid choosing
Identity Provider everytime when browser page appears.
2013-08-07 15:03:13 +02:00
Krzesimir Nowak
704ba791fd Fail the sync if we close the browser without passing proper credentials. 2013-08-07 15:03:13 +02:00
Daniel Molkentin
c24d6bd71c Notify user if there is a problem with the inotify watcher 2013-08-07 15:00:10 +02:00
Daniel Molkentin
9b319cf189 Significantly simply logging 2013-08-07 15:00:10 +02:00
Klaas Freitag
1a9eb19f0d Fix even more compile warnings. 2013-08-07 14:51:31 +02:00
Klaas Freitag
1943cc60b6 Added missing switch cases to calm compiler warnings. 2013-08-07 14:46:57 +02:00
Jenkins for ownCloud
761c05c358 [tx-robot] updated from transifex 2013-08-07 08:01:30 -04:00
Daniel Molkentin
b08c1ada02 -Wno-long-long required due to Qt headers 2013-08-07 13:19:21 +02:00
Daniel Molkentin
dab17e381c Merge pull request #836 from krnowak/warnings
Add -Wall -Wextra -Wpedantic (or -pedantic) warnings by default.
2013-08-07 04:08:26 -07:00
Krzesimir Nowak
114f66f297 Add -Wall -Wextra -Wpedantic (or -pedantic) warnings by default.
This also introduces a MIRALL_FATAL_WARNINGS to enable a -Werror for
GNU compilers and clang.

MSVC compilers are yet to be handled.
2013-08-07 12:54:34 +02:00
Daniel Molkentin
f8878833de Fix compiler warning 2013-08-07 12:18:44 +02:00
Klaas Freitag
364d4340fd Show a more informative problem message in progress. 2013-08-07 12:15:28 +02:00
Klaas Freitag
6226a6ee8f Enable delete messages for progress display. 2013-08-07 10:28:34 +02:00
Daniel Molkentin
c49edeb09d Fix regression in FolderWizard over 1.3
Now it's possible to also pick subdirectory
of in arbitrary depths again
2013-08-06 21:50:22 +02:00
Klaas Freitag
7e794cd94f Tell the progress dialog about a new sync result object. 2013-08-06 18:07:59 +02:00
Klaas Freitag
933a62de01 Add recent action entries in correct sequence to menu. 2013-08-06 18:07:31 +02:00
Klaas Freitag
23f8e3b4f8 Allow Delete notification. 2013-08-06 18:06:54 +02:00
Klaas Freitag
3502edf71b More detailed strings for the progress kind. 2013-08-06 18:06:39 +02:00
Klaas Freitag
4d8a371e43 Display information from SyncResult. 2013-08-06 18:06:05 +02:00
Klaas Freitag
01fd3242c4 Add folder alias to sync result structure. 2013-08-06 18:04:42 +02:00
Klaas Freitag
9c289334e9 Handle notifications for deletes. 2013-08-06 18:04:08 +02:00
Krzesimir Nowak
002142539b Remove most of the dead code in Shibboleth setup page.
The effect of most of the code wasn't even visible after making a
browser an independent window.
2013-08-06 14:56:26 +02:00
Krzesimir Nowak
e1d1c10fad Fix workflow in wizard.
Pressing back in third window should show us browser again. Closing
browser window should show us server setup window again.
2013-08-06 14:35:11 +02:00
Krzesimir Nowak
577bc546d8 Handle SSL problems also for Shibboleth servers. 2013-08-06 11:56:27 +02:00
Daniel Molkentin
85a832fb7c NSIS: Add WebKit dll 2013-08-05 23:25:14 +02:00
Olivier Goffart
a9f23a8331 Get the etag of all subfolder for the root directory
The root directory etag is not updated when the files change.
That's why we need to check the etag of all its sub items
2013-08-05 20:13:39 +02:00
Olivier Goffart
5d24599546 Fetch the etag of the folder to see if it has changed before syncing
Notice that this does not work for the toplevel directory yet, as
owncloud does not change the etag of the root folder
2013-08-05 20:13:39 +02:00
Olivier Goffart
07d9d3770b Refactor: move the pollTimer to the folder 2013-08-05 20:13:39 +02:00
Daniel Molkentin
c9ddb12b5a Build after merge 2013-08-05 20:14:22 +02:00
Daniel Molkentin
0932ee6051 Merge branch 'shibboleth'
Conflicts:
	src/CMakeLists.txt
	src/mirall/application.h
2013-08-05 19:54:06 +02:00
Daniel Molkentin
ee1b8465a3 Revert "Temporarily log HTTP traffic done by MirallAccessManager."
This reverts commit 5930ca8ac7.
2013-08-05 19:47:48 +02:00
Daniel Molkentin
26bd164168 Safer implementation for substitution of WebDav URL
- Use of RegEx is dangerous with folder names
  containing e.g. '(' or ')'.
- Didn't work in my case at all.
2013-08-05 19:45:24 +02:00
Krzesimir Nowak
00e819bd92 Make sure we don't append several copies of cookies for syncing.
Also, make sure we don't append old Shibboleth ones as well.
2013-08-05 18:39:26 +02:00
Krzesimir Nowak
740d33b378 Revert "Go back if browser in wizard is destroyed before getting a cookie."
This reverts commit da370c8b36.
2013-08-05 17:57:32 +02:00
Krzesimir Nowak
f0b284eda7 Refresh Shibboleth cookie if it gets outdated during sync. 2013-08-05 17:42:03 +02:00
Krzesimir Nowak
da370c8b36 Go back if browser in wizard is destroyed before getting a cookie. 2013-08-05 17:29:11 +02:00
Olivier Goffart
203b9c7f6b leak fix 2013-08-05 16:49:18 +02:00
Daniel Molkentin
7932ec3cc1 IgnoreListEditor: slightly better text 2013-08-05 16:26:17 +02:00
Daniel Molkentin
3f15e02881 Add support for deletable ignores in IgnoreListEditor. 2013-08-05 16:13:26 +02:00
Olivier Goffart
6fb5c04bde Finish danimo's patch that refactor the scheduler
Wait a full poll interval after the end of the sync
2013-08-05 15:15:15 +02:00
Olivier Goffart
578bcc3522 Remove scheduler logic from folderman and moved it in folderman
Also fetch and parse the etag when doing a PROPFIND to get the quota

(Patch from danimo)
2013-08-05 15:15:15 +02:00
Daniel Molkentin
99dea76fd1 Fix path display in folder wizard
The new implementation did not work correctly when owncloud is installed
in a subdirectory.
2013-08-05 14:28:05 +02:00
Jenkins for ownCloud
b680540adf [tx-robot] updated from transifex 2013-08-05 06:54:39 -04:00
Klaas Freitag
67f57a443c Disable unknown csync flags for now. 2013-08-05 12:30:24 +02:00
Klaas Freitag
2b3b4f9daf Start setup wizard if no config exists on startup. 2013-08-05 12:19:53 +02:00
Klaas Freitag
ba01a697e6 Renamed test case to get rid of stinking name. 2013-08-05 09:39:06 +02:00
Klaas Freitag
16d81db117 Show deletes in progress. 2013-08-05 09:39:06 +02:00
Jenkins for ownCloud
4ce92f1a98 [tx-robot] updated from transifex 2013-08-04 01:26:52 -04:00
Jenkins for ownCloud
21c63637bd [tx-robot] updated from transifex 2013-08-03 01:27:06 -04:00
Klaas Freitag
18f764e4d5 Fix a typo. 2013-08-02 21:55:09 +02:00
Krzesimir Nowak
5930ca8ac7 Temporarily log HTTP traffic done by MirallAccessManager.
To be reverted later.
2013-08-02 14:30:15 +02:00
Klaas Freitag
49be4a3be2 Propagate soft errors into SyncResult, icon fixes. 2013-08-02 14:22:01 +02:00
dragotin
1e50620f53 Merge pull request #822 from RealRancor/master
Updated options with missing cl switches - thanks a lot :)
2013-08-02 03:19:24 -07:00
Klaas Freitag
5e82dc1841 Some more refinements on item progress 2013-08-02 12:16:21 +02:00
RealRancor
33d76962a7 Updated options with missing cl switches 2013-08-02 11:59:57 +02:00
Klaas Freitag
6ce1c17ee1 Give application class control over itemprogressdialog 2013-08-02 11:33:45 +02:00
Jenkins for ownCloud
8d9b4d3669 [tx-robot] updated from transifex 2013-08-02 01:27:17 -04:00
Krzesimir Nowak
fb79b8a7f8 Omit the server setup page in wizard if URL is overriden by theme. 2013-08-01 19:17:41 +02:00
Krzesimir Nowak
b0236eaa24 Fix folder item being inactive. 2013-08-01 19:11:05 +02:00
Daniel Molkentin
19bbff708e Improve FolderWizard 2013-08-01 18:22:13 +02:00
Klaas Freitag
1399ea13cc Fixed rounding of size display. 2013-08-01 16:58:36 +02:00
Klaas Freitag
a25d9fd3b4 Added itemprogressdialog class to show the sync progress and problems. 2013-08-01 16:58:36 +02:00
Krzesimir Nowak
72d51e4667 Show browser as independent window instead of putting it into dialog.
When browser is shown, dialog is hidden. It reappears after we are
done with logging in.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
f6e3838eb2 Create a QNetworkAccessManager subclass which sets proper user agent and use it. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
c1fdecae2d Fix misc warnings. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
4104db65bb Remove unused method. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
36cb827406 Print correct subtitles when modifying account details. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
69a4558fe4 Actually use the remoteFolder sent by signal. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
928eae419f Hide resolution widget in advanced setup page if nothing has changed. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
50edac8ee8 Split setup page into server and advanced parts. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
945951cda5 Fix indentation. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
fa95a638af Fill username in HTTP credentials setup page. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
3b6aeb1fc8 Make browser inside Shibboleth wizard page a bit larger. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
6543a01418 Fix warnings emitted by clang. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
9724e52f1b Fix warnings.
Should be now compilable when run with -Wall -Wextra -Wpedantic
-Werror.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
9413a30a08 Fix build when using QtKeyChain. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
6a9a2559d2 Fix shibboleth syncing after client restart.
We needed to notify shibboleth-specific QNAM about new cookie and then
stop cramming that cookie into request via setHeader, but rather put
it to cookie jar and let Qt handle the cookie insertion into requests.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
78b6f4df01 Move the creds/ and wizard/ directories one level higher. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
b7e88aa2ef Fix copyright headers. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
224fd21612 Fix displaying URL in account settings.
I deleted too much code there last time.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
a6bf33c501 Make "Connect" button inactive until we get a Shibboleth cookie. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
306a9421fb Avoid a crash when disposing Shibboleth browser. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
d5885daf0f Move credential related files to creds directory.
CredentialStore is now only used for HTTP credentials, so it was moved
into http directory under creds.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
52b3f7105d Remove unused stuff. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
7f6dc291c0 Create HTTP credentials object for config without type value. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
eacb849353 Fix credentials handling in csync.
CSync module's properties can be set only after it was initialized, so
we split the sync context vfunc into two: the one ran before
initialization and the one ran before sync beginning.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
367bc401ee Add Shibboleth credentials implementation. 2013-08-01 16:53:43 +02:00
Krzesimir Nowak
92af3ea725 Add and use credentials abstraction. Implement HTTP and dummy credentials.
To achieve that MirallConfigFile now exports several general
settings-related methods for subclasses (here: for HttpConfigFile), so
it does not have to contain any code targeted for specific
credentials.
2013-08-01 16:53:43 +02:00
Krzesimir Nowak
582ce4cfa0 Cleanup setup wizard. Split setup page into two.
First page is for giving server URL and advanced setup and second page
is for giving credentials. Second page will differ based on
authentication type supported by ownCloud server. For now only HTTP
credentials are supported.
2013-08-01 16:53:43 +02:00
Thomas Müller
43ae3dfce5 no zh, but zh_CN 2013-08-01 15:32:45 +02:00
Daniel Molkentin
ce851a7a8b Use wildcards for translations, so they are always up-to-date 2013-08-01 15:28:06 +02:00
Jenkins for ownCloud
b5b7589b41 [tx-robot] updated from transifex 2013-08-01 08:51:42 -04:00
Daniel Molkentin
b638341c14 Merge pull request #821 from owncloud/ts-cleanup
Translation cleanup
2013-08-01 05:38:50 -07:00
Thomas Müller
7274417f84 explicit selection and mapping of maguages 2013-08-01 14:06:17 +02:00
Daniel Molkentin
1967226c71 fix networksettings.ui _again_ 2013-08-01 13:48:42 +02:00
Thomas Müller
227ea8ed24 kill existing 2013-08-01 13:23:21 +02:00
Jenkins for ownCloud
e705db8339 [tx-robot] updated from transifex 2013-08-01 01:26:44 -04:00
Daniel Molkentin
be65f78174 Fix win32 installer 2013-07-31 08:35:13 +02:00
Daniel Molkentin
fd6a17f3e6 Add journal and temp journal to ignore file
csync ignores them anyway, but inotify and other
watchers don't.
2013-07-31 08:35:13 +02:00
Jenkins for ownCloud
0353d7b6a6 [tx-robot] updated from transifex 2013-07-31 01:26:54 -04:00
Klaas Freitag
a64724be0e More sophisticated progress, add Recent Changes Menu to tray.
ProgressDispatcher now stores a queue of events as well as sync
problems, better known as soft errors. The tray menu displays a
sub menu with 5 recent changes and adds a more... button that
shows a warning icon if soft errors had happend.
More to implement.
2013-07-30 22:24:48 +02:00
Klaas Freitag
a0d9d41455 Use const reference parameters for custom struct parameters. 2013-07-30 22:24:48 +02:00
Klaas Freitag
84e8ab5b71 Make progress aware of errors, first step. 2013-07-30 22:24:48 +02:00
Markus Goetz
f5bbb12434 Crash hotfix, need to dig down actual cause 2013-07-30 15:49:57 +02:00
Jenkins for ownCloud
a07d2cddd2 [tx-robot] updated from transifex 2013-07-30 07:57:21 -04:00
Daniel Molkentin
f1878640c8 Linux: install all available icon sizes 2013-07-27 13:06:38 +02:00
Klaas Freitag
edf8147561 Small cleanups. 2013-07-26 15:10:03 +02:00
Klaas Freitag
3a9ab3a86f Consolidated progress displaying to a single slot from csync. 2013-07-26 13:44:38 +02:00
Klaas Freitag
8f912ca0c5 Adopt to new progress API in csync, WIP 2013-07-25 16:28:45 +02:00
Olivier Goffart
a827056d28 Fix upload limit 2013-07-25 11:21:02 +02:00
Klaas Freitag
6c90989584 Make progress display more robust. 2013-07-25 11:00:19 +02:00
Daniel Molkentin
08acf5e9aa Network Settings: Reword entries, fix uic warnings 2013-07-25 10:41:44 +02:00
Daniel Molkentin
bca295183b Make use of bandwidth limiting, split general/network settings.
Fixes #14
2013-07-24 19:53:20 +02:00
Markus Goetz
a400a2e0bb OS X: Fix window messup 2013-07-24 17:06:35 +02:00
Daniel Molkentin
8c15839753 Remove all >= Qt 4.7 ifdefs 2013-07-24 15:34:37 +02:00
Klaas Freitag
810024f4c9 Reworked progress display for more performance.
Moved most of the logic to the dialog class to be able to only update
the GUI if there are really changes rather than doing it whenever a
change happens.
2013-07-24 14:40:47 +02:00
Klaas Freitag
de7bcca5fe Fixed signal and slot parameter list for progress. 2013-07-24 14:40:47 +02:00
Klaas Freitag
6552a48639 Make compactFormatDouble a bit smarter. 2013-07-24 14:40:47 +02:00
Daniel Molkentin
aee0f0c882 make addFolderDefintion easier to read 2013-07-24 14:27:57 +02:00
Daniel Molkentin
e353193fbb FolderMan: Remove dead code 2013-07-24 14:23:32 +02:00
Daniel Molkentin
fb547e9100 Fix/compactify comments in folderman.h 2013-07-24 14:16:52 +02:00
Markus Goetz
44289c8781 OS X: Fix compile 2013-07-24 14:08:16 +02:00
Daniel Molkentin
d4a5ab252d Drop Compat with Qt 4.6
We now require Qt 4.7 due to QNAM
2013-07-24 13:59:03 +02:00
Daniel Molkentin
a3b3c28694 CSyncThread: Fix unreachable code 2013-07-23 14:34:06 +02:00
Klaas Freitag
976c41a3b8 Beautified ProgressDispatcher 2013-07-23 13:32:33 +02:00
Daniel Molkentin
da087292fd First version of progress in the context menu 2013-07-23 13:19:17 +02:00
Klaas Freitag
274f59f93b Reworked progress again following mirall bug #137 2013-07-23 12:23:29 +02:00
Klaas Freitag
85d810d2cf Rename signal from folderProgress to itemProgress. 2013-07-23 12:23:20 +02:00
Daniel Molkentin
47f151c594 FolderWizard: Return targetPath properly 2013-07-22 23:14:41 +02:00
Daniel Molkentin
7b5ef2186e Remove accidential change 2013-07-22 23:14:12 +02:00
Daniel Molkentin
8236dafb96 INotify backend: honor ignored files
Actually this needs a careful redesign,
but this is good enough to fix the issue.

Fixes: #763
2013-07-22 21:39:13 +02:00
Daniel Molkentin
56e5627b6b ownCloudFolder -> Folder
Fixes #769
2013-07-22 20:30:38 +02:00
Daniel Molkentin
38db0eddab Make selection of target ListView-based
Fixes #222
2013-07-22 18:07:44 +02:00
Daniel Molkentin
710625e2a3 Spelling bee 2013-07-22 18:06:27 +02:00
Daniel Molkentin
a5e7af6c1f Make FolderMan a singleton
It doesn't "belong" to application (think about lib use cases).
This is better.
2013-07-22 13:59:52 +02:00
Daniel Molkentin
5fe4d2db2b Introduce MirallConfigFile::save/restoreGeometry() 2013-07-22 12:28:43 +02:00
Daniel Molkentin
b2c587e2f8 Settings: Show overall state as account icon
Requires the previous refactoring commit
2013-07-22 12:11:12 +02:00
Daniel Molkentin
40c2d891c1 Refactor sync result
Sync result computation is now owned by FolderMan
2013-07-22 12:10:13 +02:00
Klaas Freitag
d5ad3a8a70 Removed mistakenly checked in file. 2013-07-22 09:47:15 +02:00
Daniel Molkentin
c74382af4f octetsToString: Add TB case to pretty printer 2013-07-20 01:27:41 +02:00
Daniel Molkentin
0be0111724 Use WA_DeleteOnClose for settings
instead of trying brain dead things
2013-07-20 01:27:00 +02:00
Daniel Molkentin
f0d454b511 Ignore list editor fixes 2013-07-20 01:26:11 +02:00
Daniel Molkentin
b79a45403e Implement UI changes discussed with Jan.
Fixes #785
Fixes #782
Fixes #781
2013-07-19 22:14:21 +02:00
Klaas Freitag
1cc60e755b Rather show overall progress in settings dialog. 2013-07-19 17:05:46 +02:00
Daniel Molkentin
7fb7cc8c46 Don't hardcode strings, use constants instead
This hopefully reduces the chances for
regressions during refactoring.
2013-07-19 16:28:12 +02:00
Daniel Molkentin
1400889b23 Small cleanups 2013-07-19 16:28:12 +02:00
Daniel Molkentin
44fa9bd141 remove skip save pwd parameter -- not used any longer 2013-07-19 16:28:12 +02:00
Klaas Freitag
992dffa032 Add an overall progress counter to progressDispatcher 2013-07-19 13:05:30 +02:00
Klaas Freitag
5fa7e48c24 Do not show the frame around the progress. 2013-07-19 11:07:47 +02:00
Klaas Freitag
3b00dfebed Add a textual display of the file size in progress. 2013-07-19 10:04:09 +02:00
Klaas Freitag
b42c7e07e6 More work on progress display of file up and download.
This is at least working for file upload, untested for downloads
and other operations. Needs more testing and more polish.
2013-07-17 22:34:11 +02:00
Markus Goetz
ab7bfabf12 Put cookies into csync
We already have the auth cookies from the quota request,
so put them into csync to avoid a 401 roundtrip
2013-07-17 16:27:18 +02:00
Markus Goetz
ab72644ace Check if quota request had HTTP 207 2013-07-17 15:08:48 +02:00
Daniel Molkentin
218fa040c8 Reduce thread priority 2013-07-16 16:05:37 +02:00
Daniel Molkentin
b8f783f104 Set proxies and ignored files before every sync run 2013-07-16 16:05:04 +02:00
Daniel Molkentin
ae2e3e7fb1 Remove autostart option from Windows installer
now handled by the application
2013-07-15 11:05:14 +02:00
Daniel Molkentin
edd9d9aee3 Fix disabled state _again_
Seems like a bug in Designer
2013-07-12 18:05:59 +02:00
Daniel Molkentin
8c66085621 Folder wizard: Warn if a chosen dir is read-only
Fixes #755
2013-07-12 13:54:20 +02:00
Klaas Freitag
40ab325a37 Cleaned up credentialstore class a bit.
Removed all "user mode" stuff from the class that was used to ask the
user interactively for the password. We do not do that any more.
2013-07-12 12:22:11 +02:00
Klaas Freitag
7ae95b14f4 Added method to check if the password is in the config file. 2013-07-12 12:21:34 +02:00
Klaas Freitag
5da6103fb5 Documented the docexpire option. 2013-07-11 12:32:58 +02:00
Klaas Freitag
6b5b9db20a Handle progress through ProgressDispatcher class, WIP. 2013-07-11 12:32:58 +02:00
Daniel Molkentin
eb39d144e4 Use default cookie jar again 2013-07-10 18:59:01 +02:00
Daniel Molkentin
cfaaf4a2c4 Don't always send Auth headers
Use slotAuthentication again instead.
2013-07-10 18:57:52 +02:00
Daniel Molkentin
279a738aa6 General Settings: Fix spacing in layout 2013-07-10 12:46:43 +02:00
Daniel Molkentin
10b55f11a2 Fix build for Qt <= 4.6 2013-07-10 09:48:10 +02:00
Daniel Molkentin
25065c4151 Remember geometry of LogBrowser 2013-07-10 09:34:41 +02:00
Daniel Molkentin
d2657bc154 Do display floating points with precision of 1
For several GB of quota, we might end up showing
<1% far too long. So show one decimal number,
unless it's zero.
2013-07-10 09:34:41 +02:00
Daniel Molkentin
4fde3f4a65 Quota in Account Settings: properly round value 2013-07-10 09:34:41 +02:00
Daniel Molkentin
e398cfb27c Introduce Utility::compactFormatDouble()
Like QLocale::toString(double, 'f', prec), but drops trailing
zeros after the decimal point.
2013-07-10 09:34:27 +02:00
Daniel Molkentin
3cc670ec29 Send non-standard port along with host
In QNAM, that's actually the default,
and we were overriding it incorrectly
for no obvious reason. When using the
QHTTP fallback, we need to submit a
non-standard port manually.

Fixes #748
2013-07-10 08:43:10 +02:00
Markus Goetz
c8d9e8458a OS X: Find out if we are a startup item 2013-07-09 17:30:50 +02:00
Markus Goetz
892419e880 OS X: Ability to disable startup item 2013-07-09 17:13:41 +02:00
Markus Goetz
13fb49cf39 Make Jenkins happy 2013-07-09 14:23:58 +02:00
Markus Goetz
eabe3f968e Raise settings dialog when creating
Else at least on OS X it is burried somewhere.
2013-07-09 14:18:23 +02:00
Markus Goetz
2ca5eaaab9 utility.cpp: Make compile on OS X 2013-07-09 14:07:48 +02:00
Daniel Molkentin
eed3deac67 Acount Settings: Enable in place password change 2013-07-08 14:49:01 +02:00
Daniel Molkentin
a63863b65c Detect missing system tray
Fixes #497
2013-07-08 14:48:58 +02:00
Daniel Molkentin
748ff13bce Utility::freeDiskSpace()
Does not yet work for UNC locations
2013-07-08 14:48:17 +02:00
Daniel Molkentin
6c7700c2e7 Save size of settings dialog
Fixes #541
2013-07-08 04:01:36 +02:00
Daniel Molkentin
c8ccb014c8 Use plural form for sync notification
Fixes #419
2013-07-08 03:19:49 +02:00
Daniel Molkentin
f1bd14e8de Remove superflous cast 2013-07-08 01:44:49 +02:00
Daniel Molkentin
024d01a192 Cleanly install themed clients next to owncloud on Unix
- Renames libowncloudsync for themes
- Properly names i18n dir
2013-07-08 01:44:49 +02:00
Daniel Molkentin
b2d02ef0bd Improve labeling in case of root sync 2013-07-07 01:24:56 +02:00
Daniel Molkentin
751d7deda6 Utility: Compile on Windows again
Theme moc files belong with libowncloud
2013-07-06 22:42:02 +02:00
Daniel Molkentin
fff795146e Make Utility::*Startup* testable 2013-07-06 22:41:59 +02:00
Daniel Molkentin
c6219581f6 More work on ignore/exclude files:
- Introduce split between sytem and user files
- Add Editor

Current limitation: Picking up the changes requires
a restart
2013-07-05 16:54:11 +02:00
Klaas Freitag
d0c5fb2395 Check connection at startup through ConnectionValidator 2013-07-05 13:14:48 +02:00
Klaas Freitag
6c2c81dc83 Format integer to string. 2013-07-05 11:40:44 +02:00
Daniel Molkentin
bfdb0c0012 Enable autostart when the client has been configured 2013-07-05 01:05:23 +02:00
Daniel Molkentin
d870d6c326 Implement "Show Desktop Notifications" option (default: on)
Should make #314 subscriber happy :-)
2013-07-05 01:01:23 +02:00
Daniel Molkentin
c06410e726 Implement Launch on Startup (Win, Linux).
Mac OS started. Untested.
2013-07-05 00:25:19 +02:00
Daniel Molkentin
ef03ebe086 Remove obsolete include 2013-07-04 20:43:20 +02:00
Daniel Molkentin
a217e8f24c Compile fix 2013-07-04 20:21:40 +02:00
Daniel Molkentin
c164beb040 Commit squashed refactoring branch
- introduce settings dialog
- general settings for general / proxy settings
- standalone proxy settings dialog removed
- standalone status dialog removed
- moved status dialog into settings dialog
- radically cut down links in context menu
- add help link to context menu

This needs more cleanup and testing. Known issues:

- When the wizard is called while the settings dialog
  is open, it will not show old information (see FIXME)
- Some settings are not implemented yet (greyed out)
- The counter in the FileItemDialog is not correct the
  first time around.

Based on the discussion done with Jan and Klaas during the
Nuenberg workshop.
2013-07-04 20:02:44 +02:00
Klaas Freitag
5171e5880d Initialize member var with zero to avoid crash. 2013-07-02 17:01:16 +02:00
Klaas Freitag
2ef62524d6 Use showTrayMessage instead of _tray directly 2013-07-02 17:01:16 +02:00
Klaas Freitag
4bbb29c2b4 Terminate sync process in the wizard rather than in folderman unload. 2013-07-02 17:01:16 +02:00
Klaas Freitag
261776cc78 Remove useless log message. 2013-07-02 17:01:16 +02:00
Daniel Molkentin
3008142b1b lowercase webdav request 2013-07-01 14:21:04 +02:00
Daniel Molkentin
152e729768 thinko 2013-06-28 23:23:42 +02:00
Daniel Molkentin
e7c77df59e Quota: Distinguish 0 and <1 2013-06-28 23:14:09 +02:00
Daniel Molkentin
4ff1a13f32 Implement folder display.
- Displayed as disabled action
- Refactor davRequest along the way
  - take Verb as QByteArray
  - pass along a QIODevice

Not implemented for Qt < 4.8
2013-06-28 23:01:31 +02:00
Daniel Molkentin
b9b18d6120 Remove unused configfile instance 2013-06-28 22:28:18 +02:00
Daniel Molkentin
4945ce3c8c Use awesome Qt template magic 2013-06-28 22:27:53 +02:00
Daniel Molkentin
19aa8c63c0 build again 2013-06-28 22:27:16 +02:00
Daniel Molkentin
efd11b61c6 Merge branch '1.3' 2013-06-28 11:47:09 +02:00
Daniel Molkentin
1facb1f95d 1.3.80 (pre 1.4.0) 2013-06-27 16:58:55 +02:00
Jenkins for ownCloud
3db0788a91 [tx-robot] updated from transifex 2013-06-27 16:57:44 +02:00
Jenkins for ownCloud
e068098046 [tx-robot] updated from transifex 2013-06-27 16:57:44 +02:00
Daniel Molkentin
f2d289326b More whitespace changes 2013-06-27 16:57:44 +02:00
Jenkins for ownCloud
ae57f27eb9 [tx-robot] updated from transifex 2013-06-27 16:57:44 +02:00
Daniel Molkentin
a1767b2f7f String whitespace fixes 2013-06-27 16:57:15 +02:00
Markus Goetz
5e9fcf7537 Raise setup wizard 2013-06-27 16:57:15 +02:00
Markus Goetz
8192cc7eea Setup Dialog: Fix widget size issue (2) 2013-06-27 16:57:15 +02:00
Markus Goetz
2b8e1f2504 Setup Dialog: Fix widget size issue
in advanced settings
2013-06-27 16:57:15 +02:00
Daniel Molkentin
9d01f80744 Fix Dialog behavior
- Fix proxy dialog being callable multiple times
- Use smart pointers in more places
- _owncloudSetupWizard was guarded by a smart pointer,
  this needs to be accounted for in other code pathes.
  Fixes Setup dialog not showing up when left-clicking
  on unconfigured instances on Windows.
2013-06-27 16:54:33 +02:00
Jenkins for ownCloud
1a04c9da67 [tx-robot] updated from transifex 2013-06-27 01:42:28 +02:00
Daniel Molkentin
d35e1baee1 Remove obsolete code 2013-06-26 17:27:44 +02:00
Jenkins for ownCloud
2f16e50c87 [tx-robot] updated from transifex 2013-06-26 15:55:25 +02:00
Jenkins for ownCloud
3a662f7afb [tx-robot] updated from transifex 2013-06-26 01:27:29 +02:00
Daniel Molkentin
903a78623c Minor theme changes 2013-06-25 14:51:39 +02:00
Jenkins for ownCloud
9dd776ff6c [tx-robot] updated from transifex 2013-06-24 22:55:32 +02:00
Jenkins for ownCloud
5d9c664fba [tx-robot] updated from transifex 2013-06-24 21:25:21 +02:00
Daniel Molkentin
5c4b7d427d Add "Reset Folder" option to status dialog
to recover from invalid databases. Features
a big fat warning.
2013-06-24 21:15:22 +02:00
Olivier Goffart
60a4180dd6 Delay the csync_init after we called status.php
That way, if there was a redirection, it is handled propertly
2013-06-21 00:34:46 +02:00
Olivier Goffart
8e42721959 Do not store the redirected URL in the config file.
Because it may be a temporary URL.
Especially anoying in captive portal

Fix issue #368

This is a revert of the revert 0bc9b6f44e
With small changes
2013-06-21 00:34:28 +02:00
Olivier Goffart
4553fa1d09 Merge remote-tracking branch '1.3' into master 2013-06-21 00:04:01 +02:00
Daniel Molkentin
e43ff398cd Merge branch '1.3'
Conflicts:
	doc/troubleshooting.rst
2013-06-19 16:09:54 +02:00
Daniel Molkentin
c0cd255ea3 Doc: More structure in troubleshooting.rst 2013-06-19 15:39:03 +02:00
Daniel Molkentin
a0375fd000 Doc: Expand trouble shooting section 2013-06-19 15:06:47 +02:00
Daniel Molkentin
6d847cd5f9 Doc: Add back --logfile, as it still exists 2013-06-19 15:06:16 +02:00
Daniel Molkentin
6470a3f539 Doc: remove now meaningless scentences 2013-06-19 15:05:33 +02:00
Daniel Molkentin
c6111d09ce doc: Fix wording 2013-06-19 14:29:56 +02:00
Daniel Molkentin
d6012854a9 Remove mirall man page 2013-06-19 14:29:28 +02:00
208 changed files with 49514 additions and 90548 deletions

View File

@@ -4,6 +4,8 @@ project(mirall)
set(PACKAGE "mirall")
set( CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules )
include(Warnings)
set(OEM_THEME_DIR "" CACHE STRING "Define directory containing a custom theme")
if ( EXISTS ${OEM_THEME_DIR}/OEM.cmake )
include ( ${OEM_THEME_DIR}/OEM.cmake )
@@ -62,9 +64,9 @@ endif()
####
#### find libs
find_package(Qt4 4.6.0 COMPONENTS QtCore QtGui QtXml QtNetwork QtTest REQUIRED )
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.6.0 COMPONENTS QtDBus REQUIRED )
find_package(Qt4 4.7.0 COMPONENTS QtDBus REQUIRED )
endif()
find_package(Csync REQUIRED)
if(UNIX)

View File

@@ -1,5 +1,33 @@
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

View File

@@ -1,6 +1,6 @@
set( VERSION_MAJOR 1 )
set( VERSION_MINOR 3 )
set( VERSION_MINOR 4 )
set( VERSION_PATCH 0 )
set( VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX})
set( VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}${VERSION_SUFFIX}beta2")
set( SOVERSION 0 )

View File

@@ -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
@@ -283,84 +282,20 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
!ifndef INSTALL_PATH
;Main executable.
File "${BUILD_PATH}\bin\${APPLICATION_EXECUTABLE}"
File "${BUILD_PATH}\src\libowncloudsync.dll"
File "${BUILD_PATH}\src\lib${APPLICATION_SHORTNAME}sync.dll"
File "${BUILD_PATH}\src\mirall_ca.qm"
File "${BUILD_PATH}\src\mirall_cs_CZ.qm"
File "${BUILD_PATH}\src\mirall_da.qm"
File "${BUILD_PATH}\src\mirall_de.qm"
File "${BUILD_PATH}\src\mirall_el.qm"
File "${BUILD_PATH}\src\mirall_en.qm"
File "${BUILD_PATH}\src\mirall_eo.qm"
File "${BUILD_PATH}\src\mirall_es.qm"
File "${BUILD_PATH}\src\mirall_es_AR.qm"
File "${BUILD_PATH}\src\mirall_et_EE.qm"
File "${BUILD_PATH}\src\mirall_eu.qm"
File "${BUILD_PATH}\src\mirall_fa.qm"
File "${BUILD_PATH}\src\mirall_fi_FI.qm"
File "${BUILD_PATH}\src\mirall_fr.qm"
File "${BUILD_PATH}\src\mirall_gl.qm"
File "${BUILD_PATH}\src\mirall_he.qm"
File "${BUILD_PATH}\src\mirall_hr.qm"
File "${BUILD_PATH}\src\mirall_hu_HU.qm"
File "${BUILD_PATH}\src\mirall_it.qm"
File "${BUILD_PATH}\src\mirall_ja_JP.qm"
File "${BUILD_PATH}\src\mirall_ko.qm"
File "${BUILD_PATH}\src\mirall_lb.qm"
File "${BUILD_PATH}\src\mirall_lt_LT.qm"
File "${BUILD_PATH}\src\mirall_lv.qm"
File "${BUILD_PATH}\src\mirall_mk.qm"
File "${BUILD_PATH}\src\mirall_ms_MY.qm"
File "${BUILD_PATH}\src\mirall_nb_NO.qm"
File "${BUILD_PATH}\src\mirall_nl.qm"
File "${BUILD_PATH}\src\mirall_oc.qm"
File "${BUILD_PATH}\src\mirall_pl.qm"
File "${BUILD_PATH}\src\mirall_pt_BR.qm"
File "${BUILD_PATH}\src\mirall_pt_PT.qm"
File "${BUILD_PATH}\src\mirall_ro.qm"
File "${BUILD_PATH}\src\mirall_ru.qm"
File "${BUILD_PATH}\src\mirall_ru_RU.qm"
File "${BUILD_PATH}\src\mirall_si_LK.qm"
File "${BUILD_PATH}\src\mirall_sk_SK.qm"
File "${BUILD_PATH}\src\mirall_sl.qm"
File "${BUILD_PATH}\src\mirall_sr@latin.qm"
File "${BUILD_PATH}\src\mirall_sv.qm"
File "${BUILD_PATH}\src\mirall_ta_LK.qm"
File "${BUILD_PATH}\src\mirall_th_TH.qm"
File "${BUILD_PATH}\src\mirall_tr.qm"
File "${BUILD_PATH}\src\mirall_uk.qm"
File "${BUILD_PATH}\src\mirall_vi.qm"
File "${BUILD_PATH}\src\mirall_zh_CN.qm"
File "${BUILD_PATH}\src\mirall_zh_TW.qm"
#File "${MING_SHARE}\qt4\translations\qt_ar.qm"
File "${MING_SHARE}\qt4\translations\qt_cs.qm"
File "${MING_SHARE}\qt4\translations\qt_da.qm"
File "${MING_SHARE}\qt4\translations\qt_de.qm"
File "${MING_SHARE}\qt4\translations\qt_es.qm"
File "${MING_SHARE}\qt4\translations\qt_fa.qm"
File "${MING_SHARE}\qt4\translations\qt_fr.qm"
File "${MING_SHARE}\qt4\translations\qt_gl.qm"
File "${MING_SHARE}\qt4\translations\qt_he.qm"
File "${MING_SHARE}\qt4\translations\qt_hu.qm"
File "${MING_SHARE}\qt4\translations\qt_ja.qm"
File "${MING_SHARE}\qt4\translations\qt_ko.qm"
File "${MING_SHARE}\qt4\translations\qt_lt.qm"
File "${MING_SHARE}\qt4\translations\qt_pl.qm"
File "${MING_SHARE}\qt4\translations\qt_pt.qm"
File "${MING_SHARE}\qt4\translations\qt_ru.qm"
File "${MING_SHARE}\qt4\translations\qt_sk.qm"
File "${MING_SHARE}\qt4\translations\qt_sl.qm"
File "${MING_SHARE}\qt4\translations\qt_sv.qm"
File "${MING_SHARE}\qt4\translations\qt_uk.qm"
File "${MING_SHARE}\qt4\translations\qt_zh_CN.qm"
File "${MING_SHARE}\qt4\translations\qt_zh_TW.qm"
File "${MING_SHARE}\qt4\translations\qt_zh_TW.qm"
File "${MING_SHARE}\qt4\translations\qtkeychain_de.qm"
File "${BUILD_PATH}\src\mirall_*.qm"
; Make sure only to copy qt, not qt_help, etc
File "${MING_SHARE}\qt4\translations\qt_??.qm"
File "${MING_SHARE}\qt4\translations\qt_??_??.qm"
File "${MING_SHARE}\qt4\translations\qtkeychain_*.qm"
SetOutPath "$INSTDIR\accessible"
File "${ACCESSIBLE_DLL_PATH}\qtaccessiblewidgets4.dll"
SetOutPath "$INSTDIR\imageformats"
File "${IMAGEFORMATS_DLL_PATH}\qgif4.dll"
File "${IMAGEFORMATS_DLL_PATH}\qjpeg4.dll"
File "${IMAGEFORMATS_DLL_PATH}\qico4.dll"
SetOutPath "$INSTDIR\modules"
; FIXME: fix installation dir of module, currently needs manual copying to
@@ -381,6 +316,7 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
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_APPLICATION
; Other
;File "${MING_BIN}\libpng15-15.dll"
;File "${MING_BIN}\libjpeg-8.dll"
File "${MING_BIN}\libjpeg-8.dll"
File "${MING_BIN}\zlib1.dll"
File "${MING_BIN}\libcrypto-10.dll"
File "${MING_BIN}\libssl-10.dll"
@@ -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_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

View 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)

View File

@@ -2,7 +2,6 @@
#define CONFIG_H
#cmakedefine USE_INOTIFY 1
#cmakedefine WITH_CSYNC 1
#cmakedefine WITH_QTKEYCHAIN 1
#cmakedefine GIT_SHA1 "@GIT_SHA1@"

View File

@@ -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

View File

@@ -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, thats 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.

View File

@@ -37,7 +37,7 @@ Next, install the missing dependencies::
brew install $(brew deps ocsync)
brew install $(brew deps mirall)
To build mirall and cmake, follow the `generic build instructions`_.
To build mirall and csync, follow the `generic build instructions`_.
.. note::
You should not call ``make install`` at any time, since the product of the

View File

@@ -1,17 +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`` `<file>`
write log output to a single file
``--logfile`` `<filename>`
write log output to file <filename>.
``--logdir`` `<dir>`
write log output to dir, one for each sync run.
``--logdir`` `<name>`
write each sync log output in a new file in directory <name>
``--flushlog``
``--logexpire`` `<hours>`
removes logs older than <hours> hours. (to be used with --logdir)
``--logflush``
flush the log file after every write.
``--monoicons``
Use black/white pictograms for systray.
``--confdir`` `<dirname>`
Use the given configuration directory.

View File

@@ -90,7 +90,16 @@ create a huge amount of data, as the log window has a limited buffer.
To write logs to disk, start the client with ``--logfile <file>``, where
``<file`` is the file you want to log to, or ``--logdir <dir>``, where ``<dir>``
is an existing directory. In case of ``--logdir``, each sync run will create a
new file.
new file. To limit the amount of data that accumulates over time, there is another
useful parameter: ``--logexpire <hours>```. If that is combined with ```--logdir```
the client automatically erases log data in that directory that is older than the
given expiry period.
For example, for a long running test where you intend to keep the log data of the
last two days, this would be the command line:
```
owncloud --logdir /tmp/owncloud_logs --logexpire 48
```
:ownCloud server Logfile:
The ownCloud server maintains an ownCloud specific logfile as well. It can and

View File

@@ -26,23 +26,63 @@ also opens the status dialog.
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
----------------------
Changing Your Password and Account Settings
-------------------------------------------
Use the ``Configure`` option. It will open the Connection Wizard, which next to
reconfiguring your connection to use a different user or server also will allow
to change the password for the local account, or to switch from HTTP to HTTPS.
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
Setting up a Proxy
------------------
By default, the configured system proxy will be picked up. This may not be
working reliable on some Linux distributions, as only the ``http_proxy``
variable gets parsed. You can configure a proxy different from your
system default by choosing ``Configure proxy...`` from the menu.
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.
By default, ownCloud expects a HTTP proxy. If you want to specify a SOCKS5
proxy instead, tick the "Use as SOCKSv5 proxy" option.
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
-------

View File

@@ -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

View File

@@ -10,6 +10,8 @@
<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>

BIN
resources/network.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
resources/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -11,16 +11,22 @@ 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/statusdialog.ui
mirall/owncloudsetuppage_ng.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
@@ -63,8 +69,6 @@ set(libsync_SRCS
mirall/syncresult.cpp
mirall/networklocation.cpp
mirall/mirallconfigfile.cpp
mirall/credentialstore.cpp
mirall/owncloudfolder.cpp
mirall/csyncthread.cpp
mirall/fileutils.cpp
mirall/theme.cpp
@@ -73,17 +77,47 @@ set(libsync_SRCS
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/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 )
@@ -114,20 +148,20 @@ if(QTKEYCHAIN_FOUND)
include_directories(${QTKEYCHAIN_INCLUDE_DIR})
endif()
add_library(owncloudsync SHARED ${libsync_SRCS} ${syncMoc})
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}
@@ -138,22 +172,32 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
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
@@ -161,13 +205,23 @@ set(mirall_HEADERS
mirall/systray.h
mirall/folderwizard.h
mirall/owncloudsetupwizard.h
mirall/owncloudwizard.h
mirall/statusdialog.h
wizard/owncloudwizard.h
wizard/owncloudsetuppage.h
wizard/owncloudhttpcredspage.h
wizard/owncloudwizardresultpage.h
wizard/owncloudwizardcommon.h
wizard/owncloudshibbolethcredspage.h
wizard/owncloudadvancedsetuppage.h
mirall/folderstatusmodel.h
mirall/updatedetector.h
mirall/sslerrordialog.h
mirall/logbrowser.h
mirall/proxydialog.h
mirall/fileitemdialog.h
mirall/settingsdialog.h
mirall/generalsettings.h
mirall/networksettings.h
mirall/accountsettings.h
mirall/ignorelisteditor.h
mirall/itemprogressdialog.h
)
if( UNIX AND NOT APPLE)
@@ -208,12 +262,16 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
if(NOT WIN32)
install(FILES
${theme_dir}/colored/${APPLICATION_SHORTNAME}-icon-48.png
DESTINATION share/icons/hicolor/48x48/apps/ RENAME ${APPLICATION_SHORTNAME}.png)
file( GLOB _icons "${theme_dir}/colored/${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
@@ -251,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}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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,39 +66,14 @@ CredentialStore::CredState CredentialStore::state()
return _state;
}
bool CredentialStore::canTryAgain()
{
if( _tries > MAX_LOGIN_ATTEMPTS ) {
qDebug() << "canTryAgain: Max attempts reached.";
return false;
}
/* Since QtKeyChain is required now, it makes sense to only query once. */
if( _state == NotFetched || _state == AsyncWriting ) {
return true;
} else {
return false;
}
}
void CredentialStore::fetchCredentials()
{
MirallConfigFile cfgFile;
if( ++_tries > MAX_LOGIN_ATTEMPTS ) {
qDebug() << "Too many attempts to enter password!";
_state = TooManyAttempts;
emit( fetchCredentialsFinished(false) );
return;
}
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);
@@ -111,26 +85,17 @@ 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->open();
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: {
@@ -177,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
@@ -242,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;
@@ -293,43 +241,36 @@ 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);
@@ -338,9 +279,10 @@ void CredentialStore::saveCredentials( )
_state = AsyncWriting;
job->start();
#endif
}
break;
case CredentialStore::Settings:
cfgFile.writePassword( _passwd );
cfgFile.setPassword( _passwd );
reset();
break;
default:
@@ -369,10 +311,9 @@ void CredentialStore::slotKeyChainWriteFinished( QKeychain::Job *job )
} 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;
_tries = 0;
}
} else {
qDebug() << "Error: KeyChain Write Password Job failed!";

View File

@@ -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,20 +47,16 @@ class CredentialStore : public QObject
public:
enum CredState { NotFetched = 0,
Ok,
UserCanceled,
Fetching,
AsyncFetching,
EntryNotFound,
AccessDeniedByUser,
AccessDenied,
NoKeychainBackend,
Error,
AsyncWriting,
TooManyAttempts };
AsyncWriting };
enum CredentialType {
User = 0,
Settings,
Settings = 0,
KeyChain
};
@@ -97,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:
/**
@@ -125,7 +113,6 @@ signals:
protected slots:
void slotKeyChainReadFinished( QKeychain::Job* );
void slotKeyChainWriteFinished( QKeychain::Job* );
void slotUserDialogDone(int);
private:
explicit CredentialStore(QObject *parent = 0);
@@ -138,9 +125,7 @@ private:
static QString _user;
static QString _url;
static QString _errorMsg;
static int _tries;
static CredentialType _type;
QInputDialog *_inputDialog;
};
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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();
}

View 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

View 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

View 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 &lt;server&gt; as &lt;user&gt;</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>&lt;b&gt;Note:&lt;/b&gt; 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>

File diff suppressed because it is too large Load Diff

View File

@@ -18,15 +18,16 @@
#include <QApplication>
#include <QNetworkReply>
#include <QSslError>
#include <QPointer>
#include <QQueue>
#include "qtsingleapplication.h"
#include "mirall/syncresult.h"
#include "mirall/folder.h"
#include "mirall/logbrowser.h"
#include "mirall/folderman.h"
#include "mirall/fileitemdialog.h"
#include "mirall/systray.h"
#include "mirall/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,32 +56,19 @@ public:
bool giveHelp();
void showHelp();
signals:
protected slots:
void slotAddFolder();
void slotOpenStatus();
void slotRemoveFolder( const QString& );
void slotResetFolder( 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();
@@ -90,56 +78,69 @@ protected:
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
Systray *_tray;
QAction *_actionQuit;
QAction *_actionAddFolder;
QAction *_actionOpenStatus;
QAction *_actionConfigure;
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;
QScopedPointer<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;
LogBrowser *_logBrowser;
QPointer<SettingsDialog> _settingsDialog;
QPointer<ItemProgressDialog> _progressDialog;
QString _logFile;
QString _logDirectory;
int _logExpire;
bool _showLogWindow;
bool _logFlush;

View File

@@ -17,7 +17,6 @@
#include "mirall/owncloudinfo.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/theme.h"
#include "mirall/credentialstore.h"
namespace Mirall {
@@ -28,7 +27,8 @@ ConnectionValidator::ConnectionValidator(QObject *parent) :
}
ConnectionValidator::ConnectionValidator(const QString& connection, QObject *parent)
:_connection(connection)
: QObject(parent),
_connection(connection)
{
ownCloudInfo::instance()->setCustomConfigHandle(_connection);
}
@@ -38,9 +38,42 @@ QStringList ConnectionValidator::errors() const
return _errors;
}
QString ConnectionValidator::statusString( Status )
QString ConnectionValidator::statusString( Status stat ) const
{
return QLatin1String("Get your street creds!");
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;
}
@@ -60,7 +93,7 @@ void ConnectionValidator::checkConnection()
}
}
void ConnectionValidator::slotStatusFound( const QString& url, const QString& versionStr, const QString& version, const QString& edition)
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 << ")";
@@ -82,7 +115,7 @@ void ConnectionValidator::slotStatusFound( const QString& url, const QString& ve
return;
}
QTimer::singleShot( 0, this, SLOT( slotFetchCredentials() ));
QTimer::singleShot( 0, this, SLOT( slotCheckAuthentication() ));
}
// status.php could not be loaded.
@@ -100,77 +133,19 @@ void ConnectionValidator::slotNoStatusFound(QNetworkReply *reply)
}
void ConnectionValidator::slotFetchCredentials()
{
if( _connection.isEmpty() ) {
if( CredentialStore::instance()->canTryAgain() ) {
connect( CredentialStore::instance(), SIGNAL(fetchCredentialsFinished(bool)),
this, SLOT(slotCredentialsFetched(bool)) );
CredentialStore::instance()->fetchCredentials();
}
if( CredentialStore::instance()->state() == CredentialStore::TooManyAttempts ) {
_errors << tr("Too many attempts to get a valid password.");
emit connectionResult( CredentialsTooManyAttempts );
}
} else {
// Pull credentials from Mirall config.
slotCredentialsFetched( true );
}
}
void ConnectionValidator::slotCredentialsFetched( bool ok )
{
qDebug() << "Credentials successfully fetched: " << ok;
disconnect( CredentialStore::instance(), SIGNAL(fetchCredentialsFinished(bool)) );
if( ! ok ) {
Status stat;
_errors << tr("Error: Could not retrieve the password!");
if( CredentialStore::instance()->state() == CredentialStore::UserCanceled ) {
_errors << tr("Password dialog was canceled!");
stat = CredentialsUserCanceled;
} else {
_errors << CredentialStore::instance()->errorMessage();
stat = CredentialError;
}
qDebug() << "Could not fetch credentials" << _errors;
emit connectionResult( stat );
} else {
QString user, pwd;
if( _connection.isEmpty() ) {
user = CredentialStore::instance()->user();
pwd = CredentialStore::instance()->password();
} else {
// in case of reconfiguration, the _connection is set.
MirallConfigFile cfg(_connection);
user = cfg.ownCloudUser();
pwd = cfg.ownCloudPasswd();
}
ownCloudInfo::instance()->setCredentials( user, pwd );
// Credential fetched ok.
QTimer::singleShot( 0, this, SLOT( slotCheckAuthentication() ));
}
}
void ConnectionValidator::slotCheckAuthentication()
{
connect( ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
this, SLOT(slotAuthCheck(QString,QNetworkReply*)));
qDebug() << "# checking for authentication settings.";
ownCloudInfo::instance()->getRequest(QLatin1String("/"), true ); // this call needs to be authenticated.
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 )
{
bool ok = true;
Status stat = Connected;
if( reply->error() == QNetworkReply::AuthenticationRequiredError ||
@@ -178,7 +153,6 @@ void ConnectionValidator::slotAuthCheck( const QString& ,QNetworkReply *reply )
qDebug() << "******** Password is wrong!";
_errors << "The provided credentials are wrong.";
stat = CredentialsWrong;
ok = false;
}
// disconnect from ownCloud Info signals
@@ -186,7 +160,6 @@ void ConnectionValidator::slotAuthCheck( const QString& ,QNetworkReply *reply )
this,SLOT(slotAuthCheck(QString,QNetworkReply*)));
emit connectionResult( stat );
}

View File

@@ -45,12 +45,12 @@ public:
void checkConnection();
QString statusString( Status );
QString statusString( Status ) const;
signals:
void connectionResult( ConnectionValidator::Status );
void connectionAvailable();
void connectionFailed();
// void connectionAvailable();
// void connectionFailed();
public slots:
@@ -58,8 +58,6 @@ protected slots:
void slotStatusFound( const QString&, const QString&, const QString&, const QString& );
void slotNoStatusFound(QNetworkReply *);
void slotFetchCredentials();
void slotCredentialsFetched( bool );
void slotCheckAuthentication();
void slotAuthCheck( const QString& ,QNetworkReply * );

View File

@@ -18,6 +18,7 @@
#include "mirall/theme.h"
#include "mirall/logger.h"
#include "mirall/owncloudinfo.h"
#include "creds/abstractcredentials.h"
#ifdef Q_OS_WIN
#include <windows.h>
@@ -232,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);
@@ -242,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);
@@ -251,9 +266,10 @@ int CSyncThread::treewalkError(TREE_WALK_FILE* file)
if( file &&
(file->instruction == CSYNC_INSTRUCTION_STAT_ERROR ||
file->instruction == CSYNC_INSTRUCTION_ERROR) ) {
file->instruction == CSYNC_INSTRUCTION_ERROR) ) {
_mutex.lock();
_syncedItems[indx]._instruction = file->instruction;
_syncedItems[indx]._errorString = QString::fromUtf8(file->error_string);
_mutex.unlock();
}
@@ -315,11 +331,50 @@ void CSyncThread::startSync()
// cleans up behind us and emits finished() to ease error handling
CSyncRunScopeHelper helper(_csync_ctx, this);
// maybe move this somewhere else where it can influence a running sync?
MirallConfigFile cfg;
int downloadLimit = 0;
if (cfg.useDownloadLimit()) {
downloadLimit = cfg.downloadLimit() * 1000;
}
csync_set_module_property(_csync_ctx, "bandwidth_limit_download", &downloadLimit);
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 );
csync_set_progress_callback( _csync_ctx, progress );
qDebug() << "#### Update start #################################################### >>";
if( csync_update(_csync_ctx) < 0 ) {
@@ -329,7 +384,7 @@ void CSyncThread::startSync()
qDebug() << "<<#### Update end ###########################################################";
if( csync_reconcile(_csync_ctx) < 0 ) {
handleSyncError(_csync_ctx, "cysnc_reconcile");
handleSyncError(_csync_ctx, "csync_reconcile");
return;
}
@@ -363,7 +418,7 @@ void CSyncThread::startSync()
if( walkOk ) {
if( csync_walk_local_tree(_csync_ctx, &walkFinalize, 0) < 0 ||
csync_walk_remote_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.
@@ -373,16 +428,78 @@ void CSyncThread::startSync()
qDebug() << Q_FUNC_INFO << "Sync finished";
}
void CSyncThread::progress(const char *remote_url, enum csync_notify_type_e kind,
long long o1, long long o2, void *userdata)
Progress::Kind CSyncThread::csyncToProgressKind( enum csync_notify_type_e kind )
{
(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);
Progress::Kind pKind = Progress::Invalid;
switch(kind) {
case CSYNC_NOTIFY_INVALID:
pKind = Progress::Invalid;
break;
case CSYNC_NOTIFY_START_SYNC_SEQUENCE:
pKind = Progress::StartSync;
break;
case CSYNC_NOTIFY_START_DOWNLOAD:
pKind = Progress::StartDownload;
break;
case CSYNC_NOTIFY_START_UPLOAD:
pKind = Progress::StartUpload;
break;
case CSYNC_NOTIFY_PROGRESS:
pKind = Progress::Context;
break;
case CSYNC_NOTIFY_FINISHED_DOWNLOAD:
pKind = Progress::EndDownload;
break;
case CSYNC_NOTIFY_FINISHED_UPLOAD:
pKind = Progress::EndUpload;
break;
case CSYNC_NOTIFY_FINISHED_SYNC_SEQUENCE:
pKind = Progress::EndSync;
break;
case CSYNC_NOTIFY_START_DELETE:
pKind = Progress::StartDelete;
break;
case CSYNC_NOTIFY_END_DELETE:
pKind = Progress::EndDelete;
break;
case CSYNC_NOTIFY_ERROR:
pKind = Progress::Error;
break;
default:
pKind = Progress::Invalid;
break;
}
return pKind;
}
void CSyncThread::cb_progress( CSYNC_PROGRESS *progress, void *userdata )
{
if( !progress ) {
qDebug() << "No progress block in progress callback found!";
return;
}
if( !userdata ) {
qDebug() << "No thread given in progress callback!";
return;
}
Progress::Info pInfo;
CSyncThread *thread = static_cast<CSyncThread*>(userdata);
pInfo.kind = thread->csyncToProgressKind( progress->kind );
pInfo.current_file = QUrl::fromEncoded( progress->path ).toString();
pInfo.file_size = progress->file_size;
pInfo.current_file_bytes = progress->curr_bytes;
pInfo.overall_file_count = progress->overall_file_count;
pInfo.current_file_no = progress->current_file_no;
pInfo.overall_transmission_size = progress->overall_transmission_size;
pInfo.overall_current_bytes = progress->current_overall_bytes;
pInfo.timestamp = QDateTime::currentDateTime();
// Connect to something in folder!
thread->transmissionProgress( pInfo );
}
} // ns Mirall

View File

@@ -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;
@@ -50,6 +52,7 @@ signals:
void csyncUnavailable();
void treeWalkResult(const SyncFileItemVector&);
void transmissionProgress( const Progress::Info& progress );
void csyncStateDbFile( const QString& );
void wipeDb();
@@ -60,16 +63,15 @@ signals:
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* );
@@ -83,7 +85,7 @@ private:
bool _hasFiles; // true if there is at least one file that is not ignored or removed
friend class CSyncRunScopeHelper;
friend struct CSyncRunScopeHelper;
};
}

View File

@@ -1,304 +0,0 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <QtGui>
#include "mirall/fileitemdialog.h"
#include "mirall/theme.h"
#include "mirall/syncresult.h"
#include "mirall/logger.h"
#define TYPE_SUCCESS 1
#define TYPE_CONFLICT 2
#define TYPE_NEW 3
#define TYPE_DELETED 4
#define TYPE_ERROR 5
#define TYPE_RENAME 6
#define TYPE_IGNORE 7
#define FILE_TYPE 100
namespace Mirall {
FileItemDialog::FileItemDialog(Theme *theme, QWidget *parent) :
QDialog(parent),
_theme(theme)
{
setupUi(this);
connect(_dialogButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()),
this, SLOT(accept()));
QStringList header;
header << tr("Files");
QString firstColString = tr("File Count");
header << firstColString;
_treeWidget->setHeaderLabels( header );
_treeWidget->setColumnWidth(0, 480);
_timer.setInterval(1000);
connect(&_timer, SIGNAL(timeout()), this, SLOT(slotSetFolderMessage()));
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
QPushButton *copyBtn = _dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
connect(copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
setWindowTitle(tr("Sync Protocol"));
}
void FileItemDialog::setSyncResult( const SyncResult& result )
{
QString folderMessage;
SyncResult::Status syncStatus = result.status();
switch( syncStatus ) {
case SyncResult::Undefined:
folderMessage = tr( "Undefined Folder State" );
break;
case SyncResult::NotYetStarted:
folderMessage = tr( "The folder waits to start syncing." );
break;
case SyncResult::SyncPrepare:
folderMessage = tr( "Determining which files to sync." );
break;
case SyncResult::Unavailable:
folderMessage = tr( "Server is currently not available." );
break;
case SyncResult::SyncRunning:
folderMessage = tr("Sync is running.");
break;
case SyncResult::Success:
folderMessage = tr("Last Sync was successful.");
break;
case SyncResult::Error:
folderMessage = tr( "Syncing Error." );
break;
case SyncResult::SetupError:
folderMessage = tr( "Setup Error." );
break;
default:
folderMessage = tr( "Undefined Error State." );
}
_folderMessage = folderMessage;
_lastSyncTime = result.syncTime();
if( result.errorStrings().count() ) {
_errorLabel->setVisible(true);
_errorLabel->setTextFormat(Qt::RichText);
QString errStr;
foreach( QString err, result.errorStrings() ) {
errStr.append(QString("<p>%1</p>").arg(err));
}
_errorLabel->setText(errStr);
} else {
_errorLabel->setText(QString::null);
_errorLabel->setVisible(false);
}
slotSetFolderMessage();
if( syncStatus == SyncResult::SyncRunning ) {
_timer.stop();
} else {
_timer.start();
}
setSyncFileItems( result.syncFileItemVector() );
}
void FileItemDialog::slotSetFolderMessage()
{
QDateTime now = QDateTime::currentDateTime();
int secs = _lastSyncTime.secsTo(now);
if (secs < 60)
_timelabel->setText(tr("%1 (last finished %n sec. ago)", "", secs).arg(_folderMessage));
else
_timelabel->setText(tr("%1 (last finished %n min. ago)", "", secs/60).arg(_folderMessage));
}
void FileItemDialog::copyToClipboard()
{
QString text;
QTextStream ts(&text);
int topLevelItems = _treeWidget->topLevelItemCount();
for (int i = 0; i < topLevelItems; i++) {
QTreeWidgetItem *item = _treeWidget->topLevelItem(i);
ts << left << qSetFieldWidth(50)
<< item->data(0, Qt::DisplayRole).toString()
<< right << qSetFieldWidth(6)
<< item->data(1, Qt::DisplayRole).toString()
<< endl;
int childItems = item->childCount();
for (int j = 0; j < childItems; j++) {
QTreeWidgetItem *child =item->child(j);
ts << left << qSetFieldWidth(0) << QLatin1String(" ")
<< child->data(0,Qt::DisplayRole).toString()
<< QString::fromLatin1(" (%1)").arg(
child->data(1, Qt::DisplayRole).toString()
)
<< endl;
}
}
QApplication::clipboard()->setText(text);
emit guiLog(tr("Copied to clipboard"), tr("The sync protocol has been copied to the clipboard."));
}
void FileItemDialog::accept()
{
_timer.stop();
QDialog::accept();
}
void FileItemDialog::setSyncFileItems( const SyncFileItemVector& list )
{
_treeWidget->clear();
QStringList strings;
QFont headerFont;
headerFont.setWeight(QFont::Bold);
strings.clear();
strings.append(tr("Synced Files"));
_syncedFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_SUCCESS );
_syncedFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_syncedFileItem);
strings.clear();
strings.append(tr("New Files"));
_newFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_NEW );
_newFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_newFileItem);
strings.clear();
strings.append(tr("Deleted Files"));
_deletedFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_DELETED );
_deletedFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_deletedFileItem);
strings.clear();
strings.append(tr("Renamed Files"));
_renamedFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_RENAME);
_renamedFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_renamedFileItem);
strings.clear();
strings.append(tr("Ignored Files"));
_ignoredFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_IGNORE);
_ignoredFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_renamedFileItem);
strings.clear();
strings.append(tr("Errors"));
_errorFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_ERROR );
_errorFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_errorFileItem);
strings.clear();
strings.append(tr("Conflicts"));
_conflictFileItem = new QTreeWidgetItem( _treeWidget, strings, TYPE_CONFLICT);
_conflictFileItem->setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless);
_treeWidget->addTopLevelItem(_conflictFileItem);
QList<QTreeWidgetItem*> syncedItems;
QList<QTreeWidgetItem*> renamedItems;
QList<QTreeWidgetItem*> newItems;
QList<QTreeWidgetItem*> deletedItems;
QList<QTreeWidgetItem*> ignoredItems;
QList<QTreeWidgetItem*> conflictItems;
QList<QTreeWidgetItem*> errorItems;
quint64 overall_files = 0;
foreach( SyncFileItem item, list ) {
overall_files++;
QString dir;
QStringList str( item._file );
if( item._dir == SyncFileItem::Up ) dir = tr("Uploaded");
if( item._dir == SyncFileItem::Down ) dir = tr("Downloaded");
str << dir;
switch( item._instruction ) {
case CSYNC_INSTRUCTION_NONE:
// do nothing.
break;
case CSYNC_INSTRUCTION_EVAL:
// should not happen
break;
case CSYNC_INSTRUCTION_REMOVE:
case CSYNC_INSTRUCTION_DELETED:
deletedItems.append( new QTreeWidgetItem(_deletedFileItem, str, FILE_TYPE) );
break;
case CSYNC_INSTRUCTION_RENAME:
renamedItems.append( new QTreeWidgetItem(_renamedFileItem, str, FILE_TYPE) );
break;
case CSYNC_INSTRUCTION_NEW:
newItems.append( new QTreeWidgetItem(_newFileItem, str, FILE_TYPE) );
break;
case CSYNC_INSTRUCTION_CONFLICT:
conflictItems.append( new QTreeWidgetItem(_conflictFileItem, str, FILE_TYPE) );
break;
case CSYNC_INSTRUCTION_IGNORE:
ignoredItems.append( new QTreeWidgetItem(_ignoredFileItem, str, FILE_TYPE) );
break;
case CSYNC_INSTRUCTION_SYNC:
case CSYNC_INSTRUCTION_UPDATED:
syncedItems.append( new QTreeWidgetItem(_syncedFileItem, str, FILE_TYPE) );
break;
case CSYNC_INSTRUCTION_STAT_ERROR:
case CSYNC_INSTRUCTION_ERROR:
errorItems.append( new QTreeWidgetItem(_errorFileItem, str, FILE_TYPE) );
break;
default:
break;
}
}
formatHeaderItem( _syncedFileItem, syncedItems );
formatHeaderItem( _newFileItem, newItems );
formatHeaderItem( _deletedFileItem, deletedItems );
formatHeaderItem( _renamedFileItem, renamedItems );
formatHeaderItem( _errorFileItem, errorItems );
formatHeaderItem( _conflictFileItem, conflictItems );
formatHeaderItem( _ignoredFileItem, ignoredItems );
}
void FileItemDialog::formatHeaderItem( QTreeWidgetItem *header, const QList<QTreeWidgetItem*>& list )
{
if( !header ) return;
header->addChildren( list );
int count = list.count();
#if LEAVE_THAT_TO_DESIGNERS
QColor col("#adc5d3");
header->setBackgroundColor(0, col);
header->setBackgroundColor(1, col);
#endif
header->setText(1, QString::number( count ));
if( count ) {
QFont font;
font.setWeight( QFont::Bold );
header->setFont(0, font);
header->setFont(1, font);
header->setExpanded(true);
} else {
header->setExpanded(false);
}
}
}

View File

@@ -25,7 +25,7 @@ class FileUtils
public:
enum SubFolderListOption {
SubFolderNoOptions = 0x0,
SubFolderRecursive = 0x1,
SubFolderRecursive = 0x1
};
Q_DECLARE_FLAGS(SubFolderListOptions, SubFolderListOption)

View File

@@ -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;
@@ -148,9 +197,6 @@ void Folder::setSyncEnabled( bool doit )
{
_enabled = doit;
_watcher->setEventsEnabled( doit );
if( doit && ! _pollTimer->isActive() ) {
_pollTimer->start();
}
qDebug() << "setSyncEnabled - ############################ " << doit;
if( doit ) {
@@ -163,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() );
@@ -250,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)
@@ -267,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());
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!";
}
}
@@ -312,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

View File

@@ -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,12 +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;
/**
* switch sync on or off
* If the sync is switched off, the startSync method is not going to
@@ -71,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.
*/
@@ -145,9 +122,6 @@ public:
*/
virtual void wipe();
QIcon icon( int size ) const;
QTimer *_pollTimer;
signals:
void syncStateChange();
void syncStarted();
@@ -155,7 +129,6 @@ signals:
void scheduleToSync( const QString& );
public slots:
void slotSyncFinished(const SyncResult &);
/**
*
@@ -165,48 +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());
/**
* If folder is network-based, reimplement to react to proxy changes
*/
virtual void setProxy() {}
private slots:
void slotCSyncStarted();
void slotCSyncError(const QString& );
void slotCsyncUnavailable();
void slotCSyncFinished();
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);
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)
@@ -214,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;
};

View File

@@ -14,10 +14,11 @@
#include "mirall/folderman.h"
#include "mirall/mirallconfigfile.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>
@@ -32,6 +33,8 @@
namespace Mirall {
FolderMan* FolderMan::_instance = 0;
FolderMan::FolderMan(QObject *parent) :
QObject(parent),
_syncEnabled( true )
@@ -48,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()
@@ -64,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()
@@ -82,9 +83,6 @@ void FolderMan::slotReparseConfiguration()
int FolderMan::unloadAllFolders()
{
// first terminate sync jobs.
terminateCurrentSync();
int cnt = 0;
// clear the list of existing folders.
@@ -246,43 +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("owncloud") ) {
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 {
qWarning() << "unknown backend" << backend;
return 0;
}
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;
}
@@ -313,7 +296,7 @@ void FolderMan::terminateSyncProcess( const QString& alias )
f->slotTerminateSync();
if(_currentSyncFolder == folderAlias )
_currentSyncFolder = QString::null;
_currentSyncFolder.clear();
}
}
}
@@ -330,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() );
}
}
}
@@ -361,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;
}
@@ -394,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() );
}
}
}
}
@@ -420,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();
}
@@ -533,11 +512,89 @@ bool FolderMan::startFromScratch( const QString& localFolder )
return false;
}
void FolderMan::setProxy()
SyncResult FolderMan::accountStatus(const QList<Folder*> &folders)
{
foreach( Folder *f, _folderMap.values() ) {
f->setProxy();
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

View File

@@ -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,55 +44,43 @@ 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, false otherwise.
* @returns false if the journal could not be removed, true otherwise.
*/
static bool ensureJournalGone(const QString &path);
/**
* Creates a new and empty local directory.
*/
/** Creates a new and empty local directory. */
bool startFromScratch( const QString& );
/**
* called whenever proxy configuration changes
*/
void setProxy();
QString statusToString( SyncResult, bool enabled ) const;
static SyncResult accountStatus( const QList<Folder*> &folders );
signals:
/**
@@ -141,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

View 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

View 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

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,7 +35,7 @@ namespace Mirall
{
FolderWizardSourcePage::FolderWizardSourcePage()
:_folderMap(0)
: QWizardPage()
{
_ui.setupUi(this);
registerField(QLatin1String("sourceFolder*"), _ui.localFolderLineEdit);
@@ -42,10 +45,6 @@ FolderWizardSourcePage::FolderWizardSourcePage()
_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()
@@ -72,15 +71,20 @@ bool FolderWizardSourcePage::isComplete() const
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() )
@@ -119,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 ) {
@@ -162,64 +166,38 @@ void FolderWizardSourcePage::on_localFolderLineEdit_textChanged()
// =================================================================================
FolderWizardTargetPage::FolderWizardTargetPage()
: _dirChecked( false ),
_warnWasVisible(false)
: _warnWasVisible(false)
{
_ui.setupUi(this);
_ui.warnFrame->hide();
registerField(QLatin1String("OCFolderLineEdit"), _ui.OCFolderLineEdit);
connect( _ui.OCFolderLineEdit, SIGNAL(textChanged(QString)),
SLOT(slotFolderTextChanged(QString)));
_timer = new QTimer(this);
_timer->setSingleShot( true );
connect( _timer, SIGNAL(timeout()), SLOT(slotTimerFires()));
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)
{
const QString folder = _ui.OCFolderLineEdit->text();
if( folder.isEmpty() ) return;
_ui.OCFolderLineEdit->setEnabled( false );
qDebug() << "creating folder on ownCloud: " << folder;
ownCloudInfo::instance()->mkdirRequest( folder );
}
@@ -227,32 +205,105 @@ 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
{
QString dir = _ui.OCFolderLineEdit->text();
if( dir.isEmpty() || dir == QLatin1String("/") ) {
showWarn( tr("If you sync the root folder, you can <b>not</b> configure another sync directory."), 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 {
if( _dirChecked ) {
showWarn();
}
return _dirChecked;
showWarn();
return true;
}
}
@@ -268,53 +319,19 @@ void FolderWizardTargetPage::initializePage()
/* 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(directoryListingUpdated(QStringList)),
SLOT(slotUpdateDirectories(QStringList)));
connect(_ui._buttCreateFolder, SIGNAL(clicked()), SLOT(slotCreateRemoteFolder()));
ocInfo->checkInstallation();
_ui.OCFolderLineEdit->setEnabled( false );
QString dir = _ui.OCFolderLineEdit->text();
if( !dir.isEmpty() ) {
slotFolderTextChanged( dir );
}
}
}
void FolderWizardTargetPage::slotOwnCloudFound( const QString& url, const QString& infoStr, const QString& version, const QString& edition)
{
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 );
qDebug() << "ownCloud found on " << url << " with version: " << infoStr;
slotRefreshFolders();
}
}
void FolderWizardTargetPage::slotNoOwnCloudFound( QNetworkReply* error )
void FolderWizardTargetPage::showWarn( const QString& msg ) const
{
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.OCFolderLineEdit->setEnabled( false );
}
void FolderWizardTargetPage::showWarn( const QString& msg, bool showCreateButton ) const
{
_ui._buttCreateFolder->setVisible( showCreateButton && !msg.isEmpty() );
if( msg.isEmpty() ) {
_ui.warnFrame->hide();
@@ -332,34 +349,27 @@ void FolderWizardTargetPage::showWarn( const QString& msg, bool showCreateButton
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

View File

@@ -42,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;
};
@@ -70,19 +70,16 @@ public:
virtual void cleanupPage();
protected slots:
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;
@@ -103,7 +100,7 @@ public:
FolderWizard(QWidget *parent = 0);
~FolderWizard();
void setFolderMap( Folder::Map* );
void setFolderMap( const Folder::Map &map );
private:

View File

@@ -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/>

View File

@@ -7,93 +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</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="0">
<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="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>
</layout>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QFrame" name="warnFrame">
<property name="palette">
<palette>
@@ -191,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>
@@ -222,7 +190,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>102</height>
<height>0</height>
</size>
</property>
</spacer>

View 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

View 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

View 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>

View 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

View 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

View 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>

View File

@@ -47,7 +47,7 @@ INotify::INotify(QObject *parent, int mask)
_buffer = (char *) malloc(_buffer_size);
}
void INotify::slotActivated(int fd)
void INotify::slotActivated(int /*fd*/)
{
int len;
struct inotify_event* event;
@@ -82,7 +82,7 @@ void INotify::slotActivated(int fd)
// reset counter
i = 0;
// while there are enough events in the buffer
while(i + sizeof(struct inotify_event) < len) {
while(i + sizeof(struct inotify_event) < static_cast<unsigned int>(len)) {
// cast an inotify_event
event = (struct inotify_event*)&_buffer[i];
// with the help of watch descriptor, retrieve, corresponding INotify
@@ -115,14 +115,17 @@ INotify::~INotify()
delete _notifier;
}
void INotify::addPath(const QString &path)
bool INotify::addPath(const QString &path)
{
// Add an inotify watch.
int wd = inotify_add_watch(_fd, path.toUtf8().constData(), _mask);
if( wd > -1 )
if( wd > -1 ) {
_wds[path] = wd;
else
return true;
} else {
qDebug() << "WRN: Could not watch " << path << ':' << strerror(errno);
return false;
}
}
void INotify::removePath(const QString &path)

View File

@@ -38,10 +38,7 @@ public:
INotify(QObject *parent, int mask);
~INotify();
static void initialize();
static void cleanup();
void addPath(const QString &name);
bool addPath(const QString &name);
void removePath(const QString &name);
QStringList directories() const;

View File

@@ -0,0 +1,311 @@
/*
* 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/itemprogressdialog.h"
#include "mirall/syncresult.h"
#include "mirall/logger.h"
#include "mirall/utility.h"
#include "mirall/theme.h"
#include "mirall/folderman.h"
#include "mirall/syncfileitem.h"
#include "ui_itemprogressdialog.h"
namespace Mirall {
ItemProgressDialog::ItemProgressDialog(Application*, QWidget *parent) :
QDialog(parent),
ErrorIndicatorRole( Qt::UserRole +1 ),
_ui(new Ui::ItemProgressDialog)
{
_ui->setupUi(this);
connect(_ui->_dialogButtonBox->button(QDialogButtonBox::Close), SIGNAL(clicked()),
this, SLOT(accept()));
connect(ProgressDispatcher::instance(), SIGNAL(progressInfo(QString,Progress::Info)),
this, SLOT(slotProgressInfo(QString,Progress::Info)));
connect(ProgressDispatcher::instance(), SIGNAL(progressSyncProblem(const QString&,const Progress::SyncProblem&)),
this, SLOT(slotProgressErrors(const QString&, const Progress::SyncProblem&)));
QStringList header;
header << tr("Time");
header << tr("File");
header << tr("Folder");
header << tr("Action");
header << tr("Size");
_ui->_treeWidget->setHeaderLabels( header );
_ui->_treeWidget->setColumnWidth(1, 180);
_ui->_treeWidget->setColumnCount(5);
_ui->_treeWidget->setRootIsDecorated(false);
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
connect(copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
setWindowTitle(tr("Sync Protocol"));
}
void ItemProgressDialog::setSyncResultStatus(const SyncResult& result )
{
if( result.errorStrings().count() ) {
_ui->_errorLabel->setVisible(true);
_ui->_errorLabel->setTextFormat(Qt::RichText);
QString errStr;
QStringList errors = result.errorStrings();
int cnt = errors.size();
bool appendDots = false;
if( cnt > 3 ) {
cnt = 3;
appendDots = true;
}
for( int i = 0; i < cnt; i++) {
errStr.append(QString("%1<br/>").arg(errors.at(i)));
}
if( appendDots ) {
errStr.append(QString("..."));
}
_ui->_errorLabel->setText(errStr);
} else {
_ui->_errorLabel->setText(QString::null);
_ui->_errorLabel->setVisible(false);
}
}
void ItemProgressDialog::setSyncResult( const SyncResult& result )
{
setSyncResultStatus(result);
const QString& folder = result.folder();
qDebug() << "Setting sync result for folder " << folder;
SyncFileItemVector::const_iterator i;
const SyncFileItemVector& items = result.syncFileItemVector();
QDateTime dt = QDateTime::currentDateTime();
for (i = items.begin(); i != items.end(); ++i) {
const SyncFileItem& item = *i;
QString errMsg;
// handle ignored files here.
if( item._instruction == CSYNC_INSTRUCTION_IGNORE ) {
QStringList columns;
QString timeStr = timeString(dt);
QString longTimeStr = timeString(dt, QLocale::LongFormat);
columns << timeStr;
columns << item._file;
columns << folder;
if( item._type == SyncFileItem::File ) {
errMsg = tr("File ignored.");
} else if( item._type == SyncFileItem::Directory ){
errMsg = tr("Directory ignored.");
} else if( item._type == SyncFileItem::SoftLink ) {
errMsg = tr("Soft Link ignored.");
} else {
errMsg = tr("Ignored.");
}
columns << errMsg;
QTreeWidgetItem *twitem = new QTreeWidgetItem(columns);
twitem->setData(0, ErrorIndicatorRole, QVariant(true) );
twitem->setToolTip(0, longTimeStr);
twitem->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Problem, true));
_ui->_treeWidget->addTopLevelItem(twitem);
}
}
}
void ItemProgressDialog::setupList()
{
// get the folders to set up the top level list.
Folder::Map map = FolderMan::instance()->map();
SyncResult lastResult;
QDateTime dt;
bool haveSyncResult = false;
foreach( Folder *f, map.values() ) {
if( f->syncResult().syncTime() > dt ) {
dt = f->syncResult().syncTime();
lastResult = f->syncResult();
haveSyncResult = true;
}
}
if( haveSyncResult ) {
setSyncResult(lastResult);
}
QList<Progress::Info> progressList = ProgressDispatcher::instance()->recentChangedItems(0); // All.
QHash <QString, int> folderHash;
foreach( Progress::Info info, progressList ) {
slotProgressInfo( info.folder, info );
folderHash[info.folder] = 1;
}
QList<Progress::SyncProblem> problemList = ProgressDispatcher::instance()->recentProblems(0);
foreach( Progress::SyncProblem prob, problemList ) {
slotProgressErrors(prob.folder, prob);
folderHash[prob.folder] = 1;
}
}
ItemProgressDialog::~ItemProgressDialog()
{
delete _ui;
}
void ItemProgressDialog::copyToClipboard()
{
QString text;
QTextStream ts(&text);
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
for (int i = 0; i < topLevelItems; i++) {
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(i);
ts << left << qSetFieldWidth(0)
<< item->data(0, Qt::DisplayRole).toString()
<< endl;
int childItems = item->childCount();
for (int j = 0; j < childItems; j++) {
QTreeWidgetItem *child =item->child(j);
// time stamp
ts << left << qSetFieldWidth(10)
<< child->data(0,Qt::DisplayRole).toString()
// file name
<< qSetFieldWidth(50)
<< child->data(1,Qt::DisplayRole).toString()
// action
<< qSetFieldWidth(15)
<< child->data(2, Qt::DisplayRole).toString()
// size
<< qSetFieldWidth(10)
<< child->data(3, Qt::DisplayRole).toString()
<< qSetFieldWidth(0)
<< endl;
}
}
QApplication::clipboard()->setText(text);
emit guiLog(tr("Copied to clipboard"), tr("The sync protocol has been copied to the clipboard."));
}
void ItemProgressDialog::accept()
{
QDialog::accept();
}
void ItemProgressDialog::cleanErrors( const QString& /* folder */ ) // FIXME: Use the folder to detect which errors can be deleted.
{
_problemCounter = 0;
QList<QTreeWidgetItem*> wipeList;
int itemCnt = _ui->_treeWidget->topLevelItemCount();
for( int cnt = 0; cnt < itemCnt; cnt++ ) {
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
bool isErrorItem = item->data(0, ErrorIndicatorRole).toBool();
if( isErrorItem ) {
wipeList.append(item);
}
}
qDeleteAll(wipeList.begin(), wipeList.end());
}
QString ItemProgressDialog::timeString(QDateTime dt, QLocale::FormatType format) const
{
QLocale loc = QLocale::system();
QString timeStr;
QDate today = QDate::currentDate();
if( format == QLocale::NarrowFormat ) {
if( dt.date().day() == today.day() ) {
timeStr = loc.toString(dt.time(), QLocale::NarrowFormat);
} else {
timeStr = loc.toString(dt, QLocale::NarrowFormat);
}
} else {
timeStr = loc.toString(dt, format);
}
return timeStr;
}
void ItemProgressDialog::slotProgressErrors( const QString& folder, const Progress::SyncProblem& problem )
{
QStringList columns;
QString timeStr = timeString(problem.timestamp);
QString longTimeStr = timeString(problem.timestamp, QLocale::LongFormat);
columns << timeStr;
columns << problem.current_file;
columns << folder;
QString errMsg = tr("Problem: %1").arg(problem.error_message);
#if 0
if( problem.error_code == 507 ) {
errMsg = tr("No more storage space available on server.");
}
#endif
columns << errMsg;
QTreeWidgetItem *item = new QTreeWidgetItem(columns);
item->setData(0, ErrorIndicatorRole, QVariant(true) );
// Maybe we should not set the error icon for all problems but distinguish
// by error_code. A quota problem is considered an error, others might not??
item->setIcon(0, Theme::instance()->syncStateIcon(SyncResult::Error, true));
item->setToolTip(0, longTimeStr);
_ui->_treeWidget->addTopLevelItem(item);
Q_UNUSED(item);
}
void ItemProgressDialog::slotProgressInfo( const QString& folder, const Progress::Info& progress )
{
if( progress.kind == Progress::StartSync ) {
cleanErrors( folder );
}
if( progress.kind == Progress::EndSync ) {
// decorateFolderItem( folder );
}
// Ingore other events than finishing an individual up- or download.
if( !(progress.kind == Progress::EndDownload || progress.kind == Progress::EndUpload || progress.kind == Progress::EndDelete)) {
return;
}
QStringList columns;
QString timeStr = timeString(progress.timestamp);
QString longTimeStr = timeString(progress.timestamp, QLocale::LongFormat);
columns << timeStr;
columns << progress.current_file;
columns << progress.folder;
columns << Progress::asResultString(progress.kind);
columns << Utility::octetsToString( progress.file_size );
QTreeWidgetItem *item = new QTreeWidgetItem(columns);
item->setToolTip(0, longTimeStr);
_ui->_treeWidget->addTopLevelItem(item);
Q_UNUSED(item);
}
}

View File

@@ -16,52 +16,51 @@
#include <QDialog>
#include <QDateTime>
#include <QTimer>
#include <QLocale>
#include "mirall/syncfileitem.h"
#include "mirall/progressdispatcher.h"
#include "ui_fileitemdialog.h"
#include "ui_itemprogressdialog.h"
namespace Mirall {
class Theme;
class SyncResult;
namespace Ui {
class ItemProgressDialog;
}
class Application;
class FileItemDialog : public QDialog, public Ui::_fileItemDialog
class ItemProgressDialog : public QDialog
{
Q_OBJECT
public:
explicit FileItemDialog(Theme*, QWidget *parent = 0);
void setSyncResult( const SyncResult& );
explicit ItemProgressDialog(Application *app, QWidget *parent = 0);
~ItemProgressDialog();
void setupList();
void setSyncResult( const SyncResult& result );
signals:
public slots:
void accept();
void slotProgressInfo( const QString& folder, const Progress::Info& progress );
void slotProgressErrors( const QString& folder, const Progress::SyncProblem& problem );
protected slots:
void slotSetFolderMessage();
void copyToClipboard();
signals:
void guiLog(const QString&, const QString&);
private:
void setSyncFileItems( const SyncFileItemVector& list );
void formatHeaderItem( QTreeWidgetItem *, const QList<QTreeWidgetItem*>& );
void setSyncResultStatus(const SyncResult& result );
void cleanErrors( const QString& folder );
QString timeString(QDateTime dt, QLocale::FormatType format = QLocale::NarrowFormat) const;
QTreeWidgetItem *_newFileItem;
QTreeWidgetItem *_syncedFileItem;
QTreeWidgetItem *_deletedFileItem;
QTreeWidgetItem *_renamedFileItem;
QTreeWidgetItem *_errorFileItem;
QTreeWidgetItem *_conflictFileItem;
QTreeWidgetItem *_ignoredFileItem;
Theme *_theme;
QString _folderMessage;
QDateTime _lastSyncTime;
QTimer _timer;
const int ErrorIndicatorRole;
Ui::ItemProgressDialog *_ui;
int _problemCounter;
};
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>_fileItemDialog</class>
<widget class="QWidget" name="_fileItemDialog">
<class>Mirall::ItemProgressDialog</class>
<widget class="QWidget" name="Mirall::ItemProgressDialog">
<property name="geometry">
<rect>
<x>0</x>
@@ -43,7 +43,7 @@
<bool>true</bool>
</property>
<property name="columnCount">
<number>2</number>
<number>4</number>
</property>
<column>
<property name="text">
@@ -55,13 +55,16 @@
<string notr="true">2</string>
</property>
</column>
</widget>
</item>
<item>
<widget class="QLabel" name="_timelabel">
<property name="text">
<string>TextLabel</string>
</property>
<column>
<property name="text">
<string>3</string>
</property>
</column>
<column>
<property name="text">
<string>4</string>
</property>
</column>
</widget>
</item>
<item>

View File

@@ -28,6 +28,8 @@
#include <QMessageBox>
#include <QCoreApplication>
#include <QDebug>
#include <QSettings>
#include <QAction>
#include "mirall/mirallconfigfile.h"
#include "mirall/logger.h"
@@ -44,11 +46,6 @@ LogWidget::LogWidget(QWidget *parent)
font.setFamily(QLatin1String("Courier New"));
font.setFixedPitch(true);
document()->setDefaultFont( font );
MirallConfigFile cfg;
int lines = cfg.maxLogLines();
// qDebug() << "# ## Have " << lines << " Loglines!";
document()->setMaximumBlockCount( lines );
}
// ==============================================================================
@@ -56,9 +53,9 @@ LogWidget::LogWidget(QWidget *parent)
LogBrowser::LogBrowser(QWidget *parent) :
QDialog(parent),
_logWidget( new LogWidget(parent) ),
_logstream(0),
_doFileFlush(false)
{
setObjectName("LogBrowser"); // for save/restoreGeometry()
setWindowTitle(tr("Log Output"));
setMinimumWidth(600);
@@ -115,6 +112,18 @@ LogBrowser::LogBrowser(QWidget *parent) :
// Direct connection for log comming from this thread, and queued for the one in a different thread
connect(Logger::instance(), SIGNAL(newLog(QString)),this,SLOT(slotNewLog(QString)), Qt::AutoConnection);
QAction *showLogWindow = new QAction(this);
showLogWindow->setShortcut(QKeySequence("F12"));
connect(showLogWindow, SIGNAL(triggered()), SLOT(close()));
addAction(showLogWindow);
MirallConfigFile cfg;
cfg.restoreGeometry(this);
int lines = cfg.maxLogLines();
// qDebug() << "# ## Have " << lines << " Loglines!";
_logWidget->document()->setMaximumBlockCount( lines );
}
LogBrowser::~LogBrowser()
@@ -123,9 +132,7 @@ LogBrowser::~LogBrowser()
void LogBrowser::slotNewLog( const QString& msg )
{
if( _logWidget->isVisible() ) {
_logWidget->appendPlainText( msg );
}
_logWidget->appendPlainText( msg );
if( _logstream ) {
(*_logstream) << msg << endl;
@@ -138,9 +145,16 @@ void LogBrowser::setLogFile( const QString & name, bool flush )
if( _logstream ) {
_logFile.close();
}
_logFile.setFileName( name );
if(!_logFile.open(QIODevice::WriteOnly)) {
bool openSucceeded = false;
if (name == QLatin1String("-")) {
openSucceeded = _logFile.open(1, QIODevice::WriteOnly);
} else {
_logFile.setFileName( name );
openSucceeded = _logFile.open(QIODevice::WriteOnly);
}
if(!openSucceeded) {
QMessageBox::warning(
this,
tr("Error"),
@@ -211,4 +225,11 @@ void LogBrowser::slotClearLog()
_logWidget->clear();
}
void LogBrowser::closeEvent(QCloseEvent *event)
{
MirallConfigFile cfg;
cfg.saveGeometry(this);
QWidget::closeEvent(event);
}
} // namespace

View File

@@ -47,9 +47,8 @@ public:
void setLogFile(const QString& , bool );
signals:
public slots:
protected:
void closeEvent(QCloseEvent *event);
protected slots:
void slotNewLog( const QString &msg );

View File

@@ -22,7 +22,6 @@ Logger::Logger( QObject* parent)
: QObject(parent),
_showTime(true)
{
}
Logger *Logger::instance()
@@ -39,6 +38,21 @@ void Logger::destroy()
}
}
void Logger::postGuiLog(const QString &title, const QString &message)
{
emit guiLog(title, message);
}
void Logger::postOptionalGuiLog(const QString &title, const QString &message)
{
emit optionalGuiLog(title, message);
}
void Logger::postGuiMessage(const QString &title, const QString &message)
{
emit postGuiMessage(title, message);
}
void Logger::log(Log log)
{
QString msg;

View File

@@ -46,9 +46,15 @@ public:
static Logger* instance();
static void destroy();
void postGuiLog(const QString& title, const QString& message);
void postOptionalGuiLog(const QString& title, const QString& message);
void postGuiMessage(const QString& title, const QString& message);
signals:
void newLog(const QString&);
void guiLog(const QString&, const QString&);
void guiMessage(const QString&, const QString&);
void optionalGuiLog(const QString&, const QString&);
protected:
Logger(QObject* parent=0);

View 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 <QNetworkRequest>
#include "mirall/mirallaccessmanager.h"
#include "mirall/utility.h"
namespace Mirall
{
MirallAccessManager::MirallAccessManager(QObject* parent)
: QNetworkAccessManager (parent)
{}
QNetworkReply* MirallAccessManager::createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData)
{
QNetworkRequest newRequest(request);
newRequest.setRawHeader( QByteArray("User-Agent"), Utility::userAgentString());
return QNetworkAccessManager::createRequest (op, newRequest, outgoingData);
}
} // ns Mirall

View File

@@ -0,0 +1,35 @@
/*
* 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_ACCESS_MANAGER_H
#define MIRALL_ACCESS_MANAGER_H
#include <QNetworkAccessManager>
namespace Mirall
{
class MirallAccessManager : public QNetworkAccessManager
{
Q_OBJECT
public:
MirallAccessManager(QObject* parent = 0);
protected:
QNetworkReply* createRequest(QNetworkAccessManager::Operation op, const QNetworkRequest& request, QIODevice* outgoingData = 0);
};
} // ns Mirall
#endif

View File

@@ -18,97 +18,192 @@
#include "mirall/owncloudinfo.h"
#include "mirall/owncloudtheme.h"
#include "mirall/theme.h"
#include "mirall/credentialstore.h"
#include "creds/abstractcredentials.h"
#include "creds/credentialsfactory.h"
#include <QtCore>
#include <QtGui>
#define DEFAULT_REMOTE_POLL_INTERVAL 30000 // default remote poll time in milliseconds
#define CA_CERTS_KEY QLatin1String("CaCertificates")
#define DEFAULT_MAX_LOG_LINES 20000
namespace Mirall {
static const char urlC[] = "url";
static const char authTypeC[] = "authType";
static const char caCertsKeyC[] = "CaCertificates";
static const char remotePollIntervalC[] = "remotePollInterval";
static const char forceSyncIntervalC[] = "forceSyncInterval";
static const char monoIconsC[] = "monoIcons";
static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications";
static const char skipUpdateCheckC[] = "skipUpdateCheck";
static const char geometryC[] = "geometry";
static const char proxyHostC[] = "Proxy/host";
static const char proxyTypeC[] = "Proxy/type";
static const char proxyPortC[] = "Proxy/port";
static const char proxyUserC[] = "Proxy/user";
static const char proxyPassC[] = "Proxy/pass";
static const char proxyNeedsAuthC[] = "Proxy/needsAuth";
static const char useUploadLimitC[] = "BWLimit/useUploadLimit";
static const char useDownloadLimitC[] = "BWLimit/useDownloadLimit";
static const char uploadLimitC[] = "BWLimit/uploadLimit";
static const char downloadLimitC[] = "BWLimit/downloadLimit";
static const char seenVersionC[] = "Updater/seenVersion";
static const char maxLogLinesC[] = "Logging/maxLogLines";
QString MirallConfigFile::_oCVersion;
QString MirallConfigFile::_confDir = QString::null;
bool MirallConfigFile::_askedUser = false;
QMap< QString, MirallConfigFile::SharedCreds > MirallConfigFile::credentialsPerConfig;
MirallConfigFile::MirallConfigFile( const QString& appendix )
:_customHandle(appendix)
MirallConfigFile::MirallConfigFile( const QString& appendix, bool useOldConfig )
{
if (useOldConfig && !appendix.isEmpty()) {
QString oldConfigFile = configFile();
_customHandle = appendix;
QString newConfigFile = configFile();
QFile::copy(oldConfigFile, newConfigFile);
} else {
_customHandle = appendix;
}
QSettings::setDefaultFormat(QSettings::IniFormat);
if (! credentialsPerConfig.contains(_customHandle)) {
QString con( _customHandle );
if( _customHandle.isEmpty() ) con = defaultConnection();
const QString config = configFile();
qDebug() << "Loading config: " << config;
QSettings settings(config, QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
QString type = settings.value( QLatin1String(authTypeC) ).toString();
qDebug() << "Getting credentials of type " << type << " for " << _customHandle;
credentialsPerConfig.insert(_customHandle, SharedCreds(CredentialsFactory::create (type)));
}
}
void MirallConfigFile::setConfDir(const QString &value)
{
if( value.isEmpty() ) return;
QString dirPath = value;
if( dirPath.isEmpty() ) return;
QFileInfo fi(value);
if( fi.exists() && fi.isDir() ) {
qDebug() << "** Using custom config dir " << value;
_confDir=value;
QFileInfo fi(dirPath);
if ( !fi.exists() && !fi.isAbsolute() ) {
QDir::current().mkdir(dirPath);
QDir dir = QDir::current();
dir.cd(dirPath);
fi.setFile(dir.path());
}
if( fi.exists() && fi.isDir() ) {
dirPath = fi.absoluteFilePath();
qDebug() << "** Using custom config dir " << dirPath;
_confDir=dirPath;
}
}
bool MirallConfigFile::optionalDesktopNotifications() const
{
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
return settings.value(QLatin1String(optionalDesktopNoficationsC), true).toBool();
}
void MirallConfigFile::setOptionalDesktopNotifications(bool show)
{
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(QLatin1String(optionalDesktopNoficationsC), show);
settings.sync();
}
QString MirallConfigFile::seenVersion() const
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup(QLatin1String("Updater"));
return settings.value(QLatin1String("seenVersion")).toString();
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
return settings.value(QLatin1String(seenVersionC)).toString();
}
void MirallConfigFile::setSeenVersion(const QString &version)
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup(QLatin1String("Updater"));
settings.setValue(QLatin1String("seenVersion"), version);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(QLatin1String(seenVersionC), version);
settings.sync();
}
void MirallConfigFile::saveGeometry(QWidget *w)
{
Q_ASSERT(!w->objectName().isNull());
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup(w->objectName());
settings.setValue(QLatin1String(geometryC), w->saveGeometry());
settings.sync();
}
void MirallConfigFile::restoreGeometry(QWidget *w)
{
w->restoreGeometry(getValue(geometryC, w->objectName()).toByteArray());
}
QString MirallConfigFile::configPath() const
{
QString dir = _confDir;
if( _confDir.isEmpty() )
if( _confDir.isEmpty() ) {
_confDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
}
QString dir = _confDir;
if( !dir.endsWith(QLatin1Char('/')) ) dir.append(QLatin1Char('/'));
return dir;
}
QString MirallConfigFile::excludeFile() const
QString MirallConfigFile::excludeFile(Scope scope) const
{
// prefer sync-exclude.lst, but if it does not exist, check for
// exclude.lst for compatibility reasonsin the user writeable
// exclude.lst for compatibility reasons in the user writeable
// directories.
const QString exclFile("sync-exclude.lst");
QFileInfo fi;
fi.setFile( configPath(), exclFile );
if( ! fi.isReadable() ) {
fi.setFile( configPath(), QLatin1String("exclude.lst") );
if (scope != SystemScope) {
fi.setFile( configPath(), exclFile );
if( ! fi.isReadable() ) {
fi.setFile( configPath(), QLatin1String("exclude.lst") );
}
if( ! fi.isReadable() ) {
fi.setFile( configPath(), exclFile );
}
}
// Check alternative places...
if( ! fi.isReadable() ) {
#ifdef Q_OS_WIN32
fi.setFile( QApplication::applicationDirPath(), exclFile );
if (scope != UserScope) {
// Check alternative places...
if( ! fi.isReadable() ) {
#ifdef Q_OS_WIN
fi.setFile( QApplication::applicationDirPath(), exclFile );
#endif
#ifdef Q_OS_LINUX
fi.setFile( QString("/etc/%1").arg(Theme::instance()->appName()), exclFile );
#ifdef Q_OS_UNIX
fi.setFile( QString("/etc/%1").arg(Theme::instance()->appName()), exclFile );
#endif
#ifdef Q_OS_MAC
// exec path is inside the bundle
fi.setFile( QApplication::applicationDirPath(),
QLatin1String("../Resources/") + exclFile );
// exec path is inside the bundle
fi.setFile( QApplication::applicationDirPath(),
QLatin1String("../Resources/") + exclFile );
#endif
}
}
if( fi.isReadable() ) {
qDebug() << " ==> returning exclude file path: " << fi.absoluteFilePath();
return fi.absoluteFilePath();
}
qDebug() << " ==> returning exclude file path: " << fi.absoluteFilePath();
return fi.absoluteFilePath();
qDebug() << "EMPTY exclude file path!";
return QString::null;
}
@@ -118,13 +213,13 @@ QString MirallConfigFile::configFile() const
if( qApp->applicationName().isEmpty() ) {
qApp->setApplicationName( Theme::instance()->appNameGUI() );
}
QString dir = configPath() + Theme::instance()->configFileName();
QString file = configPath() + Theme::instance()->configFileName();
if( !_customHandle.isEmpty() ) {
dir.append( QLatin1Char('_'));
dir.append( _customHandle );
qDebug() << " OO Custom config file in use: " << dir;
file.append( QLatin1Char('_'));
file.append( _customHandle );
qDebug() << __PRETTY_FUNCTION__ << " OO Custom config file in use: " << file;
}
return dir;
return file;
}
bool MirallConfigFile::exists()
@@ -143,122 +238,92 @@ bool MirallConfigFile::connectionExists( const QString& conn )
QString con = conn;
if( conn.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat);
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
return settings.contains( QString::fromLatin1("%1/url").arg( conn ) );
settings.beginGroup(conn);
return settings.contains( QLatin1String(urlC) );
}
void MirallConfigFile::writeOwncloudConfig( const QString& connection,
const QString& url,
const QString& user,
const QString& passwd )
AbstractCredentials* credentials)
{
const QString file = configFile();
bool skipPwd = false; // can be refactored - remove it.
qDebug() << "*** writing mirall config to " << file << " Skippwd: " << skipPwd;
qDebug() << "*** writing mirall config to " << file;
QSettings settings( file, QSettings::IniFormat);
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( connection );
settings.setValue( QLatin1String("url"), url );
settings.setValue( QLatin1String("user"), user );
#ifdef WITH_QTKEYCHAIN
// Password is stored to QtKeyChain now by default in CredentialStore
// The CredentialStore calls clearPasswordFromConfig after the creds
// were successfully wiritten to delete the passwd entry from config.
qDebug() << "Going to delete the password from settings file.";
#else
if( !skipPwd )
writePassword( passwd );
#endif
if( !skipPwd )
writePassword( passwd );
else
clearPasswordFromConfig(); // wipe the password.
settings.setValue( QLatin1String("nostoredpassword"), QVariant(skipPwd) );
settings.setValue( QLatin1String(urlC), url );
settings.setValue(QLatin1String(authTypeC), credentials->authType());
credentialsPerConfig.insert(_customHandle, SharedCreds(credentials));
settings.sync();
// check the perms, only read-write for the owner.
QFile::setPermissions( file, QFile::ReadOwner|QFile::WriteOwner );
// Store credentials temporar until the config is finalized.
ownCloudInfo::instance()->setCredentials( user, passwd, _customHandle );
//ownCloudInfo::instance()->setCredentials( user, passwd, _customHandle );
}
// This method is called after the password was successfully stored into the
// QKeyChain in CredentialStore.
void MirallConfigFile::clearPasswordFromConfig( const QString& connection )
void MirallConfigFile::storeData(const QString& group, const QString& key, const QVariant& value)
{
const QString file = configFile();
QString con( defaultConnection() );
if( !connection.isEmpty() )
con = connection;
const QString con(group.isEmpty() ? defaultConnection() : group);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
QSettings settings( file, QSettings::IniFormat);
settings.setIniCodec( "UTF-8" );
settings.beginGroup( con );
settings.remove(QLatin1String("passwd"));
settings.remove(QLatin1String("password"));
settings.beginGroup(con);
settings.setValue(key, value);
settings.sync();
}
bool MirallConfigFile::writePassword( const QString& passwd, const QString& connection )
QVariant MirallConfigFile::retrieveData(const QString& group, const QString& key) const
{
const QString file = configFile();
QString pwd( passwd );
QString con( defaultConnection() );
if( !connection.isEmpty() )
con = connection;
const QString con(group.isEmpty() ? defaultConnection() : group);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
QSettings settings( file, QSettings::IniFormat);
settings.setIniCodec( "UTF-8" );
// store password into settings file.
settings.beginGroup( con );
QByteArray pwdba = pwd.toUtf8();
settings.setValue( QLatin1String("passwd"), QVariant(pwdba.toBase64()) );
settings.sync();
return true;
settings.beginGroup(con);
return settings.value(key);
}
// set the url, called from redirect handling.
void MirallConfigFile::setOwnCloudUrl( const QString& connection, const QString & url )
void MirallConfigFile::removeData(const QString& group, const QString& key)
{
const QString file = configFile();
const QString con(group.isEmpty() ? defaultConnection() : group);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
QSettings settings( file, QSettings::IniFormat);
settings.setIniCodec( "UTF-8" );
settings.beginGroup( connection );
settings.setValue( QLatin1String("url"), url );
settings.beginGroup(con);
settings.remove(key);
}
settings.sync();
bool MirallConfigFile::dataExists(const QString& group, const QString& key) const
{
const QString con(group.isEmpty() ? defaultConnection() : group);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup(con);
return settings.contains(key);
}
QByteArray MirallConfigFile::caCerts( )
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QByteArray certs = settings.value( CA_CERTS_KEY ).toByteArray();
return certs;
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
return settings.value( QLatin1String(caCertsKeyC) ).toByteArray();
}
void MirallConfigFile::setCaCerts( const QByteArray & certs )
{
const QString file = configFile();
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
QSettings settings( file, QSettings::IniFormat);
settings.setIniCodec( "UTF-8" );
settings.setValue( CA_CERTS_KEY, certs );
settings.setValue( QLatin1String(caCertsKeyC), certs );
settings.sync();
}
@@ -271,7 +336,8 @@ void MirallConfigFile::removeConnection( const QString& connection )
qDebug() << " removing the config file for connection " << con;
// Currently its just removing the entire config file
QSettings settings( configFile(), QSettings::IniFormat);
// TODO: Eh? Shouldn't it try to load a file under configFile() and set it to INI?
QSettings settings;
settings.setIniCodec( "UTF-8" );
settings.beginGroup( con );
settings.remove(QString::null); // removes all content from the group
@@ -282,41 +348,22 @@ void MirallConfigFile::removeConnection( const QString& connection )
* returns the configured owncloud url if its already configured, otherwise an empty
* string.
* The returned url always has a trailing hash.
* If webdav is true, the webdav-server url is returned.
*/
QString MirallConfigFile::ownCloudUrl( const QString& connection, bool webdav ) const
QString MirallConfigFile::ownCloudUrl( const QString& connection) const
{
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
QString url = settings.value( QLatin1String("url") ).toString();
QString url = settings.value( QLatin1String(urlC) ).toString();
if( ! url.isEmpty() ) {
if( ! url.endsWith(QLatin1Char('/'))) url.append(QLatin1String("/"));
if( webdav ) url.append( QLatin1String("remote.php/webdav/") );
}
qDebug() << "Returning configured owncloud url: " << url;
return url;
}
QString MirallConfigFile::ownCloudUser( const QString& connection ) const
{
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup( con );
QString user = settings.value( QLatin1String("user") ).toString();
// qDebug() << "Returning configured owncloud user: " << user;
return user;
return url;
}
int MirallConfigFile::remotePollInterval( const QString& connection ) const
@@ -324,11 +371,11 @@ int MirallConfigFile::remotePollInterval( const QString& connection ) const
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
int remoteInterval = settings.value( QLatin1String("remotePollInterval"), DEFAULT_REMOTE_POLL_INTERVAL ).toInt();
int remoteInterval = settings.value( QLatin1String(remotePollIntervalC), DEFAULT_REMOTE_POLL_INTERVAL ).toInt();
if( remoteInterval < 5000) {
qDebug() << "Remote Interval is less than 5 seconds, reverting to" << DEFAULT_REMOTE_POLL_INTERVAL;
remoteInterval = DEFAULT_REMOTE_POLL_INTERVAL;
@@ -345,52 +392,29 @@ void MirallConfigFile::setRemotePollInterval(int interval, const QString &connec
qDebug() << "Remote Poll interval of " << interval << " is below fife seconds.";
return;
}
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
settings.setValue("remotePollInterval", interval );
settings.setValue(QLatin1String(remotePollIntervalC), interval );
settings.sync();
}
bool MirallConfigFile::passwordStorageAllowed( const QString& connection )
quint64 MirallConfigFile::forceSyncInterval(const QString& connection) const
{
uint pollInterval = remotePollInterval(connection);
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
bool skipPwd = settings.value( QLatin1String("nostoredpassword"), false ).toBool();
return !skipPwd;
}
QString MirallConfigFile::ownCloudPasswd( const QString& connection ) const
{
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup( con );
QByteArray pwdba = settings.value(QLatin1String("passwd")).toByteArray();
if( !pwdba.isEmpty() ) {
return QString::fromUtf8( QByteArray::fromBase64(pwdba) );
quint64 interval = settings.value( QLatin1String(forceSyncIntervalC), 10 * pollInterval ).toULongLong();
if( interval < pollInterval) {
qDebug() << "Force sync interval is less than the remote poll inteval, reverting to" << pollInterval;
interval = pollInterval;
}
// check the password entry, cleartext from before
// read it and convert to base64, delete the cleartext entry.
QString p = settings.value(QLatin1String("password")).toString();
if( ! p.isEmpty() ) {
// its there, save base64-encoded and delete.
pwdba = p.toUtf8();
settings.setValue( QLatin1String("passwd"), QVariant(pwdba.toBase64()) );
settings.remove( QLatin1String("password") );
settings.sync();
}
return p;
return interval;
}
QString MirallConfigFile::ownCloudVersion() const
@@ -410,11 +434,11 @@ bool MirallConfigFile::ownCloudSkipUpdateCheck( const QString& connection ) cons
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
bool skipIt = settings.value( QLatin1String("skipUpdateCheck"), false ).toBool();
bool skipIt = settings.value( QLatin1String(skipUpdateCheckC), false ).toBool();
return skipIt;
}
@@ -424,31 +448,27 @@ void MirallConfigFile::setOwnCloudSkipUpdateCheck( bool skip, const QString& con
QString con( connection );
if( connection.isEmpty() ) con = defaultConnection();
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.beginGroup( con );
settings.setValue( QLatin1String("skipUpdateCheck"), QVariant(skip) );
settings.setValue( QLatin1String(skipUpdateCheckC), QVariant(skip) );
settings.sync();
}
int MirallConfigFile::maxLogLines() const
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup(QLatin1String("Logging"));
int logLines = settings.value( QLatin1String("maxLogLines"), 20000 ).toInt();
return logLines;
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
return settings.value( QLatin1String(maxLogLinesC), DEFAULT_MAX_LOG_LINES ).toInt();
}
void MirallConfigFile::setMaxLogLines( int lines )
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup(QLatin1String("Logging"));
settings.setValue(QLatin1String("maxLogLines"), lines);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(QLatin1String(maxLogLinesC), lines);
settings.sync();
}
@@ -475,6 +495,8 @@ void MirallConfigFile::acceptCustomConfig()
QString srcConfig = configFile(); // this considers the custom handle
credentialsPerConfig.insert(QString(), credentialsPerConfig[_customHandle]);
credentialsPerConfig.remove(_customHandle);
_customHandle.clear();
QString targetConfig = configFile();
QString targetBak = targetConfig + QLatin1String(".bak");
@@ -496,18 +518,7 @@ void MirallConfigFile::acceptCustomConfig()
}
QFile::remove( targetBak );
// inform the credential store about the password change.
QString url = ownCloudUrl();
QString user = ownCloudUser();
QString pwd = ownCloudPasswd();
bool allow = passwordStorageAllowed();
if( pwd.isEmpty() ) {
qDebug() << "Password is empty, skipping to write cred store.";
} else {
CredentialStore::instance()->setCredentials(url, user, pwd, allow);
CredentialStore::instance()->saveCredentials();
}
credentialsPerConfig[QString()]->persistForUrl(ownCloudUrl());
}
void MirallConfigFile::setProxyType(int proxyType,
@@ -516,61 +527,129 @@ void MirallConfigFile::setProxyType(int proxyType,
const QString& user,
const QString& pass)
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup(QLatin1String("proxy"));
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(QLatin1String("type"), proxyType);
settings.setValue(QLatin1String(proxyTypeC), proxyType);
if (proxyType == QNetworkProxy::HttpProxy ||
proxyType == QNetworkProxy::Socks5Proxy) {
settings.setValue(QLatin1String("host"), host);
settings.setValue(QLatin1String("port"), port);
settings.setValue(QLatin1String("needsAuth"), needsAuth);
settings.setValue(QLatin1String("user"), user);
settings.setValue(QLatin1String("pass"), pass.toUtf8().toBase64());
settings.setValue(QLatin1String(proxyHostC), host);
settings.setValue(QLatin1String(proxyPortC), port);
settings.setValue(QLatin1String(proxyNeedsAuthC), needsAuth);
settings.setValue(QLatin1String(proxyUserC), user);
settings.setValue(QLatin1String(proxyPassC), pass.toUtf8().toBase64());
}
settings.sync();
}
QVariant MirallConfigFile::getValue(const QString& param, const QString& group) const
QVariant MirallConfigFile::getValue(const QString& param, const QString& group,
const QVariant& defaultValue) const
{
QSettings settings( configFile(), QSettings::IniFormat );
settings.setIniCodec( "UTF-8" );
settings.beginGroup(group);
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
if (!group.isEmpty())
settings.beginGroup(group);
return settings.value(param);
return settings.value(param, defaultValue);
}
void MirallConfigFile::setValue(const QString& key, const QVariant &value)
{
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(key, value);
}
int MirallConfigFile::proxyType() const
{
return getValue(QLatin1String("type"), QLatin1String("proxy")).toInt();
return getValue(QLatin1String(proxyTypeC)).toInt();
}
QString MirallConfigFile::proxyHostName() const
{
return getValue(QLatin1String("host"), QLatin1String("proxy")).toString();
return getValue(QLatin1String(proxyHostC)).toString();
}
int MirallConfigFile::proxyPort() const
{
return getValue(QLatin1String("port"), QLatin1String("proxy")).toInt();
return getValue(QLatin1String(proxyPortC)).toInt();
}
bool MirallConfigFile::proxyNeedsAuth() const
{
return getValue(QLatin1String("needsAuth"), QLatin1String("proxy")).toBool();
return getValue(QLatin1String(proxyNeedsAuthC)).toBool();
}
QString MirallConfigFile::proxyUser() const
{
return getValue(QLatin1String("user"), QLatin1String("proxy")).toString();
return getValue(QLatin1String(proxyUserC)).toString();
}
QString MirallConfigFile::proxyPassword() const
{
QByteArray pass = getValue(QLatin1String("pass"), QLatin1String("proxy")).toByteArray();
QByteArray pass = getValue(proxyPassC).toByteArray();
return QString::fromUtf8(QByteArray::fromBase64(pass));
}
int MirallConfigFile::useUploadLimit() const
{
return getValue(useUploadLimitC, QString::null, -1).toInt();
}
bool MirallConfigFile::useDownloadLimit() const
{
return getValue(useDownloadLimitC, QString::null, false).toBool();
}
void MirallConfigFile::setUseUploadLimit(int val)
{
setValue(useUploadLimitC, val);
}
void MirallConfigFile::setUseDownloadLimit(bool enable)
{
setValue(useDownloadLimitC, enable);
}
int MirallConfigFile::uploadLimit() const
{
return getValue(uploadLimitC, QString::null, 10).toInt();
}
int MirallConfigFile::downloadLimit() const
{
return getValue(downloadLimitC, QString::null, 80).toInt();
}
void MirallConfigFile::setUploadLimit(int kbytes)
{
setValue(uploadLimitC, kbytes);
}
void MirallConfigFile::setDownloadLimit(int kbytes)
{
setValue(downloadLimitC, kbytes);
}
bool MirallConfigFile::monoIcons() const
{
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
return settings.value(QLatin1String(monoIconsC), false).toBool();
}
void MirallConfigFile::setMonoIcons(bool useMonoIcons)
{
QSettings settings(configFile(), QSettings::IniFormat);
settings.setIniCodec("UTF-8");
settings.setValue(QLatin1String(monoIconsC), useMonoIcons);
}
AbstractCredentials* MirallConfigFile::getCredentials() const
{
return credentialsPerConfig[_customHandle].data();
}
}

View File

@@ -15,33 +15,26 @@
#ifndef MIRALLCONFIGFILE_H
#define MIRALLCONFIGFILE_H
#include <QSharedPointer>
#include <QString>
#include <QVariant>
class QVariant;
class QWidget;
namespace Mirall {
class AbstractCredentials;
class MirallConfigFile
{
/* let only CredentialStore read the password from the file. All other classes
* should work with CredentialStore to get the credentials. */
friend class CredentialStore;
friend class ConnectionValidator;
public:
MirallConfigFile( const QString& appendix = QString() );
MirallConfigFile( const QString& appendix = QString(), bool useOldConfig = false );
// enum customMediaType {
// oCSetupTop, // ownCloud connect page
// oCSetupSide,
// oCSetupBottom,
// oCSetupFixUrl,
// oCSetupResultTop // ownCloud connect result page
// };
enum Scope { UserScope, SystemScope };
QString configPath() const;
QString configFile() const;
QString excludeFile() const;
QString excludeFile(Scope scope) const;
bool exists();
@@ -50,15 +43,13 @@ public:
void writeOwncloudConfig( const QString& connection,
const QString& url,
const QString& user,
const QString& passwd );
AbstractCredentials* credentials);
AbstractCredentials* getCredentials() const;
void removeConnection( const QString& connection = QString() );
QString ownCloudUser( const QString& connection = QString() ) const;
QString ownCloudUrl( const QString& connection = QString(), bool webdav = false ) const;
void setOwnCloudUrl(const QString &connection, const QString& );
QString ownCloudUrl( const QString& connection = QString() ) const;
// the certs do not depend on a connection.
QByteArray caCerts();
@@ -81,11 +72,17 @@ public:
/* Set poll interval. Value in microseconds has to be larger than 5000 */
void setRemotePollInterval(int interval, const QString& connection = QString() );
/* Force sync interval, in milliseconds */
quint64 forceSyncInterval(const QString &connection = QString()) const;
// Custom Config: accept the custom config to become the main one.
void acceptCustomConfig();
// Custom Config: remove the custom config file.
void cleanupCustomConfig();
bool monoIcons() const;
void setMonoIcons(bool);
// proxy settings
void setProxyType(int proxyType,
const QString& host = QString(),
@@ -100,26 +97,47 @@ public:
QString proxyUser() const;
QString proxyPassword() const;
/** 0: no limit, 1: manual, >0: automatic */
int useUploadLimit() const;
bool useDownloadLimit() const;
void setUseUploadLimit(int);
void setUseDownloadLimit(bool);
/** in kbyte/s */
int uploadLimit() const;
int downloadLimit() const;
void setUploadLimit(int kbytes);
void setDownloadLimit(int kbytes);
static void setConfDir(const QString &value);
bool optionalDesktopNotifications() const;
void setOptionalDesktopNotifications(bool show);
QString seenVersion() const;
void setSeenVersion(const QString &version);
void saveGeometry(QWidget *w);
void restoreGeometry(QWidget *w);
protected:
// these classes can only be access from CredentialStore as a friend class.
QString ownCloudPasswd( const QString& connection = QString() ) const;
void clearPasswordFromConfig( const QString& connect = QString() );
bool writePassword( const QString& passwd, const QString& connection = QString() );
void storeData(const QString& group, const QString& key, const QVariant& value);
QVariant retrieveData(const QString& group, const QString& key) const;
void removeData(const QString& group, const QString& key);
bool dataExists(const QString& group, const QString& key) const;
private:
QVariant getValue(const QString& param, const QString& group) const;
QVariant getValue(const QString& param, const QString& group = QString::null,
const QVariant& defaultValue = QVariant()) const;
void setValue(const QString& key, const QVariant &value);
private:
typedef QSharedPointer< AbstractCredentials > SharedCreds;
static bool _askedUser;
static QString _oCVersion;
static QString _confDir;
static QMap< QString, SharedCreds > credentialsPerConfig;
QString _customHandle;
};
}

View File

@@ -0,0 +1,161 @@
/*
* 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 "networksettings.h"
#include "ui_networksettings.h"
#include "mirall/theme.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/application.h"
#include "mirall/utility.h"
#include "mirall/mirallconfigfile.h"
#include <QNetworkProxy>
namespace Mirall {
NetworkSettings::NetworkSettings(QWidget *parent) :
QWidget(parent),
_ui(new Ui::NetworkSettings)
{
_ui->setupUi(this);
_ui->hostLineEdit->setPlaceholderText(tr("Hostname of proxy server"));
_ui->userLineEdit->setPlaceholderText(tr("Username for proxy server"));
_ui->passwordLineEdit->setPlaceholderText(tr("Password for proxy server"));
_ui->typeComboBox->addItem(tr("HTTP(S) proxy"), QNetworkProxy::HttpProxy);
_ui->typeComboBox->addItem(tr("SOCKS5 proxy"), QNetworkProxy::Socks5Proxy);
_ui->authRequiredcheckBox->setEnabled(true);
connect(_ui->manualProxyRadioButton, SIGNAL(toggled(bool)),
_ui->manualSettings, SLOT(setEnabled(bool)));
connect(_ui->manualProxyRadioButton, SIGNAL(toggled(bool)),
_ui->typeComboBox, SLOT(setEnabled(bool)));
connect(_ui->authRequiredcheckBox, SIGNAL(toggled(bool)),
_ui->authWidgets, SLOT(setEnabled(bool)));
loadProxySettings();
loadBWLimitSettings();
// proxy
connect(_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(saveProxySettings()));
connect(_ui->proxyButtonGroup, SIGNAL(buttonClicked(int)), SLOT(saveProxySettings()));
connect(_ui->hostLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->userLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->passwordLineEdit, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->portSpinBox, SIGNAL(editingFinished()), SLOT(saveProxySettings()));
connect(_ui->uploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
connect(_ui->noUploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
connect(_ui->autoUploadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
connect(_ui->downloadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
connect(_ui->noDownloadLimitRadioButton, SIGNAL(clicked()), SLOT(saveBWLimitSettings()));
connect(_ui->downloadSpinBox, SIGNAL(valueChanged(int)), SLOT(saveBWLimitSettings()));
connect(_ui->uploadSpinBox, SIGNAL(valueChanged(int)), SLOT(saveBWLimitSettings()));
}
NetworkSettings::~NetworkSettings()
{
delete _ui;
}
void NetworkSettings::loadProxySettings()
{
// load current proxy settings
Mirall::MirallConfigFile cfgFile;
int type = cfgFile.proxyType();
switch (type) {
case QNetworkProxy::NoProxy:
_ui->noProxyRadioButton->setChecked(true);
break;
case QNetworkProxy::DefaultProxy:
_ui->systemProxyRadioButton->setChecked(true);
break;
case QNetworkProxy::Socks5Proxy:
case QNetworkProxy::HttpProxy:
_ui->typeComboBox->setCurrentIndex(_ui->typeComboBox->findData(type));
_ui->manualProxyRadioButton->setChecked(true);
break;
default:
break;
}
_ui->hostLineEdit->setText(cfgFile.proxyHostName());
int port = cfgFile.proxyPort();
if (port == 0)
port = 8080;
_ui->portSpinBox->setValue(port);
if (!cfgFile.proxyUser().isEmpty())
{
_ui->authRequiredcheckBox->setChecked(true);
_ui->userLineEdit->setText(cfgFile.proxyUser());
_ui->passwordLineEdit->setText(cfgFile.proxyPassword());
}
}
void NetworkSettings::loadBWLimitSettings()
{
MirallConfigFile cfgFile;
_ui->downloadLimitRadioButton->setChecked(cfgFile.useDownloadLimit());
int uploadLimit = cfgFile.useUploadLimit();
if ( uploadLimit >= 1 ) {
_ui->uploadLimitRadioButton->setChecked(true);
} else if (uploadLimit == 0){
_ui->noDownloadLimitRadioButton->setChecked(true);
} else {
_ui->autoUploadLimitRadioButton->setChecked(true);
}
_ui->downloadSpinBox->setValue(cfgFile.downloadLimit());
_ui->uploadSpinBox->setValue(cfgFile.uploadLimit());
}
void NetworkSettings::saveProxySettings()
{
MirallConfigFile cfgFile;
if (_ui->noProxyRadioButton->isChecked()){
cfgFile.setProxyType(QNetworkProxy::NoProxy);
} else if (_ui->systemProxyRadioButton->isChecked()){
cfgFile.setProxyType(QNetworkProxy::DefaultProxy);
} else if (_ui->manualProxyRadioButton->isChecked()) {
int type = _ui->typeComboBox->itemData(_ui->typeComboBox->currentIndex()).toInt();
bool needsAuth = _ui->authRequiredcheckBox->isChecked();
QString user = _ui->userLineEdit->text();
QString pass = _ui->passwordLineEdit->text();
cfgFile.setProxyType(type, _ui->hostLineEdit->text(),
_ui->portSpinBox->value(), needsAuth, user, pass);
}
emit proxySettingsChanged();
}
void NetworkSettings::saveBWLimitSettings()
{
MirallConfigFile cfgFile;
cfgFile.setUseDownloadLimit(_ui->downloadLimitRadioButton->isChecked());
if (_ui->uploadLimitRadioButton->isChecked()) {
cfgFile.setUseUploadLimit(1);
} else if (_ui->noUploadLimitRadioButton->isChecked()) {
cfgFile.setUseUploadLimit(0);
} else if (_ui->autoUploadLimitRadioButton->isChecked()) {
cfgFile.setUseUploadLimit(-1);
}
cfgFile.setDownloadLimit(_ui->downloadSpinBox->value());
cfgFile.setUploadLimit(_ui->uploadSpinBox->value());
}
} // namespace Mirall

View 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_NETWORKSETTINGS_H
#define MIRALL_NETWORKSETTINGS_H
#include <QWidget>
namespace Mirall {
namespace Ui {
class NetworkSettings;
}
class NetworkSettings : public QWidget
{
Q_OBJECT
public:
explicit NetworkSettings(QWidget *parent = 0);
~NetworkSettings();
signals:
void proxySettingsChanged();
private slots:
void saveProxySettings();
void saveBWLimitSettings();
private:
void loadProxySettings();
void loadBWLimitSettings();
Ui::NetworkSettings *_ui;
};
} // namespace Mirall
#endif // MIRALL_NETWORKSETTINGS_H

View File

@@ -0,0 +1,371 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Mirall::NetworkSettings</class>
<widget class="QWidget" name="Mirall::NetworkSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>518</width>
<height>384</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="proxyGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>Proxy Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QRadioButton" name="noProxyRadioButton">
<property name="text">
<string>No Proxy</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">proxyButtonGroup</string>
</attribute>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="systemProxyRadioButton">
<property name="text">
<string>Use system proxy</string>
</property>
<attribute name="buttonGroup">
<string notr="true">proxyButtonGroup</string>
</attribute>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="manualProxyRadioButton">
<property name="text">
<string>Specify proxy manually as</string>
</property>
<attribute name="buttonGroup">
<string notr="true">proxyButtonGroup</string>
</attribute>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="typeComboBox">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QWidget" name="manualSettings" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="rightMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="hostLabel">
<property name="text">
<string>Host</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="hostLineEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="portSpinBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>8080</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="authRequiredcheckBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Proxy server requires authentication</string>
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="authWidgets" native="true">
<property name="enabled">
<bool>false</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="userLineEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="passwordLineEdit">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Download Bandwidth</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QRadioButton" name="downloadLimitRadioButton">
<property name="text">
<string>Limit to</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="downloadSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>80</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>KBytes/s</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>147</width>
<height>25</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="2">
<widget class="QRadioButton" name="noDownloadLimitRadioButton">
<property name="text">
<string>No Limit</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="horizontalSpacer">
<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>
</widget>
</item>
<item row="1" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Upload Bandwidth</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0" colspan="2">
<widget class="QRadioButton" name="noUploadLimitRadioButton">
<property name="text">
<string>No Limit</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QRadioButton" name="autoUploadLimitRadioButton">
<property name="text">
<string>Limit automatically</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="uploadLimitRadioButton">
<property name="text">
<string>Limit to</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QSpinBox" name="uploadSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>KBytes/s</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
<zorder>autoUploadLimitRadioButton</zorder>
<zorder>uploadLimitRadioButton</zorder>
<zorder>noUploadLimitRadioButton</zorder>
</widget>
</item>
<item row="2" column="1">
<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>
<connection>
<sender>downloadLimitRadioButton</sender>
<signal>toggled(bool)</signal>
<receiver>downloadSpinBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>73</x>
<y>69</y>
</hint>
<hint type="destinationlabel">
<x>131</x>
<y>78</y>
</hint>
</hints>
</connection>
<connection>
<sender>uploadLimitRadioButton</sender>
<signal>toggled(bool)</signal>
<receiver>uploadSpinBox</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>322</x>
<y>101</y>
</hint>
<hint type="destinationlabel">
<x>411</x>
<y>106</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="proxyButtonGroup"/>
</buttongroups>
</ui>

View File

@@ -1,138 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OwncloudCredentialsPage</class>
<widget class="QWidget" name="OwncloudCredentialsPage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>576</width>
<height>302</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<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>ownCloud Credentials</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>Provide ownCloud Credentials</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:11pt; color:#585858;&quot;&gt;Please enter the username and password for your ownCloud Instance. &lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11pt; color:#585858;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:11pt; color:#585858;&quot;&gt;If you do not wish to store the credentials on your local machine, check the box. ownCloud will prompt you for your password every time the client starts.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>
</layout>
</widget>
</item>
<item row="2" column="0">
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>ownCloud-User:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="OCUserEdit">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Password:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="OCPasswdEdit">
<property name="text">
<string/>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="cbPwdNoLocalStore">
<property name="text">
<string>Do not store password on local machine.</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>568</width>
<height>158</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -1,532 +0,0 @@
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
* Copyright (C) by Klaas Freitag <freitag@owncloud.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/owncloudfolder.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/owncloudinfo.h"
#include "mirall/credentialstore.h"
#include "mirall/logger.h"
#include "mirall/utility.h"
#include <csync.h>
#include <QDebug>
#include <QDir>
#include <QUrl>
#include <QMutexLocker>
#include <QThread>
#include <QStringList>
#include <QTextStream>
#include <QTimer>
#include <QNetworkProxy>
#include <QNetworkAccessManager>
#include <QNetworkProxyFactory>
#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) );
}
static QString replaceScheme(const QString &urlStr)
{
QUrl url( urlStr );
if( url.scheme() == QLatin1String("http") ) {
url.setScheme( QLatin1String("owncloud") );
} else {
// connect SSL!
url.setScheme( QLatin1String("ownclouds") );
}
return url.toString();
}
ownCloudFolder::ownCloudFolder(const QString &alias,
const QString &mpath,
const QString &secondPath,
QObject *parent)
: Folder(alias, mpath, secondPath, parent)
, _thread(0)
, _csync(0)
, _csyncError(false)
, _csyncUnavail(false)
{
ServerActionNotifier *notifier = new ServerActionNotifier(this);
connect(notifier, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
connect(this, SIGNAL(syncFinished(SyncResult)), notifier, SLOT(slotSyncFinished(SyncResult)));
qDebug() << "****** ownCloud folder using watcher *******";
// The folder interval is set in the folder parent class.
QString url = replaceScheme(secondPath);
QString localpath = path();
if( csync_create( &_csync_ctx, localpath.toUtf8().data(), url.toUtf8().data() ) < 0 ) {
qDebug() << "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);
QString excludeList = cfgFile.excludeFile();
if( !excludeList.isEmpty() ) {
qDebug() << "==== added CSync exclude List: " << excludeList.toUtf8();
csync_add_exclude_list( _csync_ctx, excludeList.toUtf8() );
}
csync_set_auth_callback( _csync_ctx, getauth );
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;
}
setProxy();
}
}
ownCloudFolder::~ownCloudFolder()
{
if( _thread ) {
_thread->quit();
csync_request_abort(_csync_ctx);
_thread->wait();
}
delete _csync;
// Destroy csync here.
csync_destroy(_csync_ctx);
}
void ownCloudFolder::setProxy()
{
if( _csync_ctx ) {
/* Store proxy */
MirallConfigFile cfgFile;
QUrl proxyUrl(cfgFile.ownCloudUrl());
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" << cfgFile.ownCloudUrl();
} else {
qDebug() << "Passing" << proxy.hostName() << "of proxy type " << proxy.type()
<< " to csync for" << cfgFile.ownCloudUrl();
}
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() );
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
}
}
const char* ownCloudFolder::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 ownCloudFolder::getauth(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata
)
{
int re = 0;
QMutex mutex;
QString qPrompt = QString::fromLatin1( prompt ).trimmed();
QString user = CredentialStore::instance()->user();
QString pwd = CredentialStore::instance()->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 {
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;
}
}
return re;
}
bool ownCloudFolder::isBusy() const
{
return ( _thread && _thread->isRunning() );
}
QString ownCloudFolder::secondPath() const
{
QString re(Folder::secondPath());
MirallConfigFile cfg;
QString ocUrl = cfg.ownCloudUrl(QString::null, true);
if (ocUrl.endsWith(QLatin1Char('/')))
ocUrl.chop(1);
// qDebug() << "**** " << ocUrl << " <-> " << re;
if( re.startsWith( ocUrl ) ) {
re.remove( ocUrl );
}
return re;
}
void ownCloudFolder::startSync()
{
startSync( QStringList() );
}
void ownCloudFolder::startSync(const QStringList &pathList)
{
if (!_csync_ctx) {
qDebug() << Q_FUNC_INFO << "_csync_ctx is empty. probably because csync_init has 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;
MirallConfigFile cfgFile;
_syncResult.clearErrors();
_syncResult.setStatus( SyncResult::SyncPrepare );
emit syncStateChange();
qDebug() << "*** Start syncing";
_thread = new QThread(this);
_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);
_thread->start();
QMetaObject::invokeMethod(_csync, "startSync", Qt::QueuedConnection);
emit syncStarted();
}
void ownCloudFolder::slotCSyncStarted()
{
qDebug() << " * csync thread started";
_syncResult.setStatus(SyncResult::SyncRunning);
emit syncStateChange();
}
void ownCloudFolder::slotCSyncError(const QString& err)
{
_errors.append( err );
_csyncError = true;
}
void ownCloudFolder::slotCsyncUnavailable()
{
_csyncUnavail = true;
}
void ownCloudFolder::slotCSyncFinished()
{
qDebug() << "-> CSync Finished slot with error " << _csyncError;
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 {
_syncResult.setStatus(SyncResult::Success);
}
if( _thread && _thread->isRunning() ) {
_thread->quit();
}
emit syncFinished( _syncResult );
}
void ownCloudFolder::slotThreadTreeWalkResult(const SyncFileItemVector& items)
{
_syncResult.setSyncFileItemVector(items);
}
void ownCloudFolder::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();
}
void ownCloudFolder::slotLocalPathChanged( const QString& dir )
{
QDir notifiedDir(dir);
QDir localPath( path() );
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!";
}
}
}
// 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 ownCloudFolder::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();
}
}
ServerActionNotifier::ServerActionNotifier(QObject *parent)
: QObject(parent)
{
}
void ServerActionNotifier::slotSyncFinished(const SyncResult &result)
{
SyncFileItemVector items = result.syncFileItemVector();
if (items.count() == 0)
return;
int newItems = 0;
int removedItems = 0;
int updatedItems = 0;
SyncFileItem firstItemNew;
SyncFileItem firstItemDeleted;
SyncFileItem firstItemUpdated;
foreach (const SyncFileItem &item, items) {
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;
default:
// nothing.
break;
}
}
}
if (newItems > 0) {
QString file = QDir::toNativeSeparators(firstItemNew._file);
if (newItems == 1)
emit guiLog(tr("New file available"), tr("'%1' has been synced to this machine.").arg(file));
else
emit guiLog(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)
emit guiLog(tr("File removed"), tr("'%1' has been removed.").arg(file));
else
emit guiLog(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)
emit guiLog(tr("File updated"), tr("'%1' has been updated.").arg(file));
else
emit guiLog(tr("Files updated"), tr("'%1' and %n other file(s) have been updated.",
"", updatedItems-1).arg(file));
}
}
void ownCloudFolder::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();
}
}
} // ns

View File

@@ -1,116 +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.
*/
#ifndef MIRALL_ownCloudFolder_H
#define MIRALL_ownCloudFolder_H
#include <QMutex>
#include <QThread>
#include <QStringList>
#include "mirall/folder.h"
#include "mirall/csyncthread.h"
class QProcess;
class QTimer;
namespace Mirall {
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
};
typedef SyncFileStatus_s SyncFileStatus;
class ServerActionNotifier : public QObject
{
Q_OBJECT
public:
ServerActionNotifier(QObject *parent = 0);
public slots:
void slotSyncFinished(const SyncResult &result);
signals:
void guiLog(const QString&, const QString&);
void sendResults();
private:
};
class ownCloudFolder : public Folder
{
Q_OBJECT
public:
ownCloudFolder(const QString &alias,
const QString &path,
const QString &secondPath, QObject *parent = 0L);
virtual ~ownCloudFolder();
QString secondPath() const;
virtual bool isBusy() const;
virtual void startSync(const QStringList &pathList);
virtual void wipe();
/* get status about a singel file. */
SyncFileStatus fileStatus( const QString& );
void setProxy();
public slots:
void startSync();
void slotTerminateSync();
void slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool*);
protected slots:
void slotLocalPathChanged( const QString& );
void slotThreadTreeWalkResult(const SyncFileItemVector& );
private slots:
void slotCSyncStarted();
void slotCSyncError(const QString& );
void slotCsyncUnavailable();
void slotCSyncFinished();
private:
static int getauth(const char *prompt,
char *buf,
size_t len,
int echo,
int verify,
void *userdata
);
const char* proxyTypeToCStr(QNetworkProxy::ProxyType type);
QString _secondPath;
QThread *_thread;
CSyncThread *_csync;
QStringList _errors;
bool _csyncError;
bool _csyncUnavail;
bool _wipeDb;
SyncFileItemVector _items;
CSYNC *_csync_ctx;
};
}
#endif

View File

@@ -15,33 +15,19 @@
#include "mirall/owncloudinfo.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/theme.h"
#include "mirall/utility.h"
#include "mirall/logger.h"
#include "creds/abstractcredentials.h"
#include <QtCore>
#include <QtGui>
#include <QAuthenticator>
#if QT46_IMPL
#include <QHttp>
#endif
#define DEFAULT_CONNECTION QLatin1String("default");
static const char WEBDAV_PATH[] = "remote.php/webdav/";
namespace Mirall
{
class oCCookieJar : public QNetworkCookieJar
{
public:
QList<QNetworkCookie> cookiesForUrl ( const QUrl & url ) const {
QList<QNetworkCookie> list;
return list;
}
};
ownCloudInfo *ownCloudInfo::_instance = 0;
ownCloudInfo* ownCloudInfo::instance()
@@ -62,13 +48,16 @@ ownCloudInfo* ownCloudInfo::instance()
ownCloudInfo::ownCloudInfo() :
QObject(0),
_manager(0)
_manager(0),
_authAttempts(0),
_lastQuotaUsedBytes(0),
_lastQuotaTotalBytes(0)
{
_connection = Theme::instance()->appName();
connect(this, SIGNAL(guiLog(QString,QString)),
Logger::instance(), SIGNAL(guiLog(QString,QString)));
setNetworkAccessManager( new QNetworkAccessManager( this ) );
// this will set credentials specific qnam
setCustomConfigHandle(QString());
}
void ownCloudInfo::setNetworkAccessManager( QNetworkAccessManager* qnam )
@@ -83,18 +72,7 @@ void ownCloudInfo::setNetworkAccessManager( QNetworkAccessManager* qnam )
connect( _manager, SIGNAL( sslErrors(QNetworkReply*, QList<QSslError>)),
this, SIGNAL(sslFailed(QNetworkReply*, QList<QSslError>)) );
// The authenticationRequired signal is not handled because the creds are set
// in the request header.
#if 0
connect( _manager, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
this, SLOT(slotAuthentication(QNetworkReply*,QAuthenticator*)));
#endif
// no cookie jar so far.
_manager->setCookieJar(new oCCookieJar);
_certsUntrusted = false;
}
ownCloudInfo::~ownCloudInfo()
@@ -106,6 +84,8 @@ void ownCloudInfo::setCustomConfigHandle( const QString& handle )
_configHandle = handle;
_authAttempts = 0; // allow a couple of tries again.
resetSSLUntrust();
MirallConfigFile cfg(_configHandle);
setNetworkAccessManager (cfg.getCredentials()->getQNAM());
}
bool ownCloudInfo::isConfigured()
@@ -116,28 +96,32 @@ bool ownCloudInfo::isConfigured()
QNetworkReply *ownCloudInfo::checkInstallation()
{
_redirectCount = 0;
MirallConfigFile cfgFile( _configHandle );
QUrl url ( cfgFile.ownCloudUrl( _connection ) + QLatin1String("status.php") );
/* No authentication required for this. */
return getRequest( QLatin1String("status.php"), false );
return getRequest(url);
}
QNetworkReply* ownCloudInfo::getWebDAVPath( const QString& path )
{
return getRequest( path, true );
_redirectCount = 0;
QUrl url ( webdavUrl( _connection ) + path );
QNetworkReply *reply = getRequest(url);
_directories[reply] = path;
return reply;
}
QNetworkReply* ownCloudInfo::getRequest( const QString& path, bool webdav )
QNetworkReply* ownCloudInfo::getRequest( const QUrl& url )
{
qDebug() << "Get Request to " << path;
qDebug() << "Get Request to " << url;
MirallConfigFile cfgFile( _configHandle );
QString url = cfgFile.ownCloudUrl( _connection, webdav ) + path;
QNetworkRequest request;
request.setUrl( QUrl( url ) );
request.setUrl( url );
setupHeaders( request, 0 );
QNetworkReply *reply = _manager->get( request );
connect( reply, SIGNAL(finished()), SLOT(slotReplyFinished()));
_directories[reply] = path;
if( !_configHandle.isEmpty() ) {
qDebug() << "Setting config handle " << _configHandle;
@@ -149,87 +133,13 @@ QNetworkReply* ownCloudInfo::getRequest( const QString& path, bool webdav )
return reply;
}
#if QT46_IMPL
QNetworkReply* ownCloudInfo::mkdirRequest( const QString& dir )
{
qDebug() << "OCInfo Making dir " << dir;
MirallConfigFile cfgFile( _configHandle );
QUrl url = QUrl( cfgFile.ownCloudUrl( _connection, true ) + dir );
QHttp::ConnectionMode conMode = QHttp::ConnectionModeHttp;
if (url.scheme() == "https")
conMode = QHttp::ConnectionModeHttps;
QHttp* qhttp = new QHttp(QString(url.encodedHost()), conMode, 0, this);
connect(qhttp, SIGNAL(requestStarted(int)), this,SLOT(qhttpRequestStarted(int)));
connect(qhttp, SIGNAL(requestFinished(int, bool)), this,SLOT(qhttpRequestFinished(int,bool)));
connect(qhttp, SIGNAL(responseHeaderReceived(QHttpResponseHeader)), this, SLOT(qhttpResponseHeaderReceived(QHttpResponseHeader)));
//connect(qhttp, SIGNAL(authenticationRequired(QString,quint16,QAuthenticator*)), this, SLOT(qhttpAuthenticationRequired(QString,quint16,QAuthenticator*)));
QHttpRequestHeader header("MKCOL", QString(url.encodedPath()), 1,1); /* header */
header.setValue("Host", QString(url.encodedHost()));
header.setValue("User-Agent", Utility::userAgentString());
header.setValue("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
header.setValue("Accept-Language", "it,de-de;q=0.8,it-it;q=0.6,en-us;q=0.4,en;q=0.2");
header.setValue("Connection", "keep-alive");
header.setContentType("application/x-www-form-urlencoded"); //important
header.setContentLength(0);
QString con = _configHandle;
if( con.isEmpty() ) con = DEFAULT_CONNECTION;
if( _credentials.contains(con)) {
oCICredentials creds = _credentials.value(con);
QString concatenated = creds.user + QLatin1Char(':') + creds.passwd;
const QString b(QLatin1String("Basic "));
QByteArray data = b.toLocal8Bit() + concatenated.toLocal8Bit().toBase64();
header.setValue("Authorization", data);
qhttp->setUser( creds.user, creds.passwd );
}
int david = qhttp->request(header,0,0);
//////////////// connect(davinfo, SIGNAL(dataSendProgress(int,int)), this, SLOT(SendStatus(int, int)));
/////////////////connect(davinfo, SIGNAL(done(bool)), this,SLOT(DavWake(bool)));
//connect(_http, SIGNAL(requestFinished(int, bool)), this,SLOT(qhttpRequestFinished(int,bool)));
///////////connect(davinfo, SIGNAL(responseHeaderReceived(constQHttpResponseHeader &)), this, SLOT(RegisterBackHeader(constQHttpResponseHeader &)));
return NULL;
}
void ownCloudInfo::qhttpResponseHeaderReceived(const QHttpResponseHeader& header)
{
qDebug() << "Resp:" << header.toString();
if (header.statusCode() == 201)
emit webdavColCreated( QNetworkReply::NoError );
else
qDebug() << "http request failed" << header.toString();
}
void ownCloudInfo::qhttpRequestStarted(int id)
{
qDebug() << "QHttp based request started " << id;
}
void ownCloudInfo::qhttpRequestFinished(int id, bool success )
{
qDebug() << "HIT!";
QHttp* qhttp = qobject_cast<QHttp*>(sender());
if( success ) {
qDebug() << "QHttp based request successful";
} else {
qDebug() << "QHttp based request failed: " << qhttp->errorString();
}
}
#else
QNetworkReply* ownCloudInfo::mkdirRequest( const QString& dir )
{
qDebug() << "OCInfo Making dir " << dir;
_authAttempts = 0;
MirallConfigFile cfgFile( _configHandle );
QNetworkRequest req;
req.setUrl( QUrl( cfgFile.ownCloudUrl( _connection, true ) + dir ) );
QNetworkReply *reply = davRequest(QLatin1String("MKCOL"), req, 0);
req.setUrl( QUrl( webdavUrl(_connection) + dir ) );
QNetworkReply *reply = davRequest("MKCOL", req, 0);
// remember the confighandle used for this request
if( ! _configHandle.isEmpty() )
@@ -246,6 +156,62 @@ QNetworkReply* ownCloudInfo::mkdirRequest( const QString& dir )
return reply;
}
QNetworkReply* ownCloudInfo::getQuotaRequest( const QString& dir )
{
QNetworkRequest req;
req.setUrl( QUrl( webdavUrl(_connection) + dir ) );
req.setRawHeader("Depth", "0");
QByteArray xml("<?xml version=\"1.0\" ?>\n"
"<d:propfind xmlns:d=\"DAV:\">\n"
" <d:prop>\n"
" <d:quota-available-bytes/>\n"
" <d:quota-used-bytes/>\n"
" <d:getetag/>"
" </d:prop>\n"
"</d:propfind>\n");
QBuffer *buf = new QBuffer;
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
QNetworkReply *reply = davRequest("PROPFIND", req, buf);
buf->setParent(reply);
if( reply->error() != QNetworkReply::NoError ) {
qDebug() << "getting quota: request network error: " << reply->errorString();
}
connect( reply, SIGNAL( finished()), SLOT(slotGetQuotaFinished()) );
connect( reply, SIGNAL( error(QNetworkReply::NetworkError)),
this, SLOT( slotError(QNetworkReply::NetworkError)));
return reply;
}
QNetworkReply* ownCloudInfo::getDirectoryListing( const QString& dir )
{
QNetworkRequest req;
req.setUrl( QUrl( webdavUrl(_connection) + dir ) );
req.setRawHeader("Depth", "1");
QByteArray xml("<?xml version=\"1.0\" ?>\n"
"<d:propfind xmlns:d=\"DAV:\">\n"
" <d:prop>\n"
" <d:resourcetype/>\n"
" </d:prop>\n"
"</d:propfind>\n");
QBuffer *buf = new QBuffer;
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
QNetworkReply *reply = davRequest("PROPFIND", req, buf);
buf->setParent(reply);
if( reply->error() != QNetworkReply::NoError ) {
qDebug() << "getting quota: request network error: " << reply->errorString();
}
connect( reply, SIGNAL( finished()), SLOT(slotGetDirectoryListingFinished()) );
connect( reply, SIGNAL( error(QNetworkReply::NetworkError)),
this, SLOT( slotError(QNetworkReply::NetworkError)));
return reply;
}
void ownCloudInfo::slotMkdirFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
@@ -263,47 +229,90 @@ void ownCloudInfo::slotMkdirFinished()
reply->deleteLater();
}
#endif
// FIXME: remove this later, once the new connection dialog has settled.
#if 0
void ownCloudInfo::slotAuthentication( QNetworkReply *reply, QAuthenticator *auth )
void ownCloudInfo::slotGetQuotaFinished()
{
if( !(auth && reply) ) return;
QString configHandle;
bool ok = false;
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
// an empty config handle is ok for the default config.
if( _configHandleMap.contains(reply) ) {
configHandle = _configHandleMap[reply];
qDebug() << "Auth: Have a custom config handle: " << configHandle;
}
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
// Parse DAV response
QXmlStreamReader reader(reply);
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
qDebug() << "Auth request to me and I am " << this;
_authAttempts++;
MirallConfigFile cfgFile( configHandle );
qDebug() << "Authenticating request for " << reply->url();
if( reply->url().toString().startsWith( cfgFile.ownCloudUrl( _connection, true )) ) {
qint64 quotaUsedBytes = 0;
qint64 quotaAvailableBytes = 0;
QString etag;
QString con = configHandle;
if( con.isEmpty() ) con = DEFAULT_CONNECTION;
if( _credentials.contains(con)) {
oCICredentials creds = _credentials.value(con);
auth->setUser( creds.user );
auth->setPassword( creds.passwd );
} else {
qDebug() << "Unable to get Credentials, not set!";
reply->close();
while (!reader.atEnd()) {
QXmlStreamReader::TokenType type = reader.readNext();
if (type == QXmlStreamReader::StartElement &&
reader.namespaceUri() == QLatin1String("DAV:")) {
QString name = reader.name().toString();
if (name == QLatin1String("quota-used-bytes")) {
quotaUsedBytes = reader.readElementText().toLongLong(&ok);
if (!ok) quotaUsedBytes = 0;
} else if (name == QLatin1String("quota-available-bytes")) {
quotaAvailableBytes = reader.readElementText().toLongLong(&ok);
if (!ok) quotaAvailableBytes = 0;
} else if (name == QLatin1String("getetag")) {
etag = reader.readElementText();
}
}
}
qint64 total = quotaUsedBytes + quotaAvailableBytes;
_lastQuotaTotalBytes = total;
_lastQuotaUsedBytes = quotaUsedBytes;
emit quotaUpdated(total, quotaUsedBytes);
_lastEtag = etag;
} else {
qDebug() << "WRN: attempt to authenticate to different url - attempt " <<_authAttempts;
}
if( _authAttempts > 1) {
qDebug() << "Too many attempts to authenticate. Stop request.";
reply->close();
_lastQuotaTotalBytes = 0;
_lastQuotaUsedBytes = 0;
}
reply->deleteLater();
}
void ownCloudInfo::slotGetDirectoryListingFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
// Parse DAV response
QXmlStreamReader reader(reply);
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
QStringList folders;
QString currentItem;
while (!reader.atEnd()) {
QXmlStreamReader::TokenType type = reader.readNext();
if (type == QXmlStreamReader::StartElement &&
reader.namespaceUri() == QLatin1String("DAV:")) {
QString name = reader.name().toString();
if (name == QLatin1String("href")) {
currentItem = reader.readElementText();
} else if (name == QLatin1String("collection") &&
!currentItem.isEmpty()) {
folders.append(QUrl::fromEncoded(currentItem.toLatin1()).path());
currentItem.clear();
}
}
}
emit directoryListingUpdated(folders);
}
reply->deleteLater();
}
QList<QNetworkCookie> ownCloudInfo::getLastAuthCookies()
{
QUrl url = QUrl( webdavUrl(_connection));
QList<QNetworkCookie> cookies = _manager->cookieJar()->cookiesForUrl(url);
return cookies;
}
#endif
QString ownCloudInfo::configHandle(QNetworkReply *reply)
{
@@ -320,20 +329,6 @@ QList<QSslCertificate> ownCloudInfo::certificateChain() const
return _certificateChain;
}
QUrl ownCloudInfo::redirectUrl(const QUrl& possibleRedirectUrl,
const QUrl& oldRedirectUrl) const {
QUrl redirectUrl;
/*
* Check if the URL is empty and
* that we aren't being fooled into a infinite redirect loop.
*/
if(!possibleRedirectUrl.isEmpty() &&
possibleRedirectUrl != oldRedirectUrl) {
redirectUrl = possibleRedirectUrl;
}
return redirectUrl;
}
//
// There have been problems with the finish-signal coming from the networkmanager.
// To avoid that, the reply-signals were connected and the data is taken from the
@@ -354,12 +349,17 @@ void ownCloudInfo::slotReplyFinished()
}
// Detect redirect url
QVariant possibleRedirUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
QUrl possibleRedirUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
/* We'll deduct if the redirection is valid in the redirectUrl function */
_urlRedirectedTo = redirectUrl( possibleRedirUrl.toUrl(),
_urlRedirectedTo );
if(!_urlRedirectedTo.isEmpty()) {
if (!possibleRedirUrl.isEmpty() && _redirectCount++ > 10) {
// Are we in a redirect loop
qDebug() << "Redirect loop while redirecting to" << possibleRedirUrl;
possibleRedirUrl.clear();
}
if(!possibleRedirUrl.isEmpty()) {
QString configHandle;
qDebug() << "Redirected to " << possibleRedirUrl;
@@ -372,24 +372,28 @@ void ownCloudInfo::slotReplyFinished()
}
QString path = _directories[reply];
if (path.isEmpty()) {
path = QLatin1String("status.php");
} else {
path.prepend( QLatin1String(WEBDAV_PATH) );
}
qDebug() << "This path was redirected: " << path;
MirallConfigFile cfgFile( configHandle );
QString newUrl = _urlRedirectedTo.toString();
if( newUrl.endsWith( path )) {
QString newUrl = possibleRedirUrl.toString();
if( !path.isEmpty() && newUrl.endsWith( path )) {
// cut off the trailing path
newUrl.chop( path.length() );
cfgFile.setOwnCloudUrl( _connection, newUrl );
qDebug() << "Update the config file url to " << newUrl;
getRequest( path, false ); // FIXME: Redirect for webdav!
reply->deleteLater();
return;
_urlRedirectedTo = newUrl;
qDebug() << "Updated url to" << newUrl;
getRequest( possibleRedirUrl );
} else {
qDebug() << "WRN: Path is not part of the redirect URL. NO redirect.";
}
reply->deleteLater();
_directories.remove(reply);
_configHandleMap.remove(reply);
return;
}
_urlRedirectedTo.clear();
// TODO: check if this is always the correct encoding
const QString version = QString::fromUtf8( reply->readAll() );
@@ -439,8 +443,8 @@ void ownCloudInfo::slotReplyFinished()
// get version out
edition = val;
} else if(key == QLatin1String("installed")) {
// Silently ignoring "installed = true" information
} else {
// Silently ignoring "installed = true" information
} else {
qDebug() << "Unknown info from ownCloud status.php: "<< key << "=" << val;
}
}
@@ -455,15 +459,13 @@ void ownCloudInfo::slotReplyFinished()
QString dir(QLatin1String("unknown"));
if( _directories.contains(reply) ) {
dir = _directories[reply];
_directories.remove(reply);
}
emit ownCloudDirExists( dir, reply );
}
if( _configHandleMap.contains(reply)) {
_configHandleMap.remove(reply);
}
reply->deleteLater();
_directories.remove(reply);
_configHandleMap.remove(reply);
}
void ownCloudInfo::resetSSLUntrust()
@@ -518,43 +520,11 @@ void ownCloudInfo::slotError( QNetworkReply::NetworkError err)
}
}
void ownCloudInfo::setCredentials( const QString& user, const QString& passwd,
const QString& configHandle )
{
QString con( configHandle );
if( configHandle.isEmpty() )
con = DEFAULT_CONNECTION;
if( _credentials.contains(con) ) {
qDebug() << "Overwriting credentials for connection " << con;
}
oCICredentials creds;
creds.user = user;
creds.passwd = passwd;
creds.connection = con;
_credentials[con] = creds;
}
// ============================================================================
void ownCloudInfo::setupHeaders( QNetworkRequest & req, quint64 size )
{
MirallConfigFile cfgFile(_configHandle );
QUrl url( cfgFile.ownCloudUrl( QString::null, false ) );
QUrl url( req.url() );
qDebug() << "Setting up host header: " << url.host();
req.setRawHeader( QByteArray("Host"), url.host().toUtf8() );
req.setRawHeader( QByteArray("User-Agent"), Utility::userAgentString());
QString con = _configHandle;
if( con.isEmpty() ) con = DEFAULT_CONNECTION;
if( _credentials.contains(con)) {
oCICredentials creds = _credentials.value(con);
QString concatenated = creds.user + QLatin1Char(':') + creds.passwd;
const QString b(QLatin1String("Basic "));
QByteArray data = b.toUtf8() + concatenated.toUtf8().toBase64();
req.setRawHeader( QByteArray("Authorization"), data );
}
if (size) {
req.setHeader( QNetworkRequest::ContentLengthHeader, size);
@@ -562,18 +532,92 @@ void ownCloudInfo::setupHeaders( QNetworkRequest & req, quint64 size )
}
}
#if QT46_IMPL
#else
QNetworkReply* ownCloudInfo::davRequest(const QString& reqVerb, QNetworkRequest& req, QByteArray *data)
QNetworkReply* ownCloudInfo::davRequest(const QByteArray& reqVerb, QNetworkRequest& req, QIODevice *data)
{
setupHeaders(req, quint64(data ? data->size() : 0));
if( data ) {
QBuffer iobuf( data );
return _manager->sendCustomRequest(req, reqVerb.toUtf8(), &iobuf );
} else {
return _manager->sendCustomRequest(req, reqVerb.toUtf8(), 0 );
}
}
#endif
return _manager->sendCustomRequest(req, reqVerb, data );
}
QString ownCloudInfo::webdavUrl(const QString &connection)
{
QString url;
if (!_urlRedirectedTo.isEmpty()) {
url = _urlRedirectedTo.toString();
} else {
MirallConfigFile cfgFile(_configHandle );
url = cfgFile.ownCloudUrl( connection );
}
url.append( QLatin1String( WEBDAV_PATH ) );
if (!url.endsWith('/')) url.append('/');
return url;
}
RequestEtagJob::RequestEtagJob(const QString& dir, QObject* parent)
: QObject(parent)
{
QNetworkRequest req;
req.setUrl( QUrl( ownCloudInfo::instance()->webdavUrl(ownCloudInfo::instance()->_connection) + dir ) );
if (dir.isEmpty() || dir == "/") {
/* For the root directory, we need to query the etags of all the sub directories
* because, at the time I am writing this comment (Owncloud 5.0.9), the etag of the
* root directory is not updated when the sub directories changes */
req.setRawHeader("Depth", "1");
} else {
req.setRawHeader("Depth", "0");
}
QByteArray xml("<?xml version=\"1.0\" ?>\n"
"<d:propfind xmlns:d=\"DAV:\">\n"
" <d:prop>\n"
" <d:getetag/>"
" </d:prop>\n"
"</d:propfind>\n");
QBuffer *buf = new QBuffer;
buf->setData(xml);
buf->open(QIODevice::ReadOnly);
_reply = ownCloudInfo::instance()->davRequest("PROPFIND", req, buf);
buf->setParent(_reply);
if( _reply->error() != QNetworkReply::NoError ) {
qDebug() << "getting etag: request network error: " << _reply->errorString();
}
connect( _reply, SIGNAL( finished()), SLOT(slotFinished()) );
connect( _reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotError()));
connect( _reply, SIGNAL(error(QNetworkReply::NetworkError)),
ownCloudInfo::instance(), SLOT(slotError(QNetworkReply::NetworkError)));
}
void RequestEtagJob::slotFinished()
{
if (_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) == 207) {
// Parse DAV response
QXmlStreamReader reader(_reply);
reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
QString etag;
while (!reader.atEnd()) {
QXmlStreamReader::TokenType type = reader.readNext();
if (type == QXmlStreamReader::StartElement &&
reader.namespaceUri() == QLatin1String("DAV:")) {
QString name = reader.name().toString();
if (name == QLatin1String("getetag")) {
etag += reader.readElementText();
}
}
}
emit etagRetreived(etag);
}
_reply->deleteLater();
deleteLater();
}
void RequestEtagJob::slotError()
{
qDebug() << "RequestEtagJob Error: " << _reply->errorString();
_reply->deleteLater();
deleteLater();
emit networkError();
}
} // ns Mirall

View File

@@ -18,21 +18,9 @@
#include <QObject>
#include <QtNetwork>
#if QT_VERSION >= 0x040700
#define QT46_IMPL 0
#else
#define QT46_IMPL 1
#endif
namespace Mirall
{
typedef struct {
QString user;
QString passwd;
QString connection;
} oCICredentials;
class ownCloudInfo : public QObject
{
Q_OBJECT
@@ -47,12 +35,6 @@ public:
*/
QNetworkReply* checkInstallation();
/**
* a general GET request to the ownCloud. If the second bool parameter is
* true, the WebDAV server is queried.
*/
QNetworkReply* getRequest( const QString&, bool );
/**
* convenience: GET request to the WebDAV server.
*/
@@ -87,6 +69,16 @@ public:
*/
QNetworkReply* mkdirRequest( const QString& );
/**
* Retrieve quota for a path. Provide a relative path.
*/
QNetworkReply* getQuotaRequest( const QString& );
/**
* provide collections in a directory via owncloud. Provide a relative path.
*/
QNetworkReply* getDirectoryListing( const QString& dir );
/**
* Use a custom ownCloud configuration file identified by handle
*/
@@ -104,11 +96,17 @@ public:
QList<QSslCertificate> certificateChain() const;
/**
* Store credentials for a given connection. Empty connection parameter
* means "default connection".
* returns the owncloud webdav url.
* It may be different from the one in the config if there was a HTTP redirection
* The returned URL is guaranteed to end in a forward slash ('/')
*/
void setCredentials( const QString&, const QString&,
const QString& configHandle = QString::null );
QString webdavUrl(const QString& connection = QString());
qint64 lastQuotaUsedBytes() const { return _lastQuotaUsedBytes; }
qint64 lastQuotaTotalBytes() const { return _lastQuotaTotalBytes; }
QString lastEtag() const { return _lastEtag; }
QList<QNetworkCookie> getLastAuthCookies();
signals:
// result signal with url- and version string.
@@ -119,34 +117,29 @@ signals:
void webdavColCreated( QNetworkReply::NetworkError );
void sslFailed( QNetworkReply *reply, QList<QSslError> errors );
void guiLog( const QString& title, const QString& content );
public slots:
void quotaUpdated( qint64 total, qint64 quotaUsedBytes );
void directoryListingUpdated(const QStringList &directories);
protected slots:
void slotReplyFinished( );
void slotError( QNetworkReply::NetworkError );
// void slotAuthentication( QNetworkReply*, QAuthenticator *);
#if QT46_IMPL
void qhttpRequestFinished(int id, bool success );
void qhttpRequestStarted(int id);
void qhttpResponseHeaderReceived(const QHttpResponseHeader& header);
// void qhttpAuthenticationRequired(const QString& hostname, quint16 port ,QAuthenticator* authenticator);
#else
void slotMkdirFinished();
#endif
void slotGetQuotaFinished();
void slotGetDirectoryListingFinished();
private:
explicit ownCloudInfo();
QUrl redirectUrl(const QUrl&, const QUrl& ) const;
/**
* a general GET request to the ownCloud WebDAV.
*/
QNetworkReply* getRequest( const QUrl &url);
QNetworkReply* davRequest(const QByteArray&, QNetworkRequest&, QIODevice* );
~ownCloudInfo();
void setupHeaders(QNetworkRequest &req, quint64 size );
#if QT46_IMPL
#else
QNetworkReply* davRequest(const QString&, QNetworkRequest&, QByteArray* );
#endif
static ownCloudInfo *_instance;
@@ -159,10 +152,35 @@ private:
QList<QSslCertificate> _certificateChain;
bool _certsUntrusted;
int _authAttempts;
QMap<QString, oCICredentials> _credentials;
QMutex _certChainMutex;
int _redirectCount;
qint64 _lastQuotaUsedBytes;
qint64 _lastQuotaTotalBytes;
QString _lastEtag;
friend class RequestEtagJob;
};
class RequestEtagJob : public QObject {
Q_OBJECT
QNetworkReply *_reply;
public:
explicit RequestEtagJob(const QString &dir , QObject* parent = 0);
private slots:
void slotFinished();
void slotError();
signals:
void etagRetreived(const QString &etag);
void networkError();
};
} // ns Mirall
#endif // OWNCLOUDINFO_H

View File

@@ -1,5 +1,6 @@
/*
* Copyright (C) by Klaas Freitag <freitag@kde.org>
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
* 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
@@ -12,39 +13,44 @@
* for more details.
*/
#include <QAbstractButton>
#include <QtCore>
#include <QProcess>
#include <QMessageBox>
#include <QDesktopServices>
#include "wizard/owncloudwizardcommon.h"
#include "wizard/owncloudwizard.h"
#include "mirall/owncloudsetupwizard.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/owncloudinfo.h"
#include "mirall/folderman.h"
#include "mirall/credentialstore.h"
#include "mirall/utility.h"
#include "mirall/mirallaccessmanager.h"
#include "creds/abstractcredentials.h"
#include "creds/dummycredentials.h"
namespace Mirall {
class Theme;
OwncloudSetupWizard::OwncloudSetupWizard( FolderMan *folderMan, Theme *theme, QObject *parent ) :
OwncloudSetupWizard::OwncloudSetupWizard(QObject* parent) :
QObject( parent ),
_mkdirRequestReply(0),
_checkInstallationRequest(0),
_folderMan(folderMan)
_ocWizard(new OwncloudWizard),
_mkdirRequestReply(),
_checkInstallationRequest(),
_checkRemoteFolderRequest(),
_configHandle(),
_remoteFolder()
{
_ocWizard = new OwncloudWizard();
connect( _ocWizard, SIGNAL(determineAuthType(const QString&)),
this, SLOT(slotDetermineAuthType(const QString&)));
connect( _ocWizard, SIGNAL(connectToOCUrl( const QString& ) ),
this, SLOT(slotConnectToOCUrl( const QString& )));
connect( _ocWizard, SIGNAL(createLocalAndRemoteFolders(QString, QString)),
this, SLOT(slotCreateLocalAndRemoteFolders(QString, QString)));
connect( _ocWizard, SIGNAL(finished(int)),this,SLOT(slotAssistantFinished(int)));
connect( _ocWizard, SIGNAL(clearPendingRequests()),
this, SLOT(slotClearPendingRequests()));
_ocWizard->setWindowTitle( tr("%1 Connection Wizard").arg( theme->appNameGUI() ) );
}
OwncloudSetupWizard::~OwncloudSetupWizard()
@@ -52,8 +58,12 @@ OwncloudSetupWizard::~OwncloudSetupWizard()
_ocWizard->deleteLater();
}
OwncloudWizard *OwncloudSetupWizard::wizard() {
return _ocWizard;
void OwncloudSetupWizard::runWizard(QObject* obj, const char* amember, QWidget *parent)
{
OwncloudSetupWizard *wiz = new OwncloudSetupWizard(parent);
connect( wiz, SIGNAL(ownCloudWizardDone(int)), obj, amember);
FolderMan::instance()->setSyncEnabled(false);
wiz->startWizard();
}
void OwncloudSetupWizard::startWizard()
@@ -62,14 +72,10 @@ void OwncloudSetupWizard::startWizard()
MirallConfigFile cfgFile;
// Fill the entry fields with existing values.
QString url = cfgFile.ownCloudUrl();
QString user = cfgFile.ownCloudUser();
bool configExists = !( url.isEmpty() || user.isEmpty() );
//QString user = cfgFile.ownCloudUser();
bool configExists = !( url.isEmpty()/* || user.isEmpty()*/ );
_ocWizard->setConfigExists( configExists );
if( !user.isEmpty() ) {
_ocWizard->setOCUser( user );
}
if( !url.isEmpty() ) {
_ocWizard->setOCUrl( url );
}
@@ -80,133 +86,30 @@ void OwncloudSetupWizard::startWizard()
QString localFolder = Theme::instance()->defaultClientFolder();
// if its a relative path, prepend with users home dir, otherwise use as absolute path
if( !localFolder.startsWith(QLatin1Char('/')) ) {
localFolder = QDir::homePath() + QDir::separator() + Theme::instance()->defaultClientFolder();
if( !QDir(localFolder).isAbsolute() ) {
localFolder = QDir::homePath() + QDir::separator() + localFolder;
}
_ocWizard->setProperty("localFolder", localFolder);
_ocWizard->setRemoteFolder(_remoteFolder);
_ocWizard->setStartId(OwncloudWizard::Page_oCSetup);
_ocWizard->setStartId(WizardCommon::Page_ServerSetup);
_ocWizard->restart();
// settings re-initialized in initPage must be set here after restart
_ocWizard->setMultipleFoldersExist(_folderMan->map().count() > 1);
_ocWizard->setMultipleFoldersExist(FolderMan::instance()->map().count() > 1);
_ocWizard->show();
_ocWizard->open();
_ocWizard->raise();
}
// Method executed when the user ends the wizard, either with 'accept' or 'reject'.
// accept the custom config to be the main one if Accepted.
void OwncloudSetupWizard::slotAssistantFinished( int result )
{
MirallConfigFile cfg( _configHandle );
if( result == QDialog::Rejected ) {
// the old config remains valid. Remove the temporary one.
cfg.cleanupCustomConfig();
qDebug() << "Rejected the new config, use the old!";
} else if( result == QDialog::Accepted ) {
qDebug() << "Config Changes were accepted!";
// go through all folders and remove the journals if the server changed.
MirallConfigFile prevCfg;
QUrl prevUrl( prevCfg.ownCloudUrl() );
QUrl newUrl( cfg.ownCloudUrl() );
bool urlHasChanged = (prevUrl.host() != newUrl.host() || prevUrl.path() != newUrl.path());
// if the user changed, its also a changed url.
if( prevCfg.ownCloudUser() != cfg.ownCloudUser() ) {
urlHasChanged = true;
qDebug() << "The User has changed, same as url change.";
}
const QString localFolder = _ocWizard->localFolder();
bool acceptCfg = true;
if( urlHasChanged ) {
_folderMan->unloadAllFolders();
bool startFromScratch = _ocWizard->field( "OCSyncFromScratch" ).toBool();
if( startFromScratch ) {
// first try to rename (backup) the current local dir.
bool renameOk = false;
while( !renameOk ) {
renameOk = _folderMan->startFromScratch(localFolder);
if( ! renameOk ) {
QMessageBox::StandardButton but;
but = QMessageBox::question( 0, tr("Folder rename failed"),
tr("Can't remove and back up the folder because the folder or a file in it is open in another program."
"Please close the folder or file and hit retry or cancel the setup."), QMessageBox::Retry | QMessageBox::Abort, QMessageBox::Retry);
if( but == QMessageBox::Abort ) {
renameOk = true;
acceptCfg = false;
}
}
}
}
}
// save the user credentials and afterwards clear the cred store.
if( acceptCfg ) {
cfg.acceptCustomConfig();
}
// Now write the resulting folder definition if folder names are set.
if( acceptCfg && urlHasChanged ) {
_folderMan->removeAllFolderDefinitions();
_folderMan->addFolderDefinition( QLatin1String("owncloud"), Theme::instance()->appName(),
localFolder, _remoteFolder, false );
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
} else {
// url is unchanged. Only the password was changed.
if( acceptCfg ) {
qDebug() << "Only password was changed, no changes to folder configuration.";
} else {
qDebug() << "User interrupted change of configuration.";
}
}
}
// clear the custom config handle
_configHandle.clear();
ownCloudInfo::instance()->setCustomConfigHandle( QString::null );
// notify others.
emit ownCloudWizardDone( result );
}
void OwncloudSetupWizard::slotConnectToOCUrl( const QString& url )
{
qDebug() << "Connect to url: " << url;
_ocWizard->setField(QLatin1String("OCUrl"), url );
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2...")
.arg( Theme::instance()->appNameGUI() ).arg(url) );
testOwnCloudConnect();
}
void OwncloudSetupWizard::slotClearPendingRequests()
{
qDebug() << "Pending request: " << _mkdirRequestReply;
if( _mkdirRequestReply && _mkdirRequestReply->isRunning() ) {
qDebug() << "ABORTing pending mkdir request.";
_mkdirRequestReply->abort();
}
if( _checkInstallationRequest && _checkInstallationRequest->isRunning() ) {
qDebug() << "ABORTing pending check installation request.";
_checkInstallationRequest->abort();
}
if( _checkRemoteFolderRequest && _checkRemoteFolderRequest->isRunning() ) {
qDebug() << "ABORTing pending remote folder check request.";
_checkRemoteFolderRequest->abort();
}
}
void OwncloudSetupWizard::testOwnCloudConnect()
void OwncloudSetupWizard::slotDetermineAuthType(const QString& serverUrl)
{
QString url(serverUrl);
qDebug() << "Connect to url: " << url;
_ocWizard->setField(QLatin1String("OCUrl"), url );
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2 to determine authentication type...")
.arg( Theme::instance()->appNameGUI() ).arg(url) );
// write a temporary config.
QDateTime now = QDateTime::currentDateTime();
@@ -219,8 +122,7 @@ void OwncloudSetupWizard::testOwnCloudConnect()
_configHandle = now.toString(QLatin1String("MMddyyhhmmss"));
MirallConfigFile cfgFile( _configHandle );
QString url = _ocWizard->field(QLatin1String("OCUrl")).toString();
MirallConfigFile cfgFile( _configHandle, true );
if( url.isEmpty() ) return;
if( !( url.startsWith(QLatin1String("https://")) || url.startsWith(QLatin1String("http://"))) ) {
qDebug() << "url does not start with a valid protocol, assuming https.";
@@ -230,50 +132,87 @@ void OwncloudSetupWizard::testOwnCloudConnect()
}
cfgFile.writeOwncloudConfig( Theme::instance()->appName(),
url,
_ocWizard->field(QLatin1String("OCUser")).toString(),
_ocWizard->field(QLatin1String("OCPasswd")).toString() );
new DummyCredentials);
// If there is already a config, take its proxy config.
if( ownCloudInfo::instance()->isConfigured() ) {
MirallConfigFile prevCfg;
cfgFile.setProxyType( prevCfg.proxyType(), prevCfg.proxyHostName(), prevCfg.proxyPort(),
prevCfg.proxyNeedsAuth(), prevCfg.proxyUser(), prevCfg.proxyPassword() );
}
// now start ownCloudInfo to check the connection.
ownCloudInfo* info = ownCloudInfo::instance();
info->setCustomConfigHandle( _configHandle );
if( info->isConfigured() ) {
// reset the SSL Untrust flag to let the SSL dialog appear again.
info->resetSSLUntrust();
connect(info, SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
SLOT(slotOwnCloudFoundAuth(QString,QString,QString,QString)));
connect(info, SIGNAL(noOwncloudFound(QNetworkReply*)),
SLOT(slotNoOwnCloudFound(QNetworkReply*)));
SLOT(slotNoOwnCloudFoundAuth(QNetworkReply*)));
_checkInstallationRequest = info->checkInstallation();
} else {
qDebug() << " ownCloud seems not to be configured, can not start test connect.";
}
}
void OwncloudSetupWizard::slotOwnCloudFound( const QString& url, const QString& infoString, const QString& version, const QString& )
void OwncloudSetupWizard::slotOwnCloudFoundAuth( const QString& url, const QString& infoString, const QString& version, const QString& )
{
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
this, SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
this, SLOT(slotOwnCloudFoundAuth(QString,QString,QString,QString)));
disconnect(ownCloudInfo::instance(), SIGNAL(noOwncloudFound(QNetworkReply*)),
this, SLOT(slotNoOwnCloudFound(QNetworkReply*)));
this, SLOT(slotNoOwnCloudFoundAuth(QNetworkReply*)));
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\">Successfully connected to %1: %2 version %3 (%4)</font><br/><br/>")
.arg( url ).arg(Theme::instance()->appNameGUI()).arg(infoString).arg(version));
// enable the finish button.
_ocWizard->button( QWizard::FinishButton )->setEnabled( true );
MirallAccessManager* nm = new MirallAccessManager(this);
// TODO: We should get this path from owncloud info.
QNetworkReply* reply = nm->get (QNetworkRequest (url + "/remote.php/webdav/"));
// start the local folder creation
setupLocalSyncFolder();
connect (reply, SIGNAL(finished()),
this, SLOT(slotAuthCheckReplyFinished()));
nm->setProperty ("mirallRedirs", QVariant (0));
}
void OwncloudSetupWizard::slotNoOwnCloudFound( QNetworkReply *err )
void OwncloudSetupWizard::slotAuthCheckReplyFinished()
{
QNetworkReply* reply = qobject_cast< QNetworkReply* > (sender ());
QUrl redirection = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
QNetworkAccessManager* nm = reply->manager ();
const int redirCount = nm->property ("mirallRedirs").toInt();
if (redirCount > 10) {
redirection.clear ();
}
disconnect (reply, SIGNAL(finished()),
this, SLOT(slotAuthCheckReplyFinished()));
if ((reply->error () == QNetworkReply::AuthenticationRequiredError) || redirection.isEmpty()) {
reply->deleteLater();
nm->deleteLater();
_ocWizard->setAuthType (WizardCommon::HttpCreds);
} else if (redirection.toString().endsWith ("/remote.php/webdav/")) {
QNetworkReply* newReply = nm->get (QNetworkRequest(redirection));
connect (newReply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotAuthCheckReplyError(QNetworkReply::NetworkError)));
connect (newReply, SIGNAL(finished()),
this, SLOT(slotAuthCheckReplyFinished(QNetworkReply::NetworkError)));
reply->deleteLater();
nm->setProperty ("mirallRedirs", QVariant(redirCount + 1));
} else {
QRegExp shibbolethyWords ("SAML|wayf");
shibbolethyWords.setCaseSensitivity (Qt::CaseInsensitive);
if (redirection.toString ().contains (shibbolethyWords)) {
_ocWizard->setAuthType(WizardCommon::Shibboleth);
} else {
// TODO: Send an error.
// eh?
_ocWizard->setAuthType (WizardCommon::HttpCreds);
}
reply->deleteLater();
nm->deleteLater();
}
}
void OwncloudSetupWizard::slotNoOwnCloudFoundAuth( QNetworkReply *err )
{
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudInfoFound(QString,QString,QString,QString)),
this, SLOT(slotOwnCloudFound(QString,QString,QString,QString)));
@@ -286,19 +225,81 @@ void OwncloudSetupWizard::slotNoOwnCloudFound( QNetworkReply *err )
// remove the config file again
MirallConfigFile cfgFile( _configHandle );
cfgFile.cleanupCustomConfig();
finalizeSetup( false );
}
void OwncloudSetupWizard::setupLocalSyncFolder()
void OwncloudSetupWizard::slotConnectToOCUrl( const QString& url )
{
if( ! _folderMan ) return;
qDebug() << "Connect to url: " << url;
_ocWizard->setField(QLatin1String("OCUrl"), url );
_ocWizard->appendToConfigurationLog(tr("Trying to connect to %1 at %2...")
.arg( Theme::instance()->appNameGUI() ).arg(url) );
testOwnCloudConnect();
}
const QString localFolder = _ocWizard->property("localFolder").toString();
void OwncloudSetupWizard::testOwnCloudConnect()
{
// write a temporary config.
QDateTime now = QDateTime::currentDateTime();
if( _configHandle.isEmpty() ) {
_configHandle = now.toString(QLatin1String("MMddyyhhmmss"));
}
MirallConfigFile cfgFile( _configHandle, true );
QString url = _ocWizard->field(QLatin1String("OCUrl")).toString();
if( url.isEmpty() ) return;
if( !( url.startsWith(QLatin1String("https://")) || url.startsWith(QLatin1String("http://"))) ) {
qDebug() << "url does not start with a valid protocol, assuming https.";
url.prepend(QLatin1String("https://"));
// FIXME: give a hint about the auto completion
_ocWizard->setOCUrl(url);
}
cfgFile.writeOwncloudConfig( Theme::instance()->appName(),
url,
_ocWizard->getCredentials());
ownCloudInfo* info(ownCloudInfo::instance());
info->setCustomConfigHandle( _configHandle );
// If there is already a config, take its proxy config.
if( info->isConfigured() ) {
MirallConfigFile prevCfg;
cfgFile.setProxyType( prevCfg.proxyType(), prevCfg.proxyHostName(), prevCfg.proxyPort(),
prevCfg.proxyNeedsAuth(), prevCfg.proxyUser(), prevCfg.proxyPassword() );
}
connect( info,SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
this,SLOT(slotConnectionCheck(QString,QNetworkReply*)));
qDebug() << "# checking for authentication settings.";
_checkRemoteFolderRequest = info->getWebDAVPath(_remoteFolder ); // this call needs to be authenticated.
// continue in slotConnectionCheck
}
void OwncloudSetupWizard::slotConnectionCheck(const QString&, QNetworkReply* reply)
{
// disconnect from ownCloud Info signals
disconnect(ownCloudInfo::instance(), SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
this, SLOT(slotConnectionCheck(QString,QNetworkReply*)));
switch (reply->error()) {
case QNetworkReply::NoError:
case QNetworkReply::ContentNotFoundError:
_ocWizard->successfulStep();
break;
default:
_ocWizard->displayError(tr("Error: Wrong credentials."));
break;
}
}
void OwncloudSetupWizard::slotCreateLocalAndRemoteFolders(const QString& localFolder, const QString& remoteFolder)
{
qDebug() << "Setup local sync folder for new oC connection " << localFolder;
QDir fi( localFolder );
const QDir fi( localFolder );
// FIXME: Show problems with local folder properly.
bool localFolderOk = true;
if( fi.exists() ) {
// there is an existing local folder. If its non empty, it can only be synced if the
// ownCloud is newly created.
@@ -319,18 +320,19 @@ void OwncloudSetupWizard::setupLocalSyncFolder()
}
if( localFolderOk ) {
checkRemoteFolder();
checkRemoteFolder(remoteFolder);
}
}
void OwncloudSetupWizard::checkRemoteFolder()
void OwncloudSetupWizard::checkRemoteFolder(const QString& remoteFolder)
{
connect( ownCloudInfo::instance(),SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
ownCloudInfo* info(ownCloudInfo::instance());
connect( info,SIGNAL(ownCloudDirExists(QString,QNetworkReply*)),
this,SLOT(slotAuthCheckReply(QString,QNetworkReply*)));
qDebug() << "# checking for authentication settings.";
ownCloudInfo::instance()->setCustomConfigHandle(_configHandle);
_checkRemoteFolderRequest = ownCloudInfo::instance()->getRequest(_remoteFolder, true ); // this call needs to be authenticated.
qDebug() << "# checking for existence of remote folder.";
info->setCustomConfigHandle(_configHandle);
_checkRemoteFolderRequest = info->getWebDAVPath(remoteFolder); // this call needs to be authenticated.
// continue in slotAuthCheckReply
}
@@ -346,22 +348,12 @@ void OwncloudSetupWizard::slotAuthCheckReply( const QString&, QNetworkReply *rep
if( errId == QNetworkReply::NoError ) {
qDebug() << "******** Remote folder found, all cool!";
} else if( errId == QNetworkReply::AuthenticationRequiredError ) { // returned if the user is wrong.
qDebug() << "******** Password is wrong!";
error = tr("The given credentials do not authenticate.");
ok = false;
} else if( errId == QNetworkReply::OperationCanceledError ) {
// the username was wrong and ownCloudInfo was closing the request after a couple of auth tries.
qDebug() << "******** Username or password is wrong!";
error = tr("Username or password is wrong!");
ok = false;
} else if( errId == QNetworkReply::ContentNotFoundError ) {
// FIXME try to create the remote folder!
if( !createRemoteFolder() ) {
if( createRemoteFolder() ) {
return; // Finish here, the mkdir request will go on.
} else {
error = tr("The remote folder could not be accessed!");
ok = false;
} else {
return; // Finish here, the mkdir request will go on.
}
} else {
error = tr("Error: %1").arg(reply->errorString());
@@ -370,8 +362,6 @@ void OwncloudSetupWizard::slotAuthCheckReply( const QString&, QNetworkReply *rep
if( !ok ) {
_ocWizard->displayError(error);
} else {
_ocWizard->setRemoteFolder( _remoteFolder );
}
finalizeSetup( ok );
@@ -382,10 +372,11 @@ bool OwncloudSetupWizard::createRemoteFolder()
if( _remoteFolder.isEmpty() ) return false;
_ocWizard->appendToConfigurationLog( tr("creating folder on ownCloud: %1" ).arg( _remoteFolder ));
connect(ownCloudInfo::instance(), SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
ownCloudInfo* info(ownCloudInfo::instance());
connect(info, SIGNAL(webdavColCreated(QNetworkReply::NetworkError)),
this, SLOT(slotCreateRemoteFolderFinished(QNetworkReply::NetworkError)));
_mkdirRequestReply = ownCloudInfo::instance()->mkdirRequest( _remoteFolder );
_mkdirRequestReply = info->mkdirRequest( _remoteFolder );
return (_mkdirRequestReply != NULL);
}
@@ -440,13 +431,118 @@ void OwncloudSetupWizard::finalizeSetup( bool success )
+ tr("Successfully connected to %1!")
.arg(Theme::instance()->appNameGUI())
+ QLatin1String("</b></font></p>"));
_ocWizard->successfulStep();
} else {
_ocWizard->appendToConfigurationLog(QLatin1String("<p><font color=\"red\">")
+ tr("Connection to %1 could not be established. Please check again.")
.arg(Theme::instance()->appNameGUI())
+ QLatin1String("</font></p>"));
}
_ocWizard->successfullyConnected(success);
}
// Method executed when the user ends the wizard, either with 'accept' or 'reject'.
// accept the custom config to be the main one if Accepted.
void OwncloudSetupWizard::slotAssistantFinished( int result )
{
MirallConfigFile cfg( _configHandle );
FolderMan *folderMan = FolderMan::instance();
if( result == QDialog::Rejected ) {
// the old config remains valid. Remove the temporary one.
cfg.cleanupCustomConfig();
qDebug() << "Rejected the new config, use the old!";
} else if( result == QDialog::Accepted ) {
AbstractCredentials* credentials(_ocWizard->getCredentials());
qDebug() << "Config Changes were accepted!";
// go through all folders and remove the journals if the server changed.
MirallConfigFile prevCfg;
QUrl prevUrl( prevCfg.ownCloudUrl() );
QUrl newUrl( cfg.ownCloudUrl() );
AbstractCredentials* oldCredentials(prevCfg.getCredentials());
bool urlHasChanged = (prevUrl.host() != newUrl.host() ||
prevUrl.port() != newUrl.port() ||
prevUrl.path() != newUrl.path());
// if the user changed, its also a changed url.
if(credentials->changed(oldCredentials)) {
urlHasChanged = true;
qDebug() << "The User has changed, same as url change.";
}
const QString localFolder = _ocWizard->localFolder();
bool acceptCfg = true;
if( urlHasChanged ) {
// first terminate sync jobs.
folderMan->terminateSyncProcess();
bool startFromScratch = _ocWizard->field( "OCSyncFromScratch" ).toBool();
if( startFromScratch ) {
// first try to rename (backup) the current local dir.
bool renameOk = false;
while( !renameOk ) {
renameOk = folderMan->startFromScratch(localFolder);
if( ! renameOk ) {
QMessageBox::StandardButton but;
but = QMessageBox::question( 0, tr("Folder rename failed"),
tr("Can't remove and back up the folder because the folder or a file in it is open in another program."
"Please close the folder or file and hit retry or cancel the setup."), QMessageBox::Retry | QMessageBox::Abort, QMessageBox::Retry);
if( but == QMessageBox::Abort ) {
renameOk = true;
acceptCfg = false;
}
}
}
}
}
// Now write the resulting folder definition if folder names are set.
if( acceptCfg && urlHasChanged ) {
folderMan->removeAllFolderDefinitions();
folderMan->addFolderDefinition(Theme::instance()->appName(),
localFolder, _remoteFolder );
_ocWizard->appendToConfigurationLog(tr("<font color=\"green\"><b>Local sync folder %1 successfully created!</b></font>").arg(localFolder));
} else {
// url is unchanged. Only the password was changed.
if( acceptCfg ) {
qDebug() << "Only password was changed, no changes to folder configuration.";
} else {
qDebug() << "User interrupted change of configuration.";
}
}
// save the user credentials and afterwards clear the cred store.
if( acceptCfg ) {
cfg.acceptCustomConfig();
}
}
// clear the custom config handle
_configHandle.clear();
ownCloudInfo::instance()->setCustomConfigHandle( QString::null );
// notify others.
emit ownCloudWizardDone( result );
}
void OwncloudSetupWizard::slotClearPendingRequests()
{
qDebug() << "Pending request: " << _mkdirRequestReply;
if( _mkdirRequestReply && _mkdirRequestReply->isRunning() ) {
qDebug() << "ABORTing pending mkdir request.";
_mkdirRequestReply->abort();
}
if( _checkInstallationRequest && _checkInstallationRequest->isRunning() ) {
qDebug() << "ABORTing pending check installation request.";
_checkInstallationRequest->abort();
}
if( _checkRemoteFolderRequest && _checkRemoteFolderRequest->isRunning() ) {
qDebug() << "ABORTing pending remote folder check request.";
_checkRemoteFolderRequest->abort();
}
}
} // ns Mirall

View File

@@ -21,81 +21,54 @@
#include <QNetworkReply>
#include <QPointer>
#include "mirall/owncloudwizard.h"
#include "mirall/theme.h"
namespace Mirall {
class SiteCopyFolder;
class SyncResult;
class ownCloudInfo;
class FolderMan;
class OwncloudWizard;
class OwncloudSetupWizard : public QObject
{
Q_OBJECT
public:
explicit OwncloudSetupWizard( FolderMan *folderMan = 0, Theme *theme = 0, QObject *parent = 0 );
~OwncloudSetupWizard();
/**
* @intro wether or not to show the intro wizard page
*/
void startWizard();
void installServer();
bool isBusy();
void writeOwncloudConfig();
/**
* returns the configured owncloud url if its already configured, otherwise an empty
* string.
*/
void setupLocalSyncFolder();
OwncloudWizard *wizard();
/** Run the wizard */
static void runWizard(QObject *obj, const char* amember, QWidget *parent = 0 );
signals:
// issued if the oC Setup process (owncloud-admin) is finished.
void ownCloudSetupFinished( bool );
// overall dialog close signal.
void ownCloudWizardDone( int );
public slots:
protected slots:
// wizard dialog signals
void slotConnectToOCUrl( const QString& );
void ownCloudWizardDone( int );
private slots:
void slotOwnCloudFound( const QString&, const QString&, const QString&, const QString& );
void slotNoOwnCloudFound( QNetworkReply* );
void slotCreateRemoteFolderFinished( QNetworkReply::NetworkError );
void slotDetermineAuthType(const QString&);
void slotOwnCloudFoundAuth(const QString&, const QString&, const QString&, const QString&);
void slotAuthCheckReplyFinished();
void slotNoOwnCloudFoundAuth(QNetworkReply*);
void slotConnectToOCUrl(const QString&);
void slotConnectionCheck(const QString&, QNetworkReply*);
void slotCreateLocalAndRemoteFolders(const QString&, const QString&);
void slotAuthCheckReply(const QString&, QNetworkReply*);
void slotCreateRemoteFolderFinished(QNetworkReply::NetworkError);
void slotAssistantFinished( int );
void slotClearPendingRequests();
void slotAuthCheckReply( const QString&, QNetworkReply * );
private:
bool createRemoteFolder();
void checkRemoteFolder();
private:
explicit OwncloudSetupWizard(QObject *parent = 0 );
~OwncloudSetupWizard();
void startWizard();
void testOwnCloudConnect();
void checkRemoteFolder(const QString& remoteFolder);
bool createRemoteFolder();
void finalizeSetup( bool );
/* Start a request to the newly installed ownCloud to check the connection */
void testOwnCloudConnect();
OwncloudWizard *_ocWizard;
OwncloudWizard* _ocWizard;
QPointer<QNetworkReply> _mkdirRequestReply;
QPointer<QNetworkReply> _checkInstallationRequest;
QPointer<QNetworkReply> _checkRemoteFolderRequest;
FolderMan *_folderMan;
QString _configHandle;
QString _remoteFolder;
QString _configHandle;
QString _remoteFolder;
};
}

View File

@@ -52,32 +52,28 @@ QString ownCloudTheme::about() const
QString devString;
#ifdef GIT_SHA1
const QString githubPrefix(QLatin1String(
" https://github.com/owncloud/mirall/commit/"));
"https://github.com/owncloud/mirall/commit/"));
const QString gitSha1(QLatin1String(GIT_SHA1));
devString = QCoreApplication::translate("ownCloudTheme::about()",
"<p><small>Built from Git revision <a href=\"%1\">%2</a>"
" on %3, %4<br>using OCsync %5 and Qt %6.</small><p>")
" on %3, %4 using OCsync %5 and Qt %6.</small><p>")
.arg(githubPrefix+gitSha1).arg(gitSha1.left(6))
.arg(__DATE__).arg(__TIME__)
.arg(MIRALL_STRINGIFY(LIBCSYNC_VERSION))
.arg(QT_VERSION_STR);
#endif
return QCoreApplication::translate("ownCloudTheme::about()",
"<p><b>%1 Client Version %2</b></p>"
"<p><b>Authors</b>"
"<br><a href=\"mailto:freitag@owncloud.com\">"
"Klaas Freitag</a>, ownCloud, Inc."
"<br><a href=\"mailto:danimo@owncloud.com\">"
"Daniel Molkentin</a>, ownCloud, Inc."
"<br><br>Based on Mirall by Duncan Mac-Vicar P.</p>"
"<p>For more information visit <a href=\"%3\">%4</a>.</p>"
"<p>Version %2. "
"For more information visit <a href=\"%3\">%4</a></p>"
"<p><small>By Klaas Freitag, Daniel Molkentin, Jan-Christoph Borchardt, ownCloud Inc.<br>"
"Based on Mirall by Duncan Mac-Vicar P.</small></p>"
"%7"
)
.arg(appName())
.arg(MIRALL_STRINGIFY(MIRALL_VERSION))
.arg("http://" MIRALL_STRINGIFY(APPLICATION_DOMAIN))
.arg(MIRALL_STRINGIFY(APPLICATION_DOMAIN))
.arg(devString);
}
QIcon ownCloudTheme::trayFolderIcon( const QString& ) const
@@ -109,6 +105,11 @@ QVariant ownCloudTheme::customMedia(Theme::CustomMediaType type)
}
}
QString ownCloudTheme::helpUrl() const
{
return QString::fromLatin1("http://doc.owncloud.org/desktop/%1.%2/").arg(MIRALL_VERSION_MAJOR).arg(MIRALL_VERSION_MINOR);
}
QColor ownCloudTheme::wizardHeaderBackgroundColor() const
{
return QColor("#1d2d42");
@@ -119,5 +120,12 @@ QColor ownCloudTheme::wizardHeaderTitleColor() const
return QColor("#ffffff");
}
QPixmap ownCloudTheme::wizardHeaderLogo() const
{
return QPixmap(":/mirall/theme/colored/wizard_logo.png");
}
}

View File

@@ -21,6 +21,7 @@ namespace Mirall {
class ownCloudTheme : public Theme
{
Q_OBJECT
public:
ownCloudTheme();
@@ -35,10 +36,11 @@ public:
QIcon applicationIcon() const;
QVariant customMedia(CustomMediaType type);
QString helpUrl() const;
QColor wizardHeaderBackgroundColor() const;
QColor wizardHeaderTitleColor() const;
QPixmap wizardHeaderLogo() const;
private:

View File

@@ -1,626 +0,0 @@
/*
* Copyright (C) by Duncan Mac-Vicar P. <duncan@kde.org>
* 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/owncloudwizard.h"
#include "mirall/mirallconfigfile.h"
#include "mirall/theme.h"
#include "QProgressIndicator.h"
#include <QtCore>
#include <QtGui>
#include <stdlib.h>
namespace Mirall
{
void setupCustomMedia( QVariant variant, QLabel *label )
{
if( !label ) return;
QPixmap pix = variant.value<QPixmap>();
if( !pix.isNull() ) {
label->setPixmap(pix);
label->setAlignment( Qt::AlignTop | Qt::AlignRight );
label->setVisible(true);
} else {
QString str = variant.toString();
if( !str.isEmpty() ) {
label->setText( str );
label->setTextFormat( Qt::RichText );
label->setVisible(true);
label->setOpenExternalLinks(true);
}
}
}
// ======================================================================
OwncloudSetupPage::OwncloudSetupPage()
{
_ui.setupUi(this);
Theme *theme = Theme::instance();
setTitle( tr("<font color=\"%1\" size=\"5\">Connect to %2</font>")
.arg(theme->wizardHeaderTitleColor().name()).arg( theme->appNameGUI()));
setSubTitle( tr("<font color=\"%1\">Enter user credentials</font>")
.arg(theme->wizardHeaderTitleColor().name()));
registerField( QLatin1String("OCUrl"), _ui.leUrl );
registerField( QLatin1String("OCUser"), _ui.leUsername );
registerField( QLatin1String("OCPasswd"), _ui.lePassword);
registerField( QLatin1String("OCSyncFromScratch"), _ui.cbSyncFromScratch);
_ui.errorLabel->setVisible(true);
_ui.advancedBox->setVisible(false);
_progressIndi = new QProgressIndicator;
_ui.resultLayout->addWidget( _progressIndi );
_progressIndi->setVisible(false);
_ui.resultLayout->setEnabled(false);
// Error label
QString style = QLatin1String("border: 1px solid #eed3d7; border-radius: 5px; padding: 3px;"
"background-color: #f2dede; color: #b94a48;");
_ui.errorLabel->setStyleSheet( style );
_ui.errorLabel->setWordWrap(true);
_ui.errorLabel->setVisible(false);
_checking = false;
setupCustomization();
connect(_ui.leUrl, SIGNAL(textChanged(QString)), SLOT(slotUrlChanged(QString)));
connect( _ui.leUsername, SIGNAL(textChanged(QString)), this, SLOT(slotUserChanged(QString)));
connect( _ui.lePassword, SIGNAL(textChanged(QString)), this, SIGNAL(completeChanged()));
connect( _ui.leUsername, SIGNAL(textChanged(QString)), this, SIGNAL(completeChanged()));
connect( _ui.cbAdvanced, SIGNAL(stateChanged (int)), SLOT(slotToggleAdvanced(int)));
connect( _ui.pbSelectLocalFolder, SIGNAL(clicked()), SLOT(slotSelectFolder()));
}
OwncloudSetupPage::~OwncloudSetupPage()
{
delete _progressIndi;
}
void OwncloudSetupPage::slotToggleAdvanced(int state)
{
_ui.advancedBox->setVisible( state == Qt::Checked );
slotHandleUserInput();
QSize size = wizard()->sizeHint();
// need to substract header for some reason
size -= QSize(0, 63);
wizard()->setMinimumSize(size);
wizard()->resize(size);
}
void OwncloudSetupPage::setOCUser( const QString & user )
{
_ocUser = user;
_ui.leUsername->setText(user);
}
void OwncloudSetupPage::setServerUrl( const QString& newUrl )
{
_oCUrl = newUrl;
if( _oCUrl.isEmpty() ) {
_ui.leUrl->clear();
return;
}
_ui.leUrl->setText( _oCUrl );
}
void OwncloudSetupPage::setupCustomization()
{
// set defaults for the customize labels.
_ui.topLabel->hide();
_ui.bottomLabel->hide();
Theme *theme = Theme::instance();
QVariant variant = theme->customMedia( Theme::oCSetupTop );
if( !variant.isNull() ) {
setupCustomMedia( variant, _ui.topLabel );
}
variant = theme->customMedia( Theme::oCSetupBottom );
setupCustomMedia( variant, _ui.bottomLabel );
QString fixUrl = theme->overrideServerUrl();
if( !fixUrl.isEmpty() ) {
_ui.label_2->hide();
setServerUrl( fixUrl );
_ui.leUrl->setEnabled( false );
_ui.leUrl->hide();
}
}
void OwncloudSetupPage::slotUserChanged(const QString& user )
{
slotHandleUserInput();
}
// slot hit from textChanged of the url entry field.
void OwncloudSetupPage::slotUrlChanged(const QString& ocUrl)
{
slotHandleUserInput();
#if 0
QString url = ocUrl;
bool visible = false;
if (url.startsWith(QLatin1String("https://"))) {
_ui.urlLabel->setPixmap( QPixmap(":/mirall/resources/security-high.png"));
_ui.urlLabel->setToolTip(tr("This url is secure. You can use it."));
visible = true;
}
if (url.startsWith(QLatin1String("http://"))) {
_ui.urlLabel->setPixmap( QPixmap(":/mirall/resources/security-low.png"));
_ui.urlLabel->setToolTip(tr("This url is NOT secure. You should not use it."));
visible = true;
}
#endif
}
bool OwncloudSetupPage::isComplete() const
{
if( _ui.leUrl->text().isEmpty() ) return false;
if( _checking ) return false;
return !( _ui.lePassword->text().isEmpty() || _ui.leUsername->text().isEmpty() );
}
void OwncloudSetupPage::initializePage()
{
_connected = false;
_checking = false;
_multipleFoldersExist = false;
// call to init label
slotHandleUserInput();
if( _configExists ) {
_ui.lePassword->setFocus();
} else {
_ui.leUrl->setFocus();
}
}
bool OwncloudSetupPage::urlHasChanged()
{
bool change = false;
const QChar slash('/');
QUrl currentUrl( url() );
QUrl initialUrl( _oCUrl );
QString currentPath = currentUrl.path();
QString initialPath = initialUrl.path();
// add a trailing slash.
if( ! currentPath.endsWith( slash )) currentPath += slash;
if( ! initialPath.endsWith( slash )) initialPath += slash;
if( currentUrl.host() != initialUrl.host() ||
currentPath != initialPath ) {
change = true;
}
if( !change) { // no change yet, check the user.
QString user = _ui.leUsername->text().simplified();
if( user != _ocUser ) change = true;
}
return change;
}
// Called if the user changes the user- or url field. Adjust the texts and
// evtl. warnings on the dialog.
void OwncloudSetupPage::slotHandleUserInput()
{
// if the url has not changed, return.
if( ! urlHasChanged() ) {
// disable the advanced button as nothing has changed.
_ui.cbAdvanced->setEnabled(false);
_ui.advancedBox->setEnabled(false);
} else {
// Enable advanced stuff for new connection configuration.
_ui.cbAdvanced->setEnabled(true);
_ui.advancedBox->setEnabled(true);
}
const QString locFolder = localFolder();
// check if the local folder exists. If so, and if its not empty, show a warning.
QDir dir( locFolder );
QStringList entries = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
QString t;
if( !urlHasChanged() && _configExists ) {
// This is the password change mode: No change to the url and a config
// to an ownCloud exists.
t = tr("Change the Password for your configured account.");
} else {
// Complete new setup.
_ui.pbSelectLocalFolder->setText(QDir::toNativeSeparators(locFolder));
if( _remoteFolder.isEmpty() || _remoteFolder == QLatin1String("/") ) {
t = tr("Your entire account will be synced to the local folder '%1'.")
.arg(QDir::toNativeSeparators(locFolder));
} else {
t = tr("%1 folder '%2' is synced to local folder '%3'")
.arg(Theme::instance()->appName()).arg(_remoteFolder)
.arg(QDir::toNativeSeparators(locFolder));
}
if ( _multipleFoldersExist ) {
t += tr("<p><small><strong>Warning:</strong> You currently have multiple folders "
"configured. If you continue with the current settings, the folder configurations "
"will be discarded and a single root folder sync will be created!</small></p>");
}
if( entries.count() > 0) {
// the directory is not empty
if (!_ui.cbAdvanced->isChecked()) {
t += tr("<p><small><strong>Warning:</strong> The local directory is not empty. "
"Pick a resolution in the advanced settings!</small></p>");
}
_ui.resolutionWidget->setVisible(true);
} else {
// the dir is empty, which means that there is no problem.
_ui.resolutionWidget->setVisible(false);
}
}
_ui.syncModeLabel->setText(t);
_ui.syncModeLabel->setFixedHeight(_ui.syncModeLabel->sizeHint().height());
}
int OwncloudSetupPage::nextId() const
{
return OwncloudWizard::Page_Result;
}
QString OwncloudSetupPage::url() const
{
QString url = _ui.leUrl->text().simplified();
return url;
}
QString OwncloudSetupPage::localFolder() const
{
QString folder = wizard()->property("localFolder").toString();
return folder;
}
void OwncloudSetupPage::setConnected( bool comp )
{
_connected = comp;
_ui.resultLayout->setEnabled(true);
_progressIndi->setVisible(false);
_progressIndi->stopAnimation();
}
bool OwncloudSetupPage::validatePage()
{
bool re = false;
if( ! _connected) {
setErrorString(QString::null);
_checking = true;
_ui.resultLayout->setEnabled(true);
_progressIndi->setVisible(true);
_progressIndi->startAnimation();
emit completeChanged();
emit connectToOCUrl( url() );
return false;
} else {
// connecting is running
stopSpinner();
_checking = false;
emit completeChanged();
return true;
}
}
void OwncloudSetupPage::setErrorString( const QString& err )
{
if( err.isEmpty()) {
_ui.errorLabel->setVisible(false);
} else {
_ui.errorLabel->setVisible(true);
_ui.errorLabel->setText(err);
}
_checking = false;
emit completeChanged();
stopSpinner();
}
void OwncloudSetupPage::stopSpinner()
{
_ui.resultLayout->setEnabled(false);
_progressIndi->setVisible(false);
_progressIndi->stopAnimation();
}
OwncloudSetupPage::SyncMode OwncloudSetupPage::syncMode()
{
return BoxMode;
}
void OwncloudSetupPage::setRemoteFolder( const QString& remoteFolder )
{
if( !remoteFolder.isEmpty() ) {
_remoteFolder = remoteFolder;
}
}
void OwncloudSetupPage::setMultipleFoldersExist(bool exist)
{
_multipleFoldersExist = exist;
}
void OwncloudSetupPage::slotSelectFolder()
{
QString dir = QFileDialog::getExistingDirectory(0, tr("Local Sync Folder"), QDir::homePath());
if( !dir.isEmpty() ) {
_ui.pbSelectLocalFolder->setText(dir);
wizard()->setProperty("localFolder", dir);
slotHandleUserInput();
}
}
OwncloudSetupPage::SyncMode OwncloudWizard::syncMode()
{
return _setupPage->syncMode();
return OwncloudSetupPage::BoxMode;
}
void OwncloudWizard::setMultipleFoldersExist(bool exist)
{
_setupPage->setMultipleFoldersExist(exist);
}
void OwncloudSetupPage::setConfigExists( bool config )
{
_configExists = config;
if (config == true) {
setSubTitle( tr("<font color=\"%1\">Change your user credentials</font>")
.arg(Theme::instance()->wizardHeaderTitleColor().name()));
}
}
// ======================================================================
OwncloudWizardResultPage::OwncloudWizardResultPage()
{
_ui.setupUi(this);
// no fields to register.
Theme *theme = Theme::instance();
setTitle( tr("<font color=\"%1\" size=\"5\">Everything set up!</font>")
.arg(theme->wizardHeaderTitleColor().name()));
// required to show header in QWizard's modern style
setSubTitle( QLatin1String(" ") );
_ui.pbOpenLocal->setText("Open local folder");
_ui.pbOpenServer->setText(tr("Open %1").arg(Theme::instance()->appNameGUI()));
_ui.pbOpenLocal->setIcon(QIcon(":/mirall/resources/folder-sync.png"));
_ui.pbOpenLocal->setText(tr("Open Local Folder"));
_ui.pbOpenLocal->setIconSize(QSize(48, 48));
connect(_ui.pbOpenLocal, SIGNAL(clicked()), SLOT(slotOpenLocal()));
_ui.pbOpenLocal->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
QIcon appIcon = theme->applicationIcon();
_ui.pbOpenServer->setIcon(appIcon.pixmap(48));
_ui.pbOpenServer->setText(tr("Open %1").arg(theme->appNameGUI()));
_ui.pbOpenServer->setIconSize(QSize(48, 48));
_ui.pbOpenServer->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
connect(_ui.pbOpenServer, SIGNAL(clicked()), SLOT(slotOpenServer()));
setupCustomization();
}
OwncloudWizardResultPage::~OwncloudWizardResultPage()
{
}
void OwncloudWizardResultPage::setComplete(bool complete)
{
_complete = complete;
emit completeChanged();
}
bool OwncloudWizardResultPage::isComplete() const
{
return _complete;
}
void OwncloudWizardResultPage::initializePage()
{
const QString localFolder = wizard()->property("localFolder").toString();
QString text;
if( _remoteFolder == QLatin1String("/") || _remoteFolder.isEmpty() ) {
text = tr("Your entire account is synced to the local folder <i>%1</i>")
.arg(QDir::toNativeSeparators(localFolder));
} else {
text = tr("ownCloud folder <i>%1</i> is synced to local folder <i>%2</i>")
.arg(_remoteFolder).arg(QDir::toNativeSeparators(localFolder));
}
_ui.localFolderLabel->setText( text );
}
void OwncloudWizardResultPage::setRemoteFolder(const QString &remoteFolder)
{
_remoteFolder = remoteFolder;
}
void OwncloudWizardResultPage::setupCustomization()
{
// set defaults for the customize labels.
_ui.topLabel->setText( QString::null );
_ui.topLabel->hide();
QVariant variant = Theme::instance()->customMedia( Theme::oCSetupResultTop );
setupCustomMedia( variant, _ui.topLabel );
}
// ======================================================================
/**
* Folder wizard itself
*/
OwncloudWizard::OwncloudWizard(QWidget *parent)
: QWizard(parent),
_configExists(false)
{
_setupPage = new OwncloudSetupPage;
_resultPage = new OwncloudWizardResultPage;
setPage(Page_oCSetup, _setupPage );
setPage(Page_Result, _resultPage );
// note: start Id is set by the calling class depending on if the
// welcome text is to be shown or not.
setWizardStyle( QWizard::ModernStyle );
connect( this, SIGNAL(currentIdChanged(int)), SLOT(slotCurrentPageChanged(int)));
connect( _setupPage, SIGNAL(connectToOCUrl(QString)), SIGNAL(connectToOCUrl(QString)));
Theme *theme = Theme::instance();
setWizardStyle(QWizard::ModernStyle);
setPixmap( QWizard::BannerPixmap, theme->wizardHeaderBanner() );
setPixmap( QWizard::LogoPixmap, theme->wizardHeaderLogo() );
setOption( QWizard::NoBackButtonOnStartPage );
setOption( QWizard::NoBackButtonOnLastPage );
setOption( QWizard::NoCancelButton );
setTitleFormat(Qt::RichText);
setSubTitleFormat(Qt::RichText);
}
QString OwncloudWizard::localFolder() const
{
return(_setupPage->localFolder());
}
QString OwncloudWizard::ocUrl() const
{
QString url = field("OCUrl").toString().simplified();
return url;
}
void OwncloudWizard::enableFinishOnResultWidget(bool enable)
{
_resultPage->setComplete(enable);
}
void OwncloudWizard::setRemoteFolder( const QString& remoteFolder )
{
_setupPage->setRemoteFolder( remoteFolder );
_resultPage->setRemoteFolder( remoteFolder );
}
void OwncloudWizard::showConnectInfo( const QString& msg )
{
if( _setupPage ) {
_setupPage->setErrorString( msg );
}
}
void OwncloudWizard::successfullyConnected(bool enable)
{
_setupPage->setConnected( enable );
if( enable ) {
next();
}
}
void OwncloudWizard::slotCurrentPageChanged( int id )
{
qDebug() << "Current Wizard page changed to " << id;
if( id == Page_oCSetup ) {
setButtonText( QWizard::NextButton, tr("Connect...") );
emit clearPendingRequests();
_setupPage->initializePage();
}
if( id == Page_Result ) {
appendToConfigurationLog( QString::null );
}
}
void OwncloudWizard::displayError( const QString& msg )
{
_setupPage->setErrorString( msg );
}
void OwncloudWizard::appendToConfigurationLog( const QString& msg, LogType type )
{
_setupLog << msg;
qDebug() << "Setup-Log: " << msg;
}
void OwncloudWizard::setOCUrl( const QString& url )
{
_setupPage->setServerUrl( url );
}
void OwncloudWizard::setOCUser( const QString& user )
{
_oCUser = user;
_setupPage->setOCUser( user );
}
void OwncloudWizard::setConfigExists( bool config )
{
_configExists = config;
_setupPage->setConfigExists( config );
}
bool OwncloudWizard::configExists()
{
return _configExists;
}
void OwncloudWizardResultPage::slotOpenLocal()
{
const QString localFolder = wizard()->property("localFolder").toString();
QDesktopServices::openUrl(QUrl::fromLocalFile(localFolder));
}
void OwncloudWizardResultPage::slotOpenServer()
{
QUrl url = field("OCUrl").toUrl();
qDebug() << Q_FUNC_INFO << url;
QDesktopServices::openUrl(url);
}
} // end namespace

Some files were not shown because too many files have changed in this diff Show More