Wrap up version 1.3.3.
[minidlna.git] / tivo_commands.c
blobd4ab1570415e942fc733b109fb140cc35983069d
1 /* MiniDLNA media server
2 * Copyright (C) 2009 Justin Maggard
4 * This file is part of MiniDLNA.
6 * MiniDLNA is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * MiniDLNA is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with MiniDLNA. If not, see <http://www.gnu.org/licenses/>.
18 #include "config.h"
19 #ifdef TIVO_SUPPORT
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <libgen.h>
24 #include <time.h>
25 #include <sys/stat.h>
27 #include "event.h"
28 #include "tivo_utils.h"
29 #include "upnpglobalvars.h"
30 #include "upnphttp.h"
31 #include "upnpsoap.h"
32 #include "utils.h"
33 #include "sql.h"
34 #include "log.h"
36 static void
37 SendRootContainer(struct upnphttp *h)
39 char *resp;
40 int len;
42 len = xasprintf(&resp, "<?xml version='1.0' encoding='UTF-8' ?>\n"
43 "<TiVoContainer>"
44 "<Details>"
45 "<ContentType>x-container/tivo-server</ContentType>"
46 "<SourceFormat>x-container/folder</SourceFormat>"
47 "<TotalDuration>0</TotalDuration>"
48 "<TotalItems>3</TotalItems>"
49 "<Title>%s</Title>"
50 "</Details>"
51 "<ItemStart>0</ItemStart>"
52 "<ItemCount>3</ItemCount>"
53 "<Item>"
54 "<Details>"
55 "<ContentType>x-container/tivo-photos</ContentType>"
56 "<SourceFormat>x-container/folder</SourceFormat>"
57 "<Title>Pictures on %s</Title>"
58 "</Details>"
59 "<Links>"
60 "<Content>"
61 "<Url>/TiVoConnect?Command=QueryContainer&amp;Container=3</Url>"
62 "</Content>"
63 "</Links>"
64 "</Item>"
65 "<Item>"
66 "<Details>"
67 "<ContentType>x-container/tivo-music</ContentType>"
68 "<SourceFormat>x-container/folder</SourceFormat>"
69 "<Title>Music on %s</Title>"
70 "</Details>"
71 "<Links>"
72 "<Content>"
73 "<Url>/TiVoConnect?Command=QueryContainer&amp;Container=1</Url>"
74 "</Content>"
75 "</Links>"
76 "</Item>"
77 "<Item>"
78 "<Details>"
79 "<ContentType>x-container/tivo-videos</ContentType>"
80 "<SourceFormat>x-container/folder</SourceFormat>"
81 "<Title>Videos on %s</Title>"
82 "</Details>"
83 "<Links>"
84 "<Content>"
85 "<Url>/TiVoConnect?Command=QueryContainer&amp;Container=2</Url>"
86 "<ContentType>x-container/tivo-videos</ContentType>"
87 "</Content>"
88 "</Links>"
89 "</Item>"
90 "</TiVoContainer>",
91 friendly_name, friendly_name, friendly_name, friendly_name);
92 BuildResp_upnphttp(h, resp, len);
93 free(resp);
94 SendResp_upnphttp(h);
97 static void
98 SendFormats(struct upnphttp *h, const char *sformat)
100 char *resp;
101 int len;
103 len = xasprintf(&resp, "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
104 "<TiVoFormats>"
105 "<Format>"
106 "<ContentType>video/x-tivo-mpeg</ContentType>"
107 "<Description/>"
108 "</Format>"
109 "<Format>"
110 "<ContentType>%s</ContentType>"
111 "<Description/>"
112 "</Format>"
113 "</TiVoFormats>", sformat);
114 BuildResp_upnphttp(h, resp, len);
115 free(resp);
116 SendResp_upnphttp(h);
119 static char *
120 tivo_unescape_tag(char *tag)
122 modifyString(tag, "&amp;amp;", "&amp;", 1);
123 modifyString(tag, "&amp;amp;lt;", "&lt;", 1);
124 modifyString(tag, "&amp;lt;", "&lt;", 1);
125 modifyString(tag, "&amp;amp;gt;", "&gt;", 1);
126 modifyString(tag, "&amp;gt;", "&gt;", 1);
127 modifyString(tag, "&amp;quot;", "&quot;", 1);
128 return tag;
131 #define FLAG_SEND_RESIZED 0x01
132 #define FLAG_NO_PARAMS 0x02
133 #define FLAG_VIDEO 0x04
134 static int
135 callback(void *args, int argc, char **argv, char **azColName)
137 struct Response *passed_args = (struct Response *)args;
138 char *id = argv[0], *class = argv[1], *detailID = argv[2], *size = argv[3], *title = argv[4], *duration = argv[5],
139 *bitrate = argv[6], *sampleFrequency = argv[7], *artist = argv[8], *album = argv[9], *genre = argv[10],
140 *comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14];
141 struct string_s *str = passed_args->str;
143 if( strncmp(class, "item", 4) == 0 )
145 int flags = 0;
146 tivo_unescape_tag(title);
147 if( strncmp(mime, "audio", 5) == 0 )
149 flags |= FLAG_NO_PARAMS;
150 strcatf(str, "<Item><Details>"
151 "<ContentType>%s</ContentType>"
152 "<SourceFormat>%s</SourceFormat>"
153 "<SourceSize>%s</SourceSize>",
154 "audio/*", mime, size);
155 strcatf(str, "<SongTitle>%s</SongTitle>", title);
156 if( date )
157 strcatf(str, "<AlbumYear>%.*s</AlbumYear>", 4, date);
159 else if( strcmp(mime, "image/jpeg") == 0 )
161 flags |= FLAG_SEND_RESIZED;
162 strcatf(str, "<Item><Details>"
163 "<ContentType>%s</ContentType>"
164 "<SourceFormat>%s</SourceFormat>"
165 "<SourceSize>%s</SourceSize>",
166 "image/*", mime, size);
167 if( date )
169 struct tm tm;
170 memset(&tm, 0, sizeof(tm));
171 tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not
172 strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
173 strcatf(str, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
175 if( comment )
176 strcatf(str, "<Caption>%s</Caption>", comment);
178 else if( strncmp(mime, "video", 5) == 0 )
180 char *episode;
181 flags |= FLAG_VIDEO;
182 strcatf(str, "<Item><Details>"
183 "<ContentType>%s</ContentType>"
184 "<SourceFormat>%s</SourceFormat>"
185 "<SourceSize>%s</SourceSize>",
186 mime, mime, size);
187 episode = strstr(title, " - ");
188 if( episode )
190 strcatf(str, "<EpisodeTitle>%s</EpisodeTitle>", episode+3);
191 *episode = '\0';
193 if( date )
195 struct tm tm;
196 memset(&tm, 0, sizeof(tm));
197 tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not
198 strptime(date, "%Y-%m-%dT%H:%M:%S", &tm);
199 strcatf(str, "<CaptureDate>0x%X</CaptureDate>", (unsigned int)mktime(&tm));
201 if( comment )
202 strcatf(str, "<Description>%s</Description>", tivo_unescape_tag(comment));
204 else
206 return 0;
208 strcatf(str, "<Title>%s</Title>", title);
209 if( artist ) {
210 strcatf(str, "<ArtistName>%s</ArtistName>", tivo_unescape_tag(artist));
212 if( album ) {
213 strcatf(str, "<AlbumTitle>%s</AlbumTitle>", tivo_unescape_tag(album));
215 if( genre ) {
216 strcatf(str, "<MusicGenre>%s</MusicGenre>", tivo_unescape_tag(genre));
218 if( resolution ) {
219 char *width = strsep(&resolution, "x");
220 strcatf(str, "<SourceWidth>%s</SourceWidth>"
221 "<SourceHeight>%s</SourceHeight>",
222 width, resolution);
224 if( duration ) {
225 strcatf(str, "<Duration>%d</Duration>",
226 atoi(strrchr(duration, '.')+1) + (1000*atoi(strrchr(duration, ':')+1))
227 + (60000*atoi(strrchr(duration, ':')-2)) + (3600000*atoi(duration)));
229 if( bitrate ) {
230 strcatf(str, "<SourceBitRate>%s</SourceBitRate>", bitrate);
232 if( sampleFrequency ) {
233 strcatf(str, "<SourceSampleRate>%s</SourceSampleRate>", sampleFrequency);
235 strcatf(str, "</Details><Links>"
236 "<Content>"
237 "<ContentType>%s</ContentType>"
238 "<Url>/%s/%s.%s</Url>%s"
239 "</Content>",
240 mime,
241 (flags & FLAG_SEND_RESIZED) ? "Resized" : "MediaItems",
242 detailID, mime_to_ext(mime),
243 (flags & FLAG_NO_PARAMS) ? "<AcceptsParams>No</AcceptsParams>" : "");
244 if( flags & FLAG_VIDEO )
246 strcatf(str, "<CustomIcon>"
247 "<ContentType>image/*</ContentType>"
248 "<Url>urn:tivo:image:save-until-i-delete-recording</Url>"
249 "</CustomIcon>");
251 strcatf(str, "</Links>");
253 else if( strncmp(class, "container", 9) == 0 )
255 int count;
256 /* Determine the number of children */
257 #ifdef __sparc__ /* Adding filters on large containers can take a long time on slow processors */
258 count = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", id);
259 #else
260 count = sql_get_int_field(db, "SELECT count(*) from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID) where PARENT_ID = '%s' and "
261 " (MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts')"
262 " or CLASS glob 'container*')", id);
263 #endif
264 strcatf(str, "<Item>"
265 "<Details>"
266 "<ContentType>x-container/folder</ContentType>"
267 "<SourceFormat>x-container/folder</SourceFormat>"
268 "<Title>%s</Title>"
269 "<TotalItems>%d</TotalItems>"
270 "</Details>"
271 "<Links>"
272 "<Content>"
273 "<Url>/TiVoConnect?Command=QueryContainer&amp;Container=%s</Url>"
274 "<ContentType>x-tivo-container/folder</ContentType>"
275 "</Content>"
276 "</Links>",
277 tivo_unescape_tag(title), count, id);
279 strcatf(str, "</Item>");
281 passed_args->returned++;
283 return 0;
286 #define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.CLASS, o.DETAIL_ID, d.SIZE, d.TITLE," \
287 " d.DURATION, d.BITRATE, d.SAMPLERATE, d.ARTIST, d.ALBUM, d.GENRE," \
288 " d.COMMENT, d.DATE, d.RESOLUTION, d.MIME, d.DISC, d.TRACK "
290 static void
291 SendItemDetails(struct upnphttp *h, int64_t item)
293 char *sql;
294 char *zErrMsg = NULL;
295 struct Response args;
296 struct string_s str;
297 int ret;
298 memset(&args, 0, sizeof(args));
299 memset(&str, 0, sizeof(str));
301 str.data = malloc(32768);
302 str.size = 32768;
303 str.off = sprintf(str.data, "<?xml version='1.0' encoding='UTF-8' ?>\n<TiVoItem>");
304 args.str = &str;
305 args.requested = 1;
306 xasprintf(&sql, SELECT_COLUMNS
307 "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
308 " where o.DETAIL_ID = %lld group by o.DETAIL_ID", (long long)item);
309 DPRINTF(E_DEBUG, L_TIVO, "%s\n", sql);
310 ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
311 free(sql);
312 if( ret != SQLITE_OK )
314 DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
315 sqlite3_free(zErrMsg);
317 strcatf(&str, "</TiVoItem>");
319 BuildResp_upnphttp(h, str.data, str.off);
320 free(str.data);
321 SendResp_upnphttp(h);
324 static void
325 SendContainer(struct upnphttp *h, const char *objectID, int itemStart, int itemCount, char *anchorItem,
326 int anchorOffset, int recurse, char *sortOrder, char *filter, unsigned long int randomSeed)
328 char *resp = malloc(262144);
329 char *sql, *item, *saveptr;
330 char *zErrMsg = NULL;
331 char **result;
332 char *title, *which;
333 char what[10], order[96]={0}, order2[96]={0}, myfilter[256]={0};
334 char str_buf[1024];
335 char type[8];
336 char groupBy[19] = {0};
337 struct Response args;
338 struct string_s str;
339 int totalMatches = 0;
340 int i, ret;
341 memset(&args, 0, sizeof(args));
342 memset(&str, 0, sizeof(str));
344 args.str = &str;
345 str.data = resp+1024;
346 str.size = 262144-1024;
347 if( itemCount >= 0 )
349 args.requested = itemCount;
351 else
353 if( itemCount == -100 )
354 itemCount = 1;
355 args.requested = itemCount * -1;
358 switch( *objectID )
360 case '1':
361 strcpy(type, "music");
362 break;
363 case '2':
364 strcpy(type, "videos");
365 break;
366 case '3':
367 strcpy(type, "photos");
368 break;
369 default:
370 strcpy(type, "server");
371 break;
374 if( strlen(objectID) == 1 )
376 switch( *objectID )
378 case '1':
379 xasprintf(&title, "Music on %s", friendly_name);
380 break;
381 case '2':
382 xasprintf(&title, "Videos on %s", friendly_name);
383 break;
384 case '3':
385 xasprintf(&title, "Pictures on %s", friendly_name);
386 break;
387 default:
388 xasprintf(&title, "Unknown on %s", friendly_name);
389 break;
392 else
394 item = sql_get_text_field(db, "SELECT NAME from OBJECTS where OBJECT_ID = '%q'", objectID);
395 if( item )
397 title = escape_tag(item, 1);
398 sqlite3_free(item);
400 else
401 title = strdup("UNKNOWN");
404 if( recurse )
406 which = sqlite3_mprintf("OBJECT_ID glob '%q$*'", objectID);
407 strcpy(groupBy, "group by DETAIL_ID");
409 else
411 which = sqlite3_mprintf("PARENT_ID = '%q'", objectID);
414 if( sortOrder )
416 if( strcasestr(sortOrder, "Random") )
418 sprintf(order, "tivorandom(%lu)", randomSeed);
419 if( itemCount < 0 )
420 sprintf(order2, "tivorandom(%lu) DESC", randomSeed);
421 else
422 sprintf(order2, "tivorandom(%lu)", randomSeed);
424 else
426 short title_state = 0;
427 item = strtok_r(sortOrder, ",", &saveptr);
428 while( item != NULL )
430 int reverse=0;
431 if( *item == '!' )
433 reverse = 1;
434 item++;
436 if( strcasecmp(item, "Type") == 0 )
438 strcat(order, "CLASS");
439 strcat(order2, "CLASS");
441 else if( strcasecmp(item, "Title") == 0 )
443 /* Explicitly sort music by track then title. */
444 if( title_state < 2 && *objectID == '1' )
446 if( !title_state )
448 strcat(order, "DISC");
449 strcat(order2, "DISC");
450 title_state = 1;
452 else
454 strcat(order, "TRACK");
455 strcat(order2, "TRACK");
456 title_state = 2;
459 else
461 strcat(order, "TITLE");
462 strcat(order2, "TITLE");
463 title_state = -1;
466 else if( strcasecmp(item, "CreationDate") == 0 ||
467 strcasecmp(item, "CaptureDate") == 0 )
469 strcat(order, "DATE");
470 strcat(order2, "DATE");
472 else
474 DPRINTF(E_INFO, L_TIVO, "Unhandled SortOrder [%s]\n", item);
475 goto unhandled_order;
478 if( reverse )
480 strcat(order, " DESC");
481 if( itemCount >= 0 )
482 strcat(order2, " DESC");
483 else
484 strcat(order2, " ASC");
486 else
488 strcat(order, " ASC");
489 if( itemCount >= 0 )
490 strcat(order2, " ASC");
491 else
492 strcat(order2, " DESC");
494 strcat(order, ", ");
495 strcat(order2, ", ");
496 unhandled_order:
497 if( title_state <= 0 )
498 item = strtok_r(NULL, ",", &saveptr);
500 if( title_state != -1 )
502 strcat(order, "TITLE ASC, ");
503 if( itemCount >= 0 )
504 strcat(order2, "TITLE ASC, ");
505 else
506 strcat(order2, "TITLE DESC, ");
508 strcat(order, "DETAIL_ID ASC");
509 if( itemCount >= 0 )
510 strcat(order2, "DETAIL_ID ASC");
511 else
512 strcat(order2, "DETAIL_ID DESC");
515 else
517 sprintf(order, "CLASS, NAME, DETAIL_ID");
518 if( itemCount < 0 )
519 sprintf(order2, "CLASS DESC, NAME DESC, DETAIL_ID DESC");
520 else
521 sprintf(order2, "CLASS, NAME, DETAIL_ID");
524 if( filter )
526 item = strtok_r(filter, ",", &saveptr);
527 for( i=0; item != NULL; i++ )
529 if( i )
531 strcat(myfilter, " or ");
533 if( (strcasecmp(item, "x-container/folder") == 0) ||
534 (strncasecmp(item, "x-tivo-container/", 17) == 0) )
536 strcat(myfilter, "CLASS glob 'container*'");
538 else if( strncasecmp(item, "image", 5) == 0 )
540 strcat(myfilter, "MIME = 'image/jpeg'");
542 else if( strncasecmp(item, "audio", 5) == 0 )
544 strcat(myfilter, "MIME = 'audio/mpeg'");
546 else if( strncasecmp(item, "video", 5) == 0 )
548 strcat(myfilter, "MIME in ('video/mpeg', 'video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts')");
550 else
552 DPRINTF(E_INFO, L_TIVO, "Unhandled Filter [%s]\n", item);
553 if( i )
555 ret = strlen(myfilter);
556 myfilter[ret-4] = '\0';
558 i--;
560 item = strtok_r(NULL, ",", &saveptr);
563 else
565 strcpy(myfilter, "MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg', 'video/x-tivo-mpeg-ts') or CLASS glob 'container*'");
568 if( anchorItem )
570 if( strstr(anchorItem, "QueryContainer") )
572 strcpy(what, "OBJECT_ID");
573 saveptr = strrchr(anchorItem, '=');
574 if( saveptr )
575 anchorItem = saveptr + 1;
577 else
579 strcpy(what, "DETAIL_ID");
581 sqlite3Prng.isInit = 0;
582 sql = sqlite3_mprintf("SELECT %s from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
583 " where %s and (%s)"
584 " %s"
585 " order by %s", what, which, myfilter, groupBy, order2);
586 DPRINTF(E_DEBUG, L_TIVO, "%s\n", sql);
587 if( (sql_get_table(db, sql, &result, &ret, NULL) == SQLITE_OK) && ret )
589 for( i=1; i<=ret; i++ )
591 if( strcmp(anchorItem, result[i]) == 0 )
593 if( itemCount < 0 )
594 itemStart = ret - i + itemCount;
595 else
596 itemStart += i;
597 break;
600 sqlite3_free_table(result);
602 sqlite3_free(sql);
604 args.start = itemStart+anchorOffset;
605 sqlite3Prng.isInit = 0;
607 ret = sql_get_int_field(db, "SELECT count(distinct DETAIL_ID) "
608 "from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)"
609 " where %s and (%s)",
610 which, myfilter);
611 totalMatches = (ret > 0) ? ret : 0;
612 if( itemCount < 0 && !itemStart && !anchorOffset )
614 args.start = totalMatches + itemCount;
617 sql = sqlite3_mprintf(SELECT_COLUMNS
618 "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
619 " where %s and (%s)"
620 " %s"
621 " order by %s limit %d, %d",
622 which, myfilter, groupBy, order, args.start, args.requested);
623 DPRINTF(E_DEBUG, L_TIVO, "%s\n", sql);
624 ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
625 sqlite3_free(sql);
626 if( ret != SQLITE_OK )
628 DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
629 sqlite3_free(zErrMsg);
630 Send500(h);
631 sqlite3_free(which);
632 free(title);
633 free(resp);
634 return;
636 strcatf(&str, "</TiVoContainer>");
638 ret = sprintf(str_buf, "<?xml version='1.0' encoding='UTF-8' ?>\n"
639 "<TiVoContainer>"
640 "<Details>"
641 "<ContentType>x-container/tivo-%s</ContentType>"
642 "<SourceFormat>x-container/folder</SourceFormat>"
643 "<TotalItems>%d</TotalItems>"
644 "<Title>%s</Title>"
645 "</Details>"
646 "<ItemStart>%d</ItemStart>"
647 "<ItemCount>%d</ItemCount>",
648 type, totalMatches, title, args.start, args.returned);
649 str.data -= ret;
650 memcpy(str.data, &str_buf, ret);
651 str.size = str.off+ret;
652 free(title);
653 sqlite3_free(which);
654 BuildResp_upnphttp(h, str.data, str.size);
655 free(resp);
656 SendResp_upnphttp(h);
659 void
660 ProcessTiVoCommand(struct upnphttp *h, const char *orig_path)
662 char *path;
663 char *key, *val;
664 char *saveptr = NULL, *item;
665 char *command = NULL, *container = NULL, *anchorItem = NULL;
666 char *sortOrder = NULL, *filter = NULL, *sformat = NULL;
667 int64_t detailItem=0;
668 int itemStart=0, itemCount=-100, anchorOffset=0, recurse=0;
669 unsigned long int randomSeed=0;
671 path = strdup(orig_path);
672 DPRINTF(E_DEBUG, L_GENERAL, "Processing TiVo command %s\n", path);
674 item = strtok_r( path, "&", &saveptr );
675 while( item != NULL )
677 if( *item == '\0' )
679 item = strtok_r( NULL, "&", &saveptr );
680 continue;
682 decodeString(item, 1);
683 val = item;
684 key = strsep(&val, "=");
685 decodeString(val, 1);
686 DPRINTF(E_DEBUG, L_GENERAL, "%s: %s\n", key, val);
687 if( strcasecmp(key, "Command") == 0 )
689 command = val;
691 else if( strcasecmp(key, "Container") == 0 )
693 container = val;
695 else if( strcasecmp(key, "ItemStart") == 0 )
697 itemStart = atoi(val);
699 else if( strcasecmp(key, "ItemCount") == 0 )
701 itemCount = atoi(val);
703 else if( strcasecmp(key, "AnchorItem") == 0 )
705 anchorItem = basename(val);
707 else if( strcasecmp(key, "AnchorOffset") == 0 )
709 anchorOffset = atoi(val);
711 else if( strcasecmp(key, "Recurse") == 0 )
713 recurse = strcasecmp("yes", val) == 0 ? 1 : 0;
715 else if( strcasecmp(key, "SortOrder") == 0 )
717 sortOrder = val;
719 else if( strcasecmp(key, "Filter") == 0 )
721 filter = val;
723 else if( strcasecmp(key, "RandomSeed") == 0 )
725 randomSeed = strtoul(val, NULL, 10);
727 else if( strcasecmp(key, "Url") == 0 )
729 if( val )
730 detailItem = strtoll(basename(val), NULL, 10);
732 else if( strcasecmp(key, "SourceFormat") == 0 )
734 sformat = val;
736 else if( strcasecmp(key, "Format") == 0 || // Only send XML
737 strcasecmp(key, "SerialNum") == 0 || // Unused for now
738 strcasecmp(key, "DoGenres") == 0 ) // Not sure what this is, so ignore it
742 else
744 DPRINTF(E_DEBUG, L_GENERAL, "Unhandled parameter [%s]\n", key);
746 item = strtok_r( NULL, "&", &saveptr );
748 if( anchorItem )
750 strip_ext(anchorItem);
753 if( command )
755 if( strcmp(command, "QueryContainer") == 0 )
757 if( !container || (strcmp(container, "/") == 0) )
759 SendRootContainer(h);
761 else
763 SendContainer(h, container, itemStart, itemCount, anchorItem,
764 anchorOffset, recurse, sortOrder, filter, randomSeed);
767 else if( strcmp(command, "QueryItem") == 0 )
769 SendItemDetails(h, detailItem);
771 else if( strcmp(command, "QueryFormats") == 0 )
773 SendFormats(h, sformat);
775 else
777 DPRINTF(E_DEBUG, L_GENERAL, "Unhandled command [%s]\n", command);
778 Send501(h);
779 free(path);
780 return;
783 free(path);
784 CloseSocket_upnphttp(h);
786 #endif // TIVO_SUPPORT