entry dht_tracker::state() const
	{
		entry ret(entry::dictionary_t);
		{
			entry nodes(entry::list_t);
			for (node_impl::iterator i(m_dht.begin())
				, end(m_dht.end()); i != end; ++i)
			{
				std::string node;
				std::back_insert_iterator<std::string> out(node);
				write_endpoint(i->addr, out);
				nodes.list().push_back(entry(node));
			}
			bucket_t cache;
			m_dht.replacement_cache(cache);
			for (bucket_t::iterator i(cache.begin())
				, end(cache.end()); i != end; ++i)
			{
				std::string node;
				std::back_insert_iterator<std::string> out(node);
				write_endpoint(i->addr, out);
				nodes.list().push_back(entry(node));
			}
			if (!nodes.list().empty())
				ret["nodes"] = nodes;
		}
		
		ret["node-id"] = boost::lexical_cast<std::string>(m_dht.nid());
		return ret;
	}
示例#2
0
	void on_read(lt::error_code const& ec, std::size_t bytes_transferred)
	{
		if (ec) return;

		using libtorrent::entry;
		using libtorrent::bdecode;

		int pos;
		error_code err;

		// since the simulation is single threaded, we can get away with just
		// allocating a single of these
		static bdecode_node msg;
		int ret = bdecode(m_buffer, m_buffer + bytes_transferred, msg, err, &pos, 10, 500);
		if (ret != 0) return;

		if (msg.type() != bdecode_node::dict_t) return;

		libtorrent::dht::msg m(msg, m_ep);
		dht().incoming(m);

		sock().async_receive_from(asio::mutable_buffers_1(m_buffer, sizeof(m_buffer))
			, m_ep, [&](lt::error_code const& ec, std::size_t bytes_transferred)
				{ this->on_read(ec, bytes_transferred); });
	}
