From 97e4fe2e949e8c5ca64ebdb46a6bfd988af7453e Mon Sep 17 00:00:00 2001 From: jcorporation Date: Sat, 5 Jan 2019 00:01:34 +0000 Subject: [PATCH] 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