1 /*
2   This file is part of Deadbeef Player source code
3   http://deadbeef.sourceforge.net
4 
5   plugin management
6 
7   Copyright (C) 2009-2013 Alexey Yakovenko
8 
9   This software is provided 'as-is', without any express or implied
10   warranty.  In no event will the authors be held liable for any damages
11   arising from the use of this software.
12 
13   Permission is granted to anyone to use this software for any purpose,
14   including commercial applications, and to alter it and redistribute it
15   freely, subject to the following restrictions:
16 
17   1. The origin of this software must not be misrepresented; you must not
18      claim that you wrote the original software. If you use this software
19      in a product, an acknowledgment in the product documentation would be
20      appreciated but is not required.
21   2. Altered source versions must be plainly marked as such, and must not be
22      misrepresented as being the original software.
23   3. This notice may not be removed or altered from any source distribution.
24 
25   Alexey Yakovenko waker@users.sourceforge.net
26 */
27 #include <dirent.h>
28 #include <dlfcn.h>
29 #include <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #ifndef __linux__
36 #define _POSIX_C_SOURCE 1
37 #endif
38 #include <limits.h>
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42 #include "gettext.h"
43 #include "plugins.h"
44 #include "md5/md5.h"
45 #include "messagepump.h"
46 #include "threading.h"
47 #include "playlist.h"
48 #include "volume.h"
49 #include "streamer.h"
50 #include "common.h"
51 #include "conf.h"
52 #include "junklib.h"
53 #include "vfs.h"
54 #include "premix.h"
55 #include "dsppreset.h"
56 #include "pltmeta.h"
57 #include "metacache.h"
58 #include "tf.h"
59 #include "playqueue.h"
60 #include "sort.h"
61 
62 #define trace(...) { fprintf(stderr, __VA_ARGS__); }
63 //#define trace(fmt,...)
64 
65 //#define DISABLE_VERSIONCHECK 1
66 
67 #if defined(HAVE_COCOAUI) || defined(OSX_APPBUNDLE)
68 #define PLUGINEXT ".dylib"
69 #else
70 #define PLUGINEXT ".so"
71 #endif
72 
73 const char *lowprio_plugin_ids[] = {
74     "ffmpeg",
75     NULL
76 };
77 
78 // internal plugin list
79 typedef struct plugin_s {
80     void *handle;
81     DB_plugin_t *plugin;
82     struct plugin_s *next;
83 } plugin_t;
84 
85 static plugin_t *plugins;
86 static plugin_t *plugins_tail;
87 
88 // this list only gets used during plugin loading,
89 // then it gets appended to the above "plugins" list,
90 // and set to NULL
91 static plugin_t *plugins_lowprio;
92 static plugin_t *plugins_lowprio_tail;
93 
94 #define MAX_PLUGINS 100
95 static DB_plugin_t *g_plugins[MAX_PLUGINS+1];
96 
97 #define MAX_GUI_PLUGINS 10
98 static char *g_gui_names[MAX_GUI_PLUGINS+1];
99 static int g_num_gui_names;
100 
101 #define MAX_DECODER_PLUGINS 50
102 static DB_decoder_t *g_decoder_plugins[MAX_DECODER_PLUGINS+1];
103 
104 #define MAX_VFS_PLUGINS 10
105 static DB_vfs_t *g_vfs_plugins[MAX_VFS_PLUGINS+1];
106 
107 #define MAX_DSP_PLUGINS 10
108 static DB_dsp_t *g_dsp_plugins[MAX_DSP_PLUGINS+1];
109 
110 #define MAX_OUTPUT_PLUGINS 10
111 static DB_output_t *g_output_plugins[MAX_OUTPUT_PLUGINS+1];
112 static DB_output_t *output_plugin = NULL;
113 
114 #define MAX_PLAYLIST_PLUGINS 10
115 static DB_playlist_t *g_playlist_plugins[MAX_PLAYLIST_PLUGINS+1];
116 
117 static uintptr_t background_jobs_mutex;
118 static int num_background_jobs;
119 
120 // deadbeef api
121 static DB_functions_t deadbeef_api = {
122     .vmajor = DB_API_VERSION_MAJOR,
123     .vminor = DB_API_VERSION_MINOR,
124     .md5 = plug_md5,
125     .md5_to_str = plug_md5_to_str,
126     .md5_init = (void (*)(DB_md5_t *s))md5_init,
127     .md5_append = (void (*)(DB_md5_t *s, const uint8_t *daya, int nbytes))md5_append,
128     .md5_finish = (void (*)(DB_md5_t *s, uint8_t digest[16]))md5_finish,
129     .get_output = plug_get_output,
130     .playback_get_pos = plug_playback_get_pos,
131     .playback_set_pos = plug_playback_set_pos,
132     // streamer access
133     .streamer_get_playing_track = (DB_playItem_t *(*) (void))streamer_get_playing_track,
134     .streamer_get_streaming_track = (DB_playItem_t *(*) (void))streamer_get_streaming_track,
135     .streamer_get_playpos = streamer_get_playpos,
136     .streamer_ok_to_read = streamer_ok_to_read,
137     .streamer_reset = streamer_reset,
138     .streamer_read = streamer_read,
139     .streamer_set_bitrate = streamer_set_bitrate,
140     .streamer_get_apx_bitrate = streamer_get_apx_bitrate,
141     .streamer_get_current_fileinfo = streamer_get_current_fileinfo,
142     .streamer_get_current_playlist = streamer_get_current_playlist,
143     .streamer_get_dsp_chain = streamer_get_dsp_chain,
144     .streamer_set_dsp_chain = streamer_set_dsp_chain,
145     .streamer_dsp_refresh = streamer_dsp_refresh,
146     // folders
147     .get_config_dir = plug_get_config_dir,
148     .get_prefix = plug_get_prefix,
149     .get_doc_dir = plug_get_doc_dir,
150     .get_plugin_dir = plug_get_plugin_dir,
151     .get_pixmap_dir = plug_get_pixmap_dir,
152     // threading
153     .thread_start = thread_start,
154     .thread_start_low_priority = thread_start_low_priority,
155     .thread_join = thread_join,
156     .thread_detach = thread_detach,
157     .thread_exit = thread_exit,
158     .mutex_create = mutex_create,
159     .mutex_create_nonrecursive = mutex_create_nonrecursive,
160     .mutex_free = mutex_free,
161     .mutex_lock = mutex_lock,
162     .mutex_unlock = mutex_unlock,
163     .cond_create = cond_create,
164     .cond_free = cond_free,
165     .cond_wait = cond_wait,
166     .cond_signal = cond_signal,
167     .cond_broadcast = cond_broadcast,
168     // playlist management
169     .plt_ref = (void (*) (ddb_playlist_t *plt))plt_ref,
170     .plt_unref = (void (*) (ddb_playlist_t *plt))plt_unref,
171     .plt_get_count = plt_get_count,
172     .plt_get_head = (DB_playItem_t * (*) (int plt))plt_get_head,
173     .plt_get_sel_count = plt_get_sel_count,
174     .plt_add = plt_add,
175     .plt_remove = plt_remove,
176     .plt_clear = (void (*)(ddb_playlist_t *))plt_clear,
177     .pl_clear = pl_clear,
178     .plt_set_curr = (void (*) (ddb_playlist_t *plt))plt_set_curr,
179     .plt_get_curr = (ddb_playlist_t *(*) (void))plt_get_curr,
180     .plt_set_curr_idx = (void (*) (int plt))plt_set_curr_idx,
181     .plt_get_curr_idx = (int (*) (void))plt_get_curr_idx,
182     .plt_move = plt_move,
183     // playlist saving and loading
184     .plt_load = (DB_playItem_t * (*) (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_load,
185     .plt_save = (int (*)(ddb_playlist_t *plt, DB_playItem_t *first, DB_playItem_t *last, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_save,
186     // getting and working with a handle must be guarded using plt_lock/unlock
187     .plt_get_for_idx = (ddb_playlist_t *(*)(int idx))plt_get_for_idx,
188     .plt_get_title = (int (*)(ddb_playlist_t *handle, char *buffer, int sz))plt_get_title,
189     .plt_set_title = (int (*)(ddb_playlist_t *handle, const char *buffer))plt_set_title,
190     .plt_modified = (void (*) (ddb_playlist_t *handle))plt_modified,
191     .plt_get_modification_idx = (int (*) (ddb_playlist_t *handle))plt_get_modification_idx,
192     .plt_get_item_idx = (int (*) (ddb_playlist_t *plt, DB_playItem_t *it, int iter))plt_get_item_idx,
193 
194     // playlist metadata
195     .plt_add_meta = (void (*) (ddb_playlist_t *handle, const char *key, const char *value))plt_add_meta,
196     .plt_replace_meta = (void (*) (ddb_playlist_t *handle, const char *key, const char *value))plt_replace_meta,
197     .plt_append_meta = (void (*) (ddb_playlist_t *handle, const char *key, const char *value))plt_append_meta,
198     .plt_set_meta_int = (void (*) (ddb_playlist_t *handle, const char *key, int value))plt_set_meta_int,
199     .plt_set_meta_float = (void (*) (ddb_playlist_t *handle, const char *key, float value))plt_set_meta_float,
200     .plt_find_meta = (const char *(*) (ddb_playlist_t *handle, const char *key))plt_find_meta,
201     .plt_get_metadata_head = (DB_metaInfo_t * (*) (ddb_playlist_t *handle))plt_get_metadata_head,
202     .plt_delete_metadata = (void (*) (ddb_playlist_t *handle, DB_metaInfo_t *meta))plt_delete_metadata,
203     .plt_find_meta_int = (int (*) (ddb_playlist_t *handle, const char *key, int def))plt_find_meta_int,
204     .plt_find_meta_float = (float (*) (ddb_playlist_t *handle, const char *key, float def))plt_find_meta_float,
205     .plt_delete_all_meta = (void (*) (ddb_playlist_t *handle))plt_delete_all_meta,
206 
207     // operating on playlist items
208     .plt_insert_item = (DB_playItem_t *(*) (ddb_playlist_t *playlist, DB_playItem_t *after, DB_playItem_t *it))plt_insert_item,
209     .plt_insert_file = (DB_playItem_t *(*) (ddb_playlist_t *playlist, DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_insert_file,
210     .plt_insert_dir = (DB_playItem_t *(*) (ddb_playlist_t *plt, DB_playItem_t *after, const char *dirname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_insert_dir,
211     .plt_set_item_duration = (void (*) (ddb_playlist_t *plt, DB_playItem_t *it, float duration))plt_set_item_duration,
212     .plt_remove_item = (int (*) (ddb_playlist_t *playlist, DB_playItem_t *it))plt_remove_item,
213     .plt_getselcount = (int (*) (ddb_playlist_t *playlist))plt_getselcount,
214 
215     // playlist access
216     .pl_lock = pl_lock,
217     .pl_unlock = pl_unlock,
218     //.plt_lock = plt_lock,
219     //.plt_unlock = plt_unlock,
220     .pl_item_alloc = (DB_playItem_t* (*)(void))pl_item_alloc,
221     .pl_item_alloc_init = (DB_playItem_t* (*)(const char *fname, const char *decoder_id))pl_item_alloc_init,
222     .pl_item_ref = (void (*)(DB_playItem_t *))pl_item_ref,
223     .pl_item_unref = (void (*)(DB_playItem_t *))pl_item_unref,
224     .pl_item_copy = (void (*)(DB_playItem_t *, DB_playItem_t *))pl_item_copy,
225     .plt_add_file = (int (*) (ddb_playlist_t *plt, const char *, int (*cb)(DB_playItem_t *it, void *data), void *))plt_add_file,
226     .plt_add_dir = (int (*) (ddb_playlist_t *plt, const char *dirname, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_add_dir,
227     .pl_add_files_begin = (int (*) (ddb_playlist_t *plt))pl_add_files_begin,
228     .pl_add_files_end = pl_add_files_end,
229     .pl_get_idx_of = (int (*) (DB_playItem_t *it))pl_get_idx_of,
230     .pl_get_idx_of_iter = (int (*) (DB_playItem_t *it, int iter))pl_get_idx_of_iter,
231     .pl_get_for_idx = (DB_playItem_t * (*)(int))pl_get_for_idx,
232     .pl_get_for_idx_and_iter = (DB_playItem_t * (*) (int idx, int iter))pl_get_for_idx_and_iter,
233     .pl_get_item_duration = (float (*) (DB_playItem_t *it))pl_get_item_duration,
234     .pl_get_item_flags = (uint32_t (*) (DB_playItem_t *it))pl_get_item_flags,
235     .pl_set_item_flags = (void (*) (DB_playItem_t *it, uint32_t flags))pl_set_item_flags,
236     .plt_sort = (void (*) (ddb_playlist_t *plt, int iter, int id, const char *format, int order))plt_sort,
237     .pl_items_copy_junk = (void (*)(DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last))pl_items_copy_junk,
238     .pl_set_item_replaygain = (void (*)(DB_playItem_t *it, int idx, float value))pl_set_item_replaygain,
239     .pl_get_item_replaygain = (float (*)(DB_playItem_t *it, int idx))pl_get_item_replaygain,
240     .plt_get_totaltime = (float (*) (ddb_playlist_t *plt))plt_get_totaltime,
241     .plt_get_item_for_idx = (DB_playItem_t * (*) (ddb_playlist_t *playlist, int idx, int iter))plt_get_item_for_idx,
242     .pl_get_totaltime = pl_get_totaltime,
243     .pl_getcount = pl_getcount,
244     .plt_get_item_count = (int (*)(ddb_playlist_t *plt, int iter))plt_get_item_count,
245     .plt_delete_selected = (int (*) (ddb_playlist_t *plt))plt_delete_selected,
246     .pl_delete_selected = pl_delete_selected,
247     .plt_set_cursor = (void (*)(ddb_playlist_t *plt, int iter, int cursor))plt_set_cursor,
248     .pl_set_cursor = pl_set_cursor,
249     .plt_get_cursor = (int (*)(ddb_playlist_t *plt, int iter))plt_get_cursor,
250     .pl_get_cursor = pl_get_cursor,
251     .pl_set_selected = (void (*) (DB_playItem_t *, int))pl_set_selected,
252     .pl_is_selected = (int (*) (DB_playItem_t *))pl_is_selected,
253     .pl_save_current = pl_save_current,
254     .pl_save_all = pl_save_all,
255     .plt_select_all = (void (*) (ddb_playlist_t *plt))plt_select_all,
256     .pl_select_all = pl_select_all,
257     .plt_crop_selected = (void (*) (ddb_playlist_t *plt))plt_crop_selected,
258     .pl_crop_selected = pl_crop_selected,
259     .pl_getselcount = pl_getselcount,
260     .plt_get_first = (DB_playItem_t *(*) (ddb_playlist_t *plt, int))plt_get_first,
261     .pl_get_first = (DB_playItem_t *(*) (int))pl_get_first,
262     .plt_get_last = (DB_playItem_t *(*) (ddb_playlist_t *plt, int))plt_get_last,
263     .pl_get_last = (DB_playItem_t *(*) (int))pl_get_last,
264     .pl_get_next = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_next,
265     .pl_get_prev = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_prev,
266     .pl_format_title = (int (*) (DB_playItem_t *it, int idx, char *s, int size, int id, const char *fmt))pl_format_title,
267     .pl_format_title_escaped = (int (*) (DB_playItem_t *it, int idx, char *s, int size, int id, const char *fmt))pl_format_title_escaped,
268     .pl_format_time = pl_format_time,
269     .plt_move_items = (void (*) (ddb_playlist_t *to, int iter, ddb_playlist_t *from, DB_playItem_t *drop_before, uint32_t *indexes, int count))plt_move_items,
270     .plt_copy_items = (void (*) (ddb_playlist_t *to, int iter, ddb_playlist_t *from, DB_playItem_t *before, uint32_t *indices, int cnt))plt_copy_items,
271     .plt_search_reset = (void (*)(ddb_playlist_t *plt))plt_search_reset,
272     .plt_search_process = (void (*)(ddb_playlist_t *plt, const char *t))plt_search_process,
273     .pl_get_playlist = (ddb_playlist_t * (*) (DB_playItem_t *it))pl_get_playlist,
274     // metainfo
275     .pl_add_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_add_meta,
276     .pl_append_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_append_meta,
277     .pl_set_meta_int = (void (*) (DB_playItem_t *it, const char *key, int value))pl_set_meta_int,
278     .pl_set_meta_float = (void (*) (DB_playItem_t *it, const char *key, float value))pl_set_meta_float,
279     .pl_delete_meta = (void (*)(DB_playItem_t *, const char *key))pl_delete_meta,
280     .pl_find_meta = (const char *(*) (DB_playItem_t *, const char *))pl_find_meta,
281     .pl_find_meta_int = (int (*) (DB_playItem_t *it, const char *key, int def))pl_find_meta_int,
282     .pl_find_meta_float = (float (*) (DB_playItem_t *it, const char *key, float def))pl_find_meta_float,
283     .pl_replace_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_replace_meta,
284     .pl_delete_all_meta = (void (*) (DB_playItem_t *it))pl_delete_all_meta,
285     .pl_get_metadata_head = (DB_metaInfo_t *(*)(DB_playItem_t *it))pl_get_metadata_head,
286     .pl_delete_metadata = (void (*)(DB_playItem_t *, DB_metaInfo_t *))pl_delete_metadata,
287     // cuesheet support
288     .plt_insert_cue_from_buffer = (DB_playItem_t *(*) (ddb_playlist_t *plt, DB_playItem_t *after, DB_playItem_t *origin, const uint8_t *buffer, int buffersize, int numsamples, int samplerate))plt_insert_cue_from_buffer,
289     .plt_insert_cue = (DB_playItem_t *(*)(ddb_playlist_t *plt, DB_playItem_t *after, DB_playItem_t *origin, int numsamples, int samplerate))plt_insert_cue,
290     // playqueue support
291     .pl_playqueue_push = (int (*) (DB_playItem_t *))playqueue_push,
292     .pl_playqueue_clear = playqueue_clear,
293     .pl_playqueue_pop = playqueue_pop,
294     .pl_playqueue_remove = (void (*) (DB_playItem_t *))playqueue_remove,
295     .pl_playqueue_test = (int (*) (DB_playItem_t *))playqueue_test,
296     // volume control
297     .volume_set_db = plug_volume_set_db,
298     .volume_get_db = volume_get_db,
299     .volume_set_amp = plug_volume_set_amp,
300     .volume_get_amp = volume_get_amp,
301     .volume_get_min_db = volume_get_min_db,
302     // junk reading
303     .junk_id3v1_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v1_read,
304     .junk_id3v1_find = junk_id3v1_find,
305     .junk_id3v1_write = (int (*) (FILE *, DB_playItem_t *, const char *))junk_id3v1_write,
306     .junk_id3v2_find = junk_id3v2_find,
307     .junk_id3v2_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v2_read,
308     .junk_id3v2_read_full = (int (*)(DB_playItem_t *, DB_id3v2_tag_t *tag, DB_FILE *fp))junk_id3v2_read_full,
309     .junk_id3v2_convert_24_to_23 = junk_id3v2_convert_24_to_23,
310     .junk_id3v2_convert_23_to_24 = junk_id3v2_convert_23_to_24,
311     .junk_id3v2_convert_22_to_24 = junk_id3v2_convert_22_to_24,
312     .junk_id3v2_free = junk_id3v2_free,
313     .junk_id3v2_write = junk_id3v2_write,
314     .junk_id3v2_remove_frames = junk_id3v2_remove_frames,
315     .junk_id3v2_add_text_frame = junk_id3v2_add_text_frame,
316     .junk_apev2_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_apev2_read,
317     .junk_apev2_read_mem = (int (*) (DB_playItem_t *it, char *mem, int size))junk_apev2_read_mem,
318     .junk_apev2_read_full = (int (*) (DB_playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp))junk_apev2_read_full,
319     .junk_apev2_read_full_mem = (int (*) (DB_playItem_t *it, DB_apev2_tag_t *tag_store, char *mem, int memsize))junk_apev2_read_full_mem,
320     .junk_apev2_find = junk_apev2_find,
321     .junk_apev2_remove_frames = junk_apev2_remove_frames,
322     .junk_apev2_add_text_frame = junk_apev2_add_text_frame,
323     .junk_apev2_free = junk_apev2_free,
324     .junk_apev2_write = junk_apev2_write,
325     .junk_get_leading_size = junk_get_leading_size,
326     .junk_get_leading_size_stdio = junk_get_leading_size_stdio,
327     .junk_detect_charset = junk_detect_charset,
328     .junk_recode = junk_recode,
329     .junk_iconv = junk_iconv,
330     .junk_rewrite_tags = (int (*) (DB_playItem_t *it, uint32_t flags, int id3v2_version, const char *id3v1_encoding))junk_rewrite_tags,
331     // vfs
332     .fopen = vfs_fopen,
333     .fclose = vfs_fclose,
334     .fread = vfs_fread,
335     .fseek = vfs_fseek,
336     .ftell = vfs_ftell,
337     .rewind = vfs_rewind,
338     .fgetlength = vfs_fgetlength,
339     .fget_content_type = vfs_get_content_type,
340     .fset_track = (void (*) (DB_FILE *stream, DB_playItem_t *it))vfs_set_track,
341     .fabort = vfs_fabort,
342     // message passing
343     .sendmessage = messagepump_push,
344     .event_alloc = messagepump_event_alloc,
345     .event_free = messagepump_event_free,
346     .event_send = messagepump_push_event,
347     // configuration access
348     .conf_lock = conf_lock,
349     .conf_unlock = conf_unlock,
350     .conf_get_str_fast = conf_get_str_fast,
351     .conf_get_str = conf_get_str,
352     .conf_get_float = conf_get_float,
353     .conf_get_int = conf_get_int,
354     .conf_get_int64 = conf_get_int64,
355     .conf_set_str = conf_set_str,
356     .conf_set_int = conf_set_int,
357     .conf_set_int64 = conf_set_int64,
358     .conf_set_float = conf_set_float,
359     .conf_find = conf_find,
360     .conf_remove_items = conf_remove_items,
361     .conf_save = conf_save,
362     // plugin communication
363     .plug_get_decoder_list = plug_get_decoder_list,
364     .plug_get_vfs_list = plug_get_vfs_list,
365     .plug_get_output_list = plug_get_output_list,
366     .plug_get_dsp_list = plug_get_dsp_list,
367     .plug_get_playlist_list = plug_get_playlist_list,
368     .plug_get_list = plug_get_list,
369     .plug_get_gui_names = plug_get_gui_names,
370     .plug_get_decoder_id = plug_get_decoder_id,
371     .plug_remove_decoder_id = plug_remove_decoder_id,
372     .plug_get_for_id = plug_get_for_id,
373     // misc utilities
374     .is_local_file = plug_is_local_file,
375     // pcm utilities
376     .pcm_convert = pcm_convert,
377     // dsp preset management
378     .dsp_preset_load = dsp_preset_load,
379     .dsp_preset_save = dsp_preset_save,
380     .dsp_preset_free = dsp_preset_free,
381     // new 1.2 APIs
382     .plt_alloc = (ddb_playlist_t *(*)(const char *title))plt_alloc,
383     .plt_free = (void (*)(ddb_playlist_t *plt))plt_free,
384     //.plt_insert = plt_insert,
385     .plt_set_fast_mode = (void (*)(ddb_playlist_t *plt, int fast))plt_set_fast_mode,
386     .plt_is_fast_mode = (int (*)(ddb_playlist_t *plt))plt_is_fast_mode,
387     .metacache_add_string = metacache_add_string,
388     .metacache_remove_string = metacache_remove_string,
389     .metacache_ref = metacache_ref,
390     .metacache_unref = metacache_unref,
391     .pl_find_meta_raw = (const char *(*) (DB_playItem_t *it, const char *key))pl_find_meta_raw,
392     // ******* new 1.3 APIs ********
393     .streamer_dsp_chain_save = streamer_dsp_chain_save,
394     // ******* new 1.4 APIs ********
395     .pl_get_meta = (int (*) (DB_playItem_t *it, const char *key, char *val, int size))pl_get_meta,
396     .pl_get_meta_raw = (int (*) (DB_playItem_t *it, const char *key, char *val, int size))pl_get_meta_raw,
397     .plt_get_meta = (int (*) (ddb_playlist_t *handle, const char *key, char *val, int size))plt_get_meta,
398     .pl_meta_exists = (int (*) (DB_playItem_t *it, const char *key))pl_meta_exists,
399     // ******* new 1.5 APIs ********
400     .vis_waveform_listen = vis_waveform_listen,
401     .vis_waveform_unlisten = vis_waveform_unlisten,
402     .vis_spectrum_listen = vis_spectrum_listen,
403     .vis_spectrum_unlisten = vis_spectrum_unlisten,
404     .audio_set_mute = audio_set_mute,
405     .audio_is_mute = audio_is_mute,
406     .background_job_increment = background_job_increment,
407     .background_job_decrement = background_job_decrement,
408     .have_background_jobs = have_background_jobs,
409     .plt_get_idx = (int (*)(ddb_playlist_t *))plt_get_idx,
410     .plt_save_n = plt_save_n,
411     .plt_save_config = (int (*)(ddb_playlist_t *))plt_save_config,
412     .listen_file_added = listen_file_added,
413     .unlisten_file_added = unlisten_file_added,
414     .listen_file_add_beginend = listen_file_add_beginend,
415     .unlisten_file_add_beginend = unlisten_file_add_beginend,
416     .plt_load2 = (DB_playItem_t * (*) (int visibility, ddb_playlist_t *plt, ddb_playItem_t *after, const char *fname, int *pabort, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_load2,
417     .plt_add_file2 = (int (*) (int visibility, ddb_playlist_t *plt, const char *fname, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_add_file2,
418     .plt_add_dir2 = (int (*) (int visibility, ddb_playlist_t *plt, const char *dirname, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_add_dir2,
419     .plt_insert_file2 = (ddb_playItem_t * (*) (int visibility, ddb_playlist_t *playlist, ddb_playItem_t *after, const char *fname, int *pabort, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_insert_file2,
420     .plt_insert_dir2 = (ddb_playItem_t *(*) (int visibility, ddb_playlist_t *plt, ddb_playItem_t *after, const char *dirname, int *pabort, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_insert_dir2,
421     .plt_add_files_begin = (int (*) (ddb_playlist_t *plt, int visibility))plt_add_files_begin,
422     .plt_add_files_end = (void (*) (ddb_playlist_t *plt, int visibility))plt_add_files_end,
423     .plt_deselect_all = (void (*) (ddb_playlist_t *plt))plt_deselect_all,
424     // ******* new 1.6 APIs ********
425     .plt_set_scroll = (void (*) (ddb_playlist_t *plt, int scroll))plt_set_scroll,
426     .plt_get_scroll = (int (*) (ddb_playlist_t *plt))plt_get_scroll,
427     .tf_compile = tf_compile,
428     .tf_free = tf_free,
429     .tf_eval= tf_eval,
430 
431     .plt_sort_v2 = (void (*) (ddb_playlist_t *plt, int iter, int id, const char *format, int order))plt_sort_v2,
432 
433     .playqueue_push = (int (*) (DB_playItem_t *))playqueue_push,
434     .playqueue_clear = playqueue_clear,
435     .playqueue_pop = playqueue_pop,
436     .playqueue_remove = (void (*) (DB_playItem_t *))playqueue_remove,
437     .playqueue_test = (int (*) (DB_playItem_t *))playqueue_test,
438     .playqueue_get_count = (int (*) (void))playqueue_getcount,
439     .playqueue_get_item = (DB_playItem_t *(*) (int n))playqueue_get_item,
440     .playqueue_remove_nth = (int (*) (int n))playqueue_remove_nth,
441     .playqueue_insert_at = (void (*) (int n, DB_playItem_t *it))playqueue_insert_at,
442 
443     .get_system_dir = plug_get_system_dir,
444 
445     .action_set_playlist = action_set_playlist,
446     .action_get_playlist = action_get_playlist,
447 
448     .tf_import_legacy = tf_import_legacy,
449 };
450 
451 DB_functions_t *deadbeef = &deadbeef_api;
452 
453 const char *
plug_get_config_dir(void)454 plug_get_config_dir (void) {
455     return dbconfdir;
456 }
457 
458 const char *
plug_get_prefix(void)459 plug_get_prefix (void) {
460     return dbinstalldir;
461 }
462 
463 const char *
plug_get_doc_dir(void)464 plug_get_doc_dir (void) {
465     return dbdocdir;
466 }
467 
468 const char *
plug_get_plugin_dir(void)469 plug_get_plugin_dir (void) {
470     return dbplugindir;
471 }
472 
473 const char *
plug_get_pixmap_dir(void)474 plug_get_pixmap_dir (void) {
475     return dbpixmapdir;
476 }
477 
478 const char *
plug_get_system_dir(int dir_id)479 plug_get_system_dir (int dir_id) {
480     switch (dir_id) {
481     case DDB_SYS_DIR_CONFIG:
482         return dbconfdir;
483     case DDB_SYS_DIR_PREFIX:
484         return dbinstalldir;
485     case DDB_SYS_DIR_DOC:
486         return dbdocdir;
487     case DDB_SYS_DIR_PLUGIN:
488         return dbplugindir;
489     case DDB_SYS_DIR_PIXMAP:
490         return dbpixmapdir;
491     case DDB_SYS_DIR_CACHE:
492         return dbcachedir;
493     }
494     return NULL;
495 }
496 
497 void
plug_volume_set_db(float db)498 plug_volume_set_db (float db) {
499     volume_set_db (db);
500     messagepump_push (DB_EV_VOLUMECHANGED, 0, 0, 0);
501 }
502 
503 void
plug_volume_set_amp(float amp)504 plug_volume_set_amp (float amp) {
505     volume_set_amp (amp);
506     messagepump_push (DB_EV_VOLUMECHANGED, 0, 0, 0);
507 }
508 
509 void
plug_md5(uint8_t sig[16],const char * in,int len)510 plug_md5 (uint8_t sig[16], const char *in, int len) {
511     md5_state_t st;
512     md5_init (&st);
513     md5_append (&st, in, len);
514     md5_finish (&st, sig);
515 }
516 
517 void
plug_md5_to_str(char * str,const uint8_t sig[16])518 plug_md5_to_str (char *str, const uint8_t sig[16]) {
519     int i = 0;
520     static const char hex[] = "0123456789abcdef";
521     for (i = 0; i < 16; i++) {
522         *str++ = hex[(sig[i]&0xf0)>>4];
523         *str++ = hex[sig[i]&0xf];
524     }
525     *str = 0;
526 }
527 
528 float
plug_playback_get_pos(void)529 plug_playback_get_pos (void) {
530     playItem_t *trk = streamer_get_playing_track ();
531     float dur = trk ? pl_get_item_duration (trk) : -1;
532     if (!trk || dur <= 0) {
533         if (trk) {
534             pl_item_unref (trk);
535         }
536         return 0;
537     }
538     if (trk) {
539         pl_item_unref (trk);
540     }
541     return streamer_get_playpos () * 100 / dur;
542 }
543 
544 void
plug_playback_set_pos(float pos)545 plug_playback_set_pos (float pos) {
546     playItem_t *trk = streamer_get_playing_track ();
547     float dur = trk ? pl_get_item_duration (trk) : -1;
548     if (!trk || dur <= 0) {
549         if (trk) {
550             pl_item_unref (trk);
551         }
552         return;
553     }
554     float t = pos * dur / 100.f;
555     if (trk) {
556         pl_item_unref (trk);
557     }
558     streamer_set_seek (t);
559 }
560 
561 int
plug_init_plugin(DB_plugin_t * (* loadfunc)(DB_functions_t *),void * handle)562 plug_init_plugin (DB_plugin_t* (*loadfunc)(DB_functions_t *), void *handle) {
563     DB_plugin_t *plugin_api = loadfunc (&deadbeef_api);
564     if (!plugin_api) {
565         return -1;
566     }
567 
568     // check if same plugin with the same or bigger version is loaded already
569     plugin_t *prev = NULL;
570     for (plugin_t *p = plugins; p; prev = p, p = p->next) {
571         int same_id = p->plugin->id && plugin_api->id && !strcmp (p->plugin->id, plugin_api->id);
572         int same_name = (!p->plugin->id || !plugin_api->id) && p->plugin->name && plugin_api->name && !strcmp (p->plugin->name, plugin_api->name);
573         if (same_id || same_name) {
574             if (plugin_api->version_major > p->plugin->version_major || (plugin_api->version_major == p->plugin->version_major && plugin_api->version_minor > p->plugin->version_minor)) {
575                 trace ("found newer version of plugin \"%s\" (%s), replacing\n", plugin_api->id, plugin_api->name);
576                 // unload older plugin before replacing
577                 dlclose (p->handle);
578                 if (prev) {
579                     prev->next = p->next;
580                 }
581                 else {
582                     plugins = p->next;
583                 }
584                 if (p->handle) {
585                     dlclose (p->handle);
586                 }
587                 free (p);
588             }
589             else {
590                 trace ("found copy of plugin \"%s\" (%s), but newer version is already loaded\n", plugin_api->id, plugin_api->name)
591                 return -1;
592             }
593         }
594     }
595 
596 #if !DISABLE_VERSIONCHECK
597     if (plugin_api->api_vmajor != 0 || plugin_api->api_vminor != 0) {
598         // version check enabled
599         if (DB_API_VERSION_MAJOR != 9 && DB_API_VERSION_MINOR != 9) {
600             if (plugin_api->api_vmajor != DB_API_VERSION_MAJOR || plugin_api->api_vminor > DB_API_VERSION_MINOR) {
601                 trace ("\033[0;31mWARNING: plugin \"%s\" wants API v%d.%d (got %d.%d), will not be loaded\033[0;m\n", plugin_api->name, plugin_api->api_vmajor, plugin_api->api_vminor, DB_API_VERSION_MAJOR, DB_API_VERSION_MINOR);
602                 return -1;
603             }
604         }
605     }
606     else {
607             trace ("\033[0;31mWARNING: plugin \"%s\" has disabled version check. please don't distribute it!\033[0;m\n", plugin_api->name);
608     }
609 #endif
610 
611     plugin_t *plug = malloc (sizeof (plugin_t));
612     memset (plug, 0, sizeof (plugin_t));
613     plug->plugin = plugin_api;
614     plug->handle = handle;
615 
616     int lowprio = 0;
617     for (int n = 0; lowprio_plugin_ids[n]; n++) {
618         if (plugin_api->id && !strcmp (lowprio_plugin_ids[n], plugin_api->id)) {
619             lowprio = 1;
620             break;
621         }
622     }
623 
624     if (lowprio) {
625         if (plugins_lowprio_tail) {
626             plugins_lowprio_tail->next = plug;
627             plugins_lowprio_tail = plug;
628         }
629         else {
630             plugins_lowprio = plugins_lowprio_tail = plug;
631         }
632     }
633     else {
634         if (plugins_tail) {
635             plugins_tail->next = plug;
636             plugins_tail = plug;
637         }
638         else {
639             plugins = plugins_tail = plug;
640         }
641     }
642 
643     return 0;
644 }
645 
dirent_alphasort(const struct dirent ** a,const struct dirent ** b)646 static int dirent_alphasort (const struct dirent **a, const struct dirent **b) {
647     return strcmp ((*a)->d_name, (*b)->d_name);
648 }
649 
650 void
plug_remove_plugin(void * p)651 plug_remove_plugin (void *p) {
652     int i;
653     for (i = 0; g_plugins[i]; i++) {
654         if (g_plugins[i] == p) {
655             memmove (&g_plugins[i], &g_plugins[i+1], (MAX_PLUGINS+1-i-1) * sizeof (void*));
656         }
657     }
658     for (i = 0; g_decoder_plugins[i]; i++) {
659         if (g_decoder_plugins[i] == p) {
660             memmove (&g_decoder_plugins[i], &g_decoder_plugins[i+1], (MAX_DECODER_PLUGINS+1-i-1) * sizeof (void*));
661             break;
662         }
663     }
664     for (i = 0; g_vfs_plugins[i]; i++) {
665         if (g_vfs_plugins[i] == p) {
666             memmove (&g_vfs_plugins[i], &g_vfs_plugins[i+1], (MAX_VFS_PLUGINS+1-i-1) * sizeof (void*));
667             break;
668         }
669     }
670     for (i = 0; g_dsp_plugins[i]; i++) {
671         if (g_dsp_plugins[i] == p) {
672             memmove (&g_dsp_plugins[i], &g_dsp_plugins[i+1], (MAX_DSP_PLUGINS+1-i-1) * sizeof (void*));
673             break;
674         }
675     }
676     for (i = 0; g_output_plugins[i]; i++) {
677         if (g_output_plugins[i] == p) {
678             memmove (&g_output_plugins[i], &g_output_plugins[i+1], (MAX_OUTPUT_PLUGINS+1-i-1) * sizeof (void*));
679             break;
680         }
681     }
682     for (i = 0; g_playlist_plugins[i]; i++) {
683         if (g_playlist_plugins[i] == p) {
684             memmove (&g_playlist_plugins[i], &g_playlist_plugins[i+1], (MAX_PLAYLIST_PLUGINS+1-i-1) * sizeof (void*));
685             break;
686         }
687     }
688 }
689 
690 // d_name must be writable w/o sideeffects; contain valid .so name
691 // l must be strlen(d_name)
692 static int
load_plugin(const char * plugdir,char * d_name,int l)693 load_plugin (const char *plugdir, char *d_name, int l) {
694     // hack for osx to skip *.0.so files
695     if (strstr (d_name, ".0.so")) {
696         return -1;
697     }
698     char fullname[PATH_MAX];
699     snprintf (fullname, PATH_MAX, "%s/%s", plugdir, d_name);
700 
701     // check if the file exists, to avoid printing bogus errors
702     struct stat s;
703     if (0 != stat (fullname, &s)) {
704         return -1;
705     }
706 
707     trace ("loading plugin %s/%s\n", plugdir, d_name);
708     void *handle = dlopen (fullname, RTLD_NOW);
709     if (!handle) {
710         trace ("dlopen error: %s\n", dlerror ());
711 #if defined(ANDROID) || defined(HAVE_COCOAUI)
712         return -1;
713 #else
714         strcpy (fullname + strlen(fullname) - sizeof (PLUGINEXT)+1, ".fallback.so");
715         trace ("trying %s...\n", fullname);
716         handle = dlopen (fullname, RTLD_NOW);
717         if (!handle) {
718             //trace ("dlopen error: %s\n", dlerror ());
719             return -1;
720         }
721         else {
722             fprintf (stderr, "successfully started fallback plugin %s\n", fullname);
723         }
724 #endif
725     }
726     d_name[l-sizeof (PLUGINEXT)+1] = 0;
727     strcat (d_name, "_load");
728 #ifndef ANDROID
729     DB_plugin_t *(*plug_load)(DB_functions_t *api) = dlsym (handle, d_name);
730 #else
731     DB_plugin_t *(*plug_load)(DB_functions_t *api) = dlsym (handle, d_name+3);
732 #endif
733     if (!plug_load) {
734         trace ("dlsym error: %s (%s)\n", dlerror (), d_name + 3);
735         dlclose (handle);
736         return -1;
737     }
738     if (plug_init_plugin (plug_load, handle) < 0) {
739         d_name[l-sizeof (PLUGINEXT)+1] = 0;
740         dlclose (handle);
741         return -1;
742     }
743     return 0;
744 }
745 
746 static int
load_gui_plugin(const char ** plugdirs)747 load_gui_plugin (const char **plugdirs) {
748 #if defined HAVE_COCOAUI
749     return 0;
750 #endif
751 
752     char conf_gui_plug[100];
753     conf_get_str ("gui_plugin", "GTK2", conf_gui_plug, sizeof (conf_gui_plug));
754     char name[100];
755 
756     // try to load selected plugin
757     for (int i = 0; g_gui_names[i]; i++) {
758         trace ("checking GUI plugin: %s\n", g_gui_names[i]);
759         if (!strcmp (g_gui_names[i], conf_gui_plug)) {
760             trace ("found selected GUI plugin: %s\n", g_gui_names[i]);
761             for (int n = 0; plugdirs[n]; n++) {
762                 snprintf (name, sizeof (name), "ddb_gui_%s" PLUGINEXT, conf_gui_plug);
763                 if (!load_plugin (plugdirs[n], name, strlen (name))) {
764                     return 0;
765                 }
766             }
767             break;
768         }
769     }
770     trace ("selected GUI plugin not found or failed to load, trying to find another GUI plugin\n");
771 
772     // try any plugin
773     for (int i = 0; g_gui_names[i]; i++) {
774         for (int n = 0; plugdirs[n]; n++) {
775             snprintf (name, sizeof (name), "ddb_gui_%s" PLUGINEXT, g_gui_names[i]);
776             if (!load_plugin (plugdirs[n], name, strlen (name))) {
777                 return 0;
778             }
779             else {
780                 trace ("the plugin not found or failed to load\n");
781             }
782         }
783     }
784     return -1;
785 }
786 
787 static int
load_plugin_dir(const char * plugdir,int gui_scan)788 load_plugin_dir (const char *plugdir, int gui_scan) {
789     int n = 0;
790     char conf_blacklist_plugins[1000];
791     conf_get_str ("blacklist_plugins", "", conf_blacklist_plugins, sizeof (conf_blacklist_plugins));
792     if (gui_scan) {
793         trace ("searching for GUI plugins in %s\n", plugdir);
794     }
795     else {
796         trace ("loading plugins from %s\n", plugdir);
797     }
798     struct dirent **namelist = NULL;
799     n = scandir (plugdir, &namelist, NULL, dirent_alphasort);
800     if (n < 0)
801     {
802         if (namelist) {
803             free (namelist);
804         }
805         return 0;
806     }
807     else
808     {
809         trace ("load_plugin_dir %s: scandir found %d files\n", plugdir, n);
810         int i;
811         for (i = 0; i < n; i++)
812         {
813             // skip hidden files and fallback plugins
814             while (namelist[i]->d_name[0] != '.'
815 #if !defined(ANDROID) && !defined(HAVE_COCOAUI)
816                     && !strstr (namelist[i]->d_name, ".fallback.")
817 #elif !defined(ANDROID)
818                     && !strstr (namelist[i]->d_name, "libdeadbeef")
819 #endif
820                   )
821             {
822                 size_t l = strlen (namelist[i]->d_name);
823                 if (l < (sizeof(PLUGINEXT)-1)) {
824                     break;
825                 }
826                 if (strcasecmp (namelist[i]->d_name + l - sizeof(PLUGINEXT) + 1, PLUGINEXT)) {
827                     break;
828                 }
829                 char d_name[256];
830                 memcpy (d_name, namelist[i]->d_name, l+1);
831 #ifndef ANDROID
832                 // no blacklisted
833                 const uint8_t *p = conf_blacklist_plugins;
834                 while (*p) {
835                     const uint8_t *e = p;
836                     while (*e && *e > 0x20) {
837                         e++;
838                     }
839                     if (l-sizeof (PLUGINEXT)+1 == e-p) {
840                         if (!strncmp (p, d_name, e-p)) {
841                             p = NULL;
842                             break;
843                         }
844                     }
845                     p = e;
846                     while (*p && *p <= 0x20) {
847                         p++;
848                     }
849                 }
850                 if (!p) {
851                     trace ("plugin %s is blacklisted in config file\n", d_name);
852                     break;
853                 }
854 #endif
855 
856                 // add gui plugin names
857                 if (!strncmp (d_name, "ddb_gui_", 8)) {
858                     if (gui_scan) {
859                         trace ("found gui plugin %s\n", d_name);
860                         if (g_num_gui_names >= MAX_GUI_PLUGINS) {
861                             fprintf (stderr, "too many gui plugins\n");
862                             break; // no more gui plugins allowed
863                         }
864                         char *nm = d_name + 8;
865                         char *e = strrchr (nm, '.');
866                         if (!e) {
867                             break;
868                         }
869                         if (strcmp (e, PLUGINEXT)) {
870                             break;
871                         }
872                         *e = 0;
873                         // ignore fallbacks
874                         e = strrchr (nm, '.');
875                         if (e && !strcasecmp (e, ".fallback")) {
876                             break;
877                         }
878                         // add to list of unique names
879                         size_t i;
880                         for (i = 0; g_gui_names[i] && strcmp(g_gui_names[i], nm); i++);
881                         if (!g_gui_names[i]) {
882                             g_gui_names[i] = strdup (nm);
883                             g_gui_names[++g_num_gui_names] = NULL;
884                             trace ("added %s gui plugin\n", nm);
885                         }
886                     }
887                     break;
888                 }
889 
890                 if (!gui_scan) {
891                     if (0 != load_plugin (plugdir, d_name, (int)l)) {
892                         trace ("plugin not found or failed to load\n");
893                     }
894                 }
895                 break;
896             }
897             free (namelist[i]);
898         }
899         free (namelist);
900     }
901     return 0;
902 }
903 
904 int
plug_load_all(void)905 plug_load_all (void) {
906 #if DISABLE_VERSIONCHECK
907     trace ("\033[0;31mDISABLE_VERSIONCHECK=1! do not distribute!\033[0;m\n");
908 #endif
909 
910     background_jobs_mutex = mutex_create ();
911 
912     const char *dirname = deadbeef->get_plugin_dir ();
913 
914 #ifdef HAVE_COCOAUI
915     const char *plugins_dirs[] = { dirname, NULL };
916 #else
917 #ifndef ANDROID
918     char *xdg_local_home = getenv ("XDG_LOCAL_HOME");
919     char xdg_plugin_dir[1024];
920     char xdg_plugin_dir_explicit_arch[1024];
921 
922     if (xdg_local_home) {
923         strncpy (xdg_plugin_dir, xdg_local_home, sizeof (xdg_plugin_dir));
924         xdg_plugin_dir[sizeof(xdg_plugin_dir)-1] = 0;
925     } else {
926         char *homedir = getenv ("HOME");
927 
928         if (!homedir) {
929             trace ("plug_load_all: warning: unable to find home directory\n");
930             xdg_plugin_dir[0] = 0;
931         }
932         else {
933             // multilib support:
934             // 1. load from lib$ARCH if present
935             // 2. load from lib if present
936             int written = snprintf (xdg_plugin_dir, sizeof (xdg_plugin_dir), "%s/.local/lib/deadbeef", homedir);
937             if (written > sizeof (xdg_plugin_dir)) {
938                 trace ("warning: XDG_LOCAL_HOME value is too long: %s. Ignoring.", xdg_local_home);
939                 xdg_plugin_dir[0] = 0;
940             }
941             written = snprintf (xdg_plugin_dir_explicit_arch, sizeof (xdg_plugin_dir_explicit_arch), "%s/.local/lib%d/deadbeef", homedir, (int)(sizeof (long) * 8));
942             if (written > sizeof (xdg_plugin_dir_explicit_arch)) {
943                 trace ("warning: XDG_LOCAL_HOME value is too long: %s. Ignoring.", xdg_local_home);
944                 xdg_plugin_dir_explicit_arch[0] = 0;
945             }
946         }
947     }
948 
949     // load from HOME 1st, than replace from installdir if needed
950     const char *plugins_dirs[] = { xdg_plugin_dir_explicit_arch, xdg_plugin_dir, dirname, NULL };
951 
952     // If xdg_plugin_dir and dirname is the same, we should avoid each plugin
953     // to be load twice.
954     // XXX: Here absolute path is assumed, however if dirname is a relative
955     // path it won't work.
956     if (strcmp(xdg_plugin_dir, dirname) == 0) {
957         plugins_dirs[1] = NULL;
958     }
959 #else
960     const char *plugins_dirs[] = { dirname, NULL };
961 #endif
962 #endif
963 
964     int k = 0;
965 #ifndef ANDROID
966     // load gui plugin before others
967     while (plugins_dirs[k]) {
968         const char *plugdir = plugins_dirs[k++];
969         if (!(*plugdir)) {
970             continue;
971         }
972         load_plugin_dir (plugdir, 1);
973     }
974     trace ("load gui plugin\n");
975     load_gui_plugin (plugins_dirs);
976 #endif
977 
978     k = 0;
979     while (plugins_dirs[k]) {
980         const char *plugdir = plugins_dirs[k++];
981         if (!(*plugdir)) {
982             continue;
983         }
984         load_plugin_dir (plugdir, 0);
985     }
986 
987 #ifdef ANDROID
988     char plugin_path[1000];
989     strncpy (plugin_path, conf_get_str_fast ("android.plugin_path", ""), sizeof (plugin_path)-1);
990     plugin_path[sizeof(plugin_path)-1] = 0;
991     char *p = plugin_path;
992     while (*p) {
993         while (*p == ':') {
994             p++;
995         }
996         if (!(*p)) {
997             break;
998         }
999         char *e = strchr (p, ':');
1000         if (e) {
1001             *e = 0;
1002         }
1003 
1004         char path[PATH_MAX];
1005         snprintf (path, sizeof (path), "/data/data/%s/lib", p);
1006         load_plugin_dir (path);
1007         if (!e) {
1008             break;
1009         }
1010         p = e+1;
1011     }
1012 #endif
1013 
1014 // load all compiled-in modules
1015 #define PLUG(n) extern DB_plugin_t * n##_load (DB_functions_t *api);
1016 #include "moduleconf.h"
1017 #undef PLUG
1018 #define PLUG(n) plug_init_plugin (n##_load, NULL);
1019 #include "moduleconf.h"
1020 #undef PLUG
1021 #ifdef ANDROID
1022 #define PLUG(n) extern DB_plugin_t * n##_load (DB_functions_t *api);
1023 #include "moduleconf-android.h"
1024 #undef PLUG
1025 #define PLUG(n) plug_init_plugin (n##_load, NULL);
1026 #include "moduleconf-android.h"
1027 #undef PLUG
1028 #endif
1029 
1030     if (plugins_lowprio) {
1031         if (plugins_tail) {
1032             plugins_tail->next = plugins_lowprio;
1033         }
1034         else {
1035             plugins = plugins_tail = plugins_lowprio;
1036         }
1037         while (plugins_tail->next) {
1038             plugins_tail = plugins_tail->next;
1039         }
1040         plugins_lowprio = plugins_lowprio_tail = NULL;
1041     }
1042 
1043     plugin_t *plug;
1044     // categorize plugins
1045     int numplugins = 0;
1046     int numdecoders = 0;
1047     int numvfs = 0;
1048     int numoutput = 0;
1049     int numdsp = 0;
1050     int numplaylist = 0;
1051     for (plug = plugins; plug; plug = plug->next) {
1052         g_plugins[numplugins++] = plug->plugin;
1053         if (plug->plugin->type == DB_PLUGIN_DECODER) {
1054 //            trace ("found decoder plugin %s\n", plug->plugin->name);
1055             if (numdecoders >= MAX_DECODER_PLUGINS) {
1056                 break;
1057             }
1058             g_decoder_plugins[numdecoders++] = (DB_decoder_t *)plug->plugin;
1059         }
1060         else if (plug->plugin->type == DB_PLUGIN_VFS) {
1061 //            trace ("found vfs plugin %s\n", plug->plugin->name);
1062             if (numvfs >= MAX_VFS_PLUGINS) {
1063                 break;
1064             }
1065             g_vfs_plugins[numvfs++] = (DB_vfs_t *)plug->plugin;
1066         }
1067         else if (plug->plugin->type == DB_PLUGIN_OUTPUT) {
1068 //            trace ("found output plugin %s\n", plug->plugin->name);
1069             if (numoutput >= MAX_OUTPUT_PLUGINS) {
1070                 break;
1071             }
1072             g_output_plugins[numoutput++] = (DB_output_t *)plug->plugin;
1073         }
1074         else if (plug->plugin->type == DB_PLUGIN_DSP) {
1075 //            trace ("found dsp plugin %s\n", plug->plugin->name);
1076             if (numdsp >= MAX_DSP_PLUGINS) {
1077                 break;
1078             }
1079             g_dsp_plugins[numdsp++] = (DB_dsp_t *)plug->plugin;
1080         }
1081         else if (plug->plugin->type == DB_PLUGIN_PLAYLIST) {
1082             if (numplaylist >= MAX_PLAYLIST_PLUGINS) {
1083                 break;
1084             }
1085             g_playlist_plugins[numplaylist++] = (DB_playlist_t *)plug->plugin;
1086         }
1087     }
1088     // start plugins
1089     plugin_t *prev = NULL;
1090     for (plug = plugins; plug;) {
1091         if (plug->plugin->type != DB_PLUGIN_GUI && plug->plugin->start) {
1092             if (plug->plugin->start () < 0) {
1093                 fprintf (stderr, "plugin %s failed to start, deactivated.\n", plug->plugin->name);
1094                 if (plug->plugin->stop) {
1095                     plug->plugin->stop ();
1096                 }
1097                 if (plug->handle) {
1098                     dlclose (plug->handle);
1099                 }
1100                 plug_remove_plugin (plug->plugin);
1101                 if (prev) {
1102                     prev->next = plug->next;
1103                 }
1104                 else {
1105                     plugins = plug->next;
1106                 }
1107                 plugin_t *next = plug->next;
1108                 free (plug);
1109                 plug = next;
1110                 continue;
1111             }
1112         }
1113         prev = plug;
1114         plug = plug->next;
1115     }
1116 //    trace ("numplugins: %d, numdecoders: %d, numvfs: %d\n", numplugins, numdecoders, numvfs);
1117     g_plugins[numplugins] = NULL;
1118     g_decoder_plugins[numdecoders] = NULL;
1119     g_vfs_plugins[numvfs] = NULL;
1120     g_output_plugins[numoutput] = NULL;
1121     g_dsp_plugins[numdsp] = NULL;
1122     g_playlist_plugins[numplaylist] = NULL;
1123 
1124     // select output plugin
1125     if (plug_select_output () < 0) {
1126         trace ("failed to find output plugin!\n");
1127         return -1;
1128     }
1129     return 0;
1130 }
1131 
1132 void
plug_connect_all(void)1133 plug_connect_all (void) {
1134     plugin_t *plug;
1135     plugin_t *prev = NULL;
1136     for (plug = plugins; plug;) {
1137         if (plug->plugin->connect) {
1138             if (plug->plugin->connect () < 0) {
1139                 fprintf (stderr, "plugin %s failed to connect to dependencies, deactivated.\n", plug->plugin->name);
1140 
1141                 if (plug->plugin->disconnect) {
1142                     plug->plugin->disconnect ();
1143                 }
1144                 if (plug->plugin->type != DB_PLUGIN_GUI && plug->plugin->stop) {
1145                     plug->plugin->stop ();
1146                 }
1147                 if (plug->handle) {
1148                     dlclose (plug->handle);
1149                 }
1150                 plug_remove_plugin (plug->plugin);
1151 
1152                 if (prev) {
1153                     prev->next = plug->next;
1154                 }
1155                 else {
1156                     plugins = plug->next;
1157                 }
1158                 plugin_t *next = plug->next;
1159                 free (plug);
1160                 plug = next;
1161                 continue;
1162             }
1163         }
1164         prev = plug;
1165         plug = plug->next;
1166     }
1167 
1168 }
1169 
1170 void
plug_disconnect_all(void)1171 plug_disconnect_all (void) {
1172     trace ("plug_disconnect_all\n");
1173     plugin_t *plug;
1174     plugin_t *prev = NULL;
1175     for (plug = plugins; plug;) {
1176         if (plug->plugin->disconnect) {
1177             if (plug->plugin->disconnect () < 0) {
1178                 trace ("plugin %s failed to disconnect\n", plug->plugin->name);
1179             }
1180         }
1181         prev = plug;
1182         plug = plug->next;
1183     }
1184 }
1185 
1186 void
plug_unload_all(void)1187 plug_unload_all (void) {
1188     action_set_playlist (NULL);
1189     trace ("plug_unload_all\n");
1190     plugin_t *p;
1191     for (p = plugins; p; p = p->next) {
1192         if (p->plugin->stop) {
1193             trace ("stopping %s...\n", p->plugin->name);
1194             fflush (stderr);
1195 #if HAVE_COCOAUI
1196             if (p->plugin->type == DB_PLUGIN_GUI) {
1197                 continue;
1198             }
1199 #endif
1200             p->plugin->stop ();
1201         }
1202     }
1203     trace ("stopped all plugins\n");
1204     while (plugins) {
1205         plugin_t *next = plugins->next;
1206         if (plugins->handle) {
1207             dlclose (plugins->handle);
1208         }
1209         free (plugins);
1210         plugins = next;
1211     }
1212     for (int i = 0; g_gui_names[i]; i++) {
1213         free (g_gui_names[i]);
1214         g_gui_names[i] = NULL;
1215     }
1216     plugins_tail = NULL;
1217 
1218     memset (g_plugins, 0, sizeof (g_plugins));
1219     memset (g_gui_names, 0, sizeof (g_gui_names));
1220     g_num_gui_names = 0;
1221     memset (g_decoder_plugins, 0, sizeof (g_decoder_plugins));
1222     memset (g_vfs_plugins, 0, sizeof (g_vfs_plugins));
1223     memset (g_dsp_plugins, 0, sizeof (g_dsp_plugins));
1224     memset (g_output_plugins, 0, sizeof (g_output_plugins));
1225     output_plugin = NULL;
1226     memset (g_playlist_plugins, 0, sizeof (g_playlist_plugins));
1227 
1228     trace ("all plugins had been unloaded\n");
1229     if (background_jobs_mutex) {
1230         mutex_free (background_jobs_mutex);
1231         background_jobs_mutex = 0;
1232     }
1233 }
1234 
1235 void
plug_set_output(DB_output_t * out)1236 plug_set_output (DB_output_t *out) {
1237     output_plugin = out;
1238 }
1239 
1240 void
plug_cleanup(void)1241 plug_cleanup (void) {
1242     plug_free_decoder_ids ();
1243 }
1244 
1245 struct DB_decoder_s **
plug_get_decoder_list(void)1246 plug_get_decoder_list (void) {
1247     return g_decoder_plugins;
1248 }
1249 
1250 struct DB_vfs_s **
plug_get_vfs_list(void)1251 plug_get_vfs_list (void) {
1252     return g_vfs_plugins;
1253 }
1254 
1255 struct DB_output_s **
plug_get_output_list(void)1256 plug_get_output_list (void) {
1257     return g_output_plugins;
1258 }
1259 
1260 struct DB_dsp_s **
plug_get_dsp_list(void)1261 plug_get_dsp_list (void) {
1262     return g_dsp_plugins;
1263 }
1264 
1265 struct DB_playlist_s **
plug_get_playlist_list(void)1266 plug_get_playlist_list (void) {
1267     return g_playlist_plugins;
1268 }
1269 
1270 struct DB_plugin_s **
plug_get_list(void)1271 plug_get_list (void) {
1272     return g_plugins;
1273 }
1274 
1275 const char **
plug_get_gui_names(void)1276 plug_get_gui_names (void) {
1277     return (const char **)g_gui_names;
1278 }
1279 
1280 DB_output_t *
plug_get_output(void)1281 plug_get_output (void) {
1282     return output_plugin;
1283 }
1284 
1285 int
plug_select_output(void)1286 plug_select_output (void) {
1287 #ifdef ANDROID
1288     return 0;
1289 #else
1290     char outplugname[100];
1291 #ifdef HAVE_COCOAUI
1292     conf_get_str ("output_plugin", "CoreAudio", outplugname, sizeof (outplugname));
1293 #else
1294     conf_get_str ("output_plugin", "ALSA output plugin", outplugname, sizeof (outplugname));
1295 #endif
1296     for (int i = 0; g_output_plugins[i]; i++) {
1297         DB_output_t *p = g_output_plugins[i];
1298         if (!strcmp (p->plugin.name, outplugname)) {
1299             trace ("selected output plugin: %s\n", outplugname);
1300             output_plugin = p;
1301             break;
1302         }
1303     }
1304     if (!output_plugin) {
1305         output_plugin = g_output_plugins[0];
1306         if (output_plugin) {
1307             trace ("selected output plugin: %s\n", output_plugin->plugin.name);
1308             conf_set_str ("output_plugin", output_plugin->plugin.name);
1309         }
1310     }
1311     if (!output_plugin) {
1312         return -1;
1313     }
1314     messagepump_push (DB_EV_OUTPUTCHANGED, 0, 0, 0);
1315     return 0;
1316 #endif
1317 }
1318 
1319 void
plug_reinit_sound(void)1320 plug_reinit_sound (void) {
1321     DB_output_t *prev = plug_get_output ();
1322     int state = OUTPUT_STATE_STOPPED;
1323 
1324     ddb_waveformat_t fmt = {0};
1325 
1326     streamer_get_output_format (&fmt);
1327     if (prev) {
1328         state = prev->state ();
1329         if (!fmt.channels) {
1330             memcpy (&fmt, &prev->fmt, sizeof (fmt));
1331         }
1332         prev->free ();
1333     }
1334 
1335     if (plug_select_output () < 0) {
1336         char outplugname[100];
1337 #ifdef HAVE_COCOAUI
1338         conf_get_str ("output_plugin", "core audio output plugin", outplugname, sizeof (outplugname));
1339 #else
1340         conf_get_str ("output_plugin", "ALSA output plugin", outplugname, sizeof (outplugname));
1341 #endif
1342         trace ("failed to select output plugin %s\nreverted to %s\n", outplugname, prev->plugin.name);
1343         output_plugin = prev;
1344     }
1345     DB_output_t *output = plug_get_output ();
1346     if (fmt.channels) {
1347         output->setformat (&fmt);
1348     }
1349     if (output->init () < 0) {
1350         streamer_reset (1);
1351         streamer_set_nextsong (-2, 0);
1352         return;
1353     }
1354 
1355     if (state != OUTPUT_STATE_PAUSED && state != OUTPUT_STATE_STOPPED) {
1356         if (output->play () < 0) {
1357             trace ("failed to reinit sound output\n");
1358             streamer_set_nextsong (-2, 0);
1359         }
1360     }
1361 }
1362 
1363 // list of all unique decoder ids used in current session
1364 static char *decoder_ids[MAX_DECODER_PLUGINS];
1365 
1366 const char *
plug_get_decoder_id(const char * id)1367 plug_get_decoder_id (const char *id) {
1368     int i;
1369     char **lastnull = NULL;
1370     for (i = 0; i < MAX_DECODER_PLUGINS; i++) {
1371         if (decoder_ids[i] && !strcmp (id, decoder_ids[i])) {
1372             return decoder_ids[i];
1373         }
1374         else if (!lastnull && !decoder_ids[i]) {
1375             lastnull = &decoder_ids[i];
1376         }
1377     }
1378     if (!lastnull) {
1379         return NULL;
1380     }
1381     char *newid = strdup (id);
1382     *lastnull = newid;
1383     return newid;
1384 }
1385 
1386 void
plug_remove_decoder_id(const char * id)1387 plug_remove_decoder_id (const char *id) {
1388     int i;
1389     for (i = 0; i < MAX_DECODER_PLUGINS; i++) {
1390         if (decoder_ids[i] && !strcmp (decoder_ids[i], id)) {
1391             free (decoder_ids[i]);
1392             decoder_ids[i] = NULL;
1393         }
1394     }
1395 }
1396 
1397 void
plug_free_decoder_ids(void)1398 plug_free_decoder_ids (void) {
1399     int i;
1400     for (i = 0; i < MAX_DECODER_PLUGINS; i++) {
1401         if (decoder_ids[i]) {
1402             free (decoder_ids[i]);
1403             decoder_ids[i] = NULL;
1404         }
1405     }
1406 }
1407 
1408 DB_decoder_t *
plug_get_decoder_for_id(const char * id)1409 plug_get_decoder_for_id (const char *id) {
1410     DB_decoder_t **plugins = plug_get_decoder_list ();
1411     for (int c = 0; plugins[c]; c++) {
1412         if (!strcmp (id, plugins[c]->plugin.id)) {
1413             return plugins[c];
1414         }
1415     }
1416     return NULL;
1417 }
1418 
1419 DB_plugin_t *
plug_get_for_id(const char * id)1420 plug_get_for_id (const char *id) {
1421     DB_plugin_t **plugins = plug_get_list ();
1422     for (int c = 0; plugins[c]; c++) {
1423         if (plugins[c]->id && !strcmp (id, plugins[c]->id)) {
1424             return plugins[c];
1425         }
1426     }
1427     return NULL;
1428 }
1429 
1430 int
plug_is_local_file(const char * fname)1431 plug_is_local_file (const char *fname) {
1432     if (!strncasecmp (fname, "file://", 7)) {
1433         return 1;
1434     }
1435 
1436     const char *f = fname;
1437     for (; *f; f++) {
1438         if (!strncmp (f, "://", 3)) {
1439             DB_vfs_t **plug = plug_get_vfs_list ();
1440             for (int i = 0; plug[i]; i++) {
1441                 if (plug[i]->get_schemes && plug[i]->is_streaming && plug[i]->is_streaming()) {
1442                     const char **sch = plug[i]->get_schemes ();
1443                     for (int k = 0; sch[k]; k++) {
1444                         if (!strncmp (sch[k], fname, strlen (sch[k]))) {
1445                             return 0;
1446                         }
1447                     }
1448                 }
1449             }
1450             break;
1451         }
1452     }
1453 
1454     return 1;
1455 }
1456 
1457 void
background_job_increment(void)1458 background_job_increment (void) {
1459     mutex_lock (background_jobs_mutex);
1460     num_background_jobs++;
1461     mutex_unlock (background_jobs_mutex);
1462 }
1463 
1464 void
background_job_decrement(void)1465 background_job_decrement (void) {
1466     mutex_lock (background_jobs_mutex);
1467     num_background_jobs--;
1468     mutex_unlock (background_jobs_mutex);
1469 }
1470 
1471 int
have_background_jobs(void)1472 have_background_jobs (void) {
1473     return num_background_jobs;
1474 }
1475 
1476 static ddb_playlist_t *action_playlist;
1477 
1478 void
action_set_playlist(ddb_playlist_t * plt)1479 action_set_playlist (ddb_playlist_t *plt) {
1480     if (action_playlist) {
1481         plt_unref ((playlist_t *)action_playlist);
1482     }
1483     action_playlist = plt;
1484     if (action_playlist) {
1485         plt_ref ((playlist_t *)action_playlist);
1486     }
1487 }
1488 
1489 ddb_playlist_t *
action_get_playlist(void)1490 action_get_playlist (void) {
1491     if (!action_playlist) {
1492         return (ddb_playlist_t *)plt_get_curr ();
1493     }
1494 
1495     plt_ref ((playlist_t *)action_playlist);
1496     return action_playlist;
1497 }
1498