From 253f4fe0096f7bb81d1ee516c7b3aff27ac2c53e Mon Sep 17 00:00:00 2001 From: Charles Kerr Date: Mon, 31 Jan 2022 14:54:21 -0600 Subject: [PATCH] chore: remove /upload endpoint from rpc-server (#2550) It's never been documented or supported and is unused by the web client. In the unlikely case that a 3rd party app is using it, use 'torrent-add' instead; it's documented + supported. --- CMakeLists.txt | 1 - extras/rpc-spec.txt | 1 + libtransmission/rpc-server.cc | 141 ---------------------------- libtransmission/utils.cc | 37 -------- libtransmission/utils.h | 3 - tests/libtransmission/utils-test.cc | 10 -- 6 files changed, 1 insertion(+), 192 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6305f3dd8..066b8b541 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -560,7 +560,6 @@ set(NEEDED_FUNCTIONS htonll localtime_r localtime_s - memmem mkdtemp ntohll posix_fadvise diff --git a/extras/rpc-spec.txt b/extras/rpc-spec.txt index 506ad6124..564d49617 100644 --- a/extras/rpc-spec.txt +++ b/extras/rpc-spec.txt @@ -856,6 +856,7 @@ | | | torrent-get | new arg "file-count" | | | torrent-get | new arg "primary-mime-type" | | | free-space | new return arg "total-capacity" + | | | | undocumented "/upload" endpoint removed 5.1. Upcoming Breakage diff --git a/libtransmission/rpc-server.cc b/libtransmission/rpc-server.cc index 78e2a1a29..70e214184 100644 --- a/libtransmission/rpc-server.cc +++ b/libtransmission/rpc-server.cc @@ -87,143 +87,6 @@ static void send_simple_response(struct evhttp_request* req, int code, char cons evbuffer_free(body); } -struct tr_mimepart -{ - std::string headers; - std::string body; -}; - -static auto extract_parts_from_multipart(struct evkeyvalq const* headers, struct evbuffer* body) -{ - auto ret = std::vector{}; - - auto const* const content_type = evhttp_find_header(headers, "Content-Type"); - auto const* in = (char const*)evbuffer_pullup(body, -1); - size_t inlen = evbuffer_get_length(body); - - char const* boundary_key = "boundary="; - char const* boundary_key_begin = content_type != nullptr ? strstr(content_type, boundary_key) : nullptr; - char const* boundary_val = boundary_key_begin != nullptr ? boundary_key_begin + strlen(boundary_key) : "arglebargle"; - char* boundary = tr_strdup_printf("--%s", boundary_val); - size_t const boundary_len = strlen(boundary); - - char const* delim = tr_memmem(in, inlen, boundary, boundary_len); - - while (delim != nullptr) - { - char const* part = delim + boundary_len; - - inlen -= part - in; - in = part; - - delim = tr_memmem(in, inlen, boundary, boundary_len); - size_t part_len = delim != nullptr ? (size_t)(delim - part) : inlen; - - if (part_len != 0) - { - char const* rnrn = tr_memmem(part, part_len, "\r\n\r\n", 4); - - if (rnrn != nullptr) - { - auto tmp = tr_mimepart{}; - tmp.headers.assign(part, rnrn - part); - tmp.body.assign(rnrn + 4, (part + part_len) - (rnrn + 4)); - ret.push_back(tmp); - } - } - } - - tr_free(boundary); - - return ret; -} - -static void handle_upload(struct evhttp_request* req, tr_rpc_server* server) -{ - if (req->type != EVHTTP_REQ_POST) - { - send_simple_response(req, 405, nullptr); - } - else - { - bool hasSessionId = false; - - char const* query = strchr(req->uri, '?'); - bool const paused = query != nullptr && strstr(query + 1, "paused=true") != nullptr; - - auto const parts = extract_parts_from_multipart(req->input_headers, req->input_buffer); - - /* first look for the session id */ - for (auto const& p : parts) - { - if (tr_strcasestr(p.headers.c_str(), TR_RPC_SESSION_ID_HEADER) != nullptr) - { - char const* ours = get_current_session_id(server); - size_t const ourlen = strlen(ours); - hasSessionId = ourlen <= std::size(p.body) && memcmp(p.body.c_str(), ours, ourlen) == 0; - break; - } - } - - if (!hasSessionId) - { - int code = 409; - char const* codetext = tr_webGetResponseStr(code); - struct evbuffer* body = evbuffer_new(); - evbuffer_add_printf(body, "%s", "{ \"success\": false, \"msg\": \"Bad Session-Id\" }"); - evhttp_send_reply(req, code, codetext, body); - evbuffer_free(body); - } - else - { - for (auto const& p : parts) - { - auto body = std::string_view{ p.body }; - if (tr_strvEndsWith(body, "\r\n"sv)) - { - body.remove_suffix(2); - } - - auto top = tr_variant{}; - tr_variantInitDict(&top, 2); - tr_variantDictAddStrView(&top, TR_KEY_method, "torrent-add"); - auto* const args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2); - tr_variantDictAddBool(args, TR_KEY_paused, paused); - - auto test = tr_variant{}; - auto have_source = bool{ false }; - if (tr_urlIsValid(body)) - { - tr_variantDictAddStrView(args, TR_KEY_filename, body); - have_source = true; - } - else if (tr_variantFromBuf(&test, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, body)) - { - tr_variantDictAddStrView(args, TR_KEY_metainfo, tr_base64_encode(body)); - have_source = true; - } - - if (have_source) - { - tr_rpc_request_exec_json(server->session, &top, nullptr, nullptr); - } - - tr_variantFree(&top); - } - } - - /* send "success" response */ - { - int code = HTTP_OK; - char const* codetext = tr_webGetResponseStr(code); - struct evbuffer* body = evbuffer_new(); - evbuffer_add_printf(body, "%s", "{ \"success\": true, \"msg\": \"Torrent Added\" }"); - evhttp_send_reply(req, code, codetext, body); - evbuffer_free(body); - } - } -} - /*** **** ***/ @@ -628,10 +491,6 @@ static void handle_request(struct evhttp_request* req, void* arg) { handle_web_client(req, server); } - else if (tr_strvStartsWith(location, "upload"sv)) - { - handle_upload(req, server); - } else if (!isHostnameAllowed(server, req)) { char const* const tmp = diff --git a/libtransmission/utils.cc b/libtransmission/utils.cc index bc25ebc7f..fa141600a 100644 --- a/libtransmission/utils.cc +++ b/libtransmission/utils.cc @@ -3,12 +3,6 @@ // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. -#ifdef HAVE_MEMMEM -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* glibc's string.h needs this to pick up memmem */ -#endif -#endif - #include // std::sort #include // std::array #include /* isdigit() */ @@ -461,37 +455,6 @@ char* tr_strdup(void const* in) return in == nullptr ? nullptr : tr_strvDup(static_cast(in)); } -char const* tr_memmem(char const* haystack, size_t haystacklen, char const* needle, size_t needlelen) -{ -#ifdef HAVE_MEMMEM - - return static_cast(memmem(haystack, haystacklen, needle, needlelen)); - -#else - - if (needlelen == 0) - { - return haystack; - } - - if (needlelen > haystacklen || haystack == nullptr || needle == nullptr) - { - return nullptr; - } - - for (size_t i = 0; i <= haystacklen - needlelen; ++i) - { - if (memcmp(haystack + i, needle, needlelen) == 0) - { - return haystack + i; - } - } - - return nullptr; - -#endif -} - extern "C" { int DoMatch(char const* text, char const* p); diff --git a/libtransmission/utils.h b/libtransmission/utils.h index 7af4a69e5..ad5d14ed3 100644 --- a/libtransmission/utils.h +++ b/libtransmission/utils.h @@ -276,9 +276,6 @@ char const* tr_strerror(int errnum); /** @brief Returns true if the string ends with the specified case-insensitive suffix */ bool tr_str_has_suffix(char const* str, char const* suffix); -/** @brief Portability wrapper for memmem() that uses the system implementation if available */ -char const* tr_memmem(char const* haystack, size_t haystack_len, char const* needle, size_t needle_len); - /** @brief Portability wrapper for strcasestr() that uses the system implementation if available */ char const* tr_strcasestr(char const* haystack, char const* needle); diff --git a/tests/libtransmission/utils-test.cc b/tests/libtransmission/utils-test.cc index c61e30b83..b3f3179b5 100644 --- a/tests/libtransmission/utils-test.cc +++ b/tests/libtransmission/utils-test.cc @@ -264,16 +264,6 @@ TEST_F(UtilsTest, trStrlower) EXPECT_EQ("hello"sv, tr_strlower("hello"sv)); } -TEST_F(UtilsTest, trMemmem) -{ - auto const haystack = std::string{ "abcabcabcabc" }; - auto const needle = std::string{ "cab" }; - - EXPECT_EQ(haystack, tr_memmem(haystack.data(), haystack.size(), haystack.data(), haystack.size())); - EXPECT_EQ(haystack.substr(2), tr_memmem(haystack.data(), haystack.size(), needle.data(), needle.size())); - EXPECT_EQ(nullptr, tr_memmem(needle.data(), needle.size(), haystack.data(), haystack.size())); -} - TEST_F(UtilsTest, array) { auto array = std::array{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };