1
/***************************************************************************
2
* Copyright (C) 2005 by Joris Guisson *
3
* joris.guisson@gmail.com *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; if not, write to the *
17
* Free Software Foundation, Inc., *
18
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19
***************************************************************************/
24
#include <qhostaddress.h>
26
#include <util/functions.h>
27
#include <util/error.h>
28
#include <util/waitjob.h>
29
#include <interfaces/exitoperation.h>
31
#include <kio/netaccess.h>
32
#include <kio/scheduler.h>
34
#include "httptracker.h"
35
#include "torrentcontrol.h"
37
#include "peermanager.h"
48
HTTPTracker::HTTPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier)
49
: Tracker(url,tor,id,tier)
53
interval = 5 * 60; // default interval 5 minutes
55
seeders = leechers = 0;
59
HTTPTracker::~HTTPTracker()
63
void HTTPTracker::start()
69
void HTTPTracker::stop(WaitJob* wjob)
79
void HTTPTracker::completed()
83
event = QString::null;
86
void HTTPTracker::manualUpdate()
93
void HTTPTracker::scrape()
97
Out(SYS_TRK|LOG_NOTICE) << "Invalid tracker url, canceling scrape" << endl;
101
if (!url.fileName(false).startsWith("announce"))
103
Out(SYS_TRK|LOG_NOTICE) << "Tracker " << url << " does not support scraping" << endl;
107
KURL scrape_url = url;
108
scrape_url.setFileName(url.fileName(false).replace("announce","scrape"));
110
QString epq = scrape_url.encodedPathAndQuery();
111
const SHA1Hash & info_hash = tor->getInfoHash();
112
if (scrape_url.queryItems().count() > 0)
113
epq += "&info_hash=" + info_hash.toURLString();
115
epq += "?info_hash=" + info_hash.toURLString();
116
scrape_url.setEncodedPathAndQuery(epq);
118
Out(SYS_TRK|LOG_NOTICE) << "Doing scrape request to url : " << scrape_url.prettyURL() << endl;
122
KIO::StoredTransferJob* j = KIO::storedGet(scrape_url,false,false);
125
KIO::Scheduler::scheduleJob(j);
127
connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onScrapeResult( KIO::Job* )));
130
void HTTPTracker::onScrapeResult(KIO::Job* j)
134
Out(SYS_TRK|LOG_IMPORTANT) << "Scrape failed : " << j->errorString() << endl;
138
KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
139
BDecoder dec(st->data(),false,0);
146
catch (bt::Error & err)
148
Out(SYS_TRK|LOG_IMPORTANT) << "Invalid scrape data " << err.toString() << endl;
152
if (n && n->getType() == BNode::DICT)
154
BDictNode* d = (BDictNode*)n;
155
d = d->getDict("files");
158
d = d->getDict(tor->getInfoHash().toByteArray());
161
BValueNode* vn = d->getValue("complete");
162
if (vn && vn->data().getType() == Value::INT)
164
seeders = vn->data().toInt();
168
vn = d->getValue("incomplete");
169
if (vn && vn->data().getType() == Value::INT)
171
leechers = vn->data().toInt();
174
Out(SYS_TRK|LOG_DEBUG) << "Scrape : leechers = " << leechers
175
<< ", seeders = " << seeders << endl;
183
void HTTPTracker::doRequest(WaitJob* wjob)
185
const TorrentStats & s = tor->getStats();
191
QTimer::singleShot(500,this,SLOT(emitInvalidURLFailure()));
195
Uint16 port = Globals::instance().getServer().getPortInUse();;
197
u.addQueryItem("peer_id",peer_id.toString());
198
u.addQueryItem("port",QString::number(port));
199
u.addQueryItem("uploaded",QString::number(s.trk_bytes_uploaded));
200
u.addQueryItem("downloaded",QString::number(s.trk_bytes_downloaded));
202
if (event == "completed")
203
u.addQueryItem("left","0"); // need to send 0 when we are completed
205
u.addQueryItem("left",QString::number(s.bytes_left));
207
u.addQueryItem("compact","1");
208
if (event != "stopped")
209
u.addQueryItem("numwant","100");
211
u.addQueryItem("numwant","0");
213
u.addQueryItem("key",QString::number(key));
214
QString cip = Tracker::getCustomIP();
216
u.addQueryItem("ip",cip);
218
if (event != QString::null)
219
u.addQueryItem("event",event);
220
QString epq = u.encodedPathAndQuery();
221
const SHA1Hash & info_hash = tor->getInfoHash();
222
epq += "&info_hash=" + info_hash.toURLString();
225
u.setEncodedPathAndQuery(epq);
229
announce_queue.append(u);
230
Out(SYS_TRK|LOG_NOTICE) << "Announce ongoing, queueing announce" << endl;
235
// if there is a wait job, add this job to the waitjob
237
wjob->addExitOperation(new kt::ExitJobOperation(active_job));
241
bool HTTPTracker::updateData(const QByteArray & data)
243
//#define DEBUG_PRINT_RESPONSE
244
#ifdef DEBUG_PRINT_RESPONSE
245
Out() << "Data : " << endl;
246
Out() << QString(data) << endl;
248
// search for dictionary, there might be random garbage infront of the data
250
while (i < data.size())
257
if (i == data.size())
260
requestFailed(i18n("Invalid response from tracker"));
264
BDecoder dec(data,false,i);
273
requestFailed(i18n("Invalid data from tracker"));
277
if (!n || n->getType() != BNode::DICT)
280
requestFailed(i18n("Invalid response from tracker"));
284
BDictNode* dict = (BDictNode*)n;
285
if (dict->getData("failure reason"))
287
BValueNode* vn = dict->getValue("failure reason");
288
QString msg = vn->data().toString();
295
BValueNode* vn = dict->getValue("interval");
297
// if no interval is specified, use 5 minutes
299
interval = vn->data().toInt();
303
vn = dict->getValue("incomplete");
305
leechers = vn->data().toInt();
307
vn = dict->getValue("complete");
309
seeders = vn->data().toInt();
311
BListNode* ln = dict->getList("peers");
314
// no list, it might however be a compact response
315
vn = dict->getValue("peers");
320
requestFailed(i18n("Invalid response from tracker"));
324
QByteArray arr = vn->data().toByteArray();
325
for (Uint32 i = 0;i < arr.size();i+=6)
328
for (int j = 0;j < 6;j++)
331
addPeer(QHostAddress(ReadUint32(buf,0)).toString(),ReadUint16(buf,4));
336
for (Uint32 i = 0;i < ln->getNumChildren();i++)
338
BDictNode* dict = dynamic_cast<BDictNode*>(ln->getChild(i));
343
BValueNode* ip_node = dict->getValue("ip");
344
BValueNode* port_node = dict->getValue("port");
346
if (!ip_node || !port_node)
349
addPeer(ip_node->data().toString(),port_node->data().toInt());
358
void HTTPTracker::onAnnounceResult(KIO::Job* j)
362
KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
366
Out(SYS_TRK|LOG_IMPORTANT) << "Error : " << st->errorString() << endl;
367
if (u.queryItem("event") != "stopped")
370
requestFailed(j->errorString());
379
KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
383
if (u.queryItem("event") != "stopped")
387
if (updateData(st->data()))
392
if (u.queryItem("event") == "started")
396
catch (bt::Error & err)
399
requestFailed(i18n("Invalid response from tracker"));
401
event = QString::null;
412
void HTTPTracker::emitInvalidURLFailure()
415
requestFailed(i18n("Invalid tracker URL"));
418
void HTTPTracker::setupMetaData(KIO::MetaData & md)
420
md["UserAgent"] = "ktorrent/" VERSION;
421
md["SendLanguageSettings"] = "false";
422
md["Cookies"] = "none";
423
// md["accept"] = "text/plain";
424
md["accept"] = "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2";
425
if (Settings::doNotUseKDEProxy())
427
// set the proxy if the doNotUseKDEProxy ix enabled (URL must be valid to)
428
KURL url = KURL::fromPathOrURL(Settings::httpTrackerProxy());
430
md["UseProxy"] = url.pathOrURL();
432
md["UseProxy"] = QString::null;
436
void HTTPTracker::doAnnounceQueue()
438
if (announce_queue.empty())
441
KURL u = announce_queue.front();
442
announce_queue.pop_front();
446
void HTTPTracker::doAnnounce(const KURL & u)
448
Out(SYS_TRK|LOG_NOTICE) << "Doing tracker request to url : " << u.prettyURL() << endl;
451
KIO::StoredTransferJob* j = KIO::storedGet(u,false,false);
454
KIO::Scheduler::scheduleJob(j);
456
connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onAnnounceResult( KIO::Job* )));
462
#include "httptracker.moc"