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