addonsvorepository: fix compilation on OS/2
[vlc.git] / modules / misc / addons / vorepository.c
blobb014d49fef26e7f8e53f6b48efef396932b492a1
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 /*****************************************************************************
22 * Preamble
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <assert.h>
30 #include <fcntl.h>
31 #include <unistd.h> /* write() */
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_stream.h>
36 #include <vlc_stream_extractor.h>
37 #include <vlc_addons.h>
38 #include <vlc_xml.h>
39 #include <vlc_fs.h>
40 #include <vlc_url.h>
41 #include "xmlreading.h"
43 /*****************************************************************************
44 * Local prototypes
45 *****************************************************************************/
47 static int Open ( vlc_object_t * );
48 static void Close ( vlc_object_t * );
49 static int Retrieve ( addons_finder_t *p_finder, addon_entry_t *p_entry );
50 static int OpenDesignated ( vlc_object_t * );
51 static int FindDesignated ( addons_finder_t *p_finder );
53 #define ADDONS_MODULE_SHORTCUT "addons.vo"
54 #define ADDONS_REPO_SCHEMEHOST "https://api-addons.videolan.org"
55 /*****************************************************************************
56 * Module descriptor
57 ****************************************************************************/
59 vlc_module_begin ()
60 set_category(CAT_ADVANCED)
61 set_subcategory(SUBCAT_ADVANCED_MISC)
62 set_shortname(N_("Videolan.org's addons finder"))
63 add_shortcut(ADDONS_MODULE_SHORTCUT)
64 set_description(N_("addons.videolan.org addons finder"))
65 set_capability("addons finder", 100)
66 set_callbacks(Open, Close)
67 add_submodule ()
68 set_category(CAT_ADVANCED)
69 set_subcategory(SUBCAT_ADVANCED_MISC)
70 set_shortname(N_("Videolan.org's single archive addons finder"))
71 add_shortcut(ADDONS_MODULE_SHORTCUT".vlp")
72 set_description(N_("single .vlp archive addons finder"))
73 set_capability("addons finder", 101)
74 set_callbacks(OpenDesignated, NULL)
75 vlc_module_end ()
77 struct addons_finder_sys_t
79 char *psz_tempfile;
82 static int ParseManifest( addons_finder_t *p_finder, addon_entry_t *p_entry,
83 const char *psz_tempfileuri, stream_t *p_stream )
85 int i_num_entries_created = 0;
86 const char *p_node;
87 int i_current_node_type;
89 /* attr */
90 const char *attr, *value;
92 /* temp reading */
93 char *psz_filename = NULL;
94 int i_filetype = -1;
96 xml_reader_t *p_xml_reader = xml_ReaderCreate( p_finder, p_stream );
97 if( !p_xml_reader ) return 0;
99 if( xml_ReaderNextNode( p_xml_reader, &p_node ) != XML_READER_STARTELEM )
101 msg_Err( p_finder, "invalid xml file" );
102 goto end;
105 if ( strcmp( p_node, "videolan") )
107 msg_Err( p_finder, "unsupported XML data format" );
108 goto end;
111 while( (i_current_node_type = xml_ReaderNextNode( p_xml_reader, &p_node )) > 0 )
113 switch( i_current_node_type )
115 case XML_READER_STARTELEM:
117 BINDNODE("resource", psz_filename, TYPE_STRING)
118 data_pointer.e_type = TYPE_NONE;
121 * Manifests are not allowed to update addons properties
122 * such as uuid, score, downloads, ...
123 * On the other hand, repo API must not set files directly.
126 if ( ! strcmp( p_node, "resource" ) )
128 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
130 if ( !strcmp( attr, "type" ) )
132 i_filetype = ReadType( value );
136 else if ( ! strcmp( p_node, "addon" ) )
138 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
140 if ( !strcmp( attr, "type" ) )
142 p_entry->e_type = ReadType( value );
147 break;
149 case XML_READER_TEXT:
150 if ( data_pointer.e_type == TYPE_NONE || !p_entry ) break;
151 if ( data_pointer.e_type == TYPE_STRING )
153 if( data_pointer.u_data.ppsz )
154 free( *data_pointer.u_data.ppsz );
155 *data_pointer.u_data.ppsz = strdup( p_node );
157 else
158 if ( data_pointer.e_type == TYPE_LONG )
159 *data_pointer.u_data.pl = atol( p_node );
160 else
161 if ( data_pointer.e_type == TYPE_INTEGER )
162 *data_pointer.u_data.pi = atoi( p_node );
163 break;
165 case XML_READER_ENDELEM:
167 if ( ! strcmp( p_node, "resource" ) )
169 if ( psz_filename && i_filetype >= 0 )
171 addon_file_t *p_file = malloc( sizeof(addon_file_t) );
172 p_file->e_filetype = i_filetype;
173 p_file->psz_filename = strdup( psz_filename );
174 if ( asprintf( & p_file->psz_download_uri, "%s#!/%s",
175 psz_tempfileuri, psz_filename ) > 0 )
177 ARRAY_APPEND( p_entry->files, p_file );
178 msg_Dbg( p_finder, "manifest lists file %s extractable from %s",
179 psz_filename, p_file->psz_download_uri );
180 i_num_entries_created++;
182 else
184 free( p_file->psz_filename );
185 free( p_file );
188 /* reset temp */
189 free( psz_filename );
190 psz_filename = NULL;
191 i_filetype = -1;
194 data_pointer.e_type = TYPE_NONE;
195 break;
197 default:
198 break;
202 end:
203 xml_ReaderDelete( p_xml_reader );
204 return i_num_entries_created;
207 static int ParseCategoriesInfo( addons_finder_t *p_finder, stream_t *p_stream )
209 int i_num_entries_created = 0;
211 const char *p_node;
212 const char *attr, *value;
213 int i_current_node_type;
214 addon_entry_t *p_entry = NULL;
216 xml_reader_t *p_xml_reader = xml_ReaderCreate( p_finder, p_stream );
217 if( !p_xml_reader ) return 0;
219 if( xml_ReaderNextNode( p_xml_reader, &p_node ) != XML_READER_STARTELEM )
221 msg_Err( p_finder, "invalid xml file" );
222 goto end;
225 if ( strcmp( p_node, "videolan") )
227 msg_Err( p_finder, "unsupported XML data format" );
228 goto end;
231 while( (i_current_node_type = xml_ReaderNextNode( p_xml_reader, &p_node )) > 0 )
233 switch( i_current_node_type )
235 case XML_READER_STARTELEM:
237 if ( ! strcmp( p_node, "addon" ) )
239 if ( p_entry ) /* Unclosed tag */
240 addon_entry_Release( p_entry );
241 p_entry = addon_entry_New();
242 p_entry->psz_source_module = strdup( ADDONS_MODULE_SHORTCUT );
243 p_entry->e_flags = ADDON_MANAGEABLE;
244 p_entry->e_state = ADDON_NOTINSTALLED;
246 while( (attr = xml_ReaderNextAttr( p_xml_reader, &value )) )
248 if ( !strcmp( attr, "type" ) )
250 p_entry->e_type = ReadType( value );
252 else if ( !strcmp( attr, "id" ) )
254 addons_uuid_read( value, & p_entry->uuid );
256 else if ( !strcmp( attr, "downloads" ) )
258 p_entry->i_downloads = atoi( value );
259 if ( p_entry->i_downloads < 0 )
260 p_entry->i_downloads = 0;
262 else if ( !strcmp( attr, "score" ) )
264 p_entry->i_score = atoi( value );
265 if ( p_entry->i_score < 0 )
266 p_entry->i_score = 0;
267 else if ( p_entry->i_score > ADDON_MAX_SCORE )
268 p_entry->i_score = ADDON_MAX_SCORE;
270 else if ( !strcmp( attr, "version" ) )
272 p_entry->psz_version = strdup( value );
276 break;
278 if ( !p_entry ) break;
280 BINDNODE("name", p_entry->psz_name, TYPE_STRING)
281 BINDNODE("archive", p_entry->psz_archive_uri, TYPE_STRING)
282 BINDNODE("summary", p_entry->psz_summary, TYPE_STRING)
283 BINDNODE("description", p_entry->psz_description, TYPE_STRING)
284 BINDNODE("image", p_entry->psz_image_data, TYPE_STRING)
285 BINDNODE("creator", p_entry->psz_author, TYPE_STRING)
286 BINDNODE("sourceurl", p_entry->psz_source_uri, TYPE_STRING)
287 data_pointer.e_type = TYPE_NONE;
289 break;
291 case XML_READER_TEXT:
292 if ( data_pointer.e_type == TYPE_NONE || !p_entry ) break;
293 if ( data_pointer.e_type == TYPE_STRING )
295 if( data_pointer.u_data.ppsz )
296 free( *data_pointer.u_data.ppsz );
297 *data_pointer.u_data.ppsz = strdup( p_node );
299 else
300 if ( data_pointer.e_type == TYPE_LONG )
301 *data_pointer.u_data.pl = atol( p_node );
302 else
303 if ( data_pointer.e_type == TYPE_INTEGER )
304 *data_pointer.u_data.pi = atoi( p_node );
305 break;
307 case XML_READER_ENDELEM:
308 if ( !p_entry ) break;
309 if ( ! strcmp( p_node, "addon" ) )
311 /* then append entry */
312 ARRAY_APPEND( p_finder->entries, p_entry );
313 p_entry = NULL;
314 i_num_entries_created++;
317 data_pointer.e_type = TYPE_NONE;
318 break;
320 default:
321 break;
325 end:
326 if ( p_entry ) /* Unclosed tag */
327 addon_entry_Release( p_entry );
328 xml_ReaderDelete( p_xml_reader );
329 return i_num_entries_created;
332 static stream_t * vlc_stream_NewURL_ND( addons_finder_t *p_obj, const char *psz_uri )
334 stream_t *p_stream = vlc_stream_NewURL( p_obj, psz_uri );
335 if( p_stream )
337 /* (non applicable everywhere) remove extra
338 * compression, bad wine madness :YYY */
339 stream_t *p_chain = vlc_stream_FilterNew( p_stream, "inflate" );
340 if( p_chain )
341 p_stream = p_chain;
343 return p_stream;
346 static int Find( addons_finder_t *p_finder )
348 bool b_done = false;
350 while ( !b_done )
352 char *psz_uri = NULL;
354 if ( ! asprintf( &psz_uri, ADDONS_REPO_SCHEMEHOST"/xml" ) ) return VLC_ENOMEM;
355 b_done = true;
357 stream_t *p_stream = vlc_stream_NewURL_ND( p_finder, psz_uri );
358 free( psz_uri );
359 if ( !p_stream ) return VLC_EGENERIC;
361 if ( ! ParseCategoriesInfo( p_finder, p_stream ) )
363 /* no more entries have been read: was last page or error */
364 b_done = true;
367 vlc_stream_Delete( p_stream );
370 return VLC_SUCCESS;
373 static int Retrieve( addons_finder_t *p_finder, addon_entry_t *p_entry )
375 vlc_mutex_lock( &p_entry->lock );
376 if ( !p_entry->psz_archive_uri )
378 vlc_mutex_unlock( &p_entry->lock );
379 return VLC_EGENERIC;
381 char *psz_archive_uri = strdup( p_entry->psz_archive_uri );
382 vlc_mutex_unlock( &p_entry->lock );
383 if ( !psz_archive_uri )
384 return VLC_ENOMEM;
386 /* get archive and parse manifest */
387 stream_t *p_stream;
389 if ( psz_archive_uri[0] == '/' )
391 /* Relative path */
392 char *psz_uri;
393 if ( ! asprintf( &psz_uri, ADDONS_REPO_SCHEMEHOST"%s", psz_archive_uri ) )
395 free( psz_archive_uri );
396 return VLC_ENOMEM;
398 p_stream = vlc_stream_NewURL_ND( p_finder, psz_uri );
399 free( psz_uri );
401 else
403 p_stream = vlc_stream_NewURL_ND( p_finder, psz_archive_uri );
406 msg_Dbg( p_finder, "downloading archive %s", psz_archive_uri );
407 free ( psz_archive_uri );
408 if ( !p_stream ) return VLC_EGENERIC;
410 /* In case of pf_ reuse */
411 if ( p_finder->p_sys->psz_tempfile )
413 vlc_unlink( p_finder->p_sys->psz_tempfile );
414 FREENULL( p_finder->p_sys->psz_tempfile );
417 p_finder->p_sys->psz_tempfile = tempnam( NULL, "vlp" );
418 if ( !p_finder->p_sys->psz_tempfile )
420 msg_Err( p_finder, "Can't create temp storage file" );
421 vlc_stream_Delete( p_stream );
422 return VLC_EGENERIC;
425 int fd = vlc_open( p_finder->p_sys->psz_tempfile,
426 O_WRONLY | O_CREAT | O_EXCL, 0600 );
427 if( fd == -1 )
429 msg_Err( p_finder, "Failed to open addon temp storage file" );
430 FREENULL(p_finder->p_sys->psz_tempfile);
431 vlc_stream_Delete( p_stream );
432 return VLC_EGENERIC;
435 char buffer[1<<10];
436 ssize_t i_read = 0;
437 int i_ret = VLC_SUCCESS;
439 while ( ( i_read = vlc_stream_Read( p_stream, &buffer, 1<<10 ) ) > 0 )
441 if ( write( fd, buffer, i_read ) != i_read )
443 msg_Err( p_finder, "Failed to write to Addon file" );
444 i_ret = VLC_EGENERIC;
445 break;
449 vlc_close( fd );
450 vlc_stream_Delete( p_stream );
452 if (i_ret)
453 return i_ret;
455 msg_Dbg( p_finder, "Reading manifest from %s", p_finder->p_sys->psz_tempfile );
457 char *psz_tempfileuri = vlc_path2uri( p_finder->p_sys->psz_tempfile, NULL );
458 if ( !psz_tempfileuri )
459 return VLC_ENOMEM;
461 char *psz_manifest_uri;
462 if ( asprintf( &psz_manifest_uri, "%s#!/manifest.xml", psz_tempfileuri ) < 1 )
464 free( psz_tempfileuri );
465 return VLC_ENOMEM;
468 p_stream = vlc_stream_NewMRL( p_finder, psz_manifest_uri );
469 free( psz_manifest_uri );
470 if ( !p_stream )
472 free( psz_tempfileuri );
473 return VLC_EGENERIC;
476 vlc_mutex_lock( &p_entry->lock );
477 i_ret = ( ParseManifest( p_finder, p_entry, psz_tempfileuri, p_stream ) > 0 )
478 ? VLC_SUCCESS : VLC_EGENERIC;
479 vlc_mutex_unlock( &p_entry->lock );
480 free( psz_tempfileuri );
481 vlc_stream_Delete( p_stream );
483 return i_ret;
486 static int FindDesignated( addons_finder_t *p_finder )
488 char *psz_manifest;
489 const char *psz_path = p_finder->psz_uri + 7; // remove scheme
491 if ( asprintf( &psz_manifest, "file://%s#!/manifest.xml",
492 psz_path ) < 1 )
493 return VLC_ENOMEM;
495 stream_t *p_stream = vlc_stream_NewMRL( p_finder, psz_manifest );
496 free( psz_manifest );
497 if ( !p_stream ) return VLC_EGENERIC;
499 if ( ParseCategoriesInfo( p_finder, p_stream ) )
501 /* Do archive uri fixup */
502 FOREACH_ARRAY( addon_entry_t *p_entry, p_finder->entries )
503 if ( likely( !p_entry->psz_archive_uri ) )
504 p_entry->psz_archive_uri = strdup( p_finder->psz_uri );
505 FOREACH_END()
507 else
509 vlc_stream_Delete( p_stream );
510 return VLC_EGENERIC;
513 vlc_stream_Delete( p_stream );
515 return VLC_SUCCESS;
518 static int Open(vlc_object_t *p_this)
520 addons_finder_t *p_finder = (addons_finder_t*) p_this;
522 p_finder->p_sys = (addons_finder_sys_t*) malloc(sizeof(addons_finder_sys_t));
523 if ( !p_finder->p_sys )
524 return VLC_ENOMEM;
525 p_finder->p_sys->psz_tempfile = NULL;
527 if ( p_finder->psz_uri &&
528 strcmp( "repo://"ADDONS_MODULE_SHORTCUT, p_finder->psz_uri ) &&
529 memcmp( "repo://", p_finder->psz_uri, 8 ) )
531 free( p_finder->p_sys );
532 return VLC_EGENERIC;
535 p_finder->pf_find = Find;
536 p_finder->pf_retrieve = Retrieve;
538 return VLC_SUCCESS;
541 static void Close(vlc_object_t *p_this)
543 addons_finder_t *p_finder = (addons_finder_t*) p_this;
544 if ( p_finder->p_sys->psz_tempfile )
546 vlc_unlink( p_finder->p_sys->psz_tempfile );
547 free( p_finder->p_sys->psz_tempfile );
549 free( p_finder->p_sys );
552 static int OpenDesignated(vlc_object_t *p_this)
554 addons_finder_t *p_finder = (addons_finder_t*) p_this;
555 if ( !p_finder->psz_uri
556 || strncmp( "file://", p_finder->psz_uri, 7 )
557 || strncmp( ".vlp", p_finder->psz_uri + strlen( p_finder->psz_uri ) - 4, 4 )
559 return VLC_EGENERIC;
561 p_finder->pf_find = FindDesignated;
562 p_finder->pf_retrieve = Retrieve;
564 return VLC_SUCCESS;