~ubuntu-branches/ubuntu/lucid/ktorrent/lucid

« back to all changes in this revision

Viewing changes to libktorrent/torrent/httptracker.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Richard Birnie
  • Date: 2008-06-03 20:32:46 UTC
  • mfrom: (1.1.20 upstream)
  • Revision ID: james.westby@ubuntu.com-20080603203246-dfyemn010uhsf433
Tags: 3.1~rc1+dfsg.1-1ubuntu1
* New upstream development release      
 - Dropped 01_support_external_libbtcore.diffm,
   97_fix_target_link_libraries.diff,
   99_libbtcore_scramble_soname.diff

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/***************************************************************************
2
 
 *   Copyright (C) 2005 by Joris Guisson                                   *
3
 
 *   joris.guisson@gmail.com                                               *
4
 
 *                                                                         *
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.                                   *
9
 
 *                                                                         *
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.                          *
14
 
 *                                                                         *
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
 
 ***************************************************************************/
20
 
#include <config.h>
21
 
 
22
 
#include <kurl.h>
23
 
#include <klocale.h>
24
 
#include <qhostaddress.h>
25
 
#include <util/log.h>
26
 
#include <util/functions.h>
27
 
#include <util/error.h>
28
 
#include <util/waitjob.h>
29
 
#include <interfaces/exitoperation.h>
30
 
#include <kio/job.h>
31
 
#include <kio/netaccess.h>
32
 
#include <kio/scheduler.h>
33
 
#include "bnode.h"
34
 
#include "httptracker.h"
35
 
#include "torrentcontrol.h"
36
 
#include "bdecoder.h"
37
 
#include "peermanager.h"
38
 
#include "server.h"
39
 
#include "globals.h"
40
 
#include "settings.h"
41
 
 
42
 
 
43
 
using namespace kt;
44
 
 
45
 
namespace bt
46
 
{
47
 
 
48
 
        HTTPTracker::HTTPTracker(const KURL & url,kt::TorrentInterface* tor,const PeerID & id,int tier)
49
 
                : Tracker(url,tor,id,tier)
50
 
        {
51
 
                active_job = 0;
52
 
                
53
 
                interval = 5 * 60; // default interval 5 minutes
54
 
                failures = 0;
55
 
                seeders = leechers = 0;
56
 
        }
57
 
 
58
 
 
59
 
        HTTPTracker::~HTTPTracker()
60
 
        {
61
 
        }
62
 
        
63
 
        void HTTPTracker::start()
64
 
        {
65
 
                event = "started";
66
 
                doRequest();
67
 
        }
68
 
        
69
 
        void HTTPTracker::stop(WaitJob* wjob)
70
 
        {
71
 
                if (!started)
72
 
                        return;
73
 
                
74
 
                event = "stopped";
75
 
                doRequest(wjob);
76
 
                started = false;
77
 
        }
78
 
        
79
 
        void HTTPTracker::completed()
80
 
        {
81
 
                event = "completed";
82
 
                doRequest();
83
 
                event = QString::null;
84
 
        }
85
 
        
86
 
        void HTTPTracker::manualUpdate()
87
 
        {
88
 
                if (!started)
89
 
                        event = "started";
90
 
                doRequest();
91
 
        }
92
 
        
93
 
        void HTTPTracker::scrape()
94
 
        {
95
 
                if (!url.isValid())
96
 
                {
97
 
                        Out(SYS_TRK|LOG_NOTICE) << "Invalid tracker url, canceling scrape" << endl;
98
 
                        return;
99
 
                }
100
 
                
101
 
                if (!url.fileName(false).startsWith("announce"))
102
 
                {
103
 
                        Out(SYS_TRK|LOG_NOTICE) << "Tracker " << url << " does not support scraping" << endl;
104
 
                        return;
105
 
                }
106
 
                
107
 
                KURL scrape_url = url;
108
 
                scrape_url.setFileName(url.fileName(false).replace("announce","scrape"));
109
 
                
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();
114
 
                else
115
 
                        epq += "?info_hash=" + info_hash.toURLString();
116
 
                scrape_url.setEncodedPathAndQuery(epq);
117
 
        
118
 
                Out(SYS_TRK|LOG_NOTICE) << "Doing scrape request to url : " << scrape_url.prettyURL() << endl;
119
 
                KIO::MetaData md;
120
 
                setupMetaData(md);
121
 
                
122
 
                KIO::StoredTransferJob* j = KIO::storedGet(scrape_url,false,false);
123
 
                // set the meta data
124
 
                j->setMetaData(md);
125
 
                KIO::Scheduler::scheduleJob(j);
126
 
                
127
 
                connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onScrapeResult( KIO::Job* )));