示例#3
0
entry bdecode(const path &file) {
	ifstream fs(file, ifstream::binary);
	if(fs.is_open()) {
		fs.unsetf(ifstream::skipws);
		return libtorrent::bdecode(std::istream_iterator<char>(fs), std::istream_iterator<char>());
	}
	else return entry();
}
void test_buffer()
{
	char data[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

	buffer b;

	TEST_CHECK(b.size() == 0);
	TEST_CHECK(b.capacity() == 0);
	TEST_CHECK(b.empty());
	
	b.resize(10);
	TEST_CHECK(b.size() == 10);
	TEST_CHECK(b.capacity() == 10);
	
	std::memcpy(b.begin(), data, 10);
	b.reserve(50);
	TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0);
	TEST_CHECK(b.capacity() == 50);
	
	b.erase(b.begin() + 6, b.end());
	TEST_CHECK(std::memcmp(b.begin(), data, 6) == 0);
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 6);

	b.insert(b.begin(), data + 5, data + 10);
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 11);
	TEST_CHECK(std::memcmp(b.begin(), data + 5, 5) == 0);

	b.clear();
	TEST_CHECK(b.size() == 0);
	TEST_CHECK(b.capacity() == 50);

	b.insert(b.end(), data, data + 10);
	TEST_CHECK(b.size() == 10);
	TEST_CHECK(std::memcmp(b.begin(), data, 10) == 0);
	
	b.erase(b.begin(), b.end());
	TEST_CHECK(b.capacity() == 50);
	TEST_CHECK(b.size() == 0);

	buffer().swap(b);
	TEST_CHECK(b.capacity() == 0);

}
示例#5
0
    static void construct(PyObject* x, converter::rvalue_from_python_stage1_data* data)
    {
        using libtorrent::wchar_utf8;
        void* storage = ((converter::rvalue_from_python_storage<
                          std::string>*)data)->storage.bytes;

        if (PyUnicode_Check(x))
        {
            std::wstring str;
            str.resize(PyUnicode_GetSize(x) + 1, 0);
#if PY_VERSION_HEX >= 0x03020000
            int len = PyUnicode_AsWideChar(x, &str[0], str.size());
#else
            int len = PyUnicode_AsWideChar((PyUnicodeObject*)x, &str[0], str.size());
#endif
            if (len > -1)
            {
                assert(len < int(str.size()));
                str[len] = 0;
            }
            else str[str.size()-1] = 0;

            std::string utf8;
            wchar_utf8(str, utf8);
            new (storage) std::string(utf8);
        }
        else
        {
#if PY_VERSION_HEX >= 0x03000000
            new (storage) std::string(PyBytes_AsString(x));
#else
            new (storage) std::string(PyString_AsString(x));
#endif
        }
        data->convertible = storage;
    }
	void dht_tracker::send_packet(msg const& m)
		try
	{
		using libtorrent::bencode;
		using libtorrent::entry;
		entry e(entry::dictionary_t);
		TORRENT_ASSERT(!m.transaction_id.empty() || m.message_id == messages::error);
		e["t"] = m.transaction_id;
		static char const version_str[] = {'L', 'T'
			, LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR};
		e["v"] = std::string(version_str, version_str + 4);

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		TORRENT_LOG(dht_tracker) << time_now_string()
			<< " SENDING [" << m.addr << "]:";
		TORRENT_LOG(dht_tracker) << "   transaction: " << m.transaction_id;
#endif

		if (m.message_id == messages::error)
		{
			TORRENT_ASSERT(m.reply);
			e["y"] = "e";
			entry error_list(entry::list_t);
			TORRENT_ASSERT(m.error_code > 200 && m.error_code <= 204);
			error_list.list().push_back(entry(m.error_code));
			error_list.list().push_back(entry(m.error_msg));
			e["e"] = error_list;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(dht_tracker) << time_now_string()
				<< "   outgoing error: " << m.error_code << " " << m.error_msg;
#endif
		}
		else if (m.reply)
		{
			e["y"] = "r";
			e["r"] = entry(entry::dictionary_t);
			entry& r = e["r"];
			r["id"] = std::string(m.id.begin(), m.id.end());

#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(dht_tracker) << time_now_string()
				<< "   reply: " << messages::ids[m.message_id];
#endif

			if (m.write_token.type() != entry::undefined_t)
				r["token"] = m.write_token;

			switch (m.message_id)
			{
				case messages::ping:
					break;
				case messages::find_node:
				{
					write_nodes_entry(r, m);
					break;
				}
				case messages::get_peers:
				{
					if (m.peers.empty())
					{
						write_nodes_entry(r, m);
					}
					else
					{
						r["values"] = entry(entry::list_t);
						entry& p = r["values"];
						std::string endpoint;
						for (msg::peers_t::const_iterator i = m.peers.begin()
							, end(m.peers.end()); i != end; ++i)
						{
							endpoint.resize(18);
							std::string::iterator out = endpoint.begin();
							write_endpoint(*i, out);
							endpoint.resize(out - endpoint.begin());
							p.list().push_back(entry(endpoint));
						}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
						TORRENT_LOG(dht_tracker) << "   peers: " << m.peers.size();
#endif
					}
					break;
				}

				case messages::announce_peer:
					break;
				break;
			}
		}
		else
		{
			e["y"] = "q";
			e["a"] = entry(entry::dictionary_t);
			entry& a = e["a"];
			a["id"] = std::string(m.id.begin(), m.id.end());

			if (m.write_token.type() != entry::undefined_t)
				a["token"] = m.write_token;
			TORRENT_ASSERT(m.message_id <= messages::error);
			e["q"] = messages::ids[m.message_id];

#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(dht_tracker) << "   query: "
				<< messages::ids[m.message_id];
#endif

			switch (m.message_id)
			{
				case messages::find_node:
				{
					a["target"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   target: "
						<< boost::lexical_cast<std::string>(m.info_hash);
#endif
					break;
				}
				case messages::get_peers:
				{
					a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   info_hash: "
						<< boost::lexical_cast<std::string>(m.info_hash);
#endif
					break;	
				}
				case messages::announce_peer:
					a["port"] = m.port;
					a["info_hash"] = std::string(m.info_hash.begin(), m.info_hash.end());
					a["token"] = m.write_token;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker)
						<< "   port: " << m.port
						<< "   info_hash: " << boost::lexical_cast<std::string>(m.info_hash);
#endif
					break;
				default: break;
			}

		}

		m_send_buf.clear();
		bencode(std::back_inserter(m_send_buf), e);
		error_code ec;
		m_socket.send_to(asio::buffer(&m_send_buf[0]
			, (int)m_send_buf.size()), m.addr, 0, ec);
		if (ec) return;

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		m_total_out_bytes += m_send_buf.size();
		
		if (m.reply)
		{
			++m_replies_sent[m.message_id];
			m_replies_bytes_sent[m.message_id] += int(m_send_buf.size());
		}
		else
		{
			m_queries_out_bytes += m_send_buf.size();
		}
		TORRENT_LOG(dht_tracker) << e;
#endif

		if (!m.piggy_backed_ping) return;
		
		msg pm;
		pm.reply = false;
		pm.piggy_backed_ping = false;
		pm.message_id = messages::ping;
		pm.transaction_id = m.ping_transaction_id;
		pm.id = m.id;
		pm.addr = m.addr;
	
		send_packet(pm);	
	}
	catch (std::exception&)
	{
		// m_send may fail with "no route to host"
		// but it shouldn't throw since an error code
		// is passed in instead
		TORRENT_ASSERT(false);
	}
	// translate bittorrent kademlia message into the generice kademlia message
	// used by the library
	void dht_tracker::on_receive(error_code const& error, size_t bytes_transferred)
		try
	{
		if (error == asio::error::operation_aborted) return;
		if (!m_socket.is_open()) return;
	
		int current_buffer = m_buffer;
		m_buffer = (m_buffer + 1) & 1;
		m_socket.async_receive_from(asio::buffer(&m_in_buf[m_buffer][0]
			, m_in_buf[m_buffer].size()), m_remote_endpoint[m_buffer]
			, m_strand.wrap(bind(&dht_tracker::on_receive, self(), _1, _2)));

		if (error) return;

		node_ban_entry* match = 0;
		node_ban_entry* min = m_ban_nodes;
		ptime now = time_now();
		for (node_ban_entry* i = m_ban_nodes; i < m_ban_nodes + num_ban_nodes; ++i)
		{
			if (i->src == m_remote_endpoint[current_buffer])
			{
				match = i;
				break;
			}
			if (i->count < min->count) min = i;
		}

		if (match)
		{
			++match->count;
			if (match->count >= 20)
			{
				if (now < match->limit)
				{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					if (match->count == 20)
					{
						TORRENT_LOG(dht_tracker) << time_now_string() << " BANNING PEER [ ip: "
							<< m_remote_endpoint[current_buffer] << " | "
							"time: " << total_seconds((now - match->limit) + seconds(5))
							<< " | count: " << match->count << " ]";
					}
#endif
					// we've received 20 messages in less than 5 seconds from
					// this node. Ignore it until it's silent for 5 minutes
					match->limit = now + minutes(5);
					return;
				}

				// we got 50 messages from this peer, but it was in
				// more than 5 seconds. Reset the counter and the timer
				match->count = 0;
				match->limit = now + seconds(5);
			}
		}
		else
		{
			min->count = 1;
			min->limit = now + seconds(5);
			min->src = m_remote_endpoint[current_buffer];
		}

#ifdef TORRENT_DHT_VERBOSE_LOGGING
		++m_total_message_input;
		m_total_in_bytes += bytes_transferred;
#endif

		try
		{
			using libtorrent::entry;
			using libtorrent::bdecode;
			
			TORRENT_ASSERT(bytes_transferred > 0);

			entry e = bdecode(m_in_buf[current_buffer].begin()
				, m_in_buf[current_buffer].end());

#ifdef TORRENT_DHT_VERBOSE_LOGGING
			TORRENT_LOG(dht_tracker) << time_now_string() << " RECEIVED ["
				<< m_remote_endpoint[current_buffer] << "]:";
#endif

			libtorrent::dht::msg m;
			m.message_id = 0;
			m.addr = m_remote_endpoint[current_buffer];
			m.transaction_id = e["t"].string();

#ifdef TORRENT_DHT_VERBOSE_LOGGING
			try
			{
				entry const* ver = e.find_key("v");
				if (!ver) throw std::exception();

				std::string const& client = ver->string();
				if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "UT"))
				{
					++m_ut_message_input;
					TORRENT_LOG(dht_tracker) << "   client: uTorrent";
				}
				else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "LT"))
				{
					++m_lt_message_input;
					TORRENT_LOG(dht_tracker) << "   client: libtorrent";
				}
				else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MP"))
				{
					++m_mp_message_input;
					TORRENT_LOG(dht_tracker) << "   client: MooPolice";
				}
				else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "GR"))
				{
					++m_gr_message_input;
					TORRENT_LOG(dht_tracker) << "   client: GetRight";
				}
				else if (client.size() > 1 && std::equal(client.begin(), client.begin() + 2, "MO"))
				{
					++m_mo_message_input;
					TORRENT_LOG(dht_tracker) << "   client: Mono Torrent";
				}
				else
				{
					TORRENT_LOG(dht_tracker) << "   client: " << client;
				}
			}
			catch (std::exception&)
			{
				TORRENT_LOG(dht_tracker) << "   client: generic";
			};
