]> git.sesse.net Git - vlc/blob - modules/services_discovery/bonjour.c
Don't print a message a malloc failed.
[vlc] / modules / services_discovery / bonjour.c
1 /*****************************************************************************
2  * bonjour.c: Bonjour services discovery module
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon@nanocrew.net>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Includes
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_playlist.h>
35 #include <vlc_arrays.h>
36
37 #include <avahi-client/client.h>
38 #ifdef HAVE_AVAHI_06
39 # include <avahi-client/publish.h>
40 # include <avahi-client/lookup.h>
41 #endif
42 #include <avahi-common/simple-watch.h>
43 #include <avahi-common/malloc.h>
44 #include <avahi-common/error.h>
45
46 /*****************************************************************************
47  * Module descriptor
48  *****************************************************************************/
49
50 /* Callbacks */
51     static int  Open ( vlc_object_t * );
52     static void Close( vlc_object_t * );
53
54 vlc_module_begin();
55     set_shortname( "Bonjour" );
56     set_description( N_("Bonjour services") );
57     set_category( CAT_PLAYLIST );
58     set_subcategory( SUBCAT_PLAYLIST_SD );
59     set_capability( "services_discovery", 0 );
60     set_callbacks( Open, Close );
61 vlc_module_end();
62
63 /*****************************************************************************
64  * Local structures
65  *****************************************************************************/
66
67 struct services_discovery_sys_t
68 {
69     AvahiSimplePoll     *simple_poll;
70     AvahiClient         *client;
71     AvahiServiceBrowser *sb;
72     vlc_dictionary_t    services_name_to_input_item;
73 };
74
75 /*****************************************************************************
76  * Local prototypes
77  *****************************************************************************/
78
79 /* Main functions */
80     static void Run    ( services_discovery_t *p_intf );
81
82 /*****************************************************************************
83  * client_callback
84  *****************************************************************************/
85 static void client_callback( AvahiClient *c, AvahiClientState state,
86                              void * userdata )
87 {
88     services_discovery_t *p_sd = ( services_discovery_t* )userdata;
89     services_discovery_sys_t *p_sys = p_sd->p_sys;
90
91 #ifdef HAVE_AVAHI_06
92     if( state == AVAHI_CLIENT_FAILURE &&
93         (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) )
94 #else
95     if( state == AVAHI_CLIENT_DISCONNECTED )
96 #endif
97     {
98         msg_Err( p_sd, "avahi client disconnected" );
99         avahi_simple_poll_quit( p_sys->simple_poll );
100     }
101 }
102
103 /*****************************************************************************
104  * resolve_callback
105  *****************************************************************************/
106 static void resolve_callback(
107     AvahiServiceResolver *r,
108     AvahiIfIndex interface,
109     AvahiProtocol protocol,
110     AvahiResolverEvent event,
111     const char *name,
112     const char *type,
113     const char *domain,
114     const char *host_name,
115     const AvahiAddress *address,
116     uint16_t port,
117     AvahiStringList *txt,
118 #ifdef HAVE_AVAHI_06
119     AvahiLookupResultFlags flags,
120 #endif
121     void* userdata )
122 {
123     services_discovery_t *p_sd = ( services_discovery_t* )userdata;
124     services_discovery_sys_t *p_sys = p_sd->p_sys;
125     
126     VLC_UNUSED(interface); VLC_UNUSED(host_name);
127 #ifdef HAVE_AVAHI_06
128     VLC_UNUSED(flags);
129 #endif
130
131 #ifdef HAVE_AVAHI_06
132     if( event == AVAHI_RESOLVER_FAILURE )
133 #else
134     if( event == AVAHI_RESOLVER_TIMEOUT )
135 #endif
136     {
137         msg_Err( p_sd,
138                  "failed to resolve service '%s' of type '%s' in domain '%s'",
139                  name, type, domain );
140     }
141     else if( event == AVAHI_RESOLVER_FOUND )
142     {
143         char a[128];
144         char *psz_uri = NULL;
145         char *psz_addr = NULL;
146         AvahiStringList *asl = NULL;
147         input_item_t *p_input = NULL;
148
149         msg_Dbg( p_sd, "service '%s' of type '%s' in domain '%s'",
150                  name, type, domain );
151
152         avahi_address_snprint(a, (sizeof(a)/sizeof(a[0]))-1, address);
153         if( protocol == AVAHI_PROTO_INET6 )
154             if( asprintf( &psz_addr, "[%s]", a ) == -1 )
155                 return;
156
157         if( txt != NULL )
158             asl = avahi_string_list_find( txt, "path" );
159         if( asl != NULL )
160         {
161             size_t size;
162             char *key = NULL;
163             char *value = NULL;
164             if( avahi_string_list_get_pair( asl, &key, &value, &size ) == 0 &&
165                 value != NULL )
166             {
167                 if( asprintf( &psz_uri, "http://%s:%d%s",
168                           psz_addr != NULL ? psz_addr : a, port, value ) == -1 )
169                 {
170                     free( psz_addr );
171                     return;
172                 }
173             }
174             if( key != NULL )
175                 avahi_free( (void *)key );
176             if( value != NULL )
177                 avahi_free( (void *)value );
178         }
179         else
180         {
181             if( asprintf( &psz_uri, "http://%s:%d",
182                       psz_addr != NULL ? psz_addr : a, port ) == -1 )
183             {
184                 free( psz_addr );
185                 return;
186             }
187         }
188
189         if( psz_addr != NULL )
190             free( (void *)psz_addr );
191
192         if( psz_uri != NULL )
193         {
194             p_input = input_ItemNewExt( p_sd, psz_uri, name, 0, NULL, -1 );
195             free( (void *)psz_uri );
196         }
197         if( p_input != NULL )
198         {
199             vlc_dictionary_insert( &p_sys->services_name_to_input_item,
200                 name, p_input );
201             services_discovery_AddItem( p_sd, p_input, NULL /* no category */ );
202             vlc_gc_decref( p_input );
203        }
204     }
205
206     avahi_service_resolver_free( r );
207 }
208
209 /*****************************************************************************
210  * browser_callback
211  *****************************************************************************/
212 static void browse_callback(
213     AvahiServiceBrowser *b,
214     AvahiIfIndex interface,
215     AvahiProtocol protocol,
216     AvahiBrowserEvent event,
217     const char *name,
218     const char *type,
219     const char *domain,
220 #ifdef HAVE_AVAHI_06
221     AvahiLookupResultFlags flags,
222 #endif
223     void* userdata )
224 {
225     VLC_UNUSED(b);
226 #ifdef HAVE_AVAHI_06
227     VLC_UNUSED(flags);
228 #endif
229     services_discovery_t *p_sd = ( services_discovery_t* )userdata;
230     services_discovery_sys_t *p_sys = p_sd->p_sys;
231     if( event == AVAHI_BROWSER_NEW )
232     {
233         if( avahi_service_resolver_new( p_sys->client, interface, protocol,
234                                         name, type, domain, AVAHI_PROTO_UNSPEC,
235 #ifdef HAVE_AVAHI_06
236                                         0,
237 #endif
238                                         resolve_callback, userdata ) == NULL )
239         {
240             msg_Err( p_sd, "failed to resolve service '%s': %s", name,
241                      avahi_strerror( avahi_client_errno( p_sys->client ) ) );
242         }
243     }
244     else if( name )
245     {
246         /** \todo Store the input id and search it, rather than searching the items */
247         input_item_t *p_item;
248         p_item = vlc_dictionary_value_for_key(
249                         &p_sys->services_name_to_input_item,
250                         name );
251         if( !p_item )
252             msg_Err( p_sd, "failed to find service '%s' in playlist", name );
253         else
254         {
255             services_discovery_RemoveItem( p_sd, p_item );
256             vlc_dictionary_remove_value_for_key(
257                         &p_sys->services_name_to_input_item,
258                         name );
259         }
260     }
261 }
262
263 /*****************************************************************************
264  * Open: initialize and create stuff
265  *****************************************************************************/
266 static int Open( vlc_object_t *p_this )
267 {
268     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
269     services_discovery_sys_t *p_sys;
270     int err;
271
272     p_sd->p_sys = p_sys = (services_discovery_sys_t *)malloc(
273         sizeof( services_discovery_sys_t ) );
274
275     if( !p_sys )
276         return VLC_ENOMEM;
277
278     memset( p_sys, 0, sizeof(*p_sys) );
279
280     vlc_dictionary_init( &p_sys->services_name_to_input_item, 1 );
281
282     p_sys->simple_poll = avahi_simple_poll_new();
283     if( p_sys->simple_poll == NULL )
284     {
285         msg_Err( p_sd, "failed to create avahi simple poll" );
286         goto error;
287     }
288
289     p_sys->client = avahi_client_new( avahi_simple_poll_get(p_sys->simple_poll),
290 #ifdef HAVE_AVAHI_06
291                                       0,
292 #endif
293                                       client_callback, p_sd, &err );
294     if( p_sys->client == NULL )
295     {
296         msg_Err( p_sd, "failed to create avahi client: %s",
297                  avahi_strerror( err ) );
298         goto error;
299     }
300
301     p_sys->sb = avahi_service_browser_new( p_sys->client, AVAHI_IF_UNSPEC,
302                                            AVAHI_PROTO_UNSPEC,
303                                            "_vlc-http._tcp", NULL,
304 #ifdef HAVE_AVAHI_06
305                                            0,
306 #endif
307                                            browse_callback, p_sd );
308     if( p_sys->sb == NULL )
309     {
310         msg_Err( p_sd, "failed to create avahi service browser" );
311         goto error;
312     }
313
314     services_discovery_SetLocalizedName( p_sd, _("Bonjour") );
315
316     p_sd->pf_run = Run;
317
318     return VLC_SUCCESS;
319
320 error:
321     if( p_sys->sb != NULL )
322         avahi_service_browser_free( p_sys->sb );
323     if( p_sys->client != NULL )
324         avahi_client_free( p_sys->client );
325     if( p_sys->simple_poll != NULL )
326         avahi_simple_poll_free( p_sys->simple_poll );
327
328     vlc_dictionary_clear( &p_sys->services_name_to_input_item );
329     free( p_sys );
330
331     return VLC_EGENERIC;
332 }
333
334 /*****************************************************************************
335  * Close: cleanup
336  *****************************************************************************/
337 static void Close( vlc_object_t *p_this )
338 {
339     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
340     services_discovery_sys_t *p_sys = p_sd->p_sys;
341
342     avahi_service_browser_free( p_sys->sb );
343     avahi_client_free( p_sys->client );
344     avahi_simple_poll_free( p_sys->simple_poll );
345
346     vlc_dictionary_clear( &p_sys->services_name_to_input_item );
347     free( p_sys );
348 }
349
350 /*****************************************************************************
351  * Run: main thread
352  *****************************************************************************/
353 static void Run( services_discovery_t *p_sd )
354 {
355     services_discovery_sys_t *p_sys = p_sd->p_sys;
356
357     while( vlc_object_alive (p_sd) )
358     {
359         if( avahi_simple_poll_iterate( p_sys->simple_poll, 100 ) != 0 )
360         {
361             msg_Err( p_sd, "poll iterate failed" );
362             break;
363         }
364     }
365 }