1 /*****************************************************************************
2 * update.c: VLC update and plugins download
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either release 2 of the License, or
12 * (at your option) any later release.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
25 * --> check release types.
26 * --> make sure that the version comparision method is ok.
31 * This file contains functions related to VLC and plugins update management
34 /*****************************************************************************
36 *****************************************************************************/
40 #include <ctype.h> /* tolower() */
44 #include <vlc_update.h>
46 #include <vlc_block.h>
47 #include <vlc_stream.h>
49 #include <vlc_interface.h>
50 #include <vlc_charset.h>
53 /*****************************************************************************
55 *****************************************************************************/
57 /* All release notes and source packages should match on "*"
58 * Only binary installers are OS specific ( we only provide these
59 * for Win32, Mac OS X, WincCE, beos(?) ) */
60 #if defined( UNDER_CE )
61 # define UPDATE_VLC_OS "*"
62 # define UPDATE_VLC_ARCH "*"
63 #elif defined( WIN32 )
64 # define UPDATE_VLC_OS "windows"
65 # define UPDATE_VLC_ARCH "i386"
66 #elif defined( __APPLE__ )
67 # define UPDATE_VLC_OS "macosx"
68 # if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
69 # define UPDATE_VLC_ARCH "ppc"
71 # define UPDATE_VLC_ARCH "x86"
73 #elif defined( SYS_BEOS )
74 # define UPDATE_VLC_OS "beos"
75 # define UPDATE_VLC_ARCH "i386"
77 # define UPDATE_VLC_OS "*"
78 # define UPDATE_VLC_ARCH "*"
81 #define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status.xml"
82 #define UPDATE_VLC_MIRRORS_URL "http://update.videolan.org/mirrors.xml"
84 #define STRDUP( a ) ( a ? strdup( a ) : NULL )
86 /*****************************************************************************
88 *****************************************************************************/
90 static void FreeMirrorsList( update_t * );
91 static void FreeReleasesList( update_t * );
92 static void GetMirrorsList( update_t *, vlc_bool_t );
93 static void GetFilesList( update_t *, vlc_bool_t );
95 static int CompareReleases( const struct update_release_t *,
96 const struct update_release_t * );
97 static int CompareReleaseToCurrent( const struct update_release_t * );
99 static unsigned int update_iterator_Reset( update_iterator_t * );
100 static unsigned int update_iterator_NextFile( update_iterator_t * );
101 static unsigned int update_iterator_PrevFile( update_iterator_t * );
102 static unsigned int update_iterator_NextMirror( update_iterator_t * );
103 static unsigned int update_iterator_PrevMirror( update_iterator_t * );
105 static void update_iterator_GetData( update_iterator_t * );
106 static void update_iterator_ClearData( update_iterator_t * );
108 /*****************************************************************************
110 *****************************************************************************/
113 * Create a new update VLC struct
115 * \param p_this the calling vlc_object
116 * \return pointer to new update_t or NULL
118 update_t *__update_New( vlc_object_t *p_this )
122 if( p_this == NULL ) return NULL;
124 p_update = (update_t *)malloc( sizeof( update_t ) );
125 if( !p_update ) return NULL;
127 vlc_mutex_init( p_this, &p_update->lock );
129 p_update->p_libvlc = p_this->p_libvlc;
131 p_update->p_releases = NULL;
132 p_update->i_releases = 0;
133 p_update->b_releases = VLC_FALSE;
135 p_update->p_mirrors = NULL;
136 p_update->i_mirrors = 0;
137 p_update->b_mirrors = VLC_FALSE;
140 msg_Err( p_this, "Auto-update currently disabled." );
141 vlc_mutex_destroy( &p_update->lock );
150 * Delete an update_t struct
152 * \param p_update update_t* pointer
155 void update_Delete( update_t *p_update )
159 vlc_mutex_destroy( &p_update->lock );
160 FreeMirrorsList( p_update );
161 FreeReleasesList( p_update );
166 * Empty the mirrors list
167 * *p_update should be locked before using this function
169 * \param p_update pointer to the update struct
172 static void FreeMirrorsList( update_t *p_update )
176 for( i = 0; i < p_update->i_mirrors; i++ )
178 free( p_update->p_mirrors[i].psz_name );
179 free( p_update->p_mirrors[i].psz_location );
180 free( p_update->p_mirrors[i].psz_type );
181 free( p_update->p_mirrors[i].psz_base_url );
183 FREENULL( p_update->p_mirrors );
184 p_update->i_mirrors = 0;
185 p_update->b_mirrors = VLC_FALSE;
189 * Empty the releases list
190 * *p_update should be locked before calling this function
192 * \param p_update pointer to the update struct
195 static void FreeReleasesList( update_t *p_update )
199 for( i = 0; i < p_update->i_releases; i++ )
202 struct update_release_t *p_release = (p_update->p_releases + i);
203 for( j = 0; j < p_release->i_files; j++ )
205 free( p_release->p_files[j].psz_md5 );
206 free( p_release->p_files[j].psz_url );
207 free( p_release->p_files[j].psz_description );
209 free( p_release->psz_major );
210 free( p_release->psz_minor );
211 free( p_release->psz_revision );
212 free( p_release->psz_extra );
213 free( p_release->psz_svn_revision );
214 free( p_release->p_files );
216 FREENULL( p_update->p_releases );
217 p_update->i_releases = 0;
218 p_update->b_releases = VLC_FALSE;
222 * Get the mirrors list XML file and parse it
223 * *p_update has to be unlocked when calling this function
225 * \param p_update pointer to the update struct
226 * \param b_force set to VLC_TRUE if you want to force the mirrors list update
229 static void GetMirrorsList( update_t *p_update, vlc_bool_t b_force )
231 stream_t *p_stream = NULL;
234 xml_reader_t *p_xml_reader = NULL;
235 char *psz_eltname = NULL;
236 //char *psz_eltvalue = NULL;
237 char *psz_name = NULL;
238 char *psz_value = NULL;
239 struct update_mirror_t tmp_mirror;
241 vlc_mutex_lock( &p_update->lock );
243 memset( &tmp_mirror, 0, sizeof(struct update_mirror_t));
245 if( p_update->b_mirrors && b_force == VLC_FALSE )
247 vlc_mutex_unlock( &p_update->lock );
251 p_xml = xml_Create( p_update->p_libvlc );
254 msg_Err( p_update->p_libvlc, "Failed to open XML parser" );
258 p_stream = stream_UrlNew( p_update->p_libvlc, UPDATE_VLC_MIRRORS_URL );
261 msg_Err( p_update->p_libvlc, "Failed to open %s for reading",
262 UPDATE_VLC_MIRRORS_URL );
266 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
270 msg_Err( p_update->p_libvlc, "Failed to open %s for parsing",
271 UPDATE_VLC_MIRRORS_URL );
275 if( p_update->p_mirrors )
277 FreeMirrorsList( p_update );
280 while( xml_ReaderRead( p_xml_reader ) == 1 )
282 switch( xml_ReaderNodeType( p_xml_reader ) )
285 msg_Err( p_update->p_libvlc, "Error while parsing %s",
286 UPDATE_VLC_MIRRORS_URL );
289 case XML_READER_STARTELEM:
290 psz_eltname = xml_ReaderName( p_xml_reader );
293 msg_Err( p_update->p_libvlc, "Error while parsing %s",
294 UPDATE_VLC_MIRRORS_URL );
298 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
300 psz_name = xml_ReaderName( p_xml_reader );
301 psz_value = xml_ReaderValue( p_xml_reader );
303 if( !psz_name || !psz_value )
305 msg_Err( p_update->p_libvlc, "Error while parsing %s",
306 UPDATE_VLC_MIRRORS_URL );
310 if( !strcmp( psz_eltname, "mirror" ) )
312 if( !strcmp( psz_name, "name" ) )
313 tmp_mirror.psz_name = STRDUP( psz_value );
314 else if( !strcmp( psz_name, "location" ) )
315 tmp_mirror.psz_location = STRDUP( psz_value );
317 else if( !strcmp( psz_eltname, "url" ) )
319 if( !strcmp( psz_name, "type" ) )
320 tmp_mirror.psz_type = STRDUP( psz_value );
321 else if( !strcmp( psz_name, "base" ) )
322 tmp_mirror.psz_base_url = STRDUP( psz_value );
324 FREENULL( psz_name );
325 FREENULL( psz_value );
327 if( !strcmp( psz_eltname, "url" ) )
329 /* append to mirrors list */
330 p_update->p_mirrors =
331 (struct update_mirror_t *)realloc( p_update->p_mirrors,
332 (++(p_update->i_mirrors))
333 *sizeof( struct update_mirror_t ) );
334 p_update->p_mirrors[ p_update->i_mirrors - 1 ] =
336 tmp_mirror.psz_name = STRDUP( tmp_mirror.psz_name );
337 tmp_mirror.psz_location = STRDUP( tmp_mirror.psz_location );
338 tmp_mirror.psz_type = NULL;
339 tmp_mirror.psz_base_url = NULL;
341 FREENULL( psz_eltname );
344 case XML_READER_ENDELEM:
345 psz_eltname = xml_ReaderName( p_xml_reader );
348 msg_Err( p_update->p_libvlc, "Error while parsing %s",
349 UPDATE_VLC_MIRRORS_URL );
353 if( !strcmp( psz_eltname, "mirror" ) )
355 FREENULL( tmp_mirror.psz_name );
356 FREENULL( tmp_mirror.psz_location );
359 FREENULL( psz_eltname );
362 /*case XML_READER_TEXT:
363 psz_eltvalue = xml_ReaderValue( p_xml_reader );
364 FREENULL( psz_eltvalue );
369 p_update->b_mirrors = VLC_TRUE;
372 vlc_mutex_unlock( &p_update->lock );
375 //free( psz_eltvalue );
379 free( tmp_mirror.psz_name );
380 free( tmp_mirror.psz_location );
381 free( tmp_mirror.psz_type );
382 free( tmp_mirror.psz_base_url );
384 if( p_xml_reader && p_xml )
385 xml_ReaderDelete( p_xml, p_xml_reader );
387 stream_Delete( p_stream );
393 * Get the files list XML file and parse it
394 * *p_update has to be unlocked when calling this function
396 * \param p_update pointer to update struct
397 * \param b_force set to VLC_TRUE if you want to force the files list update
400 static void GetFilesList( update_t *p_update, vlc_bool_t b_force )
402 stream_t *p_stream = NULL;
405 xml_reader_t *p_xml_reader = NULL;
407 char *psz_eltname = NULL;
408 char *psz_eltvalue = NULL;
409 char *psz_name = NULL;
410 char *psz_value = NULL;
412 struct update_release_t *p_release = NULL;
413 struct update_release_t tmp_release;
414 struct update_file_t tmp_file;
416 vlc_bool_t b_os = VLC_FALSE, b_arch = VLC_FALSE;
418 memset( &tmp_release, 0, sizeof(struct update_release_t) );
419 memset( &tmp_file, 0, sizeof(struct update_file_t) );
421 tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
423 vlc_mutex_lock( &p_update->lock );
425 if( p_update->b_releases && b_force == VLC_FALSE )
427 vlc_mutex_unlock( &p_update->lock );
431 p_xml = xml_Create( p_update->p_libvlc );
434 msg_Err( p_update->p_libvlc, "Failed to open XML parser" );
438 p_stream = stream_UrlNew( p_update->p_libvlc, UPDATE_VLC_STATUS_URL );
441 msg_Err( p_update->p_libvlc, "Failed to open %s for reading",
442 UPDATE_VLC_STATUS_URL );
446 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
450 msg_Err( p_update->p_libvlc, "Failed to open %s for parsing",
451 UPDATE_VLC_STATUS_URL );
455 if( p_update->p_releases )
457 FreeReleasesList( p_update );
460 while( xml_ReaderRead( p_xml_reader ) == 1 )
462 switch( xml_ReaderNodeType( p_xml_reader ) )
465 msg_Err( p_update->p_libvlc, "Error while parsing %s",
466 UPDATE_VLC_STATUS_URL );
469 case XML_READER_STARTELEM:
470 psz_eltname = xml_ReaderName( p_xml_reader );
473 msg_Err( p_update->p_libvlc, "Error while parsing %s",
474 UPDATE_VLC_STATUS_URL );
478 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
480 psz_name = xml_ReaderName( p_xml_reader );
481 psz_value = xml_ReaderValue( p_xml_reader );
483 if( !psz_name || !psz_value )
485 msg_Err( p_update->p_libvlc, "Error while parsing %s",
486 UPDATE_VLC_STATUS_URL );
492 if( strcmp( psz_eltname, "version" ) == 0 )
494 if( !strcmp( psz_name, "major" ) )
495 tmp_release.psz_major = STRDUP( psz_value );
496 else if( !strcmp( psz_name, "minor" ) )
497 tmp_release.psz_minor = STRDUP( psz_value );
498 else if( !strcmp( psz_name, "revision" ) )
499 tmp_release.psz_revision = STRDUP( psz_value );
500 else if( !strcmp( psz_name, "extra" ) )
501 tmp_release.psz_extra = STRDUP( psz_value );
502 else if( !strcmp( psz_name, "svn" ) )
503 tmp_release.psz_svn_revision =
505 else if( !strcmp( psz_name, "version" ) )
507 if( !strcmp( psz_value, "unstable" ) )
509 UPDATE_RELEASE_TYPE_UNSTABLE;
510 else if( !strcmp( psz_value, "testing" ) )
512 UPDATE_RELEASE_TYPE_TESTING;
515 UPDATE_RELEASE_TYPE_STABLE;
518 else if( !strcmp( psz_eltname, "file" ) )
520 if( !strcmp( psz_name, "type" ) )
522 if( !strcmp( psz_value, "info" ) )
523 tmp_file.i_type = UPDATE_FILE_TYPE_INFO;
524 else if( !strcmp( psz_value, "source" ) )
525 tmp_file.i_type = UPDATE_FILE_TYPE_SOURCE;
526 else if( !strcmp( psz_value, "binary" ) )
527 tmp_file.i_type = UPDATE_FILE_TYPE_BINARY;
528 else if( !strcmp( psz_value, "plugin" ) )
529 tmp_file.i_type = UPDATE_FILE_TYPE_PLUGIN;
531 tmp_file.i_type = UPDATE_FILE_TYPE_UNDEF;
533 else if( !strcmp( psz_name, "md5" ) )
534 tmp_file.psz_md5 = STRDUP( psz_value );
535 else if( !strcmp( psz_name, "size" ) )
536 tmp_file.l_size = atol( psz_value );
537 else if( !strcmp( psz_name, "url" ) )
538 tmp_file.psz_url = STRDUP( psz_value );
541 if( !strcmp( psz_name, "name" )
542 && ( !strcmp( psz_value, UPDATE_VLC_OS )
543 || !strcmp( psz_value, "*" ) )
544 && !strcmp( psz_eltname, "os" ) )
548 if( b_os && !strcmp( psz_name, "name" )
549 && ( !strcmp( psz_value, UPDATE_VLC_ARCH )
550 || !strcmp( psz_value, "*" ) )
551 && !strcmp( psz_eltname, "arch" ) )
555 FREENULL( psz_name );
556 FREENULL( psz_value );
558 if( ( b_os && b_arch && strcmp( psz_eltname, "arch" ) ) )
560 if( !strcmp( psz_eltname, "version" ) )
563 /* look for a previous occurrence of this release */
564 for( i = 0; i < p_update->i_releases; i++ )
566 p_release = p_update->p_releases + i;
567 if( CompareReleases( p_release, &tmp_release )
568 == UPDATE_RELEASE_STATUS_EQUAL )
573 /* if this is the first time that we see this release,
574 * append it to the list of releases */
575 if( i == p_update->i_releases )
577 tmp_release.i_status =
578 CompareReleaseToCurrent( &tmp_release );
579 p_update->p_releases =
580 (struct update_release_t *)realloc( p_update->p_releases,
581 (++(p_update->i_releases))*sizeof( struct update_release_t ) );
582 p_update->p_releases[ p_update->i_releases - 1 ] =
585 p_update->p_releases + p_update->i_releases - 1;
586 tmp_release.psz_major = NULL;
587 tmp_release.psz_minor = NULL;
588 tmp_release.psz_revision = NULL;
589 tmp_release.psz_extra = NULL;
590 tmp_release.psz_svn_revision = NULL;
591 tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
592 tmp_release.i_status = 0;
593 tmp_release.p_files = NULL;
594 tmp_release.i_files = 0;
598 FREENULL( tmp_release.psz_major );
599 FREENULL( tmp_release.psz_minor );
600 FREENULL( tmp_release.psz_revision );
601 FREENULL( tmp_release.psz_extra );
602 FREENULL( tmp_release.psz_svn_revision );
603 tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
604 FREENULL( tmp_release.p_files );
605 tmp_release.i_files = 0;
608 else if( !strcmp( psz_eltname, "file" ) )
610 /* append file to p_release's file list */
611 if( p_release == NULL )
616 (struct update_file_t *)realloc( p_release->p_files,
617 (++(p_release->i_files))*sizeof( struct update_file_t ) );
618 p_release->p_files[ p_release->i_files - 1 ] = tmp_file;
619 tmp_file.i_type = UPDATE_FILE_TYPE_UNDEF;
620 tmp_file.psz_md5 = NULL;
622 tmp_file.psz_url = NULL;
623 tmp_file.psz_description = NULL;
626 FREENULL( psz_eltname );
629 case XML_READER_ENDELEM:
630 psz_eltname = xml_ReaderName( p_xml_reader );
633 msg_Err( p_update->p_libvlc, "Error while parsing %s",
634 UPDATE_VLC_STATUS_URL );
638 if( !strcmp( psz_eltname, "os" ) )
640 else if( !strcmp( psz_eltname, "arch" ) )
642 FREENULL( psz_eltname );
645 case XML_READER_TEXT:
646 psz_eltvalue = xml_ReaderValue( p_xml_reader );
647 if( p_release && p_release->i_files )
648 p_release->p_files[ p_release->i_files - 1 ]
649 .psz_description = STRDUP( psz_eltvalue );
650 FREENULL( psz_eltvalue );
655 p_update->b_releases = VLC_TRUE;
658 vlc_mutex_unlock( &p_update->lock );
661 free( psz_eltvalue );
665 free( tmp_release.psz_major );
666 free( tmp_release.psz_minor );
667 free( tmp_release.psz_revision );
668 free( tmp_release.psz_extra );
669 free( tmp_release.psz_svn_revision );
671 free( tmp_file.psz_md5 );
672 free( tmp_file.psz_url );
673 free( tmp_file.psz_description );
675 if( p_xml_reader && p_xml )
676 xml_ReaderDelete( p_xml, p_xml_reader );
678 stream_Delete( p_stream );
686 * \param p_update pointer to update struct
687 * \param b_force set to VLC_TRUE if you want to force the update
690 void update_Check( update_t *p_update, vlc_bool_t b_force )
694 GetMirrorsList( p_update, b_force );
695 GetFilesList( p_update, b_force );
699 * Compare two release numbers
700 * The comparision algorith basically performs an alphabetical order (strcmp)
701 * comparision of each of the version number elements until it finds two
702 * different ones. This is the tricky function.
704 * \param p1 first release
705 * \param p2 second release
706 * \return like strcmp
708 static int CompareReleases( const struct update_release_t *p1,
709 const struct update_release_t *p2 )
712 if( ( d = strcmp( p1->psz_major, p2->psz_major ) ) ) ;
713 else if( ( d = strcmp( p1->psz_minor, p2->psz_minor ) ) ) ;
714 else if( ( d = strcmp( p1->psz_revision, p2->psz_revision ) ) ) ;
717 d = strcmp( p1->psz_extra, p2->psz_extra );
721 * not num < NULL < num
722 * -test and -svn releases are thus always considered older than
723 * -'' or -0 releases, which is the best i could come up with */
726 strtol( p1->psz_extra, &psz_end1, 10 );
727 strtol( p2->psz_extra, &psz_end2, 10 );
728 if( psz_end2 == p2->psz_extra
729 && ( psz_end1 != p1->psz_extra || *p1->psz_extra == '\0' ) )
734 return UPDATE_RELEASE_STATUS_OLDER;
736 return UPDATE_RELEASE_STATUS_EQUAL;
738 return UPDATE_RELEASE_STATUS_NEWER;
742 * Compare a given release's version number to the current VLC's one
745 * \return >0 if newer, 0 if equal and <0 if older
747 static int CompareReleaseToCurrent( const struct update_release_t *p )
749 struct update_release_t c;
752 memset( &c, 0, sizeof(struct update_release_t) );
753 c.psz_major = STRDUP( PACKAGE_VERSION_MAJOR );
754 c.psz_minor = STRDUP( PACKAGE_VERSION_MINOR );
755 c.psz_revision = STRDUP( PACKAGE_VERSION_REVISION );
756 c.psz_extra = STRDUP( PACKAGE_VERSION_EXTRA );
757 r = CompareReleases( p, &c );
760 free( c.psz_revision );
765 /*****************************************************************************
766 * Updatei_iterator_t functions
767 *****************************************************************************/
770 * Create a new update iterator structure. This structure can then be used to
771 * describe a position and move through the update and mirror trees/lists.
772 * This will use an existing update struct or create a new one if none is
775 * \param p_u the calling update_t
776 * \return a pointer to an update iterator
778 update_iterator_t *update_iterator_New( update_t *p_u )
780 update_iterator_t *p_uit = NULL;
784 p_uit = (update_iterator_t *)malloc( sizeof( update_iterator_t ) );
785 if( p_uit == NULL ) return NULL;
793 p_uit->i_t = UPDATE_FILE_TYPE_ALL;
794 p_uit->i_rs = UPDATE_RELEASE_STATUS_ALL;
795 p_uit->i_rt = UPDATE_RELEASE_TYPE_STABLE;
797 p_uit->file.i_type = UPDATE_FILE_TYPE_NONE;
798 p_uit->file.psz_md5 = NULL;
799 p_uit->file.psz_url = NULL;
800 p_uit->file.l_size = 0;
801 p_uit->file.psz_description = NULL;
803 p_uit->release.psz_version = NULL;
804 p_uit->release.psz_svn_revision = NULL;
805 p_uit->release.i_type = UPDATE_RELEASE_TYPE_UNSTABLE;
806 p_uit->release.i_status = UPDATE_RELEASE_STATUS_NONE;
808 p_uit->mirror.psz_name = NULL;
809 p_uit->mirror.psz_location = NULL;
810 p_uit->mirror.psz_type = NULL;
816 * Delete an update iterator structure (duh!)
818 * \param p_uit pointer to an update iterator
821 void update_iterator_Delete( update_iterator_t *p_uit )
825 update_iterator_ClearData( p_uit );
830 * Reset an update_iterator_t structure
832 * \param p_uit pointer to an update iterator
833 * \return UPDATE_FAIL upon error, UPDATE_SUCCESS otherwise
835 unsigned int update_iterator_Reset( update_iterator_t *p_uit )
843 update_iterator_ClearData( p_uit );
844 return UPDATE_SUCCESS;
848 * Finds the next file in the update tree that matches status and type
849 * requirements set in the update_iterator
851 * \param p_uit update iterator
852 * \return UPDATE_FAIL if we can't find the next file, UPDATE_SUCCESS|UPDATE_FILE if we stay in the same release, UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE if we change the release index
854 static unsigned int update_iterator_NextFile( update_iterator_t *p_uit )
861 /* if the update iterator was already in a "no match" state, start over */
862 if( p_uit->i_r == -1 ) p_uit->i_r = 0;
863 //if( p_uit->i_f == -1 ) p_uit->i_f = 0;
865 vlc_mutex_lock( &p_uit->p_u->lock );
867 for( r = p_uit->i_r; r < p_uit->p_u->i_releases; r++ )
869 if( !( p_uit->p_u->p_releases[r].i_status & p_uit->i_rs ) ) continue;
870 for( f = ( r == p_uit->i_r ? p_uit->i_f + 1 : 0 );
871 f < p_uit->p_u->p_releases[r].i_files; f++ )
873 if( p_uit->p_u->p_releases[r].p_files[f].i_type & p_uit->i_t )
875 goto done;/* "double break" */
883 r = p_uit->p_u->i_releases;
885 if( old_r == p_uit->i_r )
887 update_iterator_GetData( p_uit );
888 vlc_mutex_unlock( &p_uit->p_u->lock );
889 return UPDATE_SUCCESS|UPDATE_FILE;
891 else if( p_uit->i_r == r )
895 update_iterator_GetData( p_uit );
896 vlc_mutex_unlock( &p_uit->p_u->lock );
901 update_iterator_GetData( p_uit );
902 vlc_mutex_unlock( &p_uit->p_u->lock );
903 return UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE;
908 * Finds the previous file in the update tree that matches status and type
909 * requirements set in the update_iterator
911 * \param p_uit update iterator
912 * \return UPDATE_FAIL if we can't find the previous file, UPDATE_SUCCESS|UPDATE_FILE if we stay in the same release, UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE if we change the release index
915 static unsigned int update_iterator_PrevFile( update_iterator_t *p_uit )
919 if( !p_uit ) return UPDATE_FAIL;
923 /* if the update iterator was already in a "no match" state, start over
924 * (begin at the end of the list) */
925 if( p_uit->i_r == -1 ) p_uit->i_r = p_uit->p_u->i_releases - 1;
926 p_uit->i_f = p_uit->p_u->p_releases[p_uit->i_r].i_files + 1;
928 vlc_mutex_lock( &p_uit->p_u->lock );
930 for( r = p_uit->i_r; r >= 0; r-- )
932 if( !( p_uit->p_u->p_releases[r].i_status & p_uit->i_rs ) ) continue;
933 for( f =( r==p_uit->i_r ? p_uit->i_f - 1 : p_uit->p_u->p_releases[r].i_files );
936 if( p_uit->p_u->p_releases[r].p_files[f].i_type & p_uit->i_t )
938 goto done;/* "double break" */
946 r = p_uit->p_u->i_releases;
948 if( old_r == p_uit->i_r )
950 update_iterator_GetData( p_uit );
951 vlc_mutex_unlock( &p_uit->p_u->lock );
952 return UPDATE_SUCCESS|UPDATE_FILE;
954 else if( p_uit->i_r == -1 )
958 update_iterator_GetData( p_uit );
959 vlc_mutex_unlock( &p_uit->p_u->lock );
964 update_iterator_GetData( p_uit );
965 vlc_mutex_unlock( &p_uit->p_u->lock );
966 return UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE;
971 * Finds the next mirror in the update tree
973 * \param update iterator
974 * \return UPDATE_FAIL if we can't find the next mirror, UPDATE_SUCCESS|UPDATE_MIRROR otherwise
976 static unsigned int update_iterator_NextMirror( update_iterator_t *p_uit )
978 if( !p_uit ) return UPDATE_FAIL;
979 vlc_mutex_lock( &p_uit->p_u->lock );
981 if( p_uit->i_m >= p_uit->p_u->i_mirrors ) p_uit->i_m = -1;
982 update_iterator_GetData( p_uit );
983 vlc_mutex_unlock( &p_uit->p_u->lock );
984 return p_uit->i_m == -1 ? UPDATE_FAIL : UPDATE_SUCCESS|UPDATE_MIRROR;
988 * Finds the previous mirror in the update tree
990 * \param update iterator
991 * \return UPDATE_FAIL if we can't find a previous mirror, UPDATE_SUCCESS|UPDATE_MIRROR otherwise
993 static unsigned int update_iterator_PrevMirror( update_iterator_t *p_uit )
995 if( !p_uit ) return UPDATE_FAIL;
996 vlc_mutex_lock( &p_uit->p_u->lock );
998 update_iterator_GetData( p_uit );
999 vlc_mutex_unlock( &p_uit->p_u->lock );
1000 return p_uit->i_m == -1 ? UPDATE_FAIL : UPDATE_SUCCESS|UPDATE_MIRROR;
1004 * Change the update iterator's position in the file and mirrors tree
1005 * If position is negative, don't change it
1007 * \param i_m position in mirrors list
1008 * \param i_r position in releases list
1009 * \param i_f position in release's files list
1010 * \return UPDATE_FAIL when changing position fails or position wasn't changed, a combination of UPDATE_MIRROR, UPDATE_RELEASE and UPDATE_FILE otherwise
1012 unsigned int update_iterator_ChooseMirrorAndFile( update_iterator_t *p_uit,
1013 int i_m, int i_r, int i_f )
1015 unsigned int i_val = 0;
1017 if( !p_uit ) return 0;
1018 vlc_mutex_lock( &p_uit->p_u->lock );
1022 if( i_m < p_uit->p_u->i_mirrors )
1024 if( i_m != p_uit->i_m )
1025 i_val |= UPDATE_MIRROR;
1033 if( i_r < p_uit->p_u->i_releases )
1035 if( i_r != p_uit->i_r )
1036 i_val |= UPDATE_FILE;
1044 if( i_r >= 0 && i_r < p_uit->p_u->i_releases
1045 && i_f < p_uit->p_u->p_releases[p_uit->i_r].i_files )
1047 if( i_f != p_uit->i_f )
1048 i_val |= UPDATE_FILE;
1054 update_iterator_GetData( p_uit );
1055 vlc_mutex_unlock( &p_uit->p_u->lock );
1057 if( ( i_m < 0 || p_uit->i_m >= 0 )
1058 && ( i_r < 0 || p_uit->i_r >= 0 )
1059 && ( i_f < 0 || p_uit->i_f >= 0 ) )
1061 /* Everything worked */
1062 return UPDATE_SUCCESS|i_val;
1066 /* Something failed */
1072 * Fills the iterator data (file, release and mirror structs)
1073 * The update struct should be locked before calling this function.
1075 * \param p_uit update iterator
1078 static void update_iterator_GetData( update_iterator_t *p_uit )
1080 struct update_release_t *p_r = NULL;
1081 struct update_file_t *p_f = NULL;
1082 struct update_mirror_t *p_m = NULL;
1084 update_iterator_ClearData( p_uit );
1086 if( p_uit->i_m >= 0 )
1088 p_m = p_uit->p_u->p_mirrors + p_uit->i_m;
1089 p_uit->mirror.psz_name = STRDUP( p_m->psz_name );
1090 p_uit->mirror.psz_location = STRDUP( p_m->psz_location );
1091 p_uit->mirror.psz_type = STRDUP( p_m->psz_type );
1094 if( p_uit->i_r >= 0 )
1096 p_r = p_uit->p_u->p_releases + p_uit->i_r;
1097 asprintf( &p_uit->release.psz_version, "%s.%s.%s-%s",
1102 p_uit->release.psz_svn_revision = STRDUP( p_r->psz_svn_revision );
1103 p_uit->release.i_type = p_r->i_type;
1104 p_uit->release.i_status = p_r->i_status;
1105 if( p_uit->i_f >= 0 )
1107 p_f = p_r->p_files + p_uit->i_f;
1108 p_uit->file.i_type = p_f->i_type;
1109 p_uit->file.psz_md5 = STRDUP( p_f->psz_md5 );
1110 p_uit->file.l_size = p_f->l_size;
1111 p_uit->file.psz_description = STRDUP( p_f->psz_description);
1112 if( p_f->psz_url[0] == '/' )
1116 asprintf( &p_uit->file.psz_url, "%s%s",
1117 p_m->psz_base_url, p_f->psz_url );
1122 p_uit->file.psz_url = STRDUP( p_f->psz_url );
1129 * Clears the iterator data (file, release and mirror structs)
1131 * \param p_uit update iterator
1134 static void update_iterator_ClearData( update_iterator_t *p_uit )
1136 p_uit->file.i_type = UPDATE_FILE_TYPE_NONE;
1137 FREENULL( p_uit->file.psz_md5 );
1138 p_uit->file.l_size = 0;
1139 FREENULL( p_uit->file.psz_description );
1140 FREENULL( p_uit->file.psz_url );
1141 FREENULL( p_uit->release.psz_version );
1142 FREENULL( p_uit->release.psz_svn_revision );
1143 p_uit->release.i_type = UPDATE_RELEASE_TYPE_UNSTABLE;
1144 p_uit->release.i_status = UPDATE_RELEASE_STATUS_NONE;
1145 FREENULL( p_uit->mirror.psz_name );
1146 FREENULL( p_uit->mirror.psz_location );
1147 FREENULL( p_uit->mirror.psz_type );
1151 * Perform an action on the update iterator
1152 * Only the first matching action is performed.
1154 * \param p_uit update iterator
1155 * \param i_action update action bitmask. can be a combination of UPDATE_NEXT, UPDATE_PREV, UPDATE_MIRROR, UPDATE_RELEASE, UPDATE_FILE, UPDATE_RESET
1156 * \return UPDATE_FAIL if action fails, UPDATE_SUCCESS|(combination of UPDATE_MIRROR, UPDATE_RELEASE and UPDATE_FILE if these changed) otherwise
1158 unsigned int update_iterator_Action( update_iterator_t *p_uit, int i_action )
1160 if( i_action & UPDATE_RESET )
1162 return update_iterator_Reset( p_uit );
1165 if( i_action & UPDATE_MIRROR )
1167 if( i_action & UPDATE_PREV )
1169 return update_iterator_PrevMirror( p_uit );
1173 return update_iterator_NextMirror( p_uit );
1176 /*else if( i_action & UPDATE_RELEASE )
1178 if( i_action & UPDATE_PREV )
1180 return update_iterator_PrevRelease( p_uit );
1184 return update_iterator_NextRelease( p_uit );
1187 else if( i_action & UPDATE_FILE )
1189 if( i_action & UPDATE_PREV )
1191 return update_iterator_PrevFile( p_uit );
1195 return update_iterator_NextFile( p_uit );
1200 return UPDATE_SUCCESS;
1205 * Object to launch download thread in a different object
1209 char *psz_dest; //< Download destination
1210 struct update_file_t src; //< Download source
1211 char *psz_status; //< Download status displayed in progress dialog
1212 } download_thread_t;
1214 void update_download_for_real( download_thread_t *p_this );
1217 * Download the file selected by the update iterator. This function will
1218 * launch the download in a new thread (downloads can be long)
1220 * \param p_uit update iterator
1221 * \param psz_dest destination file path
1224 void update_download( update_iterator_t *p_uit, const char *psz_dest )
1226 download_thread_t *p_dt =
1227 vlc_object_create( p_uit->p_u->p_libvlc, sizeof( download_thread_t ) );
1229 p_dt->psz_dest = strdup( psz_dest );
1230 p_dt->src.i_type = p_uit->file.i_type;
1231 p_dt->src.l_size = p_uit->file.l_size;
1232 p_dt->src.psz_md5 = STRDUP( p_uit->file.psz_md5 );
1233 p_dt->src.psz_url = STRDUP( p_uit->file.psz_url );
1234 p_dt->src.psz_description = STRDUP( p_uit->file.psz_description );
1235 asprintf( &p_dt->psz_status, "%s - %s (%s)\nSource: %s\nDestination: %s",
1236 p_uit->file.psz_description, p_uit->release.psz_version,
1237 p_uit->release.psz_svn_revision, p_uit->file.psz_url,
1240 vlc_thread_create( p_dt, "download thread", update_download_for_real,
1241 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1245 * Convert a long int size in bytes to a string
1247 * \param l_size the size in bytes
1248 * \return the size as a string
1250 static char *size_str( long int l_size )
1254 asprintf( &psz_tmp, "%.1f GB", (float)l_size/(1<<30) );
1256 asprintf( &psz_tmp, "%.1f MB", (float)l_size/(1<<20) );
1257 else if( l_size >> 10 )
1258 asprintf( &psz_tmp, "%.1f kB", (float)l_size/(1<<10) );
1260 asprintf( &psz_tmp, "%ld B", l_size );
1265 * The true download function.
1267 * \param p_this the download_thread_t object
1270 void update_download_for_real( download_thread_t *p_this )
1272 char *psz_dest = p_this->psz_dest;
1273 char *psz_src = p_this->src.psz_url;
1275 libvlc_int_t *p_libvlc = p_this->p_libvlc;
1277 FILE *p_file = NULL;
1283 long int l_size, l_done = 0;
1285 vlc_thread_ready( p_this );
1287 asprintf( &psz_status, "%s\nDownloading... 0.0/? %.1f%% done",
1288 p_this->psz_status, 0.0 );
1289 i_progress = intf_UserProgress( p_libvlc, "Downloading...",
1290 psz_status, 0.0, 0 );
1292 p_stream = stream_UrlNew( p_libvlc, psz_src );
1295 msg_Err( p_libvlc, "Failed to open %s for reading", psz_src );
1296 intf_UserFatal( p_libvlc, VLC_TRUE, "Download Error",
1297 "VLC failed to open %s for reading.", psz_src );
1298 intf_UserHide( p_libvlc, i_progress );
1302 l_size = stream_Size(p_stream);
1303 if( l_size != p_this->src.l_size )
1305 stream_Delete( p_stream );
1307 msg_Err( p_this, "%s hasn't a correct size (%li instead of %li)."
1308 " Cancelling download.",
1309 p_this->src.psz_description,
1311 p_this->src.l_size );
1314 p_file = utf8_fopen( psz_dest, "w" );
1317 msg_Err( p_libvlc, "Failed to open %s for writing", psz_dest );
1318 intf_UserFatal( p_libvlc, VLC_TRUE, "Download Error",
1319 "VLC failed to open %s for writing.", psz_dest );
1320 intf_UserHide( p_libvlc, i_progress );
1325 char *psz_s1; char *psz_s2;
1328 p_buffer = (void *)malloc( 1<<10 );
1331 if( p_this->src.i_type & ( UPDATE_FILE_TYPE_SOURCE | UPDATE_FILE_TYPE_BINARY | UPDATE_FILE_TYPE_PLUGIN ) )
1333 while( ( i_read = stream_Read( p_stream, p_buffer, 1<<10 ) ) )
1337 fwrite( p_buffer, i_read, 1, p_file );
1338 if( p_this->src.i_type & ( UPDATE_FILE_TYPE_SOURCE | UPDATE_FILE_TYPE_BINARY | UPDATE_FILE_TYPE_PLUGIN ) )
1339 AddMD5( &md5_s, p_buffer, (size_t) i_read );
1343 f_progress = 100.0*(float)l_done/(float)l_size;
1344 psz_s1 = size_str( l_done );
1345 psz_s2 = size_str( l_size );
1346 asprintf( &psz_status, "%s\nDownloading... %s/%s (%.1f%%) done",
1347 p_this->psz_status, psz_s1, psz_s2, f_progress );
1351 intf_ProgressUpdate( p_libvlc, i_progress,
1352 psz_status, f_progress, 0 );
1357 stream_Delete( p_stream );
1359 if( l_done == p_this->src.l_size &&
1360 p_this->src.i_type & ( UPDATE_FILE_TYPE_SOURCE |
1361 UPDATE_FILE_TYPE_BINARY | UPDATE_FILE_TYPE_PLUGIN ) )
1364 char *psz_md5 = psz_md5_hash( &md5_s );
1365 if( !p_this->src.psz_md5 || !psz_md5 ||
1366 strncmp( psz_md5, p_this->src.psz_md5, 32 ) )
1368 msg_Err( p_this, _("%s has an incorrect checksum, download failed or mirror is compromised.\n Please run an antivirus on %s, and report if that file is trojaned.\n If not, please try later."),
1369 p_this->src.psz_description, psz_dest );
1375 psz_s2 = size_str( l_size );
1376 asprintf( &psz_status, "%s\nDone %s (100.00%%)",
1377 p_this->psz_status, psz_s2 );
1379 intf_ProgressUpdate( p_libvlc, i_progress, psz_status, 100.0, 0 );
1385 free( p_this->psz_dest );
1386 free( p_this->src.psz_url );
1387 free( p_this->src.psz_description );
1388 free( p_this->src.psz_md5 );
1389 free( p_this->psz_status );
1391 vlc_object_destroy( p_this );