Comparar commits

..

257 Commits

Autor SHA1 Mensagem Data
Klaas Freitag 22e31b2232 Bump version to 2.1.0 beta1 2015-11-17 15:33:22 +01:00
Klaas Freitag 5ec4fd94e0 ActivityWidget: No progress indic. for non connected accounts. 2015-11-17 15:05:54 +01:00
Klaas Freitag c9ef4d5fa0 ActivityWidget: Detect new items in the list to refetch the activities.
On refresh, remove the activity list object from the models list.
2015-11-17 14:46:25 +01:00
Klaas Freitag 1cdf0e8597 ActivityWidget: Always combine the final activity list.
In cases where the activity app is not activated on the server, the
returned list of activities is empty, so it is better this way.
2015-11-17 14:46:25 +01:00
Daniel Molkentin 7ba88cc9e3 Merge remote-tracking branch 'origin/2.0' 2015-11-17 12:57:31 +01:00
Daniel Molkentin 914df32587 Documentation: Fix PDF logo, version number 2015-11-17 12:55:41 +01:00
Olivier Goffart e0f54428d0 ShareDialog: softer line separator (#3737) 2015-11-17 12:02:20 +01:00
Markus Goetz 52a11b0835 Activities: Fix on Mac #4083 2015-11-17 11:53:49 +01:00
Markus Goetz ef17dc6482 Connectivity: Improve log output 2015-11-17 11:02:53 +01:00
Markus Goetz 9978dc3f6c QNAM: Use invalid configuration instead of default configuration
For #3969 and others.
2015-11-17 11:02:53 +01:00
Olivier Goffart f618ed3dfb gui: Fix some icons in highdpi
QIcon does not need to use Theme::hidpiFileName because QIcon takes care
of the @2x images
2015-11-17 10:48:45 +01:00
Olivier Goffart ef915fb2e5 SharedDialog: Fix the delete icon on windows and mac (#3737) 2015-11-17 10:40:45 +01:00
Olivier Goffart 6e42405113 ShareDialog: remove the search button (#3737) 2015-11-17 10:24:35 +01:00
Olivier Goffart 11ef07c74a ActivityWidget: fix compiler warnings 2015-11-17 09:54:38 +01:00
Jenkins for ownCloud 486d7690c4 [tx-robot] updated from transifex 2015-11-17 02:19:08 -05:00
Klaas Freitag 8852911f67 Merge branch 'master' of github.com:owncloud/mirall 2015-11-16 18:16:58 +01:00
Klaas Freitag e38bc6eab8 AcitivityWidget: Moved timespan-in-words method to utility.
Also added a second parameter, fixed plural translation and added
a less-than-a-minute-ago term.
2015-11-16 18:08:25 +01:00
Olivier Goffart 459e200ac0 ShareDialog: add a QScrollArea (issue #4125 ) 2015-11-16 18:01:11 +01:00
Klaas Freitag c781155b60 General Settings page: Remove the add account button.
It is now in the toolbox on the account page.
2015-11-16 17:07:05 +01:00
Klaas Freitag 87aa1de67a Merge branch 'newactivity' 2015-11-16 17:02:05 +01:00
Klaas Freitag ccb871c30b ActivityWidget: Show the subject in the Tooltip.
the original text might be elided.
2015-11-16 16:44:52 +01:00
Klaas Freitag 74ed0b4f09 Merge pull request #4139 from owncloud/account_toolbox
AccountSettings: Add a toolbox button for the account specific actions.
2015-11-16 15:53:30 +01:00
Klaas Freitag becbb7b284 AccountSettings: Address jans suggestions in #4139
- Changed sequence of menu items
- lowercased entries
- removed the "Account" from entries, its in the toolbox button already
- added a little space between toolbox button label and the rectangle.
2015-11-16 15:50:32 +01:00
Klaas Freitag ff76a842d0 Added some documentation. 2015-11-16 15:38:08 +01:00
Klaas Freitag a56926b8d9 ActivityWidget: open local file in file manager if exists. 2015-11-16 15:31:24 +01:00
Olivier Goffart 0d21503ee5 ShareDialog: fix auto completion to actualy auto complete 2015-11-16 15:23:02 +01:00
Klaas Freitag 631cb095dd ActivityWidget: Show the local path in a tooltip.
Note that the activity has also entries of files that are not synced so
that not every activity entry has to have a local pendant.

Also, one activity entry can reference multiple files, so only the first
one is shown.
2015-11-16 15:07:02 +01:00
Klaas Freitag caba719950 Folderman: Added method to find local files for a rel. server path. 2015-11-16 15:04:59 +01:00
Roeland Douma 786b602c26 Merge pull request #4133 from owncloud/sharedialog_uit_improvements
[Sharedialog] Fix UI stuff
2015-11-16 13:36:00 +01:00
Jenkins for ownCloud f4bfce153d [tx-robot] updated from transifex 2015-11-16 02:18:48 -05:00
Jenkins for ownCloud e1ab50b17c [tx-robot] updated from transifex 2015-11-14 02:19:03 -05:00
Klaas Freitag 7e4c0bd515 AccountSettings: Add a toolbox button for the account specific actions.
Also move the 'Add Account' button from the General Tab, where it
is not properly found, to the new account toolbox.
2015-11-13 14:50:07 +01:00
Klaas Freitag ddcec2971e ActivityDelegate: Gray out font if account is not connected.
This gives a visual indication of the account not being connected
and as a result maybe outdated activity data.
2015-11-13 12:48:25 +01:00
Roeland Jago Douma 72d119a05f [Sharing] Delete animation when share is removed 2015-11-13 11:03:51 +01:00
Roeland Jago Douma d423cf2c7f [Sharedialog] Fix UI stuff
Implemented suggestions form
https://github.com/owncloud/client/issues/3737#issuecomment-156036279

* Removed Shares text
* Permissions now next to username
* Simplified permissions by default
* Button to get more detailed permissions
2015-11-13 10:27:26 +01:00
Jenkins for ownCloud 3d847b50cf [tx-robot] updated from transifex 2015-11-13 02:19:01 -05:00
Klaas Freitag 731a13cfd1 ActivityWidget: Take the account state (connected or not) in account.
Display the activity entries in a different mode if the account is not
longer connected.
2015-11-12 17:52:00 +01:00
Klaas Freitag 81296fae9d AccountManager: new method to return AccountStatePtr from the name.
It uses the Account displayName to destinguish.
2015-11-12 17:50:00 +01:00
Klaas Freitag aa38f7a4f2 ActivityWidget: implement removeAccount. 2015-11-12 15:39:07 +01:00
Jenkins for ownCloud bac69f9984 [tx-robot] updated from transifex 2015-11-12 02:19:14 -05:00
Klaas Freitag c46547592c sync excludes: Add .directory files to be ignored 2015-11-11 17:27:01 +01:00
Markus Goetz fd4a5100a6 NSIS: Include Qt bearer management DLLs #3969 2015-11-11 15:19:15 +01:00
Klaas Freitag ff4a8c9202 GeneralSettings: Remove references to protocolwidget.
The ProtocolWidget is now the Activitytab Widget
2015-11-11 14:49:11 +01:00
Christian Kamm c871d721fd Tray tooltip: Use aliasGui #4096 2015-11-11 14:35:56 +01:00
Christian Kamm 05d1cc9a94 Sharing: Fix crash with share deletion #4111
The problem was that Share could be deleted *before*
the OcsShareJob itself finished. Since Share was the
parent of the network job, its object would be deleted
too early.

In general, it's unnecessary to assign parents to the OcsJobs
because they delete themselves when finished.
2015-11-11 13:28:20 +01:00
Christian Kamm 196ee05fcc Share dialog: Initialize expected expire date #4075 2015-11-11 13:01:12 +01:00
Christian Kamm e50cfa4e1b Recent Activity: Don't reset for no reason #3889 2015-11-11 12:48:15 +01:00
Daniel Molkentin 3224a959a4 Forward-declare QModelIndex. Fixes Qt4 builds. 2015-11-11 11:52:10 +01:00
Christian Kamm 2ccb3648c7 Recent Activity: Filter metadata updates #3963 2015-11-11 11:19:04 +01:00
Christian Kamm 01aa647527 FolderMan: Fix infinite wait on pause #4093
If a folder was paused while being the next item in the scheduling
queue, the whole scheduling could get stuck.

This also fixes the progress information of paused folders possibly
getting stuck.
2015-11-11 10:59:04 +01:00
Christian Kamm eb3388de68 Fix OwnSql test
3993a7f636 added asserts for failing
prepare() calls
2015-11-11 09:42:31 +01:00
Christian Kamm beb970646b Fix csync_exclude test
The exclude file was reorganized in
b6aa18bfbc
2015-11-11 09:42:31 +01:00
Christian Kamm 25c177ca3b SyncJournalFileRecord: Remove unused mode field 2015-11-11 09:42:31 +01:00
Daniel Molkentin 1d9c591c08 Fix OS X compilation: use mode_t instead of __mode_t 2015-11-11 09:36:17 +01:00
Jenkins for ownCloud f753960add [tx-robot] updated from transifex 2015-11-11 02:19:02 -05:00
Klaas Freitag 5e98894a97 ActivityView: Add a progress indicator widget to indicate action. 2015-11-10 18:10:58 +01:00
Markus Goetz 15fe3b569b Merge pull request #4100 from mnutt/credentials-from-url
Populate account setup credentials from server URL
2015-11-10 16:12:34 +01:00
Klaas Freitag 0e0b6026fc Activity: Unify the GUI, and reactivate copy to clipboard for all. 2015-11-10 15:12:35 +01:00
Christian Kamm 17dd199cba Checksums: Treat more carefully in db #4034
In particular, preserve them on local rename or remote move.
2015-11-10 15:05:00 +01:00
Christian Kamm 8f7dbe71a1 CleanupPollsJobs: Preserve more journal data 2015-11-10 14:26:25 +01:00
Christian Kamm 4b5c3d8f09 Tray: Show settings dialog on click if it's not active
Before, you often had to click twice to bring the window
to the front, because the first click would close it.
2015-11-10 12:33:36 +01:00
Christian Kamm 9955b0756a Settings: Show first account page on startup #4036 2015-11-10 12:20:08 +01:00
Christian Kamm 76d9b9c0e2 Rename env variables to include unit #2939 2015-11-10 12:07:10 +01:00
Christian Kamm 175ad6fb77 Reflect read-only permissions in filesystem #3244 2015-11-10 10:14:25 +01:00
Christian Kamm 51896902e3 Silence warning by removing extra semicolon 2015-11-10 10:06:49 +01:00
Jenkins for ownCloud b55220905e [tx-robot] updated from transifex 2015-11-10 02:19:10 -05:00
Michael Nutt bd65eb32b7 SetupWizard: populate credentials from HTTP server URL
If a user enters a server URL in the form of
https://user:pass@example.com/, pre-populate the following credentials
page with those values.
2015-11-09 18:21:33 -05:00
Olivier Goffart b29d1e94b5 ShareDialog: fix autocompletion took the wrong user 2015-11-09 17:22:17 +01:00
Olivier Goffart b74e812671 Share dialog: a few layout adjustements 2015-11-09 17:22:17 +01:00
Olivier Goffart aa27b5db14 Discovery: silent static analysis warning
We do a check one line earlier that the size is smaller than the buffer
Then we don't need to call strncpy,  strcpy is enough
2015-11-09 17:22:17 +01:00
Markus Goetz 81e3a62360 Account: Show local path in tooltip 2015-11-09 16:39:08 +01:00
Jenkins for ownCloud b0dc264369 [tx-robot] updated from transifex 2015-11-09 02:22:38 -05:00
Jenkins for ownCloud ac3f179420 [tx-robot] updated from transifex 2015-11-08 02:18:58 -05:00
Jenkins for ownCloud d9af837974 [tx-robot] updated from transifex 2015-11-07 02:19:09 -05:00
Markus Goetz 4784b327e7 Folder Status: Use same fat ... icon as web 2015-11-06 16:10:50 +01:00
Markus Goetz 1a1541ecd6 macdeployqt: Forgot comma 2015-11-06 15:18:14 +01:00
Klaas Freitag cf9fec73cf ActivityView: Add a second treeview for sync issues.
So there are three views now: One displaying the server activities, one for
the sync protocol and a third one for ignored files and issues.
2015-11-06 11:43:43 +01:00
Klaas Freitag a1551ef6ab ActivityDelegate: Make the row height public accessible.
Now it can be used to be set on the other two treeviews.
2015-11-06 11:41:32 +01:00
Markus Goetz 0163839cfb OS X: Fix toolbar oddity #3719 2015-11-06 10:18:09 +01:00
Roeland Douma 914a942e33 Merge pull request #4055 from owncloud/user_group_sharing
User group sharing
2015-11-06 09:35:42 +01:00
Jenkins for ownCloud 5e482ad4f7 [tx-robot] updated from transifex 2015-11-06 02:19:01 -05:00
Markus Goetz 6901fc9e38 macdeployqt: Also copy bearer plugins
See #3449
2015-11-05 18:15:45 +01:00
Markus Goetz 0070835330 macdeployqt: Hack to make Qt 5.5 work 2015-11-05 17:19:49 +01:00
Roeland Jago Douma 6431a2aa46 [Sharing] Build sharee list on every search 2015-11-05 15:01:29 +01:00
Roeland Jago Douma 4cf2422a83 [Sharing] Clear completer on activation 2015-11-05 13:16:52 +01:00
Roeland Jago Douma 806ec98eab [Sharing] Properly delete widget 2015-11-05 13:10:32 +01:00
Roeland Jago Douma 20fd349e17 [Sharing] Filter sharee list properly
You can't share with a user/group that you've already shared with
You can't share with yourself
2015-11-05 12:30:34 +01:00
Markus Goetz dae724b21c QtVersionAbstraction: Quick not neeed on OS X 2015-11-05 11:35:42 +01:00
Roeland Jago Douma 3e4612a1f0 [Sharing] Reorganized sharedialog code
Now we have 1 simple dialog that includes 2 widgets.
* ShareLinkWidget (for link shares)
* ShareUserGroupWidget (for user/group shares)

The ShareUserGroupWidget is only included if the server version is >=
8.2.0

For <8.2.0 the old behavior is preserved
2015-11-05 09:58:16 +01:00
Jenkins for ownCloud 6052e49bcc [tx-robot] updated from transifex 2015-11-05 02:19:06 -05:00
Roeland Jago Douma 90cbd461ab [Sharing] Allow sharing with users/groups from desktop 2015-11-04 22:00:35 +01:00
Lukas Reschke 517f2ed03d Add coverity badge 2015-11-04 21:06:48 +01:00
Daniel Molkentin 5fccc25f36 Fix Qt4 build 2015-11-04 19:28:54 +01:00
Klaas Freitag cb1571c6c5 ActivityWidget: Rather use accountState pointer directly.
Do not use it via a smart pointer class.
2015-11-04 16:40:22 +01:00
Markus Goetz 99b0d659bd Merge pull request #4073 from RealRancor/backport_4052_2.0
Backport #4052 to 2.0
2015-11-04 16:32:34 +01:00
RealRancor 854264c3d2 Backport #4052 to 2.0 2015-11-04 15:55:07 +01:00
Markus Goetz d2a6cae695 Merge pull request #4052 from RealRancor/fix_autoupdate
Make registry key a code block in autoupdate docs.
2015-11-04 15:11:45 +01:00
Markus Goetz be9ed2f6a9 Merge pull request #4058 from owncloud/client-updates
some doc fixes & updates
2015-11-04 15:11:28 +01:00
Markus Goetz 3ee8beb8a3 Exclude: Add .nfs*
From eltrai at #4017
2015-11-04 15:06:05 +01:00
Jenkins for ownCloud f816e5e2af [tx-robot] updated from transifex 2015-11-04 08:32:32 -05:00
Jenkins for ownCloud 448d8bff18 [tx-robot] updated from transifex 2015-11-04 08:30:20 -05:00
Daniel Molkentin b82ffb52c7 Merge remote-tracking branch 'origin/2.0' 2015-11-04 14:25:14 +01:00
Klaas Freitag 3bccfb8993 ActivityView: Add information about the account. 2015-11-04 13:22:03 +01:00
Roeland Jago Douma 6fb4e59120 [Sharing] First step towards proper group sharing 2015-11-04 12:56:06 +01:00
Roeland Jago Douma 309be57a12 [Sharing] Add user/group sharing dialog
Dialog can now retrive current shares for path, set the permissions on
those shares and delete the share.
2015-11-04 12:53:07 +01:00
Roeland Jago Douma 37098c96f9 [Sharing] Add setpermission to sharing code
For user/group/remote shares we were just missing the setPermissions
functionality
2015-11-04 12:53:02 +01:00
Jenkins for ownCloud 03cc67a2b1 [tx-robot] updated from transifex 2015-11-04 02:19:04 -05:00
Klaas Freitag cb4fba7658 ActivityWidget: Created a delegate for prettier display of the activities 2015-11-03 17:54:37 +01:00
Jenkins for ownCloud 89734b95c4 [tx-robot] updated from transifex 2015-11-03 02:19:04 -05:00
Carla Schroder a59c3ef278 some doc fixes & updates 2015-11-02 11:18:31 -08:00
Klaas Freitag b600ac882a ActivityListModel: Add method to refresh one Account (WIP) 2015-11-02 17:52:04 +01:00
Klaas Freitag 71849c4372 ActivityListModel: Add the page and pagesize parameter to ocs query. 2015-11-02 17:51:12 +01:00
Klaas Freitag 9545af0d43 JSONApiJob: Add method to add additional query parameter. 2015-11-02 17:37:23 +01:00
Klaas Freitag 6c6ee358d4 ActivityListModel: Check if the account is connected when fetching more. 2015-11-02 16:39:33 +01:00
Klaas Freitag 060f4f291b SettingsDialog: Add a tab with the SyncProtocol to ActivityWidget. 2015-11-02 15:46:00 +01:00
Klaas Freitag 3fb43d2322 ActivityWidget UI: Remove the preconfigured tabs from the tabwidget.
It is better to create the tabs from the code in SettingsDialog.
2015-11-02 15:45:17 +01:00
Klaas Freitag 4895683bab ActivityWidget: Do a proper asynchronous model to display the activity. 2015-11-02 15:44:13 +01:00
Klaas Freitag 43800e3d1c Merge pull request #4049 from Naereen/patch-1
Reviewing, improved style, and a initializing message
2015-11-02 10:14:07 +01:00
Klaas Freitag 3b8e1dcd89 SettingsDialog: Make ActivityWidget a member. 2015-11-01 22:30:46 +01:00
Klaas Freitag 302d6b321e ActivityWidget: Add a job to fetch activities, Activity object added 2015-11-01 22:30:37 +01:00
Jenkins for ownCloud 3f85694394 [tx-robot] updated from transifex 2015-11-01 02:18:55 -05:00
RealRancor 8c5ef2f1c3 Make registry key a code block in autoupdate docs. 2015-10-31 19:05:21 +01:00
Lilian Besson aa1a2d1247 Reviewing, improved style, and a initializing message
- Add an "Initializing owncloud-client-nautilus extension" message
2015-10-31 13:07:18 +01:00
Olivier Goffart 3993a7f636 OwnSQL: add asserts in case of error that should not happen 2015-10-30 14:05:58 +01:00
Olivier Goffart aaf16ff0e8 SyncJournalDB, clear all the queries before closing the database 2015-10-30 14:03:08 +01:00
Olivier Goffart 16c078963b owncloudcmd: add --max-sync-retries #4037
And limit by default to 3 retries
2015-10-30 13:36:31 +01:00
Klaas Freitag 9279bcdba4 Add a tabwidget to Settings dialog for activity. 2015-10-30 13:26:27 +01:00
Olivier Goffart 0c467ef5b4 Sync engine: fix signal/slot connection 2015-10-30 13:21:34 +01:00
Olivier Goffart 49cd53ee44 FolderStatusModel: attempt to detect removed undecided files #3612 2015-10-30 12:43:33 +01:00
Olivier Goffart 0e6a463564 Discovery: Don't leak DiscoveryDirectoryResult in case of error
Also remove redundent Q_FUNC_INFO
2015-10-30 12:43:33 +01:00
Roeland Douma d38b190317 Merge pull request #4033 from owncloud/share_object
Add share manager and the share objects
2015-10-30 11:01:03 +01:00
Klaas Freitag 29558cb7bb Merge pull request #4040 from owncloud/phil-davis-changelog-master
ChangeLog typos that reverted in merge from 2.0
2015-10-30 10:36:38 +01:00
Jenkins for ownCloud d2cd237e25 [tx-robot] updated from transifex 2015-10-30 02:19:00 -04:00
Roeland Jago Douma c7b814337a Use proper QFlags
Now the ShareTypes and Permissions are part of the Share class (which is
a bit better abstracted away).
2015-10-29 21:47:47 +01:00
Phil Davis ced51813c7 ChangeLog typos that reverted in merge from 2.0
Hopefully the last place I need to submit this again :)
2015-10-29 23:35:48 +05:45
Roeland Jago Douma dd8d02b8ef Act properly if OCS endpoint returned an error (OCS error)
For now pass it on to the gui. So at least they know something is wrong.
2015-10-29 16:56:23 +01:00
Olivier Goffart c3cf6aef7d SyncEngine: Don't whipe the white list if the sync was aborted
Issue #4018
2015-10-29 16:47:39 +01:00
Olivier Goffart 38a8e5ee03 Discovery: conding style: use const references for function parameters 2015-10-29 16:47:36 +01:00
Klaas Freitag 46269dac4e Merge branch '2.0'
Conflicts:
	ChangeLog
2015-10-29 16:34:15 +01:00
Roeland Jago Douma 8c0297f688 Use QLatin1Char 2015-10-29 16:03:47 +01:00
Klaas Freitag bc5890d8b5 Add version suffix git. 2015-10-29 15:46:02 +01:00
Klaas Freitag ee65315520 Merge branch '2.0.2' into 2.0
Conflicts:
	ChangeLog
2015-10-29 15:44:56 +01:00
Roeland Jago Douma 12f7cfde87 Shares do not have parents
Also some pointer cleanups
2015-10-29 15:42:25 +01:00
Roeland Jago Douma 6d80f3d756 Proper foreach 2015-10-29 15:42:25 +01:00
Roeland Jago Douma b32f752d31 Properly cleanup OCS jobs 2015-10-29 15:42:25 +01:00
Roeland Jago Douma cf8be7de91 Proper const usage 2015-10-29 15:42:25 +01:00
Roeland Jago Douma 30a3498c22 Fix typos 2015-10-29 15:42:25 +01:00
Roeland Jago Douma 239603e24c Make sure enforced passwords are properly respected
Fixes for old and new servers
2015-10-29 15:42:25 +01:00
Roeland Jago Douma 390daed3de Added getPublicUpload to LinkShare
so the gui does not have to know abou the internal permissions
2015-10-29 15:42:25 +01:00
Roeland Jago Douma 918c06aba3 Add share manager and the share objects 2015-10-29 15:42:20 +01:00
Klaas Freitag 726be08917 Revert "Config: Use monochrome icons per default on MacOSX."
This reverts commit 546cab3f62.
For OEMs this patch causes an empty tray icon set.
2015-10-29 15:37:31 +01:00
Olivier Goffart a127debc54 Quota: handle special negative value for the quota #3940
Don't show a progress bar if there is an unkown or unlimited total
2015-10-29 14:33:29 +01:00
Olivier Goffart 6aa26654f6 Merge remote-tracking branch 'origin/2.0' 2015-10-29 12:44:28 +01:00
Olivier Goffart 0fde7f0e6b Propagator: Keep a meaningfull error with old server and invalid file names
We changed the discovery code not to ignore files whose filename contains
charachter invalid on windows. (Because newer versions of the server
supports them)
Servers older than 8.1 will just say "Bad Request" as an error and it's a
regression against previous client version. So keep nice error even with
older server.

Relates to #3736
2015-10-29 12:44:08 +01:00
Christian Kamm d9f8edd259 Hidden files: Consider .* hidden everywhere #4023
This seems to be the only place where we did this only on
non-windows OSes.
2015-10-29 11:59:46 +01:00
ckamm 251679253a Merge pull request #3951 from ckamm/checksum
Checksums stored in database #3735
2015-10-29 10:40:24 +01:00
Christian Kamm 64756c5dce --version also shows Qt version
That makes it much easier for people reporting bugs.
2015-10-29 09:48:36 +01:00
Christian Kamm 9788055147 Propagator: Add blacklisting of disk space errors #2939 2015-10-29 09:36:59 +01:00
Jenkins for ownCloud 4d7fde59c2 [tx-robot] updated from transifex 2015-10-29 02:18:59 -04:00
Daniel Molkentin 4737c16996 State application name in update notification
Fixes #4020
2015-10-28 20:45:57 +01:00
Markus Goetz c97dfbf60c ChangeLog: Mention propagator removal 2015-10-28 17:03:47 +01:00
Christian Kamm 496b1e907d Checksum: Don't lose it on metadata update #3735
Also improve tests.
2015-10-28 14:49:55 +01:00
Christian Kamm 566131209d Checksum: Fixes after review 2015-10-28 14:46:20 +01:00
Christian Kamm b7823dc648 Checksum: Put checksum type into separate column #3735 2015-10-28 14:46:20 +01:00
Olivier Goffart 6d28a1b645 Fix Qt4 compilation 2015-10-28 13:26:35 +01:00
Markus Goetz b6aa18bfbc sync-exclude.lst: More likely matching files first 2015-10-28 11:25:02 +01:00
Markus Goetz d91ffc216a Exclude: Add .fuse_hidden #3999
No matter if we sync hidden files or not, those are files we should not sync
2015-10-28 11:18:44 +01:00
Markus Goetz a6c9e8c5b4 Merge branch 'kill_legacy_propagator' 2015-10-28 11:06:50 +01:00
Markus Goetz 9337927722 legacy propagator: Remove more code 2015-10-28 10:59:02 +01:00
Christian Kamm c81b02c7d9 csync tests: Remove unused function 2015-10-28 10:04:15 +01:00
Christian Kamm 5ea09d2668 Checksum: Disallow ADLER32, use Adler32 #3735 2015-10-28 09:59:33 +01:00
Christian Kamm b9fc4c5994 Checksum: Fix recomputation when forced in cfg file #3735
Don't recompute the checksum on each upload when the server does not
advertise supporting checksums.
2015-10-28 09:56:39 +01:00
Christian Kamm f1b500d3e0 Checksum: Add env variables to disable #3735 2015-10-28 09:56:39 +01:00
Christian Kamm dff37e11eb Propagate: Store computed checksums in db during upload #3735 2015-10-28 09:56:39 +01:00
Christian Kamm b1387f801b Propagate: On download store checksum header in db #3735 2015-10-28 09:56:39 +01:00
Christian Kamm 60b2312ab6 SyncJournal: Add checksumHeader column to metadata table #3735 2015-10-28 09:56:39 +01:00
Christian Kamm 0354289795 Checksums: Improvement in async computation and validation #3735 2015-10-28 09:56:39 +01:00
Christian Kamm c11c35c459 Revert "In case of empty checksum type, emit validated."
This reverts commit b05ca526a4.

The checksum type setting should not matter for downloads.
2015-10-28 09:56:39 +01:00
Christian Kamm 7c5e70ac3c PropagateUpload: Fix double-emission of finished #3844 2015-10-28 09:53:54 +01:00
Christian Kamm d2e5ba123d Fix compile after pull request merge
It conflicted with the optimization in
f18b40f7e7
2015-10-28 09:53:06 +01:00
Klaas Freitag 0c9568f6dc Merge pull request #4004 from owncloud/fix_hidden_detection
Fix hidden detection
2015-10-28 09:28:35 +01:00
Olivier Goffart f1d48a9356 Folder Model: The item needs to be selectable for the currenIndex to appear selected
And we set the current index in AccountSettings::slotLinkActivated
2015-10-27 17:37:12 +01:00
Olivier Goffart 89f2a9e6dc Account Settings: A link should make sure all the parents are expanded 2015-10-27 17:37:12 +01:00
Klaas Freitag a203da3919 FolderStatusModel: Check for null argument at start. 2015-10-27 17:37:12 +01:00
Klaas Freitag 1c1ef52cf1 AccountSettings: Display a link for undecided dirs in the sub text.
This only works in case the index is known. If not, no link is
shown, but we wait for the next update of the model.
2015-10-27 17:37:12 +01:00
Klaas Freitag 40c82c5c36 AccountSettings: Add index to undecided folder to be able to select it. 2015-10-27 17:37:12 +01:00
Klaas Freitag 81f0c6535e Enable external links in the label. 2015-10-27 17:37:12 +01:00
Klaas Freitag 46558d79a5 Add slot to handle clicks on folder names. 2015-10-27 17:37:12 +01:00
Olivier Goffart e86b4203b9 IgnoreFiles: Fix the socket API would not load the new custom ignored files #3496
We did not flush or closed the file after having modified it from the UI.
So when the socket api was reloading it, it wouldn't be able to load
the newly added rules
2015-10-27 16:07:59 +01:00
Olivier Goffart 05dd9554f9 SocketApi: Fix returning ignore for the root item all the time 2015-10-27 16:06:38 +01:00
Olivier Goffart 30a0423f81 Dolphin integration: fix error icons 2015-10-27 15:19:10 +01:00
Olivier Goffart 26e1223f9a FolderStatusModel: reset also if a folder was renamed #4011 2015-10-27 13:51:56 +01:00
Olivier Goffart 84a04de7be Settings Dialog: don't put padding on the toolbar extension
Otherwise the extension just disapear with some styles (see Issue #3795)
2015-10-27 13:42:25 +01:00
Olivier Goffart 3ff7fa0092 Merge pull request #3997 from owncloud/phil-davis-patch-1
ChangeLog 2.0.2 minor typos for 2.0 branch
2015-10-27 12:58:59 +01:00
Olivier Goffart 3f2a2cb14b FolderDelegate: put the progressbar in place of the remote or local folder #3403 #3569
So the size of the delegate does not change
2015-10-27 12:52:33 +01:00
Olivier Goffart fac00348d9 Use the term folder sync connection in more places #3757 2015-10-27 12:12:21 +01:00
Olivier Goffart 3c93fd4fb7 AccountSettings: don't disable pause when offline (#4010) 2015-10-27 10:32:33 +01:00
Klaas Freitag 6b71273380 Discovery: Fix detection of hidden files.
In the discovery phase we want to detect that dot-files are hidden
on Linux and Mac.

This fixes strange behaviour seen in issue #3980
2015-10-26 14:24:05 +01:00
Olivier Goffart dccf4e9c34 Download: Soft errors for error while resuming (#4000) 2015-10-26 12:31:07 +01:00
Olivier Goffart 39289a3164 SocketAPI: don't trim the command #3297 2015-10-26 10:15:50 +01:00
Jenkins for ownCloud 6611d878ea [tx-robot] updated from transifex 2015-10-25 02:19:01 -04:00
Jenkins for ownCloud c3fc711095 [tx-robot] updated from transifex 2015-10-24 02:18:59 -04:00
Olivier Goffart 84f1bdbc87 Folder::wipe: We need to shut the socket API down before removing the DB
Because the DB stays open and locked.

Should fix #3824
2015-10-23 17:43:01 +02:00
Phil Davis 2eb5715599 ChangeLog 2.0.2 minor typos for 2.0 branch
These changes were committed to 2.0.2-rc1 branch https://github.com/owncloud/client/pull/3957/files https://github.com/owncloud/client/commit/6da2139a1bc4069f1cd1dfb017502f089c3af26f but that branch was never merged up into 2.0 (or 2.0.2-rc2) so the changes never went anywhere.

These changes could also be applied on 2.0.2-rc2 and 2.0.2 branch for completeness - then they would be in every branch in which they have appeared.
2015-10-23 21:21:31 +05:45
Olivier Goffart c93defc82d SyncEngine: remove unused functions 2015-10-23 17:11:19 +02:00
Olivier Goffart bd39c64798 Delete all files: make the 'Keep' on by default
On windows, we need to specify at least one AcceptRole.
Otherwise the DestructiveRole might become the default

Issue #3824
2015-10-23 17:06:14 +02:00
Olivier Goffart 42a6b242c7 SettingsDialog: use QWidgetAction for the toolbar so the extension works
When the toolbar is full because there is no enough room, make the extension
of the toolbar work, by using QWidgetAction::createWidget instead of
QToolBar::insertWidget

There should not be prolem when the window is too narrow.

Relates #3832
2015-10-23 16:23:37 +02:00
Markus Goetz e5570c24f2 Merge pull request #3995 from owncloud/return-code-of-version-and-help
owncloudcmd: Make returncode 0 for --version and --help
2015-10-23 15:36:01 +02:00
Joas Schilling 6d87bd15cd Make returncode 0 for --version and --help 2015-10-23 15:17:29 +02:00
Olivier Goffart d657c00b11 FolderStatusModel: fix getting the size of the folder in the selective sync (#3986)
Regressed since d610693af1. The problem
is that the _size vector contains the pathToRemove and that it was removed
before.
Reorganize a bit the code so there is only one loop that has still all the
 information.
2015-10-23 15:13:15 +02:00
Jenkins for ownCloud 6fae06f1d0 [tx-robot] updated from transifex 2015-10-23 02:19:00 -04:00
Olivier Goffart f18b40f7e7 csync_vio: reuse the information from readdir in stat
On unix we don't safe much (otherwise csync would have been
designed differently).
On windows however, the readdir already fetch all the info, so we
can as well use it.

We still have to query for the file id but we might optimize that later
2015-10-22 18:46:41 +02:00
Olivier Goffart 6a0633083d Network Settings: Adjust the bandwidth limit option with old Qt
- Disable the whole group box
- Add a tooltip explaining why it is disabled
- Make sure it is disabled in the settings in case of upgrade
- Do a runtime check in case the running Qt is greater
2015-10-22 17:57:34 +02:00
Daniel Molkentin 899f52be4f Revert "Settings dialog: remove the close button #3713"
This reverts commit ebee6f0bc2.

Unix window managers do not have a reliable way of hinting buttons,
so we need the close button.
2015-10-22 16:20:59 +02:00
Olivier Goffart 91525a7d33 csync_exclude: Don't ignore invalid char client side (#3736)
If the server does not support it, then the server will reply with an error
2015-10-22 12:32:53 +02:00
Olivier Goffart 455c3ae57d test/CMakeLists.txt: remove comments 2015-10-22 12:32:53 +02:00
Jenkins for ownCloud b5390b5aa2 [tx-robot] updated from transifex 2015-10-22 02:19:02 -04:00
Olivier Goffart a608b4e9e0 Folder: set csync verbosity to 0 if the Logger is not there
csync_log was still accounting for 8% of the local discovery (because
of vsnprintf and asprintf)
2015-10-22 01:13:35 +02:00
Olivier Goffart f6a543ada3 Logger: speedup the sync discovery when the log is innactive
The sync throw a lot of log message, and QDateTime::fromCurrentTime is
quite expensive. So don't call it if it's not needed.
2015-10-22 00:22:25 +02:00
Daniel Molkentin c7bf09c3d4 Merge remote-tracking branch 'origin/2.0' 2015-10-21 18:49:58 +02:00
Daniel Molkentin c8ae54d9e8 Merge pull request #3982 from owncloud/20-update
update 3-dot menu
2015-10-21 18:49:38 +02:00
Carla Schroder daf6d8772f update 3-dot menu 2015-10-21 09:44:38 -07:00
Olivier Goffart c80e72da83 Sharing: change coding style of enum
From all upper case to camel case

This hopefully fix the Windows build which fails because DELETE seems
to be a macro
2015-10-21 18:40:04 +02:00
Roeland Douma ec351e00ad Merge pull request #3773 from owncloud/split_sharedialog
Split sharedialog code
2015-10-21 16:55:59 +02:00
Olivier Goffart cf242871ea SyncEngine: keep a static pointer to the codec
The QTextCodec for UTF-8 is not going to change during the application life time.
So no need to look it up for every file
2015-10-21 16:38:26 +02:00
Olivier Goffart 597d36dcf2 csync_statedb: Use the index in csync_statedb_get_below_path
Make an index from the path, and make a query that uses the index
2015-10-21 16:38:26 +02:00
Olivier Goffart 9c388787bb csync_update: Don't fetch the etag in the local discovery from the DB
We don't need it, and it's slow.
This saves a lot of DB queries

(Also replaced a strlen>0 with a faster check)
2015-10-21 16:38:26 +02:00
Markus Goetz f739d8fdd3 sqlite: Update to version 3.9.1
For OS X and Windows.
This is in line with the tests/benchmarks Olivier is doing on ArchLinux.
2015-10-21 15:55:41 +02:00
Daniel Molkentin 78d782601e 2.0.2 final 2015-10-21 09:54:02 +02:00
Daniel Molkentin 86a83dc32c Finalize ChangeLog, adjust formatting 2015-10-21 09:53:34 +02:00
Daniel Molkentin c67e53c7b2 set version suffix back to git 2015-10-21 09:48:18 +02:00
Jenkins for ownCloud 5659e04e89 [tx-robot] updated from transifex 2015-10-21 02:19:02 -04:00
Olivier Goffart 8ff3055b47 sync-exclude.lst: Remove entries that are hardcoded anyway
The more item in the sync-exclude.lst, the slower is the sync.
Many items are already hardcoded. Some are files that no longer
exist.
2015-10-20 18:38:47 +02:00
Olivier Goffart 71827549d6 csync_exclude: Use PathMatchSpecA instead of PathMatchSpecW
So we avoid lots of memory allocation.
We can work with char* directly since both the pattern and the file
name are in UTF-8 and there is no need to understand unicode for
such pattern.

(In fact, '?' would not match anyore non-ascii characters, but I
don't think that's a problem. I don't think anyone use '?' in its
exclude list. And the two allocations per call to csync_fnmatch are
really worth getting rid of)
2015-10-20 18:38:47 +02:00
Olivier Goffart de5de7acc5 csync_exclude: Optimize
Avoid alocations as much as possible
2015-10-20 18:38:47 +02:00
Markus Goetz c8590c4468 Remove legacy propagator and neon
The code was already uneeded/unbuilt on Windows and OS X.
2015-10-20 17:57:43 +02:00
Markus Goetz 5f43c9cfad Merge tag 'v2.0.2-rc2' into 2.0
Second RC of ownCloud Client 2.0.2
2015-10-20 15:46:09 +02:00
Markus Goetz 4c9bc42b69 Revert libqsqlite changes for now
QtWebKit depends on it unfortunately
2015-10-20 13:04:33 +02:00
Klaas Freitag d09de79491 Bump version to RC2 2015-10-20 11:30:00 +02:00
Klaas Freitag c66c259447 SettingsDialog: Add new widet called ActivityWidget.
This is the new widget to display server activity.
2015-10-19 14:41:53 +02:00
Klaas Freitag 4ad165ce26 Utility: add method fileNameForGuiUse(), pimp up filename for GUI
Currently, this one only replaces colons by / on Mac platform. This makes
the function resuseable.
2015-10-19 14:36:55 +02:00
Markus Goetz 24a801dfd3 CMake/NSIS: We don't need QtSql (more) 2015-10-19 13:56:36 +02:00
Markus Goetz af79bc9211 CMake/NSIS: We don't need QtSql
The NSIS change also had a redundant libsqlite3 DLL entry
2015-10-19 13:46:46 +02:00
Klaas Freitag a3904f4d32 Theme: Revert logic of singleAccount switch.
To use the same logic as the other clients and unify ownBrander
implementations, the switch is now called multiAccount() rather
than singleAccount() with a reverse logic.
Desktop Client stays with the default of having multiaacount
enabled.

Note that existing brandings need to rename the switch.

https://github.com/owncloud/ownbrander/issues/443
2015-10-19 12:26:42 +02:00
Roeland Jago Douma 4a04dc1a3e Typos 2015-10-16 12:51:24 +02:00
Roeland Jago Douma 0e97fbb730 Use overloaded functions 2015-10-16 12:48:48 +02:00
Roeland Jago Douma 40ab3ee751 Now only 1 constructor to ocssharejob
* Pass the share_id to the functions that need it
2015-10-16 08:28:13 +02:00
Roeland Jago Douma f95fea9866 Move permissions to OcsShareJob 2015-10-16 08:09:57 +02:00
Roeland Jago Douma 03719334ea Remove unused members 2015-10-15 22:27:55 +02:00
Roeland Jago Douma 4441053b1c Thumbnailjob fixes
* Comments
* Use the path of the abstractnetworkjob
2015-10-15 22:18:22 +02:00
Roeland Jago Douma a34b663828 Now add parameters in a less crappy way 2015-10-15 21:58:16 +02:00
Roeland Jago Douma db1f4d4016 OCSJob -> OcsJob and more docs 2015-10-15 20:54:52 +02:00
Roeland Jago Douma 3ea944d1b3 Added setPublicUpload to OcsShareJob 2015-10-15 20:34:56 +02:00
Roeland Jago Douma b293aa762c Split sharing code
There is now a generic OCSJob which must be inherited by other jobs. This is in
prepartion for the other OCS job that will come (for the Sharee API endpoint
for example).

More logic is moved from the sharedialog to the OcsShareJob. So in the GUI code
we now only say what we want (a new share, set the password etc). And the code
in libsync will make that happen. Error handling is for now still done in the
GUI part.

For now the ocsjob and ocssharejob live in gui but probabaly we should
create a libshare or libocs at some point.
2015-10-15 20:05:47 +02:00
Klaas Freitag a8eb913535 SyncLogDialog: Do not delete SyncLogDialog after close.
Keep one instance for the lifetime of the generalsettings widget.
2015-10-07 18:59:48 +02:00
Klaas Freitag afd081f40b Settings: Move synclog widget to a seperate dialog.
This a first step to integrate the server activity view, see #3732
2015-10-01 16:57:37 +02:00
Klaas Freitag 0c148025a3 SyncLog Dialog WIP 2015-09-30 12:02:05 +02:00
221 arquivos alterados com 68828 adições e 32306 exclusões
-7
Ver Arquivo
@@ -133,13 +133,9 @@ endif()
#endif()
set(USE_NEON TRUE)
if(HAVE_QT5)
message(STATUS "Using Qt ${Qt5Core_VERSION_MAJOR}.${Qt5Core_VERSION_MINOR}.x")
if (${Qt5Core_VERSION_MAJOR} EQUAL "5")
if (${Qt5Core_VERSION_MINOR} EQUAL "4" OR ${Qt5Core_VERSION_MINOR} GREATER 4)
message(STATUS "We do not require Neon in this setup, compile without!")
set(USE_NEON FALSE)
else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
@@ -148,9 +144,6 @@ else()
message(STATUS "If possible compile me with Qt 5.4 or higher.")
endif()
if (USE_NEON)
find_package(Neon REQUIRED)
endif(USE_NEON)
find_package(OpenSSL 1.0.0 REQUIRED)
if(NOT TOKEN_AUTH_ONLY)
+38 -32
Ver Arquivo
@@ -1,49 +1,55 @@
ChangeLog
=========
version 2.0.2 (release 2015-10-xx)
version 2.1 (release 2015-yy-zz)
* We removed the old libneon-based propagator.
It was already disabled on OS X and Windows and only used on
Linux when building with Qt < 5.4 and having bandwidth limiting enabled.
So now you need Qt 5.4 on Linux if you want bandwidth limiting.
version 2.0.2 (release 2015-10-21)
* csync_file_stat_s: Save a bit of memory
* Shibboleth: Add our base user agent to WebKit
* SelectiveSync: Increase folder list timeout to 60
* Propagation: Try another sync on 423 Locked #3387
* Propagation: Make 423 Locked a soft error #3387
* Propagation: Reset upload blacklist if a chunk suceeds
* Application: Fix crash on early shutdown #3898
* Linux: Don't show settings dialog always when launched twice #3273 #3771 #3485
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. #3813
* Propagation: Try another sync on 423 Locked (#3387)
* Propagation: Make 423 Locked a soft error (#3387)
* Propagation: Reset upload blacklist if a chunk succeeds
* Application: Fix crash on early shutdown (#3898)
* Linux: Don't show settings dialog always when launched twice (#3273, #3771, #3485)
* win32 vio: Add the OPEN_REPARSE_POINTS flag to the CreateFileW call. (#3813)
* AccountSettings: only expand root elements on single click.
* AccountSettings: Do not allow to expand the folder list when disconnected.
* Use application SHORT name for the name of the MacOSX pkg file (ownBrander).
* FolderMan: Fix for removing a syncing folder #3843
* ConnectionMethodDialog: Don't be insecure on close #3863
* Updater: Ensure folders are not removed #3747
* Folder settings: Ensure path is cleaned #3811
* Propagator: Simplify sub job finished counting #3844
* Share dialog: Hide settings dialog before showing #3783
* UI: Only expand 1 level in folder list #3585
* UI: Allow folder expanding from button click #3585
* UI: Expand folder treeview on single click #3585
* GUI: Change tray menu order #3657
* FolderMan: Fix for removing a syncing folder (#3843)
* ConnectionMethodDialog: Don't be insecure on close (#3863)
* Updater: Ensure folders are not removed (#3747)
* Folder settings: Ensure path is cleaned (#3811)
* Propagator: Simplify sub job finished counting (#3844)
* Share dialog: Hide settings dialog before showing (#3783)
* UI: Only expand 1 level in folder list (#3585)
* UI: Allow folder expanding from button click (#3585)
* UI: Expand folder treeview on single click (#3585)
* GUI: Change tray menu order (#3657)
* GUI: Replace term "sign in" with "Log in" and friends.
* SetupPage: Fix crash caused by uninitialized Account object.
* Use a themable WebDAV path all over.
* Units: Back to the "usual" mix units (JEDEC standard).
* csync io: Full UNC path support on Win #3748
* Tray: Don't use the tray workaround with the KDE theme #3706, #3765
* ShareDialog: Fix folder display #3659
* AccountSettings: Restore from legacy only once #3565
* SSL Certificate Error Dialog: show account name #3729
* Tray notification: Don't show a message about modified folder #3613
* csync io: Full UNC path support on Win (#3748)
* Tray: Don't use the tray workaround with the KDE theme (#3706, #3765)
* ShareDialog: Fix folder display (#3659)
* AccountSettings: Restore from legacy only once (#3565)
* SSL Certificate Error Dialog: show account name (#3729)
* Tray notification: Don't show a message about modified folder (#3613)
* PropagateLocalRemove: remove entries from the DB even if there was an error.
* Settings UI improvements (eg. #3713, #3721, #3619 and others)
* Folder: Do not create the sync folder if it does not exist #3692
* Shell integratioon: don't show share menu item for top level folders
* Tray: Hide while modifying menus #3656 #3672
* AddFolder: Improve remote path selection error handling #3573
* csync_update: Use excluded_traversal() to improve performance #3638
* csync_excluded: Add fast _traversal() function #3638
* csync_exclude: Speed up siginificantly #3638
* AccountSettings: Adjust quota info design #3644 #3651
* Adjust buttons on remove folder/account questions #3654
* Folder: Do not create the sync folder if it does not exist (#3692)
* Shell integration: don't show share menu item for top level folders
* Tray: Hide while modifying menus (#3656, #3672)
* AddFolder: Improve remote path selection error handling (#3573)
* csync_update: Use excluded_traversal() to improve performance (#3638)
* csync_excluded: Add fast _traversal() function (#3638)
* csync_exclude: Speed up significantly (#3638)
* AccountSettings: Adjust quota info design (#3644, #3651)
* Adjust buttons on remove folder/account questions (#3654)
version 2.0.1 (release 2015-09-01)
* AccountWizard: fix when the theme specify a override URL (#3699)
+1 -1
Ver Arquivo
@@ -4,7 +4,7 @@
|-----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| client-build-matrix | [![Build Status](https://ci.owncloud.org/job/client-build-matrix-linux/badge/icon)](https://ci.owncloud.org/job/client-build-matrix-linux/) |
| client-test-matrix-linux-no-build | [![Build Status](https://ci.owncloud.org/buildStatus/icon?job=client-test-matrix-linux-no-build)](https://ci.owncloud.org/job/client-test-matrix-linux-no-build/) |
| coverity_scan | [![Build Status](https://img.shields.io/coverity/scan/2482.svg)](https://scan.coverity.com/projects/owncloud-mirall)
## Introduction
+1 -1
Ver Arquivo
@@ -4,7 +4,7 @@ set( MIRALL_VERSION_PATCH 0 )
set( MIRALL_SOVERSION 0 )
if ( NOT DEFINED MIRALL_VERSION_SUFFIX )
set( MIRALL_VERSION_SUFFIX "git") #e.g. beta1, beta2, rc1
set( MIRALL_VERSION_SUFFIX "beta1") #e.g. beta1, beta2, rc1
endif( NOT DEFINED MIRALL_VERSION_SUFFIX )
if( NOT DEFINED MIRALL_VERSION_BUILD )
+6
Ver Arquivo
@@ -38,6 +38,8 @@ QT_PLUGINS = [
'imageformats/libqgif.dylib',
'imageformats/libqico.dylib',
'imageformats/libqjpeg.dylib',
'bearer/libqcorewlanbearer.dylib',
'bearer/libqgenericbearer.dylib',
'imageformats/libqsvg.dylib',
'imageformats/libqmng.dylib',
]
@@ -144,6 +146,10 @@ def FindFramework(path):
search_pathes = FRAMEWORK_SEARCH_PATH
search_pathes.insert(0, QueryQMake('QT_INSTALL_LIBS'))
for search_path in search_pathes:
# The following two lines are needed for a custom built Qt from version 5.5 on, possibly not for the one from the Qt SDK.
# Looks like the upstream macdeployqt also had an issue there https://bugreports.qt.io/browse/QTBUG-47868
if path.find( "\@rpath/"):
path = path.replace("@rpath/", "")
abs_path = os.path.join(search_path, path)
if os.path.exists(abs_path):
return abs_path
-4
Ver Arquivo
@@ -32,7 +32,3 @@ SET(QT_MOC_EXECUTABLE ${MINGW_PREFIX}-moc)
SET(QT_RCC_EXECUTABLE ${MINGW_PREFIX}-rcc)
SET(QT_UIC_EXECUTABLE ${MINGW_PREFIX}-uic)
SET(QT_LRELEASE_EXECUTABLE ${MINGW_PREFIX}-lrelease)
# neon config
SET(NEON_CONFIG_EXECUTABLE ${CMAKE_FIND_ROOT_PATH}/bin/neon-config)
# /usr/i686-w64-mingw32/sys-root/mingw/bin/neon-config
+2
Ver Arquivo
@@ -20,5 +20,7 @@
<file>resources/lock-https.png</file>
<file>resources/lock-https@2x.png</file>
<file>resources/account.png</file>
<file>resources/more.png</file>
<file>resources/delete.png</file>
</qresource>
</RCC>
-73
Ver Arquivo
@@ -1,73 +0,0 @@
# - Try to find Neon
# Once done this will define
#
# NEON_FOUND - system has Neon
# NEON_INCLUDE_DIRS - the Neon include directory
# NEON_LIBRARIES - Link these to use Neon
# NEON_DEFINITIONS - Compiler switches required for using Neon
#
# Copyright (c) 2011-2013 Andreas Schneider <asn@cryptomilk.org>
#
# Redistribution and use is allowed according to the terms of the New
# BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
find_package(PkgConfig)
if (PKG_CONFIG_FOUND)
pkg_check_modules(_NEON neon)
endif (PKG_CONFIG_FOUND)
include(GNUInstallDirs)
find_path(NEON_INCLUDE_DIRS
NAMES
neon/ne_basic.h
HINTS
${_NEON_INCLUDEDIR}
${CMAKE_INSTALL_INCLUDEDIR}
)
find_library(NEON_LIBRARIES
NAMES
neon neon-27
HINTS
${_NEON_LIBDIR}
${CMAKE_INSTALL_LIBDIR}
${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Neon DEFAULT_MSG NEON_LIBRARIES NEON_INCLUDE_DIRS)
# show the NEON_INCLUDE_DIRS and NEON_LIBRARIES variables only in the advanced view
mark_as_advanced(NEON_INCLUDE_DIRS NEON_LIBRARIES)
# Check if neon was compiled with LFS support, if so, the NE_LFS variable has to
# be defined in the owncloud module.
# If neon was not compiled with LFS its also ok since the underlying system
# than probably supports large files anyway.
IF( CMAKE_FIND_ROOT_PATH )
FIND_PROGRAM( NEON_CONFIG_EXECUTABLE NAMES neon-config HINTS ${CMAKE_FIND_ROOT_PATH}/bin )
ELSE( CMAKE_FIND_ROOT_PATH )
FIND_PROGRAM( NEON_CONFIG_EXECUTABLE NAMES neon-config )
ENDIF( CMAKE_FIND_ROOT_PATH )
IF ( NEON_CONFIG_EXECUTABLE )
MESSAGE(STATUS "neon-config executable: ${NEON_CONFIG_EXECUTABLE}")
# neon-config --support lfs
EXECUTE_PROCESS( COMMAND ${NEON_CONFIG_EXECUTABLE} "--support" "lfs"
RESULT_VARIABLE LFS
OUTPUT_STRIP_TRAILING_WHITESPACE )
IF (LFS EQUAL 0)
MESSAGE(STATUS "libneon has been compiled with LFS support")
SET(NEON_WITH_LFS 1)
ELSE (LFS EQUAL 0)
MESSAGE(STATUS "libneon has not been compiled with LFS support, rely on OS")
ENDIF (LFS EQUAL 0)
ELSE ( NEON_CONFIG_EXECUTABLE )
MESSAGE(STATUS, "neon-config could not be found.")
ENDIF ( NEON_CONFIG_EXECUTABLE )
+5
Ver Arquivo
@@ -40,6 +40,7 @@
!define QT_DLL_PATH "${MING_BIN}"
!define ACCESSIBLE_DLL_PATH "${MING_LIB}/qt5/plugins/accessible"
!define SQLITE_DLL_PATH "${MING_LIB}/qt5/plugins/sqldrivers"
!define BEARER_DLL_PATH "${MING_LIB}/qt5/plugins/bearer"
!define IMAGEFORMATS_DLL_PATH "${MING_LIB}/qt5/plugins/imageformats"
!define PLATFORMS_DLL_PATH "${MING_LIB}/qt5/plugins/platforms"
@@ -406,6 +407,10 @@ Section "${APPLICATION_NAME}" SEC_APPLICATION
SetOutPath "$INSTDIR\sqldrivers"
File "${SQLITE_DLL_PATH}\qsqlite.dll"
SetOutPath "$INSTDIR\bearer"
File "${BEARER_DLL_PATH}\qgenericbearer.dll"
File "${BEARER_DLL_PATH}\qnativewifibearer.dll"
SetOutPath "$INSTDIR"
;License & release notes.
File "@CPACK_RESOURCE_FILE_LICENSE@"
+5 -1
Ver Arquivo
@@ -22,7 +22,9 @@ if( Qt5Core_FOUND )
find_package(Qt5WebKitWidgets REQUIRED)
find_package(Qt5WebKit REQUIRED)
find_package(Qt5PrintSupport REQUIRED)
find_package(Qt5Quick REQUIRED)
if(NOT APPLE)
find_package(Qt5Quick REQUIRED) # only needed on Windows because of OBS dependencies(?)
endif()
find_package(Qt5Widgets REQUIRED)
if(APPLE)
find_package(Qt5MacExtras REQUIRED)
@@ -31,7 +33,9 @@ if( Qt5Core_FOUND )
else( Qt5Core_FOUND )
if(WIN32 OR APPLE)
if (NOT BUILD_WITH_QT4)
message(FATAL_ERROR "Qt 5 not found, but application depends on Qt5 on Windows and Mac OS X")
endif ()
endif(WIN32 OR APPLE)
endif( Qt5Core_FOUND )
-4
Ver Arquivo
@@ -23,8 +23,4 @@
#cmakedefine SYSCONFDIR "@SYSCONFDIR@"
#cmakedefine SHAREDIR "@SHAREDIR@"
#ifndef NEON_WITH_LFS
#cmakedefine NEON_WITH_LFS "@NEON_WITH_LFS@"
#endif
#endif
-4
Ver Arquivo
@@ -24,10 +24,6 @@
#cmakedefine HAVE_ICONV 1
#cmakedefine HAVE_ICONV_CONST 1
#ifndef NEON_WITH_LFS
#cmakedefine NEON_WITH_LFS 1
#endif
#cmakedefine HAVE___MINGW_ASPRINTF 1
#cmakedefine HAVE_ASPRINTF 1
+9 -29
Ver Arquivo
@@ -1,9 +1,6 @@
project(libcsync)
add_subdirectory(std)
if(USE_NEON)
add_subdirectory(httpbf)
endif()
# Statically include sqlite
@@ -71,25 +68,14 @@ else()
endif()
if(USE_NEON)
list(APPEND csync_SRCS
csync_owncloud.c
csync_owncloud_util.c
)
list(APPEND CSYNC_LINK_LIBRARIES
${NEON_LIBRARIES}
)
include_directories(${NEON_INCLUDE_DIRS})
add_definitions(-DUSE_NEON)
endif(USE_NEON)
configure_file(csync_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/csync_version.h)
set(csync_HDRS
${CMAKE_CURRENT_BINARY_DIR}/csync_version.h
${CMAKE_CURRENT_BINARY_DIR}/../config_csync.h
csync.h
vio/csync_vio.h
vio/csync_vio_method.h
vio/csync_vio_module.h
)
# Statically include sqlite
@@ -140,18 +126,12 @@ else()
RUNTIME DESTINATION
${BIN_INSTALL_DIR}/${APPLICATION_EXECUTABLE}
)
INSTALL(
FILES
${csync_HDRS}
DESTINATION
${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}
)
INSTALL(
FILES
std/c_private.h
DESTINATION
${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}/std
)
endif()
# INSTALL(
# FILES
# ${csync_HDRS}
# DESTINATION
# ${INCLUDE_INSTALL_DIR}/${APPLICATION_NAME}
# )
-47
Ver Arquivo
@@ -58,12 +58,6 @@
#include "csync_rename.h"
#include "c_jhash.h"
#ifdef USE_NEON
// Breaking the abstraction for fun and profit.
#include "csync_owncloud.h"
#endif
static int _key_cmp(const void *key, const void *data) {
uint64_t a;
csync_file_stat_t *b;
@@ -154,9 +148,6 @@ int csync_init(CSYNC *ctx) {
ctx->local.type = LOCAL_REPLICA;
#ifdef USE_NEON
owncloud_init(ctx);
#endif
ctx->remote.type = REMOTE_REPLICA;
if (c_rbtree_create(&ctx->local.tree, _key_cmp, _data_cmp) < 0) {
@@ -216,14 +207,6 @@ int csync_update(CSYNC *ctx) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "No exclude file loaded or defined!");
}
#ifdef USE_NEON
/* This is not actually connecting, just setting the info for neon. The legacy propagator can use it.. */
if (dav_connect( ctx, ctx->remote.uri ) < 0) {
ctx->status_code = CSYNC_STATUS_CONNECT_ERROR;
return -1;
}
#endif
/* update detection for local replica */
csync_gettime(&start);
ctx->current = LOCAL_REPLICA;
@@ -646,10 +629,6 @@ int csync_destroy(CSYNC *ctx) {
SAFE_FREE(ctx->remote.uri);
SAFE_FREE(ctx->error_string);
#ifdef USE_NEON
owncloud_destroy(ctx);
#endif
#ifdef WITH_ICONV
c_close_iconv();
#endif
@@ -672,21 +651,6 @@ void csync_clear_exclude_list(CSYNC *ctx)
csync_exclude_clear(ctx);
}
int csync_set_auth_callback(CSYNC *ctx, csync_auth_callback cb) {
if (ctx == NULL || cb == NULL) {
return -1;
}
if (ctx->status & CSYNC_STATUS_INIT) {
ctx->status_code = CSYNC_STATUS_CSYNC_STATUS_ERROR;
fprintf(stderr, "This function must be called before initialization.");
return -1;
}
ctx->callbacks.auth_function = cb;
return 0;
}
void *csync_get_userdata(CSYNC *ctx) {
if (ctx == NULL) {
return NULL;
@@ -781,14 +745,3 @@ void csync_file_stat_free(csync_file_stat_t *st)
SAFE_FREE(st);
}
}
int csync_set_module_property(CSYNC* ctx, const char* key, void* value)
{
#ifdef USE_NEON
return owncloud_set_property(ctx, key, value);
#else
(void)ctx, (void)key, (void)value;
return 0;
#endif
}
+1 -15
Ver Arquivo
@@ -173,7 +173,7 @@ enum csync_vio_file_stat_fields_e {
CSYNC_VIO_FILE_STAT_FIELDS_TYPE = 1 << 0,
CSYNC_VIO_FILE_STAT_FIELDS_MODE = 1 << 1, // local POSIX mode
CSYNC_VIO_FILE_STAT_FIELDS_FLAGS = 1 << 2,
CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3,
// CSYNC_VIO_FILE_STAT_FIELDS_DEVICE = 1 << 3,
CSYNC_VIO_FILE_STAT_FIELDS_INODE = 1 << 4,
// CSYNC_VIO_FILE_STAT_FIELDS_LINK_COUNT = 1 << 5,
CSYNC_VIO_FILE_STAT_FIELDS_SIZE = 1 << 6,
@@ -212,7 +212,6 @@ struct csync_vio_file_stat_s {
mode_t mode;
dev_t device;
uint64_t inode;
int fields; // actually enum csync_vio_file_stat_fields_e fields;
@@ -519,19 +518,6 @@ const char *csync_get_status_string(CSYNC *ctx);
int csync_set_iconv_codec(const char *from);
#endif
/**
* @brief Set a property to module
*
* @param ctx The csync context.
*
* @param key The property key
*
* @param value An opaque pointer to the data.
*
* @return 0 on success, less than 0 if an error occured.
*/
int csync_set_module_property(CSYNC *ctx, const char *key, void *value);
/**
* @brief Aborts the current sync run as soon as possible. Can be called from another thread.
*
+59 -45
Ver Arquivo
@@ -171,7 +171,6 @@ bool csync_is_windows_reserved_word(const char* filename) {
static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const char *path, int filetype, bool check_leading_dirs) {
size_t i = 0;
const char *p = NULL;
const char *bname = NULL;
size_t blen = 0;
char *conflict = NULL;
@@ -179,22 +178,6 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
CSYNC_EXCLUDE_TYPE match = CSYNC_NOT_EXCLUDED;
CSYNC_EXCLUDE_TYPE type = CSYNC_NOT_EXCLUDED;
for (p = path; *p; p++) {
switch (*p) {
case '\\':
case ':':
case '?':
case '*':
case '"':
case '>':
case '<':
case '|':
return CSYNC_FILE_EXCLUDE_INVALID_CHAR;
default:
break;
}
}
/* split up the path */
bname = strrchr(path, '/');
if (bname) {
@@ -217,7 +200,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
goto out;
}
#ifdef _WIN32
#ifdef _WIN32
// Windows cannot sync files ending in spaces (#2176). It also cannot
// distinguish files ending in '.' from files without an ending,
// as '.' is a separator that is not stored internally, so let's
@@ -231,7 +214,26 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
goto out;
}
#endif
// Filter out characters not allowed in a filename on windows
const char *p = NULL;
for (p = path; *p; p++) {
switch (*p) {
case '\\':
case ':':
case '?':
case '*':
case '"':
case '>':
case '<':
case '|':
match = CSYNC_FILE_EXCLUDE_INVALID_CHAR;
goto out;
default:
break;
}
}
#endif
rc = csync_fnmatch(".owncloudsync.log*", bname, 0);
if (rc == 0) {
@@ -264,40 +266,41 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
goto out;
}
/* Build a list of path components to check. */
c_strlist_t *path_components = c_strlist_new(32);
char *path_split = strdup(path);
size_t len = strlen(path_split);
for (i = len; ; --i) {
// read backwards until a path separator is found
if (i != 0 && path_split[i-1] != '/') {
continue;
}
c_strlist_t *path_components = NULL;
if (check_leading_dirs) {
/* Build a list of path components to check. */
path_components = c_strlist_new(32);
char *path_split = strdup(path);
size_t len = strlen(path_split);
for (i = len; ; --i) {
// read backwards until a path separator is found
if (i != 0 && path_split[i-1] != '/') {
continue;
}
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
if (path_split[i] != 0) {
c_strlist_add_grow(&path_components, path_split + i);
}
// check 'basename', i.e. for "/foo/bar/fi" we'd check 'fi', 'bar', 'foo'
if (path_split[i] != 0) {
c_strlist_add_grow(&path_components, path_split + i);
}
if (i == 0 || !check_leading_dirs) {
break;
}
if (i == 0) {
break;
}
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
path_split[i-1] = '\0';
c_strlist_add_grow(&path_components, path_split);
// check 'dirname', i.e. for "/foo/bar/fi" we'd check '/foo/bar', '/foo'
path_split[i-1] = '\0';
c_strlist_add_grow(&path_components, path_split);
}
SAFE_FREE(path_split);
}
SAFE_FREE(path_split);
/* Loop over all exclude patterns and evaluate the given path */
for (i = 0; match == CSYNC_NOT_EXCLUDED && i < excludes->count; i++) {
bool match_dirs_only = false;
char *pattern_stored = c_strdup(excludes->vector[i]);
char* pattern = pattern_stored;
char *pattern = excludes->vector[i];
type = CSYNC_FILE_EXCLUDE_LIST;
if (strlen(pattern) < 1) {
SAFE_FREE(pattern_stored);
if (!pattern[0]) { /* empty pattern */
continue;
}
/* Excludes starting with ']' means it can be cleanup */
@@ -309,6 +312,9 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
}
/* Check if the pattern applies to pathes only. */
if (pattern[strlen(pattern)-1] == '/') {
if (!check_leading_dirs && filetype == CSYNC_FTW_TYPE_FILE) {
continue;
}
match_dirs_only = true;
pattern[strlen(pattern)-1] = '\0'; /* Cut off the slash */
}
@@ -326,7 +332,7 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
}
/* if still not excluded, check each component and leading directory of the path */
if (match == CSYNC_NOT_EXCLUDED) {
if (match == CSYNC_NOT_EXCLUDED && check_leading_dirs) {
size_t j = 0;
if (match_dirs_only && filetype == CSYNC_FTW_TYPE_FILE) {
j = 1; // skip the first entry, which is bname
@@ -338,8 +344,16 @@ static CSYNC_EXCLUDE_TYPE _csync_excluded_common(c_strlist_t *excludes, const ch
break;
}
}
} else if (match == CSYNC_NOT_EXCLUDED && !check_leading_dirs) {
rc = csync_fnmatch(pattern, bname, 0);
if (rc == 0) {
match = type;
}
}
if (match_dirs_only) {
/* restore the '/' */
pattern[strlen(pattern)] = '/';
}
SAFE_FREE(pattern_stored);
}
c_strlist_destroy(path_components);
+3 -10
Ver Arquivo
@@ -57,20 +57,13 @@ int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
#else /* HAVE_FNMATCH */
#include <shlwapi.h>
int csync_fnmatch(__const char *__pattern, __const char *__name, int __flags) {
wchar_t *pat = NULL;
wchar_t *name = NULL;
int csync_fnmatch(const char *pattern, const char *name, int flags) {
BOOL match;
(void) __flags;
(void) flags;
name = c_utf8_string_to_locale(__name);
pat = c_utf8_string_to_locale(__pattern);
match = PathMatchSpecA(name, pattern);
match = PathMatchSpecW(name, pat);
c_free_locale_string(pat);
c_free_locale_string(name);
if(match)
return 0;
else
-617
Ver Arquivo
@@ -1,617 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "csync_owncloud.h"
#include "csync_owncloud_private.h"
#include <inttypes.h>
#include "csync_private.h"
#include "csync_version.h"
/*
* helper method to build up a user text for SSL problems, called from the
* verify_sslcert callback.
*/
static void addSSLWarning( char *ptr, const char *warn, int len )
{
char *concatHere = ptr;
int remainingLen = 0;
if( ! (warn && ptr )) return;
remainingLen = len - strlen(ptr);
if( remainingLen <= 0 ) return;
concatHere = ptr + strlen(ptr); /* put the write pointer to the end. */
strncpy( concatHere, warn, remainingLen );
}
/*
* Callback to verify the SSL certificate, called from libneon.
* It analyzes the SSL problem, creates a user information text and passes
* it to the csync callback to ask the user.
*/
#define LEN 4096
static int ssl_callback_by_neon(void *userdata, int failures,
const ne_ssl_certificate *certificate)
{
char problem[LEN];
char buf[MAX(NE_SSL_DIGESTLEN, NE_ABUFSIZ)];
int ret = -1;
const ne_ssl_certificate *cert = certificate;
csync_auth_callback authcb = NULL;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
memset( problem, 0, LEN );
while( cert ) {
addSSLWarning( problem, "There are problems with the SSL certificate:\n", LEN );
if( failures & NE_SSL_NOTYETVALID ) {
addSSLWarning( problem, " * The certificate is not yet valid.\n", LEN );
}
if( failures & NE_SSL_EXPIRED ) {
addSSLWarning( problem, " * The certificate has expired.\n", LEN );
}
if( failures & NE_SSL_UNTRUSTED ) {
addSSLWarning( problem, " * The certificate is not trusted!\n", LEN );
}
if( failures & NE_SSL_IDMISMATCH ) {
addSSLWarning( problem, " * The hostname for which the certificate was "
"issued does not match the hostname of the server\n", LEN );
}
if( failures & NE_SSL_BADCHAIN ) {
addSSLWarning( problem, " * The certificate chain contained a certificate other than the server cert\n", LEN );
}
if( failures & NE_SSL_REVOKED ) {
addSSLWarning( problem, " * The server certificate has been revoked by the issuing authority.\n", LEN );
}
if (ne_ssl_cert_digest(cert, buf) == 0) {
addSSLWarning( problem, "Certificate fingerprint: ", LEN );
addSSLWarning( problem, buf, LEN );
addSSLWarning( problem, "\n", LEN );
}
cert = ne_ssl_cert_signedby( cert );
}
addSSLWarning( problem, "Do you want to accept the certificate chain anyway?\nAnswer yes to do so and take the risk: ", LEN );
if( ctx->csync_ctx ) {
authcb = csync_get_auth_callback( ctx->csync_ctx );
}
if( authcb ){
/* call the csync callback */
DEBUG_WEBDAV("Call the csync callback for SSL problems");
memset( buf, 0, NE_ABUFSIZ );
(*authcb) ( problem, buf, NE_ABUFSIZ-1, 1, 0, csync_get_userdata(ctx->csync_ctx) );
if( buf[0] == 'y' || buf[0] == 'Y') {
ret = 0;
} else {
DEBUG_WEBDAV("Authentication callback replied %s", buf );
}
}
DEBUG_WEBDAV("## VERIFY_SSL CERT: %d", ret );
return ret;
}
/*
* Authentication callback. Is set by ne_set_server_auth to be called
* from the neon lib to authenticate a request.
*/
static int authentication_callback_by_neon( void *userdata, const char *realm, int attempt,
char *username, char *password)
{
char buf[NE_ABUFSIZ];
csync_auth_callback authcb = NULL;
int re = attempt;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
(void) realm;
/* DEBUG_WEBDAV( "Authentication required %s", realm ); */
if( username && password ) {
DEBUG_WEBDAV( "Authentication required %s", username );
if( ctx->dav_session.user ) {
/* allow user without password */
if( strlen( ctx->dav_session.user ) < NE_ABUFSIZ ) {
strcpy( username, ctx->dav_session.user );
}
if( ctx->dav_session.pwd && strlen( ctx->dav_session.pwd ) < NE_ABUFSIZ ) {
strcpy( password, ctx->dav_session.pwd );
}
} else {
authcb = csync_get_auth_callback( ctx->csync_ctx );
if( authcb != NULL ){
/* call the csync callback */
DEBUG_WEBDAV("Call the csync callback for %s", realm );
memset( buf, 0, NE_ABUFSIZ );
(*authcb) ("Enter your username: ", buf, NE_ABUFSIZ-1, 1, 0, csync_get_userdata(ctx->csync_ctx) );
if( strlen(buf) < NE_ABUFSIZ ) {
strcpy( username, buf );
}
memset( buf, 0, NE_ABUFSIZ );
(*authcb) ("Enter your password: ", buf, NE_ABUFSIZ-1, 0, 0, csync_get_userdata(ctx->csync_ctx) );
if( strlen(buf) < NE_ABUFSIZ) {
strcpy( password, buf );
}
} else {
re = 1;
}
}
}
return re;
}
/*
* Authentication callback. Is set by ne_set_proxy_auth to be called
* from the neon lib to authenticate against a proxy. The data to authenticate
* against comes from mirall throught vio_module_init function.
*/
static int proxy_authentication_callback_by_neon( void *userdata, const char *realm, int attempt,
char *username, char *password)
{
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
(void) realm;
if( ctx->dav_session.proxy_user && strlen( ctx->dav_session.proxy_user ) < NE_ABUFSIZ) {
strcpy( username, ctx->dav_session.proxy_user );
if( ctx->dav_session.proxy_pwd && strlen( ctx->dav_session.proxy_pwd ) < NE_ABUFSIZ) {
strcpy( password, ctx->dav_session.proxy_pwd );
}
}
/* NTLM needs several attempts */
return (attempt < 3) ? 0 : -1;
}
/* Configure the proxy depending on the variables */
static int configureProxy( csync_owncloud_ctx_t *ctx, ne_session *session )
{
int port = 8080;
int re = -1;
if( ! session ) return -1;
if( ! ctx->dav_session.proxy_type ) return 0; /* Go by NoProxy per default */
if( ctx->dav_session.proxy_port > 0 ) {
port = ctx->dav_session.proxy_port;
}
if( c_streq(ctx->dav_session.proxy_type, "NoProxy" )) {
DEBUG_WEBDAV("No proxy configured.");
re = 0;
} else if( c_streq(ctx->dav_session.proxy_type, "DefaultProxy") ||
c_streq(ctx->dav_session.proxy_type, "HttpProxy") ||
c_streq(ctx->dav_session.proxy_type, "HttpCachingProxy") ||
c_streq(ctx->dav_session.proxy_type, "Socks5Proxy")) {
if( ctx->dav_session.proxy_host ) {
DEBUG_WEBDAV("%s at %s:%d", ctx->dav_session.proxy_type, ctx->dav_session.proxy_host, port );
if (c_streq(ctx->dav_session.proxy_type, "Socks5Proxy")) {
ne_session_socks_proxy(session, NE_SOCK_SOCKSV5, ctx->dav_session.proxy_host, port,
ctx->dav_session.proxy_user, ctx->dav_session.proxy_pwd);
} else {
ne_session_proxy(session, ctx->dav_session.proxy_host, port );
}
re = 2;
} else {
DEBUG_WEBDAV("%s requested but no proxy host defined.", ctx->dav_session.proxy_type );
/* we used to try ne_system_session_proxy here, but we should rather err out
to behave exactly like the caller. */
}
} else {
DEBUG_WEBDAV( "Unsupported Proxy: %s", ctx->dav_session.proxy_type );
}
return re;
}
/*
* This hook is called for with the response of a request. Here its checked
* if a Set-Cookie header is there for the PHPSESSID. The key is stored into
* the webdav session to be added to subsequent requests.
*/
static void post_request_hook(ne_request *req, void *userdata, const ne_status *status)
{
const char *set_cookie_header = NULL;
const char *sc = NULL;
char *key = NULL;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t*) userdata;
if (ctx->dav_session.session_key)
return; /* We already have a session cookie, and we should ignore other ones */
if(!(status && req)) return;
if( status->klass == 2 || status->code == 401 ) {
/* successful request */
set_cookie_header = ne_get_response_header( req, "Set-Cookie" );
if( set_cookie_header ) {
DEBUG_WEBDAV(" Set-Cookie found: %s", set_cookie_header);
/* try to find a ', ' sequence which is the separator of neon if multiple Set-Cookie
* headers are there.
* The following code parses a string like this:
* Set-Cookie: 50ace6bd8a669=p537brtt048jh8srlp2tuep7em95nh9u98mj992fbqc47d1aecp1;
*/
sc = set_cookie_header;
while(sc) {
const char *sc_val = sc;
const char *sc_end = sc_val;
int cnt = 0;
int len = strlen(sc_val); /* The length of the rest of the header string. */
while( cnt < len && *sc_end != ';' && *sc_end != ',') {
cnt++;
sc_end++;
}
if( cnt == len ) {
/* exit: We are at the end. */
sc = NULL;
} else if( *sc_end == ';' ) {
/* We are at the end of the session key. */
int keylen = sc_end-sc_val;
if( key ) {
int oldlen = strlen(key);
key = c_realloc(key, oldlen + 2 + keylen+1);
strcpy(key + oldlen, "; ");
strncpy(key + oldlen + 2, sc_val, keylen);
key[oldlen + 2 + keylen] = '\0';
} else {
key = c_malloc(keylen+1);
strncpy( key, sc_val, keylen );
key[keylen] = '\0';
}
/* now search for a ',' to find a potential other header entry */
while(cnt < len && *sc_end != ',') {
cnt++;
sc_end++;
}
if( cnt < len )
sc = sc_end+2; /* mind the space after the comma */
else
sc = NULL;
} else if( *sc_end == ',' ) {
/* A new entry is to check. */
if( *(sc_end + 1) == ' ') {
sc = sc_end+2;
} else {
/* error condition */
sc = NULL;
}
}
}
}
} else {
DEBUG_WEBDAV("Request failed, don't take session header.");
}
if( key ) {
DEBUG_WEBDAV("----> Session-key: %s", key);
SAFE_FREE(ctx->dav_session.session_key);
ctx->dav_session.session_key = key;
}
}
/*
* this hook is called just after a request has been created, before its sent.
* Here it is used to set the proxy connection header if available.
*/
static void request_created_hook(ne_request *req, void *userdata,
const char *method, const char *requri)
{
// FIXME Can possibly be merged with pre_send_hook
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
(void) method;
(void) requri;
if( !req ) return;
if(ctx->dav_session.proxy_type) {
/* required for NTLM */
ne_add_request_header(req, "Proxy-Connection", "Keep-Alive");
}
}
/*
* this hook is called just before a request has been sent.
* Here it is used to set the session cookie if available.
*/
static void pre_send_hook(ne_request *req, void *userdata,
ne_buffer *header)
{
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
if( !req ) return;
if(ctx->dav_session.session_key) {
ne_buffer_concat(header, "Cookie: ", ctx->dav_session.session_key, "\r\n", NULL);
} else {
DEBUG_WEBDAV("csync pre_send_hook We don't have a Auth Cookie (session_key), this is wrong!");
}
}
static int post_send_hook(ne_request *req, void *userdata,
const ne_status *status)
{
const char *location;
csync_owncloud_ctx_t *ctx = (csync_owncloud_ctx_t *) userdata;
(void) status;
location = ne_get_response_header(req, "Location");
if( !location ) return NE_OK;
if( ctx->dav_session.redir_callback ) {
if( ctx->dav_session.redir_callback( ctx->csync_ctx, location ) ) {
return NE_REDIRECT;
} else {
return NE_RETRY;
}
}
return NE_REDIRECT;
}
/*
* Connect to a DAV server
* This function sets the flag _connected if the connection is established
* and returns if the flag is set, so calling it frequently is save.
*/
int dav_connect(CSYNC *csyncCtx, const char *base_url) {
int useSSL = 0;
int rc;
char protocol[6] = {'\0'};
char uaBuf[256];
char *path = NULL;
char *scheme = NULL;
char *host = NULL;
unsigned int port = 0;
int proxystate = -1;
csync_owncloud_ctx_t *ctx = csyncCtx->owncloud_context;
struct csync_client_certs_s* clientCerts = csyncCtx->clientCerts;
if (ctx->_connected) {
return 0;
}
rc = c_parse_uri( base_url, &scheme,
&ctx->dav_session.user,
&ctx->dav_session.pwd,
&host, &port, &path );
if( rc < 0 ) {
DEBUG_WEBDAV("Failed to parse uri %s", base_url );
goto out;
}
DEBUG_WEBDAV("* scheme %s", scheme );
DEBUG_WEBDAV("* host %s", host );
DEBUG_WEBDAV("* port %u", port );
DEBUG_WEBDAV("* path %s", path );
if( strcmp( scheme, "owncloud" ) == 0 || strcmp( scheme, "http" ) == 0 ) {
strcpy( protocol, "http");
} else if( strcmp( scheme, "ownclouds" ) == 0 || strcmp( scheme, "https") == 0 ) {
strcpy( protocol, "https");
useSSL = 1;
} else {
DEBUG_WEBDAV("Invalid scheme %s, go out here!", scheme );
rc = -1;
goto out;
}
DEBUG_WEBDAV("* user %s", ctx->dav_session.user ? ctx->dav_session.user : "");
if (port == 0) {
port = ne_uri_defaultport(protocol);
}
ctx->dav_session.ctx = ne_session_create( protocol, host, port);
if (ctx->dav_session.ctx == NULL) {
DEBUG_WEBDAV("Session create with protocol %s failed", protocol );
rc = -1;
goto out;
}
if (ctx->dav_session.read_timeout != 0) {
ne_set_read_timeout(ctx->dav_session.ctx, ctx->dav_session.read_timeout);
DEBUG_WEBDAV("Timeout set to %u seconds", ctx->dav_session.read_timeout );
}
// Should never take more than some seconds, 30 is really a max.
ne_set_connect_timeout(ctx->dav_session.ctx, 30);
snprintf( uaBuf, sizeof(uaBuf), "Mozilla/5.0 (%s) mirall/%s (csyncoC)",
CSYNC_STRINGIFY( MIRALL_VERSION ), csync_owncloud_get_platform() );
ne_set_useragent( ctx->dav_session.ctx, uaBuf);
ne_set_server_auth(ctx->dav_session.ctx, authentication_callback_by_neon, ctx);
if( useSSL ) {
if (!ne_has_support(NE_FEATURE_SSL)) {
DEBUG_WEBDAV("Error: SSL is not enabled.");
rc = -1;
goto out;
}
if(clientCerts != NULL) {
ne_ssl_client_cert *clicert;
DEBUG_WEBDAV("dav_connect: certificatePath and certificatePasswd are set, so we use it" );
DEBUG_WEBDAV(" with certificatePath: %s", clientCerts->certificatePath );
DEBUG_WEBDAV(" with certificatePasswd: %s", clientCerts->certificatePasswd );
clicert = ne_ssl_clicert_read ( clientCerts->certificatePath );
if ( clicert == NULL ) {
DEBUG_WEBDAV ( "Error read certificate : %s", ne_get_error ( ctx->dav_session.ctx ) );
} else {
if ( ne_ssl_clicert_encrypted ( clicert ) ) {
int rtn = ne_ssl_clicert_decrypt ( clicert, clientCerts->certificatePasswd );
if ( !rtn ) {
DEBUG_WEBDAV ( "Certificate was deciphered successfully." );
ne_ssl_set_clicert ( ctx->dav_session.ctx, clicert );
} else {
DEBUG_WEBDAV ( "Errors while deciphering certificate: %s", ne_get_error ( ctx->dav_session.ctx ) );
}
}
}
} else {
DEBUG_WEBDAV("dav_connect: error with csync_client_certs_s* clientCerts");
}
ne_ssl_trust_default_ca( ctx->dav_session.ctx );
ne_ssl_set_verify( ctx->dav_session.ctx, ssl_callback_by_neon, ctx);
}
/* Hook called when a request is created. It sets the proxy connection header. */
ne_hook_create_request( ctx->dav_session.ctx, request_created_hook, ctx );
/* Hook called after response headers are read. It gets the Session ID. */
ne_hook_post_headers( ctx->dav_session.ctx, post_request_hook, ctx );
/* Hook called before a request is sent. It sets the cookies. */
ne_hook_pre_send( ctx->dav_session.ctx, pre_send_hook, ctx );
/* Hook called after request is dispatched. Used for handling possible redirections. */
ne_hook_post_send( ctx->dav_session.ctx, post_send_hook, ctx );
/* Proxy support */
proxystate = configureProxy( ctx, ctx->dav_session.ctx );
if( proxystate < 0 ) {
DEBUG_WEBDAV("Error: Proxy-Configuration failed.");
} else if( proxystate > 0 ) {
ne_set_proxy_auth( ctx->dav_session.ctx, proxy_authentication_callback_by_neon, ctx );
}
ctx->_connected = 1;
rc = 0;
out:
SAFE_FREE(path);
SAFE_FREE(host);
SAFE_FREE(scheme);
return rc;
}
char *owncloud_error_string(CSYNC* ctx)
{
return ctx->owncloud_context->dav_session.error_string;
}
int owncloud_commit(CSYNC* ctx) {
if (!ctx->owncloud_context) {
return 0;
}
if( ctx->owncloud_context->dav_session.ctx ) {
ne_forget_auth(ctx->owncloud_context->dav_session.ctx);
ne_session_destroy(ctx->owncloud_context->dav_session.ctx );
ctx->owncloud_context->dav_session.ctx = 0;
}
/* DEBUG_WEBDAV( "********** vio_module_shutdown" ); */
ctx->owncloud_context->dav_session.ctx = 0;
// ne_sock_exit();
ctx->owncloud_context->_connected = 0; /* triggers dav_connect to go through the whole neon setup */
SAFE_FREE( ctx->owncloud_context->dav_session.user );
SAFE_FREE( ctx->owncloud_context->dav_session.pwd );
SAFE_FREE( ctx->owncloud_context->dav_session.session_key);
SAFE_FREE( ctx->owncloud_context->dav_session.error_string );
return 0;
}
void owncloud_destroy(CSYNC* ctx)
{
owncloud_commit(ctx);
SAFE_FREE(ctx->owncloud_context);
if (ctx->clientCerts) {
SAFE_FREE(ctx->clientCerts->certificatePasswd);
SAFE_FREE(ctx->clientCerts->certificatePath);
SAFE_FREE(ctx->clientCerts);
}
ne_sock_exit();
}
int owncloud_set_property(CSYNC* ctx, const char *key, void *data) {
#define READ_STRING_PROPERTY(P) \
if (c_streq(key, #P)) { \
SAFE_FREE(ctx->owncloud_context->dav_session.P); \
ctx->owncloud_context->dav_session.P = c_strdup((const char*)data); \
return 0; \
}
READ_STRING_PROPERTY(session_key)
READ_STRING_PROPERTY(proxy_type)
READ_STRING_PROPERTY(proxy_host)
READ_STRING_PROPERTY(proxy_user)
READ_STRING_PROPERTY(proxy_pwd)
#undef READ_STRING_PROPERTY
if (c_streq(key, "proxy_port")) {
ctx->owncloud_context->dav_session.proxy_port = *(int*)(data);
return 0;
}
if (c_streq(key, "read_timeout") || c_streq(key, "timeout")) {
ctx->owncloud_context->dav_session.read_timeout = *(int*)(data);
return 0;
}
if( c_streq(key, "get_dav_session")) {
/* Give the ne_session to the caller */
*(ne_session**)data = ctx->owncloud_context->dav_session.ctx;
return 0;
}
if( c_streq(key, "redirect_callback")) {
if (data) {
csync_owncloud_redirect_callback_t* cb_wrapper = data;
ctx->owncloud_context->dav_session.redir_callback = *cb_wrapper;
} else {
ctx->owncloud_context->dav_session.redir_callback = NULL;
}
}
if( c_streq(key, "SSLClientCerts")) {
if(ctx->clientCerts != NULL) {
SAFE_FREE(ctx->clientCerts->certificatePasswd);
SAFE_FREE(ctx->clientCerts->certificatePath);
SAFE_FREE(ctx->clientCerts);
ctx->clientCerts = NULL;
}
if (data) {
struct csync_client_certs_s* clientCerts = (struct csync_client_certs_s*) data;
struct csync_client_certs_s* newCerts = c_malloc(sizeof(struct csync_client_certs_s));
newCerts->certificatePath = c_strdup(clientCerts->certificatePath);
newCerts->certificatePasswd = c_strdup(clientCerts->certificatePasswd);
ctx->clientCerts = newCerts;
} else {
DEBUG_WEBDAV("error: in owncloud_set_property for 'SSLClientCerts'" );
}
}
return -1;
}
void owncloud_init(CSYNC* ctx) {
ne_sock_init();
ctx->owncloud_context = c_malloc( sizeof( struct csync_owncloud_ctx_s ));
ctx->owncloud_context->csync_ctx = ctx; // back reference
}
/* vim: set ts=4 sw=4 et cindent: */
-36
Ver Arquivo
@@ -1,36 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CSYNC_OWNCLOUD_H
#define CSYNC_OWNCLOUD_H
#include "csync.h"
#include "vio/csync_vio.h"
// Public API used by csync
int owncloud_commit(CSYNC* ctx);
void owncloud_destroy(CSYNC* ctx);
char *owncloud_error_string(CSYNC* ctx);
int owncloud_set_property(CSYNC* ctx, const char *key, void *data);
void owncloud_init(CSYNC* ctx);
int dav_connect(CSYNC* ctx, const char *base_url);
#endif /* CSYNC_OWNCLOUD_H */
-115
Ver Arquivo
@@ -1,115 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef CSYNC_OWNCLOUD_PRIVATE_H
#define CSYNC_OWNCLOUD_PRIVATE_H
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config_csync.h"
#ifdef NEON_WITH_LFS /* Switch on LFS in libneon. Never remove the NE_LFS! */
#define NE_LFS
#endif
#include <neon/ne_basic.h>
#include <neon/ne_socket.h>
#include <neon/ne_session.h>
#include <neon/ne_request.h>
#include <neon/ne_props.h>
#include <neon/ne_auth.h>
#include <neon/ne_dates.h>
#include <neon/ne_compress.h>
#include <neon/ne_redirect.h>
#include "c_rbtree.h"
#include "c_lib.h"
#include "csync.h"
#include "csync_misc.h"
#include "csync_macros.h"
#include "c_private.h"
#include "vio/csync_vio.h"
#include "csync_log.h"
#include "csync_owncloud.h"
#define DEBUG_WEBDAV(...) csync_log( 9, "oc_module", __VA_ARGS__);
typedef int (*csync_owncloud_redirect_callback_t)(CSYNC* ctx, const char* uri);
/* Struct with the WebDAV session */
struct dav_session_s {
ne_session *ctx;
char *user;
char *pwd;
char *proxy_type;
char *proxy_host;
int proxy_port;
char *proxy_user;
char *proxy_pwd;
char *session_key;
char *error_string;
int read_timeout;
csync_owncloud_redirect_callback_t redir_callback;
};
struct csync_owncloud_ctx_s {
CSYNC *csync_ctx;
// For the WebDAV connection
struct dav_session_s dav_session; /* The DAV Session, initialised in dav_connect */
int _connected; /* flag to indicate if a connection exists, ie.
the dav_session is valid */
};
typedef struct csync_owncloud_ctx_s csync_owncloud_ctx_t;
//typedef csync_owncloud_ctx_t* csync_owncloud_ctx_p;
void set_errno_from_http_errcode( int err );
void set_error_message( csync_owncloud_ctx_t *ctx, const char *msg );
void set_errno_from_neon_errcode(csync_owncloud_ctx_t *ctx, int neon_code );
int http_result_code_from_session(csync_owncloud_ctx_t *ctx);
void set_errno_from_session(csync_owncloud_ctx_t *ctx);
time_t oc_httpdate_parse( const char *date );
const char* csync_owncloud_get_platform(void);
char *_cleanPath( const char* uri );
#endif // CSYNC_OWNCLOUD_PRIVATE_H
-125
Ver Arquivo
@@ -1,125 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2011 by Andreas Schneider <asn@cryptomilk.org>
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "csync_owncloud.h"
#include "csync_owncloud_private.h"
#include "csync_misc.h"
void set_errno_from_http_errcode( int err ) {
int new_errno = 0;
switch(err) {
case 200: /* OK */
case 201: /* Created */
case 202: /* Accepted */
case 203: /* Non-Authoritative Information */
case 204: /* No Content */
case 205: /* Reset Content */
case 207: /* Multi-Status */
case 304: /* Not Modified */
new_errno = 0;
break;
case 401: /* Unauthorized */
case 402: /* Payment Required */
case 407: /* Proxy Authentication Required */
case 405:
new_errno = EPERM;
break;
case 301: /* Moved Permanently */
case 303: /* See Other */
case 404: /* Not Found */
case 410: /* Gone */
new_errno = ENOENT;
break;
case 408: /* Request Timeout */
case 504: /* Gateway Timeout */
new_errno = EAGAIN;
break;
case 423: /* Locked */
new_errno = EACCES;
break;
case 400: /* Bad Request */
case 403: /* Forbidden */
case 409: /* Conflict */
case 411: /* Length Required */
case 412: /* Precondition Failed */
case 414: /* Request-URI Too Long */
case 415: /* Unsupported Media Type */
case 424: /* Failed Dependency */
case 501: /* Not Implemented */
new_errno = EINVAL;
break;
case 507: /* Insufficient Storage */
new_errno = ENOSPC;
break;
case 206: /* Partial Content */
case 300: /* Multiple Choices */
case 302: /* Found */
case 305: /* Use Proxy */
case 306: /* (Unused) */
case 307: /* Temporary Redirect */
case 406: /* Not Acceptable */
case 416: /* Requested Range Not Satisfiable */
case 417: /* Expectation Failed */
case 422: /* Unprocessable Entity */
case 500: /* Internal Server Error */
case 502: /* Bad Gateway */
case 505: /* HTTP Version Not Supported */
new_errno = EIO;
break;
case 503: /* Service Unavailable */
new_errno = ERRNO_SERVICE_UNAVAILABLE;
break;
case 413: /* Request Entity too Large */
new_errno = EFBIG;
break;
default:
new_errno = EIO;
}
errno = new_errno;
}
// as per http://sourceforge.net/p/predef/wiki/OperatingSystems/
// extend as required
const char* csync_owncloud_get_platform() {
#if defined (_WIN32)
return "Windows";
#elif defined(__APPLE__)
return "Macintosh";
#elif defined(__gnu_linux__)
return "Linux";
#elif defined(__DragonFly__)
/* might also define __FreeBSD__ */
return "DragonFlyBSD";
#elif defined(__FreeBSD__)
return "FreeBSD";
#elif defined(__NetBSD__)
return "NetBSD";
#elif defined(__OpenBSD__)
return "OpenBSD";
#elif defined(sun) || defined(__sun)
return "Solaris";
#else
return "Unknown OS";
#endif
}
-6
Ver Arquivo
@@ -77,9 +77,6 @@ enum csync_replica_e {
typedef struct csync_file_stat_s csync_file_stat_t;
struct csync_owncloud_ctx_s; // csync_owncloud.c
/**
* @brief csync public structure
*/
@@ -169,9 +166,6 @@ struct csync_s {
bool db_is_empty;
bool ignore_hidden_files;
struct csync_owncloud_ctx_s *owncloud_context;
};
+8 -38
Ver Arquivo
@@ -416,37 +416,10 @@ csync_file_stat_t *csync_statedb_get_stat_by_inode(CSYNC *ctx,
return st;
}
/* Get the etag. */
char *csync_statedb_get_etag( CSYNC *ctx, uint64_t jHash ) {
char *ret = NULL;
csync_file_stat_t *fs = NULL;
if( !ctx ) {
return NULL;
}
if( ! csync_get_statedb_exists(ctx)) return ret;
fs = csync_statedb_get_stat_by_hash(ctx, jHash );
if( fs ) {
if( fs->etag ) {
ret = c_strdup(fs->etag);
}
csync_file_stat_free(fs);
}
return ret;
}
#define BELOW_PATH_QUERY "SELECT phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM metadata WHERE pathlen>? AND path LIKE(?)"
int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
int rc;
sqlite3_stmt *stmt = NULL;
int64_t cnt = 0;
char *likepath;
int asp;
int min_path_len;
if( !path ) {
return -1;
@@ -456,7 +429,12 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
return -1;
}
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, BELOW_PATH_QUERY, -1, &stmt, NULL));
/* Select the entries for anything that starts with (path+'/')
* In other words, anything that is between path+'/' and path+'0',
* (because '0' follows '/' in ascii)
*/
const char *below_path_query = "SELECT phash, pathlen, path, inode, uid, gid, mode, modtime, type, md5, fileid, remotePerm, filesize, ignoredChildrenRemote FROM metadata WHERE path > (?||'/') AND path < (?||'0')";
SQLITE_BUSY_HANDLED(sqlite3_prepare_v2(ctx->statedb.db, below_path_query, -1, &stmt, NULL));
ctx->statedb.lastReturnValue = rc;
if( rc != SQLITE_OK ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "WRN: Unable to create stmt for below path query.");
@@ -467,15 +445,8 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
return -1;
}
asp = asprintf( &likepath, "%s/%%%%", path);
if (asp < 0) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_ERROR, "asprintf failed!");
return -1;
}
min_path_len = strlen(path);
sqlite3_bind_int(stmt, 1, min_path_len);
sqlite3_bind_text(stmt, 2, likepath, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 1, path, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, path, -1, SQLITE_STATIC);
cnt = 0;
@@ -518,7 +489,6 @@ int csync_statedb_get_below_path( CSYNC *ctx, const char *path ) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "%" PRId64 " entries read below path %s from db.", cnt, path);
}
sqlite3_finalize(stmt);
SAFE_FREE(likepath);
return 0;
}
+13 -42
Ver Arquivo
@@ -270,10 +270,6 @@ static int _csync_detect_update(CSYNC *ctx, const char *file,
((int64_t) fs->mtime), ((int64_t) tmp->modtime),
fs->etag, tmp->etag, (uint64_t) fs->inode, (uint64_t) tmp->inode,
(uint64_t) fs->size, (uint64_t) tmp->size, fs->remotePerm, tmp->remotePerm, tmp->has_ignored_files );
if( !fs->etag) {
st->instruction = CSYNC_INSTRUCTION_EVAL;
goto out;
}
if((ctx->current == REMOTE_REPLICA && !c_streq(fs->etag, tmp->etag ))
|| (ctx->current == LOCAL_REPLICA && (!_csync_mtime_equal(fs->mtime, tmp->modtime)
// zero size in statedb can happen during migration
@@ -584,7 +580,6 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
char *d_name = NULL;
csync_vio_handle_t *dh = NULL;
csync_vio_file_stat_t *dirent = NULL;
csync_vio_file_stat_t *fs = NULL;
csync_file_stat_t *previous_fs = NULL;
int read_from_db = 0;
int rc = 0;
@@ -688,8 +683,8 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
}
/* skip "." and ".." */
if (d_name[0] == '.' && (d_name[1] == '\0'
|| (d_name[1] == '.' && d_name[2] == '\0'))) {
if ( (d_name[0] == '.' && d_name[1] == '\0')
|| (d_name[0] == '.' && d_name[1] == '.' && d_name[2] == '\0')) {
csync_vio_file_stat_destroy(dirent);
dirent = NULL;
continue;
@@ -741,15 +736,21 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
/* Only for the local replica we have to stat(), for the remote one we have all data already */
if (ctx->replica == LOCAL_REPLICA) {
fs = csync_vio_file_stat_new();
res = csync_vio_stat(ctx, filename, fs);
res = csync_vio_stat(ctx, filename, dirent);
} else {
fs = dirent;
res = 0;
}
/* if the filename starts with a . we consider it a hidden file
* For windows, the hidden state is also discovered within the vio
* local stat function.
*/
if( d_name[0] == '.' ) {
dirent->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
if( res == 0) {
switch (fs->type) {
switch (dirent->type) {
case CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK:
flag = CSYNC_FTW_FLAG_SLINK;
break;
@@ -772,42 +773,12 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
flag = CSYNC_FTW_FLAG_NSTAT;
}
if( ctx->current == LOCAL_REPLICA ) {
char *etag = NULL;
int len = strlen( path );
uint64_t h = c_jhash64((uint8_t *) path, len, 0);
etag = csync_statedb_get_etag( ctx, h );
if(_last_db_return_error(ctx)) {
ctx->status_code = CSYNC_STATUS_UNSUCCESSFUL;
SAFE_FREE(etag);
goto error;
}
if( etag ) {
SAFE_FREE(fs->etag);
fs->etag = etag;
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ETAG;
if( c_streq(etag, "")) {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Uniq ID from Database is EMPTY: %s", path);
} else {
CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Uniq ID from Database: %s -> %s", path, fs->etag ? fs->etag : "<NULL>" );
}
}
}
previous_fs = ctx->current_fs;
/* Call walker function for each file */
rc = fn(ctx, filename, fs, flag);
rc = fn(ctx, filename, dirent, flag);
/* this function may update ctx->current and ctx->read_from_db */
/* Only for the local replica we have to destroy stat(), for the remote one it is a pointer to dirent */
if (ctx->replica == LOCAL_REPLICA) {
csync_vio_file_stat_destroy(fs);
}
if (rc < 0) {
if (CSYNC_STATUS_IS_OK(ctx->status_code)) {
ctx->status_code = CSYNC_STATUS_UPDATE_ERROR;
-57
Ver Arquivo
@@ -1,57 +0,0 @@
project(httpbflib C)
find_package(Neon)
set(HTTPBF_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/src
${NEON_INCLUDE_DIRS}
CACHE INTERNAL "httpbflib public include directories"
)
set(HTTPBF_LIBRARY
httpbf
CACHE INTERNAL "httpbf library"
)
set(HTTPBF_LINK_LIBRARIES
${HTTPBF_LIBRARY}
)
set(httpbf_SRCS
src/httpbf.c
)
set(httpbf_HEADERS
src/httpbf.h
)
include_directories(
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
add_library(${HTTPBF_LIBRARY} STATIC ${httpbf_SRCS})
target_link_libraries(${HTTPBF_LIBRARY} ${NEON_LIBRARIES})
if(NOT WIN32)
add_definitions( -fPIC )
endif()
INSTALL(
TARGETS
${HTTPBF_LIBRARY}
LIBRARY DESTINATION
${LIB_INSTALL_DIR}
ARCHIVE DESTINATION
${LIB_INSTALL_DIR}
RUNTIME DESTINATION
${BIN_INSTALL_DIR}
)
if (NOT APPLE)
INSTALL(
FILES
${httpbf_HEADERS}
DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}
)
endif (NOT APPLE)
-29
Ver Arquivo
@@ -1,29 +0,0 @@
This is a little code that does ownCloud file chunking.
Basically to put a local file to an url:
(Also see the client example code in client dir.)
/* Initialize the transfer, get a transfer struct. */
hbf_transfer_t *trans = hbf_init_transfer( url );
Hbf_State state;
if( trans ) {
int fd = open_local_file( file );
/* create a neon session to use for the transfer */
ne_session *session = create_neon_session(uri);
if( session && fd > -1 ) {
/* Prepare the list of chunks, ie. calculate chunks and write back to trans. */
state = hbf_splitlist(trans, fd);
if( state == HBF_SUCCESS ) {
/* Transfer all the chunks through the HTTP session using PUT. */
state = hbf_transfer( session, trans, "PUT" );
}
}
}
GET a large file:
Do GET Range requests.
-36
Ver Arquivo
@@ -1,36 +0,0 @@
project(client C)
set(CLIENT_EXECUTABLE httpbfclient CACHE INTERNAL "httpbf client")
set(CLIENT_LINK_LIBRARIES ${NEON_LIBRARIES} ${HBF_LIBRARY})
set(HTTPBF_INCLUDE_DIR ${CMAKE_SOURCE_DIR}/src})
if(NOT LINUX)
list(APPEND CLIENT_LINK_LIBRARIES ${ARGP_LIBRARIES})
endif()
set(client_SRCS
httpbf_client.c
)
include_directories(
${HTTPBF_INCLUDE_DIR}
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
add_executable(${CLIENT_EXECUTABLE} ${client_SRCS})
target_link_libraries(${CLIENT_EXECUTABLE} ${CLIENT_LINK_LIBRARIES})
set_target_properties(
${CLIENT_EXECUTABLE}
PROPERTIES
OUTPUT_NAME
httpbf
)
# install( TARGETS ${CLIENT_EXECUTABLE} DESTINATION ${BIN_INSTALL_DIR} )
install(TARGETS ${CLIENT_EXECUTABLE} DESTINATION bin)
-225
Ver Arquivo
@@ -1,225 +0,0 @@
/*
* httpbf - send big files via http
*
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <neon/ne_auth.h>
#include "httpbf.h"
/* Program documentation. */
static char doc[] = "Usage: httpbf [OPTION...] LOCAL REMOTEDIR\n\
httpbf - command line client to upload big files via http.\n\
\n\
Transfer a big file to a remote directory on ownCloud.\n\
\n\
-?, --help Give this help list\n\
--usage Give a short usage message\n\
-V, --version Print program version\n\
";
static char _user[NE_ABUFSIZ];
static char _pwd[NE_ABUFSIZ];
/* The options we understand. */
static const struct option long_options[] =
{
{"version", no_argument, 0, 'V' },
{"usage", no_argument, 0, 'h' },
{0, 0, 0, 0}
};
static const char* httpbf_version = "0.1";
static void print_version()
{
printf( "%s\n", httpbf_version );
exit(0);
}
static void print_help()
{
printf( "%s\n", doc );
exit(0);
}
static int ne_auth( void *userdata, const char *realm, int attempt,
char *username, char *password)
{
(void) userdata;
(void) realm;
if( username && password ) {
if( _user ) {
/* allow user without password */
if( strlen( _user ) < NE_ABUFSIZ ) {
strcpy( username, _user );
}
if( _pwd && strlen( _pwd ) < NE_ABUFSIZ ) {
strcpy( password, _pwd );
}
}
}
return attempt;
}
static int parse_args(int argc, char **argv)
{
while(optind < argc) {
int c = -1;
struct option *opt = NULL;
int result = getopt_long( argc, argv, "Vh", long_options, &c );
if( result == -1 ) {
break;
}
switch(result) {
case 'V':
print_version();
break;
case 'h':
print_help();
break;
case 0:
opt = (struct option*)&(long_options[c]);
if(strcmp(opt->name, "no-name-yet")) {
} else {
fprintf(stderr, "Argument: No idea what!\n");
}
break;
default:
break;
}
}
return optind;
}
static ne_session* create_neon_session( const char *url )
{
ne_uri uri;
ne_session *sess = NULL;
memset( _user, 0, NE_ABUFSIZ );
memset( _pwd, 0, NE_ABUFSIZ );
if( ne_uri_parse( url, &uri ) == 0 ) {
unsigned int port = ne_uri_defaultport(uri.scheme);
if( uri.userinfo ) {
char *slash = NULL;
strcpy( _user, uri.userinfo );
slash = strchr( _user, ':');
if( slash ) {
strcpy( _pwd, slash+1);
*slash = 0;
}
}
sess = ne_session_create(uri.scheme, uri.host, port);
ne_set_server_auth(sess, ne_auth, 0 );
ne_uri_free(&uri);
}
return sess;
}
static int open_local_file( const char *file )
{
int fd = -1;
if( !file ) return -1;
fd = open(file, O_RDONLY);
return fd;
}
static void transfer( const char* local, const char* uri )
{
if( !(local && uri )) return;
char *whole_url;
int len;
char *filename = basename(local);
if( ! filename ) {
return;
}
len = strlen(filename)+strlen(uri)+2;
whole_url = malloc( len );
strcpy(whole_url, uri);
strcat(whole_url, "/");
strcat(whole_url, filename);
hbf_transfer_t *trans = hbf_init_transfer( whole_url );
Hbf_State state;
if( trans ) {
ne_session *session = create_neon_session(uri);
if( session ) {
int fd = open_local_file( local );
if( fd > -1 ) {
state = hbf_splitlist(trans, fd );
if( state == HBF_SUCCESS ) {
state = hbf_transfer( session, trans, "PUT" );
}
}
ne_session_destroy(session);
}
}
if( state != HBF_SUCCESS ) {
printf("Upload failed: %s\n", hbf_error_string(state));
printf(" HTTP result %d, Server Error: %s\n",
trans->status_code, trans->error_string ? trans->error_string : "<empty>" );
}
/* Print the result of the recent transfer */
hbf_free_transfer( trans );
free(whole_url);
}
int main(int argc, char **argv) {
int rc = 0;
char errbuf[256] = {0};
parse_args(argc, argv);
/* two options must remain as source and target */
/* printf("ARGC: %d -> optind: %d\n", argc, optind ); */
if( argc - optind < 2 ) {
print_help();
}
transfer( argv[optind], argv[optind+1]);
}
/* vim: set ts=8 sw=2 et cindent: */
-38
Ver Arquivo
@@ -1,38 +0,0 @@
project(httpbf C)
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked
set(HTTPBF_PUBLIC_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
CACHE INTERNAL "httpbf public include directories"
)
set(HTTPBFLIB_PRIVATE_INCLUDE_DIRS
)
set(HBF_LIBRARY
httpbf
CACHE INTERNAL "httpbflib library"
)
set(HTTPBFLIB_LINK_LIBRARIES
${HBF_LIBRARY}
)
set(httpbflib_SRCS
httpbf.c
)
include_directories(
${NEON_INCLUDE_DIRS}
${GLIB2_INCLUDE_DIRS}
)
if(NOT WIN32)
add_definitions( -fPIC )
endif()
add_library(${HBF_LIBRARY} SHARED ${httpbflib_SRCS} )
target_link_libraries(${HBF_LIBRARY} ${NEON_LIBRARIES})
-688
Ver Arquivo
@@ -1,688 +0,0 @@
/*
* httpbf - send big files via http
*
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/timeb.h>
#include <sys/time.h>
#include <inttypes.h>
#include "httpbf.h"
#include <neon/ne_session.h>
#include <neon/ne_request.h>
#include <neon/ne_basic.h>
// #ifdef NDEBUG
// #define DEBUG_HBF(...)
// #else
#define DEBUG_HBF(...) { if(transfer->log_cb) { \
char buf[1024]; \
snprintf(buf, 1024, __VA_ARGS__); \
transfer->log_cb(__func__, buf, transfer->user_data); \
} }
// #endif
#define DEFAULT_BLOCK_SIZE (10*1024*1024)
/* Platform specific defines go here. */
#ifdef _WIN32
#define _hbf_fstat _fstat64
typedef struct stat64 hbf_stat_t;
#else
#define _hbf_fstat fstat
typedef struct stat hbf_stat_t;
#endif
static int transfer_id( hbf_stat_t *sb ) {
struct timeval tp;
int res;
int r;
if( gettimeofday(&tp, 0) < 0 ) {
return 0;
}
/* build a Unique ID:
* take the current epoch and shift 8 bits up to keep the least bits.
* than add the milliseconds, again shift by 8
* and finally add the least 8 bit of the inode of the file.
*/
res = tp.tv_sec; /* epoche value in seconds */
res = res << 8;
r = (sb->st_ino & 0xFF);
res += r; /* least six bit of inode */
res = res << sizeof(tp.tv_usec);
res += tp.tv_usec; /* milliseconds */
return res;
}
hbf_transfer_t *hbf_init_transfer( const char *dest_uri ) {
hbf_transfer_t * transfer = NULL;
transfer = malloc( sizeof(hbf_transfer_t) );
memset(transfer, 0, sizeof(hbf_transfer_t));
/* store the target uri */
transfer->url = strdup(dest_uri);
transfer->status_code = 200;
transfer->error_string = NULL;
transfer->start_id = 0;
transfer->block_size = DEFAULT_BLOCK_SIZE;
transfer->threshold = transfer->block_size;
transfer->modtime_accepted = 0;
transfer->oc_header_modtime = 0;
return transfer;
}
/* Create the splitlist of a given file descriptor */
Hbf_State hbf_splitlist(hbf_transfer_t *transfer, int fd ) {
hbf_stat_t sb;
int64_t num_blocks;
int64_t blk_size;
int64_t remainder = 0;
if( ! transfer ) {
return HBF_PARAM_FAIL;
}
if( fd <= 0 ) {
DEBUG_HBF("File descriptor is invalid.");
return HBF_PARAM_FAIL;
}
if( _hbf_fstat(fd, &sb) < 0 ) {
DEBUG_HBF("Failed to stat the file descriptor: errno = %d", errno);
return HBF_FILESTAT_FAIL;
}
/* Store the file characteristics. */
transfer->fd = fd;
transfer->stat_size = sb.st_size;
transfer->modtime = sb.st_mtime;
transfer->previous_etag = NULL;
#ifndef NDEBUG
transfer->calc_size = 0;
#endif
DEBUG_HBF("block_size: %" PRId64 " threshold: %" PRId64 " st_size: %" PRId64, transfer->block_size, transfer->threshold, sb.st_size );
/* calc the number of blocks to split in */
blk_size = transfer->block_size;
if (sb.st_size < transfer->threshold) {
blk_size = transfer->threshold;
}
num_blocks = sb.st_size / blk_size;
/* there migth be a remainder. */
remainder = sb.st_size - num_blocks * blk_size;
/* if there is a remainder, add one block */
if( remainder > 0 ) {
num_blocks++;
}
/* The file has size 0. There still needs to be at least one block. */
if( sb.st_size == 0 ) {
num_blocks = 1;
blk_size = 0;
}
DEBUG_HBF("num_blocks: %" PRId64 " rmainder: %" PRId64 " blk_size: %" PRId64, num_blocks, remainder, blk_size );
if( num_blocks ) {
int cnt;
int64_t overall = 0;
/* create a datastructure for the transfer data */
transfer->block_arr = calloc(num_blocks, sizeof(hbf_block_t*));
transfer->block_cnt = num_blocks;
transfer->transfer_id = transfer_id(&sb);
transfer->start_id = 0;
for( cnt=0; cnt < num_blocks; cnt++ ) {
/* allocate a block struct and fill */
hbf_block_t *block = malloc( sizeof(hbf_block_t) );
memset(block, 0, sizeof(hbf_block_t));
block->seq_number = cnt;
if( cnt > 0 ) {
block->start = cnt * blk_size;
}
block->size = blk_size;
block->state = HBF_NOT_TRANSFERED;
/* consider the remainder if we're already at the end */
if( cnt == num_blocks-1 && remainder > 0 ) {
block->size = remainder;
}
overall += block->size;
/* store the block data into the result array in the transfer */
*((transfer->block_arr)+cnt) = block;
DEBUG_HBF("created block %d (start: %" PRId64 " size: %" PRId64 ")", cnt, block->start, block->size );
}
#ifndef NDEBUG
transfer->calc_size = overall;
#endif
}
return HBF_SUCCESS;
}
void hbf_free_transfer( hbf_transfer_t *transfer ) {
int cnt;
if( !transfer ) return;
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
hbf_block_t *block = transfer->block_arr[cnt];
if( !block ) continue;
if( block->http_error_msg ) free( block->http_error_msg );
if( block->etag ) free( block->etag );
}
free( transfer->block_arr );
free( transfer->url );
free( transfer->file_id );
if( transfer->error_string) free( (void*) transfer->error_string );
free( transfer );
}
static char* get_transfer_url( hbf_transfer_t *transfer, int indx ) {
char *res = NULL;
hbf_block_t *block = NULL;
if( ! transfer ) return NULL;
if( indx >= transfer->block_cnt ) return NULL;
block = transfer->block_arr[indx];
if( ! block ) return NULL;
if( transfer->block_cnt == 1 ) {
/* Just one chunk. We send as an ordinary request without chunking. */
res = strdup( transfer->url );
} else {
char trans_id_str[32];
char trans_block_str[32];
char indx_str[32];
int len = 1; /* trailing zero. */
int tlen = 0;
tlen = sprintf( trans_id_str, "%u", transfer->transfer_id );
if( tlen < 0 ) {
return NULL;
}
len += tlen;
tlen = sprintf( trans_block_str, "%u", transfer->block_cnt );
if( tlen < 0 ) {
return NULL;
}
len += tlen;
tlen = sprintf( indx_str, "%u", indx );
if( tlen < 0 ) {
return NULL;
}
len += tlen;
len += strlen(transfer->url);
len += strlen("-chunking---");
res = malloc(len);
/* Note: must be %u for unsigned because one does not want '--' */
if( sprintf(res, "%s-chunking-%u-%u-%u", transfer->url, transfer->transfer_id,
transfer->block_cnt, indx ) < 0 ) {
return NULL;
}
}
return res;
}
/*
* perform one transfer of one block.
* returns HBF_TRANSFER_SUCCESS if the transfer of this block was a success
* returns HBF_SUCCESS if the server aknoweldge that he received all the blocks
*/
static int _hbf_dav_request(hbf_transfer_t *transfer, ne_request *req, int fd, hbf_block_t *blk ) {
Hbf_State state = HBF_TRANSFER_SUCCESS;
int res;
const ne_status *req_status = NULL;
const char *etag = NULL;
(void) transfer;
if( ! (blk && req) ) return HBF_PARAM_FAIL;
ne_set_request_body_fd(req, fd, blk->start, blk->size);
DEBUG_HBF("Block: %d , Start: %" PRId64 " and Size: %" PRId64 "", blk->seq_number, blk->start, blk->size );
res = ne_request_dispatch(req);
req_status = ne_get_status( req );
switch(res) {
case NE_OK:
blk->state = HBF_TRANSFER_FAILED;
state = HBF_FAIL;
etag = 0;
if( req_status->klass == 2 ) {
state = HBF_TRANSFER_SUCCESS;
blk->state = HBF_TRANSFER_SUCCESS;
etag = ne_get_response_header(req, "ETag");
if (etag && etag[0]) {
/* When there is an etag, it means the transfer was complete */
state = HBF_SUCCESS;
if( etag[0] == '"' && etag[ strlen(etag)-1] == '"') {
int len = strlen( etag )-2;
blk->etag = malloc( len+1 );
strncpy( blk->etag, etag+1, len );
blk->etag[len] = '\0';
} else {
blk->etag = strdup( etag );
}
} else {
/* DEBUG_HBF("OOOOOOOO No etag returned!"); */
}
/* check if the server was able to set the mtime already. */
etag = ne_get_response_header(req, "X-OC-MTime");
if( etag && strcmp(etag, "accepted") == 0 ) {
/* the server acknowledged that the mtime was set. */
transfer->modtime_accepted = 1;
}
etag = ne_get_response_header(req, "OC-FileID");
if( etag ) {
transfer->file_id = strdup( etag );
}
}
break;
case NE_AUTH:
state = HBF_AUTH_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_PROXYAUTH:
state = HBF_PROXY_AUTH_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_CONNECT:
state = HBF_CONNECT_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_TIMEOUT:
state = HBF_TIMEOUT_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
case NE_ERROR:
state = HBF_FAIL;
blk->state = HBF_TRANSFER_FAILED;
break;
}
blk->http_result_code = req_status->code;
if( req_status->reason_phrase ) {
blk->http_error_msg = strdup(req_status->reason_phrase);
}
return state;
}
Hbf_State hbf_validate_source_file( hbf_transfer_t *transfer ) {
Hbf_State state = HBF_SUCCESS;
hbf_stat_t sb;
if( transfer == NULL ) {
state = HBF_PARAM_FAIL;
}
if( state == HBF_SUCCESS ) {
if( transfer->fd <= 0 ) {
state = HBF_PARAM_FAIL;
}
}
if( state == HBF_SUCCESS ) {
int rc = _hbf_fstat( transfer->fd, &sb );
if( rc != 0 ) {
state = HBF_STAT_FAIL;
}
}
if( state == HBF_SUCCESS ) {
if( sb.st_mtime != transfer->modtime || sb.st_size != transfer->stat_size ) {
state = HBF_SOURCE_FILE_CHANGE;
}
}
return state;
}
/* Get the HTTP error code for the last request */
static int _hbf_http_error_code(ne_session *session) {
const char *msg = ne_get_error( session );
char *msg2;
int err;
err = strtol(msg, &msg2, 10);
if (msg == msg2) {
err = 0;
}
return err;
}
static Hbf_State _hbf_transfer_no_chunk(ne_session *session, hbf_transfer_t *transfer, const char *verb) {
int res;
const ne_status* req_status;
ne_request *req = ne_request_create(session, verb ? verb : "PUT", transfer->url);
if (!req)
return HBF_MEMORY_FAIL;
ne_add_request_header( req, "Content-Type", "application/octet-stream");
ne_set_request_body_fd(req, transfer->fd, 0, transfer->stat_size);
DEBUG_HBF("HBF: chunking not supported for %s", transfer->url);
res = ne_request_dispatch(req);
req_status = ne_get_status( req );
if (res == NE_OK && req_status->klass == 2) {
ne_request_destroy(req);
return HBF_SUCCESS;
}
if( transfer->error_string ) free( transfer->error_string );
transfer->error_string = strdup( ne_get_error(session) );
transfer->status_code = req_status->code;
ne_request_destroy(req);
return HBF_FAIL;
}
Hbf_State hbf_transfer( ne_session *session, hbf_transfer_t *transfer, const char *verb ) {
Hbf_State state = HBF_TRANSFER_SUCCESS;
int cnt;
if( ! session ) {
state = HBF_SESSION_FAIL;
}
if( ! transfer ) {
state = HBF_SPLITLIST_FAIL;
}
if( ! verb ) {
state = HBF_PARAM_FAIL;
}
if(state == HBF_TRANSFER_SUCCESS) {
DEBUG_HBF("%s request to %s", verb, transfer->url);
}
for( cnt=0; state == HBF_TRANSFER_SUCCESS && cnt < transfer->block_cnt; cnt++ ) {
/* cnt goes from O to block_cnt, but block_id starts at start_id and wrap around
* That way if we have not finished uploaded when we reach block_cnt, we re-upload
* the beginning of the file that the server did not have in cache anymore.
*/
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
char *transfer_url = NULL;
if( ! block ) state = HBF_PARAM_FAIL;
if( transfer->abort_cb ) {
int do_abort = (transfer->abort_cb)(transfer->user_data);
if( do_abort ) {
state = HBF_USER_ABORTED;
transfer->start_id = block_id % transfer->block_cnt;
}
}
if( state == HBF_TRANSFER_SUCCESS ) {
transfer_url = get_transfer_url( transfer, block_id );
if( ! transfer_url ) {
state = HBF_PARAM_FAIL;
}
}
if( state == HBF_TRANSFER_SUCCESS ) {
if( transfer->block_cnt > 1 && cnt > 0 ) {
/* The block count is > 1, check size and mtime before transmitting. */
state = hbf_validate_source_file(transfer);
if( state == HBF_SOURCE_FILE_CHANGE ) {
/* The source file has changed meanwhile */
}
}
}
if( state == HBF_TRANSFER_SUCCESS || state == HBF_SUCCESS ) {
ne_request *req = ne_request_create(session, verb, transfer_url);
if( req ) {
char buf[21];
snprintf(buf, sizeof(buf), "%"PRId64, transfer->stat_size);
ne_add_request_header(req, "OC-Total-Length", buf);
if( transfer->oc_header_modtime > 0 ) {
snprintf(buf, sizeof(buf), "%"PRId64, transfer->oc_header_modtime);
ne_add_request_header(req, "X-OC-Mtime", buf);
}
if( transfer->previous_etag ) {
ne_add_request_header(req, "If-Match", transfer->previous_etag);
}
if( transfer->block_cnt > 1 ) {
ne_add_request_header(req, "OC-Chunked", "1");
snprintf(buf, sizeof(buf), "%"PRId64, transfer->threshold);
ne_add_request_header(req, "OC-Chunk-Size", buf);
}
ne_add_request_header( req, "Content-Type", "application/octet-stream");
state = _hbf_dav_request(transfer, req, transfer->fd, block );
if( state != HBF_TRANSFER_SUCCESS && state != HBF_SUCCESS) {
if( transfer->error_string ) free( transfer->error_string );
transfer->error_string = strdup( ne_get_error(session) );
transfer->start_id = block_id % transfer->block_cnt;
/* Set the code of the last transmission. */
state = HBF_FAIL;
transfer->status_code = transfer->block_arr[block_id]->http_result_code;
}
ne_request_destroy(req);
if (transfer->block_cnt > 1 && state == HBF_SUCCESS && cnt == 0) {
/* Success on the first chunk is suspicious.
It could happen that the server did not support chunking */
int rc = ne_delete(session, transfer_url);
if (rc == NE_OK && _hbf_http_error_code(session) == 204) {
/* If delete suceeded, it means some proxy strips the OC_CHUNKING header
start again without chunking: */
free( transfer_url );
return _hbf_transfer_no_chunk(session, transfer, verb);
}
}
if (state == HBF_TRANSFER_SUCCESS && transfer->chunk_finished_cb) {
transfer->chunk_finished_cb(transfer, block_id, transfer->user_data);
}
} else {
state = HBF_MEMORY_FAIL;
}
}
free( transfer_url );
}
/* do the source file validation finally (again). */
if( state == HBF_TRANSFER_SUCCESS ) {
/* This means that no etag was returned on one of the chunks to indicate
* that the upload was finished. */
state = HBF_TRANSFER_NOT_ACKED;
}
return state;
}
int hbf_fail_http_code( hbf_transfer_t *transfer )
{
int cnt;
if( ! transfer ) return 0;
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
if( block->state != HBF_NOT_TRANSFERED && block->state != HBF_TRANSFER_SUCCESS ) {
return block->http_result_code;
}
}
return 200;
}
const char *hbf_transfer_etag( hbf_transfer_t *transfer )
{
int cnt;
const char *etag = NULL;
if( ! transfer ) return 0;
/* Loop over all parts and do a assertion that there is only one etag. */
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
if( block->etag ) {
if( etag && strcmp(etag, block->etag) != 0 ) {
/* multiple etags in the transfer, not equal. */
DEBUG_HBF( "WARN: etags are not equal in blocks of one single transfer." );
}
etag = block->etag;
}
}
return etag;
}
const char *hbf_transfer_file_id( hbf_transfer_t *transfer )
{
const char *re = NULL;
if(transfer) {
re = transfer->file_id;
}
return re;
}
const char *hbf_error_string(hbf_transfer_t *transfer, Hbf_State state)
{
const char *re;
int cnt;
switch( state ) {
case HBF_SUCCESS:
re = "Ok.";
break;
case HBF_NOT_TRANSFERED: /* never tried to transfer */
re = "Block was not yet tried to transfer.";
break;
case HBF_TRANSFER: /* transfer currently running */
re = "Block is currently transferred.";
break;
case HBF_TRANSFER_FAILED: /* transfer tried but failed */
re = "Block transfer failed.";
break;
case HBF_TRANSFER_SUCCESS: /* transfer succeeded. */
re = "Block transfer successful.";
break;
case HBF_SPLITLIST_FAIL: /* the file could not be split */
re = "Splitlist could not be computed.";
break;
case HBF_SESSION_FAIL:
re = "No valid session in transfer.";
break;
case HBF_FILESTAT_FAIL:
re = "Source file could not be stat'ed.";
break;
case HBF_PARAM_FAIL:
re = "Parameter fail.";
break;
case HBF_AUTH_FAIL:
re = "Authentication fail.";
break;
case HBF_PROXY_AUTH_FAIL:
re = "Proxy Authentication fail.";
break;
case HBF_CONNECT_FAIL:
re = "Connection could not be established.";
break;
case HBF_TIMEOUT_FAIL:
re = "Network timeout.";
break;
case HBF_MEMORY_FAIL:
re = "Out of memory.";
break;
case HBF_STAT_FAIL:
re = "Filesystem stat on file failed.";
break;
case HBF_SOURCE_FILE_CHANGE:
re = "Source file changed too often during upload.";
break;
case HBF_USER_ABORTED:
re = "Transmission aborted by user.";
break;
case HBF_TRANSFER_NOT_ACKED:
re = "The server did not provide an Etag.";
break;
case HBF_FAIL:
default:
for( cnt = 0; cnt < transfer->block_cnt; cnt++ ) {
int block_id = (cnt + transfer->start_id) % transfer->block_cnt;
hbf_block_t *block = transfer->block_arr[block_id];
if( block->state != HBF_NOT_TRANSFERED && block->state != HBF_TRANSFER_SUCCESS
&& block->http_error_msg != NULL) {
return block->http_error_msg;
}
}
re = "Unknown error.";
}
return re;
}
void hbf_set_abort_callback( hbf_transfer_t *transfer, hbf_abort_callback cb)
{
if( transfer ) {
transfer->abort_cb = cb;
}
}
void hbf_set_log_callback(hbf_transfer_t* transfer, hbf_log_callback cb)
{
if( transfer ) {
transfer->log_cb = cb;
}
}
-142
Ver Arquivo
@@ -1,142 +0,0 @@
/**
* http big file functions
*
* Copyright (c) 2012 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HBF_SEND_H
#define _HBF_SEND_H
#include "config_csync.h"
#ifdef NEON_WITH_LFS /* Switch on LFS in libneon. Never remove the NE_LFS! */
#define NE_LFS
#endif
#include <neon/ne_session.h>
#ifdef __cplusplus
extern "C" {
#endif
enum hbf_state_e {
HBF_SUCCESS,
HBF_NOT_TRANSFERED, /* never tried to transfer */
HBF_TRANSFER, /* transfer currently running */
HBF_TRANSFER_FAILED, /* transfer tried but failed */
HBF_TRANSFER_SUCCESS, /* block transfer succeeded. */
HBF_SPLITLIST_FAIL, /* the file could not be split */
HBF_SESSION_FAIL,
HBF_FILESTAT_FAIL,
HBF_PARAM_FAIL,
HBF_AUTH_FAIL,
HBF_PROXY_AUTH_FAIL,
HBF_CONNECT_FAIL,
HBF_TIMEOUT_FAIL,
HBF_MEMORY_FAIL,
HBF_STAT_FAIL,
HBF_SOURCE_FILE_CHANGE,
HBF_USER_ABORTED,
HBF_TRANSFER_NOT_ACKED,
HBF_FAIL
};
typedef enum hbf_state_e Hbf_State;
typedef struct hbf_block_s hbf_block_t;
struct hbf_block_s {
int seq_number;
int64_t start;
int64_t size;
Hbf_State state;
int http_result_code;
char *http_error_msg;
char *etag;
int tries;
};
typedef struct hbf_transfer_s hbf_transfer_t;
/* Callback for to check on abort */
typedef int (*hbf_abort_callback) (void *);
typedef void (*hbf_log_callback) (const char *, const char *, void*);
typedef void (*hbf_chunk_finished_callback) (hbf_transfer_t*,int, void*);
struct hbf_transfer_s {
hbf_block_t **block_arr;
int block_cnt;
int fd;
int transfer_id;
char *url;
int start_id;
int status_code;
char *error_string;
int64_t stat_size;
time_t modtime;
time_t oc_header_modtime;
int64_t block_size;
int64_t threshold;
void *user_data;
hbf_abort_callback abort_cb;
hbf_log_callback log_cb;
hbf_chunk_finished_callback chunk_finished_cb;
int modtime_accepted;
const char *previous_etag; /* etag send as the If-Match http header */
char *file_id;
#ifndef NDEBUG
int64_t calc_size;
#endif
};
hbf_transfer_t *hbf_init_transfer( const char *dest_uri );
Hbf_State hbf_transfer( ne_session *session, hbf_transfer_t *transfer, const char *verb );
Hbf_State hbf_splitlist( hbf_transfer_t *transfer, int fd );
void hbf_free_transfer( hbf_transfer_t *transfer );
const char *hbf_error_string(hbf_transfer_t* transfer, Hbf_State state);
const char *hbf_transfer_etag( hbf_transfer_t *transfer );
const char *hbf_transfer_file_id( hbf_transfer_t *transfer );
void hbf_set_abort_callback( hbf_transfer_t *transfer, hbf_abort_callback cb);
void hbf_set_log_callback( hbf_transfer_t *transfer, hbf_log_callback cb);
/* returns an http (error) code of the transmission. If the transmission
* succeeded, the code is 200. If it failed, its the error code of the
* first part transmission that failed.
*/
int hbf_fail_http_code( hbf_transfer_t *transfer );
Hbf_State hbf_validate_source_file( hbf_transfer_t *transfer );
#ifdef __cplusplus
}
#endif
#endif
-18
Ver Arquivo
@@ -1,18 +0,0 @@
project(hbf_test C )
add_definitions(-DUNIT_TESTING=1)
find_package(CMocka REQUIRED)
include_directories(
${CMAKE_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMOCKA_INCLUDE_DIRS}
${NEON_INCLUDE_DIRS}
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
add_executable(send_test hbf_send_test.c)
target_link_libraries(send_test ${CMOCKA_LIBRARIES} ${NEON_LIBRARIES} ${HBF_LIBRARY} )
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 1.2 MiB

-162
Ver Arquivo
@@ -1,162 +0,0 @@
/*
* httpbf - send big files via http
*
* Copyright (c) 2012 Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <string.h>
#include <cmocka.h>
#include "config_csync.h"
#include <httpbf.h>
// A test case that does nothing and succeeds.
static void null_test_success(void **state) {
(void) state;
}
static char* test_file( const char* name ) {
if( ! name ) return 0;
char path[260];
strcpy( path, TESTFILEDIR);
if(path[strlen(TESTFILEDIR)-1] != '/')
strcat( path, "/");
strcat( path, name );
return strdup(path);
}
static void test_get_transfer_url( void **state ) {
const char *url = "http://example.org/owncloud";
const char *turl = NULL;
char res[256];
int i;
Hbf_State hbf_state;
hbf_transfer_t *list = NULL;
list = hbf_init_transfer( url );
assert_non_null( list );
/* open a file */
int fd = open( test_file("church.jpg"), O_RDONLY );
assert_true(fd >= 0);
hbf_state = hbf_splitlist(list, fd);
assert_true( hbf_state == HBF_SUCCESS);
for( i=0; i < list->block_cnt; i++ ) {
turl = get_transfer_url( list, i );
sprintf(res, "%s-chunking-%d-%d-%d", url, list->transfer_id,
list->block_cnt, i );
printf( "XX: %s\n", res );
assert_string_equal( turl, res );
}
}
static void test_hbf_init_transfer( void **state ) {
hbf_transfer_t *list = NULL;
const char *url = "http://example.org/owncloud";
list = hbf_init_transfer( url );
assert_non_null( list );
assert_string_equal( url, list->url );
}
/* test with a file size that is not a multiply of the slize size. */
static void test_hbf_splitlist_odd( void **state ){
hbf_transfer_t *list = NULL;
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/church.jpg";
/* open a file */
int fd = open(test_file("church.jpg"), O_RDONLY);
assert_true(fd >= 0);
int prev_id = 0;
int i;
Hbf_State hbf_state;
/* do a smoke test for uniqueness */
for( i=0; i < 10000; i++) {
list = hbf_init_transfer(dest_url);
assert_non_null(list);
usleep(1);
hbf_state = hbf_splitlist(list, fd);
assert_int_not_equal(list->transfer_id, prev_id);
prev_id = list->transfer_id;
hbf_free_transfer(list);
}
list = hbf_init_transfer(dest_url);
assert_non_null(list);
hbf_state = hbf_splitlist(list, fd);
assert_non_null(list);
assert_int_equal(list->calc_size, list->stat_size);
assert_int_not_equal(list->block_cnt, 0);
assert_true( hbf_state == HBF_SUCCESS);
/* checks on the block list */
int seen_zero_seq = 0;
int prev_seq = -1;
int64_t prev_block_end = -1;
for( i=0; i < list->block_cnt; i++) {
hbf_block_t *blk = list->block_arr[i];
assert_non_null(blk);
if( blk->seq_number == 0 ) seen_zero_seq++;
assert_int_equal(prev_seq, blk->seq_number -1 );
prev_seq = blk->seq_number;
assert_true((prev_block_end+1) == (blk->start));
prev_block_end = blk->start + blk->size;
}
/* Make sure we exactly saw blk->seq_number == 0 exactly one times */
assert_int_equal( seen_zero_seq, 1 );
hbf_free_transfer( list );
}
int main(void) {
const UnitTest tests[] = {
unit_test(null_test_success),
unit_test(test_hbf_splitlist_odd),
unit_test(test_hbf_init_transfer),
unit_test(test_get_transfer_url)
};
return run_tests(tests);
}
-6
Ver Arquivo
@@ -36,9 +36,6 @@
#define CSYNC_LOG_CATEGORY_NAME "csync.vio.main"
#include "csync_log.h"
#if USE_NEON
#include "csync_owncloud.h"
#endif
csync_vio_handle_t *csync_vio_opendir(CSYNC *ctx, const char *name) {
switch(ctx->replica) {
@@ -132,8 +129,5 @@ char *csync_vio_get_status_string(CSYNC *ctx) {
if(ctx->error_string) {
return ctx->error_string;
}
#ifdef USE_NEON
return owncloud_error_string(ctx);
#endif
return 0;
}
-10
Ver Arquivo
@@ -157,13 +157,6 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
return -1;
}
buf->name = c_basename(uri);
if (buf->name == NULL) {
csync_vio_file_stat_destroy(buf);
c_free_locale_string(wuri);
return -1;
}
buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
switch(sb.st_mode & S_IFMT) {
@@ -210,9 +203,6 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
#endif
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
buf->device = sb.st_dev;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
buf->inode = sb.st_ino;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
+64 -88
Ver Arquivo
@@ -116,10 +116,31 @@ int csync_vio_local_closedir(csync_vio_handle_t *dhandle) {
return rc;
}
static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
{
long long int t = filetime->dwHighDateTime;
t <<= 32;
t += (UINT32)filetime->dwLowDateTime;
t -= 116444736000000000LL;
if (t < 0)
{
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
return -1 - ((-t - 1) / 10000000);
}
else
{
if (remainder) *remainder = t % 10000000;
return t / 10000000;
}
}
csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
dhandle_t *handle = NULL;
csync_vio_file_stat_t *file_stat = NULL;
ULARGE_INTEGER FileIndex;
DWORD rem;
handle = (dhandle_t *) dhandle;
@@ -148,13 +169,43 @@ csync_vio_file_stat_t *csync_vio_local_readdir(csync_vio_handle_t *dhandle) {
file_stat->name = c_utf8_from_locale(handle->ffd.cFileName);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
} else {
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
}
if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT
&& handle->ffd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) {
file_stat->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
file_stat->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|| handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
file_stat->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
} else if (handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
} else {
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
}
return file_stat;
file_stat->flags = CSYNC_VIO_FILE_FLAGS_NONE;
/* Check for the hidden flag */
if( handle->ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
file_stat->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
file_stat->size = (handle->ffd.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + handle->ffd.nFileSizeLow;
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
file_stat->atime = FileTimeToUnixTime(&handle->ffd.ftLastAccessTime, &rem);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
file_stat->mtime = FileTimeToUnixTime(&handle->ffd.ftLastWriteTime, &rem);
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
file_stat->ctime = FileTimeToUnixTime(&handle->ffd.ftCreationTime, &rem);
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
return file_stat;
err:
SAFE_FREE(file_stat);
@@ -162,29 +213,16 @@ err:
return NULL;
}
static time_t FileTimeToUnixTime(FILETIME *filetime, DWORD *remainder)
{
long long int t = filetime->dwHighDateTime;
t <<= 32;
t += (UINT32)filetime->dwLowDateTime;
t -= 116444736000000000LL;
if (t < 0)
{
if (remainder) *remainder = 9999999 - (-t - 1) % 10000000;
return -1 - ((-t - 1) / 10000000);
}
else
{
if (remainder) *remainder = t % 10000000;
return t / 10000000;
}
}
int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
HANDLE h, hFind;
FILETIME ftCreate, ftAccess, ftWrite;
/* Almost nothing to do since csync_vio_local_readdir already filled up most of the information
But we still need to fetch the file ID.
Possible optimisation: only fetch the file id when we need it (for new files)
*/
HANDLE h;
BY_HANDLE_FILE_INFORMATION fileInfo;
WIN32_FIND_DATAW FindFileData;
ULARGE_INTEGER FileIndex;
mbchar_t *wuri = c_utf8_path_to_locale( uri );
@@ -205,47 +243,6 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
return -1;
}
buf->flags = CSYNC_VIO_FILE_FLAGS_NONE;
do {
// Check first if it is a symlink (code from c_islink)
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
hFind = FindFirstFileW(wuri, &FindFileData );
if (hFind != INVALID_HANDLE_VALUE) {
if( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
(FindFileData.dwReserved0 & IO_REPARSE_TAG_SYMLINK) ) {
buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
break;
}
}
FindClose(hFind);
}
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DEVICE
|| fileInfo.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE
|| fileInfo.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) {
buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
break;
}
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
break;
}
// fallthrough:
buf->type = CSYNC_VIO_FILE_TYPE_REGULAR;
break;
} while (0);
/* Check for the hidden flag */
if( fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) {
buf->flags |= CSYNC_VIO_FILE_FLAGS_HIDDEN;
}
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
buf->device = fileInfo.dwVolumeSerialNumber;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
/* Get the Windows file id as an inode replacement. */
FileIndex.HighPart = fileInfo.nFileIndexHigh;
FileIndex.LowPart = fileInfo.nFileIndexLow;
@@ -253,28 +250,7 @@ int csync_vio_local_stat(const char *uri, csync_vio_file_stat_t *buf) {
/* printf("Index: %I64i\n", FileIndex.QuadPart); */
buf->inode = FileIndex.QuadPart;
buf->size = (fileInfo.nFileSizeHigh * ((int64_t)(MAXDWORD)+1)) + fileInfo.nFileSizeLow;
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
/* Get the file time with a win32 call rather than through stat. See
* http://www.codeproject.com/Articles/1144/Beating-the-Daylight-Savings-Time-bug-and-getting
* for deeper explanation.
*/
if( GetFileTime(h, &ftCreate, &ftAccess, &ftWrite) ) {
DWORD rem;
buf->atime = FileTimeToUnixTime(&ftAccess, &rem);
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
buf->mtime = FileTimeToUnixTime(&ftWrite, &rem);
/* CSYNC_LOG(CSYNC_LOG_PRIORITY_DEBUG, "Local File MTime: %llu", (unsigned long long) buf->mtime ); */
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
buf->ctime = FileTimeToUnixTime(&ftCreate, &rem);
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
}
c_free_locale_string(wuri);
CloseHandle(h);
return 0;
}
-5
Ver Arquivo
@@ -7,7 +7,6 @@ include_directories(
${CSTDLIB_PUBLIC_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}
${CMOCKA_INCLUDE_DIR}
${HTTPBF_PUBLIC_INCLUDE_DIRS}
)
include_directories(${CHECK_INCLUDE_DIRS})
@@ -54,10 +53,6 @@ add_cmocka_test(check_csync_update csync_tests/check_csync_update.c ${TEST_TARGE
# encoding
add_cmocka_test(check_encoding_functions encoding_tests/check_encoding.c ${TEST_TARGET_LIBRARIES})
# httpbf
set(TEST_HTTPBF_LIBRARIES ${TEST_TARGET_LIBRARIES} ${NEON_LIBRARIES})
add_cmocka_test(check_httpbf httpbf_tests/hbf_send_test.c ${TEST_HTTPBF_LIBRARIES} )
if(UNIT_TESTING)
INSTALL( FILES "${CMOCKA_LIBRARIES}" DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif(UNIT_TESTING)
+17 -1
Ver Arquivo
@@ -48,6 +48,12 @@ static void setup_init(void **state) {
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes));
assert_int_equal(rc, 0);
/* and add some unicode stuff */
rc = _csync_exclude_add(&(csync->excludes), "*.💩");
assert_int_equal(rc, 0);
rc = _csync_exclude_add(&(csync->excludes), "пятницы.*");
assert_int_equal(rc, 0);
*state = csync;
}
@@ -81,7 +87,7 @@ static void check_csync_exclude_load(void **state)
rc = csync_exclude_load(EXCLUDE_LIST_FILE, &(csync->excludes) );
assert_int_equal(rc, 0);
assert_string_equal(csync->excludes->vector[0], "*.filepart");
assert_string_equal(csync->excludes->vector[0], "*~");
assert_int_not_equal(csync->excludes->count, 0);
}
@@ -145,6 +151,16 @@ static void check_csync_excluded(void **state)
rc = csync_excluded(csync, ".netscape/cache", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* Not excluded */
rc = csync_excluded(csync, "unicode/中文.hé", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_NOT_EXCLUDED);
/* excluded */
rc = csync_excluded(csync, "unicode/пятницы.txt", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
rc = csync_excluded(csync, "unicode/中文.💩", CSYNC_FTW_TYPE_FILE);
assert_int_equal(rc, CSYNC_FILE_EXCLUDE_LIST);
}
static void check_csync_excluded_traversal(void **state)
@@ -201,7 +201,6 @@ static csync_vio_file_stat_t* create_fstat(const char *name,
}
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
fs->device = 0;
fs->size = 157459;
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
-241
Ver Arquivo
@@ -1,241 +0,0 @@
/*
* libcsync -- a library to sync a directory with another
*
* Copyright (c) 2013 by Klaas Freitag <freitag@owncloud.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <string.h>
#include <unistd.h>
#include <cmocka.h>
#include "config_test.h"
#if USE_NEON
#include "httpbf.c"
#endif
// A test case that does nothing and succeeds.
static void null_test_success(void **state) {
(void) state;
}
#if USE_NEON
static char* test_file( const char* name ) {
char path[260];
if( ! name ) return 0;
strcpy( path, TESTFILES_DIR);
if(path[strlen(TESTFILES_DIR)-1] != '/')
strcat( path, "/");
strcat( path, name );
return strdup(path);
}
static void test_get_transfer_url( void **state ) {
const char *url = "http://example.org/owncloud";
const char *turl = NULL;
int fd;
Hbf_State hbf_state;
hbf_transfer_t *list = NULL;
(void) state;
list = hbf_init_transfer( url );
assert_non_null( list );
/* open a file */
fd = open( test_file("church.jpg"), O_RDONLY );
assert_true(fd >= 0);
hbf_state = hbf_splitlist(list, fd);
assert_true( hbf_state == HBF_SUCCESS);
assert_true( list->block_cnt == 1);
turl = get_transfer_url( list, 0 );
assert_non_null( turl );
assert_string_equal( url, turl );
hbf_free_transfer( list );
}
static void test_get_transfer_url_bigfile( void **state ) {
const char *url = "http://example.org/big_file";
const char *turl = NULL;
char res[256];
int i, fd;
Hbf_State hbf_state;
hbf_transfer_t *list = NULL;
(void) state;
list = hbf_init_transfer( url );
assert_non_null( list );
list->threshold = list->block_size = (1024*1024); /* block size 1 MB */
/* open a file */
fd = open( test_file("church.jpg"), O_RDONLY );
assert_true(fd >= 0);
hbf_state = hbf_splitlist(list, fd);
assert_true( hbf_state == HBF_SUCCESS);
assert_true( list->block_cnt == 2 );
for( i=0; i < list->block_cnt; i++ ) {
turl = get_transfer_url( list, i );
assert_non_null(turl);
sprintf(res, "%s-chunking-%u-%u-%u", url, list->transfer_id,
list->block_cnt, i );
/* printf( "XX: %s\n", res ); */
assert_string_equal( turl, res );
}
hbf_free_transfer(list);
}
static void test_hbf_init_transfer( void **state ) {
hbf_transfer_t *list = NULL;
const char *url = "http://example.org/owncloud";
(void) state;
list = hbf_init_transfer( url );
assert_non_null( list );
assert_string_equal( url, list->url );
}
/* test with a file size that is not a multiply of the slize size. */
static void test_hbf_splitlist_odd( void **state ){
hbf_transfer_t *list = NULL;
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/church.jpg";
int prev_id = 0;
int i, fd;
Hbf_State hbf_state;
(void) state;
/* open a file */
fd = open(test_file("church.jpg"), O_RDONLY);
assert_true(fd >= 0);
/* do a smoke test for uniqueness */
for( i=0; i < 10000; i++) {
list = hbf_init_transfer(dest_url);
assert_non_null(list);
usleep(1);
hbf_state = hbf_splitlist(list, fd);
assert_int_not_equal(list->transfer_id, prev_id);
prev_id = list->transfer_id;
hbf_free_transfer(list);
}
list = hbf_init_transfer(dest_url);
assert_non_null(list);
hbf_state = hbf_splitlist(list, fd);
assert_non_null(list);
#ifndef NDEBUG
assert_int_equal(list->calc_size, list->stat_size);
#endif
assert_int_not_equal(list->block_cnt, 0);
assert_true( hbf_state == HBF_SUCCESS);
/* checks on the block list */
if( 1 ) {
int seen_zero_seq = 0;
int prev_seq = -1;
int64_t prev_block_end = -1;
for( i=0; i < list->block_cnt; i++) {
hbf_block_t *blk = list->block_arr[i];
assert_non_null(blk);
if( blk->seq_number == 0 ) seen_zero_seq++;
assert_int_equal(prev_seq, blk->seq_number -1 );
prev_seq = blk->seq_number;
assert_true((prev_block_end+1) == (blk->start));
prev_block_end = blk->start + blk->size;
}
/* Make sure we exactly saw blk->seq_number == 0 exactly one times */
assert_int_equal( seen_zero_seq, 1 );
}
hbf_free_transfer( list );
}
/* test with a file size that is not a multiply of the slize size. */
static void test_hbf_splitlist_zero( void **state ){
hbf_transfer_t *list = NULL;
const char *dest_url = "http://localhost/ocm/remote.php/webdav/big/zerofile.txt";
int fd;
Hbf_State hbf_state;
(void) state;
/* open a file */
fd = open(test_file("zerofile.txt"), O_RDONLY);
assert_true(fd >= 0);
list = hbf_init_transfer(dest_url);
assert_non_null(list);
hbf_state = hbf_splitlist(list, fd);
assert_non_null(list);
assert_int_equal(list->stat_size, 0);
#ifndef NDEBUG
assert_int_equal(list->calc_size, list->stat_size);
#endif
assert_int_equal(list->block_cnt, 1);
assert_true( hbf_state == HBF_SUCCESS);
hbf_free_transfer( list );
}
#endif
int main(void) {
const UnitTest tests[] = {
unit_test(null_test_success),
#if USE_NEON
unit_test(test_hbf_splitlist_odd),
unit_test(test_hbf_splitlist_zero),
unit_test(test_hbf_init_transfer),
unit_test(test_get_transfer_url),
unit_test(test_get_transfer_url_bigfile)
#endif
};
return run_tests(tests);
}
-52
Ver Arquivo
@@ -73,15 +73,6 @@ static void setup_dir(void **state) {
assert_int_equal(rc, 0);
}
static void setup_file(void **state) {
int rc;
setup_dir(state);
rc = system("echo \"This is a test\" > /tmp/csync_test/file.txt");
assert_int_equal(rc, 0);
}
static void teardown(void **state) {
CSYNC *csync = *state;
int rc;
@@ -164,46 +155,6 @@ static void check_csync_vio_readdir(void **state)
}
/*
* Test for general functions (stat, chmod, chown, ...)
*/
static void check_csync_vio_stat_dir(void **state)
{
CSYNC *csync = *state;
csync_vio_file_stat_t *fs;
int rc;
fs = csync_vio_file_stat_new();
assert_non_null(fs);
rc = csync_vio_stat(csync, CSYNC_TEST_DIR, fs);
assert_int_equal(rc, 0);
assert_string_equal(fs->name, "csync_test");
assert_int_equal(fs->type, CSYNC_VIO_FILE_TYPE_DIRECTORY);
csync_vio_file_stat_destroy(fs);
}
static void check_csync_vio_stat_file(void **state)
{
CSYNC *csync = *state;
csync_vio_file_stat_t *fs;
int rc;
fs = csync_vio_file_stat_new();
assert_non_null(fs);
rc = csync_vio_stat(csync, CSYNC_TEST_FILE, fs);
assert_int_equal(rc, 0);
assert_string_equal(fs->name, "file.txt");
assert_int_equal(fs->type, CSYNC_VIO_FILE_TYPE_REGULAR);
csync_vio_file_stat_destroy(fs);
}
int torture_run_tests(void)
{
const UnitTest tests[] = {
@@ -211,9 +162,6 @@ int torture_run_tests(void)
unit_test_setup_teardown(check_csync_vio_opendir_perm, setup, teardown),
unit_test(check_csync_vio_closedir_null),
unit_test_setup_teardown(check_csync_vio_readdir, setup_dir, teardown),
unit_test_setup_teardown(check_csync_vio_stat_dir, setup_dir, teardown),
unit_test_setup_teardown(check_csync_vio_stat_file, setup_file, teardown),
};
return run_tests(tests);
+3 -3
Ver Arquivo
@@ -11,8 +11,8 @@ the Updater will check for updates and notify you when a new version is
available.
.. note:: Because of various technical issues, desktop sync clients older than
1.7 will not be allowed to connect and sync with the ownCloud 8.1 server. It is
highly recommended to keep your client updated.
1.7 will not be allowed to connect and sync with the ownCloud 8.1+ server. It
is highly recommended to keep your client updated.
Basic Workflow
--------------
@@ -89,7 +89,7 @@ To prevent automatic updates and disallow manual overrides:
1. Edit this Registry key:
HKEY_LOCAL_MACHINE\Software\Policies\ownCloud\ownCloud
``HKEY_LOCAL_MACHINE\Software\Policies\ownCloud\ownCloud``
2. Add the key ``skipUpdateCheck`` (of type DWORD).
+3 -3
Ver Arquivo
@@ -48,9 +48,9 @@ copyright = u'2013, The ownCloud developers'
# built documents.
#
# The short X.Y version.
version = '@VERSION_MAJOR@.@VERSION_MINOR@'
version = '@MIRALL_VERSION_MAJOR@.@MIRALL_VERSION_MINOR@'
# The full version, including alpha/beta/rc tags.
release = '@VERSION@'
release = '@MIRALL_VERSION@'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -189,7 +189,7 @@ latex_documents = [
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
latex_logo = '@LATEX_LOGO@'
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
Arquivo binário não exibido.

Antes

Largura:  |  Altura:  |  Tamanho: 5.7 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 5.5 KiB

+5 -1
Ver Arquivo
@@ -3,7 +3,8 @@ Installing the Desktop Synchronization Client
=============================================
You can download the latest version of the ownCloud Desktop Synchronization
Client from the `ownCloud Website <https://owncloud.org/install/#desktop>`_.
Client from the `ownCloud download page
<https://owncloud.org/install/#desktop>`_.
There are clients for Linux, Mac OS X, and Microsoft Windows.
Installation on Mac OS X and Windows is the same as for any software
@@ -21,6 +22,9 @@ will display a notification when an update is available.
Linux users must also have a password manager enabled, such as GNOME Keyring or
KWallet, so that the sync client can login automatically.
You will also find links to source code archives and older versions on the
download page.
Installation Wizard
-------------------
+4 -3
Ver Arquivo
@@ -13,14 +13,15 @@ Your files are always automatically synchronized between your ownCloud server
and local PC.
.. note:: Because of various technical issues, desktop sync clients older than
1.7 will not allowed to connect and sync with the ownCloud 8.1 server. It is
highly recommended to keep your client updated.
1.7 will not allowed to connect and sync with the ownCloud 8.1+ server. It
is highly recommended to keep your client updated.
Improvements and New Features
-----------------------------
The 2.0 release of the ownCloud desktop sync client has many new features and
improvements.
improvements. (See the `complete changelog
<https://owncloud.org/changelog/desktop/>`_.)
* Multi-account support
* Many UI improvements
Arquivo binário não exibido.
+26 -13
Ver Arquivo
@@ -92,18 +92,27 @@ have the following features:
removed synchronization on an account (see **Remove Sync** below).
The little button with three dots that sits to the right of the sync status bar
offers three additional options:
offers four additional options:
* Open Folder
* Pause Sync
* Choose What to Sync
* Pause Sync / Resume Sync
* Remove Sync
**Pause Sync** pauses sync operations without making any changes to your account.
**Open Folder** opens a file explorer window displaying the client-side folder
that is being synced.
**Remove Sync** suspends synchronization without removing the account, and it
removes your folder sync selection. When you're ready to resume synchronization
click the **Add Folder Sync Connection** button, and re-select the folders you
want to sync.
**Choose What to Sync** opens the folder sync tree view. From there you can
choose to sync all or only some of the folders in the folder tree.
**Pause Sync** pauses sync operations for just this folder sync connection
without making any changes to your account.
**Resume Sync** resumes sync operations for this folder sync connection.
**Remove Sync** removes this folder sync connection without removing the
account. If you want to synchronize the folder tree again then click the
**Add Folder Sync Connection** button, and re-select the folder tree that
you want to sync.
.. image:: images/client-7.png
:alt: Extra options for sync operations
@@ -122,12 +131,15 @@ Multi-Account Support
You may now configure multiple ownCloud accounts in your desktop sync client.
Simply click the **Add an Account** button on the General tab, and follow the
account creation wizard. The new account will appear as a new tab in the settings dialog, where you can adjust its settings at any time.
account creation wizard. The new account will appear as a new tab in the
settings dialog, where you can adjust its settings at any time.
Editing Ignored Files
---------------------
The Ignored Files Editor can be opened by clicking on the button in the General tab of the settings dialog. The settings apply to all configured accounts. The :guilabel:`Ignored Files Editor` provides a list of files that are ignored
The Ignored Files Editor can be opened by clicking on the button in the General
tab of the settings dialog. The settings apply to all configured accounts. The
:guilabel:`Ignored Files Editor` provides a list of files that are ignored
(that is, not synchronized) by the client and server during synchronizations.
You may add additional files or directories that you want to exclude from the
synchronization process. In addition to using standard characters, the Ignored
@@ -184,7 +196,8 @@ components of the path being checked.
.. note:: Custom entries are currently not validated for syntactical
correctness by the editor, so you will not see any warnings for bad
syntax. If your synchronization does not work as you expected, check your syntax.
syntax. If your synchronization does not work as you expected, check your
syntax.
Each pattern string in the list is preceded by a checkbox. When the check box
contains a check mark, in addition to ignoring the file or directory component
@@ -197,8 +210,8 @@ this list:
- The ownCloud Client always excludes files containing characters that cannot
be synchronized to other file systems.
- Files are removed that cause individual errors three times during a synchronization.
However, the client provides the option of retrying a synchronization three additional
times on files that produce errors.
- Files are removed that cause individual errors three times during a
synchronization. However, the client provides the option of retrying a
synchronization three additional times on files that produce errors.
For more detailed information see :ref:`ignored-files-label`.
+6 -6
Ver Arquivo
@@ -51,8 +51,8 @@ Where:
* ``Connected to <ownCloud instance> as <user>``: Indicates the ownCloud server
which the client is syncing with and the user account on that server.
* ``Add Folder...``: Provides the ability to add another folder to the sync
(see ``Adding a folder``).
* ``Add Folder Sync Connection...``: Provides the ability to add another folder to the sync
(see ``Adding a folder sync connection``).
* ``Pause/Resume``: Will pause the current sync or prevent the client from
starting a new sync. Resume will resume the sync process.
* ``Remove``: Will remove the selected folder from being synced. This is used,
@@ -74,11 +74,11 @@ Where:
.. image:: images/settings_account.png
:scale: 50 %
Adding a Folder
^^^^^^^^^^^^^^^
Adding a Folder Sync Connection
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Adding a new sync is initiated by clicking ``Add Folder...`` in the ``Account``
settings.
Adding a new sync is initiated by clicking ``Add Folder Sync Connection`` in
the ``Account`` settings.
..note:: To add a folder, you must not already sync a folder that contains this
folder. By default, the wizard sets up the root folder of the ownCloud
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 243 B

Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 122 B

@@ -66,11 +66,11 @@ private:
if (status.startsWith("OK"))
r << "ownCloud_ok";
if (status.startsWith("SYNC") || status.startsWith("NEW"))
r << "owncloud_sync";
r << "ownCloud_sync";
if (status.startsWith("IGNORE") || status.startsWith("WARN"))
r << "owncloud_warn";
r << "ownCloud_warn";
if (status.startsWith("ERROR"))
r << "owncloud_error";
r << "ownCloud_error";
if (status.contains("+SWM"))
r << "document-share";
+48 -42
Ver Arquivo
@@ -2,6 +2,10 @@
#
# Copyright (C) by Klaas Freitag <freitag@owncloud.com>
#
# This program is the core of OwnCloud integration to Nautilus
# It will be installed on /usr/share/nautilus-python/extensions/ with the paquet owncloud-client-nautilus
# (https://github.com/owncloud/client/edit/master/shell_integration/nautilus/syncstate.py)
#
# 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
@@ -18,7 +22,9 @@ import socket
from gi.repository import GObject, Nautilus
# do not touch the following line.
print("Initializing owncloud-client-nautilus extension")
# Do not touch the following line.
appname = 'ownCloud'
def get_local_path(url):
@@ -38,7 +44,6 @@ def get_runtime_dir():
return fallback
class SocketConnect(GObject.GObject):
def __init__(self):
GObject.GObject.__init__(self)
@@ -48,8 +53,8 @@ class SocketConnect(GObject.GObject):
self._sock = None
self._listeners = [self._update_registered_paths]
self._remainder = ''
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
# all over the other objects.
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
# all over the other objects.
# returns true when one should try again!
if self._connectToSocketServer():
@@ -77,38 +82,38 @@ class SocketConnect(GObject.GObject):
def _connectToSocketServer(self):
try:
self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
postfix = "/"+appname+"/socket"
sock_file = get_runtime_dir()+postfix
postfix = "/" + appname + "/socket" # Should use os.path.join instead
sock_file = get_runtime_dir() + postfix
print ("Socket: " + sock_file + " <=> " + postfix)
if sock_file != postfix:
try:
print("Socket File: "+sock_file)
print("Socket File: " + sock_file)
self._sock.connect(sock_file)
self.connected = True
print("Setting connected to %r" % self.connected )
print("Setting connected to %r." % self.connected )
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
print("Socket watch id: "+str(self._watch_id))
return False # don't run again
print("Socket watch id: " + str(self._watch_id))
return False # Don't run again
except Exception as e:
print("Could not connect to unix socket." + str(e))
print("Could not connect to unix socket. " + str(e))
else:
print("Sock-File not valid: "+sock_file)
except Exception as e:
print("Connect could not be established, try again later ")
print("Sock-File not valid: " + sock_file)
except Exception as e: # Bad habbit
print("Connect could not be established, try again later.")
self._sock.close()
return True # run again, if enabled via timeout_add()
return True # Run again, if enabled via timeout_add()
# notify is the raw answer from the socket
# Notify is the raw answer from the socket
def _handle_notify(self, source, condition):
data = source.recv(1024)
# prepend the remaining data from last call
# Prepend the remaining data from last call
if len(self._remainder) > 0:
data = self._remainder+data
data = self._remainder + data
self._remainder = ''
if len(data) > 0:
# remember the remainder for next round
# Remember the remainder for next round
lastNL = data.rfind('\n');
if lastNL > -1 and lastNL < len(data):
self._remainder = data[lastNL+1:]
@@ -119,10 +124,10 @@ class SocketConnect(GObject.GObject):
else:
return False
return True # run again
return True # Run again
def _handle_server_response(self, line):
print("Server response: "+line)
print("Server response: " + line)
parts = line.split(':')
action = parts[0]
args = parts[1:]
@@ -151,32 +156,33 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def get_file_items(self, window, files):
if len(files) != 1:
return
file=files[0]
items=[]
file = files[0]
items = []
# internal or external file?!
# Internal or external file?!
syncedFile = False
for reg_path in socketConnect.registered_paths:
topLevelFolder=False
topLevelFolder = False
filename = get_local_path(file.get_uri())
#check if its a folder (ends with an /), if yes add a "/" otherwise it will not find the entry in the table
if os.path.isdir(filename+"/"):
filename=filename+"/"
#check if toplevel folder, we need to ignore those as they cannot be shared
# Check if its a folder (ends with an /), if yes add a "/"
# otherwise it will not find the entry in the table
if os.path.isdir(filename + "/"):
filename += "/"
# Check if toplevel folder, we need to ignore those as they cannot be shared
if filename.count("/") < (reg_path.count("/")+2):
topLevelFolder=True
# only show the menu extension if the file is synced and the sync
# Only show the menu extension if the file is synced and the sync
# status is ok. Not for ignored files etc.
# ignore top level folders
if filename.startswith(reg_path) and topLevelFolder == False and socketConnect.nautilusVFSFile_table[filename]['state'] == 'OK':
syncedFile = True
# if it is neither in a synced folder or is a directory
if (not syncedFile):
# If it is neither in a synced folder or is a directory
if not syncedFile:
return items
# create an menu item
labelStr = "Share with "+appname+"..."
# Create an menu item
labelStr = "Share with " + appname + "..."
item = Nautilus.MenuItem(name='NautilusPython::ShareItem', label=labelStr,
tip='Share file %s through ownCloud' % file.get_name())
item.connect("activate", self.menu_share, file)
@@ -187,8 +193,8 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
def menu_share(self, menu, file):
filename = get_local_path(file.get_uri())
print("Share file "+filename)
socketConnect.sendCommand("SHARE:"+filename+"\n")
print("Share file " + filename)
socketConnect.sendCommand("SHARE:" + filename + "\n")
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
@@ -205,7 +211,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
return None
def askForOverlay(self, file):
# print("Asking for overlay for "+file)
# print("Asking for overlay for "+file) # For debug only
if os.path.isdir(file):
folderStatus = socketConnect.sendCommand("RETRIEVE_FOLDER_STATUS:"+file+"\n");
@@ -240,8 +246,8 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
'NOP' : appname +'_error'
}
# file = args[0]
# print "Action for " + file + ": "+args[0]
# file = args[0] # For debug only
# print("Action for " + file + ": " + args[0]) # For debug only
if action == 'STATUS':
newState = args[0]
emblem = Emblems[newState]
@@ -253,7 +259,7 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if( not itemStore['state'] or newState != itemStore['state'] ):
item = itemStore['item']
item.add_emblem(emblem)
# print "Setting emblem on " + args[1]+ "<>"+emblem+"<>"
# print("Setting emblem on " + args[1] + "<>" + emblem + "<>") # For debug only
socketConnect.nautilusVFSFile_table[args[1]] = {'item': item, 'state':newState}
elif action == 'UPDATE_VIEW':
@@ -278,9 +284,9 @@ class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.Info
if filename.startswith(reg_path):
socketConnect.nautilusVFSFile_table[filename] = {'item': item, 'state':''}
# item.add_string_attribute('share_state', "share state")
# item.add_string_attribute('share_state', "share state") # ?
self.askForOverlay(filename)
break
else:
# print("Not in scope:"+filename)
# print("Not in scope:" + filename) # For debug only
pass
+40028 -9160
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+1049 -321
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
-1
Ver Arquivo
@@ -10,7 +10,6 @@ if (APPLE)
endif()
add_subdirectory(libsync)
add_subdirectory(libclient)
if (NOT BUILD_LIBRARIES_ONLY)
add_subdirectory(gui)
add_subdirectory(cmd)
+15 -22
Ver Arquivo
@@ -42,8 +42,6 @@
#include <termios.h>
#endif
#include <csync_private.h>
using namespace OCC;
struct CmdOptions {
@@ -60,6 +58,7 @@ struct CmdOptions {
bool ignoreHiddenFiles;
QString exclude;
QString unsyncedfolders;
int restartTimes;
};
// we can't use csync_set_userdata because the SyncEngine sets it already.
@@ -157,17 +156,18 @@ void help()
std::cout << " --password, -p [pass] Use [pass] as password" << std::endl;
std::cout << " -n Use netrc (5) for login" << std::endl;
std::cout << " --non-interactive Do not block execution with interaction" << std::endl;
std::cout << " --max-sync-retries [n] Retries maximum n times (default to 3)" << std::endl;
std::cout << " -h Sync hidden files,do not ignore them" << std::endl;
std::cout << " --version, -v Display version and exit" << std::endl;
std::cout << "" << std::endl;
exit(1);
exit(0);
}
void showVersion() {
const char *binaryName = APPLICATION_EXECUTABLE "cmd";
std::cout << binaryName << " version " << qPrintable(Theme::instance()->version()) << std::endl;
exit(1);
exit(0);
}
void parseOptions( const QStringList& app_args, CmdOptions *options )
@@ -224,6 +224,8 @@ void parseOptions( const QStringList& app_args, CmdOptions *options )
options->exclude = it.next();
} else if( option == "--unsyncedfolders" && !it.peekNext().startsWith("-") ) {
options->unsyncedfolders = it.next();
} else if( option == "--max-sync-retries" && !it.peekNext().startsWith("-") ) {
options->restartTimes = it.next().toInt();
} else {
help();
}
@@ -270,6 +272,7 @@ int main(int argc, char **argv) {
options.useNetrc = false;
options.interactive = true;
options.ignoreHiddenFiles = true;
options.restartTimes = 3;
ClientProxy clientProxy;
parseOptions( app.arguments(), &options );
@@ -358,6 +361,7 @@ int main(int argc, char **argv) {
account->setCredentials(cred);
account->setSslErrorHandler(sslErrorHandler);
int restartCount = 0;
restart_sync:
CSYNC *_csync_ctx;
@@ -371,7 +375,6 @@ restart_sync:
csync_set_log_level(options.silent ? 1 : 11);
opts = &options;
cred->syncContextPreInit(_csync_ctx);
if( csync_init( _csync_ctx ) < 0 ) {
qFatal("Could not initialize csync!");
@@ -381,15 +384,11 @@ restart_sync:
// ignore hidden files or not
_csync_ctx->ignore_hidden_files = options.ignoreHiddenFiles;
csync_set_module_property(_csync_ctx, "csync_context", _csync_ctx);
if( !options.proxy.isNull() ) {
QString host;
int port = 0;
bool ok;
// Set as default and let overwrite later
csync_set_module_property(_csync_ctx, "proxy_type", (void*) "NoProxy");
QStringList pList = options.proxy.split(':');
if(pList.count() == 3) {
// http: //192.168.178.23 : 8080
@@ -399,13 +398,6 @@ restart_sync:
port = pList.at(2).toInt(&ok);
if( !host.isNull() ) {
csync_set_module_property(_csync_ctx, "proxy_type", (void*) "HttpProxy");
csync_set_module_property(_csync_ctx, "proxy_host", host.toUtf8().data());
if( ok && port ) {
csync_set_module_property(_csync_ctx, "proxy_port", (void*) &port);
}
}
QNetworkProxyFactory::setUseSystemConfiguration(false);
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, host, port));
}
@@ -416,7 +408,6 @@ restart_sync:
url.remove(0, 8);
url = QString("http%1").arg(url);
}
clientProxy.setCSyncProxy(QUrl(url), _csync_ctx);
}
// Exclude lists
@@ -455,8 +446,6 @@ restart_sync:
return EXIT_FAILURE;
}
cred->syncContextPreStart(_csync_ctx);
Cmd cmd;
SyncJournalDb db(options.source_dir);
if (!selectiveSyncList.empty()) {
@@ -464,7 +453,7 @@ restart_sync:
}
SyncEngine engine(account, _csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db);
QObject::connect(&engine, SIGNAL(finished()), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(finished(bool)), &app, SLOT(quit()));
QObject::connect(&engine, SIGNAL(transmissionProgress(ProgressInfo)), &cmd, SLOT(transmissionProgressSlot()));
// Have to be done async, else, an error before exec() does not terminate the event loop.
@@ -475,8 +464,12 @@ restart_sync:
csync_destroy(_csync_ctx);
if (engine.isAnotherSyncNeeded()) {
qDebug() << "Restarting Sync, because another sync is needed";
goto restart_sync;
if (restartCount < options.restartTimes) {
restartCount++;
qDebug() << "Restarting Sync, because another sync is needed" << restartCount;
goto restart_sync;
}
qWarning() << "Another sync is needed, but not done because restart count is exceeded" << restartCount;
}
return 0;
+16
Ver Arquivo
@@ -21,8 +21,13 @@ set(client_UI
ignorelisteditor.ui
networksettings.ui
protocolwidget.ui
activitywidget.ui
synclogdialog.ui
settingsdialog.ui
sharedialog.ui
sharelinkwidget.ui
shareusergroupwidget.ui
sharewidget.ui
sslerrordialog.ui
owncloudsetuppage.ui
addcertificatedialog.ui
@@ -50,24 +55,35 @@ set(client_SRCS
ignorelisteditor.cpp
logbrowser.cpp
networksettings.cpp
ocsjob.cpp
ocssharejob.cpp
ocsshareejob.cpp
openfilemanager.cpp
owncloudgui.cpp
owncloudsetupwizard.cpp
protocolwidget.cpp
activitywidget.cpp
activityitemdelegate.cpp
selectivesyncdialog.cpp
settingsdialog.cpp
share.cpp
sharedialog.cpp
sharelinkwidget.cpp
shareusergroupwidget.cpp
sharee.cpp
socketapi.cpp
sslbutton.cpp
sslerrordialog.cpp
syncrunfilelog.cpp
systray.cpp
thumbnailjob.cpp
quotainfo.cpp
accountstate.cpp
addcertificatedialog.cpp
authenticationdialog.cpp
proxyauthhandler.cpp
proxyauthdialog.cpp
synclogdialog.cpp
creds/credentialsfactory.cpp
creds/httpcredentialsgui.cpp
creds/shibbolethcredentials.cpp
+10
Ver Arquivo
@@ -215,6 +215,16 @@ AccountPtr AccountManager::load(QSettings& settings)
return acc;
}
AccountStatePtr AccountManager::account(const QString& name)
{
foreach (const auto& acc, _accounts) {
if (acc->account()->displayName() == name) {
return acc;
}
}
return AccountStatePtr();
}
AccountState *AccountManager::addAccount(const AccountPtr& newAccount)
{
auto id = newAccount->id();
+5
Ver Arquivo
@@ -58,6 +58,11 @@ public:
*/
QList<AccountStatePtr> accounts() { return _accounts; }
/**
* Return the account state pointer for an account identified by its display name
*/
AccountStatePtr account(const QString& name);
/**
* Delete the AccountState
*/
+133 -23
Ver Arquivo
@@ -83,11 +83,16 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
#else
ui->_folderList->setMinimumWidth( 300 );
#endif
createAccountToolbox();
connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState*)),
SLOT(slotAccountAdded(AccountState*)));
connect(ui->_folderList, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(slotCustomContextMenuRequested(QPoint)));
connect(ui->_folderList, SIGNAL(expanded(QModelIndex)) , this, SLOT(refreshSelectiveSyncStatus()));
connect(ui->_folderList, SIGNAL(collapsed(QModelIndex)) , this, SLOT(refreshSelectiveSyncStatus()));
connect(ui->selectiveSyncNotification, SIGNAL(linkActivated(QString)),
this, SLOT(slotLinkActivated(QString)));
connect(_model, SIGNAL(suggestExpand(QModelIndex)), ui->_folderList, SLOT(expand(QModelIndex)));
connect(_model, SIGNAL(dirtyChanged()), this, SLOT(refreshSelectiveSyncStatus()));
refreshSelectiveSyncStatus();
@@ -124,8 +129,46 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent) :
connect( &_quotaInfo, SIGNAL(quotaUpdated(qint64,qint64)),
this, SLOT(slotUpdateQuota(qint64,qint64)));
connect(ui->signInButton, SIGNAL(clicked()) , this, SLOT(slotSignInAccount()));
connect(ui->deleteButton, SIGNAL(clicked()) , this, SLOT(slotDeleteAccount()));
}
void AccountSettings::createAccountToolbox()
{
QMenu *menu = new QMenu();
_addAccountAction = new QAction(tr("Add new"), this);
menu->addAction(_addAccountAction);
connect(_addAccountAction, SIGNAL(triggered(bool)), SLOT(slotOpenAccountWizard()));
_toggleSignInOutAction = new QAction(tr("Sign out"), this);
connect(_toggleSignInOutAction, SIGNAL(triggered(bool)), SLOT(slotToggleSignInState()));
menu->addAction(_toggleSignInOutAction);
QAction *action = new QAction(tr("Remove"), this);
menu->addAction(action);
connect( action, SIGNAL(triggered(bool)), SLOT(slotDeleteAccount()));
ui->_accountToolbox->setText(tr("Account") + QLatin1Char(' '));
ui->_accountToolbox->setMenu(menu);
ui->_accountToolbox->setPopupMode(QToolButton::InstantPopup);
// Expand already on single click
ui->_folderList->setExpandsOnDoubleClick(false);
QObject::connect(ui->_folderList, SIGNAL(clicked(const QModelIndex &)),
this, SLOT(slotFolderListClicked(const QModelIndex&)));
}
void AccountSettings::slotOpenAccountWizard()
{
if (QSystemTrayIcon::isSystemTrayAvailable()) {
topLevelWidget()->close();
}
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), 0);
}
void AccountSettings::slotToggleSignInState()
{
bool signedInState = _accountState->isSignedOut();
_accountState->setSignedOut( !signedInState );
}
void AccountSettings::doExpand()
@@ -156,15 +199,14 @@ void AccountSettings::slotCustomContextMenuRequested(const QPoint &pos)
QAction *ac = menu->addAction(tr("Open folder"));
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotOpenCurrentFolder()));
ac = menu->addAction(tr("Choose What to Sync"));
ac = menu->addAction(tr("Choose what to sync"));
ac->setEnabled(folderConnected);
connect(ac, SIGNAL(triggered(bool)), this, SLOT(doExpand()));
ac = menu->addAction(folderPaused ? tr("Resume sync") : tr("Pause sync"));
ac->setEnabled(folderConnected);
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotEnableCurrentFolder()));
ac = menu->addAction(tr("Remove sync"));
ac = menu->addAction(tr("Remove folder sync connection"));
connect(ac, SIGNAL(triggered(bool)), this, SLOT(slotRemoveCurrentFolder()));
menu->exec(tv->mapToGlobal(pos));
}
@@ -278,13 +320,13 @@ void AccountSettings::slotRemoveCurrentFolder()
qDebug() << "Remove Folder alias " << alias;
if( !alias.isEmpty() ) {
QMessageBox messageBox(QMessageBox::Question,
tr("Confirm Sync Removal"),
tr("Confirm Folder Sync Connection Removal"),
tr("<p>Do you really want to stop syncing the folder <i>%1</i>?</p>"
"<p><b>Note:</b> This will <b>not</b> delete any files.</p>").arg(alias),
QMessageBox::NoButton,
this);
QPushButton* yesButton =
messageBox.addButton(tr("Stop syncing"), QMessageBox::YesRole);
messageBox.addButton(tr("Remove Folder Sync Connection"), QMessageBox::YesRole);
messageBox.addButton(tr("Cancel"), QMessageBox::NoRole);
messageBox.exec();
@@ -454,7 +496,15 @@ void AccountSettings::slotUpdateQuota(qint64 total, qint64 used)
ui->quotaProgressBar->setToolTip(toolTip);
} else {
ui->quotaProgressBar->setVisible(false);
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
ui->quotaInfoLabel->setToolTip(QString());
/* -1 means not computed; -2 means unknown; -3 means unlimited (#3940)*/
if (total == 0 || total == -1) {
ui->quotaInfoLabel->setText(tr("Currently there is no storage usage information available."));
} else {
QString usedStr = Utility::octetsToString(used);
ui->quotaInfoLabel->setText(tr("%1 in use").arg(usedStr));
}
}
}
@@ -476,12 +526,6 @@ void AccountSettings::slotAccountStateChanged(int state)
serverWithUser = tr("%1 as <i>%2</i>").arg(server, cred->user());
}
if (state != AccountState::SignedOut && ui->signInButton->hasFocus()) {
// The button is about to be hidden, clear the focus so the focus don't go to the
// "remove account" button
ui->signInButton->clearFocus();
}
ui->signInButton->setVisible(state == AccountState::SignedOut);
if (state == AccountState::Connected) {
showConnectionLabel( tr("Connected to %1.").arg(serverWithUser) );
} else if (state == AccountState::ServiceUnavailable) {
@@ -509,6 +553,49 @@ void AccountSettings::slotAccountStateChanged(int state)
ui->_folderList->setExpanded(_model->index(i), false);
}
}
/* set the correct label for the Account toolbox button */
if( _accountState ) {
bool isConnected = _accountState->isConnected();
if( isConnected ) {
_toggleSignInOutAction->setText(tr("Sign out"));
} else {
_toggleSignInOutAction->setText(tr("Sign in"));
}
}
}
void AccountSettings::slotLinkActivated(const QString& link)
{
// Parse folder alias and filename from the link, calculate the index
// and select it if it exists.
const QStringList li = link.split(QLatin1String("?folder="));
if( li.count() > 1) {
QString myFolder = li[0];
const QString alias = li[1];
if(myFolder.endsWith(QLatin1Char('/'))) myFolder.chop(1);
// Make sure the folder itself is expanded
Folder *f = FolderMan::instance()->folder(alias);
QModelIndex folderIndx = _model->indexForPath(f, QString());
if( !ui->_folderList->isExpanded(folderIndx)) {
ui->_folderList->setExpanded(folderIndx, true);
}
QModelIndex indx = _model->indexForPath(f, myFolder);
if( indx.isValid() ) {
// make sure all the parents are expanded
for (auto i = indx.parent(); i.isValid(); i = i.parent()) {
if( !ui->_folderList->isExpanded(i)) {
ui->_folderList->setExpanded(i, true);
}
}
ui->_folderList->setSelectionMode(QAbstractItemView::SingleSelection);
ui->_folderList->setCurrentIndex(indx);
ui->_folderList->scrollTo(indx);
} else {
qDebug() << "Unable to find a valid index for " << myFolder;
}
}
}
AccountSettings::~AccountSettings()
@@ -519,7 +606,6 @@ AccountSettings::~AccountSettings()
void AccountSettings::refreshSelectiveSyncStatus()
{
bool shouldBeVisible = _model->isDirty();
QStringList undecidedFolder;
for (int i = 0; !shouldBeVisible && i < _model->rowCount(); ++i) {
auto index = _model->index(i);
if (ui->_folderList->isExpanded(index) && _model->rowCount(index) > 0) {
@@ -527,29 +613,45 @@ void AccountSettings::refreshSelectiveSyncStatus()
}
}
QString msg;
int cnt = 0;
foreach (Folder *folder, FolderMan::instance()->map().values()) {
if (folder->accountState() != _accountState) {
continue;
}
auto undecidedList = folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
QString p;
foreach(const auto &it, undecidedList) {
undecidedFolder.append(it);
// FIXME: add the folder alias in a hoover hint.
// folder->alias() + QLatin1String("/")
if( cnt++ ) {
msg += QLatin1String(", ");
}
QString myFolder = (it);
if( myFolder.endsWith('/')) {
myFolder.chop(1);
}
QModelIndex theIndx = _model->indexForPath(folder, myFolder);
if(theIndx.isValid()) {
msg += QString::fromLatin1("<a href=\"%1?folder=%2\">%1</a>").arg(myFolder).arg(folder->alias());
} else {
msg += myFolder; // no link because we do not know the index yet.
}
}
}
if (undecidedFolder.isEmpty()) {
if (msg.isEmpty()) {
ui->selectiveSyncNotification->setVisible(false);
ui->selectiveSyncNotification->setText(QString());
} else {
ui->selectiveSyncNotification->setVisible(true);
ui->selectiveSyncNotification->setText(
tr("There are new folders that were not synchronized because they are too big: %1")
.arg(undecidedFolder.join(tr(", "))));
QString wholeMsg = tr("There are new folders that were not synchronized because they are too big: ") + msg;
ui->selectiveSyncNotification->setText(wholeMsg);
shouldBeVisible = true;
}
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !undecidedFolder.isEmpty());
ui->selectiveSyncApply->setEnabled(_model->isDirty() || !msg.isEmpty());
bool wasVisible = !ui->selectiveSyncStatus->isHidden();
if (wasVisible != shouldBeVisible) {
QSize hint = ui->selectiveSyncStatus->sizeHint();
@@ -566,9 +668,14 @@ void AccountSettings::refreshSelectiveSyncStatus()
}
}
void AccountSettings::slotSignInAccount()
void AccountSettings::slotAccountAdded(AccountState*)
{
_accountState->setSignedOut(false);
// if the theme is limited to single account, the button must hide if
// there is already one account.
if( AccountManager::instance()->accounts().size() > 1 &&
!Theme::instance()->multiAccount() ) {
_addAccountAction->setVisible(false);
}
}
void AccountSettings::slotDeleteAccount()
@@ -599,6 +706,9 @@ void AccountSettings::slotDeleteAccount()
// if there is no more account, show the wizard.
if( manager->accounts().isEmpty() ) {
// allow to add a new account if there is non any more. Always think
// about single account theming!
_addAccountAction->setVisible(true);
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)));
}
+7 -1
Ver Arquivo
@@ -76,17 +76,21 @@ protected slots:
void slotOpenCurrentFolder();
void slotFolderWizardAccepted();
void slotFolderWizardRejected();
void slotSignInAccount();
void slotDeleteAccount();
void slotToggleSignInState();
void slotOpenAccountWizard();
void slotAccountAdded(AccountState *);
void refreshSelectiveSyncStatus();
void slotCustomContextMenuRequested(const QPoint&);
void slotFolderListClicked( const QModelIndex& indx );
void doExpand();
void slotLinkActivated(const QString &link);
private:
void showConnectionLabel(const QString& message,
QStringList errors = QStringList());
bool event(QEvent*) Q_DECL_OVERRIDE;
void createAccountToolbox();
Ui::AccountSettings *ui;
@@ -95,6 +99,8 @@ private:
bool _wasDisabledBefore;
AccountState *_accountState;
QuotaInfo _quotaInfo;
QAction *_toggleSignInOutAction;
QAction *_addAccountAction;
};
} // namespace OCC
+16 -50
Ver Arquivo
@@ -6,30 +6,18 @@
<rect>
<x>0</x>
<y>0</y>
<width>469</width>
<height>652</height>
<width>575</width>
<height>557</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QWidget" name="accountStatus" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="SslButton" name="sslButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@@ -42,7 +30,7 @@
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QLabel" name="connectLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
@@ -61,42 +49,17 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="signInButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Enter your credentials to connect to the server</string>
</property>
<item row="0" column="2">
<widget class="QToolButton" name="_accountToolbox">
<property name="text">
<string>Sign in</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="deleteButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Remove the account configuration from the client</string>
</property>
<property name="text">
<string>Remove Account</string>
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="storageGroupBox">
<item>
<widget class="QLabel" name="quotaInfoLabel">
@@ -147,7 +110,7 @@
</item>
</layout>
</item>
<item>
<item row="2" column="0">
<widget class="QTreeView" name="_folderList">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Expanding">
@@ -166,7 +129,7 @@
</property>
</widget>
</item>
<item>
<item row="3" column="0">
<widget class="QWidget" name="selectiveSyncStatus" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
@@ -185,6 +148,9 @@
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
+1 -1
Ver Arquivo
@@ -142,7 +142,7 @@ void AccountState::checkConnectivity(CredentialFetchMode credentialsFetchMode)
}
if (_connectionValidator) {
qDebug() << "ConnectionValidator already running, ignoring";
qDebug() << "ConnectionValidator already running, ignoring" << account()->displayName();
return;
}
_credentialsFetchMode = credentialsFetchMode;
+153
Ver Arquivo
@@ -0,0 +1,153 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "activityitemdelegate.h"
#include "folderstatusmodel.h"
#include "folderman.h"
#include "accountstate.h"
#include "utility.h"
#include <theme.h>
#include <account.h>
#include <QFileIconProvider>
#include <QPainter>
#include <QApplication>
namespace OCC {
int ActivityItemDelegate::_iconHeight = 0;
int ActivityItemDelegate::_margin = 0;
int ActivityItemDelegate::iconHeight()
{
if( _iconHeight == 0 ) {
QStyleOptionViewItem option;
QFont font = option.font;
QFontMetrics fm(font);
_iconHeight = qRound(fm.height() / 5.0 * 8.0);
}
return _iconHeight;
}
int ActivityItemDelegate::rowHeight()
{
if( _margin == 0 ) {
QStyleOptionViewItem opt;
QFont f = opt.font;
QFontMetrics fm(f);
_margin = fm.height()/4;
}
return iconHeight() + 2 * _margin;
}
QSize ActivityItemDelegate::sizeHint(const QStyleOptionViewItem & option ,
const QModelIndex & /* index */) const
{
QFont font = option.font;
return QSize( 0, rowHeight() );
}
void ActivityItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
QStyledItemDelegate::paint(painter,option,index);
QFont font = option.font;
QFontMetrics fm( font );
int margin = fm.height()/4;
painter->save();
QIcon actionIcon = qvariant_cast<QIcon>(index.data(ActionIconRole));
QIcon userIcon = qvariant_cast<QIcon>(index.data(UserIconRole));
QString actionText = qvariant_cast<QString>(index.data(ActionTextRole));
QString pathText = qvariant_cast<QString>(index.data(PathRole));
QString remoteLink = qvariant_cast<QString>(index.data(LinkRole));
QString timeText = qvariant_cast<QString>(index.data(PointInTimeRole));
QString accountRole = qvariant_cast<QString>(index.data(AccountRole));
bool accountOnline = qvariant_cast<bool> (index.data(AccountConnectedRole));
QRect actionIconRect = option.rect;
QRect userIconRect = option.rect;
int iconHeight = qRound(fm.height() / 5.0 * 8.0);
int iconWidth = iconHeight;
actionIconRect.setLeft( option.rect.left() + margin );
actionIconRect.setWidth( iconWidth );
actionIconRect.setHeight( iconHeight );
actionIconRect.setTop( actionIconRect.top() + margin );
userIconRect.setLeft( actionIconRect.right() + margin );
userIconRect.setWidth( iconWidth );
userIconRect.setHeight( iconHeight );
userIconRect.setTop( actionIconRect.top() );
int textTopOffset = qRound( (iconHeight - fm.height())/ 2.0 );
// time rect
QRect timeBox;
int timeBoxWidth = fm.boundingRect(QLatin1String("4 hour(s) ago on longlongdomain.org")).width(); // FIXME.
timeBox.setTop( actionIconRect.top()+textTopOffset);
timeBox.setLeft( option.rect.right() - timeBoxWidth- margin );
timeBox.setWidth( timeBoxWidth);
timeBox.setHeight( fm.height() );
QRect actionTextBox = timeBox;
actionTextBox.setLeft( userIconRect.right()+margin );
actionTextBox.setRight( timeBox.left()-margin );
/* === start drawing === */
QPixmap pm = actionIcon.pixmap(iconWidth, iconHeight, QIcon::Normal);
painter->drawPixmap(QPoint(actionIconRect.left(), actionIconRect.top()), pm);
pm = userIcon.pixmap(iconWidth, iconHeight, QIcon::Normal);
painter->drawPixmap(QPoint(userIconRect.left(), userIconRect.top()), pm);
const QString elidedAction = fm.elidedText(actionText, Qt::ElideRight, actionTextBox.width());
painter->drawText(actionTextBox, elidedAction);
int atPos = accountRole.indexOf(QLatin1Char('@'));
if( atPos > -1 ) {
accountRole.remove(0, atPos+1);
}
QString timeStr = tr("%1 on %2").arg(timeText).arg(accountRole);
if( !accountOnline ) {
QPalette p = option.palette;
painter->setPen(p.color(QPalette::Disabled, QPalette::Text));
timeStr.append(" ");
timeStr.append(tr("(disconnected)"));
}
const QString elidedTime = fm.elidedText(timeStr, Qt::ElideRight, timeBox.width());
painter->drawText(timeBox, elidedTime);
painter->restore();
}
bool ActivityItemDelegate::editorEvent ( QEvent * event, QAbstractItemModel * model,
const QStyleOptionViewItem & option, const QModelIndex & index )
{
return QStyledItemDelegate::editorEvent(event, model, option, index);
}
} // namespace OCC
+53
Ver Arquivo
@@ -0,0 +1,53 @@
/*
* Copyright (C) by Klaas Freitag <freitag@kde.org>
* Copyright (C) by Olivier Goffart <ogoffart@woboq.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#pragma once
#include <QStyledItemDelegate>
namespace OCC {
/**
* @brief The ActivityItemDelegate class
* @ingroup gui
*/
class ActivityItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
enum datarole { ActionIconRole = Qt::UserRole + 1,
UserIconRole,
AccountRole,
ActionTextRole,
PathRole,
LinkRole,
PointInTimeRole,
AccountConnectedRole };
void paint( QPainter*, const QStyleOptionViewItem&, const QModelIndex& ) const Q_DECL_OVERRIDE;
QSize sizeHint( const QStyleOptionViewItem&, const QModelIndex& ) const Q_DECL_OVERRIDE;
bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option,
const QModelIndex& index ) Q_DECL_OVERRIDE;
static int rowHeight();
static int iconHeight();
private:
static int _margin;
static int _iconHeight;
};
} // namespace OCC
+433
Ver Arquivo
@@ -0,0 +1,433 @@
/*
* 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>
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QtWidgets>
#endif
#include "activitywidget.h"
#include "configfile.h"
#include "syncresult.h"
#include "logger.h"
#include "utility.h"
#include "theme.h"
#include "folderman.h"
#include "syncfileitem.h"
#include "folder.h"
#include "openfilemanager.h"
#include "owncloudpropagator.h"
#include "account.h"
#include "accountstate.h"
#include "accountmanager.h"
#include "activityitemdelegate.h"
#include "protocolwidget.h"
#include "QProgressIndicator.h"
#include "ui_activitywidget.h"
#include <climits>
namespace OCC {
void ActivityList::setAccountName( const QString& name )
{
_accountName = name;
}
QString ActivityList::accountName() const
{
return _accountName;
}
/* ==================================================================== */
ActivityListModel::ActivityListModel(QWidget *parent)
:QAbstractListModel(parent)
{
}
QVariant ActivityListModel::data(const QModelIndex &index, int role) const
{
Activity a;
if (!index.isValid())
return QVariant();
a = _finalList.at(index.row());
AccountStatePtr ast = AccountManager::instance()->account(a._accName);
QStringList list;
if (role == Qt::EditRole)
return QVariant();
switch (role) {
case ActivityItemDelegate::PathRole:
list = FolderMan::instance()->findFileInLocalFolders(a._file);
if( list.count() > 0 ) {
return QVariant(list.at(0));
}
return QVariant();
break;
case ActivityItemDelegate::ActionIconRole:
return QVariant(); // FIXME once the action can be quantified, display on Icon
break;
case ActivityItemDelegate::UserIconRole:
return QIcon(QLatin1String(":/client/resources/account.png"));
break;
case Qt::ToolTipRole:
case ActivityItemDelegate::ActionTextRole:
return a._subject;
break;
case ActivityItemDelegate::LinkRole:
return a._link;
break;
case ActivityItemDelegate::AccountRole:
return a._accName;
break;
case ActivityItemDelegate::PointInTimeRole:
return Utility::timeAgoInWords(a._dateTime);
break;
case ActivityItemDelegate::AccountConnectedRole:
return (ast && ast->isConnected());
break;
default:
return QVariant();
}
return QVariant();
}
int ActivityListModel::rowCount(const QModelIndex&) const
{
return _finalList.count();
}
// current strategy: Fetch 100 items per Account
bool ActivityListModel::canFetchMore(const QModelIndex& ) const
{
if( _activityLists.count() == 0 ) return true;
QMap<AccountState*, ActivityList>::const_iterator i = _activityLists.begin();
while (i != _activityLists.end()) {
AccountState *ast = i.key();
if( !ast->isConnected() ) {
return false;
}
ActivityList activities = i.value();
if( activities.count() == 0 &&
! _currentlyFetching.contains(ast) ) {
return true;
}
++i;
}
return false;
}
void ActivityListModel::startFetchJob(AccountState* s)
{
if( !s->isConnected() ) {
return;
}
JsonApiJob *job = new JsonApiJob(s->account(), QLatin1String("ocs/v1.php/cloud/activity"), this);
QObject::connect(job, SIGNAL(jsonRecieved(QVariantMap)), this, SLOT(slotActivitiesReceived(QVariantMap)));
job->setProperty("AccountStatePtr", QVariant::fromValue<AccountState*>(s));
QList< QPair<QString,QString> > params;
params.append(qMakePair(QLatin1String("page"), QLatin1String("0")));
params.append(qMakePair(QLatin1String("pagesize"), QLatin1String("100")));
job->addQueryParams(params);
_currentlyFetching.insert(s);
qDebug() << "Start fetching activities for " << s->account()->displayName();
job->start();
}
void ActivityListModel::slotActivitiesReceived(const QVariantMap& json)
{
auto activities = json.value("ocs").toMap().value("data").toList();
qDebug() << "*** activities" << activities;
ActivityList list;
AccountState* ai = qvariant_cast<AccountState*>(sender()->property("AccountStatePtr"));
_currentlyFetching.remove(ai);
list.setAccountName( ai->account()->displayName());
foreach( auto activ, activities ) {
auto json = activ.toMap();
Activity a;
a._accName = ai->account()->displayName();
a._id = json.value("id").toLongLong();
a._subject = json.value("subject").toString();
a._message = json.value("message").toString();
a._file = json.value("file").toString();
a._link = json.value("link").toUrl();
a._dateTime = json.value("date").toDateTime();
list.append(a);
}
_activityLists[ai] = list;
combineActivityLists();
}
void ActivityListModel::combineActivityLists()
{
ActivityList resultList;
foreach( ActivityList list, _activityLists.values() ) {
resultList.append(list);
}
std::sort( resultList.begin(), resultList.end() );
beginInsertRows(QModelIndex(), 0, resultList.count()-1);
_finalList = resultList;
endInsertRows();
}
void ActivityListModel::fetchMore(const QModelIndex &)
{
QList<AccountStatePtr> accounts = AccountManager::instance()->accounts();
foreach (AccountStatePtr asp, accounts) {
bool newItem = false;
// if the account is not yet managed, add an empty list.
if( !_activityLists.contains(asp.data()) ) {
_activityLists[asp.data()] = ActivityList();
newItem = true;
}
ActivityList activities = _activityLists[asp.data()];
if( newItem ) {
startFetchJob(asp.data());
}
}
}
void ActivityListModel::slotRefreshActivity(AccountState *ast)
{
if(ast && _activityLists.contains(ast)) {
qDebug() << "**** Refreshing Activity list for" << ast->account()->displayName();
_activityLists.remove(ast);
}
startFetchJob(ast);
}
void ActivityListModel::slotRemoveAccount(AccountState *ast )
{
if( _activityLists.contains(ast) ) {
int i = 0;
const QString accountToRemove = ast->account()->displayName();
QMutableListIterator<Activity> it(_finalList);
while (it.hasNext()) {
Activity activity = it.next();
if( activity._accName == accountToRemove ) {
beginRemoveRows(QModelIndex(), i, i+1);
it.remove();
endRemoveRows();
}
}
_activityLists.remove(ast);
_currentlyFetching.remove(ast);
}
}
/* ==================================================================== */
ActivityWidget::ActivityWidget(QWidget *parent) :
QWidget(parent),
_ui(new Ui::ActivityWidget)
{
_ui->setupUi(this);
// Adjust copyToClipboard() when making changes here!
#if defined(Q_OS_MAC)
_ui->_activityList->setMinimumWidth(400);
#endif
_model = new ActivityListModel(this);
ActivityItemDelegate *delegate = new ActivityItemDelegate;
delegate->setParent(this);
_ui->_activityList->setItemDelegate(delegate);
_ui->_activityList->setAlternatingRowColors(true);
_ui->_activityList->setModel(_model);
_ui->_headerLabel->setText(tr("Server Activities"));
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
connect(_copyBtn, SIGNAL(clicked()), SIGNAL(copyToClipboard()));
connect(_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(rowsInserted()));
connect( _ui->_activityList, SIGNAL(activated(QModelIndex)), this,
SLOT(slotOpenFile(QModelIndex)));
}
ActivityWidget::~ActivityWidget()
{
delete _ui;
}
void ActivityWidget::slotRefresh(AccountState *ptr)
{
_model->slotRefreshActivity(ptr);
}
void ActivityWidget::slotRemoveAccount( AccountState *ptr )
{
_model->slotRemoveAccount(ptr);
}
// FIXME: Reused from protocol widget. Move over to utilities.
QString ActivityWidget::timeString(QDateTime dt, QLocale::FormatType format) const
{
const QLocale loc = QLocale::system();
QString dtFormat = loc.dateTimeFormat(format);
static const QRegExp re("(HH|H|hh|h):mm(?!:s)");
dtFormat.replace(re, "\\1:mm:ss");
return loc.toString(dt, dtFormat);
}
void ActivityWidget::storeActivityList( QTextStream& ts )
{
ActivityList activities = _model->activityList();
foreach( Activity activity, activities ) {
ts << left
// account name
<< qSetFieldWidth(30)
<< activity._accName
// date and time
<< qSetFieldWidth(34)
<< activity._dateTime.toString()
// subject
<< qSetFieldWidth(10)
<< activity._subject
// file
<< qSetFieldWidth(30)
<< activity._file
// message (mostly empty)
<< qSetFieldWidth(55)
<< activity._message
//
<< qSetFieldWidth(0)
<< endl;
}
}
void ActivityWidget::slotOpenFile(QModelIndex indx)
{
if( indx.isValid() ) {
QString fullPath = indx.data(ActivityItemDelegate::PathRole).toString();
if (QFile::exists(fullPath)) {
showInFileManager(fullPath);
}
}
}
/* ==================================================================== */
ActivitySettings::ActivitySettings(QWidget *parent)
:QWidget(parent)
{
QHBoxLayout *hbox = new QHBoxLayout(this);
setLayout(hbox);
// create a tab widget for the three activity views
_tab = new QTabWidget(this);
hbox->addWidget(_tab);
_activityWidget = new ActivityWidget(this);
_tab->addTab(_activityWidget, Theme::instance()->applicationIcon(), tr("Server Activity"));
connect(_activityWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
_protocolWidget = new ProtocolWidget(this);
_tab->addTab(_protocolWidget, Theme::instance()->syncStateIcon(SyncResult::Success), tr("Sync Protocol"));
connect(_protocolWidget, SIGNAL(copyToClipboard()), this, SLOT(slotCopyToClipboard()));
// Add the not-synced list into the tab
QWidget *w = new QWidget;
QVBoxLayout *vbox2 = new QVBoxLayout(this);
vbox2->addWidget(new QLabel(tr("List of ignored or errornous files"), this));
vbox2->addWidget(_protocolWidget->issueWidget());
QDialogButtonBox *dlgButtonBox = new QDialogButtonBox(this);
vbox2->addWidget(dlgButtonBox);
QPushButton *_copyBtn = dlgButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
_copyBtn->setEnabled(true);
connect(_copyBtn, SIGNAL(clicked()), this, SLOT(slotCopyToClipboard()));
w->setLayout(vbox2);
_tab->addTab(w, Theme::instance()->syncStateIcon(SyncResult::Problem), tr("Not Synced"));
// Add a progress indicator to spin if the acitivity list is updated.
_progressIndicator = new QProgressIndicator(this);
_tab->setCornerWidget(_progressIndicator);
// connect a model signal to stop the animation.
connect(_activityWidget, SIGNAL(rowsInserted()), _progressIndicator, SLOT(stopAnimation()));
}
void ActivitySettings::slotCopyToClipboard()
{
QString text;
QTextStream ts(&text);
int idx = _tab->currentIndex();
QString theSubject;
if( idx == 0 ) {
// the activity widget
_activityWidget->storeActivityList(ts);
theSubject = tr("server activity list");
} else if(idx == 1 ) {
// the protocol widget
_protocolWidget->storeSyncActivity(ts);
theSubject = tr("sync activity list");
} else if(idx == 2 ) {
// issues Widget
theSubject = tr("not syned items list");
_protocolWidget->storeSyncIssues(ts);
}
QApplication::clipboard()->setText(text);
emit guiLog(tr("Copied to clipboard"), tr("The %1 has been copied to the clipboard.").arg(theSubject));
}
void ActivitySettings::slotRemoveAccount( AccountState *ptr )
{
_activityWidget->slotRemoveAccount(ptr);
}
void ActivitySettings::slotRefresh( AccountState* ptr )
{
if( ptr && ptr->isConnected() ) {
_progressIndicator->startAnimation();
_activityWidget->slotRefresh(ptr);
}
}
ActivitySettings::~ActivitySettings()
{
}
}
+193
Ver Arquivo
@@ -0,0 +1,193 @@
/*
* Copyright (C) by Klaas Freitag <freitag@owncloud.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#ifndef ACTIVITYWIDGET_H
#define ACTIVITYWIDGET_H
#include <QDialog>
#include <QDateTime>
#include <QLocale>
#include <QAbstractListModel>
#include "progressdispatcher.h"
#include "owncloudgui.h"
#include "account.h"
#include "ui_activitywidget.h"
class QPushButton;
class QProgressIndicator;
namespace OCC {
class Account;
class AccountStatusPtr;
class ProtocolWidget;
namespace Ui {
class ActivityWidget;
}
class Application;
/**
* @brief Activity Structure
* @ingroup gui
*
* contains all the information describing a single activity.
*/
class Activity
{
public:
qlonglong _id;
QString _subject;
QString _message;
QString _file;
QUrl _link;
QDateTime _dateTime;
QString _accName;
/**
* @brief Sort operator to sort the list youngest first.
* @param val
* @return
*/
bool operator<( const Activity& val ) const {
return _dateTime.toMSecsSinceEpoch() > val._dateTime.toMSecsSinceEpoch();
}
};
/**
* @brief The ActivityList
* @ingroup gui
*
* A QList based list of Activities
*/
class ActivityList:public QList<Activity>
{
public:
void setAccountName( const QString& name );
QString accountName() const;
private:
QString _accountName;
};
/**
* @brief The ActivityListModel
* @ingroup gui
*
* Simple list model to provide the list view with data.
*/
class ActivityListModel : public QAbstractListModel
{
Q_OBJECT
public:
explicit ActivityListModel(QWidget *parent=0);
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
int rowCount(const QModelIndex& parent = QModelIndex()) const Q_DECL_OVERRIDE;
bool canFetchMore(const QModelIndex& ) const Q_DECL_OVERRIDE;
void fetchMore(const QModelIndex&) Q_DECL_OVERRIDE;
ActivityList activityList() { return _finalList; }
public slots:
void slotRefreshActivity(AccountState* ast);
void slotRemoveAccount( AccountState *ast );
private slots:
void slotActivitiesReceived(const QVariantMap& json);
private:
void startFetchJob(AccountState* s);
void combineActivityLists();
QMap<AccountState*, ActivityList> _activityLists;
ActivityList _finalList;
QSet<AccountState*> _currentlyFetching;
};
/**
* @brief The ActivityWidget class
* @ingroup gui
*
* The list widget to display the activities, contained in the
* subsequent ActivitySettings widget.
*/
class ActivityWidget : public QWidget
{
Q_OBJECT
public:
explicit ActivityWidget(QWidget *parent = 0);
~ActivityWidget();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
void storeActivityList(QTextStream &ts);
public slots:
void slotOpenFile(QModelIndex indx);
void slotRefresh(AccountState* ptr);
void slotRemoveAccount( AccountState *ptr );
signals:
void guiLog(const QString&, const QString&);
void copyToClipboard();
void rowsInserted();
private:
QString timeString(QDateTime dt, QLocale::FormatType format) const;
Ui::ActivityWidget *_ui;
QPushButton *_copyBtn;
ActivityListModel *_model;
};
/**
* @brief The ActivitySettings class
* @ingroup gui
*
* Implements a tab for the settings dialog, displaying the three activity
* lists.
*/
class ActivitySettings : public QWidget
{
Q_OBJECT
public:
explicit ActivitySettings(QWidget *parent = 0);
~ActivitySettings();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
public slots:
void slotRefresh( AccountState* ptr );
void slotRemoveAccount( AccountState *ptr );
void slotCopyToClipboard();
signals:
void guiLog(const QString&, const QString&);
private:
QTabWidget *_tab;
ActivityWidget *_activityWidget;
ProtocolWidget *_protocolWidget;
QProgressIndicator *_progressIndicator;
};
}
#endif // ActivityWIDGET_H
+38
Ver Arquivo
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCC::ActivityWidget</class>
<widget class="QWidget" name="OCC::ActivityWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>693</width>
<height>556</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="_headerLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QListView" name="_activityList"/>
</item>
<item row="2" column="0">
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
</item>
</layout>
</widget>
<tabstops>
<tabstop>_activityList</tabstop>
<tabstop>_dialogButtonBox</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
+1
Ver Arquivo
@@ -427,6 +427,7 @@ void Application::showVersion()
stream << _theme->appName().toLatin1().constData()
<< QLatin1String(" version ")
<< _theme->version().toLatin1().constData() << endl;
stream << "Using Qt " << qVersion() << endl;
displayHelpText(helpText);
}
-25
Ver Arquivo
@@ -73,29 +73,6 @@ void ShibbolethCredentials::setAccount(Account* account)
}
}
void ShibbolethCredentials::syncContextPreInit(CSYNC* ctx)
{
csync_set_auth_callback (ctx, handleNeonSSLProblems);
}
QByteArray ShibbolethCredentials::prepareCookieData() const
{
QString cookiesAsString;
QList<QNetworkCookie> cookies = accountCookies(_account);
foreach(const QNetworkCookie &cookie, cookies) {
cookiesAsString += cookie.toRawForm(QNetworkCookie::NameAndValueOnly) + QLatin1String("; ");
}
return cookiesAsString.toLatin1();
}
void ShibbolethCredentials::syncContextPreStart (CSYNC* ctx)
{
csync_set_module_property(ctx, "session_key", prepareCookieData().data());
}
bool ShibbolethCredentials::changed(AbstractCredentials* credentials) const
{
ShibbolethCredentials* other(qobject_cast< ShibbolethCredentials* >(credentials));
@@ -207,8 +184,6 @@ void ShibbolethCredentials::invalidateToken()
jar->clearSessionCookies();
removeShibCookie();
_shibCookie = QNetworkCookie();
// ### access to ctx missing, but might not be required at all
//csync_set_module_property(ctx, "session_key", "");
}
void ShibbolethCredentials::onShibbolethCookieReceived(const QNetworkCookie& shibCookie)
-2
Ver Arquivo
@@ -48,8 +48,6 @@ public:
ShibbolethCredentials(const QNetworkCookie &cookie);
void setAccount(Account* account) Q_DECL_OVERRIDE;
void syncContextPreInit(CSYNC* ctx) Q_DECL_OVERRIDE;
void syncContextPreStart(CSYNC* ctx) Q_DECL_OVERRIDE;
bool changed(AbstractCredentials* credentials) const Q_DECL_OVERRIDE;
QString authType() const Q_DECL_OVERRIDE;
QString user() const Q_DECL_OVERRIDE;
+16 -15
Ver Arquivo
@@ -28,12 +28,11 @@
#include "clientproxy.h"
#include "syncengine.h"
#include "syncrunfilelog.h"
#include "socketapi.h"
#include "theme.h"
#include "filesystem.h"
#include "excludedfiles.h"
#include <csync_private.h>
#include "creds/abstractcredentials.h"
#include <QDebug>
@@ -110,10 +109,9 @@ bool Folder::init()
_csync_ctx = 0;
} else {
csync_set_log_callback( csyncLogCatcher );
csync_set_log_level( 11 );
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
Q_ASSERT( _accountState );
_accountState->account()->credentials()->syncContextPreInit(_csync_ctx);
if( csync_init( _csync_ctx ) < 0 ) {
qDebug() << "Could not initialize csync!" << csync_get_status(_csync_ctx) << csync_get_status_string(_csync_ctx);
@@ -704,16 +702,16 @@ void Folder::removeFromSettings() const
bool Folder::isFileExcludedAbsolute(const QString& fullPath) const
{
if (!fullPath.startsWith(path())) {
// Mark paths we're not responsible for as excluded...
return true;
}
QString myFullPath = fullPath;
if (myFullPath.endsWith(QLatin1Char('/'))) {
myFullPath.chop(1);
}
if (!myFullPath.startsWith(path())) {
// Mark paths we're not responsible for as excluded...
return true;
}
QString relativePath = myFullPath.mid(path().size());
auto excl = ExcludedFiles::instance().isExcluded(myFullPath, relativePath, _definition.ignoreHiddenFiles);
return excl != CSYNC_NOT_EXCLUDED;
@@ -773,6 +771,8 @@ void Folder::wipe()
// Delete files that have been partially downloaded.
slotDiscardDownloadProgress();
//Unregister the socket API so it does not keep the .sync_journal file open
FolderMan::instance()->socketApi()->slotUnregisterPath(alias());
_journal.close(); // close the sync journal
QFile file(stateDbFile);
@@ -791,6 +791,8 @@ void Folder::wipe()
QFile::remove( stateDbFile + "-shm" );
QFile::remove( stateDbFile + "-wal" );
QFile::remove( stateDbFile + "-journal" );
FolderMan::instance()->socketApi()->slotRegisterPath(alias());
}
bool Folder::setIgnoredFiles()
@@ -841,11 +843,10 @@ void Folder::startSync(const QStringList &pathList)
QMetaObject::invokeMethod(this, "slotSyncFinished", Qt::QueuedConnection);
return;
}
_clientProxy.setCSyncProxy(_accountState->account()->url(), _csync_ctx);
} else if (proxyDirty()) {
_clientProxy.setCSyncProxy(_accountState->account()->url(), _csync_ctx);
setProxyDirty(false);
}
csync_set_log_level( Logger::instance()->isNoop() ? 0 : 11 );
if (isBusy()) {
qCritical() << "* ERROR csync is still running and new sync requested.";
@@ -886,7 +887,7 @@ void Folder::startSync(const QStringList &pathList)
this, SLOT(slotAboutToPropagate(SyncFileItemVector&)));
connect(_engine.data(), SIGNAL(started()), SLOT(slotSyncStarted()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished()), SLOT(slotSyncFinished()), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(finished(bool)), SLOT(slotSyncFinished(bool)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncError(QString)), SLOT(slotSyncError(QString)), Qt::QueuedConnection);
connect(_engine.data(), SIGNAL(csyncUnavailable()), SLOT(slotCsyncUnavailable()), Qt::QueuedConnection);
@@ -958,7 +959,7 @@ void Folder::slotCsyncUnavailable()
_csyncUnavail = true;
}
void Folder::slotSyncFinished()
void Folder::slotSyncFinished(bool success)
{
qDebug() << " - client version" << qPrintable(Theme::instance()->version())
<< " Qt" << qVersion()
@@ -1016,7 +1017,7 @@ void Folder::slotSyncFinished()
qDebug() << "the last" << _consecutiveFailingSyncs << "syncs failed";
}
if (_syncResult.status() == SyncResult::Success) {
if (_syncResult.status() == SyncResult::Success && success) {
// Clear the white list as all the folders that should be on that list are sync-ed
journalDb()->setSelectiveSyncList(SyncJournalDb::SelectiveSyncWhiteList, QStringList());
}
@@ -1143,7 +1144,7 @@ void Folder::slotAboutToRemoveAllFiles(SyncFileItem::Direction, bool *cancel)
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);
QPushButton* keepBtn = msgBox.addButton(tr("Keep files"), QMessageBox::AcceptRole);
if (msgBox.exec() == -1) {
*cancel = true;
return;
+1 -1
Ver Arquivo
@@ -251,7 +251,7 @@ private slots:
void slotSyncStarted();
void slotSyncError(const QString& );
void slotCsyncUnavailable();
void slotSyncFinished();
void slotSyncFinished(bool);
void slotFolderDiscovered(bool local, QString folderName);
void slotTransmissionProgress(const ProgressInfo& pi);
+32 -8
Ver Arquivo
@@ -427,14 +427,13 @@ void FolderMan::slotSetFolderPaused( Folder *f, bool paused )
return;
}
slotScheduleSync(f);
f->setSyncPaused(paused);
if (!paused) {
_disabledFolders.remove(f);
slotScheduleSync(f);
} else {
_disabledFolders.insert(f);
}
f->setSyncPaused(paused);
emit folderSyncStateChange(f);
}
@@ -681,15 +680,22 @@ void FolderMan::slotStartScheduledFolderSync()
return;
}
// Try to start the top scheduled sync.
Folder *f = _scheduleQueue.dequeue();
// Find the first folder in the queue that can be synced.
Folder* f = nullptr;
while( !_scheduleQueue.isEmpty() ) {
f = _scheduleQueue.dequeue();
Q_ASSERT(f);
if( f->canSync() ) {
break;
}
}
emit scheduleQueueChanged();
Q_ASSERT(f);
// Start syncing this folder!
if(f->canSync()) {
if( f ) {
_currentSyncFolder = f;
f->startSync( QStringList() );
}
}
@@ -818,6 +824,24 @@ Folder *FolderMan::folderForPath(const QString &path)
return 0;
}
QStringList FolderMan::findFileInLocalFolders( const QString& relPath )
{
QStringList re;
foreach(Folder* folder, this->map().values()) {
QString path = folder->cleanPath();
QString remRelPath;
// cut off the remote path from the server path.
remRelPath = relPath.mid(folder->remotePath().length());
path += remRelPath;
if( QFile::exists(path) ) {
re.append( path );
}
}
return re;
}
void FolderMan::slotRemoveFolder( Folder *f )
{
if( !f ) {
+7
Ver Arquivo
@@ -57,6 +57,13 @@ public:
/** Returns the folder which the file or directory stored in path is in */
Folder* folderForPath(const QString& path);
/**
* returns a list of local files that exist on the local harddisk for an
* incoming relative server path. The method checks with all existing sync
* folders.
*/
QStringList findFileInLocalFolders( const QString& relPath );
/** Returns the folder by alias or NULL if no folder with the alias exists. */
Folder *folder( const QString& );
+24 -21
Ver Arquivo
@@ -28,6 +28,10 @@
namespace OCC {
FolderStatusDelegate::FolderStatusDelegate() : QStyledItemDelegate() {
m_moreIcon = QIcon(QLatin1String(":/client/resources/more.png"));
}
QString FolderStatusDelegate::addFolderText()
{
return tr("Add Folder Sync Connection");
@@ -79,12 +83,6 @@ QSize FolderStatusDelegate::sizeHint(const QStyleOptionViewItem & option ,
h += aliasMargin*2 + errMsgs.count()*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);
}
@@ -221,20 +219,23 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
painter->setFont(aliasFont);
painter->drawText(aliasRect, elidedAlias);
painter->setFont(subFont);
QString elidedRemotePathText;
const bool showProgess = !overallString.isEmpty() || !itemString.isEmpty();
if(!showProgess) {
painter->setFont(subFont);
QString elidedRemotePathText;
if (remotePath.isEmpty() || remotePath == QLatin1String("/")) {
elidedRemotePathText = subFm.elidedText(tr("Syncing selected files in your account with"),
Qt::ElideRight, remotePathRect.width());
} else {
elidedRemotePathText = subFm.elidedText(tr("Remote path: %1").arg(remotePath),
Qt::ElideMiddle, remotePathRect.width());
if (remotePath.isEmpty() || remotePath == QLatin1String("/")) {
elidedRemotePathText = subFm.elidedText(tr("Syncing selected 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);
}
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
@@ -270,7 +271,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
h += aliasMargin;
// Sync File Progress Bar: Show it if syncFile is not empty.
if( !overallString.isEmpty() || !itemString.isEmpty()) {
if (showProgess) {
int fileNameTextHeight = subFm.boundingRect(tr("File")).height();
int barHeight = qMax(fileNameTextHeight, aliasFm.height()+4); ;
int overallWidth = option.rect.width()-aliasMargin-nextToIcon;
@@ -283,7 +284,7 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
// Overall Progress Bar.
QRect pBRect;
pBRect.setTop( h );
pBRect.setTop( remotePathRect.top() );
pBRect.setLeft( nextToIcon );
pBRect.setHeight(barHeight);
pBRect.setWidth( overallWidth - progressTextWidth - 2 * margin );
@@ -327,13 +328,15 @@ void FolderStatusDelegate::paint(QPainter *painter, const QStyleOptionViewItem &
{
QStyleOptionToolButton btnOpt;
btnOpt.text = QLatin1String("...");
//btnOpt.text = QLatin1String("...");
btnOpt.state = option.state;
btnOpt.state &= ~(QStyle::State_Selected | QStyle::State_HasFocus);
btnOpt.state |= QStyle::State_Raised;
btnOpt.arrowType = Qt::NoArrow;
btnOpt.subControls = QStyle::SC_ToolButton;
btnOpt.rect = optionsButtonRect(option.rect);
btnOpt.icon = m_moreIcon;
btnOpt.iconSize = btnOpt.rect.size();
QApplication::style()->drawComplexControl( QStyle::CC_ToolButton, &btnOpt, painter );
}
}
+2 -1
Ver Arquivo
@@ -26,6 +26,8 @@ class FolderStatusDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
QIcon m_moreIcon;
FolderStatusDelegate();
enum datarole { FolderAliasRole = Qt::UserRole + 100,
HeaderRole,
@@ -41,7 +43,6 @@ public:
SyncProgressOverallPercent,
SyncProgressOverallString,
SyncProgressItemString,
AddProgressSpace,
WarningCount,
SyncRunning,
+76 -41
Ver Arquivo
@@ -22,6 +22,7 @@
#include <QFileIconProvider>
#include <QVarLengthArray>
#include <set>
Q_DECLARE_METATYPE(QPersistentModelIndex)
@@ -99,7 +100,7 @@ Qt::ItemFlags FolderStatusModel::flags ( const QModelIndex &index ) const
case RootFolder:
return Qt::ItemIsEnabled;
case SubFolder:
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsSelectable;
}
return 0;
}
@@ -184,11 +185,16 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
case FolderStatusDelegate::FolderAliasRole : return f->alias();
case FolderStatusDelegate::FolderSyncPaused : return f->syncPaused();
case FolderStatusDelegate::FolderAccountConnected : return accountConnected;
case Qt::ToolTipRole:
case Qt::ToolTipRole: {
QString toolTip;
if ( accountConnected )
return Theme::instance()->statusHeaderText(f->syncResult().status());
toolTip = Theme::instance()->statusHeaderText(f->syncResult().status());
else
return tr("Signed out");
toolTip = tr("Signed out");
toolTip += "\n";
toolTip += folderInfo._folder->path();
return toolTip;
}
case FolderStatusDelegate::FolderStatusIconRole:
if ( accountConnected ) {
auto theme = Theme::instance();
@@ -212,8 +218,6 @@ QVariant FolderStatusModel::data(const QModelIndex &index, int role) const
} else {
return Theme::instance()->folderOfflineIcon();
}
case FolderStatusDelegate::AddProgressSpace:
return !progress.isNull();
case FolderStatusDelegate::SyncProgressItemString:
return progress._progressString;
case FolderStatusDelegate::WarningCount:
@@ -351,13 +355,21 @@ FolderStatusModel::SubFolderInfo* FolderStatusModel::infoForIndex(const QModelIn
QModelIndex FolderStatusModel::indexForPath(Folder *f, const QString& path) const
{
if( !f ) {
return QModelIndex();
}
int slashPos = path.lastIndexOf('/');
if (slashPos == -1) {
// first level folder
for (int i = 0; i < _folders.size(); ++i) {
if (_folders.at(i)._folder == f) {
if( path.isEmpty() ) { // the folder object
return index(i, 0);
}
for (int j = 0; j < _folders.at(i)._subs.size(); ++j) {
if (_folders.at(i)._subs.at(j)._name == path) {
const QString subName = _folders.at(i)._subs.at(j)._name;
if (subName == path) {
return index(j, 0, index(i));
}
}
@@ -501,6 +513,7 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
connect(job, SIGNAL(finishedWithError(QNetworkReply*)),
this, SLOT(slotLscolFinishedWithError(QNetworkReply*)));
job->start();
QPersistentModelIndex persistentIndex(parent);
job->setProperty(propertyParentIndexC , QVariant::fromValue(persistentIndex));
@@ -509,7 +522,7 @@ void FolderStatusModel::fetchMore(const QModelIndex& parent)
QTimer::singleShot(1000, this, SLOT(slotShowFetchProgress()));
}
void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
void FolderStatusModel::slotUpdateDirectories(const QStringList &list)
{
auto job = qobject_cast<LsColJob *>(sender());
Q_ASSERT(job);
@@ -529,46 +542,45 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
parentInfo->_fetching = false;
parentInfo->_fetched = true;
auto list = list_;
list.removeFirst(); // remove the parent item
QUrl url = parentInfo->_folder->remoteUrl();
QString pathToRemove = url.path();
if (!pathToRemove.endsWith('/'))
pathToRemove += '/';
// Drop the folder base path and check for excludes.
QMutableListIterator<QString> it(list);
while (it.hasNext()) {
it.next();
it.value().remove(pathToRemove);
if (parentInfo->_folder->isFileExcludedRelative(it.value())) {
it.remove();
}
}
beginInsertRows(idx, 0, list.count() - 1);
QStringList selectiveSyncBlackList;
if (parentInfo->_checked == Qt::PartiallyChecked) {
selectiveSyncBlackList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncBlackList);
}
auto selectiveSyncUndecidedList = parentInfo->_folder->journalDb()->getSelectiveSyncList(SyncJournalDb::SelectiveSyncUndecidedList);
QVarLengthArray<int, 10> undecidedIndexes;
QVector<SubFolderInfo> newSubs;
QVarLengthArray<int> undecidedIndexes;
std::set<QString> selectiveSyncUndecidedSet; // not QSet because it's not sorted
foreach (const QString &str, selectiveSyncUndecidedList) {
if (str.startsWith(parentInfo->_path) || parentInfo->_path == QLatin1String("/")) {
selectiveSyncUndecidedSet.insert(str);
}
}
newSubs.reserve(list.size() - 1);
for (int i = 1; // skip the parent item (first in the list)
i < list.size(); ++i) {
const QString &path = list.at(i);
auto relativePath = path.mid(pathToRemove.size());
if (parentInfo->_folder->isFileExcludedRelative(relativePath)) {
continue;
}
int i = 0;
foreach (QString path, list) {
SubFolderInfo newInfo;
newInfo._folder = parentInfo->_folder;
newInfo._pathIdx = parentInfo->_pathIdx;
newInfo._pathIdx << i++;
newInfo._pathIdx << newSubs.size();
auto size = job ? job->_sizes.value(path) : 0;
newInfo._size = size;
newInfo._path = path;
newInfo._name = path.split('/', QString::SkipEmptyParts).last();
newInfo._path = relativePath;
newInfo._name = relativePath.split('/', QString::SkipEmptyParts).last();
if (path.isEmpty())
if (relativePath.isEmpty())
continue;
if (parentInfo->_checked == Qt::Unchecked) {
@@ -577,30 +589,50 @@ void FolderStatusModel::slotUpdateDirectories(const QStringList &list_)
newInfo._checked = Qt::Checked;
} else {
foreach(const QString &str , selectiveSyncBlackList) {
if (str == path || str == QLatin1String("/")) {
if (str == relativePath || str == QLatin1String("/")) {
newInfo._checked = Qt::Unchecked;
break;
} else if (str.startsWith(path)) {
} else if (str.startsWith(relativePath)) {
newInfo._checked = Qt::PartiallyChecked;
}
}
}
foreach(const QString &str , selectiveSyncUndecidedList) {
if (str == path) {
auto it = selectiveSyncUndecidedSet.lower_bound(relativePath);
if (it != selectiveSyncUndecidedSet.end()) {
if (*it == relativePath) {
newInfo._isUndecided = true;
} else if (str.startsWith(path)) {
selectiveSyncUndecidedSet.erase(it);
} else if ((*it).startsWith(relativePath)) {
undecidedIndexes.append(newInfo._pathIdx.last());
// Remove all the items from the selectiveSyncUndecidedSet that starts with this path
QString relativePathNext = relativePath;
relativePathNext[relativePathNext.length()-1].unicode()++;
auto it2 = selectiveSyncUndecidedSet.lower_bound(relativePathNext);
selectiveSyncUndecidedSet.erase(it, it2);
}
}
parentInfo->_subs.append(newInfo);
newSubs.append(newInfo);
}
beginInsertRows(idx, 0, newSubs.size() - 1);
parentInfo->_subs = std::move(newSubs);
endInsertRows();
for (auto it = undecidedIndexes.begin(); it != undecidedIndexes.end(); ++it) {
suggestExpand(idx.child(*it, 0));
}
/* Try to remove the the undecided lists the items that are not on the server. */
auto it = std::remove_if(selectiveSyncUndecidedList.begin(), selectiveSyncUndecidedList.end(),
[&](const QString &s) { return selectiveSyncUndecidedSet.count(s); } );
if (it != selectiveSyncUndecidedList.end()) {
selectiveSyncUndecidedList.erase(it, selectiveSyncUndecidedList.end());
parentInfo->_folder->journalDb()->setSelectiveSyncList(
SyncJournalDb::SelectiveSyncUndecidedList, selectiveSyncUndecidedList);
emit dirtyChanged();
}
}
void FolderStatusModel::slotLscolFinishedWithError(QNetworkReply* r)
@@ -663,7 +695,7 @@ void FolderStatusModel::slotUpdateFolderState(Folder *folder)
for (int i = 0; i < _folders.count(); ++i) {
if (_folders.at(i)._folder == folder) {
emit dataChanged(index(i), index(i),
QVector<int>() << FolderStatusDelegate::AddProgressSpace);
QVector<int>() << FolderStatusDelegate::FolderStatus);
}
}
}
@@ -756,8 +788,7 @@ void FolderStatusModel::slotSetProgress(const ProgressInfo &progress)
auto *pi = &_folders[folderIndex]._progress;
QVector<int> roles;
roles << FolderStatusDelegate::AddProgressSpace << FolderStatusDelegate::SyncProgressItemString
<< FolderStatusDelegate::WarningCount;
roles << FolderStatusDelegate::SyncProgressItemString << FolderStatusDelegate::WarningCount;
if (!progress._currentDiscoveredFolder.isEmpty()) {
pi->_progressString = tr("Checking for changes in '%1'").arg(progress._currentDiscoveredFolder);
@@ -894,7 +925,10 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
if (folderIndex < 0) { return; }
SyncResult::Status state = f->syncResult().status();
if (state == SyncResult::NotYetStarted) {
if (f->syncPaused()) {
// Reset progress info.
_folders[folderIndex]._progress = SubFolderInfo::Progress();
} else if (state == SyncResult::NotYetStarted) {
FolderMan* folderMan = FolderMan::instance();
int pos = folderMan->scheduleQueue().indexOf(f);
if (folderMan->currentSyncFolder()
@@ -925,7 +959,8 @@ void FolderStatusModel::slotFolderSyncStateChange(Folder *f)
if (state == SyncResult::Success) {
foreach (const SyncFileItemPtr &i, f->syncResult().syncFileItemVector()) {
if (i->_isDirectory && (i->_instruction == CSYNC_INSTRUCTION_NEW
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE)) {
|| i->_instruction == CSYNC_INSTRUCTION_REMOVE
|| i->_instruction == CSYNC_INSTRUCTION_RENAME)) {
// There is a new or a removed folder. reset all data
auto & info = _folders[folderIndex];
info.resetSubs(this, index(folderIndex));
+2
Ver Arquivo
@@ -14,6 +14,8 @@
#ifndef MIRALL_FOLDERWATCHER_H
#define MIRALL_FOLDERWATCHER_H
#include "config.h"
#include <QList>
#include <QObject>
#include <QString>
+2 -2
Ver Arquivo
@@ -559,9 +559,9 @@ FolderWizard::FolderWizard(AccountPtr account, QWidget *parent)
}
setPage(Page_SelectiveSync, _folderWizardSelectiveSyncPage);
setWindowTitle( tr("Add Folder") );
setWindowTitle( tr("Add Folder Sync Connection") );
setOptions(QWizard::CancelButtonOnLeft);
setButtonText(QWizard::FinishButton, tr("Add Folder"));
setButtonText(QWizard::FinishButton, tr("Add Sync Connection"));
}
FolderWizard::~FolderWizard()
+2 -15
Ver Arquivo
@@ -21,6 +21,7 @@
#include "configfile.h"
#include "owncloudsetupwizard.h"
#include "accountmanager.h"
#include "synclogdialog.h"
#include "updater/updater.h"
#include "updater/ocupdater.h"
@@ -83,18 +84,12 @@ GeneralSettings::GeneralSettings(QWidget *parent) :
_ui->monoIconsCheckBox->setVisible(QDir(themeDir).exists());
connect(_ui->ignoredFilesButton, SIGNAL(clicked()), SLOT(slotIgnoreFilesEditor()));
connect(_ui->addAccountButton, SIGNAL(clicked()), SLOT(slotOpenAccountWizard()));
connect(AccountManager::instance(), SIGNAL(accountAdded(AccountState*)),
SLOT(slotAccountAddedOrRemoved()));
connect(AccountManager::instance(), SIGNAL(accountRemoved(AccountState*)),
SLOT(slotAccountAddedOrRemoved()));
slotAccountAddedOrRemoved();
}
GeneralSettings::~GeneralSettings()
{
delete _ui;
delete _syncLogDialog;
}
QSize GeneralSettings::sizeHint() const {
@@ -169,12 +164,4 @@ void GeneralSettings::slotOpenAccountWizard()
OwncloudSetupWizard::runWizard(qApp, SLOT(slotownCloudWizardDone(int)), 0);
}
void GeneralSettings::slotAccountAddedOrRemoved()
{
_ui->addAccountButton->setVisible(
AccountManager::instance()->accounts().isEmpty()
|| !Theme::instance()->singleAccount());
}
} // namespace OCC
+2 -2
Ver Arquivo
@@ -19,6 +19,7 @@
namespace OCC {
class IgnoreListEditor;
class SyncLogDialog;
namespace Ui {
class GeneralSettings;
@@ -44,14 +45,13 @@ private slots:
void slotUpdateInfo();
void slotIgnoreFilesEditor();
void slotOpenAccountWizard();
void slotAccountAddedOrRemoved();
private:
void loadMiscSettings();
Ui::GeneralSettings *_ui;
QPointer<IgnoreListEditor> _ignoreEditor;
QPointer<SyncLogDialog> _syncLogDialog;
};
+60 -65
Ver Arquivo
@@ -13,8 +13,8 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QGroupBox" name="generalGroupBox">
<property name="title">
<string>General Settings</string>
@@ -23,14 +23,14 @@
<item row="0" column="0">
<widget class="QCheckBox" name="autostartCheckBox">
<property name="text">
<string>Launch on System Startup</string>
<string>&amp;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>
<string>Show &amp;Desktop Notifications</string>
</property>
</widget>
</item>
@@ -40,73 +40,45 @@
<string>For System Tray</string>
</property>
<property name="text">
<string>Use Monochrome Icons</string>
<string>Use &amp;Monochrome Icons</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Advanced</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="ignoredFilesButton">
<property name="text">
<string>Edit Ignored Files</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<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 class="QPushButton" name="ignoredFilesButton">
<property name="text">
<string>Edit &amp;Ignored Files</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="addAccountButton">
<property name="text">
<string>Add an Account</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<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 row="0" column="1" colspan="2">
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>555</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<item row="1" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="newFolderLimitCheckBox">
<property name="text">
<string>Ask confirmation before downloading folders larger than</string>
<string>Ask &amp;confirmation before downloading folders larger than</string>
</property>
<property name="checked">
<bool>true</bool>
@@ -146,26 +118,39 @@
</layout>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="crashreporterCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>S&amp;how crash reporter</string>
</property>
</widget>
</item>
<item row="2" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QCheckBox" name="crashreporterCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="text">
<string>Show crash reporter</string>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</widget>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<item row="2" column="0">
<widget class="QGroupBox" name="aboutGroupBox">
<property name="title">
<string>About</string>
@@ -181,7 +166,7 @@
</layout>
</widget>
</item>
<item>
<item row="3" column="0">
<widget class="QGroupBox" name="updatesGroupBox">
<property name="title">
<string>Updates</string>
@@ -232,7 +217,7 @@
</layout>
</widget>
</item>
<item>
<item row="4" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -247,6 +232,16 @@
</item>
</layout>
</widget>
<tabstops>
<tabstop>autostartCheckBox</tabstop>
<tabstop>desktopNotificationsCheckBox</tabstop>
<tabstop>monoIconsCheckBox</tabstop>
<tabstop>ignoredFilesButton</tabstop>
<tabstop>newFolderLimitCheckBox</tabstop>
<tabstop>newFolderLimitSpinBox</tabstop>
<tabstop>crashreporterCheckBox</tabstop>
<tabstop>restartButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
+1
Ver Arquivo
@@ -110,6 +110,7 @@ void IgnoreListEditor::slotUpdateLocalIgnoreList()
QMessageBox::warning(this, tr("Could not open file"),
tr("Cannot write changes to '%1'.").arg(ignoreFile));
}
ignores.close(); //close the file before reloading stuff.
FolderMan * folderMan = FolderMan::instance();
+21
Ver Arquivo
@@ -119,6 +119,27 @@ void NetworkSettings::loadBWLimitSettings()
{
ConfigFile cfgFile;
#if QT_VERSION < QT_VERSION_CHECK(5,3,3)
// QNAM bandwidth limiting only works with versions of Qt greater or equal to 5.3.3
// (It needs Qt commits 097b641 and b99fa32)
const char *v = qVersion(); // "x.y.z";
if (QLatin1String(v) < QLatin1String("5.3.3")) {
QString tooltip = tr("Qt >= 5.4 is required in order to use the bandwidth limit");
_ui->downloadBox->setEnabled(false);
_ui->uploadBox->setEnabled(false);
_ui->downloadBox->setToolTip(tooltip);
_ui->uploadBox->setToolTip(tooltip);
_ui->noDownloadLimitRadioButton->setChecked(true);
_ui->noUploadLimitRadioButton->setChecked(true);
if (cfgFile.useUploadLimit() != 0 || cfgFile.useDownloadLimit() != 0) {
// Update from old mirall that was using neon propagator jobs.
saveBWLimitSettings();
}
return;
}
#endif
int useDownloadLimit = cfgFile.useDownloadLimit();
if ( useDownloadLimit >= 1 ) {
_ui->downloadLimitRadioButton->setChecked(true);
+5 -5
Ver Arquivo
@@ -179,7 +179,7 @@
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QGroupBox" name="groupBox">
<widget class="QGroupBox" name="downloadBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -258,7 +258,7 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<widget class="QGroupBox" name="uploadBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
@@ -284,9 +284,6 @@
<property name="text">
<string>Limit automatically</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
@@ -294,6 +291,9 @@
<property name="text">
<string>No limit</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
+124
Ver Arquivo
@@ -0,0 +1,124 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 "ocsjob.h"
#include "networkjobs.h"
#include "account.h"
#include "json.h"
#include <QBuffer>
namespace OCC {
OcsJob::OcsJob(AccountPtr account)
: AbstractNetworkJob(account, "")
{
_passStatusCodes.append(100);
setIgnoreCredentialFailure(true);
}
void OcsJob::setVerb(const QByteArray &verb)
{
_verb = verb;
}
void OcsJob::addParam(const QString &name, const QString &value)
{
_params.append(qMakePair(name, value));
}
void OcsJob::addPassStatusCode(int code)
{
_passStatusCodes.append(code);
}
void OcsJob::appendPath(const QString &id)
{
setPath(path() + QLatin1Char('/') + id);
}
void OcsJob::start()
{
QNetworkRequest req;
req.setRawHeader("Ocs-APIREQUEST", "true");
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
QUrl url = Account::concatUrlPath(account()->url(), path());
QBuffer *buffer = new QBuffer;
if (_verb == "GET") {
url.setQueryItems(_params);
} else if (_verb == "POST" || _verb == "PUT") {
// Url encode the _postParams and put them in a buffer.
QByteArray postData;
Q_FOREACH(auto tmp, _params) {
if (!postData.isEmpty()) {
postData.append("&");
}
postData.append(QUrl::toPercentEncoding(tmp.first));
postData.append("=");
postData.append(QUrl::toPercentEncoding(tmp.second));
}
buffer->setData(postData);
}
//We want json data
auto queryItems = url.queryItems();
queryItems.append(qMakePair(QString::fromLatin1("format"), QString::fromLatin1("json")));
url.setQueryItems(queryItems);
setReply(davRequest(_verb, url, req, buffer));
setupConnections(reply());
buffer->setParent(reply());
AbstractNetworkJob::start();
}
bool OcsJob::finished()
{
const QString replyData = reply()->readAll();
bool success;
QVariantMap json = QtJson::parse(replyData, success).toMap();
if (!success) {
qDebug() << "Could not parse reply to"
<< _verb
<< Account::concatUrlPath(account()->url(), path())
<< _params
<< ":" << replyData;
}
QString message;
const int statusCode = getJsonReturnCode(json, message);
if (!_passStatusCodes.contains(statusCode)) {
qDebug() << "Reply to"
<< _verb
<< Account::concatUrlPath(account()->url(), path())
<< _params
<< "has unexpected status code:" << statusCode << replyData;
emit ocsError(statusCode, message);
} else {
emit jobFinished(json);
}
return true;
}
int OcsJob::getJsonReturnCode(const QVariantMap &json, QString &message)
{
//TODO proper checking
int code = json.value("ocs").toMap().value("meta").toMap().value("statuscode").toInt();
message = json.value("ocs").toMap().value("meta").toMap().value("message").toString();
return code;
}
}
+131
Ver Arquivo
@@ -0,0 +1,131 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 OCSJOB_H
#define OCSJOB_H
#include "accountfwd.h"
#include "abstractnetworkjob.h"
#include <QVector>
#include <QList>
#include <QPair>
#include <QUrl>
namespace OCC {
/**
* @brief The OcsShareJob class
* @ingroup gui
*
* Base class for jobs that talk to the OCS endpoints on the server.
* All the communication logic is handled in this class.
*
* All OCS jobs (e.g. sharing) should extend this class.
*/
class OcsJob : public AbstractNetworkJob {
Q_OBJECT
protected:
explicit OcsJob(AccountPtr account);
/**
* Set the verb for the job
*
* @param verb currently supported PUT POST DELETE
*/
void setVerb(const QByteArray& verb);
/**
* Add a new parameter to the request.
* Depending on the verb this is GET or POST parameter
*
* @param name The name of the parameter
* @param value The value of the parameter
*/
void addParam(const QString& name, const QString &value);
/**
* Set the post parameters
*
* @param postParams list of pairs to add (urlEncoded) to the body of the
* request
*/
void setPostParams(const QList<QPair<QString, QString> >& postParams);
/**
* List of expected statuscodes for this request
* A warning will be printed to the debug log if a different status code is
* encountered
*
* @param code Accepted status code
*/
void addPassStatusCode(int code);
/**
* The base path for an OcsJob is always the same. But it could be the case that
* certain operations need to append something to the URL.
*
* This function appends the common id. so <PATH>/<ID>
*/
void appendPath(const QString &id);
public:
/**
* Parse the response and return the status code and the message of the
* reply (metadata)
*
* @param json The reply from OCS
* @param message The message that is set in the metadata
* @return The statuscode of the OCS response
*/
static int getJsonReturnCode(const QVariantMap &json, QString &message);
protected slots:
/**
* Start the OCS request
*/
void start() Q_DECL_OVERRIDE;
signals:
/**
* Result of the OCS request
*
* @param reply the reply
*/
void jobFinished(QVariantMap reply);
/**
* The status code was not one of the expected (passing)
* status code for this command
*
* @param statusCode The actual status code
* @param message The message provided by the server
*/
void ocsError(int statusCode, const QString &message);
private slots:
virtual bool finished() Q_DECL_OVERRIDE;
private:
QByteArray _verb;
QList<QPair<QString, QString> > _params;
QVector<int> _passStatusCodes;
};
}
#endif // OCSJOB_H
+46
Ver Arquivo
@@ -0,0 +1,46 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@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 "ocsshareejob.h"
namespace OCC {
OcsShareeJob::OcsShareeJob(AccountPtr account)
: OcsJob(account)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/sharees");
connect(this, SIGNAL(jobFinished(QVariantMap)), SLOT(jobDone(QVariantMap)));
}
void OcsShareeJob::getSharees(const QString &search,
const QString &itemType,
int page,
int perPage)
{
setVerb("GET");
addParam(QString::fromLatin1("search"), search);
addParam(QString::fromLatin1("itemType"), itemType);
addParam(QString::fromLatin1("page"), QString::number(page));
addParam(QString::fromLatin1("perPage"), QString::number(perPage));
start();
}
void OcsShareeJob::jobDone(const QVariantMap &reply)
{
emit shareeJobFinished(reply);
}
}
+55
Ver Arquivo
@@ -0,0 +1,55 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@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 OCSSHAREEJOB_H
#define OCSSHAREEJOB_H
#include "ocsjob.h"
#include <QVariantMap>
namespace OCC {
/**
* @brief The OcsShareeJob class
* @ingroup gui
*
* Fetching sharees from the OCS Sharee API
*/
class OcsShareeJob : public OcsJob {
Q_OBJECT
public:
explicit OcsShareeJob(AccountPtr account);
/**
* Get a list of sharees
*
* @param path Path to request shares for (default all shares)
*/
void getSharees(const QString& search, const QString& itemType, int page = 1, int perPage = 50);
signals:
/**
* Result of the OCS request
*
* @param reply The reply
*/
void shareeJobFinished(const QVariantMap &reply);
private slots:
void jobDone(const QVariantMap &reply);
};
}
#endif // OCSSHAREEJOB_H
+135
Ver Arquivo
@@ -0,0 +1,135 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 "ocssharejob.h"
#include "networkjobs.h"
#include "account.h"
#include "json.h"
#include <QBuffer>
namespace OCC {
OcsShareJob::OcsShareJob(AccountPtr account)
: OcsJob(account)
{
setPath("ocs/v1.php/apps/files_sharing/api/v1/shares");
connect(this, SIGNAL(jobFinished(QVariantMap)), this, SLOT(jobDone(QVariantMap)));
}
void OcsShareJob::getShares(const QString &path)
{
setVerb("GET");
addParam(QString::fromLatin1("path"), path);
addPassStatusCode(404);
start();
}
void OcsShareJob::deleteShare(const QString &shareId)
{
appendPath(shareId);
setVerb("DELETE");
start();
}
void OcsShareJob::setExpireDate(const QString &shareId, const QDate &date)
{
appendPath(shareId);
setVerb("PUT");
if (date.isValid()) {
addParam(QString::fromLatin1("expireDate"), date.toString("yyyy-MM-dd"));
} else {
addParam(QString::fromLatin1("expireDate"), QString());
}
_value = date;
start();
}
void OcsShareJob::setPassword(const QString &shareId, const QString &password)
{
appendPath(shareId);
setVerb("PUT");
addParam(QString::fromLatin1("password"), password);
_value = password;
start();
}
void OcsShareJob::setPublicUpload(const QString &shareId, bool publicUpload)
{
appendPath(shareId);
setVerb("PUT");
const QString value = QString::fromLatin1(publicUpload ? "true" : "false");
addParam(QString::fromLatin1("publicUpload"), value);
_value = publicUpload;
start();
}
void OcsShareJob::setPermissions(const QString &shareId,
const Share::Permissions permissions)
{
appendPath(shareId);
setVerb("PUT");
addParam(QString::fromLatin1("permissions"), QString::number(permissions));
_value = (int)permissions;
start();
}
void OcsShareJob::createLinkShare(const QString &path,
const QString &password)
{
setVerb("POST");
addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("shareType"), QString::number(Share::TypeLink));
if (!password.isEmpty()) {
addParam(QString::fromLatin1("password"), password);
}
addPassStatusCode(403);
start();
}
void OcsShareJob::createShare(const QString& path,
const Share::ShareType shareType,
const QString& shareWith,
const Share::Permissions permissions)
{
setVerb("POST");
addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("shareType"), QString::number(shareType));
addParam(QString::fromLatin1("shareWith"), shareWith);
addParam(QString::fromLatin1("permissions"), QString::number(permissions));
start();
}
void OcsShareJob::jobDone(QVariantMap reply)
{
emit shareJobFinished(reply, _value);
}
}
+127
Ver Arquivo
@@ -0,0 +1,127 @@
/*
* Copyright (C) by Roeland Jago Douma <roeland@famdouma.nl>
*
* 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 OCSSHAREJOB_H
#define OCSSHAREJOB_H
#include "ocsjob.h"
#include "share.h"
#include <QVector>
#include <QList>
#include <QPair>
namespace OCC {
/**
* @brief The OcsShareJob class
* @ingroup gui
*
* Handle talking to the OCS Share API.
* For creation, deletion and modification of shares.
*/
class OcsShareJob : public OcsJob {
Q_OBJECT
public:
/**
* Constructor for new shares or listing of shares
*/
explicit OcsShareJob(AccountPtr account);
/**
* Get all the shares
*
* @param path Path to request shares for (default all shares)
*/
void getShares(const QString& path = "");
/**
* Delete the current Share
*/
void deleteShare(const QString &shareId);
/**
* Set the expiration date of a share
*
* @param date The expire date, if this date is invalid the expire date
* will be removed
*/
void setExpireDate(const QString &shareId, const QDate& date);
/**
* Set the password of a share
*
* @param password The password of the share, if the password is empty the
* share will be removed
*/
void setPassword(const QString &shareId, const QString& password);
/**
* Void set the share to be public upload
*
* @param publicUpload Set or remove public upload
*/
void setPublicUpload(const QString &shareId, bool publicUpload);
/**
* Set the permissions
*
* @param permissions
*/
void setPermissions(const QString &shareId,
const Share::Permissions permissions);
/**
* Create a new link share
*
* @param path The path of the file/folder to share
* @param password Optionally a password for the share
*/
void createLinkShare(const QString& path,
const QString& password = "");
/**
* Create a new share
*
* @param path The path of the file/folder to share
* @param shareType The type of share (user/group/link/federated)
* @param shareWith The uid/gid/federated id to share with
* @param permissions The permissions the share will have
*/
void createShare(const QString& path,
const Share::ShareType shareType,
const QString& shareWith = "",
const Share::Permissions permissions = Share::PermissionRead);
signals:
/**
* Result of the OCS request
* The value parameter is only set if this was a put request.
* e.g. if we set the password to 'foo' the QVariant will hold a QString with 'foo'.
* This is needed so we can update the share objects properly
*
* @param reply The reply
* @param value To what did we set a variable (if we set any).
*/
void shareJobFinished(QVariantMap reply, QVariant value);
private slots:
void jobDone(QVariantMap reply);
private:
QVariant _value;
};
}
#endif // OCSSHAREJOB_H
+19 -13
Ver Arquivo
@@ -150,7 +150,7 @@ void ownCloudGui::slotOpenSettingsDialog()
{
// if account is set up, start the configuration wizard.
if( !AccountManager::instance()->accounts().isEmpty() ) {
if (_settingsDialog.isNull() || !_settingsDialog->isVisible()) {
if (_settingsDialog.isNull() || QApplication::activeWindow() != _settingsDialog) {
slotShowSettings();
} else {
_settingsDialog->close();
@@ -204,6 +204,10 @@ void ownCloudGui::slotSyncStateChange( Folder* folder )
if (result.status() == SyncResult::Success || result.status() == SyncResult::Error) {
Logger::instance()->enterNextLogFile();
}
if (result.status() == SyncResult::NotYetStarted) {
_settingsDialog->slotRefreshActivity( folder->accountState() );
}
}
void ownCloudGui::slotFoldersChanged()
@@ -286,7 +290,7 @@ void ownCloudGui::slotComputeOverallSyncStatus()
foreach(Folder* folder, map.values()) {
qDebug() << "Folder in overallStatus Message: " << folder << " with name " << folder->alias();
QString folderMessage = folderMan->statusToString(folder->syncResult().status(), folder->syncPaused());
allStatusStrings += tr("Folder %1: %2").arg(folder->alias(), folderMessage);
allStatusStrings += tr("Folder %1: %2").arg(folder->aliasGui(), folderMessage);
}
trayMessage = allStatusStrings.join(QLatin1String("\n"));
#endif
@@ -331,13 +335,6 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
menu->addAction(tr("Managed Folders:"))->setDisabled(true);
}
// If there can only be a single sync folder, showing the alias is
// unnecessary.
QString folderName = folder->alias();
if (singleSyncFolder) {
folderName = Theme::instance()->appNameGUI();
}
QAction *action = new QAction( tr("Open folder '%1'").arg(folder->path()), this );
connect(action, SIGNAL(triggered()),_folderOpenActionMapper, SLOT(map()));
_folderOpenActionMapper->setMapping( action, folder->alias() );
@@ -383,9 +380,7 @@ void ownCloudGui::setupContextMenu()
_tray->hide();
}
_contextMenu->clear();
_recentActionsMenu->clear();
_recentActionsMenu->addAction(tr("None."));
_recentActionsMenu->addAction(_actionRecent);
slotRebuildRecentMenus();
} else {
_contextMenu.reset(new QMenu());
_recentActionsMenu = new QMenu(tr("Recent Changes"), _contextMenu.data());
@@ -552,6 +547,16 @@ void ownCloudGui::slotRebuildRecentMenus()
_recentActionsMenu->addAction(_actionRecent);
}
/// Returns true if the completion of a given item should show up in the
/// 'Recent Activity' menu
static bool shouldShowInRecentsMenu(const SyncFileItem& item)
{
return
!Progress::isIgnoredKind(item._status)
&& item._instruction != CSYNC_INSTRUCTION_EVAL
&& item._instruction != CSYNC_INSTRUCTION_NONE;
}
void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo& progress)
{
@@ -577,7 +582,8 @@ void ownCloudGui::slotUpdateProgress(const QString &folder, const ProgressInfo&
_actionRecent->setIcon( QIcon() ); // Fixme: Set a "in-progress"-item eventually.
if (!progress._lastCompletedItem.isEmpty() && !Progress::isIgnoredKind(progress._lastCompletedItem._status)) {
if (!progress._lastCompletedItem.isEmpty()
&& shouldShowInRecentsMenu(progress._lastCompletedItem)) {
if (Progress::isWarningKind(progress._lastCompletedItem._status)) {
// display a warn icon if warnings happened.
+91 -55
Ver Arquivo
@@ -27,6 +27,7 @@
#include "folder.h"
#include "openfilemanager.h"
#include "owncloudpropagator.h"
#include "activityitemdelegate.h"
#include "ui_protocolwidget.h"
@@ -65,17 +66,24 @@ ProtocolWidget::ProtocolWidget(QWidget *parent) :
#if defined(Q_OS_MAC)
_ui->_treeWidget->setMinimumWidth(400);
#endif
_ui->_headerLabel->setText(tr("Local sync protocol"));
connect(this, SIGNAL(guiLog(QString,QString)), Logger::instance(), SIGNAL(guiLog(QString,QString)));
QPushButton *copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
copyBtn->setEnabled(true);
connect(copyBtn, SIGNAL(clicked()), SIGNAL(copyToClipboard()));
_retrySyncBtn = _ui->_dialogButtonBox->addButton(tr("Retry Sync"), QDialogButtonBox::ActionRole);
_retrySyncBtn->setEnabled(false);
connect(_retrySyncBtn, SIGNAL(clicked()), SLOT(slotRetrySync()));
_copyBtn = _ui->_dialogButtonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
_copyBtn->setToolTip( tr("Copy the activity list to the clipboard."));
_copyBtn->setEnabled(false);
connect(_copyBtn, SIGNAL(clicked()), SLOT(copyToClipboard()));
// this view is used to display all errors such as real errors, soft errors and ignored files
// it is instantiated here, but made accessible via the method issueWidget() so that it can
// be embedded into another gui element.
_issueItemView = new QTreeWidget(this);
header.removeLast();
_issueItemView->setHeaderLabels( header );
_issueItemView->setColumnWidth(1, 180);
_issueItemView->setColumnCount(4);
_issueItemView->setRootIsDecorated(false);
_issueItemView->setTextElideMode(Qt::ElideMiddle);
_issueItemView->header()->setObjectName("ActivityErrorListHeader");
}
ProtocolWidget::~ProtocolWidget()
@@ -83,38 +91,7 @@ ProtocolWidget::~ProtocolWidget()
delete _ui;
}
void ProtocolWidget::copyToClipboard()
{
QString text;
QTextStream ts(&text);
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
for (int i = 0; i < topLevelItems; i++) {
QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i);
ts << left
// time stamp
<< qSetFieldWidth(10)
<< child->data(0,Qt::DisplayRole).toString()
// file name
<< qSetFieldWidth(64)
<< child->data(1,Qt::DisplayRole).toString()
// folder
<< qSetFieldWidth(30)
<< child->data(2, Qt::DisplayRole).toString()
// action
<< qSetFieldWidth(15)
<< child->data(3, Qt::DisplayRole).toString()
// size
<< qSetFieldWidth(10)
<< child->data(4, Qt::DisplayRole).toString()
<< qSetFieldWidth(0)
<< endl;
}
QApplication::clipboard()->setText(text);
emit guiLog(tr("Copied to clipboard"), tr("The sync status has been copied to the clipboard."));
}
#if 0
void ProtocolWidget::slotRetrySync()
{
FolderMan *folderMan = FolderMan::instance();
@@ -133,6 +110,7 @@ void ProtocolWidget::slotRetrySync()
folderMan->slotScheduleAllFolders();
}
#endif
void ProtocolWidget::showEvent(QShowEvent *ev)
{
@@ -158,8 +136,18 @@ void ProtocolWidget::cleanIgnoreItems(const QString& folder)
itemCnt--;
}
// limit also in the protocol widget
itemCnt = _issueItemView->topLevelItemCount();
// Limit the number of items in the issue view
while(itemCnt > 2000) {
delete _issueItemView->takeTopLevelItem(itemCnt - 1);
itemCnt--;
}
// clean up the issue list
for( int cnt = itemCnt-1; cnt >=0 ; cnt-- ) {
QTreeWidgetItem *item = _ui->_treeWidget->topLevelItem(cnt);
QTreeWidgetItem *item = _issueItemView->topLevelItem(cnt);
bool isErrorItem = item->data(0, IgnoredIndicatorRole).toBool();
QString itemFolder = item->data(2, Qt::UserRole).toString();
if( isErrorItem && itemFolder == folder ) {
@@ -192,15 +180,6 @@ void ProtocolWidget::slotOpenFile( QTreeWidgetItem *item, int )
}
}
QString ProtocolWidget::fixupFilename( const QString& name )
{
if( Utility::isMac() ) {
QString n(name);
return n.replace(QChar(':'), QChar('/'));
}
return name;
}
QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& folder, const SyncFileItem& item)
{
auto f = FolderMan::instance()->folder(folder);
@@ -214,7 +193,7 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
const QString longTimeStr = timeString(timestamp, QLocale::LongFormat);
columns << timeStr;
columns << fixupFilename(item._originalFile);
columns << Utility::fileNameForGuiUse(item._originalFile);
columns << f->shortGuiPath();
// If the error string is set, it's prefered because it is a useful user message.
@@ -242,6 +221,7 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
twitem->setData(0, IgnoredIndicatorRole, true);
}
twitem->setData(0, Qt::SizeHintRole, QSize(0, ActivityItemDelegate::rowHeight()));
twitem->setIcon(0, icon);
twitem->setToolTip(0, longTimeStr);
twitem->setToolTip(1, item._file);
@@ -252,6 +232,7 @@ QTreeWidgetItem* ProtocolWidget::createCompletedTreewidgetItem(const QString& fo
void ProtocolWidget::computeResyncButtonEnabled()
{
#if 0
FolderMan *folderMan = FolderMan::instance();
Folder::Map folders = folderMan->map();
@@ -272,6 +253,7 @@ void ProtocolWidget::computeResyncButtonEnabled()
_retrySyncBtn->setEnabled(enabled);
_retrySyncBtn->setToolTip(t);
#endif
}
@@ -295,11 +277,65 @@ void ProtocolWidget::slotItemCompleted(const QString &folder, const SyncFileItem
QTreeWidgetItem *line = createCompletedTreewidgetItem(folder, item);
if(line) {
_ui->_treeWidget->insertTopLevelItem(0, line);
if (!_copyBtn->isEnabled()) {
_copyBtn->setEnabled(true);
if( item.hasErrorStatus() ) {
_issueItemView->insertTopLevelItem(0, line);
} else {
_ui->_treeWidget->insertTopLevelItem(0, line);
}
}
}
void ProtocolWidget::storeSyncActivity(QTextStream& ts)
{
int topLevelItems = _ui->_treeWidget->topLevelItemCount();
for (int i = 0; i < topLevelItems; i++) {
QTreeWidgetItem *child = _ui->_treeWidget->topLevelItem(i);
ts << left
// time stamp
<< qSetFieldWidth(10)
<< child->data(0,Qt::DisplayRole).toString()
// file name
<< qSetFieldWidth(64)
<< child->data(1,Qt::DisplayRole).toString()
// folder
<< qSetFieldWidth(30)
<< child->data(2, Qt::DisplayRole).toString()
// action
<< qSetFieldWidth(15)
<< child->data(3, Qt::DisplayRole).toString()
// size
<< qSetFieldWidth(10)
<< child->data(4, Qt::DisplayRole).toString()
<< qSetFieldWidth(0)
<< endl;
}
}
void ProtocolWidget::storeSyncIssues(QTextStream& ts)
{
int topLevelItems = _issueItemView->topLevelItemCount();
for (int i = 0; i < topLevelItems; i++) {
QTreeWidgetItem *child = _issueItemView->topLevelItem(i);
ts << left
// time stamp
<< qSetFieldWidth(10)
<< child->data(0,Qt::DisplayRole).toString()
// file name
<< qSetFieldWidth(64)
<< child->data(1,Qt::DisplayRole).toString()
// folder
<< qSetFieldWidth(30)
<< child->data(2, Qt::DisplayRole).toString()
// action
<< qSetFieldWidth(15)
<< child->data(3, Qt::DisplayRole).toString()
<< qSetFieldWidth(0)
<< endl;
}
}
}
+6 -9
Ver Arquivo
@@ -45,28 +45,26 @@ public:
~ProtocolWidget();
QSize sizeHint() const { return ownCloudGui::settingsDialogSize(); }
QTreeWidget *issueWidget() { return _issueItemView; }
void storeSyncActivity(QTextStream& ts);
void storeSyncIssues(QTextStream& ts);
public slots:
void slotProgressInfo( const QString& folder, const ProgressInfo& progress );
void slotItemCompleted( const QString& folder, const SyncFileItem& item, const PropagatorJob& job);
void slotOpenFile( QTreeWidgetItem* item, int );
protected slots:
void copyToClipboard();
void slotRetrySync();
protected:
void showEvent(QShowEvent *);
void hideEvent(QHideEvent *);
signals:
void guiLog(const QString&, const QString&);
void copyToClipboard();
private:
void setSyncResultStatus(const SyncResult& result );
void cleanIgnoreItems( const QString& folder );
void computeResyncButtonEnabled();
QString fixupFilename( const QString& name );
QTreeWidgetItem* createCompletedTreewidgetItem(const QString &folder, const SyncFileItem &item );
@@ -74,8 +72,7 @@ private:
const int IgnoredIndicatorRole;
Ui::ProtocolWidget *_ui;
QPushButton *_retrySyncBtn;
QPushButton *_copyBtn;
QTreeWidget *_issueItemView;
};
}
+43 -49
Ver Arquivo
@@ -13,59 +13,53 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Sync Activity</string>
<widget class="QLabel" name="_headerLabel">
<property name="text">
<string>TextLabel</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QTreeWidget" name="_treeWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="columnCount">
<number>4</number>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">2</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">3</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">4</string>
</property>
</column>
</widget>
</item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="_dialogButtonBox">
<property name="standardButtons">
<set>QDialogButtonBox::NoButton</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTreeWidget" name="_treeWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="uniformRowHeights">
<bool>true</bool>
</property>
<property name="columnCount">
<number>4</number>
</property>
<column>
<property name="text">
<string notr="true">1</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">2</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">3</string>
</property>
</column>
<column>
<property name="text">
<string notr="true">4</string>
</property>
</column>
</widget>
</item>
<item row="2" column="1">
<widget class="QDialogButtonBox" name="_dialogButtonBox"/>
</item>
</layout>
</widget>
<resources/>
+3 -2
Ver Arquivo
@@ -102,9 +102,10 @@ void QuotaInfo::slotUpdateLastQuota(const QVariantMap &result)
{
// The server can return fractional bytes (#1374)
// <d:quota-available-bytes>1374532061.2</d:quota-available-bytes>
quint64 avail = result["quota-available-bytes"].toDouble();
qint64 avail = result["quota-available-bytes"].toDouble();
_lastQuotaUsedBytes = result["quota-used-bytes"].toDouble();
_lastQuotaTotalBytes = _lastQuotaUsedBytes + avail;
// negative value of the available quota have special meaning (#3940)
_lastQuotaTotalBytes = avail >= 0 ? _lastQuotaUsedBytes + avail : avail;
emit quotaUpdated(_lastQuotaTotalBytes, _lastQuotaUsedBytes);
_jobRestartTimer.start(defaultIntervalT);
_lastQuotaRecieved = QDateTime::currentDateTime();

Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais