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