diff --git a/minidlnatypes.h b/minidlnatypes.h index dec1ba7..f16f9c4 100644 --- a/minidlnatypes.h +++ b/minidlnatypes.h @@ -44,6 +44,12 @@ struct runtime_vars_s { int notify_interval; /* seconds between SSDP announces */ }; +struct string_s { + char *data; // ptr to start of memory area + int off; + int size; +}; + enum media_types { ALL_MEDIA, AUDIO_ONLY, diff --git a/tivo_commands.c b/tivo_commands.c index 450ebea..3b9d5ca 100644 --- a/tivo_commands.c +++ b/tivo_commands.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "tivo_utils.h" @@ -84,7 +85,8 @@ SendRootContainer(struct upnphttp * h) "" "" "" - "", friendly_name, friendly_name, friendly_name, friendly_name); + "", + friendly_name, friendly_name, friendly_name, friendly_name); BuildResp_upnphttp(h, resp, len); free(resp); SendResp_upnphttp(h); @@ -110,8 +112,8 @@ int callback(void *args, int argc, char **argv, char **azColName) char *id = argv[0], *class = argv[1], *detailID = argv[2], *size = argv[3], *title = argv[4], *duration = argv[5], *bitrate = argv[6], *sampleFrequency = argv[7], *artist = argv[8], *album = argv[9], *genre = argv[10], *comment = argv[11], *date = argv[12], *resolution = argv[13], *mime = argv[14], *path = argv[15]; - char str_buf[4096]; int ret = 0; + struct string_s *str = passed_args->str; if( strncmp(class, "item", 4) == 0 ) { @@ -120,44 +122,34 @@ int callback(void *args, int argc, char **argv, char **azColName) if( strncmp(mime, "audio", 5) == 0 ) { flags |= FLAG_NO_PARAMS; - ret = sprintf(str_buf, "
" - "audio/*" - "%s" - "%s" - "%s", mime, size, title); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "
" + "audio/*" + "%s" + "%s" + "%s", mime, size, title); if( date ) { - ret = sprintf(str_buf, "%.*s", 4, date); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%.*s", 4, date); } } else if( strcmp(mime, "image/jpeg") == 0 ) { flags |= FLAG_SEND_RESIZED; - ret = sprintf(str_buf, "
" - "image/*" - "image/jpeg" - "%s", size); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "
" + "image/*" + "image/jpeg" + "%s", size); if( date ) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not strptime(date, "%Y-%m-%dT%H:%M:%S", &tm); - ret = sprintf(str_buf, "0x%X", (unsigned int)mktime(&tm)); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "0x%X", (unsigned int)mktime(&tm)); } if( comment ) { - ret = sprintf(str_buf, "%s", comment); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", comment); } } else if( strncmp(mime, "video", 5) == 0 ) @@ -165,111 +157,85 @@ int callback(void *args, int argc, char **argv, char **azColName) char *episode; flags |= FLAG_NO_PARAMS; flags |= FLAG_VIDEO; - ret = sprintf(str_buf, "
" - "video/x-tivo-mpeg" - "%s" - "%s", mime, size); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "
" + "video/x-tivo-mpeg" + "%s" + "%s", mime, size); episode = strstr(title, " - "); if( episode ) { - ret = sprintf(str_buf, "%.*s" - "%s", - episode-title, title, episode+3); + ret = strcatf(str, "%.*s" + "%s", + (int)(episode-title), title, episode+3); } else { - ret = sprintf(str_buf, "%s", title); + ret = strcatf(str, "%s", title); } - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; if( date ) { struct tm tm; memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; // Have libc figure out if DST is in effect or not strptime(date, "%Y-%m-%dT%H:%M:%S", &tm); - ret = sprintf(str_buf, "0x%X", (unsigned int)mktime(&tm)); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "0x%X", (unsigned int)mktime(&tm)); } if( comment ) { - ret = sprintf(str_buf, "%s", comment); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", comment); } } else { return 0; } - ret = sprintf(str_buf, "%s", unescape_tag(title)); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", unescape_tag(title)); if( artist ) { - ret = sprintf(str_buf, "%s", unescape_tag(artist)); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", unescape_tag(artist)); } if( album ) { - ret = sprintf(str_buf, "%s", unescape_tag(album)); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", unescape_tag(album)); } if( genre ) { - ret = sprintf(str_buf, "%s", unescape_tag(genre)); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", unescape_tag(genre)); } if( resolution ) { char *width = strsep(&resolution, "x"); - ret = sprintf(str_buf, "%s" - "%s", - width, resolution); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s" + "%s", + width, resolution); } if( duration ) { - ret = sprintf(str_buf, "%d", + ret = strcatf(str, "%d", atoi(strrchr(duration, '.')+1) + (1000*atoi(strrchr(duration, ':')+1)) + (60000*atoi(strrchr(duration, ':')-2)) + (3600000*atoi(duration))); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; } if( bitrate ) { - ret = sprintf(str_buf, "%s", bitrate); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", bitrate); } if( sampleFrequency ) { - ret = sprintf(str_buf, "%s", sampleFrequency); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%s", sampleFrequency); } - ret = sprintf(str_buf, "
" - "%s" - "/%s/%s.dat%s", - mime, - (flags & FLAG_SEND_RESIZED)?"Resized":"MediaItems", detailID, - (flags & FLAG_NO_PARAMS)?"No":""); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "
" + "%s" + "/%s/%s.dat%s", + mime, + (flags & FLAG_SEND_RESIZED)?"Resized":"MediaItems", detailID, + (flags & FLAG_NO_PARAMS)?"No":""); if( flags & FLAG_VIDEO ) { char *esc_name = escape_tag(basename(path), 1); - ret = sprintf(str_buf, "" - "video/*" - "urn:tivo:image:save-until-i-delete-recording" - "" - "Videos" - "%s ", esc_name); + ret = strcatf(str, "" + "video/*" + "urn:tivo:image:save-until-i-delete-recording" + "" + "Videos" + "%s ", esc_name); free(esc_name); } else { - ret = sprintf(str_buf, ""); + ret = strcatf(str, ""); } } else if( strncmp(class, "container", 9) == 0 ) @@ -283,26 +249,22 @@ int callback(void *args, int argc, char **argv, char **azColName) " (MIME in ('image/jpeg', 'audio/mpeg', 'video/mpeg', 'video/x-tivo-mpeg')" " or CLASS glob 'container*')", id); #endif - ret = sprintf(str_buf, "" - "
" - "x-container/folder" - "x-container/folder" - "%s" - "%d" - "
" - "" - "" - "/TiVoConnect?Command=QueryContainer&Container=%s" - "x-tivo-container/folder" - "" - "", - unescape_tag(title), count, id); + ret = strcatf(str, "" + "
" + "x-container/folder" + "x-container/folder" + "%s" + "%d" + "
" + "" + "" + "/TiVoConnect?Command=QueryContainer&Container=%s" + "x-tivo-container/folder" + "" + "", + unescape_tag(title), count, id); } - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; - ret = sprintf(str_buf, "
"); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "
"); passed_args->returned++; @@ -320,11 +282,13 @@ SendItemDetails(struct upnphttp * h, sqlite_int64 item) char *sql; char *zErrMsg = NULL; struct Response args; + struct string_s str; int ret; memset(&args, 0, sizeof(args)); + memset(&str, 0, sizeof(str)); - args.resp = resp; - args.size = sprintf(resp, "\n"); + str.data = resp; + ret = strcatf(&str, "\n"); args.requested = 1; asprintf(&sql, SELECT_COLUMNS "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" @@ -337,10 +301,10 @@ SendItemDetails(struct upnphttp * h, sqlite_int64 item) DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } - strcat(resp, ""); + strcatf(&str, ""); - BuildResp_upnphttp(h, resp, strlen(resp)); - free(resp); + BuildResp_upnphttp(h, str.data, str.off); + free(str.data); SendResp_upnphttp(h); } @@ -358,12 +322,15 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite char type[8]; char groupBy[19] = {0}; struct Response args; + struct string_s str; int totalMatches = 0; int i, ret; memset(&args, 0, sizeof(args)); + memset(&str, 0, sizeof(str)); - args.resp = resp; - args.size = 1024; + args.str = &str; + str.data = resp+1024; + str.size = 262144-1024; if( itemCount >= 0 ) { args.requested = itemCount; @@ -653,6 +620,7 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite free(resp); return; } + strcatf(&str, ""); ret = sprintf(str_buf, "\n" "" @@ -665,15 +633,12 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite "%d" "%d", type, totalMatches, title, args.start, args.returned); - args.resp = resp+1024-ret; - memcpy(args.resp, &str_buf, ret); - ret = sprintf(str_buf, ""); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; - args.size -= args.resp-resp; + str.data -= ret; + memcpy(str.data, &str_buf, ret); + str.size = str.off+ret; free(title); free(which); - BuildResp_upnphttp(h, args.resp, args.size); + BuildResp_upnphttp(h, str.data, str.size); free(resp); SendResp_upnphttp(h); } @@ -696,7 +661,7 @@ ProcessTiVoCommand(struct upnphttp * h, const char * orig_path) item = strtok_r( path, "&", &saveptr ); while( item != NULL ) { - if( strlen(item) == 0 ) + if( *item == '\0' ) { item = strtok_r( NULL, "&", &saveptr ); continue; diff --git a/upnpsoap.c b/upnpsoap.c index 1014374..b36ce22 100644 --- a/upnpsoap.c +++ b/upnpsoap.c @@ -61,13 +61,12 @@ #include "config.h" #include "upnpglobalvars.h" +#include "utils.h" #include "upnphttp.h" #include "upnpsoap.h" #include "upnpreplyparse.h" #include "getifaddr.h" - #include "scanner.h" -#include "utils.h" #include "sql.h" #include "log.h" @@ -555,17 +554,13 @@ inline static void add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn, char *detailID, struct Response *passed_args) { - int ret; int dstw = reqw; int dsth = reqh; - char str_buf[256]; if( passed_args->flags & FLAG_NO_RESIZE ) return; - ret = sprintf(str_buf, "<res "); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + strcatf(passed_args->str, "<res "); if( passed_args->filter & FILTER_RES_RESOLUTION ) { dstw = reqw; @@ -574,17 +569,12 @@ add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn, dsth = reqh; dstw = (((reqh<<10)/srch) * srcw>>10); } - ret = sprintf(str_buf, "resolution=\"%dx%d\" ", dstw, dsth); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + strcatf(passed_args->str, "resolution=\"%dx%d\" ", dstw, dsth); } - ret = sprintf(str_buf, "protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=%s;DLNA.ORG_CI=1\">" - "http://%s:%d/Resized/%s.jpg?width=%d,height=%d" - "</res>", - dlna_pn, lan_addr[0].str, runtime_vars.port, - detailID, dstw, dsth); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + strcatf(passed_args->str, "protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=%s;DLNA.ORG_CI=1\">" + "http://%s:%d/Resized/%s.jpg?width=%d,height=%d" + "</res>", + dlna_pn, lan_addr[0].str, runtime_vars.port, detailID, dstw, dsth); } inline static void @@ -592,48 +582,30 @@ add_res(char *size, char *duration, char *bitrate, char *sampleFrequency, char *nrAudioChannels, char *resolution, char *dlna_pn, char *mime, char *detailID, char *ext, struct Response *passed_args) { - int ret; - char str_buf[256]; - - ret = sprintf(str_buf, "<res "); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + strcatf(passed_args->str, "<res "); 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; + strcatf(passed_args->str, "size=\"%s\" ", size); } 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; + strcatf(passed_args->str, "duration=\"%s\" ", duration); } if( bitrate && (passed_args->filter & FILTER_RES_BITRATE) ) { - ret = sprintf(str_buf, "bitrate=\"%s\" ", bitrate); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + strcatf(passed_args->str, "bitrate=\"%s\" ", bitrate); } 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; + strcatf(passed_args->str, "sampleFrequency=\"%s\" ", sampleFrequency); } 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; + strcatf(passed_args->str, "nrAudioChannels=\"%s\" ", nrAudioChannels); } 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; + strcatf(passed_args->str, "resolution=\"%s\" ", resolution); } - ret = snprintf(str_buf, sizeof(str_buf), "protocolInfo=\"http-get:*:%s:%s\">" - "http://%s:%d/MediaItems/%s.%s" - "</res>", - mime, dlna_pn, lan_addr[0].str, runtime_vars.port, detailID, ext); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + strcatf(passed_args->str, "protocolInfo=\"http-get:*:%s:%s\">" + "http://%s:%d/MediaItems/%s.%s" + "</res>", + mime, dlna_pn, lan_addr[0].str, + runtime_vars.port, detailID, ext); } #define SELECT_COLUMNS "SELECT o.OBJECT_ID, o.PARENT_ID, o.REF_ID, o.DETAIL_ID, o.CLASS," \ @@ -651,21 +623,22 @@ callback(void *args, int argc, char **argv, char **azColName) *tn = argv[18], *creator = argv[19], *dlna_pn = argv[20], *mime = argv[21], *album_art = argv[22]; char dlna_buf[96]; char ext[5]; - char str_buf[512]; + struct string_s *str = passed_args->str; int ret = 0; - /* Make sure we have at least 4KB left of allocated memory to finish the response. */ - if( passed_args->size > (passed_args->alloced - 4096) ) + /* Make sure we have at least 8KB left of allocated memory to finish the response. */ + if( str->off > (str->size - 8192) ) { #if MAX_RESPONSE_SIZE > 0 - if( (passed_args->alloced+1048576) <= MAX_RESPONSE_SIZE ) + if( (str->size+DEFAULT_RESP_SIZE) <= MAX_RESPONSE_SIZE ) { #endif - passed_args->resp = realloc(passed_args->resp, (passed_args->alloced+1048576)); - if( passed_args->resp ) + str->data = realloc(str->data, (str->off+DEFAULT_RESP_SIZE)); + if( str->data ) { - passed_args->alloced += 1048576; - DPRINTF(E_DEBUG, L_HTTP, "HUGE RESPONSE ALERT: UPnP SOAP response had to be enlarged to %d. [%d results so far]\n", passed_args->alloced, passed_args->returned); + str->size += DEFAULT_RESP_SIZE; + DPRINTF(E_DEBUG, L_HTTP, "UPnP SOAP response enlarged to %d. [%d results so far]\n", + str->size, passed_args->returned); } else { @@ -739,120 +712,85 @@ callback(void *args, int argc, char **argv, char **azColName) } } - ret = snprintf(str_buf, 512, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<item id=\"%s\" parentID=\"%s\" restricted=\"1\"", id, parent); 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; + ret = strcatf(str, " refID=\"%s\"", refID); } - ret = snprintf(str_buf, 512, ">" - "<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; + ret = strcatf(str, ">" + "<dc:title>%s</dc:title>" + "<upnp:class>object.%s</upnp:class>", + title, class); if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) { - ret = snprintf(str_buf, 512, "<dc:description>%.384s</dc:description>", comment); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<dc:description>%.384s</dc:description>", comment); } if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) { - ret = snprintf(str_buf, 512, "<dc:creator>%s</dc:creator>", creator); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<dc:creator>%s</dc:creator>", creator); } if( date && (passed_args->filter & FILTER_DC_DATE) ) { - ret = snprintf(str_buf, 512, "<dc:date>%s</dc:date>", date); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<dc:date>%s</dc:date>", date); } if( artist ) { if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ACTOR) ) { - ret = snprintf(str_buf, 512, "<upnp:actor>%s</upnp:actor>", artist); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:actor>%s</upnp:actor>", artist); } if( passed_args->filter & FILTER_UPNP_ARTIST ) { - ret = snprintf(str_buf, 512, "<upnp:artist>%s</upnp:artist>", artist); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:artist>%s</upnp:artist>", artist); } } if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) { - ret = snprintf(str_buf, 512, "<upnp:album>%s</upnp:album>", album); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:album>%s</upnp:album>", album); } if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) { - ret = snprintf(str_buf, 512, "<upnp:genre>%s</upnp:genre>", genre); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:genre>%s</upnp:genre>", genre); } if( strncmp(id, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) { track = strrchr(id, '$')+1; } 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; + ret = strcatf(str, "<upnp:originalTrackNumber>%s</upnp:originalTrackNumber>", track); } if( album_art && atoi(album_art) ) { /* Video and audio album art is handled differently */ if( *mime == 'v' && (passed_args->filter & FILTER_RES) && !(passed_args->flags & FLAG_MS_PFS) ) { - ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN\">" - "http://%s:%d/AlbumArt/%s-%s.jpg" - "</res>", - lan_addr[0].str, runtime_vars.port, album_art, detailID); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<res protocolInfo=\"http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN\">" + "http://%s:%d/AlbumArt/%s-%s.jpg" + "</res>", + lan_addr[0].str, runtime_vars.port, album_art, detailID); } else if( 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; + ret = strcatf(str, "<upnp:albumArtURI"); if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) { - ret = sprintf(str_buf, " dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"", "JPEG_TN"); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, " dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""); } - ret = sprintf(str_buf, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>", - lan_addr[0].str, runtime_vars.port, album_art, detailID); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>", + lan_addr[0].str, runtime_vars.port, album_art, detailID); } } #ifdef PFS_HACK if( (passed_args->flags & FLAG_MS_PFS) && *mime == 'i' ) { - ret = snprintf(str_buf, 512, "<upnp:album>%s</upnp:album>", "[No Keywords]"); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:album>%s</upnp:album>", "[No Keywords]"); if( tn && atoi(tn) ) { - ret = snprintf(str_buf, 512, "<upnp:albumArtURI>" - "http://%s:%d/Thumbnails/%s.jpg" - "</upnp:albumArtURI>", - lan_addr[0].str, runtime_vars.port, detailID); + ret = strcatf(str, "<upnp:albumArtURI>" + "http://%s:%d/Thumbnails/%s.jpg" + "</upnp:albumArtURI>", + lan_addr[0].str, runtime_vars.port, detailID); } else { - ret = snprintf(str_buf, 512, "<upnp:albumArtURI>" - "http://%s:%d/Resized/%s.jpg?width=160,height=160" - "</upnp:albumArtURI>", - lan_addr[0].str, runtime_vars.port, detailID); + ret = strcatf(str, "<upnp:albumArtURI>" + "http://%s:%d/Resized/%s.jpg?width=160,height=160" + "</upnp:albumArtURI>", + lan_addr[0].str, runtime_vars.port, detailID); } - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; } #endif if( passed_args->filter & FILTER_RES ) { mime_to_ext(mime, ext); if( (passed_args->client == EFreeBox) && tn && atoi(tn) ) { - ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:%s:%s\">" - "http://%s:%d/Thumbnails/%s.jpg" - "</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 = strcatf(str, "<res protocolInfo=\"http-get:*:%s:%s\">" + "http://%s:%d/Thumbnails/%s.jpg" + "</res>", + mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, + runtime_vars.port, detailID); } add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels, resolution, dlna_buf, mime, detailID, ext, passed_args); @@ -868,12 +806,11 @@ callback(void *args, int argc, char **argv, char **azColName) } #endif if( tn && atoi(tn) ) { - ret = sprintf(str_buf, "<res protocolInfo=\"http-get:*:%s:%s\">" - "http://%s:%d/Thumbnails/%s.jpg" - "</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 = strcatf(str, "<res protocolInfo=\"http-get:*:%s:%s\">" + "http://%s:%d/Thumbnails/%s.jpg" + "</res>", + mime, "DLNA.ORG_PN=JPEG_TN", lan_addr[0].str, + runtime_vars.port, detailID); } } else if( *mime == 'v' ) { @@ -947,74 +884,52 @@ callback(void *args, int argc, char **argv, char **azColName) } } } - ret = sprintf(str_buf, "</item>"); + ret = strcatf(str, "</item>"); } else if( strncmp(class, "container", 9) == 0 ) { - 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; + ret = strcatf(str, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent); if( passed_args->filter & FILTER_CHILDCOUNT ) { int children; ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s';", id); children = (ret > 0) ? ret : 0; - ret = sprintf(str_buf, "childCount=\"%d\"", children); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "childCount=\"%d\"", children); } /* 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 & FILTER_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; + ret = strcatf(str, ">" + "<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 = snprintf(str_buf, 512, ">" - "<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; + ret = strcatf(str, ">" + "<dc:title>%s</dc:title>" + "<upnp:class>object.%s</upnp:class>", + title, class); if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) { - ret = snprintf(str_buf, 512, "<dc:creator>%s</dc:creator>", creator); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<dc:creator>%s</dc:creator>", creator); } if( genre && (passed_args->filter & FILTER_UPNP_GENRE) ) { - ret = snprintf(str_buf, 512, "<upnp:genre>%s</upnp:genre>", genre); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:genre>%s</upnp:genre>", genre); } if( artist && (passed_args->filter & FILTER_UPNP_ARTIST) ) { - ret = snprintf(str_buf, 512, "<upnp:artist>%s</upnp:artist>", artist); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "<upnp:artist>%s</upnp:artist>", artist); } 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; + ret = strcatf(str, "<upnp:albumArtURI "); if( passed_args->filter & FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID ) { - ret = sprintf(str_buf, "dlna:profileID=\"%s\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\"", "JPEG_TN"); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "dlna:profileID=\"JPEG_TN\" xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\""); } - ret = sprintf(str_buf, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>", - lan_addr[0].str, runtime_vars.port, album_art, detailID); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, ">http://%s:%d/AlbumArt/%s-%s.jpg</upnp:albumArtURI>", + lan_addr[0].str, runtime_vars.port, album_art, detailID); } - ret = sprintf(str_buf, "</container>"); + ret = strcatf(str, "</container>"); } - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; return 0; } @@ -1028,16 +943,13 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) "" "<DIDL-Lite" CONTENT_DIRECTORY_SCHEMAS; - - char *resp = malloc(1048576); - char str_buf[512]; char *zErrMsg = 0; char *sql, *ptr; int ret; struct Response args; + struct string_s str; int totalMatches; struct NameValueParserData data; - *resp = '\0'; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); char * ObjectId = GetValueFromNameValueList(&data, "ObjectID"); @@ -1068,21 +980,19 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) goto browse_error; } memset(&args, 0, sizeof(args)); + memset(&str, 0, sizeof(str)); - args.alloced = 1048576; - args.resp = resp; - args.size = sprintf(resp, "%s", resp0); + str.data = malloc(DEFAULT_RESP_SIZE); + str.size = DEFAULT_RESP_SIZE; + str.off = sprintf(str.data, "%s", resp0); + args.str = &str; /* See if we need to include DLNA namespace reference */ args.filter = set_filter_flags(Filter, h->req_client); if( args.filter & FILTER_DLNA_NAMESPACE ) { - ret = sprintf(str_buf, DLNA_NAMESPACE); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; + ret = strcatf(&str, DLNA_NAMESPACE); } - ret = sprintf(str_buf, ">\n"); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; + strcatf(&str, ">\n"); args.returned = 0; args.requested = RequestedCount; @@ -1188,22 +1098,19 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) goto browse_error; } } - ret = snprintf(str_buf, sizeof(str_buf), "</DIDL-Lite>\n" - "%u\n" - "%u\n" - "%u" - "", - args.returned, totalMatches, updateID); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; - BuildSendAndCloseSoapResp(h, resp, args.size); + ret = strcatf(&str, "</DIDL-Lite>\n" + "%u\n" + "%u\n" + "%u" + "", + args.returned, totalMatches, updateID); + BuildSendAndCloseSoapResp(h, str.data, str.off); browse_error: ClearNameValueList(&data); - if( orderBy ) - free(orderBy); if( args.flags & FLAG_FREE_OBJECT_ID ) sqlite3_free(ObjectId); - free(resp); + free(orderBy); + free(str.data); } static void @@ -1215,16 +1122,13 @@ SearchContentDirectory(struct upnphttp * h, const char * action) "" "<DIDL-Lite" CONTENT_DIRECTORY_SCHEMAS; - - char *resp = malloc(1048576); char *zErrMsg = 0; char *sql, *ptr; char **result; - char str_buf[4096]; - int ret; struct Response args; + struct string_s str; int totalMatches = 0; - *resp = '\0'; + int ret; struct NameValueParserData data; ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); @@ -1254,26 +1158,24 @@ SearchContentDirectory(struct upnphttp * h, const char * action) } } memset(&args, 0, sizeof(args)); + memset(&str, 0, sizeof(str)); - args.alloced = 1048576; - args.resp = resp; - args.size = sprintf(resp, "%s", resp0); + str.data = malloc(DEFAULT_RESP_SIZE); + str.size = DEFAULT_RESP_SIZE; + str.off = sprintf(str.data, "%s", resp0); /* See if we need to include DLNA namespace reference */ args.filter = set_filter_flags(Filter, h->req_client); if( args.filter & FILTER_DLNA_NAMESPACE ) { - ret = sprintf(str_buf, DLNA_NAMESPACE); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; + ret = strcatf(&str, DLNA_NAMESPACE); } - ret = sprintf(str_buf, ">\n"); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; + ret = strcatf(&str, ">\n"); args.returned = 0; args.requested = RequestedCount; args.client = h->req_client; args.flags = h->reqflags; + args.str = &str; if( h->reqflags & FLAG_MS_PFS ) { if( strchr(ContainerID, '$') || (strcmp(ContainerID, "0") == 0) ) @@ -1346,13 +1248,13 @@ SearchContentDirectory(struct upnphttp * h, const char * action) if( strstr(SearchCriteria, "@id") ) { newSearchCriteria = strdup(SearchCriteria); - newSearchCriteria = SearchCriteria = modifyString(newSearchCriteria, "@id", "OBJECT_ID", 0); + SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "@id", "OBJECT_ID", 0); } if( strstr(SearchCriteria, "res is ") ) { if( !newSearchCriteria ) newSearchCriteria = strdup(SearchCriteria); - newSearchCriteria = SearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0); + SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0); } #if 0 // Does 360 need this? if( strstr(SearchCriteria, "&") ) @@ -1367,14 +1269,16 @@ SearchContentDirectory(struct upnphttp * h, const char * action) } DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria); - sprintf(str_buf, "SELECT (select count(distinct DETAIL_ID) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" - " where (OBJECT_ID glob '%s$*') and (%s))" - " + " - "(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" - " where (OBJECT_ID = '%s') and (%s))", - ContainerID, SearchCriteria, ContainerID, SearchCriteria); + sql = sqlite3_mprintf("SELECT (select count(distinct DETAIL_ID)" + " from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" + " where (OBJECT_ID glob '%s$*') and (%s))" + " + " + "(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" + " where (OBJECT_ID = '%s') and (%s))", + ContainerID, SearchCriteria, ContainerID, SearchCriteria); //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, sql, &result, NULL, NULL); + sqlite3_free(sql); if( ret == SQLITE_OK ) { totalMatches = atoi(result[1]); @@ -1428,27 +1332,20 @@ SearchContentDirectory(struct upnphttp * h, const char * action) sqlite3_free(zErrMsg); } sqlite3_free(sql); - strcat(resp, str_buf); - ret = snprintf(str_buf, sizeof(str_buf), "</DIDL-Lite>\n" - "%u\n" - "%u\n" - "%u" - "", - args.returned, totalMatches, updateID); - memcpy(resp+args.size, &str_buf, ret+1); - args.size += ret; - BuildSendAndCloseSoapResp(h, resp, args.size); + ret = strcatf(&str, "</DIDL-Lite>\n" + "%u\n" + "%u\n" + "%u" + "", + args.returned, totalMatches, updateID); + BuildSendAndCloseSoapResp(h, str.data, str.off); search_error: ClearNameValueList(&data); - if( orderBy ) - free(orderBy); - if( newSearchCriteria ) - free(newSearchCriteria); - free(resp); if( h->reqflags & FLAG_MS_PFS ) - { sqlite3_free(ContainerID); - } + free(orderBy); + free(newSearchCriteria); + free(str.data); } /* diff --git a/upnpsoap.h b/upnpsoap.h index d2c006d..9f75c03 100644 --- a/upnpsoap.h +++ b/upnpsoap.h @@ -21,7 +21,8 @@ #ifndef __UPNPSOAP_H__ #define __UPNPSOAP_H__ -#define MAX_RESPONSE_SIZE 1048576 +#define DEFAULT_RESP_SIZE 131072 +#define MAX_RESPONSE_SIZE 2097152 #define CONTENT_DIRECTORY_SCHEMAS \ " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" \ @@ -32,14 +33,12 @@ struct Response { - char *resp; + struct string_s *str; int start; int returned; int requested; - int size; - int alloced; - u_int32_t filter; - u_int32_t flags; + uint32_t filter; + uint32_t flags; enum client_types client; }; diff --git a/utils.c b/utils.c index f0e8788..136fbda 100644 --- a/utils.c +++ b/utils.c @@ -31,6 +31,20 @@ #include "upnpglobalvars.h" #include "log.h" +inline int +strcatf(struct string_s *str, const char *fmt, ...) +{ + int ret; + va_list ap; + + va_start(ap, fmt); + ret = vsnprintf(str->data + str->off, str->size - str->off, fmt, ap); + str->off += ret; + va_end(ap); + + return ret; +} + int ends_with(const char * haystack, const char * needle) { diff --git a/utils.h b/utils.h index abc4260..05ebb13 100644 --- a/utils.h +++ b/utils.h @@ -24,6 +24,9 @@ #ifndef __UTILS_H__ #define __UTILS_H__ +int +strcatf(struct string_s *str, char *fmt, ...); + int ends_with(const char * haystack, const char * needle);