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>
50 #include <vlc_dialog.h>
51 #include <vlc_interface.h>
54 #include <vlc_gcrypt.h>
59 #include "../libvlc.h"
61 /*****************************************************************************
63 *****************************************************************************/
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
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"
85 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-mac-x86"
87 #elif defined( SYS_BEOS )
88 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-beos-x86"
90 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status"
94 /*****************************************************************************
96 *****************************************************************************/
100 * Create a new update VLC struct
102 * \param p_this the calling vlc_object
103 * \return pointer to new update_t or NULL
105 update_t
*update_New( vlc_object_t
*p_this
)
110 p_update
= (update_t
*)malloc( sizeof( update_t
) );
111 if( !p_update
) return NULL
;
113 vlc_mutex_init( &p_update
->lock
);
115 p_update
->p_libvlc
= p_this
->p_libvlc
;
117 p_update
->release
.psz_url
= NULL
;
118 p_update
->release
.psz_desc
= NULL
;
120 p_update
->p_download
= NULL
;
121 p_update
->p_check
= NULL
;
123 p_update
->p_pkey
= NULL
;
130 * Delete an update_t struct
132 * \param p_update update_t* pointer
135 void update_Delete( update_t
*p_update
)
139 if( p_update
->p_check
)
141 vlc_object_kill( p_update
->p_check
);
142 vlc_thread_join( p_update
->p_check
);
143 vlc_object_release( p_update
->p_check
);
146 if( p_update
->p_download
)
148 vlc_object_kill( p_update
->p_download
);
149 vlc_thread_join( p_update
->p_download
);
150 vlc_object_release( p_update
->p_download
);
153 vlc_mutex_destroy( &p_update
->lock
);
155 free( p_update
->release
.psz_url
);
156 free( p_update
->release
.psz_desc
);
157 free( p_update
->p_pkey
);
163 * Empty the release struct
165 * \param p_update update_t* pointer
168 static void EmptyRelease( update_t
*p_update
)
170 p_update
->release
.i_major
= 0;
171 p_update
->release
.i_minor
= 0;
172 p_update
->release
.i_revision
= 0;
174 FREENULL( p_update
->release
.psz_url
);
175 FREENULL( p_update
->release
.psz_desc
);
179 * Get the update file and parse it
180 * p_update has to be locked when calling this function
182 * \param p_update pointer to update struct
183 * \return true if the update is valid and authenticated
185 static bool GetUpdateFile( update_t
*p_update
)
187 stream_t
*p_stream
= NULL
;
192 char *psz_version_line
= NULL
;
193 char *psz_update_data
= NULL
;
195 p_stream
= stream_UrlNew( p_update
->p_libvlc
, UPDATE_VLC_STATUS_URL
);
198 msg_Err( p_update
->p_libvlc
, "Failed to open %s for reading",
199 UPDATE_VLC_STATUS_URL
);
203 const int64_t i_read
= stream_Size( p_stream
);
204 psz_update_data
= malloc( i_read
+ 1 ); /* terminating '\0' */
205 if( !psz_update_data
)
208 if( stream_Read( p_stream
, psz_update_data
, i_read
) != i_read
)
210 msg_Err( p_update
->p_libvlc
, "Couldn't download update file %s",
211 UPDATE_VLC_STATUS_URL
);
214 psz_update_data
[i_read
] = '\0';
216 stream_Delete( p_stream
);
219 /* first line : version number */
220 char *psz_update_data_parser
= psz_update_data
;
221 size_t i_len
= strcspn( psz_update_data
, "\r\n" );
222 psz_update_data_parser
+= i_len
;
223 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
224 psz_update_data_parser
++;
226 if( !(psz_version_line
= malloc( i_len
+ 1)) )
228 strncpy( psz_version_line
, psz_update_data
, i_len
);
229 psz_version_line
[i_len
] = '\0';
231 p_update
->release
.extra
= 0;
232 switch( sscanf( psz_version_line
, "%i.%i.%i%c",
233 &i_major
, &i_minor
, &i_revision
, &extra
) )
236 p_update
->release
.extra
= extra
;
238 p_update
->release
.i_major
= i_major
;
239 p_update
->release
.i_minor
= i_minor
;
240 p_update
->release
.i_revision
= i_revision
;
243 msg_Err( p_update
->p_libvlc
, "Update version false formated" );
247 /* second line : URL */
248 i_len
= strcspn( psz_update_data_parser
, "\r\n" );
251 msg_Err( p_update
->p_libvlc
, "Update file %s is corrupted: URL missing",
252 UPDATE_VLC_STATUS_URL
);
257 if( !(p_update
->release
.psz_url
= malloc( i_len
+ 1)) )
259 strncpy( p_update
->release
.psz_url
, psz_update_data_parser
, i_len
);
260 p_update
->release
.psz_url
[i_len
] = '\0';
262 psz_update_data_parser
+= i_len
;
263 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
264 psz_update_data_parser
++;
266 /* Remaining data : description */
267 i_len
= strlen( psz_update_data_parser
);
270 msg_Err( p_update
->p_libvlc
,
271 "Update file %s is corrupted: description missing",
272 UPDATE_VLC_STATUS_URL
);
276 if( !(p_update
->release
.psz_desc
= malloc( i_len
+ 1)) )
278 strncpy( p_update
->release
.psz_desc
, psz_update_data_parser
, i_len
);
279 p_update
->release
.psz_desc
[i_len
] = '\0';
281 /* Now that we know the status is valid, we must download its signature
282 * to authenticate it */
283 signature_packet_t sign
;
284 if( download_signature( VLC_OBJECT( p_update
->p_libvlc
), &sign
,
285 UPDATE_VLC_STATUS_URL
) != VLC_SUCCESS
)
287 msg_Err( p_update
->p_libvlc
, "Couldn't download signature of status file" );
291 if( sign
.type
!= BINARY_SIGNATURE
&& sign
.type
!= TEXT_SIGNATURE
)
293 msg_Err( p_update
->p_libvlc
, "Invalid signature type" );
297 p_update
->p_pkey
= (public_key_t
*)malloc( sizeof( public_key_t
) );
298 if( !p_update
->p_pkey
)
301 if( parse_public_key( videolan_public_key
, sizeof( videolan_public_key
),
302 p_update
->p_pkey
, NULL
) != VLC_SUCCESS
)
304 msg_Err( p_update
->p_libvlc
, "Couldn't parse embedded public key, something went really wrong..." );
305 FREENULL( p_update
->p_pkey
);
309 memcpy( p_update
->p_pkey
->longid
, videolan_public_key_longid
, 8 );
311 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) != 0 )
313 msg_Dbg( p_update
->p_libvlc
, "Need to download the GPG key" );
314 public_key_t
*p_new_pkey
= download_key(
315 VLC_OBJECT(p_update
->p_libvlc
),
316 sign
.issuer_longid
, videolan_public_key_longid
);
319 msg_Err( p_update
->p_libvlc
, "Couldn't download GPG key" );
320 FREENULL( p_update
->p_pkey
);
324 uint8_t *p_hash
= hash_sha1_from_public_key( p_new_pkey
);
327 msg_Err( p_update
->p_libvlc
, "Failed to hash signature" );
329 FREENULL( p_update
->p_pkey
);
333 if( verify_signature( p_new_pkey
->sig
.r
, p_new_pkey
->sig
.s
,
334 &p_update
->p_pkey
->key
, p_hash
) == VLC_SUCCESS
)
337 msg_Info( p_update
->p_libvlc
, "Key authenticated" );
338 free( p_update
->p_pkey
);
339 p_update
->p_pkey
= p_new_pkey
;
344 msg_Err( p_update
->p_libvlc
, "Key signature invalid !\n" );
349 uint8_t *p_hash
= hash_sha1_from_text( psz_update_data
, &sign
);
352 msg_Warn( p_update
->p_libvlc
, "Can't compute SHA1 hash for status file" );
356 else if( p_hash
[0] != sign
.hash_verification
[0] ||
357 p_hash
[1] != sign
.hash_verification
[1] )
359 msg_Warn( p_update
->p_libvlc
, "Bad SHA1 hash for status file" );
363 else if( verify_signature( sign
.r
, sign
.s
, &p_update
->p_pkey
->key
, p_hash
)
366 msg_Err( p_update
->p_libvlc
, "BAD SIGNATURE for status file" );
372 msg_Info( p_update
->p_libvlc
, "Status file authenticated" );
378 stream_Delete( p_stream
);
379 free( psz_version_line
);
380 free( psz_update_data
);
384 static void* update_CheckReal( vlc_object_t
*p_this
);
389 * \param p_update pointer to update struct
390 * \param pf_callback pointer to a function to call when the update_check is finished
391 * \param p_data pointer to some datas to give to the callback
394 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ), void *p_data
)
398 // If the object already exist, destroy it
399 if( p_update
->p_check
)
401 vlc_object_kill( p_update
->p_check
);
402 vlc_thread_join( p_update
->p_check
);
403 vlc_object_release( p_update
->p_check
);
406 update_check_thread_t
*p_uct
=
407 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_uct
),
408 VLC_OBJECT_GENERIC
, "update check" );
411 p_uct
->p_update
= p_update
;
412 p_update
->p_check
= p_uct
;
413 p_uct
->pf_callback
= pf_callback
;
414 p_uct
->p_data
= p_data
;
416 vlc_thread_create( p_uct
, "check for update", update_CheckReal
,
417 VLC_THREAD_PRIORITY_LOW
);
420 void* update_CheckReal( vlc_object_t
* p_this
)
422 update_check_thread_t
*p_uct
= (update_check_thread_t
*)p_this
;
426 canc
= vlc_savecancel ();
427 vlc_mutex_lock( &p_uct
->p_update
->lock
);
429 EmptyRelease( p_uct
->p_update
);
430 b_ret
= GetUpdateFile( p_uct
->p_update
);
431 vlc_mutex_unlock( &p_uct
->p_update
->lock
);
433 if( p_uct
->pf_callback
)
434 (p_uct
->pf_callback
)( p_uct
->p_data
, b_ret
);
436 vlc_restorecancel (canc
);
441 * Compare a given release's version number to the current VLC's one
443 * \param p_update structure
444 * \return true if we have to upgrade to the given version to be up to date
446 static bool is_strictly_greater( int * a
, int * b
, int n
)
448 if( n
<= 0 ) return false;
449 if(a
[0] > b
[0] ) return true;
450 if(a
[0] == b
[0] ) return is_strictly_greater( a
+1, b
+1, n
-1 );
451 /* a[0] < b[0] */ return false;
454 bool update_NeedUpgrade( update_t
*p_update
)
458 int current_version
[] = {
459 *PACKAGE_VERSION_MAJOR
- '0',
460 *PACKAGE_VERSION_MINOR
- '0',
461 *PACKAGE_VERSION_REVISION
- '0',
462 /* extra string of development versions is "-git", "-rc" ..
463 * so make sure version a.b.c is newer than a.b.c-XXX */
464 (*PACKAGE_VERSION_EXTRA
== '-') ? -1 : *PACKAGE_VERSION_EXTRA
466 int latest_version
[] = {
467 p_update
->release
.i_major
,
468 p_update
->release
.i_minor
,
469 p_update
->release
.i_revision
,
470 p_update
->release
.extra
473 return is_strictly_greater( latest_version
, current_version
, 4 );
477 * Convert a long int size in bytes to a string
479 * \param l_size the size in bytes
480 * \return the size as a string
482 static char *size_str( long int l_size
)
484 char *psz_tmp
= NULL
;
487 i_retval
= asprintf( &psz_tmp
, _("%.1f GiB"), (float)l_size
/(1<<30) );
488 else if( l_size
>> 20 )
489 i_retval
= asprintf( &psz_tmp
, _("%.1f MiB"), (float)l_size
/(1<<20) );
490 else if( l_size
>> 10 )
491 i_retval
= asprintf( &psz_tmp
, _("%.1f KiB"), (float)l_size
/(1<<10) );
493 i_retval
= asprintf( &psz_tmp
, _("%ld B"), l_size
);
495 return i_retval
== -1 ? NULL
: psz_tmp
;
498 static void* update_DownloadReal( vlc_object_t
*p_this
);
501 * Download the file given in the update_t
503 * \param p_update structure
504 * \param dir to store the download file
507 void update_Download( update_t
*p_update
, const char *psz_destdir
)
511 // If the object already exist, destroy it
512 if( p_update
->p_download
)
514 vlc_object_kill( p_update
->p_download
);
515 vlc_thread_join( p_update
->p_download
);
516 vlc_object_release( p_update
->p_download
);
519 update_download_thread_t
*p_udt
=
520 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_udt
),
521 VLC_OBJECT_GENERIC
, "update download" );
525 p_udt
->p_update
= p_update
;
526 p_update
->p_download
= p_udt
;
527 p_udt
->psz_destdir
= psz_destdir
? strdup( psz_destdir
) : NULL
;
529 vlc_thread_create( p_udt
, "download update", update_DownloadReal
,
530 VLC_THREAD_PRIORITY_LOW
);
533 static void* update_DownloadReal( vlc_object_t
*p_this
)
535 update_download_thread_t
*p_udt
= (update_download_thread_t
*)p_this
;
536 dialog_progress_bar_t
*p_progress
= NULL
;
538 long int l_downloaded
= 0;
540 char *psz_status
= NULL
;
541 char *psz_downloaded
= NULL
;
542 char *psz_size
= NULL
;
543 char *psz_destfile
= NULL
;
544 char *psz_tmpdestfile
= NULL
;
547 stream_t
*p_stream
= NULL
;
548 void* p_buffer
= NULL
;
552 update_t
*p_update
= p_udt
->p_update
;
553 char *psz_destdir
= p_udt
->psz_destdir
;
555 msg_Dbg( p_udt
, "Opening Stream '%s'", p_update
->release
.psz_url
);
556 canc
= vlc_savecancel ();
558 /* Open the stream */
559 p_stream
= stream_UrlNew( p_udt
, p_update
->release
.psz_url
);
562 msg_Err( p_udt
, "Failed to open %s for reading", p_update
->release
.psz_url
);
566 /* Get the stream size */
567 l_size
= stream_Size( p_stream
);
569 /* Get the file name and open it*/
570 psz_tmpdestfile
= strrchr( p_update
->release
.psz_url
, '/' );
571 if( !psz_tmpdestfile
)
573 msg_Err( p_udt
, "The URL %s is badly formated",
574 p_update
->release
.psz_url
);
578 if( asprintf( &psz_destfile
, "%s%s", psz_destdir
, psz_tmpdestfile
) == -1 )
581 p_file
= vlc_fopen( psz_destfile
, "w" );
584 msg_Err( p_udt
, "Failed to open %s for writing", psz_destfile
);
585 dialog_FatalWait( p_udt
, _("Saving file failed"),
586 _("Failed to open \"%s\" for writing"),
591 /* Create a buffer and fill it with the downloaded file */
592 p_buffer
= (void *)malloc( 1 << 10 );
595 msg_Err( p_udt
, "Can't malloc (1 << 10) bytes! download cancelled." );
599 msg_Dbg( p_udt
, "Downloading Stream '%s'", p_update
->release
.psz_url
);
601 psz_size
= size_str( l_size
);
602 if( asprintf( &psz_status
, _("%s\nDownloading... %s/%s %.1f%% done"),
603 p_update
->release
.psz_url
, "0.0", psz_size
, 0.0 ) != -1 )
605 p_progress
= dialog_ProgressCreate( p_udt
, _( "Downloading ..."),
606 psz_status
, _("Cancel") );
610 while( vlc_object_alive( p_udt
) &&
611 ( i_read
= stream_Read( p_stream
, p_buffer
, 1 << 10 ) ) &&
612 !dialog_ProgressCancelled( p_progress
) )
614 if( fwrite( p_buffer
, i_read
, 1, p_file
) < 1 )
616 msg_Err( p_udt
, "Failed to write into %s", psz_destfile
);
620 l_downloaded
+= i_read
;
621 psz_downloaded
= size_str( l_downloaded
);
622 f_progress
= (float)l_downloaded
/(float)l_size
;
624 if( asprintf( &psz_status
, _( "%s\nDownloading... %s/%s - %.1f%% done" ),
625 p_update
->release
.psz_url
, psz_downloaded
, psz_size
,
626 f_progress
*100 ) != -1 )
628 dialog_ProgressSet( p_progress
, psz_status
, f_progress
);
631 free( psz_downloaded
);
634 /* Finish the progress bar or delete the file if the user had canceled */
638 if( vlc_object_alive( p_udt
) &&
639 !dialog_ProgressCancelled( p_progress
) )
641 if( asprintf( &psz_status
, _("%s\nDone %s (100.0%%)"),
642 p_update
->release
.psz_url
, psz_size
) != -1 )
644 dialog_ProgressDestroy( p_progress
);
651 vlc_unlink( psz_destfile
);
655 signature_packet_t sign
;
656 if( download_signature( VLC_OBJECT( p_udt
), &sign
,
657 p_update
->release
.psz_url
) != VLC_SUCCESS
)
659 vlc_unlink( psz_destfile
);
661 dialog_FatalWait( p_udt
, _("File could not be verified"),
662 _("It was not possible to download a cryptographic signature for "
663 "the downloaded file \"%s\". Thus, it was deleted."),
665 msg_Err( p_udt
, "Couldn't download signature of downloaded file" );
669 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) )
671 vlc_unlink( psz_destfile
);
672 msg_Err( p_udt
, "Invalid signature issuer" );
673 dialog_FatalWait( p_udt
, _("Invalid signature"),
674 _("The cryptographic signature for the downloaded file \"%s\" was "
675 "invalid and could not be used to securely verify it. Thus, the "
676 "file was deleted."),
681 if( sign
.type
!= BINARY_SIGNATURE
)
683 vlc_unlink( psz_destfile
);
684 msg_Err( p_udt
, "Invalid signature type" );
685 dialog_FatalWait( p_udt
, _("Invalid signature"),
686 _("The cryptographic signature for the downloaded file \"%s\" was "
687 "invalid and could not be used to securely verify it. Thus, the "
688 "file was deleted."),
693 uint8_t *p_hash
= hash_sha1_from_file( psz_destfile
, &sign
);
696 msg_Err( p_udt
, "Unable to hash %s", psz_destfile
);
697 vlc_unlink( psz_destfile
);
698 dialog_FatalWait( p_udt
, _("File not verifiable"),
699 _("It was not possible to securely verify the downloaded file"
700 " \"%s\". Thus, it was deleted."),
706 if( p_hash
[0] != sign
.hash_verification
[0] ||
707 p_hash
[1] != sign
.hash_verification
[1] )
709 vlc_unlink( psz_destfile
);
710 dialog_FatalWait( p_udt
, _("File corrupted"),
711 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
713 msg_Err( p_udt
, "Bad SHA1 hash for %s", psz_destfile
);
718 if( verify_signature( sign
.r
, sign
.s
, &p_update
->p_pkey
->key
, p_hash
)
721 vlc_unlink( psz_destfile
);
722 dialog_FatalWait( p_udt
, _("File corrupted"),
723 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
725 msg_Err( p_udt
, "BAD SIGNATURE for %s", psz_destfile
);
730 msg_Info( p_udt
, "%s authenticated", psz_destfile
);
734 int answer
= dialog_Question( p_udt
, _("Update VLC media player"),
735 _("The new version was successfully downloaded. Do you want to close VLC and install it now?"),
736 _("Install"), _("Cancel"), NULL
);
740 wchar_t psz_wdestfile
[MAX_PATH
];
741 MultiByteToWideChar( CP_UTF8
, 0, psz_destfile
, -1, psz_wdestfile
, MAX_PATH
);
742 answer
= ShellExecuteW( NULL
, L
"open", psz_wdestfile
, NULL
, NULL
, SW_SHOW
);
744 libvlc_Quit(p_this
->p_libvlc
);
749 dialog_ProgressDestroy( p_progress
);
751 stream_Delete( p_stream
);
755 free( psz_destfile
);
759 vlc_restorecancel( canc
);
763 update_release_t
*update_GetRelease( update_t
*p_update
)
765 return &p_update
->release
;
770 update_t
*update_New( vlc_object_t
*p_this
)
776 void update_Delete( update_t
*p_update
)
781 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ),
784 (void)p_update
; (void)pf_callback
; (void)p_data
;
787 bool update_NeedUpgrade( update_t
*p_update
)
793 void update_Download( update_t
*p_update
, const char *psz_destdir
)
795 (void)p_update
; (void)psz_destdir
;
798 update_release_t
*update_GetRelease( update_t
*p_update
)