Files
OSSP/src/libopensubsonic/httpclient.c
T

320 lines
11 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include "../external/cJSON.h"
#include "httpclient.h"
#include "../configHandler.h"
#include "endpoint_ping.h"
#include "endpoint_getStarred.h"
#include "endpoint_getSong.h"
#include "endpoint_getPlaylists.h"
#include "endpoint_getPlaylist.h"
#include "endpoint_getArtists.h"
#include "endpoint_getArtist.h"
#include "endpoint_getLyricsBySongId.h"
#include "endpoint_getAlbumList.h"
#include "endpoint_getAlbum.h"
#include "endpoint_scrobble.h"
#include "endpoint_getInternetRadioStations.h"
static int rc = 0;
extern configHandler_config_t* configObj;
/*
* 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 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); }
}
// ----
int OSSP_httpCli_createURL(OSSP_httpCli_UrlObj_t* obj) {
switch (obj->endpoint) {
case OPENSUBSONIC_ENDPOINT_PING:
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:
return 1;
break;
}
return 0;
}
// ---
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 {
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;
}
}
return 0;
}
/*
// Functions for preparing / freeing a HTTP Request struct
void opensubsonic_httpClient_prepareRequest(opensubsonic_httpClientRequest_t** httpReq) {
// Allocate struct
*httpReq = (opensubsonic_httpClientRequest_t*)malloc(sizeof(opensubsonic_httpClientRequest_t));
// Initialize struct variables
(*httpReq)->requestUrl = NULL;
(*httpReq)->requestBody = NULL;
(*httpReq)->method = 0;
(*httpReq)->isBodyRequired = false;
(*httpReq)->scrobbler = 0;
(*httpReq)->responseCode = 0;
(*httpReq)->responseMsg = NULL;
}
void opensubsonic_httpClient_cleanup(opensubsonic_httpClientRequest_t** httpReq) {
// Free heap-allocated struct variables
if ((*httpReq)->requestUrl != NULL) { free((*httpReq)->requestUrl); }
if ((*httpReq)->requestBody != NULL) { free((*httpReq)->requestBody); }
if ((*httpReq)->responseMsg != NULL) { free((*httpReq)->responseMsg); }
// 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.");
// UNIX_HttpRequest(httpReq);
// return 0;
//}
struct memory {
char *data;
size_t size;
};
static size_t write_to_memory(void *ptr, size_t size, size_t nmemb, void *userdata) {
struct memory *mem = (struct memory *)userdata;
size_t total_size = size * nmemb;
mem->data = realloc(mem->data, mem->size + total_size + 1);
if (!mem->data) return 0; // Fail on OOM
memcpy(&(mem->data[mem->size]), ptr, total_size);
mem->size += total_size;
mem->data[mem->size] = '\0'; // Null-terminate
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;
struct memory chunk = {0};
long httpCode = 0;
if (curl_handle) {
// Set method
if ((*httpReq)->method == HTTP_METHOD_GET) {
curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "GET");
} else if ((*httpReq)->method == HTTP_METHOD_POST) {
curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "POST");
if ((*httpReq)->isBodyRequired == 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, (*httpReq)->requestUrl);
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);
(*httpReq)->responseMsg = strdup(chunk.data);
(*httpReq)->responseCode = (int)httpCode;
free(chunk.data);
}
*/