]> git.sesse.net Git - vlc/blob - src/misc/update.c
14a930b3115e77c6aa66cdb01d1409505367a859
[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
86 /*****************************************************************************
87  * OpenPGP functions
88  *****************************************************************************/
89
90 #define packet_type( c ) ( ( c & 0x3c ) >> 2 )      /* 0x3C = 00111100 */
91 #define packet_header_len( c ) ( ( c & 0x03 ) + 1 ) /* number of bytes in a packet header */
92
93 static inline int scalar_number( uint8_t *p, int header_len )
94 {
95     if( header_len == 1 )
96         return( p[0] );
97     else if( header_len == 2 )
98         return( (p[0] << 8) + p[1] );
99     else if( header_len == 4 )
100         return( (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3] );
101     else
102         abort();
103 }
104
105 /* number of data bytes in a MPI */
106 #define mpi_len( mpi ) ( ( scalar_number( mpi, 2 ) + 7 ) / 8 )
107
108 /* 
109  * fill a public_key_packet_t structure from public key packet data
110  * verify that it is a version 4 public key packet, using DSA
111  */
112 static int parse_public_key_packet( public_key_packet_t *p_key, uint8_t *p_buf,
113                                     size_t i_packet_len )
114 {
115     if( i_packet_len != 418 )
116         return VLC_EGENERIC;
117
118     p_key->version   = *p_buf++;
119     if( p_key->version != 4 )
120         return VLC_EGENERIC;
121
122     /* warn when timestamp is > date ? */
123     memcpy( p_key->timestamp, p_buf, 4 ); p_buf += 4;
124
125     p_key->algo      = *p_buf++;
126     if( p_key->algo != PUBLIC_KEY_ALGO_DSA )
127         return VLC_EGENERIC;
128
129     memcpy( p_key->p, p_buf, 2+128 ); p_buf += 2+128;
130     if( mpi_len( p_key->p ) != 128 )
131         return VLC_EGENERIC;
132
133     memcpy( p_key->q, p_buf, 2+20 );  p_buf += 2+20;
134     if( mpi_len( p_key->q ) != 20 )
135         return VLC_EGENERIC;
136
137     memcpy( p_key->g, p_buf, 2+128 ); p_buf += 2+128;
138     if( mpi_len( p_key->g ) != 128 )
139         return VLC_EGENERIC;
140
141     memcpy( p_key->y, p_buf, 2+128 ); p_buf += 2+128;
142     if( mpi_len( p_key->y ) != 128 )
143         return VLC_EGENERIC;
144
145     return VLC_SUCCESS;
146 }
147
148 /*
149  * fill a signature_packet_v4_t from signature packet data
150  * verify that it was used with a DSA public key, using SHA-1 digest
151  */
152 static int parse_signature_v4_packet( signature_packet_v4_t *p_sig,
153                                       uint8_t *p_buf, size_t i_sig_len )
154 {
155     if( i_sig_len < 54 )
156         return VLC_EGENERIC;
157
158     p_sig->version = *p_buf++;
159     if( p_sig->version != 4 )
160         return VLC_EGENERIC;
161
162     p_sig->type = *p_buf++;
163     if( p_sig->type < GENERIC_KEY_SIGNATURE ||
164         p_sig->type > POSITIVE_KEY_SIGNATURE )
165         return VLC_EGENERIC;
166
167     p_sig->public_key_algo = *p_buf++;
168     if( p_sig->public_key_algo != PUBLIC_KEY_ALGO_DSA )
169         return VLC_EGENERIC;
170
171     p_sig->digest_algo = *p_buf++;
172     if( p_sig->digest_algo != DIGEST_ALGO_SHA1 )
173         return VLC_EGENERIC;
174
175     memcpy( p_sig->hashed_data_len, p_buf, 2 ); p_buf += 2;
176
177     size_t i_pos = 6;
178     size_t i_hashed_data_len = scalar_number( p_sig->hashed_data_len, 2 );
179     i_pos += i_hashed_data_len;
180     if( i_pos > i_sig_len - 48 ) /* r & s are 44 bytes in total, 
181                               * + the unhashed data length (2 bytes)
182                               * + the hash verification (2 bytes) */
183         return VLC_EGENERIC;
184
185     p_sig->hashed_data = (uint8_t*) malloc( i_hashed_data_len );
186     if( !p_sig->hashed_data )
187         return VLC_ENOMEM;
188     memcpy( p_sig->hashed_data, p_buf, i_hashed_data_len );
189     p_buf += i_hashed_data_len;
190
191     memcpy( p_sig->unhashed_data_len, p_buf, 2 ); p_buf += 2;
192
193     size_t i_unhashed_data_len = scalar_number( p_sig->unhashed_data_len, 2 );
194     i_pos += 2 + i_unhashed_data_len;
195     if( i_pos != i_sig_len - 46 )
196     {
197         free( p_sig->hashed_data );
198         return VLC_EGENERIC;
199     }
200
201     p_sig->unhashed_data = (uint8_t*) malloc( i_unhashed_data_len );
202     if( !p_sig->unhashed_data )
203     {
204         free( p_sig->hashed_data );
205         return VLC_ENOMEM;
206     }
207     memcpy( p_sig->unhashed_data, p_buf, i_unhashed_data_len );
208     p_buf += i_unhashed_data_len;
209
210     memcpy( p_sig->hash_verification, p_buf, 2 ); p_buf += 2;
211
212     memcpy( p_sig->r, p_buf, 22 ); p_buf += 22;
213     if( mpi_len( p_sig->r ) != 20 )
214     {
215         free( p_sig->hashed_data );
216         free( p_sig->unhashed_data );
217         return VLC_EGENERIC;
218     }
219
220     memcpy( p_sig->s, p_buf, 22 );
221     if( mpi_len( p_sig->s ) != 20 )
222     {
223         free( p_sig->hashed_data );
224         free( p_sig->unhashed_data );
225         return VLC_EGENERIC;
226     }
227
228     return VLC_SUCCESS;
229 }
230
231 /*
232  * crc_octets() was lamely copied from rfc 2440
233  * Copyright (C) The Internet Society (1998).  All Rights Reserved.
234  */
235 #define CRC24_INIT 0xB704CEL
236 #define CRC24_POLY 0x1864CFBL
237
238 static long crc_octets( uint8_t *octets, size_t len )
239 {
240     long crc = CRC24_INIT;
241     int i;
242     while (len--)
243     {
244         crc ^= (*octets++) << 16;
245         for (i = 0; i < 8; i++)
246         {
247             crc <<= 1;
248             if (crc & 0x1000000)
249                 crc ^= CRC24_POLY;
250         }
251     }
252     return crc & 0xFFFFFFL;
253 }
254
255 /*
256  * Transform an armored document in binary format
257  * Used on public keys and signatures
258  */
259 static int pgp_unarmor( char *p_ibuf, size_t i_ibuf_len,
260                         uint8_t *p_obuf, size_t i_obuf_len )
261 {
262     char *p_ipos = p_ibuf;
263     uint8_t *p_opos = p_obuf;
264     int i_end = 0;
265
266     int i_header_skipped = 0;
267
268     while( !i_end && p_ipos < p_ibuf + i_ibuf_len )
269     {
270         if( *p_ipos == '\r' || *p_ipos == '\n' )
271         {
272             p_ipos++;
273             continue;
274         }
275
276         size_t i_line_len = strcspn( p_ipos, "\r\n" );
277         if( i_line_len == 0 )
278             continue;
279
280         if( !i_header_skipped )
281         {
282             if( !strncmp( p_ipos, "-----BEGIN PGP", 14 ) )
283                 i_header_skipped = 1;
284
285             p_ipos += i_line_len + 1;
286             continue;
287         }
288         
289         if( !strncmp( p_ipos, "Version:", 8 ) )
290         {
291             p_ipos += i_line_len + 1;
292             continue;
293         }
294
295         if( p_ipos[i_line_len - 1] == '=' )
296         {
297             i_end = 1;
298             p_ipos[i_line_len - 1] = '\0';
299         }
300         else
301             p_ipos[i_line_len] = '\0';
302
303         p_opos += vlc_b64_decode_binary_to_buffer(  p_opos,
304                                                     p_obuf - p_opos + i_obuf_len,
305                                                     p_ipos );
306
307         p_ipos += i_line_len + 1;
308     }
309
310     /* XXX: the CRC is OPTIONAL, really require it ? */
311     if( p_ipos + 5 > p_ibuf + i_ibuf_len || *p_ipos++ != '=' )
312         return 0;
313
314     uint8_t p_crc[3];
315     if( vlc_b64_decode_binary_to_buffer( p_crc, 3, p_ipos ) != 3 )
316         return 0;
317
318     long l_crc = crc_octets( p_obuf, p_opos - p_obuf );
319     long l_crc2 = ( 0 << 24 ) + ( p_crc[0] << 16 ) + ( p_crc[1] << 8 ) + p_crc[2];
320
321     return l_crc2 == l_crc ? p_opos - p_obuf : 0;
322 }
323
324 /*
325  * Download the signature associated to a document or a binary file.
326  * We're given the file's url, we just append ".asc" to it and download 
327  */
328 static int download_signature(  vlc_object_t *p_this,
329                                 signature_packet_v3_t *p_sig, char *psz_url )
330 {
331     char *psz_sig = (char*) malloc( strlen( psz_url ) + 4 + 1 ); /* ".asc" + \0 */
332     if( !psz_sig )
333         return VLC_ENOMEM;
334
335     strcpy( psz_sig, psz_url );
336     strcat( psz_sig, ".asc" );
337
338     stream_t *p_stream = stream_UrlNew( p_this, psz_sig );
339     free( psz_sig );
340
341     if( !p_stream )
342         return VLC_ENOMEM;
343
344     int64_t i_size = stream_Size( p_stream );
345     if( i_size < 65 )
346     {
347         stream_Delete( p_stream );
348         return VLC_EGENERIC;
349     }
350     else if( i_size == 65 ) /* binary format signature */
351     {
352         int i_read = stream_Read( p_stream, p_sig, (int)i_size );
353         stream_Delete( p_stream );
354         if( i_read != i_size )
355             return VLC_EGENERIC;
356         else
357             return VLC_SUCCESS;
358     }
359
360     char *p_buf = (char*)malloc( i_size );
361     if( !p_buf )
362     {
363         stream_Delete( p_stream );
364         return VLC_ENOMEM;
365     }
366     
367     int i_read = stream_Read( p_stream, p_buf, (int)i_size );
368
369     stream_Delete( p_stream );
370
371     if( i_read != i_size )
372     {
373         free( p_buf );
374         return VLC_EGENERIC;
375     }
376     
377     int i_bytes = pgp_unarmor( p_buf, i_size, (uint8_t*)p_sig, 65 );
378     free( p_buf );
379
380     if( i_bytes != 65 )
381         return VLC_EGENERIC;
382     else
383         return VLC_SUCCESS;
384 }
385
386 /*
387  * Verify an OpenPGP signature made on some SHA-1 hash, with some DSA public key
388  */
389 static int verify_signature( vlc_object_t *p_this, uint8_t *p_r, uint8_t *p_s,
390         public_key_packet_t *p_key, uint8_t *p_hash )
391 {
392     /* the data to be verified (a SHA-1 hash) */
393     const char *hash_sexp_s = "(data(flags raw)(value %m))";
394     /* the public key */
395     const char *key_sexp_s = "(public-key(dsa(p %m)(q %m)(g %m)(y %m)))";
396     /* the signature */
397     const char *sig_sexp_s = "(sig-val(dsa(r %m )(s %m )))";
398
399     size_t erroff;
400     gcry_mpi_t p, q, g, y, r, s, hash;
401     p = q = g = y = r = s = hash = NULL;
402     gcry_sexp_t key_sexp, hash_sexp, sig_sexp;
403     key_sexp = hash_sexp = sig_sexp = NULL;
404
405     if( gcry_mpi_scan( &p, GCRYMPI_FMT_USG, p_key->p + 2, 128, NULL ) ||
406         gcry_mpi_scan( &q, GCRYMPI_FMT_USG, p_key->q + 2, 20, NULL ) ||
407         gcry_mpi_scan( &g, GCRYMPI_FMT_USG, p_key->g + 2, 128, NULL ) ||
408         gcry_mpi_scan( &y, GCRYMPI_FMT_USG, p_key->y + 2, 128, NULL ) ||
409         gcry_sexp_build( &key_sexp, &erroff, key_sexp_s, p, q, g, y ) )
410         goto problem;
411
412     if( gcry_mpi_scan( &r, GCRYMPI_FMT_USG, p_r + 2, 20, NULL ) ||
413         gcry_mpi_scan( &s, GCRYMPI_FMT_USG, p_s + 2, 20, NULL ) ||
414         gcry_sexp_build( &sig_sexp, &erroff, sig_sexp_s, r, s ) )
415         goto problem;
416
417     if( gcry_mpi_scan( &hash, GCRYMPI_FMT_USG, p_hash, 20, NULL ) ||
418         gcry_sexp_build( &hash_sexp, &erroff, hash_sexp_s, hash ) )
419         goto problem;
420
421     if( gcry_pk_verify( sig_sexp, hash_sexp, key_sexp ) )
422         goto problem;
423
424     return VLC_SUCCESS;
425
426 problem:
427     if( p ) gcry_mpi_release( p );
428     if( q ) gcry_mpi_release( q );
429     if( g ) gcry_mpi_release( g );
430     if( y ) gcry_mpi_release( y );
431     if( r ) gcry_mpi_release( r );
432     if( s ) gcry_mpi_release( s );
433     if( hash ) gcry_mpi_release( hash );
434     if( key_sexp ) gcry_sexp_release( key_sexp );
435     if( sig_sexp ) gcry_sexp_release( sig_sexp );
436     if( hash_sexp ) gcry_sexp_release( hash_sexp );
437     return VLC_EGENERIC;
438 }
439
440 /*
441  * Return the long id (8 bytes) of the public key used to generate a signature
442  */
443 static uint8_t *get_issuer_from_signature_v4( signature_packet_v4_t *p_sig )
444 {
445     uint8_t *p = p_sig->unhashed_data;
446     uint8_t *max_pos = p + scalar_number( p_sig->unhashed_data_len, 2 );
447
448     while( p < max_pos )
449     {
450         int i_subpacket_len = *p < 192 ? *p++ :
451                 *p < 255 ? ((*p++ - 192) << 8) + *p++ + 192 :
452                 ((*++p) << 24) + (*++p << 16) + (*++p << 8) + *++p;
453
454         if( p >= max_pos - 1 )
455             return NULL;
456
457         if( *p == ISSUER_SUBPACKET )
458             return p+1;
459         else
460             p += i_subpacket_len;
461     }
462     return NULL;
463 }
464
465 /*
466  * fill a public_key_t with public key data, including:
467  *   * public key packet
468  *   * signature packet issued by key which long id is p_sig_issuer
469  *   * user id packet
470  */
471 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 )
472 {
473     uint8_t *pos = (uint8_t*) p_key_data;
474     uint8_t *max_pos = pos + i_key_len;
475
476     int i_status = 0;
477 #define PUBLIC_KEY_FOUND    0x01
478 #define USER_ID_FOUND       0x02
479 #define SIGNATURE_FOUND     0X04
480
481     uint8_t *p_key_unarmored = NULL;
482
483     signature_packet_v4_t sig;
484
485     p_key->psz_username = NULL;
486     p_key->sig.hashed_data = p_key->sig.unhashed_data = NULL;
487
488     if( !( *pos & 0x80 ) )
489     {   /* first byte is ASCII, unarmoring */
490         p_key_unarmored = (uint8_t*)malloc( i_key_len );
491         if( !p_key_unarmored )
492             return VLC_ENOMEM;
493         int i_len = pgp_unarmor( (char*)p_key_data, i_key_len,
494                                  p_key_unarmored, i_key_len );
495
496         if( i_len == 0 )
497             goto error;
498
499         pos = p_key_unarmored;
500         max_pos = pos + i_len;
501     }
502
503     while( pos < max_pos )
504     {
505         if( !(*pos & 0x80) || *pos & 0x40 )
506             goto error;
507
508         int i_type = packet_type( *pos );
509
510         int i_header_len = packet_header_len( *pos++ );
511         if( pos + i_header_len > max_pos )
512             goto error;
513
514         int i_packet_len = scalar_number( pos, i_header_len );
515         pos += i_header_len;
516
517         if( pos + i_packet_len > max_pos )
518             goto error;
519
520         switch( i_type )
521         {
522             uint8_t *p_issuer;
523
524             case PUBLIC_KEY_PACKET:
525                 i_status |= PUBLIC_KEY_FOUND;
526                 if( parse_public_key_packet( &p_key->key, pos, i_packet_len ) != VLC_SUCCESS )
527                     goto error;
528                 break;
529
530             case SIGNATURE_PACKET:
531                 if( !p_sig_issuer || i_status & SIGNATURE_FOUND ||
532                     parse_signature_v4_packet( &sig, pos, i_packet_len ) != VLC_SUCCESS )
533                     break;
534                 p_issuer = get_issuer_from_signature_v4( &sig );
535                 if( memcmp( p_issuer, p_sig_issuer, 8 ) == 0 )
536                 {
537                     memcpy( &p_key->sig, &sig, sizeof( signature_packet_v4_t ) );
538                     i_status |= SIGNATURE_FOUND;
539                 }
540                 else
541                 {
542                     free( sig.hashed_data );
543                     free( sig.unhashed_data );
544                 }
545                 break;
546
547             case USER_ID_PACKET:
548                 if( p_key->psz_username ) /* save only the first User ID */
549                     break;
550                 i_status |= USER_ID_FOUND;
551                 p_key->psz_username = (uint8_t*)malloc( i_packet_len + 1);
552                 if( !p_key->psz_username )
553                     goto error;
554
555                 memcpy( p_key->psz_username, pos, i_packet_len );
556                 p_key->psz_username[i_packet_len] = '\0';
557                 break;
558             
559             default:
560                 break;
561         }
562         pos += i_packet_len;
563     }
564     free( p_key_unarmored );
565
566     if( !( i_status & ( PUBLIC_KEY_FOUND + USER_ID_FOUND ) ) )
567         return VLC_EGENERIC;
568
569     if( p_sig_issuer && !( i_status & SIGNATURE_FOUND ) )
570         return VLC_EGENERIC;
571
572     return VLC_SUCCESS;
573
574 error:
575     free( p_key->sig.hashed_data );
576     free( p_key->sig.unhashed_data );
577     free( p_key->psz_username );
578     free( p_key_unarmored );
579     return VLC_EGENERIC;
580 }
581
582 /*
583  * return a sha1 hash of a file
584  */
585 static uint8_t *hash_sha1_from_file( const char *psz_file,
586                             signature_packet_v3_t *p_sig )
587 {
588     FILE *f = utf8_fopen( psz_file, "r" );
589     if( !f )
590         return NULL;
591
592     uint8_t buffer[4096]; //FIXME
593
594     gcry_md_hd_t hd;
595     if( gcry_md_open( &hd, GCRY_MD_SHA1, 0 ) )
596     {
597         fclose( f );
598         return NULL;
599     } 
600
601     size_t i_read;
602     while( ( i_read = fread( buffer, 1, sizeof(buffer), f ) ) > 0 )
603         gcry_md_write( hd, buffer, i_read );
604
605     gcry_md_putc( hd, p_sig->type );
606     gcry_md_write( hd, &p_sig->timestamp, 4 );
607
608     fclose( f );
609     gcry_md_final( hd );
610
611     return( (uint8_t*) gcry_md_read( hd, GCRY_MD_SHA1) );
612 }
613
614 /*
615  * download a public key (the last one) from videolan server, and parse it
616  */
617 static public_key_t *download_key( vlc_object_t *p_this, const uint8_t *p_longid, const uint8_t *p_signature_issuer )
618 {
619     char *psz_url;
620     if( asprintf( &psz_url, "http://download.videolan.org/pub/keys/%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x.asc",
621                             p_longid[0], p_longid[1],
622                             p_longid[2], p_longid[3],
623                             p_longid[4], p_longid[5],
624                             p_longid[6], p_longid[7] ) == -1 )
625         return NULL;
626
627     stream_t *p_stream = stream_UrlNew( p_this, psz_url );
628     free( psz_url );
629     if( !p_stream )
630         return NULL;
631
632     int64_t i_size = stream_Size( p_stream );
633     if( i_size < 0 )
634     {
635         stream_Delete( p_stream );
636         return NULL;
637     }
638
639     uint8_t *p_buf = (uint8_t*)malloc( i_size );
640     if( !p_buf )
641     {
642         stream_Delete( p_stream );
643         return NULL;
644     }
645
646     int i_read = stream_Read( p_stream, p_buf, (int)i_size );
647     stream_Delete( p_stream );
648
649     if( i_read != (int)i_size )
650     {
651         free( p_buf );
652         return NULL;
653     }
654
655     public_key_t *p_pkey = (public_key_t*) malloc( sizeof( public_key_t ) );
656     if( !p_pkey )
657     {
658         free( p_buf );
659         return NULL;
660     }
661
662     int i_error = parse_public_key( p_buf, i_read, p_pkey, p_signature_issuer );
663     free( p_buf );
664
665     if( i_error != VLC_SUCCESS )
666     {
667         free( p_pkey );
668         return NULL;
669     }
670
671     return p_pkey;
672 }
673
674 /*
675  * Generate a SHA-1 hash on a public key, to verify a signature made on that hash
676  * Note that we need the signature to compute the hash
677  */
678 static uint8_t *key_sign_hash( public_key_t *p_pkey )
679 {
680     gcry_error_t error = 0;
681     gcry_md_hd_t hd;
682
683     error = gcry_md_open( &hd, GCRY_MD_SHA1, 0 );
684     if( error )
685         return NULL;
686
687     gcry_md_putc( hd, 0x99 );
688
689     gcry_md_putc( hd, (418 >> 8) & 0xff );
690     gcry_md_putc( hd, 418 & 0xff );
691
692     gcry_md_write( hd, (uint8_t*)&p_pkey->key, 418 );
693
694     gcry_md_putc( hd, 0xb4 );
695
696     int i_len = strlen((char*)p_pkey->psz_username);
697
698     gcry_md_putc( hd, (i_len << 24) & 0xff );
699     gcry_md_putc( hd, (i_len << 16) & 0xff );
700     gcry_md_putc( hd, (i_len << 8) & 0xff );
701     gcry_md_putc( hd, (i_len) & 0xff );
702
703     gcry_md_write( hd, p_pkey->psz_username, i_len );
704
705     size_t i_hashed_data_len = scalar_number( p_pkey->sig.hashed_data_len, 2 );
706
707     gcry_md_putc( hd, p_pkey->sig.version );
708     gcry_md_putc( hd, p_pkey->sig.type );
709     gcry_md_putc( hd, p_pkey->sig.public_key_algo );
710     gcry_md_putc( hd, p_pkey->sig.digest_algo );
711     gcry_md_write( hd, p_pkey->sig.hashed_data_len, 2 );
712     gcry_md_write( hd, p_pkey->sig.hashed_data, i_hashed_data_len );
713
714     gcry_md_putc( hd, 0x04 );
715     gcry_md_putc( hd, 0xff );
716
717     i_hashed_data_len += 6; /* hashed data + 6 bytes header */
718
719     gcry_md_putc( hd, (i_hashed_data_len << 24) & 0xff);
720     gcry_md_putc( hd, (i_hashed_data_len << 16) &0xff );
721     gcry_md_putc( hd, (i_hashed_data_len << 8) & 0xff );
722     gcry_md_putc( hd, (i_hashed_data_len) & 0xff );
723
724     gcry_md_final( hd );
725
726     uint8_t *p_hash = gcry_md_read( hd, GCRY_MD_SHA1);
727
728     if( p_hash[0] != p_pkey->sig.hash_verification[0] ||
729         p_hash[1] != p_pkey->sig.hash_verification[1] )
730     {
731         free( p_hash );
732         return NULL;
733     }
734
735     return p_hash;
736 }
737
738
739 /*****************************************************************************
740  * Update_t functions
741  *****************************************************************************/
742
743 /**
744  * Create a new update VLC struct
745  *
746  * \param p_this the calling vlc_object
747  * \return pointer to new update_t or NULL
748  */
749 update_t *__update_New( vlc_object_t *p_this )
750 {
751     update_t *p_update;
752
753     if( p_this == NULL ) return NULL;
754
755     p_update = (update_t *)malloc( sizeof( update_t ) );
756     if( !p_update ) return NULL;
757
758     vlc_mutex_init( p_this, &p_update->lock );
759
760     p_update->p_libvlc = p_this->p_libvlc;
761
762     p_update->release.psz_svnrev = NULL;
763     p_update->release.psz_extra = NULL;
764     p_update->release.psz_url = NULL;
765     p_update->release.psz_desc = NULL;
766
767     return p_update;
768 }
769
770 /**
771  * Delete an update_t struct
772  *
773  * \param p_update update_t* pointer
774  * \return nothing
775  */
776 void update_Delete( update_t *p_update )
777 {
778     assert( p_update );
779
780     vlc_mutex_destroy( &p_update->lock );
781
782     FREENULL( p_update->release.psz_svnrev );
783     FREENULL( p_update->release.psz_extra );
784     FREENULL( p_update->release.psz_url );
785     FREENULL( p_update->release.psz_desc );
786
787     free( p_update );
788 }
789
790 /**
791  * Empty the release struct
792  *
793  * \param p_update update_t* pointer
794  * \return nothing
795  */
796 static void EmptyRelease( update_t *p_update )
797 {
798     p_update->release.i_major = 0;
799     p_update->release.i_minor = 0;
800     p_update->release.i_revision = 0;
801
802     FREENULL( p_update->release.psz_svnrev );
803     FREENULL( p_update->release.psz_extra );
804     FREENULL( p_update->release.psz_url );
805     FREENULL( p_update->release.psz_desc );
806 }
807
808 /**
809  * Get the update file and parse it
810  * *p_update has to be unlocked when calling this function
811  *
812  * \param p_update pointer to update struct
813  * \return nothing
814  */
815 static void GetUpdateFile( update_t *p_update )
816 {
817     stream_t *p_stream = NULL;
818     int i_major = 0;
819     int i_minor = 0;
820     int i_revision = 0;
821     char *psz_extra = NULL;
822     char *psz_svnrev = NULL;
823     char *psz_line = NULL;
824
825     vlc_mutex_lock( &p_update->lock );
826
827     p_stream = stream_UrlNew( p_update->p_libvlc, UPDATE_VLC_STATUS_URL );
828     if( !p_stream )
829     {
830         msg_Err( p_update->p_libvlc, "Failed to open %s for reading",
831                  UPDATE_VLC_STATUS_URL );
832         goto error;
833     }
834
835     /* Try to read three lines */
836     if( !( psz_line = stream_ReadLine( p_stream ) ) )
837     {
838         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : missing version",
839                  UPDATE_VLC_STATUS_URL );
840         goto error;
841     }
842
843     /* first line : version number */
844     if( sscanf( psz_line, "%i.%i.%i%as %as", &i_major, &i_minor, &i_revision, &psz_extra, &psz_svnrev ) )
845     {
846         p_update->release.i_major = i_major;
847         p_update->release.i_minor = i_minor;
848         p_update->release.i_revision = i_revision;
849
850         p_update->release.psz_svnrev = psz_svnrev ? psz_svnrev : STRDUP( "" );
851         p_update->release.psz_extra = psz_extra ? psz_extra : STRDUP( "" );
852     }
853     else
854     {
855         msg_Err( p_update->p_libvlc, "Update version false formated" );
856         free( psz_line );
857         goto error;
858     }
859
860     /* Second line : URL */
861     if( !( psz_line = stream_ReadLine( p_stream ) ) )
862     {
863         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : URL missing",
864                  UPDATE_VLC_STATUS_URL );
865         goto error;
866     }
867     p_update->release.psz_url = psz_line;
868
869
870     /* Third line : description */
871     if( !( psz_line = stream_ReadLine( p_stream ) ) )
872     {
873         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : description missing",
874                  UPDATE_VLC_STATUS_URL );
875         goto error;
876     }
877     p_update->release.psz_desc = psz_line;
878
879     error:
880         vlc_mutex_unlock( &p_update->lock );
881
882         if( p_stream )
883             stream_Delete( p_stream );
884 }
885
886 /**
887  * Check for updates
888  *
889  * \param p_update pointer to update struct
890  * \returns nothing
891  */
892 void update_Check( update_t *p_update )
893 {
894     assert( p_update );
895
896     EmptyRelease( p_update );
897
898     GetUpdateFile( p_update );
899 }
900
901 /**
902  * Compare two extra
903  *
904  * \param p1 first integer
905  * \param p2 second integer
906  * \return like strcmp
907  */
908 static int extracmp( char *psz_1, char *psz_2 )
909 {
910     if( psz_1[0] == '-' )
911     {
912         if( psz_2[0] == '-' )
913             return strcmp( psz_1, psz_2 );
914         else
915             return 1;
916     }
917     else
918     {
919         if( psz_2[0] == '-' )
920             return -1;
921         else
922             return strcmp( psz_1, psz_2 );
923     }
924 }
925 /**
926  * Compare two release numbers
927  *
928  * \param p1 first release
929  * \param p2 second release
930  * \return UpdateReleaseStatus(Older|Equal|Newer)
931  */
932 static int CompareReleases( const struct update_release_t *p1,
933                             const struct update_release_t *p2 )
934 {
935     int32_t d;
936     d = ( p1->i_major << 24 ) + ( p1->i_minor << 16 ) + ( p1->i_revision << 8 );
937     d = d - ( p2->i_major << 24 ) - ( p2->i_minor << 16 ) - ( p2->i_revision << 8 );
938     d += extracmp( p1->psz_extra, p2->psz_extra );
939
940     if( d == 0 )
941         d = strcmp( p1->psz_svnrev, p2->psz_svnrev );
942
943     if( d < 0 )
944         return UpdateReleaseStatusOlder;
945     else if( d == 0 )
946         return UpdateReleaseStatusEqual;
947     else
948         return UpdateReleaseStatusNewer;
949 }
950
951 /**
952  * Compare a given release's version number to the current VLC's one
953  *
954  * \param p a release
955  * \return UpdateReleaseStatus(Older|Equal|Newer)
956  */
957 int update_CompareReleaseToCurrent( update_t *p_update )
958 {
959     assert( p_update );
960
961     struct update_release_t c;
962     int i_major = 0;
963     int i_minor = 0;
964     int i_revision = 0;
965     char *psz_extra;
966     int i_result = UpdateReleaseStatusOlder;
967
968     /* get the current version number */
969     if( sscanf( PACKAGE_VERSION, "%i.%i.%i%as", &i_major, &i_minor, &i_revision, &psz_extra ) )
970     {
971         c.i_major = i_major;
972         c.i_minor = i_minor;
973         c.i_revision = i_revision;
974         if( psz_extra )
975             c.psz_extra = psz_extra;
976         else
977             c.psz_extra = STRDUP( "" );
978         c.psz_svnrev = STRDUP( VLC_Changeset() );
979
980         i_result = CompareReleases( &p_update->release, &c );
981
982         free( c.psz_extra );
983         free( c.psz_svnrev );
984     }
985     return i_result;
986 }
987
988 #endif