1 /*****************************************************************************
2 * addons.c: VLC addons manager
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 *****************************************************************************/
25 #include <vlc_common.h>
26 #include <vlc_atomic.h>
27 #include <vlc_modules.h>
28 #include <vlc_arrays.h>
29 #include <vlc_events.h>
32 #include <vlc_addons.h>
34 /*****************************************************************************
35 * Structures/definitions
36 *****************************************************************************/
38 typedef struct addon_entry_owner
42 } addon_entry_owner_t;
44 struct addons_manager_private_t
46 vlc_object_t *p_parent;
54 DECL_ARRAY(char*) uris;
55 DECL_ARRAY(addon_entry_t*) entries;
64 DECL_ARRAY(addon_entry_t*) entries;
68 static void *FinderThread( void * );
69 static void LoadLocalStorage( addons_manager_t *p_manager );
71 /*****************************************************************************
73 *****************************************************************************/
75 addon_entry_t * addon_entry_New()
77 addon_entry_owner_t *owner = calloc( 1, sizeof(addon_entry_owner_t) );
78 if( unlikely(owner == NULL) )
81 atomic_init( &owner->refs, 1 );
83 addon_entry_t *p_entry = &owner->entry;
84 vlc_mutex_init( &p_entry->lock );
85 ARRAY_INIT( p_entry->files );
89 addon_entry_t * addon_entry_Hold( addon_entry_t * p_entry )
91 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
93 atomic_fetch_add( &owner->refs, 1 );
97 void addon_entry_Release( addon_entry_t * p_entry )
99 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
101 if( atomic_fetch_sub(&owner->refs, 1) != 1 )
104 free( p_entry->psz_name );
105 free( p_entry->psz_summary );
106 free( p_entry->psz_description );
107 free( p_entry->psz_archive_uri );
108 free( p_entry->psz_author );
109 free( p_entry->psz_source_uri );
110 free( p_entry->psz_image_uri );
111 free( p_entry->psz_image_data );
112 free( p_entry->psz_source_module );
113 free( p_entry->psz_version );
114 free( p_entry->p_custom );
116 addon_file_t *p_file;
117 FOREACH_ARRAY( p_file, p_entry->files )
118 free( p_file->psz_filename );
119 free( p_file->psz_download_uri );
121 ARRAY_RESET( p_entry->files );
123 vlc_mutex_destroy( &p_entry->lock );
127 addons_manager_t *addons_manager_New( vlc_object_t *p_this )
129 addons_manager_t *p_manager = malloc( sizeof(addons_manager_t) );
130 if ( !p_manager ) return NULL;
132 p_manager->p_priv = malloc( sizeof(addons_manager_private_t) );
133 if ( !p_manager->p_priv )
139 p_manager->p_event_manager = malloc( sizeof(vlc_event_manager_t) );
140 if ( !p_manager->p_event_manager )
142 free( p_manager->p_priv );
147 p_manager->p_priv->p_parent = p_this;
149 #define INIT_QUEUE( name ) \
150 p_manager->p_priv->name.b_live = false;\
151 vlc_mutex_init( &p_manager->p_priv->name.lock );\
152 vlc_cond_init( &p_manager->p_priv->name.waitcond );\
153 ARRAY_INIT( p_manager->p_priv->name.entries );
156 INIT_QUEUE( installer )
157 ARRAY_INIT( p_manager->p_priv->finder.uris );
159 vlc_event_manager_t *em = p_manager->p_event_manager;
160 vlc_event_manager_init( em, p_manager );
161 vlc_event_manager_register_event_type(em, vlc_AddonFound);
162 vlc_event_manager_register_event_type(em, vlc_AddonsDiscoveryEnded);
163 vlc_event_manager_register_event_type(em, vlc_AddonChanged);
168 void addons_manager_Delete( addons_manager_t *p_manager )
172 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
173 b_live = p_manager->p_priv->finder.b_live;
174 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
177 vlc_cancel( p_manager->p_priv->finder.thread );
178 vlc_join( p_manager->p_priv->finder.thread, NULL );
181 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
182 b_live = p_manager->p_priv->installer.b_live;
183 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
186 vlc_cancel( p_manager->p_priv->installer.thread );
187 vlc_join( p_manager->p_priv->installer.thread, NULL );
190 vlc_event_manager_fini( p_manager->p_event_manager );
192 #define FREE_QUEUE( name ) \
193 vlc_mutex_lock( &p_manager->p_priv->name.lock );\
194 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->name.entries )\
195 addon_entry_Release( p_entry );\
197 ARRAY_RESET( p_manager->p_priv->name.entries );\
198 vlc_mutex_unlock( &p_manager->p_priv->name.lock );\
199 vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
200 vlc_cond_destroy( &p_manager->p_priv->name.waitcond );
203 FREE_QUEUE( installer )
204 FOREACH_ARRAY( char *psz_uri, p_manager->p_priv->finder.uris )
207 ARRAY_RESET( p_manager->p_priv->finder.uris );
209 free( p_manager->p_priv );
210 free( p_manager->p_event_manager );
214 void addons_manager_Gather( addons_manager_t *p_manager, const char *psz_uri )
219 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
221 ARRAY_APPEND( p_manager->p_priv->finder.uris, strdup( psz_uri ) );
223 if( !p_manager->p_priv->finder.b_live )
225 if( vlc_clone( &p_manager->p_priv->finder.thread, FinderThread, p_manager,
226 VLC_THREAD_PRIORITY_LOW ) )
228 msg_Err( p_manager->p_priv->p_parent,
229 "cannot spawn entries provider thread" );
230 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
233 p_manager->p_priv->finder.b_live = true;
236 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
237 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
240 /*****************************************************************************
242 *****************************************************************************/
244 static addon_entry_t * getHeldEntryByUUID( addons_manager_t *p_manager,
245 const addon_uuid_t uuid )
247 addon_entry_t *p_return = NULL;
248 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
249 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->finder.entries )
250 if ( !memcmp( p_entry->uuid, uuid, sizeof( addon_uuid_t ) ) )
253 addon_entry_Hold( p_return );
257 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
261 static void MergeSources( addons_manager_t *p_manager,
262 addon_entry_t **pp_addons, int i_count )
264 addon_entry_t *p_entry, *p_manager_entry;
265 addon_uuid_t zerouuid;
266 memset( zerouuid, 0, sizeof( addon_uuid_t ) );
267 for ( int i=0; i < i_count; i++ )
269 p_entry = pp_addons[i];
270 vlc_mutex_lock( &p_entry->lock );
271 if ( memcmp( p_entry->uuid, zerouuid, sizeof( addon_uuid_t ) ) )
272 p_manager_entry = getHeldEntryByUUID( p_manager, p_entry->uuid );
274 p_manager_entry = NULL;
275 if ( !p_manager_entry )
277 ARRAY_APPEND( p_manager->p_priv->finder.entries, p_entry );
279 event.type = vlc_AddonFound;
280 event.u.addon_generic_event.p_entry = p_entry;
281 vlc_event_send( p_manager->p_event_manager, &event );
285 vlc_mutex_lock( &p_manager_entry->lock );
286 if ( ( p_manager_entry->psz_version && p_entry->psz_version )
287 && /* FIXME: better version comparison */
288 strcmp( p_manager_entry->psz_version, p_entry->psz_version )
291 p_manager_entry->e_flags |= ADDON_UPDATABLE;
293 vlc_mutex_unlock( &p_manager_entry->lock );
294 addon_entry_Release( p_manager_entry );
296 vlc_mutex_unlock( &p_entry->lock );
300 static void LoadLocalStorage( addons_manager_t *p_manager )
302 addons_finder_t *p_finder =
303 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
305 module_t *p_module = module_need( p_finder, "addons finder",
306 "addons.store.list", true );
309 ARRAY_INIT( p_finder->entries );
310 p_finder->psz_uri = NULL;
311 p_finder->pf_find( p_finder );
312 module_unneed( p_finder, p_module );
314 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
316 ARRAY_RESET( p_finder->entries );
318 vlc_object_release( p_finder );
321 static void *FinderThread( void *p_data )
323 addons_manager_t *p_manager = p_data;
329 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
330 mutex_cleanup_push( &p_manager->p_priv->finder.lock );
331 while( p_manager->p_priv->finder.uris.i_size == 0 )
333 vlc_cond_wait( &p_manager->p_priv->finder.waitcond,
334 &p_manager->p_priv->finder.lock );
336 psz_uri = p_manager->p_priv->finder.uris.p_elems[0];
337 ARRAY_REMOVE( p_manager->p_priv->finder.uris, 0 );
340 addons_finder_t *p_finder =
341 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
343 i_cancel = vlc_savecancel();
344 if( p_finder != NULL )
347 ARRAY_INIT( p_finder->entries );
348 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
349 p_finder->psz_uri = psz_uri;
350 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
352 p_module = module_need( p_finder, "addons finder", NULL, false );
355 p_finder->pf_find( p_finder );
356 module_unneed( p_finder, p_module );
357 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
359 ARRAY_RESET( p_finder->entries );
361 vlc_object_release( p_finder );
365 event.type = vlc_AddonsDiscoveryEnded;
366 event.u.addon_generic_event.p_entry = NULL;
367 vlc_event_send( p_manager->p_event_manager, &event );
369 vlc_restorecancel( i_cancel );
376 static int addons_manager_WriteCatalog( addons_manager_t *p_manager )
378 int i_return = VLC_EGENERIC;
380 addons_storage_t *p_storage =
381 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
383 module_t *p_module = module_need( p_storage, "addons storage",
384 "addons.store.install", true );
387 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
388 i_return = p_storage->pf_catalog( p_storage, p_manager->p_priv->finder.entries.p_elems,
389 p_manager->p_priv->finder.entries.i_size );
390 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
391 module_unneed( p_storage, p_module );
393 vlc_object_release( p_storage );
398 int addons_manager_LoadCatalog( addons_manager_t *p_manager )
400 LoadLocalStorage( p_manager );
404 static int installOrRemoveAddon( addons_manager_t *p_manager, addon_entry_t *p_entry, bool b_install )
406 int i_return = VLC_EGENERIC;
408 addons_storage_t *p_storage =
409 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
411 module_t *p_module = module_need( p_storage, "addons storage",
412 "addons.store.install", true );
416 i_return = p_storage->pf_install( p_storage, p_entry );
418 i_return = p_storage->pf_remove( p_storage, p_entry );
419 module_unneed( p_storage, p_module );
420 msg_Dbg( p_manager->p_priv->p_parent, "InstallAddon returns %d", i_return );
421 if ( i_return == VLC_SUCCESS )
424 vlc_mutex_lock( &p_entry->lock );
425 p_entry->e_flags = ADDON_MANAGEABLE;
426 vlc_mutex_unlock( &p_entry->lock );
429 vlc_object_release( p_storage );
434 static void *InstallerThread( void *p_data )
436 addons_manager_t *p_manager = p_data;
439 event.type = vlc_AddonChanged;
443 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
444 mutex_cleanup_push( &p_manager->p_priv->installer.lock );
445 while ( !p_manager->p_priv->installer.entries.i_size )
447 /* No queued addons */
448 vlc_cond_wait( &p_manager->p_priv->installer.waitcond,
449 &p_manager->p_priv->installer.lock );
453 addon_entry_t *p_entry = p_manager->p_priv->installer.entries.p_elems[0];
454 ARRAY_REMOVE( p_manager->p_priv->installer.entries, 0 );
455 addon_entry_Hold( p_entry );
456 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
458 vlc_mutex_lock( &p_entry->lock );
460 if ( p_entry->e_state == ADDON_INSTALLED )
462 p_entry->e_state = ADDON_UNINSTALLING;
463 vlc_mutex_unlock( &p_entry->lock );
466 i_cancel = vlc_savecancel();
467 event.u.addon_generic_event.p_entry = p_entry;
468 vlc_event_send( p_manager->p_event_manager, &event );
470 i_ret = installOrRemoveAddon( p_manager, p_entry, false );
471 vlc_restorecancel( i_cancel );
473 vlc_mutex_lock( &p_entry->lock );
474 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_NOTINSTALLED
476 vlc_mutex_unlock( &p_entry->lock );
478 else if ( p_entry->e_state == ADDON_NOTINSTALLED )
480 p_entry->e_state = ADDON_INSTALLING;
481 vlc_mutex_unlock( &p_entry->lock );
484 i_cancel = vlc_savecancel();
485 event.u.addon_generic_event.p_entry = p_entry;
486 vlc_event_send( p_manager->p_event_manager, &event );
488 i_ret = installOrRemoveAddon( p_manager, p_entry, true );
489 vlc_restorecancel( i_cancel );
491 vlc_mutex_lock( &p_entry->lock );
492 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_INSTALLED
493 : ADDON_NOTINSTALLED;
494 vlc_mutex_unlock( &p_entry->lock );
497 vlc_mutex_unlock( &p_entry->lock );
500 i_cancel = vlc_savecancel();
501 event.u.addon_generic_event.p_entry = p_entry;
502 vlc_event_send( p_manager->p_event_manager, &event );
503 vlc_restorecancel( i_cancel );
505 addon_entry_Release( p_entry );
507 i_cancel = vlc_savecancel();
508 addons_manager_WriteCatalog( p_manager );
509 vlc_restorecancel( i_cancel );
515 static int InstallEntry( addons_manager_t *p_manager, addon_entry_t *p_entry )
517 if ( p_entry->e_type == ADDON_UNKNOWN ||
518 p_entry->e_type == ADDON_PLUGIN ||
519 p_entry->e_type == ADDON_OTHER )
522 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
523 ARRAY_APPEND( p_manager->p_priv->installer.entries, p_entry );
524 if( !p_manager->p_priv->installer.b_live )
526 if( vlc_clone( &p_manager->p_priv->installer.thread, InstallerThread, p_manager,
527 VLC_THREAD_PRIORITY_LOW ) )
529 msg_Err( p_manager->p_priv->p_parent,
530 "cannot spawn addons installer thread" );
531 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
535 p_manager->p_priv->installer.b_live = true;
537 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
538 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
542 int addons_manager_Install( addons_manager_t *p_manager, const addon_uuid_t uuid )
544 addon_entry_t *p_install_entry = getHeldEntryByUUID( p_manager, uuid );
545 if ( ! p_install_entry ) return VLC_EGENERIC;
546 int i_ret = InstallEntry( p_manager, p_install_entry );
547 addon_entry_Release( p_install_entry );
551 int addons_manager_Remove( addons_manager_t *p_manager, const addon_uuid_t uuid )
553 return addons_manager_Install( p_manager, uuid );