Merge lp:keeper/devel into lp:keeper

Proposed by dobey
Status: Merged
Approved by: Charles Kerr
Approved revision: 129
Merged at revision: 94
Proposed branch: lp:keeper/devel
Merge into: lp:keeper
Diff against target: 11619 lines (+7871/-1027)
124 files modified
CMakeLists.txt (+1/-1)
data/CMakeLists.txt (+6/-0)
data/helper-registry.json.in (+6/-1)
debian/changelog (+14/-0)
debian/control (+5/-1)
debian/keeper.install (+3/-1)
include/client/client.h (+18/-4)
include/client/keeper-errors.h (+57/-0)
include/client/keeper-items.h (+102/-0)
include/helper/backup-helper.h (+1/-0)
include/helper/data-dir-registry.h (+2/-0)
include/helper/helper.h (+3/-1)
include/helper/metadata.h (+6/-27)
include/helper/registry.h (+1/-0)
include/helper/restore-helper.h (+62/-0)
src/cli/CMakeLists.txt (+4/-0)
src/cli/command-line-client-view.cpp (+194/-0)
src/cli/command-line-client-view.h (+60/-0)
src/cli/command-line-client.cpp (+250/-0)
src/cli/command-line-client.h (+58/-0)
src/cli/command-line.cpp (+234/-0)
src/cli/command-line.h (+55/-0)
src/cli/main.cpp (+40/-46)
src/client/CMakeLists.txt (+22/-0)
src/client/client.cpp (+223/-60)
src/client/items.cpp (+196/-0)
src/client/keeper-errors.cpp (+74/-0)
src/client/qml-plugin/CMakeLists.txt (+1/-1)
src/helper/CMakeLists.txt (+18/-1)
src/helper/backup-helper.cpp (+30/-8)
src/helper/data-dir-registry.cpp (+54/-22)
src/helper/folder-backup.sh.in (+1/-1)
src/helper/folder-restore.sh.in (+23/-0)
src/helper/helper.cpp (+6/-8)
src/helper/metadata.cpp (+45/-50)
src/helper/restore-helper.cpp (+377/-0)
src/qdbus-stubs/CMakeLists.txt (+19/-1)
src/qdbus-stubs/com.canonical.keeper.User.xml (+47/-3)
src/qdbus-stubs/dbus-types.h (+6/-2)
src/qdbus-stubs/org.freedesktop.DBus.Properties.xml (+27/-0)
src/service/CMakeLists.txt (+4/-0)
src/service/backup-choices.cpp (+20/-12)
src/service/backup-choices.h (+2/-1)
src/service/keeper-helper.cpp (+5/-2)
src/service/keeper-task-backup.cpp (+40/-11)
src/service/keeper-task-backup.h (+4/-2)
src/service/keeper-task-restore.cpp (+130/-0)
src/service/keeper-task-restore.h (+46/-0)
src/service/keeper-task.cpp (+96/-23)
src/service/keeper-task.h (+10/-2)
src/service/keeper-user.cpp (+37/-45)
src/service/keeper-user.h (+8/-6)
src/service/keeper.cpp (+365/-62)
src/service/keeper.h (+19/-5)
src/service/main.cpp (+2/-1)
src/service/manifest.cpp (+248/-0)
src/service/manifest.h (+51/-0)
src/service/metadata-provider.h (+10/-2)
src/service/private/keeper-task_p.h (+10/-2)
src/service/restore-choices.cpp (+55/-93)
src/service/restore-choices.h (+10/-5)
src/service/task-manager.cpp (+170/-25)
src/service/task-manager.h (+12/-6)
src/storage-framework/CMakeLists.txt (+3/-0)
src/storage-framework/downloader.h (+46/-0)
src/storage-framework/sf-downloader.cpp (+54/-0)
src/storage-framework/sf-downloader.h (+48/-0)
src/storage-framework/sf-uploader.cpp (+12/-2)
src/storage-framework/sf-uploader.h (+3/-0)
src/storage-framework/storage_framework_client.cpp (+444/-36)
src/storage-framework/storage_framework_client.h (+25/-5)
src/storage-framework/uploader.h (+1/-0)
src/tar/CMakeLists.txt (+72/-27)
src/tar/tar-creator.cpp (+31/-29)
src/tar/untar-main.cpp (+169/-0)
src/tar/untar.cpp (+144/-0)
src/tar/untar.h (+38/-0)
src/util/connection-helper.h (+9/-4)
tests/CMakeLists.txt (+9/-1)
tests/com_canonical_keeper.py (+80/-45)
tests/dbusmock/keeper-template-test.cpp (+113/-102)
tests/fakes/CMakeLists.txt (+14/-0)
tests/fakes/fake-restore-helper.cpp (+136/-0)
tests/fakes/fake-restore-helper.h (+27/-0)
tests/fakes/folder-restore.sh.in (+41/-0)
tests/fakes/helper-test.sh.in (+16/-0)
tests/fakes/upstart/upstart-job-mock.cpp (+4/-1)
tests/integration/helpers/CMakeLists.txt (+94/-1)
tests/integration/helpers/helpers-test-failure.cpp (+20/-8)
tests/integration/helpers/helpers-test.cc (+289/-9)
tests/integration/helpers/restore-test.cpp (+126/-0)
tests/integration/helpers/state-change-test-manager.h (+55/-0)
tests/integration/helpers/state-change-test.cpp (+56/-0)
tests/integration/helpers/state-test-helper.h (+55/-0)
tests/integration/helpers/test-helpers-base.cpp (+363/-135)
tests/integration/helpers/test-helpers-base.h (+22/-13)
tests/unit/CMakeLists.txt (+3/-0)
tests/unit/helper/CMakeLists.txt (+1/-5)
tests/unit/helper/speed-test.cpp (+3/-3)
tests/unit/manifest/CMakeLists.txt (+45/-0)
tests/unit/manifest/manifest-test.cpp (+170/-0)
tests/unit/metadata-providers/CMakeLists.txt (+3/-0)
tests/unit/metadata-providers/user-dirs-test.cpp (+19/-12)
tests/unit/metadata/CMakeLists.txt (+44/-0)
tests/unit/metadata/metadata-json-test.cpp (+100/-0)
tests/unit/storage-framework/CMakeLists.txt (+73/-0)
tests/unit/storage-framework/create-uploader-test.cpp (+147/-0)
tests/unit/storage-framework/folders-test.cpp (+109/-0)
tests/unit/tar/CMakeLists.txt (+104/-29)
tests/unit/tar/keeper-tar-test.cpp (+5/-5)
tests/unit/tar/keeper-untar-test.cpp (+232/-0)
tests/unit/tar/ku-invoke-nobus.sh.in (+1/-0)
tests/unit/tar/ku-invoke.sh.in (+1/-0)
tests/unit/tar/tar-creator-libarchive-failure-test.cpp (+5/-1)
tests/unit/tar/tar-creator-test.cpp (+1/-1)
tests/unit/tar/untar-test.cpp (+120/-0)
tests/utils/CMakeLists.txt (+2/-0)
tests/utils/file-utils.cpp (+88/-6)
tests/utils/file-utils.h (+5/-1)
tests/utils/keeper-dbusmock-fixture.h (+9/-0)
tests/utils/main.cpp (+1/-1)
tests/utils/storage-framework-local.cpp (+207/-0)
tests/utils/storage-framework-local.h (+48/-0)
tests/utils/xdg-user-dirs-sandbox.cpp (+1/-1)
To merge this branch: bzr merge lp:keeper/devel
Reviewer Review Type Date Requested Status
Charles Kerr (community) Approve
Review via email: mp+318388@code.launchpad.net

Description of the change

This is just to merge what's in devel (which has apparently already mostly been released to zesty/xenial+overlay), into trunk. Only missing piece from release is for the UAL API/ABI change.

To post a comment you must log in.
lp:keeper/devel updated
130. By dobey

Update the changelog for Ken's change, and to keep the already released version when releasing to trunk.
Fix comment starter removed in a previous change.

Approved by unity-api-1-bot, Charles Kerr.

