From eec8a9ebc6cdfcb8b23232cdd452296f5f87d849 Mon Sep 17 00:00:00 2001 From: Shibby Date: Mon, 13 Jun 2011 12:26:22 +0200 Subject: [PATCH] MiniDLNA update: 1.0.19.1 to 1.0.20 --- release/src/router/minidlna/NEWS | 23 + release/src/router/minidlna/albumart.c | 12 +- release/src/router/minidlna/daemonize.c | 2 +- release/src/router/minidlna/getifaddr.c | 4 +- release/src/router/minidlna/image_utils.c | 46 +- release/src/router/minidlna/image_utils.h | 14 +- release/src/router/minidlna/inotify.c | 146 +++--- release/src/router/minidlna/metadata.c | 142 +++--- release/src/router/minidlna/minidlna.c | 65 ++- release/src/router/minidlna/minidlnatypes.h | 9 + release/src/router/minidlna/minissdp.c | 29 +- release/src/router/minidlna/po/LINGUAS | 2 +- release/src/router/minidlna/po/da.po | 2 +- release/src/router/minidlna/po/de.po | 2 +- release/src/router/minidlna/po/es.po | 1 - release/src/router/minidlna/po/fr.po | 4 +- release/src/router/minidlna/po/it.po | 14 +- release/src/router/minidlna/po/ja.po | 14 +- release/src/router/minidlna/po/nb.po | 2 +- release/src/router/minidlna/po/nl.po | 2 +- release/src/router/minidlna/po/ru.po | 14 +- release/src/router/minidlna/po/sl.po | 4 +- release/src/router/minidlna/po/sv.po | 2 +- release/src/router/minidlna/scanner.c | 227 +++++---- release/src/router/minidlna/scanner.h | 2 +- .../src/router/minidlna/tagutils/tagutils-asf.c | 88 ++-- .../src/router/minidlna/tagutils/tagutils-flc.c | 2 - .../src/router/minidlna/tagutils/tagutils-ogg.c | 7 +- release/src/router/minidlna/tagutils/tagutils.h | 7 +- release/src/router/minidlna/tagutils/textutils.c | 1 + release/src/router/minidlna/tivo_beacon.c | 31 +- release/src/router/minidlna/tivo_commands.c | 246 +++++----- release/src/router/minidlna/tivo_utils.c | 2 + release/src/router/minidlna/upnpdescgen.c | 25 +- release/src/router/minidlna/upnpglobalvars.h | 29 +- release/src/router/minidlna/upnphttp.c | 475 +++++++++---------- release/src/router/minidlna/upnphttp.h | 8 +- release/src/router/minidlna/upnpsoap.c | 509 +++++++++------------ release/src/router/minidlna/upnpsoap.h | 12 +- release/src/router/minidlna/utils.c | 25 +- release/src/router/minidlna/utils.h | 5 +- 41 files changed, 1109 insertions(+), 1147 deletions(-) create mode 100644 release/src/router/minidlna/NEWS diff --git a/release/src/router/minidlna/NEWS b/release/src/router/minidlna/NEWS new file mode 100644 index 0000000000..6391f04985 --- /dev/null +++ b/release/src/router/minidlna/NEWS @@ -0,0 +1,23 @@ +1.0.20 - Released 09-June-2011 +-------------------------------- +- Fix a crash bug when scanning MPEG-TS files with odd packet sizes. +- Fix AVI file streaming on Samsung A-Series TV's. +- Improve support for the NETGEAR Digital Entertainer Live (EVA2000). +- Add support for multiple network interfaces. +- Add subtitle support for LG TV's and Blu-Ray players. +- Fix some minor coding issues found by cppcheck. +- Add client adaptation support for Toshiba Regza TV's. +- Send known audio-only devices straight to the Music section on root requests. +- Add client adaptation support for Roku SoundBridge audio clients. +- Improve Sony client adaptation to allow support for more file types. +- Add support for reading tags from MP4 video files with recent lavf versions. +- Add support for Samsung's GetFeatureList method. + +1.0.19 - Released 11-Mar-2011 +-------------------------------- +- When called with -R, only remove art_cache and files.db in case users use an + imporant directory as their db dir. +- Properly scan newly created directory symlinks. +- Improve Windows 7 interoperability. +- Add basic NLS support, so clients can display localized strings. +- Optimize JPEG scaling by downscaling as much as possible during decompression. diff --git a/release/src/router/minidlna/albumart.c b/release/src/router/minidlna/albumart.c index 1ff67b4cb5..9ba5c8f81b 100644 --- a/release/src/router/minidlna/albumart.c +++ b/release/src/router/minidlna/albumart.c @@ -44,10 +44,10 @@ art_cache_exists(const char * orig_path, char ** cache_file) } char * -save_resized_album_art(image * imsrc, const char * path) +save_resized_album_art(image_s * imsrc, const char * path) { int dstw, dsth; - image * imdst; + image_s * imdst; char * cache_file; char * cache_dir; @@ -174,8 +174,7 @@ check_embedded_art(const char * path, const char * image_data, int image_size) char * art_path = NULL; char * cache_dir; FILE * dstfile; - image * imsrc; - size_t nwritten; + image_s * imsrc; static char last_path[PATH_MAX]; static unsigned int last_hash = 0; static int last_success = 0; @@ -193,7 +192,7 @@ check_embedded_art(const char * path, const char * image_data, int image_size) if( !last_success ) return NULL; art_cache_exists(path, &art_path); - if( link(last_path, art_path) == 0 || (errno == EEXIST) ) + if( link(last_path, art_path) == 0 ) { return(art_path); } @@ -229,6 +228,7 @@ check_embedded_art(const char * path, const char * image_data, int image_size) } else if( width > 0 && height > 0 ) { + size_t nwritten; if( art_cache_exists(path, &art_path) ) goto end_art; cache_dir = strdup(art_path); @@ -272,7 +272,7 @@ check_for_album_file(char * dir, const char * path) { char * file = malloc(PATH_MAX); struct album_art_name_s * album_art_name; - image * imsrc = NULL; + image_s * imsrc = NULL; int width=0, height=0; char * art_file; diff --git a/release/src/router/minidlna/daemonize.c b/release/src/router/minidlna/daemonize.c index 70ca49c797..b40f31ffe1 100644 --- a/release/src/router/minidlna/daemonize.c +++ b/release/src/router/minidlna/daemonize.c @@ -93,7 +93,7 @@ writepidfile(const char * fname, int pid) if(!fname || (strlen(fname) == 0)) return -1; - if( (pidfile = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) + if( (pidfile = open(fname, O_WRONLY|O_CREAT, 0644)) < 0) { DPRINTF(E_INFO, L_GENERAL, "Unable to open pidfile for writing %s: %s\n", fname, strerror(errno)); return -1; diff --git a/release/src/router/minidlna/getifaddr.c b/release/src/router/minidlna/getifaddr.c index 068422badc..f0cbdfcdbb 100644 --- a/release/src/router/minidlna/getifaddr.c +++ b/release/src/router/minidlna/getifaddr.c @@ -1,4 +1,4 @@ -/* $Id: getifaddr.c,v 1.13 2011/04/11 22:52:34 jmaggard Exp $ */ +/* $Id: getifaddr.c,v 1.14 2011/05/02 23:50:52 jmaggard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * @@ -217,7 +217,7 @@ get_remote_mac(struct in_addr ip_addr, unsigned char * mac) return 1; while( !feof(arp) ) { - matches = fscanf(arp, "%s 0x%X 0x%X %hhx:%hhx:%hhx:%hhx:%hhx:%hhx", + matches = fscanf(arp, "%15s 0x%8X 0x%8X %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", remote_ip, &hwtype, &flags, &mac[0], &mac[1], &mac[2], &mac[3], &mac[4], &mac[5]); if( matches != 9 ) diff --git a/release/src/router/minidlna/image_utils.c b/release/src/router/minidlna/image_utils.c index 8ce4bb6f70..79b4625180 100644 --- a/release/src/router/minidlna/image_utils.c +++ b/release/src/router/minidlna/image_utils.c @@ -203,14 +203,14 @@ libjpeg_error_handler(j_common_ptr cinfo) } void -image_free(image *pimage) +image_free(image_s *pimage) { free(pimage->buf); free(pimage); } pix -get_pix(image *pimage, int32_t x, int32_t y) +get_pix(image_s *pimage, int32_t x, int32_t y) { if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height)) { @@ -224,7 +224,7 @@ get_pix(image *pimage, int32_t x, int32_t y) } void -put_pix_alpha_replace(image *pimage, int32_t x, int32_t y, pix col) +put_pix_alpha_replace(image_s *pimage, int32_t x, int32_t y, pix col) { if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height)) pimage->buf[(y * pimage->width) + x] = col; @@ -295,7 +295,7 @@ image_get_jpeg_date_xmp(const char * path, char ** date) { FILE *img; unsigned char buf[8]; - char *data = NULL; + char *data = NULL, *newdata; u_int16_t offset; struct NameValueParserData xml; char * exif; @@ -337,7 +337,11 @@ image_get_jpeg_date_xmp(const char * path, char ** date) continue; } - data = realloc(data, 30); + newdata = realloc(data, 30); + if( !newdata ) + break; + data = newdata; + fread(data, 29, 1, img); offset -= 29; if( strcmp(data, "http://ns.adobe.com/xap/1.0/") != 0 ) @@ -346,7 +350,10 @@ image_get_jpeg_date_xmp(const char * path, char ** date) continue; } - data = realloc(data, offset+1); + newdata = realloc(data, offset+1); + if( !newdata ) + break; + data = newdata; fread(data, offset, 1, img); ParseNameValue(data, offset, &xml); @@ -378,12 +385,12 @@ image_get_jpeg_date_xmp(const char * path, char ** date) return ret; } -image * +image_s * image_new(int32_t width, int32_t height) { - image *vimage; + image_s *vimage; - if((vimage = (image *)malloc(sizeof(image))) == NULL) + if((vimage = (image_s *)malloc(sizeof(image_s))) == NULL) { DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); return NULL; @@ -399,10 +406,10 @@ image_new(int32_t width, int32_t height) return(vimage); } -image * +image_s * image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, int scale) { - image *vimage; + image_s *vimage; FILE *file = NULL; struct jpeg_decompress_struct cinfo; unsigned char *line[16], *ptr; @@ -478,6 +485,9 @@ image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height + 8)) == NULL) { DPRINTF(E_WARN, L_METADATA, "malloc failed\n"); + image_free(vimage); + if( is_file ) + fclose(file); return NULL; } @@ -541,7 +551,7 @@ image_new_from_jpeg(const char * path, int is_file, const char * buf, int size, } void -image_upsize(image * pdest, image * psrc, int32_t width, int32_t height) +image_upsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height) { int32_t vx, vy; #if !defined __i386__ && !defined __x86_64__ @@ -604,7 +614,7 @@ image_upsize(image * pdest, image * psrc, int32_t width, int32_t height) } void -image_downsize(image * pdest, image * psrc, int32_t width, int32_t height) +image_downsize(image_s * pdest, image_s * psrc, int32_t width, int32_t height) { int32_t vx, vy; pix vcol; @@ -747,10 +757,10 @@ image_downsize(image * pdest, image * psrc, int32_t width, int32_t height) } } -image * -image_resize(image * src_image, int32_t width, int32_t height) +image_s * +image_resize(image_s * src_image, int32_t width, int32_t height) { - image * dst_image; + image_s * dst_image; dst_image = image_new(width, height); if( !dst_image ) @@ -765,7 +775,7 @@ image_resize(image * src_image, int32_t width, int32_t height) unsigned char * -image_save_to_jpeg_buf(image * pimage, int * size) +image_save_to_jpeg_buf(image_s * pimage, int * size) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -813,7 +823,7 @@ image_save_to_jpeg_buf(image * pimage, int * size) } int -image_save_to_jpeg_file(image * pimage, const char * path) +image_save_to_jpeg_file(image_s * pimage, const char * path) { int nwritten, size = 0; unsigned char * buf; diff --git a/release/src/router/minidlna/image_utils.h b/release/src/router/minidlna/image_utils.h index 74d684e5eb..40ffaeac6f 100644 --- a/release/src/router/minidlna/image_utils.h +++ b/release/src/router/minidlna/image_utils.h @@ -29,10 +29,10 @@ typedef struct { int32_t width; int32_t height; pix *buf; -} image; +} image_s; void -image_free(image *pimage); +image_free(image_s *pimage); int image_get_jpeg_date_xmp(const char * path, char ** date); @@ -40,14 +40,14 @@ image_get_jpeg_date_xmp(const char * path, char ** date); int image_get_jpeg_resolution(const char * path, int * width, int * height); -image * +image_s * image_new_from_jpeg(const char * path, int is_file, const char * ptr, int size, int scale); -image * -image_resize(image * src_image, int32_t width, int32_t height); +image_s * +image_resize(image_s * src_image, int32_t width, int32_t height); unsigned char * -image_save_to_jpeg_buf(image * pimage, int * size); +image_save_to_jpeg_buf(image_s * pimage, int * size); int -image_save_to_jpeg_file(image * pimage, const char * path); +image_save_to_jpeg_file(image_s * pimage, const char * path); diff --git a/release/src/router/minidlna/inotify.c b/release/src/router/minidlna/inotify.c index 07b9c06619..0da4126371 100644 --- a/release/src/router/minidlna/inotify.c +++ b/release/src/router/minidlna/inotify.c @@ -154,7 +154,7 @@ inotify_create_watches(int fd) max_watches = fopen("/proc/sys/fs/inotify/max_user_watches", "r"); if( max_watches ) { - fscanf(max_watches, "%u", &watch_limit); + fscanf(max_watches, "%10u", &watch_limit); fclose(max_watches); if( (watch_limit < DESIRED_WATCH_LIMIT) || (watch_limit < (num_watches*3/4)) ) { @@ -276,16 +276,15 @@ int add_dir_watch(int fd, char * path, char * filename) int inotify_insert_file(char * name, const char * path) { - char * sql; - char **result; - int rows; - char * last_dir = strdup(path); - char * path_buf = strdup(path); - char * base_name = malloc(strlen(path)); - char * base_copy = base_name; + int len; + char * last_dir; + char * path_buf; + char * base_name; + char * base_copy; char * parent_buf = NULL; char * id = NULL; int depth = 1; + int ts; enum media_types type = ALL_MEDIA; struct media_dir_s * media_path = media_dirs; struct stat st; @@ -337,74 +336,59 @@ inotify_insert_file(char * name, const char * path) if( stat(path, &st) != 0 ) return -1; - sql = sqlite3_mprintf("SELECT TIMESTAMP from DETAILS where PATH = '%q'", path); - if( sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK ) + ts = sql_get_int_field(db, "SELECT TIMESTAMP from DETAILS where PATH = '%q'", path); + if( !ts && is_playlist(path) && (sql_get_int_field(db, "SELECT ID from PLAYLISTS where PATH = '%q'", path) > 0) ) { - if( rows ) - { - if( atoi(result[1]) < st.st_mtime ) - { - DPRINTF(E_DEBUG, L_INOTIFY, "%s is newer than the last db entry.\n", path); - inotify_remove_file(path_buf); - } - else - { - free(last_dir); - free(path_buf); - free(base_name); - sqlite3_free(sql); - sqlite3_free_table(result); - return -1; - } - } - else if( is_playlist(path) && (sql_get_int_field(db, "SELECT ID from PLAYLISTS where PATH = '%q'", path) > 0) ) - { - DPRINTF(E_DEBUG, L_INOTIFY, "Re-reading modified playlist.\n", path); - inotify_remove_file(path_buf); - next_pl_fill = 1; - } - sqlite3_free_table(result); + DPRINTF(E_DEBUG, L_INOTIFY, "Re-reading modified playlist.\n", path); + inotify_remove_file(path); + next_pl_fill = 1; } - sqlite3_free(sql); + else if( ts < st.st_mtime ) + { + if( ts > 0 ) + DPRINTF(E_DEBUG, L_INOTIFY, "%s is newer than the last db entry.\n", path); + inotify_remove_file(path); + } + /* Find the parentID. If it's not found, create all necessary parents. */ + len = strlen(path)+1; + if( !(path_buf = malloc(len)) || + !(last_dir = malloc(len)) || + !(base_name = malloc(len)) ) + return -1; + base_copy = base_name; while( depth ) { depth = 0; strcpy(path_buf, path); parent_buf = dirname(path_buf); - strcpy(last_dir, path_buf); do { //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Checking %s\n", parent_buf); - sql = sqlite3_mprintf("SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" - " where d.PATH = '%q' and REF_ID is NULL", parent_buf); - if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) && rows ) + id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" + " where d.PATH = '%q' and REF_ID is NULL", parent_buf); + if( id ) { - id = strdup(result[1]); - sqlite3_free_table(result); - sqlite3_free(sql); if( !depth ) break; - DPRINTF(E_DEBUG, L_INOTIFY, "Found first known parentID: %s\n", id); + DPRINTF(E_DEBUG, L_INOTIFY, "Found first known parentID: %s [%s]\n", id, parent_buf); /* Insert newly-found directory */ strcpy(base_name, last_dir); base_copy = basename(base_name); insert_directory(base_copy, last_dir, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id)); - free(id); + sqlite3_free(id); break; } depth++; - strcpy(last_dir, path_buf); + strcpy(last_dir, parent_buf); parent_buf = dirname(parent_buf); - sqlite3_free_table(result); - sqlite3_free(sql); } while( strcmp(parent_buf, "/") != 0 ); if( strcmp(parent_buf, "/") == 0 ) { - id = strdup(BROWSEDIR_ID); + id = sqlite3_mprintf("%s", BROWSEDIR_ID); depth = 0; break; } @@ -418,7 +402,7 @@ inotify_insert_file(char * name, const char * path) { //DEBUG DPRINTF(E_DEBUG, L_INOTIFY, "Inserting %s\n", name); insert_file(name, path, id+2, get_next_available_id("OBJECTS", id)); - free(id); + sqlite3_free(id); if( (is_audio(path) || is_playlist(path)) && next_pl_fill != 1 ) { next_pl_fill = time(NULL) + 120; // Schedule a playlist scan for 2 minutes from now. @@ -433,11 +417,8 @@ inotify_insert_directory(int fd, char *name, const char * path) { DIR * ds; struct dirent * e; - char * sql; - char **result; - char *id=NULL, *path_buf, *parent_buf, *esc_name; + char *id, *path_buf, *parent_buf, *esc_name; int wd; - int rows; enum file_types type = TYPE_UNKNOWN; enum media_types dir_type = ALL_MEDIA; struct media_dir_s * media_path; @@ -448,18 +429,20 @@ inotify_insert_directory(int fd, char *name, const char * path) DPRINTF(E_WARN, L_INOTIFY, "Could not access %s [%s]\n", path, strerror(errno)); return -1; } - - parent_buf = dirname(strdup(path)); - sql = sqlite3_mprintf("SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" - " where d.PATH = '%q' and REF_ID is NULL", parent_buf); - if( sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK ) + if( sql_get_int_field(db, "SELECT ID from DETAILS where PATH = '%q'", path) > 0 ) { - id = strdup(rows?result[1]:BROWSEDIR_ID); - insert_directory(name, path, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id)); - free(id); + DPRINTF(E_DEBUG, L_INOTIFY, "%s already exists\n", path); + return 0; } + + parent_buf = strdup(path); + id = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" + " where d.PATH = '%q' and REF_ID is NULL", dirname(parent_buf)); + if( !id ) + id = sqlite3_mprintf("%s", BROWSEDIR_ID); + insert_directory(name, path, BROWSEDIR_ID, id+2, get_next_available_id("OBJECTS", id)); + sqlite3_free(id); free(parent_buf); - sqlite3_free(sql); wd = add_watch(fd, path); if( wd == -1 ) @@ -492,9 +475,7 @@ inotify_insert_directory(int fd, char *name, const char * path) { if( e->d_name[0] == '.' ) continue; - esc_name = escape_tag(e->d_name); - if( !esc_name ) - esc_name = strdup(e->d_name); + esc_name = escape_tag(e->d_name, 1); asprintf(&path_buf, "%s/%s", path, e->d_name); switch( e->d_type ) { @@ -531,24 +512,19 @@ inotify_remove_file(const char * path) char * sql; char **result; char * art_cache; - sqlite_int64 detailID = 0; - int i, rows, children, playlist, ret = 1; + char * ptr; + sqlite_int64 detailID; + int rows, playlist; /* Invalidate the scanner cache so we don't insert files into non-existent containers */ valid_cache = 0; playlist = is_playlist(path); - sql = sqlite3_mprintf("SELECT ID from %s where PATH = '%q'", playlist?"PLAYLISTS":"DETAILS", path); - if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) ) - { - if( rows ) - { - detailID = strtoll(result[1], NULL, 10); - ret = 0; - } - sqlite3_free_table(result); - } + sql = sql_get_text_field(db, "SELECT ID from %s where PATH = '%q'", playlist?"PLAYLISTS":"DETAILS", path); + if( !sql ) + return 1; + detailID = strtoll(sql, NULL, 10); sqlite3_free(sql); - if( playlist && detailID ) + if( playlist ) { sql_exec(db, "DELETE from PLAYLISTS where ID = %lld", detailID); sql_exec(db, "DELETE from DETAILS where ID =" @@ -557,12 +533,13 @@ inotify_remove_file(const char * path) sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s$%lld' or PARENT_ID = '%s$%lld'", MUSIC_PLIST_ID, detailID, MUSIC_PLIST_ID, detailID); } - else if( detailID ) + else { /* Delete the parent containers if we are about to empty them. */ asprintf(&sql, "SELECT PARENT_ID from OBJECTS where DETAIL_ID = %lld", detailID); if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) ) { + int i, children; for( i=1; i <= rows; i++ ) { /* If it's a playlist item, adjust the item count of the playlist */ @@ -581,7 +558,9 @@ inotify_remove_file(const char * path) " (SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s')", result[i]); sql_exec(db, "DELETE from OBJECTS where OBJECT_ID = '%s'", result[i]); - *rindex(result[i], '$') = '\0'; + ptr = strrchr(result[i], '$'); + if( ptr ) + *ptr = '\0'; if( sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", result[i]) == 0 ) { sql_exec(db, "DELETE from DETAILS where ID =" @@ -601,7 +580,7 @@ inotify_remove_file(const char * path) remove(art_cache); free(art_cache); - return ret; + return 0; } int @@ -705,10 +684,7 @@ start_inotify() DPRINTF(E_DEBUG, L_INOTIFY, "The directory %s was %s.\n", path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created")); begin_scan(); - /* This could be a directory created by auto-mount. - * It will be empty until the drive is mounted to this directory. - * So let's wait a few seconds to allow mount to complete. - */ + sleep(5); if ( wait_for_mount(path_buf) >= 0 ) inotify_insert_directory(pollfds[0].fd, esc_name, path_buf); end_scan(); diff --git a/release/src/router/minidlna/metadata.c b/release/src/router/minidlna/metadata.c index 3adb4a706e..049b2e2d39 100644 --- a/release/src/router/minidlna/metadata.c +++ b/release/src/router/minidlna/metadata.c @@ -137,10 +137,8 @@ is_tivo_file(const char * path) void check_for_captions(const char * path, sqlite_int64 detailID) { - char * sql; - char * file = malloc(PATH_MAX); - char **result; - int ret, rows; + char *file = malloc(PATH_MAX); + char *id = NULL; sprintf(file, "%s", path); strip_ext(file); @@ -148,25 +146,18 @@ check_for_captions(const char * path, sqlite_int64 detailID) /* If we weren't given a detail ID, look for one. */ if( !detailID ) { - sql = sqlite3_mprintf("SELECT ID from DETAILS where PATH glob '%q.*'" - " and MIME glob 'video/*' limit 1", file); - ret = sql_get_table(db, sql, &result, &rows, NULL); - if( ret == SQLITE_OK ) + id = sql_get_text_field(db, "SELECT ID from DETAILS where PATH glob '%q.*'" + " and MIME glob 'video/*' limit 1", file); + if( id ) { - if( rows ) - { - detailID = strtoll(result[1], NULL, 10); - //DEBUG DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like a caption file.\n", path); - } - /*else - { - DPRINTF(E_DEBUG, L_METADATA, "No file found for caption %s.\n", path); - }*/ - sqlite3_free_table(result); + //DEBUG DPRINTF(E_DEBUG, L_METADATA, "New file %s looks like a caption file.\n", path); + detailID = strtoll(id, NULL, 10); } - sqlite3_free(sql); - if( !detailID ) + else + { + //DPRINTF(E_DEBUG, L_METADATA, "No file found for caption %s.\n", path); goto no_source_video; + } } strcat(file, ".srt"); @@ -178,6 +169,8 @@ check_for_captions(const char * path, sqlite_int64 detailID) " (%lld, %Q)", detailID, file); } no_source_video: + if( id ) + sqlite3_free(id); free(file); } @@ -336,10 +329,10 @@ GetAudioMetadata(const char * path, char * name) strcpy(type, "wav"); m.mime = strdup("audio/x-wav"); } - else if( ends_with(path, ".ogg") ) + else if( ends_with(path, ".ogg") || ends_with(path, ".oga") ) { strcpy(type, "ogg"); - m.mime = strdup("application/ogg"); + m.mime = strdup("audio/ogg"); } else if( ends_with(path, ".pcm") ) { @@ -381,7 +374,7 @@ GetAudioMetadata(const char * path, char * name) if( song.title && *song.title ) { m.title = trim(song.title); - if( (esc_tag = escape_tag(m.title)) ) + if( (esc_tag = escape_tag(m.title, 0)) ) { free_flags |= FLAG_TITLE; m.title = esc_tag; @@ -401,7 +394,7 @@ GetAudioMetadata(const char * path, char * name) m.creator = strdup("Various Artists"); free_flags |= FLAG_CREATOR; } - else if( (esc_tag = escape_tag(m.creator)) ) + else if( (esc_tag = escape_tag(m.creator, 0)) ) { m.creator = esc_tag; free_flags |= FLAG_CREATOR; @@ -422,7 +415,7 @@ GetAudioMetadata(const char * path, char * name) m.artist = strdup("Various Artists"); free_flags |= FLAG_ARTIST; } - else if( (esc_tag = escape_tag(m.artist)) ) + else if( (esc_tag = escape_tag(m.artist, 0)) ) { m.artist = esc_tag; free_flags |= FLAG_ARTIST; @@ -432,7 +425,7 @@ GetAudioMetadata(const char * path, char * name) if( song.album && *song.album ) { m.album = trim(song.album); - if( (esc_tag = escape_tag(m.album)) ) + if( (esc_tag = escape_tag(m.album, 0)) ) { free_flags |= FLAG_ALBUM; m.album = esc_tag; @@ -441,7 +434,7 @@ GetAudioMetadata(const char * path, char * name) if( song.genre && *song.genre ) { m.genre = trim(song.genre); - if( (esc_tag = escape_tag(m.genre)) ) + if( (esc_tag = escape_tag(m.genre, 0)) ) { free_flags |= FLAG_GENRE; m.genre = esc_tag; @@ -450,7 +443,7 @@ GetAudioMetadata(const char * path, char * name) if( song.comment && *song.comment ) { m.comment = trim(song.comment); - if( (esc_tag = escape_tag(m.comment)) ) + if( (esc_tag = escape_tag(m.comment, 0)) ) { free_flags |= FLAG_COMMENT; m.comment = esc_tag; @@ -506,7 +499,7 @@ GetImageMetadata(const char * path, char * name) char b[1024]; struct stat file; sqlite_int64 ret; - image * imsrc; + image_s * imsrc; metadata_t m; uint32_t free_flags = 0xFFFFFFFF; memset(&m, '\0', sizeof(metadata_t)); @@ -646,11 +639,7 @@ GetVideoMetadata(const char * path, char * name) int audio_stream = -1, video_stream = -1; enum audio_profiles audio_profile = PROFILE_AUDIO_UNKNOWN; tsinfo_t *ts; - ts_timestamp_t ts_timestamp = NONE; char fourcc[4]; - int off; - int duration, hours, min, sec, ms; - aac_object_type_t aac_type = AAC_INVALID; sqlite_int64 album_art = 0; char nfo[PATH_MAX], *ext; struct song_metadata video; @@ -719,6 +708,7 @@ GetVideoMetadata(const char * path, char * name) if( ac ) { + aac_object_type_t aac_type = AAC_INVALID; switch( ac->codec_id ) { case CODEC_ID_MP3: @@ -807,6 +797,9 @@ GetVideoMetadata(const char * path, char * name) } if( vc ) { + int off; + int duration, hours, min, sec, ms; + ts_timestamp_t ts_timestamp = NONE; DPRINTF(E_DEBUG, L_METADATA, "Container: '%s' [%s]\n", ctx->iformat->name, basename(path)); asprintf(&m.resolution, "%dx%d", vc->width, vc->height); if( ctx->bit_rate > 8 ) @@ -820,7 +813,8 @@ GetVideoMetadata(const char * path, char * name) asprintf(&m.duration, "%d:%02d:%02d.%03d", hours, min, sec, ms); } - /* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers. Skip DLNA parsing for everything else. */ + /* NOTE: The DLNA spec only provides for ASF (WMV), TS, PS, and MP4 containers. + * Skip DLNA parsing for everything else. */ if( strcmp(ctx->iformat->name, "avi") == 0 ) { asprintf(&m.mime, "video/x-msvideo"); @@ -864,7 +858,8 @@ GetVideoMetadata(const char * path, char * name) off = sprintf(m.dlna_pn, "MPEG_"); if( strcmp(ctx->iformat->name, "mpegts") == 0 ) { - DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 TS\n", video_stream, basename(path), m.resolution); + DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 TS\n", + video_stream, basename(path), m.resolution); off += sprintf(m.dlna_pn+off, "TS_"); if( (vc->width >= 1280) && (vc->height >= 720) ) @@ -881,16 +876,17 @@ GetVideoMetadata(const char * path, char * name) off += sprintf(m.dlna_pn+off, "NA"); } ts = ctx->priv_data; - if( ts->packet_size == 192 ) + if( ts->packet_size == MPEG_TS_PACKET_LENGTH_DLNA ) { if( dlna_timestamp_is_present(path) ) ts_timestamp = VALID; else ts_timestamp = EMPTY; } - else if( ts->packet_size != 188 ) + else if( ts->packet_size != MPEG_TS_PACKET_LENGTH ) { - DPRINTF(E_DEBUG, L_METADATA, "Invalid TS packet size [%s]\n", basename(path)); + DPRINTF(E_DEBUG, L_METADATA, "Unsupported DLNA TS packet size [%d] (%s)\n", + ts->packet_size, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; } @@ -898,7 +894,8 @@ GetVideoMetadata(const char * path, char * name) { case NONE: asprintf(&m.mime, "video/mpeg"); - off += sprintf(m.dlna_pn+off, "_ISO"); + if( m.dlna_pn ) + off += sprintf(m.dlna_pn+off, "_ISO"); break; case VALID: off += sprintf(m.dlna_pn+off, "_T"); @@ -910,7 +907,8 @@ GetVideoMetadata(const char * path, char * name) } else if( strcmp(ctx->iformat->name, "mpeg") == 0 ) { - DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 PS\n", video_stream, basename(path), m.resolution); + DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s MPEG2 PS\n", + video_stream, basename(path), m.resolution); off += sprintf(m.dlna_pn+off, "PS_"); if( (vc->height == 576) || (vc->height == 288) ) @@ -921,7 +919,8 @@ GetVideoMetadata(const char * path, char * name) } else { - DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s [UNKNOWN CONTAINER] is %s MPEG2\n", video_stream, basename(path), m.resolution); + DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s [%s] is %s non-DLNA MPEG2\n", + video_stream, basename(path), ctx->iformat->name, m.resolution); free(m.dlna_pn); m.dlna_pn = NULL; } @@ -980,11 +979,7 @@ GetVideoMetadata(const char * path, char * name) else { DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 video profile! [%s, %dx%d, %dbps : %s]\n", - m.dlna_pn, - vc->width, - vc->height, - vc->bit_rate, - basename(path)); + m.dlna_pn, vc->width, vc->height, vc->bit_rate, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; } @@ -1001,8 +996,7 @@ GetVideoMetadata(const char * path, char * name) else { DPRINTF(E_DEBUG, L_METADATA, "Unsupported h.264 HP video profile! [%dbps, %d audio : %s]\n", - vc->bit_rate, - audio_profile, basename(path)); + vc->bit_rate, audio_profile, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; } @@ -1032,17 +1026,26 @@ GetVideoMetadata(const char * path, char * name) if( !m.dlna_pn ) break; ts = ctx->priv_data; - if( ts->packet_size == 192 ) + if( ts->packet_size == MPEG_TS_PACKET_LENGTH_DLNA ) { - if( dlna_timestamp_is_present(path) ) + if( vc->profile == FF_PROFILE_H264_HIGH || + dlna_timestamp_is_present(path) ) ts_timestamp = VALID; else ts_timestamp = EMPTY; } + else if( ts->packet_size != MPEG_TS_PACKET_LENGTH ) + { + DPRINTF(E_DEBUG, L_METADATA, "Unsupported DLNA TS packet size [%d] (%s)\n", + ts->packet_size, basename(path)); + free(m.dlna_pn); + m.dlna_pn = NULL; + } switch( ts_timestamp ) { case NONE: - off += sprintf(m.dlna_pn+off, "_ISO"); + if( m.dlna_pn ) + off += sprintf(m.dlna_pn+off, "_ISO"); break; case VALID: off += sprintf(m.dlna_pn+off, "_T"); @@ -1075,9 +1078,9 @@ GetVideoMetadata(const char * path, char * name) else if( audio_profile == PROFILE_AUDIO_AAC ) { off += sprintf(m.dlna_pn+off, "AAC_"); - if( ctx->bit_rate < 540000 ) + if( ctx->bit_rate < 520000 ) { - off += sprintf(m.dlna_pn+off, "540"); + off += sprintf(m.dlna_pn+off, "520"); } else if( ctx->bit_rate < 940000 ) { @@ -1291,7 +1294,8 @@ GetVideoMetadata(const char * path, char * name) off += sprintf(m.dlna_pn+off, "BASE"); break; default: - DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPLL/0x%X file %s\n", audio_profile, basename(path)); + DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPLL/0x%X file %s\n", + audio_profile, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; break; @@ -1312,7 +1316,8 @@ GetVideoMetadata(const char * path, char * name) off += sprintf(m.dlna_pn+off, "BASE"); break; default: - DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPML/0x%X file %s\n", audio_profile, basename(path)); + DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVSPML/0x%X file %s\n", + audio_profile, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; break; @@ -1335,7 +1340,8 @@ GetVideoMetadata(const char * path, char * name) off += sprintf(m.dlna_pn+off, "BASE"); break; default: - DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVMED/0x%X file %s\n", audio_profile, basename(path)); + DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVMED/0x%X file %s\n", + audio_profile, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; break; @@ -1355,7 +1361,8 @@ GetVideoMetadata(const char * path, char * name) off += sprintf(m.dlna_pn+off, "FULL"); break; default: - DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVHIGH/0x%X file %s\n", audio_profile, basename(path)); + DPRINTF(E_DEBUG, L_METADATA, "No DLNA profile found for WMVHIGH/0x%X file %s\n", + audio_profile, basename(path)); free(m.dlna_pn); m.dlna_pn = NULL; break; @@ -1367,7 +1374,8 @@ GetVideoMetadata(const char * path, char * name) case CODEC_ID_MSMPEG4V3: asprintf(&m.mime, "video/x-msvideo"); default: - DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s [type %d]\n", video_stream, basename(path), m.resolution, vc->codec_id); + DPRINTF(E_DEBUG, L_METADATA, "Stream %d of %s is %s [type %d]\n", + video_stream, basename(path), m.resolution, vc->codec_id); break; } } @@ -1398,19 +1406,19 @@ GetVideoMetadata(const char * path, char * name) { if( video.title && *video.title ) { - m.title = strdup(trim(video.title)); + m.title = escape_tag(trim(video.title), 1); } if( video.genre && *video.genre ) { - m.genre = strdup(trim(video.genre)); + m.genre = escape_tag(trim(video.genre), 1); } if( video.contributor[ROLE_TRACKARTIST] && *video.contributor[ROLE_TRACKARTIST] ) { - m.artist = strdup(trim(video.contributor[ROLE_TRACKARTIST])); + m.artist = escape_tag(trim(video.contributor[ROLE_TRACKARTIST]), 1); } if( video.contributor[ROLE_ALBUMARTIST] && *video.contributor[ROLE_ALBUMARTIST] ) { - m.creator = strdup(trim(video.contributor[ROLE_ALBUMARTIST])); + m.creator = escape_tag(trim(video.contributor[ROLE_ALBUMARTIST]), 1); } else { @@ -1419,6 +1427,7 @@ GetVideoMetadata(const char * path, char * name) } } } + #ifndef NETGEAR #if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0) else if( strcmp(ctx->iformat->name, "mov,mp4,m4a,3gp,3g2,mj2") == 0 ) { @@ -1431,17 +1440,18 @@ GetVideoMetadata(const char * path, char * name) { //DEBUG DPRINTF(E_DEBUG, L_METADATA, " %-16s: %s\n", tag->key, tag->value); if( strcmp(tag->key, "title") == 0 ) - m.title = strdup(trim(tag->value)); + m.title = escape_tag(trim(tag->value), 1); else if( strcmp(tag->key, "genre") == 0 ) - m.genre = strdup(trim(tag->value)); + m.genre = escape_tag(trim(tag->value), 1); else if( strcmp(tag->key, "artist") == 0 ) - m.artist = strdup(trim(tag->value)); + m.artist = escape_tag(trim(tag->value), 1); else if( strcmp(tag->key, "comment") == 0 ) - m.comment = strdup(trim(tag->value)); + m.comment = escape_tag(trim(tag->value), 1); } } } #endif + #endif video_no_dlna: av_close_input_file(ctx); diff --git a/release/src/router/minidlna/minidlna.c b/release/src/router/minidlna/minidlna.c index be146e0112..757b01290b 100644 --- a/release/src/router/minidlna/minidlna.c +++ b/release/src/router/minidlna/minidlna.c @@ -201,7 +201,7 @@ parselanaddr(struct lan_addr_s * lan_addr, const char * str) return 0; } -void +static void getfriendlyname(char * buf, int len) { char * dot = NULL; @@ -296,7 +296,7 @@ getfriendlyname(char * buf, int len) #endif } -int +static int open_db(void) { char path[PATH_MAX]; @@ -347,7 +347,7 @@ init(int argc, char * * argv) enum media_types type; char * path; char real_path[PATH_MAX]; - char ext_ip_addr[INET_ADDRSTRLEN + 3] = {'\0'}; + char ip_addr[INET_ADDRSTRLEN + 3] = {'\0'}; /* first check if "-f" option is used */ for(i=2; i= 0) + for( string = ary_options[i].value; (word = strtok(string, ",")); string = NULL ) { - if( *ext_ip_addr && parselanaddr(&lan_addr[n_lan_addr], ext_ip_addr) == 0 ) - n_lan_addr++; + if(n_lan_addr < MAX_LAN_ADDR) + { + if(getifaddr(word, ip_addr, sizeof(ip_addr)) >= 0) + { + if( *ip_addr && parselanaddr(&lan_addr[n_lan_addr], ip_addr) == 0 ) + if(n_lan_addr < MAX_LAN_ADDR) + n_lan_addr++; + } + else + fprintf(stderr, "Interface %s not found, ignoring.\n", word); + } + else + { + fprintf(stderr, "Too many listening ips (max: %d), ignoring %s\n", + MAX_LAN_ADDR, word); + } } - else - fprintf(stderr, "Interface %s not found, ignoring.\n", ary_options[i].value); break; case UPNPLISTENING_IP: if(n_lan_addr < MAX_LAN_ADDR) @@ -485,7 +497,8 @@ init(int argc, char * * argv) } break; case UPNPALBUMART_NAMES: - for( string = ary_options[i].value; (word = strtok(string, "/")); string = NULL ) { + for( string = ary_options[i].value; (word = strtok(string, "/")); string = NULL ) + { struct album_art_name_s * this_name = calloc(1, sizeof(struct album_art_name_s)); int len = strlen(word); if( word[len-1] == '*' ) @@ -652,7 +665,7 @@ init(int argc, char * * argv) int address_already_there = 0; int j; i++; - if( getifaddr(argv[i], ext_ip_addr, sizeof(ext_ip_addr)) < 0 ) + if( getifaddr(argv[i], ip_addr, sizeof(ip_addr)) < 0 ) { fprintf(stderr, "Network interface '%s' not found.\n", argv[i]); @@ -661,7 +674,7 @@ init(int argc, char * * argv) for(j=0; j= 0) { FD_SET(sudp, &readset); - max_fd = MAX( max_fd, sudp); + max_fd = MAX(max_fd, sudp); } if (shttpl >= 0) { FD_SET(shttpl, &readset); - max_fd = MAX( max_fd, shttpl); + max_fd = MAX(max_fd, shttpl); } #ifdef TIVO_SUPPORT if (sbeacon >= 0) @@ -1082,7 +1099,7 @@ main(int argc, char * * argv) if((e->socket >= 0) && (e->state <= 2)) { FD_SET(e->socket, &readset); - max_fd = MAX( max_fd, e->socket); + max_fd = MAX(max_fd, e->socket); i++; } } @@ -1118,14 +1135,14 @@ main(int argc, char * * argv) #endif /* increment SystemUpdateID if the content database has changed, * and if there is an active HTTP connection, at most once every 2 seconds */ - if( i && (time(NULL) >= (lastupdatetime.tv_sec + 2)) ) + if( i && (timeofday.tv_sec >= (lastupdatetime + 2)) ) { - if( sqlite3_total_changes(db) != last_changecnt ) + if( scanning || sqlite3_total_changes(db) != last_changecnt ) { updateID++; last_changecnt = sqlite3_total_changes(db); upnp_event_var_change_notify(EContentDirectory); - memcpy(&lastupdatetime, &timeofday, sizeof(struct timeval)); + lastupdatetime = timeofday.tv_sec; } } /* process active HTTP connections */ diff --git a/release/src/router/minidlna/minidlnatypes.h b/release/src/router/minidlna/minidlnatypes.h index dec1ba74e4..539202d276 100644 --- a/release/src/router/minidlna/minidlnatypes.h +++ b/release/src/router/minidlna/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, @@ -70,6 +76,9 @@ enum client_types { ESonyBravia, ERokuSoundBridge, EToshibaTV, + ELGDevice, + ENetgearEVA2000, + ESamsungSeriesA, EStandardDLNA150 = 100 }; diff --git a/release/src/router/minidlna/minissdp.c b/release/src/router/minidlna/minissdp.c index 35a9e2a826..3f14ccbbc7 100644 --- a/release/src/router/minidlna/minissdp.c +++ b/release/src/router/minidlna/minissdp.c @@ -1,4 +1,4 @@ -/* $Id: minissdp.c,v 1.16 2011/04/12 20:55:32 jmaggard Exp $ */ +/* $Id: minissdp.c,v 1.18 2011/05/03 06:14:25 jmaggard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * @@ -223,6 +223,16 @@ static const char * const known_service_types[] = 0 }; +static void +_usleep(long usecs) +{ + struct timespec sleep_time; + + sleep_time.tv_sec = 0; + sleep_time.tv_nsec = usecs * 1000; + nanosleep(&sleep_time, NULL); +} + /* not really an SSDP "announce" as it is the response * to a SSDP "M-SEARCH" */ static void @@ -282,7 +292,7 @@ SendSSDPNotifies(int s, const char * host, unsigned short port, for( dup=0; dup<2; dup++ ) { if( dup ) - usleep(200000); + _usleep(200000); i=0; while(known_service_types[i]) { @@ -477,10 +487,9 @@ ProcessSSDPRequest(int s, unsigned short port) char bufr[1500]; socklen_t len_r; struct sockaddr_in sendername; - int i, l; - int lan_addr_index = 0; + int i; char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL, *loc = NULL, *srv = NULL; - int st_len = 0, mx_len = 0, man_len = 0, mx_val = 0, loc_len = 0, srv_len = 0; + int man_len = 0; len_r = sizeof(struct sockaddr_in); n = recvfrom(s, bufr, sizeof(bufr)-1, 0, @@ -494,6 +503,7 @@ ProcessSSDPRequest(int s, unsigned short port) if(memcmp(bufr, "NOTIFY", 6) == 0) { + int loc_len = 0, srv_len = 0; //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Received SSDP notify:\n%.*s", n, bufr); for(i=0; i < n; i++) { @@ -522,6 +532,7 @@ ProcessSSDPRequest(int s, unsigned short port) loc_len = 0; while(*loc == ' ' || *loc == '\t') loc++; while(loc[loc_len]!='\r' && loc[loc_len]!='\n') loc_len++; + loc[loc_len] = '\0'; } else if(strncasecmp(bufr+i, "NTS:", 4) == 0) { @@ -531,11 +542,10 @@ ProcessSSDPRequest(int s, unsigned short port) while(man[man_len]!='\r' && man[man_len]!='\n') man_len++; } } - if (!man || (strncmp(man, "ssdp:alive", man_len) != 0)) + if (!loc || !srv || !man || (strncmp(man, "ssdp:alive", man_len) != 0)) { return; } - loc[loc_len] = '\0'; if (strncmp(srv, "Allegro-Software-RomPlug", 24) == 0) { /* Check if the client is already in cache */ @@ -551,6 +561,7 @@ ProcessSSDPRequest(int s, unsigned short port) } else if(memcmp(bufr, "M-SEARCH", 8) == 0) { + int st_len = 0, mx_len = 0, mx_val = 0; //DPRINTF(E_DEBUG, L_SSDP, "Received SSDP request:\n%.*s", n, bufr); for(i=0; i < n; i++) { @@ -608,6 +619,8 @@ ProcessSSDPRequest(int s, unsigned short port) } else if( st && (st_len > 0) ) { + int l; + int lan_addr_index = 0; /* find in which sub network the client is */ for(i = 0; i>20); + _usleep(random()>>20); SendSSDPAnnounce2(s, sendername, i, lan_addr[lan_addr_index].str, port); diff --git a/release/src/router/minidlna/po/LINGUAS b/release/src/router/minidlna/po/LINGUAS index 7673daa944..78420991a2 100644 --- a/release/src/router/minidlna/po/LINGUAS +++ b/release/src/router/minidlna/po/LINGUAS @@ -1 +1 @@ -de +da de es fr it ja nb nl ru sl sv diff --git a/release/src/router/minidlna/po/da.po b/release/src/router/minidlna/po/da.po index 1206948ea4..b64a54377a 100644 --- a/release/src/router/minidlna/po/da.po +++ b/release/src/router/minidlna/po/da.po @@ -3,7 +3,6 @@ # This file is distributed under the same license as the MiniDLNA package. # Justin Maggard , 2010. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -12,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-08-09 17:00-0700\n" "Last-Translator: ljensen \n" "Language-Team: Danish\n" +"Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/release/src/router/minidlna/po/de.po b/release/src/router/minidlna/po/de.po index ae9dfffda9..46a80706a2 100644 --- a/release/src/router/minidlna/po/de.po +++ b/release/src/router/minidlna/po/de.po @@ -18,7 +18,6 @@ # You should have received a copy of the GNU General Public License # along with MiniDLNA. If not, see . # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -27,6 +26,7 @@ msgstr "" "PO-Revision-Date: 2010-10-19 11:00-0800\n" "Last-Translator: Andi Miko \n" "Language-Team: German\n" +"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/release/src/router/minidlna/po/es.po b/release/src/router/minidlna/po/es.po index c1102bb966..0f5ba7b0ed 100644 --- a/release/src/router/minidlna/po/es.po +++ b/release/src/router/minidlna/po/es.po @@ -3,7 +3,6 @@ # This file is distributed under the same license as the MiniDLNA package. # Justin Maggard , 2010. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" diff --git a/release/src/router/minidlna/po/fr.po b/release/src/router/minidlna/po/fr.po index d3948e62c3..791b62c4c6 100644 --- a/release/src/router/minidlna/po/fr.po +++ b/release/src/router/minidlna/po/fr.po @@ -3,7 +3,6 @@ # This file is distributed under the same license as the MiniDLNA package. # Justin Maggard , 2010. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -12,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-08-09 17:00-0700\n" "Last-Translator: super-poussin \n" "Language-Team: French\n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -104,4 +104,4 @@ msgstr "Analyse de %s\n" #: scanner.c:789 #, c-format msgid "Scanning %s finished (%llu files)!\n" -msgstr "Analyse réalisée à %s % (%llu fichiers)!\n" +msgstr "Analyse réalisée à %s (%llu fichiers)!\n" diff --git a/release/src/router/minidlna/po/it.po b/release/src/router/minidlna/po/it.po index a5b927b640..10f89614cd 100644 --- a/release/src/router/minidlna/po/it.po +++ b/release/src/router/minidlna/po/it.po @@ -11,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-11-01 10:04+0100\n" "Last-Translator: Andrea Musuruane \n" "Language-Team: Italian\n" +"Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -29,9 +30,7 @@ msgstr "Fotocamera sconosciuta" msgid "- All Albums -" msgstr "- Tutti gli album -" -#: scanner.c:290 -#: scanner.c:297 -#: scanner.c:300 +#: scanner.c:290 scanner.c:297 scanner.c:300 msgid "Unknown Album" msgstr "Album sconosciuto" @@ -39,9 +38,7 @@ msgstr "Album sconosciuto" msgid "- All Artists -" msgstr "- Tutti gli artisti -" -#: scanner.c:331 -#: scanner.c:337 -#: scanner.c:340 +#: scanner.c:331 scanner.c:337 scanner.c:340 msgid "Unknown Artist" msgstr "Artista sconosciuto" @@ -65,9 +62,7 @@ msgstr "Artista" msgid "Album" msgstr "Album" -#: scanner.c:548 -#: scanner.c:552 -#: scanner.c:557 +#: scanner.c:548 scanner.c:552 scanner.c:557 msgid "Folders" msgstr "Cartelle" @@ -112,4 +107,3 @@ msgstr "Scansione di %s\n" #, c-format msgid "Scanning %s finished (%llu files)!\n" msgstr "Scansione di %s finita (%llu file)\n" - diff --git a/release/src/router/minidlna/po/ja.po b/release/src/router/minidlna/po/ja.po index 0f686fd39b..37c59467f4 100644 --- a/release/src/router/minidlna/po/ja.po +++ b/release/src/router/minidlna/po/ja.po @@ -11,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-07-23 15:57-0800\n" "Last-Translator: r2d2 \n" "Language-Team: Japanese\n" +"Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -27,9 +28,7 @@ msgstr "カメラ名不明" msgid "- All Albums -" msgstr "- すべてのアルバム -" -#: scanner.c:290 -#: scanner.c:297 -#: scanner.c:300 +#: scanner.c:290 scanner.c:297 scanner.c:300 msgid "Unknown Album" msgstr "アルバム名不明" @@ -37,9 +36,7 @@ msgstr "アルバム名不明" msgid "- All Artists -" msgstr "- すべてのアーティスト -" -#: scanner.c:331 -#: scanner.c:337 -#: scanner.c:340 +#: scanner.c:331 scanner.c:337 scanner.c:340 msgid "Unknown Artist" msgstr "アーティスト名不明" @@ -63,9 +60,7 @@ msgstr "アーティスト" msgid "Album" msgstr "アルバム" -#: scanner.c:548 -#: scanner.c:552 -#: scanner.c:557 +#: scanner.c:548 scanner.c:552 scanner.c:557 msgid "Folders" msgstr "フォルダ" @@ -110,4 +105,3 @@ msgstr "%s を検索中\n" #, c-format msgid "Scanning %s finished (%llu files)!\n" msgstr "%s (%llu ファイル) の検索終了!\n" - diff --git a/release/src/router/minidlna/po/nb.po b/release/src/router/minidlna/po/nb.po index 72207eb474..6404318024 100644 --- a/release/src/router/minidlna/po/nb.po +++ b/release/src/router/minidlna/po/nb.po @@ -3,7 +3,6 @@ # This file is distributed under the same license as the MiniDLNA package. # Justin Maggard , 2010. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -12,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-08-09 17:00-0700\n" "Last-Translator: samundsen \n" "Language-Team: Norwegian\n" +"Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/release/src/router/minidlna/po/nl.po b/release/src/router/minidlna/po/nl.po index e1bf6118d4..b92a39cc51 100644 --- a/release/src/router/minidlna/po/nl.po +++ b/release/src/router/minidlna/po/nl.po @@ -3,7 +3,6 @@ # This file is distributed under the same license as the MiniDLNA package. # Justin Maggard , 2010. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -12,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-08-09 17:00-0700\n" "Last-Translator: frejac \n" "Language-Team: Swedish\n" +"Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/release/src/router/minidlna/po/ru.po b/release/src/router/minidlna/po/ru.po index 1350625174..ea234e9795 100644 --- a/release/src/router/minidlna/po/ru.po +++ b/release/src/router/minidlna/po/ru.po @@ -26,6 +26,7 @@ msgstr "" "PO-Revision-Date: 2011-03-25 22:40+0500\n" "Last-Translator: Ivan Mironov \n" "Language-Team: \n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -44,9 +45,7 @@ msgstr "Неизвестная Камера" msgid "- All Albums -" msgstr "- Все Альбомы -" -#: scanner.c:290 -#: scanner.c:297 -#: scanner.c:300 +#: scanner.c:290 scanner.c:297 scanner.c:300 msgid "Unknown Album" msgstr "Неизвестный Альбом" @@ -54,9 +53,7 @@ msgstr "Неизвестный Альбом" msgid "- All Artists -" msgstr "- Все Исполнители -" -#: scanner.c:331 -#: scanner.c:337 -#: scanner.c:340 +#: scanner.c:331 scanner.c:337 scanner.c:340 msgid "Unknown Artist" msgstr "Неизвестный Исполнитель" @@ -80,9 +77,7 @@ msgstr "Исполнитель" msgid "Album" msgstr "Альбом" -#: scanner.c:548 -#: scanner.c:552 -#: scanner.c:557 +#: scanner.c:548 scanner.c:552 scanner.c:557 msgid "Folders" msgstr "Папки" @@ -127,4 +122,3 @@ msgstr "Сканирование %s\n" #, c-format msgid "Scanning %s finished (%llu files)!\n" msgstr "Сканирование %s завершено (%llu файлов)!\n" - diff --git a/release/src/router/minidlna/po/sl.po b/release/src/router/minidlna/po/sl.po index 225306b794..592141c786 100644 --- a/release/src/router/minidlna/po/sl.po +++ b/release/src/router/minidlna/po/sl.po @@ -18,7 +18,6 @@ # You should have received a copy of the GNU General Public License # along with MiniDLNA. If not, see . # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -27,6 +26,7 @@ msgstr "" "PO-Revision-Date: 2010-10-19 11:00-0800\n" "Last-Translator: Bojan.Krstic \n" "Language-Team: Slovenski\n" +"Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -114,7 +114,7 @@ msgstr "Prebrskaj Mape" #: scanner.c:721 #, c-format msgid "Scanning %s\n" -msgstr "Skeniranje... \n" +msgstr "Skeniranje %s\n" #: scanner.c:789 #, c-format diff --git a/release/src/router/minidlna/po/sv.po b/release/src/router/minidlna/po/sv.po index 72e69fca6c..7d49e69012 100644 --- a/release/src/router/minidlna/po/sv.po +++ b/release/src/router/minidlna/po/sv.po @@ -3,7 +3,6 @@ # This file is distributed under the same license as the MiniDLNA package. # Justin Maggard , 2010. # -#, fuzzy msgid "" msgstr "" "Project-Id-Version: minidlna 1.0.18\n" @@ -12,6 +11,7 @@ msgstr "" "PO-Revision-Date: 2010-08-09 17:00-0700\n" "Last-Translator: frejac \n" "Language-Team: Swedish\n" +"Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/release/src/router/minidlna/scanner.c b/release/src/router/minidlna/scanner.c index 94c821c70b..ac7a19be1d 100644 --- a/release/src/router/minidlna/scanner.c +++ b/release/src/router/minidlna/scanner.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -46,7 +47,7 @@ int valid_cache = 0; struct virtual_item { - int objectID; + sqlite3_int64 objectID; char parentID[64]; char name[256]; }; @@ -85,66 +86,59 @@ wait_for_mount(const char * path) return r; } + sqlite_int64 get_next_available_id(const char * table, const char * parentID) { - char * sql; - char **result; - int ret, rows; + char *ret, *base; sqlite_int64 objectID = 0; - asprintf(&sql, "SELECT OBJECT_ID from %s where ID = " - "(SELECT max(ID) from %s where PARENT_ID = '%s')", table, table, parentID); - ret = sql_get_table(db, sql, &result, &rows, NULL); - if( rows ) + ret = sql_get_text_field(db, "SELECT OBJECT_ID from %s where ID = " + "(SELECT max(ID) from %s where PARENT_ID = '%s')", + table, table, parentID); + if( ret ) { - objectID = strtoll(strrchr(result[1], '$')+1, NULL, 16) + 1; + base = strrchr(ret, '$'); + if( base ) + objectID = strtoll(base+1, NULL, 16) + 1; + sqlite3_free(ret); } - sqlite3_free_table(result); - free(sql); return objectID; } -long long int +int insert_container(const char * item, const char * rootParent, const char * refID, const char *class, - const char *artist, const char *genre, const char *album_art) + const char *artist, const char *genre, const char *album_art, sqlite3_int64 *objectID, sqlite3_int64 *parentID) { - char **result; - char **result2; - char *sql; - int cols, rows, ret; - int parentID = 0, objectID = 0; + char *result; + char *base; + int ret = 0; sqlite_int64 detailID = 0; - sql = sqlite3_mprintf("SELECT OBJECT_ID from OBJECTS" - " where PARENT_ID = '%s'" - " and NAME = '%q'" - " and CLASS = 'container.%s' limit 1", - rootParent, item, class); - ret = sql_get_table(db, sql, &result, &rows, &cols); - sqlite3_free(sql); - if( cols ) + result = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS" + " where PARENT_ID = '%s'" + " and NAME = '%q'" + " and CLASS = 'container.%s' limit 1", + rootParent, item, class); + if( result ) { - parentID = strtol(rindex(result[1], '$')+1, NULL, 16); - objectID = get_next_available_id("OBJECTS", result[1]); + base = strrchr(result, '$'); + if( base ) + *parentID = strtoll(base+1, NULL, 16); + else + *parentID = 0; + *objectID = get_next_available_id("OBJECTS", result); } else { - parentID = get_next_available_id("OBJECTS", rootParent); + *objectID = 0; + *parentID = get_next_available_id("OBJECTS", rootParent); if( refID ) { - sql = sqlite3_mprintf("SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID); - ret = sql_get_table(db, sql, &result2, &rows, NULL); - if( ret == SQLITE_OK ) - { - if( rows ) - { - detailID = strtoll(result2[1], NULL, 10); - } - sqlite3_free_table(result2); - } - sqlite3_free(sql); + result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = %Q", refID); + if( result ) + detailID = strtoll(result, NULL, 10); } if( !detailID ) { @@ -153,22 +147,22 @@ insert_container(const char * item, const char * rootParent, const char * refID, ret = sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) " "VALUES" - " ('%s$%X', '%s', %Q, %lld, 'container.%s', '%q')", - rootParent, parentID, rootParent, refID, detailID, class, item); + " ('%s$%"PRIX64"', '%s', %Q, %"PRId64", 'container.%s', '%q')", + rootParent, *parentID, rootParent, refID, detailID, class, item); } - sqlite3_free_table(result); + sqlite3_free(result); - return (long long)parentID<<32|objectID; + return ret; } -void -insert_containers(const char * name, const char *path, const char * refID, const char * class, long unsigned int detailID) +static void +insert_containers(const char * name, const char *path, const char * refID, const char * class, sqlite3_int64 detailID) { char *sql; char **result; int ret; int cols, row; - sqlite_int64 container; + sqlite_int64 objectID, parentID; if( strstr(class, "imageItem") ) { @@ -179,7 +173,7 @@ insert_containers(const char * name, const char *path, const char * refID, const static struct virtual_item last_camdate; static sqlite_int64 last_all_objectID = 0; - asprintf(&sql, "SELECT DATE, CREATOR from DETAILS where ID = %lu", detailID); + asprintf(&sql, "SELECT DATE, CREATOR from DETAILS where ID = %"PRId64, detailID); ret = sql_get_table(db, sql, &result, &row, &cols); free(sql); if( ret == SQLITE_OK ) @@ -204,16 +198,16 @@ insert_containers(const char * name, const char *path, const char * refID, const } else { - container = insert_container(date_taken, IMAGE_DATE_ID, NULL, "album.photoAlbum", NULL, NULL, NULL); - sprintf(last_date.parentID, IMAGE_DATE_ID"$%llX", container>>32); - last_date.objectID = (int)container; + insert_container(date_taken, IMAGE_DATE_ID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID); + sprintf(last_date.parentID, IMAGE_DATE_ID"$%"PRIX64, parentID); + last_date.objectID = objectID; strcpy(last_date.name, date_taken); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached date item: %s/%s/%X\n", last_date.name, last_date.parentID, last_date.objectID); } sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_date.parentID, last_date.objectID, last_date.parentID, refID, class, detailID, name); if( cam ) @@ -227,8 +221,8 @@ insert_containers(const char * name, const char *path, const char * refID, const } if( !valid_cache || strcmp(camera, last_cam.name) != 0 ) { - container = insert_container(camera, IMAGE_CAMERA_ID, NULL, "storageFolder", NULL, NULL, NULL); - sprintf(last_cam.parentID, IMAGE_CAMERA_ID"$%llX", container>>32); + insert_container(camera, IMAGE_CAMERA_ID, NULL, "storageFolder", NULL, NULL, NULL, &objectID, &parentID); + sprintf(last_cam.parentID, IMAGE_CAMERA_ID"$%"PRIX64, parentID); strncpy(last_cam.name, camera, 255); last_camdate.name[0] = '\0'; } @@ -239,16 +233,16 @@ insert_containers(const char * name, const char *path, const char * refID, const } else { - container = insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL); - sprintf(last_camdate.parentID, "%s$%llX", last_cam.parentID, container>>32); - last_camdate.objectID = (int)container; + insert_container(date_taken, last_cam.parentID, NULL, "album.photoAlbum", NULL, NULL, NULL, &objectID, &parentID); + sprintf(last_camdate.parentID, "%s$%"PRIX64, last_cam.parentID, parentID); + last_camdate.objectID = objectID; strcpy(last_camdate.name, date_taken); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached camdate item: %s/%s/%s/%X\n", camera, last_camdate.name, last_camdate.parentID, last_camdate.objectID); } sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_camdate.parentID, last_camdate.objectID, last_camdate.parentID, refID, class, detailID, name); /* All Images */ if( !last_all_objectID ) @@ -258,12 +252,12 @@ insert_containers(const char * name, const char *path, const char * refID, const sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('"IMAGE_ALL_ID"$%llX', '"IMAGE_ALL_ID"', '%s', '%s', %lu, %Q)", + " ('"IMAGE_ALL_ID"$%"PRIX64"', '"IMAGE_ALL_ID"', '%s', '%s', %"PRId64", %Q)", last_all_objectID++, refID, class, detailID, name); } else if( strstr(class, "audioItem") ) { - asprintf(&sql, "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %lu", detailID); + asprintf(&sql, "SELECT ALBUM, ARTIST, GENRE, ALBUM_ART from DETAILS where ID = %"PRId64, detailID); ret = sql_get_table(db, sql, &result, &row, &cols); free(sql); if( ret != SQLITE_OK ) @@ -294,29 +288,29 @@ insert_containers(const char * name, const char *path, const char * refID, const else { strcpy(last_album.name, album); - container = insert_container(album, MUSIC_ALBUM_ID, NULL, "album.musicAlbum", artist, genre, album_art); - sprintf(last_album.parentID, MUSIC_ALBUM_ID"$%llX", container>>32); - last_album.objectID = (int)container; + insert_container(album, MUSIC_ALBUM_ID, NULL, "album.musicAlbum", artist, genre, album_art, &objectID, &parentID); + sprintf(last_album.parentID, MUSIC_ALBUM_ID"$%llX", parentID); + last_album.objectID = objectID; //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached album item: %s/%s/%X\n", last_album.name, last_album.parentID, last_album.objectID); } sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_album.parentID, last_album.objectID, last_album.parentID, refID, class, detailID, name); } if( artist ) { if( !valid_cache || strcmp(artist, last_artist.name) != 0 ) { - container = insert_container(artist, MUSIC_ARTIST_ID, NULL, "person.musicArtist", NULL, genre, NULL); - sprintf(last_artist.parentID, MUSIC_ARTIST_ID"$%llX", container>>32); + insert_container(artist, MUSIC_ARTIST_ID, NULL, "person.musicArtist", NULL, genre, NULL, &objectID, &parentID); + sprintf(last_artist.parentID, MUSIC_ARTIST_ID"$%"PRIX64, parentID); strcpy(last_artist.name, artist); last_artistAlbum.name[0] = '\0'; /* Add this file to the "- All Albums -" container as well */ - container = insert_container(_("- All Albums -"), last_artist.parentID, NULL, "album", artist, genre, NULL); - sprintf(last_artistAlbumAll.parentID, "%s$%llX", last_artist.parentID, container>>32); - last_artistAlbumAll.objectID = (int)container; + insert_container(_("- All Albums -"), last_artist.parentID, NULL, "album", artist, genre, NULL, &objectID, &parentID); + sprintf(last_artistAlbumAll.parentID, "%s$%"PRIX64, last_artist.parentID, parentID); + last_artistAlbumAll.objectID = objectID; } else { @@ -329,35 +323,36 @@ insert_containers(const char * name, const char *path, const char * refID, const } else { - container = insert_container(album?album:_("Unknown Album"), last_artist.parentID, album?last_album.parentID:NULL, "album.musicAlbum", artist, genre, album_art); - sprintf(last_artistAlbum.parentID, "%s$%llX", last_artist.parentID, container>>32); - last_artistAlbum.objectID = (int)container; + insert_container(album?album:_("Unknown Album"), last_artist.parentID, album?last_album.parentID:NULL, + "album.musicAlbum", artist, genre, album_art, &objectID, &parentID); + sprintf(last_artistAlbum.parentID, "%s$%"PRIX64, last_artist.parentID, parentID); + last_artistAlbum.objectID = objectID; strcpy(last_artistAlbum.name, album?album:_("Unknown Album")); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached artist/album item: %s/%s/%X\n", last_artist.name, last_artist.parentID, last_artist.objectID); } sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_artistAlbum.parentID, last_artistAlbum.objectID, last_artistAlbum.parentID, refID, class, detailID, name); sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_artistAlbumAll.parentID, last_artistAlbumAll.objectID, last_artistAlbumAll.parentID, refID, class, detailID, name); } if( genre ) { if( !valid_cache || strcmp(genre, last_genre.name) != 0 ) { - container = insert_container(genre, MUSIC_GENRE_ID, NULL, "genre.musicGenre", NULL, NULL, NULL); - sprintf(last_genre.parentID, MUSIC_GENRE_ID"$%llX", container>>32); + insert_container(genre, MUSIC_GENRE_ID, NULL, "genre.musicGenre", NULL, NULL, NULL, &objectID, &parentID); + sprintf(last_genre.parentID, MUSIC_GENRE_ID"$%"PRIX64, parentID); strcpy(last_genre.name, genre); last_genreArtist.name[0] = '\0'; /* Add this file to the "- All Artists -" container as well */ - container = insert_container(_("- All Artists -"), last_genre.parentID, NULL, "person", NULL, genre, NULL); - sprintf(last_genreArtistAll.parentID, "%s$%llX", last_genre.parentID, container>>32); - last_genreArtistAll.objectID = (int)container; + insert_container(_("- All Artists -"), last_genre.parentID, NULL, "person", NULL, genre, NULL, &objectID, &parentID); + sprintf(last_genreArtistAll.parentID, "%s$%"PRIX64, last_genre.parentID, parentID); + last_genreArtistAll.objectID = objectID; } else { @@ -369,21 +364,22 @@ insert_containers(const char * name, const char *path, const char * refID, const } else { - container = insert_container(artist?artist:_("Unknown Artist"), last_genre.parentID, artist?last_artist.parentID:NULL, "person.musicArtist", NULL, genre, NULL); - sprintf(last_genreArtist.parentID, "%s$%llX", last_genre.parentID, container>>32); - last_genreArtist.objectID = (int)container; + insert_container(artist?artist:_("Unknown Artist"), last_genre.parentID, artist?last_artist.parentID:NULL, + "person.musicArtist", NULL, genre, NULL, &objectID, &parentID); + sprintf(last_genreArtist.parentID, "%s$%"PRIX64, last_genre.parentID, parentID); + last_genreArtist.objectID = objectID; strcpy(last_genreArtist.name, artist?artist:_("Unknown Artist")); //DEBUG DPRINTF(E_DEBUG, L_SCANNER, "Creating cached genre/artist item: %s/%s/%X\n", last_genreArtist.name, last_genreArtist.parentID, last_genreArtist.objectID); } sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_genreArtist.parentID, last_genreArtist.objectID, last_genreArtist.parentID, refID, class, detailID, name); sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s$%X', '%s', '%s', '%s', %lu, %Q)", + " ('%s$%"PRIX64"', '%s', '%s', '%s', %"PRId64", %Q)", last_genreArtistAll.parentID, last_genreArtistAll.objectID, last_genreArtistAll.parentID, refID, class, detailID, name); } /* All Music */ @@ -394,7 +390,7 @@ insert_containers(const char * name, const char *path, const char * refID, const sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('"MUSIC_ALL_ID"$%llX', '"MUSIC_ALL_ID"', '%s', '%s', %lu, %Q)", + " ('"MUSIC_ALL_ID"$%"PRIX64"', '"MUSIC_ALL_ID"', '%s', '%s', %"PRId64", %Q)", last_all_objectID++, refID, class, detailID, name); } else if( strstr(class, "videoItem") ) @@ -409,7 +405,7 @@ insert_containers(const char * name, const char *path, const char * refID, const sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('"VIDEO_ALL_ID"$%llX', '"VIDEO_ALL_ID"', '%s', '%s', %lu, %Q)", + " ('"VIDEO_ALL_ID"$%"PRIX64"', '"VIDEO_ALL_ID"', '%s', '%s', %"PRId64", %Q)", last_all_objectID++, refID, class, detailID, name); return; } @@ -424,15 +420,14 @@ insert_containers(const char * name, const char *path, const char * refID, const int insert_directory(const char * name, const char * path, const char * base, const char * parentID, int objectID) { - char * sql; - int rows, found = 0; + int found = 0; sqlite_int64 detailID = 0; char * refID = NULL; char class[] = "container.storageFolder"; char * id_buf = NULL; char * parent_buf = NULL; - char **result; - char *dir = NULL; + char *dir_buf, *dir; + char *result, *p; static char last_found[256] = "-1"; if( strcmp(base, BROWSEDIR_ID) != 0 ) @@ -440,8 +435,8 @@ insert_directory(const char * name, const char * path, const char * base, const if( refID ) { - dir = strdup(path); - dir = dirname(dir); + dir_buf = strdup(path); + dir = dirname(dir_buf); asprintf(&id_buf, "%s%s$%X", base, parentID, objectID); asprintf(&parent_buf, "%s%s", base, parentID); while( !found ) @@ -454,30 +449,29 @@ insert_directory(const char * name, const char * path, const char * base, const break; } /* Does not exist. Need to create, and may need to create parents also */ - sql = sqlite3_mprintf("SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID); - if( (sql_get_table(db, sql, &result, &rows, NULL) == SQLITE_OK) && rows ) + result = sql_get_text_field(db, "SELECT DETAIL_ID from OBJECTS where OBJECT_ID = '%s'", refID); + if( result ) { - detailID = atoi(result[1]); + detailID = strtoll(result, NULL, 10); + sqlite3_free(result); } - sqlite3_free_table(result); - sqlite3_free(sql); sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) " "VALUES" - " ('%s', '%s', %Q, '%lld', '%s', '%q')", - id_buf, parent_buf, refID, detailID, class, rindex(dir, '/')+1); - if( rindex(id_buf, '$') ) - *rindex(id_buf, '$') = '\0'; - if( rindex(parent_buf, '$') ) - *rindex(parent_buf, '$') = '\0'; - if( rindex(refID, '$') ) - *rindex(refID, '$') = '\0'; + " ('%s', '%s', %Q, %"PRId64", '%s', '%q')", + id_buf, parent_buf, refID, detailID, class, strrchr(dir, '/')+1); + if( (p = strrchr(id_buf, '$')) ) + *p = '\0'; + if( (p = strrchr(parent_buf, '$')) ) + *p = '\0'; + if( (p = strrchr(refID, '$')) ) + *p = '\0'; dir = dirname(dir); } free(refID); free(parent_buf); free(id_buf); - free(dir); + free(dir_buf); return 1; } @@ -485,7 +479,7 @@ insert_directory(const char * name, const char * path, const char * base, const sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, DETAIL_ID, CLASS, NAME) " "VALUES" - " ('%s%s$%X', '%s%s', %Q, '%lld', '%s', '%q')", + " ('%s%s$%X', '%s%s', %Q, %"PRId64", '%s', '%q')", base, parentID, objectID, base, parentID, refID, detailID, class, name); if( refID ) free(refID); @@ -498,7 +492,7 @@ insert_file(char * name, const char * path, const char * parentID, int object) { char class[32]; char objectID[64]; - unsigned long int detailID = 0; + sqlite3_int64 detailID = 0; char base[8]; char * typedir_parentID; int typedir_objectID; @@ -546,17 +540,17 @@ insert_file(char * name, const char * path, const char * parentID, int object) sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s', '%s%s', '%s', %lu, '%q')", + " ('%s', '%s%s', '%s', %"PRId64", '%q')", objectID, BROWSEDIR_ID, parentID, class, detailID, name); if( *parentID ) { typedir_objectID = 0; typedir_parentID = strdup(parentID); - baseid = rindex(typedir_parentID, '$'); + baseid = strrchr(typedir_parentID, '$'); if( baseid ) { - sscanf(baseid+1, "%X", &typedir_objectID); + typedir_objectID = strtol(baseid+1, NULL, 16); *baseid = '\0'; } insert_directory(name, path, base, typedir_parentID, typedir_objectID); @@ -565,7 +559,7 @@ insert_file(char * name, const char * path, const char * parentID, int object) sql_exec(db, "INSERT into OBJECTS" " (OBJECT_ID, PARENT_ID, REF_ID, CLASS, DETAIL_ID, NAME) " "VALUES" - " ('%s%s$%X', '%s%s', '%s', '%s', %lu, '%q')", + " ('%s%s$%X', '%s%s', '%s', '%s', %"PRId64", '%q')", base, parentID, object, base, parentID, objectID, class, detailID, name); insert_containers(name, path, objectID, class, detailID); @@ -798,7 +792,7 @@ ScanDirectory(const char * dir, const char * parent, enum media_types dir_type) #endif type = TYPE_UNKNOWN; sprintf(full_path, "%s/%s", dir, namelist[i]->d_name); - name = escape_tag(namelist[i]->d_name); + name = escape_tag(namelist[i]->d_name, 1); if( namelist[i]->d_type == DT_DIR ) { type = TYPE_DIR; @@ -813,17 +807,16 @@ ScanDirectory(const char * dir, const char * parent, enum media_types dir_type) } if( (type == TYPE_DIR) && (access(full_path, R_OK|X_OK) == 0) ) { - insert_directory(name?name:namelist[i]->d_name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID); + insert_directory(name, full_path, BROWSEDIR_ID, (parent ? parent:""), i+startID); sprintf(parent_id, "%s$%X", (parent ? parent:""), i+startID); ScanDirectory(full_path, parent_id, dir_type); } else if( type == TYPE_FILE ) { - if( insert_file(name?name:namelist[i]->d_name, full_path, (parent ? parent:""), i+startID) == 0 ) + if( insert_file(name, full_path, (parent ? parent:""), i+startID) == 0 ) fileno++; } - if( name ) - free(name); + free(name); free(namelist[i]); } free(namelist); diff --git a/release/src/router/minidlna/scanner.h b/release/src/router/minidlna/scanner.h index db978567e2..86f8461569 100644 --- a/release/src/router/minidlna/scanner.h +++ b/release/src/router/minidlna/scanner.h @@ -84,6 +84,6 @@ void start_scanner(); int -wait_for_mount(const char * path); // Tomato +wait_for_mount(const char * path); // Tomato #endif diff --git a/release/src/router/minidlna/tagutils/tagutils-asf.c b/release/src/router/minidlna/tagutils/tagutils-asf.c index b6ba0d7309..11da1ae530 100644 --- a/release/src/router/minidlna/tagutils/tagutils-asf.c +++ b/release/src/router/minidlna/tagutils/tagutils-asf.c @@ -39,6 +39,29 @@ _asf_read_file_properties(FILE *fp, asf_file_properties_t *p, __u32 size) return 0; } +static void +_pick_dlna_profile(struct song_metadata *psong, uint16_t format) +{ + /* DLNA Profile Name */ + switch( le16_to_cpu(format) ) + { + case WMA: + if( psong->max_bitrate < 193000 ) + asprintf(&(psong->dlna_pn), "WMABASE"); + else if( psong->max_bitrate < 385000 ) + asprintf(&(psong->dlna_pn), "WMAFULL"); + break; + case WMAPRO: + asprintf(&(psong->dlna_pn), "WMAPRO"); + break; + case WMALSL: + asprintf(&(psong->dlna_pn), "WMALSL%s", + psong->channels > 2 ? "_MULT5" : ""); + default: + break; + } +} + static int _asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size) { @@ -55,24 +78,9 @@ _asf_read_audio_stream(FILE *fp, struct song_metadata *psong, int size) psong->channels = le16_to_cpu(s.wfx.nChannels); psong->bitrate = le32_to_cpu(s.wfx.nAvgBytesPerSec) * 8; psong->samplerate = le32_to_cpu(s.wfx.nSamplesPerSec); - /* DLNA Profile Name */ - switch( le16_to_cpu(s.wfx.wFormatTag) ) - { - case WMAV1: - case WMAV2: - if( (psong->bitrate/1000+1) >= 385 || psong->samplerate > 48000 ) - asprintf(&(psong->dlna_pn), "WMAPRO"); - else if( ((psong->bitrate+1) / 1000) <= 192 ) - asprintf(&(psong->dlna_pn), "WMABASE"); - else - asprintf(&(psong->dlna_pn), "WMAFULL"); - break; - case WMAPRO: - asprintf(&(psong->dlna_pn), "WMAPRO"); - break; - default: - break; - } + if (!psong->max_bitrate) + psong->max_bitrate = psong->bitrate; + _pick_dlna_profile(psong, s.wfx.wFormatTag); return 0; } @@ -101,25 +109,11 @@ _asf_read_media_stream(FILE *fp, struct song_metadata *psong, __u32 size) psong->channels = le16_to_cpu(wfx.nChannels); psong->bitrate = le32_to_cpu(wfx.nAvgBytesPerSec) * 8; psong->samplerate = le32_to_cpu(wfx.nSamplesPerSec); - /* DLNA Profile Name */ - switch( le16_to_cpu(wfx.wFormatTag) ) - { - case WMAV1: - case WMAV2: - if( (psong->bitrate/1000+1) >= 385 || psong->samplerate > 48000 ) - asprintf(&(psong->dlna_pn), "WMAPRO"); - else if( (psong->bitrate / 1000)+1 < 192 ) - asprintf(&(psong->dlna_pn), "WMABASE"); - else - asprintf(&(psong->dlna_pn), "WMAFULL"); - break; - case WMAPRO: - asprintf(&(psong->dlna_pn), "WMAPRO"); - break; - default: - break; - } + if (!psong->max_bitrate) + psong->max_bitrate = psong->bitrate; + _pick_dlna_profile(psong, wfx.wFormatTag); } + return 0; } @@ -280,10 +274,10 @@ _asf_load_string(FILE *fp, int type, int size, char *buf, int len) case ASF_VT_QWORD: if(size >= 8) { + wd64 = (__s64 *) &data[0]; #if __WORDSIZE == 64 - i = snprintf(buf, len, "%ld", le64_to_cpu(*(__s64*)&data[0])); + i = snprintf(buf, len, "%ld", le64_to_cpu(*wd64)); #else - wd64 = (__s64 *) &data[0]; i = snprintf(buf, len, "%lld", le64_to_cpu(*wd64)); #endif } @@ -310,9 +304,7 @@ _asf_load_picture(FILE *fp, int size, void *bm, int *bm_size) { int i; char buf[256]; - char pic_type; - long pic_size; - +#if 0 // // Picture type $xx // Data length $xx $xx $xx $xx @@ -320,9 +312,15 @@ _asf_load_picture(FILE *fp, int size, void *bm, int *bm_size) // Description $00 // Picture data + char pic_type; + long pic_size; + pic_type = fget_byte(fp); size -= 1; pic_size = fget_le32(fp); size -= 4; - +#else + fseek(fp, 5, SEEK_CUR); + size -= 5; +#endif for(i = 0; i < sizeof(buf) - 1; i++) { buf[i] = fget_le16(fp); size -= 2; @@ -388,7 +386,6 @@ _get_asffileinfo(char *file, struct song_metadata *psong) asf_object_t hdr; asf_object_t tmp; unsigned long NumObjects; - unsigned short Reserved; unsigned short TitleLength; unsigned short AuthorLength; unsigned short CopyrightLength; @@ -400,7 +397,6 @@ _get_asffileinfo(char *file, struct song_metadata *psong) unsigned short ValueLength; off_t pos; char buf[2048]; - int mask; asf_file_properties_t FileProperties; psong->vbr_scale = -1; @@ -426,10 +422,9 @@ _get_asffileinfo(char *file, struct song_metadata *psong) return -1; } NumObjects = fget_le32(fp); - Reserved = fget_le16(fp); + fseek(fp, 2, SEEK_CUR); // Reserved le16 pos = ftell(fp); - mask = 0; while(NumObjects > 0) { if(sizeof(tmp) != fread(&tmp, 1, sizeof(tmp), fp)) @@ -447,6 +442,7 @@ _get_asffileinfo(char *file, struct song_metadata *psong) _asf_read_file_properties(fp, &FileProperties, tmp.Size); psong->song_length = le64_to_cpu(FileProperties.PlayDuration) / 10000; psong->bitrate = le64_to_cpu(FileProperties.MaxBitrate); + psong->max_bitrate = psong->bitrate; } else if(IsEqualGUID(&tmp.ID, &ASF_ContentDescription)) { diff --git a/release/src/router/minidlna/tagutils/tagutils-flc.c b/release/src/router/minidlna/tagutils/tagutils-flc.c index a2a9ec8777..b8f41d48c1 100644 --- a/release/src/router/minidlna/tagutils/tagutils-flc.c +++ b/release/src/router/minidlna/tagutils/tagutils-flc.c @@ -25,7 +25,6 @@ _get_flctags(char *filename, struct song_metadata *psong) { FLAC__Metadata_SimpleIterator *iterator = 0; FLAC__StreamMetadata *block; - int block_number; unsigned int sec, ms; int i; int err = 0; @@ -36,7 +35,6 @@ _get_flctags(char *filename, struct song_metadata *psong) return -1; } - block_number = 0; if(!FLAC__metadata_simple_iterator_init(iterator, filename, true, true)) { DPRINTF(E_ERROR, L_SCANNER, "Cannot extract tag from %s\n", filename); diff --git a/release/src/router/minidlna/tagutils/tagutils-ogg.c b/release/src/router/minidlna/tagutils/tagutils-ogg.c index 9efd5ccf95..ed9de4018a 100644 --- a/release/src/router/minidlna/tagutils/tagutils-ogg.c +++ b/release/src/router/minidlna/tagutils/tagutils-ogg.c @@ -196,7 +196,6 @@ static void _ogg_vorbis_end(ogg_stream_processor *stream, struct song_metadata *psong) { ogg_misc_vorbis_info *inf = stream->data; - long minutes, seconds; double bitrate, time; time = (double)inf->lastgranulepos / inf->vi.rate; @@ -211,9 +210,6 @@ _ogg_vorbis_end(ogg_stream_processor *stream, struct song_metadata *psong) psong->song_length = time * 1000; } - minutes = (long)time / 60; - seconds = (long)time - minutes * 60; - vorbis_comment_clear(&inf->vc); vorbis_info_clear(&inf->vi); @@ -307,7 +303,7 @@ static ogg_stream_processor * _ogg_find_stream_processor(ogg_stream_set *set, ogg_page *page) { ogg_uint32_t serial = ogg_page_serialno(page); - int i, found = 0; + int i; int invalid = 0; int constraint = 0; ogg_stream_processor *stream; @@ -316,7 +312,6 @@ _ogg_find_stream_processor(ogg_stream_set *set, ogg_page *page) { if(serial == set->streams[i].serial) { - found = 1; stream = &(set->streams[i]); set->in_headers = 0; diff --git a/release/src/router/minidlna/tagutils/tagutils.h b/release/src/router/minidlna/tagutils/tagutils.h index 395e6c5ce8..0d5f3625b6 100644 --- a/release/src/router/minidlna/tagutils/tagutils.h +++ b/release/src/router/minidlna/tagutils/tagutils.h @@ -75,6 +75,7 @@ struct song_metadata { char compilation; // YTCP int bitrate; + int max_bitrate; int samplerate; int samplesize; int channels; @@ -105,9 +106,9 @@ struct song_metadata { int plist_id; }; -#define WMAV1 0x161 -#define WMAV2 0x162 -#define WMAPRO 0x163 +#define WMA 0x161 +#define WMAPRO 0x162 +#define WMALSL 0x163 extern int scan_init(char *path); extern void make_composite_tags(struct song_metadata *psong); diff --git a/release/src/router/minidlna/tagutils/textutils.c b/release/src/router/minidlna/tagutils/textutils.c index d80da3d9dd..6cffa17101 100644 --- a/release/src/router/minidlna/tagutils/textutils.c +++ b/release/src/router/minidlna/tagutils/textutils.c @@ -289,6 +289,7 @@ fetch_string_txt(char *fname, char *lang, int n, ...) if (!*strs[i]) *strs[i] = defstr[i]; } + fclose(fp); _exit: free(keys); diff --git a/release/src/router/minidlna/tivo_beacon.c b/release/src/router/minidlna/tivo_beacon.c index 8de73af215..5f111322a7 100644 --- a/release/src/router/minidlna/tivo_beacon.c +++ b/release/src/router/minidlna/tivo_beacon.c @@ -51,8 +51,6 @@ #include "upnpglobalvars.h" #include "log.h" -static struct aBeacon* topBeacon = NULL; - /* OpenAndConfHTTPSocket() : * setup the socket used to handle incoming HTTP connections. */ int @@ -187,8 +185,6 @@ rcvBeaconMessage(char * beacon) char * cp; char * scp; char * tokptr; - struct aBeacon * b; - time_t current; cp = strtok_r(beacon, "=\r\n", &tokptr); while( cp != NULL ) @@ -217,6 +213,14 @@ rcvBeaconMessage(char * beacon) if( strcmp(identity, uuidvalue) == 0) return 0; +#ifdef DEBUG + static struct aBeacon* topBeacon = NULL; + struct aBeacon * b; + time_t current; + int len; + char buf[32]; + static time_t lastSummary = 0; + current = time(NULL); for( b = topBeacon; b != NULL; b = b->next ) { @@ -241,10 +245,6 @@ rcvBeaconMessage(char * beacon) platform ? platform : "-", services ? services : "-" ); } -#ifdef DEBUG - int len; - char buf[32]; - static time_t lastSummary = 0; b->lastSeen = current; if( !lastSummary ) @@ -292,6 +292,21 @@ void ProcessTiVoBeacon(int s) (struct sockaddr *)&sendername, &len_r); if( n > 0 ) bufr[n] = '\0'; + + /* find which subnet the client is in */ + for(n = 0; n #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,53 +112,44 @@ 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, flags = 0, count; + int ret = 0; + struct string_s *str = passed_args->str; if( strncmp(class, "item", 4) == 0 ) { + int flags = 0; unescape_tag(title); 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 ) @@ -164,116 +157,90 @@ 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", - atoi(rindex(duration, '.')+1) + (1000*atoi(rindex(duration, ':')+1)) + (60000*atoi(rindex(duration, ':')-2)) + (3600000*atoi(duration))); - memcpy(passed_args->resp+passed_args->size, &str_buf, ret+1); - passed_args->size += ret; + ret = strcatf(str, "%d", + atoi(strrchr(duration, '.')+1) + (1000*atoi(strrchr(duration, ':')+1)) + + (60000*atoi(strrchr(duration, ':')-2)) + (3600000*atoi(duration))); } 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 = 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", sampleFrequency); + } + 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 *name = basename(path); - char *esc_name = escape_tag(name); - ret = sprintf(str_buf, "" - "video/*" - "urn:tivo:image:save-until-i-delete-recording" - "" - "Videos" - "%s ", esc_name?esc_name:name); - if( esc_name ) - free(esc_name); + char *esc_name = escape_tag(basename(path), 1); + 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 ) { + int count; /* Determine the number of children */ #ifdef __sparc__ /* Adding filters on large containers can take a long time on slow processors */ count = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", id); @@ -282,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++; @@ -319,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)" @@ -336,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); } @@ -347,24 +312,25 @@ void SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int itemCount, char * anchorItem, int anchorOffset, int recurse, char * sortOrder, char * filter, unsigned long int randomSeed) { - char * resp = malloc(262144); + char *resp = malloc(262144); char *sql, *item, *saveptr; char *zErrMsg = NULL; char **result; - char *title = NULL; + char *title, *which; char what[10], order[96]={0}, order2[96]={0}, myfilter[256]={0}; char str_buf[1024]; - char *which; 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(resp, 0, sizeof(262144)); + 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; @@ -412,17 +378,14 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite } else { - sql = sqlite3_mprintf("SELECT NAME from OBJECTS where OBJECT_ID = '%s'", objectID); - if( (sql_get_table(db, sql, &result, &ret, NULL) == SQLITE_OK) && ret ) + item = sql_get_text_field(db, "SELECT NAME from OBJECTS where OBJECT_ID = '%s'", objectID); + if( item ) { - title = escape_tag(result[1]); - if( !title ) - title = strdup(result[1]); + title = escape_tag(item, 1); + sqlite3_free(item); } else title = strdup("UNKNOWN"); - sqlite3_free(sql); - sqlite3_free_table(result); } if( recurse ) @@ -523,11 +486,11 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite } if( title_state != -1 ) { - strcat(order, "TITLE ASC"); + strcat(order, "TITLE ASC, "); if( itemCount >= 0 ) - strcat(order2, "TITLE ASC"); + strcat(order2, "TITLE ASC, "); else - strcat(order2, "TITLE DESC"); + strcat(order2, "TITLE DESC, "); } strcat(order, "DETAIL_ID ASC"); if( itemCount >= 0 ) @@ -594,7 +557,9 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite if( strstr(anchorItem, "QueryContainer") ) { strcpy(what, "OBJECT_ID"); - anchorItem = rindex(anchorItem, '=')+1; + saveptr = strrchr(anchorItem, '='); + if( saveptr ) + anchorItem = saveptr + 1; } else { @@ -649,7 +614,13 @@ SendContainer(struct upnphttp * h, const char * objectID, int itemStart, int ite { DPRINTF(E_ERROR, L_HTTP, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); + Send500(h); + free(title); + free(which); + free(resp); + return; } + strcatf(&str, ""); ret = sprintf(str_buf, "\n" "" @@ -662,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); } @@ -693,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/release/src/router/minidlna/tivo_utils.c b/release/src/router/minidlna/tivo_utils.c index 8adbe57b7b..2ae6247aae 100644 --- a/release/src/router/minidlna/tivo_utils.c +++ b/release/src/router/minidlna/tivo_utils.c @@ -74,6 +74,8 @@ decodeString(char * string, int inplace) } if( inplace ) { + if( ns ) + free(ns); return string; } else diff --git a/release/src/router/minidlna/upnpdescgen.c b/release/src/router/minidlna/upnpdescgen.c index fe40199c0f..97eaabdcbb 100644 --- a/release/src/router/minidlna/upnpdescgen.c +++ b/release/src/router/minidlna/upnpdescgen.c @@ -1,4 +1,4 @@ -/* $Id: upnpdescgen.c,v 1.17 2011/02/17 23:17:24 jmaggard Exp $ */ +/* $Id: upnpdescgen.c,v 1.18 2011/05/02 23:50:52 jmaggard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * @@ -527,6 +527,7 @@ static const struct serviceDesc scpdX_MS_MediaReceiverRegistrar = static char * strcat_str(char * str, int * len, int * tmplen, const char * s2) { + char *p; int s2len; s2len = (int)strlen(s2); if(*tmplen <= (*len + s2len)) @@ -535,7 +536,17 @@ strcat_str(char * str, int * len, int * tmplen, const char * s2) *tmplen += 256; else *tmplen += s2len + 1; - str = (char *)realloc(str, *tmplen); + p = realloc(str, *tmplen); + if (!p) + { + if(s2len < 256) + *tmplen -= 256; + else + *tmplen -= s2len + 1; + return str; + } + else + str = p; } /*strcpy(str + *len, s2); */ memcpy(str + *len, s2, s2len + 1); @@ -549,10 +560,18 @@ strcat_str(char * str, int * len, int * tmplen, const char * s2) static char * strcat_char(char * str, int * len, int * tmplen, char c) { + char *p; if(*tmplen <= (*len + 1)) { *tmplen += 256; - str = (char *)realloc(str, *tmplen); + p = (char *)realloc(str, *tmplen); + if (!p) + { + *tmplen -= 256; + return str; + } + else + str = p; } str[*len] = c; (*len)++; diff --git a/release/src/router/minidlna/upnpglobalvars.h b/release/src/router/minidlna/upnpglobalvars.h index d9a39d3968..0b52075792 100644 --- a/release/src/router/minidlna/upnpglobalvars.h +++ b/release/src/router/minidlna/upnpglobalvars.h @@ -56,7 +56,7 @@ #include -#define MINIDLNA_VERSION "1.0.19.1" +#define MINIDLNA_VERSION "1.0.20" #ifdef NETGEAR # define SERVER_NAME "ReadyDLNA" @@ -78,16 +78,11 @@ #define PNPX 0 #endif -#if 0 // Add these once the newer ffmpeg libs that can detect WMAPRO are more widely used - "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=WMVMED_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," -#endif #define RESOURCE_PROTOCOL_INFO_VALUES \ "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_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:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HP_HD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_HP_HD_AC3_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mpeg:DLNA.ORG_PN=AVC_TS_MP_HD_AAC_MULT5_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ @@ -109,7 +104,7 @@ "http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;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/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AC3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ - "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_CIF15_AAC_540;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_CIF15_AAC_520;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_CIF30_AAC_940;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_L31_HD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_BL_L32_HD_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ @@ -142,15 +137,17 @@ "http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ - "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=WMAFULL;DLNA.ORG_OP=01," \ - "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAPRO;DLNA.ORG_OP=01," \ - "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01," \ - "http-get:*:audio/3gpp: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_MULT5_ISO;DLNA.ORG_OP=01," \ - "http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01," \ + "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAPRO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMALSL;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMALSL_MULT5;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/3gpp:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/mp4:DLNA.ORG_PN=AAC_MULT5_ISO;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ + "http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_CI=0," \ "http-get:*:image/jpeg:*," \ "http-get:*:video/avi:*," \ "http-get:*:video/divx:*," \ diff --git a/release/src/router/minidlna/upnphttp.c b/release/src/router/minidlna/upnphttp.c index f2000dae61..d479d3157e 100644 --- a/release/src/router/minidlna/upnphttp.c +++ b/release/src/router/minidlna/upnphttp.c @@ -117,10 +117,8 @@ Delete_upnphttp(struct upnphttp * h) { if(h->socket >= 0) CloseSocket_upnphttp(h); - if(h->req_buf) - free(h->req_buf); - if(h->res_buf) - free(h->res_buf); + free(h->req_buf); + free(h->res_buf); free(h); } } @@ -257,15 +255,32 @@ intervening space) by either an integer or the keyword "infinite". */ p++; if(strncasecmp(p, "bytes=", 6)==0) { h->reqflags |= FLAG_RANGE; - h->req_RangeEnd = atoll(index(p+6, '-')+1); - h->req_RangeStart = atoll(p+6); + h->req_RangeStart = strtoll(p+6, &colon, 10); + h->req_RangeEnd = colon ? atoll(colon+1) : 0; DPRINTF(E_DEBUG, L_HTTP, "Range Start-End: %lld - %lld\n", h->req_RangeStart, h->req_RangeEnd?h->req_RangeEnd:-1); } } else if(strncasecmp(line, "Host", 4)==0) { + int i; h->reqflags |= FLAG_HOST; + p = colon + 1; + while(isspace(*p)) + p++; + for(n = 0; niface = n; + break; + } + } } else if(strncasecmp(line, "User-Agent", 10)==0) { @@ -287,13 +302,20 @@ intervening space) by either an integer or the keyword "infinite". */ h->reqflags |= FLAG_DLNA; h->reqflags |= FLAG_MIME_AVI_DIVX; } - else if(strncmp(p, "SamsungWiselinkPro", 18)==0 || - strncmp(p, "SEC_HHP_", 8)==0) + else if(strncmp(p, "SEC_HHP_", 8)==0) { h->req_client = ESamsungTV; + h->reqflags |= FLAG_SAMSUNG; + h->reqflags |= FLAG_DLNA; + h->reqflags |= FLAG_NO_RESIZE; + } + else if(strncmp(p, "SamsungWiselinkPro", 18)==0 || + strncmp(p, "SEC_HHP_TV", 10)==0) + { + h->req_client = ESamsungSeriesA; + h->reqflags |= FLAG_SAMSUNG; h->reqflags |= FLAG_DLNA; h->reqflags |= FLAG_NO_RESIZE; - //h->reqflags |= FLAG_MIME_AVI_DIVX; } else if(strstrc(p, "bridgeCo-DMP/3", '\r')) { @@ -314,6 +336,16 @@ intervening space) by either an integer or the keyword "infinite". */ h->req_client = EMediaRoom; h->reqflags |= FLAG_MS_PFS; } + else if(strstrc(p, "LGE_DLNA_SDK", '\r')) + { + h->req_client = ELGDevice; + h->reqflags |= FLAG_DLNA; + } + else if(strncmp(p, "Verismo,", 8)==0) + { + h->req_client = ENetgearEVA2000; + h->reqflags |= FLAG_MS_PFS; + } else if(strstrc(p, "UPnP/1.0 DLNADOC/1.50 Intel_SDK_for_UPnP_devices/1.2", '\r')) { h->req_client = EToshibaTV; @@ -542,7 +574,7 @@ Send416(struct upnphttp * h) } /* very minimalistic 500 error message */ -static void +void Send500(struct upnphttp * h) { static const char body500[] = @@ -853,13 +885,14 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h) } else { - printf("Invalid TiVo request! %s\n", HttpUrl+12); + DPRINTF(E_WARN, L_HTTP, "Invalid TiVo request! %s\n", HttpUrl+12); Send404(h); } } else { - printf("TiVo request with out TiVo support enabled! %s\n", HttpUrl+12); + DPRINTF(E_WARN, L_HTTP, "TiVo request with out TiVo support enabled! %s\n", + HttpUrl+12); Send404(h); } } @@ -1174,18 +1207,17 @@ send_file(struct upnphttp * h, int sendfd, off_t offset, off_t end_offset) } offset+=ret; } - if( buf ) - free(buf); + free(buf); } void SendResp_icon(struct upnphttp * h, char * icon) { - char * header; - char * data; - int size, ret; - char mime[12]; + char header[512]; + char mime[12] = "image/"; char date[30]; + char *data; + int size, ret; time_t curtime = time(NULL); if( strcmp(icon, "sm.png") == 0 ) @@ -1193,28 +1225,28 @@ SendResp_icon(struct upnphttp * h, char * icon) DPRINTF(E_DEBUG, L_HTTP, "Sending small PNG icon\n"); data = (char *)png_sm; size = sizeof(png_sm)-1; - strcpy(mime, "image/png"); + strcpy(mime+6, "png"); } else if( strcmp(icon, "lrg.png") == 0 ) { DPRINTF(E_DEBUG, L_HTTP, "Sending large PNG icon\n"); data = (char *)png_lrg; size = sizeof(png_lrg)-1; - strcpy(mime, "image/png"); + strcpy(mime+6, "png"); } else if( strcmp(icon, "sm.jpg") == 0 ) { DPRINTF(E_DEBUG, L_HTTP, "Sending small JPEG icon\n"); data = (char *)jpeg_sm; size = sizeof(jpeg_sm)-1; - strcpy(mime, "image/jpeg"); + strcpy(mime+6, "jpeg"); } else if( strcmp(icon, "lrg.jpg") == 0 ) { DPRINTF(E_DEBUG, L_HTTP, "Sending large JPEG icon\n"); data = (char *)jpeg_lrg; size = sizeof(jpeg_lrg)-1; - strcpy(mime, "image/jpeg"); + strcpy(mime+6, "jpeg"); } else { @@ -1223,38 +1255,33 @@ SendResp_icon(struct upnphttp * h, char * icon) return; } - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - ret = asprintf(&header, "HTTP/1.1 200 OK\r\n" - "Content-Type: %s\r\n" - "Content-Length: %d\r\n" - "Connection: close\r\n" - "Date: %s\r\n" - "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n", - mime, size, date); + ret = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\n" + "Content-Type: %s\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "Date: %s\r\n" + "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n", + mime, size, date); - if( (send_data(h, header, ret, MSG_MORE) == 0) && (h->req_command != EHead) ) + if( send_data(h, header, ret, MSG_MORE) == 0 ) { - send_data(h, data, size, 0); + if( h->req_command != EHead ) + send_data(h, data, size, 0); } - free(header); } void SendResp_albumArt(struct upnphttp * h, char * object) { - char header[1500]; - char sql_buf[256]; - char **result; - int rows = 0; + char header[512]; char *path; char *dash; char date[30]; time_t curtime = time(NULL); - off_t offset = 0, size; - int sendfh; - - memset(header, 0, 1500); + off_t size; + int fd; + int ret; if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE ) { @@ -1266,98 +1293,81 @@ SendResp_albumArt(struct upnphttp * h, char * object) dash = strchr(object, '-'); if( dash ) *dash = '\0'; - sprintf(sql_buf, "SELECT PATH from ALBUM_ART where ID = %s", object); - sql_get_table(db, sql_buf, &result, &rows, NULL); - if( !rows ) + + path = sql_get_text_field(db, "SELECT PATH from ALBUM_ART where ID = '%s'", object); + if( !path ) { DPRINTF(E_WARN, L_HTTP, "ALBUM_ART ID %s not found, responding ERROR 404\n", object); Send404(h); - goto error; + return; } - path = result[1]; DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %s [%s]\n", object, path); - if( access(path, F_OK) == 0 ) - { - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); + strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - sendfh = open(path, O_RDONLY); - if( sendfh < 0 ) { - DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); - goto error; - } - size = lseek(sendfh, 0, SEEK_END); - lseek(sendfh, 0, SEEK_SET); - - sprintf(header, "HTTP/1.1 200 OK\r\n" - "Content-Type: image/jpeg\r\n" - "Content-Length: %jd\r\n" - "Connection: close\r\n" - "Date: %s\r\n" - "EXT:\r\n" - "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" - "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n" - "Server: " MINIDLNA_SERVER_STRING "\r\n", - size, date); - - if( h->reqflags & FLAG_XFERBACKGROUND ) - { - strcat(header, "transferMode.dlna.org: Background\r\n\r\n"); - } - else //if( h->reqflags & FLAG_XFERINTERACTIVE ) - { - strcat(header, "transferMode.dlna.org: Interactive\r\n\r\n"); - } + fd = open(path, O_RDONLY); + if( fd < 0 ) { + DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); + sqlite3_free(path); + Send404(h); + return; + } + sqlite3_free(path); + size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + ret = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\n" + "Content-Type: image/jpeg\r\n" + "Content-Length: %jd\r\n" + "Connection: close\r\n" + "Date: %s\r\n" + "EXT:\r\n" + "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" + "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n" + "Server: " MINIDLNA_SERVER_STRING "\r\n" + "transferMode.dlna.org: %s\r\n\r\n", + (intmax_t)size, date, + (h->reqflags & FLAG_XFERBACKGROUND) ? "Background" : "Interactive"); - if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) ) - { - send_file(h, sendfh, offset, size); - } - close(sendfh); + if( send_data(h, header, ret, MSG_MORE) == 0 ) + { + if( h->req_command != EHead ) + send_file(h, fd, 0, size-1); } - error: - sqlite3_free_table(result); + close(fd); } void SendResp_caption(struct upnphttp * h, char * object) { - char header[1500]; - char sql_buf[256]; - char **result; - int rows = 0; + char header[512]; char *path; char date[30]; time_t curtime = time(NULL); - off_t offset = 0, size; - int sendfh, ret; - - memset(header, 0, 1500); + off_t size; + int fd, ret; strip_ext(object); - sprintf(sql_buf, "SELECT PATH from CAPTIONS where ID = %s", object); - sql_get_table(db, sql_buf, &result, &rows, NULL); - if( !rows ) + path = sql_get_text_field(db, "SELECT PATH from CAPTIONS where ID = %s", object); + if( !path ) { DPRINTF(E_WARN, L_HTTP, "CAPTION ID %s not found, responding ERROR 404\n", object); Send404(h); - goto error; + return; } - path = result[1]; DPRINTF(E_INFO, L_HTTP, "Serving caption ID: %s [%s]\n", object, path); - if( access(path, F_OK) != 0 ) - goto error; - - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - sendfh = open(path, O_RDONLY); - if( sendfh < 0 ) { + fd = open(path, O_RDONLY); + if( fd < 0 ) { DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); - goto error; + sqlite3_free(path); + Send404(h); + return; } - size = lseek(sendfh, 0, SEEK_END); - lseek(sendfh, 0, SEEK_SET); + sqlite3_free(path); + size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); ret = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\n" "Content-Type: smi/caption\r\n" @@ -1366,33 +1376,27 @@ SendResp_caption(struct upnphttp * h, char * object) "Date: %s\r\n" "EXT:\r\n" "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n", - size, date); + (intmax_t)size, date); - if( (send_data(h, header, ret, MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) ) + if( send_data(h, header, ret, MSG_MORE) == 0 ) { - send_file(h, sendfh, offset, size); + if( h->req_command != EHead ) + send_file(h, fd, 0, size-1); } - close(sendfh); - - error: - sqlite3_free_table(result); + close(fd); } void SendResp_thumbnail(struct upnphttp * h, char * object) { - char header[1500]; - char sql_buf[256]; - char **result; - int rows = 0; + char header[512]; char *path; char date[30]; time_t curtime = time(NULL); + int ret; ExifData *ed; ExifLoader *l; - memset(header, 0, 1500); - if( h->reqflags & FLAG_XFERSTREAMING || h->reqflags & FLAG_RANGE ) { DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Streaming with an image!\n"); @@ -1401,88 +1405,86 @@ SendResp_thumbnail(struct upnphttp * h, char * object) } strip_ext(object); - sprintf(sql_buf, "SELECT PATH from DETAILS where ID = '%s'", object); - sql_get_table(db, sql_buf, &result, &rows, NULL); - if( !rows ) + path = sql_get_text_field(db, "SELECT PATH from DETAILS where ID = '%s'", object); + if( !path ) { - DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", object); + DPRINTF(E_WARN, L_HTTP, "DETAIL ID %s not found, responding ERROR 404\n", object); Send404(h); - goto error; + return; } - path = result[1]; DPRINTF(E_INFO, L_HTTP, "Serving thumbnail for ObjectId: %s [%s]\n", object, path); - if( access(path, F_OK) == 0 ) + if( access(path, F_OK) != 0 ) { - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); + DPRINTF(E_ERROR, L_HTTP, "Error accessing %s\n", path); + sqlite3_free(path); + return; + } + strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - l = exif_loader_new(); - exif_loader_write_file(l, path); - ed = exif_loader_get_data(l); - exif_loader_unref(l); + l = exif_loader_new(); + exif_loader_write_file(l, path); + ed = exif_loader_get_data(l); + exif_loader_unref(l); + sqlite3_free(path); - if( !ed || !ed->size ) - { - Send404(h); - if( ed ) - exif_data_unref(ed); - goto error; - } - sprintf(header, "HTTP/1.1 200 OK\r\n" - "Content-Type: image/jpeg\r\n" - "Content-Length: %d\r\n" - "Connection: close\r\n" - "Date: %s\r\n" - "EXT:\r\n" - "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" - "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n" - "Server: " MINIDLNA_SERVER_STRING "\r\n", - ed->size, date); - - if( h->reqflags & FLAG_XFERBACKGROUND ) - { - strcat(header, "transferMode.dlna.org: Background\r\n\r\n"); - } - else //if( h->reqflags & FLAG_XFERINTERACTIVE ) - { - strcat(header, "transferMode.dlna.org: Interactive\r\n\r\n"); - } + if( !ed || !ed->size ) + { + Send404(h); + if( ed ) + exif_data_unref(ed); + return; + } + ret = snprintf(header, sizeof(header), "HTTP/1.1 200 OK\r\n" + "Content-Type: image/jpeg\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "Date: %s\r\n" + "EXT:\r\n" + "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" + "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_TN\r\n" + "Server: " MINIDLNA_SERVER_STRING "\r\n" + "transferMode.dlna.org: %s\r\n\r\n", + ed->size, date, + (h->reqflags & FLAG_XFERBACKGROUND) ? "Background" : "Interactive"); - if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) ) - { + if( send_data(h, header, ret, MSG_MORE) == 0 ) + { + if( h->req_command != EHead ) send_data(h, (char *)ed->data, ed->size, 0); - } - exif_data_unref(ed); } + exif_data_unref(ed); CloseSocket_upnphttp(h); - error: - sqlite3_free_table(result); } void SendResp_resizedimg(struct upnphttp * h, char * object) { - char header[1500]; + char header[512]; char str_buf[256]; + struct string_s str; char **result; char date[30]; char dlna_pn[4]; time_t curtime = time(NULL); - int width=640, height=480, dstw, dsth, rotation, size; + int width=640, height=480, dstw, dsth, size; long srcw, srch; unsigned char * data = NULL; char *path, *file_path; - char *resolution, *tn; + char *resolution; char *key, *val; char *saveptr=NULL, *item=NULL; + /* Not implemented yet * char *pixelshape=NULL; + int rotation; */ sqlite_int64 id; - int rows=0, chunked=0, ret; + int rows=0, chunked, ret; #ifdef __sparc__ + char *tn; ExifData *ed; ExifLoader *l; #endif - image *imsrc = NULL, *imdst = NULL; + image_s *imsrc = NULL, *imdst = NULL; int scale = 1; id = strtoll(object, NULL, 10); @@ -1509,7 +1511,6 @@ SendResp_resizedimg(struct upnphttp * h, char * object) #endif file_path = result[3]; resolution = result[4]; - tn = result[5]; srcw = strtol(resolution, &saveptr, 10); srch = strtol(saveptr+1, NULL, 10); @@ -1520,9 +1521,9 @@ SendResp_resizedimg(struct upnphttp * h, char * object) } while( item != NULL ) { - #ifdef TIVO_SUPPORT +#ifdef TIVO_SUPPORT decodeString(item, 1); - #endif +#endif val = item; key = strsep(&val, "="); DPRINTF(E_DEBUG, L_GENERAL, "%s: %s\n", key, val); @@ -1534,6 +1535,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object) { height = atoi(val); } + /* Not implemented yet * else if( strcasecmp(key, "rotation") == 0 ) { rotation = atoi(val); @@ -1541,7 +1543,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object) else if( strcasecmp(key, "pixelshape") == 0 ) { pixelshape = val; - } + } */ item = strtok_r(NULL, "&,", &saveptr); } free(path); @@ -1579,26 +1581,32 @@ SendResp_resizedimg(struct upnphttp * h, char * object) scale = 2; strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - snprintf(header, sizeof(header)-100, "HTTP/1.1 200 OK\r\n" - "Content-Type: image/jpeg\r\n" - "Connection: close\r\n" - "Date: %s\r\n" - "EXT:\r\n" - "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" - "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_%s;DLNA.ORG_CI=1\r\n" - "Server: " MINIDLNA_SERVER_STRING "\r\n", - date, dlna_pn); + + str.data = header; + str.size = sizeof(header); + str.off = 0; + + strcatf(&str, "HTTP/1.1 200 OK\r\n" + "Content-Type: image/jpeg\r\n" + "Connection: close\r\n" + "Date: %s\r\n" + "EXT:\r\n" + "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" + "contentFeatures.dlna.org: DLNA.ORG_PN=JPEG_%s;DLNA.ORG_CI=1\r\n" + "Server: " MINIDLNA_SERVER_STRING "\r\n", + date, dlna_pn); if( h->reqflags & FLAG_XFERINTERACTIVE ) { - strcat(header, "transferMode.dlna.org: Interactive\r\n"); + strcatf(&str, "transferMode.dlna.org: Interactive\r\n"); } else if( h->reqflags & FLAG_XFERBACKGROUND ) { - strcat(header, "transferMode.dlna.org: Background\r\n"); + strcatf(&str, "transferMode.dlna.org: Background\r\n"); } /* Resizing from a thumbnail is much faster than from a large image */ #ifdef __sparc__ + tn = result[5]; if( dstw <= 160 && dsth <= 120 && atoi(tn) ) { l = exif_loader_new(); @@ -1621,12 +1629,13 @@ SendResp_resizedimg(struct upnphttp * h, char * object) #endif if( strcmp(h->HttpVer, "HTTP/1.0") == 0 ) { + chunked = 0; imsrc = image_new_from_jpeg(file_path, 1, NULL, 0, scale); } else { chunked = 1; - strcat(header, "Transfer-Encoding: chunked\r\n\r\n"); + strcatf(&str, "Transfer-Encoding: chunked\r\n\r\n"); } if( !chunked ) @@ -1641,11 +1650,10 @@ SendResp_resizedimg(struct upnphttp * h, char * object) imdst = image_resize(imsrc, dstw, dsth); data = image_save_to_jpeg_buf(imdst, &size); - sprintf(str_buf, "Content-Length: %d\r\n\r\n", size); - strcat(header, str_buf); + strcatf(&str, "Content-Length: %d\r\n\r\n", size); } - if( (send_data(h, header, strlen(header), 0) == 0) && (h->req_command != EHead) ) + if( (send_data(h, str.data, str.off, 0) == 0) && (h->req_command != EHead) ) { if( chunked ) { @@ -1685,8 +1693,8 @@ SendResp_resizedimg(struct upnphttp * h, char * object) void SendResp_dlnafile(struct upnphttp * h, char * object) { - char header[1500]; - char hdr_buf[512]; + char header[1024]; + struct string_s str; char sql_buf[256]; char **result; int rows, ret; @@ -1695,7 +1703,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object) off_t total, offset, size; sqlite_int64 id; int sendfh; - static struct { sqlite_int64 id; char path[PATH_MAX]; char mime[32]; char dlna[64]; } last_file = { 0 }; + static struct { sqlite_int64 id; char path[PATH_MAX]; char mime[32]; char dlna[96]; } last_file = { 0 }; #if USE_FORK pid_t newpid = 0; #endif @@ -1725,10 +1733,16 @@ SendResp_dlnafile(struct upnphttp * h, char * object) { strncpy(last_file.mime, result[4], sizeof(last_file.mime)-1); /* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */ - if( h->req_client == ESamsungTV ) + if( h->reqflags & FLAG_SAMSUNG ) { if( strcmp(last_file.mime+6, "x-matroska") == 0 ) strcpy(last_file.mime+8, "mkv"); + /* Samsung TV's such as the A750 can natively support many + Xvid/DivX AVI's however, the DLNA server needs the + mime type to say video/mpeg */ + else if( h->req_client == ESamsungSeriesA && + strcmp(last_file.mime+6, "x-msvideo") == 0 ) + strcpy(last_file.mime+6, "mpeg"); } /* ... and Sony BDP-S370 won't play MKV unless we pretend it's a DiVX file */ else if( h->req_client == ESonyBDP ) @@ -1780,7 +1794,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object) DPRINTF(E_WARN, L_HTTP, "Client tried to specify transferMode as Interactive without an image!\n"); /* Samsung TVs (well, at least the A950) do this for some reason, * and I don't see them fixing this bug any time soon. */ - if( h->req_client != ESamsungTV || GETFLAG(DLNA_STRICT_MASK) ) + if( !(h->reqflags & FLAG_SAMSUNG) || GETFLAG(DLNA_STRICT_MASK) ) { Send406(h); goto error; @@ -1793,17 +1807,26 @@ SendResp_dlnafile(struct upnphttp * h, char * object) sendfh = open(last_file.path, O_RDONLY); if( sendfh < 0 ) { DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", last_file.path); + Send404(h); goto error; } size = lseek(sendfh, 0, SEEK_END); lseek(sendfh, 0, SEEK_SET); - sprintf(header, "HTTP/1.1 20%c OK\r\n" - "Content-Type: %s\r\n", (h->reqflags & FLAG_RANGE ? '6' : '0'), last_file.mime); + str.data = header; + str.size = sizeof(header); + str.off = 0; + + strcatf(&str, "HTTP/1.1 20%c OK\r\n" + "Content-Type: %s\r\n", + (h->reqflags & FLAG_RANGE ? '6' : '0'), + last_file.mime); if( h->reqflags & FLAG_RANGE ) { - if( !h->req_RangeEnd ) - h->req_RangeEnd = size; + if( !h->req_RangeEnd || h->req_RangeEnd == size ) + { + h->req_RangeEnd = size - 1; + } if( (h->req_RangeStart > h->req_RangeEnd) || (h->req_RangeStart < 0) ) { DPRINTF(E_WARN, L_HTTP, "Specified range was invalid!\n"); @@ -1811,7 +1834,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object) close(sendfh); goto error; } - if( h->req_RangeEnd > size ) + if( h->req_RangeEnd >= size ) { DPRINTF(E_WARN, L_HTTP, "Specified range was outside file boundaries!\n"); Send416(h); @@ -1819,49 +1842,38 @@ SendResp_dlnafile(struct upnphttp * h, char * object) goto error; } - if( h->req_RangeEnd < size ) - { - total = h->req_RangeEnd - h->req_RangeStart + 1; - sprintf(hdr_buf, "Content-Length: %jd\r\n" - "Content-Range: bytes %jd-%jd/%jd\r\n", - total, h->req_RangeStart, h->req_RangeEnd, size); - } - else - { - h->req_RangeEnd = size; - total = size - h->req_RangeStart; - sprintf(hdr_buf, "Content-Length: %jd\r\n" - "Content-Range: bytes %jd-%jd/%jd\r\n", - total, h->req_RangeStart, size-1, size); - } + total = h->req_RangeEnd - h->req_RangeStart + 1; + strcatf(&str, "Content-Length: %jd\r\n" + "Content-Range: bytes %jd-%jd/%jd\r\n", + (intmax_t)total, (intmax_t)h->req_RangeStart, + (intmax_t)h->req_RangeEnd, (intmax_t)size); } else { - h->req_RangeEnd = size; + h->req_RangeEnd = size - 1; total = size; - sprintf(hdr_buf, "Content-Length: %jd\r\n", total); + strcatf(&str, "Content-Length: %jd\r\n", (intmax_t)total); } - strcat(header, hdr_buf); if( h->reqflags & FLAG_XFERSTREAMING ) { - strcat(header, "transferMode.dlna.org: Streaming\r\n"); + strcatf(&str, "transferMode.dlna.org: Streaming\r\n"); } else if( h->reqflags & FLAG_XFERBACKGROUND ) { if( strncmp(last_file.mime, "image", 5) == 0 ) - strcat(header, "transferMode.dlna.org: Background\r\n"); + strcatf(&str, "transferMode.dlna.org: Background\r\n"); } else //if( h->reqflags & FLAG_XFERINTERACTIVE ) { if( (strncmp(last_file.mime, "video", 5) == 0) || (strncmp(last_file.mime, "audio", 5) == 0) ) { - strcat(header, "transferMode.dlna.org: Streaming\r\n"); + strcatf(&str, "transferMode.dlna.org: Streaming\r\n"); } else { - strcat(header, "transferMode.dlna.org: Interactive\r\n"); + strcatf(&str, "transferMode.dlna.org: Interactive\r\n"); } } @@ -1869,25 +1881,24 @@ SendResp_dlnafile(struct upnphttp * h, char * object) { if( sql_get_int_field(db, "SELECT ID from CAPTIONS where ID = '%lld'", id) > 0 ) { - sprintf(hdr_buf, "CaptionInfo.sec: http://%s:%d/Captions/%lld.srt\r\n", - lan_addr[0].str, runtime_vars.port, id); - strcat(header, hdr_buf); + strcatf(&str, "CaptionInfo.sec: http://%s:%d/Captions/%lld.srt\r\n", + lan_addr[h->iface].str, runtime_vars.port, id); } } - sprintf(hdr_buf, "Accept-Ranges: bytes\r\n" - "Connection: close\r\n" - "Date: %s\r\n" - "EXT:\r\n" - "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" - "contentFeatures.dlna.org: %s\r\n" - "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n", - date, last_file.dlna); - strcat(header, hdr_buf); + strcatf(&str, "Accept-Ranges: bytes\r\n" + "Connection: close\r\n" + "Date: %s\r\n" + "EXT:\r\n" + "realTimeInfo.dlna.org: DLNA.ORG_TLAG=*\r\n" + "contentFeatures.dlna.org: %s\r\n" + "Server: " MINIDLNA_SERVER_STRING "\r\n\r\n", + date, last_file.dlna); - if( (send_data(h, header, strlen(header), MSG_MORE) == 0) && (h->req_command != EHead) && (sendfh > 0) ) + if( send_data(h, str.data, str.off, MSG_MORE) == 0 ) { - send_file(h, sendfh, offset, h->req_RangeEnd); + if( h->req_command != EHead ) + send_file(h, sendfh, offset, h->req_RangeEnd); } close(sendfh); diff --git a/release/src/router/minidlna/upnphttp.h b/release/src/router/minidlna/upnphttp.h index 64108e6b0b..4808c12458 100644 --- a/release/src/router/minidlna/upnphttp.h +++ b/release/src/router/minidlna/upnphttp.h @@ -57,6 +57,7 @@ enum httpCommands { struct upnphttp { int socket; struct in_addr clientaddr; /* client address */ + int iface; int state; char HttpVer[16]; /* request */ @@ -77,11 +78,11 @@ struct upnphttp { off_t req_RangeEnd; long int req_chunklen; uint32_t reqflags; - uint32_t respflags; /* response */ char * res_buf; int res_buflen; int res_buf_alloclen; + uint32_t respflags; /*int res_contentlen;*/ /*int res_contentoff;*/ /* header length */ LIST_ENTRY(upnphttp) entries; @@ -110,7 +111,8 @@ struct upnphttp { #define FLAG_MIME_FLAC_FLAC 0x00800000 #define FLAG_NO_RESIZE 0x01000000 #define FLAG_MS_PFS 0x02000000 // Microsoft PlaysForSure client -#define FLAG_AUDIO_ONLY 0x04000000 +#define FLAG_SAMSUNG 0x04000000 +#define FLAG_AUDIO_ONLY 0x08000000 #define FLAG_FREE_OBJECT_ID 0x00000001 @@ -153,6 +155,8 @@ BuildResp2_upnphttp(struct upnphttp * h, int respcode, /* Error messages */ void +Send500(struct upnphttp *); +void Send501(struct upnphttp *); /* SendResp_upnphttp() */ diff --git a/release/src/router/minidlna/upnpsoap.c b/release/src/router/minidlna/upnpsoap.c index 6cbff1c0ac..0eaef05b89 100644 --- a/release/src/router/minidlna/upnpsoap.c +++ b/release/src/router/minidlna/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" @@ -347,14 +346,14 @@ mime_to_ext(const char * mime, char * buf) #define FILTER_UPNP_SEARCHCLASS 0x00100000 static u_int32_t -set_filter_flags(char * filter, enum client_types client) +set_filter_flags(char * filter, struct upnphttp *h) { char *item, *saveptr = NULL; u_int32_t flags = 0; if( !filter || (strlen(filter) <= 1) ) return 0xFFFFFFFF; - if( client == ESamsungTV ) + if( h->reqflags & FLAG_SAMSUNG ) flags |= FILTER_DLNA_NAMESPACE; item = strtok_r(filter, ",", &saveptr); while( item != NULL ) @@ -394,7 +393,7 @@ set_filter_flags(char * filter, enum client_types client) else if( strcmp(item, "upnp:albumArtURI") == 0 ) { flags |= FILTER_UPNP_ALBUMARTURI; - if( client == ESamsungTV ) + if( h->reqflags & FLAG_SAMSUNG ) flags |= FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID; } else if( strcmp(item, "upnp:albumArtURI@dlna:profileID") == 0 ) @@ -553,20 +552,16 @@ parse_sort_criteria(char * sortCriteria, int * error) inline static void add_resized_res(int srcw, int srch, int reqw, int reqh, char *dlna_pn, - char *detailID, struct Response *passed_args) + char *detailID, struct Response *args) { - int ret; int dstw = reqw; int dsth = reqh; - char str_buf[256]; - if( passed_args->flags & FLAG_NO_RESIZE ) + if( 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; - if( passed_args->filter & FILTER_RES_RESOLUTION ) + strcatf(args->str, "<res "); + if( args->filter & FILTER_RES_RESOLUTION ) { dstw = reqw; dsth = ((((reqw<<10)/srcw)*srch)>>10); @@ -574,66 +569,44 @@ 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(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(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[args->iface].str, runtime_vars.port, + detailID, dstw, dsth); } inline static void 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) + char *detailID, char *ext, struct Response *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; - 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(args->str, "<res "); + if( size && (args->filter & FILTER_RES_SIZE) ) { + strcatf(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; + if( duration && (args->filter & FILTER_RES_DURATION) ) { + strcatf(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; + if( bitrate && (args->filter & FILTER_RES_BITRATE) ) { + strcatf(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; + if( sampleFrequency && (args->filter & FILTER_RES_SAMPLEFREQUENCY) ) { + strcatf(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; + if( nrAudioChannels && (args->filter & FILTER_RES_NRAUDIOCHANNELS) ) { + strcatf(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; + if( resolution && (args->filter & FILTER_RES_RESOLUTION) ) { + strcatf(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(args->str, "protocolInfo=\"http-get:*:%s:%s\">" + "http://%s:%d/MediaItems/%s.%s" + "</res>", + mime, dlna_pn, lan_addr[args->iface].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 +624,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]; - int children, ret = 0; + 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 { @@ -720,7 +694,7 @@ callback(void *args, int argc, char **argv, char **azColName) } } /* From what I read, Samsung TV's expect a [wrong] MIME type of x-mkv. */ - if( passed_args->client == ESamsungTV ) + if( passed_args->flags & FLAG_SAMSUNG ) { if( strcmp(mime+6, "x-matroska") == 0 ) { @@ -739,125 +713,89 @@ 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 = 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, " refID=\"%s\"", refID); + } + 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 == 'a') && (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; + if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ACTOR) ) { + ret = strcatf(str, "<upnp:actor>%s</upnp:actor>", artist); } - else 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; + if( passed_args->filter & FILTER_UPNP_ARTIST ) { + 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[passed_args->iface].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[passed_args->iface].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; - - 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); + if( passed_args->client == EMediaRoom && !album ) + ret = strcatf(str, "<upnp:album>%s</upnp:album>", "[No Keywords]"); + + /* EVA2000 doesn't seem to handle embedded thumbnails */ + if( passed_args->client != ENetgearEVA2000 && tn && atoi(tn) ) { + ret = strcatf(str, "<upnp:albumArtURI>" + "http://%s:%d/Thumbnails/%s.jpg" + "</upnp:albumArtURI>", + lan_addr[passed_args->iface].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[passed_args->iface].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[passed_args->iface].str, + runtime_vars.port, detailID); } add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels, resolution, dlna_buf, mime, detailID, ext, passed_args); if( (*mime == 'i') && (passed_args->client != EFreeBox) ) { -#if 1 //JPEG_RESIZE int srcw = atoi(strsep(&resolution, "x")); int srch = atoi(resolution); if( !dlna_pn ) { @@ -866,14 +804,12 @@ callback(void *args, int argc, char **argv, char **azColName) if( !dlna_pn || !strncmp(dlna_pn, "JPEG_L", 6) || !strncmp(dlna_pn, "JPEG_M", 6) ) { add_resized_res(srcw, srch, 640, 480, "JPEG_SM", detailID, passed_args); } -#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[passed_args->iface].str, + runtime_vars.port, detailID); } } else if( *mime == 'v' ) { @@ -882,7 +818,8 @@ callback(void *args, int argc, char **argv, char **azColName) if( dlna_pn && (strncmp(dlna_pn, "MPEG_TS_HD_NA", 13) == 0 || strncmp(dlna_pn, "MPEG_TS_SD_NA", 13) == 0 || - strncmp(dlna_pn, "AVC_TS_MP_HD_AC3", 16) == 0)) + strncmp(dlna_pn, "AVC_TS_MP_HD_AC3", 16) == 0 || + strncmp(dlna_pn, "AVC_TS_HP_HD_AC3", 16) == 0)) { sprintf(dlna_buf, "DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=01;DLNA.ORG_CI=0"); add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels, @@ -942,78 +879,66 @@ callback(void *args, int argc, char **argv, char **azColName) resolution, dlna_buf, mime, detailID, ext, passed_args); } break; + case ELGDevice: + if( sql_get_int_field(db, "SELECT ID from CAPTIONS where ID = '%s'", detailID) > 0 ) + { + ret = strcatf(str, "<res protocolInfo=\"http-get:*:text/srt:*\">" + "http://%s:%d/Captions/%s.srt" + "</res>", + lan_addr[passed_args->iface].str, runtime_vars.port, detailID); + } + break; default: break; } } } - 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[passed_args->iface].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; } @@ -1027,19 +952,16 @@ 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"); + char * ObjectID = GetValueFromNameValueList(&data, "ObjectID"); char * Filter = GetValueFromNameValueList(&data, "Filter"); char * BrowseFlag = GetValueFromNameValueList(&data, "BrowseFlag"); char * SortCriteria = GetValueFromNameValueList(&data, "SortCriteria"); @@ -1055,49 +977,44 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) if( !BrowseFlag || (strcmp(BrowseFlag, "BrowseDirectChildren") && strcmp(BrowseFlag, "BrowseMetadata")) ) { SoapError(h, 402, "Invalid Args"); - if( h->reqflags & FLAG_MS_PFS ) - ObjectId = sqlite3_malloc(1); goto browse_error; } - if( !ObjectId && !(ObjectId = GetValueFromNameValueList(&data, "ContainerID")) ) + if( !ObjectID && !(ObjectID = GetValueFromNameValueList(&data, "ContainerID")) ) { SoapError(h, 701, "No such object error"); - if( h->reqflags & FLAG_MS_PFS ) - ObjectId = sqlite3_malloc(1); 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); /* See if we need to include DLNA namespace reference */ - args.filter = set_filter_flags(Filter, h->req_client); + args.iface = h->iface; + args.filter = set_filter_flags(Filter, h); 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; args.client = h->req_client; args.flags = h->reqflags; + args.str = &str; if( args.flags & FLAG_MS_PFS ) { - if( !strchr(ObjectId, '$') && (strcmp(ObjectId, "0") != 0) ) + if( !strchr(ObjectID, '$') && (strcmp(ObjectID, "0") != 0) ) { ptr = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS" " where OBJECT_ID in " "('"MUSIC_ID"$%s', '"VIDEO_ID"$%s', '"IMAGE_ID"$%s')", - ObjectId, ObjectId, ObjectId); + ObjectID, ObjectID, ObjectID); if( ptr ) { - ObjectId = ptr; + ObjectID = ptr; args.flags |= FLAG_FREE_OBJECT_ID; } } @@ -1109,12 +1026,12 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) " * BrowseFlag: %s\n" " * Filter: %s\n" " * SortCriteria: %s\n", - ObjectId, RequestedCount, StartingIndex, + ObjectID, RequestedCount, StartingIndex, BrowseFlag, Filter, SortCriteria); - if( (args.flags & FLAG_AUDIO_ONLY) && (strcmp(ObjectId, "0") == 0) ) + if( (args.flags & FLAG_AUDIO_ONLY) && (strcmp(ObjectID, "0") == 0) ) { - ObjectId = sqlite3_mprintf("%s", MUSIC_ID); + ObjectID = sqlite3_mprintf("%s", MUSIC_ID); args.flags |= FLAG_FREE_OBJECT_ID; } @@ -1124,13 +1041,13 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) sql = sqlite3_mprintf( SELECT_COLUMNS "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" " where OBJECT_ID = '%s';" - , ObjectId); + , ObjectID); ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg); totalMatches = args.returned; } else { - ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", ObjectId); + ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where PARENT_ID = '%s'", ObjectID); totalMatches = (ret > 0) ? ret : 0; ret = 0; if( SortCriteria ) @@ -1142,9 +1059,9 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) } else { - if( strncmp(ObjectId, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) + if( strncmp(ObjectID, MUSIC_PLIST_ID, strlen(MUSIC_PLIST_ID)) == 0 ) { - if( strcmp(ObjectId, MUSIC_PLIST_ID) == 0 ) + if( strcmp(ObjectID, MUSIC_PLIST_ID) == 0 ) asprintf(&orderBy, "order by d.TITLE"); else asprintf(&orderBy, "order by length(OBJECT_ID), OBJECT_ID"); @@ -1167,7 +1084,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) sql = sqlite3_mprintf( SELECT_COLUMNS "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" " where PARENT_ID = '%s' %s limit %d, %d;", - ObjectId, orderBy, StartingIndex, RequestedCount); + ObjectID, orderBy, StartingIndex, RequestedCount); DPRINTF(E_DEBUG, L_HTTP, "Browse SQL: %s\n", sql); ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg); } @@ -1180,29 +1097,26 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) /* Does the object even exist? */ if( !totalMatches ) { - ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", ObjectId); + ret = sql_get_int_field(db, "SELECT count(*) from OBJECTS where OBJECT_ID = '%s'", ObjectID); if( ret <= 0 ) { SoapError(h, 701, "No such object error"); 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); + sqlite3_free(ObjectID); + free(orderBy); + free(str.data); } static void @@ -1214,16 +1128,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); @@ -1247,48 +1158,42 @@ SearchContentDirectory(struct upnphttp * h, const char * action) if( !(ContainerID = GetValueFromNameValueList(&data, "ObjectID")) ) { SoapError(h, 701, "No such object error"); - if( h->reqflags & FLAG_MS_PFS ) - ContainerID = sqlite3_malloc(1); goto search_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); /* See if we need to include DLNA namespace reference */ - args.filter = set_filter_flags(Filter, h->req_client); + args.iface = h->iface; + args.filter = set_filter_flags(Filter, h); 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; args.client = h->req_client; args.flags = h->reqflags; - if( h->reqflags & FLAG_MS_PFS ) + args.str = &str; + if( args.flags & FLAG_MS_PFS ) { - if( strchr(ContainerID, '$') || (strcmp(ContainerID, "0") == 0) ) - { - ContainerID = sqlite3_mprintf("%s", ContainerID); - } - else + if( !strchr(ContainerID, '$') && (strcmp(ContainerID, "0") != 0) ) { ptr = sql_get_text_field(db, "SELECT OBJECT_ID from OBJECTS" " where OBJECT_ID in " "('"MUSIC_ID"$%s', '"VIDEO_ID"$%s', '"IMAGE_ID"$%s')", ContainerID, ContainerID, ContainerID); if( ptr ) + { ContainerID = ptr; - else - ContainerID = sqlite3_mprintf("%s", ContainerID); + args.flags |= FLAG_FREE_OBJECT_ID; + } } #if 0 // Looks like the 360 already does this /* Sort by track number for some containers */ @@ -1321,13 +1226,14 @@ SearchContentDirectory(struct upnphttp * h, const char * action) groupBy[0] = '\0'; if( !SearchCriteria ) { - asprintf(&newSearchCriteria, "1 = 1"); + newSearchCriteria = strdup("1 = 1"); SearchCriteria = newSearchCriteria; } else { SearchCriteria = modifyString(SearchCriteria, """, "\"", 0); SearchCriteria = modifyString(SearchCriteria, "'", "'", 0); + SearchCriteria = modifyString(SearchCriteria, "\\\"", "\"\"", 0); SearchCriteria = modifyString(SearchCriteria, "object.", "", 0); SearchCriteria = modifyString(SearchCriteria, "derivedfrom", "like", 1); SearchCriteria = modifyString(SearchCriteria, "contains", "like", 2); @@ -1343,16 +1249,14 @@ SearchContentDirectory(struct upnphttp * h, const char * action) SearchCriteria = modifyString(SearchCriteria, "@refID", "REF_ID", 0); if( strstr(SearchCriteria, "@id") ) { - newSearchCriteria = modifyString(strdup(SearchCriteria), "@id", "OBJECT_ID", 0); - SearchCriteria = newSearchCriteria; + newSearchCriteria = strdup(SearchCriteria); + SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "@id", "OBJECT_ID", 0); } if( strstr(SearchCriteria, "res is ") ) { - if( newSearchCriteria ) - newSearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0); - else - newSearchCriteria = modifyString(strdup(SearchCriteria), "res is ", "MIME is ", 0); - SearchCriteria = newSearchCriteria; + if( !newSearchCriteria ) + newSearchCriteria = strdup(SearchCriteria); + SearchCriteria = newSearchCriteria = modifyString(newSearchCriteria, "res is ", "MIME is ", 0); } #if 0 // Does 360 need this? if( strstr(SearchCriteria, "&") ) @@ -1367,14 +1271,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 +1334,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 ) - { + if( args.flags & FLAG_FREE_OBJECT_ID ) sqlite3_free(ContainerID); - } + free(orderBy); + free(newSearchCriteria); + free(str.data); } /* @@ -1546,14 +1445,14 @@ void ExecuteSoapAction(struct upnphttp * h, const char * action, int n) { char * p; - char * p2; - int i, len, methodlen; - i = 0; p = strchr(action, '#'); - if(p) { + int i = 0; + int len; + int methodlen; + char * p2; p++; p2 = strchr(p, '"'); if(p2) diff --git a/release/src/router/minidlna/upnpsoap.h b/release/src/router/minidlna/upnpsoap.h index d2c006db88..d235466b61 100644 --- a/release/src/router/minidlna/upnpsoap.h +++ b/release/src/router/minidlna/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,13 @@ 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; + int iface; + uint32_t filter; + uint32_t flags; enum client_types client; }; diff --git a/release/src/router/minidlna/utils.c b/release/src/router/minidlna/utils.c index e98ba35601..92bf86daec 100644 --- a/release/src/router/minidlna/utils.c +++ b/release/src/router/minidlna/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) { @@ -100,7 +114,12 @@ modifyString(char * string, const char * before, const char * after, short like) chgcnt++; s = p+oldlen; } - string = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like); + s = realloc(string, strlen(string)+((newlen-oldlen)*chgcnt)+1+like); + /* If we failed to realloc, return the original alloc'd string */ + if( s ) + string = s; + else + return string; } s = string; @@ -136,7 +155,7 @@ modifyString(char * string, const char * before, const char * after, short like) } char * -escape_tag(const char *tag) +escape_tag(const char *tag, int force_alloc) { char *esc_tag = NULL; @@ -147,6 +166,8 @@ escape_tag(const char *tag) esc_tag = modifyString(esc_tag, "<", "&lt;", 0); esc_tag = modifyString(esc_tag, ">", "&gt;", 0); } + else if( force_alloc ) + esc_tag = strdup(tag); return esc_tag; } diff --git a/release/src/router/minidlna/utils.h b/release/src/router/minidlna/utils.h index 07a7a72f79..e49aa4973d 100644 --- a/release/src/router/minidlna/utils.h +++ b/release/src/router/minidlna/utils.h @@ -25,6 +25,9 @@ #define __UTILS_H__ int +strcatf(struct string_s *str, char *fmt, ...); + +int ends_with(const char * haystack, const char * needle); char * @@ -37,7 +40,7 @@ char * modifyString(char * string, const char * before, const char * after, short like); char * -escape_tag(const char *tag); +escape_tag(const char *tag, int force_alloc); void strip_ext(char * name); -- 2.11.4.GIT