"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "upnpsoap.c" between
minidlna-1.1.5.tar.gz and minidlna-1.2.0.tar.gz

About: ReadyMedia (formerly known as MiniDLNA) is a simple media server software, with the aim of being fully compliant with DLNA/UPnP-AV clients.

upnpsoap.c  (minidlna-1.1.5):upnpsoap.c  (minidlna-1.2.0)
/* MiniDLNA project /* MiniDLNA project
* *
* https://sourceforge.net/projects/minidlna/ * http://sourceforge.net/projects/minidlna/
* *
* MiniDLNA media server * MiniDLNA media server
* Copyright (C) 2008-2009 Justin Maggard * Copyright (C) 2008-2017 Justin Maggard
* *
* This file is part of MiniDLNA. * This file is part of MiniDLNA.
* *
* MiniDLNA is free software; you can redistribute it and/or modify * MiniDLNA is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
* *
* MiniDLNA is distributed in the hope that it will be useful, * MiniDLNA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
skipping to change at line 372 skipping to change at line 372
int bodylen; int bodylen;
bodylen = snprintf(body, sizeof(body), resp, bodylen = snprintf(body, sizeof(body), resp,
action, "urn:schemas-upnp-org:service:ConnectionManager:1 ", action, "urn:schemas-upnp-org:service:ConnectionManager:1 ",
action); action);
BuildSendAndCloseSoapResp(h, body, bodylen); BuildSendAndCloseSoapResp(h, body, bodylen);
} }
ClearNameValueList(&data); ClearNameValueList(&data);
} }
/* Standard DLNA/UPnP filter flags */ /* Standard DLNA/UPnP filter flags */
#define FILTER_CHILDCOUNT 0x00000001 #define FILTER_CHILDCOUNT 0x00000001
#define FILTER_DC_CREATOR 0x00000002 #define FILTER_DC_CREATOR 0x00000002
#define FILTER_DC_DATE 0x00000004 #define FILTER_DC_DATE 0x00000004
#define FILTER_DC_DESCRIPTION 0x00000008 #define FILTER_DC_DESCRIPTION 0x00000008
#define FILTER_DLNA_NAMESPACE 0x00000010 #define FILTER_DLNA_NAMESPACE 0x00000010
#define FILTER_REFID 0x00000020 #define FILTER_REFID 0x00000020
#define FILTER_RES 0x00000040 #define FILTER_RES 0x00000040
#define FILTER_RES_BITRATE 0x00000080 #define FILTER_RES_BITRATE 0x00000080
#define FILTER_RES_DURATION 0x00000100 #define FILTER_RES_DURATION 0x00000100
#define FILTER_RES_NRAUDIOCHANNELS 0x00000200 #define FILTER_RES_NRAUDIOCHANNELS 0x00000200
#define FILTER_RES_RESOLUTION 0x00000400 #define FILTER_RES_RESOLUTION 0x00000400
#define FILTER_RES_SAMPLEFREQUENCY 0x00000800 #define FILTER_RES_SAMPLEFREQUENCY 0x00000800
#define FILTER_RES_SIZE 0x00001000 #define FILTER_RES_SIZE 0x00001000
#define FILTER_SEARCHABLE 0x00002000 #define FILTER_SEARCHABLE 0x00002000
#define FILTER_UPNP_ACTOR 0x00004000 #define FILTER_UPNP_ACTOR 0x00004000
#define FILTER_UPNP_ALBUM 0x00008000 #define FILTER_UPNP_ALBUM 0x00008000
#define FILTER_UPNP_ALBUMARTURI 0x00010000 #define FILTER_UPNP_ALBUMARTURI 0x00010000
#define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00020000 #define FILTER_UPNP_ALBUMARTURI_DLNA_PROFILEID 0x00020000
#define FILTER_UPNP_ARTIST 0x00040000 #define FILTER_UPNP_ARTIST 0x00040000
#define FILTER_UPNP_GENRE 0x00080000 #define FILTER_UPNP_GENRE 0x00080000
#define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00100000 #define FILTER_UPNP_ORIGINALTRACKNUMBER 0x00100000
#define FILTER_UPNP_SEARCHCLASS 0x00200000 #define FILTER_UPNP_SEARCHCLASS 0x00200000
#define FILTER_UPNP_STORAGEUSED 0x00400000 #define FILTER_UPNP_STORAGEUSED 0x00400000
/* Not normally used, so leave out of the default filter */
#define FILTER_UPNP_PLAYBACKCOUNT 0x01000000
#define FILTER_UPNP_LASTPLAYBACKPOSITION 0x02000000
/* Vendor-specific filter flags */ /* Vendor-specific filter flags */
#define FILTER_SEC_CAPTION_INFO_EX 0x01000000 #define FILTER_SEC_CAPTION_INFO_EX 0x04000000
#define FILTER_SEC_DCM_INFO 0x02000000 #define FILTER_SEC_DCM_INFO 0x08000000
#define FILTER_PV_SUBTITLE_FILE_TYPE 0x04000000 #define FILTER_PV_SUBTITLE_FILE_TYPE 0x10000000
#define FILTER_PV_SUBTITLE_FILE_URI 0x08000000 #define FILTER_PV_SUBTITLE_FILE_URI 0x20000000
#define FILTER_PV_SUBTITLE 0x0C000000 #define FILTER_PV_SUBTITLE 0x30000000
#define FILTER_AV_MEDIA_CLASS 0x10000000 #define FILTER_AV_MEDIA_CLASS 0x40000000
/* Masks */
#define STANDARD_FILTER_MASK 0x00FFFFFF
#define FILTER_BOOKMARK_MASK (FILTER_UPNP_PLAYBACKCOUNT | \
FILTER_UPNP_LASTPLAYBACKPOSITION
| \
FILTER_SEC_DCM_INFO)
static uint32_t static uint32_t
set_filter_flags(char *filter, struct upnphttp *h) set_filter_flags(char *filter, struct upnphttp *h)
{ {
char *item, *saveptr = NULL; char *item, *saveptr = NULL;
uint32_t flags = 0; uint32_t flags = 0;
int samsung = h->req_client && (h->req_client->type->flags & FLAG_SAMSUNG ); int samsung = h->req_client && (h->req_client->type->flags & FLAG_SAMSUNG );
if( !filter || (strlen(filter) <= 1) ) { if( !filter || (strlen(filter) <= 1) ) {
/* Not the full 32 bits. Skip vendor-specific stuff by default. */ /* Not the full 32 bits. Skip vendor-specific stuff by default. */
flags = 0xFFFFFF; flags = STANDARD_FILTER_MASK;
if (samsung) if (samsung)
flags |= FILTER_SEC_CAPTION_INFO_EX | FILTER_SEC_DCM_INFO ; flags |= FILTER_SEC_CAPTION_INFO_EX | FILTER_SEC_DCM_INFO ;
} }
if (flags) if (flags)
return flags; return flags;
if( samsung ) if( samsung )
flags |= FILTER_DLNA_NAMESPACE; flags |= FILTER_DLNA_NAMESPACE;
item = strtok_r(filter, ",", &saveptr); item = strtok_r(filter, ",", &saveptr);
while( item != NULL ) while( item != NULL )
skipping to change at line 541 skipping to change at line 549
flags |= FILTER_RES; flags |= FILTER_RES;
flags |= FILTER_RES_SAMPLEFREQUENCY; flags |= FILTER_RES_SAMPLEFREQUENCY;
} }
else if( (strcmp(item, "res@size") == 0) || else if( (strcmp(item, "res@size") == 0) ||
(strcmp(item, "@size") == 0) || (strcmp(item, "@size") == 0) ||
(strcmp(item, "size") == 0) ) (strcmp(item, "size") == 0) )
{ {
flags |= FILTER_RES; flags |= FILTER_RES;
flags |= FILTER_RES_SIZE; flags |= FILTER_RES_SIZE;
} }
else if( strcmp(item, "upnp:playbackCount") == 0 )
{
flags |= FILTER_UPNP_PLAYBACKCOUNT;
}
else if( strcmp(item, "upnp:lastPlaybackPosition") == 0 )
{
flags |= FILTER_UPNP_LASTPLAYBACKPOSITION;
}
else if( strcmp(item, "sec:CaptionInfoEx") == 0 ) else if( strcmp(item, "sec:CaptionInfoEx") == 0 )
{ {
flags |= FILTER_SEC_CAPTION_INFO_EX; flags |= FILTER_SEC_CAPTION_INFO_EX;
} }
else if( strcmp(item, "sec:dcmInfo") == 0 ) else if( strcmp(item, "sec:dcmInfo") == 0 )
{ {
flags |= FILTER_SEC_DCM_INFO; flags |= FILTER_SEC_DCM_INFO;
} }
else if( strcmp(item, "res@pv:subtitleFileType") == 0 ) else if( strcmp(item, "res@pv:subtitleFileType") == 0 )
{ {
skipping to change at line 730 skipping to change at line 746
strcatf(args->str, "resolution=\"%s\" ", resolution); strcatf(args->str, "resolution=\"%s\" ", resolution);
} }
if( args->filter & FILTER_PV_SUBTITLE ) if( args->filter & FILTER_PV_SUBTITLE )
{ {
if( args->flags & FLAG_HAS_CAPTIONS ) if( args->flags & FLAG_HAS_CAPTIONS )
{ {
if( args->filter & FILTER_PV_SUBTITLE_FILE_TYPE ) if( args->filter & FILTER_PV_SUBTITLE_FILE_TYPE )
strcatf(args->str, "pv:subtitleFileType=\"SRT\" " ); strcatf(args->str, "pv:subtitleFileType=\"SRT\" " );
if( args->filter & FILTER_PV_SUBTITLE_FILE_URI ) if( args->filter & FILTER_PV_SUBTITLE_FILE_URI )
strcatf(args->str, "pv:subtitleFileUri=\"http://% s:%d/Captions/%s.srt\" ", strcatf(args->str, "pv:subtitleFileUri=\"http://% s:%d/Captions/%s.srt\" ",
lan_addr[args->iface].str, runtime_vars.p ort, detailID); lan_addr[args->iface].str, runtime_vars.p ort, detailID);
} }
} }
strcatf(args->str, "protocolInfo=\"http-get:*:%s:%s\"&gt;" strcatf(args->str, "protocolInfo=\"http-get:*:%s:%s\"&gt;"
"http://%s:%d/MediaItems/%s.%s" "http://%s:%d/MediaItems/%s.%s"
"&lt;/res&gt;", "&lt;/res&gt;",
mime, dlna_pn, lan_addr[args->iface].str, mime, dlna_pn, lan_addr[args->iface].str,
runtime_vars.port, detailID, ext); runtime_vars.port, detailID, ext);
} }
static int static int
skipping to change at line 886 skipping to change at line 902
title = alt_title; title = alt_title;
else else
alt_title = NULL; alt_title = NULL;
} }
/* Asus OPlay reboots with titles longer than 23 characte rs with some file types. */ /* Asus OPlay reboots with titles longer than 23 characte rs with some file types. */
else if( passed_args->client == EAsusOPlay && (passed_arg s->flags & FLAG_HAS_CAPTIONS) ) else if( passed_args->client == EAsusOPlay && (passed_arg s->flags & FLAG_HAS_CAPTIONS) )
{ {
if( strlen(title) > 23 ) if( strlen(title) > 23 )
title[23] = '\0'; title[23] = '\0';
} }
/* Hyundai hack: Only titles with a media extension get r
ecognized. */
else if( passed_args->client == EHyundaiTV )
{
ext = mime_to_ext(mime);
ret = asprintf(&alt_title, "%s.%s", title, ext);
if( ret > 0 )
title = alt_title;
else
alt_title = NULL;
}
} }
else if( *mime == 'a' ) else if( *mime == 'a' )
{ {
dlna_flags |= DLNA_FLAG_TM_S; dlna_flags |= DLNA_FLAG_TM_S;
if( strcmp(mime+6, "x-flac") == 0 ) if( strcmp(mime+6, "x-flac") == 0 )
{ {
if( passed_args->flags & FLAG_MIME_FLAC_FLAC ) if( passed_args->flags & FLAG_MIME_FLAC_FLAC )
{ {
strcpy(mime+6, "flac"); strcpy(mime+6, "flac");
} }
skipping to change at line 908 skipping to change at line 934
{ {
if( passed_args->flags & FLAG_MIME_WAV_WAV ) if( passed_args->flags & FLAG_MIME_WAV_WAV )
{ {
strcpy(mime+6, "wav"); strcpy(mime+6, "wav");
} }
} }
} }
else else
dlna_flags |= DLNA_FLAG_TM_I; dlna_flags |= DLNA_FLAG_TM_I;
if( passed_args->flags & FLAG_SKIP_DLNA_PN )
dlna_pn = NULL;
if( dlna_pn ) if( dlna_pn )
snprintf(dlna_buf, sizeof(dlna_buf), "DLNA.ORG_PN=%s;" snprintf(dlna_buf, sizeof(dlna_buf), "DLNA.ORG_PN=%s;"
"DLNA.ORG_OP=01;" "DLNA.ORG_OP=01;"
"DLNA.ORG_CI=0;" "DLNA.ORG_CI=0;"
"DLNA.ORG_FLAGS=%08X %024X", "DLNA.ORG_FLAGS=%08X %024X",
dlna_pn, dlna_flags, 0); dlna_pn, dlna_flags, 0);
else if( passed_args->flags & FLAG_DLNA ) else if( passed_args->flags & FLAG_DLNA )
snprintf(dlna_buf, sizeof(dlna_buf), "DLNA.ORG_OP=01;" snprintf(dlna_buf, sizeof(dlna_buf), "DLNA.ORG_OP=01;"
"DLNA.ORG_CI=0;" "DLNA.ORG_CI=0;"
"DLNA.ORG_FLAGS=%08X %024X", "DLNA.ORG_FLAGS=%08X %024X",
skipping to change at line 939 skipping to change at line 968
title, class); title, class);
if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) { if( comment && (passed_args->filter & FILTER_DC_DESCRIPTION) ) {
ret = strcatf(str, "&lt;dc:description&gt;%.384s&lt;/dc:d escription&gt;", comment); ret = strcatf(str, "&lt;dc:description&gt;%.384s&lt;/dc:d escription&gt;", comment);
} }
if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) { if( creator && (passed_args->filter & FILTER_DC_CREATOR) ) {
ret = strcatf(str, "&lt;dc:creator&gt;%s&lt;/dc:creator&g t;", creator); ret = strcatf(str, "&lt;dc:creator&gt;%s&lt;/dc:creator&g t;", creator);
} }
if( date && (passed_args->filter & FILTER_DC_DATE) ) { if( date && (passed_args->filter & FILTER_DC_DATE) ) {
ret = strcatf(str, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", d ate); ret = strcatf(str, "&lt;dc:date&gt;%s&lt;/dc:date&gt;", d ate);
} }
if( passed_args->filter & FILTER_SEC_DCM_INFO ) { if( (passed_args->filter & FILTER_BOOKMARK_MASK) ) {
/* Get bookmark */ /* Get bookmark */
ret = strcatf(str, "&lt;sec:dcmInfo&gt;CREATIONDATE=0,FOL int sec = sql_get_int_field(db, "SELECT SEC from BOOKMARK
DER=%s,BM=%d&lt;/sec:dcmInfo&gt;", S where ID = '%s'", detailID);
title, sql_get_int_field(db, "SELECT SEC fr if( sec > 0 && (passed_args->filter & FILTER_UPNP_LASTPLA
om BOOKMARKS where ID = '%s'", detailID)); YBACKPOSITION) ) {
/* This format is wrong according to the UPnP/AV
spec. It should be in duration format,
** so HH:MM:SS. But Kodi seems to be the only use
r of this tag, and it only works with a
** raw seconds value.
** If Kodi gets fixed, we can use duration_str(se
c * 1000) here */
ret = strcatf(str, "&lt;upnp:lastPlaybackPosition
&gt;%d&lt;/upnp:lastPlaybackPosition&gt;",
sec);
}
if( passed_args->filter & FILTER_SEC_DCM_INFO )
ret = strcatf(str, "&lt;sec:dcmInfo&gt;CREATIONDA
TE=0,FOLDER=%s,BM=%d&lt;/sec:dcmInfo&gt;",
title, sec);
if( passed_args->filter & FILTER_UPNP_PLAYBACKCOUNT ) {
ret = strcatf(str, "&lt;upnp:playbackCount&gt;%d&
lt;/upnp:playbackCount&gt;",
sql_get_int_field(db, "SELECT WATCH
_COUNT from BOOKMARKS where ID = '%s'", detailID));
}
} }
if( artist ) { if( artist ) {
if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ ACTOR) ) { if( (*mime == 'v') && (passed_args->filter & FILTER_UPNP_ ACTOR) ) {
ret = strcatf(str, "&lt;upnp:actor&gt;%s&lt;/upnp :actor&gt;", artist); ret = strcatf(str, "&lt;upnp:actor&gt;%s&lt;/upnp :actor&gt;", artist);
} }
if( passed_args->filter & FILTER_UPNP_ARTIST ) { if( passed_args->filter & FILTER_UPNP_ARTIST ) {
ret = strcatf(str, "&lt;upnp:artist&gt;%s&lt;/upn p:artist&gt;", artist); ret = strcatf(str, "&lt;upnp:artist&gt;%s&lt;/upn p:artist&gt;", artist);
} }
} }
if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) { if( album && (passed_args->filter & FILTER_UPNP_ALBUM) ) {
skipping to change at line 1058 skipping to change at line 1101
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)) strncmp(dlna_pn, "AVC_TS_HP_HD_AC3", 16) == 0))
{ {
sprintf(dlna_buf, "DLNA.ORG_PN=AV C_TS_HD_50_AC3%s", dlna_pn + 16); sprintf(dlna_buf, "DLNA.ORG_PN=AV C_TS_HD_50_AC3%s", dlna_pn + 16);
add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels, add_res(size, duration, bitrate, sampleFrequency, nrAudioChannels,
resolution, dlna_buf, mim e, detailID, ext, passed_args); resolution, dlna_buf, mim e, detailID, ext, passed_args);
} }
break; break;
case ESamsungSeriesCDE: case ESamsungSeriesCDE:
case ELGDevice: case ELGDevice:
case ELGNetCastDevice:
case EAsusOPlay: case EAsusOPlay:
default: default:
if( passed_args->flags & FLAG_HAS_CAPTION S ) if( passed_args->flags & FLAG_HAS_CAPTION S )
{ {
if( passed_args->flags & FLAG_CAP TION_RES ) if( passed_args->flags & FLAG_CAP TION_RES )
ret = strcatf(str, "&lt;r es protocolInfo=\"http-get:*:text/srt:*\"&gt;" ret = strcatf(str, "&lt;r es protocolInfo=\"http-get:*:text/srt:*\"&gt;"
"htt p://%s:%d/Captions/%s.srt" "htt p://%s:%d/Captions/%s.srt"
"&lt;/ res&gt;", "&lt;/ res&gt;",
lan_ad dr[passed_args->iface].str, runtime_vars.port, detailID); lan_ad dr[passed_args->iface].str, runtime_vars.port, detailID);
else if( passed_args->filter & FI LTER_SEC_CAPTION_INFO_EX ) if( passed_args->filter & FILTER_ SEC_CAPTION_INFO_EX )
ret = strcatf(str, "&lt;s ec:CaptionInfoEx sec:type=\"srt\"&gt;" ret = strcatf(str, "&lt;s ec:CaptionInfoEx sec:type=\"srt\"&gt;"
"htt p://%s:%d/Captions/%s.srt" "htt p://%s:%d/Captions/%s.srt"
"&lt;/ sec:CaptionInfoEx&gt;", "&lt;/ sec:CaptionInfoEx&gt;",
lan_ad dr[passed_args->iface].str, runtime_vars.port, detailID); lan_ad dr[passed_args->iface].str, runtime_vars.port, detailID);
} }
free(alt_title); free(alt_title);
break; break;
} }
} }
} }
skipping to change at line 1358 skipping to change at line 1402
} }
} }
/* If it's a DLNA client, return an error for bad sort criteria * / /* If it's a DLNA client, return an error for bad sort criteria * /
if( ret < 0 && ((args.flags & FLAG_DLNA) || GETFLAG(DLNA_STRICT_M ASK)) ) if( ret < 0 && ((args.flags & FLAG_DLNA) || GETFLAG(DLNA_STRICT_M ASK)) )
{ {
SoapError(h, 709, "Unsupported or invalid sort criteria") ; SoapError(h, 709, "Unsupported or invalid sort criteria") ;
goto browse_error; goto browse_error;
} }
sql = sqlite3_mprintf("SELECT %s, %s, %s, " COLUMNS sql = sqlite3_mprintf("SELECT %s, %s, %s, " COLUMNS
"from OBJECTS o left join DETAILS d on (d.I D = o.DETAIL_ID)" "from OBJECTS o left join DETAILS d on (d.I D = o.DETAIL_ID)"
" where %s %s limit %d, %d;", " where %s %s limit %d, %d;",
objectid_sql, parentid_sql, refid_sql, objectid_sql, parentid_sql, refid_sql,
where, THISORNUL(orderBy), StartingIndex, R equestedCount); where, THISORNUL(orderBy), StartingIndex, R equestedCount);
DPRINTF(E_DEBUG, L_HTTP, "Browse SQL: %s\n", sql); DPRINTF(E_DEBUG, L_HTTP, "Browse SQL: %s\n", sql);
ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg); ret = sqlite3_exec(db, sql, callback, (void *) &args, &zErrMsg);
} }
if( (ret != SQLITE_OK) && (zErrMsg != NULL) ) if( (ret != SQLITE_OK) && (zErrMsg != NULL) )
{ {
DPRINTF(E_WARN, L_HTTP, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql); DPRINTF(E_WARN, L_HTTP, "SQL error: %s\nBAD SQL: %s\n", zErrMsg, sql);
sqlite3_free(zErrMsg); sqlite3_free(zErrMsg);
skipping to change at line 1833 skipping to change at line 1877
buffered in memory within (or otherwise available from) the service, buffered in memory within (or otherwise available from) the service,
the service must return a SOAP fault with an errorCode of 404 Invalid Var. the service must return a SOAP fault with an errorCode of 404 Invalid Var.
QueryStateVariable remains useful as a limited test tool but may not be QueryStateVariable remains useful as a limited test tool but may not be
part of some future versions of UPnP. part of some future versions of UPnP.
*/ */
static void static void
QueryStateVariable(struct upnphttp * h, const char * action) QueryStateVariable(struct upnphttp * h, const char * action)
{ {
static const char resp[] = static const char resp[] =
"<u:%sResponse " "<u:%sResponse "
"xmlns:u=\"%s\">" "xmlns:u=\"%s\">"
"<return>%s</return>" "<return>%s</return>"
"</u:%sResponse>"; "</u:%sResponse>";
char body[512]; char body[512];
struct NameValueParserData data; struct NameValueParserData data;
const char * var_name; const char * var_name;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data, 0); ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data, 0);
/*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */ /*var_name = GetValueFromNameValueList(&data, "QueryStateVariable"); */
/*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/ /*var_name = GetValueFromNameValueListIgnoreNS(&data, "varName");*/
var_name = GetValueFromNameValueList(&data, "varName"); var_name = GetValueFromNameValueList(&data, "varName");
DPRINTF(E_INFO, L_HTTP, "QueryStateVariable(%.40s)\n", var_name); DPRINTF(E_INFO, L_HTTP, "QueryStateVariable(%.40s)\n", var_name);
if(!var_name) if(!var_name)
{ {
SoapError(h, 402, "Invalid Args"); SoapError(h, 402, "Invalid Args");
} }
else if(strcmp(var_name, "ConnectionStatus") == 0) else if(strcmp(var_name, "ConnectionStatus") == 0)
{ {
int bodylen; int bodylen;
bodylen = snprintf(body, sizeof(body), resp, bodylen = snprintf(body, sizeof(body), resp,
action, "urn:schemas-upnp-org:control-1-0", action, "urn:schemas-upnp-org:control-1-0",
"Connected", action); "Connected", action);
BuildSendAndCloseSoapResp(h, body, bodylen); BuildSendAndCloseSoapResp(h, body, bodylen);
} }
else else
{ {
DPRINTF(E_WARN, L_HTTP, "%s: Unknown: %s\n", action, THISORNUL(va r_name)); DPRINTF(E_WARN, L_HTTP, "%s: Unknown: %s\n", action, THISORNUL(va r_name));
SoapError(h, 404, "Invalid Var"); SoapError(h, 404, "Invalid Var");
} }
ClearNameValueList(&data); ClearNameValueList(&data);
} }
/* For some reason, Kodi does URI encoding and appends a trailing slash */
static void _kodi_decode(char *str)
{
while (*str)
{
switch (*str) {
case '%':
{
if (isxdigit(str[1]) && isxdigit(str[2]))
{
char x[3] = { str[1], str[2], '\0' };
*str++ = (char)strtol(x, NULL, 16);
memmove(str, str+2, strlen(str+1));
}
break;
}
case '/':
if (!str[1])
*str = '\0';
default:
str++;
break;
}
}
}
static int duration_sec(const char *str)
{
int hr, min, sec;
if (sscanf(str, "%d:%d:%d", &hr, &min, &sec) == 3)
return (hr * 3600) + (min * 60) + sec;
return atoi(str);
}
static void UpdateObject(struct upnphttp * h, const char * action)
{
static const char resp[] =
"<u:UpdateObjectResponse"
" xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
"</u:UpdateObjecResponse>";
struct NameValueParserData data;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data,
0);
char *ObjectID = GetValueFromNameValueList(&data, "ObjectID");
char *CurrentTagValue = GetValueFromNameValueList(&data, "CurrentTagValue
");
char *NewTagValue = GetValueFromNameValueList(&data, "NewTagValue");
const char *rid = ObjectID;
char tag[32], current[32], new[32];
char *item, *saveptr = NULL;
int64_t detailID;
int ret = 1;
if (!ObjectID || !CurrentTagValue || !NewTagValue)
{
SoapError(h, 402, "Invalid Args");
ClearNameValueList(&data);
return;
}
_kodi_decode(ObjectID);
DPRINTF(E_DEBUG, L_HTTP, "UpdateObject %s: %s => %s\n", ObjectID, Current
TagValue, NewTagValue);
in_magic_container(ObjectID, 0, &rid);
detailID = sql_get_int64_field(db, "SELECT DETAIL_ID from OBJECTS where O
BJECT_ID = '%q'", rid);
if (detailID <= 0)
{
SoapError(h, 701, "No such object");
ClearNameValueList(&data);
return;
}
for (item = strtok_r(CurrentTagValue, ",", &saveptr); item; item = strtok
_r(NULL, ",", &saveptr))
{
char *p;
if (sscanf(item, "&lt;%31[^&]&gt;%31[^&]", tag, current) != 2)
continue;
p = strstr(NewTagValue, tag);
if (!p || sscanf(p, "%*[^&]&gt;%31[^&]", new) != 1)
continue;
DPRINTF(E_DEBUG, L_HTTP, "Setting %s to %s\n", tag, new);
/* Kodi uses incorrect tag "upnp:playCount" instead of "upnp:play
backCount" */
if (strcmp(tag, "upnp:playbackCount") == 0 || strcmp(tag, "upnp:p
layCount") == 0)
{
//ret = sql_exec(db, "INSERT OR IGNORE into BOOKMARKS (ID
, WATCH_COUNT)"
ret = sql_exec(db, "INSERT into BOOKMARKS (ID, WATCH_COUN
T)"
" VALUES (%lld, %Q)", (long long)detai
lID, new);
if (atoi(new))
ret = sql_exec(db, "UPDATE BOOKMARKS set WATCH_CO
UNT = %Q"
" where WATCH_COUNT = %Q and I
D = %lld",
new, current, (long long)detai
lID);
else
ret = sql_exec(db, "UPDATE BOOKMARKS set WATCH_CO
UNT = 0"
" where ID = %lld", (long long
)detailID);
}
else if (strcmp(tag, "upnp:lastPlaybackPosition") == 0)
{
int sec = duration_sec(new);
if (sec < 30)
sec = 0;
else
sec -= 1;
ret = sql_exec(db, "INSERT OR IGNORE into BOOKMARKS (ID,
SEC)"
" VALUES (%lld, %d)", (long long)detai
lID, sec);
ret = sql_exec(db, "UPDATE BOOKMARKS set SEC = %d"
" where SEC = %Q and ID = %lld",
sec, current, (long long)detailID);
}
else
DPRINTF(E_WARN, L_HTTP, "Tag %s unsupported for writing\n
", tag);
}
if (ret == SQLITE_OK)
BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
else
SoapError(h, 501, "Action Failed");
ClearNameValueList(&data);
}
static void static void
SamsungGetFeatureList(struct upnphttp * h, const char * action) SamsungGetFeatureList(struct upnphttp * h, const char * action)
{ {
static const char resp[] = static const char resp[] =
"<u:X_GetFeatureListResponse xmlns:u=\"urn:schemas-upnp-org:servi ce:ContentDirectory:1\">" "<u:X_GetFeatureListResponse xmlns:u=\"urn:schemas-upnp-org:servi ce:ContentDirectory:1\">"
"<FeatureList>" "<FeatureList>"
"&lt;Features xmlns=\"urn:schemas-upnp-org:av:avs\"" "&lt;Features xmlns=\"urn:schemas-upnp-org:av:avs\""
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upn p.org/schemas/av/avs.xsd\"&gt;" " xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upn p.org/schemas/av/avs.xsd\"&gt;"
"&lt;Feature name=\"samsung.com_BASICVIEW\" version=\"1\"&gt;" "&lt;Feature name=\"samsung.com_BASICVIEW\" version=\"1\"&gt;"
skipping to change at line 1927 skipping to change at line 2095
"<u:X_SetBookmarkResponse" "<u:X_SetBookmarkResponse"
" xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">" " xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">"
"</u:X_SetBookmarkResponse>"; "</u:X_SetBookmarkResponse>";
struct NameValueParserData data; struct NameValueParserData data;
char *ObjectID, *PosSecond; char *ObjectID, *PosSecond;
ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data, 0); ParseNameValue(h->req_buf + h->req_contentoff, h->req_contentlen, &data, 0);
ObjectID = GetValueFromNameValueList(&data, "ObjectID"); ObjectID = GetValueFromNameValueList(&data, "ObjectID");
PosSecond = GetValueFromNameValueList(&data, "PosSecond"); PosSecond = GetValueFromNameValueList(&data, "PosSecond");
if( ObjectID && PosSecond ) if( ObjectID && PosSecond )
{ {
const char *rid = ObjectID;
int64_t detailID;
int sec = atoi(PosSecond);
int ret; int ret;
ret = sql_exec(db, "INSERT OR REPLACE into BOOKMARKS"
" VALUES " in_magic_container(ObjectID, 0, &rid);
"((select DETAIL_ID from OBJECTS where OBJECT_ detailID = sql_get_int64_field(db, "SELECT DETAIL_ID from OBJECTS
ID = '%q'), %q)", ObjectID, PosSecond); where OBJECT_ID = '%q'", rid);
if ( sec < 30 )
sec = 0;
ret = sql_exec(db, "INSERT OR IGNORE into BOOKMARKS (ID, SEC)"
" VALUES (%lld, %d)", (long long)detailID, sec
);
ret = sql_exec(db, "UPDATE BOOKMARKS set SEC = %d"
" where ID = %lld",
sec, (long long)detailID);
if( ret != SQLITE_OK ) 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); BuildSendAndCloseSoapResp(h, resp, sizeof(resp)-1);
} }
else else
SoapError(h, 402, "Invalid Args"); SoapError(h, 402, "Invalid Args");
ClearNameValueList(&data); ClearNameValueList(&data);
} }
static const struct static const struct
{ {
skipping to change at line 1962 skipping to change at line 2142
{ "Search", SearchContentDirectory}, { "Search", SearchContentDirectory},
{ "GetSearchCapabilities", GetSearchCapabilities}, { "GetSearchCapabilities", GetSearchCapabilities},
{ "GetSortCapabilities", GetSortCapabilities}, { "GetSortCapabilities", GetSortCapabilities},
{ "GetSystemUpdateID", GetSystemUpdateID}, { "GetSystemUpdateID", GetSystemUpdateID},
{ "GetProtocolInfo", GetProtocolInfo}, { "GetProtocolInfo", GetProtocolInfo},
{ "GetCurrentConnectionIDs", GetCurrentConnectionIDs}, { "GetCurrentConnectionIDs", GetCurrentConnectionIDs},
{ "GetCurrentConnectionInfo", GetCurrentConnectionInfo}, { "GetCurrentConnectionInfo", GetCurrentConnectionInfo},
{ "IsAuthorized", IsAuthorizedValidated}, { "IsAuthorized", IsAuthorizedValidated},
{ "IsValidated", IsAuthorizedValidated}, { "IsValidated", IsAuthorizedValidated},
{ "RegisterDevice", RegisterDevice}, { "RegisterDevice", RegisterDevice},
{ "UpdateObject", UpdateObject},
{ "X_GetFeatureList", SamsungGetFeatureList}, { "X_GetFeatureList", SamsungGetFeatureList},
{ "X_SetBookmark", SamsungSetBookmark}, { "X_SetBookmark", SamsungSetBookmark},
{ 0, 0 } { 0, 0 }
}; };
void void
ExecuteSoapAction(struct upnphttp * h, const char * action, int n) ExecuteSoapAction(struct upnphttp * h, const char * action, int n)
{ {
char * p; char * p;
 End of changes. 22 change blocks. 
48 lines changed or deleted 257 lines changed or added

Home  |  About  |  All  |  Newest  |  Fossies Dox  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTPS