]> git.sesse.net Git - vlc/blob - src/misc/update.c
Use a hash for stats. Not finished
[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     tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
402
403     vlc_bool_t b_os = VLC_FALSE, b_arch = VLC_FALSE;
404
405     vlc_mutex_lock( &p_update->lock );
406
407     if( p_update->b_releases && b_force == VLC_FALSE )
408     {
409         vlc_mutex_unlock( &p_update->lock );
410         return;
411     }
412
413     p_xml = xml_Create( p_update->p_vlc );
414     if( !p_xml )
415     {
416         msg_Err( p_update->p_vlc, "Failed to open XML parser" );
417         goto error;
418     }
419
420     p_stream = stream_UrlNew( p_update->p_vlc, UPDATE_VLC_STATUS_URL );
421     if( !p_stream )
422     {
423         msg_Err( p_update->p_vlc, "Failed to open %s for reading",
424                  UPDATE_VLC_STATUS_URL );
425         goto error;
426     }
427
428     p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
429
430     if( !p_xml_reader )
431     {
432         msg_Err( p_update->p_vlc, "Failed to open %s for parsing",
433                  UPDATE_VLC_STATUS_URL );
434         goto error;
435     }
436
437     if( p_update->p_releases )
438     {
439         FreeReleasesList( p_update );
440     }
441
442     while( xml_ReaderRead( p_xml_reader ) == 1 )
443     {
444         switch( xml_ReaderNodeType( p_xml_reader ) )
445         {
446             case -1:
447                 msg_Err( p_update->p_vlc, "Error while parsing %s",
448                          UPDATE_VLC_STATUS_URL );
449                 goto error;
450
451             case XML_READER_STARTELEM:
452                 psz_eltname = xml_ReaderName( p_xml_reader );
453                 if( !psz_eltname )
454                 {
455                     msg_Err( p_update->p_vlc, "Error while parsing %s",
456                              UPDATE_VLC_STATUS_URL );
457                     goto error;
458                 }
459
460                 while( xml_ReaderNextAttr( p_xml_reader ) == VLC_SUCCESS )
461                 {
462                     psz_name = xml_ReaderName( p_xml_reader );
463                     psz_value = xml_ReaderValue( p_xml_reader );
464
465                     if( !psz_name || !psz_value )
466                     {
467                         msg_Err( p_update->p_vlc, "Error while parsing %s",
468                                  UPDATE_VLC_STATUS_URL );
469                         goto error;
470                     }
471
472                     if( b_os && b_arch )
473                     {
474                         if( strcmp( psz_eltname, "version" ) == 0 )
475                         {
476                             if( !strcmp( psz_name, "major" ) )
477                                 tmp_release.psz_major = STRDUP( psz_value );
478                             else if( !strcmp( psz_name, "minor" ) )
479                                 tmp_release.psz_minor = STRDUP( psz_value );
480                             else if( !strcmp( psz_name, "revision" ) )
481                                 tmp_release.psz_revision = STRDUP( psz_value );
482                             else if( !strcmp( psz_name, "extra" ) )
483                                 tmp_release.psz_extra = STRDUP( psz_value );
484                             else if( !strcmp( psz_name, "svn" ) )
485                                 tmp_release.psz_svn_revision =
486                                                            STRDUP( psz_value );
487                             else if( !strcmp( psz_name, "version" ) )
488                             {
489                                 if( !strcmp( psz_value, "unstable" ) )
490                                     tmp_release.i_type =
491                                                   UPDATE_RELEASE_TYPE_UNSTABLE;
492                                 else if( !strcmp( psz_value, "testing" ) )
493                                     tmp_release.i_type =
494                                                   UPDATE_RELEASE_TYPE_TESTING;
495                                 else
496                                     tmp_release.i_type =
497                                                   UPDATE_RELEASE_TYPE_STABLE;
498                             }
499                         }
500                         else if( !strcmp( psz_eltname, "file" ) )
501                         {
502                             if( !strcmp( psz_name, "type" ) )
503                             {
504                                 if( !strcmp( psz_value, "info" ) )
505                                     tmp_file.i_type = UPDATE_FILE_TYPE_INFO;
506                                 else if( !strcmp( psz_value, "source" ) )
507                                     tmp_file.i_type = UPDATE_FILE_TYPE_SOURCE;
508                                 else if( !strcmp( psz_value, "binary" ) )
509                                     tmp_file.i_type = UPDATE_FILE_TYPE_BINARY;
510                                 else if( !strcmp( psz_value, "plugin" ) )
511                                     tmp_file.i_type = UPDATE_FILE_TYPE_PLUGIN;
512                                 else
513                                     tmp_file.i_type = UPDATE_FILE_TYPE_UNDEF;
514                             }
515                             else if( !strcmp( psz_name, "md5" ) )
516                                 tmp_file.psz_md5 = STRDUP( psz_value );
517                             else if( !strcmp( psz_name, "size" ) )
518                                 tmp_file.l_size = atol( psz_value );
519                             else if( !strcmp( psz_name, "url" ) )
520                                 tmp_file.psz_url = STRDUP( psz_value );
521                         }
522                     }
523                     if( !strcmp( psz_name, "name" )
524                         && ( !strcmp( psz_value, UPDATE_VLC_OS )
525                            || !strcmp( psz_value, "*" ) )
526                         && !strcmp( psz_eltname, "os" ) )
527                     {
528                         b_os = VLC_TRUE;
529                     }
530                     if( b_os && !strcmp( psz_name, "name" )
531                         && ( !strcmp( psz_value, UPDATE_VLC_ARCH )
532                            || !strcmp( psz_value, "*" ) )
533                         && !strcmp( psz_eltname, "arch" ) )
534                     {
535                         b_arch = VLC_TRUE;
536                     }
537                     FREE( psz_name );
538                     FREE( psz_value );
539                 }
540                 if( ( b_os && b_arch && strcmp( psz_eltname, "arch" ) ) )
541                 {
542                     if( !strcmp( psz_eltname, "version" ) )
543                     {
544                         int i;
545                         /* look for a previous occurence of this release */
546                         for( i = 0; i < p_update->i_releases; i++ )
547                         {
548                             p_release = p_update->p_releases + i;
549                             if( CompareReleases( p_release, &tmp_release )
550                                 == UPDATE_RELEASE_STATUS_EQUAL )
551                             {
552                                 break;
553                             }
554                         }
555                         /* if this is the first time that we see this release,
556                          * append it to the list of releases */
557                         if( i == p_update->i_releases )
558                         {
559                             tmp_release.i_status =
560                                 CompareReleaseToCurrent( &tmp_release );
561                             p_update->p_releases =
562                (struct update_release_t *)realloc( p_update->p_releases,
563                (++(p_update->i_releases))*sizeof( struct update_release_t ) );
564                             p_update->p_releases[ p_update->i_releases - 1 ] =
565                                 tmp_release;
566                             p_release =
567                                 p_update->p_releases + p_update->i_releases - 1;
568                             tmp_release.psz_major = NULL;
569                             tmp_release.psz_minor = NULL;
570                             tmp_release.psz_revision = NULL;
571                             tmp_release.psz_extra = NULL;
572                             tmp_release.psz_svn_revision = NULL;
573                             tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
574                             tmp_release.i_status = 0;
575                             tmp_release.p_files = NULL;
576                             tmp_release.i_files = 0;
577                         }
578                         else
579                         {
580                             FREE( tmp_release.psz_major );
581                             FREE( tmp_release.psz_minor );
582                             FREE( tmp_release.psz_revision );
583                             FREE( tmp_release.psz_extra );
584                             FREE( tmp_release.psz_svn_revision );
585                             tmp_release.i_type = UPDATE_RELEASE_TYPE_STABLE;
586                             FREE( tmp_release.p_files );
587                             tmp_release.i_files = 0;
588                         }
589                     }
590                     else if( !strcmp( psz_eltname, "file" ) )
591                     {
592                         /* append file to p_release's file list */
593                         if( p_release == NULL )
594                         {
595                             goto error;
596                         }
597                         p_release->p_files =
598                     (struct update_file_t *)realloc( p_release->p_files,
599                     (++(p_release->i_files))*sizeof( struct update_file_t ) );
600                         p_release->p_files[ p_release->i_files - 1 ] = tmp_file;
601                         tmp_file.i_type = UPDATE_FILE_TYPE_UNDEF;
602                         tmp_file.psz_md5 = NULL;
603                         tmp_file.l_size = 0;
604                         tmp_file.psz_url = NULL;
605                         tmp_file.psz_description = NULL;
606                     }
607                 }
608                 FREE( psz_eltname );
609                 break;
610
611             case XML_READER_ENDELEM:
612                 psz_eltname = xml_ReaderName( p_xml_reader );
613                 if( !psz_eltname )
614                 {
615                     msg_Err( p_update->p_vlc, "Error while parsing %s",
616                              UPDATE_VLC_STATUS_URL );
617                     goto error;
618                 }
619
620                 if( !strcmp( psz_eltname, "os" ) )
621                     b_os = VLC_FALSE;
622                 else if( !strcmp( psz_eltname, "arch" ) )
623                     b_arch = VLC_FALSE;
624                 FREE( psz_eltname );
625                 break;
626
627             case XML_READER_TEXT:
628                 psz_eltvalue = xml_ReaderValue( p_xml_reader );
629                 if( p_release && p_release->i_files )
630                     p_release->p_files[ p_release->i_files - 1 ]
631                                .psz_description = STRDUP( psz_eltvalue );
632                 FREE( psz_eltvalue );
633                 break;
634         }
635     }
636
637     p_update->b_releases = VLC_TRUE;
638
639     error:
640         vlc_mutex_unlock( &p_update->lock );
641
642         free( psz_eltname );
643         free( psz_eltvalue );
644         free( psz_name );
645         free( psz_value );
646
647         free( tmp_release.psz_major );
648         free( tmp_release.psz_minor );
649         free( tmp_release.psz_revision );
650         free( tmp_release.psz_extra );
651         free( tmp_release.psz_svn_revision );
652
653         free( tmp_file.psz_md5 );
654         free( tmp_file.psz_url );
655         free( tmp_file.psz_description );
656
657         if( p_xml_reader && p_xml )
658             xml_ReaderDelete( p_xml, p_xml_reader );
659         if( p_stream )
660             stream_Delete( p_stream );
661         if( p_xml )
662             xml_Delete( p_xml );
663 }
664
665 /**
666  * Check for updates
667  *
668  * \param p_update pointer to update struct
669  * \param b_force set to VLC_TRUE if you want to force the update
670  * \returns nothing
671  */
672 void update_Check( update_t *p_update, vlc_bool_t b_force )
673 {
674     if( p_update == NULL ) return;
675     GetMirrorsList( p_update, b_force );
676     GetFilesList( p_update, b_force );
677 }
678
679 /**
680  * Compare two release numbers
681  * The comparision algorith basically performs an alphabetical order (strcmp)
682  * comparision of each of the version number elements until it finds two
683  * different ones. This is the tricky function.
684  *
685  * \param p1 first release
686  * \param p2 second release
687  * \return like strcmp
688  */
689 int CompareReleases( struct update_release_t *p1, struct update_release_t *p2 )
690 {
691     int d;
692     if( ( d = strcmp( p1->psz_major, p2->psz_major ) ) ) ;
693     else if( ( d = strcmp( p1->psz_minor, p2->psz_minor ) ) ) ;
694     else if( ( d = strcmp( p1->psz_revision, p2->psz_revision ) ) ) ;
695     else
696     {
697         d = strcmp( p1->psz_extra, p2->psz_extra );
698         if( d<0 )
699         {
700         /* FIXME:
701          * not num < NULL < num
702          * -test and -svn releases are thus always considered older than
703          * -'' or -0 releases, which is the best i could come up with */
704             char *psz_end1;
705             char *psz_end2;
706             strtol( p1->psz_extra, &psz_end1, 10 );
707             strtol( p2->psz_extra, &psz_end2, 10 );
708             if( psz_end2 == p2->psz_extra
709              && ( psz_end1 != p1->psz_extra || *p1->psz_extra == '\0' ) )
710                 d = 1;
711         }
712     }
713     if( d < 0 )
714         return UPDATE_RELEASE_STATUS_OLDER;
715     else if( d == 0 )
716         return UPDATE_RELEASE_STATUS_EQUAL;
717     else
718         return UPDATE_RELEASE_STATUS_NEWER;
719 }
720
721 /**
722  * Compare a given release's version number to the current VLC's one
723  *
724  * \param p a release
725  * \return >0 if newer, 0 if equal and <0 if older
726  */
727 int CompareReleaseToCurrent( struct update_release_t *p )
728 {
729     struct update_release_t c = {0};
730     int r;
731     c.psz_major = STRDUP( PACKAGE_VERSION_MAJOR );
732     c.psz_minor = STRDUP( PACKAGE_VERSION_MINOR );
733     c.psz_revision = STRDUP( PACKAGE_VERSION_REVISION );
734     c.psz_extra = STRDUP( PACKAGE_VERSION_EXTRA );
735     r =  CompareReleases( p, &c );
736     free( c.psz_major );
737     free( c.psz_minor );
738     free( c.psz_revision );
739     free( c.psz_extra );
740     return r;
741 }
742
743 /*****************************************************************************
744  * Updatei_iterator_t functions
745  *****************************************************************************/
746
747 /**
748  * Create a new update iterator structure. This structure can then be used to
749  * describe a position and move through the update and mirror trees/lists.
750  * This will use an existing update struct or create a new one if none is
751  * found
752  *
753  * \param p_u the calling update_t
754  * \return a pointer to an update iterator
755  */
756 update_iterator_t *update_iterator_New( update_t *p_u )
757 {
758     update_iterator_t *p_uit = NULL;
759
760     if( p_u == NULL )
761         return NULL;
762
763     p_uit = (update_iterator_t *)malloc( sizeof( update_iterator_t ) );
764     if( p_uit == NULL ) return NULL;
765
766     p_uit->p_u = p_u;
767
768     p_uit->i_m = -1;
769     p_uit->i_r = -1;
770     p_uit->i_f = -1;
771
772     p_uit->i_t = UPDATE_FILE_TYPE_ALL;
773     p_uit->i_rs = UPDATE_RELEASE_STATUS_ALL;
774     p_uit->i_rt = UPDATE_RELEASE_TYPE_STABLE;
775
776     p_uit->file.i_type = UPDATE_FILE_TYPE_NONE;
777     p_uit->file.psz_md5 = NULL;
778     p_uit->file.psz_url = NULL;
779     p_uit->file.l_size = 0;
780     p_uit->file.psz_description = NULL;
781
782     p_uit->release.psz_version = NULL;
783     p_uit->release.psz_svn_revision = NULL;
784     p_uit->release.i_type = UPDATE_RELEASE_TYPE_UNSTABLE;
785     p_uit->release.i_status = UPDATE_RELEASE_STATUS_NONE;
786
787     p_uit->mirror.psz_name = NULL;
788     p_uit->mirror.psz_location = NULL;
789     p_uit->mirror.psz_type = NULL;
790
791     return p_uit;
792 }
793
794 /**
795  * Delete an update iterator structure (duh!)
796  *
797  * \param p_uit pointer to an update iterator
798  * \return nothing
799  */
800 void update_iterator_Delete( update_iterator_t *p_uit )
801 {
802     if( !p_uit ) return;
803     update_iterator_ClearData( p_uit );
804     free( p_uit );
805 }
806
807 /**
808  * Reset an update_iterator_t structure
809  *
810  * \param p_uit pointer to an update iterator
811  * \return UPDATE_FAIL upon error, UPDATE_SUCCESS otherwise
812  */
813 unsigned int update_iterator_Reset( update_iterator_t *p_uit )
814 {
815     if( !p_uit ) return UPDATE_FAIL;
816
817     p_uit->i_r = -1;
818     p_uit->i_f = -1;
819     p_uit->i_m = -1;
820
821     update_iterator_ClearData( p_uit );
822     return UPDATE_SUCCESS;
823 }
824
825 /**
826  * Finds the next file in the update tree that matches status and type
827  * requirements set in the update_iterator
828  *
829  * \param p_uit update iterator
830  * \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
831  */
832 unsigned int update_iterator_NextFile( update_iterator_t *p_uit )
833 {
834     int r,f=-1,old_r;
835
836     if( !p_uit ) return UPDATE_FAIL;
837
838     old_r=p_uit->i_r;
839
840     /* if the update iterator was already in a "no match" state, start over */
841     if( p_uit->i_r == -1 ) p_uit->i_r = 0;
842     //if( p_uit->i_f == -1 ) p_uit->i_f = 0;
843
844     vlc_mutex_lock( &p_uit->p_u->lock );
845
846     for( r = p_uit->i_r; r < p_uit->p_u->i_releases; r++ )
847     {
848         if( !( p_uit->p_u->p_releases[r].i_status & p_uit->i_rs ) ) continue;
849         for( f = ( r == p_uit->i_r ? p_uit->i_f + 1 : 0 );
850              f < p_uit->p_u->p_releases[r].i_files; f++ )
851         {
852             if( p_uit->p_u->p_releases[r].p_files[f].i_type & p_uit->i_t )
853             {
854                 goto done;/* "double break" */
855             }
856         }
857     }
858     done:
859     p_uit->i_r = r;
860     p_uit->i_f = f;
861
862     r = p_uit->p_u->i_releases;
863
864     if( old_r == p_uit->i_r )
865     {
866         update_iterator_GetData( p_uit );
867         vlc_mutex_unlock( &p_uit->p_u->lock );
868         return UPDATE_SUCCESS|UPDATE_FILE;
869     }
870     else if( p_uit->i_r == r )
871     {
872         p_uit->i_r = -1;
873         p_uit->i_f = -1;
874         update_iterator_GetData( p_uit );
875         vlc_mutex_unlock( &p_uit->p_u->lock );
876         return UPDATE_FAIL;
877     }
878     else
879     {
880         update_iterator_GetData( p_uit );
881         vlc_mutex_unlock( &p_uit->p_u->lock );
882         return UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE;
883     }
884 }
885
886 /**
887  * Finds the previous file in the update tree that matches status and type
888  * requirements set in the update_iterator
889  *
890  * \param p_uit update iterator
891  * \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
892  */
893 //TODO: test
894 unsigned int update_iterator_PrevFile( update_iterator_t *p_uit )
895 {
896     int r,f=-1,old_r;
897
898     if( !p_uit ) return UPDATE_FAIL;
899
900     old_r=p_uit->i_r;
901
902     /* if the update iterator was already in a "no match" state, start over
903      * (begin at the end of the list) */
904     if( p_uit->i_r == -1 ) p_uit->i_r = p_uit->p_u->i_releases - 1;
905     p_uit->i_f = p_uit->p_u->p_releases[p_uit->i_r].i_files + 1;
906
907     vlc_mutex_lock( &p_uit->p_u->lock );
908
909     for( r = p_uit->i_r; r >= 0; r-- )
910     {
911         if( !( p_uit->p_u->p_releases[r].i_status & p_uit->i_rs ) ) continue;
912         for( f =( r==p_uit->i_r ? p_uit->i_f - 1 : p_uit->p_u->p_releases[r].i_files );
913              f >= 0; f-- )
914         {
915             if( p_uit->p_u->p_releases[r].p_files[f].i_type & p_uit->i_t )
916             {
917                 goto done;/* "double break" */
918             }
919         }
920     }
921     done:
922     p_uit->i_r = r;
923     p_uit->i_f = f;
924
925     r = p_uit->p_u->i_releases;
926
927     if( old_r == p_uit->i_r )
928     {
929         update_iterator_GetData( p_uit );
930         vlc_mutex_unlock( &p_uit->p_u->lock );
931         return UPDATE_SUCCESS|UPDATE_FILE;
932     }
933     else if( p_uit->i_r == -1 )
934     {
935         p_uit->i_r = -1;
936         p_uit->i_f = -1;
937         update_iterator_GetData( p_uit );
938         vlc_mutex_unlock( &p_uit->p_u->lock );
939         return UPDATE_FAIL;
940     }
941     else
942     {
943         update_iterator_GetData( p_uit );
944         vlc_mutex_unlock( &p_uit->p_u->lock );
945         return UPDATE_SUCCESS|UPDATE_RELEASE|UPDATE_FILE;
946     }
947 }
948
949 /**
950  * Finds the next mirror in the update tree
951  *
952  * \param update iterator
953  * \return UPDATE_FAIL if we can't find the next mirror, UPDATE_SUCCESS|UPDATE_MIRROR otherwise
954  */
955 unsigned int update_iterator_NextMirror( update_iterator_t *p_uit )
956 {
957     if( !p_uit ) return UPDATE_FAIL;
958     vlc_mutex_lock( &p_uit->p_u->lock );
959     p_uit->i_m++;
960     if( p_uit->i_m >= p_uit->p_u->i_mirrors ) p_uit->i_m = -1;
961     update_iterator_GetData( p_uit );
962     vlc_mutex_unlock( &p_uit->p_u->lock );
963     return p_uit->i_m == -1 ? UPDATE_FAIL : UPDATE_SUCCESS|UPDATE_MIRROR;
964 }
965
966 /**
967  * Finds the previous mirror in the update tree
968  *
969  * \param update iterator
970  * \return UPDATE_FAIL if we can't find a previous mirror, UPDATE_SUCCESS|UPDATE_MIRROR otherwise
971  */
972 unsigned int update_iterator_PrevMirror( update_iterator_t *p_uit )
973 {
974     if( !p_uit ) return UPDATE_FAIL;
975     vlc_mutex_lock( &p_uit->p_u->lock );
976     p_uit->i_m--;
977     update_iterator_GetData( p_uit );
978     vlc_mutex_unlock( &p_uit->p_u->lock );
979     return p_uit->i_m == -1 ? UPDATE_FAIL : UPDATE_SUCCESS|UPDATE_MIRROR;
980 }
981
982 /**
983  * Change the update iterator's position in the file and mirrors tree
984  * If position is negative, don't change it
985  *
986  * \param i_m position in mirrors list
987  * \param i_r position in releases list
988  * \param i_f position in release's files list
989  * \return UPDATE_FAIL when changing position fails or position wasn't changed, a combination of UPDATE_MIRROR, UPDATE_RELEASE and UPDATE_FILE otherwise
990  */
991 unsigned int update_iterator_ChooseMirrorAndFile( update_iterator_t *p_uit,
992                                         int i_m, int i_r, int i_f )
993 {
994     unsigned int i_val = 0;
995
996     if( !p_uit ) return 0;
997     vlc_mutex_lock( &p_uit->p_u->lock );
998
999     if( i_m >= 0 )
1000     {
1001         if( i_m < p_uit->p_u->i_mirrors )
1002         {
1003             if( i_m != p_uit->i_m )
1004                 i_val |= UPDATE_MIRROR;
1005             p_uit->i_m = i_m;
1006         }
1007         else i_m = -1;
1008     }
1009
1010     if( i_r >= 0 )
1011     {
1012         if( i_r < p_uit->p_u->i_releases )
1013         {
1014             if( i_r != p_uit->i_r )
1015                 i_val |= UPDATE_FILE;
1016             p_uit->i_r = i_r;
1017         }
1018         else i_r = -1;
1019     }
1020
1021     if( i_f >= 0 )
1022     {
1023         if( i_r >= 0 && i_r < p_uit->p_u->i_releases
1024             && i_f < p_uit->p_u->p_releases[p_uit->i_r].i_files )
1025         {
1026             if( i_f != p_uit->i_f )
1027                 i_val |= UPDATE_FILE;
1028             p_uit->i_f = i_f;
1029         }
1030         else i_f = -1;
1031     }
1032
1033     update_iterator_GetData( p_uit );
1034     vlc_mutex_unlock( &p_uit->p_u->lock );
1035
1036     if(    ( i_m < 0 || p_uit->i_m >= 0 )
1037         && ( i_r < 0 || p_uit->i_r >= 0 )
1038         && ( i_f < 0 || p_uit->i_f >= 0 ) )
1039     {
1040         /* Everything worked */
1041         return UPDATE_SUCCESS|i_val;
1042     }
1043     else
1044     {
1045         /* Something failed */
1046         return UPDATE_FAIL;
1047     }
1048 }
1049
1050 /**
1051  * Fills the iterator data (file, release and mirror structs)
1052  * The update struct should be locked before calling this function.
1053  *
1054  * \param p_uit update iterator
1055  * \return nothing
1056  */
1057 void update_iterator_GetData( update_iterator_t *p_uit )
1058 {
1059     struct update_release_t *p_r = NULL;
1060     struct update_file_t *p_f = NULL;
1061     struct update_mirror_t *p_m = NULL;
1062
1063     update_iterator_ClearData( p_uit );
1064
1065     if( p_uit->i_m >= 0 )
1066     {
1067         p_m = p_uit->p_u->p_mirrors + p_uit->i_m;
1068         p_uit->mirror.psz_name = STRDUP( p_m->psz_name );
1069         p_uit->mirror.psz_location = STRDUP( p_m->psz_location );
1070         p_uit->mirror.psz_type = STRDUP( p_m->psz_type );
1071     }
1072
1073     if( p_uit->i_r >= 0 )
1074     {
1075         p_r = p_uit->p_u->p_releases + p_uit->i_r;
1076         asprintf( &p_uit->release.psz_version, "%s.%s.%s-%s",
1077                                               p_r->psz_major,
1078                                               p_r->psz_minor,
1079                                               p_r->psz_revision,
1080                                               p_r->psz_extra );
1081         p_uit->release.psz_svn_revision = STRDUP( p_r->psz_svn_revision );
1082         p_uit->release.i_type = p_r->i_type;
1083         p_uit->release.i_status = p_r->i_status;
1084         if( p_uit->i_f >= 0 )
1085         {
1086             p_f = p_r->p_files + p_uit->i_f;
1087             p_uit->file.i_type = p_f->i_type;
1088             p_uit->file.psz_md5 = STRDUP( p_f->psz_md5 );
1089             p_uit->file.l_size = p_f->l_size;
1090             p_uit->file.psz_description = STRDUP( p_f->psz_description);
1091             if( p_f->psz_url[0] == '/' )
1092             {
1093                 if( p_m )
1094                 {
1095                     asprintf( &p_uit->file.psz_url, "%s%s",
1096                               p_m->psz_base_url, p_f->psz_url );
1097                 }
1098             }
1099             else
1100             {
1101                 p_uit->file.psz_url = STRDUP( p_f->psz_url );
1102             }
1103         }
1104     }
1105 }
1106
1107 /**
1108  * Clears the iterator data (file, release and mirror structs)
1109  *
1110  * \param p_uit update iterator
1111  * \return nothing
1112  */
1113 void update_iterator_ClearData( update_iterator_t *p_uit )
1114 {
1115     p_uit->file.i_type = UPDATE_FILE_TYPE_NONE;
1116     FREE( p_uit->file.psz_md5 );
1117     p_uit->file.l_size = 0;
1118     FREE( p_uit->file.psz_description );
1119     FREE( p_uit->file.psz_url );
1120     FREE( p_uit->release.psz_version );
1121     FREE( p_uit->release.psz_svn_revision );
1122     p_uit->release.i_type = UPDATE_RELEASE_TYPE_UNSTABLE;
1123     p_uit->release.i_status = UPDATE_RELEASE_STATUS_NONE;
1124     FREE( p_uit->mirror.psz_name );
1125     FREE( p_uit->mirror.psz_location );
1126     FREE( p_uit->mirror.psz_type );
1127 }
1128
1129 /**
1130  * Perform an action on the update iterator
1131  * Only the first matching action is performed.
1132  *
1133  * \param p_uit update iterator
1134  * \param i_action update action bitmask. can be a combination of UPDATE_NEXT, UPDATE_PREV, UPDATE_MIRROR, UPDATE_RELEASE, UPDATE_FILE, UPDATE_RESET
1135  * \return UPDATE_FAIL if action fails, UPDATE_SUCCESS|(combination of UPDATE_MIRROR, UPDATE_RELEASE and UPDATE_FILE if these changed) otherwise
1136  */
1137 unsigned int update_iterator_Action( update_iterator_t *p_uit, int i_action )
1138 {
1139     if( i_action & UPDATE_RESET )
1140     {
1141         return update_iterator_Reset( p_uit );
1142     }
1143     else
1144     if( i_action & UPDATE_MIRROR )
1145     {
1146         if( i_action & UPDATE_PREV )
1147         {
1148             return update_iterator_PrevMirror( p_uit );
1149         }
1150         else
1151         {
1152             return update_iterator_NextMirror( p_uit );
1153         }
1154     }
1155     /*else if( i_action & UPDATE_RELEASE )
1156     {
1157         if( i_action & UPDATE_PREV )
1158         {
1159             return update_iterator_PrevRelease( p_uit );
1160         }
1161         else
1162         {
1163             return update_iterator_NextRelease( p_uit );
1164         }
1165     }*/
1166     else if( i_action & UPDATE_FILE )
1167     {
1168         if( i_action & UPDATE_PREV )
1169         {
1170             return update_iterator_PrevFile( p_uit );
1171         }
1172         else
1173         {
1174             return update_iterator_NextFile( p_uit );
1175         }
1176     }
1177     else
1178     {
1179         return UPDATE_SUCCESS;
1180     }
1181 }
1182
1183 /**
1184  * Object to launch download thread in a different object
1185  */
1186 typedef struct {
1187     VLC_COMMON_MEMBERS
1188     char *psz_dest;     //< Download destination
1189     char *psz_src;      //< Download source
1190     char *psz_status;   //< Download status displayed in progress dialog
1191 } download_thread_t;
1192
1193 void update_download_for_real( download_thread_t *p_this );
1194
1195 /**
1196  * Download the file selected by the update iterator. This function will
1197  * launch the download in a new thread (downloads can be long)
1198  *
1199  * \param p_uit update iterator
1200  * \param psz_dest destination file path
1201  * \return nothing
1202  */
1203 void update_download( update_iterator_t *p_uit, char *psz_dest )
1204 {
1205     download_thread_t *p_dt =
1206         vlc_object_create( p_uit->p_u->p_vlc, sizeof( download_thread_t ) );
1207
1208     p_dt->psz_dest = strdup( psz_dest );
1209     p_dt->psz_src = strdup( p_uit->file.psz_url );
1210     asprintf( &p_dt->psz_status, "%s - %s (%s)\nSource: %s\nDestination: %s",
1211               p_uit->file.psz_description, p_uit->release.psz_version,
1212               p_uit->release.psz_svn_revision, p_uit->file.psz_url,
1213               psz_dest);
1214
1215     vlc_thread_create( p_dt, "download thread", update_download_for_real,
1216                        VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
1217 }
1218
1219 /**
1220  * The true download function.
1221  *
1222  * \param p_this the download_thread_t object
1223  * \return nothing
1224  */
1225 void update_download_for_real( download_thread_t *p_this )
1226 {
1227     char *psz_dest = p_this->psz_dest;
1228     char *psz_src = p_this->psz_src;
1229     stream_t *p_stream;
1230     vlc_t *p_vlc = p_this->p_vlc;
1231
1232     FILE *p_file = NULL;
1233     void *p_buffer;
1234
1235     char *psz_status;
1236
1237     int i_progress;
1238     int i_size;
1239     int i_done = 0;
1240
1241     vlc_thread_ready( p_this );
1242
1243     asprintf( &psz_status, "%s\nDownloading... %.1f%% done",
1244               p_this->psz_status, 0.0 );
1245     i_progress = intf_UserProgress( p_vlc, "Downloading...",
1246                                     psz_status, 0.0 );
1247
1248     p_stream = stream_UrlNew( p_vlc, psz_src );
1249     if( !p_stream )
1250     {
1251         msg_Err( p_vlc, "Failed to open %s for reading", psz_src );
1252     }
1253     else
1254     {
1255
1256         p_file = fopen( psz_dest, "w" );
1257         if( !p_file )
1258         {
1259             msg_Err( p_vlc, "Failed to open %s for writing", psz_dest );
1260         }
1261         else
1262         {
1263             int i_read;
1264
1265             i_size = (int)(stream_Size(p_stream)/(1<<10));
1266             p_buffer = (void *)malloc( 1<<10 );
1267
1268             while( ( i_read = stream_Read( p_stream, p_buffer, 1<<10 ) ) )
1269             {
1270                 float f_progress;
1271
1272                 fwrite( p_buffer, i_read, 1, p_file );
1273
1274                 i_done++;
1275                 free( psz_status );
1276                 f_progress = 100.0*(float)i_done/(float)i_size;
1277                 asprintf( &psz_status, "%s\nDownloading... %.1f%% done",
1278                            p_this->psz_status, f_progress );
1279                 intf_UserProgressUpdate( p_vlc, i_progress,
1280                                          psz_status, f_progress );
1281             }
1282
1283             free( p_buffer );
1284             fclose( p_file );
1285             stream_Delete( p_stream );
1286
1287             free( psz_status );
1288             asprintf( &psz_status, "%s\nDone (100.00%%)",
1289                        p_this->psz_status );
1290             intf_UserProgressUpdate( p_vlc, i_progress, psz_status, 100.0 );
1291             free( psz_status );
1292         }
1293     }
1294
1295     free( p_this->psz_dest );
1296     free( p_this->psz_src );
1297     free( p_this->psz_status );
1298
1299 #ifdef WIN32
1300     CloseHandle( p_this->thread_id );
1301 #endif
1302
1303     vlc_object_destroy( p_this );
1304 }