Fix a bunch of warnings and errors
[vlc.git] / modules / services_discovery / daap.c
1 /*****************************************************************************
2  * daap.c :  Apple DAAP discovery module
3  *****************************************************************************
4  * Copyright (C) 2004 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
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 #include <stdlib.h>                                      /* malloc(), free() */
28
29 #include <vlc/vlc.h>
30 #include <vlc/intf.h>
31
32 #include "vlc_url.h"
33
34 #include <vlc/input.h>
35
36 #include <daap/client.h>
37
38 /************************************************************************
39  * Macros and definitions
40  ************************************************************************/
41
42 /*****************************************************************************
43  * Module descriptor
44  *****************************************************************************/
45
46 /* Callbacks */
47     static int  Open ( vlc_object_t * );
48     static void Close( vlc_object_t * );
49     static int  OpenAccess ( vlc_object_t * );
50     static void CloseAccess( vlc_object_t * );
51
52 vlc_module_begin();
53     set_description( _("DAAP shares") );
54     set_category( CAT_PLAYLIST );
55     set_subcategory( SUBCAT_PLAYLIST_SD );
56
57     set_capability( "services_discovery", 0 );
58     set_callbacks( Open, Close );
59
60     add_submodule();
61         set_description( _( "DAAP access") );
62         set_capability( "access2", 0 );
63         set_callbacks( OpenAccess, CloseAccess );
64 vlc_module_end();
65
66
67 /*****************************************************************************
68  * Local structures
69  *****************************************************************************/
70
71 typedef struct dhost_s {
72     char *psz_name;
73     int i_id;
74
75     DAAP_SClientHost *p_host;
76     vlc_bool_t b_updated;
77     vlc_bool_t b_new;
78     int i_database_id;
79
80     playlist_item_t *p_node;
81
82     DAAP_ClientHost_DatabaseItem *p_songs;
83     int i_songs;
84 } dhost_t;
85
86 typedef struct daap_db_s {
87     dhost_t **pp_hosts;
88     int       i_hosts;
89
90     int i_last_id;
91
92     vlc_mutex_t search_lock;
93 } daap_db_t;
94
95 struct services_discovery_sys_t {
96     playlist_item_t *p_node;
97
98     DAAP_SClient *p_client;
99     DAAP_SClientHost *p_host;
100
101     daap_db_t *p_db;
102 };
103
104 struct access_sys_t {
105     vlc_url_t url;
106
107     dhost_t *p_host;
108     int i_host;
109     int i_song;
110
111     daap_db_t *p_db;
112
113     DAAP_ClientHost_Song song;
114     DAAP_ClientHost_DatabaseItem songdata;
115     int i_orig_size;
116     void *p_orig_buffer;
117 };
118
119 /*****************************************************************************
120  * Local prototypes
121  *****************************************************************************/
122
123 /* Main functions */
124     static void Run    ( services_discovery_t *p_sd );
125     static void Callback( DAAP_SClient *p_client, DAAP_Status status,
126                           int i_pos, void *p_context );
127     static int EnumerateCallback( DAAP_SClient *p_client,
128                                   DAAP_SClientHost *p_host,
129                                   void *p_context );
130     static void OnHostsUpdate( services_discovery_t *p_sd );
131     static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host );
132     static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host );
133
134     static int Control( access_t *p_access, int i_query, va_list args );
135     static int Read( access_t *, uint8_t *, int );
136     static int Seek( access_t *, int64_t );
137
138 /*****************************************************************************
139  * Open: initialize and create stuff
140  *****************************************************************************/
141 static int Open( vlc_object_t *p_this )
142 {
143     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
144     services_discovery_sys_t *p_sys  = (services_discovery_sys_t *)
145                                 malloc( sizeof( services_discovery_sys_t ) );
146
147     playlist_t          *p_playlist;
148     playlist_view_t     *p_view;
149     vlc_value_t         val;
150
151     p_sd->pf_run = Run;
152     p_sd->p_sys  = p_sys;
153
154     p_sys->p_db = (daap_db_t *)malloc( sizeof( daap_db_t ) );
155     if( !p_sys->p_db )
156     {
157         return VLC_EGENERIC;
158     }
159     p_sys->p_db->pp_hosts = NULL;
160     p_sys->p_db->i_hosts = 0;
161
162     var_Create( p_sd->p_vlc, "daap-db", VLC_VAR_ADDRESS );
163     val.p_address = p_sys->p_db;
164     var_Set( p_sd->p_vlc, "daap-db", val );
165
166     vlc_mutex_init( p_sd, &p_sys->p_db->search_lock );
167
168     /* Init DAAP */
169     p_sys->p_client = DAAP_Client_Create( Callback, p_sd );
170     p_sys->p_db->i_last_id = 0;
171
172     /* TODO: Set debugging correctly */
173 //    DAAP_Client_SetDebug( p_sys->p_client, "+trace" );
174
175
176     /* Create our playlist node */
177     p_playlist = (playlist_t *)vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
178                                                 FIND_ANYWHERE );
179     if( !p_playlist )
180     {
181         msg_Warn( p_sd, "unable to find playlist, cancelling DAAP" );
182         return VLC_EGENERIC;
183     }
184     msg_Err( p_sd, "DAAP IS BROKEN !! Fix it if you want it !" );
185     return VLC_EGENERIC;
186
187 #if 0
188     p_sys->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
189                                          _("DAAP shares"), p_view->p_root );
190     p_sys->p_node->i_flags |= PLAYLIST_RO_FLAG;
191
192     val.b_bool = VLC_TRUE;
193     var_Set( p_playlist, "intf-change", val );
194     vlc_object_release( p_playlist );
195
196     return VLC_SUCCESS;
197 #endif
198 }
199
200 static int OpenAccess( vlc_object_t *p_this )
201 {
202 #if 0
203     access_t     *p_access = (access_t*)p_this;
204     access_sys_t *p_sys;
205     vlc_value_t val;
206     vlc_bool_t b_found = VLC_FALSE;
207     int i, i_ret;
208
209     p_access->pf_read = Read;
210     p_access->pf_block = NULL;
211     p_access->pf_control = Control;
212     p_access->pf_seek = Seek;
213     p_access->info.i_update = 0;
214     p_access->info.i_size = 0;
215     p_access->info.i_pos = 0;
216     p_access->info.b_eof = VLC_FALSE;
217     p_access->info.i_title = 0;
218     p_access->info.i_seekpoint = 0;
219     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
220     memset( p_sys, 0, sizeof( access_sys_t ) );
221
222     i_ret = var_Get( p_access->p_vlc , "daap-db", &val );
223     p_sys->p_db = val.p_address;
224
225     if( p_sys->p_db == NULL || i_ret )
226     {
227         msg_Err( p_access, "the DAAP services_discovery module must be enabled" );
228         return VLC_EGENERIC;
229     }
230
231     vlc_UrlParse( &p_sys->url, p_access->psz_path, 0 );
232
233     p_sys->p_host = NULL;
234     p_sys->i_host = atoi( p_sys->url.psz_host ) ;
235     p_sys->i_song = p_sys->url.i_port;
236
237     if( !p_sys->i_host || !p_sys->i_song )
238     {
239         msg_Err( p_access, "invalid host or song" );
240         return VLC_EGENERIC;
241     }
242
243     /* Search the host */
244     vlc_mutex_lock( &p_sys->p_db->search_lock );
245     for( i = 0 ; i < p_sys->p_db->i_hosts ; i++ )
246     {
247         if( p_sys->p_db->pp_hosts[i]->i_id == p_sys->i_host )
248         {
249             p_sys->p_host = p_sys->p_db->pp_hosts[i];
250             break;
251         }
252     }
253     if( p_sys->p_host )
254     {
255        for( i = 0 ; i < p_sys->p_host->i_songs ; i++ )
256        {
257            if( p_sys->p_host->p_songs[i].id == p_sys->i_song )
258            {
259                p_sys->songdata = p_sys->p_host->p_songs[i];
260                b_found = VLC_TRUE;
261                break;
262            }
263        }
264        if( !b_found )
265        {
266            msg_Err( p_access, "invalid song (not found in %i)",
267                              p_sys->p_host->i_songs );
268        }
269     }
270     else
271     {
272         msg_Warn( p_access, "invalid host (not found in %i)",
273                              p_sys->p_db->i_hosts );
274     }
275     vlc_mutex_unlock( &p_sys->p_db->search_lock );
276
277     if( !p_sys->p_host || !b_found )
278     {
279         return VLC_EGENERIC;
280     }
281
282
283     msg_Dbg( p_access, "downloading %s song %i (db %i)",
284                            p_sys->songdata.songformat,
285                            p_sys->i_song, p_sys->p_host->i_database_id );
286
287     /* FIXME: wait for better method by upstream */
288     i_ret = DAAP_ClientHost_GetAudioFile( p_sys->p_host->p_host,
289                                           p_sys->p_host->i_database_id,
290                                           p_sys->i_song,
291                                           p_sys->songdata.songformat,
292                                           &(p_sys->song) );
293
294     msg_Dbg( p_access, "finished downloading, read %i bytes (ret %i)",
295                                           p_sys->song.size, i_ret );
296
297     p_access->info.i_size = p_sys->song.size;
298
299     if( i_ret != 0 )
300         return VLC_EGENERIC;
301 #endif
302     return VLC_SUCCESS;
303 }
304
305 /*****************************************************************************
306  * Close:
307  *****************************************************************************/
308 static void Close( vlc_object_t *p_this )
309 {
310 #if 0
311     services_discovery_t *p_sd = ( services_discovery_t* )p_this;
312     services_discovery_sys_t    *p_sys  = p_sd->p_sys;
313
314     playlist_t *p_playlist;
315     int i;
316
317     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
318                                                  FIND_ANYWHERE );
319
320     for( i = 0 ; i< p_sys->p_db->i_hosts ; i++ )
321     {
322         FreeHost( p_sd, p_sys->p_db->pp_hosts[i] );
323     }
324
325     var_Destroy( p_sd->p_vlc, "daap-db" );
326
327     if( p_playlist )
328     {
329         playlist_NodeDelete( p_playlist, p_sys->p_node, VLC_TRUE, VLC_TRUE );
330         vlc_object_release( p_playlist );
331     }
332
333     free( p_sys );
334 #endif
335 }
336
337 static void CloseAccess( vlc_object_t *p_this )
338 {
339     access_t *p_access = (access_t*) p_this;
340     access_sys_t *p_sys = p_access->p_sys;
341
342     if( p_sys )
343     {
344         if( p_sys->p_host )
345         {
346             p_sys->song.data = p_sys->p_orig_buffer;
347             p_sys->song.size = p_sys->i_orig_size;
348             DAAP_ClientHost_FreeAudioFile( p_sys->p_host->p_host, &p_sys->song );
349         }
350         free( p_sys );
351     }
352 }
353
354 /*****************************************************************************
355  * Run: main DAAP thread
356  *****************************************************************************/
357 static void Run( services_discovery_t *p_sd )
358 {
359     while( !p_sd->b_die )
360     {
361         msleep( 100000 );
362     }
363 }
364
365 /*****************************************************************************
366  * Access functions
367  *****************************************************************************/
368 static int Control( access_t *p_access, int i_query, va_list args )
369 {
370     vlc_bool_t *pb_bool;
371     int64_t *pi_64;
372     switch( i_query )
373     {
374         case ACCESS_CAN_SEEK:
375         case ACCESS_CAN_FASTSEEK:
376             pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
377             *pb_bool = VLC_TRUE;
378             break;
379         case ACCESS_CAN_PAUSE:
380         case ACCESS_CAN_CONTROL_PACE:
381             pb_bool = (vlc_bool_t *)va_arg( args, vlc_bool_t *);
382             *pb_bool = VLC_TRUE;
383             break;
384
385         case ACCESS_GET_PTS_DELAY:
386             pi_64 = (int64_t *)va_arg( args, int64_t *);
387             *pi_64 = (int64_t)300000;
388             break;
389
390         case ACCESS_SET_PAUSE_STATE:
391             break;
392
393         case ACCESS_GET_TITLE_INFO:
394         case ACCESS_SET_TITLE:
395         case ACCESS_SET_SEEKPOINT:
396         case ACCESS_SET_PRIVATE_ID_STATE:
397             return VLC_EGENERIC;
398
399         default:
400             msg_Warn( p_access, "unimplemented query control %i", i_query );
401             return VLC_EGENERIC;
402     }
403     return VLC_SUCCESS;
404 }
405
406 static int Read( access_t *p_access, uint8_t *p_buffer, int i_size )
407 {
408     access_sys_t *p_sys = (access_sys_t *)p_access->p_sys;
409     int i_send;
410
411     if( i_size < p_sys->song.size && p_sys->song.size > 0 )
412     {
413         i_send = i_size;
414     }
415     else if( p_sys->song.size == 0 )
416     {
417         return 0;
418     }
419     else
420     {
421         i_send = p_sys->song.size;
422     }
423
424     memcpy( p_buffer, p_sys->song.data, i_send );
425     p_sys->song.size -= i_send;
426     p_sys->song.data += i_send;
427
428     return i_send;
429 }
430
431 static int Seek( access_t *p_access, int64_t i_pos )
432 {
433     if( i_pos > p_access->p_sys->i_orig_size )
434     {
435         return VLC_EGENERIC;
436     }
437     p_access->p_sys->song.size = p_access->p_sys->i_orig_size - i_pos;
438     p_access->p_sys->song.data = p_access->p_sys->p_orig_buffer + i_pos;
439     return VLC_SUCCESS;
440 }
441
442 /**************************************************************
443  * Local functions
444  **************************************************************/
445 static void Callback( DAAP_SClient *p_client, DAAP_Status status,
446                       int i_pos, void *p_context )
447 {
448     services_discovery_t *p_sd = (services_discovery_t *)p_context;
449
450     if( status == DAAP_STATUS_hostschanged )
451     {
452         OnHostsUpdate( p_sd );
453     }
454     else if( status == DAAP_STATUS_downloading )
455     {
456     }
457 }
458
459 static void OnHostsUpdate( services_discovery_t *p_sd )
460 {
461     int i;
462
463     for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
464     {
465         p_sd->p_sys->p_db->pp_hosts[i]->b_updated = VLC_FALSE;
466         p_sd->p_sys->p_db->pp_hosts[i]->b_new     = VLC_FALSE;
467     }
468
469     vlc_mutex_lock( &p_sd->p_sys->p_db->search_lock );
470     DAAP_Client_EnumerateHosts( p_sd->p_sys->p_client, EnumerateCallback, p_sd);
471
472     for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
473     {
474         if( p_sd->p_sys->p_db->pp_hosts[i]->b_updated == VLC_FALSE )
475         {
476             dhost_t *p_host = p_sd->p_sys->p_db->pp_hosts[i];
477             FreeHost( p_sd, p_host );
478             REMOVE_ELEM( p_sd->p_sys->p_db->pp_hosts,
479                          p_sd->p_sys->p_db->i_hosts, i );
480         }
481     }
482     vlc_mutex_unlock( &p_sd->p_sys->p_db->search_lock );
483
484     for( i = 0 ; i< p_sd->p_sys->p_db->i_hosts ; i ++ )
485     {
486         if( p_sd->p_sys->p_db->pp_hosts[i]->b_new )
487             ProcessHost( p_sd, p_sd->p_sys->p_db->pp_hosts[i] );
488     }
489 }
490
491 static int EnumerateCallback( DAAP_SClient *p_client,
492                               DAAP_SClientHost *p_host,
493                               void *p_context )
494 {
495     int i;
496     int i_size = DAAP_ClientHost_GetSharename( p_host, NULL, 0 );
497     vlc_bool_t b_found = VLC_FALSE;
498     char *psz_buffer = (char *)malloc( i_size );
499     DAAP_ClientHost_GetSharename( p_host, psz_buffer, i_size );
500
501     services_discovery_t *p_sd = (services_discovery_t *)p_context;
502     services_discovery_sys_t *p_sys = p_sd->p_sys;
503
504     for( i = 0 ; i< p_sys->p_db->i_hosts; i++ )
505     {
506         if( !strcmp( p_sys->p_db->pp_hosts[i]->psz_name, psz_buffer ) )
507         {
508             p_sys->p_db->pp_hosts[i]->b_updated = VLC_TRUE;
509             b_found = VLC_TRUE;
510             break;
511         }
512     }
513
514     if( !b_found )
515     {
516         dhost_t *p_vlchost = (dhost_t *)malloc( sizeof( dhost_t ) );
517         p_vlchost->p_node = NULL;
518         p_vlchost->p_host = p_host;
519         p_vlchost->psz_name = psz_buffer;
520         p_vlchost->b_new = VLC_TRUE;
521         p_vlchost->b_updated = VLC_TRUE;
522         INSERT_ELEM( p_sys->p_db->pp_hosts, p_sys->p_db->i_hosts,
523                      p_sys->p_db->i_hosts, p_vlchost );
524     }
525
526     return VLC_SUCCESS;
527 }
528
529 static void ProcessHost( services_discovery_t *p_sd, dhost_t *p_host )
530 {
531 #if 0
532     int i_dbsize, i_db, i, i_songsize, i_ret;
533     int i_size = DAAP_ClientHost_GetSharename( p_host->p_host, NULL, 0 );
534
535     playlist_t *p_playlist;
536
537     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
538                                                        FIND_ANYWHERE );
539
540     if( !p_playlist )
541     {
542         return;
543     }
544
545     /* Connect to host */
546     if( p_host->b_new )
547     {
548         p_host->psz_name = (char *)malloc( i_size );
549         p_host->b_new = VLC_FALSE;
550         DAAP_ClientHost_GetSharename( p_host->p_host, p_host->psz_name ,
551                                       i_size );
552
553         msg_Dbg( p_sd, "new share %s", p_host->psz_name );
554         DAAP_ClientHost_AddRef( p_host->p_host );
555         i_ret = DAAP_ClientHost_Connect( p_host->p_host );
556         if( i_ret )
557         {
558             msg_Warn( p_sd, "unable to connect to DAAP host %s",
559                              p_host->psz_name );
560 //            DAAP_ClientHost_Release( p_host->p_host );
561             vlc_object_release( p_playlist );
562             return;
563         }
564
565         p_host->p_node = playlist_NodeCreate( p_playlist, VIEW_CATEGORY,
566                                               p_host->psz_name,
567                                               p_sd->p_sys->p_node );
568         p_host->i_id = ++p_sd->p_sys->p_db->i_last_id;
569     }
570
571     /* Get DB */
572     i_dbsize = DAAP_ClientHost_GetDatabases( p_host->p_host, NULL, NULL, 0 );
573
574     DAAP_ClientHost_Database *p_database = malloc( i_dbsize );
575     DAAP_ClientHost_GetDatabases( p_host->p_host, p_database, &i_db, i_dbsize );
576
577
578     if( !i_db || !p_database )
579     {
580         msg_Warn( p_sd, "no database on DAAP host %s", p_host->psz_name );
581         vlc_object_release( p_playlist );
582         return;
583     }
584
585     /* We only use the first database */
586     p_host->i_database_id = p_database[0].id;
587
588     /* Get songs */
589     i_songsize = DAAP_ClientHost_GetDatabaseItems( p_host->p_host,
590                                                    p_host->i_database_id,
591                                                    NULL, NULL, 0 );
592     if( !i_songsize )
593     {
594         vlc_object_release( p_playlist );
595         return;
596     }
597     p_host->p_songs = malloc( i_songsize );
598
599     DAAP_ClientHost_GetDatabaseItems( p_host->p_host ,
600                                       p_host->i_database_id,
601                                       p_host->p_songs,
602                                       &p_host->i_songs, i_songsize );
603
604     for( i = 0; i< p_host->i_songs; i++ )
605     {
606         playlist_item_t *p_item;
607         int i_len = 7 + 10 + 1 + 10 ;    /* "daap://" + host + ":" + song */
608         char *psz_buff = (char *)malloc( i_len );
609
610         snprintf( psz_buff, i_len, "daap://%i:%i", p_host->i_id,
611                                                    p_host->p_songs[i].id );
612         p_item = playlist_ItemNew( p_sd, psz_buff,
613                                          p_host->p_songs[i].itemname );
614         vlc_input_item_AddInfo( &p_item->input, _(VLC_META_INFO_CAT),
615                                 _(VLC_META_ARTIST), p_host->p_songs[i].songartist );
616         vlc_input_item_AddInfo( &p_item->input, _(VLC_META_INFO_CAT),
617                                 _(VLC_META_COLLECTION), p_host->p_songs[i].songalbum );
618
619         playlist_NodeAddItem( p_playlist, p_item, VIEW_CATEGORY,
620                               p_host->p_node, PLAYLIST_APPEND, PLAYLIST_END );
621
622     }
623
624     DAAP_ClientHost_AsyncWaitUpdate( p_host->p_host );
625
626     vlc_object_release( p_playlist );
627 #endif
628 }
629
630 static void FreeHost( services_discovery_t *p_sd, dhost_t *p_host )
631 {
632     playlist_t *p_playlist;
633
634     if( p_host->p_host )
635     {
636         DAAP_ClientHost_Disconnect( p_host->p_host );
637         DAAP_ClientHost_Release( p_host->p_host );
638     }
639
640     p_playlist = (playlist_t *) vlc_object_find( p_sd, VLC_OBJECT_PLAYLIST,
641                                                  FIND_ANYWHERE );
642     if( p_playlist )
643     {
644         if( p_host->p_node )
645             playlist_NodeDelete( p_playlist, p_host->p_node, VLC_TRUE ,
646                                                              VLC_TRUE);
647         vlc_object_release( p_playlist );
648     }
649
650     if( p_host->p_songs ) free( p_host->p_songs );
651 }