2022-01-20 18:27:56 +00:00
|
|
|
// This file Copyright © 2012-2022 Mnemosyne LLC.
|
2022-08-08 18:05:39 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2022-01-13 02:13:58 +00:00
|
|
|
#include <algorithm>
|
2021-12-17 20:48:02 +00:00
|
|
|
#include <array>
|
2021-12-25 22:41:18 +00:00
|
|
|
#include <ctime>
|
2022-01-13 02:13:58 +00:00
|
|
|
#include <string>
|
2021-12-25 22:41:18 +00:00
|
|
|
#include <string_view>
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2010-12-12 16:43:19 +00:00
|
|
|
#include <curl/curl.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2010-12-20 02:07:51 +00:00
|
|
|
#include <event2/buffer.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2022-04-22 19:56:54 +00:00
|
|
|
#include <fmt/chrono.h>
|
2022-05-24 04:05:16 +00:00
|
|
|
#include <fmt/core.h>
|
2022-04-22 19:56:54 +00:00
|
|
|
#include <fmt/format.h>
|
|
|
|
|
2010-06-16 14:27:24 +00:00
|
|
|
#include <libtransmission/transmission.h>
|
2021-12-25 22:41:18 +00:00
|
|
|
|
|
|
|
#include <libtransmission/error.h>
|
2022-03-17 22:39:06 +00:00
|
|
|
#include <libtransmission/log.h>
|
2021-12-25 22:41:18 +00:00
|
|
|
#include <libtransmission/torrent-metainfo.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
#include <libtransmission/tr-getopt.h>
|
2021-12-25 22:41:18 +00:00
|
|
|
#include <libtransmission/tr-macros.h>
|
2022-04-07 22:26:59 +00:00
|
|
|
#include <libtransmission/tr-strbuf.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
#include <libtransmission/utils.h>
|
2012-12-14 04:34:42 +00:00
|
|
|
#include <libtransmission/variant.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
#include <libtransmission/version.h>
|
2021-12-25 22:41:18 +00:00
|
|
|
#include <libtransmission/web-utils.h>
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2014-06-10 00:43:21 +00:00
|
|
|
#include "units.h"
|
|
|
|
|
2021-12-14 20:59:40 +00:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
|
|
|
|
auto constexpr TimeoutSecs = long{ 30 };
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
char constexpr MyName[] = "transmission-show";
|
2022-03-21 14:15:48 +00:00
|
|
|
char constexpr Usage[] = "Usage: transmission-show [options] <torrent-file>";
|
2021-12-25 22:41:18 +00:00
|
|
|
char constexpr UserAgent[] = "transmission-show/" LONG_VERSION_STRING;
|
2021-12-17 20:48:02 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
auto options = std::array<tr_option, 14>{
|
|
|
|
{ { 'd', "header", "Show only header section", "d", false, nullptr },
|
|
|
|
{ 'i', "info", "Show only info section", "i", false, nullptr },
|
|
|
|
{ 't', "trackers", "Show only trackers section", "t", false, nullptr },
|
|
|
|
{ 'f', "files", "Show only file list", "f", false, nullptr },
|
|
|
|
{ 'D', "no-header", "Do not show header section", "D", false, nullptr },
|
|
|
|
{ 'I', "no-info", "Do not show info section", "I", false, nullptr },
|
|
|
|
{ 'T', "no-trackers", "Do not show trackers section", "T", false, nullptr },
|
|
|
|
{ 'F', "no-files", "Do not show files section", "F", false, nullptr },
|
|
|
|
{ 'b', "bytes", "Show file sizes in bytes", "b", false, nullptr },
|
|
|
|
{ 'm', "magnet", "Give a magnet link for the specified torrent", "m", false, nullptr },
|
2021-12-17 20:48:02 +00:00
|
|
|
{ 's', "scrape", "Ask the torrent's trackers how many peers are in the torrent's swarm", "s", false, nullptr },
|
|
|
|
{ 'u', "unsorted", "Do not sort files by name", "u", false, nullptr },
|
|
|
|
{ 'V', "version", "Show version number and exit", "V", false, nullptr },
|
|
|
|
{ 0, nullptr, nullptr, nullptr, false, nullptr } }
|
|
|
|
};
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
struct app_opts
|
|
|
|
{
|
|
|
|
std::string_view filename;
|
|
|
|
bool scrape = false;
|
|
|
|
bool show_magnet = false;
|
|
|
|
bool show_version = false;
|
|
|
|
bool unsorted = false;
|
2022-03-28 04:43:28 +00:00
|
|
|
bool print_header = true;
|
|
|
|
bool print_info = true;
|
|
|
|
bool print_trackers = true;
|
|
|
|
bool print_files = true;
|
|
|
|
bool show_bytesize = false;
|
2021-12-27 00:32:36 +00:00
|
|
|
};
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
int parseCommandLine(app_opts& opts, int argc, char const* const* argv)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
int c;
|
2017-04-20 16:02:19 +00:00
|
|
|
char const* optarg;
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
while ((c = tr_getopt(Usage, argc, argv, std::data(options), &optarg)) != TR_OPT_DONE)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
switch (c)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-03-28 04:43:28 +00:00
|
|
|
case 'b':
|
|
|
|
opts.show_bytesize = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'f':
|
|
|
|
opts.print_header = false;
|
|
|
|
opts.print_info = false;
|
|
|
|
opts.print_trackers = false;
|
|
|
|
opts.print_files = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
opts.print_header = true;
|
|
|
|
opts.print_info = false;
|
|
|
|
opts.print_trackers = false;
|
|
|
|
opts.print_files = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'i':
|
|
|
|
opts.print_header = false;
|
|
|
|
opts.print_info = true;
|
|
|
|
opts.print_trackers = false;
|
|
|
|
opts.print_files = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
opts.print_header = false;
|
|
|
|
opts.print_info = false;
|
|
|
|
opts.print_trackers = true;
|
|
|
|
opts.print_files = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'D':
|
|
|
|
opts.print_header = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'I':
|
|
|
|
opts.print_info = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'T':
|
|
|
|
opts.print_trackers = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F':
|
|
|
|
opts.print_files = false;
|
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'm':
|
2021-12-27 00:32:36 +00:00
|
|
|
opts.show_magnet = true;
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 's':
|
2021-12-27 00:32:36 +00:00
|
|
|
opts.scrape = true;
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2018-11-19 10:35:32 +00:00
|
|
|
case 'u':
|
2021-12-27 00:32:36 +00:00
|
|
|
opts.unsorted = true;
|
2018-11-19 10:35:32 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case 'V':
|
2021-12-27 00:32:36 +00:00
|
|
|
opts.show_version = true;
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
case TR_OPT_UNK:
|
2021-12-27 00:32:36 +00:00
|
|
|
opts.filename = optarg;
|
2012-12-05 17:29:46 +00:00
|
|
|
break;
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
default:
|
2012-12-05 17:29:46 +00:00
|
|
|
return 1;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-04-22 19:56:54 +00:00
|
|
|
[[nodiscard]] auto toString(time_t now)
|
2018-10-06 21:26:16 +00:00
|
|
|
{
|
2022-04-22 19:56:54 +00:00
|
|
|
return now == 0 ? "Unknown" : fmt::format("{:%a %b %d %T %Y}", fmt::localtime(now));
|
2018-10-06 21:26:16 +00:00
|
|
|
}
|
|
|
|
|
2022-05-24 04:05:16 +00:00
|
|
|
bool compareSecondField(std::string_view l, std::string_view r)
|
2022-03-28 04:43:28 +00:00
|
|
|
{
|
2022-05-24 04:05:16 +00:00
|
|
|
auto const lpos = l.find(' ');
|
|
|
|
if (lpos == std::string_view::npos)
|
2022-03-28 04:43:28 +00:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2022-05-24 04:05:16 +00:00
|
|
|
|
|
|
|
auto const rpos = r.find(' ');
|
|
|
|
if (rpos == std::string_view::npos)
|
2022-03-28 04:43:28 +00:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2022-05-24 04:05:16 +00:00
|
|
|
|
|
|
|
return l.substr(lpos) <= r.substr(rpos);
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
void showInfo(app_opts const& opts, tr_torrent_metainfo const& metainfo)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
|
|
|
*** General Info
|
|
|
|
**/
|
2022-03-28 04:43:28 +00:00
|
|
|
if (opts.print_info)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("GENERAL\n\n");
|
|
|
|
fmt::print(" Name: {:s}\n", metainfo.name());
|
2022-07-01 14:49:33 +00:00
|
|
|
if (metainfo.hasV1Metadata())
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" Hash v1: {:s}\n", metainfo.infoHashString());
|
2022-07-01 14:49:33 +00:00
|
|
|
}
|
|
|
|
if (metainfo.hasV2Metadata())
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" Hash v2: {:s}\n", metainfo.infoHash2String());
|
2022-07-01 14:49:33 +00:00
|
|
|
}
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" Created by: {:s}\n", std::empty(metainfo.creator()) ? "Unknown" : metainfo.creator());
|
|
|
|
fmt::print(" Created on: {:s}\n\n", toString(metainfo.dateCreated()));
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
if (!std::empty(metainfo.comment()))
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" Comment: {:s}\n", metainfo.comment());
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
if (!std::empty(metainfo.source()))
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" Source: {:s}\n", metainfo.source());
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" Piece Count: {:d}\n", metainfo.pieceCount());
|
|
|
|
fmt::print(" Piece Size: {:s}\n", tr_formatter_mem_B(metainfo.pieceSize()));
|
|
|
|
fmt::print(" Total Size: {:s}\n", tr_formatter_size_B(metainfo.totalSize()));
|
|
|
|
fmt::print(" Privacy: {:s}\n", metainfo.isPrivate() ? "Private torrent" : "Public torrent");
|
2021-12-25 22:41:18 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
|
|
|
*** Trackers
|
|
|
|
**/
|
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
if (opts.print_trackers)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("\nTRACKERS\n");
|
2022-03-28 04:43:28 +00:00
|
|
|
auto current_tier = std::optional<tr_tracker_tier_t>{};
|
|
|
|
auto print_tier = size_t{ 1 };
|
|
|
|
for (auto const& tracker : metainfo.announceList())
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-03-28 04:43:28 +00:00
|
|
|
if (!current_tier || current_tier != tracker.tier)
|
|
|
|
{
|
|
|
|
current_tier = tracker.tier;
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("\n Tier #{:d}\n", print_tier);
|
2022-03-28 04:43:28 +00:00
|
|
|
++print_tier;
|
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" {:s}\n", tracker.announce.sv());
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
/**
|
2022-09-23 19:36:37 +00:00
|
|
|
***
|
|
|
|
**/
|
2011-01-05 16:15:41 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
if (auto const n_webseeds = metainfo.webseedCount(); n_webseeds > 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("\nWEBSEEDS\n\n");
|
2022-03-28 04:43:28 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < n_webseeds; ++i)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(" {:s}\n", metainfo.webseed(i));
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2011-01-05 16:15:41 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/**
|
|
|
|
*** Files
|
|
|
|
**/
|
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
if (opts.print_files)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-03-28 04:43:28 +00:00
|
|
|
if (!opts.show_bytesize)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("\nFILES\n\n");
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
2019-02-10 11:05:16 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
auto filenames = std::vector<std::string>{};
|
|
|
|
for (tr_file_index_t i = 0, n = metainfo.fileCount(); i < n; ++i)
|
|
|
|
{
|
|
|
|
std::string filename;
|
|
|
|
if (opts.show_bytesize)
|
|
|
|
{
|
|
|
|
filename = std::to_string(metainfo.fileSize(i));
|
|
|
|
filename += " ";
|
|
|
|
filename += metainfo.fileSubpath(i);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
filename = " ";
|
|
|
|
filename += metainfo.fileSubpath(i);
|
|
|
|
filename += " (";
|
|
|
|
filename += tr_formatter_size_B(metainfo.fileSize(i));
|
|
|
|
filename += ')';
|
|
|
|
}
|
|
|
|
filenames.emplace_back(filename);
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2022-03-28 04:43:28 +00:00
|
|
|
if (!opts.unsorted)
|
|
|
|
{
|
|
|
|
if (opts.show_bytesize)
|
|
|
|
{
|
2022-05-24 04:05:16 +00:00
|
|
|
std::sort(std::begin(filenames), std::end(filenames), compareSecondField);
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::sort(std::begin(filenames), std::end(filenames));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto const& filename : filenames)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("{:s}\n", filename);
|
2022-03-28 04:43:28 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
size_t writeFunc(void* ptr, size_t size, size_t nmemb, void* vbuf)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2021-09-21 23:03:39 +00:00
|
|
|
auto* buf = static_cast<evbuffer*>(vbuf);
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const byteCount = size * nmemb;
|
2017-04-19 12:04:45 +00:00
|
|
|
evbuffer_add(buf, ptr, byteCount);
|
|
|
|
return byteCount;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
CURL* tr_curl_easy_init(struct evbuffer* writebuf)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
CURL* curl = curl_easy_init();
|
2022-04-02 14:06:02 +00:00
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_USERAGENT, UserAgent);
|
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFunc);
|
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_WRITEDATA, writebuf);
|
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_VERBOSE, tr_env_key_exists("TR_CURL_VERBOSE"));
|
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_ENCODING, "");
|
2017-04-19 12:04:45 +00:00
|
|
|
return curl;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
void doScrape(tr_torrent_metainfo const& metainfo)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2021-12-27 00:32:36 +00:00
|
|
|
auto* const buf = evbuffer_new();
|
|
|
|
auto* const curl = tr_curl_easy_init(buf);
|
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
for (auto const& tracker : metainfo.announceList())
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-04-16 20:41:51 +00:00
|
|
|
if (std::empty(tracker.scrape))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
// build the full scrape URL
|
2022-08-19 19:33:06 +00:00
|
|
|
auto scrape_url = tr_urlbuf{ tracker.scrape.sv() };
|
|
|
|
auto delimiter = tr_strvContains(scrape_url, '?') ? '&' : '?';
|
|
|
|
scrape_url.append(delimiter, "info_hash=");
|
2022-08-21 13:43:09 +00:00
|
|
|
tr_urlPercentEncode(std::back_inserter(scrape_url), metainfo.infoHash());
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("{:s} ... ", scrape_url);
|
2017-04-19 12:04:45 +00:00
|
|
|
fflush(stdout);
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
// execute the http scrape
|
2022-10-03 00:45:38 +00:00
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_URL, scrape_url.c_str());
|
|
|
|
(void)curl_easy_setopt(curl, CURLOPT_TIMEOUT, TimeoutSecs);
|
2022-01-24 00:53:35 +00:00
|
|
|
if (auto const res = curl_easy_perform(curl); res != CURLE_OK)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("error: {:s}\n", curl_easy_strerror(res));
|
2021-12-27 00:32:36 +00:00
|
|
|
continue;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
2021-12-27 00:32:36 +00:00
|
|
|
|
|
|
|
// check the response code
|
|
|
|
long response;
|
|
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
|
|
|
|
if (response != 200 /*HTTP OK*/)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("error: unexpected response {:d} '{:s}'\n", response, tr_webGetResponseStr(response));
|
2021-12-27 00:32:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
// print it out
|
|
|
|
tr_variant top;
|
2022-01-17 18:39:50 +00:00
|
|
|
auto* const begin = (char const*)evbuffer_pullup(buf, -1);
|
2021-12-27 00:32:36 +00:00
|
|
|
auto sv = std::string_view{ begin, evbuffer_get_length(buf) };
|
|
|
|
if (!tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, sv))
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("error parsing scrape response\n");
|
2021-12-27 00:32:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool matched = false;
|
2021-12-28 07:14:52 +00:00
|
|
|
if (tr_variant* files = nullptr; tr_variantDictFindDict(&top, TR_KEY_files, &files))
|
2021-12-27 00:32:36 +00:00
|
|
|
{
|
|
|
|
size_t child_pos = 0;
|
|
|
|
tr_quark key;
|
|
|
|
tr_variant* val;
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
auto hashsv = std::string_view{ reinterpret_cast<char const*>(std::data(metainfo.infoHash())),
|
|
|
|
std::size(metainfo.infoHash()) };
|
|
|
|
|
2021-12-28 07:14:52 +00:00
|
|
|
while (tr_variantDictChild(files, child_pos, &key, &val))
|
2021-12-27 00:32:36 +00:00
|
|
|
{
|
|
|
|
if (hashsv == tr_quark_get_string_view(key))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-12-27 00:32:36 +00:00
|
|
|
auto i = int64_t{};
|
|
|
|
auto const seeders = tr_variantDictFindInt(val, TR_KEY_complete, &i) ? int(i) : -1;
|
|
|
|
auto const leechers = tr_variantDictFindInt(val, TR_KEY_incomplete, &i) ? int(i) : -1;
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("{:d} seeders, {:d} leechers\n", seeders, leechers);
|
2021-12-27 00:32:36 +00:00
|
|
|
matched = true;
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2021-12-28 07:14:52 +00:00
|
|
|
|
|
|
|
++child_pos;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-18 01:25:42 +00:00
|
|
|
tr_variantClear(&top);
|
2021-12-27 00:32:36 +00:00
|
|
|
|
|
|
|
if (!matched)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("no match\n");
|
2021-12-27 00:32:36 +00:00
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
2021-12-27 00:32:36 +00:00
|
|
|
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
|
evbuffer_free(buf);
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-25 22:41:18 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
int tr_main(int argc, char* argv[])
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
tr_logSetLevel(TR_LOG_ERROR);
|
2021-12-28 17:23:47 +00:00
|
|
|
tr_formatter_mem_init(MemK, MemKStr, MemMStr, MemGStr, MemTStr);
|
|
|
|
tr_formatter_size_init(DiskK, DiskKStr, DiskMStr, DiskGStr, DiskTStr);
|
|
|
|
tr_formatter_speed_init(SpeedK, SpeedKStr, SpeedMStr, SpeedGStr, SpeedTStr);
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
auto opts = app_opts{};
|
|
|
|
if (parseCommandLine(opts, argc, (char const* const*)argv) != 0)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
if (opts.show_version)
|
2010-08-08 22:53:24 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(stderr, "{:s} {:s}\n", MyName, LONG_VERSION_STRING);
|
2017-04-19 12:04:45 +00:00
|
|
|
return EXIT_SUCCESS;
|
2010-08-08 22:53:24 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* make sure the user specified a filename */
|
2021-12-27 00:32:36 +00:00
|
|
|
if (std::empty(opts.filename))
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(stderr, "ERROR: No torrent file specified.\n");
|
2021-12-25 22:41:18 +00:00
|
|
|
tr_getopt_usage(MyName, Usage, std::data(options));
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(stderr, "\n");
|
2017-04-19 12:04:45 +00:00
|
|
|
return EXIT_FAILURE;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2022-03-21 14:15:48 +00:00
|
|
|
/* try to parse the torrent file */
|
2021-12-25 22:41:18 +00:00
|
|
|
auto metainfo = tr_torrent_metainfo{};
|
|
|
|
tr_error* error = nullptr;
|
2021-12-27 00:32:36 +00:00
|
|
|
auto const parsed = metainfo.parseTorrentFile(opts.filename, nullptr, &error);
|
2021-12-25 22:41:18 +00:00
|
|
|
if (error != nullptr)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print(stderr, "Error parsing torrent file '{:s}': {:s} ({:d})\n", opts.filename, error->message, error->code);
|
2021-12-25 22:41:18 +00:00
|
|
|
tr_error_clear(&error);
|
|
|
|
}
|
|
|
|
if (!parsed)
|
2010-06-16 14:27:24 +00:00
|
|
|
{
|
2017-04-19 12:04:45 +00:00
|
|
|
return EXIT_FAILURE;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
if (opts.show_magnet)
|
2012-07-23 15:28:27 +00:00
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("{:s}", metainfo.magnet());
|
2012-07-23 15:28:27 +00:00
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
else
|
2012-07-23 15:28:27 +00:00
|
|
|
{
|
2022-03-28 04:43:28 +00:00
|
|
|
if (opts.print_header)
|
|
|
|
{
|
2022-09-23 19:36:37 +00:00
|
|
|
fmt::print("Name: {:s}\n", metainfo.name());
|
|
|
|
fmt::print("File: {:s}\n", opts.filename);
|
|
|
|
fmt::print("\n");
|
2022-03-28 04:43:28 +00:00
|
|
|
fflush(stdout);
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2021-12-27 00:32:36 +00:00
|
|
|
if (opts.scrape)
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2021-12-25 22:41:18 +00:00
|
|
|
doScrape(metainfo);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-27 00:32:36 +00:00
|
|
|
showInfo(opts, metainfo);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2012-07-23 15:28:27 +00:00
|
|
|
}
|
2010-06-16 14:27:24 +00:00
|
|
|
|
2017-04-19 12:04:45 +00:00
|
|
|
/* cleanup */
|
|
|
|
putc('\n', stdout);
|
|
|
|
return EXIT_SUCCESS;
|
2010-06-16 14:27:24 +00:00
|
|
|
}
|