pupnp (libupnp) snapshot from SourceForge: git clone git://pupnp.git.sourceforge...
[igd2-for-linux:pandonghui1211s-igd2-for-linux.git] / pupnp_branch-1.6.x / upnp / sample / common / sample_util.c
1 /*******************************************************************************
2  *
3  * Copyright (c) 2000-2003 Intel Corporation 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions are met: 
8  *
9  * - Redistributions of source code must retain the above copyright notice, 
10  * this list of conditions and the following disclaimer. 
11  * - Redistributions in binary form must reproduce the above copyright notice, 
12  * this list of conditions and the following disclaimer in the documentation 
13  * and/or other materials provided with the distribution. 
14  * - Neither name of Intel Corporation nor the names of its contributors 
15  * may be used to endorse or promote products derived from this software 
16  * without specific prior written permission.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR 
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  ******************************************************************************/
31
32
33 #include "sample_util.h"
34
35
36 #include <stdarg.h>
37 #include <stdio.h>
38
39
40 #if !UPNP_HAVE_TOOLS
41 #       error "Need upnptools.h to compile samples ; try ./configure --enable-tools"
42 #endif
43
44
45 /*
46    Function pointer to use for displaying formatted
47    strings. Set on Initialization of device. 
48  */
49 int initialize = 1;
50 print_string gPrintFun = NULL;
51 state_update gStateUpdateFun = NULL;
52
53 /*! mutex to control displaying of events */
54 ithread_mutex_t display_mutex;
55
56 /********************************************************************************
57  * SampleUtil_Initialize
58  *
59  * Description: 
60  *     Initializes the sample util. Must be called before any sample util 
61  *     functions. May be called multiple times.
62  *
63  * Parameters:
64  *   print_function - print function to use in SampleUtil_Print
65  *
66  ********************************************************************************/
67 int
68 SampleUtil_Initialize( print_string print_function )
69 {
70     if( initialize ) {
71         ithread_mutexattr_t attr;
72
73         gPrintFun = print_function;
74         ithread_mutexattr_init( &attr );
75         ithread_mutexattr_setkind_np( &attr, ITHREAD_MUTEX_RECURSIVE_NP );
76         ithread_mutex_init( &display_mutex, &attr );
77         ithread_mutexattr_destroy( &attr );
78         initialize = 0;
79     }
80     return UPNP_E_SUCCESS;
81 }
82
83 /********************************************************************************
84  * SampleUtil_RegisterUpdateFunction
85  *
86  * Description: 
87  *
88  * Parameters:
89  *
90  ********************************************************************************/
91 int
92 SampleUtil_RegisterUpdateFunction( state_update update_function )
93 {
94     static int initialize = 1;  //only intialize once
95
96     if( initialize ) {
97         gStateUpdateFun = update_function;
98         initialize = 0;
99     }
100     return UPNP_E_SUCCESS;
101 }
102
103 /********************************************************************************
104  * SampleUtil_Finish
105  *
106  * Description: 
107  *     Releases Resources held by sample util.
108  *
109  * Parameters:
110  *
111  ********************************************************************************/
112 int
113 SampleUtil_Finish()
114 {
115     ithread_mutex_destroy( &display_mutex );
116     gPrintFun = NULL;
117     initialize = 1;
118     return UPNP_E_SUCCESS;
119 }
120
121 /********************************************************************************
122  * SampleUtil_GetElementValue
123  *
124  * Description: 
125  *       Given a DOM node such as <Channel>11</Channel>, this routine
126  *       extracts the value (e.g., 11) from the node and returns it as 
127  *       a string. The string must be freed by the caller using 
128  *       free.
129  *
130  * Parameters:
131  *   node -- The DOM node from which to extract the value
132  *
133  ********************************************************************************/
134
135 char *
136 SampleUtil_GetElementValue( IN IXML_Element * element )
137 {
138
139     IXML_Node *child = ixmlNode_getFirstChild( ( IXML_Node * ) element );
140
141     char *temp = NULL;
142
143     if( ( child != 0 ) && ( ixmlNode_getNodeType( child ) == eTEXT_NODE ) ) {
144         temp = strdup( ixmlNode_getNodeValue( child ) );
145     }
146
147     return temp;
148 }
149
150 /********************************************************************************
151  * SampleUtil_GetFirstServiceList
152  *
153  * Description: 
154  *       Given a DOM node representing a UPnP Device Description Document,
155  *       this routine parses the document and finds the first service list
156  *       (i.e., the service list for the root device).  The service list
157  *       is returned as a DOM node list.
158  *
159  * Parameters:
160  *   node -- The DOM node from which to extract the service list
161  *
162  ********************************************************************************/
163 IXML_NodeList *
164 SampleUtil_GetFirstServiceList( IN IXML_Document * doc )
165 {
166     IXML_NodeList *ServiceList = NULL;
167     IXML_NodeList *servlistnodelist = NULL;
168     IXML_Node *servlistnode = NULL;
169
170     servlistnodelist =
171         ixmlDocument_getElementsByTagName( doc, "serviceList" );
172     if( servlistnodelist && ixmlNodeList_length( servlistnodelist ) ) {
173
174         /*
175            we only care about the first service list, from the root device 
176          */
177         servlistnode = ixmlNodeList_item( servlistnodelist, 0 );
178
179         /*
180            create as list of DOM nodes 
181          */
182         ServiceList =
183             ixmlElement_getElementsByTagName( ( IXML_Element * )
184                                               servlistnode, "service" );
185     }
186
187     if( servlistnodelist )
188         ixmlNodeList_free( servlistnodelist );
189
190     return ServiceList;
191 }
192
193 /********************************************************************************
194  * SampleUtil_GetFirstDocumentItem
195  *
196  * Description: 
197  *       Given a document node, this routine searches for the first element
198  *       named by the input string item, and returns its value as a string.
199  *       String must be freed by caller using free.
200  * Parameters:
201  *   doc -- The DOM document from which to extract the value
202  *   item -- The item to search for
203  *
204  ********************************************************************************/
205 char *
206 SampleUtil_GetFirstDocumentItem( IN IXML_Document * doc,
207                                  IN const char *item )
208 {
209     IXML_NodeList *nodeList = NULL;
210     IXML_Node *textNode = NULL;
211     IXML_Node *tmpNode = NULL;
212
213     char *ret = NULL;
214
215     nodeList = ixmlDocument_getElementsByTagName( doc, ( char * )item );
216
217     if( nodeList ) {
218         if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) ) {
219             textNode = ixmlNode_getFirstChild( tmpNode );
220
221             ret = strdup( ixmlNode_getNodeValue( textNode ) );
222         }
223     }
224
225     if( nodeList )
226         ixmlNodeList_free( nodeList );
227     return ret;
228 }
229
230 /********************************************************************************
231  * SampleUtil_GetFirstElementItem
232  *
233  * Description: 
234  *       Given a DOM element, this routine searches for the first element
235  *       named by the input string item, and returns its value as a string.
236  *       The string must be freed using free.
237  * Parameters:
238  *   node -- The DOM element from which to extract the value
239  *   item -- The item to search for
240  *
241  ********************************************************************************/
242 char *
243 SampleUtil_GetFirstElementItem( IN IXML_Element * element,
244                                 IN const char *item )
245 {
246     IXML_NodeList *nodeList = NULL;
247     IXML_Node *textNode = NULL;
248     IXML_Node *tmpNode = NULL;
249
250     char *ret = NULL;
251
252     nodeList = ixmlElement_getElementsByTagName( element, ( char * )item );
253
254     if( nodeList == NULL ) {
255         SampleUtil_Print( "Error finding %s in XML Node\n", item );
256         return NULL;
257     }
258
259     if( ( tmpNode = ixmlNodeList_item( nodeList, 0 ) ) == NULL ) {
260         SampleUtil_Print( "Error finding %s value in XML Node\n", item );
261         ixmlNodeList_free( nodeList );
262         return NULL;
263     }
264
265     textNode = ixmlNode_getFirstChild( tmpNode );
266
267     ret = strdup( ixmlNode_getNodeValue( textNode ) );
268
269     if( !ret ) {
270         SampleUtil_Print( "Error allocating memory for %s in XML Node\n",
271                           item );
272         ixmlNodeList_free( nodeList );
273         return NULL;
274     }
275
276     ixmlNodeList_free( nodeList );
277
278     return ret;
279 }
280
281 /********************************************************************************
282  * SampleUtil_PrintEventType
283  *
284  * Description: 
285  *       Prints a callback event type as a string.
286  *
287  * Parameters:
288  *   S -- The callback event
289  *
290  ********************************************************************************/
291 void
292 SampleUtil_PrintEventType( IN Upnp_EventType S )
293 {
294     switch ( S ) {
295
296         case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
297             SampleUtil_Print( "UPNP_DISCOVERY_ADVERTISEMENT_ALIVE\n" );
298             break;
299         case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
300             SampleUtil_Print( "UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE\n" );
301             break;
302         case UPNP_DISCOVERY_SEARCH_RESULT:
303             SampleUtil_Print( "UPNP_DISCOVERY_SEARCH_RESULT\n" );
304             break;
305         case UPNP_DISCOVERY_SEARCH_TIMEOUT:
306             SampleUtil_Print( "UPNP_DISCOVERY_SEARCH_TIMEOUT\n" );
307             break;
308
309             /*
310                SOAP Stuff 
311              */
312         case UPNP_CONTROL_ACTION_REQUEST:
313             SampleUtil_Print( "UPNP_CONTROL_ACTION_REQUEST\n" );
314             break;
315         case UPNP_CONTROL_ACTION_COMPLETE:
316             SampleUtil_Print( "UPNP_CONTROL_ACTION_COMPLETE\n" );
317             break;
318         case UPNP_CONTROL_GET_VAR_REQUEST:
319             SampleUtil_Print( "UPNP_CONTROL_GET_VAR_REQUEST\n" );
320             break;
321         case UPNP_CONTROL_GET_VAR_COMPLETE:
322             SampleUtil_Print( "UPNP_CONTROL_GET_VAR_COMPLETE\n" );
323             break;
324
325             /*
326                GENA Stuff 
327              */
328         case UPNP_EVENT_SUBSCRIPTION_REQUEST:
329             SampleUtil_Print( "UPNP_EVENT_SUBSCRIPTION_REQUEST\n" );
330             break;
331         case UPNP_EVENT_RECEIVED:
332             SampleUtil_Print( "UPNP_EVENT_RECEIVED\n" );
333             break;
334         case UPNP_EVENT_RENEWAL_COMPLETE:
335             SampleUtil_Print( "UPNP_EVENT_RENEWAL_COMPLETE\n" );
336             break;
337         case UPNP_EVENT_SUBSCRIBE_COMPLETE:
338             SampleUtil_Print( "UPNP_EVENT_SUBSCRIBE_COMPLETE\n" );
339             break;
340         case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
341             SampleUtil_Print( "UPNP_EVENT_UNSUBSCRIBE_COMPLETE\n" );
342             break;
343
344         case UPNP_EVENT_AUTORENEWAL_FAILED:
345             SampleUtil_Print( "UPNP_EVENT_AUTORENEWAL_FAILED\n" );
346             break;
347         case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
348             SampleUtil_Print( "UPNP_EVENT_SUBSCRIPTION_EXPIRED\n" );
349             break;
350
351     }
352 }
353
354 /********************************************************************************
355  * SampleUtil_PrintEvent
356  *
357  * Description: 
358  *       Prints callback event structure details.
359  *
360  * Parameters:
361  *   EventType -- The type of callback event
362  *   Event -- The callback event structure
363  *
364  ********************************************************************************/
365 int
366 SampleUtil_PrintEvent( IN Upnp_EventType EventType,
367                        IN void *Event )
368 {
369
370     ithread_mutex_lock( &display_mutex );
371
372     SampleUtil_Print
373         ( "\n\n\n======================================================================\n" );
374     SampleUtil_Print
375         ( "----------------------------------------------------------------------\n" );
376     SampleUtil_PrintEventType( EventType );
377
378     switch ( EventType ) {
379
380             /*
381                SSDP 
382              */
383         case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
384         case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
385         case UPNP_DISCOVERY_SEARCH_RESULT:
386             {
387                 struct Upnp_Discovery *d_event =
388                     ( struct Upnp_Discovery * )Event;
389
390                 SampleUtil_Print( "ErrCode     =  %d\n",
391                                   d_event->ErrCode );
392                 SampleUtil_Print( "Expires     =  %d\n",
393                                   d_event->Expires );
394                 SampleUtil_Print( "DeviceId    =  %s\n",
395                                   d_event->DeviceId );
396                 SampleUtil_Print( "DeviceType  =  %s\n",
397                                   d_event->DeviceType );
398                 SampleUtil_Print( "ServiceType =  %s\n",
399                                   d_event->ServiceType );
400                 SampleUtil_Print( "ServiceVer  =  %s\n",
401                                   d_event->ServiceVer );
402                 SampleUtil_Print( "Location    =  %s\n",
403                                   d_event->Location );
404                 SampleUtil_Print( "OS          =  %s\n", d_event->Os );
405                 SampleUtil_Print( "Ext         =  %s\n", d_event->Ext );
406
407             }
408             break;
409
410         case UPNP_DISCOVERY_SEARCH_TIMEOUT:
411             // Nothing to print out here
412             break;
413
414             /*
415                SOAP 
416              */
417         case UPNP_CONTROL_ACTION_REQUEST:
418             {
419                 struct Upnp_Action_Request *a_event =
420                     ( struct Upnp_Action_Request * )Event;
421                 char *xmlbuff = NULL;
422
423                 SampleUtil_Print( "ErrCode     =  %d\n",
424                                   a_event->ErrCode );
425                 SampleUtil_Print( "ErrStr      =  %s\n", a_event->ErrStr );
426                 SampleUtil_Print( "ActionName  =  %s\n",
427                                   a_event->ActionName );
428                 SampleUtil_Print( "UDN         =  %s\n", a_event->DevUDN );
429                 SampleUtil_Print( "ServiceID   =  %s\n",
430                                   a_event->ServiceID );
431                 if( a_event->ActionRequest ) {
432                     xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionRequest );
433                     if ( xmlbuff ) {
434                         SampleUtil_Print( "ActRequest  =  %s\n", xmlbuff );
435                         ixmlFreeDOMString( xmlbuff );
436                     }
437                     xmlbuff = NULL;
438                 } else {
439                     SampleUtil_Print( "ActRequest  =  (null)\n" );
440                 }
441
442                 if( a_event->ActionResult ) {
443                     xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionResult );
444                     if ( xmlbuff ) {
445                         SampleUtil_Print( "ActResult   =  %s\n", xmlbuff );
446                         ixmlFreeDOMString( xmlbuff );
447                     }
448                     xmlbuff = NULL;
449                 } else {
450                     SampleUtil_Print( "ActResult   =  (null)\n" );
451                 }
452             }
453             break;
454
455         case UPNP_CONTROL_ACTION_COMPLETE:
456             {
457                 struct Upnp_Action_Complete *a_event =
458                     ( struct Upnp_Action_Complete * )Event;
459                 char *xmlbuff = NULL;
460
461                 SampleUtil_Print( "ErrCode     =  %d\n",
462                                   a_event->ErrCode );
463                 SampleUtil_Print( "CtrlUrl     =  %s\n",
464                                   a_event->CtrlUrl );
465                 if( a_event->ActionRequest ) {
466                     xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionRequest );
467                     if( xmlbuff ) {
468                         SampleUtil_Print( "ActRequest  =  %s\n", xmlbuff );
469                         ixmlFreeDOMString( xmlbuff );
470                     }
471                     xmlbuff = NULL;
472                 } else {
473                     SampleUtil_Print( "ActRequest  =  (null)\n" );
474                 }
475
476                 if( a_event->ActionResult ) {
477                     xmlbuff = ixmlPrintNode( ( IXML_Node * ) a_event->ActionResult );
478                     if( xmlbuff ) {
479                         SampleUtil_Print( "ActResult   =  %s\n", xmlbuff );
480                         ixmlFreeDOMString( xmlbuff );
481                     }
482                     xmlbuff = NULL;
483                 } else {
484                     SampleUtil_Print( "ActResult   =  (null)\n" );
485                 }
486             }
487             break;
488
489         case UPNP_CONTROL_GET_VAR_REQUEST:
490             {
491                 struct Upnp_State_Var_Request *sv_event =
492                     ( struct Upnp_State_Var_Request * )Event;
493
494                 SampleUtil_Print( "ErrCode     =  %d\n",
495                                   sv_event->ErrCode );
496                 SampleUtil_Print( "ErrStr      =  %s\n",
497                                   sv_event->ErrStr );
498                 SampleUtil_Print( "UDN         =  %s\n",
499                                   sv_event->DevUDN );
500                 SampleUtil_Print( "ServiceID   =  %s\n",
501                                   sv_event->ServiceID );
502                 SampleUtil_Print( "StateVarName=  %s\n",
503                                   sv_event->StateVarName );
504                 SampleUtil_Print( "CurrentVal  =  %s\n",
505                                   sv_event->CurrentVal );
506             }
507             break;
508
509         case UPNP_CONTROL_GET_VAR_COMPLETE:
510             {
511                 struct Upnp_State_Var_Complete *sv_event =
512                     ( struct Upnp_State_Var_Complete * )Event;
513
514                 SampleUtil_Print( "ErrCode     =  %d\n",
515                                   sv_event->ErrCode );
516                 SampleUtil_Print( "CtrlUrl     =  %s\n",
517                                   sv_event->CtrlUrl );
518                 SampleUtil_Print( "StateVarName=  %s\n",
519                                   sv_event->StateVarName );
520                 SampleUtil_Print( "CurrentVal  =  %s\n",
521                                   sv_event->CurrentVal );
522             }
523             break;
524
525             /*
526                GENA 
527              */
528         case UPNP_EVENT_SUBSCRIPTION_REQUEST:
529             {
530                 struct Upnp_Subscription_Request *sr_event =
531                     ( struct Upnp_Subscription_Request * )Event;
532
533                 SampleUtil_Print( "ServiceID   =  %s\n",
534                                   sr_event->ServiceId );
535                 SampleUtil_Print( "UDN         =  %s\n", sr_event->UDN );
536                 SampleUtil_Print( "SID         =  %s\n", sr_event->Sid );
537             }
538             break;
539
540         case UPNP_EVENT_RECEIVED:
541             {
542                 struct Upnp_Event *e_event = ( struct Upnp_Event * )Event;
543                 char *xmlbuff = NULL;
544
545                 SampleUtil_Print( "SID         =  %s\n", e_event->Sid );
546                 SampleUtil_Print( "EventKey    =  %d\n",
547                                   e_event->EventKey );
548                 xmlbuff = ixmlPrintNode( ( IXML_Node * ) e_event->ChangedVariables );
549                 SampleUtil_Print( "ChangedVars =  %s\n", xmlbuff );
550                 ixmlFreeDOMString(xmlbuff);
551                 xmlbuff = NULL;
552             }
553             break;
554
555         case UPNP_EVENT_RENEWAL_COMPLETE:
556             {
557                 struct Upnp_Event_Subscribe *es_event =
558                     ( struct Upnp_Event_Subscribe * )Event;
559
560                 SampleUtil_Print( "SID         =  %s\n", es_event->Sid );
561                 SampleUtil_Print( "ErrCode     =  %d\n",
562                                   es_event->ErrCode );
563                 SampleUtil_Print( "TimeOut     =  %d\n",
564                                   es_event->TimeOut );
565             }
566             break;
567
568         case UPNP_EVENT_SUBSCRIBE_COMPLETE:
569         case UPNP_EVENT_UNSUBSCRIBE_COMPLETE:
570             {
571                 struct Upnp_Event_Subscribe *es_event =
572                     ( struct Upnp_Event_Subscribe * )Event;
573
574                 SampleUtil_Print( "SID         =  %s\n", es_event->Sid );
575                 SampleUtil_Print( "ErrCode     =  %d\n",
576                                   es_event->ErrCode );
577                 SampleUtil_Print( "PublisherURL=  %s\n",
578                                   es_event->PublisherUrl );
579                 SampleUtil_Print( "TimeOut     =  %d\n",
580                                   es_event->TimeOut );
581             }
582             break;
583
584         case UPNP_EVENT_AUTORENEWAL_FAILED:
585         case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
586             {
587                 struct Upnp_Event_Subscribe *es_event =
588                     ( struct Upnp_Event_Subscribe * )Event;
589
590                 SampleUtil_Print( "SID         =  %s\n", es_event->Sid );
591                 SampleUtil_Print( "ErrCode     =  %d\n",
592                                   es_event->ErrCode );
593                 SampleUtil_Print( "PublisherURL=  %s\n",
594                                   es_event->PublisherUrl );
595                 SampleUtil_Print( "TimeOut     =  %d\n",
596                                   es_event->TimeOut );
597             }
598             break;
599
600     }
601     SampleUtil_Print
602         ( "----------------------------------------------------------------------\n" );
603     SampleUtil_Print
604         ( "======================================================================\n\n\n\n" );
605
606     ithread_mutex_unlock( &display_mutex );
607     return 0;
608 }
609
610 /********************************************************************************
611  * SampleUtil_FindAndParseService
612  *
613  * Description: 
614  *       This routine finds the first occurance of a service in a DOM representation
615  *       of a description document and parses it.  
616  *
617  * Parameters:
618  *   DescDoc -- The DOM description document
619  *   location -- The location of the description document
620  *   serviceSearchType -- The type of service to search for
621  *   serviceId -- OUT -- The service ID
622  *   eventURL -- OUT -- The event URL for the service
623  *   controlURL -- OUT -- The control URL for the service
624  *
625  ********************************************************************************/
626 int
627 SampleUtil_FindAndParseService( IN IXML_Document * DescDoc,
628                                 IN const char *location,
629                                 IN char *serviceType,
630                                 OUT char **serviceId,
631                                 OUT char **eventURL,
632                                 OUT char **controlURL )
633 {
634     int i,
635       length,
636       found = 0;
637     int ret;
638     char *tempServiceType = NULL;
639     char *baseURL = NULL;
640     const char *base = NULL;
641     char *relcontrolURL = NULL,
642      *releventURL = NULL;
643     IXML_NodeList *serviceList = NULL;
644     IXML_Element *service = NULL;
645
646     baseURL = SampleUtil_GetFirstDocumentItem( DescDoc, "URLBase" );
647     if (baseURL) {
648         base = baseURL;
649     } else {
650         base = location;
651     }
652
653     serviceList = SampleUtil_GetFirstServiceList( DescDoc );
654     length = ixmlNodeList_length( serviceList );
655     for( i = 0; i < length; i++ ) {
656         service = ( IXML_Element * ) ixmlNodeList_item( serviceList, i );
657         tempServiceType =
658             SampleUtil_GetFirstElementItem( ( IXML_Element * ) service,
659                                             "serviceType" );
660
661         if( strcmp( tempServiceType, serviceType ) == 0 ) {
662             SampleUtil_Print( "Found service: %s\n", serviceType );
663             *serviceId =
664                 SampleUtil_GetFirstElementItem( service, "serviceId" );
665             SampleUtil_Print( "serviceId: %s\n", ( *serviceId ) );
666             relcontrolURL =
667                 SampleUtil_GetFirstElementItem( service, "controlURL" );
668             releventURL =
669                 SampleUtil_GetFirstElementItem( service, "eventSubURL" );
670
671             *controlURL =
672                 malloc( strlen( base ) + strlen( relcontrolURL ) + 1 );
673             if( *controlURL ) {
674                 ret = UpnpResolveURL( base, relcontrolURL, *controlURL );
675                 if( ret != UPNP_E_SUCCESS )
676                     SampleUtil_Print
677                         ( "Error generating controlURL from %s + %s\n",
678                           base, relcontrolURL );
679             }
680
681             *eventURL =
682                 malloc( strlen( base ) + strlen( releventURL ) + 1 );
683             if( *eventURL ) {
684                 ret = UpnpResolveURL( base, releventURL, *eventURL );
685                 if( ret != UPNP_E_SUCCESS )
686                     SampleUtil_Print
687                         ( "Error generating eventURL from %s + %s\n", base,
688                           releventURL );
689             }
690
691             if( relcontrolURL )
692                 free( relcontrolURL );
693             if( releventURL )
694                 free( releventURL );
695             relcontrolURL = releventURL = NULL;
696
697             found = 1;
698             break;
699         }
700
701         if( tempServiceType )
702             free( tempServiceType );
703         tempServiceType = NULL;
704     }
705
706     if( tempServiceType )
707         free( tempServiceType );
708     if( serviceList )
709         ixmlNodeList_free( serviceList );
710     if( baseURL )
711         free( baseURL );
712
713     return ( found );
714 }
715
716 // printf wrapper for portable code
717 int
718 uprint1( char *fmt,
719          ... )
720 {
721     va_list ap;
722     char *buf = NULL;
723     int size;
724
725     va_start( ap, fmt );
726     size = vsnprintf( buf, 0, fmt, ap );
727     va_end( ap );
728
729     if( size > 0 ) {
730         buf = ( char * )malloc( size + 1 );
731         if( vsnprintf( buf, size + 1, fmt, ap ) != size ) {
732             free( buf );
733             buf = NULL;
734         }
735     }
736
737     if( buf ) {
738         ithread_mutex_lock( &display_mutex );
739         if( gPrintFun )
740             gPrintFun( buf );
741         ithread_mutex_unlock( &display_mutex );
742         free( buf );
743     }
744
745     return size;
746 }
747
748 /********************************************************************************
749  * SampleUtil_Print
750  *
751  * Description: 
752  *      Provides platform-specific print functionality.  This function should be
753  *      called when you want to print content suitable for console output (i.e.,
754  *      in a large text box or on a screen).  If your device/operating system is 
755  *      not supported here, you should add a port.
756  *
757  * Parameters:
758  *              Same as printf()
759  *
760  ********************************************************************************/
761 int SampleUtil_Print(char *fmt, ...)
762 {
763 #define MAX_BUF 1024
764     va_list ap;
765     static char buf[MAX_BUF];
766     int rc;
767
768     /* Protect both the display and the static buffer with the mutex */
769     ithread_mutex_lock(&display_mutex);
770     va_start(ap, fmt);
771     rc = vsnprintf(buf, MAX_BUF, fmt, ap);
772     va_end(ap);
773
774     if (gPrintFun) {
775         gPrintFun(buf);
776     }
777     ithread_mutex_unlock(&display_mutex);
778
779     return rc;
780 }
781
782 /********************************************************************************
783  * SampleUtil_StateUpdate
784  *
785  * Description: 
786  *
787  * Parameters:
788  *
789  ********************************************************************************/
790 void
791 SampleUtil_StateUpdate( const char *varName,
792                         const char *varValue,
793                         const char *UDN,
794                         eventType type )
795 {
796     if( gStateUpdateFun )
797         gStateUpdateFun( varName, varValue, UDN, type );
798     // TBD: Add mutex here?
799 }