]> git.sesse.net Git - vlc/blob - src/misc/addons.c
Fix NEWS for 3.0.0-git
[vlc] / src / misc / addons.c
1 /*****************************************************************************
2  * addons.c: VLC addons manager
3  *****************************************************************************
4  * Copyright (C) 2014 VLC authors and VideoLAN
5  *
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.
10  *
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.
15  *
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  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <vlc_common.h>
26 #include <vlc_atomic.h>
27 #include <vlc_modules.h>
28 #include <vlc_arrays.h>
29 #include <vlc_events.h>
30 #include "libvlc.h"
31
32 #include <vlc_addons.h>
33
34 /*****************************************************************************
35  * Structures/definitions
36  *****************************************************************************/
37
38 typedef struct addon_entry_owner
39 {
40     addon_entry_t entry;
41     atomic_uint refs;
42 } addon_entry_owner_t;
43
44 struct addons_manager_private_t
45 {
46     vlc_object_t *p_parent;
47
48     struct
49     {
50         vlc_thread_t thread;
51         vlc_cond_t waitcond;
52         bool b_live;
53         vlc_mutex_t lock;
54         DECL_ARRAY(char*) uris;
55         DECL_ARRAY(addon_entry_t*) entries;
56     } finder;
57
58     struct
59     {
60         vlc_thread_t thread;
61         vlc_cond_t waitcond;
62         bool b_live;
63         vlc_mutex_t lock;
64         DECL_ARRAY(addon_entry_t*) entries;
65     } installer;
66 };
67
68 static void *FinderThread( void * );
69 static void LoadLocalStorage( addons_manager_t *p_manager );
70
71 /*****************************************************************************
72  * Public functions
73  *****************************************************************************/
74
75 addon_entry_t * addon_entry_New()
76 {
77     addon_entry_owner_t *owner = calloc( 1, sizeof(addon_entry_owner_t) );
78     if( unlikely(owner == NULL) )
79         return NULL;
80
81     atomic_init( &owner->refs, 1 );
82
83     addon_entry_t *p_entry = &owner->entry;
84     vlc_mutex_init( &p_entry->lock );
85     ARRAY_INIT( p_entry->files );
86     return p_entry;
87 }
88
89 addon_entry_t * addon_entry_Hold( addon_entry_t * p_entry )
90 {
91     addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
92
93     atomic_fetch_add( &owner->refs, 1 );
94     return p_entry;
95 }
96
97 void addon_entry_Release( addon_entry_t * p_entry )
98 {
99     addon_entry_owner_t *owner = (addon_entry_owner_t *) p_entry;
100
101     if( atomic_fetch_sub(&owner->refs, 1) != 1 )
102         return;
103
104     free( p_entry->psz_name );
105     free( p_entry->psz_summary );
106     free( p_entry->psz_description );
107     free( p_entry->psz_archive_uri );
108     free( p_entry->psz_author );
109     free( p_entry->psz_source_uri );
110     free( p_entry->psz_image_uri );
111     free( p_entry->psz_image_data );
112     free( p_entry->psz_source_module );
113     free( p_entry->psz_version );
114     free( p_entry->p_custom );
115
116     addon_file_t *p_file;
117     FOREACH_ARRAY( p_file, p_entry->files )
118     free( p_file->psz_filename );
119     free( p_file->psz_download_uri );
120     FOREACH_END()
121     ARRAY_RESET( p_entry->files );
122
123     vlc_mutex_destroy( &p_entry->lock );
124     free( owner );
125 }
126
127 addons_manager_t *addons_manager_New( vlc_object_t *p_this )
128 {
129     addons_manager_t *p_manager = malloc( sizeof(addons_manager_t) );
130     if ( !p_manager ) return NULL;
131
132     p_manager->p_priv = malloc( sizeof(addons_manager_private_t) );
133     if ( !p_manager->p_priv )
134     {
135         free( p_manager );
136         return NULL;
137     }
138
139     p_manager->p_event_manager = malloc( sizeof(vlc_event_manager_t) );
140     if ( !p_manager->p_event_manager )
141     {
142         free( p_manager->p_priv );
143         free( p_manager );
144         return NULL;
145     }
146
147     p_manager->p_priv->p_parent = p_this;
148
149 #define INIT_QUEUE( name ) \
150     p_manager->p_priv->name.b_live = false;\
151     vlc_mutex_init( &p_manager->p_priv->name.lock );\
152     vlc_cond_init( &p_manager->p_priv->name.waitcond );\
153     ARRAY_INIT( p_manager->p_priv->name.entries );
154
155     INIT_QUEUE( finder )
156     INIT_QUEUE( installer )
157     ARRAY_INIT( p_manager->p_priv->finder.uris );
158
159     vlc_event_manager_t *em = p_manager->p_event_manager;
160     vlc_event_manager_init( em, p_manager );
161     vlc_event_manager_register_event_type(em, vlc_AddonFound);
162     vlc_event_manager_register_event_type(em, vlc_AddonsDiscoveryEnded);
163     vlc_event_manager_register_event_type(em, vlc_AddonChanged);
164
165     return p_manager;
166 }
167
168 void addons_manager_Delete( addons_manager_t *p_manager )
169 {
170     bool b_live;
171
172     vlc_mutex_lock( &p_manager->p_priv->finder.lock );
173     b_live = p_manager->p_priv->finder.b_live;
174     vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
175     if ( b_live )
176     {
177         vlc_cancel( p_manager->p_priv->finder.thread );
178         vlc_join( p_manager->p_priv->finder.thread, NULL );
179     }
180
181     vlc_mutex_lock( &p_manager->p_priv->installer.lock );
182     b_live = p_manager->p_priv->installer.b_live;
183     vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
184     if ( b_live )
185     {
186         vlc_cancel( p_manager->p_priv->installer.thread );
187         vlc_join( p_manager->p_priv->installer.thread, NULL );
188     }
189
190     vlc_event_manager_fini( p_manager->p_event_manager );
191
192 #define FREE_QUEUE( name ) \
193     vlc_mutex_lock( &p_manager->p_priv->name.lock );\
194     FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->name.entries )\
195         addon_entry_Release( p_entry );\
196     FOREACH_END();\
197     ARRAY_RESET( p_manager->p_priv->name.entries );\
198     vlc_mutex_unlock( &p_manager->p_priv->name.lock );\
199     vlc_mutex_destroy( &p_manager->p_priv->name.lock );\
200     vlc_cond_destroy( &p_manager->p_priv->name.waitcond );
201
202     FREE_QUEUE( finder )
203     FREE_QUEUE( installer )
204     FOREACH_ARRAY( char *psz_uri, p_manager->p_priv->finder.uris )
205        free( psz_uri );
206     FOREACH_END();
207     ARRAY_RESET( p_manager->p_priv->finder.uris );
208
209     free( p_manager->p_priv );
210     free( p_manager->p_event_manager );
211     free( p_manager );
212 }
213
214 void addons_manager_Gather( addons_manager_t *p_manager, const char *psz_uri )
215 {
216     if ( !psz_uri )
217         return;
218
219     vlc_mutex_lock( &p_manager->p_priv->finder.lock );
220
221     ARRAY_APPEND( p_manager->p_priv->finder.uris, strdup( psz_uri ) );
222
223     if( !p_manager->p_priv->finder.b_live )
224     {
225         if( vlc_clone( &p_manager->p_priv->finder.thread, FinderThread, p_manager,
226                        VLC_THREAD_PRIORITY_LOW ) )
227         {
228             msg_Err( p_manager->p_priv->p_parent,
229                      "cannot spawn entries provider thread" );
230             vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
231             return;
232         }
233         p_manager->p_priv->finder.b_live = true;
234     }
235
236     vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
237     vlc_cond_signal( &p_manager->p_priv->finder.waitcond );
238 }
239
240 /*****************************************************************************
241  * Private functions
242  *****************************************************************************/
243
244 static addon_entry_t * getHeldEntryByUUID( addons_manager_t *p_manager,
245                                            const addon_uuid_t uuid )
246 {
247     addon_entry_t *p_return = NULL;
248     vlc_mutex_lock( &p_manager->p_priv->finder.lock );
249     FOREACH_ARRAY( addon_entry_t *p_entry, p_manager->p_priv->finder.entries )
250     if ( !memcmp( p_entry->uuid, uuid, sizeof( addon_uuid_t ) ) )
251     {
252         p_return = p_entry;
253         addon_entry_Hold( p_return );
254         break;
255     }
256     FOREACH_END()
257     vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
258     return p_return;
259 }
260
261 static void MergeSources( addons_manager_t *p_manager,
262                           addon_entry_t **pp_addons, int i_count )
263 {
264     addon_entry_t *p_entry, *p_manager_entry;
265     addon_uuid_t zerouuid;
266     memset( zerouuid, 0, sizeof( addon_uuid_t ) );
267     for ( int i=0; i < i_count; i++ )
268     {
269         p_entry = pp_addons[i];
270         vlc_mutex_lock( &p_entry->lock );
271         if ( memcmp( p_entry->uuid, zerouuid, sizeof( addon_uuid_t ) ) )
272             p_manager_entry = getHeldEntryByUUID( p_manager, p_entry->uuid );
273         else
274             p_manager_entry = NULL;
275         if ( !p_manager_entry )
276         {
277             ARRAY_APPEND( p_manager->p_priv->finder.entries, p_entry );
278             vlc_event_t event;
279             event.type = vlc_AddonFound;
280             event.u.addon_generic_event.p_entry = p_entry;
281             vlc_event_send( p_manager->p_event_manager, &event );
282         }
283         else
284         {
285             vlc_mutex_lock( &p_manager_entry->lock );
286             if ( ( p_manager_entry->psz_version && p_entry->psz_version )
287                  && /* FIXME: better version comparison */
288                  strcmp( p_manager_entry->psz_version, p_entry->psz_version )
289                  )
290             {
291                 p_manager_entry->e_flags |= ADDON_UPDATABLE;
292             }
293             vlc_mutex_unlock( &p_manager_entry->lock );
294             addon_entry_Release( p_manager_entry );
295         }
296         vlc_mutex_unlock( &p_entry->lock );
297     }
298 }
299
300 static void LoadLocalStorage( addons_manager_t *p_manager )
301 {
302     addons_finder_t *p_finder =
303         vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
304
305     module_t *p_module = module_need( p_finder, "addons finder",
306                                       "addons.store.list", true );
307     if( p_module )
308     {
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 );
313
314         MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
315
316         ARRAY_RESET( p_finder->entries );
317     }
318     vlc_object_release( p_finder );
319 }
320
321 static void *FinderThread( void *p_data )
322 {
323     addons_manager_t *p_manager = p_data;
324     int i_cancel;
325     char *psz_uri;
326
327     for( ;; )
328     {
329         vlc_mutex_lock( &p_manager->p_priv->finder.lock );
330         mutex_cleanup_push( &p_manager->p_priv->finder.lock );
331         while( p_manager->p_priv->finder.uris.i_size == 0 )
332         {
333             vlc_cond_wait( &p_manager->p_priv->finder.waitcond,
334                            &p_manager->p_priv->finder.lock );
335         }
336         psz_uri = p_manager->p_priv->finder.uris.p_elems[0];
337         ARRAY_REMOVE( p_manager->p_priv->finder.uris, 0 );
338         vlc_cleanup_run();
339
340         addons_finder_t *p_finder =
341                 vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_finder ), "entries finder" );
342
343         i_cancel = vlc_savecancel();
344         if( p_finder != NULL )
345         {
346             module_t *p_module;
347             ARRAY_INIT( p_finder->entries );
348             vlc_mutex_lock( &p_manager->p_priv->finder.lock );
349             p_finder->psz_uri = psz_uri;
350             vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
351
352             p_module = module_need( p_finder, "addons finder", NULL, false );
353             if( p_module )
354             {
355                 p_finder->pf_find( p_finder );
356                 module_unneed( p_finder, p_module );
357                 MergeSources( p_manager, p_finder->entries.p_elems, p_finder->entries.i_size );
358             }
359             ARRAY_RESET( p_finder->entries );
360             free( psz_uri );
361             vlc_object_release( p_finder );
362         }
363
364         vlc_event_t event;
365         event.type = vlc_AddonsDiscoveryEnded;
366         event.u.addon_generic_event.p_entry = NULL;
367         vlc_event_send( p_manager->p_event_manager, &event );
368
369         vlc_restorecancel( i_cancel );
370         vlc_testcancel();
371     }
372
373     return NULL;
374 }
375
376 static int addons_manager_WriteCatalog( addons_manager_t *p_manager )
377 {
378     int i_return = VLC_EGENERIC;
379
380     addons_storage_t *p_storage =
381         vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
382
383     module_t *p_module = module_need( p_storage, "addons storage",
384                                       "addons.store.install", true );
385     if( p_module )
386     {
387         vlc_mutex_lock( &p_manager->p_priv->finder.lock );
388         i_return = p_storage->pf_catalog( p_storage, p_manager->p_priv->finder.entries.p_elems,
389                                           p_manager->p_priv->finder.entries.i_size );
390         vlc_mutex_unlock( &p_manager->p_priv->finder.lock );
391         module_unneed( p_storage, p_module );
392     }
393     vlc_object_release( p_storage );
394
395     return i_return;
396 }
397
398 int addons_manager_LoadCatalog( addons_manager_t *p_manager )
399 {
400     LoadLocalStorage( p_manager );
401     return VLC_SUCCESS;
402 }
403
404 static int installOrRemoveAddon( addons_manager_t *p_manager, addon_entry_t *p_entry, bool b_install )
405 {
406     int i_return = VLC_EGENERIC;
407
408     addons_storage_t *p_storage =
409         vlc_custom_create( p_manager->p_priv->p_parent, sizeof( *p_storage ), "entries storage" );
410
411     module_t *p_module = module_need( p_storage, "addons storage",
412                                       "addons.store.install", true );
413     if( p_module )
414     {
415         if ( b_install )
416             i_return = p_storage->pf_install( p_storage, p_entry );
417         else
418             i_return = p_storage->pf_remove( p_storage, p_entry );
419         module_unneed( p_storage, p_module );
420         msg_Dbg( p_manager->p_priv->p_parent, "InstallAddon returns %d", i_return );
421         if ( i_return == VLC_SUCCESS )
422         {
423             /* Reset flags */
424             vlc_mutex_lock( &p_entry->lock );
425             p_entry->e_flags = ADDON_MANAGEABLE;
426             vlc_mutex_unlock( &p_entry->lock );
427         }
428     }
429     vlc_object_release( p_storage );
430
431     return i_return;
432 }
433
434 static void *InstallerThread( void *p_data )
435 {
436     addons_manager_t *p_manager = p_data;
437     int i_ret, i_cancel;
438     vlc_event_t event;
439     event.type = vlc_AddonChanged;
440
441     for( ;; )
442     {
443         vlc_mutex_lock( &p_manager->p_priv->installer.lock );
444         mutex_cleanup_push( &p_manager->p_priv->installer.lock );
445         while ( !p_manager->p_priv->installer.entries.i_size )
446         {
447             /* No queued addons */
448             vlc_cond_wait( &p_manager->p_priv->installer.waitcond,
449                            &p_manager->p_priv->installer.lock );
450         }
451         vlc_cleanup_pop();
452
453         addon_entry_t *p_entry = p_manager->p_priv->installer.entries.p_elems[0];
454         ARRAY_REMOVE( p_manager->p_priv->installer.entries, 0 );
455         addon_entry_Hold( p_entry );
456         vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
457
458         vlc_mutex_lock( &p_entry->lock );
459         /* DO WORK */
460         if ( p_entry->e_state == ADDON_INSTALLED )
461         {
462             p_entry->e_state = ADDON_UNINSTALLING;
463             vlc_mutex_unlock( &p_entry->lock );
464
465             /* notify */
466             i_cancel = vlc_savecancel();
467             event.u.addon_generic_event.p_entry = p_entry;
468             vlc_event_send( p_manager->p_event_manager, &event );
469
470             i_ret = installOrRemoveAddon( p_manager, p_entry, false );
471             vlc_restorecancel( i_cancel );
472
473             vlc_mutex_lock( &p_entry->lock );
474             p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_NOTINSTALLED
475                                                         : ADDON_INSTALLED;
476             vlc_mutex_unlock( &p_entry->lock );
477         }
478         else if ( p_entry->e_state == ADDON_NOTINSTALLED )
479         {
480             p_entry->e_state = ADDON_INSTALLING;
481             vlc_mutex_unlock( &p_entry->lock );
482
483             /* notify */
484             i_cancel = vlc_savecancel();
485             event.u.addon_generic_event.p_entry = p_entry;
486             vlc_event_send( p_manager->p_event_manager, &event );
487
488             i_ret = installOrRemoveAddon( p_manager, p_entry, true );
489             vlc_restorecancel( i_cancel );
490
491             vlc_mutex_lock( &p_entry->lock );
492             p_entry->e_state = ( i_ret == VLC_SUCCESS ) ? ADDON_INSTALLED
493                                                         : ADDON_NOTINSTALLED;
494             vlc_mutex_unlock( &p_entry->lock );
495         }
496         else
497             vlc_mutex_unlock( &p_entry->lock );
498         /* !DO WORK */
499
500         i_cancel = vlc_savecancel();
501         event.u.addon_generic_event.p_entry = p_entry;
502         vlc_event_send( p_manager->p_event_manager, &event );
503         vlc_restorecancel( i_cancel );
504
505         addon_entry_Release( p_entry );
506
507         i_cancel = vlc_savecancel();
508         addons_manager_WriteCatalog( p_manager );
509         vlc_restorecancel( i_cancel );
510     }
511
512     return NULL;
513 }
514
515 static int InstallEntry( addons_manager_t *p_manager, addon_entry_t *p_entry )
516 {
517     if ( p_entry->e_type == ADDON_UNKNOWN ||
518          p_entry->e_type == ADDON_PLUGIN ||
519          p_entry->e_type == ADDON_OTHER )
520         return VLC_EBADVAR;
521
522     vlc_mutex_lock( &p_manager->p_priv->installer.lock );
523     ARRAY_APPEND( p_manager->p_priv->installer.entries, p_entry );
524     if( !p_manager->p_priv->installer.b_live )
525     {
526         if( vlc_clone( &p_manager->p_priv->installer.thread, InstallerThread, p_manager,
527                        VLC_THREAD_PRIORITY_LOW ) )
528         {
529             msg_Err( p_manager->p_priv->p_parent,
530                      "cannot spawn addons installer thread" );
531             vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
532             return VLC_EGENERIC;
533         }
534         else
535             p_manager->p_priv->installer.b_live = true;
536     }
537     vlc_mutex_unlock( &p_manager->p_priv->installer.lock );
538     vlc_cond_signal( &p_manager->p_priv->installer.waitcond );
539     return VLC_SUCCESS;
540 }
541
542 int addons_manager_Install( addons_manager_t *p_manager, const addon_uuid_t uuid )
543 {
544     addon_entry_t *p_install_entry = getHeldEntryByUUID( p_manager, uuid );
545     if ( ! p_install_entry ) return VLC_EGENERIC;
546     int i_ret = InstallEntry( p_manager, p_install_entry );
547     addon_entry_Release( p_install_entry );
548     return i_ret;
549 }
550
551 int addons_manager_Remove( addons_manager_t *p_manager, const addon_uuid_t uuid )
552 {
553     return addons_manager_Install( p_manager, uuid );
554 }