Mercurial > pt1.oyama
annotate src/cds.c @ 185:7a0f498af035 default tip
Fix a race condition.
| author | Naoya OYAMA <naoya.oyama@gmail.com> |
|---|---|
| date | Wed, 14 May 2014 22:43:57 +0900 |
| parents | 2659ebefb192 |
| children |
| rev | line source |
|---|---|
| 125 | 1 /* |
| 2 * cds.c : GeeXboX uShare Content Directory Service | |
| 3 * Originally developped for the GeeXboX project. | |
| 4 * Parts of the code are originated from GMediaServer from Oskar Liljeblad. | |
| 5 * Copyright (C) 2005-2007 Benjamin Zores <ben@geexbox.org> | |
| 6 * | |
| 7 * This program is free software; you can redistribute it and/or modify | |
| 8 * it under the terms of the GNU General Public License as published by | |
| 9 * the Free Software Foundation; either version 2 of the License, or | |
| 10 * (at your option) any later version. | |
| 11 * | |
| 12 * This program is distributed in the hope that it will be useful, | |
| 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 15 * GNU Library General Public License for more details. | |
| 16 * | |
| 17 * You should have received a copy of the GNU General Public License along | |
| 18 * with this program; if not, write to the Free Software Foundation, | |
| 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| 20 */ | |
| 21 | |
| 22 #include <stdlib.h> | |
| 180 | 23 #include <string.h> |
|
136
2a9ac5ce2c7e
Remove internal libdlna and libupnp.(using OS package by default)
Naoya OYAMA <naoya.oyama@gmail.com>
parents:
125
diff
changeset
|
24 #include <upnp.h> |
|
2a9ac5ce2c7e
Remove internal libdlna and libupnp.(using OS package by default)
Naoya OYAMA <naoya.oyama@gmail.com>
parents:
125
diff
changeset
|
25 #include <upnptools.h> |
| 125 | 26 |
| 27 #include "ushare.h" | |
| 28 #include "services.h" | |
| 29 #include "ushare.h" | |
| 30 #include "services.h" | |
| 31 #include "metadata.h" | |
| 32 #include "mime.h" | |
| 33 #include "buffer.h" | |
| 34 #include "minmax.h" | |
| 35 | |
| 36 /* Represent the CDS GetSearchCapabilities action. */ | |
| 37 #define SERVICE_CDS_ACTION_SEARCH_CAPS "GetSearchCapabilities" | |
| 38 | |
| 39 /* Represent the CDS GetSortCapabilities action. */ | |
| 40 #define SERVICE_CDS_ACTION_SORT_CAPS "GetSortCapabilities" | |
| 41 | |
| 42 /* Represent the CDS GetSystemUpdateID action. */ | |
| 43 #define SERVICE_CDS_ACTION_UPDATE_ID "GetSystemUpdateID" | |
| 44 | |
| 45 /* Represent the CDS Browse action. */ | |
| 46 #define SERVICE_CDS_ACTION_BROWSE "Browse" | |
| 47 | |
| 48 /* Represent the CDS Search action. */ | |
| 49 #define SERVICE_CDS_ACTION_SEARCH "Search" | |
| 50 | |
| 51 /* Represent the CDS SearchCaps argument. */ | |
| 52 #define SERVICE_CDS_ARG_SEARCH_CAPS "SearchCaps" | |
| 53 | |
| 54 /* Represent the CDS SortCaps argument. */ | |
| 55 #define SERVICE_CDS_ARG_SORT_CAPS "SortCaps" | |
| 56 | |
| 57 /* Represent the CDS UpdateId argument. */ | |
| 58 #define SERVICE_CDS_ARG_UPDATE_ID "Id" | |
| 59 | |
| 60 /* Represent the CDS StartingIndex argument. */ | |
| 61 #define SERVICE_CDS_ARG_START_INDEX "StartingIndex" | |
| 62 | |
| 63 /* Represent the CDS RequestedCount argument. */ | |
| 64 #define SERVICE_CDS_ARG_REQUEST_COUNT "RequestedCount" | |
| 65 | |
| 66 /* Represent the CDS ObjectID argument. */ | |
| 67 #define SERVICE_CDS_ARG_OBJECT_ID "ObjectID" | |
| 68 | |
| 69 /* Represent the CDS Filter argument. */ | |
| 70 #define SERVICE_CDS_ARG_FILTER "Filter" | |
| 71 | |
| 72 /* Represent the CDS BrowseFlag argument. */ | |
| 73 #define SERVICE_CDS_ARG_BROWSE_FLAG "BrowseFlag" | |
| 74 | |
| 75 /* Represent the CDS SortCriteria argument. */ | |
| 76 #define SERVICE_CDS_ARG_SORT_CRIT "SortCriteria" | |
| 77 | |
| 78 /* Represent the CDS SearchCriteria argument. */ | |
| 79 #define SERVICE_CDS_ARG_SEARCH_CRIT "SearchCriteria" | |
| 80 | |
| 81 /* Represent the CDS Root Object ID argument. */ | |
| 82 #define SERVICE_CDS_ROOT_OBJECT_ID "0" | |
| 83 | |
| 84 /* Represent the CDS DIDL Message Metadata Browse flag argument. */ | |
| 85 #define SERVICE_CDS_BROWSE_METADATA "BrowseMetadata" | |
| 86 | |
| 87 /* Represent the CDS DIDL Message DirectChildren Browse flag argument. */ | |
| 88 #define SERVICE_CDS_BROWSE_CHILDREN "BrowseDirectChildren" | |
| 89 | |
| 90 /* Represent the CDS DIDL Message Result argument. */ | |
| 91 #define SERVICE_CDS_DIDL_RESULT "Result" | |
| 92 | |
| 93 /* Represent the CDS DIDL Message NumberReturned argument. */ | |
| 94 #define SERVICE_CDS_DIDL_NUM_RETURNED "NumberReturned" | |
| 95 | |
| 96 /* Represent the CDS DIDL Message TotalMatches argument. */ | |
| 97 #define SERVICE_CDS_DIDL_TOTAL_MATCH "TotalMatches" | |
| 98 | |
| 99 /* Represent the CDS DIDL Message UpdateID argument. */ | |
| 100 #define SERVICE_CDS_DIDL_UPDATE_ID "UpdateID" | |
| 101 | |
| 102 /* DIDL parameters */ | |
| 103 /* Represent the CDS DIDL Message Header Namespace. */ | |
| 104 #define DIDL_NAMESPACE \ | |
| 105 "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " \ | |
| 106 "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " \ | |
| 107 "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"" | |
| 108 | |
| 109 /* Represent the CDS DIDL Message Header Tag. */ | |
| 110 #define DIDL_LITE "DIDL-Lite" | |
| 111 | |
| 112 /* Represent the CDS DIDL Message Item value. */ | |
| 113 #define DIDL_ITEM "item" | |
| 114 | |
| 115 /* Represent the CDS DIDL Message Item ID value. */ | |
| 116 #define DIDL_ITEM_ID "id" | |
| 117 | |
| 118 /* Represent the CDS DIDL Message Item Parent ID value. */ | |
| 119 #define DIDL_ITEM_PARENT_ID "parentID" | |
| 120 | |
| 121 /* Represent the CDS DIDL Message Item Restricted value. */ | |
| 122 #define DIDL_ITEM_RESTRICTED "restricted" | |
| 123 | |
| 124 /* Represent the CDS DIDL Message Item UPnP Class value. */ | |
| 125 #define DIDL_ITEM_CLASS "upnp:class" | |
| 126 | |
| 127 /* Represent the CDS DIDL Message Item Title value. */ | |
| 128 #define DIDL_ITEM_TITLE "dc:title" | |
| 129 | |
| 130 /* Represent the CDS DIDL Message Item Resource value. */ | |
| 131 #define DIDL_RES "res" | |
| 132 | |
| 133 /* Represent the CDS DIDL Message Item Protocol Info value. */ | |
| 134 #define DIDL_RES_INFO "protocolInfo" | |
| 135 | |
| 136 /* Represent the CDS DIDL Message Item Resource Size value. */ | |
| 137 #define DIDL_RES_SIZE "size" | |
| 138 | |
| 139 /* Represent the CDS DIDL Message Container value. */ | |
| 140 #define DIDL_CONTAINER "container" | |
| 141 | |
| 142 /* Represent the CDS DIDL Message Container ID value. */ | |
| 143 #define DIDL_CONTAINER_ID "id" | |
| 144 | |
| 145 /* Represent the CDS DIDL Message Container Parent ID value. */ | |
| 146 #define DIDL_CONTAINER_PARENT_ID "parentID" | |
| 147 | |
| 148 /* Represent the CDS DIDL Message Container number of children value. */ | |
| 149 #define DIDL_CONTAINER_CHILDS "childCount" | |
| 150 | |
| 151 /* Represent the CDS DIDL Message Container Restricted value. */ | |
| 152 #define DIDL_CONTAINER_RESTRICTED "restricted" | |
| 153 | |
| 154 /* Represent the CDS DIDL Message Container Searchable value. */ | |
| 155 #define DIDL_CONTAINER_SEARCH "searchable" | |
| 156 | |
| 157 /* Represent the CDS DIDL Message Container UPnP Class value. */ | |
| 158 #define DIDL_CONTAINER_CLASS "upnp:class" | |
| 159 | |
| 160 /* Represent the CDS DIDL Message Container Title value. */ | |
| 161 #define DIDL_CONTAINER_TITLE "dc:title" | |
| 162 | |
| 163 /* Represent the "upnp:class" reserved keyword for Search action */ | |
| 164 #define SEARCH_CLASS_MATCH_KEYWORD "(upnp:class = \"" | |
| 165 | |
| 166 /* Represent the "upnp:class derived from" reserved keyword */ | |
| 167 #define SEARCH_CLASS_DERIVED_KEYWORD "(upnp:class derivedfrom \"" | |
| 168 | |
| 169 /* Represent the "res@protocolInfo contains" reserved keyword */ | |
| 170 #define SEARCH_PROTOCOL_CONTAINS_KEYWORD "(res@protocolInfo contains \"" | |
| 171 | |
| 172 /* Represent the Search default keyword */ | |
| 173 #define SEARCH_OBJECT_KEYWORD "object" | |
| 174 | |
| 175 /* Represent the Search 'AND' connector keyword */ | |
| 176 #define SEARCH_AND ") and (" | |
| 177 | |
| 178 static bool | |
| 179 filter_has_val (const char *filter, const char *val) | |
| 180 { | |
| 181 char *x = NULL, *token = NULL; | |
| 182 char *m_buffer = NULL, *buffer; | |
| 183 int len = strlen (val); | |
| 184 bool ret = false; | |
| 185 | |
| 186 if (!strcmp (filter, "*")) | |
| 187 return true; | |
| 188 | |
| 189 x = strdup (filter); | |
| 190 if (x) | |
| 191 { | |
| 192 m_buffer = (char*) malloc (strlen (x)); | |
| 193 if (m_buffer) | |
| 194 { | |
| 195 buffer = m_buffer; | |
| 196 token = strtok_r (x, ",", &buffer); | |
| 197 while (token) | |
| 198 { | |
| 199 if (*val == '@') | |
| 200 token = strchr (token, '@'); | |
| 201 if (token && !strncmp (token, val, len)) | |
| 202 { | |
| 203 ret = true; | |
| 204 break; | |
| 205 } | |
| 206 token = strtok_r (NULL, ",", &buffer); | |
| 207 } | |
| 208 free (m_buffer); | |
| 209 } | |
| 210 free (x); | |
| 211 } | |
| 212 return ret; | |
| 213 } | |
| 214 | |
| 215 /* UPnP ContentDirectory Service actions */ | |
| 216 static bool | |
| 217 cds_get_search_capabilities (struct action_event_t *event) | |
| 218 { | |
| 219 upnp_add_response (event, SERVICE_CDS_ARG_SEARCH_CAPS, ""); | |
| 220 | |
| 221 return event->status; | |
| 222 } | |
| 223 | |
| 224 static bool | |
| 225 cds_get_sort_capabilities (struct action_event_t *event) | |
| 226 { | |
| 227 upnp_add_response (event, SERVICE_CDS_ARG_SORT_CAPS, ""); | |
| 228 | |
| 229 return event->status; | |
| 230 } | |
| 231 | |
| 232 static bool | |
| 233 cds_get_system_update_id (struct action_event_t *event) | |
| 234 { | |
| 235 upnp_add_response (event, SERVICE_CDS_ARG_UPDATE_ID, | |
| 236 SERVICE_CDS_ROOT_OBJECT_ID); | |
| 237 | |
| 238 return event->status; | |
| 239 } | |
| 240 | |
| 241 static void | |
| 242 didl_add_header (struct buffer_t *out) | |
| 243 { | |
| 244 buffer_appendf (out, "<%s %s>", DIDL_LITE, DIDL_NAMESPACE); | |
| 245 } | |
| 246 | |
| 247 static void | |
| 248 didl_add_footer (struct buffer_t *out) | |
| 249 { | |
| 250 buffer_appendf (out, "</%s>", DIDL_LITE); | |
| 251 } | |
| 252 | |
| 253 static void | |
| 254 didl_add_tag (struct buffer_t *out, char *tag, char *value) | |
| 255 { | |
| 256 if (value) | |
| 257 buffer_appendf (out, "<%s>%s</%s>", tag, value, tag); | |
| 258 } | |
| 259 | |
| 260 static void | |
| 261 didl_add_param (struct buffer_t *out, char *param, char *value) | |
| 262 { | |
| 263 if (value) | |
| 264 buffer_appendf (out, " %s=\"%s\"", param, value); | |
| 265 } | |
| 266 | |
| 267 static void | |
| 268 didl_add_value (struct buffer_t *out, char *param, off_t value) | |
| 269 { | |
| 270 buffer_appendf (out, " %s=\"%lld\"", param, value); | |
| 271 } | |
| 272 | |
| 273 static void | |
| 274 didl_add_item (struct buffer_t *out, int item_id, | |
| 275 int parent_id, char *restricted, char *class, char *title, | |
| 276 char *protocol_info, off_t size, char *url, char *filter) | |
| 277 { | |
| 278 buffer_appendf (out, "<%s", DIDL_ITEM); | |
| 279 didl_add_value (out, DIDL_ITEM_ID, item_id); | |
| 280 didl_add_value (out, DIDL_ITEM_PARENT_ID, parent_id); | |
| 281 didl_add_param (out, DIDL_ITEM_RESTRICTED, restricted); | |
| 282 buffer_append (out, ">"); | |
| 283 | |
| 284 didl_add_tag (out, DIDL_ITEM_CLASS, class); | |
| 285 didl_add_tag (out, DIDL_ITEM_TITLE, title); | |
| 286 | |
| 287 if (filter_has_val (filter, DIDL_RES)) | |
| 288 { | |
| 289 buffer_appendf (out, "<%s", DIDL_RES); | |
| 290 // protocolInfo is required : | |
| 291 didl_add_param (out, DIDL_RES_INFO, protocol_info); | |
| 292 if (filter_has_val (filter, "@"DIDL_RES_SIZE)) | |
| 293 didl_add_value (out, DIDL_RES_SIZE, size); | |
| 294 buffer_append (out, ">"); | |
| 295 if (url) | |
| 296 { | |
| 297 extern struct ushare_t *ut; | |
| 298 buffer_appendf (out, "http://%s:%d%s/%s", | |
| 299 UpnpGetServerIpAddress (), ut->port, VIRTUAL_DIR, url); | |
| 300 } | |
| 301 buffer_appendf (out, "</%s>", DIDL_RES); | |
| 302 } | |
| 303 buffer_appendf (out, "</%s>", DIDL_ITEM); | |
| 304 } | |
| 305 | |
| 306 static void | |
| 307 didl_add_container (struct buffer_t *out, int id, int parent_id, | |
| 308 int child_count, char *restricted, char *searchable, | |
| 309 char *title, char *class) | |
| 310 { | |
| 311 buffer_appendf (out, "<%s", DIDL_CONTAINER); | |
| 312 | |
| 313 didl_add_value (out, DIDL_CONTAINER_ID, id); | |
| 314 didl_add_value (out, DIDL_CONTAINER_PARENT_ID, parent_id); | |
| 315 if (child_count >= 0) | |
| 316 didl_add_value (out, DIDL_CONTAINER_CHILDS, child_count); | |
| 317 didl_add_param (out, DIDL_CONTAINER_RESTRICTED, restricted); | |
| 318 didl_add_param (out, DIDL_CONTAINER_SEARCH, searchable); | |
| 319 buffer_append (out, ">"); | |
| 320 | |
| 321 didl_add_tag (out, DIDL_CONTAINER_CLASS, class); | |
| 322 didl_add_tag (out, DIDL_CONTAINER_TITLE, title); | |
| 323 | |
| 324 buffer_appendf (out, "</%s>", DIDL_CONTAINER); | |
| 325 } | |
| 326 | |
| 327 static int | |
| 328 cds_browse_metadata (struct action_event_t *event, struct buffer_t *out, | |
| 329 int index, int count, struct upnp_entry_t *entry, | |
| 330 char *filter) | |
| 331 { | |
| 332 int result_count = 0, c = 0; | |
| 333 | |
| 334 if (!entry) | |
| 335 return -1; | |
| 336 | |
| 337 if (entry->child_count == -1) /* item : file */ | |
| 338 { | |
| 339 #ifdef HAVE_DLNA | |
| 340 extern struct ushare_t *ut; | |
| 341 #endif /* HAVE_DLNA */ | |
| 342 | |
| 343 char *protocol = | |
| 344 #ifdef HAVE_DLNA | |
| 345 entry->dlna_profile ? | |
| 346 dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP, | |
| 347 DLNA_ORG_PLAY_SPEED_NORMAL, | |
| 348 DLNA_ORG_CONVERSION_NONE, | |
| 349 DLNA_ORG_OPERATION_RANGE, | |
| 350 ut->dlna_flags, entry->dlna_profile) : | |
| 351 #endif /* HAVE_DLNA */ | |
| 352 mime_get_protocol (entry->mime_type); | |
| 353 | |
| 354 didl_add_header (out); | |
| 355 #ifdef HAVE_DLNA | |
| 356 entry->dlna_profile ? | |
| 357 didl_add_item (out, entry->id, entry->parent | |
| 358 ? entry->parent->id : -1, "false", | |
| 359 dlna_profile_upnp_object_item (entry->dlna_profile), | |
| 360 entry->title, | |
| 361 protocol, entry->size, | |
| 362 entry->url, filter) : | |
| 363 #endif /* HAVE_DLNA */ | |
| 364 didl_add_item (out, entry->id, entry->parent | |
| 365 ? entry->parent->id : -1, "false", | |
| 366 entry->mime_type->mime_class, entry->title, | |
| 367 protocol, entry->size, | |
| 368 entry->url, filter); | |
| 369 | |
| 370 didl_add_footer (out); | |
| 371 free (protocol); | |
| 372 | |
| 373 for (c = index; c < MIN (index + count, entry->child_count); c++) | |
| 374 result_count++; | |
| 375 } | |
| 376 else /* container : directory */ | |
| 377 { | |
| 378 didl_add_header (out); | |
| 379 didl_add_container (out, entry->id, entry->parent | |
| 380 ? entry->parent->id : -1, entry->child_count, | |
| 381 "true", "true", entry->title, | |
| 382 entry->mime_type->mime_class); | |
| 383 didl_add_footer (out); | |
| 384 | |
| 385 result_count = 1; | |
| 386 } | |
| 387 | |
| 388 upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf); | |
| 389 upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, "1"); | |
| 390 upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, "1"); | |
| 391 | |
| 392 return result_count; | |
| 393 } | |
| 394 | |
| 395 static int | |
| 396 cds_browse_directchildren (struct action_event_t *event, | |
| 397 struct buffer_t *out, int index, | |
| 398 int count, struct upnp_entry_t *entry, char *filter) | |
| 399 { | |
| 400 struct upnp_entry_t **childs; | |
| 401 int s, result_count = 0; | |
| 402 char tmp[32]; | |
| 403 | |
| 404 if (entry->child_count == -1) /* item : file */ | |
| 405 return -1; | |
| 406 | |
| 407 didl_add_header (out); | |
| 408 | |
| 409 /* go to the child pointed out by index */ | |
| 410 childs = entry->childs; | |
| 411 for (s = 0; s < index; s++) | |
| 412 if (*childs) | |
| 413 childs++; | |
| 414 | |
| 415 /* UPnP CDS compliance : If starting index = 0 and requested count = 0 | |
| 416 then all children must be returned */ | |
| 417 if (index == 0 && count == 0) | |
| 418 count = entry->child_count; | |
| 419 | |
| 420 for (; *childs; childs++) | |
| 421 { | |
| 422 if (count == 0 || result_count < count) | |
| 423 /* only fetch the requested count number or all entries if count = 0 */ | |
| 424 { | |
| 425 if ((*childs)->child_count >= 0) /* container */ | |
| 426 didl_add_container (out, (*childs)->id, (*childs)->parent ? | |
| 427 (*childs)->parent->id : -1, | |
| 428 (*childs)->child_count, "true", NULL, | |
| 429 (*childs)->title, | |
| 430 (*childs)->mime_type->mime_class); | |
| 431 else /* item */ | |
| 432 { | |
| 433 #ifdef HAVE_DLNA | |
| 434 extern struct ushare_t *ut; | |
| 435 #endif /* HAVE_DLNA */ | |
| 436 | |
| 437 char *protocol = | |
| 438 #ifdef HAVE_DLNA | |
| 439 (*childs)->dlna_profile ? | |
| 440 dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP, | |
| 441 DLNA_ORG_PLAY_SPEED_NORMAL, | |
| 442 DLNA_ORG_CONVERSION_NONE, | |
| 443 DLNA_ORG_OPERATION_RANGE, | |
| 444 ut->dlna_flags, (*childs)->dlna_profile) : | |
| 445 #endif /* HAVE_DLNA */ | |
| 446 mime_get_protocol ((*childs)->mime_type); | |
| 447 | |
| 448 #ifdef HAVE_DLNA | |
| 449 (*childs)->dlna_profile ? | |
| 450 didl_add_item (out, (*childs)->id, | |
| 451 (*childs)->parent ? (*childs)->parent->id : -1, | |
| 452 "true", dlna_profile_upnp_object_item ((*childs)->dlna_profile), | |
| 453 (*childs)->title, protocol, | |
| 454 (*childs)->size, (*childs)->url, filter) : | |
| 455 #endif /* HAVE_DLNA */ | |
| 456 didl_add_item (out, (*childs)->id, | |
| 457 (*childs)->parent ? (*childs)->parent->id : -1, | |
| 458 "true", (*childs)->mime_type->mime_class, | |
| 459 (*childs)->title, protocol, | |
| 460 (*childs)->size, (*childs)->url, filter); | |
| 461 | |
| 462 free (protocol); | |
| 463 } | |
| 464 result_count++; | |
| 465 } | |
| 466 } | |
| 467 | |
| 468 didl_add_footer (out); | |
| 469 | |
| 470 upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf); | |
| 471 sprintf (tmp, "%d", result_count); | |
| 472 upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp); | |
| 473 sprintf (tmp, "%d", entry->child_count); | |
| 474 upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp); | |
| 475 | |
| 476 return result_count; | |
| 477 } | |
| 478 | |
| 479 static bool | |
| 480 cds_browse (struct action_event_t *event) | |
| 481 { | |
| 482 extern struct ushare_t *ut; | |
| 483 struct upnp_entry_t *entry = NULL; | |
| 484 int result_count = 0, index, count, id, sort_criteria; | |
| 485 char *flag = NULL; | |
| 486 char *filter = NULL; | |
| 487 struct buffer_t *out = NULL; | |
| 488 bool metadata; | |
| 489 | |
| 490 if (!event) | |
| 491 return false; | |
| 492 | |
| 493 /* Check for status */ | |
| 494 if (!event->status) | |
| 495 return false; | |
| 496 | |
| 497 /* check if metadatas have been well inited */ | |
| 498 if (!ut->init) | |
| 499 return false; | |
| 500 | |
| 501 /* Retrieve Browse arguments */ | |
| 502 index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX); | |
| 503 count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT); | |
| 504 id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID); | |
| 505 flag = upnp_get_string (event->request, SERVICE_CDS_ARG_BROWSE_FLAG); | |
| 506 filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER); | |
| 507 sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT); | |
| 508 | |
| 509 if (!flag || !filter) | |
| 510 return false; | |
| 511 | |
| 512 /* Check arguments validity */ | |
| 513 if (!strcmp (flag, SERVICE_CDS_BROWSE_METADATA)) | |
| 514 { | |
| 515 if (index) | |
| 516 { | |
| 517 free (flag); | |
| 518 return false; | |
| 519 } | |
| 520 metadata = true; | |
| 521 } | |
| 522 else if (!strcmp (flag, SERVICE_CDS_BROWSE_CHILDREN)) | |
| 523 metadata = false; | |
| 524 else | |
| 525 { | |
| 526 free (flag); | |
| 527 return false; | |
| 528 } | |
| 529 free (flag); | |
| 530 | |
| 531 entry = upnp_get_entry (ut, id); | |
| 532 if (!entry && (id < ut->starting_id)) | |
| 533 entry = upnp_get_entry (ut, ut->starting_id); | |
| 534 | |
| 535 if (!entry) | |
| 536 { | |
| 537 free (filter); | |
| 538 return false; | |
| 539 } | |
| 540 | |
| 541 out = buffer_new (); | |
| 542 if (!out) | |
| 543 { | |
| 544 free (filter); | |
| 545 return false; | |
| 546 } | |
| 547 | |
| 548 if (metadata) | |
| 549 result_count = | |
| 550 cds_browse_metadata (event, out, index, count, entry, filter); | |
| 551 else | |
| 552 result_count = | |
| 553 cds_browse_directchildren (event, out, index, count, entry, filter); | |
| 554 free (filter); | |
| 555 | |
| 556 if (result_count < 0) | |
| 557 { | |
| 558 buffer_free (out); | |
| 559 return false; | |
| 560 } | |
| 561 | |
| 562 buffer_free (out); | |
| 563 upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID, | |
| 564 SERVICE_CDS_ROOT_OBJECT_ID); | |
| 565 | |
| 566 return event->status; | |
| 567 } | |
| 568 | |
| 569 static bool | |
| 570 matches_search (char *search_criteria, struct upnp_entry_t *entry) | |
| 571 { | |
| 572 char keyword[256] = SEARCH_OBJECT_KEYWORD; | |
| 573 bool derived_from = false, protocol_contains = false, result = false; | |
| 574 char *quote_closed = NULL, *and_clause = NULL; | |
| 575 #ifdef HAVE_DLNA | |
| 576 extern struct ushare_t *ut; | |
| 577 #endif /* HAVE_DLNA */ | |
| 578 char *protocol = | |
| 579 #ifdef HAVE_DLNA | |
| 580 entry->dlna_profile ? | |
| 581 dlna_write_protocol_info (DLNA_PROTOCOL_INFO_TYPE_HTTP, | |
| 582 DLNA_ORG_PLAY_SPEED_NORMAL, | |
| 583 DLNA_ORG_CONVERSION_NONE, | |
| 584 DLNA_ORG_OPERATION_RANGE, | |
| 585 ut->dlna_flags, entry->dlna_profile) : | |
| 586 #endif /* HAVE_DLNA */ | |
| 587 mime_get_protocol (entry->mime_type); | |
| 588 | |
| 589 if (!strncmp (search_criteria, SEARCH_CLASS_MATCH_KEYWORD, | |
| 590 strlen (SEARCH_CLASS_MATCH_KEYWORD))) | |
| 591 { | |
| 592 strncpy (keyword, search_criteria | |
| 593 + strlen (SEARCH_CLASS_MATCH_KEYWORD), sizeof (keyword)); | |
| 594 quote_closed = strchr (keyword, '"'); | |
| 595 | |
| 596 if (quote_closed) | |
| 597 *quote_closed = '\0'; | |
| 598 } | |
| 599 else if (!strncmp (search_criteria, SEARCH_CLASS_DERIVED_KEYWORD, | |
| 600 strlen (SEARCH_CLASS_DERIVED_KEYWORD))) | |
| 601 { | |
| 602 derived_from = true; | |
| 603 strncpy (keyword, search_criteria | |
| 604 + strlen (SEARCH_CLASS_DERIVED_KEYWORD), sizeof (keyword)); | |
| 605 quote_closed = strchr (keyword, '"'); | |
| 606 | |
| 607 if (quote_closed) | |
| 608 *quote_closed = '\0'; | |
| 609 } | |
| 610 else if (!strncmp (search_criteria, SEARCH_PROTOCOL_CONTAINS_KEYWORD, | |
| 611 strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD))) | |
| 612 { | |
| 613 protocol_contains = true; | |
| 614 strncpy (keyword, search_criteria | |
| 615 + strlen (SEARCH_PROTOCOL_CONTAINS_KEYWORD), sizeof (keyword)); | |
| 616 quote_closed = strchr (keyword, '"'); | |
| 617 | |
| 618 if (quote_closed) | |
| 619 *quote_closed = '\0'; | |
| 620 } | |
| 621 | |
| 622 if (derived_from && entry->mime_type | |
| 623 && !strncmp (entry->mime_type->mime_class, keyword, strlen (keyword))) | |
| 624 result = true; | |
| 625 else if (protocol_contains && strstr (protocol, keyword)) | |
| 626 result = true; | |
| 627 else if (entry->mime_type && | |
| 628 !strcmp (entry->mime_type->mime_class, keyword)) | |
| 629 result = true; | |
| 630 free (protocol); | |
| 631 | |
| 632 and_clause = strstr (search_criteria, SEARCH_AND); | |
| 633 if (and_clause) | |
| 634 return (result && | |
| 635 matches_search (and_clause + strlen (SEARCH_AND) -1, entry)); | |
| 636 | |
| 637 return true; | |
| 638 } | |
| 639 | |
| 640 static int | |
| 641 cds_search_directchildren_recursive (struct buffer_t *out, int count, | |
| 642 struct upnp_entry_t *entry, char *filter, | |
| 643 char *search_criteria) | |
| 644 { | |
| 645 struct upnp_entry_t **childs; | |
| 646 int result_count = 0; | |
| 647 | |
| 648 if (entry->child_count == -1) /* item : file */ | |
| 649 return -1; | |
| 650 | |
| 651 /* go to the first child */ | |
| 652 childs = entry->childs; | |
| 653 | |
| 654 for (; *childs; childs++) | |
| 655 { | |
| 656 if (count == 0 || result_count < count) | |
| 657 /* only fetch the requested count number or all entries if count = 0 */ | |
| 658 { | |
| 659 if ((*childs)->child_count >= 0) /* container */ | |
| 660 { | |
| 661 int new_count; | |
| 662 new_count = cds_search_directchildren_recursive | |
| 663 (out, (count == 0) ? 0 : (count - result_count), | |
| 664 (*childs), filter, search_criteria); | |
| 665 result_count += new_count; | |
| 666 } | |
| 667 else /* item */ | |
| 668 { | |
| 669 if (matches_search (search_criteria, *childs)) | |
| 670 { | |
| 671 #ifdef HAVE_DLNA | |
| 672 extern struct ushare_t *ut; | |
| 673 #endif /* HAVE_DLNA */ | |
| 674 char *protocol = | |
| 675 #ifdef HAVE_DLNA | |
| 676 (*childs)->dlna_profile ? | |
| 677 dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP, | |
| 678 DLNA_ORG_PLAY_SPEED_NORMAL, | |
| 679 DLNA_ORG_CONVERSION_NONE, | |
| 680 DLNA_ORG_OPERATION_RANGE, | |
| 681 ut->dlna_flags, (*childs)->dlna_profile): | |
| 682 #endif /* HAVE_DLNA */ | |
| 683 mime_get_protocol ((*childs)->mime_type); | |
| 684 | |
| 685 #ifdef HAVE_DLNA | |
| 686 (*childs)->dlna_profile ? | |
| 687 didl_add_item (out, (*childs)->id, | |
| 688 (*childs)->parent ? (*childs)->parent->id : -1, | |
| 689 "true", dlna_profile_upnp_object_item ((*childs)->dlna_profile), | |
| 690 (*childs)->title, protocol, | |
| 691 (*childs)->size, (*childs)->url, filter) : | |
| 692 #endif /* HAVE_DLNA */ | |
| 693 didl_add_item (out, (*childs)->id, | |
| 694 (*childs)->parent ? (*childs)->parent->id : -1, | |
| 695 "true", (*childs)->mime_type->mime_class, | |
| 696 (*childs)->title, protocol, | |
| 697 (*childs)->size, (*childs)->url, filter); | |
| 698 free (protocol); | |
| 699 result_count++; | |
| 700 } | |
| 701 } | |
| 702 } | |
| 703 } | |
| 704 | |
| 705 return result_count; | |
| 706 } | |
| 707 | |
| 708 static int | |
| 709 cds_search_directchildren (struct action_event_t *event, | |
| 710 struct buffer_t *out, int index, | |
| 711 int count, struct upnp_entry_t *entry, | |
| 712 char *filter, char *search_criteria) | |
| 713 { | |
| 714 struct upnp_entry_t **childs; | |
| 715 int s, result_count = 0; | |
| 716 char tmp[32]; | |
| 717 | |
| 718 index = 0; | |
| 719 | |
| 720 if (entry->child_count == -1) /* item : file */ | |
| 721 return -1; | |
| 722 | |
| 723 didl_add_header (out); | |
| 724 | |
| 725 /* go to the child pointed out by index */ | |
| 726 childs = entry->childs; | |
| 727 for (s = 0; s < index; s++) | |
| 728 if (*childs) | |
| 729 childs++; | |
| 730 | |
| 731 /* UPnP CDS compliance : If starting index = 0 and requested count = 0 | |
| 732 then all children must be returned */ | |
| 733 if (index == 0 && count == 0) | |
| 734 count = entry->child_count; | |
| 735 | |
| 736 for (; *childs; childs++) | |
| 737 { | |
| 738 if (count == 0 || result_count < count) | |
| 739 /* only fetch the requested count number or all entries if count = 0 */ | |
| 740 { | |
| 741 if ((*childs)->child_count >= 0) /* container */ | |
| 742 { | |
| 743 int new_count; | |
| 744 new_count = cds_search_directchildren_recursive | |
| 745 (out, (count == 0) ? 0 : (count - result_count), | |
| 746 (*childs), filter, search_criteria); | |
| 747 result_count += new_count; | |
| 748 } | |
| 749 else /* item */ | |
| 750 { | |
| 751 if (matches_search (search_criteria, *childs)) | |
| 752 { | |
| 753 #ifdef HAVE_DLNA | |
| 754 extern struct ushare_t *ut; | |
| 755 #endif /* HAVE_DLNA */ | |
| 756 char *protocol = | |
| 757 #ifdef HAVE_DLNA | |
| 758 (*childs)->dlna_profile ? | |
| 759 dlna_write_protocol_info(DLNA_PROTOCOL_INFO_TYPE_HTTP, | |
| 760 DLNA_ORG_PLAY_SPEED_NORMAL, | |
| 761 DLNA_ORG_CONVERSION_NONE, | |
| 762 DLNA_ORG_OPERATION_RANGE, | |
| 763 ut->dlna_flags, (*childs)->dlna_profile): | |
| 764 #endif /* HAVE_DLNA */ | |
| 765 mime_get_protocol ((*childs)->mime_type); | |
| 766 | |
| 767 #ifdef HAVE_DLNA | |
| 768 (*childs)->dlna_profile ? | |
| 769 didl_add_item (out, (*childs)->id, | |
| 770 (*childs)->parent ? (*childs)->parent->id : -1, | |
| 771 "true", dlna_profile_upnp_object_item ((*childs)->dlna_profile), | |
| 772 (*childs)->title, protocol, | |
| 773 (*childs)->size, (*childs)->url, filter) : | |
| 774 #endif /* HAVE_DLNA */ | |
| 775 didl_add_item (out, (*childs)->id, | |
| 776 (*childs)->parent ? (*childs)->parent->id : -1, | |
| 777 "true", (*childs)->mime_type->mime_class, | |
| 778 (*childs)->title, protocol, | |
| 779 (*childs)->size, (*childs)->url, filter); | |
| 780 free (protocol); | |
| 781 result_count++; | |
| 782 } | |
| 783 } | |
| 784 } | |
| 785 } | |
| 786 | |
| 787 didl_add_footer (out); | |
| 788 | |
| 789 upnp_add_response (event, SERVICE_CDS_DIDL_RESULT, out->buf); | |
| 790 | |
| 791 sprintf (tmp, "%d", result_count); | |
| 792 upnp_add_response (event, SERVICE_CDS_DIDL_NUM_RETURNED, tmp); | |
| 793 sprintf (tmp, "%d", result_count); | |
| 794 upnp_add_response (event, SERVICE_CDS_DIDL_TOTAL_MATCH, tmp); | |
| 795 | |
| 796 return result_count; | |
| 797 } | |
| 798 | |
| 799 static bool | |
| 800 cds_search (struct action_event_t *event) | |
| 801 { | |
| 802 extern struct ushare_t *ut; | |
| 803 struct upnp_entry_t *entry = NULL; | |
| 804 int result_count = 0, index, count, id, sort_criteria; | |
| 805 char *search_criteria = NULL; | |
| 806 char *filter = NULL; | |
| 807 struct buffer_t *out = NULL; | |
| 808 | |
| 809 if (!event) | |
| 810 return false; | |
| 811 | |
| 812 /* Check for status */ | |
| 813 if (!event->status) | |
| 814 return false; | |
| 815 | |
| 816 /* check if metadatas have been well inited */ | |
| 817 if (!ut->init) | |
| 818 return false; | |
| 819 | |
| 820 /* Retrieve Browse arguments */ | |
| 821 index = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_START_INDEX); | |
| 822 count = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_REQUEST_COUNT); | |
| 823 id = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_OBJECT_ID); | |
| 824 | |
| 825 search_criteria = upnp_get_string (event->request, | |
| 826 SERVICE_CDS_ARG_SEARCH_CRIT); | |
| 827 filter = upnp_get_string (event->request, SERVICE_CDS_ARG_FILTER); | |
| 828 sort_criteria = upnp_get_ui4 (event->request, SERVICE_CDS_ARG_SORT_CRIT); | |
| 829 | |
| 830 if (!search_criteria || !filter) | |
| 831 return false; | |
| 832 | |
| 833 entry = upnp_get_entry (ut, id); | |
| 834 | |
| 835 if (!entry && (id < ut->starting_id)) | |
| 836 entry = upnp_get_entry (ut, ut->starting_id); | |
| 837 | |
| 838 if (!entry) | |
| 839 return false; | |
| 840 | |
| 841 out = buffer_new (); | |
| 842 if (!out) | |
| 843 return false; | |
| 844 | |
| 845 result_count = | |
| 846 cds_search_directchildren (event, out, index, count, entry, | |
| 847 filter, search_criteria); | |
| 848 | |
| 849 if (result_count < 0) | |
| 850 { | |
| 851 buffer_free (out); | |
| 852 return false; | |
| 853 } | |
| 854 | |
| 855 buffer_free (out); | |
| 856 upnp_add_response (event, SERVICE_CDS_DIDL_UPDATE_ID, | |
| 857 SERVICE_CDS_ROOT_OBJECT_ID); | |
| 858 | |
| 859 free (search_criteria); | |
| 860 free (filter); | |
| 861 | |
| 862 return event->status; | |
| 863 } | |
| 864 | |
| 865 /* List of UPnP ContentDirectory Service actions */ | |
| 866 struct service_action_t cds_service_actions[] = { | |
| 867 { SERVICE_CDS_ACTION_SEARCH_CAPS, cds_get_search_capabilities }, | |
| 868 { SERVICE_CDS_ACTION_SORT_CAPS, cds_get_sort_capabilities }, | |
| 869 { SERVICE_CDS_ACTION_UPDATE_ID, cds_get_system_update_id }, | |
| 870 { SERVICE_CDS_ACTION_BROWSE, cds_browse }, | |
| 871 { SERVICE_CDS_ACTION_SEARCH, cds_search }, | |
| 872 { NULL, NULL } | |
| 873 }; |
