~ubuntu-branches/ubuntu/wily/libtorrent/wily-proposed

« back to all changes in this revision

Viewing changes to .pc/spelling-fixes/src/download/download_constructor.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 <cstdio>
 
40
#include <cstring>
 
41
#include <string.h>
 
42
#include <rak/functional.h>
 
43
#include <rak/string_manip.h>
 
44
 
 
45
#include "download/download_wrapper.h"
 
46
#include "torrent/dht_manager.h"
 
47
#include "torrent/exceptions.h"
 
48
#include "torrent/object.h"
 
49
#include "torrent/data/file.h"
 
50
#include "torrent/data/file_list.h"
 
51
#include "tracker/tracker_manager.h"
 
52
 
 
53
#include "download_constructor.h"
 
54
 
 
55
#include "manager.h"
 
56
 
 
57
namespace torrent {
 
58
 
 
59
struct download_constructor_is_single_path {
 
60
  bool operator () (Object::map_type::const_reference v) const {
 
61
    return
 
62
      std::strncmp(v.first.c_str(), "name.", sizeof("name.") - 1) == 0 &&
 
63
      v.second.is_string();
 
64
  }
 
65
};
 
66
 
 
67
struct download_constructor_is_multi_path {
 
68
  bool operator () (Object::map_type::const_reference v) const {
 
69
    return
 
70
      std::strncmp(v.first.c_str(), "path.", sizeof("path.") - 1) == 0 &&
 
71
      v.second.is_list();
 
72
  }
 
73
};
 
74
 
 
75
struct download_constructor_encoding_match :
 
76
    public std::binary_function<const Path&, const char*, bool> {
 
77
 
 
78
  bool operator () (const Path& p, const char* enc) {
 
79
    return strcasecmp(p.encoding().c_str(), enc) == 0;
 
80
  }
 
81
};
 
82
 
 
83
void
 
84
DownloadConstructor::initialize(Object& b) {
 
85
  if (!b.has_key_map("info") && b.has_key_string("magnet-uri"))
 
86
    parse_magnet_uri(b, b.get_key_string("magnet-uri"));
 
87
 
 
88
  if (b.has_key_string("encoding"))
 
89
    m_defaultEncoding = b.get_key_string("encoding");
 
90
 
 
91
  if (b.has_key_value("creation date"))
 
92
    m_download->info()->set_creation_date(b.get_key_value("creation date"));
 
93
 
 
94
  m_download->info()->change_flags(DownloadInfo::flag_private,
 
95
                                   b.get_key("info").has_key_value("private") && 
 
96
                                   b.get_key("info").get_key_value("private") == 1);
 
97
 
 
98
  parse_name(b.get_key("info"));
 
99
  parse_info(b.get_key("info"));
 
100
 
 
101
  parse_tracker(b);
 
102
}
 
103
 
 
104
// Currently using a hack of the path thingie to extract the correct
 
105
// torrent name.
 
106
void
 
107
DownloadConstructor::parse_name(const Object& b) {
 
108
  if (is_invalid_path_element(b.get_key("name")))
 
109
    throw input_error("Bad torrent file, \"name\" is an invalid path name.");
 
110
 
 
111
  std::list<Path> pathList;
 
112
 
 
113
  pathList.push_back(Path());
 
114
  pathList.back().set_encoding(m_defaultEncoding);
 
115
  pathList.back().push_back(b.get_key_string("name"));
 
116
 
 
117
  for (Object::map_const_iterator itr = b.as_map().begin();
 
118
       (itr = std::find_if(itr, b.as_map().end(), download_constructor_is_single_path())) != b.as_map().end();
 
119
       ++itr) {
 
120
    pathList.push_back(Path());
 
121
    pathList.back().set_encoding(itr->first.substr(sizeof("name.") - 1));
 
122
    pathList.back().push_back(itr->second.as_string());
 
123
  }
 
124
 
 
125
  if (pathList.empty())
 
126
    throw input_error("Bad torrent file, an entry has no valid name.");
 
127
 
 
128
  Path name = choose_path(&pathList);
 
129
 
 
130
  if (name.empty())
 
131
    throw internal_error("DownloadConstructor::parse_name(...) Ended up with an empty Path.");
 
132
 
 
133
  m_download->info()->set_name(name.front());
 
134
}
 
135
 
 
136
void
 
137
DownloadConstructor::parse_info(const Object& b) {
 
138
  FileList* fileList = m_download->main()->file_list();
 
139
 
 
140
  if (!fileList->empty())
 
141
    throw internal_error("parse_info received an already initialized Content object.");
 
142
 
 
143
  if (b.flags() & Object::flag_unordered)
 
144
    throw input_error("Download has unordered info dictionary.");
 
145
 
 
146
  uint32_t chunkSize;
 
147
 
 
148
  if (b.has_key_value("meta_download") && b.get_key_value("meta_download"))
 
149
    m_download->info()->set_flags(DownloadInfo::flag_meta_download);
 
150
 
 
151
  if (m_download->info()->is_meta_download()) {
 
152
    if (b.get_key_string("pieces").length() != HashString::size_data)
 
153
      throw input_error("Meta-download has invalid piece data.");
 
154
 
 
155
    chunkSize = 1;
 
156
    parse_single_file(b, chunkSize);
 
157
 
 
158
  } else {
 
159
    chunkSize = b.get_key_value("piece length");
 
160
 
 
161
    if (chunkSize <= (1 << 10) || chunkSize > (128 << 20))
 
162
      throw input_error("Torrent has an invalid \"piece length\".");
 
163
  }
 
164
 
 
165
  if (b.has_key("length")) {
 
166
    parse_single_file(b, chunkSize);
 
167
 
 
168
  } else if (b.has_key("files")) {
 
169
    parse_multi_files(b.get_key("files"), chunkSize);
 
170
    fileList->set_root_dir("./" + m_download->info()->name());
 
171
 
 
172
  } else if (!m_download->info()->is_meta_download()) {
 
173
    throw input_error("Torrent must have either length or files entry.");
 
174
  }
 
175
 
 
176
  if (fileList->size_bytes() == 0 && !m_download->info()->is_meta_download())
 
177
    throw input_error("Torrent has zero length.");
 
178
 
 
179
  // Set chunksize before adding files to make sure the index range is
 
180
  // correct.
 
181
  m_download->set_complete_hash(b.get_key_string("pieces"));
 
182
 
 
183
  if (m_download->complete_hash().size() / 20 < fileList->size_chunks())
 
184
    throw bencode_error("Torrent size and 'info:pieces' length does not match.");
 
185
}
 
186
 
 
187
void
 
188
DownloadConstructor::parse_tracker(const Object& b) {
 
189
  TrackerManager* tracker = m_download->main()->tracker_manager();
 
190
 
 
191
  if (b.has_key_list("announce-list"))
 
192
    std::for_each(b.get_key_list("announce-list").begin(), b.get_key_list("announce-list").end(),
 
193
                  rak::make_mem_fun(this, &DownloadConstructor::add_tracker_group));
 
194
 
 
195
  else if (b.has_key("announce"))
 
196
    add_tracker_single(b.get_key("announce"), 0);
 
197
 
 
198
  else if (!manager->dht_manager()->is_valid() || m_download->info()->is_private())
 
199
    throw bencode_error("Could not find any trackers");
 
200
 
 
201
  if (manager->dht_manager()->is_valid() && !m_download->info()->is_private())
 
202
    tracker->insert(tracker->group_size(), "dht://");
 
203
 
 
204
  if (manager->dht_manager()->is_valid() && b.has_key_list("nodes"))
 
205
    std::for_each(b.get_key_list("nodes").begin(), b.get_key_list("nodes").end(),
 
206
                  rak::make_mem_fun(this, &DownloadConstructor::add_dht_node));
 
207
 
 
208
  tracker->randomize();
 
209
}
 
210
 
 
211
void
 
212
DownloadConstructor::add_tracker_group(const Object& b) {
 
213
  if (!b.is_list())
 
214
    throw bencode_error("Tracker group list not a list");
 
215
 
 
216
  std::for_each(b.as_list().begin(), b.as_list().end(),
 
217
                rak::bind2nd(rak::make_mem_fun(this, &DownloadConstructor::add_tracker_single),
 
218
                             m_download->main()->tracker_manager()->group_size()));
 
219
}
 
220
 
 
221
void
 
222
DownloadConstructor::add_tracker_single(const Object& b, int group) {
 
223
  if (!b.is_string())
 
224
    throw bencode_error("Tracker entry not a string");
 
225
    
 
226
  m_download->main()->tracker_manager()->insert(group, rak::trim_classic(b.as_string()));
 
227
}
 
228
 
 
229
void
 
230
DownloadConstructor::add_dht_node(const Object& b) {
 
231
  if (!b.is_list() || b.as_list().size() < 2)
 
232
    return;
 
233
 
 
234
  Object::list_type::const_iterator el = b.as_list().begin();
 
235
 
 
236
  if (!el->is_string())
 
237
    return;
 
238
 
 
239
  const std::string& host = el->as_string();
 
240
 
 
241
  if (!(++el)->is_value())
 
242
    return;
 
243
 
 
244
  manager->dht_manager()->add_node(host, el->as_value());
 
245
}
 
246
 
 
247
bool
 
248
DownloadConstructor::is_valid_path_element(const Object& b) {
 
249
  return
 
250
    b.is_string() &&
 
251
    b.as_string() != "." &&
 
252
    b.as_string() != ".." &&
 
253
    std::find(b.as_string().begin(), b.as_string().end(), '/') == b.as_string().end() &&
 
254
    std::find(b.as_string().begin(), b.as_string().end(), '\0') == b.as_string().end();
 
255
}
 
256
 
 
257
void
 
258
DownloadConstructor::parse_single_file(const Object& b, uint32_t chunkSize) {
 
259
  if (is_invalid_path_element(b.get_key("name")))
 
260
    throw input_error("Bad torrent file, \"name\" is an invalid path name.");
 
261
 
 
262
  FileList* fileList = m_download->main()->file_list();
 
263
  fileList->initialize(chunkSize == 1 ? 1 : b.get_key_value("length"), chunkSize);
 
264
  fileList->set_multi_file(false);
 
265
 
 
266
  std::list<Path> pathList;
 
267
 
 
268
  pathList.push_back(Path());
 
269
  pathList.back().set_encoding(m_defaultEncoding);
 
270
  pathList.back().push_back(b.get_key_string("name"));
 
271
 
 
272
  for (Object::map_const_iterator itr = b.as_map().begin();
 
273
       (itr = std::find_if(itr, b.as_map().end(), download_constructor_is_single_path())) != b.as_map().end();
 
274
       ++itr) {
 
275
    pathList.push_back(Path());
 
276
    pathList.back().set_encoding(itr->first.substr(sizeof("name.") - 1));
 
277
    pathList.back().push_back(itr->second.as_string());
 
278
  }
 
279
 
 
280
  if (pathList.empty())
 
281
    throw input_error("Bad torrent file, an entry has no valid filename.");
 
282
 
 
283
  *fileList->front()->mutable_path() = choose_path(&pathList);
 
284
  fileList->update_paths(fileList->begin(), fileList->end());  
 
285
}
 
286
 
 
287
void
 
288
DownloadConstructor::parse_multi_files(const Object& b, uint32_t chunkSize) {
 
289
  const Object::list_type& objectList = b.as_list();
 
290
 
 
291
  // Multi file torrent
 
292
  if (objectList.empty())
 
293
    throw input_error("Bad torrent file, entry has no files.");
 
294
 
 
295
  int64_t torrentSize = 0;
 
296
  std::vector<FileList::split_type> splitList(objectList.size());
 
297
  std::vector<FileList::split_type>::iterator splitItr = splitList.begin();
 
298
 
 
299
  for (Object::list_const_iterator listItr = objectList.begin(), listLast = objectList.end(); listItr != listLast; ++listItr, ++splitItr) {
 
300
    std::list<Path> pathList;
 
301
 
 
302
    if (listItr->has_key_list("path"))
 
303
      pathList.push_back(create_path(listItr->get_key_list("path"), m_defaultEncoding));
 
304
 
 
305
    Object::map_const_iterator itr = listItr->as_map().begin();
 
306
    Object::map_const_iterator last = listItr->as_map().end();
 
307
  
 
308
    while ((itr = std::find_if(itr, last, download_constructor_is_multi_path())) != last) {
 
309
      pathList.push_back(create_path(itr->second.as_list(), itr->first.substr(sizeof("path.") - 1)));
 
310
      ++itr;
 
311
    }
 
312
 
 
313
    if (pathList.empty())
 
314
      throw input_error("Bad torrent file, an entry has no valid filename.");
 
315
 
 
316
    int64_t length = listItr->get_key_value("length");
 
317
 
 
318
    if (length < 0 || torrentSize + length < 0)
 
319
      throw input_error("Bad torrent file, invalid length for file.");
 
320
 
 
321
    torrentSize += length;
 
322
    *splitItr = FileList::split_type(length, choose_path(&pathList));
 
323
  }
 
324
 
 
325
  FileList* fileList = m_download->main()->file_list();
 
326
  fileList->set_multi_file(true);
 
327
 
 
328
  fileList->initialize(torrentSize, chunkSize);
 
329
  fileList->split(fileList->begin(), &*splitList.begin(), &*splitList.end());
 
330
  fileList->update_paths(fileList->begin(), fileList->end());  
 
331
}
 
332
 
 
333
inline Path
 
334
DownloadConstructor::create_path(const Object::list_type& plist, const std::string enc) {
 
335
  // Make sure we are given a proper file path.
 
336
  if (plist.empty())
 
337
    throw input_error("Bad torrent file, \"path\" has zero entries.");
 
338
 
 
339
  if (std::find_if(plist.begin(), plist.end(), std::ptr_fun(&DownloadConstructor::is_invalid_path_element)) != plist.end())
 
340
    throw input_error("Bad torrent file, \"path\" has zero entries or a zero lenght entry.");
 
341
 
 
342
  Path p;
 
343
  p.set_encoding(enc);
 
344
 
 
345
  std::transform(plist.begin(), plist.end(), std::back_inserter(p), std::mem_fun_ref<const Object::string_type&>(&Object::as_string));
 
346
 
 
347
  return p;
 
348
}
 
349
 
 
350
inline Path
 
351
DownloadConstructor::choose_path(std::list<Path>* pathList) {
 
352
  std::list<Path>::iterator pathFirst        = pathList->begin();
 
353
  std::list<Path>::iterator pathLast         = pathList->end();
 
354
  EncodingList::const_iterator encodingFirst = m_encodingList->begin();
 
355
  EncodingList::const_iterator encodingLast  = m_encodingList->end();
 
356
  
 
357
  for ( ; encodingFirst != encodingLast; ++encodingFirst) {
 
358
    std::list<Path>::iterator itr = std::find_if(pathFirst, pathLast, rak::bind2nd(download_constructor_encoding_match(), encodingFirst->c_str()));
 
359
    
 
360
    if (itr != pathLast)
 
361
      pathList->splice(pathFirst, *pathList, itr);
 
362
  }
 
363
 
 
364
  return pathList->front();
 
365
}
 
366
 
 
367
static const char*
 
368
parse_base32_sha1(const char* pos, HashString& hash) {
 
369
  HashString::iterator hashItr = hash.begin();
 
370
 
 
371
  static const int base_shift = 8+8-5;
 
372
  int shift = base_shift;
 
373
  uint16_t decoded = 0;
 
374
 
 
375
  while (*pos) {
 
376
    char c = *pos++;
 
377
    uint16_t value;
 
378
 
 
379
    if (c >= 'A' && c <= 'Z')
 
380
      value = c - 'A';
 
381
    else if (c >= 'a' && c <= 'z')
 
382
      value = c - 'a';
 
383
    else if (c >= '2' && c <= '7')
 
384
      value = 26 + c - '2';
 
385
    else if (c == '&')
 
386
      break;
 
387
    else
 
388
      return NULL;
 
389
 
 
390
    decoded |= (value << shift);
 
391
    if (shift <= 8) {
 
392
      // Too many characters for a base32 SHA1.
 
393
      if (hashItr == hash.end())
 
394
        return NULL;
 
395
 
 
396
      *hashItr++ = (decoded >> 8);
 
397
      decoded <<= 8;
 
398
      shift += 3;
 
399
    } else {
 
400
      shift -= 5;
 
401
    }
 
402
  }
 
403
 
 
404
  return hashItr != hash.end() || shift != base_shift ? NULL : pos;
 
405
}
 
406
 
 
407
void
 
408
DownloadConstructor::parse_magnet_uri(Object& b, const std::string& uri) {
 
409
  if (std::strncmp(uri.c_str(), "magnet:?", 8))
 
410
    throw input_error("Invalid magnet URI.");
 
411
 
 
412
  const char* pos = uri.c_str() + 8;
 
413
 
 
414
  Object trackers(Object::create_list());
 
415
  HashString hash;
 
416
  bool hashValid = false;
 
417
 
 
418
  while (*pos) {
 
419
    const char* tagStart = pos;
 
420
    while (*pos != '=')
 
421
      if (!*pos++)
 
422
        break;
 
423
 
 
424
    raw_string tag(tagStart, pos - tagStart);
 
425
    pos++;
 
426
 
 
427
    // hash may be base32 encoded (optional in BEP 0009 and common practice)
 
428
    if (raw_bencode_equal_c_str(tag, "xt")) {
 
429
      if (strncmp(pos, "urn:btih:", 9))
 
430
        throw input_error("Invalid magnet URI.");
 
431
 
 
432
      pos += 9;
 
433
 
 
434
      const char* nextPos = parse_base32_sha1(pos, hash);
 
435
      if (nextPos != NULL) {
 
436
        pos = nextPos;
 
437
        hashValid = true;
 
438
        continue;
 
439
      }
 
440
    }
 
441
 
 
442
    // everything else, including sometimes the hash, is url encoded.
 
443
    std::string decoded;
 
444
    while (*pos) {
 
445
      char c = *pos++;
 
446
      if (c == '%') {
 
447
        if (sscanf(pos, "%02hhx", &c) != 1)
 
448
          throw input_error("Invalid magnet URI.");
 
449
 
 
450
        pos += 2;
 
451
 
 
452
      } else if (c == '&') {
 
453
        break;
 
454
      }
 
455
 
 
456
      decoded.push_back(c);
 
457
    }
 
458
 
 
459
    if (raw_bencode_equal_c_str(tag, "xt")) {
 
460
      // url-encoded hash as per magnet URN specs
 
461
      if (decoded.length() == hash.size_data) {
 
462
        hash = *HashString::cast_from(decoded);
 
463
        hashValid = true;
 
464
 
 
465
      // hex-encoded hash as per BEP 0009
 
466
      } else if (decoded.length() == hash.size_data * 2) {
 
467
        std::string::iterator hexItr = decoded.begin();
 
468
        for (HashString::iterator itr = hash.begin(), last = hash.end(); itr != last; itr++, hexItr += 2)
 
469
          *itr = (rak::hexchar_to_value(*hexItr) << 4) + rak::hexchar_to_value(*(hexItr + 1));
 
470
        hashValid = true;
 
471
 
 
472
      } else {
 
473
        throw input_error("Invalid magnet URI.");
 
474
      }
 
475
 
 
476
    } else if (raw_bencode_equal_c_str(tag, "tr")) {
 
477
      trackers.insert_back(Object::create_list()).insert_back(decoded);
 
478
    }
 
479
    // could also handle "dn" = display name (torrent name), but we can't really use that
 
480
  }
 
481
 
 
482
  if (!hashValid)
 
483
    throw input_error("Invalid magnet URI.");
 
484
 
 
485
  Object& info = b.insert_key("info", Object::create_map());
 
486
  info.insert_key("pieces", hash.str());
 
487
  info.insert_key("name", rak::transform_hex(hash.str()) + ".meta");
 
488
  info.insert_key("meta_download", (int64_t)1);
 
489
 
 
490
  if (!trackers.as_list().empty()) {
 
491
    b.insert_preserve_copy("announce", trackers.as_list().begin()->as_list().begin()->as_string());
 
492
    b.insert_preserve_type("announce-list", trackers);
 
493
  }
 
494
}
 
495
 
 
496
}