QXmppTransferManager.cpp 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684
  1. /*
  2. * Copyright (C) 2008-2012 The QXmpp developers
  3. *
  4. * Author:
  5. * Jeremy Lainé
  6. *
  7. * Source:
  8. * http://code.google.com/p/qxmpp
  9. *
  10. * This file is a part of QXmpp library.
  11. *
  12. * This library is free software; you can redistribute it and/or
  13. * modify it under the terms of the GNU Lesser General Public
  14. * License as published by the Free Software Foundation; either
  15. * version 2.1 of the License, or (at your option) any later version.
  16. *
  17. * This library is distributed in the hope that it will be useful,
  18. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20. * Lesser General Public License for more details.
  21. *
  22. */
  23. #include <QCryptographicHash>
  24. #include <QDomElement>
  25. #include <QFile>
  26. #include <QFileInfo>
  27. #include <QHash>
  28. #include <QHostAddress>
  29. #include <QNetworkInterface>
  30. #include <QTime>
  31. #include <QTimer>
  32. #include <QUrl>
  33. #include "QXmppByteStreamIq.h"
  34. #include "QXmppClient.h"
  35. #include "QXmppConstants.h"
  36. #include "QXmppIbbIq.h"
  37. #include "QXmppSocks.h"
  38. #include "QXmppStreamInitiationIq_p.h"
  39. #include "QXmppStun.h"
  40. #include "QXmppTransferManager.h"
  41. #include "QXmppTransferManager_p.h"
  42. #include "QXmppUtils.h"
  43. // time to try to connect to a SOCKS host (7 seconds)
  44. const int socksTimeout = 7000;
  45. static QString streamHash(const QString &sid, const QString &initiatorJid, const QString &targetJid)
  46. {
  47. QCryptographicHash hash(QCryptographicHash::Sha1);
  48. QString str = sid + initiatorJid + targetJid;
  49. hash.addData(str.toAscii());
  50. return hash.result().toHex();
  51. }
  52. class QXmppTransferFileInfoPrivate : public QSharedData
  53. {
  54. public:
  55. QXmppTransferFileInfoPrivate();
  56. QDateTime date;
  57. QByteArray hash;
  58. QString name;
  59. QString description;
  60. qint64 size;
  61. };
  62. QXmppTransferFileInfoPrivate::QXmppTransferFileInfoPrivate()
  63. : size(0)
  64. {
  65. }
  66. QXmppTransferFileInfo::QXmppTransferFileInfo()
  67. : d(new QXmppTransferFileInfoPrivate)
  68. {
  69. }
  70. QXmppTransferFileInfo::QXmppTransferFileInfo(const QXmppTransferFileInfo &other)
  71. : d(other.d)
  72. {
  73. }
  74. QXmppTransferFileInfo::~QXmppTransferFileInfo()
  75. {
  76. }
  77. QDateTime QXmppTransferFileInfo::date() const
  78. {
  79. return d->date;
  80. }
  81. void QXmppTransferFileInfo::setDate(const QDateTime &date)
  82. {
  83. d->date = date;
  84. }
  85. QByteArray QXmppTransferFileInfo::hash() const
  86. {
  87. return d->hash;
  88. }
  89. void QXmppTransferFileInfo::setHash(const QByteArray &hash)
  90. {
  91. d->hash = hash;
  92. }
  93. QString QXmppTransferFileInfo::name() const
  94. {
  95. return d->name;
  96. }
  97. void QXmppTransferFileInfo::setName(const QString &name)
  98. {
  99. d->name = name;
  100. }
  101. QString QXmppTransferFileInfo::description() const
  102. {
  103. return d->description;
  104. }
  105. void QXmppTransferFileInfo::setDescription(const QString &description)
  106. {
  107. d->description = description;
  108. }
  109. qint64 QXmppTransferFileInfo::size() const
  110. {
  111. return d->size;
  112. }
  113. void QXmppTransferFileInfo::setSize(qint64 size)
  114. {
  115. d->size = size;
  116. }
  117. bool QXmppTransferFileInfo::isNull() const
  118. {
  119. return d->date.isNull()
  120. && d->description.isEmpty()
  121. && d->hash.isEmpty()
  122. && d->name.isEmpty()
  123. && d->size == 0;
  124. }
  125. QXmppTransferFileInfo& QXmppTransferFileInfo::operator=(const QXmppTransferFileInfo &other)
  126. {
  127. d = other.d;
  128. return *this;
  129. }
  130. bool QXmppTransferFileInfo::operator==(const QXmppTransferFileInfo &other) const
  131. {
  132. return other.d->size == d->size &&
  133. other.d->hash == d->hash &&
  134. other.d->name == d->name;
  135. }
  136. void QXmppTransferFileInfo::parse(const QDomElement &element)
  137. {
  138. d->date = QXmppUtils::datetimeFromString(element.attribute("date"));
  139. d->hash = QByteArray::fromHex(element.attribute("hash").toAscii());
  140. d->name = element.attribute("name");
  141. d->size = element.attribute("size").toLongLong();
  142. d->description = element.firstChildElement("desc").text();
  143. }
  144. void QXmppTransferFileInfo::toXml(QXmlStreamWriter *writer) const
  145. {
  146. writer->writeStartElement("file");
  147. writer->writeAttribute("xmlns", ns_stream_initiation_file_transfer);
  148. if (d->date.isValid())
  149. writer->writeAttribute("date", QXmppUtils::datetimeToString(d->date));
  150. if (!d->hash.isEmpty())
  151. writer->writeAttribute("hash", d->hash.toHex());
  152. if (!d->name.isEmpty())
  153. writer->writeAttribute("name", d->name);
  154. if (d->size > 0)
  155. writer->writeAttribute("size", QString::number(d->size));
  156. if (!d->description.isEmpty())
  157. writer->writeTextElement("desc", d->description);
  158. writer->writeEndElement();
  159. }
  160. class QXmppTransferJobPrivate
  161. {
  162. public:
  163. QXmppTransferJobPrivate();
  164. int blockSize;
  165. QXmppClient *client;
  166. QXmppTransferJob::Direction direction;
  167. qint64 done;
  168. QXmppTransferJob::Error error;
  169. QCryptographicHash hash;
  170. QIODevice *iodevice;
  171. QString offerId;
  172. QString jid;
  173. QUrl localFileUrl;
  174. QString sid;
  175. QXmppTransferJob::Method method;
  176. QString mimeType;
  177. QString requestId;
  178. QXmppTransferJob::State state;
  179. QTime transferStart;
  180. // file meta-data
  181. QXmppTransferFileInfo fileInfo;
  182. // for in-band bytestreams
  183. int ibbSequence;
  184. // for socks5 bytestreams
  185. QTcpSocket *socksSocket;
  186. QXmppByteStreamIq::StreamHost socksProxy;
  187. };
  188. QXmppTransferJobPrivate::QXmppTransferJobPrivate()
  189. : blockSize(16384),
  190. client(0),
  191. done(0),
  192. error(QXmppTransferJob::NoError),
  193. hash(QCryptographicHash::Md5),
  194. iodevice(0),
  195. method(QXmppTransferJob::NoMethod),
  196. state(QXmppTransferJob::OfferState),
  197. ibbSequence(0),
  198. socksSocket(0)
  199. {
  200. }
  201. QXmppTransferJob::QXmppTransferJob(const QString &jid, QXmppTransferJob::Direction direction, QXmppClient *client, QObject *parent)
  202. : QXmppLoggable(parent),
  203. d(new QXmppTransferJobPrivate)
  204. {
  205. d->client = client;
  206. d->direction = direction;
  207. d->jid = jid;
  208. }
  209. QXmppTransferJob::~QXmppTransferJob()
  210. {
  211. delete d;
  212. }
  213. /// Call this method if you wish to abort on ongoing transfer job.
  214. ///
  215. void QXmppTransferJob::abort()
  216. {
  217. terminate(AbortError);
  218. }
  219. /// Call this method if you wish to accept an incoming transfer job.
  220. ///
  221. void QXmppTransferJob::accept(const QString &filePath)
  222. {
  223. if (d->direction == IncomingDirection && d->state == OfferState && !d->iodevice)
  224. {
  225. QFile *file = new QFile(filePath, this);
  226. if (!file->open(QIODevice::WriteOnly))
  227. {
  228. warning(QString("Could not write to %1").arg(filePath));
  229. abort();
  230. return;
  231. }
  232. d->iodevice = file;
  233. setLocalFileUrl(QUrl::fromLocalFile(filePath));
  234. setState(QXmppTransferJob::StartState);
  235. }
  236. }
  237. /// Call this method if you wish to accept an incoming transfer job.
  238. ///
  239. void QXmppTransferJob::accept(QIODevice *iodevice)
  240. {
  241. if (d->direction == IncomingDirection && d->state == OfferState && !d->iodevice)
  242. {
  243. d->iodevice = iodevice;
  244. setState(QXmppTransferJob::StartState);
  245. }
  246. }
  247. /// Returns the job's transfer direction.
  248. ///
  249. QXmppTransferJob::Direction QXmppTransferJob::direction() const
  250. {
  251. return d->direction;
  252. }
  253. /// Returns the last error that was encountered.
  254. ///
  255. QXmppTransferJob::Error QXmppTransferJob::error() const
  256. {
  257. return d->error;
  258. }
  259. /// Returns the remote party's JID.
  260. ///
  261. QString QXmppTransferJob::jid() const
  262. {
  263. return d->jid;
  264. }
  265. /// Returns the local file URL.
  266. ///
  267. QUrl QXmppTransferJob::localFileUrl() const
  268. {
  269. return d->localFileUrl;
  270. }
  271. /// Sets the local file URL.
  272. ///
  273. /// \note You do not need to call this method if you called accept()
  274. /// with a file path.
  275. void QXmppTransferJob::setLocalFileUrl(const QUrl &localFileUrl)
  276. {
  277. if (localFileUrl != d->localFileUrl) {
  278. d->localFileUrl = localFileUrl;
  279. emit localFileUrlChanged(localFileUrl);
  280. }
  281. }
  282. /// Returns meta-data about the file being transferred.
  283. ///
  284. QXmppTransferFileInfo QXmppTransferJob::fileInfo() const
  285. {
  286. return d->fileInfo;
  287. }
  288. /// \cond
  289. QDateTime QXmppTransferJob::fileDate() const
  290. {
  291. return d->fileInfo.date();
  292. }
  293. QByteArray QXmppTransferJob::fileHash() const
  294. {
  295. return d->fileInfo.hash();
  296. }
  297. QString QXmppTransferJob::fileName() const
  298. {
  299. return d->fileInfo.name();
  300. }
  301. qint64 QXmppTransferJob::fileSize() const
  302. {
  303. return d->fileInfo.size();
  304. }
  305. /// \endcond
  306. /// Returns the job's transfer method.
  307. ///
  308. QXmppTransferJob::Method QXmppTransferJob::method() const
  309. {
  310. return d->method;
  311. }
  312. /// Returns the job's session identifier.
  313. ///
  314. QString QXmppTransferJob::sid() const
  315. {
  316. return d->sid;
  317. }
  318. /// Returns the job's transfer speed in bytes per second.
  319. ///
  320. /// If the transfer has not started yet or is already finished, returns 0.
  321. ///
  322. qint64 QXmppTransferJob::speed() const
  323. {
  324. qint64 elapsed = d->transferStart.elapsed();
  325. if (d->state != QXmppTransferJob::TransferState || !elapsed)
  326. return 0;
  327. return (d->done * 1000.0) / elapsed;
  328. }
  329. /// Returns the job's state.
  330. ///
  331. QXmppTransferJob::State QXmppTransferJob::state() const
  332. {
  333. return d->state;
  334. }
  335. void QXmppTransferJob::setState(QXmppTransferJob::State state)
  336. {
  337. if (d->state != state)
  338. {
  339. d->state = state;
  340. if (d->state == QXmppTransferJob::TransferState)
  341. d->transferStart.start();
  342. emit stateChanged(d->state);
  343. }
  344. }
  345. void QXmppTransferJob::_q_terminated()
  346. {
  347. emit stateChanged(d->state);
  348. if (d->error != NoError)
  349. emit error(d->error);
  350. emit finished();
  351. }
  352. void QXmppTransferJob::terminate(QXmppTransferJob::Error cause)
  353. {
  354. if (d->state == FinishedState)
  355. return;
  356. // change state
  357. d->error = cause;
  358. d->state = FinishedState;
  359. // close IO device
  360. if (d->iodevice)
  361. d->iodevice->close();
  362. // close socket
  363. if (d->socksSocket)
  364. {
  365. d->socksSocket->flush();
  366. d->socksSocket->close();
  367. }
  368. // emit signals later
  369. QTimer::singleShot(0, this, SLOT(_q_terminated()));
  370. }
  371. /// \cond
  372. QXmppTransferIncomingJob::QXmppTransferIncomingJob(const QString& jid, QXmppClient* client, QObject* parent)
  373. : QXmppTransferJob(jid, IncomingDirection, client, parent)
  374. , m_candidateClient(0)
  375. , m_candidateTimer(0)
  376. {
  377. }
  378. void QXmppTransferIncomingJob::checkData()
  379. {
  380. if ((d->fileInfo.size() && d->done != d->fileInfo.size()) ||
  381. (!d->fileInfo.hash().isEmpty() && d->hash.result() != d->fileInfo.hash()))
  382. terminate(QXmppTransferJob::FileCorruptError);
  383. else
  384. terminate(QXmppTransferJob::NoError);
  385. }
  386. void QXmppTransferIncomingJob::connectToNextHost()
  387. {
  388. bool check;
  389. Q_UNUSED(check);
  390. if (m_streamCandidates.isEmpty()) {
  391. // could not connect to any stream host
  392. QXmppByteStreamIq response;
  393. response.setId(m_streamOfferId);
  394. response.setTo(m_streamOfferFrom);
  395. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
  396. error.setCode(404);
  397. response.setType(QXmppIq::Error);
  398. response.setError(error);
  399. d->client->sendPacket(response);
  400. terminate(QXmppTransferJob::ProtocolError);
  401. return;
  402. }
  403. // try next host
  404. m_candidateHost = m_streamCandidates.takeFirst();
  405. info(QString("Connecting to streamhost: %1 (%2 %3)").arg(
  406. m_candidateHost.jid(),
  407. m_candidateHost.host(),
  408. QString::number(m_candidateHost.port())));
  409. const QString hostName = streamHash(d->sid,
  410. d->jid,
  411. d->client->configuration().jid());
  412. // try to connect to stream host
  413. m_candidateClient = new QXmppSocksClient(m_candidateHost.host(), m_candidateHost.port(), this);
  414. m_candidateTimer = new QTimer(this);
  415. check = connect(m_candidateClient, SIGNAL(disconnected()),
  416. this, SLOT(_q_candidateDisconnected()));
  417. Q_ASSERT(check);
  418. check = connect(m_candidateClient, SIGNAL(ready()),
  419. this, SLOT(_q_candidateReady()));
  420. Q_ASSERT(check);
  421. check = connect(m_candidateTimer, SIGNAL(timeout()),
  422. this, SLOT(_q_candidateDisconnected()));
  423. Q_ASSERT(check);
  424. m_candidateTimer->setSingleShot(true);
  425. m_candidateTimer->start(socksTimeout);
  426. m_candidateClient->connectToHost(hostName, 0);
  427. }
  428. void QXmppTransferIncomingJob::connectToHosts(const QXmppByteStreamIq &iq)
  429. {
  430. bool check;
  431. Q_UNUSED(check);
  432. m_streamCandidates = iq.streamHosts();
  433. m_streamOfferId = iq.id();
  434. m_streamOfferFrom = iq.from();
  435. connectToNextHost();
  436. }
  437. bool QXmppTransferIncomingJob::writeData(const QByteArray &data)
  438. {
  439. const qint64 written = d->iodevice->write(data);
  440. if (written < 0)
  441. return false;
  442. d->done += written;
  443. if (!d->fileInfo.hash().isEmpty())
  444. d->hash.addData(data);
  445. progress(d->done, d->fileInfo.size());
  446. return true;
  447. }
  448. void QXmppTransferIncomingJob::_q_candidateReady()
  449. {
  450. bool check;
  451. Q_UNUSED(check);
  452. if (!m_candidateClient)
  453. return;
  454. info(QString("Connected to streamhost: %1 (%2 %3)").arg(
  455. m_candidateHost.jid(),
  456. m_candidateHost.host(),
  457. QString::number(m_candidateHost.port())));
  458. setState(QXmppTransferJob::TransferState);
  459. d->socksSocket = m_candidateClient;
  460. m_candidateClient = 0;
  461. m_candidateTimer->deleteLater();
  462. m_candidateTimer = 0;
  463. check = connect(d->socksSocket, SIGNAL(readyRead()),
  464. this, SLOT(_q_receiveData()));
  465. Q_ASSERT(check);
  466. check = connect(d->socksSocket, SIGNAL(disconnected()),
  467. this, SLOT(_q_disconnected()));
  468. Q_ASSERT(check);
  469. QXmppByteStreamIq ackIq;
  470. ackIq.setId(m_streamOfferId);
  471. ackIq.setTo(m_streamOfferFrom);
  472. ackIq.setType(QXmppIq::Result);
  473. ackIq.setSid(d->sid);
  474. ackIq.setStreamHostUsed(m_candidateHost.jid());
  475. d->client->sendPacket(ackIq);
  476. }
  477. void QXmppTransferIncomingJob::_q_candidateDisconnected()
  478. {
  479. if (!m_candidateClient)
  480. return;
  481. warning(QString("Failed to connect to streamhost: %1 (%2 %3)").arg(
  482. m_candidateHost.jid(),
  483. m_candidateHost.host(),
  484. QString::number(m_candidateHost.port())));
  485. m_candidateClient->deleteLater();
  486. m_candidateClient = 0;
  487. m_candidateTimer->deleteLater();
  488. m_candidateTimer = 0;
  489. // try next host
  490. connectToNextHost();
  491. }
  492. void QXmppTransferIncomingJob::_q_disconnected()
  493. {
  494. if (d->state == QXmppTransferJob::FinishedState)
  495. return;
  496. checkData();
  497. }
  498. void QXmppTransferIncomingJob::_q_receiveData()
  499. {
  500. if (d->state != QXmppTransferJob::TransferState)
  501. return;
  502. // receive data block
  503. if (d->direction == QXmppTransferJob::IncomingDirection)
  504. {
  505. writeData(d->socksSocket->readAll());
  506. // if we have received all the data, stop here
  507. if (fileSize() && d->done >= fileSize())
  508. checkData();
  509. }
  510. }
  511. QXmppTransferOutgoingJob::QXmppTransferOutgoingJob(const QString& jid, QXmppClient* client, QObject* parent)
  512. : QXmppTransferJob(jid, OutgoingDirection, client, parent)
  513. {
  514. }
  515. void QXmppTransferOutgoingJob::connectToProxy()
  516. {
  517. bool check;
  518. Q_UNUSED(check);
  519. info(QString("Connecting to proxy: %1 (%2 %3)").arg(
  520. d->socksProxy.jid(),
  521. d->socksProxy.host(),
  522. QString::number(d->socksProxy.port())));
  523. const QString hostName = streamHash(d->sid,
  524. d->client->configuration().jid(),
  525. d->jid);
  526. QXmppSocksClient *socksClient = new QXmppSocksClient(d->socksProxy.host(), d->socksProxy.port(), this);
  527. check = connect(socksClient, SIGNAL(disconnected()),
  528. this, SLOT(_q_disconnected()));
  529. Q_ASSERT(check);
  530. check = connect(socksClient, SIGNAL(ready()),
  531. this, SLOT(_q_proxyReady()));
  532. Q_ASSERT(check);
  533. d->socksSocket = socksClient;
  534. socksClient->connectToHost(hostName, 0);
  535. }
  536. void QXmppTransferOutgoingJob::startSending()
  537. {
  538. bool check;
  539. Q_UNUSED(check);
  540. setState(QXmppTransferJob::TransferState);
  541. check = connect(d->socksSocket, SIGNAL(bytesWritten(qint64)),
  542. this, SLOT(_q_sendData()));
  543. Q_ASSERT(check);
  544. check = connect(d->iodevice, SIGNAL(readyRead()),
  545. this, SLOT(_q_sendData()));
  546. Q_ASSERT(check);
  547. _q_sendData();
  548. }
  549. void QXmppTransferOutgoingJob::_q_disconnected()
  550. {
  551. if (d->state == QXmppTransferJob::FinishedState)
  552. return;
  553. if (fileSize() && d->done != fileSize())
  554. terminate(QXmppTransferJob::ProtocolError);
  555. else
  556. terminate(QXmppTransferJob::NoError);
  557. }
  558. void QXmppTransferOutgoingJob::_q_proxyReady()
  559. {
  560. // activate stream
  561. QXmppByteStreamIq streamIq;
  562. streamIq.setType(QXmppIq::Set);
  563. streamIq.setFrom(d->client->configuration().jid());
  564. streamIq.setTo(d->socksProxy.jid());
  565. streamIq.setSid(d->sid);
  566. streamIq.setActivate(d->jid);
  567. d->requestId = streamIq.id();
  568. d->client->sendPacket(streamIq);
  569. }
  570. void QXmppTransferOutgoingJob::_q_sendData()
  571. {
  572. if (d->state != QXmppTransferJob::TransferState)
  573. return;
  574. // don't saturate the outgoing socket
  575. if (d->socksSocket->bytesToWrite() > 2 * d->blockSize)
  576. return;
  577. // check whether we have written the whole file
  578. if (d->fileInfo.size() && d->done >= d->fileInfo.size())
  579. {
  580. if (!d->socksSocket->bytesToWrite())
  581. terminate(QXmppTransferJob::NoError);
  582. return;
  583. }
  584. char *buffer = new char[d->blockSize];
  585. qint64 length = d->iodevice->read(buffer, d->blockSize);
  586. if (length < 0)
  587. {
  588. delete [] buffer;
  589. terminate(QXmppTransferJob::FileAccessError);
  590. return;
  591. }
  592. if (length > 0)
  593. {
  594. d->socksSocket->write(buffer, length);
  595. delete [] buffer;
  596. d->done += length;
  597. emit progress(d->done, fileSize());
  598. }
  599. }
  600. /// \endcond
  601. class QXmppTransferManagerPrivate
  602. {
  603. public:
  604. QXmppTransferManagerPrivate(QXmppTransferManager *qq);
  605. QXmppTransferIncomingJob *getIncomingJobByRequestId(const QString &jid, const QString &id);
  606. QXmppTransferIncomingJob *getIncomingJobBySid(const QString &jid, const QString &sid);
  607. QXmppTransferOutgoingJob *getOutgoingJobByRequestId(const QString &jid, const QString &id);
  608. int ibbBlockSize;
  609. QList<QXmppTransferJob*> jobs;
  610. QString proxy;
  611. bool proxyOnly;
  612. QXmppSocksServer *socksServer;
  613. QXmppTransferJob::Methods supportedMethods;
  614. private:
  615. QXmppTransferJob *getJobByRequestId(QXmppTransferJob::Direction direction, const QString &jid, const QString &id);
  616. QXmppTransferManager *q;
  617. };
  618. QXmppTransferManagerPrivate::QXmppTransferManagerPrivate(QXmppTransferManager *qq)
  619. : ibbBlockSize(4096)
  620. , proxyOnly(false)
  621. , socksServer(0)
  622. , supportedMethods(QXmppTransferJob::AnyMethod)
  623. , q(qq)
  624. {
  625. }
  626. QXmppTransferJob* QXmppTransferManagerPrivate::getJobByRequestId(QXmppTransferJob::Direction direction, const QString &jid, const QString &id)
  627. {
  628. foreach (QXmppTransferJob *job, jobs)
  629. if (job->d->direction == direction &&
  630. job->d->jid == jid &&
  631. job->d->requestId == id)
  632. return job;
  633. return 0;
  634. }
  635. QXmppTransferIncomingJob *QXmppTransferManagerPrivate::getIncomingJobByRequestId(const QString &jid, const QString &id)
  636. {
  637. return static_cast<QXmppTransferIncomingJob*>(getJobByRequestId(QXmppTransferJob::IncomingDirection, jid, id));
  638. }
  639. QXmppTransferIncomingJob* QXmppTransferManagerPrivate::getIncomingJobBySid(const QString &jid, const QString &sid)
  640. {
  641. foreach (QXmppTransferJob *job, jobs)
  642. if (job->d->direction == QXmppTransferJob::IncomingDirection &&
  643. job->d->jid == jid &&
  644. job->d->sid == sid)
  645. return static_cast<QXmppTransferIncomingJob*>(job);
  646. return 0;
  647. }
  648. QXmppTransferOutgoingJob *QXmppTransferManagerPrivate::getOutgoingJobByRequestId(const QString &jid, const QString &id)
  649. {
  650. return static_cast<QXmppTransferOutgoingJob*>(getJobByRequestId(QXmppTransferJob::OutgoingDirection, jid, id));
  651. }
  652. /// Constructs a QXmppTransferManager to handle incoming and outgoing
  653. /// file transfers.
  654. QXmppTransferManager::QXmppTransferManager()
  655. {
  656. bool check;
  657. Q_UNUSED(check);
  658. d = new QXmppTransferManagerPrivate(this);
  659. // start SOCKS server
  660. d->socksServer = new QXmppSocksServer(this);
  661. check = connect(d->socksServer, SIGNAL(newConnection(QTcpSocket*,QString,quint16)),
  662. this, SLOT(_q_socksServerConnected(QTcpSocket*,QString,quint16)));
  663. Q_ASSERT(check);
  664. if (!d->socksServer->listen()) {
  665. qWarning("QXmppSocksServer could not start listening");
  666. }
  667. }
  668. QXmppTransferManager::~QXmppTransferManager()
  669. {
  670. delete d;
  671. }
  672. void QXmppTransferManager::byteStreamIqReceived(const QXmppByteStreamIq &iq)
  673. {
  674. // handle IQ from proxy
  675. foreach (QXmppTransferJob *job, d->jobs)
  676. {
  677. if (job->d->socksProxy.jid() == iq.from() && job->d->requestId == iq.id())
  678. {
  679. if (iq.type() == QXmppIq::Result && iq.streamHosts().size() > 0)
  680. {
  681. job->d->socksProxy = iq.streamHosts().first();
  682. socksServerSendOffer(job);
  683. return;
  684. }
  685. }
  686. }
  687. if (iq.type() == QXmppIq::Result)
  688. byteStreamResultReceived(iq);
  689. else if (iq.type() == QXmppIq::Set)
  690. byteStreamSetReceived(iq);
  691. }
  692. /// Handle a response to a bystream set, i.e. after we informed the remote party
  693. /// that we connected to a stream host.
  694. void QXmppTransferManager::byteStreamResponseReceived(const QXmppIq &iq)
  695. {
  696. QXmppTransferJob *job = d->getIncomingJobByRequestId(iq.from(), iq.id());
  697. if (!job ||
  698. job->method() != QXmppTransferJob::SocksMethod ||
  699. job->state() != QXmppTransferJob::StartState)
  700. return;
  701. if (iq.type() == QXmppIq::Error)
  702. job->terminate(QXmppTransferJob::ProtocolError);
  703. }
  704. /// Handle a bytestream result, i.e. after the remote party has connected to
  705. /// a stream host.
  706. void QXmppTransferManager::byteStreamResultReceived(const QXmppByteStreamIq &iq)
  707. {
  708. bool check;
  709. Q_UNUSED(check);
  710. QXmppTransferOutgoingJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id());
  711. if (!job ||
  712. job->method() != QXmppTransferJob::SocksMethod ||
  713. job->state() != QXmppTransferJob::StartState)
  714. return;
  715. // check the stream host
  716. if (iq.streamHostUsed() == job->d->socksProxy.jid())
  717. {
  718. job->connectToProxy();
  719. return;
  720. }
  721. // direction connection, start sending data
  722. if (!job->d->socksSocket)
  723. {
  724. warning("Client says they connected to our SOCKS server, but they did not");
  725. job->terminate(QXmppTransferJob::ProtocolError);
  726. return;
  727. }
  728. check = connect(job->d->socksSocket, SIGNAL(disconnected()),
  729. job, SLOT(_q_disconnected()));
  730. Q_ASSERT(check);
  731. job->startSending();
  732. }
  733. /// Handle a bytestream set, i.e. an invitation from the remote party to connect
  734. /// to a stream host.
  735. void QXmppTransferManager::byteStreamSetReceived(const QXmppByteStreamIq &iq)
  736. {
  737. bool check;
  738. Q_UNUSED(check);
  739. QXmppIq response;
  740. response.setId(iq.id());
  741. response.setTo(iq.from());
  742. QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
  743. if (!job ||
  744. job->method() != QXmppTransferJob::SocksMethod ||
  745. job->state() != QXmppTransferJob::StartState)
  746. {
  747. // the stream is unknown
  748. QXmppStanza::Error error(QXmppStanza::Error::Auth, QXmppStanza::Error::NotAcceptable);
  749. error.setCode(406);
  750. response.setType(QXmppIq::Error);
  751. response.setError(error);
  752. client()->sendPacket(response);
  753. return;
  754. }
  755. job->connectToHosts(iq);
  756. }
  757. /// \cond
  758. QStringList QXmppTransferManager::discoveryFeatures() const
  759. {
  760. return QStringList()
  761. << ns_ibb // XEP-0047: In-Band Bytestreams
  762. << ns_bytestreams // XEP-0065: SOCKS5 Bytestreams
  763. << ns_stream_initiation // XEP-0095: Stream Initiation
  764. << ns_stream_initiation_file_transfer; // XEP-0096: SI File Transfer
  765. }
  766. bool QXmppTransferManager::handleStanza(const QDomElement &element)
  767. {
  768. if (element.tagName() != "iq")
  769. return false;
  770. // XEP-0047 In-Band Bytestreams
  771. if(QXmppIbbCloseIq::isIbbCloseIq(element))
  772. {
  773. QXmppIbbCloseIq ibbCloseIq;
  774. ibbCloseIq.parse(element);
  775. ibbCloseIqReceived(ibbCloseIq);
  776. return true;
  777. }
  778. else if(QXmppIbbDataIq::isIbbDataIq(element))
  779. {
  780. QXmppIbbDataIq ibbDataIq;
  781. ibbDataIq.parse(element);
  782. ibbDataIqReceived(ibbDataIq);
  783. return true;
  784. }
  785. else if(QXmppIbbOpenIq::isIbbOpenIq(element))
  786. {
  787. QXmppIbbOpenIq ibbOpenIq;
  788. ibbOpenIq.parse(element);
  789. ibbOpenIqReceived(ibbOpenIq);
  790. return true;
  791. }
  792. // XEP-0065: SOCKS5 Bytestreams
  793. else if(QXmppByteStreamIq::isByteStreamIq(element))
  794. {
  795. QXmppByteStreamIq byteStreamIq;
  796. byteStreamIq.parse(element);
  797. byteStreamIqReceived(byteStreamIq);
  798. return true;
  799. }
  800. // XEP-0095: Stream Initiation
  801. else if(QXmppStreamInitiationIq::isStreamInitiationIq(element))
  802. {
  803. QXmppStreamInitiationIq siIq;
  804. siIq.parse(element);
  805. streamInitiationIqReceived(siIq);
  806. return true;
  807. }
  808. return false;
  809. }
  810. void QXmppTransferManager::setClient(QXmppClient *client)
  811. {
  812. bool check;
  813. Q_UNUSED(check);
  814. QXmppClientExtension::setClient(client);
  815. // XEP-0047: In-Band Bytestreams
  816. check = connect(client, SIGNAL(iqReceived(QXmppIq)),
  817. this, SLOT(_q_iqReceived(QXmppIq)));
  818. Q_ASSERT(check);
  819. }
  820. /// \endcond
  821. void QXmppTransferManager::ibbCloseIqReceived(const QXmppIbbCloseIq &iq)
  822. {
  823. QXmppIq response;
  824. response.setTo(iq.from());
  825. response.setId(iq.id());
  826. QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
  827. if (!job ||
  828. job->method() != QXmppTransferJob::InBandMethod)
  829. {
  830. // the job is unknown, cancel it
  831. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
  832. response.setType(QXmppIq::Error);
  833. response.setError(error);
  834. client()->sendPacket(response);
  835. return;
  836. }
  837. // acknowledge the packet
  838. response.setType(QXmppIq::Result);
  839. client()->sendPacket(response);
  840. // check received data
  841. job->checkData();
  842. }
  843. void QXmppTransferManager::ibbDataIqReceived(const QXmppIbbDataIq &iq)
  844. {
  845. QXmppIq response;
  846. response.setTo(iq.from());
  847. response.setId(iq.id());
  848. QXmppTransferIncomingJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
  849. if (!job ||
  850. job->method() != QXmppTransferJob::InBandMethod ||
  851. job->state() != QXmppTransferJob::TransferState)
  852. {
  853. // the job is unknown, cancel it
  854. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
  855. response.setType(QXmppIq::Error);
  856. response.setError(error);
  857. client()->sendPacket(response);
  858. return;
  859. }
  860. if (iq.sequence() != job->d->ibbSequence)
  861. {
  862. // the packet is out of sequence
  863. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::UnexpectedRequest);
  864. response.setType(QXmppIq::Error);
  865. response.setError(error);
  866. client()->sendPacket(response);
  867. return;
  868. }
  869. // write data
  870. job->writeData(iq.payload());
  871. job->d->ibbSequence++;
  872. // acknowledge the packet
  873. response.setType(QXmppIq::Result);
  874. client()->sendPacket(response);
  875. }
  876. void QXmppTransferManager::ibbOpenIqReceived(const QXmppIbbOpenIq &iq)
  877. {
  878. QXmppIq response;
  879. response.setTo(iq.from());
  880. response.setId(iq.id());
  881. QXmppTransferJob *job = d->getIncomingJobBySid(iq.from(), iq.sid());
  882. if (!job ||
  883. job->method() != QXmppTransferJob::InBandMethod)
  884. {
  885. // the job is unknown, cancel it
  886. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::ItemNotFound);
  887. response.setType(QXmppIq::Error);
  888. response.setError(error);
  889. client()->sendPacket(response);
  890. return;
  891. }
  892. if (iq.blockSize() > d->ibbBlockSize)
  893. {
  894. // we prefer a smaller block size
  895. QXmppStanza::Error error(QXmppStanza::Error::Modify, QXmppStanza::Error::ResourceConstraint);
  896. response.setType(QXmppIq::Error);
  897. response.setError(error);
  898. client()->sendPacket(response);
  899. return;
  900. }
  901. job->d->blockSize = iq.blockSize();
  902. job->setState(QXmppTransferJob::TransferState);
  903. // accept transfer
  904. response.setType(QXmppIq::Result);
  905. client()->sendPacket(response);
  906. }
  907. void QXmppTransferManager::ibbResponseReceived(const QXmppIq &iq)
  908. {
  909. QXmppTransferJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id());
  910. if (!job ||
  911. job->method() != QXmppTransferJob::InBandMethod ||
  912. job->state() == QXmppTransferJob::FinishedState)
  913. return;
  914. // if the IO device is closed, do nothing
  915. if (!job->d->iodevice->isOpen())
  916. return;
  917. if (iq.type() == QXmppIq::Result)
  918. {
  919. const QByteArray buffer = job->d->iodevice->read(job->d->blockSize);
  920. job->setState(QXmppTransferJob::TransferState);
  921. if (buffer.size())
  922. {
  923. // send next data block
  924. QXmppIbbDataIq dataIq;
  925. dataIq.setTo(job->d->jid);
  926. dataIq.setSid(job->d->sid);
  927. dataIq.setSequence(job->d->ibbSequence++);
  928. dataIq.setPayload(buffer);
  929. job->d->requestId = dataIq.id();
  930. client()->sendPacket(dataIq);
  931. job->d->done += buffer.size();
  932. job->progress(job->d->done, job->fileSize());
  933. } else {
  934. // close the bytestream
  935. QXmppIbbCloseIq closeIq;
  936. closeIq.setTo(job->d->jid);
  937. closeIq.setSid(job->d->sid);
  938. job->d->requestId = closeIq.id();
  939. client()->sendPacket(closeIq);
  940. job->terminate(QXmppTransferJob::NoError);
  941. }
  942. }
  943. else if (iq.type() == QXmppIq::Error)
  944. {
  945. // close the bytestream
  946. QXmppIbbCloseIq closeIq;
  947. closeIq.setTo(job->d->jid);
  948. closeIq.setSid(job->d->sid);
  949. job->d->requestId = closeIq.id();
  950. client()->sendPacket(closeIq);
  951. job->terminate(QXmppTransferJob::ProtocolError);
  952. }
  953. }
  954. void QXmppTransferManager::_q_iqReceived(const QXmppIq &iq)
  955. {
  956. bool check;
  957. Q_UNUSED(check);
  958. foreach (QXmppTransferJob *ptr, d->jobs)
  959. {
  960. // handle IQ from proxy
  961. if (ptr->direction() == QXmppTransferJob::OutgoingDirection && ptr->d->socksProxy.jid() == iq.from() && ptr->d->requestId == iq.id())
  962. {
  963. QXmppTransferOutgoingJob *job = static_cast<QXmppTransferOutgoingJob*>(ptr);
  964. if (job->d->socksSocket)
  965. {
  966. // proxy connection activation result
  967. if (iq.type() == QXmppIq::Result)
  968. {
  969. // proxy stream activated, start sending data
  970. job->startSending();
  971. } else if (iq.type() == QXmppIq::Error) {
  972. // proxy stream not activated, terminate
  973. warning("Could not activate SOCKS5 proxy bytestream");
  974. job->terminate(QXmppTransferJob::ProtocolError);
  975. }
  976. } else {
  977. // we could not get host/port from proxy, procede without a proxy
  978. if (iq.type() == QXmppIq::Error)
  979. socksServerSendOffer(job);
  980. }
  981. return;
  982. }
  983. // handle IQ from peer
  984. else if (ptr->d->jid == iq.from() && ptr->d->requestId == iq.id())
  985. {
  986. QXmppTransferJob *job = ptr;
  987. if (job->direction() == QXmppTransferJob::OutgoingDirection &&
  988. job->method() == QXmppTransferJob::InBandMethod)
  989. {
  990. ibbResponseReceived(iq);
  991. return;
  992. }
  993. else if (job->direction() == QXmppTransferJob::IncomingDirection &&
  994. job->method() == QXmppTransferJob::SocksMethod)
  995. {
  996. byteStreamResponseReceived(iq);
  997. return;
  998. }
  999. else if (job->direction() == QXmppTransferJob::OutgoingDirection &&
  1000. iq.type() == QXmppIq::Error)
  1001. {
  1002. // remote party cancelled stream initiation
  1003. job->terminate(QXmppTransferJob::AbortError);
  1004. return;
  1005. }
  1006. }
  1007. }
  1008. }
  1009. void QXmppTransferManager::_q_jobDestroyed(QObject *object)
  1010. {
  1011. d->jobs.removeAll(static_cast<QXmppTransferJob*>(object));
  1012. }
  1013. void QXmppTransferManager::_q_jobError(QXmppTransferJob::Error error)
  1014. {
  1015. QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
  1016. if (!job || !d->jobs.contains(job))
  1017. return;
  1018. if (job->direction() == QXmppTransferJob::OutgoingDirection &&
  1019. job->method() == QXmppTransferJob::InBandMethod &&
  1020. error == QXmppTransferJob::AbortError)
  1021. {
  1022. // close the bytestream
  1023. QXmppIbbCloseIq closeIq;
  1024. closeIq.setTo(job->d->jid);
  1025. closeIq.setSid(job->d->sid);
  1026. job->d->requestId = closeIq.id();
  1027. client()->sendPacket(closeIq);
  1028. }
  1029. }
  1030. void QXmppTransferManager::_q_jobFinished()
  1031. {
  1032. QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
  1033. if (!job || !d->jobs.contains(job))
  1034. return;
  1035. emit jobFinished(job);
  1036. }
  1037. void QXmppTransferManager::_q_jobStateChanged(QXmppTransferJob::State state)
  1038. {
  1039. bool check;
  1040. Q_UNUSED(check);
  1041. QXmppTransferJob *job = qobject_cast<QXmppTransferJob *>(sender());
  1042. if (!job || !d->jobs.contains(job))
  1043. return;
  1044. if (job->direction() != QXmppTransferJob::IncomingDirection)
  1045. return;
  1046. // disconnect from the signal
  1047. disconnect(job, SIGNAL(stateChanged(QXmppTransferJob::State)),
  1048. this, SLOT(_q_jobStateChanged(QXmppTransferJob::State)));
  1049. // the job was refused by the local party
  1050. if (state != QXmppTransferJob::StartState || !job->d->iodevice || !job->d->iodevice->isWritable())
  1051. {
  1052. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden);
  1053. error.setCode(403);
  1054. QXmppIq response;
  1055. response.setTo(job->jid());
  1056. response.setId(job->d->offerId);
  1057. response.setType(QXmppIq::Error);
  1058. response.setError(error);
  1059. client()->sendPacket(response);
  1060. job->terminate(QXmppTransferJob::AbortError);
  1061. return;
  1062. }
  1063. // the job was accepted by the local party
  1064. check = connect(job, SIGNAL(error(QXmppTransferJob::Error)),
  1065. this, SLOT(_q_jobError(QXmppTransferJob::Error)));
  1066. Q_ASSERT(check);
  1067. QXmppDataForm form;
  1068. form.setType(QXmppDataForm::Submit);
  1069. QXmppDataForm::Field methodField(QXmppDataForm::Field::ListSingleField);
  1070. methodField.setKey("stream-method");
  1071. if (job->method() == QXmppTransferJob::InBandMethod)
  1072. methodField.setValue(ns_ibb);
  1073. else if (job->method() == QXmppTransferJob::SocksMethod)
  1074. methodField.setValue(ns_bytestreams);
  1075. form.setFields(QList<QXmppDataForm::Field>() << methodField);
  1076. QXmppStreamInitiationIq response;
  1077. response.setTo(job->jid());
  1078. response.setId(job->d->offerId);
  1079. response.setType(QXmppIq::Result);
  1080. response.setProfile(QXmppStreamInitiationIq::FileTransfer);
  1081. response.setFeatureForm(form);
  1082. client()->sendPacket(response);
  1083. // notify user
  1084. emit jobStarted(job);
  1085. }
  1086. /// Send file to a remote party.
  1087. ///
  1088. /// The remote party will be given the choice to accept or refuse the transfer.
  1089. ///
  1090. QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, const QString &filePath, const QString &description)
  1091. {
  1092. if (jid.isEmpty()) {
  1093. warning("Refusing to send file to an empty jid");
  1094. return 0;
  1095. }
  1096. QFileInfo info(filePath);
  1097. QXmppTransferFileInfo fileInfo;
  1098. fileInfo.setDate(info.lastModified());
  1099. fileInfo.setName(info.fileName());
  1100. fileInfo.setSize(info.size());
  1101. fileInfo.setDescription(description);
  1102. // open file
  1103. QIODevice *device = new QFile(filePath);
  1104. if (!device->open(QIODevice::ReadOnly))
  1105. {
  1106. warning(QString("Could not read from %1").arg(filePath));
  1107. delete device;
  1108. device = 0;
  1109. }
  1110. // hash file
  1111. if (device && !device->isSequential())
  1112. {
  1113. QCryptographicHash hash(QCryptographicHash::Md5);
  1114. QByteArray buffer;
  1115. while (device->bytesAvailable())
  1116. {
  1117. buffer = device->read(16384);
  1118. hash.addData(buffer);
  1119. }
  1120. device->reset();
  1121. fileInfo.setHash(hash.result());
  1122. }
  1123. // create job
  1124. QXmppTransferJob *job = sendFile(jid, device, fileInfo);
  1125. job->setLocalFileUrl(filePath);
  1126. return job;
  1127. }
  1128. /// Send file to a remote party.
  1129. ///
  1130. /// The remote party will be given the choice to accept or refuse the transfer.
  1131. ///
  1132. QXmppTransferJob *QXmppTransferManager::sendFile(const QString &jid, QIODevice *device, const QXmppTransferFileInfo &fileInfo, const QString &sid)
  1133. {
  1134. bool check;
  1135. Q_UNUSED(check);
  1136. if (jid.isEmpty()) {
  1137. warning("Refusing to send file to an empty jid");
  1138. return 0;
  1139. }
  1140. QXmppTransferOutgoingJob *job = new QXmppTransferOutgoingJob(jid, client(), this);
  1141. if (sid.isEmpty())
  1142. job->d->sid = QXmppUtils::generateStanzaHash();
  1143. else
  1144. job->d->sid = sid;
  1145. job->d->fileInfo = fileInfo;
  1146. job->d->iodevice = device;
  1147. if (device)
  1148. device->setParent(job);
  1149. // check file is open
  1150. if (!device || !device->isReadable())
  1151. {
  1152. job->terminate(QXmppTransferJob::FileAccessError);
  1153. return job;
  1154. }
  1155. // check we support some methods
  1156. if (!d->supportedMethods)
  1157. {
  1158. job->terminate(QXmppTransferJob::ProtocolError);
  1159. return job;
  1160. }
  1161. // collect supported stream methods
  1162. QXmppDataForm form;
  1163. form.setType(QXmppDataForm::Form);
  1164. QXmppDataForm::Field methodField(QXmppDataForm::Field::ListSingleField);
  1165. methodField.setKey("stream-method");
  1166. if (d->supportedMethods & QXmppTransferJob::InBandMethod)
  1167. methodField.setOptions(methodField.options() << qMakePair(QString(), QString::fromLatin1(ns_ibb)));
  1168. if (d->supportedMethods & QXmppTransferJob::SocksMethod)
  1169. methodField.setOptions(methodField.options() << qMakePair(QString(), QString::fromLatin1(ns_bytestreams)));
  1170. form.setFields(QList<QXmppDataForm::Field>() << methodField);
  1171. // start job
  1172. d->jobs.append(job);
  1173. check = connect(job, SIGNAL(destroyed(QObject*)),
  1174. this, SLOT(_q_jobDestroyed(QObject*)));
  1175. Q_ASSERT(check);
  1176. check = connect(job, SIGNAL(error(QXmppTransferJob::Error)),
  1177. this, SLOT(_q_jobError(QXmppTransferJob::Error)));
  1178. Q_ASSERT(check);
  1179. check = connect(job, SIGNAL(finished()),
  1180. this, SLOT(_q_jobFinished()));
  1181. Q_ASSERT(check);
  1182. QXmppStreamInitiationIq request;
  1183. request.setType(QXmppIq::Set);
  1184. request.setTo(jid);
  1185. request.setProfile(QXmppStreamInitiationIq::FileTransfer);
  1186. request.setFileInfo(job->d->fileInfo);
  1187. request.setFeatureForm(form);
  1188. request.setSiId(job->d->sid);
  1189. job->d->requestId = request.id();
  1190. client()->sendPacket(request);
  1191. // notify user
  1192. emit jobStarted(job);
  1193. return job;
  1194. }
  1195. void QXmppTransferManager::_q_socksServerConnected(QTcpSocket *socket, const QString &hostName, quint16 port)
  1196. {
  1197. const QString ownJid = client()->configuration().jid();
  1198. foreach (QXmppTransferJob *job, d->jobs)
  1199. {
  1200. if (hostName == streamHash(job->d->sid, ownJid, job->jid()) && port == 0)
  1201. {
  1202. job->d->socksSocket = socket;
  1203. return;
  1204. }
  1205. }
  1206. warning("QXmppSocksServer got a connection for a unknown stream");
  1207. socket->close();
  1208. }
  1209. void QXmppTransferManager::socksServerSendOffer(QXmppTransferJob *job)
  1210. {
  1211. const QString ownJid = client()->configuration().jid();
  1212. QList<QXmppByteStreamIq::StreamHost> streamHosts;
  1213. // discover local IPs
  1214. if (!d->proxyOnly) {
  1215. foreach (const QHostAddress &address, QXmppIceComponent::discoverAddresses()) {
  1216. QXmppByteStreamIq::StreamHost streamHost;
  1217. streamHost.setJid(ownJid);
  1218. streamHost.setHost(address.toString());
  1219. streamHost.setPort(d->socksServer->serverPort());
  1220. streamHosts.append(streamHost);
  1221. }
  1222. }
  1223. // add proxy
  1224. if (!job->d->socksProxy.jid().isEmpty())
  1225. streamHosts.append(job->d->socksProxy);
  1226. // check we have some stream hosts
  1227. if (!streamHosts.size())
  1228. {
  1229. warning("Could not determine local stream hosts");
  1230. job->terminate(QXmppTransferJob::ProtocolError);
  1231. return;
  1232. }
  1233. // send offer
  1234. QXmppByteStreamIq streamIq;
  1235. streamIq.setType(QXmppIq::Set);
  1236. streamIq.setTo(job->d->jid);
  1237. streamIq.setSid(job->d->sid);
  1238. streamIq.setStreamHosts(streamHosts);
  1239. job->d->requestId = streamIq.id();
  1240. client()->sendPacket(streamIq);
  1241. }
  1242. void QXmppTransferManager::streamInitiationIqReceived(const QXmppStreamInitiationIq &iq)
  1243. {
  1244. if (iq.type() == QXmppIq::Result)
  1245. streamInitiationResultReceived(iq);
  1246. else if (iq.type() == QXmppIq::Set)
  1247. streamInitiationSetReceived(iq);
  1248. }
  1249. // The remote party has accepted an outgoing transfer.
  1250. void QXmppTransferManager::streamInitiationResultReceived(const QXmppStreamInitiationIq &iq)
  1251. {
  1252. QXmppTransferJob *job = d->getOutgoingJobByRequestId(iq.from(), iq.id());
  1253. if (!job ||
  1254. job->state() != QXmppTransferJob::OfferState)
  1255. return;
  1256. foreach (const QXmppDataForm::Field &field, iq.featureForm().fields()) {
  1257. if (field.key() == "stream-method") {
  1258. if ((field.value().toString() == ns_ibb) &&
  1259. (d->supportedMethods & QXmppTransferJob::InBandMethod))
  1260. job->d->method = QXmppTransferJob::InBandMethod;
  1261. else if ((field.value().toString() == ns_bytestreams) &&
  1262. (d->supportedMethods & QXmppTransferJob::SocksMethod))
  1263. job->d->method = QXmppTransferJob::SocksMethod;
  1264. }
  1265. }
  1266. // remote party accepted stream initiation
  1267. job->setState(QXmppTransferJob::StartState);
  1268. if (job->method() == QXmppTransferJob::InBandMethod)
  1269. {
  1270. // lower block size for IBB
  1271. job->d->blockSize = d->ibbBlockSize;
  1272. QXmppIbbOpenIq openIq;
  1273. openIq.setTo(job->d->jid);
  1274. openIq.setSid(job->d->sid);
  1275. openIq.setBlockSize(job->d->blockSize);
  1276. job->d->requestId = openIq.id();
  1277. client()->sendPacket(openIq);
  1278. } else if (job->method() == QXmppTransferJob::SocksMethod) {
  1279. if (!d->proxy.isEmpty())
  1280. {
  1281. job->d->socksProxy.setJid(d->proxy);
  1282. // query proxy
  1283. QXmppByteStreamIq streamIq;
  1284. streamIq.setType(QXmppIq::Get);
  1285. streamIq.setTo(job->d->socksProxy.jid());
  1286. streamIq.setSid(job->d->sid);
  1287. job->d->requestId = streamIq.id();
  1288. client()->sendPacket(streamIq);
  1289. } else {
  1290. socksServerSendOffer(job);
  1291. }
  1292. } else {
  1293. warning("QXmppTransferManager received an unsupported method");
  1294. job->terminate(QXmppTransferJob::ProtocolError);
  1295. }
  1296. }
  1297. void QXmppTransferManager::streamInitiationSetReceived(const QXmppStreamInitiationIq &iq)
  1298. {
  1299. bool check;
  1300. Q_UNUSED(check);
  1301. QXmppIq response;
  1302. response.setTo(iq.from());
  1303. response.setId(iq.id());
  1304. // check we support the profile
  1305. if (iq.profile() != QXmppStreamInitiationIq::FileTransfer)
  1306. {
  1307. // FIXME : we should add:
  1308. // <bad-profile xmlns='http://jabber.org/protocol/si'/>
  1309. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest);
  1310. error.setCode(400);
  1311. response.setType(QXmppIq::Error);
  1312. response.setError(error);
  1313. client()->sendPacket(response);
  1314. return;
  1315. }
  1316. // check there is a receiver connected to the fileReceived() signal
  1317. if (!receivers(SIGNAL(fileReceived(QXmppTransferJob*))))
  1318. {
  1319. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::Forbidden);
  1320. error.setCode(403);
  1321. response.setType(QXmppIq::Error);
  1322. response.setError(error);
  1323. client()->sendPacket(response);
  1324. return;
  1325. }
  1326. // check the stream type
  1327. QXmppTransferIncomingJob *job = new QXmppTransferIncomingJob(iq.from(), client(), this);
  1328. int offeredMethods = QXmppTransferJob::NoMethod;
  1329. job->d->offerId = iq.id();
  1330. job->d->sid = iq.siId();
  1331. job->d->mimeType = iq.mimeType();
  1332. job->d->fileInfo = iq.fileInfo();
  1333. foreach (const QXmppDataForm::Field &field, iq.featureForm().fields()) {
  1334. if (field.key() == "stream-method") {
  1335. QPair<QString, QString> option;
  1336. foreach (option, field.options()) {
  1337. if (option.second == ns_ibb)
  1338. offeredMethods = offeredMethods | QXmppTransferJob::InBandMethod;
  1339. else if (option.second == ns_bytestreams)
  1340. offeredMethods = offeredMethods | QXmppTransferJob::SocksMethod;
  1341. }
  1342. }
  1343. }
  1344. // select a method supported by both parties
  1345. int sharedMethods = (offeredMethods & d->supportedMethods);
  1346. if (sharedMethods & QXmppTransferJob::SocksMethod)
  1347. job->d->method = QXmppTransferJob::SocksMethod;
  1348. else if (sharedMethods & QXmppTransferJob::InBandMethod)
  1349. job->d->method = QXmppTransferJob::InBandMethod;
  1350. else
  1351. {
  1352. // FIXME : we should add:
  1353. // <no-valid-streams xmlns='http://jabber.org/protocol/si'/>
  1354. QXmppStanza::Error error(QXmppStanza::Error::Cancel, QXmppStanza::Error::BadRequest);
  1355. error.setCode(400);
  1356. response.setType(QXmppIq::Error);
  1357. response.setError(error);
  1358. client()->sendPacket(response);
  1359. delete job;
  1360. return;
  1361. }
  1362. // register job
  1363. d->jobs.append(job);
  1364. check = connect(job, SIGNAL(destroyed(QObject*)),
  1365. this, SLOT(_q_jobDestroyed(QObject*)));
  1366. Q_ASSERT(check);
  1367. check = connect(job, SIGNAL(finished()),
  1368. this, SLOT(_q_jobFinished()));
  1369. Q_ASSERT(check);
  1370. check = connect(job, SIGNAL(stateChanged(QXmppTransferJob::State)),
  1371. this, SLOT(_q_jobStateChanged(QXmppTransferJob::State)));
  1372. Q_ASSERT(check);
  1373. // allow user to accept or decline the job
  1374. emit fileReceived(job);
  1375. }
  1376. /// Return the JID of the bytestream proxy to use for
  1377. /// outgoing transfers.
  1378. ///
  1379. QString QXmppTransferManager::proxy() const
  1380. {
  1381. return d->proxy;
  1382. }
  1383. /// Set the JID of the SOCKS5 bytestream proxy to use for
  1384. /// outgoing transfers.
  1385. ///
  1386. /// If you set a proxy, when you send a file the proxy will
  1387. /// be offered to the recipient in addition to your own IP
  1388. /// addresses.
  1389. ///
  1390. void QXmppTransferManager::setProxy(const QString &proxyJid)
  1391. {
  1392. d->proxy = proxyJid;
  1393. }
  1394. /// Return whether the proxy will systematically be used for
  1395. /// outgoing SOCKS5 bytestream transfers.
  1396. ///
  1397. bool QXmppTransferManager::proxyOnly() const
  1398. {
  1399. return d->proxyOnly;
  1400. }
  1401. /// Set whether the proxy should systematically be used for
  1402. /// outgoing SOCKS5 bytestream transfers.
  1403. ///
  1404. /// \note If you set this to true and do not provide a proxy
  1405. /// using setProxy(), your outgoing transfers will fail!
  1406. ///
  1407. void QXmppTransferManager::setProxyOnly(bool proxyOnly)
  1408. {
  1409. d->proxyOnly = proxyOnly;
  1410. }
  1411. /// Return the supported stream methods.
  1412. ///
  1413. /// The methods are a combination of zero or more QXmppTransferJob::Method.
  1414. ///
  1415. QXmppTransferJob::Methods QXmppTransferManager::supportedMethods() const
  1416. {
  1417. return d->supportedMethods;
  1418. }
  1419. /// Set the supported stream methods. This allows you to selectively
  1420. /// enable or disable stream methods (In-Band or SOCKS5 bytestreams).
  1421. ///
  1422. /// The methods argument is a combination of zero or more
  1423. /// QXmppTransferJob::Method.
  1424. ///
  1425. void QXmppTransferManager::setSupportedMethods(QXmppTransferJob::Methods methods)
  1426. {
  1427. d->supportedMethods = methods;
  1428. }