ushare-/appl/cds.cpp
2016-04-08 19:37:49 +02:00

851 lines
26 KiB
C++

/*
* cds.c : GeeXboX uShare Content Directory Service
* Originally developped for the GeeXboX project.
* Parts of the code are originated from GMediaServer from Oskar Liljeblad.
* Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org>
*
* 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 Library 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <upnp/upnp.h>
#include <upnp/upnptools.h>
#include <string.h>
#include <stdio.h>
#include "ushare.h"
#include "services.h"
#include "ushare.h"
#include "services.h"
#include "metadata.h"
#include "mime.h"
#include "buffer.h"
#include "minmax.h"
/* Represent the CDS GetSearchCapabilities action. */
#define SERVICE_CDS_ACTION_SEARCH_CAPS "GetSearchCapabilities"
/* Represent the CDS GetSortCapabilities action. */
#define SERVICE_CDS_ACTION_SORT_CAPS "GetSortCapabilities"
/* Represent the CDS GetSystemUpdateID action. */
#define SERVICE_CDS_ACTION_UPDATE_ID "GetSystemUpdateID"
/* Represent the CDS Browse action. */
#define SERVICE_CDS_ACTION_BROWSE "Browse"
/* Represent the CDS Search action. */
#define SERVICE_CDS_ACTION_SEARCH "Search"
/* Represent the CDS SearchCaps argument. */
#define SERVICE_CDS_ARG_SEARCH_CAPS "SearchCaps"
/* Represent the CDS SortCaps argument. */
#define SERVICE_CDS_ARG_SORT_CAPS "SortCaps"
/* Represent the CDS UpdateId argument. */
#define SERVICE_CDS_ARG_UPDATE_ID "Id"
/* Represent the CDS StartingIndex argument. */
#define SERVICE_CDS_ARG_START_INDEX "StartingIndex"
/* Represent the CDS RequestedCount argument. */
#define SERVICE_CDS_ARG_REQUEST_COUNT "RequestedCount"
/* Represent the CDS ObjectID argument. */
#define SERVICE_CDS_ARG_OBJECT_ID "ObjectID"
/* Represent the CDS Filter argument. */
#define SERVICE_CDS_ARG_FILTER "Filter"
/* Represent the CDS BrowseFlag argument. */
#define SERVICE_CDS_ARG_BROWSE_FLAG "BrowseFlag"
/* Represent the CDS SortCriteria argument. */
#define SERVICE_CDS_ARG_SORT_CRIT "SortCriteria"
/* Represent the CDS SearchCriteria argument. */
#define SERVICE_CDS_ARG_SEARCH_CRIT "SearchCriteria"
/* Represent the CDS Root Object ID argument. */
#define SERVICE_CDS_ROOT_OBJECT_ID "0"
/* Represent the CDS DIDL Message Metadata Browse flag argument. */
#define SERVICE_CDS_BROWSE_METADATA "BrowseMetadata"
/* Represent the CDS DIDL Message DirectChildren Browse flag argument. */
#define SERVICE_CDS_BROWSE_CHILDREN "BrowseDirectChildren"
/* Represent the CDS DIDL Message Result argument. */
#define SERVICE_CDS_DIDL_RESULT "Result"
/* Represent the CDS DIDL Message NumberReturned argument. */
#define SERVICE_CDS_DIDL_NUM_RETURNED "NumberReturned"
/* Represent the CDS DIDL Message TotalMatches argument. */
#define SERVICE_CDS_DIDL_TOTAL_MATCH "TotalMatches"
/* Represent the CDS DIDL Message UpdateID argument. */
#define SERVICE_CDS_DIDL_UPDATE_ID "UpdateID"
/* DIDL parameters */
/* Represent the CDS DIDL Message Header Namespace. */
#define DIDL_NAMESPACE \
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " \
"xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " \
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\""
/* Represent the CDS DIDL Message Header Tag. */
#define DIDL_LITE "DIDL-Lite"
/* Represent the CDS DIDL Message Item value. */
#define DIDL_ITEM "item"
/* Represent the CDS DIDL Message Item ID value. */
#define DIDL_ITEM_ID "id"
/* Represent the CDS DIDL Message Item Parent ID value. */
#define DIDL_ITEM_PARENT_ID "parentID"
/* Represent the CDS DIDL Message Item Restricted value. */
#define DIDL_ITEM_RESTRICTED "restricted"
/* Represent the CDS DIDL Message Item UPnP Class value. */
#define DIDL_ITEM_CLASS "upnp:class"
/* Represent the CDS DIDL Message Item Title value. */
#define DIDL_ITEM_TITLE "dc:title"
/* Represent the CDS DIDL Message Item Resource value. */
#define DIDL_RES "res"
/* Represent the CDS DIDL Message Item Protocol Info value. */
#define DIDL_RES_INFO "protocolInfo"
/* Represent the CDS DIDL Message Item Resource Size value. */
#define DIDL_RES_SIZE "size"
/* Represent the CDS DIDL Message Container value. */
#define DIDL_CONTAINER "container"
/* Represent the CDS DIDL Message Container ID value. */
#define DIDL_CONTAINER_ID "id"
/* Represent the CDS DIDL Message Container Parent ID value. */
#define DIDL_CONTAINER_PARENT_ID "parentID"
/* Represent the CDS DIDL Message Container number of children value. */
#define DIDL_CONTAINER_CHILDS "childCount"
/* Represent the CDS DIDL Message Container Restricted value. */
#define DIDL_CONTAINER_RESTRICTED "restricted"
/* Represent the CDS DIDL Message Container Searchable value. */
#define DIDL_CONTAINER_SEARCH "searchable"
/* Represent the CDS DIDL Message Container UPnP Class value. */
#define DIDL_CONTAINER_CLASS "upnp:class"
/* Represent the CDS DIDL Message Container Title value. */
#define DIDL_CONTAINER_TITLE "dc:title"
/* Represent the "upnp:class" reserved keyword for Search action */
#define SEARCH_CLASS_MATCH_KEYWORD "(upnp:class = \""
/* Represent the "upnp:class derived from" reserved keyword */
#define SEARCH_CLASS_DERIVED_KEYWORD "(upnp:class derivedfrom \""
/* Represent the "res@protocolInfo contains" reserved keyword */
#define SEARCH_PROTOCOL_CONTAINS_KEYWORD "(res@protocolInfo contains \""
/* Represent the Search default keyword */
#define SEARCH_OBJECT_KEYWORD "object"
/* Represent the Search 'AND' connector keyword */
#define SEARCH_AND ") and ("
static bool filter_has_val(const char *filter, const char *val) {
char *x = NULL, *token = NULL;
char *m_buffer = NULL, *buffer;
int len = strlen (val);
bool ret = false;
if (!strcmp (filter, "*")) {
return true;
}
x = strdup (filter);
if (x) {
m_buffer = (char*) malloc (strlen (x));
if (m_buffer) {
buffer = m_buffer;
token = strtok_r (x, ",", &buffer);
while (token) {
if (*val == '@') {
token = strchr (token, '@');
}
if (token && !strncmp (token, val, len)) {
ret = true;
break;
}
token = strtok_r (NULL, ",", &buffer);
}
free (m_buffer);
}
free (x);
}
return ret;
}
/* UPnP ContentDirectory Service actions */
static bool cds_get_search_capabilities(struct action_event_t *event) {
upnp_add_response(event, SERVICE_CDS_ARG_SEARCH_CAPS, "");
return event->status;
}
static bool cds_get_sort_capabilities(struct action_event_t *event) {
upnp_add_response(event, SERVICE_CDS_ARG_SORT_CAPS, "");
return event->status;
}
static bool cds_get_system_update_id(struct action_event_t *event) {
upnp_add_response(event, SERVICE_CDS_ARG_UPDATE_ID,
SERVICE_CDS_ROOT_OBJECT_ID);
return event->status;
}
static void didl_add_header (struct buffer_t *out) {
buffer_appendf(out, "<%s %s>", DIDL_LITE, DIDL_NAMESPACE);
}
static void didl_add_footer (struct buffer_t *out) {
buffer_appendf(out, "</%s>", DIDL_LITE);
}
static void didl_add_tag(struct buffer_t *out, char *tag, char *value) {
if (value) {
buffer_appendf(out, "<%s>%s</%s>", tag, value, tag);
}
}
static void didl_add_param(struct buffer_t *out, char *param, char *value) {
if (value) {
buffer_appendf (out, " %s=\"%s\"", param, value);
}
}
static void didl_add_value(struct buffer_t *out, char *param, off_t value) {
buffer_appendf (out, " %s=\"%lld\"", param, value);
}
static void didl_add_item (struct buffer_t *out, int item_id,
int parent_id, char *restricted, char *classPointer, char *title,
char *protocol_info, off_t size, char *url, char *filter) {
buffer_appendf (out, "<%s", DIDL_ITEM);
didl_add_value (out, DIDL_ITEM_ID, item_id);
didl_add_value (out, DIDL_ITEM_PARENT_ID, parent_id);
didl_add_param (out, DIDL_ITEM_RESTRICTED, restricted);
buffer_append (out, ">");
didl_add_tag (out, DIDL_ITEM_CLASS, classPointer);
didl_add_tag (out, DIDL_ITEM_TITLE, title);
if (filter_has_val (filter, DIDL_RES))
{
buffer_appendf (out, "<%s", DIDL_RES);
// protocolInfo is required :
didl_add_param (out, DIDL_RES_INFO, protocol_info);
if (filter_has_val (filter, "@"DIDL_RES_SIZE))
didl_add_value (out, DIDL_RES_SIZE, size);
buffer_append (out, ">");
if (url)
{
extern struct ushare_t *ut;
buffer_appendf (out, "http://%s:%d%s/%s",
UpnpGetServerIpAddress (), ut->port, VIRTUAL_DIR, url);
}
buffer_appendf (out, "</%s>", DIDL_RES);
}
buffer_appendf (out, "</%s>", DIDL_ITEM);
}
static void
didl_add_container (struct buffer_t *out, int id, int parent_id,
int child_count, char *restricted, char *searchable,
char *title, char *classPointer)
{
buffer_appendf (out, "<%s", DIDL_CONTAINER);
didl_add_value (out, DIDL_CONTAINER_ID, id);
didl_add_value (out, DIDL_CONTAINER_PARENT_ID, parent_id);
if (child_count >= 0)
didl_add_value (out, DIDL_CONTAINER_CHILDS, child_count);
didl_add_param (out, DIDL_CONTAINER_RESTRICTED, restricted);
didl_add_param (out, DIDL_CONTAINER_SEARCH, searchable);
buffer_append (out, ">");
didl_add_tag (out, DIDL_CONTAINER_CLASS, classPointer);
didl_add_tag (out, DIDL_CONTAINER_TITLE, title);
buffer_appendf (out, "</%s>", DIDL_CONTAINER);
}
static int
cds_browse_metadata (struct action_event_t *event, struct buffer_t *out,
int index, int count, struct upnp_entry_t *entry,
char *filter)
{
int result_count = 0, c = 0;
if (!entry)
return -1;
if (entry->child_count == -1) /* item : file */
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
entry->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, entry->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol (entry->mime_type);
didl_add_header (out);
#ifdef HAVE_DLNA
entry->dlna_profile ?
didl_add_item (out, entry->id, entry->parent
? entry->parent->id : -1, "false",
dlna_profile_upnp_object_item (entry->dlna_profile),
entry->title,
protocol, entry->size,
entry->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, entry->id, entry->parent
? entry->parent->id : -1, "false",
entry->mime_type->mime_class, entry->title,
protocol, entry->size,
entry->url, filter);
didl_add_footer (out);
free (protocol);
for (c = index; c < MIN (index + count, entry->child_count); c++)
result_count++;
}
else /* container : directory */
{
didl_add_header (out);
didl_add_container (out, entry->id, entry->parent
? entry->parent->id : -1, entry->child_count,
"true", "true", entry->title,
entry->mime_type->mime_class);
didl_add_footer (out);
result_count = 1;
}
upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, "1");
upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, "1");
return result_count;
}
static int
cds_browse_directchildren (struct action_event_t *event,
struct buffer_t *out, int index,
int count, struct upnp_entry_t *entry, char *filter)
{
struct upnp_entry_t **childs;
int s, result_count = 0;
char tmp[32];
if (entry->child_count == -1) /* item : file */
return -1;
didl_add_header (out);
/* go to the child pointed out by index */
childs = entry->childs;
for (s = 0; s < index; s++)
if (*childs)
childs++;
/* UPnP CDS compliance : If starting index = 0 and requested count = 0
then all children must be returned */
if (index == 0 && count == 0)
count = entry->child_count;
for (; *childs; childs++)
{
if (count == 0 || result_count < count)
/* only fetch the requested count number or all entries if count = 0 */
{
if ((*childs)->child_count >= 0) /* container */
didl_add_container (out, (*childs)->id, (*childs)->parent ?
(*childs)->parent->id : -1,
(*childs)->child_count, "true", NULL,
(*childs)->title,
(*childs)->mime_type->mime_class);
else /* item */
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, (*childs)->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol ((*childs)->mime_type);
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", (*childs)->mime_type->mime_class,
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter);
free (protocol);
}
result_count++;
}
}
didl_add_footer (out);
upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
sprintf (tmp, "%d", result_count);
upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp);
sprintf (tmp, "%d", entry->child_count);
upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp);
return result_count;
}
static bool
cds_browse (struct action_event_t *event)
{
extern struct ushare_t *ut;
struct upnp_entry_t *entry = NULL;
int result_count = 0, index, count, id, sort_criteria;
char *flag = NULL;
char *filter = NULL;
struct buffer_t *out = NULL;
bool metadata;
if (!event)
return false;
/* Check for status */
if (!event->status)
return false;
/* check if metadatas have been well inited */
if (!ut->init)
return false;
/* Retrieve Browse arguments */
index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX);
count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT);
id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID);
flag = upnp_get_string (event->request, SERVICE_CDS_ARG_BROWSE_FLAG);
filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER);
sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT);
if (!flag || !filter)
return false;
/* Check arguments validity */
if (!strcmp (flag, SERVICE_CDS_BROWSE_METADATA))
{
if (index)
{
free (flag);
return false;
}
metadata = true;
}
else if (!strcmp (flag, SERVICE_CDS_BROWSE_CHILDREN))
metadata = false;
else
{
free (flag);
return false;
}
free (flag);
entry = upnp_get_entry (ut, id);
if (!entry && (id < ut->starting_id))
entry = upnp_get_entry (ut, ut->starting_id);
if (!entry)
{
free (filter);
return false;
}
out = buffer_new ();
if (!out)
{
free (filter);
return false;
}
if (metadata)
result_count =
cds_browse_metadata (event, out, index, count, entry, filter);
else
result_count =
cds_browse_directchildren (event, out, index, count, entry, filter);
free (filter);
if (result_count < 0)
{
buffer_free (out);
return false;
}
buffer_free (out);
upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID,
SERVICE_CDS_ROOT_OBJECT_ID);
return event->status;
}
static bool
matches_search (char *search_criteria, struct upnp_entry_t *entry)
{
char keyword[256] = SEARCH_OBJECT_KEYWORD;
bool derived_from = false, protocol_contains = false, result = false;
char *quote_closed = NULL, *and_clause = NULL;
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
entry->dlna_profile ?
dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, entry->dlna_profile) :
#endif /* HAVE_DLNA */
mime_get_protocol (entry->mime_type);
if (!strncmp (search_criteria, SEARCH_CLASS_MATCH_KEYWORD,
strlen (SEARCH_CLASS_MATCH_KEYWORD)))
{
strncpy (keyword, search_criteria
+ strlen (SEARCH_CLASS_MATCH_KEYWORD), sizeof (keyword));
quote_closed = strchr (keyword, '"');
if (quote_closed)
*quote_closed = '\0';
}
else if (!strncmp (search_criteria, SEARCH_CLASS_DERIVED_KEYWORD,
strlen (SEARCH_CLASS_DERIVED_KEYWORD)))
{
derived_from = true;
strncpy (keyword, search_criteria
+ strlen (SEARCH_CLASS_DERIVED_KEYWORD), sizeof (keyword));
quote_closed = strchr (keyword, '"');
if (quote_closed)
*quote_closed = '\0';
}
else if (!strncmp (search_criteria, SEARCH_PROTOCOL_CONTAINS_KEYWORD,
strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD)))
{
protocol_contains = true;
strncpy (keyword, search_criteria
+ strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD), sizeof (keyword));
quote_closed = strchr (keyword, '"');
if (quote_closed)
*quote_closed = '\0';
}
if (derived_from && entry->mime_type
&& !strncmp (entry->mime_type->mime_class, keyword, strlen (keyword)))
result = true;
else if (protocol_contains && strstr (protocol, keyword))
result = true;
else if (entry->mime_type &&
!strcmp (entry->mime_type->mime_class, keyword))
result = true;
free (protocol);
and_clause = strstr (search_criteria, SEARCH_AND);
if (and_clause)
return (result &&
matches_search (and_clause + strlen (SEARCH_AND) -1, entry));
return true;
}
static int
cds_search_directchildren_recursive (struct buffer_t *out, int count,
struct upnp_entry_t *entry, char *filter,
char *search_criteria)
{
struct upnp_entry_t **childs;
int result_count = 0;
if (entry->child_count == -1) /* item : file */
return -1;
/* go to the first child */
childs = entry->childs;
for (; *childs; childs++)
{
if (count == 0 || result_count < count)
/* only fetch the requested count number or all entries if count = 0 */
{
if ((*childs)->child_count >= 0) /* container */
{
int new_count;
new_count = cds_search_directchildren_recursive
(out, (count == 0) ? 0 : (count - result_count),
(*childs), filter, search_criteria);
result_count += new_count;
}
else /* item */
{
if (matches_search (search_criteria, *childs))
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, (*childs)->dlna_profile):
#endif /* HAVE_DLNA */
mime_get_protocol ((*childs)->mime_type);
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", (*childs)->mime_type->mime_class,
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter);
free (protocol);
result_count++;
}
}
}
}
return result_count;
}
static int
cds_search_directchildren (struct action_event_t *event,
struct buffer_t *out, int index,
int count, struct upnp_entry_t *entry,
char *filter, char *search_criteria)
{
struct upnp_entry_t **childs;
int s, result_count = 0;
char tmp[32];
index = 0;
if (entry->child_count == -1) /* item : file */
return -1;
didl_add_header (out);
/* go to the child pointed out by index */
childs = entry->childs;
for (s = 0; s < index; s++)
if (*childs)
childs++;
/* UPnP CDS compliance : If starting index = 0 and requested count = 0
then all children must be returned */
if (index == 0 && count == 0)
count = entry->child_count;
for (; *childs; childs++)
{
if (count == 0 || result_count < count)
/* only fetch the requested count number or all entries if count = 0 */
{
if ((*childs)->child_count >= 0) /* container */
{
int new_count;
new_count = cds_search_directchildren_recursive
(out, (count == 0) ? 0 : (count - result_count),
(*childs), filter, search_criteria);
result_count += new_count;
}
else /* item */
{
if (matches_search (search_criteria, *childs))
{
#ifdef HAVE_DLNA
extern struct ushare_t *ut;
#endif /* HAVE_DLNA */
char *protocol =
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP,
DLNA_ORG_PLAY_SPEED_NORMAL,
DLNA_ORG_CONVERSION_NONE,
DLNA_ORG_OPERATION_RANGE,
ut->dlna_flags, (*childs)->dlna_profile):
#endif /* HAVE_DLNA */
mime_get_protocol ((*childs)->mime_type);
#ifdef HAVE_DLNA
(*childs)->dlna_profile ?
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", dlna_profile_upnp_object_item ((*childs)->dlna_profile),
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter) :
#endif /* HAVE_DLNA */
didl_add_item (out, (*childs)->id,
(*childs)->parent ? (*childs)->parent->id : -1,
"true", (*childs)->mime_type->mime_class,
(*childs)->title, protocol,
(*childs)->size, (*childs)->url, filter);
free (protocol);
result_count++;
}
}
}
}
didl_add_footer (out);
upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf);
sprintf (tmp, "%d", result_count);
upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp);
sprintf (tmp, "%d", result_count);
upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp);
return result_count;
}
static bool
cds_search (struct action_event_t *event)
{
extern struct ushare_t *ut;
struct upnp_entry_t *entry = NULL;
int result_count = 0, index, count, id, sort_criteria;
char *search_criteria = NULL;
char *filter = NULL;
struct buffer_t *out = NULL;
if (!event)
return false;
/* Check for status */
if (!event->status)
return false;
/* check if metadatas have been well inited */
if (!ut->init)
return false;
/* Retrieve Browse arguments */
index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX);
count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT);
id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID);
search_criteria = upnp_get_string (event->request,
SERVICE_CDS_ARG_SEARCH_CRIT);
filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER);
sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT);
if (!search_criteria || !filter)
return false;
entry = upnp_get_entry (ut, id);
if (!entry && (id < ut->starting_id))
entry = upnp_get_entry (ut, ut->starting_id);
if (!entry)
return false;
out = buffer_new ();
if (!out)
return false;
result_count =
cds_search_directchildren (event, out, index, count, entry,
filter, search_criteria);
if (result_count < 0)
{
buffer_free (out);
return false;
}
buffer_free (out);
upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID,
SERVICE_CDS_ROOT_OBJECT_ID);
free (search_criteria);
free (filter);
return event->status;
}
/* List of UPnP ContentDirectory Service actions */
struct service_action_t cds_service_actions[] = {
{ SERVICE_CDS_ACTION_SEARCH_CAPS, cds_get_search_capabilities },
{ SERVICE_CDS_ACTION_SORT_CAPS, cds_get_sort_capabilities },
{ SERVICE_CDS_ACTION_UPDATE_ID, cds_get_system_update_id },
{ SERVICE_CDS_ACTION_BROWSE, cds_browse },
{ SERVICE_CDS_ACTION_SEARCH, cds_search },
{ NULL, NULL }
};