mirror of
https://git.code.sf.net/p/minidlna/git
synced 2025-03-30 04:08:05 +00:00
774 lines
21 KiB
C
774 lines
21 KiB
C
/* MiniUPnP project
|
|
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
|
*
|
|
* Copyright (c) 2006-2008, Thomas Bernard
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "config.h"
|
|
#include "event.h"
|
|
#include "getifaddr.h"
|
|
#include "upnpdescgen.h"
|
|
#include "minidlnapath.h"
|
|
#include "upnpglobalvars.h"
|
|
|
|
#undef DESC_DEBUG
|
|
|
|
static const char * const upnptypes[] =
|
|
{
|
|
"string",
|
|
"boolean",
|
|
"ui2",
|
|
"ui4",
|
|
"i4",
|
|
"uri",
|
|
"int",
|
|
"bin.base64"
|
|
};
|
|
|
|
static const char * const upnpdefaultvalues[] =
|
|
{
|
|
0,
|
|
"Unconfigured"
|
|
};
|
|
|
|
static const char * const upnpallowedvalues[] =
|
|
{
|
|
0, /* 0 */
|
|
"DSL", /* 1 */
|
|
"POTS",
|
|
"Cable",
|
|
"Ethernet",
|
|
0,
|
|
"Up", /* 6 */
|
|
"Down",
|
|
"Initializing",
|
|
"Unavailable",
|
|
0,
|
|
"TCP", /* 11 */
|
|
"UDP",
|
|
0,
|
|
"Unconfigured", /* 14 */
|
|
"IP_Routed",
|
|
"IP_Bridged",
|
|
0,
|
|
"Unconfigured", /* 18 */
|
|
"Connecting",
|
|
"Connected",
|
|
"PendingDisconnect",
|
|
"Disconnecting",
|
|
"Disconnected",
|
|
0,
|
|
"ERROR_NONE", /* 25 */
|
|
0,
|
|
"OK", /* 27 */
|
|
"ContentFormatMismatch",
|
|
"InsufficientBandwidth",
|
|
"UnreliableChannel",
|
|
"Unknown",
|
|
0,
|
|
"Input", /* 33 */
|
|
"Output",
|
|
0,
|
|
"BrowseMetadata", /* 36 */
|
|
"BrowseDirectChildren",
|
|
0,
|
|
"COMPLETED", /* 39 */
|
|
"ERROR",
|
|
"IN_PROGRESS",
|
|
"STOPPED",
|
|
0,
|
|
RESOURCE_PROTOCOL_INFO_VALUES, /* 44 */
|
|
0,
|
|
"0", /* 46 */
|
|
0,
|
|
"", /* 48 */
|
|
0
|
|
};
|
|
|
|
static const char xmlver[] =
|
|
"<?xml version=\"1.0\"?>\r\n";
|
|
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\"";
|
|
|
|
/* root Description of the UPnP Device */
|
|
static const struct XMLElt rootDesc[] =
|
|
{
|
|
{root_device, INITHELPER(1,2)},
|
|
{"specVersion", INITHELPER(3,2)},
|
|
{"device", INITHELPER(5,(14))},
|
|
{"/major", "1"},
|
|
{"/minor", "0"},
|
|
{"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
|
|
{"/friendlyName", friendly_name}, /* required */
|
|
{"/manufacturer", ROOTDEV_MANUFACTURER}, /* required */
|
|
{"/manufacturerURL", ROOTDEV_MANUFACTURERURL}, /* optional */
|
|
{"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
|
|
{"/modelName", modelname}, /* required */
|
|
{"/modelNumber", modelnumber},
|
|
{"/modelURL", ROOTDEV_MODELURL},
|
|
{"/serialNumber", serialnumber},
|
|
{"/UDN", uuidvalue}, /* required */
|
|
{"/dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"", "DMS-1.50"},
|
|
{"/presentationURL", presentationurl}, /* recommended */
|
|
{"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"},
|
|
{"/depth", "24"},
|
|
{"/url", "/icons/sm.png"},
|
|
{"/mimetype", "image/png"},
|
|
{"/width", "120"},
|
|
{"/height", "120"},
|
|
{"/depth", "24"},
|
|
{"/url", "/icons/lrg.png"},
|
|
{"/mimetype", "image/jpeg"},
|
|
{"/width", "48"},
|
|
{"/height", "48"},
|
|
{"/depth", "24"},
|
|
{"/url", "/icons/sm.jpg"},
|
|
{"/mimetype", "image/jpeg"},
|
|
{"/width", "120"},
|
|
{"/height", "120"},
|
|
{"/depth", "24"},
|
|
{"/url", "/icons/lrg.jpg"},
|
|
{"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},
|
|
{"/eventSubURL", CONTENTDIRECTORY_EVENTURL},
|
|
{"/SCPDURL", CONTENTDIRECTORY_PATH},
|
|
{"/serviceType", "urn:schemas-upnp-org:service:ConnectionManager:1"},
|
|
{"/serviceId", "urn:upnp-org:serviceId:ConnectionManager"},
|
|
{"/controlURL", CONNECTIONMGR_CONTROLURL},
|
|
{"/eventSubURL", CONNECTIONMGR_EVENTURL},
|
|
{"/SCPDURL", CONNECTIONMGR_PATH},
|
|
{"/serviceType", "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"},
|
|
{"/serviceId", "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"},
|
|
{"/controlURL", X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL},
|
|
{"/eventSubURL", X_MS_MEDIARECEIVERREGISTRAR_EVENTURL},
|
|
{"/SCPDURL", X_MS_MEDIARECEIVERREGISTRAR_PATH},
|
|
{0, 0}
|
|
};
|
|
|
|
/* For ConnectionManager */
|
|
static const struct argument GetProtocolInfoArgs[] =
|
|
{
|
|
{"Source", 2, 0},
|
|
{"Sink", 2, 1},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
static const struct argument GetCurrentConnectionIDsArgs[] =
|
|
{
|
|
{"ConnectionIDs", 2, 2},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
static const struct argument GetCurrentConnectionInfoArgs[] =
|
|
{
|
|
{"ConnectionID", 1, 7},
|
|
{"RcsID", 2, 9},
|
|
{"AVTransportID", 2, 8},
|
|
{"ProtocolInfo", 2, 6},
|
|
{"PeerConnectionManager", 2, 4},
|
|
{"PeerConnectionID", 2, 7},
|
|
{"Direction", 2, 5},
|
|
{"Status", 2, 3},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
static const struct action ConnectionManagerActions[] =
|
|
{
|
|
{"GetProtocolInfo", GetProtocolInfoArgs}, /* R */
|
|
{"GetCurrentConnectionIDs", GetCurrentConnectionIDsArgs}, /* R */
|
|
{"GetCurrentConnectionInfo", GetCurrentConnectionInfoArgs}, /* R */
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct stateVar ConnectionManagerVars[] =
|
|
{
|
|
{"SourceProtocolInfo", 0|EVENTED, 0, 0, 44}, /* required */
|
|
{"SinkProtocolInfo", 0|EVENTED, 0, 0, 48}, /* required */
|
|
{"CurrentConnectionIDs", 0|EVENTED, 0, 0, 46}, /* required */
|
|
{"A_ARG_TYPE_ConnectionStatus", 0, 0, 27}, /* required */
|
|
{"A_ARG_TYPE_ConnectionManager", 0, 0}, /* required */
|
|
{"A_ARG_TYPE_Direction", 0, 0, 33}, /* required */
|
|
{"A_ARG_TYPE_ProtocolInfo", 0, 0}, /* required */
|
|
{"A_ARG_TYPE_ConnectionID", 4, 0}, /* required */
|
|
{"A_ARG_TYPE_AVTransportID", 4, 0}, /* required */
|
|
{"A_ARG_TYPE_RcsID", 4, 0}, /* required */
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument GetSearchCapabilitiesArgs[] =
|
|
{
|
|
{"SearchCaps", 2, 11},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument GetSortCapabilitiesArgs[] =
|
|
{
|
|
{"SortCaps", 2, 12},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument GetSystemUpdateIDArgs[] =
|
|
{
|
|
{"Id", 2, 13},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument UpdateObjectArgs[] =
|
|
{
|
|
{"ObjectID", 1, 1},
|
|
{"CurrentTagValue", 1, 10},
|
|
{"NewTagValue", 1, 10},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument BrowseArgs[] =
|
|
{
|
|
{"ObjectID", 1, 1},
|
|
{"BrowseFlag", 1, 4},
|
|
{"Filter", 1, 5},
|
|
{"StartingIndex", 1, 7},
|
|
{"RequestedCount", 1, 8},
|
|
{"SortCriteria", 1, 6},
|
|
{"Result", 2, 2},
|
|
{"NumberReturned", 2, 8},
|
|
{"TotalMatches", 2, 8},
|
|
{"UpdateID", 2, 9},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument SearchArgs[] =
|
|
{
|
|
{"ContainerID", 1, 1},
|
|
{"SearchCriteria", 1, 3},
|
|
{"Filter", 1, 5},
|
|
{"StartingIndex", 1, 7},
|
|
{"RequestedCount", 1, 8},
|
|
{"SortCriteria", 1, 6},
|
|
{"Result", 2, 2},
|
|
{"NumberReturned", 2, 8},
|
|
{"TotalMatches", 2, 8},
|
|
{"UpdateID", 2, 9},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct action ContentDirectoryActions[] =
|
|
{
|
|
{"GetSearchCapabilities", GetSearchCapabilitiesArgs}, /* R */
|
|
{"GetSortCapabilities", GetSortCapabilitiesArgs}, /* R */
|
|
{"GetSystemUpdateID", GetSystemUpdateIDArgs}, /* R */
|
|
{"Browse", BrowseArgs}, /* R */
|
|
{"Search", SearchArgs}, /* O */
|
|
{"UpdateObject", UpdateObjectArgs}, /* O */
|
|
#if 0 // Not implementing optional features yet...
|
|
{"CreateObject", CreateObjectArgs}, /* O */
|
|
{"DestroyObject", DestroyObjectArgs}, /* O */
|
|
{"ImportResource", ImportResourceArgs}, /* O */
|
|
{"ExportResource", ExportResourceArgs}, /* O */
|
|
{"StopTransferResource", StopTransferResourceArgs}, /* O */
|
|
{"GetTransferProgress", GetTransferProgressArgs}, /* O */
|
|
{"DeleteResource", DeleteResourceArgs}, /* O */
|
|
{"CreateReference", CreateReferenceArgs}, /* O */
|
|
#endif
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct stateVar ContentDirectoryVars[] =
|
|
{
|
|
{"TransferIDs", 0|EVENTED, 0, 0, 48}, /* 0 */
|
|
{"A_ARG_TYPE_ObjectID", 0, 0},
|
|
{"A_ARG_TYPE_Result", 0, 0},
|
|
{"A_ARG_TYPE_SearchCriteria", 0, 0},
|
|
{"A_ARG_TYPE_BrowseFlag", 0, 0, 36},
|
|
/* Allowed Values : BrowseMetadata / BrowseDirectChildren */
|
|
{"A_ARG_TYPE_Filter", 0, 0}, /* 5 */
|
|
{"A_ARG_TYPE_SortCriteria", 0, 0},
|
|
{"A_ARG_TYPE_Index", 3, 0},
|
|
{"A_ARG_TYPE_Count", 3, 0},
|
|
{"A_ARG_TYPE_UpdateID", 3, 0},
|
|
{"A_ARG_TYPE_TagValueList", 0, 0},
|
|
{"SearchCapabilities", 0, 0},
|
|
{"SortCapabilities", 0, 0},
|
|
{"SystemUpdateID", 3|EVENTED, 0, 0, 255},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct argument GetIsAuthorizedArgs[] =
|
|
{
|
|
{"DeviceID", 1, 0},
|
|
{"Result", 2, 3},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
static const struct argument GetIsValidatedArgs[] =
|
|
{
|
|
{"DeviceID", 1, 0},
|
|
{"Result", 2, 3},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
static const struct argument GetRegisterDeviceArgs[] =
|
|
{
|
|
{"RegistrationReqMsg", 1, 1},
|
|
{"RegistrationRespMsg", 2, 2},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
static const struct action X_MS_MediaReceiverRegistrarActions[] =
|
|
{
|
|
{"IsAuthorized", GetIsAuthorizedArgs}, /* R */
|
|
{"IsValidated", GetIsValidatedArgs}, /* R */
|
|
{"RegisterDevice", GetRegisterDeviceArgs},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct stateVar X_MS_MediaReceiverRegistrarVars[] =
|
|
{
|
|
{"A_ARG_TYPE_DeviceID", 0, 0},
|
|
{"A_ARG_TYPE_RegistrationReqMsg", 7, 0},
|
|
{"A_ARG_TYPE_RegistrationRespMsg", 7, 0},
|
|
{"A_ARG_TYPE_Result", 6, 0},
|
|
{"AuthorizationDeniedUpdateID", 3|EVENTED, 0},
|
|
{"AuthorizationGrantedUpdateID", 3|EVENTED, 0},
|
|
{"ValidationRevokedUpdateID", 3|EVENTED, 0},
|
|
{"ValidationSucceededUpdateID", 3|EVENTED, 0},
|
|
{0, 0}
|
|
};
|
|
|
|
static const struct serviceDesc scpdContentDirectory =
|
|
{ ContentDirectoryActions, ContentDirectoryVars };
|
|
|
|
static const struct serviceDesc scpdConnectionManager =
|
|
{ ConnectionManagerActions, ConnectionManagerVars };
|
|
|
|
static const struct serviceDesc scpdX_MS_MediaReceiverRegistrar =
|
|
{ X_MS_MediaReceiverRegistrarActions, X_MS_MediaReceiverRegistrarVars };
|
|
|
|
/* strcat_str()
|
|
* concatenate the string and use realloc to increase the
|
|
* memory buffer if needed. */
|
|
static char *
|
|
strcat_str(char * str, int * len, int * tmplen, const char * s2)
|
|
{
|
|
char *p;
|
|
int s2len;
|
|
s2len = (int)strlen(s2);
|
|
if(*tmplen <= (*len + s2len))
|
|
{
|
|
if(s2len < 256)
|
|
*tmplen += 256;
|
|
else
|
|
*tmplen += s2len + 1;
|
|
p = realloc(str, *tmplen);
|
|
if (!p)
|
|
{
|
|
if(s2len < 256)
|
|
*tmplen -= 256;
|
|
else
|
|
*tmplen -= s2len + 1;
|
|
return str;
|
|
}
|
|
else
|
|
str = p;
|
|
}
|
|
/*strcpy(str + *len, s2); */
|
|
memcpy(str + *len, s2, s2len + 1);
|
|
*len += s2len;
|
|
return str;
|
|
}
|
|
|
|
/* strcat_char() :
|
|
* concatenate a character and use realloc to increase the
|
|
* size of the memory buffer if needed */
|
|
static char *
|
|
strcat_char(char * str, int * len, int * tmplen, char c)
|
|
{
|
|
char *p;
|
|
if(*tmplen <= (*len + 1))
|
|
{
|
|
*tmplen += 256;
|
|
p = (char *)realloc(str, *tmplen);
|
|
if (!p)
|
|
{
|
|
*tmplen -= 256;
|
|
return str;
|
|
}
|
|
else
|
|
str = p;
|
|
}
|
|
str[*len] = c;
|
|
(*len)++;
|
|
return str;
|
|
}
|
|
|
|
/* iterative subroutine using a small stack
|
|
* This way, the progam stack usage is kept low */
|
|
static char *
|
|
genXML(char * str, int * len, int * tmplen,
|
|
const struct XMLElt * p)
|
|
{
|
|
uint16_t i, j, k;
|
|
int top;
|
|
const char * eltname, *s;
|
|
char c;
|
|
char element[64];
|
|
struct {
|
|
unsigned short i;
|
|
unsigned short j;
|
|
const char * eltname;
|
|
} pile[16]; /* stack */
|
|
top = -1;
|
|
i = 0; /* current node */
|
|
j = 1; /* i + number of nodes*/
|
|
for(;;)
|
|
{
|
|
eltname = p[i].eltname;
|
|
if(!eltname)
|
|
return str;
|
|
if(eltname[0] == '/')
|
|
{
|
|
#ifdef DESC_DEBUG
|
|
printf("DBG: <%s>%s<%s>\n", eltname+1, p[i].data, eltname);
|
|
#endif
|
|
str = strcat_char(str, len, tmplen, '<');
|
|
str = strcat_str(str, len, tmplen, eltname+1);
|
|
str = strcat_char(str, len, tmplen, '>');
|
|
str = strcat_str(str, len, tmplen, p[i].data);
|
|
str = strcat_char(str, len, tmplen, '<');
|
|
sscanf(eltname, "%s", element);
|
|
str = strcat_str(str, len, tmplen, element);
|
|
str = strcat_char(str, len, tmplen, '>');
|
|
for(;;)
|
|
{
|
|
if(top < 0)
|
|
return str;
|
|
i = ++(pile[top].i);
|
|
j = pile[top].j;
|
|
#ifdef DESC_DEBUG
|
|
printf("DBG: pile[%d]\t%d %d\n", top, i, j);
|
|
#endif
|
|
if(i==j)
|
|
{
|
|
#ifdef DESC_DEBUG
|
|
printf("DBG: i==j, </%s>\n", pile[top].eltname);
|
|
#endif
|
|
str = strcat_char(str, len, tmplen, '<');
|
|
str = strcat_char(str, len, tmplen, '/');
|
|
s = pile[top].eltname;
|
|
for(c = *s; c > ' '; c = *(++s))
|
|
str = strcat_char(str, len, tmplen, c);
|
|
str = strcat_char(str, len, tmplen, '>');
|
|
top--;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef DESC_DEBUG
|
|
printf("DBG: [%d] <%s>\n", i, eltname);
|
|
#endif
|
|
str = strcat_char(str, len, tmplen, '<');
|
|
str = strcat_str(str, len, tmplen, eltname);
|
|
str = strcat_char(str, len, tmplen, '>');
|
|
k = i;
|
|
/*i = p[k].index; */
|
|
/*j = i + p[k].nchild; */
|
|
i = (unsigned long)p[k].data & 0xffff;
|
|
j = i + ((unsigned long)p[k].data >> 16);
|
|
top++;
|
|
#ifdef DESC_DEBUG
|
|
printf("DBG: +pile[%d]\t%d %d\n", top, i, j);
|
|
#endif
|
|
pile[top].i = i;
|
|
pile[top].j = j;
|
|
pile[top].eltname = eltname;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* genRootDesc() :
|
|
* - Generate the root description of the UPnP device.
|
|
* - the len argument is used to return the length of
|
|
* the returned string.
|
|
* - tmp_uuid argument is used to build the uuid string */
|
|
char *
|
|
genRootDesc(int * len)
|
|
{
|
|
char * str;
|
|
int tmplen;
|
|
tmplen = 2560;
|
|
str = (char *)malloc(tmplen);
|
|
if(str == NULL)
|
|
return NULL;
|
|
* len = strlen(xmlver);
|
|
memcpy(str, xmlver, *len + 1);
|
|
str = genXML(str, len, &tmplen, rootDesc);
|
|
str[*len] = '\0';
|
|
return str;
|
|
}
|
|
|
|
char *
|
|
genRootDescSamsung(int * len)
|
|
{
|
|
char * str;
|
|
int tmplen;
|
|
struct XMLElt samsungRootDesc[sizeof(rootDesc)/sizeof(struct XMLElt)];
|
|
tmplen = 2560;
|
|
str = (char *)malloc(tmplen);
|
|
if(str == NULL)
|
|
return NULL;
|
|
* len = strlen(xmlver);
|
|
memcpy(str, xmlver, *len + 1);
|
|
/* Replace the optional modelURL and manufacturerURL fields with Samsung foo */
|
|
memcpy(&samsungRootDesc, &rootDesc, sizeof(rootDesc));
|
|
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;
|
|
}
|
|
|
|
/* genServiceDesc() :
|
|
* Generate service description with allowed methods and
|
|
* related variables. */
|
|
static char *
|
|
genServiceDesc(int * len, const struct serviceDesc * s)
|
|
{
|
|
int i, j;
|
|
const struct action * acts;
|
|
const struct stateVar * vars;
|
|
const struct argument * args;
|
|
const char * p;
|
|
char * str;
|
|
int tmplen;
|
|
tmplen = 2048;
|
|
str = (char *)malloc(tmplen);
|
|
if(str == NULL)
|
|
return NULL;
|
|
/*strcpy(str, xmlver); */
|
|
*len = strlen(xmlver);
|
|
memcpy(str, xmlver, *len + 1);
|
|
|
|
acts = s->actionList;
|
|
vars = s->serviceStateTable;
|
|
|
|
str = strcat_char(str, len, &tmplen, '<');
|
|
str = strcat_str(str, len, &tmplen, root_service);
|
|
str = strcat_char(str, len, &tmplen, '>');
|
|
|
|
str = strcat_str(str, len, &tmplen,
|
|
"<specVersion><major>1</major><minor>0</minor></specVersion>");
|
|
|
|
i = 0;
|
|
str = strcat_str(str, len, &tmplen, "<actionList>");
|
|
while(acts[i].name)
|
|
{
|
|
str = strcat_str(str, len, &tmplen, "<action><name>");
|
|
str = strcat_str(str, len, &tmplen, acts[i].name);
|
|
str = strcat_str(str, len, &tmplen, "</name>");
|
|
/* argument List */
|
|
args = acts[i].args;
|
|
if(args)
|
|
{
|
|
str = strcat_str(str, len, &tmplen, "<argumentList>");
|
|
j = 0;
|
|
while(args[j].dir)
|
|
{
|
|
str = strcat_str(str, len, &tmplen, "<argument><name>");
|
|
p = vars[args[j].relatedVar].name;
|
|
str = strcat_str(str, len, &tmplen, (args[j].name ? args[j].name : p));
|
|
str = strcat_str(str, len, &tmplen, "</name><direction>");
|
|
str = strcat_str(str, len, &tmplen, args[j].dir==1?"in":"out");
|
|
str = strcat_str(str, len, &tmplen,
|
|
"</direction><relatedStateVariable>");
|
|
str = strcat_str(str, len, &tmplen, p);
|
|
str = strcat_str(str, len, &tmplen,
|
|
"</relatedStateVariable></argument>");
|
|
j++;
|
|
}
|
|
str = strcat_str(str, len, &tmplen,"</argumentList>");
|
|
}
|
|
str = strcat_str(str, len, &tmplen, "</action>");
|
|
/*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */
|
|
i++;
|
|
}
|
|
str = strcat_str(str, len, &tmplen, "</actionList><serviceStateTable>");
|
|
i = 0;
|
|
while(vars[i].name)
|
|
{
|
|
str = strcat_str(str, len, &tmplen,
|
|
"<stateVariable sendEvents=\"");
|
|
str = strcat_str(str, len, &tmplen, (vars[i].itype & EVENTED)?"yes":"no");
|
|
str = strcat_str(str, len, &tmplen, "\"><name>");
|
|
str = strcat_str(str, len, &tmplen, vars[i].name);
|
|
str = strcat_str(str, len, &tmplen, "</name><dataType>");
|
|
str = strcat_str(str, len, &tmplen, upnptypes[vars[i].itype & 0x0f]);
|
|
str = strcat_str(str, len, &tmplen, "</dataType>");
|
|
if(vars[i].iallowedlist)
|
|
{
|
|
str = strcat_str(str, len, &tmplen, "<allowedValueList>");
|
|
for(j=vars[i].iallowedlist; upnpallowedvalues[j]; j++)
|
|
{
|
|
str = strcat_str(str, len, &tmplen, "<allowedValue>");
|
|
str = strcat_str(str, len, &tmplen, upnpallowedvalues[j]);
|
|
str = strcat_str(str, len, &tmplen, "</allowedValue>");
|
|
}
|
|
str = strcat_str(str, len, &tmplen, "</allowedValueList>");
|
|
}
|
|
/*if(vars[i].defaultValue) */
|
|
if(vars[i].idefault)
|
|
{
|
|
str = strcat_str(str, len, &tmplen, "<defaultValue>");
|
|
/*str = strcat_str(str, len, &tmplen, vars[i].defaultValue); */
|
|
str = strcat_str(str, len, &tmplen, upnpdefaultvalues[vars[i].idefault]);
|
|
str = strcat_str(str, len, &tmplen, "</defaultValue>");
|
|
}
|
|
str = strcat_str(str, len, &tmplen, "</stateVariable>");
|
|
/*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */
|
|
i++;
|
|
}
|
|
str = strcat_str(str, len, &tmplen, "</serviceStateTable></scpd>");
|
|
str[*len] = '\0';
|
|
return str;
|
|
}
|
|
|
|
/* genContentDirectory() :
|
|
* Generate the ContentDirectory xml description */
|
|
char *
|
|
genContentDirectory(int * len)
|
|
{
|
|
return genServiceDesc(len, &scpdContentDirectory);
|
|
}
|
|
|
|
/* genConnectionManager() :
|
|
* Generate the ConnectionManager xml description */
|
|
char *
|
|
genConnectionManager(int * len)
|
|
{
|
|
return genServiceDesc(len, &scpdConnectionManager);
|
|
}
|
|
|
|
/* genX_MS_MediaReceiverRegistrar() :
|
|
* Generate the X_MS_MediaReceiverRegistrar xml description */
|
|
char *
|
|
genX_MS_MediaReceiverRegistrar(int * len)
|
|
{
|
|
return genServiceDesc(len, &scpdX_MS_MediaReceiverRegistrar);
|
|
}
|
|
|
|
static char *
|
|
genEventVars(int * len, const struct serviceDesc * s, const char * servns)
|
|
{
|
|
const struct stateVar * v;
|
|
char * str;
|
|
int tmplen;
|
|
char buf[512];
|
|
tmplen = 512;
|
|
str = (char *)malloc(tmplen);
|
|
if(str == NULL)
|
|
return NULL;
|
|
*len = 0;
|
|
v = s->serviceStateTable;
|
|
snprintf(buf, sizeof(buf), "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\" xmlns:s=\"%s\">", servns);
|
|
str = strcat_str(str, len, &tmplen, buf);
|
|
while(v->name) {
|
|
if(v->itype & EVENTED) {
|
|
snprintf(buf, sizeof(buf), "<e:property><%s>", v->name);
|
|
str = strcat_str(str, len, &tmplen, buf);
|
|
//printf("<e:property><s:%s>", v->name);
|
|
switch(v->ieventvalue) {
|
|
case 0:
|
|
break;
|
|
case 255: /* Magical values should go around here */
|
|
if( strcmp(v->name, "SystemUpdateID") == 0 )
|
|
{
|
|
snprintf(buf, sizeof(buf), "%d", updateID);
|
|
str = strcat_str(str, len, &tmplen, buf);
|
|
}
|
|
break;
|
|
default:
|
|
str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]);
|
|
//printf("%s", upnpallowedvalues[v->ieventvalue]);
|
|
}
|
|
snprintf(buf, sizeof(buf), "</%s></e:property>", v->name);
|
|
str = strcat_str(str, len, &tmplen, buf);
|
|
//printf("</s:%s></e:property>\n", v->name);
|
|
}
|
|
v++;
|
|
}
|
|
str = strcat_str(str, len, &tmplen, "</e:propertyset>");
|
|
//printf("</e:propertyset>\n");
|
|
//printf("\n");
|
|
//printf("%d\n", tmplen);
|
|
str[*len] = '\0';
|
|
return str;
|
|
}
|
|
|
|
char *
|
|
getVarsContentDirectory(int * l)
|
|
{
|
|
return genEventVars(l,
|
|
&scpdContentDirectory,
|
|
"urn:schemas-upnp-org:service:ContentDirectory:1");
|
|
}
|
|
|
|
char *
|
|
getVarsConnectionManager(int * l)
|
|
{
|
|
return genEventVars(l,
|
|
&scpdConnectionManager,
|
|
"urn:schemas-upnp-org:service:ConnectionManager:1");
|
|
}
|
|
|
|
char *
|
|
getVarsX_MS_MediaReceiverRegistrar(int * l)
|
|
{
|
|
return genEventVars(l,
|
|
&scpdX_MS_MediaReceiverRegistrar,
|
|
"urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1");
|
|
}
|
|
|