Example #1
0
void handle_alert(libtorrent::session& ses, libtorrent::alert* a)
{
	using namespace libtorrent;

	if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a))
	{
		p->handle.set_max_connections(30);

		// write resume data for the finished torrent
		torrent_handle h = p->handle;
		h.save_resume_data();
	}
	else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a))
	{
		torrent_handle h = p->handle;
		TORRENT_ASSERT(p->resume_data);
		if (p->resume_data)
		{
			boost::filesystem::ofstream out(h.save_path() / (h.name() + ".fastresume")
				, std::ios_base::binary);
			out.unsetf(std::ios_base::skipws);
			bencode(std::ostream_iterator<char>(out), *p->resume_data);
			if (h.is_paused() && !h.is_auto_managed()) ses.remove_torrent(h);
		}
	}
}
Example #2
0
void load_dht_state(lt::session& s)
{
	FILE* f = fopen(".dht", "rb");
	if (f == NULL) return;

	fseek(f, 0, SEEK_END);
	int size = ftell(f);
	fseek(f, 0, SEEK_SET);

	if (size > 0)
	{
		std::vector<char> state;
		state.resize(size);
		fread(&state[0], 1, state.size(), f);

		bdecode_node e;
		error_code ec;
		bdecode(&state[0], &state[0] + state.size(), e, ec);
		if (ec)
			fprintf(stderr, "failed to parse .dht file: (%d) %s\n"
				, ec.value(), ec.message().c_str());
		else
		{
			printf("load dht state from .dht\n");
			s.load_state(e);
		}
	}
	fclose(f);
}
	bool on_alert(libtorrent::alert const* alert
		, int session_idx
		, std::vector<libtorrent::torrent_handle> const& handles
		, libtorrent::session& ses) override
	{
		if (alert_cast<metadata_received_alert>(alert))
		{
			m_metadata_alerts += 1;
		}

		// make sure this function can be called on
		// torrents without metadata
		if ((m_flags & disconnect) == 0)
		{
			handles[session_idx].status();
		}

		if ((m_flags & disconnect)
			&& session_idx == 1
			&& alert_cast<metadata_received_alert>(alert))
		{
			ses.remove_torrent(handles[session_idx]);
			return true;
		}
		return false;
	}
