]> git.sesse.net Git - vlc/blob - modules/services_discovery/upnp_intel.cpp
upnp_intel: Remove trailing whitespaces.
[vlc] / modules / services_discovery / upnp_intel.cpp
1 /*****************************************************************************
2  * Upnp_intel.cpp :  UPnP discovery module (Intel SDK)
3  *****************************************************************************
4  * Copyright (C) 2004-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: RĂ©mi Denis-Courmont <rem # videolan.org> (original plugin)
8  *          Christian Henz <henz # c-lab.de>
9  *          Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
10  *
11  * UPnP Plugin using the Intel SDK (libupnp) instead of CyberLink
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
27
28 /*
29   \TODO: Debug messages: "__FILE__, __LINE__" ok ???, Wrn/Err ???
30   \TODO: Change names to VLC standard ???
31 */
32 #undef PACKAGE_NAME
33 #ifdef HAVE_CONFIG_H
34 # include "config.h"
35 #endif
36
37 #include "upnp_intel.hpp"
38
39 #include <vlc_plugin.h>
40 #include <vlc_services_discovery.h>
41
42
43 // Constants
44 const char* MEDIA_SERVER_DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
45 const char* CONTENT_DIRECTORY_SERVICE_TYPE = "urn:schemas-upnp-org:service:ContentDirectory:1";
46
47 // VLC handle
48 struct services_discovery_sys_t
49 {
50     UpnpClient_Handle clientHandle;
51     MediaServerList* serverList;
52     vlc_mutex_t callbackLock;
53 };
54
55 // VLC callback prototypes
56 static int Open( vlc_object_t* );
57 static void Close( vlc_object_t* );
58 VLC_SD_PROBE_HELPER("upnp", N_("Universal Plug'n'Play"))
59
60 // Module descriptor
61
62 vlc_module_begin();
63     set_shortname( "UPnP" );
64     set_description( N_( "Universal Plug'n'Play" ) );
65     set_category( CAT_PLAYLIST );
66     set_subcategory( SUBCAT_PLAYLIST_SD );
67     set_capability( "services_discovery", 0 );
68     set_callbacks( Open, Close );
69
70     VLC_SD_PROBE_SUBMODULE
71 vlc_module_end();
72
73
74 // More prototypes...
75
76 static int Callback( Upnp_EventType eventType, void* event, void* user_data );
77
78 const char* xml_getChildElementValue( IXML_Element* parent,
79                                       const char*   tagName );
80
81 IXML_Document* parseBrowseResult( IXML_Document* doc );
82
83
84 // VLC callbacks...
85
86 static int Open( vlc_object_t *p_this )
87 {
88     int res;
89     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
90     services_discovery_sys_t *p_sys  = ( services_discovery_sys_t * )
91             calloc( 1, sizeof( services_discovery_sys_t ) );
92
93     if(!(p_sd->p_sys = p_sys))
94         return VLC_ENOMEM;
95
96     res = UpnpInit( 0, 0 );
97     if( res != UPNP_E_SUCCESS )
98     {
99         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
100         free( p_sys );
101         return VLC_EGENERIC;
102     }
103
104     p_sys->serverList = new MediaServerList( p_sd );
105     vlc_mutex_init( &p_sys->callbackLock );
106
107     res = UpnpRegisterClient( Callback, p_sd, &p_sys->clientHandle );
108     if( res != UPNP_E_SUCCESS )
109     {
110         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
111         Close( (vlc_object_t*) p_sd );
112         return VLC_EGENERIC;
113     }
114
115     res = UpnpSearchAsync( p_sys->clientHandle, 5,
116             MEDIA_SERVER_DEVICE_TYPE, p_sd );
117
118     if( res != UPNP_E_SUCCESS )
119     {
120         msg_Err( p_sd, "%s", UpnpGetErrorMessage( res ) );
121         Close( (vlc_object_t*) p_sd );
122         return VLC_EGENERIC;
123     }
124
125     return VLC_SUCCESS;
126 }
127
128 static void Close( vlc_object_t *p_this )
129 {
130     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
131
132     UpnpFinish();
133     delete p_sd->p_sys->serverList;
134     vlc_mutex_destroy( &p_sd->p_sys->callbackLock );
135
136     free( p_sd->p_sys );
137 }
138
139 // XML utility functions:
140
141 // Returns the value of a child element, or 0 on error
142 const char* xml_getChildElementValue( IXML_Element* parent,
143                                       const char*   tagName )
144 {
145     if ( !parent ) return 0;
146     if ( !tagName ) return 0;
147
148     char* s = strdup( tagName );
149     IXML_NodeList* nodeList = ixmlElement_getElementsByTagName( parent, s );
150     free( s );
151     if ( !nodeList ) return 0;
152
153     IXML_Node* element = ixmlNodeList_item( nodeList, 0 );
154     ixmlNodeList_free( nodeList );
155     if ( !element ) return 0;
156
157     IXML_Node* textNode = ixmlNode_getFirstChild( element );
158     if ( !textNode ) return 0;
159
160     return ixmlNode_getNodeValue( textNode );
161 }
162
163 // Extracts the result document from a SOAP response
164 IXML_Document* parseBrowseResult( IXML_Document* doc )
165 {
166     ixmlRelaxParser(1);
167
168     if ( !doc ) return 0;
169
170     IXML_NodeList* resultList = ixmlDocument_getElementsByTagName( doc,
171                                                                    "Result" );
172
173     if ( !resultList ) return 0;
174
175     IXML_Node* resultNode = ixmlNodeList_item( resultList, 0 );
176
177     ixmlNodeList_free( resultList );
178
179     if ( !resultNode ) return 0;
180
181     IXML_Node* textNode = ixmlNode_getFirstChild( resultNode );
182     if ( !textNode ) return 0;
183
184     const char* resultString = ixmlNode_getNodeValue( textNode );
185     char* resultXML = strdup( resultString );
186
187     IXML_Document* browseDoc = ixmlParseBuffer( resultXML );
188
189     free( resultXML );
190
191     return browseDoc;
192 }
193
194
195 // Handles all UPnP events
196 static int Callback( Upnp_EventType eventType, void* event, void* user_data )
197 {
198     services_discovery_t *p_sd = ( services_discovery_t* ) user_data;
199     services_discovery_sys_t* p_sys = p_sd->p_sys;
200     vlc_mutex_locker locker( &p_sys->callbackLock );
201
202     switch( eventType ) {
203
204     case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE:
205     case UPNP_DISCOVERY_SEARCH_RESULT:
206     {
207         struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
208
209         IXML_Document *descriptionDoc = 0;
210
211         int res;
212         res = UpnpDownloadXmlDoc( discovery->Location, &descriptionDoc );
213         if ( res != UPNP_E_SUCCESS )
214         {
215             msg_Dbg( p_sd,
216                     "%s:%d: Could not download device description!",
217                     __FILE__, __LINE__ );
218             return res;
219         }
220
221         MediaServer::parseDeviceDescription( descriptionDoc,
222                 discovery->Location, p_sd );
223
224         ixmlDocument_free( descriptionDoc );
225     }
226     break;
227
228     case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE:
229     {
230         struct Upnp_Discovery* discovery = ( struct Upnp_Discovery* )event;
231
232         p_sys->serverList->removeServer( discovery->DeviceId );
233     }
234     break;
235
236     case UPNP_EVENT_RECEIVED:
237     {
238         Upnp_Event* e = ( Upnp_Event* )event;
239
240         MediaServer* server = p_sys->serverList->getServerBySID( e->Sid );
241         if ( server ) server->fetchContents();
242     }
243     break;
244
245     case UPNP_EVENT_AUTORENEWAL_FAILED:
246     case UPNP_EVENT_SUBSCRIPTION_EXPIRED:
247     {
248         // Re-subscribe...
249
250         Upnp_Event_Subscribe* s = ( Upnp_Event_Subscribe* )event;
251
252         MediaServer* server = p_sys->serverList->getServerBySID( s->Sid );
253         if ( server ) server->subscribeToContentDirectory();
254     }
255     break;
256
257     case UPNP_EVENT_SUBSCRIBE_COMPLETE:
258         msg_Warn( p_sd, "subscription complete" );
259         break;
260
261     case UPNP_DISCOVERY_SEARCH_TIMEOUT:
262         msg_Warn( p_sd, "search timeout" );
263         break;
264
265     default:
266     msg_Dbg( p_sd,
267             "%s:%d: DEBUG: UNHANDLED EVENT ( TYPE=%d )",
268             __FILE__, __LINE__, eventType );
269     break;
270     }
271
272     return UPNP_E_SUCCESS;
273 }
274
275
276 // Class implementations...
277
278 // MediaServer...
279
280 void MediaServer::parseDeviceDescription( IXML_Document* doc,
281                                           const char*    location,
282                                           services_discovery_t* p_sd )
283 {
284     if ( !doc )
285     {
286         msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
287         return;
288     }
289
290     if ( !location )
291     {
292         msg_Dbg( p_sd, "%s:%d: NULL", __FILE__, __LINE__ );
293         return;
294     }
295
296     const char* baseURL = location;
297
298     // Try to extract baseURL
299
300     IXML_NodeList* urlList = ixmlDocument_getElementsByTagName( doc, "baseURL" );
301     if ( !urlList )
302     {
303
304         if ( IXML_Node* urlNode = ixmlNodeList_item( urlList, 0 ) )
305         {
306             IXML_Node* textNode = ixmlNode_getFirstChild( urlNode );
307             if ( textNode ) baseURL = ixmlNode_getNodeValue( textNode );
308         }
309
310         ixmlNodeList_free( urlList );
311     }
312
313     // Get devices
314
315     IXML_NodeList* deviceList =
316                 ixmlDocument_getElementsByTagName( doc, "device" );
317
318     if ( deviceList )
319     {
320         for ( unsigned int i = 0; i < ixmlNodeList_length( deviceList ); i++ )
321         {
322             IXML_Element* deviceElement =
323                    ( IXML_Element* ) ixmlNodeList_item( deviceList, i );
324
325             const char* deviceType = xml_getChildElementValue( deviceElement,
326                                                                "deviceType" );
327             if ( !deviceType )
328             {
329                 msg_Dbg( p_sd,
330                         "%s:%d: no deviceType!",
331                         __FILE__, __LINE__ );
332                 continue;
333             }
334
335             if ( strcmp( MEDIA_SERVER_DEVICE_TYPE, deviceType ) != 0 )
336                 continue;
337
338             const char* UDN = xml_getChildElementValue( deviceElement, "UDN" );
339             if ( !UDN )
340             {
341                 msg_Dbg( p_sd, "%s:%d: no UDN!",
342                         __FILE__, __LINE__ );
343                 continue;
344             }
345
346             if ( p_sd->p_sys->serverList->getServer( UDN ) != 0 )
347                 continue;
348
349             const char* friendlyName =
350                        xml_getChildElementValue( deviceElement,
351                                                  "friendlyName" );
352
353             if ( !friendlyName )
354             {
355                 msg_Dbg( p_sd, "%s:%d: no friendlyName!", __FILE__, __LINE__ );
356                 continue;
357             }
358
359             MediaServer* server = new MediaServer( UDN, friendlyName, p_sd );
360
361             if ( !p_sd->p_sys->serverList->addServer( server ) )
362             {
363
364                 delete server;
365                 server = 0;
366                 continue;
367             }
368
369             // Check for ContentDirectory service...
370             IXML_NodeList* serviceList =
371                        ixmlElement_getElementsByTagName( deviceElement,
372                                                          "service" );
373             if ( serviceList )
374             {
375                 for ( unsigned int j = 0;
376                       j < ixmlNodeList_length( serviceList ); j++ )
377                 {
378                     IXML_Element* serviceElement =
379                         ( IXML_Element* ) ixmlNodeList_item( serviceList, j );
380
381                     const char* serviceType =
382                         xml_getChildElementValue( serviceElement,
383                                                   "serviceType" );
384                     if ( !serviceType )
385                         continue;
386
387                     if ( strcmp( CONTENT_DIRECTORY_SERVICE_TYPE,
388                                 serviceType ) != 0 )
389                         continue;
390
391                     const char* eventSubURL =
392                         xml_getChildElementValue( serviceElement,
393                                                   "eventSubURL" );
394                     if ( !eventSubURL )
395                         continue;
396
397                     const char* controlURL =
398                         xml_getChildElementValue( serviceElement,
399                                                   "controlURL" );
400                     if ( !controlURL )
401                         continue;
402
403                     // Try to subscribe to ContentDirectory service
404
405                     char* url = ( char* ) malloc( strlen( baseURL ) +
406                             strlen( eventSubURL ) + 1 );
407                     if ( url )
408                     {
409                         char* s1 = strdup( baseURL );
410                         char* s2 = strdup( eventSubURL );
411
412                         if ( UpnpResolveURL( s1, s2, url ) ==
413                                 UPNP_E_SUCCESS )
414                         {
415                             server->setContentDirectoryEventURL( url );
416                             server->subscribeToContentDirectory();
417                         }
418
419                         free( s1 );
420                         free( s2 );
421                         free( url );
422                     }
423
424                     // Try to browse content directory...
425
426                     url = ( char* ) malloc( strlen( baseURL ) +
427                             strlen( controlURL ) + 1 );
428                     if ( url )
429                     {
430                         char* s1 = strdup( baseURL );
431                         char* s2 = strdup( controlURL );
432
433                         if ( UpnpResolveURL( s1, s2, url ) ==
434                                 UPNP_E_SUCCESS )
435                         {
436                             server->setContentDirectoryControlURL( url );
437                             server->fetchContents();
438                         }
439
440                         free( s1 );
441                         free( s2 );
442                         free( url );
443                     }
444                }
445                ixmlNodeList_free( serviceList );
446            }
447        }
448        ixmlNodeList_free( deviceList );
449     }
450 }
451
452 MediaServer::MediaServer( const char* UDN,
453                           const char* friendlyName,
454                           services_discovery_t* p_sd )
455 {
456     _p_sd = p_sd;
457
458     _UDN = UDN;
459     _friendlyName = friendlyName;
460
461     _contents = NULL;
462     _inputItem = NULL;
463 }
464
465 MediaServer::~MediaServer()
466 {
467     delete _contents;
468 }
469
470 const char* MediaServer::getUDN() const
471 {
472   const char* s = _UDN.c_str();
473   return s;
474 }
475
476 const char* MediaServer::getFriendlyName() const
477 {
478     const char* s = _friendlyName.c_str();
479     return s;
480 }
481
482 void MediaServer::setContentDirectoryEventURL( const char* url )
483 {
484     _contentDirectoryEventURL = url;
485 }
486
487 const char* MediaServer::getContentDirectoryEventURL() const
488 {
489     const char* s =  _contentDirectoryEventURL.c_str();
490     return s;
491 }
492
493 void MediaServer::setContentDirectoryControlURL( const char* url )
494 {
495     _contentDirectoryControlURL = url;
496 }
497
498 const char* MediaServer::getContentDirectoryControlURL() const
499 {
500     return _contentDirectoryControlURL.c_str();
501 }
502
503 void MediaServer::subscribeToContentDirectory()
504 {
505     const char* url = getContentDirectoryEventURL();
506     if ( !url || strcmp( url, "" ) == 0 )
507     {
508         msg_Dbg( _p_sd, "No subscription url set!" );
509         return;
510     }
511
512     int timeOut = 1810;
513     Upnp_SID sid;
514
515     int res = UpnpSubscribe( _p_sd->p_sys->clientHandle, url, &timeOut, sid );
516
517     if ( res == UPNP_E_SUCCESS )
518     {
519         _subscriptionTimeOut = timeOut;
520         memcpy( _subscriptionID, sid, sizeof( Upnp_SID ) );
521     }
522     else
523     {
524         msg_Dbg( _p_sd,
525                 "%s:%d: WARNING: '%s': %s", __FILE__, __LINE__,
526                 getFriendlyName(), UpnpGetErrorMessage( res ) );
527     }
528 }
529
530 IXML_Document* MediaServer::_browseAction( const char* pObjectID,
531                                            const char* pBrowseFlag,
532                                            const char* pFilter,
533                                            const char* pStartingIndex,
534                                            const char* pRequestedCount,
535                                            const char* pSortCriteria )
536 {
537     IXML_Document* action = 0;
538     IXML_Document* response = 0;
539     const char* url = getContentDirectoryControlURL();
540
541     if ( !url || strcmp( url, "" ) == 0 )
542     {
543         msg_Dbg( _p_sd, "No subscription url set!" );
544         return 0;
545     }
546
547     char* ObjectID = strdup( pObjectID );
548     char* BrowseFlag = strdup( pBrowseFlag );
549     char* Filter = strdup( pFilter );
550     char* StartingIndex = strdup( pStartingIndex );
551     char* RequestedCount = strdup( pRequestedCount );
552     char* SortCriteria = strdup( pSortCriteria );
553     char* serviceType = strdup( CONTENT_DIRECTORY_SERVICE_TYPE );
554
555     int res;
556
557     res = UpnpAddToAction( &action, "Browse",
558             serviceType, "ObjectID", ObjectID );
559
560     if ( res != UPNP_E_SUCCESS )
561     {
562         msg_Dbg( _p_sd,
563                  "%s:%d: ERROR: %s", __FILE__, __LINE__,
564                  UpnpGetErrorMessage( res ) );
565         goto browseActionCleanup;
566     }
567
568     res = UpnpAddToAction( &action, "Browse",
569             serviceType, "BrowseFlag", BrowseFlag );
570
571     if ( res != UPNP_E_SUCCESS )
572     {
573         msg_Dbg( _p_sd,
574              "%s:%d: ERROR: %s", __FILE__, __LINE__,
575              UpnpGetErrorMessage( res ) );
576         goto browseActionCleanup;
577     }
578
579     res = UpnpAddToAction( &action, "Browse",
580             serviceType, "Filter", Filter );
581
582     if ( res != UPNP_E_SUCCESS )
583     {
584         msg_Dbg( _p_sd,
585              "%s:%d: ERROR: %s", __FILE__, __LINE__,
586              UpnpGetErrorMessage( res ) );
587         goto browseActionCleanup;
588     }
589
590     res = UpnpAddToAction( &action, "Browse",
591             serviceType, "StartingIndex", StartingIndex );
592
593     if ( res != UPNP_E_SUCCESS )
594     {
595         msg_Dbg( _p_sd,
596              "%s:%d: ERROR: %s", __FILE__, __LINE__,
597              UpnpGetErrorMessage( res ) );
598         goto browseActionCleanup;
599     }
600
601     res = UpnpAddToAction( &action, "Browse",
602             serviceType, "RequestedCount", RequestedCount );
603
604     if ( res != UPNP_E_SUCCESS )
605     {
606         msg_Dbg( _p_sd,
607                 "%s:%d: ERROR: %s", __FILE__, __LINE__,
608                 UpnpGetErrorMessage( res ) ); goto browseActionCleanup; }
609
610     res = UpnpAddToAction( &action, "Browse",
611             serviceType, "SortCriteria", SortCriteria );
612
613     if ( res != UPNP_E_SUCCESS )
614     {
615         msg_Dbg( _p_sd,
616              "%s:%d: ERROR: %s", __FILE__, __LINE__,
617              UpnpGetErrorMessage( res ) );
618         goto browseActionCleanup;
619     }
620
621     res = UpnpSendAction( _p_sd->p_sys->clientHandle,
622               url,
623               CONTENT_DIRECTORY_SERVICE_TYPE,
624               0,
625               action,
626               &response );
627
628     if ( res != UPNP_E_SUCCESS )
629     {
630         msg_Dbg( _p_sd,
631                 "%s:%d: ERROR: %s when trying the send() action with URL: %s",
632                 __FILE__, __LINE__,
633                 UpnpGetErrorMessage( res ), url );
634
635         ixmlDocument_free( response );
636         response = 0;
637     }
638
639  browseActionCleanup:
640
641     free( ObjectID );
642     free( BrowseFlag );
643     free( Filter );
644     free( StartingIndex );
645     free( RequestedCount );
646     free( SortCriteria );
647
648     free( serviceType );
649
650     ixmlDocument_free( action );
651     return response;
652 }
653
654 void MediaServer::fetchContents()
655 {
656     Container* root = new Container( 0, "0", getFriendlyName() );
657     _fetchContents( root );
658
659     _contents = root;
660     _contents->setInputItem( _inputItem );
661
662     _buildPlaylist( _contents, NULL );
663 }
664
665 bool MediaServer::_fetchContents( Container* parent )
666 {
667     if (!parent)
668     {
669         msg_Dbg( _p_sd,
670                 "%s:%d: parent==NULL", __FILE__, __LINE__ );
671         return false;
672     }
673
674     IXML_Document* response = _browseAction( parent->getObjectID(),
675                                       "BrowseDirectChildren",
676                                       "*", "0", "0", "" );
677     if ( !response )
678     {
679         msg_Dbg( _p_sd,
680                 "%s:%d: ERROR! No response from browse() action",
681                 __FILE__, __LINE__ );
682         return false;
683     }
684
685     IXML_Document* result = parseBrowseResult( response );
686     ixmlDocument_free( response );
687
688     if ( !result )
689     {
690         msg_Dbg( _p_sd,
691                 "%s:%d: ERROR! browse() response parsing failed",
692                 __FILE__, __LINE__ );
693         return false;
694     }
695
696     IXML_NodeList* containerNodeList =
697                 ixmlDocument_getElementsByTagName( result, "container" );
698
699     if ( containerNodeList )
700     {
701         for ( unsigned int i = 0;
702                 i < ixmlNodeList_length( containerNodeList ); i++ )
703         {
704             IXML_Element* containerElement =
705                   ( IXML_Element* )ixmlNodeList_item( containerNodeList, i );
706
707             const char* objectID = ixmlElement_getAttribute( containerElement,
708                                                              "id" );
709             if ( !objectID )
710                 continue;
711
712             const char* childCountStr =
713                     ixmlElement_getAttribute( containerElement, "childCount" );
714
715             if ( !childCountStr )
716                 continue;
717
718             int childCount = atoi( childCountStr );
719             const char* title = xml_getChildElementValue( containerElement,
720                                                           "dc:title" );
721
722             if ( !title )
723                 continue;
724
725             const char* resource = xml_getChildElementValue( containerElement,
726                                                              "res" );
727
728             if ( resource && childCount < 1 )
729             {
730                 Item* item = new Item( parent, objectID, title, resource );
731                 parent->addItem( item );
732             }
733
734             else
735             {
736                 Container* container = new Container( parent, objectID, title );
737                 parent->addContainer( container );
738
739                 if ( childCount > 0 )
740                     _fetchContents( container );
741             }
742         }
743         ixmlNodeList_free( containerNodeList );
744     }
745
746     IXML_NodeList* itemNodeList = ixmlDocument_getElementsByTagName( result,
747                                                                      "item" );
748     if ( itemNodeList )
749     {
750         for ( unsigned int i = 0; i < ixmlNodeList_length( itemNodeList ); i++ )
751         {
752             IXML_Element* itemElement =
753                         ( IXML_Element* )ixmlNodeList_item( itemNodeList, i );
754
755             const char* objectID =
756                         ixmlElement_getAttribute( itemElement, "id" );
757
758             if ( !objectID )
759                 continue;
760
761             const char* title =
762                         xml_getChildElementValue( itemElement, "dc:title" );
763
764             if ( !title )
765                 continue;
766
767             const char* resource =
768                         xml_getChildElementValue( itemElement, "res" );
769
770             if ( !resource )
771                 continue;
772
773             Item* item = new Item( parent, objectID, title, resource );
774             parent->addItem( item );
775         }
776         ixmlNodeList_free( itemNodeList );
777     }
778
779     ixmlDocument_free( result );
780     return true;
781 }
782
783 void MediaServer::_buildPlaylist( Container* parent, input_item_node_t *p_input_node )
784 {
785     bool send = p_input_node == NULL;
786     if( send )
787         p_input_node = input_item_node_Create( parent->getInputItem() );
788
789     for ( unsigned int i = 0; i < parent->getNumContainers(); i++ )
790     {
791         Container* container = parent->getContainer( i );
792
793         input_item_t* p_input_item = input_item_New( _p_sd, "vlc://nop", parent->getTitle() ); 
794         input_item_AddSubItem( parent->getInputItem(), p_input_item );
795         input_item_node_t *p_new_node =
796             input_item_node_AppendItem( p_input_node, p_input_item );
797
798         container->setInputItem( p_input_item );
799         _buildPlaylist( container, p_new_node );
800     }
801
802     for ( unsigned int i = 0; i < parent->getNumItems(); i++ )
803     {
804         Item* item = parent->getItem( i );
805
806         input_item_t* p_input_item = input_item_New( _p_sd,
807                                                item->getResource(),
808                                                item->getTitle() );
809         assert( p_input_item );
810         input_item_AddSubItem( parent->getInputItem(), p_input_item );
811         input_item_node_AppendItem( p_input_node, p_input_item );
812         item->setInputItem( p_input_item );
813     }
814
815     if( send )
816     {
817         input_item_AddSubItemTree( p_input_node );
818         input_item_node_Delete( p_input_node );
819     }
820 }
821
822 void MediaServer::setInputItem( input_item_t* p_input_item )
823 {
824     if(_inputItem == p_input_item)
825         return;
826
827     if(_inputItem)
828         vlc_gc_decref( _inputItem );
829
830     vlc_gc_incref( p_input_item );
831     _inputItem = p_input_item;
832 }
833
834 bool MediaServer::compareSID( const char* sid )
835 {
836     return ( strncmp( _subscriptionID, sid, sizeof( Upnp_SID ) ) == 0 );
837 }
838
839
840 // MediaServerList...
841
842 MediaServerList::MediaServerList( services_discovery_t* p_sd )
843 {
844     _p_sd = p_sd;
845 }
846
847 MediaServerList::~MediaServerList()
848 {
849     for ( unsigned int i = 0; i < _list.size(); i++ )
850     {
851         delete _list[i];
852     }
853 }
854
855 bool MediaServerList::addServer( MediaServer* s )
856 {
857     input_item_t* p_input_item = NULL;
858     if ( getServer( s->getUDN() ) != 0 ) return false;
859
860     msg_Dbg( _p_sd, "Adding server '%s'",
861             s->getFriendlyName() );
862
863     services_discovery_t* p_sd = _p_sd;
864
865     p_input_item = input_item_New( p_sd, "vlc://nop", s->getFriendlyName() );
866     s->setInputItem( p_input_item );
867
868     services_discovery_AddItem( p_sd, p_input_item, NULL );
869
870     _list.push_back( s );
871
872     return true;
873 }
874
875 MediaServer* MediaServerList::getServer( const char* UDN )
876 {
877     MediaServer* result = 0;
878
879     for ( unsigned int i = 0; i < _list.size(); i++ )
880     {
881         if( strcmp( UDN, _list[i]->getUDN() ) == 0 )
882         {
883             result = _list[i];
884             break;
885         }
886     }
887
888     return result;
889 }
890
891 MediaServer* MediaServerList::getServerBySID( const char* sid )
892 {
893     MediaServer* server = 0;
894
895     for ( unsigned int i = 0; i < _list.size(); i++ )
896     {
897         if ( _list[i]->compareSID( sid ) )
898         {
899             server = _list[i];
900             break;
901         }
902     }
903
904     return server;
905 }
906
907 void MediaServerList::removeServer( const char* UDN )
908 {
909     MediaServer* server = getServer( UDN );
910     if ( !server ) return;
911
912     msg_Dbg( _p_sd,
913             "Removing server '%s'", server->getFriendlyName() );
914
915     std::vector<MediaServer*>::iterator it;
916     for ( it = _list.begin(); it != _list.end(); it++ )
917     {
918         if ( *it == server )
919         {
920             _list.erase( it );
921             delete server;
922             break;
923         }
924     }
925 }
926
927
928 // Item...
929
930 Item::Item( Container* parent, const char* objectID, const char* title, const char* resource )
931 {
932     _parent = parent;
933
934     _objectID = objectID;
935     _title = title;
936     _resource = resource;
937
938     _inputItem = NULL;
939 }
940
941 Item::~Item()
942 {
943     if(_inputItem)
944         vlc_gc_decref( _inputItem );
945 }
946
947 const char* Item::getObjectID() const
948 {
949     return _objectID.c_str();
950 }
951
952 const char* Item::getTitle() const
953 {
954     return _title.c_str();
955 }
956
957 const char* Item::getResource() const
958 {
959     return _resource.c_str();
960 }
961
962 void Item::setInputItem( input_item_t* p_input_item )
963 {
964     if(_inputItem == p_input_item)
965         return;
966
967     if(_inputItem)
968         vlc_gc_decref( _inputItem );
969
970     vlc_gc_incref( p_input_item );
971     _inputItem = p_input_item;
972 }
973
974 input_item_t* Item::getInputItem() const
975 {
976     return _inputItem;
977 }
978
979
980 // Container...
981
982 Container::Container( Container*  parent,
983                       const char* objectID,
984                       const char* title )
985 {
986     _parent = parent;
987
988     _objectID = objectID;
989     _title = title;
990
991     _inputItem = NULL;
992 }
993
994 Container::~Container()
995 {
996     for ( unsigned int i = 0; i < _containers.size(); i++ )
997     {
998         delete _containers[i];
999     }
1000
1001     for ( unsigned int i = 0; i < _items.size(); i++ )
1002     {
1003         delete _items[i];
1004     }
1005
1006     if(_inputItem )
1007         vlc_gc_decref( _inputItem );
1008 }
1009
1010 void Container::addItem( Item* item )
1011 {
1012     _items.push_back( item );
1013 }
1014
1015 void Container::addContainer( Container* container )
1016 {
1017     _containers.push_back( container );
1018 }
1019
1020 const char* Container::getObjectID() const
1021 {
1022     return _objectID.c_str();
1023 }
1024
1025 const char* Container::getTitle() const
1026 {
1027     return _title.c_str();
1028 }
1029
1030 unsigned int Container::getNumItems() const
1031 {
1032     return _items.size();
1033 }
1034
1035 unsigned int Container::getNumContainers() const
1036 {
1037     return _containers.size();
1038 }
1039
1040 Item* Container::getItem( unsigned int i ) const
1041 {
1042     if ( i < _items.size() ) return _items[i];
1043     return 0;
1044 }
1045
1046 Container* Container::getContainer( unsigned int i ) const
1047 {
1048     if ( i < _containers.size() ) return _containers[i];
1049     return 0;
1050 }
1051
1052 Container* Container::getParent()
1053 {
1054     return _parent;
1055 }
1056
1057 void Container::setInputItem( input_item_t* p_input_item )
1058 {
1059     if(_inputItem == p_input_item)
1060         return;
1061
1062     if(_inputItem)
1063         vlc_gc_decref( _inputItem );
1064
1065     vlc_gc_incref( p_input_item );
1066     _inputItem = p_input_item;
1067 }
1068
1069 input_item_t* Container::getInputItem() const
1070 {
1071     return _inputItem;
1072 }