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 }; |