]> git.sesse.net Git - vlc/blob - src/misc/update.c
update system:
[vlc] / src / misc / update.c
1 /*****************************************************************************
2  * update.c: VLC update checking and downloading
3  *****************************************************************************
4  * Copyright © 2005-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *          Rémi Duraffort <ivoire at via.ecp.fr>
9             Rafaël Carré <funman@videolanorg>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either release 2 of the License, or
14  * (at your option) any later release.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /**
27  *   \file
28  *   This file contains functions related to VLC and plugins update management
29  */
30
31 /* TODO: pgp verification of the status file, and downloaded binaries */
32
33 /*****************************************************************************
34  * Preamble
35  *****************************************************************************/
36
37 #include <vlc/vlc.h>
38
39 #ifdef UPDATE_CHECK
40
41 #include <ctype.h>                                              /* tolower() */
42 #include <assert.h>
43
44
45 #include <vlc_update.h>
46
47 #include <vlc_block.h>
48 #include <vlc_stream.h>
49 #include <vlc_interface.h>
50 #include <vlc_charset.h>
51
52 /*****************************************************************************
53  * Misc defines
54  *****************************************************************************/
55
56 /*
57  * Here is the format of these "status files" :
58  * First line is the last version: "X.Y.Ze" where:
59  *      * X is the major number
60  *      * Y is the minor number
61  *      * Z is the revision number
62  *      * e is an OPTIONAL extra letter
63  *      * AKA "0.8.6d" or "0.9.0"
64  * Second line is an url to the last binary
65  * Third line is a description of the update (it MAY be extended to several lines, but for now it is only one line)
66  */
67
68 #if defined( UNDER_CE )
69 #   define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-ce"
70 #elif defined( WIN32 )
71 #   define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-win-x86"
72 #elif defined( __APPLE__ )
73 #   define UPDATE_VLC_OS "macosx"
74 #   if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
75 #       define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-ppc"
76 #   else
77 #       define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-x86"
78 #   endif
79 #elif defined( SYS_BEOS )
80 #       define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-beos-x86"
81 #else
82 #   define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status"
83 #endif
84
85 #define STRDUP( a ) ( a ? strdup( a ) : NULL )
86
87
88 /*****************************************************************************
89  * Local Prototypes
90  *****************************************************************************/
91 static void EmptyRelease( update_t *p_update );
92 static void GetUpdateFile( update_t *p_update );
93 static int CompareReleases( const struct update_release_t *p1,
94                             const struct update_release_t *p2 );
95 static char * size_str( long int l_size );
96
97
98 /*****************************************************************************
99  * OpenPGP functions
100  *****************************************************************************/
101
102 #define packet_type( c ) ( ( c & 0x3c ) >> 2 )      /* 0x3C = 00111100 */
103 #define packet_header_len( c ) ( ( c & 0x03 ) + 1 ) /* number of bytes in a packet header */
104
105 static inline int scalar_number( uint8_t *p, int header_len )
106 {
107     if( header_len == 1 )
108         return( p[0] );
109     else if( header_len == 2 )
110         return( (p[0] << 8) + p[1] );
111     else if( header_len == 4 )
112         return( (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3] );
113     else
114         abort();
115 }
116
117 /* number of data bytes in a MPI */
118 #define mpi_len( mpi ) ( ( scalar_number( mpi, 2 ) + 7 ) / 8 )
119
120 /* 
121  * fill a public_key_packet_t structure from public key packet data
122  * verify that it is a version 4 public key packet, using DSA
123  */
124 static int parse_public_key_packet( public_key_packet_t *p_key, uint8_t *p_buf,
125                                     size_t i_packet_len )
126 {
127     if( i_packet_len != 418 )
128         return VLC_EGENERIC;
129
130     p_key->version   = *p_buf++;
131     if( p_key->version != 4 )
132         return VLC_EGENERIC;
133
134     /* warn when timestamp is > date ? */
135     memcpy( p_key->timestamp, p_buf, 4 ); p_buf += 4;
136
137     p_key->algo      = *p_buf++;
138     if( p_key->algo != PUBLIC_KEY_ALGO_DSA )
139         return VLC_EGENERIC;
140
141     memcpy( p_key->p, p_buf, 2+128 ); p_buf += 2+128;
142     if( mpi_len( p_key->p ) != 128 )
143         return VLC_EGENERIC;
144
145     memcpy( p_key->q, p_buf, 2+20 );  p_buf += 2+20;
146     if( mpi_len( p_key->q ) != 20 )
147         return VLC_EGENERIC;
148
149     memcpy( p_key->g, p_buf, 2+128 ); p_buf += 2+128;
150     if( mpi_len( p_key->g ) != 128 )
151         return VLC_EGENERIC;
152
153     memcpy( p_key->y, p_buf, 2+128 ); p_buf += 2+128;
154     if( mpi_len( p_key->y ) != 128 )
155         return VLC_EGENERIC;
156
157     return VLC_SUCCESS;
158 }
159
160 /*
161  * fill a signature_packet_v4_t from signature packet data
162  * verify that it was used with a DSA public key, using SHA-1 digest
163  */
164 static int parse_signature_v4_packet( signature_packet_v4_t *p_sig,
165                                       uint8_t *p_buf, size_t i_sig_len )
166 {
167     if( i_sig_len < 54 )
168         return VLC_EGENERIC;
169
170     p_sig->version = *p_buf++;
171     if( p_sig->version != 4 )
172         return VLC_EGENERIC;
173
174     p_sig->type = *p_buf++;
175     if( p_sig->type < GENERIC_KEY_SIGNATURE ||
176         p_sig->type > POSITIVE_KEY_SIGNATURE )
177         return VLC_EGENERIC;
178
179     p_sig->public_key_algo = *p_buf++;
180     if( p_sig->public_key_algo != PUBLIC_KEY_ALGO_DSA )
181         return VLC_EGENERIC;
182
183     p_sig->digest_algo = *p_buf++;
184     if( p_sig->digest_algo != DIGEST_ALGO_SHA1 )
185         return VLC_EGENERIC;
186
187     memcpy( p_sig->hashed_data_len, p_buf, 2 ); p_buf += 2;
188
189     size_t i_pos = 6;
190     size_t i_hashed_data_len = scalar_number( p_sig->hashed_data_len, 2 );
191     i_pos += i_hashed_data_len;
192     if( i_pos > i_sig_len - 48 ) /* r & s are 44 bytes in total, 
193                               * + the unhashed data length (2 bytes)
194                               * + the hash verification (2 bytes) */
195         return VLC_EGENERIC;
196
197     p_sig->hashed_data = (uint8_t*) malloc( i_hashed_data_len );
198     if( !p_sig->hashed_data )
199         return VLC_ENOMEM;
200     memcpy( p_sig->hashed_data, p_buf, i_hashed_data_len );
201     p_buf += i_hashed_data_len;
202
203     memcpy( p_sig->unhashed_data_len, p_buf, 2 ); p_buf += 2;
204
205     size_t i_unhashed_data_len = scalar_number( p_sig->unhashed_data_len, 2 );
206     i_pos += 2 + i_unhashed_data_len;
207     if( i_pos != i_sig_len - 46 )
208     {
209         free( p_sig->hashed_data );
210         return VLC_EGENERIC;
211     }
212
213     p_sig->unhashed_data = (uint8_t*) malloc( i_unhashed_data_len );
214     if( !p_sig->unhashed_data )
215     {
216         free( p_sig->hashed_data );
217         return VLC_ENOMEM;
218     }
219     memcpy( p_sig->unhashed_data, p_buf, i_unhashed_data_len );
220     p_buf += i_unhashed_data_len;
221
222     memcpy( p_sig->hash_verification, p_buf, 2 ); p_buf += 2;
223
224     memcpy( p_sig->r, p_buf, 22 ); p_buf += 22;
225     if( mpi_len( p_sig->r ) != 20 )
226     {
227         free( p_sig->hashed_data );
228         free( p_sig->unhashed_data );
229         return VLC_EGENERIC;
230     }
231
232     memcpy( p_sig->s, p_buf, 22 );
233     if( mpi_len( p_sig->s ) != 20 )
234     {
235         free( p_sig->hashed_data );
236         free( p_sig->unhashed_data );
237         return VLC_EGENERIC;
238     }
239
240     return VLC_SUCCESS;
241 }
242
243 /*
244  * crc_octets() was lamely copied from rfc 2440
245  * Copyright (C) The Internet Society (1998).  All Rights Reserved.
246  */
247 #define CRC24_INIT 0xB704CEL
248 #define CRC24_POLY 0x1864CFBL
249
250 static long crc_octets( uint8_t *octets, size_t len )
251 {
252     long crc = CRC24_INIT;
253     int i;
254     while (len--)
255     {
256         crc ^= (*octets++) << 16;
257         for (i = 0; i < 8; i++)
258         {
259             crc <<= 1;
260             if (crc & 0x1000000)
261                 crc ^= CRC24_POLY;
262         }
263     }
264     return crc & 0xFFFFFFL;
265 }
266
267 /*
268  * Transform an armored document in binary format
269  * Used on public keys and signatures
270  */
271 static int pgp_unarmor( char *p_ibuf, size_t i_ibuf_len,
272                         uint8_t *p_obuf, size_t i_obuf_len )
273 {
274     char *p_ipos = p_ibuf;
275     uint8_t *p_opos = p_obuf;
276     int i_end = 0;
277
278     int i_header_skipped = 0;
279
280     while( !i_end && p_ipos < p_ibuf + i_ibuf_len )
281     {
282         if( *p_ipos == '\r' || *p_ipos == '\n' )
283         {
284             p_ipos++;
285             continue;
286         }
287
288         size_t i_line_len = strcspn( p_ipos, "\r\n" );
289         if( i_line_len == 0 )
290             continue;
291
292         if( !i_header_skipped )
293         {
294             if( !strncmp( p_ipos, "-----BEGIN PGP", 14 ) )
295                 i_header_skipped = 1;
296
297             p_ipos += i_line_len + 1;
298             continue;
299         }
300         
301         if( !strncmp( p_ipos, "Version:", 8 ) )
302         {
303             p_ipos += i_line_len + 1;
304             continue;
305         }
306
307         if( p_ipos[i_line_len - 1] == '=' )
308         {
309             i_end = 1;
310             p_ipos[i_line_len - 1] = '\0';
311         }
312         else
313             p_ipos[i_line_len] = '\0';
314
315         p_opos += vlc_b64_decode_binary_to_buffer(  p_opos,
316                                                     p_obuf - p_opos + i_obuf_len,
317                                                     p_ipos );
318
319         p_ipos += i_line_len + 1;
320     }
321
322     /* XXX: the CRC is OPTIONAL, really require it ? */
323     if( p_ipos + 5 > p_ibuf + i_ibuf_len || *p_ipos++ != '=' )
324         return 0;
325
326     uint8_t p_crc[3];
327     if( vlc_b64_decode_binary_to_buffer( p_crc, 3, p_ipos ) != 3 )
328         return 0;
329
330     long l_crc = crc_octets( p_obuf, p_opos - p_obuf );
331     long l_crc2 = ( 0 << 24 ) + ( p_crc[0] << 16 ) + ( p_crc[1] << 8 ) + p_crc[2];
332
333     return l_crc2 == l_crc ? p_opos - p_obuf : 0;
334 }
335
336 /*
337  * Download the signature associated to a document or a binary file.
338  * We're given the file's url, we just append ".asc" to it and download 
339  */
340 static int download_signature(  vlc_object_t *p_this,
341                                 signature_packet_v3_t *p_sig, char *psz_url )
342 {
343     char *psz_sig = (char*) malloc( strlen( psz_url ) + 4 + 1 ); /* ".asc" + \0 */
344     if( !psz_sig )
345         return VLC_ENOMEM;
346
347     strcpy( psz_sig, psz_url );
348     strcat( psz_sig, ".asc" );
349
350     stream_t *p_stream = stream_UrlNew( p_this, psz_sig );
351     free( psz_sig );
352
353     if( !p_stream )
354         return VLC_ENOMEM;
355
356     int64_t i_size = stream_Size( p_stream );
357     if( i_size < 65 )
358     {
359         stream_Delete( p_stream );
360         return VLC_EGENERIC;
361     }
362     else if( i_size == 65 ) /* binary format signature */
363     {
364         int i_read = stream_Read( p_stream, p_sig, (int)i_size );
365         stream_Delete( p_stream );
366         if( i_read != i_size )
367             return VLC_EGENERIC;
368         else
369             return VLC_SUCCESS;
370     }
371
372     char *p_buf = (char*)malloc( i_size );
373     if( !p_buf )
374     {
375         stream_Delete( p_stream );
376         return VLC_ENOMEM;
377     }
378     
379     int i_read = stream_Read( p_stream, p_buf, (int)i_size );
380
381     stream_Delete( p_stream );
382
383     if( i_read != i_size )
384     {
385         free( p_buf );
386         return VLC_EGENERIC;
387     }
388     
389     int i_bytes = pgp_unarmor( p_buf, i_size, (uint8_t*)p_sig, 65 );
390     free( p_buf );
391
392     if( i_bytes != 65 )
393         return VLC_EGENERIC;
394     else
395         return VLC_SUCCESS;
396 }
397
398 /*
399  * Verify an OpenPGP signature made on some SHA-1 hash, with some DSA public key
400  */
401 static int verify_signature( vlc_object_t *p_this, uint8_t *p_r, uint8_t *p_s,
402         public_key_packet_t *p_key, uint8_t *p_hash )
403 {
404     /* the data to be verified (a SHA-1 hash) */
405     const char *hash_sexp_s = "(data(flags raw)(value %m))";
406     /* the public key */
407     const char *key_sexp_s = "(public-key(dsa(p %m)(q %m)(g %m)(y %m)))";
408     /* the signature */
409     const char *sig_sexp_s = "(sig-val(dsa(r %m )(s %m )))";
410
411     size_t erroff;
412     gcry_mpi_t p, q, g, y, r, s, hash;
413     p = q = g = y = r = s = hash = NULL;
414     gcry_sexp_t key_sexp, hash_sexp, sig_sexp;
415     key_sexp = hash_sexp = sig_sexp = NULL;
416
417     if( gcry_mpi_scan( &p, GCRYMPI_FMT_USG, p_key->p + 2, 128, NULL ) ||
418         gcry_mpi_scan( &q, GCRYMPI_FMT_USG, p_key->q + 2, 20, NULL ) ||
419         gcry_mpi_scan( &g, GCRYMPI_FMT_USG, p_key->g + 2, 128, NULL ) ||
420         gcry_mpi_scan( &y, GCRYMPI_FMT_USG, p_key->y + 2, 128, NULL ) ||
421         gcry_sexp_build( &key_sexp, &erroff, key_sexp_s, p, q, g, y ) )
422         goto problem;
423
424     if( gcry_mpi_scan( &r, GCRYMPI_FMT_USG, p_r + 2, 20, NULL ) ||
425         gcry_mpi_scan( &s, GCRYMPI_FMT_USG, p_s + 2, 20, NULL ) ||
426         gcry_sexp_build( &sig_sexp, &erroff, sig_sexp_s, r, s ) )
427         goto problem;
428
429     if( gcry_mpi_scan( &hash, GCRYMPI_FMT_USG, p_hash, 20, NULL ) ||
430         gcry_sexp_build( &hash_sexp, &erroff, hash_sexp_s, hash ) )
431         goto problem;
432
433     if( gcry_pk_verify( sig_sexp, hash_sexp, key_sexp ) )
434         goto problem;
435
436     return VLC_SUCCESS;
437
438 problem:
439     if( p ) gcry_mpi_release( p );
440     if( q ) gcry_mpi_release( q );
441     if( g ) gcry_mpi_release( g );
442     if( y ) gcry_mpi_release( y );
443     if( r ) gcry_mpi_release( r );
444     if( s ) gcry_mpi_release( s );
445     if( hash ) gcry_mpi_release( hash );
446     if( key_sexp ) gcry_sexp_release( key_sexp );
447     if( sig_sexp ) gcry_sexp_release( sig_sexp );
448     if( hash_sexp ) gcry_sexp_release( hash_sexp );
449     return VLC_EGENERIC;
450 }
451
452 /*
453  * Return the long id (8 bytes) of the public key used to generate a signature
454  */
455 static uint8_t *get_issuer_from_signature_v4( signature_packet_v4_t *p_sig )
456 {
457     uint8_t *p = p_sig->unhashed_data;
458     uint8_t *max_pos = p + scalar_number( p_sig->unhashed_data_len, 2 );
459
460     while( p < max_pos )
461     {
462         int i_subpacket_len = *p < 192 ? *p++ :
463                 *p < 255 ? ((*p++ - 192) << 8) + *p++ + 192 :
464                 ((*++p) << 24) + (*++p << 16) + (*++p << 8) + *++p;
465
466         if( p >= max_pos - 1 )
467             return NULL;
468
469         if( *p == ISSUER_SUBPACKET )
470             return p+1;
471         else
472             p += i_subpacket_len;
473     }
474     return NULL;
475 }
476
477 /*
478  * fill a public_key_t with public key data, including:
479  *   * public key packet
480  *   * signature packet issued by key which long id is p_sig_issuer
481  *   * user id packet
482  */
483 static int parse_public_key( const uint8_t *p_key_data, size_t i_key_len, public_key_t *p_key, const uint8_t *p_sig_issuer )
484 {
485     uint8_t *pos = (uint8_t*) p_key_data;
486     uint8_t *max_pos = pos + i_key_len;
487
488     int i_status = 0;
489 #define PUBLIC_KEY_FOUND    0x01
490 #define USER_ID_FOUND       0x02
491 #define SIGNATURE_FOUND     0X04
492
493     uint8_t *p_key_unarmored = NULL;
494
495     signature_packet_v4_t sig;
496
497     p_key->psz_username = NULL;
498     p_key->sig.hashed_data = p_key->sig.unhashed_data = NULL;
499
500     if( !( *pos & 0x80 ) )
501     {   /* first byte is ASCII, unarmoring */
502         p_key_unarmored = (uint8_t*)malloc( i_key_len );
503         if( !p_key_unarmored )
504             return VLC_ENOMEM;
505         int i_len = pgp_unarmor( (char*)p_key_data, i_key_len,
506                                  p_key_unarmored, i_key_len );
507
508         if( i_len == 0 )
509             goto error;
510
511         pos = p_key_unarmored;
512         max_pos = pos + i_len;
513     }
514
515     while( pos < max_pos )
516     {
517         if( !(*pos & 0x80) || *pos & 0x40 )
518             goto error;
519
520         int i_type = packet_type( *pos );
521
522         int i_header_len = packet_header_len( *pos++ );
523         if( pos + i_header_len > max_pos )
524             goto error;
525
526         int i_packet_len = scalar_number( pos, i_header_len );
527         pos += i_header_len;
528
529         if( pos + i_packet_len > max_pos )
530             goto error;
531
532         switch( i_type )
533         {
534             uint8_t *p_issuer;
535
536             case PUBLIC_KEY_PACKET:
537                 i_status |= PUBLIC_KEY_FOUND;
538                 if( parse_public_key_packet( &p_key->key, pos, i_packet_len ) != VLC_SUCCESS )
539                     goto error;
540                 break;
541
542             case SIGNATURE_PACKET:
543                 if( !p_sig_issuer || i_status & SIGNATURE_FOUND ||
544                     parse_signature_v4_packet( &sig, pos, i_packet_len ) != VLC_SUCCESS )
545                     break;
546                 p_issuer = get_issuer_from_signature_v4( &sig );
547                 if( memcmp( p_issuer, p_sig_issuer, 8 ) == 0 )
548                 {
549                     memcpy( &p_key->sig, &sig, sizeof( signature_packet_v4_t ) );
550                     i_status |= SIGNATURE_FOUND;
551                 }
552                 else
553                 {
554                     free( sig.hashed_data );
555                     free( sig.unhashed_data );
556                 }
557                 break;
558
559             case USER_ID_PACKET:
560                 if( p_key->psz_username ) /* save only the first User ID */
561                     break;
562                 i_status |= USER_ID_FOUND;
563                 p_key->psz_username = (uint8_t*)malloc( i_packet_len + 1);
564                 if( !p_key->psz_username )
565                     goto error;
566
567                 memcpy( p_key->psz_username, pos, i_packet_len );
568                 p_key->psz_username[i_packet_len] = '\0';
569                 break;
570             
571             default:
572                 break;
573         }
574         pos += i_packet_len;
575     }
576     free( p_key_unarmored );
577
578     if( !( i_status & ( PUBLIC_KEY_FOUND + USER_ID_FOUND ) ) )
579         return VLC_EGENERIC;
580
581     if( p_sig_issuer && !( i_status & SIGNATURE_FOUND ) )
582         return VLC_EGENERIC;
583
584     return VLC_SUCCESS;
585
586 error:
587     free( p_key->sig.hashed_data );
588     free( p_key->sig.unhashed_data );
589     free( p_key->psz_username );
590     free( p_key_unarmored );
591     return VLC_EGENERIC;
592 }
593
594 /*
595  * return a sha1 hash of a file
596  */
597 static uint8_t *hash_sha1_from_file( const char *psz_file,
598                             signature_packet_v3_t *p_sig )
599 {
600     FILE *f = utf8_fopen( psz_file, "r" );
601     if( !f )
602         return NULL;
603
604     uint8_t buffer[4096]; //FIXME
605
606     gcry_md_hd_t hd;
607     if( gcry_md_open( &hd, GCRY_MD_SHA1, 0 ) )
608     {
609         fclose( f );
610         return NULL;
611     } 
612
613     size_t i_read;
614     while( ( i_read = fread( buffer, 1, sizeof(buffer), f ) ) > 0 )
615         gcry_md_write( hd, buffer, i_read );
616
617     gcry_md_putc( hd, p_sig->type );
618     gcry_md_write( hd, &p_sig->timestamp, 4 );
619
620     fclose( f );
621     gcry_md_final( hd );
622
623     return( (uint8_t*) gcry_md_read( hd, GCRY_MD_SHA1) );
624 }
625
626 /*
627  * download a public key (the last one) from videolan server, and parse it
628  */
629 static public_key_t *download_key( vlc_object_t *p_this, const uint8_t *p_longid, const uint8_t *p_signature_issuer )
630 {
631     char *psz_url;
632     if( asprintf( &psz_url, "http://download.videolan.org/pub/keys/%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x.asc",
633                             p_longid[0], p_longid[1],
634                             p_longid[2], p_longid[3],
635                             p_longid[4], p_longid[5],
636                             p_longid[6], p_longid[7] ) == -1 )
637         return NULL;
638
639     stream_t *p_stream = stream_UrlNew( p_this, psz_url );
640     free( psz_url );
641     if( !p_stream )
642         return NULL;
643
644     int64_t i_size = stream_Size( p_stream );
645     if( i_size < 0 )
646     {
647         stream_Delete( p_stream );
648         return NULL;
649     }
650
651     uint8_t *p_buf = (uint8_t*)malloc( i_size );
652     if( !p_buf )
653     {
654         stream_Delete( p_stream );
655         return NULL;
656     }
657
658     int i_read = stream_Read( p_stream, p_buf, (int)i_size );
659     stream_Delete( p_stream );
660
661     if( i_read != (int)i_size )
662     {
663         free( p_buf );
664         return NULL;
665     }
666
667     public_key_t *p_pkey = (public_key_t*) malloc( sizeof( public_key_t ) );
668     if( !p_pkey )
669     {
670         free( p_buf );
671         return NULL;
672     }
673
674     int i_error = parse_public_key( p_buf, i_read, p_pkey, p_signature_issuer );
675     free( p_buf );
676
677     if( i_error != VLC_SUCCESS )
678     {
679         free( p_pkey );
680         return NULL;
681     }
682
683     return p_pkey;
684 }
685
686 /*
687  * Generate a SHA-1 hash on a public key, to verify a signature made on that hash
688  * Note that we need the signature to compute the hash
689  */
690 static uint8_t *key_sign_hash( public_key_t *p_pkey )
691 {
692     gcry_error_t error = 0;
693     gcry_md_hd_t hd;
694
695     error = gcry_md_open( &hd, GCRY_MD_SHA1, 0 );
696     if( error )
697         return NULL;
698
699     gcry_md_putc( hd, 0x99 );
700
701     gcry_md_putc( hd, (418 >> 8) & 0xff );
702     gcry_md_putc( hd, 418 & 0xff );
703
704     gcry_md_write( hd, (uint8_t*)&p_pkey->key, 418 );
705
706     gcry_md_putc( hd, 0xb4 );
707
708     int i_len = strlen((char*)p_pkey->psz_username);
709
710     gcry_md_putc( hd, (i_len << 24) & 0xff );
711     gcry_md_putc( hd, (i_len << 16) & 0xff );
712     gcry_md_putc( hd, (i_len << 8) & 0xff );
713     gcry_md_putc( hd, (i_len) & 0xff );
714
715     gcry_md_write( hd, p_pkey->psz_username, i_len );
716
717     size_t i_hashed_data_len = scalar_number( p_pkey->sig.hashed_data_len, 2 );
718
719     gcry_md_putc( hd, p_pkey->sig.version );
720     gcry_md_putc( hd, p_pkey->sig.type );
721     gcry_md_putc( hd, p_pkey->sig.public_key_algo );
722     gcry_md_putc( hd, p_pkey->sig.digest_algo );
723     gcry_md_write( hd, p_pkey->sig.hashed_data_len, 2 );
724     gcry_md_write( hd, p_pkey->sig.hashed_data, i_hashed_data_len );
725
726     gcry_md_putc( hd, 0x04 );
727     gcry_md_putc( hd, 0xff );
728
729     i_hashed_data_len += 6; /* hashed data + 6 bytes header */
730
731     gcry_md_putc( hd, (i_hashed_data_len << 24) & 0xff);
732     gcry_md_putc( hd, (i_hashed_data_len << 16) &0xff );
733     gcry_md_putc( hd, (i_hashed_data_len << 8) & 0xff );
734     gcry_md_putc( hd, (i_hashed_data_len) & 0xff );
735
736     gcry_md_final( hd );
737
738     uint8_t *p_hash = gcry_md_read( hd, GCRY_MD_SHA1);
739
740     if( p_hash[0] != p_pkey->sig.hash_verification[0] ||
741         p_hash[1] != p_pkey->sig.hash_verification[1] )
742     {
743         free( p_hash );
744         return NULL;
745     }
746
747     return p_hash;
748 }
749
750
751 /*****************************************************************************
752  * Update_t functions
753  *****************************************************************************/
754
755 /**
756  * Create a new update VLC struct
757  *
758  * \param p_this the calling vlc_object
759  * \return pointer to new update_t or NULL
760  */
761 update_t *__update_New( vlc_object_t *p_this )
762 {
763     update_t *p_update;
764
765     if( p_this == NULL ) return NULL;
766
767     p_update = (update_t *)malloc( sizeof( update_t ) );
768     if( !p_update ) return NULL;
769
770     vlc_mutex_init( p_this, &p_update->lock );
771
772     p_update->p_libvlc = p_this->p_libvlc;
773
774     p_update->release.psz_url = NULL;
775     p_update->release.psz_desc = NULL;
776
777     var_Create( p_this->p_libvlc, "update-notify", VLC_VAR_INTEGER |
778                 VLC_VAR_ISCOMMAND );
779
780     return p_update;
781 }
782
783 /**
784  * Delete an update_t struct
785  *
786  * \param p_update update_t* pointer
787  * \return nothing
788  */
789 void update_Delete( update_t *p_update )
790 {
791     assert( p_update );
792
793     vlc_mutex_destroy( &p_update->lock );
794
795     var_Destroy( p_update->p_libvlc, "update-notify" );
796
797     FREENULL( p_update->release.psz_url );
798     FREENULL( p_update->release.psz_desc );
799
800     free( p_update );
801 }
802
803 /**
804  * Empty the release struct
805  *
806  * \param p_update update_t* pointer
807  * \return nothing
808  */
809 static void EmptyRelease( update_t *p_update )
810 {
811     p_update->release.i_major = 0;
812     p_update->release.i_minor = 0;
813     p_update->release.i_revision = 0;
814
815     FREENULL( p_update->release.psz_url );
816     FREENULL( p_update->release.psz_desc );
817 }
818
819 /**
820  * Get the update file and parse it
821  * *p_update has to be locked when calling this function
822  *
823  * \param p_update pointer to update struct
824  * \return nothing
825  */
826 static void GetUpdateFile( update_t *p_update )
827 {
828     stream_t *p_stream = NULL;
829     int i_major = 0;
830     int i_minor = 0;
831     int i_revision = 0;
832     unsigned char extra;
833     char *psz_line = NULL;
834
835     p_stream = stream_UrlNew( p_update->p_libvlc, UPDATE_VLC_STATUS_URL );
836     if( !p_stream )
837     {
838         msg_Err( p_update->p_libvlc, "Failed to open %s for reading",
839                  UPDATE_VLC_STATUS_URL );
840         goto error;
841     }
842
843     /* Try to read three lines */
844     if( !( psz_line = stream_ReadLine( p_stream ) ) )
845     {
846         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : missing version",
847                  UPDATE_VLC_STATUS_URL );
848         goto error;
849     }
850
851     /* first line : version number */
852     p_update->release.extra = 0;
853     switch( sscanf( psz_line, "%i.%i.%i%c", &i_major, &i_minor, &i_revision, &extra ) )
854     {
855         case 4:
856             p_update->release.extra = extra;
857         case 3:
858             p_update->release.i_major = i_major;
859             p_update->release.i_minor = i_minor;
860             p_update->release.i_revision = i_revision;
861             break;
862         default:
863             msg_Err( p_update->p_libvlc, "Update version false formated" );
864             free( psz_line );
865             goto error;
866     }
867
868     /* Second line : URL */
869     if( !( psz_line = stream_ReadLine( p_stream ) ) )
870     {
871         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : URL missing",
872                  UPDATE_VLC_STATUS_URL );
873         goto error;
874     }
875     p_update->release.psz_url = psz_line;
876
877
878     /* Third line : description */
879     if( !( psz_line = stream_ReadLine( p_stream ) ) )
880     {
881         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : description missing",
882                  UPDATE_VLC_STATUS_URL );
883         goto error;
884     }
885     p_update->release.psz_desc = psz_line;
886
887     error:
888         if( p_stream )
889             stream_Delete( p_stream );
890 }
891
892
893 /**
894  * Struct to launch the check in an other thread
895  */
896 typedef struct
897 {
898     VLC_COMMON_MEMBERS
899     update_t *p_update;
900 } update_check_thread_t;
901
902 void update_CheckReal( update_check_thread_t *p_uct );
903
904 /**
905  * Check for updates
906  *
907  * \param p_update pointer to update struct
908  * \returns nothing
909  */
910 void update_Check( update_t *p_update )
911 {
912     assert( p_update );
913
914     update_check_thread_t *p_uct = vlc_object_create( p_update->p_libvlc,
915                                             sizeof( update_check_thread_t ) );
916     p_uct->p_update = p_update;
917
918     vlc_thread_create( p_uct, "check for update", update_CheckReal,
919                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
920 }
921
922 void update_CheckReal( update_check_thread_t *p_uct )
923 {
924     vlc_mutex_lock( &p_uct->p_update->lock );
925
926     EmptyRelease( p_uct->p_update );
927     GetUpdateFile( p_uct->p_update );
928
929     vlc_mutex_unlock( &p_uct->p_update->lock );
930
931     var_TriggerCallback( p_uct->p_update->p_libvlc, "update-notify" );
932 }
933
934 /**
935  * Compare two release numbers
936  *
937  * \param p1 first release
938  * \param p2 second release
939  * \return UpdateReleaseStatus(Older|Equal|Newer)
940  */
941 static int CompareReleases( const struct update_release_t *p1,
942                             const struct update_release_t *p2 )
943 {
944     int32_t d;
945     d = ( p1->i_major << 24 ) + ( p1->i_minor << 16 ) + ( p1->i_revision << 8 )
946       - ( p2->i_major << 24 ) - ( p2->i_minor << 16 ) - ( p2->i_revision << 8 )
947       + ( p1->extra ) - ( p2->extra );
948
949     if( d < 0 )
950         return UpdateReleaseStatusOlder;
951     else if( d == 0 )
952         return UpdateReleaseStatusEqual;
953     else
954         return UpdateReleaseStatusNewer;
955 }
956
957 /**
958  * Compare a given release's version number to the current VLC's one
959  *
960  * \param p_update structure
961  * \return UpdateReleaseStatus(Older|Equal|Newer)
962  */
963 int update_CompareReleaseToCurrent( update_t *p_update )
964 {
965     assert( p_update );
966
967     struct update_release_t c;
968
969     /* get the current version number */
970     c.i_major = *PACKAGE_VERSION_MAJOR - '0';
971     c.i_minor = *PACKAGE_VERSION_MINOR - '0';
972     c.i_revision = *PACKAGE_VERSION_REVISION - '0';
973     c.extra = *PACKAGE_VERSION_EXTRA;
974
975     return CompareReleases( &p_update->release, &c );
976 }
977
978 /**
979  * Convert a long int size in bytes to a string
980  *
981  * \param l_size the size in bytes
982  * \return the size as a string
983  */
984 static char *size_str( long int l_size )
985 {
986     char *psz_tmp = NULL;
987     if( l_size >> 30 )
988         asprintf( &psz_tmp, "%.1f GB", (float)l_size/(1<<30) );
989     else if( l_size >> 20 )
990         asprintf( &psz_tmp, "%.1f MB", (float)l_size/(1<<20) );
991     else if( l_size >> 10 )
992         asprintf( &psz_tmp, "%.1f kB", (float)l_size/(1<<10) );
993     else
994         asprintf( &psz_tmp, "%ld B", l_size );
995     return psz_tmp;
996 }
997
998
999 /*
1000  * Struct to launch the download in a thread
1001  */
1002 typedef struct
1003 {
1004     VLC_COMMON_MEMBERS
1005     update_t *p_update;
1006     char *psz_destdir;
1007 } update_download_thread_t;
1008
1009 void update_DownloadReal( update_download_thread_t *p_udt );
1010
1011 /**
1012  * Download the file given in the update_t
1013  *
1014  * \param p_update structure
1015  * \param dir to store the download file
1016  * \return nothing
1017  */
1018 void update_Download( update_t *p_update, char *psz_destdir )
1019 {
1020     assert( p_update );
1021
1022     update_download_thread_t *p_udt = vlc_object_create( p_update->p_libvlc,
1023                                                       sizeof( update_download_thread_t ) );
1024
1025     p_udt->p_update = p_update;
1026     p_udt->psz_destdir = STRDUP( psz_destdir );
1027
1028     vlc_thread_create( p_udt, "download update", update_DownloadReal,
1029                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1030 }
1031 void update_DownloadReal( update_download_thread_t *p_udt )
1032 {
1033     int i_progress = 0;
1034     long int l_size;
1035     long int l_downloaded = 0;
1036     char *psz_status;
1037     char *psz_downloaded;
1038     char *psz_size;
1039     char *psz_destfile;
1040     char *psz_tmpdestfile;
1041
1042     FILE *p_file = NULL;
1043     stream_t *p_stream;
1044     void* p_buffer;
1045     int i_read;
1046
1047     update_t *p_update = p_udt->p_update;
1048     char *psz_destdir = p_udt->psz_destdir;
1049
1050     /* Open the stream */
1051     p_stream = stream_UrlNew( p_update->p_libvlc, p_update->release.psz_url );
1052
1053     if( !p_stream )
1054     {
1055         msg_Err( p_update->p_libvlc, "Failed to open %s for reading", p_update->release.psz_url );
1056     }
1057     else
1058     {
1059         /* Get the stream size and open the output file */
1060         l_size = stream_Size( p_stream );
1061
1062         psz_tmpdestfile = strrchr( p_update->release.psz_url, '/' );
1063         psz_tmpdestfile++;
1064         asprintf( &psz_destfile, "%s%s", psz_destdir, psz_tmpdestfile );
1065
1066         p_file = utf8_fopen( psz_destfile, "w" );
1067         if( !p_file )
1068         {
1069             msg_Err( p_update->p_libvlc, "Failed to open %s for writing", psz_destfile );
1070         }
1071         else
1072         {
1073             /* Create a buffer and fill it with the downloaded file */
1074             p_buffer = (void *)malloc( 1 << 10 );
1075             if( p_buffer )
1076             {
1077                 psz_size = size_str( l_size );
1078                 asprintf( &psz_status, "%s\nDownloading... O.O/%s %.1f%% done",  p_update->release.psz_url, psz_size, 0.0 );
1079                 i_progress = intf_UserProgress( p_update->p_libvlc, "Downloading ...", psz_status, 0.0, 0 );
1080                 free( psz_status );
1081
1082                 while( ( i_read = stream_Read( p_stream, p_buffer, 1 << 10 ) ) &&
1083                          !intf_ProgressIsCancelled( p_update->p_libvlc, i_progress ) )
1084                 {
1085                     fwrite( p_buffer, i_read, 1, p_file );
1086
1087                     l_downloaded += i_read;
1088                     psz_downloaded = size_str( l_downloaded );
1089                     asprintf( &psz_status, "%s\nDonwloading... %s/%s %.1f%% done", p_update->release.psz_url,
1090                               psz_size, psz_downloaded, 100.0*(float)l_downloaded/(float)l_size );
1091                     intf_ProgressUpdate( p_update->p_libvlc, i_progress, psz_status, 10.0, 0 );
1092                     free( psz_downloaded );
1093                     free( psz_status );
1094                 }
1095
1096                 /* If the user cancelled the download */
1097                 if( !intf_ProgressIsCancelled( p_update->p_libvlc, i_progress ) )
1098                 {
1099                     asprintf( &psz_status, "%s\nDone %s (100.0%%)", p_update->release.psz_url, psz_size );
1100                     intf_ProgressUpdate( p_update->p_libvlc, i_progress, psz_status, 100.0, 0 );
1101                     free( psz_status );
1102                 }
1103                 free( p_buffer );
1104                 free( psz_size );
1105             }
1106             fclose( p_file );
1107             if( intf_ProgressIsCancelled( p_update->p_libvlc, i_progress ) )
1108                 remove( psz_destfile );
1109         }
1110         stream_Delete( p_stream );
1111     }
1112     free( psz_destdir );
1113 }
1114
1115 #endif