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