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