1 /*****************************************************************************
2 * update.c: VLC update checking and downloading
3 *****************************************************************************
4 * Copyright © 2005-2008 VLC authors and VideoLAN
6 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
7 * Rémi Duraffort <ivoire at via.ecp.fr>
8 Rafaël Carré <funman@videolanorg>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either release 2 of the License, or
13 * (at your option) any later release.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
27 * This file contains functions related to VLC update management
30 /*****************************************************************************
32 *****************************************************************************/
38 #include <vlc_common.h>
39 #include <vlc_update.h>
43 #include <vlc_pgpkey.h>
44 #include <vlc_stream.h>
45 #include <vlc_strings.h>
47 #include <vlc_dialog.h>
48 #include <vlc_interface.h>
51 #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.Z.E" 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 number
69 * * IE "1.2.0" or "1.1.10.1"
70 * Second line is a url of the binary for this last version
71 * Remaining text is a required description of the update
75 # define UPDATE_OS_SUFFIX "-win-x64"
76 #elif defined( _WIN32 )
77 # define UPDATE_OS_SUFFIX "-win-x86"
79 # define UPDATE_OS_SUFFIX ""
83 # define UPDATE_VLC_STATUS_URL "http://update-test.videolan.org/vlc/status-win-x86"
85 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status" UPDATE_OS_SUFFIX
88 #define dialog_FatalWait( p_obj, psz_title, psz_fmt, ... ) \
89 vlc_dialog_wait_question( p_obj, VLC_DIALOG_QUESTION_CRITICAL, "OK", NULL, \
90 NULL, psz_title, psz_fmt, ##__VA_ARGS__ );
92 /*****************************************************************************
94 *****************************************************************************/
98 * Create a new update VLC struct
100 * \param p_this the calling vlc_object
101 * \return pointer to new update_t or NULL
103 update_t
*update_New( vlc_object_t
*p_this
)
108 p_update
= (update_t
*)malloc( sizeof( update_t
) );
109 if( !p_update
) return NULL
;
111 vlc_mutex_init( &p_update
->lock
);
113 p_update
->p_libvlc
= vlc_object_instance(p_this
);
115 p_update
->release
.psz_url
= NULL
;
116 p_update
->release
.psz_desc
= NULL
;
118 p_update
->p_download
= NULL
;
119 p_update
->p_check
= NULL
;
121 p_update
->p_pkey
= NULL
;
128 * Delete an update_t struct
130 * \param p_update update_t* pointer
133 void update_Delete( update_t
*p_update
)
137 if( p_update
->p_check
)
139 vlc_join( p_update
->p_check
->thread
, NULL
);
140 free( p_update
->p_check
);
143 if( p_update
->p_download
)
145 atomic_store( &p_update
->p_download
->aborted
, true );
146 vlc_join( p_update
->p_download
->thread
, NULL
);
147 vlc_object_delete(p_update
->p_download
);
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
= vlc_stream_NewURL( 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
);
195 if( vlc_stream_GetSize( p_stream
, &i_read
) || i_read
>= UINT16_MAX
)
197 msg_Err(p_update
->p_libvlc
, "Status file too large");
201 psz_update_data
= malloc( i_read
+ 1 ); /* terminating '\0' */
202 if( !psz_update_data
)
205 if( vlc_stream_Read( p_stream
, psz_update_data
,
206 i_read
) != (ssize_t
)i_read
)
208 msg_Err( p_update
->p_libvlc
, "Couldn't download update file %s",
209 UPDATE_VLC_STATUS_URL
);
212 psz_update_data
[i_read
] = '\0';
214 vlc_stream_Delete( p_stream
);
217 /* first line : version number */
218 char *psz_update_data_parser
= psz_update_data
;
219 size_t i_len
= strcspn( psz_update_data
, "\r\n" );
220 psz_update_data_parser
+= i_len
;
221 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
222 psz_update_data_parser
++;
224 if( !(psz_version_line
= malloc( i_len
+ 1)) )
226 strncpy( psz_version_line
, psz_update_data
, i_len
);
227 psz_version_line
[i_len
] = '\0';
229 p_update
->release
.i_extra
= 0;
230 int ret
= sscanf( psz_version_line
, "%i.%i.%i.%i",
231 &p_update
->release
.i_major
, &p_update
->release
.i_minor
,
232 &p_update
->release
.i_revision
, &p_update
->release
.i_extra
);
233 if( ret
!= 3 && ret
!= 4 )
235 msg_Err( p_update
->p_libvlc
, "Update version false formatted" );
239 /* second line : URL */
240 i_len
= strcspn( psz_update_data_parser
, "\r\n" );
243 msg_Err( p_update
->p_libvlc
, "Update file %s is corrupted: URL missing",
244 UPDATE_VLC_STATUS_URL
);
249 if( !(p_update
->release
.psz_url
= malloc( i_len
+ 1)) )
251 strncpy( p_update
->release
.psz_url
, psz_update_data_parser
, i_len
);
252 p_update
->release
.psz_url
[i_len
] = '\0';
254 psz_update_data_parser
+= i_len
;
255 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
256 psz_update_data_parser
++;
258 /* Remaining data : description */
259 i_len
= strlen( psz_update_data_parser
);
262 msg_Err( p_update
->p_libvlc
,
263 "Update file %s is corrupted: description missing",
264 UPDATE_VLC_STATUS_URL
);
268 if( !(p_update
->release
.psz_desc
= malloc( i_len
+ 1)) )
270 strncpy( p_update
->release
.psz_desc
, psz_update_data_parser
, i_len
);
271 p_update
->release
.psz_desc
[i_len
] = '\0';
273 /* Now that we know the status is valid, we must download its signature
274 * to authenticate it */
275 signature_packet_t sign
;
276 if( download_signature( VLC_OBJECT( p_update
->p_libvlc
), &sign
,
277 UPDATE_VLC_STATUS_URL
) != VLC_SUCCESS
)
279 msg_Err( p_update
->p_libvlc
, "Couldn't download signature of status file" );
283 if( sign
.type
!= BINARY_SIGNATURE
&& sign
.type
!= TEXT_SIGNATURE
)
285 msg_Err( p_update
->p_libvlc
, "Invalid signature type" );
289 p_update
->p_pkey
= (public_key_t
*)malloc( sizeof( public_key_t
) );
290 if( !p_update
->p_pkey
)
293 if( parse_public_key( videolan_public_key
, sizeof( videolan_public_key
),
294 p_update
->p_pkey
, NULL
) != VLC_SUCCESS
)
296 msg_Err( p_update
->p_libvlc
, "Couldn't parse embedded public key, something went really wrong..." );
297 FREENULL( p_update
->p_pkey
);
301 memcpy( p_update
->p_pkey
->longid
, videolan_public_key_longid
, 8 );
303 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) != 0 )
305 msg_Dbg( p_update
->p_libvlc
, "Need to download the GPG key" );
306 public_key_t
*p_new_pkey
= download_key(
307 VLC_OBJECT(p_update
->p_libvlc
),
308 sign
.issuer_longid
, videolan_public_key_longid
);
311 msg_Err( p_update
->p_libvlc
, "Couldn't download GPG key" );
312 FREENULL( p_update
->p_pkey
);
316 uint8_t *p_hash
= hash_from_public_key( p_new_pkey
);
319 msg_Err( p_update
->p_libvlc
, "Failed to hash signature" );
321 FREENULL( p_update
->p_pkey
);
325 if( verify_signature( &p_new_pkey
->sig
,
326 &p_update
->p_pkey
->key
, p_hash
) == VLC_SUCCESS
)
329 msg_Info( p_update
->p_libvlc
, "Key authenticated" );
330 free( p_update
->p_pkey
);
331 p_update
->p_pkey
= p_new_pkey
;
336 msg_Err( p_update
->p_libvlc
, "Key signature invalid !" );
341 uint8_t *p_hash
= hash_from_text( psz_update_data
, &sign
);
344 msg_Warn( p_update
->p_libvlc
, "Can't compute hash for status file" );
348 else if( p_hash
[0] != sign
.hash_verification
[0] ||
349 p_hash
[1] != sign
.hash_verification
[1] )
351 msg_Warn( p_update
->p_libvlc
, "Bad hash for status file" );
356 else if( verify_signature( &sign
, &p_update
->p_pkey
->key
, p_hash
)
359 msg_Err( p_update
->p_libvlc
, "BAD SIGNATURE for status file" );
366 msg_Info( p_update
->p_libvlc
, "Status file authenticated" );
368 free( psz_version_line
);
369 free( psz_update_data
);
375 vlc_stream_Delete( p_stream
);
376 free( psz_version_line
);
377 free( psz_update_data
);
381 static void* update_CheckReal( void * );
386 * \param p_update pointer to update struct
387 * \param pf_callback pointer to a function to call when the update_check is finished
388 * \param p_data pointer to some datas to give to the callback
391 void update_Check( update_t
*p_update
, void (*pf_callback
)( void*, bool ), void *p_data
)
395 // If the object already exist, destroy it
396 if( p_update
->p_check
)
398 vlc_join( p_update
->p_check
->thread
, NULL
);
399 free( p_update
->p_check
);
402 update_check_thread_t
*p_uct
= calloc( 1, sizeof( *p_uct
) );
405 p_uct
->p_update
= p_update
;
406 p_update
->p_check
= p_uct
;
407 p_uct
->pf_callback
= pf_callback
;
408 p_uct
->p_data
= p_data
;
410 vlc_clone( &p_uct
->thread
, update_CheckReal
, p_uct
, VLC_THREAD_PRIORITY_LOW
);
413 void* update_CheckReal( void *obj
)
415 update_check_thread_t
*p_uct
= (update_check_thread_t
*)obj
;
419 canc
= vlc_savecancel ();
420 vlc_mutex_lock( &p_uct
->p_update
->lock
);
422 EmptyRelease( p_uct
->p_update
);
423 b_ret
= GetUpdateFile( p_uct
->p_update
);
424 vlc_mutex_unlock( &p_uct
->p_update
->lock
);
426 if( p_uct
->pf_callback
)
427 (p_uct
->pf_callback
)( p_uct
->p_data
, b_ret
);
429 vlc_restorecancel (canc
);
433 bool update_NeedUpgrade( update_t
*p_update
)
437 static const int current
[4] = {
438 PACKAGE_VERSION_MAJOR
,
439 PACKAGE_VERSION_MINOR
,
440 PACKAGE_VERSION_REVISION
,
441 PACKAGE_VERSION_EXTRA
443 const int latest
[4] = {
444 p_update
->release
.i_major
,
445 p_update
->release
.i_minor
,
446 p_update
->release
.i_revision
,
447 p_update
->release
.i_extra
450 for (unsigned i
= 0; i
< ARRAY_SIZE( latest
); i
++) {
451 /* there is a new version available */
452 if (latest
[i
] > current
[i
])
455 /* current version is more recent than the latest version ?! */
456 if (latest
[i
] < current
[i
])
460 /* current version is not a release, it's a -git or -rc version */
461 if (*PACKAGE_VERSION_DEV
)
464 /* current version is latest version */
469 * Convert a long int size in bytes to a string
471 * \param l_size the size in bytes
472 * \return the size as a string
474 static char *size_str( uint64_t l_size
)
476 char *psz_tmp
= NULL
;
479 i_retval
= asprintf( &psz_tmp
, _("%.1f GiB"), (float)l_size
/(1<<30) );
480 else if( l_size
>> 20 )
481 i_retval
= asprintf( &psz_tmp
, _("%.1f MiB"), (float)l_size
/(1<<20) );
482 else if( l_size
>> 10 )
483 i_retval
= asprintf( &psz_tmp
, _("%.1f KiB"), (float)l_size
/(1<<10) );
485 i_retval
= asprintf( &psz_tmp
, _("%"PRIu64
" B"), l_size
);
487 return i_retval
== -1 ? NULL
: psz_tmp
;
490 static void* update_DownloadReal( void * );
493 * Download the file given in the update_t
495 * \param p_update structure
496 * \param dir to store the download file
499 void update_Download( update_t
*p_update
, const char *psz_destdir
)
503 // If the object already exist, destroy it
504 if( p_update
->p_download
)
506 atomic_store( &p_update
->p_download
->aborted
, true );
507 vlc_join( p_update
->p_download
->thread
, NULL
);
508 vlc_object_delete(p_update
->p_download
);
511 update_download_thread_t
*p_udt
=
512 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_udt
),
517 p_udt
->p_update
= p_update
;
518 p_update
->p_download
= p_udt
;
519 p_udt
->psz_destdir
= psz_destdir
? strdup( psz_destdir
) : NULL
;
521 atomic_store(&p_udt
->aborted
, false);
522 vlc_clone( &p_udt
->thread
, update_DownloadReal
, p_udt
, VLC_THREAD_PRIORITY_LOW
);
525 static void* update_DownloadReal( void *obj
)
527 update_download_thread_t
*p_udt
= (update_download_thread_t
*)obj
;
529 uint64_t l_downloaded
= 0;
531 char *psz_downloaded
= NULL
;
532 char *psz_size
= NULL
;
533 char *psz_destfile
= NULL
;
534 char *psz_tmpdestfile
= NULL
;
537 stream_t
*p_stream
= NULL
;
538 void* p_buffer
= NULL
;
542 vlc_dialog_id
*p_dialog_id
= NULL
;
543 update_t
*p_update
= p_udt
->p_update
;
544 char *psz_destdir
= p_udt
->psz_destdir
;
546 msg_Dbg( p_udt
, "Opening Stream '%s'", p_update
->release
.psz_url
);
547 canc
= vlc_savecancel ();
549 /* Open the stream */
550 p_stream
= vlc_stream_NewURL( p_udt
, p_update
->release
.psz_url
);
553 msg_Err( p_udt
, "Failed to open %s for reading", p_update
->release
.psz_url
);
557 /* Get the stream size */
558 if( vlc_stream_GetSize( p_stream
, &l_size
) || l_size
== 0 )
561 /* Get the file name and open it*/
562 psz_tmpdestfile
= strrchr( p_update
->release
.psz_url
, '/' );
563 if( !psz_tmpdestfile
)
565 msg_Err( p_udt
, "The URL %s is badly formatted",
566 p_update
->release
.psz_url
);
570 if( asprintf( &psz_destfile
, "%s%s", psz_destdir
, psz_tmpdestfile
) == -1 )
573 p_file
= vlc_fopen( psz_destfile
, "w" );
576 msg_Err( p_udt
, "Failed to open %s for writing", psz_destfile
);
577 dialog_FatalWait( p_udt
, _("Saving file failed"),
578 _("Failed to open \"%s\" for writing"),
583 /* Create a buffer and fill it with the downloaded file */
584 p_buffer
= (void *)malloc( 1 << 10 );
585 if( unlikely(p_buffer
== NULL
) )
588 msg_Dbg( p_udt
, "Downloading Stream '%s'", p_update
->release
.psz_url
);
590 psz_size
= size_str( l_size
);
593 vlc_dialog_display_progress( p_udt
, false, 0.0, _("Cancel"),
595 _("%s\nDownloading... %s/%s %.1f%% done"),
596 p_update
->release
.psz_url
, "0.0", psz_size
,
599 if( p_dialog_id
== NULL
)
602 while( !atomic_load( &p_udt
->aborted
) &&
603 ( i_read
= vlc_stream_Read( p_stream
, p_buffer
, 1 << 10 ) ) &&
604 !vlc_dialog_is_cancelled( p_udt
, p_dialog_id
) )
606 if( fwrite( p_buffer
, i_read
, 1, p_file
) < 1 )
608 msg_Err( p_udt
, "Failed to write into %s", psz_destfile
);
612 l_downloaded
+= i_read
;
613 psz_downloaded
= size_str( l_downloaded
);
614 f_progress
= (float)l_downloaded
/(float)l_size
;
616 vlc_dialog_update_progress_text( p_udt
, p_dialog_id
, f_progress
,
617 "%s\nDownloading... %s/%s - %.1f%% done",
618 p_update
->release
.psz_url
,
619 psz_downloaded
, psz_size
,
621 free( psz_downloaded
);
624 /* Finish the progress bar or delete the file if the user had canceled */
628 if( !atomic_load( &p_udt
->aborted
) &&
629 !vlc_dialog_is_cancelled( p_udt
, p_dialog_id
) )
631 vlc_dialog_release( p_udt
, p_dialog_id
);
636 vlc_unlink( psz_destfile
);
640 signature_packet_t sign
;
641 if( download_signature( VLC_OBJECT( p_udt
), &sign
,
642 p_update
->release
.psz_url
) != VLC_SUCCESS
)
644 vlc_unlink( psz_destfile
);
646 dialog_FatalWait( p_udt
, _("File could not be verified"),
647 _("It was not possible to download a cryptographic signature for "
648 "the downloaded file \"%s\". Thus, it was deleted."),
650 msg_Err( p_udt
, "Couldn't download signature of downloaded file" );
654 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) )
656 vlc_unlink( psz_destfile
);
657 msg_Err( p_udt
, "Invalid signature issuer" );
658 dialog_FatalWait( p_udt
, _("Invalid signature"),
659 _("The cryptographic signature for the downloaded file \"%s\" was "
660 "invalid and could not be used to securely verify it. Thus, the "
661 "file was deleted."),
666 if( sign
.type
!= BINARY_SIGNATURE
)
668 vlc_unlink( psz_destfile
);
669 msg_Err( p_udt
, "Invalid signature type" );
670 dialog_FatalWait( p_udt
, _("Invalid signature"),
671 _("The cryptographic signature for the downloaded file \"%s\" was "
672 "invalid and could not be used to securely verify it. Thus, the "
673 "file was deleted."),
678 uint8_t *p_hash
= hash_from_file( psz_destfile
, &sign
);
681 msg_Err( p_udt
, "Unable to hash %s", psz_destfile
);
682 vlc_unlink( psz_destfile
);
683 dialog_FatalWait( p_udt
, _("File not verifiable"),
684 _("It was not possible to securely verify the downloaded file"
685 " \"%s\". Thus, it was deleted."),
691 if( p_hash
[0] != sign
.hash_verification
[0] ||
692 p_hash
[1] != sign
.hash_verification
[1] )
694 vlc_unlink( psz_destfile
);
695 dialog_FatalWait( p_udt
, _("File corrupted"),
696 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
698 msg_Err( p_udt
, "Bad hash for %s", psz_destfile
);
703 if( verify_signature( &sign
, &p_update
->p_pkey
->key
, p_hash
)
706 vlc_unlink( psz_destfile
);
707 dialog_FatalWait( p_udt
, _("File corrupted"),
708 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
710 msg_Err( p_udt
, "BAD SIGNATURE for %s", psz_destfile
);
715 msg_Info( p_udt
, "%s authenticated", psz_destfile
);
719 const char *psz_msg
=
720 _("The new version was successfully downloaded. "
721 "Do you want to close VLC and install it now?");
722 int answer
= vlc_dialog_wait_question( p_udt
, VLC_DIALOG_QUESTION_NORMAL
,
723 _("Cancel"), _("Install"), NULL
,
724 _("Update VLC media player"), "%s",
728 #ifndef VLC_WINSTORE_APP
729 wchar_t psz_wdestfile
[MAX_PATH
];
730 MultiByteToWideChar( CP_UTF8
, 0, psz_destfile
, -1, psz_wdestfile
, MAX_PATH
);
731 answer
= (int)ShellExecuteW( NULL
, L
"open", psz_wdestfile
, NULL
, NULL
, SW_SHOW
);
734 libvlc_Quit(vlc_object_instance(p_udt
));
738 if( p_dialog_id
!= NULL
)
739 vlc_dialog_release( p_udt
, p_dialog_id
);
741 vlc_stream_Delete( p_stream
);
745 free( psz_destfile
);
749 vlc_restorecancel( canc
);
753 update_release_t
*update_GetRelease( update_t
*p_update
)
755 return &p_update
->release
;