1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2025-01-03 05:25:52 +00:00

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.
This commit is contained in:
Charles Kerr 2022-01-31 14:54:21 -06:00 committed by GitHub
parent 87dfabb9d2
commit 253f4fe009
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 1 additions and 192 deletions

View file

@ -560,7 +560,6 @@ set(NEEDED_FUNCTIONS
htonll htonll
localtime_r localtime_r
localtime_s localtime_s
memmem
mkdtemp mkdtemp
ntohll ntohll
posix_fadvise posix_fadvise

View file

@ -856,6 +856,7 @@
| | | torrent-get | new arg "file-count" | | | torrent-get | new arg "file-count"
| | | torrent-get | new arg "primary-mime-type" | | | torrent-get | new arg "primary-mime-type"
| | | free-space | new return arg "total-capacity" | | | free-space | new return arg "total-capacity"
| | | | undocumented "/upload" endpoint removed
5.1. Upcoming Breakage 5.1. Upcoming Breakage

View file

@ -87,143 +87,6 @@ static void send_simple_response(struct evhttp_request* req, int code, char cons
evbuffer_free(body); 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<tr_mimepart>{};
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); handle_web_client(req, server);
} }
else if (tr_strvStartsWith(location, "upload"sv))
{
handle_upload(req, server);
}
else if (!isHostnameAllowed(server, req)) else if (!isHostnameAllowed(server, req))
{ {
char const* const tmp = char const* const tmp =

View file

@ -3,12 +3,6 @@
// or any future license endorsed by Mnemosyne LLC. // or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder. // 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 <algorithm> // std::sort #include <algorithm> // std::sort
#include <array> // std::array #include <array> // std::array
#include <cctype> /* isdigit() */ #include <cctype> /* isdigit() */
@ -461,37 +455,6 @@ char* tr_strdup(void const* in)
return in == nullptr ? nullptr : tr_strvDup(static_cast<char const*>(in)); return in == nullptr ? nullptr : tr_strvDup(static_cast<char const*>(in));
} }
char const* tr_memmem(char const* haystack, size_t haystacklen, char const* needle, size_t needlelen)
{
#ifdef HAVE_MEMMEM
return static_cast<char const*>(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" extern "C"
{ {
int DoMatch(char const* text, char const* p); int DoMatch(char const* text, char const* p);

View file

@ -276,9 +276,6 @@ char const* tr_strerror(int errnum);
/** @brief Returns true if the string ends with the specified case-insensitive suffix */ /** @brief Returns true if the string ends with the specified case-insensitive suffix */
bool tr_str_has_suffix(char const* str, char const* 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 */ /** @brief Portability wrapper for strcasestr() that uses the system implementation if available */
char const* tr_strcasestr(char const* haystack, char const* needle); char const* tr_strcasestr(char const* haystack, char const* needle);

View file

@ -264,16 +264,6 @@ TEST_F(UtilsTest, trStrlower)
EXPECT_EQ("hello"sv, tr_strlower("hello"sv)); 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) TEST_F(UtilsTest, array)
{ {
auto array = std::array<size_t, 10>{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; auto array = std::array<size_t, 10>{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };