/* Copyright (C) 2025 awy sttorrent is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. sttorrent is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sttorrent. If not, see . */ #include #include #include #include #include "cjson/cJSON.h" struct MemoryStruct { char *memory; size_t size; }; static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct MemoryStruct *mem = (struct MemoryStruct *)userp; mem->memory = realloc(mem->memory, mem->size + realsize + 1); if(mem->memory == NULL) { /* out of memory! */ puts("not enough memory (realloc returned NULL)"); return 0; } memcpy(&(mem->memory[mem->size]), contents, realsize); mem->size += realsize; mem->memory[mem->size] = 0; return realsize; } int get_session_id(char id[75]) { CURL* handle; CURLcode res; struct MemoryStruct chunk; chunk.memory = malloc(1); chunk.size = 0; handle = curl_easy_init(); if (!handle) { return 1; } curl_easy_setopt(handle, CURLOPT_URL, "http://localhost:9091/transmission/rpc"); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&chunk); curl_easy_setopt(handle, CURLOPT_HEADERDATA, (void *)&chunk); res = curl_easy_perform(handle); if (res != CURLE_OK) { return 1; } int nl = 0; int idx = 0; char buff[128] = {'\0'}; for (size_t i = 0; i < chunk.size; i++) { if (nl > 3) { break; } if (chunk.memory[i] == '\n') { nl++; } if (nl == 3) { buff[idx] = chunk.memory[i+1]; idx++; } } /* cleanup \r\n garbage at the end of the buff*/ buff[strcspn(buff, "\r\n")] = 0; strcpy(id, buff); curl_easy_cleanup(handle); if (chunk.memory) free(chunk.memory); return 0; } int main(void) { char id[75]; CURL* handle; CURLcode res; cJSON *root, *arguments, *torrents; int status_count[7] = {0}; struct MemoryStruct chunk; struct curl_slist *headers = NULL; const char *json = "{\"method\":\"torrent-get\",\"arguments\":{\"fields\":[\"status\", \"percentDone\"]}}"; curl_global_init(CURL_GLOBAL_ALL); /* To get status from transmission we need to send two CURL requests, first one to obtain session id. */ get_session_id(id); chunk.memory = malloc(1); chunk.size = 0; handle = curl_easy_init(); if (!handle) { return 1; } curl_easy_setopt(handle, CURLOPT_URL, "localhost:9091/transmission/rpc"); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, (void *)&chunk); headers = curl_slist_append(headers, "Content-Type: application/json"); headers = curl_slist_append(headers, "Accept: application/json"); headers = curl_slist_append(headers, id); curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(handle, CURLOPT_POST, 1L); curl_easy_setopt(handle, CURLOPT_POSTFIELDS, json); res = curl_easy_perform(handle); if (res != CURLE_OK) { return 1; } /* JSON parsing */ root = cJSON_Parse(chunk.memory); /* Cleanup */ curl_easy_cleanup(handle); if (chunk.memory) free(chunk.memory); curl_global_cleanup(); arguments = cJSON_GetObjectItem(root, "arguments"); torrents = cJSON_GetObjectItem(arguments, "torrents"); if (cJSON_GetArraySize(torrents) == 0) { cJSON_Delete(root); return 0; } /* Counting statuses */ for (int i = 0; i < cJSON_GetArraySize(torrents); i++) { cJSON *val = cJSON_GetArrayItem(torrents, i); cJSON *status = cJSON_GetObjectItem(val, "status"); if (status) { int s = status->valueint; status_count[s]++; } } for (int i = 0; i < 7; i++) { if (status_count[i] == 0) { continue; } switch (i) { case 0: printf(" "); break; case 3: printf("󱕱  "); break; case 4: printf(" "); break; case 5: printf("󱕱  "); break; case 6: printf(" "); break; default: break; } printf("%d", status_count[i]); } cJSON_Delete(root); return 0; }