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