Revision history for this message
Charles Kerr (charlesk) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2016-08-26 09:30:11 +0000
3+++ CMakeLists.txt 2017-02-27 19:05:17 +0000
4@@ -272,7 +272,7 @@
5 include(CTest)
6 enable_testing()
7
8-include(EnableCoverageReport)
9+find_package(CoverageReport)
10
11 add_subdirectory(data)
12 add_subdirectory(src)
13
14=== modified file 'data/CMakeLists.txt'
15--- data/CMakeLists.txt 2016-08-10 07:43:36 +0000
16+++ data/CMakeLists.txt 2017-02-27 19:05:17 +0000
17@@ -6,6 +6,12 @@
18 FOLDER_BACKUP_EXEC
19 ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}/folder-backup.sh
20 )
21+
22+set(
23+ FOLDER_RESTORE_EXEC
24+ ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}/folder-restore.sh
25+)
26+
27 configure_file(
28 ${HELPER_REGISTRY_FILENAME}.in
29 ${HELPER_REGISTRY_FILENAME}
30
31=== modified file 'data/helper-registry.json.in'
32--- data/helper-registry.json.in 2016-08-09 04:20:33 +0000
33+++ data/helper-registry.json.in 2017-02-27 19:05:17 +0000
34@@ -4,5 +4,10 @@
35 "@FOLDER_BACKUP_EXEC@",
36 "${subtype}"
37 ]
38- }
39+ ,
40+ "restore-urls": [
41+ "@FOLDER_RESTORE_EXEC@",
42+ "${subtype}"
43+ ]
44+ }
45 }
46
47=== modified file 'debian/changelog'
48--- debian/changelog 2016-09-15 19:20:14 +0000
49+++ debian/changelog 2017-02-27 19:05:17 +0000
50@@ -1,3 +1,17 @@
51+keeper (0.1.1-0ubuntu1) UNRELEASED; urgency=medium
52+
53+ [ Ken Vandine ]
54+ * Update to handle the API/ABI break in ubuntu-app-launch.
55+
56+ -- Rodney Dawes <rodney.dawes@canonical.com> Mon, 27 Feb 2017 13:46:45 -0500
57+
58+keeper (0.1.0+17.04.20170213.1-0ubuntu1) zesty; urgency=medium
59+
60+ [ Charles Kerr, Xavi Garcia, Xavi Garcia Mena ]
61+ * Adds integration tests for restore and restore cancellation.
62+
63+ -- Xavi Garcia <xavi.garcia.mena@canonical.com> Mon, 13 Feb 2017 08:57:47 +0000
64+
65 keeper (0.1.0+16.10.20160915.3-0ubuntu1) yakkety; urgency=medium
66
67 * Initial release.
68
69=== modified file 'debian/control'
70--- debian/control 2016-08-26 08:48:05 +0000
71+++ debian/control 2017-02-27 19:05:17 +0000
72@@ -9,7 +9,7 @@
73 # for building the code:
74 libarchive-dev (>= 3.1.2),
75 libproperties-cpp-dev,
76- libubuntu-app-launch2-dev (>= 0.9),
77+ libubuntu-app-launch3-dev,
78 storage-framework-client-dev,
79 libclick-0.4-dev,
80 uuid-dev,
81@@ -45,6 +45,8 @@
82 Depends: ${shlibs:Depends},
83 ${misc:Depends},
84 systemd | systemd-shim,
85+ tar,
86+ xz-utils
87 Description: Backup Tool
88 A backup/restore utility for Ubuntu
89
90@@ -53,6 +55,8 @@
91 Depends: ${shlibs:Depends},
92 ${misc:Depends},
93 systemd | systemd-shim,
94+ tar,
95+ xz-utils
96 Description: Backup Tool
97 A backup/restore utility for Ubuntu (client application)
98
99
100=== modified file 'debian/keeper.install'
101--- debian/keeper.install 2016-08-10 07:43:36 +0000
102+++ debian/keeper.install 2017-02-27 19:05:17 +0000
103@@ -1,6 +1,8 @@
104 /usr/lib/*/keeper/folder-backup.sh
105+/usr/lib/*/keeper/folder-restore.sh
106 /usr/lib/*/keeper/keeper-service
107-/usr/lib/*/keeper/keeper-tar-create
108+/usr/lib/*/keeper/keeper-tar
109+/usr/lib/*/keeper/keeper-untar
110 /usr/lib/*/ubuntu-app-launch/backup-helper/exec-tool
111 /usr/share/keeper/helper-registry.json
112 /usr/share/dbus-1/services/com.canonical.keeper.service
113
114=== modified file 'include/client/client.h'
115--- include/client/client.h 2016-09-06 18:28:45 +0000
116+++ include/client/client.h 2017-02-27 19:05:17 +0000
117@@ -19,10 +19,13 @@
118
119 #pragma once
120
121+#include "keeper-errors.h"
122+
123 #include <QObject>
124 #include <QScopedPointer>
125 #include <QStringList>
126 #include <QVariant>
127+#include "keeper-items.h"
128
129 struct KeeperClientPrivate;
130
131@@ -53,14 +56,22 @@
132
133 Q_INVOKABLE QString getBackupName(QString uuid);
134 Q_INVOKABLE void enableBackup(QString uuid, bool enabled);
135- Q_INVOKABLE void startBackup();
136+ Q_INVOKABLE void startBackup(QString const & storage);
137+
138+ Q_INVOKABLE void enableRestore(QString uuid, bool enabled);
139+ Q_INVOKABLE void startRestore(QString const & storage);
140+
141+ Q_INVOKABLE void cancel();
142
143 // C++
144 public:
145- QMap<QString, QVariantMap> getBackupChoices() const;
146- void startBackup(QStringList const& uuids) const;
147+ keeper::Items getBackupChoices(keeper::Error & error) const;
148+ keeper::Items getRestoreChoices(QString const & storage, keeper::Error & error) const;
149+ void startBackup(QStringList const& uuids, QString const & storage) const;
150+ void startRestore(QStringList const& uuids, QString const & storage) const;
151
152- QMap<QString, QVariantMap> getState() const;
153+ keeper::Items getState() const;
154+ QStringList getStorageAccounts() const;
155
156 Q_SIGNALS:
157 void statusChanged();
158@@ -68,6 +79,9 @@
159 void readyToBackupChanged();
160 void backupBusyChanged();
161
162+ void taskStatusChanged(QString const & displayName, QString const & status, double percentage, keeper::Error error);
163+ void finished();
164+
165 private Q_SLOTS:
166 void stateUpdated();
167
168
169=== added file 'include/client/keeper-errors.h'
170--- include/client/keeper-errors.h 1970-01-01 00:00:00 +0000
171+++ include/client/keeper-errors.h 2017-02-27 19:05:17 +0000
172@@ -0,0 +1,57 @@
173+/*
174+ * Copyright (C) 2016 Canonical, Ltd.
175+ *
176+ * This program is free software: you can redistribute it and/or modify it
177+ * under the terms of the GNU General Public License version 3, as published
178+ * by the Free Software Foundation.
179+ *
180+ * This program is distributed in the hope that it will be useful, but
181+ * WITHOUT ANY WARRANTY; without even the implied warranties of
182+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
183+ * PURPOSE. See the GNU General Public License for more details.
184+ *
185+ * You should have received a copy of the GNU General Public License along
186+ * with this program. If not, see <http://www.gnu.org/licenses/>.
187+ *
188+ * Authors:
189+ * Xavi Garcia Mena <xavi.garcia.mena@canonical.com>
190+ */
191+
192+#pragma once
193+
194+#include <QDBusArgument>
195+#include <QMetaType>
196+
197+namespace keeper
198+{
199+
200+enum class Error
201+{
202+ OK,
203+ UNKNOWN,
204+ HELPER_READ,
205+ HELPER_WRITE,
206+ HELPER_INACTIVITY_DETECTED,
207+ HELPER_SOCKET,
208+ HELPER_START_TIMEOUT,
209+ NO_HELPER_INFORMATION_IN_REGISTRY,
210+ HELPER_BAD_URL,
211+ MANIFEST_STORAGE,
212+ COMMITTING_DATA,
213+
214+ CREATING_REMOTE_DIR,
215+ CREATING_REMOTE_FILE,
216+ READING_REMOTE_FILE,
217+ REMOTE_DIR_NOT_EXISTS,
218+ NO_REMOTE_ACCOUNTS,
219+ NO_REMOTE_ROOTS,
220+ ACCOUNT_NOT_FOUND
221+};
222+
223+Error convert_from_dbus_variant(const QVariant & value, bool *conversion_ok = nullptr);
224+} // namespace keeper
225+
226+QDBusArgument &operator<<(QDBusArgument &argument, keeper::Error value);
227+const QDBusArgument &operator>>(const QDBusArgument &argument, keeper::Error &val);
228+
229+Q_DECLARE_METATYPE(keeper::Error)
230
231=== added file 'include/client/keeper-items.h'
232--- include/client/keeper-items.h 1970-01-01 00:00:00 +0000
233+++ include/client/keeper-items.h 2017-02-27 19:05:17 +0000
234@@ -0,0 +1,102 @@
235+/*
236+ * Copyright (C) 2016 Canonical, Ltd.
237+ *
238+ * This program is free software: you can redistribute it and/or modify it
239+ * under the terms of the GNU General Public License version 3, as published
240+ * by the Free Software Foundation.
241+ *
242+ * This program is distributed in the hope that it will be useful, but
243+ * WITHOUT ANY WARRANTY; without even the implied warranties of
244+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
245+ * PURPOSE. See the GNU General Public License for more details.
246+ *
247+ * You should have received a copy of the GNU General Public License along
248+ * with this program. If not, see <http://www.gnu.org/licenses/>.
249+ *
250+ * Authors:
251+ * Xavi Garcia Mena <xavi.garcia.mena@canonical.com>
252+ */
253+
254+#pragma once
255+
256+#include "client/keeper-errors.h"
257+
258+#include <QJsonObject>
259+
260+typedef QMap<QString, QVariantMap> QVariantDictMap;
261+
262+namespace keeper
263+{
264+
265+class Item : public QVariantMap
266+{
267+public:
268+ Item();
269+ ~Item();
270+ explicit Item(QVariantMap const & values);
271+
272+ // keys
273+ static QString const UUID_KEY;
274+ static QString const TYPE_KEY;
275+ static QString const SUBTYPE_KEY;
276+ static QString const NAME_KEY;
277+ static QString const PACKAGE_KEY;
278+ static QString const TITLE_KEY;
279+ static QString const VERSION_KEY;
280+ static QString const FILE_NAME_KEY;
281+ static QString const DIR_NAME_KEY;
282+ static QString const DISPLAY_NAME_KEY;
283+ static QString const STATUS_KEY;
284+ static QString const ERROR_KEY;
285+ static QString const PERCENT_DONE_KEY;
286+ static QString const SPEED_KEY;
287+
288+ // values
289+ static QString const FOLDER_VALUE;
290+ static QString const SYSTEM_DATA_VALUE;
291+ static QString const APPLICATION_VALUE;
292+
293+
294+ // methods created for convenience
295+ bool has_property(QString const & property) const;
296+ template<typename T> T get_property(QString const & property, bool * valid) const;
297+ QVariant get_property_value(QString const & property) const;
298+ void set_property_value(QString const& property, QVariant const& value);
299+
300+ // checks that the item is valid
301+ bool is_valid() const;
302+
303+ // predefined properties
304+ QString get_uuid(bool *valid = nullptr) const;
305+ QString get_type(bool *valid = nullptr) const;
306+ QString get_display_name(bool *valid = nullptr) const;
307+ QString get_dir_name(bool *valid = nullptr) const;
308+ QString get_status(bool *valid = nullptr) const;
309+ double get_percent_done(bool *valid = nullptr) const;
310+ keeper::Error get_error(bool *valid = nullptr) const;
311+ QString get_file_name(bool *valid = nullptr) const;
312+
313+ // d-bus
314+ static void registerMetaType();
315+};
316+
317+class Items : public QMap<QString, Item>
318+{
319+public:
320+ Items();
321+ ~Items();
322+ explicit Items(Error error);
323+
324+ QStringList get_uuids() const;
325+
326+ // d-bus
327+ static void registerMetaType();
328+
329+private:
330+ Error error_ = Error::OK;
331+};
332+
333+} // namespace keeper
334+
335+Q_DECLARE_METATYPE(keeper::Item)
336+Q_DECLARE_METATYPE(keeper::Items)
337
338=== modified file 'include/helper/backup-helper.h'
339--- include/helper/backup-helper.h 2016-09-15 16:02:54 +0000
340+++ include/helper/backup-helper.h 2017-02-27 19:05:17 +0000
341@@ -53,6 +53,7 @@
342 int get_helper_socket() const;
343 QString to_string(Helper::State state) const override;
344 void set_state(State) override;
345+ QString get_uploader_committed_file_name() const;
346 protected:
347 void on_helper_finished() override;
348
349
350=== modified file 'include/helper/data-dir-registry.h'
351--- include/helper/data-dir-registry.h 2016-08-09 05:44:25 +0000
352+++ include/helper/data-dir-registry.h 2017-02-27 19:05:17 +0000
353@@ -39,6 +39,8 @@
354
355 QStringList get_backup_helper_urls(Metadata const& metadata) override;
356
357+ QStringList get_restore_helper_urls(Metadata const& metadata) override;
358+
359 private:
360 class Impl;
361 friend class Impl;
362
363=== modified file 'include/helper/helper.h'
364--- include/helper/helper.h 2016-09-15 16:02:54 +0000
365+++ include/helper/helper.h 2017-02-27 19:05:17 +0000
366@@ -19,6 +19,7 @@
367
368 #pragma once
369
370+#include <client/keeper-errors.h>
371 #include <util/attributes.h>
372
373 #include <QObject>
374@@ -64,11 +65,12 @@
375 virtual void start(QStringList const& urls);
376 virtual void stop();
377
378- static constexpr int MAX_UAL_WAIT_TIME = 5000;
379+ static constexpr int MAX_UAL_WAIT_TIME = 10000;
380
381 Q_SIGNALS:
382 void state_changed(Helper::State);
383 void percent_done_changed(float);
384+ void error(keeper::Error error);
385
386 protected:
387 Helper(QString const & appid, const clock_func& clock=default_clock, QObject *parent=nullptr);
388
389=== modified file 'include/helper/metadata.h'
390--- include/helper/metadata.h 2016-09-06 18:51:30 +0000
391+++ include/helper/metadata.h 2017-02-27 19:05:17 +0000
392@@ -19,42 +19,21 @@
393
394 #pragma once
395
396+#include "client/keeper-items.h"
397+#include <QJsonObject>
398 #include <QMap>
399 #include <QString>
400
401 /**
402- * Information about a backup
403+ * Information about a backup or restore item
404 */
405-class Metadata
406+class Metadata : public keeper::Item
407 {
408 public:
409
410 Metadata();
411+ explicit Metadata(QJsonObject const & json_object);
412 Metadata(QString const& uuid, QString const& display_name);
413
414- // metadata keys
415- static QString const TYPE_KEY;
416- static QString const SUBTYPE_KEY;
417- static QString const NAME_KEY;
418- static QString const PACKAGE_KEY;
419- static QString const TITLE_KEY;
420- static QString const VERSION_KEY;
421-
422- // metadata values
423- static QString const FOLDER_VALUE;
424- static QString const SYSTEM_DATA_VALUE;
425- static QString const APPLICATION_VALUE;
426-
427- QString uuid() const { return uuid_; }
428- QString display_name() const { return display_name_; }
429- bool get_property(QString const& property_name, QString& setme_value) const;
430- void set_property(QString const& property_name, QString const& value);
431-
432- QMap<QString,QString> get_public_properties() const;
433-
434-private:
435-
436- QString uuid_;
437- QString display_name_;
438- QMap<QString,QString> properties_{};
439+ QJsonObject json() const;
440 };
441
442=== modified file 'include/helper/registry.h'
443--- include/helper/registry.h 2016-08-08 04:56:35 +0000
444+++ include/helper/registry.h 2017-02-27 19:05:17 +0000
445@@ -30,6 +30,7 @@
446 Q_DISABLE_COPY(HelperRegistry)
447
448 virtual QStringList get_backup_helper_urls(Metadata const& task) =0;
449+ virtual QStringList get_restore_helper_urls(Metadata const& task) =0;
450
451 protected:
452 HelperRegistry() =default;
453
454=== added file 'include/helper/restore-helper.h'
455--- include/helper/restore-helper.h 1970-01-01 00:00:00 +0000
456+++ include/helper/restore-helper.h 2017-02-27 19:05:17 +0000
457@@ -0,0 +1,62 @@
458+/*
459+ * Copyright (C) 2016 Canonical, Ltd.
460+ *
461+ * This program is free software: you can redistribute it and/or modify it
462+ * under the terms of the GNU General Public License version 3, as published
463+ * by the Free Software Foundation.
464+ *
465+ * This program is distributed in the hope that it will be useful, but
466+ * WITHOUT ANY WARRANTY; without even the implied warranties of
467+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
468+ * PURPOSE. See the GNU General Public License for more details.
469+ *
470+ * You should have received a copy of the GNU General Public License along
471+ * with this program. If not, see <http://www.gnu.org/licenses/>.
472+ *
473+ * Authors:
474+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
475+ * Charles Kerr <charles.kerr@canonical.com>
476+ */
477+
478+#pragma once
479+
480+#include "storage-framework/downloader.h"
481+#include "helper/helper.h" // parent class
482+#include "helper/registry.h"
483+
484+#include <QObject>
485+#include <QScopedPointer>
486+#include <QString>
487+
488+#include <memory>
489+
490+class RestoreHelperPrivate;
491+class RestoreHelper final: public Helper
492+{
493+ Q_OBJECT
494+ Q_DECLARE_PRIVATE(RestoreHelper)
495+
496+public:
497+ RestoreHelper(
498+ QString const & appid,
499+ clock_func const & clock=Helper::default_clock,
500+ QObject * parent=nullptr
501+ );
502+ virtual ~RestoreHelper();
503+ Q_DISABLE_COPY(RestoreHelper)
504+
505+ static constexpr int MAX_INACTIVITY_TIME = 15000;
506+
507+ void set_downloader(std::shared_ptr<Downloader> const& downloader);
508+
509+ void start(QStringList const& urls) override;
510+ void stop() override;
511+ int get_helper_socket() const;
512+ QString to_string(Helper::State state) const override;
513+ void set_state(State) override;
514+protected:
515+ void on_helper_finished() override;
516+
517+private:
518+ QScopedPointer<RestoreHelperPrivate> const d_ptr;
519+};
520
521=== modified file 'src/cli/CMakeLists.txt'
522--- src/cli/CMakeLists.txt 2016-09-06 01:31:59 +0000
523+++ src/cli/CMakeLists.txt 2017-02-27 19:05:17 +0000
524@@ -5,6 +5,9 @@
525
526 set(CLI_SOURCES
527 main.cpp
528+ command-line.cpp
529+ command-line-client.cpp
530+ command-line-client-view.cpp
531 )
532
533 add_executable(
534@@ -19,6 +22,7 @@
535 util
536 qdbus-stubs
537 ${SERVICE_DEVEL_SF_DEPS_LIBRARIES}
538+ ${KEEPER_CLIENT_LIB}
539 Qt5::Core
540 Qt5::DBus
541 )
542
543=== added file 'src/cli/command-line-client-view.cpp'
544--- src/cli/command-line-client-view.cpp 1970-01-01 00:00:00 +0000
545+++ src/cli/command-line-client-view.cpp 2017-02-27 19:05:17 +0000
546@@ -0,0 +1,194 @@
547+/*
548+ * Copyright (C) 2016 Canonical, Ltd.
549+ *
550+ * This program is free software: you can redistribute it and/or modify it
551+ * under the terms of the GNU General Public License version 3, as published
552+ * by the Free Software Foundation.
553+ *
554+ * This program is distributed in the hope that it will be useful, but
555+ * WITHOUT ANY WARRANTY; without even the implied warranties of
556+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
557+ * PURPOSE. See the GNU General Public License for more details.
558+ *
559+ * You should have received a copy of the GNU General Public License along
560+ * with this program. If not, see <http://www.gnu.org/licenses/>.
561+ *
562+ * Authors:
563+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
564+ */
565+#include "command-line-client-view.h"
566+
567+#include <client/client.h>
568+
569+#include <iostream>
570+#include <iomanip>
571+
572+CommandLineClientView::CommandLineClientView(QObject * parent)
573+ : QObject(parent)
574+{
575+ connect(&timer_status_, &QTimer::timeout, this, &CommandLineClientView::show_info);
576+
577+ // TODO see if we can do this in a better way
578+ // This line is for the global progress status and percentage
579+ std::cout << std::endl;
580+}
581+
582+void CommandLineClientView::progress_changed(double percentage)
583+{
584+ percentage_ = percentage * 100;
585+}
586+
587+void CommandLineClientView::status_changed(QString const & status)
588+{
589+ if (status_ != status)
590+ {
591+ status_ = status;
592+ }
593+}
594+
595+void CommandLineClientView::add_task(QString const & display_name, QString const & initial_status, double initial_percentage)
596+{
597+ tasks_strings_[display_name] = get_task_string(display_name, initial_status, initial_percentage, keeper::Error::OK);
598+ // TODO see if we can do this in a better way
599+ // We add a line per each backup task
600+ std::cout << std::endl;
601+}
602+
603+void CommandLineClientView::clear_all_tasks()
604+{
605+ tasks_strings_.clear();
606+}
607+
608+void CommandLineClientView::start_printing_tasks()
609+{
610+ timer_status_.start(300);
611+}
612+
613+void CommandLineClientView::clear_all()
614+{
615+ timer_status_.stop();
616+ std::cout << std::endl;
617+}
618+
619+void CommandLineClientView::print_sections(QStringList const & sections)
620+{
621+ for (auto const & section : sections)
622+ {
623+ std::cout << section.toStdString() << std::endl;
624+ }
625+}
626+
627+void CommandLineClientView::print_error_message(QString const & error_message)
628+{
629+ std::cerr << error_message.toStdString() << std::endl;
630+}
631+
632+void CommandLineClientView::show_info()
633+{
634+ // TODO Revisit this code to see if we can do this in a different way
635+ // Maybe using ncurses?
636+
637+ // Rewind to the beginning
638+ std::cout << '\r' << std::flush;
639+ // For every backup task we go up 1 line
640+ for (auto i = 0; i < tasks_strings_.size(); ++i)
641+ {
642+ std::cout << "\e[A";
643+ }
644+ // print the tasks
645+ for (auto iter = tasks_strings_.begin(); iter != tasks_strings_.end(); ++iter)
646+ {
647+ std::cout << (*iter).toStdString() << std::setfill(' ') << std::endl;
648+ }
649+ std::cout << '\r' << std::fixed << std::setw(30) << status_.toStdString() << std::setprecision(3)
650+ << std::setfill(' ') << " " << percentage_ << " % " << get_next_spin_char() << " " << std::flush;
651+}
652+
653+char CommandLineClientView::get_next_spin_char()
654+{
655+ char cursor[4]={'/','-','\\','|'};
656+ auto ret = cursor[spin_value_];
657+ spin_value_ = (spin_value_ + 1) % 4;
658+ return ret;
659+}
660+
661+QString CommandLineClientView::get_task_string(QString const & displayName, QString const & status, double percentage, keeper::Error error)
662+{
663+
664+ if (error == keeper::Error::OK)
665+ return QStringLiteral("%1 %2 % %3").arg(displayName, 15).arg((percentage * 100), 10, 'f', 2, ' ').arg(status, -15);
666+ else
667+ return QStringLiteral("%1 %2 % %3 %4").arg(displayName, 15).arg((percentage * 100), 10, 'f', 2, ' ').arg(status, -15).arg(get_error_string(error));
668+}
669+
670+QString CommandLineClientView::get_error_string(keeper::Error error)
671+{
672+ QString ret;
673+ switch(error)
674+ {
675+ case keeper::Error::UNKNOWN:
676+ ret = QStringLiteral("Unknown error");
677+ break;
678+ case keeper::Error::HELPER_BAD_URL:
679+ ret = QStringLiteral("Bad URL for keeper helper");
680+ break;
681+ case keeper::Error::HELPER_INACTIVITY_DETECTED:
682+ ret = QStringLiteral("Inactivity detected in task");
683+ break;
684+ case keeper::Error::HELPER_START_TIMEOUT:
685+ ret = QStringLiteral("Task failed to start");
686+ break;
687+ case keeper::Error::HELPER_READ:
688+ ret = QStringLiteral("Read error");
689+ break;
690+ case keeper::Error::HELPER_SOCKET:
691+ ret = QStringLiteral("Error creating internal socket");
692+ break;
693+ case keeper::Error::HELPER_WRITE:
694+ ret = QStringLiteral("Write error");
695+ break;
696+ case keeper::Error::MANIFEST_STORAGE:
697+ ret = QStringLiteral("Error storing manifest file");
698+ break;
699+ case keeper::Error::NO_HELPER_INFORMATION_IN_REGISTRY:
700+ ret = QStringLiteral("No helper information in registry");
701+ break;
702+ case keeper::Error::OK:
703+ ret = QStringLiteral("Success");
704+ break;
705+ case keeper::Error::COMMITTING_DATA:
706+ ret = QStringLiteral("Error uploading data");
707+ break;
708+ case keeper::Error::CREATING_REMOTE_DIR:
709+ ret = QStringLiteral("Error creating remote directory");
710+ break;
711+ case keeper::Error::CREATING_REMOTE_FILE:
712+ ret = QStringLiteral("Error creating remote file");
713+ break;
714+ case keeper::Error::READING_REMOTE_FILE:
715+ ret = QStringLiteral("Error reading remote file");
716+ break;
717+ case keeper::Error::REMOTE_DIR_NOT_EXISTS:
718+ ret = QStringLiteral("Remote directory does not exist");
719+ break;
720+ case keeper::Error::NO_REMOTE_ACCOUNTS:
721+ ret = QStringLiteral("No remote accounts were found");
722+ break;
723+ case keeper::Error::NO_REMOTE_ROOTS:
724+ ret = QStringLiteral("No remote root accounts were found");
725+ break;
726+ case keeper::Error::ACCOUNT_NOT_FOUND:
727+ ret = QStringLiteral("The storage account was not found");
728+ break;
729+ }
730+ return ret;
731+}
732+
733+void CommandLineClientView::on_task_state_changed(QString const & displayName, QString const & status, double percentage, keeper::Error error)
734+{
735+ auto iter = tasks_strings_.find(displayName);
736+ if (iter != tasks_strings_.end())
737+ {
738+ tasks_strings_[displayName] = get_task_string(displayName, status, percentage, error);
739+ }
740+}
741
742=== added file 'src/cli/command-line-client-view.h'
743--- src/cli/command-line-client-view.h 1970-01-01 00:00:00 +0000
744+++ src/cli/command-line-client-view.h 2017-02-27 19:05:17 +0000
745@@ -0,0 +1,60 @@
746+/*
747+ * Copyright (C) 2016 Canonical, Ltd.
748+ *
749+ * This program is free software: you can redistribute it and/or modify it
750+ * under the terms of the GNU General Public License version 3, as published
751+ * by the Free Software Foundation.
752+ *
753+ * This program is distributed in the hope that it will be useful, but
754+ * WITHOUT ANY WARRANTY; without even the implied warranties of
755+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
756+ * PURPOSE. See the GNU General Public License for more details.
757+ *
758+ * You should have received a copy of the GNU General Public License along
759+ * with this program. If not, see <http://www.gnu.org/licenses/>.
760+ *
761+ * Authors:
762+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
763+ */
764+#pragma once
765+
766+#include <client/keeper-errors.h>
767+
768+#include <QMap>
769+#include <QObject>
770+#include <QTimer>
771+
772+class CommandLineClientView : public QObject
773+{
774+ Q_OBJECT
775+public:
776+ explicit CommandLineClientView(QObject * parent = nullptr);
777+ ~CommandLineClientView() = default;
778+
779+ Q_DISABLE_COPY(CommandLineClientView)
780+
781+ void progress_changed(double percentage);
782+ void status_changed(QString const & status);
783+
784+ void add_task(QString const & display_name, QString const & initial_status, double initial_percentage);
785+ void clear_all_tasks();
786+ void start_printing_tasks();
787+ void clear_all();
788+ void print_sections(QStringList const & sections);
789+ void print_error_message(QString const & error_message);
790+ QString get_error_string(keeper::Error error);
791+
792+public Q_SLOTS:
793+ void show_info();
794+ void on_task_state_changed(QString const & displayName, QString const & status, double percentage, keeper::Error error);
795+
796+private:
797+ char get_next_spin_char();
798+ QString get_task_string(QString const & displayName, QString const & status, double percentage, keeper::Error error);
799+
800+ QString status_;
801+ QTimer timer_status_;
802+ double percentage_ = 0.0;
803+ int spin_value_ = 0;
804+ QMap<QString, QString> tasks_strings_;
805+};
806
807=== added file 'src/cli/command-line-client.cpp'
808--- src/cli/command-line-client.cpp 1970-01-01 00:00:00 +0000
809+++ src/cli/command-line-client.cpp 2017-02-27 19:05:17 +0000
810@@ -0,0 +1,250 @@
811+/*
812+ * Copyright (C) 2016 Canonical, Ltd.
813+ *
814+ * This program is free software: you can redistribute it and/or modify it
815+ * under the terms of the GNU General Public License version 3, as published
816+ * by the Free Software Foundation.
817+ *
818+ * This program is distributed in the hope that it will be useful, but
819+ * WITHOUT ANY WARRANTY; without even the implied warranties of
820+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
821+ * PURPOSE. See the GNU General Public License for more details.
822+ *
823+ * You should have received a copy of the GNU General Public License along
824+ * with this program. If not, see <http://www.gnu.org/licenses/>.
825+ *
826+ * Authors:
827+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
828+ */
829+#include "command-line-client.h"
830+#include "command-line-client-view.h"
831+
832+#include <client/client.h>
833+
834+#include <QCoreApplication>
835+#include <QDebug>
836+
837+#include <iostream>
838+#include <iomanip>
839+
840+CommandLineClient::CommandLineClient(QObject * parent)
841+ : QObject(parent)
842+ , keeper_client_(new KeeperClient(this))
843+ , view_(new CommandLineClientView(this))
844+{
845+ connect(keeper_client_.data(), &KeeperClient::statusChanged, this, &CommandLineClient::on_status_changed);
846+ connect(keeper_client_.data(), &KeeperClient::progressChanged, this, &CommandLineClient::on_progress_changed);
847+ connect(keeper_client_.data(), &KeeperClient::finished, this, &CommandLineClient::on_keeper_client_finished);
848+ connect(keeper_client_.data(), &KeeperClient::taskStatusChanged, view_.data(), &CommandLineClientView::on_task_state_changed);
849+}
850+
851+CommandLineClient::~CommandLineClient() = default;
852+
853+void CommandLineClient::run_list_sections(bool remote, QString const & storage)
854+{
855+ keeper::Items choices_values;
856+ keeper::Error error;
857+ if(!remote)
858+ {
859+ choices_values = keeper_client_->getBackupChoices(error);
860+ check_for_choices_error(error);
861+ list_backup_sections(choices_values);
862+ }
863+ else
864+ {
865+ choices_values = keeper_client_->getRestoreChoices(storage, error);
866+ check_for_choices_error(error);
867+ list_restore_sections(choices_values);
868+ }
869+}
870+
871+void CommandLineClient::run_list_storage_accounts()
872+{
873+ list_storage_accounts(keeper_client_->getStorageAccounts());
874+}
875+
876+void CommandLineClient::run_backup(QStringList & sections, QString const & storage)
877+{
878+ auto unhandled_sections = sections;
879+ keeper::Error error;
880+ auto choices_values = keeper_client_->getBackupChoices(error);
881+ check_for_choices_error(error);
882+ QStringList uuids;
883+
884+ auto uuids_choices = choices_values.get_uuids();
885+ for(auto iter = uuids_choices.begin(); iter != uuids_choices.end() && unhandled_sections.size(); ++iter)
886+ {
887+ const auto& values = choices_values[(*iter)];
888+
889+ if (values.is_valid() && values.get_type() == keeper::Item::FOLDER_VALUE)
890+ {
891+
892+ auto display_name = values.get_display_name();
893+ auto index = unhandled_sections.indexOf(display_name);
894+ if (index != -1)
895+ {
896+ // we have to backup this section
897+ uuids << (*iter);
898+ unhandled_sections.removeAt(index);
899+ view_->add_task(display_name, "waiting", 0.0);
900+ }
901+ }
902+ }
903+
904+ if (!unhandled_sections.isEmpty())
905+ {
906+ QString error_message("The following sections were not found: \n");
907+ for (auto const & section : unhandled_sections)
908+ {
909+ error_message += QStringLiteral("\t %1 \n").arg(section);
910+ }
911+ view_->print_error_message(error_message);
912+ exit(1);
913+ }
914+
915+ for (auto const & uuid: uuids)
916+ {
917+ keeper_client_->enableBackup(uuid, true);
918+ }
919+ keeper_client_->startBackup(storage);
920+ view_->start_printing_tasks();
921+}
922+
923+void CommandLineClient::run_restore(QStringList & sections, QString const & storage)
924+{
925+ auto unhandled_sections = sections;
926+ keeper::Error error;
927+
928+ auto choices_values = keeper_client_->getRestoreChoices(storage, error);
929+ check_for_choices_error(error);
930+ QStringList uuids;
931+
932+ auto uuids_choices = choices_values.get_uuids();
933+ for(auto iter = uuids_choices.begin(); iter != uuids_choices.end(); ++iter)
934+ {
935+ const auto& values = choices_values[(*iter)];
936+
937+ if (values.is_valid() && values.get_type() == keeper::Item::FOLDER_VALUE)
938+ {
939+ auto display_name = values.get_display_name();
940+ auto dir_name = values.get_dir_name();
941+
942+ auto section_name = QStringLiteral("%1:%2").arg(display_name).arg(dir_name);
943+ auto index = unhandled_sections.indexOf(section_name);
944+ if (index != -1)
945+ {
946+ // we have to restore this section
947+ uuids << (*iter);
948+ unhandled_sections.removeAt(index);
949+ view_->add_task(display_name, "waiting", 0.0);
950+ }
951+ }
952+ }
953+ if (!unhandled_sections.isEmpty())
954+ {
955+ QString error_message("The following sections were not found: \n");
956+ for (auto const & section : unhandled_sections)
957+ {
958+ error_message += QStringLiteral("\t %1 \n").arg(section);
959+ }
960+ view_->print_error_message(error_message);
961+ exit(1);
962+ }
963+
964+ for (auto const & uuid: uuids)
965+ {
966+ keeper_client_->enableRestore(uuid, true);
967+ }
968+ keeper_client_->startRestore(storage);
969+ view_->start_printing_tasks();
970+}
971+
972+void CommandLineClient::run_cancel() const
973+{
974+ keeper_client_->cancel();
975+}
976+
977+void CommandLineClient::list_backup_sections(keeper::Items const & choices_values)
978+{
979+ QStringList sections;
980+ for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
981+ {
982+ if ((*iter).is_valid() && (*iter).get_type() == keeper::Item::FOLDER_VALUE)
983+ {
984+ sections << (*iter).get_display_name();
985+ }
986+ }
987+ view_->print_sections(sections);
988+}
989+
990+void CommandLineClient::list_restore_sections(keeper::Items const & choices_values)
991+{
992+ QMap<QString, QList<keeper::Item>> values_per_dir;
993+
994+ for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
995+ {
996+ if ((*iter).is_valid() && (*iter).get_type() == keeper::Item::FOLDER_VALUE)
997+ {
998+ auto dir_name = (*iter).get_dir_name();
999+ if (!dir_name.isEmpty())
1000+ {
1001+ values_per_dir[dir_name].push_back((*iter));
1002+ }
1003+ }
1004+ }
1005+
1006+ QStringList sections;
1007+ for(auto iter = values_per_dir.begin(); iter != values_per_dir.end(); ++iter)
1008+ {
1009+ for(auto iter_items = (*iter).begin(); iter_items != (*iter).end(); ++iter_items)
1010+ {
1011+ const auto& values = (*iter_items);
1012+ sections << QStringLiteral("%1:%2").arg(values.get_display_name()).arg(iter.key());
1013+ }
1014+ sections << "";
1015+ }
1016+ view_->print_sections(sections);
1017+}
1018+
1019+void CommandLineClient::list_storage_accounts(QStringList const & accounts)
1020+{
1021+ view_->print_sections(accounts);
1022+}
1023+
1024+void CommandLineClient::on_progress_changed()
1025+{
1026+ view_->progress_changed(keeper_client_->progress());
1027+}
1028+
1029+void CommandLineClient::on_status_changed()
1030+{
1031+ view_->status_changed(keeper_client_->status());
1032+}
1033+
1034+void CommandLineClient::on_keeper_client_finished()
1035+{
1036+ QCoreApplication::processEvents();
1037+ view_->show_info();
1038+ view_->clear_all();
1039+ QCoreApplication::exit(0);
1040+}
1041+
1042+bool CommandLineClient::find_choice_value(QVariantMap const & choice, QString const & id, QVariant & value)
1043+{
1044+ auto iter = choice.find(id);
1045+ if (iter == choice.end())
1046+ return false;
1047+ value = (*iter);
1048+ return true;
1049+}
1050+
1051+void CommandLineClient::check_for_choices_error(keeper::Error error)
1052+{
1053+ if (error != keeper::Error::OK)
1054+ {
1055+ // an error occurred
1056+ auto error_message = QStringLiteral("Error obtaining keeper choices: %1").arg(view_->get_error_string(error));
1057+ view_->print_error_message(error_message);
1058+ return;
1059+ }
1060+}
1061
1062=== added file 'src/cli/command-line-client.h'
1063--- src/cli/command-line-client.h 1970-01-01 00:00:00 +0000
1064+++ src/cli/command-line-client.h 2017-02-27 19:05:17 +0000
1065@@ -0,0 +1,58 @@
1066+/*
1067+ * Copyright (C) 2016 Canonical, Ltd.
1068+ *
1069+ * This program is free software: you can redistribute it and/or modify it
1070+ * under the terms of the GNU General Public License version 3, as published
1071+ * by the Free Software Foundation.
1072+ *
1073+ * This program is distributed in the hope that it will be useful, but
1074+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1075+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1076+ * PURPOSE. See the GNU General Public License for more details.
1077+ *
1078+ * You should have received a copy of the GNU General Public License along
1079+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1080+ *
1081+ * Authors:
1082+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
1083+ */
1084+#pragma once
1085+
1086+#include <QMap>
1087+#include <QObject>
1088+#include <QScopedPointer>
1089+#include <QTimer>
1090+#include "../../include/client/keeper-items.h"
1091+
1092+class KeeperClient;
1093+class CommandLineClientView;
1094+
1095+class CommandLineClient : public QObject
1096+{
1097+ Q_OBJECT
1098+public:
1099+ explicit CommandLineClient(QObject * parent = nullptr);
1100+ ~CommandLineClient();
1101+
1102+ Q_DISABLE_COPY(CommandLineClient)
1103+
1104+ void run_list_sections(bool remote, QString const & storage = "");
1105+ void run_list_storage_accounts();
1106+ void run_backup(QStringList & sections, QString const & storage);
1107+ void run_restore(QStringList & sections, QString const & storage);
1108+ void run_cancel() const;
1109+
1110+private Q_SLOTS:
1111+ void on_progress_changed();
1112+ void on_status_changed();
1113+ void on_keeper_client_finished();
1114+
1115+private:
1116+ bool find_choice_value(QVariantMap const & choice, QString const & id, QVariant & value);
1117+ void list_backup_sections(keeper::Items const & choices);
1118+ void list_restore_sections(keeper::Items const & choices);
1119+ void list_storage_accounts(QStringList const & accounts);
1120+ void check_for_choices_error(keeper::Error error);
1121+ QScopedPointer<KeeperClient> keeper_client_;
1122+ QScopedPointer<CommandLineClientView> view_;
1123+};
1124
1125=== added file 'src/cli/command-line.cpp'
1126--- src/cli/command-line.cpp 1970-01-01 00:00:00 +0000
1127+++ src/cli/command-line.cpp 2017-02-27 19:05:17 +0000
1128@@ -0,0 +1,234 @@
1129+/*
1130+ * Copyright (C) 2016 Canonical, Ltd.
1131+ *
1132+ * This program is free software: you can redistribute it and/or modify it
1133+ * under the terms of the GNU General Public License version 3, as published
1134+ * by the Free Software Foundation.
1135+ *
1136+ * This program is distributed in the hope that it will be useful, but
1137+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1138+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1139+ * PURPOSE. See the GNU General Public License for more details.
1140+ *
1141+ * You should have received a copy of the GNU General Public License along
1142+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1143+ *
1144+ * Authors:
1145+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
1146+ */
1147+#include "command-line.h"
1148+
1149+#include <QCommandLineParser>
1150+#include <QDebug>
1151+
1152+#include <iostream>
1153+
1154+namespace
1155+{
1156+ // arguments
1157+ constexpr const char ARGUMENT_LIST_SECTIONS[] = "list-sections";
1158+ constexpr const char ARGUMENT_LIST_STORAGE_ACCOUNTS[] = "list-storage-configs";
1159+ constexpr const char ARGUMENT_BACKUP[] = "backup";
1160+ constexpr const char ARGUMENT_RESTORE[] = "restore";
1161+
1162+ // argument descriptions
1163+ constexpr const char ARGUMENT_LIST_SECTIONS_DESCRIPTION[] = "List the sections available to backup";
1164+ constexpr const char ARGUMENT_LIST_STORAGE_ACCOUNTS_DESCRIPTION[] = "List the available storage accounts";
1165+ constexpr const char ARGUMENT_BACKUP_DESCRIPTION[] = "Starts a backup";
1166+ constexpr const char ARGUMENT_RESTORE_DESCRIPTION[] = "Starts a restore";
1167+
1168+ // options
1169+ constexpr const char OPTION_STORAGE[] = "storage";
1170+ constexpr const char OPTION_SECTIONS[] = "sections";
1171+
1172+ // option descriptions
1173+ constexpr const char OPTION_STORAGE_DESCRIPTION[] = "Defines the available storage to use. Pass 'default' to use the default one";
1174+ constexpr const char OPTION_SECTIONS_DESCRIPTION[] = "Lists the sections to backup or restore";
1175+}
1176+
1177+CommandLineParser::CommandLineParser()
1178+ : parser_(new QCommandLineParser)
1179+{
1180+ parser_->setApplicationDescription("Keeper command line client");
1181+ parser_->addHelpOption();
1182+ parser_->addVersionOption();
1183+ parser_->addPositionalArgument(ARGUMENT_LIST_SECTIONS, QCoreApplication::translate("main", ARGUMENT_LIST_SECTIONS_DESCRIPTION));
1184+ parser_->addPositionalArgument(ARGUMENT_LIST_STORAGE_ACCOUNTS, QCoreApplication::translate("main", ARGUMENT_LIST_STORAGE_ACCOUNTS_DESCRIPTION));
1185+ parser_->addPositionalArgument(ARGUMENT_BACKUP, QCoreApplication::translate("main", ARGUMENT_BACKUP_DESCRIPTION));
1186+ parser_->addPositionalArgument(ARGUMENT_RESTORE, QCoreApplication::translate("main", ARGUMENT_RESTORE_DESCRIPTION));
1187+}
1188+
1189+bool CommandLineParser::parse(QStringList const & arguments, QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
1190+{
1191+ parser_->parse(arguments);
1192+ const QStringList args = parser_->positionalArguments();
1193+ if (!check_number_of_args(args))
1194+ {
1195+ return false;
1196+ }
1197+
1198+ if (args.size() == 1)
1199+ {
1200+ // if something fails at the process call the process exists
1201+ if (args.at(0) == ARGUMENT_LIST_SECTIONS)
1202+ {
1203+ return handle_list_sections(app, cmd_args);
1204+ }
1205+ else if (args.at(0) == ARGUMENT_LIST_STORAGE_ACCOUNTS)
1206+ {
1207+ return handle_list_storage_accounts(app, cmd_args);
1208+ }
1209+ else if (args.at(0) == ARGUMENT_BACKUP)
1210+ {
1211+ return handle_backup(app, cmd_args);
1212+ }
1213+ else if (args.at(0) == ARGUMENT_RESTORE)
1214+ {
1215+ return handle_restore(app, cmd_args);
1216+ }
1217+ else
1218+ {
1219+ std::cerr << "Bad argument." << std::endl;
1220+ std::cerr << parser_->errorText().toStdString() << std::endl;
1221+ exit(1);
1222+ }
1223+ }
1224+ else
1225+ {
1226+ qDebug() << "More or none arguments";
1227+ // maybe we have the version or help options
1228+ parser_->process(app);
1229+ }
1230+
1231+ return false;
1232+}
1233+
1234+bool CommandLineParser::handle_list_sections(QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
1235+{
1236+ parser_->clearPositionalArguments();
1237+ parser_->addPositionalArgument(ARGUMENT_LIST_SECTIONS, QCoreApplication::translate("main", ARGUMENT_LIST_SECTIONS_DESCRIPTION));
1238+
1239+ parser_->addOptions({
1240+ {{"r", OPTION_STORAGE},
1241+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION),
1242+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION)
1243+ },
1244+ });
1245+ parser_->process(app);
1246+
1247+ // it didn't exit... we're good
1248+ cmd_args.sections.clear();
1249+ cmd_args.storage.clear();
1250+ if (parser_->isSet(OPTION_STORAGE))
1251+ {
1252+ cmd_args.cmd = CommandLineParser::Command::LIST_REMOTE_SECTIONS;
1253+ cmd_args.storage = get_storage_string(parser_->value(OPTION_STORAGE));
1254+ }
1255+ else
1256+ {
1257+ cmd_args.cmd = CommandLineParser::Command::LIST_LOCAL_SECTIONS;
1258+ }
1259+
1260+ return true;
1261+}
1262+
1263+bool CommandLineParser::handle_list_storage_accounts(QCoreApplication const & app, CommandArgs & cmd_args)
1264+{
1265+ parser_->process(app);
1266+ // it didn't exit... we're good
1267+ cmd_args.sections.clear();
1268+ cmd_args.storage.clear();
1269+ cmd_args.cmd = CommandLineParser::Command::LIST_STORAGE_ACCOUNTS;
1270+
1271+ return true;
1272+}
1273+
1274+bool CommandLineParser::handle_backup(QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
1275+{
1276+ parser_->clearPositionalArguments();
1277+ parser_->addPositionalArgument(ARGUMENT_BACKUP, QCoreApplication::translate("main", ARGUMENT_BACKUP_DESCRIPTION));
1278+
1279+ parser_->addOptions({
1280+ {{"s", OPTION_SECTIONS},
1281+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION),
1282+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION)
1283+ },
1284+ {{"r", OPTION_STORAGE},
1285+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION),
1286+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION)
1287+ },
1288+ });
1289+ parser_->process(app);
1290+
1291+ // it didn't exit... we're good
1292+ cmd_args.sections.clear();
1293+ cmd_args.storage.clear();
1294+ cmd_args.cmd = CommandLineParser::Command::BACKUP;
1295+ if (!parser_->isSet(OPTION_SECTIONS))
1296+ {
1297+ std::cerr << "You need to specify some sections to run a backup." << std::endl;
1298+ return false;
1299+ }
1300+ if (parser_->isSet(OPTION_STORAGE))
1301+ {
1302+ cmd_args.storage = get_storage_string(parser_->value(OPTION_STORAGE));
1303+ }
1304+ cmd_args.sections = parser_->value(OPTION_SECTIONS).split(',');
1305+
1306+ return true;
1307+}
1308+
1309+bool CommandLineParser::handle_restore(QCoreApplication const & app, CommandLineParser::CommandArgs & cmd_args)
1310+{
1311+ parser_->clearPositionalArguments();
1312+ parser_->addPositionalArgument(ARGUMENT_RESTORE, QCoreApplication::translate("main", ARGUMENT_RESTORE_DESCRIPTION));
1313+
1314+ parser_->addOptions({
1315+ {{"s", OPTION_SECTIONS},
1316+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION),
1317+ QCoreApplication::translate("main", OPTION_SECTIONS_DESCRIPTION)
1318+ },
1319+ {{"r", OPTION_STORAGE},
1320+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION),
1321+ QCoreApplication::translate("main", OPTION_STORAGE_DESCRIPTION)
1322+ },
1323+ });
1324+ parser_->process(app);
1325+
1326+ // it didn't exit... we're good
1327+ cmd_args.sections.clear();
1328+ cmd_args.storage.clear();
1329+ cmd_args.cmd = CommandLineParser::Command::RESTORE;
1330+ if (!parser_->isSet(OPTION_SECTIONS))
1331+ {
1332+ std::cerr << "You need to specify some sections to run a restore." << std::endl;
1333+ return false;
1334+ }
1335+ if (parser_->isSet(OPTION_STORAGE))
1336+ {
1337+ cmd_args.storage = get_storage_string(parser_->value(OPTION_STORAGE));
1338+ }
1339+ cmd_args.sections = parser_->value(OPTION_SECTIONS).split(',');
1340+
1341+ return true;
1342+}
1343+
1344+bool CommandLineParser::check_number_of_args(QStringList const & args)
1345+{
1346+ if (args.size() > 1)
1347+ {
1348+ std::cerr << "Please, pass only one argument." << std::endl;
1349+ std::cerr << parser_->helpText().toStdString() << std::endl;
1350+ return false;
1351+ }
1352+ return true;
1353+}
1354+
1355+QString CommandLineParser::get_storage_string(QString const & value)
1356+{
1357+ if (value == "default")
1358+ {
1359+ return QString();
1360+ }
1361+ return value;
1362+}
1363
1364=== added file 'src/cli/command-line.h'
1365--- src/cli/command-line.h 1970-01-01 00:00:00 +0000
1366+++ src/cli/command-line.h 2017-02-27 19:05:17 +0000
1367@@ -0,0 +1,55 @@
1368+/*
1369+ * Copyright (C) 2016 Canonical, Ltd.
1370+ *
1371+ * This program is free software: you can redistribute it and/or modify it
1372+ * under the terms of the GNU General Public License version 3, as published
1373+ * by the Free Software Foundation.
1374+ *
1375+ * This program is distributed in the hope that it will be useful, but
1376+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1377+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1378+ * PURPOSE. See the GNU General Public License for more details.
1379+ *
1380+ * You should have received a copy of the GNU General Public License along
1381+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1382+ *
1383+ * Authors:
1384+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
1385+ */
1386+#pragma once
1387+
1388+#include <QCoreApplication>
1389+#include <QSharedPointer>
1390+
1391+class QCommandLineParser;
1392+
1393+class CommandLineParser
1394+{
1395+public:
1396+ Q_ENUMS(Command)
1397+ enum class Command {LIST_LOCAL_SECTIONS, LIST_REMOTE_SECTIONS, LIST_STORAGE_ACCOUNTS, BACKUP, RESTORE};
1398+ struct CommandArgs
1399+ {
1400+ Command cmd;
1401+ QStringList sections;
1402+ QString storage;
1403+ };
1404+
1405+ CommandLineParser();
1406+ ~CommandLineParser() = default;
1407+ Q_DISABLE_COPY(CommandLineParser)
1408+
1409+ bool parse(QStringList const & arguments, QCoreApplication const & app, CommandArgs & cmd_args);
1410+
1411+private:
1412+ bool handle_list_sections(QCoreApplication const & app, CommandArgs & cmd_args);
1413+ bool handle_list_storage_accounts(QCoreApplication const & app, CommandArgs & cmd_args);
1414+ bool handle_backup(QCoreApplication const & app, CommandArgs & cmd_args);
1415+ bool handle_restore(QCoreApplication const & app, CommandArgs & cmd_args);
1416+
1417+ bool check_number_of_args(QStringList const & args);
1418+
1419+ QString get_storage_string(QString const & value);
1420+
1421+ QSharedPointer<QCommandLineParser> parser_;
1422+};
1423
1424=== modified file 'src/cli/main.cpp'
1425--- src/cli/main.cpp 2016-08-30 14:16:19 +0000
1426+++ src/cli/main.cpp 2017-02-27 19:05:17 +0000
1427@@ -17,9 +17,12 @@
1428 * Charles Kerr <charles.kerr@canonical.com>
1429 * Xavi Garcia <xavi.garcia.mena@canonical.com>
1430 */
1431+#include "command-line.h"
1432+#include "command-line-client.h"
1433
1434 #include <dbus-types.h>
1435 #include <util/logging.h>
1436+#include "util/unix-signal-handler.h"
1437
1438 #include <keeper_user_interface.h>
1439
1440@@ -29,6 +32,8 @@
1441 #include <libintl.h>
1442 #include <cstdlib>
1443 #include <ctime>
1444+#include <iostream>
1445+
1446
1447 int
1448 main(int argc, char **argv)
1449@@ -37,60 +42,49 @@
1450
1451 QCoreApplication app(argc, argv);
1452 DBusTypes::registerMetaTypes();
1453-// Variant::registerMetaTypes();
1454 std::srand(unsigned(std::time(nullptr)));
1455
1456+ util::UnixSignalHandler handler([]{
1457+ CommandLineClient client;
1458+ client.run_cancel();
1459+ });
1460+ handler.setupUnixSignalHandlers();
1461+
1462 // boilerplate locale
1463 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
1464 setlocale(LC_ALL, "");
1465 bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
1466 textdomain(GETTEXT_PACKAGE);
1467
1468- if (argc == 2 && QString("--print-address") == argv[1])
1469- {
1470- qDebug() << QDBusConnection::sessionBus().baseService();
1471- }
1472-
1473- qDebug() << "Argc =" << argc;
1474- if (argc == 2 && QString("--use-uuids") == argv[1])
1475- {
1476- QScopedPointer<DBusInterfaceKeeperUser> user_iface(new DBusInterfaceKeeperUser(
1477- DBusTypes::KEEPER_SERVICE,
1478- DBusTypes::KEEPER_USER_PATH,
1479- QDBusConnection::sessionBus()
1480- ) );
1481- QDBusReply<QVariantDictMap> choices = user_iface->call("GetBackupChoices");
1482- if (!choices.isValid())
1483- {
1484- qWarning() << "Error getting backup choices:" << choices.error().message();
1485- }
1486-
1487- QStringList uuids;
1488- auto choices_values = choices.value();
1489- for(auto iter = choices_values.begin(); iter != choices_values.end(); ++iter)
1490- {
1491- const auto& values = iter.value();
1492- auto iter_values = values.find("type");
1493- if (iter_values != values.end())
1494- {
1495- if (iter_values.value().toString() == "folder")
1496- {
1497- qDebug() << "Adding uuid" << iter.key() << "with type:" << "folder";
1498- uuids << iter.key();
1499- }
1500- }
1501- }
1502-
1503- QDBusReply<void> backup_reply = user_iface->call("StartBackup", uuids);
1504-
1505- if (!backup_reply.isValid())
1506- {
1507- qWarning() << "Error starting backup:" << backup_reply.error().message();
1508- }
1509- }
1510- else
1511- {
1512- qWarning() << "FIXME";
1513+ QCoreApplication::setApplicationName("keeper");
1514+ QCoreApplication::setApplicationVersion("1.0");
1515+
1516+ CommandLineParser parser;
1517+ CommandLineClient client;
1518+ CommandLineParser::CommandArgs cmd_args;
1519+ if (parser.parse(QCoreApplication::arguments(), app, cmd_args))
1520+ {
1521+ switch(cmd_args.cmd)
1522+ {
1523+ case CommandLineParser::Command::LIST_LOCAL_SECTIONS:
1524+ client.run_list_sections(false);
1525+ exit(0);
1526+ break;
1527+ case CommandLineParser::Command::LIST_STORAGE_ACCOUNTS:
1528+ client.run_list_storage_accounts();
1529+ exit(0);
1530+ break;
1531+ case CommandLineParser::Command::LIST_REMOTE_SECTIONS:
1532+ client.run_list_sections(true, cmd_args.storage);
1533+ exit(0);
1534+ break;
1535+ case CommandLineParser::Command::BACKUP:
1536+ client.run_backup(cmd_args.sections, cmd_args.storage);
1537+ break;
1538+ case CommandLineParser::Command::RESTORE:
1539+ client.run_restore(cmd_args.sections, cmd_args.storage);
1540+ break;
1541+ };
1542 }
1543
1544
1545
1546=== modified file 'src/client/CMakeLists.txt'
1547--- src/client/CMakeLists.txt 2016-08-26 09:30:11 +0000
1548+++ src/client/CMakeLists.txt 2017-02-27 19:05:17 +0000
1549@@ -1,8 +1,24 @@
1550 add_subdirectory(qml-plugin)
1551
1552 set(
1553+ KEEPER_ERRORS_LIB
1554+ keeper-errors-lib
1555+)
1556+
1557+add_library(
1558+ ${KEEPER_ERRORS_LIB}
1559+ STATIC
1560+ keeper-errors.cpp
1561+ ${CMAKE_SOURCE_DIR}/include/client/keeper-errors.h
1562+ ${CMAKE_SOURCE_DIR}/include/client/keeper-items.h
1563+ items.cpp
1564+)
1565+
1566+set(
1567 CLIENT_HEADERS
1568 ${CMAKE_SOURCE_DIR}/include/client/client.h
1569+ ${CMAKE_SOURCE_DIR}/include/client/keeper-errors.h
1570+ ${CMAKE_SOURCE_DIR}/include/client/keeper-items.h
1571 )
1572
1573 add_library(
1574@@ -11,8 +27,14 @@
1575 ${CLIENT_HEADERS}
1576 )
1577
1578+set_target_properties(${KEEPER_CLIENT_LIB} PROPERTIES
1579+ AUTOMOC TRUE
1580+ LINK_FLAGS "-Wl,--no-undefined"
1581+)
1582+
1583 target_link_libraries(
1584 ${KEEPER_CLIENT_LIB}
1585+ ${KEEPER_ERRORS_LIB}
1586 qdbus-stubs
1587 Qt5::Core
1588 Qt5::DBus
1589
1590=== modified file 'src/client/client.cpp'
1591--- src/client/client.cpp 2016-08-26 08:48:05 +0000
1592+++ src/client/client.cpp 2017-02-27 19:05:17 +0000
1593@@ -22,34 +22,100 @@
1594 #include <client/client.h>
1595
1596 #include <qdbus-stubs/keeper_user_interface.h>
1597+#include <qdbus-stubs/DBusPropertiesInterface.h>
1598+#include <qdbus-stubs/dbus-types.h>
1599
1600 struct KeeperClientPrivate final
1601 {
1602 Q_DISABLE_COPY(KeeperClientPrivate)
1603
1604- KeeperClientPrivate(QObject* parent)
1605+ enum class TasksMode { IDLE_MODE, BACKUP_MODE, RESTORE_MODE };
1606+
1607+ explicit KeeperClientPrivate(QObject* /* parent */)
1608 : userIface(new DBusInterfaceKeeperUser(
1609 DBusTypes::KEEPER_SERVICE,
1610 DBusTypes::KEEPER_USER_PATH,
1611 QDBusConnection::sessionBus()
1612- )),
1613- status(""),
1614- progress(0),
1615- readyToBackup(false),
1616- backupBusy(false),
1617- stateTimer(parent)
1618+ ))
1619+ , propertiesIface(new DBusPropertiesInterface(
1620+ DBusTypes::KEEPER_SERVICE,
1621+ DBusTypes::KEEPER_USER_PATH,
1622+ QDBusConnection::sessionBus()
1623+ ))
1624 {
1625 }
1626
1627 ~KeeperClientPrivate() = default;
1628
1629+ struct TaskStatus
1630+ {
1631+ QString status;
1632+ double percentage;
1633+ };
1634+
1635+ static bool stateIsFinal(QString const & stateString)
1636+ {
1637+ return (stateString == "complete" || stateString == "cancelled" || stateString == "failed");
1638+ }
1639+
1640+ bool checkAllTasksFinished(keeper::Items const & state) const
1641+ {
1642+ bool ret = true;
1643+ for (auto iter = state.begin(); ret && (iter != state.end()); ++iter)
1644+ {
1645+ auto statusString = (*iter).get_status();
1646+ ret = stateIsFinal(statusString);
1647+ }
1648+ return ret;
1649+ }
1650+
1651+ static keeper::Items getValue(QDBusMessage const & message, keeper::Error & error)
1652+ {
1653+ if (message.errorMessage().isEmpty())
1654+ {
1655+ if (message.arguments().count() != 1)
1656+ {
1657+ error = keeper::Error::UNKNOWN;
1658+ return keeper::Items();
1659+ }
1660+
1661+ auto value = message.arguments().at(0);
1662+ if (value.typeName() != QStringLiteral("QDBusArgument"))
1663+ {
1664+ error = keeper::Error::UNKNOWN;
1665+ return keeper::Items();
1666+ }
1667+ auto dbus_arg = value.value<QDBusArgument>();
1668+ error = keeper::Error::OK;
1669+ keeper::Items ret;
1670+ dbus_arg >> ret;
1671+ return ret;
1672+ }
1673+ if (message.arguments().count() != 2)
1674+ {
1675+ error = keeper::Error::UNKNOWN;
1676+ return keeper::Items();
1677+ }
1678+
1679+ // pick up the error
1680+ bool valid;
1681+ error = keeper::convert_from_dbus_variant(message.arguments().at(1), &valid);
1682+ if (!valid)
1683+ {
1684+ error = keeper::Error::UNKNOWN;
1685+ }
1686+ return keeper::Items();
1687+ }
1688+
1689 QScopedPointer<DBusInterfaceKeeperUser> userIface;
1690+ QScopedPointer<DBusPropertiesInterface> propertiesIface;
1691 QString status;
1692- QMap<QString, QVariantMap> backups;
1693- double progress;
1694- bool readyToBackup;
1695- bool backupBusy;
1696- QTimer stateTimer;
1697+ keeper::Items backups;
1698+ double progress = 0;
1699+ bool readyToBackup = false;
1700+ bool backupBusy = false;
1701+ QMap<QString, TaskStatus> taskStatus;
1702+ TasksMode mode = TasksMode::IDLE_MODE;
1703 };
1704
1705 KeeperClient::KeeperClient(QObject* parent) :
1706@@ -60,13 +126,15 @@
1707
1708 // Store backups list locally with an additional "enabled" pair to keep track enabled states
1709 // TODO: We should be listening to a backupChoicesChanged signal to keep this list updated
1710- d->backups = getBackupChoices();
1711+ keeper::Error error;
1712+ d->backups = getBackupChoices(error);
1713+
1714 for(auto iter = d->backups.begin(); iter != d->backups.end(); ++iter)
1715 {
1716 iter.value()["enabled"] = false;
1717 }
1718
1719- connect(&d->stateTimer, &QTimer::timeout, this, &KeeperClient::stateUpdated);
1720+ connect(d->propertiesIface.data(), &DBusPropertiesInterface::PropertiesChanged, this, &KeeperClient::stateUpdated);
1721 }
1722
1723 KeeperClient::~KeeperClient() = default;
1724@@ -77,7 +145,7 @@
1725 for(auto iter = d->backups.begin(); iter != d->backups.end(); ++iter)
1726 {
1727 // TODO: We currently only support "folder" type backups
1728- if (iter.value().value("type").toString() == "folder")
1729+ if (iter.value().get_type() == keeper::Item::FOLDER_VALUE)
1730 {
1731 returnList.append(iter.key());
1732 }
1733@@ -120,17 +188,19 @@
1734 }
1735 }
1736
1737+ d->taskStatus[uuid] = KeeperClientPrivate::TaskStatus{"", 0.0};
1738+
1739 Q_EMIT readyToBackupChanged();
1740 }
1741
1742-void KeeperClient::startBackup()
1743+void KeeperClient::enableRestore(QString uuid, bool enabled)
1744 {
1745- // TODO: Instead of polling for state, we should be listening to a stateChanged signal
1746- if (!d->stateTimer.isActive())
1747- {
1748- d->stateTimer.start(200);
1749- }
1750+ // Until we re-design the client we treat restores as backups
1751+ enableBackup(uuid, enabled);
1752+}
1753
1754+void KeeperClient::startBackup(QString const & storage)
1755+{
1756 // Determine which backups are enabled, and start only those
1757 QStringList backupList;
1758 for(auto iter = d->backups.begin(); iter != d->backups.end(); ++iter)
1759@@ -143,8 +213,9 @@
1760
1761 if (!backupList.empty())
1762 {
1763- startBackup(backupList);
1764+ startBackup(backupList, storage);
1765
1766+ d->mode = KeeperClientPrivate::TasksMode::BACKUP_MODE;
1767 d->status = "Preparing Backup...";
1768 Q_EMIT statusChanged();
1769 d->backupBusy = true;
1770@@ -152,27 +223,60 @@
1771 }
1772 }
1773
1774+void KeeperClient::startRestore(QString const & storage)
1775+{
1776+ // Determine which restores are enabled, and start only those
1777+ QStringList restoreList;
1778+ for(auto iter = d->backups.begin(); iter != d->backups.end(); ++iter)
1779+ {
1780+ if (iter.value().value("enabled").toBool())
1781+ {
1782+ restoreList.append(iter.key());
1783+ }
1784+ }
1785+
1786+ if (!restoreList.empty())
1787+ {
1788+ startRestore(restoreList, storage);
1789+
1790+ d->mode = KeeperClientPrivate::TasksMode::RESTORE_MODE;
1791+ d->status = "Preparing Restore...";
1792+ Q_EMIT statusChanged();
1793+ d->backupBusy = true;
1794+ Q_EMIT backupBusyChanged();
1795+ }
1796+}
1797+
1798+void KeeperClient::cancel()
1799+{
1800+ QDBusReply<void> cancelReply = d->userIface->call("Cancel");
1801+
1802+ if (!cancelReply.isValid())
1803+ {
1804+ qWarning() << "Error canceling" << cancelReply.error().message();
1805+ }
1806+}
1807+
1808 QString KeeperClient::getBackupName(QString uuid)
1809 {
1810- return d->backups.value(uuid).value("display-name").toString();
1811-}
1812-
1813-QMap<QString, QVariantMap> KeeperClient::getBackupChoices() const
1814-{
1815- QDBusReply<QMap<QString, QVariantMap>> choices = d->userIface->call("GetBackupChoices");
1816-
1817- if (!choices.isValid())
1818- {
1819- qWarning() << "Error getting backup choices:" << choices.error().message();
1820- return QMap<QString, QVariantMap>();
1821- }
1822-
1823- return choices.value();
1824-}
1825-
1826-void KeeperClient::startBackup(const QStringList& uuids) const
1827-{
1828- QDBusReply<void> backupReply = d->userIface->call("StartBackup", uuids);
1829+ return d->backups.value(uuid).get_display_name();
1830+}
1831+
1832+keeper::Items KeeperClient::getBackupChoices(keeper::Error & error) const
1833+{
1834+ QDBusMessage choices = d->userIface->call("GetBackupChoices");
1835+ return KeeperClientPrivate::getValue(choices, error);
1836+}
1837+
1838+keeper::Items KeeperClient::getRestoreChoices(QString const & storage, keeper::Error & error) const
1839+{
1840+ QDBusMessage choices = d->userIface->call("GetRestoreChoices", storage);
1841+ return KeeperClientPrivate::getValue(choices, error);
1842+}
1843+
1844+void KeeperClient::startBackup(const QStringList& uuids, QString const & storage) const
1845+{
1846+ QDBusReply<void> backupReply = d->userIface->call("StartBackup", uuids, storage);
1847
1848 if (!backupReply.isValid())
1849 {
1850@@ -180,51 +284,110 @@
1851 }
1852 }
1853
1854-QMap<QString, QVariantMap> KeeperClient::getState() const
1855+void KeeperClient::startRestore(const QStringList& uuids, QString const & storage) const
1856+{
1857+ QDBusReply<void> backupReply = d->userIface->call("StartRestore", uuids, storage);
1858+
1859+ if (!backupReply.isValid())
1860+ {
1861+ qWarning() << "Error starting restore:" << backupReply.error().message();
1862+ }
1863+}
1864+
1865+keeper::Items KeeperClient::getState() const
1866 {
1867 return d->userIface->state();
1868 }
1869
1870+QStringList KeeperClient::getStorageAccounts() const
1871+{
1872+ QDBusPendingReply<QStringList> accountsReply = d->userIface->call("GetStorageAccounts");
1873+
1874+ accountsReply.waitForFinished();
1875+ if (!accountsReply.isValid())
1876+ {
1877+ qWarning() << "Error retrieving storage accounts:" << accountsReply.error().message();
1878+ }
1879+
1880+ return accountsReply.value();
1881+}
1882+
1883 void KeeperClient::stateUpdated()
1884 {
1885 auto states = getState();
1886
1887 if (!states.empty())
1888 {
1889- // Calculate current total progress
1890- // TODO: May be better to monitor each backup's progress separately instead of total
1891- // to avoid irregular jumps in progress between larger and smaller backups
1892+ for (auto const & uuid : d->taskStatus.keys())
1893+ {
1894+ auto iter_state = states.find(uuid);
1895+ if (iter_state == states.end())
1896+ {
1897+ qWarning() << "State for uuid: " << uuid << " was not found";
1898+ }
1899+ keeper::Item keeper_item((*iter_state));
1900+ auto progress = keeper_item.get_percent_done();
1901+ auto status = keeper_item.get_status();
1902+ auto keeper_error = keeper_item.get_error();
1903+
1904+ auto current_state = d->taskStatus[uuid];
1905+ if (current_state.status != status || current_state.percentage < progress)
1906+ {
1907+ d->taskStatus[uuid].status = status;
1908+ d->taskStatus[uuid].percentage = progress;
1909+ Q_EMIT(taskStatusChanged(keeper_item.get_display_name(), status, progress, keeper_error));
1910+ }
1911+ }
1912+
1913 double totalProgress = 0;
1914 for (auto const& state : states)
1915 {
1916- totalProgress += state.value("percent-done").toDouble();
1917+ keeper::Item keeper_item(state);
1918+ totalProgress += keeper_item.get_percent_done();
1919 }
1920
1921 d->progress = totalProgress / states.count();
1922 Q_EMIT progressChanged();
1923
1924+ auto allTasksFinished = d->checkAllTasksFinished(states);
1925 // Update backup status
1926+ QString statusString;
1927+ if (d->mode == KeeperClientPrivate::TasksMode::BACKUP_MODE)
1928+ {
1929+ statusString = QStringLiteral("Backup");
1930+ }
1931+ else if (d->mode == KeeperClientPrivate::TasksMode::RESTORE_MODE)
1932+ {
1933+ statusString = QStringLiteral("Restore");
1934+ }
1935 if (d->progress > 0 && d->progress < 1)
1936 {
1937- d->status = "Backup In Progress...";
1938- Q_EMIT statusChanged();
1939-
1940- d->backupBusy = true;
1941- Q_EMIT backupBusyChanged();
1942- }
1943- else if (d->progress >= 1)
1944- {
1945- d->status = "Backup Complete";
1946+ d->status = statusString + QStringLiteral(" In Progress...");
1947+ Q_EMIT statusChanged();
1948+
1949+ d->backupBusy = true;
1950+ Q_EMIT backupBusyChanged();
1951+ }
1952+ else if (d->progress >= 1 && !allTasksFinished)
1953+ {
1954+ d->status = statusString + QStringLiteral(" Finishing...");
1955+ Q_EMIT statusChanged();
1956+
1957+ d->backupBusy = true;
1958+ Q_EMIT backupBusyChanged();
1959+ }
1960+ else if (allTasksFinished)
1961+ {
1962+ d->status = statusString + QStringLiteral(" Complete");
1963 Q_EMIT statusChanged();
1964
1965 d->backupBusy = false;
1966 Q_EMIT backupBusyChanged();
1967+ }
1968
1969- // Stop state timer
1970- if (d->stateTimer.isActive())
1971- {
1972- d->stateTimer.stop();
1973- }
1974+ if (d->checkAllTasksFinished(states))
1975+ {
1976+ Q_EMIT(finished());
1977 }
1978 }
1979 }
1980
1981=== added file 'src/client/items.cpp'
1982--- src/client/items.cpp 1970-01-01 00:00:00 +0000
1983+++ src/client/items.cpp 2017-02-27 19:05:17 +0000
1984@@ -0,0 +1,196 @@
1985+/*
1986+ * Copyright (C) 2016 Canonical, Ltd.
1987+ *
1988+ * This program is free software: you can redistribute it and/or modify it
1989+ * under the terms of the GNU General Public License version 3, as published
1990+ * by the Free Software Foundation.
1991+ *
1992+ * This program is distributed in the hope that it will be useful, but
1993+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1994+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1995+ * PURPOSE. See the GNU General Public License for more details.
1996+ *
1997+ * You should have received a copy of the GNU General Public License along
1998+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1999+ *
2000+ * Authors:
2001+ * Xavi Garcia Mena <xavi.garcia.mena@canonical.com>
2002+ */
2003+
2004+#include "client/keeper-items.h"
2005+
2006+#include <QtDBus>
2007+#include <QVariantMap>
2008+
2009+namespace keeper
2010+{
2011+// json keys
2012+constexpr const char PROPERTIES_KEY[] = "properties";
2013+
2014+// keys
2015+const QString Item::UUID_KEY = QStringLiteral("uuid");
2016+const QString Item::TYPE_KEY = QStringLiteral("type");
2017+const QString Item::SUBTYPE_KEY = QStringLiteral("subtype");
2018+const QString Item::NAME_KEY = QStringLiteral("name");
2019+const QString Item::PACKAGE_KEY = QStringLiteral("package");
2020+const QString Item::TITLE_KEY = QStringLiteral("title");
2021+const QString Item::VERSION_KEY = QStringLiteral("version");
2022+const QString Item::FILE_NAME_KEY = QStringLiteral("file-name");
2023+const QString Item::DIR_NAME_KEY = QStringLiteral("dir-name");
2024+const QString Item::DISPLAY_NAME_KEY = QStringLiteral("display-name");
2025+const QString Item::STATUS_KEY = QStringLiteral("action");
2026+const QString Item::ERROR_KEY = QStringLiteral("error");
2027+const QString Item::PERCENT_DONE_KEY = QStringLiteral("percent-done");
2028+const QString Item::SPEED_KEY = QStringLiteral("speed");
2029+
2030+
2031+// values
2032+const QString Item::FOLDER_VALUE = QStringLiteral("folder");
2033+const QString Item::SYSTEM_DATA_VALUE = QStringLiteral("system-data");
2034+const QString Item::APPLICATION_VALUE = QStringLiteral("application");
2035+
2036+Item::Item() = default;
2037+
2038+Item::Item(QVariantMap const & values)
2039+ : QVariantMap(values)
2040+{
2041+}
2042+
2043+Item::~Item() = default;
2044+
2045+QVariant Item::get_property_value(QString const & property) const
2046+{
2047+ auto iter = this->find(property);
2048+ if (iter != this->end())
2049+ {
2050+ return (*iter);
2051+ }
2052+ else
2053+ {
2054+ return QVariant();
2055+ }
2056+}
2057+
2058+void Item::set_property_value(QString const& property, QVariant const& value)
2059+{
2060+ this->insert(property, value);
2061+}
2062+
2063+template<typename T> T Item::get_property(QString const & property, bool * valid) const
2064+{
2065+ auto it = this->find(property);
2066+
2067+ if (it == this->end())
2068+ {
2069+ if (valid != nullptr)
2070+ *valid = false;
2071+
2072+ return T{};
2073+ }
2074+
2075+ if (valid != nullptr)
2076+ *valid = true;
2077+
2078+ return it->value<T>();
2079+}
2080+
2081+bool Item::is_valid()const
2082+{
2083+ // check for required properties
2084+ for (auto& property : {TYPE_KEY, DISPLAY_NAME_KEY})
2085+ if (!this->has_property(property))
2086+ return false;
2087+
2088+ return true;
2089+}
2090+
2091+bool Item::has_property(QString const & property) const
2092+{
2093+ return this->contains(property);
2094+}
2095+
2096+QString Item::get_uuid(bool *valid) const
2097+{
2098+ return get_property<QString>(UUID_KEY, valid);
2099+}
2100+
2101+QString Item::get_type(bool *valid) const
2102+{
2103+ return get_property<QString>(TYPE_KEY, valid);
2104+}
2105+
2106+QString Item::get_display_name(bool *valid) const
2107+{
2108+ return get_property<QString>(DISPLAY_NAME_KEY, valid);
2109+}
2110+
2111+QString Item::get_dir_name(bool *valid) const
2112+{
2113+ return get_property<QString>(DIR_NAME_KEY, valid);
2114+}
2115+
2116+QString Item::get_status(bool *valid) const
2117+{
2118+ return get_property<QString>(STATUS_KEY, valid);
2119+}
2120+
2121+double Item::get_percent_done(bool *valid) const
2122+{
2123+ return get_property<double>(PERCENT_DONE_KEY, valid);
2124+}
2125+
2126+keeper::Error Item::get_error(bool *valid) const
2127+{
2128+ auto it = this->find(ERROR_KEY);
2129+
2130+ // if no error was sent, things must be OK
2131+ if (it == this->end())
2132+ {
2133+ return keeper::Error::OK;
2134+ }
2135+
2136+ return keeper::convert_from_dbus_variant(*it, valid);
2137+}
2138+
2139+QString Item::get_file_name(bool *valid) const
2140+{
2141+ return get_property<QString>(FILE_NAME_KEY, valid);
2142+}
2143+
2144+void Item::registerMetaType()
2145+{
2146+ qRegisterMetaType<Item>("Item");
2147+
2148+ qDBusRegisterMetaType<Item>();
2149+}
2150+
2151+/////
2152+// Items
2153+/////
2154+
2155+
2156+Items::Items() =default;
2157+
2158+Items::~Items() =default;
2159+
2160+
2161+Items::Items(Error error)
2162+ : error_{error}
2163+{
2164+}
2165+
2166+QStringList Items::get_uuids() const
2167+{
2168+ return this->keys();
2169+}
2170+
2171+void Items::registerMetaType()
2172+{
2173+ qRegisterMetaType<Items>("Items");
2174+
2175+ qDBusRegisterMetaType<Items>();
2176+
2177+ Item::registerMetaType();
2178+}
2179+
2180+} // namespace keeper
2181
2182=== added file 'src/client/keeper-errors.cpp'
2183--- src/client/keeper-errors.cpp 1970-01-01 00:00:00 +0000
2184+++ src/client/keeper-errors.cpp 2017-02-27 19:05:17 +0000
2185@@ -0,0 +1,74 @@
2186+/*
2187+ * Copyright (C) 2016 Canonical, Ltd.
2188+ *
2189+ * This program is free software: you can redistribute it and/or modify it
2190+ * under the terms of the GNU General Public License version 3, as published
2191+ * by the Free Software Foundation.
2192+ *
2193+ * This program is distributed in the hope that it will be useful, but
2194+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2195+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2196+ * PURPOSE. See the GNU General Public License for more details.
2197+ *
2198+ * You should have received a copy of the GNU General Public License along
2199+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2200+ *
2201+ * Authors:
2202+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
2203+ */
2204+
2205+#include <client/keeper-errors.h>
2206+
2207+#include <QDebug>
2208+
2209+QDBusArgument &operator<<(QDBusArgument &argument, keeper::Error value)
2210+{
2211+ argument.beginStructure();
2212+ argument << static_cast<int>(value);
2213+ argument.endStructure();
2214+ return argument;
2215+}
2216+
2217+const QDBusArgument &operator>>(const QDBusArgument &argument, keeper::Error &val)
2218+{
2219+ int int_val;
2220+ argument.beginStructure();
2221+ argument >> int_val;
2222+ val = static_cast<keeper::Error>(int_val);
2223+ argument.endStructure();
2224+ return argument;
2225+}
2226+
2227+namespace keeper
2228+{
2229+Error convert_from_dbus_variant(const QVariant & value, bool *conversion_ok)
2230+{
2231+ if (value.typeName() != QStringLiteral("QDBusArgument"))
2232+ {
2233+ qWarning() << Q_FUNC_INFO
2234+ << " Error converting dbus QVariant to Error, expected type is [ QDBusArgument ] and current type is: ["
2235+ << value.typeName() << "]";
2236+ if (conversion_ok)
2237+ *conversion_ok = false;
2238+ return Error(keeper::Error::UNKNOWN);
2239+ }
2240+ auto dbus_arg = value.value<QDBusArgument>();
2241+
2242+ if (dbus_arg.currentSignature() != "(i)")
2243+ {
2244+ qWarning() << Q_FUNC_INFO
2245+ << " Error converting dbus QVariant to Error, expected signature is \"(i)\" and current signature is: \""
2246+ << dbus_arg.currentSignature() << "\"";
2247+ if (conversion_ok)
2248+ *conversion_ok = false;
2249+ return Error(keeper::Error::UNKNOWN);
2250+ }
2251+ Error ret;
2252+ dbus_arg >> ret;
2253+
2254+ if (conversion_ok)
2255+ *conversion_ok = true;
2256+
2257+ return ret;
2258+}
2259+}
2260
2261=== modified file 'src/client/qml-plugin/CMakeLists.txt'
2262--- src/client/qml-plugin/CMakeLists.txt 2016-08-26 09:30:11 +0000
2263+++ src/client/qml-plugin/CMakeLists.txt 2017-02-27 19:05:17 +0000
2264@@ -6,7 +6,7 @@
2265 add_definitions(-DKEEPER_MAJOR=${KEEPER_MAJOR})
2266 add_definitions(-DKEEPER_MINOR=${KEEPER_MINOR})
2267
2268-include(QmlPlugins)
2269+find_package(QmlPlugins)
2270
2271 find_package(Qt5Quick REQUIRED)
2272
2273
2274=== modified file 'src/helper/CMakeLists.txt'
2275--- src/helper/CMakeLists.txt 2016-09-06 01:31:59 +0000
2276+++ src/helper/CMakeLists.txt 2017-02-27 19:05:17 +0000
2277@@ -18,6 +18,21 @@
2278 ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}
2279 )
2280
2281+set(
2282+ FOLDER_RESTORE
2283+ folder-restore.sh
2284+)
2285+configure_file(
2286+ ${CMAKE_CURRENT_SOURCE_DIR}/${FOLDER_RESTORE}.in
2287+ ${CMAKE_CURRENT_BINARY_DIR}/${FOLDER_RESTORE}
2288+)
2289+
2290+install(
2291+ PROGRAMS
2292+ ${CMAKE_CURRENT_BINARY_DIR}/${FOLDER_RESTORE}
2293+ DESTINATION
2294+ ${CMAKE_INSTALL_FULL_PKGLIBEXECDIR}
2295+)
2296
2297 #
2298 # the library
2299@@ -30,7 +45,7 @@
2300
2301 include(FindPkgConfig)
2302 pkg_check_modules(BACKUP_HELPER_DEPENDENCIES REQUIRED
2303- ubuntu-app-launch-2
2304+ ubuntu-app-launch-3
2305 properties-cpp
2306 )
2307
2308@@ -48,10 +63,12 @@
2309 ${HELPER_LIB}
2310 STATIC
2311 backup-helper.cpp
2312+ restore-helper.cpp
2313 data-dir-registry.cpp
2314 helper.cpp
2315 metadata.cpp
2316 ${CMAKE_SOURCE_DIR}/include/helper/backup-helper.h
2317+ ${CMAKE_SOURCE_DIR}/include/helper/restore-helper.h
2318 ${CMAKE_SOURCE_DIR}/include/helper/data-dir-registry.h
2319 ${CMAKE_SOURCE_DIR}/include/helper/helper.h
2320 ${CMAKE_SOURCE_DIR}/include/helper/registry.h
2321
2322=== modified file 'src/helper/backup-helper.cpp'
2323--- src/helper/backup-helper.cpp 2016-09-12 15:13:33 +0000
2324+++ src/helper/backup-helper.cpp 2017-02-27 19:05:17 +0000
2325@@ -62,8 +62,8 @@
2326 int rc = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds);
2327 if (rc == -1)
2328 {
2329- // TODO throw exception.
2330- qWarning() << "BackupHelperPrivate: error creating socket pair to communicate with helper ";
2331+ qWarning() << QStringLiteral("Error creating socket to communicate with helper");;
2332+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_SOCKET));
2333 return;
2334 }
2335
2336@@ -139,9 +139,14 @@
2337 &Uploader::commit_finished,
2338 std::function<void(bool)>{[this](bool success){
2339 qDebug() << "Commit finished";
2340- uploader_.reset();
2341 if (!success)
2342+ {
2343 write_error_ = true;
2344+ Q_EMIT(q_ptr->error(keeper::Error::COMMITTING_DATA));
2345+ }
2346+ else
2347+ uploader_committed_file_name_ = uploader_->file_name();
2348+ uploader_.reset();
2349 check_for_done();
2350 }}
2351 );
2352@@ -162,12 +167,18 @@
2353 check_for_done();
2354 }
2355
2356+ QString get_uploader_committed_file_name() const
2357+ {
2358+ return uploader_committed_file_name_;
2359+ }
2360+
2361 private:
2362
2363 void on_inactivity_detected()
2364 {
2365 stop_inactivity_timer();
2366 qWarning() << "Inactivity detected in the helper...stopping it";
2367+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_INACTIVITY_DETECTED));
2368 stop();
2369 }
2370
2371@@ -180,7 +191,6 @@
2372 {
2373 n_uploaded_ += n;
2374 q_ptr->record_data_transferred(n);
2375- qDebug("n_read %zu n_uploaded %zu (newly uploaded %zu)", size_t(n_read_), size_t(n_uploaded_), size_t(n));
2376 process_more();
2377 check_for_done();
2378 }
2379@@ -201,10 +211,10 @@
2380 if (n > 0) {
2381 n_read_ += n;
2382 upload_buffer_.append(readbuf, int(n));
2383- qDebug("upload_buffer_.size() is %zu after reading %zu from helper", size_t(upload_buffer_.size()), size_t(n));
2384 }
2385 else if (n < 0) {
2386 read_error_ = true;
2387+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_READ));
2388 stop();
2389 return;
2390 }
2391@@ -214,13 +224,13 @@
2392 const auto n = socket->write(upload_buffer_);
2393 if (n > 0) {
2394 upload_buffer_.remove(0, int(n));
2395- qDebug("upload_buffer_.size() is %zu after writing %zu to cloud", size_t(upload_buffer_.size()), size_t(n));
2396 continue;
2397 }
2398 else {
2399 if (n < 0) {
2400 write_error_ = true;
2401 qWarning() << "Write error:" << socket->errorString();
2402+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_WRITE));
2403 stop();
2404 }
2405 break;
2406@@ -251,6 +261,10 @@
2407 {
2408 if (!q_ptr->is_helper_running())
2409 {
2410+ if (n_uploaded_ > q_ptr->expected_size())
2411+ {
2412+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_WRITE));
2413+ }
2414 q_ptr->set_state(Helper::State::FAILED);
2415 }
2416 }
2417@@ -289,6 +303,7 @@
2418 bool write_error_ = false;
2419 bool cancelled_ = false;
2420 ConnectionHelper connections_;
2421+ QString uploader_committed_file_name_;
2422 };
2423
2424 /***
2425@@ -352,14 +367,21 @@
2426 {
2427 Q_D(BackupHelper);
2428
2429- qDebug() << Q_FUNC_INFO;
2430+ Helper::set_state(state);
2431 d->on_state_changed(state);
2432- Helper::set_state(state);
2433 }
2434
2435 void BackupHelper::on_helper_finished()
2436 {
2437 Q_D(BackupHelper);
2438+
2439 Helper::on_helper_finished();
2440 d->on_helper_finished();
2441 }
2442+
2443+QString BackupHelper::get_uploader_committed_file_name() const
2444+{
2445+ Q_D(const BackupHelper);
2446+
2447+ return d->get_uploader_committed_file_name();
2448+}
2449
2450=== modified file 'src/helper/data-dir-registry.cpp'
2451--- src/helper/data-dir-registry.cpp 2016-08-10 02:29:17 +0000
2452+++ src/helper/data-dir-registry.cpp 2017-02-27 19:05:17 +0000
2453@@ -49,15 +49,27 @@
2454
2455 QStringList get_backup_helper_urls(Metadata const& task)
2456 {
2457+ return get_helper_urls(task, "backup");
2458+ }
2459+
2460+ QStringList get_restore_helper_urls(Metadata const& task)
2461+ {
2462+ return get_helper_urls(task, "restore");
2463+ }
2464+
2465+private:
2466+
2467+ QStringList get_helper_urls(Metadata const& task, QString const & prop)
2468+ {
2469 QStringList ret;
2470
2471- QString type;
2472- if (task.get_property(Metadata::TYPE_KEY, type))
2473+ auto type = task.get_type();
2474+ if (!type.isEmpty())
2475 {
2476- auto it = registry_.find(std::make_pair(type,QStringLiteral("backup")));
2477+ auto it = registry_.find(std::make_pair(type,prop));
2478 if (it == registry_.end())
2479 {
2480- qCritical() << "can't get backup helper urls for unhandled type" << type;
2481+ qCritical() << "can't get " << prop << " helper urls for unhandled type" << type;
2482 }
2483 else
2484 {
2485@@ -73,30 +85,28 @@
2486 return ret;
2487 }
2488
2489-private:
2490-
2491 // replace "${key}" with task.get_property("key")
2492 QStringList perform_url_substitution(Metadata const& task, QStringList const& urls_in)
2493 {
2494 std::array<QString,6> keys = {
2495- Metadata::TYPE_KEY,
2496- Metadata::SUBTYPE_KEY,
2497- Metadata::NAME_KEY,
2498- Metadata::PACKAGE_KEY,
2499- Metadata::TITLE_KEY,
2500- Metadata::VERSION_KEY
2501+ keeper::Item::TYPE_KEY,
2502+ keeper::Item::SUBTYPE_KEY,
2503+ keeper::Item::NAME_KEY,
2504+ keeper::Item::PACKAGE_KEY,
2505+ keeper::Item::TITLE_KEY,
2506+ keeper::Item::VERSION_KEY
2507 };
2508
2509 QStringList urls {urls_in};
2510
2511 for (auto const& key : keys)
2512 {
2513- QString after;
2514- if (task.get_property(key, after))
2515+ QVariant after = task.get_property_value(key);
2516+ if (after.isValid())
2517 {
2518 QString before = QStringLiteral("${%1}").arg(key);
2519 for (auto& url : urls)
2520- url.replace(before, after);
2521+ url.replace(before, after.toString());
2522 }
2523 }
2524
2525@@ -139,6 +149,10 @@
2526 * "backup-urls": [
2527 * "/path/to/helper.sh",
2528 * "${subtype}"
2529+ * ],
2530+ * "restore-urls": [
2531+ * "/path/to/helper.sh",
2532+ * "${subtype}"
2533 * ]
2534 * }
2535 * }
2536@@ -156,25 +170,37 @@
2537 if (error.error != QJsonParseError::NoError)
2538 qCritical() << path << "parse error at offset" << error.offset << error.errorString();
2539
2540- auto obj = doc.object();
2541+ const auto obj = doc.object();
2542 for (auto tit=obj.begin(), tend=obj.end(); tit!=tend; ++tit)
2543 {
2544 auto const type = tit.key();
2545- auto& info = registry_[std::make_pair(type,QStringLiteral("backup"))];
2546-
2547 auto const props = tit.value().toObject();
2548- auto const urls_jsonval = props["backup-urls"];
2549+
2550+ auto const &urls_jsonval = props["backup-urls"];
2551 if (urls_jsonval.isArray())
2552 {
2553+ auto& info = registry_[std::make_pair(type,QStringLiteral("backup"))];
2554 for (auto url_jsonval : urls_jsonval.toArray())
2555 {
2556 info.urls.push_back(url_jsonval.toString());
2557 }
2558+ qDebug() << "loaded" << type << "backup urls from" << path;
2559+ for(auto const& url : info.urls)
2560+ qDebug() << "\turl:" << url;
2561 }
2562
2563- qDebug() << "loaded" << type << "backup urls from" << path;
2564- for(auto const& url : info.urls)
2565- qDebug() << "\turl:" << url;
2566+ auto const &urls_jsonval_restore = props["restore-urls"];
2567+ if (urls_jsonval_restore.isArray())
2568+ {
2569+ auto& info = registry_[std::make_pair(type,QStringLiteral("restore"))];
2570+ for (auto url_jsonval : urls_jsonval_restore.toArray())
2571+ {
2572+ info.urls.push_back(url_jsonval.toString());
2573+ }
2574+ qDebug() << "loaded" << type << "restore urls from" << path;
2575+ for(auto const& url : info.urls)
2576+ qDebug() << "\turl:" << url;
2577+ }
2578 }
2579 }
2580 }
2581@@ -198,3 +224,9 @@
2582 {
2583 return impl_->get_backup_helper_urls(task);
2584 }
2585+
2586+QStringList
2587+DataDirRegistry::get_restore_helper_urls(Metadata const& task)
2588+{
2589+ return impl_->get_restore_helper_urls(task);
2590+}
2591
2592=== modified file 'src/helper/folder-backup.sh.in'
2593--- src/helper/folder-backup.sh.in 2016-08-10 05:01:08 +0000
2594+++ src/helper/folder-backup.sh.in 2017-02-27 19:05:17 +0000
2595@@ -20,4 +20,4 @@
2596 #
2597
2598 echo $PWD
2599-find ./ -type f -print0 | @CMAKE_INSTALL_FULL_PKGLIBEXECDIR@/keeper-tar-create -a /com/canonical/keeper/helper
2600+find ./ -type f -print0 | @CMAKE_INSTALL_FULL_PKGLIBEXECDIR@/keeper-tar -a /com/canonical/keeper/helper
2601
2602=== added file 'src/helper/folder-restore.sh.in'
2603--- src/helper/folder-restore.sh.in 1970-01-01 00:00:00 +0000
2604+++ src/helper/folder-restore.sh.in 2017-02-27 19:05:17 +0000
2605@@ -0,0 +1,23 @@
2606+#!/bin/bash
2607+#
2608+# Copyright (C) 2016 Canonical, Ltd.
2609+#
2610+# This program is free software: you can redistribute it and/or modify it
2611+# under the terms of the GNU General Public License version 3, as published
2612+# by the Free Software Foundation.
2613+#
2614+# This program is distributed in the hope that it will be useful, but
2615+# WITHOUT ANY WARRANTY; without even the implied warranties of
2616+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2617+# PURPOSE. See the GNU General Public License for more details.
2618+#
2619+# You should have received a copy of the GNU General Public License along
2620+# with this program. If not, see <http://www.gnu.org/licenses/>.
2621+#
2622+# Authors:
2623+# Xavi Garcia <xavi.garcia.mena@canonical.com>
2624+# Charles Kerr <charles.kerr@canonical.com>
2625+#
2626+
2627+echo $PWD
2628+@CMAKE_INSTALL_FULL_PKGLIBEXECDIR@/keeper-untar -a /com/canonical/keeper/helper
2629
2630=== modified file 'src/helper/helper.cpp'
2631--- src/helper/helper.cpp 2016-09-12 15:13:33 +0000
2632+++ src/helper/helper.cpp 2017-02-27 19:05:17 +0000
2633@@ -75,7 +75,6 @@
2634 uint32_t
2635 speed_bytes_per_second(uint64_t now, unsigned int interval_msec=HISTORY_MSEC) const
2636 {
2637-qDebug() << "now" << now << "cache_time" << cache_time << "interval_msec" << interval_msec;
2638 if (cache_time != now)
2639 {
2640 auto i = newest;
2641@@ -88,22 +87,18 @@
2642 break;
2643
2644 bytes += transfers[i].size;
2645-qDebug() << "i" << i << "transfers[i] .date" << transfers[i].date << ".size" << transfers[i].size << "sum" << bytes;
2646
2647 if (--i == -1) {
2648 i = HISTORY_SIZE - 1; // circular history
2649-qDebug() << "circular history";
2650 }
2651
2652 if (i == newest) {
2653-qDebug() << "i" << i << "transfers[i] .date" << transfers[i].date << ".size" << transfers[i].size << "sum" << bytes;
2654 break; // we've come all the way around
2655 }
2656 }
2657
2658 cache_val = uint32_t((bytes * 1000u) / interval_msec);
2659 cache_time = now;
2660-qDebug() << "cache_val" << cache_val << "cache_time" << cache_time;
2661 }
2662
2663 return cache_val;
2664@@ -170,7 +165,10 @@
2665 {
2666 qDebug() << "changing state of helper" << static_cast<void*>(this) << "from" << q_ptr->to_string(state_) << "to" << q_ptr->to_string(state);
2667 state_ = state;
2668- q_ptr->state_changed(state);
2669+ QMetaObject::invokeMethod(q_ptr,
2670+ "state_changed",
2671+ Qt::QueuedConnection,
2672+ Q_ARG(Helper::State, state));
2673 }
2674 }
2675
2676@@ -337,7 +335,6 @@
2677
2678 if (is_noteworthy)
2679 {
2680- qDebug() << "emitting percent-done-changed" << percent_done_;
2681 Q_EMIT(q_ptr->percent_done_changed(percent_done_));
2682 last_notified_percent_done_ = percent_done_;
2683 }
2684@@ -356,7 +353,8 @@
2685
2686 void on_max_time_waiting_for_ual_started()
2687 {
2688- qDebug() << "Max time reached waiting for UAL to start";
2689+ qWarning() << "Maximum time reached waiting for the helper to start.";
2690+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_START_TIMEOUT));
2691 q_ptr->set_state(Helper::State::FAILED);
2692 stop_wait_for_ual_timer();
2693 }
2694
2695=== modified file 'src/helper/metadata.cpp'
2696--- src/helper/metadata.cpp 2016-09-06 18:51:30 +0000
2697+++ src/helper/metadata.cpp 2017-02-27 19:05:17 +0000
2698@@ -19,63 +19,58 @@
2699
2700 #include "helper/metadata.h"
2701
2702-///
2703-///
2704-
2705-// Metadata keys
2706-const QString Metadata::TYPE_KEY = QStringLiteral("type");
2707-const QString Metadata::SUBTYPE_KEY = QStringLiteral("subtype");
2708-const QString Metadata::NAME_KEY = QStringLiteral("name");
2709-const QString Metadata::PACKAGE_KEY = QStringLiteral("package");
2710-const QString Metadata::TITLE_KEY = QStringLiteral("title");
2711-const QString Metadata::VERSION_KEY = QStringLiteral("version");
2712-
2713-// Metadata values
2714-const QString Metadata::FOLDER_VALUE = QStringLiteral("folder");
2715-const QString Metadata::SYSTEM_DATA_VALUE = QStringLiteral("system-data");
2716-const QString Metadata::APPLICATION_VALUE = QStringLiteral("application");
2717+#include <QDebug>
2718+#include <QJsonArray>
2719+#include <QJsonDocument>
2720+
2721+///
2722+///
2723+
2724+// JSON Keys
2725+namespace
2726+{
2727+ constexpr const char PROPERTIES_KEY[] = "properties";
2728+}
2729
2730 ///
2731 ///
2732
2733 Metadata::Metadata()
2734- : uuid_()
2735- , display_name_()
2736- , properties_()
2737-{
2738+ : keeper::Item()
2739+{
2740+}
2741+
2742+Metadata::Metadata(QJsonObject const & json)
2743+ : keeper::Item()
2744+{
2745+ auto properties = json[PROPERTIES_KEY].toObject();
2746+ for (auto const & key : properties.keys())
2747+ {
2748+ this->insert(key, properties[key].toString());
2749+ }
2750 }
2751
2752 Metadata::Metadata(QString const& uuid, QString const& display_name)
2753- : uuid_(uuid)
2754- , display_name_(display_name)
2755- , properties_()
2756-{
2757-}
2758-
2759-bool
2760-Metadata::get_property(QString const& property_name, QString& setme) const
2761-{
2762- auto it = properties_.constFind(property_name);
2763- const bool found = it != properties_.end();
2764-
2765- if (found)
2766- setme = it.value();
2767-
2768- return found;
2769-}
2770-
2771-void
2772-Metadata::set_property(QString const& property_name, QString const& value)
2773-{
2774- properties_.insert(property_name, value);
2775-}
2776-
2777-QMap<QString,QString>
2778-Metadata::get_public_properties() const
2779-{
2780- // they're all public so far...
2781- auto ret = properties_;
2782- ret.insert(QStringLiteral("uuid"), uuid_);
2783- ret.insert(QStringLiteral("display-name"), display_name_);
2784+ : keeper::Item()
2785+{
2786+ this->insert(keeper::Item::UUID_KEY, uuid);
2787+ this->insert(keeper::Item::DISPLAY_NAME_KEY, display_name);
2788+}
2789+
2790+QJsonObject
2791+Metadata::json() const
2792+{
2793+ QJsonArray json_properties;
2794+ QJsonObject properties_obj;
2795+ for (auto iter = this->begin(); iter != this->end(); ++iter)
2796+ {
2797+ properties_obj[iter.key()] = (*iter).toString();
2798+ }
2799+
2800+ QJsonObject ret
2801+ {
2802+ { PROPERTIES_KEY, properties_obj }
2803+ };
2804+
2805 return ret;
2806 }
2807
2808=== added file 'src/helper/restore-helper.cpp'
2809--- src/helper/restore-helper.cpp 1970-01-01 00:00:00 +0000
2810+++ src/helper/restore-helper.cpp 2017-02-27 19:05:17 +0000
2811@@ -0,0 +1,377 @@
2812+/*
2813+ * Copyright (C) 2016 Canonical, Ltd.
2814+ *
2815+ * This program is free software: you can redistribute it and/or modify it
2816+ * under the terms of the GNU General Public License version 3, as published
2817+ * by the Free Software Foundation.
2818+ *
2819+ * This program is distributed in the hope that it will be useful, but
2820+ * WITHOUT ANY WARRANTY; without even the implied warranties of
2821+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
2822+ * PURPOSE. See the GNU General Public License for more details.
2823+ *
2824+ * You should have received a copy of the GNU General Public License along
2825+ * with this program. If not, see <http://www.gnu.org/licenses/>.
2826+ *
2827+ * Authors:
2828+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
2829+ * Charles Kerr <charles.kerr@canonical.com>
2830+ */
2831+
2832+#include "util/connection-helper.h"
2833+#include "helper/restore-helper.h"
2834+#include "service/app-const.h" // HELPER_TYPE
2835+
2836+#include <QByteArray>
2837+#include <QDebug>
2838+#include <QLocalSocket>
2839+#include <QMap>
2840+#include <QObject>
2841+#include <QString>
2842+#include <QTimer>
2843+#include <QVector>
2844+
2845+#include <fcntl.h>
2846+#include <sys/types.h>
2847+#include <sys/socket.h>
2848+
2849+#include <functional> // std::bind()
2850+
2851+
2852+class RestoreHelperPrivate
2853+{
2854+public:
2855+
2856+ explicit RestoreHelperPrivate(
2857+ RestoreHelper* backup_helper
2858+ )
2859+ : q_ptr(backup_helper)
2860+ {
2861+ // listen for inactivity from storage framework
2862+ QObject::connect(&timer_, &QTimer::timeout,
2863+ std::bind(&RestoreHelperPrivate::on_inactivity_detected, this)
2864+ );
2865+
2866+ // fire up the sockets
2867+ int fds[2];
2868+ int rc = socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, fds);
2869+ if (rc == -1)
2870+ {
2871+ qWarning() << QStringLiteral("Error creating socket to communicate with helper");;
2872+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_SOCKET));
2873+ return;
2874+ }
2875+
2876+ // helper socket is for the client.
2877+ // We don't use a QLocalSocket here as it buffers data and it makes the helper miss packets.
2878+ helper_socket_ = fds[1];
2879+
2880+ write_socket_.setSocketDescriptor(fds[0], QLocalSocket::ConnectedState, QIODevice::WriteOnly);
2881+ }
2882+
2883+ ~RestoreHelperPrivate() = default;
2884+
2885+ Q_DISABLE_COPY(RestoreHelperPrivate)
2886+
2887+ void start(QStringList const& urls)
2888+ {
2889+ q_ptr->Helper::start(urls);
2890+ reset_inactivity_timer();
2891+ }
2892+
2893+ void set_downloader(std::shared_ptr<Downloader> const& downloader)
2894+ {
2895+ n_read_ = 0;
2896+ n_uploaded_ = 0;
2897+ read_error_ = false;
2898+ write_error_ = false;
2899+ cancelled_ = false;
2900+
2901+ q_ptr->set_expected_size(downloader->file_size());
2902+ downloader_ = downloader;
2903+
2904+ connections_.remember(QObject::connect(
2905+ &write_socket_, &QLocalSocket::bytesWritten,
2906+ std::bind(&RestoreHelperPrivate::on_data_uploaded, this, std::placeholders::_1)
2907+ ));
2908+
2909+ // listen for data ready to read
2910+ QObject::connect(downloader_->socket().get(), &QLocalSocket::readyRead,
2911+ std::bind(&RestoreHelperPrivate::on_ready_read, this)
2912+ );
2913+
2914+ // TODO investigate why UAL takes so long to call the helper started callback
2915+ // At this point we are sure that the helper started, as it is the helper
2916+ // the ones that asks for a downloader socket.
2917+ q_ptr->Helper::on_helper_started();
2918+
2919+ // maybe there's data already to be read
2920+ process_more();
2921+
2922+ reset_inactivity_timer();
2923+ }
2924+
2925+ void stop()
2926+ {
2927+ write_socket_.disconnectFromServer();
2928+ cancelled_ = true;
2929+ q_ptr->Helper::stop();
2930+ }
2931+
2932+ int get_helper_socket() const
2933+ {
2934+ return helper_socket_;
2935+ }
2936+
2937+ QString to_string(Helper::State state) const
2938+ {
2939+ return state == Helper::State::STARTED
2940+ ? QStringLiteral("restoring")
2941+ : q_ptr->Helper::to_string(state);
2942+ }
2943+
2944+ void on_state_changed(Helper::State state)
2945+ {
2946+ switch (state)
2947+ {
2948+ case Helper::State::CANCELLED:
2949+ case Helper::State::FAILED:
2950+ qDebug() << "cancelled/failed, calling downloader_.reset()";
2951+ downloader_.reset();
2952+ break;
2953+
2954+ case Helper::State::DATA_COMPLETE: {
2955+ qDebug() << "Restore helper finished, calling downloader_.finish()";
2956+ write_socket_.disconnectFromServer();
2957+ downloader_->finish();
2958+ downloader_.reset();
2959+ break;
2960+ }
2961+
2962+ //case Helper::State::NOT_STARTED:
2963+ //case Helper::State::STARTED:
2964+ default:
2965+ break;
2966+ }
2967+ }
2968+
2969+ void on_helper_finished()
2970+ {
2971+ stop_inactivity_timer();
2972+ check_for_done();
2973+ }
2974+
2975+private:
2976+
2977+ void on_inactivity_detected()
2978+ {
2979+ stop_inactivity_timer();
2980+ qWarning() << "Inactivity detected in the helper...stopping it";
2981+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_INACTIVITY_DETECTED));
2982+ stop();
2983+ }
2984+
2985+ void on_ready_read()
2986+ {
2987+ process_more();
2988+ }
2989+
2990+ void on_data_uploaded(qint64 n)
2991+ {
2992+ n_uploaded_ += n;
2993+ q_ptr->record_data_transferred(n);
2994+ process_more();
2995+ check_for_done();
2996+ }
2997+
2998+ void process_more()
2999+ {
3000+ if (!downloader_)
3001+ return;
3002+
3003+ char readbuf[UPLOAD_BUFFER_MAX_];
3004+ auto socket = downloader_->socket();
3005+ while(socket->bytesAvailable() || upload_buffer_.size())
3006+ {
3007+ if (socket->bytesAvailable())
3008+ {
3009+ // try to fill the upload buf
3010+ int max_bytes = UPLOAD_BUFFER_MAX_ - upload_buffer_.size();
3011+ if (max_bytes > 0) {
3012+ const auto n = socket->read(readbuf, max_bytes);
3013+ if (n > 0) {
3014+ n_read_ += n;
3015+ upload_buffer_.append(readbuf, int(n));
3016+ }
3017+ else if (n < 0) {
3018+ read_error_ = true;
3019+ qDebug() << "Read error in restore helper: " << socket->errorString();
3020+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_READ));
3021+ stop();
3022+ check_for_done();
3023+ return;
3024+ }
3025+ }
3026+ }
3027+
3028+ if (upload_buffer_.size())
3029+ {
3030+ // try to empty the upload buf
3031+ const auto n = write_socket_.write(upload_buffer_);
3032+ if (n > 0) {
3033+ upload_buffer_.remove(0, int(n));
3034+ continue;
3035+ }
3036+ else {
3037+ if (n < 0) {
3038+ write_error_ = true;
3039+ qWarning() << "Write error:" << write_socket_.errorString();
3040+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_WRITE));
3041+ stop();
3042+ check_for_done();
3043+ }
3044+ break;
3045+ }
3046+ }
3047+ }
3048+
3049+ reset_inactivity_timer();
3050+ }
3051+
3052+ void reset_inactivity_timer()
3053+ {
3054+ static constexpr int MAX_TIME_WAITING_FOR_DATA {RestoreHelper::MAX_INACTIVITY_TIME};
3055+ timer_.start(MAX_TIME_WAITING_FOR_DATA);
3056+ }
3057+
3058+ void stop_inactivity_timer()
3059+ {
3060+ timer_.stop();
3061+ }
3062+
3063+ void check_for_done()
3064+ {
3065+ if (cancelled_)
3066+ {
3067+ q_ptr->set_state(Helper::State::CANCELLED);
3068+ }
3069+ else if (read_error_ || write_error_ || n_uploaded_ > q_ptr->expected_size())
3070+ {
3071+ if (!q_ptr->is_helper_running())
3072+ {
3073+ if (n_uploaded_ > q_ptr->expected_size())
3074+ {
3075+ Q_EMIT(q_ptr->error(keeper::Error::HELPER_WRITE));
3076+ }
3077+ q_ptr->set_state(Helper::State::FAILED);
3078+ }
3079+ }
3080+ else if (n_uploaded_ == q_ptr->expected_size())
3081+ {
3082+ if (downloader_)
3083+ {
3084+ if (q_ptr->is_helper_running())
3085+ {
3086+ // only in the case that the helper process finished we move to the next state
3087+ // this is to prevent to start the next task too early
3088+ q_ptr->set_state(Helper::State::DATA_COMPLETE);
3089+ stop_inactivity_timer();
3090+ }
3091+ }
3092+ else
3093+ q_ptr->set_state(Helper::State::COMPLETE);
3094+ }
3095+ }
3096+
3097+ /***
3098+ ****
3099+ ***/
3100+
3101+ static constexpr int UPLOAD_BUFFER_MAX_ {1024*16};
3102+
3103+ RestoreHelper * const q_ptr;
3104+ QTimer timer_;
3105+ std::shared_ptr<Downloader> downloader_;
3106+ int helper_socket_ = -1;
3107+ QLocalSocket write_socket_;
3108+ QByteArray upload_buffer_;
3109+ qint64 n_read_ = 0;
3110+ qint64 n_uploaded_ = 0;
3111+ bool read_error_ = false;
3112+ bool write_error_ = false;
3113+ bool cancelled_ = false;
3114+ ConnectionHelper connections_;
3115+};
3116+
3117+/***
3118+****
3119+***/
3120+
3121+RestoreHelper::RestoreHelper(
3122+ QString const & appid,
3123+ clock_func const & clock,
3124+ QObject * parent
3125+)
3126+ : Helper(appid, clock, parent)
3127+ , d_ptr(new RestoreHelperPrivate(this))
3128+{
3129+}
3130+
3131+RestoreHelper::~RestoreHelper() =default;
3132+
3133+void
3134+RestoreHelper::start(QStringList const& url)
3135+{
3136+ Q_D(RestoreHelper);
3137+
3138+ d->start(url);
3139+}
3140+
3141+void
3142+RestoreHelper::stop()
3143+{
3144+ Q_D(RestoreHelper);
3145+
3146+ d->stop();
3147+}
3148+
3149+void
3150+RestoreHelper::set_downloader(std::shared_ptr<Downloader> const& downloader)
3151+{
3152+ Q_D(RestoreHelper);
3153+
3154+ d->set_downloader(downloader);
3155+}
3156+
3157+int
3158+RestoreHelper::get_helper_socket() const
3159+{
3160+ Q_D(const RestoreHelper);
3161+
3162+ return d->get_helper_socket();
3163+}
3164+
3165+QString
3166+RestoreHelper::to_string(Helper::State state) const
3167+{
3168+ Q_D(const RestoreHelper);
3169+
3170+ return d->to_string(state);
3171+}
3172+
3173+void
3174+RestoreHelper::set_state(Helper::State state)
3175+{
3176+ Q_D(RestoreHelper);
3177+
3178+ Helper::set_state(state);
3179+ d->on_state_changed(state);
3180+}
3181+
3182+void RestoreHelper::on_helper_finished()
3183+{
3184+ Q_D(RestoreHelper);
3185+
3186+ Helper::on_helper_finished();
3187+ d->on_helper_finished();
3188+}
3189
3190=== modified file 'src/qdbus-stubs/CMakeLists.txt'
3191--- src/qdbus-stubs/CMakeLists.txt 2016-08-10 05:41:26 +0000
3192+++ src/qdbus-stubs/CMakeLists.txt 2017-02-27 19:05:17 +0000
3193@@ -67,6 +67,7 @@
3194 ${user_xml}
3195 PROPERTIES
3196 CLASSNAME DBusInterfaceKeeperUser
3197+ INCLUDE "client/keeper-items.h"
3198 )
3199
3200 qt5_add_dbus_interface(
3201@@ -84,6 +85,23 @@
3202 KeeperUserAdaptor
3203 )
3204
3205+set(
3206+ properties_xml
3207+ "org.freedesktop.DBus.Properties.xml"
3208+)
3209+
3210+set_source_files_properties(
3211+ "${properties_xml}"
3212+ PROPERTIES
3213+ NO_NAMESPACE YES
3214+ CLASSNAME DBusPropertiesInterface
3215+)
3216+
3217+qt5_add_dbus_interface(
3218+ interface_files
3219+ ${properties_xml}
3220+ DBusPropertiesInterface
3221+)
3222 #
3223 #
3224
3225@@ -101,5 +119,5 @@
3226
3227 target_link_libraries(
3228 ${STUBS_LIB}
3229- backup-helper
3230+ keeper-errors-lib
3231 )
3232
3233=== modified file 'src/qdbus-stubs/com.canonical.keeper.User.xml'
3234--- src/qdbus-stubs/com.canonical.keeper.User.xml 2016-07-28 19:44:52 +0000
3235+++ src/qdbus-stubs/com.canonical.keeper.User.xml 2017-02-27 19:05:17 +0000
3236@@ -5,7 +5,7 @@
3237 <interface name="com.canonical.keeper.User">
3238
3239 <method name="GetBackupChoices">
3240- <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantDictMap"/>
3241+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="keeper::Items"/>
3242 <arg direction="out" name="backups" type="a{sa{sv}}">
3243 <doc:doc>
3244 <doc:summary>A list of backup possibilities for the user to choose from</doc:summary>
3245@@ -32,10 +32,32 @@
3246 </doc:description>
3247 </doc:doc>
3248 </arg>
3249+ <arg direction="in" name="storage" type="s">
3250+ <doc:doc>
3251+ <doc:summary>The storage identifier</doc:summary>
3252+ <doc:description>
3253+ <doc:para>Because keeper supports multiple storage providers the user can define
3254+ which is the storage provider to use.
3255+ If the passed storage id is an empty string the default storage provider
3256+ will be used.</doc:para>
3257+ </doc:description>
3258+ </doc:doc>
3259+ </arg>
3260 </method>
3261
3262 <method name="GetRestoreChoices">
3263- <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantDictMap"/>
3264+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="keeper::Items"/>
3265+ <arg direction="in" name="storage" type="s">
3266+ <doc:doc>
3267+ <doc:summary>The storage identifier</doc:summary>
3268+ <doc:description>
3269+ <doc:para>Because keeper supports multiple storage providers the user can define
3270+ which is the storage provider to use.
3271+ If the passed storage id is an empty string the default storage provider
3272+ will be used.</doc:para>
3273+ </doc:description>
3274+ </doc:doc>
3275+ </arg>
3276 <arg direction="out" name="backups" type="a{sa{sv}}">
3277 <doc:doc>
3278 <doc:summary>The backups which already exist and can be restored</doc:summary>
3279@@ -62,10 +84,21 @@
3280 </doc:description>
3281 </doc:doc>
3282 </arg>
3283+ <arg direction="in" name="storage" type="s">
3284+ <doc:doc>
3285+ <doc:summary>The storage identifier</doc:summary>
3286+ <doc:description>
3287+ <doc:para>Because keeper supports multiple storage providers the user can define
3288+ which is the storage provider to use.
3289+ If the passed storage id is an empty string the default storage provider
3290+ will be used.</doc:para>
3291+ </doc:description>
3292+ </doc:doc>
3293+ </arg>
3294 </method>
3295
3296 <property name="State" type="a{sa{sv}}" access="read">
3297- <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantDictMap"/>
3298+ <annotation name="org.qtproject.QtDBus.QtTypeName" value="keeper::Items"/>
3299 <doc:doc>
3300 <doc:description>
3301 <doc:para>Provides state information so the user interface can show
3302@@ -86,6 +119,17 @@
3303 </doc:doc>
3304 </property>
3305
3306+ <method name="GetStorageAccounts">
3307+ <arg direction="out" name="accounts" type="as">
3308+ <doc:doc>
3309+ <doc:summary>The list of available storage accounts.</doc:summary>
3310+ <doc:description>
3311+ <doc:para>An array of the available accounts to choose from to backup/restore</doc:para>
3312+ </doc:description>
3313+ </doc:doc>
3314+ </arg>
3315+ </method>
3316+
3317 <method name="Cancel">
3318 <doc:doc>
3319 <doc:summary>Cancels the current backup or restore actions.</doc:summary>
3320
3321=== modified file 'src/qdbus-stubs/dbus-types.h'
3322--- src/qdbus-stubs/dbus-types.h 2016-07-14 15:31:11 +0000
3323+++ src/qdbus-stubs/dbus-types.h 2017-02-27 19:05:17 +0000
3324@@ -22,7 +22,8 @@
3325 #include <QtCore>
3326 #include <QString>
3327 #include <QVariantMap>
3328-#include <helper/helper.h>
3329+#include <client/keeper-errors.h>
3330+#include "client/keeper-items.h"
3331
3332 typedef QMap<QString, QVariantMap> QVariantDictMap;
3333 Q_DECLARE_METATYPE(QVariantDictMap)
3334@@ -36,10 +37,13 @@
3335 {
3336 qRegisterMetaType<QVariantDictMap>("QVariantDictMap");
3337 qRegisterMetaType<QStringMap>("QStringMap");
3338+ qRegisterMetaType<keeper::Error>("keeper::Error");
3339
3340 qDBusRegisterMetaType<QVariantDictMap>();
3341 qDBusRegisterMetaType<QStringMap>();
3342- Helper::registerMetaTypes();
3343+ qDBusRegisterMetaType<keeper::Error>();
3344+
3345+ keeper::Items::registerMetaType();
3346 }
3347
3348 constexpr const char KEEPER_SERVICE[] = "com.canonical.keeper";
3349
3350=== added file 'src/qdbus-stubs/org.freedesktop.DBus.Properties.xml'
3351--- src/qdbus-stubs/org.freedesktop.DBus.Properties.xml 1970-01-01 00:00:00 +0000
3352+++ src/qdbus-stubs/org.freedesktop.DBus.Properties.xml 2017-02-27 19:05:17 +0000
3353@@ -0,0 +1,27 @@
3354+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
3355+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3356+<node>
3357+ <interface name="org.freedesktop.DBus.Properties">
3358+ <method name="Get">
3359+ <arg type="s" name="interface_name" direction="in"/>
3360+ <arg type="s" name="property_name" direction="in"/>
3361+ <arg type="v" name="value" direction="out"/>
3362+ </method>
3363+ <method name="GetAll">
3364+ <arg type="s" name="interface_name" direction="in"/>
3365+ <arg type="a{sv}" name="properties" direction="out"/>
3366+ <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
3367+ </method>
3368+ <method name="Set">
3369+ <arg type="s" name="interface_name" direction="in"/>
3370+ <arg type="s" name="property_name" direction="in"/>
3371+ <arg type="v" name="value" direction="in"/>
3372+ </method>
3373+ <signal name="PropertiesChanged">
3374+ <arg type="s" name="interface_name"/>
3375+ <arg type="a{sv}" name="changed_properties"/>
3376+ <annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
3377+ <arg type="as" name="invalidated_properties"/>
3378+ </signal>
3379+ </interface>
3380+</node>
3381
3382=== modified file 'src/service/CMakeLists.txt'
3383--- src/service/CMakeLists.txt 2016-09-06 01:31:59 +0000
3384+++ src/service/CMakeLists.txt 2017-02-27 19:05:17 +0000
3385@@ -15,6 +15,9 @@
3386 task-manager.cpp
3387 keeper-task.cpp
3388 keeper-task-backup.cpp
3389+ keeper-task-restore.cpp
3390+ manifest.cpp
3391+ metadata-provider.h
3392 )
3393 add_library(
3394 ${SERVICE_LIB}
3395@@ -46,6 +49,7 @@
3396 storage-framework
3397 util
3398 qdbus-stubs
3399+ keeper-errors-lib
3400 )
3401
3402 target_link_libraries(
3403
3404=== modified file 'src/service/backup-choices.cpp'
3405--- src/service/backup-choices.cpp 2016-09-05 18:38:36 +0000
3406+++ src/service/backup-choices.cpp 2017-02-27 19:05:17 +0000
3407@@ -45,22 +45,30 @@
3408 }
3409 }
3410
3411-BackupChoices::BackupChoices() =default;
3412+BackupChoices::BackupChoices(QObject *parent)
3413+ : MetadataProvider(parent)
3414+{
3415+}
3416
3417 BackupChoices::~BackupChoices() =default;
3418
3419 QVector<Metadata>
3420 BackupChoices::get_backups() const
3421 {
3422- QVector<Metadata> ret;
3423+ return backups_;
3424+}
3425
3426+void
3427+BackupChoices::get_backups_async(QString const & /*storage*/)
3428+{
3429+ backups_.clear();
3430 //
3431 // System Data
3432 //
3433 {
3434 Metadata m(generate_new_uuid(), "System Data"); // FIXME: how to i18n in a Qt DBus service?
3435- m.set_property(Metadata::TYPE_KEY, Metadata::SYSTEM_DATA_VALUE);
3436- ret.push_back(m);
3437+ m.set_property_value(Metadata::TYPE_KEY, Metadata::SYSTEM_DATA_VALUE);
3438+ backups_.push_back(m);
3439 }
3440
3441 //
3442@@ -112,13 +120,13 @@
3443 display_name = QStringLiteral("%1 (%2)").arg(display_name).arg(version.toString());
3444
3445 Metadata m(generate_new_uuid(), display_name);
3446- m.set_property(Metadata::PACKAGE_KEY, name.toString());
3447- m.set_property(Metadata::TYPE_KEY, Metadata::APPLICATION_VALUE);
3448+ m.set_property_value(Metadata::PACKAGE_KEY, name.toString());
3449+ m.set_property_value(Metadata::TYPE_KEY, Metadata::APPLICATION_VALUE);
3450
3451 if (version != QJsonValue::Undefined)
3452- m.set_property(Metadata::VERSION_KEY, version.toString());
3453+ m.set_property_value(Metadata::VERSION_KEY, version.toString());
3454
3455- ret.push_back(m);
3456+ backups_.push_back(m);
3457 }
3458 }
3459 }
3460@@ -146,11 +154,11 @@
3461 {
3462 const auto keystr = generate_new_uuid();
3463 Metadata m(keystr, name);
3464- m.set_property(Metadata::TYPE_KEY, Metadata::FOLDER_VALUE);
3465- m.set_property(Metadata::SUBTYPE_KEY, locations.front());
3466- ret.push_back(m);
3467+ m.set_property_value(Metadata::TYPE_KEY, Metadata::FOLDER_VALUE);
3468+ m.set_property_value(Metadata::SUBTYPE_KEY, locations.front());
3469+ backups_.push_back(m);
3470 }
3471 }
3472
3473- return ret;
3474+ Q_EMIT(finished(keeper::Error::OK));
3475 }
3476
3477=== modified file 'src/service/backup-choices.h'
3478--- src/service/backup-choices.h 2016-09-05 18:38:36 +0000
3479+++ src/service/backup-choices.h 2017-02-27 19:05:17 +0000
3480@@ -27,7 +27,8 @@
3481 class BackupChoices: public MetadataProvider
3482 {
3483 public:
3484- BackupChoices();
3485+ explicit BackupChoices(QObject *parent = nullptr);
3486 virtual ~BackupChoices();
3487 QVector<Metadata> get_backups() const override;
3488+ void get_backups_async(QString const & storage = "") override;
3489 };
3490
3491=== modified file 'src/service/keeper-helper.cpp'
3492--- src/service/keeper-helper.cpp 2016-08-10 02:06:08 +0000
3493+++ src/service/keeper-helper.cpp 2017-02-27 19:05:17 +0000
3494@@ -44,8 +44,11 @@
3495
3496 QDBusUnixFileDescriptor KeeperHelper::StartRestore()
3497 {
3498- // TODO get the file descriptor of the item in storage framework
3499- return QDBusUnixFileDescriptor();
3500+ // pass it back to Keeper to do the work
3501+ Q_ASSERT(calledFromDBus());
3502+ auto bus = connection();
3503+ auto& msg = message();
3504+ return keeper_.StartRestore(bus, msg);
3505 }
3506
3507 void KeeperHelper::UpdateStatus(const QString &app_id, const QString &status, double percentage)
3508
3509=== modified file 'src/service/keeper-task-backup.cpp'
3510--- src/service/keeper-task-backup.cpp 2016-09-06 20:08:14 +0000
3511+++ src/service/keeper-task-backup.cpp 2017-02-27 19:05:17 +0000
3512@@ -31,7 +31,7 @@
3513 Q_DECLARE_PUBLIC(KeeperTaskBackup)
3514 public:
3515 KeeperTaskBackupPrivate(KeeperTask * keeper_task,
3516- KeeperTask::TaskData const & task_data,
3517+ KeeperTask::TaskData & task_data,
3518 QSharedPointer<HelperRegistry> const & helper_registry,
3519 QSharedPointer<StorageFrameworkClient> const & storage)
3520 : KeeperTaskPrivate(keeper_task, task_data, helper_registry, storage)
3521@@ -50,33 +50,52 @@
3522 qDebug() << "Initializing a backup helper";
3523 helper_.reset(new BackupHelper(DEKKO_APP_ID), [](Helper *h){h->deleteLater();});
3524 qDebug() << "Helper " << static_cast<void*>(helper_.data()) << " was created";
3525+ QObject::connect(helper_.data(), &Helper::error, [this](keeper::Error error){ error_ = error;});
3526 }
3527
3528- void ask_for_uploader(quint64 n_bytes)
3529+ void ask_for_uploader(quint64 n_bytes, QString const & dir_name)
3530 {
3531 qDebug() << "asking storage framework for a socket";
3532
3533 helper_->set_expected_size(n_bytes);
3534
3535+ const auto file_name = QString("%1.keeper").arg(task_data_.metadata.get_display_name());
3536+
3537 connections_.connect_future(
3538- storage_->get_new_uploader(n_bytes),
3539+ storage_->get_new_uploader(n_bytes, dir_name, file_name),
3540 std::function<void(std::shared_ptr<Uploader> const&)>{
3541 [this](std::shared_ptr<Uploader> const& uploader){
3542- qDebug("calling helper.set_storage_framework_socket(socket=%d)", int(uploader->socket()->socketDescriptor()));
3543- qDebug() << "Helper is " << static_cast<void*>(helper_.data());
3544- auto backup_helper = qSharedPointerDynamicCast<BackupHelper>(helper_);
3545- backup_helper->set_uploader(uploader);
3546- Q_EMIT(q_ptr->task_socket_ready(backup_helper->get_helper_socket()));
3547+ auto fd {-1};
3548+ if (uploader) {
3549+ auto backup_helper = qSharedPointerDynamicCast<BackupHelper>(helper_);
3550+ backup_helper->set_uploader(uploader);
3551+ fd = backup_helper->get_helper_socket();
3552+ qDebug("emitting task_socket_ready(socket=%d)", fd);
3553+ Q_EMIT(q_ptr->task_socket_ready(fd));
3554+ }
3555+ else
3556+ {
3557+ error_ = storage_->get_last_error();
3558+ qDebug("Emitting task_socket_error(error=%d)", static_cast<int>(error_));
3559+ Q_EMIT(q_ptr->task_socket_error(error_));
3560+ }
3561 }
3562 }
3563 );
3564 }
3565
3566+ QString get_file_name() const
3567+ {
3568+ auto backup_helper = qSharedPointerDynamicCast<BackupHelper>(helper_);
3569+ return backup_helper->get_uploader_committed_file_name();
3570+ }
3571+
3572 private:
3573 ConnectionHelper connections_;
3574+ QString file_name_;
3575 };
3576
3577-KeeperTaskBackup::KeeperTaskBackup(TaskData const & task_data,
3578+KeeperTaskBackup::KeeperTaskBackup(TaskData & task_data,
3579 QSharedPointer<HelperRegistry> const & helper_registry,
3580 QSharedPointer<StorageFrameworkClient> const & storage,
3581 QObject *parent)
3582@@ -89,17 +108,27 @@
3583 QStringList KeeperTaskBackup::get_helper_urls() const
3584 {
3585 Q_D(const KeeperTaskBackup);
3586+
3587 return d->get_helper_urls();
3588 }
3589
3590 void KeeperTaskBackup::init_helper()
3591 {
3592 Q_D(KeeperTaskBackup);
3593+
3594 d->init_helper();
3595 }
3596
3597-void KeeperTaskBackup::ask_for_uploader(quint64 n_bytes)
3598+void KeeperTaskBackup::ask_for_uploader(quint64 n_bytes, QString const & dir_name)
3599 {
3600 Q_D(KeeperTaskBackup);
3601- d->ask_for_uploader(n_bytes);
3602+
3603+ d->ask_for_uploader(n_bytes, dir_name);
3604+}
3605+
3606+QString KeeperTaskBackup::get_file_name() const
3607+{
3608+ Q_D(const KeeperTaskBackup);
3609+
3610+ return d->get_file_name();
3611 }
3612
3613=== modified file 'src/service/keeper-task-backup.h'
3614--- src/service/keeper-task-backup.h 2016-09-06 02:13:34 +0000
3615+++ src/service/keeper-task-backup.h 2017-02-27 19:05:17 +0000
3616@@ -29,7 +29,7 @@
3617 Q_DECLARE_PRIVATE(KeeperTaskBackup)
3618 public:
3619
3620- KeeperTaskBackup(TaskData const & task_data,
3621+ KeeperTaskBackup(TaskData & task_data,
3622 QSharedPointer<HelperRegistry> const & helper_registry,
3623 QSharedPointer<StorageFrameworkClient> const & storage,
3624 QObject *parent = nullptr);
3625@@ -37,7 +37,9 @@
3626
3627 Q_DISABLE_COPY(KeeperTaskBackup)
3628
3629- void ask_for_uploader(quint64 n_bytes);
3630+ void ask_for_uploader(quint64 n_bytes, QString const & dir_name);
3631+
3632+ QString get_file_name() const;
3633
3634 protected:
3635 QStringList get_helper_urls() const override;
3636
3637=== added file 'src/service/keeper-task-restore.cpp'
3638--- src/service/keeper-task-restore.cpp 1970-01-01 00:00:00 +0000
3639+++ src/service/keeper-task-restore.cpp 2017-02-27 19:05:17 +0000
3640@@ -0,0 +1,130 @@
3641+/*
3642+ * Copyright (C) 2016 Canonical, Ltd.
3643+ *
3644+ * This program is free software: you can redistribute it and/or modify it
3645+ * under the terms of the GNU General Public License version 3, as published
3646+ * by the Free Software Foundation.
3647+ *
3648+ * This program is distributed in the hope that it will be useful, but
3649+ * WITHOUT ANY WARRANTY; without even the implied warranties of
3650+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3651+ * PURPOSE. See the GNU General Public License for more details.
3652+ *
3653+ * You should have received a copy of the GNU General Public License along
3654+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3655+ *
3656+ * Authors:
3657+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
3658+ * Charles Kerr <charles.kerr@canonical.com>
3659+ */
3660+
3661+#include "util/connection-helper.h"
3662+#include "storage-framework/storage_framework_client.h"
3663+#include "helper/restore-helper.h"
3664+#include "service/app-const.h" // DEKKO_APP_ID
3665+#include "service/keeper-task-restore.h"
3666+#include "service/keeper-task.h"
3667+#include "service/private/keeper-task_p.h"
3668+
3669+namespace sf = unity::storage::qt::client;
3670+
3671+class KeeperTaskRestorePrivate : public KeeperTaskPrivate
3672+{
3673+ Q_DECLARE_PUBLIC(KeeperTaskRestore)
3674+public:
3675+ KeeperTaskRestorePrivate(KeeperTask * keeper_task,
3676+ KeeperTask::TaskData & task_data,
3677+ QSharedPointer<HelperRegistry> const & helper_registry,
3678+ QSharedPointer<StorageFrameworkClient> const & storage)
3679+ : KeeperTaskPrivate(keeper_task, task_data, helper_registry, storage)
3680+ {
3681+ }
3682+
3683+ ~KeeperTaskRestorePrivate() = default;
3684+
3685+ QStringList get_helper_urls() const
3686+ {
3687+ return helper_registry_->get_restore_helper_urls(task_data_.metadata);
3688+ }
3689+
3690+ void init_helper()
3691+ {
3692+ helper_.reset(new RestoreHelper(DEKKO_APP_ID), [](Helper *h){h->deleteLater();});
3693+ qDebug() << "Helper " << static_cast<void*>(helper_.data()) << " was created";
3694+ }
3695+
3696+ void ask_for_downloader()
3697+ {
3698+ qDebug() << "asking storage framework for a socket for reading";
3699+
3700+ auto file_name = task_data_.metadata.get_file_name();
3701+ if (file_name.isEmpty())
3702+ {
3703+ qWarning() << "ERROR: the restore task does not provide a valid file name to read from.";
3704+ return;
3705+ }
3706+
3707+ auto dir_name = task_data_.metadata.get_dir_name();
3708+ if (dir_name.isEmpty())
3709+ {
3710+ qWarning() << "ERROR: the restore task does not provide a valid directory name.";
3711+ return;
3712+ }
3713+
3714+ // extract the dir_name.
3715+ connections_.connect_future(
3716+ storage_->get_new_downloader(dir_name, file_name),
3717+ std::function<void(std::shared_ptr<Downloader> const&)>{
3718+ [this](std::shared_ptr<Downloader> const& downloader){
3719+ auto fd {-1};
3720+ if (downloader) {
3721+ auto restore_helper = qSharedPointerDynamicCast<RestoreHelper>(helper_);
3722+ restore_helper->set_downloader(downloader);
3723+ fd = restore_helper->get_helper_socket();
3724+ Q_EMIT(q_ptr->task_socket_ready(fd));
3725+ }
3726+ else
3727+ {
3728+ error_ = storage_->get_last_error();
3729+ qDebug("Emitting task_socket_error(error=%d)", static_cast<int>(error_));
3730+ Q_EMIT(q_ptr->task_socket_error(error_));
3731+ }
3732+ }
3733+ }
3734+ );
3735+ }
3736+
3737+private:
3738+ ConnectionHelper connections_;
3739+};
3740+
3741+KeeperTaskRestore::KeeperTaskRestore(TaskData & task_data,
3742+ QSharedPointer<HelperRegistry> const & helper_registry,
3743+ QSharedPointer<StorageFrameworkClient> const & storage,
3744+ QObject *parent)
3745+ : KeeperTask(*new KeeperTaskRestorePrivate(this, task_data, helper_registry, storage), parent)
3746+{
3747+}
3748+
3749+KeeperTaskRestore::~KeeperTaskRestore() = default;
3750+
3751+QStringList KeeperTaskRestore::get_helper_urls() const
3752+{
3753+ Q_D(const KeeperTaskRestore);
3754+
3755+ return d->get_helper_urls();
3756+}
3757+
3758+void KeeperTaskRestore::init_helper()
3759+{
3760+ Q_D(KeeperTaskRestore);
3761+
3762+ d->init_helper();
3763+}
3764+
3765+void KeeperTaskRestore::ask_for_downloader()
3766+{
3767+ Q_D(KeeperTaskRestore);
3768+
3769+ d->ask_for_downloader();
3770+}
3771
3772=== added file 'src/service/keeper-task-restore.h'
3773--- src/service/keeper-task-restore.h 1970-01-01 00:00:00 +0000
3774+++ src/service/keeper-task-restore.h 2017-02-27 19:05:17 +0000
3775@@ -0,0 +1,46 @@
3776+/*
3777+ * Copyright (C) 2016 Canonical, Ltd.
3778+ *
3779+ * This program is free software: you can redistribute it and/or modify it
3780+ * under the terms of the GNU General Public License version 3, as published
3781+ * by the Free Software Foundation.
3782+ *
3783+ * This program is distributed in the hope that it will be useful, but
3784+ * WITHOUT ANY WARRANTY; without even the implied warranties of
3785+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
3786+ * PURPOSE. See the GNU General Public License for more details.
3787+ *
3788+ * You should have received a copy of the GNU General Public License along
3789+ * with this program. If not, see <http://www.gnu.org/licenses/>.
3790+ *
3791+ * Authors:
3792+ * Xavi Garcia <xavi.garcia.mena@canonical.com>
3793+ * Charles Kerr <charles.kerr@canonical.com>
3794+ */
3795+#pragma once
3796+
3797+#include "keeper-task.h"
3798+
3799+class KeeperTaskRestorePrivate;
3800+
3801+class KeeperTaskRestore : public KeeperTask
3802+{
3803+ Q_OBJECT
3804+ Q_DECLARE_PRIVATE(KeeperTaskRestore)
3805+public:
3806+
3807+ KeeperTaskRestore(TaskData & task_data,
3808+ QSharedPointer<HelperRegistry> const & helper_registry,
3809+ QSharedPointer<StorageFrameworkClient> const & storage,
3810+ QObject *parent = nullptr);
3811+ virtual ~KeeperTaskRestore();
3812+
3813+ Q_DISABLE_COPY(KeeperTaskRestore)
3814+
3815+ void ask_for_downloader();
3816+
3817+protected:
3818+ QStringList get_helper_urls() const override;
3819+ void init_helper() override;
3820+
3821+};
3822
3823=== modified file 'src/service/keeper-task.cpp'
3824--- src/service/keeper-task.cpp 2016-09-13 08:47:14 +0000
3825+++ src/service/keeper-task.cpp 2017-02-27 19:05:17 +0000
3826@@ -28,13 +28,14 @@
3827 #include <QString>
3828
3829 KeeperTaskPrivate::KeeperTaskPrivate(KeeperTask * keeper_task,
3830- KeeperTask::TaskData const & task_data,
3831+ KeeperTask::TaskData & task_data,
3832 QSharedPointer<HelperRegistry> const & helper_registry,
3833 QSharedPointer<StorageFrameworkClient> const & storage)
3834 : q_ptr(keeper_task)
3835 , task_data_(task_data)
3836 , helper_registry_(helper_registry)
3837 , storage_(storage)
3838+ , error_(keeper::Error::OK)
3839 {
3840 }
3841
3842@@ -49,8 +50,8 @@
3843 if (urls.isEmpty())
3844 {
3845 task_data_.action = helper_->to_string(Helper::State::FAILED);
3846- task_data_.error = "no helper information in registry";
3847- qWarning() << "ERROR: uuid: " << task_data_.metadata.uuid() << " has no url";
3848+ error_ = keeper::Error::HELPER_BAD_URL;
3849+ qWarning() << QStringLiteral("Error: uuid %1 has no url").arg(task_data_.metadata.get_uuid());
3850 calculate_and_notify_state(Helper::State::FAILED);
3851 return false;
3852 }
3853@@ -65,6 +66,10 @@
3854 std::bind(&KeeperTaskPrivate::on_helper_percent_done_changed, this, std::placeholders::_1)
3855 );
3856
3857+ QObject::connect(helper_.data(), &Helper::error, [this](keeper::Error error){
3858+ error_ = error;
3859+ });
3860+
3861 helper_->start(urls);
3862 return true;
3863 }
3864@@ -120,22 +125,28 @@
3865 {
3866 QVariantMap ret;
3867
3868- auto const uuid = task_data_.metadata.uuid();
3869-
3870- ret.insert(QStringLiteral("action"), task_data_.action);
3871-
3872- ret.insert(QStringLiteral("display-name"), task_data_.metadata.display_name());
3873+ auto const uuid = task_data_.metadata.get_uuid();
3874+
3875+ ret.insert(keeper::Item::STATUS_KEY, task_data_.action);
3876+ ret.insert(keeper::Item::DISPLAY_NAME_KEY, task_data_.metadata.get_display_name());
3877
3878 auto const speed = helper_->speed();
3879- ret.insert(QStringLiteral("speed"), int32_t(speed));
3880+ ret.insert(keeper::Item::SPEED_KEY, int32_t(speed));
3881
3882 auto const percent_done = helper_->percent_done();
3883- ret.insert(QStringLiteral("percent-done"), double(percent_done));
3884-
3885- if (task_data_.action == "failed")
3886- ret.insert(QStringLiteral("error"), task_data_.error);
3887-
3888- ret.insert(QStringLiteral("uuid"), uuid);
3889+ ret.insert(keeper::Item::PERCENT_DONE_KEY, double(percent_done));
3890+
3891+ if (task_data_.action == "failed" || task_data_.action == "cancelled")
3892+ {
3893+ auto error = error_;
3894+ if (task_data_.error != keeper::Error::OK)
3895+ {
3896+ error = task_data_.error;
3897+ }
3898+ ret.insert(keeper::Item::ERROR_KEY, QVariant::fromValue(error));
3899+ }
3900+
3901+ ret.insert(keeper::Item::UUID_KEY, uuid);
3902
3903 QJsonDocument doc(QJsonObject::fromVariantMap(ret));
3904 qDebug() << QString(doc.toJson(QJsonDocument::Compact));
3905@@ -145,29 +156,60 @@
3906
3907 void KeeperTaskPrivate::calculate_and_notify_state(Helper::State state)
3908 {
3909+ recalculate_task_state();
3910+ Q_EMIT(q_ptr->task_state_changed(state));
3911+}
3912+
3913+void KeeperTaskPrivate::recalculate_task_state()
3914+{
3915 state_ = calculate_task_state();
3916- Q_EMIT(q_ptr->task_state_changed(state));
3917+}
3918+
3919+void KeeperTaskPrivate::cancel()
3920+{
3921+ if (helper_)
3922+ {
3923+ helper_->stop();
3924+ }
3925 }
3926
3927 QVariantMap KeeperTaskPrivate::get_initial_state(KeeperTask::TaskData const &td)
3928 {
3929 QVariantMap ret;
3930
3931- auto const uuid = td.metadata.uuid();
3932+ auto const uuid = td.metadata.get_uuid();
3933
3934- ret.insert(QStringLiteral("action"), td.action);
3935+ ret.insert(keeper::Item::STATUS_KEY, td.action);
3936
3937 // TODO review this when we add the restore tasks.
3938 // TODO we maybe have different fields
3939- ret.insert(QStringLiteral("display-name"), td.metadata.display_name());
3940- ret.insert(QStringLiteral("speed"), 0);
3941- ret.insert(QStringLiteral("percent-done"), double(0.0));
3942- ret.insert(QStringLiteral("uuid"), uuid);
3943+ ret.insert(keeper::Item::DISPLAY_NAME_KEY, td.metadata.get_display_name());
3944+ ret.insert(keeper::Item::SPEED_KEY, 0);
3945+ ret.insert(keeper::Item::PERCENT_DONE_KEY, double(0.0));
3946+ ret.insert(keeper::Item::UUID_KEY, uuid);
3947
3948 return ret;
3949 }
3950
3951-KeeperTask::KeeperTask(TaskData const & task_data,
3952+QString KeeperTaskPrivate::to_string(Helper::State state)
3953+{
3954+ if (helper_)
3955+ {
3956+ return helper_->to_string(state);
3957+ }
3958+ else
3959+ {
3960+ qWarning() << "Asking for the string of a state when the helper is not initialized yet";
3961+ return "bug";
3962+ }
3963+}
3964+
3965+keeper::Error KeeperTaskPrivate::error() const
3966+{
3967+ return error_;
3968+}
3969+
3970+KeeperTask::KeeperTask(TaskData & task_data,
3971 QSharedPointer<HelperRegistry> const & helper_registry,
3972 QSharedPointer<StorageFrameworkClient> const & storage,
3973 QObject *parent)
3974@@ -188,16 +230,47 @@
3975 bool KeeperTask::start()
3976 {
3977 Q_D(KeeperTask);
3978+
3979 return d->start();
3980 }
3981
3982 QVariantMap KeeperTask::state() const
3983 {
3984 Q_D(const KeeperTask);
3985+
3986 return d->state();
3987 }
3988
3989+void KeeperTask::recalculate_task_state()
3990+{
3991+ Q_D(KeeperTask);
3992+
3993+ return d->recalculate_task_state();
3994+}
3995+
3996+
3997 QVariantMap KeeperTask::get_initial_state(KeeperTask::TaskData const &td)
3998 {
3999 return KeeperTaskPrivate::get_initial_state(td);
4000 }
4001+
4002+void KeeperTask::cancel()
4003+{
4004+ Q_D(KeeperTask);
4005+
4006+ return d->cancel();
4007+}
4008+
4009+QString KeeperTask::to_string(Helper::State state)
4010+{
4011+ Q_D(KeeperTask);
4012+
4013+ return d->to_string(state);
4014+}
4015+
4016+keeper::Error KeeperTask::error() const
4017+{
4018+ Q_D(const KeeperTask);
4019+
4020+ return d->error();
4021+}
4022
4023=== modified file 'src/service/keeper-task.h'
4024--- src/service/keeper-task.h 2016-09-07 17:07:23 +0000
4025+++ src/service/keeper-task.h 2017-02-27 19:05:17 +0000
4026@@ -20,6 +20,7 @@
4027
4028 #pragma once
4029
4030+#include "client/keeper-errors.h"
4031 #include "helper/metadata.h"
4032 #include "helper/backup-helper.h"
4033 #include "helper/helper.h"
4034@@ -40,11 +41,11 @@
4035 struct TaskData
4036 {
4037 QString action;
4038- QString error;
4039+ keeper::Error error;
4040 Metadata metadata;
4041 };
4042
4043- KeeperTask(TaskData const & task_data,
4044+ KeeperTask(TaskData & task_data,
4045 QSharedPointer<HelperRegistry> const & helper_registry,
4046 QSharedPointer<StorageFrameworkClient> const & storage,
4047 QObject *parent = nullptr);
4048@@ -54,12 +55,19 @@
4049
4050 bool start();
4051 QVariantMap state() const;
4052+ void recalculate_task_state();
4053
4054 static QVariantMap get_initial_state(KeeperTask::TaskData const &td);
4055
4056+ void cancel();
4057+
4058+ QString to_string(Helper::State state);
4059+
4060+ keeper::Error error() const;
4061 Q_SIGNALS:
4062 void task_state_changed(Helper::State state);
4063 void task_socket_ready(int socket_descriptor);
4064+ void task_socket_error(keeper::Error error);
4065
4066 protected:
4067 KeeperTask(KeeperTaskPrivate & d, QObject *parent = nullptr);
4068
4069=== modified file 'src/service/keeper-user.cpp'
4070--- src/service/keeper-user.cpp 2016-09-07 16:35:26 +0000
4071+++ src/service/keeper-user.cpp 2017-02-27 19:05:17 +0000
4072@@ -33,75 +33,67 @@
4073
4074 KeeperUser::~KeeperUser() =default;
4075
4076-namespace
4077-{
4078- QVariantMap strings_to_variants(const QMap<QString,QString>& strings)
4079- {
4080- QVariantMap variants;
4081-
4082- for (auto it=strings.begin(), end=strings.end(); it!=end; ++it)
4083- variants.insert(it.key(), QVariant::fromValue(it.value()));
4084-
4085- return variants;
4086- }
4087-
4088- QVariantDictMap choices_to_variant_dict_map(const QVector<Metadata>& choices)
4089- {
4090- QVariantDictMap ret;
4091-
4092- for (auto const& metadata : choices)
4093- ret.insert(metadata.uuid(), strings_to_variants(metadata.get_public_properties()));
4094-
4095- return ret;
4096- }
4097-}
4098-
4099-QVariantDictMap
4100+keeper::Items
4101 KeeperUser::GetBackupChoices()
4102 {
4103- return choices_to_variant_dict_map(keeper_.get_backup_choices());
4104+ auto bus = connection();
4105+ auto& msg = message();
4106+ return keeper_.get_backup_choices_var_dict_map(bus, msg);
4107 }
4108
4109 void
4110-KeeperUser::StartBackup (const QStringList& keys)
4111+KeeperUser::StartBackup (const QStringList& keys, QString const & storage)
4112 {
4113 Q_ASSERT(calledFromDBus());
4114
4115- auto const unhandled = keeper_.start_tasks(keys);
4116-
4117- if (!unhandled.empty())
4118- {
4119- QString text = QStringLiteral("unhandled uuids:");
4120- for (auto const& uuid : unhandled)
4121- text += ' ' + uuid;
4122- connection().send(message().createErrorReply(QDBusError::InvalidArgs, text));
4123- }
4124+ auto bus = connection();
4125+ auto& msg = message();
4126+ keeper_.start_tasks(keys, storage, bus, msg);
4127 }
4128
4129 void
4130 KeeperUser::Cancel()
4131 {
4132- // FIXME: writeme
4133-
4134- qDebug() << "hello world";
4135+ keeper_.cancel();
4136 }
4137
4138-QVariantDictMap
4139-KeeperUser::GetRestoreChoices()
4140+keeper::Items
4141+KeeperUser::GetRestoreChoices(QString const & storage)
4142 {
4143- return choices_to_variant_dict_map(keeper_.get_restore_choices());
4144+ Q_ASSERT(calledFromDBus());
4145+
4146+ auto bus = connection();
4147+ auto& msg = message();
4148+ return keeper_.get_restore_choices(storage, bus, msg);
4149 }
4150
4151 void
4152-KeeperUser::StartRestore (const QStringList& keys)
4153+KeeperUser::StartRestore (const QStringList& keys, QString const & storage)
4154 {
4155- // FIXME: writeme
4156+ Q_ASSERT(calledFromDBus());
4157
4158- qDebug() << keys;
4159+ auto bus = connection();
4160+ auto& msg = message();
4161+ // if we start a restore right after a backup the uuid
4162+ // will be found as a backup uuid.
4163+ // Just clear the backup cache to avoid that.
4164+ keeper_.invalidate_choices_cache();
4165+ keeper_.start_tasks(keys, storage, bus, msg);
4166 }
4167
4168-QVariantDictMap
4169+keeper::Items
4170 KeeperUser::get_state() const
4171 {
4172 return keeper_.get_state();
4173 }
4174+
4175+QStringList
4176+KeeperUser::GetStorageAccounts()
4177+{
4178+ Q_ASSERT(calledFromDBus());
4179+
4180+ auto bus = connection();
4181+ auto& msg = message();
4182+
4183+ return keeper_.get_storage_accounts(bus, msg);
4184+}
4185
4186=== modified file 'src/service/keeper-user.h'
4187--- src/service/keeper-user.h 2016-09-05 18:38:36 +0000
4188+++ src/service/keeper-user.h 2017-02-27 19:05:17 +0000
4189@@ -37,11 +37,11 @@
4190 virtual ~KeeperUser();
4191 Q_DISABLE_COPY(KeeperUser)
4192
4193- Q_PROPERTY(QVariantDictMap State
4194+ Q_PROPERTY(keeper::Items State
4195 READ get_state
4196 NOTIFY state_changed)
4197
4198- QVariantDictMap get_state() const;
4199+ keeper::Items get_state() const;
4200
4201 Q_SIGNALS:
4202
4203@@ -49,14 +49,16 @@
4204
4205 public Q_SLOTS:
4206
4207- QVariantDictMap GetBackupChoices();
4208- void StartBackup(const QStringList&);
4209+ keeper::Items GetBackupChoices();
4210+ void StartBackup(const QStringList&, QString const & storage);
4211
4212- QVariantDictMap GetRestoreChoices();
4213- void StartRestore(const QStringList&);
4214+ keeper::Items GetRestoreChoices(QString const & storage);
4215+ void StartRestore(const QStringList&, QString const & storage);
4216
4217 void Cancel();
4218
4219+ QStringList GetStorageAccounts();
4220+
4221 private:
4222
4223 Keeper& keeper_;
4224
4225=== modified file 'src/service/keeper.cpp'
4226--- src/service/keeper.cpp 2016-09-07 16:35:26 +0000
4227+++ src/service/keeper.cpp 2017-02-27 19:05:17 +0000
4228@@ -32,78 +32,232 @@
4229 #include <QVector>
4230
4231 #include <algorithm> // std::find_if
4232-
4233-class KeeperPrivate
4234-{
4235+#include <unistd.h>
4236+
4237+namespace
4238+{
4239+// QVariantMap strings_to_variants(const QMap<QString,QString>& strings)
4240+// {
4241+// QVariantMap variants;
4242+//
4243+// for (auto it=strings.begin(), end=strings.end(); it!=end; ++it)
4244+// variants.insert(it.key(), QVariant::fromValue(it.value()));
4245+//
4246+// return variants;
4247+// }
4248+
4249+ keeper::Items choices_to_variant_dict_map(QVector<Metadata> const & choices)
4250+ {
4251+ keeper::Items ret;
4252+
4253+ for (auto const& metadata : choices)
4254+ {
4255+ keeper::Item value(metadata);
4256+ ret.insert(metadata.get_uuid(), value);
4257+ }
4258+
4259+ return ret;
4260+ }
4261+}
4262+
4263+class KeeperPrivate : public QObject
4264+{
4265+ Q_OBJECT
4266 public:
4267
4268 KeeperPrivate(Keeper* keeper,
4269 const QSharedPointer<HelperRegistry>& helper_registry,
4270 const QSharedPointer<MetadataProvider>& backup_choices,
4271- const QSharedPointer<MetadataProvider>& restore_choices)
4272- : q_ptr(keeper)
4273+ const QSharedPointer<MetadataProvider>& restore_choices,
4274+ QObject *parent = nullptr)
4275+ : QObject(parent)
4276+ , q_ptr(keeper)
4277 , storage_(new StorageFrameworkClient())
4278 , helper_registry_(helper_registry)
4279 , backup_choices_(backup_choices)
4280 , restore_choices_(restore_choices)
4281 , task_manager_{helper_registry, storage_}
4282 {
4283+ QObject::connect(&task_manager_, &TaskManager::finished,
4284+ std::bind(&KeeperPrivate::on_task_manager_finished, this)
4285+ );
4286 }
4287
4288+ enum class ChoicesType { BACKUP_CHOICES, RESTORES_CHOICES };
4289+
4290 ~KeeperPrivate() =default;
4291
4292 Q_DISABLE_COPY(KeeperPrivate)
4293
4294- QStringList start_tasks(QStringList const & uuids)
4295+ void start_tasks(QStringList const & uuids,
4296+ QString const & storage,
4297+ QDBusConnection bus,
4298+ QDBusMessage const & msg)
4299 {
4300- auto unhandled = QSet<QString>::fromList(uuids);
4301-
4302 auto get_tasks = [](const QVector<Metadata>& pool, QStringList const& keys){
4303 QMap<QString,Metadata> tasks;
4304 for (auto const& key : keys) {
4305- auto it = std::find_if(pool.begin(), pool.end(), [key](Metadata const & m){return m.uuid()==key;});
4306+ auto it = std::find_if(pool.begin(), pool.end(), [key](Metadata const & m){return m.get_uuid()==key;});
4307 if (it != pool.end())
4308 tasks[key] = *it;
4309 }
4310 return tasks;
4311 };
4312
4313- auto tasks = get_tasks(get_backup_choices(), uuids);
4314- if (!tasks.empty())
4315- {
4316- if (task_manager_.start_backup(tasks.values()))
4317- unhandled.subtract(QSet<QString>::fromList(tasks.keys()));
4318+ // async part
4319+ qDebug() << "Looking for backup options....";
4320+ connections_.connect_oneshot(
4321+ this,
4322+ &KeeperPrivate::backup_choices_ready,
4323+ std::function<void()>{[this, uuids, msg, bus, get_tasks, storage](){
4324+ auto tasks = get_tasks(cached_backup_choices_, uuids);
4325+ if (!tasks.empty())
4326+ {
4327+ auto unhandled = QSet<QString>::fromList(uuids);
4328+ if (task_manager_.start_backup(tasks.values(), storage))
4329+ unhandled.subtract(QSet<QString>::fromList(tasks.keys()));
4330+
4331+ check_for_unhandled_tasks_and_reply(unhandled, bus, msg);
4332+ }
4333+ else // restore
4334+ {
4335+ qDebug() << "Looking for restore options....";
4336+ connections_.connect_oneshot(
4337+ this,
4338+ &KeeperPrivate::restore_choices_ready,
4339+ std::function<void(keeper::Error)>{[this, uuids, msg, bus, get_tasks, storage](keeper::Error error){
4340+ qDebug() << "Choices ready";
4341+ auto unhandled = QSet<QString>::fromList(uuids);
4342+ if (error == keeper::Error::OK)
4343+ {
4344+ auto restore_tasks = get_tasks(cached_restore_choices_, uuids);
4345+ qDebug() << "After getting tasks...";
4346+ if (!restore_tasks.empty() && task_manager_.start_restore(restore_tasks.values(), storage))
4347+ unhandled.subtract(QSet<QString>::fromList(restore_tasks.keys()));
4348+ }
4349+ check_for_unhandled_tasks_and_reply(unhandled, bus, msg);
4350+ }}
4351+ );
4352+ get_choices(restore_choices_, KeeperPrivate::ChoicesType::RESTORES_CHOICES);
4353+ }
4354+ }}
4355+ );
4356+
4357+ get_choices(backup_choices_, KeeperPrivate::ChoicesType::BACKUP_CHOICES);
4358+ msg.setDelayedReply(true);
4359+ }
4360+
4361+ void emit_choices_ready(ChoicesType type, keeper::Error error)
4362+ {
4363+ switch(type)
4364+ {
4365+ case KeeperPrivate::ChoicesType::BACKUP_CHOICES:
4366+ Q_EMIT(backup_choices_ready(error));
4367+ break;
4368+ case KeeperPrivate::ChoicesType::RESTORES_CHOICES:
4369+ Q_EMIT(restore_choices_ready(error));
4370+ break;
4371+ }
4372+ }
4373+
4374+ void get_choices(const QSharedPointer<MetadataProvider> & provider, ChoicesType type, QString const & storage = "")
4375+ {
4376+ bool check_empty = (type == KeeperPrivate::ChoicesType::BACKUP_CHOICES)
4377+ ? cached_backup_choices_.isEmpty() : cached_restore_choices_.isEmpty();
4378+ if (check_empty)
4379+ {
4380+ connections_.connect_oneshot(
4381+ provider.data(),
4382+ &MetadataProvider::finished,
4383+ std::function<void(keeper::Error)>{[this, provider, type](keeper::Error error){
4384+ qDebug() << "Get choices finished";
4385+ if (error == keeper::Error::OK)
4386+ {
4387+ switch (type)
4388+ {
4389+ case KeeperPrivate::ChoicesType::BACKUP_CHOICES:
4390+ cached_backup_choices_ = provider->get_backups();
4391+ break;
4392+ case KeeperPrivate::ChoicesType::RESTORES_CHOICES:
4393+ cached_restore_choices_ = provider->get_backups();
4394+ break;
4395+ }
4396+ }
4397+ emit_choices_ready(type, error);
4398+ }}
4399+ );
4400+ provider->get_backups_async(storage);
4401 }
4402 else
4403 {
4404- tasks = get_tasks(get_restore_choices(), uuids);
4405- if (!tasks.empty() && task_manager_.start_restore(tasks.values()))
4406- unhandled.subtract(QSet<QString>::fromList(tasks.keys()));
4407+ emit_choices_ready(type, keeper::Error::OK);
4408 }
4409-
4410- if (!unhandled.empty())
4411- qWarning() << "skipped tasks" << unhandled;
4412-
4413- return QStringList::fromSet(unhandled);
4414- }
4415-
4416- QVector<Metadata> get_backup_choices() const
4417- {
4418- if (cached_backup_choices_.isEmpty())
4419- cached_backup_choices_ = backup_choices_->get_backups();
4420-
4421- return cached_backup_choices_;
4422- }
4423-
4424- QVector<Metadata> get_restore_choices() const
4425- {
4426- if (cached_restore_choices_.isEmpty())
4427- cached_restore_choices_ = restore_choices_->get_backups();
4428-
4429- return cached_restore_choices_;
4430- }
4431-
4432- QVariantDictMap get_state() const
4433+ }
4434+
4435+ keeper::Items get_backup_choices_var_dict_map(QDBusConnection bus,
4436+ QDBusMessage const & msg)
4437+ {
4438+ connections_.connect_oneshot(
4439+ this,
4440+ &KeeperPrivate::backup_choices_ready,
4441+ std::function<void(keeper::Error)>{[this, msg, bus](keeper::Error error){
4442+ qDebug() << "Backup choices are ready";
4443+ if (error == keeper::Error::OK)
4444+ {
4445+ // reply now to the dbus call
4446+ auto reply = msg.createReply();
4447+ reply << QVariant::fromValue(choices_to_variant_dict_map(cached_backup_choices_));
4448+ bus.send(reply);
4449+ }
4450+ else
4451+ {
4452+ auto message = QStringLiteral("Error obtaining backup choices, keeper returned error: %1").arg(static_cast<int>(error));
4453+ qWarning() << message;
4454+ auto reply = msg.createErrorReply(QDBusError::Failed, message);
4455+ reply << QVariant::fromValue(error);
4456+ bus.send(reply);
4457+ }
4458+ }}
4459+ );
4460+ get_choices(backup_choices_, KeeperPrivate::ChoicesType::BACKUP_CHOICES);
4461+ msg.setDelayedReply(true);
4462+ return keeper::Items();
4463+ }
4464+
4465+ keeper::Items get_restore_choices_var_dict_map(QString const & storage,
4466+ QDBusConnection bus,
4467+ QDBusMessage const & msg)
4468+ {
4469+ qDebug() << "Getting restores for storage " << storage << " --------------------------------";
4470+ cached_restore_choices_.clear();
4471+ connections_.connect_oneshot(
4472+ this,
4473+ &KeeperPrivate::restore_choices_ready,
4474+ std::function<void(keeper::Error)>{[this, msg, bus](keeper::Error error){
4475+ qDebug() << "Restore choices are ready";
4476+ if (error == keeper::Error::OK)
4477+ {
4478+ // reply now to the dbus call
4479+ auto reply = msg.createReply();
4480+ reply << QVariant::fromValue(choices_to_variant_dict_map(cached_restore_choices_));
4481+ bus.send(reply);
4482+ }
4483+ else
4484+ {
4485+ auto message = QStringLiteral("Error obtaining restore choices, keeper returned error: %1").arg(static_cast<int>(error));
4486+ qWarning() << message;
4487+ auto reply = msg.createErrorReply(QDBusError::Failed, message);
4488+ reply << QVariant::fromValue(error);
4489+ bus.send(reply);
4490+ }
4491+ }}
4492+ );
4493+ get_choices(restore_choices_, KeeperPrivate::ChoicesType::RESTORES_CHOICES, storage);
4494+ msg.setDelayedReply(true);
4495+ return keeper::Items();
4496+ }
4497+
4498+ keeper::Items get_state() const
4499 {
4500 return task_manager_.get_state();
4501 }
4502@@ -127,7 +281,18 @@
4503 }
4504 );
4505
4506- qDebug() << "Asking for an storage framework socket to the task manager";
4507+ connections_.connect_oneshot(
4508+ &task_manager_,
4509+ &TaskManager::socket_error,
4510+ std::function<void(keeper::Error)>{
4511+ [bus,msg](keeper::Error error){
4512+ qDebug("BackupManager returned socket error: %d", static_cast<int>(error));
4513+ bus.send(msg.createErrorReply(QDBusError::InvalidArgs, "Error obtaining remote backup socket"));
4514+ }
4515+ }
4516+ );
4517+
4518+ qDebug() << "Asking for a storage framework socket from the task manager";
4519 task_manager_.ask_for_uploader(n_bytes);
4520
4521 // tell the caller that we'll be responding async
4522@@ -135,7 +300,102 @@
4523 return QDBusUnixFileDescriptor(0);
4524 }
4525
4526+
4527+ QDBusUnixFileDescriptor start_restore(QDBusConnection bus,
4528+ QDBusMessage const & msg)
4529+ {
4530+ qDebug() << "Keeper::StartRestore()";
4531+
4532+ connections_.connect_oneshot(
4533+ &task_manager_,
4534+ &TaskManager::socket_ready,
4535+ std::function<void(int)>{
4536+ [bus,msg](int fd){
4537+ qDebug("RestoreManager returned socket %d", fd);
4538+ auto reply = msg.createReply();
4539+ reply << QVariant::fromValue(QDBusUnixFileDescriptor(fd));
4540+ close(fd);
4541+ bus.send(reply);
4542+ }
4543+ }
4544+ );
4545+
4546+ connections_.connect_oneshot(
4547+ &task_manager_,
4548+ &TaskManager::socket_error,
4549+ std::function<void(keeper::Error)>{
4550+ [bus,msg](keeper::Error error){
4551+ qDebug("RestoreManager returned socket error: %d", static_cast<int>(error));
4552+ bus.send(msg.createErrorReply(QDBusError::InvalidArgs, "Error obtaining remote restore socket"));
4553+ }
4554+ }
4555+ );
4556+
4557+ qDebug() << "Asking for a storage framework socket from the task manager";
4558+ task_manager_.ask_for_downloader();
4559+
4560+ // tell the caller that we'll be responding async
4561+ msg.setDelayedReply(true);
4562+ return QDBusUnixFileDescriptor(0);
4563+ }
4564+
4565+ void cancel()
4566+ {
4567+ task_manager_.cancel();
4568+ }
4569+
4570+ void invalidate_choices_cache()
4571+ {
4572+ cached_backup_choices_.clear();
4573+ }
4574+
4575+ QStringList get_storage_accounts(QDBusConnection bus,
4576+ QDBusMessage const & msg)
4577+ {
4578+ connections_.connect_future(
4579+ storage_->get_accounts(),
4580+ std::function<void(QStringList const &)>{
4581+ [this, msg, bus](QStringList const& accounts){
4582+ qDebug() << "get_storage_accounts() finished";
4583+ // reply now to the dbus call
4584+ auto reply = msg.createReply();
4585+ reply << QVariant::fromValue(accounts);
4586+ bus.send(reply);
4587+ }
4588+ }
4589+ );
4590+ msg.setDelayedReply(true);
4591+ return QStringList();
4592+ }
4593+
4594+Q_SIGNALS:
4595+ void backup_choices_ready(keeper::Error error);
4596+ void restore_choices_ready(keeper::Error error);
4597+
4598 private:
4599+ void on_task_manager_finished()
4600+ {
4601+ // force a backup choices regeneration to avoid repeating uuids
4602+ // between backups
4603+ invalidate_choices_cache();
4604+ }
4605+
4606+ void check_for_unhandled_tasks_and_reply(QSet<QString> const & unhandled,
4607+ QDBusConnection bus,
4608+ QDBusMessage const & msg )
4609+ {
4610+ if (!unhandled.empty())
4611+ {
4612+ qWarning() << "skipped tasks" << unhandled;
4613+ QString text = QStringLiteral("unhandled uuids:");
4614+ for (auto const& uuid : unhandled)
4615+ text += ' ' + uuid;
4616+ bus.send(msg.createErrorReply(QDBusError::InvalidArgs, text));
4617+ }
4618+
4619+ auto reply = msg.createReply();
4620+ bus.send(reply);
4621+ }
4622
4623 Keeper * const q_ptr;
4624 QSharedPointer<StorageFrameworkClient> storage_;
4625@@ -160,12 +420,15 @@
4626
4627 Keeper::~Keeper() = default;
4628
4629-QStringList
4630-Keeper::start_tasks(QStringList const & uuids)
4631+void
4632+Keeper::start_tasks(QStringList const & uuids,
4633+ QString const & storage,
4634+ QDBusConnection bus,
4635+ QDBusMessage const & msg)
4636 {
4637 Q_D(Keeper);
4638
4639- return d->start_tasks(uuids);
4640+ d->start_tasks(uuids, storage, bus, msg);
4641 }
4642
4643 QDBusUnixFileDescriptor
4644@@ -178,26 +441,66 @@
4645 return d->start_backup(bus, msg, n_bytes);
4646 }
4647
4648-QVector<Metadata>
4649-Keeper::get_backup_choices()
4650-{
4651- Q_D(Keeper);
4652-
4653- return d->get_backup_choices();
4654-}
4655-
4656-QVector<Metadata>
4657-Keeper::get_restore_choices()
4658-{
4659- Q_D(Keeper);
4660-
4661- return d->get_restore_choices();
4662-}
4663-
4664-QVariantDictMap
4665+QDBusUnixFileDescriptor
4666+Keeper::StartRestore(QDBusConnection bus,
4667+ QDBusMessage const & msg)
4668+{
4669+ Q_D(Keeper);
4670+
4671+ return d->start_restore(bus, msg);
4672+}
4673+
4674+keeper::Items
4675+Keeper::get_backup_choices_var_dict_map(QDBusConnection bus,
4676+ QDBusMessage const & msg)
4677+{
4678+ Q_D(Keeper);
4679+
4680+ return d->get_backup_choices_var_dict_map(bus, msg);
4681+}
4682+
4683+keeper::Items
4684+Keeper::get_restore_choices(QString const & storage,
4685+ QDBusConnection bus,
4686+ QDBusMessage const & msg)
4687+{
4688+ Q_D(Keeper);
4689+
4690+ return d->get_restore_choices_var_dict_map(storage, bus, msg);
4691+}
4692+
4693+keeper::Items
4694 Keeper::get_state() const
4695 {
4696 Q_D(const Keeper);
4697
4698 return d->get_state();
4699 }
4700+
4701+void
4702+Keeper::cancel()
4703+{
4704+ Q_D(Keeper);
4705+
4706+ return d->cancel();
4707+}
4708+
4709+void
4710+Keeper::invalidate_choices_cache()
4711+{
4712+ Q_D(Keeper);
4713+
4714+ d->invalidate_choices_cache();
4715+}
4716+
4717+QStringList
4718+Keeper::get_storage_accounts(QDBusConnection bus,
4719+ QDBusMessage const & message)
4720+{
4721+ Q_D(Keeper);
4722+
4723+ return d->get_storage_accounts(bus,message);
4724+}
4725+
4726+
4727+#include "keeper.moc"
4728
4729=== modified file 'src/service/keeper.h'
4730--- src/service/keeper.h 2016-09-07 16:47:55 +0000
4731+++ src/service/keeper.h 2017-02-27 19:05:17 +0000
4732@@ -52,16 +52,30 @@
4733
4734 virtual ~Keeper();
4735
4736- QVector<Metadata> get_backup_choices();
4737- QVector<Metadata> get_restore_choices();
4738+ keeper::Items get_backup_choices_var_dict_map(QDBusConnection bus, QDBusMessage const & msg);
4739+ keeper::Items get_restore_choices(QString const & storage, QDBusConnection bus, QDBusMessage const & msg);
4740
4741 QDBusUnixFileDescriptor StartBackup(QDBusConnection,
4742 QDBusMessage const & message,
4743 quint64 nbytes);
4744
4745- QStringList start_tasks(QStringList const & uuids);
4746-
4747- QVariantDictMap get_state() const;
4748+
4749+ QDBusUnixFileDescriptor StartRestore(QDBusConnection,
4750+ QDBusMessage const & message);
4751+
4752+ void start_tasks(QStringList const & uuids,
4753+ QString const & storage,
4754+ QDBusConnection bus,
4755+ QDBusMessage const & msg);
4756+
4757+ keeper::Items get_state() const;
4758+
4759+ void cancel();
4760+
4761+ void invalidate_choices_cache();
4762+
4763+ QStringList get_storage_accounts(QDBusConnection,
4764+ QDBusMessage const & message);
4765
4766 private:
4767 QScopedPointer<KeeperPrivate> const d_ptr;
4768
4769=== modified file 'src/service/main.cpp'
4770--- src/service/main.cpp 2016-08-10 05:41:26 +0000
4771+++ src/service/main.cpp 2017-02-27 19:05:17 +0000
4772@@ -20,6 +20,7 @@
4773
4774 #include "dbus-types.h"
4775 #include "helper/data-dir-registry.h"
4776+#include "helper/helper.h"
4777 #include "service/backup-choices.h"
4778 #include "service/restore-choices.h"
4779 #include "service/keeper.h"
4780@@ -45,7 +46,7 @@
4781
4782 QCoreApplication app(argc, argv);
4783 DBusTypes::registerMetaTypes();
4784-// Variant::registerMetaTypes();
4785+ Helper::registerMetaTypes();
4786 std::srand(unsigned(std::time(nullptr)));
4787
4788 util::UnixSignalHandler handler([]{
4789
4790=== added file 'src/service/manifest.cpp'
4791--- src/service/manifest.cpp 1970-01-01 00:00:00 +0000
4792+++ src/service/manifest.cpp 2017-02-27 19:05:17 +0000
4793@@ -0,0 +1,248 @@
4794+/*
4795+ * Copyright (C) 2016 Canonical, Ltd.
4796+ *
4797+ * This program is free software: you can redistribute it and/or modify it
4798+ * under the terms of the GNU General Public License version 3, as published
4799+ * by the Free Software Foundation.
4800+ *
4801+ * This program is distributed in the hope that it will be useful, but
4802+ * WITHOUT ANY WARRANTY; without even the implied warranties of
4803+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
4804+ * PURPOSE. See the GNU General Public License for more details.
4805+ *
4806+ * You should have received a copy of the GNU General Public License along
4807+ * with this program. If not, see <http://www.gnu.org/licenses/>.
4808+ *
4809+ * Authors:
4810+ * Xavi Garcia Mena <xavi.garcia.mena@canonical.com>
4811+ */
4812+
4813+#include "manifest.h"
4814+
4815+#include "storage-framework/storage_framework_client.h"
4816+#include "util/connection-helper.h"
4817+
4818+#include <QJsonArray>
4819+#include <QJsonDocument>
4820+#include <QJsonObject>
4821+#include <QSharedPointer>
4822+#include <QVector>
4823+
4824+#include <fcntl.h>
4825+#include <sys/types.h>
4826+#include <sys/socket.h>
4827+
4828+namespace sf = unity::storage::qt::client;
4829+
4830+// JSON Keys
4831+namespace
4832+{
4833+ constexpr const char ENTRIES_KEY[] = "entries";
4834+ constexpr const char MANIFEST_FILE_NAME[] = "manifest.json";
4835+}
4836+
4837+/***
4838+****
4839+***/
4840+
4841+class ManifestPrivate
4842+{
4843+public:
4844+ ManifestPrivate(QSharedPointer<StorageFrameworkClient> const & storage, QString const & dir, Manifest * manifest)
4845+ : q_ptr{manifest}
4846+ , storage_{storage}
4847+ , dir_{dir}
4848+ {
4849+ }
4850+
4851+ ~ManifestPrivate() = default;
4852+
4853+ Q_DISABLE_COPY(ManifestPrivate)
4854+
4855+ void add_entry(Metadata const & entry)
4856+ {
4857+ entries_.push_back(entry);
4858+ }
4859+
4860+ void store()
4861+ {
4862+ qDebug() << "Metadata asking storage framework for a socket";
4863+ auto json_data = to_json();
4864+ auto n_bytes = json_data.size();
4865+
4866+ connections_.connect_future(
4867+ storage_->get_new_uploader(n_bytes, dir_, MANIFEST_FILE_NAME),
4868+ std::function<void(std::shared_ptr<Uploader> const&)>{
4869+ [this, json_data](std::shared_ptr<Uploader> const& uploader){
4870+ qDebug() << "Manifest uploader is" << static_cast<void*>(uploader.get());
4871+ if (uploader)
4872+ {
4873+ auto socket = uploader->socket();
4874+ socket->write(json_data);
4875+ connections_.connect_oneshot(
4876+ uploader.get(),
4877+ &Uploader::commit_finished,
4878+ std::function<void(bool)>{[this, uploader](bool success){
4879+ qDebug() << "Metadata commit finished";
4880+ if (!success)
4881+ {
4882+ finish_with_error(QStringLiteral("Error committing manifest file to storage-framework"));
4883+ }
4884+ else
4885+ {
4886+ uploader_committed_file_name_ = uploader->file_name();
4887+ finish();
4888+ }
4889+ }}
4890+ );
4891+ uploader->commit();
4892+ }
4893+ else
4894+ {
4895+ finish_with_error(QStringLiteral("Error retrieving uploader for manifest file from storage-framework"));
4896+ }
4897+ }
4898+ }
4899+ );
4900+ }
4901+
4902+ void read()
4903+ {
4904+ connections_.connect_future(
4905+ storage_->get_new_downloader(dir_, MANIFEST_FILE_NAME),
4906+ std::function<void(std::shared_ptr<Downloader> const&)>{
4907+ [this](std::shared_ptr<Downloader> const& downloader){
4908+ if (downloader)
4909+ {
4910+ auto socket = downloader->socket();
4911+ if (socket->atEnd())
4912+ {
4913+ if (!socket->waitForReadyRead(5000))
4914+ {
4915+ qWarning() << "Manifest socket was not ready to read after timeout";
4916+ }
4917+ }
4918+ auto json_content = socket->readAll();
4919+ from_json(json_content);
4920+ downloader->finish();
4921+ finish();
4922+ }
4923+ else
4924+ {
4925+ finish_with_error(QStringLiteral("Error retrieving downloader for manifest file from storage-framework"));
4926+ }
4927+ }
4928+ }
4929+ );
4930+ }
4931+
4932+ QVector<Metadata> get_entries()
4933+ {
4934+ return entries_;
4935+ }
4936+
4937+ QString error() const
4938+ {
4939+ return error_string_;
4940+ }
4941+
4942+ QByteArray to_json() const
4943+ {
4944+ QJsonArray json_array;
4945+ for (auto metadata : entries_)
4946+ {
4947+ json_array.append(metadata.json());
4948+ }
4949+ QJsonObject json_root;
4950+ json_root[ENTRIES_KEY] = json_array;
4951+ QJsonDocument doc(json_root);
4952+
4953+ return doc.toJson(QJsonDocument::Compact);
4954+ }
4955+
4956+ void from_json(QByteArray const & json)
4957+ {
4958+ auto doc_read = QJsonDocument::fromJson(json);
4959+
4960+ auto json_read_root = doc_read.object();
4961+ auto items = json_read_root[ENTRIES_KEY].toArray();
4962+
4963+ QVector<Metadata> read_metadata;
4964+ for( auto iter = items.begin(); iter != items.end(); ++iter)
4965+ {
4966+ entries_.push_back(Metadata((*iter).toObject()));
4967+ }
4968+ }
4969+
4970+private:
4971+
4972+ void finish_with_error(QString const & message)
4973+ {
4974+ error_string_ = message;
4975+ Q_EMIT(q_ptr->finished(false));
4976+ }
4977+
4978+ void finish()
4979+ {
4980+ error_string_ = "";
4981+ Q_EMIT(q_ptr->finished(true));
4982+ }
4983+
4984+ Manifest * const q_ptr;
4985+ QSharedPointer<StorageFrameworkClient> storage_;
4986+ QString dir_;
4987+
4988+ QVector<Metadata> entries_;
4989+ QString error_string_;
4990+ QString uploader_committed_file_name_;
4991+
4992+ ConnectionHelper connections_;
4993+};
4994+
4995+/***
4996+****
4997+***/
4998+
4999+Manifest::Manifest(QSharedPointer<StorageFrameworkClient> const & storage, QString const & dir, QObject * parent)
5000+ : QObject (parent)
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: