2009-04-28 11:48:57 -04:00
|
|
|
/*
|
2011-03-31 12:05:41 -04:00
|
|
|
* Copyright (C) 2009-2011 Julien BLACHE <jb@jblache.org>
|
2010-08-14 07:57:49 -04:00
|
|
|
* Copyright (C) 2010 Kai Elwert <elwertk@googlemail.com>
|
2009-04-28 11:48:57 -04:00
|
|
|
*
|
|
|
|
* Adapted from mt-daapd:
|
|
|
|
* Copyright (C) 2003-2007 Ron Pedde <ron@pedde.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2009-04-28 16:51:52 -04:00
|
|
|
#include <errno.h>
|
2009-04-28 11:48:57 -04:00
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <regex.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdint.h>
|
2010-01-04 12:00:05 -05:00
|
|
|
#include <inttypes.h>
|
2014-08-23 19:23:23 -04:00
|
|
|
#include <time.h>
|
2010-08-14 07:57:49 -04:00
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
#include <uninorm.h>
|
2014-08-23 19:23:23 -04:00
|
|
|
#include <unistd.h>
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-05-08 11:46:32 -04:00
|
|
|
#include "logger.h"
|
2009-06-07 12:58:02 -04:00
|
|
|
#include "db.h"
|
2009-04-28 11:48:57 -04:00
|
|
|
#include "conffile.h"
|
2009-04-30 08:25:52 -04:00
|
|
|
#include "misc.h"
|
2009-04-28 11:48:57 -04:00
|
|
|
#include "httpd.h"
|
2009-05-01 09:33:48 -04:00
|
|
|
#include "transcode.h"
|
2010-03-07 05:11:09 -05:00
|
|
|
#include "artwork.h"
|
2009-04-28 11:48:57 -04:00
|
|
|
#include "httpd_daap.h"
|
2009-06-02 09:51:38 -04:00
|
|
|
#include "daap_query.h"
|
2011-03-08 13:22:16 -05:00
|
|
|
#include "dmap_common.h"
|
2014-11-10 16:53:08 -05:00
|
|
|
#include "cache.h"
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
#include <event2/event.h>
|
|
|
|
#include <event2/buffer.h>
|
|
|
|
#include <event2/http_struct.h>
|
2015-10-19 15:15:29 -04:00
|
|
|
#include <event2/keyvalq_struct.h>
|
2014-05-29 17:22:00 -04:00
|
|
|
|
2010-03-06 04:30:59 -05:00
|
|
|
/* httpd event base, from httpd.c */
|
|
|
|
extern struct event_base *evbase_httpd;
|
|
|
|
|
2015-10-03 04:00:42 -04:00
|
|
|
/* Max number of sessions and session timeout
|
|
|
|
* Many clients (including iTunes) don't seem to respect the timeout capability
|
|
|
|
* that we announce, and just keep using the same session. Therefore we take a
|
|
|
|
* lenient approach to actually timing out: We wait an entire week, and to
|
|
|
|
* avoid running a timer for that long, we only check for expiration when adding
|
|
|
|
* new sessions - see daap_session_cleanup().
|
|
|
|
*/
|
|
|
|
#define DAAP_SESSION_MAX 200
|
|
|
|
#define DAAP_SESSION_TIMEOUT 604800 // One week in seconds
|
2012-01-02 02:15:18 -05:00
|
|
|
/* We announce this timeout to the client when returning server capabilities */
|
2015-10-03 04:00:42 -04:00
|
|
|
#define DAAP_SESSION_TIMEOUT_CAPABILITY 1800 // 30 minutes
|
2011-09-10 12:37:16 -04:00
|
|
|
/* Update requests refresh interval in seconds */
|
2012-01-02 02:15:18 -05:00
|
|
|
#define DAAP_UPDATE_REFRESH 0
|
2010-03-06 04:30:59 -05:00
|
|
|
|
2015-03-30 15:10:59 -04:00
|
|
|
/* Database number for the Radio item */
|
2015-03-16 18:33:42 -04:00
|
|
|
#define DAAP_DB_RADIO 2
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
struct uri_map {
|
|
|
|
regex_t preg;
|
|
|
|
char *regexp;
|
2014-08-22 18:02:01 -04:00
|
|
|
int (*handler)(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua);
|
2009-04-28 11:48:57 -04:00
|
|
|
};
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session {
|
|
|
|
int id;
|
2014-03-11 18:20:29 -04:00
|
|
|
char *user_agent;
|
2015-10-03 04:00:42 -04:00
|
|
|
time_t mtime;
|
2015-02-24 16:58:47 -05:00
|
|
|
|
|
|
|
struct daap_session *next;
|
2010-01-20 12:24:52 -05:00
|
|
|
};
|
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
struct daap_update_request {
|
|
|
|
struct evhttp_request *req;
|
|
|
|
|
2011-09-10 12:37:16 -04:00
|
|
|
/* Refresh tiemout */
|
2015-03-06 15:22:05 -05:00
|
|
|
struct event *timeout;
|
2011-09-10 12:37:16 -04:00
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
struct daap_update_request *next;
|
|
|
|
};
|
|
|
|
|
2010-08-14 07:57:49 -04:00
|
|
|
struct sort_ctx {
|
|
|
|
struct evbuffer *headerlist;
|
|
|
|
int16_t mshc;
|
|
|
|
uint32_t mshi;
|
|
|
|
uint32_t mshn;
|
|
|
|
uint32_t misc_mshn;
|
|
|
|
};
|
|
|
|
|
2010-02-08 13:02:58 -05:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
/* Default meta tags if not provided in the query */
|
|
|
|
static char *default_meta_plsongs = "dmap.itemkind,dmap.itemid,dmap.itemname,dmap.containeritemid,dmap.parentcontainerid";
|
|
|
|
static char *default_meta_pl = "dmap.itemid,dmap.itemname,dmap.persistentid,com.apple.itunes.smart-playlist";
|
2010-01-10 07:04:57 -05:00
|
|
|
static char *default_meta_group = "dmap.itemname,dmap.persistentid,daap.songalbumartist";
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
/* DAAP session tracking */
|
2015-02-24 16:58:47 -05:00
|
|
|
static struct daap_session *daap_sessions;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
/* Update requests */
|
2011-09-10 12:37:16 -04:00
|
|
|
static int current_rev;
|
2010-01-28 13:20:00 -05:00
|
|
|
static struct daap_update_request *update_requests;
|
2015-03-06 15:22:05 -05:00
|
|
|
static struct timeval daap_update_refresh_tv = { DAAP_UPDATE_REFRESH, 0 };
|
2010-01-28 13:20:00 -05:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:36:12 -05:00
|
|
|
/* Session handling */
|
2015-02-24 16:58:47 -05:00
|
|
|
static void
|
|
|
|
daap_session_free(struct daap_session *s)
|
2010-01-20 12:24:52 -05:00
|
|
|
{
|
2015-02-24 16:58:47 -05:00
|
|
|
if (s->user_agent)
|
|
|
|
free(s->user_agent);
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
free(s);
|
2010-01-20 12:24:52 -05:00
|
|
|
}
|
|
|
|
|
2010-03-06 04:29:53 -05:00
|
|
|
static void
|
2015-02-24 16:58:47 -05:00
|
|
|
daap_session_remove(struct daap_session *s)
|
2010-03-06 04:29:53 -05:00
|
|
|
{
|
2015-02-24 16:58:47 -05:00
|
|
|
struct daap_session *ptr;
|
|
|
|
struct daap_session *prev;
|
2010-07-30 15:52:04 -04:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
prev = NULL;
|
|
|
|
for (ptr = daap_sessions; ptr; ptr = ptr->next)
|
|
|
|
{
|
|
|
|
if (ptr == s)
|
|
|
|
break;
|
2010-07-30 15:52:04 -04:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
prev = ptr;
|
|
|
|
}
|
2014-03-11 18:20:29 -04:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
if (!ptr)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Error: Request to remove non-existent session. BUG!\n");
|
|
|
|
return;
|
|
|
|
}
|
2014-03-11 18:20:29 -04:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
if (!prev)
|
|
|
|
daap_sessions = s->next;
|
|
|
|
else
|
|
|
|
prev->next = s->next;
|
2010-03-06 04:30:59 -05:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
daap_session_free(s);
|
2010-03-06 04:29:53 -05:00
|
|
|
}
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
static struct daap_session *
|
2015-02-24 16:58:47 -05:00
|
|
|
daap_session_get(int id)
|
|
|
|
{
|
|
|
|
struct daap_session *s;
|
|
|
|
|
|
|
|
for (s = daap_sessions; s; s = s->next)
|
|
|
|
{
|
|
|
|
if (id == s->id)
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-10-03 04:00:42 -04:00
|
|
|
/* Removes stale sessions and also drops the oldest sessions if DAAP_SESSION_MAX
|
|
|
|
* will otherwise be exceeded
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
daap_session_cleanup(void)
|
|
|
|
{
|
|
|
|
struct daap_session *s;
|
|
|
|
struct daap_session *next;
|
|
|
|
time_t now;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
count = 0;
|
|
|
|
now = time(NULL);
|
|
|
|
|
|
|
|
for (s = daap_sessions; s; s = next)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
next = s->next;
|
|
|
|
|
|
|
|
if ((difftime(now, s->mtime) > DAAP_SESSION_TIMEOUT) || (count > DAAP_SESSION_MAX))
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Cleaning up DAAP session (id %d)\n", s->id);
|
|
|
|
|
|
|
|
daap_session_remove(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
static struct daap_session *
|
|
|
|
daap_session_add(const char *user_agent, int request_session_id)
|
2010-01-20 12:24:52 -05:00
|
|
|
{
|
|
|
|
struct daap_session *s;
|
|
|
|
|
2015-10-03 04:00:42 -04:00
|
|
|
daap_session_cleanup();
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
s = (struct daap_session *)malloc(sizeof(struct daap_session));
|
|
|
|
if (!s)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Out of memory for DAAP session\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(s, 0, sizeof(struct daap_session));
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
if (request_session_id)
|
2014-04-01 16:47:04 -04:00
|
|
|
{
|
2015-02-24 16:58:47 -05:00
|
|
|
if (daap_session_get(request_session_id))
|
2014-04-01 16:47:04 -04:00
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Session id requested in login (%d) is not available\n", request_session_id);
|
|
|
|
return NULL;
|
|
|
|
}
|
2015-02-24 16:58:47 -05:00
|
|
|
|
|
|
|
s->id = request_session_id;
|
|
|
|
}
|
2014-04-01 16:47:04 -04:00
|
|
|
else
|
|
|
|
{
|
2015-02-24 16:58:47 -05:00
|
|
|
while ( (s->id = rand() + 100) && daap_session_get(s->id) );
|
2014-04-01 16:47:04 -04:00
|
|
|
}
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2015-10-03 04:00:42 -04:00
|
|
|
s->mtime = time(NULL);
|
|
|
|
|
2014-03-11 18:20:29 -04:00
|
|
|
if (user_agent)
|
|
|
|
s->user_agent = strdup(user_agent);
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
if (daap_sessions)
|
|
|
|
s->next = daap_sessions;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
daap_sessions = s;
|
2014-03-11 18:20:29 -04:00
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2010-01-29 16:38:40 -05:00
|
|
|
struct daap_session *
|
2010-01-20 12:24:52 -05:00
|
|
|
daap_session_find(struct evhttp_request *req, struct evkeyvalq *query, struct evbuffer *evbuf)
|
|
|
|
{
|
2010-03-06 04:30:59 -05:00
|
|
|
struct daap_session *s;
|
2010-01-20 12:24:52 -05:00
|
|
|
const char *param;
|
2015-02-24 16:58:47 -05:00
|
|
|
int id;
|
2010-01-20 12:24:52 -05:00
|
|
|
int ret;
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
if (!req)
|
|
|
|
return NULL;
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
param = evhttp_find_header(query, "session-id");
|
|
|
|
if (!param)
|
|
|
|
{
|
|
|
|
DPRINTF(E_WARN, L_DAAP, "No session-id specified in request\n");
|
|
|
|
goto invalid;
|
|
|
|
}
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
ret = safe_atoi32(param, &id);
|
2010-01-20 12:24:52 -05:00
|
|
|
if (ret < 0)
|
|
|
|
goto invalid;
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
s = daap_session_get(id);
|
|
|
|
if (!s)
|
2010-01-20 12:24:52 -05:00
|
|
|
{
|
2015-10-03 04:00:42 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "DAAP session id %d not found\n", id);
|
2010-01-20 12:24:52 -05:00
|
|
|
goto invalid;
|
|
|
|
}
|
|
|
|
|
2015-10-03 04:00:42 -04:00
|
|
|
s->mtime = time(NULL);
|
2010-03-06 04:30:59 -05:00
|
|
|
|
|
|
|
return s;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
|
|
|
invalid:
|
2010-01-27 06:18:22 -05:00
|
|
|
evhttp_send_error(req, 403, "Forbidden");
|
2010-01-20 12:24:52 -05:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-01-29 16:36:12 -05:00
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
/* Update requests helpers */
|
2015-02-24 16:58:47 -05:00
|
|
|
static void
|
|
|
|
update_free(struct daap_update_request *ur)
|
|
|
|
{
|
2015-03-06 15:22:05 -05:00
|
|
|
if (ur->timeout)
|
|
|
|
event_free(ur->timeout);
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
free(ur);
|
|
|
|
}
|
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
static void
|
2011-09-10 12:37:16 -04:00
|
|
|
update_remove(struct daap_update_request *ur)
|
2010-01-28 13:20:00 -05:00
|
|
|
{
|
|
|
|
struct daap_update_request *p;
|
|
|
|
|
|
|
|
if (ur == update_requests)
|
|
|
|
update_requests = ur->next;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (p = update_requests; p && (p->next != ur); p = p->next)
|
|
|
|
;
|
|
|
|
|
|
|
|
if (!p)
|
|
|
|
{
|
2010-01-29 16:37:05 -05:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "WARNING: struct daap_update_request not found in list; BUG!\n");
|
2010-01-28 13:20:00 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->next = ur->next;
|
|
|
|
}
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
update_free(ur);
|
2010-01-28 13:20:00 -05:00
|
|
|
}
|
|
|
|
|
2011-09-10 12:37:16 -04:00
|
|
|
static void
|
|
|
|
update_refresh_cb(int fd, short event, void *arg)
|
|
|
|
{
|
|
|
|
struct daap_update_request *ur;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evhttp_connection *evcon;
|
2011-09-10 12:37:16 -04:00
|
|
|
struct evbuffer *evbuf;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ur = (struct daap_update_request *)arg;
|
|
|
|
|
|
|
|
evbuf = evbuffer_new();
|
|
|
|
if (!evbuf)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not allocate evbuffer for DAAP update data\n");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = evbuffer_expand(evbuf, 32);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP update data\n");
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send back current revision */
|
|
|
|
dmap_add_container(evbuf, "mupd", 24);
|
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
|
|
|
dmap_add_int(evbuf, "musr", current_rev); /* 12 */
|
|
|
|
|
2014-05-29 17:22:00 -04:00
|
|
|
evcon = evhttp_request_get_connection(ur->req);
|
|
|
|
evhttp_connection_set_closecb(evcon, NULL, NULL);
|
2011-09-10 12:37:16 -04:00
|
|
|
|
|
|
|
httpd_send_reply(ur->req, HTTP_OK, "OK", evbuf);
|
|
|
|
|
|
|
|
update_remove(ur);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
update_fail_cb(struct evhttp_connection *evcon, void *arg)
|
|
|
|
{
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evhttp_connection *evc;
|
2011-09-10 12:37:16 -04:00
|
|
|
struct daap_update_request *ur;
|
|
|
|
|
|
|
|
ur = (struct daap_update_request *)arg;
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Update request: client closed connection\n");
|
|
|
|
|
2014-05-29 17:22:00 -04:00
|
|
|
evc = evhttp_request_get_connection(ur->req);
|
|
|
|
if (evc)
|
|
|
|
evhttp_connection_set_closecb(evc, NULL, NULL);
|
2011-09-10 12:37:16 -04:00
|
|
|
|
|
|
|
update_remove(ur);
|
|
|
|
}
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2010-08-14 07:57:49 -04:00
|
|
|
/* DAAP sort headers helpers */
|
|
|
|
static struct sort_ctx *
|
|
|
|
daap_sort_context_new(void)
|
|
|
|
{
|
|
|
|
struct sort_ctx *ctx;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ctx = (struct sort_ctx *)malloc(sizeof(struct sort_ctx));
|
|
|
|
if (!ctx)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Out of memory for sorting context\n");
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(ctx, 0, sizeof(struct sort_ctx));
|
|
|
|
|
|
|
|
ctx->headerlist = evbuffer_new();
|
|
|
|
if (!ctx->headerlist)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DAAP sort headers list\n");
|
|
|
|
|
|
|
|
free(ctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = evbuffer_expand(ctx->headerlist, 512);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP sort headers list\n");
|
|
|
|
|
|
|
|
evbuffer_free(ctx->headerlist);
|
|
|
|
free(ctx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx->mshc = -1;
|
|
|
|
|
|
|
|
return ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
daap_sort_context_free(struct sort_ctx *ctx)
|
|
|
|
{
|
|
|
|
evbuffer_free(ctx->headerlist);
|
|
|
|
free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
daap_sort_build(struct sort_ctx *ctx, char *str)
|
|
|
|
{
|
|
|
|
uint8_t *ret;
|
|
|
|
size_t len;
|
|
|
|
char fl;
|
|
|
|
|
|
|
|
len = strlen(str);
|
2013-12-12 17:27:50 -05:00
|
|
|
if (len > 0)
|
2010-08-14 07:57:49 -04:00
|
|
|
{
|
2015-06-06 08:47:54 -04:00
|
|
|
ret = u8_normalize(UNINORM_NFD, (uint8_t *)str, len + 1, NULL, &len);
|
2013-12-12 17:27:50 -05:00
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not normalize string for sort header\n");
|
2010-08-14 07:57:49 -04:00
|
|
|
|
2013-12-12 17:27:50 -05:00
|
|
|
return -1;
|
|
|
|
}
|
2010-08-14 07:57:49 -04:00
|
|
|
|
2013-12-12 17:27:50 -05:00
|
|
|
fl = ret[0];
|
|
|
|
free(ret);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fl = 0;
|
2010-08-14 07:57:49 -04:00
|
|
|
|
|
|
|
if (isascii(fl) && isalpha(fl))
|
|
|
|
{
|
|
|
|
fl = toupper(fl);
|
|
|
|
|
|
|
|
/* Init */
|
|
|
|
if (ctx->mshc == -1)
|
|
|
|
ctx->mshc = fl;
|
|
|
|
|
|
|
|
if (fl == ctx->mshc)
|
|
|
|
ctx->mshn++;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dmap_add_container(ctx->headerlist, "mlit", 34);
|
|
|
|
dmap_add_short(ctx->headerlist, "mshc", ctx->mshc); /* 10 */
|
|
|
|
dmap_add_int(ctx->headerlist, "mshi", ctx->mshi); /* 12 */
|
|
|
|
dmap_add_int(ctx->headerlist, "mshn", ctx->mshn); /* 12 */
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Added sort header: mshc = %c, mshi = %u, mshn = %u fl %c\n", ctx->mshc, ctx->mshi, ctx->mshn, fl);
|
|
|
|
|
|
|
|
ctx->mshi = ctx->mshi + ctx->mshn;
|
|
|
|
ctx->mshn = 1;
|
|
|
|
ctx->mshc = fl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Non-ASCII, goes to misc category */
|
|
|
|
ctx->misc_mshn++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-11-10 06:35:24 -05:00
|
|
|
static void
|
|
|
|
daap_sort_finalize(struct sort_ctx *ctx)
|
2010-08-14 07:57:49 -04:00
|
|
|
{
|
|
|
|
/* Add current entry, if any */
|
|
|
|
if (ctx->mshc != -1)
|
|
|
|
{
|
|
|
|
dmap_add_container(ctx->headerlist, "mlit", 34);
|
|
|
|
dmap_add_short(ctx->headerlist, "mshc", ctx->mshc); /* 10 */
|
|
|
|
dmap_add_int(ctx->headerlist, "mshi", ctx->mshi); /* 12 */
|
|
|
|
dmap_add_int(ctx->headerlist, "mshn", ctx->mshn); /* 12 */
|
|
|
|
|
|
|
|
ctx->mshi = ctx->mshi + ctx->mshn;
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Added sort header: mshc = %c, mshi = %u, mshn = %u (final)\n", ctx->mshc, ctx->mshi, ctx->mshn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add misc category */
|
|
|
|
dmap_add_container(ctx->headerlist, "mlit", 34);
|
|
|
|
dmap_add_short(ctx->headerlist, "mshc", '0'); /* 10 */
|
|
|
|
dmap_add_int(ctx->headerlist, "mshi", ctx->mshi); /* 12 */
|
|
|
|
dmap_add_int(ctx->headerlist, "mshn", ctx->misc_mshn); /* 12 */
|
|
|
|
}
|
|
|
|
|
2014-12-28 17:37:12 -05:00
|
|
|
/* Remotes are clients that will issue DACP commands. For these clients we will
|
|
|
|
* do the playback, and we will not stream to them. This is a crude function to
|
|
|
|
* identify them, so we can give them appropriate treatment.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
is_remote(const char *user_agent)
|
|
|
|
{
|
|
|
|
if (!user_agent)
|
|
|
|
return 0;
|
|
|
|
if (strcasestr(user_agent, "remote"))
|
|
|
|
return 1;
|
|
|
|
if (strstr(user_agent, "Retune"))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-11 18:20:29 -04:00
|
|
|
/* We try not to return items that the client cannot play (like Spotify and
|
|
|
|
* internet streams in iTunes), or which are inappropriate (like internet streams
|
2014-12-28 17:37:12 -05:00
|
|
|
* in the album tab of remotes)
|
2014-03-11 18:20:29 -04:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
user_agent_filter(const char *user_agent, struct query_params *qp)
|
|
|
|
{
|
2014-12-28 17:37:12 -05:00
|
|
|
const char *filter;
|
2014-03-11 18:20:29 -04:00
|
|
|
char *buffer;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (!user_agent)
|
|
|
|
return;
|
|
|
|
|
2014-04-01 15:51:07 -04:00
|
|
|
// Valgrind doesn't like strlen(filter) below, so instead we allocate 128 bytes
|
|
|
|
// to hold the string and the leading " AND ". Remember to adjust the 128 if
|
|
|
|
// you define strings here that will be too large for the buffer.
|
2014-12-28 17:37:12 -05:00
|
|
|
if (is_remote(user_agent))
|
|
|
|
filter = "(f.data_kind <> 1)"; // No internet radio
|
2014-03-11 18:20:29 -04:00
|
|
|
else
|
2014-12-28 17:37:12 -05:00
|
|
|
filter = "(f.data_kind = 0)"; // Only real files
|
2014-03-11 18:20:29 -04:00
|
|
|
|
|
|
|
if (qp->filter)
|
|
|
|
{
|
2014-04-01 15:51:07 -04:00
|
|
|
len = strlen(qp->filter) + 128;
|
|
|
|
buffer = (char *)malloc(len);
|
|
|
|
snprintf(buffer, len, "%s AND %s", qp->filter, filter);
|
2014-03-11 18:20:29 -04:00
|
|
|
free(qp->filter);
|
|
|
|
qp->filter = strdup(buffer);
|
|
|
|
free(buffer);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
qp->filter = strdup(filter);
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "SQL filter w/client mod: %s\n", qp->filter);
|
|
|
|
}
|
2010-08-14 07:57:49 -04:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
/* Returns eg /databases/1/containers from /databases/1/containers?meta=dmap.item... */
|
|
|
|
static char *
|
|
|
|
extract_uri(char *full_uri)
|
|
|
|
{
|
|
|
|
char *uri;
|
|
|
|
char *ptr;
|
|
|
|
|
|
|
|
ptr = strchr(full_uri, '?');
|
|
|
|
if (ptr)
|
|
|
|
*ptr = '\0';
|
|
|
|
|
|
|
|
uri = strdup(full_uri);
|
|
|
|
|
|
|
|
if (ptr)
|
|
|
|
*ptr = '?';
|
|
|
|
|
|
|
|
if (!uri)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ptr = uri;
|
|
|
|
uri = evhttp_decode_uri(uri);
|
|
|
|
free(ptr);
|
|
|
|
|
|
|
|
return uri;
|
|
|
|
}
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
static void
|
2010-08-14 06:13:41 -04:00
|
|
|
get_query_params(struct evkeyvalq *query, int *sort_headers, struct query_params *qp)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
const char *param;
|
|
|
|
char *ptr;
|
2009-06-07 12:58:02 -04:00
|
|
|
int low;
|
|
|
|
int high;
|
2009-04-28 11:48:57 -04:00
|
|
|
int ret;
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
low = 0;
|
|
|
|
high = -1; /* No limit */
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
param = evhttp_find_header(query, "index");
|
|
|
|
if (param)
|
|
|
|
{
|
|
|
|
if (param[0] == '-') /* -n, last n entries */
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Unsupported index range: %s\n", param);
|
|
|
|
else
|
|
|
|
{
|
2010-02-02 15:02:24 -05:00
|
|
|
ret = safe_atoi32(param, &low);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (ret < 0)
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not parse index range: %s\n", param);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ptr = strchr(param, '-');
|
|
|
|
if (!ptr) /* single item */
|
2009-06-07 12:58:02 -04:00
|
|
|
high = low;
|
2009-04-28 11:48:57 -04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
ptr++;
|
|
|
|
if (*ptr != '\0') /* low-high */
|
|
|
|
{
|
2010-02-02 15:02:24 -05:00
|
|
|
ret = safe_atoi32(ptr, &high);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (ret < 0)
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not parse high index in range: %s\n", param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "Index range %s: low %d, high %d (offset %d, limit %d)\n", param, low, high, qp->offset, qp->limit);
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
if (high < low)
|
|
|
|
high = -1; /* No limit */
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
qp->offset = low;
|
|
|
|
if (high < 0)
|
|
|
|
qp->limit = -1; /* No limit */
|
|
|
|
else
|
|
|
|
qp->limit = (high - low) + 1;
|
|
|
|
|
2016-04-09 18:11:33 -04:00
|
|
|
if (qp->limit == -1 && qp->offset == 0)
|
|
|
|
qp->idx_type = I_NONE;
|
|
|
|
else
|
|
|
|
qp->idx_type = I_SUB;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-04-24 04:20:26 -04:00
|
|
|
qp->sort = S_NONE;
|
2010-08-03 17:28:46 -04:00
|
|
|
param = evhttp_find_header(query, "sort");
|
|
|
|
if (param)
|
|
|
|
{
|
|
|
|
if (strcmp(param, "name") == 0)
|
|
|
|
qp->sort = S_NAME;
|
|
|
|
else if (strcmp(param, "album") == 0)
|
|
|
|
qp->sort = S_ALBUM;
|
2010-10-09 09:57:34 -04:00
|
|
|
else if (strcmp(param, "artist") == 0)
|
|
|
|
qp->sort = S_ARTIST;
|
2013-12-04 15:29:58 -05:00
|
|
|
else if (strcmp(param, "releasedate") == 0)
|
|
|
|
qp->sort = S_NAME;
|
2010-08-03 17:28:46 -04:00
|
|
|
else
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Unknown sort param: %s\n", param);
|
|
|
|
|
|
|
|
if (qp->sort != S_NONE)
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Sorting songlist by %s\n", param);
|
|
|
|
}
|
2010-04-24 04:20:26 -04:00
|
|
|
|
2010-08-14 06:13:41 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
|
|
|
*sort_headers = 0;
|
|
|
|
param = evhttp_find_header(query, "include-sort-headers");
|
|
|
|
if (param)
|
|
|
|
{
|
|
|
|
if (strcmp(param, "1") == 0)
|
|
|
|
{
|
|
|
|
*sort_headers = 1;
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Sort headers requested\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Unknown include-sort-headers param: %s\n", param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
param = evhttp_find_header(query, "query");
|
2009-06-02 10:32:09 -04:00
|
|
|
if (!param)
|
|
|
|
param = evhttp_find_header(query, "filter");
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
if (param)
|
|
|
|
{
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "DAAP browse query filter: %s\n", param);
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
qp->filter = daap_query_parse_sql(param);
|
|
|
|
if (!qp->filter)
|
2015-04-22 15:54:31 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Ignoring improper DAAP query: %s\n", param);
|
2015-05-05 15:46:56 -04:00
|
|
|
|
|
|
|
/* iTunes seems to default to this when there is a query (which there is for audiobooks, but not for normal playlists) */
|
|
|
|
if (qp->sort == S_NONE)
|
|
|
|
qp->sort = S_ALBUM;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
static int
|
|
|
|
parse_meta(struct evhttp_request *req, char *tag, const char *param, const struct dmap_field ***out_meta)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2011-04-01 13:01:35 -04:00
|
|
|
const struct dmap_field **meta;
|
2009-04-28 11:48:57 -04:00
|
|
|
char *ptr;
|
2011-04-01 13:01:35 -04:00
|
|
|
char *field;
|
2009-04-28 11:48:57 -04:00
|
|
|
char *metastr;
|
2011-04-01 13:01:35 -04:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
int nmeta;
|
2009-04-28 16:51:52 -04:00
|
|
|
int i;
|
2013-12-16 16:09:18 -05:00
|
|
|
int n;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
metastr = strdup(param);
|
|
|
|
if (!metastr)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not duplicate meta parameter; out of memory\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2011-04-01 13:01:35 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
nmeta = 1;
|
|
|
|
ptr = metastr;
|
2013-12-02 14:55:16 -05:00
|
|
|
while ((ptr = strchr(ptr + 1, ',')) && (strlen(ptr) > 1))
|
2009-04-28 11:48:57 -04:00
|
|
|
nmeta++;
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Asking for %d meta tags\n", nmeta);
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
meta = (const struct dmap_field **)malloc(nmeta * sizeof(const struct dmap_field *));
|
|
|
|
if (!meta)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not allocate meta array; out of memory\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
nmeta = -1;
|
|
|
|
goto out;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
2011-04-01 13:01:35 -04:00
|
|
|
memset(meta, 0, nmeta * sizeof(struct dmap_field *));
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
field = strtok_r(metastr, ",", &ptr);
|
2009-04-28 16:51:52 -04:00
|
|
|
for (i = 0; i < nmeta; i++)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2013-12-16 16:09:18 -05:00
|
|
|
for (n = 0; (n < i) && (strcmp(field, meta[n]->desc) != 0); n++);
|
2011-04-01 13:01:35 -04:00
|
|
|
|
2013-12-16 16:09:18 -05:00
|
|
|
if (n == i)
|
2011-04-01 13:01:35 -04:00
|
|
|
{
|
2013-12-16 16:09:18 -05:00
|
|
|
meta[i] = dmap_find_field(field, strlen(field));
|
|
|
|
|
|
|
|
if (!meta[i])
|
|
|
|
{
|
|
|
|
DPRINTF(E_WARN, L_DAAP, "Could not find requested meta field '%s'\n", field);
|
|
|
|
|
|
|
|
i--;
|
|
|
|
nmeta--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINTF(E_WARN, L_DAAP, "Parser will ignore duplicate occurrence of meta field '%s'\n", field);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
i--;
|
|
|
|
nmeta--;
|
|
|
|
}
|
|
|
|
|
|
|
|
field = strtok_r(NULL, ",", &ptr);
|
|
|
|
if (!field)
|
2009-04-28 16:51:52 -04:00
|
|
|
break;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Found %d meta tags\n", nmeta);
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
*out_meta = meta;
|
2009-04-28 16:51:52 -04:00
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
out:
|
2009-04-28 16:51:52 -04:00
|
|
|
free(metastr);
|
2011-04-01 13:01:35 -04:00
|
|
|
|
|
|
|
return nmeta;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_server_info(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-03-26 18:59:28 -04:00
|
|
|
struct evbuffer *content;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evkeyvalq *headers;
|
2009-04-28 11:48:57 -04:00
|
|
|
cfg_t *lib;
|
|
|
|
char *name;
|
|
|
|
char *passwd;
|
|
|
|
const char *clientver;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2009-04-28 11:48:57 -04:00
|
|
|
int mpro;
|
|
|
|
int apro;
|
|
|
|
|
2010-03-19 14:06:47 -04:00
|
|
|
lib = cfg_getsec(cfg, "library");
|
2009-04-28 11:48:57 -04:00
|
|
|
passwd = cfg_getstr(lib, "password");
|
|
|
|
name = cfg_getstr(lib, "name");
|
|
|
|
|
2014-03-26 18:59:28 -04:00
|
|
|
content = evbuffer_new();
|
|
|
|
if (!content)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-03-26 18:59:28 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DAAP server-info reply\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "msrv", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-03-26 18:59:28 -04:00
|
|
|
mpro = 2 << 16 | 10;
|
|
|
|
apro = 3 << 16 | 12;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2014-05-29 17:22:00 -04:00
|
|
|
headers = evhttp_request_get_input_headers(req);
|
|
|
|
clientver = evhttp_find_header(headers, "Client-DAAP-Version");
|
2009-04-28 11:48:57 -04:00
|
|
|
if (clientver)
|
|
|
|
{
|
|
|
|
if (strcmp(clientver, "1.0") == 0)
|
|
|
|
{
|
|
|
|
mpro = 1 << 16;
|
|
|
|
apro = 1 << 16;
|
|
|
|
}
|
|
|
|
else if (strcmp(clientver, "2.0") == 0)
|
|
|
|
{
|
|
|
|
mpro = 1 << 16;
|
|
|
|
apro = 2 << 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-26 18:59:28 -04:00
|
|
|
dmap_add_int(content, "mstt", 200);
|
|
|
|
dmap_add_int(content, "mpro", mpro); // dmap.protocolversion
|
|
|
|
dmap_add_string(content, "minm", name); // dmap.itemname (server name)
|
|
|
|
|
|
|
|
dmap_add_int(content, "apro", apro); // daap.protocolversion
|
|
|
|
dmap_add_int(content, "aeSV", apro); // com.apple.itunes.music-sharing-version (determines if itunes shows share types)
|
|
|
|
|
|
|
|
dmap_add_short(content, "ated", 7); // daap.supportsextradata
|
2015-02-06 16:14:26 -05:00
|
|
|
|
|
|
|
/* Sub-optimal user-agent sniffing to solve the problem that iTunes 12.1
|
|
|
|
* does not work if we announce support for groups.
|
|
|
|
*/
|
|
|
|
ua = evhttp_find_header(headers, "User-Agent");
|
|
|
|
if (ua && (strncmp(ua, "iTunes", strlen("iTunes")) == 0))
|
|
|
|
dmap_add_short(content, "asgr", 0); // daap.supportsgroups (1=artists, 2=albums, 3=both)
|
|
|
|
else
|
|
|
|
dmap_add_short(content, "asgr", 3); // daap.supportsgroups (1=artists, 2=albums, 3=both)
|
2014-03-26 18:59:28 -04:00
|
|
|
|
|
|
|
// dmap_add_long(content, "asse", 0x80000); // unknown - used by iTunes
|
|
|
|
|
|
|
|
dmap_add_char(content, "aeMQ", 1); // unknown - used by iTunes
|
|
|
|
|
|
|
|
// dmap_add_long(content, "mscu", ); // unknown - used by iTunes
|
|
|
|
// dmap_add_char(content, "aeFR", ); // unknown - used by iTunes
|
|
|
|
|
|
|
|
dmap_add_char(content, "aeTr", 1); // unknown - used by iTunes
|
|
|
|
dmap_add_char(content, "aeSL", 1); // unknown - used by iTunes
|
|
|
|
dmap_add_char(content, "aeSR", 1); // unknown - used by iTunes
|
|
|
|
// dmap_add_char(content, "aeFP", 2); // triggers FairPlay request
|
|
|
|
// dmap_add_long(content, "aeSX", ); // unknown - used by iTunes
|
|
|
|
|
|
|
|
// dmap_add_int(content, "ppro", ); // dpap.protocolversion
|
|
|
|
|
2014-04-20 17:34:04 -04:00
|
|
|
dmap_add_char(content, "msed", 0); // dmap.supportsedit? - we don't support playlist editing
|
2014-03-26 18:59:28 -04:00
|
|
|
|
|
|
|
dmap_add_char(content, "mslr", 1); // dmap.loginrequired
|
|
|
|
dmap_add_int(content, "mstm", DAAP_SESSION_TIMEOUT_CAPABILITY); // dmap.timeoutinterval
|
|
|
|
dmap_add_char(content, "msal", 1); // dmap.supportsautologout
|
|
|
|
// dmap_add_char(content, "msas", 3); // dmap.authenticationschemes
|
|
|
|
dmap_add_char(content, "msau", (passwd) ? 2 : 0); // dmap.authenticationmethod
|
2010-03-06 04:30:59 -05:00
|
|
|
|
2014-03-26 18:59:28 -04:00
|
|
|
dmap_add_char(content, "msup", 1); // dmap.supportsupdate
|
|
|
|
dmap_add_char(content, "mspi", 1); // dmap.supportspersistentids
|
|
|
|
dmap_add_char(content, "msex", 1); // dmap.supportsextensions
|
|
|
|
dmap_add_char(content, "msbr", 1); // dmap.supportsbrowse
|
|
|
|
dmap_add_char(content, "msqy", 1); // dmap.supportsquery
|
|
|
|
dmap_add_char(content, "msix", 1); // dmap.supportsindex
|
|
|
|
// dmap_add_char(content, "msrs", 1); // dmap.supportsresolve
|
2012-01-02 02:15:18 -05:00
|
|
|
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(content, "msdc", 2); // dmap.databasescount
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2015-02-06 16:14:26 -05:00
|
|
|
// dmap_add_int(content, "mstc", ); // dmap.utctime
|
|
|
|
// dmap_add_int(content, "msto", ); // dmap.utcoffset
|
|
|
|
|
2014-03-26 18:59:28 -04:00
|
|
|
// Create container
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(content);
|
|
|
|
dmap_add_container(evbuf, "msrv", len);
|
2014-03-26 18:59:28 -04:00
|
|
|
evbuffer_add_buffer(evbuf, content);
|
|
|
|
evbuffer_free(content);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_content_codes(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2011-04-02 08:40:48 -04:00
|
|
|
const struct dmap_field *dmap_fields;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2011-04-02 08:40:48 -04:00
|
|
|
int nfields;
|
2009-04-28 11:48:57 -04:00
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
|
2011-04-02 08:40:48 -04:00
|
|
|
dmap_fields = dmap_get_fields_table(&nfields);
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
len = 12;
|
2011-04-02 08:40:48 -04:00
|
|
|
for (i = 0; i < nfields; i++)
|
2011-04-01 13:01:35 -04:00
|
|
|
len += 8 + 12 + 10 + 8 + strlen(dmap_fields[i].desc);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
ret = evbuffer_expand(evbuf, len + 8);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP content-codes reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "mccr", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
dmap_add_container(evbuf, "mccr", len);
|
|
|
|
dmap_add_int(evbuf, "mstt", 200);
|
|
|
|
|
2011-04-02 08:40:48 -04:00
|
|
|
for (i = 0; i < nfields; i++)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2011-04-01 13:01:35 -04:00
|
|
|
len = 12 + 10 + 8 + strlen(dmap_fields[i].desc);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
dmap_add_container(evbuf, "mdcl", len);
|
2011-04-01 13:01:35 -04:00
|
|
|
dmap_add_string(evbuf, "mcnm", dmap_fields[i].tag); /* 12 */
|
|
|
|
dmap_add_string(evbuf, "mcna", dmap_fields[i].desc); /* 8 + strlen(desc) */
|
|
|
|
dmap_add_short(evbuf, "mcty", dmap_fields[i].type); /* 10 */
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_login(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2010-01-30 10:50:00 -05:00
|
|
|
struct pairing_info pi;
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
2014-04-01 16:47:04 -04:00
|
|
|
const char *param;
|
|
|
|
int request_session_id;
|
2009-04-28 11:48:57 -04:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = evbuffer_expand(evbuf, 32);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP login reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "mlog", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-01-27 14:43:22 -05:00
|
|
|
if (ua && (strncmp(ua, "Remote", strlen("Remote")) == 0))
|
2010-01-30 10:50:00 -05:00
|
|
|
{
|
2014-04-01 16:47:04 -04:00
|
|
|
param = evhttp_find_header(query, "pairing-guid");
|
|
|
|
if (!param)
|
2010-01-30 10:50:00 -05:00
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Login attempt with U-A: Remote and no pairing-guid\n");
|
|
|
|
|
|
|
|
evhttp_send_error(req, 403, "Forbidden");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-30 10:50:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(&pi, 0, sizeof(struct pairing_info));
|
2014-04-01 16:47:04 -04:00
|
|
|
pi.guid = strdup(param + 2); /* Skip leading 0X */
|
2010-01-30 10:50:00 -05:00
|
|
|
|
|
|
|
ret = db_pairing_fetch_byguid(&pi);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Login attempt with invalid pairing-guid\n");
|
|
|
|
|
|
|
|
free_pi(&pi, 1);
|
|
|
|
evhttp_send_error(req, 403, "Forbidden");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-30 10:50:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_INFO, L_DAAP, "Remote '%s' logging in with GUID %s\n", pi.name, pi.guid);
|
|
|
|
free_pi(&pi, 1);
|
|
|
|
}
|
|
|
|
|
2014-04-01 16:47:04 -04:00
|
|
|
param = evhttp_find_header(query, "request-session-id");
|
|
|
|
if (param)
|
|
|
|
{
|
|
|
|
ret = safe_atoi32(param, &request_session_id);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Login request where request-session-id is not an integer\n");
|
|
|
|
request_session_id = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
request_session_id = 0;
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
s = daap_session_add(ua, request_session_id);
|
2010-01-20 12:24:52 -05:00
|
|
|
if (!s)
|
|
|
|
{
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "mlog", "Could not start session");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
}
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_container(evbuf, "mlog", 24);
|
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
2010-01-20 12:24:52 -05:00
|
|
|
dmap_add_int(evbuf, "mlid", s->id); /* 12 */
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_logout(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
|
|
|
|
|
|
|
s = daap_session_find(req, query, evbuf);
|
|
|
|
if (!s)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
daap_session_remove(s);
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, 204, "Logout Successful", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_update(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2010-01-30 06:57:50 -05:00
|
|
|
struct daap_session *s;
|
2010-01-28 13:20:00 -05:00
|
|
|
struct daap_update_request *ur;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evhttp_connection *evcon;
|
2010-01-28 13:20:00 -05:00
|
|
|
const char *param;
|
|
|
|
int reqd_rev;
|
2009-04-28 11:48:57 -04:00
|
|
|
int ret;
|
|
|
|
|
2010-01-30 06:57:50 -05:00
|
|
|
s = daap_session_find(req, query, evbuf);
|
|
|
|
if (!s)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-30 06:57:50 -05:00
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
param = evhttp_find_header(query, "revision-number");
|
|
|
|
if (!param)
|
|
|
|
{
|
2014-12-16 18:33:16 -05:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "Missing revision-number in client update request\n");
|
2012-01-02 04:22:39 -05:00
|
|
|
/* Some players (Amarok, Banshee) don't supply a revision number.
|
|
|
|
They get a standard update of everything. */
|
|
|
|
param = "1"; /* Default to "1" will insure update */
|
2010-01-28 13:20:00 -05:00
|
|
|
}
|
|
|
|
|
2010-02-02 15:02:24 -05:00
|
|
|
ret = safe_atoi32(param, &reqd_rev);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
2010-01-28 13:20:00 -05:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Parameter revision-number not an integer\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "mupd", "Invalid request");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-28 13:20:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (reqd_rev == 1) /* Or revision is not valid */
|
|
|
|
{
|
|
|
|
ret = evbuffer_expand(evbuf, 32);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP update reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "mupd", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-28 13:20:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Send back current revision */
|
|
|
|
dmap_add_container(evbuf, "mupd", 24);
|
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
|
|
|
dmap_add_int(evbuf, "musr", current_rev); /* 12 */
|
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2010-01-28 13:20:00 -05:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return 0;
|
2010-01-28 13:20:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Else, just let the request hang until we have changes to push back */
|
|
|
|
ur = (struct daap_update_request *)malloc(sizeof(struct daap_update_request));
|
|
|
|
if (!ur)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Out of memory for update request\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "mupd", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
2011-09-10 12:37:16 -04:00
|
|
|
memset(ur, 0, sizeof(struct daap_update_request));
|
|
|
|
|
2015-03-06 15:22:05 -05:00
|
|
|
if (DAAP_UPDATE_REFRESH > 0)
|
|
|
|
{
|
|
|
|
ur->timeout = evtimer_new(evbase_httpd, update_refresh_cb, ur);
|
|
|
|
if (ur->timeout)
|
|
|
|
ret = evtimer_add(ur->timeout, &daap_update_refresh_tv);
|
|
|
|
else
|
|
|
|
ret = -1;
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Out of memory for update request event\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, "mupd", "Could not register timer");
|
|
|
|
update_free(ur);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
/* NOTE: we may need to keep reqd_rev in there too */
|
|
|
|
ur->req = req;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-28 13:20:00 -05:00
|
|
|
ur->next = update_requests;
|
|
|
|
update_requests = ur;
|
|
|
|
|
2010-02-14 03:34:29 -05:00
|
|
|
/* If the connection fails before we have an update to push out
|
|
|
|
* to the client, we need to know.
|
2010-01-28 13:20:00 -05:00
|
|
|
*/
|
2014-05-29 17:22:00 -04:00
|
|
|
evcon = evhttp_request_get_connection(req);
|
|
|
|
if (evcon)
|
|
|
|
evhttp_connection_set_closecb(evcon, update_fail_cb, ur);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_activity(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-12-20 08:43:28 -05:00
|
|
|
{
|
|
|
|
/* That's so nice, thanks for letting us know */
|
|
|
|
evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-12-20 08:43:28 -05:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_dblist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-03-26 18:59:28 -04:00
|
|
|
struct evbuffer *content;
|
2014-05-29 03:29:23 -04:00
|
|
|
struct evbuffer *item;
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
2009-04-28 11:48:57 -04:00
|
|
|
cfg_t *lib;
|
|
|
|
char *name;
|
2015-03-30 15:10:59 -04:00
|
|
|
char *name_radio;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2009-06-07 12:58:02 -04:00
|
|
|
int count;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
s = daap_session_find(req, query, evbuf);
|
|
|
|
if (!s)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2010-03-19 14:06:47 -04:00
|
|
|
lib = cfg_getsec(cfg, "library");
|
2009-04-28 11:48:57 -04:00
|
|
|
name = cfg_getstr(lib, "name");
|
2015-03-30 15:10:59 -04:00
|
|
|
name_radio = cfg_getstr(lib, "name_radio");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2014-03-26 18:59:28 -04:00
|
|
|
content = evbuffer_new();
|
|
|
|
if (!content)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-03-26 18:59:28 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DAAP dblist reply\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "avdb", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-05-29 03:29:23 -04:00
|
|
|
// Add db entry for library with dbid = 1
|
|
|
|
item = evbuffer_new();
|
2015-03-15 11:26:06 -04:00
|
|
|
if (!item)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DAAP dblist library item\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, "avdb", "Out of memory");
|
|
|
|
return -1;
|
|
|
|
}
|
2014-05-29 03:29:23 -04:00
|
|
|
|
|
|
|
dmap_add_int(item, "miid", 1);
|
|
|
|
dmap_add_long(item, "mper", 1);
|
|
|
|
dmap_add_int(item, "mdbk", 1);
|
|
|
|
dmap_add_int(item, "aeCs", 1);
|
|
|
|
dmap_add_string(item, "minm", name);
|
2009-06-11 12:41:50 -04:00
|
|
|
count = db_files_get_count();
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(item, "mimc", count);
|
2014-03-22 19:23:34 -04:00
|
|
|
count = db_pl_get_count(); // TODO Don't count empty smart playlists, because they get excluded in aply
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(item, "mctc", count);
|
2014-03-28 14:25:15 -04:00
|
|
|
// dmap_add_int(content, "aeMk", 0x405); // com.apple.itunes.extended-media-kind (OR of all in library)
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(item, "meds", 3);
|
|
|
|
|
|
|
|
// Create container for library db
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(item);
|
|
|
|
dmap_add_container(content, "mlit", len);
|
2014-05-29 03:29:23 -04:00
|
|
|
evbuffer_add_buffer(content, item);
|
|
|
|
evbuffer_free(item);
|
|
|
|
|
2015-03-16 18:33:42 -04:00
|
|
|
// Add second db entry for radio with dbid = DAAP_DB_RADIO
|
2014-05-29 03:29:23 -04:00
|
|
|
item = evbuffer_new();
|
2015-03-15 11:26:06 -04:00
|
|
|
if (!item)
|
2014-05-29 03:29:23 -04:00
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DAAP dblist radio item\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, "avdb", "Out of memory");
|
2015-03-15 11:26:06 -04:00
|
|
|
return -1;
|
2014-05-29 03:29:23 -04:00
|
|
|
}
|
2015-03-15 11:26:06 -04:00
|
|
|
|
2015-03-16 18:33:42 -04:00
|
|
|
dmap_add_int(item, "miid", DAAP_DB_RADIO);
|
|
|
|
dmap_add_long(item, "mper", DAAP_DB_RADIO);
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(item, "mdbk", 0x64);
|
|
|
|
dmap_add_int(item, "aeCs", 0);
|
2015-03-30 15:10:59 -04:00
|
|
|
dmap_add_string(item, "minm", name_radio);
|
2015-03-16 18:33:42 -04:00
|
|
|
count = db_pl_get_count(); // TODO This counts too much, should only include stream playlists
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(item, "mimc", count);
|
|
|
|
dmap_add_int(item, "mctc", 0);
|
|
|
|
dmap_add_int(item, "aeMk", 1); // com.apple.itunes.extended-media-kind (OR of all in library)
|
|
|
|
dmap_add_int(item, "meds", 3);
|
|
|
|
|
|
|
|
// Create container for radio db
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(item);
|
|
|
|
dmap_add_container(content, "mlit", len);
|
2014-05-29 03:29:23 -04:00
|
|
|
evbuffer_add_buffer(content, item);
|
|
|
|
evbuffer_free(item);
|
2014-03-26 18:59:28 -04:00
|
|
|
|
|
|
|
// Create container
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(content);
|
|
|
|
dmap_add_container(evbuf, "avdb", len + 53);
|
2014-03-26 18:59:28 -04:00
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
|
|
|
dmap_add_char(evbuf, "muty", 0); /* 9 */
|
2014-05-29 03:29:23 -04:00
|
|
|
dmap_add_int(evbuf, "mtco", 2); /* 12 */
|
|
|
|
dmap_add_int(evbuf, "mrco", 2); /* 12 */
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, "mlcl", len); /* 8 */
|
2014-03-26 18:59:28 -04:00
|
|
|
evbuffer_add_buffer(evbuf, content);
|
|
|
|
evbuffer_free(content);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_songlist_generic(struct evhttp_request *req, struct evbuffer *evbuf, int playlist, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-03-11 18:20:29 -04:00
|
|
|
struct daap_session *s;
|
2009-06-07 12:58:02 -04:00
|
|
|
struct query_params qp;
|
|
|
|
struct db_media_file_info dbmfi;
|
2009-04-28 11:48:57 -04:00
|
|
|
struct evbuffer *song;
|
|
|
|
struct evbuffer *songlist;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evkeyvalq *headers;
|
2011-04-01 13:01:35 -04:00
|
|
|
const struct dmap_field **meta;
|
2010-08-30 10:59:48 -04:00
|
|
|
struct sort_ctx *sctx;
|
2009-04-28 11:48:57 -04:00
|
|
|
const char *param;
|
2014-08-22 18:02:01 -04:00
|
|
|
const char *client_codecs;
|
2014-12-28 17:37:12 -05:00
|
|
|
char *last_codectype;
|
2009-04-28 11:48:57 -04:00
|
|
|
char *tag;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2009-04-28 11:48:57 -04:00
|
|
|
int nmeta;
|
2010-08-30 10:59:48 -04:00
|
|
|
int sort_headers;
|
2009-04-28 11:48:57 -04:00
|
|
|
int nsongs;
|
2014-12-28 17:37:12 -05:00
|
|
|
int remote;
|
2009-04-28 11:48:57 -04:00
|
|
|
int transcode;
|
|
|
|
int ret;
|
|
|
|
|
2014-03-11 18:20:29 -04:00
|
|
|
s = daap_session_find(req, query, evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
if (!s && req)
|
|
|
|
return -1;
|
2014-03-11 18:20:29 -04:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "Fetching song list for playlist %d\n", playlist);
|
|
|
|
|
|
|
|
if (playlist != -1)
|
|
|
|
tag = "apso"; /* Songs in playlist */
|
|
|
|
else
|
|
|
|
tag = "adbs"; /* Songs in database */
|
|
|
|
|
|
|
|
ret = evbuffer_expand(evbuf, 61);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP song list reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
songlist = evbuffer_new();
|
|
|
|
if (!songlist)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP song list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start with a big enough evbuffer - it'll expand as needed */
|
|
|
|
ret = evbuffer_expand(songlist, 4096);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP song list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-01-10 08:34:48 -05:00
|
|
|
goto out_list_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
song = evbuffer_new();
|
|
|
|
if (!song)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP song block\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-01-10 08:34:48 -05:00
|
|
|
goto out_list_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The buffer will expand if needed */
|
|
|
|
ret = evbuffer_expand(song, 512);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP song block\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-01-10 08:34:48 -05:00
|
|
|
goto out_song_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
param = evhttp_find_header(query, "meta");
|
|
|
|
if (!param)
|
|
|
|
{
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "No meta parameter in query, using default\n");
|
|
|
|
|
|
|
|
if (playlist != -1)
|
|
|
|
param = default_meta_plsongs;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (param)
|
|
|
|
{
|
2011-04-01 13:01:35 -04:00
|
|
|
nmeta = parse_meta(req, tag, param, &meta);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (nmeta < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n");
|
|
|
|
|
2010-01-10 08:34:48 -05:00
|
|
|
goto out_song_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2009-04-28 16:51:52 -04:00
|
|
|
{
|
|
|
|
meta = NULL;
|
|
|
|
nmeta = 0;
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
memset(&qp, 0, sizeof(struct query_params));
|
2010-08-30 10:59:48 -04:00
|
|
|
get_query_params(query, &sort_headers, &qp);
|
2014-08-21 17:06:52 -04:00
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
if (playlist == -1)
|
|
|
|
user_agent_filter(ua, &qp);
|
2010-08-30 10:59:48 -04:00
|
|
|
|
|
|
|
sctx = NULL;
|
|
|
|
if (sort_headers)
|
|
|
|
{
|
|
|
|
sctx = daap_sort_context_new();
|
|
|
|
if (!sctx)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create sort context\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
|
|
|
goto out_query_free;
|
|
|
|
}
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-22 13:02:29 -05:00
|
|
|
if (playlist != -1)
|
2009-06-07 12:58:02 -04:00
|
|
|
{
|
|
|
|
qp.type = Q_PLITEMS;
|
2010-03-07 05:53:16 -05:00
|
|
|
qp.id = playlist;
|
2009-06-07 12:58:02 -04:00
|
|
|
}
|
2010-01-22 13:02:29 -05:00
|
|
|
else
|
|
|
|
qp.type = Q_ITEMS;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
ret = db_query_start(&qp);
|
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2009-06-07 12:58:02 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Could not start query");
|
2010-08-30 10:59:48 -04:00
|
|
|
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
2010-01-10 08:34:48 -05:00
|
|
|
goto out_query_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-12-28 17:37:12 -05:00
|
|
|
remote = is_remote(ua);
|
|
|
|
|
|
|
|
client_codecs = NULL;
|
|
|
|
if (!remote && req)
|
|
|
|
{
|
|
|
|
headers = evhttp_request_get_input_headers(req);
|
|
|
|
client_codecs = evhttp_find_header(headers, "Accept-Codecs");
|
|
|
|
}
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
nsongs = 0;
|
2014-12-28 17:37:12 -05:00
|
|
|
last_codectype = NULL;
|
2009-06-07 12:58:02 -04:00
|
|
|
while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
nsongs++;
|
|
|
|
|
2014-12-28 17:37:12 -05:00
|
|
|
if (!dbmfi.codectype)
|
2014-08-22 18:02:01 -04:00
|
|
|
{
|
2014-12-28 17:37:12 -05:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Cannot transcode '%s', codec type is unknown\n", dbmfi.fname);
|
|
|
|
|
|
|
|
transcode = 0;
|
2014-08-22 18:02:01 -04:00
|
|
|
}
|
2014-12-28 17:37:12 -05:00
|
|
|
else if (remote)
|
|
|
|
{
|
|
|
|
transcode = 1;
|
|
|
|
}
|
|
|
|
else if (!last_codectype || (strcmp(last_codectype, dbmfi.codectype) != 0))
|
|
|
|
{
|
|
|
|
transcode = transcode_needed(ua, client_codecs, dbmfi.codectype);
|
2014-08-22 18:02:01 -04:00
|
|
|
|
2014-12-28 17:37:12 -05:00
|
|
|
if (last_codectype)
|
|
|
|
free(last_codectype);
|
|
|
|
|
|
|
|
last_codectype = strdup(dbmfi.codectype);
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2013-12-25 17:06:41 -05:00
|
|
|
ret = dmap_encode_file_metadata(songlist, song, &dbmfi, meta, nmeta, sort_headers, transcode);
|
2011-04-02 09:07:26 -04:00
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2011-04-02 09:07:26 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Failed to encode song metadata\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2011-04-02 09:07:26 -04:00
|
|
|
ret = -100;
|
|
|
|
break;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-08-30 10:59:48 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
2011-03-15 16:24:24 -04:00
|
|
|
ret = daap_sort_build(sctx, dbmfi.title_sort);
|
2010-08-30 10:59:48 -04:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add sort header to DAAP song list reply\n");
|
|
|
|
|
|
|
|
ret = -100;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 16:51:13 -04:00
|
|
|
DPRINTF(E_SPAM, L_DAAP, "Done with song\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2009-04-28 16:51:52 -04:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "Done with song list, %d songs\n", nsongs);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2014-12-28 17:37:12 -05:00
|
|
|
if (last_codectype)
|
|
|
|
free(last_codectype);
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
if (nmeta > 0)
|
2009-04-28 16:51:52 -04:00
|
|
|
free(meta);
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
evbuffer_free(song);
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
if (qp.filter)
|
|
|
|
free(qp.filter);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2010-08-29 05:51:00 -04:00
|
|
|
if (ret == -100)
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Error fetching results\n");
|
|
|
|
dmap_send_error(req, tag, "Error fetching query results");
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
db_query_end(&qp);
|
2010-08-30 10:59:48 -04:00
|
|
|
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
2010-01-10 08:34:48 -05:00
|
|
|
goto out_list_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add header to evbuf, add songlist to evbuf */
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(songlist);
|
2010-08-30 10:59:48 -04:00
|
|
|
if (sort_headers)
|
2013-11-10 06:35:24 -05:00
|
|
|
{
|
|
|
|
daap_sort_finalize(sctx);
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, tag, len + evbuffer_get_length(sctx->headerlist) + 61);
|
2013-11-10 06:35:24 -05:00
|
|
|
}
|
2010-08-30 10:59:48 -04:00
|
|
|
else
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, tag, len + 53);
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
|
|
|
dmap_add_char(evbuf, "muty", 0); /* 9 */
|
2009-06-07 12:58:02 -04:00
|
|
|
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_int(evbuf, "mrco", nsongs); /* 12 */
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, "mlcl", len); /* 8 */
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
db_query_end(&qp);
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
ret = evbuffer_add_buffer(evbuf, songlist);
|
|
|
|
evbuffer_free(songlist);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add song list to DAAP song list reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-08-30 10:59:48 -04:00
|
|
|
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-08-30 10:59:48 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(sctx->headerlist);
|
|
|
|
dmap_add_container(evbuf, "mshl", len); /* 8 */
|
2013-11-10 06:35:24 -05:00
|
|
|
ret = evbuffer_add_buffer(evbuf, sctx->headerlist);
|
2010-08-30 10:59:48 -04:00
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add sort headers to DAAP song list reply\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-08-30 10:59:48 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2010-01-10 08:34:48 -05:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return 0;
|
2010-01-10 08:34:48 -05:00
|
|
|
|
|
|
|
out_query_free:
|
|
|
|
if (nmeta > 0)
|
|
|
|
free(meta);
|
|
|
|
|
|
|
|
if (qp.filter)
|
|
|
|
free(qp.filter);
|
|
|
|
|
|
|
|
out_song_free:
|
|
|
|
evbuffer_free(song);
|
|
|
|
|
|
|
|
out_list_free:
|
|
|
|
evbuffer_free(songlist);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_dbsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-08-22 18:02:01 -04:00
|
|
|
return daap_reply_songlist_generic(req, evbuf, -1, query, ua);
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_plsonglist(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
int playlist;
|
|
|
|
int ret;
|
|
|
|
|
2010-02-02 15:02:24 -05:00
|
|
|
ret = safe_atoi32(uri[3], &playlist);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "apso", "Invalid playlist ID");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
return daap_reply_songlist_generic(req, evbuf, playlist, query, ua);
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_playlists(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2009-06-07 12:58:02 -04:00
|
|
|
struct query_params qp;
|
|
|
|
struct db_playlist_info dbpli;
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
2009-04-28 11:48:57 -04:00
|
|
|
struct evbuffer *playlistlist;
|
|
|
|
struct evbuffer *playlist;
|
2015-03-30 15:10:59 -04:00
|
|
|
cfg_t *lib;
|
2011-04-01 13:01:35 -04:00
|
|
|
const struct dmap_field_map *dfm;
|
|
|
|
const struct dmap_field *df;
|
|
|
|
const struct dmap_field **meta;
|
2009-04-28 11:48:57 -04:00
|
|
|
const char *param;
|
|
|
|
char **strval;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2015-03-16 18:33:42 -04:00
|
|
|
int database;
|
2015-03-30 15:10:59 -04:00
|
|
|
int cfg_radiopl;
|
2009-04-28 11:48:57 -04:00
|
|
|
int nmeta;
|
|
|
|
int npls;
|
2013-12-17 17:27:37 -05:00
|
|
|
int32_t plid;
|
|
|
|
int32_t pltype;
|
|
|
|
int32_t plitems;
|
2015-03-16 18:33:42 -04:00
|
|
|
int32_t plstreams;
|
2015-03-14 17:00:57 -04:00
|
|
|
int32_t plparent;
|
2009-04-28 16:51:52 -04:00
|
|
|
int i;
|
2009-04-28 11:48:57 -04:00
|
|
|
int ret;
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
s = daap_session_find(req, query, evbuf);
|
|
|
|
if (!s)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2015-03-16 18:33:42 -04:00
|
|
|
ret = safe_atoi32(uri[1], &database);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
dmap_send_error(req, "aply", "Invalid database ID");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-03-30 15:10:59 -04:00
|
|
|
lib = cfg_getsec(cfg, "library");
|
|
|
|
cfg_radiopl = cfg_getbool(lib, "radio_playlists");
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
ret = evbuffer_expand(evbuf, 61);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP playlists reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
playlistlist = evbuffer_new();
|
|
|
|
if (!playlistlist)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP playlist list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start with a big enough evbuffer - it'll expand as needed */
|
|
|
|
ret = evbuffer_expand(playlistlist, 1024);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP playlist list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
2010-01-10 08:40:03 -05:00
|
|
|
goto out_list_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
playlist = evbuffer_new();
|
|
|
|
if (!playlist)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP playlist block\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
2010-01-10 08:40:03 -05:00
|
|
|
goto out_list_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* The buffer will expand if needed */
|
|
|
|
ret = evbuffer_expand(playlist, 128);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP playlist block\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
2010-01-10 08:40:03 -05:00
|
|
|
goto out_pl_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
param = evhttp_find_header(query, "meta");
|
|
|
|
if (!param)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n");
|
|
|
|
|
|
|
|
param = default_meta_pl;
|
|
|
|
}
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
nmeta = parse_meta(req, "aply", param, &meta);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (nmeta < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n");
|
|
|
|
|
2010-01-10 08:40:03 -05:00
|
|
|
goto out_pl_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
memset(&qp, 0, sizeof(struct query_params));
|
2010-08-14 06:13:41 -04:00
|
|
|
get_query_params(query, NULL, &qp);
|
2009-06-07 12:58:02 -04:00
|
|
|
qp.type = Q_PL;
|
2014-03-11 18:20:29 -04:00
|
|
|
qp.sort = S_PLAYLIST;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
ret = db_query_start(&qp);
|
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2009-06-07 12:58:02 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Could not start query");
|
2010-01-10 08:40:03 -05:00
|
|
|
goto out_query_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
npls = 0;
|
2016-02-21 12:05:11 -05:00
|
|
|
while (((ret = db_query_fetch_pl(&qp, &dbpli, 1)) == 0) && (dbpli.id))
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2013-12-17 17:27:37 -05:00
|
|
|
plid = 1;
|
|
|
|
if (safe_atoi32(dbpli.id, &plid) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pltype = 0;
|
|
|
|
if (safe_atoi32(dbpli.type, &pltype) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
plitems = 0;
|
|
|
|
if (safe_atoi32(dbpli.items, &plitems) != 0)
|
|
|
|
continue;
|
|
|
|
|
2015-03-16 18:33:42 -04:00
|
|
|
plstreams = 0;
|
|
|
|
if (safe_atoi32(dbpli.streams, &plstreams) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Database DAAP_DB_RADIO is radio, so for that db skip playlists without
|
|
|
|
* streams and for other databases skip playlists which are just streams
|
|
|
|
*/
|
|
|
|
if ((database == DAAP_DB_RADIO) && (plstreams == 0))
|
|
|
|
continue;
|
2015-03-30 15:10:59 -04:00
|
|
|
if (!cfg_radiopl && (database != DAAP_DB_RADIO) && (plstreams > 0) && (plstreams == plitems))
|
2015-03-16 18:33:42 -04:00
|
|
|
continue;
|
|
|
|
|
2015-04-11 10:55:13 -04:00
|
|
|
/* Don't add empty Special playlists */
|
2015-04-21 13:11:48 -04:00
|
|
|
if ((plid > 1) && (plitems == 0) && (pltype == PL_SPECIAL))
|
2013-12-17 17:27:37 -05:00
|
|
|
continue;
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
npls++;
|
|
|
|
|
2009-04-28 16:51:52 -04:00
|
|
|
for (i = 0; i < nmeta; i++)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2011-04-01 13:01:35 -04:00
|
|
|
df = meta[i];
|
|
|
|
dfm = df->dfm;
|
2010-02-08 13:28:42 -05:00
|
|
|
|
2009-04-28 16:51:52 -04:00
|
|
|
/* dmap.itemcount - always added */
|
2011-04-01 13:01:35 -04:00
|
|
|
if (dfm == &dfm_dmap_mimc)
|
2009-04-28 11:48:57 -04:00
|
|
|
continue;
|
|
|
|
|
2015-04-11 10:55:13 -04:00
|
|
|
/* Add field "com.apple.itunes.smart-playlist" for special and smart playlists
|
|
|
|
(excluding the special playlist for "library" with id = 1) */
|
2011-04-01 13:01:35 -04:00
|
|
|
if (dfm == &dfm_dmap_aeSP)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2015-04-11 10:55:13 -04:00
|
|
|
if ((pltype == PL_SMART) || ((pltype == PL_SPECIAL) && (plid != 1)))
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2013-12-17 17:27:37 -05:00
|
|
|
dmap_add_char(playlist, "aeSP", 1);
|
2015-04-11 10:55:13 -04:00
|
|
|
}
|
2010-01-21 11:56:44 -05:00
|
|
|
|
2015-04-11 10:55:13 -04:00
|
|
|
/* Add field "com.apple.itunes.special-playlist" for special playlists
|
|
|
|
(excluding the special playlist for "library" with id = 1) */
|
|
|
|
if ((pltype == PL_SPECIAL) && (plid != 1))
|
|
|
|
{
|
|
|
|
int32_t aePS = 0;
|
2013-12-17 17:27:37 -05:00
|
|
|
ret = safe_atoi32(dbpli.special_id, &aePS);
|
|
|
|
if ((ret == 0) && (aePS > 0))
|
|
|
|
dmap_add_char(playlist, "aePS", aePS);
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-04-28 16:51:52 -04:00
|
|
|
/* Not in struct playlist_info */
|
|
|
|
if (dfm->pli_offset < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
continue;
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
strval = (char **) ((char *)&dbpli + dfm->pli_offset);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-04-28 16:51:52 -04:00
|
|
|
if (!(*strval) || (**strval == '\0'))
|
2009-04-28 11:48:57 -04:00
|
|
|
continue;
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
dmap_add_field(playlist, df, *strval, 0);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2013-11-14 17:08:20 -05:00
|
|
|
DPRINTF(E_SPAM, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Item count (mimc) */
|
2015-03-29 17:34:36 -04:00
|
|
|
dmap_add_int(playlist, "mimc", plitems);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-07-31 04:40:39 -04:00
|
|
|
/* Container ID (mpco) */
|
2015-03-14 17:00:57 -04:00
|
|
|
ret = safe_atoi32(dbpli.parent_id, &plparent);
|
|
|
|
if (ret == 0)
|
|
|
|
dmap_add_int(playlist, "mpco", plparent);
|
|
|
|
else
|
|
|
|
dmap_add_int(playlist, "mpco", 0);
|
2010-07-31 04:40:39 -04:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
/* Base playlist (abpl), id = 1 */
|
2013-12-17 17:27:37 -05:00
|
|
|
if (plid == 1)
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_char(playlist, "abpl", 1);
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Done with playlist\n");
|
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(playlist);
|
|
|
|
dmap_add_container(playlistlist, "mlit", len);
|
2009-04-28 11:48:57 -04:00
|
|
|
ret = evbuffer_add_buffer(playlistlist, playlist);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2010-08-29 05:51:00 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add playlist to playlist list for DAAP playlists reply\n");
|
|
|
|
|
|
|
|
ret = -100;
|
2009-04-28 11:48:57 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-30 12:46:41 -05:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "Done with playlist list, %d playlists\n", npls);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
free(meta);
|
|
|
|
evbuffer_free(playlist);
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
if (qp.filter)
|
|
|
|
free(qp.filter);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2010-08-29 05:51:00 -04:00
|
|
|
if (ret == -100)
|
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Error fetching results\n");
|
|
|
|
dmap_send_error(req, "aply", "Error fetching query results");
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
db_query_end(&qp);
|
2010-01-10 08:40:03 -05:00
|
|
|
goto out_list_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add header to evbuf, add playlistlist to evbuf */
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(playlistlist);
|
|
|
|
dmap_add_container(evbuf, "aply", len + 53);
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
|
|
|
dmap_add_char(evbuf, "muty", 0); /* 9 */
|
2009-06-07 12:58:02 -04:00
|
|
|
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_int(evbuf,"mrco", npls); /* 12 */
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, "mlcl", len);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
db_query_end(&qp);
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
ret = evbuffer_add_buffer(evbuf, playlistlist);
|
|
|
|
evbuffer_free(playlistlist);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add playlist list to DAAP playlists reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "aply", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2010-01-10 08:40:03 -05:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return 0;
|
2010-01-10 08:40:03 -05:00
|
|
|
|
|
|
|
out_query_free:
|
|
|
|
free(meta);
|
|
|
|
if (qp.filter)
|
|
|
|
free(qp.filter);
|
|
|
|
|
|
|
|
out_pl_free:
|
|
|
|
evbuffer_free(playlist);
|
|
|
|
|
|
|
|
out_list_free:
|
|
|
|
evbuffer_free(playlistlist);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_groups(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2010-01-10 07:04:57 -05:00
|
|
|
{
|
|
|
|
struct query_params qp;
|
|
|
|
struct db_group_info dbgri;
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
2010-01-10 07:04:57 -05:00
|
|
|
struct evbuffer *group;
|
|
|
|
struct evbuffer *grouplist;
|
2011-04-01 13:01:35 -04:00
|
|
|
const struct dmap_field_map *dfm;
|
|
|
|
const struct dmap_field *df;
|
|
|
|
const struct dmap_field **meta;
|
2010-08-29 05:59:17 -04:00
|
|
|
struct sort_ctx *sctx;
|
2015-12-12 17:28:48 -05:00
|
|
|
cfg_t *lib;
|
2010-01-10 07:04:57 -05:00
|
|
|
const char *param;
|
|
|
|
char **strval;
|
2015-07-29 17:05:47 -04:00
|
|
|
char *tag;
|
|
|
|
size_t len;
|
2010-01-10 07:04:57 -05:00
|
|
|
int nmeta;
|
2010-08-29 05:59:17 -04:00
|
|
|
int sort_headers;
|
2010-01-10 07:04:57 -05:00
|
|
|
int ngrp;
|
2010-02-02 15:02:24 -05:00
|
|
|
int32_t val;
|
2010-01-10 07:04:57 -05:00
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
s = daap_session_find(req, query, evbuf);
|
2014-08-21 17:06:52 -04:00
|
|
|
if (!s && req)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
memset(&qp, 0, sizeof(struct query_params));
|
|
|
|
|
|
|
|
get_query_params(query, &sort_headers, &qp);
|
2014-08-21 17:06:52 -04:00
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
user_agent_filter(ua, &qp);
|
2013-12-03 17:16:07 -05:00
|
|
|
|
2013-08-29 16:00:37 -04:00
|
|
|
param = evhttp_find_header(query, "group-type");
|
|
|
|
if (strcmp(param, "artists") == 0)
|
|
|
|
{
|
|
|
|
tag = "agar";
|
2013-12-03 17:16:07 -05:00
|
|
|
qp.type = Q_GROUP_ARTISTS;
|
2013-12-03 17:35:36 -05:00
|
|
|
qp.sort = S_ARTIST;
|
2013-08-29 16:00:37 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tag = "agal";
|
2013-12-03 17:16:07 -05:00
|
|
|
qp.type = Q_GROUP_ALBUMS;
|
2013-12-03 17:35:36 -05:00
|
|
|
qp.sort = S_ALBUM;
|
2013-08-29 16:00:37 -04:00
|
|
|
}
|
2010-01-10 07:04:57 -05:00
|
|
|
|
|
|
|
ret = evbuffer_expand(evbuf, 61);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP groups reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_qfilter_free;
|
2010-01-10 07:04:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
grouplist = evbuffer_new();
|
|
|
|
if (!grouplist)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP group list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_qfilter_free;
|
2010-01-10 07:04:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start with a big enough evbuffer - it'll expand as needed */
|
|
|
|
ret = evbuffer_expand(grouplist, 1024);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP group list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-01-10 07:04:57 -05:00
|
|
|
goto out_list_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
group = evbuffer_new();
|
|
|
|
if (!group)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP group block\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-01-10 07:04:57 -05:00
|
|
|
goto out_list_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The buffer will expand if needed */
|
|
|
|
ret = evbuffer_expand(group, 128);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP group block\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-01-10 07:04:57 -05:00
|
|
|
goto out_group_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
param = evhttp_find_header(query, "meta");
|
|
|
|
if (!param)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "No meta parameter in query, using default\n");
|
|
|
|
|
|
|
|
param = default_meta_group;
|
|
|
|
}
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
nmeta = parse_meta(req, tag, param, &meta);
|
2010-01-10 07:04:57 -05:00
|
|
|
if (nmeta < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Failed to parse meta parameter in DAAP query\n");
|
|
|
|
|
|
|
|
goto out_group_free;
|
|
|
|
}
|
|
|
|
|
2010-08-29 05:59:17 -04:00
|
|
|
sctx = NULL;
|
|
|
|
if (sort_headers)
|
|
|
|
{
|
|
|
|
sctx = daap_sort_context_new();
|
|
|
|
if (!sctx)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create sort context\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
|
|
|
goto out_query_free;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
ret = db_query_start(&qp);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Could not start query");
|
2010-08-29 05:59:17 -04:00
|
|
|
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
goto out_query_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
ngrp = 0;
|
|
|
|
while ((ret = db_query_fetch_group(&qp, &dbgri)) == 0)
|
|
|
|
{
|
2013-12-12 17:27:50 -05:00
|
|
|
/* Don't add item if no name (eg blank album name) */
|
|
|
|
if (strlen(dbgri.itemname) == 0)
|
|
|
|
continue;
|
|
|
|
|
2015-12-12 17:28:48 -05:00
|
|
|
/* Don't add single item albums/artists if configured to hide */
|
|
|
|
lib = cfg_getsec(cfg, "library");
|
|
|
|
if (cfg_getbool(lib, "hide_singles") && (strcmp(dbgri.itemcount, "1") == 0))
|
|
|
|
continue;
|
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
ngrp++;
|
|
|
|
|
|
|
|
for (i = 0; i < nmeta; i++)
|
|
|
|
{
|
2011-04-01 13:01:35 -04:00
|
|
|
df = meta[i];
|
|
|
|
dfm = df->dfm;
|
2010-01-10 07:04:57 -05:00
|
|
|
|
2010-02-08 13:28:42 -05:00
|
|
|
/* dmap.itemcount - always added */
|
2011-04-01 13:01:35 -04:00
|
|
|
if (dfm == &dfm_dmap_mimc)
|
2010-02-08 13:28:42 -05:00
|
|
|
continue;
|
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
/* Not in struct group_info */
|
|
|
|
if (dfm->gri_offset < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
strval = (char **) ((char *)&dbgri + dfm->gri_offset);
|
|
|
|
|
|
|
|
if (!(*strval) || (**strval == '\0'))
|
|
|
|
continue;
|
|
|
|
|
2011-04-01 13:01:35 -04:00
|
|
|
dmap_add_field(group, df, *strval, 0);
|
2010-01-10 07:04:57 -05:00
|
|
|
|
2013-11-16 16:52:53 -05:00
|
|
|
DPRINTF(E_SPAM, L_DAAP, "Done with meta tag %s (%s)\n", df->desc, *strval);
|
2010-01-10 07:04:57 -05:00
|
|
|
}
|
|
|
|
|
2010-08-29 05:59:17 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
2013-12-18 13:14:39 -05:00
|
|
|
ret = daap_sort_build(sctx, dbgri.itemname_sort);
|
2010-08-29 05:59:17 -04:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add sort header to DAAP groups reply\n");
|
|
|
|
|
|
|
|
ret = -100;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
/* Item count, always added (mimc) */
|
|
|
|
val = 0;
|
2010-02-02 15:02:24 -05:00
|
|
|
ret = safe_atoi32(dbgri.itemcount, &val);
|
2010-01-10 07:04:57 -05:00
|
|
|
if ((ret == 0) && (val > 0))
|
|
|
|
dmap_add_int(group, "mimc", val);
|
|
|
|
|
2013-08-29 16:00:37 -04:00
|
|
|
/* Song album artist (asaa), always added if group-type is albums */
|
2013-12-03 17:16:07 -05:00
|
|
|
if (qp.type == Q_GROUP_ALBUMS)
|
2013-08-29 16:00:37 -04:00
|
|
|
dmap_add_string(group, "asaa", dbgri.songalbumartist);
|
2010-01-10 07:04:57 -05:00
|
|
|
|
|
|
|
/* Item id (miid) */
|
2010-03-06 12:56:30 -05:00
|
|
|
val = 0;
|
|
|
|
ret = safe_atoi32(dbgri.id, &val);
|
|
|
|
if ((ret == 0) && (val > 0))
|
|
|
|
dmap_add_int(group, "miid", val);
|
2010-01-10 07:04:57 -05:00
|
|
|
|
2015-03-05 15:24:27 -05:00
|
|
|
DPRINTF(E_SPAM, L_DAAP, "Done with group\n");
|
2010-01-10 07:04:57 -05:00
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(group);
|
|
|
|
dmap_add_container(grouplist, "mlit", len);
|
2010-01-10 07:04:57 -05:00
|
|
|
ret = evbuffer_add_buffer(grouplist, group);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2010-08-29 05:51:00 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add group to group list for DAAP groups reply\n");
|
|
|
|
|
|
|
|
ret = -100;
|
2010-01-10 07:04:57 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Done with group list, %d groups\n", ngrp);
|
|
|
|
|
|
|
|
free(meta);
|
|
|
|
evbuffer_free(group);
|
|
|
|
|
|
|
|
if (qp.filter)
|
2014-12-16 18:32:21 -05:00
|
|
|
{
|
|
|
|
free(qp.filter);
|
|
|
|
qp.filter = NULL;
|
|
|
|
}
|
2010-01-10 07:04:57 -05:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2010-08-29 05:51:00 -04:00
|
|
|
if (ret == -100)
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Error fetching results\n");
|
|
|
|
dmap_send_error(req, tag, "Error fetching query results");
|
|
|
|
}
|
2010-01-10 07:04:57 -05:00
|
|
|
|
|
|
|
db_query_end(&qp);
|
2010-08-29 05:59:17 -04:00
|
|
|
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
goto out_list_free;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add header to evbuf, add grouplist to evbuf */
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(grouplist);
|
2010-08-29 05:59:17 -04:00
|
|
|
if (sort_headers)
|
2013-11-10 06:35:24 -05:00
|
|
|
{
|
|
|
|
daap_sort_finalize(sctx);
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, tag, len + evbuffer_get_length(sctx->headerlist) + 61);
|
2013-11-10 06:35:24 -05:00
|
|
|
}
|
2010-08-29 05:59:17 -04:00
|
|
|
else
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, tag, len + 53);
|
2010-08-29 05:59:17 -04:00
|
|
|
|
2010-01-10 07:04:57 -05:00
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
|
|
|
dmap_add_char(evbuf, "muty", 0); /* 9 */
|
|
|
|
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
|
|
|
|
dmap_add_int(evbuf,"mrco", ngrp); /* 12 */
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, "mlcl", len); /* 8 */
|
2010-01-10 07:04:57 -05:00
|
|
|
|
|
|
|
db_query_end(&qp);
|
|
|
|
|
|
|
|
ret = evbuffer_add_buffer(evbuf, grouplist);
|
|
|
|
evbuffer_free(grouplist);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add group list to DAAP groups reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-08-29 05:59:17 -04:00
|
|
|
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-10 07:04:57 -05:00
|
|
|
}
|
|
|
|
|
2010-08-29 05:59:17 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(sctx->headerlist);
|
|
|
|
dmap_add_container(evbuf, "mshl", len); /* 8 */
|
2013-11-10 06:35:24 -05:00
|
|
|
ret = evbuffer_add_buffer(evbuf, sctx->headerlist);
|
2010-08-29 05:59:17 -04:00
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
2013-11-10 06:35:24 -05:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add sort headers to DAAP groups reply\n");
|
2010-08-29 05:59:17 -04:00
|
|
|
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-08-29 05:59:17 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2010-01-10 07:04:57 -05:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
return 0;
|
2010-01-10 07:04:57 -05:00
|
|
|
|
|
|
|
out_query_free:
|
|
|
|
free(meta);
|
|
|
|
|
|
|
|
out_group_free:
|
|
|
|
evbuffer_free(group);
|
|
|
|
|
|
|
|
out_list_free:
|
|
|
|
evbuffer_free(grouplist);
|
2013-12-03 17:16:07 -05:00
|
|
|
|
|
|
|
out_qfilter_free:
|
2014-12-16 18:32:21 -05:00
|
|
|
free(qp.filter);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return -1;
|
2010-01-10 07:04:57 -05:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_browse(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2009-06-07 12:58:02 -04:00
|
|
|
struct query_params qp;
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
2009-04-28 11:48:57 -04:00
|
|
|
struct evbuffer *itemlist;
|
2010-08-14 07:58:03 -04:00
|
|
|
struct sort_ctx *sctx;
|
2009-06-07 12:58:02 -04:00
|
|
|
char *browse_item;
|
2010-12-05 10:28:33 -05:00
|
|
|
char *sort_item;
|
2009-04-28 11:48:57 -04:00
|
|
|
char *tag;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2010-08-14 07:58:03 -04:00
|
|
|
int sort_headers;
|
2009-04-28 11:48:57 -04:00
|
|
|
int nitems;
|
|
|
|
int ret;
|
|
|
|
|
2010-01-20 12:24:52 -05:00
|
|
|
s = daap_session_find(req, query, evbuf);
|
2014-08-22 18:02:01 -04:00
|
|
|
if (!s && req)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
memset(&qp, 0, sizeof(struct query_params));
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
get_query_params(query, &sort_headers, &qp);
|
2014-08-22 18:02:01 -04:00
|
|
|
|
|
|
|
user_agent_filter(ua, &qp);
|
2013-12-03 17:16:07 -05:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
if (strcmp(uri[3], "artists") == 0)
|
|
|
|
{
|
|
|
|
tag = "abar";
|
2009-06-07 12:58:02 -04:00
|
|
|
qp.type = Q_BROWSE_ARTISTS;
|
2013-12-03 17:16:07 -05:00
|
|
|
qp.sort = S_ARTIST;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
else if (strcmp(uri[3], "albums") == 0)
|
|
|
|
{
|
|
|
|
tag = "abal";
|
2009-06-07 12:58:02 -04:00
|
|
|
qp.type = Q_BROWSE_ALBUMS;
|
2013-12-03 17:16:07 -05:00
|
|
|
qp.sort = S_ALBUM;
|
|
|
|
}
|
|
|
|
else if (strcmp(uri[3], "genres") == 0)
|
|
|
|
{
|
|
|
|
tag = "abgn";
|
|
|
|
qp.type = Q_BROWSE_GENRES;
|
2015-05-26 15:10:28 -04:00
|
|
|
qp.sort = S_GENRE;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
else if (strcmp(uri[3], "composers") == 0)
|
|
|
|
{
|
|
|
|
tag = "abcp";
|
2009-06-07 12:58:02 -04:00
|
|
|
qp.type = Q_BROWSE_COMPOSERS;
|
2015-05-26 15:10:28 -04:00
|
|
|
qp.sort = S_COMPOSER;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Invalid DAAP browse request type '%s'\n", uri[3]);
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "abro", "Invalid browse type");
|
2014-08-21 04:01:47 -04:00
|
|
|
ret = -1;
|
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_qfilter_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
ret = evbuffer_expand(evbuf, 52);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DAAP browse reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "abro", "Out of memory");
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_qfilter_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
itemlist = evbuffer_new();
|
|
|
|
if (!itemlist)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP browse item list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "abro", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
ret = -1;
|
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_qfilter_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Start with a big enough evbuffer - it'll expand as needed */
|
|
|
|
ret = evbuffer_expand(itemlist, 512);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not expand evbuffer for DMAP browse item list\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "abro", "Out of memory");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_itemlist_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-08-14 07:58:03 -04:00
|
|
|
sctx = NULL;
|
|
|
|
if (sort_headers)
|
|
|
|
{
|
|
|
|
sctx = daap_sort_context_new();
|
|
|
|
if (!sctx)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create sort context\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, "abro", "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
ret = -1;
|
2010-08-14 07:58:03 -04:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_itemlist_free;
|
2010-08-14 07:58:03 -04:00
|
|
|
}
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
ret = db_query_start(&qp);
|
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2009-06-07 12:58:02 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not start query\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, "abro", "Could not start query");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_sort_headers_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
nitems = 0;
|
2010-12-05 10:28:33 -05:00
|
|
|
while (((ret = db_query_fetch_string_sort(&qp, &browse_item, &sort_item)) == 0) && (browse_item))
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
nitems++;
|
|
|
|
|
2010-08-14 07:58:03 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
2010-12-05 10:28:33 -05:00
|
|
|
ret = daap_sort_build(sctx, sort_item);
|
2010-08-14 07:58:03 -04:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add sort header to DAAP browse reply\n");
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
dmap_add_string(itemlist, "mlit", browse_item);
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
if (ret < 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2010-08-14 07:58:03 -04:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Error fetching/building results\n");
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2010-08-14 07:58:03 -04:00
|
|
|
dmap_send_error(req, "abro", "Error fetching/building query results");
|
2009-06-07 12:58:02 -04:00
|
|
|
db_query_end(&qp);
|
2010-08-14 07:58:03 -04:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_sort_headers_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(itemlist);
|
2010-08-14 07:58:03 -04:00
|
|
|
if (sort_headers)
|
2013-11-10 06:35:24 -05:00
|
|
|
{
|
|
|
|
daap_sort_finalize(sctx);
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, "abro", len + evbuffer_get_length(sctx->headerlist) + 52);
|
2013-11-10 06:35:24 -05:00
|
|
|
}
|
2010-08-14 07:58:03 -04:00
|
|
|
else
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, "abro", len + 44);
|
2010-08-14 07:58:03 -04:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_int(evbuf, "mstt", 200); /* 12 */
|
2009-06-07 12:58:02 -04:00
|
|
|
dmap_add_int(evbuf, "mtco", qp.results); /* 12 */
|
2009-04-28 11:48:57 -04:00
|
|
|
dmap_add_int(evbuf, "mrco", nitems); /* 12 */
|
2010-08-14 07:58:03 -04:00
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, tag, len); /* 8 */
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-06-07 12:58:02 -04:00
|
|
|
db_query_end(&qp);
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
ret = evbuffer_add_buffer(evbuf, itemlist);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add item list to DAAP browse reply\n");
|
|
|
|
|
2010-01-29 16:35:42 -05:00
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2010-08-14 07:58:03 -04:00
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
goto out_sort_headers_free;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-08-14 07:58:03 -04:00
|
|
|
if (sort_headers)
|
|
|
|
{
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(sctx->headerlist);
|
|
|
|
dmap_add_container(evbuf, "mshl", len); /* 8 */
|
2013-11-10 06:35:24 -05:00
|
|
|
ret = evbuffer_add_buffer(evbuf, sctx->headerlist);
|
2010-08-14 07:58:03 -04:00
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add sort headers to DAAP browse reply\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, tag, "Out of memory");
|
2013-12-03 17:16:07 -05:00
|
|
|
|
|
|
|
goto out_sort_headers_free;
|
2010-08-14 07:58:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf);
|
2013-12-03 17:16:07 -05:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
ret = 0;
|
|
|
|
|
2013-12-03 17:16:07 -05:00
|
|
|
out_sort_headers_free:
|
|
|
|
if (sort_headers)
|
|
|
|
daap_sort_context_free(sctx);
|
|
|
|
|
|
|
|
out_itemlist_free:
|
|
|
|
evbuffer_free(itemlist);
|
|
|
|
|
|
|
|
out_qfilter_free:
|
|
|
|
if (qp.filter)
|
|
|
|
free(qp.filter);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return ret;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2010-03-07 05:11:09 -05:00
|
|
|
/* NOTE: We only handle artwork at the moment */
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_extra_data(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2010-01-03 12:11:16 -05:00
|
|
|
{
|
2010-03-07 05:11:09 -05:00
|
|
|
char clen[32];
|
2010-01-20 12:24:52 -05:00
|
|
|
struct daap_session *s;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evkeyvalq *headers;
|
2010-03-07 05:11:09 -05:00
|
|
|
const char *param;
|
2011-03-30 13:35:13 -04:00
|
|
|
char *ctype;
|
2015-07-29 17:05:47 -04:00
|
|
|
size_t len;
|
2010-03-07 05:11:09 -05:00
|
|
|
int id;
|
|
|
|
int max_w;
|
|
|
|
int max_h;
|
|
|
|
int ret;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
|
|
|
s = daap_session_find(req, query, evbuf);
|
|
|
|
if (!s)
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-20 12:24:52 -05:00
|
|
|
|
2010-03-07 05:11:09 -05:00
|
|
|
ret = safe_atoi32(uri[3], &id);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-03-07 05:11:09 -05:00
|
|
|
}
|
|
|
|
|
2013-12-25 17:06:41 -05:00
|
|
|
if (evhttp_find_header(query, "mw") && evhttp_find_header(query, "mh"))
|
2010-03-07 05:11:09 -05:00
|
|
|
{
|
2013-12-25 17:06:41 -05:00
|
|
|
param = evhttp_find_header(query, "mw");
|
|
|
|
ret = safe_atoi32(param, &max_w);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not convert mw parameter to integer\n");
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2013-12-25 17:06:41 -05:00
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2013-12-25 17:06:41 -05:00
|
|
|
}
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2013-12-25 17:06:41 -05:00
|
|
|
param = evhttp_find_header(query, "mh");
|
|
|
|
ret = safe_atoi32(param, &max_h);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not convert mh parameter to integer\n");
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2013-12-25 17:06:41 -05:00
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2013-12-25 17:06:41 -05:00
|
|
|
}
|
2010-03-07 05:11:09 -05:00
|
|
|
}
|
2013-12-25 17:06:41 -05:00
|
|
|
else
|
2010-03-07 05:11:09 -05:00
|
|
|
{
|
2013-12-25 17:06:41 -05:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "Request for artwork without mw/mh parameter\n");
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2013-12-25 17:06:41 -05:00
|
|
|
max_w = 0;
|
|
|
|
max_h = 0;
|
2010-03-07 05:11:09 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(uri[2], "groups") == 0)
|
2015-04-09 15:23:20 -04:00
|
|
|
ret = artwork_get_group(evbuf, id, max_w, max_h);
|
2010-03-07 05:11:09 -05:00
|
|
|
else if (strcmp(uri[2], "items") == 0)
|
2015-04-09 15:23:20 -04:00
|
|
|
ret = artwork_get_item(evbuf, id, max_w, max_h);
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
len = evbuffer_get_length(evbuf);
|
|
|
|
|
2011-03-30 13:35:13 -04:00
|
|
|
switch (ret)
|
2010-03-07 05:11:09 -05:00
|
|
|
{
|
2011-03-30 13:35:13 -04:00
|
|
|
case ART_FMT_PNG:
|
|
|
|
ctype = "image/png";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ART_FMT_JPEG:
|
|
|
|
ctype = "image/jpeg";
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2015-07-29 17:05:47 -04:00
|
|
|
if (len > 0)
|
|
|
|
evbuffer_drain(evbuf, len);
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2011-03-30 13:35:13 -04:00
|
|
|
goto no_artwork;
|
2010-03-07 05:11:09 -05:00
|
|
|
}
|
|
|
|
|
2014-05-29 17:22:00 -04:00
|
|
|
headers = evhttp_request_get_output_headers(req);
|
|
|
|
evhttp_remove_header(headers, "Content-Type");
|
|
|
|
evhttp_add_header(headers, "Content-Type", ctype);
|
2015-07-29 17:05:47 -04:00
|
|
|
snprintf(clen, sizeof(clen), "%ld", (long)len);
|
2014-05-29 17:22:00 -04:00
|
|
|
evhttp_add_header(headers, "Content-Length", clen);
|
2010-03-07 05:11:09 -05:00
|
|
|
|
2010-05-03 12:19:41 -04:00
|
|
|
/* No gzip compression for artwork */
|
2010-03-07 05:11:09 -05:00
|
|
|
evhttp_send_reply(req, HTTP_OK, "OK", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
return 0;
|
2010-03-07 05:11:09 -05:00
|
|
|
|
|
|
|
no_artwork:
|
2010-01-03 12:11:16 -05:00
|
|
|
evhttp_send_reply(req, HTTP_NOCONTENT, "No Content", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-01-03 12:11:16 -05:00
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_stream(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
int id;
|
|
|
|
int ret;
|
|
|
|
|
2010-02-02 15:02:24 -05:00
|
|
|
ret = safe_atoi32(uri[3], &id);
|
2009-04-28 11:48:57 -04:00
|
|
|
if (ret < 0)
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
else
|
|
|
|
httpd_stream_file(req, id);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return ret;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
|
2015-02-06 16:14:26 -05:00
|
|
|
static char *
|
|
|
|
uri_relative(char *uri, const char *protocol)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
if (strncmp(uri, protocol, strlen(protocol)) != 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = strchr(uri + strlen(protocol), '/');
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Malformed DAAP Request URI '%s'\n", uri);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-11-13 15:53:47 -05:00
|
|
|
static char *
|
|
|
|
daap_fix_request_uri(struct evhttp_request *req, char *uri)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
|
2009-11-14 05:23:03 -05:00
|
|
|
/* iTunes 9 gives us an absolute request-uri like
|
2009-11-13 15:53:47 -05:00
|
|
|
* daap://10.1.1.20:3689/server-info
|
2015-02-06 16:14:26 -05:00
|
|
|
* iTunes 12.1 gives us an absolute request-uri for streaming like
|
|
|
|
* http://10.1.1.20:3689/databases/1/items/1.mp3
|
2009-11-13 15:53:47 -05:00
|
|
|
*/
|
|
|
|
|
2015-02-06 16:14:26 -05:00
|
|
|
if ( (ret = uri_relative(uri, "daap://")) || (ret = uri_relative(uri, "http://")) )
|
2009-11-13 15:53:47 -05:00
|
|
|
{
|
2015-02-06 16:14:26 -05:00
|
|
|
/* Clear the proxy request flag set by evhttp
|
|
|
|
* due to the request URI being absolute.
|
|
|
|
* It has side-effects on Connection: keep-alive
|
|
|
|
*/
|
|
|
|
req->flags &= ~EVHTTP_PROXY_REQUEST;
|
|
|
|
return ret;
|
2009-11-13 15:53:47 -05:00
|
|
|
}
|
|
|
|
|
2015-02-06 16:14:26 -05:00
|
|
|
return uri;
|
2009-11-13 15:53:47 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-02-08 13:58:50 -05:00
|
|
|
#ifdef DMAP_TEST
|
2011-04-01 13:01:35 -04:00
|
|
|
static const struct dmap_field dmap_TEST = { "test.container", "TEST", NULL, DMAP_TYPE_LIST };
|
|
|
|
static const struct dmap_field dmap_TST1 = { "test.ubyte", "TST1", NULL, DMAP_TYPE_UBYTE };
|
|
|
|
static const struct dmap_field dmap_TST2 = { "test.byte", "TST2", NULL, DMAP_TYPE_BYTE };
|
|
|
|
static const struct dmap_field dmap_TST3 = { "test.ushort", "TST3", NULL, DMAP_TYPE_USHORT };
|
|
|
|
static const struct dmap_field dmap_TST4 = { "test.short", "TST4", NULL, DMAP_TYPE_SHORT };
|
|
|
|
static const struct dmap_field dmap_TST5 = { "test.uint", "TST5", NULL, DMAP_TYPE_UINT };
|
|
|
|
static const struct dmap_field dmap_TST6 = { "test.int", "TST6", NULL, DMAP_TYPE_INT };
|
|
|
|
static const struct dmap_field dmap_TST7 = { "test.ulong", "TST7", NULL, DMAP_TYPE_ULONG };
|
|
|
|
static const struct dmap_field dmap_TST8 = { "test.long", "TST8", NULL, DMAP_TYPE_LONG };
|
|
|
|
static const struct dmap_field dmap_TST9 = { "test.string", "TST9", NULL, DMAP_TYPE_STRING };
|
2010-02-08 13:58:50 -05:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
static int
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_dmap_test(struct evhttp_request *req, struct evbuffer *evbuf, char **uri, struct evkeyvalq *query, const char *ua)
|
2010-02-08 13:58:50 -05:00
|
|
|
{
|
|
|
|
char buf[64];
|
|
|
|
struct evbuffer *test;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
test = evbuffer_new();
|
|
|
|
if (!test)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not create evbuffer for DMAP test\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, dmap_TEST.tag, "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-02-08 13:58:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* UBYTE */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIu8, UINT8_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST1, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* BYTE */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi8, INT8_MIN);
|
|
|
|
dmap_add_field(test, &dmap_TST2, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi8, INT8_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST2, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* USHORT */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIu16, UINT16_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST3, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* SHORT */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi16, INT16_MIN);
|
|
|
|
dmap_add_field(test, &dmap_TST4, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi16, INT16_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST4, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* UINT */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIu32, UINT32_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST5, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* INT */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi32, INT32_MIN);
|
|
|
|
dmap_add_field(test, &dmap_TST6, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi32, INT32_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST6, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* ULONG */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIu64, UINT64_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST7, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
|
|
|
/* LONG */
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi64, INT64_MIN);
|
|
|
|
dmap_add_field(test, &dmap_TST8, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
snprintf(buf, sizeof(buf), "%" PRIi64, INT64_MAX);
|
|
|
|
dmap_add_field(test, &dmap_TST8, buf, 0);
|
|
|
|
dmap_add_field(test, &dmap_TST9, buf, 0);
|
|
|
|
|
2015-07-29 17:05:47 -04:00
|
|
|
dmap_add_container(evbuf, dmap_TEST.tag, evbuffer_get_length(test));
|
2010-02-08 13:58:50 -05:00
|
|
|
|
|
|
|
ret = evbuffer_add_buffer(evbuf, test);
|
|
|
|
evbuffer_free(test);
|
|
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not add test results to DMAP test reply\n");
|
|
|
|
|
|
|
|
dmap_send_error(req, dmap_TEST.tag, "Out of memory");
|
2014-08-21 04:01:47 -04:00
|
|
|
return -1;
|
2010-02-08 13:58:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
evhttp_send_reply(req, HTTP_OK, "OK", evbuf);
|
2014-08-21 04:01:47 -04:00
|
|
|
|
|
|
|
return 0;
|
2010-02-08 13:58:50 -05:00
|
|
|
}
|
|
|
|
#endif /* DMAP_TEST */
|
|
|
|
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
static struct uri_map daap_handlers[] =
|
|
|
|
{
|
|
|
|
|
|
|
|
{
|
|
|
|
.regexp = "^/server-info$",
|
|
|
|
.handler = daap_reply_server_info
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.regexp = "^/content-codes$",
|
|
|
|
.handler = daap_reply_content_codes
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.regexp = "^/login$",
|
|
|
|
.handler = daap_reply_login
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.regexp = "^/logout$",
|
|
|
|
.handler = daap_reply_logout
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.regexp = "^/update$",
|
|
|
|
.handler = daap_reply_update
|
|
|
|
},
|
2009-12-20 08:43:28 -05:00
|
|
|
{
|
|
|
|
.regexp = "^/activity$",
|
|
|
|
.handler = daap_reply_activity
|
|
|
|
},
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
.regexp = "^/databases$",
|
|
|
|
.handler = daap_reply_dblist
|
|
|
|
},
|
|
|
|
{
|
2009-11-01 06:39:11 -05:00
|
|
|
.regexp = "^/databases/[[:digit:]]+/browse/[^/]+$",
|
2009-04-28 11:48:57 -04:00
|
|
|
.handler = daap_reply_browse
|
|
|
|
},
|
|
|
|
{
|
2009-11-01 06:39:11 -05:00
|
|
|
.regexp = "^/databases/[[:digit:]]+/items$",
|
2009-04-28 11:48:57 -04:00
|
|
|
.handler = daap_reply_dbsonglist
|
|
|
|
},
|
|
|
|
{
|
2009-11-01 06:39:11 -05:00
|
|
|
.regexp = "^/databases/[[:digit:]]+/items/[[:digit:]]+[.][^/]+$",
|
2009-04-28 11:48:57 -04:00
|
|
|
.handler = daap_stream
|
|
|
|
},
|
2010-01-03 12:11:16 -05:00
|
|
|
{
|
|
|
|
.regexp = "^/databases/[[:digit:]]+/items/[[:digit:]]+/extra_data/artwork$",
|
|
|
|
.handler = daap_reply_extra_data
|
|
|
|
},
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2009-11-01 06:39:11 -05:00
|
|
|
.regexp = "^/databases/[[:digit:]]+/containers$",
|
2009-04-28 11:48:57 -04:00
|
|
|
.handler = daap_reply_playlists
|
|
|
|
},
|
|
|
|
{
|
2009-11-01 06:39:11 -05:00
|
|
|
.regexp = "^/databases/[[:digit:]]+/containers/[[:digit:]]+/items$",
|
2009-04-28 11:48:57 -04:00
|
|
|
.handler = daap_reply_plsonglist
|
|
|
|
},
|
2010-01-10 07:04:57 -05:00
|
|
|
{
|
|
|
|
.regexp = "^/databases/[[:digit:]]+/groups$",
|
|
|
|
.handler = daap_reply_groups
|
|
|
|
},
|
2010-01-03 12:11:16 -05:00
|
|
|
{
|
|
|
|
.regexp = "^/databases/[[:digit:]]+/groups/[[:digit:]]+/extra_data/artwork$",
|
|
|
|
.handler = daap_reply_extra_data
|
|
|
|
},
|
2010-02-08 13:58:50 -05:00
|
|
|
#ifdef DMAP_TEST
|
|
|
|
{
|
|
|
|
.regexp = "^/dmap-test$",
|
|
|
|
.handler = daap_reply_dmap_test
|
|
|
|
},
|
|
|
|
#endif /* DMAP_TEST */
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
|
|
|
.regexp = NULL,
|
|
|
|
.handler = NULL
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
daap_request(struct evhttp_request *req)
|
|
|
|
{
|
|
|
|
char *full_uri;
|
|
|
|
char *uri;
|
|
|
|
char *ptr;
|
|
|
|
char *uri_parts[7];
|
|
|
|
struct evbuffer *evbuf;
|
|
|
|
struct evkeyvalq query;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evkeyvalq *headers;
|
2014-08-23 19:23:23 -04:00
|
|
|
struct timespec start;
|
|
|
|
struct timespec end;
|
2010-01-30 07:04:17 -05:00
|
|
|
const char *ua;
|
2009-05-01 14:59:32 -04:00
|
|
|
cfg_t *lib;
|
|
|
|
char *libname;
|
|
|
|
char *passwd;
|
2014-08-23 19:23:23 -04:00
|
|
|
int msec;
|
2009-04-28 11:48:57 -04:00
|
|
|
int handler;
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
2009-06-07 13:03:18 -04:00
|
|
|
memset(&query, 0, sizeof(struct evkeyvalq));
|
|
|
|
|
2009-05-02 11:20:33 -04:00
|
|
|
full_uri = httpd_fixup_uri(req);
|
|
|
|
if (!full_uri)
|
|
|
|
{
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
return;
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2009-11-13 15:53:47 -05:00
|
|
|
ptr = daap_fix_request_uri(req, full_uri);
|
|
|
|
if (!ptr)
|
|
|
|
{
|
|
|
|
free(full_uri);
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ptr != full_uri)
|
|
|
|
{
|
|
|
|
uri = strdup(ptr);
|
|
|
|
free(full_uri);
|
|
|
|
|
|
|
|
if (!uri)
|
|
|
|
{
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
full_uri = uri;
|
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
uri = extract_uri(full_uri);
|
2009-05-02 11:20:33 -04:00
|
|
|
if (!uri)
|
|
|
|
{
|
2010-09-08 13:19:17 -04:00
|
|
|
free(full_uri);
|
2009-05-02 11:20:33 -04:00
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
return;
|
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "DAAP request: %s\n", full_uri);
|
|
|
|
|
|
|
|
handler = -1;
|
|
|
|
for (i = 0; daap_handlers[i].handler; i++)
|
|
|
|
{
|
|
|
|
ret = regexec(&daap_handlers[i].preg, uri, 0, NULL, 0);
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
handler = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handler < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Unrecognized DAAP request\n");
|
|
|
|
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
|
|
|
|
free(uri);
|
|
|
|
free(full_uri);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-05-01 14:59:32 -04:00
|
|
|
/* Check authentication */
|
2010-03-19 14:06:47 -04:00
|
|
|
lib = cfg_getsec(cfg, "library");
|
2009-05-01 14:59:32 -04:00
|
|
|
passwd = cfg_getstr(lib, "password");
|
|
|
|
|
|
|
|
/* No authentication for these URIs */
|
|
|
|
if ((strcmp(uri, "/server-info") == 0)
|
|
|
|
|| (strcmp(uri, "/logout") == 0)
|
2016-07-13 13:52:58 -04:00
|
|
|
|| (strcmp(uri, "/content-codes") == 0)
|
2009-05-01 14:59:32 -04:00
|
|
|
|| (strncmp(uri, "/databases/1/items/", strlen("/databases/1/items/")) == 0))
|
|
|
|
passwd = NULL;
|
|
|
|
|
2010-01-30 07:04:17 -05:00
|
|
|
/* Waive HTTP authentication for Remote
|
|
|
|
* Remotes are authentified by their pairing-guid; DAAP queries require a
|
|
|
|
* valid session-id that Remote can only obtain if its pairing-guid is in
|
|
|
|
* our database. So HTTP authentication is waived for Remote.
|
|
|
|
*/
|
2014-05-29 17:22:00 -04:00
|
|
|
headers = evhttp_request_get_input_headers(req);
|
|
|
|
ua = evhttp_find_header(headers, "User-Agent");
|
2010-01-30 07:04:17 -05:00
|
|
|
if ((ua) && (strncmp(ua, "Remote", strlen("Remote")) == 0))
|
|
|
|
passwd = NULL;
|
|
|
|
|
2009-05-01 14:59:32 -04:00
|
|
|
if (passwd)
|
|
|
|
{
|
|
|
|
libname = cfg_getstr(lib, "name");
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_HTTPD, "Checking authentication for library '%s'\n", libname);
|
|
|
|
|
|
|
|
/* We don't care about the username */
|
|
|
|
ret = httpd_basic_auth(req, NULL, passwd, libname);
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
|
|
|
free(uri);
|
|
|
|
free(full_uri);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_HTTPD, "Library authentication successful\n");
|
|
|
|
}
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
memset(uri_parts, 0, sizeof(uri_parts));
|
|
|
|
|
|
|
|
uri_parts[0] = strtok_r(uri, "/", &ptr);
|
|
|
|
for (i = 1; (i < sizeof(uri_parts) / sizeof(uri_parts[0])) && uri_parts[i - 1]; i++)
|
|
|
|
{
|
|
|
|
uri_parts[i] = strtok_r(NULL, "/", &ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!uri_parts[0] || uri_parts[i - 1] || (i < 2))
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "DAAP URI has too many/few components (%d)\n", (uri_parts[0]) ? i : 0);
|
|
|
|
|
|
|
|
evhttp_send_error(req, HTTP_BADREQUEST, "Bad Request");
|
|
|
|
|
|
|
|
free(uri);
|
|
|
|
free(full_uri);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
// Set reply headers
|
|
|
|
headers = evhttp_request_get_output_headers(req);
|
|
|
|
evhttp_add_header(headers, "Accept-Ranges", "bytes");
|
|
|
|
evhttp_add_header(headers, "DAAP-Server", "forked-daapd/" VERSION);
|
|
|
|
/* Content-Type for all replies, even the actual audio streaming. Note that
|
|
|
|
* video streaming will override this Content-Type with a more appropriate
|
|
|
|
* video/<type> Content-Type as expected by clients like Front Row.
|
|
|
|
*/
|
|
|
|
evhttp_add_header(headers, "Content-Type", "application/x-dmap-tagged");
|
|
|
|
|
2014-11-10 16:53:08 -05:00
|
|
|
evbuf = evbuffer_new();
|
|
|
|
if (!evbuf)
|
2014-08-21 04:01:47 -04:00
|
|
|
{
|
2014-11-10 16:53:08 -05:00
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not allocate evbuffer for DAAP reply\n");
|
|
|
|
|
|
|
|
evhttp_send_error(req, HTTP_SERVUNAVAIL, "Internal Server Error");
|
2014-08-22 16:51:13 -04:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
free(uri);
|
|
|
|
free(full_uri);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-10 16:53:08 -05:00
|
|
|
// Try the cache
|
|
|
|
ret = cache_daap_get(full_uri, evbuf);
|
|
|
|
if (ret == 0)
|
2009-04-28 11:48:57 -04:00
|
|
|
{
|
2014-11-10 16:53:08 -05:00
|
|
|
httpd_send_reply(req, HTTP_OK, "OK", evbuf); // TODO not all want this reply
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2014-11-10 16:53:08 -05:00
|
|
|
evbuffer_free(evbuf);
|
2009-04-28 11:48:57 -04:00
|
|
|
free(uri);
|
|
|
|
free(full_uri);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-10 16:53:08 -05:00
|
|
|
// No cache, so prepare handler arguments and send to the handler
|
2009-04-28 11:48:57 -04:00
|
|
|
evhttp_parse_query(full_uri, &query);
|
|
|
|
|
2014-08-23 19:23:23 -04:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &start);
|
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_handlers[handler].handler(req, evbuf, uri_parts, &query, ua);
|
2009-04-28 11:48:57 -04:00
|
|
|
|
2014-08-23 19:23:23 -04:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
|
|
|
|
|
|
|
msec = (end.tv_sec * 1000 + end.tv_nsec / 1000000) - (start.tv_sec * 1000 + start.tv_nsec / 1000000);
|
|
|
|
|
2015-01-06 17:26:29 -05:00
|
|
|
DPRINTF(E_DBG, L_DAAP, "DAAP request handled in %d milliseconds\n", msec);
|
2014-08-23 19:23:23 -04:00
|
|
|
|
2014-11-10 16:53:08 -05:00
|
|
|
if (msec > cache_daap_threshold())
|
|
|
|
cache_daap_add(full_uri, ua, msec);
|
2014-08-23 19:23:23 -04:00
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
evhttp_clear_headers(&query);
|
2014-08-21 04:01:47 -04:00
|
|
|
evbuffer_free(evbuf);
|
2009-04-28 11:48:57 -04:00
|
|
|
free(uri);
|
|
|
|
free(full_uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
daap_is_request(struct evhttp_request *req, char *uri)
|
|
|
|
{
|
2009-11-13 15:53:47 -05:00
|
|
|
uri = daap_fix_request_uri(req, uri);
|
|
|
|
if (!uri)
|
|
|
|
return 0;
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
if (strncmp(uri, "/databases/", strlen("/databases/")) == 0)
|
|
|
|
return 1;
|
|
|
|
if (strcmp(uri, "/databases") == 0)
|
|
|
|
return 1;
|
|
|
|
if (strcmp(uri, "/server-info") == 0)
|
|
|
|
return 1;
|
|
|
|
if (strcmp(uri, "/content-codes") == 0)
|
|
|
|
return 1;
|
|
|
|
if (strcmp(uri, "/login") == 0)
|
|
|
|
return 1;
|
|
|
|
if (strcmp(uri, "/update") == 0)
|
|
|
|
return 1;
|
2009-12-20 08:43:28 -05:00
|
|
|
if (strcmp(uri, "/activity") == 0)
|
|
|
|
return 1;
|
2009-04-28 11:48:57 -04:00
|
|
|
if (strcmp(uri, "/logout") == 0)
|
|
|
|
return 1;
|
|
|
|
|
2010-02-08 13:58:50 -05:00
|
|
|
#ifdef DMAP_TEST
|
|
|
|
if (strcmp(uri, "/dmap-test") == 0)
|
|
|
|
return 1;
|
|
|
|
#endif
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-08-19 18:21:48 -04:00
|
|
|
struct evbuffer *
|
2014-08-22 18:02:01 -04:00
|
|
|
daap_reply_build(char *full_uri, const char *ua)
|
2014-08-19 18:21:48 -04:00
|
|
|
{
|
2014-08-21 04:01:47 -04:00
|
|
|
char *uri;
|
|
|
|
char *ptr;
|
|
|
|
char *uri_parts[7];
|
2014-08-19 18:21:48 -04:00
|
|
|
struct evbuffer *evbuf;
|
2014-08-21 04:01:47 -04:00
|
|
|
struct evkeyvalq query;
|
|
|
|
int handler;
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
DPRINTF(E_DBG, L_DAAP, "Building reply for DAAP request: %s\n", full_uri);
|
|
|
|
|
|
|
|
uri = extract_uri(full_uri);
|
|
|
|
if (!uri)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Error extracting DAAP request: %s\n", full_uri);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
handler = -1;
|
|
|
|
for (i = 0; daap_handlers[i].handler; i++)
|
|
|
|
{
|
|
|
|
ret = regexec(&daap_handlers[i].preg, uri, 0, NULL, 0);
|
|
|
|
if (ret == 0)
|
|
|
|
{
|
|
|
|
handler = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (handler < 0)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Unrecognized DAAP request: %s\n", full_uri);
|
|
|
|
|
|
|
|
free(uri);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(uri_parts, 0, sizeof(uri_parts));
|
|
|
|
|
|
|
|
uri_parts[0] = strtok_r(uri, "/", &ptr);
|
|
|
|
for (i = 1; (i < sizeof(uri_parts) / sizeof(uri_parts[0])) && uri_parts[i - 1]; i++)
|
|
|
|
{
|
|
|
|
uri_parts[i] = strtok_r(NULL, "/", &ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!uri_parts[0] || uri_parts[i - 1] || (i < 2))
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "DAAP URI has too many/few components (%d)\n", (uri_parts[0]) ? i : 0);
|
|
|
|
|
|
|
|
free(uri);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-08-19 18:21:48 -04:00
|
|
|
|
|
|
|
evbuf = evbuffer_new();
|
2014-08-21 04:01:47 -04:00
|
|
|
if (!evbuf)
|
|
|
|
{
|
|
|
|
DPRINTF(E_LOG, L_DAAP, "Could not allocate evbuffer for building DAAP reply\n");
|
|
|
|
|
|
|
|
free(uri);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-08-19 18:21:48 -04:00
|
|
|
|
2014-08-21 04:01:47 -04:00
|
|
|
evhttp_parse_query(full_uri, &query);
|
|
|
|
|
2014-08-22 18:02:01 -04:00
|
|
|
ret = daap_handlers[handler].handler(NULL, evbuf, uri_parts, &query, ua);
|
2014-08-21 04:01:47 -04:00
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
evbuffer_free(evbuf);
|
|
|
|
evbuf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
evhttp_clear_headers(&query);
|
|
|
|
free(uri);
|
2014-08-19 18:21:48 -04:00
|
|
|
|
|
|
|
return evbuf;
|
|
|
|
}
|
|
|
|
|
2009-04-28 11:48:57 -04:00
|
|
|
int
|
|
|
|
daap_init(void)
|
|
|
|
{
|
|
|
|
char buf[64];
|
|
|
|
int i;
|
|
|
|
int ret;
|
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
srand((unsigned)time(NULL));
|
2011-09-10 12:37:16 -04:00
|
|
|
current_rev = 2;
|
2010-01-28 13:20:00 -05:00
|
|
|
update_requests = NULL;
|
2009-04-28 11:48:57 -04:00
|
|
|
|
|
|
|
for (i = 0; daap_handlers[i].handler; i++)
|
|
|
|
{
|
|
|
|
ret = regcomp(&daap_handlers[i].preg, daap_handlers[i].regexp, REG_EXTENDED | REG_NOSUB);
|
|
|
|
if (ret != 0)
|
|
|
|
{
|
|
|
|
regerror(ret, &daap_handlers[i].preg, buf, sizeof(buf));
|
|
|
|
|
|
|
|
DPRINTF(E_FATAL, L_DAAP, "DAAP init failed; regexp error: %s\n", buf);
|
2011-03-31 12:05:41 -04:00
|
|
|
return -1;
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
daap_deinit(void)
|
|
|
|
{
|
2015-02-24 16:58:47 -05:00
|
|
|
struct daap_session *s;
|
2010-01-28 13:20:00 -05:00
|
|
|
struct daap_update_request *ur;
|
2014-05-29 17:22:00 -04:00
|
|
|
struct evhttp_connection *evcon;
|
2009-04-28 11:48:57 -04:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; daap_handlers[i].handler; i++)
|
|
|
|
regfree(&daap_handlers[i].preg);
|
2009-04-28 16:51:52 -04:00
|
|
|
|
2015-02-24 16:58:47 -05:00
|
|
|
for (s = daap_sessions; daap_sessions; s = daap_sessions)
|
|
|
|
{
|
|
|
|
daap_sessions = s->next;
|
|
|
|
daap_session_free(s);
|
|
|
|
}
|
2010-01-28 13:20:00 -05:00
|
|
|
|
|
|
|
for (ur = update_requests; update_requests; ur = update_requests)
|
|
|
|
{
|
|
|
|
update_requests = ur->next;
|
|
|
|
|
2014-05-29 17:22:00 -04:00
|
|
|
evcon = evhttp_request_get_connection(ur->req);
|
|
|
|
if (evcon)
|
2010-07-30 15:42:53 -04:00
|
|
|
{
|
2014-05-29 17:22:00 -04:00
|
|
|
evhttp_connection_set_closecb(evcon, NULL, NULL);
|
|
|
|
evhttp_connection_free(evcon);
|
2010-07-30 15:42:53 -04:00
|
|
|
}
|
2010-07-23 12:15:18 -04:00
|
|
|
|
2011-09-10 12:37:16 -04:00
|
|
|
update_free(ur);
|
2010-01-28 13:20:00 -05:00
|
|
|
}
|
2009-04-28 11:48:57 -04:00
|
|
|
}
|