1 /*****************************************************************************
2 * update.c: VLC update checking and downloading
3 *****************************************************************************
4 * Copyright © 2005-2008 VLC authors and VideoLAN
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 it
12 * under the terms of the GNU Lesser 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 Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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.Z.E" 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 number
72 * * IE "1.2.0" or "1.1.10.1"
73 * Second line is a 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 )
81 # define UPDATE_VLC_STATUS_URL "http://update-test.videolan.org/vlc/status-win-x86"
83 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status-win-x86"
86 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status"
90 /*****************************************************************************
92 *****************************************************************************/
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_join( p_update
->p_check
->thread
, NULL
);
138 free( p_update
->p_check
);
141 if( p_update
->p_download
)
143 vlc_object_kill( p_update
->p_download
);
144 vlc_join( p_update
->p_download
->thread
, NULL
);
145 vlc_object_release( p_update
->p_download
);
148 vlc_mutex_destroy( &p_update
->lock
);
150 free( p_update
->release
.psz_url
);
151 free( p_update
->release
.psz_desc
);
152 free( p_update
->p_pkey
);
158 * Empty the release struct
160 * \param p_update update_t* pointer
163 static void EmptyRelease( update_t
*p_update
)
165 p_update
->release
.i_major
= 0;
166 p_update
->release
.i_minor
= 0;
167 p_update
->release
.i_revision
= 0;
169 FREENULL( p_update
->release
.psz_url
);
170 FREENULL( p_update
->release
.psz_desc
);
174 * Get the update file and parse it
175 * p_update has to be locked when calling this function
177 * \param p_update pointer to update struct
178 * \return true if the update is valid and authenticated
180 static bool GetUpdateFile( update_t
*p_update
)
182 stream_t
*p_stream
= NULL
;
183 char *psz_version_line
= NULL
;
184 char *psz_update_data
= NULL
;
186 p_stream
= stream_UrlNew( p_update
->p_libvlc
, UPDATE_VLC_STATUS_URL
);
189 msg_Err( p_update
->p_libvlc
, "Failed to open %s for reading",
190 UPDATE_VLC_STATUS_URL
);
194 const int64_t i_read
= stream_Size( p_stream
);
195 psz_update_data
= malloc( i_read
+ 1 ); /* terminating '\0' */
196 if( !psz_update_data
)
199 if( stream_Read( p_stream
, psz_update_data
, i_read
) != i_read
)
201 msg_Err( p_update
->p_libvlc
, "Couldn't download update file %s",
202 UPDATE_VLC_STATUS_URL
);
205 psz_update_data
[i_read
] = '\0';
207 stream_Delete( p_stream
);
210 /* first line : version number */
211 char *psz_update_data_parser
= psz_update_data
;
212 size_t i_len
= strcspn( psz_update_data
, "\r\n" );
213 psz_update_data_parser
+= i_len
;
214 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
215 psz_update_data_parser
++;
217 if( !(psz_version_line
= malloc( i_len
+ 1)) )
219 strncpy( psz_version_line
, psz_update_data
, i_len
);
220 psz_version_line
[i_len
] = '\0';
222 p_update
->release
.i_extra
= 0;
223 int ret
= sscanf( psz_version_line
, "%i.%i.%i.%i",
224 &p_update
->release
.i_major
, &p_update
->release
.i_minor
,
225 &p_update
->release
.i_revision
, &p_update
->release
.i_extra
);
226 if( ret
!= 3 && ret
!= 4 )
228 msg_Err( p_update
->p_libvlc
, "Update version false formated" );
232 /* second line : URL */
233 i_len
= strcspn( psz_update_data_parser
, "\r\n" );
236 msg_Err( p_update
->p_libvlc
, "Update file %s is corrupted: URL missing",
237 UPDATE_VLC_STATUS_URL
);
242 if( !(p_update
->release
.psz_url
= malloc( i_len
+ 1)) )
244 strncpy( p_update
->release
.psz_url
, psz_update_data_parser
, i_len
);
245 p_update
->release
.psz_url
[i_len
] = '\0';
247 psz_update_data_parser
+= i_len
;
248 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
249 psz_update_data_parser
++;
251 /* Remaining data : description */
252 i_len
= strlen( psz_update_data_parser
);
255 msg_Err( p_update
->p_libvlc
,
256 "Update file %s is corrupted: description missing",
257 UPDATE_VLC_STATUS_URL
);
261 if( !(p_update
->release
.psz_desc
= malloc( i_len
+ 1)) )
263 strncpy( p_update
->release
.psz_desc
, psz_update_data_parser
, i_len
);
264 p_update
->release
.psz_desc
[i_len
] = '\0';
266 /* Now that we know the status is valid, we must download its signature
267 * to authenticate it */
268 signature_packet_t sign
;
269 if( download_signature( VLC_OBJECT( p_update
->p_libvlc
), &sign
,
270 UPDATE_VLC_STATUS_URL
) != VLC_SUCCESS
)
272 msg_Err( p_update
->p_libvlc
, "Couldn't download signature of status file" );
276 if( sign
.type
!= BINARY_SIGNATURE
&& sign
.type
!= TEXT_SIGNATURE
)
278 msg_Err( p_update
->p_libvlc
, "Invalid signature type" );
282 p_update
->p_pkey
= (public_key_t
*)malloc( sizeof( public_key_t
) );
283 if( !p_update
->p_pkey
)
286 if( parse_public_key( videolan_public_key
, sizeof( videolan_public_key
),
287 p_update
->p_pkey
, NULL
) != VLC_SUCCESS
)
289 msg_Err( p_update
->p_libvlc
, "Couldn't parse embedded public key, something went really wrong..." );
290 FREENULL( p_update
->p_pkey
);
294 memcpy( p_update
->p_pkey
->longid
, videolan_public_key_longid
, 8 );
296 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) != 0 )
298 msg_Dbg( p_update
->p_libvlc
, "Need to download the GPG key" );
299 public_key_t
*p_new_pkey
= download_key(
300 VLC_OBJECT(p_update
->p_libvlc
),
301 sign
.issuer_longid
, videolan_public_key_longid
);
304 msg_Err( p_update
->p_libvlc
, "Couldn't download GPG key" );
305 FREENULL( p_update
->p_pkey
);
309 uint8_t *p_hash
= hash_sha1_from_public_key( p_new_pkey
);
312 msg_Err( p_update
->p_libvlc
, "Failed to hash signature" );
314 FREENULL( p_update
->p_pkey
);
318 if( verify_signature( p_new_pkey
->sig
.r
, p_new_pkey
->sig
.s
,
319 &p_update
->p_pkey
->key
, p_hash
) == VLC_SUCCESS
)
322 msg_Info( p_update
->p_libvlc
, "Key authenticated" );
323 free( p_update
->p_pkey
);
324 p_update
->p_pkey
= p_new_pkey
;
329 msg_Err( p_update
->p_libvlc
, "Key signature invalid !" );
334 uint8_t *p_hash
= hash_sha1_from_text( psz_update_data
, &sign
);
337 msg_Warn( p_update
->p_libvlc
, "Can't compute SHA1 hash for status file" );
341 else if( p_hash
[0] != sign
.hash_verification
[0] ||
342 p_hash
[1] != sign
.hash_verification
[1] )
344 msg_Warn( p_update
->p_libvlc
, "Bad SHA1 hash for status file" );
348 else if( verify_signature( sign
.r
, sign
.s
, &p_update
->p_pkey
->key
, p_hash
)
351 msg_Err( p_update
->p_libvlc
, "BAD SIGNATURE for status file" );
357 msg_Info( p_update
->p_libvlc
, "Status file authenticated" );
363 stream_Delete( p_stream
);
364 free( psz_version_line
);
365 free( psz_update_data
);
369 static void* update_CheckReal( void * );
374 * \param p_update pointer to update struct
375 * \param pf_callback pointer to a function to call when the update_check is finished
376 * \param p_data pointer to some datas to give to the callback
379 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ), void *p_data
)
383 // If the object already exist, destroy it
384 if( p_update
->p_check
)
386 vlc_join( p_update
->p_check
->thread
, NULL
);
387 free( p_update
->p_check
);
390 update_check_thread_t
*p_uct
= calloc( 1, sizeof( *p_uct
) );
393 p_uct
->p_update
= p_update
;
394 p_update
->p_check
= p_uct
;
395 p_uct
->pf_callback
= pf_callback
;
396 p_uct
->p_data
= p_data
;
398 vlc_clone( &p_uct
->thread
, update_CheckReal
, p_uct
, VLC_THREAD_PRIORITY_LOW
);
401 void* update_CheckReal( void *obj
)
403 update_check_thread_t
*p_uct
= (update_check_thread_t
*)obj
;
407 canc
= vlc_savecancel ();
408 vlc_mutex_lock( &p_uct
->p_update
->lock
);
410 EmptyRelease( p_uct
->p_update
);
411 b_ret
= GetUpdateFile( p_uct
->p_update
);
412 vlc_mutex_unlock( &p_uct
->p_update
->lock
);
414 if( p_uct
->pf_callback
)
415 (p_uct
->pf_callback
)( p_uct
->p_data
, b_ret
);
417 vlc_restorecancel (canc
);
421 bool update_NeedUpgrade( update_t
*p_update
)
425 static const int current
[4] = {
426 PACKAGE_VERSION_MAJOR
,
427 PACKAGE_VERSION_MINOR
,
428 PACKAGE_VERSION_REVISION
,
429 PACKAGE_VERSION_EXTRA
431 const int latest
[4] = {
432 p_update
->release
.i_major
,
433 p_update
->release
.i_minor
,
434 p_update
->release
.i_revision
,
435 p_update
->release
.i_extra
438 for (unsigned i
= 0; i
< sizeof latest
/ sizeof *latest
; i
++) {
439 /* there is a new version available */
440 if (latest
[i
] > current
[i
])
443 /* current version is more recent than the latest version ?! */
444 if (latest
[i
] < current
[i
])
448 /* current version is not a release, it's a -git or -rc version */
449 if (*PACKAGE_VERSION_DEV
)
452 /* current version is latest version */
457 * Convert a long int size in bytes to a string
459 * \param l_size the size in bytes
460 * \return the size as a string
462 static char *size_str( long int l_size
)
464 char *psz_tmp
= NULL
;
467 i_retval
= asprintf( &psz_tmp
, _("%.1f GiB"), (float)l_size
/(1<<30) );
468 else if( l_size
>> 20 )
469 i_retval
= asprintf( &psz_tmp
, _("%.1f MiB"), (float)l_size
/(1<<20) );
470 else if( l_size
>> 10 )
471 i_retval
= asprintf( &psz_tmp
, _("%.1f KiB"), (float)l_size
/(1<<10) );
473 i_retval
= asprintf( &psz_tmp
, _("%ld B"), l_size
);
475 return i_retval
== -1 ? NULL
: psz_tmp
;
478 static void* update_DownloadReal( void * );
481 * Download the file given in the update_t
483 * \param p_update structure
484 * \param dir to store the download file
487 void update_Download( update_t
*p_update
, const char *psz_destdir
)
491 // If the object already exist, destroy it
492 if( p_update
->p_download
)
494 vlc_object_kill( p_update
->p_download
);
495 vlc_join( p_update
->p_download
->thread
, NULL
);
496 vlc_object_release( p_update
->p_download
);
499 update_download_thread_t
*p_udt
=
500 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_udt
),
505 p_udt
->p_update
= p_update
;
506 p_update
->p_download
= p_udt
;
507 p_udt
->psz_destdir
= psz_destdir
? strdup( psz_destdir
) : NULL
;
509 vlc_clone( &p_udt
->thread
, update_DownloadReal
, p_udt
, VLC_THREAD_PRIORITY_LOW
);
512 static void* update_DownloadReal( void *obj
)
514 update_download_thread_t
*p_udt
= (update_download_thread_t
*)obj
;
515 dialog_progress_bar_t
*p_progress
= NULL
;
517 long int l_downloaded
= 0;
520 char *psz_downloaded
= NULL
;
521 char *psz_size
= NULL
;
522 char *psz_destfile
= NULL
;
523 char *psz_tmpdestfile
= NULL
;
526 stream_t
*p_stream
= NULL
;
527 void* p_buffer
= NULL
;
531 update_t
*p_update
= p_udt
->p_update
;
532 char *psz_destdir
= p_udt
->psz_destdir
;
534 msg_Dbg( p_udt
, "Opening Stream '%s'", p_update
->release
.psz_url
);
535 canc
= vlc_savecancel ();
537 /* Open the stream */
538 p_stream
= stream_UrlNew( p_udt
, p_update
->release
.psz_url
);
541 msg_Err( p_udt
, "Failed to open %s for reading", p_update
->release
.psz_url
);
545 /* Get the stream size */
546 l_size
= stream_Size( p_stream
);
548 /* Get the file name and open it*/
549 psz_tmpdestfile
= strrchr( p_update
->release
.psz_url
, '/' );
550 if( !psz_tmpdestfile
)
552 msg_Err( p_udt
, "The URL %s is badly formated",
553 p_update
->release
.psz_url
);
557 if( asprintf( &psz_destfile
, "%s%s", psz_destdir
, psz_tmpdestfile
) == -1 )
560 p_file
= vlc_fopen( psz_destfile
, "w" );
563 msg_Err( p_udt
, "Failed to open %s for writing", psz_destfile
);
564 dialog_FatalWait( p_udt
, _("Saving file failed"),
565 _("Failed to open \"%s\" for writing"),
570 /* Create a buffer and fill it with the downloaded file */
571 p_buffer
= (void *)malloc( 1 << 10 );
574 msg_Err( p_udt
, "Can't malloc (1 << 10) bytes! download cancelled." );
578 msg_Dbg( p_udt
, "Downloading Stream '%s'", p_update
->release
.psz_url
);
580 psz_size
= size_str( l_size
);
581 if( asprintf( &psz_status
, _("%s\nDownloading... %s/%s %.1f%% done"),
582 p_update
->release
.psz_url
, "0.0", psz_size
, 0.0 ) != -1 )
584 p_progress
= dialog_ProgressCreate( p_udt
, _( "Downloading ..."),
585 psz_status
, _("Cancel") );
589 while( vlc_object_alive( p_udt
) &&
590 ( i_read
= stream_Read( p_stream
, p_buffer
, 1 << 10 ) ) &&
591 !dialog_ProgressCancelled( p_progress
) )
593 if( fwrite( p_buffer
, i_read
, 1, p_file
) < 1 )
595 msg_Err( p_udt
, "Failed to write into %s", psz_destfile
);
599 l_downloaded
+= i_read
;
600 psz_downloaded
= size_str( l_downloaded
);
601 f_progress
= (float)l_downloaded
/(float)l_size
;
603 if( asprintf( &psz_status
, _( "%s\nDownloading... %s/%s - %.1f%% done" ),
604 p_update
->release
.psz_url
, psz_downloaded
, psz_size
,
605 f_progress
*100 ) != -1 )
607 dialog_ProgressSet( p_progress
, psz_status
, f_progress
);
610 free( psz_downloaded
);
613 /* Finish the progress bar or delete the file if the user had canceled */
617 if( vlc_object_alive( p_udt
) &&
618 !dialog_ProgressCancelled( p_progress
) )
620 dialog_ProgressDestroy( p_progress
);
625 vlc_unlink( psz_destfile
);
629 signature_packet_t sign
;
630 if( download_signature( VLC_OBJECT( p_udt
), &sign
,
631 p_update
->release
.psz_url
) != VLC_SUCCESS
)
633 vlc_unlink( psz_destfile
);
635 dialog_FatalWait( p_udt
, _("File could not be verified"),
636 _("It was not possible to download a cryptographic signature for "
637 "the downloaded file \"%s\". Thus, it was deleted."),
639 msg_Err( p_udt
, "Couldn't download signature of downloaded file" );
643 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) )
645 vlc_unlink( psz_destfile
);
646 msg_Err( p_udt
, "Invalid signature issuer" );
647 dialog_FatalWait( p_udt
, _("Invalid signature"),
648 _("The cryptographic signature for the downloaded file \"%s\" was "
649 "invalid and could not be used to securely verify it. Thus, the "
650 "file was deleted."),
655 if( sign
.type
!= BINARY_SIGNATURE
)
657 vlc_unlink( psz_destfile
);
658 msg_Err( p_udt
, "Invalid signature type" );
659 dialog_FatalWait( p_udt
, _("Invalid signature"),
660 _("The cryptographic signature for the downloaded file \"%s\" was "
661 "invalid and could not be used to securely verify it. Thus, the "
662 "file was deleted."),
667 uint8_t *p_hash
= hash_sha1_from_file( psz_destfile
, &sign
);
670 msg_Err( p_udt
, "Unable to hash %s", psz_destfile
);
671 vlc_unlink( psz_destfile
);
672 dialog_FatalWait( p_udt
, _("File not verifiable"),
673 _("It was not possible to securely verify the downloaded file"
674 " \"%s\". Thus, it was deleted."),
680 if( p_hash
[0] != sign
.hash_verification
[0] ||
681 p_hash
[1] != sign
.hash_verification
[1] )
683 vlc_unlink( psz_destfile
);
684 dialog_FatalWait( p_udt
, _("File corrupted"),
685 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
687 msg_Err( p_udt
, "Bad SHA1 hash for %s", psz_destfile
);
692 if( verify_signature( sign
.r
, sign
.s
, &p_update
->p_pkey
->key
, p_hash
)
695 vlc_unlink( psz_destfile
);
696 dialog_FatalWait( p_udt
, _("File corrupted"),
697 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
699 msg_Err( p_udt
, "BAD SIGNATURE for %s", psz_destfile
);
704 msg_Info( p_udt
, "%s authenticated", psz_destfile
);
708 int answer
= dialog_Question( p_udt
, _("Update VLC media player"),
709 _("The new version was successfully downloaded. Do you want to close VLC and install it now?"),
710 _("Install"), _("Cancel"), NULL
);
714 wchar_t psz_wdestfile
[MAX_PATH
];
715 MultiByteToWideChar( CP_UTF8
, 0, psz_destfile
, -1, psz_wdestfile
, MAX_PATH
);
716 answer
= (int)ShellExecuteW( NULL
, L
"open", psz_wdestfile
, NULL
, NULL
, SW_SHOW
);
718 libvlc_Quit(p_udt
->p_libvlc
);
723 dialog_ProgressDestroy( p_progress
);
725 stream_Delete( p_stream
);
729 free( psz_destfile
);
733 vlc_restorecancel( canc
);
737 update_release_t
*update_GetRelease( update_t
*p_update
)
739 return &p_update
->release
;
744 update_t
*update_New( vlc_object_t
*p_this
)
750 void update_Delete( update_t
*p_update
)
755 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ),
758 (void)p_update
; (void)pf_callback
; (void)p_data
;
761 bool update_NeedUpgrade( update_t
*p_update
)
767 void update_Download( update_t
*p_update
, const char *psz_destdir
)
769 (void)p_update
; (void)psz_destdir
;
772 update_release_t
*update_GetRelease( update_t
*p_update
)