1/* MiniUPnP project
2 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
3 *
4 * Copyright (c) 2006-2008, Thomas Bernard
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *     * Redistributions of source code must retain the above copyright
10 *       notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above copyright
12 *       notice, this list of conditions and the following disclaimer in the
13 *       documentation and/or other materials provided with the distribution.
14 *     * The name of the author may not be used to endorse or promote products
15 *       derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include "config.h"
34#include "getifaddr.h"
35#include "upnpdescgen.h"
36#include "minidlnapath.h"
37#include "upnpglobalvars.h"
38
39#include "upnpdescstrings.h"
40
41#undef DESC_DEBUG
42
43static const char * const upnptypes[] =
44{
45	"string",
46	"boolean",
47	"ui2",
48	"ui4",
49	"i4",
50	"uri",
51	"int",
52	"bin.base64"
53};
54
55static const char * const upnpdefaultvalues[] =
56{
57	0,
58	"Unconfigured"
59};
60
61static const char * const upnpallowedvalues[] =
62{
63	0,			/* 0 */
64	"DSL",			/* 1 */
65	"POTS",
66	"Cable",
67	"Ethernet",
68	0,
69	"Up",			/* 6 */
70	"Down",
71	"Initializing",
72	"Unavailable",
73	0,
74	"TCP",			/* 11 */
75	"UDP",
76	0,
77	"Unconfigured",		/* 14 */
78	"IP_Routed",
79	"IP_Bridged",
80	0,
81	"Unconfigured",		/* 18 */
82	"Connecting",
83	"Connected",
84	"PendingDisconnect",
85	"Disconnecting",
86	"Disconnected",
87	0,
88	"ERROR_NONE",		/* 25 */
89	0,
90	"OK",			/* 27 */
91	"ContentFormatMismatch",
92	"InsufficientBandwidth",
93	"UnreliableChannel",
94	"Unknown",
95	0,
96	"Input",		/* 33 */
97	"Output",
98	0,
99	"BrowseMetadata",	/* 36 */
100	"BrowseDirectChildren",
101	0,
102	"COMPLETED",		/* 39 */
103	"ERROR",
104	"IN_PROGRESS",
105	"STOPPED",
106	0,
107	RESOURCE_PROTOCOL_INFO_VALUES,		/* 44 */
108	0,
109	"0",			/* 46 */
110	0,
111	"",			/* 48 */
112	0
113};
114
115static const char xmlver[] =
116	"<?xml version=\"1.0\"?>\r\n";
117static const char root_service[] =
118	"scpd xmlns=\"urn:schemas-upnp-org:service-1-0\"";
119static const char root_device[] =
120	"root xmlns=\"urn:schemas-upnp-org:device-1-0\""
121#if PNPX
122	" xmlns:pnpx=\"http://schemas.microsoft.com/windows/pnpx/2005/11\""
123	" xmlns:df=\"http://schemas.microsoft.com/windows/2008/09/devicefoundation\""
124#endif
125	;
126
127/* root Description of the UPnP Device */
128static const struct XMLElt rootDesc[] =
129{
130	{root_device, INITHELPER(1,2)},
131	{"specVersion", INITHELPER(3,2)},
132	{"device", INITHELPER(5,(14+PNPX))},
133	{"/major", "1"},
134	{"/minor", "0"},
135	{"/deviceType", "urn:schemas-upnp-org:device:MediaServer:1"},
136#if PNPX == 5
137	{"/pnpx:X_hardwareId", pnpx_hwid},
138	{"/pnpx:X_compatibleId", "MS_DigitalMediaDeviceClass_DMS_V001"},
139	{"/pnpx:X_deviceCategory", "MediaDevices"},
140	{"/df:X_deviceCategory", "Multimedia.DMS"},
141	{"/microsoft:magicPacketWakeSupported xmlns:microsoft=\"urn:schemas-microsoft-com:WMPNSS-1-0\"", "0"},
142#endif
143	{"/friendlyName", friendly_name},	/* required */
144	{"/manufacturer", ROOTDEV_MANUFACTURER},		/* required */
145	{"/manufacturerURL", ROOTDEV_MANUFACTURERURL},	/* optional */
146	{"/modelDescription", ROOTDEV_MODELDESCRIPTION}, /* recommended */
147	{"/modelName", modelname},	/* required */
148	{"/modelNumber", modelnumber},
149	{"/modelURL", ROOTDEV_MODELURL},
150	{"/serialNumber", serialnumber},
151	{"/UDN", uuidvalue},	/* required */
152	{"/dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\"", "DMS-1.50"},
153	{"/presentationURL", presentationurl},	/* recommended */
154	{"iconList", INITHELPER((19+PNPX),4)},
155	{"serviceList", INITHELPER((43+PNPX),3)},
156	{"icon", INITHELPER((23+PNPX),5)},
157	{"icon", INITHELPER((28+PNPX),5)},
158	{"icon", INITHELPER((33+PNPX),5)},
159	{"icon", INITHELPER((38+PNPX),5)},
160	{"/mimetype", "image/png"},
161	{"/width", "48"},
162	{"/height", "48"},
163	{"/depth", "24"},
164	{"/url", "/icons/sm.png"},
165	{"/mimetype", "image/png"},
166	{"/width", "120"},
167	{"/height", "120"},
168	{"/depth", "24"},
169	{"/url", "/icons/lrg.png"},
170	{"/mimetype", "image/jpeg"},
171	{"/width", "48"},
172	{"/height", "48"},
173	{"/depth", "24"},
174	{"/url", "/icons/sm.jpg"},
175	{"/mimetype", "image/jpeg"},
176	{"/width", "120"},
177	{"/height", "120"},
178	{"/depth", "24"},
179	{"/url", "/icons/lrg.jpg"},
180	{"service", INITHELPER((46+PNPX),5)},
181	{"service", INITHELPER((51+PNPX),5)},
182	{"service", INITHELPER((56+PNPX),5)},
183	{"/serviceType", "urn:schemas-upnp-org:service:ContentDirectory:1"},
184	{"/serviceId", "urn:upnp-org:serviceId:ContentDirectory"},
185	{"/controlURL", CONTENTDIRECTORY_CONTROLURL},
186	{"/eventSubURL", CONTENTDIRECTORY_EVENTURL},
187	{"/SCPDURL", CONTENTDIRECTORY_PATH},
188	{"/serviceType", "urn:schemas-upnp-org:service:ConnectionManager:1"},
189	{"/serviceId", "urn:upnp-org:serviceId:ConnectionManager"},
190	{"/controlURL", CONNECTIONMGR_CONTROLURL},
191	{"/eventSubURL", CONNECTIONMGR_EVENTURL},
192	{"/SCPDURL", CONNECTIONMGR_PATH},
193	{"/serviceType", "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1"},
194	{"/serviceId", "urn:microsoft.com:serviceId:X_MS_MediaReceiverRegistrar"},
195	{"/controlURL", X_MS_MEDIARECEIVERREGISTRAR_CONTROLURL},
196	{"/eventSubURL", X_MS_MEDIARECEIVERREGISTRAR_EVENTURL},
197	{"/SCPDURL", X_MS_MEDIARECEIVERREGISTRAR_PATH},
198	{0, 0}
199};
200
201/* For ConnectionManager */
202static const struct argument GetProtocolInfoArgs[] =
203{
204	{"Source", 2, 0},
205	{"Sink", 2, 1},
206	{NULL, 0, 0}
207};
208
209static const struct argument PrepareForConnectionArgs[] =
210{
211	{"RemoteProtocolInfo", 1, 6},
212	{"PeerConnectionManager", 1, 4},
213	{"PeerConnectionID", 1, 7},
214	{"Direction", 1, 5},
215	{"ConnectionID", 2, 7},
216	{"AVTransportID", 2, 8},
217	{"RcsID", 2, 9},
218	{NULL, 0, 0}
219};
220
221static const struct argument ConnectionCompleteArgs[] =
222{
223	{"ConnectionID", 1, 7},
224	{NULL, 0, 0}
225};
226
227static const struct argument GetCurrentConnectionIDsArgs[] =
228{
229	{"ConnectionIDs", 2, 2},
230	{NULL, 0, 0}
231};
232
233static const struct argument GetCurrentConnectionInfoArgs[] =
234{
235	{"ConnectionID", 1, 7},
236	{"RcsID", 2, 9},
237	{"AVTransportID", 2, 8},
238	{"ProtocolInfo", 2, 6},
239	{"PeerConnectionManager", 2, 4},
240	{"PeerConnectionID", 2, 7},
241	{"Direction", 2, 5},
242	{"Status", 2, 3},
243	{NULL, 0, 0}
244};
245
246static const struct action ConnectionManagerActions[] =
247{
248	{"GetProtocolInfo", GetProtocolInfoArgs}, /* R */
249	//OPTIONAL {"PrepareForConnection", PrepareForConnectionArgs}, /* R */
250	//OPTIONAL {"ConnectionComplete", ConnectionCompleteArgs}, /* R */
251	{"GetCurrentConnectionIDs", GetCurrentConnectionIDsArgs}, /* R */
252	{"GetCurrentConnectionInfo", GetCurrentConnectionInfoArgs}, /* R */
253	{0, 0}
254};
255
256static const struct stateVar ConnectionManagerVars[] =
257{
258	{"SourceProtocolInfo", 0|EVENTED, 0, 0, 44}, /* required */
259	{"SinkProtocolInfo", 0|EVENTED, 0, 0, 48}, /* required */
260	{"CurrentConnectionIDs", 0|EVENTED, 0, 0, 46}, /* required */
261	{"A_ARG_TYPE_ConnectionStatus", 0, 0, 27}, /* required */
262	{"A_ARG_TYPE_ConnectionManager", 0, 0}, /* required */
263	{"A_ARG_TYPE_Direction", 0, 0, 33}, /* required */
264	{"A_ARG_TYPE_ProtocolInfo", 0, 0}, /* required */
265	{"A_ARG_TYPE_ConnectionID", 4, 0}, /* required */
266	{"A_ARG_TYPE_AVTransportID", 4, 0}, /* required */
267	{"A_ARG_TYPE_RcsID", 4, 0}, /* required */
268	{0, 0}
269};
270
271static const struct argument GetSearchCapabilitiesArgs[] =
272{
273	{"SearchCaps", 2, 10},
274	{0, 0}
275};
276
277static const struct argument GetSortCapabilitiesArgs[] =
278{
279	{"SortCaps", 2, 11},
280	{0, 0}
281};
282
283static const struct argument GetSystemUpdateIDArgs[] =
284{
285	{"Id", 2, 12},
286	{0, 0}
287};
288
289static const struct argument BrowseArgs[] =
290{
291	{"ObjectID", 1, 1},
292	{"BrowseFlag", 1, 4},
293	{"Filter", 1, 5},
294	{"StartingIndex", 1, 7},
295	{"RequestedCount", 1, 8},
296	{"SortCriteria", 1, 6},
297	{"Result", 2, 2},
298	{"NumberReturned", 2, 8},
299	{"TotalMatches", 2, 8},
300	{"UpdateID", 2, 9},
301	{0, 0}
302};
303
304static const struct argument SearchArgs[] =
305{
306	{"ContainerID", 1, 1},
307	{"SearchCriteria", 1, 3},
308	{"Filter", 1, 5},
309	{"StartingIndex", 1, 7},
310	{"RequestedCount", 1, 8},
311	{"SortCriteria", 1, 6},
312	{"Result", 2, 2},
313	{"NumberReturned", 2, 8},
314	{"TotalMatches", 2, 8},
315	{"UpdateID", 2, 9},
316	{0, 0}
317};
318
319static const struct action ContentDirectoryActions[] =
320{
321	{"GetSearchCapabilities", GetSearchCapabilitiesArgs}, /* R */
322	{"GetSortCapabilities", GetSortCapabilitiesArgs}, /* R */
323	{"GetSystemUpdateID", GetSystemUpdateIDArgs}, /* R */
324	{"Browse", BrowseArgs}, /* R */
325	{"Search", SearchArgs}, /* O */
326#if 0 // Not implementing optional features yet...
327	{"CreateObject", CreateObjectArgs}, /* O */
328	{"DestroyObject", DestroyObjectArgs}, /* O */
329	{"UpdateObject", UpdateObjectArgs}, /* O */
330	{"ImportResource", ImportResourceArgs}, /* O */
331	{"ExportResource", ExportResourceArgs}, /* O */
332	{"StopTransferResource", StopTransferResourceArgs}, /* O */
333	{"GetTransferProgress", GetTransferProgressArgs}, /* O */
334	{"DeleteResource", DeleteResourceArgs}, /* O */
335	{"CreateReference", CreateReferenceArgs}, /* O */
336#endif
337	{0, 0}
338};
339
340static const struct stateVar ContentDirectoryVars[] =
341{
342	{"TransferIDs", 0|EVENTED, 0, 0, 48}, /* 0 */
343	{"A_ARG_TYPE_ObjectID", 0, 0},
344	{"A_ARG_TYPE_Result", 0, 0},
345	{"A_ARG_TYPE_SearchCriteria", 0, 0},
346	{"A_ARG_TYPE_BrowseFlag", 0, 0, 36},
347	/* Allowed Values : BrowseMetadata / BrowseDirectChildren */
348	{"A_ARG_TYPE_Filter", 0, 0}, /* 5 */
349	{"A_ARG_TYPE_SortCriteria", 0, 0},
350	{"A_ARG_TYPE_Index", 3, 0},
351	{"A_ARG_TYPE_Count", 3, 0},
352	{"A_ARG_TYPE_UpdateID", 3, 0},
353	{"SearchCapabilities", 0, 0},
354	{"SortCapabilities", 0, 0},
355	{"SystemUpdateID", 3|EVENTED, 0, 0, 255},
356	{0, 0}
357};
358
359static const struct argument GetIsAuthorizedArgs[] =
360{
361	{"DeviceID", 1, 0},
362	{"Result", 2, 3},
363	{NULL, 0, 0}
364};
365
366static const struct argument GetIsValidatedArgs[] =
367{
368	{"DeviceID", 1, 0},
369	{"Result", 2, 3},
370	{NULL, 0, 0}
371};
372
373static const struct argument GetRegisterDeviceArgs[] =
374{
375	{"RegistrationReqMsg", 1, 1},
376	{"RegistrationRespMsg", 2, 2},
377	{NULL, 0, 0}
378};
379
380static const struct action X_MS_MediaReceiverRegistrarActions[] =
381{
382	{"IsAuthorized", GetIsAuthorizedArgs}, /* R */
383	{"IsValidated", GetIsValidatedArgs}, /* R */
384	{"RegisterDevice", GetRegisterDeviceArgs},
385	{0, 0}
386};
387
388static const struct stateVar X_MS_MediaReceiverRegistrarVars[] =
389{
390	{"A_ARG_TYPE_DeviceID", 0, 0},
391	{"A_ARG_TYPE_RegistrationReqMsg", 7, 0},
392	{"A_ARG_TYPE_RegistrationRespMsg", 7, 0},
393	{"A_ARG_TYPE_Result", 6, 0},
394	{"AuthorizationDeniedUpdateID", 3|EVENTED, 0},
395	{"AuthorizationGrantedUpdateID", 3|EVENTED, 0},
396	{"ValidationRevokedUpdateID", 3|EVENTED, 0},
397	{"ValidationSucceededUpdateID", 3|EVENTED, 0},
398	{0, 0}
399};
400
401static const struct serviceDesc scpdContentDirectory =
402{ ContentDirectoryActions, ContentDirectoryVars };
403
404static const struct serviceDesc scpdConnectionManager =
405{ ConnectionManagerActions, ConnectionManagerVars };
406
407static const struct serviceDesc scpdX_MS_MediaReceiverRegistrar =
408{ X_MS_MediaReceiverRegistrarActions, X_MS_MediaReceiverRegistrarVars };
409
410/* strcat_str()
411 * concatenate the string and use realloc to increase the
412 * memory buffer if needed. */
413static char *
414strcat_str(char * str, int * len, int * tmplen, const char * s2)
415{
416	char *p;
417	int s2len;
418	s2len = (int)strlen(s2);
419	if(*tmplen <= (*len + s2len))
420	{
421		if(s2len < 256)
422			*tmplen += 256;
423		else
424			*tmplen += s2len + 1;
425		p = realloc(str, *tmplen);
426		if (!p)
427		{
428			if(s2len < 256)
429				*tmplen -= 256;
430			else
431				*tmplen -= s2len + 1;
432			return str;
433		}
434		else
435			str = p;
436	}
437	/*strcpy(str + *len, s2); */
438	memcpy(str + *len, s2, s2len + 1);
439	*len += s2len;
440	return str;
441}
442
443/* strcat_char() :
444 * concatenate a character and use realloc to increase the
445 * size of the memory buffer if needed */
446static char *
447strcat_char(char * str, int * len, int * tmplen, char c)
448{
449	char *p;
450	if(*tmplen <= (*len + 1))
451	{
452		*tmplen += 256;
453		p = (char *)realloc(str, *tmplen);
454		if (!p)
455		{
456			*tmplen -= 256;
457			return str;
458		}
459		else
460			str = p;
461	}
462	str[*len] = c;
463	(*len)++;
464	return str;
465}
466
467/* iterative subroutine using a small stack
468 * This way, the progam stack usage is kept low */
469static char *
470genXML(char * str, int * len, int * tmplen,
471                   const struct XMLElt * p)
472{
473	uint16_t i, j, k;
474	int top;
475	const char * eltname, *s;
476	char c;
477	char element[64];
478	struct {
479		unsigned short i;
480		unsigned short j;
481		const char * eltname;
482	} pile[16]; /* stack */
483	top = -1;
484	i = 0;	/* current node */
485	j = 1;	/* i + number of nodes*/
486	for(;;)
487	{
488		eltname = p[i].eltname;
489		if(!eltname)
490			return str;
491		if(eltname[0] == '/')
492		{
493			#ifdef DESC_DEBUG
494			printf("DBG: <%s>%s<%s>\n", eltname+1, p[i].data, eltname);
495			#endif
496			str = strcat_char(str, len, tmplen, '<');
497			str = strcat_str(str, len, tmplen, eltname+1);
498			str = strcat_char(str, len, tmplen, '>');
499			str = strcat_str(str, len, tmplen, p[i].data);
500			str = strcat_char(str, len, tmplen, '<');
501			sscanf(eltname, "%s", element);
502			str = strcat_str(str, len, tmplen, element);
503			str = strcat_char(str, len, tmplen, '>');
504			for(;;)
505			{
506				if(top < 0)
507					return str;
508				i = ++(pile[top].i);
509				j = pile[top].j;
510				#ifdef DESC_DEBUG
511				printf("DBG:  pile[%d]\t%d %d\n", top, i, j);
512				#endif
513				if(i==j)
514				{
515					#ifdef DESC_DEBUG
516					printf("DBG: i==j, </%s>\n", pile[top].eltname);
517					#endif
518					str = strcat_char(str, len, tmplen, '<');
519					str = strcat_char(str, len, tmplen, '/');
520					s = pile[top].eltname;
521					for(c = *s; c > ' '; c = *(++s))
522						str = strcat_char(str, len, tmplen, c);
523					str = strcat_char(str, len, tmplen, '>');
524					top--;
525				}
526				else
527					break;
528			}
529		}
530		else
531		{
532			#ifdef DESC_DEBUG
533			printf("DBG: [%d] <%s>\n", i, eltname);
534			#endif
535			str = strcat_char(str, len, tmplen, '<');
536			str = strcat_str(str, len, tmplen, eltname);
537			str = strcat_char(str, len, tmplen, '>');
538			k = i;
539			/*i = p[k].index; */
540			/*j = i + p[k].nchild; */
541			i = (unsigned long)p[k].data & 0xffff;
542			j = i + ((unsigned long)p[k].data >> 16);
543			top++;
544			#ifdef DESC_DEBUG
545			printf("DBG: +pile[%d]\t%d %d\n", top, i, j);
546			#endif
547			pile[top].i = i;
548			pile[top].j = j;
549			pile[top].eltname = eltname;
550		}
551	}
552}
553
554/* genRootDesc() :
555 * - Generate the root description of the UPnP device.
556 * - the len argument is used to return the length of
557 *   the returned string.
558 * - tmp_uuid argument is used to build the uuid string */
559char *
560genRootDesc(int * len)
561{
562	char * str;
563	int tmplen;
564	tmplen = 2560;
565	str = (char *)malloc(tmplen);
566	if(str == NULL)
567		return NULL;
568	* len = strlen(xmlver);
569	memcpy(str, xmlver, *len + 1);
570	str = genXML(str, len, &tmplen, rootDesc);
571	str[*len] = '\0';
572	return str;
573}
574
575char *
576genRootDescSamsung(int * len)
577{
578	char * str;
579	int tmplen;
580	struct XMLElt samsungRootDesc[sizeof(rootDesc)/sizeof(struct XMLElt)];
581	tmplen = 2560;
582	str = (char *)malloc(tmplen);
583	if(str == NULL)
584		return NULL;
585	* len = strlen(xmlver);
586	memcpy(str, xmlver, *len + 1);
587	/* Replace the optional modelURL and manufacturerURL fields with Samsung foo */
588	memcpy(&samsungRootDesc, &rootDesc, sizeof(rootDesc));
589	samsungRootDesc[8+PNPX].eltname = "/sec:ProductCap";
590	samsungRootDesc[8+PNPX].data = "smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec";
591	samsungRootDesc[12+PNPX].eltname = "/sec:X_ProductCap";
592	samsungRootDesc[12+PNPX].data = "smi,DCM10,getMediaInfo.sec,getCaptionInfo.sec";
593	str = genXML(str, len, &tmplen, samsungRootDesc);
594	str[*len] = '\0';
595	return str;
596}
597
598/* genServiceDesc() :
599 * Generate service description with allowed methods and
600 * related variables. */
601static char *
602genServiceDesc(int * len, const struct serviceDesc * s)
603{
604	int i, j;
605	const struct action * acts;
606	const struct stateVar * vars;
607	const struct argument * args;
608	const char * p;
609	char * str;
610	int tmplen;
611	tmplen = 2048;
612	str = (char *)malloc(tmplen);
613	if(str == NULL)
614		return NULL;
615	/*strcpy(str, xmlver); */
616	*len = strlen(xmlver);
617	memcpy(str, xmlver, *len + 1);
618
619	acts = s->actionList;
620	vars = s->serviceStateTable;
621
622	str = strcat_char(str, len, &tmplen, '<');
623	str = strcat_str(str, len, &tmplen, root_service);
624	str = strcat_char(str, len, &tmplen, '>');
625
626	str = strcat_str(str, len, &tmplen,
627		"<specVersion><major>1</major><minor>0</minor></specVersion>");
628
629	i = 0;
630	str = strcat_str(str, len, &tmplen, "<actionList>");
631	while(acts[i].name)
632	{
633		str = strcat_str(str, len, &tmplen, "<action><name>");
634		str = strcat_str(str, len, &tmplen, acts[i].name);
635		str = strcat_str(str, len, &tmplen, "</name>");
636		/* argument List */
637		args = acts[i].args;
638		if(args)
639		{
640			str = strcat_str(str, len, &tmplen, "<argumentList>");
641			j = 0;
642			while(args[j].dir)
643			{
644				str = strcat_str(str, len, &tmplen, "<argument><name>");
645				p = vars[args[j].relatedVar].name;
646				str = strcat_str(str, len, &tmplen, (args[j].name ? args[j].name : p));
647				str = strcat_str(str, len, &tmplen, "</name><direction>");
648				str = strcat_str(str, len, &tmplen, args[j].dir==1?"in":"out");
649				str = strcat_str(str, len, &tmplen,
650						"</direction><relatedStateVariable>");
651				str = strcat_str(str, len, &tmplen, p);
652				str = strcat_str(str, len, &tmplen,
653						"</relatedStateVariable></argument>");
654				j++;
655			}
656			str = strcat_str(str, len, &tmplen,"</argumentList>");
657		}
658		str = strcat_str(str, len, &tmplen, "</action>");
659		/*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */
660		i++;
661	}
662	str = strcat_str(str, len, &tmplen, "</actionList><serviceStateTable>");
663	i = 0;
664	while(vars[i].name)
665	{
666		str = strcat_str(str, len, &tmplen,
667				"<stateVariable sendEvents=\"");
668		str = strcat_str(str, len, &tmplen, (vars[i].itype & EVENTED)?"yes":"no");
669		str = strcat_str(str, len, &tmplen, "\"><name>");
670		str = strcat_str(str, len, &tmplen, vars[i].name);
671		str = strcat_str(str, len, &tmplen, "</name><dataType>");
672		str = strcat_str(str, len, &tmplen, upnptypes[vars[i].itype & 0x0f]);
673		str = strcat_str(str, len, &tmplen, "</dataType>");
674		if(vars[i].iallowedlist)
675		{
676		  str = strcat_str(str, len, &tmplen, "<allowedValueList>");
677		  for(j=vars[i].iallowedlist; upnpallowedvalues[j]; j++)
678		  {
679		    str = strcat_str(str, len, &tmplen, "<allowedValue>");
680		    str = strcat_str(str, len, &tmplen, upnpallowedvalues[j]);
681		    str = strcat_str(str, len, &tmplen, "</allowedValue>");
682		  }
683		  str = strcat_str(str, len, &tmplen, "</allowedValueList>");
684		}
685		/*if(vars[i].defaultValue) */
686		if(vars[i].idefault)
687		{
688		  str = strcat_str(str, len, &tmplen, "<defaultValue>");
689		  /*str = strcat_str(str, len, &tmplen, vars[i].defaultValue); */
690		  str = strcat_str(str, len, &tmplen, upnpdefaultvalues[vars[i].idefault]);
691		  str = strcat_str(str, len, &tmplen, "</defaultValue>");
692		}
693		str = strcat_str(str, len, &tmplen, "</stateVariable>");
694		/*str = strcat_char(str, len, &tmplen, '\n'); // TEMP ! */
695		i++;
696	}
697	str = strcat_str(str, len, &tmplen, "</serviceStateTable></scpd>");
698	str[*len] = '\0';
699	return str;
700}
701
702/* genContentDirectory() :
703 * Generate the ContentDirectory xml description */
704char *
705genContentDirectory(int * len)
706{
707	return genServiceDesc(len, &scpdContentDirectory);
708}
709
710/* genConnectionManager() :
711 * Generate the ConnectionManager xml description */
712char *
713genConnectionManager(int * len)
714{
715	return genServiceDesc(len, &scpdConnectionManager);
716}
717
718/* genX_MS_MediaReceiverRegistrar() :
719 * Generate the X_MS_MediaReceiverRegistrar xml description */
720char *
721genX_MS_MediaReceiverRegistrar(int * len)
722{
723	return genServiceDesc(len, &scpdX_MS_MediaReceiverRegistrar);
724}
725
726static char *
727genEventVars(int * len, const struct serviceDesc * s, const char * servns)
728{
729	const struct stateVar * v;
730	char * str;
731	int tmplen;
732	char buf[512];
733	tmplen = 512;
734	str = (char *)malloc(tmplen);
735	if(str == NULL)
736		return NULL;
737	*len = 0;
738	v = s->serviceStateTable;
739	snprintf(buf, sizeof(buf), "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\" xmlns:s=\"%s\">", servns);
740	str = strcat_str(str, len, &tmplen, buf);
741	while(v->name) {
742		if(v->itype & EVENTED) {
743			snprintf(buf, sizeof(buf), "<e:property><%s>", v->name);
744			str = strcat_str(str, len, &tmplen, buf);
745			//printf("<e:property><s:%s>", v->name);
746			switch(v->ieventvalue) {
747			case 0:
748				break;
749			case 255:	/* Magical values should go around here */
750				if( strcmp(v->name, "SystemUpdateID") == 0 )
751				{
752					snprintf(buf, sizeof(buf), "%d", updateID);
753					str = strcat_str(str, len, &tmplen, buf);
754				}
755				break;
756			default:
757				str = strcat_str(str, len, &tmplen, upnpallowedvalues[v->ieventvalue]);
758				//printf("%s", upnpallowedvalues[v->ieventvalue]);
759			}
760			snprintf(buf, sizeof(buf), "</%s></e:property>", v->name);
761			str = strcat_str(str, len, &tmplen, buf);
762			//printf("</s:%s></e:property>\n", v->name);
763		}
764		v++;
765	}
766	str = strcat_str(str, len, &tmplen, "</e:propertyset>");
767	//printf("</e:propertyset>\n");
768	//printf("\n");
769	//printf("%d\n", tmplen);
770	str[*len] = '\0';
771	return str;
772}
773
774char *
775getVarsContentDirectory(int * l)
776{
777	return genEventVars(l,
778                        &scpdContentDirectory,
779	                    "urn:schemas-upnp-org:service:ContentDirectory:1");
780}
781
782char *
783getVarsConnectionManager(int * l)
784{
785	return genEventVars(l,
786                        &scpdConnectionManager,
787	                    "urn:schemas-upnp-org:service:ConnectionManager:1");
788}
789
790char *
791getVarsX_MS_MediaReceiverRegistrar(int * l)
792{
793	return genEventVars(l,
794                        &scpdX_MS_MediaReceiverRegistrar,
795	                    "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1");
796}
797
798