1 //
2 // Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
3 //
4 // gadudcctransaction.cpp
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 // 02110-1301, USA.
20
21 #include "gadudcctransaction.h"
22
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27
28 #include <netinet/in.h>
29
30 #include <kdebug.h>
31 #include <klocale.h>
32 #include <kmessagebox.h>
33
34 #include "kopetetransfermanager.h"
35 #include "kopetemetacontact.h"
36 #include "kopeteuiglobal.h"
37
38 #include <qsocketnotifier.h>
39 #include <qfile.h>
40
41 #include "gaducontact.h"
42 #include "gaduaccount.h"
43 #include "gadudcc.h"
44
45 #include "libgadu.h"
46
GaduDCCTransaction(GaduDCC * parent)47 GaduDCCTransaction::GaduDCCTransaction(GaduDCC *parent)
48 : QObject(parent)
49 , gaduDCC_(parent)
50 {
51 read_ = NULL;
52 write_ = NULL;
53 contact = NULL;
54 transfer_ = NULL;
55 dccSock_ = NULL;
56 peer = 0;
57 }
58
~GaduDCCTransaction()59 GaduDCCTransaction::~GaduDCCTransaction()
60 {
61 closeDCC();
62 }
63
64 unsigned int
recvUIN()65 GaduDCCTransaction::recvUIN()
66 {
67 if (dccSock_) {
68 return dccSock_->uin;
69 }
70 return 0;
71 }
72
73 unsigned int
peerUIN()74 GaduDCCTransaction::peerUIN()
75 {
76 if (dccSock_) {
77 return dccSock_->peer_uin;
78 }
79 return 0;
80 }
81
82 bool
setupOutgoing(GaduContact * peerContact,QString & filePath)83 GaduDCCTransaction::setupOutgoing(GaduContact *peerContact, QString &filePath)
84 {
85 GaduContact *me;
86 GaduAccount *metoo;
87
88 if (!peerContact) {
89 return false;
90 }
91
92 me = static_cast<GaduContact *>(peerContact->account()->myself());
93
94 QString aaa = peerContact->contactIp().toString();
95 kDebug(14100) << "slotOutgoin for UIN: " << peerContact->uin() << " port "
96 << peerContact->contactPort() << " ip " <<aaa;
97 kDebug(14100) << "File path is " << filePath;
98
99 if (peerContact->contactPort() >= 10) {
100 dccSock_ = gg_dcc_send_file(htonl(
101 peerContact->contactIp().toIPv4Address()),
102 peerContact->contactPort(),
103 me->uin(), peerContact->uin());
104 gg_dcc_fill_file_info(dccSock_, filePath.toAscii());
105 transfer_ = Kopete::TransferManager::transferManager()->addTransfer(peerContact,
106 filePath,
107 dccSock_->file_info.size,
108 peerContact->metaContact()->displayName(),
109 Kopete::FileTransferInfo::Outgoing);
110 createNotifiers(true);
111 enableNotifiers(dccSock_->check);
112 } else {
113 kDebug(14100) << "Peer " << peerContact->uin()
114 << " is passive, requesting reverse connection";
115 metoo = static_cast<GaduAccount *>(me->account());
116 gaduDCC_->requests[peerContact->uin()] = filePath;
117 metoo->dccRequest(peerContact);
118 }
119
120 return false;
121 }
122
123 bool
setupIncoming(const unsigned int uin,GaduContact * peerContact)124 GaduDCCTransaction::setupIncoming(const unsigned int uin, GaduContact *peerContact)
125 {
126 if (!peerContact) {
127 kDebug(14100) << "setupIncoming called with peerContact == NULL ";
128 return false;
129 return false;
130 }
131
132 QString aaa = peerContact->contactIp().toString();
133 kDebug(14100) << "setupIncoming for UIN: " << uin << " port " << peerContact->contactPort()
134 << " ip " <<aaa;
135
136 peer = peerContact->uin();
137 dccSock_ = gg_dcc_get_file(htonl(
138 peerContact->contactIp().toIPv4Address()),
139 peerContact->contactPort(), uin, peer);
140
141 contact = peerContact;
142 return setupIncoming(dccSock_);
143 }
144
145 bool
setupIncoming(gg_dcc * dccS)146 GaduDCCTransaction::setupIncoming(gg_dcc *dccS)
147 {
148 if (!dccS) {
149 kDebug(14100) << "gg_dcc_get_file failed in GaduDCCTransaction::setupIncoming";
150 return false;
151 }
152
153 dccSock_ = dccS;
154
155 peer = dccS->uin;
156
157 connect(Kopete::TransferManager::transferManager(), SIGNAL(accepted(Kopete::Transfer *,
158 QString)),
159 this, SLOT(slotIncomingTransferAccepted(Kopete::Transfer*,QString)));
160 connect(Kopete::TransferManager::transferManager(), SIGNAL(refused(Kopete::FileTransferInfo)),
161 this, SLOT(slotTransferRefused(Kopete::FileTransferInfo)));
162
163 incoming = true;
164 createNotifiers(true);
165 enableNotifiers(dccSock_->check);
166
167 return true;
168 }
169
170 void
closeDCC()171 GaduDCCTransaction::closeDCC()
172 {
173 kDebug(14100) << "closeDCC()";
174
175 disableNotifiers();
176 destroyNotifiers();
177 gg_dcc_free(dccSock_);
178 dccSock_ = NULL;
179 }
180
181 void
destroyNotifiers()182 GaduDCCTransaction::destroyNotifiers()
183 {
184 disableNotifiers();
185 delete read_;
186 read_ = NULL;
187 delete write_;
188 write_ = NULL;
189 }
190
191 void
createNotifiers(bool connect)192 GaduDCCTransaction::createNotifiers(bool connect)
193 {
194 if (!dccSock_) {
195 return;
196 }
197
198 read_ = new QSocketNotifier(dccSock_->fd, QSocketNotifier::Read, this);
199 read_->setEnabled(false);
200
201 write_ = new QSocketNotifier(dccSock_->fd, QSocketNotifier::Write, this);
202 write_->setEnabled(false);
203
204 if (connect) {
205 QObject::connect(read_, SIGNAL(activated(int)), SLOT(watcher()));
206 QObject::connect(write_, SIGNAL(activated(int)), SLOT(watcher()));
207 }
208 }
209
210 void
enableNotifiers(int checkWhat)211 GaduDCCTransaction::enableNotifiers(int checkWhat)
212 {
213 if ((checkWhat & GG_CHECK_READ) && read_) {
214 read_->setEnabled(true);
215 }
216 if ((checkWhat & GG_CHECK_WRITE) && write_) {
217 write_->setEnabled(true);
218 }
219 }
220
221 void
disableNotifiers()222 GaduDCCTransaction::disableNotifiers()
223 {
224 if (read_) {
225 read_->setEnabled(false);
226 }
227 if (write_) {
228 write_->setEnabled(false);
229 }
230 }
231
232 void
slotIncomingTransferAccepted(Kopete::Transfer * transfer,const QString & fileName)233 GaduDCCTransaction::slotIncomingTransferAccepted(Kopete::Transfer *transfer, const QString &fileName)
234 {
235 if ((long)transfer->info().transferId() != transferId_) {
236 return;
237 }
238
239 transfer_ = transfer;
240 localFile_.setFileName(fileName);
241
242 if (localFile_.exists()) {
243 KGuiItem resumeButton(i18n("&Resume"));
244 KGuiItem overwriteButton(i18n("Over&write"));
245 switch (KMessageBox::questionYesNoCancel(Kopete::UI::Global::mainWidget(),
246 i18n(
247 "The file %1 already exists, do you want to resume or overwrite it?",
248 fileName),
249 i18n("File Exists: %1", fileName), resumeButton,
250 overwriteButton)) {
251 // resume
252 case KMessageBox::Yes:
253 if (localFile_.open(QIODevice::WriteOnly | QIODevice::Append)) {
254 dccSock_->offset = localFile_.size();
255 dccSock_->file_fd = localFile_.handle();
256 }
257 break;
258 // overwrite
259 case KMessageBox::No:
260 if (localFile_.open(QIODevice::ReadWrite)) {
261 dccSock_->offset = 0;
262 dccSock_->file_fd = localFile_.handle();
263 }
264 break;
265
266 // cancel
267 default:
268 closeDCC();
269 deleteLater();
270 return;
271 break;
272 }
273 if (localFile_.handle() < 1) {
274 closeDCC();
275 deleteLater();
276 return;
277 }
278 } else {
279 // overwrite by default
280 if (localFile_.open(QIODevice::ReadWrite) == false) {
281 transfer->slotError(KIO::ERR_COULD_NOT_WRITE, fileName);
282 closeDCC();
283 deleteLater();
284 return;
285 }
286 dccSock_->offset = 0;
287 dccSock_->file_fd = localFile_.handle();
288 }
289
290 connect(transfer, SIGNAL(result(KJob*)), this, SLOT(slotTransferResult()));
291
292 // reenable notifiers
293 enableNotifiers(dccSock_->check);
294 }
295
296 void
slotTransferResult()297 GaduDCCTransaction::slotTransferResult()
298 {
299 if (transfer_->error() == KIO::ERR_USER_CANCELED) {
300 closeDCC();
301 deleteLater();
302 }
303 }
304
305 void
slotTransferRefused(const Kopete::FileTransferInfo & transfer)306 GaduDCCTransaction::slotTransferRefused(const Kopete::FileTransferInfo &transfer)
307 {
308 if ((long)transfer.transferId() != transferId_) {
309 return;
310 }
311 closeDCC();
312 deleteLater();
313 }
314
315 void
askIncommingTransfer()316 GaduDCCTransaction::askIncommingTransfer()
317 {
318 transferId_ = Kopete::TransferManager::transferManager()->askIncomingTransfer(contact,
319 QString((const
320 char *)
321 dccSock_->
322 file_info.
323 filename), dccSock_->file_info.size);
324 }
325
326 void
watcher()327 GaduDCCTransaction::watcher()
328 {
329 gg_event *dccEvent;
330 GaduAccount *account;
331
332 disableNotifiers();
333
334 dccEvent = gg_dcc_watch_fd(dccSock_);
335 if (!dccEvent) {
336 // connection is fucked
337 closeDCC();
338 return;
339 }
340 switch (dccEvent->type) {
341 case GG_EVENT_DCC_CLIENT_ACCEPT:
342 kDebug(14100) << " GG_EVENT_DCC_CLIENT_ACCEPT ";
343 // check dccsock->peer_uin, if unknown, fuck it;
344
345 // is it for us ?
346 account = gaduDCC_->account(dccSock_->uin);
347 if (!account) {
348 kDebug(14100) << " this dcc transaction is for uin " << dccSock_->uin
349 << ", which is not quite for me... closing";
350 // unknown 'to' ?, we're off
351 gg_free_event(dccEvent);
352 closeDCC();
353 deleteLater();
354 return;
355 }
356
357 if (!peer) {
358 contact
359 = static_cast<GaduContact *>(account->contacts().value(QString::number(dccSock_->
360 peer_uin)));
361 } else {
362 contact = static_cast<GaduContact *>(account->contacts().value(QString::number(peer)));
363 }
364
365 if (contact == NULL) {
366 // refusing, contact on the list
367 kDebug(14100) << " dcc connection from " << dccSock_->peer_uin
368 << " refused, UIN not on the list ";
369 gg_free_event(dccEvent);
370 closeDCC();
371 // emit error
372 deleteLater();
373 return;
374 } else {
375 // ask user to accept that transfer
376 kDebug(14100) << " dcc accepted from " << dccSock_->peer_uin;
377 }
378
379 break;
380 case GG_EVENT_DCC_CALLBACK:
381 kDebug(14100) << "GG_DCC_EVENT_CALLBACK";
382 break;
383 case GG_EVENT_NONE:
384 kDebug(14100) << " GG_EVENT_NONE";
385 // update gui with progress
386 if (transfer_) {
387 transfer_->slotProcessed(dccSock_->offset);
388 }
389 break;
390
391 case GG_EVENT_DCC_NEED_FILE_ACK:
392 kDebug(14100) << " GG_EVENT_DCC_NEED_FILE_ACK ";
393 gg_free_event(dccEvent);
394 askIncommingTransfer();
395 return;
396 break;
397 case GG_EVENT_DCC_NEED_FILE_INFO:
398 if (gaduDCC_->requests.contains(dccSock_->peer_uin)) {
399 QString filePath = gaduDCC_->requests[dccSock_->peer_uin];
400 kDebug() << "Callback request found. Sending " << filePath;
401 gaduDCC_->requests.remove(dccSock_->peer_uin);
402 gg_dcc_fill_file_info(dccSock_, filePath.toAscii());
403 transfer_ = Kopete::TransferManager::transferManager()->addTransfer(contact,
404 filePath,
405 dccSock_->file_info.size,
406 contact->metaContact()->displayName(),
407 Kopete::FileTransferInfo::Outgoing);
408 } else {
409 gg_free_event(dccEvent);
410 closeDCC();
411 deleteLater();
412 return;
413 }
414 break;
415
416 case GG_EVENT_DCC_ERROR:
417 kDebug(14100) << " GG_EVENT_DCC_ERROR :" << dccEvent->event.dcc_error;
418 if (transfer_) {
419 switch (dccEvent->event.dcc_error) {
420 case GG_ERROR_DCC_REFUSED:
421 transfer_->slotError(KIO::ERR_SLAVE_DEFINED,
422 i18n(
423 "Connection to peer was refused; it possibly does not listen for incoming connections."));
424 break;
425
426 case GG_ERROR_DCC_EOF:
427 transfer_->slotError(KIO::ERR_SLAVE_DEFINED,
428 i18n("File transfer transaction was not agreed by peer."));
429 break;
430
431 case GG_ERROR_DCC_HANDSHAKE:
432 transfer_->slotError(KIO::ERR_SLAVE_DEFINED, i18n(
433 "File-transfer handshake failure."));
434 break;
435 case GG_ERROR_DCC_FILE:
436 transfer_->slotError(KIO::ERR_SLAVE_DEFINED,
437 i18n("File transfer had problems with the file."));
438 break;
439 case GG_ERROR_DCC_NET:
440 transfer_->slotError(KIO::ERR_SLAVE_DEFINED,
441 i18n("There was network error during file transfer."));
442 break;
443 default:
444 transfer_->slotError(KIO::ERR_SLAVE_DEFINED, i18n("Unknown File-Transfer error."));
445 break;
446 }
447 }
448 gg_free_event(dccEvent);
449 closeDCC();
450 deleteLater();
451 return;
452
453 case GG_EVENT_DCC_DONE:
454 if (transfer_) {
455 transfer_->slotComplete();
456 }
457 closeDCC();
458 deleteLater();
459 if (dccEvent) {
460 gg_free_event(dccEvent);
461 }
462 return;
463
464 default:
465 kDebug(14100) << "unknown/unhandled DCC EVENT: " << dccEvent->type;
466 break;
467 }
468
469 if (dccEvent) {
470 gg_free_event(dccEvent);
471 }
472
473 enableNotifiers(dccSock_->check);
474 }
475