#endif

			std::string const& msg_type = e["y"].string();

			if (msg_type == "r")
			{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
				TORRENT_LOG(dht_tracker) << "   reply: transaction: "
					<< m.transaction_id;
#endif

				m.reply = true;
				entry const& r = e["r"];
				std::string const& id = r["id"].string();
				if (id.size() != 20) throw std::runtime_error("invalid size of id");
				std::copy(id.begin(), id.end(), m.id.begin());

				if (entry const* n = r.find_key("values"))
				{
					m.peers.clear();
					if (n->list().size() == 1)
					{
						// assume it's mainline format
						std::string const& peers = n->list().front().string();
						std::string::const_iterator i = peers.begin();
						std::string::const_iterator end = peers.end();

						while (std::distance(i, end) >= 6)
							m.peers.push_back(read_v4_endpoint<tcp::endpoint>(i));
					}
					else
					{
						// assume it's uTorrent/libtorrent format
						read_endpoint_list<tcp::endpoint>(n, m.peers);
					}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   peers: " << m.peers.size();
#endif
				}

				m.nodes.clear();
				if (entry const* n = r.find_key("nodes"))				
				{
					std::string const& nodes = n->string();
					std::string::const_iterator i = nodes.begin();
					std::string::const_iterator end = nodes.end();

					while (std::distance(i, end) >= 26)
					{
						node_id id;
						std::copy(i, i + 20, id.begin());
						i += 20;
						m.nodes.push_back(libtorrent::dht::node_entry(
							id, read_v4_endpoint<udp::endpoint>(i)));
					}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   nodes: " << m.nodes.size();
#endif
				}

				if (entry const* n = r.find_key("nodes2"))				
				{
					entry::list_type const& contacts = n->list();
					for (entry::list_type::const_iterator i = contacts.begin()
						, end(contacts.end()); i != end; ++i)
					{
						std::string const& p = i->string();
						if (p.size() < 6 + 20) continue;
						std::string::const_iterator in = p.begin();

						node_id id;
						std::copy(in, in + 20, id.begin());
						in += 20;
						if (p.size() == 6 + 20)
							m.nodes.push_back(libtorrent::dht::node_entry(
								id, read_v4_endpoint<udp::endpoint>(in)));
						else if (p.size() == 18 + 20)
							m.nodes.push_back(libtorrent::dht::node_entry(
								id, read_v6_endpoint<udp::endpoint>(in)));
					}
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   nodes2 + nodes: " << m.nodes.size();
#endif
				}

				entry const* token = r.find_key("token");
				if (token) m.write_token = *token;
			}
			else if (msg_type == "q")
			{
				m.reply = false;
				entry const& a = e["a"];
				std::string const& id = a["id"].string();
				if (id.size() != 20) throw std::runtime_error("invalid size of id");
				std::copy(id.begin(), id.end(), m.id.begin());

				std::string request_kind(e["q"].string());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
				TORRENT_LOG(dht_tracker) << "   query: " << request_kind;
#endif

				if (request_kind == "ping")
				{
					m.message_id = libtorrent::dht::messages::ping;
				}
				else if (request_kind == "find_node")
				{
					std::string const& target = a["target"].string();
					if (target.size() != 20) throw std::runtime_error("invalid size of target id");
					std::copy(target.begin(), target.end(), m.info_hash.begin());
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   target: "
						<< boost::lexical_cast<std::string>(m.info_hash);
#endif

					m.message_id = libtorrent::dht::messages::find_node;
				}
				else if (request_kind == "get_peers")
				{
					std::string const& info_hash = a["info_hash"].string();
					if (info_hash.size() != 20) throw std::runtime_error("invalid size of info-hash");
					std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
					m.message_id = libtorrent::dht::messages::get_peers;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   info_hash: "
						<< boost::lexical_cast<std::string>(m.info_hash);
#endif
				}
				else if (request_kind == "announce_peer")
				{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					++m_announces;
#endif
					std::string const& info_hash = a["info_hash"].string();
					if (info_hash.size() != 20)
						throw std::runtime_error("invalid size of info-hash");
					std::copy(info_hash.begin(), info_hash.end(), m.info_hash.begin());
					m.port = a["port"].integer();
					m.write_token = a["token"];
					m.message_id = libtorrent::dht::messages::announce_peer;
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "   info_hash: "
						<< boost::lexical_cast<std::string>(m.info_hash);
					TORRENT_LOG(dht_tracker) << "   port: " << m.port;

					if (!m_dht.verify_token(m))
						++m_failed_announces;
#endif
				}
				else
				{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
					TORRENT_LOG(dht_tracker) << "  *** UNSUPPORTED REQUEST *** : "
						<< request_kind;
#endif
					throw std::runtime_error("unsupported request: " + request_kind);
				}
			}
			else if (msg_type == "e")
			{
				entry::list_type const& list = e["e"].list();
				m.message_id = messages::error;
				m.error_msg = list.back().string();
				m.error_code = list.front().integer();
#ifdef TORRENT_DHT_VERBOSE_LOGGING
				TORRENT_LOG(dht_tracker) << "   incoming error: " << m.error_code << " "
					<< m.error_msg;
#endif
				throw std::runtime_error("DHT error message");
			}
			else
			{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
				TORRENT_LOG(dht_tracker) << "  *** UNSUPPORTED MESSAGE TYPE *** : "
					<< msg_type;
#endif
				throw std::runtime_error("unsupported message type: " + msg_type);
			}

