From 8d12ee4ae733d09e39fa5f8ea0212b588ecec390 Mon Sep 17 00:00:00 2001 From: Justin Maggard Date: Wed, 1 Apr 2009 20:19:00 +0000 Subject: [PATCH] * 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. --- upnpsoap.c | 439 ++++++++++++++++++++++++++++++++++------------------- upnpsoap.h | 3 +- 2 files changed, 285 insertions(+), 157 deletions(-) diff --git a/upnpsoap.c b/upnpsoap.c index af5992a..bd263a5 100644 --- a/upnpsoap.c +++ b/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) "" "<DIDL-Lite" CONTENT_DIRECTORY_SCHEMAS; - static const char resp1[] = "</DIDL-Lite>"; - static const char resp2[] = ""; 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%u\n" - "%u\n" - "%u", - 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>\n" + "%u\n" + "%u\n" + "%u" + "", + 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) "" "<DIDL-Lite" CONTENT_DIRECTORY_SCHEMAS; - static const char resp1[] = "</DIDL-Lite>"; - static const char resp2[] = ""; 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%u\n" - "%u\n" - "%u", - 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>\n" + "%u\n" + "%u\n" + "%u" + "", + 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); diff --git a/upnpsoap.h b/upnpsoap.h index 689ca71..c910213 100644 --- a/upnpsoap.h +++ b/upnpsoap.h @@ -21,7 +21,8 @@ struct Response int returned; int requested; int total; - char *filter; + int size; + u_int32_t filter; enum clientType client; };