From aef9da5c9c188dd3c3b6c70523e4c909af4b2d70 Mon Sep 17 00:00:00 2001 From: Justin Maggard Date: Sat, 21 Jan 2012 01:00:26 +0000 Subject: * Enhance error checking in some additional places as required by the latest UPnP CTT. --- NEWS | 12 ++++++ codelength.h | 1 - daemonize.c | 1 - getifaddr.c | 1 - inotify.c | 3 +- minidlna.c | 5 --- minissdp.c | 75 +++++++++++++++++++++------------- minixml.c | 1 - testupnpdescgen.c | 1 - upnpdescgen.c | 1 - upnpevents.c | 62 ++++++++-------------------- upnphttp.c | 119 ++++++++++++++++++++++++++++++++++++++---------------- upnphttp.h | 5 ++- upnpsoap.c | 74 ++++++++++++++++++++++++++++----- 14 files changed, 230 insertions(+), 131 deletions(-) diff --git a/NEWS b/NEWS index d8275a3..3093c3f 100644 --- a/NEWS +++ b/NEWS @@ -2,9 +2,21 @@ -------------------------------- - Add support for other operating systems. - Switch to autoconf from our little genconfig.sh. + +1.0.23 - Released 00-Month-0000 +-------------------------------- - Enable the subtitle menu on some Samsung TV's. - Add subtitle support for Panasonic TV's. - Add workarounds for LifeTab tablets' bad behavior. +- Speed up playlist parsing. +- Make metadata-based virtual containers case insensitive. +- Add folder art support (very few clients support this though). +- Improve trimming of quotation marks. +- Fix SRT caption support with the latest Samsung Series D firmware. +- Fix subtitles on LG TV's for items whose titles don't have a dot in them. +- Add support for the av:mediaClass tag, so some Sony devices can filter items by media type. +- Fix inotify detection issues on first-level folders. +- Work around LifeTab's broken DLNA support. 1.0.22 - Released 24-Aug-2011 -------------------------------- diff --git a/codelength.h b/codelength.h index dcb6868..3e298a4 100644 --- a/codelength.h +++ b/codelength.h @@ -1,4 +1,3 @@ -/* $Id$ */ /* Project : miniupnp * Author : Thomas BERNARD * copyright (c) 2005-2008 Thomas Bernard diff --git a/daemonize.c b/daemonize.c index 944f8f1..7873d32 100644 --- a/daemonize.c +++ b/daemonize.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * diff --git a/getifaddr.c b/getifaddr.c index 4d159a3..f5c2e08 100644 --- a/getifaddr.c +++ b/getifaddr.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * diff --git a/inotify.c b/inotify.c index 6d524ec..5dc0962 100644 --- a/inotify.c +++ b/inotify.c @@ -156,7 +156,7 @@ inotify_create_watches(int fd) add_watch(fd, media_path->path); num_watches++; } - sql_get_table(db, "SELECT PATH from DETAILS where SIZE is NULL and PATH is not NULL", &result, &rows, NULL); + sql_get_table(db, "SELECT PATH from DETAILS where MIME is NULL and PATH is not NULL", &result, &rows, NULL); for( i=1; i <= rows; i++ ) { DPRINTF(E_DEBUG, L_INOTIFY, "Add watch to %s\n", result[i]); @@ -646,6 +646,7 @@ start_inotify() inotify_create_watches(pollfds[0].fd); if (setpriority(PRIO_PROCESS, 0, 19) == -1) DPRINTF(E_WARN, L_INOTIFY, "Failed to reduce inotify thread priority\n"); + sqlite3_release_memory(1<<31); while( !quitting ) { diff --git a/minidlna.c b/minidlna.c index db757d3..5e3f0a1 100644 --- a/minidlna.c +++ b/minidlna.c @@ -825,13 +825,8 @@ init(int argc, char * * argv) } else { -#ifdef READYNAS - snprintf(presentationurl, PRESENTATIONURL_MAX_LEN, - "http://%s/admin/", lan_addr[0].str); -#else snprintf(presentationurl, PRESENTATIONURL_MAX_LEN, "http://%s:%d/", lan_addr[0].str, runtime_vars.port); -#endif } /* set signal handler */ diff --git a/minissdp.c b/minissdp.c index 320711b..f5a8c19 100644 --- a/minissdp.c +++ b/minissdp.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -650,12 +651,12 @@ ProcessSSDPRequest(int s, unsigned short port) } else if( !man || (strncmp(man, "\"ssdp:discover\"", 15) != 0) ) { - DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MAN header %.*s]\n", - inet_ntoa(sendername.sin_addr), man_len, man); + DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n", + inet_ntoa(sendername.sin_addr), "MAN", man_len, man); } else if( !mx || mx == mx_end || mx_val < 0 ) { - DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad MX header %.*s]\n", - inet_ntoa(sendername.sin_addr), mx_len, mx); + DPRINTF(E_INFO, L_SSDP, "WARNING: Ignoring invalid SSDP M-SEARCH from %s [bad %s header '%.*s']\n", + inet_ntoa(sendername.sin_addr), "MX", mx_len, mx); } else if( st && (st_len > 0) ) { @@ -685,11 +686,26 @@ ProcessSSDPRequest(int s, unsigned short port) for(i = 0; known_service_types[i]; i++) { l = strlen(known_service_types[i]); - if(l<=st_len && (0 == memcmp(st, known_service_types[i], l))) + if( l <= st_len && (memcmp(st, known_service_types[i], l) == 0)) { - /* Check version number - must always be 1 currently. */ - if( (st[st_len-2] == ':') && (atoi(st+st_len-1) != 1) ) - break; + if( st_len != l ) + { + /* Check version number - must always be 1 currently. */ + if( (st[l-1] == ':') && (st[l] == '1') ) + l++; + while( l < st_len ) + { + if( !isspace(st[l]) ) + { + DPRINTF(E_DEBUG, L_SSDP, "Ignoring SSDP M-SEARCH with bad extra data [%s]\n", + inet_ntoa(sendername.sin_addr)); + break; + } + l++; + } + if( l != st_len ) + break; + } _usleep(random()>>20); SendSSDPAnnounce2(s, sendername, i, @@ -731,6 +747,7 @@ SendSSDPGoodbye(int * sockets, int n_sockets) struct sockaddr_in sockname; int n, l; int i, j; + int dup, ret = 0; char bufr[512]; memset(&sockname, 0, sizeof(struct sockaddr_in)); @@ -738,31 +755,35 @@ SendSSDPGoodbye(int * sockets, int n_sockets) sockname.sin_port = htons(SSDP_PORT); sockname.sin_addr.s_addr = inet_addr(SSDP_MCAST_ADDR); - for(j=0; j1?"1":""), - uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") ); - //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr); - n = sendto(sockets[j], bufr, l, 0, - (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); - if(n < 0) + for(i=0; known_service_types[i]; i++) { - DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno)); - return -1; + l = snprintf(bufr, sizeof(bufr), + "NOTIFY * HTTP/1.1\r\n" + "HOST:%s:%d\r\n" + "NT:%s%s\r\n" + "USN:%s%s%s%s\r\n" + "NTS:ssdp:byebye\r\n" + "\r\n", + SSDP_MCAST_ADDR, SSDP_PORT, + known_service_types[i], (i>1?"1":""), + uuidvalue, (i>0?"::":""), (i>0?known_service_types[i]:""), (i>1?"1":"") ); + //DEBUG DPRINTF(E_DEBUG, L_SSDP, "Sending NOTIFY:\n%s", bufr); + n = sendto(sockets[j], bufr, l, 0, + (struct sockaddr *)&sockname, sizeof(struct sockaddr_in) ); + if(n < 0) + { + DPRINTF(E_ERROR, L_SSDP, "sendto(udp_shutdown=%d): %s\n", sockets[j], strerror(errno)); + ret = -1; + break; + } } } } - return 0; + return ret; } /* SubmitServicesToMiniSSDPD() : diff --git a/minixml.c b/minixml.c index b335b68..3dfb9a2 100644 --- a/minixml.c +++ b/minixml.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* minixml.c : the minimum size a xml parser can be ! */ /* Project : miniupnp * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ diff --git a/testupnpdescgen.c b/testupnpdescgen.c index 398d731..5d88443 100644 --- a/testupnpdescgen.c +++ b/testupnpdescgen.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * diff --git a/upnpdescgen.c b/upnpdescgen.c index c9a40f6..e251412 100644 --- a/upnpdescgen.c +++ b/upnpdescgen.c @@ -1,4 +1,3 @@ -/* $Id$ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * diff --git a/upnpevents.c b/upnpevents.c index 895819d..6ce931b 100644 --- a/upnpevents.c +++ b/upnpevents.c @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +74,6 @@ struct subscriber { struct upnp_event_notify * notify; time_t timeout; uint32_t seq; - /*enum { EWanCFG = 1, EWanIPC, EL3F } service;*/ enum subscriber_service_enum service; char uuid[42]; char callback[]; @@ -147,12 +147,8 @@ upnpevents_addSubscriber(const char * eventurl, int timeout) { struct subscriber * tmp; - /*static char uuid[42];*/ - /* "uuid:00000000-0000-0000-0000-000000000000"; 5+36+1=42bytes */ DPRINTF(E_DEBUG, L_HTTP, "addSubscriber(%s, %.*s, %d)\n", eventurl, callbacklen, callback, timeout); - /*strncpy(uuid, uuidvalue, sizeof(uuid)); - uuid[sizeof(uuid)-1] = '\0';*/ tmp = newSubscriber(eventurl, callback, callbacklen); if(!tmp) return NULL; @@ -198,8 +194,7 @@ upnpevents_removeSubscriber(const char * sid, int sidlen) return -1; } -/* notifies all subscriber of a number of port mapping change - * or external ip address change */ +/* notifies all subscribers of a SystemUpdateID change */ void upnp_event_var_change_notify(enum subscriber_service_enum service) { @@ -265,7 +260,7 @@ upnp_event_notify_connect(struct upnp_event_notify * obj) } p = obj->sub->callback; p += 7; /* http:// */ - while(*p != '/' && *p != ':') + while(*p != '/' && *p != ':' && i < (sizeof(obj->addrstr)-1)) obj->addrstr[i++] = *(p++); obj->addrstr[i] = '\0'; if(*p == ':') { @@ -349,16 +344,15 @@ static void upnp_event_send(struct upnp_event_notify * obj) { int i; //DEBUG DPRINTF(E_DEBUG, L_HTTP, "Sending UPnP Event:\n%s", obj->buffer+obj->sent); - i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0); - if(i<0) { - DPRINTF(E_WARN, L_HTTP, "%s: send(): %s\n", "upnp_event_send", strerror(errno)); - obj->state = EError; - return; + while( obj->sent < obj->tosend ) { + i = send(obj->s, obj->buffer + obj->sent, obj->tosend - obj->sent, 0); + if(i<0) { + DPRINTF(E_WARN, L_HTTP, "%s: send(): %s\n", "upnp_event_send", strerror(errno)); + obj->state = EError; + return; + } + obj->sent += i; } - else if(i != (obj->tosend - obj->sent)) - DPRINTF(E_WARN, L_HTTP, "%s: %d bytes send out of %d\n", - "upnp_event_send", i, obj->tosend - obj->sent); - obj->sent += i; if(obj->sent == obj->tosend) obj->state = EWaitingForResponse; } @@ -376,7 +370,11 @@ static void upnp_event_recv(struct upnp_event_notify * obj) n, n, obj->buffer); obj->state = EFinished; if(obj->sub) + { obj->sub->seq++; + if (!obj->sub->seq) + obj->sub->seq++; + } } static void @@ -421,13 +419,13 @@ void upnpevents_selectfds(fd_set *readset, fd_set *writeset, int * max_fd) if(obj->s > *max_fd) *max_fd = obj->s; break; - case EFinished: - case EError: case EWaitingForResponse: FD_SET(obj->s, readset); if(obj->s > *max_fd) *max_fd = obj->s; break; + default: + break; } } } @@ -483,29 +481,3 @@ void upnpevents_processfds(fd_set *readset, fd_set *writeset) } } -#ifdef USE_MINIDLNACTL -void write_events_details(int s) { - int n; - char buff[80]; - struct upnp_event_notify * obj; - struct subscriber * sub; - write(s, "Events details\n", 15); - for(obj = notifylist.lh_first; obj != NULL; obj = obj->entries.le_next) { - n = snprintf(buff, sizeof(buff), " %p sub=%p state=%d s=%d\n", - obj, obj->sub, obj->state, obj->s); - write(s, buff, n); - } - write(s, "Subscribers :\n", 14); - for(sub = subscriberlist.lh_first; sub != NULL; sub = sub->entries.le_next) { - n = snprintf(buff, sizeof(buff), " %p timeout=%d seq=%u service=%d\n", - sub, sub->timeout, sub->seq, sub->service); - write(s, buff, n); - n = snprintf(buff, sizeof(buff), " notify=%p %s\n", - sub->notify, sub->uuid); - write(s, buff, n); - n = snprintf(buff, sizeof(buff), " %s\n", - sub->callback); - write(s, buff, n); - } -} -#endif diff --git a/upnphttp.c b/upnphttp.c index 4e46548..c3c0626 100644 --- a/upnphttp.c +++ b/upnphttp.c @@ -209,6 +209,9 @@ ParseHttpHeaders(struct upnphttp * h) n++; h->req_Callback = p + 1; h->req_CallbackLen = MAX(0, n - 1); + /* Verify callback validity */ + if(strncmp(h->req_Callback, "http://", 7) != 0) + h->req_Callback = NULL; } else if(strncasecmp(line, "SID", 3)==0) { @@ -233,6 +236,17 @@ ParseHttpHeaders(struct upnphttp * h) h->req_SIDLen = n; } } + else if(strncasecmp(line, "NT", 2)==0) + { + p = colon + 1; + while(isspace(*p)) + p++; + n = 0; + while(!isspace(p[n])) + n++; + h->req_NT = p; + h->req_NTLen = n; + } /* Timeout: Seconds-nnnn */ /* TIMEOUT Recommended. Requested duration until subscription expires, @@ -406,6 +420,10 @@ intervening space) by either an integer or the keyword "infinite". */ h->reqflags |= FLAG_CHUNKED; } } + else if(strncasecmp(line, "Accept-Language", 15)==0) + { + h->reqflags |= FLAG_LANGUAGE; + } else if(strncasecmp(line, "getcontentFeatures.dlna.org", 27)==0) { p = colon + 1; @@ -651,6 +669,34 @@ sendXMLdesc(struct upnphttp * h, char * (f)(int *)) free(desc); } +static void +SendResp_presentation(struct upnphttp * h) +{ + char body[1024]; + int l; + h->respflags = FLAG_HTML; + +#ifdef READYNAS + l = snprintf(body, sizeof(body), "", + lan_addr[h->iface].str); +#else + int a, v, p; + a = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'a*'"); + v = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'v*'"); + p = sql_get_int_field(db, "SELECT count(*) from DETAILS where MIME glob 'i*'"); + l = snprintf(body, sizeof(body), + "" SERVER_NAME "" + "
" + "

