* Major optimization for large Search/Browse responses. Using strcat gets very slow when the data size gets too big. Switching to memcpy gives us a huge performance improvement with very large responses.
This commit is contained in:
parent
047b9511a6
commit
8d12ee4ae7
439
upnpsoap.c
439
upnpsoap.c
@ -203,7 +203,80 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action)
|
||||
BuildSendAndCloseSoapResp(h, body, bodylen);
|
||||
}
|
||||
|
||||
static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
#define FILTER_CHILDCOUNT 0x00000001
|
||||
#define FILTER_DC_CREATOR 0x00000002
|
||||
#define FILTER_DC_DATE 0x00000004
|
||||
#define FILTER_DC_DESCRIPTION 0x00000008
|
||||
#define FILTER_DLNA_NAMESPACE 0x00000010
|
||||
#define FILTER_REFID 0x00000020
|
||||
#define FILTER_RES 0x00000040
|
||||
#define FILTER_RES_BITRATE 0x00000080
|
||||
#define FILTER_RES_DURATION 0x00000100
|
||||
#define FILTER_RES_NRAUDIOCHANNELS 0x00000200
|
||||
#define FILTER_RES_RESOLUTION 0x00000400
|
||||
#define FILTER_RES_SAMPLEFREQUENCY 0x00000800
|
||||
#define FILTER_RES_SIZE 0x00001000
|
||||
#define FILTER_UPNP_ALBUM 0x00002000
|
||||
#define FILTER_UPNP_ALBUMARTURI 0x00004000
|
||||
#define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00008000
|
||||
#define FILTER_UPNP_ARTIST 0x00010000
|
||||
#define FILTER_UPNP_GENRE 0x00020000
|
||||
#define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00040000
|
||||
#define FILTER_UPNP_SEARCHCLASS 0x00080000
|
||||
|
||||
static u_int32_t
|
||||
set_filter_flags(char * filter)
|
||||
{
|
||||
u_int32_t flags = 0;
|
||||
|
||||
if( !filter || (strlen(filter) <= 1) )
|
||||
return 0xFFFFFFFF;
|
||||
if( strstr(filter, "@childCount") )
|
||||
flags |= FILTER_CHILDCOUNT;
|
||||
if( strstr(filter, "dc:creator") )
|
||||
flags |= FILTER_DC_CREATOR;
|
||||
if( strstr(filter, "dc:date") )
|
||||
flags |= FILTER_DC_DATE;
|
||||
if( strstr(filter, "dc:description") )
|
||||
flags |= FILTER_DC_DESCRIPTION;
|
||||
if( strstr(filter, "dlna") )
|
||||
flags |= FILTER_DLNA_NAMESPACE;
|
||||
if( strstr(filter, "@refID") )
|
||||
flags |= FILTER_REFID;
|
||||
if( strstr(filter, "res") )
|
||||
flags |= FILTER_RES;
|
||||
if( strstr(filter, "res@bitrate") )
|
||||
flags |= FILTER_RES_BITRATE;
|
||||
if( strstr(filter, "res@duration") )
|
||||
flags |= FILTER_RES_DURATION;
|
||||
if( strstr(filter, "res@nrAudioChannels") )
|
||||
flags |= FILTER_RES_NRAUDIOCHANNELS;
|
||||
if( strstr(filter, "res@resolution") )
|
||||
flags |= FILTER_RES_RESOLUTION;
|
||||
if( strstr(filter, "res@sampleFrequency") )
|
||||
flags |= FILTER_RES_SAMPLEFREQUENCY;
|
||||
if( strstr(filter, "res@size") )
|
||||
flags |= FILTER_RES_SIZE;
|
||||
if( strstr(filter, "upnp:album") )
|
||||
flags |= FILTER_UPNP_ALBUM;
|
||||
if( strstr(filter, "upnp:albumArtURI") )
|
||||
flags |= FILTER_UPNP_ALBUMARTURI;
|
||||
if( strstr(filter, "upnp:albumArtURI@dlna:profileID") )
|
||||
flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID;
|
||||
if( strstr(filter, "upnp:artist") )
|
||||
flags |= FILTER_UPNP_ARTIST;
|
||||
if( strstr(filter, "upnp:genre") )
|
||||
flags |= FILTER_UPNP_GENRE;
|
||||
if( strstr(filter, "upnp:originalTrackNumber") )
|
||||
flags |= FILTER_UPNP_ORIGINALTRACKNUMBER;
|
||||
if( strstr(filter, "upnp:searchClass") )
|
||||
flags |= FILTER_UPNP_SEARCHCLASS;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static int
|
||||
callback(void *args, int argc, char **argv, char **azColName)
|
||||
{
|
||||
struct Response *passed_args = (struct Response *)args;
|
||||
char *id = argv[1], *parent = argv[2], *refID = argv[3], *class = argv[4], *detailID = argv[5], *size = argv[9], *title = argv[10],
|
||||
@ -211,14 +284,22 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
*genre = argv[16], *comment = argv[17], *nrAudioChannels = argv[18], *track = argv[19], *date = argv[20], *resolution = argv[21],
|
||||
*tn = argv[22], *creator = argv[23], *dlna_pn = argv[24], *mime = argv[25], *album_art = argv[26], *art_dlna_pn = argv[27];
|
||||
char dlna_buf[64];
|
||||
char str_buf[4096];
|
||||
char str_buf[512];
|
||||
char **result;
|
||||
int ret;
|
||||
int children, ret = 0;
|
||||
static int warned = 0;
|
||||
|
||||
passed_args->total++;
|
||||
|
||||
if( passed_args->requested && (passed_args->returned >= passed_args->requested) )
|
||||
return 0;
|
||||
/* Make sure we have at least 4KB left of allocated memory to finish the response. */
|
||||
if( passed_args->size > 1044480 && !warned )
|
||||
{
|
||||
DPRINTF(E_ERROR, L_HTTP, "UPnP SOAP response is getting too big! [%d returned]\n", passed_args->returned);
|
||||
warned = 1;
|
||||
return 0;
|
||||
}
|
||||
passed_args->returned++;
|
||||
|
||||
if( dlna_pn )
|
||||
@ -238,92 +319,115 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
mime[9] = '\0';
|
||||
}
|
||||
}
|
||||
sprintf(str_buf, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( refID && (!passed_args->filter || strstr(passed_args->filter, "@refID")) ) {
|
||||
sprintf(str_buf, " refID=\"%s\"", refID);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
ret = sprintf(str_buf, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( refID && (passed_args->filter & FILTER_REFID) ) {
|
||||
ret = sprintf(str_buf, " refID=\"%s\"", refID);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
sprintf(str_buf, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
"<upnp:class>object.%s</upnp:class>",
|
||||
title, class);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( comment && (!passed_args->filter || strstr(passed_args->filter, "dc:description")) ) {
|
||||
sprintf(str_buf, "<dc:description>%s</dc:description>", comment);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
ret = sprintf(str_buf, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
"<upnp:class>object.%s</upnp:class>",
|
||||
title, class);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
|
||||
ret = sprintf(str_buf, "<dc:description>%s</dc:description>", comment);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
|
||||
sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
|
||||
ret = sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( date && (!passed_args->filter || strstr(passed_args->filter, "dc:date")) ) {
|
||||
sprintf(str_buf, "<dc:date>%s</dc:date>", date);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( date && (passed_args->filter & FILTER_DC_DATE) ) {
|
||||
ret = sprintf(str_buf, "<dc:date>%s</dc:date>", date);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
|
||||
sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
|
||||
ret = sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( album && (!passed_args->filter || strstr(passed_args->filter, "upnp:album")) ) {
|
||||
sprintf(str_buf, "<upnp:album>%s</upnp:album>", album);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
|
||||
ret = sprintf(str_buf, "<upnp:album>%s</upnp:album>", album);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
|
||||
sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
|
||||
ret = sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( track && atoi(track) && (!passed_args->filter || strstr(passed_args->filter, "upnp:originalTrackNumber")) ) {
|
||||
sprintf(str_buf, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( track && atoi(track) && (passed_args->filter & FILTER_UPNP_ORIGINALTRACKNUMBER) ) {
|
||||
ret = sprintf(str_buf, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
|
||||
strcat(passed_args->resp, "<upnp:albumArtURI ");
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) {
|
||||
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
|
||||
ret = sprintf(str_buf, "<upnp:albumArtURI ");
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
|
||||
ret = sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
sprintf(str_buf, ">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
ret = sprintf(str_buf, ">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
lan_addr[0].str, runtime_vars.port, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "res") ) {
|
||||
strcat(passed_args->resp, "<res ");
|
||||
if( size && (!passed_args->filter || strstr(passed_args->filter, "res@size")) ) {
|
||||
sprintf(str_buf, "size=\"%s\" ", size);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( passed_args->filter & FILTER_RES ) {
|
||||
ret = sprintf(str_buf, "<res ");
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( size && (passed_args->filter & FILTER_RES_SIZE) ) {
|
||||
ret = sprintf(str_buf, "size=\"%s\" ", size);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( duration && (!passed_args->filter || strstr(passed_args->filter, "res@duration")) ) {
|
||||
sprintf(str_buf, "duration=\"%s\" ", duration);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( duration && (passed_args->filter & FILTER_RES_DURATION) ) {
|
||||
ret = sprintf(str_buf, "duration=\"%s\" ", duration);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( bitrate && (!passed_args->filter || strstr(passed_args->filter, "res@bitrate")) ) {
|
||||
if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) {
|
||||
if( passed_args->client == EXbox )
|
||||
sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024);
|
||||
ret = sprintf(str_buf, "bitrate=\"%d\" ", atoi(bitrate)/1024);
|
||||
else
|
||||
sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( sampleFrequency && (!passed_args->filter || strstr(passed_args->filter, "res@sampleFrequency")) ) {
|
||||
sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( sampleFrequency && (passed_args->filter & FILTER_RES_SAMPLEFREQUENCY) ) {
|
||||
ret = sprintf(str_buf, "sampleFrequency=\"%s\" ", sampleFrequency);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( nrAudioChannels && (!passed_args->filter || strstr(passed_args->filter, "res@nrAudioChannels")) ) {
|
||||
sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( nrAudioChannels && (passed_args->filter & FILTER_RES_NRAUDIOCHANNELS) ) {
|
||||
ret = sprintf(str_buf, "nrAudioChannels=\"%s\" ", nrAudioChannels);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( resolution && (!passed_args->filter || strstr(passed_args->filter, "res@resolution")) ) {
|
||||
sprintf(str_buf, "resolution=\"%s\" ", resolution);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( resolution && (passed_args->filter & FILTER_RES_RESOLUTION) ) {
|
||||
ret = sprintf(str_buf, "resolution=\"%s\" ", resolution);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/MediaItems/%s.dat"
|
||||
"</res>",
|
||||
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
|
||||
ret = sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/MediaItems/%s.dat"
|
||||
"</res>",
|
||||
mime, dlna_buf, lan_addr[0].str, runtime_vars.port, detailID);
|
||||
#if 0 //JPEG_RESIZE
|
||||
if( dlna_pn && (strncmp(dlna_pn, "JPEG_LRG", 8) == 0) ) {
|
||||
strcat(passed_args->resp, str_buf);
|
||||
sprintf(str_buf, "<res "
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
ret = sprintf(str_buf, "<res "
|
||||
"protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/Resized/%s"
|
||||
"</res>",
|
||||
@ -331,70 +435,90 @@ static int callback(void *args, int argc, char **argv, char **azColName)
|
||||
}
|
||||
#endif
|
||||
if( tn && atoi(tn) && dlna_pn ) {
|
||||
strcat(passed_args->resp, str_buf);
|
||||
strcat(passed_args->resp, "<res ");
|
||||
sprintf(str_buf, "protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/Thumbnails/%s.dat"
|
||||
"</res>",
|
||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:%s:%s\">"
|
||||
"http://%s:%d/Thumbnails/%s.dat"
|
||||
"</res>",
|
||||
mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, runtime_vars.port, detailID);
|
||||
}
|
||||
strcat(passed_args->resp, str_buf);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
strcpy(str_buf, "</item>");
|
||||
ret = sprintf(str_buf, "</item>");
|
||||
}
|
||||
else if( strncmp(class, "container", 9) == 0 )
|
||||
{
|
||||
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
|
||||
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
|
||||
sprintf(str_buf, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "@childCount"))
|
||||
ret = sprintf(str_buf, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( passed_args->filter & FILTER_CHILDCOUNT )
|
||||
{
|
||||
sprintf(str_buf, "childCount=\"%s\"", result[1]);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
sprintf(str_buf, "SELECT count(ID) from OBJECTS where PARENT_ID = '%s';", id);
|
||||
ret = sql_get_table(db, str_buf, &result, NULL, NULL);
|
||||
if( ret == SQLITE_OK ) {
|
||||
children = atoi(result[1]);
|
||||
sqlite3_free_table(result);
|
||||
}
|
||||
else {
|
||||
children = 0;
|
||||
}
|
||||
ret = sprintf(str_buf, "childCount=\"%d\"", children);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
/* If the client calls for BrowseMetadata on root, we have to include our "upnp:searchClass"'s, unless they're filtered out */
|
||||
if( (passed_args->requested == 1) && (strcmp(id, "0") == 0) )
|
||||
{
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:searchClass") )
|
||||
if( passed_args->filter & FILTER_UPNP_SEARCHCLASS )
|
||||
{
|
||||
strcat(passed_args->resp, ">"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
||||
ret = sprintf(str_buf, ">"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.audioItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.imageItem</upnp:searchClass>"
|
||||
"<upnp:searchClass includeDerived=\"1\">object.item.videoItem</upnp:searchClass");
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
}
|
||||
sprintf(str_buf, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
"<upnp:class>object.%s</upnp:class>",
|
||||
title, class);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( creator && (!passed_args->filter || strstr(passed_args->filter, "dc:creator")) ) {
|
||||
sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
ret = sprintf(str_buf, ">"
|
||||
"<dc:title>%s</dc:title>"
|
||||
"<upnp:class>object.%s</upnp:class>",
|
||||
title, class);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
|
||||
ret = sprintf(str_buf, "<dc:creator>%s</dc:creator>", creator);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( genre && (!passed_args->filter || strstr(passed_args->filter, "upnp:genre")) ) {
|
||||
sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) {
|
||||
ret = sprintf(str_buf, "<upnp:genre>%s</upnp:genre>", genre);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( artist && (!passed_args->filter || strstr(passed_args->filter, "upnp:artist")) ) {
|
||||
sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) {
|
||||
ret = sprintf(str_buf, "<upnp:artist>%s</upnp:artist>", artist);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
if( album_art && atoi(album_art) && (!passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI")) ) {
|
||||
strcat(passed_args->resp, "<upnp:albumArtURI ");
|
||||
if( !passed_args->filter || strstr(passed_args->filter, "upnp:albumArtURI@dlna:profileID") ) {
|
||||
sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
if( album_art && atoi(album_art) && (passed_args->filter & FILTER_UPNP_ALBUMARTURI) ) {
|
||||
ret = sprintf(str_buf, "<upnp:albumArtURI ");
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) {
|
||||
ret = sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlnaorg:metadata-1-0/\"", art_dlna_pn);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
sprintf(str_buf, ">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
ret = sprintf(str_buf, ">http://%s:%d/AlbumArt/%s.jpg</upnp:albumArtURI>",
|
||||
lan_addr[0].str, runtime_vars.port, album_art);
|
||||
strcat(passed_args->resp, str_buf);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
}
|
||||
sprintf(str_buf, "</container>");
|
||||
sqlite3_free_table(result);
|
||||
ret = sprintf(str_buf, "</container>");
|
||||
}
|
||||
strcat(passed_args->resp, str_buf);
|
||||
memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1);
|
||||
passed_args->size += ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -408,11 +532,9 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
"<Result>"
|
||||
"<DIDL-Lite"
|
||||
CONTENT_DIRECTORY_SCHEMAS;
|
||||
static const char resp1[] = "</DIDL-Lite></Result>";
|
||||
static const char resp2[] = "</u:BrowseResponse>";
|
||||
|
||||
char *resp = malloc(1048576);
|
||||
char str_buf[4096];
|
||||
char str_buf[512];
|
||||
char *zErrMsg = 0;
|
||||
char *sql;
|
||||
int ret;
|
||||
@ -429,20 +551,25 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria");
|
||||
if( !ObjectId )
|
||||
ObjectId = GetValueFromNameValueList(&data, "ContainerID");
|
||||
|
||||
memset(str_buf, '\0', sizeof(str_buf));
|
||||
memset(&args, 0, sizeof(args));
|
||||
strcpy(resp, resp0);
|
||||
|
||||
args.resp = resp;
|
||||
args.size = sprintf(resp, "%s", resp0);
|
||||
/* See if we need to include DLNA namespace reference */
|
||||
if( Filter && ((strlen(Filter) <= 1) || strstr(Filter, "dlna")) )
|
||||
strcat(resp, DLNA_NAMESPACE);
|
||||
strcat(resp, ">\n");
|
||||
args.filter = set_filter_flags(Filter);
|
||||
if( args.filter & FILTER_DLNA_NAMESPACE )
|
||||
{
|
||||
ret = sprintf(str_buf, DLNA_NAMESPACE);
|
||||
memcpy(resp+args.size, &str_buf, ret+1);
|
||||
args.size += ret;
|
||||
}
|
||||
ret = sprintf(str_buf, ">\n");
|
||||
memcpy(resp+args.size, &str_buf, ret+1);
|
||||
args.size += ret;
|
||||
|
||||
args.total = StartingIndex;
|
||||
args.returned = 0;
|
||||
args.requested = RequestedCount;
|
||||
args.resp = NULL;
|
||||
args.filter = NULL;
|
||||
args.client = h->req_client;
|
||||
if( h->req_client == EXbox )
|
||||
{
|
||||
@ -463,10 +590,6 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
ObjectId, RequestedCount, StartingIndex,
|
||||
BrowseFlag, Filter, SortCriteria);
|
||||
|
||||
if( Filter && (strlen(Filter) > 1) )
|
||||
args.filter = Filter;
|
||||
|
||||
args.resp = resp;
|
||||
if( strcmp(BrowseFlag, "BrowseMetadata") == 0 )
|
||||
{
|
||||
args.requested = 1;
|
||||
@ -486,14 +609,15 @@ BrowseContentDirectory(struct upnphttp * h, const char * action)
|
||||
DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg);
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
strcat(resp, resp1);
|
||||
sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n"
|
||||
"<TotalMatches>%u</TotalMatches>\n"
|
||||
"<UpdateID>%u</UpdateID>",
|
||||
args.returned, args.total, updateID);
|
||||
strcat(resp, str_buf);
|
||||
strcat(resp, resp2);
|
||||
BuildSendAndCloseSoapResp(h, resp, strlen(resp));
|
||||
ret = snprintf(str_buf, sizeof(str_buf), "</DIDL-Lite></Result>\n"
|
||||
"<NumberReturned>%u</NumberReturned>\n"
|
||||
"<TotalMatches>%u</TotalMatches>\n"
|
||||
"<UpdateID>%u</UpdateID>"
|
||||
"</u:BrowseResponse>",
|
||||
args.returned, args.total, updateID);
|
||||
memcpy(resp+args.size, &str_buf, ret+1);
|
||||
args.size += ret;
|
||||
BuildSendAndCloseSoapResp(h, resp, args.size);
|
||||
ClearNameValueList(&data);
|
||||
free(resp);
|
||||
if( h->req_client == EXbox )
|
||||
@ -511,8 +635,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
"<Result>"
|
||||
"<DIDL-Lite"
|
||||
CONTENT_DIRECTORY_SCHEMAS;
|
||||
static const char resp1[] = "</DIDL-Lite></Result>";
|
||||
static const char resp2[] = "</u:SearchResponse>";
|
||||
|
||||
char *resp = malloc(1048576);
|
||||
char *zErrMsg = 0;
|
||||
@ -534,11 +656,23 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
|
||||
args.total = 0;
|
||||
args.resp = resp;
|
||||
args.size = sprintf(resp, "%s", resp0);
|
||||
/* See if we need to include DLNA namespace reference */
|
||||
args.filter = set_filter_flags(Filter);
|
||||
if( args.filter & FILTER_DLNA_NAMESPACE )
|
||||
{
|
||||
ret = sprintf(str_buf, DLNA_NAMESPACE);
|
||||
memcpy(resp+args.size, &str_buf, ret+1);
|
||||
args.size += ret;
|
||||
}
|
||||
ret = sprintf(str_buf, ">\n");
|
||||
memcpy(resp+args.size, &str_buf, ret+1);
|
||||
args.size += ret;
|
||||
|
||||
args.total = StartingIndex;
|
||||
args.returned = 0;
|
||||
args.requested = RequestedCount;
|
||||
args.resp = NULL;
|
||||
args.filter = NULL;
|
||||
args.client = h->req_client;
|
||||
if( h->req_client == EXbox )
|
||||
{
|
||||
@ -563,14 +697,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
ContainerID, RequestedCount, StartingIndex,
|
||||
SearchCriteria, Filter, SortCriteria);
|
||||
|
||||
strcpy(resp, resp0);
|
||||
/* See if we need to include DLNA namespace reference */
|
||||
if( Filter && ((strlen(Filter) <= 1) || strstr(Filter, "dlna")) )
|
||||
strcat(resp, DLNA_NAMESPACE);
|
||||
strcat(resp, ">\n");
|
||||
|
||||
if( Filter && (strlen(Filter) > 1) )
|
||||
args.filter = Filter;
|
||||
if( strcmp(ContainerID, "0") == 0 )
|
||||
*ContainerID = '*';
|
||||
if( !SearchCriteria )
|
||||
@ -603,7 +729,6 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
}
|
||||
DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria);
|
||||
|
||||
args.resp = resp;
|
||||
sql = sqlite3_mprintf("SELECT distinct * from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)"
|
||||
" where OBJECT_ID glob '%s$*' and (%s) group by DETAIL_ID "
|
||||
"%z"
|
||||
@ -621,14 +746,16 @@ SearchContentDirectory(struct upnphttp * h, const char * action)
|
||||
sqlite3_free(zErrMsg);
|
||||
}
|
||||
sqlite3_free(sql);
|
||||
strcat(resp, resp1);
|
||||
sprintf(str_buf, "\n<NumberReturned>%u</NumberReturned>\n"
|
||||
"<TotalMatches>%u</TotalMatches>\n"
|
||||
"<UpdateID>%u</UpdateID>",
|
||||
args.returned, args.total, updateID);
|
||||
strcat(resp, str_buf);
|
||||
strcat(resp, resp2);
|
||||
BuildSendAndCloseSoapResp(h, resp, strlen(resp));
|
||||
ret = snprintf(str_buf, sizeof(str_buf), "</DIDL-Lite></Result>\n"
|
||||
"<NumberReturned>%u</NumberReturned>\n"
|
||||
"<TotalMatches>%u</TotalMatches>\n"
|
||||
"<UpdateID>%u</UpdateID>"
|
||||
"</u:SearchResponse>",
|
||||
args.returned, args.total, updateID);
|
||||
memcpy(resp+args.size, &str_buf, ret+1);
|
||||
args.size += ret;
|
||||
BuildSendAndCloseSoapResp(h, resp, args.size);
|
||||
ClearNameValueList(&data);
|
||||
if( newSearchCriteria )
|
||||
free(newSearchCriteria);
|
||||
|
@ -21,7 +21,8 @@ struct Response
|
||||
int returned;
|
||||
int requested;
|
||||
int total;
|
||||
char *filter;
|
||||
int size;
|
||||
u_int32_t filter;
|
||||
enum clientType client;
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user