Guest User

Untitled

a guest
Sep 16th, 2014
442
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 44.99 KB | None | 0 0
  1. /*
  2.  
  3. Copyright (c) 2003, Arvid Norberg
  4. All rights reserved.
  5.  
  6. Redistribution and use in source and binary forms, with or without
  7. modification, are permitted provided that the following conditions
  8. are met:
  9.  
  10.     * Redistributions of source code must retain the above copyright
  11.       notice, this list of conditions and the following disclaimer.
  12.     * Redistributions in binary form must reproduce the above copyright
  13.       notice, this list of conditions and the following disclaimer in
  14.       the documentation and/or other materials provided with the distribution.
  15.     * Neither the name of the author nor the names of its
  16.       contributors may be used to endorse or promote products derived
  17.       from this software without specific prior written permission.
  18.  
  19. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  20. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  23. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29. POSSIBILITY OF SUCH DAMAGE.
  30.  
  31. */
  32.  
  33. #include <iterator>
  34.  
  35. #include "libtorrent/config.hpp"
  36.  
  37. #ifdef _MSC_VER
  38. #pragma warning(push, 1)
  39. #endif
  40.  
  41. #include <boost/filesystem/operations.hpp>
  42. #include <boost/filesystem/convenience.hpp>
  43. #include <boost/bind.hpp>
  44.  
  45. #ifdef _MSC_VER
  46. #pragma warning(pop)
  47. #endif
  48.  
  49. #include "libtorrent/extensions/metadata_transfer.hpp"
  50. #include "libtorrent/extensions/ut_metadata.hpp"
  51. #include "libtorrent/extensions/ut_pex.hpp"
  52. #include "libtorrent/extensions/smart_ban.hpp"
  53.  
  54. #include "libtorrent/entry.hpp"
  55. #include "libtorrent/bencode.hpp"
  56. #include "libtorrent/session.hpp"
  57. #include "libtorrent/identify_client.hpp"
  58. #include "libtorrent/alert_types.hpp"
  59. #include "libtorrent/ip_filter.hpp"
  60. #include "libtorrent/magnet_uri.hpp"
  61. #include "libtorrent/bitfield.hpp"
  62. #include "libtorrent/file.hpp"
  63.  
  64. using boost::bind;
  65.  
  66. #ifdef _WIN32
  67.  
  68. #if defined(_MSC_VER)
  69. #   define for if (false) {} else for
  70. #endif
  71.  
  72. #include <windows.h>
  73. #include <conio.h>
  74.  
  75. bool sleep_and_input(char* c, int sleep)
  76. {
  77.     for (int i = 0; i < sleep * 2; ++i)
  78.     {
  79.         if (_kbhit())
  80.         {
  81.             *c = _getch();
  82.             return true;
  83.         }
  84.         Sleep(500);
  85.     }
  86.     return false;
  87. };
  88.  
  89. void clear_home()
  90. {
  91.     CONSOLE_SCREEN_BUFFER_INFO si;
  92.     HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
  93.     GetConsoleScreenBufferInfo(h, &si);
  94.     COORD c = {0, 0};
  95.     DWORD n;
  96.     FillConsoleOutputCharacter(h, ' ', si.dwSize.X * si.dwSize.Y, c, &n);
  97.     SetConsoleCursorPosition(h, c);
  98. }
  99.  
  100. #else
  101.  
  102. #include <stdlib.h>
  103. #include <stdio.h>
  104.  
  105. #include <termios.h>
  106. #include <string.h>
  107. #include <sys/ioctl.h>
  108.  
  109. #define ANSI_TERMINAL_COLORS
  110.  
  111. struct set_keypress
  112. {
  113.     set_keypress()
  114.     {
  115.         termios new_settings;
  116.         tcgetattr(0,&stored_settings);
  117.         new_settings = stored_settings;
  118.         // Disable canonical mode, and set buffer size to 1 byte
  119.         new_settings.c_lflag &= (~ICANON);
  120.         new_settings.c_cc[VTIME] = 0;
  121.         new_settings.c_cc[VMIN] = 1;
  122.         tcsetattr(0,TCSANOW,&new_settings);
  123.     }
  124.     ~set_keypress() { tcsetattr(0,TCSANOW,&stored_settings); }
  125.     termios stored_settings;
  126. };
  127.  
  128. bool sleep_and_input(char* c, int sleep)
  129. {
  130.     // sets the terminal to single-character mode
  131.     // and resets when destructed
  132.     set_keypress s;
  133.  
  134.     fd_set set;
  135.     FD_ZERO(&set);
  136.     FD_SET(0, &set);
  137.     timeval tv = {sleep, 0};
  138.     if (select(1, &set, 0, 0, &tv) > 0)
  139.     {
  140.         *c = getc(stdin);
  141.         return true;
  142.     }
  143.     return false;
  144. }
  145.  
  146. void clear_home()
  147. {
  148.     puts("\033[2J\033[0;0H");
  149. }
  150.  
  151. #endif
  152.  
  153. bool print_trackers = false;
  154. bool print_peers = false;
  155. bool print_log = false;
  156. bool print_downloads = false;
  157. bool print_piece_bar = false;
  158. bool print_file_progress = false;
  159. bool show_pad_files = false;
  160. bool show_dht_status = false;
  161. bool sequential_download = false;
  162.  
  163. bool print_ip = true;
  164. bool print_as = false;
  165. bool print_timers = false;
  166. bool print_block = false;
  167. bool print_peer_rate = false;
  168. bool print_fails = false;
  169. bool print_send_bufs = true;
  170.  
  171. FILE* g_log_file = 0;
  172.  
  173. int active_torrent = 0;
  174.  
  175. char const* esc(char const* code)
  176. {
  177. #ifdef ANSI_TERMINAL_COLORS
  178.     // this is a silly optimization
  179.     // to avoid copying of strings
  180.     enum { num_strings = 200 };
  181.     static char buf[num_strings][20];
  182.     static int round_robin = 0;
  183.     char* ret = buf[round_robin];
  184.     ++round_robin;
  185.     if (round_robin >= num_strings) round_robin = 0;
  186.     ret[0] = '\033';
  187.     ret[1] = '[';
  188.     int i = 2;
  189.     int j = 0;
  190.     while (code[j]) ret[i++] = code[j++];
  191.     ret[i++] = 'm';
  192.     ret[i++] = 0;
  193.     return ret;
  194. #else
  195.     return "";
  196. #endif
  197. }
  198.  
  199. std::string to_string(int v, int width)
  200. {
  201.     std::stringstream s;
  202.     s.flags(std::ios_base::right);
  203.     s.width(width);
  204.     s.fill(' ');
  205.     s << v;
  206.     return s.str();
  207. }
  208.  
  209. std::string& to_string(float v, int width, int precision = 3)
  210. {
  211.     // this is a silly optimization
  212.     // to avoid copying of strings
  213.     enum { num_strings = 20 };
  214.     static std::string buf[num_strings];
  215.     static int round_robin = 0;
  216.     std::string& ret = buf[round_robin];
  217.     ++round_robin;
  218.     if (round_robin >= num_strings) round_robin = 0;
  219.     ret.resize(20);
  220.     int size = std::snprintf(&ret[0], 20, "%*.*f", width, precision, v);
  221.     ret.resize((std::min)(size, width));
  222.     return ret;
  223. }
  224.  
  225. std::string add_suffix(float val, char const* suffix = 0)
  226. {
  227.     std::string ret;
  228.     if (val == 0)
  229.     {
  230.         ret.resize(4 + 2, ' ');
  231.         if (suffix) ret.resize(4 + 2 + strlen(suffix), ' ');
  232.         return ret;
  233.     }
  234.  
  235.     const char* prefix[] = {"kB", "MB", "GB", "TB"};
  236.     const int num_prefix = sizeof(prefix) / sizeof(const char*);
  237.     for (int i = 0; i < num_prefix; ++i)
  238.     {
  239.         val /= 1000.f;
  240.         if (std::fabs(val) < 1000.f)
  241.         {
  242.             ret = to_string(val, 4);
  243.             ret += prefix[i];
  244.             if (suffix) ret += suffix;
  245.             return ret;
  246.         }
  247.     }
  248.     ret = to_string(val, 4);
  249.     ret += "PB";
  250.     if (suffix) ret += suffix;
  251.     return ret;
  252. }
  253.  
  254. std::string const& piece_bar(libtorrent::bitfield const& p, int width)
  255. {
  256. #ifdef ANSI_TERMINAL_COLORS
  257.     static const char* lookup[] =
  258.     {
  259.         // black, blue, cyan, white
  260.         "40", "44", "46", "47"
  261.     };
  262.  
  263.     const int table_size = sizeof(lookup) / sizeof(lookup[0]);
  264. #else
  265.     static const char char_lookup[] =
  266.     { ' ', '.', ':', '-', '+', '*', '#'};
  267.  
  268.     const int table_size = sizeof(char_lookup) / sizeof(char_lookup[0]);
  269. #endif
  270.    
  271.     double piece_per_char = p.size() / double(width);
  272.     static std::string bar;
  273.     bar.clear();
  274.     bar.reserve(width * 6);
  275.     bar += "[";
  276.     if (p.size() == 0)
  277.     {
  278.         for (int i = 0; i < width; ++i) bar += ' ';
  279.         bar += "]";
  280.         return bar;
  281.     }
  282.  
  283.     // the [piece, piece + pieces_per_char) range is the pieces that are represented by each character
  284.     double piece = 0;
  285.     for (int i = 0; i < width; ++i, piece += piece_per_char)
  286.     {
  287.         int num_pieces = 0;
  288.         int num_have = 0;
  289.         int end = (std::max)(int(piece + piece_per_char), int(piece) + 1);
  290.         for (int k = int(piece); k < end; ++k, ++num_pieces)
  291.             if (p[k]) ++num_have;
  292.         int color = int(std::ceil(num_have / float(num_pieces) * (table_size - 1)));
  293. #ifdef ANSI_TERMINAL_COLORS
  294.         bar += esc(lookup[color]);
  295.         bar += " ";
  296. #else
  297.         bar += char_lookup[color];
  298. #endif
  299.     }
  300. #ifdef ANSI_TERMINAL_COLORS
  301.     bar += esc("0");
  302. #endif
  303.     bar += "]";
  304.     return bar;
  305. }
  306.  
  307. std::string const& progress_bar(float progress, int width, char const* code = "33")
  308. {
  309.     static std::string bar;
  310.     bar.clear();
  311.     bar.reserve(width + 10);
  312.  
  313.     int progress_chars = static_cast<int>(progress * width + .5f);
  314.     bar = esc(code);
  315.     std::fill_n(std::back_inserter(bar), progress_chars, '#');
  316.     bar += esc("0");
  317.     std::fill_n(std::back_inserter(bar), width - progress_chars, '-');
  318.     return bar;
  319. }
  320.  
  321. int peer_index(libtorrent::tcp::endpoint addr, std::vector<libtorrent::peer_info> const& peers)
  322. {
  323.     using namespace libtorrent;
  324.     std::vector<peer_info>::const_iterator i = std::find_if(peers.begin()
  325.         , peers.end(), bind(&peer_info::ip, _1) == addr);
  326.     if (i == peers.end()) return -1;
  327.  
  328.     return i - peers.begin();
  329. }
  330.  
  331. void print_peer_info(std::string& out, std::vector<libtorrent::peer_info> const& peers)
  332. {
  333.     using namespace libtorrent;
  334.     if (print_ip) out += "IP                           ";
  335. #ifndef TORRENT_DISABLE_GEO_IP
  336.     if (print_as) out += "AS                                         ";
  337. #endif
  338.     out += "down     (total | peak   )  up      (total | peak   ) sent-req recv flags         source  ";
  339.     if (print_fails) out += "fail hshf ";
  340.     if (print_send_bufs) out += "rq sndb            quota rcvb            q-bytes ";
  341.     if (print_timers) out += "inactive wait timeout q-time ";
  342.     out += "disk   rtt ";
  343.     if (print_block) out += "block-progress ";
  344. #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
  345.     out += "country ";
  346. #endif
  347.     if (print_peer_rate) out += "peer-rate ";
  348.     out += "client \n";
  349.  
  350.     char str[500];
  351.     for (std::vector<peer_info>::const_iterator i = peers.begin();
  352.         i != peers.end(); ++i)
  353.     {
  354.         if (i->flags & (peer_info::handshake | peer_info::connecting | peer_info::queued))
  355.             continue;
  356.  
  357.         if (print_ip)
  358.         {
  359.             error_code ec;
  360.             snprintf(str, sizeof(str), "%-22s:%-5d ", i->ip.address().to_string(ec).c_str(), i->ip.port());
  361.             out += str;
  362.         }
  363.  
  364. #ifndef TORRENT_DISABLE_GEO_IP
  365.         if (print_as)
  366.         {
  367.             error_code ec;
  368.             snprintf(str, sizeof(str), "%-42s ", i->inet_as_name.c_str());
  369.             out += str;
  370.         }
  371. #endif
  372.  
  373.         snprintf(str, sizeof(str)
  374.             , "%s%s (%s|%s) %s%s (%s|%s) %s%3d (%3d) %3d %c%c%c%c%c%c%c%c%c%c%c%c%c%c %c%c%c%c%c%c "
  375.             , esc("32"), add_suffix(i->down_speed, "/s").c_str()
  376.             , add_suffix(i->total_download).c_str(), add_suffix(i->download_rate_peak, "/s").c_str()
  377.             , esc("31"), add_suffix(i->up_speed, "/s").c_str(), add_suffix(i->total_upload).c_str()
  378.             , add_suffix(i->upload_rate_peak, "/s").c_str(), esc("0")
  379.  
  380.             , i->download_queue_length
  381.             , i->target_dl_queue_length
  382.             , i->upload_queue_length
  383.  
  384.             , (i->flags & peer_info::interesting)?'I':'.'
  385.             , (i->flags & peer_info::choked)?'C':'.'
  386.             , (i->flags & peer_info::remote_interested)?'i':'.'
  387.             , (i->flags & peer_info::remote_choked)?'c':'.'
  388.             , (i->flags & peer_info::supports_extensions)?'e':'.'
  389.             , (i->flags & peer_info::local_connection)?'l':'r'
  390.             , (i->flags & peer_info::seed)?'s':'.'
  391.             , (i->flags & peer_info::on_parole)?'p':'.'
  392.             , (i->flags & peer_info::optimistic_unchoke)?'O':'.'
  393.             , (i->read_state == peer_info::bw_limit)?'r':
  394.                 (i->read_state == peer_info::bw_network)?'R':'.'
  395.             , (i->write_state == peer_info::bw_limit)?'w':
  396.                 (i->write_state == peer_info::bw_network)?'W':'.'
  397.             , (i->flags & peer_info::snubbed)?'S':'.'
  398.             , (i->flags & peer_info::upload_only)?'U':'D'
  399. #ifndef TORRENT_DISABLE_ENCRYPTION
  400.             , (i->flags & peer_info::rc4_encrypted)?'E':
  401.                 (i->flags & peer_info::plaintext_encrypted)?'e':'.'
  402. #else
  403.             , '.'
  404. #endif
  405.             , (i->source & peer_info::tracker)?'T':'_'
  406.             , (i->source & peer_info::pex)?'P':'_'
  407.             , (i->source & peer_info::dht)?'D':'_'
  408.             , (i->source & peer_info::lsd)?'L':'_'
  409.             , (i->source & peer_info::resume_data)?'R':'_'
  410.             , (i->source & peer_info::incoming)?'I':'_');
  411.         out += str;
  412.  
  413.         if (print_fails)
  414.         {
  415.             snprintf(str, sizeof(str), "%3d %3d "
  416.                 , i->failcount, i->num_hashfails);
  417.             out += str;
  418.         }
  419.         if (print_send_bufs)
  420.         {
  421.             snprintf(str, sizeof(str), "%2d %6d (%s) %5d %6d (%s) %6d "
  422.                 , i->requests_in_buffer, i->used_send_buffer, add_suffix(i->send_buffer_size).c_str()
  423.                 , i->send_quota, i->used_receive_buffer, add_suffix(i->receive_buffer_size).c_str()
  424.                 , i->queue_bytes);
  425.             out += str;
  426.         }
  427.         if (print_timers)
  428.         {
  429.             snprintf(str, sizeof(str), "%8d %4d %7d %6d "
  430.                 , total_seconds(i->last_active)
  431.                 , total_seconds(i->last_request)
  432.                 , i->request_timeout
  433.                 , total_seconds(i->download_queue_time));
  434.             out += str;
  435.         }
  436.         snprintf(str, sizeof(str), "%s %4d "
  437.             , add_suffix(i->pending_disk_bytes).c_str()
  438.             , i->rtt);
  439.         out += str;
  440.  
  441.         if (print_block)
  442.         {
  443.             if (i->downloading_piece_index >= 0)
  444.             {
  445.                 out += progress_bar(
  446.                     i->downloading_progress / float(i->downloading_total), 14);
  447.             }
  448.             else
  449.             {
  450.                 out += progress_bar(0.f, 14);
  451.             }
  452.         }
  453.  
  454. #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
  455.         if (i->country[0] == 0)
  456.         {
  457.             out += " ..";
  458.         }
  459.         else
  460.         {
  461.             snprintf(str, sizeof(str), " %c%c", i->country[0], i->country[1]);
  462.             out += str;
  463.         }
  464. #endif
  465.         if (print_peer_rate)
  466.         {
  467.             snprintf(str, sizeof(str), " %s", add_suffix(i->remote_dl_rate, "/s").c_str());
  468.             out += str;
  469.         }
  470.         out += " ";
  471.  
  472.         if (i->flags & peer_info::handshake)
  473.         {
  474.             out += esc("31");
  475.             out += " waiting for handshake";
  476.             out += esc("0");
  477.             out += "\n";
  478.         }
  479.         else if (i->flags & peer_info::connecting)
  480.         {
  481.             out += esc("31");
  482.             out += " connecting to peer";
  483.             out += esc("0");
  484.             out += "\n";
  485.         }
  486.         else if (i->flags & peer_info::queued)
  487.         {
  488.             out += esc("33");
  489.             out += " queued";
  490.             out += esc("0");
  491.             out += "\n";
  492.         }
  493.         else
  494.         {
  495.             out += " ";
  496.             out += i->client;
  497.             out += "\n";
  498.         }
  499.     }
  500. }
  501.  
  502. typedef std::multimap<std::string, libtorrent::torrent_handle> handles_t;
  503.  
  504. using boost::bind;
  505. using boost::filesystem::path;
  506. using boost::filesystem::exists;
  507. using boost::filesystem::directory_iterator;
  508. using boost::filesystem::extension;
  509.  
  510.  
  511. // monitored_dir is true if this torrent is added because
  512. // it was found in the directory that is monitored. If it
  513. // is, it should be remembered so that it can be removed
  514. // if it's no longer in that directory.
  515. void add_torrent(libtorrent::session& ses
  516.     , handles_t& handles
  517.     , std::string const& torrent
  518.     , float preferred_ratio
  519.     , bool compact_mode
  520.     , path const& save_path
  521.     , bool monitored_dir
  522.     , int torrent_upload_limit
  523.     , int torrent_download_limit)
  524. {
  525.     using namespace libtorrent;
  526.  
  527.     boost::intrusive_ptr<torrent_info> t;
  528.     error_code ec;
  529.     t = new torrent_info(torrent.c_str(), ec);
  530.     if (ec)
  531.     {
  532.         fprintf(stderr, "%s: %s\n", torrent.c_str(), ec.message().c_str());
  533.         return;
  534.     }
  535.  
  536.     printf("%s\n", t->name().c_str());
  537.  
  538.     add_torrent_params p;
  539.     lazy_entry resume_data;
  540.  
  541.     std::string filename = (save_path / (t->name() + ".resume")).string();
  542.  
  543.     std::vector<char> buf;
  544.     if (load_file(filename.c_str(), buf) == 0)
  545.         p.resume_data = &buf;
  546.  
  547.     p.ti = t;
  548.     p.save_path = save_path;
  549.     p.storage_mode = compact_mode ? storage_mode_compact : storage_mode_sparse;
  550.     p.paused = true;
  551.     p.duplicate_is_error = false;
  552.     p.auto_managed = true;
  553.     torrent_handle h = ses.add_torrent(p, ec);
  554.  
  555.     handles.insert(std::make_pair(
  556.         monitored_dir?std::string(torrent):std::string(), h));
  557.  
  558.     h.set_max_connections(50);
  559.     h.set_max_uploads(-1);
  560.     h.set_ratio(preferred_ratio);
  561.     h.set_upload_limit(torrent_upload_limit);
  562.     h.set_download_limit(torrent_download_limit);
  563. #ifndef TORRENT_DISABLE_RESOLVE_COUNTRIES
  564.     h.resolve_countries(true);
  565. #endif
  566. }
  567.  
  568. void scan_dir(path const& dir_path
  569.     , libtorrent::session& ses
  570.     , handles_t& handles
  571.     , float preferred_ratio
  572.     , bool compact_mode
  573.     , path const& save_path
  574.     , int torrent_upload_limit
  575.     , int torrent_download_limit)
  576. {
  577.     std::set<std::string> valid;
  578.  
  579.     using namespace libtorrent;
  580.  
  581.     for (directory_iterator i(dir_path), end; i != end; ++i)
  582.     {
  583.         if (extension(*i) != ".torrent") continue;
  584.         std::string file = i->path().string();
  585.  
  586.         handles_t::iterator k = handles.find(file);
  587.         if (k != handles.end())
  588.         {
  589.             valid.insert(file);
  590.             continue;
  591.         }
  592.  
  593.         // the file has been added to the dir, start
  594.         // downloading it.
  595.         add_torrent(ses, handles, file, preferred_ratio, compact_mode
  596.             , save_path, true, torrent_upload_limit, torrent_download_limit);
  597.         valid.insert(file);
  598.     }
  599.  
  600.     // remove the torrents that are no longer in the directory
  601.  
  602.     for (handles_t::iterator i = handles.begin(); !handles.empty() && i != handles.end();)
  603.     {
  604.         if (i->first.empty() || valid.find(i->first) != valid.end())
  605.         {
  606.             ++i;
  607.             continue;
  608.         }
  609.  
  610.         torrent_handle& h = i->second;
  611.         if (!h.is_valid())
  612.         {
  613.             handles.erase(i++);
  614.             continue;
  615.         }
  616.        
  617.         h.auto_managed(false);
  618.         h.pause();
  619.         // the alert handler for save_resume_data_alert
  620.         // will save it to disk
  621.         h.save_resume_data();
  622.  
  623.         handles.erase(i++);
  624.     }
  625. }
  626.  
  627. libtorrent::torrent_handle get_active_torrent(handles_t const& handles)
  628. {
  629.     if (active_torrent >= handles.size()
  630.         || active_torrent < 0) return libtorrent::torrent_handle();
  631.     handles_t::const_iterator i = handles.begin();
  632.     std::advance(i, active_torrent);
  633.     return i->second;
  634. }
  635.  
  636. void print_alert(libtorrent::alert const* a, std::string& str)
  637. {
  638.     using namespace libtorrent;
  639.  
  640. #ifdef ANSI_TERMINAL_COLORS
  641.     if (a->category() & alert::error_notification)
  642.     {
  643.         str += esc("31");
  644.     }
  645.     else if (a->category() & (alert::peer_notification | alert::storage_notification))
  646.     {
  647.         str += esc("33");
  648.     }
  649. #endif
  650.     str += "[";
  651.     str += time_now_string();
  652.     str += "] ";
  653.     str += a->message();
  654. #ifdef ANSI_TERMINAL_COLORS
  655.     str += esc("0");
  656. #endif
  657.  
  658.     if (g_log_file)
  659.         fprintf(g_log_file, "[%s] %s\n", time_now_string(),  a->message().c_str());
  660. }
  661.  
  662. int save_file(boost::filesystem::path const& filename, std::vector<char>& v)
  663. {
  664.     using namespace libtorrent;
  665.  
  666.     file f;
  667.     error_code ec;
  668.     if (!f.open(filename, file::write_only, ec)) return -1;
  669.     if (ec) return -1;
  670.     file::iovec_t b = {&v[0], v.size()};
  671.     size_type written = f.writev(0, &b, 1, ec);
  672.     if (written != v.size()) return -3;
  673.     if (ec) return -3;
  674.     return 0;
  675. }
  676.  
  677. void handle_alert(libtorrent::session& ses, libtorrent::alert* a
  678.     , handles_t const& handles)
  679. {
  680.     using namespace libtorrent;
  681.  
  682.     if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a))
  683.     {
  684.         p->handle.set_max_connections(30);
  685.  
  686.         // write resume data for the finished torrent
  687.         // the alert handler for save_resume_data_alert
  688.         // will save it to disk
  689.         torrent_handle h = p->handle;
  690.         h.save_resume_data();
  691.     }
  692.     else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a))
  693.     {
  694.         torrent_handle h = p->handle;
  695.         TORRENT_ASSERT(p->resume_data);
  696.         if (p->resume_data)
  697.         {
  698.             std::vector<char> out;
  699.             bencode(std::back_inserter(out), *p->resume_data);
  700.             save_file(h.save_path() / (h.name() + ".resume"), out);
  701.             if (std::find_if(handles.begin(), handles.end()
  702.                 , bind(&handles_t::value_type::second, _1) == h) == handles.end())
  703.                 ses.remove_torrent(h);
  704.         }
  705.     }
  706.     else if (save_resume_data_failed_alert* p = dynamic_cast<save_resume_data_failed_alert*>(a))
  707.     {
  708.         torrent_handle h = p->handle;
  709.         if (std::find_if(handles.begin(), handles.end()
  710.             , bind(&handles_t::value_type::second, _1) == h) == handles.end())
  711.             ses.remove_torrent(h);
  712.     }
  713. }
  714.  
  715. static char const* state_str[] =
  716.     {"checking (q)", "checking", "dl metadata"
  717.     , "downloading", "finished", "seeding", "allocating", "checking (r)"};
  718.  
  719. int main(int argc, char* argv[])
  720. {
  721. #if BOOST_VERSION < 103400
  722.     using boost::filesystem::no_check;
  723.     path::default_name_check(no_check);
  724. #endif
  725.  
  726.     if (argc == 1)
  727.     {
  728.         fprintf(stderr, "usage: client_test [OPTIONS] [TORRENT|MAGNETURL]\n\n"
  729.             "OPTIONS:\n"
  730.             "  -f <log file>         logs all events to the given file\n"
  731.             "  -o <limit>            limits the number of simultaneous\n"
  732.             "                        half-open TCP connections to the\n"
  733.             "                        given number.\n"
  734.             "  -p <port>             sets the listen port\n"
  735.             "  -r <ratio>            sets the preferred share ratio\n"
  736.             "  -d <rate>             limits the download rate\n"
  737.             "  -u <rate>             limits the upload rate\n"
  738.             "  -S <limit>            limits the upload slots\n"
  739.             "  -a <mode>             sets the allocation mode. [compact|full]\n"
  740.             "  -s <path>             sets the save path for downloads\n"
  741.             "  -U <rate>             sets per-torrent upload rate\n"
  742.             "  -D <rate>             sets per-torrent download rate\n"
  743.             "  -m <path>             sets the .torrent monitor directory\n"
  744.             "  -b <IP>               sets IP of the interface to bind the\n"
  745.             "                        listen socket to\n"
  746.             "  -w <seconds>          sets the retry time for failed web seeds\n"
  747.             "  -t <seconds>          sets the scan interval of the monitor dir\n"
  748.             "  -x <file>             loads an emule IP-filter file\n"
  749.             "  -c <limit>            sets the max number of connections\n"
  750.             "  -C <limit>            sets the max cache size. Specified in 16kB blocks\n"
  751.             "  -F <seconds>          sets the UI refresh rate. This is the number of\n"
  752.             "                        seconds between screen refreshes.\n"
  753.             "\n\n"
  754.             "TORRENT is a path to a .torrent file\n"
  755.             "MAGNETURL is a magnet: url\n")
  756.             ;
  757.         return 0;
  758.     }
  759.  
  760.     using namespace libtorrent;
  761.     session_settings settings;
  762.     proxy_settings ps;
  763.  
  764.     settings.user_agent = "client_test/" LIBTORRENT_VERSION;
  765.     settings.auto_upload_slots_rate_based = true;
  766.     settings.announce_to_all_trackers = true;
  767.     settings.optimize_hashing_for_speed = false;
  768.     settings.disk_cache_algorithm = session_settings::largest_contiguous;
  769.  
  770.     int refresh_delay = 1;
  771.  
  772.     std::deque<std::string> events;
  773.  
  774.     ptime next_dir_scan = time_now();
  775.  
  776.     // the string is the filename of the .torrent file, but only if
  777.     // it was added through the directory monitor. It is used to
  778.     // be able to remove torrents that were added via the directory
  779.     // monitor when they're not in the directory anymore.
  780.     handles_t handles;
  781.     session ses(fingerprint("LT", LIBTORRENT_VERSION_MAJOR, LIBTORRENT_VERSION_MINOR, 0, 0)
  782.         , session::start_default_features | session::add_default_plugins, alert::all_categories);
  783.  
  784.     std::vector<char> in;
  785.     if (load_file(".ses_state", in) == 0)
  786.     {
  787.         ses.load_state(bdecode(in.begin(), in.end()));
  788.     }
  789.  
  790. #ifndef TORRENT_DISABLE_DHT
  791.     settings.use_dht_as_fallback = false;
  792.  
  793.     ses.add_dht_router(std::make_pair(
  794.             std::string("router.bittorrent.com"), 6881));
  795.     ses.add_dht_router(std::make_pair(
  796.             std::string("router.utorrent.com"), 6881));
  797.     ses.add_dht_router(std::make_pair(
  798.             std::string("router.bitcomet.com"), 6881));
  799.  
  800.     if (load_file(".dht_state", in) == 0)
  801.     {
  802.         ses.start_dht(bdecode(in.begin(), in.end()));
  803.     }
  804. #endif
  805.  
  806. #ifndef TORRENT_DISABLE_GEO_IP
  807.     ses.load_asnum_db("GeoIPASNum.dat");
  808.     ses.load_country_db("GeoIP.dat");
  809. #endif
  810.  
  811.     int listen_port = 6881;
  812.     float preferred_ratio = 0.f;
  813.     std::string allocation_mode = "sparse";
  814.     boost::filesystem::path save_path(".");
  815.     int torrent_upload_limit = 0;
  816.     int torrent_download_limit = 0;
  817.     boost::filesystem::path monitor_dir;
  818.     std::string bind_to_interface = "";
  819.     int poll_interval = 5;
  820.  
  821.     // load the torrents given on the commandline
  822.  
  823.     for (int i = 1; i < argc; ++i)
  824.     {
  825.         if (argv[i][0] != '-')
  826.         {
  827.             // interpret this as a torrent
  828.  
  829.             // first see if this is a torrentless download
  830.             if (std::strstr("magnet:", argv[i]) == argv[i])
  831.             {
  832.                 add_torrent_params p;
  833.                 p.save_path = save_path;
  834.                 p.storage_mode = allocation_mode == "compact" ? storage_mode_compact
  835.                     : storage_mode_sparse;
  836.                 printf("adding MANGET link: %s\n", argv[i]);
  837.                 error_code ec;
  838.                 torrent_handle h = add_magnet_uri(ses, argv[i], p, ec);
  839.                 if (ec)
  840.                 {
  841.                     fprintf(stderr, "%s\n", ec.message().c_str());
  842.                     continue;
  843.                 }
  844.  
  845.                 handles.insert(std::make_pair(std::string(), h));
  846.  
  847.                 h.set_max_connections(50);
  848.                 h.set_max_uploads(-1);
  849.                 h.set_ratio(preferred_ratio);
  850.                 h.set_upload_limit(torrent_upload_limit);
  851.                 h.set_download_limit(torrent_download_limit);
  852.                 continue;
  853.             }
  854.  
  855.             // match it against the <hash>@<tracker> format
  856.             if (strlen(argv[i]) > 45
  857.                 && is_hex(argv[i], 40)
  858.                 && string_begins_no_case(argv[i] + 40, "@http"))
  859.             {
  860.                 sha1_hash info_hash;
  861.                 from_hex(argv[i], 40, (char*)&info_hash[0]);
  862.  
  863.                 add_torrent_params p;
  864.                 p.tracker_url = argv[i] + 41;
  865.                 p.info_hash = info_hash;
  866.                 p.save_path = save_path;
  867.                 p.storage_mode = allocation_mode == "compact" ? storage_mode_compact
  868.                     : storage_mode_sparse;
  869.                 p.paused = true;
  870.                 p.duplicate_is_error = false;
  871.                 p.auto_managed = true;
  872.                 error_code ec;
  873.                 torrent_handle h = ses.add_torrent(p, ec);
  874.                 if (ec)
  875.                 {
  876.                     fprintf(stderr, "%s\n", ec.message().c_str());
  877.                     continue;
  878.                 }
  879.  
  880.                 handles.insert(std::make_pair(std::string(), h));
  881.  
  882.                 h.set_max_connections(50);
  883.                 h.set_max_uploads(-1);
  884.                 h.set_ratio(preferred_ratio);
  885.                 h.set_upload_limit(torrent_upload_limit);
  886.                 h.set_download_limit(torrent_download_limit);
  887.                 continue;
  888.             }
  889.  
  890.             // if it's a torrent file, open it as usual
  891.             add_torrent(ses, handles, argv[i], preferred_ratio
  892.                 , allocation_mode == "compact", save_path, false
  893.                 , torrent_upload_limit, torrent_download_limit);
  894.             continue;
  895.         }
  896.  
  897.         // if there's a flag but no argument following, ignore it
  898.         if (argc == i) continue;
  899.         char const* arg = argv[i+1];
  900.         switch (argv[i][1])
  901.         {
  902.             case 'f': g_log_file = fopen(arg, "w+"); break;
  903.             case 'o': ses.set_max_half_open_connections(atoi(arg)); break;
  904.             case 'p': listen_port = atoi(arg); break;
  905.             case 'r':
  906.                 preferred_ratio = atoi(arg);
  907.                 if (preferred_ratio != 0 && preferred_ratio < 1.f) preferred_ratio = 1.f;
  908.                 break;
  909.             case 'd': ses.set_download_rate_limit(atoi(arg) * 1000); break;
  910.             case 'u': ses.set_upload_rate_limit(atoi(arg) * 1000); break;
  911.             case 'S': ses.set_max_uploads(atoi(arg)); break;
  912.             case 'a': allocation_mode = arg; break;
  913.             case 's': save_path = arg; break;
  914.             case 'U': torrent_upload_limit = atoi(arg) * 1000; break;
  915.             case 'D': torrent_download_limit = atoi(arg) * 1000; break;
  916.             case 'm': monitor_dir = arg; break;
  917.             case 'b': bind_to_interface = arg; break;
  918.             case 'w': settings.urlseed_wait_retry = atoi(arg); break;
  919.             case 't': poll_interval = atoi(arg); break;
  920.             case 'F': refresh_delay = atoi(arg); break;
  921.             case 'x':
  922.                 {
  923.                     /*
  924.                     std::ifstream in(arg);
  925.                     ip_filter filter;
  926.                     while (in.good())
  927.                     {
  928.                         char line[300];
  929.                         in.getline(line, 300);
  930.                         int len = in.gcount();
  931.                         if (len <= 0) continue;
  932.                         if (line[0] == '#') continue;
  933.                         int a, b, c, d;
  934.                         char dummy;
  935.                         std::stringstream ln(line);
  936.                         ln >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
  937.                         address_v4 start((a << 24) + (b << 16) + (c << 8) + d);
  938.                         ln >> a >> dummy >> b >> dummy >> c >> dummy >> d;
  939.                         address_v4 last((a << 24) + (b << 16) + (c << 8) + d);
  940.                         int flags;
  941.                         ln >> flags;
  942.                         if (flags <= 127) flags = ip_filter::blocked;
  943.                         else flags = 0;
  944.                         if (ln.fail()) break;
  945.                         filter.add_rule(start, last, flags);
  946.                     }
  947.                     ses.set_ip_filter(filter);
  948.                     */
  949.                 }
  950.                 break;
  951.             case 'c': ses.set_max_connections(atoi(arg)); break;
  952.             case 'C': settings.cache_size = atoi(arg); break;
  953.         }
  954.         ++i; // skip the argument
  955.     }
  956.  
  957.     ses.listen_on(std::make_pair(listen_port, listen_port + 10)
  958.         , bind_to_interface.c_str());
  959.  
  960.     ses.set_settings(settings);
  961.  
  962.     // main loop
  963.     std::vector<peer_info> peers;
  964.     std::vector<partial_piece_info> queue;
  965.  
  966.     for (;;)
  967.     {
  968.         char c;
  969.         while (sleep_and_input(&c, refresh_delay))
  970.         {
  971.             if (c == 27)
  972.             {
  973.                 // escape code, read another character
  974. #ifdef _WIN32
  975.                 c = _getch();
  976. #else
  977.                 c = getc(stdin);
  978. #endif
  979.                 if (c != '[') break;
  980. #ifdef _WIN32
  981.                 c = _getch();
  982. #else
  983.                 c = getc(stdin);
  984. #endif
  985.                 if (c == 65)
  986.                 {
  987.                     // arrow up
  988.                     --active_torrent;
  989.                     if (active_torrent < 0) active_torrent = 0;
  990.                 }
  991.                 else if (c == 66)
  992.                 {
  993.                     // arrow down
  994.                     ++active_torrent;
  995.                     if (active_torrent >= handles.size()) active_torrent = handles.size() - 1;
  996.                 }
  997.             }
  998.  
  999.             if (c == ' ')
  1000.             {
  1001.                 if (ses.is_paused()) ses.resume();
  1002.                 else ses.pause();
  1003.             }
  1004.  
  1005.             if (c == 'm')
  1006.             {
  1007.                 printf("saving peers for torrents\n");
  1008.  
  1009.                 std::vector<peer_list_entry> peers;
  1010.                 for (handles_t::iterator i = handles.begin();
  1011.                     i != handles.end(); ++i)
  1012.                 {
  1013.                     i->second.get_full_peer_list(peers);
  1014.                     FILE* f = fopen(("peers_" + i->second.name()).c_str(), "w+");
  1015.                     if (!f) break;
  1016.                     for (std::vector<peer_list_entry>::iterator k = peers.begin()
  1017.                         , end(peers.end()); k != end; ++k)
  1018.                     {
  1019.                         fprintf(f, "%s\t%d\n", print_address(k->ip.address()).c_str()
  1020. #ifndef TORRENT_DISABLE_GEO_IP
  1021.                             , ses.as_for_ip(k->ip.address())
  1022. #else
  1023.                             , 0
  1024. #endif
  1025.                             );
  1026.                     }
  1027.                 }
  1028.             }
  1029.  
  1030.             if (c == 'q')
  1031.             {
  1032.                 // keep track of the number of resume data
  1033.                 // alerts to wait for
  1034.                 int num_resume_data = 0;
  1035.                 ses.pause();
  1036.                 for (handles_t::iterator i = handles.begin();
  1037.                     i != handles.end(); ++i)
  1038.                 {
  1039.                     torrent_handle& h = i->second;
  1040.                     if (!h.is_valid()) continue;
  1041.                     if (h.is_paused()) continue;
  1042.                     if (!h.has_metadata()) continue;
  1043.  
  1044.                     printf("saving resume data for %s\n", h.name().c_str());
  1045.                     // save_resume_data will generate an alert when it's done
  1046.                     h.save_resume_data();
  1047.                     ++num_resume_data;
  1048.                 }
  1049.                 printf("waiting for resume data\n");
  1050.  
  1051.                 while (num_resume_data > 0)
  1052.                 {
  1053.                     alert const* a = ses.wait_for_alert(seconds(30));
  1054.                     if (a == 0)
  1055.                     {
  1056.                         printf(" aborting with %d outstanding "
  1057.                             "torrents to save resume data for", num_resume_data);
  1058.                         break;
  1059.                     }
  1060.  
  1061.                     std::auto_ptr<alert> holder = ses.pop_alert();
  1062.  
  1063.                     std::string log;
  1064.                     ::print_alert(holder.get(), log);
  1065.                     printf("%s\n", log.c_str());
  1066.  
  1067.                     if (dynamic_cast<save_resume_data_failed_alert const*>(a))
  1068.                     {
  1069.                         --num_resume_data;
  1070.                         continue;
  1071.                     }
  1072.  
  1073.                     save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
  1074.                     if (!rd) continue;
  1075.                     --num_resume_data;
  1076.  
  1077.                     if (!rd->resume_data) continue;
  1078.  
  1079.                     torrent_handle h = rd->handle;
  1080.                     std::vector<char> out;
  1081.                     bencode(std::back_inserter(out), *rd->resume_data);
  1082.                     save_file(h.save_path() / (h.name() + ".resume"), out);
  1083.                 }
  1084.                 break;
  1085.             }
  1086.  
  1087.             if (c == 'j')
  1088.             {
  1089.                 torrent_handle h = get_active_torrent(handles);
  1090.                 if (h.is_valid()) h.force_recheck();
  1091.             }
  1092.  
  1093.             if (c == 'r')
  1094.             {
  1095.                 torrent_handle h = get_active_torrent(handles);
  1096.                 if (h.is_valid()) h.force_reannounce();
  1097.             }
  1098.  
  1099.             if (c == 's')
  1100.             {
  1101.                 torrent_handle h = get_active_torrent(handles);
  1102.                 if (h.is_valid()) h.set_sequential_download(!h.is_sequential_download());
  1103.             }
  1104.  
  1105.             if (c == 'v')
  1106.             {
  1107.                 torrent_handle h = get_active_torrent(handles);
  1108.                 if (h.is_valid()) h.scrape_tracker();
  1109.             }
  1110.  
  1111.             if (c == 'p')
  1112.             {
  1113.                 torrent_handle h = get_active_torrent(handles);
  1114.                 if (h.is_valid())
  1115.                 {
  1116.                     if (!h.is_auto_managed() && h.is_paused())
  1117.                     {
  1118.                         h.auto_managed(true);
  1119.                     }
  1120.                     else
  1121.                     {
  1122.                         h.auto_managed(false);
  1123.                         h.pause();
  1124.                     }
  1125.                     // the alert handler for save_resume_data_alert
  1126.                     // will save it to disk
  1127.                     h.save_resume_data();
  1128.                 }
  1129.             }
  1130.  
  1131.             if (c == 'c')
  1132.             {
  1133.                 torrent_handle h = get_active_torrent(handles);
  1134.                 if (h.is_valid()) h.clear_error();
  1135.             }
  1136.  
  1137.             // toggle displays
  1138.             if (c == 't') print_trackers = !print_trackers;
  1139.             if (c == 'i') print_peers = !print_peers;
  1140.             if (c == 'l') print_log = !print_log;
  1141.             if (c == 'd') print_downloads = !print_downloads;
  1142.             if (c == 'f') print_file_progress = !print_file_progress;
  1143.             if (c == 'h') show_pad_files = !show_pad_files;
  1144.             if (c == 'a') print_piece_bar = !print_piece_bar;
  1145.             if (c == 'g') show_dht_status = !show_dht_status;
  1146.             // toggle columns
  1147.             if (c == '1') print_ip = !print_ip;
  1148.             if (c == '2') print_as = !print_as;
  1149.             if (c == '3') print_timers = !print_timers;
  1150.             if (c == '4') print_block = !print_block;
  1151.             if (c == '5') print_peer_rate = !print_peer_rate;
  1152.             if (c == '6') print_fails = !print_fails;
  1153.             if (c == '7') print_send_bufs = !print_send_bufs;
  1154.         }
  1155.         if (c == 'q') break;
  1156.  
  1157.         int terminal_width = 80;
  1158.  
  1159. #ifndef _WIN32
  1160.         {
  1161.             winsize size;
  1162.             ioctl(STDOUT_FILENO, TIOCGWINSZ, (char*)&size);
  1163.             terminal_width = size.ws_col;
  1164.         }
  1165. #endif
  1166.  
  1167.         // loop through the alert queue to see if anything has happened.
  1168.         std::auto_ptr<alert> a;
  1169.         a = ses.pop_alert();
  1170.         std::string now = time_now_string();
  1171.         while (a.get())
  1172.         {
  1173.             std::string event_string;
  1174.  
  1175.             ::print_alert(a.get(), event_string);
  1176.             ::handle_alert(ses, a.get(), handles);
  1177.  
  1178.             events.push_back(event_string);
  1179.             if (events.size() >= 20) events.pop_front();
  1180.  
  1181.             a = ses.pop_alert();
  1182.         }
  1183.  
  1184.         session_status sess_stat = ses.status();
  1185.  
  1186.         std::string out;
  1187.         out = "[q] quit [i] toggle peers [d] toggle downloading pieces [p] toggle paused "
  1188.             "[a] toggle piece bar [s] toggle download sequential [f] toggle files "
  1189.             "[j] force recheck [space] toggle session pause [c] clear error [v] scrape [g] show DHT\n"
  1190.             "[1] toggle IP [2] toggle AS [3] toggle timers [4] toggle block progress "
  1191.             "[5] toggle peer rate [6] toggle failures [7] toggle send buffers\n";
  1192.  
  1193.         char str[500];
  1194.         int torrent_index = 0;
  1195.         torrent_handle active_handle;
  1196.         for (handles_t::iterator i = handles.begin();
  1197.             i != handles.end(); ++torrent_index)
  1198.         {
  1199.             torrent_handle& h = i->second;
  1200.             if (!h.is_valid())
  1201.             {
  1202.                 handles.erase(i++);
  1203.                 continue;
  1204.             }
  1205.             else
  1206.             {
  1207.                 ++i;
  1208.             }
  1209.  
  1210. #ifdef ANSI_TERMINAL_COLORS
  1211.             char const* term = "\x1b[0m";
  1212. #else
  1213.             char const* term = "";
  1214. #endif
  1215.             if (active_torrent == torrent_index)
  1216.             {
  1217.                 term = "\x1b[0m\x1b[7m";
  1218.                 out += esc("7");
  1219.                 out += "*";
  1220.             }
  1221.             else
  1222.             {
  1223.                 out += " ";
  1224.             }
  1225.  
  1226.             int queue_pos = h.queue_position();
  1227.             if (queue_pos == -1) out += "-  ";
  1228.             else
  1229.             {
  1230.                 snprintf(str, sizeof(str), "%-3d", queue_pos);
  1231.                 out += str;
  1232.             }
  1233.  
  1234.             if (h.is_paused()) out += esc("34");
  1235.             else out += esc("37");
  1236.  
  1237.             std::string name = h.name();
  1238.             if (name.size() > 40) name.resize(40);
  1239.             snprintf(str, sizeof(str), "%-40s %s ", name.c_str(), term);
  1240.             out += str;
  1241.  
  1242.             torrent_status s = h.status();
  1243.  
  1244.             bool paused = h.is_paused();
  1245.             bool auto_managed = h.is_auto_managed();
  1246.             bool sequential_download = h.is_sequential_download();
  1247.  
  1248.             if (!s.error.empty())
  1249.             {
  1250.                 out += esc("31");
  1251.                 out += "error ";
  1252.                 out += s.error;
  1253.                 out += esc("0");
  1254.                 out += "\n";
  1255.                 continue;
  1256.             }
  1257.  
  1258.             int seeds = 0;
  1259.             int downloaders = 0;
  1260.  
  1261.             if (s.num_complete >= 0) seeds = s.num_complete;
  1262.             else seeds = s.list_seeds;
  1263.  
  1264.             if (s.num_incomplete >= 0) downloaders = s.num_incomplete;
  1265.             else downloaders = s.list_peers - s.list_seeds;
  1266.  
  1267.             snprintf(str, sizeof(str), "%-13s down: (%s%s%s) up: %s%s%s (%s%s%s) swarm: %4d:%4d"
  1268.                 "  bw queue: (%d|%d) all-time (Rx: %s%s%s Tx: %s%s%s) seed rank: %x%s\n"
  1269.                 , (paused && !auto_managed)?"paused":(paused && auto_managed)?"queued":state_str[s.state]
  1270.                 , esc("32"), add_suffix(s.total_download).c_str(), term
  1271.                 , esc("31"), add_suffix(s.upload_rate, "/s").c_str(), term
  1272.                 , esc("31"), add_suffix(s.total_upload).c_str(), term
  1273.                 , downloaders, seeds
  1274.                 , s.up_bandwidth_queue, s.down_bandwidth_queue
  1275.                 , esc("32"), add_suffix(s.all_time_download).c_str(), term
  1276.                 , esc("31"), add_suffix(s.all_time_upload).c_str(), term
  1277.                 , s.seed_rank, esc("0"));
  1278.             out += str;
  1279.  
  1280.             if (torrent_index != active_torrent && s.state == torrent_status::seeding) continue;
  1281.             char const* progress_bar_color = "33"; // yellow
  1282.             if (s.state == torrent_status::checking_files
  1283.                 || s.state == torrent_status::downloading_metadata)
  1284.             {
  1285.                 progress_bar_color = "35"; // magenta
  1286.             }
  1287.             else if (s.current_tracker.empty())
  1288.             {
  1289.                 progress_bar_color = "31"; // red
  1290.             }
  1291.             else if (sess_stat.has_incoming_connections)
  1292.             {
  1293.                 progress_bar_color = "32"; // green
  1294.             }
  1295.  
  1296.             snprintf(str, sizeof(str), "     %-10s: %s%-10"PRId64"%s Bytes %6.2f%% %s\n"
  1297.                 , sequential_download?"sequential":"progress"
  1298.                 , esc("32"), s.total_done, esc("0")
  1299.                 , s.progress*100.f
  1300.                 , progress_bar(s.progress, terminal_width - 42, progress_bar_color).c_str());
  1301.             out += str;
  1302.  
  1303.             if (print_piece_bar && s.progress < 1.f)
  1304.             {
  1305.                 out += "     ";
  1306.                 out += piece_bar(s.pieces, terminal_width - 7);
  1307.                 out += "\n";
  1308.             }
  1309.  
  1310.             boost::posix_time::time_duration t = s.next_announce;
  1311.             snprintf(str, sizeof(str)
  1312.                 , "     peers: %s%d%s (%s%d%s) seeds: %s%d%s distributed copies: %s%4.2f%s "
  1313.                     "sparse regions: %d download: %s%s%s next announce: %s%02d:%02d:%02d%s "
  1314.                     "tracker: %s%s%s\n"
  1315.                 , esc("37"), s.num_peers, esc("0")
  1316.                 , esc("37"), s.connect_candidates, esc("0")
  1317.                 , esc("37"), s.num_seeds, esc("0")
  1318.                 , esc("37"), s.distributed_copies, esc("0")
  1319.                 , s.sparse_regions
  1320.                 , esc("32"), add_suffix(s.download_rate, "/s").c_str(), esc("0")
  1321.                 , esc("37"), t.hours(), t.minutes(), t.seconds(), esc("0")
  1322.                 , esc("36"), s.current_tracker.c_str(), esc("0"));
  1323.             out += str;
  1324.  
  1325.             if (torrent_index != active_torrent) continue;
  1326.             active_handle = h;
  1327.         }
  1328.  
  1329.         cache_status cs = ses.get_cache_status();
  1330.         if (cs.blocks_read < 1) cs.blocks_read = 1;
  1331.         if (cs.blocks_written < 1) cs.blocks_written = 1;
  1332.  
  1333.         snprintf(str, sizeof(str), "==== conns: %d down: %s%s%s (%s%s%s) up: %s%s%s (%s%s%s) "
  1334.             "tcp/ip: %s%s%s %s%s%s DHT: %s%s%s %s%s%s tracker: %s%s%s %s%s%s ====\n"
  1335.             , sess_stat.num_peers
  1336.             , esc("32"), add_suffix(sess_stat.download_rate, "/s").c_str(), esc("0")
  1337.             , esc("32"), add_suffix(sess_stat.total_download).c_str(), esc("0")
  1338.             , esc("31"), add_suffix(sess_stat.upload_rate, "/s").c_str(), esc("0")
  1339.             , esc("31"), add_suffix(sess_stat.total_upload).c_str(), esc("0")
  1340.             , esc("32"), add_suffix(sess_stat.ip_overhead_download_rate, "/s").c_str(), esc("0")
  1341.             , esc("31"), add_suffix(sess_stat.ip_overhead_upload_rate, "/s").c_str(), esc("0")
  1342.             , esc("32"), add_suffix(sess_stat.dht_download_rate, "/s").c_str(), esc("0")
  1343.             , esc("31"), add_suffix(sess_stat.dht_upload_rate, "/s").c_str(), esc("0")
  1344.             , esc("32"), add_suffix(sess_stat.tracker_download_rate, "/s").c_str(), esc("0")
  1345.             , esc("31"), add_suffix(sess_stat.tracker_upload_rate, "/s").c_str(), esc("0"));
  1346.         out += str;
  1347.  
  1348.         snprintf(str, sizeof(str), "==== waste: %s fail: %s unchoked: %d / %d "
  1349.             "bw queues: %8d (%d) | %8d (%d) cache: w: %"PRId64"%% r: %lld%% size: %s (%s) / %s ===\n"
  1350.             , add_suffix(sess_stat.total_redundant_bytes).c_str()
  1351.             , add_suffix(sess_stat.total_failed_bytes).c_str()
  1352.             , sess_stat.num_unchoked, sess_stat.allowed_upload_slots
  1353.             , sess_stat.up_bandwidth_bytes_queue
  1354.             , sess_stat.up_bandwidth_queue
  1355.             , sess_stat.down_bandwidth_bytes_queue
  1356.             , sess_stat.down_bandwidth_queue
  1357.             , (cs.blocks_written - cs.writes) * 100 / cs.blocks_written
  1358.             , cs.blocks_read_hit * 100 / cs.blocks_read
  1359.             , add_suffix(cs.cache_size * 16 * 1024).c_str()
  1360.             , add_suffix(cs.read_cache_size * 16 * 1024).c_str()
  1361.             , add_suffix(cs.total_used_buffers * 16 * 1024).c_str());
  1362.         out += str;
  1363.  
  1364.         snprintf(str, sizeof(str), "==== optimistic unchoke: %d unchoke counter: %d ====\n"
  1365.             , sess_stat.optimistic_unchoke_counter, sess_stat.unchoke_counter);
  1366.         out += str;
  1367.  
  1368.         if (show_dht_status)
  1369.         {
  1370.             snprintf(str, sizeof(str), "DHT nodes: %d DHT cached nodes: %d total DHT size: %"PRId64"\n"
  1371.                 , sess_stat.dht_nodes, sess_stat.dht_node_cache, sess_stat.dht_global_nodes);
  1372.             out += str;
  1373.  
  1374.             for (std::vector<dht_lookup>::iterator i = sess_stat.active_requests.begin()
  1375.                 , end(sess_stat.active_requests.end()); i != end; ++i)
  1376.             {
  1377.                 snprintf(str, sizeof(str), "  %s %d (%d) ( timeouts %d responses %d)\n"
  1378.                     , i->type, i->outstanding_requests, i->branch_factor, i->timeouts, i->responses);
  1379.                 out += str;
  1380.             }
  1381.         }
  1382.  
  1383.         if (active_handle.is_valid())
  1384.         {
  1385.             torrent_handle h = active_handle;
  1386.             torrent_status s = h.status();
  1387.  
  1388.             if ((print_downloads && s.state != torrent_status::seeding)
  1389.                 || print_peers)
  1390.                 h.get_peer_info(peers);
  1391.  
  1392.             out += "====== ";
  1393.             out += h.name();
  1394.             out += " ======\n";
  1395.  
  1396.             if (print_peers && !peers.empty())
  1397.                 print_peer_info(out, peers);
  1398.  
  1399.             if (print_trackers)
  1400.             {
  1401.                 std::vector<announce_entry> tr = h.trackers();
  1402.                 ptime now = time_now();
  1403.                 for (std::vector<announce_entry>::iterator i = tr.begin()
  1404.                     , end(tr.end()); i != end; ++i)
  1405.                 {
  1406.                     snprintf(str, sizeof(str), "%2d %-55s fails: %-3d %s %s\n"
  1407.                         , i->tier, i->url.c_str(), i->fails, i->verified?"OK ":"-  "
  1408.                         , i->updating?"updating"
  1409.                             :!i->verified?""
  1410.                             :to_string(total_seconds(i->next_announce - now), 8).c_str());
  1411.                     out += str;
  1412.                 }
  1413.             }
  1414.  
  1415.             if (print_downloads)
  1416.             {
  1417.  
  1418.                 h.get_download_queue(queue);
  1419.                 std::sort(queue.begin(), queue.end(), bind(&partial_piece_info::piece_index, _1)
  1420.                     < bind(&partial_piece_info::piece_index, _2));
  1421.  
  1422.                 std::vector<cached_piece_info> pieces;
  1423.                 ses.get_cache_info(h.info_hash(), pieces);
  1424.  
  1425.                 for (std::vector<partial_piece_info>::iterator i = queue.begin();
  1426.                     i != queue.end(); ++i)
  1427.                 {
  1428.                     cached_piece_info* cp = 0;
  1429.                     std::vector<cached_piece_info>::iterator cpi = std::find_if(pieces.begin(), pieces.end()
  1430.                         , bind(&cached_piece_info::piece, _1) == i->piece_index);
  1431.                     if (cpi != pieces.end()) cp = &*cpi;
  1432.  
  1433.                     snprintf(str, sizeof(str), "%5d: [", i->piece_index);
  1434.                     out += str;
  1435.                     for (int j = 0; j < i->blocks_in_piece; ++j)
  1436.                     {
  1437.                         int index = peer_index(i->blocks[j].peer(), peers);
  1438.                         char chr = '+';
  1439.                         if (index >= 0)
  1440.                             chr = (index < 10)?'0' + index:'A' + index - 10;
  1441.  
  1442.                         char const* color = "";
  1443.  
  1444. #ifdef ANSI_TERMINAL_COLORS
  1445.                         if (cp && cp->blocks[j]) color = esc("36;7");
  1446.                         else if (i->blocks[j].bytes_progress > 0
  1447.                             && i->blocks[j].state == block_info::requested)
  1448.                         {
  1449.                             if (i->blocks[j].num_peers > 1) color = esc("1;7");
  1450.                             else color = esc("33;7");
  1451.                             chr = '0' + (i->blocks[j].bytes_progress / float(i->blocks[j].block_size) * 10);
  1452.                         }
  1453.                         else if (i->blocks[j].state == block_info::finished) color = esc("32;7");
  1454.                         else if (i->blocks[j].state == block_info::writing) color = esc("35;7");
  1455.                         else if (i->blocks[j].state == block_info::requested) color = esc("0");
  1456.                         else { color = esc("0"); chr = ' '; }
  1457. #else
  1458.                         if (cp && cp->blocks[j]) chr = 'c';
  1459.                         else if (i->blocks[j].state == block_info::finished) chr = '#';
  1460.                         else if (i->blocks[j].state == block_info::writing) chr = '+';
  1461.                         else if (i->blocks[j].state == block_info::requested) chr = '-';
  1462.                         else chr = ' '
  1463. #endif
  1464.                         snprintf(str, sizeof(str), "%s%c", color, chr);
  1465.                         out += str;
  1466.                     }
  1467. #ifdef ANSI_TERMINAL_COLORS
  1468.                     out += esc("0");
  1469. #endif
  1470.                     char const* piece_state[4] = {"", " slow", " medium", " fast"};
  1471.                     snprintf(str, sizeof(str), "]%s", piece_state[i->piece_state]);
  1472.                     out += str;
  1473.                     if (cp)
  1474.                     {
  1475.                         snprintf(str, sizeof(str), " %scache age: %-.1f"
  1476.                             , i->piece_state > 0?"| ":""
  1477.                             , total_milliseconds(time_now() - cp->last_use) / 1000.f);
  1478.                         out += str;
  1479.                     }
  1480.                     out += "\n";
  1481.                 }
  1482.  
  1483.                 for (std::vector<cached_piece_info>::iterator i = pieces.begin()
  1484.                     , end(pieces.end()); i != end; ++i)
  1485.                 {
  1486.                     if (i->kind != cached_piece_info::read_cache) continue;
  1487.                     snprintf(str, sizeof(str), "%5d: [", i->piece);
  1488.                     out += str;
  1489.                     for (std::vector<bool>::iterator k = i->blocks.begin()
  1490.                         , end(i->blocks.end()); k != end; ++k)
  1491.                     {
  1492.                         char const* color = "";
  1493.                         char chr = ' ';
  1494. #ifdef ANSI_TERMINAL_COLORS
  1495.                         color = *k?esc("33;7"):esc("0");
  1496. #else
  1497.                         chr = *k?'#':' ';
  1498. #endif
  1499.                         snprintf(str, sizeof(str), "%s%c", color, chr);
  1500.                         out += str;
  1501.                     }
  1502. #ifdef ANSI_TERMINAL_COLORS
  1503.                     out += esc("0");
  1504. #endif
  1505.                     snprintf(str, sizeof(str), "] cache age: %-.1f\n"
  1506.                         , total_milliseconds(time_now() - i->last_use) / 1000.f);
  1507.                     out += str;
  1508.                 }
  1509.                 out += "___________________________________\n";
  1510.             }
  1511.  
  1512.             if (print_file_progress
  1513.                 && s.state != torrent_status::seeding
  1514.                 && h.has_metadata())
  1515.             {
  1516.                 std::vector<size_type> file_progress;
  1517.                 h.file_progress(file_progress);
  1518.                 torrent_info const& info = h.get_torrent_info();
  1519.                 for (int i = 0; i < info.num_files(); ++i)
  1520.                 {
  1521.                     bool pad_file = info.file_at(i).pad_file;
  1522.                     if (!show_pad_files && pad_file) continue;
  1523.                     float progress = info.file_at(i).size > 0
  1524.                         ?float(file_progress[i]) / info.file_at(i).size:1;
  1525.  
  1526.                     char const* color = (file_progress[i] == info.file_at(i).size)
  1527.                         ?"32":"33";
  1528.  
  1529.                     snprintf(str, sizeof(str), "%s %s %-5.2f%% %s %s%s\n",
  1530.                         progress_bar(progress, 100, color).c_str()
  1531.                         , pad_file?esc("34"):""
  1532.                         , progress * 100.f
  1533.                         , add_suffix(file_progress[i]).c_str()
  1534.                         , info.file_at(i).path.leaf().c_str()
  1535.                         , pad_file?esc("0"):"");
  1536.                     out += str;
  1537.                 }
  1538.  
  1539.                 out += "___________________________________\n";
  1540.             }
  1541.  
  1542.         }
  1543.  
  1544.         if (print_log)
  1545.         {
  1546.             for (std::deque<std::string>::iterator i = events.begin();
  1547.                 i != events.end(); ++i)
  1548.             {
  1549.                 out += "\n";
  1550.                 out += *i;
  1551.             }
  1552.         }
  1553.  
  1554.         clear_home();
  1555.         puts(out.c_str());
  1556.  
  1557.         if (!monitor_dir.empty()
  1558.             && next_dir_scan < time_now())
  1559.         {
  1560.             scan_dir(monitor_dir, ses, handles, preferred_ratio
  1561.                 , allocation_mode == "compact", save_path, torrent_upload_limit
  1562.                 , torrent_download_limit);
  1563.             next_dir_scan = time_now() + seconds(poll_interval);
  1564.         }
  1565.     }
  1566.  
  1567.     printf("saving session state\n");
  1568.     {  
  1569.         entry session_state = ses.state();
  1570.  
  1571.         std::vector<char> out;
  1572.         bencode(std::back_inserter(out), session_state);
  1573.         save_file(".ses_state", out);
  1574.     }
  1575.  
  1576. #ifndef TORRENT_DISABLE_DHT
  1577.     printf("saving DHT state\n");
  1578.     entry dht_state = ses.dht_state();
  1579.  
  1580.     std::vector<char> out;
  1581.     bencode(std::back_inserter(out), dht_state);
  1582.     save_file(".dht_state", out);
  1583. #endif
  1584.     printf("closing session");
  1585.  
  1586.     return 0;
  1587. }
Advertisement
Add Comment
Please, Sign In to add comment