diff --git a/ChangeLog b/ChangeLog index 6a7f32de4..cd88f8922 100644 --- a/ChangeLog +++ b/ChangeLog @@ -27,6 +27,7 @@ * almost completely changed the storage interface (for custom storage) * added support for hashing pieces in multiple threads + * added missing force_proxy to python binding * anonymous_mode defaults to false * make DHT DOS detection more forgiving to bursts * support IPv6 multicast in local service discovery diff --git a/bindings/python/src/session_settings.cpp b/bindings/python/src/session_settings.cpp index 1329f1dbf..33f393ce1 100644 --- a/bindings/python/src/session_settings.cpp +++ b/bindings/python/src/session_settings.cpp @@ -135,6 +135,7 @@ void bind_session_settings() .def_readwrite("ignore_resume_timestamps", &session_settings::ignore_resume_timestamps) .def_readwrite("no_recheck_incomplete_resume", &session_settings::no_recheck_incomplete_resume) .def_readwrite("anonymous_mode", &session_settings::anonymous_mode) + .def_readwrite("force_proxy", &session_settings::force_proxy) .def_readwrite("tick_interval", &session_settings::tick_interval) .def_readwrite("report_web_seed_downloads", &session_settings::report_web_seed_downloads) .def_readwrite("share_mode_target", &session_settings::share_mode_target) diff --git a/docs/examples.html b/docs/examples.html index 421d6a2d1..a45d361ea 100644 --- a/docs/examples.html +++ b/docs/examples.html @@ -41,15 +41,17 @@
Table of contents
@@ -60,244 +62,613 @@ of a (little bit) more complete client, client_test. There are separate instructions for how to use it here if you'd like to try it. Note that building client_test also requires boost.regex and boost.program_options library. -This is an example of a program that will take a torrent-file as a parameter and -print information about it to std out:
--#include <iostream> -#include <fstream> -#include <iterator> -#include <iomanip> - -#include "libtorrent/entry.hpp" -#include "libtorrent/bencode.hpp" -#include "libtorrent/torrent_info.hpp" -#include "libtorrent/lazy_entry.hpp" -#include <boost/filesystem/operations.hpp> - - -int main(int argc, char* argv[]) -{ - using namespace libtorrent; - using namespace boost::filesystem; - - if (argc != 2) - { - std::cerr << "usage: dump_torrent torrent-file\n"; - return 1; - } -#if BOOST_VERSION < 103400 - boost::filesystem::path::default_name_check(boost::filesystem::no_check); -#endif - -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - - int size = file_size(argv[1]); - if (size > 10 * 1000000) - { - std::cerr << "file too big (" << size << "), aborting\n"; - return 1; - } - std::vector<char> buf(size); - std::ifstream(argv[1], std::ios_base::binary).read(&buf[0], size); - lazy_entry e; - int ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), e); - - if (ret != 0) - { - std::cerr << "invalid bencoding: " << ret << std::endl; - return 1; - } - - std::cout << "\n\n----- raw info -----\n\n"; - std::cout << e << std::endl; - - torrent_info t(e); - - // print info about torrent - std::cout << "\n\n----- torrent file info -----\n\n"; - std::cout << "nodes:\n"; - typedef std::vector<std::pair<std::string, int> > node_vec; - node_vec const& nodes = t.nodes(); - for (node_vec::const_iterator i = nodes.begin(), end(nodes.end()); - i != end; ++i) - { - std::cout << i->first << ":" << i->second << "\n"; - } - std::cout << "trackers:\n"; - for (std::vector<announce_entry>::const_iterator i = t.trackers().begin(); - i != t.trackers().end(); ++i) - { - std::cout << i->tier << ": " << i->url << "\n"; - } - - std::cout << "number of pieces: " << t.num_pieces() << "\n"; - std::cout << "piece length: " << t.piece_length() << "\n"; - std::cout << "info hash: " << t.info_hash() << "\n"; - std::cout << "comment: " << t.comment() << "\n"; - std::cout << "created by: " << t.creator() << "\n"; - std::cout << "files:\n"; - int index = 0; - for (torrent_info::file_iterator i = t.begin_files(); - i != t.end_files(); ++i, ++index) - { - int first = t.map_file(index, 0, 1).piece; - int last = t.map_file(index, i->size - 1, 1).piece; - std::cout << " " << std::setw(11) << i->size - << " " << i->path.string() << "[ " << first << ", " - << last << " ]\n"; - } - -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - std::cout << e.what() << "\n"; - } -#endif - - return 0; -} --
This is a simple client. It doesn't have much output to keep it simple:
--int main(int argc, char* argv[]) -{ - using namespace libtorrent; ++#include <stdlib.h> +#include <boost/make_shared.hpp> +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/session.hpp" + +int main(int argc, char* argv[]) +{ + using namespace libtorrent; + namespace lt = libtorrent; - if (argc != 2) - { - fputs("usage: ./simple_client torrent-file\n" - "to stop the client, press return.\n", stderr); - return 1; - } + if (argc != 2) + { + fputs("usage: ./simple_client torrent-file\n" + "to stop the client, press return.\n", stderr); + return 1; + } - session s; - error_code ec; - s.listen_on(std::make_pair(6881, 6889), ec); - if (ec) - { - fprintf(stderr, "failed to open listen socket: %s\n", ec.message().c_str()); - return 1; - } - add_torrent_params p; - p.save_path = "./"; - p.ti = boost::make_shared<torrent_info>(argv[1], ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } - s.add_torrent(p, ec); - if (ec) - { - fprintf(stderr, "%s\n", ec.message().c_str()); - return 1; - } + settings_pack sett; + sett.set_str(settings_pack::listen_interfaces, "0.0.0.0:6881"); + lt::session s(sett); + error_code ec; + if (ec) + { + fprintf(stderr, "failed to open listen socket: %s\n", ec.message().c_str()); + return 1; + } + add_torrent_params p; + p.save_path = "./"; + p.ti = boost::make_shared<torrent_info>(std::string(argv[1]), boost::ref(ec), 0); + if (ec) + { + fprintf(stderr, "%s\n", ec.message().c_str()); + return 1; + } + s.add_torrent(p, ec); + if (ec) + { + fprintf(stderr, "%s\n", ec.message().c_str()); + return 1; + } - // wait for the user to end - char a; - scanf("%c\n", &a); - return 0; -} + // wait for the user to end + char a; + scanf("%c\n", &a); + return 0; +}
Shows how to create a torrent from a directory tree:
--#include <iostream> -#include <fstream> -#include <iterator> -#include <iomanip> - -#include "libtorrent/entry.hpp" ++#include "libtorrent/entry.hpp" #include "libtorrent/bencode.hpp" #include "libtorrent/torrent_info.hpp" #include "libtorrent/file.hpp" #include "libtorrent/storage.hpp" #include "libtorrent/hasher.hpp" #include "libtorrent/create_torrent.hpp" +#include "libtorrent/file.hpp" +#include "libtorrent/file_pool.hpp" + +#include <boost/bind.hpp> + +using namespace libtorrent; -#include <boost/filesystem/operations.hpp> -#include <boost/filesystem/path.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/bind.hpp> +int load_file(std::string const& filename, std::vector<char>& v, libtorrent::error_code& ec, int limit = 8000000) +{ + ec.clear(); + FILE* f = fopen(filename.c_str(), "rb"); + if (f == NULL) + { + ec.assign(errno, boost::system::generic_category()); + return -1; + } -using namespace boost::filesystem; -using namespace libtorrent; + int r = fseek(f, 0, SEEK_END); + if (r != 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + long s = ftell(f); + if (s < 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } -// do not include files and folders whose + if (s > limit) + { + fclose(f); + return -2; + } + + r = fseek(f, 0, SEEK_SET); + if (r != 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + + v.resize(s); + if (s == 0) + { + fclose(f); + return 0; + } + + r = fread(&v[0], 1, v.size(), f); + if (r < 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + + fclose(f); + + if (r != s) return -3; + + return 0; +} + +// do not include files and folders whose // name starts with a . -bool file_filter(boost::filesystem::path const& filename) -{ - if (filename.leaf()[0] == '.') return false; - std::cerr << filename << std::endl; - return true; -} +bool file_filter(std::string const& f) +{ + if (filename(f)[0] == '.') return false; + fprintf(stderr, "%s\n", f.c_str()); + return true; +} -void print_progress(int i, int num) -{ - std::cerr << "\r" << (i+1) << "/" << num; -} +void print_progress(int i, int num) +{ + fprintf(stderr, "\r%d/%d", i+1, num); +} -int main(int argc, char* argv[]) -{ - using namespace libtorrent; - using namespace boost::filesystem; +void print_usage() +{ + fputs("usage: make_torrent FILE [OPTIONS]\n" + "\n" + "Generates a torrent file from the specified file\n" + "or directory and writes it to standard out\n\n" + "OPTIONS:\n" + "-m file generate a merkle hash tree torrent.\n" + " merkle torrents require client support\n" + " the resulting full merkle tree is written to\n" + " the specified file\n" + "-f include sha-1 file hashes in the torrent\n" + " this helps supporting mixing sources from\n" + " other networks\n" + "-w url adds a web seed to the torrent with\n" + " the specified url\n" + "-t url adds the specified tracker to the\n" + " torrent. For multiple trackers, specify more\n" + " -t options\n" + "-c comment sets the comment to the specified string\n" + "-C creator sets the created-by field to the specified string\n" + "-p bytes enables padding files. Files larger\n" + " than bytes will be piece-aligned\n" + "-s bytes specifies a piece size for the torrent\n" + " This has to be a multiple of 16 kiB\n" + "-l Don't follow symlinks, instead encode them as\n" + " links in the torrent file\n" + "-o file specifies the output filename of the torrent file\n" + " If this is not specified, the torrent file is\n" + " printed to the standard out, except on windows\n" + " where the filename defaults to a.torrent\n" + "-r file add root certificate to the torrent, to verify\n" + " the HTTPS tracker\n" + , stderr); +} - int piece_size = 256 * 1024; - char const* creator_str = "libtorrent"; +int main(int argc, char* argv[]) +{ + using namespace libtorrent; - path::default_name_check(no_check); + std::string creator_str = "libtorrent"; + std::string comment_str; - if (argc != 4 && argc != 5) - { - std::cerr << "usage: make_torrent <output torrent-file> " - "<announce url> <file or directory to create torrent from> " - "[url-seed]\n"; - return 1; -} + if (argc < 2) + { + print_usage(); + return 1; + } -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - file_storage fs; - file_pool fp; - path full_path = complete(path(argv[3])); +#ifndef BOOST_NO_EXCEPTIONS + try + { +#endif + std::vector<std::string> web_seeds; + std::vector<std::string> trackers; + int pad_file_limit = -1; + int piece_size = 0; + int flags = 0; + std::string root_cert; - add_files(fs, full_path, file_filter); + std::string outfile; + std::string merklefile; +#ifdef TORRENT_WINDOWS + // don't ever write binary data to the console on windows + // it will just be interpreted as text and corrupted + outfile = "a.torrent"; +#endif + + for (int i = 2; i < argc; ++i) + { + if (argv[i][0] != '-') + { + print_usage(); + return 1; + } - create_torrent t(fs, piece_size); - t.add_tracker(argv[2]); - set_piece_hashes(t, full_path.branch_path() - , boost::bind(&print_progress, _1, t.num_pieces())); - std::cerr << std::endl; - t.set_creator(creator_str); + switch (argv[i][1]) + { + case 'w': + ++i; + web_seeds.push_back(argv[i]); + break; + case 't': + ++i; + trackers.push_back(argv[i]); + break; + case 'p': + ++i; + pad_file_limit = atoi(argv[i]); + flags |= create_torrent::optimize; + break; + case 's': + ++i; + piece_size = atoi(argv[i]); + break; + case 'm': + ++i; + merklefile = argv[i]; + flags |= create_torrent::merkle; + break; + case 'o': + ++i; + outfile = argv[i]; + break; + case 'f': + flags |= create_torrent::calculate_file_hashes; + break; + case 'l': + flags |= create_torrent::symlinks; + break; + case 'C': + ++i; + creator_str = argv[i]; + break; + case 'c': + ++i; + comment_str = argv[i]; + break; + case 'r': + ++i; + root_cert = argv[i]; + break; + default: + print_usage(); + return 1; + } + } - if (argc == 5) t.add_url_seed(argv[4]); + file_storage fs; + std::string full_path = libtorrent::complete(argv[1]); - // create the torrent and print it to out - ofstream out(complete(path(argv[1])), std::ios_base::binary); - bencode(std::ostream_iterator<char>(out), t.generate()); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - std::cerr << e.what() << "\n"; - } -#endif + add_files(fs, full_path, file_filter, flags); + if (fs.num_files() == 0) + { + fputs("no files specified.\n", stderr); + return 1; + } - return 0; -} + create_torrent t(fs, piece_size, pad_file_limit, flags); + int tier = 0; + for (std::vector<std::string>::iterator i = trackers.begin() + , end(trackers.end()); i != end; ++i, ++tier) + t.add_tracker(*i, tier); + + for (std::vector<std::string>::iterator i = web_seeds.begin() + , end(web_seeds.end()); i != end; ++i) + t.add_url_seed(*i); + + error_code ec; + set_piece_hashes(t, parent_path(full_path) + , boost::bind(&print_progress, _1, t.num_pieces()), ec); + if (ec) + { + fprintf(stderr, "%s\n", ec.message().c_str()); + return 1; + } + + fprintf(stderr, "\n"); + t.set_creator(creator_str.c_str()); + if (!comment_str.empty()) + t.set_comment(comment_str.c_str()); + + if (!root_cert.empty()) + { + std::vector<char> pem; + load_file(root_cert, pem, ec, 10000); + if (ec) + { + fprintf(stderr, "failed to load root certificate for tracker: %s\n", ec.message().c_str()); + } + else + { + t.set_root_cert(std::string(&pem[0], pem.size())); + } + } + + // create the torrent and print it to stdout + std::vector<char> torrent; + bencode(back_inserter(torrent), t.generate()); + FILE* output = stdout; + if (!outfile.empty()) + output = fopen(outfile.c_str(), "wb+"); + if (output == NULL) + { + fprintf(stderr, "failed to open file \"%s\": (%d) %s\n" + , outfile.c_str(), errno, strerror(errno)); + return 1; + } + fwrite(&torrent[0], 1, torrent.size(), output); + + if (output != stdout) + fclose(output); + + if (!merklefile.empty()) + { + output = fopen(merklefile.c_str(), "wb+"); + if (output == NULL) + { + fprintf(stderr, "failed to open file \"%s\": (%d) %s\n" + , merklefile.c_str(), errno, strerror(errno)); + return 1; + } + int ret = fwrite(&t.merkle_tree()[0], 20, t.merkle_tree().size(), output); + if (ret != int(t.merkle_tree().size())) + { + fprintf(stderr, "failed to write %s: (%d) %s\n" + , merklefile.c_str(), errno, strerror(errno)); + } + fclose(output); + } + +#ifndef BOOST_NO_EXCEPTIONS + } + catch (std::exception& e) + { + fprintf(stderr, "%s\n", e.what()); + } +#endif + + return 0; +} ++
This is an example of a program that will take a torrent-file as a parameter and +print information about it to std out:
++#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/torrent_info.hpp" +#include "libtorrent/lazy_entry.hpp" +#include "libtorrent/magnet_uri.hpp" + +int load_file(std::string const& filename, std::vector<char>& v, libtorrent::error_code& ec, int limit = 8000000) +{ + ec.clear(); + FILE* f = fopen(filename.c_str(), "rb"); + if (f == NULL) + { + ec.assign(errno, boost::system::generic_category()); + return -1; + } + + int r = fseek(f, 0, SEEK_END); + if (r != 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + long s = ftell(f); + if (s < 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + + if (s > limit) + { + fclose(f); + return -2; + } + + r = fseek(f, 0, SEEK_SET); + if (r != 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + + v.resize(s); + if (s == 0) + { + fclose(f); + return 0; + } + + r = fread(&v[0], 1, v.size(), f); + if (r < 0) + { + ec.assign(errno, boost::system::generic_category()); + fclose(f); + return -1; + } + + fclose(f); + + if (r != s) return -3; + + return 0; +} + +int line_longer_than(libtorrent::lazy_entry const& e, int limit) +{ + using namespace libtorrent; + + int line_len = 0; + switch (e.type()) + { + case lazy_entry::list_t: + line_len += 4; + if (line_len > limit) return -1; + for (int i = 0; i < e.list_size(); ++i) + { + int ret = line_longer_than(*e.list_at(i), limit - line_len); + if (ret == -1) return -1; + line_len += ret + 2; + } + break; + case lazy_entry::dict_t: + line_len += 4; + if (line_len > limit) return -1; + for (int i = 0; i < e.dict_size(); ++i) + { + line_len += 4 + e.dict_at(i).first.size(); + if (line_len > limit) return -1; + int ret = line_longer_than(*e.dict_at(i).second, limit - line_len); + if (ret == -1) return -1; + line_len += ret + 1; + } + break; + case lazy_entry::string_t: + line_len += 3 + e.string_length(); + break; + case lazy_entry::int_t: + { + size_type val = e.int_value(); + while (val > 0) + { + ++line_len; + val /= 10; + } + line_len += 2; + } + break; + case lazy_entry::none_t: + line_len += 4; + break; + } + + if (line_len > limit) return -1; + return line_len; +} + +int main(int argc, char* argv[]) +{ + using namespace libtorrent; + + if (argc < 2 || argc > 4) + { + fputs("usage: dump_torrent torrent-file [total-items-limit] [recursion-limit]\n", stderr); + return 1; + } + + int item_limit = 1000000; + int depth_limit = 1000; + + if (argc > 2) item_limit = atoi(argv[2]); + if (argc > 3) depth_limit = atoi(argv[3]); + + std::vector<char> buf; + error_code ec; + int ret = load_file(argv[1], buf, ec, 40 * 1000000); + if (ret == -1) + { + fprintf(stderr, "file too big, aborting\n"); + return 1; + } + + if (ret != 0) + { + fprintf(stderr, "failed to load file: %s\n", ec.message().c_str()); + return 1; + } + lazy_entry e; + int pos = -1; + printf("decoding. recursion limit: %d total item count limit: %d\n" + , depth_limit, item_limit); + ret = lazy_bdecode(&buf[0], &buf[0] + buf.size(), e, ec, &pos + , depth_limit, item_limit); + + printf("\n\n----- raw info -----\n\n%s\n", print_entry(e).c_str()); + + if (ret != 0) + { + fprintf(stderr, "failed to decode: '%s' at character: %d\n", ec.message().c_str(), pos); + return 1; + } + + torrent_info t(e, ec); + if (ec) + { + fprintf(stderr, "%s\n", ec.message().c_str()); + return 1; + } + e.clear(); + std::vector<char>().swap(buf); + + // print info about torrent + printf("\n\n----- torrent file info -----\n\n" + "nodes:\n"); + + typedef std::vector<std::pair<std::string, int> > node_vec; + node_vec const& nodes = t.nodes(); + for (node_vec::const_iterator i = nodes.begin(), end(nodes.end()); + i != end; ++i) + { + printf("%s: %d\n", i->first.c_str(), i->second); + } + puts("trackers:\n"); + for (std::vector<announce_entry>::const_iterator i = t.trackers().begin(); + i != t.trackers().end(); ++i) + { + printf("%2d: %s\n", i->tier, i->url.c_str()); + } + + char ih[41]; + to_hex((char const*)&t.info_hash()[0], 20, ih); + printf("number of pieces: %d\n" + "piece length: %d\n" + "info hash: %s\n" + "comment: %s\n" + "created by: %s\n" + "magnet link: %s\n" + "name: %s\n" + "number of files: %d\n" + "files:\n" + , t.num_pieces() + , t.piece_length() + , ih + , t.comment().c_str() + , t.creator().c_str() + , make_magnet_uri(t).c_str() + , t.name().c_str() + , t.num_files()); + file_storage const& st = t.files(); + for (int i = 0; i < st.num_files(); ++i) + { + int first = st.map_file(i, 0, 0).piece; + int last = st.map_file(i, (std::max)(size_type(st.file_size(i))-1, size_type(0)), 0).piece; + int flags = st.file_flags(i); + printf(" %8" PRIx64 " %11" PRId64 " %c%c%c%c [ %5d, %5d ] %7u %s %s %s%s\n" + , st.file_offset(i) + , st.file_size(i) + , ((flags & file_storage::flag_pad_file)?'p':'-') + , ((flags & file_storage::flag_executable)?'x':'-') + , ((flags & file_storage::flag_hidden)?'h':'-') + , ((flags & file_storage::flag_symlink)?'l':'-') + , first, last + , boost::uint32_t(st.mtime(i)) + , st.hash(i) != sha1_hash(0) ? to_hex(st.hash(i).to_string()).c_str() : "" + , st.file_path(i).c_str() + , (flags & file_storage::flag_symlink) ? "-> " : "" + , (flags & file_storage::flag_symlink) ? st.symlink(i).c_str() : ""); + } + + return 0; +}