Implement URI encoding quirk for iTunes and Roku

iTunes and Roku devices do not encode + as %2B in the query string and
do not encode space as + either in the query string (though at least the
Roku encode space as %20 everywhere). This needs to be worked around or
browse queries fail to parse because + was decoded as space when the query
really needs a + character.
This commit is contained in:
Julien BLACHE 2009-05-02 17:20:33 +02:00
parent 09ef188d90
commit c9868175fd
4 changed files with 110 additions and 19 deletions

View File

@ -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 = "<html><head><title>401 Unauthorized</title></head><body>Authorization required</body></html>";
int

View File

@ -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);

View File

@ -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);

View File

@ -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);