~ubuntu-branches/ubuntu/oneiric/libtorrent/oneiric

« back to all changes in this revision

Viewing changes to src/tracker/tracker_http.cc

  • Committer: Bazaar Package Importer
  • Author(s): Rogério Brito
  • Date: 2011-03-20 01:06:18 UTC
  • mfrom: (1.1.13 upstream) (4.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110320010618-g3wyylccqzqko73c
Tags: 0.12.7-5
* Use Steinar's "real" patch for IPv6. Addresses #490277, #618275,
  and Closes: #617791.
* Adapt libtorrent-0.12.6-ipv6-07.patch. It FTBFS otherwise.
* Add proper attibution to the IPv6 patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// libTorrent - BitTorrent library
 
2
// Copyright (C) 2005-2007, Jari Sundell
 
3
//
 
4
// This program is free software; you can redistribute it and/or modify
 
5
// it under the terms of the GNU General Public License as published by
 
6
// the Free Software Foundation; either version 2 of the License, or
 
7
// (at your option) any later version.
 
8
// 
 
9
// This program is distributed in the hope that it will be useful,
 
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
// GNU General Public License for more details.
 
13
// 
 
14
// You should have received a copy of the GNU General Public License
 
15
// along with this program; if not, write to the Free Software
 
16
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
//
 
18
// In addition, as a special exception, the copyright holders give
 
19
// permission to link the code of portions of this program with the
 
20
// OpenSSL library under certain conditions as described in each
 
21
// individual source file, and distribute linked combinations
 
22
// including the two.
 
23
//
 
24
// You must obey the GNU General Public License in all respects for
 
25
// all of the code used other than OpenSSL.  If you modify file(s)
 
26
// with this exception, you may extend this exception to your version
 
27
// of the file(s), but you are not obligated to do so.  If you do not
 
28
// wish to do so, delete this exception statement from your version.
 
29
// If you delete this exception statement from all source files in the
 
30
// program, then also delete it here.
 
31
//
 
32
// Contact:  Jari Sundell <jaris@ifi.uio.no>
 
33
//
 
34
//           Skomakerveien 33
 
35
//           3185 Skoppum, NORWAY
 
36
 
 
37
#include "config.h"
 
38
 
 
39
#include <iomanip>
 
40
#include <sstream>
 
41
#include <rak/functional.h>
 
42
#include <rak/string_manip.h>
 
43
 
 
44
#include "net/address_list.h"
 
45
#include "net/local_addr.h"
 
46
#include "torrent/connection_manager.h"
 
47
#include "torrent/download_info.h"
 
48
#include "torrent/exceptions.h"
 
49
#include "torrent/http.h"
 
50
#include "torrent/object_stream.h"
 
51
#include "torrent/tracker_list.h"
 
52
 
 
53
#include "tracker_http.h"
 
54
 
 
55
#include "globals.h"
 
56
#include "manager.h"
 
57
 
 
58
namespace torrent {
 
59
 
 
60
TrackerHttp::TrackerHttp(TrackerList* parent, const std::string& url) :
 
61
  Tracker(parent, url),
 
62
 
 
63
  m_get(Http::call_factory()),
 
64
  m_data(NULL) {
 
65
 
 
66
  m_get->signal_done().connect(sigc::mem_fun(*this, &TrackerHttp::receive_done));
 
67
  m_get->signal_failed().connect(sigc::mem_fun(*this, &TrackerHttp::receive_failed));
 
68
 
 
69
  // Haven't considered if this needs any stronger error detection,
 
70
  // can dropping the '?' be used for malicious purposes?
 
71
  size_t delim = url.rfind('?');
 
72
 
 
73
  m_dropDeliminator = delim != std::string::npos &&
 
74
    url.find('/', delim) == std::string::npos;
 
75
}
 
76
 
 
77
TrackerHttp::~TrackerHttp() {
 
78
  delete m_get;
 
79
  delete m_data;
 
80
}
 
81
 
 
82
bool
 
83
TrackerHttp::is_busy() const {
 
84
  return m_data != NULL;
 
85
}
 
86
 
 
87
void
 
88
TrackerHttp::send_state(int state) {
 
89
  close();
 
90
 
 
91
  if (m_parent == NULL)
 
92
    throw internal_error("TrackerHttp::send_state(...) does not have a valid m_parent.");
 
93
 
 
94
  std::stringstream s;
 
95
  s.imbue(std::locale::classic());
 
96
 
 
97
  char hash[61];
 
98
  char localId[61];
 
99
 
 
100
  DownloadInfo* info = m_parent->info();
 
101
 
 
102
  *rak::copy_escape_html(info->hash().begin(), info->hash().end(), hash) = '\0';
 
103
  *rak::copy_escape_html(info->local_id().begin(), info->local_id().end(), localId) = '\0';
 
104
 
 
105
  s << m_url
 
106
    << (m_dropDeliminator ? '&' : '?')
 
107
    << "info_hash=" << hash
 
108
    << "&peer_id=" << localId;
 
109
 
 
110
  if (m_parent->key())
 
111
    s << "&key=" << std::hex << std::setw(8) << std::setfill('0') << m_parent->key() << std::dec;
 
112
 
 
113
  if (!m_trackerId.empty())
 
114
    s << "&trackerid=" << rak::copy_escape_html(m_trackerId);
 
115
 
 
116
  const rak::socket_address* localAddress = rak::socket_address::cast_from(manager->connection_manager()->local_address());
 
117
 
 
118
  if (!localAddress->is_address_any())
 
119
    s << "&ip=" << localAddress->address_str();
 
120
  
 
121
#ifdef RAK_USE_INET6
 
122
  if (localAddress->is_address_any() || localAddress->family() != rak::socket_address::pf_inet6) {
 
123
    rak::socket_address local_v6;
 
124
    if (get_local_address(rak::socket_address::af_inet6, &local_v6))
 
125
      s << "&ipv6=" << rak::copy_escape_html(local_v6.address_str());
 
126
  }
 
127
#endif
 
128
 
 
129
  if (info->is_compact())
 
130
    s << "&compact=1";
 
131
 
 
132
  if (m_parent->numwant() >= 0 && state != DownloadInfo::STOPPED)
 
133
    s << "&numwant=" << m_parent->numwant();
 
134
 
 
135
  if (manager->connection_manager()->listen_port())
 
136
    s << "&port=" << manager->connection_manager()->listen_port();
 
137
 
 
138
  s << "&uploaded=" << info->uploaded_adjusted()
 
139
    << "&downloaded=" << info->completed_adjusted()
 
140
    << "&left=" << info->slot_left()();
 
141
 
 
142
  switch(state) {
 
143
  case DownloadInfo::STARTED:
 
144
    s << "&event=started";
 
145
    break;
 
146
  case DownloadInfo::STOPPED:
 
147
    s << "&event=stopped";
 
148
    break;
 
149
  case DownloadInfo::COMPLETED:
 
150
    s << "&event=completed";
 
151
    break;
 
152
  default:
 
153
    break;
 
154
  }
 
155
 
 
156
  m_data = new std::stringstream();
 
157
 
 
158
  m_get->set_url(s.str());
 
159
  m_get->set_stream(m_data);
 
160
  m_get->set_timeout(2 * 60);
 
161
 
 
162
  m_get->start();
 
163
}
 
164
 
 
165
void
 
166
TrackerHttp::close() {
 
167
  if (m_data == NULL)
 
168
    return;
 
169
 
 
170
  m_get->close();
 
171
  m_get->set_stream(NULL);
 
172
 
 
173
  delete m_data;
 
174
  m_data = NULL;
 
175
}
 
176
 
 
177
TrackerHttp::Type
 
178
TrackerHttp::type() const {
 
179
  return TRACKER_HTTP;
 
180
}
 
181
 
 
182
void
 
183
TrackerHttp::receive_done() {
 
184
  if (m_data == NULL)
 
185
    throw internal_error("TrackerHttp::receive_done() called on an invalid object");
 
186
 
 
187
  DownloadInfo* info = m_parent->info();
 
188
 
 
189
  if (!info->signal_tracker_dump().empty()) {
 
190
    std::string dump = m_data->str();
 
191
 
 
192
    info->signal_tracker_dump().emit(m_get->url(), dump.c_str(), dump.size());
 
193
  }
 
194
 
 
195
  Object b;
 
196
  *m_data >> b;
 
197
 
 
198
  if (m_data->fail())
 
199
    return receive_failed("Could not parse bencoded data");
 
200
 
 
201
  if (!b.is_map())
 
202
    return receive_failed("Root not a bencoded map");
 
203
 
 
204
  if (b.has_key("failure reason"))
 
205
    return receive_failed("Failure reason \"" +
 
206
                         (b.get_key("failure reason").is_string() ?
 
207
                          b.get_key_string("failure reason") :
 
208
                          std::string("failure reason not a string"))
 
209
                         + "\"");
 
210
 
 
211
  if (b.has_key_value("interval"))
 
212
    set_normal_interval(b.get_key_value("interval"));
 
213
  
 
214
  if (b.has_key_value("min interval"))
 
215
    set_min_interval(b.get_key_value("min interval"));
 
216
 
 
217
  if (b.has_key_string("tracker id"))
 
218
    m_trackerId = b.get_key_string("tracker id");
 
219
 
 
220
  if (b.has_key_value("complete") && b.has_key_value("incomplete")) {
 
221
    m_scrapeComplete   = std::max<int64_t>(b.get_key_value("complete"), 0);
 
222
    m_scrapeIncomplete = std::max<int64_t>(b.get_key_value("incomplete"), 0);
 
223
    m_scrapeTimeLast   = rak::timer::current().seconds();
 
224
  }
 
225
 
 
226
  if (b.has_key_value("downloaded"))
 
227
    m_scrapeDownloaded = std::max<int64_t>(b.get_key_value("downloaded"), 0);
 
228
 
 
229
  AddressList l;
 
230
 
 
231
  if (!b.has_key("peers")
 
232
#ifdef RAK_USE_INET6
 
233
      && !b.has_key("peers6")
 
234
#endif
 
235
  ) {
 
236
    return receive_failed("No peers returned");
 
237
  }
 
238
 
 
239
  if (b.has_key("peers")) {
 
240
    try {
 
241
      // Due to some trackers sending the wrong type when no peers are
 
242
      // available, don't bork on it.
 
243
      if (b.get_key("peers").is_string())
 
244
        l.parse_address_compact(b.get_key_string("peers"));
 
245
 
 
246
      else if (b.get_key("peers").is_list())
 
247
        l.parse_address_normal(b.get_key_list("peers"));
 
248
 
 
249
    } catch (bencode_error& e) {
 
250
      return receive_failed(e.what());
 
251
    }
 
252
  }
 
253
 
 
254
#ifdef RAK_USE_INET6
 
255
  if (b.has_key("peers6")) {
 
256
    l.parse_address_compact_ipv6(b.get_key_string("peers6"));
 
257
  }
 
258
#endif
 
259
 
 
260
  close();
 
261
  m_parent->receive_success(this, &l);
 
262
}
 
263
 
 
264
void
 
265
TrackerHttp::receive_failed(std::string msg) {
 
266
  // Does the order matter?
 
267
  close();
 
268
  m_parent->receive_failed(this, msg);
 
269
}
 
270
 
 
271
}