From 73393bfab65515c68159a649c10856659b5ac016 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 15 Jul 2020 09:28:51 +0200 Subject: Use transmission rpc, fixes rss torrent name --- src/transmission.c | 208 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 120 insertions(+), 88 deletions(-) (limited to 'src/transmission.c') diff --git a/src/transmission.c b/src/transmission.c index b9cb123..d789053 100644 --- a/src/transmission.c +++ b/src/transmission.c @@ -1,13 +1,51 @@ #include "transmission.h" #include "program.h" #include "buffer.h" +#include "json.h" +#include "rss_html_common.h" #include #include #include #include +/* TODO: Make port configurable */ + #define NUM_COLUMNS 10 +int transmission_connect(TransmissionSession *session) { + int result = 0; + Buffer buffer; + buffer_init(&buffer); + + const char *args[] = { "curl", "-s", "-I", "http://127.0.0.1:9091/transmission/rpc", NULL }; + result = program_exec(args, program_buffer_write_callback, &buffer); + if(result != 0) { + fprintf(stderr, "Failed to retrieve transmission session id\n"); + goto cleanup; + } + + char *session_id_start = strstr(buffer.data, "X-Transmission-Session-Id: "); + if(!session_id_start) { + fprintf(stderr, "Failed to find session id in transmission response\n"); + result = -1; + goto cleanup; + } + + char *session_id_end = strchr(session_id_start + 27, '\r'); + if(!session_id_start) { + fprintf(stderr, "Failed to find session id in transmission response\n"); + result = -1; + goto cleanup; + } + + memcpy(session->session_header, session_id_start, session_id_end - session_id_start); + session->session_header[session_id_end - session_id_start] = '\0'; + + cleanup: + buffer_deinit(&buffer); + return result; +} + int transmission_is_daemon_running(void) { const char *args[] = { "transmission-remote", "-si", NULL }; return program_exec(args, NULL, NULL); @@ -38,119 +76,113 @@ int transmission_start_daemon(const char *download_dir) { return 0; } -int transmission_add_torrent(const char *url) { - const char *args[] = { "transmission-remote", "-a", "--", url, NULL }; - return program_exec(args, NULL, NULL); +static struct json_object_s* json_object_get_child_object_by_name(struct json_object_s *json_obj, const char *field_name) { + struct json_value_s *json_child_obj = json_object_get_field_by_name(json_obj, field_name); + if(!json_child_obj || json_child_obj->type != json_type_object) + return NULL; + return (struct json_object_s*)json_child_obj->payload; } -int transmission_get_all_torrents(TorrentListCallback callback, void *userdata) { - int result = 0; - - Buffer buffer; - buffer_init(&buffer); - - const char *args[] = { "transmission-remote", "--list", NULL }; - int exec_res = program_exec(args, program_buffer_write_callback, &buffer); - if(exec_res != 0) { - result = exec_res; - goto cleanup; - } - buffer_append(&buffer, "\0", 1); - - char id[6]; - char done[6]; - char have[13]; - char format[7]; - char eta[33]; - char eta2[13]; - char up[11]; - char down[11]; - char ratio[11]; - char status[33]; - char name[256]; - - char *end_of_first_line = strchr(buffer.data, '\n'); - if(!end_of_first_line) - goto cleanup; +static struct json_string_s* json_object_get_child_string_by_name(struct json_object_s *json_obj, const char *field_name) { + struct json_value_s *json_child_str = json_object_get_field_by_name(json_obj, field_name); + if(!json_child_str || json_child_str->type != json_type_string) + return NULL; + return (struct json_string_s*)json_child_str->payload; +} - int num_bytes_read = 0; - size_t offset = end_of_first_line - (char*)buffer.data; - while(offset < buffer.size) { - /* ID, Done, Have (size, format), ETA, Up, Down, Ratio, Status, Name */ - int res = sscanf(buffer.data + offset, "%5s %5s %12s %6s %32s %12s %10s %10s %10s %32s %[^\n] %n", id, done, have, format, eta, eta2, up, down, ratio, status, name, &num_bytes_read); - if(res == EOF || res != NUM_COLUMNS + 1) { - int res = sscanf(buffer.data + offset, "%5s %5s %12s %6s %32s %10s %10s %10s %32s %[^\n] %n", id, done, have, format, eta, up, down, ratio, status, name, &num_bytes_read); - if(res == EOF || res != NUM_COLUMNS) - break; - } - callback(atoi(id), atof(done), name, userdata); - offset += num_bytes_read; +static int transmission_response_is_success(struct json_object_s *json_root) { + struct json_string_s *result_field = json_object_get_child_string_by_name(json_root, "result"); + if(!result_field) { + fprintf(stderr, "Error: Transmission response is missing result field\n"); + return -1; } - cleanup: - buffer_deinit(&buffer); - return result; + return strcmp(result_field->string, "success"); } -static int find_start_of_line(const char *str, int offset) { - for(int i = offset; i >= 0; --i) { - if(*str == '\n') - return i + 1; +static int transmission_add_torrent_response_get_torrent_name(struct json_object_s *json_root, const char **torrent_name) { + struct json_object_s *arguments_obj = json_object_get_child_object_by_name(json_root, "arguments"); + if(!arguments_obj) { + fprintf(stderr, "Error: transmission add torrent response is missing arguments field or its not an object\n"); + return -1; } - return 0; -} -static int find_end_of_previous_line(const char *str, int offset) { - for(int i = offset; i >= 0; --i) { - if(*str == '\n') - return i - 1; + struct json_object_s *torrent_added_obj = json_object_get_child_object_by_name(arguments_obj, "torrent-added"); + if(!torrent_added_obj) + torrent_added_obj = json_object_get_child_object_by_name(arguments_obj, "torrent-duplicate"); + + if(!torrent_added_obj) { + fprintf(stderr, "Error: transmission add torrent response is missing both torrent-added and torrent-duplicate argument\n"); + return -1; } + + struct json_string_s *torrent_name_field = json_object_get_child_string_by_name(torrent_added_obj, "name"); + if(!torrent_name_field) { + fprintf(stderr, "Error: transmission add torrent response is missing torrent name\n"); + return -1; + } + + *torrent_name = torrent_name_field->string; return 0; } -int transmission_get_last_added_torrent(int *id_result, float *percentage_finished_result, char *title_result) { +int transmission_add_torrent(TransmissionSession *session, const char *url, char **torrent_name) { int result = 0; - Buffer buffer; buffer_init(&buffer); - - const char *args[] = { "transmission-remote", "--list", NULL }; - int exec_res = program_exec(args, program_buffer_write_callback, &buffer); - if(exec_res != 0) { - result = exec_res; + struct json_value_s *json_response_val = NULL; + + /* TODO: json escape url */ + char request[4096]; + int written_bytes = snprintf(request, sizeof(request), "{ \"arguments\": { \"filename\": \"%s\" }, \"method\": \"torrent-add\" }", url); + if(written_bytes >= (int)sizeof(request)) { + fprintf(stderr, "Failed to write all bytes to request (this is a bug)\n"); + result = -1; goto cleanup; } - char id[6]; - char done[6]; - char have[13]; - char format[7]; - char eta[33]; - char eta2[13]; - char up[11]; - char down[11]; - char ratio[11]; - char status[33]; - char name[256]; - - int end_of_second_last_line = find_end_of_previous_line(buffer.data, (int)buffer.size - 1); - int start_of_second_last_line = find_start_of_line(buffer.data, end_of_second_last_line); - - /* ID, Done, Have (size, format), ETA, Up, Down, Ratio, Status, Name */ - int res = sscanf(buffer.data + start_of_second_last_line, "%5s %5s %12s %6s %32s %12s %10s %10s %10s %32s %[^\n]", id, done, have, format, eta, eta2, up, down, ratio, status, name); - if(res == EOF || res != NUM_COLUMNS + 1) { - int res = sscanf(buffer.data + start_of_second_last_line, "%5s %5s %12s %6s %32s %10s %10s %10s %32s %[^\n]", id, done, have, format, eta, up, down, ratio, status, name); - if(res == EOF || res != NUM_COLUMNS) { - result = -1; + const char *args[] = { "curl", "-s", "-f", "-d", request, "-H", session->session_header, "--", "http://127.0.0.1:9091/transmission/rpc", NULL }; + result = program_exec(args, program_buffer_write_callback, &buffer); + if(result != 0) { + /* Maybe the request failed because the session is no longer valid? retry once with new session id */ + if(transmission_connect(session) != 0) { + fprintf(stderr, "Failed to add torrent: %s\n", url); goto cleanup; } + + buffer_clear(&buffer); + const char *new_args[] = { "curl", "-s", "-f", "-d", request, "-H", session->session_header, "--", "http://127.0.0.1:9091/transmission/rpc", NULL }; + result = program_exec(new_args, program_buffer_write_callback, &buffer); + if(result != 0) { + fprintf(stderr, "Failed to add torrent: %s\n", url); + goto cleanup; + } + } + + json_response_val = json_parse(buffer.data, buffer.size); + if(!json_response_val || json_response_val->type != json_type_object) { + fprintf(stderr, "Failed to parse torrent add response: %.*s as json\n", (int)buffer.size, (char*)buffer.data); + result = -1; + goto cleanup; } - *id_result = atoi(id); - *percentage_finished_result = atof(done); - strcpy(title_result, name); + struct json_object_s *json_response_obj = json_value_as_object(json_response_val); + + if(transmission_response_is_success(json_response_obj) != 0) { + fprintf(stderr, "Error: transmission torrent add request failed, response: %.*s\n", (int)buffer.size, (char*)buffer.data); + result = -1; + goto cleanup; + } + + const char *response_torrent_name; + result = transmission_add_torrent_response_get_torrent_name(json_response_obj, &response_torrent_name); + if(result != 0) + goto cleanup; + + *torrent_name = strdup(response_torrent_name); cleanup: + free(json_response_val); buffer_deinit(&buffer); return result; } -- cgit v1.2.3