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