]> git.sesse.net Git - vlc/blob - src/misc/update.c
src/playlist/loadsave.c, modules/demux/playlist/*, modules/gui/*,
[vlc] / src / misc / update.c
1 /*****************************************************************************
2  * update.c: VLC update and plugins download
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id: $
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24 /* TODO
25  * --> check release types.
26  * --> make sure that the version comparision method is ok.
27  */
28
29 /**
30  *   \file
31  *   This file contains functions related to VLC and plugins update management
32  */
33
34 /*****************************************************************************
35  * Preamble
36  *****************************************************************************/
37 #include <stdlib.h>                                      /* malloc(), free() */
38 #include <ctype.h>                                              /* tolower() */
39
40 #include <vlc/vlc.h>
41
42 #include "vlc_update.h"
43
44 #include "vlc_block.h"
45 #include "vlc_stream.h"
46 #include "vlc_xml.h"
47 #include "vlc_interaction.h"
48 #include "charset.h"
49
50 /*****************************************************************************
51  * Misc defines
52  *****************************************************************************/
53
54 /* All release notes and source packages should match on "*"
55  * Only binary installers are OS specific ( we only provide these
56  * for Win32, Mac OS X, WincCE, beos(?) ) */
57 #if defined( UNDER_CE )
58 #   define UPDATE_VLC_OS "*"
59 #   define UPDATE_VLC_ARCH "*"
60 #elif defined( WIN32 )
61 #   define UPDATE_VLC_OS "windows"
62 #   define UPDATE_VLC_ARCH "i386"
63 #elif defined( __APPLE__ )
64 #   define UPDATE_VLC_OS "macosx"
65 #   if defined( __powerpc__ ) || defined( __ppc__ ) || defined( __ppc64__ )
66 #       define UPDATE_VLC_ARCH "ppc"
67 #   else
68 #       define UPDATE_VLC_ARCH "x86"
69 #   endif
70 #elif defined( SYS_BEOS )
71 #   define UPDATE_VLC_OS "beos"
72 #   define UPDATE_VLC_ARCH "i386"
73 #else
74 #   define UPDATE_VLC_OS "*"
75 #   define UPDATE_VLC_ARCH "*"
76 #endif
77
78 #define UPDATE_VLC_STATUS_URL "http://update.videolan.org/vlc/status.xml"
79 #define UPDATE_VLC_MIRRORS_URL "http://update.videolan.org/mirrors.xml"
80
81 #define FREE( a ) free(a);a=NULL;
82 #define STRDUP( a ) ( a ? strdup( a ) : NULL )
83
84 /*****************************************************************************
85  * Local Prototypes
86  *****************************************************************************/
87
88 void FreeMirrorsList( update_t * );
89 void FreeReleasesList( update_t * );
90 void GetMirrorsList( update_t *, vlc_bool_t );
91 void GetFilesList( update_t *, vlc_bool_t );
92
93 int CompareReleases( struct update_release_t *, struct update_release_t * );
94 int CompareReleaseToCurrent( struct update_release_t * );
95
96 unsigned int update_iterator_Reset( update_iterator_t * );
97 unsigned int update_iterator_NextFile( update_iterator_t * );
98 unsigned int update_iterator_PrevFile( update_iterator_t * );
99 unsigned int update_iterator_NextMirror( update_iterator_t * );
100 unsigned int update_iterator_PrevMirror( update_iterator_t * );
101
102 void update_iterator_GetData( update_iterator_t * );
103 void update_iterator_ClearData( update_iterator_t * );
104
105 /*****************************************************************************
106  * Update_t functions
107  *****************************************************************************/
108
109 /**
110  * Create a new update VLC struct
111  *
112  * \param p_this the calling vlc_object
113  * \return pointer to new update_t or NULL
114  */
115 update_t *__update_New( vlc_object_t *p_this )
116 {
117     update_t *p_update;
118
119     if( p_this == NULL ) return NULL;
120
121     p_update = (update_t *)malloc( sizeof( update_t ) );
122
123     vlc_mutex_init( p_this, &p_update->lock );
124
125     p_update->p_vlc = p_this->p_vlc;
126
127     p_update->p_releases = NULL;
128     p_update->i_releases = 0;
129     p_update->b_releases = VLC_FALSE;
130
131     p_update->p_mirrors = NULL;
132     p_update->i_mirrors = 0;
133     p_update->b_mirrors = VLC_FALSE;
134
135     return p_update;
136 }
137
138 /**
139  * Delete an update_t struct
140  *
141  * \param p_update update_t* pointer
142  * \return nothing
143  */
144 void update_Delete( update_t *p_update )
145 {
146     vlc_mutex_destroy( &p_update->lock );
147     FreeMirrorsList( p_update );
148     FreeReleasesList( p_update );
149     free( p_update );
150 }
151
152 /**
153  * Empty the mirrors list
154  * *p_update should be locked before using this function
155  *
156  * \param p_update pointer to the update struct
157  * \return nothing
158  */
159 void FreeMirrorsList( update_t *p_update )
160 {
161     int i;
162
163     for( i = 0; i < p_update->i_mirrors; i++ )
164     {
165         free( p_update->p_mirrors[i].psz_name );
166         free( p_update->p_mirrors[i].psz_location );
167         free( p_update->p_mirrors[i].psz_type );
168         free( p_update->p_mirrors[i].psz_base_url );
169     }
170     FREE( p_update->p_mirrors );
171     p_update->i_mirrors = 0;
172     p_update->b_mirrors = VLC_FALSE;
173 }
174
175 /**
176  * Empty the releases list
177  * *p_update should be locked before calling this function
178  *
179  * \param p_update pointer to the update struct
180  * \return nothing
181  */
182 void FreeReleasesList( update_t *p_update )
183 {
184     int i;
185
186     for( i = 0; i < p_update->i_releases; i++ )
187     {
188         int j;
189         struct update_release_t *p_release = (p_update->p_releases + i);
190         for( j = 0; j < p_release->i_files; j++ )
191         {
192             free( p_release->p_files[j].psz_md5 );
193             free( p_release->p_files[j].psz_url );
194             free( p_release->p_files[j].psz_description );
195         }
196         free( p_release->psz_major );
197         free( p_release->psz_minor );
198         free( p_release->psz_revision );
199         free( p_release->psz_extra );
200         free( p_release->psz_svn_revision );
201         free( p_release->p_files );
202     }
203     FREE( p_update->p_releases );
204     p_update->i_releases = 0;
205     p_update->b_releases = VLC_FALSE;
206 }
207
208 /**
209  * Get the mirrors list XML file and parse it
210  * *p_update has to be unlocked when calling this function
211  *
212  * \param p_update pointer to the update struct
213  * \param b_force set to VLC_TRUE if you want to force the mirrors list update
214  * \return nothing
215  */
216 void GetMirrorsList( update_t *p_update, vlc_bool_t b_force )
217 {
218     stream_t *p_stream = NULL;
219
220     xml_t *p_xml = NULL;
221     xml_reader_t *p_xml_reader = NULL;
222
223     char *psz_eltname = NULL;
224     //char *psz_eltvalue = NULL;
225     char *psz_name = NULL;
226     char *psz_value = NULL;
227
228     struct update_mirror_t tmp_mirror = {0};
229
230     vlc_mutex_lock( &p_update->lock );
231
232     if( p_update->b_mirrors && b_force == VLC_FALSE )
233     {
234         vlc_mutex_unlock( &p_update->lock );
235         return;
236     }
237
238     p_xml = xml_Create( p_update->p_vlc );
239     if( !p_xml )
240     {
241         msg_Err( p_update->p_vlc, "Failed to open XML parser" );
242         goto error;
243     }
244
245     p_stream = stream_UrlNew( p_update->p_vlc, UPDATE_VLC_MIRRORS_URL );
246     if( !p_stream )
247     {
248         msg_Err( p_update->p_vlc, "Failed to open %s for reading",
249                  UPDATE_VLC_MIRRORS_URL );
250         goto error;
251     }
252
253     p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
254
255     if( !p_xml_reader )
256     {
257         msg_Err( p_update->p_vlc, "Failed to open %s for parsing",
258                  UPDATE_VLC_MIRRORS_URL );
259         goto error;
260     }
261
262     if( p_update->p_mirrors )
263     {
264         FreeMirrorsList( p_update );
265     }
266
267     while( xml_ReaderRead( p_xml_reader ) == 1 )
268     {
269         switch( xml_ReaderNodeType( p_xml_reader ) )
270         {
271             case -1:
272                 msg_Err( p_update->p_vlc, "Error while parsing %s",
273                          UPDATE_VLC_MIRRORS_URL );
274                 goto error;
275
276             case XML_READER_STARTELEM:
277                 psz_eltname = xml_ReaderName( p_xml_reader );
278                 if( !psz_eltname )
279                 {
280                     msg_Err( p_update->p_vlc, "Error while parsing %s",
281                              UPDATE_VLC_MIRRORS_URL );
282                     goto error;
283                 }
284
285                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
286                 {
287                     psz_name = xml_ReaderName( p_xml_reader );
288                     psz_value = xml_ReaderValue( p_xml_reader );
289
290                     if( !psz_name || !psz_value )
291                     {
292                         msg_Err( p_update->p_vlc, "Error while parsing %s",
293                                  UPDATE_VLC_MIRRORS_URL );
294                         goto error;
295                     }
296
297                     if( !strcmp( psz_eltname, "mirror" ) )
298                     {
299                         if( !strcmp( psz_name, "name" ) )
300                             tmp_mirror.psz_name = STRDUP( psz_value );
301                         else if( !strcmp( psz_name, "location" ) )
302                             tmp_mirror.psz_location = STRDUP( psz_value );
303                     }
304                     else if( !strcmp( psz_eltname, "url" ) )
305                     {
306                         if( !strcmp( psz_name, "type" ) )
307                             tmp_mirror.psz_type = STRDUP( psz_value );
308                         else if( !strcmp( psz_name, "base" ) )
309                             tmp_mirror.psz_base_url = STRDUP( psz_value );
310                     }
311                     FREE( psz_name );
312                     FREE( psz_value );
313                 }
314                 if( !strcmp( psz_eltname, "url" ) )
315                 {
316                     /* append to mirrors list */
317                     p_update->p_mirrors =
318                     (struct update_mirror_t *)realloc( p_update->p_mirrors,
319                                        (++(p_update->i_mirrors))
320                                        *sizeof( struct update_mirror_t ) );
321                     p_update->p_mirrors[ p_update->i_mirrors - 1 ] =
322                         tmp_mirror;
323                     tmp_mirror.psz_name = STRDUP( tmp_mirror.psz_name );
324                     tmp_mirror.psz_location = STRDUP( tmp_mirror.psz_location );
325                     tmp_mirror.psz_type = NULL;
326                     tmp_mirror.psz_base_url = NULL;
327                 }
328                 FREE( psz_eltname );
329                 break;
330
331             case XML_READER_ENDELEM:
332                 psz_eltname = xml_ReaderName( p_xml_reader );
333                 if( !psz_eltname )
334                 {
335                     msg_Err( p_update->p_vlc, "Error while parsing %s",
336                              UPDATE_VLC_MIRRORS_URL );
337                     goto error;
338                 }
339
340                 if( !strcmp( psz_eltname, "mirror" ) )
341                 {
342                     FREE( tmp_mirror.psz_name );
343                     FREE( tmp_mirror.psz_location );
344                 }
345
346                 FREE( psz_eltname );
347                 break;
348
349             /*case XML_READER_TEXT:
350                 psz_eltvalue = xml_ReaderValue( p_xml_reader );
351                 FREE( psz_eltvalue );
352                 break;*/
353         }
354     }
355
356     p_update->b_mirrors = VLC_TRUE;
357
358     error:
359         vlc_mutex_unlock( &p_update->lock );
360
361         free( psz_eltname );
362         //free( psz_eltvalue );
363         free( psz_name );
364         free( psz_value );
365
366         free( tmp_mirror.psz_name );
367         free( tmp_mirror.psz_location );
368         free( tmp_mirror.psz_type );
369         free( tmp_mirror.psz_base_url );
370
371         if( p_xml_reader && p_xml )
372             xml_ReaderDelete( p_xml, p_xml_reader );
373         if( p_stream )
374             stream_Delete( p_stream );
375         if( p_xml )
376             xml_Delete( p_xml );
377 }
378
379 /**
380  * Get the files list XML file and parse it
381  * *p_update has to be unlocked when calling this function
382  *
383  * \param p_update pointer to update struct
384  * \param b_force set to VLC_TRUE if you want to force the files list update
385  * \return nothing
386  */
387 void GetFilesList( update_t *p_update, vlc_bool_t b_force )
388 {
389     stream_t *p_stream = NULL;
390
391     xml_t *p_xml = NULL;
392     xml_reader_t *p_xml_reader = NULL;
393
394     char *psz_eltname = NULL;
395     char *psz_eltvalue = NULL;
396     char *psz_name = NULL;
397     char *psz_value = NULL;
398
399     struct update_release_t *p_release = NULL;
400     struct update_release_t tmp_release = {0};
401     struct update_file_t tmp_file = {0};
402
403     vlc_bool_t b_os = VLC_FALSE, b_arch = VLC_FALSE;
404
405     tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
406
407     vlc_mutex_lock( &p_update->lock );
408
409     if( p_update->b_releases && b_force == VLC_FALSE )
410     {
411         vlc_mutex_unlock( &p_update->lock );
412         return;
413     }
414
415     p_xml = xml_Create( p_update->p_vlc );
416     if( !p_xml )
417     {
418         msg_Err( p_update->p_vlc, "Failed to open XML parser" );
419         goto error;
420     }
421
422     p_stream = stream_UrlNew( p_update->p_vlc, UPDATE_VLC_STATUS_URL );
423     if( !p_stream )
424     {
425         msg_Err( p_update->p_vlc, "Failed to open %s for reading",
426                  UPDATE_VLC_STATUS_URL );
427         goto error;
428     }
429
430     p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
431
432     if( !p_xml_reader )
433     {
434         msg_Err( p_update->p_vlc, "Failed to open %s for parsing",
435                  UPDATE_VLC_STATUS_URL );
436         goto error;
437     }
438
439     if( p_update->p_releases )
440     {
441         FreeReleasesList( p_update );
442     }
443
444     while( xml_ReaderRead( p_xml_reader ) == 1 )
445     {
446         switch( xml_ReaderNodeType( p_xml_reader ) )
447         {
448             case -1:
449                 msg_Err( p_update->p_vlc, "Error while parsing %s",
450                          UPDATE_VLC_STATUS_URL );
451                 goto error;
452
453             case XML_READER_STARTELEM:
454                 psz_eltname = xml_ReaderName( p_xml_reader );
455                 if( !psz_eltname )
456                 {
457                     msg_Err( p_update->p_vlc, "Error while parsing %s",
458                              UPDATE_VLC_STATUS_URL );
459                     goto error;
460                 }
461
462                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
463                 {
464                     psz_name = xml_ReaderName( p_xml_reader );
465                     psz_value = xml_ReaderValue( p_xml_reader );
466
467                     if( !psz_name || !psz_value )
468                     {
469                         msg_Err( p_update->p_vlc, "Error while parsing %s",
470                                  UPDATE_VLC_STATUS_URL );
471                         goto error;
472                     }
473
474                     if( b_os && b_arch )
475                     {
476                         if( strcmp( psz_eltname, "version" ) == 0 )
477                         {
478                             if( !strcmp( psz_name, "major" ) )
479                                 tmp_release.psz_major = STRDUP( psz_value );
480                             else if( !strcmp( psz_name, "minor" ) )
481                                 tmp_release.psz_minor = STRDUP( psz_value );
482                             else if( !strcmp( psz_name, "revision" ) )
483                                 tmp_release.psz_revision = STRDUP( psz_value );
484                             else if( !strcmp( psz_name, "extra" ) )
485                                 tmp_release.psz_extra = STRDUP( psz_value );
486                             else if( !strcmp( psz_name, "svn" ) )
487                                 tmp_release.psz_svn_revision =
488                                                            STRDUP( psz_value );
489                             else if( !strcmp( psz_name, "version" ) )
490                             {
491                                 if( !strcmp( psz_value, "unstable" ) )
492                                     tmp_release.i_type =
493                                                   UPDATE_RELEASE_TYPE_UNSTABLE;
494                                 else if( !strcmp( psz_value, "testing" ) )
495                                     tmp_release.i_type =
496                                                   UPDATE_RELEASE_TYPE_TESTING;
497                                 else
498                                     tmp_release.i_type =
499                                                   UPDATE_RELEASE_TYPE_STABLE;
500                             }
501                         }
502                         else if( !strcmp( psz_eltname, "file" ) )
503                         {
504                             if( !strcmp( psz_name, "type" ) )
505                             {
506                                 if( !strcmp( psz_value, "info" ) )
507                                     tmp_file.i_type = UPDATE_FILE_TYPE_INFO;
508                                 else if( !strcmp( psz_value, "source" ) )
509                                     tmp_file.i_type = UPDATE_FILE_TYPE_SOURCE;
510                                 else if( !strcmp( psz_value, "binary" ) )
511                                     tmp_file.i_type = UPDATE_FILE_TYPE_BINARY;
512                                 else if( !strcmp( psz_value, "plugin" ) )
513                                     tmp_file.i_type = UPDATE_FILE_TYPE_PLUGIN;
514                                 else
515                                     tmp_file.i_type = UPDATE_FILE_TYPE_UNDEF;
516                             }
517                             else if( !strcmp( psz_name, "md5" ) )
518                                 tmp_file.psz_md5 = STRDUP( psz_value );
519                             else if( !strcmp( psz_name, "size" ) )
520                                 tmp_file.l_size = atol( psz_value );
521                             else if( !strcmp( psz_name, "url" ) )
522                                 tmp_file.psz_url = STRDUP( psz_value );
523                         }
524                     }
525                     if( !strcmp( psz_name, "name" )
526                         && ( !strcmp( psz_value, UPDATE_VLC_OS )
527                            || !strcmp( psz_value, "*" ) )
528                         && !strcmp( psz_eltname, "os" ) )
529                     {
530                         b_os = VLC_TRUE;
531                     }
532                     if( b_os && !strcmp( psz_name, "name" )
533                         && ( !strcmp( psz_value, UPDATE_VLC_ARCH )
534                            || !strcmp( psz_value, "*" ) )
535                         && !strcmp( psz_eltname, "arch" ) )
536                     {
537                         b_arch = VLC_TRUE;
538                     }
539                     FREE( psz_name );
540                     FREE( psz_value );
541                 }
542                 if( ( b_os && b_arch && strcmp( psz_eltname, "arch" ) ) )
543                 {
544                     if( !strcmp( psz_eltname, "version" ) )
545                     {
546                         int i;
547                         /* look for a previous occurence of this release */
548                         for( i = 0; i < p_update->i_releases; i++ )
549                         {
550                             p_release = p_update->p_releases + i;
551                             if( CompareReleases( p_release, &tmp_release )
552                                 == UPDATE_RELEASE_STATUS_EQUAL )
553                             {
554                                 break;
555                             }
556                         }
557                         /* if this is the first time that we see this release,
558                          * append it to the list of releases */
559                         if( i == p_update->i_releases )
560                         {
561                             tmp_release.i_status =
562                                 CompareReleaseToCurrent( &tmp_release );
563                             p_update->p_releases =
564                (struct update_release_t *)realloc( p_update->p_releases,
565                (++(p_update->i_releases))*sizeof( struct update_release_t ) );
566                             p_update->p_releases[ p_update->i_releases - 1 ] =
567                                 tmp_release;
568                             p_release =
569                                 p_update->p_releases + p_update->i_releases - 1;
570                             tmp_release.psz_major = NULL;
571                             tmp_release.psz_minor = NULL;
572                             tmp_release.psz_revision = NULL;
573                             tmp_release.psz_extra = NULL;
574                             tmp_release.psz_svn_revision = NULL;
575                             tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
576                             tmp_release.i_status = 0;
577                             tmp_release.p_files = NULL;
578                             tmp_release.i_files = 0;
579                         }
580                         else
581                         {
582                             FREE( tmp_release.psz_major );
583                             FREE( tmp_release.psz_minor );
584                             FREE( tmp_release.psz_revision );
585                             FREE( tmp_release.psz_extra );
586                             FREE( tmp_release.psz_svn_revision );
587                             tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
588                             FREE( tmp_release.p_files );
589                             tmp_release.i_files = 0;
590                         }
591                     }
592                     else if( !strcmp( psz_eltname, "file" ) )
593                     {
594                         /* append file to p_release's file list */
595                         if( p_release == NULL )
596                         {
597                             goto error;
598                         }
599                         p_release->p_files =
600                     (struct update_file_t *)realloc( p_release->p_files,
601                     (++(p_release->i_files))*sizeof( struct update_file_t ) );
602                         p_release->p_files[ p_release->i_files - 1 ] = tmp_file;
603                         tmp_file.i_type = UPDATE_FILE_TYPE_UNDEF;
604                         tmp_file.psz_md5 = NULL;
605                         tmp_file.l_size = 0;
606                         tmp_file.psz_url = NULL;
607                         tmp_file.psz_description = NULL;
608                     }
609                 }
610                 FREE( psz_eltname );
611                 break;
612
613             case XML_READER_ENDELEM:
614                 psz_eltname = xml_ReaderName( p_xml_reader );
615                 if( !psz_eltname )
616                 {
617                     msg_Err( p_update->p_vlc, "Error while parsing %s",
618                              UPDATE_VLC_STATUS_URL );
619                     goto error;
620                 }
621
622                 if( !strcmp( psz_eltname, "os" ) )
623                     b_os = VLC_FALSE;
624                 else if( !strcmp( psz_eltname, "arch" ) )
625                     b_arch = VLC_FALSE;
626                 FREE( psz_eltname );
627                 break;
628
629             case XML_READER_TEXT:
630                 psz_eltvalue = xml_ReaderValue( p_xml_reader );
631                 if( p_release && p_release->i_files )
632                     p_release->p_files[ p_release->i_files - 1 ]
633                                .psz_description = STRDUP( psz_eltvalue );
634                 FREE( psz_eltvalue );
635                 break;
636         }
637     }
638
639     p_update->b_releases = VLC_TRUE;
640
641     error:
642         vlc_mutex_unlock( &p_update->lock );
643
644         free( psz_eltname );
645         free( psz_eltvalue );
646         free( psz_name );
647         free( psz_value );
648
649         free( tmp_release.psz_major );
650         free( tmp_release.psz_minor );
651         free( tmp_release.psz_revision );
652         free( tmp_release.psz_extra );
653         free( tmp_release.psz_svn_revision );
654
655         free( tmp_file.psz_md5 );
656         free( tmp_file.psz_url );
657         free( tmp_file.psz_description );
658
659         if( p_xml_reader && p_xml )
660             xml_ReaderDelete( p_xml, p_xml_reader );
661         if( p_stream )
662             stream_Delete( p_stream );
663         if( p_xml )
664             xml_Delete( p_xml );
665 }
666
667 /**
668  * Check for updates
669  *
670  * \param p_update pointer to update struct
671  * \param b_force set to VLC_TRUE if you want to force the update
672  * \returns nothing
673  */
674 void update_Check( update_t *p_update, vlc_bool_t b_force )
675 {
676     if( p_update == NULL ) return;
677     GetMirrorsList( p_update, b_force );
678     GetFilesList( p_update, b_force );
679 }
680
681 /**
682  * Compare two release numbers
683  * The comparision algorith basically performs an alphabetical order (strcmp)
684  * comparision of each of the version number elements until it finds two
685  * different ones. This is the tricky function.
686  *
687  * \param p1 first release
688  * \param p2 second release
689  * \return like strcmp
690  */
691 int CompareReleases( struct update_release_t *p1, struct update_release_t *p2 )
692 {
693     int d;
694     if( ( d = strcmp( p1->psz_major, p2->psz_major ) ) ) ;
695     else if( ( d = strcmp( p1->psz_minor, p2->psz_minor ) ) ) ;
696     else if( ( d = strcmp( p1->psz_revision, p2->psz_revision ) ) ) ;
697     else
698     {
699         d = strcmp( p1->psz_extra, p2->psz_extra );
700         if( d<0 )
701         {
702         /* FIXME:
703          * not num < NULL < num
704          * -test and -svn releases are thus always considered older than
705          * -'' or -0 releases, which is the best i could come up with */
706             char *psz_end1;
707             char *psz_end2;
708             strtol( p1->psz_extra, &psz_end1, 10 );
709             strtol( p2->psz_extra, &psz_end2, 10 );
710             if( psz_end2 == p2->psz_extra
711              && ( psz_end1 != p1->psz_extra || *p1->psz_extra == '\0' ) )
712                 d = 1;
713         }
714     }
715     if( d < 0 )
716         return UPDATE_RELEASE_STATUS_OLDER;
717     else if( d == 0 )
718         return UPDATE_RELEASE_STATUS_EQUAL;
719     else
720         return UPDATE_RELEASE_STATUS_NEWER;
721 }
722
723 /**
724  * Compare a given release's version number to the current VLC's one
725  *
726  * \param p a release
727  * \return >0 if newer, 0 if equal and <0 if older
728  */
729 int CompareReleaseToCurrent( struct update_release_t *p )
730 {
731     struct update_release_t c = {0};
732     int r;
733     c.psz_major = STRDUP( PACKAGE_VERSION_MAJOR );
734     c.psz_minor = STRDUP( PACKAGE_VERSION_MINOR );
735     c.psz_revision = STRDUP( PACKAGE_VERSION_REVISION );
736     c.psz_extra = STRDUP( PACKAGE_VERSION_EXTRA );
737     r =  CompareReleases( p, &c );
738     free( c.psz_major );
739     free( c.psz_minor );
740     free( c.psz_revision );
741     free( c.psz_extra );
742     return r;
743 }
744
745 /*****************************************************************************
746  * Updatei_iterator_t functions
747  *****************************************************************************/
748
749 /**
750  * Create a new update iterator structure. This structure can then be used to
751  * describe a position and move through the update and mirror trees/lists.
752  * This will use an existing update struct or create a new one if none is
753  * found
754  *
755  * \param p_u the calling update_t
756  * \return a pointer to an update iterator
757  */
758 update_iterator_t *update_iterator_New( update_t *p_u )
759 {
760     update_iterator_t *p_uit = NULL;
761
762     if( p_u == NULL )
763         return NULL;
764
765     p_uit = (update_iterator_t *)malloc( sizeof( update_iterator_t ) );
766     if( p_uit == NULL ) return NULL;
767
768     p_uit->p_u = p_u;
769
770     p_uit->i_m = -1;
771     p_uit->i_r = -1;
772     p_uit->i_f = -1;
773
774     p_uit->i_t = UPDATE_FILE_TYPE_ALL;
775     p_uit->i_rs = UPDATE_RELEASE_STATUS_ALL;
776     p_uit->i_rt = UPDATE_RELEASE_TYPE_STABLE;
777
778     p_uit->file.i_type = UPDATE_FILE_TYPE_NONE;
779     p_uit->file.psz_md5 = NULL;
780     p_uit->file.psz_url = NULL;
781     p_uit->file.l_size = 0;
782     p_uit->file.psz_description = NULL;
783
784     p_uit->release.psz_version = NULL;
785     p_uit->release.psz_svn_revision = NULL;
786     p_uit->release.i_type = UPDATE_RELEASE_TYPE_UNSTABLE;
787     p_uit->release.i_status = UPDATE_RELEASE_STATUS_NONE;
788
789     p_uit->mirror.psz_name = NULL;
790     p_uit->mirror.psz_location = NULL;
791     p_uit->mirror.psz_type = NULL;
792
793     return p_uit;
794 }
795
796 /**
797  * Delete an update iterator structure (duh!)
798  *
799  * \param p_uit pointer to an update iterator
800  * \return nothing
801  */
802 void update_iterator_Delete( update_iterator_t *p_uit )
803 {
804     if( !p_uit ) return;
805     update_iterator_ClearData( p_uit );
806     free( p_uit );
807 }
808
809 /**
810  * Reset an update_iterator_t structure
811  *
812  * \param p_uit pointer to an update iterator
813  * \return UPDATE_FAIL upon error, UPDATE_SUCCESS otherwise
814  */
815 unsigned int update_iterator_Reset( update_iterator_t *p_uit )
816 {
817     if( !p_uit ) return UPDATE_FAIL;
818
819     p_uit->i_r = -1;
820     p_uit->i_f = -1;
821     p_uit->i_m = -1;
822
823     update_iterator_ClearData( p_uit );
824     return UPDATE_SUCCESS;
825 }
826
827 /**
828  * Finds the next file in the update tree that matches status and type
829  * requirements set in the update_iterator
830  *
831  * \param p_uit update iterator
832  * \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
833  */
834 unsigned int update_iterator_NextFile( update_iterator_t *p_uit )
835 {
836     int r,f=-1,old_r;
837
838     if( !p_uit ) return UPDATE_FAIL;
839
840     old_r=p_uit->i_r;
841
842     /* if the update iterator was already in a "no match" state, start over */
843     if( p_uit->i_r == -1 ) p_uit->i_r = 0;
844     //if( p_uit->i_f == -1 ) p_uit->i_f = 0;
845
846     vlc_mutex_lock( &p_uit->p_u->lock );
847
848     for( r = p_uit->i_r; r < p_uit->p_u->i_releases; r++ )
849     {
850         if( !( p_uit->p_u->p_releases[r].i_status & p_uit->i_rs ) ) continue;
851         for( f = ( r == p_uit->i_r ? p_uit->i_f + 1 : 0 );
852              f < p_uit->p_u->p_releases[r].i_files; f++ )
853         {
854             if( p_uit->p_u->p_releases[r].p_files[f].i_type & p_uit->i_t )
855             {
856                 goto done;/* "double break" */
857             }
858         }
859     }
860     done:
861     p_uit->i_r = r;
862     p_uit->i_f = f;
863
864     r = p_uit->p_u->i_releases;
865
866     if( old_r == p_uit->i_r )
867     {
868         update_iterator_GetData( p_uit );
869         vlc_mutex_unlock( &p_uit->p_u->lock );
870         return UPDATE_SUCCESS|UPDATE_FILE;
871     }
872     else if( p_uit->i_r == r )
873     {
874         p_uit->i_r = -1;
875         p_uit->i_f = -1;
876         update_iterator_GetData( p_uit );
877         vlc_mutex_unlock( &p_uit->p_u->lock );
878         return UPDATE_FAIL;
879     }
880     else
881     {
882         update_iterator_GetData( p_uit );
883         vlc_mutex_unlock( &p_uit->p_u->lock );
884         return UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE;
885     }
886 }
887
888 /**
889  * Finds the previous file in the update tree that matches status and type
890  * requirements set in the update_iterator
891  *
892  * \param p_uit update iterator
893  * \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
894  */
895 //TODO: test
896 unsigned int update_iterator_PrevFile( update_iterator_t *p_uit )
897 {
898     int r,f=-1,old_r;
899
900     if( !p_uit ) return UPDATE_FAIL;
901
902     old_r=p_uit->i_r;
903
904     /* if the update iterator was already in a "no match" state, start over
905      * (begin at the end of the list) */
906     if( p_uit->i_r == -1 ) p_uit->i_r = p_uit->p_u->i_releases - 1;
907     p_uit->i_f = p_uit->p_u->p_releases[p_uit->i_r].i_files + 1;
908
909     vlc_mutex_lock( &p_uit->p_u->lock );
910
911     for( r = p_uit->i_r; r >= 0; r-- )
912     {
913         if( !( p_uit->p_u->p_releases[r].i_status & p_uit->i_rs ) ) continue;
914         for( f =( r==p_uit->i_r ? p_uit->i_f - 1 : p_uit->p_u->p_releases[r].i_files );
915              f >= 0; f-- )
916         {
917             if( p_uit->p_u->p_releases[r].p_files[f].i_type & p_uit->i_t )
918             {
919                 goto done;/* "double break" */
920             }
921         }
922     }
923     done:
924     p_uit->i_r = r;
925     p_uit->i_f = f;
926
927     r = p_uit->p_u->i_releases;
928
929     if( old_r == p_uit->i_r )
930     {
931         update_iterator_GetData( p_uit );
932         vlc_mutex_unlock( &p_uit->p_u->lock );
933         return UPDATE_SUCCESS|UPDATE_FILE;
934     }
935     else if( p_uit->i_r == -1 )
936     {
937         p_uit->i_r = -1;
938         p_uit->i_f = -1;
939         update_iterator_GetData( p_uit );
940         vlc_mutex_unlock( &p_uit->p_u->lock );
941         return UPDATE_FAIL;
942     }
943     else
944     {
945         update_iterator_GetData( p_uit );
946         vlc_mutex_unlock( &p_uit->p_u->lock );
947         return UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE;
948     }
949 }
950
951 /**
952  * Finds the next mirror in the update tree
953  *
954  * \param update iterator
955  * \return UPDATE_FAIL if we can't find the next mirror, UPDATE_SUCCESS|UPDATE_MIRROR otherwise
956  */
957 unsigned int update_iterator_NextMirror( update_iterator_t *p_uit )
958 {
959     if( !p_uit ) return UPDATE_FAIL;
960     vlc_mutex_lock( &p_uit->p_u->lock );
961     p_uit->i_m++;
962     if( p_uit->i_m >= p_uit->p_u->i_mirrors ) p_uit->i_m = -1;
963     update_iterator_GetData( p_uit );
964     vlc_mutex_unlock( &p_uit->p_u->lock );
965     return p_uit->i_m == -1 ? UPDATE_FAIL : UPDATE_SUCCESS|UPDATE_MIRROR;
966 }
967
968 /**
969  * Finds the previous mirror in the update tree
970  *
971  * \param update iterator
972  * \return UPDATE_FAIL if we can't find a previous mirror, UPDATE_SUCCESS|UPDATE_MIRROR otherwise
973  */
974 unsigned int update_iterator_PrevMirror( update_iterator_t *p_uit )
975 {
976     if( !p_uit ) return UPDATE_FAIL;
977     vlc_mutex_lock( &p_uit->p_u->lock );
978     p_uit->i_m--;
979     update_iterator_GetData( p_uit );
980     vlc_mutex_unlock( &p_uit->p_u->lock );
981     return p_uit->i_m == -1 ? UPDATE_FAIL : UPDATE_SUCCESS|UPDATE_MIRROR;
982 }
983
984 /**
985  * Change the update iterator's position in the file and mirrors tree
986  * If position is negative, don't change it
987  *
988  * \param i_m position in mirrors list
989  * \param i_r position in releases list
990  * \param i_f position in release's files list
991  * \return UPDATE_FAIL when changing position fails or position wasn't changed, a combination of UPDATE_MIRROR, UPDATE_RELEASE and UPDATE_FILE otherwise
992  */
993 unsigned int update_iterator_ChooseMirrorAndFile( update_iterator_t *p_uit,
994                                         int i_m, int i_r, int i_f )
995 {
996     unsigned int i_val = 0;
997
998     if( !p_uit ) return 0;
999     vlc_mutex_lock( &p_uit->p_u->lock );
1000
1001     if( i_m >= 0 )
1002     {
1003         if( i_m < p_uit->p_u->i_mirrors )
1004         {
1005             if( i_m != p_uit->i_m )
1006                 i_val |= UPDATE_MIRROR;
1007             p_uit->i_m = i_m;
1008         }
1009         else i_m = -1;
1010     }
1011
1012     if( i_r >= 0 )
1013     {
1014         if( i_r < p_uit->p_u->i_releases )
1015         {
1016             if( i_r != p_uit->i_r )
1017                 i_val |= UPDATE_FILE;
1018             p_uit->i_r = i_r;
1019         }
1020         else i_r = -1;
1021     }
1022
1023     if( i_f >= 0 )
1024     {
1025         if( i_r >= 0 && i_r < p_uit->p_u->i_releases
1026             && i_f < p_uit->p_u->p_releases[p_uit->i_r].i_files )
1027         {
1028             if( i_f != p_uit->i_f )
1029                 i_val |= UPDATE_FILE;
1030             p_uit->i_f = i_f;
1031         }
1032         else i_f = -1;
1033     }
1034
1035     update_iterator_GetData( p_uit );
1036     vlc_mutex_unlock( &p_uit->p_u->lock );
1037
1038     if(    ( i_m < 0 || p_uit->i_m >= 0 )
1039         && ( i_r < 0 || p_uit->i_r >= 0 )
1040         && ( i_f < 0 || p_uit->i_f >= 0 ) )
1041     {
1042         /* Everything worked */
1043         return UPDATE_SUCCESS|i_val;
1044     }
1045     else
1046     {
1047         /* Something failed */
1048         return UPDATE_FAIL;
1049     }
1050 }
1051
1052 /**
1053  * Fills the iterator data (file, release and mirror structs)
1054  * The update struct should be locked before calling this function.
1055  *
1056  * \param p_uit update iterator
1057  * \return nothing
1058  */
1059 void update_iterator_GetData( update_iterator_t *p_uit )
1060 {
1061     struct update_release_t *p_r = NULL;
1062     struct update_file_t *p_f = NULL;
1063     struct update_mirror_t *p_m = NULL;
1064
1065     update_iterator_ClearData( p_uit );
1066
1067     if( p_uit->i_m >= 0 )
1068     {
1069         p_m = p_uit->p_u->p_mirrors + p_uit->i_m;
1070         p_uit->mirror.psz_name = STRDUP( p_m->psz_name );
1071         p_uit->mirror.psz_location = STRDUP( p_m->psz_location );
1072         p_uit->mirror.psz_type = STRDUP( p_m->psz_type );
1073     }
1074
1075     if( p_uit->i_r >= 0 )
1076     {
1077         p_r = p_uit->p_u->p_releases + p_uit->i_r;
1078         asprintf( &p_uit->release.psz_version, "%s.%s.%s-%s",
1079                                               p_r->psz_major,
1080                                               p_r->psz_minor,
1081                                               p_r->psz_revision,
1082                                               p_r->psz_extra );
1083         p_uit->release.psz_svn_revision = STRDUP( p_r->psz_svn_revision );
1084         p_uit->release.i_type = p_r->i_type;
1085         p_uit->release.i_status = p_r->i_status;
1086         if( p_uit->i_f >= 0 )
1087         {
1088             p_f = p_r->p_files + p_uit->i_f;
1089             p_uit->file.i_type = p_f->i_type;
1090             p_uit->file.psz_md5 = STRDUP( p_f->psz_md5 );
1091             p_uit->file.l_size = p_f->l_size;
1092             p_uit->file.psz_description = STRDUP( p_f->psz_description);
1093             if( p_f->psz_url[0] == '/' )
1094             {
1095                 if( p_m )
1096                 {
1097                     asprintf( &p_uit->file.psz_url, "%s%s",
1098                               p_m->psz_base_url, p_f->psz_url );
1099                 }
1100             }
1101             else
1102             {
1103                 p_uit->file.psz_url = STRDUP( p_f->psz_url );
1104             }
1105         }
1106     }
1107 }
1108
1109 /**
1110  * Clears the iterator data (file, release and mirror structs)
1111  *
1112  * \param p_uit update iterator
1113  * \return nothing
1114  */
1115 void update_iterator_ClearData( update_iterator_t *p_uit )
1116 {
1117     p_uit->file.i_type = UPDATE_FILE_TYPE_NONE;
1118     FREE( p_uit->file.psz_md5 );
1119     p_uit->file.l_size = 0;
1120     FREE( p_uit->file.psz_description );
1121     FREE( p_uit->file.psz_url );
1122     FREE( p_uit->release.psz_version );
1123     FREE( p_uit->release.psz_svn_revision );
1124     p_uit->release.i_type = UPDATE_RELEASE_TYPE_UNSTABLE;
1125     p_uit->release.i_status = UPDATE_RELEASE_STATUS_NONE;
1126     FREE( p_uit->mirror.psz_name );
1127     FREE( p_uit->mirror.psz_location );
1128     FREE( p_uit->mirror.psz_type );
1129 }
1130
1131 /**
1132  * Perform an action on the update iterator
1133  * Only the first matching action is performed.
1134  *
1135  * \param p_uit update iterator
1136  * \param i_action update action bitmask. can be a combination of UPDATE_NEXT, UPDATE_PREV, UPDATE_MIRROR, UPDATE_RELEASE, UPDATE_FILE, UPDATE_RESET
1137  * \return UPDATE_FAIL if action fails, UPDATE_SUCCESS|(combination of UPDATE_MIRROR, UPDATE_RELEASE and UPDATE_FILE if these changed) otherwise
1138  */
1139 unsigned int update_iterator_Action( update_iterator_t *p_uit, int i_action )
1140 {
1141     if( i_action & UPDATE_RESET )
1142     {
1143         return update_iterator_Reset( p_uit );
1144     }
1145     else
1146     if( i_action & UPDATE_MIRROR )
1147     {
1148         if( i_action & UPDATE_PREV )
1149         {
1150             return update_iterator_PrevMirror( p_uit );
1151         }
1152         else
1153         {
1154             return update_iterator_NextMirror( p_uit );
1155         }
1156     }
1157     /*else if( i_action & UPDATE_RELEASE )
1158     {
1159         if( i_action & UPDATE_PREV )
1160         {
1161             return update_iterator_PrevRelease( p_uit );
1162         }
1163         else
1164         {
1165             return update_iterator_NextRelease( p_uit );
1166         }
1167     }*/
1168     else if( i_action & UPDATE_FILE )
1169     {
1170         if( i_action & UPDATE_PREV )
1171         {
1172             return update_iterator_PrevFile( p_uit );
1173         }
1174         else
1175         {
1176             return update_iterator_NextFile( p_uit );
1177         }
1178     }
1179     else
1180     {
1181         return UPDATE_SUCCESS;
1182     }
1183 }
1184
1185 /**
1186  * Object to launch download thread in a different object
1187  */
1188 typedef struct {
1189     VLC_COMMON_MEMBERS
1190     char *psz_dest;     //< Download destination
1191     char *psz_src;      //< Download source
1192     char *psz_status;   //< Download status displayed in progress dialog
1193 } download_thread_t;
1194
1195 void update_download_for_real( download_thread_t *p_this );
1196
1197 /**
1198  * Download the file selected by the update iterator. This function will
1199  * launch the download in a new thread (downloads can be long)
1200  *
1201  * \param p_uit update iterator
1202  * \param psz_dest destination file path
1203  * \return nothing
1204  */
1205 void update_download( update_iterator_t *p_uit, char *psz_dest )
1206 {
1207     download_thread_t *p_dt =
1208         vlc_object_create( p_uit->p_u->p_vlc, sizeof( download_thread_t ) );
1209
1210     p_dt->psz_dest = strdup( psz_dest );
1211     p_dt->psz_src = strdup( p_uit->file.psz_url );
1212     asprintf( &p_dt->psz_status, "%s - %s (%s)\nSource: %s\nDestination: %s",
1213               p_uit->file.psz_description, p_uit->release.psz_version,
1214               p_uit->release.psz_svn_revision, p_uit->file.psz_url,
1215               psz_dest);
1216
1217     vlc_thread_create( p_dt, "download thread", update_download_for_real,
1218                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1219 }
1220
1221 /**
1222  * Convert a long int size in bytes to a string
1223  *
1224  * \param l_size the size in bytes
1225  * \return the size as a string
1226  */
1227 char *size_str( long int l_size )
1228 {
1229     char *psz_tmp;
1230     if( l_size>> 30 )
1231         asprintf( &psz_tmp, "%.1f GB", (float)l_size/(1<<30) );
1232     if( l_size >> 20 )
1233         asprintf( &psz_tmp, "%.1f MB", (float)l_size/(1<<20) );
1234     else if( l_size >> 10 )
1235         asprintf( &psz_tmp, "%.1f kB", (float)l_size/(1<<10) );
1236     else
1237         asprintf( &psz_tmp, "%ld B", l_size );
1238     return psz_tmp;
1239 }
1240
1241 /**
1242  * The true download function.
1243  *
1244  * \param p_this the download_thread_t object
1245  * \return nothing
1246  */
1247 void update_download_for_real( download_thread_t *p_this )
1248 {
1249     char *psz_dest = p_this->psz_dest;
1250     char *psz_src = p_this->psz_src;
1251     stream_t *p_stream;
1252     vlc_t *p_vlc = p_this->p_vlc;
1253
1254     FILE *p_file = NULL;
1255     void *p_buffer;
1256
1257     char *psz_status;
1258
1259     int i_progress;
1260     long int l_size, l_done = 0;
1261
1262     vlc_thread_ready( p_this );
1263
1264     asprintf( &psz_status, "%s\nDownloading... 0.0/? %.1f%% done",
1265               p_this->psz_status, 0.0 );
1266     i_progress = intf_UserProgress( p_vlc, "Downloading...",
1267                                     psz_status, 0.0 );
1268
1269     p_stream = stream_UrlNew( p_vlc, psz_src );
1270     if( !p_stream )
1271     {
1272         msg_Err( p_vlc, "Failed to open %s for reading", psz_src );
1273         intf_UserFatal( p_vlc, "Downloading...",
1274                         "Failed to open %s for reading", psz_src );
1275         intf_UserHide( p_vlc, i_progress );
1276     }
1277     else
1278     {
1279
1280         p_file = utf8_fopen( psz_dest, "w" );
1281         if( !p_file )
1282         {
1283             msg_Err( p_vlc, "Failed to open %s for writing", psz_dest );
1284             intf_UserFatal( p_vlc, "Downloading...",
1285                             "Failed to open %s for writing", psz_dest );
1286             intf_UserHide( p_vlc, i_progress );
1287         }
1288         else
1289         {
1290             long int l_read;
1291             char *psz_s1; char *psz_s2;
1292
1293             l_size = stream_Size(p_stream);
1294             p_buffer = (void *)malloc( 1<<10 );
1295
1296             while( ( l_read = stream_Read( p_stream, p_buffer, 1<<10 ) ) )
1297             {
1298                 float f_progress;
1299
1300                 fwrite( p_buffer, l_read, 1, p_file );
1301
1302                 l_done += l_read;
1303                 free( psz_status );
1304                 f_progress = 100.0*(float)l_done/(float)l_size;
1305                 psz_s1 = size_str( l_done );
1306                 psz_s2 = size_str( l_size );
1307                 asprintf( &psz_status, "%s\nDownloading... %s/%s (%.1f%%) done",
1308                            p_this->psz_status, psz_s1, psz_s2, f_progress );
1309                 free( psz_s1 ); free( psz_s2 );
1310
1311                 intf_UserProgressUpdate( p_vlc, i_progress,
1312                                          psz_status, f_progress );
1313             }
1314
1315             free( p_buffer );
1316             fclose( p_file );
1317             stream_Delete( p_stream );
1318
1319             free( psz_status );
1320             psz_s2 = size_str( l_size );
1321             asprintf( &psz_status, "%s\nDone %s (100.00%%)",
1322                        p_this->psz_status, psz_s2 );
1323             free( psz_s2 );
1324             intf_UserProgressUpdate( p_vlc, i_progress, psz_status, 100.0 );
1325             free( psz_status );
1326         }
1327     }
1328
1329     free( p_this->psz_dest );
1330     free( p_this->psz_src );
1331     free( p_this->psz_status );
1332
1333 #ifdef WIN32
1334     CloseHandle( p_this->thread_id );
1335 #endif
1336
1337     vlc_object_destroy( p_this );
1338 }