From cd588ab72e10ee934a8ac58f4add1dc9268ed79f Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 6 Dec 2018 17:06:50 +0000 Subject: [PATCH 01/59] Fix: bump version to 5.0.0 --- CMakeLists.txt | 6 +++--- PKGBUILD | 2 +- contrib/myMPD.spec | 2 +- debian/changelog | 4 ++-- htdocs/sw.js | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ae5712..9ff3aad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 2.6) project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") -set(CPACK_PACKAGE_VERSION_MAJOR "4") -set(CPACK_PACKAGE_VERSION_MINOR "7") -set(CPACK_PACKAGE_VERSION_PATCH "1") +set(CPACK_PACKAGE_VERSION_MAJOR "5") +set(CPACK_PACKAGE_VERSION_MINOR "0") +set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "/usr/share/${PROJECT_NAME}/htdocs") diff --git a/PKGBUILD b/PKGBUILD index 01a844f..e7621dd 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,7 +4,7 @@ pkgname=mympd _pkgname=myMPD -pkgver=4.7.1 +pkgver=5.0.0 pkgrel=1 pkgdesc="myMPD is a standalone and mobile friendly web mpdclient." arch=('x86_64' 'armv7h' 'aarch64') diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index 611180f..2fcd6d1 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -4,7 +4,7 @@ # (c) 2018 Juergen Mang Name: myMPD -Version: 4.7.1 +Version: 5.0.0 Release: 0 License: GPL-2.0 Group: Productivity/Multimedia/Sound/Players diff --git a/debian/changelog b/debian/changelog index 0055f92..36733db 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -mympd (4.7.1-1) stable; urgency=medium +mympd (5.0.0-1) stable; urgency=medium * Release from master - -- Juergen Mang Mon, 03 Dec 2018 21:12:30 +0000 + -- Juergen Mang Thu, 06 Dec 2018 17:12:43 +0000 diff --git a/htdocs/sw.js b/htdocs/sw.js index 9d4c8d5..77e85b0 100644 --- a/htdocs/sw.js +++ b/htdocs/sw.js @@ -1,4 +1,4 @@ -var CACHE = 'myMPD-cache-v4.7.1'; +var CACHE = 'myMPD-cache-v5.0.0'; var urlsToCache = [ '/', '/player.html', From c4df154cf32199cbcc7c3d4dc65ee51782f9a091 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 6 Dec 2018 17:06:50 +0000 Subject: [PATCH 02/59] Fix: bump version to 5.0.0 --- CMakeLists.txt | 4 ++-- PKGBUILD | 2 +- contrib/myMPD.spec | 2 +- debian/changelog | 3 ++- htdocs/sw.js | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ff813f0..934e512 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 2.6) project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") set(CPACK_PACKAGE_VERSION_MAJOR "4") -set(CPACK_PACKAGE_VERSION_MINOR "7") -set(CPACK_PACKAGE_VERSION_PATCH "2") +set(CPACK_PACKAGE_VERSION_MINOR "8") +set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "/usr/share/${PROJECT_NAME}/htdocs") diff --git a/PKGBUILD b/PKGBUILD index 9ec4877..595aff9 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,7 +4,7 @@ pkgname=mympd _pkgname=myMPD -pkgver=4.7.2 +pkgver=4.8.0 pkgrel=1 pkgdesc="myMPD is a standalone and mobile friendly web mpdclient." arch=('x86_64' 'armv7h' 'aarch64') diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index 066628b..e2f0943 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -4,7 +4,7 @@ # (c) 2018 Juergen Mang Name: myMPD -Version: 4.7.2 +Version: 4.8.0 Release: 0 License: GPL-2.0 Group: Productivity/Multimedia/Sound/Players diff --git a/debian/changelog b/debian/changelog index 191146e..b1bca6d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,5 @@ -mympd (4.7.2-1) stable; urgency=medium +<<<<<<< HEAD +mympd (4.8.0-1) stable; urgency=medium * Release from master diff --git a/htdocs/sw.js b/htdocs/sw.js index 91bb3db..bb1e862 100644 --- a/htdocs/sw.js +++ b/htdocs/sw.js @@ -1,4 +1,4 @@ -var CACHE = 'myMPD-cache-v4.7.2'; +var CACHE = 'myMPD-cache-v4.8.0'; var urlsToCache = [ '/', '/player.html', From 97e4fe2e949e8c5ca64ebdb46a6bfd988af7453e Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sat, 5 Jan 2019 00:01:34 +0000 Subject: [PATCH 03/59] Feat: initial working code --- CMakeLists.txt | 12 +- PKGBUILD | 2 +- contrib/myMPD.spec | 2 +- debian/changelog | 4 +- htdocs/sw.js | 2 +- src/{validate.c => common.c} | 23 +-- src/common.h | 72 +++++++++ src/list.h | 4 + src/mpd_client.c | 269 +++++++++++++++---------------- src/mpd_client.h | 56 +------ src/mympd.c | 132 ++++++--------- src/tiny_queue.c | 80 +++++++++ src/{validate.h => tiny_queue.h} | 28 +++- src/web_server.c | 144 +++++++++++++++++ src/web_server.h | 52 ++++++ 15 files changed, 582 insertions(+), 300 deletions(-) rename src/{validate.c => common.c} (78%) create mode 100644 src/common.h create mode 100644 src/tiny_queue.c rename src/{validate.h => tiny_queue.h} (56%) create mode 100644 src/web_server.c create mode 100644 src/web_server.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ff813f0..4369c8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 2.6) project (mympd C) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") -set(CPACK_PACKAGE_VERSION_MAJOR "4") -set(CPACK_PACKAGE_VERSION_MINOR "7") -set(CPACK_PACKAGE_VERSION_PATCH "2") +set(CPACK_PACKAGE_VERSION_MAJOR "5") +set(CPACK_PACKAGE_VERSION_MINOR "0") +set(CPACK_PACKAGE_VERSION_PATCH "0") if(CMAKE_BUILD_TYPE MATCHES RELEASE) set(ASSETS_PATH "/usr/share/${PROJECT_NAME}/htdocs") @@ -21,7 +21,7 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D MG_ENABLE_SSL -D MG_ENABLE_IPV6 -D MG_DISABLE_MQTT -D MG_DISABLE_MQTT_BROKER -D MG_DISABLE_DNS_SERVER -D MG_DISABLE_COAP -D MG_DISABLE_HTTP_CGI -D MG_DISABLE_HTTP_SSI -D MG_DISABLE_HTTP_WEBDAV") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -pedantic -D MG_ENABLE_SSL -D MG_ENABLE_THREADS -D MG_ENABLE_IPV6 -D MG_DISABLE_MQTT -D MG_DISABLE_MQTT_BROKER -D MG_DISABLE_DNS_SERVER -D MG_DISABLE_COAP -D MG_DISABLE_HTTP_CGI -D MG_DISABLE_HTTP_SSI -D MG_DISABLE_HTTP_WEBDAV") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -D_FORTIFY_SOURCE=2 -fstack-protector -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") find_package(OpenSSL REQUIRED) @@ -31,8 +31,10 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL) set(SOURCES src/mympd.c src/mpd_client.c + src/web_server.c src/list.c - src/validate.c + src/tiny_queue.c + src/common.c dist/src/mongoose/mongoose.c dist/src/frozen/frozen.c dist/src/inih/ini.c diff --git a/PKGBUILD b/PKGBUILD index 9ec4877..e7621dd 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -4,7 +4,7 @@ pkgname=mympd _pkgname=myMPD -pkgver=4.7.2 +pkgver=5.0.0 pkgrel=1 pkgdesc="myMPD is a standalone and mobile friendly web mpdclient." arch=('x86_64' 'armv7h' 'aarch64') diff --git a/contrib/myMPD.spec b/contrib/myMPD.spec index 066628b..2fcd6d1 100644 --- a/contrib/myMPD.spec +++ b/contrib/myMPD.spec @@ -4,7 +4,7 @@ # (c) 2018 Juergen Mang Name: myMPD -Version: 4.7.2 +Version: 5.0.0 Release: 0 License: GPL-2.0 Group: Productivity/Multimedia/Sound/Players diff --git a/debian/changelog b/debian/changelog index 191146e..201926a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -mympd (4.7.2-1) stable; urgency=medium +mympd (5.0.0-1) stable; urgency=medium * Release from master - -- Juergen Mang Fri, 07 Dec 2018 17:12:12 +0000 + -- Juergen Mang Fri, 04 Jan 2019 09:01:07 +0000 diff --git a/htdocs/sw.js b/htdocs/sw.js index 91bb3db..77e85b0 100644 --- a/htdocs/sw.js +++ b/htdocs/sw.js @@ -1,4 +1,4 @@ -var CACHE = 'myMPD-cache-v4.7.2'; +var CACHE = 'myMPD-cache-v5.0.0'; var urlsToCache = [ '/', '/player.html', diff --git a/src/validate.c b/src/common.c similarity index 78% rename from src/validate.c rename to src/common.c index 92b72bf..d1e6a09 100644 --- a/src/validate.c +++ b/src/common.c @@ -1,5 +1,5 @@ /* myMPD - (c) 2018 Juergen Mang + (c) 2018-2019 Juergen Mang This project's homepage is: https://github.com/jcorporation/mympd myMPD ist fork of: @@ -26,7 +26,7 @@ #include #include #include -#include "validate.h" +#include "common.h" void sanitize_string(const char *data) { static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz" @@ -38,18 +38,11 @@ void sanitize_string(const char *data) { *cp = '_'; } -int validate_path(char *path, const char *basepath) { - char *rpath = NULL; - char *ptr; - ptr = realpath(path, rpath); - if (ptr == NULL) - return 1; - if (strncmp(basepath, ptr, strlen(basepath)) == 0) { - free(rpath); +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; - } - else { - free(rpath); - return 1; - } + size_t const max = (src_len < dst_len) ? src_len : dst_len -1; + memcpy(dest, src, max); + dest[max] = '\0'; + return max; } diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..6213976 --- /dev/null +++ b/src/common.h @@ -0,0 +1,72 @@ +/* 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 __COMMON_H__ +#define __COMMON_H__ + +#include +#include + +#define MAX_SIZE 2048 * 400 +#define MAX_ELEMENTS_PER_PAGE 400 + +#define LOG_INFO() if (config.loglevel >= 1) +#define LOG_VERBOSE() if (config.loglevel >= 2) +#define LOG_DEBUG() if (config.loglevel == 3) + +typedef struct { + long mpdport; + const char *mpdhost; + const char *mpdpass; + const char *webport; + bool ssl; + const char *sslport; + const char *sslcert; + const char *sslkey; + const char *user; + bool coverimage; + const char *coverimagename; + long coverimagesize; + bool stickers; + bool mixramp; + const char *taglist; + const char *searchtaglist; + const char *browsetaglist; + bool smartpls; + const char *varlibdir; + const char *etcdir; + unsigned long max_elements_per_page; + bool syscmds; + bool localplayer; + long streamport; + const char *streamurl; + unsigned long last_played_count; + long loglevel; +} t_config; + +t_config config; + +void sanitize_string(const char *data); +int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len); +#endif diff --git a/src/list.h b/src/list.h index e4c9ecf..607ff84 100644 --- a/src/list.h +++ b/src/list.h @@ -18,6 +18,9 @@ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#ifndef __LIST_H__ +#define __LIST_H__ + struct node { char *data; long value; @@ -43,3 +46,4 @@ int list_shuffle(struct list *l); int list_sort_by_value(struct list *l, bool order); int list_swap_item(struct node *n1, struct node *n2); struct node *list_node_at(const struct list * l, unsigned index); +#endif diff --git a/src/mpd_client.c b/src/mpd_client.c index 4309d56..eb4b61a 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -32,8 +32,9 @@ #include #include +#include "common.h" #include "mpd_client.h" -#include "validate.h" +#include "web_server.h" #include "config.h" #include "../dist/src/frozen/frozen.h" @@ -49,9 +50,7 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { return -1; } -enum mpd_idle idle_bitmask_save = 0; - -void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { +void mympd_api(struct work_request_t *request) { size_t n = 0; char *cmd; unsigned int uint_buf1, uint_buf2, uint_rc; @@ -61,49 +60,40 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { char *p_charbuf1, *p_charbuf2, *p_charbuf3, *p_charbuf4; char p_char[4]; enum mpd_cmd_ids cmd_id; - struct pollfd fds[1]; - int pollrc; #ifdef DEBUG struct timespec start, end; #endif - LOG_VERBOSE() printf("API request: %s\n", msg.p); + LOG_VERBOSE() printf("API request: %.*s\n", request->length, request->data); - je = json_scanf(msg.p, msg.len, "{cmd: %Q}", &cmd); - if (je == 1) + je = json_scanf(request->data, request->length, "{cmd: %Q}", &cmd); + if (je == 1) { cmd_id = get_cmd_id(cmd); - else - cmd_id = get_cmd_id("MPD_API_UNKNOWN"); + } + 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; + } if (cmd_id == -1) cmd_id = get_cmd_id("MPD_API_UNKNOWN"); - LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode\n"); - if (mpd_send_noidle(mpd.conn)) { - //save idle events (processing later) - LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events\n"); - fds[0].fd = mpd_connection_get_fd(mpd.conn); - fds[0].events = POLLIN; - pollrc = poll(fds, 1, 200); - if (pollrc > 0) { - idle_bitmask_save = mpd_recv_idle(mpd.conn, false); - if (idle_bitmask_save > 0) - LOG_DEBUG() fprintf(stderr, "DEBUG: Idle event before request: %d\n", idle_bitmask_save); - } - } - mpd_response_finish(mpd.conn); - //handle request #ifdef DEBUG clock_gettime(CLOCK_MONOTONIC_RAW, &start); #endif switch(cmd_id) { case MPD_API_UNKNOWN: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); - printf("Unknown API request: %s\n", msg.p); + printf("Unknown API request: %.*s\n", request->length, request->data); break; case MPD_API_LIKE: if (config.stickers) { - je = json_scanf(msg.p, msg.len, "{data: {uri: %Q, like: %d}}", &p_charbuf1, &uint_buf1); + je = json_scanf(request->data, request->length, "{data: {uri: %Q, like: %d}}", &p_charbuf1, &uint_buf1); if (je == 2) { if (!mympd_like_song_uri(p_charbuf1, uint_buf1)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); @@ -118,10 +108,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_COLS_SAVE: - je = json_scanf(msg.p, msg.len, "{data: {table: %Q}}", &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: {table: %Q}}", &p_charbuf1); if (je == 1) { char column_list[800]; - snprintf(column_list, 800, "%.*s", msg.len, msg.p); + snprintf(column_list, 800, "%.*s", request->length, request->data); char *cols = strchr(column_list, '['); int len = strlen(cols); if (len > 1) @@ -169,7 +159,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { break; case MPD_API_SYSCMD: if (config.syscmds == true) { - je = json_scanf(msg.p, msg.len, "{data: {cmd: %Q}}", &p_charbuf1); + 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) @@ -188,73 +178,73 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = mympd_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(msg.p, msg.len, "{data: {notificationWeb: %B}}", &mympd_state.notificationWeb); + je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mympd_state.notificationWeb); if (je == 1) if (!mympd_state_set("notificationWeb", (mympd_state.notificationWeb == true ? "true" : "false"))) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {notificationPage: %B}}", &mympd_state.notificationPage); + je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mympd_state.notificationPage); if (je == 1) if (!mympd_state_set("notificationPage", (mympd_state.notificationPage == true ? "true" : "false"))) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {jukeboxMode: %d}}", &mympd_state.jukeboxMode); + 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 (!mympd_state_set("jukeboxMode", p_char)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); } - je = json_scanf(msg.p, msg.len, "{data: {jukeboxPlaylist: %Q}}", &mympd_state.jukeboxPlaylist); + je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mympd_state.jukeboxPlaylist); if (je == 1) if (!mympd_state_set("jukeboxPlaylist", mympd_state.jukeboxPlaylist)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {jukeboxQueueLength: %d}}", &mympd_state.jukeboxQueueLength); + 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 (!mympd_state_set("jukeboxQueueLength", p_char)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); } - je = json_scanf(msg.p, msg.len, "{data: {random: %u}}", &uint_buf1); + 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.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {repeat: %u}}", &uint_buf1); + 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.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {consume: %u}}", &uint_buf1); + 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.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {single: %u}}", &uint_buf1); + 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.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {crossfade: %u}}", &uint_buf1); + 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.\"}"); if (config.mixramp) { - je = json_scanf(msg.p, msg.len, "{data: {mixrampdb: %f}}", &float_buf); + 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.\"}"); - je = json_scanf(msg.p, msg.len, "{data: {mixrampdelay: %f}}", &float_buf); + 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.\"}"); } - je = json_scanf(msg.p, msg.len, "{data: {replaygain: %Q}}", &p_charbuf1); + 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.\"}"); @@ -287,11 +277,11 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}"); break; case MPD_API_SMARTPLS_SAVE: - je = json_scanf(msg.p, msg.len, "{data: {type: %Q}}", &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: {type: %Q}}", &p_charbuf1); n = 1; if (je == 1) { if (strcmp(p_charbuf1, "sticker") == 0) { - je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q, sticker: %Q, maxentries: %d}}", &p_charbuf2, &p_charbuf3, &int_buf1); + je = json_scanf(request->data, request->length, "{data: {playlist: %Q, sticker: %Q, maxentries: %d}}", &p_charbuf2, &p_charbuf3, &int_buf1); if (je == 3) { n = mympd_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); free(p_charbuf2); @@ -299,14 +289,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } } else if (strcmp(p_charbuf1, "newest") == 0) { - je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q, timerange: %d}}", &p_charbuf2, &int_buf1); + je = json_scanf(request->data, request->length, "{data: {playlist: %Q, timerange: %d}}", &p_charbuf2, &int_buf1); if (je == 2) { n = mympd_smartpls_save(p_charbuf1, p_charbuf2, NULL, NULL, 0, int_buf1); free(p_charbuf2); } } else if (strcmp(p_charbuf1, "search") == 0) { - je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q, tag: %Q, searchstr: %Q}}", &p_charbuf2, &p_charbuf3, &p_charbuf4); + je = json_scanf(request->data, request->length, "{data: {playlist: %Q, tag: %Q, searchstr: %Q}}", &p_charbuf2, &p_charbuf3, &p_charbuf4); if (je == 3) { n = mympd_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); free(p_charbuf2); @@ -322,7 +312,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving playlist failed\"}"); break; case MPD_API_SMARTPLS_GET: - je = json_scanf(msg.p, msg.len, "{data: {playlist: %Q}}", &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: {playlist: %Q}}", &p_charbuf1); if (je == 1) { n = mympd_smartpls_put(mpd.buf, p_charbuf1); free(p_charbuf1); @@ -382,7 +372,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = mympd_queue_crop(mpd.buf); break; case MPD_API_QUEUE_RM_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {track:%u}}", &uint_buf1); + 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\"}"); @@ -393,7 +383,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_RM_RANGE: - je = json_scanf(msg.p, msg.len, "{data: {start: %u, end: %u}}", &uint_buf1, &uint_buf2); + 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\"}"); @@ -404,7 +394,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_MOVE_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {from: %u, to: %u}}", &uint_buf1, &uint_buf2); + je = json_scanf(request->data, request->length, "{data: {from: %u, to: %u}}", &uint_buf1, &uint_buf2); if (je == 2) { uint_buf1--; uint_buf2--; @@ -419,7 +409,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_MOVE_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {plist: %Q, from: %u, to: %u }}", &p_charbuf1, &uint_buf1, &uint_buf2); + je = json_scanf(request->data, request->length, "{data: {plist: %Q, from: %u, to: %u }}", &p_charbuf1, &uint_buf1, &uint_buf2); if (je == 3) { uint_buf1--; uint_buf2--; @@ -437,7 +427,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYER_PLAY_TRACK: - je = json_scanf(msg.p, msg.len, "{data: { track:%u}}", &uint_buf1); + 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\"}"); @@ -451,7 +441,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = mympd_put_outputs(mpd.buf); break; case MPD_API_PLAYER_TOGGLE_OUTPUT: - je = json_scanf(msg.p, msg.len, "{data: {output: %u, state: %u}}", &uint_buf1, &uint_buf2); + 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)) @@ -472,7 +462,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYER_VOLUME_SET: - je = json_scanf(msg.p, msg.len, "{data: {volume:%u}}", &uint_buf1); + 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\"}"); @@ -486,7 +476,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = mympd_put_volume(mpd.buf); break; case MPD_API_PLAYER_SEEK: - je = json_scanf(msg.p, msg.len, "{data: {songid: %u, seek: %u}}", &uint_buf1, &uint_buf2); + 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\"}"); @@ -497,13 +487,13 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_LIST: - je = json_scanf(msg.p, msg.len, "{data: {offset: %u}}", &uint_buf1); + je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { n = mympd_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length); } break; case MPD_API_QUEUE_LAST_PLAYED: - je = json_scanf(msg.p, msg.len, "{data: {offset: %u}}", &uint_buf1); + je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { n = mympd_put_last_played_songs(mpd.buf, uint_buf1); } @@ -512,14 +502,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { n = mympd_put_current_song(mpd.buf); break; case MPD_API_DATABASE_SONGDETAILS: - je = json_scanf(msg.p, msg.len, "{data: { uri: %Q}}", &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: { uri: %Q}}", &p_charbuf1); if (je == 1) { n = mympd_put_songdetails(mpd.buf, p_charbuf1); free(p_charbuf1); } break; case MPD_API_DATABASE_TAG_LIST: - je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); + je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); if (je == 3) { n = mympd_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1); free(p_charbuf1); @@ -527,7 +517,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_DATABASE_TAG_ALBUM_LIST: - je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q, search: %Q, tag: %Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2, &p_charbuf3); + 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 = mympd_put_db_tag(mpd.buf, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); free(p_charbuf1); @@ -536,7 +526,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_DATABASE_TAG_ALBUM_TITLE_LIST: - je = json_scanf(msg.p, msg.len, "{data: {album: %Q, search: %Q, tag: %Q}}", &p_charbuf1, &p_charbuf2, &p_charbuf3); + je = json_scanf(request->data, request->length, "{data: {album: %Q, search: %Q, tag: %Q}}", &p_charbuf1, &p_charbuf2, &p_charbuf3); if (je == 3) { n = mympd_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3); free(p_charbuf1); @@ -545,7 +535,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_RENAME: - je = json_scanf(msg.p, msg.len, "{data: {from: %Q, to: %Q}}", &p_charbuf1, &p_charbuf2); + je = json_scanf(request->data, request->length, "{data: {from: %Q, to: %Q}}", &p_charbuf1, &p_charbuf2); if (je == 2) { //rename smart playlist char old_pl_file[400]; @@ -583,14 +573,14 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_LIST: - je = json_scanf(msg.p, msg.len, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1); if (je == 2) { n = mympd_put_playlists(mpd.buf, uint_buf1, p_charbuf1); free(p_charbuf1); } break; case MPD_API_PLAYLIST_CONTENT_LIST: - je = json_scanf(msg.p, msg.len, "{data: {uri: %Q, offset:%u, filter:%Q}}", &p_charbuf1, &uint_buf1, &p_charbuf2); + je = json_scanf(request->data, request->length, "{data: {uri: %Q, offset:%u, filter:%Q}}", &p_charbuf1, &uint_buf1, &p_charbuf2); if (je == 3) { n = mympd_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); @@ -598,7 +588,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_ADD_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {plist:%Q, uri:%Q}}", &p_charbuf1, &p_charbuf2); + 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); @@ -611,7 +601,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_CLEAR: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); + 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\"}"); @@ -623,7 +613,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_RM_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q, track:%u}}", &p_charbuf1, &uint_buf1); + 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\"}"); @@ -635,7 +625,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_DATABASE_FILESYSTEM_LIST: - je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q, path:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); + je = json_scanf(request->data, request->length, "{data: {offset:%u, filter:%Q, path:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); if (je == 3) { n = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); free(p_charbuf1); @@ -643,7 +633,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_ADD_TRACK_AFTER: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q, to:%d}}", &p_charbuf1, &int_buf1); + je = json_scanf(request->data, request->length, "{data: {uri:%Q, to:%d}}", &p_charbuf1, &int_buf1); if (je == 2) { int_rc = mpd_run_add_id_to(mpd.conn, p_charbuf1, int_buf1); if (int_rc > -1 ) @@ -656,7 +646,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_REPLACE_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q }}", &p_charbuf1); + 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.\"}"); @@ -676,7 +666,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_ADD_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); + 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\"}"); @@ -688,7 +678,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_ADD_PLAY_TRACK: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { int_buf1 = mpd_run_add_id(mpd.conn, p_charbuf1); if (int_buf1 != -1) { @@ -707,7 +697,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_REPLACE_PLAYLIST: - je = json_scanf(msg.p, msg.len, "{data: {plist:%Q}}", &p_charbuf1); + 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.\"}"); @@ -727,7 +717,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_ADD_PLAYLIST: - je = json_scanf(msg.p, msg.len, "{data: {plist:%Q}}", &p_charbuf1); + 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\"}"); @@ -739,7 +729,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_SAVE: - je = json_scanf(msg.p, msg.len, "{ data: {plist:%Q}}", &p_charbuf1); + 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\"}"); @@ -751,7 +741,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_QUEUE_SEARCH: - je = json_scanf(msg.p, msg.len, "{data: {offset:%u, filter:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); + je = json_scanf(request->data, request->length, "{data: {offset:%u, filter:%Q, searchstr:%Q}}", &uint_buf1, &p_charbuf1, &p_charbuf2); if (je == 3) { n = mympd_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); @@ -759,7 +749,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_DATABASE_SEARCH: - je = json_scanf(msg.p, msg.len, "{data: {searchstr:%Q, filter:%Q, plist:%Q, offset:%u}}", &p_charbuf1, &p_charbuf2, &p_charbuf3, &uint_buf1); + 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 = mympd_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); free(p_charbuf1); @@ -767,7 +757,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_DATABASE_SEARCH_ADV: - je = json_scanf(msg.p, msg.len, "{data: {expression:%Q, sort:%Q, sortdesc:%B, plist:%Q, offset:%u}}", + 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 = mympd_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); @@ -785,7 +775,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { } break; case MPD_API_PLAYLIST_RM: - je = json_scanf(msg.p, msg.len, "{data: {uri:%Q}}", &p_charbuf1); + je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { //remove smart playlist char pl_file[400]; @@ -833,33 +823,31 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg) { fprintf(stderr, "DEBUG: Time used: %lu\n", delta_us); #endif - if (n == 0) - n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd %s\"}", cmd); - - if (is_websocket(nc)) { - LOG_DEBUG() fprintf(stderr, "DEBUG: Send websocket response:\n %s\n", mpd.buf); - mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, mpd.buf, n); - } - else { - LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response (first 800 chars):\n%*.*s\n", 0, 800, mpd.buf); - mg_send_http_chunk(nc, mpd.buf, n); + if (n == 0) { + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd %s.\"}", cmd); } + 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); + tiny_queue_push(web_server_queue, response); + free(cmd); - LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode\n"); - mpd_send_idle(mpd.conn); + free(request); } -void mympd_notify(struct mg_mgr *s) { - for (struct mg_connection *c = mg_next(s, NULL); c != NULL; c = mg_next(s, c)) { - if (!is_websocket(c)) - continue; - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, mpd.buf, strlen(mpd.buf)); - } - LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s\n", mpd.buf); +void mympd_notify(size_t len) { + LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s.\n", mpd.buf); + + struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t)); + response->conn_id = 0; + response->length = copy_string(response->data, mpd.buf, MAX_SIZE, len); + tiny_queue_push(web_server_queue, response); } -void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) { - int len = 0; +void mympd_parse_idle(int idle_bitmask) { + size_t n = 0; for (unsigned j = 0;; j++) { enum mpd_idle idle_event = 1 << j; const char *idle_name = mpd_idle_name(idle_event); @@ -869,19 +857,19 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) { LOG_VERBOSE() printf("MPD idle event: %s\n", idle_name); switch(idle_event) { case MPD_IDLE_DATABASE: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); mympd_smartpls_update_all(); break; case MPD_IDLE_STORED_PLAYLIST: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); break; case MPD_IDLE_QUEUE: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); if (mympd_state.jukeboxMode > 0) mympd_jukebox(); break; case MPD_IDLE_PLAYER: - len = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); + n = mympd_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) mympd_last_played_list(mpd.song_id); @@ -893,31 +881,32 @@ void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask) { } break; case MPD_IDLE_MIXER: - len = mympd_put_volume(mpd.buf); + n = mympd_put_volume(mpd.buf); break; case MPD_IDLE_OUTPUT: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}"); break; case MPD_IDLE_OPTIONS: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}"); break; case MPD_IDLE_UPDATE: - len = mympd_get_updatedb_state(mpd.buf); + n = mympd_get_updatedb_state(mpd.buf); break; case MPD_IDLE_STICKER: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}"); break; case MPD_IDLE_SUBSCRIPTION: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}"); break; case MPD_IDLE_MESSAGE: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}"); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}"); break; default: - len = 0; + n = 0; + } + if (n > 0) { + mympd_notify(n); } - if (len > 0) - mympd_notify(s); } } } @@ -1039,15 +1028,16 @@ void mympd_mpd_features() { if (LIBMPDCLIENT_CHECK_VERSION(2, 17, 0) && mpd_connection_cmp_server_version(mpd.conn, 0, 21, 0) >= 0) { mpd.feat_advsearch = true; - LOG_INFO() printf("Enabling advanced search\n"); + LOG_INFO() printf("Enabling advanced search.\n"); } else - LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0\n"); + LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0.\n"); } -void mympd_idle(struct mg_mgr *s, int timeout) { +void mympd_idle(int timeout) { struct pollfd fds[1]; int pollrc; + size_t n = 0; switch (mpd.conn_state) { case MPD_DISCONNECTED: @@ -1056,24 +1046,24 @@ void mympd_idle(struct mg_mgr *s, int timeout) { mpd.conn = mpd_connection_new(config.mpdhost, config.mpdport, mpd.timeout); if (mpd.conn == NULL) { printf("MPD connection failed."); - snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mympd_notify(s); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); + mympd_notify(n); 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)); - snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); - mympd_notify(s); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); + mympd_notify(n); 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)); - snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); - mympd_notify(s); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd.conn)); + mympd_notify(n); mpd.conn_state = MPD_FAILURE; return; } @@ -1090,8 +1080,8 @@ void mympd_idle(struct mg_mgr *s, int timeout) { case MPD_FAILURE: printf("MPD connection failed.\n"); - snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mympd_notify(s); + n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); + mympd_notify(n); case MPD_DISCONNECT: case MPD_RECONNECT: @@ -1105,25 +1095,26 @@ void mympd_idle(struct mg_mgr *s, int timeout) { fds[0].fd = mpd_connection_get_fd(mpd.conn); fds[0].events = POLLIN; pollrc = poll(fds, 1, timeout); - if (pollrc > 0 || idle_bitmask_save > 0) { - //Handle idle event - LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode\n"); + unsigned mpd_client_queue_length = tiny_queue_length(mpd_client_queue); + if (pollrc > 0 || mpd_client_queue_length > 0) { + LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode.\n"); mpd_send_noidle(mpd.conn); if (pollrc > 0) { - LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events\n"); + //Handle idle events + LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events.\n"); enum mpd_idle idle_bitmask = mpd_recv_idle(mpd.conn, false); - mympd_parse_idle(s, idle_bitmask); + mympd_parse_idle(idle_bitmask); } else { mpd_response_finish(mpd.conn); } - if (idle_bitmask_save > 0) { - //Handle idle event saved in mympd_callback - LOG_DEBUG() fprintf(stderr, "DEBUG: Handle saved idle event\n"); - mympd_parse_idle(s, idle_bitmask_save); - idle_bitmask_save = 0; + if (mpd_client_queue_length > 0) { + //Handle request + LOG_DEBUG() fprintf(stderr, "DEBUG: Handle request.\n"); + struct work_request_t *req = tiny_queue_shift(mpd_client_queue); + mympd_api(req); } - LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode\n"); + LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode.\n"); mpd_send_idle(mpd.conn); } break; @@ -2647,7 +2638,7 @@ int mympd_put_stats(char *buffer) { void mympd_disconnect() { mpd.conn_state = MPD_DISCONNECT; - mympd_idle(NULL, 0); + mympd_idle(100); } int mympd_smartpls_put(char *buffer, char *playlist) { diff --git a/src/mpd_client.h b/src/mpd_client.h index 9f99409..16ddcc7 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -26,7 +26,10 @@ #define __MPD_CLIENT_H__ #include "../dist/src/mongoose/mongoose.h" +#include "common.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)); \ @@ -65,13 +68,6 @@ } while (0) -#define LOG_INFO() if (config.loglevel >= 1) -#define LOG_VERBOSE() if (config.loglevel >= 2) -#define LOG_DEBUG() if (config.loglevel == 3) - -#define MAX_SIZE 2048 * 400 -#define MAX_ELEMENTS_PER_PAGE 400 - #define GEN_ENUM(X) X, #define GEN_STR(X) #X, #define MPD_CMDS(X) \ @@ -153,7 +149,6 @@ struct t_mpd { // Reponse Buffer char buf[MAX_SIZE]; - size_t buf_size; // States int song_id; @@ -181,38 +176,6 @@ struct list mympd_browsetags; struct list last_played; struct list syscmds; -typedef struct { - long mpdport; - const char *mpdhost; - const char *mpdpass; - const char *webport; - bool ssl; - const char *sslport; - const char *sslcert; - const char *sslkey; - const char *user; - bool coverimage; - const char *coverimagename; - long coverimagesize; - bool stickers; - bool mixramp; - const char *taglist; - const char *searchtaglist; - const char *browsetaglist; - bool smartpls; - const char *varlibdir; - const char *etcdir; - unsigned long max_elements_per_page; - bool syscmds; - bool localplayer; - long streamport; - const char *streamurl; - unsigned long last_played_count; - long loglevel; -} t_config; - -t_config config; - typedef struct { long playCount; long skipCount; @@ -236,16 +199,13 @@ typedef struct { } t_mympd_state; t_mympd_state mympd_state; - -static int is_websocket(const struct mg_connection *nc) { - return nc->flags & MG_F_IS_WEBSOCKET; -} +tiny_queue_t *mpd_client_queue; int randrange(int n); -void mympd_idle(struct mg_mgr *sm, int timeout); -void mympd_parse_idle(struct mg_mgr *s, int idle_bitmask); -void callback_mympd(struct mg_connection *nc, const struct mg_str msg); -void mympd_notify(struct mg_mgr *s); +void mympd_idle(int timeout); +void mympd_parse_idle(int idle_bitmask); +void mympd_api(struct work_request_t *request); +void mympd_notify(size_t n); bool mympd_count_song_id(int song_id, char *name, int value); bool mympd_count_song_uri(const char *uri, char *name, int value); bool mympd_like_song_uri(const char *uri, int value); diff --git a/src/mympd.c b/src/mympd.c index 7f3e449..415279d 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -30,97 +30,23 @@ #include #include #include +#include #include #include "../dist/src/mongoose/mongoose.h" -#include "../dist/src/frozen/frozen.h" #include "../dist/src/inih/ini.h" +#include "common.h" #include "mpd_client.h" +#include "web_server.h" #include "config.h" static sig_atomic_t s_signal_received = 0; -static struct mg_serve_http_opts s_http_server_opts; - static void signal_handler(int sig_num) { signal(sig_num, signal_handler); // Reinstantiate signal handler s_signal_received = sig_num; } -static void handle_api(struct mg_connection *nc, struct http_message *hm) { - if (!is_websocket(nc)) - mg_printf(nc, "%s", "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Type: application/json\r\n\r\n"); - - char buf[1000] = {0}; - int len = sizeof(buf) - 1 < hm->body.len ? sizeof(buf) - 1 : hm->body.len; - memcpy(buf, hm->body.p, len); - struct mg_str d = {buf, len}; - callback_mympd(nc, d); - - if (!is_websocket(nc)) - mg_send_http_chunk(nc, "", 0); /* Send empty chunk, the end of response */ -} - -static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - switch(ev) { - case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { - struct http_message *hm = (struct http_message *) ev_data; - LOG_VERBOSE() printf("New websocket request: %.*s\n", hm->uri.len, hm->uri.p); - if (mg_vcmp(&hm->uri, "/ws") != 0) { - printf("ERROR: Websocket request not to /ws, closing connection\n"); - mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n"); - nc->flags |= MG_F_SEND_AND_CLOSE; - } - break; - } - case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { - LOG_VERBOSE() printf("New Websocket connection established\n"); - struct mg_str d = mg_mk_str("{\"cmd\":\"MPD_API_WELCOME\"}"); - callback_mympd(nc, d); - break; - } - case MG_EV_HTTP_REQUEST: { - struct http_message *hm = (struct http_message *) ev_data; - LOG_VERBOSE() printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p); - if (mg_vcmp(&hm->uri, "/api") == 0) - handle_api(nc, hm); - else - mg_serve_http(nc, hm, s_http_server_opts); - break; - } - case MG_EV_CLOSE: { - if (is_websocket(nc)) { - LOG_VERBOSE() printf("Websocket connection closed\n"); - } - else { - LOG_VERBOSE() printf("HTTP connection closed\n"); - } - break; - } - } -} - -static void ev_handler_http(struct mg_connection *nc_http, int ev, void *ev_data) { - char *host; - char host_header[1024]; - switch(ev) { - case MG_EV_HTTP_REQUEST: { - struct http_message *hm = (struct http_message *) ev_data; - struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); - snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p); - host = strtok(host_header, ":"); - char s_redirect[250]; - if (strcmp(config.sslport, "443") == 0) - snprintf(s_redirect, 250, "https://%s/", host); - else - snprintf(s_redirect, 250, "https://%s:%s/", host, config.sslport); - LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect); - mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); - break; - } - } -} - static int inihandler(void* user, const char* section, const char* name, const char* value) { t_config* p_config = (t_config*)user; char *crap; @@ -373,6 +299,36 @@ bool testdir(char *name, char *dirname) { } } +void *mpd_client_thread(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; + while (s_signal_received == 0) { + mympd_idle(100); + } + mympd_disconnect(); + return NULL; +} + +void *web_server_thread(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; + while (s_signal_received == 0) { + mg_mgr_poll(mgr, 10); + unsigned web_server_queue_length = tiny_queue_length(web_server_queue); + if (web_server_queue_length > 0) { + struct work_result_t *response = tiny_queue_shift(web_server_queue); + if (response->conn_id == 0) { + //Websocket notify from mpd idle + send_ws_notify(mgr, response); + } + else { + //api response + send_api_response(mgr, response); + } + } + } + mg_mgr_free(mgr); + return NULL; +} + int main(int argc, char **argv) { struct mg_mgr mgr; struct mg_connection *nc; @@ -380,6 +336,8 @@ int main(int argc, char **argv) { struct mg_bind_opts bind_opts; const char *err; char testdirname[400]; + mpd_client_queue = tiny_queue_create(); + web_server_queue = tiny_queue_create(); //defaults config.mpdhost = "127.0.0.1"; @@ -447,11 +405,11 @@ int main(int argc, char **argv) { signal(SIGINT, signal_handler); setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); - + mg_mgr_init(&mgr, NULL); if (config.ssl == true) { - nc_http = mg_bind(&mgr, config.webport, ev_handler_http); + nc_http = mg_bind(&mgr, config.webport, ev_handler_redirect); if (nc_http == NULL) { printf("Error listening on port %s\n", config.webport); return EXIT_FAILURE; @@ -552,13 +510,25 @@ int main(int argc, char **argv) { if (config.ssl == true) LOG_INFO() printf("Listening on ssl port %s\n", config.sslport); + pthread_t mpd_client, web_server; + pthread_create(&mpd_client, NULL, mpd_client_thread, &mgr); + pthread_create(&web_server, NULL, web_server_thread, &mgr); + + //Do nothing... + + pthread_join(mpd_client, NULL); + pthread_join(web_server, NULL); + +/* + mg_start_thread(mpd_client_thread, &mgr); + while (s_signal_received == 0) { mg_mgr_poll(&mgr, 100); - mympd_idle(&mgr, 0); } mg_mgr_free(&mgr); +*/ + list_free(&mpd_tags); list_free(&mympd_tags); - mympd_disconnect(); return EXIT_SUCCESS; } diff --git a/src/tiny_queue.c b/src/tiny_queue.c new file mode 100644 index 0000000..0da15ee --- /dev/null +++ b/src/tiny_queue.c @@ -0,0 +1,80 @@ +/* myMPD + (c) 2018 Juergen Mang + This project's homepage is: https://github.com/jcorporation/mympd + + This linked list implementation is based on: https://github.com/joshkunz/ashuffle + + 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 "tiny_queue.h" + +tiny_queue_t* tiny_queue_create() { + struct tiny_queue_t* queue = (struct tiny_queue_t*)malloc(sizeof(struct tiny_queue_t)); + queue->head = NULL; + queue->tail = NULL; + queue->length = 0; + + queue->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; + queue->wakeup = (pthread_cond_t)PTHREAD_COND_INITIALIZER; + return queue; +} + +void tiny_queue_push(tiny_queue_t *queue, void *data) { + pthread_mutex_lock(&queue->mutex); + struct tiny_msg_t* new_node = (struct tiny_msg_t*)malloc(sizeof(struct tiny_msg_t)); + new_node->data = data; + new_node->next = NULL; + queue->length++; + if (queue->head == NULL && queue->tail == NULL){ + queue->head = queue->tail = new_node; + } + else { + queue->tail->next = new_node; + queue->tail = new_node; + } + pthread_mutex_unlock(&queue->mutex); + pthread_cond_signal(&queue->wakeup); +} + +int tiny_queue_length(tiny_queue_t *queue) { + pthread_mutex_lock(&queue->mutex); + unsigned len = queue->length; + pthread_mutex_unlock(&queue->mutex); + return len; +} + +void *tiny_queue_shift(tiny_queue_t *queue) { + pthread_mutex_lock(&queue->mutex); + while (queue->head == NULL) { + // block if buffer is empty + pthread_cond_wait(&queue->wakeup, &queue->mutex); + } + + struct tiny_msg_t* current_head = queue->head; + void *data = current_head->data; + if (queue->head == queue->tail) { + queue->head = queue->tail = NULL; + } + else { + queue->head = queue->head->next; + } + free(current_head); + queue->length--; + pthread_mutex_unlock(&queue->mutex); + return data; +} diff --git a/src/validate.h b/src/tiny_queue.h similarity index 56% rename from src/validate.h rename to src/tiny_queue.h index 1a14d99..0adecb6 100644 --- a/src/validate.h +++ b/src/tiny_queue.h @@ -2,11 +2,7 @@ (c) 2018 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 linked list implementation is based on: https://github.com/joshkunz/ashuffle 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 @@ -21,6 +17,24 @@ with this program; if not, write to the Free Software Foundation, Inc., Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#ifndef __TINY_QUEUE_H__ +#define __TINY_QUEUE_H__ -void sanitize_string(const char *data); -int validate_path(char *path, const char *basepath); +typedef struct tiny_msg_t { + void *data; + struct tiny_msg_t *next; +} tiny_msg_t; + +typedef struct tiny_queue_t { + unsigned length; + struct tiny_msg_t *head; + struct tiny_msg_t *tail; + pthread_mutex_t mutex; + pthread_cond_t wakeup; +} tiny_queue_t; + +tiny_queue_t* tiny_queue_create(); +void tiny_queue_push(struct tiny_queue_t *queue, void *data); +void *tiny_queue_shift(struct tiny_queue_t *queue); +int tiny_queue_length(struct tiny_queue_t *queue); +#endif diff --git a/src/web_server.c b/src/web_server.c new file mode 100644 index 0000000..3116f6b --- /dev/null +++ b/src/web_server.c @@ -0,0 +1,144 @@ +/* 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 "common.h" +#include "config.h" +#include "web_server.h" +#include "mpd_client.h" + +static unsigned long s_next_id = 1; + +int is_websocket(const struct mg_connection *nc) { + return nc->flags & MG_F_IS_WEBSOCKET; +} + +void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { + struct mg_connection *c; + LOG_DEBUG() fprintf(stderr, "DEBUG: Got ws notify, broadcasting\n"); + + for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) { + if (!is_websocket(c)) + continue; + mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, response->data, response->length); + } + free(response); +} + +void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { + struct mg_connection *c; + LOG_DEBUG() fprintf(stderr, "DEBUG: Got API response for connection %lu.\n", response->conn_id); + + for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) { + if (c->user_data != NULL) { + if ((unsigned long)c->user_data == response->conn_id) { + LOG_DEBUG() fprintf(stderr, "DEBUG: Sending to connection %lu: %s\n", (unsigned long)c->user_data, response->data); + mg_send_head(c, 200, response->length, "Content-Type: application/json"); + mg_printf(c, "%s", response->data); + } + } + } + free(response); +} + +void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { + (void) nc; + (void) ev_data; + + switch(ev) { + case MG_EV_ACCEPT: { + nc->user_data = (void *)++s_next_id; + LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %lu.\n", s_next_id); + break; + } + case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { + struct http_message *hm = (struct http_message *) ev_data; + LOG_VERBOSE() printf("New websocket request: %.*s\n", hm->uri.len, hm->uri.p); + if (mg_vcmp(&hm->uri, "/ws") != 0) { + printf("ERROR: Websocket request not to /ws, closing connection\n"); + mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n"); + nc->flags |= MG_F_SEND_AND_CLOSE; + } + break; + } + case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { + LOG_VERBOSE() printf("New Websocket connection established.\n"); + char response[] = "{\"type\": \"welcome\", \"data\": {\"mympdVersion\": \"" MYMPD_VERSION "\"}}"; + mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response, strlen(response)); + break; + } + case MG_EV_HTTP_REQUEST: { + struct http_message *hm = (struct http_message *) ev_data; + LOG_VERBOSE() printf("HTTP request: %.*s\n", 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 = (unsigned long)nc->user_data; + request->length = copy_string(request->data, hm->body.p, 1000, hm->body.len); + //sizeof(request->data) - 1 < hm->body.len ? sizeof(request->data) - 1 : hm->body.len; + //memcpy(request->data, hm->body.p, request->length); + tiny_queue_push(mpd_client_queue, request); + } + else { + mg_serve_http(nc, hm, s_http_server_opts); + } + break; + } + case MG_EV_CLOSE: { + if (nc->user_data) { + LOG_VERBOSE() fprintf(stderr, "HTTP connection %lu closed.\n", (unsigned long)nc->user_data); + nc->user_data = NULL; + } + else { + LOG_VERBOSE() printf("HTTP connection closed.\n"); + } + break; + } + default: { + break; + } + } +} + +void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data) { + char *host; + char host_header[1024]; + switch(ev) { + case MG_EV_HTTP_REQUEST: { + struct http_message *hm = (struct http_message *) ev_data; + struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); + snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p); + host = strtok(host_header, ":"); + char s_redirect[250]; + if (strcmp(config.sslport, "443") == 0) + snprintf(s_redirect, 250, "https://%s/", host); + else + snprintf(s_redirect, 250, "https://%s:%s/", host, config.sslport); + LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect); + mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); + break; + } + default: { + break; + } + } +} diff --git a/src/web_server.h b/src/web_server.h new file mode 100644 index 0000000..740ae74 --- /dev/null +++ b/src/web_server.h @@ -0,0 +1,52 @@ +/* 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 __WEB_SERVER_H__ +#define __WEB_SERVER_H__ + +#include "../dist/src/mongoose/mongoose.h" +#include "tiny_queue.h" + +tiny_queue_t *web_server_queue; + +struct work_request_t { + unsigned 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 { + unsigned long conn_id; // needed to identify the connection where to send the reply + char data[MAX_SIZE]; + int length; +} work_result_t; + +struct mg_serve_http_opts s_http_server_opts; + +int is_websocket(const struct mg_connection *nc); +void ev_handler(struct mg_connection *nc, int ev, void *ev_data); +void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data); +void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response); +void send_api_response(struct mg_mgr *mgr, struct work_result_t *response); +#endif From b65a5de933454d057ea546430b3da07756b9f4aa Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sat, 5 Jan 2019 00:25:30 +0000 Subject: [PATCH 04/59] Fix: cleanups --- CMakeLists.txt | 3 ++- dist/htdocs/sw.min.js | 2 +- src/mpd_client.h | 1 - src/mympd.c | 18 +++++------------- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4369c8a..21382a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ else() endif() find_package(LibMPDClient REQUIRED) +find_package(Threads REQUIRED) configure_file(src/config.h.in ${PROJECT_BINARY_DIR}/config.h) include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_INCLUDE_DIR}) @@ -41,7 +42,7 @@ set(SOURCES ) add_executable(mympd ${SOURCES}) -target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${OPENSSL_LIBRARIES}) +target_link_libraries(mympd ${LIBMPDCLIENT_LIBRARY} ${OPENSSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS mympd DESTINATION bin) install(FILES contrib/mympd.1 DESTINATION share/man/man1) diff --git a/dist/htdocs/sw.min.js b/dist/htdocs/sw.min.js index ecae2c6..a50cd2a 100644 --- a/dist/htdocs/sw.min.js +++ b/dist/htdocs/sw.min.js @@ -10,5 +10,5 @@ function(){function a(a){return function(d){c||(c=!0,a.call(b,d))}}var b=this,c= void 0;try{d=a.then}catch(h){this.reject_(h);return}"function"==typeof d?this.settleSameAsThenable_(d,a):this.fulfill_(a)};c.prototype.reject_=function(a){this.settle_(2,a)};c.prototype.fulfill_=function(a){this.settle_(1,a)};c.prototype.settle_=function(a,b){if(0!=this.state_)throw Error("Cannot settle("+a+", "+b+"): Promise already settled in state"+this.state_);this.state_=a;this.result_=b;this.executeOnSettledCallbacks_()};c.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var a= 0;a Date: Sat, 5 Jan 2019 12:29:10 +0000 Subject: [PATCH 05/59] Fix: move webserver in separate source files --- CMakeLists.txt | 12 +++- src/config.h.in | 34 ----------- src/{common.c => global.c} | 3 +- src/{common.h => global.h.in} | 24 +++++++- src/mpd_client.c | 12 ++-- src/mpd_client.h | 2 +- src/mympd.c | 106 +++++++++------------------------- src/tiny_queue.c | 12 ++++ src/tiny_queue.h | 1 + src/web_server.c | 97 ++++++++++++++++++++++++++++--- src/web_server.h | 10 +--- 11 files changed, 172 insertions(+), 141 deletions(-) delete mode 100644 src/config.h.in rename src/{common.c => global.c} (97%) rename src/{common.h => global.h.in} (75%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 21382a8..8b6810e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,18 @@ else() set(DEBUG "ON") endif() +if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + MESSAGE("++ 64 bit architecture") + set(PKGARCH64 "ON") +else() + MESSAGE("++ 32 bit architecture") + set(PKGARCH64 "OFF") +endif() + find_package(LibMPDClient REQUIRED) find_package(Threads REQUIRED) -configure_file(src/config.h.in ${PROJECT_BINARY_DIR}/config.h) +configure_file(src/global.h.in ${PROJECT_BINARY_DIR}/global.h) include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_INCLUDE_DIR}) include(CheckCSourceCompiles) @@ -35,7 +43,7 @@ set(SOURCES src/web_server.c src/list.c src/tiny_queue.c - src/common.c + src/global.c dist/src/mongoose/mongoose.c dist/src/frozen/frozen.c dist/src/inih/ini.c diff --git a/src/config.h.in b/src/config.h.in deleted file mode 100644 index d5c41cd..0000000 --- a/src/config.h.in +++ /dev/null @@ -1,34 +0,0 @@ -/* myMPD - (c) 2018 Juergen Mang - This project's homepage is: https://github.com/jcorporation/ympd - - 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 __CONFIG_H__ -#define __CONFIG_H__ - -#define MYMPD_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} -#define MYMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} -#define MYMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} -#define MYMPD_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}" -#define SRC_PATH "${ASSETS_PATH}" -#cmakedefine DEBUG -#endif diff --git a/src/common.c b/src/global.c similarity index 97% rename from src/common.c rename to src/global.c index d1e6a09..943402b 100644 --- a/src/common.c +++ b/src/global.c @@ -22,11 +22,10 @@ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include #include #include #include -#include "common.h" +#include "global.h" void sanitize_string(const char *data) { static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz" diff --git a/src/common.h b/src/global.h.in similarity index 75% rename from src/common.h rename to src/global.h.in index 6213976..025f2a0 100644 --- a/src/common.h +++ b/src/global.h.in @@ -22,19 +22,39 @@ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef __COMMON_H__ -#define __COMMON_H__ +#ifndef __GLOBAL_H__ +#define __GLOBAL_H__ #include #include +#include +//architecture +#cmakedefine PKGARCH64 + +//myMPD version from cmake +#define MYMPD_VERSION_MAJOR ${CPACK_PACKAGE_VERSION_MAJOR} +#define MYMPD_VERSION_MINOR ${CPACK_PACKAGE_VERSION_MINOR} +#define MYMPD_VERSION_PATCH ${CPACK_PACKAGE_VERSION_PATCH} +#define MYMPD_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}" + +//Webserver document root +#define DOC_ROOT "${ASSETS_PATH}" + +//Max size of mpd_client response buffer #define MAX_SIZE 2048 * 400 #define MAX_ELEMENTS_PER_PAGE 400 +//central logging definition +#cmakedefine DEBUG #define LOG_INFO() if (config.loglevel >= 1) #define LOG_VERBOSE() if (config.loglevel >= 2) #define LOG_DEBUG() if (config.loglevel == 3) +//signal handler +sig_atomic_t s_signal_received; + +//myMPD configuration typedef struct { long mpdport; const char *mpdhost; diff --git a/src/mpd_client.c b/src/mpd_client.c index eb4b61a..17d95ab 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -30,12 +30,10 @@ #include #include #include +#include #include -#include "common.h" #include "mpd_client.h" -#include "web_server.h" -#include "config.h" #include "../dist/src/frozen/frozen.h" const char * mpd_cmd_strs[] = { @@ -820,7 +818,11 @@ void mympd_api(struct work_request_t *request) { #ifdef DEBUG clock_gettime(CLOCK_MONOTONIC_RAW, &end); uint64_t delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000; + #ifdef PKGARCH44 fprintf(stderr, "DEBUG: Time used: %lu\n", delta_us); + #else + fprintf(stderr, "DEBUG: Time used: %llu\n", delta_us); + #endif #endif if (n == 0) { @@ -1835,7 +1837,7 @@ int mympd_get_cover(const char *uri, char *cover, int cover_len) { path += 8; replacechar(path, '/', '_'); replacechar(path, '.', '_'); - snprintf(cover, cover_len, "%s/pics/%s.png", SRC_PATH, path); + snprintf(cover, cover_len, "%s/pics/%s.png", DOC_ROOT, path); if (access(cover, F_OK ) == -1 ) len = snprintf(cover, cover_len, "/assets/coverimage-httpstream.png"); else @@ -1846,7 +1848,7 @@ int mympd_get_cover(const char *uri, char *cover, int cover_len) { else { if (mpd.feat_library) { dirname(path); - snprintf(cover, cover_len, "%s/library/%s/%s", SRC_PATH, path, config.coverimagename); + snprintf(cover, cover_len, "%s/library/%s/%s", DOC_ROOT, path, config.coverimagename); if (access(cover, F_OK ) == -1 ) len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png"); else diff --git a/src/mpd_client.h b/src/mpd_client.h index 6bab60b..59d13f3 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -25,7 +25,7 @@ #ifndef __MPD_CLIENT_H__ #define __MPD_CLIENT_H__ -#include "common.h" +#include "global.h" #include "web_server.h" #include "list.h" #include "tiny_queue.h" diff --git a/src/mympd.c b/src/mympd.c index 05919cb..21fc07f 100644 --- a/src/mympd.c +++ b/src/mympd.c @@ -31,16 +31,13 @@ #include #include #include +#include #include -#include "../dist/src/mongoose/mongoose.h" #include "../dist/src/inih/ini.h" -#include "common.h" +#include "global.h" #include "mpd_client.h" #include "web_server.h" -#include "config.h" - -static sig_atomic_t s_signal_received = 0; static void signal_handler(int sig_num) { signal(sig_num, signal_handler); // Reinstantiate signal handler @@ -307,33 +304,8 @@ void *mpd_client_thread() { return NULL; } -void *web_server_thread(void *arg) { - struct mg_mgr *mgr = (struct mg_mgr *) arg; - while (s_signal_received == 0) { - mg_mgr_poll(mgr, 10); - unsigned web_server_queue_length = tiny_queue_length(web_server_queue); - if (web_server_queue_length > 0) { - struct work_result_t *response = tiny_queue_shift(web_server_queue); - if (response->conn_id == 0) { - //Websocket notify from mpd idle - send_ws_notify(mgr, response); - } - else { - //api response - send_api_response(mgr, response); - } - } - } - mg_mgr_free(mgr); - return NULL; -} - int main(int argc, char **argv) { - struct mg_mgr mgr; - struct mg_connection *nc; - struct mg_connection *nc_http; - struct mg_bind_opts bind_opts; - const char *err; + s_signal_received = 0; char testdirname[400]; mpd_client_queue = tiny_queue_create(); web_server_queue = tiny_queue_create(); @@ -385,7 +357,7 @@ int main(int argc, char **argv) { } else { printf("myMPD %s\n" - "Copyright (C) 2018 Juergen Mang \n" + "Copyright (C) 2018-2019 Juergen Mang \n" "https://github.com/jcorporation/myMPD\n" "Built " __DATE__ " "__TIME__"\n\n" "Usage: %s /path/to/mympd.conf\n", @@ -405,67 +377,45 @@ int main(int argc, char **argv) { setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); - mg_mgr_init(&mgr, NULL); - - if (config.ssl == true) { - nc_http = mg_bind(&mgr, config.webport, ev_handler_redirect); - if (nc_http == NULL) { - printf("Error listening on port %s\n", config.webport); - return EXIT_FAILURE; - } - memset(&bind_opts, 0, sizeof(bind_opts)); - bind_opts.ssl_cert = config.sslcert; - bind_opts.ssl_key = config.sslkey; - bind_opts.error_string = &err; - - nc = mg_bind_opt(&mgr, config.sslport, ev_handler, bind_opts); - if (nc == NULL) { - printf("Error listening on port %s: %s\n", config.sslport, err); - mg_mgr_free(&mgr); - return EXIT_FAILURE; - } - } - else { - nc = mg_bind(&mgr, config.webport, ev_handler); - if (nc == NULL) { - printf("Error listening on port %s\n", config.webport); - mg_mgr_free(&mgr); - return EXIT_FAILURE; - } + //init webserver + if (!web_server_init()) { + return EXIT_FAILURE; } + //drop privileges if (config.user != NULL) { LOG_INFO() printf("Droping privileges to %s\n", config.user); struct passwd *pw; if ((pw = getpwnam(config.user)) == NULL) { printf("getpwnam() failed, unknown user\n"); - mg_mgr_free(&mgr); + web_server_free(); return EXIT_FAILURE; } else if (setgroups(0, NULL) != 0) { printf("setgroups() failed\n"); - mg_mgr_free(&mgr); + web_server_free(); return EXIT_FAILURE; } else if (setgid(pw->pw_gid) != 0) { printf("setgid() failed\n"); - mg_mgr_free(&mgr); + web_server_free(); return EXIT_FAILURE; } else if (setuid(pw->pw_uid) != 0) { printf("setuid() failed\n"); - mg_mgr_free(&mgr); + web_server_free(); return EXIT_FAILURE; } } if (getuid() == 0) { printf("myMPD should not be run with root privileges\n"); - mg_mgr_free(&mgr); + web_server_free(); return EXIT_FAILURE; } - if (!testdir("Document root", SRC_PATH)) + //check needed directories + if (!testdir("Document root", DOC_ROOT)) return EXIT_FAILURE; - snprintf(testdirname, 400, "%s/library", SRC_PATH); + snprintf(testdirname, 400, "%s/library", DOC_ROOT); if (testdir("Link to mpd music_directory", testdirname)) { LOG_INFO() printf("Enabling featLibrary support\n"); mpd.feat_library = true; @@ -487,40 +437,38 @@ int main(int argc, char **argv) { if (!testdir("State dir", testdirname)) return EXIT_FAILURE; + //read myMPD states under config.varlibdir read_statefiles(); + //read system command files list_init(&syscmds); read_syscmds(); list_sort_by_value(&syscmds, true); + //init lists for tag handling list_init(&mpd_tags); list_init(&mympd_tags); + + //read last played songs history file list_init(&last_played); LOG_INFO() printf("Reading last played songs: %d\n", read_last_played()); - if (config.ssl == true) - mg_set_protocol_http_websocket(nc_http); - - mg_set_protocol_http_websocket(nc); - s_http_server_opts.document_root = SRC_PATH; - s_http_server_opts.enable_directory_listing = "no"; - - LOG_INFO() printf("Listening on http port %s\n", config.webport); - if (config.ssl == true) - LOG_INFO() printf("Listening on ssl port %s\n", config.sslport); - + //Create working threads pthread_t mpd_client, web_server; + //mpd connection pthread_create(&mpd_client, NULL, mpd_client_thread, NULL); - pthread_create(&web_server, NULL, web_server_thread, &mgr); + //webserver + pthread_create(&web_server, NULL, web_server_thread, NULL); //Do nothing... //clean up - //todo: destroy tiny queues pthread_join(mpd_client, NULL); pthread_join(web_server, NULL); list_free(&mpd_tags); list_free(&mympd_tags); + tiny_queue_free(web_server_queue); + tiny_queue_free(mpd_client_queue); return EXIT_SUCCESS; } diff --git a/src/tiny_queue.c b/src/tiny_queue.c index 0da15ee..2389bc0 100644 --- a/src/tiny_queue.c +++ b/src/tiny_queue.c @@ -34,6 +34,18 @@ tiny_queue_t* tiny_queue_create() { return queue; } +void tiny_queue_free(tiny_queue_t *queue) { + struct tiny_msg_t *current_head = queue->head, *tmp = NULL; + while (current_head != NULL) { + free(current_head->data); + tmp = current_head; + current_head = current_head->next; + free(tmp); + } + free(queue); +} + + void tiny_queue_push(tiny_queue_t *queue, void *data) { pthread_mutex_lock(&queue->mutex); struct tiny_msg_t* new_node = (struct tiny_msg_t*)malloc(sizeof(struct tiny_msg_t)); diff --git a/src/tiny_queue.h b/src/tiny_queue.h index 0adecb6..7a94124 100644 --- a/src/tiny_queue.h +++ b/src/tiny_queue.h @@ -34,6 +34,7 @@ typedef struct tiny_queue_t { } tiny_queue_t; tiny_queue_t* tiny_queue_create(); +void tiny_queue_free(tiny_queue_t *queue); void tiny_queue_push(struct tiny_queue_t *queue, void *data); void *tiny_queue_shift(struct tiny_queue_t *queue); int tiny_queue_length(struct tiny_queue_t *queue); diff --git a/src/web_server.c b/src/web_server.c index 3116f6b..3e3f457 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -22,18 +22,99 @@ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "common.h" -#include "config.h" +#include "global.h" #include "web_server.h" #include "mpd_client.h" +#include "../dist/src/mongoose/mongoose.h" +//non-api definitions static unsigned long s_next_id = 1; +struct mg_mgr mgr; +static struct mg_serve_http_opts s_http_server_opts; -int is_websocket(const struct mg_connection *nc) { +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); + +//api functions +bool web_server_init() { + struct mg_connection *nc; + struct mg_connection *nc_http; + struct mg_bind_opts bind_opts; + const char *err; + + mg_mgr_init(&mgr, NULL); + + if (config.ssl == true) { + nc_http = mg_bind(&mgr, config.webport, ev_handler_redirect); + if (nc_http == NULL) { + printf("Error listening on port %s\n", config.webport); + mg_mgr_free(&mgr); + return false; + } + mg_set_protocol_http_websocket(nc_http); + LOG_INFO() printf("Listening on http port %s (redirect only).\n", config.webport); + + memset(&bind_opts, 0, sizeof(bind_opts)); + bind_opts.ssl_cert = config.sslcert; + bind_opts.ssl_key = config.sslkey; + bind_opts.error_string = &err; + nc = mg_bind_opt(&mgr, config.sslport, ev_handler, bind_opts); + if (nc == NULL) { + printf("Error listening on port %s: %s\n", config.sslport, err); + mg_mgr_free(&mgr); + return false; + } + LOG_INFO() printf("Listening on ssl port %s\n", config.sslport); + } + else { + nc = mg_bind(&mgr, config.webport, ev_handler); + if (nc == NULL) { + printf("Error listening on port %s\n", config.webport); + mg_mgr_free(&mgr); + return false; + } + LOG_INFO() printf("Listening on http port %s\n", config.webport); + } + + mg_set_protocol_http_websocket(nc); + s_http_server_opts.document_root = DOC_ROOT; + s_http_server_opts.enable_directory_listing = "no"; + return true; +} + +void web_server_free() { + mg_mgr_free(&mgr); +} + +void *web_server_thread() { + while (s_signal_received == 0) { + mg_mgr_poll(&mgr, 10); + unsigned web_server_queue_length = tiny_queue_length(web_server_queue); + if (web_server_queue_length > 0) { + struct work_result_t *response = tiny_queue_shift(web_server_queue); + if (response->conn_id == 0) { + //Websocket notify from mpd idle + send_ws_notify(&mgr, response); + } + else { + //api response + send_api_response(&mgr, response); + } + } + } + mg_mgr_free(&mgr); + return NULL; +} + +//non-api functions +static int is_websocket(const struct mg_connection *nc) { return nc->flags & MG_F_IS_WEBSOCKET; } -void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { +static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { struct mg_connection *c; LOG_DEBUG() fprintf(stderr, "DEBUG: Got ws notify, broadcasting\n"); @@ -45,7 +126,7 @@ void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { free(response); } -void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { +static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { struct mg_connection *c; LOG_DEBUG() fprintf(stderr, "DEBUG: Got API response for connection %lu.\n", response->conn_id); @@ -61,7 +142,7 @@ void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { free(response); } -void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { +static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { (void) nc; (void) ev_data; @@ -94,8 +175,6 @@ void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { struct work_request_t *request = (struct work_request_t*)malloc(sizeof(struct work_request_t)); request->conn_id = (unsigned long)nc->user_data; request->length = copy_string(request->data, hm->body.p, 1000, hm->body.len); - //sizeof(request->data) - 1 < hm->body.len ? sizeof(request->data) - 1 : hm->body.len; - //memcpy(request->data, hm->body.p, request->length); tiny_queue_push(mpd_client_queue, request); } else { @@ -119,7 +198,7 @@ void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { } } -void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data) { +static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data) { char *host; char host_header[1024]; switch(ev) { diff --git a/src/web_server.h b/src/web_server.h index 740ae74..6e859ba 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -25,7 +25,6 @@ #ifndef __WEB_SERVER_H__ #define __WEB_SERVER_H__ -#include "../dist/src/mongoose/mongoose.h" #include "tiny_queue.h" tiny_queue_t *web_server_queue; @@ -42,11 +41,8 @@ struct work_result_t { int length; } work_result_t; -struct mg_serve_http_opts s_http_server_opts; +void *web_server_thread(); +bool web_server_init(); +void web_server_free(); -int is_websocket(const struct mg_connection *nc); -void ev_handler(struct mg_connection *nc, int ev, void *ev_data); -void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_data); -void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response); -void send_api_response(struct mg_mgr *mgr, struct work_result_t *response); #endif From 4c086da611c2e8595bd9366c9e7e08aa6c78b09a Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sat, 5 Jan 2019 21:28:18 +0000 Subject: [PATCH 06/59] Fix: various cleanups --- CMakeLists.txt | 4 +- src/global.h.in | 12 ++ src/{mympd.c => main.c} | 57 ++++---- src/mpd_client.c | 298 ++++++++++++++++++---------------------- src/mpd_client.h | 105 +++++++------- src/web_server.c | 3 +- 6 files changed, 229 insertions(+), 250 deletions(-) rename src/{mympd.c => main.c} (88%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b6810e..9eb144d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,12 +38,12 @@ include_directories(${OPENSSL_INCLUDE_DIR}) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS NS_ENABLE_SSL) set(SOURCES - src/mympd.c + src/main.c + src/global.c src/mpd_client.c src/web_server.c src/list.c src/tiny_queue.c - src/global.c dist/src/mongoose/mongoose.c dist/src/frozen/frozen.c dist/src/inih/ini.c diff --git a/src/global.h.in b/src/global.h.in index 025f2a0..23e9d3c 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -51,6 +51,17 @@ #define LOG_VERBOSE() if (config.loglevel >= 2) #define LOG_DEBUG() if (config.loglevel == 3) +//check and return buffer size +#define CHECK_RETURN_LEN() do { \ + if (len > MAX_SIZE) \ + printf("Buffer truncated: %d, %d\n", len, MAX_SIZE); \ + return len; \ +} while (0) + +//enumeration helpers +#define GEN_ENUM(X) X, +#define GEN_STR(X) #X, + //signal handler sig_atomic_t s_signal_received; @@ -87,6 +98,7 @@ typedef struct { t_config config; +//global functions void sanitize_string(const char *data); int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len); #endif diff --git a/src/mympd.c b/src/main.c similarity index 88% rename from src/mympd.c rename to src/main.c index 21fc07f..32df3a6 100644 --- a/src/mympd.c +++ b/src/main.c @@ -165,7 +165,7 @@ void read_statefiles() { char value[400]; LOG_INFO() printf("Reading states\n"); - if (mympd_state_get("notificationWeb", value)) { + if (mpd_client_state_get("notificationWeb", value)) { if (strcmp(value, "true") == 0) mympd_state.notificationWeb = true; else @@ -173,10 +173,10 @@ void read_statefiles() { } else { mympd_state.notificationWeb = false; - mympd_state_set("notificationWeb", "false"); + mpd_client_state_set("notificationWeb", "false"); } - if (mympd_state_get("notificationPage", value)) { + if (mpd_client_state_get("notificationPage", value)) { if (strcmp(value, "true") == 0) mympd_state.notificationPage = true; else @@ -184,77 +184,77 @@ void read_statefiles() { } else { mympd_state.notificationPage = true; - mympd_state_set("notificationPage", "true"); + mpd_client_state_set("notificationPage", "true"); } - if (mympd_state_get("jukeboxMode", value)) + if (mpd_client_state_get("jukeboxMode", value)) mympd_state.jukeboxMode = strtol(value, &crap, 10); else { mympd_state.jukeboxMode = 0; - mympd_state_set("jukeboxMode", "0"); + mpd_client_state_set("jukeboxMode", "0"); } - if (mympd_state_get("jukeboxPlaylist", value)) + if (mpd_client_state_get("jukeboxPlaylist", value)) mympd_state.jukeboxPlaylist = strdup(value); else { mympd_state.jukeboxPlaylist = strdup("Database"); - mympd_state_set("jukeboxPlaylist", "Database"); + mpd_client_state_set("jukeboxPlaylist", "Database"); } - if (mympd_state_get("jukeboxQueueLength", value)) + if (mpd_client_state_get("jukeboxQueueLength", value)) mympd_state.jukeboxQueueLength = strtol(value, &crap, 10); else { mympd_state.jukeboxQueueLength = 1; - mympd_state_set("jukeboxQueueLength", "1"); + mpd_client_state_set("jukeboxQueueLength", "1"); } - if (mympd_state_get("colsQueueCurrent", value)) + if (mpd_client_state_get("colsQueueCurrent", value)) mympd_state.colsQueueCurrent = strdup(value); else { mympd_state.colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mympd_state_set("colsQueueCurrent", mympd_state.colsQueueCurrent); + mpd_client_state_set("colsQueueCurrent", mympd_state.colsQueueCurrent); } - if (mympd_state_get("colsSearch", value)) + if (mpd_client_state_get("colsSearch", value)) mympd_state.colsSearch = strdup(value); else { mympd_state.colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mympd_state_set("colsSearch", mympd_state.colsSearch); + mpd_client_state_set("colsSearch", mympd_state.colsSearch); } - if (mympd_state_get("colsBrowseDatabase", value)) + if (mpd_client_state_get("colsBrowseDatabase", value)) mympd_state.colsBrowseDatabase = strdup(value); else { mympd_state.colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]"); - mympd_state_set("colsBrowseDatabase", mympd_state.colsBrowseDatabase); + mpd_client_state_set("colsBrowseDatabase", mympd_state.colsBrowseDatabase); } - if (mympd_state_get("colsBrowsePlaylistsDetail", value)) + if (mpd_client_state_get("colsBrowsePlaylistsDetail", value)) mympd_state.colsBrowsePlaylistsDetail = strdup(value); else { mympd_state.colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mympd_state_set("colsBrowsePlaylistsDetail", mympd_state.colsBrowsePlaylistsDetail); + mpd_client_state_set("colsBrowsePlaylistsDetail", mympd_state.colsBrowsePlaylistsDetail); } - if (mympd_state_get("colsBrowseFilesystem", value)) + if (mpd_client_state_get("colsBrowseFilesystem", value)) mympd_state.colsBrowseFilesystem = strdup(value); else { mympd_state.colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mympd_state_set("colsBrowseFilesystem", mympd_state.colsBrowseFilesystem); + mpd_client_state_set("colsBrowseFilesystem", mympd_state.colsBrowseFilesystem); } - if (mympd_state_get("colsPlayback", value)) + if (mpd_client_state_get("colsPlayback", value)) mympd_state.colsPlayback = strdup(value); else { mympd_state.colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); - mympd_state_set("colsPlayback", mympd_state.colsPlayback); + mpd_client_state_set("colsPlayback", mympd_state.colsPlayback); } - if (mympd_state_get("colsQueueLastPlayed", value)) + if (mpd_client_state_get("colsQueueLastPlayed", value)) mympd_state.colsQueueLastPlayed = strdup(value); else { mympd_state.colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); - mympd_state_set("colsQueueLastPlayed", mympd_state.colsQueueLastPlayed); + mpd_client_state_set("colsQueueLastPlayed", mympd_state.colsQueueLastPlayed); } } @@ -262,6 +262,7 @@ int read_last_played() { char cfgfile[400]; char *line; char *data; + char *crap; size_t n = 0; ssize_t read; long value; @@ -276,7 +277,7 @@ int read_last_played() { value = strtol(line, &data, 10); if (strlen(data) > 2) data = data + 2; - strtok(data, "\n"); + strtok_r(data, "\n", &crap); list_push(&last_played, data, value); } fclose(fp); @@ -298,9 +299,9 @@ bool testdir(char *name, char *dirname) { void *mpd_client_thread() { while (s_signal_received == 0) { - mympd_idle(100); + mpd_client_idle(100); } - mympd_disconnect(); + mpd_client_disconnect(); return NULL; } @@ -309,6 +310,7 @@ int main(int argc, char **argv) { char testdirname[400]; mpd_client_queue = tiny_queue_create(); web_server_queue = tiny_queue_create(); + //mympd_queue = tiny_queue_create(); //defaults config.mpdhost = "127.0.0.1"; @@ -470,5 +472,6 @@ int main(int argc, char **argv) { list_free(&mympd_tags); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); + //tiny_queue_free(mympd_queue); return EXIT_SUCCESS; } diff --git a/src/mpd_client.c b/src/mpd_client.c index 17d95ab..6d46f5a 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -1,5 +1,5 @@ /* myMPD - (c) 2018 Juergen Mang + (c) 2018-2019 Juergen Mang This project's homepage is: https://github.com/jcorporation/mympd myMPD ist fork of: @@ -45,10 +45,10 @@ static inline enum mpd_cmd_ids get_cmd_id(const char *cmd) { if (!strncmp(cmd, mpd_cmd_strs[i], strlen(mpd_cmd_strs[i]))) return i; - return -1; + return 0; } -void mympd_api(struct work_request_t *request) { +void mpd_client_api(struct work_request_t *request) { size_t n = 0; char *cmd; unsigned int uint_buf1, uint_buf2, uint_rc; @@ -78,9 +78,6 @@ void mympd_api(struct work_request_t *request) { return; } - if (cmd_id == -1) - cmd_id = get_cmd_id("MPD_API_UNKNOWN"); - #ifdef DEBUG clock_gettime(CLOCK_MONOTONIC_RAW, &start); #endif @@ -93,7 +90,7 @@ void mympd_api(struct work_request_t *request) { if (config.stickers) { je = json_scanf(request->data, request->length, "{data: {uri: %Q, like: %d}}", &p_charbuf1, &uint_buf1); if (je == 2) { - if (!mympd_like_song_uri(p_charbuf1, uint_buf1)) + if (!mpd_client_like_song_uri(p_charbuf1, uint_buf1)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); else n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); @@ -149,7 +146,7 @@ void mympd_api(struct work_request_t *request) { break; } if (n == 0) { - if (mympd_state_set(p_charbuf1, cols)) + if (mpd_client_state_set(p_charbuf1, cols)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } free(p_charbuf1); @@ -161,7 +158,7 @@ void mympd_api(struct work_request_t *request) { if (je == 1) { int_buf1 = list_get_value(&syscmds, p_charbuf1); if (int_buf1 > -1) - n = mympd_syscmd(mpd.buf, p_charbuf1, int_buf1); + 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\"}"); @@ -173,35 +170,35 @@ void mympd_api(struct work_request_t *request) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); break; case MPD_API_PLAYER_STATE: - n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); + n = 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 (!mympd_state_set("notificationWeb", (mympd_state.notificationWeb == true ? "true" : "false"))) + 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.\"}"); je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mympd_state.notificationPage); if (je == 1) - if (!mympd_state_set("notificationPage", (mympd_state.notificationPage == true ? "true" : "false"))) + 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.\"}"); 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 (!mympd_state_set("jukeboxMode", p_char)) + if (!mpd_client_state_set("jukeboxMode", p_char)) n = 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 (!mympd_state_set("jukeboxPlaylist", mympd_state.jukeboxPlaylist)) + if (!mpd_client_state_set("jukeboxPlaylist", mympd_state.jukeboxPlaylist)) n = 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 (!mympd_state_set("jukeboxQueueLength", p_char)) + if (!mpd_client_state_set("jukeboxQueueLength", p_char)) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); } @@ -250,13 +247,10 @@ void mympd_api(struct work_request_t *request) { free(p_charbuf1); } if (mympd_state.jukeboxMode > 0) - mympd_jukebox(); + mpd_client_jukebox(); if (n == 0) n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; - case MPD_API_WELCOME: - n = mympd_put_welcome(mpd.buf); - break; case MPD_API_DATABASE_UPDATE: uint_rc = mpd_run_update(mpd.conn, NULL); if (uint_rc > 0) @@ -268,7 +262,7 @@ void mympd_api(struct work_request_t *request) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_SMARTPLS_UPDATE_ALL: - uint_rc = mympd_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\"}"); else @@ -281,7 +275,7 @@ void mympd_api(struct work_request_t *request) { 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 = mympd_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); + n = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); free(p_charbuf2); free(p_charbuf3); } @@ -289,14 +283,14 @@ void mympd_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 = mympd_smartpls_save(p_charbuf1, p_charbuf2, NULL, NULL, 0, int_buf1); + n = 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 = mympd_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); + n = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); free(p_charbuf2); free(p_charbuf3); free(p_charbuf4); @@ -312,7 +306,7 @@ void mympd_api(struct work_request_t *request) { case MPD_API_SMARTPLS_GET: je = json_scanf(request->data, request->length, "{data: {playlist: %Q}}", &p_charbuf1); if (je == 1) { - n = mympd_smartpls_put(mpd.buf, p_charbuf1); + n = mpd_client_smartpls_put(mpd.buf, p_charbuf1); free(p_charbuf1); } break; @@ -334,7 +328,7 @@ void mympd_api(struct work_request_t *request) { break; case MPD_API_PLAYER_NEXT: if (config.stickers) - mympd_count_song_id(mpd.song_id, "skipCount", 1); + 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\"}"); else { @@ -367,7 +361,7 @@ void mympd_api(struct work_request_t *request) { } break; case MPD_API_QUEUE_CROP: - n = mympd_queue_crop(mpd.buf); + n = 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); @@ -436,7 +430,7 @@ void mympd_api(struct work_request_t *request) { } break; case MPD_API_PLAYER_OUTPUT_LIST: - n = mympd_put_outputs(mpd.buf); + n = 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); @@ -471,7 +465,7 @@ void mympd_api(struct work_request_t *request) { } break; case MPD_API_PLAYER_VOLUME_GET: - n = mympd_put_volume(mpd.buf); + n = 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); @@ -487,29 +481,29 @@ void mympd_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 = mympd_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length); + n = 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 = mympd_put_last_played_songs(mpd.buf, uint_buf1); + n = mpd_client_put_last_played_songs(mpd.buf, uint_buf1); } break; case MPD_API_PLAYER_CURRENT_SONG: - n = mympd_put_current_song(mpd.buf); + n = 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 = mympd_put_songdetails(mpd.buf, p_charbuf1); + n = 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 = mympd_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1); + n = mpd_client_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -517,7 +511,7 @@ void mympd_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 = mympd_put_db_tag(mpd.buf, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); + n = 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); @@ -526,7 +520,7 @@ void mympd_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 = mympd_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3); + n = mpd_client_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -573,14 +567,14 @@ void mympd_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 = mympd_put_playlists(mpd.buf, uint_buf1, p_charbuf1); + n = 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 = mympd_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + n = mpd_client_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -625,7 +619,7 @@ void mympd_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 = mympd_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); + n = mpd_client_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -741,7 +735,7 @@ void mympd_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 = mympd_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + n = mpd_client_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -749,7 +743,7 @@ void mympd_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 = mympd_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); + n = mpd_client_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); } @@ -758,7 +752,7 @@ void mympd_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 = mympd_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); + n = 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); @@ -798,10 +792,10 @@ void mympd_api(struct work_request_t *request) { } break; case MPD_API_SETTINGS_GET: - n = mympd_put_settings(mpd.buf); + n = mpd_client_put_settings(mpd.buf); break; case MPD_API_DATABASE_STATS: - n = mympd_put_stats(mpd.buf); + n = mpd_client_put_stats(mpd.buf); break; } @@ -839,7 +833,7 @@ void mympd_api(struct work_request_t *request) { free(request); } -void mympd_notify(size_t len) { +void mpd_client_notify(size_t len) { LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s.\n", mpd.buf); struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t)); @@ -848,7 +842,7 @@ void mympd_notify(size_t len) { tiny_queue_push(web_server_queue, response); } -void mympd_parse_idle(int idle_bitmask) { +void mpd_client_parse_idle(int idle_bitmask) { size_t n = 0; for (unsigned j = 0;; j++) { enum mpd_idle idle_event = 1 << j; @@ -860,7 +854,7 @@ void mympd_parse_idle(int idle_bitmask) { switch(idle_event) { case MPD_IDLE_DATABASE: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); - mympd_smartpls_update_all(); + mpd_client_smartpls_update_all(); break; case MPD_IDLE_STORED_PLAYLIST: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); @@ -868,22 +862,22 @@ void mympd_parse_idle(int idle_bitmask) { case MPD_IDLE_QUEUE: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); if (mympd_state.jukeboxMode > 0) - mympd_jukebox(); + mpd_client_jukebox(); break; case MPD_IDLE_PLAYER: - n = mympd_put_state(mpd.buf, &mpd.song_id, &mpd.next_song_id, &mpd.last_song_id, &mpd.queue_version, &mpd.queue_length); + n = 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) - mympd_last_played_list(mpd.song_id); + mpd_client_last_played_list(mpd.song_id); if (config.stickers && mpd.last_update_sticker_song_id != mpd.song_id) { - mympd_count_song_id(mpd.song_id, "playCount", 1); - mympd_last_played_song_id(mpd.song_id); + mpd_client_count_song_id(mpd.song_id, "playCount", 1); + mpd_client_last_played_song_id(mpd.song_id); mpd.last_update_sticker_song_id = mpd.song_id; } } break; case MPD_IDLE_MIXER: - n = mympd_put_volume(mpd.buf); + n = mpd_client_put_volume(mpd.buf); break; case MPD_IDLE_OUTPUT: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}"); @@ -892,7 +886,7 @@ void mympd_parse_idle(int idle_bitmask) { n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}"); break; case MPD_IDLE_UPDATE: - n = mympd_get_updatedb_state(mpd.buf); + n = mpd_client_get_updatedb_state(mpd.buf); break; case MPD_IDLE_STICKER: n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}"); @@ -907,19 +901,19 @@ void mympd_parse_idle(int idle_bitmask) { n = 0; } if (n > 0) { - mympd_notify(n); + mpd_client_notify(n); } } } } -void mympd_mpd_features() { +void mpd_client_mpd_features() { struct mpd_pair *pair; char s[2] = ","; char *taglist = strdup(config.taglist); char *searchtaglist = strdup(config.searchtaglist); char *browsetaglist = strdup(config.browsetaglist); - char *token; + char *token, *rest; mpd.protocol = mpd_connection_get_server_version(mpd.conn); @@ -980,14 +974,14 @@ void mympd_mpd_features() { LOG_INFO() printf("\nmyMPD enabled tags: "); enum mpd_tag_type types[64]; unsigned n = 0; - token = strtok(taglist, s); + token = strtok_r(taglist, s, &rest); while (token != NULL) { if (list_get_value(&mpd_tags, token) == 1) { list_push(&mympd_tags, token, 1); types[n++] = mpd_tag_name_parse(token); LOG_INFO() printf("%s ", token); } - token = strtok(NULL, s); + token = strtok_r(NULL, s, &rest); } LOG_INFO() printf("\n"); #if LIBMPDCLIENT_CHECK_VERSION(2,12,0) @@ -1005,22 +999,22 @@ void mympd_mpd_features() { } #endif LOG_INFO() printf("myMPD enabled searchtags: "); - token = strtok(searchtaglist, s); + token = strtok_r(searchtaglist, s, &rest); while (token != NULL) { if (list_get_value(&mympd_tags, token) == 1) { list_push(&mympd_searchtags, token, 1); LOG_INFO() printf("%s ", token); } - token = strtok(NULL, s); + token = strtok_r(NULL, s, &rest); } LOG_INFO() printf("\nmyMPD enabled browsetags: "); - token = strtok(browsetaglist, s); + token = strtok_r(browsetaglist, s, &rest); while (token != NULL) { if (list_get_value(&mympd_tags, token) == 1) { list_push(&mympd_browsetags, token, 1); LOG_INFO() printf("%s ", token); } - token = strtok(NULL, s); + token = strtok_r(NULL, s, &rest); } LOG_INFO() printf("\n"); } @@ -1036,7 +1030,7 @@ void mympd_mpd_features() { LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0.\n"); } -void mympd_idle(int timeout) { +void mpd_client_idle(int timeout) { struct pollfd fds[1]; int pollrc; size_t n = 0; @@ -1049,7 +1043,7 @@ void mympd_idle(int timeout) { if (mpd.conn == NULL) { printf("MPD connection failed."); n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mympd_notify(n); + mpd_client_notify(n); mpd.conn_state = MPD_FAILURE; return; } @@ -1057,7 +1051,7 @@ void mympd_idle(int timeout) { 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)); - mympd_notify(n); + mpd_client_notify(n); mpd.conn_state = MPD_FAILURE; return; } @@ -1065,7 +1059,7 @@ void mympd_idle(int timeout) { 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)); - mympd_notify(n); + mpd_client_notify(n); mpd.conn_state = MPD_FAILURE; return; } @@ -1073,17 +1067,17 @@ void mympd_idle(int timeout) { LOG_INFO() printf("MPD connected.\n"); mpd_connection_set_timeout(mpd.conn, mpd.timeout); mpd.conn_state = MPD_CONNECTED; - mympd_mpd_features(); - mympd_smartpls_update_all(); + mpd_client_mpd_features(); + mpd_client_smartpls_update_all(); if (mympd_state.jukeboxMode > 0) - mympd_jukebox(); + mpd_client_jukebox(); mpd_send_idle(mpd.conn); break; case MPD_FAILURE: printf("MPD connection failed.\n"); n = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mympd_notify(n); + mpd_client_notify(n); case MPD_DISCONNECT: case MPD_RECONNECT: @@ -1105,7 +1099,7 @@ void mympd_idle(int timeout) { //Handle idle events LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events.\n"); enum mpd_idle idle_bitmask = mpd_recv_idle(mpd.conn, false); - mympd_parse_idle(idle_bitmask); + mpd_client_parse_idle(idle_bitmask); } else { mpd_response_finish(mpd.conn); @@ -1114,7 +1108,7 @@ void mympd_idle(int timeout) { //Handle request LOG_DEBUG() fprintf(stderr, "DEBUG: Handle request.\n"); struct work_request_t *req = tiny_queue_shift(mpd_client_queue); - mympd_api(req); + mpd_client_api(req); } LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode.\n"); mpd_send_idle(mpd.conn); @@ -1123,7 +1117,7 @@ void mympd_idle(int timeout) { } } -int mympd_get_updatedb_state(char *buffer) { +int mpd_client_get_updatedb_state(char *buffer) { struct mpd_status *status; int len, update_id; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); @@ -1141,11 +1135,10 @@ int mympd_get_updatedb_state(char *buffer) { mpd_status_free(status); CHECK_RETURN_LEN(); - return len; } -bool mympd_get_sticker(const char *uri, t_sticker *sticker) { +bool mpd_client_get_sticker(const char *uri, t_sticker *sticker) { struct mpd_pair *pair; char *crap; sticker->playCount = 0; @@ -1175,12 +1168,12 @@ bool mympd_get_sticker(const char *uri, t_sticker *sticker) { return true; } -bool mympd_count_song_id(int song_id, char *name, int value) { +bool mpd_client_count_song_id(int song_id, char *name, int value) { struct mpd_song *song; if (song_id > -1) { song = mpd_run_get_queue_song_id(mpd.conn, song_id); if (song) { - if (!mympd_count_song_uri(mpd_song_get_uri(song), name, value)) { + if (!mpd_client_count_song_uri(mpd_song_get_uri(song), name, value)) { mpd_song_free(song); return false; } @@ -1196,7 +1189,7 @@ bool mympd_count_song_id(int song_id, char *name, int value) { return true; } -bool mympd_count_song_uri(const char *uri, char *name, int value) { +bool mpd_client_count_song_uri(const char *uri, char *name, int value) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; struct mpd_pair *pair; @@ -1227,7 +1220,7 @@ bool mympd_count_song_uri(const char *uri, char *name, int value) { return true; } -bool mympd_like_song_uri(const char *uri, int value) { +bool mpd_client_like_song_uri(const char *uri, int value) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; char v[2]; @@ -1244,7 +1237,7 @@ bool mympd_like_song_uri(const char *uri, int value) { return true; } -bool mympd_last_played_list(int song_id) { +bool mpd_client_last_played_list(int song_id) { struct mpd_song *song; char tmp_file[400]; char cfg_file[400]; @@ -1283,13 +1276,13 @@ bool mympd_last_played_list(int song_id) { return true; } -bool mympd_last_played_song_id(int song_id) { +bool mpd_client_last_played_song_id(int song_id) { struct mpd_song *song; if (song_id > -1) { song = mpd_run_get_queue_song_id(mpd.conn, song_id); if (song) { - if (!mympd_last_played_song_uri(mpd_song_get_uri(song))) { + if (!mpd_client_last_played_song_uri(mpd_song_get_uri(song))) { mpd_song_free(song); return false; } @@ -1309,7 +1302,7 @@ bool mympd_last_played_song_id(int song_id) { return true; } -bool mympd_last_played_song_uri(const char *uri) { +bool mpd_client_last_played_song_uri(const char *uri) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; char v[20]; @@ -1323,7 +1316,7 @@ bool mympd_last_played_song_uri(const char *uri) { } -char *mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { +char *mpd_client_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { char *str; str = (char *)mpd_song_get_tag(song, tag, 0); if (str == NULL) { @@ -1337,7 +1330,7 @@ char *mympd_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { return str; } -bool mympd_jukebox() { +bool mpd_client_jukebox() { struct mpd_status *status; status = mpd_run_status(mpd.conn); int queue_length, addSongs, i; @@ -1484,7 +1477,7 @@ bool mympd_jukebox() { mpd_run_play(mpd.conn); else { printf("Error adding song(s), trying again...\n"); - mympd_jukebox(); + mpd_client_jukebox(); } return true; } @@ -1493,7 +1486,7 @@ int randrange(int n) { return rand() / (RAND_MAX / (n + 1) + 1); } -int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length) { +int mpd_client_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length) { struct mpd_status *status; const struct mpd_audio_format *audioformat; int len; @@ -1539,10 +1532,9 @@ int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int * mpd_status_free(status); CHECK_RETURN_LEN(); - return len; } -int mympd_put_volume(char *buffer) { +int mpd_client_put_volume(char *buffer) { struct mpd_status *status; int len; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); @@ -1559,10 +1551,9 @@ int mympd_put_volume(char *buffer) { mpd_status_free(status); CHECK_RETURN_LEN(); - return len; } -int mympd_put_welcome(char *buffer) { +int mpd_client_put_welcome(char *buffer) { int len; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); const unsigned *version = mpd_connection_get_server_version(mpd.conn); @@ -1579,10 +1570,9 @@ int mympd_put_welcome(char *buffer) { ); CHECK_RETURN_LEN(); - return len; } -bool mympd_state_get(char *name, char *value) { +bool mpd_client_state_get(char *name, char *value) { char cfg_file[400]; char *line; size_t n = 0; @@ -1605,7 +1595,7 @@ bool mympd_state_get(char *name, char *value) { return false; } -bool mympd_state_set(const char *name, const char *value) { +bool mpd_client_state_set(const char *name, const char *value) { char tmp_file[400]; char cfg_file[400]; @@ -1627,10 +1617,11 @@ bool mympd_state_set(const char *name, const char *value) { return true; } -int mympd_syscmd(char *buffer, char *cmd, int order) { +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; @@ -1645,7 +1636,7 @@ int mympd_syscmd(char *buffer, char *cmd, int order) { read = getline(&line, &n, fp); fclose(fp); if (read > 0) { - strtok(line, "\n"); + 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); @@ -1659,10 +1650,9 @@ int mympd_syscmd(char *buffer, char *cmd, int order) { printf("Can't execute syscmd \"%s\"\n", cmd); } CHECK_RETURN_LEN(); - return len; } -int mympd_put_settings(char *buffer) { +int mpd_client_put_settings(char *buffer) { struct mpd_status *status; char *replaygain = strdup(""); int len; @@ -1777,11 +1767,10 @@ int mympd_put_settings(char *buffer) { ); CHECK_RETURN_LEN(); - return len; } -int mympd_put_outputs(char *buffer) { +int mpd_client_put_outputs(char *buffer) { struct mpd_output *output; int len; int nr; @@ -1808,7 +1797,6 @@ int mympd_put_outputs(char *buffer) { len += json_printf(&out,"]}}"); CHECK_RETURN_LEN(); - return len; } int replacechar(char *str, char orig, char rep) { @@ -1821,7 +1809,7 @@ int replacechar(char *str, char orig, char rep) { return n; } -int mympd_get_cover(const char *uri, char *cover, int cover_len) { +int mpd_client_get_cover(const char *uri, char *cover, int cover_len) { char *orgpath = strdup(uri); char *path = orgpath; int len; @@ -1860,7 +1848,7 @@ int mympd_get_cover(const char *uri, char *cover, int cover_len) { return len; } -int mympd_put_current_song(char *buffer) { +int mpd_client_put_current_song(char *buffer) { struct mpd_song *song; int len; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); @@ -1872,7 +1860,7 @@ int mympd_put_current_song(char *buffer) { return len; } - mympd_get_cover(mpd_song_get_uri(song), cover, 500); + mpd_client_get_cover(mpd_song_get_uri(song), cover, 500); len = json_printf(&out, "{type: song_change, data: {pos: %d, currentSongId: %d, cover: %Q, ", mpd_song_get_pos(song), @@ -1888,7 +1876,7 @@ int mympd_put_current_song(char *buffer) { if (config.stickers) { t_sticker *sticker = (t_sticker *) malloc(sizeof(t_sticker)); - mympd_get_sticker(mpd_song_get_uri(song), sticker); + mpd_client_get_sticker(mpd_song_get_uri(song), sticker); len += json_printf(&out, ", playCount: %d, skipCount: %d, like: %d, lastPlayed: %d", sticker->playCount, sticker->skipCount, @@ -1901,10 +1889,9 @@ int mympd_put_current_song(char *buffer) { mpd_song_free(song); CHECK_RETURN_LEN(); - return len; } -int mympd_put_songdetails(char *buffer, char *uri) { +int mpd_client_put_songdetails(char *buffer, char *uri) { struct mpd_entity *entity; const struct mpd_song *song; int len; @@ -1916,7 +1903,7 @@ int mympd_put_songdetails(char *buffer, char *uri) { RETURN_ERROR_AND_RECOVER("mpd_send_list_all_meta"); if ((entity = mpd_recv_entity(mpd.conn)) != NULL) { song = mpd_entity_get_song(entity); - mympd_get_cover(uri, cover, 500); + mpd_client_get_cover(uri, cover, 500); len += json_printf(&out, "cover: %Q, ", cover); if (mpd.feat_tags == true) PUT_SONG_TAGS(); @@ -1928,7 +1915,7 @@ int mympd_put_songdetails(char *buffer, char *uri) { if (config.stickers) { t_sticker *sticker = (t_sticker *) malloc(sizeof(t_sticker)); - mympd_get_sticker(uri, sticker); + mpd_client_get_sticker(uri, sticker); len += json_printf(&out, ", playCount: %d, skipCount: %d, like: %d, lastPlayed: %d", sticker->playCount, sticker->skipCount, @@ -1940,10 +1927,9 @@ int mympd_put_songdetails(char *buffer, char *uri) { len += json_printf(&out, "}}"); CHECK_RETURN_LEN(); - return len; } -int mympd_put_last_played_songs(char *buffer, unsigned int offset) { +int mpd_client_put_last_played_songs(char *buffer, unsigned int offset) { const struct mpd_song *song; struct mpd_entity *entity; int len; @@ -1983,12 +1969,9 @@ int mympd_put_last_played_songs(char *buffer, unsigned int offset) { ); CHECK_RETURN_LEN(); - return len; } - - -int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length) { +int mpd_client_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length) { struct mpd_entity *entity; unsigned long totalTime = 0; unsigned long entity_count = 0; @@ -2041,10 +2024,9 @@ int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, mpd_status_free(status); CHECK_RETURN_LEN(); - return len; } -int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter) { +int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *filter) { struct mpd_entity *entity; const struct mpd_playlist *pl; unsigned int entity_count = 0; @@ -2071,7 +2053,7 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter break; case MPD_ENTITY_TYPE_SONG: song = mpd_entity_get_song(entity); - entityName = mympd_get_tag(song, MPD_TAG_TITLE); + entityName = mpd_client_get_tag(song, MPD_TAG_TITLE); if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, entityName, 1) == 0 || (strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 ) ) { @@ -2161,10 +2143,9 @@ int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter ); CHECK_RETURN_LEN(); - return len; } -int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter) { +int mpd_client_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter) { struct mpd_pair *pair; unsigned long entity_count = 0; unsigned long entities_returned = 0; @@ -2217,10 +2198,9 @@ int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char * ); CHECK_RETURN_LEN(); - return len; } -int mympd_put_songs_in_album(char *buffer, char *album, char *search, char *tag) { +int mpd_client_put_songs_in_album(char *buffer, char *album, char *search, char *tag) { struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; @@ -2249,8 +2229,8 @@ int mympd_put_songs_in_album(char *buffer, char *album, char *search, char *tag) if (entities_returned++) len += json_printf(&out, ", "); else { - mympd_get_cover(mpd_song_get_uri(song), cover, 500); - albumartist = strdup(mympd_get_tag(song, MPD_TAG_ALBUM_ARTIST)); + mpd_client_get_cover(mpd_song_get_uri(song), cover, 500); + albumartist = strdup(mpd_client_get_tag(song, MPD_TAG_ALBUM_ARTIST)); } len += json_printf(&out, "{Type: song, "); PUT_SONG_TAGS(); @@ -2273,10 +2253,9 @@ int mympd_put_songs_in_album(char *buffer, char *album, char *search, char *tag) free(albumartist); CHECK_RETURN_LEN(); - return len; } -int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { +int mpd_client_put_playlists(char *buffer, unsigned int offset, char *filter) { struct mpd_playlist *pl; unsigned int entity_count = 0; unsigned int entities_returned = 0; @@ -2326,10 +2305,9 @@ int mympd_put_playlists(char *buffer, unsigned int offset, char *filter) { ); CHECK_RETURN_LEN(); - return len; } -int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter) { +int mpd_client_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter) { struct mpd_entity *entity; unsigned int entity_count = 0; unsigned int entities_returned = 0; @@ -2349,7 +2327,7 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char * entity_count++; if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { song = mpd_entity_get_song(entity); - entityName = mympd_get_tag(song, MPD_TAG_TITLE); + entityName = mpd_client_get_tag(song, MPD_TAG_TITLE); if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, entityName, 1) == 0 || (strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 ) ) { @@ -2384,10 +2362,9 @@ int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char * ); CHECK_RETURN_LEN(); - return len; } -int mympd_search(char *buffer, char *searchstr, char *filter, char *plist, unsigned int offset) { +int mpd_client_search(char *buffer, char *searchstr, char *filter, char *plist, unsigned int offset) { struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; @@ -2439,11 +2416,10 @@ int mympd_search(char *buffer, char *searchstr, char *filter, char *plist, unsig len = json_printf(&out, "{type: result, data: ok}"); CHECK_RETURN_LEN(); - return len; } -int mympd_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, char *grouptag, char *plist, unsigned int offset) { +int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, char *grouptag, char *plist, unsigned int offset) { int len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); #if LIBMPDCLIENT_CHECK_VERSION(2, 17, 0) @@ -2518,10 +2494,9 @@ int mympd_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, len = json_printf(&out, "{type: error, data: %Q}", "Advanced search is disabled."); #endif CHECK_RETURN_LEN(); - return len; } -int mympd_queue_crop(char *buffer) { +int mpd_client_queue_crop(char *buffer) { int len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); struct mpd_status *status = mpd_run_status(mpd.conn); @@ -2548,10 +2523,9 @@ int mympd_queue_crop(char *buffer) { mpd_status_free(status); CHECK_RETURN_LEN(); - return len; } -int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { +int mpd_client_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; @@ -2603,10 +2577,9 @@ int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char } CHECK_RETURN_LEN(); - return len; } -int mympd_put_stats(char *buffer) { +int mpd_client_put_stats(char *buffer) { struct mpd_stats *stats = mpd_run_stats(mpd.conn); const unsigned *version = mpd_connection_get_server_version(mpd.conn); int len; @@ -2617,7 +2590,7 @@ int mympd_put_stats(char *buffer) { snprintf(libmpdclient_version, 20, "%i.%i.%i", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); if (stats == NULL) - RETURN_ERROR_AND_RECOVER("mympd_put_stats"); + RETURN_ERROR_AND_RECOVER("mpd_run_stats"); len = json_printf(&out, "{type: mpdstats, data: {artists: %d, albums: %d, songs: %d, " "playtime: %d, uptime: %d, dbUpdated: %d, dbPlaytime: %d, mympdVersion: %Q, mpdVersion: %Q, " "libmpdclientVersion: %Q}}", @@ -2635,15 +2608,14 @@ int mympd_put_stats(char *buffer) { mpd_stats_free(stats); CHECK_RETURN_LEN(); - return len; } -void mympd_disconnect() { +void mpd_client_disconnect() { mpd.conn_state = MPD_DISCONNECT; - mympd_idle(100); + mpd_client_idle(100); } -int mympd_smartpls_put(char *buffer, char *playlist) { +int mpd_client_smartpls_put(char *buffer, char *playlist) { char pl_file[400]; char *smartpltype; char *p_charbuf1, *p_charbuf2; @@ -2713,7 +2685,7 @@ int mympd_smartpls_put(char *buffer, char *playlist) { return len; } -int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange) { +int mpd_client_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange) { char tmp_file[400]; char pl_file[400]; sanitize_string(playlist); @@ -2728,7 +2700,7 @@ int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *sear printf("Error renaming file from %s to %s\n", tmp_file, pl_file); return 1; } - else if (mympd_smartpls_update_sticker(playlist, tag, maxentries) == 1) { + else if (mpd_client_smartpls_update_sticker(playlist, tag, maxentries) == 1) { printf("Update of smart playlist %s failed.\n", playlist); return 1; } @@ -2742,7 +2714,7 @@ int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *sear printf("Error renaming file from %s to %s\n", tmp_file, pl_file); return 1; } - else if (mympd_smartpls_update_newest(playlist, timerange) == 1) { + else if (mpd_client_smartpls_update_newest(playlist, timerange) == 1) { printf("Update of smart playlist %s failed.\n", playlist); return 1; } @@ -2756,7 +2728,7 @@ int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *sear printf("Error renaming file from %s to %s\n", tmp_file, pl_file); return 1; } - else if (mympd_smartpls_update_search(playlist, tag, searchstr) == 1) { + else if (mpd_client_smartpls_update_search(playlist, tag, searchstr) == 1) { printf("Update of smart playlist %s failed.\n", playlist); return 1; } @@ -2764,7 +2736,7 @@ int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *sear return 0; } -int mympd_smartpls_update_all() { +int mpd_client_smartpls_update_all() { DIR *dir; struct dirent *ent; char *smartpltype; @@ -2794,7 +2766,7 @@ int mympd_smartpls_update_all() { if (strcmp(smartpltype, "sticker") == 0) { je = json_scanf(content, strlen(content), "{sticker: %Q, maxentries: %d}", &p_charbuf1, &int_buf1); if (je == 2) { - mympd_smartpls_update_sticker(ent->d_name, p_charbuf1, int_buf1); + mpd_client_smartpls_update_sticker(ent->d_name, p_charbuf1, int_buf1); free(p_charbuf1); } else @@ -2803,7 +2775,7 @@ int mympd_smartpls_update_all() { else if (strcmp(smartpltype, "newest") == 0) { je = json_scanf(content, strlen(content), "{timerange: %d}", &int_buf1); if (je == 1) { - mympd_smartpls_update_newest(ent->d_name, int_buf1); + mpd_client_smartpls_update_newest(ent->d_name, int_buf1); } else printf("Can't parse smart playlist file %s\n", filename); @@ -2811,7 +2783,7 @@ int mympd_smartpls_update_all() { else if (strcmp(smartpltype, "search") == 0) { je = json_scanf(content, strlen(content), "{tag: %Q, searchstr: %Q}", &p_charbuf1, &p_charbuf2); if (je == 2) { - mympd_smartpls_update_search(ent->d_name, p_charbuf1, p_charbuf2); + mpd_client_smartpls_update_search(ent->d_name, p_charbuf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -2829,7 +2801,7 @@ int mympd_smartpls_update_all() { return 0; } -int mympd_smartpls_clear(char *playlist) { +int mpd_client_smartpls_clear(char *playlist) { struct mpd_playlist *pl; const char *plpath; bool exists = false; @@ -2856,17 +2828,17 @@ int mympd_smartpls_clear(char *playlist) { return 0; } -int mympd_smartpls_update_search(char *playlist, char *tag, char *searchstr) { - mympd_smartpls_clear(playlist); +int mpd_client_smartpls_update_search(char *playlist, char *tag, char *searchstr) { + mpd_client_smartpls_clear(playlist); if (mpd.feat_advsearch == true && strcmp(tag, "expression") == 0) - mympd_search_adv(mpd.buf, searchstr, NULL, true, NULL, playlist, 0); + mpd_client_search_adv(mpd.buf, searchstr, NULL, true, NULL, playlist, 0); else - mympd_search(mpd.buf, searchstr, tag, playlist, 0); + mpd_client_search(mpd.buf, searchstr, tag, playlist, 0); LOG_INFO() printf("Updated %s\n", playlist); return 0; } -int mympd_smartpls_update_sticker(char *playlist, char *sticker, int maxentries) { +int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxentries) { struct mpd_pair *pair; char *uri = NULL; const char *p_value; @@ -2903,7 +2875,7 @@ int mympd_smartpls_update_sticker(char *playlist, char *sticker, int maxentries) mpd_response_finish(mpd.conn); free(uri); - mympd_smartpls_clear(playlist); + mpd_client_smartpls_clear(playlist); if (value_max > 2) value_max = value_max / 2; @@ -2929,7 +2901,7 @@ int mympd_smartpls_update_sticker(char *playlist, char *sticker, int maxentries) return 0; } -int mympd_smartpls_update_newest(char *playlist, int timerange) { +int mpd_client_smartpls_update_newest(char *playlist, int timerange) { unsigned long value_max = 0; char searchstr[50]; @@ -2943,16 +2915,16 @@ int mympd_smartpls_update_newest(char *playlist, int timerange) { return 1; } - mympd_smartpls_clear(playlist); + mpd_client_smartpls_clear(playlist); value_max -= timerange; if (value_max > 0) { if (mpd.feat_advsearch == true) { snprintf(searchstr, 50, "(modified-since '%lu')", value_max); - mympd_search_adv(mpd.buf, searchstr, NULL, true, NULL, playlist, 0); + mpd_client_search_adv(mpd.buf, searchstr, NULL, true, NULL, playlist, 0); } else { snprintf(searchstr, 20, "%lu", value_max); - mympd_search(mpd.buf, searchstr, "modified-since", playlist, 0); + mpd_client_search(mpd.buf, searchstr, "modified-since", playlist, 0); } LOG_INFO() printf("Updated %s\n", playlist); } diff --git a/src/mpd_client.h b/src/mpd_client.h index 59d13f3..ab2b12e 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.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 myMPD ist fork of: @@ -44,32 +44,25 @@ mpd.conn_state = MPD_FAILURE; \ } while (0) -#define CHECK_RETURN_LEN() do { \ - if (len > MAX_SIZE) \ - printf("Buffer truncated: %d, %d\n", len, MAX_SIZE); \ - return len; \ -} while (0) - #define PUT_SONG_TAGS() do { \ struct node *current = mympd_tags.list; \ int tagnr = 0; \ while (current != NULL) { \ if (tagnr ++) \ len += json_printf(&out, ","); \ - len += json_printf(&out, "%Q: %Q", current->data, mympd_get_tag(song, mpd_tag_name_parse(current->data))); \ + len += json_printf(&out, "%Q: %Q", current->data, mpd_client_get_tag(song, mpd_tag_name_parse(current->data))); \ current = current->next; \ } \ len += json_printf(&out, ", Duration: %d, uri: %Q", mpd_song_get_duration(song), mpd_song_get_uri(song)); \ } while (0) #define PUT_MIN_SONG_TAGS() do { \ - len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", mympd_get_tag(song, MPD_TAG_TITLE), mpd_song_get_duration(song), mpd_song_get_uri(song)); \ + len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", mpd_client_get_tag(song, MPD_TAG_TITLE), mpd_song_get_duration(song), mpd_song_get_uri(song)); \ } while (0) -#define GEN_ENUM(X) X, -#define GEN_STR(X) #X, #define MPD_CMDS(X) \ + X(MPD_API_UNKNOWN) \ X(MPD_API_QUEUE_CLEAR) \ X(MPD_API_QUEUE_CROP) \ X(MPD_API_QUEUE_SAVE) \ @@ -122,11 +115,9 @@ X(MPD_API_PLAYER_STATE) \ X(MPD_API_SETTINGS_GET) \ X(MPD_API_SETTINGS_SET) \ - X(MPD_API_WELCOME) \ X(MPD_API_LIKE) \ - X(MPD_API_SYSCMD) \ X(MPD_API_COLS_SAVE) \ - X(MPD_API_UNKNOWN) + X(MPD_API_SYSCMD) enum mpd_cmd_ids { MPD_CMDS(GEN_ENUM) @@ -201,47 +192,47 @@ t_mympd_state mympd_state; tiny_queue_t *mpd_client_queue; int randrange(int n); -void mympd_idle(int timeout); -void mympd_parse_idle(int idle_bitmask); -void mympd_api(struct work_request_t *request); -void mympd_notify(size_t n); -bool mympd_count_song_id(int song_id, char *name, int value); -bool mympd_count_song_uri(const char *uri, char *name, int value); -bool mympd_like_song_uri(const char *uri, int value); -bool mympd_last_played_song_uri(const char *uri); -bool mympd_last_played_song_id(int song_id); -bool mympd_get_sticker(const char *uri, t_sticker *sticker); -bool mympd_last_played_list(int song_id); -bool mympd_jukebox(); -bool mympd_state_get(char *name, char *value); -bool mympd_state_set(const char *name, const char *value); -int mympd_syscmd(char *buffer, char *cmd, int order); -int mympd_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange); -int mympd_smartpls_put(char *buffer, char *playlist); -int mympd_smartpls_update_all(); -int mympd_smartpls_clear(char *playlist); -int mympd_smartpls_update_sticker(char *playlist, char *sticker, int maxentries); -int mympd_smartpls_update_newest(char *playlist, int timerange); -int mympd_smartpls_update_search(char *playlist, char *tag, char *searchstr); -int mympd_get_updatedb_state(char *buffer); -int mympd_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length); -int mympd_put_outputs(char *buffer); -int mympd_put_current_song(char *buffer); -int mympd_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length); -int mympd_put_browse(char *buffer, char *path, unsigned int offset, char *filter); -int mympd_search(char *buffer, char *searchstr, char *filter, char *plist, unsigned int offset); -int mympd_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, char *grouptag, char *plist, unsigned int offset); -int mympd_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); -int mympd_put_welcome(char *buffer); -int mympd_put_volume(char *buffer); -int mympd_put_stats(char *buffer); -int mympd_put_settings(char *buffer); -int mympd_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter); -int mympd_put_songs_in_album(char *buffer, char *album, char *search, char *tag); -int mympd_put_playlists(char *buffer, unsigned int offset, char *filter); -int mympd_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter); -int mympd_put_songdetails(char *buffer, char *uri); -int mympd_put_last_played_songs(char *buffer, unsigned int offset); -int mympd_queue_crop(char *buffer); -void mympd_disconnect(); +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_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); +bool mpd_client_like_song_uri(const char *uri, int value); +bool mpd_client_last_played_song_uri(const char *uri); +bool mpd_client_last_played_song_id(int song_id); +bool mpd_client_get_sticker(const char *uri, t_sticker *sticker); +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(); +int mpd_client_smartpls_clear(char *playlist); +int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxentries); +int mpd_client_smartpls_update_newest(char *playlist, int timerange); +int mpd_client_smartpls_update_search(char *playlist, char *tag, char *searchstr); +int mpd_client_get_updatedb_state(char *buffer); +int mpd_client_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length); +int mpd_client_put_outputs(char *buffer); +int mpd_client_put_current_song(char *buffer); +int mpd_client_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length); +int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *filter); +int mpd_client_search(char *buffer, char *searchstr, char *filter, char *plist, unsigned int offset); +int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, char *grouptag, char *plist, unsigned int offset); +int mpd_client_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); +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_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); +int mpd_client_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter); +int mpd_client_put_songdetails(char *buffer, char *uri); +int mpd_client_put_last_played_songs(char *buffer, unsigned int offset); +int mpd_client_queue_crop(char *buffer); +void mpd_client_disconnect(); #endif diff --git a/src/web_server.c b/src/web_server.c index 3e3f457..919fae0 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -200,13 +200,14 @@ 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) { char *host; + char *crap; char host_header[1024]; switch(ev) { case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p); - host = strtok(host_header, ":"); + host = strtok_r(host_header, ":", &crap); char s_redirect[250]; if (strcmp(config.sslport, "443") == 0) snprintf(s_redirect, 250, "https://%s/", host); From 8634a52194f1760fc16229cb7cb5117579b143d4 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sun, 6 Jan 2019 09:36:34 +0000 Subject: [PATCH 07/59] Fix: no global state in web_server.c --- src/global.c | 4 ++++ src/global.h.in | 1 + src/list.c | 2 +- src/main.c | 31 +++++++++++++++-------------- src/mpd_client.c | 6 +----- src/mpd_client.h | 1 - src/web_server.c | 52 ++++++++++++++++++++++++++---------------------- src/web_server.h | 6 +++--- 8 files changed, 54 insertions(+), 49 deletions(-) diff --git a/src/global.c b/src/global.c index 943402b..a8e3eef 100644 --- a/src/global.c +++ b/src/global.c @@ -27,6 +27,10 @@ #include #include "global.h" +int randrange(int n) { + return rand() / (RAND_MAX / (n + 1) + 1); +} + void sanitize_string(const char *data) { static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" diff --git a/src/global.h.in b/src/global.h.in index 23e9d3c..6fb0267 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -99,6 +99,7 @@ typedef struct { t_config config; //global functions +int randrange(int n); void sanitize_string(const char *data); int copy_string(char * const dest, char const * const src, size_t const dst_len, size_t const src_len); #endif diff --git a/src/list.c b/src/list.c index ce94a64..9929630 100644 --- a/src/list.c +++ b/src/list.c @@ -81,7 +81,7 @@ int list_shuffle(struct list *l) { if (l->length < 2) return 1; - srand((unsigned int)time(NULL)); +// srand((unsigned int)time(NULL)); struct node *current = l->list; while (current != NULL) { diff --git a/src/main.c b/src/main.c index 32df3a6..24fc055 100644 --- a/src/main.c +++ b/src/main.c @@ -38,6 +38,7 @@ #include "global.h" #include "mpd_client.h" #include "web_server.h" +#include "../dist/src/mongoose/mongoose.h" static void signal_handler(int sig_num) { signal(sig_num, signal_handler); // Reinstantiate signal handler @@ -297,7 +298,7 @@ bool testdir(char *name, char *dirname) { } } -void *mpd_client_thread() { +void *mpd_client_loop() { while (s_signal_received == 0) { mpd_client_idle(100); } @@ -310,7 +311,8 @@ int main(int argc, char **argv) { char testdirname[400]; mpd_client_queue = tiny_queue_create(); web_server_queue = tiny_queue_create(); - //mympd_queue = tiny_queue_create(); + + srand((unsigned int)time(NULL)); //defaults config.mpdhost = "127.0.0.1"; @@ -380,36 +382,36 @@ int main(int argc, char **argv) { setvbuf(stderr, NULL, _IOLBF, 0); //init webserver - if (!web_server_init()) { + struct mg_mgr mgr; + if (!web_server_init(&mgr)) { return EXIT_FAILURE; } - //drop privileges if (config.user != NULL) { LOG_INFO() printf("Droping privileges to %s\n", config.user); struct passwd *pw; if ((pw = getpwnam(config.user)) == NULL) { printf("getpwnam() failed, unknown user\n"); - web_server_free(); + web_server_free(&mgr); return EXIT_FAILURE; } else if (setgroups(0, NULL) != 0) { printf("setgroups() failed\n"); - web_server_free(); + web_server_free(&mgr); return EXIT_FAILURE; } else if (setgid(pw->pw_gid) != 0) { printf("setgid() failed\n"); - web_server_free(); + web_server_free(&mgr); return EXIT_FAILURE; } else if (setuid(pw->pw_uid) != 0) { printf("setuid() failed\n"); - web_server_free(); + web_server_free(&mgr); return EXIT_FAILURE; } } if (getuid() == 0) { printf("myMPD should not be run with root privileges\n"); - web_server_free(); + web_server_free(&mgr); return EXIT_FAILURE; } @@ -456,22 +458,21 @@ 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, web_server; + pthread_t mpd_client_thread, web_server_thread; //mpd connection - pthread_create(&mpd_client, NULL, mpd_client_thread, NULL); + pthread_create(&mpd_client_thread, NULL, mpd_client_loop, NULL); //webserver - pthread_create(&web_server, NULL, web_server_thread, NULL); + pthread_create(&web_server_thread, NULL, web_server_loop, &mgr); //Do nothing... //clean up - pthread_join(mpd_client, NULL); - pthread_join(web_server, NULL); + pthread_join(mpd_client_thread, NULL); + pthread_join(web_server_thread, NULL); list_free(&mpd_tags); list_free(&mympd_tags); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); - //tiny_queue_free(mympd_queue); return EXIT_SUCCESS; } diff --git a/src/mpd_client.c b/src/mpd_client.c index 6d46f5a..d0a5d76 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -1362,7 +1362,7 @@ bool mpd_client_jukebox() { return true; } - srand((unsigned int)time(NULL)); + //srand((unsigned int)time(NULL)); struct list add_list; list_init(&add_list); @@ -1482,10 +1482,6 @@ bool mpd_client_jukebox() { return true; } -int randrange(int n) { - return rand() / (RAND_MAX / (n + 1) + 1); -} - int mpd_client_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length) { struct mpd_status *status; const struct mpd_audio_format *audioformat; diff --git a/src/mpd_client.h b/src/mpd_client.h index ab2b12e..881f585 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -191,7 +191,6 @@ typedef struct { t_mympd_state mympd_state; tiny_queue_t *mpd_client_queue; -int randrange(int n); void mpd_client_idle(int timeout); void mpd_client_parse_idle(int idle_bitmask); void mpd_client_api(struct work_request_t *request); diff --git a/src/web_server.c b/src/web_server.c index 919fae0..a4a62c1 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -28,10 +28,6 @@ #include "../dist/src/mongoose/mongoose.h" //non-api definitions -static unsigned long s_next_id = 1; -struct mg_mgr mgr; -static struct mg_serve_http_opts s_http_server_opts; - 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); @@ -39,19 +35,20 @@ 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); //api functions -bool web_server_init() { +bool web_server_init(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; struct mg_connection *nc; struct mg_connection *nc_http; struct mg_bind_opts bind_opts; const char *err; - mg_mgr_init(&mgr, NULL); + mg_mgr_init(mgr, NULL); if (config.ssl == true) { - nc_http = mg_bind(&mgr, config.webport, ev_handler_redirect); + nc_http = mg_bind(mgr, config.webport, ev_handler_redirect); if (nc_http == NULL) { printf("Error listening on port %s\n", config.webport); - mg_mgr_free(&mgr); + mg_mgr_free(mgr); return false; } mg_set_protocol_http_websocket(nc_http); @@ -61,51 +58,52 @@ bool web_server_init() { bind_opts.ssl_cert = config.sslcert; bind_opts.ssl_key = config.sslkey; bind_opts.error_string = &err; - nc = mg_bind_opt(&mgr, config.sslport, ev_handler, bind_opts); + nc = mg_bind_opt(mgr, config.sslport, ev_handler, bind_opts); if (nc == NULL) { printf("Error listening on port %s: %s\n", config.sslport, err); - mg_mgr_free(&mgr); + mg_mgr_free(mgr); return false; } LOG_INFO() printf("Listening on ssl port %s\n", config.sslport); } else { - nc = mg_bind(&mgr, config.webport, ev_handler); + nc = mg_bind(mgr, config.webport, ev_handler); if (nc == NULL) { printf("Error listening on port %s\n", config.webport); - mg_mgr_free(&mgr); + mg_mgr_free(mgr); return false; } LOG_INFO() printf("Listening on http port %s\n", config.webport); } mg_set_protocol_http_websocket(nc); - s_http_server_opts.document_root = DOC_ROOT; - s_http_server_opts.enable_directory_listing = "no"; - return true; + + return mgr; } -void web_server_free() { - mg_mgr_free(&mgr); +void web_server_free(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; + mg_mgr_free(mgr); } -void *web_server_thread() { +void *web_server_loop(void *arg) { + struct mg_mgr *mgr = (struct mg_mgr *) arg; while (s_signal_received == 0) { - mg_mgr_poll(&mgr, 10); + mg_mgr_poll(mgr, 100); unsigned web_server_queue_length = tiny_queue_length(web_server_queue); if (web_server_queue_length > 0) { struct work_result_t *response = tiny_queue_shift(web_server_queue); if (response->conn_id == 0) { //Websocket notify from mpd idle - send_ws_notify(&mgr, response); + send_ws_notify(mgr, response); } else { //api response - send_api_response(&mgr, response); + send_api_response(mgr, response); } } } - mg_mgr_free(&mgr); + mg_mgr_free(mgr); return NULL; } @@ -148,8 +146,11 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { switch(ev) { case MG_EV_ACCEPT: { - nc->user_data = (void *)++s_next_id; - LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %lu.\n", s_next_id); + struct timespec start; + clock_gettime(CLOCK_MONOTONIC_RAW, &start); + long unsigned conn_id = (start.tv_sec * 1000 + start.tv_nsec / 1000) * 100 + randrange(100); + nc->user_data = (void *)conn_id; + LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %lu.\n", conn_id); break; } case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { @@ -178,6 +179,9 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { tiny_queue_push(mpd_client_queue, request); } else { + static struct mg_serve_http_opts s_http_server_opts; + s_http_server_opts.document_root = DOC_ROOT; + s_http_server_opts.enable_directory_listing = "no"; mg_serve_http(nc, hm, s_http_server_opts); } break; diff --git a/src/web_server.h b/src/web_server.h index 6e859ba..789eea1 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -41,8 +41,8 @@ struct work_result_t { int length; } work_result_t; -void *web_server_thread(); -bool web_server_init(); -void web_server_free(); +void *web_server_loop(void *arg); +bool web_server_init(void *arg); +void web_server_free(void *arg); #endif From dffe9b17d76ac8bd364fa8ee3afce0533c1aa1ce Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 7 Jan 2019 18:54:40 +0000 Subject: [PATCH 08/59] Fix: use mongoose user_data to avoid global states --- src/global.h.in | 8 +-- src/main.c | 5 +- src/mpd_client.h | 3 + src/web_server.c | 159 +++++++++++++++++++++++++---------------------- src/web_server.h | 6 +- 5 files changed, 99 insertions(+), 82 deletions(-) diff --git a/src/global.h.in b/src/global.h.in index 6fb0267..777289b 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -51,6 +51,10 @@ #define LOG_VERBOSE() if (config.loglevel >= 2) #define LOG_DEBUG() if (config.loglevel == 3) +#define LOG_INFO2() if (config->loglevel >= 1) +#define LOG_VERBOSE2() if (config->loglevel >= 2) +#define LOG_DEBUG2() if (config->loglevel == 3) + //check and return buffer size #define CHECK_RETURN_LEN() do { \ if (len > MAX_SIZE) \ @@ -58,10 +62,6 @@ return len; \ } while (0) -//enumeration helpers -#define GEN_ENUM(X) X, -#define GEN_STR(X) #X, - //signal handler sig_atomic_t s_signal_received; diff --git a/src/main.c b/src/main.c index 24fc055..46b176a 100644 --- a/src/main.c +++ b/src/main.c @@ -339,7 +339,8 @@ int main(int argc, char **argv) { config.max_elements_per_page = 100; config.last_played_count = 20; char *etcdir = strdup(argv[1]); - config.etcdir = dirname(etcdir); + config.etcdir = strdup(dirname(etcdir)); + free(etcdir); config.syscmds = false; config.localplayer = true; config.loglevel = 1; @@ -383,7 +384,7 @@ int main(int argc, char **argv) { //init webserver struct mg_mgr mgr; - if (!web_server_init(&mgr)) { + if (!web_server_init(&mgr, &config)) { return EXIT_FAILURE; } //drop privileges diff --git a/src/mpd_client.h b/src/mpd_client.h index 881f585..cf5e01e 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -119,6 +119,9 @@ 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) }; diff --git a/src/web_server.c b/src/web_server.c index a4a62c1..9c4670c 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -22,6 +22,8 @@ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include + #include "global.h" #include "web_server.h" #include "mpd_client.h" @@ -34,50 +36,60 @@ static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_ 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); +typedef struct t_user_data { + void *config; //pointer to mympd config + long conn_id; +} t_user_data; + //api functions -bool web_server_init(void *arg) { - struct mg_mgr *mgr = (struct mg_mgr *) arg; - struct mg_connection *nc; +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; + struct mg_connection *nc_https; struct mg_connection *nc_http; - struct mg_bind_opts bind_opts; - const char *err; + struct mg_bind_opts bind_opts_https; + struct mg_bind_opts bind_opts_http; + const char *err_https; + const char *err_http; + + t_user_data *user_data = (t_user_data*)malloc(sizeof(t_user_data)); + user_data->config = config; + user_data->conn_id = 1; mg_mgr_init(mgr, NULL); + + //bind to webport + memset(&bind_opts_http, 0, sizeof(bind_opts_http)); + bind_opts_http.user_data = (void *)user_data; + bind_opts_http.error_string = &err_http; + if (config->ssl == true) + nc_http = mg_bind_opt(mgr, config->webport, ev_handler_redirect, bind_opts_http); + else + nc_http = mg_bind_opt(mgr, config->webport, ev_handler, bind_opts_http); + if (nc_http == NULL) { + printf("Error listening on port %s\n", config->webport); + mg_mgr_free(mgr); + return false; + } + mg_set_protocol_http_websocket(nc_http); + LOG_INFO2() printf("Listening on http port %s.\n", config->webport); - if (config.ssl == true) { - nc_http = mg_bind(mgr, config.webport, ev_handler_redirect); - if (nc_http == NULL) { - printf("Error listening on port %s\n", config.webport); - mg_mgr_free(mgr); - return false; - } - mg_set_protocol_http_websocket(nc_http); - LOG_INFO() printf("Listening on http port %s (redirect only).\n", config.webport); - - memset(&bind_opts, 0, sizeof(bind_opts)); - bind_opts.ssl_cert = config.sslcert; - bind_opts.ssl_key = config.sslkey; - bind_opts.error_string = &err; - nc = mg_bind_opt(mgr, config.sslport, ev_handler, bind_opts); - if (nc == NULL) { - printf("Error listening on port %s: %s\n", config.sslport, err); + //bind to sslport + if (config->ssl == true) { + memset(&bind_opts_https, 0, sizeof(bind_opts_https)); + bind_opts_https.user_data = (void *)user_data; + bind_opts_https.error_string = &err_https; + bind_opts_https.ssl_cert = config->sslcert; + bind_opts_https.ssl_key = config->sslkey; + nc_https = mg_bind_opt(mgr, config->sslport, ev_handler, bind_opts_https); + if (nc_https == NULL) { + printf("Error listening on port %s: %s\n", config->sslport, err_https); mg_mgr_free(mgr); return false; } - LOG_INFO() printf("Listening on ssl port %s\n", config.sslport); + LOG_INFO2() printf("Listening on ssl port %s\n", config->sslport); + mg_set_protocol_http_websocket(nc_https); } - else { - nc = mg_bind(mgr, config.webport, ev_handler); - if (nc == NULL) { - printf("Error listening on port %s\n", config.webport); - mg_mgr_free(mgr); - return false; - } - LOG_INFO() printf("Listening on http port %s\n", config.webport); - } - - mg_set_protocol_http_websocket(nc); - return mgr; } @@ -113,27 +125,23 @@ static int is_websocket(const struct mg_connection *nc) { } static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { - struct mg_connection *c; - LOG_DEBUG() fprintf(stderr, "DEBUG: Got ws notify, broadcasting\n"); - - for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) { - if (!is_websocket(c)) + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + if (!is_websocket(nc)) continue; - mg_send_websocket_frame(c, WEBSOCKET_OP_TEXT, response->data, response->length); + mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response->data, response->length); } free(response); } static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { - struct mg_connection *c; - LOG_DEBUG() fprintf(stderr, "DEBUG: Got API response for connection %lu.\n", response->conn_id); - - for (c = mg_next(mgr, NULL); c != NULL; c = mg_next(mgr, c)) { - if (c->user_data != NULL) { - if ((unsigned long)c->user_data == response->conn_id) { - LOG_DEBUG() fprintf(stderr, "DEBUG: Sending to connection %lu: %s\n", (unsigned long)c->user_data, response->data); - mg_send_head(c, 200, response->length, "Content-Type: application/json"); - mg_printf(c, "%s", response->data); + struct mg_connection *nc; + for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { + if (nc->user_data != NULL) { + t_user_data *user_data = (t_user_data *) nc->user_data; + if (user_data->conn_id == response->conn_id) { + mg_send_head(nc, 200, response->length, "Content-Type: application/json"); + mg_printf(nc, "%s", response->data); } } } @@ -141,21 +149,28 @@ static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response } static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { - (void) nc; - (void) ev_data; + t_user_data *user_data = (t_user_data *) nc->user_data; + t_config *config = (t_config *) user_data->config; switch(ev) { case MG_EV_ACCEPT: { - struct timespec start; - clock_gettime(CLOCK_MONOTONIC_RAW, &start); - long unsigned conn_id = (start.tv_sec * 1000 + start.tv_nsec / 1000) * 100 + randrange(100); - nc->user_data = (void *)conn_id; - LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %lu.\n", conn_id); + //increment conn_id + if (user_data->conn_id < LONG_MAX) + user_data->conn_id++; + else + user_data->conn_id = 1; + + //remove mgr user_data and set 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; + nc->user_data = nc_user_data; + LOG_DEBUG2() fprintf(stderr, "DEBUG: New connection id %ld.\n", user_data->conn_id); break; } case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; - LOG_VERBOSE() printf("New websocket request: %.*s\n", hm->uri.len, hm->uri.p); + LOG_VERBOSE2() printf("New websocket request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); if (mg_vcmp(&hm->uri, "/ws") != 0) { printf("ERROR: Websocket request not to /ws, closing connection\n"); mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n"); @@ -164,17 +179,17 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { break; } case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { - LOG_VERBOSE() printf("New Websocket connection established.\n"); + LOG_VERBOSE2() printf("New Websocket connection established (%ld).\n", user_data->conn_id); char response[] = "{\"type\": \"welcome\", \"data\": {\"mympdVersion\": \"" MYMPD_VERSION "\"}}"; mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response, strlen(response)); break; } case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; - LOG_VERBOSE() printf("HTTP request: %.*s\n", hm->uri.len, hm->uri.p); + 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 = (unsigned long)nc->user_data; + 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); } @@ -187,13 +202,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { break; } case MG_EV_CLOSE: { - if (nc->user_data) { - LOG_VERBOSE() fprintf(stderr, "HTTP connection %lu closed.\n", (unsigned long)nc->user_data); - nc->user_data = NULL; - } - else { - LOG_VERBOSE() printf("HTTP connection closed.\n"); - } + LOG_VERBOSE2() fprintf(stderr, "HTTP connection %ld closed.\n", user_data->conn_id); + free(nc->user_data); break; } default: { @@ -202,7 +212,7 @@ 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 ev_handler_redirect(struct mg_connection *nc, int ev, void *ev_data) { char *host; char *crap; char host_header[1024]; @@ -210,15 +220,18 @@ static void ev_handler_redirect(struct mg_connection *nc_http, int ev, void *ev_ case MG_EV_HTTP_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; struct mg_str *host_hdr = mg_get_http_header(hm, "Host"); + t_user_data *user_data = (t_user_data *) nc->user_data; + t_config *config = (t_config *) user_data->config; + snprintf(host_header, 1024, "%.*s", host_hdr->len, host_hdr->p); host = strtok_r(host_header, ":", &crap); char s_redirect[250]; - if (strcmp(config.sslport, "443") == 0) + if (strcmp(config->sslport, "443") == 0) snprintf(s_redirect, 250, "https://%s/", host); else - snprintf(s_redirect, 250, "https://%s:%s/", host, config.sslport); - LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect); - mg_http_send_redirect(nc_http, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); + snprintf(s_redirect, 250, "https://%s:%s/", host, config->sslport); + LOG_VERBOSE2() printf("Redirecting to %s\n", s_redirect); + mg_http_send_redirect(nc, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); break; } default: { diff --git a/src/web_server.h b/src/web_server.h index 789eea1..11986f3 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -30,19 +30,19 @@ tiny_queue_t *web_server_queue; struct work_request_t { - unsigned long conn_id; // needed to identify the connection where to send the reply + 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 { - unsigned long conn_id; // needed to identify the connection where to send the reply + 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); -bool web_server_init(void *arg); +bool web_server_init(void *arg_mgr, void *arg_config); void web_server_free(void *arg); #endif From a7d07fbcc9e95e05549405bf4d43b4d633e5cc3b Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 8 Jan 2019 00:09:43 +0000 Subject: [PATCH 09/59] Feat: mympd_api handler for non mpd api requests --- CMakeLists.txt | 1 + htdocs/js/mympd.js | 2 +- src/global.c | 26 +++ src/global.h.in | 90 +++++++++- src/list.h | 4 +- src/main.c | 38 ++-- src/mpd_client.c | 429 +++++++++++++++++++-------------------------- src/mpd_client.h | 77 +------- src/mympd_api.c | 135 ++++++++++++++ src/mympd_api.h | 30 ++++ src/web_server.c | 58 ++++-- src/web_server.h | 20 +-- 12 files changed, 535 insertions(+), 375 deletions(-) create mode 100644 src/mympd_api.c create mode 100644 src/mympd_api.h 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 From 3c214baa164b2df89455d97c37c326baaff32e8f Mon Sep 17 00:00:00 2001 From: jcorporation Date: Fri, 11 Jan 2019 00:38:44 +0000 Subject: [PATCH 10/59] Fix: cleanup mpd_client for thread safety Fix: remove all possible global states --- src/global.c | 40 +- src/global.h.in | 44 +- src/main.c | 210 +----- src/mpd_client.c | 1760 +++++++++++++++++++++++++++------------------- src/mpd_client.h | 140 +--- src/mympd_api.c | 29 +- src/web_server.c | 33 +- src/web_server.h | 2 +- 8 files changed, 1143 insertions(+), 1115 deletions(-) diff --git a/src/global.c b/src/global.c index 151ac03..151ad76 100644 --- a/src/global.c +++ b/src/global.c @@ -27,35 +27,51 @@ #include #include #include +#include #include "tiny_queue.h" +#include "list.h" #include "global.h" +bool testdir(char *name, char *dirname) { + DIR* dir = opendir(dirname); + if (dir) { + closedir(dir); + LOG_INFO() printf("%s: \"%s\"\n", name, dirname); + return true; + } + else { + printf("%s: \"%s\" don't exists\n", name, dirname); + return false; + } +} + int randrange(int n) { return rand() / (RAND_MAX / (n + 1) + 1); } -void sanitize_string(const char *data) { - static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "1234567890_-. "; - char *cp = data; - const char *end = data + strlen(data); - for (cp += strspn(cp, ok_chars); cp != end; cp += strspn(cp, ok_chars)) - *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)) + for (cp += strspn(cp, ok_chars); cp != end; cp += strspn(cp, ok_chars)) { + printf("ERROR: Invalid character in string\n"); return false; + } return true; } +int replacechar(char *str, const char orig, const char rep) { + char *ix = str; + int n = 0; + while ((ix = strchr(ix, orig)) != NULL) { + *ix++ = rep; + n++; + } + return n; +} 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) @@ -66,7 +82,7 @@ int copy_string(char * const dest, char const * const src, size_t const dst_len, return max; } -enum mypd_cmd_ids get_cmd_id(const char *cmd) { +enum mympd_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++) diff --git a/src/global.h.in b/src/global.h.in index 56424cf..d8c810f 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -45,13 +45,9 @@ //central logging definition #cmakedefine DEBUG -#define LOG_INFO() if (config.loglevel >= 1) -#define LOG_VERBOSE() if (config.loglevel >= 2) -#define LOG_DEBUG() if (config.loglevel == 3) - -#define LOG_INFO2() if (config->loglevel >= 1) -#define LOG_VERBOSE2() if (config->loglevel >= 2) -#define LOG_DEBUG2() if (config->loglevel == 3) +#define LOG_INFO() if (loglevel >= 1) +#define LOG_VERBOSE() if (loglevel >= 2) +#define LOG_DEBUG() if (loglevel == 3) //check and return buffer size #define CHECK_RETURN_LEN() do { \ @@ -122,7 +118,14 @@ #define GEN_ENUM(X) X, #define GEN_STR(X) #X, -enum mypd_cmd_ids { +//Global variables + +int loglevel; + +//signal handler +sig_atomic_t s_signal_received; + +enum mympd_cmd_ids { MYMPD_CMDS(GEN_ENUM) }; @@ -131,25 +134,21 @@ tiny_queue_t *web_server_queue; tiny_queue_t *mpd_client_queue; tiny_queue_t *mympd_api_queue; -struct work_request_t { +typedef struct t_work_request { 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; + enum mympd_cmd_ids cmd_id; +} t_work_request; -struct work_result_t { +typedef struct t_work_result { 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; +} t_work_result; //myMPD configuration -typedef struct { +typedef struct t_config { long mpdport; const char *mpdhost; const char *mpdpass; @@ -177,15 +176,14 @@ typedef struct { const char *streamurl; unsigned long last_played_count; long loglevel; - void *syscmd_list; + struct list syscmd_list; } t_config; -t_config config; - //global functions +bool testdir(char *name, char *dirname); 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); +int replacechar(char *str, const char orig, const char rep); +enum mympd_cmd_ids get_cmd_id(const char *cmd); #endif diff --git a/src/main.c b/src/main.c index 03ba8f1..67f87d1 100644 --- a/src/main.c +++ b/src/main.c @@ -39,17 +39,18 @@ #include "tiny_queue.h" #include "global.h" #include "mpd_client.h" +#include "../dist/src/mongoose/mongoose.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) { signal(sig_num, signal_handler); // Reinstantiate signal handler s_signal_received = sig_num; } -static int inihandler(void* user, const char* section, const char* name, const char* value) { +static int inihandler(void *user, const char *section, const char *name, const char* value) { t_config* p_config = (t_config*)user; char *crap; @@ -141,178 +142,32 @@ static int inihandler(void* user, const char* section, const char* name, const c return 1; } -void read_syscmds(void *arg_config) { +void read_syscmds(t_config *config) { DIR *dir; struct dirent *ent; char dirname[400]; char *cmd; long order; - 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); + 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(config->syscmd_list, strdup(cmd), order); + list_push(&config->syscmd_list, strdup(cmd), order); } closedir(dir); } } else { - LOG_INFO2() printf("Syscmds are disabled\n"); + printf("Syscmds are disabled\n"); } } -void read_statefiles() { - char *crap; - char value[400]; - - LOG_INFO() printf("Reading states\n"); - if (mpd_client_state_get("notificationWeb", value)) { - if (strcmp(value, "true") == 0) - mympd_state.notificationWeb = true; - else - mympd_state.notificationWeb = false; - } - else { - mympd_state.notificationWeb = false; - mpd_client_state_set("notificationWeb", "false"); - } - - if (mpd_client_state_get("notificationPage", value)) { - if (strcmp(value, "true") == 0) - mympd_state.notificationPage = true; - else - mympd_state.notificationPage = false; - } - else { - mympd_state.notificationPage = true; - mpd_client_state_set("notificationPage", "true"); - } - - if (mpd_client_state_get("jukeboxMode", value)) - mympd_state.jukeboxMode = strtol(value, &crap, 10); - else { - mympd_state.jukeboxMode = 0; - mpd_client_state_set("jukeboxMode", "0"); - } - - if (mpd_client_state_get("jukeboxPlaylist", value)) - mympd_state.jukeboxPlaylist = strdup(value); - else { - mympd_state.jukeboxPlaylist = strdup("Database"); - mpd_client_state_set("jukeboxPlaylist", "Database"); - } - - if (mpd_client_state_get("jukeboxQueueLength", value)) - mympd_state.jukeboxQueueLength = strtol(value, &crap, 10); - else { - mympd_state.jukeboxQueueLength = 1; - mpd_client_state_set("jukeboxQueueLength", "1"); - } - - if (mpd_client_state_get("colsQueueCurrent", value)) - mympd_state.colsQueueCurrent = strdup(value); - else { - mympd_state.colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set("colsQueueCurrent", mympd_state.colsQueueCurrent); - } - - if (mpd_client_state_get("colsSearch", value)) - mympd_state.colsSearch = strdup(value); - else { - mympd_state.colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set("colsSearch", mympd_state.colsSearch); - } - - if (mpd_client_state_get("colsBrowseDatabase", value)) - mympd_state.colsBrowseDatabase = strdup(value); - else { - mympd_state.colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]"); - mpd_client_state_set("colsBrowseDatabase", mympd_state.colsBrowseDatabase); - } - - if (mpd_client_state_get("colsBrowsePlaylistsDetail", value)) - mympd_state.colsBrowsePlaylistsDetail = strdup(value); - else { - mympd_state.colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set("colsBrowsePlaylistsDetail", mympd_state.colsBrowsePlaylistsDetail); - } - - if (mpd_client_state_get("colsBrowseFilesystem", value)) - mympd_state.colsBrowseFilesystem = strdup(value); - else { - mympd_state.colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set("colsBrowseFilesystem", mympd_state.colsBrowseFilesystem); - } - - if (mpd_client_state_get("colsPlayback", value)) - mympd_state.colsPlayback = strdup(value); - else { - mympd_state.colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); - mpd_client_state_set("colsPlayback", mympd_state.colsPlayback); - } - - if (mpd_client_state_get("colsQueueLastPlayed", value)) - mympd_state.colsQueueLastPlayed = strdup(value); - else { - mympd_state.colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); - mpd_client_state_set("colsQueueLastPlayed", mympd_state.colsQueueLastPlayed); - } -} - -int read_last_played() { - char cfgfile[400]; - char *line; - char *data; - char *crap; - size_t n = 0; - ssize_t read; - long value; - - snprintf(cfgfile, 400, "%s/state/last_played", config.varlibdir); - FILE *fp = fopen(cfgfile, "r"); - if (fp == NULL) { - printf("Error opening %s\n", cfgfile); - return 0; - } - while ((read = getline(&line, &n, fp)) > 0) { - value = strtol(line, &data, 10); - if (strlen(data) > 2) - data = data + 2; - strtok_r(data, "\n", &crap); - list_push(&last_played, data, value); - } - fclose(fp); - return last_played.length;; -} - -bool testdir(char *name, char *dirname) { - DIR* dir = opendir(dirname); - if (dir) { - closedir(dir); - LOG_INFO() printf("%s: \"%s\"\n", name, dirname); - return true; - } - else { - printf("%s: \"%s\" don't exists\n", name, dirname); - return false; - } -} - -void *mpd_client_loop() { - while (s_signal_received == 0) { - mpd_client_idle(100); - } - mpd_client_disconnect(); - return NULL; -} - int main(int argc, char **argv) { s_signal_received = 0; char testdirname[400]; @@ -322,7 +177,8 @@ int main(int argc, char **argv) { srand((unsigned int)time(NULL)); - //defaults + //mympd config defaults + t_config config; config.mpdhost = "127.0.0.1"; config.mpdport = 6600; config.mpdpass = NULL; @@ -353,16 +209,10 @@ int main(int argc, char **argv) { config.localplayer = true; config.loglevel = 1; - mpd.timeout = 3000; - mpd.last_update_sticker_song_id = -1; - mpd.last_song_id = -1; - mpd.last_last_played_id = -1; - mpd.feat_library = false; - if (argc == 2) { - LOG_INFO() printf("Starting myMPD %s\n", MYMPD_VERSION); - LOG_INFO() printf("Libmpdclient %i.%i.%i\n", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); - LOG_INFO() printf("Parsing config file: %s\n", argv[1]); + printf("Starting myMPD %s\n", MYMPD_VERSION); + printf("Libmpdclient %i.%i.%i\n", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); + printf("Parsing config file: %s\n", argv[1]); if (ini_parse(argv[1], inihandler, &config) < 0) { printf("Can't load config file \"%s\"\n", argv[1]); return EXIT_FAILURE; @@ -383,6 +233,7 @@ int main(int argc, char **argv) { #ifdef DEBUG printf("Debug flag enabled, setting loglevel to debug\n"); config.loglevel = 3; + loglevel = config.loglevel; #endif signal(SIGTERM, signal_handler); @@ -398,7 +249,7 @@ int main(int argc, char **argv) { //drop privileges if (config.user != NULL) { - LOG_INFO() printf("Droping privileges to %s\n", config.user); + printf("Droping privileges to %s\n", config.user); struct passwd *pw; if ((pw = getpwnam(config.user)) == NULL) { printf("getpwnam() failed, unknown user\n"); @@ -430,12 +281,8 @@ int main(int argc, char **argv) { return EXIT_FAILURE; snprintf(testdirname, 400, "%s/library", DOC_ROOT); - if (testdir("Link to mpd music_directory", testdirname)) { - LOG_INFO() printf("Enabling featLibrary support\n"); - mpd.feat_library = true; - } - else { - LOG_INFO() printf("Disabling coverimage support\n"); + if (!testdir("Link to mpd music_directory", testdirname)) { + printf("Disabling coverimage support\n"); config.coverimage = false; } @@ -451,41 +298,26 @@ int main(int argc, char **argv) { if (!testdir("State dir", testdirname)) return EXIT_FAILURE; - //read myMPD states under config.varlibdir - read_statefiles(); - //read system command files - config.syscmd_list = malloc(sizeof(struct list)); - list_init(config.syscmd_list); + list_init(&config.syscmd_list); read_syscmds(&config); - list_sort_by_value(config.syscmd_list, true); + list_sort_by_value(&config.syscmd_list, true); - //init lists for tag handling - list_init(&mpd_tags); - list_init(&mympd_tags); - - //read last played songs history file - list_init(&last_played); - LOG_INFO() printf("Reading last played songs: %d\n", read_last_played()); - //Create working threads pthread_t mpd_client_thread, web_server_thread, mympd_api_thread; //mpd connection - pthread_create(&mpd_client_thread, NULL, mpd_client_loop, NULL); + pthread_create(&mpd_client_thread, NULL, mpd_client_loop, &config); //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... - + //Outsourced all work to separate threads, do nothing... //clean up pthread_join(mpd_client_thread, NULL); pthread_join(web_server_thread, NULL); - list_free(&mpd_tags); - list_free(&mympd_tags); - list_free(config.syscmd_list); + list_free(&config.syscmd_list); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); diff --git a/src/mpd_client.c b/src/mpd_client.c index d27b176..8ef6f07 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -39,8 +39,191 @@ #include "mpd_client.h" #include "../dist/src/frozen/frozen.h" -void mpd_client_api(void *arg_request) { - struct work_request_t *request = (struct work_request_t*) arg_request; +//private definitions +#define RETURN_ERROR_AND_RECOVER(X) do { \ + printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd_state->conn)); \ + len = json_printf(&out, "{type: error, data: %Q}", mpd_connection_get_error_message(mpd_state->conn)); \ + if (!mpd_connection_clear_error(mpd_state->conn)) \ + mpd_state->conn_state = MPD_FAILURE; \ + return len; \ +} while (0) + +#define LOG_ERROR_AND_RECOVER(X) do { \ + printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd_state->conn)); \ + if (!mpd_connection_clear_error(mpd_state->conn)) \ + mpd_state->conn_state = MPD_FAILURE; \ +} while (0) + +#define PUT_SONG_TAGS() do { \ + struct node *current = mpd_state->mympd_tags.list; \ + int tagnr = 0; \ + while (current != NULL) { \ + if (tagnr ++) \ + len += json_printf(&out, ","); \ + len += json_printf(&out, "%Q: %Q", current->data, mpd_client_get_tag(song, mpd_tag_name_parse(current->data))); \ + current = current->next; \ + } \ + len += json_printf(&out, ", Duration: %d, uri: %Q", mpd_song_get_duration(song), mpd_song_get_uri(song)); \ +} while (0) + +#define PUT_MIN_SONG_TAGS() do { \ + len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", mpd_client_get_tag(song, MPD_TAG_TITLE), mpd_song_get_duration(song), mpd_song_get_uri(song)); \ +} while (0) + + +enum mpd_conn_states { + MPD_DISCONNECTED, + MPD_FAILURE, + MPD_CONNECTED, + MPD_RECONNECT, + MPD_DISCONNECT +}; + +typedef struct t_mpd_state { + // Connection + struct mpd_connection *conn; + enum mpd_conn_states conn_state; + int timeout; + + // States + int song_id; + int next_song_id; + int last_song_id; + unsigned queue_version; + unsigned queue_length; + int last_update_sticker_song_id; + int last_last_played_id; + + // Features + const unsigned* protocol; + bool feat_sticker; + bool feat_playlists; + bool feat_tags; + bool feat_library; + bool feat_advsearch; + + //mympd states + bool notificationWeb; + bool notificationPage; + int jukeboxMode; + const char *jukeboxPlaylist; + int jukeboxQueueLength; + char *colsQueueCurrent; + char *colsSearch; + char *colsBrowseDatabase; + char *colsBrowsePlaylistsDetail; + char *colsBrowseFilesystem; + char *colsPlayback; + char *colsQueueLastPlayed; + + //taglists + struct list mpd_tags; + struct list mympd_tags; + struct list mympd_searchtags; + struct list mympd_browsetags; + struct list last_played; +} t_mpd_state; + +typedef struct t_sticker { + long playCount; + long skipCount; + long lastPlayed; + long like; +} t_sticker; + +static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state, const int timeout); +static void mpd_client_parse_idle(t_config *config, t_mpd_state *mpd_state, const int idle_bitmask); +static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_request); +static void mpd_client_notify(const char *message, const size_t n); +static bool mpd_client_count_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id, const char *name, const int value); +static bool mpd_client_count_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, const char *name, const int value); +static bool mpd_client_like_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, int value); +static bool mpd_client_last_played_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id); +static bool mpd_client_last_played_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri); +static bool mpd_client_get_sticker(t_config *config, t_mpd_state *mpd_state, const char *uri, t_sticker *sticker); +static bool mpd_client_last_played_list(t_config *config, t_mpd_state *mpd_state, const int song_id); +static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state); +static bool mpd_client_state_get(t_config *config, t_mpd_state *mpd_state, const char *name, char *value); +static bool mpd_client_state_set(t_config *config, t_mpd_state *mpd_state, const char *name, const char *value); +static bool mpd_client_smartpls_save(t_config *config, t_mpd_state *mpd_state, const char *smartpltype, const char *playlist, const char *tag, const char *searchstr, const int maxentries, const int timerange); +static int mpd_client_smartpls_put(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *playlist); +static bool mpd_client_smartpls_update_all(t_config *config, t_mpd_state *mpd_state); +static bool mpd_client_smartpls_clear(t_config *config, t_mpd_state *mpd_state, const char *playlist); +static bool mpd_client_smartpls_update_sticker(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *sticker, const int maxentries); +static bool mpd_client_smartpls_update_newest(t_config *config, t_mpd_state *mpd_state, const char *playlist, const int timerange); +static bool mpd_client_smartpls_update_search(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *tag, const char *searchstr); +static int mpd_client_get_updatedb_state(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_state(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_outputs(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_current_song(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_queue(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset); +static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *path, const unsigned int offset, const char *filter); +static int mpd_client_search(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *searchstr, const char *filter, const char *plist, const unsigned int offset); +static int mpd_client_search_adv(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *expression, const char *sort, const bool sortdesc, const char *grouptag, const char *plist, const unsigned int offset); +static int mpd_client_search_queue(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *mpdtagtype, const unsigned int offset, const char *searchstr); +static int mpd_client_put_volume(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_stats(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_db_tag(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset, const char *mpdtagtype, const char *mpdsearchtagtype, const char *searchstr, const char *filter); +static int mpd_client_put_songs_in_album(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *album, const char *search, const char *tag); +static int mpd_client_put_playlists(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset, const char *filter); +static int mpd_client_put_playlist_list(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *uri, const unsigned int offset, const char *filter); +static int mpd_client_put_songdetails(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *uri); +static int mpd_client_put_last_played_songs(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset); +static int mpd_client_queue_crop(t_config *config, t_mpd_state *mpd_state, char *buffer); +static void mpd_client_disconnect(t_config *config, t_mpd_state *mpd_state); +static void mpd_client_read_statefiles(t_config *config, t_mpd_state *mpd_state); +static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state); + +//public functions +void *mpd_client_loop(void *arg_config) { + t_config *config = (t_config *) arg_config; + //State of mpd connection + t_mpd_state mpd_state; + mpd_state.conn_state = MPD_DISCONNECTED; + mpd_state.timeout = 3000; + mpd_state.last_update_sticker_song_id = -1; + mpd_state.last_song_id = -1; + mpd_state.last_last_played_id = -1; + mpd_state.feat_library = false; + list_init(&mpd_state.mpd_tags); + list_init(&mpd_state.mympd_tags); + list_init(&mpd_state.mympd_searchtags); + list_init(&mpd_state.mympd_browsetags); + + char testdirname[400]; + snprintf(testdirname, 400, "%s/library", DOC_ROOT); + if (testdir("Link to mpd music_directory", testdirname)) { + LOG_INFO() printf("Enabling featLibrary support\n"); + mpd_state.feat_library = true; + } + + //read myMPD states under config.varlibdir + mpd_client_read_statefiles(config, &mpd_state); + + //read last played songs history file + list_init(&mpd_state.last_played); + int len = mpd_client_read_last_played(config, &mpd_state); + LOG_INFO() printf("Reading last played songs: %d\n", len); + + while (s_signal_received == 0) { + mpd_client_idle(config, &mpd_state, 100); + } + //Cleanup + mpd_client_disconnect(config, &mpd_state); + list_free(&mpd_state.mpd_tags); + list_free(&mpd_state.mympd_tags); + list_free(&mpd_state.mympd_searchtags); + list_free(&mpd_state.mympd_browsetags); + list_free(&mpd_state.last_played); + + return NULL; +} + +//private functions +static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_request) { + t_work_request *request = (t_work_request*) arg_request; + char buffer[MAX_SIZE]; size_t len = 0; unsigned int uint_buf1, uint_buf2, uint_rc; int je, int_buf1, int_rc; @@ -58,23 +241,25 @@ void mpd_client_api(void *arg_request) { clock_gettime(CLOCK_MONOTONIC_RAW, &start); #endif switch(request->cmd_id) { + case MYMPD_API_SYSCMD: + //is handled in mympd_api.c case MPD_API_UNKNOWN: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); printf("Unknown API request: %.*s\n", request->length, request->data); break; case MPD_API_LIKE: - if (config.stickers) { + if (config->stickers) { 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); + if (!mpd_client_like_song_uri(config, mpd_state, p_charbuf1, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); else - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } } else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}"); printf("MPD_API_LIKE: MPD stickers are disabled\n"); } break; @@ -88,144 +273,144 @@ void mpd_client_api(void *arg_request) { if (col_len > 1) cols[col_len - 2] = '\0'; if (strcmp(p_charbuf1, "colsQueueCurrent") == 0) { - free(mympd_state.colsQueueCurrent); - mympd_state.colsQueueCurrent = strdup(cols); + free(mpd_state->colsQueueCurrent); + mpd_state->colsQueueCurrent = strdup(cols); } else if (strcmp(p_charbuf1, "colsSearch") == 0) { - free(mympd_state.colsSearch); - mympd_state.colsSearch = strdup(cols); + free(mpd_state->colsSearch); + mpd_state->colsSearch = strdup(cols); } else if (strcmp(p_charbuf1, "colsBrowseDatabase") == 0) { - free(mympd_state.colsBrowseDatabase); - mympd_state.colsBrowseDatabase = strdup(cols); + free(mpd_state->colsBrowseDatabase); + mpd_state->colsBrowseDatabase = strdup(cols); } else if (strcmp(p_charbuf1, "colsBrowsePlaylistsDetail") == 0) { - free(mympd_state.colsBrowsePlaylistsDetail); - mympd_state.colsBrowsePlaylistsDetail = strdup(cols); + free(mpd_state->colsBrowsePlaylistsDetail); + mpd_state->colsBrowsePlaylistsDetail = strdup(cols); } else if (strcmp(p_charbuf1, "colsBrowseFilesystem") == 0) { - free(mympd_state.colsBrowseFilesystem); - mympd_state.colsBrowseFilesystem = strdup(cols); + free(mpd_state->colsBrowseFilesystem); + mpd_state->colsBrowseFilesystem = strdup(cols); } else if (strcmp(p_charbuf1, "colsPlayback") == 0) { - free(mympd_state.colsPlayback); - mympd_state.colsPlayback = strdup(cols); + free(mpd_state->colsPlayback); + mpd_state->colsPlayback = strdup(cols); } else if (strcmp(p_charbuf1, "colsQueueLastPlayed") == 0) { - free(mympd_state.colsQueueLastPlayed); - mympd_state.colsQueueLastPlayed = strdup(cols); + free(mpd_state->colsQueueLastPlayed); + mpd_state->colsQueueLastPlayed = strdup(cols); } else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown table %s\"}", p_charbuf1); + len = snprintf(buffer, 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 (len == 0) { - if (mpd_client_state_set(p_charbuf1, cols)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_client_state_set(config, mpd_state, p_charbuf1, cols)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } free(p_charbuf1); } break; case MPD_API_PLAYER_STATE: - len = 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(config, mpd_state, buffer); break; case MPD_API_SETTINGS_SET: - je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mympd_state.notificationWeb); + je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mpd_state->notificationWeb); if (je == 1) - if (!mpd_client_state_set("notificationWeb", (mympd_state.notificationWeb == true ? "true" : "false"))) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); + if (!mpd_client_state_set(config, mpd_state, "notificationWeb", (mpd_state->notificationWeb == true ? "true" : "false"))) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); - je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mympd_state.notificationPage); + je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mpd_state->notificationPage); if (je == 1) - if (!mpd_client_state_set("notificationPage", (mympd_state.notificationPage == true ? "true" : "false"))) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); + if (!mpd_client_state_set(config, mpd_state, "notificationPage", (mpd_state->notificationPage == true ? "true" : "false"))) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); - je = json_scanf(request->data, request->length, "{data: {jukeboxMode: %d}}", &mympd_state.jukeboxMode); + je = json_scanf(request->data, request->length, "{data: {jukeboxMode: %d}}", &mpd_state->jukeboxMode); if (je == 1) { - snprintf(p_char, 4, "%d", mympd_state.jukeboxMode); - if (!mpd_client_state_set("jukeboxMode", p_char)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); + snprintf(p_char, 4, "%d", mpd_state->jukeboxMode); + if (!mpd_client_state_set(config, mpd_state, "jukeboxMode", p_char)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); } - je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mympd_state.jukeboxPlaylist); + je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mpd_state->jukeboxPlaylist); if (je == 1) - if (!mpd_client_state_set("jukeboxPlaylist", mympd_state.jukeboxPlaylist)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); + if (!mpd_client_state_set(config, mpd_state, "jukeboxPlaylist", mpd_state->jukeboxPlaylist)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); - je = json_scanf(request->data, request->length, "{data: {jukeboxQueueLength: %d}}", &mympd_state.jukeboxQueueLength); + je = json_scanf(request->data, request->length, "{data: {jukeboxQueueLength: %d}}", &mpd_state->jukeboxQueueLength); if (je == 1) { - snprintf(p_char, 4, "%d", mympd_state.jukeboxQueueLength); - if (!mpd_client_state_set("jukeboxQueueLength", p_char)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); + snprintf(p_char, 4, "%d", mpd_state->jukeboxQueueLength); + if (!mpd_client_state_set(config, mpd_state, "jukeboxQueueLength", p_char)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state random.\"}"); + if (!mpd_run_random(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state repeat.\"}"); + if (!mpd_run_repeat(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state consume.\"}"); + if (!mpd_run_consume(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state single.\"}"); + if (!mpd_run_single(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state crossfade.\"}"); + if (!mpd_run_crossfade(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state crossfade.\"}"); - if (config.mixramp) { + 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdb.\"}"); + if (!mpd_run_mixrampdb(mpd_state->conn, float_buf)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdelay.\"}"); + if (!mpd_run_mixrampdelay(mpd_state->conn, float_buf)) + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}"); - mpd_response_finish(mpd.conn); + if (!mpd_send_command(mpd_state->conn, "replay_gain_mode", p_charbuf1, NULL)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}"); + mpd_response_finish(mpd_state->conn); free(p_charbuf1); } - if (mympd_state.jukeboxMode > 0) - mpd_client_jukebox(); + if (mpd_state->jukeboxMode > 0) + mpd_client_jukebox(config, mpd_state); if (len == 0) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_DATABASE_UPDATE: - uint_rc = mpd_run_update(mpd.conn, NULL); + uint_rc = mpd_run_update(mpd_state->conn, NULL); if (uint_rc > 0) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_DATABASE_RESCAN: - uint_rc = mpd_run_rescan(mpd.conn, NULL); + uint_rc = mpd_run_rescan(mpd_state->conn, NULL); if (uint_rc > 0) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_SMARTPLS_UPDATE_ALL: - uint_rc = mpd_client_smartpls_update_all(); + uint_rc = mpd_client_smartpls_update_all(config, mpd_state); if (uint_rc == 0) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}"); else - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}"); + len = snprintf(buffer, 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); @@ -234,7 +419,7 @@ void mpd_client_api(void *arg_request) { 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) { - len = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); + len = mpd_client_smartpls_save(config, mpd_state, p_charbuf1, p_charbuf2, p_charbuf3, NULL, int_buf1, 0); free(p_charbuf2); free(p_charbuf3); } @@ -242,14 +427,14 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, NULL, NULL, 0, int_buf1); + len = mpd_client_smartpls_save(config, mpd_state, 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) { - len = mpd_client_smartpls_save(p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); + len = mpd_client_smartpls_save(config, mpd_state, p_charbuf1, p_charbuf2, p_charbuf3, p_charbuf4, 0, 0); free(p_charbuf2); free(p_charbuf3); free(p_charbuf4); @@ -258,77 +443,77 @@ void mpd_client_api(void *arg_request) { free(p_charbuf1); } if (len == 0) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving playlist failed\"}"); + len = snprintf(buffer, 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) { - len = mpd_client_smartpls_put(mpd.buf, p_charbuf1); + len = mpd_client_smartpls_put(config, mpd_state, buffer, p_charbuf1); free(p_charbuf1); } break; case MPD_API_PLAYER_PAUSE: - if (mpd_run_toggle_pause(mpd.conn)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_toggle_pause(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Toggling player pause failed.\"}"); + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_previous(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Goto previous song failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Goto previous song failed.\"}"); printf("MPD_API_PLAYER_PREV: Error mpd_run_previous()\n"); } break; case MPD_API_PLAYER_NEXT: - if (config.stickers) - mpd_client_count_song_id(mpd.song_id, "skipCount", 1); - if (mpd_run_next(mpd.conn)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (config->stickers) + mpd_client_count_song_id(config, mpd_state, mpd_state->song_id, "skipCount", 1); + if (mpd_run_next(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Skip to next song failed.\"}"); + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_play(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Begin to play failed.\"}"); + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_stop(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Stopping player failed.\"}"); + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_clear(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); printf("MPD_API_QUEUE_CLEAR: Error mpd_run_clear()\n"); } break; case MPD_API_QUEUE_CROP: - len = mpd_client_queue_crop(mpd.buf); + len = mpd_client_queue_crop(config, mpd_state, buffer); 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_delete_id(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from queue failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from queue failed.\"}"); printf("MPD_API_QUEUE_RM_TRACK: Error mpd_run_delete_id()\n"); } } @@ -336,10 +521,10 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_RM_RANGE: 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_delete_range(mpd_state->conn, uint_buf1, uint_buf2)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track range from queue failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track range from queue failed.\"}"); printf("MPD_API_QUEUE_RM_RANGE: Error mpd_run_delete_range()\n"); } } @@ -351,10 +536,10 @@ void mpd_client_api(void *arg_request) { uint_buf2--; if (uint_buf1 < uint_buf2) uint_buf2--; - if (mpd_run_move(mpd.conn, uint_buf1, uint_buf2)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_move(mpd_state->conn, uint_buf1, uint_buf2)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in queue failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in queue failed.\"}"); printf("MPD_API_QUEUE_MOVE_TRACK: Error mpd_run_move()\n"); } } @@ -366,12 +551,12 @@ void mpd_client_api(void *arg_request) { uint_buf2--; if (uint_buf1 < uint_buf2) uint_buf2--; - if (mpd_send_playlist_move(mpd.conn, p_charbuf1, uint_buf1, uint_buf2)) { - mpd_response_finish(mpd.conn); - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_send_playlist_move(mpd_state->conn, p_charbuf1, uint_buf1, uint_buf2)) { + mpd_response_finish(mpd_state->conn); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in playlist failed.\"}"); + len = snprintf(buffer, 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); @@ -380,33 +565,33 @@ void mpd_client_api(void *arg_request) { case MPD_API_PLAYER_PLAY_TRACK: je = json_scanf(request->data, request->length, "{data: { track:%u}}", &uint_buf1); if (je == 1) { - if (mpd_run_play_id(mpd.conn, uint_buf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_play_id(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Set playing track failed.\"}"); + len = snprintf(buffer, 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: - len = mpd_client_put_outputs(mpd.buf); + len = mpd_client_put_outputs(config, mpd_state, buffer); 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_enable_output(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Enabling output failed.\"}"); + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_disable_output(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Disabling output failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Disabling output failed.\"}"); printf("MPD_API_PLAYER_TOGGLE_OUTPUT: Error mpd_run_disable_output()\n"); } } @@ -415,24 +600,24 @@ void mpd_client_api(void *arg_request) { case MPD_API_PLAYER_VOLUME_SET: je = json_scanf(request->data, request->length, "{data: {volume:%u}}", &uint_buf1); if (je == 1) { - if (mpd_run_set_volume(mpd.conn, uint_buf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_set_volume(mpd_state->conn, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting volume failed.\"}"); + len = snprintf(buffer, 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: - len = mpd_client_put_volume(mpd.buf); + len = mpd_client_put_volume(config, mpd_state, buffer); 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_seek_id(mpd_state->conn, uint_buf1, uint_buf2)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Seeking song failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Seeking song failed.\"}"); printf("MPD_API_PLAYER_SEEK: Error mpd_run_seek_id()\n"); } } @@ -440,29 +625,29 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { - len = mpd_client_put_queue(mpd.buf, uint_buf1, &mpd.queue_version, &mpd.queue_length); + len = mpd_client_put_queue(config, mpd_state, buffer, uint_buf1); } break; case MPD_API_QUEUE_LAST_PLAYED: je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { - len = mpd_client_put_last_played_songs(mpd.buf, uint_buf1); + len = mpd_client_put_last_played_songs(config, mpd_state, buffer, uint_buf1); } break; case MPD_API_PLAYER_CURRENT_SONG: - len = mpd_client_put_current_song(mpd.buf); + len = mpd_client_put_current_song(config, mpd_state, buffer); break; case MPD_API_DATABASE_SONGDETAILS: je = json_scanf(request->data, request->length, "{data: { uri: %Q}}", &p_charbuf1); if (je == 1) { - len = mpd_client_put_songdetails(mpd.buf, p_charbuf1); + len = mpd_client_put_songdetails(config, mpd_state, buffer, 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) { - len = mpd_client_put_db_tag(mpd.buf, uint_buf1, p_charbuf2, "", "", p_charbuf1); + len = mpd_client_put_db_tag(config, mpd_state, buffer, uint_buf1, p_charbuf2, "", "", p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -470,7 +655,7 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_put_db_tag(mpd.buf, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); + len = mpd_client_put_db_tag(config, mpd_state, buffer, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -479,7 +664,7 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_put_songs_in_album(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3); + len = mpd_client_put_songs_in_album(config, mpd_state, buffer, p_charbuf1, p_charbuf2, p_charbuf3); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -491,31 +676,33 @@ void mpd_client_api(void *arg_request) { //rename smart playlist char old_pl_file[400]; char new_pl_file[400]; - sanitize_string(p_charbuf1); - sanitize_string(p_charbuf2); - snprintf(old_pl_file, 400, "%s/smartpls/%s", config.varlibdir, p_charbuf1); - snprintf(new_pl_file, 400, "%s/smartpls/%s", config.varlibdir, p_charbuf2); + if (validate_string(p_charbuf1) == false || validate_string(p_charbuf2) == false) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid filename.\"}"); + break; + } + snprintf(old_pl_file, 400, "%s/smartpls/%s", config->varlibdir, p_charbuf1); + snprintf(new_pl_file, 400, "%s/smartpls/%s", config->varlibdir, p_charbuf2); 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) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + len = snprintf(buffer, 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); + else if (mpd_run_rename(mpd_state->conn, p_charbuf1, p_charbuf2)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Error mpd_run_rename()\n"); } } else - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Destination playlist %s already exists\"}", p_charbuf2); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Destination playlist %s already exists\"}", p_charbuf2); } else { - if (mpd_run_rename(mpd.conn, p_charbuf1, p_charbuf2)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); + if (mpd_run_rename(mpd_state->conn, p_charbuf1, p_charbuf2)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Error mpd_run_rename()\n"); } } @@ -526,14 +713,14 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_put_playlists(mpd.buf, uint_buf1, p_charbuf1); + len = mpd_client_put_playlists(config, mpd_state, buffer, 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) { - len = mpd_client_put_playlist_list(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + len = mpd_client_put_playlist_list(config, mpd_state, buffer, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -541,10 +728,10 @@ void mpd_client_api(void *arg_request) { case MPD_API_PLAYLIST_ADD_TRACK: 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); + if (mpd_run_playlist_add(mpd_state->conn, p_charbuf1, p_charbuf2)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding song to playlist failed.\"}"); + len = snprintf(buffer, 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); @@ -554,10 +741,10 @@ void mpd_client_api(void *arg_request) { case MPD_API_PLAYLIST_CLEAR: je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { - if (mpd_run_playlist_clear(mpd.conn, p_charbuf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_playlist_clear(mpd_state->conn, p_charbuf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); printf("MPD_API_PLAYLIST_CLEAR: Error mpd_run_playlist_clear()\n"); } free(p_charbuf1); @@ -566,10 +753,10 @@ void mpd_client_api(void *arg_request) { case MPD_API_PLAYLIST_RM_TRACK: 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)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_playlist_delete(mpd_state->conn, p_charbuf1, uint_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from playlist failed.\"}"); + len = snprintf(buffer, 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); @@ -578,7 +765,7 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_put_browse(mpd.buf, p_charbuf2, uint_buf1, p_charbuf1); + len = mpd_client_put_browse(config, mpd_state, buffer, p_charbuf2, uint_buf1, p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -586,11 +773,11 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_ADD_TRACK_AFTER: je = json_scanf(request->data, request->length, "{data: {uri:%Q, to:%d}}", &p_charbuf1, &int_buf1); if (je == 2) { - int_rc = mpd_run_add_id_to(mpd.conn, p_charbuf1, int_buf1); + int_rc = mpd_run_add_id_to(mpd_state->conn, p_charbuf1, int_buf1); if (int_rc > -1 ) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + len = snprintf(buffer, 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); @@ -599,30 +786,30 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_REPLACE_TRACK: je = json_scanf(request->data, request->length, "{data: {uri:%Q }}", &p_charbuf1); if (je == 1) { - if (!mpd_run_clear(mpd.conn)) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); + if (!mpd_run_clear(mpd_state->conn)) { + len = snprintf(buffer, 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)) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + else if (!mpd_run_add(mpd_state->conn, p_charbuf1)) { + len = snprintf(buffer, 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)) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Playing failed.\"}"); + else if (!mpd_run_play(mpd_state->conn)) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Playing failed.\"}"); printf("MPD_API_QUEUE_REPLACE_TRACK: Error mpd_run_play()\n"); } else - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } break; case MPD_API_QUEUE_ADD_TRACK: je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { - if (mpd_run_add(mpd.conn, p_charbuf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_add(mpd_state->conn, p_charbuf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Append track to queue failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Append track to queue failed.\"}"); printf("MPD_API_QUEUE_ADD_TRACK: Error mpd_run_add()\n"); } free(p_charbuf1); @@ -631,17 +818,17 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_ADD_PLAY_TRACK: je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { - int_buf1 = mpd_run_add_id(mpd.conn, p_charbuf1); + int_buf1 = mpd_run_add_id(mpd_state->conn, p_charbuf1); if (int_buf1 != -1) { - if (mpd_run_play_id(mpd.conn, int_buf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_play_id(mpd_state->conn, int_buf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); printf("MPD_API_QUEUE_ADD_PLAY_TRACK: Error mpd_run_play_id()\n"); } } else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + len = snprintf(buffer, 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); @@ -650,30 +837,30 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_REPLACE_PLAYLIST: je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1); if (je == 1) { - if (!mpd_run_clear(mpd.conn)) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); + if (!mpd_run_clear(mpd_state->conn)) { + len = snprintf(buffer, 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)) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); + else if (!mpd_run_load(mpd_state->conn, p_charbuf1)) { + len = snprintf(buffer, 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)) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); + else if (!mpd_run_play(mpd_state->conn)) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); printf("MPD_API_QUEUE_REPLACE_PLAYLIST: Error mpd_run_play()\n"); } else - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } break; case MPD_API_QUEUE_ADD_PLAYLIST: je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1); if (je == 1) { - if (mpd_run_load(mpd.conn, p_charbuf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_load(mpd_state->conn, p_charbuf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); + len = snprintf(buffer, 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); @@ -682,10 +869,10 @@ void mpd_client_api(void *arg_request) { case MPD_API_QUEUE_SAVE: je = json_scanf(request->data, request->length, "{ data: {plist:%Q}}", &p_charbuf1); if (je == 1) { - if (mpd_run_save(mpd.conn, p_charbuf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_save(mpd_state->conn, p_charbuf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving queue as playlist failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving queue as playlist failed.\"}"); printf("MPD_API_QUEUE_SAVE: Error mpd_run_save()\n"); } free(p_charbuf1); @@ -694,7 +881,7 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_search_queue(mpd.buf, p_charbuf1, uint_buf1, p_charbuf2); + len = mpd_client_search_queue(config, mpd_state, buffer, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -702,7 +889,7 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_search(mpd.buf, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); + len = mpd_client_search(config, mpd_state, buffer, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); } @@ -711,17 +898,17 @@ void mpd_client_api(void *arg_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) { - len = mpd_client_search_adv(mpd.buf, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); + len = mpd_client_search_adv(config, mpd_state, buffer, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); } break; case MPD_API_QUEUE_SHUFFLE: - if (mpd_run_shuffle(mpd.conn)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_shuffle(mpd_state->conn)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Shuffling queue failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Shuffling queue failed.\"}"); printf("MPD_API_QUEUE_SHUFFLE: Error mpd_run_shuffle()\n"); } break; @@ -730,42 +917,45 @@ void mpd_client_api(void *arg_request) { if (je == 1) { //remove smart playlist char pl_file[400]; - sanitize_string(p_charbuf1); - snprintf(pl_file, 400, "%s/smartpls/%s", config.varlibdir, p_charbuf1); + if (validate_string(p_charbuf1) == false) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid filename.\"}"); + break; + } + snprintf(pl_file, 400, "%s/smartpls/%s", config->varlibdir, p_charbuf1); if (access(pl_file, F_OK ) != -1 ) { if (unlink(pl_file) == -1) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting smart playlist failed.\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting smart playlist failed.\"}"); printf("MPD_API_PLAYLIST_RM: Error unlinking smart playlist file()\n"); free(p_charbuf1); break; } } //remove mpd playlist - if (mpd_run_rm(mpd.conn, p_charbuf1)) - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (mpd_run_rm(mpd_state->conn, p_charbuf1)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting playlist failed.\"}"); + len = snprintf(buffer, 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: - len = mpd_client_put_settings(mpd.buf, &config); + len = mpd_client_put_settings(config, mpd_state, buffer); break; case MPD_API_DATABASE_STATS: - len = mpd_client_put_stats(mpd.buf); + len = mpd_client_put_stats(config, mpd_state, buffer); 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)); - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", - mpd_connection_get_error_message(mpd.conn)); + if (mpd_state->conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd_state->conn) != MPD_ERROR_SUCCESS) { + printf("Error: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + len = snprintf(buffer, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", + mpd_connection_get_error_message(mpd_state->conn)); /* Try to recover error */ - if (!mpd_connection_clear_error(mpd.conn)) - mpd.conn_state = MPD_FAILURE; + if (!mpd_connection_clear_error(mpd_state->conn)) + mpd_state->conn_state = MPD_FAILURE; } #ifdef DEBUG @@ -779,29 +969,31 @@ void mpd_client_api(void *arg_request) { #endif if (len == 0) { - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd_id %u.\"}", request->cmd_id); + len = snprintf(buffer, 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); + 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)); + t_work_result *response = (t_work_result*)malloc(sizeof(t_work_result)); response->conn_id = request->conn_id; - response->length = copy_string(response->data, mpd.buf, MAX_SIZE, len); + response->length = copy_string(response->data, buffer, MAX_SIZE, len); tiny_queue_push(web_server_queue, response); free(request); } -void mpd_client_notify(size_t len) { - LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s.\n", mpd.buf); +static void mpd_client_notify(const char *message, const size_t len) { + LOG_DEBUG() fprintf(stderr, "DEBUG: Websocket notify: %s.\n", message); - struct work_result_t *response = (struct work_result_t*)malloc(sizeof(struct work_result_t)); + t_work_result *response = (t_work_result *)malloc(sizeof(t_work_result)); response->conn_id = 0; - response->length = copy_string(response->data, mpd.buf, MAX_SIZE, len); + response->length = copy_string(response->data, message, MAX_SIZE, len); tiny_queue_push(web_server_queue, response); } -void mpd_client_parse_idle(int idle_bitmask) { +static void mpd_client_parse_idle(t_config *config, t_mpd_state *mpd_state, int idle_bitmask) { size_t len = 0; + char buffer[MAX_SIZE]; + for (unsigned j = 0;; j++) { enum mpd_idle idle_event = 1 << j; const char *idle_name = mpd_idle_name(idle_event); @@ -811,131 +1003,131 @@ 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: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_database\"}"); - mpd_client_smartpls_update_all(); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_database\"}"); + mpd_client_smartpls_update_all(config, mpd_state); break; case MPD_IDLE_STORED_PLAYLIST: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_stored_playlist\"}"); break; case MPD_IDLE_QUEUE: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_queue\"}"); - if (mympd_state.jukeboxMode > 0) - mpd_client_jukebox(); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_queue\"}"); + if (mpd_state->jukeboxMode > 0) + mpd_client_jukebox(config, mpd_state); break; case MPD_IDLE_PLAYER: - 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); - if (config.stickers && mpd.last_update_sticker_song_id != mpd.song_id) { - mpd_client_count_song_id(mpd.song_id, "playCount", 1); - mpd_client_last_played_song_id(mpd.song_id); - mpd.last_update_sticker_song_id = mpd.song_id; + len = mpd_client_put_state(config, mpd_state, buffer); + if (mpd_state->song_id != mpd_state->last_song_id) { + if (mpd_state->last_last_played_id != mpd_state->song_id) + mpd_client_last_played_list(config, mpd_state, mpd_state->song_id); + if (config->stickers && mpd_state->last_update_sticker_song_id != mpd_state->song_id) { + mpd_client_count_song_id(config, mpd_state, mpd_state->song_id, "playCount", 1); + mpd_client_last_played_song_id(config, mpd_state, mpd_state->song_id); + mpd_state->last_update_sticker_song_id = mpd_state->song_id; } } break; case MPD_IDLE_MIXER: - len = mpd_client_put_volume(mpd.buf); + len = mpd_client_put_volume(config, mpd_state, buffer); break; case MPD_IDLE_OUTPUT: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_outputs\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_outputs\"}"); break; case MPD_IDLE_OPTIONS: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_options\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_options\"}"); break; case MPD_IDLE_UPDATE: - len = mpd_client_get_updatedb_state(mpd.buf); + len = mpd_client_get_updatedb_state(config, mpd_state, buffer); break; case MPD_IDLE_STICKER: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_sticker\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_sticker\"}"); break; case MPD_IDLE_SUBSCRIPTION: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_subscription\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_subscription\"}"); break; case MPD_IDLE_MESSAGE: - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"update_message\"}"); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_message\"}"); break; default: len = 0; } if (len > 0) { - mpd_client_notify(len); + mpd_client_notify(buffer, len); } } } } -void mpd_client_mpd_features() { +static void mpd_client_mpd_features(t_config *config, t_mpd_state *mpd_state) { struct mpd_pair *pair; char s[2] = ","; - char *taglist = strdup(config.taglist); - char *searchtaglist = strdup(config.searchtaglist); - char *browsetaglist = strdup(config.browsetaglist); + char *taglist = strdup(config->taglist); + char *searchtaglist = strdup(config->searchtaglist); + char *browsetaglist = strdup(config->browsetaglist); char *token, *rest; - mpd.protocol = mpd_connection_get_server_version(mpd.conn); + mpd_state->protocol = mpd_connection_get_server_version(mpd_state->conn); - LOG_INFO() printf("MPD protocoll version: %u.%u.%u\n", mpd.protocol[0], mpd.protocol[1], mpd.protocol[2]); + LOG_INFO() printf("MPD protocoll version: %u.%u.%u\n", mpd_state->protocol[0], mpd_state->protocol[1], mpd_state->protocol[2]); // Defaults - mpd.feat_sticker = false; - mpd.feat_playlists = false; - mpd.feat_tags = false; - mpd.feat_advsearch = false; + mpd_state->feat_sticker = false; + mpd_state->feat_playlists = false; + mpd_state->feat_tags = false; + mpd_state->feat_advsearch = false; - if (mpd_send_allowed_commands(mpd.conn)) { - while ((pair = mpd_recv_command_pair(mpd.conn)) != NULL) { + if (mpd_send_allowed_commands(mpd_state->conn)) { + while ((pair = mpd_recv_command_pair(mpd_state->conn)) != NULL) { if (strcmp(pair->value, "sticker") == 0) - mpd.feat_sticker = true; - if (strcmp(pair->value, "listplaylists") == 0) - mpd.feat_playlists = true; - mpd_return_pair(mpd.conn, pair); + mpd_state->feat_sticker = true; + else if (strcmp(pair->value, "listplaylists") == 0) + mpd_state->feat_playlists = true; + mpd_return_pair(mpd_state->conn, pair); } - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); } else { LOG_ERROR_AND_RECOVER("mpd_send_allowed_commands"); } - if (mpd.feat_sticker == false && config.stickers == true) { + if (mpd_state->feat_sticker == false && config->stickers == true) { LOG_INFO() printf("MPD don't support stickers, disabling myMPD feature\n"); - config.stickers = false; + config->stickers = false; } - if (config.stickers == false && config.smartpls == true) { + if (config->stickers == false && config->smartpls == true) { LOG_INFO() printf("Stickers are disabled, disabling smartplaylists\n"); - config.smartpls = false; + config->smartpls = false; } - if (mpd.feat_playlists == false && config.smartpls == true) { + if (mpd_state->feat_playlists == false && config->smartpls == true) { LOG_INFO() printf("Playlists are disabled, disabling smartplaylists\n"); - config.smartpls = false; + config->smartpls = false; } LOG_INFO() printf("MPD supported tags: "); - list_free(&mpd_tags); - if (mpd_send_list_tag_types(mpd.conn)) { - while ((pair = mpd_recv_tag_type_pair(mpd.conn)) != NULL) { + list_free(&mpd_state->mpd_tags); + if (mpd_send_list_tag_types(mpd_state->conn)) { + while ((pair = mpd_recv_tag_type_pair(mpd_state->conn)) != NULL) { LOG_INFO() printf("%s ", pair->value); - list_push(&mpd_tags, pair->value, 1); - mpd_return_pair(mpd.conn, pair); + list_push(&mpd_state->mpd_tags, pair->value, 1); + mpd_return_pair(mpd_state->conn, pair); } - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); } else { LOG_ERROR_AND_RECOVER("mpd_send_list_tag_types"); } - list_free(&mympd_tags); - if (mpd_tags.length == 0) { + list_free(&mpd_state->mympd_tags); + if (mpd_state->mpd_tags.length == 0) { LOG_INFO() printf("none\nTags are disabled\n"); - mpd.feat_tags = false; + mpd_state->feat_tags = false; } else { - mpd.feat_tags = true; + mpd_state->feat_tags = true; LOG_INFO() printf("\nmyMPD enabled tags: "); enum mpd_tag_type types[64]; unsigned n = 0; token = strtok_r(taglist, s, &rest); while (token != NULL) { - if (list_get_value(&mpd_tags, token) == 1) { - list_push(&mympd_tags, token, 1); + if (list_get_value(&mpd_state->mpd_tags, token) == 1) { + list_push(&mpd_state->mympd_tags, token, 1); types[n++] = mpd_tag_name_parse(token); LOG_INFO() printf("%s ", token); } @@ -943,24 +1135,24 @@ void mpd_client_mpd_features() { } LOG_INFO() printf("\n"); #if LIBMPDCLIENT_CHECK_VERSION(2,12,0) - if (mpd_connection_cmp_server_version(mpd.conn, 0, 21, 0) >= 0) { + if (mpd_connection_cmp_server_version(mpd_state->conn, 0, 21, 0) >= 0) { LOG_VERBOSE() printf("Enabling mpd tag types\n"); - if (mpd_command_list_begin(mpd.conn, false)) { - mpd_send_clear_tag_types(mpd.conn); - mpd_send_enable_tag_types(mpd.conn, types, n); - if (!mpd_command_list_end(mpd.conn)) + if (mpd_command_list_begin(mpd_state->conn, false)) { + mpd_send_clear_tag_types(mpd_state->conn); + mpd_send_enable_tag_types(mpd_state->conn, types, n); + if (!mpd_command_list_end(mpd_state->conn)) LOG_ERROR_AND_RECOVER("mpd_command_list_end"); } else LOG_ERROR_AND_RECOVER("mpd_command_list_begin"); - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); } #endif LOG_INFO() printf("myMPD enabled searchtags: "); token = strtok_r(searchtaglist, s, &rest); while (token != NULL) { - if (list_get_value(&mympd_tags, token) == 1) { - list_push(&mympd_searchtags, token, 1); + if (list_get_value(&mpd_state->mympd_tags, token) == 1) { + list_push(&mpd_state->mympd_searchtags, token, 1); LOG_INFO() printf("%s ", token); } token = strtok_r(NULL, s, &rest); @@ -968,8 +1160,8 @@ void mpd_client_mpd_features() { LOG_INFO() printf("\nmyMPD enabled browsetags: "); token = strtok_r(browsetaglist, s, &rest); while (token != NULL) { - if (list_get_value(&mympd_tags, token) == 1) { - list_push(&mympd_browsetags, token, 1); + if (list_get_value(&mpd_state->mympd_tags, token) == 1) { + list_push(&mpd_state->mympd_browsetags, token, 1); LOG_INFO() printf("%s ", token); } token = strtok_r(NULL, s, &rest); @@ -980,107 +1172,110 @@ void mpd_client_mpd_features() { free(searchtaglist); free(browsetaglist); - if (LIBMPDCLIENT_CHECK_VERSION(2, 17, 0) && mpd_connection_cmp_server_version(mpd.conn, 0, 21, 0) >= 0) { - mpd.feat_advsearch = true; + if (LIBMPDCLIENT_CHECK_VERSION(2, 17, 0) && mpd_connection_cmp_server_version(mpd_state->conn, 0, 21, 0) >= 0) { + mpd_state->feat_advsearch = true; LOG_INFO() printf("Enabling advanced search.\n"); } else LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0.\n"); } -void mpd_client_idle(int timeout) { +static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state, const int timeout) { struct pollfd fds[1]; int pollrc; + char buffer[MAX_SIZE]; size_t len = 0; - switch (mpd.conn_state) { + switch (mpd_state->conn_state) { case MPD_DISCONNECTED: /* Try to connect */ - LOG_INFO() printf("MPD Connecting to %s:%ld\n", config.mpdhost, config.mpdport); - mpd.conn = mpd_connection_new(config.mpdhost, config.mpdport, mpd.timeout); - if (mpd.conn == NULL) { + LOG_INFO() printf("MPD Connecting to %s:%ld\n", config->mpdhost, config->mpdport); + mpd_state->conn = mpd_connection_new(config->mpdhost, config->mpdport, mpd_state->timeout); + if (mpd_state->conn == NULL) { printf("MPD connection failed."); - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mpd_client_notify(len); - mpd.conn_state = MPD_FAILURE; + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"disconnected\"}"); + mpd_client_notify(buffer, len); + mpd_state->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)); - 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; + if (mpd_connection_get_error(mpd_state->conn) != MPD_ERROR_SUCCESS) { + printf("MPD connection: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd_state->conn)); + mpd_client_notify(buffer, len); + mpd_state->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)); - 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; + if (config->mpdpass && !mpd_run_password(mpd_state->conn, config->mpdpass)) { + printf("MPD connection: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd_state->conn)); + mpd_client_notify(buffer, len); + mpd_state->conn_state = MPD_FAILURE; return; } LOG_INFO() printf("MPD connected.\n"); - mpd_connection_set_timeout(mpd.conn, mpd.timeout); - mpd.conn_state = MPD_CONNECTED; - mpd_client_mpd_features(); - mpd_client_smartpls_update_all(); - if (mympd_state.jukeboxMode > 0) - mpd_client_jukebox(); - mpd_send_idle(mpd.conn); + mpd_connection_set_timeout(mpd_state->conn, mpd_state->timeout); + mpd_state->conn_state = MPD_CONNECTED; + mpd_client_mpd_features(config, mpd_state); + mpd_client_smartpls_update_all(config, mpd_state); + if (mpd_state->jukeboxMode > 0) + mpd_client_jukebox(config, mpd_state); + mpd_send_idle(mpd_state->conn); break; case MPD_FAILURE: printf("MPD connection failed.\n"); - len = snprintf(mpd.buf, MAX_SIZE, "{\"type\": \"disconnected\"}"); - mpd_client_notify(len); + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"disconnected\"}"); + mpd_client_notify(buffer, len); case MPD_DISCONNECT: case MPD_RECONNECT: - if (mpd.conn != NULL) - mpd_connection_free(mpd.conn); - mpd.conn = NULL; - mpd.conn_state = MPD_DISCONNECTED; + if (mpd_state->conn != NULL) + mpd_connection_free(mpd_state->conn); + mpd_state->conn = NULL; + mpd_state->conn_state = MPD_DISCONNECTED; break; case MPD_CONNECTED: - fds[0].fd = mpd_connection_get_fd(mpd.conn); + fds[0].fd = mpd_connection_get_fd(mpd_state->conn); fds[0].events = POLLIN; pollrc = poll(fds, 1, timeout); unsigned mpd_client_queue_length = tiny_queue_length(mpd_client_queue); if (pollrc > 0 || mpd_client_queue_length > 0) { LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode.\n"); - mpd_send_noidle(mpd.conn); + mpd_send_noidle(mpd_state->conn); if (pollrc > 0) { //Handle idle events LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events.\n"); - enum mpd_idle idle_bitmask = mpd_recv_idle(mpd.conn, false); - mpd_client_parse_idle(idle_bitmask); + enum mpd_idle idle_bitmask = mpd_recv_idle(mpd_state->conn, false); + mpd_client_parse_idle(config, mpd_state, idle_bitmask); } else { - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); } if (mpd_client_queue_length > 0) { //Handle request LOG_DEBUG() fprintf(stderr, "DEBUG: Handle request.\n"); struct work_request_t *req = tiny_queue_shift(mpd_client_queue); - mpd_client_api(req); + mpd_client_api(config, mpd_state, req); } LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode.\n"); - mpd_send_idle(mpd.conn); + mpd_send_idle(mpd_state->conn); } break; + default: + printf("ERROR: Invalid mpd connection state\n"); } } -int mpd_client_get_updatedb_state(char *buffer) { +static int mpd_client_get_updatedb_state(t_config *config, t_mpd_state *mpd_state, char *buffer) { struct mpd_status *status; int len, update_id; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - status = mpd_run_status(mpd.conn); + status = mpd_run_status(mpd_state->conn); if (!status) RETURN_ERROR_AND_RECOVER("mpd_run_status"); update_id = mpd_status_get_update_id(status); @@ -1096,7 +1291,7 @@ int mpd_client_get_updatedb_state(char *buffer) { } -bool mpd_client_get_sticker(const char *uri, t_sticker *sticker) { +static bool mpd_client_get_sticker(t_config *config, t_mpd_state *mpd_state, const char *uri, t_sticker *sticker) { struct mpd_pair *pair; char *crap; sticker->playCount = 0; @@ -1106,8 +1301,8 @@ bool mpd_client_get_sticker(const char *uri, t_sticker *sticker) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; - if (mpd_send_sticker_list(mpd.conn, "song", uri)) { - while ((pair = mpd_recv_sticker(mpd.conn)) != NULL) { + if (mpd_send_sticker_list(mpd_state->conn, "song", uri)) { + while ((pair = mpd_recv_sticker(mpd_state->conn)) != NULL) { if (strcmp(pair->name, "playCount") == 0) sticker->playCount = strtol(pair->value, &crap, 10); else if (strcmp(pair->name, "skipCount") == 0) @@ -1116,7 +1311,7 @@ bool mpd_client_get_sticker(const char *uri, t_sticker *sticker) { sticker->lastPlayed = strtol(pair->value, &crap, 10); else if (strcmp(pair->name, "like") == 0) sticker->like = strtol(pair->value, &crap, 10); - mpd_return_sticker(mpd.conn, pair); + mpd_return_sticker(mpd_state->conn, pair); } } else { @@ -1126,12 +1321,12 @@ bool mpd_client_get_sticker(const char *uri, t_sticker *sticker) { return true; } -bool mpd_client_count_song_id(int song_id, char *name, int value) { +static bool mpd_client_count_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id, const char *name, const int value) { struct mpd_song *song; if (song_id > -1) { - song = mpd_run_get_queue_song_id(mpd.conn, song_id); + song = mpd_run_get_queue_song_id(mpd_state->conn, song_id); if (song) { - if (!mpd_client_count_song_uri(mpd_song_get_uri(song), name, value)) { + if (!mpd_client_count_song_uri(config, mpd_state, mpd_song_get_uri(song), name, value)) { mpd_song_free(song); return false; } @@ -1147,18 +1342,18 @@ bool mpd_client_count_song_id(int song_id, char *name, int value) { return true; } -bool mpd_client_count_song_uri(const char *uri, char *name, int value) { +static bool mpd_client_count_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, const char *name, const int value) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; struct mpd_pair *pair; char *crap; int old_value = 0; char v[4]; - if (mpd_send_sticker_list(mpd.conn, "song", uri)) { - while ((pair = mpd_recv_sticker(mpd.conn)) != NULL) { + if (mpd_send_sticker_list(mpd_state->conn, "song", uri)) { + while ((pair = mpd_recv_sticker(mpd_state->conn)) != NULL) { if (strcmp(pair->name, name) == 0) old_value = strtol(pair->value, &crap, 10); - mpd_return_sticker(mpd.conn, pair); + mpd_return_sticker(mpd_state->conn, pair); } } else { LOG_ERROR_AND_RECOVER("mpd_send_sticker_list"); @@ -1171,14 +1366,14 @@ bool mpd_client_count_song_uri(const char *uri, char *name, int value) { old_value = 0; snprintf(v, 4, "%d", old_value); LOG_VERBOSE() printf("Setting sticker: \"%s\" -> %s: %s\n", uri, name, v); - if (!mpd_run_sticker_set(mpd.conn, "song", uri, name, v)) { + if (!mpd_run_sticker_set(mpd_state->conn, "song", uri, name, v)) { LOG_ERROR_AND_RECOVER("mpd_send_sticker_set"); return false; } return true; } -bool mpd_client_like_song_uri(const char *uri, int value) { +static bool mpd_client_like_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, int value) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; char v[2]; @@ -1188,35 +1383,35 @@ bool mpd_client_like_song_uri(const char *uri, int value) { value = 0; snprintf(v, 2, "%d", value); LOG_VERBOSE() printf("Setting sticker: \"%s\" -> like: %s\n", uri, v); - if (!mpd_run_sticker_set(mpd.conn, "song", uri, "like", v)) { + if (!mpd_run_sticker_set(mpd_state->conn, "song", uri, "like", v)) { LOG_ERROR_AND_RECOVER("mpd_send_sticker_set"); return false; } return true; } -bool mpd_client_last_played_list(int song_id) { +static bool mpd_client_last_played_list(t_config *config, t_mpd_state *mpd_state, const int song_id) { struct mpd_song *song; char tmp_file[400]; char cfg_file[400]; - snprintf(cfg_file, 400, "%s/state/last_played", config.varlibdir); - snprintf(tmp_file, 400, "%s/tmp/last_played", config.varlibdir); + snprintf(cfg_file, 400, "%s/state/last_played", config->varlibdir); + snprintf(tmp_file, 400, "%s/tmp/last_played", config->varlibdir); if (song_id > -1) { - song = mpd_run_get_queue_song_id(mpd.conn, song_id); + song = mpd_run_get_queue_song_id(mpd_state->conn, song_id); if (song) { - list_insert(&last_played, mpd_song_get_uri(song), time(NULL)); - mpd.last_last_played_id = song_id; + list_insert(&mpd_state->last_played, mpd_song_get_uri(song), time(NULL)); + mpd_state->last_last_played_id = song_id; mpd_song_free(song); - if (last_played.length > config.last_played_count) { - list_shift(&last_played, last_played.length -1); + if (mpd_state->last_played.length > config->last_played_count) { + list_shift(&mpd_state->last_played, mpd_state->last_played.length -1); } FILE *fp = fopen(tmp_file, "w"); if (fp == NULL) { printf("Error opening %s\n", tmp_file); return false; } - struct node *current = last_played.list; + struct node *current = mpd_state->last_played.list; while (current != NULL) { fprintf(fp, "%ld::%s\n", current->value, current->data); current = current->next; @@ -1234,13 +1429,13 @@ bool mpd_client_last_played_list(int song_id) { return true; } -bool mpd_client_last_played_song_id(int song_id) { +static bool mpd_client_last_played_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id) { struct mpd_song *song; if (song_id > -1) { - song = mpd_run_get_queue_song_id(mpd.conn, song_id); + song = mpd_run_get_queue_song_id(mpd_state->conn, song_id); if (song) { - if (!mpd_client_last_played_song_uri(mpd_song_get_uri(song))) { + if (mpd_client_last_played_song_uri(config, mpd_state, mpd_song_get_uri(song)) == false) { mpd_song_free(song); return false; } @@ -1260,13 +1455,13 @@ bool mpd_client_last_played_song_id(int song_id) { return true; } -bool mpd_client_last_played_song_uri(const char *uri) { +static bool mpd_client_last_played_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; char v[20]; snprintf(v, 20, "%lu", time(NULL)); LOG_VERBOSE() printf("Setting sticker: \"%s\" -> lastPlayed: %s\n", uri, v); - if (!mpd_run_sticker_set(mpd.conn, "song", uri, "lastPlayed", v)) { + if (!mpd_run_sticker_set(mpd_state->conn, "song", uri, "lastPlayed", v)) { LOG_ERROR_AND_RECOVER("mpd_send_sticker_set"); return false; } @@ -1274,7 +1469,7 @@ bool mpd_client_last_played_song_uri(const char *uri) { } -char *mpd_client_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { +static char *mpd_client_get_tag(struct mpd_song const *song, const enum mpd_tag_type tag) { char *str; str = (char *)mpd_song_get_tag(song, tag, 0); if (str == NULL) { @@ -1288,9 +1483,8 @@ char *mpd_client_get_tag(struct mpd_song const *song, enum mpd_tag_type tag) { return str; } -bool mpd_client_jukebox() { - struct mpd_status *status; - status = mpd_run_status(mpd.conn); +static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state) { + struct mpd_status *status = mpd_run_status(mpd_state->conn); int queue_length, addSongs, i; struct mpd_entity *entity; const struct mpd_song *song; @@ -1304,18 +1498,18 @@ bool mpd_client_jukebox() { } queue_length = mpd_status_get_queue_length(status); mpd_status_free(status); - if (queue_length > mympd_state.jukeboxQueueLength) + if (queue_length > mpd_state->jukeboxQueueLength) return true; - if (mympd_state.jukeboxMode == 1) - addSongs = mympd_state.jukeboxQueueLength - queue_length; + if (mpd_state->jukeboxMode == 1) + addSongs = mpd_state->jukeboxQueueLength - queue_length; else addSongs = 1; if (addSongs < 1) return true; - if (mpd.feat_playlists == false && strcmp(mympd_state.jukeboxPlaylist, "Database") != 0) { + if (mpd_state->feat_playlists == false && strcmp(mpd_state->jukeboxPlaylist, "Database") != 0) { LOG_INFO() printf("Jukebox: Playlists are disabled\n"); return true; } @@ -1325,23 +1519,23 @@ bool mpd_client_jukebox() { struct list add_list; list_init(&add_list); - if (mympd_state.jukeboxMode == 1) { + if (mpd_state->jukeboxMode == 1) { //add songs - if (strcmp(mympd_state.jukeboxPlaylist, "Database") == 0) { - if (!mpd_send_list_all(mpd.conn, "/")) { + if (strcmp(mpd_state->jukeboxPlaylist, "Database") == 0) { + if (!mpd_send_list_all(mpd_state->conn, "/")) { LOG_ERROR_AND_RECOVER("mpd_send_list_all"); list_free(&add_list); return false; } } else { - if (!mpd_send_list_playlist(mpd.conn, mympd_state.jukeboxPlaylist)) { + if (!mpd_send_list_playlist(mpd_state->conn, mpd_state->jukeboxPlaylist)) { LOG_ERROR_AND_RECOVER("mpd_send_list_playlist"); list_free(&add_list); return false; } } - while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + while ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { if (randrange(lineno) < addSongs) { if (nkeep < addSongs) { @@ -1368,19 +1562,19 @@ bool mpd_client_jukebox() { mpd_entity_free(entity); } } - else if (mympd_state.jukeboxMode == 2) { + else if (mpd_state->jukeboxMode == 2) { //add album - if (!mpd_search_db_tags(mpd.conn, MPD_TAG_ALBUM)) { + if (!mpd_search_db_tags(mpd_state->conn, MPD_TAG_ALBUM)) { LOG_ERROR_AND_RECOVER("mpd_search_db_tags"); list_free(&add_list); return false; } - if (!mpd_search_commit(mpd.conn)) { + if (!mpd_search_commit(mpd_state->conn)) { LOG_ERROR_AND_RECOVER("mpd_search_commit"); list_free(&add_list); return false; } - while ((pair = mpd_recv_pair_tag(mpd.conn, MPD_TAG_ALBUM )) != NULL) { + while ((pair = mpd_recv_pair_tag(mpd_state->conn, MPD_TAG_ALBUM )) != NULL) { if (randrange(lineno) < addSongs) { if (nkeep < addSongs) { list_push(&add_list, strdup(pair->value), lineno); @@ -1399,7 +1593,7 @@ bool mpd_client_jukebox() { } } lineno++; - mpd_return_pair(mpd.conn, pair); + mpd_return_pair(mpd_state->conn, pair); } } @@ -1411,51 +1605,49 @@ bool mpd_client_jukebox() { nkeep = 0; struct node *current = add_list.list; while (current != NULL) { - if (mympd_state.jukeboxMode == 1) { + if (mpd_state->jukeboxMode == 1) { LOG_INFO() printf("Jukebox adding song: %s\n", current->data); - if (!mpd_run_add(mpd.conn, current->data)) + if (!mpd_run_add(mpd_state->conn, current->data)) LOG_ERROR_AND_RECOVER("mpd_run_add"); else nkeep++; } else { LOG_INFO() printf("Jukebox adding album: %s\n", current->data); - if (!mpd_send_command(mpd.conn, "searchadd", "Album", current->data, NULL)) { + if (!mpd_send_command(mpd_state->conn, "searchadd", "Album", current->data, NULL)) { LOG_ERROR_AND_RECOVER("mpd_send_command"); return false; } else nkeep++; - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); } current = current->next; } list_free(&add_list); if (nkeep > 0) - mpd_run_play(mpd.conn); + mpd_run_play(mpd_state->conn); else { printf("Error adding song(s), trying again...\n"); - mpd_client_jukebox(); + mpd_client_jukebox(config, mpd_state); } return true; } -int mpd_client_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length) { - struct mpd_status *status; - const struct mpd_audio_format *audioformat; - int len; +static int mpd_client_put_state(t_config *config, t_mpd_state *mpd_state, char *buffer) { + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - status = mpd_run_status(mpd.conn); + struct mpd_status *status = mpd_run_status(mpd_state->conn); if (!status) { - printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn)); - mpd.conn_state = MPD_FAILURE; + printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + mpd_state->conn_state = MPD_FAILURE; return 0; } - audioformat = mpd_status_get_audio_format(status); - int song_id = mpd_status_get_song_id(status); - if (*current_song_id != song_id) - *last_song_id = *current_song_id; + const struct mpd_audio_format *audioformat = mpd_status_get_audio_format(status); + const int song_id = mpd_status_get_song_id(status); + if (mpd_state->song_id != song_id) + mpd_state->last_song_id = mpd_state->song_id; len = json_printf(&out, "{type: update_state, data: {state: %d, volume: %d, songPos: %d, elapsedTime: %d, " "totalTime: %d, currentSongId: %d, kbitrate: %d, audioFormat: { sampleRate: %d, bits: %d, channels: %d}, " @@ -1473,30 +1665,29 @@ int mpd_client_put_state(char *buffer, int *current_song_id, int *next_song_id, mpd_status_get_queue_length(status), mpd_status_get_next_song_pos(status), mpd_status_get_next_song_id(status), - *last_song_id ? *last_song_id : -1, + mpd_state->last_song_id ? mpd_state->last_song_id : -1, mpd_status_get_queue_version(status) ); len += json_printf(&out, "}}"); - *current_song_id = song_id; - *next_song_id = mpd_status_get_next_song_id(status); - *queue_version = mpd_status_get_queue_version(status); - *queue_length = mpd_status_get_queue_length(status); + mpd_state->song_id = song_id; + mpd_state->next_song_id = mpd_status_get_next_song_id(status); + mpd_state->queue_version = mpd_status_get_queue_version(status); + mpd_state->queue_length = mpd_status_get_queue_length(status); mpd_status_free(status); CHECK_RETURN_LEN(); } -int mpd_client_put_volume(char *buffer) { - struct mpd_status *status; - int len; +static int mpd_client_put_volume(t_config *config, t_mpd_state *mpd_state, char *buffer) { + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - status = mpd_run_status(mpd.conn); + struct mpd_status *status = mpd_run_status(mpd_state->conn); if (!status) { - printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn)); - mpd.conn_state = MPD_FAILURE; + printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + mpd_state->conn_state = MPD_FAILURE; return 0; } len = json_printf(&out, "{type: update_volume, data: {volume: %d}}", @@ -1507,33 +1698,15 @@ int mpd_client_put_volume(char *buffer) { CHECK_RETURN_LEN(); } -int mpd_client_put_welcome(char *buffer) { - int len; - struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - const unsigned *version = mpd_connection_get_server_version(mpd.conn); - char mpd_version[20]; - char libmpdclient_version[20]; - - snprintf(mpd_version, 20, "%u.%u.%u", version[0], version[1], version[2]); - snprintf(libmpdclient_version, 20, "%i.%i.%i", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); - - len = json_printf(&out, "{type: welcome, data: {mympdVersion: %Q, mpdVersion: %Q, libmpdclientVersion: %Q}}", - MYMPD_VERSION, - mpd_version, - libmpdclient_version - ); - - CHECK_RETURN_LEN(); -} - -bool mpd_client_state_get(char *name, char *value) { +static bool mpd_client_state_get(t_config *config, t_mpd_state *mpd_state, const char *name, char *value) { char cfg_file[400]; char *line; size_t n = 0; ssize_t read; - sanitize_string(name); - snprintf(cfg_file, 400, "%s/state/%s", config.varlibdir, name); + if (!validate_string(name)) + return false; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); FILE *fp = fopen(cfg_file, "r"); if (fp == NULL) { printf("Error opening %s\n", cfg_file); @@ -1549,13 +1722,14 @@ bool mpd_client_state_get(char *name, char *value) { return false; } -bool mpd_client_state_set(const char *name, const char *value) { +static bool mpd_client_state_set(t_config *config, t_mpd_state *mpd_state, const char *name, const char *value) { char tmp_file[400]; char cfg_file[400]; - sanitize_string(name); - snprintf(cfg_file, 400, "%s/state/%s", config.varlibdir, name); - snprintf(tmp_file, 400, "%s/tmp/%s", config.varlibdir, name); + if (!validate_string(name)) + return false; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name); FILE *fp = fopen(tmp_file, "w"); if (fp == NULL) { @@ -1571,27 +1745,25 @@ bool mpd_client_state_set(const char *name, const char *value) { return true; } -int mpd_client_put_settings(char *buffer, void *arg_config) { - struct mpd_status *status; - char *replaygain = strdup(""); - int len; +static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, char *buffer) { + char *replaygain = ""; + size_t 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); + struct mpd_status *status = mpd_run_status(mpd_state->conn); if (!status) { - printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn)); - mpd.conn_state = MPD_FAILURE; + printf("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + mpd_state->conn_state = MPD_FAILURE; return 0; } - if (!mpd_send_command(mpd.conn, "replay_gain_status", NULL)) + if (!mpd_send_command(mpd_state->conn, "replay_gain_status", NULL)) RETURN_ERROR_AND_RECOVER("replay_gain_status"); struct mpd_pair *pair; - while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { + while ((pair = mpd_recv_pair(mpd_state->conn)) != NULL) { replaygain = strdup(pair->value); - mpd_return_pair(mpd.conn, pair); + mpd_return_pair(mpd_state->conn, pair); } len = json_printf(&out, "{type: settings, data: {" @@ -1611,10 +1783,10 @@ int mpd_client_put_settings(char *buffer, void *arg_config) { config->mpdport, config->mpdpass ? "true" : "false", config->syscmds, - mpd.feat_playlists, - mpd.feat_tags, - mpd.feat_library, - mpd.feat_advsearch, + mpd_state->feat_playlists, + mpd_state->feat_tags, + mpd_state->feat_library, + mpd_state->feat_advsearch, config->localplayer, config->streamport, config->streamurl, @@ -1625,18 +1797,18 @@ int mpd_client_put_settings(char *buffer, void *arg_config) { config->smartpls, config->max_elements_per_page, replaygain, - mympd_state.notificationWeb, - mympd_state.notificationPage, - mympd_state.jukeboxMode, - mympd_state.jukeboxPlaylist, - mympd_state.jukeboxQueueLength, + mpd_state->notificationWeb, + mpd_state->notificationPage, + mpd_state->jukeboxMode, + mpd_state->jukeboxPlaylist, + mpd_state->jukeboxQueueLength, config->coverimagesize ); mpd_status_free(status); free(replaygain); nr = 0; - struct node *current = mympd_tags.list; + struct node *current = mpd_state->mympd_tags.list; while (current != NULL) { if (nr++) len += json_printf(&out, ","); @@ -1645,7 +1817,7 @@ int mpd_client_put_settings(char *buffer, void *arg_config) { } len += json_printf(&out, "], searchtags: ["); nr = 0; - current = mympd_searchtags.list; + current = mpd_state->mympd_searchtags.list; while (current != NULL) { if (nr++) len += json_printf(&out, ","); @@ -1654,7 +1826,7 @@ int mpd_client_put_settings(char *buffer, void *arg_config) { } len += json_printf(&out, "], browsetags: ["); nr = 0; - current = mympd_browsetags.list; + current = mpd_state->mympd_browsetags.list; while (current != NULL) { if (nr++) len += json_printf(&out, ","); @@ -1666,8 +1838,7 @@ int mpd_client_put_settings(char *buffer, void *arg_config) { if (config->syscmds == true) { len += json_printf(&out, ", syscmds: ["); nr = 0; - struct list *syscmd_list = (struct list *) config->syscmd_list; - current = syscmd_list->list; + current = config->syscmd_list.list; while (current != NULL) { if (nr++) len += json_printf(&out, ","); @@ -1678,41 +1849,40 @@ int mpd_client_put_settings(char *buffer, void *arg_config) { } len += json_printf(&out, ", colsQueueCurrent: %s, colsSearch: %s, colsBrowseDatabase: %s, colsBrowsePlaylistsDetail: %s, " "colsBrowseFilesystem: %s, colsPlayback: %s, colsQueueLastPlayed: %s}}", - mympd_state.colsQueueCurrent, - mympd_state.colsSearch, - mympd_state.colsBrowseDatabase, - mympd_state.colsBrowsePlaylistsDetail, - mympd_state.colsBrowseFilesystem, - mympd_state.colsPlayback, - mympd_state.colsQueueLastPlayed + mpd_state->colsQueueCurrent, + mpd_state->colsSearch, + mpd_state->colsBrowseDatabase, + mpd_state->colsBrowsePlaylistsDetail, + mpd_state->colsBrowseFilesystem, + mpd_state->colsPlayback, + mpd_state->colsQueueLastPlayed ); CHECK_RETURN_LEN(); } - -int mpd_client_put_outputs(char *buffer) { +static int mpd_client_put_outputs(t_config *config, t_mpd_state *mpd_state, char *buffer) { struct mpd_output *output; - int len; + size_t len; int nr; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (!mpd_send_outputs(mpd.conn)) + if (!mpd_send_outputs(mpd_state->conn)) RETURN_ERROR_AND_RECOVER("outputs"); len = json_printf(&out, "{type: outputs, data: {outputs: ["); nr = 0; - while ((output = mpd_recv_output(mpd.conn)) != NULL) { + while ((output = mpd_recv_output(mpd_state->conn)) != NULL) { if (nr++) len += json_printf(&out, ","); - len += json_printf(&out,"{id: %d, name: %Q, state: %d}", + len += json_printf(&out, "{id: %d, name: %Q, state: %d}", mpd_output_get_id(output), mpd_output_get_name(output), mpd_output_get_enabled(output) ); mpd_output_free(output); } - if (!mpd_response_finish(mpd.conn)) + if (!mpd_response_finish(mpd_state->conn)) LOG_ERROR_AND_RECOVER("outputs"); len += json_printf(&out,"]}}"); @@ -1720,22 +1890,12 @@ int mpd_client_put_outputs(char *buffer) { CHECK_RETURN_LEN(); } -int replacechar(char *str, char orig, char rep) { - char *ix = str; - int n = 0; - while ((ix = strchr(ix, orig)) != NULL) { - *ix++ = rep; - n++; - } - return n; -} - -int mpd_client_get_cover(const char *uri, char *cover, int cover_len) { +static int mpd_client_get_cover(t_config *config, t_mpd_state *mpd_state, const char *uri, char *cover, const int cover_len) { char *orgpath = strdup(uri); char *path = orgpath; - int len; + size_t len = 0; - if (!config.coverimage) { + if (!config->coverimage) { len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png"); } else if (strncasecmp("http:", path, 5) == 0 || strncasecmp("https:", path, 6) == 0) { @@ -1755,13 +1915,13 @@ int mpd_client_get_cover(const char *uri, char *cover, int cover_len) { len = snprintf(cover, cover_len, "/assets/coverimage-httpstream.png"); } else { - if (mpd.feat_library) { + if (mpd_state->feat_library) { dirname(path); - snprintf(cover, cover_len, "%s/library/%s/%s", DOC_ROOT, path, config.coverimagename); + snprintf(cover, cover_len, "%s/library/%s/%s", DOC_ROOT, path, config->coverimagename); if (access(cover, F_OK ) == -1 ) len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png"); else - len = snprintf(cover, cover_len, "/library/%s/%s", path, config.coverimagename); + len = snprintf(cover, cover_len, "/library/%s/%s", path, config->coverimagename); } else len = snprintf(cover, cover_len, "/assets/coverimage-notavailable.png"); } @@ -1769,35 +1929,34 @@ int mpd_client_get_cover(const char *uri, char *cover, int cover_len) { return len; } -int mpd_client_put_current_song(char *buffer) { - struct mpd_song *song; - int len; +static int mpd_client_put_current_song(t_config *config, t_mpd_state *mpd_state, char *buffer) { + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); char cover[500] = ""; - song = mpd_run_current_song(mpd.conn); + struct mpd_song *song = mpd_run_current_song(mpd_state->conn); if (song == NULL) { len = json_printf(&out, "{type: result, data: ok}"); return len; } - mpd_client_get_cover(mpd_song_get_uri(song), cover, 500); + mpd_client_get_cover(config, mpd_state, mpd_song_get_uri(song), cover, 500); len = json_printf(&out, "{type: song_change, data: {pos: %d, currentSongId: %d, cover: %Q, ", mpd_song_get_pos(song), - mpd.song_id, + mpd_state->song_id, cover ); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); - if (config.stickers) { + if (config->stickers) { t_sticker *sticker = (t_sticker *) malloc(sizeof(t_sticker)); - mpd_client_get_sticker(mpd_song_get_uri(song), sticker); + mpd_client_get_sticker(config, mpd_state, mpd_song_get_uri(song), sticker); len += json_printf(&out, ", playCount: %d, skipCount: %d, like: %d, lastPlayed: %d", sticker->playCount, sticker->skipCount, @@ -1812,31 +1971,31 @@ int mpd_client_put_current_song(char *buffer) { CHECK_RETURN_LEN(); } -int mpd_client_put_songdetails(char *buffer, char *uri) { +static int mpd_client_put_songdetails(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *uri) { struct mpd_entity *entity; const struct mpd_song *song; - int len; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); char cover[500] = ""; len = json_printf(&out, "{type: song_details, data: {"); - if (!mpd_send_list_all_meta(mpd.conn, uri)) + if (!mpd_send_list_all_meta(mpd_state->conn, uri)) RETURN_ERROR_AND_RECOVER("mpd_send_list_all_meta"); - if ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + if ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { song = mpd_entity_get_song(entity); - mpd_client_get_cover(uri, cover, 500); + mpd_client_get_cover(config, mpd_state, uri, cover, 500); len += json_printf(&out, "cover: %Q, ", cover); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); mpd_entity_free(entity); } - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); - if (config.stickers) { + if (config->stickers) { t_sticker *sticker = (t_sticker *) malloc(sizeof(t_sticker)); - mpd_client_get_sticker(uri, sticker); + mpd_client_get_sticker(config, mpd_state, uri, sticker); len += json_printf(&out, ", playCount: %d, skipCount: %d, like: %d, lastPlayed: %d", sticker->playCount, sticker->skipCount, @@ -1850,33 +2009,33 @@ int mpd_client_put_songdetails(char *buffer, char *uri) { CHECK_RETURN_LEN(); } -int mpd_client_put_last_played_songs(char *buffer, unsigned int offset) { +static int mpd_client_put_last_played_songs(t_config *config, t_mpd_state *mpd_state, char *buffer, unsigned int offset) { const struct mpd_song *song; struct mpd_entity *entity; - int len; - unsigned int entity_count = 0; - unsigned int entities_returned = 0; + size_t len = 0; + unsigned entity_count = 0; + unsigned entities_returned = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); len = json_printf(&out, "{type: last_played_songs, data: ["); - struct node *current = last_played.list; + struct node *current = mpd_state->last_played.list; while (current != NULL) { entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { if (entities_returned++) len += json_printf(&out, ","); len += json_printf(&out, "{Pos: %d, LastPlayed: %ld, ", entity_count, current->value); - if (!mpd_send_list_all_meta(mpd.conn, current->data)) + if (!mpd_send_list_all_meta(mpd_state->conn, current->data)) RETURN_ERROR_AND_RECOVER("mpd_send_list_all_meta"); - if ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + if ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { song = mpd_entity_get_song(entity); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); mpd_entity_free(entity); - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); } len += json_printf(&out, "}"); } @@ -1892,24 +2051,24 @@ int mpd_client_put_last_played_songs(char *buffer, unsigned int offset) { CHECK_RETURN_LEN(); } -int mpd_client_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length) { +static int mpd_client_put_queue(t_config *config, t_mpd_state *mpd_state, char *buffer, unsigned int offset) { struct mpd_entity *entity; - unsigned long totalTime = 0; - unsigned long entity_count = 0; - unsigned long entities_returned = 0; - int len; + int totalTime = 0; + unsigned entity_count = 0; + unsigned entities_returned = 0; + size_t len; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - struct mpd_status *status = mpd_run_status(mpd.conn); + struct mpd_status *status = mpd_run_status(mpd_state->conn); if (!status) RETURN_ERROR_AND_RECOVER("mpd_run_status"); - if (!mpd_send_list_queue_range_meta(mpd.conn, offset, offset + config.max_elements_per_page)) + if (!mpd_send_list_queue_range_meta(mpd_state->conn, offset, offset + config->max_elements_per_page)) RETURN_ERROR_AND_RECOVER("mpd_send_list_queue_meta"); len = json_printf(&out, "{type: queue, data: ["); - while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + while ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { const struct mpd_song *song; unsigned int drtn; if (mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) { @@ -1923,7 +2082,7 @@ int mpd_client_put_queue(char *buffer, unsigned int offset, unsigned *queue_vers mpd_song_get_id(song), mpd_song_get_pos(song) ); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); @@ -1940,34 +2099,34 @@ int mpd_client_put_queue(char *buffer, unsigned int offset, unsigned *queue_vers mpd_status_get_queue_version(status) ); - *queue_version = mpd_status_get_queue_version(status); - *queue_length = mpd_status_get_queue_length(status); + mpd_state->queue_version = mpd_status_get_queue_version(status); + mpd_state->queue_length = mpd_status_get_queue_length(status); mpd_status_free(status); CHECK_RETURN_LEN(); } -int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *filter) { +static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *path, const unsigned int offset, const char *filter) { struct mpd_entity *entity; const struct mpd_playlist *pl; - unsigned int entity_count = 0; - unsigned int entities_returned = 0; + unsigned entity_count = 0; + unsigned entities_returned = 0; const char *entityName; char smartpls_file[400]; bool smartpls; - int len; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (!mpd_send_list_meta(mpd.conn, path)) + if (!mpd_send_list_meta(mpd_state->conn, path)) RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); len = json_printf(&out, "{type: browse, data: ["); - while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + while ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { const struct mpd_song *song; const struct mpd_directory *dir; entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { switch (mpd_entity_get_type(entity)) { case MPD_ENTITY_TYPE_UNKNOWN: entity_count--; @@ -1981,7 +2140,7 @@ int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *f if (entities_returned++) len += json_printf(&out, ","); len += json_printf(&out, "{Type: song, "); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); @@ -2026,16 +2185,23 @@ int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *f plName = strdup(entityName); } if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, plName, 1) == 0 || - (strncmp(filter, "0", 1) == 0 && isalpha(*plName) == 0 ) - ) { - if (entities_returned++) + (strncmp(filter, "0", 1) == 0 && isalpha(*plName) == 0 )) + { + if (entities_returned++) { len += json_printf(&out, ","); - sanitize_string(plName); - snprintf(smartpls_file, 400, "%s/smartpls/%s", config.varlibdir, plName); - if (access(smartpls_file, F_OK ) != -1) - smartpls = true; - else - smartpls = false; + } + if (validate_string(plName) == true) { + snprintf(smartpls_file, 400, "%s/smartpls/%s", config->varlibdir, plName); + if (access(smartpls_file, F_OK ) != -1) { + smartpls = true; + } + else { + smartpls = false; + } + } + else { + smartpls = false; + } len += json_printf(&out, "{Type: %Q, uri: %Q, name: %Q}", (smartpls == true ? "smartpls" : "plist"), entityName, @@ -2050,9 +2216,9 @@ int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *f mpd_entity_free(entity); } - if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) { - printf("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(mpd.conn)); - mpd.conn_state = MPD_FAILURE; + if (mpd_connection_get_error(mpd_state->conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd_state->conn)) { + printf("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(mpd_state->conn)); + mpd_state->conn_state = MPD_FAILURE; return 0; } @@ -2066,28 +2232,28 @@ int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *f CHECK_RETURN_LEN(); } -int mpd_client_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, char *mpdsearchtagtype, char *searchstr, char *filter) { +static int mpd_client_put_db_tag(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset, const char *mpdtagtype, const char *mpdsearchtagtype, const char *searchstr, const char *filter) { struct mpd_pair *pair; - unsigned long entity_count = 0; - unsigned long entities_returned = 0; - int len; + unsigned entity_count = 0; + unsigned entities_returned = 0; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (mpd_search_db_tags(mpd.conn, mpd_tag_name_parse(mpdtagtype)) == false) + if (mpd_search_db_tags(mpd_state->conn, mpd_tag_name_parse(mpdtagtype)) == false) RETURN_ERROR_AND_RECOVER("mpd_search_db_tags"); if (mpd_tag_name_parse(mpdsearchtagtype) != MPD_TAG_UNKNOWN) { - if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdsearchtagtype), searchstr) == false) + if (mpd_search_add_tag_constraint(mpd_state->conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdsearchtagtype), searchstr) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); } - if (mpd_search_commit(mpd.conn) == false) + if (mpd_search_commit(mpd_state->conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); len = json_printf(&out, "{type: listDBtags, data: ["); - while ((pair = mpd_recv_pair_tag(mpd.conn, mpd_tag_name_parse(mpdtagtype))) != NULL) { + while ((pair = mpd_recv_pair_tag(mpd_state->conn, mpd_tag_name_parse(mpdtagtype))) != NULL) { entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { if (strcmp(pair->value, "") == 0) { entity_count--; } @@ -2104,7 +2270,7 @@ int mpd_client_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, c entity_count--; } } - mpd_return_pair(mpd.conn, pair); + mpd_return_pair(mpd_state->conn, pair); } len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, " @@ -2121,36 +2287,36 @@ int mpd_client_put_db_tag(char *buffer, unsigned int offset, char *mpdtagtype, c CHECK_RETURN_LEN(); } -int mpd_client_put_songs_in_album(char *buffer, char *album, char *search, char *tag) { +static int mpd_client_put_songs_in_album(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *album, const char *search, const char *tag) { struct mpd_song *song; unsigned long entity_count = 0; unsigned long entities_returned = 0; - int len; + size_t len = 0; char cover[500] = ""; char *albumartist = NULL; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (mpd_search_db_songs(mpd.conn, true) == false) + if (mpd_search_db_songs(mpd_state->conn, true) == false) RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); - if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(tag), search) == false) + if (mpd_search_add_tag_constraint(mpd_state->conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(tag), search) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); - if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album) == false) + if (mpd_search_add_tag_constraint(mpd_state->conn, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); - if (mpd_search_commit(mpd.conn) == false) + if (mpd_search_commit(mpd_state->conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); else { len = json_printf(&out, "{type: listTitles, data: ["); - while ((song = mpd_recv_song(mpd.conn)) != NULL) { + while ((song = mpd_recv_song(mpd_state->conn)) != NULL) { entity_count++; - if (entity_count <= config.max_elements_per_page) { + if (entity_count <= config->max_elements_per_page) { if (entities_returned++) len += json_printf(&out, ", "); else { - mpd_client_get_cover(mpd_song_get_uri(song), cover, 500); + mpd_client_get_cover(config, mpd_state, mpd_song_get_uri(song), cover, 500); albumartist = strdup(mpd_client_get_tag(song, MPD_TAG_ALBUM_ARTIST)); } len += json_printf(&out, "{Type: song, "); @@ -2176,36 +2342,42 @@ int mpd_client_put_songs_in_album(char *buffer, char *album, char *search, char CHECK_RETURN_LEN(); } -int mpd_client_put_playlists(char *buffer, unsigned int offset, char *filter) { +static int mpd_client_put_playlists(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset, const char *filter) { struct mpd_playlist *pl; - unsigned int entity_count = 0; - unsigned int entities_returned = 0; + unsigned entity_count = 0; + unsigned entities_returned = 0; const char *plpath; - int len; + size_t len = 0; bool smartpls; char smartpls_file[400]; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (!mpd_send_list_playlists(mpd.conn)) + if (mpd_send_list_playlists(mpd_state->conn) == false) RETURN_ERROR_AND_RECOVER("mpd_send_lists_playlists"); len = json_printf(&out, "{type: playlists, data: ["); - while ((pl = mpd_recv_playlist(mpd.conn)) != NULL) { + while ((pl = mpd_recv_playlist(mpd_state->conn)) != NULL) { entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { plpath = mpd_playlist_get_path(pl); if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, plpath, 1) == 0 || - (strncmp(filter, "0", 1) == 0 && isalpha(*plpath) == 0 ) - ) { + (strncmp(filter, "0", 1) == 0 && isalpha(*plpath) == 0 )) + { if (entities_returned++) len += json_printf(&out, ", "); - snprintf(smartpls_file, 400, "%s/smartpls/%s", config.varlibdir, plpath); - sanitize_string(plpath); - if (access(smartpls_file, F_OK ) != -1) - smartpls = true; - else + snprintf(smartpls_file, 400, "%s/smartpls/%s", config->varlibdir, plpath); + if (validate_string(plpath) == true) { + if (access(smartpls_file, F_OK ) != -1) { + smartpls = true; + } + else { + smartpls = false; + } + } + else { smartpls = false; + } len += json_printf(&out, "{Type: %Q, uri: %Q, name: %Q, last_modified: %d}", (smartpls == true ? "smartpls" : "plist"), plpath, @@ -2228,51 +2400,57 @@ int mpd_client_put_playlists(char *buffer, unsigned int offset, char *filter) { CHECK_RETURN_LEN(); } -int mpd_client_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter) { +static int mpd_client_put_playlist_list(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *uri, const unsigned int offset, const char *filter) { struct mpd_entity *entity; - unsigned int entity_count = 0; - unsigned int entities_returned = 0; + unsigned entity_count = 0; + unsigned entities_returned = 0; const char *entityName; char smartpls_file[400]; bool smartpls; - int len; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (!mpd_send_list_playlist_meta(mpd.conn, uri)) + if (mpd_send_list_playlist_meta(mpd_state->conn, uri) == false) RETURN_ERROR_AND_RECOVER("mpd_send_list_meta"); len = json_printf(&out, "{type: playlist_detail, data: ["); - while ((entity = mpd_recv_entity(mpd.conn)) != NULL) { + while ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { const struct mpd_song *song; entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { song = mpd_entity_get_song(entity); entityName = mpd_client_get_tag(song, MPD_TAG_TITLE); if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, entityName, 1) == 0 || - (strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 ) - ) { + (strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0)) + { if (entities_returned++) len += json_printf(&out, ","); len += json_printf(&out, "{Type: song, "); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); len += json_printf(&out, ", Pos: %d", entity_count); len += json_printf(&out, "}"); - } else { + } + else { entity_count--; } } mpd_entity_free(entity); } - sanitize_string(uri); - snprintf(smartpls_file, 400, "%s/smartpls/%s", config.varlibdir, uri); - if (access(smartpls_file, F_OK ) != -1) - smartpls = true; - else - smartpls = false; + + smartpls = false; + if (validate_string(uri) == true) { + snprintf(smartpls_file, 400, "%s/smartpls/%s", config->varlibdir, uri); + if (access(smartpls_file, F_OK ) != -1) { + smartpls = true; + } + else { + smartpls = false; + } + } len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, filter: %Q, uri: %Q, smartpls: %B}", entity_count, offset, @@ -2285,35 +2463,35 @@ int mpd_client_put_playlist_list(char *buffer, char *uri, unsigned int offset, c CHECK_RETURN_LEN(); } -int mpd_client_search(char *buffer, char *searchstr, char *filter, char *plist, unsigned int offset) { +static int mpd_client_search(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *searchstr, const char *filter, const char *plist, const unsigned int offset) { struct mpd_song *song; - unsigned long entity_count = 0; - unsigned long entities_returned = 0; - int len = 0; + unsigned entity_count = 0; + unsigned entities_returned = 0; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); if (strcmp(plist, "") == 0) { - if (mpd_send_command(mpd.conn, "search", filter, searchstr, NULL) == false) + if (mpd_send_command(mpd_state->conn, "search", filter, searchstr, NULL) == false) RETURN_ERROR_AND_RECOVER("mpd_search"); len = json_printf(&out, "{type: search, data: ["); } else if (strcmp(plist, "queue") == 0) { - if (mpd_send_command(mpd.conn, "searchadd", filter, searchstr, NULL) == false) + if (mpd_send_command(mpd_state->conn, "searchadd", filter, searchstr, NULL) == false) RETURN_ERROR_AND_RECOVER("mpd_searchadd"); } else { - if (mpd_send_command(mpd.conn, "searchaddpl", plist, filter, searchstr, NULL) == false) + if (mpd_send_command(mpd_state->conn, "searchaddpl", plist, filter, searchstr, NULL) == false) RETURN_ERROR_AND_RECOVER("mpd_searchaddpl"); } if (strcmp(plist, "") == 0) { - while ((song = mpd_recv_song(mpd.conn)) != NULL) { + while ((song = mpd_recv_song(mpd_state->conn)) != NULL) { entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { if (entities_returned++) len += json_printf(&out, ", "); len += json_printf(&out, "{Type: song, "); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); @@ -2323,7 +2501,7 @@ int mpd_client_search(char *buffer, char *searchstr, char *filter, char *plist, } } else - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); if (strcmp(plist, "") == 0) { len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, searchstr: %Q}", @@ -2340,8 +2518,8 @@ int mpd_client_search(char *buffer, char *searchstr, char *filter, char *plist, } -int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, char *grouptag, char *plist, unsigned int offset) { - int len = 0; +static int mpd_client_search_adv(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *expression, const char *sort, const bool sortdesc, const char *grouptag, const char *plist, const unsigned int offset) { + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); #if LIBMPDCLIENT_CHECK_VERSION(2, 17, 0) struct mpd_song *song; @@ -2349,44 +2527,44 @@ int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortd unsigned long entities_returned = 0; if (strcmp(plist, "") == 0) { - if (mpd_search_db_songs(mpd.conn, false) == false) + if (mpd_search_db_songs(mpd_state->conn, false) == false) RETURN_ERROR_AND_RECOVER("mpd_search_db_songs"); len = json_printf(&out, "{type: search, data: ["); } else if (strcmp(plist, "queue") == 0) { - if (mpd_search_add_db_songs(mpd.conn, false) == false) + if (mpd_search_add_db_songs(mpd_state->conn, false) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs"); } else { - if (mpd_search_add_db_songs_to_playlist(mpd.conn, plist) == false) + if (mpd_search_add_db_songs_to_playlist(mpd_state->conn, plist) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_db_songs_to_playlist"); } - if (mpd_search_add_expression(mpd.conn, expression) == false) + if (mpd_search_add_expression(mpd_state->conn, expression) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_expression"); if (strcmp(plist, "") == 0) { - if (sort != NULL && strcmp(sort, "") != 0 && mpd.feat_tags == true) { - if (mpd_search_add_sort_name(mpd.conn, sort, sortdesc) == false) + if (sort != NULL && strcmp(sort, "") != 0 && mpd_state->feat_tags == true) { + if (mpd_search_add_sort_name(mpd_state->conn, sort, sortdesc) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_sort_name"); } - if (grouptag != NULL && strcmp(grouptag, "") != 0 && mpd.feat_tags == true) { - if (mpd_search_add_group_tag(mpd.conn, mpd_tag_name_parse(grouptag)) == false) + if (grouptag != NULL && strcmp(grouptag, "") != 0 && mpd_state->feat_tags == true) { + if (mpd_search_add_group_tag(mpd_state->conn, mpd_tag_name_parse(grouptag)) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_group_tag"); } - if (mpd_search_add_window(mpd.conn, offset, offset + config.max_elements_per_page) == false) + if (mpd_search_add_window(mpd_state->conn, offset, offset + config->max_elements_per_page) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_window"); } - if (mpd_search_commit(mpd.conn) == false) + if (mpd_search_commit(mpd_state->conn) == false) RETURN_ERROR_AND_RECOVER("mpd_search_commit"); if (strcmp(plist, "") == 0) { - while ((song = mpd_recv_song(mpd.conn)) != NULL) { + while ((song = mpd_recv_song(mpd_state->conn)) != NULL) { if (entities_returned++) len += json_printf(&out, ", "); len += json_printf(&out, "{Type: song, "); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); @@ -2394,8 +2572,9 @@ int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortd mpd_song_free(song); } } - else - mpd_response_finish(mpd.conn); + else { + mpd_response_finish(mpd_state->conn); + } if (strcmp(plist, "") == 0) { len += json_printf(&out, "], totalEntities: %d, offset: %d, returnedEntities: %d, expression: %Q, " @@ -2409,32 +2588,33 @@ int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortd grouptag ); } - else + else { len = json_printf(&out, "{type: result, data: ok}"); + } #else len = json_printf(&out, "{type: error, data: %Q}", "Advanced search is disabled."); #endif CHECK_RETURN_LEN(); } -int mpd_client_queue_crop(char *buffer) { - int len = 0; +static int mpd_client_queue_crop(t_config *config, t_mpd_state *mpd_state, char *buffer) { + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - struct mpd_status *status = mpd_run_status(mpd.conn); - int length = mpd_status_get_queue_length(status) - 1; + struct mpd_status *status = mpd_run_status(mpd_state->conn); + const int length = mpd_status_get_queue_length(status) - 1; int playing_song_pos = mpd_status_get_song_pos(status); - if (length < 0) { + if (length < 1) { len = json_printf(&out, "{type: error, data: %Q}", "A playlist longer than 1 song in length is required to crop."); printf("A playlist longer than 1 song in length is required to crop.\n"); } else if (mpd_status_get_state(status) == MPD_STATE_PLAY || mpd_status_get_state(status) == MPD_STATE_PAUSE) { playing_song_pos++; if (playing_song_pos < length) - mpd_run_delete_range(mpd.conn, playing_song_pos, -1); + mpd_run_delete_range(mpd_state->conn, playing_song_pos, -1); playing_song_pos--; if (playing_song_pos > 0 ) - mpd_run_delete_range(mpd.conn, 0, playing_song_pos--); + mpd_run_delete_range(mpd_state->conn, 0, playing_song_pos--); len = json_printf(&out, "{type: result, data: ok}"); } else { len = json_printf(&out, "{type: error, data: %Q}", "You need to be playing to crop the playlist"); @@ -2446,41 +2626,42 @@ int mpd_client_queue_crop(char *buffer) { CHECK_RETURN_LEN(); } -int mpd_client_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr) { +static int mpd_client_search_queue(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *mpdtagtype, const unsigned int offset, const char *searchstr) { struct mpd_song *song; - unsigned long entity_count = 0; - unsigned long entities_returned = 0; - int len; + unsigned entity_count = 0; + unsigned entities_returned = 0; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - if (mpd_search_queue_songs(mpd.conn, false) == false) { + if (mpd_search_queue_songs(mpd_state->conn, false) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_queue_songs"); } if (mpd_tag_name_parse(mpdtagtype) != MPD_TAG_UNKNOWN) { - if (mpd_search_add_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false) + if (mpd_search_add_tag_constraint(mpd_state->conn, MPD_OPERATOR_DEFAULT, mpd_tag_name_parse(mpdtagtype), searchstr) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_tag_constraint"); } else { - if (mpd_search_add_any_tag_constraint(mpd.conn, MPD_OPERATOR_DEFAULT, searchstr) == false) + if (mpd_search_add_any_tag_constraint(mpd_state->conn, MPD_OPERATOR_DEFAULT, searchstr) == false) RETURN_ERROR_AND_RECOVER("mpd_search_add_any_tag_constraint"); } - if (mpd_search_commit(mpd.conn) == false) + if (mpd_search_commit(mpd_state->conn) == false) { RETURN_ERROR_AND_RECOVER("mpd_search_commit"); + } else { len = json_printf(&out, "{type: queuesearch, data: ["); - while ((song = mpd_recv_song(mpd.conn)) != NULL) { + while ((song = mpd_recv_song(mpd_state->conn)) != NULL) { entity_count++; - if (entity_count > offset && entity_count <= offset + config.max_elements_per_page) { + if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { if (entities_returned++) len += json_printf(&out, ", "); len += json_printf(&out, "{type: song, id: %d, Pos: %d, ", mpd_song_get_id(song), mpd_song_get_pos(song) ); - if (mpd.feat_tags == true) + if (mpd_state->feat_tags == true) PUT_SONG_TAGS(); else PUT_MIN_SONG_TAGS(); @@ -2500,18 +2681,19 @@ int mpd_client_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, CHECK_RETURN_LEN(); } -int mpd_client_put_stats(char *buffer) { - struct mpd_stats *stats = mpd_run_stats(mpd.conn); - const unsigned *version = mpd_connection_get_server_version(mpd.conn); - int len; +static int mpd_client_put_stats(t_config *config, t_mpd_state *mpd_state, char *buffer) { + struct mpd_stats *stats = mpd_run_stats(mpd_state->conn); + const unsigned *version = mpd_connection_get_server_version(mpd_state->conn); + size_t len = 0; char mpd_version[20]; char libmpdclient_version[20]; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); snprintf(mpd_version, 20, "%u.%u.%u", version[0], version[1], version[2]); snprintf(libmpdclient_version, 20, "%i.%i.%i", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); - if (stats == NULL) + if (stats == NULL) { RETURN_ERROR_AND_RECOVER("mpd_run_stats"); + } len = json_printf(&out, "{type: mpdstats, data: {artists: %d, albums: %d, songs: %d, " "playtime: %d, uptime: %d, dbUpdated: %d, dbPlaytime: %d, mympdVersion: %Q, mpdVersion: %Q, " "libmpdclientVersion: %Q}}", @@ -2531,21 +2713,24 @@ int mpd_client_put_stats(char *buffer) { CHECK_RETURN_LEN(); } -void mpd_client_disconnect() { - mpd.conn_state = MPD_DISCONNECT; - mpd_client_idle(100); +static void mpd_client_disconnect(t_config *config, t_mpd_state *mpd_state) { + mpd_state->conn_state = MPD_DISCONNECT; + mpd_client_idle(config, mpd_state, 100); } -int mpd_client_smartpls_put(char *buffer, char *playlist) { +static int mpd_client_smartpls_put(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *playlist) { char pl_file[400]; char *smartpltype; char *p_charbuf1, *p_charbuf2; int je, int_buf1; - int len = 0; + size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); - sanitize_string(playlist); - snprintf(pl_file, 400, "%s/smartpls/%s", config.varlibdir, playlist); + if (validate_string(playlist) == false) { + len = json_printf(&out, "{type: error, data: %Q}}", "Can't read smart playlist file"); + return len; + } + snprintf(pl_file, 400, "%s/smartpls/%s", config->varlibdir, playlist); char *content = json_fread(pl_file); if (content == NULL) { len = json_printf(&out, "{type: error, data: %Q}}", "Can't read smart playlist file"); @@ -2606,58 +2791,60 @@ int mpd_client_smartpls_put(char *buffer, char *playlist) { return len; } -int mpd_client_smartpls_save(char *smartpltype, char *playlist, char *tag, char *searchstr, int maxentries, int timerange) { +static bool mpd_client_smartpls_save(t_config *config, t_mpd_state *mpd_state, const char *smartpltype, const char *playlist, const char *tag, const char *searchstr, const int maxentries, const int timerange) { char tmp_file[400]; char pl_file[400]; - sanitize_string(playlist); - snprintf(tmp_file, 400, "%s/tmp/%s", config.varlibdir, playlist); - snprintf(pl_file, 400, "%s/smartpls/%s", config.varlibdir, playlist); + if (validate_string(playlist) == false) { + return false; + } + snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, playlist); + snprintf(pl_file, 400, "%s/smartpls/%s", config->varlibdir, playlist); if (strcmp(smartpltype, "sticker") == 0) { if (json_fprintf(tmp_file, "{type: %Q, sticker: %Q, maxentries: %d}", smartpltype, tag, maxentries) == -1) { printf("Error creating file %s\n", tmp_file); - return 1; + return false; } else if (rename(tmp_file, pl_file) == -1) { printf("Error renaming file from %s to %s\n", tmp_file, pl_file); - return 1; + return false; } - else if (mpd_client_smartpls_update_sticker(playlist, tag, maxentries) == 1) { + else if (mpd_client_smartpls_update_sticker(config, mpd_state, playlist, tag, maxentries) == 1) { printf("Update of smart playlist %s failed.\n", playlist); - return 1; + return false; } } else if (strcmp(smartpltype, "newest") == 0) { if (json_fprintf(tmp_file, "{type: %Q, timerange: %d}", smartpltype, timerange) == -1) { printf("Error creating file %s\n", tmp_file); - return 1; + return false; } else if (rename(tmp_file, pl_file) == -1) { printf("Error renaming file from %s to %s\n", tmp_file, pl_file); - return 1; + return false; } - else if (mpd_client_smartpls_update_newest(playlist, timerange) == 1) { + else if (mpd_client_smartpls_update_newest(config, mpd_state, playlist, timerange) == 1) { printf("Update of smart playlist %s failed.\n", playlist); - return 1; + return false; } } else if (strcmp(smartpltype, "search") == 0) { if (json_fprintf(tmp_file, "{type: %Q, tag: %Q, searchstr: %Q}", smartpltype, tag, searchstr) == -1) { printf("Error creating file %s\n", tmp_file); - return 1; + return false; } else if (rename(tmp_file, pl_file) == -1) { printf("Error renaming file from %s to %s\n", tmp_file, pl_file); - return 1; + return false; } - else if (mpd_client_smartpls_update_search(playlist, tag, searchstr) == 1) { + else if (mpd_client_smartpls_update_search(config, mpd_state, playlist, tag, searchstr) == 1) { printf("Update of smart playlist %s failed.\n", playlist); - return 1; + return false; } } - return 0; + return true; } -int mpd_client_smartpls_update_all() { +static bool mpd_client_smartpls_update_all(t_config *config, t_mpd_state *mpd_state) { DIR *dir; struct dirent *ent; char *smartpltype; @@ -2667,15 +2854,16 @@ int mpd_client_smartpls_update_all() { char *p_charbuf1, *p_charbuf2; int int_buf1; - if (!config.smartpls) - return 0; + if (config->smartpls == false) { + return true; + } - snprintf(dirname, 400, "%s/smartpls", config.varlibdir); + snprintf(dirname, 400, "%s/smartpls", config->varlibdir); if ((dir = opendir (dirname)) != NULL) { while ((ent = readdir(dir)) != NULL) { if (strncmp(ent->d_name, ".", 1) == 0) continue; - snprintf(filename, 400, "%s/smartpls/%s", config.varlibdir, ent->d_name); + snprintf(filename, 400, "%s/smartpls/%s", config->varlibdir, ent->d_name); char *content = json_fread(filename); if (content == NULL) { printf("Cant read smart playlist file %s\n", filename); @@ -2687,7 +2875,7 @@ int mpd_client_smartpls_update_all() { if (strcmp(smartpltype, "sticker") == 0) { je = json_scanf(content, strlen(content), "{sticker: %Q, maxentries: %d}", &p_charbuf1, &int_buf1); if (je == 2) { - mpd_client_smartpls_update_sticker(ent->d_name, p_charbuf1, int_buf1); + mpd_client_smartpls_update_sticker(config, mpd_state, ent->d_name, p_charbuf1, int_buf1); free(p_charbuf1); } else @@ -2696,7 +2884,7 @@ int mpd_client_smartpls_update_all() { else if (strcmp(smartpltype, "newest") == 0) { je = json_scanf(content, strlen(content), "{timerange: %d}", &int_buf1); if (je == 1) { - mpd_client_smartpls_update_newest(ent->d_name, int_buf1); + mpd_client_smartpls_update_newest(config, mpd_state, ent->d_name, int_buf1); } else printf("Can't parse smart playlist file %s\n", filename); @@ -2704,7 +2892,7 @@ int mpd_client_smartpls_update_all() { else if (strcmp(smartpltype, "search") == 0) { je = json_scanf(content, strlen(content), "{tag: %Q, searchstr: %Q}", &p_charbuf1, &p_charbuf2); if (je == 2) { - mpd_client_smartpls_update_search(ent->d_name, p_charbuf1, p_charbuf2); + mpd_client_smartpls_update_search(config, mpd_state, ent->d_name, p_charbuf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -2717,49 +2905,53 @@ int mpd_client_smartpls_update_all() { closedir (dir); } else { printf("Can't open smart playlist directory %s\n", dirname); - return 1; + return false; } - return 0; + return true; } -int mpd_client_smartpls_clear(char *playlist) { +static bool mpd_client_smartpls_clear(t_config *config, t_mpd_state *mpd_state, const char *playlist) { struct mpd_playlist *pl; const char *plpath; bool exists = false; - if (!mpd_send_list_playlists(mpd.conn)) { + if (mpd_send_list_playlists(mpd_state->conn) == false) { LOG_ERROR_AND_RECOVER("mpd_send_list_playlists"); return 1; } - while ((pl = mpd_recv_playlist(mpd.conn)) != NULL) { + while ((pl = mpd_recv_playlist(mpd_state->conn)) != NULL) { plpath = mpd_playlist_get_path(pl); if (strcmp(playlist, plpath) == 0) exists = true; mpd_playlist_free(pl); - if (exists) + if (exists == true) { break; - } - mpd_response_finish(mpd.conn); - - if (exists) { - if (!mpd_run_rm(mpd.conn, playlist)) { - LOG_ERROR_AND_RECOVER("mpd_run_rm"); - return 1; } } - return 0; + mpd_response_finish(mpd_state->conn); + + if (exists) { + if (mpd_run_rm(mpd_state->conn, playlist) == false) { + LOG_ERROR_AND_RECOVER("mpd_run_rm"); + return false; + } + } + return true; } -int mpd_client_smartpls_update_search(char *playlist, char *tag, char *searchstr) { - mpd_client_smartpls_clear(playlist); - if (mpd.feat_advsearch == true && strcmp(tag, "expression") == 0) - mpd_client_search_adv(mpd.buf, searchstr, NULL, true, NULL, playlist, 0); - else - mpd_client_search(mpd.buf, searchstr, tag, playlist, 0); +static bool mpd_client_smartpls_update_search(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *tag, const char *searchstr) { + char buffer[MAX_SIZE]; + mpd_client_smartpls_clear(config, mpd_state, playlist); + if (mpd_state->feat_advsearch == true && strcmp(tag, "expression") == 0) { + mpd_client_search_adv(config, mpd_state, buffer, searchstr, NULL, true, NULL, playlist, 0); + } + else { + mpd_client_search(config, mpd_state, buffer, searchstr, tag, playlist, 0); + } LOG_INFO() printf("Updated %s\n", playlist); - return 0; + return true; } -int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxentries) { +static bool mpd_client_smartpls_update_sticker(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *sticker, const int maxentries) { struct mpd_pair *pair; char *uri = NULL; const char *p_value; @@ -2769,15 +2961,15 @@ int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxent long i = 0; size_t j; - if (!mpd_send_sticker_find(mpd.conn, "song", "", sticker)) { + if (mpd_send_sticker_find(mpd_state->conn, "song", "", sticker) == false) { LOG_ERROR_AND_RECOVER("mpd_send_sticker_find"); - return 1; + return false; } struct list add_list; list_init(&add_list); - while ((pair = mpd_recv_pair(mpd.conn)) != NULL) { + while ((pair = mpd_recv_pair(mpd_state->conn)) != NULL) { if (strcmp(pair->name, "file") == 0) { uri = strdup(pair->value); } @@ -2785,28 +2977,31 @@ int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxent p_value = mpd_parse_sticker(pair->value, &j); if (p_value != NULL) { value = strtol(p_value, &crap, 10); - if (value >= 1) + if (value >= 1) { list_push(&add_list, uri, value); - if (value > value_max) + } + if (value > value_max) { value_max = value; + } } } - mpd_return_pair(mpd.conn, pair); + mpd_return_pair(mpd_state->conn, pair); } - mpd_response_finish(mpd.conn); + mpd_response_finish(mpd_state->conn); free(uri); - mpd_client_smartpls_clear(playlist); + mpd_client_smartpls_clear(config, mpd_state, playlist); - if (value_max > 2) + if (value_max > 2) { value_max = value_max / 2; + } list_sort_by_value(&add_list, false); struct node *current = add_list.list; while (current != NULL) { if (current->value >= value_max) { - if (!mpd_run_playlist_add(mpd.conn, playlist, current->data)) { + if (mpd_run_playlist_add(mpd_state->conn, playlist, current->data) == false) { LOG_ERROR_AND_RECOVER("mpd_run_playlist_add"); list_free(&add_list); return 1; @@ -2819,35 +3014,160 @@ int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxent } list_free(&add_list); LOG_INFO() printf("Updated %s with %ld songs, minValue: %ld\n", playlist, i, value_max); - return 0; + return true; } -int mpd_client_smartpls_update_newest(char *playlist, int timerange) { - unsigned long value_max = 0; +static bool mpd_client_smartpls_update_newest(t_config *config, t_mpd_state *mpd_state, const char *playlist, const int timerange) { + int value_max = 0; + char buffer[MAX_SIZE]; char searchstr[50]; - struct mpd_stats *stats = mpd_run_stats(mpd.conn); + struct mpd_stats *stats = mpd_run_stats(mpd_state->conn); if (stats != NULL) { value_max = mpd_stats_get_db_update_time(stats); mpd_stats_free(stats); } else { LOG_ERROR_AND_RECOVER("mpd_run_stats"); - return 1; + return false; } - mpd_client_smartpls_clear(playlist); + mpd_client_smartpls_clear(config, mpd_state, playlist); value_max -= timerange; if (value_max > 0) { - if (mpd.feat_advsearch == true) { - snprintf(searchstr, 50, "(modified-since '%lu')", value_max); - mpd_client_search_adv(mpd.buf, searchstr, NULL, true, NULL, playlist, 0); + if (mpd_state->feat_advsearch == true) { + snprintf(searchstr, 50, "(modified-since '%d')", value_max); + mpd_client_search_adv(config, mpd_state, buffer, searchstr, NULL, true, NULL, playlist, 0); } else { - snprintf(searchstr, 20, "%lu", value_max); - mpd_client_search(mpd.buf, searchstr, "modified-since", playlist, 0); + snprintf(searchstr, 20, "%d", value_max); + mpd_client_search(config, mpd_state, buffer, searchstr, "modified-since", playlist, 0); } LOG_INFO() printf("Updated %s\n", playlist); } - return 0; + return true; +} + +static void mpd_client_read_statefiles(t_config *config, t_mpd_state *mpd_state) { + char *crap; + char value[400]; + + LOG_INFO() printf("Reading states\n"); + if (mpd_client_state_get(config, mpd_state, "notificationWeb", value)) { + if (strcmp(value, "true") == 0) + mpd_state->notificationWeb = true; + else + mpd_state->notificationWeb = false; + } + else { + mpd_state->notificationWeb = false; + mpd_client_state_set(config, mpd_state, "notificationWeb", "false"); + } + + if (mpd_client_state_get(config, mpd_state, "notificationPage", value)) { + if (strcmp(value, "true") == 0) + mpd_state->notificationPage = true; + else + mpd_state->notificationPage = false; + } + else { + mpd_state->notificationPage = true; + mpd_client_state_set(config, mpd_state, "notificationPage", "true"); + } + + if (mpd_client_state_get(config, mpd_state, "jukeboxMode", value)) + mpd_state->jukeboxMode = strtol(value, &crap, 10); + else { + mpd_state->jukeboxMode = 0; + mpd_client_state_set(config, mpd_state, "jukeboxMode", "0"); + } + + if (mpd_client_state_get(config, mpd_state, "jukeboxPlaylist", value)) + mpd_state->jukeboxPlaylist = strdup(value); + else { + mpd_state->jukeboxPlaylist = strdup("Database"); + mpd_client_state_set(config, mpd_state, "jukeboxPlaylist", "Database"); + } + + if (mpd_client_state_get(config, mpd_state, "jukeboxQueueLength", value)) + mpd_state->jukeboxQueueLength = strtol(value, &crap, 10); + else { + mpd_state->jukeboxQueueLength = 1; + mpd_client_state_set(config, mpd_state, "jukeboxQueueLength", "1"); + } + + if (mpd_client_state_get(config, mpd_state, "colsQueueCurrent", value)) + mpd_state->colsQueueCurrent = strdup(value); + else { + mpd_state->colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mpd_client_state_set(config, mpd_state, "colsQueueCurrent", mpd_state->colsQueueCurrent); + } + + if (mpd_client_state_get(config, mpd_state, "colsSearch", value)) + mpd_state->colsSearch = strdup(value); + else { + mpd_state->colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mpd_client_state_set(config, mpd_state, "colsSearch", mpd_state->colsSearch); + } + + if (mpd_client_state_get(config, mpd_state, "colsBrowseDatabase", value)) + mpd_state->colsBrowseDatabase = strdup(value); + else { + mpd_state->colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]"); + mpd_client_state_set(config, mpd_state, "colsBrowseDatabase", mpd_state->colsBrowseDatabase); + } + + if (mpd_client_state_get(config, mpd_state, "colsBrowsePlaylistsDetail", value)) + mpd_state->colsBrowsePlaylistsDetail = strdup(value); + else { + mpd_state->colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mpd_client_state_set(config, mpd_state, "colsBrowsePlaylistsDetail", mpd_state->colsBrowsePlaylistsDetail); + } + + if (mpd_client_state_get(config, mpd_state, "colsBrowseFilesystem", value)) + mpd_state->colsBrowseFilesystem = strdup(value); + else { + mpd_state->colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mpd_client_state_set(config, mpd_state, "colsBrowseFilesystem", mpd_state->colsBrowseFilesystem); + } + + if (mpd_client_state_get(config, mpd_state, "colsPlayback", value)) + mpd_state->colsPlayback = strdup(value); + else { + mpd_state->colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); + mpd_client_state_set(config, mpd_state, "colsPlayback", mpd_state->colsPlayback); + } + + if (mpd_client_state_get(config, mpd_state, "colsQueueLastPlayed", value)) + mpd_state->colsQueueLastPlayed = strdup(value); + else { + mpd_state->colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); + mpd_client_state_set(config, mpd_state, "colsQueueLastPlayed", mpd_state->colsQueueLastPlayed); + } +} + +static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state) { + char cfgfile[400]; + char *line; + char *data; + char *crap; + size_t n = 0; + ssize_t read; + long value; + + snprintf(cfgfile, 400, "%s/state/last_played", config->varlibdir); + FILE *fp = fopen(cfgfile, "r"); + if (fp == NULL) { + printf("Error opening %s\n", cfgfile); + return 0; + } + while ((read = getline(&line, &n, fp)) > 0) { + value = strtol(line, &data, 10); + if (strlen(data) > 2) + data = data + 2; + strtok_r(data, "\n", &crap); + list_push(&mpd_state->last_played, data, value); + } + fclose(fp); + return mpd_state->last_played.length;; } diff --git a/src/mpd_client.h b/src/mpd_client.h index 9f6365b..ac6de0c 100644 --- a/src/mpd_client.h +++ b/src/mpd_client.h @@ -24,144 +24,6 @@ #ifndef __MPD_CLIENT_H__ #define __MPD_CLIENT_H__ +void *mpd_client_loop(void *arg_config); -#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)); \ - if (!mpd_connection_clear_error(mpd.conn)) \ - mpd.conn_state = MPD_FAILURE; \ - return len; \ -} while (0) - -#define LOG_ERROR_AND_RECOVER(X) do { \ - printf("MPD %s: %s\n", X, mpd_connection_get_error_message(mpd.conn)); \ - if (!mpd_connection_clear_error(mpd.conn)) \ - mpd.conn_state = MPD_FAILURE; \ -} while (0) - -#define PUT_SONG_TAGS() do { \ - struct node *current = mympd_tags.list; \ - int tagnr = 0; \ - while (current != NULL) { \ - if (tagnr ++) \ - len += json_printf(&out, ","); \ - len += json_printf(&out, "%Q: %Q", current->data, mpd_client_get_tag(song, mpd_tag_name_parse(current->data))); \ - current = current->next; \ - } \ - len += json_printf(&out, ", Duration: %d, uri: %Q", mpd_song_get_duration(song), mpd_song_get_uri(song)); \ -} while (0) - -#define PUT_MIN_SONG_TAGS() do { \ - len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", mpd_client_get_tag(song, MPD_TAG_TITLE), mpd_song_get_duration(song), mpd_song_get_uri(song)); \ -} while (0) - - -enum mpd_conn_states { - MPD_DISCONNECTED, - MPD_FAILURE, - MPD_CONNECTED, - MPD_RECONNECT, - MPD_DISCONNECT -}; - -struct t_mpd { - // Connection - struct mpd_connection *conn; - enum mpd_conn_states conn_state; - int timeout; - - // Reponse Buffer - char buf[MAX_SIZE]; - - // States - int song_id; - int next_song_id; - int last_song_id; - unsigned queue_version; - unsigned queue_length; - int last_update_sticker_song_id; - int last_last_played_id; - - // Features - const unsigned* protocol; - // Supported tags - bool feat_sticker; - bool feat_playlists; - bool feat_tags; - bool feat_library; - bool feat_advsearch; -} mpd; - -struct list mpd_tags; -struct list mympd_tags; -struct list mympd_searchtags; -struct list mympd_browsetags; -struct list last_played; - -typedef struct { - long playCount; - long skipCount; - long lastPlayed; - long like; -} t_sticker; - -typedef struct { - bool notificationWeb; - bool notificationPage; - int jukeboxMode; - const char *jukeboxPlaylist; - int jukeboxQueueLength; - char *colsQueueCurrent; - char *colsSearch; - char *colsBrowseDatabase; - char *colsBrowsePlaylistsDetail; - char *colsBrowseFilesystem; - char *colsPlayback; - char *colsQueueLastPlayed; -} t_mympd_state; - -t_mympd_state mympd_state; - -void mpd_client_idle(int timeout); -void mpd_client_parse_idle(int idle_bitmask); -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); -bool mpd_client_like_song_uri(const char *uri, int value); -bool mpd_client_last_played_song_uri(const char *uri); -bool mpd_client_last_played_song_id(int song_id); -bool mpd_client_get_sticker(const char *uri, t_sticker *sticker); -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_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(); -int mpd_client_smartpls_clear(char *playlist); -int mpd_client_smartpls_update_sticker(char *playlist, char *sticker, int maxentries); -int mpd_client_smartpls_update_newest(char *playlist, int timerange); -int mpd_client_smartpls_update_search(char *playlist, char *tag, char *searchstr); -int mpd_client_get_updatedb_state(char *buffer); -int mpd_client_put_state(char *buffer, int *current_song_id, int *next_song_id, int *last_song_id, unsigned *queue_version, unsigned *queue_length); -int mpd_client_put_outputs(char *buffer); -int mpd_client_put_current_song(char *buffer); -int mpd_client_put_queue(char *buffer, unsigned int offset, unsigned *queue_version, unsigned *queue_length); -int mpd_client_put_browse(char *buffer, char *path, unsigned int offset, char *filter); -int mpd_client_search(char *buffer, char *searchstr, char *filter, char *plist, unsigned int offset); -int mpd_client_search_adv(char *buffer, char *expression, char *sort, bool sortdesc, char *grouptag, char *plist, unsigned int offset); -int mpd_client_search_queue(char *buffer, char *mpdtagtype, unsigned int offset, char *searchstr); -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, 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); -int mpd_client_put_playlist_list(char *buffer, char *uri, unsigned int offset, char *filter); -int mpd_client_put_songdetails(char *buffer, char *uri); -int mpd_client_put_last_played_songs(char *buffer, unsigned int offset); -int mpd_client_queue_crop(char *buffer); -void mpd_client_disconnect(); #endif diff --git a/src/mympd_api.c b/src/mympd_api.c index f265b53..1d0c397 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -39,21 +39,22 @@ #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); +//static void mympd_api(void *arg_request, void *arg_config); +static void mympd_api(t_work_request *request, t_config *config); +static int mympd_api_syscmd(char *buffer, const char *cmd, t_config *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); + struct t_work_request *request = tiny_queue_shift(mympd_api_queue); + mympd_api(request, 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; +static void mympd_api(t_work_request *request, t_config *config) { + //t_work_request *request = (t_work_request *) arg_request; size_t len = 0; char buffer[MAX_SIZE]; int je; @@ -61,10 +62,10 @@ static void mympd_api(void *arg_request, void *arg_config) { LOG_VERBOSE() printf("MYMPD API request: %.*s\n", request->length, request->data); if (request->cmd_id == MYMPD_API_SYSCMD) { - if (config.syscmds == true) { + 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); + len = mympd_api_syscmd(buffer, p_charbuf1, config); free(p_charbuf1); } } @@ -83,7 +84,7 @@ static void mympd_api(void *arg_request, void *arg_config) { } 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)); + t_work_result *response = (t_work_result *)malloc(sizeof(t_work_result)); response->conn_id = request->conn_id; response->length = copy_string(response->data, buffer, MAX_SIZE, len); tiny_queue_push(web_server_queue, response); @@ -91,16 +92,15 @@ static void mympd_api(void *arg_request, void *arg_config) { free(request); } -static int mympd_api_syscmd(char *buffer, const char *cmd, void *arg_config) { +static int mympd_api_syscmd(char *buffer, const char *cmd, t_config *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); + 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\"}"); @@ -118,9 +118,10 @@ static int mympd_api_syscmd(char *buffer, const char *cmd, void *arg_config) { fclose(fp); if (read > 0) { strtok_r(line, "\n", &crap); - if (system(line) == 0) { + const int rc = system(line); + if ( rc == 0) { len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Executed cmd %s.\"}", cmd); - LOG_VERBOSE2() printf("Executed syscmd: \"%s\"\n", line); + LOG_VERBOSE() printf("Executed syscmd: \"%s\"\n", line); } else { len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Executing cmd %s failed.\"}", cmd); diff --git a/src/web_server.c b/src/web_server.c index 2f51b7b..aceddf8 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -38,8 +38,8 @@ 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 void send_ws_notify(struct mg_mgr *mgr, t_work_result *response); +static void send_api_response(struct mg_mgr *mgr, t_work_result *response); static bool handle_api(long conn_id, const char *request, int request_len); typedef struct t_user_data { @@ -48,9 +48,8 @@ typedef struct t_user_data { } t_user_data; //public functions -bool web_server_init(void *arg_mgr, void *arg_config) { +bool web_server_init(void *arg_mgr, t_config *config) { struct mg_mgr *mgr = (struct mg_mgr *) arg_mgr; - t_config *config = (t_config *) arg_config; struct mg_connection *nc_https; struct mg_connection *nc_http; struct mg_bind_opts bind_opts_https; @@ -78,7 +77,7 @@ bool web_server_init(void *arg_mgr, void *arg_config) { return false; } mg_set_protocol_http_websocket(nc_http); - LOG_INFO2() printf("Listening on http port %s.\n", config->webport); + LOG_INFO() printf("Listening on http port %s.\n", config->webport); //bind to sslport if (config->ssl == true) { @@ -93,7 +92,7 @@ bool web_server_init(void *arg_mgr, void *arg_config) { mg_mgr_free(mgr); return false; } - LOG_INFO2() printf("Listening on ssl port %s\n", config->sslport); + LOG_INFO() printf("Listening on ssl port %s\n", config->sslport); mg_set_protocol_http_websocket(nc_https); } return mgr; @@ -110,7 +109,7 @@ void *web_server_loop(void *arg_mgr) { mg_mgr_poll(mgr, 100); unsigned web_server_queue_length = tiny_queue_length(web_server_queue); if (web_server_queue_length > 0) { - struct work_result_t *response = tiny_queue_shift(web_server_queue); + t_work_result *response = tiny_queue_shift(web_server_queue); if (response->conn_id == 0) { //Websocket notify from mpd idle send_ws_notify(mgr, response); @@ -130,7 +129,7 @@ static int is_websocket(const struct mg_connection *nc) { return nc->flags & MG_F_IS_WEBSOCKET; } -static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { +static void send_ws_notify(struct mg_mgr *mgr, t_work_result *response) { struct mg_connection *nc; for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { if (!is_websocket(nc)) @@ -140,7 +139,7 @@ static void send_ws_notify(struct mg_mgr *mgr, struct work_result_t *response) { free(response); } -static void send_api_response(struct mg_mgr *mgr, struct work_result_t *response) { +static void send_api_response(struct mg_mgr *mgr, t_work_result *response) { struct mg_connection *nc; for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) { if (nc->user_data != NULL) { @@ -171,12 +170,12 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { nc_user_data->config = config; nc_user_data->conn_id = user_data->conn_id; nc->user_data = nc_user_data; - LOG_DEBUG2() fprintf(stderr, "DEBUG: New connection id %ld.\n", user_data->conn_id); + LOG_DEBUG() fprintf(stderr, "DEBUG: New connection id %ld.\n", user_data->conn_id); break; } case MG_EV_WEBSOCKET_HANDSHAKE_REQUEST: { struct http_message *hm = (struct http_message *) ev_data; - LOG_VERBOSE2() printf("New websocket request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); + LOG_VERBOSE() printf("New websocket request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); if (mg_vcmp(&hm->uri, "/ws") != 0) { printf("ERROR: Websocket request not to /ws, closing connection\n"); mg_printf(nc, "%s", "HTTP/1.1 403 FORBIDDEN\r\n\r\n"); @@ -185,14 +184,14 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { break; } case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { - LOG_VERBOSE2() printf("New Websocket connection established (%ld).\n", user_data->conn_id); + LOG_VERBOSE() printf("New Websocket connection established (%ld).\n", user_data->conn_id); char response[] = "{\"type\": \"welcome\", \"data\": {\"mympdVersion\": \"" MYMPD_VERSION "\"}}"; mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, response, strlen(response)); break; } case MG_EV_HTTP_REQUEST: { 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); + LOG_VERBOSE() printf("HTTP request (%ld): %.*s\n", user_data->conn_id, hm->uri.len, hm->uri.p); if (mg_vcmp(&hm->uri, "/api") == 0) { bool rc = handle_api(user_data->conn_id, hm->body.p, hm->body.len); if (rc == false) { @@ -211,7 +210,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { break; } case MG_EV_CLOSE: { - LOG_VERBOSE2() fprintf(stderr, "HTTP connection %ld closed.\n", user_data->conn_id); + LOG_VERBOSE() fprintf(stderr, "HTTP connection %ld closed.\n", user_data->conn_id); free(nc->user_data); break; } @@ -239,7 +238,7 @@ static void ev_handler_redirect(struct mg_connection *nc, int ev, void *ev_data) snprintf(s_redirect, 250, "https://%s/", host); else snprintf(s_redirect, 250, "https://%s:%s/", host, config->sslport); - LOG_VERBOSE2() printf("Redirecting to %s\n", s_redirect); + LOG_VERBOSE() printf("Redirecting to %s\n", s_redirect); mg_http_send_redirect(nc, 301, mg_mk_str(s_redirect), mg_mk_str(NULL)); break; } @@ -257,11 +256,11 @@ static bool handle_api(long conn_id, const char *request_body, int request_len) if (je < 1) return false; - enum mypd_cmd_ids cmd_id = get_cmd_id(cmd); + enum mympd_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)); + t_work_request *request = (t_work_request*)malloc(sizeof(t_work_request)); request->conn_id = conn_id; request->cmd_id = cmd_id; request->length = copy_string(request->data, request_body, 1000, request_len); diff --git a/src/web_server.h b/src/web_server.h index 1e052d7..93bb650 100644 --- a/src/web_server.h +++ b/src/web_server.h @@ -26,7 +26,7 @@ #define __WEB_SERVER_H__ void *web_server_loop(void *arg_mgr); -bool web_server_init(void *arg_mgr, void *arg_config); +bool web_server_init(void *arg_mgr, t_config *config); void web_server_free(void *arg_mgr); #endif From aa245510ee6263f03fc15c9ae279eee530734a09 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 14 Jan 2019 19:43:05 +0000 Subject: [PATCH 11/59] Fix: ini file with sections --- contrib/mympd.conf.dist | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/contrib/mympd.conf.dist b/contrib/mympd.conf.dist index 88cc7ec..f63bb04 100644 --- a/contrib/mympd.conf.dist +++ b/contrib/mympd.conf.dist @@ -1,17 +1,16 @@ #myMPD config file -#Loglevel -#0 = quiet -#1 = default -#2 = verbose -#3 = debug -loglevel = 1 - +[mpd] #Connection to mpd mpdhost = 127.0.0.1 mpdport = 6600 #mpdpass = +#Port for mpd http stream +streamport = 8000 + + +[webserver] #Webserver options webport = 80 @@ -21,15 +20,21 @@ sslport = 443 sslcert = /etc/mympd/ssl/server.pem sslkey = /etc/mympd/ssl/server.key + +[mympd] +#Loglevel +#0 = quiet +#1 = default +#2 = verbose +#3 = debug +loglevel = 1 + #myMPD user user = mympd #Enable local player, needs streamport or streamurl setting localplayer = true -#Port for mpd http stream -streamport = 8000 - #Manual streamurl, overwrites streamport #streamurl = http://mpdhost:8000 From 230395e4454d83def599eb8296ad010f96195b8e Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 14 Jan 2019 19:43:40 +0000 Subject: [PATCH 12/59] Fix: move functions in mympd_api.c --- src/global.c | 48 +++++ src/global.h.in | 17 +- src/main.c | 92 +++------ src/mpd_client.c | 504 ++++++++++++----------------------------------- src/mympd_api.c | 342 +++++++++++++++++++++++++++++++- 5 files changed, 548 insertions(+), 455 deletions(-) diff --git a/src/global.c b/src/global.c index 151ad76..1743762 100644 --- a/src/global.c +++ b/src/global.c @@ -33,6 +33,54 @@ #include "list.h" #include "global.h" +bool state_file_read(t_config *config, const char *name, char *value) { + char cfg_file[400]; + char *line; + size_t n = 0; + ssize_t read; + + if (!validate_string(name)) + return false; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + FILE *fp = fopen(cfg_file, "r"); + if (fp == NULL) { + printf("Error opening %s\n", cfg_file); + return false; + } + read = getline(&line, &n, fp); + snprintf(value, 400, "%s", line); + LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, value); + fclose(fp); + if (read > 0) + return true; + else + return false; +} + +bool state_file_write(t_config *config, const char *name, const char *value) { + char tmp_file[400]; + char cfg_file[400]; + + if (!validate_string(name)) + return false; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name); + + FILE *fp = fopen(tmp_file, "w"); + if (fp == NULL) { + printf("Error opening %s\n", tmp_file); + return false; + } + fprintf(fp, "%s", value); + fclose(fp); + if (rename(tmp_file, cfg_file) == -1) { + printf("Error renaming file from %s to %s\n", tmp_file, cfg_file); + return false; + } + return true; +} + + bool testdir(char *name, char *dirname) { DIR* dir = opendir(dirname); if (dir) { diff --git a/src/global.h.in b/src/global.h.in index d8c810f..6911700 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -110,10 +110,11 @@ 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) + X(MYMPD_API_COLS_SAVE) \ + X(MYMPD_API_SYSCMD) \ + X(MYMPD_API_SETTINGS_GET) \ + X(MYMPD_API_SETTINGS_SET) \ #define GEN_ENUM(X) X, #define GEN_STR(X) #X, @@ -129,6 +130,13 @@ enum mympd_cmd_ids { MYMPD_CMDS(GEN_ENUM) }; + +enum jukebox_modes { + JUKEBOX_OFF, + JUKEBOX_ADD_SONG, + JUKEBOX_ADD_ALBUM, +}; + //message queue tiny_queue_t *web_server_queue; tiny_queue_t *mpd_client_queue; @@ -176,7 +184,6 @@ typedef struct t_config { const char *streamurl; unsigned long last_played_count; long loglevel; - struct list syscmd_list; } t_config; //global functions @@ -186,4 +193,6 @@ 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); int replacechar(char *str, const char orig, const char rep); enum mympd_cmd_ids get_cmd_id(const char *cmd); +bool state_file_read(t_config *config, const char *name, char *value); +bool state_file_write(t_config *config, const char *name, const char *value); #endif diff --git a/src/main.c b/src/main.c index 67f87d1..c50ae96 100644 --- a/src/main.c +++ b/src/main.c @@ -54,120 +54,94 @@ static int inihandler(void *user, const char *section, const char *name, const c t_config* p_config = (t_config*)user; char *crap; - #define MATCH(n) strcmp(name, n) == 0 + #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 - if (MATCH("mpdhost")) + if (MATCH("mpd", "mpdhost")) p_config->mpdhost = strdup(value); - else if (MATCH("mpdport")) + else if (MATCH("mpd", "mpdport")) p_config->mpdport = strtol(value, &crap, 10); - else if (MATCH("mpdpass")) + else if (MATCH("mpd", "mpdpass")) p_config->mpdpass = strdup(value); - else if (MATCH("webport")) + else if (MATCH("mpd", "streamport")) + p_config->streamport = strtol(value, &crap, 10); + else if (MATCH("webserver", "webport")) p_config->webport = strdup(value); - else if (MATCH("ssl")) + else if (MATCH("webserver", "ssl")) if (strcmp(value, "true") == 0) p_config->ssl = true; else p_config->ssl = false; - else if (MATCH("sslport")) + else if (MATCH("webserver", "sslport")) p_config->sslport = strdup(value); - else if (MATCH("sslcert")) + else if (MATCH("webserver", "sslcert")) p_config->sslcert = strdup(value); - else if (MATCH("sslkey")) + else if (MATCH("webserver", "sslkey")) p_config->sslkey = strdup(value); - else if (MATCH("user")) + else if (MATCH("mympd", "user")) p_config->user = strdup(value); - else if (MATCH("streamport")) - p_config->streamport = strtol(value, &crap, 10); - else if (MATCH("coverimage")) + else if (MATCH("mympd", "coverimage")) if (strcmp(value, "true") == 0) p_config->coverimage = true; else p_config->coverimage = false; - else if (MATCH("coverimagename")) + else if (MATCH("mympd", "coverimagename")) p_config->coverimagename = strdup(value); - else if (MATCH("coverimagesize")) + else if (MATCH("mympd", "coverimagesize")) p_config->coverimagesize = strtol(value, &crap, 10); - else if (MATCH("varlibdir")) + else if (MATCH("mympd", "varlibdir")) p_config->varlibdir = strdup(value); - else if (MATCH("stickers")) + else if (MATCH("mympd", "stickers")) if (strcmp(value, "true") == 0) p_config->stickers = true; else p_config->stickers = false; - else if (MATCH("smartpls")) + else if (MATCH("mympd", "smartpls")) if (strcmp(value, "true") == 0) p_config->smartpls = true; else p_config->smartpls = false; - else if (MATCH("mixramp")) + else if (MATCH("mympd", "mixramp")) if (strcmp(value, "true") == 0) p_config->mixramp = true; else p_config->mixramp = false; - else if (MATCH("taglist")) + else if (MATCH("mympd", "taglist")) p_config->taglist = strdup(value); - else if (MATCH("searchtaglist")) + else if (MATCH("mympd", "searchtaglist")) p_config->searchtaglist = strdup(value); - else if (MATCH("browsetaglist")) + else if (MATCH("mympd", "browsetaglist")) p_config->browsetaglist = strdup(value); - else if (MATCH("max_elements_per_page")) { + else if (MATCH("mympd", "max_elements_per_page")) { p_config->max_elements_per_page = strtol(value, &crap, 10); if (p_config->max_elements_per_page > MAX_ELEMENTS_PER_PAGE) { printf("Setting max_elements_per_page to maximal value %d", MAX_ELEMENTS_PER_PAGE); p_config->max_elements_per_page = MAX_ELEMENTS_PER_PAGE; } } - else if (MATCH("syscmds")) + else if (MATCH("mympd", "syscmds")) if (strcmp(value, "true") == 0) p_config->syscmds = true; else p_config->syscmds = false; - else if (MATCH("localplayer")) + else if (MATCH("mympd", "localplayer")) if (strcmp(value, "true") == 0) p_config->localplayer = true; else p_config->localplayer = false; - else if (MATCH("streamurl")) + else if (MATCH("mympd", "streamurl")) p_config->streamurl = strdup(value); - else if (MATCH("last_played_count")) + else if (MATCH("mympd", "last_played_count")) p_config->last_played_count = strtol(value, &crap, 10); - else if (MATCH("loglevel")) + else if (MATCH("mympd", "loglevel")) p_config->loglevel = strtol(value, &crap, 10); else { - printf("Unkown config line: %s\n", name); + printf("Unkown config option: %s - %s\n", section, name); return 0; /* unknown section/name, error */ } return 1; } -void read_syscmds(t_config *config) { - DIR *dir; - struct dirent *ent; - char dirname[400]; - char *cmd; - long order; - - if (config->syscmds == true) { - snprintf(dirname, 400, "%s/syscmds", config->etcdir); - 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(&config->syscmd_list, strdup(cmd), order); - } - closedir(dir); - } - } - else { - printf("Syscmds are disabled\n"); - } -} - int main(int argc, char **argv) { s_signal_received = 0; char testdirname[400]; @@ -298,11 +272,6 @@ int main(int argc, char **argv) { if (!testdir("State dir", testdirname)) return EXIT_FAILURE; - //read system command files - list_init(&config.syscmd_list); - read_syscmds(&config); - list_sort_by_value(&config.syscmd_list, true); - //Create working threads pthread_t mpd_client_thread, web_server_thread, mympd_api_thread; //mpd connection @@ -314,10 +283,9 @@ int main(int argc, char **argv) { //Outsourced all work to separate threads, do nothing... - //clean up + //cleanup pthread_join(mpd_client_thread, NULL); pthread_join(web_server_thread, NULL); - list_free(&config.syscmd_list); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); diff --git a/src/mpd_client.c b/src/mpd_client.c index 8ef6f07..0f7647a 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -101,26 +101,20 @@ typedef struct t_mpd_state { bool feat_tags; bool feat_library; bool feat_advsearch; + bool feat_smartpls; //mympd states - bool notificationWeb; - bool notificationPage; - int jukeboxMode; + enum jukebox_modes jukeboxMode; const char *jukeboxPlaylist; int jukeboxQueueLength; - char *colsQueueCurrent; - char *colsSearch; - char *colsBrowseDatabase; - char *colsBrowsePlaylistsDetail; - char *colsBrowseFilesystem; - char *colsPlayback; - char *colsQueueLastPlayed; //taglists struct list mpd_tags; struct list mympd_tags; struct list mympd_searchtags; struct list mympd_browsetags; + + //last played list struct list last_played; } t_mpd_state; @@ -135,44 +129,41 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state, const int static void mpd_client_parse_idle(t_config *config, t_mpd_state *mpd_state, const int idle_bitmask); static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_request); static void mpd_client_notify(const char *message, const size_t n); -static bool mpd_client_count_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id, const char *name, const int value); -static bool mpd_client_count_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, const char *name, const int value); -static bool mpd_client_like_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, int value); -static bool mpd_client_last_played_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id); -static bool mpd_client_last_played_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri); -static bool mpd_client_get_sticker(t_config *config, t_mpd_state *mpd_state, const char *uri, t_sticker *sticker); +static bool mpd_client_count_song_id(t_mpd_state *mpd_state, const int song_id, const char *name, const int value); +static bool mpd_client_count_song_uri(t_mpd_state *mpd_state, const char *uri, const char *name, const int value); +static bool mpd_client_like_song_uri(t_mpd_state *mpd_state, const char *uri, int value); +static bool mpd_client_last_played_song_id(t_mpd_state *mpd_state, const int song_id); +static bool mpd_client_last_played_song_uri(t_mpd_state *mpd_state, const char *uri); +static bool mpd_client_get_sticker(t_mpd_state *mpd_state, const char *uri, t_sticker *sticker); static bool mpd_client_last_played_list(t_config *config, t_mpd_state *mpd_state, const int song_id); static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state); -static bool mpd_client_state_get(t_config *config, t_mpd_state *mpd_state, const char *name, char *value); -static bool mpd_client_state_set(t_config *config, t_mpd_state *mpd_state, const char *name, const char *value); static bool mpd_client_smartpls_save(t_config *config, t_mpd_state *mpd_state, const char *smartpltype, const char *playlist, const char *tag, const char *searchstr, const int maxentries, const int timerange); -static int mpd_client_smartpls_put(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *playlist); +static int mpd_client_smartpls_put(t_config *config, char *buffer, const char *playlist); static bool mpd_client_smartpls_update_all(t_config *config, t_mpd_state *mpd_state); -static bool mpd_client_smartpls_clear(t_config *config, t_mpd_state *mpd_state, const char *playlist); -static bool mpd_client_smartpls_update_sticker(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *sticker, const int maxentries); +static bool mpd_client_smartpls_clear(t_mpd_state *mpd_state, const char *playlist); +static bool mpd_client_smartpls_update_sticker(t_mpd_state *mpd_state, const char *playlist, const char *sticker, const int maxentries); static bool mpd_client_smartpls_update_newest(t_config *config, t_mpd_state *mpd_state, const char *playlist, const int timerange); static bool mpd_client_smartpls_update_search(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *tag, const char *searchstr); -static int mpd_client_get_updatedb_state(t_config *config, t_mpd_state *mpd_state, char *buffer); -static int mpd_client_put_state(t_config *config, t_mpd_state *mpd_state, char *buffer); -static int mpd_client_put_outputs(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_get_updatedb_state(t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_state(t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_outputs(t_mpd_state *mpd_state, char *buffer); static int mpd_client_put_current_song(t_config *config, t_mpd_state *mpd_state, char *buffer); static int mpd_client_put_queue(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset); static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *path, const unsigned int offset, const char *filter); static int mpd_client_search(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *searchstr, const char *filter, const char *plist, const unsigned int offset); static int mpd_client_search_adv(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *expression, const char *sort, const bool sortdesc, const char *grouptag, const char *plist, const unsigned int offset); static int mpd_client_search_queue(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *mpdtagtype, const unsigned int offset, const char *searchstr); -static int mpd_client_put_volume(t_config *config, t_mpd_state *mpd_state, char *buffer); -static int mpd_client_put_stats(t_config *config, t_mpd_state *mpd_state, char *buffer); -static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_volume(t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_stats(t_mpd_state *mpd_state, char *buffer); +static int mpd_client_put_settings(t_mpd_state *mpd_state, char *buffer); static int mpd_client_put_db_tag(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset, const char *mpdtagtype, const char *mpdsearchtagtype, const char *searchstr, const char *filter); static int mpd_client_put_songs_in_album(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *album, const char *search, const char *tag); static int mpd_client_put_playlists(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset, const char *filter); static int mpd_client_put_playlist_list(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *uri, const unsigned int offset, const char *filter); static int mpd_client_put_songdetails(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *uri); static int mpd_client_put_last_played_songs(t_config *config, t_mpd_state *mpd_state, char *buffer, const unsigned int offset); -static int mpd_client_queue_crop(t_config *config, t_mpd_state *mpd_state, char *buffer); +static int mpd_client_queue_crop(t_mpd_state *mpd_state, char *buffer); static void mpd_client_disconnect(t_config *config, t_mpd_state *mpd_state); -static void mpd_client_read_statefiles(t_config *config, t_mpd_state *mpd_state); static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state); //public functions @@ -186,6 +177,9 @@ void *mpd_client_loop(void *arg_config) { mpd_state.last_song_id = -1; mpd_state.last_last_played_id = -1; mpd_state.feat_library = false; + mpd_state.jukeboxMode = JUKEBOX_OFF; + mpd_state.jukeboxPlaylist = strdup("Database"); + mpd_state.jukeboxQueueLength = 1; list_init(&mpd_state.mpd_tags); list_init(&mpd_state.mympd_tags); list_init(&mpd_state.mympd_searchtags); @@ -198,9 +192,6 @@ void *mpd_client_loop(void *arg_config) { mpd_state.feat_library = true; } - //read myMPD states under config.varlibdir - mpd_client_read_statefiles(config, &mpd_state); - //read last played songs history file list_init(&mpd_state.last_played); int len = mpd_client_read_last_played(config, &mpd_state); @@ -230,7 +221,6 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r float float_buf; bool bool_buf; char *p_charbuf1, *p_charbuf2, *p_charbuf3, *p_charbuf4; - char p_char[4]; #ifdef DEBUG struct timespec start, end; #endif @@ -242,16 +232,18 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r #endif switch(request->cmd_id) { case MYMPD_API_SYSCMD: - //is handled in mympd_api.c + case MYMPD_API_COLS_SAVE: + case MYMPD_API_SETTINGS_GET: + //are handled in mympd_api.c case MPD_API_UNKNOWN: len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); printf("Unknown API request: %.*s\n", request->length, request->data); break; case MPD_API_LIKE: - if (config->stickers) { + if (mpd_state->feat_sticker) { 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(config, mpd_state, p_charbuf1, uint_buf1)) + if (!mpd_client_like_song_uri(mpd_state, p_charbuf1, uint_buf1)) len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); else len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); @@ -263,124 +255,51 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r printf("MPD_API_LIKE: MPD stickers are disabled\n"); } break; - case MPD_API_COLS_SAVE: - je = json_scanf(request->data, request->length, "{data: {table: %Q}}", &p_charbuf1); - if (je == 1) { - char column_list[800]; - snprintf(column_list, 800, "%.*s", request->length, request->data); - char *cols = strchr(column_list, '['); - int col_len = strlen(cols); - if (col_len > 1) - cols[col_len - 2] = '\0'; - if (strcmp(p_charbuf1, "colsQueueCurrent") == 0) { - free(mpd_state->colsQueueCurrent); - mpd_state->colsQueueCurrent = strdup(cols); - } - else if (strcmp(p_charbuf1, "colsSearch") == 0) { - free(mpd_state->colsSearch); - mpd_state->colsSearch = strdup(cols); - } - else if (strcmp(p_charbuf1, "colsBrowseDatabase") == 0) { - free(mpd_state->colsBrowseDatabase); - mpd_state->colsBrowseDatabase = strdup(cols); - } - else if (strcmp(p_charbuf1, "colsBrowsePlaylistsDetail") == 0) { - free(mpd_state->colsBrowsePlaylistsDetail); - mpd_state->colsBrowsePlaylistsDetail = strdup(cols); - } - else if (strcmp(p_charbuf1, "colsBrowseFilesystem") == 0) { - free(mpd_state->colsBrowseFilesystem); - mpd_state->colsBrowseFilesystem = strdup(cols); - } - else if (strcmp(p_charbuf1, "colsPlayback") == 0) { - free(mpd_state->colsPlayback); - mpd_state->colsPlayback = strdup(cols); - } - else if (strcmp(p_charbuf1, "colsQueueLastPlayed") == 0) { - free(mpd_state->colsQueueLastPlayed); - mpd_state->colsQueueLastPlayed = strdup(cols); - } - else { - len = snprintf(buffer, 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 (len == 0) { - if (mpd_client_state_set(config, mpd_state, p_charbuf1, cols)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); - } - free(p_charbuf1); - } - break; case MPD_API_PLAYER_STATE: - len = mpd_client_put_state(config, mpd_state, buffer); + len = mpd_client_put_state(mpd_state, buffer); break; - case MPD_API_SETTINGS_SET: - je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mpd_state->notificationWeb); - if (je == 1) - if (!mpd_client_state_set(config, mpd_state, "notificationWeb", (mpd_state->notificationWeb == true ? "true" : "false"))) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); - - je = json_scanf(request->data, request->length, "{data: {notificationPage: %B}}", &mpd_state->notificationPage); - if (je == 1) - if (!mpd_client_state_set(config, mpd_state, "notificationPage", (mpd_state->notificationPage == true ? "true" : "false"))) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); - + case MYMPD_API_SETTINGS_SET: + //only update mpd_state, already saved in mympd_api.c je = json_scanf(request->data, request->length, "{data: {jukeboxMode: %d}}", &mpd_state->jukeboxMode); - if (je == 1) { - snprintf(p_char, 4, "%d", mpd_state->jukeboxMode); - if (!mpd_client_state_set(config, mpd_state, "jukeboxMode", p_char)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); - } - je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mpd_state->jukeboxPlaylist); - if (je == 1) - if (!mpd_client_state_set(config, mpd_state, "jukeboxPlaylist", mpd_state->jukeboxPlaylist)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); - je = json_scanf(request->data, request->length, "{data: {jukeboxQueueLength: %d}}", &mpd_state->jukeboxQueueLength); - if (je == 1) { - snprintf(p_char, 4, "%d", mpd_state->jukeboxQueueLength); - if (!mpd_client_state_set(config, mpd_state, "jukeboxQueueLength", p_char)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); - } - + //set mpd options je = json_scanf(request->data, request->length, "{data: {random: %u}}", &uint_buf1); - if (je == 1) + if (je == 1) { if (!mpd_run_random(mpd_state->conn, uint_buf1)) len = snprintf(buffer, 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 (je == 1) { if (!mpd_run_repeat(mpd_state->conn, uint_buf1)) len = snprintf(buffer, 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 (je == 1) { if (!mpd_run_consume(mpd_state->conn, uint_buf1)) len = snprintf(buffer, 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 (je == 1) { if (!mpd_run_single(mpd_state->conn, uint_buf1)) len = snprintf(buffer, 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 (je == 1) { if (!mpd_run_crossfade(mpd_state->conn, uint_buf1)) len = snprintf(buffer, 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 (je == 1) { if (!mpd_run_mixrampdb(mpd_state->conn, float_buf)) len = snprintf(buffer, 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 (je == 1) { if (!mpd_run_mixrampdelay(mpd_state->conn, float_buf)) len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdelay.\"}"); + } } je = json_scanf(request->data, request->length, "{data: {replaygain: %Q}}", &p_charbuf1); @@ -390,8 +309,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r mpd_response_finish(mpd_state->conn); free(p_charbuf1); } - if (mpd_state->jukeboxMode > 0) + if (mpd_state->jukeboxMode != JUKEBOX_OFF) { mpd_client_jukebox(config, mpd_state); + } if (len == 0) len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; @@ -450,7 +370,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r case MPD_API_SMARTPLS_GET: je = json_scanf(request->data, request->length, "{data: {playlist: %Q}}", &p_charbuf1); if (je == 1) { - len = mpd_client_smartpls_put(config, mpd_state, buffer, p_charbuf1); + len = mpd_client_smartpls_put(config, buffer, p_charbuf1); free(p_charbuf1); } break; @@ -471,8 +391,8 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r } break; case MPD_API_PLAYER_NEXT: - if (config->stickers) - mpd_client_count_song_id(config, mpd_state, mpd_state->song_id, "skipCount", 1); + if (mpd_state->feat_sticker) + mpd_client_count_song_id(mpd_state, mpd_state->song_id, "skipCount", 1); if (mpd_run_next(mpd_state->conn)) len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { @@ -505,7 +425,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r } break; case MPD_API_QUEUE_CROP: - len = mpd_client_queue_crop(config, mpd_state, buffer); + len = mpd_client_queue_crop(mpd_state, buffer); break; case MPD_API_QUEUE_RM_TRACK: je = json_scanf(request->data, request->length, "{data: {track:%u}}", &uint_buf1); @@ -574,7 +494,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r } break; case MPD_API_PLAYER_OUTPUT_LIST: - len = mpd_client_put_outputs(config, mpd_state, buffer); + len = mpd_client_put_outputs(mpd_state, buffer); break; case MPD_API_PLAYER_TOGGLE_OUTPUT: je = json_scanf(request->data, request->length, "{data: {output: %u, state: %u}}", &uint_buf1, &uint_buf2); @@ -609,7 +529,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r } break; case MPD_API_PLAYER_VOLUME_GET: - len = mpd_client_put_volume(config, mpd_state, buffer); + len = mpd_client_put_volume(mpd_state, buffer); break; case MPD_API_PLAYER_SEEK: je = json_scanf(request->data, request->length, "{data: {songid: %u, seek: %u}}", &uint_buf1, &uint_buf2); @@ -941,11 +861,14 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r } break; case MPD_API_SETTINGS_GET: - len = mpd_client_put_settings(config, mpd_state, buffer); + len = mpd_client_put_settings(mpd_state, buffer); break; case MPD_API_DATABASE_STATS: - len = mpd_client_put_stats(config, mpd_state, buffer); + len = mpd_client_put_stats(mpd_state, buffer); break; + default: + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); + printf("Unknown API request: %.*s\n", request->length, request->data); } if (mpd_state->conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd_state->conn) != MPD_ERROR_SUCCESS) { @@ -1011,23 +934,23 @@ static void mpd_client_parse_idle(t_config *config, t_mpd_state *mpd_state, int break; case MPD_IDLE_QUEUE: len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_queue\"}"); - if (mpd_state->jukeboxMode > 0) + if (mpd_state->jukeboxMode != JUKEBOX_OFF) mpd_client_jukebox(config, mpd_state); break; case MPD_IDLE_PLAYER: - len = mpd_client_put_state(config, mpd_state, buffer); + len = mpd_client_put_state(mpd_state, buffer); if (mpd_state->song_id != mpd_state->last_song_id) { if (mpd_state->last_last_played_id != mpd_state->song_id) mpd_client_last_played_list(config, mpd_state, mpd_state->song_id); - if (config->stickers && mpd_state->last_update_sticker_song_id != mpd_state->song_id) { - mpd_client_count_song_id(config, mpd_state, mpd_state->song_id, "playCount", 1); - mpd_client_last_played_song_id(config, mpd_state, mpd_state->song_id); + if (mpd_state->feat_sticker && mpd_state->last_update_sticker_song_id != mpd_state->song_id) { + mpd_client_count_song_id(mpd_state, mpd_state->song_id, "playCount", 1); + mpd_client_last_played_song_id(mpd_state, mpd_state->song_id); mpd_state->last_update_sticker_song_id = mpd_state->song_id; } } break; case MPD_IDLE_MIXER: - len = mpd_client_put_volume(config, mpd_state, buffer); + len = mpd_client_put_volume(mpd_state, buffer); break; case MPD_IDLE_OUTPUT: len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_outputs\"}"); @@ -1036,7 +959,7 @@ static void mpd_client_parse_idle(t_config *config, t_mpd_state *mpd_state, int len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_options\"}"); break; case MPD_IDLE_UPDATE: - len = mpd_client_get_updatedb_state(config, mpd_state, buffer); + len = mpd_client_get_updatedb_state(mpd_state, buffer); break; case MPD_IDLE_STICKER: len = snprintf(buffer, MAX_SIZE, "{\"type\": \"update_sticker\"}"); @@ -1070,10 +993,11 @@ static void mpd_client_mpd_features(t_config *config, t_mpd_state *mpd_state) { LOG_INFO() printf("MPD protocoll version: %u.%u.%u\n", mpd_state->protocol[0], mpd_state->protocol[1], mpd_state->protocol[2]); // Defaults - mpd_state->feat_sticker = false; + mpd_state->feat_sticker = config->stickers; mpd_state->feat_playlists = false; mpd_state->feat_tags = false; mpd_state->feat_advsearch = false; + mpd_state->feat_smartpls = config->smartpls; if (mpd_send_allowed_commands(mpd_state->conn)) { while ((pair = mpd_recv_command_pair(mpd_state->conn)) != NULL) { @@ -1088,17 +1012,17 @@ static void mpd_client_mpd_features(t_config *config, t_mpd_state *mpd_state) { else { LOG_ERROR_AND_RECOVER("mpd_send_allowed_commands"); } - if (mpd_state->feat_sticker == false && config->stickers == true) { + if (mpd_state->feat_sticker == true && config->stickers == false) { LOG_INFO() printf("MPD don't support stickers, disabling myMPD feature\n"); - config->stickers = false; + mpd_state->feat_sticker = false; } - if (config->stickers == false && config->smartpls == true) { - LOG_INFO() printf("Stickers are disabled, disabling smartplaylists\n"); - config->smartpls = false; + if (mpd_state->feat_sticker == false && config->smartpls == true) { + LOG_INFO() printf("Stickers are disabled, disabling smart playlists\n"); + mpd_state->feat_smartpls = false; } if (mpd_state->feat_playlists == false && config->smartpls == true) { - LOG_INFO() printf("Playlists are disabled, disabling smartplaylists\n"); - config->smartpls = false; + LOG_INFO() printf("Playlists are disabled, disabling smart playlists\n"); + mpd_state->feat_smartpls = false; } LOG_INFO() printf("MPD supported tags: "); @@ -1220,7 +1144,7 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state, const int mpd_state->conn_state = MPD_CONNECTED; mpd_client_mpd_features(config, mpd_state); mpd_client_smartpls_update_all(config, mpd_state); - if (mpd_state->jukeboxMode > 0) + if (mpd_state->jukeboxMode != JUKEBOX_OFF) mpd_client_jukebox(config, mpd_state); mpd_send_idle(mpd_state->conn); break; @@ -1270,7 +1194,7 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state, const int } } -static int mpd_client_get_updatedb_state(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_get_updatedb_state(t_mpd_state *mpd_state, char *buffer) { struct mpd_status *status; int len, update_id; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); @@ -1291,7 +1215,7 @@ static int mpd_client_get_updatedb_state(t_config *config, t_mpd_state *mpd_stat } -static bool mpd_client_get_sticker(t_config *config, t_mpd_state *mpd_state, const char *uri, t_sticker *sticker) { +static bool mpd_client_get_sticker(t_mpd_state *mpd_state, const char *uri, t_sticker *sticker) { struct mpd_pair *pair; char *crap; sticker->playCount = 0; @@ -1321,12 +1245,12 @@ static bool mpd_client_get_sticker(t_config *config, t_mpd_state *mpd_state, con return true; } -static bool mpd_client_count_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id, const char *name, const int value) { +static bool mpd_client_count_song_id(t_mpd_state *mpd_state, const int song_id, const char *name, const int value) { struct mpd_song *song; if (song_id > -1) { song = mpd_run_get_queue_song_id(mpd_state->conn, song_id); if (song) { - if (!mpd_client_count_song_uri(config, mpd_state, mpd_song_get_uri(song), name, value)) { + if (!mpd_client_count_song_uri(mpd_state, mpd_song_get_uri(song), name, value)) { mpd_song_free(song); return false; } @@ -1342,7 +1266,7 @@ static bool mpd_client_count_song_id(t_config *config, t_mpd_state *mpd_state, c return true; } -static bool mpd_client_count_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, const char *name, const int value) { +static bool mpd_client_count_song_uri(t_mpd_state *mpd_state, const char *uri, const char *name, const int value) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; struct mpd_pair *pair; @@ -1373,7 +1297,7 @@ static bool mpd_client_count_song_uri(t_config *config, t_mpd_state *mpd_state, return true; } -static bool mpd_client_like_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri, int value) { +static bool mpd_client_like_song_uri(t_mpd_state *mpd_state, const char *uri, int value) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; char v[2]; @@ -1429,13 +1353,13 @@ static bool mpd_client_last_played_list(t_config *config, t_mpd_state *mpd_state return true; } -static bool mpd_client_last_played_song_id(t_config *config, t_mpd_state *mpd_state, const int song_id) { +static bool mpd_client_last_played_song_id(t_mpd_state *mpd_state, const int song_id) { struct mpd_song *song; if (song_id > -1) { song = mpd_run_get_queue_song_id(mpd_state->conn, song_id); if (song) { - if (mpd_client_last_played_song_uri(config, mpd_state, mpd_song_get_uri(song)) == false) { + if (mpd_client_last_played_song_uri(mpd_state, mpd_song_get_uri(song)) == false) { mpd_song_free(song); return false; } @@ -1455,7 +1379,7 @@ static bool mpd_client_last_played_song_id(t_config *config, t_mpd_state *mpd_st return true; } -static bool mpd_client_last_played_song_uri(t_config *config, t_mpd_state *mpd_state, const char *uri) { +static bool mpd_client_last_played_song_uri(t_mpd_state *mpd_state, const char *uri) { if (uri == NULL || strncasecmp("http:", uri, 5) == 0 || strncasecmp("https:", uri, 6) == 0) return false; char v[20]; @@ -1501,7 +1425,7 @@ static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state) { if (queue_length > mpd_state->jukeboxQueueLength) return true; - if (mpd_state->jukeboxMode == 1) + if (mpd_state->jukeboxMode == JUKEBOX_ADD_SONG) addSongs = mpd_state->jukeboxQueueLength - queue_length; else addSongs = 1; @@ -1519,7 +1443,7 @@ static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state) { struct list add_list; list_init(&add_list); - if (mpd_state->jukeboxMode == 1) { + if (mpd_state->jukeboxMode == JUKEBOX_ADD_SONG) { //add songs if (strcmp(mpd_state->jukeboxPlaylist, "Database") == 0) { if (!mpd_send_list_all(mpd_state->conn, "/")) { @@ -1562,7 +1486,7 @@ static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state) { mpd_entity_free(entity); } } - else if (mpd_state->jukeboxMode == 2) { + else if (mpd_state->jukeboxMode == JUKEBOX_ADD_ALBUM) { //add album if (!mpd_search_db_tags(mpd_state->conn, MPD_TAG_ALBUM)) { LOG_ERROR_AND_RECOVER("mpd_search_db_tags"); @@ -1605,7 +1529,7 @@ static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state) { nkeep = 0; struct node *current = add_list.list; while (current != NULL) { - if (mpd_state->jukeboxMode == 1) { + if (mpd_state->jukeboxMode == JUKEBOX_ADD_SONG) { LOG_INFO() printf("Jukebox adding song: %s\n", current->data); if (!mpd_run_add(mpd_state->conn, current->data)) LOG_ERROR_AND_RECOVER("mpd_run_add"); @@ -1634,7 +1558,7 @@ static bool mpd_client_jukebox(t_config *config, t_mpd_state *mpd_state) { return true; } -static int mpd_client_put_state(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_put_state(t_mpd_state *mpd_state, char *buffer) { size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); @@ -1680,7 +1604,7 @@ static int mpd_client_put_state(t_config *config, t_mpd_state *mpd_state, char * CHECK_RETURN_LEN(); } -static int mpd_client_put_volume(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_put_volume(t_mpd_state *mpd_state, char *buffer) { size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); @@ -1698,54 +1622,7 @@ static int mpd_client_put_volume(t_config *config, t_mpd_state *mpd_state, char CHECK_RETURN_LEN(); } -static bool mpd_client_state_get(t_config *config, t_mpd_state *mpd_state, const char *name, char *value) { - char cfg_file[400]; - char *line; - size_t n = 0; - ssize_t read; - - if (!validate_string(name)) - return false; - snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); - FILE *fp = fopen(cfg_file, "r"); - if (fp == NULL) { - printf("Error opening %s\n", cfg_file); - return false; - } - read = getline(&line, &n, fp); - snprintf(value, 400, "%s", line); - LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, value); - fclose(fp); - if (read > 0) - return true; - else - return false; -} - -static bool mpd_client_state_set(t_config *config, t_mpd_state *mpd_state, const char *name, const char *value) { - char tmp_file[400]; - char cfg_file[400]; - - if (!validate_string(name)) - return false; - snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); - snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name); - - FILE *fp = fopen(tmp_file, "w"); - if (fp == NULL) { - printf("Error opening %s\n", tmp_file); - return false; - } - fprintf(fp, "%s", value); - fclose(fp); - if (rename(tmp_file, cfg_file) == -1) { - printf("Error renaming file from %s to %s\n", tmp_file, cfg_file); - return false; - } - return true; -} - -static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_put_settings(t_mpd_state *mpd_state, char *buffer) { char *replaygain = ""; size_t len; int nr = 0; @@ -1758,8 +1635,9 @@ static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, cha return 0; } - if (!mpd_send_command(mpd_state->conn, "replay_gain_status", NULL)) + if (!mpd_send_command(mpd_state->conn, "replay_gain_status", NULL)) { RETURN_ERROR_AND_RECOVER("replay_gain_status"); + } struct mpd_pair *pair; while ((pair = mpd_recv_pair(mpd_state->conn)) != NULL) { replaygain = strdup(pair->value); @@ -1768,10 +1646,8 @@ static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, cha len = json_printf(&out, "{type: settings, data: {" "repeat: %d, single: %d, crossfade: %d, consume: %d, random: %d, " - "mixrampdb: %f, mixrampdelay: %f, mpdhost: %Q, mpdport: %d, passwort_set: %B, featSyscmds: %B, featPlaylists: %B, featTags: %B, featLibrary: %B, " - "featAdvsearch: %B, featLocalplayer: %B, streamport: %d, streamurl: %Q, featCoverimage: %B, coverimagename: %Q, featStickers: %B, mixramp: %B, " - "featSmartpls: %B, maxElementsPerPage: %d, replaygain: %Q, notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, " - "jukeboxQueueLength: %d, coverimagesize: %d, tags: [", + "mixrampdb: %f, mixrampdelay: %f, replaygain: %Q, featPlaylists: %B, featTags: %B, featLibrary: %B, " + "featAdvsearch: %B, featStickers: %B, featSmartpls: %B, tags: [", mpd_status_get_repeat(status), mpd_status_get_single(status), mpd_status_get_crossfade(status), @@ -1779,30 +1655,13 @@ static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, cha 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, + replaygain, mpd_state->feat_playlists, mpd_state->feat_tags, mpd_state->feat_library, mpd_state->feat_advsearch, - config->localplayer, - config->streamport, - config->streamurl, - config->coverimage, - config->coverimagename, - config->stickers, - config->mixramp, - config->smartpls, - config->max_elements_per_page, - replaygain, - mpd_state->notificationWeb, - mpd_state->notificationPage, - mpd_state->jukeboxMode, - mpd_state->jukeboxPlaylist, - mpd_state->jukeboxQueueLength, - config->coverimagesize + mpd_state->feat_sticker, + mpd_state->feat_smartpls ); mpd_status_free(status); free(replaygain); @@ -1833,35 +1692,12 @@ static int mpd_client_put_settings(t_config *config, t_mpd_state *mpd_state, cha len += json_printf(&out, "%Q", current->data); current = current->next; } - len += json_printf(&out, "]"); + len += json_printf(&out, "]}}"); - if (config->syscmds == true) { - len += json_printf(&out, ", syscmds: ["); - nr = 0; - current = config->syscmd_list.list; - while (current != NULL) { - if (nr++) - len += json_printf(&out, ","); - len += json_printf(&out, "%Q", current->data); - current = current->next; - } - len += json_printf(&out, "]"); - } - len += json_printf(&out, ", colsQueueCurrent: %s, colsSearch: %s, colsBrowseDatabase: %s, colsBrowsePlaylistsDetail: %s, " - "colsBrowseFilesystem: %s, colsPlayback: %s, colsQueueLastPlayed: %s}}", - mpd_state->colsQueueCurrent, - mpd_state->colsSearch, - mpd_state->colsBrowseDatabase, - mpd_state->colsBrowsePlaylistsDetail, - mpd_state->colsBrowseFilesystem, - mpd_state->colsPlayback, - mpd_state->colsQueueLastPlayed - ); - CHECK_RETURN_LEN(); } -static int mpd_client_put_outputs(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_put_outputs(t_mpd_state *mpd_state, char *buffer) { struct mpd_output *output; size_t len; int nr; @@ -1954,9 +1790,9 @@ static int mpd_client_put_current_song(t_config *config, t_mpd_state *mpd_state, mpd_response_finish(mpd_state->conn); - if (config->stickers) { + if (mpd_state->feat_sticker) { t_sticker *sticker = (t_sticker *) malloc(sizeof(t_sticker)); - mpd_client_get_sticker(config, mpd_state, mpd_song_get_uri(song), sticker); + mpd_client_get_sticker(mpd_state, mpd_song_get_uri(song), sticker); len += json_printf(&out, ", playCount: %d, skipCount: %d, like: %d, lastPlayed: %d", sticker->playCount, sticker->skipCount, @@ -1993,9 +1829,9 @@ static int mpd_client_put_songdetails(t_config *config, t_mpd_state *mpd_state, } mpd_response_finish(mpd_state->conn); - if (config->stickers) { + if (mpd_state->feat_sticker) { t_sticker *sticker = (t_sticker *) malloc(sizeof(t_sticker)); - mpd_client_get_sticker(config, mpd_state, uri, sticker); + mpd_client_get_sticker(mpd_state, uri, sticker); len += json_printf(&out, ", playCount: %d, skipCount: %d, like: %d, lastPlayed: %d", sticker->playCount, sticker->skipCount, @@ -2597,7 +2433,7 @@ static int mpd_client_search_adv(t_config *config, t_mpd_state *mpd_state, char CHECK_RETURN_LEN(); } -static int mpd_client_queue_crop(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_queue_crop(t_mpd_state *mpd_state, char *buffer) { size_t len = 0; struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); struct mpd_status *status = mpd_run_status(mpd_state->conn); @@ -2681,7 +2517,7 @@ static int mpd_client_search_queue(t_config *config, t_mpd_state *mpd_state, cha CHECK_RETURN_LEN(); } -static int mpd_client_put_stats(t_config *config, t_mpd_state *mpd_state, char *buffer) { +static int mpd_client_put_stats(t_mpd_state *mpd_state, char *buffer) { struct mpd_stats *stats = mpd_run_stats(mpd_state->conn); const unsigned *version = mpd_connection_get_server_version(mpd_state->conn); size_t len = 0; @@ -2718,7 +2554,7 @@ static void mpd_client_disconnect(t_config *config, t_mpd_state *mpd_state) { mpd_client_idle(config, mpd_state, 100); } -static int mpd_client_smartpls_put(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *playlist) { +static int mpd_client_smartpls_put(t_config *config, char *buffer, const char *playlist) { char pl_file[400]; char *smartpltype; char *p_charbuf1, *p_charbuf2; @@ -2808,7 +2644,7 @@ static bool mpd_client_smartpls_save(t_config *config, t_mpd_state *mpd_state, c printf("Error renaming file from %s to %s\n", tmp_file, pl_file); return false; } - else if (mpd_client_smartpls_update_sticker(config, mpd_state, playlist, tag, maxentries) == 1) { + else if (mpd_client_smartpls_update_sticker(mpd_state, playlist, tag, maxentries) == 1) { printf("Update of smart playlist %s failed.\n", playlist); return false; } @@ -2854,7 +2690,7 @@ static bool mpd_client_smartpls_update_all(t_config *config, t_mpd_state *mpd_st char *p_charbuf1, *p_charbuf2; int int_buf1; - if (config->smartpls == false) { + if (mpd_state->feat_smartpls == false) { return true; } @@ -2875,7 +2711,7 @@ static bool mpd_client_smartpls_update_all(t_config *config, t_mpd_state *mpd_st if (strcmp(smartpltype, "sticker") == 0) { je = json_scanf(content, strlen(content), "{sticker: %Q, maxentries: %d}", &p_charbuf1, &int_buf1); if (je == 2) { - mpd_client_smartpls_update_sticker(config, mpd_state, ent->d_name, p_charbuf1, int_buf1); + mpd_client_smartpls_update_sticker(mpd_state, ent->d_name, p_charbuf1, int_buf1); free(p_charbuf1); } else @@ -2910,7 +2746,7 @@ static bool mpd_client_smartpls_update_all(t_config *config, t_mpd_state *mpd_st return true; } -static bool mpd_client_smartpls_clear(t_config *config, t_mpd_state *mpd_state, const char *playlist) { +static bool mpd_client_smartpls_clear(t_mpd_state *mpd_state, const char *playlist) { struct mpd_playlist *pl; const char *plpath; bool exists = false; @@ -2940,7 +2776,7 @@ static bool mpd_client_smartpls_clear(t_config *config, t_mpd_state *mpd_state, static bool mpd_client_smartpls_update_search(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *tag, const char *searchstr) { char buffer[MAX_SIZE]; - mpd_client_smartpls_clear(config, mpd_state, playlist); + mpd_client_smartpls_clear(mpd_state, playlist); if (mpd_state->feat_advsearch == true && strcmp(tag, "expression") == 0) { mpd_client_search_adv(config, mpd_state, buffer, searchstr, NULL, true, NULL, playlist, 0); } @@ -2951,7 +2787,7 @@ static bool mpd_client_smartpls_update_search(t_config *config, t_mpd_state *mpd return true; } -static bool mpd_client_smartpls_update_sticker(t_config *config, t_mpd_state *mpd_state, const char *playlist, const char *sticker, const int maxentries) { +static bool mpd_client_smartpls_update_sticker(t_mpd_state *mpd_state, const char *playlist, const char *sticker, const int maxentries) { struct mpd_pair *pair; char *uri = NULL; const char *p_value; @@ -2990,7 +2826,7 @@ static bool mpd_client_smartpls_update_sticker(t_config *config, t_mpd_state *mp mpd_response_finish(mpd_state->conn); free(uri); - mpd_client_smartpls_clear(config, mpd_state, playlist); + mpd_client_smartpls_clear(mpd_state, playlist); if (value_max > 2) { value_max = value_max / 2; @@ -3032,7 +2868,7 @@ static bool mpd_client_smartpls_update_newest(t_config *config, t_mpd_state *mpd return false; } - mpd_client_smartpls_clear(config, mpd_state, playlist); + mpd_client_smartpls_clear(mpd_state, playlist); value_max -= timerange; if (value_max > 0) { if (mpd_state->feat_advsearch == true) { @@ -3048,104 +2884,6 @@ static bool mpd_client_smartpls_update_newest(t_config *config, t_mpd_state *mpd return true; } -static void mpd_client_read_statefiles(t_config *config, t_mpd_state *mpd_state) { - char *crap; - char value[400]; - - LOG_INFO() printf("Reading states\n"); - if (mpd_client_state_get(config, mpd_state, "notificationWeb", value)) { - if (strcmp(value, "true") == 0) - mpd_state->notificationWeb = true; - else - mpd_state->notificationWeb = false; - } - else { - mpd_state->notificationWeb = false; - mpd_client_state_set(config, mpd_state, "notificationWeb", "false"); - } - - if (mpd_client_state_get(config, mpd_state, "notificationPage", value)) { - if (strcmp(value, "true") == 0) - mpd_state->notificationPage = true; - else - mpd_state->notificationPage = false; - } - else { - mpd_state->notificationPage = true; - mpd_client_state_set(config, mpd_state, "notificationPage", "true"); - } - - if (mpd_client_state_get(config, mpd_state, "jukeboxMode", value)) - mpd_state->jukeboxMode = strtol(value, &crap, 10); - else { - mpd_state->jukeboxMode = 0; - mpd_client_state_set(config, mpd_state, "jukeboxMode", "0"); - } - - if (mpd_client_state_get(config, mpd_state, "jukeboxPlaylist", value)) - mpd_state->jukeboxPlaylist = strdup(value); - else { - mpd_state->jukeboxPlaylist = strdup("Database"); - mpd_client_state_set(config, mpd_state, "jukeboxPlaylist", "Database"); - } - - if (mpd_client_state_get(config, mpd_state, "jukeboxQueueLength", value)) - mpd_state->jukeboxQueueLength = strtol(value, &crap, 10); - else { - mpd_state->jukeboxQueueLength = 1; - mpd_client_state_set(config, mpd_state, "jukeboxQueueLength", "1"); - } - - if (mpd_client_state_get(config, mpd_state, "colsQueueCurrent", value)) - mpd_state->colsQueueCurrent = strdup(value); - else { - mpd_state->colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set(config, mpd_state, "colsQueueCurrent", mpd_state->colsQueueCurrent); - } - - if (mpd_client_state_get(config, mpd_state, "colsSearch", value)) - mpd_state->colsSearch = strdup(value); - else { - mpd_state->colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set(config, mpd_state, "colsSearch", mpd_state->colsSearch); - } - - if (mpd_client_state_get(config, mpd_state, "colsBrowseDatabase", value)) - mpd_state->colsBrowseDatabase = strdup(value); - else { - mpd_state->colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]"); - mpd_client_state_set(config, mpd_state, "colsBrowseDatabase", mpd_state->colsBrowseDatabase); - } - - if (mpd_client_state_get(config, mpd_state, "colsBrowsePlaylistsDetail", value)) - mpd_state->colsBrowsePlaylistsDetail = strdup(value); - else { - mpd_state->colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set(config, mpd_state, "colsBrowsePlaylistsDetail", mpd_state->colsBrowsePlaylistsDetail); - } - - if (mpd_client_state_get(config, mpd_state, "colsBrowseFilesystem", value)) - mpd_state->colsBrowseFilesystem = strdup(value); - else { - mpd_state->colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - mpd_client_state_set(config, mpd_state, "colsBrowseFilesystem", mpd_state->colsBrowseFilesystem); - } - - if (mpd_client_state_get(config, mpd_state, "colsPlayback", value)) - mpd_state->colsPlayback = strdup(value); - else { - mpd_state->colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); - mpd_client_state_set(config, mpd_state, "colsPlayback", mpd_state->colsPlayback); - } - - if (mpd_client_state_get(config, mpd_state, "colsQueueLastPlayed", value)) - mpd_state->colsQueueLastPlayed = strdup(value); - else { - mpd_state->colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); - mpd_client_state_set(config, mpd_state, "colsQueueLastPlayed", mpd_state->colsQueueLastPlayed); - } -} - static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state) { char cfgfile[400]; char *line; @@ -3163,10 +2901,14 @@ static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state) } while ((read = getline(&line, &n, fp)) > 0) { value = strtol(line, &data, 10); - if (strlen(data) > 2) + if (strlen(data) > 2) { data = data + 2; - strtok_r(data, "\n", &crap); - list_push(&mpd_state->last_played, data, value); + strtok_r(data, "\n", &crap); + list_push(&mpd_state->last_played, data, value); + } + else { + printf("Error reading last_played line.\n"); + } } fclose(fp); return mpd_state->last_played.length;; diff --git a/src/mympd_api.c b/src/mympd_api.c index 1d0c397..31ccdb5 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -36,36 +36,91 @@ #include "tiny_queue.h" #include "global.h" #include "mympd_api.h" +#include "mpd_client.h" #include "../dist/src/frozen/frozen.h" //private definitions -//static void mympd_api(void *arg_request, void *arg_config); -static void mympd_api(t_work_request *request, t_config *config); -static int mympd_api_syscmd(char *buffer, const char *cmd, t_config *config); +typedef struct t_mympd_state { + //notifications + bool notificationWeb; + bool notificationPage; + + //jukebox + enum jukebox_modes jukeboxMode; + char *jukeboxPlaylist; + int jukeboxQueueLength; + + //columns + char *colsQueueCurrent; + char *colsSearch; + char *colsBrowseDatabase; + char *colsBrowsePlaylistsDetail; + char *colsBrowseFilesystem; + char *colsPlayback; + char *colsQueueLastPlayed; + + //system commands + struct list syscmd_list; +} t_mympd_state; + +static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_request *request); +static bool mympd_api_read_syscmds(t_config *config, t_mympd_state *mympd_state); +static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char *buffer, const char *cmd); +static void mympd_api_read_statefiles(t_config *config, t_mympd_state *mympd_state); +static int mympd_api_put_settings(t_config *config, t_mympd_state *mympd_state, char *buffer); + //public functions void *mympd_api_loop(void *arg_config) { + t_config *config = (t_config *) arg_config; + + //read myMPD states under config.varlibdir + t_mympd_state mympd_state; + mympd_api_read_statefiles(config, &mympd_state); + + //push jukebox settings to mpd_client queue + t_work_request *mpd_client_request = (t_work_request *)malloc(sizeof(t_work_request)); + mpd_client_request->conn_id = 0; + mpd_client_request->cmd_id = MYMPD_API_SETTINGS_SET; + mpd_client_request->length = snprintf(mpd_client_request->data, MAX_SIZE, + "{\"cmd\":\"MYMPD_API_SETTINGS_SET\", \"data\":{\"jukeboxMode\": %d, \"jukeboxPlaylist\": \"%s\", \"jukeboxQueueLength\": %d}}", + mympd_state.jukeboxMode, + mympd_state.jukeboxPlaylist, + mympd_state.jukeboxQueueLength + ); + tiny_queue_push(mpd_client_queue, mpd_client_request); + + //read system command files + list_init(&mympd_state.syscmd_list); + bool rc = mympd_api_read_syscmds(config, &mympd_state); + if (rc == true) { + list_sort_by_value(&mympd_state.syscmd_list, true); + } + while (s_signal_received == 0) { struct t_work_request *request = tiny_queue_shift(mympd_api_queue); - mympd_api(request, arg_config); + mympd_api(config, &mympd_state, request); } + + list_free(&mympd_state.syscmd_list); return NULL; } //private functions -static void mympd_api(t_work_request *request, t_config *config) { +static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_request *request) { //t_work_request *request = (t_work_request *) arg_request; size_t len = 0; char buffer[MAX_SIZE]; int je; char *p_charbuf1; + char p_char[4]; 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, config); + len = mympd_api_syscmd(config, mympd_state, buffer, p_charbuf1); free(p_charbuf1); } } @@ -73,6 +128,95 @@ static void mympd_api(t_work_request *request, t_config *config) { len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); } } + else if (request->cmd_id == MYMPD_API_COLS_SAVE) { + je = json_scanf(request->data, request->length, "{data: {table: %Q}}", &p_charbuf1); + if (je == 1) { + char column_list[800]; + snprintf(column_list, 800, "%.*s", request->length, request->data); + char *cols = strchr(column_list, '['); + 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); + } + else if (strcmp(p_charbuf1, "colsSearch") == 0) { + free(mympd_state->colsSearch); + mympd_state->colsSearch = strdup(cols); + } + else if (strcmp(p_charbuf1, "colsBrowseDatabase") == 0) { + free(mympd_state->colsBrowseDatabase); + mympd_state->colsBrowseDatabase = strdup(cols); + } + else if (strcmp(p_charbuf1, "colsBrowsePlaylistsDetail") == 0) { + free(mympd_state->colsBrowsePlaylistsDetail); + mympd_state->colsBrowsePlaylistsDetail = strdup(cols); + } + else if (strcmp(p_charbuf1, "colsBrowseFilesystem") == 0) { + free(mympd_state->colsBrowseFilesystem); + mympd_state->colsBrowseFilesystem = strdup(cols); + } + else if (strcmp(p_charbuf1, "colsPlayback") == 0) { + free(mympd_state->colsPlayback); + mympd_state->colsPlayback = strdup(cols); + } + else if (strcmp(p_charbuf1, "colsQueueLastPlayed") == 0) { + free(mympd_state->colsQueueLastPlayed); + mympd_state->colsQueueLastPlayed = strdup(cols); + } + else { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown table %s\"}", p_charbuf1); + printf("MYMPD_API_COLS_SAVE: Unknown table %s\n", p_charbuf1); + free(p_charbuf1); + } + if (len == 0) { + if (state_file_write(config, p_charbuf1, cols)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + } + free(p_charbuf1); + } + } + else if (request->cmd_id == MYMPD_API_SETTINGS_SET) { + je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mympd_state->notificationWeb); + if (je == 1) { + if (!state_file_write(config, "notificationWeb", (mympd_state->notificationWeb == true ? "true" : "false"))) + len = snprintf(buffer, 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 (!state_file_write(config, "notificationPage", (mympd_state->notificationPage == true ? "true" : "false"))) + len = snprintf(buffer, 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 (!state_file_write(config, "jukeboxMode", p_char)) + len = snprintf(buffer, 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 (!state_file_write(config, "jukeboxPlaylist", mympd_state->jukeboxPlaylist)) + len = snprintf(buffer, 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 (!state_file_write(config, "jukeboxQueueLength", p_char)) + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); + } + if (len == 0) { + len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + } + //push settings to mpd_client queue + t_work_request *mpd_client_request = (t_work_request *)malloc(sizeof(t_work_request)); + mpd_client_request->conn_id = request->conn_id; + mpd_client_request->length = copy_string(mpd_client_request->data, request->data, MAX_SIZE, request->length); + tiny_queue_push(mpd_client_queue, mpd_client_request); + } + else if (request->cmd_id == MYMPD_API_SETTINGS_GET) { + len = mympd_api_put_settings(config, mympd_state, buffer); + } 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); @@ -92,7 +236,37 @@ static void mympd_api(t_work_request *request, t_config *config) { free(request); } -static int mympd_api_syscmd(char *buffer, const char *cmd, t_config *config) { +static bool mympd_api_read_syscmds(t_config *config, t_mympd_state *mympd_state) { + DIR *dir; + struct dirent *ent; + char dirname[400]; + char *cmd; + long order; + + if (config->syscmds == true) { + snprintf(dirname, 400, "%s/syscmds", config->etcdir); + 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(&mympd_state->syscmd_list, strdup(cmd), order); + } + closedir(dir); + } + else { + printf("ERROR: Can't read syscmds"); + } + } + else { + printf("Syscmds are disabled\n"); + } + return true; +} + +static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char *buffer, const char *cmd) { int len; char filename[400]; char *line; @@ -100,7 +274,7 @@ static int mympd_api_syscmd(char *buffer, const char *cmd, t_config *config) { size_t n = 0; ssize_t read; - const int order = list_get_value(&config->syscmd_list, cmd); + const int order = list_get_value(&mympd_state->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\"}"); @@ -134,3 +308,155 @@ static int mympd_api_syscmd(char *buffer, const char *cmd, t_config *config) { CHECK_RETURN_LEN(); } +static void mympd_api_read_statefiles(t_config *config, t_mympd_state *mympd_state) { + char *crap; + char value[400]; + + LOG_INFO() printf("Reading states\n"); + if (state_file_read(config, "notificationWeb", value)) { + if (strcmp(value, "true") == 0) + mympd_state->notificationWeb = true; + else + mympd_state->notificationWeb = false; + } + else { + mympd_state->notificationWeb = false; + state_file_write(config, "notificationWeb", "false"); + } + + if (state_file_read(config, "notificationPage", value)) { + if (strcmp(value, "true") == 0) + mympd_state->notificationPage = true; + else + mympd_state->notificationPage = false; + } + else { + mympd_state->notificationPage = true; + state_file_write(config, "notificationPage", "true"); + } + + if (state_file_read(config, "jukeboxMode", value)) + mympd_state->jukeboxMode = strtol(value, &crap, 10); + else { + mympd_state->jukeboxMode = JUKEBOX_OFF; + state_file_write(config, "jukeboxMode", "0"); + } + + if (state_file_read(config, "jukeboxPlaylist", value)) + mympd_state->jukeboxPlaylist = strdup(value); + else { + mympd_state->jukeboxPlaylist = strdup("Database"); + state_file_write(config, "jukeboxPlaylist", "Database"); + } + + if (state_file_read(config, "jukeboxQueueLength", value)) + mympd_state->jukeboxQueueLength = strtol(value, &crap, 10); + else { + mympd_state->jukeboxQueueLength = 1; + state_file_write(config, "jukeboxQueueLength", "1"); + } + + if (state_file_read(config, "colsQueueCurrent", value)) + mympd_state->colsQueueCurrent = strdup(value); + else { + mympd_state->colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + state_file_write(config, "colsQueueCurrent", mympd_state->colsQueueCurrent); + } + + if (state_file_read(config, "colsSearch", value)) + mympd_state->colsSearch = strdup(value); + else { + mympd_state->colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + state_file_write(config, "colsSearch", mympd_state->colsSearch); + } + + if (state_file_read(config, "colsBrowseDatabase", value)) + mympd_state->colsBrowseDatabase = strdup(value); + else { + mympd_state->colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]"); + state_file_write(config, "colsBrowseDatabase", mympd_state->colsBrowseDatabase); + } + + if (state_file_read(config, "colsBrowsePlaylistsDetail", value)) + mympd_state->colsBrowsePlaylistsDetail = strdup(value); + else { + mympd_state->colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + state_file_write(config, "colsBrowsePlaylistsDetail", mympd_state->colsBrowsePlaylistsDetail); + } + + if (state_file_read(config, "colsBrowseFilesystem", value)) + mympd_state->colsBrowseFilesystem = strdup(value); + else { + mympd_state->colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + state_file_write(config, "colsBrowseFilesystem", mympd_state->colsBrowseFilesystem); + } + + if (state_file_read(config, "colsPlayback", value)) + mympd_state->colsPlayback = strdup(value); + else { + mympd_state->colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); + state_file_write(config, "colsPlayback", mympd_state->colsPlayback); + } + + if (state_file_read(config, "colsQueueLastPlayed", value)) + mympd_state->colsQueueLastPlayed = strdup(value); + else { + mympd_state->colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); + state_file_write(config, "colsQueueLastPlayed", mympd_state->colsQueueLastPlayed); + } +} + +static int mympd_api_put_settings(t_config *config, t_mympd_state *mympd_state, char *buffer) { + size_t len; + int nr = 0; + struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE); + + len = json_printf(&out, "{type: settings, data: {mpdhost: %Q, mpdport: %d, passwort_set: %B, featSyscmds: %B, featStickers: %B, featSmartpls: %B, " + "featLocalplayer: %B, streamport: %d, streamurl: %Q, featCoverimage: %B, coverimagename: %Q, coverimagesize: %d, featMixramp: %B, " + "maxElementsPerPage: %d, notificationWeb: %B, notificationPage: %B, jukeboxMode: %d, jukeboxPlaylist: %Q, jukeboxQueueLength: %d", + config->mpdhost, + config->mpdport, + config->mpdpass ? "true" : "false", + config->syscmds, + config->stickers, + config->smartpls, + config->localplayer, + config->streamport, + config->streamurl, + config->coverimage, + config->coverimagename, + config->coverimagesize, + config->mixramp, + config->max_elements_per_page, + mympd_state->notificationWeb, + mympd_state->notificationPage, + mympd_state->jukeboxMode, + mympd_state->jukeboxPlaylist, + mympd_state->jukeboxQueueLength + ); + + if (config->syscmds == true) { + len += json_printf(&out, ", syscmds: ["); + nr = 0; + struct node *current = mympd_state->syscmd_list.list; + while (current != NULL) { + if (nr++) + len += json_printf(&out, ","); + len += json_printf(&out, "%Q", current->data); + current = current->next; + } + len += json_printf(&out, "]"); + } + len += json_printf(&out, ", colsQueueCurrent: %s, colsSearch: %s, colsBrowseDatabase: %s, colsBrowsePlaylistsDetail: %s, " + "colsBrowseFilesystem: %s, colsPlayback: %s, colsQueueLastPlayed: %s}}", + mympd_state->colsQueueCurrent, + mympd_state->colsSearch, + mympd_state->colsBrowseDatabase, + mympd_state->colsBrowsePlaylistsDetail, + mympd_state->colsBrowseFilesystem, + mympd_state->colsPlayback, + mympd_state->colsQueueLastPlayed + ); + + CHECK_RETURN_LEN(); +} From 2f3a579d07e330f00bf9a02aa2163e9130a99c0e Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 14 Jan 2019 23:06:07 +0000 Subject: [PATCH 13/59] Feat: startup screen, connect to websocket after getting the settings --- htdocs/css/mympd.css | 4 +++ htdocs/index.html | 23 ++++++++++-- htdocs/js/mympd.js | 85 +++++++++++++++++++++++++++++++++++++------- src/mympd_api.c | 7 ++-- 4 files changed, 100 insertions(+), 19 deletions(-) diff --git a/htdocs/css/mympd.css b/htdocs/css/mympd.css index 39d07db..50f4fd9 100644 --- a/htdocs/css/mympd.css +++ b/htdocs/css/mympd.css @@ -91,6 +91,10 @@ small { display: none !important; } +.unvisible { + visibility: hidden; +} + .pull-right { float: right !important; } diff --git a/htdocs/index.html b/htdocs/index.html index ba1f51a..a46f4be 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -16,7 +16,7 @@ -
+
-
+
@@ -607,7 +607,7 @@
-
+ + + +
-
PlaylistLast modified
Playlist List
#TitleArtistAlbumDuration
TitleArtistAlbumDuration
Search
TitleArtistAlbumDuration
\ No newline at end of file +myMPD
Playback

#TitleArtistAlbumDuration
#TitleArtistAlbumDuration
PlaylistLast modified
Playlist List
#TitleArtistAlbumDuration
TitleArtistAlbumDuration
Search
TitleArtistAlbumDuration
\ No newline at end of file diff --git a/dist/htdocs/js/mympd.min.js b/dist/htdocs/js/mympd.min.js index edba422..8bd72e1 100644 --- a/dist/htdocs/js/mympd.min.js +++ b/dist/htdocs/js/mympd.min.js @@ -7,11 +7,11 @@ $jscomp.polyfill("Array.prototype.includes",function(a){return a?a:function(a,c) $jscomp.checkStringArgs=function(a,b,c){if(null==a)throw new TypeError("The 'this' value for String.prototype."+c+" must not be null or undefined");if(b instanceof RegExp)throw new TypeError("First argument to String.prototype."+c+" must not be a regular expression");return a+""};$jscomp.polyfill("String.prototype.includes",function(a){return a?a:function(a,c){return-1!==$jscomp.checkStringArgs(this,a,"includes").indexOf(a,c||0)}},"es6","es3"); $jscomp.polyfill("String.prototype.repeat",function(a){return a?a:function(a){var b=$jscomp.checkStringArgs(this,null,"repeat");if(0>a||1342177279>>=1)b+=b;return d}},"es6","es3");$jscomp.owns=function(a,b){return Object.prototype.hasOwnProperty.call(a,b)}; $jscomp.assign="function"==typeof Object.assign?Object.assign:function(a,b){for(var c=1;c'+c[a]+"";break}e+=c[a];b+='";e+="/"}a=document.getElementById("BrowseBreadcrumb");a.innerHTML=b;b=a.getElementsByTagName("a");c=b.length;for(a= -0;a'+d+'×'; -domCache.searchCrumb.innerHTML=b;""==domCache.searchstr.value&&1<=c.length&&(a=c[c.length-1].substring(1,c[c.length-1].length-1),b=a.substring(a.indexOf("'")+1,a.length-1),domCache.searchstr.value!=b&&(domCache.searchCrumb.innerHTML+=''),a=a.substring(a.indexOf(" ")+1),a=a.substring(0,a.indexOf(" ")),""==a&&(a="contains"),document.getElementById("searchMatch").value= -a)}else""==domCache.searchstr.value&&""!=app.current.search&&(domCache.searchstr.value=app.current.search);app.last.app!=app.current.app&&""!=app.current.search&&(a=settings["cols"+app.current.app].length,a--,document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML='searchSearching...');2<=domCache.searchstr.value.length||0'+ +d+'×';domCache.searchCrumb.innerHTML=b;""==domCache.searchstr.value&&1<=c.length&&(a=c[c.length-1].substring(1,c[c.length-1].length-1),b=a.substring(a.indexOf("'")+1,a.length-1),domCache.searchstr.value!=b&&(domCache.searchCrumb.innerHTML+=''),a=a.substring(a.indexOf(" ")+1),a=a.substring(0,a.indexOf(" ")), +""==a&&(a="contains"),document.getElementById("searchMatch").value=a)}else""==domCache.searchstr.value&&""!=app.current.search&&(domCache.searchstr.value=app.current.search);app.last.app!=app.current.app&&""!=app.current.search&&(a=settings["cols"+app.current.app].length,a--,document.getElementById("SearchList").getElementsByTagName("tbody")[0].innerHTML='searchSearching...');2<=domCache.searchstr.value.length||0×';this.value="";domCache.searchCrumb.appendChild(b)}else search(this.value);else search(this.value)},!1);domCache.searchCrumb.addEventListener("click",function(a){a.preventDefault(); a.stopPropagation();if("SPAN"==a.target.nodeName)a.target.parentNode.remove(),search("");else if("BUTTON"==a.target.nodeName){var b=decodeURI(a.target.getAttribute("data-filter"));domCache.searchstr.value=b.substring(b.indexOf("'")+1,b.length-1);var c=b.substring(0,b.indexOf(" "));selectTag("searchtags","searchtagsdesc",c);b=b.substring(b.indexOf(" ")+1);b=b.substring(0,b.indexOf(" "));document.getElementById("searchMatch").value=b;a.target.remove();search(domCache.searchstr.value)}},!1);document.getElementById("searchMatch").addEventListener("change", -function(a){search(domCache.searchstr.value)},!1);document.getElementById("SearchList").getElementsByTagName("tr")[0].addEventListener("click",function(a){if(settings.featAdvsearch&&"TH"==a.target.nodeName){var b=a.target.getAttribute("data-col");if("Duration"!=b){var c=document.getElementById("SearchList").getAttribute("data-sort"),d=!0;if(c==b||c=="-"+b)0==c.indexOf("-")?(d=!0,c.substring(1)):d=!1;0==d?(c="-"+b,d=!0):(d=!1,c=b);for(var h=document.getElementById("SearchList").getElementsByClassName("sort-dir"), -l=0;l'+(1==d?"arrow_drop_up":"arrow_drop_down")+"";appRoute()}}},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click", -function(a){hideMenu()},!1);dragAndDropTable("QueueCurrentList");dragAndDropTable("BrowsePlaylistsDetailList");dragAndDropTableHeader("QueueCurrent");dragAndDropTableHeader("QueueLastPlayed");dragAndDropTableHeader("Search");dragAndDropTableHeader("BrowseFilesystem");dragAndDropTableHeader("BrowsePlaylistsDetail");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus",function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!= -a.target.tagName&&"SELECT"!=a.target.tagName&&!a.ctrlKey&&!a.altKey){var b=keymap[a.key];b&&"function"===typeof window[b.cmd]&&(void 0==keymap[a.key].req||1==settings[keymap[a.key].req])&&parseCmd(a,b)}},!1);"serviceWorker"in navigator&&"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.min.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ",a.scope);a.update()},function(a){console.log("ServiceWorker registration failed: ", -a)})});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a;domCache.btnAdd.classList.remove("hide")});domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome?console.log("User accepted the A2HS prompt"):console.log("User dismissed the A2HS prompt"); -deferredPrompt=null})});window.addEventListener("appinstalled",function(a){console.log("myMPD installed as app")});window.addEventListener("beforeunload",function(){socket.onclose=function(){};socket.close();websocketConnected=!1})} +function(a){search(domCache.searchstr.value)},!1);document.getElementById("SearchList").getElementsByTagName("tr")[0].addEventListener("click",function(a){if(settings.featAdvsearch&&"TH"==a.target.nodeName){var b=a.target.getAttribute("data-col");if("Duration"!=b){var c=app.current.sort,d=!0;if(c==b||c=="-"+b)0==c.indexOf("-")?(d=!0,c.substring(1)):d=!1;0==d?(c="-"+b,d=!0):(d=!1,c=b);for(var h=document.getElementById("SearchList").getElementsByClassName("sort-dir"),l=0;l'+(1==d?"arrow_drop_up":"arrow_drop_down")+"";appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.sort+"/"+app.current.search)}}},!1);document.getElementById("BrowseDatabaseByTagDropdown").addEventListener("click",function(a){"BUTTON"==a.target.nodeName&&appGoto(app.current.app,app.current.tab,a.target.getAttribute("data-tag"),"0/"+app.current.filter+ +"/"+app.current.sort+"/"+app.current.search)},!1);document.getElementsByTagName("body")[0].addEventListener("click",function(a){hideMenu()},!1);dragAndDropTable("QueueCurrentList");dragAndDropTable("BrowsePlaylistsDetailList");dragAndDropTableHeader("QueueCurrent");dragAndDropTableHeader("QueueLastPlayed");dragAndDropTableHeader("Search");dragAndDropTableHeader("BrowseFilesystem");dragAndDropTableHeader("BrowsePlaylistsDetail");window.addEventListener("hashchange",appRoute,!1);window.addEventListener("focus", +function(){sendAPI({cmd:"MPD_API_PLAYER_STATE"},parseState)},!1);document.addEventListener("keydown",function(a){if("INPUT"!=a.target.tagName&&"SELECT"!=a.target.tagName&&!a.ctrlKey&&!a.altKey){var b=keymap[a.key];b&&"function"===typeof window[b.cmd]&&(void 0==keymap[a.key].req||1==settings[keymap[a.key].req])&&parseCmd(a,b)}},!1);"serviceWorker"in navigator&&"https"==document.URL.substring(0,5)&&window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.min.js",{scope:"/"}).then(function(a){console.log("ServiceWorker registration successful with scope: ", +a.scope);a.update()},function(a){console.log("ServiceWorker registration failed: ",a)})});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a});window.addEventListener("beforeinstallprompt",function(a){a.preventDefault();deferredPrompt=a;domCache.btnAdd.classList.remove("hide")});domCache.btnAdd.addEventListener("click",function(a){domCache.btnAdd.classList.add("hide");deferredPrompt.prompt();deferredPrompt.userChoice.then(function(a){"accepted"===a.outcome? +console.log("User accepted the A2HS prompt"):console.log("User dismissed the A2HS prompt");deferredPrompt=null})});window.addEventListener("appinstalled",function(a){console.log("myMPD installed as app")});window.addEventListener("beforeunload",function(){socket.onclose=function(){};socket.close();websocketConnected=!1})} function parseCmd(a,b){a.preventDefault();a=b;"string"==typeof b&&(a=JSON.parse(b));if("function"===typeof window[a.cmd])switch(a.cmd){case "sendAPI":sendAPI.apply(null,$jscomp.arrayFromIterable(a.options));break;default:window[a.cmd].apply(null,$jscomp.arrayFromIterable(a.options))}} -function search(a){if(settings.featAdvsearch){for(var b="(",c=domCache.searchCrumb.children,d=0;d=b.length&&(b="");appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+encodeURI(b))}else appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+a)} -function dragAndDropTable(a){var b=document.getElementById(a).getElementsByTagName("tbody")[0];b.addEventListener("dragstart",function(a){"TR"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("id")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();if("TR"==dragEl.nodeName){var b=a.target;"TD"==a.target.nodeName&& -(b=a.target.parentNode);"TR"==b.nodeName&&b.classList.remove("dragover")}},!1);b.addEventListener("dragover",function(a){a.preventDefault();if("TR"==dragEl.nodeName){for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f=b.length&&(b="");appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+app.current.sort+"/"+encodeURI(b))}else appGoto("Search",void 0,void 0,"0/"+app.current.filter+"/"+app.current.sort+ +"/"+a)} +function dragAndDropTable(a){var b=document.getElementById(a).getElementsByTagName("tbody")[0];b.addEventListener("dragstart",function(a){"TR"==a.target.nodeName&&(a.target.classList.add("opacity05"),a.dataTransfer.setDragImage(a.target,0,0),a.dataTransfer.effectAllowed="move",a.dataTransfer.setData("Text",a.target.getAttribute("id")),dragEl=a.target.cloneNode(!0))},!1);b.addEventListener("dragleave",function(a){a.preventDefault();if("TR"==dragEl.nodeName){var b=a.target;"TD"==a.target.nodeName&&(b= +a.target.parentNode);"TR"==b.nodeName&&b.classList.remove("dragover")}},!1);b.addEventListener("dragover",function(a){a.preventDefault();if("TR"==dragEl.nodeName){for(var c=b.getElementsByClassName("dragover"),e=c.length,f=0;f'+settings.colsPlayback[a]+"";document.getElementById("cardPlaybackTags").innerHTML=b}1==settings.mixramp?document.getElementsByClassName("mixramp")[0].style.display="":document.getElementsByClassName("mixramp")[0].style.display="none";!settings.tags.includes("AlbumArtist")&&settings.featTags&&(settings.tags.includes("Artist")?app.apps.Browse.tabs.Database.active="Artist":app.apps.Browse.tabs.Database.active=settings.tags[0]); -document.getElementById("selectJukeboxMode").value=settings.jukeboxMode;document.getElementById("inputJukeboxQueueLength").value=settings.jukeboxQueueLength;0==settings.jukeboxMode||2==settings.jukeboxMode?(document.getElementById("inputJukeboxQueueLength").setAttribute("disabled","disabled"),document.getElementById("selectJukeboxPlaylist").setAttribute("disabled","disabled")):1==settings.jukeboxMode&&(document.getElementById("inputJukeboxQueueLength").removeAttribute("disabled"),document.getElementById("selectJukeboxPlaylist").removeAttribute("disabled")); -settings.featPlaylists?(playlistEl="selectJukeboxPlaylist",sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists)):document.getElementById("selectJukeboxPlaylist").innerHTML="";settings.tags.sort();settings.searchtags.sort();settings.browsetags.sort();filterCols("colsSearch");filterCols("colsQueueCurrent");filterCols("colsQueueLastPlayed");filterCols("colsBrowsePlaylistsDetail");filterCols("colsBrowseFilesystem");filterCols("colsBrowseDatabase"); -filterCols("colsPlayback");settings.featLocalplayer&&(""==settings.streamurl?(settings.mpdstream="http://",settings.mpdstream="127.0.0.1"==settings.mpdhost||"localhost"==settings.mpdhost?settings.mpdstream+window.location.hostname:settings.mpdstream+settings.mpdhost,settings.mpdstream+=":"+settings.streamport+"/"):settings.mpdstream=settings.streamurl);addTagList("BrowseDatabaseByTagDropdown","browsetags");addTagList("searchqueuetags","searchtags");addTagList("searchtags","searchtags");for(a=0;a< -settings.tags.length;a++)app.apps.Browse.tabs.Database.views[settings.tags[a]]={state:"0/-/",scrollPos:0};if(settings.featSyscmds){document.getElementById("mainMenuDropdown");b="";c=settings.syscmdList.length;if(0',a=0;a"+settings.syscmdList[a]+"";document.getElementById("syscmds").innerHTML=b}else document.getElementById("syscmds").innerHTML= -"";dropdownMainMenu=new Dropdown(document.getElementById("mainMenu"));setCols("QueueCurrent");setCols("Search");setCols("QueueLastPlayed");setCols("BrowseFilesystem");setCols("BrowsePlaylistsDetail");setCols("BrowseDatabase",".tblAlbumTitles");setCols("Playback");"Queue"==app.current.app&&"Current"==app.current.tab?getQueue():"Queue"==app.current.app&&"LastPlayed"==app.current.tab?appRoute():"Search"==app.current.app?appRoute():"Browse"==app.current.app&&"Filesystem"==app.current.tab?appRoute():"Browse"== -app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?appRoute():"Browse"==app.current.app&&"Database"==app.current.tab&&""!=app.current.search&&appRoute();settingsParsed=!0} +document.documentElement.style.setProperty("--mympd-coverimagesize",settings.coverimagesize+"px");document.documentElement.style.setProperty("--mympd-backgroundcolor",settings.backgroundcolor);for(var c=0;c'+ +settings.colsPlayback[a]+"";document.getElementById("cardPlaybackTags").innerHTML=b}1==settings.mixramp?document.getElementsByClassName("mixramp")[0].style.display="":document.getElementsByClassName("mixramp")[0].style.display="none";!settings.tags.includes("AlbumArtist")&&settings.featTags&&(settings.tags.includes("Artist")? +app.apps.Browse.tabs.Database.active="Artist":app.apps.Browse.tabs.Database.active=settings.tags[0]);settings.tags.includes("Title")&&(app.apps.Search.state="0/any/Title/");document.getElementById("selectJukeboxMode").value=settings.jukeboxMode;document.getElementById("inputJukeboxQueueLength").value=settings.jukeboxQueueLength;0==settings.jukeboxMode||2==settings.jukeboxMode?(document.getElementById("inputJukeboxQueueLength").setAttribute("disabled","disabled"),document.getElementById("selectJukeboxPlaylist").setAttribute("disabled", +"disabled")):1==settings.jukeboxMode&&(document.getElementById("inputJukeboxQueueLength").removeAttribute("disabled"),document.getElementById("selectJukeboxPlaylist").removeAttribute("disabled"));settings.featPlaylists?(playlistEl="selectJukeboxPlaylist",sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists)):document.getElementById("selectJukeboxPlaylist").innerHTML="";settings.tags.sort();settings.searchtags.sort();settings.browsetags.sort();filterCols("colsSearch"); +filterCols("colsQueueCurrent");filterCols("colsQueueLastPlayed");filterCols("colsBrowsePlaylistsDetail");filterCols("colsBrowseFilesystem");filterCols("colsBrowseDatabase");filterCols("colsPlayback");settings.featLocalplayer&&(""==settings.streamurl?(settings.mpdstream="http://",settings.mpdstream="127.0.0.1"==settings.mpdhost||"localhost"==settings.mpdhost?settings.mpdstream+window.location.hostname:settings.mpdstream+settings.mpdhost,settings.mpdstream+=":"+settings.streamport+"/"):settings.mpdstream= +settings.streamurl);addTagList("BrowseDatabaseByTagDropdown","browsetags");addTagList("searchqueuetags","searchtags");addTagList("searchtags","searchtags");for(a=0;a',a=0;a"+settings.syscmdList[a]+"";document.getElementById("syscmds").innerHTML=b}else document.getElementById("syscmds").innerHTML="";dropdownMainMenu=new Dropdown(document.getElementById("mainMenu"));setCols("QueueCurrent");setCols("Search");setCols("QueueLastPlayed");setCols("BrowseFilesystem");setCols("BrowsePlaylistsDetail");setCols("BrowseDatabase",".tblAlbumTitles");setCols("Playback");"Queue"==app.current.app&&"Current"==app.current.tab?getQueue():"Queue"==app.current.app&& +"LastPlayed"==app.current.tab?appRoute():"Search"==app.current.app?appRoute():"Browse"==app.current.app&&"Filesystem"==app.current.tab?appRoute():"Browse"==app.current.app&&"Playlists"==app.current.tab&&"Detail"==app.current.view?appRoute():"Browse"==app.current.app&&"Database"==app.current.tab&&""!=app.current.search&&appRoute();settingsParsed=!0} function setCols(a,b){var c="",d=settings.tags.slice();0==settings.featTags&&d.push("Title");d.push("Duration");"QueueCurrent"!=a&&"BrowsePlaylistsDetail"!=a&&"QueueLastPlayed"!=a||d.push("Pos");"BrowseFilesystem"==a&&d.push("Type");"QueueLastPlayed"==a&&d.push("LastPlayed");d.sort();for(var e=0;e  '+d[e]+"";document.getElementById(a+"ColsDropdown").firstChild.innerHTML=c;d=document.getElementById("SearchList").getAttribute("data-sort");""==d&&(d=settings.featTags?"Title":"Filename");if("Playback"!=a){c="";for(e=0;e';if("Track"==f||"Pos"==f)f="#";c+=f;"Search"==a&&f==d&&(f=!1,0==d.indexOf("-")&&(f=!0,d=d.substring(1)),c+=''+ -(1==f?"arrow_drop_up":"arrow_drop_down")+"");c+=""}c+="";if(void 0==b)document.getElementById(a+"List").getElementsByTagName("tr")[0].innerHTML=c;else for(a=document.querySelectorAll(b),e=0;e  '+d[e]+"";document.getElementById(a+"ColsDropdown").firstChild.innerHTML=c;d=app.current.sort;"Search"==a&&"0/any/Title/"==app.apps.Search.state&&(d=settings.tags.includes("Title")?"Title":0==settings.featTags?"Filename":"-");if("Playback"!=a){c="";for(e=0;e';if("Track"==f||"Pos"==f)f="#";c+=f;"Search"!=a||f!=d&&"-"+f!=d||(f=!1,0==app.current.sort.indexOf("-")&&(f= +!0),c+=''+(1==f?"arrow_drop_up":"arrow_drop_down")+"");c+=""}c+="";if(void 0==b)document.getElementById(a+"List").getElementsByTagName("tr")[0].innerHTML=c;else for(a=document.querySelectorAll(b),e=0;e"+b[d].name+"

",e.setAttribute("id","current"+b[d].name),e.setAttribute("data-tag",b[d].name),c.appendChild(e))}a={cmd:"MYMPD_API_COLS_SAVE",data:{table:"cols"+ @@ -145,11 +146,11 @@ function setPagination(a,b){var c=app.current.app+(void 0==app.current.tab?"":ap -1==a&&b>=settings.maxElementsPerPage?(document.getElementById(c+e[f]+"Next").removeAttribute("disabled"),document.getElementById(c+e[f]).classList.remove("hide"),document.getElementById(c+"ButtonsBottom").classList.remove("hide")):(document.getElementById(c+e[f]+"Next").setAttribute("disabled","disabled"),document.getElementById(c+e[f]).classList.add("hide"),document.getElementById(c+"ButtonsBottom").classList.add("hide"));0"+settings.tags[d]+'',c=settings.browsetags.includes(settings.tags[d])?c+(''+a.data[settings.tags[d]]+ ""):c+a.data[settings.tags[d]],c+="";var e=a.data.Duration;d=Math.floor(e/60);e-=60*d;c+="Duration"+(d+":"+(10>e?"0":"")+e)+"";c=settings.featLibrary?c+('Filename'+a.data.uri+""):c+("Filename"+a.data.uri+"");1==settings.featStickers&&(c+='StatisticsPlay count'+a.data.playCount+"Skip count"+ a.data.skipCount+"Last played"+(0==a.data.lastPlayed?"never":(new Date(1E3*a.data.lastPlayed)).toUTCString())+'Like
'); -b.getElementsByTagName("tbody")[0].innerHTML=c;setVoteSongBtns(a.data.like,a.data.uri)}function execSyscmd(a){sendAPI({cmd:"MYMPD_API_SYSCMD",data:{cmd:a}})}function playlistDetails(a){document.getElementById("BrowsePlaylistsAllList").classList.add("opacity05");appGoto("Browse","Playlists","Detail","0/-/"+a)}function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_PLAYLIST_RM_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")} +b.getElementsByTagName("tbody")[0].innerHTML=c;setVoteSongBtns(a.data.like,a.data.uri)}function execSyscmd(a){sendAPI({cmd:"MYMPD_API_SYSCMD",data:{cmd:a}})}function playlistDetails(a){document.getElementById("BrowsePlaylistsAllList").classList.add("opacity05");appGoto("Browse","Playlists","Detail","0/-/-/"+a)}function removeFromPlaylist(a,b){b--;sendAPI({cmd:"MPD_API_PLAYLIST_RM_TRACK",data:{uri:a,track:b}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")} function playlistClear(){var a=document.getElementById("BrowsePlaylistsDetailList").getAttribute("data-uri");sendAPI({cmd:"MPD_API_PLAYLIST_CLEAR_AND_LIST",data:{uri:a}});document.getElementById("BrowsePlaylistsDetailList").classList.add("opacity05")} function getAllPlaylists(a){var b=a.data.length,c="";0==a.offset&&("addToPlaylistPlaylist"==playlistEl?c="":"selectJukeboxPlaylist"==playlistEl&&(c=""));for(var d=0;d";0==a.offset?document.getElementById(playlistEl).innerHTML=c:document.getElementById(playlistEl).innerHTML+=c;a.totalEntities> a.returnedEntities&&(a.offset+=settings.maxElementsPerPage,sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:a.offset,filter:"-"}},getAllPlaylists))}function updateSmartPlaylists(){sendAPI({cmd:"MPD_API_SMARTPLS_UPDATE_ALL"})} @@ -196,11 +197,11 @@ function delQueueSong(a,b,c){"range"==a?sendAPI({cmd:"MPD_API_QUEUE_RM_RANGE",da function confirmSettings(){var a=!0,b=document.getElementById("inputCrossfade");if(!b.getAttribute("disabled")){var c=parseInt(b.value);isNaN(c)?(b.classList.add("is-invalid"),a=!1):b.value=c}b=document.getElementById("inputJukeboxQueueLength");c=parseInt(b.value);isNaN(c)?(b.classList.add("is-invalid"),a=!1):0app.current.page&&(app.current.page=0);break;default:app.current.page=a}appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.search)} +function gotoPage(a){switch(a){case "next":app.current.page+=settings.maxElementsPerPage;break;case "prev":app.current.page-=settings.maxElementsPerPage;0>app.current.page&&(app.current.page=0);break;default:app.current.page=a}appGoto(app.current.app,app.current.tab,app.current.view,app.current.page+"/"+app.current.filter+"/"+app.current.sort+"/"+app.current.search)} function saveQueue(){var a=document.getElementById("saveQueueName").value,b=a.replace(/[\w\-]/g,"");""!=a&&""==b?(sendAPI({cmd:"MPD_API_QUEUE_SAVE",data:{plist:a}}),modalSavequeue.hide()):(alert(b),document.getElementById("saveQueueName").classList.add("is-invalid"),document.getElementById("saveQueueFrm").classList.add("was-validated"))} function showNotification(a,b,c,d){1==settings.notificationWeb&&(b=new Notification(a,{icon:"assets/favicon.ico",body:b}),setTimeout(function(a){a.close()},3E3,b));1==settings.notificationPage&&(document.getElementById("alertBox")?b=document.getElementById("alertBox"):(b=document.createElement("div"),b.setAttribute("id","alertBox"),b.addEventListener("click",function(){hideNotification()},!1)),b.classList.remove("alert-success","alert-danger"),b.classList.add("alert","alert-"+d),b.innerHTML="
"+ a+"
"+c+"
",document.getElementsByTagName("main")[0].append(b),document.getElementById("alertBox").classList.add("alertBoxActive"),alertTimeout&&clearTimeout(alertTimeout),alertTimeout=setTimeout(function(){hideNotification()},3E3))}function hideNotification(){document.getElementById("alertBox")&&(document.getElementById("alertBox").classList.remove("alertBoxActive"),setTimeout(function(){var a=document.getElementById("alertBox");a&&a.remove()},600))} @@ -210,7 +211,7 @@ function songChange(a){if("error"!=a.type&&"result"!=a.type){var b=a.data.Title+ a.data[settings.colsPlayback[e]];f.setAttribute("data-name",encodeURI(a.data[settings.colsPlayback[e]]))}if(e=document.getElementById("queueTrackId"+a.data.currentSongId))e.getElementsByTagName("td")[1].innerText=a.data.Title;"play"==playstate&&showNotification(a.data.Title,c,d,"success");lastSong=b;lastSongObj=a}}} function doSetFilterLetter(a){var b=document.getElementById(a+"Letters").getElementsByClassName("active")[0];b&&b.classList.remove("active");b=app.current.filter;"0"==b&&(b="#");document.getElementById(a).innerText="Filter"+("-"!=b?": "+b:"");if("-"!=b){a=document.getElementById(a+"Letters").getElementsByTagName("button");for(var c=a.length,d=0;d=c;c++)b+='";a=document.getElementById(a);a.innerHTML=b;a.addEventListener("click",function(a){switch(a.target.innerText){case "delete":b="-";break;case "#":b="0";break;default:b=a.target.innerText}appGoto(app.current.app, -app.current.tab,app.current.view,"0/"+b+"/"+app.current.search)},!1)}function selectTag(a,b,c){a=document.getElementById(a);var d=a.querySelector(".active");d&&d.classList.remove("active");if(d=a.querySelector("[data-tag="+c+"]"))d.classList.add("active"),document.getElementById(b).innerText=d.innerText} +app.current.tab,app.current.view,"0/"+b+"/"+app.current.sort+"/"+app.current.search)},!1)}function selectTag(a,b,c){a=document.getElementById(a);var d=a.querySelector(".active");d&&d.classList.remove("active");if(d=a.querySelector("[data-tag="+c+"]"))d.classList.add("active"),document.getElementById(b).innerText=d.innerText} function addTagList(a,b){var c="";"searchtags"==b&&(1==settings.featTags&&(c+=''),c+='');for(var d=0;d'+settings[b][d]+"";document.getElementById(a).innerHTML=c} -function gotoTagList(){appGoto(app.current.app,app.current.tab,app.current.view,"0/-/")}function openModal(a){window[a].show()}function openDropdown(a){window[a].toggle()}function focusSearch(){"Queue"==app.current.app?document.getElementById("searchqueuestr").focus():"Search"==app.current.app?domCache.searchstr.focus():appGoto("Search")} +function gotoTagList(){appGoto(app.current.app,app.current.tab,app.current.view,"0/-/-/")}function openModal(a){window[a].show()}function openDropdown(a){window[a].toggle()}function focusSearch(){"Queue"==app.current.app?document.getElementById("searchqueuestr").focus():"Search"==app.current.app?domCache.searchstr.focus():appGoto("Search")} function chVolume(a){a=parseInt(domCache.volumeBar.value)+a;0>a?a=0:100d?"0":""):"")+d+"\u2009m "+(10>a?"0":"")+a+"\u2009s"}function genId(a){return"id"+a.replace(/[^\w\-]/g,"")}appInitStart(); diff --git a/src/list.c b/src/list.c index 9929630..0478a12 100644 --- a/src/list.c +++ b/src/list.c @@ -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 diff --git a/src/main.c b/src/main.c index 4278e3d..8e286b1 100644 --- a/src/main.c +++ b/src/main.c @@ -1,12 +1,9 @@ /* myMPD - (c) 2018 Juergen Mang + (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 + myMPD ist fork of: ympd (c) 2013-2014 Andrew Karpow + ympd 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 diff --git a/src/mpd_client.c b/src/mpd_client.c index c66bb48..d6868c5 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -1091,8 +1091,9 @@ static void mpd_client_mpd_features(t_config *config, t_mpd_state *mpd_state) { mpd_state->feat_advsearch = true; LOG_INFO() printf("Enabling advanced search.\n"); } - else + else { LOG_INFO() printf("Disabling advanced search, depends on mpd >= 0.21.0 and libmpdclient >= 2.17.0.\n"); + } } static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { diff --git a/src/tiny_queue.c b/src/tiny_queue.c index fb22b8b..7bc95ac 100644 --- a/src/tiny_queue.c +++ b/src/tiny_queue.c @@ -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 diff --git a/src/tiny_queue.h b/src/tiny_queue.h index 34d55d7..1ee5993 100644 --- a/src/tiny_queue.h +++ b/src/tiny_queue.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 From 61a69ee06ff9eca44bea03c21a62e7f1a7915fca Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 21 Jan 2019 20:51:24 +0000 Subject: [PATCH 39/59] Fix: don't use if/then blocks --- mkdebug.sh | 2 +- mkrelease.sh | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/mkdebug.sh b/mkdebug.sh index a33fb07..a20f8b4 100755 --- a/mkdebug.sh +++ b/mkdebug.sh @@ -20,7 +20,7 @@ fi echo "Linking pics directory" [ -e $PWD/htdocs/pics ] || ln -s /var/lib/mympd/pics htdocs/ -[ -d debug ] || mkdir debug +install -d debug cd debug cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=DEBUG .. make VERBOSE=1 diff --git a/mkrelease.sh b/mkrelease.sh index 6da14ba..8dc2c0d 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -9,7 +9,10 @@ function minify { DST="$3" ERROR="1" - [ "$DST" -nt "$SRC" ] && return + if [ "$DST" -nt "$SRC" ] + then + return + fi if [ "$TYPE" == "html" ] then @@ -54,7 +57,7 @@ minify html htdocs/index.html dist/htdocs/index.html minify html htdocs/player.html dist/htdocs/player.html echo "Compiling and installing mympd" -[ -d release ] || mkdir release +install -d release cd release cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE .. make From fb14d8454da5aebf9cc35396788ccea035c517b2 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 21 Jan 2019 21:01:25 +0000 Subject: [PATCH 40/59] Fix: minor modifications --- debian/postinst | 2 -- mkrelease.sh | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/debian/postinst b/debian/postinst index 240eb36..b0d85a1 100755 --- a/debian/postinst +++ b/debian/postinst @@ -6,8 +6,6 @@ getent group mympd > /dev/null getent passwd mympd > /dev/null [ "$?" = "2" ] && useradd -r mympd -g mympd -d /var/lib/mympd -s /usr/sbin/nologin - - echo "Trying to link musicdir to library" if [ -f /etc/mpd.conf ] then diff --git a/mkrelease.sh b/mkrelease.sh index 8dc2c0d..91d5417 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -14,7 +14,7 @@ function minify { return fi - if [ "$TYPE" == "html" ] + if [ "$TYPE" = "html" ] then perl -pe 's/^\s*//gm; s/\s*$//gm' $SRC > $DST ERROR="$?" From 38398ee10024232f0d7c5a980a7cc5d902074145 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 21 Jan 2019 21:42:31 +0000 Subject: [PATCH 41/59] Fix: update featurelist and copyright --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2020bc4..07b5f02 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This fork provides a reworked ui based on Bootstrap 4, a modernized backend and - Playlist management - Advanced search (requires mpd >= 0.21.x and libmpdclient >= 2.17) - Jukebox mode (add's random songs / albums from database or playlists to queue) + - AutoPlay - add song to (empty) queue and mpd starts playing - Smart playlists and saved searches - Play statistics and song voting (requires mpd stickers) - Local coverart support (Albums and Streams) @@ -65,15 +66,16 @@ Unix Build Instructions ----------------------- 1. Install dependencies: cmake, libmpdclient (dev), OpenSSL (dev) and Java are available from all major distributions. -2. Build and install: ```cd /path/to/src; ./mkrelease.sh```. +2. Build and install: ```cd /path/to/src; ./mkrelease.sh``` (toplevel directory of myMPD tarball). 3. Link your mpd music directory to ```/usr/share/mympd/htdocs/library``` and put ```folder.jpg``` files in your album directories (mkrelease.sh tries to do this for you). 4. Configure your mpd with http stream output to use the local player. -Run Flags +Run --------- ``` Usage: ./mympd /etc/mypd/mympd.conf ``` +The ```./mkrelease.sh``` script tries to install a systemd service file. If this failes you can copy the ```mympd.service``` file from ```/usr/share/mympd/``` to appropriate distribution specific location. SSL --- @@ -85,6 +87,6 @@ SSL Copyright --------- -ympd: 2013-2014 +myMPD: 2018-2019 -myMPD: 2018 +ympd: 2013-2014 From 33c490a1d71b4e81936226598d29ada99179a6bd Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 22 Jan 2019 00:20:04 +0000 Subject: [PATCH 42/59] Feat: add valingrid build Fix: first errors reported by valingrid --- CMakeLists.txt | 16 ++++++++++------ src/global.c | 3 ++- src/mpd_client.c | 15 +++++++++++++-- src/mympd_api.c | 3 ++- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 664ea5a..0f37e3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,13 +31,17 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -O2 -Wall -fstack-protector -D_FORTIFY_SOURCE=2 -pie -fPIE -DMG_ENABLE_SSL ") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -O1 -Wall -fstack-protector -D_FORTIFY_SOURCE=2 -pie -fPIE -DMG_ENABLE_SSL") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -fsanitize=address \ - -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound \ - -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict \ - -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow \ - -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") +#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -fsanitize=address \ +# -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound \ +# -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict \ +# -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow \ +# -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") + +#for use with valgrind, disable above 2 set commands and enable the following set commands +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -DMG_ENABLE_SSL") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb") #compiler flags for mympd src file(GLOB MYMPD_SRC_FILES "src/*.c") diff --git a/src/global.c b/src/global.c index dc19730..dd98a0e 100644 --- a/src/global.c +++ b/src/global.c @@ -35,7 +35,7 @@ bool state_file_read(t_config *config, const char *name, char *value) { char cfg_file[400]; - char *line; + char *line = NULL; size_t n = 0; ssize_t read; @@ -51,6 +51,7 @@ bool state_file_read(t_config *config, const char *name, char *value) { snprintf(value, 400, "%s", line); LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, value); fclose(fp); + free(line); if (read > 0) return true; else diff --git a/src/mpd_client.c b/src/mpd_client.c index d6868c5..fcd38d2 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -174,13 +174,23 @@ void *mpd_client_loop(void *arg_config) { t_mpd_state mpd_state; mpd_state.conn_state = MPD_DISCONNECTED; mpd_state.timeout = 3000; - mpd_state.last_update_sticker_song_id = -1; + mpd_state.song_id = -1; + mpd_state.next_song_id = -1; mpd_state.last_song_id = -1; + mpd_state.queue_version = 0; + mpd_state.queue_length = 0; + mpd_state.last_update_sticker_song_id = -1; mpd_state.last_last_played_id = -1; + mpd_state.feat_sticker = false; + mpd_state.feat_playlists = false; + mpd_state.feat_tags = false; mpd_state.feat_library = false; + mpd_state.feat_advsearch = false; + mpd_state.feat_smartpls = false; mpd_state.jukeboxMode = JUKEBOX_OFF; mpd_state.jukeboxPlaylist = strdup("Database"); mpd_state.jukeboxQueueLength = 1; + mpd_state.autoPlay = false; list_init(&mpd_state.mpd_tags); list_init(&mpd_state.mympd_tags); list_init(&mpd_state.mympd_searchtags); @@ -2889,7 +2899,7 @@ static bool mpd_client_smartpls_update_newest(t_config *config, t_mpd_state *mpd static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state) { char cfgfile[400]; - char *line; + char *line = NULL; char *data; char *crap; size_t n = 0; @@ -2914,5 +2924,6 @@ static int mpd_client_read_last_played(t_config *config, t_mpd_state *mpd_state) } } fclose(fp); + free(line); return mpd_state->last_played.length;; } diff --git a/src/mympd_api.c b/src/mympd_api.c index ca34e5b..c9f35a5 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -278,7 +278,7 @@ static bool mympd_api_read_syscmds(t_config *config, t_mympd_state *mympd_state) static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char *buffer, const char *cmd) { int len; char filename[400]; - char *line; + char *line = NULL; char *crap; size_t n = 0; ssize_t read; @@ -314,6 +314,7 @@ static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char * len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't execute cmd %s.\"}", cmd); printf("ERROR: Can't execute syscmd \"%s\"\n", cmd); } + free(line); CHECK_RETURN_LEN(); } From f21e96444fc05db47332c84ca36518785ba55f51 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 24 Jan 2019 00:37:04 +0100 Subject: [PATCH 43/59] Fix: some memory leaks --- src/main.c | 1 + src/mpd_client.c | 15 ++++++++++++--- src/mympd_api.c | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index 8e286b1..3e4aa5f 100644 --- a/src/main.c +++ b/src/main.c @@ -295,5 +295,6 @@ int main(int argc, char **argv) { 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 fcd38d2..a94be35 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -105,7 +105,7 @@ typedef struct t_mpd_state { //mympd states enum jukebox_modes jukeboxMode; - const char *jukeboxPlaylist; + char *jukeboxPlaylist; int jukeboxQueueLength; bool autoPlay; @@ -218,7 +218,7 @@ void *mpd_client_loop(void *arg_config) { list_free(&mpd_state.mympd_searchtags); list_free(&mpd_state.mympd_browsetags); list_free(&mpd_state.last_played); - + free(mpd_state.jukeboxPlaylist); return NULL; } @@ -1122,6 +1122,7 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { len = snprintf(buffer, MAX_SIZE, "{\"type\": \"disconnected\"}"); mpd_client_notify(buffer, len); mpd_state->conn_state = MPD_FAILURE; + sleep(3); return; } @@ -1130,6 +1131,7 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd_state->conn)); mpd_client_notify(buffer, len); mpd_state->conn_state = MPD_FAILURE; + sleep(3); return; } @@ -2820,6 +2822,10 @@ static bool mpd_client_smartpls_update_sticker(t_mpd_state *mpd_state, const cha while ((pair = mpd_recv_pair(mpd_state->conn)) != NULL) { if (strcmp(pair->name, "file") == 0) { + if (uri != NULL) { + free(uri); + uri = NULL; + } uri = strdup(pair->value); } else if (strcmp(pair->name, "sticker") == 0) { @@ -2837,7 +2843,10 @@ static bool mpd_client_smartpls_update_sticker(t_mpd_state *mpd_state, const cha mpd_return_pair(mpd_state->conn, pair); } mpd_response_finish(mpd_state->conn); - free(uri); + if (uri != NULL) { + free(uri); + uri = NULL; + } mpd_client_smartpls_clear(mpd_state, playlist); diff --git a/src/mympd_api.c b/src/mympd_api.c index c9f35a5..e4bfe75 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -107,6 +107,14 @@ void *mympd_api_loop(void *arg_config) { } list_free(&mympd_state.syscmd_list); + free(mympd_state.jukeboxPlaylist); + free(mympd_state.colsQueueCurrent); + free(mympd_state.colsSearch); + free(mympd_state.colsBrowseDatabase); + free(mympd_state.colsBrowsePlaylistsDetail); + free(mympd_state.colsBrowseFilesystem); + free(mympd_state.colsPlayback); + free(mympd_state.colsQueueLastPlayed); return NULL; } From 58390034b5417300e977424b265a3d3b99f88b97 Mon Sep 17 00:00:00 2001 From: gregory Date: Thu, 24 Jan 2019 00:11:31 -0500 Subject: [PATCH 44/59] Feat: Docker build --- Dockerfile | 43 ++++++++++++++++++++++++++++++++++++++++++ contrib/docker/init.sh | 11 +++++++++++ mkrelease.sh | 23 ++++++++++++++-------- 3 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 Dockerfile create mode 100644 contrib/docker/init.sh diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8ebfad8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,43 @@ +FROM library/debian:9 as build +RUN apt-get update +RUN apt-get install git meson ninja-build gcc cmake libssl-dev -y +RUN apt-get install openjdk-8-jre-headless perl -y +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 +RUN mkdir /libmpdclient-dist +RUN git clone https://github.com/MusicPlayerDaemon/libmpdclient.git +WORKDIR /libmpdclient +RUN meson . output +RUN ninja -C output +RUN ninja -C output install +RUN mesonconf output -Dprefix=/libmpdclient-dist +RUN ninja -C output +RUN ninja -C output install +WORKDIR / +RUN tar -czvf /libmpdclient-master.tar.gz -C /libmpdclient-dist . +COPY . /myMPD/ +ENV MYMPD_INSTALL_PREFIX=/myMPD-dist/usr +RUN mkdir -p $MYMPD_INSTALL_PREFIX +WORKDIR /myMPD +RUN ./mkrelease.sh +WORKDIR / +RUN tar -czvf /mympd.tar.gz -C /myMPD-dist . + +FROM library/debian:9-slim +ENV MYMPD_COVERIMAGENAME=folder.jpg +ENV MYMPD_MPDHOST=127.0.0.1 +ENV MYMPD_MPDPORT=6600 +ENV MYMPD_SSL=true +ENV MYMPD_LOGLEVEL=1 +RUN apt-get update && apt-get install libssl-dev openssl -y +COPY --from=build /libmpdclient-master.tar.gz / +COPY --from=build /mympd.tar.gz / +COPY --from=build /myMPD/debian/postinst / +WORKDIR / +RUN tar xfv libmpdclient-master.tar.gz -C / +RUN tar xfv mympd.tar.gz -C / +RUN rm libmpdclient-master.tar.gz +RUN rm mympd.tar.gz +COPY contrib/docker/init.sh / +RUN chmod +x /init.sh +ENTRYPOINT ["/init.sh"] diff --git a/contrib/docker/init.sh b/contrib/docker/init.sh new file mode 100644 index 0000000..e4b3e63 --- /dev/null +++ b/contrib/docker/init.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +/postinst +sed -i "s#mpdhost = 127.0.0.1#mpdhost = $MYMPD_MPDHOST#g" /etc/mympd/mympd.conf +sed -i "s#mpdport = 6600#mpdport = $MYMPD_MPDPORT#g" /etc/mympd/mympd.conf +sed -i "s#ssl = true#ssl = $MYMPD_SSL#g" /etc/mympd/mympd.conf +sed -i "s#coverimagename = folder.jpg#coverimagename = $MYMPD_COVERIMAGENAME#g" /etc/mympd/mympd.conf +sed -i "s#loglevel = 1#loglevel = $MYMPD_LOGLEVEL#g" /etc/mympd/mympd.conf +mympd /etc/mympd/mympd.conf + + diff --git a/mkrelease.sh b/mkrelease.sh index 91d5417..fdde4ac 100755 --- a/mkrelease.sh +++ b/mkrelease.sh @@ -1,4 +1,4 @@ -#/bin/sh +#!/bin/bash JAVABIN=$(which java 2> /dev/null) HASJAVA="$?" @@ -18,11 +18,11 @@ function minify { then perl -pe 's/^\s*//gm; s/\s*$//gm' $SRC > $DST ERROR="$?" - elif [ "$TYPE" = "js" ] && [ "$HASJAVA" = "1" ] + elif [ "$TYPE" = "js" ] && [ "$HASJAVA" = "0" ] then $JAVABIN -jar dist/buildtools/closure-compiler.jar $SRC > $DST ERROR="$?" - elif [ "$TYPE" = "css" ] && [ "$HASJAVA" = "1" ] + elif [ "$TYPE" = "css" ] && [ "$HASJAVA" = "0" ] then $JAVABIN -jar dist/buildtools/closure-stylesheets.jar --allow-unrecognized-properties $SRC > $DST ERROR="$?" @@ -59,12 +59,19 @@ minify html htdocs/player.html dist/htdocs/player.html echo "Compiling and installing mympd" install -d release cd release -cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DCMAKE_BUILD_TYPE=RELEASE .. +INSTALL_PREFIX="${MYMPD_INSTALL_PREFIX:-/usr}" +cmake -DCMAKE_INSTALL_PREFIX:PATH=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=RELEASE .. make -sudo make install -cd .. - -sudo debian/postinst +if [ $INSTALL_PREFIX = "/usr" ] +then + sudo make install + cd .. + sudo debian/postinst +else + # Container build implied when $INSTALL_PREFIX != /usr + make install + cd .. +fi if [ -x /usr/bin/cppcheck ] then From 79900f84e13976245807718a27a82c6f585ba76f Mon Sep 17 00:00:00 2001 From: jcorporation Date: Fri, 25 Jan 2019 01:08:38 +0100 Subject: [PATCH 45/59] Fix: valgrind memory still reachable errors in read_statefiles Fix: thread termination of mympd_api_thread --- src/global.c | 49 ----------- src/global.h.in | 32 ++++---- src/main.c | 175 +++++++++++++++++++++++---------------- src/mpd_client.c | 8 +- src/mympd_api.c | 207 ++++++++++++++++++++++++++--------------------- src/tiny_queue.c | 33 +++++--- 6 files changed, 262 insertions(+), 242 deletions(-) diff --git a/src/global.c b/src/global.c index dd98a0e..07c9f9c 100644 --- a/src/global.c +++ b/src/global.c @@ -33,55 +33,6 @@ #include "list.h" #include "global.h" -bool state_file_read(t_config *config, const char *name, char *value) { - char cfg_file[400]; - char *line = NULL; - size_t n = 0; - ssize_t read; - - if (!validate_string(name)) - return false; - snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); - FILE *fp = fopen(cfg_file, "r"); - if (fp == NULL) { - printf("Error opening %s\n", cfg_file); - return false; - } - read = getline(&line, &n, fp); - snprintf(value, 400, "%s", line); - LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, value); - fclose(fp); - free(line); - if (read > 0) - return true; - else - return false; -} - -bool state_file_write(t_config *config, const char *name, const char *value) { - char tmp_file[400]; - char cfg_file[400]; - - if (!validate_string(name)) - return false; - snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); - snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name); - - FILE *fp = fopen(tmp_file, "w"); - if (fp == NULL) { - printf("Error opening %s\n", tmp_file); - return false; - } - fprintf(fp, "%s", value); - fclose(fp); - if (rename(tmp_file, cfg_file) == -1) { - printf("Error renaming file from %s to %s\n", tmp_file, cfg_file); - return false; - } - return true; -} - - bool testdir(const char *name, const char *dirname) { DIR* dir = opendir(dirname); if (dir) { diff --git a/src/global.h.in b/src/global.h.in index 330aa4b..7e43dce 100644 --- a/src/global.h.in +++ b/src/global.h.in @@ -158,33 +158,33 @@ typedef struct t_work_result { //myMPD configuration typedef struct t_config { long mpdport; - const char *mpdhost; - const char *mpdpass; - const char *webport; + char *mpdhost; + char *mpdpass; + char *webport; bool ssl; - const char *sslport; - const char *sslcert; - const char *sslkey; - const char *user; + char *sslport; + char *sslcert; + char *sslkey; + char *user; bool coverimage; - const char *coverimagename; + char *coverimagename; long coverimagesize; bool stickers; bool mixramp; - const char *taglist; - const char *searchtaglist; - const char *browsetaglist; + char *taglist; + char *searchtaglist; + char *browsetaglist; bool smartpls; - const char *varlibdir; - const char *etcdir; + char *varlibdir; + char *etcdir; unsigned long max_elements_per_page; bool syscmds; bool localplayer; long streamport; - const char *streamurl; + char *streamurl; unsigned long last_played_count; long loglevel; - const char *backgroundcolor; + char *backgroundcolor; } t_config; //global functions @@ -194,6 +194,4 @@ 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); int replacechar(char *str, const char orig, const char rep); enum mympd_cmd_ids get_cmd_id(const char *cmd); -bool state_file_read(t_config *config, const char *name, char *value); -bool state_file_write(t_config *config, const char *name, const char *value); #endif diff --git a/src/main.c b/src/main.c index 3e4aa5f..3d6b50f 100644 --- a/src/main.c +++ b/src/main.c @@ -47,6 +47,8 @@ static void signal_handler(int sig_num) { signal(sig_num, signal_handler); // Reinstantiate signal handler s_signal_received = sig_num; + //Wakeup mympd_api_loop + pthread_cond_signal(&mympd_api_queue->wakeup); } static int inihandler(void *user, const char *section, const char *name, const char* value) { @@ -55,61 +57,78 @@ static int inihandler(void *user, const char *section, const char *name, const c #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 - if (MATCH("mpd", "mpdhost")) + if (MATCH("mpd", "mpdhost")) { + free(p_config->mpdhost); p_config->mpdhost = strdup(value); - else if (MATCH("mpd", "mpdport")) + } + else if (MATCH("mpd", "mpdport")) { p_config->mpdport = strtol(value, &crap, 10); - else if (MATCH("mpd", "mpdpass")) + } + else if (MATCH("mpd", "mpdpass")) { + free(p_config->mpdpass); p_config->mpdpass = strdup(value); - else if (MATCH("mpd", "streamport")) + } + else if (MATCH("mpd", "streamport")) { p_config->streamport = strtol(value, &crap, 10); - else if (MATCH("webserver", "webport")) + } + else if (MATCH("webserver", "webport")) { + free(p_config->webport); p_config->webport = strdup(value); - else if (MATCH("webserver", "ssl")) - if (strcmp(value, "true") == 0) - p_config->ssl = true; - else - p_config->ssl = false; - else if (MATCH("webserver", "sslport")) + } + else if (MATCH("webserver", "ssl")) { + p_config->ssl = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("webserver", "sslport")) { + free(p_config->sslport); p_config->sslport = strdup(value); - else if (MATCH("webserver", "sslcert")) + } + else if (MATCH("webserver", "sslcert")) { + free(p_config->sslcert); p_config->sslcert = strdup(value); - else if (MATCH("webserver", "sslkey")) + } + else if (MATCH("webserver", "sslkey")) { + free(p_config->sslkey); p_config->sslkey = strdup(value); - else if (MATCH("mympd", "user")) + } + else if (MATCH("mympd", "user")) { + free(p_config->user); p_config->user = strdup(value); - else if (MATCH("mympd", "coverimage")) - if (strcmp(value, "true") == 0) - p_config->coverimage = true; - else - p_config->coverimage = false; - else if (MATCH("mympd", "coverimagename")) + } + else if (MATCH("mympd", "coverimage")) { + p_config->coverimage = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("mympd", "coverimagename")) { + free(p_config->coverimagename); p_config->coverimagename = strdup(value); - else if (MATCH("mympd", "coverimagesize")) + } + else if (MATCH("mympd", "coverimagesize")) { p_config->coverimagesize = strtol(value, &crap, 10); - else if (MATCH("mympd", "varlibdir")) + } + else if (MATCH("mympd", "varlibdir")) { + free(p_config->varlibdir); p_config->varlibdir = strdup(value); - else if (MATCH("mympd", "stickers")) - if (strcmp(value, "true") == 0) - p_config->stickers = true; - else - p_config->stickers = false; - else if (MATCH("mympd", "smartpls")) - if (strcmp(value, "true") == 0) - p_config->smartpls = true; - else - p_config->smartpls = false; - else if (MATCH("mympd", "mixramp")) - if (strcmp(value, "true") == 0) - p_config->mixramp = true; - else - p_config->mixramp = false; - else if (MATCH("mympd", "taglist")) + } + else if (MATCH("mympd", "stickers")) { + p_config->stickers = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("mympd", "smartpls")) { + p_config->smartpls = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("mympd", "mixramp")) { + p_config->mixramp = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("mympd", "taglist")) { + free(p_config->taglist); p_config->taglist = strdup(value); - else if (MATCH("mympd", "searchtaglist")) - p_config->searchtaglist = strdup(value); - else if (MATCH("mympd", "browsetaglist")) + } + else if (MATCH("mympd", "searchtaglist")) { + free(p_config->searchtaglist); + p_config->searchtaglist = strdup(value); + } + else if (MATCH("mympd", "browsetaglist")) { + free(p_config->browsetaglist); p_config->browsetaglist = strdup(value); + } else if (MATCH("mympd", "max_elements_per_page")) { p_config->max_elements_per_page = strtol(value, &crap, 10); if (p_config->max_elements_per_page > MAX_ELEMENTS_PER_PAGE) { @@ -117,27 +136,29 @@ static int inihandler(void *user, const char *section, const char *name, const c p_config->max_elements_per_page = MAX_ELEMENTS_PER_PAGE; } } - else if (MATCH("mympd", "syscmds")) - if (strcmp(value, "true") == 0) - p_config->syscmds = true; - else - p_config->syscmds = false; - else if (MATCH("mympd", "localplayer")) - if (strcmp(value, "true") == 0) - p_config->localplayer = true; - else - p_config->localplayer = false; - else if (MATCH("mympd", "streamurl")) + else if (MATCH("mympd", "syscmds")) { + p_config->syscmds = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("mympd", "localplayer")) { + p_config->localplayer = strcmp(value, "true") == 0 ? true : false; + } + else if (MATCH("mympd", "streamurl")) { + free(p_config->streamurl); p_config->streamurl = strdup(value); - else if (MATCH("mympd", "last_played_count")) + } + else if (MATCH("mympd", "last_played_count")) { p_config->last_played_count = strtol(value, &crap, 10); - else if (MATCH("mympd", "loglevel")) + } + else if (MATCH("mympd", "loglevel")) { p_config->loglevel = strtol(value, &crap, 10); - else if (MATCH("theme", "backgroundcolor")) + } + else if (MATCH("theme", "backgroundcolor")) { + free(p_config->backgroundcolor); p_config->backgroundcolor = strdup(value); + } else { printf("Unkown config option: %s - %s\n", section, name); - return 0; /* unknown section/name, error */ + return 0; } return 1; @@ -154,26 +175,26 @@ int main(int argc, char **argv) { //mympd config defaults t_config config; - config.mpdhost = "127.0.0.1"; + config.mpdhost = strdup("127.0.0.1"); config.mpdport = 6600; config.mpdpass = NULL; - config.webport = "80"; + config.webport = strdup("80"); config.ssl = true; - config.sslport = "443"; - config.sslcert = "/etc/mympd/ssl/server.pem"; - config.sslkey = "/etc/mympd/ssl/server.key"; - config.user = "mympd"; + config.sslport = strdup("443"); + config.sslcert = strdup("/etc/mympd/ssl/server.pem"); + config.sslkey = strdup("/etc/mympd/ssl/server.key"); + config.user = strdup("mympd"); config.streamport = 8000; - config.streamurl = ""; + config.streamurl = strdup(""); config.coverimage = true; - config.coverimagename = "folder.jpg"; + config.coverimagename = strdup("folder.jpg"); config.coverimagesize = 240; - config.varlibdir = "/var/lib/mympd"; + config.varlibdir = strdup("/var/lib/mympd"); config.stickers = true; config.mixramp = true; - config.taglist = "Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer"; - config.searchtaglist = "Artist,Album,AlbumArtist,Title,Genre,Composer,Performer"; - config.browsetaglist = "Artist,Album,AlbumArtist,Genre,Composer,Performer"; + config.taglist = strdup("Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer"); + config.searchtaglist = strdup("Artist,Album,AlbumArtist,Title,Genre,Composer,Performer"); + config.browsetaglist = strdup("Artist,Album,AlbumArtist,Genre,Composer,Performer"); config.smartpls = true; config.max_elements_per_page = 100; config.last_played_count = 20; @@ -183,7 +204,7 @@ int main(int argc, char **argv) { config.syscmds = false; config.localplayer = true; config.loglevel = 1; - config.backgroundcolor = "#888"; + config.backgroundcolor = strdup("#888"); if (argc == 2) { printf("Starting myMPD %s\n", MYMPD_VERSION); @@ -292,9 +313,25 @@ int main(int argc, char **argv) { //cleanup pthread_join(mpd_client_thread, NULL); pthread_join(web_server_thread, NULL); + pthread_join(mympd_api_thread, NULL); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); + free(config.mpdhost); + free(config.mpdpass); + free(config.webport); + free(config.sslport); + free(config.sslcert); + free(config.sslkey); + free(config.user); + free(config.coverimagename); + free(config.taglist); + free(config.searchtaglist); + free(config.browsetaglist); + free(config.varlibdir); + free(config.etcdir); + free(config.streamurl); + free(config.backgroundcolor); return EXIT_SUCCESS; } diff --git a/src/mpd_client.c b/src/mpd_client.c index a94be35..1981a0c 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -1122,6 +1122,7 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { len = snprintf(buffer, MAX_SIZE, "{\"type\": \"disconnected\"}"); mpd_client_notify(buffer, len); mpd_state->conn_state = MPD_FAILURE; + mpd_connection_free(mpd_state->conn); sleep(3); return; } @@ -1160,8 +1161,9 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { // fall through case MPD_DISCONNECT: case MPD_RECONNECT: - if (mpd_state->conn != NULL) + if (mpd_state->conn != NULL) { mpd_connection_free(mpd_state->conn); + } mpd_state->conn = NULL; mpd_state->conn_state = MPD_DISCONNECTED; break; @@ -2557,7 +2559,9 @@ static int mpd_client_put_stats(t_mpd_state *mpd_state, char *buffer) { static void mpd_client_disconnect(t_config *config, t_mpd_state *mpd_state) { mpd_state->conn_state = MPD_DISCONNECT; -// mpd_client_idle(config, mpd_state); + if (mpd_state->conn != NULL) { + mpd_connection_free(mpd_state->conn); + } } static int mpd_client_smartpls_put(t_config *config, char *buffer, const char *playlist) { diff --git a/src/mympd_api.c b/src/mympd_api.c index e4bfe75..60ae397 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -69,6 +69,10 @@ static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_reque static bool mympd_api_read_syscmds(t_config *config, t_mympd_state *mympd_state); static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char *buffer, const char *cmd); static void mympd_api_read_statefiles(t_config *config, t_mympd_state *mympd_state); +static char *state_file_rw_string(t_config *config, const char *name, const char *def_value); +static bool state_file_rw_bool(t_config *config, const char *name, const bool def_value); +static long state_file_rw_long(t_config *config, const char *name, const long def_value); +static bool state_file_write(t_config *config, const char *name, const char *value); static int mympd_api_put_settings(t_config *config, t_mympd_state *mympd_state, char *buffer); @@ -327,112 +331,129 @@ static int mympd_api_syscmd(t_config *config, t_mympd_state *mympd_state, char * } static void mympd_api_read_statefiles(t_config *config, t_mympd_state *mympd_state) { - char *crap; - char value[400]; - LOG_INFO() printf("Reading states\n"); - if (state_file_read(config, "notificationWeb", value)) { - if (strcmp(value, "true") == 0) - mympd_state->notificationWeb = true; - else - mympd_state->notificationWeb = false; - } - else { - mympd_state->notificationWeb = false; - state_file_write(config, "notificationWeb", "false"); - } + mympd_state->notificationWeb = state_file_rw_bool(config, "notificationWeb", false); + mympd_state->notificationPage = state_file_rw_bool(config, "notificationPage", true); + mympd_state->autoPlay = state_file_rw_bool(config, "autoPlay", false); + mympd_state->jukeboxMode = state_file_rw_long(config, "jukeboxMode", JUKEBOX_OFF); + mympd_state->jukeboxPlaylist = state_file_rw_string(config, "jukeboxPlaylist", "Database"); + mympd_state->jukeboxQueueLength = state_file_rw_long(config, "jukeboxQueueLength", 1); + mympd_state->colsQueueCurrent = state_file_rw_string(config, "colsQueueCurrent", "[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mympd_state->colsSearch = state_file_rw_string(config, "colsSearch", "[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mympd_state->colsBrowseDatabase = state_file_rw_string(config, "colsBrowseDatabase", "[\"Track\",\"Title\",\"Duration\"]"); + mympd_state->colsBrowsePlaylistsDetail = state_file_rw_string(config, "colsBrowsePlaylistsDetail", "[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mympd_state->colsBrowseFilesystem = state_file_rw_string(config, "colsBrowseFilesystem", "[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); + mympd_state->colsPlayback = state_file_rw_string(config, "colsPlayback", "[\"Artist\",\"Album\"]"); + mympd_state->colsQueueLastPlayed = state_file_rw_string(config, "colsQueueLastPlayed", "[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); +} - if (state_file_read(config, "notificationPage", value)) { - if (strcmp(value, "true") == 0) - mympd_state->notificationPage = true; - else - mympd_state->notificationPage = false; +static char *state_file_rw_string(t_config *config, const char *name, const char *def_value) { + char cfg_file[400]; + char *line = NULL; + size_t n = 0; + ssize_t read; + + if (!validate_string(name)) { + return NULL; + } + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + FILE *fp = fopen(cfg_file, "r"); + if (fp == NULL) { + printf("Error opening %s\n", cfg_file); + state_file_write(config, name, def_value); + return NULL; + } + read = getline(&line, &n, fp); + if (read > 0) { + LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, line); + } + fclose(fp); + if (read > 0) { + return line; } else { - mympd_state->notificationPage = true; - state_file_write(config, "notificationPage", "true"); + free(line); + return NULL; } +} - if (state_file_read(config, "autoPlay", value)) { - if (strcmp(value, "true") == 0) - mympd_state->autoPlay = true; - else - mympd_state->autoPlay = false; - } - else { - mympd_state->autoPlay = false; - state_file_write(config, "autoPlay", "false"); - } +static bool state_file_rw_bool(t_config *config, const char *name, const bool def_value) { + char cfg_file[400]; + char *line = NULL; + size_t n = 0; + ssize_t read; + bool value = def_value; - if (state_file_read(config, "jukeboxMode", value)) - mympd_state->jukeboxMode = strtol(value, &crap, 10); - else { - mympd_state->jukeboxMode = JUKEBOX_OFF; - state_file_write(config, "jukeboxMode", "0"); + if (!validate_string(name)) + return def_value; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + FILE *fp = fopen(cfg_file, "r"); + if (fp == NULL) { + printf("Error opening %s\n", cfg_file); + state_file_write(config, name, def_value == true ? "true" : "false"); + return def_value; } + read = getline(&line, &n, fp); + if (read > 0) { + LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, line); + value = strcmp(line, "true") == 0 ? true : false; + } + fclose(fp); + free(line); + return value; +} - if (state_file_read(config, "jukeboxPlaylist", value)) - mympd_state->jukeboxPlaylist = strdup(value); - else { - mympd_state->jukeboxPlaylist = strdup("Database"); - state_file_write(config, "jukeboxPlaylist", "Database"); +static long state_file_rw_long(t_config *config, const char *name, const long def_value) { + char cfg_file[400]; + char *line = NULL; + char *crap; + size_t n = 0; + ssize_t read; + long value = def_value; + + if (!validate_string(name)) + return def_value; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + FILE *fp = fopen(cfg_file, "r"); + if (fp == NULL) { + printf("Error opening %s\n", cfg_file); + char p_value[65]; + snprintf(p_value, 65, "%ld", def_value); + state_file_write(config, name, p_value); + return def_value; } + read = getline(&line, &n, fp); + if (read > 0) { + LOG_DEBUG() fprintf(stderr, "DEBUG: State %s: %s\n", name, line); + value = strtol(line, &crap, 10); + } + fclose(fp); + free(line); + return value; +} - if (state_file_read(config, "jukeboxQueueLength", value)) - mympd_state->jukeboxQueueLength = strtol(value, &crap, 10); - else { - mympd_state->jukeboxQueueLength = 1; - state_file_write(config, "jukeboxQueueLength", "1"); - } - - if (state_file_read(config, "colsQueueCurrent", value)) - mympd_state->colsQueueCurrent = strdup(value); - else { - mympd_state->colsQueueCurrent = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - state_file_write(config, "colsQueueCurrent", mympd_state->colsQueueCurrent); - } - - if (state_file_read(config, "colsSearch", value)) - mympd_state->colsSearch = strdup(value); - else { - mympd_state->colsSearch = strdup("[\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - state_file_write(config, "colsSearch", mympd_state->colsSearch); - } - - if (state_file_read(config, "colsBrowseDatabase", value)) - mympd_state->colsBrowseDatabase = strdup(value); - else { - mympd_state->colsBrowseDatabase = strdup("[\"Track\",\"Title\",\"Duration\"]"); - state_file_write(config, "colsBrowseDatabase", mympd_state->colsBrowseDatabase); - } - - if (state_file_read(config, "colsBrowsePlaylistsDetail", value)) - mympd_state->colsBrowsePlaylistsDetail = strdup(value); - else { - mympd_state->colsBrowsePlaylistsDetail = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - state_file_write(config, "colsBrowsePlaylistsDetail", mympd_state->colsBrowsePlaylistsDetail); - } - - if (state_file_read(config, "colsBrowseFilesystem", value)) - mympd_state->colsBrowseFilesystem = strdup(value); - else { - mympd_state->colsBrowseFilesystem = strdup("[\"Type\",\"Title\",\"Artist\",\"Album\",\"Duration\"]"); - state_file_write(config, "colsBrowseFilesystem", mympd_state->colsBrowseFilesystem); - } - - if (state_file_read(config, "colsPlayback", value)) - mympd_state->colsPlayback = strdup(value); - else { - mympd_state->colsPlayback = strdup("[\"Artist\",\"Album\",\"Genre\"]"); - state_file_write(config, "colsPlayback", mympd_state->colsPlayback); - } - if (state_file_read(config, "colsQueueLastPlayed", value)) - mympd_state->colsQueueLastPlayed = strdup(value); - else { - mympd_state->colsQueueLastPlayed = strdup("[\"Pos\",\"Title\",\"Artist\",\"Album\",\"LastPlayed\"]"); - state_file_write(config, "colsQueueLastPlayed", mympd_state->colsQueueLastPlayed); +static bool state_file_write(t_config *config, const char *name, const char *value) { + char tmp_file[400]; + char cfg_file[400]; + + if (!validate_string(name)) + return false; + snprintf(cfg_file, 400, "%s/state/%s", config->varlibdir, name); + snprintf(tmp_file, 400, "%s/tmp/%s", config->varlibdir, name); + + FILE *fp = fopen(tmp_file, "w"); + if (fp == NULL) { + printf("Error opening %s\n", tmp_file); + return false; } + fprintf(fp, "%s", value); + fclose(fp); + if (rename(tmp_file, cfg_file) == -1) { + printf("Error renaming file from %s to %s\n", tmp_file, cfg_file); + return false; + } + return true; } static int mympd_api_put_settings(t_config *config, t_mympd_state *mympd_state, char *buffer) { diff --git a/src/tiny_queue.c b/src/tiny_queue.c index 7bc95ac..c7f9532 100644 --- a/src/tiny_queue.c +++ b/src/tiny_queue.c @@ -152,19 +152,28 @@ void *tiny_queue_shift(tiny_queue_t *queue, int timeout) { } } //queue has entry - struct tiny_msg_t* current_head = queue->head; - void *data = current_head->data; - if (queue->head == queue->tail) { - queue->head = queue->tail = NULL; + if (queue->head != NULL) { + struct tiny_msg_t* current_head = queue->head; + void *data = current_head->data; + if (queue->head == queue->tail) { + queue->head = queue->tail = NULL; + } + else { + queue->head = queue->head->next; + } + free(current_head); + queue->length--; + rc = pthread_mutex_unlock(&queue->mutex); + if (rc != 0) { + printf("Error in pthread_mutex_unlock: %d\n", rc); + } + return data; } else { - queue->head = queue->head->next; + rc = pthread_mutex_unlock(&queue->mutex); + if (rc != 0) { + printf("Error in pthread_mutex_unlock: %d\n", rc); + } + return NULL; } - free(current_head); - queue->length--; - rc = pthread_mutex_unlock(&queue->mutex); - if (rc != 0) { - printf("Error in pthread_mutex_unlock: %d\n", rc); - } - return data; } From 90e984df8cb10335b687be36a837e7bf589bb5c1 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 00:34:18 +0000 Subject: [PATCH 46/59] Fix: save mympd config on the heap --- src/main.c | 117 +++++++++++++++++++++++++++-------------------------- 1 file changed, 59 insertions(+), 58 deletions(-) diff --git a/src/main.c b/src/main.c index 3d6b50f..c1fbb12 100644 --- a/src/main.c +++ b/src/main.c @@ -174,43 +174,43 @@ int main(int argc, char **argv) { srand((unsigned int)time(NULL)); //mympd config defaults - t_config config; - config.mpdhost = strdup("127.0.0.1"); - config.mpdport = 6600; - config.mpdpass = NULL; - config.webport = strdup("80"); - config.ssl = true; - config.sslport = strdup("443"); - config.sslcert = strdup("/etc/mympd/ssl/server.pem"); - config.sslkey = strdup("/etc/mympd/ssl/server.key"); - config.user = strdup("mympd"); - config.streamport = 8000; - config.streamurl = strdup(""); - config.coverimage = true; - config.coverimagename = strdup("folder.jpg"); - config.coverimagesize = 240; - config.varlibdir = strdup("/var/lib/mympd"); - config.stickers = true; - config.mixramp = true; - config.taglist = strdup("Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer"); - config.searchtaglist = strdup("Artist,Album,AlbumArtist,Title,Genre,Composer,Performer"); - config.browsetaglist = strdup("Artist,Album,AlbumArtist,Genre,Composer,Performer"); - config.smartpls = true; - config.max_elements_per_page = 100; - config.last_played_count = 20; + t_config *config = (t_config *)malloc(sizeof(t_config)); + config->mpdhost = strdup("127.0.0.1"); + config->mpdport = 6600; + config->mpdpass = NULL; + config->webport = strdup("80"); + config->ssl = true; + config->sslport = strdup("443"); + config->sslcert = strdup("/etc/mympd/ssl/server.pem"); + config->sslkey = strdup("/etc/mympd/ssl/server.key"); + config->user = strdup("mympd"); + config->streamport = 8000; + config->streamurl = strdup(""); + config->coverimage = true; + config->coverimagename = strdup("folder.jpg"); + config->coverimagesize = 240; + config->varlibdir = strdup("/var/lib/mympd"); + config->stickers = true; + config->mixramp = true; + config->taglist = strdup("Artist,Album,AlbumArtist,Title,Track,Genre,Date,Composer,Performer"); + config->searchtaglist = strdup("Artist,Album,AlbumArtist,Title,Genre,Composer,Performer"); + config->browsetaglist = strdup("Artist,Album,AlbumArtist,Genre,Composer,Performer"); + config->smartpls = true; + config->max_elements_per_page = 100; + config->last_played_count = 20; char *etcdir = strdup(argv[1]); - config.etcdir = strdup(dirname(etcdir)); + config->etcdir = strdup(dirname(etcdir)); free(etcdir); - config.syscmds = false; - config.localplayer = true; - config.loglevel = 1; - config.backgroundcolor = strdup("#888"); + config->syscmds = false; + config->localplayer = true; + config->loglevel = 1; + config->backgroundcolor = strdup("#888"); if (argc == 2) { printf("Starting myMPD %s\n", MYMPD_VERSION); printf("Libmpdclient %i.%i.%i\n", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); printf("Parsing config file: %s\n", argv[1]); - if (ini_parse(argv[1], inihandler, &config) < 0) { + if (ini_parse(argv[1], inihandler, config) < 0) { printf("Can't load config file \"%s\"\n", argv[1]); return EXIT_FAILURE; } @@ -229,10 +229,10 @@ int main(int argc, char **argv) { #ifdef DEBUG printf("Debug flag enabled, setting loglevel to debug\n"); - config.loglevel = 3; + config->loglevel = 3; #endif - printf("Setting loglevel to %ld\n", config.loglevel); - loglevel = config.loglevel; + printf("Setting loglevel to %ld\n", config->loglevel); + loglevel = config->loglevel; signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); @@ -241,15 +241,15 @@ int main(int argc, char **argv) { //init webserver struct mg_mgr mgr; - if (!web_server_init(&mgr, &config)) { + if (!web_server_init(&mgr, config)) { return EXIT_FAILURE; } //drop privileges - if (config.user != NULL) { - printf("Droping privileges to %s\n", config.user); + if (config->user != NULL) { + printf("Droping privileges to %s\n", config->user); struct passwd *pw; - if ((pw = getpwnam(config.user)) == NULL) { + if ((pw = getpwnam(config->user)) == NULL) { printf("getpwnam() failed, unknown user\n"); web_server_free(&mgr); return EXIT_FAILURE; @@ -281,31 +281,31 @@ int main(int argc, char **argv) { snprintf(testdirname, 400, "%s/library", DOC_ROOT); if (!testdir("Link to mpd music_directory", testdirname)) { printf("Disabling coverimage support\n"); - config.coverimage = false; + config->coverimage = false; } - snprintf(testdirname, 400, "%s/tmp", config.varlibdir); + snprintf(testdirname, 400, "%s/tmp", config->varlibdir); if (!testdir("Temp dir", testdirname)) return EXIT_FAILURE; - snprintf(testdirname, 400, "%s/smartpls", config.varlibdir); + snprintf(testdirname, 400, "%s/smartpls", config->varlibdir); if (!testdir("Smartpls dir", testdirname)) return EXIT_FAILURE; - snprintf(testdirname, 400, "%s/state", config.varlibdir); + snprintf(testdirname, 400, "%s/state", config->varlibdir); if (!testdir("State dir", testdirname)) return EXIT_FAILURE; //Create working threads pthread_t mpd_client_thread, web_server_thread, mympd_api_thread; //mpd connection - pthread_create(&mpd_client_thread, NULL, mpd_client_loop, &config); + pthread_create(&mpd_client_thread, NULL, mpd_client_loop, config); pthread_setname_np(mpd_client_thread, "mympd_mpdclient"); //webserver pthread_create(&web_server_thread, NULL, web_server_loop, &mgr); pthread_setname_np(web_server_thread, "mympd_webserver"); //mympd api - pthread_create(&mympd_api_thread, NULL, mympd_api_loop, &config); + pthread_create(&mympd_api_thread, NULL, mympd_api_loop, config); pthread_setname_np(mympd_api_thread, "mympd_mympdapi"); //Outsourced all work to separate threads, do nothing... @@ -317,21 +317,22 @@ int main(int argc, char **argv) { tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); - free(config.mpdhost); - free(config.mpdpass); - free(config.webport); - free(config.sslport); - free(config.sslcert); - free(config.sslkey); - free(config.user); - free(config.coverimagename); - free(config.taglist); - free(config.searchtaglist); - free(config.browsetaglist); - free(config.varlibdir); - free(config.etcdir); - free(config.streamurl); - free(config.backgroundcolor); + free(config->mpdhost); + free(config->mpdpass); + free(config->webport); + free(config->sslport); + free(config->sslcert); + free(config->sslkey); + free(config->user); + free(config->coverimagename); + free(config->taglist); + free(config->searchtaglist); + free(config->browsetaglist); + free(config->varlibdir); + free(config->etcdir); + free(config->streamurl); + free(config->backgroundcolor); + free(config); return EXIT_SUCCESS; } From 26eeffb3aece6fb824aecc1fb9a4a2ff6e777a69 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 00:35:03 +0000 Subject: [PATCH 47/59] Disable build for valingrid --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f37e3c..9d05450 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,17 +31,17 @@ include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${LIBMPDCLIENT_I include(CheckCSourceCompiles) -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -O1 -Wall -fstack-protector -D_FORTIFY_SOURCE=2 -pie -fPIE -DMG_ENABLE_SSL") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -O1 -Wall -fstack-protector -D_FORTIFY_SOURCE=2 -pie -fPIE -DMG_ENABLE_SSL") -#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -fsanitize=address \ -# -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound \ -# -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict \ -# -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow \ -# -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -fsanitize=address \ + -fsanitize=undefined -fsanitize=shift -fsanitize=integer-divide-by-zero -fsanitize=unreachable -fsanitize=vla-bound \ + -fsanitize=null -fsanitize=return -fsanitize=signed-integer-overflow -fsanitize=bounds -fsanitize=bounds-strict \ + -fsanitize=alignment -fsanitize=object-size -fsanitize=float-divide-by-zero -fsanitize=float-cast-overflow \ + -fsanitize=nonnull-attribute -fsanitize=returns-nonnull-attribute -fsanitize=bool -fsanitize=enum -fsanitize=vptr -static-libasan") #for use with valgrind, disable above 2 set commands and enable the following set commands -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -DMG_ENABLE_SSL") -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -DMG_ENABLE_SSL") +#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb") #compiler flags for mympd src file(GLOB MYMPD_SRC_FILES "src/*.c") From d1959e841895a26cda8f6807809c8332186b479c Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 20:05:21 +0100 Subject: [PATCH 48/59] Fix: memory leaks reported by valingrid --- src/mpd_client.c | 82 ++++++++++++++++++++++++++++++------------------ src/mympd_api.c | 5 ++- src/web_server.c | 3 +- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index 1981a0c..e0cc704 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -60,14 +60,18 @@ while (current_tag != NULL) { \ if (tagnr ++) \ len += json_printf(&out, ","); \ - len += json_printf(&out, "%Q: %Q", current_tag->data, mpd_client_get_tag(song, mpd_tag_name_parse(current_tag->data))); \ + char *value = mpd_client_get_tag(song, mpd_tag_name_parse(current_tag->data)); \ + len += json_printf(&out, "%Q: %Q", current_tag->data, value); \ + if (strncmp(value, "-", 1) == 0) { free(value); } \ current_tag = current_tag->next; \ } \ len += json_printf(&out, ", Duration: %d, uri: %Q", mpd_song_get_duration(song), mpd_song_get_uri(song)); \ } while (0) #define PUT_MIN_SONG_TAGS() do { \ - len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", mpd_client_get_tag(song, MPD_TAG_TITLE), mpd_song_get_duration(song), mpd_song_get_uri(song)); \ + char *value = mpd_client_get_tag(song, MPD_TAG_TITLE); \ + len += json_printf(&out, "Title: %Q, Duration: %d, uri: %Q", value, mpd_song_get_duration(song), mpd_song_get_uri(song)); \ + if (strncmp(value, "-", 1) == 0) { free(value); } \ } while (0) @@ -258,7 +262,12 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r case MYMPD_API_SETTINGS_SET: //only update mpd_state, already saved in mympd_api.c je = json_scanf(request->data, request->length, "{data: {jukeboxMode: %d}}", &mpd_state->jukeboxMode); - je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mpd_state->jukeboxPlaylist); + je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &p_charbuf1); + if (je == 1) { + free(mpd_state->jukeboxPlaylist); + mpd_state->jukeboxPlaylist = p_charbuf1; + p_charbuf1 = NULL; + } je = json_scanf(request->data, request->length, "{data: {jukeboxQueueLength: %d}}", &mpd_state->jukeboxQueueLength); je = json_scanf(request->data, request->length, "{data: {autoPlay: %B}}", &mpd_state->autoPlay); //set mpd options @@ -1405,12 +1414,15 @@ static char *mpd_client_get_tag(struct mpd_song const *song, const enum mpd_tag_ char *str; str = (char *)mpd_song_get_tag(song, tag, 0); if (str == NULL) { - if (tag == MPD_TAG_TITLE) + if (tag == MPD_TAG_TITLE) { str = basename((char *)mpd_song_get_uri(song)); - else if (tag == MPD_TAG_ALBUM_ARTIST) + } + else if (tag == MPD_TAG_ALBUM_ARTIST) { str = (char *)mpd_song_get_tag(song, MPD_TAG_ARTIST, 0); - else + } + else { str = strdup("-"); + } } return str; } @@ -1648,6 +1660,7 @@ static int mpd_client_put_settings(t_mpd_state *mpd_state, char *buffer) { } struct mpd_pair *pair; while ((pair = mpd_recv_pair(mpd_state->conn)) != NULL) { + free(replaygain); replaygain = strdup(pair->value); mpd_return_pair(mpd_state->conn, pair); } @@ -1952,7 +1965,6 @@ static int mpd_client_put_queue(t_config *config, t_mpd_state *mpd_state, char * static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char *buffer, const char *path, const unsigned int offset, const char *filter) { struct mpd_entity *entity; - const struct mpd_playlist *pl; unsigned entity_count = 0; unsigned entities_returned = 0; const char *entityName; @@ -1967,20 +1979,19 @@ static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char len = json_printf(&out, "{type: browse, data: ["); while ((entity = mpd_recv_entity(mpd_state->conn)) != NULL) { - const struct mpd_song *song; - const struct mpd_directory *dir; entity_count++; if (entity_count > offset && entity_count <= offset + config->max_elements_per_page) { switch (mpd_entity_get_type(entity)) { - case MPD_ENTITY_TYPE_UNKNOWN: + case MPD_ENTITY_TYPE_UNKNOWN: { entity_count--; break; - case MPD_ENTITY_TYPE_SONG: - song = mpd_entity_get_song(entity); + } + case MPD_ENTITY_TYPE_SONG: { + const struct mpd_song *song = mpd_entity_get_song(entity); entityName = mpd_client_get_tag(song, MPD_TAG_TITLE); if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, entityName, 1) == 0 || - (strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 ) - ) { + (strncmp(filter, "0", 1) == 0 && isalpha(*entityName) == 0 )) + { if (entities_returned++) len += json_printf(&out, ","); len += json_printf(&out, "{Type: song, "); @@ -1990,43 +2001,49 @@ static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char PUT_MIN_SONG_TAGS(); len += json_printf(&out, "}"); - } else { + } + else { entity_count--; } + if (strncmp(entityName, "-", 1) == 0) { free((void *) entityName); } break; - - case MPD_ENTITY_TYPE_DIRECTORY: - dir = mpd_entity_get_directory(entity); + } + case MPD_ENTITY_TYPE_DIRECTORY: { + const struct mpd_directory *dir = mpd_entity_get_directory(entity); entityName = mpd_directory_get_path(dir); char *dirName = strrchr(entityName, '/'); - if (dirName != NULL) + if (dirName != NULL) { dirName++; - else - dirName = strdup(entityName); + } + else { + dirName = (char *) entityName; + } if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, dirName, 1) == 0 || - (strncmp(filter, "0", 1) == 0 && isalpha(*dirName) == 0 ) - ) { + (strncmp(filter, "0", 1) == 0 && isalpha(*dirName) == 0 )) + { if (entities_returned++) len += json_printf(&out, ","); len += json_printf(&out, "{Type: dir, uri: %Q, name: %Q}", entityName, dirName ); - } else { + } + else { entity_count--; } + dirName = NULL; break; - - case MPD_ENTITY_TYPE_PLAYLIST: - pl = mpd_entity_get_playlist(entity); + } + case MPD_ENTITY_TYPE_PLAYLIST: { + const struct mpd_playlist *pl = mpd_entity_get_playlist(entity); entityName = mpd_playlist_get_path(pl); char *plName = strrchr(entityName, '/'); if (plName != NULL) { plName++; } else { - plName = strdup(entityName); + plName = (char *) entityName; } if (strncmp(filter, "-", 1) == 0 || strncasecmp(filter, plName, 1) == 0 || (strncmp(filter, "0", 1) == 0 && isalpha(*plName) == 0 )) @@ -2054,7 +2071,9 @@ static int mpd_client_put_browse(t_config *config, t_mpd_state *mpd_state, char } else { entity_count--; } + plName = NULL; break; + } } } mpd_entity_free(entity); @@ -2161,7 +2180,9 @@ static int mpd_client_put_songs_in_album(t_config *config, t_mpd_state *mpd_stat len += json_printf(&out, ", "); else { mpd_client_get_cover(config, mpd_state, mpd_song_get_uri(song), cover, 500); - albumartist = strdup(mpd_client_get_tag(song, MPD_TAG_ALBUM_ARTIST)); + char *value = mpd_client_get_tag(song, MPD_TAG_ALBUM_ARTIST); + albumartist = strdup(value); + if (strncmp(value, "-", 1) == 0) { free(value); } } len += json_printf(&out, "{Type: song, "); PUT_SONG_TAGS(); @@ -2177,7 +2198,7 @@ static int mpd_client_put_songs_in_album(t_config *config, t_mpd_state *mpd_stat search, tag, cover, - (albumartist != NULL ? albumartist : "") + (albumartist != NULL ? albumartist : "-") ); } if (albumartist != NULL) @@ -2281,6 +2302,7 @@ static int mpd_client_put_playlist_list(t_config *config, t_mpd_state *mpd_state else { entity_count--; } + if (strncmp(entityName, "-", 1) == 0) { free((void *) entityName); } } mpd_entity_free(entity); } diff --git a/src/mympd_api.c b/src/mympd_api.c index 60ae397..06b3419 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -214,8 +214,11 @@ static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_reque if (!state_file_write(config, "jukeboxMode", p_char)) len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); } - je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &mympd_state->jukeboxPlaylist); + je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &p_charbuf1); if (je == 1) { + free(mympd_state->jukeboxPlaylist); + mympd_state->jukeboxPlaylist = p_charbuf1; + p_charbuf1 = NULL; if (!state_file_write(config, "jukeboxPlaylist", mympd_state->jukeboxPlaylist)) len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); } diff --git a/src/web_server.c b/src/web_server.c index caac8db..bfe6c56 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -271,6 +271,7 @@ static bool handle_api(long conn_id, const char *request_body, int request_len) tiny_queue_push(mympd_api_queue, request); else tiny_queue_push(mpd_client_queue, request); - + + free(cmd); return true; } From c587a120d027a8fc32b806a737bb74790411b520 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 20:49:46 +0100 Subject: [PATCH 49/59] Fix: memory leaks reported by valingrid --- src/mpd_client.c | 2 ++ src/mympd_api.c | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index e0cc704..9a66f9d 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -822,6 +822,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r len = mpd_client_search(config, mpd_state, buffer, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); + free(p_charbuf3); } break; case MPD_API_DATABASE_SEARCH_ADV: @@ -2656,6 +2657,7 @@ static int mpd_client_smartpls_put(t_config *config, char *buffer, const char *p len = json_printf(&out, "{type: error, data: %Q}}", "Unknown smart playlist type"); printf("Unknown smart playlist type: %s\n", pl_file); } + free(content); return len; } diff --git a/src/mympd_api.c b/src/mympd_api.c index 06b3419..3c3bd1e 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -275,13 +275,17 @@ static bool mympd_api_read_syscmds(t_config *config, t_mympd_state *mympd_state) if (strncmp(ent->d_name, ".", 1) == 0) continue; order = strtol(ent->d_name, &cmd, 10); - if (strcmp(cmd, "") != 0) - list_push(&mympd_state->syscmd_list, strdup(cmd), order); + if (strcmp(cmd, "") != 0) { + list_push(&mympd_state->syscmd_list, cmd, order); + } + else { + printf("ERROR: Can't read syscmd file %s\n", ent->d_name); + } } closedir(dir); } else { - printf("ERROR: Can't read syscmds"); + printf("ERROR: Can't read syscmds\n"); } } else { From fe887084fce7b578efaa13600e055d03558932d9 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 21:01:14 +0000 Subject: [PATCH 50/59] Fix: improve cleanup --- src/main.c | 142 +++++++++++++++++++++++++++++------------------ src/web_server.c | 1 - 2 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/main.c b/src/main.c index c1fbb12..122d572 100644 --- a/src/main.c +++ b/src/main.c @@ -164,9 +164,34 @@ static int inihandler(void *user, const char *section, const char *name, const c return 1; } +static void free_config(t_config *config) { + free(config->mpdhost); + free(config->mpdpass); + free(config->webport); + free(config->sslport); + free(config->sslcert); + free(config->sslkey); + free(config->user); + free(config->coverimagename); + free(config->taglist); + free(config->searchtaglist); + free(config->browsetaglist); + free(config->varlibdir); + free(config->etcdir); + free(config->streamurl); + free(config->backgroundcolor); + free(config); +} + int main(int argc, char **argv) { s_signal_received = 0; char testdirname[400]; + bool init_webserver = false; + bool init_thread_webserver = false; + bool init_thread_mpdclient = false; + bool init_thread_mympdapi = false; + int rc = EXIT_FAILURE; + mpd_client_queue = tiny_queue_create(); mympd_api_queue = tiny_queue_create(); web_server_queue = tiny_queue_create(); @@ -212,7 +237,7 @@ int main(int argc, char **argv) { printf("Parsing config file: %s\n", argv[1]); if (ini_parse(argv[1], inihandler, config) < 0) { printf("Can't load config file \"%s\"\n", argv[1]); - return EXIT_FAILURE; + goto cleanup; } } else { @@ -224,7 +249,7 @@ int main(int argc, char **argv) { MYMPD_VERSION, argv[0] ); - return EXIT_FAILURE; + goto cleanup; } #ifdef DEBUG @@ -242,7 +267,10 @@ int main(int argc, char **argv) { //init webserver struct mg_mgr mgr; if (!web_server_init(&mgr, config)) { - return EXIT_FAILURE; + goto cleanup; + } + else { + init_webserver = true; } //drop privileges @@ -250,33 +278,29 @@ int main(int argc, char **argv) { printf("Droping privileges to %s\n", config->user); struct passwd *pw; if ((pw = getpwnam(config->user)) == NULL) { - printf("getpwnam() failed, unknown user\n"); - web_server_free(&mgr); - return EXIT_FAILURE; + printf("ERROR: getpwnam() failed, unknown user\n"); + goto cleanup; } else if (setgroups(0, NULL) != 0) { - printf("setgroups() failed\n"); - web_server_free(&mgr); - return EXIT_FAILURE; + printf("ERROR: setgroups() failed\n"); + goto cleanup; } else if (setgid(pw->pw_gid) != 0) { - printf("setgid() failed\n"); - web_server_free(&mgr); - return EXIT_FAILURE; + printf("ERROR: setgid() failed\n"); + goto cleanup; } else if (setuid(pw->pw_uid) != 0) { - printf("setuid() failed\n"); - web_server_free(&mgr); - return EXIT_FAILURE; + printf("ERROR: setuid() failed\n"); + goto cleanup; } } if (getuid() == 0) { printf("myMPD should not be run with root privileges\n"); - web_server_free(&mgr); - return EXIT_FAILURE; + goto cleanup; } //check needed directories - if (!testdir("Document root", DOC_ROOT)) - return EXIT_FAILURE; + if (!testdir("Document root", DOC_ROOT)) { + goto cleanup; + } snprintf(testdirname, 400, "%s/library", DOC_ROOT); if (!testdir("Link to mpd music_directory", testdirname)) { @@ -285,54 +309,66 @@ int main(int argc, char **argv) { } snprintf(testdirname, 400, "%s/tmp", config->varlibdir); - if (!testdir("Temp dir", testdirname)) - return EXIT_FAILURE; + if (!testdir("Temp dir", testdirname)) { + goto cleanup; + } snprintf(testdirname, 400, "%s/smartpls", config->varlibdir); - if (!testdir("Smartpls dir", testdirname)) - return EXIT_FAILURE; + if (!testdir("Smartpls dir", testdirname)) { + goto cleanup; + } snprintf(testdirname, 400, "%s/state", config->varlibdir); - if (!testdir("State dir", testdirname)) - return EXIT_FAILURE; + if (!testdir("State dir", testdirname)) { + goto cleanup; + } //Create working threads pthread_t mpd_client_thread, web_server_thread, mympd_api_thread; //mpd connection - pthread_create(&mpd_client_thread, NULL, mpd_client_loop, config); - pthread_setname_np(mpd_client_thread, "mympd_mpdclient"); + if (pthread_create(&mpd_client_thread, NULL, mpd_client_loop, config) == 0) { + pthread_setname_np(mpd_client_thread, "mympd_mpdclient"); + init_thread_mpdclient = true; + } + else { + printf("ERROR: can't create mympd_client thread\n"); + s_signal_received = SIGTERM; + } //webserver - pthread_create(&web_server_thread, NULL, web_server_loop, &mgr); - pthread_setname_np(web_server_thread, "mympd_webserver"); + if (pthread_create(&web_server_thread, NULL, web_server_loop, &mgr) == 0) { + pthread_setname_np(web_server_thread, "mympd_webserver"); + init_thread_webserver = true; + } + else { + printf("ERROR: can't create mympd_webserver thread\n"); + s_signal_received = SIGTERM; + } //mympd api - pthread_create(&mympd_api_thread, NULL, mympd_api_loop, config); - pthread_setname_np(mympd_api_thread, "mympd_mympdapi"); + if (pthread_create(&mympd_api_thread, NULL, mympd_api_loop, config) == 0) { + pthread_setname_np(mympd_api_thread, "mympd_mympdapi"); + init_thread_mympdapi = true; + } + else { + printf("ERROR: can't create mympd_mympdapi thread\n"); + s_signal_received = SIGTERM; + } //Outsourced all work to separate threads, do nothing... + rc = EXIT_SUCCESS; - //cleanup - pthread_join(mpd_client_thread, NULL); - pthread_join(web_server_thread, NULL); - pthread_join(mympd_api_thread, NULL); + //Try to cleanup all + cleanup: + if (init_thread_mpdclient) + pthread_join(mpd_client_thread, NULL); + if (init_thread_webserver) + pthread_join(web_server_thread, NULL); + if (init_thread_mympdapi) + pthread_join(mympd_api_thread, NULL); + if (init_webserver) + web_server_free(&mgr); tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); - free(config->mpdhost); - free(config->mpdpass); - free(config->webport); - free(config->sslport); - free(config->sslcert); - free(config->sslkey); - free(config->user); - free(config->coverimagename); - free(config->taglist); - free(config->searchtaglist); - free(config->browsetaglist); - free(config->varlibdir); - free(config->etcdir); - free(config->streamurl); - free(config->backgroundcolor); - free(config); - - return EXIT_SUCCESS; + free_config(config); + return rc; } diff --git a/src/web_server.c b/src/web_server.c index bfe6c56..1df64a5 100644 --- a/src/web_server.c +++ b/src/web_server.c @@ -122,7 +122,6 @@ void *web_server_loop(void *arg_mgr) { } // } } - mg_mgr_free(mgr); return NULL; } From f7482ab5a7fbc3cc90fd75aa10852c2005084503 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 22:59:48 +0100 Subject: [PATCH 51/59] Fix: search with disabled metadata --- htdocs/index.html | 2 +- htdocs/js/mympd.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/htdocs/index.html b/htdocs/index.html index ab46ca8..98ab7a3 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -567,7 +567,7 @@ - +
diff --git a/htdocs/js/mympd.js b/htdocs/js/mympd.js index a84d97a..ed4b216 100644 --- a/htdocs/js/mympd.js +++ b/htdocs/js/mympd.js @@ -1275,8 +1275,8 @@ function parseSettings() { if (settings.featTags == false) { app.apps.Browse.active = 'Filesystem'; - app.apps.Search.state = '0/filename/'; - app.apps.Queue.state = '0/filename/'; + app.apps.Search.state = '0/filename/-/'; + app.apps.Queue.state = '0/filename/-/'; settings.colsQueueCurrent = ["Pos", "Title", "Duration"]; settings.colsQueueLastPlayed = ["Pos", "Title", "LastPlayed"]; settings.colsSearch = ["Title", "Duration"]; From 6948fad379e08d4db4772bce9ba4bef2473e2164 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 23:00:08 +0100 Subject: [PATCH 52/59] Fix: add error checking for mpd_send_idle command --- src/mpd_client.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index 9a66f9d..ecf866d 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -1185,7 +1185,11 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { unsigned mpd_client_queue_length = tiny_queue_length(mpd_client_queue, 50); if (pollrc > 0 || mpd_client_queue_length > 0) { LOG_DEBUG() fprintf(stderr, "DEBUG: Leaving mpd idle mode.\n"); - mpd_send_noidle(mpd_state->conn); + if (!mpd_send_noidle(mpd_state->conn)) { + printf("ERROR: entering idle mode failed\n"); + mpd_state->conn_state = MPD_FAILURE; + break; + } if (pollrc > 0) { //Handle idle events LOG_DEBUG() fprintf(stderr, "DEBUG: Checking for idle events.\n"); @@ -1204,7 +1208,10 @@ static void mpd_client_idle(t_config *config, t_mpd_state *mpd_state) { } } LOG_DEBUG() fprintf(stderr, "DEBUG: Entering mpd idle mode.\n"); - mpd_send_idle(mpd_state->conn); + if (!mpd_send_idle(mpd_state->conn)) { + printf("ERROR: entering idle mode failed\n"); + mpd_state->conn_state = MPD_FAILURE; + } } break; default: From 9053f7f4196e019dc672ec988b9dd5ce15518285 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Mon, 28 Jan 2019 22:18:36 +0000 Subject: [PATCH 53/59] Fix: update minified files Fix: typo in mympd.css --- dist/htdocs/css/mympd.min.css | 347 +--------------------------------- dist/htdocs/index.html | 2 +- dist/htdocs/js/mympd.min.js | 4 +- htdocs/css/mympd.css | 2 +- 4 files changed, 5 insertions(+), 350 deletions(-) diff --git a/dist/htdocs/css/mympd.min.css b/dist/htdocs/css/mympd.min.css index d2d31de..13f3f91 100644 --- a/dist/htdocs/css/mympd.min.css +++ b/dist/htdocs/css/mympd.min.css @@ -1,346 +1 @@ -:root { - --mympd-coverimagesize: 240px; - --mympd-bacgkroundcolor: #888; -} - -html { - position: relative; - min-height: 100%; -} - -body { - padding-top: 4rem; - padding-bottom: 4rem; - background-color: var(--mympd-backgroundcolor, #888); -} - -main { - padding-top: 1rem; -} - -footer { - position: absolute; - bottom: 0; -} - -button { - overflow: hidden; -} - -.cardBodyPlayback { - padding-bottom:0px; -} - -#BrowseBreadrumb { - overflow: auto; - white-space: nowrap; -} - -#BrowseBreadcrumb > li > a { - cursor: pointer; -} - - -@media only screen and (max-width: 576px) { - .header-logo { - display:none !important; - } -} - -.clickable { - cursor: pointer; -} - -[data-col=Pos], [data-col=Type], [data-col=Track], [data-col=Action] { - width: 2rem; -} - -small { - color: #aaa; -} - -.card { - min-height: 300px; -} - -.cardFooterPlayback { - padding: 0px; -} - -.album-cover { - background-size: cover; - background-image: url("/assets/coverimage-loading.png"); - border: 1px solid black; - border-radius: 5px; - overflow: hidden; - width: var(--mympd-coverimagesize, 250px); - max-width:100%; - height: var(--mympd-coverimagesize, 250px); - background-color: #eee; - float: left; - margin-right: 1.25rem; - margin-bottom: 1.25rem; -} - -.album-desc { - min-width: 240px; - float: left; - padding-bottom:1.25rem; -} - -.hide { - display: none !important; -} - -.unvisible { - visibility: hidden; -} - -.pull-right { - float: right !important; -} - -.card-toolbar { - margin-bottom: 10px; -} - -.card-toolbar > div, .card-toolbar > form { - margin-bottom: 5px; -} - -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - src: url(/assets/MaterialIcons-Regular.woff2) format('woff2'), - url(/assets/MaterialIcons-Regular.woff) format('woff'), - url(/assets/MaterialIcons-Regular.ttf) format('truetype'); -} - -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 1.4rem; /* Preferred icon size */ - display:inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - vertical-align: top; - /* Support for all WebKit browsers. */ - -webkit-font-smoothing: antialiased; - /* Support for Safari and Chrome. */ - text-rendering: optimizeLegibility; - /* Support for Firefox. */ - -moz-osx-font-smoothing: grayscale; - /* Support for IE. */ - font-feature-settings: 'liga'; -} - -.material-icons-left { - font-size: 1rem; - margin-left: -1em; - vertical-align:middle; -} - -.material-icons-small { - font-size: 1rem; -} - -.material-icons-small-left { - font-size: 1rem; - margin-left: -1em; -} - -.color-darkgrey, .color-darkgrey:hover { - color:#6c757d !important; -} - -#btn-outputs-block > button { - margin-bottom:10px; -} - -#btn-outputs-block > button:last-child { - margin-bottom:0px; -} - -.card-body { - overflow-x:hidden; -} - -.progressBarPlay { - font-size: 1.8rem; -} - -#counter { - cursor: text; -} - -#volumeBar { - margin-top:2px; - width:160px; -} - -.title-icon { - float:left; - margin-right:5px; - font-size:1.8rem; -} - -.header-logo { - font-size: 2rem; - float: left; - margin-right: 5px; -} - -.letters > button { - width: 28px; - height: 28px; -} - -.col-md { - max-width: 250px; - min-width: 250px; - -} - -a.card-img-left { - overflow: hidden; - display: block; - width: var(--mympd-coverimagesize); - height: var(--mympd-coverimagesize); - border-radius: 5px; - background-size: cover; - background-image: url(/assets/coverimage-loading.png); - margin-bottom: 5px; - cursor: pointer; -} - -button.active { - color: #fff; - background-color: #28a745 !important; - border-color: #28a745 !important; -} - -button.active-fg-green { - color: #28a745 !important; -} - -button.active-fg-red { - color: #bd2130 !important; -} - -div#alertBox { - position:fixed; - top: 4rem; - right:1rem; - width:80%; - max-width:400px; - z-index:1000; - opacity:0; - visibility:visible; - transition:opacity 0.5s ease-in; -} - -div.alertBoxActive { - opacity:1 !important; - visibility:visible !important; - transition:opacity 0.5s ease-in; -} - -.popover-content { - padding-top:4px; - padding-bottom:4px; -} - -.opacity05 { - opacity:0.5; -} - -caption { - caption-side: top; - font-size: 120%; - font-weight: bold; - color: black; -} - -.dragover > td { - border-top:2px solid #28a745; -} - -.dragover-th { - border-left:2px solid #28a745; -} - -[draggable] { - -moz-user-select: none; - -khtml-user-select: none; - -webkit-user-select: none; - user-select: none; - /* Required to make elements draggable in old WebKit */ - -khtml-user-drag: element; - -webkit-user-drag: element; - cursor: pointer; -} - -@keyframes changewidth { - from { margin-left: -20px; } - to { margin-left: 100%; } -} - -#updateDBprogress { - width:20px; -} - -.updateDBprogressAnimate { - animation-duration: 2s; - animation-name: changewidth; - animation-iteration-count: infinite; -} - -.modal-body { - overflow-x: hidden; -} - -.modal-body .album-cover { - float:none; -} - -#BrowseDatabaseAlbumListCaption { - margin-left: 15px; - margin-right: 15px; - width: 100%; -} - -#BrowseDatabaseAlbumListCaption h2 { - display: inline; -} - -#BrowseDatabaseAlbumListCaption small { - padding-top: 0.8rem; -} - -#menu-dbupdate { - padding-left:1rem; -} - -div.key { - border: 1px solid #bbb; - background-color: #eee; - border-radius: 2px; - width: 20px; - height: 20px; - text-align: center; -} - -ol#searchCrumb { - padding: .5rem; -} - -.nodropdown::after { - content: none; -} - +:root{--mympd-coverimagesize:240px;--mympd-backgroundcolor:#888}html{position:relative;min-height:100%}body{padding-top:4rem;padding-bottom:4rem;background-color:var(--mympd-backgroundcolor,#888)}main{padding-top:1rem}footer{position:absolute;bottom:0}button{overflow:hidden}.cardBodyPlayback{padding-bottom:0}#BrowseBreadrumb{overflow:auto;white-space:nowrap}#BrowseBreadcrumb>li>a{cursor:pointer}@media only screen and (max-width:576px){.header-logo{display:none!important}}.clickable{cursor:pointer}[data-col=Pos],[data-col=Type],[data-col=Track],[data-col=Action]{width:2rem}small{color:#aaa}.card{min-height:300px}.cardFooterPlayback{padding:0}.album-cover{background-size:cover;background-image:url("/assets/coverimage-loading.png");border:1px solid black;border-radius:5px;overflow:hidden;width:var(--mympd-coverimagesize,250px);max-width:100%;height:var(--mympd-coverimagesize,250px);background-color:#eee;float:left;margin-right:1.25rem;margin-bottom:1.25rem}.album-desc{min-width:240px;float:left;padding-bottom:1.25rem}.hide{display:none!important}.unvisible{visibility:hidden}.pull-right{float:right!important}.card-toolbar{margin-bottom:10px}.card-toolbar>div,.card-toolbar>form{margin-bottom:5px}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(/assets/MaterialIcons-Regular.woff2) format('woff2'),url(/assets/MaterialIcons-Regular.woff) format('woff'),url(/assets/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:1.4rem;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;vertical-align:top;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}.material-icons-left{font-size:1rem;margin-left:-1em;vertical-align:middle}.material-icons-small{font-size:1rem}.material-icons-small-left{font-size:1rem;margin-left:-1em}.color-darkgrey,.color-darkgrey:hover{color:#6c757d!important}#btn-outputs-block>button{margin-bottom:10px}#btn-outputs-block>button:last-child{margin-bottom:0}.card-body{overflow-x:hidden}.progressBarPlay{font-size:1.8rem}#counter{cursor:text}#volumeBar{margin-top:2px;width:160px}.title-icon{float:left;margin-right:5px;font-size:1.8rem}.header-logo{font-size:2rem;float:left;margin-right:5px}.letters>button{width:28px;height:28px}.col-md{max-width:250px;min-width:250px}a.card-img-left{overflow:hidden;display:block;width:var(--mympd-coverimagesize);height:var(--mympd-coverimagesize);border-radius:5px;background-size:cover;background-image:url(/assets/coverimage-loading.png);margin-bottom:5px;cursor:pointer}button.active{color:#fff;background-color:#28a745!important;border-color:#28a745!important}button.active-fg-green{color:#28a745!important}button.active-fg-red{color:#bd2130!important}div#alertBox{position:fixed;top:4rem;right:1rem;width:80%;max-width:400px;z-index:1000;opacity:0;visibility:visible;transition:opacity .5s ease-in}div.alertBoxActive{opacity:1!important;visibility:visible!important;transition:opacity .5s ease-in}.popover-content{padding-top:4px;padding-bottom:4px}.opacity05{opacity:.5}caption{caption-side:top;font-size:120%;font-weight:bold;color:black}.dragover>td{border-top:2px solid #28a745}.dragover-th{border-left:2px solid #28a745}[draggable]{-moz-user-select:none;-khtml-user-select:none;-webkit-user-select:none;user-select:none;-khtml-user-drag:element;-webkit-user-drag:element;cursor:pointer}@keyframes changewidth{0%{margin-left:-20px}to{margin-left:100%}}#updateDBprogress{width:20px}.updateDBprogressAnimate{animation-duration:2s;animation-name:changewidth;animation-iteration-count:infinite}.modal-body{overflow-x:hidden}.modal-body .album-cover{float:none}#BrowseDatabaseAlbumListCaption{margin-left:15px;margin-right:15px;width:100%}#BrowseDatabaseAlbumListCaption h2{display:inline}#BrowseDatabaseAlbumListCaption small{padding-top:.8rem}#menu-dbupdate{padding-left:1rem}div.key{border:1px solid #bbb;background-color:#eee;border-radius:2px;width:20px;height:20px;text-align:center}ol#searchCrumb{padding:.5rem}.nodropdown::after{content:none} \ No newline at end of file diff --git a/dist/htdocs/index.html b/dist/htdocs/index.html index 2fc2290..c6cc019 100644 --- a/dist/htdocs/index.html +++ b/dist/htdocs/index.html @@ -1 +1 @@ -myMPD
Playback

#TitleArtistAlbumDuration
#TitleArtistAlbumDuration
PlaylistLast modified
Playlist List
#TitleArtistAlbumDuration
TitleArtistAlbumDuration
Search
TitleArtistAlbumDuration
\ No newline at end of file +myMPD
Playback

#TitleArtistAlbumDuration
#TitleArtistAlbumDuration
PlaylistLast modified
Playlist List
#TitleArtistAlbumDuration
TitleArtistAlbumDuration
Search
TitleArtistAlbumDuration
\ No newline at end of file diff --git a/dist/htdocs/js/mympd.min.js b/dist/htdocs/js/mympd.min.js index 8bd72e1..f4cb354 100644 --- a/dist/htdocs/js/mympd.min.js +++ b/dist/htdocs/js/mympd.min.js @@ -83,8 +83,8 @@ function filterCols(a){var b=settings.tags.slice();0==settings.featTags&&b.push( function parseSettings(){toggleBtn("btnRandom",settings.random);toggleBtn("btnConsume",settings.consume);toggleBtn("btnSingle",settings.single);toggleBtn("btnRepeat",settings.repeat);toggleBtn("btnAutoPlay",settings.autoPlay);void 0!=settings.crossfade?(document.getElementById("inputCrossfade").removeAttribute("disabled"),document.getElementById("inputCrossfade").value=settings.crossfade):document.getElementById("inputCrossfade").setAttribute("disabled","disabled");void 0!=settings.mixrampdb?(document.getElementById("inputMixrampdb").removeAttribute("disabled"), document.getElementById("inputMixrampdb").value=settings.mixrampdb):document.getElementById("inputMixrampdb").setAttribute("disabled","disabled");void 0!=settings.mixrampdelay?(document.getElementById("inputMixrampdelay").removeAttribute("disabled"),document.getElementById("inputMixrampdelay").value=settings.mixrampdelay):document.getElementById("inputMixrampdelay").setAttribute("disabled","disabled");document.getElementById("selectReplaygain").value=settings.replaygain;var a=document.getElementById("btnnotifyWeb"); notificationsSupported()?settings.notificationWeb?(toggleBtn("btnnotifyWeb",settings.notificationWeb),Notification.requestPermission(function(a){"permission"in Notification||(Notification.permission=a);"granted"===a?toggleBtn("btnnotifyWeb",1):(toggleBtn("btnnotifyWeb",0),settings.notificationWeb=!0)})):toggleBtn("btnnotifyWeb",0):(a.setAttribute("disabled","disabled"),toggleBtn("btnnotifyWeb",0));toggleBtn("btnnotifyPage",settings.notificationPage);var b="featStickers featSmartpls featPlaylists featTags featLocalplayer featSyscmds featCoverimage featAdvsearch".split(" "); -document.documentElement.style.setProperty("--mympd-coverimagesize",settings.coverimagesize+"px");document.documentElement.style.setProperty("--mympd-backgroundcolor",settings.backgroundcolor);for(var c=0;c'+ +document.documentElement.style.setProperty("--mympd-coverimagesize",settings.coverimagesize+"px");document.documentElement.style.setProperty("--mympd-backgroundcolor",settings.backgroundcolor);for(var c=0;c'+ settings.colsPlayback[a]+"";document.getElementById("cardPlaybackTags").innerHTML=b}1==settings.mixramp?document.getElementsByClassName("mixramp")[0].style.display="":document.getElementsByClassName("mixramp")[0].style.display="none";!settings.tags.includes("AlbumArtist")&&settings.featTags&&(settings.tags.includes("Artist")? app.apps.Browse.tabs.Database.active="Artist":app.apps.Browse.tabs.Database.active=settings.tags[0]);settings.tags.includes("Title")&&(app.apps.Search.state="0/any/Title/");document.getElementById("selectJukeboxMode").value=settings.jukeboxMode;document.getElementById("inputJukeboxQueueLength").value=settings.jukeboxQueueLength;0==settings.jukeboxMode||2==settings.jukeboxMode?(document.getElementById("inputJukeboxQueueLength").setAttribute("disabled","disabled"),document.getElementById("selectJukeboxPlaylist").setAttribute("disabled", "disabled")):1==settings.jukeboxMode&&(document.getElementById("inputJukeboxQueueLength").removeAttribute("disabled"),document.getElementById("selectJukeboxPlaylist").removeAttribute("disabled"));settings.featPlaylists?(playlistEl="selectJukeboxPlaylist",sendAPI({cmd:"MPD_API_PLAYLIST_LIST",data:{offset:0,filter:"-"}},getAllPlaylists)):document.getElementById("selectJukeboxPlaylist").innerHTML="";settings.tags.sort();settings.searchtags.sort();settings.browsetags.sort();filterCols("colsSearch"); diff --git a/htdocs/css/mympd.css b/htdocs/css/mympd.css index d2d31de..c5a6cb5 100644 --- a/htdocs/css/mympd.css +++ b/htdocs/css/mympd.css @@ -1,6 +1,6 @@ :root { --mympd-coverimagesize: 240px; - --mympd-bacgkroundcolor: #888; + --mympd-backgroundcolor: #888; } html { From dbfdef524d50611ab1c3a1322345cc8bae8fccb9 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Tue, 29 Jan 2019 23:49:40 +0000 Subject: [PATCH 54/59] Feat: support environment variables Feat: set default config file to /etc/mymmpd/mympd.conf Feat: use defaults, if config file doesn't exists --- src/main.c | 101 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 28 deletions(-) diff --git a/src/main.c b/src/main.c index 122d572..39697b1 100644 --- a/src/main.c +++ b/src/main.c @@ -44,18 +44,18 @@ #include "../dist/src/inih/ini.h" -static void signal_handler(int sig_num) { - signal(sig_num, signal_handler); // Reinstantiate signal handler +static void mympd_signal_handler(int sig_num) { + signal(sig_num, mympd_signal_handler); // Reinstantiate signal handler s_signal_received = sig_num; //Wakeup mympd_api_loop pthread_cond_signal(&mympd_api_queue->wakeup); } -static int inihandler(void *user, const char *section, const char *name, const char* value) { +static int mympd_inihandler(void *user, const char *section, const char *name, const char* value) { t_config* p_config = (t_config*)user; char *crap; - #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0 + #define MATCH(s, n) strcasecmp(section, s) == 0 && strcasecmp(name, n) == 0 if (MATCH("mpd", "mpdhost")) { free(p_config->mpdhost); @@ -164,7 +164,7 @@ static int inihandler(void *user, const char *section, const char *name, const c return 1; } -static void free_config(t_config *config) { +static void mympd_free_config(t_config *config) { free(config->mpdhost); free(config->mpdpass); free(config->webport); @@ -183,6 +183,35 @@ static void free_config(t_config *config) { free(config); } +static void mympd_parse_env(struct t_config *config, const char *envvar) { + char *name, *section; + const char *value = getenv(envvar); + if (value != NULL) { + char *var = strdup(envvar); + section = strtok_r(var, "_", &name); + if (section != NULL && name != NULL) { + LOG_DEBUG() printf("DEBUG: Using environment variable %s_%s: %s\n", section, name, value); + mympd_inihandler(config, section, name, value); + } + value = NULL; + } +} + +static void mympd_get_env(struct t_config *config) { + const char* env_vars[]={"MPD_MPDHOST", "MPD_MPDPORT", "MPD_STREAMPORT", + "WEBSERVER_WEBPORT", "WEBSERVER_SSL", "WEBSERVER_SSLPORT", "WEBSERVER_SSLCERT", "WEBSERVER_SSLKEY", + "MYMPD_LOGLEVEL", "MYMPD_USER", "MYMPD_LOCALPLAYER", "MYMPD_COVERIMAGE", "MYMPD_COVERIMAGENAME", + "MYMPD_COVERIMAGESIZE", "MYMPD_VARLIBDIR", "MYMPD_MIXRAMP", "MYMPD_STICKERS", "MYMPD_TAGLIST", + "MYMPD_SEARCHTAGLIST", "MYMPD_BROWSETAGLIST", "MYMPD_SMARTPLS", "MYMPD_SYSCMDS", + "MYMPD_MAX_ELEMENTS_PER_PAGE", "MYMPD_LAST_PLAYED_COUNT", + "THEME_BACKGROUNDCOLOR", 0}; + const char** ptr = env_vars; + while (*ptr != 0) { + mympd_parse_env(config, *ptr); + ++ptr; + } +} + int main(int argc, char **argv) { s_signal_received = 0; char testdirname[400]; @@ -223,34 +252,49 @@ int main(int argc, char **argv) { config->smartpls = true; config->max_elements_per_page = 100; config->last_played_count = 20; - char *etcdir = strdup(argv[1]); - config->etcdir = strdup(dirname(etcdir)); - free(etcdir); + config->etcdir = strdup("/etc/mympd"); config->syscmds = false; config->localplayer = true; config->loglevel = 1; config->backgroundcolor = strdup("#888"); - + + char *configfile = strdup("/etc/mympd/mympd.conf"); if (argc == 2) { - printf("Starting myMPD %s\n", MYMPD_VERSION); - printf("Libmpdclient %i.%i.%i\n", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); - printf("Parsing config file: %s\n", argv[1]); - if (ini_parse(argv[1], inihandler, config) < 0) { - printf("Can't load config file \"%s\"\n", argv[1]); + if (strncmp(argv[1], "/", 1) == 0) { + printf("Starting myMPD %s\n", MYMPD_VERSION); + printf("Libmpdclient %i.%i.%i\n", LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION, LIBMPDCLIENT_PATCH_VERSION); + free(configfile); + configfile = argv[1]; + char *etcdir = strdup(configfile); + free(config->etcdir); + config->etcdir = strdup(dirname(etcdir)); + free(etcdir); + } + else { + printf("myMPD %s\n" + "Copyright (C) 2018-2019 Juergen Mang \n" + "https://github.com/jcorporation/myMPD\n" + "Built " __DATE__ " "__TIME__"\n\n" + "Usage: %s [/path/to/mympd.conf]\n", + MYMPD_VERSION, + argv[0] + ); goto cleanup; } - } - else { - printf("myMPD %s\n" - "Copyright (C) 2018-2019 Juergen Mang \n" - "https://github.com/jcorporation/myMPD\n" - "Built " __DATE__ " "__TIME__"\n\n" - "Usage: %s /path/to/mympd.conf\n", - MYMPD_VERSION, - argv[0] - ); - goto cleanup; } + if (access(configfile, F_OK ) != -1) { + printf("Parsing config file: %s\n", configfile); + if (ini_parse(configfile, mympd_inihandler, config) < 0) { + printf("Can't load config file \"%s\"\n", configfile); + goto cleanup; + } + } + else { + printf("No config file found, using defaults\n"); + } + + //read environment - overwrites config file definitions + mympd_get_env(config); #ifdef DEBUG printf("Debug flag enabled, setting loglevel to debug\n"); @@ -259,8 +303,9 @@ int main(int argc, char **argv) { printf("Setting loglevel to %ld\n", config->loglevel); loglevel = config->loglevel; - signal(SIGTERM, signal_handler); - signal(SIGINT, signal_handler); + //set signal handler + signal(SIGTERM, mympd_signal_handler); + signal(SIGINT, mympd_signal_handler); setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); @@ -369,6 +414,6 @@ int main(int argc, char **argv) { tiny_queue_free(web_server_queue); tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); - free_config(config); + mympd_free_config(config); return rc; } From c5cac48ac7dce5093cb230501b85abf0fb94eef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Mang?= Date: Wed, 30 Jan 2019 10:00:26 +0100 Subject: [PATCH 55/59] Fix: check for syscmds directory --- src/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.c b/src/main.c index 39697b1..be48d45 100644 --- a/src/main.c +++ b/src/main.c @@ -368,6 +368,14 @@ int main(int argc, char **argv) { goto cleanup; } + if (config->syscmds == true) { + snprintf(testdirname, 400, "%s/syscmds", config->etcdir); + if (!testdir("Syscmds directory", testdirname)) { + printf("Disabling syscmd support\n"); + config->syscmds = false; + } + } + //Create working threads pthread_t mpd_client_thread, web_server_thread, mympd_api_thread; //mpd connection From c7a093ae1d6fce03e37bf5fe19f99158c75e1f33 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 30 Jan 2019 23:10:16 +0100 Subject: [PATCH 56/59] Fix: memory leaks detected by valingrid Fix: dont't change user/group if startup user is not root --- src/main.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/main.c b/src/main.c index be48d45..a7acca5 100644 --- a/src/main.c +++ b/src/main.c @@ -194,6 +194,7 @@ static void mympd_parse_env(struct t_config *config, const char *envvar) { mympd_inihandler(config, section, name, value); } value = NULL; + free(var); } } @@ -319,21 +320,23 @@ int main(int argc, char **argv) { } //drop privileges - if (config->user != NULL) { - printf("Droping privileges to %s\n", config->user); - struct passwd *pw; - if ((pw = getpwnam(config->user)) == NULL) { - printf("ERROR: getpwnam() failed, unknown user\n"); - goto cleanup; - } else if (setgroups(0, NULL) != 0) { - printf("ERROR: setgroups() failed\n"); - goto cleanup; - } else if (setgid(pw->pw_gid) != 0) { - printf("ERROR: setgid() failed\n"); - goto cleanup; - } else if (setuid(pw->pw_uid) != 0) { - printf("ERROR: setuid() failed\n"); - goto cleanup; + if (getuid() == 0) { + if (config->user != NULL || strlen(config->user) != 0) { + printf("Droping privileges to %s\n", config->user); + struct passwd *pw; + if ((pw = getpwnam(config->user)) == NULL) { + printf("ERROR: getpwnam() failed, unknown user\n"); + goto cleanup; + } else if (setgroups(0, NULL) != 0) { + printf("ERROR: setgroups() failed\n"); + goto cleanup; + } else if (setgid(pw->pw_gid) != 0) { + printf("ERROR: setgid() failed\n"); + goto cleanup; + } else if (setuid(pw->pw_uid) != 0) { + printf("ERROR: setuid() failed\n"); + goto cleanup; + } } } @@ -423,5 +426,6 @@ int main(int argc, char **argv) { tiny_queue_free(mpd_client_queue); tiny_queue_free(mympd_api_queue); mympd_free_config(config); + free(configfile); return rc; } From 26f1ac365fecbdb46505a51f1b81cfdb7a4cd623 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Wed, 30 Jan 2019 23:30:45 +0100 Subject: [PATCH 57/59] Fix: typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07b5f02..b104653 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Unix Build Instructions Run --------- ``` -Usage: ./mympd /etc/mypd/mympd.conf +Usage: ./mympd [/etc/mympd/mympd.conf] ``` The ```./mkrelease.sh``` script tries to install a systemd service file. If this failes you can copy the ```mympd.service``` file from ```/usr/share/mympd/``` to appropriate distribution specific location. From cbd82417e2cdfa996cc820cde8b835fdeb34abf0 Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 31 Jan 2019 20:52:21 +0000 Subject: [PATCH 58/59] Fix: avoid copying large response messages --- src/mpd_client.c | 246 ++++++++++++++++++++++++----------------------- src/mympd_api.c | 54 +++++------ 2 files changed, 152 insertions(+), 148 deletions(-) diff --git a/src/mpd_client.c b/src/mpd_client.c index ecf866d..562aa0e 100644 --- a/src/mpd_client.c +++ b/src/mpd_client.c @@ -229,8 +229,8 @@ void *mpd_client_loop(void *arg_config) { //private functions static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_request) { t_work_request *request = (t_work_request*) arg_request; - char buffer[MAX_SIZE]; - size_t len = 0; +// char buffer[MAX_SIZE]; +// size_t len = 0; unsigned int uint_buf1, uint_buf2, uint_rc; int je, int_buf1, int_rc; float float_buf; @@ -238,6 +238,10 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r char *p_charbuf1, *p_charbuf2, *p_charbuf3, *p_charbuf4; LOG_VERBOSE() printf("API request (%ld): %.*s\n", request->conn_id, request->length, request->data); + //create response struct + t_work_result *response = (t_work_result*)malloc(sizeof(t_work_result)); + response->conn_id = request->conn_id; + response->length = 0; switch(request->cmd_id) { case MPD_API_LIKE: @@ -245,19 +249,19 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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(mpd_state, p_charbuf1, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set like.\"}"); else - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"MPD stickers are disabled\"}"); printf("MPD_API_LIKE: MPD stickers are disabled\n"); } break; case MPD_API_PLAYER_STATE: - len = mpd_client_put_state(mpd_state, buffer); + response->length = mpd_client_put_state(mpd_state, response->data); break; case MYMPD_API_SETTINGS_SET: //only update mpd_state, already saved in mympd_api.c @@ -274,74 +278,74 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {random: %u}}", &uint_buf1); if (je == 1) { if (!mpd_run_random(mpd_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state random.\"}"); + response->length = snprintf(response->data, 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state repeat.\"}"); + response->length = snprintf(response->data, 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state consume.\"}"); + response->length = snprintf(response->data, 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state single.\"}"); + response->length = snprintf(response->data, 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state crossfade.\"}"); + response->length = snprintf(response->data, 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_state->conn, float_buf)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdb.\"}"); + response->length = snprintf(response->data, 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_state->conn, float_buf)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state mixrampdelay.\"}"); + response->length = snprintf(response->data, 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_state->conn, "replay_gain_mode", p_charbuf1, NULL)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set mpd state replaygain.\"}"); mpd_response_finish(mpd_state->conn); free(p_charbuf1); } if (mpd_state->jukeboxMode != JUKEBOX_OFF) { mpd_client_jukebox(config, mpd_state); } - if (len == 0) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (response->length == 0) + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_DATABASE_UPDATE: uint_rc = mpd_run_update(mpd_state->conn, NULL); if (uint_rc > 0) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_DATABASE_RESCAN: uint_rc = mpd_run_rescan(mpd_state->conn, NULL); if (uint_rc > 0) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); break; case MPD_API_SMARTPLS_UPDATE_ALL: rc = mpd_client_smartpls_update_all(config, mpd_state); if (rc == true) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Smart Playlists updated\"}"); else - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Smart Playlists update failed\"}"); + response->length = snprintf(response->data, 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); - len = 0; + response->length = 0; rc = false; if (je == 1) { if (strcmp(p_charbuf1, "sticker") == 0) { @@ -371,32 +375,32 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r free(p_charbuf1); } if (rc == true) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving playlist failed\"}"); + response->length = snprintf(response->data, 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) { - len = mpd_client_smartpls_put(config, buffer, p_charbuf1); + response->length = mpd_client_smartpls_put(config, response->data, p_charbuf1); free(p_charbuf1); } break; case MPD_API_PLAYER_PAUSE: if (mpd_run_toggle_pause(mpd_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Toggling player pause failed.\"}"); + response->length = snprintf(response->data, 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_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Goto previous song failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Goto previous song failed.\"}"); printf("MPD_API_PLAYER_PREV: Error mpd_run_previous()\n"); } break; @@ -404,46 +408,46 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r if (mpd_state->feat_sticker) mpd_client_count_song_id(mpd_state, mpd_state->song_id, "skipCount", 1); if (mpd_run_next(mpd_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Skip to next song failed.\"}"); + response->length = snprintf(response->data, 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_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Begin to play failed.\"}"); + response->length = snprintf(response->data, 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_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Stopping player failed.\"}"); + response->length = snprintf(response->data, 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_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); printf("MPD_API_QUEUE_CLEAR: Error mpd_run_clear()\n"); } break; case MPD_API_QUEUE_CROP: - len = mpd_client_queue_crop(mpd_state, buffer); + response->length = mpd_client_queue_crop(mpd_state, response->data); 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from queue failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from queue failed.\"}"); printf("MPD_API_QUEUE_RM_TRACK: Error mpd_run_delete_id()\n"); } } @@ -452,9 +456,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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_state->conn, uint_buf1, uint_buf2)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track range from queue failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track range from queue failed.\"}"); printf("MPD_API_QUEUE_RM_RANGE: Error mpd_run_delete_range()\n"); } } @@ -467,9 +471,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r if (uint_buf1 < uint_buf2) uint_buf2--; if (mpd_run_move(mpd_state->conn, uint_buf1, uint_buf2)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in queue failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in queue failed.\"}"); printf("MPD_API_QUEUE_MOVE_TRACK: Error mpd_run_move()\n"); } } @@ -483,10 +487,10 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r uint_buf2--; if (mpd_send_playlist_move(mpd_state->conn, p_charbuf1, uint_buf1, uint_buf2)) { mpd_response_finish(mpd_state->conn); - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Moving track in playlist failed.\"}"); + response->length = snprintf(response->data, 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); @@ -496,32 +500,32 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: { track:%u}}", &uint_buf1); if (je == 1) { if (mpd_run_play_id(mpd_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Set playing track failed.\"}"); + response->length = snprintf(response->data, 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: - len = mpd_client_put_outputs(mpd_state, buffer); + response->length = mpd_client_put_outputs(mpd_state, response->data); 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Enabling output failed.\"}"); + response->length = snprintf(response->data, 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_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Disabling output failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Disabling output failed.\"}"); printf("MPD_API_PLAYER_TOGGLE_OUTPUT: Error mpd_run_disable_output()\n"); } } @@ -531,23 +535,23 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {volume:%u}}", &uint_buf1); if (je == 1) { if (mpd_run_set_volume(mpd_state->conn, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting volume failed.\"}"); + response->length = snprintf(response->data, 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: - len = mpd_client_put_volume(mpd_state, buffer); + response->length = mpd_client_put_volume(mpd_state, response->data); 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_state->conn, uint_buf1, uint_buf2)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Seeking song failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Seeking song failed.\"}"); printf("MPD_API_PLAYER_SEEK: Error mpd_run_seek_id()\n"); } } @@ -555,29 +559,29 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r case MPD_API_QUEUE_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { - len = mpd_client_put_queue(config, mpd_state, buffer, uint_buf1); + response->length = mpd_client_put_queue(config, mpd_state, response->data, uint_buf1); } break; case MPD_API_QUEUE_LAST_PLAYED: je = json_scanf(request->data, request->length, "{data: {offset: %u}}", &uint_buf1); if (je == 1) { - len = mpd_client_put_last_played_songs(config, mpd_state, buffer, uint_buf1); + response->length = mpd_client_put_last_played_songs(config, mpd_state, response->data, uint_buf1); } break; case MPD_API_PLAYER_CURRENT_SONG: - len = mpd_client_put_current_song(config, mpd_state, buffer); + response->length = mpd_client_put_current_song(config, mpd_state, response->data); break; case MPD_API_DATABASE_SONGDETAILS: je = json_scanf(request->data, request->length, "{data: { uri: %Q}}", &p_charbuf1); if (je == 1) { - len = mpd_client_put_songdetails(config, mpd_state, buffer, p_charbuf1); + response->length = mpd_client_put_songdetails(config, mpd_state, response->data, 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) { - len = mpd_client_put_db_tag(config, mpd_state, buffer, uint_buf1, p_charbuf2, "", "", p_charbuf1); + response->length = mpd_client_put_db_tag(config, mpd_state, response->data, uint_buf1, p_charbuf2, "", "", p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -585,7 +589,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = mpd_client_put_db_tag(config, mpd_state, buffer, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); + response->length = mpd_client_put_db_tag(config, mpd_state, response->data, uint_buf1, "Album", p_charbuf3, p_charbuf2, p_charbuf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -594,7 +598,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = mpd_client_put_songs_in_album(config, mpd_state, buffer, p_charbuf1, p_charbuf2, p_charbuf3); + response->length = mpd_client_put_songs_in_album(config, mpd_state, response->data, p_charbuf1, p_charbuf2, p_charbuf3); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -607,7 +611,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r char old_pl_file[400]; char new_pl_file[400]; if (validate_string(p_charbuf1) == false || validate_string(p_charbuf2) == false) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid filename.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid filename.\"}"); break; } snprintf(old_pl_file, 400, "%s/smartpls/%s", config->varlibdir, p_charbuf1); @@ -615,24 +619,24 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + response->length = snprintf(response->data, 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_state->conn, p_charbuf1, p_charbuf2)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Error mpd_run_rename()\n"); } } else - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Destination playlist %s already exists\"}", p_charbuf2); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Destination playlist %s already exists\"}", p_charbuf2); } else { if (mpd_run_rename(mpd_state->conn, p_charbuf1, p_charbuf2)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Renamed playlist %s to %s\"}", p_charbuf1, p_charbuf2); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Renaming playlist failed.\"}"); printf("MPD_API_PLAYLIST_RENAME: Error mpd_run_rename()\n"); } } @@ -643,14 +647,14 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r case MPD_API_PLAYLIST_LIST: je = json_scanf(request->data, request->length, "{data: {offset: %u, filter: %Q}}", &uint_buf1, &p_charbuf1); if (je == 2) { - len = mpd_client_put_playlists(config, mpd_state, buffer, uint_buf1, p_charbuf1); + response->length = mpd_client_put_playlists(config, mpd_state, response->data, 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) { - len = mpd_client_put_playlist_list(config, mpd_state, buffer, p_charbuf1, uint_buf1, p_charbuf2); + response->length = mpd_client_put_playlist_list(config, mpd_state, response->data, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -659,9 +663,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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_state->conn, p_charbuf1, p_charbuf2)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"Added %s to playlist %s\"}", p_charbuf2, p_charbuf1); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding song to playlist failed.\"}"); + response->length = snprintf(response->data, 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); @@ -672,9 +676,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_playlist_clear(mpd_state->conn, p_charbuf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing playlist failed.\"}"); printf("MPD_API_PLAYLIST_CLEAR: Error mpd_run_playlist_clear()\n"); } free(p_charbuf1); @@ -684,9 +688,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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_state->conn, p_charbuf1, uint_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Removing track from playlist failed.\"}"); + response->length = snprintf(response->data, 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); @@ -695,7 +699,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = mpd_client_put_browse(config, mpd_state, buffer, p_charbuf2, uint_buf1, p_charbuf1); + response->length = mpd_client_put_browse(config, mpd_state, response->data, p_charbuf2, uint_buf1, p_charbuf1); free(p_charbuf1); free(p_charbuf2); } @@ -705,9 +709,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r if (je == 2) { int_rc = mpd_run_add_id_to(mpd_state->conn, p_charbuf1, int_buf1); if (int_rc > -1 ) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + response->length = snprintf(response->data, 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); @@ -717,19 +721,19 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {uri:%Q }}", &p_charbuf1); if (je == 1) { if (!mpd_run_clear(mpd_state->conn)) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); + response->length = snprintf(response->data, 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_state->conn, p_charbuf1)) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + response->length = snprintf(response->data, 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_state->conn)) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Playing failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Playing failed.\"}"); printf("MPD_API_QUEUE_REPLACE_TRACK: Error mpd_run_play()\n"); } else - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } break; @@ -737,9 +741,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {uri:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_add(mpd_state->conn, p_charbuf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Append track to queue failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Append track to queue failed.\"}"); printf("MPD_API_QUEUE_ADD_TRACK: Error mpd_run_add()\n"); } free(p_charbuf1); @@ -751,14 +755,14 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r int_buf1 = mpd_run_add_id(mpd_state->conn, p_charbuf1); if (int_buf1 != -1) { if (mpd_run_play_id(mpd_state->conn, int_buf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); printf("MPD_API_QUEUE_ADD_PLAY_TRACK: Error mpd_run_play_id()\n"); } } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding track to queue failed.\"}"); + response->length = snprintf(response->data, 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); @@ -768,19 +772,19 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1); if (je == 1) { if (!mpd_run_clear(mpd_state->conn)) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Clearing queue failed.\"}"); + response->length = snprintf(response->data, 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_state->conn, p_charbuf1)) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); + response->length = snprintf(response->data, 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_state->conn)) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Setting playstate failed.\"}"); printf("MPD_API_QUEUE_REPLACE_PLAYLIST: Error mpd_run_play()\n"); } else - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); free(p_charbuf1); } break; @@ -788,9 +792,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{data: {plist:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_load(mpd_state->conn, p_charbuf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Adding playlist to queue failed.\"}"); + response->length = snprintf(response->data, 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); @@ -800,9 +804,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r je = json_scanf(request->data, request->length, "{ data: {plist:%Q}}", &p_charbuf1); if (je == 1) { if (mpd_run_save(mpd_state->conn, p_charbuf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving queue as playlist failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Saving queue as playlist failed.\"}"); printf("MPD_API_QUEUE_SAVE: Error mpd_run_save()\n"); } free(p_charbuf1); @@ -811,7 +815,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = mpd_client_search_queue(config, mpd_state, buffer, p_charbuf1, uint_buf1, p_charbuf2); + response->length = mpd_client_search_queue(config, mpd_state, response->data, p_charbuf1, uint_buf1, p_charbuf2); free(p_charbuf1); free(p_charbuf2); } @@ -819,7 +823,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = mpd_client_search(config, mpd_state, buffer, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); + response->length = mpd_client_search(config, mpd_state, response->data, p_charbuf1, p_charbuf2, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -829,7 +833,7 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r 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) { - len = mpd_client_search_adv(config, mpd_state, buffer, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); + response->length = mpd_client_search_adv(config, mpd_state, response->data, p_charbuf1, p_charbuf2, bool_buf, NULL, p_charbuf3, uint_buf1); free(p_charbuf1); free(p_charbuf2); free(p_charbuf3); @@ -837,9 +841,9 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r break; case MPD_API_QUEUE_SHUFFLE: if (mpd_run_shuffle(mpd_state->conn)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Shuffling queue failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Shuffling queue failed.\"}"); printf("MPD_API_QUEUE_SHUFFLE: Error mpd_run_shuffle()\n"); } break; @@ -849,13 +853,13 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r //remove smart playlist char pl_file[400]; if (validate_string(p_charbuf1) == false) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid filename.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Invalid filename.\"}"); break; } snprintf(pl_file, 400, "%s/smartpls/%s", config->varlibdir, p_charbuf1); if (access(pl_file, F_OK ) != -1 ) { if (unlink(pl_file) == -1) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting smart playlist failed.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting smart playlist failed.\"}"); printf("MPD_API_PLAYLIST_RM: Error unlinking smart playlist file()\n"); free(p_charbuf1); break; @@ -863,28 +867,28 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r } //remove mpd playlist if (mpd_run_rm(mpd_state->conn, p_charbuf1)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Deleting playlist failed.\"}"); + response->length = snprintf(response->data, 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: - len = mpd_client_put_settings(mpd_state, buffer); + response->length = mpd_client_put_settings(mpd_state, response->data); break; case MPD_API_DATABASE_STATS: - len = mpd_client_put_stats(mpd_state, buffer); + response->length = mpd_client_put_stats(mpd_state, response->data); break; default: - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown request\"}"); printf("Unknown API request: %.*s\n", request->length, request->data); } if (mpd_state->conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd_state->conn) != MPD_ERROR_SUCCESS) { printf("Error: %s\n", mpd_connection_get_error_message(mpd_state->conn)); - len = snprintf(buffer, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", + response->length = snprintf(response->data, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}", mpd_connection_get_error_message(mpd_state->conn)); /* Try to recover error */ @@ -892,16 +896,16 @@ static void mpd_client_api(t_config *config, t_mpd_state *mpd_state, void *arg_r mpd_state->conn_state = MPD_FAILURE; } - if (len == 0) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd_id %u.\"}", request->cmd_id); + if (response->length == 0) { + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"No response for cmd_id %u.\"}", request->cmd_id); + } + if (response->conn_id > -1) { + LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response to connection %lu (first 800 chars):\n%*.*s\n", request->conn_id, 0, 800, response->data); + tiny_queue_push(web_server_queue, response); + } + else { + free(response); } - LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response to connection %lu (first 800 chars):\n%*.*s\n", request->conn_id, 0, 800, buffer); - - t_work_result *response = (t_work_result*)malloc(sizeof(t_work_result)); - 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); } diff --git a/src/mympd_api.c b/src/mympd_api.c index 3c3bd1e..164631f 100644 --- a/src/mympd_api.c +++ b/src/mympd_api.c @@ -86,7 +86,7 @@ void *mympd_api_loop(void *arg_config) { //push jukebox settings to mpd_client queue t_work_request *mpd_client_request = (t_work_request *)malloc(sizeof(t_work_request)); - mpd_client_request->conn_id = 0; + mpd_client_request->conn_id = -1; mpd_client_request->cmd_id = MYMPD_API_SETTINGS_SET; mpd_client_request->length = snprintf(mpd_client_request->data, 1000, "{\"cmd\":\"MYMPD_API_SETTINGS_SET\", \"data\":{\"jukeboxMode\": %d, \"jukeboxPlaylist\": \"%s\", \"jukeboxQueueLength\": %d}}", @@ -124,24 +124,28 @@ void *mympd_api_loop(void *arg_config) { //private functions static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_request *request) { - //t_work_request *request = (t_work_request *) arg_request; - size_t len = 0; - char buffer[MAX_SIZE]; +// size_t len = 0; +// char buffer[MAX_SIZE]; int je; char *p_charbuf1; char p_char[4]; LOG_VERBOSE() printf("MYMPD API request: %.*s\n", request->length, request->data); + //create response struct + t_work_result *response = (t_work_result *)malloc(sizeof(t_work_result)); + response->conn_id = request->conn_id; + response->length = 0; + 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(config, mympd_state, buffer, p_charbuf1); + response->length = mympd_api_syscmd(config, mympd_state, response->data, p_charbuf1); free(p_charbuf1); } } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"System commands are disabled.\"}"); } } else if (request->cmd_id == MYMPD_API_COLS_SAVE) { @@ -182,12 +186,12 @@ static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_reque mympd_state->colsQueueLastPlayed = strdup(cols); } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown table %s\"}", p_charbuf1); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown table %s\"}", p_charbuf1); printf("MYMPD_API_COLS_SAVE: Unknown table %s\n", p_charbuf1); } - if (len == 0) { + if (response->length == 0) { if (state_file_write(config, p_charbuf1, cols)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } free(p_charbuf1); } @@ -196,23 +200,23 @@ static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_reque je = json_scanf(request->data, request->length, "{data: {notificationWeb: %B}}", &mympd_state->notificationWeb); if (je == 1) { if (!state_file_write(config, "notificationWeb", (mympd_state->notificationWeb == true ? "true" : "false"))) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationWeb.\"}"); + response->length = snprintf(response->data, 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 (!state_file_write(config, "notificationPage", (mympd_state->notificationPage == true ? "true" : "false"))) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state notificationPage.\"}"); } je = json_scanf(request->data, request->length, "{data: {autoPlay: %B}}", &mympd_state->autoPlay); if (je == 1) { if (!state_file_write(config, "autoPlay", (mympd_state->autoPlay == true ? "true" : "false"))) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state autoPlay.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state autoPlay.\"}"); } 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 (!state_file_write(config, "jukeboxMode", p_char)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxMode.\"}"); } je = json_scanf(request->data, request->length, "{data: {jukeboxPlaylist: %Q}}", &p_charbuf1); if (je == 1) { @@ -220,43 +224,39 @@ static void mympd_api(t_config *config, t_mympd_state *mympd_state, t_work_reque mympd_state->jukeboxPlaylist = p_charbuf1; p_charbuf1 = NULL; if (!state_file_write(config, "jukeboxPlaylist", mympd_state->jukeboxPlaylist)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxPlaylist.\"}"); + response->length = snprintf(response->data, 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 (!state_file_write(config, "jukeboxQueueLength", p_char)) - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Can't set state jukeboxQueueLength.\"}"); } - if (len == 0) { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); + if (response->length == 0) { + response->length = snprintf(response->data, MAX_SIZE, "{\"type\": \"result\", \"data\": \"ok\"}"); } //push settings to mpd_client queue t_work_request *mpd_client_request = (t_work_request *)malloc(sizeof(t_work_request)); - mpd_client_request->conn_id = request->conn_id; + mpd_client_request->conn_id = -1; mpd_client_request->cmd_id = request->cmd_id; mpd_client_request->length = copy_string(mpd_client_request->data, request->data, 1000, request->length); tiny_queue_push(mpd_client_queue, mpd_client_request); } else if (request->cmd_id == MYMPD_API_SETTINGS_GET) { - len = mympd_api_put_settings(config, mympd_state, buffer); + response->length = mympd_api_put_settings(config, mympd_state, response->data); } else { - len = snprintf(buffer, MAX_SIZE, "{\"type\": \"error\", \"data\": \"Unknown cmd_id %u.\"}", request->cmd_id); + response->length = snprintf(response->data, 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); + if (response->length == 0) { + response->length = snprintf(response->data, 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); + LOG_DEBUG() fprintf(stderr, "DEBUG: Send http response to connection %lu (first 800 chars):\n%*.*s\n", request->conn_id, 0, 800, response->data); - t_work_result *response = (t_work_result *)malloc(sizeof(t_work_result)); - 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); } From 09dd391ab839cffc0b089ff4f54e826f0a321aed Mon Sep 17 00:00:00 2001 From: jcorporation Date: Thu, 31 Jan 2019 21:02:53 +0000 Subject: [PATCH 59/59] Feat: docker support files --- Dockerfile | 7 +++---- README.md | 3 ++- contrib/docker/compose | 29 +++++++++++++++++++++++++++++ contrib/docker/init.sh | 9 +-------- 4 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 contrib/docker/compose diff --git a/Dockerfile b/Dockerfile index 8ebfad8..b94ee37 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,11 +24,10 @@ WORKDIR / RUN tar -czvf /mympd.tar.gz -C /myMPD-dist . FROM library/debian:9-slim -ENV MYMPD_COVERIMAGENAME=folder.jpg -ENV MYMPD_MPDHOST=127.0.0.1 -ENV MYMPD_MPDPORT=6600 -ENV MYMPD_SSL=true ENV MYMPD_LOGLEVEL=1 +ENV MPD_MPDHOST=127.0.0.1 +ENV MPD_MPDPORT=6600 +ENV WEBSERVER_SSL=true RUN apt-get update && apt-get install libssl-dev openssl -y COPY --from=build /libmpdclient-master.tar.gz / COPY --from=build /mympd.tar.gz / diff --git a/README.md b/README.md index b104653..6863c64 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ myMPD myMPD is a lightweight MPD web client that runs without a dedicated webserver or interpreter. It's tuned for minimal resource usage and requires only very litte dependencies. -myMPD is a fork of ympd (https://github.com/notandy/ympd). +myMPD is a fork of ympd (https://github.com/notandy/ympd). This fork provides a reworked ui based on Bootstrap 4, a modernized backend and many new features while having the same small footprint as ympd. **Design principles:** @@ -30,6 +30,7 @@ This fork provides a reworked ui based on Bootstrap 4, a modernized backend and - Local playback of mpd http stream (html5 audio api) - Progressiv Web App enabled - Embedded Webserver (mongoose) + - Docker support myMPD is work in progress. Bugreportes and feature requests are very welcome. - https://github.com/jcorporation/myMPD/issues diff --git a/contrib/docker/compose b/contrib/docker/compose new file mode 100644 index 0000000..edde227 --- /dev/null +++ b/contrib/docker/compose @@ -0,0 +1,29 @@ +version: '3' + +services: + mympd: + container_name: mympd +# environment: +# MPD_MPDHOST: 192.168.1.1 +# MPD_MPDPORT: 6600 +# WEBSERVER_SSL: true +# MYMPD_LOGLEVEL: 2 +# image: mympd:latest + build: $MYMPD_BUILD_PATH + networks: + - mympd + ports: + - "40000:80" + - "40001:443" + restart: always + volumes: + - /path/to/music/:/usr/share/mympd/htdocs/library/:ro + - mympd:/etc/mympd/ + +networks: + + mympd: + +volumes: + + mympd: diff --git a/contrib/docker/init.sh b/contrib/docker/init.sh index e4b3e63..4f4d9a7 100644 --- a/contrib/docker/init.sh +++ b/contrib/docker/init.sh @@ -1,11 +1,4 @@ #!/bin/sh /postinst -sed -i "s#mpdhost = 127.0.0.1#mpdhost = $MYMPD_MPDHOST#g" /etc/mympd/mympd.conf -sed -i "s#mpdport = 6600#mpdport = $MYMPD_MPDPORT#g" /etc/mympd/mympd.conf -sed -i "s#ssl = true#ssl = $MYMPD_SSL#g" /etc/mympd/mympd.conf -sed -i "s#coverimagename = folder.jpg#coverimagename = $MYMPD_COVERIMAGENAME#g" /etc/mympd/mympd.conf -sed -i "s#loglevel = 1#loglevel = $MYMPD_LOGLEVEL#g" /etc/mympd/mympd.conf -mympd /etc/mympd/mympd.conf - - +mympd