1
0
mirror of https://github.com/SuperBFG7/ympd synced 2025-11-05 00:03:00 +00:00

initial mongoose checkin

This commit is contained in:
Andrew Karpow
2014-02-16 19:46:53 +01:00
parent 5920d9f1bf
commit 79e38e7edd
13 changed files with 4938 additions and 676 deletions

View File

@@ -26,159 +26,29 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <libwebsockets.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <mpd/client.h>
#include "http_server.h"
#include "mpd_client.h"
#include "config.h"
#include "http_files.h"
char *resource_path = LOCAL_RESOURCE_PATH;
extern enum mpd_conn_states mpd_conn_state;
struct serveable {
const char *urlpath;
const char *mimetype;
};
static const struct serveable whitelist[] = {
{ "/css/bootstrap.css", "text/css" },
{ "/css/mpd.css", "text/css" },
{ "/js/bootstrap.min.js", "text/javascript" },
{ "/js/mpd.js", "text/javascript" },
{ "/js/jquery-1.10.2.min.js", "text/javascript" },
{ "/js/jquery.cookie.js", "text/javascript" },
{ "/js/bootstrap-slider.js", "text/javascript" },
{ "/js/bootstrap-notify.js", "text/javascript" },
{ "/js/sammy.js", "text/javascript" },
{ "/fonts/glyphicons-halflings-regular.woff", "application/x-font-woff"},
{ "/fonts/glyphicons-halflings-regular.svg", "image/svg+xml"},
{ "/fonts/glyphicons-halflings-regular.ttf", "application/x-font-ttf"},
{ "/fonts/glyphicons-halflings-regular.eot", "application/vnd.ms-fontobject"},
{ "/assets/favicon.ico", "image/vnd.microsoft.icon" },
/* last one is the default served if no match */
{ "/index.html", "text/html" },
};
static const char http_header[] = "HTTP/1.0 200 OK\x0d\x0a"
"Server: libwebsockets\x0d\x0a"
"Content-Type: application/json\x0d\x0a"
"Content-Length: 000000\x0d\x0a\x0d\x0a";
/* Converts a hex character to its integer value */
char from_hex(char ch) {
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
/* Returns a url-decoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_decode(char *str) {
char *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
while (*pstr) {
if (*pstr == '%') {
if (pstr[1] && pstr[2]) {
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
pstr += 2;
}
} else if (*pstr == '+') {
*pbuf++ = ' ';
} else {
*pbuf++ = *pstr;
}
pstr++;
}
*pbuf = '\0';
return buf;
}
int callback_http(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len)
int callback_http(struct mg_connection *c)
{
char *response_buffer, *p;
char buf[64];
size_t n, response_size = 0;
const struct embedded_file *req_file;
switch (reason) {
case LWS_CALLBACK_HTTP:
if(in && strncmp((const char *)in, "/api/", 5) == 0)
{
if(!strcmp(c->uri, "/"))
req_file = find_embedded_file("/index.html");
else
req_file = find_embedded_file(c->uri);
p = (char *)malloc(MAX_SIZE + 100);
memcpy(p, http_header, sizeof(http_header) - 1);
response_buffer = p + sizeof(http_header) - 1;
/* put content length and payload to buffer */
if(mpd_conn_state != MPD_CONNECTED) {}
else if(strncmp((const char *)in, "/api/get_browse", 15) == 0)
{
char *url;
if(sscanf(in, "/api/get_browse/%m[^\t\n]", &url) == 1)
{
char *url_decoded = url_decode(url);
response_size = mpd_put_browse(response_buffer, url_decoded);
free(url_decoded);
free(url);
}
else
response_size = mpd_put_browse(response_buffer, "/");
}
else if(strncmp((const char *)in, "/api/get_playlist", 17) == 0)
response_size = mpd_put_playlist(response_buffer);
else if(strncmp((const char *)in, "/api/get_version", 16) == 0)
response_size = snprintf(response_buffer, MAX_SIZE,
"{\"type\":\"version\",\"data\":{"
"\"ympd_version\":\"%d.%d.%d\","
"\"mpd_version\":\"%d.%d.%d\""
"}}",
YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH,
LIBMPDCLIENT_MAJOR_VERSION, LIBMPDCLIENT_MINOR_VERSION,
LIBMPDCLIENT_PATCH_VERSION);
/* Copy size to content-length field */
sprintf(buf, "%6zu", response_size);
memcpy(p + sizeof(http_header) - 11, buf, 6);
n = libwebsocket_write(wsi, (unsigned char *)p,
sizeof(http_header) - 1 + response_size, LWS_WRITE_HTTP);
free(p);
/*
* book us a LWS_CALLBACK_HTTP_WRITEABLE callback
*/
libwebsocket_callback_on_writable(context, wsi);
}
else
{
for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++)
if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0)
break;
sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype, NULL))
return -1; /* through completion or error, close the socket */
}
break;
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
/* kill the connection after we sent one file */
return -1;
default:
break;
if(req_file)
{
mg_send_header(c, "Content-Type", req_file->mimetype);
mg_send_data(c, req_file->data, req_file->size);
return MG_REQUEST_PROCESSED;
}
return 0;
mg_send_status(c, 404);
mg_printf_data(c, "Not Found");
return MG_REQUEST_PROCESSED;
}

View File

@@ -29,15 +29,9 @@
#ifndef __HTTP_SERVER_H__
#define __HTTP_SERVER_H__
#include <libwebsockets.h>
#include "mongoose.h"
struct per_session_data__http {
int fd;
};
int callback_http(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason, void *user,
void *in, size_t len);
int callback_http(struct mg_connection *c);
#endif

4296
src/mongoose.c Normal file

File diff suppressed because it is too large Load Diff

125
src/mongoose.h Normal file
View File

