diff --git a/CMakeLists.txt b/CMakeLists.txt index 9eb144d..17b3e17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ set(SOURCES src/global.c src/mpd_client.c src/web_server.c + src/mympd_api.c src/list.c src/tiny_queue.c dist/src/mongoose/mongoose.c diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index 2d76570..ed4d733 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -2232,7 +2232,7 @@ function parseSongDetails(obj) { } function execSyscmd(cmd) { - sendAPI({"cmd": "MPD_API_SYSCMD", "data": {"cmd": cmd}}); + sendAPI({"cmd": "MYMPD_API_SYSCMD", "data": {"cmd": cmd}}); } function playlistDetails(uri) { diff --git a/src/global.c b/src/global.c index a8e3eef..151ac03 100644 --- a/src/global.c +++ b/src/global.c @@ -25,6 +25,10 @@ #include #include #include +#include +#include + +#include "tiny_queue.h" #include "global.h" int randrange(int n) { @@ -41,6 +45,18 @@ void sanitize_string(const char *data) { *cp = '_'; } +bool validate_string(const char *data) { + static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "1234567890_-. "; + const char *cp = data; + const char *end = data + strlen(data); + for (cp += strspn(cp, ok_chars); cp != end; cp += strspn(cp, ok_chars)) + return false; + return true; +} + + int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len) { if (dst_len == 0 || src_len == 0) return 0; @@ -49,3 +65,13 @@ int copy_string(char * const dest, char const * const src, size_t const dst_len, dest[max] = '\0'; return max; } + +enum mypd_cmd_ids get_cmd_id(const char *cmd) { + const char * mympd_cmd_strs[] = { MYMPD_CMDS(GEN_STR) }; + + for (unsigned i = 0; i < sizeof(mympd_cmd_strs) / sizeof(mympd_cmd_strs[0]); i++) + if (!strncmp(cmd, mympd_cmd_strs[i], strlen(mympd_cmd_strs[i]))) + return i; + + return 0; +} diff --git a/src/global.h.in b/src/global.h.in index 777289b..56424cf 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -25,8 +25,6 @@ #ifndef __GLOBAL_H__ #define __GLOBAL_H__ -#include -#include #include //architecture @@ -62,6 +60,91 @@ return len; \ } while (0) +//API cmds +#define MYMPD_CMDS(X) \ + X(MPD_API_UNKNOWN) \ + X(MPD_API_QUEUE_CLEAR) \ + X(MPD_API_QUEUE_CROP) \ + X(MPD_API_QUEUE_SAVE) \ + X(MPD_API_QUEUE_LIST) \ + X(MPD_API_QUEUE_SEARCH) \ + X(MPD_API_QUEUE_RM_TRACK) \ + X(MPD_API_QUEUE_RM_RANGE) \ + X(MPD_API_QUEUE_MOVE_TRACK) \ + X(MPD_API_QUEUE_ADD_TRACK_AFTER) \ + X(MPD_API_QUEUE_ADD_TRACK) \ + X(MPD_API_QUEUE_ADD_PLAY_TRACK) \ + X(MPD_API_QUEUE_REPLACE_TRACK) \ + X(MPD_API_QUEUE_ADD_PLAYLIST) \ + X(MPD_API_QUEUE_REPLACE_PLAYLIST) \ + X(MPD_API_QUEUE_SHUFFLE) \ + X(MPD_API_QUEUE_LAST_PLAYED) \ + X(MPD_API_PLAYLIST_CLEAR) \ + X(MPD_API_PLAYLIST_RENAME) \ + X(MPD_API_PLAYLIST_MOVE_TRACK) \ + X(MPD_API_PLAYLIST_ADD_TRACK) \ + X(MPD_API_PLAYLIST_RM_TRACK) \ + X(MPD_API_PLAYLIST_RM) \ + X(MPD_API_PLAYLIST_LIST) \ + X(MPD_API_PLAYLIST_CONTENT_LIST) \ + X(MPD_API_SMARTPLS_UPDATE_ALL) \ + X(MPD_API_SMARTPLS_SAVE) \ + X(MPD_API_SMARTPLS_GET) \ + X(MPD_API_DATABASE_SEARCH_ADV) \ + X(MPD_API_DATABASE_SEARCH) \ + X(MPD_API_DATABASE_UPDATE) \ + X(MPD_API_DATABASE_RESCAN) \ + X(MPD_API_DATABASE_FILESYSTEM_LIST) \ + X(MPD_API_DATABASE_TAG_LIST) \ + X(MPD_API_DATABASE_TAG_ALBUM_LIST) \ + X(MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST) \ + X(MPD_API_DATABASE_STATS) \ + X(MPD_API_DATABASE_SONGDETAILS) \ + X(MPD_API_PLAYER_PLAY_TRACK) \ + X(MPD_API_PLAYER_VOLUME_SET) \ + X(MPD_API_PLAYER_VOLUME_GET) \ + X(MPD_API_PLAYER_PAUSE) \ + X(MPD_API_PLAYER_PLAY) \ + X(MPD_API_PLAYER_STOP) \ + X(MPD_API_PLAYER_SEEK) \ + X(MPD_API_PLAYER_NEXT) \ + X(MPD_API_PLAYER_PREV) \ + X(MPD_API_PLAYER_OUTPUT_LIST) \ + X(MPD_API_PLAYER_TOGGLE_OUTPUT) \ + X(MPD_API_PLAYER_CURRENT_SONG) \ + X(MPD_API_PLAYER_STATE) \ + X(MPD_API_SETTINGS_GET) \ + X(MPD_API_SETTINGS_SET) \ + X(MPD_API_LIKE) \ + X(MPD_API_COLS_SAVE) \ + X(MYMPD_API_SYSCMD) + +#define GEN_ENUM(X) X, +#define GEN_STR(X) #X, + +enum mypd_cmd_ids { + MYMPD_CMDS(GEN_ENUM) +}; + +//message queue +tiny_queue_t *web_server_queue; +tiny_queue_t *mpd_client_queue; +tiny_queue_t *mympd_api_queue; + +struct work_request_t { + long conn_id; // needed to identify the connection where to send the reply + char data[1000]; + int length; + enum mypd_cmd_ids cmd_id; +} work_request_t; + +struct work_result_t { + long conn_id; // needed to identify the connection where to send the reply + char data[MAX_SIZE]; + int length; +} work_result_t; + + //signal handler sig_atomic_t s_signal_received; @@ -94,6 +177,7 @@ typedef struct { const char *streamurl; unsigned long last_played_count; long loglevel; + void *syscmd_list; } t_config; t_config config; @@ -101,5 +185,7 @@ t_config config; //global functions int randrange(int n); void sanitize_string(const char *data); +bool validate_string(const char *data); int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len); +enum mypd_cmd_ids get_cmd_id(const char *cmd); #endif diff --git a/src/list.h b/src/list.h index 607ff84..98d6509 100644 --- a/src/list.h +++ b/src/list.h @@ -1,5 +1,5 @@ /* myMPD - (c) 2018 Juergen Mang + (c) 2018-2019 Juergen Mang This project's homepage is: https://github.com/jcorporation/mympd This linked list implementation is based on: https://github.com/joshkunz/ashuffle @@ -27,13 +27,11 @@ struct node { struct node *next; }; - struct list { unsigned length; struct node *list; }; - int list_init(struct list *l); int list_push(struct list *l, const char *data, int value); int list_insert(struct list *l, const char *data, int value); diff --git a/src/main.c b/src/main.c index 46b176a..03ba8f1 100644 --- a/src/main.c +++ b/src/main.c @@ -33,11 +33,15 @@ #include #include #include +#include -#include "../dist/src/inih/ini.h" +#include "list.h" +#include "tiny_queue.h" #include "global.h" #include "mpd_client.h" #include "web_server.h" +#include "mympd_api.h" +#include "../dist/src/inih/ini.h" #include "../dist/src/mongoose/mongoose.h" static void signal_handler(int sig_num) { @@ -137,28 +141,31 @@ static int inihandler(void* user, const char* section, const char* name, const c return 1; } -void read_syscmds() { +void read_syscmds(void *arg_config) { DIR *dir; struct dirent *ent; char dirname[400]; char *cmd; long order; - if (config.syscmds == true) { - snprintf(dirname, 400, "%s/syscmds", config.etcdir); - LOG_INFO() printf("Reading syscmds: %s\n", dirname); + t_config *config = (t_config *) arg_config; + + if (config->syscmds == true) { + snprintf(dirname, 400, "%s/syscmds", config->etcdir); + LOG_INFO2() printf("Reading syscmds: %s\n", dirname); if ((dir = opendir (dirname)) != NULL) { while ((ent = readdir(dir)) != NULL) { if (strncmp(ent->d_name, ".", 1) == 0) continue; order = strtol(ent->d_name, &cmd, 10); if (strcmp(cmd, "") != 0) - list_push(&syscmds, strdup(cmd), order); + list_push(config->syscmd_list, strdup(cmd), order); } closedir(dir); } } - else - LOG_INFO() printf("Syscmds are disabled\n"); + else { + LOG_INFO2() printf("Syscmds are disabled\n"); + } } void read_statefiles() { @@ -310,6 +317,7 @@ int main(int argc, char **argv) { s_signal_received = 0; char testdirname[400]; mpd_client_queue = tiny_queue_create(); + mympd_api_queue = tiny_queue_create(); web_server_queue = tiny_queue_create(); srand((unsigned int)time(NULL)); @@ -387,6 +395,7 @@ int main(int argc, char **argv) { if (!web_server_init(&mgr, &config)) { return EXIT_FAILURE; } + //drop privileges if (config.user != NULL) { LOG_INFO() printf("Droping privileges to %s\n", config.user); @@ -446,9 +455,10 @@ int main(int argc, char **argv) { read_statefiles(); //read system command files - list_init(&syscmds); - read_syscmds(); - list_sort_by_value(&syscmds, true); + config.syscmd_list = malloc(sizeof(struct list)); + list_init(config.syscmd_list); + read_syscmds(&config); + list_sort_by_value(config.syscmd_list, true); //init lists for tag handling list_init(&mpd_tags); @@ -459,11 +469,13 @@ int main(int argc, char **argv) { LOG_INFO() printf("Reading last played songs: %d\n", read_last_played()); //Create working threads - pthread_t mpd_client_thread, web_server_thread; + pthread_t mpd_client_thread, web_server_thread, mympd_api_thread; //mpd connection pthread_create(&mpd_client_thread, NULL, mpd_client_loop, NULL); //webserver pthread_create(&web_server_thread, NULL, web_server_loop, &mgr); + //mympd api + pthread_create(&mympd_api_thread, NULL, mympd_api_loop, &config); //Do nothing... @@ -473,7 +485,9 @@ int main(int argc, char **argv) { pthread_join(web_server_thread, NULL); list_free(&mpd_tags); list_free(&mympd_tags); + list_free(config.syscmd_list); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); + tiny_queue_free(mympd_api_queue); return EXIT_SUCCESS; } diff --git a/src/mpd_client.c b/src/mpd_client.c index d0a5d76..d27b176 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -33,57 +33,33 @@ #include #include +#include "list.h" +#include "tiny_queue.h" +#include "global.h" #include "mpd_client.h" #include "../dist/src/frozen/frozen.h" -const char * mpd_cmd_strs[] = { - MPD_CMDS(GEN_STR) -}; - -static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { - for (unsigned i = 0; i < sizeof(mpd_cmd_strs) / sizeof(mpd_cmd_strs[0]); i++) - if (!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i]))) - return i; - - return 0; -} - -void mpd_client_api(struct work_request_t *request) { - size_t n = 0; - char *cmd; +void mpd_client_api(void *arg_request) { + struct work_request_t *request = (struct work_request_t*) arg_request; + size_t len = 0; unsigned int uint_buf1, uint_buf2, uint_rc; int je, int_buf1, int_rc; float float_buf; bool bool_buf; char *p_charbuf1, *p_charbuf2, *p_charbuf3, *p_charbuf4; char p_char[4]; - enum mpd_cmd_ids cmd_id; #ifdef DEBUG struct timespec start, end; #endif - + LOG_VERBOSE() printf("API request: %.*s\n", request->length, request->data); - je = json_scanf(request->data, request->length, "{cmd: %Q}", &cmd); - if (je == 1) { - cmd_id = get_cmd_id(cmd); - } - else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid API request.\"}"); - printf("Error: Invalid API request.\n"); - struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t)); - response->conn_id = request->conn_id; - response->length = copy_string(response->data, mpd.buf, MAX_SIZE, n); - tiny_queue_push(web_server_queue, response); - return; - } - #ifdef DEBUG clock_gettime(CLOCK_MONOTONIC_RAW, &start); #endif - switch(cmd_id) { + switch(request->cmd_id) { case MPD_API_UNKNOWN: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); printf("Unknown API request: %.*s\n", request->length, request->data); break; case MPD_API_LIKE: @@ -91,14 +67,14 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {uri: %Q, like: %d}}", &p_charbuf1, &uint_buf1); if (je == 2) { if (!mpd_client_like_song_uri(p_charbuf1, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } } else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}"); printf("MPD_API_LIKE: MPD stickers are disabled\n"); } break; @@ -108,9 +84,9 @@ void mpd_client_api(struct work_request_t *request) { char column_list[800]; snprintf(column_list, 800, "%.*s", request->length, request->data); char *cols = strchr(column_list, '['); - int len = strlen(cols); - if (len > 1) - cols[len - 2] = '\0'; + int col_len = strlen(cols); + if (col_len > 1) + cols[col_len - 2] = '\0'; if (strcmp(p_charbuf1, "colsQueueCurrent") == 0) { free(mympd_state.colsQueueCurrent); mympd_state.colsQueueCurrent = strdup(cols); @@ -140,142 +116,125 @@ void mpd_client_api(struct work_request_t *request) { mympd_state.colsQueueLastPlayed = strdup(cols); } else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown table %s\"}", p_charbuf1); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown table %s\"}", p_charbuf1); printf("MPD_API_COLS_SAVE: Unknown table %s\n", p_charbuf1); free(p_charbuf1); break; } - if (n == 0) { + if (len == 0) { if (mpd_client_state_set(p_charbuf1, cols)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } free(p_charbuf1); } break; - case MPD_API_SYSCMD: - if (config.syscmds == true) { - je = json_scanf(request->data, request->length, "{data: {cmd: %Q}}", &p_charbuf1); - if (je == 1) { - int_buf1 = list_get_value(&syscmds, p_charbuf1); - if (int_buf1 > -1) - n = mpd_client_syscmd(mpd.buf, p_charbuf1, int_buf1); - else { - printf("MPD_API_SYSCMD: Syscmd not defined: %s\n", p_charbuf1); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System command not defined\"}"); - } - free(p_charbuf1); - } - } - else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); - break; case MPD_API_PLAYER_STATE: - n = mpd_client_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); + len = mpd_client_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); break; case MPD_API_SETTINGS_SET: je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mympd_state.notificationWeb); if (je == 1) if (!mpd_client_state_set("notificationWeb", (mympd_state.notificationWeb == true ? "true" : "false"))) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mympd_state.notificationPage); if (je == 1) if (!mpd_client_state_set("notificationPage", (mympd_state.notificationPage == true ? "true" : "false"))) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); je = json_scanf(request->data, request->length, "{data: {jukeboxMode: %d}}", &mympd_state.jukeboxMode); if (je == 1) { snprintf(p_char, 4, "%d", mympd_state.jukeboxMode); if (!mpd_client_state_set("jukeboxMode", p_char)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); } je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mympd_state.jukeboxPlaylist); if (je == 1) if (!mpd_client_state_set("jukeboxPlaylist", mympd_state.jukeboxPlaylist)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); je = json_scanf(request->data, request->length, "{data: {jukeboxQueueLength: %d}}", &mympd_state.jukeboxQueueLength); if (je == 1) { snprintf(p_char, 4, "%d", mympd_state.jukeboxQueueLength); if (!mpd_client_state_set("jukeboxQueueLength", p_char)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); } je = json_scanf(request->data, request->length, "{data: {random: %u}}", &uint_buf1); if (je == 1) if (!mpd_run_random(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state random.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state random.\"}"); je = json_scanf(request->data, request->length, "{data: {repeat: %u}}", &uint_buf1); if (je == 1) if (!mpd_run_repeat(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state repeat.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state repeat.\"}"); je = json_scanf(request->data, request->length, "{data: {consume: %u}}", &uint_buf1); if (je == 1) if (!mpd_run_consume(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state consume.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state consume.\"}"); je = json_scanf(request->data, request->length, "{data: {single: %u}}", &uint_buf1); if (je == 1) if (!mpd_run_single(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state single.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state single.\"}"); je = json_scanf(request->data, request->length, "{data: {crossfade: %u}}", &uint_buf1); if (je == 1) if (!mpd_run_crossfade(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state crossfade.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state crossfade.\"}"); if (config.mixramp) { je = json_scanf(request->data, request->length, "{data: {mixrampdb: %f}}", &float_buf); if (je == 1) if (!mpd_run_mixrampdb(mpd.conn, float_buf)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdb.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdb.\"}"); je = json_scanf(request->data, request->length, "{data: {mixrampdelay: %f}}", &float_buf); if (je == 1) if (!mpd_run_mixrampdelay(mpd.conn, float_buf)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdelay.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdelay.\"}"); } je = json_scanf(request->data, request->length, "{data: {replaygain: %Q}}", &p_charbuf1); if (je == 1) { if (!mpd_send_command(mpd.conn, "replay_gain_mode", p_charbuf1, NULL)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}"); mpd_response_finish(mpd.conn); free(p_charbuf1); } if (mympd_state.jukeboxMode > 0) mpd_client_jukebox(); - if (n == 0) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (len == 0) + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_DATABASE_UPDATE: uint_rc = mpd_run_update(mpd.conn, NULL); if (uint_rc > 0) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_DATABASE_RESCAN: uint_rc = mpd_run_rescan(mpd.conn, NULL); if (uint_rc > 0) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_SMARTPLS_UPDATE_ALL: uint_rc = mpd_client_smartpls_update_all(); if (uint_rc == 0) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}"); else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}"); break; case MPD_API_SMARTPLS_SAVE: je = json_scanf(request->data, request->length, "{data: {type: %Q}}", &p_charbuf1); - n = 1; + len = 1; if (je == 1) { if (strcmp(p_charbuf1, "sticker") == 0) { je = json_scanf(request->data, request->length, "{data: {playlist: %Q, sticker: %Q, maxentries: %d}}", &p_charbuf2, &p_charbuf3, &int_buf1); if (je == 3) { - n = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); + len = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); free(p_charbuf2); free(p_charbuf3); } @@ -283,14 +242,14 @@ void mpd_client_api(struct work_request_t *request) { else if (strcmp(p_charbuf1, "newest") == 0) { je = json_scanf(request->data, request->length, "{data: {playlist: %Q, timerange: %d}}", &p_charbuf2, &int_buf1); if (je == 2) { - n = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, NULL, NULL, 0, int_buf1); + len = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, NULL, NULL, 0, int_buf1); free(p_charbuf2); } } else if (strcmp(p_charbuf1, "search") == 0) { je = json_scanf(request->data, request->length, "{data: {playlist: %Q, tag: %Q, searchstr: %Q}}", &p_charbuf2, &p_charbuf3, &p_charbuf4); if (je == 3) { - n = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); + len = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); free(p_charbuf2); free(p_charbuf3); free(p_charbuf4); @@ -298,31 +257,31 @@ void mpd_client_api(struct work_request_t *request) { } free(p_charbuf1); } - if (n == 0) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (len == 0) + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving playlist failed\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving playlist failed\"}"); break; case MPD_API_SMARTPLS_GET: je = json_scanf(request->data, request->length, "{data: {playlist: %Q}}", &p_charbuf1); if (je == 1) { - n = mpd_client_smartpls_put(mpd.buf, p_charbuf1); + len = mpd_client_smartpls_put(mpd.buf, p_charbuf1); free(p_charbuf1); } break; case MPD_API_PLAYER_PAUSE: if (mpd_run_toggle_pause(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Toggling player pause failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Toggling player pause failed.\"}"); printf("MPD_API_PLAYER_PAUSE: Error mpd_run_toggle_pause()\n"); } break; case MPD_API_PLAYER_PREV: if (mpd_run_previous(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Goto previous song failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Goto previous song failed.\"}"); printf("MPD_API_PLAYER_PREV: Error mpd_run_previous()\n"); } break; @@ -330,46 +289,46 @@ void mpd_client_api(struct work_request_t *request) { if (config.stickers) mpd_client_count_song_id(mpd.song_id, "skipCount", 1); if (mpd_run_next(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Skip to next song failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Skip to next song failed.\"}"); printf("MPD_API_PLAYER_NEXT: Error mpd_run_next()\n"); } break; case MPD_API_PLAYER_PLAY: if (mpd_run_play(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Begin to play failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Begin to play failed.\"}"); printf("MPD_API_PLAYER_PLAY: Error mpd_run_play()\n"); } break; case MPD_API_PLAYER_STOP: if (mpd_run_stop(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Stopping player failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Stopping player failed.\"}"); printf("MPD_API_PLAYER_STOP: Error mpd_run_stop()\n"); } break; case MPD_API_QUEUE_CLEAR: if (mpd_run_clear(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); printf("MPD_API_QUEUE_CLEAR: Error mpd_run_clear()\n"); } break; case MPD_API_QUEUE_CROP: - n = mpd_client_queue_crop(mpd.buf); + len = mpd_client_queue_crop(mpd.buf); break; case MPD_API_QUEUE_RM_TRACK: je = json_scanf(request->data, request->length, "{data: {track:%u}}", &uint_buf1); if (je == 1) { if (mpd_run_delete_id(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from queue failed.\"}"); printf("MPD_API_QUEUE_RM_TRACK: Error mpd_run_delete_id()\n"); } } @@ -378,9 +337,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {start: %u, end: %u}}", &uint_buf1, &uint_buf2); if (je == 2) { if (mpd_run_delete_range(mpd.conn, uint_buf1, uint_buf2)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track range from queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track range from queue failed.\"}"); printf("MPD_API_QUEUE_RM_RANGE: Error mpd_run_delete_range()\n"); } } @@ -393,9 +352,9 @@ void mpd_client_api(struct work_request_t *request) { if (uint_buf1 < uint_buf2) uint_buf2--; if (mpd_run_move(mpd.conn, uint_buf1, uint_buf2)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in queue failed.\"}"); printf("MPD_API_QUEUE_MOVE_TRACK: Error mpd_run_move()\n"); } } @@ -409,10 +368,10 @@ void mpd_client_api(struct work_request_t *request) { uint_buf2--; if (mpd_send_playlist_move(mpd.conn, p_charbuf1, uint_buf1, uint_buf2)) { mpd_response_finish(mpd.conn); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in playlist failed.\"}"); printf("MPD_API_PLAYLIST_MOVE_TRACK: Error mpd_send_playlist_move()\n"); } free(p_charbuf1); @@ -422,32 +381,32 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: { track:%u}}", &uint_buf1); if (je == 1) { if (mpd_run_play_id(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Set playing track failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Set playing track failed.\"}"); printf("MPD_API_PLAYER_PLAY_TRACK: Error mpd_run_play_id()\n"); } } break; case MPD_API_PLAYER_OUTPUT_LIST: - n = mpd_client_put_outputs(mpd.buf); + len = mpd_client_put_outputs(mpd.buf); break; case MPD_API_PLAYER_TOGGLE_OUTPUT: je = json_scanf(request->data, request->length, "{data: {output: %u, state: %u}}", &uint_buf1, &uint_buf2); if (je == 2) { if (uint_buf2) { if (mpd_run_enable_output(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Enabling output failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Enabling output failed.\"}"); printf("MPD_API_PLAYER_TOGGLE_OUTPUT: Error mpd_run_enable_output()\n"); } } else { if (mpd_run_disable_output(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Disabling output failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Disabling output failed.\"}"); printf("MPD_API_PLAYER_TOGGLE_OUTPUT: Error mpd_run_disable_output()\n"); } } @@ -457,23 +416,23 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {volume:%u}}", &uint_buf1); if (je == 1) { if (mpd_run_set_volume(mpd.conn, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting volume failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting volume failed.\"}"); printf("MPD_API_PLAYER_PLAY_TRACK: Error mpd_run_set_volume()\n"); } } break; case MPD_API_PLAYER_VOLUME_GET: - n = mpd_client_put_volume(mpd.buf); + len = mpd_client_put_volume(mpd.buf); break; case MPD_API_PLAYER_SEEK: je = json_scanf(request->data, request->length, "{data: {songid: %u, seek: %u}}", &uint_buf1, &uint_buf2); if (je == 2) { if (mpd_run_seek_id(mpd.conn, uint_buf1, uint_buf2)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Seeking song failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Seeking song failed.\"}"); printf("MPD_API_PLAYER_SEEK: Error mpd_run_seek_id()\n"); } } @@ -481,29 +440,29 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_QUEUE_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { - n = mpd_client_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length); + len = mpd_client_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length); } break; case MPD_API_QUEUE_LAST_PLAYED: je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { - n = mpd_client_put_last_played_songs(mpd.buf, uint_buf1); + len = mpd_client_put_last_played_songs(mpd.buf, uint_buf1); } break; case MPD_API_PLAYER_CURRENT_SONG: - n = mpd_client_put_current_song(mpd.buf); + len = mpd_client_put_current_song(mpd.buf); break; case MPD_API_DATABASE_SONGDETAILS: je = json_scanf(request->data, request->length, "{data: { uri: %Q}}", &p_charbuf1); if (je == 1) { - n = mpd_client_put_songdetails(mpd.buf, p_charbuf1); + len = mpd_client_put_songdetails(mpd.buf, p_charbuf1); free(p_charbuf1); } break; case MPD_API_DATABASE_TAG_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); if (je == 3) { - n = mpd_client_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1); + len = mpd_client_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -511,7 +470,7 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_DATABASE_TAG_ALBUM_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q, search: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2, &p_charbuf3); if (je == 4) { - n = mpd_client_put_db_tag(mpd.buf, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); + len = mpd_client_put_db_tag(mpd.buf, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -520,7 +479,7 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST: je = json_scanf(request->data, request->length, "{data: {album: %Q, search: %Q, tag: %Q}}", &p_charbuf1, &p_charbuf2, &p_charbuf3); if (je == 3) { - n = mpd_client_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3); + len = mpd_client_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -539,24 +498,24 @@ void mpd_client_api(struct work_request_t *request) { if (access(old_pl_file, F_OK ) != -1) { if (access(new_pl_file, F_OK ) == -1) { if (rename(old_pl_file, new_pl_file) == -1) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Rename failed()\n"); } //rename mpd playlist else if (mpd_run_rename(mpd.conn, p_charbuf1, p_charbuf2)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Error mpd_run_rename()\n"); } } else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Destination playlist %s already exists\"}", p_charbuf2); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Destination playlist %s already exists\"}", p_charbuf2); } else { if (mpd_run_rename(mpd.conn, p_charbuf1, p_charbuf2)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Error mpd_run_rename()\n"); } } @@ -567,14 +526,14 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_PLAYLIST_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1); if (je == 2) { - n = mpd_client_put_playlists(mpd.buf, uint_buf1, p_charbuf1); + len = mpd_client_put_playlists(mpd.buf, uint_buf1, p_charbuf1); free(p_charbuf1); } break; case MPD_API_PLAYLIST_CONTENT_LIST: je = json_scanf(request->data, request->length, "{data: {uri: %Q, offset:%u, filter:%Q}}", &p_charbuf1, &uint_buf1, &p_charbuf2); if (je == 3) { - n = mpd_client_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + len = mpd_client_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -583,9 +542,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {plist:%Q, uri:%Q}}", &p_charbuf1, &p_charbuf2); if (je == 2) { if (mpd_run_playlist_add(mpd.conn, p_charbuf1, p_charbuf2)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding song to playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding song to playlist failed.\"}"); printf("MPD_API_PLAYLIST_ADD_TRACK: Error mpd_run_playlist_add()\n"); } free(p_charbuf1); @@ -596,9 +555,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_playlist_clear(mpd.conn, p_charbuf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); printf("MPD_API_PLAYLIST_CLEAR: Error mpd_run_playlist_clear()\n"); } free(p_charbuf1); @@ -608,9 +567,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {uri:%Q, track:%u}}", &p_charbuf1, &uint_buf1); if (je == 2) { if (mpd_run_playlist_delete(mpd.conn, p_charbuf1, uint_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from playlist failed.\"}"); printf("MPD_API_PLAYLIST_RM_TRACK: Error mpd_run_playlist_delete()\n"); } free(p_charbuf1); @@ -619,7 +578,7 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_DATABASE_FILESYSTEM_LIST: je = json_scanf(request->data, request->length, "{data: {offset:%u, filter:%Q, path:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); if (je == 3) { - n = mpd_client_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); + len = mpd_client_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -629,9 +588,9 @@ void mpd_client_api(struct work_request_t *request) { if (je == 2) { int_rc = mpd_run_add_id_to(mpd.conn, p_charbuf1, int_buf1); if (int_rc > -1 ) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); printf("MPD_API_QUEUE_ADD_TRACK_AFTER: Error mpd_run_add_id_to()\n"); } free(p_charbuf1); @@ -641,19 +600,19 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {uri:%Q }}", &p_charbuf1); if (je == 1) { if (!mpd_run_clear(mpd.conn)) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); printf("MPD_API_QUEUE_REPLACE_TRACK: Error mpd_run_add_id_to()\n"); } else if (!mpd_run_add(mpd.conn, p_charbuf1)) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); printf("MPD_API_QUEUE_REPLACE_TRACK: Error mpd_run_add_id_to()\n"); } else if (!mpd_run_play(mpd.conn)) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Playing failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Playing failed.\"}"); printf("MPD_API_QUEUE_REPLACE_TRACK: Error mpd_run_play()\n"); } else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } break; @@ -661,9 +620,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_add(mpd.conn, p_charbuf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Append track to queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Append track to queue failed.\"}"); printf("MPD_API_QUEUE_ADD_TRACK: Error mpd_run_add()\n"); } free(p_charbuf1); @@ -675,14 +634,14 @@ void mpd_client_api(struct work_request_t *request) { int_buf1 = mpd_run_add_id(mpd.conn, p_charbuf1); if (int_buf1 != -1) { if (mpd_run_play_id(mpd.conn, int_buf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); printf("MPD_API_QUEUE_ADD_PLAY_TRACK: Error mpd_run_play_id()\n"); } } else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); printf("MPD_API_QUEUE_ADD_PLAY_TRACK: Error mpd_run_add_id()\n"); } free(p_charbuf1); @@ -692,19 +651,19 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1); if (je == 1) { if (!mpd_run_clear(mpd.conn)) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); printf("MPD_API_QUEUE_REPLACE_PLAYLIST: Error mpd_run_clear()\n"); } else if (!mpd_run_load(mpd.conn, p_charbuf1)) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); printf("MPD_API_QUEUE_REPLACE_PLAYLIST: Error mpd_run_load()\n"); } else if (!mpd_run_play(mpd.conn)) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); printf("MPD_API_QUEUE_REPLACE_PLAYLIST: Error mpd_run_play()\n"); } else - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } break; @@ -712,9 +671,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_load(mpd.conn, p_charbuf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); printf("MPD_API_QUEUE_ADD_PLAYLIST: Error mpd_run_add_id()\n"); } free(p_charbuf1); @@ -724,9 +683,9 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{ data: {plist:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_save(mpd.conn, p_charbuf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving queue as playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving queue as playlist failed.\"}"); printf("MPD_API_QUEUE_SAVE: Error mpd_run_save()\n"); } free(p_charbuf1); @@ -735,7 +694,7 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_QUEUE_SEARCH: je = json_scanf(request->data, request->length, "{data: {offset:%u, filter:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); if (je == 3) { - n = mpd_client_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + len = mpd_client_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -743,7 +702,7 @@ void mpd_client_api(struct work_request_t *request) { case MPD_API_DATABASE_SEARCH: je = json_scanf(request->data, request->length, "{data: {searchstr:%Q, filter:%Q, plist:%Q, offset:%u}}", &p_charbuf1, &p_charbuf2, &p_charbuf3, &uint_buf1); if (je == 4) { - n = mpd_client_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); + len = mpd_client_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); } @@ -752,7 +711,7 @@ void mpd_client_api(struct work_request_t *request) { je = json_scanf(request->data, request->length, "{data: {expression:%Q, sort:%Q, sortdesc:%B, plist:%Q, offset:%u}}", &p_charbuf1, &p_charbuf2, &bool_buf, &p_charbuf3, &uint_buf1); if (je == 5) { - n = mpd_client_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); + len = mpd_client_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -760,9 +719,9 @@ void mpd_client_api(struct work_request_t *request) { break; case MPD_API_QUEUE_SHUFFLE: if (mpd_run_shuffle(mpd.conn)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Shuffling queue failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Shuffling queue failed.\"}"); printf("MPD_API_QUEUE_SHUFFLE: Error mpd_run_shuffle()\n"); } break; @@ -775,7 +734,7 @@ void mpd_client_api(struct work_request_t *request) { snprintf(pl_file, 400, "%s/smartpls/%s", config.varlibdir, p_charbuf1); if (access(pl_file, F_OK ) != -1 ) { if (unlink(pl_file) == -1) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting smart playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting smart playlist failed.\"}"); printf("MPD_API_PLAYLIST_RM: Error unlinking smart playlist file()\n"); free(p_charbuf1); break; @@ -783,25 +742,25 @@ void mpd_client_api(struct work_request_t *request) { } //remove mpd playlist if (mpd_run_rm(mpd.conn, p_charbuf1)) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting playlist failed.\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting playlist failed.\"}"); printf("MPD_API_QUEUE_SHUFFLE: Error mpd_run_rm()\n"); } free(p_charbuf1); } break; case MPD_API_SETTINGS_GET: - n = mpd_client_put_settings(mpd.buf); + len = mpd_client_put_settings(mpd.buf, &config); break; case MPD_API_DATABASE_STATS: - n = mpd_client_put_stats(mpd.buf); + len = mpd_client_put_stats(mpd.buf); break; } if (mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { printf("Error: %s\n", mpd_connection_get_error_message(mpd.conn)); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); /* Try to recover error */ @@ -819,17 +778,16 @@ void mpd_client_api(struct work_request_t *request) { #endif #endif - if (n == 0) { - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd %s.\"}", cmd); + if (len == 0) { + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd_id %u.\"}", request->cmd_id); } LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response to connection %lu (first 800 chars):\n%*.*s\n", request->conn_id, 0, 800, mpd.buf); struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t)); response->conn_id = request->conn_id; - response->length = copy_string(response->data, mpd.buf, MAX_SIZE, n); + response->length = copy_string(response->data, mpd.buf, MAX_SIZE, len); tiny_queue_push(web_server_queue, response); - free(cmd); free(request); } @@ -843,7 +801,7 @@ void mpd_client_notify(size_t len) { } void mpd_client_parse_idle(int idle_bitmask) { - size_t n = 0; + size_t len = 0; for (unsigned j = 0;; j++) { enum mpd_idle idle_event = 1 << j; const char *idle_name = mpd_idle_name(idle_event); @@ -853,19 +811,19 @@ void mpd_client_parse_idle(int idle_bitmask) { LOG_VERBOSE() printf("MPD idle event: %s\n", idle_name); switch(idle_event) { case MPD_IDLE_DATABASE: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); mpd_client_smartpls_update_all(); break; case MPD_IDLE_STORED_PLAYLIST: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); break; case MPD_IDLE_QUEUE: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); if (mympd_state.jukeboxMode > 0) mpd_client_jukebox(); break; case MPD_IDLE_PLAYER: - n = mpd_client_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); + len = mpd_client_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); if (mpd.song_id != mpd.last_song_id) { if (mpd.last_last_played_id != mpd.song_id) mpd_client_last_played_list(mpd.song_id); @@ -877,31 +835,31 @@ void mpd_client_parse_idle(int idle_bitmask) { } break; case MPD_IDLE_MIXER: - n = mpd_client_put_volume(mpd.buf); + len = mpd_client_put_volume(mpd.buf); break; case MPD_IDLE_OUTPUT: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}"); break; case MPD_IDLE_OPTIONS: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}"); break; case MPD_IDLE_UPDATE: - n = mpd_client_get_updatedb_state(mpd.buf); + len = mpd_client_get_updatedb_state(mpd.buf); break; case MPD_IDLE_STICKER: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}"); break; case MPD_IDLE_SUBSCRIPTION: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}"); break; case MPD_IDLE_MESSAGE: - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}"); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}"); break; default: - n = 0; + len = 0; } - if (n > 0) { - mpd_client_notify(n); + if (len > 0) { + mpd_client_notify(len); } } } @@ -1033,7 +991,7 @@ void mpd_client_mpd_features() { void mpd_client_idle(int timeout) { struct pollfd fds[1]; int pollrc; - size_t n = 0; + size_t len = 0; switch (mpd.conn_state) { case MPD_DISCONNECTED: @@ -1042,24 +1000,24 @@ void mpd_client_idle(int timeout) { mpd.conn = mpd_connection_new(config.mpdhost, config.mpdport, mpd.timeout); if (mpd.conn == NULL) { printf("MPD connection failed."); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mpd_client_notify(n); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); + mpd_client_notify(len); mpd.conn_state = MPD_FAILURE; return; } if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) { printf("MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); - mpd_client_notify(n); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); + mpd_client_notify(len); mpd.conn_state = MPD_FAILURE; return; } if (config.mpdpass && !mpd_run_password(mpd.conn, config.mpdpass)) { printf("MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn)); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); - mpd_client_notify(n); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); + mpd_client_notify(len); mpd.conn_state = MPD_FAILURE; return; } @@ -1076,8 +1034,8 @@ void mpd_client_idle(int timeout) { case MPD_FAILURE: printf("MPD connection failed.\n"); - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mpd_client_notify(n); + len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); + mpd_client_notify(len); case MPD_DISCONNECT: case MPD_RECONNECT: @@ -1613,47 +1571,13 @@ bool mpd_client_state_set(const char *name, const char *value) { return true; } -int mpd_client_syscmd(char *buffer, char *cmd, int order) { - int len; - char filename[400]; - char *line; - char *crap; - size_t n = 0; - ssize_t read; - - sanitize_string(cmd); - snprintf(filename, 400, "%s/syscmds/%d%s", config.etcdir, order, cmd); - FILE *fp = fopen(filename, "r"); - if (fp == NULL) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s.\"}", cmd); - printf("Can't execute syscmd \"%s\"\n", cmd); - return len; - } - read = getline(&line, &n, fp); - fclose(fp); - if (read > 0) { - strtok_r(line, "\n", &crap); - if (system(line) == 0) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Executed cmd %s.\"}", cmd); - LOG_VERBOSE() printf("Executed syscmd: \"%s\"\n", line); - } - else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Executing cmd %s failed.\"}", cmd); - printf("Executing syscmd \"%s\" failed.\n", cmd); - } - } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s.\"}", cmd); - printf("Can't execute syscmd \"%s\"\n", cmd); - } - CHECK_RETURN_LEN(); -} - -int mpd_client_put_settings(char *buffer) { +int mpd_client_put_settings(char *buffer, void *arg_config) { struct mpd_status *status; char *replaygain = strdup(""); int len; int nr = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); + t_config *config = (t_config *) arg_config; status = mpd_run_status(mpd.conn); if (!status) { @@ -1683,30 +1607,30 @@ int mpd_client_put_settings(char *buffer) { mpd_status_get_random(status), mpd_status_get_mixrampdb(status), mpd_status_get_mixrampdelay(status), - config.mpdhost, - config.mpdport, - config.mpdpass ? "true" : "false", - config.syscmds, + config->mpdhost, + config->mpdport, + config->mpdpass ? "true" : "false", + config->syscmds, mpd.feat_playlists, mpd.feat_tags, mpd.feat_library, mpd.feat_advsearch, - config.localplayer, - config.streamport, - config.streamurl, - config.coverimage, - config.coverimagename, - config.stickers, - config.mixramp, - config.smartpls, - config.max_elements_per_page, + config->localplayer, + config->streamport, + config->streamurl, + config->coverimage, + config->coverimagename, + config->stickers, + config->mixramp, + config->smartpls, + config->max_elements_per_page, replaygain, mympd_state.notificationWeb, mympd_state.notificationPage, mympd_state.jukeboxMode, mympd_state.jukeboxPlaylist, mympd_state.jukeboxQueueLength, - config.coverimagesize + config->coverimagesize ); mpd_status_free(status); free(replaygain); @@ -1739,10 +1663,11 @@ int mpd_client_put_settings(char *buffer) { } len += json_printf(&out, "]"); - if (config.syscmds == true) { + if (config->syscmds == true) { len += json_printf(&out, ", syscmds: ["); nr = 0; - current = syscmds.list; + struct list *syscmd_list = (struct list *) config->syscmd_list; + current = syscmd_list->list; while (current != NULL) { if (nr++) len += json_printf(&out, ","); diff --git a/src/mpd_client.h b/src/mpd_client.h index cf5e01e..9f6365b 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -25,11 +25,6 @@ #ifndef __MPD_CLIENT_H__ #define __MPD_CLIENT_H__ -#include "global.h" -#include "web_server.h" -#include "list.h" -#include "tiny_queue.h" - #define RETURN_ERROR_AND_RECOVER(X) do { \ printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd.conn)); \ len = json_printf(&out, "{type: error, data: %Q}", mpd_connection_get_error_message(mpd.conn)); \ @@ -61,71 +56,6 @@ } while (0) -#define MPD_CMDS(X) \ - X(MPD_API_UNKNOWN) \ - X(MPD_API_QUEUE_CLEAR) \ - X(MPD_API_QUEUE_CROP) \ - X(MPD_API_QUEUE_SAVE) \ - X(MPD_API_QUEUE_LIST) \ - X(MPD_API_QUEUE_SEARCH) \ - X(MPD_API_QUEUE_RM_TRACK) \ - X(MPD_API_QUEUE_RM_RANGE) \ - X(MPD_API_QUEUE_MOVE_TRACK) \ - X(MPD_API_QUEUE_ADD_TRACK_AFTER) \ - X(MPD_API_QUEUE_ADD_TRACK) \ - X(MPD_API_QUEUE_ADD_PLAY_TRACK) \ - X(MPD_API_QUEUE_REPLACE_TRACK) \ - X(MPD_API_QUEUE_ADD_PLAYLIST) \ - X(MPD_API_QUEUE_REPLACE_PLAYLIST) \ - X(MPD_API_QUEUE_SHUFFLE) \ - X(MPD_API_QUEUE_LAST_PLAYED) \ - X(MPD_API_PLAYLIST_CLEAR) \ - X(MPD_API_PLAYLIST_RENAME) \ - X(MPD_API_PLAYLIST_MOVE_TRACK) \ - X(MPD_API_PLAYLIST_ADD_TRACK) \ - X(MPD_API_PLAYLIST_RM_TRACK) \ - X(MPD_API_PLAYLIST_RM) \ - X(MPD_API_PLAYLIST_LIST) \ - X(MPD_API_PLAYLIST_CONTENT_LIST) \ - X(MPD_API_SMARTPLS_UPDATE_ALL) \ - X(MPD_API_SMARTPLS_SAVE) \ - X(MPD_API_SMARTPLS_GET) \ - X(MPD_API_DATABASE_SEARCH_ADV) \ - X(MPD_API_DATABASE_SEARCH) \ - X(MPD_API_DATABASE_UPDATE) \ - X(MPD_API_DATABASE_RESCAN) \ - X(MPD_API_DATABASE_FILESYSTEM_LIST) \ - X(MPD_API_DATABASE_TAG_LIST) \ - X(MPD_API_DATABASE_TAG_ALBUM_LIST) \ - X(MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST) \ - X(MPD_API_DATABASE_STATS) \ - X(MPD_API_DATABASE_SONGDETAILS) \ - X(MPD_API_PLAYER_PLAY_TRACK) \ - X(MPD_API_PLAYER_VOLUME_SET) \ - X(MPD_API_PLAYER_VOLUME_GET) \ - X(MPD_API_PLAYER_PAUSE) \ - X(MPD_API_PLAYER_PLAY) \ - X(MPD_API_PLAYER_STOP) \ - X(MPD_API_PLAYER_SEEK) \ - X(MPD_API_PLAYER_NEXT) \ - X(MPD_API_PLAYER_PREV) \ - X(MPD_API_PLAYER_OUTPUT_LIST) \ - X(MPD_API_PLAYER_TOGGLE_OUTPUT) \ - X(MPD_API_PLAYER_CURRENT_SONG) \ - X(MPD_API_PLAYER_STATE) \ - X(MPD_API_SETTINGS_GET) \ - X(MPD_API_SETTINGS_SET) \ - X(MPD_API_LIKE) \ - X(MPD_API_COLS_SAVE) \ - X(MPD_API_SYSCMD) - -#define GEN_ENUM(X) X, -#define GEN_STR(X) #X, - -enum mpd_cmd_ids { - MPD_CMDS(GEN_ENUM) -}; - enum mpd_conn_states { MPD_DISCONNECTED, MPD_FAILURE, @@ -167,7 +97,6 @@ struct list mympd_tags; struct list mympd_searchtags; struct list mympd_browsetags; struct list last_played; -struct list syscmds; typedef struct { long playCount; @@ -192,11 +121,10 @@ typedef struct { } t_mympd_state; t_mympd_state mympd_state; -tiny_queue_t *mpd_client_queue; void mpd_client_idle(int timeout); void mpd_client_parse_idle(int idle_bitmask); -void mpd_client_api(struct work_request_t *request); +void mpd_client_api(void *arg_request); void mpd_client_notify(size_t n); bool mpd_client_count_song_id(int song_id, char *name, int value); bool mpd_client_count_song_uri(const char *uri, char *name, int value); @@ -208,7 +136,6 @@ bool mpd_client_last_played_list(int song_id); bool mpd_client_jukebox(); bool mpd_client_state_get(char *name, char *value); bool mpd_client_state_set(const char *name, const char *value); -int mpd_client_syscmd(char *buffer, char *cmd, int order); int mpd_client_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange); int mpd_client_smartpls_put(char *buffer, char *playlist); int mpd_client_smartpls_update_all(); @@ -228,7 +155,7 @@ int mpd_client_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, int mpd_client_put_welcome(char *buffer); int mpd_client_put_volume(char *buffer); int mpd_client_put_stats(char *buffer); -int mpd_client_put_settings(char *buffer); +int mpd_client_put_settings(char *buffer, void *arg_config); int mpd_client_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter); int mpd_client_put_songs_in_album(char *buffer, char *album, char *search, char *tag); int mpd_client_put_playlists(char *buffer, unsigned int offset, char *filter); diff --git a/src/mympd_api.c b/src/mympd_api.c new file mode 100644 index 0000000..f265b53 --- /dev/null +++ b/src/mympd_api.c @@ -0,0 +1,135 @@ +/* myMPD + (c) 2018-2019 Juergen Mang + This project's homepage is: https://github.com/jcorporation/mympd + + myMPD ist fork of: + + ympd + (c) 2013-2014 Andrew Karpow + This project's homepage is: http://www.ympd.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "tiny_queue.h" +#include "global.h" +#include "mympd_api.h" +#include "../dist/src/frozen/frozen.h" + +//private definitions +static void mympd_api(void *arg_request, void *arg_config); +static int mympd_api_syscmd(char *buffer, const char *cmd, void *arg_config); + +//public functions +void *mympd_api_loop(void *arg_config) { + while (s_signal_received == 0) { + struct work_request_t *req = tiny_queue_shift(mympd_api_queue); + mympd_api(req, arg_config); + } + return NULL; +} + +//private functions +static void mympd_api(void *arg_request, void *arg_config) { + struct work_request_t *request = (struct work_request_t*) arg_request; + size_t len = 0; + char buffer[MAX_SIZE]; + int je; + char *p_charbuf1; + LOG_VERBOSE() printf("MYMPD API request: %.*s\n", request->length, request->data); + + if (request->cmd_id == MYMPD_API_SYSCMD) { + if (config.syscmds == true) { + je = json_scanf(request->data, request->length, "{data: {cmd: %Q}}", &p_charbuf1); + if (je == 1) { + len = mympd_api_syscmd(buffer, p_charbuf1, arg_config); + free(p_charbuf1); + } + } + else { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); + } + } + else { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown cmd_id %u.\"}", request->cmd_id); + printf("ERROR: Unknown cmd_id %u\n", request->cmd_id); + } + + if (len == 0) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd_id %u.\"}", request->cmd_id); + printf("ERROR: No response for cmd_id %u\n", request->cmd_id); + } + LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response to connection %lu (first 800 chars):\n%*.*s\n", request->conn_id, 0, 800, buffer); + + struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t)); + response->conn_id = request->conn_id; + response->length = copy_string(response->data, buffer, MAX_SIZE, len); + tiny_queue_push(web_server_queue, response); + + free(request); +} + +static int mympd_api_syscmd(char *buffer, const char *cmd, void *arg_config) { + int len; + char filename[400]; + char *line; + char *crap; + size_t n = 0; + ssize_t read; + t_config *config = (t_config *) arg_config; + + const int order = list_get_value(config->syscmd_list, cmd); + if (order == -1) { + printf("ERROR: Syscmd not defined: %s\n", cmd); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System command not defined\"}"); + return len; + } + + snprintf(filename, 400, "%s/syscmds/%d%s", config->etcdir, order, cmd); + FILE *fp = fopen(filename, "r"); + if (fp == NULL) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s.\"}", cmd); + printf("ERROR: Can't execute syscmd \"%s\"\n", cmd); + return len; + } + read = getline(&line, &n, fp); + fclose(fp); + if (read > 0) { + strtok_r(line, "\n", &crap); + if (system(line) == 0) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Executed cmd %s.\"}", cmd); + LOG_VERBOSE2() printf("Executed syscmd: \"%s\"\n", line); + } + else { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Executing cmd %s failed.\"}", cmd); + printf("ERROR: Executing syscmd \"%s\" failed.\n", cmd); + } + } else { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s.\"}", cmd); + printf("ERROR: Can't execute syscmd \"%s\"\n", cmd); + } + CHECK_RETURN_LEN(); +} + diff --git a/src/mympd_api.h b/src/mympd_api.h new file mode 100644 index 0000000..c52c8dd --- /dev/null +++ b/src/mympd_api.h @@ -0,0 +1,30 @@ +/* myMPD + (c) 2018-2019 Juergen Mang + This project's homepage is: https://github.com/jcorporation/mympd + + myMPD ist fork of: + + ympd + (c) 2013-2014 Andrew Karpow + This project's homepage is: http://www.ympd.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __MYMPD_API_H__ +#define __MYMPD_API_H__ + +void *mympd_api_loop(void *arg_config); + +#endif diff --git a/src/web_server.c b/src/web_server.c index 9c4670c..2f51b7b 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -23,25 +23,31 @@ */ #include +#include +#include +#include "list.h" +#include "tiny_queue.h" #include "global.h" #include "web_server.h" #include "mpd_client.h" #include "../dist/src/mongoose/mongoose.h" +#include "../dist/src/frozen/frozen.h" -//non-api definitions +//private definitions static int is_websocket(const struct mg_connection *nc); static void ev_handler(struct mg_connection *nc, int ev, void *ev_data); static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data); static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response); static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response); +static bool handle_api(long conn_id, const char *request, int request_len); typedef struct t_user_data { void *config; //pointer to mympd config long conn_id; } t_user_data; -//api functions +//public functions bool web_server_init(void *arg_mgr, void *arg_config) { struct mg_mgr *mgr = (struct mg_mgr *) arg_mgr; t_config *config = (t_config *) arg_config; @@ -93,13 +99,13 @@ bool web_server_init(void *arg_mgr, void *arg_config) { return mgr; } -void web_server_free(void *arg) { - struct mg_mgr *mgr = (struct mg_mgr *) arg; +void web_server_free(void *arg_mgr) { + struct mg_mgr *mgr = (struct mg_mgr *) arg_mgr; mg_mgr_free(mgr); } -void *web_server_loop(void *arg) { - struct mg_mgr *mgr = (struct mg_mgr *) arg; +void *web_server_loop(void *arg_mgr) { + struct mg_mgr *mgr = (struct mg_mgr *) arg_mgr; while (s_signal_received == 0) { mg_mgr_poll(mgr, 100); unsigned web_server_queue_length = tiny_queue_length(web_server_queue); @@ -119,7 +125,7 @@ void *web_server_loop(void *arg) { return NULL; } -//non-api functions +//private functions static int is_websocket(const struct mg_connection *nc) { return nc->flags & MG_F_IS_WEBSOCKET; } @@ -160,7 +166,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { else user_data->conn_id = 1; - //remove mgr user_data and set connection specific user_data + //replace mgr user_data with connection specific user_data t_user_data *nc_user_data = (t_user_data*)malloc(sizeof(t_user_data)); nc_user_data->config = config; nc_user_data->conn_id = user_data->conn_id; @@ -188,10 +194,13 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { struct http_message *hm = (struct http_message *) ev_data; LOG_VERBOSE2() printf("HTTP request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); if (mg_vcmp(&hm->uri, "/api") == 0) { - struct work_request_t *request = (struct work_request_t*)malloc(sizeof(struct work_request_t)); - request->conn_id = user_data->conn_id; - request->length = copy_string(request->data, hm->body.p, 1000, hm->body.len); - tiny_queue_push(mpd_client_queue, request); + bool rc = handle_api(user_data->conn_id, hm->body.p, hm->body.len); + if (rc == false) { + printf("ERROR: Invalid API request.\n"); + char *response = "{\"type\": \"error\", \"data\": \"Invalid API request\"}"; + mg_send_head(nc, 200, strlen(response), "Content-Type: application/json"); + mg_printf(nc, "%s", response); + } } else { static struct mg_serve_http_opts s_http_server_opts; @@ -239,3 +248,28 @@ static void ev_handler_redirect(struct mg_connection *nc, int ev, void *ev_data) } } } + +static bool handle_api(long conn_id, const char *request_body, int request_len) { + char *cmd; + + LOG_VERBOSE() printf("API request: %.*s\n", request_len, request_body); + const int je = json_scanf(request_body, request_len, "{cmd: %Q}", &cmd); + if (je < 1) + return false; + + enum mypd_cmd_ids cmd_id = get_cmd_id(cmd); + if (cmd_id == 0) + return false; + + struct work_request_t *request = (struct work_request_t*)malloc(sizeof(struct work_request_t)); + request->conn_id = conn_id; + request->cmd_id = cmd_id; + request->length = copy_string(request->data, request_body, 1000, request_len); + + if (strncmp(cmd, "MYMPD_API_", 10) == 0) + tiny_queue_push(mympd_api_queue, request); + else + tiny_queue_push(mpd_client_queue, request); + + return true; +} diff --git a/src/web_server.h b/src/web_server.h index 11986f3..1e052d7 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -25,24 +25,8 @@ #ifndef __WEB_SERVER_H__ #define __WEB_SERVER_H__ -#include "tiny_queue.h" - -tiny_queue_t *web_server_queue; - -struct work_request_t { - long conn_id; // needed to identify the connection where to send the reply - char data[1000]; - int length; -} work_request_t; - -struct work_result_t { - long conn_id; // needed to identify the connection where to send the reply - char data[MAX_SIZE]; - int length; -} work_result_t; - -void *web_server_loop(void *arg); +void *web_server_loop(void *arg_mgr); bool web_server_init(void *arg_mgr, void *arg_config); -void web_server_free(void *arg); +void web_server_free(void *arg_mgr); #endif