48f8c8cb8a09a3f56788d34724901a4edf765b97
[vlc.git] / modules / misc / addons / fsstorage.c
1 /*****************************************************************************
2  * addonsstorage.c : Addons Local filesystem storage
3  *****************************************************************************
4  * Copyright (C) 2014 VLC authors and VideoLAN
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 /*****************************************************************************
22  * Preamble
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_modules.h>
32 #include <vlc_stream.h>
33 #include <vlc_addons.h>
34 #include <vlc_fs.h>
35 #include <vlc_strings.h>
36 #include <vlc_xml.h>
37 #include <vlc_url.h>
38 #include "xmlreading.h"
39
40 #include <sys/stat.h>
41 #include <errno.h>
42
43 #include <unistd.h>     // getpid()
44
45 /*****************************************************************************
46  * Local prototypes
47  *****************************************************************************/
48
49 #define ADDONS_DIR          ""
50 #define ADDONS_SCRIPTS_DIR  ADDONS_DIR DIR_SEP "lua"
51 #define ADDONS_CATALOG      ADDONS_DIR DIR_SEP "catalog.xml"
52
53 static struct
54 {
55     addon_type_t t;
56     const char * const psz_dir;
57 } const addons_dirs[] = {
58     { ADDON_EXTENSION,           ADDONS_SCRIPTS_DIR DIR_SEP "extensions" },
59     { ADDON_PLAYLIST_PARSER,     ADDONS_SCRIPTS_DIR DIR_SEP "playlist" },
60     { ADDON_SERVICE_DISCOVERY,   ADDONS_SCRIPTS_DIR DIR_SEP "sd" },
61     { ADDON_SKIN2,               ADDONS_DIR DIR_SEP "skins2" },
62 };
63
64 static int   OpenStorage ( vlc_object_t * );
65 static void  CloseStorage ( vlc_object_t * );
66 static int   OpenLister ( vlc_object_t * );
67 static void  CloseLister ( vlc_object_t * );
68
69 static int   LoadCatalog ( addons_finder_t * );
70 static bool  FileBelongsToManagedAddon( addons_finder_t *p_finder,
71                                         const addon_type_t e_type,
72                                         const char *psz_file );
73 /*****************************************************************************
74  * Module descriptor
75  ****************************************************************************/
76
77 vlc_module_begin ()
78     set_category(CAT_ADVANCED)
79     set_subcategory(SUBCAT_ADVANCED_MISC)
80     set_shortname(N_("addons local storage"))
81     add_shortcut("addons.store.install")
82     set_description(N_("Addons local storage installer"))
83     set_capability("addons storage", 10)
84     set_callbacks(OpenStorage, CloseStorage)
85
86 add_submodule ()
87     set_category(CAT_ADVANCED)
88     set_subcategory(SUBCAT_ADVANCED_MISC)
89     add_shortcut("addons.store.list")
90     set_description( N_("Addons local storage lister") )
91     set_capability( "addons finder", 0 )
92     set_callbacks( OpenLister, CloseLister )
93
94 vlc_module_end ()
95
96 static char * getAddonInstallDir( addon_type_t t )
97 {
98     const char *psz_subdir = NULL;
99     char *psz_dir;
100     char *psz_userdir = config_GetUserDir( VLC_DATA_DIR );
101     if ( !psz_userdir ) return NULL;
102
103     for ( unsigned int i=0; i< ARRAY_SIZE(addons_dirs); i++ )
104     {
105         if ( addons_dirs[i].t == t )
106         {
107             psz_subdir = addons_dirs[i].psz_dir;
108             break;
109         }
110     }
111
112     if ( !psz_subdir )
113     {
114         free ( psz_userdir );
115         return NULL;
116     }
117
118     if ( asprintf( &psz_dir, "%s%s", psz_userdir, psz_subdir ) < 1 )
119     {
120         free( psz_userdir );
121         return NULL;
122     }
123     free( psz_userdir );
124
125     return psz_dir;
126 }
127
128 static int ListSkin_filter( const char * psz_filename )
129 {
130     int i_len = strlen( psz_filename );
131     if ( i_len  <= 4 )
132         return 0;
133     else
134         return ! strcmp( psz_filename + i_len - 4, ".vlt" );
135 }
136
137 static int ListScript_filter( const char * psz_filename )
138 {
139     int i_len = strlen( psz_filename );
140     if ( i_len  <= 4 )
141         return 0;
142     else
143         return ! strcmp( psz_filename + i_len - 4, ".lua" );
144 }
145
146 static int ParseSkins2Info( addons_finder_t *p_finder, stream_t *p_stream,
147                             char **ppsz_title, char **ppsz_source )
148 {
149     const char *p_node;
150     int i_current_node_type;
151     bool b_done = false;
152
153     xml_reader_t *p_xml_reader = xml_ReaderCreate( p_finder, p_stream );
154     if( !p_xml_reader ) return VLC_EGENERIC;
155
156     if( xml_ReaderNextNode( p_xml_reader, &p_node ) != XML_READER_STARTELEM )
157     {
158         msg_Err( p_finder, "invalid xml file" );
159         goto error;
160     }
161
162     if ( strcmp( p_node, "Theme") )
163     {
164         msg_Err( p_finder, "unsupported XML data format" );
165         goto error;
166     }
167
168     while( !b_done && (i_current_node_type = xml_ReaderNextNode( p_xml_reader, &p_node )) > 0 )
169     {
170         switch( i_current_node_type )
171         {
172         case XML_READER_STARTELEM:
173         {
174             if ( !strcmp( p_node, "ThemeInfo" ) )
175             {
176                 const char *attr, *value;
177                 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
178                 {
179                     if ( !strcmp( attr, "name" ) )
180                         *ppsz_title = strdup( value );
181                     else if ( !strcmp( attr, "webpage" ) )
182                         *ppsz_source = strdup( value );
183                 }
184                 b_done = true;
185             }
186             break;
187         }
188
189         default:
190             break;
191         }
192     }
193
194     xml_ReaderDelete( p_xml_reader );
195     return ( b_done ) ? VLC_SUCCESS : VLC_EGENERIC;
196
197 error:
198     xml_ReaderDelete( p_xml_reader );
199     return VLC_EGENERIC;
200 }
201
202 static int ListSkins( addons_finder_t *p_finder )
203 {
204     char *psz_dir = getAddonInstallDir( ADDON_SKIN2 );
205     if ( !psz_dir )
206         return VLC_EGENERIC;
207
208     char **ppsz_list = NULL;
209     int i_count = vlc_scandir( psz_dir, &ppsz_list, ListSkin_filter, NULL );
210
211     for ( int i=0; i< i_count; i++ )
212     {
213         char *psz_file = ppsz_list[i];
214         if( !psz_file )
215             break;
216
217         if ( FileBelongsToManagedAddon( p_finder, ADDON_SKIN2, psz_file ) )
218              continue;
219
220         char *psz_uri;
221         if( asprintf( &psz_uri, "unzip://%s/%s!/theme.xml", psz_dir, psz_file ) >= 0)
222         {
223             int i_ret;
224             char *psz_name = NULL;
225             char *psz_source = NULL;
226             stream_t *p_stream = stream_UrlNew( p_finder, psz_uri );
227             free( psz_uri );
228             if ( !p_stream )
229             {
230                 i_ret = VLC_EGENERIC;
231             }
232             else
233             {
234                 i_ret = ParseSkins2Info( p_finder, p_stream, &psz_name, &psz_source );
235                 if ( i_ret != VLC_SUCCESS )
236                 {
237                     free( psz_name );
238                     free( psz_source );
239                 }
240                 stream_Delete( p_stream );
241             }
242
243             addon_entry_t *p_entry = addon_entry_New();
244             p_entry->e_type = ADDON_SKIN2;
245             p_entry->e_state = ADDON_INSTALLED;
246             if ( i_ret == VLC_SUCCESS )
247             {
248                 p_entry->psz_name = psz_name;
249                 p_entry->psz_description = strdup("Skins2 theme");
250                 p_entry->psz_source_uri = psz_source;
251             }
252             else
253             {
254                 p_entry->e_flags |= ADDON_BROKEN;
255                 p_entry->psz_name = strdup(psz_file);
256                 p_entry->psz_description = strdup("Skins2 theme");
257             }
258
259             ARRAY_APPEND( p_finder->entries, p_entry );
260         }
261         free( psz_file );
262     }
263
264     free( ppsz_list );
265     free( psz_dir );
266     return VLC_SUCCESS;
267 }
268
269 static bool FileBelongsToManagedAddon( addons_finder_t *p_finder,
270                                        const addon_type_t e_type,
271                                        const char *psz_file )
272 {
273     FOREACH_ARRAY( const addon_entry_t *p_entry, p_finder->entries )
274         if ( ( p_entry->e_flags & ADDON_MANAGEABLE ) == 0 )
275             continue;
276         FOREACH_ARRAY( const addon_file_t *p_file, p_entry->files )
277             if ( p_file->e_filetype == e_type
278                  && !strcmp( p_file->psz_filename, psz_file ) )
279                 return true;
280         FOREACH_END();
281     FOREACH_END();
282     return false;
283 }
284
285 static int ListScripts( addons_finder_t *p_finder, addon_type_t type )
286 {
287     char *psz_dir = getAddonInstallDir( type );
288     if ( ! psz_dir ) return VLC_EGENERIC;
289
290     char **ppsz_list = NULL;
291     int i_count = vlc_scandir( psz_dir, &ppsz_list, ListScript_filter, NULL );
292
293     for ( int i=0; i< i_count; i++ )
294     {
295         char *psz_file = ppsz_list[i];
296         if( !psz_file )
297             break;
298         if ( FileBelongsToManagedAddon( p_finder, type, psz_file ) )
299              continue;
300         addon_entry_t *p_entry = addon_entry_New();
301         p_entry->e_state = ADDON_INSTALLED;
302         p_entry->e_type = type;
303         p_entry->e_flags |= ADDON_BROKEN;
304         p_entry->psz_name = strdup(psz_file);
305         p_entry->psz_description = strdup("Lua script");
306
307         ARRAY_APPEND( p_finder->entries, p_entry );
308         free( psz_file );
309     }
310
311     free( ppsz_list );
312     free( psz_dir );
313
314     return VLC_SUCCESS;
315 }
316
317 static int List( addons_finder_t *p_finder )
318 {
319     addon_type_t types[] = {
320         ADDON_EXTENSION,
321         ADDON_PLAYLIST_PARSER,
322         ADDON_SERVICE_DISCOVERY
323     };
324     unsigned int i_type = 0;
325
326     LoadCatalog( p_finder );
327
328     /* Browse dirs to find rogue files */
329     while( i_type < ARRAY_SIZE( types ) )
330     {
331         ListScripts( p_finder, types[i_type++] );
332     }
333     ListSkins( p_finder );
334
335     return VLC_SUCCESS;
336 }
337
338 static int recursive_mkdir( vlc_object_t *p_this, const char *psz_dirname )
339 {/* stolen from config_CreateDir() */
340     if( !psz_dirname || !*psz_dirname ) return -1;
341
342     if( vlc_mkdir( psz_dirname, 0700 ) == 0 )
343         return 0;
344
345     switch( errno )
346     {
347         case EEXIST:
348             return 0;
349
350         case ENOENT:
351         {
352             /* Let's try to create the parent directory */
353             char psz_parent[strlen( psz_dirname ) + 1], *psz_end;
354             strcpy( psz_parent, psz_dirname );
355
356             psz_end = strrchr( psz_parent, DIR_SEP_CHAR );
357             if( psz_end && psz_end != psz_parent )
358             {
359                 *psz_end = '\0';
360                 if( recursive_mkdir( p_this, psz_parent ) == 0 )
361                 {
362                     if( !vlc_mkdir( psz_dirname, 0700 ) )
363                         return 0;
364                 }
365             }
366         }
367     }
368
369     msg_Warn( p_this, "could not create %s: %m", psz_dirname );
370     return -1;
371 }
372
373 static int InstallFile( addons_storage_t *p_this, const char *psz_downloadlink,
374                         const char *psz_dest )
375 {
376     stream_t *p_stream;
377     FILE *p_destfile;
378     char buffer[1<<10];
379     int i_read = 0;
380
381     p_stream = stream_UrlNew( p_this, psz_downloadlink );
382     if( !p_stream )
383     {
384         msg_Err( p_this, "Failed to access Addon download url %s", psz_downloadlink );
385         return VLC_EGENERIC;
386     }
387
388     char *psz_path = strdup( psz_dest );
389     if ( !psz_path )
390     {
391         stream_Delete( p_stream );
392         return VLC_ENOMEM;
393     }
394     char *psz_buf = strrchr( psz_path, DIR_SEP_CHAR );
395     if( psz_buf )
396     {
397         *++psz_buf = '\0';
398         /* ensure directory exists */
399         if( !EMPTY_STR( psz_path ) ) recursive_mkdir( VLC_OBJECT(p_this), psz_path );
400         free( psz_path );
401     }
402
403     p_destfile = vlc_fopen( psz_dest, "w" );
404     if( !p_destfile )
405     {
406         msg_Err( p_this, "Failed to open Addon storage file %s", psz_dest );
407         stream_Delete( p_stream );
408         return VLC_EGENERIC;
409     }
410
411     while ( ( i_read = stream_Read( p_stream, &buffer, 1<<10 ) ) )
412     {
413         if ( fwrite( &buffer, i_read, 1, p_destfile ) < 1 )
414         {
415             msg_Err( p_this, "Failed to write to Addon file" );
416             fclose( p_destfile );
417             stream_Delete( p_stream );
418             return VLC_EGENERIC;
419         }
420     }
421
422     fclose( p_destfile );
423     stream_Delete( p_stream );
424     return VLC_SUCCESS;
425 }
426
427 static int InstallAllFiles( addons_storage_t *p_this, const addon_entry_t *p_entry )
428 {
429     const addon_file_t *p_file;
430     char *psz_dest;
431
432     if ( p_entry->files.i_size < 1 )
433         return VLC_EGENERIC;
434
435     FOREACH_ARRAY( p_file, p_entry->files )
436
437     switch( p_file->e_filetype )
438     {
439         case ADDON_EXTENSION:
440         case ADDON_PLAYLIST_PARSER:
441         case ADDON_SERVICE_DISCOVERY:
442         case ADDON_SKIN2:
443         {
444             if ( strstr( p_file->psz_filename, ".." ) )
445                 return VLC_EGENERIC;
446
447             char *psz_translated_filename = strdup( p_file->psz_filename );
448             if ( !psz_translated_filename )
449                 return VLC_ENOMEM;
450             char *tmp = psz_translated_filename;
451             while (*tmp++) if ( *tmp == '/' ) *tmp = DIR_SEP_CHAR;
452
453             char *psz_dir = getAddonInstallDir( p_file->e_filetype );
454             if ( !psz_dir || asprintf( &psz_dest, "%s"DIR_SEP"%s", psz_dir,
455                            psz_translated_filename ) < 1 )
456             {
457                 free( psz_dir );
458                 free( psz_translated_filename );
459                 return VLC_EGENERIC;
460             }
461             free( psz_translated_filename );
462             free( psz_dir );
463
464             if ( InstallFile( p_this, p_file->psz_download_uri, psz_dest ) != VLC_SUCCESS )
465             {
466                 free( psz_dest );
467                 return VLC_EGENERIC;
468             }
469
470             free( psz_dest );
471             break;
472         }
473         /* Ignore all other unhandled files */
474         case ADDON_UNKNOWN:
475         case ADDON_PLUGIN:
476         case ADDON_OTHER:
477         default:
478             break;
479     }
480
481     FOREACH_END()
482
483     return VLC_SUCCESS;
484 }
485
486 static int Install( addons_storage_t *p_storage, addon_entry_t *p_entry )
487 {
488     vlc_object_t *p_this = VLC_OBJECT( p_storage );
489     int i_ret = VLC_EGENERIC;
490
491     if ( ! p_entry->psz_source_module )
492         return i_ret;
493
494     /* Query origin module for download path */
495     addons_finder_t *p_finder = vlc_object_create( p_this, sizeof( addons_finder_t ) );
496     if( !p_finder )
497         return VLC_ENOMEM;
498
499     module_t *p_module = module_need( p_finder, "addons finder",
500                                       p_entry->psz_source_module, true );
501     if( p_module )
502     {
503         if ( p_finder->pf_retrieve( p_finder, p_entry ) == VLC_SUCCESS )
504         {
505             /* Do things while retrieved data is here */
506             vlc_mutex_lock( &p_entry->lock );
507             i_ret = InstallAllFiles( p_storage, p_entry );
508             vlc_mutex_unlock( &p_entry->lock );
509             /* !Do things while retrieved data is here */
510         }
511
512         module_unneed( p_finder, p_module );
513     }
514
515     vlc_object_release( p_finder );
516
517     return i_ret;
518 }
519
520 #define WRITE_WITH_ENTITIES( formatstring, varname ) \
521 if ( varname ) \
522 {\
523     psz_tempstring = convert_xml_special_chars( varname );\
524     fprintf( p_catalog, formatstring, psz_tempstring );\
525     free( psz_tempstring );\
526 }\
527
528 static int WriteCatalog( addons_storage_t *p_storage,
529                          addon_entry_t **pp_entries, int i_entries )
530 {
531     addon_entry_t *p_entry;
532     char *psz_file;
533     char *psz_file_tmp;
534     char *psz_tempstring;
535     char *psz_userdir = config_GetUserDir( VLC_DATA_DIR );
536     if ( !psz_userdir ) return VLC_ENOMEM;
537
538     if ( asprintf( &psz_file, "%s%s", psz_userdir, ADDONS_CATALOG ) < 1 )
539     {
540         free( psz_userdir );
541         return VLC_ENOMEM;
542     }
543     free( psz_userdir );
544
545     if ( asprintf( &psz_file_tmp, "%s.tmp%"PRIu32, psz_file, (uint32_t)getpid() ) < 1 )
546     {
547         free( psz_file );
548         return VLC_ENOMEM;
549     }
550
551     char *psz_path = strdup( psz_file );
552     if ( !psz_path )
553     {
554         free( psz_file );
555         free( psz_file_tmp );
556         return VLC_ENOMEM;
557     }
558
559     char *psz_buf = strrchr( psz_path, DIR_SEP_CHAR );
560     if( psz_buf )
561     {
562         *++psz_buf = '\0';
563         /* ensure directory exists */
564         if( !EMPTY_STR( psz_path ) ) recursive_mkdir( VLC_OBJECT(p_storage), psz_path );
565     }
566     free( psz_path );
567
568     FILE *p_catalog = vlc_fopen( psz_file_tmp, "wt" );
569     if ( !p_catalog )
570     {
571         free( psz_file );
572         free( psz_file_tmp );
573         return VLC_EGENERIC;
574     }
575
576     /* write XML header */
577     fprintf( p_catalog, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
578     fprintf( p_catalog, "<videolan xmlns=\"http://videolan.org/ns/vlc/addons/1.0\">\n" );
579     fprintf( p_catalog, "\t<addons>\n" );
580
581     for ( int i=0; i<i_entries; i++ )
582     {
583         p_entry = pp_entries[i];
584         vlc_mutex_lock( &p_entry->lock );
585         psz_tempstring = NULL;
586
587         if ( ( p_entry->e_state != ADDON_INSTALLED ) ||
588              !( p_entry->e_flags & ADDON_MANAGEABLE ) )
589         {
590             vlc_mutex_unlock( &p_entry->lock );
591             continue;
592         }
593
594         if ( p_entry->psz_source_module )
595             psz_tempstring = convert_xml_special_chars( p_entry->psz_source_module );
596
597         char *psz_uuid = addons_uuid_to_psz( ( const addon_uuid_t * ) & p_entry->uuid );
598         fprintf( p_catalog, "\t\t<addon source=\"%s\" type=\"%s\" id=\"%s\" "
599                                  "downloads=\"%ld\" score=\"%d\"",
600                  ( psz_tempstring ) ? psz_tempstring : "",
601                  getTypePsz( p_entry->e_type ),
602                  psz_uuid,
603                  p_entry->i_downloads,
604                  p_entry->i_score );
605         free( psz_uuid );
606         free( psz_tempstring );
607
608         WRITE_WITH_ENTITIES( " version=\"%s\"", p_entry->psz_version )
609         fprintf( p_catalog, ">\n" );
610
611         WRITE_WITH_ENTITIES( "\t\t\t<name>%s</name>\n", p_entry->psz_name )
612         WRITE_WITH_ENTITIES( "\t\t\t<summary>%s</summary>\n", p_entry->psz_summary )
613
614         if ( p_entry->psz_description )
615         {
616             psz_tempstring = p_entry->psz_description;
617             /* FIXME: do real escaping */
618             while( ( psz_tempstring = strstr( psz_tempstring, "]]>" ) ) )
619                 *psz_tempstring = ' ';
620             fprintf( p_catalog, "\t\t\t<description><![CDATA[%s]]></description>\n", p_entry->psz_description );
621         }
622
623         WRITE_WITH_ENTITIES( "\t\t\t<image>%s</image>\n", p_entry->psz_image_data )
624         WRITE_WITH_ENTITIES( "\t\t\t<archive>%s</archive>\n", p_entry->psz_archive_uri )
625
626         fprintf( p_catalog, "\t\t\t<authorship>\n" );
627         WRITE_WITH_ENTITIES( "\t\t\t\t<creator>%s</creator>\n", p_entry->psz_author )
628         WRITE_WITH_ENTITIES( "\t\t\t\t<sourceurl>%s</sourceurl>\n", p_entry->psz_source_uri )
629         fprintf( p_catalog, "\t\t\t</authorship>\n" );
630
631         FOREACH_ARRAY( addon_file_t *p_file, p_entry->files )
632             psz_tempstring = convert_xml_special_chars( p_file->psz_filename );
633             fprintf( p_catalog, "\t\t\t<resource type=\"%s\">%s</resource>\n",
634                      getTypePsz( p_file->e_filetype ), psz_tempstring );
635             free( psz_tempstring );
636         FOREACH_END();
637
638         fprintf( p_catalog, "\t\t</addon>\n" );
639
640         vlc_mutex_unlock( &p_entry->lock );
641     }
642
643     fprintf( p_catalog, "\t</addons>\n" );
644     fprintf( p_catalog, "</videolan>\n" );
645     fclose( p_catalog );
646
647     int i_ret = vlc_rename( psz_file_tmp, psz_file );
648     free( psz_file );
649     free( psz_file_tmp );
650
651     if( i_ret == -1 )
652     {
653         msg_Err( p_storage, "could not rename temp catalog: %s",
654                  vlc_strerror_c(errno) );
655         return VLC_EGENERIC;
656     }
657
658     return VLC_SUCCESS;
659 }
660
661 static int LoadCatalog( addons_finder_t *p_finder )
662 {
663     char *psz_path;
664     char * psz_userdir = config_GetUserDir( VLC_DATA_DIR );
665     if ( !psz_userdir ) return VLC_ENOMEM;
666
667     if ( asprintf( &psz_path, "%s%s", psz_userdir, ADDONS_CATALOG ) < 1 )
668     {
669         free( psz_userdir );
670         return VLC_ENOMEM;
671     }
672     free( psz_userdir );
673
674     addon_entry_t *p_entry = NULL;
675     const char *p_node;
676     int i_current_node_type;
677     int i_ret = VLC_SUCCESS;
678
679     /* attr */
680     const char *attr, *value;
681
682     /* temp reading */
683     char *psz_filename = NULL;
684     int i_filetype = -1;
685
686     struct stat stat_;
687     if ( vlc_stat( psz_path, &stat_ ) )
688     {
689         free( psz_path );
690         return VLC_EGENERIC;
691     }
692
693     char *psz_catalog_uri = vlc_path2uri( psz_path, "file" );
694     free( psz_path );
695     if ( !psz_catalog_uri )
696         return VLC_EGENERIC;
697
698     stream_t *p_stream = stream_UrlNew( p_finder, psz_catalog_uri );
699     free( psz_catalog_uri );
700     if (! p_stream ) return VLC_EGENERIC;
701
702     xml_reader_t *p_xml_reader = xml_ReaderCreate( p_finder, p_stream );
703     if( !p_xml_reader )
704     {
705         stream_Delete( p_stream );
706         return VLC_EGENERIC;
707     }
708
709     if( xml_ReaderNextNode( p_xml_reader, &p_node ) != XML_READER_STARTELEM )
710     {
711         msg_Err( p_finder, "invalid catalog" );
712         i_ret = VLC_EGENERIC;
713         goto end;
714     }
715
716     if ( strcmp( p_node, "videolan") )
717     {
718         msg_Err( p_finder, "unsupported catalog data format" );
719         i_ret = VLC_EGENERIC;
720         goto end;
721     }
722
723     while( (i_current_node_type = xml_ReaderNextNode( p_xml_reader, &p_node )) > 0 )
724     {
725         switch( i_current_node_type )
726         {
727         case XML_READER_STARTELEM:
728         {
729             if ( ! strcmp( p_node, "addon" ) )
730             {
731                 if ( p_entry ) /* ?!? Unclosed tag */
732                     addon_entry_Release( p_entry );
733
734                 p_entry = addon_entry_New();
735                 //p_entry->psz_source_module = strdup( ADDONS_MODULE_SHORTCUT );
736                 p_entry->e_flags = ADDON_MANAGEABLE;
737                 p_entry->e_state = ADDON_INSTALLED;
738
739                 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
740                 {
741                     if ( !strcmp( attr, "type" ) )
742                     {
743                         p_entry->e_type = ReadType( value );
744                     }
745                     else if ( !strcmp( attr, "id" ) )
746                     {
747                         addons_uuid_read( value, & p_entry->uuid );
748                     }
749                     else if ( !strcmp( attr, "downloads" ) )
750                     {
751                         p_entry->i_downloads = atoi( value );
752                         if ( p_entry->i_downloads < 0 )
753                             p_entry->i_downloads = 0;
754                     }
755                     else if ( !strcmp( attr, "score" ) )
756                     {
757                         p_entry->i_score = atoi( value );
758                         if ( p_entry->i_score < 0 )
759                             p_entry->i_score = 0;
760                         else if ( p_entry->i_score > ADDON_MAX_SCORE )
761                             p_entry->i_score = ADDON_MAX_SCORE;
762                     }
763                     else if ( !strcmp( attr, "source" ) )
764                     {
765                         p_entry->psz_source_module = strdup( value );
766                     }
767                     else if ( !strcmp( attr, "version" ) )
768                     {
769                         p_entry->psz_version = strdup( value );
770                     }
771                 }
772
773                 break;
774             }
775             if ( !p_entry ) break;
776
777             BINDNODE("name", p_entry->psz_name, TYPE_STRING)
778             BINDNODE("archive", p_entry->psz_archive_uri, TYPE_STRING)
779             BINDNODE("summary", p_entry->psz_summary, TYPE_STRING)
780             BINDNODE("description", p_entry->psz_description, TYPE_STRING)
781             BINDNODE("image", p_entry->psz_image_data, TYPE_STRING)
782             BINDNODE("resource", psz_filename, TYPE_STRING)
783             BINDNODE("creator", p_entry->psz_author, TYPE_STRING)
784             BINDNODE("sourceurl", p_entry->psz_source_uri, TYPE_STRING)
785             data_pointer.e_type = TYPE_NONE;
786
787             if ( ! strcmp( p_node, "resource" ) )
788             {
789                 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
790                 {
791                     if ( !strcmp( attr, "type" ) )
792                     {
793                         i_filetype = ReadType( value );
794                     }
795                 }
796             }
797
798             break;
799         }
800         case XML_READER_TEXT:
801             if ( data_pointer.e_type == TYPE_NONE || !p_entry ) break;
802             if ( data_pointer.e_type == TYPE_STRING )
803                 *data_pointer.u_data.ppsz = strdup( p_node );
804             else
805             if ( data_pointer.e_type == TYPE_LONG )
806                 *data_pointer.u_data.pl = atol( p_node );
807             else
808             if ( data_pointer.e_type == TYPE_INTEGER )
809                 *data_pointer.u_data.pi = atoi( p_node );
810             break;
811
812         case XML_READER_ENDELEM:
813             if ( !p_entry ) break;
814
815             if ( ! strcmp( p_node, "addon" ) )
816             {
817                 /* then append entry */
818                 ARRAY_APPEND( p_finder->entries, p_entry );
819                 p_entry = NULL;
820             }
821
822             if ( ! strcmp( p_node, "resource" ) )
823             {
824                 if ( p_entry && psz_filename && i_filetype >= 0 )
825                 {
826                     addon_file_t *p_file = malloc( sizeof(addon_file_t) );
827                     p_file->e_filetype = i_filetype;
828                     p_file->psz_filename = psz_filename;
829                     p_file->psz_download_uri = NULL;
830                     ARRAY_APPEND( p_entry->files, p_file );
831                 }
832                 /* reset temp */
833                 psz_filename = NULL;
834                 i_filetype = -1;
835             }
836
837             data_pointer.e_type = TYPE_NONE;
838             break;
839
840         default:
841             break;
842         }
843     }
844
845 end:
846    if ( p_entry ) /* ?!? Unclosed tag */
847        addon_entry_Release( p_entry );
848    xml_ReaderDelete( p_xml_reader );
849    stream_Delete( p_stream );
850    return i_ret;
851 }
852
853 static int Remove( addons_storage_t *p_storage, addon_entry_t *p_entry )
854 {
855     vlc_mutex_lock( &p_entry->lock );
856     FOREACH_ARRAY( addon_file_t *p_file, p_entry->files )
857     switch( p_file->e_filetype )
858     {
859         case ADDON_EXTENSION:
860         case ADDON_PLAYLIST_PARSER:
861         case ADDON_SERVICE_DISCOVERY:
862         case ADDON_SKIN2:
863         {
864             char *psz_dest;
865
866             char *psz_translated_filename = strdup( p_file->psz_filename );
867             if ( !psz_translated_filename )
868                 return VLC_ENOMEM;
869             char *tmp = psz_translated_filename;
870             while (*tmp++) if ( *tmp == '/' ) *tmp = DIR_SEP_CHAR;
871
872             char *psz_dir = getAddonInstallDir( p_file->e_filetype );
873             if ( !psz_dir || asprintf( &psz_dest, "%s"DIR_SEP"%s", psz_dir,
874                                        psz_translated_filename ) < 1 )
875             {
876                 free( psz_dir );
877                 free( psz_translated_filename );
878                 return VLC_EGENERIC;
879             }
880             free( psz_dir );
881             free( psz_translated_filename );
882
883             vlc_unlink( psz_dest );
884             msg_Dbg( p_storage, "removing %s", psz_dest );
885
886             free( psz_dest );
887             break;
888         }
889             /* Ignore all other unhandled files */
890         case ADDON_UNKNOWN:
891         case ADDON_PLUGIN:
892         case ADDON_OTHER:
893         default:
894             break;
895     }
896     FOREACH_END()
897
898     /* Remove file info on success */
899     FOREACH_ARRAY( addon_file_t *p_file, p_entry->files )
900     free( p_file->psz_filename );
901     free( p_file->psz_download_uri );
902     free( p_file );
903     FOREACH_END()
904     ARRAY_RESET( p_entry->files );
905
906     vlc_mutex_unlock( &p_entry->lock );
907     return VLC_SUCCESS;
908 }
909
910 static int OpenStorage(vlc_object_t *p_this)
911 {
912     addons_storage_t *p_storage = (addons_storage_t*) p_this;
913
914     p_storage->pf_install = Install;
915     p_storage->pf_remove = Remove;
916     p_storage->pf_catalog = WriteCatalog;
917
918     return VLC_SUCCESS;
919 }
920
921 static void CloseStorage(vlc_object_t *p_this)
922 {
923     VLC_UNUSED( p_this );
924 }
925
926 static int OpenLister(vlc_object_t *p_this)
927 {
928     addons_finder_t *p_finder = (addons_finder_t*) p_this;
929     p_finder->pf_find = List;
930     p_finder->pf_retrieve = NULL;
931
932     return VLC_SUCCESS;
933 }
934
935 static void CloseLister(vlc_object_t *p_this)
936 {
937     VLC_UNUSED( p_this );
938 }