@@ -0,0 +1,125 @@
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
// Copyright (c) 2013-2014 Cesanta Software Limited
// All rights reserved
//
// This library is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this library under the terms of the GNU General
// Public License, 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.
//
// Alternatively, you can license this library under a commercial
// license, as set out in <http://cesanta.com/>.
//
// NOTE: Detailed API documentation is at http://cesanta.com/#docs
#ifndef MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_HEADER_INCLUDED
#define MONGOOSE_VERSION "5.3"
#include <stdio.h> // required for FILE
#include <stddef.h> // required for size_t
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
// This structure contains information about HTTP request.
struct mg_connection {
const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL
char remote_ip[48]; // Max IPv6 string length is 45 characters
const char *local_ip; // Local IP address
unsigned short remote_port; // Client's port
unsigned short local_port; // Local port number
int num_headers; // Number of HTTP headers
struct mg_header {
const char *name; // HTTP header name
const char *value; // HTTP header value
} http_headers[30];
char *content; // POST (or websocket message) data, or NULL
size_t content_len; // content length
int is_websocket; // Connection is a websocket connection
int status_code; // HTTP status code for HTTP error handler
int wsbits; // First byte of the websocket frame
void *server_param; // Parameter passed to mg_add_uri_handler()
void *connection_param; // Placeholder for connection-specific data
void *callback_param; // Used by mg_iterate_over_connections()
};
struct mg_server; // Opaque structure describing server instance
typedef int (*mg_handler_t)(struct mg_connection *);
// Server management functions
struct mg_server *mg_create_server(void *server_param);
void mg_destroy_server(struct mg_server **);
const char *mg_set_option(struct mg_server *, const char *opt, const char *val);
unsigned int mg_poll_server(struct mg_server *, int milliseconds);
void mg_set_request_handler(struct mg_server *, mg_handler_t);
void mg_set_http_close_handler(struct mg_server *, mg_handler_t);
void mg_set_http_error_handler(struct mg_server *, mg_handler_t);
void mg_set_auth_handler(struct mg_server *, mg_handler_t);
const char **mg_get_valid_option_names(void);
const char *mg_get_option(const struct mg_server *server, const char *name);
void mg_set_listening_socket(struct mg_server *, int sock);
int mg_get_listening_socket(struct mg_server *);
void mg_iterate_over_connections(struct mg_server *, mg_handler_t, void *);
// Connection management functions
void mg_send_status(struct mg_connection *, int status_code);
void mg_send_header(struct mg_connection *, const char *name, const char *val);
void mg_send_data(struct mg_connection *, const void *data, int data_len);
void mg_printf_data(struct mg_connection *, const char *format, ...);
int mg_websocket_write(struct mg_connection *, int opcode,
const char *data, size_t data_len);
// Deprecated in favor of mg_send_* interface
int mg_write(struct mg_connection *, const void *buf, int len);
int mg_printf(struct mg_connection *conn, const char *fmt, ...);
const char *mg_get_header(const struct mg_connection *, const char *name);
const char *mg_get_mime_type(const char *name, const char *default_mime_type);
int mg_get_var(const struct mg_connection *conn, const char *var_name,
char *buf, size_t buf_len);
int mg_parse_header(const char *hdr, const char *var_name, char *buf, size_t);
int mg_parse_multipart(const char *buf, int buf_len,
char *var_name, int var_name_len,
char *file_name, int file_name_len,
const char **data, int *data_len);
// Utility functions
void *mg_start_thread(void *(*func)(void *), void *param);
char *mg_md5(char buf[33], ...);
int mg_authorize_digest(struct mg_connection *c, FILE *fp);
// Callback function return codes
enum { MG_REQUEST_NOT_PROCESSED, MG_REQUEST_PROCESSED, MG_REQUEST_CALL_AGAIN };
enum { MG_AUTH_FAIL, MG_AUTH_OK };
enum { MG_ERROR_NOT_PROCESSED, MG_ERROR_PROCESSED };
enum { MG_CLIENT_CONTINUE, MG_CLIENT_CLOSE };
// HTTP client events
enum {
MG_CONNECT_SUCCESS, MG_CONNECT_FAILURE,
MG_DOWNLOAD_SUCCESS, MG_DOWNLOAD_FAILURE
};
int mg_connect(struct mg_server *, const char *host, int port, int use_ssl,
mg_handler_t handler, void *param);
#ifdef __cplusplus
}
#endif // __cplusplus
#endif // MONGOOSE_HEADER_INCLUDED

View File

