From 4e1da37820b99431e286ad156e2187179bcd5e5d Mon Sep 17 00:00:00 2001 From: hoefer Date: Mon, 27 Jun 2016 15:01:30 +0200 Subject: [PATCH] merged from original minidlna 1.1.6 incl. fix for sql statement line 1926 and 1929 --- NEWS | 27 ++++++++++ clients.c | 30 ++++++++++- clients.h | 2 + configure.ac | 1 - getifaddr.c | 2 +- image_utils.c | 10 +++- inotify.c | 16 ++++-- metadata.c | 10 ++-- minidlna.c | 65 ++++++++++++----------- minidlna.conf | 4 ++ minidlna.conf.5 | 3 ++ minissdp.c | 63 ++++++++++++++++++++--- options.c | 1 + options.h | 2 + po/LINGUAS | 2 +- po/da.po | 4 ++ po/de.po | 4 ++ po/es.po | 4 ++ po/fr.po | 4 ++ po/it.po | 4 ++ po/ja.po | 4 ++ po/ko.po | 111 ++++++++++++++++++++++++++++++++++++++++ po/minidlna.pot | 4 ++ po/nb.po | 4 ++ po/nl.po | 4 ++ po/pl.po | 4 ++ po/ru.po | 4 ++ po/sl.po | 4 ++ po/sv.po | 4 ++ process.c | 2 +- scanner.c | 7 ++- tagutils/tagutils-flc.c | 11 +++- testupnpdescgen.c | 3 -- upnpdescgen.c | 42 ++++++--------- upnpglobalvars.c | 3 -- upnpglobalvars.h | 11 +--- upnphttp.c | 105 ++++++++++++++++++++++++++++++------- upnpsoap.c | 22 ++++++-- utils.c | 6 ++- 39 files changed, 486 insertions(+), 127 deletions(-) create mode 100755 po/ko.po diff --git a/NEWS b/NEWS index 5ef6163..e9744b2 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,30 @@ +1.1.6 - Released 16-June-2016 +-------------------------------- +- Add AllShare and Windows client detection. +- Update support for LG2012 TV. +- Fix Samsung bookmarking on magic containers. +- Fix SQL error when moving captions. +- Add wide_links config option. +- Fix external subtitles on Samsung Series J. +- Add string localization support for magic containers. +- Rework NLS init to work with non-en_US locales. + +1.1.5 - Released 10-Sep-2015 +-------------------------------- +- Re-enable Samsung DCM10, which adds video bookmarks and "BasicView" support. +- Allow SSDP M-SEARCH from other subnets. +- Fix some nfo file character escaping. +- Fix crash with >3 network interfaces. +- Support rotation of monochrome JPEGs. +- Handle cover art streams that come after the video stream. +- Recognize new hard links with inotify. +- Work around LG TV ordering bug. +- Implement TiVo image PixelShape support. +- Support thumbnail rotation. +- Use "Album Artist" tag from AAC files. +- Add Korean translations. +- Fix handling of bad FLAC files. + 1.1.4 - Released 26-Aug-2014 -------------------------------- - Add magic container infrastructure. diff --git a/clients.c b/clients.c index 9a3f8ae..929e067 100644 --- a/clients.c +++ b/clients.c @@ -53,6 +53,16 @@ struct client_type_s client_types[] = EXAVClientInfo }, + /* User-Agent: DLNADOC/1.50 SEC_HHP_[PC]LPC001/1.0 MS-DeviceCaps/1024 */ + /* This is AllShare running on a PC. We don't want to respond with Samsung + * capabilities, or Windows (and AllShare) might get grumpy. */ + { 0, + FLAG_DLNA, + "AllShare", + "SEC_HHP_[PC]", + EUserAgent + }, + /* Samsung Series [CDE] BDPs and TVs must be separated, or some of our * advertised extra features trigger a folder browsing bug on BDPs. */ /* User-Agent: DLNADOC/1.50 SEC_HHP_BD-D5100/1.0 */ @@ -65,9 +75,10 @@ struct client_type_s client_types[] = /* User-Agent: DLNADOC/1.50 SEC_HHP_[TV]UE40D7000/1.0 */ /* User-Agent: DLNADOC/1.50 SEC_HHP_ Family TV/1.0 */ + /* USER-AGENT: DLNADOC/1.50 SEC_HHP_[TV] UE65JU7000/1.0 UPnP/1.0 */ { ESamsungSeriesCDE, - FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_SAMSUNG_DCM10, - "Samsung Series [CDEF]", + FLAG_SAMSUNG | FLAG_DLNA | FLAG_NO_RESIZE | FLAG_SAMSUNG_DCM10 | FLAG_CAPTION_RES, + "Samsung Series [CDEFJ]", "SEC_HHP_", EUserAgent }, @@ -134,6 +145,14 @@ struct client_type_s client_types[] = EXAVClientInfo }, + /* USER-AGENT: Linux/2.6.35 UPnP/1.0 DLNADOC/1.50 INTEL_NMPR/2.0 LGE_DLNA_SDK/1.6.0 */ + { ELGNetCastDevice, + FLAG_DLNA | FLAG_CAPTION_RES, + "LG", + "LGE_DLNA_SDK/1.6.0", + EUserAgent + }, + /* User-Agent: Linux/2.6.31-1.0 UPnP/1.0 DLNADOC/1.50 INTEL_NMPR/2.0 LGE_DLNA_SDK/1.5.0 */ { ELGDevice, FLAG_DLNA | FLAG_CAPTION_RES, @@ -221,6 +240,13 @@ struct client_type_s client_types[] = EUserAgent }, + { 0, + FLAG_DLNA | FLAG_MIME_AVI_AVI, + "Windows", + "FDSSDP", + EUserAgent + }, + { EStandardDLNA150, FLAG_DLNA | FLAG_MIME_AVI_AVI, "Generic DLNA 1.5", diff --git a/clients.h b/clients.h index fc89110..43c0d9d 100644 --- a/clients.h +++ b/clients.h @@ -39,6 +39,7 @@ #define FLAG_CAPTION_RES 0x00001000 /* Response-related flags */ #define FLAG_HAS_CAPTIONS 0x80000000 +#define RESPONSE_FLAGS 0xF0000000 enum match_types { EMatchNone, @@ -56,6 +57,7 @@ enum client_types { EDirecTV, EFreeBox, ELGDevice, + ELGNetCastDevice, ELifeTab, EMarantzDMP, EMediaRoom, diff --git a/configure.ac b/configure.ac index 3a8a54c..79a7994 100644 --- a/configure.ac +++ b/configure.ac @@ -575,7 +575,6 @@ AC_ARG_ENABLE(readynas, AC_DEFINE([NETGEAR],[1],[Define to 1 if you want to enable generic NETGEAR device support]) AC_DEFINE([READYNAS],[1],[Define to 1 if you want to enable NETGEAR ReadyNAS support]) AC_DEFINE([TIVO_SUPPORT], 1, [Define to 1 if you want to enable TiVo support]) - AC_DEFINE([PNPX],[5],[Define to 5 if you want to enable NETGEAR ReadyNAS PnP-X support]) AC_DEFINE_UNQUOTED([OS_URL],"http://www.readynas.com/") AC_DEFINE_UNQUOTED([ROOTDEV_MANUFACTURERURL],"http://www.netgear.com/") AC_DEFINE_UNQUOTED([ROOTDEV_MANUFACTURER],"NETGEAR") diff --git a/getifaddr.c b/getifaddr.c index 329a96b..f0d3af3 100644 --- a/getifaddr.c +++ b/getifaddr.c @@ -322,7 +322,7 @@ reload_ifaces(int force_notify) do { getifaddr(runtime_vars.ifaces[i]); i++; - } while (runtime_vars.ifaces[i]); + } while (i < MAX_LAN_ADDR && runtime_vars.ifaces[i]); for (i = 0; i < n_lan_addr; i++) { diff --git a/image_utils.c b/image_utils.c index 76c6015..d90875a 100644 --- a/image_utils.c +++ b/image_utils.c @@ -526,6 +526,7 @@ image_new_from_jpeg(const char *path, int is_file, const uint8_t *buf, int size, } else if(cinfo.output_components == 1) { + int rx, ry; ofs = 0; for(i = 0; i < cinfo.rec_outbuf_height; i++) { @@ -543,12 +544,19 @@ image_new_from_jpeg(const char *path, int is_file, const uint8_t *buf, int size, } for(y = 0; y < h; y += cinfo.rec_outbuf_height) { + ry = (rotate & (ROTATE_90|ROTATE_180)) ? (y - h + 1) * -1 : y; jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height); for(i = 0; i < cinfo.rec_outbuf_height; i++) { for(x = 0; x < w; x++) { - vimage->buf[ofs++] = COL(line[i][x], line[i][x], line[i][x]); + rx = (rotate & (ROTATE_180|ROTATE_270)) ? + (x - w + 1) * -1 : x; + ofs = (rotate & (ROTATE_90|ROTATE_270)) ? + ry + (rx * h) : rx + (ry * w); + if( ofs < maxbuf ) + vimage->buf[ofs] = + COL(line[i][x], line[i][x], line[i][x]); } } } diff --git a/inotify.c b/inotify.c index 6a00930..d2daa5b 100644 --- a/inotify.c +++ b/inotify.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -329,6 +330,7 @@ inotify_insert_file(char * name, const char * path) if( !is_audio(path) && !is_video(path) && !is_playlist(path) ) + return -1; break; case TYPE_AUDIO|TYPE_IMAGES: if( !is_image(path) && @@ -349,9 +351,8 @@ inotify_insert_file(char * name, const char * path) if( !is_image(path) ) return -1; break; - default: + default: return -1; - break; } /* If it's already in the database and hasn't been modified, skip it. */ @@ -642,7 +643,7 @@ inotify_remove_directory(int fd, const char * path) } void * -start_inotify() +start_inotify(void) { struct pollfd pollfds[1]; int timeout = 1000; @@ -651,6 +652,10 @@ start_inotify() int length, i = 0; char * esc_name = NULL; struct stat st; + sigset_t set; + + sigfillset(&set); + pthread_sigmask(SIG_BLOCK, &set, NULL); pollfds[0].fd = inotify_init(); pollfds[0].events = POLLIN; @@ -717,9 +722,10 @@ start_inotify() else if ( (event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO|IN_CREATE)) && (lstat(path_buf, &st) == 0) ) { - if( S_ISLNK(st.st_mode) ) + if( (event->mask & (IN_MOVED_TO|IN_CREATE)) && (S_ISLNK(st.st_mode) || st.st_nlink > 1) ) { - DPRINTF(E_DEBUG, L_INOTIFY, "The symbolic link %s was %s.\n", + DPRINTF(E_DEBUG, L_INOTIFY, "The %s link %s was %s.\n", + (S_ISLNK(st.st_mode) ? "symbolic" : "hard"), path_buf, (event->mask & IN_MOVED_TO ? "moved here" : "created")); if( stat(path_buf, &st) == 0 && S_ISDIR(st.st_mode) ) inotify_insert_directory(pollfds[0].fd, esc_name, path_buf); diff --git a/metadata.c b/metadata.c index ce1d212..2bfa8b9 100644 --- a/metadata.c +++ b/metadata.c @@ -149,7 +149,7 @@ check_for_captions(const char *path, int64_t detailID) if (ret == 0) { - sql_exec(db, "INSERT into CAPTIONS" + sql_exec(db, "INSERT OR REPLACE into CAPTIONS" " (ID, PATH) " "VALUES" " (%lld, %Q)", detailID, file); @@ -694,16 +694,16 @@ GetVideoMetadata(const char *path, char *name) //dump_format(ctx, 0, NULL, 0); for( i=0; inb_streams; i++) { - if( audio_stream == -1 && - ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO ) + if( ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && + audio_stream == -1 ) { audio_stream = i; ac = ctx->streams[audio_stream]->codec; continue; } - else if( video_stream == -1 && + else if( ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && !lav_is_thumbnail_stream(ctx->streams[i], &m.thumb_data, &m.thumb_size) && - ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO ) + video_stream == -1 ) { video_stream = i; vc = ctx->streams[video_stream]->codec; diff --git a/minidlna.c b/minidlna.c index 67d95a3..0e47fe5 100644 --- a/minidlna.c +++ b/minidlna.c @@ -238,30 +238,6 @@ getfriendlyname(char *buf, int len) } } fclose(info); -#if PNPX - memcpy(pnpx_hwid+4, "01F2", 4); - if (strcmp(modelnumber, "NVX") == 0) - memcpy(pnpx_hwid+17, "0101", 4); - else if (strcmp(modelnumber, "Pro") == 0 || - strcmp(modelnumber, "Pro 6") == 0 || - strncmp(modelnumber, "Ultra 6", 7) == 0) - memcpy(pnpx_hwid+17, "0102", 4); - else if (strcmp(modelnumber, "Pro 2") == 0 || - strncmp(modelnumber, "Ultra 2", 7) == 0) - memcpy(pnpx_hwid+17, "0103", 4); - else if (strcmp(modelnumber, "Pro 4") == 0 || - strncmp(modelnumber, "Ultra 4", 7) == 0) - memcpy(pnpx_hwid+17, "0104", 4); - else if (strcmp(modelnumber+1, "100") == 0) - memcpy(pnpx_hwid+17, "0105", 4); - else if (strcmp(modelnumber+1, "200") == 0) - memcpy(pnpx_hwid+17, "0106", 4); - /* 0107 = Stora */ - else if (strcmp(modelnumber, "Duo v2") == 0) - memcpy(pnpx_hwid+17, "0108", 4); - else if (strcmp(modelnumber, "NV+ v2") == 0) - memcpy(pnpx_hwid+17, "0109", 4); -#endif #else char * logname; logname = getenv("LOGNAME"); @@ -380,6 +356,7 @@ check_db(sqlite3 *db, int new_db, pid_t *scanner_pid) sqlite3_close(db); log_close(); freeoptions(); + free(children); exit(EXIT_SUCCESS); } else if (*scanner_pid < 0) @@ -467,9 +444,21 @@ static int strtobool(const char *str) static void init_nls(void) { #ifdef ENABLE_NLS - setlocale(LC_MESSAGES, ""); - setlocale(LC_CTYPE, "en_US.utf8"); - DPRINTF(E_DEBUG, L_GENERAL, "Using locale dir %s\n", bindtextdomain("minidlna", getenv("TEXTDOMAINDIR"))); + const char *messages, *ctype, *locale_dir; + + ctype = setlocale(LC_CTYPE, ""); + if (!ctype || !strcmp(ctype, "C")) + ctype = setlocale(LC_CTYPE, "en_US.utf8"); + if (!ctype) + DPRINTF(E_WARN, L_GENERAL, "Unset locale\n"); + else if (!strstr(ctype, "utf8") && !strstr(ctype, "UTF8") && + !strstr(ctype, "utf-8") && !strstr(ctype, "UTF-8")) + DPRINTF(E_WARN, L_GENERAL, "Using unsupported non-utf8 locale '%s'\n", ctype); + messages = setlocale(LC_MESSAGES, ""); + if (!messages) + messages = "unset"; + locale_dir = bindtextdomain("minidlna", getenv("TEXTDOMAINDIR")); + DPRINTF(E_DEBUG, L_GENERAL, "Using locale dir '%s' and locale langauge %s/%s\n", locale_dir, messages, ctype); textdomain("minidlna"); #endif } @@ -555,6 +544,8 @@ init(int argc, char **argv) MAX_LAN_ADDR, word); break; } + while (isspace(*word)) + word++; runtime_vars.ifaces[ifaces++] = word; } break; @@ -726,7 +717,8 @@ init(int argc, char **argv) /* Symbolic username given, not UID. */ struct passwd *entry = getpwnam(ary_options[i].value); if (!entry) - DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", argv[i]); + DPRINTF(E_FATAL, L_GENERAL, "Bad user '%s'.\n", + ary_options[i].value); uid = entry->pw_uid; } break; @@ -740,6 +732,10 @@ init(int argc, char **argv) if (strtobool(ary_options[i].value)) SETFLAG(MERGE_MEDIA_DIRS_MASK); break; + case WIDE_LINKS: + if (strtobool(ary_options[i].value)) + SETFLAG(WIDE_LINKS_MASK); + break; default: DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n", optionsfile); @@ -1023,11 +1019,11 @@ main(int argc, char **argv) for (i = 0; i < L_MAX; i++) log_level[i] = E_WARN; - init_nls(); ret = init(argc, argv); if (ret != 0) return 1; + init_nls(); DPRINTF(E_WARN, L_GENERAL, "Starting " SERVER_NAME " version " MINIDLNA_VERSION ".\n"); if (sqlite3_libversion_number() < 3005001) @@ -1061,6 +1057,7 @@ main(int argc, char **argv) if (sssdp < 0) { DPRINTF(E_INFO, L_GENERAL, "Failed to open socket for receiving SSDP. Trying to use MiniSSDPd\n"); + reload_ifaces(0); /* populate lan_addr[0].str */ if (SubmitServicesToMiniSSDPD(lan_addr[0].str, runtime_vars.port) < 0) DPRINTF(E_FATAL, L_GENERAL, "Failed to connect to MiniSSDPd. EXITING"); } @@ -1300,10 +1297,6 @@ main(int argc, char **argv) if (scanning && scanner_pid) kill(scanner_pid, SIGKILL); - /* kill other child processes */ - process_reap_children(); - free(children); - /* close out open sockets */ while (upnphttphead.lh_first != NULL) { @@ -1319,6 +1312,8 @@ main(int argc, char **argv) if (sbeacon >= 0) close(sbeacon); #endif + if (smonitor >= 0) + close(smonitor); for (i = 0; i < n_lan_addr; i++) { @@ -1329,6 +1324,10 @@ main(int argc, char **argv) if (inotify_thread) pthread_join(inotify_thread, NULL); + /* kill other child processes */ + process_reap_children(); + free(children); + sql_exec(db, "UPDATE SETTINGS set VALUE = '%u' where KEY = 'UPDATE_ID'", updateID); sqlite3_close(db); diff --git a/minidlna.conf b/minidlna.conf index 1eeac91..5d728db 100644 --- a/minidlna.conf +++ b/minidlna.conf @@ -85,3 +85,7 @@ model_number=1 # maximum number of simultaneous connections # note: many clients open several simultaneous connections while streaming #max_connections=50 + +# set this to yes to allow symlinks that point outside user-defined media_dirs. +#wide_links=no + diff --git a/minidlna.conf.5 b/minidlna.conf.5 index 69f937c..441fb8d 100644 --- a/minidlna.conf.5 +++ b/minidlna.conf.5 @@ -172,6 +172,9 @@ force_sort_criteria=+upnp:class,+upnp:originalTrackNumber,+dc:title .fi +.IP "\fBwide_links\fP" +Set to 'yes' to allow symlinks that point outside user-defined media_dirs. +By default, wide symlinks are not followed. .SH VERSION diff --git a/minissdp.c b/minissdp.c index 60d69ec..7789076 100644 --- a/minissdp.c +++ b/minissdp.c @@ -73,6 +73,10 @@ AddMulticastMembership(int s, struct lan_addr_s *iface) imr.imr_multiaddr.s_addr = inet_addr(SSDP_MCAST_ADDR); imr.imr_interface.s_addr = iface->addr.s_addr; #endif + /* Setting the socket options will guarantee, tha we will only receive + * multicast traffic on a specific Interface. + * In addition the kernel is instructed to send an igmp message (choose + * mcast group) on the specific interface/subnet. */ ret = setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&imr, sizeof(imr)); if (ret < 0 && errno != EADDRINUSE) { @@ -102,12 +106,18 @@ OpenAndConfSSDPReceiveSocket(void) if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0) DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, SO_REUSEADDR): %s\n", strerror(errno)); - +#ifdef __linux__ + if (setsockopt(s, IPPROTO_IP, IP_PKTINFO, &i, sizeof(i)) < 0) + DPRINTF(E_WARN, L_SSDP, "setsockopt(udp, IP_PKTINFO): %s\n", strerror(errno)); +#endif memset(&sockname, 0, sizeof(struct sockaddr_in)); sockname.sin_family = AF_INET; sockname.sin_port = htons(SSDP_PORT); - /* NOTE : it seems it doesnt work when binding on the specific address */ - sockname.sin_addr.s_addr = htonl(INADDR_ANY); + /* NOTE: Binding a socket to a UDP multicast address means, that we just want + * to receive datagramms send to this multicast address. + * To specify the local nics we want to use we have to use setsockopt, + * see AddMulticastMembership(...). */ + sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); if (bind(s, (struct sockaddr *)&sockname, sizeof(struct sockaddr_in)) < 0) { @@ -476,15 +486,32 @@ ProcessSSDPRequest(int s, unsigned short port) { int n; char bufr[1500]; - socklen_t len_r; struct sockaddr_in sendername; int i; char *st = NULL, *mx = NULL, *man = NULL, *mx_end = NULL; int man_len = 0; - len_r = sizeof(struct sockaddr_in); +#ifdef __linux__ + char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))]; + struct iovec iovec = { + .iov_base = bufr, + .iov_len = sizeof(bufr)-1 + }; + struct msghdr mh = { + .msg_name = &sendername, + .msg_namelen = sizeof(struct sockaddr_in), + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_control = cmbuf, + .msg_controllen = sizeof(cmbuf) + }; + + n = recvmsg(s, &mh, 0); +#else + socklen_t len_r = sizeof(struct sockaddr_in); n = recvfrom(s, bufr, sizeof(bufr)-1, 0, (struct sockaddr *)&sendername, &len_r); +#endif if (n < 0) { DPRINTF(E_ERROR, L_SSDP, "recvfrom(udp): %s\n", strerror(errno)); @@ -624,6 +651,26 @@ ProcessSSDPRequest(int s, unsigned short port) else if (st && (st_len > 0)) { int l; +#ifdef __linux__ + char host[40] = "127.0.0.1"; + struct cmsghdr *cmsg; + + /* find the interface we received the msg from */ + for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) + { + struct in_addr addr; + struct in_pktinfo *pi; + /* ignore the control headers that don't match what we want */ + if (cmsg->cmsg_level != IPPROTO_IP || + cmsg->cmsg_type != IP_PKTINFO) + continue; + + pi = (struct in_pktinfo *)CMSG_DATA(cmsg); + addr = pi->ipi_spec_dst; + inet_ntop(AF_INET, &addr, host, sizeof(host)); + } +#else + const char *host; int iface = 0; /* find in which sub network the client is */ for (i = 0; i < n_lan_addr; i++) @@ -641,6 +688,8 @@ ProcessSSDPRequest(int s, unsigned short port) inet_ntoa(sendername.sin_addr)); return; } + host = lan_addr[iface].str; +#endif DPRINTF(E_DEBUG, L_SSDP, "SSDP M-SEARCH from %s:%d ST: %.*s, MX: %.*s, MAN: %.*s\n", inet_ntoa(sendername.sin_addr), ntohs(sendername.sin_port), @@ -675,7 +724,7 @@ ProcessSSDPRequest(int s, unsigned short port) } _usleep(random()>>20); SendSSDPResponse(s, sendername, i, - lan_addr[iface].str, port); + host, port); return; } /* Responds to request with ST: ssdp:all */ @@ -686,7 +735,7 @@ ProcessSSDPRequest(int s, unsigned short port) { l = strlen(known_service_types[i]); SendSSDPResponse(s, sendername, i, - lan_addr[iface].str, port); + host, port); } } } diff --git a/options.c b/options.c index 8189f56..113deda 100644 --- a/options.c +++ b/options.c @@ -65,6 +65,7 @@ static const struct { { FORCE_SORT_CRITERIA, "force_sort_criteria" }, { MAX_CONNECTIONS, "max_connections" }, { MERGE_MEDIA_DIRS, "merge_media_dirs" }, + { WIDE_LINKS, "wide_links" }, { PASSWORD_LENGTH, "password_length" } }; diff --git a/options.h b/options.h index 81e4f42..de899fd 100644 --- a/options.h +++ b/options.h @@ -57,7 +57,9 @@ enum upnpconfigoptions { USER_ACCOUNT, /* user account to run as */ FORCE_SORT_CRITERIA, /* force sorting by a given sort criteria */ MAX_CONNECTIONS, /* maximum number of simultaneous connections */ +- MERGE_MEDIA_DIRS /* don't add an extra directory level when there are multiple media dirs */ MERGE_MEDIA_DIRS, /* don't add an extra directory level when there are multiple media dirs */ + WIDE_LINKS, /* allow following symlinks outside the defined media_dirs */ PASSWORD_LENGTH /* Password */ }; diff --git a/po/LINGUAS b/po/LINGUAS index db2513e..98c79da 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -da de es fr it ja nb nl pl ru sl sv +da de es fr it ja ko nb nl pl ru sl sv diff --git a/po/da.po b/po/da.po index f071e07..7d06bd2 100644 --- a/po/da.po +++ b/po/da.po @@ -68,6 +68,10 @@ msgstr "Mapper" msgid "Playlists" msgstr "Afspilningslister" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Nylig tilføjet" + #: scanner.c:536 msgid "Video" msgstr "Film" diff --git a/po/de.po b/po/de.po index 79fe3f3..3eeb814 100644 --- a/po/de.po +++ b/po/de.po @@ -83,6 +83,10 @@ msgstr "Ordner" msgid "Playlists" msgstr "Wiedergabelisten" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Kürzlich hinzugefügt" + #: scanner.c:536 msgid "Video" msgstr "Video" diff --git a/po/es.po b/po/es.po index 0e0b150..5ac881f 100644 --- a/po/es.po +++ b/po/es.po @@ -68,6 +68,10 @@ msgstr "Carpetas" msgid "Playlists" msgstr "Listas" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Recientemente añadido" + #: scanner.c:536 msgid "Video" msgstr "Vídeo" diff --git a/po/fr.po b/po/fr.po index 62cfb1f..9769902 100644 --- a/po/fr.po +++ b/po/fr.po @@ -68,6 +68,10 @@ msgstr "Dossiers" msgid "Playlists" msgstr "Liste de lecture" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Ajouts récents" + #: scanner.c:536 msgid "Video" msgstr "Vidéo" diff --git a/po/it.po b/po/it.po index ee7e043..c8541bf 100644 --- a/po/it.po +++ b/po/it.po @@ -70,6 +70,10 @@ msgstr "Cartelle" msgid "Playlists" msgstr "Scalette" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Aggiunto recentemente" + #: scanner.c:536 msgid "Video" msgstr "Video" diff --git a/po/ja.po b/po/ja.po index caca0d5..c6605b1 100644 --- a/po/ja.po +++ b/po/ja.po @@ -68,6 +68,10 @@ msgstr "フォルダ" msgid "Playlists" msgstr "プレイリスト" +#: scanner.c:598 +msgid "Recently Added" +msgstr "最近追加された" + #: scanner.c:536 msgid "Video" msgstr "ビデオ" diff --git a/po/ko.po b/po/ko.po new file mode 100755 index 0000000..ff8198d --- /dev/null +++ b/po/ko.po @@ -0,0 +1,111 @@ +# MiniDLNA translation template file +# Copyright (C) 2010 NETGEAR +# This file is distributed under the same license as the MiniDLNA package. +# Justin Maggard , 2010. +# +msgid "" +msgstr "" +"Project-Id-Version: minidlna 1.1.0\n" +"Report-Msgid-Bugs-To: jmaggard@users.sourceforge.net\n" +"POT-Creation-Date: 2013-06-12 16:46+0200\n" +"PO-Revision-Date: 2010-08-09 17:00-0700\n" +"Last-Translator: mangg \n" +"Language-Team: Korean\n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: scanner.c:165 +msgid "Unknown Date" +msgstr "알수 없는 날짜" + +#: scanner.c:167 +msgid "Unknown Camera" +msgstr "알수 없는 카메라" + +#: scanner.c:277 +msgid "- All Albums -" +msgstr "-모든 앨범-" + +#: scanner.c:285 scanner.c:292 scanner.c:296 +msgid "Unknown Album" +msgstr "알수 없는 앨범" + +#: scanner.c:318 +msgid "- All Artists -" +msgstr "- 모든 아티스트 -" + +#: scanner.c:326 scanner.c:332 scanner.c:336 +msgid "Unknown Artist" +msgstr "알수없는 아티스트" + +#: scanner.c:528 +msgid "Music" +msgstr "음악" + +#: scanner.c:529 +msgid "All Music" +msgstr "모든 음악" + +#: scanner.c:530 +msgid "Genre" +msgstr "장르" + +#: scanner.c:531 +msgid "Artist" +msgstr "아티스트" + +#: scanner.c:532 +msgid "Album" +msgstr "앨범" + +#: scanner.c:533 scanner.c:538 scanner.c:544 +msgid "Folders" +msgstr "폴더" + +#: scanner.c:534 +msgid "Playlists" +msgstr "재생 목록" + +#: scanner.c:598 +msgid "Recently Added" +msgstr "최근에 추가" + +#: scanner.c:536 +msgid "Video" +msgstr "비디오" + +#: scanner.c:537 +msgid "All Video" +msgstr "모든 비디오" + +#: scanner.c:540 +msgid "Pictures" +msgstr "사진" + +#: scanner.c:541 +msgid "All Pictures" +msgstr "모든 사진" + +#: scanner.c:542 +msgid "Date Taken" +msgstr "촬영 날짜" + +#: scanner.c:543 +msgid "Camera" +msgstr "카메라" + +#: scanner.c:546 +msgid "Browse Folders" +msgstr "폴더 보기" + +#: scanner.c:690 +#, c-format +msgid "Scanning %s\n" +msgstr "검색중 %s\n" + +#: scanner.c:766 +#, c-format +msgid "Scanning %s finished (%llu files)!\n" +msgstr "%s 검색 종료. (%llu 파일)\n" diff --git a/po/minidlna.pot b/po/minidlna.pot index 67e53a3..bb16145 100644 --- a/po/minidlna.pot +++ b/po/minidlna.pot @@ -84,6 +84,10 @@ msgstr "" msgid "Playlists" msgstr "" +#: scanner.c:598 +msgid "Recently Added" +msgstr "" + #: scanner.c:536 msgid "Video" msgstr "" diff --git a/po/nb.po b/po/nb.po index c63332a..8d9b8fa 100644 --- a/po/nb.po +++ b/po/nb.po @@ -68,6 +68,10 @@ msgstr "Mapper" msgid "Playlists" msgstr "Spillelister" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Nylig lagt til" + #: scanner.c:536 msgid "Video" msgstr "Video" diff --git a/po/nl.po b/po/nl.po index afe3486..0e0c1b6 100644 --- a/po/nl.po +++ b/po/nl.po @@ -68,6 +68,10 @@ msgstr "Mappen" msgid "Playlists" msgstr "Afspeellijst" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Nyligen tillagd" + #: scanner.c:536 msgid "Video" msgstr "Video" diff --git a/po/pl.po b/po/pl.po index 777c6d4..2d3cd8b 100644 --- a/po/pl.po +++ b/po/pl.po @@ -83,6 +83,10 @@ msgstr "Folder" msgid "Playlists" msgstr "Lista Utworow" +#: scanner.c:598 +msgid "Recently Added" +msgstr "Niedawno dodane" + #: scanner.c:536 msgid "Video" msgstr "Filmy" diff --git a/po/ru.po b/po/ru.po index fda545d..e07502b 100644 --- a/po/ru.po +++ b/po/ru.po @@ -85,6 +85,10 @@ msgstr "Папки" msgid "Playlists" msgstr "Списки Воспроизведения" +#: scanner.c:598 +msgid "Recently Added" +msgstr "" + #: scanner.c:536 msgid "Video" msgstr "Видео" diff --git a/po/sl.po b/po/sl.po index cb28c84..ceb31d8 100644 --- a/po/sl.po +++ b/po/sl.po @@ -83,6 +83,10 @@ msgstr "Mape" msgid "Playlists" msgstr "Seznami predvajanj" +#: scanner.c:598 +msgid "Recently Added" +msgstr "nedavno dodano" + #: scanner.c:536 msgid "Video" msgstr "Video" diff --git a/po/sv.po b/po/sv.po index 5880d4b..1005813 100644 --- a/po/sv.po +++ b/po/sv.po @@ -68,6 +68,10 @@ msgstr "Mappar" msgid "Playlists" msgstr "Spelningslistor" +#: scanner.c:598 +msgid "Recently Added" +msgstr "nyligen tillagda" + #: scanner.c:536 msgid "Video" msgstr "Film" diff --git a/process.c b/process.c index 96ec1c3..ecb4922 100644 --- a/process.c +++ b/process.c @@ -96,10 +96,10 @@ process_fork(struct client_cache_s *client) pid_t pid = fork(); if (pid > 0) { - number_of_children++; if (client) client->connections++; add_process_info(pid, client); + number_of_children++; } return pid; diff --git a/scanner.c b/scanner.c index bcf61dd..939b611 100644 --- a/scanner.c +++ b/scanner.c @@ -594,7 +594,8 @@ CreateDatabase(void) ret = sql_exec(db, "INSERT into OBJECTS (OBJECT_ID, PARENT_ID, DETAIL_ID, CLASS, NAME)" " values " "('%s', '%s', %lld, 'container.storageFolder', '%q')", - magic->objectid_match, parent, GetFolderMetadata(magic->name, NULL, NULL, NULL, 0), magic->name); + magic->objectid_match, parent, + GetFolderMetadata(_(magic->name), NULL, NULL, NULL, 0), _(magic->name)); free(parent); if( ret != SQLITE_OK ) goto sql_failed; @@ -793,11 +794,13 @@ ScanDirectory(const char *dir, const char *parent, media_types dir_types, const break; default: n = -1; + errno = EINVAL; break; } if( n < 0 ) { - DPRINTF(E_WARN, L_SCANNER, "Error scanning %s\n", dir); + DPRINTF(E_WARN, L_SCANNER, "Error scanning %s [%s]\n", + dir, strerror(errno)); return; } diff --git a/tagutils/tagutils-flc.c b/tagutils/tagutils-flc.c index b8f41d4..926673b 100644 --- a/tagutils/tagutils-flc.c +++ b/tagutils/tagutils-flc.c @@ -37,8 +37,9 @@ _get_flctags(char *filename, struct song_metadata *psong) if(!FLAC__metadata_simple_iterator_init(iterator, filename, true, true)) { - DPRINTF(E_ERROR, L_SCANNER, "Cannot extract tag from %s\n", filename); - return -1; + DPRINTF(E_ERROR, L_SCANNER, "Cannot extract tag from %s [%s]\n", filename, + FLAC__Metadata_SimpleIteratorStatusString[FLAC__metadata_simple_iterator_status(iterator)]); + goto _exit; } do { @@ -52,6 +53,8 @@ _get_flctags(char *filename, struct song_metadata *psong) switch(block->type) { case FLAC__METADATA_TYPE_STREAMINFO: + if (!block->data.stream_info.sample_rate) + break; /* Info is crap, avoid div-by-zero. */ sec = (unsigned int)(block->data.stream_info.total_samples / block->data.stream_info.sample_rate); ms = (unsigned int)(((block->data.stream_info.total_samples % @@ -75,6 +78,10 @@ _get_flctags(char *filename, struct song_metadata *psong) break; #if FLAC_API_VERSION_CURRENT >= 10 case FLAC__METADATA_TYPE_PICTURE: + if (psong->image) { + DPRINTF(E_MAXDEBUG, L_SCANNER, "Ignoring additional image [%s]\n", filename); + break; + } psong->image_size = block->data.picture.data_length; if((psong->image = malloc(psong->image_size))) memcpy(psong->image, block->data.picture.data, psong->image_size); diff --git a/testupnpdescgen.c b/testupnpdescgen.c index 5d88443..657dcf5 100644 --- a/testupnpdescgen.c +++ b/testupnpdescgen.c @@ -40,9 +40,6 @@ char modelname[] = "MiniDLNA"; char modelnumber[] = "1"; char presentationurl[] = "http://192.168.0.1:8080/"; unsigned int updateID = 0; -#if PNPX -char pnpx_hwid[] = "VEN_01F2&DEV_0101&REV_01 VEN_0033&DEV_0001&REV_01"; -#endif int getifaddr(const char * ifname, char * buf, int len) { diff --git a/upnpdescgen.c b/upnpdescgen.c index c00cee0..22c8217 100644 --- a/upnpdescgen.c +++ b/upnpdescgen.c @@ -115,29 +115,17 @@ static const char xmlver[] = static const char root_service[] = "scpd xmlns=\"urn:schemas-upnp-org:service-1-0\""; static const char root_device[] = - "root xmlns=\"urn:schemas-upnp-org:device-1-0\"" -#if PNPX - " xmlns:pnpx=\"http://schemas.microsoft.com/windows/pnpx/2005/11\"" - " xmlns:df=\"http://schemas.microsoft.com/windows/2008/09/devicefoundation\"" -#endif - ; + "root xmlns=\"urn:schemas-upnp-org:device-1-0\""; /* root Description of the UPnP Device */ static const struct XMLElt rootDesc[] = { {root_device, INITHELPER(1,2)}, {"specVersion", INITHELPER(3,2)}, - {"device", INITHELPER(5,(14+PNPX))}, + {"device", INITHELPER(5,(14))}, {"/major", "1"}, {"/minor", "0"}, {"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"}, -#if PNPX == 5 - {"/pnpx:X_hardwareId", pnpx_hwid}, - {"/pnpx:X_compatibleId", "MS_DigitalMediaDeviceClass_DMS_V001"}, - {"/pnpx:X_deviceCategory", "MediaDevices"}, - {"/df:X_deviceCategory", "Multimedia.DMS"}, - {"/microsoft:magicPacketWakeSupported xmlns:microsoft=\"urn:schemas-microsoft-com:WMPNSS-1-0\"", "0"}, -#endif {"/friendlyName", friendly_name}, /* required */ {"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */ {"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */ @@ -149,12 +137,12 @@ static const struct XMLElt rootDesc[] = {"/UDN", uuidvalue}, /* required */ {"/dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"", "DMS-1.50"}, {"/presentationURL", presentationurl}, /* recommended */ - {"iconList", INITHELPER((19+PNPX),4)}, - {"serviceList", INITHELPER((43+PNPX),3)}, - {"icon", INITHELPER((23+PNPX),5)}, - {"icon", INITHELPER((28+PNPX),5)}, - {"icon", INITHELPER((33+PNPX),5)}, - {"icon", INITHELPER((38+PNPX),5)}, + {"iconList", INITHELPER((19),4)}, + {"serviceList", INITHELPER((43),3)}, + {"icon", INITHELPER((23),5)}, + {"icon", INITHELPER((28),5)}, + {"icon", INITHELPER((33),5)}, + {"icon", INITHELPER((38),5)}, {"/mimetype", "image/png"}, {"/width", "48"}, {"/height", "48"}, @@ -175,9 +163,9 @@ static const struct XMLElt rootDesc[] = {"/height", "120"}, {"/depth", "24"}, {"/url", "/icons/lrg.jpg"}, - {"service", INITHELPER((46+PNPX),5)}, - {"service", INITHELPER((51+PNPX),5)}, - {"service", INITHELPER((56+PNPX),5)}, + {"service", INITHELPER((46),5)}, + {"service", INITHELPER((51),5)}, + {"service", INITHELPER((56),5)}, {"/serviceType", "urn:schemas-upnp-org:service:ContentDirectory:1"}, {"/serviceId", "urn:upnp-org:serviceId:ContentDirectory"}, {"/controlURL", CONTENTDIRECTORY_CONTROLURL}, @@ -584,10 +572,10 @@ genRootDescSamsung(int * len) memcpy(str, xmlver, *len + 1); /* Replace the optional modelURL and manufacturerURL fields with Samsung foo */ memcpy(&samsungRootDesc, &rootDesc, sizeof(rootDesc)); - samsungRootDesc[8+PNPX].eltname = "/sec:ProductCap"; - samsungRootDesc[8+PNPX].data = "smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec"; - samsungRootDesc[12+PNPX].eltname = "/sec:X_ProductCap"; - samsungRootDesc[12+PNPX].data = "smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec"; + samsungRootDesc[8].eltname = "/sec:ProductCap"; + samsungRootDesc[8].data = "smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec"; + samsungRootDesc[12].eltname = "/sec:X_ProductCap"; + samsungRootDesc[12].data = "smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec"; str = genXML(str, len, &tmplen, samsungRootDesc); str[*len] = '\0'; return str; diff --git a/upnpglobalvars.c b/upnpglobalvars.c index fff02c8..6660ac8 100644 --- a/upnpglobalvars.c +++ b/upnpglobalvars.c @@ -66,9 +66,6 @@ char uuidvalue[] = "uuid:00000000-0000-0000-0000-000000000000"; char modelname[MODELNAME_MAX_LEN] = ROOTDEV_MODELNAME; char modelnumber[MODELNUMBER_MAX_LEN] = MINIDLNA_VERSION; char serialnumber[SERIALNUMBER_MAX_LEN] = "00000000"; -#if PNPX -char pnpx_hwid[] = "VEN_0000&DEV_0000&REV_01 VEN_0033&DEV_0001&REV_01"; -#endif /* presentation url : * http://nnn.nnn.nnn.nnn:ppppp/ => max 30 bytes including terminating 0 */ diff --git a/upnpglobalvars.h b/upnpglobalvars.h index b69846a..51cc5ed 100644 --- a/upnpglobalvars.h +++ b/upnpglobalvars.h @@ -57,7 +57,7 @@ #include -#define MINIDLNA_VERSION "1.1.4" +#define MINIDLNA_VERSION "1.1.6" #ifdef NETGEAR # define SERVER_NAME "ReadyDLNA" @@ -75,10 +75,6 @@ #endif #define THISORNUL(s) (s ? s : "") -#ifndef PNPX -#define PNPX 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," \ @@ -191,6 +187,7 @@ extern uint32_t runtime_flags; #define NO_PLAYLIST_MASK 0x0008 #define SYSTEMD_MASK 0x0010 #define MERGE_MEDIA_DIRS_MASK 0x0020 +#define WIDE_LINKS_MASK 0x0040 #define SETFLAG(mask) runtime_flags |= mask #define GETFLAG(mask) (runtime_flags & mask) @@ -212,10 +209,6 @@ extern char serialnumber[]; #define PRESENTATIONURL_MAX_LEN 64 extern char presentationurl[]; -#if PNPX -extern char pnpx_hwid[]; -#endif - /* lan addresses */ extern int n_lan_addr; extern struct lan_addr_s lan_addr[]; diff --git a/upnphttp.c b/upnphttp.c index bf6f3d3..4184bb2 100644 --- a/upnphttp.c +++ b/upnphttp.c @@ -162,6 +162,10 @@ ParseHttpHeaders(struct upnphttp * h) while(*p && (*p < '0' || *p > '9')) p++; h->req_contentlen = atoi(p); + if(h->req_contentlen < 0) { + DPRINTF(E_WARN, L_HTTP, "Invalid Content-Length %d", h->req_contentlen); + h->req_contentlen = 0; + } } else if(strncasecmp(line, "SOAPAction", 10)==0) { @@ -408,9 +412,7 @@ ParseHttpHeaders(struct upnphttp * h) next_header: line = strstr(line, "\r\n"); if (!line) - { return; - } line += 2; } if( h->reqflags & FLAG_CHUNKED ) @@ -475,6 +477,21 @@ Send400(struct upnphttp * h) CloseSocket_upnphttp(h); } +/* very minimalistic 403 error message */ +static void +Send403(struct upnphttp * h) +{ + static const char body403[] = + "403 Forbidden" + "

Forbidden

You don't have permission to access this resource." + "\r\n"; + h->respflags = FLAG_HTML; + BuildResp2_upnphttp(h, 403, "Forbidden", + body403, sizeof(body403) - 1); + SendResp_upnphttp(h); + CloseSocket_upnphttp(h); +} + /* very minimalistic 404 error message */ static void Send404(struct upnphttp * h) @@ -825,8 +842,6 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h) for(i = 0; i<15 && *p && *p != '\r'; i++) HttpVer[i] = *(p++); HttpVer[i] = '\0'; - /*DPRINTF(E_INFO, L_HTTP, "HTTP REQUEST : %s %s (%s)\n", - HttpCommand, HttpUrl, HttpVer);*/ /* set the interface here initially, in case there is no Host header */ for(i = 0; inext) + { + if (strncmp(path, media_path->path, strlen(media_path->path)) == 0) + break; + } + if (!media_path && strncmp(path, db_path, strlen(db_path))) + { + DPRINTF(E_ERROR, L_HTTP, "Rejecting wide link %s -> %s\n", + orig_path, path); + return -403; + } + } + else + path = orig_path; + + fd = open(path, O_RDONLY); + if (fd < 0) + DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); + + return fd; +} + static void SendResp_icon(struct upnphttp * h, char * icon) { @@ -1413,11 +1468,13 @@ SendResp_albumArt(struct upnphttp * h, char * object) } DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %lld [%s]\n", id, path); - fd = open(path, O_RDONLY); + fd = _open_file(path); if( fd < 0 ) { - DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); sqlite3_free(path); - Send404(h); + if (fd == -403) + Send403(h); + else + Send404(h); return; } sqlite3_free(path); @@ -1461,11 +1518,13 @@ SendResp_caption(struct upnphttp * h, char * object) } DPRINTF(E_INFO, L_HTTP, "Serving caption ID: %lld [%s]\n", id, path); - fd = open(path, O_RDONLY); + fd = _open_file(path); if( fd < 0 ) { - DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); sqlite3_free(path); - Send404(h); + if (fd == -403) + Send403(h); + else + Send404(h); return; } sqlite3_free(path); @@ -1568,8 +1627,7 @@ SendResp_resizedimg(struct upnphttp * h, char * object) char *key, *val; char *saveptr, *item = NULL; int rotate; - /* Not implemented yet * - char *pixelshape=NULL; */ + int pixw = 0, pixh = 0; long long id; int rows=0, chunked, ret; image_s *imsrc = NULL, *imdst = NULL; @@ -1622,11 +1680,12 @@ SendResp_resizedimg(struct upnphttp * h, char * object) rotate = (rotate + atoi(val)) % 360; sql_exec(db, "UPDATE DETAILS set ROTATION = %d where ID = %lld", rotate, id); } - /* Not implemented yet * else if( strcasecmp(key, "pixelshape") == 0 ) { - pixelshape = val; - } */ + ret = sscanf(val, "%d:%d", &pixw, &pixh); + if( ret != 2 ) + pixw = pixh = 0; + } } #if USE_FORK @@ -1680,6 +1739,14 @@ SendResp_resizedimg(struct upnphttp * h, char * object) dsth = height; dstw = (((height<<10)/srch) * srcw>>10); } + /* Account for pixel shape */ + if( pixw && pixh ) + { + if( pixh > pixw ) + dsth = dsth * pixw / pixh; + else if( pixw > pixh ) + dstw = dstw * pixh / pixw; + } if( dstw <= 160 && dsth <= 160 ) strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_TN;"); @@ -1906,10 +1973,12 @@ SendResp_dlnafile(struct upnphttp *h, char *object) } offset = h->req_RangeStart; - sendfh = open(last_file.path, O_RDONLY); + sendfh = _open_file(last_file.path); if( sendfh < 0 ) { - DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", last_file.path); - Send404(h); + if (sendfh == -403) + Send403(h); + else + Send404(h); goto error; } size = lseek(sendfh, 0, SEEK_END); diff --git a/upnpsoap.c b/upnpsoap.c index ee0bba3..e438480 100644 --- a/upnpsoap.c +++ b/upnpsoap.c @@ -818,6 +818,7 @@ callback(void *args, int argc, char **argv, char **azColName) #endif } passed_args->returned++; + passed_args->flags &= ~RESPONSE_FLAGS; if( strncmp(class, "item", 4) == 0 ) { @@ -1060,6 +1061,7 @@ callback(void *args, int argc, char **argv, char **azColName) break; case ESamsungSeriesCDE: case ELGDevice: + case ELGNetCastDevice: case EAsusOPlay: default: if( passed_args->flags & FLAG_HAS_CAPTIONS ) @@ -1069,7 +1071,7 @@ callback(void *args, int argc, char **argv, char **azColName) "http://%s:%d/Captions/%s.srt" "</res>", lan_addr[passed_args->iface].str, runtime_vars.port, detailID); - else if( passed_args->filter & FILTER_SEC_CAPTION_INFO_EX ) + if( passed_args->filter & FILTER_SEC_CAPTION_INFO_EX ) ret = strcatf(str, "<sec:CaptionInfoEx sec:type=\"srt\">" "http://%s:%d/Captions/%s.srt" "</sec:CaptionInfoEx>", @@ -1452,6 +1454,8 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) refid_sql = magic->refid_sql; if (magic->where) strncpyt(where, magic->where, sizeof(where)); + if (magic->orderby && !GETFLAG(DLNA_STRICT_MASK)) + orderBy = strdup(magic->orderby); if (magic->max_count > 0) { int limit = MAX(magic->max_count - StartingIndex, 0); @@ -1476,7 +1480,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) } ret = 0; - if( SortCriteria ) + if (SortCriteria && !orderBy) { __SORT_LIMIT orderBy = parse_sort_criteria(SortCriteria, &ret); @@ -1495,6 +1499,9 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) __SORT_LIMIT ret = xasprintf(&orderBy, "order by o.CLASS, d.DISC, d.TRACK, d.TITLE"); } + /* LG TV ordering bug */ + else if( args.client == ELGDevice ) + ret = xasprintf(&orderBy, "order by o.CLASS, d.TITLE"); else orderBy = parse_sort_criteria(SortCriteria, &ret); if( ret == -1 ) @@ -2085,14 +2092,21 @@ SamsungSetBookmark(struct upnphttp * h, const char * action) ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data, 0); ObjectID = GetValueFromNameValueList(&data, "ObjectID"); PosSecond = GetValueFromNameValueList(&data, "PosSecond"); + + if ( atoi(PosSecond) < 30 ) + PosSecond = "0"; + if( ObjectID && PosSecond ) { int ret; + const char *rid = ObjectID; + + in_magic_container(ObjectID, 0, &rid); ret = sql_exec(db, "INSERT OR REPLACE into BOOKMARKS" " VALUES " - "((select DETAIL_ID from OBJECTS where OBJECT_ID = '%q'), %q)", ObjectID, PosSecond); + "((select DETAIL_ID from OBJECTS where OBJECT_ID = '%q'), %q)", rid, PosSecond); if( ret != SQLITE_OK ) - DPRINTF(E_WARN, L_METADATA, "Error setting bookmark %s on ObjectID='%s'\n", PosSecond, ObjectID); + DPRINTF(E_WARN, L_METADATA, "Error setting bookmark %s on ObjectID='%s'\n", PosSecond, rid); BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1); } else diff --git a/utils.c b/utils.c index d728136..5a233dc 100644 --- a/utils.c +++ b/utils.c @@ -194,14 +194,16 @@ unescape_tag(const char *tag, int force_alloc) { char *esc_tag = NULL; - if( strstr(tag, "&") || strstr(tag, "<") || strstr(tag, ">") - || strstr(tag, """) ) + if (strchr(tag, '&') && + (strstr(tag, "&") || strstr(tag, "<") || strstr(tag, ">") || + strstr(tag, """) || strstr(tag, "'"))) { esc_tag = strdup(tag); esc_tag = modifyString(esc_tag, "&", "&", 1); esc_tag = modifyString(esc_tag, "<", "<", 1); esc_tag = modifyString(esc_tag, ">", ">", 1); esc_tag = modifyString(esc_tag, """, "\"", 1); + esc_tag = modifyString(esc_tag, "'", "'", 1); } else if( force_alloc ) esc_tag = strdup(tag);