1 /*
2 * Copyright (C) 2010, 2011 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19
20 #include "handle-outgoing-file-transfer-channel-job.h"
21 #include "telepathy-base-job_p.h"
22 #include "ktp-fth-debug.h"
23
24 #include <QTimer>
25 #include <QDebug>
26 #include <QUrl>
27
28 #include <KLocalizedString>
29 #include <kio/global.h>
30 #include <kjobtrackerinterface.h>
31
32 #include <TelepathyQt/OutgoingFileTransferChannel>
33 #include <TelepathyQt/PendingReady>
34 #include <TelepathyQt/PendingOperation>
35 #include <TelepathyQt/Contact>
36
37 class HandleOutgoingFileTransferChannelJobPrivate : public KTp::TelepathyBaseJobPrivate
38 {
39 Q_DECLARE_PUBLIC(HandleOutgoingFileTransferChannelJob)
40
41 public:
42 HandleOutgoingFileTransferChannelJobPrivate();
43 virtual ~HandleOutgoingFileTransferChannelJobPrivate();
44
45 Tp::OutgoingFileTransferChannelPtr channel;
46 QFile* file;
47 QUrl uri;
48 qulonglong offset;
49
50 void init();
51 bool kill();
52 void provideFile();
53
54 void __k__start();
55 void __k__onInitialOffsetDefined(qulonglong offset);
56 void __k__onFileTransferChannelStateChanged(Tp::FileTransferState state, Tp::FileTransferStateChangeReason reason);
57 void __k__onFileTransferChannelTransferredBytesChanged(qulonglong count);
58 void __k__onProvideFileFinished(Tp::PendingOperation* op);
59 void __k__onCancelOperationFinished(Tp::PendingOperation* op);
60 void __k__onInvalidated();
61 };
62
HandleOutgoingFileTransferChannelJob(Tp::OutgoingFileTransferChannelPtr channel,QObject * parent)63 HandleOutgoingFileTransferChannelJob::HandleOutgoingFileTransferChannelJob(Tp::OutgoingFileTransferChannelPtr channel,
64 QObject* parent)
65 : TelepathyBaseJob(*new HandleOutgoingFileTransferChannelJobPrivate(), parent)
66 {
67 qCDebug(KTP_FTH_MODULE);
68 Q_D(HandleOutgoingFileTransferChannelJob);
69
70 d->channel = channel;
71 d->init();
72 }
73
~HandleOutgoingFileTransferChannelJob()74 HandleOutgoingFileTransferChannelJob::~HandleOutgoingFileTransferChannelJob()
75 {
76 KIO::getJobTracker()->unregisterJob(this);
77 qCDebug(KTP_FTH_MODULE);
78 }
79
start()80 void HandleOutgoingFileTransferChannelJob::start()
81 {
82 qCDebug(KTP_FTH_MODULE);
83 KIO::getJobTracker()->registerJob(this);
84 // KWidgetJobTracker has an internal timer of 500 ms, if we don't wait here
85 // when the job description is emitted it won't be ready
86 QTimer::singleShot(500, this, SLOT(__k__start()));
87 }
88
doKill()89 bool HandleOutgoingFileTransferChannelJob::doKill()
90 {
91 qCDebug(KTP_FTH_MODULE) << "Outgoing file transfer killed.";
92 Q_D(HandleOutgoingFileTransferChannelJob);
93 return d->kill();
94 }
95
HandleOutgoingFileTransferChannelJobPrivate()96 HandleOutgoingFileTransferChannelJobPrivate::HandleOutgoingFileTransferChannelJobPrivate()
97 : file(0),
98 offset(0)
99 {
100 qCDebug(KTP_FTH_MODULE);
101 }
102
~HandleOutgoingFileTransferChannelJobPrivate()103 HandleOutgoingFileTransferChannelJobPrivate::~HandleOutgoingFileTransferChannelJobPrivate()
104 {
105 qCDebug(KTP_FTH_MODULE);
106 }
107
init()108 void HandleOutgoingFileTransferChannelJobPrivate::init()
109 {
110 qCDebug(KTP_FTH_MODULE);
111 Q_Q(HandleOutgoingFileTransferChannelJob);
112
113 if (channel.isNull()) {
114 qCritical() << "Channel cannot be NULL";
115 q->setError(KTp::NullChannel);
116 q->setErrorText(i18n("Invalid channel"));
117 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
118 return;
119 }
120
121 Tp::Features features = Tp::Features() << Tp::FileTransferChannel::FeatureCore;
122 if (!channel->isReady(Tp::Features() << Tp::FileTransferChannel::FeatureCore)) {
123 qCritical() << "Channel must be ready with Tp::FileTransferChannel::FeatureCore";
124 q->setError(KTp::FeatureNotReady);
125 q->setErrorText(i18n("Channel is not ready"));
126 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
127 return;
128 }
129
130 uri = QUrl(channel->uri());
131 if (uri.isEmpty()) {
132 qCWarning(KTP_FTH_MODULE) << "URI property missing";
133 q->setError(KTp::UriPropertyMissing);
134 q->setErrorText(i18n("URI property is missing"));
135 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
136 return;
137 }
138 if (!uri.isLocalFile()) {
139 // TODO handle this!
140 qCWarning(KTP_FTH_MODULE) << "Not a local file";
141 q->setError(KTp::NotALocalFile);
142 q->setErrorText(i18n("This is not a local file"));
143 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
144 return;
145 }
146
147 q->setCapabilities(KJob::Killable);
148 q->setTotalAmount(KJob::Bytes, channel->size());
149 q->setProcessedAmountAndCalculateSpeed(0);
150
151 q->connect(channel.data(),
152 SIGNAL(invalidated(Tp::DBusProxy*,QString,QString)),
153 SLOT(__k__onInvalidated()));
154 q->connect(channel.data(),
155 SIGNAL(initialOffsetDefined(qulonglong)),
156 SLOT(__k__onInitialOffsetDefined(qulonglong)));
157 q->connect(channel.data(),
158 SIGNAL(stateChanged(Tp::FileTransferState,Tp::FileTransferStateChangeReason)),
159 SLOT(__k__onFileTransferChannelStateChanged(Tp::FileTransferState,Tp::FileTransferStateChangeReason)));
160 q->connect(channel.data(),
161 SIGNAL(transferredBytesChanged(qulonglong)),
162 SLOT(__k__onFileTransferChannelTransferredBytesChanged(qulonglong)));
163 }
164
__k__start()165 void HandleOutgoingFileTransferChannelJobPrivate::__k__start()
166 {
167 qCDebug(KTP_FTH_MODULE);
168 Q_Q(HandleOutgoingFileTransferChannelJob);
169
170 Q_ASSERT(!q->error());
171 if (q->error()) {
172 qCWarning(KTP_FTH_MODULE) << "Job was started in error state. Something wrong happened." << q->errorString();
173 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
174 return;
175 }
176
177 Q_EMIT q->description(q, i18n("Outgoing file transfer"),
178 qMakePair<QString, QString>(i18n("To"), channel->targetContact()->alias()),
179 qMakePair<QString, QString>(i18n("Filename"), channel->uri()));
180
181 if (channel->state() == Tp::FileTransferStateAccepted) {
182 provideFile();
183 }
184 }
185
kill()186 bool HandleOutgoingFileTransferChannelJobPrivate::kill()
187 {
188 qCDebug(KTP_FTH_MODULE);
189 Q_Q(HandleOutgoingFileTransferChannelJob);
190
191 if (channel->state() != Tp::FileTransferStateCancelled) {
192 Tp::PendingOperation *cancelOperation = channel->cancel();
193 q->connect(cancelOperation,
194 SIGNAL(finished(Tp::PendingOperation*)),
195 SLOT(__k__onCancelOperationFinished(Tp::PendingOperation*)));
196 } else {
197 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
198 }
199
200 return true;
201 }
202
__k__onInitialOffsetDefined(qulonglong offset)203 void HandleOutgoingFileTransferChannelJobPrivate::__k__onInitialOffsetDefined(qulonglong offset)
204 {
205 qCDebug(KTP_FTH_MODULE);
206 Q_Q(HandleOutgoingFileTransferChannelJob);
207
208 this->offset = offset;
209 q->setProcessedAmountAndCalculateSpeed(offset);
210 }
211
__k__onFileTransferChannelStateChanged(Tp::FileTransferState state,Tp::FileTransferStateChangeReason stateReason)212 void HandleOutgoingFileTransferChannelJobPrivate::__k__onFileTransferChannelStateChanged(Tp::FileTransferState state,
213 Tp::FileTransferStateChangeReason stateReason)
214 {
215 qCDebug(KTP_FTH_MODULE);
216 Q_Q(HandleOutgoingFileTransferChannelJob);
217
218 qCDebug(KTP_FTH_MODULE) << "Outgoing file transfer channel state changed to" << state << "with reason" << stateReason;
219
220 switch (state) {
221 case Tp::FileTransferStateNone:
222 // This is bad
223 qCWarning(KTP_FTH_MODULE) << "An unknown error occurred.";
224 q->setError(KTp::TelepathyErrorError);
225 q->setErrorText(i18n("An unknown error occurred"));
226 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
227 break;
228 case Tp::FileTransferStateCompleted:
229 qCDebug(KTP_FTH_MODULE) << "Outgoing file transfer completed";
230 Q_EMIT q->infoMessage(q, i18n("Outgoing file transfer")); // [Finished] is added automatically to the notification
231 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
232 break;
233 case Tp::FileTransferStateCancelled:
234 q->setError(KTp::FileTransferCancelled);
235 q->setErrorText(i18n("Outgoing file transfer was canceled."));
236 q->kill(KJob::Quietly);
237 break;
238 case Tp::FileTransferStateAccepted:
239 provideFile();
240 break;
241 case Tp::FileTransferStatePending:
242 case Tp::FileTransferStateOpen:
243 default:
244 break;
245 }
246 }
247
provideFile()248 void HandleOutgoingFileTransferChannelJobPrivate::provideFile()
249 {
250 qCDebug(KTP_FTH_MODULE);
251 Q_Q(HandleOutgoingFileTransferChannelJob);
252
253 file = new QFile(uri.toLocalFile(), q->parent());
254 qCDebug(KTP_FTH_MODULE) << "Providing file" << file->fileName();
255
256 Tp::PendingOperation* provideFileOperation = channel->provideFile(file);
257 q->connect(provideFileOperation,
258 SIGNAL(finished(Tp::PendingOperation*)),
259 SLOT(__k__onProvideFileFinished(Tp::PendingOperation*)));
260 }
261
__k__onFileTransferChannelTransferredBytesChanged(qulonglong count)262 void HandleOutgoingFileTransferChannelJobPrivate::__k__onFileTransferChannelTransferredBytesChanged(qulonglong count)
263 {
264 qCDebug(KTP_FTH_MODULE);
265 Q_Q(HandleOutgoingFileTransferChannelJob);
266
267 qCDebug(KTP_FTH_MODULE).nospace() << "Sending " << channel->fileName() << " - "
268 << "Transferred bytes = " << offset + count << " ("
269 << ((int)(((double)(offset + count) / channel->size()) * 100)) << "% done)";
270 q->setProcessedAmountAndCalculateSpeed(offset + count);
271 }
272
__k__onProvideFileFinished(Tp::PendingOperation * op)273 void HandleOutgoingFileTransferChannelJobPrivate::__k__onProvideFileFinished(Tp::PendingOperation* op)
274 {
275 // This method is called when the "provideFile" operation is finished,
276 // therefore the file was not sent yet.
277 qCDebug(KTP_FTH_MODULE);
278 Q_Q(HandleOutgoingFileTransferChannelJob);
279
280 if (op->isError()) {
281 qCWarning(KTP_FTH_MODULE) << "Unable to provide file - " << op->errorName() << ":" << op->errorMessage();
282 q->setError(KTp::ProvideFileError);
283 q->setErrorText(i18n("Cannot provide file"));
284 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
285 }
286 }
287
__k__onCancelOperationFinished(Tp::PendingOperation * op)288 void HandleOutgoingFileTransferChannelJobPrivate::__k__onCancelOperationFinished(Tp::PendingOperation* op)
289 {
290 qCDebug(KTP_FTH_MODULE);
291 Q_Q(HandleOutgoingFileTransferChannelJob);
292
293 if (op->isError()) {
294 qCWarning(KTP_FTH_MODULE) << "Unable to cancel file transfer - " << op->errorName() << ":" << op->errorMessage();
295 q->setError(KTp::CancelFileTransferError);
296 q->setErrorText(i18n("Cannot cancel outgoing file transfer"));
297 }
298
299 qCDebug(KTP_FTH_MODULE) << "File transfer cancelled";
300 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
301 }
302
__k__onInvalidated()303 void HandleOutgoingFileTransferChannelJobPrivate::__k__onInvalidated()
304 {
305 qCDebug(KTP_FTH_MODULE);
306 Q_Q(HandleOutgoingFileTransferChannelJob);
307
308 qCWarning(KTP_FTH_MODULE) << "File transfer invalidated!" << channel->invalidationMessage() << "reason" << channel->invalidationReason();
309 Q_EMIT q->infoMessage(q, i18n("File transfer invalidated. %1", channel->invalidationMessage()));
310
311 QTimer::singleShot(0, q, SLOT(__k__doEmitResult()));
312 }
313
314 #include "moc_handle-outgoing-file-transfer-channel-job.cpp"
315