Skip to content

Latest commit

 

History

History
1409 lines (1200 loc) · 53 KB

transferengine.cpp

File metadata and controls

1409 lines (1200 loc) · 53 KB
 
Sep 19, 2019
Sep 19, 2019
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
* Copyright (c) 2013 - 2019 Jolla Ltd.
* Copyright (c) 2019 Open Mobile Platform LLC.
*
* All rights reserved.
*
* This file is part of Sailfish Transfer Engine package.
*
* You may use this file under the terms of the GNU Lesser General
* Public License version 2.1 as published by the Free Software Foundation
* and appearing in the file license.lgpl included in the packaging
* of this file.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* and appearing in the file license.lgpl included in the packaging
* of this file.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
25
26
27
28
29
30
#include "transferengine.h"
#include "transferengine_p.h"
#include "transferplugininterface.h"
#include "mediaitem.h"
#include "dbmanager.h"
Sep 19, 2019
Sep 19, 2019
31
#include "logging.h"
Mar 24, 2016
Mar 24, 2016
32
#include "transferengine_adaptor.h"
33
34
35
36
37
38
39
40
41
#include "transfertypes.h"
#include "transferplugininfo.h"
#include <QDir>
#include <QtDebug>
#include <QPluginLoader>
#include <QDBusMessage>
#include <QFileSystemWatcher>
#include <QTimer>
Jan 17, 2018
Jan 17, 2018
42
#include <QSettings>
Mar 19, 2015
Mar 19, 2015
44
#include <notification.h>
45
46
47
#include <signal.h>
Nov 22, 2013
Nov 22, 2013
48
49
#include <Accounts/Manager>
50
#define SHARE_PLUGINS_PATH "/usr/lib/nemo-transferengine/plugins"
Apr 16, 2013
Apr 16, 2013
51
#define CONFIG_PATH "/usr/share/nemo-transferengine/nemo-transfer-engine.conf"
52
#define FILE_WATCHER_TIMEOUT 5000
Jul 1, 2013
Jul 1, 2013
53
#define ACTIVITY_MONITOR_TIMEOUT 1*60*1000 // 1 minute in ms
Jul 1, 2013
Jul 1, 2013
54
#define TRANSFER_EXPIRATION_THRESHOLD 3*60 // 3 minutes in seconds
Jan 17, 2018
Jan 17, 2018
56
57
58
59
60
#define TRANSFER_EVENT_CATEGORY "transfer"
#define TRANSFER_COMPLETE_EVENT_CATEGORY "transfer.complete"
#define TRANSFER_ERROR_EVENT_CATEGORY "transfer.error"
#define TRANSFER_PROGRESS_HINT "x-nemo-progress"
Mar 19, 2015
Mar 19, 2015
61
62
63
64
65
66
67
68
69
TransferEngineSignalHandler * TransferEngineSignalHandler::instance()
{
static TransferEngineSignalHandler instance;
return &instance;
}
void TransferEngineSignalHandler::signalHandler(int signal)
{
Jan 17, 2018
Jan 17, 2018
70
if (signal == SIGUSR1) {
71
72
73
74
75
76
77
78
79
80
81
QMetaObject::invokeMethod(TransferEngineSignalHandler::instance(),
"exitSafely",
Qt::AutoConnection);
}
}
TransferEngineSignalHandler::TransferEngineSignalHandler()
{
signal(SIGUSR1, TransferEngineSignalHandler::signalHandler);
}
Jul 1, 2013
Jul 1, 2013
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// ---------------------------
// ClientActivityMonitor runs periodic checks if there are transfers which are expired.
// A transfer can be expired e.g. when a client has been crashed in the middle of Sync,
// Download or Upload operation or the client API isn't used properly.
//
// NOTE: This class only monitors if there are expired transfers and emit signal to indicate
// that it's cleaning time. It is up to Transfer Engine to remoce expired ids from the
// ClientActivityMonitor instance.
ClientActivityMonitor::ClientActivityMonitor(QObject *parent)
: QObject(parent)
, m_timer(new QTimer(this))
{
connect(m_timer, SIGNAL(timeout()), this, SLOT(checkActivity()));
m_timer->start(ACTIVITY_MONITOR_TIMEOUT);
}
ClientActivityMonitor::~ClientActivityMonitor()
{
m_activityMap.clear();
}
void ClientActivityMonitor::newActivity(int transferId)
{
// Update or add a new timestamp
m_activityMap.insert(transferId, QDateTime::currentDateTimeUtc().toTime_t());
}
void ClientActivityMonitor::activityFinished(int transferId)
{
if (!m_activityMap.contains(transferId)) {
Sep 19, 2019
Sep 19, 2019
113
qCWarning(lcTransferLog) << Q_FUNC_INFO << "Could not find matching TransferId. This is probably an error!";
Jul 1, 2013
Jul 1, 2013
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
return;
}
m_activityMap.remove(transferId);
}
bool ClientActivityMonitor::activeTransfers() const
{
return !m_activityMap.isEmpty();
}
void ClientActivityMonitor::checkActivity()
{
// Check if there are existing transfers which are not yet finished and
// they've been around too long. Notify TransferEngine about these transfers.
QList<int> ids;
Jul 1, 2013
Jul 1, 2013
130
quint32 currTime = QDateTime::currentDateTimeUtc().toTime_t();
Jul 1, 2013
Jul 1, 2013
131
132
133
134
135
136
137
138
139
140
141
142
143
QMap<int, quint32>::const_iterator i = m_activityMap.constBegin();
while (i != m_activityMap.constEnd()) {
if ((currTime - i.value()) >= TRANSFER_EXPIRATION_THRESHOLD) {
ids << i.key();
}
i++;
}
if (!ids.isEmpty()) {
emit transfersExpired(ids);
}
}
144
145
146
147
148
149
150
151
152
153
154
// ----------------------------
TransferEnginePrivate::TransferEnginePrivate(TransferEngine *parent):
m_notificationsEnabled(true),
q_ptr(parent)
{
m_fileWatcherTimer = new QTimer(this);
m_fileWatcherTimer->setSingleShot(true);
connect(m_fileWatcherTimer, SIGNAL(timeout()), this, SLOT(enabledPluginsCheck()));
Sep 30, 2014
Sep 30, 2014
155
156
m_delayedExitTimer = new QTimer(this);
m_delayedExitTimer->setSingleShot(true);
Sep 19, 2019
Sep 19, 2019
157
158
m_delayedExitTimer->setInterval(60000);
m_delayedExitTimer->start(); // Exit if nothing happens within 60 sec
Sep 30, 2014
Sep 30, 2014
159
160
connect(m_delayedExitTimer, SIGNAL(timeout()), this, SLOT(delayedExitSafely()));
161
162
163
164
165
166
167
168
169
m_fileWatcher = new QFileSystemWatcher(this);
m_fileWatcher->addPath(SHARE_PLUGINS_PATH);
connect(m_fileWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(pluginDirChanged()));
m_accountManager = new Accounts::Manager("sharing", this);
connect(m_accountManager, SIGNAL(accountCreated(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
connect(m_accountManager, SIGNAL(accountRemoved(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
connect(m_accountManager, SIGNAL(accountUpdated(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
Jul 1, 2013
Jul 1, 2013
170
// Exit safely stuff if we recieve certain signal or there are no active transfers
171
Q_Q(TransferEngine);
Jul 1, 2013
Jul 1, 2013
172
173
174
175
176
177
connect(TransferEngineSignalHandler::instance(), SIGNAL(exitSafely()), this, SLOT(exitSafely()));
connect(q, SIGNAL(statusChanged(int,int)), this, SLOT(exitSafely()));
// Monitor expired transfers and cleanup them if required
m_activityMonitor = new ClientActivityMonitor(this);
connect(m_activityMonitor, SIGNAL(transfersExpired(QList<int>)), this, SLOT(cleanupExpiredTransfers(QList<int>)));
Jan 17, 2018
Jan 17, 2018
178
179
180
181
QSettings settings(CONFIG_PATH, QSettings::IniFormat);
if (settings.status() != QSettings::NoError) {
Sep 19, 2019
Sep 19, 2019
182
qCWarning(lcTransferLog) << Q_FUNC_INFO << "Failed to read settings!" << settings.status();
Jan 17, 2018
Jan 17, 2018
183
184
185
186
187
188
189
190
191
192
193
194
195
} else {
settings.beginGroup("transfers");
const QString service = settings.value("service").toString();
const QString path = settings.value("path").toString();
const QString iface = settings.value("interface").toString();
const QString method = settings.value("method").toString();
settings.endGroup();
if (!service.isEmpty() && !path.isEmpty() && !iface.isEmpty() && !method.isEmpty()) {
m_defaultActions << Notification::remoteAction("default", "", service, path, iface, method)
<< Notification::remoteAction("app", "", service, path, iface, method);
}
}
196
197
198
199
200
201
202
203
204
205
206
207
}
void TransferEnginePrivate::pluginDirChanged()
{
// We need to check our plugins, but only after a short period of time
// because some operations may cause calling this slot over 10 times.
// E.g. reinstallation of plugins from the RPM package
m_fileWatcherTimer->start(FILE_WATCHER_TIMEOUT);
}
void TransferEnginePrivate::exitSafely()
{
Jul 1, 2013
Jul 1, 2013
208
if (!m_activityMonitor->activeTransfers()) {
Sep 19, 2019
Sep 19, 2019
209
qCDebug(lcTransferLog) << "Scheduling exit in" << m_delayedExitTimer->interval() << "ms";
Sep 30, 2014
Sep 30, 2014
210
211
212
m_delayedExitTimer->start();
} else {
m_delayedExitTimer->stop();
Feb 21, 2014
Feb 21, 2014
213
214
215
216
217
}
}
void TransferEnginePrivate::delayedExitSafely()
{
Mar 24, 2016
Mar 24, 2016
218
if (getenv("TRANSFER_ENGINE_KEEP_RUNNING")) {
Sep 19, 2019
Sep 19, 2019
219
qCDebug(lcTransferLog) << "Keeping transfer engine running";
Mar 24, 2016
Mar 24, 2016
220
} else {
Sep 19, 2019
Sep 19, 2019
221
qCDebug(lcTransferLog) << "Stopping transfer engine";
Mar 24, 2016
Mar 24, 2016
222
223
qApp->exit();
}
224
225
226
227
228
229
230
231
232
}
void TransferEnginePrivate::enabledPluginsCheck()
{
Q_Q(TransferEngine);
if (m_fileWatcherTimer->isActive()) {
m_fileWatcherTimer->stop();
}
Nov 22, 2013
Nov 22, 2013
233
if (m_infoObjects.count() > 0) {
Sep 19, 2019
Sep 19, 2019
234
qCWarning(lcTransferLog) << Q_FUNC_INFO << "Already quering account info" << m_infoObjects.count();
Nov 22, 2013
Nov 22, 2013
235
236
237
return;
}
238
239
// First clear old data
m_enabledPlugins.clear();
Nov 18, 2019
Nov 18, 2019
240
m_pluginMetaData.clear();
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
qDeleteAll(m_infoObjects);
m_infoObjects.clear();
QPluginLoader loader;
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
// Handle the case if all the plugins have been removed.
QStringList plugins = pluginList();
if (plugins.isEmpty()) {
emit q->transferMethodListChanged();
return;
}
// We have plugins
Q_FOREACH(QString plugin, plugins) {
loader.setFileName(plugin);
TransferPluginInterface *interface =
qobject_cast<TransferPluginInterface*>(loader.instance());
if (interface && interface->enabled()) {
TransferPluginInfo *info = interface->infoObject();
if (!info) {
Sep 19, 2019
Sep 19, 2019
264
qCWarning(lcTransferLog) << Q_FUNC_INFO << "NULL Info object!";
265
266
267
continue;
}
Nov 22, 2013
Nov 22, 2013
268
269
270
271
if (info->ready()) {
if (info->info().count() > 0) {
m_enabledPlugins << info->info();
}
Nov 18, 2019
Nov 18, 2019
272
m_pluginMetaData << info->metaData();
Sep 22, 2014
Sep 22, 2014
273
delete info;
Nov 22, 2013
Nov 22, 2013
274
275
276
277
278
279
280
} else {
// These object will be cleaned in pluginInfoReady() slot.
m_infoObjects << info;
connect(info, SIGNAL(infoReady()), this, SLOT(pluginInfoReady()));
connect(info, SIGNAL(infoError(QString)), this, SLOT(pluginInfoError(QString)));
info->query();
}
281
282
283
}
if (!interface) {
Sep 19, 2019
Sep 19, 2019
284
qCWarning(lcTransferLog) << Q_FUNC_INFO << loader.errorString();
Jul 1, 2013
Jul 1, 2013
287
}
Jul 1, 2013
Jul 1, 2013
289
290
291
292
293
294
295
296
297
298
299
300
void TransferEnginePrivate::cleanupExpiredTransfers(const QList<int> &expiredIds)
{
// Clean up expired items from the database by changing the status to TransferInterrupted. This way
// database doesn't maintain objects with unifinished state and failed items can be cleaned by the
// user manually from the UI.
Q_Q(TransferEngine);
Q_FOREACH(int id, expiredIds) {
if (DbManager::instance()->updateTransferStatus(id, TransferEngineData::TransferInterrupted)) {
m_activityMonitor->activityFinished(id);
emit q->statusChanged(id, TransferEngineData::TransferInterrupted);
}
}
301
302
303
304
305
306
307
}
void TransferEnginePrivate::recoveryCheck()
{
QList<TransferDBRecord> records = DbManager::instance()->transfers();
// Check all transfer which are not properly finished and mark those as
// interrupted
Jul 1, 2013
Jul 1, 2013
308
Q_Q(TransferEngine);
309
310
311
Q_FOREACH(TransferDBRecord record, records) {
if (record.status == TransferEngineData::TransferStarted ||
record.status == TransferEngineData::NotStarted) {
Jul 1, 2013
Jul 1, 2013
312
313
if (DbManager::instance()->updateTransferStatus(record.transfer_id, TransferEngineData::TransferInterrupted)) {
emit q->statusChanged(record.transfer_id, TransferEngineData::TransferInterrupted);
Mar 24, 2016
Mar 24, 2016
314
315
316
if (record.status == TransferEngineData::TransferStarted) {
emit q_ptr->activeTransfersChanged(); // It's not active anymore
}
Jul 1, 2013
Jul 1, 2013
317
}
318
319
320
321
322
323
}
}
}
void TransferEnginePrivate::sendNotification(TransferEngineData::TransferType type,
TransferEngineData::TransferStatus status,
Jan 17, 2018
Jan 17, 2018
324
325
326
qreal progress,
const QString &fileName,
int transferId)
Oct 2, 2013
Oct 2, 2013
328
if (!m_notificationsEnabled || fileName.isEmpty()) {
Mar 19, 2015
Mar 19, 2015
332
333
334
335
336
QString category;
QString body;
QString summary;
QString previewBody;
QString previewSummary;
Jan 17, 2018
Jan 17, 2018
337
338
339
bool useProgress = false;
Notification::Urgency urgency = Notification::Normal;
QString appIcon = QStringLiteral("icon-lock-information");
Mar 27, 2018
Mar 27, 2018
340
QString icon = QStringLiteral("icon-lock-transfer");
Mar 19, 2015
Mar 19, 2015
342
343
344
// TODO: explicit grouping of transfer notifications is now removed, as grouping
// will now be performed by lipstick. We may need to reinstate group summary
// notifications at some later point...
Apr 18, 2013
Apr 18, 2013
345
Mar 19, 2015
Mar 19, 2015
346
347
348
349
350
351
352
353
354
355
356
// Notification & Banner rules:
//
// Show Banner:
// - For succesfull uploads and for downloads
// - For failed Uploads, Downloads, Syncs
//
// Show an event in the EventView:
// - For downloads
// - For failed uploads, downloads and syncs
Jan 17, 2018
Jan 17, 2018
357
int notificationId = DbManager::instance()->notificationId(transferId);
Apr 18, 2013
Apr 18, 2013
358
359
360
361
362
363
if (status == TransferEngineData::TransferFinished) {
switch(type) {
case TransferEngineData::Upload:
//: Notification for successful file upload
//% "File uploaded"
Mar 19, 2015
Mar 19, 2015
364
365
366
previewBody = qtTrId("transferengine-no-file-upload-success");
previewSummary = fileName;
category = TRANSFER_EVENT_CATEGORY; // Use "generic" transfer event for uploads
367
368
break;
case TransferEngineData::Download:
Mar 19, 2015
Mar 19, 2015
369
category = TRANSFER_COMPLETE_EVENT_CATEGORY;
370
371
//: Notification for successful file download
//% "File downloaded"
Mar 19, 2015
Mar 19, 2015
372
373
body = qtTrId("transferengine-no-file-download-success");
summary = fileName;
Jun 3, 2015
Jun 3, 2015
374
375
previewBody = body;
previewSummary = summary;
376
377
378
break;
case TransferEngineData::Sync:
// Ok exit
Mar 19, 2015
Mar 19, 2015
379
break;
Sep 19, 2019
Sep 19, 2019
381
qCWarning(lcTransferLog) << "TransferEnginePrivate::sendNotification: unknown state";
Mar 19, 2015
Mar 19, 2015
382
break;
Jan 17, 2018
Jan 17, 2018
384
Jan 17, 2018
Jan 17, 2018
385
} else if (status == TransferEngineData::TransferInterrupted) {
Jan 17, 2018
Jan 17, 2018
386
387
urgency = Notification::Critical;
appIcon = QStringLiteral("icon-lock-information");
Mar 27, 2018
Mar 27, 2018
388
category = TRANSFER_ERROR_EVENT_CATEGORY;
Jan 17, 2018
Jan 17, 2018
389
icon.clear();
Apr 18, 2013
Apr 18, 2013
390
391
392
393
switch (type) {
case TransferEngineData::Upload:
//: Notification for failed file upload
May 20, 2015
May 20, 2015
394
395
//% "Upload failed"
body = qtTrId("transferengine-la-file_upload_failed");
396
397
break;
case TransferEngineData::Download:
Apr 18, 2013
Apr 18, 2013
398
//: Notification for failed file download
May 20, 2015
May 20, 2015
399
400
//% "Download failed"
body = qtTrId("transferengine-la-file_download_failed");
401
402
break;
case TransferEngineData::Sync:
May 20, 2015
May 20, 2015
403
404
// no notification required
category.clear();
405
406
break;
default:
Sep 19, 2019
Sep 19, 2019
407
qCWarning(lcTransferLog) << "TransferEnginePrivate::sendNotification: unknown state";
Mar 19, 2015
Mar 19, 2015
408
409
category.clear();
break;
Mar 19, 2015
Mar 19, 2015
412
413
414
summary = fileName;
previewSummary = summary;
previewBody = body;
Jan 17, 2018
Jan 17, 2018
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
} else if (status == TransferEngineData::TransferStarted) {
if (type == TransferEngineData::Upload || type == TransferEngineData::Download) {
category = TRANSFER_EVENT_CATEGORY;
if (type == TransferEngineData::Upload) {
//: Notification for ongoing upload
//% "File uploading"
body = qtTrId("transferengine-no-file-uploading");
} else {
//: Notification for ongoing file download
//% "File downloading"
body = qtTrId("transferengine-no-file-downloading");
}
summary = fileName;
if (progress > 0)
useProgress = true;
Jan 17, 2018
Jan 17, 2018
435
436
437
438
439
440
441
442
443
} else if (status == TransferEngineData::TransferCanceled && notificationId > 0) {
// Exit, no banners or events when user has canceled a transfer
// Remove any existing notification
Notification notification;
notification.setReplacesId(notificationId);
notification.close();
DbManager::instance()->setNotificationId(transferId, 0);
notificationId = 0;
Jan 17, 2018
Jan 17, 2018
444
}
Mar 19, 2015
Mar 19, 2015
446
447
if (!category.isEmpty()) {
Notification notification;
Jan 17, 2018
Jan 17, 2018
449
450
451
if (notificationId > 0) {
notification.setReplacesId(notificationId);
}
Oct 2, 2013
Oct 2, 2013
452
Jan 17, 2018
Jan 17, 2018
453
454
if (!m_defaultActions.isEmpty()) {
notification.setRemoteActions(m_defaultActions);
May 4, 2015
May 4, 2015
457
458
459
460
461
462
463
//: Group name for notifications of successful transfers
//% "Transfers"
const QString transfersGroup(qtTrId("transferengine-notification_group"));
//: Group name for notifications of failed transfers
//% "Warnings"
const QString errorsGroup(qtTrId("transferengine-notification_errors_group"));
Jan 17, 2018
Jan 17, 2018
464
465
466
467
468
469
470
notification.setCategory(category);
notification.setAppName(category == TRANSFER_ERROR_EVENT_CATEGORY ? errorsGroup : transfersGroup);
notification.setSummary(summary);
notification.setBody(body);
notification.setPreviewSummary(previewSummary);
notification.setPreviewBody(previewBody);
notification.setUrgency(urgency);
Mar 19, 2015
Mar 19, 2015
471
Mar 27, 2018
Mar 27, 2018
472
473
474
475
if (!icon.isEmpty()) {
notification.setIcon(icon);
}
Jan 17, 2018
Jan 17, 2018
476
477
478
479
480
481
482
483
484
485
if (useProgress) {
notification.setHintValue(TRANSFER_PROGRESS_HINT, static_cast<double>(progress));
}
notification.publish();
int newId = notification.replacesId();
if (newId != notificationId) {
DbManager::instance()->setNotificationId(transferId, newId);
}
}
486
487
488
489
490
491
492
493
}
int TransferEnginePrivate::uploadMediaItem(MediaItem *mediaItem,
MediaTransferInterface *muif,
const QVariantMap &userData)
{
Q_Q(TransferEngine);
Jan 17, 2018
Jan 17, 2018
494
if (mediaItem == 0) {
Sep 19, 2019
Sep 19, 2019
495
qCWarning(lcTransferLog) << "TransferEngine::uploadMediaItem invalid MediaItem";
496
497
498
return -1;
}
if (muif == 0) {
Sep 19, 2019
Sep 19, 2019
499
qCWarning(lcTransferLog) << "TransferEngine::uploadMediaItem Failed to get MediaTransferInterface";
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
return -1;
}
mediaItem->setValue(MediaItem::TransferType, TransferEngineData::Upload);
mediaItem->setValue(MediaItem::DisplayName, muif->displayName());
mediaItem->setValue(MediaItem::ServiceIcon, muif->serviceIcon());
mediaItem->setValue(MediaItem::CancelSupported, muif->cancelEnabled());
mediaItem->setValue(MediaItem::RestartSupported, muif->restartEnabled());
// Get and set data from user data if that's set. The following user data values
// are stored to the database so that's why they are set to the media item too.
// If the user data is fully custom for plugin it won't be stored to the database and
// it's up to the plugin to handle or ignore it.
QString title = userData.value("title").toString();
QString desc = userData.value("description").toString();
qint64 accId = userData.value("accountId").toInt();
qreal scale = userData.value("scalePercent").toReal();
mediaItem->setValue(MediaItem::Title, title);
mediaItem->setValue(MediaItem::Description, desc);
mediaItem->setValue(MediaItem::AccountId, accId);
mediaItem->setValue(MediaItem::ScalePercent, scale);
muif->setMediaItem(mediaItem);
connect(muif, SIGNAL(statusChanged(MediaTransferInterface::TransferStatus)),
this, SLOT(uploadItemStatusChanged(MediaTransferInterface::TransferStatus)));
connect(muif, SIGNAL(progressUpdated(qreal)),
this, SLOT(updateProgress(qreal)));
// Let's create an entry into Transfer DB
const int key = DbManager::instance()->createTransferEntry(mediaItem);
m_keyTypeCache.insert(key, TransferEngineData::Upload);
if (key < 0) {
Sep 19, 2019
Sep 19, 2019
534
qCWarning(lcTransferLog) << "TransferEngine::uploadMediaItem: Failed to create an entry to transfer database!";
535
536
537
538
delete muif;
return key;
}
Jul 1, 2013
Jul 1, 2013
539
m_activityMonitor->newActivity(key);
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
emit q->transfersChanged();
emit q->statusChanged(key, TransferEngineData::NotStarted);
// For now, we just store our uploader to a map. It'll be removed from it when
// the upload has finished.
m_plugins.insert(muif, key);
muif->start();
return key;
}
QStringList TransferEnginePrivate::pluginList() const
{
QDir dir(SHARE_PLUGINS_PATH);
QStringList plugins = dir.entryList(QStringList() << "*.so",
QDir::Files,
QDir::NoSort);
QStringList filePaths;
Q_FOREACH(QString plugin, plugins) {
filePaths << dir.absolutePath() + QDir::separator() + plugin;
}
return filePaths;
}
QList <TransferMethodInfo> TransferEnginePrivate::enabledPlugins() const
{
return m_enabledPlugins;
}
Nov 18, 2019
Nov 18, 2019
569
570
571
572
573
QList<QVariantMap> TransferEnginePrivate::pluginMetaData() const
{
return m_pluginMetaData;
}
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
MediaTransferInterface *TransferEnginePrivate::loadPlugin(const QString &pluginId) const
{
QPluginLoader loader;
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint | QLibrary::ExportExternalSymbolsHint);
Q_FOREACH(QString plugin, pluginList()) {
loader.setFileName(plugin);
TransferPluginInterface *interface =
qobject_cast<TransferPluginInterface*>(loader.instance());
if (interface && interface->enabled() && interface->pluginId() == pluginId) {
return interface->transferObject();
}
if (!interface) {
Sep 19, 2019
Sep 19, 2019
589
qCWarning(lcTransferLog) << "TransferEngine::loadPlugin: " + loader.errorString();
590
591
592
593
594
595
596
597
598
599
}
if (loader.isLoaded()) {
loader.unload();
}
}
return 0;
}
Oct 2, 2013
Oct 2, 2013
600
601
602
603
604
605
606
607
608
609
610
611
612
QString TransferEnginePrivate::mediaFileOrResourceName(MediaItem *mediaItem) const
{
if (!mediaItem) {
return QString();
}
QUrl url = mediaItem->value(MediaItem::Url).toUrl();
if (!url.isEmpty()) {
QStringList split = url.toString().split(QDir::separator());
return split.at(split.length()-1);
}
return mediaItem->value(MediaItem::ResourceName).toString();
}
613
614
615
616
617
618
619
620
621
622
623
624
625
void TransferEnginePrivate::uploadItemStatusChanged(MediaTransferInterface::TransferStatus status)
{
MediaTransferInterface *muif = qobject_cast<MediaTransferInterface*>(sender());
const int key = m_plugins.value(muif);
const TransferEngineData::TransferType type =
static_cast<TransferEngineData::TransferType>(muif->mediaItem()->value(MediaItem::TransferType).toInt());
TransferEngineData::TransferStatus tStatus = static_cast<TransferEngineData::TransferStatus>(status);
bool ok = false;
switch(tStatus) {
case TransferEngineData::TransferStarted:
ok = DbManager::instance()->updateTransferStatus(key, tStatus);
Jul 1, 2013
Jul 1, 2013
626
m_activityMonitor->newActivity(key);
627
628
629
630
631
632
633
634
635
break;
case TransferEngineData::TransferInterrupted:
case TransferEngineData::TransferCanceled:
case TransferEngineData::TransferFinished:
{
// If the flow ends up here, we are not interested in any signals the same object
// might emit. Let's just disconnect them.
muif->disconnect();
Jan 17, 2018
Jan 17, 2018
636
sendNotification(type, tStatus, muif->progress(), mediaFileOrResourceName(muif->mediaItem()), key);
637
638
ok = DbManager::instance()->updateTransferStatus(key, tStatus);
if (m_plugins.remove(muif) == 0) {
Sep 19, 2019
Sep 19, 2019
639
qCWarning(lcTransferLog) << "TransferEnginePrivate::uploadItemStatusChanged: Failed to remove media upload object from the map!";
640
641
642
643
// What to do here.. Let's just delete it..
}
muif->deleteLater();
muif = 0;
Jul 1, 2013
Jul 1, 2013
644
m_activityMonitor->activityFinished(key);
645
646
647
} break;
default:
Sep 19, 2019
Sep 19, 2019
648
qCWarning(lcTransferLog) << "TransferEnginePrivate::uploadItemStatusChanged: unhandled status: " << tStatus;
649
650
651
652
break;
}
if (!ok) {
Sep 19, 2019
Sep 19, 2019
653
qCWarning(lcTransferLog) << "TransferEnginePrivate::uploadItemStatusChanged: Failed update share status for the item: " + key;
654
655
656
657
658
659
660
661
662
663
664
665
666
return;
}
Q_Q(TransferEngine);
emit q->statusChanged(key, tStatus);
}
void TransferEnginePrivate::updateProgress(qreal progress)
{
MediaTransferInterface *muif = qobject_cast<MediaTransferInterface*>(sender());
const int key = m_plugins.value(muif);
if (!DbManager::instance()->updateProgress(key, progress)) {
Sep 19, 2019
Sep 19, 2019
667
qCWarning(lcTransferLog) << "TransferEnginePrivate::updateProgress: Failed to update progress";
Jul 1, 2013
Jul 1, 2013
671
m_activityMonitor->newActivity(key);
672
Q_Q(TransferEngine);
Jan 17, 2018
Jan 17, 2018
673
q->updateTransferProgress(key, progress);
674
675
676
677
678
679
}
void TransferEnginePrivate::pluginInfoReady()
{
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
Nov 22, 2013
Nov 22, 2013
680
681
682
683
QList<TransferMethodInfo> infoList = infoObj->info();
if (!infoList.isEmpty()) {
m_enabledPlugins << infoList;
}
Nov 18, 2019
Nov 18, 2019
684
m_pluginMetaData << infoObj->metaData();
Nov 22, 2013
Nov 22, 2013
685
686
687
688
if (m_infoObjects.removeOne(infoObj)) {
delete infoObj;
} else {
Sep 19, 2019
Sep 19, 2019
689
qCWarning(lcTransferLog) << Q_FUNC_INFO << "Failed to remove info object!";
Nov 22, 2013
Nov 22, 2013
690
691
delete infoObj;
}
692
693
694
695
696
697
698
699
700
if (m_infoObjects.isEmpty()) {
Q_Q(TransferEngine);
emit q->transferMethodListChanged();
}
}
void TransferEnginePrivate::pluginInfoError(const QString &msg)
{
Sep 19, 2019
Sep 19, 2019
701
qCWarning(lcTransferLog) << "TransferEnginePrivate::pluginInfoError:" << msg;
702
703
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
m_infoObjects.removeOne(infoObj);
May 5, 2014
May 5, 2014
704
infoObj->deleteLater();
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
if (m_infoObjects.isEmpty()) {
Q_Q(TransferEngine);
emit q->transferMethodListChanged();
}
}
TransferEngineData::TransferType TransferEnginePrivate::transferType(int transferId)
{
if (!m_keyTypeCache.contains(transferId)) {
TransferEngineData::TransferType type = DbManager::instance()->transferType(transferId);
m_keyTypeCache.insert(transferId, type);
return type;
} else {
return m_keyTypeCache.value(transferId);
}
}
void TransferEnginePrivate::callbackCall(int transferId, CallbackMethodType method)
{
// Get DBus callback information. Callback list must contain at least
// service, path, interface and one callback method name. Note that even
// if the cancel or restart method is missing, it'll be indicated as an
// empty string. So the list length is always 5.
QStringList callback = DbManager::instance()->callback(transferId);
if (callback.length() != 5) {
Sep 19, 2019
Sep 19, 2019
732
qCWarning(lcTransferLog) << "TransferEnginePrivate:callbackCall: Invalid callback interface";
733
734
735
736
737
738
739
740
return;
}
QDBusInterface remoteInterface(callback.at(Service),
callback.at(Path),
callback.at(Interface));
if (!remoteInterface.isValid()) {
Sep 19, 2019
Sep 19, 2019
741
qCWarning(lcTransferLog) << "TransferEnginePrivate::callbackCall: DBus interface is not valid!";
742
743
744
745
return;
}
if (method >= callback.size()) {
Sep 19, 2019
Sep 19, 2019
746
qCWarning(lcTransferLog) << "TransferEnginePrivate::callbackCall: method index out of range!";
747
748
749
750
751
return;
}
const QString methodName = callback.at(method);
if (methodName.isEmpty()) {
Sep 19, 2019
Sep 19, 2019
752
qCWarning(lcTransferLog) << "TransferEnginePrivate::callbackCall: Failed to get callback method name!";
Jul 1, 2013
Jul 1, 2013
754
755
}
remoteInterface.call(methodName, transferId);
756
757
758
759
760
761
762
763
764
765
766
}
/*!
\class TransferEngine
\brief The TransferEngine class implements the functionality for different transfer types.
\ingroup transfer-engine
TransferEngine is the central place for:
\list
Jan 17, 2018
Jan 17, 2018
767
768
769
\li Sharing - Provides requires plugin interfaces for share plugins
\li Downloads - Provides an API to create Download entries
\li Syncs - Provides an API to create Sync entries
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
\endlist
For Downloads and Syncs, the Transfer Engine acts only a place to keep track of these operations.
The actual Download and Sync is executed by a client using TransferEngine API. For sharing
the TransferEngine provides an API containing a few interaces, which a share plugin must implement.
TransferEngine also takes care of loading and executing the sharing, based on the API it defines.
The most essential thing to remember is that Transfer Engine provides share plugin API, DBus API e.g.
for creating Transfer UI or Share UIs, it stores data to the local sqlite database using DbManager
and that's it.
How to implement a share plugin see: TransferPluginInterface, MediaTransferInterface, MediaItem,
TransferPluginInfo
TransferEngine provides DBus API, but instead of using it directly, it's recommended to use
TransferEngineClient. If there is a need to create UI to display e.g. transfer statuses, then
the DBus API is the recommend way to implement it.
*/
/*!
\fn void TransferEngine::progressChanged(int transferId, double progress)
The signal is emitted when \a progress for a transfer with a \a transferId has changed.
*/
/*!
\fn void TransferEngine::statusChanged(int transferId, int status)
The signal is emitted when \a status for a transfer with a \a transferId has changed.
*/
/*!
\fn void TransferEngine::transferMethodListChanged()
The signal is emitted when transfer methods have changed. Usually tranfer methods change
when a new plugin is installed to the system or an account has been enabled or disabled.
*/
/*!
\fn void TransferEngine::transfersChanged()
The signal is emitted when there is a new transfer or a transfer has been removed from the
database.
*/
/*!
Constructor with optional \a parent arguement.
*/
TransferEngine::TransferEngine(QObject *parent) :
QObject(parent),
d_ptr(new TransferEnginePrivate(this))
{
Dec 12, 2017
Dec 12, 2017
823
TransferMethodInfoDeprecated::registerType();
824
825
TransferMethodInfo::registerType();
TransferDBRecord::registerType();
Nov 18, 2019
Nov 18, 2019
826
TransferPluginInfo::registerType();
827
828
829
830
831
832
QDBusConnection connection = QDBusConnection::sessionBus();
if (!connection.registerObject("/org/nemo/transferengine", this)) {
qFatal("Could not register object \'/org/nemo/transferengine\'");
}
Sep 22, 2016
Sep 22, 2016
833
834
835
836
if (!connection.registerService("org.nemo.transferengine")) {
qFatal("DBUS service already taken. Kill the other instance first.");
}
Jul 1, 2013
Jul 1, 2013
837
new TransferEngineAdaptor(this);
838
839
840
841
842
843
// Let's make sure that db is open by creating
// DbManager singleton instance.
DbManager::instance();
Q_D(TransferEngine);
d->recoveryCheck();
Jul 1, 2013
Jul 1, 2013
844
d->enabledPluginsCheck();
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
}
/*!
Destroys the TransferEngine object.
*/
TransferEngine::~TransferEngine()
{
Q_D(TransferEngine);
d->recoveryCheck();
delete d_ptr;
d_ptr = 0;
QDBusConnection connection = QDBusConnection::sessionBus();
connection.unregisterObject("/org/nemo/transferengine");
if (!connection.unregisterService("org.nemo.transferengine")) {
Sep 19, 2019
Sep 19, 2019
861
qCWarning(lcTransferLog) << "Failed to unregister org.nemo.tranferengine service";
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
}
}
/*!
DBus adaptor calls this method to start uploading a media item. The minimum information
needed to start an upload and to create an entry to the transfer database is:
\a source the path to the media item to be downloaded. \a serviceId the ID of the share
plugin. See TransferPluginInterface::pluginId() for more details. \a mimeType is the MimeType
of the media item e.g. "image/jpeg". \a metadataStripped boolean to indicate if the metadata
should be kept or removed before uploading. \a userData is various kind of data which share UI
may provide to the engine. UserData is QVariant map i.e. the data must be provided as key-value
pairs, where the keys must be QStrings.
TransferEngine handles the following user defined data automatically and stores them to the database:
\list
Jan 17, 2018
Jan 17, 2018
878
879
880
881
\li "title" Title for the media
\li "description" Description for the media
\li "accountId" The ID of the account which is used for sharing. See qt-accounts for more details.
\li "scalePercent" The scale percent e.g. downscale image to 50% from original before uploading.
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
\endlist
In practice this method instantiates a share plugin with \a serviceId and passes a MediaItem instance filled
with required data to it. When the plugin has been loaded, the MediaTransferInterface::start() method is called
and the actual sharing starts.
This method returns a transfer ID which can be used later to fetch information of this specific transfer.
*/
int TransferEngine::uploadMediaItem(const QString &source,
const QString &serviceId,
const QString &mimeType,
bool metadataStripped,
const QVariantMap &userData)
{
Q_D(TransferEngine);
Oct 1, 2014
Oct 1, 2014
897
898
d->exitSafely();
899
900
MediaTransferInterface *muif = d->loadPlugin(serviceId);
if (muif == 0) {
Sep 19, 2019
Sep 19, 2019
901
qCWarning(lcTransferLog) << "TransferEngine::uploadMediaItem Failed to get MediaTransferInterface";
902
903
904
905
906
907
return -1;
}
QUrl filePath(source);
QFileInfo fileInfo(filePath.toLocalFile());
if (!fileInfo.exists()) {
Sep 19, 2019
Sep 19, 2019
908
qCWarning(lcTransferLog) << "TransferEnginePrivate::uploadMediaItem file " << source << " doesn't exist!";
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
}
MediaItem *mediaItem = new MediaItem(muif);
mediaItem->setValue(MediaItem::Url, filePath);
mediaItem->setValue(MediaItem::MetadataStripped, metadataStripped);
mediaItem->setValue(MediaItem::ResourceName, fileInfo.fileName());
mediaItem->setValue(MediaItem::MimeType, mimeType);
mediaItem->setValue(MediaItem::FileSize, fileInfo.size());
mediaItem->setValue(MediaItem::PluginId, serviceId);
mediaItem->setValue(MediaItem::UserData, userData);
return d->uploadMediaItem(mediaItem, muif, userData);
}
/*!
DBus adaptor calls this method to start uploading media item content. Sometimes the content
to be shared is not a file, but data e.g. contact information in vcard format. In order to
avoid serializing data to a file, pass url to the file, reading the data, deleting the file,
TransferEngine provides this convenience API.
\a content is the media item content to be shared. \a serviceId is the id of the share plugin. See
TransferPluginInterface::pluginId() for more details. \a userData is a QVariantMap containing
share plugin specific data. See TransferEngine::uploadMediaItem for more details.
This method returns a transfer ID which can be used later to fetch information of this specific transfer.
*/
int TransferEngine::uploadMediaItemContent(const QVariantMap &content,
const QString &serviceId,
const QVariantMap &userData)
{
Q_D(TransferEngine);
Oct 1, 2014
Oct 1, 2014
939
940
d->exitSafely();
941
942
MediaTransferInterface *muif = d->loadPlugin(serviceId);
if (muif == 0) {
Sep 19, 2019
Sep 19, 2019
943
qCWarning(lcTransferLog) << "TransferEngine::uploadMediaItemContent Failed to get MediaTransferInterface";
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
return -1;
}
MediaItem *mediaItem = new MediaItem(muif);
mediaItem->setValue(MediaItem::ContentData, content.value("data"));
mediaItem->setValue(MediaItem::ResourceName, content.value("name"));
mediaItem->setValue(MediaItem::MimeType, content.value("type"));
mediaItem->setValue(MediaItem::ThumbnailIcon, content.value("icon"));
mediaItem->setValue(MediaItem::PluginId, serviceId);
mediaItem->setValue(MediaItem::UserData, userData);
return d->uploadMediaItem(mediaItem, muif, userData);
}
/*!
DBus adaptor calls this method to create a download entry. Note that this is purely write-only
method and doesn't involve anything else from TransferEngine side than creating a new DB record
of type 'Download'.
\list
Jan 17, 2018
Jan 17, 2018
963
964
965
966
967
968
969
970
971
\li \a displayName The name for Download which may be used by the UI displaying the download
\li \a applicationIcon The application icon of the application created the download
\li \a serviceIcon The service icon, which provides the file to be downloaded
\li \a filePath The filePath e.g. url to the file to be downloaded
\li \a mimeType the MimeType of the file to be downloaded
\li \a expectedFileSize The file size of the file to be downloaded
\li \a callback QStringList containing DBus callback information such as: service, path and interface
\li \a cancelMethod The name of the cancel callback method, which DBus callback provides
\li \a restartMethod The name of the restart callback method, which DBus callback provides
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
\endlist
This method returns the transfer id of the created Download transfer. Note that this method only
creates an entry to the database. To start the actual transfer, the startTransfer() method must
be called.
\sa startTransfer(), restartTransfer(), finishTransfer(), updateTransferProgress()
*/
int TransferEngine::createDownload(const QString &displayName,
const QString &applicationIcon,
const QString &serviceIcon,
const QString &filePath,
const QString &mimeType,
qlonglong expectedFileSize,
const QStringList &callback,
const QString &cancelMethod,
const QString &restartMethod)
{
Q_D(TransferEngine);
QUrl url(filePath);
QFileInfo fileInfo(filePath);
MediaItem *mediaItem = new MediaItem();
mediaItem->setValue(MediaItem::Url, url);
mediaItem->setValue(MediaItem::ResourceName, fileInfo.fileName());
mediaItem->setValue(MediaItem::MimeType, mimeType);
mediaItem->setValue(MediaItem::TransferType, TransferEngineData::Download);
mediaItem->setValue(MediaItem::FileSize, expectedFileSize);
mediaItem->setValue(MediaItem::DisplayName, displayName);