128
 
        }
129
 
        
130
 
        void HTTPTracker::onScrapeResult(KIO::Job* j)
131
 
        {
132
 
                if (j->error())
133
 
                {
134
 
                        Out(SYS_TRK|LOG_IMPORTANT) << "Scrape failed : " << j->errorString() << endl;
135
 
                        return;
136
 
                }
137
 
                
138
 
                KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
139
 
                BDecoder dec(st->data(),false,0);
140
 
                BNode* n = 0;
141
 
                
142
 
                try
143
 
                {
144
 
                        n = dec.decode();
145
 
                }
146
 
                catch (bt::Error & err)
147
 
                {
148
 
                        Out(SYS_TRK|LOG_IMPORTANT) << "Invalid scrape data " << err.toString() << endl;
149
 
                        return;
150
 
                }
151
 
                        
152
 
                if (n && n->getType() == BNode::DICT)
153
 
                {
154
 
                        BDictNode* d = (BDictNode*)n;
155
 
                        d = d->getDict("files");
156
 
                        if (d)
157
 
                        {
158
 
                                d = d->getDict(tor->getInfoHash().toByteArray());
159
 
                                if (d)
160
 
                                {
161
 
                                        BValueNode* vn = d->getValue("complete");
162
 
                                        if (vn && vn->data().getType() == Value::INT)
163
 
                                        {
164
 
                                                seeders = vn->data().toInt();
165
 
                                        } 
166
 
                                                
167
 
                                        
168
 
                                        vn = d->getValue("incomplete");
169
 
                                        if (vn && vn->data().getType() == Value::INT)
170
 
                                        {
171
 
                                                leechers = vn->data().toInt();
172
 
                                        }
173
 
                                        
174
 
                                        Out(SYS_TRK|LOG_DEBUG) << "Scrape : leechers = " << leechers 
175
 
                                                        << ", seeders = " << seeders << endl;
176
 
                                }
177
 
                        }
178
 
                }
179
 
                
180
 
                delete n;
181
 
        }
182
 
        
183
 
        void HTTPTracker::doRequest(WaitJob* wjob)
184
 
        {       
185
 
                const TorrentStats & s = tor->getStats();
186
 
                
187
 
                KURL u = url;
188
 
                if (!url.isValid())
189
 
                {
190
 
                        requestPending();
191
 
                        QTimer::singleShot(500,this,SLOT(emitInvalidURLFailure()));
192
 
                        return;
193
 
                }
194
 
 
195
 
                Uint16 port = Globals::instance().getServer().getPortInUse();;
196
 
                
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));
201
 
                
202
 
                if (event == "completed")
203
 
                        u.addQueryItem("left","0"); // need to send 0 when we are completed
204
 
                else
205
 
                        u.addQueryItem("left",QString::number(s.bytes_left));
206
 
                
207
 
                u.addQueryItem("compact","1");
208
 
                if (event != "stopped")
209
 
                        u.addQueryItem("numwant","100");
210
 
                else
211
 
                        u.addQueryItem("numwant","0");
212
 
                
213
 
                u.addQueryItem("key",QString::number(key));
214
 
                QString cip = Tracker::getCustomIP();
215
 
                if (!cip.isNull())
216
 
                        u.addQueryItem("ip",cip);
217
 
 
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();
223
 
 
224
 
 
225
 
                u.setEncodedPathAndQuery(epq);
226
 
                
227
 
                if (active_job)
228
 
                {
229
 
                        announce_queue.append(u);
230
 
                        Out(SYS_TRK|LOG_NOTICE) << "Announce ongoing, queueing announce" << endl;
231
 
                }
