]> git.sesse.net Git - vlc/blob - src/misc/update.c
Update mechanism: split update.c
[vlc] / src / misc / update.c
1 /*****************************************************************************
2  * update.c: VLC update checking and downloading
3  *****************************************************************************
4  * Copyright © 2005-2008 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 update management
29  */
30
31 /*****************************************************************************
32  * Preamble
33  *****************************************************************************/
34
35 #ifdef HAVE_CONFIG_H
36 # include "config.h"
37 #endif
38 #ifdef HAVE_SYS_STAT_H
39 #   include <sys/stat.h>
40 #endif
41
42 #include <vlc_common.h>
43 #include <vlc_update.h>
44
45 #ifdef UPDATE_CHECK
46
47 #include <assert.h>
48
49 #include <vlc_pgpkey.h>
50 #include <vlc_stream.h>
51 #include <vlc_strings.h>
52 #include <vlc_charset.h>
53 #include <vlc_dialog.h>
54
55 #include <gcrypt.h>
56 #include <vlc_gcrypt.h>
57
58 #include "update.h"
59 #include "../libvlc.h"
60
61 /*****************************************************************************
62  * Misc defines
63  *****************************************************************************/
64
65 /*
66  * Here is the format of these "status files" :
67  * First line is the last version: "X.Y.Ze" where:
68  *      * X is the major number
69  *      * Y is the minor number
70  *      * Z is the revision number
71  *      * e is an OPTIONAL extra letter
72  *      * AKA "0.8.6d" or "0.9.0"
73  * Second line is an url of the binary for this last version
74  * Remaining text is a required description of the update
75  */
76
77 #if defined( UNDER_CE )
78 #   define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-ce"
79 #elif defined( WIN32 )
80 #   define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-win-x86"
81 #elif defined( __APPLE__ )
82 #   if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
83 #       define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-ppc"
84 #   else
85 #       define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-x86"
86 #   endif
87 #elif defined( SYS_BEOS )
88 #       define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-beos-x86"
89 #else
90 #   define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status"
91 #endif
92
93
94 /*****************************************************************************
95  * Local Prototypes
96  *****************************************************************************/
97 static void EmptyRelease( update_t *p_update );
98 static bool GetUpdateFile( update_t *p_update );
99 static char * size_str( long int l_size );
100
101
102
103 /*****************************************************************************
104  * Update_t functions
105  *****************************************************************************/
106
107 /**
108  * Create a new update VLC struct
109  *
110  * \param p_this the calling vlc_object
111  * \return pointer to new update_t or NULL
112  */
113 update_t *__update_New( vlc_object_t *p_this )
114 {
115     update_t *p_update;
116     assert( p_this );
117
118     p_update = (update_t *)malloc( sizeof( update_t ) );
119     if( !p_update ) return NULL;
120
121     vlc_mutex_init( &p_update->lock );
122
123     p_update->p_libvlc = p_this->p_libvlc;
124
125     p_update->release.psz_url = NULL;
126     p_update->release.psz_desc = NULL;
127
128     p_update->p_download = NULL;
129     p_update->p_check = NULL;
130
131     p_update->p_pkey = NULL;
132     vlc_gcrypt_init();
133
134     return p_update;
135 }
136
137 /**
138  * Delete an update_t struct
139  *
140  * \param p_update update_t* pointer
141  * \return nothing
142  */
143 void update_Delete( update_t *p_update )
144 {
145     assert( p_update );
146
147     if( p_update->p_check )
148     {
149         assert( !p_update->p_download );
150         vlc_object_kill( p_update->p_check );
151         vlc_thread_join( p_update->p_check );
152         vlc_object_release( p_update->p_check );
153     }
154     else if( p_update->p_download )
155     {
156         vlc_object_kill( p_update->p_download );
157         vlc_thread_join( p_update->p_download );
158         vlc_object_release( p_update->p_download );
159     }
160
161     vlc_mutex_destroy( &p_update->lock );
162
163     free( p_update->release.psz_url );
164     free( p_update->release.psz_desc );
165     free( p_update->p_pkey );
166
167     free( p_update );
168 }
169
170 /**
171  * Empty the release struct
172  *
173  * \param p_update update_t* pointer
174  * \return nothing
175  */
176 static void EmptyRelease( update_t *p_update )
177 {
178     p_update->release.i_major = 0;
179     p_update->release.i_minor = 0;
180     p_update->release.i_revision = 0;
181
182     FREENULL( p_update->release.psz_url );
183     FREENULL( p_update->release.psz_desc );
184 }
185
186 /**
187  * Get the update file and parse it
188  * p_update has to be locked when calling this function
189  *
190  * \param p_update pointer to update struct
191  * \return true if the update is valid and authenticated
192  */
193 static bool GetUpdateFile( update_t *p_update )
194 {
195     stream_t *p_stream = NULL;
196     int i_major = 0;
197     int i_minor = 0;
198     int i_revision = 0;
199     unsigned char extra;
200     char *psz_version_line = NULL;
201
202     p_stream = stream_UrlNew( p_update->p_libvlc, UPDATE_VLC_STATUS_URL );
203     if( !p_stream )
204     {
205         msg_Err( p_update->p_libvlc, "Failed to open %s for reading",
206                  UPDATE_VLC_STATUS_URL );
207         goto error;
208     }
209
210     /* Start reading the status file */
211     if( !( psz_version_line = stream_ReadLine( p_stream ) ) )
212     {
213         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : missing version",
214                  UPDATE_VLC_STATUS_URL );
215         goto error;
216     }
217
218     /* first line : version number */
219     p_update->release.extra = 0;
220     switch( sscanf( psz_version_line, "%i.%i.%i%c",
221                     &i_major, &i_minor, &i_revision, &extra ) )
222     {
223         case 4:
224             p_update->release.extra = extra;
225         case 3:
226             p_update->release.i_major = i_major;
227             p_update->release.i_minor = i_minor;
228             p_update->release.i_revision = i_revision;
229             break;
230         default:
231             msg_Err( p_update->p_libvlc, "Update version false formated" );
232             goto error;
233     }
234
235     /* second line : URL */
236     if( !( p_update->release.psz_url = stream_ReadLine( p_stream ) ) )
237     {
238         msg_Err( p_update->p_libvlc, "Update file %s is corrupted : URL missing",
239                  UPDATE_VLC_STATUS_URL );
240         goto error;
241     }
242
243     /* Remaining data : description */
244     int i_read = stream_Size( p_stream ) - stream_Tell( p_stream );
245     if( i_read <= 0 )
246     {
247         msg_Err( p_update->p_libvlc,
248                 "Update file %s is corrupted: description missing",
249                 UPDATE_VLC_STATUS_URL );
250         goto error;
251     }
252
253     p_update->release.psz_desc = (char*) malloc( i_read + 1 );
254     if( !p_update->release.psz_desc )
255         goto error;
256
257     if( stream_Read( p_stream, p_update->release.psz_desc, i_read ) != i_read )
258     {
259         msg_Err( p_update->p_libvlc, "Couldn't download update file %s",
260                 UPDATE_VLC_STATUS_URL );
261         goto error;
262     }
263     p_update->release.psz_desc[i_read] = '\0';
264
265     stream_Delete( p_stream );
266     p_stream = NULL;
267
268     /* Now that we know the status is valid, we must download its signature
269      * to authenticate it */
270     signature_packet_t sign;
271     if( download_signature( VLC_OBJECT( p_update->p_libvlc ), &sign,
272             UPDATE_VLC_STATUS_URL ) != VLC_SUCCESS )
273     {
274         msg_Err( p_update->p_libvlc, "Couldn't download signature of status file" );
275         goto error;
276     }
277
278     if( sign.type != BINARY_SIGNATURE && sign.type != TEXT_SIGNATURE )
279     {
280         msg_Err( p_update->p_libvlc, "Invalid signature type" );
281         goto error;
282     }
283
284     p_update->p_pkey = (public_key_t*)malloc( sizeof( public_key_t ) );
285     if( !p_update->p_pkey )
286         goto error;
287
288     if( parse_public_key( videolan_public_key, sizeof( videolan_public_key ),
289                         p_update->p_pkey, NULL ) != VLC_SUCCESS )
290     {
291         msg_Err( p_update->p_libvlc, "Couldn't parse embedded public key, something went really wrong..." );
292         FREENULL( p_update->p_pkey );
293         goto error;
294     }
295
296     memcpy( p_update->p_pkey->longid, videolan_public_key_longid, 8 );
297
298     if( memcmp( sign.issuer_longid, p_update->p_pkey->longid , 8 ) != 0 )
299     {
300         msg_Dbg( p_update->p_libvlc, "Need to download the GPG key" );
301         public_key_t *p_new_pkey = download_key(
302                 VLC_OBJECT(p_update->p_libvlc),
303                 sign.issuer_longid, videolan_public_key_longid );
304         if( !p_new_pkey )
305         {
306             msg_Err( p_update->p_libvlc, "Couldn't download GPG key" );
307             FREENULL( p_update->p_pkey );
308             goto error;
309         }
310
311         uint8_t *p_hash = hash_sha1_from_public_key( p_new_pkey );
312         if( !p_hash )
313         {
314             msg_Err( p_update->p_libvlc, "Failed to hash signature" );
315             free( p_new_pkey );
316             FREENULL( p_update->p_pkey );
317             goto error;
318         }
319
320         if( verify_signature( p_new_pkey->sig.r, p_new_pkey->sig.s,
321                     &p_update->p_pkey->key, p_hash ) == VLC_SUCCESS )
322         {
323             free( p_hash );
324             msg_Info( p_update->p_libvlc, "Key authenticated" );
325             free( p_update->p_pkey );
326             p_update->p_pkey = p_new_pkey;
327         }
328         else
329         {
330             free( p_hash );
331             msg_Err( p_update->p_libvlc, "Key signature invalid !\n" );
332             goto error;
333         }
334     }
335
336     /* FIXME : read the status file all at once instead of line per line */
337     char *psz_text;
338     if( asprintf( &psz_text, "%s\n%s\n%s", psz_version_line,
339                 p_update->release.psz_url, p_update->release.psz_desc ) == -1 )
340     {
341         goto error;
342     }
343     FREENULL( psz_version_line );
344
345     uint8_t *p_hash = hash_sha1_from_text( psz_text, &sign );
346     if( !p_hash )
347     {
348         msg_Warn( p_update->p_libvlc, "Can't compute SHA1 hash for status file" );
349         goto error;
350     }
351
352     else if( p_hash[0] != sign.hash_verification[0] ||
353         p_hash[1] != sign.hash_verification[1] )
354     {
355         msg_Warn( p_update->p_libvlc, "Bad SHA1 hash for status file" );
356         goto error;
357     }
358
359     else if( verify_signature( sign.r, sign.s, &p_update->p_pkey->key, p_hash )
360             != VLC_SUCCESS )
361     {
362         msg_Err( p_update->p_libvlc, "BAD SIGNATURE for status file" );
363         goto error;
364     }
365
366     else
367     {
368         msg_Info( p_update->p_libvlc, "Status file authenticated" );
369         return true;
370     }
371
372 error:
373     if( p_stream )
374         stream_Delete( p_stream );
375     free( psz_version_line );
376     return false;
377 }
378
379 static void* update_CheckReal( vlc_object_t *p_this );
380
381 /**
382  * Check for updates
383  *
384  * \param p_update pointer to update struct
385  * \param pf_callback pointer to a function to call when the update_check is finished
386  * \param p_data pointer to some datas to give to the callback
387  * \returns nothing
388  */
389 void update_Check( update_t *p_update, void (*pf_callback)( void*, bool ), void *p_data )
390 {
391     assert( p_update );
392
393     // If the object already exist, destroy it
394     if( p_update->p_check )
395     {
396         vlc_object_kill( p_update->p_check );
397         vlc_thread_join( p_update->p_check );
398         vlc_object_release( p_update->p_check );
399     }
400
401     update_check_thread_t *p_uct =
402         vlc_custom_create( p_update->p_libvlc, sizeof( *p_uct ),
403                            VLC_OBJECT_GENERIC, "update check" );
404     if( !p_uct ) return;
405
406     p_uct->p_update = p_update;
407     p_update->p_check = p_uct;
408     p_uct->pf_callback = pf_callback;
409     p_uct->p_data = p_data;
410
411     vlc_thread_create( p_uct, "check for update", update_CheckReal,
412                        VLC_THREAD_PRIORITY_LOW );
413 }
414
415 void* update_CheckReal( vlc_object_t* p_this )
416 {
417     update_check_thread_t *p_uct = (update_check_thread_t *)p_this;
418     bool b_ret;
419     int canc;
420
421     canc = vlc_savecancel ();
422     vlc_mutex_lock( &p_uct->p_update->lock );
423
424     EmptyRelease( p_uct->p_update );
425     b_ret = GetUpdateFile( p_uct->p_update );
426     vlc_mutex_unlock( &p_uct->p_update->lock );
427
428     if( p_uct->pf_callback )
429         (p_uct->pf_callback)( p_uct->p_data, b_ret );
430
431     vlc_restorecancel (canc);
432     return NULL;
433 }
434
435 /**
436  * Compare a given release's version number to the current VLC's one
437  *
438  * \param p_update structure
439  * \return true if we have to upgrade to the given version to be up to date
440  */
441 static bool is_strictly_greater( int * a, int * b, int n)
442 {
443     if( n <= 0 ) return false;
444     if(a[0] > b[0] ) return true;
445     if(a[0] == b[0] ) return is_strictly_greater( a+1, b+1, n-1 );
446     /* a[0] < b[0] */ return false;
447 }
448
449 bool update_NeedUpgrade( update_t *p_update )
450 {
451     assert( p_update );
452
453     int current_version[] = {
454         *PACKAGE_VERSION_MAJOR - '0',
455         *PACKAGE_VERSION_MINOR - '0',
456         *PACKAGE_VERSION_REVISION - '0',
457         *PACKAGE_VERSION_EXTRA
458     };
459     int latest_version[] = {
460         p_update->release.i_major,
461         p_update->release.i_minor,
462         p_update->release.i_revision,
463         p_update->release.extra
464     };
465
466     return is_strictly_greater( latest_version, current_version, 4 );
467 }
468
469 /**
470  * Convert a long int size in bytes to a string
471  *
472  * \param l_size the size in bytes
473  * \return the size as a string
474  */
475 static char *size_str( long int l_size )
476 {
477     char *psz_tmp = NULL;
478     int i_retval = 0;
479     if( l_size >> 30 )
480         i_retval = asprintf( &psz_tmp, _("%.1f GB"), (float)l_size/(1<<30) );
481     else if( l_size >> 20 )
482         i_retval = asprintf( &psz_tmp, _("%.1f MB"), (float)l_size/(1<<20) );
483     else if( l_size >> 10 )
484         i_retval = asprintf( &psz_tmp, _("%.1f kB"), (float)l_size/(1<<10) );
485     else
486         i_retval = asprintf( &psz_tmp, _("%ld B"), l_size );
487
488     return i_retval == -1 ? NULL : psz_tmp;
489 }
490
491 static void* update_DownloadReal( vlc_object_t *p_this );
492
493 /**
494  * Download the file given in the update_t
495  *
496  * \param p_update structure
497  * \param destination to store the download file
498  *        This can be an existing dir, a (non)existing target fullpath filename or
499  *        NULL for the current working dir.
500  * \return nothing
501  */
502 void update_Download( update_t *p_update, const char *destination )
503 {
504     assert( p_update );
505
506     // If the object already exist, destroy it
507     if( p_update->p_download )
508     {
509         vlc_object_kill( p_update->p_download );
510         vlc_thread_join( p_update->p_download );
511         vlc_object_release( p_update->p_download );
512     }
513
514     update_download_thread_t *p_udt =
515         vlc_custom_create( p_update->p_libvlc, sizeof( *p_udt ),
516                            VLC_OBJECT_GENERIC, "update download" );
517     if( !p_udt )
518         return;
519
520     p_udt->p_update = p_update;
521     p_update->p_download = p_udt;
522     p_udt->psz_destination = destination ? strdup( destination ) : NULL;
523
524     vlc_thread_create( p_udt, "download update", update_DownloadReal,
525                        VLC_THREAD_PRIORITY_LOW );
526 }
527
528 static void* update_DownloadReal( vlc_object_t *p_this )
529 {
530     update_download_thread_t *p_udt = (update_download_thread_t *)p_this;
531     dialog_progress_bar_t *p_progress = NULL;
532     long int l_size;
533     long int l_downloaded = 0;
534     float f_progress;
535     char *psz_status = NULL;
536     char *psz_downloaded = NULL;
537     char *psz_size = NULL;
538     char *psz_destfile = NULL;
539     char *psz_tmpdestfile = NULL;
540
541     FILE *p_file = NULL;
542     struct stat p_stat;
543     stream_t *p_stream = NULL;
544     void* p_buffer = NULL;
545     int i_read;
546     int canc;
547
548     update_t *p_update = p_udt->p_update;
549     char *psz_destination = p_udt->psz_destination;
550
551     msg_Dbg( p_udt, "Opening Stream '%s'", p_update->release.psz_url );
552     canc = vlc_savecancel ();
553
554     /* Open the stream */
555     p_stream = stream_UrlNew( p_udt, p_update->release.psz_url );
556     if( !p_stream )
557     {
558         msg_Err( p_udt, "Failed to open %s for reading", p_update->release.psz_url );
559         goto end;
560     }
561
562     /* Get the stream size */
563     l_size = stream_Size( p_stream );
564
565     /* Get the file name and open it*/
566     psz_tmpdestfile = strrchr( p_update->release.psz_url, '/' );
567     if( !psz_tmpdestfile )
568     {
569         msg_Err( p_udt, "The URL %s is badly formated",
570                  p_update->release.psz_url );
571         goto end;
572     }
573     psz_tmpdestfile++;
574
575     if( utf8_stat( psz_destination, &p_stat) == 0 && (p_stat.st_mode & S_IFDIR) )
576     {
577         if( asprintf( &psz_destfile, "%s%c%s", psz_destination, DIR_SEP_CHAR, psz_tmpdestfile ) == -1 )
578             goto end;
579     }
580     else if( psz_destination )
581         psz_destfile = strdup( psz_destination );
582     else
583         psz_destfile = strdup( psz_tmpdestfile );
584
585     p_file = utf8_fopen( psz_destfile, "w" );
586     if( !p_file )
587     {
588         msg_Err( p_udt, "Failed to open %s for writing", psz_destfile );
589         dialog_FatalWait( p_udt, _("Saving file failed"),
590             _("Failed to open \"%s\" for writing"),
591              psz_destfile );
592         goto end;
593     }
594
595     /* Create a buffer and fill it with the downloaded file */
596     p_buffer = (void *)malloc( 1 << 10 );
597     if( !p_buffer )
598     {
599         msg_Err( p_udt, "Can't malloc (1 << 10) bytes! download cancelled." );
600         goto end;
601     }
602
603     msg_Dbg( p_udt, "Downloading Stream '%s'", p_update->release.psz_url );
604
605     psz_size = size_str( l_size );
606     if( asprintf( &psz_status, _("%s\nDownloading... %s/%s %.1f%% done"),
607         p_update->release.psz_url, "0.0", psz_size, 0.0 ) != -1 )
608     {
609         p_progress = dialog_ProgressCreate( p_udt, _( "Downloading ..."),
610                                             psz_status, _("Cancel") );
611         free( psz_status );
612     }
613
614     while( vlc_object_alive( p_udt ) &&
615            ( i_read = stream_Read( p_stream, p_buffer, 1 << 10 ) ) &&
616            !dialog_ProgressCancelled( p_progress ) )
617     {
618         if( fwrite( p_buffer, i_read, 1, p_file ) < 1 )
619         {
620             msg_Err( p_udt, "Failed to write into %s", psz_destfile );
621             break;
622         }
623
624         l_downloaded += i_read;
625         psz_downloaded = size_str( l_downloaded );
626         f_progress = (float)l_downloaded/(float)l_size;
627
628         if( asprintf( &psz_status, _( "%s\nDownloading... %s/%s %.1f%% done" ),
629                       p_update->release.psz_url, psz_downloaded, psz_size,
630                       f_progress ) != -1 )
631         {
632             dialog_ProgressSet( p_progress, psz_status, f_progress );
633             free( psz_status );
634         }
635         free( psz_downloaded );
636     }
637
638     /* Finish the progress bar or delete the file if the user had canceled */
639     fclose( p_file );
640     p_file = NULL;
641
642     if( vlc_object_alive( p_udt ) &&
643         !dialog_ProgressCancelled( p_progress ) )
644     {
645         if( asprintf( &psz_status, _("%s\nDone %s (100.0%%)"),
646             p_update->release.psz_url, psz_size ) != -1 )
647         {
648             dialog_ProgressDestroy( p_progress );
649             p_progress = NULL;
650             free( psz_status );
651         }
652     }
653     else
654     {
655         utf8_unlink( psz_destfile );
656         goto end;
657     }
658
659     signature_packet_t sign;
660     if( download_signature( VLC_OBJECT( p_udt ), &sign,
661             p_update->release.psz_url ) != VLC_SUCCESS )
662     {
663         utf8_unlink( psz_destfile );
664
665         dialog_FatalWait( p_udt, _("File could not be verified"),
666             _("It was not possible to download a cryptographic signature for "
667               "the downloaded file \"%s\". Thus, it was deleted."),
668             psz_destfile );
669         msg_Err( p_udt, "Couldn't download signature of downloaded file" );
670         goto end;
671     }
672
673     if( memcmp( sign.issuer_longid, p_update->p_pkey->longid, 8 ) )
674     {
675         utf8_unlink( psz_destfile );
676         msg_Err( p_udt, "Invalid signature issuer" );
677         dialog_FatalWait( p_udt, _("Invalid signature"),
678             _("The cryptographic signature for the downloaded file \"%s\" was "
679               "invalid and could not be used to securely verify it. Thus, the "
680               "file was deleted."),
681             psz_destfile );
682         goto end;
683     }
684
685     if( sign.type != BINARY_SIGNATURE )
686     {
687         utf8_unlink( psz_destfile );
688         msg_Err( p_udt, "Invalid signature type" );
689         dialog_FatalWait( p_udt, _("Invalid signature"),
690             _("The cryptographic signature for the downloaded file \"%s\" was "
691               "invalid and could not be used to securely verify it. Thus, the "
692               "file was deleted."),
693             psz_destfile );
694         goto end;
695     }
696
697     uint8_t *p_hash = hash_sha1_from_file( psz_destfile, &sign );
698     if( !p_hash )
699     {
700         msg_Err( p_udt, "Unable to hash %s", psz_destfile );
701         utf8_unlink( psz_destfile );
702         dialog_FatalWait( p_udt, _("File not verifiable"),
703             _("It was not possible to securely verify the downloaded file"
704               " \"%s\". Thus, it was deleted."),
705             psz_destfile );
706
707         goto end;
708     }
709
710     if( p_hash[0] != sign.hash_verification[0] ||
711         p_hash[1] != sign.hash_verification[1] )
712     {
713         utf8_unlink( psz_destfile );
714         dialog_FatalWait( p_udt, _("File corrupted"),
715             _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
716              psz_destfile );
717         msg_Err( p_udt, "Bad SHA1 hash for %s", psz_destfile );
718         free( p_hash );
719         goto end;
720     }
721
722     if( verify_signature( sign.r, sign.s, &p_update->p_pkey->key, p_hash )
723             != VLC_SUCCESS )
724     {
725         utf8_unlink( psz_destfile );
726         dialog_FatalWait( p_udt, _("File corrupted"),
727             _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
728              psz_destfile );
729         msg_Err( p_udt, "BAD SIGNATURE for %s", psz_destfile );
730         free( p_hash );
731         goto end;
732     }
733
734     msg_Info( p_udt, "%s authenticated", psz_destfile );
735     free( p_hash );
736
737 end:
738     if( p_progress )
739         dialog_ProgressDestroy( p_progress );
740     if( p_stream )
741         stream_Delete( p_stream );
742     if( p_file )
743         fclose( p_file );
744     free( psz_destfile );
745     free( p_buffer );
746     free( psz_size );
747
748     free( p_udt->psz_destination );
749     p_udt->p_update->p_download = NULL;
750
751     vlc_restorecancel( canc );
752     return NULL;
753 }
754
755 update_release_t *update_GetRelease( update_t *p_update )
756 {
757     return &p_update->release;
758 }
759
760 #else
761 update_t *__update_New( vlc_object_t *p_this )
762 {
763     (void)p_this;
764     return NULL;
765 }
766
767 void update_Delete( update_t *p_update )
768 {
769     (void)p_update;
770 }
771
772 void update_Check( update_t *p_update, void (*pf_callback)( void*, bool ),
773                    void *p_data )
774 {
775     (void)p_update; (void)pf_callback; (void)p_data;
776 }
777
778 bool update_NeedUpgrade( update_t *p_update )
779 {
780     (void)p_update;
781     return false;
782 }
783
784 void update_Download( update_t *p_update, const char *psz_destdir )
785 {
786     (void)p_update; (void)psz_destdir;
787 }
788
789 update_release_t *update_GetRelease( update_t *p_update )
790 {
791     (void)p_update;
792     return NULL;
793 }
794 #endif