premiere-libtorrent/src/session.cpp

1421 lines
37 KiB
C++
Raw Normal View History

/*
2003-12-14 06:56:12 +01:00
Copyright (c) 2003, Arvid Norberg, Magnus Jonsson
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.
*/
#include <ctime>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <iterator>
#include <algorithm>
#include <set>
#include <cctype>
2003-10-27 16:43:33 +01:00
#include <algorithm>
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/lexical_cast.hpp>
#include <boost/filesystem/convenience.hpp>
2003-11-28 18:29:27 +01:00
#include <boost/filesystem/exception.hpp>
#include <boost/limits.hpp>
2004-01-17 21:04:19 +01:00
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/peer_id.hpp"
#include "libtorrent/torrent_info.hpp"
2004-01-31 11:46:15 +01:00
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/fingerprint.hpp"
2004-01-07 01:48:02 +01:00
#include "libtorrent/entry.hpp"
2004-01-18 20:12:18 +01:00
#include "libtorrent/alert_types.hpp"
#include "libtorrent/invariant_check.hpp"
2004-01-26 02:08:59 +01:00
#include "libtorrent/file.hpp"
2004-02-12 15:41:39 +01:00
#include "libtorrent/allocate_resources.hpp"
2004-03-22 15:56:32 +01:00
#include "libtorrent/peer_connection.hpp"
#if defined(_MSC_VER) && _MSC_VER < 1300
namespace std
{
using ::srand;
using ::isprint;
};
#endif
2004-10-14 03:17:04 +02:00
using namespace boost::posix_time;
2004-02-26 13:59:01 +01:00
namespace libtorrent { namespace detail
2003-10-26 18:35:23 +01:00
{
2004-03-05 13:04:47 +01:00
std::string generate_auth_string(std::string const& user
, std::string const& passwd)
{
if (user.empty()) return std::string();
return user + ":" + passwd;
}
2004-03-05 13:04:47 +01:00
// This is the checker thread
// it is looping in an infinite loop
// until the session is aborted. It will
// normally just block in a wait() call,
// waiting for a signal from session that
// there's a new torrent to check.
2004-02-26 13:59:01 +01:00
void checker_impl::operator()()
2003-10-26 18:35:23 +01:00
{
2004-02-26 13:59:01 +01:00
eh_initializer();
for (;;)
2003-10-31 05:02:51 +01:00
{
2004-03-05 20:07:29 +01:00
piece_checker_data* t = 0;
2003-10-31 05:02:51 +01:00
{
2004-02-26 13:59:01 +01:00
boost::mutex::scoped_lock l(m_mutex);
2003-10-31 05:02:51 +01:00
2004-02-26 13:59:01 +01:00
// if the job queue is empty and
// we shouldn't abort
// wait for a signal
if (m_torrents.empty() && !m_abort)
m_cond.wait(l);
2003-10-31 05:02:51 +01:00
2004-02-26 13:59:01 +01:00
if (m_abort) return;
2003-10-31 05:02:51 +01:00
2004-02-26 13:59:01 +01:00
assert(!m_torrents.empty());
t = &m_torrents.front();
if (t->abort)
{
m_torrents.pop_front();
continue;
2003-10-31 05:02:51 +01:00
}
2005-01-08 22:12:19 +01:00
t->processing = true;
2004-02-26 13:59:01 +01:00
}
2003-10-31 05:02:51 +01:00
2004-02-26 13:59:01 +01:00
try
{
assert(t != 0);
2005-06-16 17:41:04 +02:00
std::string error_msg;
t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file(), error_msg);
2004-12-21 13:30:09 +01:00
// clear the resume data now that it has been used
// (the fast resume data is now parsed and stored in t)
t->resume_data = entry();
2004-02-26 13:59:01 +01:00
t->torrent_ptr->check_files(*t, m_mutex);
// lock the session to add the new torrent
2003-10-31 05:02:51 +01:00
2004-02-26 13:59:01 +01:00
boost::mutex::scoped_lock l(m_mutex);
if (t->abort)
2004-02-26 13:59:01 +01:00
{
2004-12-21 13:30:09 +01:00
m_torrents.pop_front();
continue;
}
boost::mutex::scoped_lock l2(m_ses.m_mutex);
2005-06-16 17:41:04 +02:00
if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning))
{
m_ses.m_alerts.post_alert(fastresume_rejected_alert(
t->torrent_ptr->get_handle()
, error_msg));
}
m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr));
if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info))
{
m_ses.m_alerts.post_alert(torrent_finished_alert(
t->torrent_ptr->get_handle()
, "torrent is complete"));
2003-10-31 05:02:51 +01:00
}
peer_id id;
std::fill(id.begin(), id.end(), 0);
for (std::vector<address>::const_iterator i = t->peers.begin();
i != t->peers.end(); ++i)
{
t->torrent_ptr->get_policy().peer_from_tracker(*i, id);
}
m_torrents.pop_front();
2004-02-26 13:59:01 +01:00
}
catch(const std::exception& e)
{
// This will happen if the storage fails to initialize
2004-12-21 13:30:09 +01:00
boost::mutex::scoped_lock l(m_mutex);
boost::mutex::scoped_lock l2(m_ses.m_mutex);
2004-03-03 14:47:12 +01:00
if (m_ses.m_alerts.should_post(alert::fatal))
{
m_ses.m_alerts.post_alert(
file_error_alert(
t->torrent_ptr->get_handle()
, e.what()));
}
2004-12-21 13:30:09 +01:00
m_torrents.pop_front();
2004-02-26 13:59:01 +01:00
}
catch(...)
{
2003-10-31 05:02:51 +01:00
#ifndef NDEBUG
2004-02-26 13:59:01 +01:00
std::cerr << "error while checking files\n";
2003-10-31 05:02:51 +01:00
#endif
2004-03-03 14:47:12 +01:00
assert(false);
2004-12-21 13:30:09 +01:00
boost::mutex::scoped_lock l(m_mutex);
m_torrents.pop_front();
2003-10-31 05:02:51 +01:00
}
}
2004-02-26 13:59:01 +01:00
}
2003-10-31 05:02:51 +01:00
2005-01-08 22:12:19 +01:00
detail::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash)
2004-02-26 13:59:01 +01:00
{
for (std::deque<piece_checker_data>::iterator i
2005-05-30 19:43:03 +02:00
= m_torrents.begin(); i != m_torrents.end(); ++i)
2003-10-31 05:02:51 +01:00
{
2004-02-26 13:59:01 +01:00
if (i->info_hash == info_hash) return &(*i);
2003-10-31 05:02:51 +01:00
}
2004-02-26 13:59:01 +01:00
return 0;
}
2003-10-31 05:02:51 +01:00
2005-01-08 22:12:19 +01:00
void checker_impl::remove_torrent(sha1_hash const& info_hash)
{
for (std::deque<piece_checker_data>::iterator i
2005-05-30 19:43:03 +02:00
= m_torrents.begin(); i != m_torrents.end(); ++i)
2005-01-08 22:12:19 +01:00
{
if (i->info_hash == info_hash)
{
m_torrents.erase(i);
return;
}
}
}
2004-02-26 13:59:01 +01:00
session_impl::session_impl(
std::pair<int, int> listen_port_range
, const fingerprint& cl_fprint
, const char* listen_interface = 0)
: m_tracker_manager(m_settings)
, m_listen_port_range(listen_port_range)
, m_listen_interface(listen_interface, listen_port_range.first)
, m_abort(false)
, m_upload_rate(-1)
2004-03-28 19:45:37 +02:00
, m_download_rate(-1)
2004-10-29 15:21:09 +02:00
, m_max_uploads(-1)
, m_max_connections(-1)
2004-02-26 13:59:01 +01:00
, m_incoming_connection(false)
{
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
m_logger = create_log("main_session");
2004-03-29 08:10:23 +02:00
#endif
2004-11-01 00:16:08 +01:00
std::fill(m_extension_enabled, m_extension_enabled
+ peer_connection::num_supported_extensions, true);
2004-02-26 13:59:01 +01:00
// ---- generate a peer id ----
2004-02-26 13:59:01 +01:00
std::srand((unsigned int)std::time(0));
2004-03-21 03:03:37 +01:00
m_key = rand() + (rand() << 15) + (rand() << 30);
2004-02-26 13:59:01 +01:00
std::string print = cl_fprint.to_string();
2004-07-18 02:39:58 +02:00
assert(print.length() <= 20);
2004-02-26 13:59:01 +01:00
// the client's fingerprint
std::copy(
print.begin()
, print.begin() + print.length()
, m_peer_id.begin());
2003-10-26 18:35:23 +01:00
2004-03-24 23:50:07 +01:00
// http-accepted characters:
static char const printable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.!~*'()";
2004-02-26 13:59:01 +01:00
// the random number
for (unsigned char* i = m_peer_id.begin() + print.length();
i != m_peer_id.end();
++i)
{
2004-03-24 23:50:07 +01:00
*i = printable[rand() % (sizeof(printable)-1)];
}
2004-02-26 13:59:01 +01:00
}
2004-11-01 00:16:08 +01:00
bool session_impl::extensions_enabled() const
{
const int n = peer_connection::num_supported_extensions;
return std::find(m_extension_enabled
, m_extension_enabled + n, true) != m_extension_enabled + n;
}
2004-02-26 13:59:01 +01:00
void session_impl::purge_connections()
{
while (!m_disconnect_peer.empty())
2004-01-20 12:01:50 +01:00
{
2004-02-26 13:59:01 +01:00
m_connections.erase(m_disconnect_peer.back());
m_disconnect_peer.pop_back();
2004-01-20 12:01:50 +01:00
}
2004-02-26 13:59:01 +01:00
}
2004-02-26 13:59:01 +01:00
void session_impl::open_listen_port()
{
try
{
// create listener socket
2004-08-08 23:26:40 +02:00
m_selector.remove(m_listen_socket);
2004-02-26 13:59:01 +01:00
m_listen_socket = boost::shared_ptr<socket>(new socket(socket::tcp, false));
for(;;)
{
try
{
2004-02-26 13:59:01 +01:00
m_listen_socket->listen(m_listen_interface, 10);
break;
}
2004-02-26 01:27:06 +01:00
catch (network_error& e)
{
2004-02-26 01:27:06 +01:00
if (e.error_code() == socket::address_not_available)
{
2004-03-30 21:11:07 +02:00
if (m_alerts.should_post(alert::fatal))
{
std::string msg = "cannot listen on the given interface '" + m_listen_interface.as_string() + "'";
m_alerts.post_alert(listen_failed_alert(msg));
}
2005-03-20 11:55:33 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-03-30 21:11:07 +02:00
std::string msg = "cannot listen on the given interface '" + m_listen_interface.as_string() + "'";
2004-02-26 13:59:01 +01:00
(*m_logger) << msg << "\n";
#endif
2004-03-30 21:11:07 +02:00
assert(m_listen_socket.unique());
2004-02-26 13:59:01 +01:00
m_listen_socket.reset();
break;
2004-02-26 01:27:06 +01:00
}
m_listen_interface.port++;
if (m_listen_interface.port > m_listen_port_range.second)
2004-01-26 11:29:00 +01:00
{
2004-02-26 13:59:01 +01:00
std::stringstream msg;
msg << "none of the ports in the range ["
<< m_listen_port_range.first
<< ", " << m_listen_port_range.second
<< "] could be opened for listening";
m_alerts.post_alert(listen_failed_alert(msg.str()));
2005-03-20 11:55:33 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-02-26 13:59:01 +01:00
(*m_logger) << msg.str() << "\n";
#endif
m_listen_socket.reset();
break;
2004-01-26 11:29:00 +01:00
}
}
}
2004-02-26 13:59:01 +01:00
}
catch (network_error& e)
{
m_alerts.post_alert(listen_failed_alert(e.what()));
}
2005-03-20 11:55:33 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-02-26 13:59:01 +01:00
if (m_listen_socket)
{
2004-02-26 01:27:06 +01:00
(*m_logger) << "listening on port: " << m_listen_interface.port << "\n";
2004-02-26 13:59:01 +01:00
}
#endif
2004-02-26 13:59:01 +01:00
if (m_listen_socket)
{
m_selector.monitor_readability(m_listen_socket);
m_selector.monitor_errors(m_listen_socket);
}
}
2003-11-05 00:27:06 +01:00
2004-02-26 13:59:01 +01:00
void session_impl::operator()()
{
eh_initializer();
2004-03-29 00:44:40 +02:00
if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0)
2004-10-03 13:39:34 +02:00
{
boost::mutex::scoped_lock l(m_mutex);
2004-03-29 00:44:40 +02:00
open_listen_port();
2004-10-03 13:39:34 +02:00
}
2004-02-26 13:59:01 +01:00
std::vector<boost::shared_ptr<socket> > readable_clients;
std::vector<boost::shared_ptr<socket> > writable_clients;
std::vector<boost::shared_ptr<socket> > error_clients;
2004-10-14 03:17:04 +02:00
boost::posix_time::ptime timer = second_clock::universal_time();
2004-02-26 13:59:01 +01:00
#ifndef NDEBUG
int loops_per_second = 0;
#endif
for(;;)
{
try
{
2003-11-09 19:17:09 +01:00
#ifndef NDEBUG
2004-09-08 01:16:11 +02:00
{
boost::mutex::scoped_lock l(m_mutex);
check_invariant("before SELECT");
}
2004-02-26 13:59:01 +01:00
loops_per_second++;
2003-11-09 19:17:09 +01:00
#endif
2004-02-26 13:59:01 +01:00
// if nothing happens within 500000 microseconds (0.5 seconds)
// do the loop anyway to check if anything else has changed
m_selector.wait(500000, readable_clients, writable_clients, error_clients);
2004-01-15 21:00:12 +01:00
#ifndef NDEBUG
2004-09-08 01:16:11 +02:00
{
boost::mutex::scoped_lock l(m_mutex);
check_invariant("after SELECT");
}
2004-02-26 13:59:01 +01:00
for (std::vector<boost::shared_ptr<libtorrent::socket> >::iterator i =
2005-06-06 12:33:44 +02:00
writable_clients.begin(); i != writable_clients.end();
2004-02-26 13:59:01 +01:00
++i)
{
assert((*i)->is_writable());
}
2004-09-08 01:16:11 +02:00
for (std::vector<boost::shared_ptr<libtorrent::socket> >::iterator i =
2005-06-06 12:33:44 +02:00
readable_clients.begin(); i != readable_clients.end(); ++i)
2004-09-08 01:16:11 +02:00
{
assert((*i)->is_readable());
}
2004-01-15 21:00:12 +01:00
#endif
2004-02-26 13:59:01 +01:00
boost::mutex::scoped_lock l(m_mutex);
2004-09-08 01:16:11 +02:00
#ifndef NDEBUG
check_invariant("before abort");
#endif
2005-03-05 15:17:17 +01:00
purge_connections();
2004-02-26 13:59:01 +01:00
if (m_abort)
{
m_tracker_manager.abort_all_requests();
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i =
2004-09-16 03:14:16 +02:00
m_torrents.begin(); i != m_torrents.end(); ++i)
{
2004-02-26 13:59:01 +01:00
i->second->abort();
2004-03-21 03:03:37 +01:00
tracker_request req = i->second->generate_tracker_request();
req.listen_port = m_listen_interface.port;
req.key = m_key;
m_tracker_manager.queue_request(req, i->second->tracker_login());
}
2004-02-26 13:59:01 +01:00
m_connections.clear();
m_torrents.clear();
break;
}
2004-01-15 21:03:23 +01:00
#ifndef NDEBUG
2004-02-26 13:59:01 +01:00
check_invariant("before SEND SOCKETS");
2004-01-15 21:03:23 +01:00
#endif
2004-02-26 13:59:01 +01:00
// ************************
// SEND SOCKETS
// ************************
2004-01-15 21:03:23 +01:00
2004-03-21 03:03:37 +01:00
// let the writable connections send data
2004-02-26 13:59:01 +01:00
for (std::vector<boost::shared_ptr<socket> >::iterator i
2005-06-06 12:33:44 +02:00
= writable_clients.begin(); i != writable_clients.end(); ++i)
2004-02-26 13:59:01 +01:00
{
assert((*i)->is_writable());
connection_map::iterator p = m_connections.find(*i);
// the connection may have been disconnected in the receive phase
if (p == m_connections.end())
2004-01-15 21:03:23 +01:00
{
2004-02-26 13:59:01 +01:00
m_selector.remove(*i);
}
else
{
try
2004-01-15 21:03:23 +01:00
{
2004-02-26 13:59:01 +01:00
assert(m_selector.is_writability_monitored(p->first));
2004-03-28 19:45:37 +02:00
assert(p->second->can_write());
2004-02-26 13:59:01 +01:00
assert(p->second->get_socket()->is_writable());
p->second->send_data();
2004-01-15 21:03:23 +01:00
}
2004-02-26 13:59:01 +01:00
catch (file_error& e)
2004-01-15 21:03:23 +01:00
{
2004-02-26 13:59:01 +01:00
torrent* t = p->second->associated_torrent();
assert(t != 0);
if (m_alerts.should_post(alert::fatal))
2004-01-26 02:08:59 +01:00
{
2004-02-26 13:59:01 +01:00
m_alerts.post_alert(
file_error_alert(
t->get_handle()
, e.what()));
2004-01-26 02:08:59 +01:00
}
2004-02-26 13:59:01 +01:00
2004-03-21 03:03:37 +01:00
// pause the torrent
t->pause();
2004-02-26 13:59:01 +01:00
}
catch (std::exception& e)
{
// the connection wants to disconnect for some reason,
// remove it from the connection-list
if (m_alerts.should_post(alert::debug))
2004-01-15 21:03:23 +01:00
{
2004-02-26 13:59:01 +01:00
m_alerts.post_alert(
2004-03-28 19:45:37 +02:00
peer_error_alert(
p->first->sender()
, p->second->id()
, e.what()));
2004-01-15 21:03:23 +01:00
}
2004-02-26 13:59:01 +01:00
2004-03-21 03:03:37 +01:00
p->second->set_failed();
2004-02-26 13:59:01 +01:00
m_selector.remove(*i);
m_connections.erase(p);
2004-01-15 21:03:23 +01:00
}
}
2004-02-26 13:59:01 +01:00
}
purge_connections();
2004-01-15 21:03:23 +01:00
#ifndef NDEBUG
2004-02-26 13:59:01 +01:00
check_invariant("after SEND SOCKETS");
#endif
2004-02-26 13:59:01 +01:00
// ************************
// RECEIVE SOCKETS
// ************************
2004-02-26 13:59:01 +01:00
// let the readable clients receive data
for (std::vector<boost::shared_ptr<socket> >::iterator i = readable_clients.begin();
2005-07-04 18:27:14 +02:00
i != readable_clients.end(); ++i)
2004-02-26 13:59:01 +01:00
{
// special case for m_listen_socket socket
if (*i == m_listen_socket)
{
2004-02-26 13:59:01 +01:00
assert(m_listen_socket);
2004-08-01 23:08:34 +02:00
boost::shared_ptr<libtorrent::socket> s;
try
{
s = (*i)->accept();
}
catch(std::exception& e)
{
2005-03-20 11:55:33 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-08-01 23:08:34 +02:00
(*m_logger) << "accept failed: " << e.what() << "\n";
#endif
}
2004-02-26 13:59:01 +01:00
if (s)
{
2004-02-26 13:59:01 +01:00
s->set_blocking(false);
// we got a connection request!
m_incoming_connection = true;
2005-03-20 11:55:33 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-02-26 13:59:01 +01:00
(*m_logger) << s->sender().as_string() << " <== INCOMING CONNECTION\n";
#endif
2004-02-26 13:59:01 +01:00
// TODO: filter ip:s
2004-02-26 13:59:01 +01:00
boost::shared_ptr<peer_connection> c(
new peer_connection(*this, m_selector, s));
2004-02-24 20:23:37 +01:00
2004-02-26 13:59:01 +01:00
m_connections.insert(std::make_pair(s, c));
m_selector.monitor_readability(s);
m_selector.monitor_errors(s);
}
2004-02-26 13:59:01 +01:00
continue;
}
connection_map::iterator p = m_connections.find(*i);
if(p == m_connections.end())
{
m_selector.remove(*i);
}
else
{
try
{
2004-02-26 13:59:01 +01:00
p->second->receive_data();
}
2004-02-26 13:59:01 +01:00
catch (file_error& e)
{
2004-02-26 13:59:01 +01:00
torrent* t = p->second->associated_torrent();
assert(t != 0);
if (m_alerts.should_post(alert::fatal))
2004-01-26 02:08:59 +01:00
{
2004-02-26 13:59:01 +01:00
m_alerts.post_alert(
file_error_alert(
t->get_handle()
, e.what()));
2004-01-26 02:08:59 +01:00
}
2004-02-26 13:59:01 +01:00
2004-03-21 03:03:37 +01:00
t->pause();
2004-02-26 13:59:01 +01:00
}
catch (std::exception& e)
{
if (m_alerts.should_post(alert::debug))
{
2004-02-26 13:59:01 +01:00
m_alerts.post_alert(
2004-03-28 19:45:37 +02:00
peer_error_alert(
p->first->sender()
, p->second->id()
, e.what()));
}
2004-02-26 13:59:01 +01:00
// the connection wants to disconnect for some reason, remove it
// from the connection-list
2004-03-21 03:03:37 +01:00
p->second->set_failed();
2004-02-26 13:59:01 +01:00
m_selector.remove(*i);
m_connections.erase(p);
}
}
2004-02-26 13:59:01 +01:00
}
purge_connections();
#ifndef NDEBUG
2004-02-26 13:59:01 +01:00
check_invariant("after RECEIVE SOCKETS");
#endif
2004-02-26 13:59:01 +01:00
// ************************
// ERROR SOCKETS
// ************************
2004-02-26 13:59:01 +01:00
// disconnect the one we couldn't connect to
for (std::vector<boost::shared_ptr<socket> >::iterator i = error_clients.begin();
i != error_clients.end();
++i)
{
connection_map::iterator p = m_connections.find(*i);
2004-10-11 23:50:04 +02:00
if (p != m_connections.end())
{
2004-10-11 23:50:04 +02:00
if (m_alerts.should_post(alert::debug))
{
m_alerts.post_alert(
peer_error_alert(
p->first->sender()
, p->second->id()
, "connection closed"));
}
2004-02-26 13:59:01 +01:00
}
2004-02-26 13:59:01 +01:00
m_selector.remove(*i);
// the connection may have been disconnected in the receive or send phase
if (p != m_connections.end())
{
2004-03-21 03:03:37 +01:00
p->second->set_failed();
2004-02-26 13:59:01 +01:00
m_connections.erase(p);
}
2004-03-30 21:11:07 +02:00
else if (*i == m_listen_socket)
{
if (m_alerts.should_post(alert::fatal))
{
std::string msg = "cannot listen on the given interface '" + m_listen_interface.as_string() + "'";
m_alerts.post_alert(listen_failed_alert(msg));
}
2005-03-20 11:55:33 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-03-30 21:11:07 +02:00
std::string msg = "cannot listen on the given interface '" + m_listen_interface.as_string() + "'";
(*m_logger) << msg << "\n";
#endif
assert(m_listen_socket.unique());
m_listen_socket.reset();
}
2004-02-26 13:59:01 +01:00
}
#ifndef NDEBUG
2004-02-26 13:59:01 +01:00
check_invariant("after ERROR SOCKETS");
#endif
2004-10-14 03:17:04 +02:00
boost::posix_time::time_duration d = second_clock::universal_time() - timer;
2004-02-26 13:59:01 +01:00
if (d.seconds() < 1) continue;
2004-10-14 03:17:04 +02:00
timer = second_clock::universal_time();
2004-02-26 13:59:01 +01:00
// ************************
// THE SECTION BELOW IS EXECUTED ONCE EVERY SECOND
// ************************
#ifndef NDEBUG
2004-03-30 21:11:07 +02:00
if (loops_per_second > 800) std::cout << "\n\nloops: " << loops_per_second << "\n";
2004-02-26 13:59:01 +01:00
loops_per_second = 0;
#endif
2004-02-26 13:59:01 +01:00
// do the second_tick() on each connection
// this will update their statistics (download and upload speeds)
// also purge sockets that have timed out
// and keep sockets open by keeping them alive.
for (connection_map::iterator i = m_connections.begin();
i != m_connections.end();)
{
connection_map::iterator j = i;
++i;
// if this socket has timed out
// close it.
if (j->second->has_timed_out())
{
2004-02-26 13:59:01 +01:00
if (m_alerts.should_post(alert::debug))
{
2004-02-26 13:59:01 +01:00
m_alerts.post_alert(
2004-03-28 19:45:37 +02:00
peer_error_alert(
j->first->sender()
, j->second->id()
, "connection timed out"));
}
2004-03-21 03:03:37 +01:00
j->second->set_failed();
2004-02-26 13:59:01 +01:00
m_selector.remove(j->first);
m_connections.erase(j);
continue;
}
2004-02-26 13:59:01 +01:00
j->second->keep_alive();
}
// check each torrent for abortion or
// tracker updates
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
2004-03-23 23:58:18 +01:00
= m_torrents.begin(); i != m_torrents.end();)
2004-02-26 13:59:01 +01:00
{
2004-07-24 13:54:17 +02:00
torrent& t = *i->second;
if (t.is_aborted())
{
2004-07-24 13:54:17 +02:00
tracker_request req = t.generate_tracker_request();
2004-11-18 23:33:50 +01:00
assert(req.event == tracker_request::stopped);
2004-03-21 03:03:37 +01:00
req.listen_port = m_listen_interface.port;
req.key = m_key;
m_tracker_manager.queue_request(req, t.tracker_login());
2005-06-06 12:33:44 +02:00
if (m_alerts.should_post(alert::info))
{
m_alerts.post_alert(
tracker_announce_alert(
t.get_handle(), "tracker announce, event=stopped"));
}
2003-11-10 14:15:41 +01:00
#ifndef NDEBUG
2004-07-24 13:54:17 +02:00
sha1_hash i_hash = t.torrent_file().info_hash();
2003-11-10 14:15:41 +01:00
#endif
2004-03-23 23:58:18 +01:00
m_torrents.erase(i++);
2004-02-26 13:59:01 +01:00
assert(m_torrents.find(i_hash) == m_torrents.end());
continue;
}
2004-07-24 13:54:17 +02:00
else if (t.should_request())
2004-02-26 13:59:01 +01:00
{
2004-07-24 13:54:17 +02:00
tracker_request req = t.generate_tracker_request();
2004-03-21 03:03:37 +01:00
req.listen_port = m_listen_interface.port;
req.key = m_key;
m_tracker_manager.queue_request(req, t.tracker_login(), i->second);
2005-06-06 12:33:44 +02:00
if (m_alerts.should_post(alert::info))
{
m_alerts.post_alert(
tracker_announce_alert(
t.get_handle(), "tracker announce"));
}
}
2004-02-12 15:41:39 +01:00
2004-03-23 23:58:18 +01:00
// tick() will set the used upload quota
2004-07-24 13:54:17 +02:00
t.second_tick(m_stat);
2004-02-26 13:59:01 +01:00
++i;
}
purge_connections();
2004-04-18 14:28:02 +02:00
m_stat.second_tick();
2004-03-23 23:58:18 +01:00
// distribute the maximum upload rate among the torrents
2004-03-22 15:56:32 +01:00
allocate_resources(m_upload_rate == -1
2004-02-26 13:59:01 +01:00
? std::numeric_limits<int>::max()
: m_upload_rate
2004-03-23 23:58:18 +01:00
, m_torrents
2004-03-28 19:45:37 +02:00
, &torrent::m_ul_bandwidth_quota);
allocate_resources(m_download_rate == -1
? std::numeric_limits<int>::max()
: m_download_rate
, m_torrents
, &torrent::m_dl_bandwidth_quota);
2004-10-29 15:21:09 +02:00
allocate_resources(m_max_uploads == -1
? std::numeric_limits<int>::max()
: m_max_uploads
, m_torrents
, &torrent::m_uploads_quota);
allocate_resources(m_max_connections == -1
? std::numeric_limits<int>::max()
: m_max_connections
, m_torrents
, &torrent::m_connections_quota);
2004-03-23 23:58:18 +01:00
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
= m_torrents.begin(); i != m_torrents.end(); ++i)
2004-03-22 15:56:32 +01:00
{
2004-03-23 23:58:18 +01:00
i->second->distribute_resources();
2004-03-22 15:56:32 +01:00
}
2003-11-10 14:15:41 +01:00
2004-02-26 13:59:01 +01:00
m_tracker_manager.tick();
}
catch (std::bad_cast& e)
{
std::cerr << e.what() << "\n";
assert(false);
}
catch (std::exception& e)
{
std::cerr << e.what() << "\n";
assert(false);
}
catch (...)
{
std::cerr << "error!\n";
assert(false);
}
}
2004-02-26 13:59:01 +01:00
while (!m_tracker_manager.send_finished())
{
2004-02-26 13:59:01 +01:00
m_tracker_manager.tick();
boost::xtime t;
boost::xtime_get(&t, boost::TIME_UTC);
t.nsec += 100000000;
boost::thread::sleep(t);
2003-10-30 00:28:09 +01:00
}
2003-10-27 16:43:33 +01:00
2004-02-26 13:59:01 +01:00
}
// the return value from this function is valid only as long as the
// session is locked!
torrent* session_impl::find_torrent(const sha1_hash& info_hash)
{
std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
= m_torrents.find(info_hash);
2004-03-03 14:47:12 +01:00
#ifndef NDEBUG
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator j
2005-06-13 12:58:00 +02:00
= m_torrents.begin(); j != m_torrents.end(); ++j)
2004-03-03 14:47:12 +01:00
{
torrent* p = boost::get_pointer(j->second);
2004-04-17 14:29:35 +02:00
assert(p);
2004-03-03 14:47:12 +01:00
}
#endif
2004-02-26 13:59:01 +01:00
if (i != m_torrents.end()) return boost::get_pointer(i->second);
return 0;
}
2005-03-19 13:22:40 +01:00
#ifdef TORRENT_VERBOSE_LOGGING
2004-11-30 12:17:32 +01:00
boost::shared_ptr<logger> session_impl::create_log(std::string const& name)
2004-02-26 13:59:01 +01:00
{
2004-11-30 12:17:32 +01:00
// current options are file_logger, cout_logger and null_logger
return boost::shared_ptr<logger>(new file_logger(name + ".log"));
2004-02-26 13:59:01 +01:00
}
2003-11-09 19:17:09 +01:00
#endif
#ifndef NDEBUG
2004-02-26 13:59:01 +01:00
void session_impl::check_invariant(const char *place)
{
assert(place);
2004-01-25 05:18:08 +01:00
2004-02-26 13:59:01 +01:00
for (connection_map::iterator i = m_connections.begin();
2005-06-13 12:58:00 +02:00
i != m_connections.end(); ++i)
2004-02-26 13:59:01 +01:00
{
2004-03-28 19:45:37 +02:00
if (i->second->can_write() != m_selector.is_writability_monitored(i->first)
|| i->second->can_read() != m_selector.is_readability_monitored(i->first))
{
2004-02-26 13:59:01 +01:00
std::ofstream error_log("error.log", std::ios_base::app);
boost::shared_ptr<peer_connection> p = i->second;
2004-03-28 19:45:37 +02:00
error_log << "selector::is_writability_monitored() " << m_selector.is_writability_monitored(i->first) << "\n";
error_log << "selector::is_readability_monitored() " << m_selector.is_readability_monitored(i->first) << "\n";
error_log << "peer_connection::can_write() " << p->can_write() << "\n";
error_log << "peer_connection::can_read() " << p->can_read() << "\n";
error_log << "peer_connection::ul_quota_left " << p->m_ul_bandwidth_quota.left() << "\n";
error_log << "peer_connection::dl_quota_left " << p->m_dl_bandwidth_quota.left() << "\n";
error_log << "peer_connection::m_ul_bandwidth_quota.given " << p->m_ul_bandwidth_quota.given << "\n";
2004-02-26 13:59:01 +01:00
error_log << "peer_connection::get_peer_id " << p->get_peer_id() << "\n";
error_log << "place: " << place << "\n";
error_log.flush();
assert(false);
}
if (i->second->associated_torrent())
{
assert(i->second->associated_torrent()
->get_policy().has_connection(boost::get_pointer(i->second)));
}
}
2004-02-26 13:59:01 +01:00
}
#endif
2004-02-26 13:59:01 +01:00
}}
namespace libtorrent
{
2004-02-26 01:27:06 +01:00
session::session(
2004-03-29 00:44:40 +02:00
fingerprint const& id
, std::pair<int, int> listen_port_range
, char const* listen_interface)
2004-02-26 01:27:06 +01:00
: m_impl(listen_port_range, id, listen_interface)
2004-01-25 13:37:15 +01:00
, m_checker_impl(m_impl)
, m_thread(boost::ref(m_impl))
, m_checker_thread(boost::ref(m_checker_impl))
{
2004-01-26 11:29:00 +01:00
assert(listen_port_range.first > 0);
assert(listen_port_range.first < listen_port_range.second);
#ifndef NDEBUG
// this test was added after it came to my attention
// that devstudios managed c++ failed to generate
// correct code for boost.function
boost::function0<void> test = boost::ref(m_impl);
assert(!test.empty());
#endif
}
2004-03-29 00:44:40 +02:00
session::session(fingerprint const& id)
: m_impl(std::make_pair(0, 0), id)
2004-01-25 13:37:15 +01:00
, m_checker_impl(m_impl)
, m_thread(boost::ref(m_impl))
, m_checker_thread(boost::ref(m_checker_impl))
{
#ifndef NDEBUG
boost::function0<void> test = boost::ref(m_impl);
assert(!test.empty());
#endif
}
2004-11-01 00:16:08 +01:00
void session::disable_extensions()
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
std::fill(m_impl.m_extension_enabled, m_impl.m_extension_enabled
+ peer_connection::num_supported_extensions, false);
}
2004-12-21 13:30:09 +01:00
void session::set_peer_id(peer_id const& id)
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_peer_id = id;
}
2005-01-08 22:12:19 +01:00
void session::set_key(int key)
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_key = key;
}
2004-11-01 00:16:08 +01:00
void session::enable_extension(peer_connection::extension_index i)
{
assert(i >= 0);
assert(i < peer_connection::num_supported_extensions);
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_extension_enabled[i] = true;
}
2004-11-18 23:33:50 +01:00
std::vector<torrent_handle> session::get_torrents()
{
2004-12-21 13:30:09 +01:00
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
boost::mutex::scoped_lock l2(m_impl.m_mutex);
2004-11-18 23:33:50 +01:00
std::vector<torrent_handle> ret;
2004-12-21 13:30:09 +01:00
for (std::deque<detail::piece_checker_data>::iterator i
= m_checker_impl.m_torrents.begin()
, end(m_checker_impl.m_torrents.end()); i != end; ++i)
2004-11-18 23:33:50 +01:00
{
if (i->abort) continue;
2004-12-21 13:30:09 +01:00
ret.push_back(torrent_handle(&m_impl, &m_checker_impl
, i->info_hash));
2004-11-18 23:33:50 +01:00
}
2004-12-21 13:30:09 +01:00
for (detail::session_impl::torrent_map::iterator i
= m_impl.m_torrents.begin(), end(m_impl.m_torrents.end());
i != end; ++i)
2004-11-18 23:33:50 +01:00
{
2004-12-21 13:30:09 +01:00
if (i->second->is_aborted()) continue;
ret.push_back(torrent_handle(&m_impl, &m_checker_impl
, i->first));
2004-11-18 23:33:50 +01:00
}
return ret;
}
2003-11-26 15:54:56 +01:00
// TODO: add a check to see if filenames are accepted on the
// current platform.
// if the torrent already exists, this will throw duplicate_torrent
2004-01-07 01:48:02 +01:00
torrent_handle session::add_torrent(
entry const& metadata
, boost::filesystem::path const& save_path
, entry const& resume_data
, bool compact_mode)
{
2004-10-18 12:46:55 +02:00
assert(!save_path.empty());
torrent_info ti(metadata);
2004-03-30 01:25:13 +02:00
if (ti.begin_files() == ti.end_files())
throw std::runtime_error("no files in torrent");
2003-10-31 05:02:51 +01:00
{
// lock the session
boost::mutex::scoped_lock l(m_impl.m_mutex);
// is the torrent already active?
if (m_impl.find_torrent(ti.info_hash()))
throw duplicate_torrent();
2003-10-31 05:02:51 +01:00
}
2004-08-30 11:08:36 +02:00
// lock the checker_thread
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
2003-10-31 05:02:51 +01:00
2004-08-30 11:08:36 +02:00
// is the torrent currently being checked?
if (m_checker_impl.find_torrent(ti.info_hash()))
throw duplicate_torrent();
// create the torrent and the data associated with
// the checker thread and store it before starting
// the thread
2004-01-02 21:46:24 +01:00
boost::shared_ptr<torrent> torrent_ptr(
new torrent(m_impl, metadata, save_path, m_impl.m_listen_interface
, compact_mode));
2003-10-31 05:02:51 +01:00
detail::piece_checker_data d;
d.torrent_ptr = torrent_ptr;
d.save_path = save_path;
d.info_hash = ti.info_hash();
2004-09-08 01:16:11 +02:00
d.resume_data = resume_data;
2004-11-21 11:49:02 +01:00
2003-10-31 05:02:51 +01:00
// add the torrent to the queue to be checked
m_checker_impl.m_torrents.push_back(d);
// and notify the thread that it got another
// job in its queue
m_checker_impl.m_cond.notify_one();
2003-10-31 05:02:51 +01:00
return torrent_handle(&m_impl, &m_checker_impl, ti.info_hash());
}
torrent_handle session::add_torrent(
char const* tracker_url
, sha1_hash const& info_hash
, boost::filesystem::path const& save_path
, entry const&
, bool compact_mode)
{
2005-04-24 02:50:52 +02:00
// TODO: support resume data in this case
2004-10-18 12:46:55 +02:00
assert(!save_path.empty());
{
// lock the checker_thread
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
// is the torrent currently being checked?
if (m_checker_impl.find_torrent(info_hash))
throw duplicate_torrent();
}
2004-08-30 11:08:36 +02:00
// lock the session
boost::mutex::scoped_lock l(m_impl.m_mutex);
2004-11-01 00:16:08 +01:00
// the metadata extension has to be enabled for this to work
assert(m_impl.m_extension_enabled
[peer_connection::extended_metadata_message]);
2004-08-30 11:08:36 +02:00
// is the torrent already active?
if (m_impl.find_torrent(info_hash))
throw duplicate_torrent();
// create the torrent and the data associated with
// the checker thread and store it before starting
// the thread
boost::shared_ptr<torrent> torrent_ptr(
2004-11-01 00:16:08 +01:00
new torrent(m_impl, tracker_url, info_hash, save_path
, m_impl.m_listen_interface, compact_mode));
m_impl.m_torrents.insert(
std::make_pair(info_hash, torrent_ptr)).first;
return torrent_handle(&m_impl, &m_checker_impl, info_hash);
}
2003-11-08 03:16:26 +01:00
void session::remove_torrent(const torrent_handle& h)
{
if (h.m_ses != &m_impl) return;
2004-02-29 17:39:52 +01:00
assert(h.m_chk == &m_checker_impl || h.m_chk == 0);
assert(h.m_ses != 0);
2003-11-28 18:29:27 +01:00
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
torrent* t = m_impl.find_torrent(h.m_info_hash);
if (t != 0)
{
t->abort();
return;
}
}
2004-02-29 17:39:52 +01:00
if (h.m_chk)
2003-11-28 18:29:27 +01:00
{
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
detail::piece_checker_data* d = m_checker_impl.find_torrent(h.m_info_hash);
if (d != 0)
{
2005-01-08 22:12:19 +01:00
if (d->processing) d->abort = true;
else m_checker_impl.remove_torrent(h.m_info_hash);
2003-11-28 18:29:27 +01:00
return;
}
}
2003-11-08 03:16:26 +01:00
}
2004-02-26 13:59:01 +01:00
bool session::listen_on(
std::pair<int, int> const& port_range
, const char* net_interface)
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
if (m_impl.m_listen_socket)
{
m_impl.m_selector.remove(m_impl.m_listen_socket);
m_impl.m_listen_socket.reset();
}
2004-04-18 15:41:08 +02:00
m_impl.m_incoming_connection = false;
2004-02-26 13:59:01 +01:00
m_impl.m_listen_port_range = port_range;
m_impl.m_listen_interface = address(net_interface, port_range.first);
m_impl.open_listen_port();
return m_impl.m_listen_socket;
}
unsigned short session::listen_port() const
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
return m_impl.m_listen_interface.port;
}
2004-04-18 14:28:02 +02:00
session_status session::status() const
{
session_status s;
2004-04-18 15:41:08 +02:00
s.has_incoming_connections = m_impl.m_incoming_connection;
s.num_peers = (int)m_impl.m_connections.size();
s.download_rate = m_impl.m_stat.download_rate();
s.upload_rate = m_impl.m_stat.upload_rate();
s.payload_download_rate = m_impl.m_stat.download_payload_rate();
s.payload_upload_rate = m_impl.m_stat.upload_payload_rate();
s.total_download = m_impl.m_stat.total_protocol_download()
+ m_impl.m_stat.total_payload_download();
s.total_upload = m_impl.m_stat.total_protocol_upload()
+ m_impl.m_stat.total_payload_upload();
s.total_payload_download = m_impl.m_stat.total_payload_download();
s.total_payload_upload = m_impl.m_stat.total_payload_upload();
2004-04-18 14:28:02 +02:00
return s;
}
2004-02-26 13:59:01 +01:00
bool session::is_listening() const
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
return m_impl.m_listen_socket;
}
void session::set_http_settings(const http_settings& s)
{
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_settings = s;
}
session::~session()
{
{
2004-08-30 11:08:36 +02:00
// lock the main thread and abort it
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_abort = true;
}
2003-10-31 05:02:51 +01:00
{
boost::mutex::scoped_lock l(m_checker_impl.m_mutex);
// abort the checker thread
m_checker_impl.m_abort = true;
2003-10-31 05:02:51 +01:00
// abort the currently checking torrent
if (!m_checker_impl.m_torrents.empty())
{
m_checker_impl.m_torrents.front().abort = true;
}
m_checker_impl.m_cond.notify_one();
}
m_thread.join();
m_checker_thread.join();
}
2004-10-29 15:21:09 +02:00
void session::set_max_uploads(int limit)
{
assert(limit > 0 || limit == -1);
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_max_uploads = limit;
}
void session::set_max_connections(int limit)
{
assert(limit > 0 || limit == -1);
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_max_connections = limit;
}
2003-11-09 19:17:09 +01:00
void session::set_upload_rate_limit(int bytes_per_second)
{
2003-11-10 14:15:41 +01:00
assert(bytes_per_second > 0 || bytes_per_second == -1);
2003-11-09 19:17:09 +01:00
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_upload_rate = bytes_per_second;
2004-03-28 19:45:37 +02:00
}
void session::set_download_rate_limit(int bytes_per_second)
{
assert(bytes_per_second > 0 || bytes_per_second == -1);
boost::mutex::scoped_lock l(m_impl.m_mutex);
m_impl.m_download_rate = bytes_per_second;
2003-11-09 19:17:09 +01:00
}
2003-11-29 17:34:07 +01:00
std::auto_ptr<alert> session::pop_alert()
{
2003-12-22 08:14:35 +01:00
if (m_impl.m_alerts.pending())
return m_impl.m_alerts.get();
else
return std::auto_ptr<alert>(0);
2003-11-29 17:34:07 +01:00
}
2003-12-22 08:14:35 +01:00
void session::set_severity_level(alert::severity_t s)
2003-12-21 18:28:27 +01:00
{
2003-12-22 08:14:35 +01:00
m_impl.m_alerts.set_severity(s);
}
2004-01-07 01:48:02 +01:00
void detail::piece_checker_data::parse_resume_data(
const entry& resume_data
2005-06-16 17:41:04 +02:00
, const torrent_info& info
, std::string& error)
2004-01-07 01:48:02 +01:00
{
// if we don't have any resume data, return
if (resume_data.type() == entry::undefined_t) return;
entry rd = resume_data;
try
{
2004-03-05 13:04:47 +01:00
if (rd["file-format"].string() != "libtorrent resume file")
2005-06-16 17:41:04 +02:00
{
error = "missing file format tag";
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
2005-06-16 17:41:04 +02:00
if (rd["file-version"].integer() > 1)
{
error = "incompatible file version "
+ boost::lexical_cast<std::string>(rd["file-version"].integer());
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
// verify info_hash
2004-03-05 13:04:47 +01:00
const std::string &hash = rd["info-hash"].string();
std::string real_hash((char*)info.info_hash().begin(), (char*)info.info_hash().end());
2004-01-07 01:48:02 +01:00
if (hash != real_hash)
2005-06-16 17:41:04 +02:00
{
error = "mismatching info-hash: " + hash;
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
2004-01-17 21:04:19 +01:00
// the peers
2004-10-03 13:39:34 +02:00
if (rd.find_key("peers"))
2004-01-17 21:04:19 +01:00
{
2004-10-03 13:39:34 +02:00
entry::list_type& peer_list = rd["peers"].list();
2004-01-17 21:04:19 +01:00
2004-10-03 13:39:34 +02:00
std::vector<address> tmp_peers;
tmp_peers.reserve(peer_list.size());
for (entry::list_type::iterator i = peer_list.begin();
2005-06-16 17:41:04 +02:00
i != peer_list.end(); ++i)
2004-10-03 13:39:34 +02:00
{
address a(
(*i)["ip"].string().c_str()
, (unsigned short)(*i)["port"].integer());
tmp_peers.push_back(a);
}
2004-01-17 21:04:19 +01:00
2004-10-03 13:39:34 +02:00
peers.swap(tmp_peers);
}
2004-01-17 21:04:19 +01:00
2004-01-07 01:48:02 +01:00
// read piece map
2004-03-05 13:04:47 +01:00
const entry::list_type& slots = rd["slots"].list();
2004-01-25 05:18:08 +01:00
if ((int)slots.size() > info.num_pieces())
2005-06-16 17:41:04 +02:00
{
2005-06-20 23:30:39 +02:00
error = "file has more slots than torrent (slots: "
+ boost::lexical_cast<std::string>(slots.size()) + " size: "
+ boost::lexical_cast<std::string>(info.num_pieces()) + " )";
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
2004-01-12 04:05:10 +01:00
std::vector<int> tmp_pieces;
2004-01-07 01:48:02 +01:00
tmp_pieces.reserve(slots.size());
for (entry::list_type::const_iterator i = slots.begin();
2005-06-16 17:41:04 +02:00
i != slots.end(); ++i)
2004-01-07 01:48:02 +01:00
{
int index = (int)i->integer();
2004-01-07 01:48:02 +01:00
if (index >= info.num_pieces() || index < -2)
2005-06-16 17:41:04 +02:00
{
2005-06-20 23:30:39 +02:00
error = "too high index number in slot map (index: "
+ boost::lexical_cast<std::string>(index) + " size: "
+ boost::lexical_cast<std::string>(info.num_pieces()) + ")";
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
tmp_pieces.push_back(index);
}
2004-03-17 13:14:44 +01:00
int num_blocks_per_piece = (int)rd["blocks per piece"].integer();
2004-01-15 17:45:34 +01:00
if (num_blocks_per_piece != info.piece_length() / torrent_ptr->block_size())
2005-06-16 17:41:04 +02:00
{
2005-06-20 23:30:39 +02:00
error = "invalid number of blocks per piece ("
+ boost::lexical_cast<std::string>(num_blocks_per_piece) + ")";
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
2004-01-12 04:05:10 +01:00
// the unfinished pieces
2004-03-17 13:14:44 +01:00
entry::list_type& unfinished = rd["unfinished"].list();
2004-01-07 01:48:02 +01:00
2004-01-12 04:05:10 +01:00
std::vector<piece_picker::downloading_piece> tmp_unfinished;
2004-01-07 01:48:02 +01:00
tmp_unfinished.reserve(unfinished.size());
2004-01-12 21:31:27 +01:00
for (entry::list_type::iterator i = unfinished.begin();
2005-06-16 17:41:04 +02:00
i != unfinished.end(); ++i)
2004-01-07 01:48:02 +01:00
{
piece_picker::downloading_piece p;
2004-03-17 13:14:44 +01:00
p.index = (int)(*i)["piece"].integer();
2004-01-07 01:48:02 +01:00
if (p.index < 0 || p.index >= info.num_pieces())
2005-06-16 17:41:04 +02:00
{
2005-06-20 23:30:39 +02:00
error = "invalid piece index in unfinished piece list (index: "
2005-06-21 00:54:17 +02:00
+ boost::lexical_cast<std::string>(p.index) + " size: "
2005-06-20 23:30:39 +02:00
+ boost::lexical_cast<std::string>(info.num_pieces()) + ")";
2004-01-07 01:48:02 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-07 01:48:02 +01:00
2004-03-17 13:14:44 +01:00
const std::string& bitmask = (*i)["bitmask"].string();
2004-01-07 01:48:02 +01:00
const int num_bitmask_bytes = std::max(num_blocks_per_piece / 8, 1);
2005-06-16 17:41:04 +02:00
if ((int)bitmask.size() != num_bitmask_bytes)
{
error = "invalid size of bitmask (" + boost::lexical_cast<std::string>(bitmask.size()) + ")";
return;
}
2004-01-07 01:48:02 +01:00
for (int j = 0; j < num_bitmask_bytes; ++j)
{
unsigned char bits = bitmask[j];
for (int k = 0; k < 8; ++k)
{
const int bit = j * 8 + k;
if (bits & (1 << k))
p.finished_blocks[bit] = true;
}
}
2004-01-24 18:14:03 +01:00
if (p.finished_blocks.count() == 0) continue;
std::vector<int>::iterator slot_iter
= std::find(tmp_pieces.begin(), tmp_pieces.end(), p.index);
if (slot_iter == tmp_pieces.end())
{
// this piece is marked as unfinished
// but doesn't have any storage
2005-06-16 17:41:04 +02:00
error = "piece " + boost::lexical_cast<std::string>(p.index) + " is "
"marked as unfinished, but doesn't have any storage";
2004-01-24 18:14:03 +01:00
return;
}
assert(*slot_iter == p.index);
int slot_index = static_cast<int>(slot_iter - tmp_pieces.begin());
2004-01-24 18:14:03 +01:00
unsigned long adler
= torrent_ptr->filesystem().piece_crc(
slot_index
, torrent_ptr->block_size()
, p.finished_blocks);
2004-03-05 13:04:47 +01:00
const entry& ad = (*i)["adler32"];
// crc's didn't match, don't use the resume data
if (ad.integer() != adler)
2005-06-16 17:41:04 +02:00
{
error = "checksum mismatch on piece " + boost::lexical_cast<std::string>(p.index);
2004-03-05 13:04:47 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-01-24 18:14:03 +01:00
2004-01-07 01:48:02 +01:00
tmp_unfinished.push_back(p);
}
2004-01-17 21:04:19 +01:00
// verify file sizes
2004-01-12 04:05:10 +01:00
2005-03-05 00:45:16 +01:00
std::vector<std::pair<size_type, std::time_t> > file_sizes;
2004-03-05 13:04:47 +01:00
entry::list_type& l = rd["file sizes"].list();
for (entry::list_type::iterator i = l.begin();
2005-03-05 00:45:16 +01:00
i != l.end(); ++i)
{
2005-03-05 00:45:16 +01:00
file_sizes.push_back(std::pair<size_type, std::time_t>(
i->list().front().integer()
, i->list().back().integer()));
}
2004-11-18 23:33:50 +01:00
2004-11-21 11:49:02 +01:00
if ((int)tmp_pieces.size() == info.num_pieces()
2004-11-18 23:33:50 +01:00
&& std::find_if(tmp_pieces.begin(), tmp_pieces.end()
, boost::bind(std::less<int>(), _1, 0)) == tmp_pieces.end())
{
2004-11-21 11:49:02 +01:00
if (info.num_files() != (int)file_sizes.size())
2005-06-16 17:41:04 +02:00
{
2005-06-20 23:30:39 +02:00
error = "the number of files does not match the torrent (num: "
+ boost::lexical_cast<std::string>(file_sizes.size()) + " actual: "
+ boost::lexical_cast<std::string>(info.num_files()) + ")";
2004-11-18 23:33:50 +01:00
return;
2005-06-16 17:41:04 +02:00
}
2004-11-18 23:33:50 +01:00
2005-03-05 00:45:16 +01:00
std::vector<std::pair<size_type, std::time_t> >::iterator
fs = file_sizes.begin();
2004-11-18 23:33:50 +01:00
// the resume data says we have the entire torrent
// make sure the file sizes are the right ones
for (torrent_info::file_iterator i = info.begin_files()
, end(info.end_files()); i != end; ++i, ++fs)
{
2005-06-16 17:41:04 +02:00
if (i->size != fs->first)
{
error = "file size for '" + i->path.native_file_string() + "' was expected to be "
+ boost::lexical_cast<std::string>(i->size) + " bytes";
return;
}
2004-11-18 23:33:50 +01:00
}
}
2005-06-16 17:41:04 +02:00
if (!match_filesizes(info, save_path, file_sizes, &error))
2004-01-17 21:04:19 +01:00
return;
2004-01-12 04:05:10 +01:00
2004-01-07 01:48:02 +01:00
piece_map.swap(tmp_pieces);
unfinished_pieces.swap(tmp_unfinished);
}
catch (invalid_encoding)
{
return;
}
catch (type_error)
{
return;
}
2004-03-05 20:03:02 +01:00
catch (file_error)
{
return;
}
2004-01-07 01:48:02 +01:00
}
}