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 <stdatomic.h>
27 #include <vlc_common.h>
28 #include <vlc_modules.h>
29 #include <vlc_arrays.h>
30 #include <vlc_interrupt.h>
33 #include <vlc_addons.h>
35 /*****************************************************************************
36 * Structures/definitions
37 *****************************************************************************/
39 typedef struct addon_entry_owner
43 } addon_entry_owner_t
;
45 struct addons_manager_private_t
47 vlc_object_t
*p_parent
;
55 vlc_interrupt_t
*p_interrupt
;
56 DECL_ARRAY(char*) uris
;
57 DECL_ARRAY(addon_entry_t
*) entries
;
66 vlc_interrupt_t
*p_interrupt
;
67 DECL_ARRAY(addon_entry_t
*) entries
;
71 static void *FinderThread( void * );
72 static void LoadLocalStorage( addons_manager_t
*p_manager
);
74 /*****************************************************************************
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
) )
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
);
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 );
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 )
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
);
125 ARRAY_RESET( p_entry
->files
);
127 vlc_mutex_destroy( &p_entry
->lock
);
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
)
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
);
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 );
168 INIT_QUEUE( installer
)
169 ARRAY_INIT( p_manager
->p_priv
->finder
.uris
);
174 void addons_manager_Delete( addons_manager_t
*p_manager
)
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
);
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
);
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 );\
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 );
206 FREE_QUEUE( installer
)
207 FOREACH_ARRAY( char *psz_uri
, p_manager
->p_priv
->finder
.uris
)
210 ARRAY_RESET( p_manager
->p_priv
->finder
.uris
);
212 free( p_manager
->p_priv
);
216 void addons_manager_Gather( addons_manager_t
*p_manager
, const char *psz_uri
)
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
);
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 /*****************************************************************************
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
) ) )
255 addon_entry_Hold( p_return
);
259 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
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
);
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
);
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 );
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
)
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
)
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
;
363 ARRAY_INIT( p_finder
->entries
);
364 p_finder
->psz_uri
= psz_uri
;
366 p_module
= module_need( p_finder
, "addons finder", NULL
, false );
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
);
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
);
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 );
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
);
410 int addons_manager_LoadCatalog( addons_manager_t
*p_manager
)
412 LoadLocalStorage( p_manager
);
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 );
429 i_return
= p_storage
->pf_install( p_storage
, p_entry
);
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
)
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
);
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
);
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
)
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
);
485 if ( p_entry
->e_state
== ADDON_INSTALLED
)
487 p_entry
->e_state
= ADDON_UNINSTALLING
;
488 vlc_mutex_unlock( &p_entry
->lock
);
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
499 else if ( p_entry
->e_state
== ADDON_NOTINSTALLED
)
501 p_entry
->e_state
= ADDON_INSTALLING
;
502 vlc_mutex_unlock( &p_entry
->lock
);
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
);
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
);
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
)
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
);
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
);
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
);
564 int addons_manager_Remove( addons_manager_t
*p_manager
, const addon_uuid_t uuid
)
566 return addons_manager_Install( p_manager
, uuid
);