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