Don't Close() on connection error, retry every minute
[vlc.git] / modules / misc / audioscrobbler.c
1 /*****************************************************************************
2  * audioscrobbler.c : audioscrobbler submission plugin
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Rafaël Carré <rafael -dot- carre -at- gmail -dot- com>
8  *          Kenneth Ostby <kenneo -at- idi -dot- ntnu -dot- no>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #define _GNU_SOURCE
30 #include <string.h>
31
32 #if !defined( strlwr ) && !defined( WIN32 )
33 #include <ctype.h>
34 #endif
35
36 #if defined( WIN32 )
37 #include <time.h>
38 #endif
39 /*
40  * TODO :
41  * implement musicbrainz unique track identifier in p_meta
42  * check meta_engine's state, and remove delaying of metadata reading
43  * check md5 operations on BIGENDIAN and 64 bits architectures
44  */
45 #include <vlc/vlc.h>
46 #include <vlc/intf.h>
47 #include <vlc_meta.h>
48 #include <vlc_md5.h>
49 #include <vlc_block.h>
50 #include <vlc_stream.h>
51 #include <vlc_url.h>
52 #include <network.h>
53 #include <vlc_interaction.h>
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58
59 /* Keeps track of metadata to be submitted, and if song has been submitted */
60 typedef struct audioscrobbler_song_t
61 {
62     char        *psz_a;                /* track artist     */
63     char        *psz_t;                /* track title      */
64     char        *psz_b;                /* track album      */
65     int         i_l;                   /* track length     */
66 /* vlc can't retrieve musicbrainz id, so let's ignore it   */
67 /*  int         i_m;  */               /* musicbrainz id   */
68     char        *psz_i;                /* date             */
69     time_t      time_playing;          /* date (epoch)     */
70 } audioscrobbler_song_t;
71
72
73 /* Queue to be submitted to server, 10 songs max */
74 typedef struct audioscrobbler_queue_t
75 {
76     audioscrobbler_song_t   **p_queue;      /* contains up to 10 songs        */
77     int                     i_songs_nb;     /* number of songs                */
78     void                    *p_next_queue;  /* if queue full, pointer to next */
79 } audioscrobbler_queue_t;
80
81 struct intf_sys_t
82 {
83     audioscrobbler_queue_t  *p_first_queue;     /* 1st queue              */
84     vlc_mutex_t             lock;               /* p_sys mutex            */
85
86 /* data about audioscrobbler session */
87     int                     i_interval;         /* last interval recorded */
88     time_t                  time_last_interval; /* when was it recorded ? */
89     char                    *psz_submit_host;   /* where to submit data ? */
90     int                     i_submit_port;      /* at which port ?        */
91     char                    *psz_submit_file;   /* in which file ?        */
92     char                    *psz_username;      /* last.fm username       */
93     vlc_bool_t              b_handshaked;       /* did we handshake ?     */
94     int                     i_post_socket;      /* socket for submission  */
95     char                    *psz_response_md5;  /* md5 response to use    */
96
97 /* data about input elements */
98     audioscrobbler_song_t   *p_current_song;    /* song being played      */
99     time_t                  time_pause;         /* time when vlc paused   */
100     time_t                  time_total_pauses;  /* sum of time in pause   */
101     vlc_bool_t              b_queued;           /* has it been queud ?    */
102     vlc_bool_t              b_metadata_read;    /* did we read metadata ? */
103     vlc_bool_t              b_paused;           /* are we playing ?       */
104     vlc_bool_t              b_waiting_meta;     /* we need fetched data?  */
105 };
106
107 intf_sys_t *p_sys_global;     /* for use same p_sys in all threads */
108
109 static int  Open    ( vlc_object_t * );
110 static void Close   ( vlc_object_t * );
111 static void Run     ( intf_thread_t * );
112 static int ItemChange   ( vlc_object_t *, const char *,
113                         vlc_value_t, vlc_value_t, void * );
114 static int PlayingChange( vlc_object_t *, const char *,
115                         vlc_value_t, vlc_value_t, void * );
116 static int AddToQueue   ( intf_thread_t *p_this );
117 static int Handshake    ( intf_thread_t *p_sd );
118 static int ReadMetaData ( intf_thread_t *p_this );
119 static int ReadLocalMetaData( intf_thread_t *p_this, input_thread_t  *p_input );
120 void DeleteQueue( audioscrobbler_queue_t *p_queue );
121
122 #if !defined(strlwr) && !defined( WIN32 )
123 char* strlwr(char *psz_string);
124 #endif
125
126 /*****************************************************************************
127  * Module descriptor
128  ****************************************************************************/
129
130
131 #define APPLICATION_NAME "VLC media player"
132 #define USERNAME_TEXT N_("Username")
133 #define USERNAME_LONGTEXT N_("The username of your last.fm account")
134 #define PASSWORD_TEXT N_("Password")
135 #define PASSWORD_LONGTEXT N_("The password of your last.fm account")
136 /* if something goes wrong, we wait at least one minute before trying again */
137 #define DEFAULT_INTERVAL 60
138 #define CLIENT_NAME     PACKAGE
139 #define CLIENT_VERSION  VERSION
140
141 /* HTTP POST request : to submit data */
142 #define    POST_REQUEST "POST /%s HTTP/1.1\n"                               \
143                         "Accept-Encoding: identity\n"                       \
144                         "Content-length: %d\n"                              \
145                         "Connection: close\n"                               \
146                         "Content-type: application/x-www-form-urlencoded\n" \
147                         "Host: %s\n"                                        \
148                         "User-agent: VLC Media Player/%s\r\n"               \
149                         "\r\n"                                              \
150                         "%s\r\n"                                            \
151                         "\r\n"
152
153 /* data to submit */
154 #define POST_DATA "u=%s&s=%s&a%%5B%d%%5D=%s&t%%5B%d%%5D=%s" \
155                   "&b%%5B%d%%5D=%s&m%%5B%d%%5D=&l%%5B%d%%5D=%d&i%%5B%d%%5D=%s"
156
157 vlc_module_begin();
158     set_category( CAT_INTERFACE );
159     set_subcategory( SUBCAT_INTERFACE_CONTROL );
160     set_shortname( N_( "Audioscrobbler" ) );
161     set_description( N_("Audioscrobbler submission Plugin") );
162     add_string( "lastfm-username", "", NULL,
163                 USERNAME_TEXT, USERNAME_LONGTEXT, VLC_FALSE );
164     add_string( "lastfm-password", "", NULL,
165                 PASSWORD_TEXT, PASSWORD_LONGTEXT, VLC_FALSE );
166     set_capability( "interface", 0 );
167     set_callbacks( Open, Close );
168 vlc_module_end();
169
170 /*****************************************************************************
171  * Open: initialize and create stuff
172  *****************************************************************************/
173
174 static int Open( vlc_object_t *p_this )
175 {
176     playlist_t          *p_playlist;
177
178     intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
179     intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
180     if( !p_sys )
181     {
182       goto error;
183     }
184     vlc_mutex_init( p_this, &p_sys->lock );
185
186     p_sys_global = p_sys;
187     p_sys->psz_submit_host = NULL;
188     p_sys->psz_submit_file = NULL;
189     p_sys->b_handshaked = VLC_FALSE;
190     p_sys->i_interval = 0;
191     p_sys->time_last_interval = time( NULL );
192     p_sys->psz_username = NULL;
193     p_sys->b_paused = VLC_FALSE;
194
195     /* md5 response is 32 chars, + final \0 */
196     p_sys->psz_response_md5 = malloc( sizeof( char ) * 33 );
197     if( !p_sys->psz_response_md5 )
198     {
199         vlc_mutex_destroy ( &p_sys->lock );
200         goto error;
201    }
202
203     p_sys->p_first_queue = malloc( sizeof( audioscrobbler_queue_t ) );
204     if( !p_sys->p_first_queue )
205     {
206         vlc_mutex_destroy( &p_sys->lock );
207         goto error;
208     }
209
210     p_sys->p_current_song = malloc( sizeof( audioscrobbler_song_t ) );
211     if( !p_sys->p_current_song )
212     {
213         vlc_mutex_destroy( &p_sys->lock );
214         goto error;
215     }
216
217     /* queues can't contain more than 10 songs */
218     p_sys->p_first_queue->p_queue =
219         malloc( 10 * sizeof( audioscrobbler_song_t ) );
220     if( !p_sys->p_current_song )
221     {
222         vlc_mutex_destroy( &p_sys->lock );
223         goto error;
224     }
225
226     p_sys->p_first_queue->i_songs_nb = 0;
227     p_sys->p_first_queue->p_next_queue = NULL;
228
229     p_playlist = pl_Yield( p_intf );
230     var_AddCallback( p_playlist, "playlist-current", ItemChange, p_intf );
231     pl_Release( p_playlist );
232
233     p_intf->pf_run = Run;
234
235     return VLC_SUCCESS;
236
237 error:
238     free( p_sys->p_current_song );
239     free( p_sys->p_first_queue );
240     free( p_sys->psz_response_md5 );
241     free( p_sys );
242
243     return VLC_ENOMEM;
244 }
245
246 /*****************************************************************************
247  * Close: destroy interface stuff
248  *****************************************************************************/
249 static void Close( vlc_object_t *p_this )
250 {
251     audioscrobbler_queue_t      *p_current_queue, *p_next_queue;
252     playlist_t                  *p_playlist;
253     input_thread_t              *p_input;
254
255     intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
256     intf_sys_t *p_sys = p_intf->p_sys;
257
258     p_playlist = pl_Yield( p_intf );
259     PL_LOCK;
260     p_input = p_playlist->p_input;
261     var_DelCallback( p_playlist, "playlist-current", ItemChange, p_intf );
262
263     if ( p_input )
264     {
265         vlc_object_yield( p_input );
266         var_DelCallback( p_input, "state", PlayingChange, p_intf );
267         vlc_object_release( p_input );
268     }
269
270     PL_UNLOCK;
271     pl_Release( p_playlist );
272
273     vlc_mutex_lock ( &p_sys->lock );
274     p_current_queue = p_sys->p_first_queue;
275     vlc_mutex_unlock ( &p_sys->lock );
276
277     while( ( p_current_queue->i_songs_nb == 10 ) &&
278         ( p_current_queue->p_next_queue != NULL ) )
279     {
280         p_next_queue = p_current_queue->p_next_queue;
281         DeleteQueue( p_current_queue );
282         free( p_current_queue );
283         p_current_queue = p_next_queue;
284     }
285
286     DeleteQueue( p_current_queue );
287     free( p_current_queue );
288
289     vlc_mutex_lock ( &p_sys->lock );
290     if ( p_sys->psz_username )
291     {
292         free( p_sys->psz_username );
293     }
294
295     free( p_sys->p_current_song );
296     vlc_mutex_unlock ( &p_sys->lock );
297     vlc_mutex_destroy( &p_sys->lock );
298     free( p_sys );
299 }
300
301 /*****************************************************************************
302  * Run : Handshake with audioscrobbler, then submit songs
303  *****************************************************************************/
304 static void Run( intf_thread_t *p_this )
305 {
306     char                    *psz_submit_string = NULL;
307     int                     i_handshake;
308     int                     i_netprintf;
309     int                     i_song;
310     playlist_t              *p_playlist;
311     uint8_t                 *p_buffer = NULL;
312     char                    *p_buffer_pos = NULL;
313     audioscrobbler_queue_t  *p_first_queue;
314     /* TODO: remove when meta_engine works */
315     time_t                  played_time;
316
317     p_this->p_sys = p_sys_global;
318     intf_sys_t *p_sys = p_this->p_sys;
319
320     while( !p_this->b_die )
321     {
322
323         if ( ( p_sys->p_first_queue->i_songs_nb > 0 ) &&
324             ( time( NULL ) >=
325             ( p_sys->time_last_interval + p_sys->i_interval )  ) )
326         {
327
328             if( p_sys->b_handshaked == VLC_FALSE )
329             {
330                 if ( time( NULL ) >=
331                     ( p_sys->time_last_interval + p_sys->i_interval ) )
332                 {
333                     msg_Dbg( p_this, "Handshaking with last.fm ..." );
334                     i_handshake = Handshake( p_this );
335
336                     if( i_handshake == VLC_ENOMEM )
337                     {
338                         msg_Err( p_this, "Out of memory" );
339                         return;
340                     }
341
342                     else if( i_handshake == VLC_ENOVAR )
343                     /* username not set */
344                     {
345                         vlc_mutex_unlock ( &p_sys->lock );
346                         intf_UserFatal( p_this, VLC_FALSE,
347                             _("last.fm username not set"),
348                             _("You have to set a username,"
349                             " and then restart VLC.\n"
350                             "Visit https://www.last.fm/join/"
351                             " if you don't have one.")
352                         );
353                         return;
354                     }
355
356                     else if( i_handshake == VLC_SUCCESS )
357                     {
358                         msg_Dbg( p_this, "Handshake successfull :)" );
359                         vlc_mutex_lock ( &p_sys->lock );
360                         p_sys->b_handshaked = VLC_TRUE;
361                         vlc_mutex_unlock ( &p_sys->lock );
362                     }
363
364                     else
365                     /* VLC_GENERIC : we'll try later */
366                     {
367                         vlc_mutex_lock ( &p_sys->lock );
368                         p_sys->i_interval = DEFAULT_INTERVAL;
369                         time( &p_sys->time_last_interval );
370                         vlc_mutex_unlock ( &p_sys->lock );
371                     }
372                 }
373             }
374
375
376             msg_Dbg( p_this, "Going to submit some data..." );
377             vlc_mutex_lock ( &p_sys->lock );
378             psz_submit_string = malloc( 2048 * sizeof( char ) );
379
380             if (!psz_submit_string)
381             {
382                 msg_Err( p_this, "Out of memory" );
383                 vlc_mutex_unlock ( &p_sys->lock );
384                 return;
385             }
386
387             for (i_song = 0; i_song < p_sys->p_first_queue->i_songs_nb ;
388                 i_song++ )
389             {
390                 snprintf( psz_submit_string, 2048, POST_DATA,
391                     p_sys->psz_username, p_sys->psz_response_md5,
392                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_a,
393                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_t,
394                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_b,
395                     i_song,
396                     i_song, p_sys->p_first_queue->p_queue[i_song]->i_l,
397                     i_song, p_sys->p_first_queue->p_queue[i_song]->psz_i
398                 );
399             }
400
401             p_sys->i_post_socket = net_ConnectTCP( p_this,
402                 p_sys->psz_submit_host, p_sys->i_submit_port);
403
404             i_netprintf = net_Printf(
405                 VLC_OBJECT(p_this), p_sys->i_post_socket, NULL,
406                 POST_REQUEST, p_sys->psz_submit_file,
407                 strlen( psz_submit_string), p_sys->psz_submit_file,
408                 VERSION, psz_submit_string
409             );
410
411             if ( i_netprintf == -1 )
412             {
413                 /* If connection fails, we assume we must handshake again */
414                 p_sys->i_interval = DEFAULT_INTERVAL;
415                 time( &p_sys->time_last_interval );
416                 p_sys->b_handshaked = VLC_FALSE;
417                 vlc_mutex_unlock ( &p_sys->lock );
418                 continue;
419             }
420
421             p_buffer = ( uint8_t* ) calloc( 1, 1024 );
422             if ( !p_buffer )
423             {
424                 msg_Err( p_this, "Out of memory" );
425                 vlc_mutex_unlock ( &p_sys->lock );
426                 return;
427             }
428
429             net_Read( p_this, p_sys->i_post_socket, NULL,
430                         p_buffer, 1024, VLC_FALSE );
431             net_Close( p_sys->i_post_socket );
432
433             p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
434
435             if ( p_buffer_pos )
436             {
437                 p_sys->i_interval = atoi( p_buffer_pos +
438                                             strlen( "INTERVAL " ) );
439                 time( &p_sys->time_last_interval );
440             }
441
442             p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
443
444             if ( p_buffer_pos )
445             {
446                 msg_Dbg( p_this, p_buffer_pos );
447                 vlc_mutex_unlock ( &p_sys->lock );
448                 continue;
449             }
450
451             p_buffer_pos = strstr( ( char * ) p_buffer, "BADAUTH" );
452
453             if ( p_buffer_pos )
454             {
455                 msg_Dbg( p_this, "Authentification failed, handshaking again" );
456                 p_sys->b_handshaked = VLC_FALSE;
457                 vlc_mutex_unlock ( &p_sys->lock );
458                 continue;
459             }
460
461             p_buffer_pos = strstr( ( char * ) p_buffer, "OK" );
462
463             if ( p_buffer_pos )
464             {
465                 if ( p_sys->p_first_queue->i_songs_nb == 10 )
466                 {
467                     p_first_queue = p_sys->p_first_queue->p_next_queue;
468                     DeleteQueue( p_sys->p_first_queue );
469                     free( p_sys->p_first_queue );
470                     p_sys->p_first_queue = p_first_queue;
471                 }
472                 else
473                 {
474                     DeleteQueue( p_sys->p_first_queue );
475                     p_sys->p_first_queue->i_songs_nb = 0;
476                 }
477                 msg_Dbg( p_this, "Submission successfull!" );
478             }
479             vlc_mutex_unlock ( &p_sys->lock );
480         }
481
482         msleep( INTF_IDLE_SLEEP );
483
484         p_playlist = pl_Yield( p_this );
485         PL_LOCK;
486         if( p_playlist->request.i_status == PLAYLIST_STOPPED )
487         {
488             PL_UNLOCK;
489             pl_Release( p_playlist );
490             /* if we stopped, we won't submit playing song */
491             vlc_mutex_lock( &p_sys->lock );
492             p_sys->b_queued = VLC_TRUE;
493             p_sys->b_metadata_read = VLC_TRUE;
494             vlc_mutex_unlock( &p_sys->lock );
495         }
496         else
497         {
498             PL_UNLOCK;
499             pl_Release( p_playlist );
500         }
501
502         vlc_mutex_lock( &p_sys->lock );
503         if( p_sys->b_metadata_read == VLC_FALSE )
504         {
505             /* TODO: remove when meta_engine works */
506             time( &played_time );
507             played_time -= p_sys->p_current_song->time_playing;
508             played_time -= p_sys->time_total_pauses;
509
510             vlc_mutex_unlock( &p_sys->lock );
511
512             /* TODO: remove when meta_engine works */
513             if( played_time > 10 )
514             {
515                 if ( ReadMetaData( p_this ) == VLC_ENOMEM )
516                 {
517                     msg_Err( p_this, "Out of memory" );
518                     return;
519                 }
520             }
521         }
522         else
523         {
524             if( ( p_sys->b_queued == VLC_FALSE )
525                 && ( p_sys->b_paused == VLC_FALSE ) )
526             {
527                 vlc_mutex_unlock( &p_sys->lock );
528                 if( AddToQueue( p_this ) == VLC_ENOMEM )
529                 {
530                     msg_Err( p_this, "Out of memory" );
531                     return;
532                 }
533             }
534             else
535             {
536                 vlc_mutex_unlock( &p_sys->lock );
537             }
538         }
539     }
540 }
541
542 /*****************************************************************************
543  * PlayingChange: Playing status change callback
544  *****************************************************************************/
545 static int PlayingChange( vlc_object_t *p_this, const char *psz_var,
546                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
547 {
548     intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
549     intf_sys_t *p_sys = p_intf->p_sys;
550
551     (void)p_this; (void)psz_var; (void)oldval;
552
553     vlc_mutex_lock( &p_sys->lock );
554
555     if( newval.i_int == PAUSE_S )
556     {
557         time( &p_sys->time_pause );
558         p_sys->b_paused = VLC_TRUE;
559     }
560
561     if( newval.i_int == PLAYING_S )
562     {
563         p_sys->time_total_pauses += time( NULL ) - p_sys->time_pause;
564         p_sys->b_paused = VLC_FALSE;
565     }
566
567     vlc_mutex_unlock( &p_sys->lock );
568
569     return VLC_SUCCESS;
570 }
571
572 /*****************************************************************************
573  * ItemChange: Playlist item change callback
574  *****************************************************************************/
575 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
576                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
577 {
578     playlist_t          *p_playlist;
579     input_thread_t      *p_input = NULL;
580     time_t              epoch;
581     struct tm           *epoch_tm;
582     char                psz_date[20];
583
584     (void)p_this; (void)psz_var; (void)oldval; (void)newval;
585
586     intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
587     intf_sys_t *p_sys = p_intf->p_sys;
588
589     p_playlist = pl_Yield( p_intf );
590     PL_LOCK;
591     p_input = p_playlist->p_input;
592
593     if( !p_input )
594     {
595         PL_UNLOCK;
596         pl_Release( p_playlist );
597         vlc_mutex_lock( &p_sys->lock );
598
599         /* we won't read p_input */
600         p_sys->b_queued = VLC_TRUE;
601         p_sys->b_metadata_read = VLC_TRUE;
602
603         vlc_mutex_unlock( &p_sys->lock );
604         return VLC_SUCCESS;
605     }
606
607     vlc_object_yield( p_input );
608     PL_UNLOCK;
609     pl_Release( p_playlist );
610
611     var_AddCallback( p_input, "state", PlayingChange, p_intf );
612
613     vlc_mutex_lock ( &p_sys->lock );
614
615     /* reset pause counter */
616     p_sys->time_total_pauses = 0;
617
618     /* we'll read metadata when it's present */
619     p_sys->b_metadata_read = VLC_FALSE;
620     p_sys->b_waiting_meta = VLC_FALSE;
621     p_sys->b_queued = VLC_TRUE;
622
623     time( &epoch );
624     epoch_tm = gmtime( &epoch );
625     snprintf( psz_date, 20, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
626         epoch_tm->tm_year+1900, epoch_tm->tm_mon+1, epoch_tm->tm_mday,
627         epoch_tm->tm_hour, epoch_tm->tm_min, epoch_tm->tm_sec );
628
629     p_sys->p_current_song->psz_i = encode_URI_component( psz_date );
630     p_sys->p_current_song->time_playing = epoch;
631
632     p_sys->b_paused = ( p_input->b_dead || !p_input->input.p_item->psz_name )
633                       ? VLC_TRUE : VLC_FALSE;
634
635     vlc_mutex_unlock( &p_sys->lock );
636
637     vlc_object_release( p_input );
638     return VLC_SUCCESS;
639 }
640
641 /*****************************************************************************
642  * AddToQueue: Add the played song to the queue to be submitted
643  *****************************************************************************/
644 static int AddToQueue ( intf_thread_t *p_this )
645 {
646     int                         i_songs_nb;
647     time_t                      played_time;
648     audioscrobbler_queue_t      *p_queue = NULL, *p_next_queue = NULL;
649
650     intf_sys_t *p_sys = p_this->p_sys;
651
652     /* wait for the user to listen enough before submitting */
653     time ( &played_time );
654     played_time -= p_sys->p_current_song->time_playing;
655     played_time -= p_sys->time_total_pauses;
656
657     vlc_mutex_lock( &p_sys->lock );
658     if( ( played_time < 240 )
659         && ( played_time < ( p_sys->p_current_song->i_l / 2 ) ) )
660     {
661         vlc_mutex_unlock ( &p_sys->lock );
662         return VLC_SUCCESS;
663     }
664
665     if( p_sys->p_current_song->i_l < 30 )
666     {
667         msg_Dbg( p_this, "Song too short (< 30s) -> not submitting" );
668         p_sys->b_queued = VLC_TRUE;
669         vlc_mutex_unlock ( &p_sys->lock );
670         return VLC_SUCCESS;
671     }
672
673     if( !*p_sys->p_current_song->psz_a || !*p_sys->p_current_song->psz_t )
674     {
675         msg_Dbg( p_this, "Missing artist or title -> not submitting" );
676         p_sys->b_queued = VLC_TRUE;
677         vlc_mutex_unlock ( &p_sys->lock );
678         return VLC_SUCCESS;
679     }
680
681     msg_Dbg( p_this, "Ok. We'll put it in the queue for submission" );
682
683     /* go to last queue */
684     p_queue = p_sys->p_first_queue;
685     while( ( p_queue->i_songs_nb == 10 ) && ( p_queue->p_next_queue != NULL ) )
686     {
687         p_queue = p_queue->p_next_queue;
688     }
689
690     i_songs_nb = p_queue->i_songs_nb;
691
692     if( i_songs_nb == 10 )
693     {
694         p_next_queue = malloc( sizeof( audioscrobbler_queue_t ) );
695         if( !p_next_queue )
696         {
697             vlc_mutex_unlock ( &p_sys->lock );
698             return VLC_ENOMEM;
699         }
700         p_queue->p_next_queue = p_next_queue;
701         i_songs_nb = 0;
702         p_queue = p_next_queue;
703         p_queue->i_songs_nb = i_songs_nb;
704     }
705
706     p_queue->p_queue[i_songs_nb] = malloc( sizeof( audioscrobbler_song_t ) );
707
708     p_queue->p_queue[i_songs_nb]->i_l = p_sys->p_current_song->i_l;
709
710     p_queue->p_queue[i_songs_nb]->psz_a =
711         strdup( p_sys->p_current_song->psz_a );
712
713     p_queue->p_queue[i_songs_nb]->psz_t =
714         strdup( p_sys->p_current_song->psz_t );
715
716     p_queue->p_queue[i_songs_nb]->psz_b =
717         strdup( p_sys->p_current_song->psz_b );
718
719     p_queue->p_queue[i_songs_nb]->psz_i =
720         strdup( p_sys->p_current_song->psz_i );
721
722     p_queue->i_songs_nb++;
723     p_sys->b_queued = VLC_TRUE;
724
725     vlc_mutex_unlock( &p_sys->lock );
726
727     return VLC_SUCCESS;
728 }
729
730 /*****************************************************************************
731  * Handshake : Init audioscrobbler connection
732  *****************************************************************************/
733 static int Handshake( intf_thread_t *p_this )
734 {
735     char                *psz_password = NULL;
736     struct md5_s        *p_struct_md5 = NULL;
737     char                *psz_password_md5 = NULL;
738     char                *ps_challenge_md5 = NULL;
739
740     stream_t            *p_stream;
741     char                *psz_handshake_url = NULL;
742
743     uint8_t             *p_buffer = NULL;
744     char                *p_buffer_pos = NULL;
745     char                *psz_buffer_substring = NULL;
746     char                *psz_url_parser = NULL;
747     int                 i_url_pos, i;
748
749     intf_thread_t *p_intf = ( intf_thread_t* ) p_this;
750     intf_sys_t *p_sys = p_this->p_sys;
751
752     vlc_mutex_lock ( &p_sys->lock );
753
754     p_sys->psz_username = config_GetPsz(p_this, "lastfm-username");
755     if ( !p_sys->psz_username )
756     {
757         goto memerror;
758     }
759
760
761     if ( !*p_sys->psz_username )
762     {
763         return VLC_ENOVAR;
764     }
765
766     psz_handshake_url = malloc( 1024 );
767     if ( !psz_handshake_url )
768     {
769         goto memerror;
770     }
771
772     snprintf( psz_handshake_url, 1024,
773         "http://post.audioscrobbler.com/?hs=true&p=1.1&c=%s&v=%s&u=%s",
774         CLIENT_NAME, CLIENT_VERSION, p_sys->psz_username );
775
776     p_stream = stream_UrlNew( p_intf, psz_handshake_url);
777
778     free( psz_handshake_url );
779
780     if( !p_stream )
781     {
782         vlc_mutex_unlock ( &p_sys->lock );
783         return VLC_EGENERIC;
784     }
785
786     p_buffer = ( uint8_t* ) calloc( 1, 1024 );
787     if ( !p_buffer )
788     {
789         stream_Delete( p_stream );
790         goto memerror;
791     }
792
793     if ( stream_Read( p_stream, p_buffer, 1024 ) == 0 )
794     {
795         stream_Delete( p_stream );
796         free( p_buffer );
797         vlc_mutex_unlock ( &p_sys->lock );
798         return VLC_EGENERIC;
799     }
800
801     stream_Delete( p_stream );
802
803     p_buffer_pos = strstr( ( char * ) p_buffer, "INTERVAL" );
804
805     if ( p_buffer_pos )
806     {
807         p_sys->i_interval = atoi( p_buffer_pos + strlen( "INTERVAL " ) );
808         time( &p_sys->time_last_interval );
809     }
810
811     p_buffer_pos = strstr( ( char * ) p_buffer, "FAILED" );
812
813     if ( p_buffer_pos )
814     {
815         msg_Dbg( p_this, p_buffer_pos );
816         free( p_buffer );
817         vlc_mutex_unlock ( &p_sys->lock );
818         return VLC_EGENERIC;
819     }
820
821     p_buffer_pos = strstr( ( char * ) p_buffer, "BADUSER" );
822
823     if ( p_buffer_pos )
824     {
825         intf_UserFatal( p_this, VLC_FALSE, _("Bad last.fm Username"),
826             _("last.fm username is incorrect, please verify your settings")
827         );
828         free( p_buffer );
829         vlc_mutex_unlock ( &p_sys->lock );
830         return VLC_EGENERIC;
831     }
832
833     p_buffer_pos = strstr( ( char * ) p_buffer, "UPDATE" );
834
835     if ( p_buffer_pos )
836     {
837         msg_Dbg( p_intf, "Protocol updated" );
838         msg_Dbg( p_intf, p_buffer_pos );
839     }
840
841     else
842     {
843         p_buffer_pos = strstr( ( char * ) p_buffer, "UPTODATE" );
844         if ( !p_buffer_pos )
845         {
846             msg_Dbg( p_intf, "Protocol error" );
847             free( p_buffer );
848             vlc_mutex_unlock ( &p_sys->lock );
849             return VLC_EGENERIC;
850         }
851     }
852
853     psz_buffer_substring = strndup( strstr( p_buffer_pos, "\n" ) + 1, 32 );
854     if ( !psz_buffer_substring )
855     {
856         goto memerror;
857     }
858     else
859     {
860         ps_challenge_md5 = malloc( sizeof( char ) * 32 );
861         if ( !ps_challenge_md5 )
862         {
863             goto memerror;
864         }
865         memcpy( ps_challenge_md5, psz_buffer_substring, 32 );
866         free( psz_buffer_substring );
867     }
868
869     p_buffer_pos = ( void* ) strstr( ( char* ) p_buffer, "http://" );
870
871     if ( p_sys->psz_submit_host != NULL )
872     {
873         free( p_sys->psz_submit_host );
874     }
875
876     if ( p_sys->psz_submit_file != NULL )
877     {
878         free( p_sys->psz_submit_file );
879     }
880
881     psz_url_parser = p_buffer_pos + strlen( "http://" );
882
883     i_url_pos = strcspn( psz_url_parser, ":" );
884     p_sys->psz_submit_host = strndup( psz_url_parser, i_url_pos );
885
886     p_sys->i_submit_port = atoi( psz_url_parser + i_url_pos + 1 );
887
888     psz_url_parser += strcspn( psz_url_parser , "/" ) + 1;
889     i_url_pos = strcspn( psz_url_parser, "\n" );
890     p_sys->psz_submit_file = strndup( psz_url_parser, i_url_pos );
891
892     free(p_buffer);
893
894     p_struct_md5 = malloc( sizeof( struct md5_s ) );
895     if( !p_struct_md5 )
896     {
897         goto memerror;
898     }
899
900     psz_password = config_GetPsz(p_this, "lastfm-password");
901     if ( !psz_password )
902     {
903          goto memerror;
904     }
905
906     InitMD5( p_struct_md5 );
907     AddMD5( p_struct_md5, ( uint8_t* ) psz_password, strlen( psz_password ) );
908     EndMD5( p_struct_md5 );
909
910     free( psz_password );
911
912     psz_password_md5 = malloc ( 33 * sizeof( char ) );
913     if ( !psz_password_md5 )
914     {
915         goto memerror;
916     }
917
918     for ( i = 0; i < 4; i++ )
919     {
920         sprintf( &psz_password_md5[8*i], "%02x%02x%02x%02x",
921             p_struct_md5->p_digest[i] & 0xff,
922             ( p_struct_md5->p_digest[i] >> 8 ) & 0xff,
923             ( p_struct_md5->p_digest[i] >> 16 ) & 0xff,
924             p_struct_md5->p_digest[i] >> 24
925         );
926     }
927
928     strlwr( psz_password_md5 );
929
930     InitMD5( p_struct_md5 );
931     AddMD5( p_struct_md5, ( uint8_t* ) psz_password_md5, 32 );
932     AddMD5( p_struct_md5, ( uint8_t* ) ps_challenge_md5, 32 );
933     EndMD5( p_struct_md5 );
934
935     free( ps_challenge_md5 );
936     free( psz_password_md5 );
937
938     for ( i = 0; i < 4; i++ )
939     {
940         sprintf( &p_sys->psz_response_md5[8*i], "%02x%02x%02x%02x",
941             p_struct_md5->p_digest[i] & 0xff,
942             ( p_struct_md5->p_digest[i] >> 8 ) & 0xff,
943             ( p_struct_md5->p_digest[i] >> 16 ) & 0xff,
944             p_struct_md5->p_digest[i] >> 24
945         );
946     }
947
948     p_sys->psz_response_md5[32] = 0;
949
950     strlwr( p_sys->psz_response_md5 );
951
952     vlc_mutex_unlock ( &p_sys->lock );
953
954     return VLC_SUCCESS;
955
956 memerror:
957     free( p_buffer );
958     free( p_struct_md5 );
959     free( psz_buffer_substring );
960
961     vlc_mutex_unlock ( &p_sys->lock );
962     return VLC_ENOMEM;
963 }
964
965 /*****************************************************************************
966  * strlwr : Converts a string to lower case
967  *****************************************************************************/
968 #if !defined(strlwr) && !defined( WIN32 )
969 char* strlwr(char *psz_string)
970 {
971     while ( *psz_string )
972     {
973         *psz_string++ = tolower( *psz_string );
974     }
975     return psz_string;
976 }
977 #endif
978
979 /*****************************************************************************
980  * DeleteQueue : Free all songs from an audioscrobbler_queue_t
981  *****************************************************************************/
982 void DeleteQueue( audioscrobbler_queue_t *p_queue )
983 {
984     int     i;
985
986     for( i = 0; i < p_queue->i_songs_nb; i++ )
987     {
988         free( p_queue->p_queue[i]->psz_a );
989         free( p_queue->p_queue[i]->psz_b );
990         free( p_queue->p_queue[i]->psz_t );
991         free( p_queue->p_queue[i]->psz_i );
992         free( p_queue->p_queue[i] );
993     }
994 }
995
996 /*****************************************************************************
997  * ReadMetaData : Read meta data when parsed by vlc
998  * or wait for fetching if unavailable
999  *****************************************************************************/
1000 static int ReadMetaData( intf_thread_t *p_this )
1001 {
1002     playlist_t          *p_playlist;
1003     input_thread_t      *p_input = NULL;
1004     vlc_value_t         video_val;
1005
1006     intf_sys_t *p_sys = p_this->p_sys;
1007
1008     p_playlist = pl_Yield( p_this );
1009     PL_LOCK;
1010     p_input = p_playlist->p_input;
1011
1012     if( !p_input )
1013     {
1014         PL_UNLOCK;
1015         pl_Release( p_playlist );
1016         return( VLC_SUCCESS );
1017     }
1018
1019     vlc_object_yield( p_input );
1020     PL_UNLOCK;
1021     pl_Release( p_playlist );
1022
1023     var_Change( p_input, "video-es", VLC_VAR_CHOICESCOUNT, &video_val, NULL );
1024     if( ( video_val.i_int > 0 ) || \
1025         ( p_input->input.p_item->i_type == ITEM_TYPE_NET ) )
1026     {
1027         msg_Dbg( p_this, "Not an audio file -> no submission");
1028         vlc_object_release( p_input );
1029
1030         vlc_mutex_lock( &p_sys->lock );
1031         p_sys->b_queued = VLC_TRUE;
1032         p_sys->b_metadata_read = VLC_TRUE;
1033         vlc_mutex_unlock( &p_sys->lock );
1034
1035         return VLC_SUCCESS;
1036     }
1037
1038     return ReadLocalMetaData( p_this, p_input );    
1039 }
1040
1041 /*****************************************************************************
1042  * ReadLocalMetaData : Puts current song's meta data in p_sys->p_current_song
1043  *****************************************************************************/
1044 static int ReadLocalMetaData( intf_thread_t *p_this, input_thread_t  *p_input )
1045 {
1046     char                *psz_title = NULL;
1047     char                *psz_artist = NULL;
1048     char                *psz_album = NULL;
1049     int                 i_length = -1;
1050     vlc_bool_t          b_waiting;
1051     int                 i_status;
1052
1053     intf_sys_t *p_sys = p_this->p_sys;
1054
1055     i_status = p_input->input.p_item->p_meta->i_status;
1056
1057     vlc_mutex_lock( &p_sys->lock );
1058     b_waiting = p_sys->b_waiting_meta;
1059     vlc_mutex_unlock( &p_sys->lock );
1060
1061     /* TODO : remove if (1) when meta_engine works */
1062     if ( (1/*( i_status & ITEM_PREPARSED )*/&& ( b_waiting == VLC_FALSE ) ) || \
1063         ( ( i_status & ITEM_META_FETCHED ) && ( b_waiting == VLC_TRUE ) ) )
1064     {
1065         if ( p_input->input.p_item->p_meta->psz_artist )
1066         {
1067             psz_artist = encode_URI_component(
1068                 p_input->input.p_item->p_meta->psz_artist );
1069             if ( !psz_artist )
1070             {
1071                 goto error;
1072             }
1073         }
1074         else
1075         {
1076             msg_Dbg( p_this, "No artist.." );
1077             if ( b_waiting == VLC_TRUE )
1078             {
1079                 psz_artist = calloc( 1, sizeof( char ) );
1080             }
1081             else
1082             {
1083                 goto waiting_meta_data_fetching;
1084             }
1085         }
1086
1087         if ( p_input->input.p_item->psz_name )
1088         {
1089             psz_title = encode_URI_component( p_input->input.p_item->psz_name );
1090             if ( !psz_title )
1091             {
1092                 goto error;
1093             }
1094         }
1095         else
1096         {
1097             msg_Dbg( p_this, "No track name.." );
1098             if ( b_waiting == VLC_TRUE )
1099             {
1100                 psz_title = calloc( 1, sizeof( char ) );
1101             }
1102             else
1103             {
1104                 goto waiting_meta_data_fetching;
1105             }
1106         }
1107
1108         if ( p_input->input.p_item->p_meta->psz_album )
1109         {
1110             psz_album = encode_URI_component(
1111                 p_input->input.p_item->p_meta->psz_album );
1112             if ( !psz_album )
1113             {
1114                 goto error;
1115             }
1116         }
1117         else
1118         {
1119             psz_album = calloc( 1, sizeof( char ) );
1120         }
1121
1122         i_length = p_input->input.p_item->i_duration / 1000000;
1123
1124         vlc_object_release( p_input );
1125
1126         vlc_mutex_lock ( &p_sys->lock );
1127
1128         p_sys->p_current_song->psz_a = strdup( psz_artist );
1129         p_sys->p_current_song->psz_t = strdup( psz_title );
1130         p_sys->p_current_song->psz_b = strdup( psz_album );
1131         p_sys->p_current_song->i_l = i_length;
1132         p_sys->b_queued = VLC_FALSE;
1133         p_sys->b_metadata_read = VLC_TRUE;
1134
1135         vlc_mutex_unlock ( &p_sys->lock );
1136
1137         msg_Dbg( p_this, "Meta data registered, waiting to be queued" );
1138
1139         free( psz_title );
1140         free( psz_artist );
1141         free( psz_album );
1142
1143         return VLC_SUCCESS;
1144     }
1145     
1146     return VLC_SUCCESS;
1147
1148 waiting_meta_data_fetching:
1149     vlc_object_release( p_input );
1150
1151     vlc_mutex_lock( &p_sys->lock );
1152     p_sys->b_waiting_meta = VLC_TRUE;
1153     vlc_mutex_unlock( &p_sys->lock );
1154
1155     free( psz_artist );
1156     free( psz_album );
1157     free( psz_title );
1158
1159     return VLC_SUCCESS;
1160
1161 error:
1162     vlc_object_release( p_input );
1163
1164     free( psz_artist );
1165     free( psz_album );
1166     free( psz_title );
1167     return VLC_ENOMEM;
1168 }