diff --git a/upnphttp.c b/upnphttp.c index 7de4e7a..fa0f06e 100644 --- a/upnphttp.c +++ b/upnphttp.c @@ -388,6 +388,11 @@ ParseHttpHeaders(struct upnphttp * h) } } } + else if(strncasecmp(line, "uctt.upnp.org:", 14)==0) + { + /* Conformance testing */ + SETFLAG(DLNA_STRICT_MASK); + } } next_header: while(!(line[0] == '\r' && line[1] == '\n')) @@ -1622,7 +1627,9 @@ SendResp_resizedimg(struct upnphttp * h, char * object) dstw = (((height<<10)/srch) * srcw>>10); } - if( dstw <= 640 && dsth <= 480 ) + if( dstw <= 160 && dsth <= 160 ) + strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_TN;"); + else if( dstw <= 640 && dsth <= 480 ) strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_SM;"); else if( dstw <= 1024 && dsth <= 768 ) strcpy(dlna_pn, "DLNA.ORG_PN=JPEG_MED;"); diff --git a/upnpsoap.c b/upnpsoap.c index 222a5f1..3b76aae 100644 --- a/upnpsoap.c +++ b/upnpsoap.c @@ -306,15 +306,16 @@ GetCurrentConnectionInfo(struct upnphttp * h, const char * action) #define FILTER_RES_RESOLUTION 0x00000400 #define FILTER_RES_SAMPLEFREQUENCY 0x00000800 #define FILTER_RES_SIZE 0x00001000 -#define FILTER_UPNP_ACTOR 0x00002000 -#define FILTER_UPNP_ALBUM 0x00004000 -#define FILTER_UPNP_ALBUMARTURI 0x00008000 -#define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00010000 -#define FILTER_UPNP_ARTIST 0x00020000 -#define FILTER_UPNP_GENRE 0x00040000 -#define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00080000 -#define FILTER_UPNP_SEARCHCLASS 0x00100000 -#define FILTER_UPNP_STORAGEUSED 0x00200000 +#define FILTER_SEARCHABLE 0x00002000 +#define FILTER_UPNP_ACTOR 0x00004000 +#define FILTER_UPNP_ALBUM 0x00008000 +#define FILTER_UPNP_ALBUMARTURI 0x00010000 +#define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00020000 +#define FILTER_UPNP_ARTIST 0x00040000 +#define FILTER_UPNP_GENRE 0x00080000 +#define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00100000 +#define FILTER_UPNP_SEARCHCLASS 0x00200000 +#define FILTER_UPNP_STORAGEUSED 0x00400000 /* Vendor-specific filter flags */ #define FILTER_SEC_CAPTION_INFO_EX 0x01000000 #define FILTER_SEC_DCM_INFO 0x02000000 @@ -345,6 +346,10 @@ set_filter_flags(char *filter, struct upnphttp *h) { flags |= FILTER_CHILDCOUNT; } + else if( strcmp(item, "@searchable") == 0 ) + { + flags |= FILTER_SEARCHABLE; + } else if( strcmp(item, "dc:creator") == 0 ) { flags |= FILTER_DC_CREATOR; @@ -989,30 +994,27 @@ callback(void *args, int argc, char **argv, char **azColName) else if( strncmp(class, "container", 9) == 0 ) { ret = strcatf(str, "<container id=\"%s\" parentID=\"%s\" restricted=\"1\" ", id, parent); - if( passed_args->filter & FILTER_CHILDCOUNT ) - { + if( passed_args->filter & FILTER_SEARCHABLE ) { + ret = strcatf(str, "searchable=\"1\" "); + } + 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 = 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) ) - { - ret = strcatf(str, " searchable=\"1\""); - if( passed_args->filter & FILTER_UPNP_SEARCHCLASS ) - { - 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"); - } + if( passed_args->requested == 1 && strcmp(id, "0") == 0 && (passed_args->filter & FILTER_UPNP_SEARCHCLASS) ) { + 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 = strcatf(str, ">" "<dc:title>%s</dc:title>" "<upnp:class>object.%s</upnp:class>", title, class); - if( (passed_args->filter & FILTER_UPNP_STORAGEUSED) && strcmp(class+10, "storageFolder") == 0 ) { + if( (passed_args->filter & FILTER_UPNP_STORAGEUSED) || strcmp(class+10, "storageFolder") == 0 ) { /* TODO: Implement real folder size tracking */ ret = strcatf(str, "<upnp:storageUsed>%s</upnp:storageUsed>", (size ? size : "-1")); } @@ -1331,6 +1333,12 @@ parse_search_criteria(const char *str) case 'o': if (strncmp(s, "object.", 7) == 0) s += 7; + else if (strncmp(s, "object\"", 7) == 0 || + strncmp(s, "object"", 12) == 0) + { + s += 6; + continue; + } default: charcat(&criteria, *s); break; @@ -1498,6 +1506,16 @@ parse_search_criteria(const char *str) else charcat(&criteria, *s); break; + case '(': + if (s > str && !isspace(s[-1])) + charcat(&criteria, ' '); + charcat(&criteria, *s); + break; + case ')': + charcat(&criteria, *s); + if (!isspace(s[1])) + charcat(&criteria, ' '); + break; default: charcat(&criteria, *s); break; @@ -1526,7 +1544,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action) int totalMatches; int ret; char *ContainerID, *Filter, *SearchCriteria, *SortCriteria; - char *orderBy = NULL; + char *orderBy = NULL, *where = NULL; char groupBy[] = "group by DETAIL_ID"; struct NameValueParserData data; int RequestedCount = 0; @@ -1604,8 +1622,11 @@ SearchContentDirectory(struct upnphttp * h, const char * action) else if( strcmp(ContainerID, MUSIC_ALL_ID) == 0 ) groupBy[0] = '\0'; - SearchCriteria = parse_search_criteria(SearchCriteria); - DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", SearchCriteria); + if( GETFLAG(DLNA_STRICT_MASK) ) + groupBy[0] = '\0'; + + where = parse_search_criteria(SearchCriteria); + DPRINTF(E_DEBUG, L_HTTP, "Translated SearchCriteria: %s\n", where); totalMatches = sql_get_int_field(db, "SELECT (select count(distinct DETAIL_ID)" " from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" @@ -1613,7 +1634,7 @@ SearchContentDirectory(struct upnphttp * h, const char * action) " + " "(select count(*) from OBJECTS o left join DETAILS d on (o.DETAIL_ID = d.ID)" " where (OBJECT_ID = '%q') and (%s))", - ContainerID, SearchCriteria, ContainerID, SearchCriteria); + ContainerID, where, ContainerID, where); if( totalMatches < 0 ) { /* Must be invalid SQL, so most likely bad or unhandled search criteria. */ @@ -1648,11 +1669,11 @@ SearchContentDirectory(struct upnphttp * h, const char * action) " where OBJECT_ID glob '%q$*' and (%s) %s " "%z %s" " limit %d, %d", - ContainerID, SearchCriteria, groupBy, + ContainerID, where, groupBy, (*ContainerID == '*') ? NULL : sqlite3_mprintf("UNION ALL " SELECT_COLUMNS "from OBJECTS o left join DETAILS d on (d.ID = o.DETAIL_ID)" - " where OBJECT_ID = '%q' and (%s) ", ContainerID, SearchCriteria), + " where OBJECT_ID = '%q' and (%s) ", ContainerID, where), orderBy, StartingIndex, RequestedCount); DPRINTF(E_DEBUG, L_HTTP, "Search SQL: %s\n", sql); ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg); @@ -1674,7 +1695,7 @@ search_error: if( args.flags & FLAG_FREE_OBJECT_ID ) sqlite3_free(ContainerID); free(orderBy); - free(SearchCriteria); + free(where); free(str.data); }