Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dfe3b760f9 | |||
| 510137015d | |||
| ba775e7ff3 | |||
| ba9a08cb85 |
+54
-49
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* OpenSubSonicPlayer
|
||||
* Goldenkrew3000 2025
|
||||
* OpenSubsonicPlayer
|
||||
* Goldenkrew3000 / Hojuix 2026
|
||||
* License: GNU General Public License 3.0
|
||||
* Discord Local RPC Handler
|
||||
* Info: Discord RPC Handler
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "external/discord-rpc/include/discord_rpc.h"
|
||||
#include "libopensubsonic/logger.h"
|
||||
#include "configHandler.h"
|
||||
#include "discordrpc.h"
|
||||
|
||||
@@ -23,79 +22,85 @@ const char* discordrpc_appid = "1407025303779278980";
|
||||
char* discordrpc_osString = NULL;
|
||||
static int rc = 0;
|
||||
|
||||
void discordrpc_struct_init(discordrpc_data** discordrpc_struct) {
|
||||
(*discordrpc_struct) = malloc(sizeof(discordrpc_data));
|
||||
(*discordrpc_struct)->state = 0;
|
||||
(*discordrpc_struct)->songLength = 0;
|
||||
(*discordrpc_struct)->songTitle = NULL;
|
||||
(*discordrpc_struct)->songArtist = NULL;
|
||||
(*discordrpc_struct)->coverArtUrl = NULL;
|
||||
OSSP_discordrpc_t* OSSP_discordrpc_Constructor() {
|
||||
printf("Running discordrpc Constructor.\n");
|
||||
OSSP_discordrpc_t* obj = malloc(sizeof(OSSP_discordrpc_t));
|
||||
if (obj == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
obj->state = 0;
|
||||
obj->songLength = 0;
|
||||
obj->startTime = 0;
|
||||
obj->songTitle = NULL;
|
||||
obj->songArtist = NULL;
|
||||
obj->coverArtUrl = NULL;
|
||||
return obj;
|
||||
}
|
||||
|
||||
void discordrpc_struct_deinit(discordrpc_data** discordrpc_struct) {
|
||||
if ((*discordrpc_struct)->songTitle != NULL) { free((*discordrpc_struct)->songTitle); }
|
||||
if ((*discordrpc_struct)->songArtist != NULL) { free((*discordrpc_struct)->songArtist); }
|
||||
if ((*discordrpc_struct)->coverArtUrl != NULL) { free((*discordrpc_struct)->coverArtUrl); }
|
||||
if (*discordrpc_struct != NULL) { free(*discordrpc_struct); }
|
||||
void OSSP_discordrpc_Deconstructor(OSSP_discordrpc_t* obj) {
|
||||
printf("Running discordrpc Deconstructor.\n");
|
||||
if (obj->songTitle != NULL) { free(obj->songTitle); }
|
||||
if (obj->songArtist != NULL) { free(obj->songArtist); }
|
||||
if (obj->coverArtUrl != NULL) { free(obj->coverArtUrl); }
|
||||
if (obj != NULL) { free(obj); }
|
||||
}
|
||||
|
||||
int discordrpc_init() {
|
||||
int OSSP_discordrpc_Init() {
|
||||
printf("[DiscordRPC] Initializing.\n");
|
||||
// TODO Can I just not deal with the handler callbacks at all?
|
||||
DiscordEventHandlers handlers;
|
||||
memset(&handlers, 0, sizeof(handlers));
|
||||
Discord_Initialize(discordrpc_appid, &handlers, 1, NULL);
|
||||
|
||||
// Fetch OS String for RPC (Heap-allocated)
|
||||
discordrpc_osString = discordrpc_getOS();
|
||||
discordrpc_osString = OSSP_discordrpc_getOS();
|
||||
if (discordrpc_osString == NULL) {
|
||||
logger_log_error(__func__, "asprintf() or strdup() failed.");
|
||||
printf("[DiscordRPC] (%s) asprintf() or strdup() failed.\n", __func__);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void discordrpc_update(discordrpc_data** discordrpc_struct) {
|
||||
void OSSP_discordrpc_update(OSSP_discordrpc_t* obj) {
|
||||
printf("[DiscordRPC] Updating...\n");
|
||||
DiscordRichPresence presence;
|
||||
char* detailsString = NULL;
|
||||
char* stateString = NULL;
|
||||
memset(&presence, 0, sizeof(presence));
|
||||
|
||||
if ((*discordrpc_struct)->state == DISCORDRPC_STATE_IDLE) {
|
||||
if (obj->state == DISCORDRPC_STATE_IDLE) {
|
||||
printf("[DiscordRPC] Issuing Idle RPC.\n");
|
||||
asprintf(&detailsString, "Idle");
|
||||
presence.details = detailsString;
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_OPENSUBSONIC ||
|
||||
((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_LOCALFILE)) {
|
||||
} else if (obj->state == DISCORDRPC_STATE_PLAYING_OPENSUBSONIC ||
|
||||
(obj->state == DISCORDRPC_STATE_PLAYING_LOCALFILE)) {
|
||||
// Playing a song from an OpenSubsonic server
|
||||
printf("[DiscordRPC] Issuing OpenSubsonic/Local File Song RPC.\n");
|
||||
asprintf(&detailsString, "%s", (*discordrpc_struct)->songTitle);
|
||||
asprintf(&stateString, "by %s", (*discordrpc_struct)->songArtist);
|
||||
asprintf(&detailsString, "%s", obj->songTitle);
|
||||
asprintf(&stateString, "by %s", obj->songArtist);
|
||||
presence.details = detailsString;
|
||||
presence.state = stateString;
|
||||
if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_OPENSUBSONIC) {
|
||||
if (obj->state == DISCORDRPC_STATE_PLAYING_OPENSUBSONIC) {
|
||||
// TODO As of now, local file playback does NOT deal with cover art
|
||||
presence.largeImageKey = (*discordrpc_struct)->coverArtUrl;
|
||||
presence.largeImageKey = obj->coverArtUrl;
|
||||
}
|
||||
presence.startTimestamp = (long)((*discordrpc_struct)->startTime);
|
||||
presence.endTimestamp = (long)((*discordrpc_struct)->startTime) + (*discordrpc_struct)->songLength;
|
||||
presence.startTimestamp = (long)(obj->startTime);
|
||||
presence.endTimestamp = (long)(obj->startTime) + obj->songLength;
|
||||
if (configObj->discordrpc_showSysDetails) {
|
||||
presence.largeImageText = discordrpc_osString;
|
||||
}
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_INTERNETRADIO) {
|
||||
} else if (obj->state == DISCORDRPC_STATE_PLAYING_INTERNETRADIO) {
|
||||
// Playing an internet radio station
|
||||
printf("[DiscordRPC] Issuing Internet Radio RPC.\n");
|
||||
asprintf(&detailsString, "%s", (*discordrpc_struct)->songTitle);
|
||||
asprintf(&detailsString, "%s", obj->songTitle);
|
||||
asprintf(&stateString, "Internet radio station");
|
||||
presence.details = detailsString;
|
||||
presence.state = stateString;
|
||||
presence.largeImageKey = (*discordrpc_struct)->coverArtUrl;
|
||||
presence.startTimestamp = (long)((*discordrpc_struct)->startTime);
|
||||
presence.largeImageKey = obj->coverArtUrl;
|
||||
presence.startTimestamp = (long)(obj->startTime);
|
||||
if (configObj->discordrpc_showSysDetails) {
|
||||
presence.largeImageText = discordrpc_osString;
|
||||
}
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PAUSED) {
|
||||
} else if (obj->state == DISCORDRPC_STATE_PAUSED) {
|
||||
// Player is paused
|
||||
printf("[DiscordRPC] Issuing Paused RPC.\n");
|
||||
asprintf(&detailsString, "Paused");
|
||||
@@ -109,46 +114,46 @@ void discordrpc_update(discordrpc_data** discordrpc_struct) {
|
||||
if (stateString != NULL) { free(stateString); }
|
||||
}
|
||||
|
||||
char* discordrpc_getOS() {
|
||||
char* OSSP_discordrpc_getOS() {
|
||||
#if defined(__linux__)
|
||||
// NOTE: Could have made a sysctl function, but this is literally only done here, not worth it
|
||||
FILE* fp_ostype = fopen("/proc/sys/kernel/ostype", "r");
|
||||
char buf_ostype[16];
|
||||
if (!fp_ostype) {
|
||||
logger_log_error(__func__, "Could not perform kernel.ostype sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kernel.ostype sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE* fp_osrelease = fopen("/proc/sys/kernel/osrelease", "r");
|
||||
char buf_osrelease[32];
|
||||
if (!fp_osrelease) {
|
||||
logger_log_error(__func__, "Could not perform kernel.osrelease sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kernel.osrelease sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FILE* fp_osarch = fopen("/proc/sys/kernel/arch", "r");
|
||||
char buf_osarch[16];
|
||||
if (!fp_osarch) {
|
||||
logger_log_error(__func__, "Could not perform kernel.arch sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kernel.arch sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fgets(buf_ostype, sizeof(buf_ostype), fp_ostype) == NULL) {
|
||||
logger_log_error(__func__, "Could not perform kernel.ostype sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kernel.ostype sysctl.\n", __func__);
|
||||
fclose(fp_ostype);
|
||||
fclose(fp_osrelease);
|
||||
fclose(fp_osarch);
|
||||
return NULL;
|
||||
}
|
||||
if (fgets(buf_osrelease, sizeof(buf_osrelease), fp_osrelease) == NULL) {
|
||||
logger_log_error(__func__, "Could not perform kernel.osrelease sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kernel.osrelease sysctl.\n", __func__);
|
||||
fclose(fp_ostype);
|
||||
fclose(fp_osrelease);
|
||||
fclose(fp_osarch);
|
||||
return NULL;
|
||||
}
|
||||
if (fgets(buf_osarch, sizeof(buf_osarch), fp_osarch) == NULL) {
|
||||
logger_log_error(__func__, "Could not perform kernel.arch sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kernel.arch sysctl.\n", __func__);
|
||||
fclose(fp_ostype);
|
||||
fclose(fp_osrelease);
|
||||
fclose(fp_osarch);
|
||||
@@ -166,7 +171,7 @@ char* discordrpc_getOS() {
|
||||
char* osString = NULL;
|
||||
rc = asprintf(&osString, "on %s %s %s", buf_ostype, buf_osarch, buf_osrelease);
|
||||
if (rc == -1) {
|
||||
logger_log_error(__func__, "asprintf() failed.");
|
||||
printf("[DiscordRPC] (%s) asprintf() failed.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
return osString;
|
||||
@@ -184,24 +189,24 @@ char* discordrpc_getOS() {
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_OSTYPE;
|
||||
if (sysctl(mib, 2, buf_ostype, &sz_ostype, NULL, 0) == -1) {
|
||||
logger_log_error(__func__, "Could not perform kern.ostype sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kern.ostype sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mib[1] = KERN_OSRELEASE;
|
||||
if (sysctl(mib, 2, buf_osrelease, &sz_osrelease, NULL, 0) == -1) {
|
||||
logger_log_error(__func__, "Could not perform kern.osrelease sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform kern.osrelease sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// hw.optional.arm64 does not seem to have a direct mib0/1 route
|
||||
size_t mib_len = CTL_MAXNAME;
|
||||
if (sysctlnametomib("hw.optional.arm64", mib, &mib_len) != 0) {
|
||||
logger_log_error(__func__, "Could not perform hw.optional.arm64 sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform hw.optional.arm64 sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
if (sysctl(mib, mib_len, &isArm64, &sz_isArm64, NULL, 0) != 0) {
|
||||
logger_log_error(__func__, "Could not perform hw.optional.arm64 sysctl.");
|
||||
printf("[DiscordRPC] (%s) Could not perform hw.optional.arm64 sysctl.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -212,13 +217,13 @@ char* discordrpc_getOS() {
|
||||
rc = asprintf(&osString, "on %s XNU x86_64 %s", buf_ostype, buf_osrelease);
|
||||
}
|
||||
if (rc == -1) {
|
||||
logger_log_error(__func__, "asprintf() failed.");
|
||||
printf("[DiscordRPC] (%s) asprintf() failed.\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
return osString;
|
||||
#else
|
||||
// NOTE: This is not a critical error, just let the user know
|
||||
logger_log_error(__func__, "Could not fetch OS details.");
|
||||
printf("[DiscordRPC] (%s) Could not fetch OS details.\n", __func__);
|
||||
return strdup("on Unknown");
|
||||
#endif
|
||||
}
|
||||
|
||||
+16
-8
@@ -1,13 +1,18 @@
|
||||
/*
|
||||
* OpenSubsonicPlayer
|
||||
* Goldenkrew3000 2025
|
||||
* Goldenkrew3000 / Hojuix 2026
|
||||
* License: GNU General Public License 3.0
|
||||
* Info: Discord RPC Handler
|
||||
*/
|
||||
|
||||
#ifndef _DISCORDRPC_H
|
||||
#define _DISCORDRPC_H
|
||||
#include <time.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#define DISCORDRPC_STATE_IDLE 0
|
||||
#define DISCORDRPC_STATE_PLAYING_OPENSUBSONIC 1
|
||||
#define DISCORDRPC_STATE_PLAYING_LOCALFILE 2
|
||||
@@ -21,13 +26,16 @@ typedef struct {
|
||||
char* songTitle;
|
||||
char* songArtist;
|
||||
char* coverArtUrl;
|
||||
} discordrpc_data;
|
||||
} OSSP_discordrpc_t;
|
||||
|
||||
OSSP_discordrpc_t* OSSP_discordrpc_Constructor();
|
||||
void OSSP_discordrpc_Deconstructor(OSSP_discordrpc_t* obj);
|
||||
int OSSP_discordrpc_Init();
|
||||
void OSSP_discordrpc_update(OSSP_discordrpc_t* obj);
|
||||
char* OSSP_discordrpc_getOS();
|
||||
|
||||
void discordrpc_struct_init(discordrpc_data** discordrpc_struct);
|
||||
void discordrpc_struct_deinit(discordrpc_data** discordrpc_struct);
|
||||
int discordrpc_init();
|
||||
void discordrpc_update(discordrpc_data** discordrpc_struct);
|
||||
char* discordrpc_getOS();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif
|
||||
#endif // _DISCORDRPC_H
|
||||
|
||||
@@ -1,47 +1,67 @@
|
||||
/*
|
||||
* OpenSubsonicPlayer
|
||||
* Goldenkrew3000 / Hojuix 2026
|
||||
* License: GNU General Public License 3.0
|
||||
* Info: OpenSubsonic /ping endpoint parser
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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); }
|
||||
}
|
||||
|
||||
@@ -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 <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
}
|
||||
|
||||
+149
-276
@@ -4,9 +4,7 @@
|
||||
#include <curl/curl.h>
|
||||
#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);
|
||||
}
|
||||
*/
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
#include "../external/cJSON.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
@@ -15,91 +14,39 @@
|
||||
*/
|
||||
|
||||
void OSS_Psoj(char** dest, cJSON* obj, char* child) {
|
||||
if (obj == NULL) {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Parent object is null.");
|
||||
//}
|
||||
} else {
|
||||
if (obj != NULL) {
|
||||
cJSON* childObj = cJSON_GetObjectItem(obj, child);
|
||||
|
||||
if (cJSON_IsString(childObj) && childObj->valuestring != NULL) {
|
||||
*dest = strdup(childObj->valuestring);
|
||||
} else {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Object %s is not a string or string is null.", child);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSS_Pioj(int* dest, cJSON* obj, char* child) {
|
||||
if (obj == NULL) {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Parent object is null.");
|
||||
//}
|
||||
} else {
|
||||
if (obj != NULL) {
|
||||
cJSON* childObj = cJSON_GetObjectItem(obj, child);
|
||||
|
||||
if (cJSON_IsNumber(childObj)) {
|
||||
*dest = childObj->valueint;
|
||||
} else {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Object %s is not an int.", child);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSS_Ploj(long* dest, cJSON* obj, char* child) {
|
||||
if (obj == NULL) {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Parent object is null.");
|
||||
//}
|
||||
} else {
|
||||
if (obj != NULL) {
|
||||
cJSON* childObj = cJSON_GetObjectItem(obj, child);
|
||||
|
||||
if (cJSON_IsNumber(childObj)) {
|
||||
*dest = childObj->valueint;
|
||||
} else {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Object %s is not a long.", child);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OSS_Pboj(bool* dest, cJSON* obj, char* child) {
|
||||
if (obj == NULL) {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Parent object is null.");
|
||||
//}
|
||||
} else {
|
||||
if (obj != NULL) {
|
||||
cJSON* childObj = cJSON_GetObjectItem(obj, child);
|
||||
|
||||
if (cJSON_IsBool(childObj)) {
|
||||
if (cJSON_IsTrue(childObj)) {
|
||||
*dest = true;
|
||||
}
|
||||
} else {
|
||||
//void* ret_addr = __builtin_return_address(0);
|
||||
//Dl_info info;
|
||||
//if (dladdr(ret_addr, &info)) {
|
||||
// logger_log_error(info.dli_sname, "Object is not a bool.");
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user