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:
parent
87dfabb9d2
commit
253f4fe009
6 changed files with 1 additions and 192 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 };
|
||||||
|
|
Loading…
Reference in a new issue