@@ -31,254 +31,273 @@
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <mpd/client.h>
#include <mpd/status.h>
#include <mpd/song.h>
#include <mpd/entity.h>
#include <mpd/search.h>
#include <mpd/tag.h>
#include "mpd_client.h"
#include "config.h"
struct mpd_connection *conn = NULL;
enum mpd_conn_states mpd_conn_state = MPD_DISCONNECTED;
enum mpd_state mpd_play_state = MPD_STATE_UNKNOWN;
const char * mpd_cmd_strs[] = {
MPD_CMDS(GEN_STR)
};
int callback_ympd(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len)
static inline enum mpd_cmd_ids get_cmd_id(char *cmd)
{
size_t n, m = -1;
char *buf = NULL, *p;
struct per_session_data__ympd *pss = (struct per_session_data__ympd *)user;
for(int 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;
switch (reason) {
case LWS_CALLBACK_ESTABLISHED:
lwsl_info("mpd_client: "
"LWS_CALLBACK_ESTABLISHED\n");
return -1;
}
int callback_mpd(struct mg_connection *c)
{
enum mpd_cmd_ids cmd_id = get_cmd_id(c->content);
size_t n = 0;
unsigned int uint_buf, uint_buf_2;
int int_buf;
char *p_charbuf;
if(cmd_id == -1)
return MG_CLIENT_CONTINUE;
if(mpd.conn_state != MPD_CONNECTED && cmd_id != MPD_API_SET_MPDHOST &&
cmd_id != MPD_API_GET_MPDHOST && cmd_id != MPD_API_SET_MPDPASS)
return MG_CLIENT_CONTINUE;
switch(cmd_id)
{
case MPD_API_UPDATE_DB:
mpd_run_update(mpd.conn, NULL);
break;
case LWS_CALLBACK_SERVER_WRITEABLE:
buf = (char *)malloc(MAX_SIZE + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
if(buf == NULL) {
lwsl_err("ERROR Failed allocating memory\n");
return -1;
}
p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
if(pss->do_send & DO_SEND_ERROR) {
n = snprintf(p, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}",
mpd_connection_get_error_message(conn));
pss->do_send &= ~DO_SEND_ERROR;
/* Try to recover error */
if (!mpd_connection_clear_error(conn))
mpd_conn_state = MPD_FAILURE;
}
else if(mpd_conn_state != MPD_CONNECTED) {
n = snprintf(p, MAX_SIZE, "{\"type\":\"disconnected\"}");
}
else if(pss->do_send & DO_SEND_PLAYLIST) {
/*n = mpd_put_playlist(p);*/
n = snprintf(p, MAX_SIZE, "{\"type\":\"update_playlist\"}");
pss->do_send &= ~DO_SEND_PLAYLIST;
}
else if(pss->do_send & DO_SEND_TRACK_INFO) {
n = mpd_put_current_song(p);
pss->do_send &= ~DO_SEND_TRACK_INFO;
}
else if(pss->do_send & DO_SEND_BROWSE) {
n = mpd_put_browse(p, pss->browse_path);
pss->do_send &= ~DO_SEND_BROWSE;
free(pss->browse_path);
}
else if(pss->do_send & DO_SEND_MPDHOST) {
n = snprintf(p, MAX_SIZE, "{\"type\":\"mpdhost\", \"data\": "
"{\"host\" : \"%s\", \"port\": \"%d\"}"
"}", mpd_host, mpd_port);
pss->do_send &= ~DO_SEND_MPDHOST;
}
else {
/* Default Action */
int current_song_id;
unsigned queue_version;
n = mpd_put_state(p, &current_song_id, &queue_version);
if(current_song_id != pss->current_song_id)
{
pss->current_song_id = current_song_id;
pss->do_send |= DO_SEND_TRACK_INFO;
libwebsocket_callback_on_writable(context, wsi);
}
else if(pss->queue_version != queue_version) {
pss->queue_version = queue_version;
pss->do_send |= DO_SEND_PLAYLIST;
libwebsocket_callback_on_writable(context, wsi);
}
}
if(n > 0)
m = libwebsocket_write(wsi, (unsigned char *)p, n, LWS_WRITE_TEXT);
if (m < n) {
lwsl_err("ERROR %d writing to socket\n", n, m);
free(buf);
return -1;
}
free(buf);
case MPD_API_SET_PAUSE:
mpd_run_toggle_pause(mpd.conn);
break;
case LWS_CALLBACK_RECEIVE:
if(!strcmp((const char *)in, MPD_API_GET_PLAYLIST))
pss->do_send |= DO_SEND_PLAYLIST;
else if(!strcmp((const char *)in, MPD_API_GET_TRACK_INFO))
pss->do_send |= DO_SEND_TRACK_INFO;
else if(!strcmp((const char *)in, MPD_API_UPDATE_DB))
mpd_run_update(conn, NULL);
else if(!strcmp((const char *)in, MPD_API_SET_PAUSE))
mpd_run_toggle_pause(conn);
else if(!strcmp((const char *)in, MPD_API_SET_PREV))
mpd_run_previous(conn);
else if(!strcmp((const char *)in, MPD_API_SET_NEXT))
mpd_run_next(conn);
else if(!strcmp((const char *)in, MPD_API_SET_PLAY))
mpd_run_play(conn);
else if(!strcmp((const char *)in, MPD_API_SET_STOP))
mpd_run_stop(conn);
else if(!strcmp((const char *)in, MPD_API_RM_ALL))
mpd_run_clear(conn);
else if(!strncmp((const char *)in, MPD_API_RM_TRACK, sizeof(MPD_API_RM_TRACK)-1)) {
unsigned id;
if(sscanf(in, "MPD_API_RM_TRACK,%u", &id))
mpd_run_delete_id(conn, id);
libwebsocket_callback_on_writable(context, wsi);
case MPD_API_SET_PREV:
mpd_run_previous(mpd.conn);
break;
case MPD_API_SET_NEXT:
mpd_run_next(mpd.conn);
break;
case MPD_API_SET_PLAY:
mpd_run_play(mpd.conn);
break;
case MPD_API_SET_STOP:
mpd_run_stop(mpd.conn);
break;
case MPD_API_RM_ALL:
mpd_run_clear(mpd.conn);
break;
case MPD_API_GET_PLAYLIST:
n = mpd_put_playlist(mpd.buf);
break;
case MPD_API_RM_TRACK:
if(sscanf(c->content, "MPD_API_RM_TRACK,%u", &uint_buf))
mpd_run_delete_id(mpd.conn, uint_buf);
break;
case MPD_API_PLAY_TRACK:
if(sscanf(c->content, "MPD_API_PLAY_TRACK,%u", &uint_buf))
mpd_run_play_id(mpd.conn, uint_buf);
break;
case MPD_API_TOGGLE_RANDOM:
if(sscanf(c->content, "MPD_API_TOGGLE_RANDOM,%u", &uint_buf))
mpd_run_random(mpd.conn, uint_buf);
break;
case MPD_API_TOGGLE_REPEAT:
if(sscanf(c->content, "MPD_API_TOGGLE_REPEAT,%u", &uint_buf))
mpd_run_repeat(mpd.conn, uint_buf);
break;
case MPD_API_TOGGLE_CONSUME:
if(sscanf(c->content, "MPD_API_TOGGLE_CONSUME,%u", &uint_buf))
mpd_run_consume(mpd.conn, uint_buf);
break;
case MPD_API_TOGGLE_SINGLE:
if(sscanf(c->content, "MPD_API_TOGGLE_SINGLE,%u", &uint_buf))
mpd_run_single(mpd.conn, uint_buf);
break;
case MPD_API_SET_VOLUME:
if(sscanf(c->content, "MPD_API_SET_VOLUME,%ud", &uint_buf) && uint_buf <= 100)
mpd_run_set_volume(mpd.conn, uint_buf);
break;
case MPD_API_SET_SEEK:
if(sscanf(c->content, "MPD_API_SET_SEEK,%u,%u", &uint_buf, &uint_buf_2))
mpd_run_seek_id(mpd.conn, uint_buf, uint_buf_2);
break;
case MPD_API_GET_BROWSE:
if(sscanf(c->content, "MPD_API_GET_BROWSE,%m[^\t\n]", &p_charbuf) && p_charbuf != NULL)
n = mpd_put_browse(mpd.buf, p_charbuf);
else
n = mpd_put_browse(mpd.buf, "/");
free(p_charbuf);
break;
case MPD_API_ADD_TRACK:
if(sscanf(c->content, "MPD_API_ADD_TRACK,%m[^\t\n]", &p_charbuf) && p_charbuf != NULL)
{
mpd_run_add(mpd.conn, p_charbuf);
free(p_charbuf);
}
else if(!strncmp((const char *)in, MPD_API_PLAY_TRACK, sizeof(MPD_API_PLAY_TRACK)-1)) {
unsigned id;
if(sscanf(in, "MPD_API_PLAY_TRACK,%u", &id))
mpd_run_play_id(conn, id);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_RANDOM, sizeof(MPD_API_TOGGLE_RANDOM)-1)) {
unsigned random;
if(sscanf(in, "MPD_API_TOGGLE_RANDOM,%u", &random))
mpd_run_random(conn, random);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_REPEAT, sizeof(MPD_API_TOGGLE_REPEAT)-1)) {
unsigned repeat;
if(sscanf(in, "MPD_API_TOGGLE_REPEAT,%u", &repeat))
mpd_run_repeat(conn, repeat);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_CONSUME, sizeof(MPD_API_TOGGLE_CONSUME)-1)) {
unsigned consume;
if(sscanf(in, "MPD_API_TOGGLE_CONSUME,%u", &consume))
mpd_run_consume(conn, consume);
}
else if(!strncmp((const char *)in, MPD_API_TOGGLE_SINGLE, sizeof(MPD_API_TOGGLE_SINGLE)-1)) {
unsigned single;
if(sscanf(in, "MPD_API_TOGGLE_SINGLE,%u", &single))
mpd_run_single(conn, single);
}
else if(!strncmp((const char *)in, MPD_API_SET_VOLUME, sizeof(MPD_API_SET_VOLUME)-1)) {
unsigned int volume;
if(sscanf(in, "MPD_API_SET_VOLUME,%ud", &volume) && volume <= 100)
mpd_run_set_volume(conn, volume);
}
else if(!strncmp((const char *)in, MPD_API_SET_SEEK, sizeof(MPD_API_SET_SEEK)-1)) {
unsigned int seek, songid;
if(sscanf(in, "MPD_API_SET_SEEK,%u,%u", &songid, &seek)) {
mpd_run_seek_id(conn, songid, seek);
}
}
else if(!strncmp((const char *)in, MPD_API_GET_BROWSE, sizeof(MPD_API_GET_BROWSE)-1)) {
char *dir;
if(sscanf(in, "MPD_API_GET_BROWSE,%m[^\t\n]", &dir) && dir != NULL) {
pss->do_send |= DO_SEND_BROWSE;
pss->browse_path = dir;
}
}
else if(!strncmp((const char *)in, MPD_API_ADD_TRACK, sizeof(MPD_API_ADD_TRACK)-1)) {
char *uri;
if(sscanf(in, "MPD_API_ADD_TRACK,%m[^\t\n]", &uri) && uri != NULL) {
mpd_run_add(conn, uri);
free(uri);
}
}
else if(!strncmp((const char *)in, MPD_API_ADD_PLAY_TRACK, sizeof(MPD_API_ADD_PLAY_TRACK)-1)) {
char *uri;
if(sscanf(in, "MPD_API_ADD_PLAY_TRACK,%m[^\t\n]", &uri) && uri != NULL) {
int added_song = mpd_run_add_id(conn, uri);
if(added_song != -1)
mpd_run_play_id(conn, added_song);
free(uri);
}
break;
case MPD_API_ADD_PLAY_TRACK:
if(sscanf(c->content, "MPD_API_ADD_PLAY_TRACK,%m[^\t\n]", &p_charbuf) && p_charbuf != NULL)
{
int_buf = mpd_run_add_id(mpd.conn, p_charbuf);
if(int_buf != -1)
mpd_run_play_id(mpd.conn, int_buf);
free(p_charbuf);
}
#ifdef WITH_MPD_HOST_CHANGE
else if(!strncmp((const char *)in, MPD_API_SET_MPDHOST, sizeof(MPD_API_SET_MPDHOST)-1)) {
char *host;
int port = 0;
if(sscanf(in, "MPD_API_SET_MPDHOST,%d,%m[^\t\n ]", &port, &host) && host != NULL && port > 0) {
strncpy(mpd_host, host, sizeof(mpd_host));
free(host);
mpd_port = port;
mpd_conn_state = MPD_RECONNECT;
break;
}
/* Commands allowed when disconnected from MPD server */
case MPD_API_SET_MPDHOST:
int_buf = 0;
if(sscanf(c->content, "MPD_API_SET_MPDHOST,%d,%m[^\t\n ]", &int_buf, &p_charbuf) &&
p_charbuf != NULL && int_buf > 0)
{
strncpy(mpd.host, p_charbuf, sizeof(mpd.host));
free(p_charbuf);
mpd.port = int_buf;
mpd.conn_state = MPD_RECONNECT;
return MG_CLIENT_CONTINUE;
}
else if(!strncmp((const char *)in, MPD_API_GET_MPDHOST, sizeof(MPD_API_GET_MPDHOST)-1)) {
pss->do_send |= DO_SEND_MPDHOST;
break;
case MPD_API_GET_MPDHOST:
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"mpdhost\", \"data\": "
"{\"host\" : \"%s\", \"port\": \"%d\", \"passwort_set\": %s}"
"}", mpd.host, mpd.port, mpd.password ? "true" : "false");
printf("mpd_password is %p\n", mpd.password);
case MPD_API_SET_MPDPASS:
if(sscanf(c->content, "MPD_API_SET_MPDPASS,%m[^\t\n ]", &p_charbuf) &&
p_charbuf != NULL)
{
if(mpd.password)
free(mpd.password);
mpd.password = p_charbuf;
mpd.conn_state = MPD_RECONNECT;
printf("Got mpd pw %s\n", mpd.password);
return MG_CLIENT_CONTINUE;
}
break;
#endif
if(mpd_conn_state == MPD_CONNECTED && mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS)
pss->do_send |= DO_SEND_ERROR;
break;
default:
break;
}
if(mpd.conn_state == MPD_CONNECTED && mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS)
{
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\", \"data\": \"%s\"}",
mpd_connection_get_error_message(mpd.conn));
/* Try to recover error */
if (!mpd_connection_clear_error(mpd.conn))
mpd.conn_state = MPD_FAILURE;
}
if(n > 0)
mg_websocket_write(c, 1, mpd.buf, n);
return MG_CLIENT_CONTINUE;
}
int mpd_close_handler(struct mg_connection *c)
{
/* Cleanup session data */
if(c->connection_param)
free(c->connection_param);
return 0;
}
void mpd_loop()
static int mpd_notify_callback(struct mg_connection *c) {
size_t n;
if(!c->is_websocket)
return MG_REQUEST_PROCESSED;
if(c->callback_param)
{
/* error message? */
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"error\",\"data\":\"%s\"}",
(const char *)c->callback_param);
mg_websocket_write(c, 1, mpd.buf, n);
return MG_REQUEST_PROCESSED;
}
if(!c->connection_param)
c->connection_param = calloc(1, sizeof(struct t_mpd_client_session));
struct t_mpd_client_session *s = (struct t_mpd_client_session *)c->connection_param;
if(mpd.conn_state != MPD_CONNECTED) {
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"disconnected\"}");
mg_websocket_write(c, 1, mpd.buf, n);
}
else
{
mg_websocket_write(c, 1, mpd.buf, mpd.buf_size);
if(s->song_id != mpd.song_id)
{
n = mpd_put_current_song(mpd.buf);
mg_websocket_write(c, 1, mpd.buf, n);
s->song_id = mpd.song_id;
}
if(s->queue_version != mpd.queue_version)
{
n = snprintf(mpd.buf, MAX_SIZE, "{\"type\":\"update_playlist\"}");
mg_websocket_write(c, 1, mpd.buf, n);
s->queue_version = mpd.queue_version;
}
}
return MG_REQUEST_PROCESSED;
}
void mpd_poll(struct mg_server *s)
{
switch (mpd_conn_state) {
switch (mpd.conn_state) {
case MPD_DISCONNECTED:
/* Try to connect */
lwsl_notice("MPD Connecting to %s:%d\n", mpd_host, mpd_port);
conn = mpd_connection_new(mpd_host, mpd_port, 3000);
if (conn == NULL) {
lwsl_err("Out of memory.");
mpd_conn_state = MPD_FAILURE;
fprintf(stdout, "MPD Connecting to %s:%d\n", mpd.host, mpd.port);
mpd.conn = mpd_connection_new(mpd.host, mpd.port, 3000);
if (mpd.conn == NULL) {
fprintf(stderr, "Out of memory.");
mpd.conn_state = MPD_FAILURE;
return;
}
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS) {
lwsl_err("MPD connection: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE;
if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS) {
fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
mg_iterate_over_connections(s, mpd_notify_callback,
(void *)mpd_connection_get_error_message(mpd.conn));
mpd.conn_state = MPD_FAILURE;
return;
}
lwsl_notice("MPD connected.\n");
mpd_conn_state = MPD_CONNECTED;
if(mpd.password && !mpd_run_password(mpd.conn, mpd.password))
{
fprintf(stderr, "MPD connection: %s\n", mpd_connection_get_error_message(mpd.conn));
mg_iterate_over_connections(s, mpd_notify_callback,
(void *)mpd_connection_get_error_message(mpd.conn));
mpd.conn_state = MPD_FAILURE;
return;
}
fprintf(stderr, "MPD connected.\n");
mpd.conn_state = MPD_CONNECTED;
break;
case MPD_FAILURE:
lwsl_err("MPD connection failed.\n");
fprintf(stderr, "MPD connection failed.\n");
case MPD_DISCONNECT:
case MPD_RECONNECT:
if(conn != NULL)
mpd_connection_free(conn);
conn = NULL;
mpd_conn_state = MPD_DISCONNECTED;
if(mpd.conn != NULL)
mpd_connection_free(mpd.conn);
mpd.conn = NULL;
mpd.conn_state = MPD_DISCONNECTED;
break;
case MPD_CONNECTED:
/* Nothing to do */
mpd.buf_size = mpd_put_state(mpd.buf, &mpd.song_id, &mpd.queue_version);
mg_iterate_over_connections(s, mpd_notify_callback, NULL);
break;
}
}
@@ -308,10 +327,10 @@ int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version)
struct mpd_status *status;
int len;
status = mpd_run_status(conn);
status = mpd_run_status(mpd.conn);
if (!status) {
lwsl_err("MPD mpd_run_status: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE;
fprintf(stderr, "MPD mpd_run_status: %s\n", mpd_connection_get_error_message(mpd.conn));
mpd.conn_state = MPD_FAILURE;
return 0;
}
@@ -345,7 +364,7 @@ int mpd_put_current_song(char *buffer)
const char *end = buffer + MAX_SIZE;
struct mpd_song *song;
song = mpd_run_current_song(conn);
song = mpd_run_current_song(mpd.conn);
if(song == NULL)
return 0;
@@ -362,7 +381,7 @@ int mpd_put_current_song(char *buffer)
cur += snprintf(cur, end - cur, "}}");
mpd_song_free(song);
mpd_response_finish(conn);
mpd_response_finish(mpd.conn);
return cur - buffer;
}
@@ -373,19 +392,19 @@ int mpd_put_playlist(char *buffer)
const char *end = buffer + MAX_SIZE;
struct mpd_entity *entity;
if (!mpd_send_list_queue_meta(conn)) {
lwsl_err("MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(conn));
if (!mpd_send_list_queue_meta(mpd.conn)) {
fprintf(stderr, "MPD mpd_send_list_queue_meta: %s\n", mpd_connection_get_error_message(mpd.conn));
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}",
mpd_connection_get_error_message(conn));
mpd_connection_get_error_message(mpd.conn));
if (!mpd_connection_clear_error(conn))
mpd_conn_state = MPD_FAILURE;
if (!mpd_connection_clear_error(mpd.conn))
mpd.conn_state = MPD_FAILURE;
return cur - buffer;
}
cur += snprintf(cur, end - cur, "{\"type\": \"playlist\", \"data\": [ ");
while((entity = mpd_recv_entity(conn)) != NULL) {
while((entity = mpd_recv_entity(mpd.conn)) != NULL) {
const struct mpd_song *song;
if(mpd_entity_get_type(entity) == MPD_ENTITY_TYPE_SONG) {
@@ -413,18 +432,18 @@ int mpd_put_browse(char *buffer, char *path)
const char *end = buffer + MAX_SIZE;
struct mpd_entity *entity;
if (!mpd_send_list_meta(conn, path)) {
lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn));
if (!mpd_send_list_meta(mpd.conn, path)) {
fprintf(stderr, "MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(mpd.conn));
cur += snprintf(cur, end - cur, "{\"type\":\"error\",\"data\":\"%s\"}",
mpd_connection_get_error_message(conn));
mpd_connection_get_error_message(mpd.conn));
if (!mpd_connection_clear_error(conn))
mpd_conn_state = MPD_FAILURE;
if (!mpd_connection_clear_error(mpd.conn))
mpd.conn_state = MPD_FAILURE;
return cur - buffer;
}
cur += snprintf(cur, end - cur, "{\"type\":\"browse\",\"data\":[ ");
while((entity = mpd_recv_entity(conn)) != NULL) {
while((entity = mpd_recv_entity(mpd.conn)) != NULL) {
const struct mpd_song *song;
const struct mpd_directory *dir;
const struct mpd_playlist *pl;
@@ -463,9 +482,9 @@ int mpd_put_browse(char *buffer, char *path)
mpd_entity_free(entity);
}
if (mpd_connection_get_error(conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(conn)) {
lwsl_err("MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(conn));
mpd_conn_state = MPD_FAILURE;
if (mpd_connection_get_error(mpd.conn) != MPD_ERROR_SUCCESS || !mpd_response_finish(mpd.conn)) {
fprintf(stderr, "MPD mpd_send_list_meta: %s\n", mpd_connection_get_error_message(mpd.conn));
mpd.conn_state = MPD_FAILURE;
return 0;
}
@@ -474,3 +493,9 @@ int mpd_put_browse(char *buffer, char *path)
cur += snprintf(cur, end - cur, "] }");
return cur - buffer;
}
void mpd_disconnect()
{
mpd.conn_state = MPD_DISCONNECT;
mpd_poll(NULL);
}

View File

@@ -29,68 +29,75 @@
#ifndef __MPD_CLIENT_H__
#define __MPD_CLIENT_H__
#include <libwebsockets.h>
#include "mongoose.h"
#define MAX_SIZE 1024 * 100
#define GEN_ENUM(X) X,
#define GEN_STR(X) #X,
#define MPD_CMDS(X) \
X(MPD_API_GET_PLAYLIST) \
X(MPD_API_GET_BROWSE) \
X(MPD_API_GET_MPDHOST) \
X(MPD_API_ADD_TRACK) \
X(MPD_API_ADD_PLAY_TRACK) \
X(MPD_API_PLAY_TRACK) \
X(MPD_API_RM_TRACK) \
X(MPD_API_RM_ALL) \
X(MPD_API_SET_VOLUME) \
X(MPD_API_SET_PAUSE) \
X(MPD_API_SET_PLAY) \
X(MPD_API_SET_STOP) \
X(MPD_API_SET_SEEK) \
X(MPD_API_SET_NEXT) \
X(MPD_API_SET_PREV) \
X(MPD_API_SET_MPDHOST) \
X(MPD_API_SET_MPDPASS) \
X(MPD_API_UPDATE_DB) \
X(MPD_API_TOGGLE_RANDOM) \
X(MPD_API_TOGGLE_CONSUME) \
X(MPD_API_TOGGLE_SINGLE) \
X(MPD_API_TOGGLE_REPEAT)
#define DO_SEND_STATE (1 << 0)
#define DO_SEND_PLAYLIST (1 << 1)
#define DO_SEND_TRACK_INFO (1 << 2)
#define DO_SEND_BROWSE (1 << 3)
#define DO_SEND_ERROR (1 << 4)
#define DO_SEND_MPDHOST (1 << 5)
#define MPD_API_GET_SEEK "MPD_API_GET_SEEK"
#define MPD_API_GET_PLAYLIST "MPD_API_GET_PLAYLIST"
#define MPD_API_GET_TRACK_INFO "MPD_API_GET_TRACK_INFO"
#define MPD_API_GET_BROWSE "MPD_API_GET_BROWSE"
#define MPD_API_GET_MPDHOST "MPD_API_GET_MPDHOST"
#define MPD_API_ADD_TRACK "MPD_API_ADD_TRACK"
#define MPD_API_ADD_PLAY_TRACK "MPD_API_ADD_PLAY_TRACK"
#define MPD_API_PLAY_TRACK "MPD_API_PLAY_TRACK"
#define MPD_API_RM_TRACK "MPD_API_RM_TRACK"
#define MPD_API_RM_ALL "MPD_API_RM_ALL"
#define MPD_API_SET_VOLUME "MPD_API_SET_VOLUME"
#define MPD_API_SET_PAUSE "MPD_API_SET_PAUSE"
#define MPD_API_SET_PLAY "MPD_API_SET_PLAY"
#define MPD_API_SET_STOP "MPD_API_SET_STOP"
#define MPD_API_SET_SEEK "MPD_API_SET_SEEK"
#define MPD_API_SET_NEXT "MPD_API_SET_PREV"
#define MPD_API_SET_PREV "MPD_API_SET_NEXT"
#define MPD_API_SET_MPDHOST "MPD_API_SET_MPDHOST"
#define MPD_API_UPDATE_DB "MPD_API_UPDATE_DB"
#define MPD_API_TOGGLE_RANDOM "MPD_API_TOGGLE_RANDOM"
#define MPD_API_TOGGLE_CONSUME "MPD_API_TOGGLE_CONSUME"
#define MPD_API_TOGGLE_SINGLE "MPD_API_TOGGLE_SINGLE"
#define MPD_API_TOGGLE_REPEAT "MPD_API_TOGGLE_REPEAT"
struct per_session_data__ympd {
int do_send;
unsigned queue_version;
int current_song_id;
char *browse_path;
enum mpd_cmd_ids {
MPD_CMDS(GEN_ENUM)
};
enum mpd_conn_states {
MPD_FAILURE,
MPD_DISCONNECTED,
MPD_FAILURE,
MPD_CONNECTED,
MPD_RECONNECT
MPD_RECONNECT,
MPD_DISCONNECT
};
void *mpd_idle_connection(void *_data);
int callback_ympd(struct libwebsocket_context *context,
struct libwebsocket *wsi,
enum libwebsocket_callback_reasons reason,
void *user, void *in, size_t len);
void mpd_loop();
struct t_mpd {
int port;
char host[128];
char *password;
struct mpd_connection *conn;
enum mpd_conn_states conn_state;
/* Reponse Buffer */
char buf[MAX_SIZE];
size_t buf_size;
int song_id;
unsigned queue_version;
} mpd;
struct t_mpd_client_session {
int song_id;
unsigned queue_version;
};
void mpd_poll(struct mg_server *s);
int callback_mpd(struct mg_connection *c);
int mpd_close_handler(struct mg_connection *c);
int mpd_put_state(char *buffer, int *current_song_id, unsigned *queue_version);
int mpd_put_current_song(char *buffer);
int mpd_put_playlist(char *buffer);
int mpd_put_browse(char *buffer, char *path);
int mpd_port;
char mpd_host[255];
void mpd_disconnect();
#endif

View File

@@ -1,40 +0,0 @@
/*
from http://www.geekhideout.com/urlcode.shtml
public domain
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
/* Converts a hex character to its integer value */
char from_hex(char ch) {
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}
/* Converts an integer value to its hex character*/
char to_hex(char code) {
static char hex[] = "0123456789abcdef";
return hex[code & 15];
}
/* Returns a url-decoded version of str */
/* IMPORTANT: be sure to free() the returned string after use */
char *url_decode(char *str) {
char *pstr = str, *buf = malloc(strlen(str) + 1), *pbuf = buf;
while (*pstr) {
if (*pstr == '%') {
if (pstr[1] && pstr[2]) {
*pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
pstr += 2;
}
} else if (*pstr == '+') {
*pbuf++ = ' ';
} else {
*pbuf++ = *pstr;
}
pstr++;
}
*pbuf = '\0';
return buf;
}

View File

@@ -26,39 +26,20 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <libwebsockets.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <pthread.h>
#include "mongoose.h"
#include "http_server.h"
#include "mpd_client.h"
#include "config.h"
extern char *optarg;
extern char *resource_path;
struct libwebsocket_protocols protocols[] = {
/* first protocol must always be HTTP handler */
{
"http-only", /* name */
callback_http, /* callback */
sizeof (struct per_session_data__http), /* per_session_data_size */
0, /* max frame size / rx buffer */
},
{
"ympd-client",
callback_ympd,
sizeof(struct per_session_data__ympd),
255,
0,
},
{ NULL, NULL, 0, 0, 0 } /* terminator */
};
int force_exit = 0;
@@ -67,80 +48,62 @@ void bye()
force_exit = 1;
}
static int server_callback(struct mg_connection *c) {
//printf("Got REQ: (%lu) WS:%d\n", c->content_len, c->is_websocket);
//fwrite(c->content, c->content_len, 1, stdout);
//printf("\n");
if (c->is_websocket)
{
c->content[c->content_len] = '\0';
if(c->content_len)
return callback_mpd(c);
else
return MG_CLIENT_CONTINUE;
}
else
return callback_http(c);
}
int main(int argc, char **argv)
{
int n, gid = -1, uid = -1;
int option_index = 0;
unsigned int oldus = 0;
struct libwebsocket_context *context;
struct lws_context_creation_info info;
const char *cert_filepath = NULL;
const char *private_key_filepath = NULL;
const char *iface = NULL;
int n, option_index = 0;
struct mg_server *server = mg_create_server(NULL);
unsigned int current_timer = 0, last_timer = 0;
atexit(bye);
memset(&info, 0, sizeof info);
info.port = 8080;
strcpy(mpd_host, "127.0.0.1");
mpd_port = 6600;
lws_set_log_level(LLL_ERR | LLL_WARN, NULL);
mg_set_option(server, "listening_port", "8080");
mpd.port = 6600;
strcpy(mpd.host, "127.0.0.1");
static struct option long_options[] = {
{"host", required_argument, 0, 'h'},
{"port", required_argument, 0, 'p'},
{"interface", required_argument, 0, 'i'},
{"webport", required_argument, 0, 'w'},
{"resourcepath", required_argument, 0, 'r'},
{"ssl_cert", required_argument, 0, 'c'},
{"ssl_key", required_argument, 0, 'k'},
{"gid", required_argument, 0, 'g'},
{"uid", required_argument, 0, 'u'},
{"verbose", optional_argument, 0, 'v'},
{"user", required_argument, 0, 'u'},
{"version", no_argument, 0, 'V'},
{"help", no_argument, 0, 0 },
{0, 0, 0, 0 }
};
while((n = getopt_long(argc, argv, "h:p:i:w:r:c:k:g:u:v::V",
while((n = getopt_long(argc, argv, "h:p:w:u::V",
long_options, &option_index)) != -1) {
switch (n) {
case 'h':
strncpy(mpd_host, optarg, sizeof(mpd_host));
strncpy(mpd.host, optarg, sizeof(mpd.host));
break;
case 'p':
mpd_port = atoi(optarg);
case 'i':
iface = optarg;
break;
mpd.port = atoi(optarg);
case 'w':
info.port = atoi(optarg);
break;
case 'r':
resource_path = optarg;
break;
case 'c':
cert_filepath = optarg;
break;
case 'k':
private_key_filepath = optarg;
break;
case 'g':
gid = atoi(optarg);
mg_set_option(server, "listening_port", optarg);
break;
case 'u':
uid = atoi(optarg);
break;
case 'v':
if(optarg)
lws_set_log_level(strtol(optarg, NULL, 10), NULL);
else
lws_set_log_level(LLL_ERR | LLL_WARN |
LLL_NOTICE | LLL_INFO, NULL);
mg_set_option(server, "run_as_user", optarg);
break;
case 'V':
fprintf(stdout, "ympd %d.%d.%d\n"
"Copyright (C) 2014 Andrew Karpow <andy@ympd.org>\n"
"Resource Path: "LOCAL_RESOURCE_PATH"\n"
"Copyright (C) 2014 Andrew Karpow <andy@ndyk.de>\n"
"built " __DATE__ " "__TIME__ " ("__VERSION__")\n",
YMPD_VERSION_MAJOR, YMPD_VERSION_MINOR, YMPD_VERSION_PATCH);
return EXIT_SUCCESS;
@@ -149,14 +112,8 @@ int main(int argc, char **argv)
fprintf(stderr, "Usage: %s [OPTION]...\n\n"
"\t-h, --host <host>\t\tconnect to mpd at host [localhost]\n"
"\t-p, --port <port>\t\tconnect to mpd at port [6600]\n"
"\t-i, --interface <interface>\tlisten interface for webserver [all]\n"
"\t-w, --webport <port>\t\tlisten port for webserver [8080]\n"
"\t-r, --resourcepath <path>\tresourcepath for webserver [" LOCAL_RESOURCE_PATH "]\n"
"\t-c, --ssl_cert <filepath>\tssl certificate ssl_private_key_filepath\n"
"\t-k, --ssl_key <filepath>\tssl private key filepath\n"
"\t-u, --uid <id>\t\t\tuser-id after socket bind\n"
"\t-g, --gid <id>\t\t\tgroup-id after socket bind\n"
"\t-v, --verbose[<level>]\t\tverbosity level\n"
"\t-w, --webport [ip:]<port>\t\tlisten interface/port for webserver [8080]\n"
"\t-u, --user <username>\t\t\tdrop priviliges to user after socket bind\n"
"\t-V, --version\t\t\tget version\n"
"\t--help\t\t\t\tthis help\n"
, argv[0]);
@@ -164,52 +121,19 @@ int main(int argc, char **argv)
}
}
if(cert_filepath != NULL && private_key_filepath == NULL) {
lwsl_err("private key filepath needed\n");
return EXIT_FAILURE;
}
if(private_key_filepath != NULL && cert_filepath == NULL) {
lwsl_err("public cert filepath needed\n");
return EXIT_FAILURE;
}
info.ssl_cert_filepath = cert_filepath;
info.ssl_private_key_filepath = private_key_filepath;
info.iface = iface;
info.protocols = protocols;
info.extensions = libwebsocket_get_internal_extensions();
info.gid = gid;
info.uid = uid;
info.options = 0;
context = libwebsocket_create_context(&info);
if (context == NULL) {
lwsl_err("libwebsocket init failed\n");
return EXIT_FAILURE;
}
n = 0;
while (n >= 0 && !force_exit) {
struct timeval tv;
gettimeofday(&tv, NULL);
/*
* This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
* live websocket connection using the DUMB_INCREMENT protocol,
* as soon as it can take more packets (usually immediately)
*/
if (((unsigned int)tv.tv_usec - oldus) > 1000 * 500) {
mpd_loop();
libwebsocket_callback_on_writable_all_protocol(&protocols[1]);
oldus = tv.tv_usec;
mg_set_http_close_handler(server, mpd_close_handler);
mg_set_request_handler(server, server_callback);
while (!force_exit) {
current_timer = mg_poll_server(server, 200);
if(current_timer - last_timer)
{
last_timer = current_timer;
mpd_poll(server);
}
n = libwebsocket_service(context, 50);
}
libwebsocket_context_destroy(context);
return 0;
mpd_disconnect();
mg_destroy_server(&server);
return EXIT_SUCCESS;
}