Skip to content

Latest commit

 

History

History
1383 lines (1178 loc) · 52 KB

transferengine.cpp

File metadata and controls

1383 lines (1178 loc) · 52 KB
 
1
2
/****************************************************************************************
**
Mar 24, 2016
Mar 24, 2016
3
** Copyright (C) 2013-2016 Jolla Ltd.
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
** Contact: Marko Mattila <marko.mattila@jollamobile.com>
** All rights reserved.
**
** This file is part of Nemo 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.
**
****************************************************************************************/
#include "transferengine.h"
#include "transferengine_p.h"
#include "transferplugininterface.h"
#include "mediaitem.h"
#include "dbmanager.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// ---------------------------
// 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)) {
qWarning() << Q_FUNC_INFO << "Could not find matching TransferId. This is probably an error!";
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
157
m_delayedExitTimer = new QTimer(this);
m_delayedExitTimer->setSingleShot(true);
m_delayedExitTimer->setInterval(5000);
Oct 1, 2014
Oct 1, 2014
158
m_delayedExitTimer->start(); // Exit if nothing happens within 5 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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
QSettings settings(CONFIG_PATH, QSettings::IniFormat);
if (settings.status() != QSettings::NoError) {
qWarning() << Q_FUNC_INFO << "Failed to read settings!" << settings.status();
} 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 30, 2014
Sep 30, 2014
209
210
211
212
qDebug() << "Scheduling exit in" << m_delayedExitTimer->interval() << "ms";
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
219
220
221
222
223
if (getenv("TRANSFER_ENGINE_KEEP_RUNNING")) {
qDebug() << "Keeping transfer engine running";
} else {
qDebug() << "Stopping transfer engine";
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
234
235
236
237
if (m_infoObjects.count() > 0) {
qWarning() << Q_FUNC_INFO << "Already quering account info" << m_infoObjects.count();
return;
}
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// First clear old data
m_enabledPlugins.clear();
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) {
qWarning() << Q_FUNC_INFO << "NULL Info object!";
continue;
}
Nov 22, 2013
Nov 22, 2013
267
268
269
270
if (info->ready()) {
if (info->info().count() > 0) {
m_enabledPlugins << info->info();
}
Sep 22, 2014
Sep 22, 2014
271
delete info;
Nov 22, 2013
Nov 22, 2013
272
273
274
275
276
277
278
} 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();
}
279
280
281
282
283
284
}
if (!interface) {
qWarning() << Q_FUNC_INFO << loader.errorString();
}
}
Jul 1, 2013
Jul 1, 2013
285
}
Jul 1, 2013
Jul 1, 2013
287
288
289
290
291
292
293
294
295
296
297
298
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);
}
}
299
300
301
302
303
304
305
}
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
306
Q_Q(TransferEngine);
307
308
309
Q_FOREACH(TransferDBRecord record, records) {
if (record.status == TransferEngineData::TransferStarted ||
record.status == TransferEngineData::NotStarted) {
Jul 1, 2013
Jul 1, 2013
310
311
if (DbManager::instance()->updateTransferStatus(record.transfer_id, TransferEngineData::TransferInterrupted)) {
emit q->statusChanged(record.transfer_id, TransferEngineData::TransferInterrupted);
Mar 24, 2016
Mar 24, 2016
312
313
314
if (record.status == TransferEngineData::TransferStarted) {
emit q_ptr->activeTransfersChanged(); // It's not active anymore
}
Jul 1, 2013
Jul 1, 2013
315
}
316
317
318
319
320
321
}
}
}
void TransferEnginePrivate::sendNotification(TransferEngineData::TransferType type,
TransferEngineData::TransferStatus status,
Jan 17, 2018
Jan 17, 2018
322
323
324
qreal progress,
const QString &fileName,
int transferId)
Oct 2, 2013
Oct 2, 2013
326
if (!m_notificationsEnabled || fileName.isEmpty()) {
Mar 19, 2015
Mar 19, 2015
330
331
332
333
334
QString category;
QString body;
QString summary;
QString previewBody;
QString previewSummary;
Jan 17, 2018
Jan 17, 2018
335
336
337
338
bool useProgress = false;
Notification::Urgency urgency = Notification::Normal;
QString appIcon = QStringLiteral("icon-lock-information");
QString icon = QStringLiteral("x-nemo-icon=icon-lock-transfer");
Mar 19, 2015
Mar 19, 2015
340
341
342
// 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
343
Mar 19, 2015
Mar 19, 2015
344
345
346
347
348
349
350
351
352
353
354
// 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
355
int notificationId = DbManager::instance()->notificationId(transferId);
Apr 18, 2013
Apr 18, 2013
356
357
358
359
360
361
if (status == TransferEngineData::TransferFinished) {
switch(type) {
case TransferEngineData::Upload:
//: Notification for successful file upload
//% "File uploaded"
Mar 19, 2015
Mar 19, 2015
362
363
364
previewBody = qtTrId("transferengine-no-file-upload-success");
previewSummary = fileName;
category = TRANSFER_EVENT_CATEGORY; // Use "generic" transfer event for uploads
365
366
break;
case TransferEngineData::Download:
Mar 19, 2015
Mar 19, 2015
367
category = TRANSFER_COMPLETE_EVENT_CATEGORY;
368
369
//: Notification for successful file download
//% "File downloaded"
Mar 19, 2015
Mar 19, 2015
370
371
body = qtTrId("transferengine-no-file-download-success");
summary = fileName;
Jun 3, 2015
Jun 3, 2015
372
373
previewBody = body;
previewSummary = summary;
374
375
376
break;
case TransferEngineData::Sync:
// Ok exit
Mar 19, 2015
Mar 19, 2015
377
break;
378
379
default:
qWarning() << "TransferEnginePrivate::sendNotification: unknown state";
Mar 19, 2015
Mar 19, 2015
380
break;
Jan 17, 2018
Jan 17, 2018
382
Jan 17, 2018
Jan 17, 2018
383
} else if (status == TransferEngineData::TransferInterrupted) {
Jan 17, 2018
Jan 17, 2018
384
385
386
urgency = Notification::Critical;
appIcon = QStringLiteral("icon-lock-information");
icon.clear();
Apr 18, 2013
Apr 18, 2013
387
388
389
390
switch (type) {
case TransferEngineData::Upload:
//: Notification for failed file upload
May 20, 2015
May 20, 2015
391
392
//% "Upload failed"
body = qtTrId("transferengine-la-file_upload_failed");
393
394
break;
case TransferEngineData::Download:
Apr 18, 2013
Apr 18, 2013
395
//: Notification for failed file download
May 20, 2015
May 20, 2015
396
397
//% "Download failed"
body = qtTrId("transferengine-la-file_download_failed");
398
399
break;
case TransferEngineData::Sync:
May 20, 2015
May 20, 2015
400
401
// no notification required
category.clear();
402
403
404
break;
default:
qWarning() << "TransferEnginePrivate::sendNotification: unknown state";
Mar 19, 2015
Mar 19, 2015
405
406
category.clear();
break;
Mar 19, 2015
Mar 19, 2015
409
410
411
summary = fileName;
previewSummary = summary;
previewBody = body;
Jan 17, 2018
Jan 17, 2018
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
} 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
432
433
434
435
436
437
438
439
440
} 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
441
}
Mar 19, 2015
Mar 19, 2015
443
444
if (!category.isEmpty()) {
Notification notification;
Jan 17, 2018
Jan 17, 2018
446
447
448
if (notificationId > 0) {
notification.setReplacesId(notificationId);
}
Oct 2, 2013
Oct 2, 2013
449
Jan 17, 2018
Jan 17, 2018
450
451
if (!m_defaultActions.isEmpty()) {
notification.setRemoteActions(m_defaultActions);
May 4, 2015
May 4, 2015
454
455
456
457
458
459
460
//: 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
461
462
463
464
465
466
467
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
468
Jan 17, 2018
Jan 17, 2018
469
470
471
472
473
474
475
476
477
478
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);
}
}
479
480
481
482
483
484
485
486
}
int TransferEnginePrivate::uploadMediaItem(MediaItem *mediaItem,
MediaTransferInterface *muif,
const QVariantMap &userData)
{
Q_Q(TransferEngine);
Jan 17, 2018
Jan 17, 2018
487
if (mediaItem == 0) {
488
489
490
491
492
493
494
495
496
497
498
499
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
qWarning() << "TransferEngine::uploadMediaItem invalid MediaItem";
return -1;
}
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItem Failed to get MediaTransferInterface";
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) {
qWarning() << "TransferEngine::uploadMediaItem: Failed to create an entry to transfer database!";
delete muif;
return key;
}
Jul 1, 2013
Jul 1, 2013
532
m_activityMonitor->newActivity(key);
533
534
535
536
537
538
539
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
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
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;
}
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) {
qWarning() << "TransferEngine::loadPlugin: " + loader.errorString();
}
if (loader.isLoaded()) {
loader.unload();
}
}
return 0;
}
Oct 2, 2013
Oct 2, 2013
588
589
590
591
592
593
594
595
596
597
598
599
600
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();
}
601
602
603
604
605
606
607
608
609
610
611
612
613
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
614
m_activityMonitor->newActivity(key);
615
616
617
618
619
620
621
622
623
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
624
sendNotification(type, tStatus, muif->progress(), mediaFileOrResourceName(muif->mediaItem()), key);
625
626
627
628
629
630
631
ok = DbManager::instance()->updateTransferStatus(key, tStatus);
if (m_plugins.remove(muif) == 0) {
qWarning() << "TransferEnginePrivate::uploadItemStatusChanged: Failed to remove media upload object from the map!";
// What to do here.. Let's just delete it..
}
muif->deleteLater();
muif = 0;
Jul 1, 2013
Jul 1, 2013
632
m_activityMonitor->activityFinished(key);
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
} break;
default:
qWarning() << "TransferEnginePrivate::uploadItemStatusChanged: unhandled status: " << tStatus;
break;
}
if (!ok) {
qWarning() << "TransferEnginePrivate::uploadItemStatusChanged: Failed update share status for the item: " + key;
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)) {
qWarning() << "TransferEnginePrivate::updateProgress: Failed to update progress";
return;
}
Jul 1, 2013
Jul 1, 2013
659
m_activityMonitor->newActivity(key);
660
Q_Q(TransferEngine);
Jan 17, 2018
Jan 17, 2018
661
q->updateTransferProgress(key, progress);
662
663
664
665
666
667
}
void TransferEnginePrivate::pluginInfoReady()
{
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
Nov 22, 2013
Nov 22, 2013
668
669
670
671
672
673
674
675
676
677
678
QList<TransferMethodInfo> infoList = infoObj->info();
if (!infoList.isEmpty()) {
m_enabledPlugins << infoList;
}
if (m_infoObjects.removeOne(infoObj)) {
delete infoObj;
} else {
qWarning() << Q_FUNC_INFO << "Failed to remove info object!";
delete infoObj;
}
679
680
681
682
683
684
685
686
687
688
689
690
if (m_infoObjects.isEmpty()) {
Q_Q(TransferEngine);
emit q->transferMethodListChanged();
}
}
void TransferEnginePrivate::pluginInfoError(const QString &msg)
{
qWarning() << "TransferEnginePrivate::pluginInfoError:" << msg;
TransferPluginInfo *infoObj = qobject_cast<TransferPluginInfo*>(sender());
m_infoObjects.removeOne(infoObj);
May 5, 2014
May 5, 2014
691
infoObj->deleteLater();
692
693
694
695
696
697
698
699
700
701
702
703
704
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
732
733
734
735
736
737
738
739
740
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) {
qWarning() << "TransferEnginePrivate:callbackCall: Invalid callback interface";
return;
}
QDBusInterface remoteInterface(callback.at(Service),
callback.at(Path),
callback.at(Interface));
if (!remoteInterface.isValid()) {
qWarning() << "TransferEnginePrivate::callbackCall: DBus interface is not valid!";
return;
}
if (method >= callback.size()) {
qWarning() << "TransferEnginePrivate::callbackCall: method index out of range!";
return;
}
const QString methodName = callback.at(method);
if (methodName.isEmpty()) {
qWarning() << "TransferEnginePrivate::callbackCall: Failed to get callback method name!";
return;
Jul 1, 2013
Jul 1, 2013
741
742
}
remoteInterface.call(methodName, transferId);
743
744
745
746
747
748
749
750
751
752
753
}
/*!
\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
754
755
756
\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
757
758
759
760
761
762
763
764
765
766
767
768
769
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
\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
810
TransferMethodInfoDeprecated::registerType();
811
812
813
814
815
816
817
818
TransferMethodInfo::registerType();
TransferDBRecord::registerType();
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
819
820
821
822
if (!connection.registerService("org.nemo.transferengine")) {
qFatal("DBUS service already taken. Kill the other instance first.");
}
Jul 1, 2013
Jul 1, 2013
823
new TransferEngineAdaptor(this);
824
825
826
827
828
829
// 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
830
d->enabledPluginsCheck();
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
}
/*!
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")) {
qWarning() << "Failed to unregister org.nemo.tranferengine service";
}
}
/*!
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
864
865
866
867
\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.
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
\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
883
884
d->exitSafely();
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
MediaTransferInterface *muif = d->loadPlugin(serviceId);
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItem Failed to get MediaTransferInterface";
return -1;
}
QUrl filePath(source);
QFileInfo fileInfo(filePath.toLocalFile());
if (!fileInfo.exists()) {
qWarning() << "TransferEnginePrivate::uploadMediaItem file " << source << " doesn't exist!";
}
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
925
926
d->exitSafely();
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
MediaTransferInterface *muif = d->loadPlugin(serviceId);
if (muif == 0) {
qWarning() << "TransferEngine::uploadMediaItemContent Failed to get MediaTransferInterface";
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
949
950
951
952
953
954
955
956
957
\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
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
\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);
mediaItem->setValue(MediaItem::ApplicationIcon, applicationIcon);
mediaItem->setValue(MediaItem::ServiceIcon, serviceIcon);
mediaItem->setValue(MediaItem::Callback, callback);
mediaItem->setValue(MediaItem::CancelCBMethod, cancelMethod);
mediaItem->setValue(MediaItem::RestartCBMethod, restartMethod);
mediaItem->setValue(MediaItem::CancelSupported, !cancelMethod.isEmpty());
mediaItem->setValue(MediaItem::RestartSupported,!restartMethod.isEmpty());
const int key = DbManager::instance()->createTransferEntry(mediaItem);
Jul 1, 2013
Jul 1, 2013
996
d->m_activityMonitor->newActivity(key);
997
998
d->m_keyTypeCache.insert(key, TransferEngineData::Download);
emit transfersChanged();
Jul 1, 2013
Jul 1, 2013
999
emit statusChanged(key, TransferEngineData::NotStarted);