aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2020-07-15 09:28:51 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-15 09:28:51 +0200
commit73393bfab65515c68159a649c10856659b5ac016 (patch)
tree67cb37484cccc0cf9d3cbe88a58095c2040bd79e /src
parent35aca1f0582c43b5f6818c8fc00b924247e45881 (diff)
Use transmission rpc, fixes rss torrent name
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c5
-rw-r--r--src/buffer.h1
-rw-r--r--src/download.c6
-rw-r--r--src/main.c12
-rw-r--r--src/rss.c22
-rw-r--r--src/rss.h3
-rw-r--r--src/transmission.c208
-rw-r--r--src/transmission.h13
8 files changed, 158 insertions, 112 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 5768b3a..f5e1d65 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -12,6 +12,7 @@ void buffer_init(Buffer *self) {
void buffer_deinit(Buffer *self) {
free(self->data);
+ self->data = NULL;
self->size = 0;
self->capacity = 0;
}
@@ -56,3 +57,7 @@ void* buffer_end(Buffer *self) {
size_t buffer_get_size(Buffer *self, size_t type_size) {
return self->size / type_size;
}
+
+void buffer_clear(Buffer *self) {
+ self->size = 0;
+}
diff --git a/src/buffer.h b/src/buffer.h
index a50cd85..f95e874 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -29,5 +29,6 @@ void* buffer_pop(Buffer *self, size_t size);
void* buffer_begin(Buffer *self);
void* buffer_end(Buffer *self);
size_t buffer_get_size(Buffer *self, size_t type_size);
+void buffer_clear(Buffer *self);
#endif
diff --git a/src/download.c b/src/download.c
index 6c8e33e..74348e5 100644
--- a/src/download.c
+++ b/src/download.c
@@ -12,5 +12,9 @@ int download_to_buffer(const char *url, Buffer *buffer) {
url,
NULL
};
- return program_exec(args, program_buffer_write_callback, buffer);
+ int result = program_exec(args, program_buffer_write_callback, buffer);
+ if(result != 0)
+ return result;
+ buffer_append(buffer, "\0", 1);
+ return result;
}
diff --git a/src/main.c b/src/main.c
index eabd755..bb8114a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -264,7 +264,7 @@ static void automedia_pid_signal_handler(int signum) {
running = 0;
}
-static void sync_tracked_rss(char *rss_config_dir) {
+static void sync_tracked_rss(TransmissionSession *transmission_session, char *rss_config_dir) {
char rss_tracked_dir[PATH_MAX];
strcpy(rss_tracked_dir, rss_config_dir);
strcat(rss_tracked_dir, "/tracked");
@@ -323,7 +323,7 @@ static void sync_tracked_rss(char *rss_config_dir) {
tracked_rss.title = dir->d_name;
tracked_rss.link = link_file_content;
tracked_rss.json_data = json_value_as_object(json_data);
- if(sync_rss(&tracked_rss, rss_config_dir) != 0)
+ if(sync_rss(transmission_session, &tracked_rss, rss_config_dir) != 0)
fprintf(stderr, "Failed to sync %s\n", dir->d_name);
free(link_file_content);
@@ -343,10 +343,16 @@ static void sync_rss_html(char *rss_config_dir, char *html_config_dir, const cha
}
}
+ TransmissionSession transmission_session;
+ if(transmission_connect(&transmission_session) != 0) {
+ fprintf(stderr, "Failed to connect to the transmission daemon!\n");
+ exit(2);
+ }
+
running = 1;
/* running is set to 0 in SIGINT signal handler (ctrl+c) */
while(running) {
- sync_tracked_rss(rss_config_dir);
+ sync_tracked_rss(&transmission_session, rss_config_dir);
if(running)
sleep(sync_rate_sec);
}
diff --git a/src/rss.c b/src/rss.c
index ebbb1cf..005bd03 100644
--- a/src/rss.c
+++ b/src/rss.c
@@ -364,7 +364,7 @@ static int rss_update_latest(char *rss_tracked_dir, TrackedRss *tracked_rss, con
init_json_value_str(&title_json_value, &title_json_value_str);
struct json_string_s filename_json_key;
- create_json_string(&filename_json_key, "title", 5);
+ create_json_string(&filename_json_key, "filename", 8);
struct json_string_s filename_json_value_str;
create_json_string(&filename_json_value_str, filename, strlen(filename));
@@ -473,37 +473,31 @@ static int rss_update_latest(char *rss_tracked_dir, TrackedRss *tracked_rss, con
return result;
}
-static int add_torrents_in_reverse(Buffer *download_items_buffer, TrackedRss *tracked_rss, char *rss_tracked_dir) {
+static int add_torrents_in_reverse(TransmissionSession *transmission_session, Buffer *download_items_buffer, TrackedRss *tracked_rss, char *rss_tracked_dir) {
DownloadItemsData *download_items_it = buffer_end(download_items_buffer);
DownloadItemsData *download_items_end = buffer_begin(download_items_buffer);
download_items_it--;
download_items_end--;
for(; download_items_it != download_items_end; --download_items_it) {
- if(transmission_add_torrent(download_items_it->link) != 0) {
+ char *torrent_name;
+ if(transmission_add_torrent(transmission_session, download_items_it->link, &torrent_name) != 0) {
fprintf(stderr, "Failed to add torrent: %s\n", download_items_it->link);
return 1;
}
- /* TODO: Verify if the last torrent is immediately accessible or if it gets an old torrent... */
- int id;
- float percentage_done;
- char torrent_name[256];
- if(transmission_get_last_added_torrent(&id, &percentage_done, torrent_name) != 0) {
- fprintf(stderr, "Failed to get added torrent name for torrent: %s\n", download_items_it->link);
- return 1;
- }
-
if(rss_update_latest(rss_tracked_dir, tracked_rss, download_items_it->title, download_items_it->link, torrent_name) != 0) {
+ free(torrent_name);
fprintf(stderr, "Failed to update rss tracked data for %s\n", download_items_it->title);
return 1;
}
+ free(torrent_name);
/* Show notification that download has started? */
}
return 0;
}
-int sync_rss(TrackedRss *tracked_rss, char *rss_config_dir) {
+int sync_rss(TransmissionSession *transmission_session, TrackedRss *tracked_rss, char *rss_config_dir) {
/* TODO: This can be cached */
int rss_config_dir_len = strlen(rss_config_dir);
@@ -534,7 +528,7 @@ int sync_rss(TrackedRss *tracked_rss, char *rss_config_dir) {
char *rss_tracked_dir = rss_config_dir;
strcat(rss_tracked_dir, "/tracked/");
- result = add_torrents_in_reverse(&download_items_buffer, tracked_rss, rss_tracked_dir);
+ result = add_torrents_in_reverse(transmission_session, &download_items_buffer, tracked_rss, rss_tracked_dir);
if(result != 0) {
fprintf(stderr, "Failed while adding torrents for url: %s\n", tracked_rss->link);
goto cleanup;
diff --git a/src/rss.h b/src/rss.h
index 0c3175c..d5fa548 100644
--- a/src/rss.h
+++ b/src/rss.h
@@ -2,6 +2,7 @@
#define RSS_H
struct json_object_s;
+struct TransmissionSession;
typedef struct {
char *title;
@@ -10,6 +11,6 @@ typedef struct {
} TrackedRss;
int add_rss(const char *name, const char *url, char *rss_config_dir, const char *start_after);
-int sync_rss(TrackedRss *tracked_rss, char *rss_config_dir);
+int sync_rss(struct TransmissionSession *transmission_session, TrackedRss *tracked_rss, char *rss_config_dir);
#endif
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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+/* 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;
}
diff --git a/src/transmission.h b/src/transmission.h
index 90a3594..cbb6c7c 100644
--- a/src/transmission.h
+++ b/src/transmission.h
@@ -1,15 +1,18 @@
#ifndef TRANSMISSION_H
#define TRANSMISSION_H
-/* @percentage_finished is a value between 0 and 100 [0.0, 100.0] */
-typedef void (*TorrentListCallback)(int id, float percentage_finished, const char *name, void *userdata);
+typedef struct TransmissionSession TransmissionSession;
+struct TransmissionSession {
+ char session_header[128];
+};
+
+int transmission_connect(TransmissionSession *session);
/* Returns 0 if the daemon is running, otherwise returns an error value */
int transmission_is_daemon_running();
int transmission_start_daemon(const char *download_dir);
-int transmission_add_torrent(const char *url);
-int transmission_get_all_torrents(TorrentListCallback callback, void *userdata);
-int transmission_get_last_added_torrent(int *id, float *percentage_finished, char *title);
+/* The torrent name will be stored in @torrent_name, malloc'ed */
+int transmission_add_torrent(TransmissionSession *session, const char *url, char **torrent_name);
#endif