diff --git a/src/httpd.c b/src/httpd.c
index ed9372f1..f6d272b5 100644
--- a/src/httpd.c
+++ b/src/httpd.c
@@ -49,15 +49,19 @@
/*
* HTTP client quirks by User-Agent, from mt-daapd
*
- * - Roku:
- * + Does not encode space as + in query string
* - iTunes:
- * + Does not encode space as + in query string
* + Connection: Keep-Alive on HTTP error 401
* - Hifidelio:
* + Connection: Keep-Alive for streaming (Connection: close not honoured)
*
* These quirks are not implemented. Implement as needed.
+ *
+ * Implemented quirks:
+ *
+ * - Roku:
+ * + Does not encode space as + in query string
+ * - iTunes:
+ * + Does not encode space as + in query string
*/
@@ -715,6 +719,84 @@ exit_cb(int fd, short event, void *arg)
httpd_exit = 1;
}
+char *
+httpd_fixup_uri(struct evhttp_request *req)
+{
+ const char *ua;
+ const char *uri;
+ const char *u;
+ const char *q;
+ char *fixed;
+ char *f;
+ int len;
+
+ uri = evhttp_request_uri(req);
+ if (!uri)
+ return NULL;
+
+ /* No query string, nothing to do */
+ q = strchr(uri, '?');
+ if (!q)
+ return strdup(uri);
+
+ ua = evhttp_find_header(req->input_headers, "User-Agent");
+ if (!ua)
+ return strdup(uri);
+
+ if ((strncmp(ua, "iTunes", strlen("iTunes")) != 0)
+ && (strncmp(ua, "Roku", strlen("Roku")) != 0))
+ return strdup(uri);
+
+ /* Reencode + as %2B and space as + in the query,
+ which iTunes and Roku devices don't do */
+ len = strlen(uri);
+
+ u = q;
+ while (*u)
+ {
+ if (*u == '+')
+ len += 2;
+
+ u++;
+ }
+
+ fixed = (char *)malloc(len + 1);
+ if (!fixed)
+ return NULL;
+
+ strncpy(fixed, uri, q - uri);
+
+ f = fixed + (q - uri);
+ while (*q)
+ {
+ switch (*q)
+ {
+ case '+':
+ *f = '%';
+ f++;
+ *f = '2';
+ f++;
+ *f = 'B';
+ break;
+
+ case ' ':
+ *f = '+';
+ break;
+
+ default:
+ *f = *q;
+ break;
+ }
+
+ q++;
+ f++;
+ }
+
+ *f = '\0';
+
+ return fixed;
+}
+
static char *http_reply_401 = "
401 UnauthorizedAuthorization required";
int
diff --git a/src/httpd.h b/src/httpd.h
index 34dc1e65..f7826617 100644
--- a/src/httpd.h
+++ b/src/httpd.h
@@ -9,6 +9,9 @@
void
httpd_stream_file(struct evhttp_request *req, int id);
+char *
+httpd_fixup_uri(struct evhttp_request *req);
+
int
httpd_basic_auth(struct evhttp_request *req, char *user, char *passwd, char *realm);
diff --git a/src/httpd_daap.c b/src/httpd_daap.c
index 0ce8ff95..1d225d81 100644
--- a/src/httpd_daap.c
+++ b/src/httpd_daap.c
@@ -1782,7 +1782,6 @@ static struct uri_map daap_handlers[] =
void
daap_request(struct evhttp_request *req)
{
- const char *req_uri;
char *full_uri;
char *uri;
char *ptr;
@@ -1796,23 +1795,27 @@ daap_request(struct evhttp_request *req)
int ret;
int i;
- req_uri = evhttp_request_uri(req);
-
- full_uri = strdup(req_uri);
+ full_uri = httpd_fixup_uri(req);
+ if (!full_uri)
+ {
+ evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
+ return;
+ }
ptr = strchr(full_uri, '?');
if (ptr)
*ptr = '\0';
uri = strdup(full_uri);
+ if (!uri)
+ {
+ evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
+ return;
+ }
if (ptr)
*ptr = '?';
- ptr = full_uri;
- full_uri = evhttp_decode_uri(full_uri);
- free(ptr);
-
ptr = uri;
uri = evhttp_decode_uri(uri);
free(ptr);
diff --git a/src/httpd_rsp.c b/src/httpd_rsp.c
index 302c939f..45c38c34 100644
--- a/src/httpd_rsp.c
+++ b/src/httpd_rsp.c
@@ -897,7 +897,6 @@ static struct uri_map rsp_handlers[] =
void
rsp_request(struct evhttp_request *req)
{
- const char *req_uri;
char *full_uri;
char *uri;
char *ptr;
@@ -910,23 +909,27 @@ rsp_request(struct evhttp_request *req)
int i;
int ret;
- req_uri = evhttp_request_uri(req);
-
- full_uri = strdup(req_uri);
+ full_uri = httpd_fixup_uri(req);
+ if (!full_uri)
+ {
+ rsp_send_error(req, "Server error");
+ return;
+ }
ptr = strchr(full_uri, '?');
if (ptr)
*ptr = '\0';
uri = strdup(full_uri);
+ if (!uri)
+ {
+ rsp_send_error(req, "Server error");
+ return;
+ }
if (ptr)
*ptr = '?';
- ptr = full_uri;
- full_uri = evhttp_decode_uri(full_uri);
- free(ptr);
-
ptr = uri;
uri = evhttp_decode_uri(uri);
free(ptr);