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>
44 #include <vlc_pgpkey.h>
45 #include <vlc_stream.h>
46 #include <vlc_strings.h>
48 #include <vlc_dialog.h>
49 #include <vlc_interface.h>
52 #include <vlc_gcrypt.h>
57 #include "../libvlc.h"
59 /*****************************************************************************
61 *****************************************************************************/
64 * Here is the format of these "status files" :
65 * First line is the last version: "X.Y.Z.E" where:
66 * * X is the major number
67 * * Y is the minor number
68 * * Z is the revision number
69 * * .E is an OPTIONAL extra number
70 * * IE "1.2.0" or "1.1.10.1"
71 * Second line is a url of the binary for this last version
72 * Remaining text is a required description of the update
76 # define UPDATE_OS_SUFFIX "-win-x64"
77 #elif defined( _WIN32 )
78 # define UPDATE_OS_SUFFIX "-win-x86"
80 # define UPDATE_OS_SUFFIX ""
84 # define UPDATE_VLC_STATUS_URL "http://update-test.videolan.org/vlc/status-win-x86"
86 # define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status" UPDATE_OS_SUFFIX
89 #define dialog_FatalWait( p_obj, psz_title, psz_fmt, ... ) \
90 vlc_dialog_wait_question( p_obj, VLC_DIALOG_QUESTION_CRITICAL, "OK", NULL, \
91 NULL, psz_title, psz_fmt, ##__VA_ARGS__ );
93 /*****************************************************************************
95 *****************************************************************************/
99 * Create a new update VLC struct
101 * \param p_this the calling vlc_object
102 * \return pointer to new update_t or NULL
104 update_t
*update_New( vlc_object_t
*p_this
)
109 p_update
= (update_t
*)malloc( sizeof( update_t
) );
110 if( !p_update
) return NULL
;
112 vlc_mutex_init( &p_update
->lock
);
114 p_update
->p_libvlc
= p_this
->obj
.libvlc
;
116 p_update
->release
.psz_url
= NULL
;
117 p_update
->release
.psz_desc
= NULL
;
119 p_update
->p_download
= NULL
;
120 p_update
->p_check
= NULL
;
122 p_update
->p_pkey
= NULL
;
129 * Delete an update_t struct
131 * \param p_update update_t* pointer
134 void update_Delete( update_t
*p_update
)
138 if( p_update
->p_check
)
140 vlc_join( p_update
->p_check
->thread
, NULL
);
141 free( p_update
->p_check
);
144 if( p_update
->p_download
)
146 atomic_store( &p_update
->p_download
->aborted
, true );
147 vlc_join( p_update
->p_download
->thread
, NULL
);
148 vlc_object_release( p_update
->p_download
);
151 vlc_mutex_destroy( &p_update
->lock
);
153 free( p_update
->release
.psz_url
);
154 free( p_update
->release
.psz_desc
);
155 free( p_update
->p_pkey
);
161 * Empty the release struct
163 * \param p_update update_t* pointer
166 static void EmptyRelease( update_t
*p_update
)
168 p_update
->release
.i_major
= 0;
169 p_update
->release
.i_minor
= 0;
170 p_update
->release
.i_revision
= 0;
172 FREENULL( p_update
->release
.psz_url
);
173 FREENULL( p_update
->release
.psz_desc
);
177 * Get the update file and parse it
178 * p_update has to be locked when calling this function
180 * \param p_update pointer to update struct
181 * \return true if the update is valid and authenticated
183 static bool GetUpdateFile( update_t
*p_update
)
185 stream_t
*p_stream
= NULL
;
186 char *psz_version_line
= NULL
;
187 char *psz_update_data
= NULL
;
189 p_stream
= vlc_stream_NewURL( p_update
->p_libvlc
, UPDATE_VLC_STATUS_URL
);
192 msg_Err( p_update
->p_libvlc
, "Failed to open %s for reading",
193 UPDATE_VLC_STATUS_URL
);
198 if( vlc_stream_GetSize( p_stream
, &i_read
) || i_read
>= UINT16_MAX
)
200 msg_Err(p_update
->p_libvlc
, "Status file too large");
204 psz_update_data
= malloc( i_read
+ 1 ); /* terminating '\0' */
205 if( !psz_update_data
)
208 if( vlc_stream_Read( p_stream
, psz_update_data
,
209 i_read
) != (ssize_t
)i_read
)
211 msg_Err( p_update
->p_libvlc
, "Couldn't download update file %s",
212 UPDATE_VLC_STATUS_URL
);
215 psz_update_data
[i_read
] = '\0';
217 vlc_stream_Delete( p_stream
);
220 /* first line : version number */
221 char *psz_update_data_parser
= psz_update_data
;
222 size_t i_len
= strcspn( psz_update_data
, "\r\n" );
223 psz_update_data_parser
+= i_len
;
224 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
225 psz_update_data_parser
++;
227 if( !(psz_version_line
= malloc( i_len
+ 1)) )
229 strncpy( psz_version_line
, psz_update_data
, i_len
);
230 psz_version_line
[i_len
] = '\0';
232 p_update
->release
.i_extra
= 0;
233 int ret
= sscanf( psz_version_line
, "%i.%i.%i.%i",
234 &p_update
->release
.i_major
, &p_update
->release
.i_minor
,
235 &p_update
->release
.i_revision
, &p_update
->release
.i_extra
);
236 if( ret
!= 3 && ret
!= 4 )
238 msg_Err( p_update
->p_libvlc
, "Update version false formatted" );
242 /* second line : URL */
243 i_len
= strcspn( psz_update_data_parser
, "\r\n" );
246 msg_Err( p_update
->p_libvlc
, "Update file %s is corrupted: URL missing",
247 UPDATE_VLC_STATUS_URL
);
252 if( !(p_update
->release
.psz_url
= malloc( i_len
+ 1)) )
254 strncpy( p_update
->release
.psz_url
, psz_update_data_parser
, i_len
);
255 p_update
->release
.psz_url
[i_len
] = '\0';
257 psz_update_data_parser
+= i_len
;
258 while( *psz_update_data_parser
== '\r' || *psz_update_data_parser
== '\n' )
259 psz_update_data_parser
++;
261 /* Remaining data : description */
262 i_len
= strlen( psz_update_data_parser
);
265 msg_Err( p_update
->p_libvlc
,
266 "Update file %s is corrupted: description missing",
267 UPDATE_VLC_STATUS_URL
);
271 if( !(p_update
->release
.psz_desc
= malloc( i_len
+ 1)) )
273 strncpy( p_update
->release
.psz_desc
, psz_update_data_parser
, i_len
);
274 p_update
->release
.psz_desc
[i_len
] = '\0';
276 /* Now that we know the status is valid, we must download its signature
277 * to authenticate it */
278 signature_packet_t sign
;
279 if( download_signature( VLC_OBJECT( p_update
->p_libvlc
), &sign
,
280 UPDATE_VLC_STATUS_URL
) != VLC_SUCCESS
)
282 msg_Err( p_update
->p_libvlc
, "Couldn't download signature of status file" );
286 if( sign
.type
!= BINARY_SIGNATURE
&& sign
.type
!= TEXT_SIGNATURE
)
288 msg_Err( p_update
->p_libvlc
, "Invalid signature type" );
292 p_update
->p_pkey
= (public_key_t
*)malloc( sizeof( public_key_t
) );
293 if( !p_update
->p_pkey
)
296 if( parse_public_key( videolan_public_key
, sizeof( videolan_public_key
),
297 p_update
->p_pkey
, NULL
) != VLC_SUCCESS
)
299 msg_Err( p_update
->p_libvlc
, "Couldn't parse embedded public key, something went really wrong..." );
300 FREENULL( p_update
->p_pkey
);
304 memcpy( p_update
->p_pkey
->longid
, videolan_public_key_longid
, 8 );
306 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) != 0 )
308 msg_Dbg( p_update
->p_libvlc
, "Need to download the GPG key" );
309 public_key_t
*p_new_pkey
= download_key(
310 VLC_OBJECT(p_update
->p_libvlc
),
311 sign
.issuer_longid
, videolan_public_key_longid
);
314 msg_Err( p_update
->p_libvlc
, "Couldn't download GPG key" );
315 FREENULL( p_update
->p_pkey
);
319 uint8_t *p_hash
= hash_from_public_key( p_new_pkey
);
322 msg_Err( p_update
->p_libvlc
, "Failed to hash signature" );
324 FREENULL( p_update
->p_pkey
);
328 if( verify_signature( &p_new_pkey
->sig
,
329 &p_update
->p_pkey
->key
, p_hash
) == VLC_SUCCESS
)
332 msg_Info( p_update
->p_libvlc
, "Key authenticated" );
333 free( p_update
->p_pkey
);
334 p_update
->p_pkey
= p_new_pkey
;
339 msg_Err( p_update
->p_libvlc
, "Key signature invalid !" );
344 uint8_t *p_hash
= hash_from_text( psz_update_data
, &sign
);
347 msg_Warn( p_update
->p_libvlc
, "Can't compute hash for status file" );
351 else if( p_hash
[0] != sign
.hash_verification
[0] ||
352 p_hash
[1] != sign
.hash_verification
[1] )
354 msg_Warn( p_update
->p_libvlc
, "Bad hash for status file" );
359 else if( verify_signature( &sign
, &p_update
->p_pkey
->key
, p_hash
)
362 msg_Err( p_update
->p_libvlc
, "BAD SIGNATURE for status file" );
369 msg_Info( p_update
->p_libvlc
, "Status file authenticated" );
371 free( psz_version_line
);
372 free( psz_update_data
);
378 vlc_stream_Delete( p_stream
);
379 free( psz_version_line
);
380 free( psz_update_data
);
384 static void* update_CheckReal( void * );
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_join( p_update
->p_check
->thread
, NULL
);
402 free( p_update
->p_check
);
405 update_check_thread_t
*p_uct
= calloc( 1, sizeof( *p_uct
) );
408 p_uct
->p_update
= p_update
;
409 p_update
->p_check
= p_uct
;
410 p_uct
->pf_callback
= pf_callback
;
411 p_uct
->p_data
= p_data
;
413 vlc_clone( &p_uct
->thread
, update_CheckReal
, p_uct
, VLC_THREAD_PRIORITY_LOW
);
416 void* update_CheckReal( void *obj
)
418 update_check_thread_t
*p_uct
= (update_check_thread_t
*)obj
;
422 canc
= vlc_savecancel ();
423 vlc_mutex_lock( &p_uct
->p_update
->lock
);
425 EmptyRelease( p_uct
->p_update
);
426 b_ret
= GetUpdateFile( p_uct
->p_update
);
427 vlc_mutex_unlock( &p_uct
->p_update
->lock
);
429 if( p_uct
->pf_callback
)
430 (p_uct
->pf_callback
)( p_uct
->p_data
, b_ret
);
432 vlc_restorecancel (canc
);
436 bool update_NeedUpgrade( update_t
*p_update
)
440 static const int current
[4] = {
441 PACKAGE_VERSION_MAJOR
,
442 PACKAGE_VERSION_MINOR
,
443 PACKAGE_VERSION_REVISION
,
444 PACKAGE_VERSION_EXTRA
446 const int latest
[4] = {
447 p_update
->release
.i_major
,
448 p_update
->release
.i_minor
,
449 p_update
->release
.i_revision
,
450 p_update
->release
.i_extra
453 for (unsigned i
= 0; i
< sizeof latest
/ sizeof *latest
; i
++) {
454 /* there is a new version available */
455 if (latest
[i
] > current
[i
])
458 /* current version is more recent than the latest version ?! */
459 if (latest
[i
] < current
[i
])
463 /* current version is not a release, it's a -git or -rc version */
464 if (*PACKAGE_VERSION_DEV
)
467 /* current version is latest version */
472 * Convert a long int size in bytes to a string
474 * \param l_size the size in bytes
475 * \return the size as a string
477 static char *size_str( uint64_t l_size
)
479 char *psz_tmp
= NULL
;
482 i_retval
= asprintf( &psz_tmp
, _("%.1f GiB"), (float)l_size
/(1<<30) );
483 else if( l_size
>> 20 )
484 i_retval
= asprintf( &psz_tmp
, _("%.1f MiB"), (float)l_size
/(1<<20) );
485 else if( l_size
>> 10 )
486 i_retval
= asprintf( &psz_tmp
, _("%.1f KiB"), (float)l_size
/(1<<10) );
488 i_retval
= asprintf( &psz_tmp
, _("%"PRIu64
" B"), l_size
);
490 return i_retval
== -1 ? NULL
: psz_tmp
;
493 static void* update_DownloadReal( void * );
496 * Download the file given in the update_t
498 * \param p_update structure
499 * \param dir to store the download file
502 void update_Download( update_t
*p_update
, const char *psz_destdir
)
506 // If the object already exist, destroy it
507 if( p_update
->p_download
)
509 atomic_store( &p_update
->p_download
->aborted
, true );
510 vlc_join( p_update
->p_download
->thread
, NULL
);
511 vlc_object_release( p_update
->p_download
);
514 update_download_thread_t
*p_udt
=
515 vlc_custom_create( p_update
->p_libvlc
, sizeof( *p_udt
),
520 p_udt
->p_update
= p_update
;
521 p_update
->p_download
= p_udt
;
522 p_udt
->psz_destdir
= psz_destdir
? strdup( psz_destdir
) : NULL
;
524 atomic_store(&p_udt
->aborted
, false);
525 vlc_clone( &p_udt
->thread
, update_DownloadReal
, p_udt
, VLC_THREAD_PRIORITY_LOW
);
528 static void* update_DownloadReal( void *obj
)
530 update_download_thread_t
*p_udt
= (update_download_thread_t
*)obj
;
532 uint64_t l_downloaded
= 0;
534 char *psz_downloaded
= NULL
;
535 char *psz_size
= NULL
;
536 char *psz_destfile
= NULL
;
537 char *psz_tmpdestfile
= NULL
;
540 stream_t
*p_stream
= NULL
;
541 void* p_buffer
= NULL
;
545 vlc_dialog_id
*p_dialog_id
= NULL
;
546 update_t
*p_update
= p_udt
->p_update
;
547 char *psz_destdir
= p_udt
->psz_destdir
;
549 msg_Dbg( p_udt
, "Opening Stream '%s'", p_update
->release
.psz_url
);
550 canc
= vlc_savecancel ();
552 /* Open the stream */
553 p_stream
= vlc_stream_NewURL( p_udt
, p_update
->release
.psz_url
);
556 msg_Err( p_udt
, "Failed to open %s for reading", p_update
->release
.psz_url
);
560 /* Get the stream size */
561 if( vlc_stream_GetSize( p_stream
, &l_size
) || l_size
== 0 )
564 /* Get the file name and open it*/
565 psz_tmpdestfile
= strrchr( p_update
->release
.psz_url
, '/' );
566 if( !psz_tmpdestfile
)
568 msg_Err( p_udt
, "The URL %s is badly formatted",
569 p_update
->release
.psz_url
);
573 if( asprintf( &psz_destfile
, "%s%s", psz_destdir
, psz_tmpdestfile
) == -1 )
576 p_file
= vlc_fopen( psz_destfile
, "w" );
579 msg_Err( p_udt
, "Failed to open %s for writing", psz_destfile
);
580 dialog_FatalWait( p_udt
, _("Saving file failed"),
581 _("Failed to open \"%s\" for writing"),
586 /* Create a buffer and fill it with the downloaded file */
587 p_buffer
= (void *)malloc( 1 << 10 );
588 if( unlikely(p_buffer
== NULL
) )
591 msg_Dbg( p_udt
, "Downloading Stream '%s'", p_update
->release
.psz_url
);
593 psz_size
= size_str( l_size
);
596 vlc_dialog_display_progress( p_udt
, false, 0.0, _("Cancel"),
598 _("%s\nDownloading... %s/%s %.1f%% done"),
599 p_update
->release
.psz_url
, "0.0", psz_size
,
602 if( p_dialog_id
== NULL
)
605 while( !atomic_load( &p_udt
->aborted
) &&
606 ( i_read
= vlc_stream_Read( p_stream
, p_buffer
, 1 << 10 ) ) &&
607 !vlc_dialog_is_cancelled( p_udt
, p_dialog_id
) )
609 if( fwrite( p_buffer
, i_read
, 1, p_file
) < 1 )
611 msg_Err( p_udt
, "Failed to write into %s", psz_destfile
);
615 l_downloaded
+= i_read
;
616 psz_downloaded
= size_str( l_downloaded
);
617 f_progress
= (float)l_downloaded
/(float)l_size
;
619 vlc_dialog_update_progress_text( p_udt
, p_dialog_id
, f_progress
,
620 "%s\nDownloading... %s/%s - %.1f%% done",
621 p_update
->release
.psz_url
,
622 psz_downloaded
, psz_size
,
624 free( psz_downloaded
);
627 /* Finish the progress bar or delete the file if the user had canceled */
631 if( !atomic_load( &p_udt
->aborted
) &&
632 !vlc_dialog_is_cancelled( p_udt
, p_dialog_id
) )
634 vlc_dialog_release( p_udt
, p_dialog_id
);
639 vlc_unlink( psz_destfile
);
643 signature_packet_t sign
;
644 if( download_signature( VLC_OBJECT( p_udt
), &sign
,
645 p_update
->release
.psz_url
) != VLC_SUCCESS
)
647 vlc_unlink( psz_destfile
);
649 dialog_FatalWait( p_udt
, _("File could not be verified"),
650 _("It was not possible to download a cryptographic signature for "
651 "the downloaded file \"%s\". Thus, it was deleted."),
653 msg_Err( p_udt
, "Couldn't download signature of downloaded file" );
657 if( memcmp( sign
.issuer_longid
, p_update
->p_pkey
->longid
, 8 ) )
659 vlc_unlink( psz_destfile
);
660 msg_Err( p_udt
, "Invalid signature issuer" );
661 dialog_FatalWait( p_udt
, _("Invalid signature"),
662 _("The cryptographic signature for the downloaded file \"%s\" was "
663 "invalid and could not be used to securely verify it. Thus, the "
664 "file was deleted."),
669 if( sign
.type
!= BINARY_SIGNATURE
)
671 vlc_unlink( psz_destfile
);
672 msg_Err( p_udt
, "Invalid signature type" );
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 uint8_t *p_hash
= hash_from_file( psz_destfile
, &sign
);
684 msg_Err( p_udt
, "Unable to hash %s", psz_destfile
);
685 vlc_unlink( psz_destfile
);
686 dialog_FatalWait( p_udt
, _("File not verifiable"),
687 _("It was not possible to securely verify the downloaded file"
688 " \"%s\". Thus, it was deleted."),
694 if( p_hash
[0] != sign
.hash_verification
[0] ||
695 p_hash
[1] != sign
.hash_verification
[1] )
697 vlc_unlink( psz_destfile
);
698 dialog_FatalWait( p_udt
, _("File corrupted"),
699 _("Downloaded file \"%s\" was corrupted. Thus, it was deleted."),
701 msg_Err( p_udt
, "Bad hash for %s", psz_destfile
);
706 if( verify_signature( &sign
, &p_update
->p_pkey
->key
, p_hash
)
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 SIGNATURE for %s", psz_destfile
);
718 msg_Info( p_udt
, "%s authenticated", psz_destfile
);
722 const char *psz_msg
=
723 _("The new version was successfully downloaded."
724 "Do you want to close VLC and install it now?");
725 int answer
= vlc_dialog_wait_question( p_udt
, VLC_DIALOG_QUESTION_NORMAL
,
726 _("Cancel"), _("Install"), NULL
,
727 _("Update VLC media player"), "%s",
731 wchar_t psz_wdestfile
[MAX_PATH
];
732 MultiByteToWideChar( CP_UTF8
, 0, psz_destfile
, -1, psz_wdestfile
, MAX_PATH
);
733 answer
= (int)ShellExecuteW( NULL
, L
"open", psz_wdestfile
, NULL
, NULL
, SW_SHOW
);
735 libvlc_Quit(p_udt
->obj
.libvlc
);
739 if( p_dialog_id
!= NULL
)
740 vlc_dialog_release( p_udt
, p_dialog_id
);
742 vlc_stream_Delete( p_stream
);
746 free( psz_destfile
);
750 vlc_restorecancel( canc
);
754 update_release_t
*update_GetRelease( update_t
*p_update
)
756 return &p_update
->release
;