* Some minor DLNA conformance fixups.

This commit is contained in:
Justin Maggard
2009-05-14 22:13:06 +00:00
parent 5315c89880
commit 5a08b7e275
6 changed files with 116 additions and 49 deletions

View File

@ -202,11 +202,11 @@ GetAudioMetadata(const char * path, char * name)
asprintf(&date, "%04d-01-01", song.year); asprintf(&date, "%04d-01-01", song.year);
sprintf(duration, "%d:%02d:%02d.%03d", sprintf(duration, "%d:%02d:%02d.%03d",
(song.song_length/3600000), (song.song_length/3600000),
(song.song_length/60000), (song.song_length/60000%60),
(song.song_length/1000%60), (song.song_length/1000%60),
(song.song_length%1000)); (song.song_length%1000));
title = song.title; title = song.title;
if( title ) if( title && *title )
{ {
title = trim(title); title = trim(title);
if( (esc_tag = escape_tag(title)) ) if( (esc_tag = escape_tag(title)) )
@ -221,7 +221,7 @@ GetAudioMetadata(const char * path, char * name)
} }
for( i=ROLE_START; i<N_ROLE; i++ ) for( i=ROLE_START; i<N_ROLE; i++ )
{ {
if( song.contributor[i] ) if( song.contributor[i] && *song.contributor[i] )
{ {
artist = trim(song.contributor[i]); artist = trim(song.contributor[i]);
if( (esc_tag = escape_tag(artist)) ) if( (esc_tag = escape_tag(artist)) )
@ -232,7 +232,7 @@ GetAudioMetadata(const char * path, char * name)
break; break;
} }
} }
if( song.album ) if( song.album && *song.album )
{ {
album = trim(song.album); album = trim(song.album);
if( (esc_tag = escape_tag(album)) ) if( (esc_tag = escape_tag(album)) )
@ -241,7 +241,7 @@ GetAudioMetadata(const char * path, char * name)
album = esc_tag; album = esc_tag;
} }
} }
if( song.genre ) if( song.genre && *song.genre )
{ {
genre = trim(song.genre); genre = trim(song.genre);
if( (esc_tag = escape_tag(genre)) ) if( (esc_tag = escape_tag(genre)) )
@ -250,7 +250,7 @@ GetAudioMetadata(const char * path, char * name)
genre = esc_tag; genre = esc_tag;
} }
} }
if( song.comment ) if( song.comment && *song.comment )
{ {
comment = trim(song.comment); comment = trim(song.comment);
if( (esc_tag = escape_tag(comment)) ) if( (esc_tag = escape_tag(comment)) )
@ -438,8 +438,6 @@ GetImageMetadata(const char * path, char * name)
asprintf(&m.dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); asprintf(&m.dlna_pn, "JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
else if( width <= 4096 && height <= 4096 ) else if( width <= 4096 && height <= 4096 )
asprintf(&m.dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); asprintf(&m.dlna_pn, "JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0");
else
asprintf(&m.dlna_pn, "JPEG_XL");
asprintf(&m.resolution, "%dx%d", width, height); asprintf(&m.resolution, "%dx%d", width, height);
sql = sqlite3_mprintf( "INSERT into DETAILS" sql = sqlite3_mprintf( "INSERT into DETAILS"

View File

@ -212,7 +212,7 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
"DATE: %s\r\n" "DATE: %s\r\n"
"Ext:\r\n" "Ext:\r\n"
"ST: %.*s%s\r\n" "ST: %.*s%s\r\n"
"USN: %s::%.*s%s\r\n" "USN: %s%s%.*s%s\r\n"
"EXT:\r\n" "EXT:\r\n"
"SERVER: " MINIDLNA_SERVER_STRING "\r\n" "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
"LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n" "LOCATION: http://%s:%u" ROOTDESC_PATH "\r\n"
@ -220,7 +220,7 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
"\r\n", "\r\n",
szTime, szTime,
st_len, st, suffix, st_len, st, suffix,
uuidvalue, st_len, st, suffix, uuidvalue, st_len?"::":"", st_len, st, suffix,
host, (unsigned int)port); host, (unsigned int)port);
n = sendto(s, buf, l, 0, n = sendto(s, buf, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
@ -232,12 +232,12 @@ SendSSDPAnnounce2(int s, struct sockaddr_in sockname,
static const char * const known_service_types[] = static const char * const known_service_types[] =
{ {
uuidvalue,
"upnp:rootdevice", "upnp:rootdevice",
"urn:schemas-upnp-org:device:MediaServer:", "urn:schemas-upnp-org:device:MediaServer:",
"urn:schemas-upnp-org:service:ContentDirectory:", "urn:schemas-upnp-org:service:ContentDirectory:",
"urn:schemas-upnp-org:service:ConnectionManager:", "urn:schemas-upnp-org:service:ConnectionManager:",
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:", "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:",
uuidvalue,
0 0
}; };
@ -268,14 +268,14 @@ SendSSDPNotifies(int s, const char * host, unsigned short port,
"LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n" "LOCATION:http://%s:%d" ROOTDESC_PATH"\r\n"
"SERVER: " MINIDLNA_SERVER_STRING "\r\n" "SERVER: " MINIDLNA_SERVER_STRING "\r\n"
"NT:%s%s\r\n" "NT:%s%s\r\n"
"USN:%s::%s%s\r\n" "USN:%s%s%s%s\r\n"
"NTS:ssdp:alive\r\n" "NTS:ssdp:alive\r\n"
"\r\n", "\r\n",
SSDP_MCAST_ADDR, SSDP_PORT, SSDP_MCAST_ADDR, SSDP_PORT,
lifetime, lifetime,
host, port, host, port,
known_service_types[i], (i==0?"":"1"), known_service_types[i], (i>1?"1":""),
uuidvalue, known_service_types[i], (i==0?"":"1") ); uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
if(l>=sizeof(bufr)) if(l>=sizeof(bufr))
{ {
DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n"); DPRINTF(E_WARN, L_SSDP, "SendSSDPNotifies(): truncated output\n");
@ -399,11 +399,13 @@ ProcessSSDPRequest(int s, unsigned short port)
/* strlen("ssdp:all") == 8 */ /* strlen("ssdp:all") == 8 */
if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8))) if(st_len==8 && (0 == memcmp(st, "ssdp:all", 8)))
{ {
for(i=0; known_service_types[i]; i++) SendSSDPAnnounce2(s, sendername, "", 0, "",
lan_addr[lan_addr_index].str, port);
for(i=1; known_service_types[i]; i++)
{ {
l = (int)strlen(known_service_types[i]); l = (int)strlen(known_service_types[i]);
SendSSDPAnnounce2(s, sendername, SendSSDPAnnounce2(s, sendername,
known_service_types[i], l, i==0?"":"1", known_service_types[i], l, i==1?"":"1",
lan_addr[lan_addr_index].str, port); lan_addr[lan_addr_index].str, port);
} }
} }
@ -451,12 +453,13 @@ SendSSDPGoodbye(int * sockets, int n_sockets)
"NOTIFY * HTTP/1.1\r\n" "NOTIFY * HTTP/1.1\r\n"
"HOST:%s:%d\r\n" "HOST:%s:%d\r\n"
"NT:%s%s\r\n" "NT:%s%s\r\n"
"USN:%s::%s%s\r\n" "USN:%s%s%s%s\r\n"
"NTS:ssdp:byebye\r\n" "NTS:ssdp:byebye\r\n"
"\r\n", "\r\n",
SSDP_MCAST_ADDR, SSDP_PORT, SSDP_MCAST_ADDR, SSDP_PORT,
known_service_types[i], (i==0?"":"1"), known_service_types[i], (i>1?"1":""),
uuidvalue, known_service_types[i], (i==0?"":"1")); uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") );
//DEBUG printf("Sending NOTIFY:\n%s", bufr);
n = sendto(sockets[j], bufr, l, 0, n = sendto(sockets[j], bufr, l, 0,
(struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) );
if(n < 0) if(n < 0)

View File

@ -127,22 +127,22 @@ static const struct XMLElt rootDesc[] =
{"/mimetype", "image/png"}, {"/mimetype", "image/png"},
{"/width", "48"}, {"/width", "48"},
{"/height", "48"}, {"/height", "48"},
{"/depth", "32"}, {"/depth", "24"},
{"/url", "/icons/sm.png"}, {"/url", "/icons/sm.png"},
{"/mimetype", "image/png"}, {"/mimetype", "image/png"},
{"/width", "120"}, {"/width", "120"},
{"/height", "120"}, {"/height", "120"},
{"/depth", "32"}, {"/depth", "24"},
{"/url", "/icons/lrg.png"}, {"/url", "/icons/lrg.png"},
{"/mimetype", "image/jpeg"}, {"/mimetype", "image/jpeg"},
{"/width", "48"}, {"/width", "48"},
{"/height", "48"}, {"/height", "48"},
{"/depth", "32"}, {"/depth", "24"},
{"/url", "/icons/sm.jpg"}, {"/url", "/icons/sm.jpg"},
{"/mimetype", "image/jpeg"}, {"/mimetype", "image/jpeg"},
{"/width", "120"}, {"/width", "120"},
{"/height", "120"}, {"/height", "120"},
{"/depth", "32"}, {"/depth", "24"},
{"/url", "/icons/lrg.jpg"}, {"/url", "/icons/lrg.jpg"},
{"service", INITHELPER(46,5)}, {"service", INITHELPER(46,5)},
{"service", INITHELPER(51,5)}, {"service", INITHELPER(51,5)},

View File

@ -22,26 +22,18 @@
#define DB_VERSION 1 #define DB_VERSION 1
#define RESOURCE_PROTOCOL_INFO_VALUES \ #define RESOURCE_PROTOCOL_INFO_VALUES \
"http-get:*:image/jpeg:*," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," \ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/avi:*," \
"http-get:*:video/divx:*," \
"http-get:*:video/x-matroska:*," \
"http-get:*:video/mpeg:*," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_HD_NA_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/mp4:*," \
"http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_HD_NA;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=AVC_TS_MP_HD_AC3_T;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/x-ms-wmv:*," \
"http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \
"http-get:*:video/x-msvideo:*," \
"http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01," \ "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01," \
"http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01," \ "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01," \
"http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01," \ "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01," \
@ -49,6 +41,18 @@
"http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01," \ "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01," \
"http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01," \ "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01," \
"http-get:*:audio/mp4:DLNA.ORG_PN=AAC_MULT5_ISO;DLNA.ORG_OP=01," \ "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_MULT5_ISO;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \
"http-get:*:image/jpeg:*," \
"http-get:*:video/avi:*," \
"http-get:*:video/divx:*," \
"http-get:*:video/x-matroska:*," \
"http-get:*:video/mpeg:*," \
"http-get:*:video/mp4:*," \
"http-get:*:video/x-ms-wmv:*," \
"http-get:*:video/x-msvideo:*," \
"http-get:*:audio/mp4:*," \ "http-get:*:audio/mp4:*," \
"http-get:*:audio/wav:*," \ "http-get:*:audio/wav:*," \
"http-get:*:audio/x-flac:*," \ "http-get:*:audio/x-flac:*," \

View File

@ -649,12 +649,15 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
DPRINTF(E_WARN, L_HTTP, "Invalid request, responding ERROR 400. (No Host specified in HTTP headers?)\n"); DPRINTF(E_WARN, L_HTTP, "Invalid request, responding ERROR 400. (No Host specified in HTTP headers?)\n");
Send400(h); Send400(h);
} }
else if( (h->reqflags & FLAG_TIMESEEK) || (h->reqflags & FLAG_PLAYSPEED) ) #if 1 /* 7.3.33.4 */
else if( ((h->reqflags & FLAG_TIMESEEK) || (h->reqflags & FLAG_PLAYSPEED)) &&
!(h->reqflags & FLAG_RANGE) )
{ {
DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n", DPRINTF(E_WARN, L_HTTP, "DLNA %s requested, responding ERROR 406\n",
h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed"); h->reqflags&FLAG_TIMESEEK ? "TimeSeek" : "PlaySpeed");
Send406(h); Send406(h);
} }
#endif
else if(strcmp("GET", HttpCommand) == 0) else if(strcmp("GET", HttpCommand) == 0)
{ {
h->req_command = EGet; h->req_command = EGet;
@ -1544,7 +1547,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object)
"Connection: close\r\n" "Connection: close\r\n"
"Date: %s\r\n" "Date: %s\r\n"
"EXT:\r\n" "EXT:\r\n"
"contentFeatures.dlna.org: DLNA.ORG_PN=%s\r\n" "contentFeatures.dlna.org: %s\r\n"
"Server: " MINIDLNA_SERVER_STRING "\r\n\r\n", "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n",
date, last_file.dlna); date, last_file.dlna);
strcat(header, hdr_buf); strcat(header, hdr_buf);

View File

@ -191,13 +191,11 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
"xmlns:u=\"%s\">" "xmlns:u=\"%s\">"
"<RcsID>-1</RcsID>" "<RcsID>-1</RcsID>"
"<AVTransportID>-1</AVTransportID>" "<AVTransportID>-1</AVTransportID>"
"<ProtocolInfo>" "<ProtocolInfo></ProtocolInfo>"
"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN," "<PeerConnectionManager></PeerConnectionManager>"
"</ProtocolInfo>"
"<PeerConnectionManager>0</PeerConnectionManager>"
"<PeerConnectionID>-1</PeerConnectionID>" "<PeerConnectionID>-1</PeerConnectionID>"
"<Direction>0</Direction>" "<Direction>Output</Direction>"
"<Status>0</Status>" "<Status>Unknown</Status>"
"</u:%sResponse>"; "</u:%sResponse>";
char body[sizeof(resp)+128]; char body[sizeof(resp)+128];
@ -329,6 +327,7 @@ set_filter_flags(char * filter)
} }
else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 ) else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 )
{ {
flags |= FILTER_UPNP_ALBUMARTURI;
flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID; flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
} }
else if( strcmp(item, "upnp:artist") == 0 ) else if( strcmp(item, "upnp:artist") == 0 )
@ -396,11 +395,12 @@ set_filter_flags(char * filter)
} }
char * char *
parse_sort_criteria(char * sortCriteria) parse_sort_criteria(char * sortCriteria, int * error)
{ {
char *order = NULL; char *order = NULL;
char *item, *saveptr; char *item, *saveptr;
int i, ret, reverse, title_sorted = 0; int i, ret, reverse, title_sorted = 0;
*error = 0;
if( !sortCriteria ) if( !sortCriteria )
return NULL; return NULL;
@ -444,6 +444,7 @@ parse_sort_criteria(char * sortCriteria)
else else
{ {
printf("Unhandled SortCriteria [%s]\n", item); printf("Unhandled SortCriteria [%s]\n", item);
*error = 1;
if( i ) if( i )
{ {
ret = strlen(order); ret = strlen(order);
@ -842,9 +843,16 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
sqlite3_free_table(result); sqlite3_free_table(result);
} }
#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */ #ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
ret = 0;
if( totalMatches < 10000 ) if( totalMatches < 10000 )
#endif #endif
orderBy = parse_sort_criteria(SortCriteria); orderBy = parse_sort_criteria(SortCriteria, &ret);
/* If it's a DLNA client, return an error for bad sort criteria */
if( (args.flags & FLAG_DLNA) && ret )
{
SoapError(h, 709, "Unsupported or invalid sort criteria");
goto browse_error;
}
sql = sqlite3_mprintf( SELECT_COLUMNS sql = sqlite3_mprintf( SELECT_COLUMNS
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
@ -859,6 +867,23 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg); DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg); sqlite3_free(zErrMsg);
} }
/* Does the object even exist? */
if( !totalMatches )
{
ret = 0;
sql = sqlite3_mprintf("SELECT count(*) from OBJECTS where OBJECT_ID = '%q'", ObjectId);
if( sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK )
{
ret = atoi(result[1]);
sqlite3_free_table(result);
}
sqlite3_free(sql);
if( !ret )
{
SoapError(h, 701, "No such object error");
goto browse_error;
}
}
ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n" ret = snprintf(str_buf, sizeof(str_buf), "&lt;/DIDL-Lite&gt;</Result>\n"
"<NumberReturned>%u</NumberReturned>\n" "<NumberReturned>%u</NumberReturned>\n"
"<TotalMatches>%u</TotalMatches>\n" "<TotalMatches>%u</TotalMatches>\n"
@ -868,6 +893,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
memcpy(resp+args.size, &str_buf, ret+1); memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret; args.size += ret;
BuildSendAndCloseSoapResp(h, resp, args.size); BuildSendAndCloseSoapResp(h, resp, args.size);
browse_error:
ClearNameValueList(&data); ClearNameValueList(&data);
if( orderBy ) if( orderBy )
free(orderBy); free(orderBy);
@ -959,7 +985,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
} }
#endif #endif
} }
DPRINTF(E_DEBUG, L_HTTP, "Browsing ContentDirectory:\n" DPRINTF(E_DEBUG, L_HTTP, "Searching ContentDirectory:\n"
" * ObjectID: %s\n" " * ObjectID: %s\n"
" * Count: %d\n" " * Count: %d\n"
" * StartingIndex: %d\n" " * StartingIndex: %d\n"
@ -1011,14 +1037,46 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
ContainerID, SearchCriteria, ContainerID, SearchCriteria); ContainerID, SearchCriteria, ContainerID, SearchCriteria);
//DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql); //DEBUG DPRINTF(E_DEBUG, L_HTTP, "Count SQL: %s\n", sql);
ret = sql_get_table(db, str_buf, &result, NULL, NULL); ret = sql_get_table(db, str_buf, &result, NULL, NULL);
if( ret == SQLITE_OK ) { if( ret == SQLITE_OK )
{
totalMatches = atoi(result[1]); totalMatches = atoi(result[1]);
sqlite3_free_table(result); sqlite3_free_table(result);
} }
else
{
/* Must be invalid SQL, so most likely bad or unhandled search criteria. */
SoapError(h, 708, "Unsupported or invalid search criteria");
goto search_error;
}
/* Does the object even exist? */
if( !totalMatches )
{
ret = 0;
sql = sqlite3_mprintf("SELECT count(*) from OBJECTS where OBJECT_ID = '%q'",
!strcmp(ContainerID, "*")?"0":ContainerID);
if( sql_get_table(db, sql, &result, NULL, NULL) == SQLITE_OK )
{
ret = atoi(result[1]);
sqlite3_free_table(result);
}
sqlite3_free(sql);
if( !ret )
{
SoapError(h, 710, "No such container");
goto search_error;
}
}
#ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */ #ifdef __sparc__ /* Sorting takes too long on slow processors with very large containers */
ret = 0;
if( totalMatches < 10000 ) if( totalMatches < 10000 )
#endif #endif
orderBy = parse_sort_criteria(SortCriteria); orderBy = parse_sort_criteria(SortCriteria, &ret);
/* If it's a DLNA client, return an error for bad sort criteria */
if( (args.flags & FLAG_DLNA) && ret )
{
SoapError(h, 709, "Unsupported or invalid sort criteria");
goto search_error;
}
sql = sqlite3_mprintf( SELECT_COLUMNS sql = sqlite3_mprintf( SELECT_COLUMNS
"from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
@ -1049,6 +1107,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
memcpy(resp+args.size, &str_buf, ret+1); memcpy(resp+args.size, &str_buf, ret+1);
args.size += ret; args.size += ret;
BuildSendAndCloseSoapResp(h, resp, args.size); BuildSendAndCloseSoapResp(h, resp, args.size);
search_error:
ClearNameValueList(&data); ClearNameValueList(&data);
if( orderBy ) if( orderBy )
free(orderBy); free(orderBy);