| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <chrono> | ||
| 2 | #include <csignal> | ||
| 3 | #include <fstream> | ||
| 4 | #include <iostream> | ||
| 5 | #include <string> | ||
| 6 | #include <thread> | ||
| 7 | #include <utility> | ||
| 8 | #include <vector> | ||
| 9 | #include <libtorrent/add_torrent_params.hpp> | ||
| 10 | #include <libtorrent/alert_types.hpp> | ||
| 11 | #include <libtorrent/bencode.hpp> | ||
| 12 | #include <libtorrent/error_code.hpp> | ||
| 13 | #include <libtorrent/magnet_uri.hpp> | ||
| 14 | #include <libtorrent/read_resume_data.hpp> | ||
| 15 | #include <libtorrent/session.hpp> | ||
| 16 | #include <libtorrent/session_params.hpp> | ||
| 17 | #include <libtorrent/torrent_handle.hpp> | ||
| 18 | #include <libtorrent/torrent_status.hpp> | ||
| 19 | #include <libtorrent/write_resume_data.hpp> | ||
| 20 | #include "examples/tor.h" | ||
| 21 | |||
| 22 | namespace { | ||
| 23 | |||
| 24 | using clk = std::chrono::steady_clock; | ||
| 25 | |||
| 26 | // return the name of a torrent status enum | ||
| 27 | ✗ | char const* state(lt::torrent_status::state_t s) { | |
| 28 | #ifdef __clang__ | ||
| 29 | #pragma clang diagnostic push | ||
| 30 | #pragma clang diagnostic ignored "-Wcovered-switch-default" | ||
| 31 | #endif | ||
| 32 | ✗ | switch (s) { | |
| 33 | ✗ | case lt::torrent_status::checking_files: | |
| 34 | ✗ | return "checking"; | |
| 35 | ✗ | case lt::torrent_status::downloading_metadata: | |
| 36 | ✗ | return "retrieving metadata"; | |
| 37 | ✗ | case lt::torrent_status::downloading: | |
| 38 | ✗ | return "downloading"; | |
| 39 | ✗ | case lt::torrent_status::finished: | |
| 40 | ✗ | return "finished"; | |
| 41 | ✗ | case lt::torrent_status::seeding: | |
| 42 | ✗ | return "seeding"; | |
| 43 | ✗ | case lt::torrent_status::checking_resume_data: | |
| 44 | ✗ | return "checking resume"; | |
| 45 | ✗ | default: | |
| 46 | ✗ | return "<>"; | |
| 47 | } | ||
| 48 | #ifdef __clang__ | ||
| 49 | #pragma clang diagnostic pop | ||
| 50 | #endif | ||
| 51 | } | ||
| 52 | |||
| 53 | ✗ | std::vector<char> load_file(char const* filename) { | |
| 54 | ✗ | std::ifstream ifs(filename, std::ios_base::binary); | |
| 55 | ✗ | ifs.unsetf(std::ios_base::skipws); | |
| 56 | ✗ | return {std::istream_iterator<char>(ifs), std::istream_iterator<char>()}; | |
| 57 | ✗ | } | |
| 58 | |||
| 59 | // set when we're exiting | ||
| 60 | std::atomic<bool> shut_down{false}; | ||
| 61 | |||
| 62 | ✗ | void sighandler(int) { | |
| 63 | ✗ | shut_down = true; | |
| 64 | ✗ | } | |
| 65 | |||
| 66 | } // anonymous namespace | ||
| 67 | |||
| 68 | ✗ | int torrent_example() try { | |
| 69 | ✗ | auto magnet_uri = | |
| 70 | "magnet:?xt=urn:btih:0cc30ccd5366088882f1e7a2bb6f55d66e703c8e&xt=urn:" | ||
| 71 | "btmh:" | ||
| 72 | "122070888da130f26beb551d7513c7afaa9a85546ddf75d339bcdadb0151032f0f22&" | ||
| 73 | "dn=.git&tr=http%3A%2F%2Ftracker.renfei.net%3A8080%2Fannounce&tr=udp%" | ||
| 74 | "3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=https%3A%2F%" | ||
| 75 | "2Ftracker.jdx3.org%3A443%2Fannounce"; | ||
| 76 | |||
| 77 | ✗ | const std::string dir = "tor/"; | |
| 78 | |||
| 79 | // load session parameters | ||
| 80 | ✗ | auto session_params = load_file((dir + ".session").c_str()); | |
| 81 | ✗ | lt::session_params params = session_params.empty() | |
| 82 | ✗ | ? lt::session_params() | |
| 83 | ✗ | : lt::read_session_params(session_params); | |
| 84 | ✗ | params.settings.set_int(lt::settings_pack::alert_mask, | |
| 85 | lt::alert_category::error | | ||
| 86 | lt::alert_category::storage | | ||
| 87 | lt::alert_category::status); | ||
| 88 | |||
| 89 | ✗ | lt::session ses(params); | |
| 90 | ✗ | clk::time_point last_save_resume = clk::now(); | |
| 91 | |||
| 92 | // load resume data from disk and pass it in as we add the magnet link | ||
| 93 | ✗ | auto buf = load_file((dir + ".resume_file").c_str()); | |
| 94 | |||
| 95 | ✗ | lt::add_torrent_params magnet = lt::parse_magnet_uri(magnet_uri); | |
| 96 | ✗ | if (buf.size()) { | |
| 97 | ✗ | lt::add_torrent_params atp = lt::read_resume_data(buf); | |
| 98 | ✗ | if (atp.info_hashes == magnet.info_hashes) | |
| 99 | ✗ | magnet = std::move(atp); | |
| 100 | ✗ | } | |
| 101 | ✗ | magnet.save_path = dir; // save in current dir | |
| 102 | ✗ | ses.async_add_torrent(std::move(magnet)); | |
| 103 | |||
| 104 | // this is the handle we'll set once we get the notification of it being | ||
| 105 | // added | ||
| 106 | ✗ | lt::torrent_handle h; | |
| 107 | |||
| 108 | ✗ | std::signal(SIGINT, &sighandler); | |
| 109 | |||
| 110 | // set when we're exiting | ||
| 111 | ✗ | bool done = false; | |
| 112 | for (;;) { | ||
| 113 | ✗ | std::vector<lt::alert*> alerts; | |
| 114 | ✗ | ses.pop_alerts(&alerts); | |
| 115 | |||
| 116 | ✗ | if (shut_down) { | |
| 117 | ✗ | shut_down = false; | |
| 118 | ✗ | auto const handles = ses.get_torrents(); | |
| 119 | ✗ | if (handles.size() == 1) { | |
| 120 | ✗ | handles[0].save_resume_data( | |
| 121 | lt::torrent_handle::only_if_modified | | ||
| 122 | lt::torrent_handle::save_info_dict); | ||
| 123 | ✗ | done = true; | |
| 124 | } | ||
| 125 | ✗ | } | |
| 126 | |||
| 127 | ✗ | for (lt::alert const* a : alerts) { | |
| 128 | ✗ | if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) { | |
| 129 | ✗ | h = at->handle; | |
| 130 | } | ||
| 131 | // if we receive the finished alert or an error, we're done | ||
| 132 | ✗ | if (lt::alert_cast<lt::torrent_finished_alert>(a)) { | |
| 133 | ✗ | h.save_resume_data(lt::torrent_handle::only_if_modified | | |
| 134 | lt::torrent_handle::save_info_dict); | ||
| 135 | ✗ | done = true; | |
| 136 | } | ||
| 137 | ✗ | if (lt::alert_cast<lt::torrent_error_alert>(a)) { | |
| 138 | ✗ | std::cout << a->message() << '\n'; | |
| 139 | ✗ | done = true; | |
| 140 | ✗ | h.save_resume_data(lt::torrent_handle::only_if_modified | | |
| 141 | lt::torrent_handle::save_info_dict); | ||
| 142 | } | ||
| 143 | |||
| 144 | // when resume data is ready, save it | ||
| 145 | ✗ | if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) { | |
| 146 | ✗ | std::ofstream of(dir + ".resume_file", std::ios_base::binary); | |
| 147 | ✗ | of.unsetf(std::ios_base::skipws); | |
| 148 | ✗ | auto const b = write_resume_data_buf(rd->params); | |
| 149 | ✗ | of.write(b.data(), static_cast<int>(b.size())); | |
| 150 | ✗ | if (done) | |
| 151 | ✗ | goto done; | |
| 152 | ✗ | } | |
| 153 | |||
| 154 | ✗ | if (lt::alert_cast<lt::save_resume_data_failed_alert>(a)) { | |
| 155 | ✗ | if (done) | |
| 156 | ✗ | goto done; | |
| 157 | } | ||
| 158 | |||
| 159 | ✗ | if (auto st = lt::alert_cast<lt::state_update_alert>(a)) { | |
| 160 | ✗ | if (st->status.empty()) | |
| 161 | ✗ | continue; | |
| 162 | |||
| 163 | // we only have a single torrent, so we know which one | ||
| 164 | // the status is for | ||
| 165 | ✗ | lt::torrent_status const& s = st->status[0]; | |
| 166 | ✗ | std::cout << '\r' << state(s.state) << ' ' | |
| 167 | ✗ | << (s.download_payload_rate / 1000) << " kB/s " | |
| 168 | ✗ | << (s.total_done / 1000) << " kB (" | |
| 169 | ✗ | << (s.progress_ppm / 10000) << "%) downloaded (" | |
| 170 | ✗ | << s.num_peers << " peers)\x1b[K"; | |
| 171 | ✗ | std::cout.flush(); | |
| 172 | } | ||
| 173 | } | ||
| 174 | ✗ | std::this_thread::sleep_for(std::chrono::milliseconds(200)); | |
| 175 | |||
| 176 | // ask the session to post a state_update_alert, to update our | ||
| 177 | // state output for the torrent | ||
| 178 | ✗ | ses.post_torrent_updates(); | |
| 179 | |||
| 180 | // save resume data once every 30 seconds | ||
| 181 | ✗ | if (clk::now() - last_save_resume > std::chrono::seconds(30)) { | |
| 182 | ✗ | h.save_resume_data(lt::torrent_handle::only_if_modified | | |
| 183 | lt::torrent_handle::save_info_dict); | ||
| 184 | ✗ | last_save_resume = clk::now(); | |
| 185 | } | ||
| 186 | ✗ | } | |
| 187 | |||
| 188 | ✗ | done: | |
| 189 | ✗ | std::cout << "\nsaving session state" << '\n'; | |
| 190 | { | ||
| 191 | ✗ | std::ofstream of(dir + ".session", std::ios_base::binary); | |
| 192 | ✗ | of.unsetf(std::ios_base::skipws); | |
| 193 | ✗ | auto const b = write_session_params_buf(ses.session_state(), | |
| 194 | ✗ | lt::save_state_flags_t::all()); | |
| 195 | ✗ | of.write(b.data(), static_cast<int>(b.size())); | |
| 196 | ✗ | } | |
| 197 | |||
| 198 | ✗ | std::cout << "\ndone, shutting down" << '\n'; | |
| 199 | ✗ | return 0; | |
| 200 | ✗ | } catch (std::exception& e) { | |
| 201 | ✗ | std::cerr << "Error: " << e.what() << '\n'; | |
| 202 | ✗ | return 1; | |
| 203 | ✗ | } | |
| 204 |