232
 
                else
233
 
                {
234
 
                        doAnnounce(u);
235
 
                        // if there is a wait job, add this job to the waitjob 
236
 
                        if (wjob)
237
 
                                wjob->addExitOperation(new kt::ExitJobOperation(active_job));
238
 
                }
239
 
        }
240
 
 
241
 
        bool HTTPTracker::updateData(const QByteArray & data)
242
 
        {
243
 
//#define DEBUG_PRINT_RESPONSE
244
 
#ifdef DEBUG_PRINT_RESPONSE
245
 
                Out() << "Data : " << endl;
246
 
                Out() << QString(data) << endl;
247
 
#endif
248
 
                // search for dictionary, there might be random garbage infront of the data
249
 
                Uint32 i = 0;
250
 
                while (i < data.size())
251
 
                {
252
 
                        if (data[i] == 'd')
253
 
                                break;
254
 
                        i++;
255
 
                }
256
 
                
257
 
                if (i == data.size())
258
 
                {
259
 
                        failures++;
260
 
                        requestFailed(i18n("Invalid response from tracker"));
261
 
                        return false;
262
 
                }
263
 
                
264
 
                BDecoder dec(data,false,i);
265
 
                BNode* n = 0;
266
 
                try
267
 
                {
268
 
                        n = dec.decode();
269
 
                }
270
 
                catch (...)
271
 
                {
272
 
                        failures++;
273
 
                        requestFailed(i18n("Invalid data from tracker"));
274
 
                        return false;
275
 
                }
276
 
                        
277
 
                if (!n || n->getType() != BNode::DICT)
278
 
                {
279
 
                        failures++;
280
 
                        requestFailed(i18n("Invalid response from tracker"));
281
 
                        return false;
282
 
                }
283
 
                        
284
 
                BDictNode* dict = (BDictNode*)n;
285
 
                if (dict->getData("failure reason"))
286
 
                {
287
 
                        BValueNode* vn = dict->getValue("failure reason");
288
 
                        QString msg = vn->data().toString();
289
 
                        delete n;
290
 
                        failures++;
291
 
                        requestFailed(msg);
292
 
                        return false;
293
 
                }
294
 
                        
295
 
                BValueNode* vn = dict->getValue("interval");
296
 
                        
297
 
                // if no interval is specified, use 5 minutes
298
 
                if (vn)
299
 
                        interval = vn->data().toInt();
300
 
                else
301
 
                        interval = 5 * 60;
302
 
                        
303
 
                vn = dict->getValue("incomplete");
304
 
                if (vn)
305
 
                        leechers = vn->data().toInt();
306
 
 
307
 
                vn = dict->getValue("complete");
308
 
                if (vn)
309
 
                        seeders = vn->data().toInt();
310
 
        
311
 
                BListNode* ln = dict->getList("peers");
312
 
                if (!ln)
313
 
                {
314
 
                        // no list, it might however be a compact response
315
 
                        vn = dict->getValue("peers");
316
 
                        if (!vn)
317
 
                        {
318
 
                                delete n;
319
 
                                failures++;
320
 
                                requestFailed(i18n("Invalid response from tracker"));
321
 
                                return false;
322
 
                        }
323
 
 
324
 
                        QByteArray arr = vn->data().toByteArray();
325
 
                        for (Uint32 i = 0;i < arr.size();i+=6)
326
 
                        {
327
 
                                Uint8 buf[6];
328
 
                                for (int j = 0;j < 6;j++)
329
 
                                        buf[j] = arr[i + j];
330
 
 
331
 
                                addPeer(QHostAddress(ReadUint32(buf,0)).toString(),ReadUint16(buf,4));
332
 
                        }
333
 
                }
334
 
                else
335
 
                {
336
 
                        for (Uint32 i = 0;i < ln->getNumChildren();i++)
337
 
                        {
338
 
                                BDictNode* dict = dynamic_cast<BDictNode*>(ln->getChild(i));
339
 
 
340
 
                                if (!dict)
341
 
                                        continue;
342
 
                                
343
 
                                BValueNode* ip_node = dict->getValue("ip");
344
 
                                BValueNode* port_node = dict->getValue("port");
345
 
 
346
 
                                if (!ip_node || !port_node)
347
 
                                        continue;
348
 
                                
349
 
                                addPeer(ip_node->data().toString(),port_node->data().toInt());
350
 
                        }
351
 
                }
352
 
                
353
 
                delete n;
354
 
                return true;
355
 
        }
