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