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