Fortunes about QT and "hearing" video codecs
[vlc.git] / src / misc / addons.c
blob1ec38c642bc954f6b0c0850040af2ff995969ae6
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 <vlc_common.h>
26 #include <vlc_atomic.h>
27 #include <vlc_modules.h>
28 #include <vlc_arrays.h>
29 #include <vlc_interrupt.h>
30 #include "libvlc.h"
32 #include <vlc_addons.h>
34 /*****************************************************************************
35 * Structures/definitions
36 *****************************************************************************/
38 typedef struct addon_entry_owner
40 addon_entry_t entry;
41 vlc_atomic_rc_t rc;
42 } addon_entry_owner_t;
44 struct addons_manager_private_t
46 vlc_object_t *p_parent;
48 struct
50 vlc_thread_t thread;
51 vlc_cond_t waitcond;
52 bool b_live;
53 vlc_mutex_t lock;
54 vlc_interrupt_t *p_interrupt;
55 DECL_ARRAY(char*) uris;
56 DECL_ARRAY(addon_entry_t*) entries;
57 } finder;
59 struct
61 vlc_thread_t thread;
62 vlc_cond_t waitcond;
63 bool b_live;
64 vlc_mutex_t lock;
65 vlc_interrupt_t *p_interrupt;
66 DECL_ARRAY(addon_entry_t*) entries;
67 } installer;
70 static void *FinderThread( void * );
71 static void LoadLocalStorage( addons_manager_t *p_manager );
73 /*****************************************************************************
74 * Public functions
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) )
81 return NULL;
83 vlc_atomic_rc_init( &owner->rc );
85 addon_entry_t *p_entry = &owner->entry;
86 vlc_mutex_init( &p_entry->lock );
87 ARRAY_INIT( p_entry->files );
88 return p_entry;
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 vlc_atomic_rc_inc( &owner->rc );
96 return p_entry;
99 void addon_entry_Release( addon_entry_t * p_entry )
101 addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
103 if( !vlc_atomic_rc_dec( &owner->rc ) )
104 return;
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 ARRAY_FOREACH( p_file, p_entry->files )
121 free( p_file->psz_filename );
122 free( p_file->psz_download_uri );
123 free( p_file );
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 addon_entry_t *p_entry;
198 #define FREE_QUEUE( name ) \
199 ARRAY_FOREACH( p_entry, p_manager->p_priv->name.entries )\
200 addon_entry_Release( p_entry );\
201 ARRAY_RESET( p_manager->p_priv->name.entries );\
202 vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
203 vlc_cond_destroy( &p_manager->p_priv->name.waitcond );\
204 vlc_interrupt_destroy( p_manager->p_priv->name.p_interrupt );
206 FREE_QUEUE( finder )
207 FREE_QUEUE( installer )
209 char *psz_uri;
210 ARRAY_FOREACH( psz_uri, p_manager->p_priv->finder.uris )
211 free( psz_uri );
212 ARRAY_RESET( p_manager->p_priv->finder.uris );
214 free( p_manager->p_priv );
215 free( p_manager );
218 void addons_manager_Gather( addons_manager_t *p_manager, const char *psz_uri )
220 if ( !psz_uri )
221 return;
223 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
225 ARRAY_APPEND( p_manager->p_priv->finder.uris, strdup( psz_uri ) );
227 if( !p_manager->p_priv->finder.b_live )
229 if( vlc_clone( &p_manager->p_priv->finder.thread, FinderThread, p_manager,
230 VLC_THREAD_PRIORITY_LOW ) )
232 msg_Err( p_manager->p_priv->p_parent,
233 "cannot spawn entries provider thread" );
234 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
235 return;
237 p_manager->p_priv->finder.b_live = true;
240 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
241 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
244 /*****************************************************************************
245 * Private functions
246 *****************************************************************************/
248 static addon_entry_t * getHeldEntryByUUID( addons_manager_t *p_manager,
249 const addon_uuid_t uuid )
251 addon_entry_t *p_return = NULL;
252 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
253 addon_entry_t *p_entry;
254 ARRAY_FOREACH( p_entry, p_manager->p_priv->finder.entries )
256 if ( !memcmp( p_entry->uuid, uuid, sizeof( addon_uuid_t ) ) )
258 p_return = p_entry;
259 addon_entry_Hold( p_return );
260 break;
263 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
264 return p_return;
267 static void MergeSources( addons_manager_t *p_manager,
268 addon_entry_t **pp_addons, int i_count )
270 addon_entry_t *p_entry, *p_manager_entry;
271 addon_uuid_t zerouuid;
272 memset( zerouuid, 0, sizeof( addon_uuid_t ) );
273 for ( int i=0; i < i_count; i++ )
275 p_entry = pp_addons[i];
276 vlc_mutex_lock( &p_entry->lock );
277 if ( memcmp( p_entry->uuid, zerouuid, sizeof( addon_uuid_t ) ) )
278 p_manager_entry = getHeldEntryByUUID( p_manager, p_entry->uuid );
279 else
280 p_manager_entry = NULL;
281 if ( !p_manager_entry )
283 ARRAY_APPEND( p_manager->p_priv->finder.entries, p_entry );
284 p_manager->owner.addon_found( p_manager, p_entry );
286 else
288 vlc_mutex_lock( &p_manager_entry->lock );
289 if ( ( p_manager_entry->psz_version && p_entry->psz_version )
290 && /* FIXME: better version comparison */
291 strcmp( p_manager_entry->psz_version, p_entry->psz_version )
294 p_manager_entry->e_flags |= ADDON_UPDATABLE;
296 vlc_mutex_unlock( &p_manager_entry->lock );
297 addon_entry_Release( p_manager_entry );
299 vlc_mutex_unlock( &p_entry->lock );
303 static void LoadLocalStorage( addons_manager_t *p_manager )
305 addons_finder_t *p_finder =
306 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
307 p_finder->obj.flags |= OBJECT_FLAGS_NOINTERACT;
309 module_t *p_module = module_need( p_finder, "addons finder",
310 "addons.store.list", true );
311 if( p_module )
313 ARRAY_INIT( p_finder->entries );
314 p_finder->psz_uri = NULL;
315 p_finder->pf_find( p_finder );
316 module_unneed( p_finder, p_module );
318 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
320 ARRAY_RESET( p_finder->entries );
322 vlc_object_release( p_finder );
325 static void finder_thread_interrupted( void* p_data )
327 addons_manager_t *p_manager = p_data;
328 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
329 p_manager->p_priv->finder.b_live = false;
330 vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
331 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
334 static void *FinderThread( void *p_data )
336 addons_manager_t *p_manager = p_data;
337 int i_cancel = vlc_savecancel();
338 vlc_interrupt_set( p_manager->p_priv->finder.p_interrupt );
340 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
341 while( p_manager->p_priv->finder.b_live )
343 char *psz_uri;
345 vlc_interrupt_register( finder_thread_interrupted, p_data );
346 while( p_manager->p_priv->finder.uris.i_size == 0 &&
347 p_manager->p_priv->finder.b_live )
349 vlc_cond_wait( &p_manager->p_priv->finder.waitcond,
350 &p_manager->p_priv->finder.lock );
352 vlc_interrupt_unregister();
353 if( !p_manager->p_priv->finder.b_live )
354 break;
355 psz_uri = p_manager->p_priv->finder.uris.p_elems[0];
356 ARRAY_REMOVE( p_manager->p_priv->finder.uris, 0 );
358 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
360 addons_finder_t *p_finder =
361 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
363 if( p_finder != NULL )
365 p_finder->obj.flags |= OBJECT_FLAGS_NOINTERACT;
366 module_t *p_module;
367 ARRAY_INIT( p_finder->entries );
368 p_finder->psz_uri = psz_uri;
370 p_module = module_need( p_finder, "addons finder", NULL, false );
371 if( p_module )
373 p_finder->pf_find( p_finder );
374 module_unneed( p_finder, p_module );
375 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
377 ARRAY_RESET( p_finder->entries );
378 free( psz_uri );
379 vlc_object_release( p_finder );
382 p_manager->owner.discovery_ended( p_manager );
383 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
386 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
387 vlc_restorecancel( i_cancel );
388 return NULL;
391 static int addons_manager_WriteCatalog( addons_manager_t *p_manager )
393 int i_return = VLC_EGENERIC;
395 addons_storage_t *p_storage =
396 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
397 p_storage->obj.flags |= OBJECT_FLAGS_NOINTERACT;
399 module_t *p_module = module_need( p_storage, "addons storage",
400 "addons.store.install", true );
401 if( p_module )
403 vlc_mutex_lock( &p_manager->p_priv->finder.lock );
404 i_return = p_storage->pf_catalog( p_storage, p_manager->p_priv->finder.entries.p_elems,
405 p_manager->p_priv->finder.entries.i_size );
406 vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
407 module_unneed( p_storage, p_module );
409 vlc_object_release( p_storage );
411 return i_return;
414 int addons_manager_LoadCatalog( addons_manager_t *p_manager )
416 LoadLocalStorage( p_manager );
417 return VLC_SUCCESS;
420 static int installOrRemoveAddon( addons_manager_t *p_manager, addon_entry_t *p_entry, bool b_install )
422 int i_return = VLC_EGENERIC;
424 addons_storage_t *p_storage =
425 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
426 p_storage->obj.flags |= OBJECT_FLAGS_NOINTERACT;
428 module_t *p_module = module_need( p_storage, "addons storage",
429 "addons.store.install", true );
430 if( p_module )
432 if ( b_install )
433 i_return = p_storage->pf_install( p_storage, p_entry );
434 else
435 i_return = p_storage->pf_remove( p_storage, p_entry );
436 module_unneed( p_storage, p_module );
437 msg_Dbg( p_manager->p_priv->p_parent, "InstallAddon returns %d", i_return );
438 if ( i_return == VLC_SUCCESS )
440 /* Reset flags */
441 vlc_mutex_lock( &p_entry->lock );
442 p_entry->e_flags = ADDON_MANAGEABLE;
443 vlc_mutex_unlock( &p_entry->lock );
446 vlc_object_release( p_storage );
448 return i_return;
451 static void installer_thread_interrupted( void* p_data )
453 addons_manager_t *p_manager = p_data;
454 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
455 p_manager->p_priv->installer.b_live = false;
456 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
457 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
460 static void *InstallerThread( void *p_data )
462 addons_manager_t *p_manager = p_data;
463 int i_cancel = vlc_savecancel();
464 vlc_interrupt_set( p_manager->p_priv->installer.p_interrupt );
465 int i_ret;
467 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
468 while( p_manager->p_priv->installer.b_live )
470 vlc_interrupt_register( installer_thread_interrupted, p_data );
471 while ( !p_manager->p_priv->installer.entries.i_size &&
472 p_manager->p_priv->installer.b_live )
474 /* No queued addons */
475 vlc_cond_wait( &p_manager->p_priv->installer.waitcond,
476 &p_manager->p_priv->installer.lock );
478 vlc_interrupt_unregister();
479 if( !p_manager->p_priv->installer.b_live )
480 break;
482 addon_entry_t *p_entry = p_manager->p_priv->installer.entries.p_elems[0];
483 ARRAY_REMOVE( p_manager->p_priv->installer.entries, 0 );
484 addon_entry_Hold( p_entry );
485 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
487 vlc_mutex_lock( &p_entry->lock );
488 /* DO WORK */
489 if ( p_entry->e_state == ADDON_INSTALLED )
491 p_entry->e_state = ADDON_UNINSTALLING;
492 vlc_mutex_unlock( &p_entry->lock );
494 /* notify */
495 p_manager->owner.addon_changed( p_manager, p_entry );
497 i_ret = installOrRemoveAddon( p_manager, p_entry, false );
499 vlc_mutex_lock( &p_entry->lock );
500 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_NOTINSTALLED
501 : ADDON_INSTALLED;
503 else if ( p_entry->e_state == ADDON_NOTINSTALLED )
505 p_entry->e_state = ADDON_INSTALLING;
506 vlc_mutex_unlock( &p_entry->lock );
508 /* notify */
509 p_manager->owner.addon_changed( p_manager, p_entry );
511 i_ret = installOrRemoveAddon( p_manager, p_entry, true );
513 vlc_mutex_lock( &p_entry->lock );
514 p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_INSTALLED
515 : ADDON_NOTINSTALLED;
517 vlc_mutex_unlock( &p_entry->lock );
518 /* !DO WORK */
520 p_manager->owner.addon_changed( p_manager, p_entry );
522 addon_entry_Release( p_entry );
524 addons_manager_WriteCatalog( p_manager );
525 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
527 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
528 vlc_restorecancel( i_cancel );
529 return NULL;
532 static int InstallEntry( addons_manager_t *p_manager, addon_entry_t *p_entry )
534 if ( p_entry->e_type == ADDON_UNKNOWN ||
535 p_entry->e_type == ADDON_PLUGIN ||
536 p_entry->e_type == ADDON_OTHER )
537 return VLC_EBADVAR;
539 vlc_mutex_lock( &p_manager->p_priv->installer.lock );
540 ARRAY_APPEND( p_manager->p_priv->installer.entries, p_entry );
541 if( !p_manager->p_priv->installer.b_live )
543 if( vlc_clone( &p_manager->p_priv->installer.thread, InstallerThread, p_manager,
544 VLC_THREAD_PRIORITY_LOW ) )
546 msg_Err( p_manager->p_priv->p_parent,
547 "cannot spawn addons installer thread" );
548 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
549 return VLC_EGENERIC;
551 else
552 p_manager->p_priv->installer.b_live = true;
554 vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
555 vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
556 return VLC_SUCCESS;
559 int addons_manager_Install( addons_manager_t *p_manager, const addon_uuid_t uuid )
561 addon_entry_t *p_install_entry = getHeldEntryByUUID( p_manager, uuid );
562 if ( ! p_install_entry ) return VLC_EGENERIC;
563 int i_ret = InstallEntry( p_manager, p_install_entry );
564 addon_entry_Release( p_install_entry );
565 return i_ret;
568 int addons_manager_Remove( addons_manager_t *p_manager, const addon_uuid_t uuid )
570 return addons_manager_Install( p_manager, uuid );