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