1 /*****************************************************************************
2 * update.c: VLC update checking and downloading
3 *****************************************************************************
4 * Copyright © 2005-2008 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8 * Rémi Duraffort <ivoire at via.ecp.fr>
9 Rafaël Carré <funman@videolanorg>
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.
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.
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 *****************************************************************************/
28 * This file contains functions related to VLC update management
31 /*****************************************************************************
33 *****************************************************************************/
39 #include <vlc_common.h>
40 #include <vlc_update.h>
46 #include <vlc_pgpkey.h>
47 #include <vlc_stream.h>
48 #include <vlc_strings.h>
49 #include <vlc_charset.h>
50 #include <vlc_dialog.h>
53 #include <vlc_gcrypt.h>
56 #include "../libvlc.h"
58 /*****************************************************************************
60 *****************************************************************************/
63 * Here is the format of these "status files" :
64 * First line is the last version: "X.Y.Ze" where:
65 * * X is the major number
66 * * Y is the minor number
67 * * Z is the revision number
68 * * e is an OPTIONAL extra letter
69 * * AKA "0.8.6d" or "0.9.0"
70 * Second line is an url of the binary for this last version
71 * Remaining text is a required description of the update
74 #if defined( UNDER_CE )
75 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-ce"
76 #elif defined( WIN32 )
77 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-win-x86"
78 #elif defined( __APPLE__ )
79 # if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
80 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-ppc"
82 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-x86"
84 #elif defined( SYS_BEOS )
85 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-beos-x86"
87 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status"
91 /*****************************************************************************
93 *****************************************************************************/
96 * Create a new update VLC struct
98 * \param p_this the calling vlc_object
99 * \return pointer to new update_t or NULL
101 update_t
*__update_New( vlc_object_t
*p_this
)
106 p_update
= (update_t
*)malloc( sizeof( update_t
) );
107 if( !p_update
) return NULL
;
109 vlc_mutex_init( &p_update
->lock
);
111 p_update
->p_libvlc
= p_this
->p_libvlc
;
113 p_update
->release
.psz_url
= NULL
;
114 p_update
->release
.psz_desc
= NULL
;
116 p_update
->p_download
= NULL
;
117 p_update
->p_check
= NULL
;
119 p_update
->p_pkey
= NULL
;
126 * Delete an update_t struct
128 * \param p_update update_t* pointer
131 void update_Delete( update_t
*p_update
)
135 if( p_update
->p_check
)
137 vlc_object_kill( p_update
->p_check
);
138 vlc_thread_join( p_update
->p_check
);
139 vlc_object_release( p_update
->p_check
);
142 if( p_update
->p_download
)
144 vlc_object_kill( p_update
->p_download
);
145 vlc_thread_join( p_update
->p_download
);
146 vlc_object_release( p_update
->p_download
);
149 vlc_mutex_destroy( &p_update
->lock
);
151 free( p_update
->release
.psz_url
);
152 free( p_update
->release
.psz_desc
);
153 free( p_update
->p_pkey
);
159 * Empty the release struct
161 * \param p_update update_t* pointer
164 static void EmptyRelease( update_t
*p_update
)
166 p_update
->release
.i_major
= 0;
167 p_update
->release
.i_minor
= 0;
168 p_update
->release
.i_revision
= 0;
170 FREENULL( p_update
->release
.psz_url
);
171 FREENULL( p_update
->release
.psz_desc
);
175 * Get the update file and parse it
176 * p_update has to be locked when calling this function
178 * \param p_update pointer to update struct
179 * \return true if the update is valid and authenticated
181 static bool GetUpdateFile( update_t
*p_update
)
183 stream_t
*p_stream
= NULL
;
188 char *psz_version_line
= NULL
;
189 char *psz_update_data
= NULL
;
191 p_stream
= stream_UrlNew( p_update
->p_libvlc
, UPDATE_VLC_STATUS_URL
);
194 msg_Err( p_update
->p_libvlc
, "Failed to open %s for reading",
195 UPDATE_VLC_STATUS_URL
);
199 const int64_t i_read
= stream_Size( p_stream
);
200 psz_update_data
= malloc( i_read
+ 1 ); /* terminating '\0' */
201 if( !psz_update_data
)
204 if( stream_Read( p_stream
, psz_update_data
, i_read
) != i_read
)
206 msg_Err( p_update
->p_libvlc
, "Couldn't download update file %s",
207 UPDATE_VLC_STATUS_URL
);
210 psz_update_data
[i_read
] = '\0';
212 stream_Delete( p_stream
);
215 /* first line : version number */
216 char *psz_update_data_parser
= psz_update_data
;
217 size_t i_len
= strcspn( psz_update_data
, "\r\n" );
218 psz_update_data_parser
+= i_len
;
219 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
220 psz_update_data_parser
++;
222 if( !(psz_version_line
= malloc( i_len
+ 1)) )
224 strncpy( psz_version_line
, psz_update_data
, i_len
);
225 psz_version_line
[i_len
] = '\0';
227 p_update
->release
.extra
= 0;
228 switch( sscanf( psz_version_line
, "%i.%i.%i%c",
229 &i_major
, &i_minor
, &i_revision
, &extra
) )
232 p_update
->release
.extra
= extra
;
234 p_update
->release
.i_major
= i_major
;
235 p_update
->release
.i_minor
= i_minor
;
236 p_update
->release
.i_revision
= i_revision
;
239 msg_Err( p_update
->p_libvlc
, "Update version false formated" );
243 /* second line : URL */
244 i_len
= strcspn( psz_update_data_parser
, "\r\n" );
247 msg_Err( p_update
->p_libvlc
, "Update file %s is corrupted: URL missing",
248 UPDATE_VLC_STATUS_URL
);
253 if( !(p_update
->release
.psz_url
= malloc( i_len
+ 1)) )
255 strncpy( p_update
->release
.psz_url
, psz_update_data_parser
, i_len
);
256 p_update
->release
.psz_url
[i_len
] = '\0';
258 psz_update_data_parser
+= i_len
;
259 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
260 psz_update_data_parser
++;
262 /* Remaining data : description */
263 i_len
= strlen( psz_update_data_parser
);
266 msg_Err( p_update
->p_libvlc
,
267 "Update file %s is corrupted: description missing",
268 UPDATE_VLC_STATUS_URL
);
272 if( !(p_update
->release
.psz_desc
= malloc( i_len
+ 1)) )
274 strncpy( p_update
->release
.psz_desc
, psz_update_data_parser
, i_len
);
275 p_update
->release
.psz_desc
[i_len
] = '\0';
277 printf("desc %s\n", p_update
->release
.psz_desc
);
279 /* Now that we know the status is valid, we must download its signature
280 * to authenticate it */
281 signature_packet_t sign
;
282 if( download_signature( VLC_OBJECT( p_update
->p_libvlc
), &sign
,
283 UPDATE_VLC_STATUS_URL
) != VLC_SUCCESS
)
285 msg_Err( p_update
->p_libvlc
, "Couldn't download signature of status file" );
289 if( sign
.type
!= BINARY_SIGNATURE
&& sign
.type
!= TEXT_SIGNATURE
)
291 msg_Err( p_update
->p_libvlc
, "Invalid signature type" );
295 p_update
->p_pkey
= (public_key_t
*)malloc( sizeof( public_key_t
) );
296 if( !p_update
->p_pkey
)
299 if( parse_public_key( videolan_public_key
, sizeof( videolan_public_key
),
300 p_update
->p_pkey
, NULL
) != VLC_SUCCESS
)
302 msg_Err( p_update
->p_libvlc
, "Couldn't parse embedded public key, something went really wrong..." );
303 FREENULL( p_update
->p_pkey
);
307 memcpy( p_update
->p_pkey
->longid
, videolan_public_key_longid
, 8 );
309 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) != 0 )
311 msg_Dbg( p_update
->p_libvlc
, "Need to download the GPG key" );
312 public_key_t
*p_new_pkey
= download_key(
313 VLC_OBJECT(p_update
->p_libvlc
),
314 sign
.issuer_longid
, videolan_public_key_longid
);
317 msg_Err( p_update
->p_libvlc
, "Couldn't download GPG key" );
318 FREENULL( p_update
->p_pkey
);
322 uint8_t *p_hash
= hash_sha1_from_public_key( p_new_pkey
);
325 msg_Err( p_update
->p_libvlc
, "Failed to hash signature" );
327 FREENULL( p_update
->p_pkey
);
331 if( verify_signature( p_new_pkey
->sig
.r
, p_new_pkey
->sig
.s
,
332 &p_update
->p_pkey
->key
, p_hash
) == VLC_SUCCESS
)
335 msg_Info( p_update
->p_libvlc
, "Key authenticated" );
336 free( p_update
->p_pkey
);
337 p_update
->p_pkey
= p_new_pkey
;
342 msg_Err( p_update
->p_libvlc
, "Key signature invalid !\n" );
347 uint8_t *p_hash
= hash_sha1_from_text( psz_update_data
, &sign
);
350 msg_Warn( p_update
->p_libvlc
, "Can't compute SHA1 hash for status file" );
354 else if( p_hash
[0] != sign
.hash_verification
[0] ||
355 p_hash
[1] != sign
.hash_verification
[1] )
357 msg_Warn( p_update
->p_libvlc
, "Bad SHA1 hash for status file" );
361 else if( verify_signature( sign
.r
, sign
.s
, &p_update
->p_pkey
->key
, p_hash
)
364 msg_Err( p_update
->p_libvlc
, "BAD SIGNATURE for status file" );
370 msg_Info( p_update
->p_libvlc
, "Status file authenticated" );
376 stream_Delete( p_stream
);
377 free( psz_version_line
);
378 free( psz_update_data
);
382 static void* update_CheckReal( vlc_object_t
*p_this
);
387 * \param p_update pointer to update struct
388 * \param pf_callback pointer to a function to call when the update_check is finished
389 * \param p_data pointer to some datas to give to the callback
392 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ), void *p_data
)
396 // If the object already exist, destroy it
397 if( p_update
->p_check
)
399 vlc_object_kill( p_update
->p_check
);
400 vlc_thread_join( p_update
->p_check
);
401 vlc_object_release( p_update
->p_check
);
404 update_check_thread_t
*p_uct
=
405 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_uct
),
406 VLC_OBJECT_GENERIC
, "update check" );
409 p_uct
->p_update
= p_update
;
410 p_update
->p_check
= p_uct
;
411 p_uct
->pf_callback
= pf_callback
;
412 p_uct
->p_data
= p_data
;
414 vlc_thread_create( p_uct
, "check for update", update_CheckReal
,
415 VLC_THREAD_PRIORITY_LOW
);
418 void* update_CheckReal( vlc_object_t
* p_this
)
420 update_check_thread_t
*p_uct
= (update_check_thread_t
*)p_this
;
424 canc
= vlc_savecancel ();
425 vlc_mutex_lock( &p_uct
->p_update
->lock
);
427 EmptyRelease( p_uct
->p_update
);
428 b_ret
= GetUpdateFile( p_uct
->p_update
);
429 vlc_mutex_unlock( &p_uct
->p_update
->lock
);
431 if( p_uct
->pf_callback
)
432 (p_uct
->pf_callback
)( p_uct
->p_data
, b_ret
);
434 vlc_restorecancel (canc
);
439 * Compare a given release's version number to the current VLC's one
441 * \param p_update structure
442 * \return true if we have to upgrade to the given version to be up to date
444 static bool is_strictly_greater( int * a
, int * b
, int n
)
446 if( n
<= 0 ) return false;
447 if(a
[0] > b
[0] ) return true;
448 if(a
[0] == b
[0] ) return is_strictly_greater( a
+1, b
+1, n
-1 );
449 /* a[0] < b[0] */ return false;
452 bool update_NeedUpgrade( update_t
*p_update
)
456 int current_version
[] = {
457 *PACKAGE_VERSION_MAJOR
- '0',
458 *PACKAGE_VERSION_MINOR
- '0',
459 *PACKAGE_VERSION_REVISION
- '0',
460 /* extra string of development versions is "-git", "-rc" ..
461 * so make sure version a.b.c is newer than a.b.c-XXX */
462 (*PACKAGE_VERSION_EXTRA
== '-') ? -1 : *PACKAGE_VERSION_EXTRA
464 int latest_version
[] = {
465 p_update
->release
.i_major
,
466 p_update
->release
.i_minor
,
467 p_update
->release
.i_revision
,
468 p_update
->release
.extra
471 return is_strictly_greater( latest_version
, current_version
, 4 );
475 * Convert a long int size in bytes to a string
477 * \param l_size the size in bytes
478 * \return the size as a string
480 static char *size_str( long int l_size
)
482 char *psz_tmp
= NULL
;
485 i_retval
= asprintf( &psz_tmp
, _("%.1f GB"), (float)l_size
/(1<<30) );
486 else if( l_size
>> 20 )
487 i_retval
= asprintf( &psz_tmp
, _("%.1f MB"), (float)l_size
/(1<<20) );
488 else if( l_size
>> 10 )
489 i_retval
= asprintf( &psz_tmp
, _("%.1f kB"), (float)l_size
/(1<<10) );
491 i_retval
= asprintf( &psz_tmp
, _("%ld B"), l_size
);
493 return i_retval
== -1 ? NULL
: psz_tmp
;
496 static void* update_DownloadReal( vlc_object_t
*p_this
);
499 * Download the file given in the update_t
501 * \param p_update structure
502 * \param dir to store the download file
505 void update_Download( update_t
*p_update
, const char *psz_destdir
)
509 // If the object already exist, destroy it
510 if( p_update
->p_download
)
512 vlc_object_kill( p_update
->p_download
);
513 vlc_thread_join( p_update
->p_download
);
514 vlc_object_release( p_update
->p_download
);
517 update_download_thread_t
*p_udt
=
518 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_udt
),
519 VLC_OBJECT_GENERIC
, "update download" );
523 p_udt
->p_update
= p_update
;
524 p_update
->p_download
= p_udt
;
525 p_udt
->psz_destdir
= psz_destdir
? strdup( psz_destdir
) : NULL
;
527 vlc_thread_create( p_udt
, "download update", update_DownloadReal
,
528 VLC_THREAD_PRIORITY_LOW
);
531 static void* update_DownloadReal( vlc_object_t
*p_this
)
533 update_download_thread_t
*p_udt
= (update_download_thread_t
*)p_this
;
534 dialog_progress_bar_t
*p_progress
= NULL
;
536 long int l_downloaded
= 0;
538 char *psz_status
= NULL
;
539 char *psz_downloaded
= NULL
;
540 char *psz_size
= NULL
;
541 char *psz_destfile
= NULL
;
542 char *psz_tmpdestfile
= NULL
;
545 stream_t
*p_stream
= NULL
;
546 void* p_buffer
= NULL
;
550 update_t
*p_update
= p_udt
->p_update
;
551 char *psz_destdir
= p_udt
->psz_destdir
;
553 msg_Dbg( p_udt
, "Opening Stream '%s'", p_update
->release
.psz_url
);
554 canc
= vlc_savecancel ();
556 /* Open the stream */
557 p_stream
= stream_UrlNew( p_udt
, p_update
->release
.psz_url
);
560 msg_Err( p_udt
, "Failed to open %s for reading", p_update
->release
.psz_url
);
564 /* Get the stream size */
565 l_size
= stream_Size( p_stream
);
567 /* Get the file name and open it*/
568 psz_tmpdestfile
= strrchr( p_update
->release
.psz_url
, '/' );
569 if( !psz_tmpdestfile
)
571 msg_Err( p_udt
, "The URL %s is badly formated",
572 p_update
->release
.psz_url
);
576 if( asprintf( &psz_destfile
, "%s%s", psz_destdir
, psz_tmpdestfile
) == -1 )
579 p_file
= utf8_fopen( psz_destfile
, "w" );
582 msg_Err( p_udt
, "Failed to open %s for writing", psz_destfile
);
583 dialog_FatalWait( p_udt
, _("Saving file failed"),
584 _("Failed to open \"%s\" for writing"),
589 /* Create a buffer and fill it with the downloaded file */
590 p_buffer
= (void *)malloc( 1 << 10 );
593 msg_Err( p_udt
, "Can't malloc (1 << 10) bytes! download cancelled." );
597 msg_Dbg( p_udt
, "Downloading Stream '%s'", p_update
->release
.psz_url
);
599 psz_size
= size_str( l_size
);
600 if( asprintf( &psz_status
, _("%s\nDownloading... %s/%s %.1f%% done"),
601 p_update
->release
.psz_url
, "0.0", psz_size
, 0.0 ) != -1 )
603 p_progress
= dialog_ProgressCreate( p_udt
, _( "Downloading ..."),
604 psz_status
, _("Cancel") );
608 while( vlc_object_alive( p_udt
) &&
609 ( i_read
= stream_Read( p_stream
, p_buffer
, 1 << 10 ) ) &&
610 !dialog_ProgressCancelled( p_progress
) )
612 if( fwrite( p_buffer
, i_read
, 1, p_file
) < 1 )
614 msg_Err( p_udt
, "Failed to write into %s", psz_destfile
);
618 l_downloaded
+= i_read
;
619 psz_downloaded
= size_str( l_downloaded
);
620 f_progress
= (float)l_downloaded
/(float)l_size
;
622 if( asprintf( &psz_status
, _( "%s\nDownloading... %s/%s - %.1f%% done" ),
623 p_update
->release
.psz_url
, psz_downloaded
, psz_size
,
624 f_progress
*100 ) != -1 )
626 dialog_ProgressSet( p_progress
, psz_status
, f_progress
);
629 free( psz_downloaded
);
632 /* Finish the progress bar or delete the file if the user had canceled */
636 if( vlc_object_alive( p_udt
) &&
637 !dialog_ProgressCancelled( p_progress
) )
639 if( asprintf( &psz_status
, _("%s\nDone %s (100.0%%)"),
640 p_update
->release
.psz_url
, psz_size
) != -1 )
642 dialog_ProgressDestroy( p_progress
);
649 utf8_unlink( psz_destfile
);
653 signature_packet_t sign
;
654 if( download_signature( VLC_OBJECT( p_udt
), &sign
,
655 p_update
->release
.psz_url
) != VLC_SUCCESS
)
657 utf8_unlink( psz_destfile
);
659 dialog_FatalWait( p_udt
, _("File could not be verified"),
660 _("It was not possible to download a cryptographic signature for "
661 "the downloaded file \"%s\". Thus, it was deleted."),
663 msg_Err( p_udt
, "Couldn't download signature of downloaded file" );
667 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) )
669 utf8_unlink( psz_destfile
);
670 msg_Err( p_udt
, "Invalid signature issuer" );
671 dialog_FatalWait( p_udt
, _("Invalid signature"),
672 _("The cryptographic signature for the downloaded file \"%s\" was "
673 "invalid and could not be used to securely verify it. Thus, the "
674 "file was deleted."),
679 if( sign
.type
!= BINARY_SIGNATURE
)
681 utf8_unlink( psz_destfile
);
682 msg_Err( p_udt
, "Invalid signature type" );
683 dialog_FatalWait( p_udt
, _("Invalid signature"),
684 _("The cryptographic signature for the downloaded file \"%s\" was "
685 "invalid and could not be used to securely verify it. Thus, the "
686 "file was deleted."),
691 uint8_t *p_hash
= hash_sha1_from_file( psz_destfile
, &sign
);
694 msg_Err( p_udt
, "Unable to hash %s", psz_destfile
);
695 utf8_unlink( psz_destfile
);
696 dialog_FatalWait( p_udt
, _("File not verifiable"),
697 _("It was not possible to securely verify the downloaded file"
698 " \"%s\". Thus, it was deleted."),
704 if( p_hash
[0] != sign
.hash_verification
[0] ||
705 p_hash
[1] != sign
.hash_verification
[1] )
707 utf8_unlink( psz_destfile
);
708 dialog_FatalWait( p_udt
, _("File corrupted"),
709 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
711 msg_Err( p_udt
, "Bad SHA1 hash for %s", psz_destfile
);
716 if( verify_signature( sign
.r
, sign
.s
, &p_update
->p_pkey
->key
, p_hash
)
719 utf8_unlink( psz_destfile
);
720 dialog_FatalWait( p_udt
, _("File corrupted"),
721 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
723 msg_Err( p_udt
, "BAD SIGNATURE for %s", psz_destfile
);
728 msg_Info( p_udt
, "%s authenticated", psz_destfile
);
733 dialog_ProgressDestroy( p_progress
);
735 stream_Delete( p_stream
);
739 free( psz_destfile
);
743 vlc_restorecancel( canc
);
747 update_release_t
*update_GetRelease( update_t
*p_update
)
749 return &p_update
->release
;
753 update_t
*__update_New( vlc_object_t
*p_this
)
759 void update_Delete( update_t
*p_update
)
764 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ),
767 (void)p_update
; (void)pf_callback
; (void)p_data
;
770 bool update_NeedUpgrade( update_t
*p_update
)
776 void update_Download( update_t
*p_update
, const char *psz_destdir
)
778 (void)p_update
; (void)psz_destdir
;
781 update_release_t
*update_GetRelease( update_t
*p_update
)