#ifdef TORRENT_DHT_VERBOSE_LOGGING
			if (!m.reply)
			{
				++m_queries_received[m.message_id];
				m_queries_bytes_received[m.message_id] += int(bytes_transferred);
			}
			TORRENT_LOG(dht_tracker) << e;
#endif
			TORRENT_ASSERT(m.message_id != messages::error);
			m_dht.incoming(m);
		}
		catch (std::exception& e)
		{
#ifdef TORRENT_DHT_VERBOSE_LOGGING
			int current_buffer = (m_buffer + 1) & 1;
			std::string msg(m_in_buf[current_buffer].begin()
				, m_in_buf[current_buffer].begin() + bytes_transferred);
			TORRENT_LOG(dht_tracker) << "invalid incoming packet: "
				<< e.what() << "\n" << msg << "\n";
#endif
		}
	}
	catch (std::exception& e)
	{
		TORRENT_ASSERT(false);
	};
示例#8
0
session_view::session_view()
	: m_position(0)
	, m_print_utp_stats(false)
{
	using libtorrent::find_metric_idx;

	m_width = 128;

	std::vector<lt::stats_metric> metrics = lt::session_stats_metrics();
	m_cnt[0].resize(metrics.size(), 0);
	m_cnt[1].resize(metrics.size(), 0);

	m_queued_bytes_idx = find_metric_idx("disk.queued_write_bytes");
	m_wasted_bytes_idx = find_metric_idx("net.recv_redundant_bytes");
	m_failed_bytes_idx = find_metric_idx("net.recv_failed_bytes");
	m_num_peers_idx = find_metric_idx("peer.num_peers_connected");
	m_recv_payload_idx = find_metric_idx("net.recv_payload_bytes");
	m_sent_payload_idx = find_metric_idx("net.sent_payload_bytes");
	m_unchoked_idx = find_metric_idx("peer.num_peers_up_unchoked");
	m_unchoke_slots_idx = find_metric_idx("ses.num_unchoke_slots");
	m_limiter_up_queue_idx = find_metric_idx("net.limiter_up_queue");
	m_limiter_down_queue_idx = find_metric_idx("net.limiter_down_queue");
	m_queued_writes_idx = find_metric_idx("disk.num_write_jobs");
	m_queued_reads_idx = find_metric_idx("disk.num_read_jobs");

	m_writes_cache_idx = find_metric_idx("disk.write_cache_blocks");
	m_reads_cache_idx = find_metric_idx("disk.read_cache_blocks");
	m_pinned_idx = find_metric_idx("disk.pinned_blocks");
	m_num_blocks_read_idx = find_metric_idx("disk.num_blocks_read");
	m_cache_hit_idx = find_metric_idx("disk.num_blocks_cache_hits");
	m_blocks_in_use_idx = find_metric_idx("disk.disk_blocks_in_use");
	m_blocks_written_idx = find_metric_idx("disk.num_blocks_written");
	m_write_ops_idx = find_metric_idx("disk.num_write_ops");

	m_mfu_size_idx = find_metric_idx("disk.arc_mfu_size");
	m_mfu_ghost_idx = find_metric_idx("disk.arc_mfu_ghost_size");
	m_mru_size_idx = find_metric_idx("disk.arc_mru_size");
	m_mru_ghost_idx = find_metric_idx("disk.arc_mru_ghost_size");

	m_utp_idle = find_metric_idx("utp.num_utp_idle");
	m_utp_syn_sent = find_metric_idx("utp.num_utp_syn_sent");
	m_utp_connected = find_metric_idx("utp.num_utp_connected");
	m_utp_fin_sent = find_metric_idx("utp.num_utp_fin_sent");
	m_utp_close_wait = find_metric_idx("utp.num_utp_close_wait");
}
int main(int argc, char const* argv[])
{
	using libtorrent::directory;
	using libtorrent::torrent_info;
	using libtorrent::error_code;
	using libtorrent::combine_path;
	using libtorrent::announce_entry;

	std::map<int, int> piece_sizes;
	std::map<boost::uint64_t, int> torrent_sizes;
	std::map<std::string, int> creators;
	std::map<std::string, int> trackers;

	if (argc < 2)
	{
		fprintf(stderr, "usage: torrent_analyzer <directory-with-torrent-files> ...\n");
		return 1;
	}

	int num_torrents = 0;

	int spin_cnt = 0;
	char const* spinner = "/-\\|";

	for (int a = 1; a < argc; ++a)
	{
		error_code ec;
		directory dir(argv[a], ec);
		for (; !ec && !dir.done(); dir.next(ec))
		{
			fprintf(stderr, "\r%c", spinner[spin_cnt++]);
			spin_cnt %= 4;
			std::string filename = dir.file();
			if (filename.size() < 9) continue;
			if (filename.substr(filename.size() - 8) != ".torrent") continue;

			filename = combine_path(argv[a], filename);
			torrent_info ti(filename, ec);
			if (ec)
			{
				fprintf(stderr, "failed %s: %s\n", filename.c_str(), ec.message().c_str());
				continue;
			}

			// piece size
			int piece_size = ti.piece_length();
			piece_sizes[piece_size] += 1;

			// creator
			std::string creator = ti.creator();
			if (creator.substr(0, 4) != "http") creator = creator.substr(0, creator.find_first_of('/'));
			creators[creator] += 1;

			// torrent size
			boost::uint64_t torrent_size = ti.total_size();
			torrent_sizes[torrent_size / (torrent_size_quantization*1024*1024)] += 1;

			// tracker
			std::vector<announce_entry> const& tr = ti.trackers();
			for (std::vector<announce_entry>::const_iterator i = tr.begin()
				, end(tr.end()); i != end; ++i)
			{
				std::string t = i->url;
				if (t.size() > 5 && t.substr(0, 6) == "dht://") t = "dht://xxxxx";
				trackers[t] += 1;
			}

			++num_torrents;
		}
	}

	printf("writing piece_size.dat\n");
	FILE* f = fopen("piece_size.dat", "w+");
	double sum = 0.0;
	for (std::map<int, int>::iterator i = piece_sizes.begin();
		i != piece_sizes.end(); ++i)
	{
		sum += i->second;
		fprintf(f, "%5d\t%-2.1f\n", i->first / 1024, sum * 100. / num_torrents);
	}
	fclose(f);

	printf("writing size.dat\n");
	f = fopen("size.dat", "w+");
	sum = 0.0;
	for (std::map<boost::uint64_t, int>::iterator i = torrent_sizes.begin();
		i != torrent_sizes.end(); ++i)
	{
		sum += i->second;
		fprintf(f, "%" PRId64 "\t%-4.4f\n"
			, i->first * torrent_size_quantization + (torrent_size_quantization / 2)
			, sum * 100. / num_torrents);
	}
	fclose(f);

	printf("writing creator.txt\n");
	f = fopen("creator.txt", "w+");
	std::vector<std::pair<int, std::string> > sorted_list;
	for (std::map<std::string, int>::iterator i = creators.begin();
		i != creators.end(); ++i)
	{
		sorted_list.push_back(std::make_pair(i->second, i->first));
	}

	std::sort(sorted_list.rbegin(), sorted_list.rend());

	for (std::vector<std::pair<int, std::string> >::iterator i = sorted_list.begin();
		i != sorted_list.end(); ++i)
	{
		fprintf(f, "%2.1f %%: %s\n", float(i->first) * 100.f / num_torrents, i->second.c_str());
	}
	fclose(f);


	printf("writing tracker.txt\n");
	f = fopen("tracker.txt", "w+");
	sorted_list.clear();

	for (std::map<std::string, int>::iterator i = trackers.begin();
		i != trackers.end(); ++i)
	{
		sorted_list.push_back(std::make_pair(i->second, i->first));
	}

	std::sort(sorted_list.rbegin(), sorted_list.rend());

	for (std::vector<std::pair<int, std::string> >::iterator i = sorted_list.begin();
		i != sorted_list.end(); ++i)
	{
		fprintf(f, "%-4.4f %%: %s\n", float(i->first) * 100.f / num_torrents, i->second.c_str());
	}
	fclose(f);
}