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