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>
31 #include <vlc_addons.h>
33 /*****************************************************************************
34 * Structures/definitions
35 *****************************************************************************/
37 typedef struct addon_entry_owner
41 } addon_entry_owner_t
;
43 struct addons_manager_private_t
45 vlc_object_t
*p_parent
;
53 DECL_ARRAY(char*) uris
;
54 DECL_ARRAY(addon_entry_t
*) entries
;
63 DECL_ARRAY(addon_entry_t
*) entries
;
67 static void *FinderThread( void * );
68 static void LoadLocalStorage( addons_manager_t
*p_manager
);
70 /*****************************************************************************
72 *****************************************************************************/
74 addon_entry_t
* addon_entry_New(void)
76 addon_entry_owner_t
*owner
= calloc( 1, sizeof(addon_entry_owner_t
) );
77 if( unlikely(owner
== NULL
) )
80 atomic_init( &owner
->refs
, 1 );
82 addon_entry_t
*p_entry
= &owner
->entry
;
83 vlc_mutex_init( &p_entry
->lock
);
84 ARRAY_INIT( p_entry
->files
);
88 addon_entry_t
* addon_entry_Hold( addon_entry_t
* p_entry
)
90 addon_entry_owner_t
*owner
= (addon_entry_owner_t
*) p_entry
;
92 atomic_fetch_add( &owner
->refs
, 1 );
96 void addon_entry_Release( addon_entry_t
* p_entry
)
98 addon_entry_owner_t
*owner
= (addon_entry_owner_t
*) p_entry
;
100 if( atomic_fetch_sub(&owner
->refs
, 1) != 1 )
103 free( p_entry
->psz_name
);
104 free( p_entry
->psz_summary
);
105 free( p_entry
->psz_description
);
106 free( p_entry
->psz_archive_uri
);
107 free( p_entry
->psz_author
);
108 free( p_entry
->psz_source_uri
);
109 free( p_entry
->psz_image_uri
);
110 free( p_entry
->psz_image_data
);
111 free( p_entry
->psz_source_module
);
112 free( p_entry
->psz_version
);
113 free( p_entry
->p_custom
);
115 addon_file_t
*p_file
;
116 FOREACH_ARRAY( p_file
, p_entry
->files
)
117 free( p_file
->psz_filename
);
118 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
,
128 const struct addons_manager_owner
*restrict owner
)
130 addons_manager_t
*p_manager
= malloc( sizeof(addons_manager_t
) );
131 if ( !p_manager
) return NULL
;
133 p_manager
->p_priv
= malloc( sizeof(addons_manager_private_t
) );
134 if ( !p_manager
->p_priv
)
140 p_manager
->owner
= *owner
;
141 p_manager
->p_priv
->p_parent
= p_this
;
143 #define INIT_QUEUE( name ) \
144 p_manager->p_priv->name.b_live = false;\
145 vlc_mutex_init( &p_manager->p_priv->name.lock );\
146 vlc_cond_init( &p_manager->p_priv->name.waitcond );\
147 ARRAY_INIT( p_manager->p_priv->name.entries );
150 INIT_QUEUE( installer
)
151 ARRAY_INIT( p_manager
->p_priv
->finder
.uris
);
156 void addons_manager_Delete( addons_manager_t
*p_manager
)
160 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
161 b_live
= p_manager
->p_priv
->finder
.b_live
;
162 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
165 vlc_cancel( p_manager
->p_priv
->finder
.thread
);
166 vlc_join( p_manager
->p_priv
->finder
.thread
, NULL
);
169 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
170 b_live
= p_manager
->p_priv
->installer
.b_live
;
171 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
174 vlc_cancel( p_manager
->p_priv
->installer
.thread
);
175 vlc_join( p_manager
->p_priv
->installer
.thread
, NULL
);
178 #define FREE_QUEUE( name ) \
179 FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->name.entries )\
180 addon_entry_Release( p_entry );\
182 ARRAY_RESET( p_manager->p_priv->name.entries );\
183 vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
184 vlc_cond_destroy( &p_manager->p_priv->name.waitcond );
187 FREE_QUEUE( installer
)
188 FOREACH_ARRAY( char *psz_uri
, p_manager
->p_priv
->finder
.uris
)
191 ARRAY_RESET( p_manager
->p_priv
->finder
.uris
);
193 free( p_manager
->p_priv
);
197 void addons_manager_Gather( addons_manager_t
*p_manager
, const char *psz_uri
)
202 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
204 ARRAY_APPEND( p_manager
->p_priv
->finder
.uris
, strdup( psz_uri
) );
206 if( !p_manager
->p_priv
->finder
.b_live
)
208 if( vlc_clone( &p_manager
->p_priv
->finder
.thread
, FinderThread
, p_manager
,
209 VLC_THREAD_PRIORITY_LOW
) )
211 msg_Err( p_manager
->p_priv
->p_parent
,
212 "cannot spawn entries provider thread" );
213 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
216 p_manager
->p_priv
->finder
.b_live
= true;
219 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
220 vlc_cond_signal( &p_manager
->p_priv
->finder
.waitcond
);
223 /*****************************************************************************
225 *****************************************************************************/
227 static addon_entry_t
* getHeldEntryByUUID( addons_manager_t
*p_manager
,
228 const addon_uuid_t uuid
)
230 addon_entry_t
*p_return
= NULL
;
231 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
232 FOREACH_ARRAY( addon_entry_t
*p_entry
, p_manager
->p_priv
->finder
.entries
)
233 if ( !memcmp( p_entry
->uuid
, uuid
, sizeof( addon_uuid_t
) ) )
236 addon_entry_Hold( p_return
);
240 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
244 static void MergeSources( addons_manager_t
*p_manager
,
245 addon_entry_t
**pp_addons
, int i_count
)
247 addon_entry_t
*p_entry
, *p_manager_entry
;
248 addon_uuid_t zerouuid
;
249 memset( zerouuid
, 0, sizeof( addon_uuid_t
) );
250 for ( int i
=0; i
< i_count
; i
++ )
252 p_entry
= pp_addons
[i
];
253 vlc_mutex_lock( &p_entry
->lock
);
254 if ( memcmp( p_entry
->uuid
, zerouuid
, sizeof( addon_uuid_t
) ) )
255 p_manager_entry
= getHeldEntryByUUID( p_manager
, p_entry
->uuid
);
257 p_manager_entry
= NULL
;
258 if ( !p_manager_entry
)
260 ARRAY_APPEND( p_manager
->p_priv
->finder
.entries
, p_entry
);
261 p_manager
->owner
.addon_found( p_manager
, p_entry
);
265 vlc_mutex_lock( &p_manager_entry
->lock
);
266 if ( ( p_manager_entry
->psz_version
&& p_entry
->psz_version
)
267 && /* FIXME: better version comparison */
268 strcmp( p_manager_entry
->psz_version
, p_entry
->psz_version
)
271 p_manager_entry
->e_flags
|= ADDON_UPDATABLE
;
273 vlc_mutex_unlock( &p_manager_entry
->lock
);
274 addon_entry_Release( p_manager_entry
);
276 vlc_mutex_unlock( &p_entry
->lock
);
280 static void LoadLocalStorage( addons_manager_t
*p_manager
)
282 addons_finder_t
*p_finder
=
283 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_finder
), "entries finder" );
284 p_finder
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
286 module_t
*p_module
= module_need( p_finder
, "addons finder",
287 "addons.store.list", true );
290 ARRAY_INIT( p_finder
->entries
);
291 p_finder
->psz_uri
= NULL
;
292 p_finder
->pf_find( p_finder
);
293 module_unneed( p_finder
, p_module
);
295 MergeSources( p_manager
, p_finder
->entries
.p_elems
, p_finder
->entries
.i_size
);
297 ARRAY_RESET( p_finder
->entries
);
299 vlc_object_release( p_finder
);
302 static void *FinderThread( void *p_data
)
304 addons_manager_t
*p_manager
= p_data
;
310 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
311 mutex_cleanup_push( &p_manager
->p_priv
->finder
.lock
);
312 while( p_manager
->p_priv
->finder
.uris
.i_size
== 0 )
314 vlc_cond_wait( &p_manager
->p_priv
->finder
.waitcond
,
315 &p_manager
->p_priv
->finder
.lock
);
317 psz_uri
= p_manager
->p_priv
->finder
.uris
.p_elems
[0];
318 ARRAY_REMOVE( p_manager
->p_priv
->finder
.uris
, 0 );
320 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
322 int i_cancel
= vlc_savecancel();
324 addons_finder_t
*p_finder
=
325 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_finder
), "entries finder" );
327 if( p_finder
!= NULL
)
329 p_finder
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
331 ARRAY_INIT( p_finder
->entries
);
332 p_finder
->psz_uri
= psz_uri
;
334 p_module
= module_need( p_finder
, "addons finder", NULL
, false );
337 p_finder
->pf_find( p_finder
);
338 module_unneed( p_finder
, p_module
);
339 MergeSources( p_manager
, p_finder
->entries
.p_elems
, p_finder
->entries
.i_size
);
341 ARRAY_RESET( p_finder
->entries
);
343 vlc_object_release( p_finder
);
346 p_manager
->owner
.discovery_ended( p_manager
);
347 vlc_restorecancel( i_cancel
);
354 static int addons_manager_WriteCatalog( addons_manager_t
*p_manager
)
356 int i_return
= VLC_EGENERIC
;
358 addons_storage_t
*p_storage
=
359 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_storage
), "entries storage" );
360 p_storage
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
362 module_t
*p_module
= module_need( p_storage
, "addons storage",
363 "addons.store.install", true );
366 vlc_mutex_lock( &p_manager
->p_priv
->finder
.lock
);
367 i_return
= p_storage
->pf_catalog( p_storage
, p_manager
->p_priv
->finder
.entries
.p_elems
,
368 p_manager
->p_priv
->finder
.entries
.i_size
);
369 vlc_mutex_unlock( &p_manager
->p_priv
->finder
.lock
);
370 module_unneed( p_storage
, p_module
);
372 vlc_object_release( p_storage
);
377 int addons_manager_LoadCatalog( addons_manager_t
*p_manager
)
379 LoadLocalStorage( p_manager
);
383 static int installOrRemoveAddon( addons_manager_t
*p_manager
, addon_entry_t
*p_entry
, bool b_install
)
385 int i_return
= VLC_EGENERIC
;
387 addons_storage_t
*p_storage
=
388 vlc_custom_create( p_manager
->p_priv
->p_parent
, sizeof( *p_storage
), "entries storage" );
389 p_storage
->obj
.flags
|= OBJECT_FLAGS_NOINTERACT
;
391 module_t
*p_module
= module_need( p_storage
, "addons storage",
392 "addons.store.install", true );
396 i_return
= p_storage
->pf_install( p_storage
, p_entry
);
398 i_return
= p_storage
->pf_remove( p_storage
, p_entry
);
399 module_unneed( p_storage
, p_module
);
400 msg_Dbg( p_manager
->p_priv
->p_parent
, "InstallAddon returns %d", i_return
);
401 if ( i_return
== VLC_SUCCESS
)
404 vlc_mutex_lock( &p_entry
->lock
);
405 p_entry
->e_flags
= ADDON_MANAGEABLE
;
406 vlc_mutex_unlock( &p_entry
->lock
);
409 vlc_object_release( p_storage
);
414 static void *InstallerThread( void *p_data
)
416 addons_manager_t
*p_manager
= p_data
;
421 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
422 mutex_cleanup_push( &p_manager
->p_priv
->installer
.lock
);
423 while ( !p_manager
->p_priv
->installer
.entries
.i_size
)
425 /* No queued addons */
426 vlc_cond_wait( &p_manager
->p_priv
->installer
.waitcond
,
427 &p_manager
->p_priv
->installer
.lock
);
431 addon_entry_t
*p_entry
= p_manager
->p_priv
->installer
.entries
.p_elems
[0];
432 ARRAY_REMOVE( p_manager
->p_priv
->installer
.entries
, 0 );
433 addon_entry_Hold( p_entry
);
434 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
436 int i_cancel
= vlc_savecancel();
437 vlc_mutex_lock( &p_entry
->lock
);
439 if ( p_entry
->e_state
== ADDON_INSTALLED
)
441 p_entry
->e_state
= ADDON_UNINSTALLING
;
442 vlc_mutex_unlock( &p_entry
->lock
);
445 p_manager
->owner
.addon_changed( p_manager
, p_entry
);
447 i_ret
= installOrRemoveAddon( p_manager
, p_entry
, false );
449 vlc_mutex_lock( &p_entry
->lock
);
450 p_entry
->e_state
= ( i_ret
== VLC_SUCCESS
) ? ADDON_NOTINSTALLED
453 else if ( p_entry
->e_state
== ADDON_NOTINSTALLED
)
455 p_entry
->e_state
= ADDON_INSTALLING
;
456 vlc_mutex_unlock( &p_entry
->lock
);
459 p_manager
->owner
.addon_changed( p_manager
, p_entry
);
461 i_ret
= installOrRemoveAddon( p_manager
, p_entry
, true );
463 vlc_mutex_lock( &p_entry
->lock
);
464 p_entry
->e_state
= ( i_ret
== VLC_SUCCESS
) ? ADDON_INSTALLED
465 : ADDON_NOTINSTALLED
;
467 vlc_mutex_unlock( &p_entry
->lock
);
470 p_manager
->owner
.addon_changed( p_manager
, p_entry
);
472 addon_entry_Release( p_entry
);
474 addons_manager_WriteCatalog( p_manager
);
475 vlc_restorecancel( i_cancel
);
481 static int InstallEntry( addons_manager_t
*p_manager
, addon_entry_t
*p_entry
)
483 if ( p_entry
->e_type
== ADDON_UNKNOWN
||
484 p_entry
->e_type
== ADDON_PLUGIN
||
485 p_entry
->e_type
== ADDON_OTHER
)
488 vlc_mutex_lock( &p_manager
->p_priv
->installer
.lock
);
489 ARRAY_APPEND( p_manager
->p_priv
->installer
.entries
, p_entry
);
490 if( !p_manager
->p_priv
->installer
.b_live
)
492 if( vlc_clone( &p_manager
->p_priv
->installer
.thread
, InstallerThread
, p_manager
,
493 VLC_THREAD_PRIORITY_LOW
) )
495 msg_Err( p_manager
->p_priv
->p_parent
,
496 "cannot spawn addons installer thread" );
497 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
501 p_manager
->p_priv
->installer
.b_live
= true;
503 vlc_mutex_unlock( &p_manager
->p_priv
->installer
.lock
);
504 vlc_cond_signal( &p_manager
->p_priv
->installer
.waitcond
);
508 int addons_manager_Install( addons_manager_t
*p_manager
, const addon_uuid_t uuid
)
510 addon_entry_t
*p_install_entry
= getHeldEntryByUUID( p_manager
, uuid
);
511 if ( ! p_install_entry
) return VLC_EGENERIC
;
512 int i_ret
= InstallEntry( p_manager
, p_install_entry
);
513 addon_entry_Release( p_install_entry
);
517 int addons_manager_Remove( addons_manager_t
*p_manager
, const addon_uuid_t uuid
)
519 return addons_manager_Install( p_manager
, uuid
);