diff --git a/gtk/MakeDialog.cc b/gtk/MakeDialog.cc index 3bd767042..0b926074b 100644 --- a/gtk/MakeDialog.cc +++ b/gtk/MakeDialog.cc @@ -284,7 +284,7 @@ void MakeDialog::Impl::onResponse(int response) else { announce_urls.push_front(str); - trackers.push_back(tr_tracker_info{ tier, announce_urls.front().data(), nullptr, 0 }); + trackers.push_back(tr_tracker_info{ tier, announce_urls.front().data() }); } } @@ -295,6 +295,8 @@ void MakeDialog::Impl::onResponse(int response) target.c_str(), trackers.data(), trackers.size(), + nullptr, + 0, useComment ? comment.c_str() : nullptr, isPrivate, useSource ? source.c_str() : nullptr); diff --git a/libtransmission/makemeta.cc b/libtransmission/makemeta.cc index 21d36b67d..788f66e12 100644 --- a/libtransmission/makemeta.cc +++ b/libtransmission/makemeta.cc @@ -234,6 +234,11 @@ void tr_metaInfoBuilderFree(tr_metainfo_builder* builder) tr_free(builder->trackers[i].announce); } + for (int i = 0; i < builder->webseedCount; ++i) + { + tr_free(builder->webseeds[i]); + } + tr_free(builder->trackers); tr_free(builder->outputFile); tr_free(builder); @@ -420,7 +425,6 @@ static void tr_realMakeMetaInfo(tr_metainfo_builder* builder) { tr_variant top; - /* allow an empty set, but if URLs *are* listed, verify them. #814, #971 */ for (int i = 0; i < builder->trackerCount && builder->result == TrMakemetaResult::OK; ++i) { if (!tr_urlIsValidTracker(builder->trackers[i].announce)) @@ -430,6 +434,15 @@ static void tr_realMakeMetaInfo(tr_metainfo_builder* builder) } } + for (int i = 0; i < builder->webseedCount && builder->result == TrMakemetaResult::OK; ++i) + { + if (!tr_urlIsValid(builder->webseeds[i])) + { + tr_strlcpy(builder->errfile, builder->webseeds[i], sizeof(builder->errfile)); + builder->result = TrMakemetaResult::ERR_URL; + } + } + tr_variantInitDict(&top, 6); if (builder->fileCount == 0 || builder->totalSize == 0 || builder->pieceSize == 0 || builder->pieceCount == 0) @@ -464,6 +477,16 @@ static void tr_realMakeMetaInfo(tr_metainfo_builder* builder) tr_variantDictAddStr(&top, TR_KEY_announce, builder->trackers[0].announce); } + if (builder->result == TrMakemetaResult::OK && builder->webseedCount > 0) + { + tr_variant* url_list = tr_variantDictAddList(&top, TR_KEY_url_list, builder->webseedCount); + + for (int i = 0; i < builder->webseedCount; ++i) + { + tr_variantListAddStr(url_list, builder->webseeds[i]); + } + } + if (builder->result == TrMakemetaResult::OK && !builder->abortFlag) { if (!tr_str_is_empty(builder->comment)) @@ -543,6 +566,8 @@ void tr_makeMetaInfo( char const* outputFile, tr_tracker_info const* trackers, int trackerCount, + char const** webseeds, + int webseedCount, char const* comment, bool isPrivate, char const* source) @@ -572,6 +597,13 @@ void tr_makeMetaInfo( builder->trackers[i].announce = tr_strdup(trackers[i].announce); } + builder->webseedCount = webseedCount; + builder->webseeds = tr_new0(char*, webseedCount); + for (int i = 0; i < webseedCount; ++i) + { + builder->webseeds[i] = tr_strdup(webseeds[i]); + } + builder->comment = tr_strdup(comment); builder->isPrivate = isPrivate; builder->source = tr_strdup(source); diff --git a/libtransmission/makemeta.h b/libtransmission/makemeta.h index eafc0c6dc..68858d828 100644 --- a/libtransmission/makemeta.h +++ b/libtransmission/makemeta.h @@ -26,8 +26,6 @@ struct tr_tracker_info { int tier; char* announce; - char* scrape; - uint32_t id; /* unique identifier used to match to a tr_tracker_stat */ }; struct tr_metainfo_builder @@ -53,6 +51,10 @@ struct tr_metainfo_builder tr_tracker_info* trackers; int trackerCount; + + char** webseeds; + int webseedCount; + char* comment; char* outputFile; bool isPrivate; @@ -116,9 +118,11 @@ void tr_metaInfoBuilderFree(tr_metainfo_builder*); */ void tr_makeMetaInfo( tr_metainfo_builder* builder, - char const* outputFile, + char const* output_file, tr_tracker_info const* trackers, - int trackerCount, + int n_trackers, + char const** webseeds, + int n_webseeds, char const* comment, - bool isPrivate, + bool is_private, char const* source); diff --git a/macosx/CreatorWindowController.mm b/macosx/CreatorWindowController.mm index 4504a8a15..8a3822a37 100644 --- a/macosx/CreatorWindowController.mm +++ b/macosx/CreatorWindowController.mm @@ -569,6 +569,8 @@ NSMutableSet* creatorWindowControllerSet = nil; fLocation.path.UTF8String, trackerInfo, fTrackers.count, + nullptr, + 0, fCommentView.string.UTF8String, fPrivateCheck.state == NSControlStateValueOn, fSource.stringValue.UTF8String); diff --git a/qt/MakeDialog.cc b/qt/MakeDialog.cc index 298a5d06b..17a06952f 100644 --- a/qt/MakeDialog.cc +++ b/qt/MakeDialog.cc @@ -189,6 +189,8 @@ void MakeDialog::makeTorrent() target.toUtf8().constData(), trackers.empty() ? nullptr : trackers.data(), trackers.size(), + nullptr, + 0, comment.isEmpty() ? nullptr : comment.toUtf8().constData(), ui_.privateCheck->isChecked(), source.isNull() ? nullptr : source.toUtf8().constData()); diff --git a/tests/libtransmission/makemeta-test.cc b/tests/libtransmission/makemeta-test.cc index 03832e705..a985f31b7 100644 --- a/tests/libtransmission/makemeta-test.cc +++ b/tests/libtransmission/makemeta-test.cc @@ -34,6 +34,8 @@ protected: tr_torrent_metainfo& metainfo, tr_tracker_info const* trackers, int const trackerCount, + char const** webseeds, + int const webseedCount, void const* payload, size_t const payloadSize, char const* comment, @@ -56,7 +58,16 @@ protected: // have tr_makeMetaInfo() build the .torrent file auto const torrent_file = tr_strvJoin(input_file, ".torrent"); - tr_makeMetaInfo(builder, torrent_file.c_str(), trackers, trackerCount, comment, isPrivate, std::string(source).c_str()); + tr_makeMetaInfo( + builder, + torrent_file.c_str(), + trackers, + trackerCount, + webseeds, + webseedCount, + comment, + isPrivate, + std::string(source).c_str()); EXPECT_EQ(isPrivate, builder->isPrivate); EXPECT_EQ(torrent_file, builder->outputFile); EXPECT_STREQ(comment, builder->comment); @@ -78,6 +89,7 @@ protected: EXPECT_EQ(comment, metainfo.comment()); EXPECT_EQ(isPrivate, metainfo.isPrivate()); EXPECT_EQ(size_t(trackerCount), std::size(metainfo.announceList())); + EXPECT_EQ(size_t(webseedCount), metainfo.webseedCount()); EXPECT_EQ(tr_file_index_t{ 1 }, metainfo.fileCount()); EXPECT_EQ(makeString(tr_sys_path_basename(input_file.data(), nullptr)), metainfo.fileSubpath(0)); EXPECT_EQ(payloadSize, metainfo.fileSize(0)); @@ -89,6 +101,8 @@ protected: void testSingleDirectoryImpl( tr_tracker_info const* trackers, int const tracker_count, + char const** webseeds, + int const webseed_count, void const** payloads, size_t const* payload_sizes, size_t const payload_count, @@ -135,7 +149,16 @@ protected: // build the .torrent file auto torrent_file = tr_strvJoin(top, ".torrent"sv); - tr_makeMetaInfo(builder, torrent_file.c_str(), trackers, tracker_count, comment, is_private, source); + tr_makeMetaInfo( + builder, + torrent_file.c_str(), + trackers, + tracker_count, + webseeds, + webseed_count, + comment, + is_private, + source); EXPECT_EQ(is_private, builder->isPrivate); EXPECT_EQ(torrent_file, builder->outputFile); EXPECT_STREQ(comment, builder->comment); @@ -170,6 +193,8 @@ protected: void testSingleDirectoryRandomPayloadImpl( tr_tracker_info const* trackers, int const tracker_count, + char const** webseeds, + int const webseed_count, size_t const max_file_count, size_t const max_file_size, char const* comment, @@ -193,6 +218,8 @@ protected: testSingleDirectoryImpl( trackers, tracker_count, + webseeds, + webseed_count, const_cast(payloads), payload_sizes, payload_count, @@ -229,6 +256,32 @@ TEST_F(MakemetaTest, singleFile) metainfo, trackers.data(), tracker_count, + nullptr, + 0, + payload.data(), + payload.size(), + comment, + is_private, + "TESTME"sv); +} + +TEST_F(MakemetaTest, webseed) +{ + auto trackers = std::vector{}; + auto webseeds = std::vector{}; + + webseeds.emplace_back("https://www.example.com/linux.iso"); + + auto const payload = std::string{ "Hello, World!\n" }; + char const* const comment = "This is the comment"; + bool const is_private = false; + auto metainfo = tr_torrent_metainfo{}; + testSingleFileImpl( + metainfo, + std::data(trackers), + std::size(trackers), + std::data(webseeds), + std::size(webseeds), payload.data(), payload.size(), comment, @@ -255,6 +308,8 @@ TEST_F(MakemetaTest, singleFileDifferentSourceFlags) metainfo_foobar, trackers.data(), tracker_count, + nullptr, + 0, payload.data(), payload.size(), comment, @@ -266,6 +321,8 @@ TEST_F(MakemetaTest, singleFileDifferentSourceFlags) metainfo_testme, trackers.data(), tracker_count, + nullptr, + 0, payload.data(), payload.size(), comment, @@ -297,6 +354,8 @@ TEST_F(MakemetaTest, singleDirectoryRandomPayload) testSingleDirectoryRandomPayloadImpl( trackers.data(), tracker_count, + nullptr, + 0, DefaultMaxFileCount, DefaultMaxFileSize, comment, diff --git a/utils/create.cc b/utils/create.cc index 284b3f3b6..7c9ae5b2b 100644 --- a/utils/create.cc +++ b/utils/create.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -30,13 +31,14 @@ char constexpr Usage[] = "Usage: transmission-create [options] " uint32_t constexpr KiB = 1024; -auto constexpr Options = std::array{ +auto constexpr Options = std::array{ { { 'p', "private", "Allow this torrent to only be used with the specified tracker(s)", "p", false, nullptr }, { 'r', "source", "Set the source for private trackers", "r", true, "" }, { 'o', "outfile", "Save the generated .torrent to this filename", "o", true, "" }, { 's', "piecesize", "Set the piece size in KiB, overriding the preferred default", "s", true, "" }, { 'c', "comment", "Add a comment", "c", true, "" }, { 't', "tracker", "Add a tracker's announce URL", "t", true, "" }, + { 'w', "webseed", "Add a webseed URL", "w", true, "" }, { 'V', "version", "Show version number and exit", "V", false, nullptr }, { 0, nullptr, nullptr, nullptr, false, nullptr } } }; @@ -44,6 +46,7 @@ auto constexpr Options = std::array{ struct app_options { std::vector trackers; + std::vector webseeds; std::string outfile; char const* comment = nullptr; char const* infile = nullptr; @@ -79,7 +82,11 @@ int parseCommandLine(app_options& options, int argc, char const* const* argv) break; case 't': - options.trackers.push_back(tr_tracker_info{ 0, const_cast(optarg), nullptr, 0 }); + options.trackers.push_back(tr_tracker_info{ 0, const_cast(optarg) }); + break; + + case 'w': + options.webseeds.push_back(optarg); break; case 's': @@ -220,6 +227,8 @@ int tr_main(int argc, char* argv[]) options.outfile.c_str(), std::data(options.trackers), std::size(options.trackers), + std::data(options.webseeds), + std::size(options.webseeds), options.comment, options.is_private, options.source);