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