1 /*****************************************************************************
2 * vorepository.c : Videolan.org's Addons Lister
3 *****************************************************************************
4 * Copyright (C) 2014 VLC authors and VideoLAN
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.
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.
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 *****************************************************************************/
21 /*****************************************************************************
23 *****************************************************************************/
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_stream.h>
32 #include <vlc_addons.h>
35 #include "xmlreading.h"
39 /*****************************************************************************
41 *****************************************************************************/
43 static int Open ( vlc_object_t * );
44 static void Close ( vlc_object_t * );
45 static int Find ( addons_finder_t *p_finder );
46 static int Retrieve ( addons_finder_t *p_finder, addon_entry_t *p_entry );
47 static int OpenDesignated ( vlc_object_t * );
48 static int FindDesignated ( addons_finder_t *p_finder );
50 #define ADDONS_MODULE_SHORTCUT "addons.vo"
51 #define ADDONS_REPO_SCHEMEHOST "http://api.addons.videolan.org"
52 /*****************************************************************************
54 ****************************************************************************/
57 set_category(CAT_ADVANCED)
58 set_subcategory(SUBCAT_ADVANCED_MISC)
59 set_shortname(N_("Videolan.org's addons finder"))
60 add_shortcut(ADDONS_MODULE_SHORTCUT)
61 set_description(N_("addons.videolan.org addons finder"))
62 set_capability("addons finder", 100)
63 set_callbacks(Open, Close)
65 set_category(CAT_ADVANCED)
66 set_subcategory(SUBCAT_ADVANCED_MISC)
67 set_shortname(N_("Videolan.org's single archive addons finder"))
68 add_shortcut(ADDONS_MODULE_SHORTCUT".vlp")
69 set_description(N_("single .vlp archive addons finder"))
70 set_capability("addons finder", 101)
71 set_callbacks(OpenDesignated, NULL)
74 struct addons_finder_sys_t
79 static int ParseManifest( addons_finder_t *p_finder, addon_entry_t *p_entry,
80 const char *psz_tempfile, stream_t *p_stream )
82 int i_num_entries_created = 0;
84 int i_current_node_type;
87 const char *attr, *value;
90 const char *psz_filename = NULL;
93 xml_reader_t *p_xml_reader = xml_ReaderCreate( p_finder, p_stream );
94 if( !p_xml_reader ) return 0;
96 if( xml_ReaderNextNode( p_xml_reader, &p_node ) != XML_READER_STARTELEM )
98 msg_Err( p_finder, "invalid xml file" );
102 if ( strcmp( p_node, "videolan") )
104 msg_Err( p_finder, "unsupported XML data format" );
108 while( (i_current_node_type = xml_ReaderNextNode( p_xml_reader, &p_node )) > 0 )
110 switch( i_current_node_type )
112 case XML_READER_STARTELEM:
114 BINDNODE("resource", psz_filename, TYPE_STRING)
115 data_pointer.e_type = TYPE_NONE;
118 * Manifests are not allowed to update addons properties
119 * such as uuid, score, downloads, ...
120 * On the other hand, repo API must not set files directly.
123 if ( ! strcmp( p_node, "resource" ) )
125 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
127 if ( !strcmp( attr, "type" ) )
129 i_filetype = ReadType( value );
133 else if ( ! strcmp( p_node, "addon" ) )
135 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
137 if ( !strcmp( attr, "type" ) )
139 p_entry->e_type = ReadType( value );
146 case XML_READER_TEXT:
147 if ( data_pointer.e_type == TYPE_NONE || !p_entry ) break;
148 if ( data_pointer.e_type == TYPE_STRING )
149 *data_pointer.u_data.ppsz = strdup( p_node );
151 if ( data_pointer.e_type == TYPE_LONG )
152 *data_pointer.u_data.pl = atol( p_node );
154 if ( data_pointer.e_type == TYPE_INTEGER )
155 *data_pointer.u_data.pi = atoi( p_node );
158 case XML_READER_ENDELEM:
160 if ( ! strcmp( p_node, "resource" ) )
162 if ( psz_filename && i_filetype >= 0 )
164 addon_file_t *p_file = malloc( sizeof(addon_file_t) );
165 p_file->e_filetype = i_filetype;
166 p_file->psz_filename = strdup( psz_filename );
167 if ( asprintf( & p_file->psz_download_uri, "unzip://%s!/%s",
168 psz_tempfile, psz_filename ) > 0 )
170 ARRAY_APPEND( p_entry->files, p_file );
171 msg_Dbg( p_finder, "manifest lists file %s extractable from %s",
172 psz_filename, p_file->psz_download_uri );
173 i_num_entries_created++;
177 free( p_file->psz_filename );
186 data_pointer.e_type = TYPE_NONE;
195 xml_ReaderDelete( p_xml_reader );
196 return i_num_entries_created;
199 static int ParseCategoriesInfo( addons_finder_t *p_finder, stream_t *p_stream )
201 int i_num_entries_created = 0;
204 const char *attr, *value;
205 int i_current_node_type;
206 addon_entry_t *p_entry = NULL;
208 xml_reader_t *p_xml_reader = xml_ReaderCreate( p_finder, p_stream );
209 if( !p_xml_reader ) return 0;
211 if( xml_ReaderNextNode( p_xml_reader, &p_node ) != XML_READER_STARTELEM )
213 msg_Err( p_finder, "invalid xml file" );
217 if ( strcmp( p_node, "videolan") )
219 msg_Err( p_finder, "unsupported XML data format" );
223 while( (i_current_node_type = xml_ReaderNextNode( p_xml_reader, &p_node )) > 0 )
225 switch( i_current_node_type )
227 case XML_READER_STARTELEM:
229 if ( ! strcmp( p_node, "addon" ) )
231 p_entry = addon_entry_New();
232 p_entry->psz_source_module = strdup( ADDONS_MODULE_SHORTCUT );
233 p_entry->e_flags = ADDON_MANAGEABLE;
234 p_entry->e_state = ADDON_NOTINSTALLED;
236 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
238 if ( !strcmp( attr, "type" ) )
240 p_entry->e_type = ReadType( value );
242 else if ( !strcmp( attr, "id" ) )
244 addons_uuid_read( value, & p_entry->uuid );
246 else if ( !strcmp( attr, "downloads" ) )
248 p_entry->i_downloads = atoi( value );
250 else if ( !strcmp( attr, "score" ) )
252 p_entry->i_score = atol( value );
254 else if ( !strcmp( attr, "version" ) )
256 p_entry->psz_version = strdup( value );
262 if ( !p_entry ) break;
264 BINDNODE("name", p_entry->psz_name, TYPE_STRING)
265 BINDNODE("archive", p_entry->psz_archive_uri, TYPE_STRING)
266 BINDNODE("summary", p_entry->psz_summary, TYPE_STRING)
267 BINDNODE("description", p_entry->psz_description, TYPE_STRING)
268 BINDNODE("image", p_entry->psz_image_data, TYPE_STRING)
269 BINDNODE("creator", p_entry->psz_author, TYPE_STRING)
270 BINDNODE("sourceurl", p_entry->psz_source_uri, TYPE_STRING)
271 data_pointer.e_type = TYPE_NONE;
275 case XML_READER_TEXT:
276 if ( data_pointer.e_type == TYPE_NONE || !p_entry ) break;
277 if ( data_pointer.e_type == TYPE_STRING )
278 *data_pointer.u_data.ppsz = strdup( p_node );
280 if ( data_pointer.e_type == TYPE_LONG )
281 *data_pointer.u_data.pl = atol( p_node );
283 if ( data_pointer.e_type == TYPE_INTEGER )
284 *data_pointer.u_data.pi = atoi( p_node );
287 case XML_READER_ENDELEM:
288 if ( !p_entry ) break;
289 if ( ! strcmp( p_node, "addon" ) )
291 /* then append entry */
292 ARRAY_APPEND( p_finder->entries, p_entry );
294 i_num_entries_created++;
297 data_pointer.e_type = TYPE_NONE;
306 xml_ReaderDelete( p_xml_reader );
307 return i_num_entries_created;
310 static int Find( addons_finder_t *p_finder )
316 char *psz_uri = NULL;
318 if ( ! asprintf( &psz_uri, ADDONS_REPO_SCHEMEHOST"/xml" ) ) return VLC_ENOMEM;
321 stream_t *p_stream = stream_UrlNew( p_finder, psz_uri );
323 if ( !p_stream ) return VLC_EGENERIC;
325 if ( ! ParseCategoriesInfo( p_finder, p_stream ) )
327 /* no more entries have been read: was last page or error */
331 stream_Delete( p_stream );
337 static int Retrieve( addons_finder_t *p_finder, addon_entry_t *p_entry )
339 if ( !p_entry->psz_archive_uri )
342 /* get archive and parse manifest */
345 if ( p_entry->psz_archive_uri[0] == '/' )
349 if ( ! asprintf( &psz_uri, ADDONS_REPO_SCHEMEHOST"%s", p_entry->psz_archive_uri ) )
351 p_stream = stream_UrlNew( p_finder, psz_uri );
356 p_stream = stream_UrlNew( p_finder, p_entry->psz_archive_uri );
359 msg_Dbg( p_finder, "downloading archive %s", p_entry->psz_archive_uri );
360 if ( !p_stream ) return VLC_EGENERIC;
362 /* In case of pf_ reuse */
363 if ( p_finder->p_sys->psz_tempfile )
365 vlc_unlink( p_finder->p_sys->psz_tempfile );
366 FREENULL( p_finder->p_sys->psz_tempfile );
369 p_finder->p_sys->psz_tempfile = tempnam( NULL, "vlp" );
370 if ( !p_finder->p_sys->psz_tempfile )
372 msg_Err( p_finder, "Can't create temp storage file" );
373 stream_Delete( p_stream );
377 FILE *p_destfile = vlc_fopen( p_finder->p_sys->psz_tempfile, "w" );
380 msg_Err( p_finder, "Failed to open addon temp storage file" );
381 FREENULL(p_finder->p_sys->psz_tempfile);
382 stream_Delete( p_stream );
388 while ( ( i_read = stream_Read( p_stream, &buffer, 1<<10 ) ) )
390 if ( fwrite( &buffer, i_read, 1, p_destfile ) < 1 )
392 msg_Err( p_finder, "Failed to write to Addon file" );
393 fclose( p_destfile );
394 stream_Delete( p_stream );
398 fclose( p_destfile );
399 stream_Delete( p_stream );
401 msg_Dbg( p_finder, "Reading manifest from %s", p_finder->p_sys->psz_tempfile );
404 if ( asprintf( &psz_manifest, "unzip://%s!/manifest.xml",
405 p_finder->p_sys->psz_tempfile ) < 1 )
408 p_stream = stream_UrlNew( p_finder, psz_manifest );
409 free( psz_manifest );
411 int i_ret = ( ParseManifest( p_finder, p_entry,
412 p_finder->p_sys->psz_tempfile, p_stream ) > 0 )
413 ? VLC_SUCCESS : VLC_EGENERIC;
415 stream_Delete( p_stream );
420 static int FindDesignated( addons_finder_t *p_finder )
423 const char *psz_path = p_finder->psz_uri + 7; // remove scheme
425 if ( asprintf( &psz_manifest, "unzip://%s!/manifest.xml",
429 stream_t *p_stream = stream_UrlNew( p_finder, psz_manifest );
430 free( psz_manifest );
431 if ( !p_stream ) return VLC_EGENERIC;
433 if ( ParseCategoriesInfo( p_finder, p_stream ) )
435 /* Do archive uri fixup */
436 FOREACH_ARRAY( addon_entry_t *p_entry, p_finder->entries )
437 if ( likely( !p_entry->psz_archive_uri ) )
438 p_entry->psz_archive_uri = strdup( p_finder->psz_uri );
443 stream_Delete( p_stream );
447 stream_Delete( p_stream );
452 static int Open(vlc_object_t *p_this)
454 addons_finder_t *p_finder = (addons_finder_t*) p_this;
456 p_finder->p_sys = (addons_finder_sys_t*) malloc(sizeof(addons_finder_sys_t));
457 if ( !p_finder->p_sys )
459 p_finder->p_sys->psz_tempfile = NULL;
460 /* We only support listing the whole repo */
461 if ( p_finder->psz_uri )
464 p_finder->pf_find = Find;
465 p_finder->pf_retrieve = Retrieve;
470 static void Close(vlc_object_t *p_this)
472 addons_finder_t *p_finder = (addons_finder_t*) p_this;
473 if ( p_finder->p_sys->psz_tempfile )
475 unlink( p_finder->p_sys->psz_tempfile );
476 free( p_finder->p_sys );
480 static int OpenDesignated(vlc_object_t *p_this)
482 addons_finder_t *p_finder = (addons_finder_t*) p_this;
483 if ( !p_finder->psz_uri
484 || strncmp( "file://", p_finder->psz_uri, 7 )
485 || strncmp( ".vlp", p_finder->psz_uri + strlen( p_finder->psz_uri ) - 4, 4 )
489 p_finder->pf_find = FindDesignated;
490 p_finder->pf_retrieve = Retrieve;