mirror of
https://github.com/Goldenkrew3000/OSSP_OpenSource.git
synced 2026-02-16 12:15:17 +10:00
Compare commits
14 Commits
abd00a0761
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2853898145 | |||
| 9ae53e0824 | |||
| 1f814fff6e | |||
| 26df10f8fb | |||
| 66aa616286 | |||
| ba554dc716 | |||
| 34f562149e | |||
| db7fb9385d | |||
| cd03819c65 | |||
| 6735603e9c | |||
| 8e7eb4c534 | |||
| 9f96c574e9 | |||
| 4a9e1fc845 | |||
| b4c584f60b |
@@ -17,7 +17,7 @@ Discord RPC Settings:
|
||||
Enable ('enable') - 'true' to enable Discord RPC or 'false' to disable it
|
||||
Method ('method'):
|
||||
- Method '0' is the official local Discord RPC method, local application to local discord client
|
||||
- Method '1' is a VERY buggy and in-development RPC method that uses a client-server model
|
||||
- DO NOT USE: Method '1' is a VERY buggy and in-development RPC method that uses a client-server model
|
||||
to allow Discord RPC from a mobile device securely
|
||||
Show System Details ('showSystemDetails') - 'true' or 'false'
|
||||
- Want to show off / Got bragging rights on your _unique_ system?? Well this is perfect for you!
|
||||
@@ -25,3 +25,20 @@ Show System Details ('showSystemDetails') - 'true' or 'false'
|
||||
Example: 'on Linux x86_64 6.17.1-arch1-1' will be shown in the RPC dialog
|
||||
Go on, don't be shy, show everyone you somehow have Discord and OSSP running on fucking s390x!!!
|
||||
- Setting this to 'false' will simply instead show what playlist you are playing.
|
||||
Show Cover Art ('showCoverArt') - 'true' or 'false'
|
||||
- If this is set to true, the cover art for the song playing will be shown on the Discord rich presence.
|
||||
This can be disabled because the OpenSubsonic API does not have an unauthenticated way of accessing
|
||||
album art from a server, which means you have to leak an authenticated server URL to Discord (Which
|
||||
a lot of people would not feel comfortable doing, understandably).
|
||||
- If this is set to false, Discord rich presence only shows the app icon, and does not leak any authenticated
|
||||
URLs to Discord servers
|
||||
|
||||
|
||||
|
||||
Radio Sqlite3 Database Drafting:
|
||||
Location: $HOME/.config/ossp/radio.db
|
||||
|
||||
Table: stations
|
||||
- Int: id
|
||||
- String: name
|
||||
- String: url
|
||||
@@ -1,5 +1,11 @@
|
||||
# OSSP (OpenSubsonicPlayer)
|
||||
|
||||
## AI Notice
|
||||
This project does not have ANY AI code other than a few snippets that will be removed in the future.<br>
|
||||
Yes I comment a LOT, and unfortunately this has become a red flag for excessive AI use.<br>
|
||||
I am a forgetful person, and have a habit of dropping projects for months at a time, so having
|
||||
many comments allows me to come back and instantly understand what I was doing.<br>
|
||||
|
||||
## Notice
|
||||
OSSP is under heavy development and is NOT ready for daily use.<br>
|
||||
Also, I am NOT RESPONSIBLE if you damage your hearing and/or any of your equipment using OSSP.<br>
|
||||
|
||||
@@ -12,6 +12,8 @@ find_package(OpenGL REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
|
||||
pkg_check_modules(AVFORMAT REQUIRED libavformat)
|
||||
pkg_check_modules(AVUTIL REQUIRED libavutil)
|
||||
|
||||
add_subdirectory(external/discord-rpc)
|
||||
|
||||
@@ -24,6 +26,9 @@ add_executable(ossp MACOSX_BUNDLE
|
||||
main.c
|
||||
configHandler.c
|
||||
discordrpc.c
|
||||
localRadioDBHandler.c
|
||||
localMusicHandler.cpp
|
||||
socket.c
|
||||
gui/gui_entry.cpp
|
||||
player/player.c
|
||||
player/playQueue.cpp
|
||||
@@ -44,6 +49,7 @@ add_executable(ossp MACOSX_BUNDLE
|
||||
libopensubsonic/endpoint_getStarred.c
|
||||
libopensubsonic/endpoint_ping.c
|
||||
libopensubsonic/endpoint_scrobble.c
|
||||
libopensubsonic/endpoint_getInternetRadioStations.c
|
||||
external/cJSON.c
|
||||
external/cJSON_Utils.c
|
||||
external/libcurl_uriescape.c
|
||||
@@ -55,6 +61,7 @@ add_executable(ossp MACOSX_BUNDLE
|
||||
external/imgui/imgui_demo.cpp
|
||||
external/imgui/backends/imgui_impl_sdl2.cpp
|
||||
external/imgui/backends/imgui_impl_opengl2.cpp
|
||||
external/sqlite3/sqlite3.c
|
||||
)
|
||||
|
||||
set_target_properties(ossp PROPERTIES
|
||||
@@ -62,6 +69,6 @@ set_target_properties(ossp PROPERTIES
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "org.hojuix.ossp"
|
||||
)
|
||||
|
||||
include_directories(${GSTREAMER_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_INCLUDE_DIRS} ${AVFORMAT_INCLUDE_DIR} ${AVUTIL_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(ossp PRIVATE OpenSSL::SSL OpenSSL::Crypto CURL::libcurl SDL2::SDL2 ${OPENGL_LIBRARIES} discord-rpc ${GSTREAMER_LIBRARIES})
|
||||
target_link_libraries(ossp PRIVATE OpenSSL::SSL OpenSSL::Crypto CURL::libcurl SDL2::SDL2 ${OPENGL_LIBRARIES} discord-rpc ${GSTREAMER_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVUTIL_LIBRARIES})
|
||||
|
||||
@@ -17,4 +17,5 @@ gcc runTests.c \
|
||||
libopensubsonic/endpoint_getAlbumList.c \
|
||||
libopensubsonic/endpoint_getStarred.c \
|
||||
libopensubsonic/endpoint_scrobble.c \
|
||||
libopensubsonic/endpoint_getInternetRadioStations.c \
|
||||
-o tests -lcurl
|
||||
|
||||
@@ -36,6 +36,7 @@ int configHandler_Read(configHandler_config_t** configObj) {
|
||||
(*configObj)->internal_opensubsonic_clientName = NULL;
|
||||
(*configObj)->internal_opensubsonic_loginSalt = NULL;
|
||||
(*configObj)->internal_opensubsonic_loginToken = NULL;
|
||||
(*configObj)->internal_ossp_version = NULL;
|
||||
(*configObj)->listenbrainz_enable = false;
|
||||
(*configObj)->listenbrainz_token = NULL;
|
||||
(*configObj)->lastfm_enable = false;
|
||||
@@ -67,10 +68,12 @@ int configHandler_Read(configHandler_config_t** configObj) {
|
||||
(*configObj)->lv2_parax32_frequency_left = NULL;
|
||||
(*configObj)->lv2_parax32_frequency_right = NULL;
|
||||
(*configObj)->lv2_reverb_filter_name = NULL;
|
||||
(*configObj)->local_rootdir = NULL;
|
||||
|
||||
// Set internal configuration values
|
||||
(*configObj)->internal_opensubsonic_version = strdup("1.8.0");
|
||||
(*configObj)->internal_opensubsonic_clientName = strdup("Hojuix_OSSP");
|
||||
(*configObj)->internal_ossp_version = strdup("v0.4a");
|
||||
|
||||
// Form the path to the config JSON
|
||||
char* config_path = NULL;
|
||||
@@ -463,6 +466,19 @@ int configHandler_Read(configHandler_config_t** configObj) {
|
||||
(*configObj)->lv2_reverb_filter_name = strdup(calf_reverb_filter_name->valuestring);
|
||||
}
|
||||
|
||||
// Make an object from local
|
||||
cJSON* local_root = cJSON_GetObjectItemCaseSensitive(root, "local");
|
||||
if (local_root == NULL) {
|
||||
logger_log_error(__func__, "Error parsing JSON - local does not exist.");
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cJSON* local_root_directory = cJSON_GetObjectItemCaseSensitive(local_root, "rootDirectory");
|
||||
if (cJSON_IsString(local_root_directory) && local_root_directory->valuestring != NULL) {
|
||||
(*configObj)->local_rootdir = strdup(local_root_directory->valuestring);
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
logger_log_general(__func__, "Successfully read configuration file.");
|
||||
return 0;
|
||||
@@ -477,6 +493,7 @@ void configHandler_Free(configHandler_config_t** configObj) {
|
||||
if ((*configObj)->internal_opensubsonic_clientName != NULL) { free((*configObj)->internal_opensubsonic_clientName); }
|
||||
if ((*configObj)->internal_opensubsonic_loginSalt != NULL) { free((*configObj)->internal_opensubsonic_loginSalt); }
|
||||
if ((*configObj)->internal_opensubsonic_loginToken != NULL) { free((*configObj)->internal_opensubsonic_loginToken); }
|
||||
if ((*configObj)->internal_ossp_version != NULL) { free((*configObj)->internal_ossp_version); }
|
||||
if ((*configObj)->listenbrainz_token != NULL) { free((*configObj)->listenbrainz_token); }
|
||||
if ((*configObj)->lastfm_username != NULL) { free((*configObj)->lastfm_username); }
|
||||
if ((*configObj)->lastfm_password != NULL) { free((*configObj)->lastfm_password); }
|
||||
@@ -494,5 +511,6 @@ void configHandler_Free(configHandler_config_t** configObj) {
|
||||
if ((*configObj)->lv2_parax32_frequency_left != NULL) { free((*configObj)->lv2_parax32_frequency_left); }
|
||||
if ((*configObj)->lv2_parax32_frequency_right != NULL) { free((*configObj)->lv2_parax32_frequency_right); }
|
||||
if ((*configObj)->lv2_reverb_filter_name != NULL) { free((*configObj)->lv2_reverb_filter_name); }
|
||||
if ((*configObj)->local_rootdir != NULL) { free((*configObj)->local_rootdir); }
|
||||
if (*configObj != NULL) { free(*configObj); }
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ typedef struct {
|
||||
char* internal_opensubsonic_clientName; // (Internal) Opensubsonic Client Name
|
||||
char* internal_opensubsonic_loginSalt; // (Internal) Opensubsonic Login Salt
|
||||
char* internal_opensubsonic_loginToken; // (Internal) Opensubsonic Login Token
|
||||
char* internal_ossp_version; // (Internal) OSSP Version
|
||||
|
||||
// Scrobbler Settings
|
||||
bool listenbrainz_enable; // Enable ListenBrainz Scrobbling
|
||||
@@ -64,6 +65,9 @@ typedef struct {
|
||||
char* lv2_parax32_frequency_left;
|
||||
char* lv2_parax32_frequency_right;
|
||||
char* lv2_reverb_filter_name; // LV2 Calf Reverb LV2 Name
|
||||
|
||||
// Local Settings
|
||||
char* local_rootdir; // Local Music Root Directory
|
||||
} configHandler_config_t;
|
||||
|
||||
int configHandler_Read(configHandler_config_t** config);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Goldenkrew3000 2025
|
||||
* License: GNU General Public License 3.0
|
||||
* Discord Local RPC Handler
|
||||
* Note: This provides server auth creds (encoded) directly to Discord, could use Spotify's API instead??
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
@@ -41,31 +40,11 @@ void discordrpc_struct_deinit(discordrpc_data** discordrpc_struct) {
|
||||
if (*discordrpc_struct != NULL) { free(*discordrpc_struct); }
|
||||
}
|
||||
|
||||
static void handleDiscordReady(const DiscordUser* connectedUser)
|
||||
{
|
||||
printf("\nDiscord: connected to user %s#%s - %s\n",
|
||||
connectedUser->username,
|
||||
connectedUser->discriminator,
|
||||
connectedUser->userId);
|
||||
}
|
||||
|
||||
static void handleDiscordDisconnected(int errcode, const char* message)
|
||||
{
|
||||
printf("\nDiscord: disconnected (%d: %s)\n", errcode, message);
|
||||
}
|
||||
|
||||
static void handleDiscordError(int errcode, const char* message)
|
||||
{
|
||||
printf("\nDiscord: error (%d: %s)\n", errcode, message);
|
||||
}
|
||||
|
||||
int discordrpc_init() {
|
||||
printf("[DiscordRPC] Initializing...\n");
|
||||
printf("[DiscordRPC] Initializing.\n");
|
||||
// TODO Can I just not deal with the handler callbacks at all?
|
||||
DiscordEventHandlers handlers;
|
||||
memset(&handlers, 0, sizeof(handlers));
|
||||
handlers.ready = handleDiscordReady;
|
||||
handlers.disconnected = handleDiscordDisconnected;
|
||||
handlers.errored = handleDiscordError;
|
||||
Discord_Initialize(discordrpc_appid, &handlers, 1, NULL);
|
||||
|
||||
// Fetch OS String for RPC (Heap-allocated)
|
||||
@@ -87,7 +66,8 @@ void discordrpc_update(discordrpc_data** discordrpc_struct) {
|
||||
if ((*discordrpc_struct)->state == DISCORDRPC_STATE_IDLE) {
|
||||
asprintf(&detailsString, "Idle");
|
||||
presence.details = detailsString;
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING) {
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_OPENSUBSONIC) {
|
||||
// Playing a song
|
||||
time_t currentTime = time(NULL);
|
||||
asprintf(&detailsString, "%s", (*discordrpc_struct)->songTitle);
|
||||
asprintf(&stateString, "by %s", (*discordrpc_struct)->songArtist);
|
||||
@@ -99,6 +79,20 @@ void discordrpc_update(discordrpc_data** discordrpc_struct) {
|
||||
if (configObj->discordrpc_showSysDetails) {
|
||||
presence.largeImageText = discordrpc_osString;
|
||||
}
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_LOCALFILE) {
|
||||
//
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PLAYING_INTERNETRADIO) {
|
||||
// Playing an internet radio station
|
||||
time_t currentTime = time(NULL);
|
||||
asprintf(&detailsString, "%s", (*discordrpc_struct)->songTitle);
|
||||
asprintf(&stateString, "Internet radio station");
|
||||
presence.details = detailsString;
|
||||
presence.state = stateString;
|
||||
presence.largeImageKey = (*discordrpc_struct)->coverArtUrl;
|
||||
presence.startTimestamp = (long)currentTime;
|
||||
if (configObj->discordrpc_showSysDetails) {
|
||||
presence.largeImageText = discordrpc_osString;
|
||||
}
|
||||
} else if ((*discordrpc_struct)->state == DISCORDRPC_STATE_PAUSED) {
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
#define _DISCORDRPC_H
|
||||
|
||||
#define DISCORDRPC_STATE_IDLE 0
|
||||
#define DISCORDRPC_STATE_PLAYING 1
|
||||
#define DISCORDRPC_STATE_PAUSED 2
|
||||
#define DISCORDRPC_STATE_PLAYING_OPENSUBSONIC 1
|
||||
#define DISCORDRPC_STATE_PLAYING_LOCALFILE 2
|
||||
#define DISCORDRPC_STATE_PLAYING_INTERNETRADIO 3
|
||||
#define DISCORDRPC_STATE_PAUSED 4
|
||||
|
||||
typedef struct {
|
||||
int state;
|
||||
|
||||
265952
src/external/sqlite3/sqlite3.c
vendored
Normal file
265952
src/external/sqlite3/sqlite3.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13968
src/external/sqlite3/sqlite3.h
vendored
Normal file
13968
src/external/sqlite3/sqlite3.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
730
src/external/sqlite3/sqlite3ext.h
vendored
Normal file
730
src/external/sqlite3/sqlite3ext.h
vendored
Normal file
@@ -0,0 +1,730 @@
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each other's shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*xsnprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||
void(*)(void*),unsigned char);
|
||||
int (*cancel_auto_extension)(void(*)(void));
|
||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||
void *(*malloc64)(sqlite3_uint64);
|
||||
sqlite3_uint64 (*msize)(void*);
|
||||
void *(*realloc64)(void*,sqlite3_uint64);
|
||||
void (*reset_auto_extension)(void);
|
||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||
void(*)(void*), unsigned char);
|
||||
int (*strglob)(const char*,const char*);
|
||||
/* Version 3.8.11 and later */
|
||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||
void (*value_free)(sqlite3_value*);
|
||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||
/* Version 3.9.0 and later */
|
||||
unsigned int (*value_subtype)(sqlite3_value*);
|
||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||
/* Version 3.10.0 and later */
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
/* Version 3.18.0 and later */
|
||||
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||
/* Version 3.20.0 and later */
|
||||
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
||||
sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
||||
sqlite3_stmt**,const void**);
|
||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
/* Version 3.25.0 and later */
|
||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*));
|
||||
/* Version 3.26.0 and later */
|
||||
const char *(*normalized_sql)(sqlite3_stmt*);
|
||||
/* Version 3.28.0 and later */
|
||||
int (*stmt_isexplain)(sqlite3_stmt*);
|
||||
int (*value_frombind)(sqlite3_value*);
|
||||
/* Version 3.30.0 and later */
|
||||
int (*drop_modules)(sqlite3*,const char**);
|
||||
/* Version 3.31.0 and later */
|
||||
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
|
||||
const char *(*uri_key)(const char*,int);
|
||||
const char *(*filename_database)(const char*);
|
||||
const char *(*filename_journal)(const char*);
|
||||
const char *(*filename_wal)(const char*);
|
||||
/* Version 3.32.0 and later */
|
||||
const char *(*create_filename)(const char*,const char*,const char*,
|
||||
int,const char**);
|
||||
void (*free_filename)(const char*);
|
||||
sqlite3_file *(*database_file_object)(const char*);
|
||||
/* Version 3.34.0 and later */
|
||||
int (*txn_state)(sqlite3*,const char*);
|
||||
/* Version 3.36.1 and later */
|
||||
sqlite3_int64 (*changes64)(sqlite3*);
|
||||
sqlite3_int64 (*total_changes64)(sqlite3*);
|
||||
/* Version 3.37.0 and later */
|
||||
int (*autovacuum_pages)(sqlite3*,
|
||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
||||
void*, void(*)(void*));
|
||||
/* Version 3.38.0 and later */
|
||||
int (*error_offset)(sqlite3*);
|
||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
||||
int (*vtab_distinct)(sqlite3_index_info*);
|
||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
/* Version 3.39.0 and later */
|
||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
/* Version 3.40.0 and later */
|
||||
int (*value_encoding)(sqlite3_value*);
|
||||
/* Version 3.41.0 and later */
|
||||
int (*is_interrupted)(sqlite3*);
|
||||
/* Version 3.43.0 and later */
|
||||
int (*stmt_explain)(sqlite3_stmt*,int);
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
/* Version 3.50.0 and later */
|
||||
int (*setlk_timeout)(sqlite3*,int,int);
|
||||
/* Version 3.51.0 and later */
|
||||
int (*set_errmsg)(sqlite3*,int,const char*);
|
||||
int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||
#define sqlite3_msize sqlite3_api->msize
|
||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||
#define sqlite3_strglob sqlite3_api->strglob
|
||||
/* Version 3.8.11 and later */
|
||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||
#define sqlite3_value_free sqlite3_api->value_free
|
||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||
/* Version 3.9.0 and later */
|
||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||
/* Version 3.10.0 and later */
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
/* Version 3.18.0 and later */
|
||||
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||
/* Version 3.20.0 and later */
|
||||
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
||||
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
/* Version 3.25.0 and later */
|
||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
||||
/* Version 3.26.0 and later */
|
||||
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
||||
/* Version 3.28.0 and later */
|
||||
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
|
||||
#define sqlite3_value_frombind sqlite3_api->value_frombind
|
||||
/* Version 3.30.0 and later */
|
||||
#define sqlite3_drop_modules sqlite3_api->drop_modules
|
||||
/* Version 3.31.0 and later */
|
||||
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
|
||||
#define sqlite3_uri_key sqlite3_api->uri_key
|
||||
#define sqlite3_filename_database sqlite3_api->filename_database
|
||||
#define sqlite3_filename_journal sqlite3_api->filename_journal
|
||||
#define sqlite3_filename_wal sqlite3_api->filename_wal
|
||||
/* Version 3.32.0 and later */
|
||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
||||
/* Version 3.34.0 and later */
|
||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
||||
/* Version 3.36.1 and later */
|
||||
#define sqlite3_changes64 sqlite3_api->changes64
|
||||
#define sqlite3_total_changes64 sqlite3_api->total_changes64
|
||||
/* Version 3.37.0 and later */
|
||||
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
|
||||
/* Version 3.38.0 and later */
|
||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
/* Version 3.40.0 and later */
|
||||
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
||||
/* Version 3.41.0 and later */
|
||||
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
||||
/* Version 3.43.0 and later */
|
||||
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
/* Version 3.50.0 and later */
|
||||
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||
/* Version 3.51.0 and later */
|
||||
#define sqlite3_set_errmsg sqlite3_api->set_errmsg
|
||||
#define sqlite3_db_status64 sqlite3_api->db_status64
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE3EXT_H */
|
||||
@@ -4,20 +4,45 @@
|
||||
#include "../configHandler.h"
|
||||
#include "logger.h"
|
||||
|
||||
// If the platform is Linux, but the libc isn't Glibc, assume Musl C
|
||||
// Yes, I know this ignores the existence of others such as Mlibc or Uclibc, but
|
||||
// Musl C doesn't define a precompiler macro to be identified, and this works for now
|
||||
#if defined(__linux__) && !defined(__GLIBC__)
|
||||
#define __MUSL__
|
||||
#endif
|
||||
|
||||
#if __NetBSD__ // Functions for NetBSD to use KERN_ARND
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(__MUSL__) // Musl C does not contain arc4random(), use Linux getrandom() instead
|
||||
#include <sys/random.h>
|
||||
#endif
|
||||
|
||||
static int rc = 0;
|
||||
extern configHandler_config_t* configObj;
|
||||
|
||||
// Use arc4random() to generate cryptographically secure bytes. Should work on all BSD-style systems
|
||||
#if !defined(__MUSL__) // If not Musl
|
||||
void crypto_secure_arc4random_generate(unsigned char* bytes, size_t length) {
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
bytes[i] = arc4random() & 0xFF;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use getrandom() instead of arc4random() for Musl C Linux
|
||||
#if defined(__MUSL__)
|
||||
void crypto_secure_getrandom_generate(unsigned char* bytes, size_t length) {
|
||||
if (getrandom(bytes, length, 0) != length) {
|
||||
printf("[libopensubsonic/crypto] getrandom() failed. Using insecure bytes.\n");
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
bytes[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Use the arandom sysctl on NetBSD to generate cryptographically secure bytes.
|
||||
#if __NetBSD__
|
||||
@@ -42,8 +67,12 @@ void crypto_secure_generate_salt(void) {
|
||||
// Generate cryptographically secure salt bytes using OS-native functions
|
||||
#if __NetBSD__
|
||||
crypto_secure_netbsd_arandom_generate(salt_bytes, 8);
|
||||
#else
|
||||
#if defined(__MUSL__)
|
||||
crypto_secure_getrandom_generate(salt_bytes, 8);
|
||||
#else
|
||||
crypto_secure_arc4random_generate(salt_bytes, 8);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Convert to a string hex representation
|
||||
|
||||
110
src/libopensubsonic/endpoint_getInternetRadioStations.c
Normal file
110
src/libopensubsonic/endpoint_getInternetRadioStations.c
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../external/cJSON.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include "endpoint_getInternetRadioStations.h"
|
||||
|
||||
int opensubsonic_getInternetRadioStations_parse(char* data, opensubsonic_getInternetRadioStations_struct** getInternetRadioStationsStruct) {
|
||||
// Allocate and initialize struct
|
||||
*getInternetRadioStationsStruct = malloc(sizeof(opensubsonic_getInternetRadioStations_struct));
|
||||
(*getInternetRadioStationsStruct)->status = NULL;
|
||||
(*getInternetRadioStationsStruct)->errorCode = 0;
|
||||
(*getInternetRadioStationsStruct)->errorMessage = NULL;
|
||||
(*getInternetRadioStationsStruct)->radioStationCount = 0;
|
||||
(*getInternetRadioStationsStruct)->radioStations = NULL;
|
||||
|
||||
// Parse JSON
|
||||
cJSON* root = cJSON_Parse(data);
|
||||
if (root == NULL) {
|
||||
logger_log_error(__func__, "Error parsing JSON.");
|
||||
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.");
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
OSS_Psoj(&(*getInternetRadioStationsStruct)->status, subsonic_root, "status");
|
||||
|
||||
// Check if API has returned an error
|
||||
if (strstr((*getInternetRadioStationsStruct)->status, "ok") == NULL) {
|
||||
// API has not returned 'ok' in status, fetch error, and return
|
||||
// Check if an error is present
|
||||
cJSON* subsonic_error = cJSON_GetObjectItemCaseSensitive(subsonic_root, "error");
|
||||
if (subsonic_error == NULL) {
|
||||
// Error not defined in JSON
|
||||
logger_log_error(__func__, "API has indicated failure through status, but error does not exist.");
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
OSS_Pioj(&(*getInternetRadioStationsStruct)->errorCode, subsonic_error, "code");
|
||||
OSS_Psoj(&(*getInternetRadioStationsStruct)->errorMessage, subsonic_error, "message");
|
||||
|
||||
logger_log_error(__func__, "Error noted in JSON - Code %d: %s", (*getInternetRadioStationsStruct)->errorCode, (*getInternetRadioStationsStruct)->errorMessage);
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Make an object from 'internetRadioStations'
|
||||
cJSON* internetRadioStations_root = cJSON_GetObjectItemCaseSensitive(subsonic_root, "internetRadioStations");
|
||||
if (internetRadioStations_root == NULL) {
|
||||
logger_log_error(__func__, "Error handling JSON - internetRadioStations does not exist.");
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Make an object from 'internetRadioStation
|
||||
cJSON* internetRadioStation_root = cJSON_GetObjectItemCaseSensitive(internetRadioStations_root, "internetRadioStation");
|
||||
if (internetRadioStation_root == NULL) {
|
||||
logger_log_error(__func__, "Error handling JSON - internetRadioStation does not exist.");
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get the amount of radio stiations, then allocate and initialize structs
|
||||
(*getInternetRadioStationsStruct)->radioStationCount = cJSON_GetArraySize(internetRadioStation_root);
|
||||
if ((*getInternetRadioStationsStruct)->radioStationCount != 0) {
|
||||
// If there are no radio stations, don't allocate anything, but let the program run through this.
|
||||
// Cleanup then happens at the same point, but the following allocation and fetching steps wont happen
|
||||
// Basically keeps the code cleaner for this edge case (Although this is probably present in other parts **TODO**)
|
||||
(*getInternetRadioStationsStruct)->radioStations = malloc((*getInternetRadioStationsStruct)->radioStationCount * sizeof(opensubsonic_getInternetRadioStations_radioStations_struct));
|
||||
}
|
||||
|
||||
for (int i = 0; i < (*getInternetRadioStationsStruct)->radioStationCount; i++) {
|
||||
(*getInternetRadioStationsStruct)->radioStations[i].id = NULL;
|
||||
(*getInternetRadioStationsStruct)->radioStations[i].name = NULL;
|
||||
(*getInternetRadioStationsStruct)->radioStations[i].streamUrl = NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < (*getInternetRadioStationsStruct)->radioStationCount; i++) {
|
||||
cJSON* curr_idx_root = cJSON_GetArrayItem(internetRadioStation_root, i);
|
||||
if (curr_idx_root != NULL) {
|
||||
OSS_Psoj(&(*getInternetRadioStationsStruct)->radioStations[i].id, curr_idx_root, "id");
|
||||
OSS_Psoj(&(*getInternetRadioStationsStruct)->radioStations[i].name, curr_idx_root, "name");
|
||||
OSS_Psoj(&(*getInternetRadioStationsStruct)->radioStations[i].streamUrl, curr_idx_root, "streamUrl");
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void opensubsonic_getInternetRadioStations_struct_free(opensubsonic_getInternetRadioStations_struct** getInternetRadioStationsStruct) {
|
||||
logger_log_general(__func__, "Freeing /getInternetRadioStations endpoint heap objects.");
|
||||
if ((*getInternetRadioStationsStruct)->status != NULL) { free((*getInternetRadioStationsStruct)->status); }
|
||||
if ((*getInternetRadioStationsStruct)->errorMessage != NULL) { free((*getInternetRadioStationsStruct)->errorMessage); }
|
||||
for (int i = 0; i < (*getInternetRadioStationsStruct)->radioStationCount; i++) {
|
||||
if ((*getInternetRadioStationsStruct)->radioStations[i].id != NULL) { free((*getInternetRadioStationsStruct)->radioStations[i].id); }
|
||||
if ((*getInternetRadioStationsStruct)->radioStations[i].name != NULL) { free((*getInternetRadioStationsStruct)->radioStations[i].name); }
|
||||
if ((*getInternetRadioStationsStruct)->radioStations[i].streamUrl != NULL) { free((*getInternetRadioStationsStruct)->radioStations[i].streamUrl); }
|
||||
}
|
||||
if ((*getInternetRadioStationsStruct)->radioStations != NULL) { free((*getInternetRadioStationsStruct)->radioStations); }
|
||||
if (*getInternetRadioStationsStruct != NULL) { free(*getInternetRadioStationsStruct); }
|
||||
}
|
||||
29
src/libopensubsonic/endpoint_getInternetRadioStations.h
Normal file
29
src/libopensubsonic/endpoint_getInternetRadioStations.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef _ENDPOINT_GETINTERNETRADIOSTATIONS_H
|
||||
#define _ENDPOINT_GETINTERNETRADIOSTATIONS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
typedef struct {
|
||||
char* id;
|
||||
char* name;
|
||||
char* streamUrl;
|
||||
} opensubsonic_getInternetRadioStations_radioStations_struct;
|
||||
|
||||
typedef struct {
|
||||
char* status;
|
||||
int errorCode;
|
||||
char* errorMessage;
|
||||
int radioStationCount;
|
||||
opensubsonic_getInternetRadioStations_radioStations_struct* radioStations;
|
||||
} opensubsonic_getInternetRadioStations_struct;
|
||||
|
||||
int opensubsonic_getInternetRadioStations_parse(char* data, opensubsonic_getInternetRadioStations_struct** getInternetRadioStationsStruct);
|
||||
void opensubsonic_getInternetRadioStations_struct_free(opensubsonic_getInternetRadioStations_struct** getInternetRadioStationsStruct);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _ENDPOINT_GETINTERNETRADIOSTATIONS_H
|
||||
@@ -19,6 +19,7 @@
|
||||
#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;
|
||||
@@ -214,6 +215,12 @@ void opensubsonic_httpClient_formUrl(opensubsonic_httpClient_URL_t** urlObj) {
|
||||
(*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",
|
||||
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;
|
||||
default:
|
||||
logger_log_error(__func__, "Unknown endpoint requested.");
|
||||
break;
|
||||
@@ -270,6 +277,9 @@ void opensubsonic_httpClient_fetchResponse(opensubsonic_httpClient_URL_t** urlOb
|
||||
} 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);
|
||||
} else {
|
||||
logger_log_error(__func__, "Unknown endpoint requested.");
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ extern "C" {
|
||||
#define OPENSUBSONIC_ENDPOINT_GETLYRICSBYSONGID 311
|
||||
#define OPENSUBSONIC_ENDPOINT_GETALBUMLIST 312
|
||||
#define OPENSUBSONIC_ENDPOINT_SCROBBLE 313
|
||||
#define OPENSUBSONIC_ENDPOINT_GETINTERNETRADIOSTATIONS 314
|
||||
#define OPENSUBSONIC_ENDPOINT_GETALBUMLIST_RANDOM 501
|
||||
#define OPENSUBSONIC_ENDPOINT_GETALBUMLIST_NEWEST 502
|
||||
#define OPENSUBSONIC_ENDPOINT_GETALBUMLIST_HIGHEST 503
|
||||
|
||||
257
src/localMusicHandler.cpp
Normal file
257
src/localMusicHandler.cpp
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* OpenSubsonicPlayer
|
||||
* Goldenkrew3000 2025
|
||||
* License: GNU General Public License 3.0
|
||||
* Info: Local Music File Handler
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
extern "C" {
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/dict.h>
|
||||
#include <libavformat/avio.h>
|
||||
#include "external/sqlite3/sqlite3.h"
|
||||
}
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include "configHandler.h"
|
||||
#include "localMusicHandler.hpp"
|
||||
|
||||
/*
|
||||
* I'm sorry for this messy and probably unreliable code
|
||||
* This is the first time I have ever written something like this
|
||||
* And why C++? Easy to store all of the temporary data without a ton of boilerplate
|
||||
*/
|
||||
|
||||
extern configHandler_config_t* configObj;
|
||||
std::vector<std::string> localMusicHandler_allFiles;
|
||||
class localMusicHandler_AudioObject {
|
||||
public:
|
||||
std::string path;
|
||||
std::string songTitle;
|
||||
std::string albumTitle;
|
||||
std::string artistTitle;
|
||||
std::string track;
|
||||
std::string totalTracks;
|
||||
uint64_t filesize;
|
||||
std::string uid;
|
||||
};
|
||||
std::deque<localMusicHandler_AudioObject> localMusicHandler_audioItems;
|
||||
static sqlite3* sqlite_db = NULL;
|
||||
static char* sqlite_errorMsg = NULL;
|
||||
|
||||
void localMusicHandler_scan() {
|
||||
static int rc = 0;
|
||||
printf("[LocalMusicHandler] Scanning local music directory recursively for files.\n");
|
||||
|
||||
// TODO clear all vectors
|
||||
|
||||
// Scan music directory (defined in config file) for all files
|
||||
localMusicHandler_scanDirectory(configObj->local_rootdir);
|
||||
printf("[LocalMusicHandler] Found %d files.\n", localMusicHandler_allFiles.size());
|
||||
|
||||
// Scan each file to find only music files, and pull ID3 tags
|
||||
for (int i = 0; i < localMusicHandler_allFiles.size(); i++) {
|
||||
localMusicHandler_scanFile(i);
|
||||
}
|
||||
printf("[LocalMusicHandler] Found %d songs.\n", localMusicHandler_audioItems.size());
|
||||
|
||||
// Generate a unique ID for each music file
|
||||
printf("[LocalMusicHandler] Generating unique IDs for each song.\n");
|
||||
for (int i = 0; i < localMusicHandler_audioItems.size(); i++) {
|
||||
// TODO: Technically there is a VERY SMALL chance that 2 id's repeat in the DB
|
||||
// Figure out what to do about that later
|
||||
localMusicHandler_generateUid(i);
|
||||
}
|
||||
|
||||
// Store in database
|
||||
rc = localMusicHandler_initDatabase();
|
||||
if (rc == -1) {
|
||||
// ERROR
|
||||
} else if (rc == 0) {
|
||||
// Table just made, songs not loaded in yet
|
||||
for (int i = 0; i < localMusicHandler_audioItems.size(); i++) {
|
||||
localMusicHandler_moveSongsToDatabase(i);
|
||||
}
|
||||
} else if (rc == 1) {
|
||||
// Table was already made, assume songs were loaded in before
|
||||
}
|
||||
}
|
||||
|
||||
void localMusicHandler_scanDirectory(char* directory) {
|
||||
struct dirent* dp;
|
||||
DIR* dir = opendir(directory);
|
||||
char path[1000]; // TODO Prevent potential buffer overflow
|
||||
|
||||
while ((dp = readdir(dir)) != NULL) {
|
||||
if (strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0) {
|
||||
sprintf(path, "%s/%s", directory, dp->d_name);
|
||||
|
||||
struct stat statbuf;
|
||||
stat(path, &statbuf);
|
||||
|
||||
if (S_ISDIR(statbuf.st_mode)) {
|
||||
localMusicHandler_scanDirectory(path);
|
||||
} else if (S_ISREG(statbuf.st_mode)) {
|
||||
localMusicHandler_allFiles.push_back(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
void localMusicHandler_scanFile(int idx) {
|
||||
AVFormatContext* ctx = NULL;
|
||||
AVDictionaryEntry* tag = NULL;
|
||||
static int rc = -1;
|
||||
|
||||
rc = avformat_open_input(&ctx, localMusicHandler_allFiles[idx].c_str(), NULL, NULL);
|
||||
if (rc < 0) {
|
||||
printf("[LocalMusicHandler] avformat_open_input() failed on idx %d (%s).\n", idx, localMusicHandler_allFiles[idx].c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore files that aren't audio
|
||||
if (
|
||||
strcmp(ctx->iformat->name, "lrc") == 0 || // .lrc files
|
||||
strcmp(ctx->iformat->name, "image2") == 0 || // Pictures
|
||||
strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 // .mp4 files
|
||||
) {
|
||||
avformat_close_input(&ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
localMusicHandler_AudioObject audioObject;
|
||||
audioObject.path = localMusicHandler_allFiles[idx].c_str();
|
||||
|
||||
// Get file size (Using libav for this since the file is already opened using it)
|
||||
audioObject.filesize = 0; // If the following fetch fails, set it to a known value beforehand
|
||||
if (ctx->pb) {
|
||||
uint64_t fsize = avio_size(ctx->pb);
|
||||
if (fsize > 0) {
|
||||
audioObject.filesize = fsize;
|
||||
}
|
||||
}
|
||||
|
||||
// Set all strings to known good values before fetching tags that possible don't exist
|
||||
// NOTE: Honestly don't know if C++ does this by default, but I am not trusting it either way
|
||||
audioObject.songTitle = "";
|
||||
audioObject.albumTitle = "";
|
||||
audioObject.artistTitle = "";
|
||||
audioObject.track = "";
|
||||
audioObject.totalTracks = "";
|
||||
|
||||
while ((tag = av_dict_get(ctx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
if (strcmp(tag->key, "title") == 0) {
|
||||
audioObject.songTitle = tag->value;
|
||||
} else if (strcmp(tag->key, "album") == 0) {
|
||||
audioObject.albumTitle = tag->value;
|
||||
} else if (strcmp(tag->key, "artist") == 0) {
|
||||
// In ID3, multiple artists are stored as 'Artist A;Artist B'. Replace ';' with ', '
|
||||
audioObject.artistTitle = std::regex_replace(tag->value, std::regex(";"), ", ");
|
||||
} else if (strcmp(tag->key, "track") == 0) {
|
||||
audioObject.track = tag->value;
|
||||
} else if (strcmp(tag->key, "totaltracks") == 0) {
|
||||
audioObject.totalTracks = tag->value;
|
||||
}
|
||||
}
|
||||
|
||||
localMusicHandler_audioItems.push_back(audioObject);
|
||||
avformat_close_input(&ctx);
|
||||
}
|
||||
|
||||
void localMusicHandler_generateUid(int idx) {
|
||||
// TODO: Add other operating support here, such as in libopensubsonic/crypto.c
|
||||
char uuidBytes[20];
|
||||
char uuidString[40];
|
||||
for (int i = 0; i < 20; i++) {
|
||||
uuidBytes[i] = arc4random() & 0xFF;
|
||||
}
|
||||
snprintf(uuidString, 40, "%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
|
||||
uuidBytes[0], uuidBytes[1], uuidBytes[2], uuidBytes[3], uuidBytes[4],
|
||||
uuidBytes[5], uuidBytes[6], uuidBytes[7], uuidBytes[8], uuidBytes[9],
|
||||
uuidBytes[10], uuidBytes[11], uuidBytes[12], uuidBytes[13], uuidBytes[14],
|
||||
uuidBytes[15], uuidBytes[16], uuidBytes[17], uuidBytes[18], uuidBytes[19]
|
||||
);
|
||||
localMusicHandler_audioItems[idx].uid = uuidString;
|
||||
}
|
||||
|
||||
int localMusicHandler_initDatabase() {
|
||||
// Code returns: -1 -> Error, 0 -> No songs in table, 1 -> Songs already in table (Table already existed)
|
||||
static int createTable = 0;
|
||||
static int rc = 0;
|
||||
char* dbPath = NULL;
|
||||
rc = asprintf(&dbPath, "%s/.config/ossp/local.db", getenv("HOME"));
|
||||
if (rc == -1) {
|
||||
printf("[LocalMusicHandler] asprintf() failed.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(dbPath, &st) == 0) {
|
||||
printf("[LocalMusicHandler] Database found, is %ld bytes.\n", st.st_size);
|
||||
} else {
|
||||
printf("[LocalMusicHandler] Database does not exist, creating.\n");
|
||||
createTable = 1;
|
||||
}
|
||||
|
||||
rc = sqlite3_open(dbPath, &sqlite_db);
|
||||
if (rc) {
|
||||
printf("[LocalMusicHandler] Could not create database: %s\n", sqlite3_errmsg(sqlite_db));
|
||||
free(dbPath);
|
||||
return -1;
|
||||
} else {
|
||||
printf("[LocalMusicHandler] Created/Opened database.\n");
|
||||
}
|
||||
|
||||
if (createTable == 1) {
|
||||
const char* sqlQuery = "CREATE TABLE local_songs(uid TEXT, songTitle TEXT, albumTitle TEXT, artistTitle TEXT, track TEXT, totalTracks TEXT, path TEXT, filesize INT)";
|
||||
rc = sqlite3_exec(sqlite_db, sqlQuery, NULL, 0, &sqlite_errorMsg);
|
||||
if (rc != SQLITE_OK) {
|
||||
printf("[LocalMusicHandler] Could not make table: %s\n", sqlite_errorMsg);
|
||||
sqlite3_free(sqlite_errorMsg);
|
||||
free(dbPath);
|
||||
return -1;
|
||||
}
|
||||
printf("[LocalMusicHandler] Made table.\n");
|
||||
free(dbPath);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free(dbPath);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void localMusicHandler_moveSongsToDatabase(int idx) {
|
||||
sqlite3_stmt* sqlite_stmt;
|
||||
const char* sqlQuery = "INSERT INTO local_songs VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
|
||||
if (sqlite3_prepare_v2(sqlite_db, sqlQuery, -1, &sqlite_stmt, NULL) != SQLITE_OK) {
|
||||
printf("[LocalMusicHandler] Prepare error: %s\n", sqlite3_errmsg(sqlite_db));
|
||||
return; // TODO
|
||||
}
|
||||
|
||||
sqlite3_bind_text(sqlite_stmt, 1, localMusicHandler_audioItems[idx].uid.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(sqlite_stmt, 2, localMusicHandler_audioItems[idx].songTitle.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(sqlite_stmt, 3, localMusicHandler_audioItems[idx].albumTitle.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(sqlite_stmt, 4, localMusicHandler_audioItems[idx].artistTitle.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(sqlite_stmt, 5, localMusicHandler_audioItems[idx].track.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(sqlite_stmt, 6, localMusicHandler_audioItems[idx].totalTracks.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_text(sqlite_stmt, 7, localMusicHandler_audioItems[idx].path.c_str(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_int64(sqlite_stmt, 8, localMusicHandler_audioItems[idx].filesize);
|
||||
|
||||
if (sqlite3_step(sqlite_stmt) != SQLITE_DONE) {
|
||||
printf("[LocalMusicHandler] Execution error: %s\n", sqlite3_errmsg(sqlite_db));
|
||||
}
|
||||
|
||||
sqlite3_finalize(sqlite_stmt);
|
||||
}
|
||||
26
src/localMusicHandler.hpp
Normal file
26
src/localMusicHandler.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* OpenSubsonicPlayer
|
||||
* Goldenkrew3000 2025
|
||||
* License: GNU General Public License 3.0
|
||||
*/
|
||||
|
||||
#ifndef _LOCALMUSICHANDLER_H
|
||||
#define _LOCALMUSICHANDLER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
void localMusicHandler_scan();
|
||||
void localMusicHandler_scanDirectory(char* directory);
|
||||
void localMusicHandler_scanFile(int idx);
|
||||
void localMusicHandler_generateUid(int idx);
|
||||
|
||||
int localMusicHandler_initDatabase();
|
||||
void localMusicHandler_moveSongsToDatabase(int idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
|
||||
#endif // _LOCALMUSICHANDLER_H
|
||||
@@ -20,69 +20,105 @@
|
||||
#include "playQueue.hpp"
|
||||
|
||||
// C++ interface for storing song queue data (C interface is in the header)
|
||||
class SongObject {
|
||||
class OSSPQ_SongObject {
|
||||
public:
|
||||
std::string title;
|
||||
std::string album;
|
||||
std::string artist;
|
||||
std::string id;
|
||||
std::string streamUrl;
|
||||
std::string coverArtUrl;
|
||||
long duration;
|
||||
int mode;
|
||||
};
|
||||
|
||||
// NOTE: Acronym is OpenSubsonicPlayerQueue
|
||||
std::deque<SongObject> OSSPQ_Items;
|
||||
std::deque<OSSPQ_SongObject> OSSPQ_SongQueue;
|
||||
|
||||
int internal_OSSPQ_AppendToEnd(char* title, char* artist, char* id, long duration) {
|
||||
// Append a new song to the end of the queue
|
||||
printf("Title: %s\nArtist: %s\nID: %s\nDuration: %ld\n", title, artist, id, duration);
|
||||
// TODO: Find a neater way of converting a C string to a C++ string??
|
||||
std::string cpp_title(title);
|
||||
std::string cpp_artist(artist);
|
||||
std::string cpp_id(id);
|
||||
SongObject songObject;
|
||||
songObject.title = cpp_title;
|
||||
songObject.artist = cpp_artist;
|
||||
songObject.id = cpp_id;
|
||||
songObject.duration = duration;
|
||||
OSSPQ_Items.push_back(songObject);
|
||||
int OSSPQ_AppendToEnd(char* title, char* album, char* artist, char* id, char* streamUrl, char* coverArtUrl, long duration, int mode) {
|
||||
OSSPQ_SongObject songObject;
|
||||
|
||||
if (mode == OSSPQ_MODE_OPENSUBSONIC || mode == OSSPQ_MODE_LOCALFILE) {
|
||||
// Both Local File and OpenSubsonic playback both take the same arguments
|
||||
if (mode == OSSPQ_MODE_OPENSUBSONIC) {
|
||||
printf("[OSSPQ] Appending OpenSubsonic Song: %s by %s.\n", title, artist);
|
||||
} else if (mode == OSSPQ_MODE_LOCALFILE) {
|
||||
printf("[OSSPQ] Appending Local Song: %s by %s.\n", title, artist);
|
||||
}
|
||||
|
||||
songObject.title = title;
|
||||
songObject.album = album;
|
||||
songObject.artist = artist;
|
||||
songObject.id = id;
|
||||
songObject.streamUrl = streamUrl;
|
||||
songObject.coverArtUrl = coverArtUrl;
|
||||
songObject.duration = duration;
|
||||
songObject.mode = mode;
|
||||
} else if (mode == OSSPQ_MODE_INTERNETRADIO) {
|
||||
printf("[OSSPQ] Appending Internet Radio Station: %s.\n", title);
|
||||
|
||||
songObject.title = title;
|
||||
songObject.id = id;
|
||||
songObject.streamUrl = streamUrl;
|
||||
}
|
||||
|
||||
OSSPQ_SongQueue.push_back(songObject);
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSSPQ_SongStruct* internal_OSSPQ_PopFromFront() {
|
||||
if (OSSPQ_Items.empty()) {
|
||||
// No items in play queue
|
||||
OSSPQ_SongStruct* OSSPQ_PopFromFront() {
|
||||
// Check if song queue is empty, if not, then pop oldest
|
||||
if (OSSPQ_SongQueue.empty()) {
|
||||
return NULL;
|
||||
}
|
||||
OSSPQ_SongObject songObject = OSSPQ_SongQueue.front();
|
||||
OSSPQ_SongQueue.pop_front();
|
||||
|
||||
// Pull the first song off the song queue
|
||||
SongObject songObject = OSSPQ_Items.front();
|
||||
OSSPQ_Items.pop_front();
|
||||
// Allocate, initialize, and fill C compatible song object
|
||||
OSSPQ_SongStruct* songObjectC = (OSSPQ_SongStruct*)malloc(sizeof(OSSPQ_SongStruct));
|
||||
songObjectC->title = NULL;
|
||||
songObjectC->album = NULL;
|
||||
songObjectC->artist = NULL;
|
||||
songObjectC->id = NULL;
|
||||
songObjectC->streamUrl = NULL;
|
||||
songObjectC->coverArtUrl = NULL;
|
||||
songObjectC->duration = 0;
|
||||
songObjectC->mode = 0;
|
||||
|
||||
// Move song data into a C readable format
|
||||
// NOTE: I am initializing the variables to a known value just in case there is missing information in songObject
|
||||
OSSPQ_SongStruct* playQueueObject = (OSSPQ_SongStruct*)malloc(sizeof(OSSPQ_SongStruct));
|
||||
playQueueObject->title = NULL;
|
||||
playQueueObject->artist = NULL;
|
||||
playQueueObject->id = NULL;
|
||||
playQueueObject->duration = 0;
|
||||
|
||||
playQueueObject->title = strdup(songObject.title.c_str());
|
||||
playQueueObject->artist = strdup(songObject.artist.c_str());
|
||||
playQueueObject->id = strdup(songObject.id.c_str());
|
||||
playQueueObject->duration = songObject.duration;
|
||||
return playQueueObject;
|
||||
if (songObject.mode == OSSPQ_MODE_OPENSUBSONIC || songObject.mode == OSSPQ_MODE_LOCALFILE) {
|
||||
songObjectC->title = strdup(songObject.title.c_str());
|
||||
songObjectC->album = strdup(songObject.album.c_str());
|
||||
songObjectC->artist = strdup(songObject.artist.c_str());
|
||||
songObjectC->id = strdup(songObject.id.c_str());
|
||||
songObjectC->streamUrl = strdup(songObject.streamUrl.c_str());
|
||||
songObjectC->coverArtUrl = strdup(songObject.coverArtUrl.c_str());
|
||||
songObjectC->duration = songObject.duration;
|
||||
songObjectC->mode = songObject.mode;
|
||||
} else if (songObject.mode == OSSPQ_MODE_INTERNETRADIO) {
|
||||
songObjectC->title = strdup(songObject.title.c_str());
|
||||
songObjectC->id = strdup(songObject.id.c_str());
|
||||
songObjectC->streamUrl = strdup(songObject.streamUrl.c_str());
|
||||
}
|
||||
|
||||
return songObjectC;
|
||||
}
|
||||
|
||||
void internal_OSSPQ_FreeSongObject(OSSPQ_SongStruct* songObject) {
|
||||
if (songObject->title != NULL) { free(songObject->title); }
|
||||
if (songObject->artist != NULL) { free(songObject->artist); }
|
||||
if (songObject->id != NULL) { free(songObject->id); }
|
||||
if (songObject != NULL) { free(songObject); }
|
||||
void OSSPQ_FreeSongObjectC(OSSPQ_SongStruct* songObjectC) {
|
||||
printf("[OSSPQ] Freeing SongObjectC.\n");
|
||||
if (songObjectC->title != NULL) { free(songObjectC->title); }
|
||||
if (songObjectC->album != NULL) { free(songObjectC->album); }
|
||||
if (songObjectC->artist != NULL) { free(songObjectC->artist); }
|
||||
if (songObjectC->id != NULL) { free(songObjectC->id); }
|
||||
if (songObjectC->streamUrl != NULL) { free(songObjectC->streamUrl); }
|
||||
if (songObjectC->coverArtUrl != NULL) { free(songObjectC->coverArtUrl); }
|
||||
if (songObjectC != NULL) { free(songObjectC); }
|
||||
}
|
||||
|
||||
// TODO Bullshit functions for dealing with Imgui
|
||||
char* internal_OSSPQ_GetTitleAtIndex(int idx) {
|
||||
return (char*)OSSPQ_Items[idx].title.c_str();
|
||||
return (char*)OSSPQ_SongQueue[idx].title.c_str();
|
||||
}
|
||||
|
||||
int internal_OSSPQ_GetItemCount() {
|
||||
return OSSPQ_Items.size();
|
||||
return OSSPQ_SongQueue.size();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#ifndef _PLAYQUEUE_H
|
||||
#define _PLAYQUEUE_H
|
||||
|
||||
#define OSSPQ_MODE_OPENSUBSONIC 101
|
||||
#define OSSPQ_MODE_LOCALFILE 102
|
||||
#define OSSPQ_MODE_INTERNETRADIO 103
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
@@ -14,14 +18,20 @@ extern "C" {
|
||||
// C interface for sending song queue data (C++ interface is in the C++ file)
|
||||
typedef struct {
|
||||
char* title;
|
||||
char* album;
|
||||
char* artist;
|
||||
char* id;
|
||||
char* streamUrl;
|
||||
char* coverArtUrl;
|
||||
long duration;
|
||||
int mode;
|
||||
} OSSPQ_SongStruct;
|
||||
|
||||
int internal_OSSPQ_AppendToEnd(char* title, char* artist, char* id, long duration);
|
||||
OSSPQ_SongStruct* internal_OSSPQ_PopFromFront();
|
||||
void internal_OSSPQ_FreeSongObject(OSSPQ_SongStruct* songObject);
|
||||
int OSSPQ_AppendToEnd(char* title, char* album, char* artist, char* id, char* streamUrl, char* coverArtUrl, long duration, int mode);
|
||||
OSSPQ_SongStruct* OSSPQ_PopFromFront();
|
||||
void OSSPQ_FreeSongObjectC(OSSPQ_SongStruct* songObjectC);
|
||||
|
||||
// TODO
|
||||
char* internal_OSSPQ_GetTitleAtIndex(int idx);
|
||||
int internal_OSSPQ_GetItemCount();
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "libopensubsonic/endpoint_getAlbumList.h"
|
||||
#include "libopensubsonic/endpoint_getAlbum.h"
|
||||
#include "libopensubsonic/endpoint_getLyricsBySongId.h"
|
||||
#include "libopensubsonic/endpoint_getInternetRadioStations.h"
|
||||
|
||||
configHandler_config_t* configObj = NULL;
|
||||
|
||||
@@ -179,6 +180,21 @@ void test_libopensubsonic_endpoint_getLyricsBySongId(void) {
|
||||
opensubsonic_httpClient_URL_cleanup(&url);
|
||||
}
|
||||
|
||||
void test_libopensubsonic_endpoint_getInternetRadioStations(void) {
|
||||
logger_log_general(__func__, "Testing getInternetRadioStations endpoint.");
|
||||
|
||||
opensubsonic_httpClient_URL_t* url = malloc(sizeof(opensubsonic_httpClient_URL_t));
|
||||
opensubsonic_httpClient_URL_prepare(&url);
|
||||
url->endpoint = OPENSUBSONIC_ENDPOINT_GETINTERNETRADIOSTATIONS;
|
||||
opensubsonic_httpClient_formUrl(&url);
|
||||
|
||||
opensubsonic_getInternetRadioStations_struct* getInternetRadioStationsStruct;
|
||||
opensubsonic_httpClient_fetchResponse(&url, (void**)&getInternetRadioStationsStruct);
|
||||
|
||||
opensubsonic_getInternetRadioStations_struct_free(&getInternetRadioStationsStruct);
|
||||
opensubsonic_httpClient_URL_cleanup(&url);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int rc = 0;
|
||||
|
||||
@@ -200,6 +216,7 @@ int main(void) {
|
||||
test_libopensubsonic_endpoint_getAlbumList();
|
||||
test_libopensubsonic_endpoint_getAlbum();
|
||||
test_libopensubsonic_endpoint_getLyricsBySongId();
|
||||
test_libopensubsonic_endpoint_getInternetRadioStations();
|
||||
|
||||
// Free config file
|
||||
configHandler_Free(&configObj);
|
||||
|
||||
380
src/socket.c
Normal file
380
src/socket.c
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* OpenSubsonicPlayer
|
||||
* Goldenkrew3000 2025
|
||||
* License: GNU General Public License 3.0
|
||||
* Info: Socket Handler
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include "external/cJSON.h"
|
||||
#include "configHandler.h"
|
||||
#include "socket.h"
|
||||
|
||||
#define SOCKET_PATH "/tmp/ossp_socket" // TODO Make this configurable through the configuration file
|
||||
static int server_fd = -1;
|
||||
static int client_fd = -1;
|
||||
static int rc = -1;
|
||||
socklen_t client_len;
|
||||
struct sockaddr_un server_addr;
|
||||
struct sockaddr_un client_addr;
|
||||
extern configHandler_config_t* configObj;
|
||||
|
||||
|
||||
|
||||
void socketHandler_read();
|
||||
|
||||
int SockSig_Length = 4;
|
||||
|
||||
const uint32_t OSSP_Sock_ACK = 0x7253FF87;
|
||||
const uint32_t OSSP_Sock_CliConn = 0xE3566C2E;
|
||||
const uint32_t OSSP_Sock_GetConnInfo = 0x8E4F6B01;
|
||||
const uint32_t OSSP_Sock_Size = 0x1F7E8BCF;
|
||||
|
||||
const uint32_t OSSP_Sock_ClientGetReq = 0x210829CF;
|
||||
|
||||
|
||||
|
||||
void socket_setup() {
|
||||
printf("[SocketHandler] Initializing.\n");
|
||||
|
||||
// Create server socket, and ensure that the socket file is removed
|
||||
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (server_fd == -1) {
|
||||
printf("[SocketHandler] Could not open server socket.\n");
|
||||
// TODO
|
||||
}
|
||||
|
||||
unlink(SOCKET_PATH);
|
||||
|
||||
// Bind server socket to SOCKET_PATH
|
||||
memset(&server_addr, 0, sizeof(struct sockaddr_un));
|
||||
server_addr.sun_family = AF_UNIX;
|
||||
strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);
|
||||
|
||||
rc = bind(server_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_un));
|
||||
if (rc == -1) {
|
||||
printf("[SocketHandler] Could not bind server socket.\n");
|
||||
// TODO
|
||||
}
|
||||
|
||||
rc = listen(server_fd, 5);
|
||||
if (rc == -1) {
|
||||
printf("[SocketHandler] Could not listen on server socket.\n");
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Wait for connection
|
||||
bool isServerWaiting = true;
|
||||
while (isServerWaiting) {
|
||||
client_fd = accept(server_fd, NULL, NULL); // TODO
|
||||
if (client_fd == -1) {
|
||||
printf("[SocketHandler] Error accepting connection.\n");
|
||||
} else {
|
||||
printf("[SocketHandler] Accepted connection.\n");
|
||||
isServerWaiting = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//socketHandler_read();
|
||||
socketHandler_initClientConnection();
|
||||
|
||||
|
||||
|
||||
printf("------------------------------\n");
|
||||
|
||||
|
||||
// Receive ClientGetReq with Size
|
||||
int size = 0;
|
||||
socketHandler_receiveCliGetReq(&size);
|
||||
printf("Size to alloc: %d bytes\n", size);
|
||||
char* reqBuf = malloc(size);
|
||||
|
||||
// Send ACK
|
||||
socketHandler_sendAck();
|
||||
|
||||
// Receive JSON data
|
||||
socketHandler_receiveJson(&reqBuf, size);
|
||||
printf("Received JSON: %s\n", reqBuf);
|
||||
}
|
||||
|
||||
void socketHandler_cleanup() {
|
||||
printf("[SocketHandler] Cleaning up.\n");
|
||||
|
||||
close(client_fd);
|
||||
close(server_fd);
|
||||
unlink(SOCKET_PATH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Byte Array to uint32_t Big Endian
|
||||
uint32_t util_byteArrToUint32BE(char buf[]) {
|
||||
// NOTE: I could use a combination of memcpy() and htons() here, but bitshifting is a single move
|
||||
uint32_t retVal = 0x0;
|
||||
retVal = buf[3] | buf[2] << 8 | buf[1] << 16 | buf[0] << 24;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// Byte Array to uint32_t Little Endian
|
||||
uint32_t util_byteArrToUint32LE(char buf[]) {
|
||||
uint32_t retVal = 0x0;
|
||||
retVal = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int socketHandler_initClientConnection() {
|
||||
/*
|
||||
* - Wait for client to send OSSP_Sock_CliConn
|
||||
* - Send OSSP_Sock_ACK back
|
||||
* - Wait for client to send OSSP_Sock_GetConnInfo
|
||||
* - Send back OSSP_Sock_Size
|
||||
* - Wait for client to send OSSP_Sock_ACK
|
||||
* - Send JSON
|
||||
* - Wait for client to send OSSP_Sock_ACK
|
||||
*/
|
||||
|
||||
if (socketHandler_receiveCliConn() != 0) {
|
||||
printf("[SocketHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (socketHandler_sendAck() != 0) {
|
||||
printf("[SocketHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (socketHandler_receiveGetConnInfo() != 0) {
|
||||
printf("[SockerHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Form JSON of connection info
|
||||
char* serverAddr = NULL;
|
||||
rc = asprintf(&serverAddr, "%s://%s", configObj->opensubsonic_protocol, configObj->opensubsonic_server);
|
||||
if (rc == -1) {
|
||||
printf("[SocketHandler] asprintf() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
cJSON* connInfoObj = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(connInfoObj, "ossp_version", cJSON_CreateString(configObj->internal_ossp_version));
|
||||
cJSON_AddItemToObject(connInfoObj, "server_addr", cJSON_CreateString(serverAddr));
|
||||
char* connInfoStr = cJSON_PrintUnformatted(connInfoObj);
|
||||
int connInfoLen = strlen(connInfoStr);
|
||||
free(serverAddr);
|
||||
cJSON_Delete(connInfoObj);
|
||||
|
||||
if (socketHandler_sendSize(connInfoLen) != 0) {
|
||||
printf("[SockerHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (socketHandler_receiveAck() != 0) {
|
||||
printf("[SockerHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (socketHandler_sendJson(connInfoStr, connInfoLen) != 0) {
|
||||
printf("[SockerHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (socketHandler_receiveAck() != 0) {
|
||||
printf("[SockerHandler] initClientConnection() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int socketHandler_sendAck() {
|
||||
printf("[SocketHandler] Sending OSSP_Sock_ACK.\n");
|
||||
|
||||
rc = send(client_fd, &OSSP_Sock_ACK, SockSig_Length, 0);
|
||||
if (rc != SockSig_Length) {
|
||||
printf("[SocketHandler] Failed to send OSSP_Sock_ACK.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socketHandler_receiveAck() {
|
||||
char buf[4] = { 0x00 };
|
||||
rc = read(client_fd, buf, SockSig_Length);
|
||||
if (rc != SockSig_Length) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_ACK (Signature is the wrong length).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t AckBE = util_byteArrToUint32LE(buf);
|
||||
|
||||
rc = memcmp(&AckBE, &OSSP_Sock_ACK, SockSig_Length);
|
||||
if (rc != 0) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_ACK (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_ACK, AckBE);
|
||||
return 1;
|
||||
}
|
||||
printf("[SocketHandler] Received OSSP_Sock_ACK.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int socketHandler_receiveCliConn() {
|
||||
char buf[4] = { 0x00 };
|
||||
rc = read(client_fd, buf, SockSig_Length);
|
||||
if (rc != SockSig_Length) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_CliConn (Signature is the wrong length).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//uint32_t CliConnBE = util_byteArrToUint32BE(buf);
|
||||
uint32_t CliConnBE = util_byteArrToUint32LE(buf);
|
||||
|
||||
rc = memcmp(&CliConnBE, &OSSP_Sock_CliConn, SockSig_Length);
|
||||
if (rc != 0) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_CliConn (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_CliConn, CliConnBE);
|
||||
return 1;
|
||||
}
|
||||
printf("[SocketHandler] Received OSSP_Sock_CliConn.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int socketHandler_receiveGetConnInfo() {
|
||||
char buf[4] = { 0x00 };
|
||||
rc = read(client_fd, buf, SockSig_Length);
|
||||
if (rc != SockSig_Length) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_GetConnInfo (Signature is the wrong length).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t GetConnInfoBE = util_byteArrToUint32LE(buf);
|
||||
|
||||
rc = memcmp(&GetConnInfoBE, &OSSP_Sock_GetConnInfo, SockSig_Length);
|
||||
if (rc != 0) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_GetConnInfo (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_GetConnInfo, GetConnInfoBE);
|
||||
return 1;
|
||||
}
|
||||
printf("[SocketHandler] Received OSSP_Sock_GetConnInfo.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int socketHandler_sendSize(uint32_t size) {
|
||||
printf("[SocketHandler] Sending OSSP_Sock_Size.\n");
|
||||
|
||||
OSSP_Sock_Size_t sizeData;
|
||||
sizeData.signature = OSSP_Sock_Size;
|
||||
sizeData.size = size;
|
||||
|
||||
rc = send(client_fd, (char*)&sizeData, 8, 0);
|
||||
if (rc != 8) {
|
||||
printf("[SocketHandler] Failed to send OSSP_Sock_Size.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socketHandler_receiveSize(uint32_t* size) {
|
||||
char buf[8] = { 0x00 };
|
||||
rc = read(client_fd, buf, 8);
|
||||
if (rc != 8) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_Size (Invalid size).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
OSSP_Sock_Size_t* sizeData = (OSSP_Sock_Size_t*)&buf;
|
||||
|
||||
rc = memcmp(&sizeData->signature, &OSSP_Sock_Size, 4);
|
||||
if (rc != 0) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_Size (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_Size, sizeData->signature);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[SocketHandler] Received OSSP_Sock_Size.\n");
|
||||
*size = sizeData->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int socketHandler_receiveCliGetReq(int* size) {
|
||||
char buf[8] = { 0x00 };
|
||||
rc = read(client_fd, buf, 8);
|
||||
if (rc != 8) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_ClientGetReq (Invalid size).\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
OSSP_Sock_ClientGetReq_t* clientGetReq = (OSSP_Sock_ClientGetReq_t*)&buf;
|
||||
|
||||
rc = memcmp(&clientGetReq->signature, &OSSP_Sock_ClientGetReq, 4);
|
||||
if (rc != 0) {
|
||||
printf("[SocketHandler] Failed to receive OSSP_Sock_ClientGetReq (Signature is invalid. Expected 0x%.8x, Received 0x%.8x).\n", OSSP_Sock_ClientGetReq, clientGetReq->signature);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[SocketHandler] Received OSSP_Sock_ClientGetReq.\n");
|
||||
*size = clientGetReq->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socketHandler_receiveJson(char** data, int size) {
|
||||
rc = read(client_fd, *data, size);
|
||||
if (rc != size) {
|
||||
printf("[SocketHandler] Failed to receive generic JSON data.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("[SocketHandler] Received generic JSON data.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int socketHandler_sendJson(char* json, int size) {
|
||||
printf("[SocketHandler] Sending JSON.\n");
|
||||
|
||||
rc = send(client_fd, json, size, 0);
|
||||
if (rc != size) {
|
||||
printf("[SocketHandler] Failed to send JSON.\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int socketHandler_processClientGetReq() {
|
||||
// Step 1 - Client sends GetReq with size
|
||||
|
||||
// Step 2 - Server allocates memory for future client request
|
||||
|
||||
// Step 3 - Server responds ACK
|
||||
|
||||
// Step 4 - --
|
||||
}
|
||||
30
src/socket.h
Normal file
30
src/socket.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef _SOCKET_H
|
||||
#define _SOCKET_H
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t signature;
|
||||
uint32_t size;
|
||||
} __attribute__((packed)) OSSP_Sock_Size_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t signature;
|
||||
uint32_t size;
|
||||
} __attribute__((packed)) OSSP_Sock_ClientGetReq_t;
|
||||
|
||||
void socket_setup();
|
||||
int socketHandler_initClientConnection();
|
||||
|
||||
int socketHandler_sendAck();
|
||||
int socketHandler_receiveAck();
|
||||
int socketHandler_receiveCliConn();
|
||||
int socketHandler_receiveGetConnInfo();
|
||||
|
||||
|
||||
int socketHandler_sendSize(uint32_t size);
|
||||
int socketHandler_receiveSize(uint32_t* size);
|
||||
int socketHandler_receiveCliGetReq(int* size);
|
||||
int socketHandler_receiveJson(char** data, int size);
|
||||
int socketHandler_sendJson(char* json, int size);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user