2014-01-17 18:41:41 +00:00
|
|
|
/* ympd
|
|
|
|
(c) 2013-2014 Andrew Karpow <andy@ympd.org>
|
|
|
|
This project's homepage is: http://www.ympd.org
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions
|
|
|
|
are met:
|
|
|
|
|
|
|
|
- Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in the
|
|
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
|
|
|
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2013-11-04 17:18:38 +00:00
|
|
|
#include <libwebsockets.h>
|
|
|
|
#include <stdio.h>
|
2013-11-05 13:59:12 +00:00
|
|
|
#include <string.h>
|
2013-12-03 20:48:49 +00:00
|
|
|
#include <stdlib.h>
|
2014-01-16 17:32:20 +00:00
|
|
|
#include <errno.h>
|
2013-12-03 20:48:49 +00:00
|
|
|
#include <ctype.h>
|
2014-01-08 01:23:02 +00:00
|
|
|
#include <mpd/client.h>
|
2013-12-03 20:48:49 +00:00
|
|
|
|
2013-11-04 17:18:38 +00:00
|
|
|
#include "http_server.h"
|
2013-12-03 20:48:49 +00:00
|
|
|
#include "mpd_client.h"
|
2014-01-08 01:23:02 +00:00
|
|
|
#include "config.h"
|
2013-11-04 17:18:38 +00:00
|
|
|
|
|
|
|
char *resource_path = LOCAL_RESOURCE_PATH;
|
2014-01-16 17:32:20 +00:00
|
|
|
extern enum mpd_conn_states mpd_conn_state;
|
2013-11-04 17:18:38 +00:00
|
|
|
|
|
|
|
struct serveable {
|
2013-11-09 01:07:03 +00:00
|
|
|
const char *urlpath;
|
|
|
|
const char *mimetype;
|
2013-11-04 17:18:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct serveable whitelist[] = {
|
2013-11-09 01:07:03 +00:00
|
|
|
{ "/css/bootstrap.css", "text/css" },
|
|
|
|
{ "/css/mpd.css", "text/css" },
|
2013-11-04 23:17:28 +00:00
|
|
|
|
2013-11-09 01:07:03 +00:00
|
|
|
{ "/js/bootstrap.min.js", "text/javascript" },
|
|
|
|
{ "/js/mpd.js", "text/javascript" },
|
|
|
|
{ "/js/jquery-1.10.2.min.js", "text/javascript" },
|
2014-01-17 17:34:22 +00:00
|
|
|
{ "/js/jquery.cookie.js", "text/javascript" },
|
2013-11-09 01:07:03 +00:00
|
|
|
{ "/js/bootstrap-slider.js", "text/javascript" },
|
2014-01-16 17:32:20 +00:00
|
|
|
{ "/js/bootstrap-notify.js", "text/javascript" },
|
2013-11-09 01:07:03 +00:00
|
|
|
{ "/js/sammy.js", "text/javascript" },
|
2013-11-04 17:18:38 +00:00
|
|
|
|
2013-11-09 01:07:03 +00:00
|
|
|
{ "/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"},
|
|
|
|
|
2014-01-08 14:23:14 +00:00
|
|
|
{ "/assets/favicon.ico", "image/vnd.microsoft.icon" },
|
2013-12-03 20:48:49 +00:00
|
|
|
|
2013-11-09 01:07:03 +00:00
|
|
|
/* last one is the default served if no match */
|
|
|
|
{ "/index.html", "text/html" },
|
2013-11-04 17:18:38 +00:00
|
|
|
};
|
|
|
|
|
2014-01-16 17:32:20 +00:00
|
|
|
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";
|
|
|
|
|
2013-12-03 20:48:49 +00:00
|
|
|
/* Converts a hex character to its integer value */
|
|
|
|
char from_hex(char ch) {
|
2014-01-08 14:23:14 +00:00
|
|
|
return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
|
2013-12-03 20:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns a url-decoded version of str */
|
|
|
|
/* IMPORTANT: be sure to free() the returned string after use */
|
|
|
|
char *url_decode(char *str) {
|
2014-01-08 14:23:14 +00:00
|
|
|
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++;
|
2013-12-03 20:48:49 +00:00
|
|
|
}
|
2014-01-08 14:23:14 +00:00
|
|
|
*pbuf = '\0';
|
|
|
|
return buf;
|
2013-12-03 20:48:49 +00:00
|
|
|
}
|
|
|
|
|
2013-11-04 17:18:38 +00:00
|
|
|
int callback_http(struct libwebsocket_context *context,
|
2013-11-09 01:07:03 +00:00
|
|
|
struct libwebsocket *wsi,
|
|
|
|
enum libwebsocket_callback_reasons reason, void *user,
|
|
|
|
void *in, size_t len)
|
2013-11-04 17:18:38 +00:00
|
|
|
{
|
2014-01-08 14:23:14 +00:00
|
|
|
char *response_buffer, *p;
|
2014-01-16 17:32:20 +00:00
|
|
|
char buf[64];
|
|
|
|
size_t n, response_size = 0;
|
2013-11-04 17:18:38 +00:00
|
|
|
|
2013-11-09 01:07:03 +00:00
|
|
|
switch (reason) {
|
|
|
|
case LWS_CALLBACK_HTTP:
|
2013-12-03 20:48:49 +00:00
|
|
|
if(in && strncmp((const char *)in, "/api/", 5) == 0)
|
2013-11-09 01:07:03 +00:00
|
|
|
{
|
2014-01-16 17:32:20 +00:00
|
|
|
|
|
|
|
p = (char *)malloc(MAX_SIZE + 100);
|
|
|
|
memcpy(p, http_header, sizeof(http_header) - 1);
|
|
|
|
response_buffer = p + sizeof(http_header) - 1;
|
2013-12-03 20:48:49 +00:00
|
|
|
|
|
|
|
/* put content length and payload to buffer */
|
2014-01-16 17:32:20 +00:00
|
|
|
if(mpd_conn_state != MPD_CONNECTED) {}
|
|
|
|
else if(strncmp((const char *)in, "/api/get_browse", 15) == 0)
|
2013-12-03 20:48:49 +00:00
|
|
|
{
|
|
|
|
char *url;
|
2014-01-16 17:32:20 +00:00
|
|
|
if(sscanf(in, "/api/get_browse/%m[^\t\n]", &url) == 1)
|
2013-12-03 20:48:49 +00:00
|
|
|
{
|
|
|
|
char *url_decoded = url_decode(url);
|
2014-01-16 17:32:20 +00:00
|
|
|
response_size = mpd_put_browse(response_buffer, url_decoded);
|
2013-12-03 20:48:49 +00:00
|
|
|
free(url_decoded);
|
|
|
|
free(url);
|
|
|
|
}
|
|
|
|
else
|
2014-01-16 17:32:20 +00:00
|
|
|
response_size = mpd_put_browse(response_buffer, "/");
|
2013-12-03 20:48:49 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
else if(strncmp((const char *)in, "/api/get_playlist", 17) == 0)
|
2014-01-16 17:32:20 +00:00
|
|
|
response_size = mpd_put_playlist(response_buffer);
|
|
|
|
else if(strncmp((const char *)in, "/api/get_version", 16) == 0)
|
2014-01-08 01:23:02 +00:00
|
|
|
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);
|
2014-01-16 17:32:20 +00:00
|
|
|
|
|
|
|
/* Copy size to content-length field */
|
2014-01-19 01:08:56 +00:00
|
|
|
sprintf(buf, "%6zu", response_size);
|
2014-01-16 17:32:20 +00:00
|
|
|
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);
|
2013-12-03 20:48:49 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
2014-01-08 14:23:14 +00:00
|
|
|
|
2013-12-03 20:48:49 +00:00
|
|
|
sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath);
|
|
|
|
|
2014-01-08 01:23:02 +00:00
|
|
|
if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype, NULL))
|
2013-12-03 20:48:49 +00:00
|
|
|
return -1; /* through completion or error, close the socket */
|
|
|
|
}
|
2013-11-09 01:07:03 +00:00
|
|
|
break;
|
2013-11-04 17:18:38 +00:00
|
|
|
|
2013-11-09 01:07:03 +00:00
|
|
|
case LWS_CALLBACK_HTTP_FILE_COMPLETION:
|
|
|
|
/* kill the connection after we sent one file */
|
|
|
|
return -1;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-11-04 17:18:38 +00:00
|
|
|
|
2013-11-09 01:07:03 +00:00
|
|
|
return 0;
|
2013-11-04 17:18:38 +00:00
|
|
|
}
|