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_interrupt.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 vlc_interrupt_t
*p_interrupt
;
55 DECL_ARRAY(char*) uris
;
56 DECL_ARRAY(addon_entry_t
*) entries
;
65 vlc_interrupt_t
*p_interrupt
;
66 DECL_ARRAY(addon_entry_t
*) entries
;
70 static void *FinderThread( void * );
71 static void LoadLocalStorage( addons_manager_t
*p_manager
);
73 /*****************************************************************************
75 *****************************************************************************/
77 addon_entry_t
* addon_entry_New(void)
79 addon_entry_owner_t
*owner
= calloc( 1, sizeof(addon_entry_owner_t
) );
80 if( unlikely(owner
== NULL
) )
83 atomic_init( &owner
->refs
, 1 );
85 addon_entry_t
*p_entry
= &owner
->entry
;
86 vlc_mutex_init( &p_entry
->lock
);
87 ARRAY_INIT( p_entry
->files
);
91 addon_entry_t
* addon_entry_Hold( addon_entry_t
* p_entry
)
93 addon_entry_owner_t
*owner
= (addon_entry_owner_t
*) p_entry
;
95 atomic_fetch_add( &owner
->refs
, 1 );
99 void addon_entry_Release( addon_entry_t
* p_entry
)
101 addon_entry_owner_t
*owner
= (addon_entry_owner_t
*) p_entry
;
103 if( atomic_fetch_sub(&owner
->refs
, 1) != 1 )
106 free( p_entry
->psz_name
);
107 free( p_entry
->psz_summary
);
108 free( p_entry
->psz_description
);
109 free( p_entry
->psz_archive_uri
);
110 free( p_entry
->psz_author
);
111 free( p_entry
->psz_source_uri
);
112 free( p_entry
->psz_image_uri
);
113 free( p_entry
->psz_image_data
);
114 free( p_entry
->psz_source_module
);
115 free( p_entry
->psz_version
);
116 free( p_entry
->p_custom
);
118 addon_file_t
*p_file
;
119 FOREACH_ARRAY( p_file
, p_entry
->files
)
120 free( p_file
->psz_filename
);
121 free( p_file
->psz_download_uri
);
124 ARRAY_RESET( p_entry
->files
);
126 vlc_mutex_destroy( &p_entry
->lock
);
130 addons_manager_t
*addons_manager_New( vlc_object_t
*p_this
,
131 const struct addons_manager_owner
*restrict owner
)
133 addons_manager_t
*p_manager
= malloc( sizeof(addons_manager_t
) );
134 if ( !p_manager
) return NULL
;
136 p_manager
->p_priv
= malloc( sizeof(addons_manager_private_t
) );
137 if ( !p_manager
->p_priv
)
143 p_manager
->owner
= *owner
;
144 p_manager
->p_priv
->p_parent
= p_this
;
146 p_manager
->p_priv
->finder
.p_interrupt
= vlc_interrupt_create();
147 p_manager
->p_priv
->installer
.p_interrupt
= vlc_interrupt_create();
148 if ( !p_manager
->p_priv
->finder
.p_interrupt
||
149 !p_manager
->p_priv
->installer
.p_interrupt
)
151 if( p_manager
->p_priv
->finder
.p_interrupt
)
152 vlc_interrupt_destroy( p_manager
->p_priv
->finder
.p_interrupt
);
153 if( p_manager
->p_priv
->installer
.p_interrupt
)
154 vlc_interrupt_destroy( p_manager
->p_priv
->installer
.p_interrupt
);
155 free( p_manager
->p_priv
);
160 #define INIT_QUEUE( name ) \
161 p_manager->p_priv->name.b_live = false;\
162 vlc_mutex_init( &p_manager->p_priv->name.lock );\
163 vlc_cond_init( &p_manager->p_priv->name.waitcond );\
164 ARRAY_INIT( p_manager->p_priv->name.entries );
167 INIT_QUEUE( installer
)
168 ARRAY_INIT( p_manager
->p_priv
->finder
.uris
);
173 void addons_manager_Delete( addons_manager_t
*p_manager
)
177 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
178 b_live
= p_manager
->p_priv
->finder
.b_live
;
179 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
182 vlc_interrupt_kill( p_manager
->p_priv
->finder
.p_interrupt
);
183 vlc_join( p_manager
->p_priv
->finder
.thread
, NULL
);
186 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
187 b_live
= p_manager
->p_priv
->installer
.b_live
;
188 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
191 vlc_interrupt_kill( p_manager
->p_priv
->installer
.p_interrupt
);
192 vlc_join( p_manager
->p_priv
->installer
.thread
, NULL
);
195 #define FREE_QUEUE( name ) \
196 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->name.entries )\
197 addon_entry_Release( p_entry );\
199 ARRAY_RESET( p_manager->p_priv->name.entries );\
200 vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
201 vlc_cond_destroy( &p_manager->p_priv->name.waitcond );\
202 vlc_interrupt_destroy( p_manager->p_priv->name.p_interrupt );
205 FREE_QUEUE( installer
)
206 FOREACH_ARRAY( char *psz_uri
, p_manager
->p_priv
->finder
.uris
)
209 ARRAY_RESET( p_manager
->p_priv
->finder
.uris
);
211 free( p_manager
->p_priv
);
215 void addons_manager_Gather( addons_manager_t
*p_manager
, const char *psz_uri
)
220 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
222 ARRAY_APPEND( p_manager
->p_priv
->finder
.uris
, strdup( psz_uri
) );
224 if( !p_manager
->p_priv
->finder
.b_live
)
226 if( vlc_clone( &p_manager
->p_priv
->finder
.thread
, FinderThread
, p_manager
,
227 VLC_THREAD_PRIORITY_LOW
) )
229 msg_Err( p_manager
->p_priv
->p_parent
,
230 "cannot spawn entries provider thread" );
231 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
234 p_manager
->p_priv
->finder
.b_live
= true;
237 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
238 vlc_cond_signal( &p_manager
->p_priv
->finder
.waitcond
);
241 /*****************************************************************************
243 *****************************************************************************/
245 static addon_entry_t
* getHeldEntryByUUID( addons_manager_t
*p_manager
,
246 const addon_uuid_t uuid
)
248 addon_entry_t
*p_return
= NULL
;
249 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
250 FOREACH_ARRAY( addon_entry_t
*p_entry
, p_manager
->p_priv
->finder
.entries
)
251 if ( !memcmp( p_entry
->uuid
, uuid
, sizeof( addon_uuid_t
) ) )
254 addon_entry_Hold( p_return
);
258 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
262 static void MergeSources( addons_manager_t
*p_manager
,
263 addon_entry_t
**pp_addons
, int i_count
)
265 addon_entry_t
*p_entry
, *p_manager_entry
;
266 addon_uuid_t zerouuid
;
267 memset( zerouuid
, 0, sizeof( addon_uuid_t
) );
268 for ( int i
=0; i
< i_count
; i
++ )
270 p_entry
= pp_addons
[i
];
271 vlc_mutex_lock( &p_entry
->lock
);
272 if ( memcmp( p_entry
->uuid
, zerouuid
, sizeof( addon_uuid_t
) ) )
273 p_manager_entry
= getHeldEntryByUUID( p_manager
, p_entry
->uuid
);
275 p_manager_entry
= NULL
;
276 if ( !p_manager_entry
)
278 ARRAY_APPEND( p_manager
->p_priv
->finder
.entries
, p_entry
);
279 p_manager
->owner
.addon_found( p_manager
, p_entry
);
283 vlc_mutex_lock( &p_manager_entry
->lock
);
284 if ( ( p_manager_entry
->psz_version
&& p_entry
->psz_version
)
285 && /* FIXME: better version comparison */
286 strcmp( p_manager_entry
->psz_version
, p_entry
->psz_version
)
289 p_manager_entry
->e_flags
|= ADDON_UPDATABLE
;
291 vlc_mutex_unlock( &p_manager_entry
->lock
);
292 addon_entry_Release( p_manager_entry
);
294 vlc_mutex_unlock( &p_entry
->lock
);
298 static void LoadLocalStorage( addons_manager_t
*p_manager
)
300 addons_finder_t
*p_finder
=
301 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_finder
), "entries finder" );
302 p_finder
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
304 module_t
*p_module
= module_need( p_finder
, "addons finder",
305 "addons.store.list", true );
308 ARRAY_INIT( p_finder
->entries
);
309 p_finder
->psz_uri
= NULL
;
310 p_finder
->pf_find( p_finder
);
311 module_unneed( p_finder
, p_module
);
313 MergeSources( p_manager
, p_finder
->entries
.p_elems
, p_finder
->entries
.i_size
);
315 ARRAY_RESET( p_finder
->entries
);
317 vlc_object_release( p_finder
);
320 static void finder_thread_interrupted( void* p_data
)
322 addons_manager_t
*p_manager
= p_data
;
323 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
324 p_manager
->p_priv
->finder
.b_live
= false;
325 vlc_cond_signal( &p_manager
->p_priv
->finder
.waitcond
);
326 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
329 static void *FinderThread( void *p_data
)
331 addons_manager_t
*p_manager
= p_data
;
332 int i_cancel
= vlc_savecancel();
333 vlc_interrupt_set( p_manager
->p_priv
->finder
.p_interrupt
);
335 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
336 while( p_manager
->p_priv
->finder
.b_live
)
340 vlc_interrupt_register( finder_thread_interrupted
, p_data
);
341 while( p_manager
->p_priv
->finder
.uris
.i_size
== 0 &&
342 p_manager
->p_priv
->finder
.b_live
)
344 vlc_cond_wait( &p_manager
->p_priv
->finder
.waitcond
,
345 &p_manager
->p_priv
->finder
.lock
);
347 vlc_interrupt_unregister();
348 if( !p_manager
->p_priv
->finder
.b_live
)
350 psz_uri
= p_manager
->p_priv
->finder
.uris
.p_elems
[0];
351 ARRAY_REMOVE( p_manager
->p_priv
->finder
.uris
, 0 );
353 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
355 addons_finder_t
*p_finder
=
356 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_finder
), "entries finder" );
358 if( p_finder
!= NULL
)
360 p_finder
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
362 ARRAY_INIT( p_finder
->entries
);
363 p_finder
->psz_uri
= psz_uri
;
365 p_module
= module_need( p_finder
, "addons finder", NULL
, false );
368 p_finder
->pf_find( p_finder
);
369 module_unneed( p_finder
, p_module
);
370 MergeSources( p_manager
, p_finder
->entries
.p_elems
, p_finder
->entries
.i_size
);
372 ARRAY_RESET( p_finder
->entries
);
374 vlc_object_release( p_finder
);
377 p_manager
->owner
.discovery_ended( p_manager
);
378 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
381 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
382 vlc_restorecancel( i_cancel
);
386 static int addons_manager_WriteCatalog( addons_manager_t
*p_manager
)
388 int i_return
= VLC_EGENERIC
;
390 addons_storage_t
*p_storage
=
391 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_storage
), "entries storage" );
392 p_storage
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
394 module_t
*p_module
= module_need( p_storage
, "addons storage",
395 "addons.store.install", true );
398 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
399 i_return
= p_storage
->pf_catalog( p_storage
, p_manager
->p_priv
->finder
.entries
.p_elems
,
400 p_manager
->p_priv
->finder
.entries
.i_size
);
401 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
402 module_unneed( p_storage
, p_module
);
404 vlc_object_release( p_storage
);
409 int addons_manager_LoadCatalog( addons_manager_t
*p_manager
)
411 LoadLocalStorage( p_manager
);
415 static int installOrRemoveAddon( addons_manager_t
*p_manager
, addon_entry_t
*p_entry
, bool b_install
)
417 int i_return
= VLC_EGENERIC
;
419 addons_storage_t
*p_storage
=
420 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_storage
), "entries storage" );
421 p_storage
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
423 module_t
*p_module
= module_need( p_storage
, "addons storage",
424 "addons.store.install", true );
428 i_return
= p_storage
->pf_install( p_storage
, p_entry
);
430 i_return
= p_storage
->pf_remove( p_storage
, p_entry
);
431 module_unneed( p_storage
, p_module
);
432 msg_Dbg( p_manager
->p_priv
->p_parent
, "InstallAddon returns %d", i_return
);
433 if ( i_return
== VLC_SUCCESS
)
436 vlc_mutex_lock( &p_entry
->lock
);
437 p_entry
->e_flags
= ADDON_MANAGEABLE
;
438 vlc_mutex_unlock( &p_entry
->lock
);
441 vlc_object_release( p_storage
);
446 static void installer_thread_interrupted( void* p_data
)
448 addons_manager_t
*p_manager
= p_data
;
449 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
450 p_manager
->p_priv
->installer
.b_live
= false;
451 vlc_cond_signal( &p_manager
->p_priv
->installer
.waitcond
);
452 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
455 static void *InstallerThread( void *p_data
)
457 addons_manager_t
*p_manager
= p_data
;
458 int i_cancel
= vlc_savecancel();
459 vlc_interrupt_set( p_manager
->p_priv
->installer
.p_interrupt
);
462 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
463 while( p_manager
->p_priv
->installer
.b_live
)
465 vlc_interrupt_register( installer_thread_interrupted
, p_data
);
466 while ( !p_manager
->p_priv
->installer
.entries
.i_size
&&
467 p_manager
->p_priv
->installer
.b_live
)
469 /* No queued addons */
470 vlc_cond_wait( &p_manager
->p_priv
->installer
.waitcond
,
471 &p_manager
->p_priv
->installer
.lock
);
473 vlc_interrupt_unregister();
474 if( !p_manager
->p_priv
->installer
.b_live
)
477 addon_entry_t
*p_entry
= p_manager
->p_priv
->installer
.entries
.p_elems
[0];
478 ARRAY_REMOVE( p_manager
->p_priv
->installer
.entries
, 0 );
479 addon_entry_Hold( p_entry
);
480 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
482 vlc_mutex_lock( &p_entry
->lock
);
484 if ( p_entry
->e_state
== ADDON_INSTALLED
)
486 p_entry
->e_state
= ADDON_UNINSTALLING
;
487 vlc_mutex_unlock( &p_entry
->lock
);
490 p_manager
->owner
.addon_changed( p_manager
, p_entry
);
492 i_ret
= installOrRemoveAddon( p_manager
, p_entry
, false );
494 vlc_mutex_lock( &p_entry
->lock
);
495 p_entry
->e_state
= ( i_ret
== VLC_SUCCESS
) ? ADDON_NOTINSTALLED
498 else if ( p_entry
->e_state
== ADDON_NOTINSTALLED
)
500 p_entry
->e_state
= ADDON_INSTALLING
;
501 vlc_mutex_unlock( &p_entry
->lock
);
504 p_manager
->owner
.addon_changed( p_manager
, p_entry
);
506 i_ret
= installOrRemoveAddon( p_manager
, p_entry
, true );
508 vlc_mutex_lock( &p_entry
->lock
);
509 p_entry
->e_state
= ( i_ret
== VLC_SUCCESS
) ? ADDON_INSTALLED
510 : ADDON_NOTINSTALLED
;
512 vlc_mutex_unlock( &p_entry
->lock
);
515 p_manager
->owner
.addon_changed( p_manager
, p_entry
);
517 addon_entry_Release( p_entry
);
519 addons_manager_WriteCatalog( p_manager
);
520 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
522 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
523 vlc_restorecancel( i_cancel
);
527 static int InstallEntry( addons_manager_t
*p_manager
, addon_entry_t
*p_entry
)
529 if ( p_entry
->e_type
== ADDON_UNKNOWN
||
530 p_entry
->e_type
== ADDON_PLUGIN
||
531 p_entry
->e_type
== ADDON_OTHER
)
534 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
535 ARRAY_APPEND( p_manager
->p_priv
->installer
.entries
, p_entry
);
536 if( !p_manager
->p_priv
->installer
.b_live
)
538 if( vlc_clone( &p_manager
->p_priv
->installer
.thread
, InstallerThread
, p_manager
,
539 VLC_THREAD_PRIORITY_LOW
) )
541 msg_Err( p_manager
->p_priv
->p_parent
,
542 "cannot spawn addons installer thread" );
543 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
547 p_manager
->p_priv
->installer
.b_live
= true;
549 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
550 vlc_cond_signal( &p_manager
->p_priv
->installer
.waitcond
);
554 int addons_manager_Install( addons_manager_t
*p_manager
, const addon_uuid_t uuid
)
556 addon_entry_t
*p_install_entry
= getHeldEntryByUUID( p_manager
, uuid
);
557 if ( ! p_install_entry
) return VLC_EGENERIC
;
558 int i_ret
= InstallEntry( p_manager
, p_install_entry
);
559 addon_entry_Release( p_install_entry
);
563 int addons_manager_Remove( addons_manager_t
*p_manager
, const addon_uuid_t uuid
)
565 return addons_manager_Install( p_manager
, uuid
);