2003-10-23 01:00:57 +02:00
|
|
|
/*
|
|
|
|
|
2016-01-18 00:57:46 +01:00
|
|
|
Copyright (c) 2003-2016, Arvid Norberg
|
2003-10-23 01:00:57 +02:00
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
* Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
* Neither the name of the author nor the names of its
|
|
|
|
contributors may be used to endorse or promote products derived
|
|
|
|
from this software without specific prior written permission.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2009-09-06 02:57:01 +02:00
|
|
|
#include "libtorrent/config.hpp"
|
2014-11-17 04:41:01 +01:00
|
|
|
#include "libtorrent/ConvertUTF.h"
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/torrent_info.hpp"
|
2015-04-21 02:23:00 +02:00
|
|
|
#include "libtorrent/string_util.hpp" // is_space, is_i2p_url
|
2003-10-23 01:00:57 +02:00
|
|
|
#include "libtorrent/bencode.hpp"
|
|
|
|
#include "libtorrent/hasher.hpp"
|
|
|
|
#include "libtorrent/entry.hpp"
|
2008-07-01 01:14:31 +02:00
|
|
|
#include "libtorrent/file.hpp"
|
2008-12-01 08:52:59 +01:00
|
|
|
#include "libtorrent/utf8.hpp"
|
2009-11-25 07:55:34 +01:00
|
|
|
#include "libtorrent/time.hpp"
|
2015-11-11 07:17:36 +01:00
|
|
|
#include "libtorrent/random.hpp"
|
2011-06-06 09:47:29 +02:00
|
|
|
#include "libtorrent/invariant_check.hpp"
|
2014-07-06 21:18:00 +02:00
|
|
|
#include "libtorrent/aux_/session_settings.hpp"
|
2015-03-15 00:10:20 +01:00
|
|
|
#include "libtorrent/aux_/escape_string.hpp" // maybe_url_encode
|
2015-04-24 08:06:41 +02:00
|
|
|
#include "libtorrent/aux_/merkle.hpp" // for merkle_*
|
2015-09-18 06:23:45 +02:00
|
|
|
#include "libtorrent/aux_/time.hpp"
|
2014-02-01 04:47:29 +01:00
|
|
|
#include "libtorrent/add_torrent_params.hpp"
|
|
|
|
#include "libtorrent/magnet_uri.hpp"
|
2015-09-18 06:23:45 +02:00
|
|
|
#include "libtorrent/announce_entry.hpp"
|
2016-06-04 16:01:43 +02:00
|
|
|
#include "libtorrent/hex.hpp" // to_hex
|
2003-10-23 01:00:57 +02:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
#include "libtorrent/lazy_entry.hpp"
|
|
|
|
#endif
|
|
|
|
|
2016-04-30 22:53:20 +02:00
|
|
|
#include <unordered_set>
|
2015-04-21 02:23:00 +02:00
|
|
|
#include <iterator>
|
|
|
|
#include <algorithm>
|
2014-07-06 21:18:00 +02:00
|
|
|
#include <set>
|
2015-04-21 02:23:00 +02:00
|
|
|
#include <ctime>
|
2014-07-06 21:18:00 +02:00
|
|
|
|
2009-03-13 07:09:39 +01:00
|
|
|
namespace libtorrent
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-05-17 20:59:35 +02:00
|
|
|
|
2015-04-20 02:01:27 +02:00
|
|
|
namespace {
|
|
|
|
|
2009-09-09 19:56:25 +02:00
|
|
|
bool valid_path_character(char c)
|
|
|
|
{
|
|
|
|
#ifdef TORRENT_WINDOWS
|
2009-09-25 17:08:28 +02:00
|
|
|
static const char invalid_chars[] = "?<>\"|\b*:";
|
2009-09-09 19:56:25 +02:00
|
|
|
#else
|
|
|
|
static const char invalid_chars[] = "";
|
|
|
|
#endif
|
|
|
|
if (c >= 0 && c < 32) return false;
|
2016-07-09 22:26:26 +02:00
|
|
|
return std::strchr(invalid_chars, c) == nullptr;
|
2009-09-09 19:56:25 +02:00
|
|
|
}
|
|
|
|
|
2015-04-20 02:01:27 +02:00
|
|
|
} // anonymous namespace
|
|
|
|
|
2015-12-20 00:28:12 +01:00
|
|
|
// fixes invalid UTF-8 sequences
|
2016-08-21 01:46:55 +02:00
|
|
|
bool verify_encoding(std::string& target)
|
2006-10-13 01:51:10 +02:00
|
|
|
{
|
2014-11-29 10:12:44 +01:00
|
|
|
if (target.empty()) return true;
|
|
|
|
|
2006-10-13 01:51:10 +02:00
|
|
|
std::string tmp_path;
|
2014-07-06 21:18:00 +02:00
|
|
|
tmp_path.reserve(target.size()+5);
|
2006-10-13 01:51:10 +02:00
|
|
|
bool valid_encoding = true;
|
2014-11-17 04:41:01 +01:00
|
|
|
|
2015-08-11 02:03:24 +02:00
|
|
|
UTF8 const* ptr = reinterpret_cast<UTF8 const*>(&target[0]);
|
|
|
|
UTF8 const* end = ptr + target.size();
|
2014-11-17 04:41:01 +01:00
|
|
|
while (ptr < end)
|
2006-10-13 01:51:10 +02:00
|
|
|
{
|
2014-11-17 04:41:01 +01:00
|
|
|
UTF32 codepoint;
|
|
|
|
UTF32* cp = &codepoint;
|
2015-05-17 20:59:35 +02:00
|
|
|
|
2014-11-17 04:41:01 +01:00
|
|
|
// decode a single utf-8 character
|
|
|
|
ConversionResult res = ConvertUTF8toUTF32(&ptr, end, &cp, cp + 1
|
|
|
|
, lenientConversion);
|
|
|
|
|
|
|
|
// this was the last character, and nothing was
|
|
|
|
// written to the destination buffer (i.e. the source character was
|
|
|
|
// truncated)
|
|
|
|
if (res == sourceExhausted
|
|
|
|
|| res == sourceIllegal)
|
2006-10-13 01:51:10 +02:00
|
|
|
{
|
2014-11-17 04:41:01 +01:00
|
|
|
if (cp == &codepoint)
|
2009-09-09 19:56:25 +02:00
|
|
|
{
|
2014-11-17 04:41:01 +01:00
|
|
|
if (res == sourceExhausted)
|
|
|
|
ptr = end;
|
|
|
|
else
|
|
|
|
++ptr;
|
|
|
|
|
|
|
|
codepoint = '_';
|
2009-09-09 19:56:25 +02:00
|
|
|
valid_encoding = false;
|
|
|
|
}
|
2006-10-13 01:51:10 +02:00
|
|
|
}
|
2014-11-17 04:41:01 +01:00
|
|
|
else if ((res != conversionOK && res != targetExhausted)
|
|
|
|
|| codepoint == UNI_REPLACEMENT_CHAR)
|
2006-10-13 01:51:10 +02:00
|
|
|
{
|
2014-11-17 04:41:01 +01:00
|
|
|
// we expect the conversion to fail with targetExhausted, since we
|
|
|
|
// only pass in a single destination character slot. The last
|
|
|
|
// character will succeed though. Also, if the character was replaced,
|
|
|
|
// use our own replacement symbol (underscore).
|
|
|
|
codepoint = '_';
|
2006-10-13 01:51:10 +02:00
|
|
|
valid_encoding = false;
|
|
|
|
}
|
|
|
|
|
2014-11-17 04:41:01 +01:00
|
|
|
// encode codepoint into utf-8
|
|
|
|
cp = &codepoint;
|
|
|
|
UTF8 sequence[5];
|
|
|
|
UTF8* start = sequence;
|
2015-08-11 02:03:24 +02:00
|
|
|
res = ConvertUTF32toUTF8(const_cast<const UTF32**>(&cp), cp + 1, &start, start + 5, lenientConversion);
|
2014-11-17 04:41:01 +01:00
|
|
|
TORRENT_ASSERT(res == conversionOK);
|
2006-10-13 01:51:10 +02:00
|
|
|
|
2014-11-17 04:41:01 +01:00
|
|
|
for (int i = 0; i < start - sequence; ++i)
|
2015-08-11 02:03:24 +02:00
|
|
|
tmp_path += char(sequence[i]);
|
2006-10-13 01:51:10 +02:00
|
|
|
}
|
2014-11-17 04:41:01 +01:00
|
|
|
|
2006-10-13 01:51:10 +02:00
|
|
|
// the encoding was not valid utf-8
|
|
|
|
// save the original encoding and replace the
|
|
|
|
// commonly used path with the correctly
|
|
|
|
// encoded string
|
2008-12-31 11:18:19 +01:00
|
|
|
if (!valid_encoding) target = tmp_path;
|
|
|
|
return valid_encoding;
|
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
void sanitize_append_path_element(std::string& path, string_view element)
|
2009-05-28 09:25:09 +02:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
if (element.size() == 1 && element[0] == '.') return;
|
2009-05-28 09:25:09 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
#define TORRENT_SEPARATOR "\\"
|
|
|
|
#else
|
|
|
|
#define TORRENT_SEPARATOR "/"
|
|
|
|
#endif
|
2016-08-13 13:04:53 +02:00
|
|
|
path.reserve(path.size() + element.size() + 2);
|
2014-07-06 21:18:00 +02:00
|
|
|
int added_separator = 0;
|
|
|
|
if (!path.empty())
|
2008-11-17 09:01:35 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
path += TORRENT_SEPARATOR;
|
|
|
|
added_separator = 1;
|
|
|
|
}
|
2013-12-05 08:40:57 +01:00
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
if (element.empty())
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
path += "_";
|
|
|
|
return;
|
2013-03-08 10:56:19 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
#if !TORRENT_USE_UNC_PATHS && defined TORRENT_WINDOWS
|
|
|
|
// if we're not using UNC paths on windows, there
|
|
|
|
// are certain filenames we're not allowed to use
|
2015-03-14 01:42:27 +01:00
|
|
|
static const char const* reserved_names[] =
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
"con", "prn", "aux", "clock$", "nul",
|
|
|
|
"com0", "com1", "com2", "com3", "com4",
|
|
|
|
"com5", "com6", "com7", "com8", "com9",
|
|
|
|
"lpt0", "lpt1", "lpt2", "lpt3", "lpt4",
|
|
|
|
"lpt5", "lpt6", "lpt7", "lpt8", "lpt9"
|
|
|
|
};
|
|
|
|
int num_names = sizeof(reserved_names)/sizeof(reserved_names[0]);
|
|
|
|
|
|
|
|
// this is not very efficient, but it only affects some specific
|
|
|
|
// windows builds for now anyway (not even the default windows build)
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string pe = element.to_string();
|
2014-07-06 21:18:00 +02:00
|
|
|
char const* file_end = strrchr(pe.c_str(), '.');
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string name = file_end
|
|
|
|
? std::string(pe.data(), file_end)
|
|
|
|
: pe;
|
2014-07-06 21:18:00 +02:00
|
|
|
std::transform(name.begin(), name.end(), name.begin(), &to_lower);
|
|
|
|
char const* str = std::find(reserved_names, reserved_names + num_names, name);
|
|
|
|
if (str != reserved + num_names)
|
2013-03-25 23:35:52 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
pe = "_" + pe;
|
2016-08-13 13:04:53 +02:00
|
|
|
element = string_view();
|
2013-03-25 23:35:52 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
#endif
|
|
|
|
// this counts the number of unicode characters
|
|
|
|
// we've added (which is different from the number
|
|
|
|
// of bytes)
|
|
|
|
int unicode_chars = 0;
|
|
|
|
|
|
|
|
int added = 0;
|
|
|
|
// the number of dots we've added
|
|
|
|
char num_dots = 0;
|
|
|
|
bool found_extension = false;
|
2016-11-27 14:46:53 +01:00
|
|
|
for (std::size_t i = 0; i < element.size(); ++i)
|
2013-03-08 10:56:19 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (element[i] == '/'
|
|
|
|
|| element[i] == '\\'
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
|| element[i] == ':'
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (element[i] == '.') ++num_dots;
|
2015-05-17 20:59:35 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
int last_len = 0;
|
2013-12-05 08:40:57 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if ((element[i] & 0x80) == 0)
|
2013-12-05 08:40:57 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// 1 byte
|
2015-12-20 00:28:12 +01:00
|
|
|
if (valid_path_character(element[i]))
|
|
|
|
{
|
|
|
|
path += element[i];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
path += '_';
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 1;
|
2013-12-05 08:40:57 +01:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
else if ((element[i] & 0xe0) == 0xc0)
|
|
|
|
{
|
|
|
|
// 2 bytes
|
2016-08-13 13:04:53 +02:00
|
|
|
if (element.size() - i < 2
|
2014-07-06 21:18:00 +02:00
|
|
|
|| (element[i+1] & 0xc0) != 0x80)
|
|
|
|
{
|
2015-12-20 00:28:12 +01:00
|
|
|
path += '_';
|
|
|
|
last_len = 1;
|
|
|
|
}
|
|
|
|
else if ((element[i] & 0x1f) == 0)
|
|
|
|
{
|
|
|
|
// overlong sequences are invalid
|
|
|
|
path += '_';
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
path += element[i];
|
2016-11-27 14:46:53 +01:00
|
|
|
path += element[i + 1];
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 2;
|
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
else if ((element[i] & 0xf0) == 0xe0)
|
|
|
|
{
|
|
|
|
// 3 bytes
|
2016-08-13 13:04:53 +02:00
|
|
|
if (element.size() - i < 3
|
2014-07-06 21:18:00 +02:00
|
|
|
|| (element[i+1] & 0xc0) != 0x80
|
|
|
|
|| (element[i+2] & 0xc0) != 0x80
|
|
|
|
)
|
|
|
|
{
|
2015-12-20 00:28:12 +01:00
|
|
|
path += '_';
|
|
|
|
last_len = 1;
|
|
|
|
}
|
|
|
|
else if ((element[i] & 0x0f) == 0)
|
|
|
|
{
|
|
|
|
// overlong sequences are invalid
|
|
|
|
path += '_';
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
path += element[i];
|
2016-11-27 14:46:53 +01:00
|
|
|
path += element[i + 1];
|
|
|
|
path += element[i + 2];
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 3;
|
|
|
|
}
|
|
|
|
i += 2;
|
|
|
|
}
|
|
|
|
else if ((element[i] & 0xf8) == 0xf0)
|
|
|
|
{
|
|
|
|
// 4 bytes
|
2016-08-13 13:04:53 +02:00
|
|
|
if (element.size() - i < 4
|
2016-11-27 14:46:53 +01:00
|
|
|
|| (element[i + 1] & 0xc0) != 0x80
|
|
|
|
|| (element[i + 2] & 0xc0) != 0x80
|
|
|
|
|| (element[i + 3] & 0xc0) != 0x80
|
2014-07-06 21:18:00 +02:00
|
|
|
)
|
|
|
|
{
|
2015-12-20 00:28:12 +01:00
|
|
|
path += '_';
|
|
|
|
last_len = 1;
|
|
|
|
}
|
|
|
|
else if ((element[i] & 0x07) == 0
|
2016-11-27 14:46:53 +01:00
|
|
|
&& (element[i + 1] & 0x3f) == 0)
|
2015-12-20 00:28:12 +01:00
|
|
|
{
|
|
|
|
// overlong sequences are invalid
|
|
|
|
path += '_';
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
path += element[i];
|
2016-11-27 14:46:53 +01:00
|
|
|
path += element[i + 1];
|
|
|
|
path += element[i + 2];
|
|
|
|
path += element[i + 3];
|
2014-07-06 21:18:00 +02:00
|
|
|
last_len = 4;
|
|
|
|
}
|
|
|
|
i += 3;
|
|
|
|
}
|
2015-12-20 00:28:12 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
path += '_';
|
|
|
|
last_len = 1;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
added += last_len;
|
|
|
|
++unicode_chars;
|
|
|
|
|
|
|
|
// any given path element should not
|
|
|
|
// be more than 255 characters
|
|
|
|
// if we exceed 240, pick up any potential
|
|
|
|
// file extension and add that too
|
|
|
|
#ifdef TORRENT_WINDOWS
|
|
|
|
if (unicode_chars >= 240 && !found_extension)
|
|
|
|
#else
|
|
|
|
if (added >= 240 && !found_extension)
|
|
|
|
#endif
|
2013-12-05 08:40:57 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
int dot = -1;
|
2016-11-27 14:46:53 +01:00
|
|
|
for (int j = int(element.size()) - 1;
|
|
|
|
j > std::max(int(element.size()) - 10, int(i)); --j)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
if (element[j] != '.') continue;
|
|
|
|
dot = j;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// there is no extension
|
|
|
|
if (dot == -1) break;
|
|
|
|
found_extension = true;
|
2016-11-27 14:46:53 +01:00
|
|
|
TORRENT_ASSERT(dot > 0);
|
|
|
|
i = std::size_t(dot - 1);
|
2013-12-05 08:40:57 +01:00
|
|
|
}
|
2013-03-08 10:56:19 +01:00
|
|
|
}
|
2013-12-05 08:40:57 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
if (added == num_dots && added <= 2)
|
2013-03-08 10:56:19 +01:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
// revert everything
|
2016-11-27 14:46:53 +01:00
|
|
|
path.erase(path.end() - added - added_separator, path.end());
|
2014-07-06 21:18:00 +02:00
|
|
|
return;
|
2008-11-17 09:01:35 +01:00
|
|
|
}
|
2013-12-05 08:40:57 +01:00
|
|
|
|
2016-01-18 19:07:04 +01:00
|
|
|
#ifdef TORRENT_WINDOWS
|
2014-07-06 21:18:00 +02:00
|
|
|
// remove trailing spaces and dots. These aren't allowed in filenames on windows
|
2016-04-25 23:22:09 +02:00
|
|
|
for (int i = int(path.size()) - 1; i >= 0; --i)
|
2009-05-28 09:25:09 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
if (path[i] != ' ' && path[i] != '.') break;
|
|
|
|
path.resize(i);
|
2015-12-20 00:28:12 +01:00
|
|
|
--added;
|
|
|
|
TORRENT_ASSERT(added >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (added == 0 && added_separator)
|
|
|
|
{
|
|
|
|
// remove the separator added at the beginning
|
2016-11-27 14:46:53 +01:00
|
|
|
path.erase(path.end() - 1);
|
2015-12-20 00:28:12 +01:00
|
|
|
return;
|
2009-05-28 09:25:09 +02:00
|
|
|
}
|
2016-04-08 03:44:24 +02:00
|
|
|
#endif
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
if (path.empty()) path = "_";
|
2009-05-28 09:25:09 +02:00
|
|
|
}
|
|
|
|
|
2015-04-20 02:01:27 +02:00
|
|
|
namespace {
|
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
std::uint32_t get_file_attributes(bdecode_node const& dict)
|
2016-08-08 04:37:10 +02:00
|
|
|
{
|
2016-09-07 23:51:18 +02:00
|
|
|
std::uint32_t file_flags = 0;
|
2016-08-08 04:37:10 +02:00
|
|
|
bdecode_node attr = dict.dict_find_string("attr");
|
|
|
|
if (attr)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < attr.string_length(); ++i)
|
|
|
|
{
|
|
|
|
switch (attr.string_ptr()[i])
|
|
|
|
{
|
|
|
|
case 'l': file_flags |= file_storage::flag_symlink; break;
|
|
|
|
case 'x': file_flags |= file_storage::flag_executable; break;
|
|
|
|
case 'h': file_flags |= file_storage::flag_hidden; break;
|
|
|
|
case 'p': file_flags |= file_storage::flag_pad_file; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return file_flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
// iterates an array of strings and returns the sum of the lengths of all
|
|
|
|
// strings + one additional character per entry (to account for the presumed
|
2016-09-07 23:51:18 +02:00
|
|
|
// forward- or backslash to separate directory entries)
|
2016-08-08 04:37:10 +02:00
|
|
|
int path_length(bdecode_node const& p, error_code& ec)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int const len = p.list_size();
|
|
|
|
for (int i = 0; i < len; ++i)
|
|
|
|
{
|
|
|
|
bdecode_node e = p.list_at(i);
|
|
|
|
if (e.type() != bdecode_node::string_t)
|
|
|
|
{
|
|
|
|
ec = errors::torrent_invalid_name;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ret += e.string_length();
|
|
|
|
}
|
|
|
|
return ret + len;
|
|
|
|
}
|
|
|
|
|
2015-02-17 03:08:47 +01:00
|
|
|
// 'top_level' is extracting the file for a single-file torrent. The
|
2015-01-21 16:46:12 +01:00
|
|
|
// distinction is that the filename is found in "name" rather than
|
|
|
|
// "path"
|
2015-02-17 03:08:47 +01:00
|
|
|
// root_dir is the name of the torrent, unless this is a single file
|
|
|
|
// torrent, in which case it's empty.
|
2015-03-12 06:20:12 +01:00
|
|
|
bool extract_single_file(bdecode_node const& dict, file_storage& files
|
2016-12-19 23:34:01 +01:00
|
|
|
, std::string const& root_dir, std::ptrdiff_t const info_ptr_diff, bool top_level
|
2016-08-08 04:37:10 +02:00
|
|
|
, int& pad_file_cnt, error_code& ec)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
if (dict.type() != bdecode_node::dict_t) return false;
|
2016-08-08 04:37:10 +02:00
|
|
|
|
2016-09-07 23:51:18 +02:00
|
|
|
std::uint32_t file_flags = get_file_attributes(dict);
|
2016-08-08 04:37:10 +02:00
|
|
|
|
|
|
|
// symlinks have an implied "size" of zero. i.e. they use up 0 bytes of
|
|
|
|
// the torrent payload space
|
2016-09-07 23:51:18 +02:00
|
|
|
std::int64_t const file_size = (file_flags & file_storage::flag_symlink)
|
2016-08-08 04:37:10 +02:00
|
|
|
? 0
|
|
|
|
: dict.dict_find_int_value("length", -1);
|
|
|
|
if (file_size < 0 )
|
2015-01-21 16:46:12 +01:00
|
|
|
{
|
|
|
|
ec = errors::torrent_invalid_length;
|
2013-02-25 02:54:38 +01:00
|
|
|
return false;
|
2015-01-21 16:46:12 +01:00
|
|
|
}
|
2005-08-17 03:35:37 +02:00
|
|
|
|
2016-12-19 23:34:01 +01:00
|
|
|
std::time_t const mtime = std::time_t(dict.dict_find_int_value("mtime", 0));
|
2005-08-17 03:35:37 +02:00
|
|
|
|
2010-11-25 00:49:22 +01:00
|
|
|
std::string path = root_dir;
|
2016-06-20 17:32:06 +02:00
|
|
|
char const* filename = nullptr;
|
2015-01-21 16:46:12 +01:00
|
|
|
int filename_len = 0;
|
|
|
|
|
|
|
|
if (top_level)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-02-17 03:08:47 +01:00
|
|
|
// prefer the name.utf-8 because if it exists, it is more likely to be
|
|
|
|
// correctly encoded
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node p = dict.dict_find_string("name.utf-8");
|
|
|
|
if (!p) p = dict.dict_find_string("name");
|
|
|
|
if (!p || p.string_length() == 0)
|
2015-01-21 16:46:12 +01:00
|
|
|
{
|
|
|
|
ec = errors::torrent_missing_name;
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
2015-01-21 16:46:12 +01:00
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
filename = p.string_ptr() + info_ptr_diff;
|
|
|
|
filename_len = p.string_length();
|
2016-08-13 13:04:53 +02:00
|
|
|
sanitize_append_path_element(path, p.string_value());
|
2015-01-21 16:46:12 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node p = dict.dict_find_list("path.utf-8");
|
|
|
|
if (!p) p = dict.dict_find_list("path");
|
2015-01-21 16:46:12 +01:00
|
|
|
|
2016-08-08 04:37:10 +02:00
|
|
|
if (p && p.list_size() > 0)
|
2015-01-21 16:46:12 +01:00
|
|
|
{
|
2016-12-19 23:34:01 +01:00
|
|
|
std::size_t const preallocate = path.size() + path_length(p, ec);
|
2016-08-08 04:37:10 +02:00
|
|
|
if (ec) return false;
|
|
|
|
path.reserve(preallocate);
|
|
|
|
|
|
|
|
for (int i = 0, end(p.list_size()); i < end; ++i)
|
2015-01-21 16:46:12 +01:00
|
|
|
{
|
2016-08-08 04:37:10 +02:00
|
|
|
bdecode_node e = p.list_at(i);
|
|
|
|
if (i == end - 1)
|
|
|
|
{
|
|
|
|
filename = e.string_ptr() + info_ptr_diff;
|
|
|
|
filename_len = e.string_length();
|
|
|
|
}
|
2016-08-21 22:28:49 +02:00
|
|
|
sanitize_append_path_element(path, e.string_value());
|
2015-01-21 16:46:12 +01:00
|
|
|
}
|
2015-02-17 03:08:47 +01:00
|
|
|
}
|
2016-08-08 04:37:10 +02:00
|
|
|
else if (file_flags & file_storage::flag_pad_file)
|
2015-02-17 03:08:47 +01:00
|
|
|
{
|
2016-08-08 04:37:10 +02:00
|
|
|
// pad files don't need a path element, we'll just store them
|
|
|
|
// under the .pad directory
|
|
|
|
char cnt[10];
|
2016-12-19 23:34:01 +01:00
|
|
|
std::snprintf(cnt, sizeof(cnt), "%d", pad_file_cnt);
|
2016-08-08 04:37:10 +02:00
|
|
|
path = combine_path(".pad", cnt);
|
|
|
|
++pad_file_cnt;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ec = errors::torrent_missing_name;
|
|
|
|
return false;
|
2015-01-21 16:46:12 +01:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2008-10-03 07:49:41 +02:00
|
|
|
|
|
|
|
// bitcomet pad file
|
2010-11-25 00:49:22 +01:00
|
|
|
if (path.find("_____padding_file_") != std::string::npos)
|
2015-01-21 16:46:12 +01:00
|
|
|
file_flags = file_storage::flag_pad_file;
|
2010-11-25 00:49:22 +01:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node fh = dict.dict_find_string("sha1");
|
2016-06-20 17:32:06 +02:00
|
|
|
char const* filehash = nullptr;
|
2015-03-12 06:20:12 +01:00
|
|
|
if (fh && fh.string_length() == 20)
|
|
|
|
filehash = fh.string_ptr() + info_ptr_diff;
|
2010-03-27 16:51:30 +01:00
|
|
|
|
2015-01-21 16:46:12 +01:00
|
|
|
std::string symlink_path;
|
2016-08-08 04:37:10 +02:00
|
|
|
if (file_flags & file_storage::flag_symlink)
|
2009-07-01 10:35:45 +02:00
|
|
|
{
|
2016-08-08 04:37:10 +02:00
|
|
|
if (bdecode_node s_p = dict.dict_find_list("symlink path"))
|
2009-07-01 10:35:45 +02:00
|
|
|
{
|
2016-08-08 04:37:10 +02:00
|
|
|
int const preallocate = path_length(s_p, ec);
|
|
|
|
if (ec) return false;
|
|
|
|
symlink_path.reserve(preallocate);
|
|
|
|
for (int i = 0, end(s_p.list_size()); i < end; ++i)
|
|
|
|
{
|
2016-08-21 22:28:49 +02:00
|
|
|
auto pe = s_p.list_at(i).string_value();
|
|
|
|
sanitize_append_path_element(symlink_path, pe);
|
2016-08-08 04:37:10 +02:00
|
|
|
}
|
2009-07-01 10:35:45 +02:00
|
|
|
}
|
|
|
|
}
|
2014-06-18 08:20:12 +02:00
|
|
|
else
|
|
|
|
{
|
2015-01-21 16:46:12 +01:00
|
|
|
file_flags &= ~file_storage::flag_symlink;
|
2014-06-18 08:20:12 +02:00
|
|
|
}
|
2009-07-01 10:35:45 +02:00
|
|
|
|
2016-11-27 14:46:53 +01:00
|
|
|
if (filename_len > int(path.length())
|
2015-01-27 02:09:16 +01:00
|
|
|
|| path.compare(path.size() - filename_len, filename_len, filename
|
|
|
|
, filename_len) != 0)
|
2015-01-21 16:46:12 +01:00
|
|
|
{
|
|
|
|
// if the filename was sanitized and differ, clear it to just use path
|
2016-06-20 17:32:06 +02:00
|
|
|
filename = nullptr;
|
2015-01-21 16:46:12 +01:00
|
|
|
filename_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
files.add_file_borrow(filename, filename_len, path, file_size, file_flags, filehash
|
|
|
|
, mtime, symlink_path);
|
2007-12-28 21:11:10 +01:00
|
|
|
return true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
struct string_hash_no_case
|
|
|
|
{
|
2016-12-19 23:34:01 +01:00
|
|
|
std::size_t operator()(std::string const& s) const
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
char const* s1 = s.c_str();
|
|
|
|
size_t ret = 5381;
|
|
|
|
int c;
|
|
|
|
|
2016-04-17 22:56:07 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
c = *s1++;
|
|
|
|
if (c == 0)
|
|
|
|
break;
|
2016-11-21 16:08:26 +01:00
|
|
|
ret = (ret * 33) ^ to_lower(char(c));
|
2016-04-17 22:56:07 +02:00
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct string_eq_no_case
|
|
|
|
{
|
|
|
|
bool operator()(std::string const& lhs, std::string const& rhs) const
|
|
|
|
{
|
|
|
|
char c1, c2;
|
|
|
|
char const* s1 = lhs.c_str();
|
|
|
|
char const* s2 = rhs.c_str();
|
2015-05-17 20:59:35 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
while (*s1 != 0 && *s2 != 0)
|
|
|
|
{
|
|
|
|
c1 = to_lower(*s1);
|
|
|
|
c2 = to_lower(*s2);
|
|
|
|
if (c1 != c2) return false;
|
|
|
|
++s1;
|
|
|
|
++s2;
|
|
|
|
}
|
|
|
|
return *s1 == *s2;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-02-17 03:08:47 +01:00
|
|
|
// root_dir is the name of the torrent, unless this is a single file
|
|
|
|
// torrent, in which case it's empty.
|
2015-03-12 06:20:12 +01:00
|
|
|
bool extract_files(bdecode_node const& list, file_storage& target
|
2015-01-21 16:46:12 +01:00
|
|
|
, std::string const& root_dir, ptrdiff_t info_ptr_diff, error_code& ec)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
if (list.type() != bdecode_node::list_t)
|
2015-01-21 16:46:12 +01:00
|
|
|
{
|
|
|
|
ec = errors::torrent_file_parse_failed;
|
|
|
|
return false;
|
|
|
|
}
|
2009-11-26 07:54:52 +01:00
|
|
|
target.reserve(list.list_size());
|
2013-02-24 11:16:57 +01:00
|
|
|
|
2016-08-08 04:37:10 +02:00
|
|
|
// this is the counter used to name pad files
|
|
|
|
int pad_file_cnt = 0;
|
2008-05-14 07:29:42 +02:00
|
|
|
for (int i = 0, end(list.list_size()); i < end; ++i)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
if (!extract_single_file(list.list_at(i), target, root_dir
|
2016-08-08 04:37:10 +02:00
|
|
|
, info_ptr_diff, false, pad_file_cnt, ec))
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2007-12-28 21:11:10 +01:00
|
|
|
return true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2015-04-20 02:01:27 +02:00
|
|
|
int load_file(std::string const& filename, std::vector<char>& v
|
2016-09-22 04:54:42 +02:00
|
|
|
, error_code& ec)
|
2015-04-20 02:01:27 +02:00
|
|
|
{
|
|
|
|
ec.clear();
|
|
|
|
file f;
|
|
|
|
if (!f.open(filename, file::read_only, ec)) return -1;
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t s = f.get_size(ec);
|
2015-04-20 02:01:27 +02:00
|
|
|
if (ec) return -1;
|
2015-08-14 05:52:25 +02:00
|
|
|
v.resize(std::size_t(s));
|
2015-04-20 02:01:27 +02:00
|
|
|
if (s == 0) return 0;
|
|
|
|
file::iovec_t b = {&v[0], size_t(s) };
|
2016-10-27 02:40:56 +02:00
|
|
|
std::int64_t read = f.readv(0, b, ec);
|
2015-04-20 02:01:27 +02:00
|
|
|
if (read != s) return -3;
|
|
|
|
if (ec) return -3;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2012-03-24 02:29:31 +01:00
|
|
|
web_seed_entry::web_seed_entry(std::string const& url_, type_t type_
|
|
|
|
, std::string const& auth_
|
|
|
|
, headers_t const& extra_headers_)
|
2014-07-06 21:18:00 +02:00
|
|
|
: url(url_)
|
|
|
|
, auth(auth_)
|
|
|
|
, extra_headers(extra_headers_)
|
2016-12-13 16:30:36 +01:00
|
|
|
, type(std::uint8_t(type_))
|
2012-03-24 02:29:31 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
torrent_info::torrent_info(torrent_info const& t)
|
|
|
|
: m_files(t.m_files)
|
2009-11-26 07:54:52 +01:00
|
|
|
, m_orig_files(t.m_orig_files)
|
|
|
|
, m_urls(t.m_urls)
|
2010-10-10 20:43:58 +02:00
|
|
|
, m_web_seeds(t.m_web_seeds)
|
2009-11-26 07:54:52 +01:00
|
|
|
, m_nodes(t.m_nodes)
|
2010-08-22 00:10:16 +02:00
|
|
|
, m_merkle_tree(t.m_merkle_tree)
|
|
|
|
, m_piece_hashes(t.m_piece_hashes)
|
2009-11-26 07:54:52 +01:00
|
|
|
, m_comment(t.m_comment)
|
|
|
|
, m_created_by(t.m_created_by)
|
2010-08-22 00:10:16 +02:00
|
|
|
, m_creation_date(t.m_creation_date)
|
|
|
|
, m_info_hash(t.m_info_hash)
|
|
|
|
, m_info_section_size(t.m_info_section_size)
|
2015-10-31 15:53:39 +01:00
|
|
|
, m_merkle_first_leaf(t.m_merkle_first_leaf)
|
2016-06-23 17:34:26 +02:00
|
|
|
, m_flags(t.m_flags)
|
2009-11-26 07:54:52 +01:00
|
|
|
{
|
2014-01-21 20:26:09 +01:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2011-06-06 09:47:29 +02:00
|
|
|
t.check_invariant();
|
|
|
|
#endif
|
2014-12-17 06:33:16 +01:00
|
|
|
if (m_info_section_size == 0) return;
|
2015-03-12 06:20:12 +01:00
|
|
|
TORRENT_ASSERT(m_piece_hashes);
|
2014-12-17 06:33:16 +01:00
|
|
|
|
|
|
|
m_info_section.reset(new char[m_info_section_size]);
|
2016-08-22 17:02:51 +02:00
|
|
|
std::memcpy(m_info_section.get(), t.m_info_section.get(), m_info_section_size);
|
2014-12-17 06:33:16 +01:00
|
|
|
|
|
|
|
ptrdiff_t offset = m_info_section.get() - t.m_info_section.get();
|
|
|
|
|
|
|
|
m_files.apply_pointer_offset(offset);
|
|
|
|
if (m_orig_files)
|
|
|
|
const_cast<file_storage&>(*m_orig_files).apply_pointer_offset(offset);
|
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto& c : m_collections)
|
|
|
|
c.first += offset;
|
2015-03-21 01:12:40 +01:00
|
|
|
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto& st : m_similar_torrents)
|
|
|
|
st += offset;
|
2015-03-21 01:12:40 +01:00
|
|
|
#endif
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
if (m_info_dict)
|
|
|
|
{
|
|
|
|
// make this decoded object point to our copy of the info section
|
|
|
|
// buffer
|
|
|
|
m_info_dict.switch_underlying_buffer(m_info_section.get());
|
|
|
|
}
|
2011-06-06 09:47:29 +02:00
|
|
|
|
2014-12-17 06:33:16 +01:00
|
|
|
m_piece_hashes += offset;
|
|
|
|
TORRENT_ASSERT(m_piece_hashes >= m_info_section.get());
|
|
|
|
TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size);
|
2009-11-26 07:54:52 +01:00
|
|
|
}
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
void torrent_info::resolve_duplicate_filenames()
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
2015-02-17 03:08:47 +01:00
|
|
|
|
2016-06-18 20:01:38 +02:00
|
|
|
std::unordered_set<std::uint32_t> files;
|
2015-02-17 03:08:47 +01:00
|
|
|
|
|
|
|
std::string empty_str;
|
|
|
|
|
|
|
|
// insert all directories first, to make sure no files
|
|
|
|
// are allowed to collied with them
|
2015-03-21 17:55:23 +01:00
|
|
|
m_files.all_path_hashes(files);
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < m_files.end_file(); ++i)
|
2015-02-17 03:08:47 +01:00
|
|
|
{
|
|
|
|
// as long as this file already exists
|
|
|
|
// increase the counter
|
2016-06-18 20:01:38 +02:00
|
|
|
std::uint32_t h = m_files.file_path_hash(i, empty_str);
|
2015-02-17 03:08:47 +01:00
|
|
|
if (!files.insert(h).second)
|
|
|
|
{
|
|
|
|
// This filename appears to already exist!
|
|
|
|
// If this happens, just start over and do it the slow way,
|
|
|
|
// comparing full file names and come up with new names
|
|
|
|
resolve_duplicate_filenames_slow();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::resolve_duplicate_filenames_slow()
|
|
|
|
{
|
|
|
|
INVARIANT_CHECK;
|
2014-07-06 21:18:00 +02:00
|
|
|
int cnt = 0;
|
|
|
|
|
2016-04-30 22:53:20 +02:00
|
|
|
std::unordered_set<std::string, string_hash_no_case, string_eq_no_case> files;
|
2015-02-17 03:08:47 +01:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
std::vector<std::string> const& paths = m_files.paths();
|
2015-02-17 03:08:47 +01:00
|
|
|
files.reserve(paths.size() + m_files.num_files());
|
2014-07-06 21:18:00 +02:00
|
|
|
|
|
|
|
// insert all directories first, to make sure no files
|
|
|
|
// are allowed to collied with them
|
|
|
|
for (std::vector<std::string>::const_iterator i = paths.begin()
|
|
|
|
, end(paths.end()); i != end; ++i)
|
|
|
|
{
|
2015-03-21 17:55:23 +01:00
|
|
|
std::string p = combine_path(m_files.name(), *i);
|
|
|
|
files.insert(p);
|
|
|
|
while (has_parent_path(p))
|
|
|
|
{
|
|
|
|
p = parent_path(p);
|
|
|
|
// we don't want trailing slashes here
|
2015-04-03 22:15:48 +02:00
|
|
|
TORRENT_ASSERT(p[p.size() - 1] == *TORRENT_SEPARATOR);
|
|
|
|
p.resize(p.size() - 1);
|
2015-03-21 17:55:23 +01:00
|
|
|
files.insert(p);
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < m_files.end_file(); ++i)
|
2014-07-06 21:18:00 +02:00
|
|
|
{
|
|
|
|
// as long as this file already exists
|
|
|
|
// increase the counter
|
|
|
|
std::string filename = m_files.file_path(i);
|
|
|
|
if (!files.insert(filename).second)
|
|
|
|
{
|
|
|
|
std::string base = remove_extension(filename);
|
|
|
|
std::string ext = extension(filename);
|
|
|
|
do
|
|
|
|
{
|
|
|
|
++cnt;
|
|
|
|
char new_ext[50];
|
2016-05-17 15:24:06 +02:00
|
|
|
std::snprintf(new_ext, sizeof(new_ext), ".%d%s", cnt, ext.c_str());
|
2014-07-06 21:18:00 +02:00
|
|
|
filename = base + new_ext;
|
|
|
|
}
|
|
|
|
while (!files.insert(filename).second);
|
|
|
|
|
|
|
|
copy_on_write();
|
|
|
|
m_files.rename_file(i, filename);
|
|
|
|
}
|
|
|
|
cnt = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-27 23:15:34 +01:00
|
|
|
void torrent_info::remap_files(file_storage const& f)
|
|
|
|
{
|
2011-06-06 09:47:29 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(is_loaded());
|
2009-11-27 23:15:34 +01:00
|
|
|
// the new specified file storage must have the exact
|
|
|
|
// same size as the current file storage
|
|
|
|
TORRENT_ASSERT(m_files.total_size() == f.total_size());
|
|
|
|
|
|
|
|
if (m_files.total_size() != f.total_size()) return;
|
|
|
|
copy_on_write();
|
|
|
|
m_files = f;
|
2011-01-17 09:02:23 +01:00
|
|
|
m_files.set_num_pieces(m_orig_files->num_pieces());
|
|
|
|
m_files.set_piece_length(m_orig_files->piece_length());
|
2009-11-27 23:15:34 +01:00
|
|
|
}
|
|
|
|
|
2008-08-03 17:14:08 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2015-03-12 06:20:12 +01:00
|
|
|
torrent_info::torrent_info(lazy_entry const& torrent_file, error_code& ec
|
2016-06-23 17:34:26 +02:00
|
|
|
, int const flags)
|
2015-03-12 06:20:12 +01:00
|
|
|
{
|
2015-04-26 02:51:44 +02:00
|
|
|
TORRENT_UNUSED(flags);
|
2015-03-12 06:20:12 +01:00
|
|
|
std::pair<char const*, int> buf = torrent_file.data_section();
|
|
|
|
bdecode_node e;
|
|
|
|
if (bdecode(buf.first, buf.first + buf.second, e, ec) != 0)
|
|
|
|
return;
|
|
|
|
parse_torrent_file(e, ec, 0);
|
|
|
|
}
|
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(lazy_entry const& torrent_file, int const flags)
|
2015-03-12 06:20:12 +01:00
|
|
|
{
|
2015-04-26 02:51:44 +02:00
|
|
|
TORRENT_UNUSED(flags);
|
2015-03-12 06:20:12 +01:00
|
|
|
std::pair<char const*, int> buf = torrent_file.data_section();
|
|
|
|
bdecode_node e;
|
|
|
|
error_code ec;
|
|
|
|
if (bdecode(buf.first, buf.first + buf.second, e, ec) != 0)
|
|
|
|
{
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2015-05-25 23:46:42 +02:00
|
|
|
#else
|
2015-03-12 06:20:12 +01:00
|
|
|
return;
|
2015-05-25 23:46:42 +02:00
|
|
|
#endif
|
2015-03-12 06:20:12 +01:00
|
|
|
}
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
|
|
if (!parse_torrent_file(e, ec, 0))
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2015-03-12 06:20:12 +01:00
|
|
|
#else
|
|
|
|
parse_torrent_file(e, ec, 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2004-03-03 14:47:12 +01:00
|
|
|
// standard constructor that parses a torrent file
|
2008-05-14 07:29:42 +02:00
|
|
|
torrent_info::torrent_info(entry const& torrent_file)
|
|
|
|
{
|
|
|
|
std::vector<char> tmp;
|
2016-10-25 23:27:48 +02:00
|
|
|
std::back_insert_iterator<std::vector<char>> out(tmp);
|
2008-05-14 07:29:42 +02:00
|
|
|
bencode(out, torrent_file);
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
2010-10-28 06:01:59 +02:00
|
|
|
error_code ec;
|
2016-04-27 00:09:11 +02:00
|
|
|
if (tmp.empty() || bdecode(&tmp[0], &tmp[0] + tmp.size(), e, ec) != 0)
|
2009-02-23 02:21:19 +01:00
|
|
|
{
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2015-05-25 23:46:42 +02:00
|
|
|
#else
|
2009-02-23 02:21:19 +01:00
|
|
|
return;
|
2015-05-25 23:46:42 +02:00
|
|
|
#endif
|
2009-02-23 02:21:19 +01:00
|
|
|
}
|
2008-05-14 07:29:42 +02:00
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
2010-11-15 06:10:36 +01:00
|
|
|
if (!parse_torrent_file(e, ec, 0))
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2008-05-14 07:29:42 +02:00
|
|
|
#else
|
2010-12-09 06:39:28 +01:00
|
|
|
parse_torrent_file(e, ec, 0);
|
2007-05-10 00:54:26 +02:00
|
|
|
#endif
|
2011-06-06 09:47:29 +02:00
|
|
|
INVARIANT_CHECK;
|
2008-05-14 07:29:42 +02:00
|
|
|
}
|
2008-08-03 17:14:08 +02:00
|
|
|
#endif
|
2008-05-14 07:29:42 +02:00
|
|
|
|
2009-02-23 02:21:19 +01:00
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(bdecode_node const& torrent_file
|
|
|
|
, int const flags)
|
2004-03-03 14:47:12 +01:00
|
|
|
{
|
2009-02-13 20:01:02 +01:00
|
|
|
error_code ec;
|
2010-11-15 06:10:36 +01:00
|
|
|
if (!parse_torrent_file(torrent_file, ec, flags))
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2004-03-03 14:47:12 +01:00
|
|
|
}
|
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(char const* buffer
|
|
|
|
, int const size
|
|
|
|
, int const flags)
|
2008-05-14 07:29:42 +02:00
|
|
|
{
|
2009-02-13 20:01:02 +01:00
|
|
|
error_code ec;
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
|
|
|
if (bdecode(buffer, buffer + size, e, ec) != 0)
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2009-02-23 02:21:19 +01:00
|
|
|
|
2010-11-15 06:10:36 +01:00
|
|
|
if (!parse_torrent_file(e, ec, flags))
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2008-05-14 07:29:42 +02:00
|
|
|
}
|
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(std::string const& filename
|
|
|
|
, int const flags)
|
2009-02-23 02:21:19 +01:00
|
|
|
{
|
|
|
|
std::vector<char> buf;
|
2011-01-19 11:07:51 +01:00
|
|
|
error_code ec;
|
|
|
|
int ret = load_file(filename, buf, ec);
|
2016-05-15 06:33:06 +02:00
|
|
|
if (ret < 0) throw system_error(ec);
|
2004-10-10 02:42:48 +02:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
2016-04-27 00:09:11 +02:00
|
|
|
if (buf.empty() || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2010-10-28 06:01:59 +02:00
|
|
|
|
2010-11-15 06:10:36 +01:00
|
|
|
if (!parse_torrent_file(e, ec, flags))
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2009-02-23 02:21:19 +01:00
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
2013-08-02 07:03:22 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(std::wstring const& filename
|
|
|
|
, int const flags)
|
2008-05-12 08:35:24 +02:00
|
|
|
{
|
2008-07-01 01:14:31 +02:00
|
|
|
std::vector<char> buf;
|
2011-01-19 11:07:51 +01:00
|
|
|
error_code ec;
|
2016-09-25 04:16:10 +02:00
|
|
|
int ret = load_file(wchar_utf8(filename), buf, ec);
|
2016-05-15 06:33:06 +02:00
|
|
|
if (ret < 0) throw system_error(ec);
|
2008-05-14 07:29:42 +02:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
2016-04-27 00:09:11 +02:00
|
|
|
if (buf.empty() || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2010-10-28 06:01:59 +02:00
|
|
|
|
2010-11-15 06:10:36 +01:00
|
|
|
if (!parse_torrent_file(e, ec, flags))
|
2016-05-15 06:33:06 +02:00
|
|
|
throw system_error(ec);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2009-02-23 02:21:19 +01:00
|
|
|
}
|
2013-08-09 09:36:07 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
void torrent_info::rename_file(file_index_t index, std::wstring const& new_filename)
|
2013-08-09 09:36:07 +02:00
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(is_loaded());
|
2013-08-09 09:36:07 +02:00
|
|
|
copy_on_write();
|
2014-07-05 16:10:25 +02:00
|
|
|
m_files.rename_file_deprecated(index, new_filename);
|
2013-08-09 09:36:07 +02:00
|
|
|
}
|
2013-08-02 07:03:22 +02:00
|
|
|
#endif // TORRENT_NO_DEPRECATE
|
|
|
|
#endif // TORRENT_USE_WSTRING
|
2008-12-01 08:52:59 +01:00
|
|
|
#endif
|
2009-02-23 02:21:19 +01:00
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(bdecode_node const& torrent_file
|
|
|
|
, error_code& ec
|
|
|
|
, int const flags)
|
2009-02-23 02:21:19 +01:00
|
|
|
{
|
2010-11-15 06:10:36 +01:00
|
|
|
parse_torrent_file(torrent_file, ec, flags);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2008-12-01 08:52:59 +01:00
|
|
|
}
|
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(char const* buffer
|
|
|
|
, int const size
|
|
|
|
, error_code& ec
|
|
|
|
, int const flags)
|
2009-02-23 02:21:19 +01:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
|
|
|
if (bdecode(buffer, buffer + size, e, ec) != 0)
|
2009-02-23 02:21:19 +01:00
|
|
|
return;
|
2010-11-15 06:10:36 +01:00
|
|
|
parse_torrent_file(e, ec, flags);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2009-02-23 02:21:19 +01:00
|
|
|
}
|
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(std::string const& filename, error_code& ec
|
|
|
|
, int const flags)
|
2009-02-23 02:21:19 +01:00
|
|
|
{
|
|
|
|
std::vector<char> buf;
|
2011-01-19 11:07:51 +01:00
|
|
|
int ret = load_file(filename, buf, ec);
|
2009-02-23 02:21:19 +01:00
|
|
|
if (ret < 0) return;
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
2016-04-27 00:09:11 +02:00
|
|
|
if (buf.empty() || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
|
2009-02-23 02:21:19 +01:00
|
|
|
return;
|
2010-11-15 06:10:36 +01:00
|
|
|
parse_torrent_file(e, ec, flags);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2009-02-23 02:21:19 +01:00
|
|
|
}
|
|
|
|
|
2009-10-26 02:29:39 +01:00
|
|
|
#if TORRENT_USE_WSTRING
|
2013-08-02 07:03:22 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(std::wstring const& filename
|
|
|
|
, error_code& ec
|
|
|
|
, int const flags)
|
2008-12-01 08:52:59 +01:00
|
|
|
{
|
|
|
|
std::vector<char> buf;
|
2016-09-25 04:16:10 +02:00
|
|
|
int ret = load_file(wchar_utf8(filename), buf, ec);
|
2008-12-01 08:52:59 +01:00
|
|
|
if (ret < 0) return;
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node e;
|
2016-04-27 00:09:11 +02:00
|
|
|
if (buf.empty() || bdecode(&buf[0], &buf[0] + buf.size(), e, ec) != 0)
|
2009-02-23 02:21:19 +01:00
|
|
|
return;
|
2010-11-15 06:10:36 +01:00
|
|
|
parse_torrent_file(e, ec, flags);
|
2011-06-06 09:47:29 +02:00
|
|
|
|
|
|
|
INVARIANT_CHECK;
|
2008-05-12 08:35:24 +02:00
|
|
|
}
|
2013-08-02 07:03:22 +02:00
|
|
|
#endif // TORRENT_NO_DEPRECATE
|
|
|
|
#endif // TORRENT_USE_WSTRING
|
2008-05-12 08:35:24 +02:00
|
|
|
|
2009-02-23 02:21:19 +01:00
|
|
|
// constructor used for creating new torrents
|
|
|
|
// will not contain any hashes, comments, creation date
|
|
|
|
// just the necessary to use it with piece manager
|
|
|
|
// used for torrents with no metadata
|
2016-06-23 17:34:26 +02:00
|
|
|
torrent_info::torrent_info(sha1_hash const& info_hash, int const flags)
|
|
|
|
: m_info_hash(info_hash)
|
2015-04-26 02:51:44 +02:00
|
|
|
{
|
|
|
|
TORRENT_UNUSED(flags);
|
|
|
|
}
|
2009-02-23 02:21:19 +01:00
|
|
|
|
2016-07-10 13:34:45 +02:00
|
|
|
torrent_info::~torrent_info() = default;
|
2004-10-29 15:21:09 +02:00
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
sha1_hash torrent_info::hash_for_piece(piece_index_t const index) const
|
2015-09-18 06:23:45 +02:00
|
|
|
{ return sha1_hash(hash_for_piece_ptr(index)); }
|
|
|
|
|
2008-12-24 21:07:34 +01:00
|
|
|
void torrent_info::copy_on_write()
|
|
|
|
{
|
2014-07-06 21:18:00 +02:00
|
|
|
TORRENT_ASSERT(is_loaded());
|
2011-06-06 09:47:29 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2008-12-24 21:07:34 +01:00
|
|
|
if (m_orig_files) return;
|
|
|
|
m_orig_files.reset(new file_storage(m_files));
|
|
|
|
}
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
void torrent_info::swap(torrent_info& ti)
|
|
|
|
{
|
2011-06-06 09:47:29 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2007-06-10 22:46:09 +02:00
|
|
|
using std::swap;
|
|
|
|
m_urls.swap(ti.m_urls);
|
2010-10-10 20:43:58 +02:00
|
|
|
m_web_seeds.swap(ti.m_web_seeds);
|
2007-06-10 22:46:09 +02:00
|
|
|
m_files.swap(ti.m_files);
|
2008-12-24 21:07:34 +01:00
|
|
|
m_orig_files.swap(ti.m_orig_files);
|
2007-06-10 22:46:09 +02:00
|
|
|
m_nodes.swap(ti.m_nodes);
|
|
|
|
swap(m_info_hash, ti.m_info_hash);
|
|
|
|
swap(m_creation_date, ti.m_creation_date);
|
|
|
|
m_comment.swap(ti.m_comment);
|
|
|
|
m_created_by.swap(ti.m_created_by);
|
2008-05-14 07:29:42 +02:00
|
|
|
swap(m_info_section, ti.m_info_section);
|
|
|
|
swap(m_piece_hashes, ti.m_piece_hashes);
|
2009-11-26 07:54:52 +01:00
|
|
|
m_info_dict.swap(ti.m_info_dict);
|
|
|
|
swap(m_merkle_tree, ti.m_merkle_tree);
|
2016-06-23 17:34:26 +02:00
|
|
|
swap(m_info_section_size, ti.m_info_section_size);
|
|
|
|
swap(m_merkle_first_leaf, ti.m_merkle_first_leaf);
|
|
|
|
swap(m_flags, ti.m_flags);
|
2004-10-10 02:42:48 +02:00
|
|
|
}
|
2004-03-03 14:47:12 +01:00
|
|
|
|
2013-11-17 23:38:37 +01:00
|
|
|
std::string torrent_info::ssl_cert() const
|
|
|
|
{
|
2014-01-20 02:01:03 +01:00
|
|
|
// this is parsed lazily
|
2015-03-12 06:20:12 +01:00
|
|
|
if (!m_info_dict)
|
2014-01-20 02:01:03 +01:00
|
|
|
{
|
|
|
|
error_code ec;
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode(m_info_section.get(), m_info_section.get()
|
2014-01-20 02:01:03 +01:00
|
|
|
+ m_info_section_size, m_info_dict, ec);
|
|
|
|
if (ec) return "";
|
|
|
|
}
|
2015-03-12 06:20:12 +01:00
|
|
|
if (m_info_dict.type() != bdecode_node::dict_t) return "";
|
2016-08-13 13:04:53 +02:00
|
|
|
return m_info_dict.dict_find_string_value("ssl-cert").to_string();
|
2013-11-17 23:38:37 +01:00
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bool torrent_info::parse_info_section(bdecode_node const& info
|
2016-08-26 06:14:11 +02:00
|
|
|
, error_code& ec, int const flags)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
2015-04-26 02:51:44 +02:00
|
|
|
TORRENT_UNUSED(flags);
|
2015-03-12 06:20:12 +01:00
|
|
|
if (info.type() != bdecode_node::dict_t)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_info_no_dict;
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-05-14 07:29:42 +02:00
|
|
|
// hash the info-field to calculate info-hash
|
2016-07-24 00:57:04 +02:00
|
|
|
auto section = info.data_section();
|
|
|
|
m_info_hash = hasher(section).final();
|
|
|
|
if (info.data_section().size() >= (std::numeric_limits<std::uint32_t>::max)())
|
2015-10-31 15:06:15 +01:00
|
|
|
{
|
|
|
|
ec = errors::metadata_too_large;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-05-14 07:29:42 +02:00
|
|
|
// copy the info section
|
2016-07-24 00:57:04 +02:00
|
|
|
m_info_section_size = int(section.size());
|
2008-05-14 07:29:42 +02:00
|
|
|
m_info_section.reset(new char[m_info_section_size]);
|
2016-07-24 00:57:04 +02:00
|
|
|
std::memcpy(m_info_section.get(), section.data(), m_info_section_size);
|
|
|
|
TORRENT_ASSERT(section[0] == 'd');
|
2016-11-20 03:14:16 +01:00
|
|
|
TORRENT_ASSERT(section[m_info_section_size - 1] == 'e');
|
2008-05-14 07:29:42 +02:00
|
|
|
|
2010-11-25 00:49:22 +01:00
|
|
|
// when translating a pointer that points into the 'info' tree's
|
|
|
|
// backing buffer, into a pointer to our copy of the info section,
|
|
|
|
// this is the pointer offset to use.
|
2016-07-24 00:57:04 +02:00
|
|
|
ptrdiff_t const info_ptr_diff = m_info_section.get() - section.data();
|
2010-11-25 00:49:22 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// extract piece length
|
2016-12-05 14:39:53 +01:00
|
|
|
int piece_length = int(info.dict_find_int_value("piece length", -1));
|
2008-05-28 10:44:40 +02:00
|
|
|
if (piece_length <= 0)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_missing_piece_length;
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
2014-07-06 21:18:00 +02:00
|
|
|
file_storage files;
|
|
|
|
files.set_piece_length(piece_length);
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
// extract file name (or the directory name if it's a multifile libtorrent)
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node name_ent = info.dict_find_string("name.utf-8");
|
|
|
|
if (!name_ent) name_ent = info.dict_find_string("name");
|
|
|
|
if (!name_ent)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_missing_name;
|
2008-05-14 07:29:42 +02:00
|
|
|
return false;
|
2007-12-28 21:11:10 +01:00
|
|
|
}
|
2008-05-14 07:29:42 +02:00
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
std::string name;
|
2016-08-13 13:04:53 +02:00
|
|
|
sanitize_append_path_element(name, name_ent.string_value());
|
2016-07-29 08:36:15 +02:00
|
|
|
if (name.empty()) name = aux::to_hex(m_info_hash);
|
2008-12-31 11:18:19 +01:00
|
|
|
|
2004-06-14 01:30:42 +02:00
|
|
|
// extract file list
|
2016-08-26 06:14:11 +02:00
|
|
|
bdecode_node const files_node = info.dict_find_list("files");
|
2015-08-14 05:52:25 +02:00
|
|
|
if (!files_node)
|
2004-06-14 01:30:42 +02:00
|
|
|
{
|
|
|
|
// if there's no list of files, there has to be a length
|
|
|
|
// field.
|
2016-08-08 04:37:10 +02:00
|
|
|
// this is the counter used to name pad files
|
|
|
|
int pad_file_cnt = 0;
|
|
|
|
if (!extract_single_file(info, files, "", info_ptr_diff, true, pad_file_cnt, ec))
|
2013-02-25 02:54:38 +01:00
|
|
|
return false;
|
2014-06-18 08:20:12 +02:00
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
m_flags &= ~multifile;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-08-14 05:52:25 +02:00
|
|
|
if (!extract_files(files_node, files, name, info_ptr_diff, ec))
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
2016-06-23 17:34:26 +02:00
|
|
|
m_flags |= multifile;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
2014-11-17 04:41:01 +01:00
|
|
|
TORRENT_ASSERT(!files.name().empty());
|
2004-06-14 01:30:42 +02:00
|
|
|
|
|
|
|
// extract sha-1 hashes for all pieces
|
|
|
|
// we want this division to round upwards, that's why we have the
|
|
|
|
// extra addition
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
files.set_num_pieces(int((files.total_size() + files.piece_length() - 1)
|
|
|
|
/ files.piece_length()));
|
2008-05-14 07:29:42 +02:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node pieces = info.dict_find_string("pieces");
|
|
|
|
bdecode_node root_hash = info.dict_find_string("root hash");
|
|
|
|
if (!pieces && !root_hash)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_missing_pieces;
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
2015-05-17 20:59:35 +02:00
|
|
|
|
2009-03-13 07:09:39 +01:00
|
|
|
if (pieces)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
if (pieces.string_length() != files.num_pieces() * 20)
|
2009-03-13 07:09:39 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_invalid_hashes;
|
2009-03-13 07:09:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2004-06-14 01:30:42 +02:00
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
m_piece_hashes = pieces.string_ptr() + info_ptr_diff;
|
2009-03-13 07:09:39 +01:00
|
|
|
TORRENT_ASSERT(m_piece_hashes >= m_info_section.get());
|
|
|
|
TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TORRENT_ASSERT(root_hash);
|
2015-03-12 06:20:12 +01:00
|
|
|
if (root_hash.string_length() != 20)
|
2009-03-13 07:09:39 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_invalid_hashes;
|
2009-03-13 07:09:39 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-06-23 17:34:26 +02:00
|
|
|
if (files.num_pieces() >= std::numeric_limits<int>::max()/2)
|
2015-10-31 15:53:39 +01:00
|
|
|
{
|
|
|
|
ec = errors::too_many_pieces_in_torrent;
|
|
|
|
return false;
|
|
|
|
}
|
2016-06-23 17:34:26 +02:00
|
|
|
int const num_leafs = merkle_num_leafs(files.num_pieces());
|
|
|
|
int const num_nodes = merkle_num_nodes(num_leafs);
|
2009-03-13 07:09:39 +01:00
|
|
|
m_merkle_first_leaf = num_nodes - num_leafs;
|
|
|
|
m_merkle_tree.resize(num_nodes);
|
|
|
|
std::memset(&m_merkle_tree[0], 0, num_nodes * 20);
|
2015-03-12 06:20:12 +01:00
|
|
|
m_merkle_tree[0].assign(root_hash.string_ptr());
|
2009-03-13 07:09:39 +01:00
|
|
|
}
|
2005-10-16 18:58:41 +02:00
|
|
|
|
2016-06-23 17:34:26 +02:00
|
|
|
m_flags |= (info.dict_find_int_value("private", 0) != 0)
|
|
|
|
? private_torrent : 0;
|
2011-08-28 23:06:15 +02:00
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
bdecode_node similar = info.dict_find_list("similar");
|
|
|
|
if (similar)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < similar.list_size(); ++i)
|
|
|
|
{
|
|
|
|
if (similar.list_at(i).type() != bdecode_node::string_t)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (similar.list_at(i).string_length() != 20)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m_similar_torrents.push_back(similar.list_at(i).string_ptr()
|
|
|
|
+ info_ptr_diff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bdecode_node collections = info.dict_find_list("collections");
|
|
|
|
if (collections)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < collections.list_size(); ++i)
|
|
|
|
{
|
|
|
|
bdecode_node str = collections.list_at(i);
|
|
|
|
|
|
|
|
if (str.type() != bdecode_node::string_t) continue;
|
|
|
|
|
|
|
|
m_collections.push_back(std::make_pair(str.string_ptr()
|
|
|
|
+ info_ptr_diff, str.string_length()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
|
2014-07-06 21:18:00 +02:00
|
|
|
// now, commit the files structure we just parsed out
|
|
|
|
// into the torrent_info object.
|
2016-12-23 03:27:56 +01:00
|
|
|
m_files.swap(files);
|
2007-12-28 21:11:10 +01:00
|
|
|
return true;
|
2004-06-14 01:30:42 +02:00
|
|
|
}
|
|
|
|
|
2015-09-18 06:23:45 +02:00
|
|
|
bdecode_node torrent_info::info(char const* key) const
|
|
|
|
{
|
|
|
|
if (m_info_dict.type() == bdecode_node::none_t)
|
|
|
|
{
|
|
|
|
error_code ec;
|
|
|
|
bdecode(m_info_section.get(), m_info_section.get()
|
|
|
|
+ m_info_section_size, m_info_dict, ec);
|
2016-04-08 03:44:24 +02:00
|
|
|
if (ec) return bdecode_node();
|
2015-09-18 06:23:45 +02:00
|
|
|
}
|
|
|
|
return m_info_dict.dict_find(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-13 07:09:39 +01:00
|
|
|
bool torrent_info::add_merkle_nodes(std::map<int, sha1_hash> const& subtree
|
2016-12-22 16:42:33 +01:00
|
|
|
, piece_index_t const piece)
|
2009-03-13 07:09:39 +01:00
|
|
|
{
|
2011-06-06 09:47:29 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2016-12-22 16:42:33 +01:00
|
|
|
int n = m_merkle_first_leaf + static_cast<int>(piece);
|
2016-08-26 06:14:11 +02:00
|
|
|
auto const it = subtree.find(n);
|
2015-08-17 15:01:43 +02:00
|
|
|
if (it == subtree.end()) return false;
|
|
|
|
sha1_hash h = it->second;
|
2009-03-13 07:09:39 +01:00
|
|
|
|
|
|
|
// if the verification passes, these are the
|
|
|
|
// nodes to add to our tree
|
|
|
|
std::map<int, sha1_hash> to_add;
|
|
|
|
|
|
|
|
while (n > 0)
|
|
|
|
{
|
2016-07-22 16:29:39 +02:00
|
|
|
int const sibling = merkle_get_sibling(n);
|
|
|
|
int const parent = merkle_get_parent(n);
|
2016-08-26 06:14:11 +02:00
|
|
|
auto const sibling_hash = subtree.find(sibling);
|
2009-03-13 07:09:39 +01:00
|
|
|
if (sibling_hash == subtree.end())
|
|
|
|
return false;
|
|
|
|
to_add[n] = h;
|
|
|
|
to_add[sibling] = sibling_hash->second;
|
|
|
|
hasher hs;
|
|
|
|
if (sibling < n)
|
|
|
|
{
|
2016-07-22 16:29:39 +02:00
|
|
|
hs.update(sibling_hash->second);
|
|
|
|
hs.update(h);
|
2009-03-13 07:09:39 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-07-22 16:29:39 +02:00
|
|
|
hs.update(h);
|
|
|
|
hs.update(sibling_hash->second);
|
2009-03-13 07:09:39 +01:00
|
|
|
}
|
|
|
|
h = hs.final();
|
|
|
|
n = parent;
|
|
|
|
}
|
|
|
|
if (h != m_merkle_tree[0]) return false;
|
|
|
|
|
|
|
|
// the nodes and piece hash matched the root-hash
|
|
|
|
// insert them into our tree
|
|
|
|
|
2016-08-26 06:14:11 +02:00
|
|
|
for (auto const& i : to_add)
|
2009-03-13 07:09:39 +01:00
|
|
|
{
|
2016-08-26 06:14:11 +02:00
|
|
|
m_merkle_tree[i.first] = i.second;
|
2009-03-13 07:09:39 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// builds a list of nodes that are required to verify
|
|
|
|
// the given piece
|
2016-12-22 16:42:33 +01:00
|
|
|
std::map<int, sha1_hash>
|
|
|
|
torrent_info::build_merkle_list(piece_index_t const piece) const
|
2009-03-13 07:09:39 +01:00
|
|
|
{
|
2011-06-06 09:47:29 +02:00
|
|
|
INVARIANT_CHECK;
|
|
|
|
|
2009-03-13 07:09:39 +01:00
|
|
|
std::map<int, sha1_hash> ret;
|
2016-12-22 16:42:33 +01:00
|
|
|
int n = m_merkle_first_leaf + static_cast<int>(piece);
|
2009-03-13 07:09:39 +01:00
|
|
|
ret[n] = m_merkle_tree[n];
|
|
|
|
ret[0] = m_merkle_tree[0];
|
|
|
|
while (n > 0)
|
|
|
|
{
|
|
|
|
int sibling = merkle_get_sibling(n);
|
|
|
|
int parent = merkle_get_parent(n);
|
|
|
|
ret[sibling] = m_merkle_tree[sibling];
|
|
|
|
// we cannot build the tree path if one
|
|
|
|
// of the nodes in the tree is missing
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(m_merkle_tree[sibling] != sha1_hash(nullptr));
|
2009-03-13 07:09:39 +01:00
|
|
|
n = parent;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bool torrent_info::parse_torrent_file(bdecode_node const& torrent_file
|
2016-08-26 06:14:11 +02:00
|
|
|
, error_code& ec, int const flags)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
if (torrent_file.type() != bdecode_node::dict_t)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2009-11-29 08:06:38 +01:00
|
|
|
ec = errors::torrent_is_no_dict;
|
2007-12-28 21:11:10 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node info = torrent_file.dict_find_dict("info");
|
2016-12-12 00:44:52 +01:00
|
|
|
if (!info)
|
2012-10-06 19:51:59 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node link = torrent_file.dict_find_string("magnet-uri");
|
2014-02-01 04:47:29 +01:00
|
|
|
if (link)
|
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
auto uri = link.string_value();
|
2014-02-01 04:47:29 +01:00
|
|
|
|
|
|
|
add_torrent_params p;
|
2016-08-13 13:04:53 +02:00
|
|
|
parse_magnet_uri(uri.to_string(), p, ec);
|
2014-02-01 04:47:29 +01:00
|
|
|
if (ec) return false;
|
|
|
|
|
|
|
|
m_info_hash = p.info_hash;
|
2016-10-19 23:28:35 +02:00
|
|
|
m_urls.reserve(m_urls.size() + p.trackers.size());
|
2016-10-08 20:07:11 +02:00
|
|
|
for (auto const& url : p.trackers)
|
|
|
|
m_urls.push_back(announce_entry(url));
|
2014-02-01 04:47:29 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-06 19:51:59 +02:00
|
|
|
ec = errors::torrent_missing_info;
|
|
|
|
return false;
|
|
|
|
}
|
2015-03-12 06:20:12 +01:00
|
|
|
if (!parse_info_section(info, ec, flags)) return false;
|
2014-07-06 21:18:00 +02:00
|
|
|
resolve_duplicate_filenames();
|
2012-10-06 19:51:59 +02:00
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
bdecode_node similar = torrent_file.dict_find_list("similar");
|
|
|
|
if (similar)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < similar.list_size(); ++i)
|
|
|
|
{
|
|
|
|
if (similar.list_at(i).type() != bdecode_node::string_t)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (similar.list_at(i).string_length() != 20)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m_owned_similar_torrents.push_back(
|
|
|
|
sha1_hash(similar.list_at(i).string_ptr()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bdecode_node collections = torrent_file.dict_find_list("collections");
|
|
|
|
if (collections)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < collections.list_size(); ++i)
|
|
|
|
{
|
|
|
|
bdecode_node str = collections.list_at(i);
|
|
|
|
|
|
|
|
if (str.type() != bdecode_node::string_t) continue;
|
|
|
|
|
|
|
|
m_owned_collections.push_back(std::string(str.string_ptr()
|
|
|
|
, str.string_length()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
|
2003-10-23 01:00:57 +02:00
|
|
|
// extract the url of the tracker
|
2015-08-14 05:52:25 +02:00
|
|
|
bdecode_node announce_node = torrent_file.dict_find_list("announce-list");
|
|
|
|
if (announce_node)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-08-14 05:52:25 +02:00
|
|
|
m_urls.reserve(announce_node.list_size());
|
|
|
|
for (int j = 0, end(announce_node.list_size()); j < end; ++j)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-08-14 05:52:25 +02:00
|
|
|
bdecode_node tier = announce_node.list_at(j);
|
2015-03-12 06:20:12 +01:00
|
|
|
if (tier.type() != bdecode_node::list_t) continue;
|
2015-08-17 15:01:43 +02:00
|
|
|
for (int k = 0, end2(tier.list_size()); k < end2; ++k)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
announce_entry e(tier.list_string_value_at(k).to_string());
|
2010-02-12 18:11:37 +01:00
|
|
|
e.trim();
|
2008-05-14 07:29:42 +02:00
|
|
|
if (e.url.empty()) continue;
|
2016-11-21 16:08:26 +01:00
|
|
|
e.tier = std::uint8_t(j);
|
2008-11-27 21:51:59 +01:00
|
|
|
e.fail_limit = 0;
|
2008-11-29 09:38:40 +01:00
|
|
|
e.source = announce_entry::source_torrent;
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2016-06-23 17:34:26 +02:00
|
|
|
if (is_i2p_url(e.url)) m_flags |= i2p;
|
2009-08-20 05:19:12 +02:00
|
|
|
#endif
|
2003-10-23 01:00:57 +02:00
|
|
|
m_urls.push_back(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-16 02:26:35 +01:00
|
|
|
if (!m_urls.empty())
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2008-12-16 02:26:35 +01:00
|
|
|
// shuffle each tier
|
|
|
|
std::vector<announce_entry>::iterator start = m_urls.begin();
|
|
|
|
std::vector<announce_entry>::iterator stop;
|
|
|
|
int current_tier = m_urls.front().tier;
|
|
|
|
for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2008-12-16 02:26:35 +01:00
|
|
|
if (stop->tier != current_tier)
|
|
|
|
{
|
2016-08-15 01:48:31 +02:00
|
|
|
aux::random_shuffle(start, stop);
|
2008-12-16 02:26:35 +01:00
|
|
|
start = stop;
|
|
|
|
current_tier = stop->tier;
|
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
2016-08-15 01:48:31 +02:00
|
|
|
aux::random_shuffle(start, stop);
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
}
|
2008-05-14 07:29:42 +02:00
|
|
|
|
|
|
|
if (m_urls.empty())
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
announce_entry e(torrent_file.dict_find_string_value("announce").to_string());
|
2008-11-27 21:51:59 +01:00
|
|
|
e.fail_limit = 0;
|
2008-11-29 09:38:40 +01:00
|
|
|
e.source = announce_entry::source_torrent;
|
2009-06-23 03:53:47 +02:00
|
|
|
e.trim();
|
2009-08-20 05:19:12 +02:00
|
|
|
#if TORRENT_USE_I2P
|
2016-06-23 17:34:26 +02:00
|
|
|
if (is_i2p_url(e.url)) m_flags |= i2p;
|
2009-08-20 05:19:12 +02:00
|
|
|
#endif
|
2008-05-14 07:29:42 +02:00
|
|
|
if (!e.url.empty()) m_urls.push_back(e);
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node nodes = torrent_file.dict_find_list("nodes");
|
2008-05-14 07:29:42 +02:00
|
|
|
if (nodes)
|
2003-10-23 01:00:57 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
for (int i = 0, end(nodes.list_size()); i < end; ++i)
|
2006-08-01 17:27:08 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node n = nodes.list_at(i);
|
|
|
|
if (n.type() != bdecode_node::list_t
|
|
|
|
|| n.list_size() < 2
|
|
|
|
|| n.list_at(0).type() != bdecode_node::string_t
|
|
|
|
|| n.list_at(1).type() != bdecode_node::int_t)
|
2008-05-14 07:29:42 +02:00
|
|
|
continue;
|
|
|
|
m_nodes.push_back(std::make_pair(
|
2016-08-13 13:04:53 +02:00
|
|
|
n.list_at(0).string_value().to_string()
|
2015-03-12 06:20:12 +01:00
|
|
|
, int(n.list_at(1).int_value())));
|
2006-08-01 17:27:08 +02:00
|
|
|
}
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-01-24 18:14:03 +01:00
|
|
|
// extract creation date
|
2016-06-18 20:01:38 +02:00
|
|
|
std::int64_t cd = torrent_file.dict_find_int_value("creation date", -1);
|
2008-05-14 07:29:42 +02:00
|
|
|
if (cd >= 0)
|
2004-01-24 18:14:03 +01:00
|
|
|
{
|
2010-08-22 00:10:16 +02:00
|
|
|
m_creation_date = long(cd);
|
2004-01-24 18:14:03 +01:00
|
|
|
}
|
|
|
|
|
2006-04-25 23:04:48 +02:00
|
|
|
// if there are any url-seeds, extract them
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node url_seeds = torrent_file.dict_find("url-list");
|
|
|
|
if (url_seeds && url_seeds.type() == bdecode_node::string_t
|
|
|
|
&& url_seeds.string_length() > 0)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
web_seed_entry ent(maybe_url_encode(url_seeds.string_value().to_string())
|
2012-10-06 19:51:59 +02:00
|
|
|
, web_seed_entry::url_seed);
|
2016-09-07 23:51:18 +02:00
|
|
|
if ((m_flags & multifile) && ent.url[ent.url.size() - 1] != '/') ent.url += '/';
|
2012-10-06 19:51:59 +02:00
|
|
|
m_web_seeds.push_back(ent);
|
2007-12-28 21:11:10 +01:00
|
|
|
}
|
2015-03-12 06:20:12 +01:00
|
|
|
else if (url_seeds && url_seeds.type() == bdecode_node::list_t)
|
2007-12-28 21:11:10 +01:00
|
|
|
{
|
2013-12-23 02:40:05 +01:00
|
|
|
// only add a URL once
|
|
|
|
std::set<std::string> unique;
|
2015-03-12 06:20:12 +01:00
|
|
|
for (int i = 0, end(url_seeds.list_size()); i < end; ++i)
|
2006-04-25 23:04:48 +02:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node url = url_seeds.list_at(i);
|
|
|
|
if (url.type() != bdecode_node::string_t) continue;
|
|
|
|
if (url.string_length() == 0) continue;
|
2016-08-13 13:04:53 +02:00
|
|
|
web_seed_entry ent(maybe_url_encode(url.string_value().to_string())
|
2012-10-06 19:51:59 +02:00
|
|
|
, web_seed_entry::url_seed);
|
2016-09-07 23:51:18 +02:00
|
|
|
if ((m_flags & multifile) && ent.url[ent.url.size() - 1] != '/') ent.url += '/';
|
2013-12-23 02:40:05 +01:00
|
|
|
if (unique.count(ent.url)) continue;
|
|
|
|
unique.insert(ent.url);
|
2012-10-06 19:51:59 +02:00
|
|
|
m_web_seeds.push_back(ent);
|
2008-12-30 04:54:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there are any http-seeds, extract them
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node http_seeds = torrent_file.dict_find("httpseeds");
|
|
|
|
if (http_seeds && http_seeds.type() == bdecode_node::string_t
|
|
|
|
&& http_seeds.string_length() > 0)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2016-08-13 13:04:53 +02:00
|
|
|
m_web_seeds.push_back(web_seed_entry(maybe_url_encode(http_seeds.string_value().to_string())
|
2010-10-10 20:43:58 +02:00
|
|
|
, web_seed_entry::http_seed));
|
2008-12-30 04:54:07 +01:00
|
|
|
}
|
2015-03-12 06:20:12 +01:00
|
|
|
else if (http_seeds && http_seeds.type() == bdecode_node::list_t)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2013-12-23 02:40:05 +01:00
|
|
|
// only add a URL once
|
|
|
|
std::set<std::string> unique;
|
2015-03-12 06:20:12 +01:00
|
|
|
for (int i = 0, end(http_seeds.list_size()); i < end; ++i)
|
2008-12-30 04:54:07 +01:00
|
|
|
{
|
2015-03-12 06:20:12 +01:00
|
|
|
bdecode_node url = http_seeds.list_at(i);
|
|
|
|
if (url.type() != bdecode_node::string_t || url.string_length() == 0) continue;
|
2016-08-13 13:04:53 +02:00
|
|
|
std::string u = maybe_url_encode(url.string_value().to_string());
|
2013-12-23 02:40:05 +01:00
|
|
|
if (unique.count(u)) continue;
|
|
|
|
unique.insert(u);
|
|
|
|
m_web_seeds.push_back(web_seed_entry(u, web_seed_entry::http_seed));
|
2006-04-25 23:04:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
m_comment = torrent_file.dict_find_string_value("comment.utf-8").to_string();
|
|
|
|
if (m_comment.empty()) m_comment = torrent_file.dict_find_string_value("comment").to_string();
|
2008-12-31 11:18:19 +01:00
|
|
|
verify_encoding(m_comment);
|
2015-12-20 00:28:12 +01:00
|
|
|
|
2016-08-13 13:04:53 +02:00
|
|
|
m_created_by = torrent_file.dict_find_string_value("created by.utf-8").to_string();
|
|
|
|
if (m_created_by.empty()) m_created_by = torrent_file.dict_find_string_value("created by").to_string();
|
2008-12-31 11:18:19 +01:00
|
|
|
verify_encoding(m_created_by);
|
2005-08-18 00:59:21 +02:00
|
|
|
|
2012-10-06 19:51:59 +02:00
|
|
|
return true;
|
2003-10-23 01:00:57 +02:00
|
|
|
}
|
|
|
|
|
2004-03-05 13:04:47 +01:00
|
|
|
void torrent_info::add_tracker(std::string const& url, int tier)
|
|
|
|
{
|
2016-10-19 23:28:35 +02:00
|
|
|
auto i = std::find_if(m_urls.begin(), m_urls.end()
|
|
|
|
, [&url](announce_entry const& ae) { return ae.url == url; });
|
|
|
|
if (i != m_urls.end()) return;
|
|
|
|
|
2004-09-12 12:12:16 +02:00
|
|
|
announce_entry e(url);
|
2016-11-21 16:08:26 +01:00
|
|
|
e.tier = std::uint8_t(tier);
|
2008-11-29 09:38:40 +01:00
|
|
|
e.source = announce_entry::source_client;
|
2004-03-05 13:04:47 +01:00
|
|
|
m_urls.push_back(e);
|
2004-11-01 00:16:08 +01:00
|
|
|
|
2016-05-25 06:31:52 +02:00
|
|
|
std::sort(m_urls.begin(), m_urls.end()
|
|
|
|
, [] (announce_entry const& lhs, announce_entry const& rhs)
|
|
|
|
{ return lhs.tier < rhs.tier; });
|
2004-03-05 13:04:47 +01:00
|
|
|
}
|
|
|
|
|
2010-10-10 20:43:58 +02:00
|
|
|
#ifndef TORRENT_NO_DEPRECATE
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
struct filter_web_seed_type
|
|
|
|
{
|
2016-07-10 20:27:42 +02:00
|
|
|
explicit filter_web_seed_type(web_seed_entry::type_t t_) : t(t_) {}
|
2010-10-10 20:43:58 +02:00
|
|
|
void operator() (web_seed_entry const& w)
|
|
|
|
{ if (w.type == t) urls.push_back(w.url); }
|
|
|
|
std::vector<std::string> urls;
|
|
|
|
web_seed_entry::type_t t;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> torrent_info::url_seeds() const
|
|
|
|
{
|
|
|
|
return std::for_each(m_web_seeds.begin(), m_web_seeds.end()
|
|
|
|
, filter_web_seed_type(web_seed_entry::url_seed)).urls;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> torrent_info::http_seeds() const
|
|
|
|
{
|
|
|
|
return std::for_each(m_web_seeds.begin(), m_web_seeds.end()
|
|
|
|
, filter_web_seed_type(web_seed_entry::http_seed)).urls;
|
|
|
|
}
|
|
|
|
|
2015-03-12 06:20:12 +01:00
|
|
|
bool torrent_info::parse_info_section(lazy_entry const& le, error_code& ec
|
|
|
|
, int flags)
|
|
|
|
{
|
|
|
|
if (le.type() == lazy_entry::none_t) return false;
|
|
|
|
std::pair<char const*, int> buf = le.data_section();
|
|
|
|
bdecode_node e;
|
|
|
|
if (bdecode(buf.first, buf.first + buf.second, e, ec) != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return parse_info_section(e, ec, flags);
|
|
|
|
}
|
|
|
|
|
2010-10-10 20:43:58 +02:00
|
|
|
#endif // TORRENT_NO_DEPRECATE
|
|
|
|
|
|
|
|
void torrent_info::add_url_seed(std::string const& url
|
2015-03-21 01:12:40 +01:00
|
|
|
, std::string const& ext_auth
|
|
|
|
, web_seed_entry::headers_t const& ext_headers)
|
2010-10-10 20:43:58 +02:00
|
|
|
{
|
|
|
|
m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::url_seed
|
|
|
|
, ext_auth, ext_headers));
|
|
|
|
}
|
|
|
|
|
|
|
|
void torrent_info::add_http_seed(std::string const& url
|
2015-03-21 01:12:40 +01:00
|
|
|
, std::string const& auth
|
|
|
|
, web_seed_entry::headers_t const& extra_headers)
|
2010-10-10 20:43:58 +02:00
|
|
|
{
|
|
|
|
m_web_seeds.push_back(web_seed_entry(url, web_seed_entry::http_seed
|
|
|
|
, auth, extra_headers));
|
|
|
|
}
|
|
|
|
|
2015-12-13 00:36:15 +01:00
|
|
|
void torrent_info::set_web_seeds(std::vector<web_seed_entry> seeds)
|
|
|
|
{
|
|
|
|
m_web_seeds = seeds;
|
|
|
|
}
|
|
|
|
|
2015-03-21 01:12:40 +01:00
|
|
|
std::vector<sha1_hash> torrent_info::similar_torrents() const
|
|
|
|
{
|
|
|
|
std::vector<sha1_hash> ret;
|
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
ret.reserve(m_similar_torrents.size() + m_owned_similar_torrents.size());
|
|
|
|
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto const& st : m_similar_torrents)
|
|
|
|
ret.push_back(sha1_hash(st));
|
2015-03-21 01:12:40 +01:00
|
|
|
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto const& st : m_owned_similar_torrents)
|
|
|
|
ret.push_back(st);
|
2015-03-21 01:12:40 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> torrent_info::collections() const
|
|
|
|
{
|
|
|
|
std::vector<std::string> ret;
|
|
|
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
ret.reserve(m_collections.size() + m_owned_collections.size());
|
|
|
|
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto const& c : m_collections)
|
|
|
|
ret.push_back(std::string(c.first, c.second));
|
2015-03-21 01:12:40 +01:00
|
|
|
|
2016-11-27 14:46:53 +01:00
|
|
|
for (auto const& c : m_owned_collections)
|
|
|
|
ret.push_back(c);
|
2015-03-21 01:12:40 +01:00
|
|
|
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2010-10-10 20:43:58 +02:00
|
|
|
|
2014-01-21 20:26:09 +01:00
|
|
|
#if TORRENT_USE_INVARIANT_CHECKS
|
2011-06-06 09:47:29 +02:00
|
|
|
void torrent_info::check_invariant() const
|
|
|
|
{
|
2016-12-22 16:42:33 +01:00
|
|
|
for (file_index_t i(0); i < m_files.end_file(); ++i)
|
2011-06-06 09:47:29 +02:00
|
|
|
{
|
2016-07-09 22:26:26 +02:00
|
|
|
TORRENT_ASSERT(m_files.file_name_ptr(i) != nullptr);
|
2013-10-05 06:18:24 +02:00
|
|
|
if (m_files.file_name_len(i) != -1)
|
2011-06-06 09:47:29 +02:00
|
|
|
{
|
|
|
|
// name needs to point into the allocated info section buffer
|
2013-08-12 09:30:57 +02:00
|
|
|
TORRENT_ASSERT(m_files.file_name_ptr(i) >= m_info_section.get());
|
|
|
|
TORRENT_ASSERT(m_files.file_name_ptr(i) < m_info_section.get() + m_info_section_size);
|
2011-06-06 09:47:29 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// name must be a valid string
|
2013-08-12 09:30:57 +02:00
|
|
|
TORRENT_ASSERT(strlen(m_files.file_name_ptr(i)) < 2048);
|
2011-06-06 09:47:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-09 22:26:26 +02:00
|
|
|
if (m_piece_hashes != nullptr)
|
2011-06-25 19:24:26 +02:00
|
|
|
{
|
|
|
|
TORRENT_ASSERT(m_piece_hashes >= m_info_section.get());
|
|
|
|
TORRENT_ASSERT(m_piece_hashes < m_info_section.get() + m_info_section_size);
|
|
|
|
}
|
2011-06-06 09:47:29 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2003-10-26 04:18:17 +01:00
|
|
|
}
|