diff --git a/src/client.c b/src/client.c
index 7672b959..4dedbc80 100644
--- a/src/client.c
+++ b/src/client.c
@@ -45,6 +45,16 @@ void client_destroy(client_t *client)
free(client);
}
+void client_send_400(client_t *client, char *message) {
+ int bytes;
+ bytes = sock_write(client->con->sock, "HTTP/1.0 404 File Not Found\r\n"
+ "Content-Type: text/html\r\n\r\n"
+ "%s\r\n", message);
+ if(bytes > 0) client->con->sent_bytes = bytes;
+ client->respcode = 404;
+ client_destroy(client);
+}
+
void client_send_404(client_t *client, char *message) {
int bytes;
diff --git a/src/client.h b/src/client.h
index 0fd4c397..4ae65bf3 100644
--- a/src/client.h
+++ b/src/client.h
@@ -31,5 +31,6 @@ client_t *client_create(connection_t *con, http_parser_t *parser);
void client_destroy(client_t *client);
void client_send_404(client_t *client, char *message);
void client_send_401(client_t *client);
+void client_send_400(client_t *client, char *message);
#endif /* __CLIENT_H__ */
diff --git a/src/connection.c b/src/connection.c
index 217c2dc3..a4d79cae 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -36,6 +36,7 @@
#include "fserve.h"
#include "source.h"
#include "format.h"
+#include "format_mp3.h"
#define CATMODULE "connection"
@@ -418,6 +419,65 @@ static int _check_source_pass(http_parser_t *parser)
return ret;
}
+static void handle_metadata_request(client_t *client)
+{
+ source_t *source;
+ char *action;
+ char *mount;
+ char *value;
+ mp3_state *state;
+ int bytes;
+
+ if(!_check_source_pass(client->parser)) {
+ INFO0("Metadata request with wrong or missing password");
+ client_send_401(client);
+ return;
+ }
+
+ action = httpp_get_query_param(client->parser, "mode");
+ mount = httpp_get_query_param(client->parser, "mount");
+ value = httpp_get_query_param(client->parser, "song");
+
+ if(value == NULL || action == NULL || mount == NULL) {
+ client_send_400(client, "Missing parameter");
+ return;
+ }
+
+ avl_tree_rlock(global.source_tree);
+ source = source_find_mount(mount);
+ avl_tree_unlock(global.source_tree);
+
+ if(source == NULL) {
+ client_send_400(client, "No such mountpoint");
+ return;
+ }
+
+ if(source->format->type != FORMAT_TYPE_MP3) {
+ client_send_400(client, "Not mp3, cannot update metadata");
+ return;
+ }
+
+ if(strcmp(action, "updinfo") != 0) {
+ client_send_400(client, "No such action");
+ return;
+ }
+
+ state = source->format->_state;
+ thread_mutex_lock(&(state->lock));
+ free(state->metadata);
+ state->metadata = strdup(value);
+ state->metadata_age++;
+ thread_mutex_unlock(&(state->lock));
+
+ DEBUG2("Metadata on mountpoint %s changed to \"%s\"", mount, value);
+ client->respcode = 200;
+ bytes = sock_write(client->con->sock,
+ "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
+ "Update successful");
+ if(bytes > 0) client->con->sent_bytes = bytes;
+ client_destroy(client);
+}
+
static void _handle_source_request(connection_t *con,
http_parser_t *parser, char *uri)
{
@@ -500,7 +560,7 @@ static void _handle_get_request(connection_t *con,
** aren't subject to the limits.
*/
/* TODO: add GUID-xxxxxx */
- if (strcmp(uri, "/stats.xml") == 0) {
+ if (strcmp(uri, "/admin/stats.xml") == 0) {
if (!_check_source_pass(parser)) {
INFO0("Request for stats.xml with incorrect or no password");
client_send_401(client);
@@ -512,6 +572,12 @@ static void _handle_get_request(connection_t *con,
return;
}
+ if(strcmp(uri, "/admin/metadata") == 0) {
+ DEBUG0("Got metadata update request");
+ handle_metadata_request(client);
+ return;
+ }
+
/* Here we are parsing the URI request to see
** if the extension is .xsl, if so, then process
** this request as an XSLT request
diff --git a/src/httpp/httpp.c b/src/httpp/httpp.c
index f4717902..db5d55f6 100644
--- a/src/httpp/httpp.c
+++ b/src/httpp/httpp.c
@@ -40,6 +40,7 @@ void httpp_initialize(http_parser_t *parser, http_varlist_t *defaults)
parser->req_type = httpp_req_none;
parser->uri = NULL;
parser->vars = avl_tree_new(_compare_vars, NULL);
+ parser->queryvars = avl_tree_new(_compare_vars, NULL);
/* now insert the default variables */
list = defaults;
@@ -178,6 +179,100 @@ int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long l
return 1;
}
+static int hex(char c)
+{
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ else if(c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else if(c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else
+ return -1;
+}
+
+static char *url_escape(char *src)
+{
+ int len = strlen(src);
+ unsigned char *decoded;
+ int i;
+ char *dst;
+ int done = 0;
+
+ decoded = calloc(1, len + 1);
+
+ dst = decoded;
+
+ for(i=0; i < len; i++) {
+ switch(src[i]) {
+ case '%':
+ if(i+2 >= len) {
+ free(decoded);
+ return NULL;
+ }
+ if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
+ free(decoded);
+ return NULL;
+ }
+
+ *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]);
+ i+= 2;
+ break;
+ case '#':
+ done = 1;
+ break;
+ case 0:
+ free(decoded);
+ return NULL;
+ break;
+ default:
+ *dst++ = src[i];
+ break;
+ }
+ if(done)
+ break;
+ }
+
+ *dst = 0; /* null terminator */
+
+ return decoded;
+}
+
+/** TODO: This is almost certainly buggy in some cases */
+static void parse_query(http_parser_t *parser, char *query)
+{
+ int len;
+ int i=0;
+ char *key = query;
+ char *val=NULL;
+
+ if(!query || !*query)
+ return;
+
+ len = strlen(query);
+
+ while(ireq_type = httpp_req_unknown;
}
- if (uri != NULL && strlen(uri) > 0)
+ if (uri != NULL && strlen(uri) > 0)
+ {
+ char *query;
+ if((query = strchr(uri, '?')) != NULL) {
+ *query = 0;
+ query++;
+ parse_query(parser, query);
+ }
+
parser->uri = strdup(uri);
+ }
else
parser->uri = NULL;
@@ -335,7 +439,7 @@ char *httpp_getvar(http_parser_t *parser, char *name)
http_var_t *found;
var.name = name;
- var.value = NULL;
+ var.value = NULL;
if (avl_get_by_key(parser->vars, (void *)&var, (void **)&found) == 0)
return found->value;
@@ -343,6 +447,41 @@ char *httpp_getvar(http_parser_t *parser, char *name)
return NULL;
}
+void httpp_set_query_param(http_parser_t *parser, char *name, char *value)
+{
+ http_var_t *var;
+
+ if (name == NULL || value == NULL)
+ return;
+
+ var = (http_var_t *)malloc(sizeof(http_var_t));
+ if (var == NULL) return;
+
+ var->name = strdup(name);
+ var->value = url_escape(value);
+
+ if (httpp_get_query_param(parser, name) == NULL) {
+ avl_insert(parser->queryvars, (void *)var);
+ } else {
+ avl_delete(parser->queryvars, (void *)var, _free_vars);
+ avl_insert(parser->queryvars, (void *)var);
+ }
+}
+
+char *httpp_get_query_param(http_parser_t *parser, char *name)
+{
+ http_var_t var;
+ http_var_t *found;
+
+ var.name = name;
+ var.value = NULL;
+
+ if (avl_get_by_key(parser->queryvars, (void *)&var, (void **)&found) == 0)
+ return found->value;
+ else
+ return NULL;
+}
+
void httpp_clear(http_parser_t *parser)
{
parser->req_type = httpp_req_none;
@@ -350,6 +489,7 @@ void httpp_clear(http_parser_t *parser)
free(parser->uri);
parser->uri = NULL;
avl_tree_free(parser->vars, _free_vars);
+ avl_tree_free(parser->queryvars, _free_vars);
parser->vars = NULL;
}
diff --git a/src/httpp/httpp.h b/src/httpp/httpp.h
index cf011b5d..e34f26eb 100644
--- a/src/httpp/httpp.h
+++ b/src/httpp/httpp.h
@@ -33,6 +33,7 @@ typedef struct http_parser_tag {
httpp_request_type_e req_type;
char *uri;
avl_tree *vars;
+ avl_tree *queryvars;
} http_parser_t;
http_parser_t *httpp_create_parser(void);
@@ -41,6 +42,8 @@ int httpp_parse(http_parser_t *parser, char *http_data, unsigned long len);
int httpp_parse_response(http_parser_t *parser, char *http_data, unsigned long len, char *uri);
void httpp_setvar(http_parser_t *parser, char *name, char *value);
char *httpp_getvar(http_parser_t *parser, char *name);
+void httpp_set_query_param(http_parser_t *parser, char *name, char *value);
+char *httpp_get_query_param(http_parser_t *parser, char *name);
void httpp_destroy(http_parser_t *parser);
void httpp_clear(http_parser_t *parser);
diff --git a/src/util.c b/src/util.c
index 7e165096..9e4a504a 100644
--- a/src/util.c
+++ b/src/util.c
@@ -208,50 +208,43 @@ char *util_get_path_from_normalised_uri(char *uri) {
return fullpath;
}
-/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
- * path contains 'disallowed' sequences like foo/../ (which could be used to
- * escape from the webroot) or if it cannot be URI-decoded.
- * Caller should free the path.
- */
-char *util_normalise_uri(char *uri) {
- int urilen = strlen(uri);
- unsigned char *path;
- char *dst;
+char *util_url_escape(char *src)
+{
+ int len = strlen(src);
+ unsigned char *decoded;
int i;
+ char *dst;
int done = 0;
- if(uri[0] != '/')
- return NULL;
+ decoded = calloc(1, len + 1);
- path = calloc(1, urilen + 1);
+ dst = decoded;
- dst = path;
-
- for(i=0; i < urilen; i++) {
- switch(uri[i]) {
+ for(i=0; i < len; i++) {
+ switch(src[i]) {
case '%':
- if(i+2 >= urilen) {
- free(path);
+ if(i+2 >= len) {
+ free(decoded);
return NULL;
}
- if(hex(uri[i+1]) == -1 || hex(uri[i+2]) == -1 ) {
- free(path);
+ if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
+ free(decoded);
return NULL;
}
- *dst++ = hex(uri[i+1]) * 16 + hex(uri[i+2]);
+ *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]);
i+= 2;
break;
case '#':
done = 1;
break;
case 0:
- ERROR0("Fatal internal logic error in util_get_path_from_uri()");
- free(path);
+ ERROR0("Fatal internal logic error in util_url_escape()");
+ free(decoded);
return NULL;
break;
default:
- *dst++ = uri[i];
+ *dst++ = src[i];
break;
}
if(done)
@@ -260,9 +253,30 @@ char *util_normalise_uri(char *uri) {
*dst = 0; /* null terminator */
+ return decoded;
+}
+
+/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
+ * path contains 'disallowed' sequences like foo/../ (which could be used to
+ * escape from the webroot) or if it cannot be URI-decoded.
+ * Caller should free the path.
+ */
+char *util_normalise_uri(char *uri) {
+ char *path;
+
+ if(uri[0] != '/')
+ return NULL;
+
+ path = util_url_escape(uri);
+
+ if(path == NULL) {
+ WARN1("Error decoding URI: %s\n", uri);
+ return NULL;
+ }
+
/* We now have a full URI-decoded path. Check it for allowability */
if(verify_path(path))
- return (char *)path;
+ return path;
else {
WARN1("Rejecting invalid path \"%s\"", path);
free(path);
diff --git a/src/util.h b/src/util.h
index 53b4236e..b350882f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -14,4 +14,6 @@ char *util_normalise_uri(char *uri);
char *util_base64_encode(char *data);
char *util_base64_decode(unsigned char *input);
+char *util_url_escape(char *src);
+
#endif /* __UTIL_H__ */