diff --git a/src/libopensubsonic/endpoint_ping.c b/src/libopensubsonic/endpoint_ping.c index e1e762f..29740ec 100644 --- a/src/libopensubsonic/endpoint_ping.c +++ b/src/libopensubsonic/endpoint_ping.c @@ -1,47 +1,67 @@ +/* + * OpenSubsonicPlayer + * Goldenkrew3000 / Hojuix 2026 + * License: GNU General Public License 3.0 + * Info: OpenSubsonic /ping endpoint parser + */ + #include #include #include #include "../external/cJSON.h" -#include "logger.h" #include "utils.h" #include "endpoint_ping.h" -// Parse the JSON returned from the /rest/ping endpoint -// Returns 1 if failure occured, else 0 -int opensubsonic_ping_parse(char* data, opensubsonic_ping_struct** pingStruct) { - // Allocate on the heap - *pingStruct = malloc(sizeof(opensubsonic_ping_struct)); - - // Initialize struct variables - (*pingStruct)->status = NULL; - (*pingStruct)->version = NULL; - (*pingStruct)->serverType = NULL; - (*pingStruct)->serverVersion = NULL; - (*pingStruct)->openSubsonicCapable = false; - (*pingStruct)->error = false; - (*pingStruct)->errorCode = 0; - (*pingStruct)->errorMessage = NULL; +OSSP_endpoint_ping_t* OSSP_endpoint_ping_Constructor() { + printf("[LibOpenSubsonic] Running /ping Constructor.\n"); + OSSP_endpoint_ping_t* obj = malloc(sizeof(OSSP_endpoint_ping_t)); + if (obj == NULL) { + return NULL; + } + obj->status = NULL; + obj->version = NULL; + obj->serverType = NULL; + obj->serverVersion = NULL; + obj->openSubsonicCapable = false; + obj->error = false; + obj->errorCode = 0; + obj->errorMessage = NULL; + return obj; +} + +void OSSP_endpoint_ping_Deconstructor(OSSP_endpoint_ping_t* obj) { + printf("[LibOpenSubsonic] Running /ping Deconstructor.\n"); + if (obj->status != NULL) { free(obj->status); } + if (obj->version != NULL) { free(obj->version); } + if (obj->serverType != NULL) { free(obj->serverType); } + if (obj->serverVersion != NULL) { free(obj->serverVersion); } + if (obj->errorMessage != NULL) { free(obj->errorMessage); } + if (obj != NULL) { free(obj); } +} + +int OSSP_endpoint_ping_Parse(OSSP_httpCli_UrlObj_t* obj) { + OSSP_endpoint_ping_t* structObj = obj->returnStruct; // Parse the JSON - cJSON* root = cJSON_Parse(data); + cJSON* root = cJSON_Parse(obj->resBody); if (root == NULL) { - logger_log_error(__func__, "Error parsing JSON."); + printf("[LibOpenSubsonic] (%s) Error parsing JSON.\n", __func__); return 1; } // Make an object from subsonic-response cJSON* subsonic_root = cJSON_GetObjectItemCaseSensitive(root, "subsonic-response"); if (subsonic_root == NULL) { - logger_log_error(__func__, "Error handling JSON - subsonic-response does not exist."); + printf("[LibOpenSubsonic] (%s) Error handling JSON - subsonic-response does not exist.\n", __func__); cJSON_Delete(root); return 1; } - OSS_Psoj(&(*pingStruct)->status, subsonic_root, "status"); - OSS_Psoj(&(*pingStruct)->version, subsonic_root, "version"); - OSS_Psoj(&(*pingStruct)->serverType, subsonic_root, "type"); - OSS_Psoj(&(*pingStruct)->serverVersion, subsonic_root, "serverVersion"); - OSS_Pboj(&(*pingStruct)->openSubsonicCapable, subsonic_root, "openSubsonic"); + OSS_Psoj(&structObj->status, subsonic_root, "status"); + OSS_Psoj(&structObj->version, subsonic_root, "version"); + OSS_Psoj(&structObj->serverType, subsonic_root, "type"); + OSS_Psoj(&structObj->serverVersion, subsonic_root, "serverVersion"); + OSS_Pboj(&structObj->openSubsonicCapable, subsonic_root, "openSubsonic"); // Check if an error is present cJSON* subsonic_error = cJSON_GetObjectItemCaseSensitive(subsonic_root, "error"); @@ -50,23 +70,13 @@ int opensubsonic_ping_parse(char* data, opensubsonic_ping_struct** pingStruct) { cJSON_Delete(root); return 0; } - (*pingStruct)->error = true; + structObj->error = true; // From this point on, error has occured, capture error information - OSS_Pioj(&(*pingStruct)->errorCode, subsonic_error, "code"); - OSS_Psoj(&(*pingStruct)->errorMessage, subsonic_error, "message"); - logger_log_error(__func__, "Error noted in JSON - Code %d: %s", (*pingStruct)->errorCode, (*pingStruct)->errorMessage); + OSS_Pioj(&structObj->errorCode, subsonic_error, "code"); + OSS_Psoj(&structObj->errorMessage, subsonic_error, "message"); + printf("[LibOpenSubsonic] (%s) Error noted in JSON - Code %d: %s\n", __func__, structObj->errorCode, structObj->errorMessage); cJSON_Delete(root); return 1; } - -void opensubsonic_ping_struct_free(opensubsonic_ping_struct** pingStruct) { - logger_log_general(__func__, "Freeing /ping endpoint heap objects."); - if ((*pingStruct)->status != NULL) { free((*pingStruct)->status); } - if ((*pingStruct)->version != NULL) { free((*pingStruct)->version); } - if ((*pingStruct)->serverType != NULL) { free((*pingStruct)->serverType); } - if ((*pingStruct)->serverVersion != NULL) { free((*pingStruct)->serverVersion); } - if ((*pingStruct)->errorMessage != NULL) { free((*pingStruct)->errorMessage); } - if (*pingStruct != NULL) { free(*pingStruct); } -} diff --git a/src/libopensubsonic/endpoint_ping.h b/src/libopensubsonic/endpoint_ping.h index 48b781f..ab209ea 100644 --- a/src/libopensubsonic/endpoint_ping.h +++ b/src/libopensubsonic/endpoint_ping.h @@ -1,7 +1,15 @@ +/* + * OpenSubsonicPlayer + * Goldenkrew3000 / Hojuix 2026 + * License: GNU General Public License 3.0 + * Info: OpenSubsonic /ping endpoint parser + */ + #ifndef _ENDPOINT_PING_H #define _ENDPOINT_PING_H #include #include +#include "httpclient.h" #ifdef __cplusplus extern "C" { @@ -16,10 +24,11 @@ typedef struct { bool error; int errorCode; char* errorMessage; -} opensubsonic_ping_struct; +} OSSP_endpoint_ping_t; -int opensubsonic_ping_parse(char* data, opensubsonic_ping_struct** pingStruct); -void opensubsonic_ping_struct_free(opensubsonic_ping_struct** pingStruct); +OSSP_endpoint_ping_t* OSSP_endpoint_ping_Constructor(); +void OSSP_endpoint_ping_Deconstructor(OSSP_endpoint_ping_t* obj); +int OSSP_endpoint_ping_Parse(OSSP_httpCli_UrlObj_t* obj); #ifdef __cplusplus } diff --git a/src/libopensubsonic/httpclient.c b/src/libopensubsonic/httpclient.c index 0d46d28..6cb83dc 100644 --- a/src/libopensubsonic/httpclient.c +++ b/src/libopensubsonic/httpclient.c @@ -4,9 +4,7 @@ #include #include "../external/cJSON.h" #include "httpclient.h" -#include "logger.h" #include "../configHandler.h" -#include "logger.h" #include "endpoint_ping.h" #include "endpoint_getStarred.h" @@ -24,295 +22,93 @@ static int rc = 0; extern configHandler_config_t* configObj; -void opensubsonic_httpClient_URL_prepare(opensubsonic_httpClient_URL_t** urlObj) { - // Initialize struct variables - (*urlObj)->endpoint = 0; - (*urlObj)->id = NULL; - (*urlObj)->type = 0; - (*urlObj)->amount = 0; - (*urlObj)->submit = false; - (*urlObj)->formedUrl = NULL; +/* + * URL Constructor/Deconstructor + */ +OSSP_httpCli_UrlObj_t* OSSP_httpCli_UrlObj_Constructor() { + OSSP_httpCli_UrlObj_t* obj = malloc(sizeof(OSSP_httpCli_UrlObj_t)); + if (obj == NULL) { + return NULL; + } + obj->endpoint = 0; + obj->id = NULL; + obj->type = 0; + obj->amount = 0; + obj->submit = false; + obj->url = NULL; + obj->reqBody = NULL; + obj->httpMethod = 0; + obj->isBodyReq = false; + obj->resCode = 0; + obj->resBody = NULL; + obj->returnStruct = NULL; + return obj; } -void opensubsonic_httpClient_URL_cleanup(opensubsonic_httpClient_URL_t** urlObj) { - logger_log_general(__func__, "Freeing URL object with endpoint ID of %d.", (*urlObj)->endpoint); - if ((*urlObj)->formedUrl != NULL) { free((*urlObj)->formedUrl); } - if ((*urlObj)->id != NULL) { free((*urlObj)->id); } - if (*urlObj != NULL) { free(*urlObj); } +void OSSP_httpCli_UrlObj_Deconstructor(OSSP_httpCli_UrlObj_t* obj) { + //logger_log_general(__func__, "Freeing URL object with endpoint ID of %d.", obj->endpoint); + printf("hjaha %s\n", obj->url); + if (obj->url != NULL) { free(obj->url); } + if (obj->id != NULL) { free(obj->id); } + if (obj->reqBody != NULL) { free(obj->reqBody); } + if (obj->resBody != NULL) { free(obj->resBody); } + if (obj != NULL) { free(obj); } } -void opensubsonic_httpClient_formUrl(opensubsonic_httpClient_URL_t** urlObj) { - // TODO fix hack, add error checking, - - char* url = NULL; - - switch ((*urlObj)->endpoint) { +// ---- +int OSSP_httpCli_createURL(OSSP_httpCli_UrlObj_t* obj) { + switch (obj->endpoint) { case OPENSUBSONIC_ENDPOINT_PING: - rc = asprintf(&url, "%s://%s/rest/ping?u=%s&t=%s&s=%s&f=json&v=%s&c=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName); - break; - case OPENSUBSONIC_ENDPOINT_GETSTARRED: - rc = asprintf(&url, "%s://%s/rest/getStarred?u=%s&t=%s&s=%s&f=json&v=%s&c=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName); - break; - case OPENSUBSONIC_ENDPOINT_GETSONG: - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(getSong) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/getSong?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_STREAM: // Does not have a fetchResponse counterpart - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(stream) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/stream?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_GETCOVERART: // Does not have a fetchResponse counterpart - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(getCoverArt) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/getCoverArt?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_GETALBUM: - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(getAlbum) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/getAlbum?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_GETPLAYLISTS: - rc = asprintf(&url, "%s://%s/rest/getPlaylists?u=%s&t=%s&s=%s&f=json&v=%s&c=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName); - break; - case OPENSUBSONIC_ENDPOINT_GETPLAYLIST: - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(getPlaylist) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/getPlaylist?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_GETARTISTS: - rc = asprintf(&url, "%s://%s/rest/getArtists?u=%s&t=%s&s=%s&f=json&v=%s&c=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName); - break; - case OPENSUBSONIC_ENDPOINT_GETARTIST: - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(getArtist) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/getArtist?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_GETLYRICSBYSONGID: - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(getArtist) ID is null."); - // TODO handle error - break; - } - rc = asprintf(&url, "%s://%s/rest/getLyricsBySongId?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id); - break; - case OPENSUBSONIC_ENDPOINT_GETALBUMLIST: - if ((*urlObj)->type == 0) { - logger_log_error(__func__, "(getAlbumList) Type is 0."); - // TODO handle error - break; - } - if ((*urlObj)->amount == 0) { - logger_log_error(__func__, "(getAlbumList) Amount is 0."); - // TODO handle error - break; - } - - char* typeString = NULL; - switch ((*urlObj)->type) { - case OPENSUBSONIC_ENDPOINT_GETALBUMLIST_RANDOM: - rc = asprintf(&typeString, "random"); - break; - case OPENSUBSONIC_ENDPOINT_GETALBUMLIST_NEWEST: - rc = asprintf(&typeString, "newest"); - break; - case OPENSUBSONIC_ENDPOINT_GETALBUMLIST_HIGHEST: - rc = asprintf(&typeString, "highest"); - break; - case OPENSUBSONIC_ENDPOINT_GETALBUMLIST_FREQUENT: - rc = asprintf(&typeString, "frequent"); - break; - case OPENSUBSONIC_ENDPOINT_GETALBUMLIST_RECENT: - rc = asprintf(&typeString, "recent"); - break; - default: - logger_log_error(__func__, "(getAlbumList) Unknown type requested."); - // TODO handle error - } - - rc = asprintf(&url, "%s://%s/rest/getAlbumList?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&type=%s&size=%d", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - typeString, (*urlObj)->amount); - free(typeString); - break; - case OPENSUBSONIC_ENDPOINT_SCROBBLE: - if ((*urlObj)->id == NULL) { - logger_log_error(__func__, "(scrobble) ID is null."); - // TODO handle error - break; - } - - char* submitString = NULL; - if ((*urlObj)->submit) { - rc = asprintf(&submitString, "true"); - } else { - rc = asprintf(&submitString, "false"); - } - rc = asprintf(&url, "%s://%s/rest/scrobble?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s&submission=%s", - configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, - configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, - configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName, - (*urlObj)->id, submitString); - free(submitString); - break; - case OPENSUBSONIC_ENDPOINT_GETINTERNETRADIOSTATIONS: - rc = asprintf(&url, "%s://%s/rest/getInternetRadioStations?u=%s&t=%s&s=%s&f=json&v=%s&c=%s", + rc = asprintf(&obj->url, "%s://%s/rest/ping?u=%s&t=%s&s=%s&f=json&v=%s&c=%s", configObj->opensubsonic_protocol, configObj->opensubsonic_server, configObj->opensubsonic_username, configObj->internal_opensubsonic_loginToken, configObj->internal_opensubsonic_loginSalt, configObj->internal_opensubsonic_version, configObj->internal_opensubsonic_clientName); + obj->httpMethod = HTTP_METHOD_GET; break; default: - logger_log_error(__func__, "Unknown endpoint requested."); + return 1; break; } - - if (rc == -1) { - logger_log_error(__func__, "asprintf() error."); - // TODO handle error - } - - // HACK - (*urlObj)->formedUrl = strdup(url); free(url); + + + + return 0; } -void opensubsonic_httpClient_fetchResponse(opensubsonic_httpClient_URL_t** urlObj, void** responseObj) { - // Make and prepare HTTP object - opensubsonic_httpClientRequest_t* httpReq; - opensubsonic_httpClient_prepareRequest(&httpReq); - httpReq->method = HTTP_METHOD_GET; - httpReq->requestUrl = strdup((*urlObj)->formedUrl); - opensubsonic_httpClient_request(&httpReq); - - // Cannot use a switch statement here due to maintaining compatibility with < C23 - if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_PING) { - opensubsonic_ping_struct** pingStruct = (opensubsonic_ping_struct**)responseObj; - opensubsonic_ping_parse(httpReq->responseMsg, pingStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETSTARRED) { - opensubsonic_getStarred_struct** getStarredStruct = (opensubsonic_getStarred_struct**)responseObj; - opensubsonic_getStarred_parse(httpReq->responseMsg, getStarredStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETSONG) { - opensubsonic_getSong_struct** getSongStruct = (opensubsonic_getSong_struct**)responseObj; - opensubsonic_getSong_parse(httpReq->responseMsg, getSongStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETALBUM) { - opensubsonic_getAlbum_struct** getAlbumStruct = (opensubsonic_getAlbum_struct**)responseObj; - opensubsonic_getAlbum_parse(httpReq->responseMsg, getAlbumStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETPLAYLISTS) { - opensubsonic_getPlaylists_struct** getPlaylistsStruct = (opensubsonic_getPlaylists_struct**)responseObj; - opensubsonic_getPlaylists_parse(httpReq->responseMsg, getPlaylistsStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETPLAYLIST) { - opensubsonic_getPlaylist_struct** getPlaylistStruct = (opensubsonic_getPlaylist_struct**)responseObj; - opensubsonic_getPlaylist_parse(httpReq->responseMsg, getPlaylistStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETARTISTS) { - opensubsonic_getArtists_struct** getArtistsStruct = (opensubsonic_getArtists_struct**)responseObj; - opensubsonic_getArtists_parse(httpReq->responseMsg, getArtistsStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETARTIST) { - opensubsonic_getArtist_struct** getArtistStruct = (opensubsonic_getArtist_struct**)responseObj; - opensubsonic_getArtist_parse(httpReq->responseMsg, getArtistStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETLYRICSBYSONGID) { - opensubsonic_getLyricsBySongId_struct** getLyricsBySongIdStruct = (opensubsonic_getLyricsBySongId_struct**)responseObj; - opensubsonic_getLyricsBySongId_parse(httpReq->responseMsg, getLyricsBySongIdStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETALBUMLIST) { - opensubsonic_getAlbumList_struct** getAlbumListStruct = (opensubsonic_getAlbumList_struct**)responseObj; - opensubsonic_getAlbumList_parse(httpReq->responseMsg, getAlbumListStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_SCROBBLE) { - opensubsonic_scrobble_struct** scrobbleStruct = (opensubsonic_scrobble_struct**)responseObj; - opensubsonic_scrobble_parse(httpReq->responseMsg, scrobbleStruct); - } else if ((*urlObj)->endpoint == OPENSUBSONIC_ENDPOINT_GETINTERNETRADIOSTATIONS) { - opensubsonic_getInternetRadioStations_struct** getInternetRadioStationsStruct = (opensubsonic_getInternetRadioStations_struct**)responseObj; - opensubsonic_getInternetRadioStations_parse(httpReq->responseMsg, getInternetRadioStationsStruct); +// --- +int OSSP_httpCli_sendReq(OSSP_httpCli_UrlObj_t* obj) { + static int rc = 0; + OSSP_httpCli_UNIXHttpReq(obj); + + if (obj->resBody == NULL) { + printf("[LibOpenSubsonic] HTTP Request returned no data.\n"); + return 1; } else { - logger_log_error(__func__, "Unknown endpoint requested."); + switch (obj->endpoint) { + case OPENSUBSONIC_ENDPOINT_PING: + obj->returnStruct = OSSP_endpoint_ping_Constructor(); + if (obj->returnStruct == NULL) { + printf("[LibOpenSubsonic] (%s) malloc() failed.\n", __func__); + return 1; + } + rc = OSSP_endpoint_ping_Parse(obj); + if (rc == 1) { return 1; } // Errors are already logged in their respective functions + break; + default: + break; + } } - - // Cleanup HTTP object - opensubsonic_httpClient_cleanup(&httpReq); + + return 0; } -// Contact the /rest/getAlbum endpoint -int opensubsonic_getAlbum(const char* protocol_ptr, const char* server_ptr, const char* user_ptr, char* login_token_ptr, char* login_salt_ptr, const char* opensubsonic_version_ptr, const char* client_name_ptr, char* id, char** response) { - // Generate full URL, perform HTTP GET, and free the full URL - int rc = 0; - char* full_url = malloc(256); - snprintf(full_url, 256, "%s://%s/rest/getAlbum?u=%s&t=%s&s=%s&f=json&v=%s&c=%s&id=%s", protocol_ptr, server_ptr, user_ptr, login_token_ptr, login_salt_ptr, opensubsonic_version_ptr, client_name_ptr, id); - //rc = opensubsonic_http_json_get(full_url, response); - free(full_url); - return rc; -} - - -// TODO COVER ART - Returns JSON on error. -// {"subsonic-response":{"status":"failed","version":"1.16.1","type":"navidrome","serverVersion":"0.53.1-FREEBSD (1ba390a)","openSubsonic":true,"error":{"code":70,"message":"Artwork not found"}}} -// Contact the /rest/getCoverArt endpoint (Returns binary data) - - - +/* // Functions for preparing / freeing a HTTP Request struct void opensubsonic_httpClient_prepareRequest(opensubsonic_httpClientRequest_t** httpReq) { // Allocate struct @@ -337,17 +133,16 @@ void opensubsonic_httpClient_cleanup(opensubsonic_httpClientRequest_t** httpReq) // Free struct free(*httpReq); } + */ // Perform HTTP POST for Scrobbling (This function is a wrapper around OS-specific networking functions) -int opensubsonic_httpClient_request(opensubsonic_httpClientRequest_t** httpReq) { - logger_log_general(__func__, "Performing HTTP Request."); -//#if defined(__APPLE__) && defined(__MACH__) - //XNU_HttpRequest(httpReq); -//#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) - UNIX_HttpRequest(httpReq); -//#endif - return 0; -} +//int opensubsonic_httpClient_request(opensubsonic_httpClientRequest_t** httpReq) { +// logger_log_general(__func__, "Performing HTTP Request."); + +// UNIX_HttpRequest(httpReq); + + // return 0; +//} @@ -374,6 +169,83 @@ static size_t write_to_memory(void *ptr, size_t size, size_t nmemb, void *userda return total_size; } +void OSSP_httpCli_UNIXHttpReq(OSSP_httpCli_UrlObj_t* obj) { + CURL* curl_handle = curl_easy_init(); + struct curl_slist* header_list = NULL; + struct memory chunk = {0}; + long httpCode = 0; + + if (curl_handle) { + // Set method (GET/POST) + if (obj->httpMethod == HTTP_METHOD_GET) { + curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "GET"); + } else if (obj->httpMethod == HTTP_METHOD_POST) { + curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "POST"); + if (obj->isBodyReq == false) { + header_list = curl_slist_append(header_list, "Content-Length: 0"); + } + } + + /* + // Set scrobbler information + if ((*httpReq)->scrobbler == SCROBBLER_LISTENBRAINZ && (*httpReq)->method == HTTP_METHOD_POST) { + header_list = curl_slist_append(header_list, "Content-Type: application/json"); + char* authString = NULL; + rc = asprintf(&authString, "Authorization: Token %s", configObj->listenbrainz_token); + if (rc == -1) { + logger_log_error(__func__, "asprintf() error."); + // TODO handle error + } + printf("CODE: %s\n", authString); + header_list = curl_slist_append(header_list, authString); // TODO Check does this copy the string? + // TODO free auth string + } + + if ((*httpReq)->isBodyRequired == true && (*httpReq)->scrobbler == 0) { + header_list = curl_slist_append(header_list, "X-Organization: Hojuix"); + header_list = curl_slist_append(header_list, "X-Application: OSSP"); + } + + if (header_list != NULL) { + curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, header_list); + } + + if ((*httpReq)->isBodyRequired == true) { + curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, (*httpReq)->requestBody); + curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, (long)strlen((*httpReq)->requestBody)); + } + */ + + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "OSSP/1.0 (avery@hojuix.org)"); + curl_easy_setopt(curl_handle, CURLOPT_URL, obj->url); + curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(curl_handle, CURLOPT_TCP_KEEPALIVE,0L); + +// Do not use SSL verification on iOS due to an SSL issue with using libcurl on iOS +#if defined(__APPLE__) && defined(__MACH__) && defined(XCODE) + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); +#else + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 1L); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 1L); +#endif // defined(__APPLE__) && defined(__MACH__) && defined(XCODE) + + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_to_memory); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); + CURLcode res = curl_easy_perform(curl_handle); + curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpCode); + } + + curl_easy_cleanup(curl_handle); + + if (chunk.data != NULL) { + obj->resBody = strdup(chunk.data); + free(chunk.data); + } + obj->resCode = (int)httpCode; +} + +/* void UNIX_HttpRequest(opensubsonic_httpClientRequest_t** httpReq) { CURL* curl_handle = curl_easy_init(); struct curl_slist* header_list = NULL; @@ -445,3 +317,4 @@ void UNIX_HttpRequest(opensubsonic_httpClientRequest_t** httpReq) { (*httpReq)->responseCode = (int)httpCode; free(chunk.data); } +*/ \ No newline at end of file diff --git a/src/libopensubsonic/httpclient.h b/src/libopensubsonic/httpclient.h index 55aa6ce..b280eb8 100644 --- a/src/libopensubsonic/httpclient.h +++ b/src/libopensubsonic/httpclient.h @@ -39,9 +39,20 @@ typedef struct { int type; // Type of request (As used in the /getAlbumList endpoint) int amount; // Amount of items to return (Also as used in the /getAlbumList endpoint) bool submit; // Submit scrobble (used for the /scrobble endpoint) - char* formedUrl; // Final URL -} opensubsonic_httpClient_URL_t; // Forms authenticated URLs with required parameters + char* url; // Final URL + // Internal + char* reqBody; + int httpMethod; + bool isBodyReq; + int resCode; + char* resBody; + + // Returned struct + void* returnStruct; +} OSSP_httpCli_UrlObj_t; // Forms authenticated URLs with required parameters + +/* typedef struct { // Request Information char* requestUrl; @@ -53,21 +64,23 @@ typedef struct { // Response Information int responseCode; char* responseMsg; -} opensubsonic_httpClientRequest_t; // OS-agnostic HTTP interface +} OSSP_httpCli_ResObj_t; // OS-agnostic HTTP interface + */ -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) -void UNIX_HttpRequest(opensubsonic_httpClientRequest_t** httpReq); -#endif +OSSP_httpCli_UrlObj_t* OSSP_httpCli_UrlObj_Constructor(); +void OSSP_httpCli_UrlObj_Deconstructor(OSSP_httpCli_UrlObj_t* obj); +int OSSP_httpCli_createURL(OSSP_httpCli_UrlObj_t* obj); -void opensubsonic_httpClient_URL_prepare(opensubsonic_httpClient_URL_t** urlObj); -void opensubsonic_httpClient_URL_cleanup(opensubsonic_httpClient_URL_t** urlObj); -void opensubsonic_httpClient_formUrl(opensubsonic_httpClient_URL_t** urlObj); -void opensubsonic_httpClient_fetchResponse(opensubsonic_httpClient_URL_t** urlObj, void** responseObj); -void opensubsonic_httpClient_prepareRequest(opensubsonic_httpClientRequest_t** httpReq); -void opensubsonic_httpClient_cleanup(opensubsonic_httpClientRequest_t** httpReq); -int opensubsonic_httpClient_request(opensubsonic_httpClientRequest_t** httpReq); +int OSSP_httpCli_sendReq(OSSP_httpCli_UrlObj_t* obj); +void OSSP_httpCli_UNIXHttpReq(OSSP_httpCli_UrlObj_t* obj); -void UNIX_HttpRequest(opensubsonic_httpClientRequest_t** httpReq); +//void opensubsonic_httpClient_formUrl(opensubsonic_httpClient_URL_t** urlObj); +//void opensubsonic_httpClient_fetchResponse(opensubsonic_httpClient_URL_t** urlObj, void** responseObj); +//void opensubsonic_httpClient_prepareRequest(opensubsonic_httpClientRequest_t** httpReq); +//void opensubsonic_httpClient_cleanup(opensubsonic_httpClientRequest_t** httpReq); +//int opensubsonic_httpClient_request(opensubsonic_httpClientRequest_t** httpReq); + +//void UNIX_HttpRequest(opensubsonic_httpClientRequest_t** httpReq); // DEPRECATED - TO BE REMOVED SOON - APART OF THE OLD INFRASTRUCTURE typedef struct { @@ -75,7 +88,7 @@ typedef struct { size_t size; } binary_response_struct; -int opensubsonic_getAlbum(const char* protocol_ptr, const char* server_ptr, const char* user_ptr, char* login_token_ptr, char* login_salt_ptr, const char* opensubsonic_version_ptr, const char* client_name_ptr, char* id, char** response); +//int opensubsonic_getAlbum(const char* protocol_ptr, const char* server_ptr, const char* user_ptr, char* login_token_ptr, char* login_salt_ptr, const char* opensubsonic_version_ptr, const char* client_name_ptr, char* id, char** response); #ifdef __cplusplus }