" SERVER_NAME " status

" + "Audio files: %d
" + "Video files: %d
" + "Image files: %d
" + "\r\n", a, v, p); +#endif + BuildResp_upnphttp(h, body, l); + SendResp_upnphttp(h); +} + /* ProcessHTTPPOST_upnphttp() * executes the SOAP query if it is possible */ static void @@ -693,7 +739,15 @@ ProcessHTTPSubscribe_upnphttp(struct upnphttp * h, const char * path) DPRINTF(E_DEBUG, L_HTTP, "Callback '%.*s' Timeout=%d\n", h->req_CallbackLen, h->req_Callback, h->req_Timeout); DPRINTF(E_DEBUG, L_HTTP, "SID '%.*s'\n", h->req_SIDLen, h->req_SID); - if(!h->req_Callback && !h->req_SID) { + if(!h->req_NT) { + static const char err400str[] = + "Bad request"; + BuildResp2_upnphttp(h, 400, "Bad Request", + err400str, sizeof(err400str) - 1); + SendResp_upnphttp(h); + CloseSocket_upnphttp(h); + } else if((!h->req_Callback && !h->req_SID) || + strncmp(h->req_NT, "upnp:event", h->req_NTLen) != 0) { /* Missing or invalid CALLBACK : 412 Precondition Failed. * If CALLBACK header is missing or does not contain a valid HTTP URL, * the publisher must respond with HTTP error 412 Precondition Failed*/ @@ -944,6 +998,11 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h) SendResp_caption(h, HttpUrl+10); CloseSocket_upnphttp(h); } + else if(strcmp(HttpUrl, "/") == 0) + { + SendResp_presentation(h); + CloseSocket_upnphttp(h); + } else { DPRINTF(E_WARN, L_HTTP, "%s not found, responding ERROR 404\n", HttpUrl); @@ -1047,23 +1106,6 @@ Process_upnphttp(struct upnphttp * h) } } -static const char httpresphead[] = - "%s %d %s\r\n" - "Content-Type: %s\r\n" - "Connection: close\r\n" - "Content-Length: %d\r\n" - "Server: " MINIDLNA_SERVER_STRING "\r\n" -// "Accept-Ranges: bytes\r\n" - ; /*"\r\n";*/ -/* - "\n" - "" - "" - - "" - ""; -*/ /* with response code and response message * also allocate enough memory */ @@ -1072,15 +1114,22 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode, const char * respmsg, int bodylen) { + static const char httpresphead[] = + "%s %d %s\r\n" + "Content-Type: %s\r\n" + "Connection: close\r\n" + "Content-Length: %d\r\n" + "Server: " MINIDLNA_SERVER_STRING "\r\n"; + time_t curtime = time(NULL); + char date[30]; int templen; if(!h->res_buf) { - templen = sizeof(httpresphead) + 192 + bodylen; + templen = sizeof(httpresphead) + 256 + bodylen; h->res_buf = (char *)malloc(templen); h->res_buf_alloclen = templen; } h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, - //httpresphead, h->HttpVer, httpresphead, "HTTP/1.1", respcode, respmsg, (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"", @@ -1098,7 +1147,6 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode, h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, "300\r\n"); - //JM DLNA must force to 300 - "infinite\r\n"); } } if(h->respflags & FLAG_SID) { @@ -1106,19 +1154,22 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode, h->res_buf_alloclen - h->res_buflen, "SID: %.*s\r\n", h->req_SIDLen, h->req_SID); } -#if 0 // DLNA - char szTime[30]; - time_t curtime = time(NULL); - strftime(szTime, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); + if(h->reqflags & FLAG_LANGUAGE) { + h->res_buflen += snprintf(h->res_buf + h->res_buflen, + h->res_buf_alloclen - h->res_buflen, + "Content-Language: en\r\n"); + } + strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, - "Date: %s\r\n", szTime); + "Date: %s\r\n", date); h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, - "contentFeatures.dlna.org: \r\n"); + "EXT:\r\n"); +#if 0 // DLNA h->res_buflen += snprintf(h->res_buf + h->res_buflen, h->res_buf_alloclen - h->res_buflen, - "EXT:\r\n"); + "contentFeatures.dlna.org: \r\n"); #endif h->res_buf[h->res_buflen++] = '\r'; h->res_buf[h->res_buflen++] = '\n'; @@ -1339,8 +1390,6 @@ SendResp_albumArt(struct upnphttp * h, char * object) } DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %s [%s]\n", object, path); - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - fd = open(path, O_RDONLY); if( fd < 0 ) { DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path); @@ -1352,6 +1401,7 @@ SendResp_albumArt(struct upnphttp * h, char * object) 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: image/jpeg\r\n" "Content-Length: %jd\r\n" @@ -1403,8 +1453,8 @@ SendResp_caption(struct upnphttp * h, char * object) 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)); + 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" "Content-Length: %jd\r\n" @@ -1456,7 +1506,6 @@ SendResp_thumbnail(struct upnphttp * h, char * object) 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); @@ -1471,6 +1520,7 @@ SendResp_thumbnail(struct upnphttp * h, char * object) exif_data_unref(ed); return; } + 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: image/jpeg\r\n" "Content-Length: %d\r\n" @@ -1611,12 +1661,11 @@ SendResp_resizedimg(struct upnphttp * h, char * object) else if( srcw>>1 >= dstw && srch>>1 >= dsth ) scale = 2; - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); - str.data = header; str.size = sizeof(header); str.off = 0; + strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); strcatf(&str, "HTTP/1.1 200 OK\r\n" "Content-Type: image/jpeg\r\n" "Connection: close\r\n" @@ -1816,7 +1865,6 @@ SendResp_dlnafile(struct upnphttp * h, char * object) } } - strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); offset = h->req_RangeStart; sendfh = open(last_file.path, O_RDONLY); if( sendfh < 0 ) { @@ -1900,6 +1948,7 @@ SendResp_dlnafile(struct upnphttp * h, char * object) } } + strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); strcatf(&str, "Accept-Ranges: bytes\r\n" "Connection: close\r\n" "Date: %s\r\n" diff --git a/upnphttp.h b/upnphttp.h index 9fcfc27..64df2a5 100644 --- a/upnphttp.h +++ b/upnphttp.h @@ -71,6 +71,8 @@ struct upnphttp { int req_soapActionLen; const char * req_Callback; /* For SUBSCRIBE */ int req_CallbackLen; + const char * req_NT; + int req_NTLen; int req_Timeout; const char * req_SID; /* For UNSUBSCRIBE */ int req_SIDLen; @@ -92,9 +94,10 @@ struct upnphttp { #define FLAG_SID 0x00000002 #define FLAG_RANGE 0x00000004 #define FLAG_HOST 0x00000008 +#define FLAG_LANGUAGE 0x00000010 +#define FLAG_INVALID_REQ 0x00000040 #define FLAG_HTML 0x00000080 -#define FLAG_INVALID_REQ 0x00000010 #define FLAG_CHUNKED 0x00000100 #define FLAG_TIMESEEK 0x00000200 diff --git a/upnpsoap.c b/upnpsoap.c index 6e095d1..fb64b8f 100644 --- a/upnpsoap.c +++ b/upnpsoap.c @@ -129,11 +129,22 @@ IsAuthorizedValidated(struct upnphttp * h, const char * action) char body[512]; int bodylen; + struct NameValueParserData data; + const char * id; - bodylen = snprintf(body, sizeof(body), resp, - action, "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1", - 1, action); - BuildSendAndCloseSoapResp(h, body, bodylen); + ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); + id = GetValueFromNameValueList(&data, "DeviceID"); + if(id) + { + bodylen = snprintf(body, sizeof(body), resp, + action, "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1", + 1, action); + BuildSendAndCloseSoapResp(h, body, bodylen); + } + else + SoapError(h, 402, "Invalid Args"); + + ClearNameValueList(&data); } static void @@ -246,11 +257,32 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action) char body[sizeof(resp)+128]; int bodylen; + struct NameValueParserData data; + const char * id_str; + int id; + char *endptr; - bodylen = snprintf(body, sizeof(body), resp, - action, "urn:schemas-upnp-org:service:ConnectionManager:1", - action); - BuildSendAndCloseSoapResp(h, body, bodylen); + ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data); + id_str = GetValueFromNameValueList(&data, "ConnectionID"); + DPRINTF(E_INFO, L_HTTP, "GetCurrentConnectionInfo(%s)\n", id_str); + if(id_str) + id = strtol(id_str, &endptr, 10); + if(!id_str || !id_str[0] || endptr[0] || id != 0) + { + SoapError(h, 402, "Invalid Args"); + } + else if(id != 0) + { + SoapError(h, 701, "No such object error"); + } + else + { + bodylen = snprintf(body, sizeof(body), resp, + action, "urn:schemas-upnp-org:service:ConnectionManager:1", + action); + BuildSendAndCloseSoapResp(h, body, bodylen); + } + ClearNameValueList(&data); } static void @@ -533,6 +565,12 @@ parse_sort_criteria(char *sortCriteria, int *error) reverse = 1; item++; } + else + { + DPRINTF(E_DEBUG, L_HTTP, "No order specified [%s]\n", item); + *error = 1; + goto unhandled_order; + } if( strcasecmp(item, "upnp:class") == 0 ) { strcat(order, "o.CLASS"); @@ -556,7 +594,7 @@ parse_sort_criteria(char *sortCriteria, int *error) } else { - printf("Unhandled SortCriteria [%s]\n", item); + DPRINTF(E_DEBUG, L_HTTP, "Unhandled SortCriteria [%s]\n", item); *error = 1; if( i ) { @@ -1017,6 +1055,10 @@ callback(void *args, int argc, char **argv, char **azColName) "<dc:title>%s</dc:title>" "<upnp:class>object.%s</upnp:class>", title, class); + if( strcmp(class+10, "storageFolder") == 0 ) { + /* TODO: Implement real folder size tracking */ + ret = strcatf(str, "<upnp:storageUsed>%s</upnp:storageUsed>", (size ? size : "-1")); + } if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) { ret = strcatf(str, "<dc:creator>%s</dc:creator>", creator); } @@ -1087,10 +1129,20 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) if( (ptr = GetValueFromNameValueList(&data, "RequestedCount")) ) RequestedCount = atoi(ptr); + if( RequestedCount < 0 ) + { + SoapError(h, 402, "Invalid Args"); + goto browse_error; + } if( !RequestedCount ) RequestedCount = -1; if( (ptr = GetValueFromNameValueList(&data, "StartingIndex")) ) StartingIndex = atoi(ptr); + if( StartingIndex < 0 ) + { + SoapError(h, 402, "Invalid Args"); + goto browse_error; + } if( !BrowseFlag || (strcmp(BrowseFlag, "BrowseDirectChildren") && strcmp(BrowseFlag, "BrowseMetadata")) ) { SoapError(h, 402, "Invalid Args"); @@ -1098,7 +1150,7 @@ BrowseContentDirectory(struct upnphttp * h, const char * action) } if( !ObjectID && !(ObjectID = GetValueFromNameValueList(&data, "ContainerID")) ) { - SoapError(h, 701, "No such object error"); + SoapError(h, 402, "Invalid Args"); goto browse_error; } @@ -1293,7 +1345,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action) { if( !(ContainerID = GetValueFromNameValueList(&data, "ObjectID")) ) { - SoapError(h, 701, "No such object error"); + SoapError(h, 402, "Invalid Args"); goto search_error; } } -- cgit v1.2.3