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