Example #4
0
void filter_ips(lt::session& ses)
{
	using namespace libtorrent;
	ip_filter filter;
	filter.add_rule(asio::ip::address_v4::from_string("50.0.0.1")
		, asio::ip::address_v4::from_string("50.0.0.2"), ip_filter::blocked);
	ses.set_ip_filter(filter);
}
Example #5
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_ResumeSession
	(JNIEnv *, jobject)
{
	jboolean result = JNI_FALSE;
	try {
		if(gSessionState){
			gSession.resume();
			bool paused = gSession.is_paused();
			if(!paused) result = JNI_TRUE;
		}
	} catch(...){
		LOG_ERR("Exception: failed to resume session");
		gSessionState=false;
	}
	if(!gSessionState) LOG_ERR("LibTorrent.ResumeSession SessionState==false");
	gSessionState==true ? result=JNI_TRUE : result=JNI_FALSE;
	return result;
}
Example #6
0
void enable_enc(lt::session& ses)
{
	using namespace libtorrent;
	settings_pack p;
	p.set_bool(settings_pack::prefer_rc4, true);
	p.set_int(settings_pack::in_enc_policy, settings_pack::pe_forced);
	p.set_int(settings_pack::out_enc_policy, settings_pack::pe_forced);
	p.set_int(settings_pack::allowed_enc_level, settings_pack::pe_both);
	ses.apply_settings(p);
}
Example #7
0
void enable_utp(lt::session& ses)
{
	using namespace libtorrent;
	settings_pack p;
	p.set_bool(settings_pack::enable_outgoing_tcp, false);
	p.set_bool(settings_pack::enable_incoming_tcp, false);
	p.set_bool(settings_pack::enable_outgoing_utp, true);
	p.set_bool(settings_pack::enable_incoming_utp, true);
	ses.apply_settings(p);
}
Example #8
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_SetSession
	(JNIEnv *env, jobject obj, jint ListenPort, jint UploadLimit, jint DownloadLimit)
{
	jboolean result = JNI_FALSE;
	try{
		gSession.set_alert_mask(libtorrent::alert::all_categories
			& ~(libtorrent::alert::dht_notification
			+ libtorrent::alert::progress_notification
			+ libtorrent::alert::debug_notification
			+ libtorrent::alert::stats_notification));

		int listenPort = 54321;
		if(ListenPort > 0)
			listenPort = ListenPort;
			gSession.listen_on(std::make_pair(listenPort, listenPort+10));

		int uploadLimit = UploadLimit;
		if(uploadLimit > 0){
			gSession.set_upload_rate_limit(uploadLimit * 1000);
		}
		else{
			gSession.set_upload_rate_limit(0);
		}
		int downloadLimit = DownloadLimit;
		if(downloadLimit > 0){
			gSession.set_download_rate_limit(downloadLimit * 1000);
		}
		else{
			gSession.set_download_rate_limit(0);
		}

		libtorrent::session_settings sets = gSession.settings();

		sets.announce_to_all_trackers = true;
		sets.announce_to_all_tiers = true;
		sets.prefer_udp_trackers = false;
		sets.max_peerlist_size = 0;

		gSession.set_settings(sets);

		LOG_INFO("ListenPort: %d\n", listenPort);
		LOG_INFO("DownloadLimit: %d\n", downloadLimit);
		LOG_INFO("UploadLimit: %d\n", uploadLimit);

		gSessionState=true;
	}catch(...){
		LOG_ERR("Exception: failed to set session");
		gSessionState=false;
	}
	if(!gSessionState) LOG_ERR("LibTorrent.SetSession SessionState==false");
	gSessionState==true ? result=JNI_TRUE : result=JNI_FALSE;
	return result;
}
Example #9
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_SetSessionOptions
	(JNIEnv *env, jobject obj, jboolean LSD, jboolean UPNP, jboolean NATPMP)
{
	jboolean result = JNI_FALSE;
	try{
		if(gSessionState){
			if(LSD == JNI_TRUE)
				gSession.start_lsd();
			else
				gSession.stop_lsd();
			if(UPNP == JNI_TRUE)
				gSession.start_upnp();
			else
				gSession.stop_upnp();
			if(NATPMP == JNI_TRUE)
				gSession.start_natpmp();
			else
				gSession.stop_natpmp();

			LOG_INFO("LSD: %d\n", LSD);
			LOG_INFO("UPNP: %d\n", UPNP);
			LOG_INFO("NATPMP: %d\n", NATPMP);
		}
	}catch(...){
		LOG_ERR("Exception: failed to set session options");
		gSessionState=false;
	}
	if(!gSessionState) LOG_ERR("LibTorrent.SetSessionOptions SessionState==false");
	gSessionState==true ? result=JNI_TRUE : result=JNI_FALSE;
	return result;
}
Example #10
0
void bootstrap_session(std::vector<dht_network*> networks, lt::session& ses)
{
	lt::dht_settings sett;
	sett.ignore_dark_internet = false;
	ses.set_dht_settings(sett);

	lt::entry state;

	for (auto dht : networks)
	{
		// bootstrap off of 8 of the nodes
		auto router_nodes = dht->router_nodes();

		char const* nodes_key;
		if (router_nodes.front().address().is_v6())
			nodes_key = "nodes6";
		else
			nodes_key = "nodes";

		lt::entry::list_type& nodes = state["dht state"][nodes_key].list();
		for (auto const& n : router_nodes)
		{
			std::string node;
			std::back_insert_iterator<std::string> out(node);
			lt::detail::write_endpoint(n, out);
			nodes.push_back(lt::entry(node));
		}
	}

	std::vector<char> buf;
	lt::bencode(std::back_inserter(buf), state);
	lt::bdecode_node e;
	lt::error_code ec;
	lt::bdecode(&buf[0], &buf[0] + buf.size(), e, ec);

	ses.load_state(e);
	lt::settings_pack pack;
	pack.set_bool(lt::settings_pack::enable_dht, true);
	ses.apply_settings(pack);
}
Example #11
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_AbortSession
	(JNIEnv *, jobject)
{
	jboolean result = JNI_FALSE;
	try {
		if(gSessionState)
			gSession.abort();
	} catch(...){
		LOG_ERR("Exception: failed to abort session");
		gSessionState=false;
	}
	if(!gSessionState) LOG_ERR("LibTorrent.AbortSession SessionState==false");
	gSessionState==true ? result=JNI_TRUE : result=JNI_FALSE;
	return result;
}
Example #12
0
int save_dht_state(lt::session& s)
{
	entry e;
	s.save_state(e, session::save_dht_state);
	std::vector<char> state;
	bencode(std::back_inserter(state), e);
	FILE* f = fopen(".dht", "wb+");
	if (f == NULL)
	{
		fprintf(stderr, "failed to open file .dht for writing");
		return 1;
	}
	fwrite(&state[0], 1, state.size(), f);
	fclose(f);

	return 0;
}
Example #13
0
// monitored_dir is true if this torrent is added because
// it was found in the directory that is monitored. If it
// is, it should be remembered so that it can be removed
// if it's no longer in that directory.
void add_torrent(libtorrent::session& ses
	, handles_t& handles
	, std::string const& torrent
	, float preferred_ratio
	, bool compact_mode
	, path const& save_path
	, bool monitored_dir
	, int torrent_upload_limit
	, int torrent_download_limit)
{
	using namespace libtorrent;

	boost::intrusive_ptr<torrent_info> t(new torrent_info(torrent.c_str()));

	std::cout << t->name() << "\n";

	add_torrent_params p;
	lazy_entry resume_data;

	std::string filename = (save_path / (t->name() + ".fastresume")).string();

	std::vector<char> buf;
	if (load_file(filename.c_str(), buf) == 0)
		p.resume_data = &buf;

	p.ti = t;
	p.save_path = save_path;
	p.storage_mode = compact_mode ? storage_mode_compact : storage_mode_sparse;
	p.paused = true;
	p.duplicate_is_error = false;
	p.auto_managed = true;
	torrent_handle h = ses.add_torrent(p);

	handles.insert(std::make_pair(
		monitored_dir?std::string(torrent):std::string(), h));

	h.set_max_connections(50);
	h.set_max_uploads(-1);
	h.set_ratio(preferred_ratio);
	h.set_upload_limit(torrent_upload_limit);
	h.set_download_limit(torrent_download_limit);
#ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
	h.resolve_countries(true);
#endif
}
Example #14
0
void set_proxy(lt::session& ses, int proxy_type, int flags = 0, bool proxy_peer_connections = true)
{
	// apply the proxy settings to session 0
	using namespace libtorrent;
	settings_pack p;
	p.set_int(settings_pack::proxy_type, proxy_type);
	if (proxy_type == settings_pack::socks4)
		p.set_int(settings_pack::proxy_port, 4444);
	else
		p.set_int(settings_pack::proxy_port, 5555);
	if (flags & ipv6)
		p.set_str(settings_pack::proxy_hostname, "2001::2");
	else
		p.set_str(settings_pack::proxy_hostname, "50.50.50.50");
	p.set_bool(settings_pack::proxy_hostnames, true);
	p.set_bool(settings_pack::proxy_peer_connections, proxy_peer_connections);
	p.set_bool(settings_pack::proxy_tracker_connections, true);

	ses.apply_settings(p);
}
Example #15
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_SetProxy
	(JNIEnv *env, jobject obj, jint Type, jstring HostName, jint Port, jstring UserName, jstring Password)
{
	jboolean result = JNI_FALSE;
	try{
		if(gSessionState){
			int type = Type;
//			if(type > 0){
				std::string hostName;
				JniToStdString(env, &hostName, HostName);
				int port = Port;
				std::string userName;
				JniToStdString(env, &userName, UserName);
				std::string password;
				JniToStdString(env, &password, Password);

				gProxy.type = libtorrent::proxy_settings::proxy_type(type);
				gProxy.hostname = hostName;
				gProxy.port = port;
				gProxy.username = userName;
				gProxy.password = password;

				gSession.set_proxy(gProxy);

				LOG_INFO("ProxyType: %d\n", type);
//				LOG_INFO("HostName: %s\n", hostName.c_str());
//				LOG_INFO("ProxyPort: %d\n", port);
//				LOG_INFO("UserName: %s\n", userName.c_str());
//				LOG_INFO("Password: %s\n", password.c_str());
//			}
		}
	}catch(...){
		LOG_ERR("Exception: failed to set proxy");
		gSessionState=false;
	}
	if(!gSessionState) LOG_ERR("LibTorrent.SetProxy SessionState==false");
	gSessionState==true ? result=JNI_TRUE : result=JNI_FALSE;
	return result;
}
Example #16
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_RemoveTorrent
	(JNIEnv *env, jobject obj, jstring ContentFile)
{
	jboolean result = JNI_FALSE;
	try {
		if(gSessionState){
			libtorrent::torrent_handle* pTorrent = GetTorrentHandle(env,ContentFile);
			if(pTorrent){
				LOG_INFO("Remove torrent name %s", pTorrent->name().c_str());
				pTorrent->auto_managed(false);
				pTorrent->pause();
				// the alert handler for save_resume_data_alert
				// will save it to disk
				/*pTorrent->save_resume_data();
				// loop through the alert queue to see if anything has happened.
				std::auto_ptr<libtorrent::alert> a;
				a = gSession.pop_alert();
				while (a.get()){
					//LOG_INFO("RemoveTorrent Alert: %s", a->message().c_str());
					HandleAlert(a.get());
					a = gSession.pop_alert();
				}*/
				gSession.remove_torrent(*pTorrent);
				LOG_INFO("remove_torrent");
				gTorrents.erase(TorrentFileInfo(env,ContentFile));
				result = JNI_TRUE;
			}
		}
	} catch(...){
		LOG_ERR("Exception: failed to remove torrent");
		try	{
			gTorrents.erase(TorrentFileInfo(env,ContentFile));
		}catch(...){}
	}
	return result;
}
Example #17
0
//-----------------------------------------------------------------------------
JNIEXPORT jstring JNICALL Java_com_softwarrior_libtorrent_LibTorrent_GetSessionStatusText
	(JNIEnv *env, jobject obj)
{
	jstring result = NULL;
	try {
		if(gSessionState){
			std::string out;
			char str[500]; memset(str,0,500);
			libtorrent::session_status s_s = gSession.status();
			snprintf(str, sizeof(str),
					  "%25s%20d\n"
					  "%22s%20s/%s\n"
					  "%25s%20s/%s\n"
					  "%18s%20s/%s\n"
					  "%15s%20s/%s\n"
					  "%19s%20s/%s\n"
				,"conns:"
				, s_s.num_peers
				, "down/rate:"
				, add_suffix(s_s.total_download).c_str(), add_suffix(s_s.download_rate, "/s").c_str()
				, "up/rate:"
				, add_suffix(s_s.total_upload).c_str(), add_suffix(s_s.upload_rate, "/s").c_str()
				, "ip rate down/up:"
				, add_suffix(s_s.ip_overhead_download_rate, "/s").c_str(), add_suffix(s_s.ip_overhead_upload_rate, "/s").c_str()
				, "dht rate down/up:"
				, add_suffix(s_s.dht_download_rate, "/s").c_str(), add_suffix(s_s.dht_upload_rate, "/s").c_str()
				, "tr rate down/up:"
				, add_suffix(s_s.tracker_download_rate, "/s").c_str(), add_suffix(s_s.tracker_upload_rate, "/s").c_str());
			out += str;
			result = env->NewStringUTF(out.c_str());
		}
	} catch(...){
		LOG_ERR("Exception: failed to get session status");
	}
	return result;
}
Example #18
0
torrent_handle test_resume_flags(libtorrent::session& ses, int flags
	, char const* file_priorities = "1111", char const* resume_file_prio = "")
{
	boost::shared_ptr<torrent_info> ti = generate_torrent();

	add_torrent_params p;

	p.ti = ti;
	p.flags = flags;
#ifdef TORRENT_WINDOWS
	p.save_path = "c:\\add_torrent_params save_path";
#else
	p.save_path = "/add_torrent_params save_path";
#endif
	p.trackers.push_back("http://add_torrent_params_tracker.com/announce");
	p.url_seeds.push_back("http://add_torrent_params_url_seed.com");

	std::vector<char> rd = generate_resume_data(ti.get(), resume_file_prio);
	p.resume_data.swap(rd);

	p.max_uploads = 1;
	p.max_connections = 2;
	p.upload_limit = 3;
	p.download_limit = 4;

	std::vector<boost::uint8_t> priorities_vector;
	for (int i = 0; file_priorities[i]; ++i)
		priorities_vector.push_back(file_priorities[i] - '0');

	p.file_priorities = priorities_vector;

	torrent_handle h = ses.add_torrent(p);
	torrent_status s = h.status();
	TEST_EQUAL(s.info_hash, ti->info_hash());
	return h;
}
Example #19
0
int main(int argc, char* argv[])
{       
        if (argc == 1) {
            printhelp(argv[0]);
        }
        int tmp;
        char torrentfile[255];
        int usrpid = 0;
        int mypid;
        char pidfilename[255];
        char ip[16];
        int delay = 10;
        strcpy(ip, "0.0.0.0");
        strcpy(pidfilename, argv[0]);
        strcat(pidfilename, ".pid");

        while((tmp=getopt(argc,argv,"ht:p:f:b:d:"))!=-1) {
             switch(tmp) {
                case 'h':
                    printhelp(argv[0]);
                    break;
                case 't':
                    strcpy(torrentfile, optarg);
                    break;
                case 'p':
                    usrpid = atoi(optarg);
                    break;
                case 'f':
                    strcpy(pidfilename, optarg);
                    break;
                case 'b':
                    strcpy(ip, optarg);
                    break;
                case 'd':
                    delay = atoi(optarg);
                    break;
                default:
                    printhelp(argv[0]);
                    break;
             }
        }
        if (usrpid == 0) {
            printhelp(argv[0]);
        }
        mypid = ::getpid();
        std::signal(SIGINT, exit_signalHandler);
        std::signal(SIGTERM, exit_signalHandler);

        using namespace libtorrent;

        // set peer_id to nodename
        char hostname[HOST_NAME_MAX];
        gethostname(hostname, HOST_NAME_MAX);
        char buff[21];
        snprintf(buff, sizeof(buff), "%20s", hostname);
        peer_id my_peer_id = sha1_hash(buff);
        s.set_peer_id(my_peer_id);
        error_code ec;

        // set up torrent session
        s.listen_on(std::make_pair(6881, 6889), ec, ip);
        if (ec)
        {
                fprintf(stderr, "failed to open listen socket: %s\n", ec.message().c_str());
                return 1;
        }

        // create torrent object
        add_torrent_params p;
        p.save_path = "./";
        p.ti = new torrent_info(torrentfile, ec);
        if (ec)
        {
                fprintf(stderr, "%s\n", ec.message().c_str());
                return 1;
        }

        // start downloading torrent
        torrent_handle torrent = s.add_torrent(p, ec);
        if (ec)
        {
                fprintf(stderr, "%s\n", ec.message().c_str());
                return 1;
        }

        // create pidfile
        std::ofstream pidfile;
        pidfile.open(pidfilename);
        pidfile << mypid;
        pidfile << "\n";
        pidfile.close();

        std::vector<torrent_status> vts;
        s.get_torrent_status(&vts, &yes, 0);
        torrent_status& st = vts[0];
        boost::int64_t remains = st.total_wanted - st.total_wanted_done;
        fprintf(stdout, "Remains: %i\n", remains);
        //fprintf(stdout, "Torrent: %i\n", torrent);

        while( remains > 0 && run ) {
            usleep(delay*1000000);
            std::vector<torrent_status> vts;
            s.get_torrent_status(&vts, &yes, 0);
            torrent_status& st = vts[0];
            remains = st.total_wanted - st.total_wanted_done;
            fprintf(stdout, "Remains: %i\n", remains);
            torrent.force_reannounce();

        }
        // send SIGUSR1 to process
        fprintf(stdout, "Done with torrent. Sending SIGUSR1 to PID %i\n", usrpid); 
        kill(usrpid, SIGUSR1);
        while (run) {
            usleep(delay*1000000);
            torrent.force_reannounce();
        }
        s.abort();
        remove(pidfilename);
        fprintf(stdout, "Exit.\n"); 
        return 0;
        
}
Example #20
0
bool is_seed(lt::session& ses)
{
	lt::torrent_handle h = ses.get_torrents()[0];
	return h.status().is_seeding;
}
Example #21
0
//-----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_com_softwarrior_libtorrent_LibTorrent_AddTorrent
	(JNIEnv *env, jobject obj, jstring SavePath, jstring TorrentFile, jint StorageMode)
{
	jboolean result = JNI_FALSE;
	try{
		if(gSessionState){
			TorrentFileInfo torrentFileInfo(env, SavePath, TorrentFile);
			std::map<TorrentFileInfo, libtorrent::torrent_handle>::iterator iter = gTorrents.find(torrentFileInfo);
			if(iter != gTorrents.end()) {
				LOG_INFO("Torrent file already presents: %s", torrentFileInfo.TorrentFileName.c_str());
			}
			else{
				LOG_INFO("SavePath: %s", torrentFileInfo.SavePath.c_str());
				LOG_INFO("TorrentFile: %s", torrentFileInfo.TorrentFileName.c_str());

				boost::intrusive_ptr<libtorrent::torrent_info> t;
				libtorrent::error_code ec;
				t = new libtorrent::torrent_info(torrentFileInfo.TorrentFileName.c_str(), ec);
				if (ec){
					std::string errorMessage = ec.message();
					LOG_ERR("%s: %s\n", torrentFileInfo.TorrentFileName.c_str(), errorMessage.c_str());
				}
				else{
					LOG_INFO("%s\n", t->name().c_str());
					LOG_INFO("StorageMode: %d\n", StorageMode);

					libtorrent::add_torrent_params torrentParams;
					libtorrent::lazy_entry resume_data;

					boost::filesystem::path save_path = torrentFileInfo.SavePath;
					std::string filename = torrentFileInfo.SavePath + "/" +  t->name() +  ".resume";
					std::vector<char> buf;
					boost::system::error_code errorCode;
					if (libtorrent::load_file(filename.c_str(), buf, errorCode) == 0)
						torrentParams.resume_data = &buf;

					torrentParams.ti = t;
					torrentParams.save_path = torrentFileInfo.SavePath;
					torrentParams.duplicate_is_error = false;
					torrentParams.auto_managed = true;
					libtorrent::storage_mode_t storageMode = libtorrent::storage_mode_sparse;
					switch(StorageMode){
					case 0: storageMode = libtorrent::storage_mode_allocate; break;
					case 1: storageMode = libtorrent::storage_mode_sparse; break;
					case 2: storageMode = libtorrent::storage_mode_compact; break;
					}
					torrentParams.storage_mode = storageMode;
					libtorrent::torrent_handle th = gSession.add_torrent(torrentParams,ec);
					th.move_storage(save_path.string());
					if(ec) {
						std::string errorMessage = ec.message();
						LOG_ERR("failed to add torrent: %s\n", errorMessage.c_str());
					}
					else{
						if(th.is_paused()){
							th.resume();
						}
						if(!th.is_auto_managed()){
							th.auto_managed(true);
						}
						gTorrents[torrentFileInfo] = th;
						result=JNI_TRUE;
					}
				}
			}
		}
	}catch(...){
		LOG_ERR("Exception: failed to add torrent");
		try	{
			TorrentFileInfo torrentFileInfo(env, SavePath, TorrentFile);
			gTorrents.erase(torrentFileInfo);
		}catch(...){}
	}
	return result;
}
Example #22
0
// proxy: 0=none, 1=socks4, 2=socks5, 3=socks5_pw 4=http 5=http_pw
void test_transfer(lt::session& ses, boost::shared_ptr<torrent_info> torrent_file
	, int proxy, int port, char const* protocol, bool url_seed
	, bool chunked_encoding, bool test_ban, bool keepalive, bool proxy_peers)
{
	using namespace libtorrent;

	TORRENT_ASSERT(torrent_file->web_seeds().size() > 0);

	std::string save_path = "tmp2_web_seed";
	save_path += proxy_name[proxy];

	error_code ec;
	remove_all(save_path, ec);

	static char const* test_name[] = {"no", "SOCKS4", "SOCKS5", "SOCKS5 password", "HTTP", "HTTP password"};

	fprintf(stderr, "\n\n  ==== TESTING === proxy: %s ==== protocol: %s "
		"==== seed: %s === transfer-encoding: %s === corruption: %s "
		"==== keepalive: %s\n\n\n"
		, test_name[proxy], protocol, url_seed ? "URL seed" : "HTTP seed"
		, chunked_encoding ? "chunked": "none", test_ban ? "yes" : "no"
		, keepalive ? "yes" : "no");

	int proxy_port = 0;
	if (proxy)
	{
		proxy_port = start_proxy(proxy);
		if (proxy_port < 0)
		{
			fprintf(stderr, "failed to start proxy");
			return;
		}
		settings_pack pack;
		pack.set_str(settings_pack::proxy_hostname, "127.0.0.1");
		pack.set_str(settings_pack::proxy_username, "testuser");
		pack.set_str(settings_pack::proxy_password, "testpass");
		pack.set_int(settings_pack::proxy_type, (settings_pack::proxy_type_t)proxy);
		pack.set_int(settings_pack::proxy_port, proxy_port);
		pack.set_bool(settings_pack::proxy_peer_connections, proxy_peers);
		ses.apply_settings(pack);
	}
	else
	{
		settings_pack pack;
		pack.set_str(settings_pack::proxy_hostname, "");
		pack.set_str(settings_pack::proxy_username, "");
		pack.set_str(settings_pack::proxy_password, "");
		pack.set_int(settings_pack::proxy_type, settings_pack::none);
		pack.set_int(settings_pack::proxy_port, 0);
		pack.set_bool(settings_pack::proxy_peer_connections, proxy_peers);
		ses.apply_settings(pack);
	}

	add_torrent_params p;
	p.flags &= ~add_torrent_params::flag_paused;
	p.flags &= ~add_torrent_params::flag_auto_managed;
	p.ti = torrent_file;
	p.save_path = save_path;
#ifndef TORRENT_NO_DEPRECATE
	p.storage_mode = storage_mode_compact;
#endif
	torrent_handle th = ses.add_torrent(p, ec);
	printf("adding torrent, save_path = \"%s\" cwd = \"%s\" torrent = \"%s\"\n"
		, save_path.c_str(), current_working_directory().c_str()
		, torrent_file->name().c_str());

	std::vector<announce_entry> empty;
	th.replace_trackers(empty);

	const boost::int64_t total_size = torrent_file->total_size();

	file_storage const& fs = torrent_file->files();
	int pad_file_size = 0;
	for (int i = 0; i < fs.num_files(); ++i)
	{
		if (fs.file_flags(i) & file_storage::flag_pad_file)
			pad_file_size += fs.file_size(i);
	}

	peer_disconnects = 0;
	std::map<std::string, boost::int64_t> cnt = get_counters(ses);

	for (int i = 0; i < 40; ++i)
	{
		torrent_status s = th.status();

		cnt = get_counters(ses);

		print_ses_rate(i / 10.f, &s, NULL);
		print_alerts(ses, "  >>  ses", test_ban, false, false, &on_alert);

		if (test_ban && th.url_seeds().empty() && th.http_seeds().empty())
		{
			fprintf(stderr, "testing ban: URL seed removed\n");
			// when we don't have any web seeds left, we know we successfully banned it
			break;
		}

		if (s.is_seeding)
		{
			fprintf(stderr, "SEEDING\n");
			fprintf(stderr, "session.payload: %d session.redundant: %d\n"
				, int(cnt["net.recv_payload_bytes"]), int(cnt["net.recv_redundant_bytes"]));
			fprintf(stderr, "torrent.payload: %d torrent.redundant: %d\n"
				, int(s.total_payload_download), int(s.total_redundant_bytes));

			TEST_EQUAL(s.total_payload_download - s.total_redundant_bytes, total_size - pad_file_size);
			// we need to sleep here a bit to let the session sync with the torrent stats
			// commented out because it takes such a long time
//			TEST_EQUAL(ses.status().total_payload_download - ses.status().total_redundant_bytes
//				, total_size - pad_file_size);
			break;
		}

		// if the web seed connection is disconnected, we're going to fail
		// the test. make sure to do so quickly
		if (keepalive && peer_disconnects >= 1) break;

		test_sleep(100);
	}

	// for test_ban tests, make sure we removed
	// the url seed (i.e. banned it)
	TEST_CHECK(!test_ban || (th.url_seeds().empty() && th.http_seeds().empty()));

	cnt = get_counters(ses);

	// if the web seed senr corrupt data and we banned it, we probably didn't
	// end up using all the cache anyway
	if (!test_ban)
	{
		torrent_status st = th.status();
		TEST_EQUAL(st.is_seeding, true);

		if (st.is_seeding)
		{
			for (int i = 0; i < 50; ++i)
			{
				cnt = get_counters(ses);
				if (cnt["disk.read_cache_blocks"]
						== (torrent_file->total_size() + 0x3fff) / 0x4000
					&& cnt["disk.disk_blocks_in_use"]
						== (torrent_file->total_size() + 0x3fff) / 0x4000)
					break;
				fprintf(stderr, "cache_size: %d/%d\n", int(cnt["disk.read_cache_blocks"])
					, int(cnt["disk.disk_blocks_in_use"]));
				test_sleep(100);
			}
			TEST_EQUAL(cnt["disk.disk_blocks_in_use"]
				, (torrent_file->total_size() + 0x3fff) / 0x4000);
		}
	}

	std::cerr << "total_size: " << total_size
		<< " read cache size: " << cnt["disk.disk_blocks_in_use"]
		<< " total used buffer: " << cnt["disk.disk_blocks_in_use"]
		<< " session total download: " << cnt["net.recv_payload_bytes"]
		<< " torrent total download: " << th.status().total_payload_download
		<< " redundant: " << th.status().total_redundant_bytes
		<< std::endl;

	// if test_ban is true, we're not supposed to have completed the download
	// otherwise, we are supposed to have
	TEST_CHECK(th.status().is_seeding == !test_ban);

	if (proxy) stop_proxy(proxy_port);

	th.flush_cache();

	// synchronize to make sure the files have been created on disk
	wait_for_alert(ses, cache_flushed_alert::alert_type, "ses");

	print_alerts(ses, "  >>  ses", true, true, false, &on_alert, true);

	if (!test_ban)
	{
		std::string first_file_path = combine_path(save_path, torrent_file->files().file_path(0));
		fprintf(stderr, "checking file: %s\n", first_file_path.c_str());
		TEST_CHECK(exists(first_file_path));
	}

	ses.remove_torrent(th);

	remove_all(save_path, ec);
}