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