Mercurial > pt1.oyama
view src/cds.c @ 150:036ae90f1b01
EXPERIMENTAL: Add channel scan script.
require: epgdump, XML::Simple
author | Naoya OYAMA <naoya.oyama@gmail.com> |
---|---|
date | Fri, 31 Aug 2012 05:12:44 +0900 |
parents | 2a9ac5ce2c7e |
children | 2659ebefb192 |
line wrap: on
line source
/* * 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.h> #include <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 } };