chromecast: Store the media session id as an integer
[vlc.git] / src / misc / addons.c
blob3204fb3c8e98d1601223781bd6352fe00922bd36
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 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <stdatomic.h>
27 #include <vlc_common.h>
28 #include <vlc_modules.h>
29 #include <vlc_arrays.h>
30 #include <vlc_interrupt.h>
31 #include "libvlc.h"
33 #include <vlc_addons.h>
35 /*****************************************************************************
36 * Structures/definitions
37 *****************************************************************************/
39 typedef struct addon_entry_owner
41 addon_entry_t entry;
42 atomic_uint refs;
43 } addon_entry_owner_t;
45 struct addons_manager_private_t
47 vlc_object_t *p_parent;
49 struct
51 vlc_thread_t thread;
52 vlc_cond_t waitcond;
53 bool b_live;
54 vlc_mutex_t lock;
55 vlc_interrupt_t *p_interrupt;
56 DECL_ARRAY(char*) uris;
57 DECL_ARRAY(addon_entry_t*) entries;
58 } finder;
60 struct
62 vlc_thread_t thread;
63 vlc_cond_t waitcond;
64 bool b_live;
65 vlc_mutex_t lock;
66 vlc_interrupt_t *p_interrupt;
67 DECL_ARRAY(addon_entry_t*) entries;
68 } installer;
71 static void *FinderThread( void * );
72 static void LoadLocalStorage( addons_manager_t *p_manager );
74 /*****************************************************************************
75 * Public functions
76 *****************************************************************************/
78 addon_entry_t * addon_entry_New(void)
80 addon_entry_owner_t *owner = calloc( 1, sizeof(addon_entry_owner_t) );
81 if( unlikely(owner == NULL) )
82 return NULL;
84 atomic_init( &owner->refs, 1 );
86 addon_entry_t *p_entry = &owner->entry;
87 vlc_mutex_init( &p_entry->lock );
88 ARRAY_INIT( p_entry->files );
89 return p_entry;
92 addon_entry_t * addon_entry_Hold( addon_entry_t * p_entry )
94 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
96 atomic_fetch_add( &owner->refs, 1 );
97 return p_entry;
100 void addon_entry_Release( addon_entry_t * p_entry )
102 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
104 if( atomic_fetch_sub(&owner->refs, 1) != 1 )
105 return;
107 free( p_entry->psz_name );
108 free( p_entry->psz_summary );
109 free( p_entry->psz_description );
110 free( p_entry->psz_archive_uri );
111 free( p_entry->psz_author );
112 free( p_entry->psz_source_uri );
113 free( p_entry->psz_image_uri );
114 free( p_entry->psz_image_data );
115 free( p_entry->psz_source_module );
116 free( p_entry->psz_version );
117 free( p_entry->p_custom );
119 addon_file_t *p_file;
120 FOREACH_ARRAY( p_file, p_entry->files )
121 free( p_file->psz_filename );
122 free( p_file->psz_download_uri );
123 free( p_file );
124 FOREACH_END()
125 ARRAY_RESET( p_entry->files );
127 vlc_mutex_destroy( &p_entry->lock );
128 free( owner );
131 addons_manager_t *addons_manager_New( vlc_object_t *p_this,
132 const struct addons_manager_owner *restrict owner )
134 addons_manager_t *p_manager = malloc( sizeof(addons_manager_t) );
135 if ( !p_manager ) return NULL;
137 p_manager->p_priv = malloc( sizeof(addons_manager_private_t) );
138 if ( !p_manager->p_priv )
140 free( p_manager );
141 return NULL;
144 p_manager->owner = *owner;
145 p_manager->p_priv->p_parent = p_this;
147 p_manager->p_priv->finder.p_interrupt = vlc_interrupt_create();
148 p_manager->p_priv->installer.p_interrupt = vlc_interrupt_create();
149 if ( !p_manager->p_priv->finder.p_interrupt ||
150 !p_manager->p_priv->installer.p_interrupt )
152 if( p_manager->p_priv->finder.p_interrupt )
153 vlc_interrupt_destroy( p_manager->p_priv->finder.p_interrupt );
154 if( p_manager->p_priv->installer.p_interrupt )
155 vlc_interrupt_destroy( p_manager->p_priv->installer.p_interrupt );
156 free( p_manager->p_priv );
157 free( p_manager );
158 return NULL;
161 #define INIT_QUEUE( name ) \
162 p_manager->p_priv->name.b_live = false;\
163 vlc_mutex_init( &p_manager->p_priv->name.lock );\
164 vlc_cond_init( &p_manager->p_priv->name.waitcond );\
165 ARRAY_INIT( p_manager->p_priv->name.entries );
167 INIT_QUEUE( finder )
168 INIT_QUEUE( installer )
169 ARRAY_INIT( p_manager->p_priv->finder.uris );
171 return p_manager;
174 void addons_manager_Delete( addons_manager_t *p_manager )
176 bool b_live;
178 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
179 b_live = p_manager->p_priv->finder.b_live;
180 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
181 if ( b_live )
183 vlc_interrupt_kill( p_manager->p_priv->finder.p_interrupt );
184 vlc_join( p_manager->p_priv->finder.thread, NULL );
187 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
188 b_live = p_manager->p_priv->installer.b_live;
189 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
190 if ( b_live )
192 vlc_interrupt_kill( p_manager->p_priv->installer.p_interrupt );
193 vlc_join( p_manager->p_priv->installer.thread, NULL );
196 #define FREE_QUEUE( name ) \
197 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->name.entries )\
198 addon_entry_Release( p_entry );\
199 FOREACH_END();\
200 ARRAY_RESET( p_manager->p_priv->name.entries );\
201 vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
202 vlc_cond_destroy( &p_manager->p_priv->name.waitcond );\
203 vlc_interrupt_destroy( p_manager->p_priv->name.p_interrupt );
205 FREE_QUEUE( finder )
206 FREE_QUEUE( installer )
207 FOREACH_ARRAY( char *psz_uri, p_manager->p_priv->finder.uris )
208 free( psz_uri );
209 FOREACH_END();
210 ARRAY_RESET( p_manager->p_priv->finder.uris );
212 free( p_manager->p_priv );
213 free( p_manager );
216 void addons_manager_Gather( addons_manager_t *p_manager, const char *psz_uri )
218 if ( !psz_uri )
219 return;
221 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
223 ARRAY_APPEND( p_manager->p_priv->finder.uris, strdup( psz_uri ) );
225 if( !p_manager->p_priv->finder.b_live )
227 if( vlc_clone( &p_manager->p_priv->finder.thread, FinderThread, p_manager,
228 VLC_THREAD_PRIORITY_LOW ) )
230 msg_Err( p_manager->p_priv->p_parent,
231 "cannot spawn entries provider thread" );
232 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
233 return;
235 p_manager->p_priv->finder.b_live = true;
238 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
239 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
242 /*****************************************************************************
243 * Private functions
244 *****************************************************************************/
246 static addon_entry_t * getHeldEntryByUUID( addons_manager_t *p_manager,
247 const addon_uuid_t uuid )
249 addon_entry_t *p_return = NULL;
250 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
251 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->finder.entries )
252 if ( !memcmp( p_entry->uuid, uuid, sizeof( addon_uuid_t ) ) )
254 p_return = p_entry;
255 addon_entry_Hold( p_return );
256 break;
258 FOREACH_END()
259 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
260 return p_return;
263 static void MergeSources( addons_manager_t *p_manager,
264 addon_entry_t **pp_addons, int i_count )
266 addon_entry_t *p_entry, *p_manager_entry;
267 addon_uuid_t zerouuid;
268 memset( zerouuid, 0, sizeof( addon_uuid_t ) );
269 for ( int i=0; i < i_count; i++ )
271 p_entry = pp_addons[i];
272 vlc_mutex_lock( &p_entry->lock );
273 if ( memcmp( p_entry->uuid, zerouuid, sizeof( addon_uuid_t ) ) )
274 p_manager_entry = getHeldEntryByUUID( p_manager, p_entry->uuid );
275 else
276 p_manager_entry = NULL;
277 if ( !p_manager_entry )
279 ARRAY_APPEND( p_manager->p_priv->finder.entries, p_entry );
280 p_manager->owner.addon_found( p_manager, p_entry );
282 else
284 vlc_mutex_lock( &p_manager_entry->lock );
285 if ( ( p_manager_entry->psz_version && p_entry->psz_version )
286 && /* FIXME: better version comparison */
287 strcmp( p_manager_entry->psz_version, p_entry->psz_version )
290 p_manager_entry->e_flags |= ADDON_UPDATABLE;
292 vlc_mutex_unlock( &p_manager_entry->lock );
293 addon_entry_Release( p_manager_entry );
295 vlc_mutex_unlock( &p_entry->lock );
299 static void LoadLocalStorage( addons_manager_t *p_manager )
301 addons_finder_t *p_finder =
302 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
303 p_finder->obj.flags |= OBJECT_FLAGS_NOINTERACT;
305 module_t *p_module = module_need( p_finder, "addons finder",
306 "addons.store.list", true );
307 if( p_module )
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 finder_thread_interrupted( void* p_data )
323 addons_manager_t *p_manager = p_data;
324 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
325 p_manager->p_priv->finder.b_live = false;
326 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
327 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
330 static void *FinderThread( void *p_data )
332 addons_manager_t *p_manager = p_data;
333 int i_cancel = vlc_savecancel();
334 vlc_interrupt_set( p_manager->p_priv->finder.p_interrupt );
336 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
337 while( p_manager->p_priv->finder.b_live )
339 char *psz_uri;
341 vlc_interrupt_register( finder_thread_interrupted, p_data );
342 while( p_manager->p_priv->finder.uris.i_size == 0 &&
343 p_manager->p_priv->finder.b_live )
345 vlc_cond_wait( &p_manager->p_priv->finder.waitcond,
346 &p_manager->p_priv->finder.lock );
348 vlc_interrupt_unregister();
349 if( !p_manager->p_priv->finder.b_live )
350 break;
351 psz_uri = p_manager->p_priv->finder.uris.p_elems[0];
352 ARRAY_REMOVE( p_manager->p_priv->finder.uris, 0 );
354 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
356 addons_finder_t *p_finder =
357 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
359 if( p_finder != NULL )
361 p_finder->obj.flags |= OBJECT_FLAGS_NOINTERACT;
362 module_t *p_module;
363 ARRAY_INIT( p_finder->entries );
364 p_finder->psz_uri = psz_uri;
366 p_module = module_need( p_finder, "addons finder", NULL, false );
367 if( p_module )
369 p_finder->pf_find( p_finder );
370 module_unneed( p_finder, p_module );
371 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
373 ARRAY_RESET( p_finder->entries );
374 free( psz_uri );
375 vlc_object_release( p_finder );
378 p_manager->owner.discovery_ended( p_manager );
379 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
382 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
383 vlc_restorecancel( i_cancel );
384 return NULL;
387 static int addons_manager_WriteCatalog( addons_manager_t *p_manager )
389 int i_return = VLC_EGENERIC;
391 addons_storage_t *p_storage =
392 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
393 p_storage->obj.flags |= OBJECT_FLAGS_NOINTERACT;
395 module_t *p_module = module_need( p_storage, "addons storage",
396 "addons.store.install", true );
397 if( p_module )
399 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
400 i_return = p_storage->pf_catalog( p_storage, p_manager->p_priv->finder.entries.p_elems,
401 p_manager->p_priv->finder.entries.i_size );
402 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
403 module_unneed( p_storage, p_module );
405 vlc_object_release( p_storage );
407 return i_return;
410 int addons_manager_LoadCatalog( addons_manager_t *p_manager )
412 LoadLocalStorage( p_manager );
413 return VLC_SUCCESS;
416 static int installOrRemoveAddon( addons_manager_t *p_manager, addon_entry_t *p_entry, bool b_install )
418 int i_return = VLC_EGENERIC;
420 addons_storage_t *p_storage =
421 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
422 p_storage->obj.flags |= OBJECT_FLAGS_NOINTERACT;
424 module_t *p_module = module_need( p_storage, "addons storage",
425 "addons.store.install", true );
426 if( p_module )
428 if ( b_install )
429 i_return = p_storage->pf_install( p_storage, p_entry );
430 else
431 i_return = p_storage->pf_remove( p_storage, p_entry );
432 module_unneed( p_storage, p_module );
433 msg_Dbg( p_manager->p_priv->p_parent, "InstallAddon returns %d", i_return );
434 if ( i_return == VLC_SUCCESS )
436 /* Reset flags */
437 vlc_mutex_lock( &p_entry->lock );
438 p_entry->e_flags = ADDON_MANAGEABLE;
439 vlc_mutex_unlock( &p_entry->lock );
442 vlc_object_release( p_storage );
444 return i_return;
447 static void installer_thread_interrupted( void* p_data )
449 addons_manager_t *p_manager = p_data;
450 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
451 p_manager->p_priv->installer.b_live = false;
452 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
453 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
456 static void *InstallerThread( void *p_data )
458 addons_manager_t *p_manager = p_data;
459 int i_cancel = vlc_savecancel();
460 vlc_interrupt_set( p_manager->p_priv->installer.p_interrupt );
461 int i_ret;
463 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
464 while( p_manager->p_priv->installer.b_live )
466 vlc_interrupt_register( installer_thread_interrupted, p_data );
467 while ( !p_manager->p_priv->installer.entries.i_size &&
468 p_manager->p_priv->installer.b_live )
470 /* No queued addons */
471 vlc_cond_wait( &p_manager->p_priv->installer.waitcond,
472 &p_manager->p_priv->installer.lock );
474 vlc_interrupt_unregister();
475 if( !p_manager->p_priv->installer.b_live )
476 break;
478 addon_entry_t *p_entry = p_manager->p_priv->installer.entries.p_elems[0];
479 ARRAY_REMOVE( p_manager->p_priv->installer.entries, 0 );
480 addon_entry_Hold( p_entry );
481 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
483 vlc_mutex_lock( &p_entry->lock );
484 /* DO WORK */
485 if ( p_entry->e_state == ADDON_INSTALLED )
487 p_entry->e_state = ADDON_UNINSTALLING;
488 vlc_mutex_unlock( &p_entry->lock );
490 /* notify */
491 p_manager->owner.addon_changed( p_manager, p_entry );
493 i_ret = installOrRemoveAddon( p_manager, p_entry, false );
495 vlc_mutex_lock( &p_entry->lock );
496 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_NOTINSTALLED
497 : ADDON_INSTALLED;
499 else if ( p_entry->e_state == ADDON_NOTINSTALLED )
501 p_entry->e_state = ADDON_INSTALLING;
502 vlc_mutex_unlock( &p_entry->lock );
504 /* notify */
505 p_manager->owner.addon_changed( p_manager, p_entry );
507 i_ret = installOrRemoveAddon( p_manager, p_entry, true );
509 vlc_mutex_lock( &p_entry->lock );
510 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_INSTALLED
511 : ADDON_NOTINSTALLED;
513 vlc_mutex_unlock( &p_entry->lock );
514 /* !DO WORK */
516 p_manager->owner.addon_changed( p_manager, p_entry );
518 addon_entry_Release( p_entry );
520 addons_manager_WriteCatalog( p_manager );
521 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
523 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
524 vlc_restorecancel( i_cancel );
525 return NULL;
528 static int InstallEntry( addons_manager_t *p_manager, addon_entry_t *p_entry )
530 if ( p_entry->e_type == ADDON_UNKNOWN ||
531 p_entry->e_type == ADDON_PLUGIN ||
532 p_entry->e_type == ADDON_OTHER )
533 return VLC_EBADVAR;
535 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
536 ARRAY_APPEND( p_manager->p_priv->installer.entries, p_entry );
537 if( !p_manager->p_priv->installer.b_live )
539 if( vlc_clone( &p_manager->p_priv->installer.thread, InstallerThread, p_manager,
540 VLC_THREAD_PRIORITY_LOW ) )
542 msg_Err( p_manager->p_priv->p_parent,
543 "cannot spawn addons installer thread" );
544 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
545 return VLC_EGENERIC;
547 else
548 p_manager->p_priv->installer.b_live = true;
550 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
551 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
552 return VLC_SUCCESS;
555 int addons_manager_Install( addons_manager_t *p_manager, const addon_uuid_t uuid )
557 addon_entry_t *p_install_entry = getHeldEntryByUUID( p_manager, uuid );
558 if ( ! p_install_entry ) return VLC_EGENERIC;
559 int i_ret = InstallEntry( p_manager, p_install_entry );
560 addon_entry_Release( p_install_entry );
561 return i_ret;
564 int addons_manager_Remove( addons_manager_t *p_manager, const addon_uuid_t uuid )
566 return addons_manager_Install( p_manager, uuid );