Mercurial > pt1.oyama
diff src/cds.c @ 125:e413158cae13
Add ushare project files.
author | naoyan@johnstown.minaminoshima.org |
---|---|
date | Sun, 03 Oct 2010 11:35:19 +0900 |
parents | |
children | 2a9ac5ce2c7e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cds.c Sun Oct 03 11:35:19 2010 +0900 @@ -0,0 +1,872 @@ +/* + * 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 "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 *class, 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, class); + 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 *class) +{ + 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, class); + 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 } +};