356
 
 
357
 
        
358
 
        void HTTPTracker::onAnnounceResult(KIO::Job* j)
359
 
        {
360
 
                if (j->error())
361
 
                {
362
 
                        KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
363
 
                        KURL u = st->url();
364
 
                        active_job = 0;
365
 
                        
366
 
                        Out(SYS_TRK|LOG_IMPORTANT) << "Error : " << st->errorString() << endl;
367
 
                        if (u.queryItem("event") != "stopped")
368
 
                        {
369
 
                                failures++;
370
 
                                requestFailed(j->errorString());
371
 
                        }
372
 
                        else
373
 
                        {
374
 
                                stopDone();
375
 
                        }
376
 
                }
377
 
                else
378
 
                {
379
 
                        KIO::StoredTransferJob* st = (KIO::StoredTransferJob*)j;
380
 
                        KURL u = st->url();
381
 
                        active_job = 0;
382
 
                        
383
 
                        if (u.queryItem("event") != "stopped")
384
 
                        {
385
 
                                try
386
 
                                {
387
 
                                        if (updateData(st->data()))
388
 
                                        {
389
 
                                                failures = 0;
390
 
                                                peersReady(this);
391
 
                                                requestOK();
392
 
                                                if (u.queryItem("event") == "started")
393
 
                                                        started = true;
394
 
                                        }
395
 
                                }
396
 
                                catch (bt::Error & err)
397
 
                                {
398
 
                                        failures++;
399
 
                                        requestFailed(i18n("Invalid response from tracker"));
400
 
                                }
401
 
                                event = QString::null;
402
 
                        }
403
 
                        else
404
 
                        {
405
 
                                failures = 0;
406
 
                                stopDone();
407
 
                        }
408
 
                }
409
 
                doAnnounceQueue();
410
 
        }
411
 
 
412
 
        void HTTPTracker::emitInvalidURLFailure()
413
 
        {
414
 
                failures++;
415
 
                requestFailed(i18n("Invalid tracker URL"));
416
 
        }
417
 
        
418
 
        void HTTPTracker::setupMetaData(KIO::MetaData & md)
419
 
        {
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())
426
 
                {
427
 
                        // set the proxy if the doNotUseKDEProxy ix enabled (URL must be valid to)
428
 
                        KURL url = KURL::fromPathOrURL(Settings::httpTrackerProxy());
429
 
                        if (url.isValid())
430
 
                                md["UseProxy"] = url.pathOrURL();
431
 
                        else
432
 
                                md["UseProxy"] = QString::null;
433
 
                }
434
 
        }
435
 
        
436
 
        void HTTPTracker::doAnnounceQueue()
437
 
        {
438
 
                if (announce_queue.empty())
439
 
                        return;
440
 
                
441
 
                KURL u = announce_queue.front();
442
 
                announce_queue.pop_front();
443
 
                doAnnounce(u);
444
 
        }
445
 
        
446
 
        void HTTPTracker::doAnnounce(const KURL & u)
447
 
        {
448
 
                Out(SYS_TRK|LOG_NOTICE) << "Doing tracker request to url : " << u.prettyURL() << endl;
449
 
                KIO::MetaData md;
450
 
                setupMetaData(md);
451
 
                KIO::StoredTransferJob* j = KIO::storedGet(u,false,false);
452
 
                // set the meta data
453
 
                j->setMetaData(md);
454
 
                KIO::Scheduler::scheduleJob(j);
455
 
                
456
 
                connect(j,SIGNAL(result(KIO::Job* )),this,SLOT(onAnnounceResult( KIO::Job* )));
457
 
                
458
 
                active_job = j;
459
 
                requestPending();
460
 
        }
461
 
}
462
 
#include "httptracker.moc"