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