1 /*
2 * Copyright (C) 2010, 2011 Igalia S.L.
3 * Copyright (C) 2011 Intel Corporation.
4 *
5 * Contact: Iago Toral Quiroga <itoral@igalia.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 *
22 */
23
24 /**
25 * SECTION:grl-registry
26 * @short_description: Grilo plugins loader and manager
27 * @see_also: #GrlPlugin, #GrlSource
28 *
29 * The registry holds the metadata of a set of plugins.
30 *
31 * The #GrlRegistry object is a list of plugins and some functions
32 * for dealing with them. Each #GrlPlugin is matched 1-1 with a file
33 * on disk, and may or may not be loaded a given time. There only can be
34 * a single instance of #GrlRegistry (singleton pattern).
35 *
36 * A #GrlPlugin can hold several data #GrlSource sources, and #GrlRegistry
37 * shall register each one of them.
38 */
39
40 #include "grl-registry.h"
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include "grl-registry-priv.h"
47 #include "grl-plugin-priv.h"
48 #include "grl-log.h"
49 #include "grl-error.h"
50
51 #include <glib/gi18n-lib.h>
52 #include <string.h>
53 #include <gmodule.h>
54 #include <libxml/parser.h>
55
56 #define GRL_LOG_DOMAIN_DEFAULT registry_log_domain
57 GRL_LOG_DOMAIN(registry_log_domain);
58
59 #define XML_ROOT_ELEMENT_NAME "plugin"
60
61 #define GRL_PLUGIN_INFO_SUFFIX "xml"
62
63 #define GRL_PLUGIN_INFO_MODULE "module"
64
65 #define LOCAL_NET_TAG "net:local"
66 #define INTERNET_NET_TAG "net:internet"
67
68 #define SET_INVISIBLE_SOURCE(src, val) \
69 g_object_set_data(G_OBJECT(src), "invisible", GINT_TO_POINTER(val))
70 #define SOURCE_IS_INVISIBLE(src) \
71 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(src), "invisible"))
72
73 /* GQuark-like implementation, where we manually assign the first IDs. */
74 struct KeyIDHandler {
75 GHashTable *string_to_id;
76 GArray *id_to_string;
77 gint last_id;
78 };
79
80 struct _GrlRegistryPrivate {
81 GHashTable *configs;
82 GHashTable *plugins;
83 GHashTable *sources;
84 GHashTable *related_keys;
85 GHashTable *system_keys;
86 GHashTable *ranks;
87 GSList *plugins_dir;
88 GSList *allowed_plugins;
89 gboolean all_plugins_preloaded;
90 struct KeyIDHandler key_id_handler;
91 GNetworkMonitor *netmon;
92 };
93
94 static void grl_registry_setup_ranks (GrlRegistry *registry);
95
96 static void key_id_handler_init (struct KeyIDHandler *handler);
97
98 static void key_id_handler_free (struct KeyIDHandler *handler);
99
100 static GrlKeyID key_id_handler_get_key (struct KeyIDHandler *handler,
101 const gchar *key_name);
102
103 static const gchar *key_id_handler_get_name (struct KeyIDHandler *handler,
104 GrlKeyID key);
105
106 static GrlKeyID key_id_handler_add (struct KeyIDHandler *handler,
107 GrlKeyID key, const gchar *key_name);
108
109 static gboolean param_spec_is_equal (GParamSpec *curr, GParamSpec *new);
110
111 static void shutdown_plugin (GrlPlugin *plugin);
112
113 static void configs_free (GList *configs);
114
115 static GrlPlugin *grl_registry_prepare_plugin (GrlRegistry *registry,
116 const gchar *library_filename,
117 GError **error);
118
119 /* ================ GrlRegistry GObject ================ */
120
121 enum {
122 SIG_SOURCE_ADDED,
123 SIG_SOURCE_REMOVED,
124 SIG_METADATA_KEY_ADDED,
125 SIG_LAST
126 };
127 static gint registry_signals[SIG_LAST];
128
129 G_DEFINE_TYPE_WITH_PRIVATE (GrlRegistry, grl_registry, G_TYPE_OBJECT);
130
131 static void
grl_registry_class_init(GrlRegistryClass * klass)132 grl_registry_class_init (GrlRegistryClass *klass)
133 {
134 /**
135 * GrlRegistry::source-added:
136 * @registry: the registry
137 * @source: the source that has been added
138 *
139 * Signals that a source has been added to the registry.
140 *
141 * Since: 0.2.0
142 */
143 registry_signals[SIG_SOURCE_ADDED] =
144 g_signal_new("source-added",
145 G_TYPE_FROM_CLASS(klass),
146 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
147 0,
148 NULL,
149 NULL,
150 g_cclosure_marshal_VOID__OBJECT,
151 G_TYPE_NONE, 1, GRL_TYPE_SOURCE);
152
153 /**
154 * GrlRegistry::source-removed:
155 * @registry: the registry
156 * @source: the source that has been removed
157 *
158 * Signals that a source has been removed from the registry.
159 *
160 * Since: 0.2.0
161 */
162 registry_signals[SIG_SOURCE_REMOVED] =
163 g_signal_new("source-removed",
164 G_TYPE_FROM_CLASS(klass),
165 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
166 0,
167 NULL,
168 NULL,
169 g_cclosure_marshal_VOID__OBJECT,
170 G_TYPE_NONE, 1, GRL_TYPE_SOURCE);
171
172 /**
173 * GrlRegistry::metadata-key-added:
174 * @registry: the registry
175 * @key: the name of the new key added
176 *
177 * Signals that a new metadata key has been registered.
178 *
179 * Since: 0.2.10
180 */
181 registry_signals[SIG_METADATA_KEY_ADDED] =
182 g_signal_new("metadata-key-added",
183 G_TYPE_FROM_CLASS(klass),
184 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
185 0,
186 NULL,
187 NULL,
188 g_cclosure_marshal_VOID__STRING,
189 G_TYPE_NONE, 1, G_TYPE_STRING);
190 }
191
192 static void
get_connectivity(GrlRegistry * registry,GNetworkConnectivity * connectivity,gboolean * network_available)193 get_connectivity (GrlRegistry *registry,
194 GNetworkConnectivity *connectivity,
195 gboolean *network_available)
196 {
197 g_assert (connectivity != NULL);
198 g_assert (network_available != NULL);
199
200 if (g_getenv("GRL_NET_MOCKED") != NULL) {
201 GRL_DEBUG ("Mocked network, assuming network is available and connectivity "
202 "level is FULL");
203 *connectivity = G_NETWORK_CONNECTIVITY_FULL;
204 *network_available = TRUE;
205 } else {
206 g_object_get (G_OBJECT (registry->priv->netmon),
207 "connectivity", connectivity,
208 "network-available", network_available,
209 NULL);
210
211 GRL_DEBUG ("Connectivity level is %d, Network is %s",
212 *connectivity, *network_available ? "available" : "unavailable");
213 }
214 }
215
216 static void
network_changed_cb(GObject * gobject,GParamSpec * pspec,GrlRegistry * registry)217 network_changed_cb (GObject *gobject,
218 GParamSpec *pspec,
219 GrlRegistry *registry)
220 {
221 GNetworkConnectivity connectivity;
222 gboolean network_available;
223 GrlSource *current_source;
224 GList *sources, *l;
225
226 GRL_DEBUG ("Network availability changed");
227 get_connectivity (registry, &connectivity, &network_available);
228
229 sources = g_hash_table_get_values (registry->priv->sources);
230 if (!sources)
231 return;
232
233 if (!network_available) {
234 for (l = sources; l != NULL; l = l->next) {
235 const char **tags;
236
237 current_source = l->data;
238 tags = grl_source_get_tags (current_source);
239
240 if (!tags)
241 continue;
242
243 if ((g_strv_contains (tags, LOCAL_NET_TAG) ||
244 g_strv_contains (tags, INTERNET_NET_TAG)) &&
245 !SOURCE_IS_INVISIBLE(current_source)) {
246 GRL_DEBUG ("Network isn't available for '%s', hiding",
247 grl_source_get_id (current_source));
248 SET_INVISIBLE_SOURCE(current_source, TRUE);
249 g_signal_emit (registry, registry_signals[SIG_SOURCE_REMOVED], 0, current_source);
250 }
251 }
252 } else {
253 GList *to_add, *to_remove;
254
255 to_add = to_remove = NULL;
256
257 for (l = sources; l != NULL; l = l->next) {
258 const char **tags;
259
260 current_source = l->data;
261 tags = grl_source_get_tags (current_source);
262
263 if (!tags)
264 continue;
265
266 if (g_strv_contains (tags, LOCAL_NET_TAG) &&
267 SOURCE_IS_INVISIBLE(current_source)) {
268 GRL_DEBUG ("Local network became available for '%s', showing",
269 grl_source_get_id (current_source));
270 to_add = g_list_prepend (to_add, current_source);
271 }
272
273 if (g_strv_contains (tags, INTERNET_NET_TAG) &&
274 connectivity == G_NETWORK_CONNECTIVITY_FULL &&
275 SOURCE_IS_INVISIBLE(current_source)) {
276 GRL_DEBUG ("Internet became available for '%s', showing",
277 grl_source_get_id (current_source));
278 to_add = g_list_prepend (to_add, current_source);
279 }
280
281 if (g_strv_contains (tags, INTERNET_NET_TAG) &&
282 connectivity != G_NETWORK_CONNECTIVITY_FULL &&
283 !SOURCE_IS_INVISIBLE(current_source)) {
284 GRL_DEBUG ("Internet became unavailable for '%s', hiding",
285 grl_source_get_id (current_source));
286 to_remove = g_list_prepend (to_remove, current_source);
287 }
288 }
289
290 for (l = to_add; l != NULL; l = l->next) {
291 SET_INVISIBLE_SOURCE(l->data, FALSE);
292 g_signal_emit (registry, registry_signals[SIG_SOURCE_ADDED], 0, l->data);
293 }
294 g_list_free (to_add);
295
296 for (l = to_remove; l != NULL; l = l->next) {
297 SET_INVISIBLE_SOURCE(l->data, TRUE);
298 g_signal_emit (registry, registry_signals[SIG_SOURCE_REMOVED], 0, l->data);
299 }
300 g_list_free (to_remove);
301 }
302
303 g_list_free (sources);
304 }
305
306 static void
grl_registry_init(GrlRegistry * registry)307 grl_registry_init (GrlRegistry *registry)
308 {
309 registry->priv = grl_registry_get_instance_private (registry);
310
311 registry->priv->configs =
312 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) configs_free);
313 registry->priv->plugins =
314 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
315 registry->priv->sources =
316 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
317 registry->priv->related_keys =
318 g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
319 registry->priv->system_keys =
320 g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_param_spec_unref);
321
322 registry->priv->netmon = g_network_monitor_get_default ();
323 g_signal_connect (G_OBJECT (registry->priv->netmon), "notify::connectivity",
324 G_CALLBACK (network_changed_cb), registry);
325 g_signal_connect (G_OBJECT (registry->priv->netmon), "notify::network-available",
326 G_CALLBACK (network_changed_cb), registry);
327
328 key_id_handler_init (®istry->priv->key_id_handler);
329
330 grl_registry_setup_ranks (registry);
331
332 const gchar *config_path = g_getenv (GRL_CONFIG_PATH_VAR);
333 if (config_path != NULL)
334 grl_registry_add_config_from_file (registry, config_path, NULL);
335 }
336
337 /* ================ Utitilies ================ */
338
339 static void
configs_free(GList * configs)340 configs_free (GList *configs)
341 {
342 g_list_free_full (configs, g_object_unref);
343 }
344
345 static void
update_source_visibility(GrlRegistry * registry,GrlSource * source)346 update_source_visibility (GrlRegistry *registry,
347 GrlSource *source)
348 {
349 GNetworkConnectivity connectivity;
350 gboolean network_available;
351 const char **tags;
352 gboolean needs_local, needs_inet;
353
354 tags = grl_source_get_tags (source);
355 if (!tags)
356 return;
357
358 needs_local = g_strv_contains (tags, LOCAL_NET_TAG);
359 needs_inet = g_strv_contains (tags, INTERNET_NET_TAG);
360
361 if (!needs_local &&
362 !needs_inet)
363 return;
364
365 get_connectivity (registry, &connectivity, &network_available);
366
367 GRL_DEBUG ("Source %s needs %s %s%s",
368 grl_source_get_id (source),
369 needs_local ? "local network" : "",
370 needs_inet && needs_local ? " and " : "",
371 needs_inet ? "Internet" : "");
372
373 if (!network_available) {
374 if (needs_local || needs_inet) {
375 GRL_DEBUG ("Network isn't available for '%s', hiding",
376 grl_source_get_id (source));
377 SET_INVISIBLE_SOURCE(source, TRUE);
378 }
379 } else {
380 if (connectivity != G_NETWORK_CONNECTIVITY_FULL) {
381 if (needs_inet) {
382 GRL_DEBUG ("Internet isn't available for '%s', hiding",
383 grl_source_get_id (source));
384 SET_INVISIBLE_SOURCE(source, TRUE);
385 }
386 }
387 }
388 }
389
390 static void
config_source_rank(GrlRegistry * registry,const gchar * source_id,gint rank)391 config_source_rank (GrlRegistry *registry,
392 const gchar *source_id,
393 gint rank)
394 {
395 GRL_DEBUG ("Rank configuration, '%s:%d'", source_id, rank);
396 g_hash_table_insert (registry->priv->ranks,
397 g_strdup (source_id),
398 GINT_TO_POINTER (rank));
399 }
400
401 static void
set_source_rank(GrlRegistry * registry,GrlSource * source)402 set_source_rank (GrlRegistry *registry, GrlSource *source)
403 {
404 gint rank;
405
406 rank =
407 GPOINTER_TO_INT (g_hash_table_lookup (registry->priv->ranks,
408 grl_source_get_id (source)));
409
410 if (!rank) {
411 GHashTableIter iter;
412 gpointer key, value;
413
414 g_hash_table_iter_init(&iter, registry->priv->ranks);
415
416 while (g_hash_table_iter_next (&iter, &key, &value)) {
417 if (g_pattern_match_simple (key, grl_source_get_id (source))) {
418 rank = GPOINTER_TO_INT (value);
419 break;
420 }
421 }
422 }
423
424 if (!rank) {
425 rank = GRL_RANK_DEFAULT;
426 }
427
428 g_object_set (source, "rank", rank, NULL);
429 GRL_DEBUG ("Source rank '%s' : %d", grl_source_get_id (source), rank);
430 }
431
432 static void
grl_registry_setup_ranks(GrlRegistry * registry)433 grl_registry_setup_ranks (GrlRegistry *registry)
434 {
435 const gchar *ranks_env;
436 gchar **rank_specs;
437 gchar **iter;
438
439 registry->priv->ranks = g_hash_table_new_full (g_str_hash, g_str_equal,
440 g_free, NULL);
441
442 ranks_env = g_getenv (GRL_PLUGIN_RANKS_VAR);
443 if (!ranks_env) {
444 GRL_DEBUG ("$%s is not set, using default ranks.", GRL_PLUGIN_RANKS_VAR);
445 return;
446 }
447
448 rank_specs = g_strsplit (ranks_env, ",", 0);
449 iter = rank_specs;
450
451 while (*iter) {
452 gchar **rank_info = g_strsplit (*iter, ":", 2);
453 if (rank_info[0] && rank_info[1]) {
454 gchar *tmp;
455 gchar *id = rank_info[0];
456 gchar *srank = rank_info[1];
457 gint rank = (gint) g_ascii_strtoll (srank, &tmp, 10);
458 if (*tmp != '\0') {
459 GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
460 } else {
461 config_source_rank (registry, id, rank);
462 }
463 } else {
464 GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
465 }
466 g_strfreev (rank_info);
467 iter++;
468 }
469
470 g_strfreev (rank_specs);
471 }
472
473 static gint
compare_by_rank(gconstpointer a,gconstpointer b)474 compare_by_rank (gconstpointer a,
475 gconstpointer b) {
476 gint rank_a;
477 gint rank_b;
478
479 rank_a = grl_source_get_rank (GRL_SOURCE (a));
480 rank_b = grl_source_get_rank (GRL_SOURCE (b));
481
482 return (rank_a < rank_b) - (rank_a > rank_b);
483 }
484
485 static gboolean
register_keys_plugin(GrlRegistry * registry,GrlPlugin * plugin,GError ** error)486 register_keys_plugin (GrlRegistry *registry,
487 GrlPlugin *plugin,
488 GError **error)
489 {
490 gboolean is_loaded;
491
492 /* Check if plugin is already loaded */
493 g_object_get (plugin, "loaded", &is_loaded, NULL);
494 if (is_loaded) {
495 GRL_WARNING ("Plugin is already loaded: '%s'", grl_plugin_get_id (plugin));
496 g_set_error (error,
497 GRL_CORE_ERROR,
498 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
499 _("Plugin “%s” is already loaded"), grl_plugin_get_id (plugin));
500 return FALSE;
501 }
502
503 grl_plugin_register_keys (plugin);
504
505 return TRUE;
506 }
507
508 static gboolean
activate_plugin(GrlRegistry * registry,GrlPlugin * plugin,GError ** error)509 activate_plugin (GrlRegistry *registry,
510 GrlPlugin *plugin,
511 GError **error)
512 {
513 GList *plugin_configs;
514
515 plugin_configs = g_hash_table_lookup (registry->priv->configs,
516 grl_plugin_get_id (plugin));
517
518 if (!grl_plugin_load (plugin, plugin_configs)) {
519 GRL_DEBUG ("Failed to initialize plugin from %s. Check if plugin is well configured", grl_plugin_get_filename (plugin));
520 g_set_error (error,
521 GRL_CORE_ERROR,
522 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
523 _("Failed to initialize plugin from %s"), grl_plugin_get_filename (plugin));
524 shutdown_plugin (plugin);
525 return FALSE;
526 }
527
528 GRL_DEBUG ("Loaded plugin '%s' from '%s'",
529 grl_plugin_get_id (plugin),
530 grl_plugin_get_filename (plugin));
531
532 return TRUE;
533 }
534
535 static GrlKeyID
grl_registry_register_metadata_key_full(GrlRegistry * registry,GParamSpec * param_spec,GrlKeyID key,GrlKeyID bind_key,GError ** error)536 grl_registry_register_metadata_key_full (GrlRegistry *registry,
537 GParamSpec *param_spec,
538 GrlKeyID key,
539 GrlKeyID bind_key,
540 GError **error)
541 {
542 GList *bound_partners;
543 GList *partner;
544 const gchar *key_name;
545
546 g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
547 g_return_val_if_fail (G_IS_PARAM_SPEC (param_spec), 0);
548 GrlKeyID registered_key;
549
550 key_name = g_param_spec_get_name (param_spec);
551 registered_key = key_id_handler_get_key (®istry->priv->key_id_handler, key_name);
552 if (registered_key != GRL_METADATA_KEY_INVALID) {
553 GParamSpec *key_spec = g_hash_table_lookup (registry->priv->system_keys,
554 (gpointer) key_name);
555 if (param_spec_is_equal (key_spec, param_spec)) {
556 /* Key registered */
557 GRL_DEBUG ("metadata key '%s' already registered with same spec", key_name);
558 g_param_spec_unref (param_spec);
559 return registered_key;
560 } else {
561 GRL_WARNING ("metadata key '%s' already exists", key_name);
562 g_set_error (error,
563 GRL_CORE_ERROR,
564 GRL_CORE_ERROR_REGISTER_METADATA_KEY_FAILED,
565 _("Metadata key “%s” already registered in different format"),
566 key_name);
567 return GRL_METADATA_KEY_INVALID;
568 }
569 }
570
571 registered_key = key_id_handler_add (®istry->priv->key_id_handler, key, key_name);
572
573 if (registered_key == GRL_METADATA_KEY_INVALID) {
574 GRL_WARNING ("metadata key '%s' cannot be registered", key_name);
575 g_set_error (error,
576 GRL_CORE_ERROR,
577 GRL_CORE_ERROR_REGISTER_METADATA_KEY_FAILED,
578 _("Metadata key “%s” cannot be registered"),
579 key_name);
580
581 return GRL_METADATA_KEY_INVALID;
582 }
583
584 g_hash_table_insert (registry->priv->system_keys,
585 (gpointer) key_name,
586 param_spec);
587
588 if (bind_key == GRL_METADATA_KEY_INVALID) {
589 /* Key is only related to itself */
590 g_hash_table_insert (registry->priv->related_keys,
591 GRLKEYID_TO_POINTER (registered_key),
592 g_list_prepend (NULL,
593 GRLKEYID_TO_POINTER (registered_key)));
594 } else {
595 /* Add the new key to the partners */
596 bound_partners = g_hash_table_lookup (registry->priv->related_keys, GRLKEYID_TO_POINTER (bind_key));
597 bound_partners = g_list_append (bound_partners, GRLKEYID_TO_POINTER (registered_key));
598 for (partner = bound_partners;
599 partner;
600 partner = g_list_next (partner)) {
601 g_hash_table_insert (registry->priv->related_keys,
602 partner->data,
603 bound_partners);
604 }
605 }
606
607 return registered_key;
608 }
609
610 G_GNUC_INTERNAL GrlKeyID
grl_registry_register_metadata_key_for_type(GrlRegistry * registry,const gchar * key_name,GType type,GrlKeyID bind_key)611 grl_registry_register_metadata_key_for_type (GrlRegistry *registry,
612 const gchar *key_name,
613 GType type,
614 GrlKeyID bind_key)
615 {
616 GParamSpec *spec;
617
618 switch (type) {
619 case G_TYPE_INT:
620 spec = g_param_spec_int (key_name,
621 key_name,
622 key_name,
623 0, G_MAXINT,
624 0,
625 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
626 break;
627
628 case G_TYPE_INT64:
629 spec = g_param_spec_int64 (key_name,
630 key_name,
631 key_name,
632 -1, G_MAXINT64,
633 -1,
634 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
635 break;
636
637 case G_TYPE_STRING:
638 spec = g_param_spec_string (key_name,
639 key_name,
640 key_name,
641 NULL,
642 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
643 break;
644
645 case G_TYPE_BOOLEAN:
646 spec = g_param_spec_boolean (key_name,
647 key_name,
648 key_name,
649 FALSE,
650 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
651 break;
652
653 case G_TYPE_FLOAT:
654 spec = g_param_spec_float (key_name,
655 key_name,
656 key_name,
657 0, G_MAXFLOAT,
658 0,
659 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
660 break;
661
662 default:
663 if (type == G_TYPE_DATE_TIME) {
664 spec = g_param_spec_boxed (key_name,
665 key_name,
666 key_name,
667 G_TYPE_DATE_TIME,
668 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
669 } else {
670 GRL_WARNING ("'%s' is being ignored as G_TYPE '%s' is not being handled",
671 key_name, g_type_name (type));
672 return GRL_METADATA_KEY_INVALID;
673 }
674 }
675
676 return grl_registry_register_metadata_key (registry, spec, bind_key, NULL);
677 }
678
679 /*
680 * Returns whether the string is a canonical one.
681 */
682 static gboolean
is_canonical(const gchar * key)683 is_canonical (const gchar *key)
684 {
685 if (key == NULL) {
686 return FALSE;
687 }
688
689 for (; *key != '\0'; key++) {
690 if (*key != '-' &&
691 (*key < '0' || *key > '9') &&
692 (*key < 'A' || *key > 'Z') &&
693 (*key < 'a' || *key > 'z'))
694 return FALSE;
695 }
696
697 return TRUE;
698 }
699
700 G_GNUC_INTERNAL GrlKeyID
grl_registry_register_or_lookup_metadata_key(GrlRegistry * registry,const gchar * key_name,const GValue * value,GrlKeyID bind_key)701 grl_registry_register_or_lookup_metadata_key (GrlRegistry *registry,
702 const gchar *key_name,
703 const GValue *value,
704 GrlKeyID bind_key)
705 {
706 GrlKeyID key;
707 GType value_type;
708
709 g_return_val_if_fail (GRL_IS_REGISTRY (registry), GRL_METADATA_KEY_INVALID);
710
711 if (value == NULL)
712 return GRL_METADATA_KEY_INVALID;
713
714 key_name = g_intern_string (key_name);
715 g_return_val_if_fail (is_canonical (key_name), GRL_METADATA_KEY_INVALID);
716
717 key = grl_registry_lookup_metadata_key (registry, key_name);
718 value_type = G_VALUE_TYPE (value);
719
720 if (key == GRL_METADATA_KEY_INVALID) {
721 GRL_DEBUG ("%s is not a registered metadata-key", key_name);
722 key = grl_registry_register_metadata_key_for_type (registry, key_name, value_type, bind_key);
723 } else {
724 GType key_type = grl_registry_lookup_metadata_key_type (registry, key);
725 if (!g_value_type_transformable (value_type, key_type)) {
726 GRL_WARNING ("Value type %s can't be set to existing metadata-key of type %s",
727 g_type_name (value_type),
728 g_type_name (key_type));
729 return GRL_METADATA_KEY_INVALID;
730 }
731 }
732
733 return key;
734 }
735
736 static void
key_id_handler_init(struct KeyIDHandler * handler)737 key_id_handler_init (struct KeyIDHandler *handler)
738 {
739 const gchar *null_string = NULL;
740 handler->string_to_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
741 handler->id_to_string = g_array_new (FALSE, /* zero terminated */
742 TRUE, /* zero-initialised */
743 sizeof (const gchar *));
744 /* We want indices in ->id_to_string to start from 1, so we add a NULL entry
745 * for GRL_METADATA_KEY_INVALID (i.e. 0) */
746 g_array_insert_val (handler->id_to_string,
747 GRL_METADATA_KEY_INVALID,
748 null_string);
749 }
750
751 static void
key_id_handler_free(struct KeyIDHandler * handler)752 key_id_handler_free (struct KeyIDHandler *handler)
753 {
754 g_hash_table_unref (handler->string_to_id);
755 g_array_unref (handler->id_to_string);
756 }
757
758 static
key_id_handler_get_key(struct KeyIDHandler * handler,const gchar * key_name)759 GrlKeyID key_id_handler_get_key (struct KeyIDHandler *handler, const gchar *key_name)
760 {
761 gpointer val = g_hash_table_lookup (handler->string_to_id, key_name);
762 if (val == NULL)
763 return GRL_METADATA_KEY_INVALID;
764
765 return GRLPOINTER_TO_KEYID (val);
766 }
767
768 static const gchar *
key_id_handler_get_name(struct KeyIDHandler * handler,GrlKeyID key)769 key_id_handler_get_name (struct KeyIDHandler *handler, GrlKeyID key)
770 {
771 if (key < handler->id_to_string->len)
772 return g_array_index (handler->id_to_string, const gchar *, key);
773
774 return NULL;
775 }
776
777 /*
778 * key_id_handler_add:
779 * @handler: the handler
780 * @key: a specific key for system keys, or GRL_METADATA_KEY_INVALID for it to
781 * be assigned
782 * @name: the name of the key.
783 *
784 * Add a new key<->name correspondence.
785 *
786 * Returns: the new key number, or GRL_METADATA_KEY_INVALID if the key could
787 * not be created (typically if @key or @name is already registered).
788 */
789 static GrlKeyID
key_id_handler_add(struct KeyIDHandler * handler,GrlKeyID key,const gchar * name)790 key_id_handler_add (struct KeyIDHandler *handler, GrlKeyID key, const gchar *name)
791 {
792 GrlKeyID _key = key;
793
794 if (_key == GRL_METADATA_KEY_INVALID) {
795 /* existing keys go from 1 to (id_to_string->len - 1), so the next
796 * available key is id_to_string->len, which will be incremented by
797 * g_array_insert_val() */
798 _key = handler->id_to_string->len;
799 }
800
801 if (NULL != key_id_handler_get_name (handler, _key)) {
802 GRL_WARNING ("Cannot register %d:%s because key is already defined as %s",
803 _key, name, key_id_handler_get_name (handler, _key));
804 return GRL_METADATA_KEY_INVALID;
805 } else if ( GRL_METADATA_KEY_INVALID != key_id_handler_get_key (handler, name)) {
806 /* _key or name is already in use! */
807 GRL_WARNING ("Cannot register %d:%s because name is already registered with key %d",
808 _key, name, key_id_handler_get_key (handler, name));
809 return GRL_METADATA_KEY_INVALID;
810 } else {
811 /* name_copy is shared between handler->id_to_string and
812 * handler->string_to_id */
813 gchar *name_copy = g_strdup (name);
814
815 if (_key >= handler->id_to_string->len)
816 g_array_set_size (handler->id_to_string, _key + 1);
817
818 /* yes, g_array_index() is a macro that give you an lvalue */
819 g_array_index (handler->id_to_string, const gchar *, _key) = name_copy;
820 g_hash_table_insert (handler->string_to_id,
821 name_copy, GRLKEYID_TO_POINTER (_key));
822 }
823
824 return _key;
825
826 }
827
828 static GList *
key_id_handler_get_all_keys(struct KeyIDHandler * handler)829 key_id_handler_get_all_keys (struct KeyIDHandler *handler)
830 {
831 return g_hash_table_get_values (handler->string_to_id);
832 }
833
834 #define CHECK_NUMERIC_PARAM_SPEC_LIMITS(is_type, cast_type, a, b) { \
835 if (is_type) { \
836 if ((cast_type(a))->maximum != (cast_type(b))->maximum || \
837 (cast_type(a))->minimum != (cast_type(b))->minimum || \
838 (cast_type(a))->default_value != (cast_type(b))->default_value) \
839 return FALSE; \
840 return TRUE; \
841 } \
842 }
843
844 /* @curr: The current spec we have
845 * @new: The spec to match
846 *
847 * Returns: true if specs are the same, false otherwise.
848 */
849 static gboolean
param_spec_is_equal(GParamSpec * cur,GParamSpec * new)850 param_spec_is_equal (GParamSpec *cur,
851 GParamSpec *new)
852 {
853 GType ctype = G_PARAM_SPEC_TYPE (cur);
854
855 if (ctype != G_PARAM_SPEC_TYPE (new))
856 return FALSE;
857
858 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_INT),
859 G_PARAM_SPEC_INT, cur, new);
860 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_LONG),
861 G_PARAM_SPEC_LONG, cur, new);
862 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_INT64),
863 G_PARAM_SPEC_INT64, cur, new);
864 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_CHAR),
865 G_PARAM_SPEC_CHAR, cur, new);
866 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_UINT),
867 G_PARAM_SPEC_UINT, cur, new);
868 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_ULONG),
869 G_PARAM_SPEC_ULONG, cur, new);
870 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_UINT64),
871 G_PARAM_SPEC_UINT64, cur, new);
872 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_UCHAR),
873 G_PARAM_SPEC_UCHAR, cur, new);
874 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_FLOAT),
875 G_PARAM_SPEC_FLOAT, cur, new);
876 CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_DOUBLE),
877 G_PARAM_SPEC_DOUBLE, cur, new);
878 if (ctype == G_TYPE_PARAM_STRING) {
879 GParamSpecString *c = G_PARAM_SPEC_STRING (cur);
880 GParamSpecString *n = G_PARAM_SPEC_STRING (new);
881 return (g_strcmp0 (c->default_value, n->default_value) == 0);
882 } else if (ctype == G_TYPE_PARAM_ENUM) {
883 GParamSpecEnum *c = G_PARAM_SPEC_ENUM (cur);
884 GParamSpecEnum *n = G_PARAM_SPEC_ENUM (new);
885 if (c->default_value != n->default_value ||
886 cur->value_type != new->value_type) {
887 GRL_DEBUG ("%s differ (values: %d and %d) (types: %s and %s)",
888 g_type_name (ctype), c->default_value, n->default_value,
889 g_type_name (cur->value_type), g_type_name (new->value_type));
890 return FALSE;
891 }
892 } else if (ctype == G_TYPE_PARAM_FLAGS) {
893 GParamSpecFlags *c = G_PARAM_SPEC_FLAGS (cur);
894 GParamSpecFlags *n = G_PARAM_SPEC_FLAGS (new);
895 if (c->default_value != n->default_value ||
896 cur->value_type != new->value_type) {
897 GRL_DEBUG ("%s differ (values: %d and %d) (types: %s and %s)",
898 g_type_name (ctype), c->default_value, n->default_value,
899 g_type_name (cur->value_type), g_type_name (new->value_type));
900 return FALSE;
901 }
902 } else if (ctype == G_TYPE_PARAM_BOOLEAN) {
903 GParamSpecBoolean *c = G_PARAM_SPEC_BOOLEAN (cur);
904 GParamSpecBoolean *n = G_PARAM_SPEC_BOOLEAN (new);
905 if (c->default_value != n->default_value) {
906 GRL_DEBUG ("%s type differ: %s != %s", g_type_name (ctype),
907 g_type_name (cur->value_type), g_type_name (new->value_type));
908 return FALSE;
909 }
910 } else if (ctype == G_TYPE_PARAM_BOXED || ctype == G_TYPE_PARAM_OBJECT) {
911 if (cur->value_type != new->value_type) {
912 GRL_DEBUG ("%s type differ: %s != %s", g_type_name (ctype),
913 g_type_name (cur->value_type), g_type_name (new->value_type));
914 return FALSE;
915 }
916 } else {
917 g_warn_if_reached();
918 return FALSE;
919 }
920
921 return TRUE;
922 }
923
924 #undef CHECK_NUMERIC_PARAM_SPEC_LIMITS
925
926 static void
shutdown_plugin(GrlPlugin * plugin)927 shutdown_plugin (GrlPlugin *plugin)
928 {
929 GRL_DEBUG ("Unloading plugin '%s'", grl_plugin_get_id (plugin));
930 grl_plugin_unload (plugin);
931
932 if (grl_plugin_get_module (plugin)) {
933 g_module_close (grl_plugin_get_module (plugin));
934 grl_plugin_set_module (plugin, NULL);
935 }
936 }
937
938 /* ================ PRIVATE API ================ */
939
940 /*
941 * grl_registry_restrict_plugins:
942 * @registry: the registry instance
943 * @plugins: a @NULL-terminated array of plugins identifiers
944 *
945 * Restrict the plugins that application sees to this list.
946 *
947 * Other plugins will not be available for the application, unless it uses
948 * directly #grl_registry_load_plugin() function.
949 **/
950 void
grl_registry_restrict_plugins(GrlRegistry * registry,gchar ** plugins)951 grl_registry_restrict_plugins (GrlRegistry *registry,
952 gchar **plugins)
953 {
954 g_return_if_fail (GRL_IS_REGISTRY (registry));
955 g_return_if_fail (plugins);
956
957 /* Free previous list */
958 if (registry->priv->allowed_plugins) {
959 g_slist_free_full (registry->priv->allowed_plugins, g_free);
960 registry->priv->allowed_plugins = NULL;
961 }
962
963 while (*plugins) {
964 registry->priv->allowed_plugins = g_slist_prepend (registry->priv->allowed_plugins,
965 g_strdup (*plugins));
966 plugins++;
967 }
968 }
969
970 /*
971 * grl_registry_shutdown:
972 * @registry: the registry instance
973 *
974 * Frees all the resources in the registry and the registry itself.
975 **/
976 void
grl_registry_shutdown(GrlRegistry * registry)977 grl_registry_shutdown (GrlRegistry *registry)
978 {
979 GHashTableIter iter;
980 GList *each_key;
981 GList *related_keys = NULL;
982 GrlPlugin *plugin = NULL;
983 GrlSource *source = NULL;
984
985 if (registry->priv->plugins) {
986 g_hash_table_iter_init (&iter, registry->priv->plugins);
987 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &plugin)) {
988 shutdown_plugin (plugin);
989 }
990 g_clear_pointer (®istry->priv->plugins, g_hash_table_unref);
991 }
992
993 if (registry->priv->sources) {
994 g_hash_table_iter_init (&iter, registry->priv->sources);
995 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &source)) {
996 g_object_unref (source);
997 }
998 g_clear_pointer (®istry->priv->sources, g_hash_table_unref);
999 }
1000
1001 g_clear_pointer (®istry->priv->ranks, g_hash_table_unref);
1002 g_clear_pointer (®istry->priv->configs, g_hash_table_unref);
1003
1004 /* We need to free this table with care. Several keys can be pointing to the
1005 same value, so we need to ensure that we only free the value once */
1006 if (registry->priv->related_keys) {
1007 while (TRUE) {
1008 g_hash_table_iter_init (&iter, registry->priv->related_keys);
1009 if (!g_hash_table_iter_next (&iter, NULL, (gpointer *) &related_keys)) {
1010 break;
1011 }
1012 /* This will invalidate the iterator */
1013 for (each_key = related_keys; each_key; each_key = g_list_next (each_key)) {
1014 g_hash_table_remove (registry->priv->related_keys, GRLKEYID_TO_POINTER (each_key->data));
1015 }
1016 g_list_free (related_keys);
1017 }
1018 g_clear_pointer (®istry->priv->related_keys, g_hash_table_unref);
1019 }
1020
1021 g_slist_free_full (registry->priv->plugins_dir, (GDestroyNotify) g_free);
1022 g_slist_free_full (registry->priv->allowed_plugins, (GDestroyNotify) g_free);
1023
1024 key_id_handler_free (®istry->priv->key_id_handler);
1025 g_clear_pointer (®istry->priv->system_keys, g_hash_table_unref);
1026
1027 g_object_unref (registry);
1028 }
1029
1030 /* ================ PUBLIC API ================ */
1031
1032 /**
1033 * grl_registry_get_default:
1034 *
1035 * As the registry is designed to work as a singleton, this
1036 * method is in charge of creating the only instance or
1037 * returned it if it is already in memory.
1038 *
1039 * Returns: (transfer none): a new or an already created instance of the registry.
1040 *
1041 * It is NOT MT-safe
1042 *
1043 * Since: 0.2.0
1044 */
1045 GrlRegistry *
grl_registry_get_default(void)1046 grl_registry_get_default (void)
1047 {
1048 static GrlRegistry *registry = NULL;
1049
1050 if (!registry) {
1051 registry = g_object_new (GRL_TYPE_REGISTRY, NULL);
1052 g_object_add_weak_pointer (G_OBJECT (registry), (gpointer *) ®istry);
1053 }
1054
1055 return registry;
1056 }
1057
1058 /**
1059 * grl_registry_register_source:
1060 * @registry: the registry instance
1061 * @plugin: the plugin which owns the source
1062 * @source: (transfer full): the source to register
1063 * @error: error return location or @NULL to ignore
1064 *
1065 * Register a @source in the @registry with the given @plugin information
1066 *
1067 * Returns: %TRUE if success, %FALSE% otherwise.
1068 *
1069 * Since: 0.2.0
1070 */
1071 gboolean
grl_registry_register_source(GrlRegistry * registry,GrlPlugin * plugin,GrlSource * source,GError ** error)1072 grl_registry_register_source (GrlRegistry *registry,
1073 GrlPlugin *plugin,
1074 GrlSource *source,
1075 GError **error)
1076 {
1077 gchar *id;
1078
1079 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1080 g_return_val_if_fail (GRL_IS_PLUGIN (plugin), FALSE);
1081 g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
1082
1083 g_object_get (source, "source-id", &id, NULL);
1084 GRL_DEBUG ("New source available: '%s'", id);
1085
1086 /* Take ownership of the source */
1087 g_object_ref_sink (source);
1088 g_object_unref (source);
1089
1090 /* Do not free id, since g_hash_table_insert does not copy,
1091 it will be freed when removed from the hash table */
1092 g_hash_table_insert (registry->priv->sources, id, source);
1093
1094 /* Set the plugin as owner of source */
1095 g_object_set (source, "plugin", plugin, NULL);
1096
1097 /* Set source rank */
1098 set_source_rank (registry, source);
1099
1100 /* Update whether it should be invisible */
1101 update_source_visibility (registry, source);
1102
1103 if (!SOURCE_IS_INVISIBLE(source))
1104 g_signal_emit (registry, registry_signals[SIG_SOURCE_ADDED], 0, source);
1105
1106 return TRUE;
1107 }
1108
1109 /**
1110 * grl_registry_unregister_source:
1111 * @registry: the registry instance
1112 * @source: the source to unregister
1113 * @error: error return location or @NULL to ignore
1114 *
1115 * Removes the @source from the @registry hash table
1116 *
1117 * Returns: %TRUE if success, %FALSE% otherwise.
1118 *
1119 * Since: 0.2.0
1120 */
1121 gboolean
grl_registry_unregister_source(GrlRegistry * registry,GrlSource * source,GError ** error)1122 grl_registry_unregister_source (GrlRegistry *registry,
1123 GrlSource *source,
1124 GError **error)
1125 {
1126 gchar *id;
1127 gboolean ret = TRUE;
1128
1129 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1130 g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
1131
1132 g_object_get (source, "source-id", &id, NULL);
1133 GRL_DEBUG ("Unregistering source '%s'", id);
1134
1135 if (g_hash_table_remove (registry->priv->sources, id)) {
1136 GRL_DEBUG ("source '%s' is no longer available", id);
1137 g_signal_emit (registry, registry_signals[SIG_SOURCE_REMOVED], 0, source);
1138 g_object_unref (source);
1139 } else {
1140 GRL_WARNING ("source '%s' not found", id);
1141 g_set_error (error,
1142 GRL_CORE_ERROR,
1143 GRL_CORE_ERROR_UNREGISTER_SOURCE_FAILED,
1144 _("Source with id “%s” was not found"), id);
1145 ret = FALSE;
1146 }
1147
1148 g_free (id);
1149 return ret;
1150 }
1151
1152 /**
1153 * grl_registry_add_directory:
1154 * @registry: the registry instance
1155 * @path: a path with plugins
1156 *
1157 * Set this path as part of default paths to load plugins.
1158 *
1159 * Since: 0.2.0
1160 **/
1161 void
grl_registry_add_directory(GrlRegistry * registry,const gchar * path)1162 grl_registry_add_directory (GrlRegistry *registry,
1163 const gchar *path)
1164 {
1165 g_return_if_fail (GRL_IS_REGISTRY (registry));
1166 g_return_if_fail (path);
1167
1168 /* Use append instead of prepend so plugins are loaded in the same order as
1169 they were added */
1170 registry->priv->plugins_dir = g_slist_append (registry->priv->plugins_dir,
1171 g_strdup (path));
1172 registry->priv->all_plugins_preloaded = FALSE;
1173 }
1174
1175 static GrlPlugin *
grl_registry_prepare_plugin_from_desc(GrlRegistry * registry,GrlPluginDescriptor * plugin_desc)1176 grl_registry_prepare_plugin_from_desc (GrlRegistry *registry,
1177 GrlPluginDescriptor *plugin_desc)
1178 {
1179 GrlPlugin *plugin;
1180
1181 if (!plugin_desc->init ||
1182 !plugin_desc->id) {
1183 GRL_WARNING ("Plugin descriptor is not valid");
1184 return NULL;
1185 }
1186
1187 plugin = g_object_new (GRL_TYPE_PLUGIN, NULL);
1188 grl_plugin_set_id (plugin, plugin_desc->id);
1189 grl_plugin_set_filename (plugin, plugin_desc->id);
1190
1191 grl_plugin_set_load_func (plugin, plugin_desc->init);
1192 grl_plugin_set_unload_func (plugin, plugin_desc->deinit);
1193 grl_plugin_set_register_keys_func (plugin, plugin_desc->register_keys);
1194
1195 /* Insert plugin ID as part of plugin information */
1196 grl_plugin_set_module_name (plugin, plugin_desc->id);
1197
1198 return plugin;
1199 }
1200
1201 static GrlPlugin *
grl_registry_prepare_plugin(GrlRegistry * registry,const gchar * library_filename,GError ** error)1202 grl_registry_prepare_plugin (GrlRegistry *registry,
1203 const gchar *library_filename,
1204 GError **error)
1205 {
1206 GModule *module;
1207 GrlPluginDescriptor *plugin_desc;
1208 GrlPlugin *plugin;
1209
1210 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1211
1212 module = g_module_open (library_filename, G_MODULE_BIND_LOCAL);
1213 if (!module) {
1214 GRL_WARNING ("Failed to open module: %s", g_module_error ());
1215 g_set_error (error,
1216 GRL_CORE_ERROR,
1217 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1218 _("Failed to load plugin from %s"), library_filename);
1219 return NULL;
1220 }
1221
1222 if (!g_module_symbol (module, "GRL_PLUGIN_DESCRIPTOR", (gpointer) &plugin_desc)) {
1223 GRL_WARNING ("Plugin descriptor not found in '%s'", library_filename);
1224 g_set_error (error,
1225 GRL_CORE_ERROR,
1226 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1227 _("Invalid plugin file %s"), library_filename);
1228 g_module_close (module);
1229 return NULL;
1230 }
1231
1232 if (!plugin_desc->init ||
1233 !plugin_desc->id) {
1234 GRL_WARNING ("Plugin descriptor is not valid: '%s'", library_filename);
1235 g_set_error (error,
1236 GRL_CORE_ERROR,
1237 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1238 _("“%s” is not a valid plugin file"), library_filename);
1239 g_module_close (module);
1240 return NULL;
1241 }
1242
1243 /* Check if plugin is preloaded; if not, then create one */
1244 plugin = g_hash_table_lookup (registry->priv->plugins,
1245 plugin_desc->id);
1246
1247 if (plugin) {
1248 g_module_close (module);
1249 /* Check if the existent plugin is precisely this same plugin */
1250 if (g_strcmp0 (grl_plugin_get_filename (plugin), library_filename) == 0) {
1251 return plugin;
1252 } else {
1253 GRL_WARNING ("Plugin '%s' already exists", library_filename);
1254 g_set_error (error,
1255 GRL_CORE_ERROR,
1256 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1257 _("Plugin “%s” already exists"), library_filename);
1258 return NULL;
1259 }
1260 }
1261
1262 /* Check if plugin is allowed */
1263 if (registry->priv->allowed_plugins &&
1264 !g_slist_find_custom (registry->priv->allowed_plugins,
1265 plugin_desc->id,
1266 (GCompareFunc) g_strcmp0)) {
1267 GRL_DEBUG ("Plugin '%s' not allowed; skipping", plugin_desc->id);
1268 g_module_close (module);
1269 return NULL;
1270 }
1271
1272 plugin = g_object_new (GRL_TYPE_PLUGIN, NULL);
1273 grl_plugin_set_desc (plugin, plugin_desc);
1274 grl_plugin_set_module (plugin, module);
1275 grl_plugin_set_filename (plugin, library_filename);
1276
1277 /* Make plugin resident */
1278 g_module_make_resident (module);
1279
1280 g_hash_table_insert (registry->priv->plugins, g_strdup (plugin_desc->id), plugin);
1281
1282 /* Register custom keys */
1283 grl_plugin_register_keys (plugin);
1284
1285 return plugin;
1286 }
1287
1288 /**
1289 * grl_registry_activate_all_plugins:
1290 * @registry: the registry instace
1291 *
1292 * Activate all the plugins loaded.
1293 *
1294 * Returns: %TRUE if some plugin has been activated
1295 *
1296 * Since: 0.3.0
1297 **/
1298 gboolean
grl_registry_activate_all_plugins(GrlRegistry * registry)1299 grl_registry_activate_all_plugins (GrlRegistry *registry)
1300 {
1301 GList *all_plugins;
1302 GList *l;
1303 gboolean plugin_activated = FALSE;
1304
1305 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1306
1307 all_plugins = g_hash_table_get_values (registry->priv->plugins);
1308 for (l = all_plugins; l; l = l->next) {
1309 GrlPlugin *plugin = l->data;
1310 plugin_activated |= activate_plugin (registry, plugin, NULL);
1311 }
1312 g_list_free (all_plugins);
1313
1314 return plugin_activated;
1315 }
1316
1317 /**
1318 * grl_registry_load_plugin:
1319 * @registry: the registry instance
1320 * @library_filename: the path to the so file
1321 * @error: error return location or @NULL to ignore
1322 *
1323 * Loads a module from shared object file stored in @path
1324 *
1325 * Returns: %TRUE if the module is loaded correctly
1326 *
1327 * Since: 0.2.0
1328 */
1329 gboolean
grl_registry_load_plugin(GrlRegistry * registry,const gchar * library_filename,GError ** error)1330 grl_registry_load_plugin (GrlRegistry *registry,
1331 const gchar *library_filename,
1332 GError **error)
1333 {
1334 GrlPlugin *plugin;
1335
1336 plugin = grl_registry_prepare_plugin (registry, library_filename, error);
1337 if (!plugin)
1338 return FALSE;
1339
1340 return register_keys_plugin (registry, plugin, error);
1341 }
1342
1343 /**
1344 * grl_registry_load_plugin_from_desc: (skip)
1345 * @registry: the registry instance
1346 * @plugin_desc: the #GrlPluginDescriptor for the plugin
1347 * @error: error return location or @NULL to ignore
1348 *
1349 * Loads the grilo plugin defined by @plugin_desc. This is
1350 * used to load plugins that aren't shared libraries, and are
1351 * built into applications.
1352 *
1353 * <example>
1354 * Minimal example for loading a builtin plugin, in C.
1355 * <programlisting>
1356 * static GrlPluginDescriptor descriptor = {
1357 * .plugin_id = "grl-example",
1358 * .plugin_init = grl_example_plugin_init,
1359 * };
1360 *
1361 * grl_registry_load_plugin_from_desc (registry, &descriptor, &error);
1362 * </programlisting>
1363 * </example>
1364 *
1365 * Returns: %TRUE if the plugin is initialised correctly
1366 *
1367 * Since: 0.3.0
1368 */
1369 gboolean
grl_registry_load_plugin_from_desc(GrlRegistry * registry,GrlPluginDescriptor * plugin_desc,GError ** error)1370 grl_registry_load_plugin_from_desc (GrlRegistry *registry,
1371 GrlPluginDescriptor *plugin_desc,
1372 GError **error)
1373 {
1374 GrlPlugin *plugin;
1375
1376 plugin = grl_registry_prepare_plugin_from_desc (registry, plugin_desc);
1377 if (!plugin)
1378 return FALSE;
1379
1380 return register_keys_plugin (registry, plugin, error) &&
1381 activate_plugin (registry, plugin, error);
1382 }
1383
1384 /**
1385 * grl_registry_load_plugin_directory:
1386 * @registry: the registry instance
1387 * @path: the path to the directory
1388 * @error: error return location or @NULL to ignore
1389 *
1390 * Loads a set of modules from directory in @path which contains
1391 * a group shared object files.
1392 *
1393 * Returns: %TRUE if the directory is valid.
1394 *
1395 * Since: 0.2.0
1396 */
1397 gboolean
grl_registry_load_plugin_directory(GrlRegistry * registry,const gchar * path,GError ** error)1398 grl_registry_load_plugin_directory (GrlRegistry *registry,
1399 const gchar *path,
1400 GError **error)
1401 {
1402 GDir *dir;
1403 GError *dir_error = NULL;
1404 GrlPlugin *plugin;
1405 const gchar *entry;
1406 gboolean plugin_loaded = FALSE;
1407 gchar *filename;
1408
1409 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1410 g_return_val_if_fail (path, FALSE);
1411
1412 dir = g_dir_open (path, 0, &dir_error);
1413 if (!dir) {
1414 GRL_WARNING ("Could not open directory '%s': %s",
1415 path,
1416 dir_error->message);
1417 g_set_error (error,
1418 GRL_CORE_ERROR,
1419 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1420 _("Invalid path %s"), path);
1421 g_error_free (dir_error);
1422 return FALSE;
1423 }
1424
1425 while ((entry = g_dir_read_name (dir)) != NULL) {
1426 filename = g_build_filename (path, entry, NULL);
1427 if (!g_str_has_suffix (filename, "." G_MODULE_SUFFIX)) {
1428 g_free (filename);
1429 continue;
1430 }
1431
1432 plugin = grl_registry_prepare_plugin (registry, filename, NULL);
1433 plugin_loaded |= (plugin != NULL);
1434 g_free (filename);
1435 }
1436 g_dir_close (dir);
1437
1438 return plugin_loaded;
1439 }
1440
1441 /**
1442 * grl_registry_load_all_plugins:
1443 * @registry: the registry instance
1444 * @activate: %TRUE if plugins must be activated after loading
1445 * @error: error return location or @NULL to ignore
1446 *
1447 * Load all the modules available in the default directory path.
1448 *
1449 * The default directory path can be changed through the environment
1450 * variable %GRL_PLUGIN_PATH and it can contain several paths separated
1451 * by ":"
1452 *
1453 * Returns: %FALSE% is all the configured plugin paths are invalid,
1454 * %TRUE% otherwise.
1455 *
1456 * Since: 0.2.0
1457 */
1458 gboolean
grl_registry_load_all_plugins(GrlRegistry * registry,gboolean activate,GError ** error)1459 grl_registry_load_all_plugins (GrlRegistry *registry,
1460 gboolean activate,
1461 GError **error)
1462 {
1463 GSList *plugin_dir;
1464 gboolean loaded_one;
1465
1466 g_return_val_if_fail (GRL_IS_REGISTRY (registry), TRUE);
1467
1468 /* Preload all plugins */
1469 if (!registry->priv->all_plugins_preloaded) {
1470 for (plugin_dir = registry->priv->plugins_dir;
1471 plugin_dir;
1472 plugin_dir = g_slist_next (plugin_dir)) {
1473 grl_registry_load_plugin_directory (registry,
1474 plugin_dir->data,
1475 NULL);
1476 }
1477 registry->priv->all_plugins_preloaded = TRUE;
1478 }
1479
1480 if (activate) {
1481 loaded_one = grl_registry_activate_all_plugins (registry);
1482
1483 if (!loaded_one) {
1484 g_set_error (error,
1485 GRL_CORE_ERROR,
1486 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1487 _("All configured plugin paths are invalid"));
1488 }
1489
1490 return loaded_one;
1491 } else {
1492 return TRUE;
1493 }
1494 }
1495
1496 /**
1497 * grl_registry_activate_plugin_by_id:
1498 * @registry: the registry instance
1499 * @plugin_id: plugin identifier
1500 * @error: error return location or @NULL to ignore
1501 *
1502 * Activates plugin identified by @plugin_id.
1503 *
1504 * Returns: %TRUE if the plugin is loaded correctly
1505 *
1506 * Since: 0.3.0
1507 **/
1508 gboolean
grl_registry_activate_plugin_by_id(GrlRegistry * registry,const gchar * plugin_id,GError ** error)1509 grl_registry_activate_plugin_by_id (GrlRegistry *registry,
1510 const gchar *plugin_id,
1511 GError **error)
1512 {
1513 GrlPlugin *plugin;
1514 gboolean is_loaded;
1515
1516 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1517 g_return_val_if_fail (plugin_id, FALSE);
1518
1519
1520 /* Check if plugin is available */
1521 plugin = g_hash_table_lookup (registry->priv->plugins, plugin_id);
1522 if (!plugin) {
1523 GRL_WARNING ("Plugin '%s' not available", plugin_id);
1524 g_set_error (error,
1525 GRL_CORE_ERROR,
1526 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1527 _("Plugin “%s” not available"), plugin_id);
1528 return FALSE;
1529 }
1530
1531 /* Check if plugin is already loaded */
1532 g_object_get (plugin, "loaded", &is_loaded, NULL);
1533 if (is_loaded) {
1534 GRL_WARNING ("Plugin '%s' is already loaded", plugin_id);
1535 g_set_error (error,
1536 GRL_CORE_ERROR,
1537 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
1538 _("Plugin “%s” is already loaded"), plugin_id);
1539 return FALSE;
1540 }
1541
1542 /* activate plugin */
1543 return activate_plugin (registry, plugin, error);
1544 }
1545
1546 /**
1547 * grl_registry_lookup_source:
1548 * @registry: the registry instance
1549 * @source_id: the id of a source
1550 *
1551 * This function will search and retrieve a source given its identifier.
1552 *
1553 * Returns: (transfer none): The source found.
1554 *
1555 * Since: 0.2.0
1556 */
1557 GrlSource *
grl_registry_lookup_source(GrlRegistry * registry,const gchar * source_id)1558 grl_registry_lookup_source (GrlRegistry *registry,
1559 const gchar *source_id)
1560 {
1561 GrlSource *source;
1562
1563 g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
1564 g_return_val_if_fail (source_id != NULL, NULL);
1565
1566 source = (GrlSource *) g_hash_table_lookup (registry->priv->sources,
1567 source_id);
1568 if (source && !SOURCE_IS_INVISIBLE(source))
1569 return source;
1570 return NULL;
1571 }
1572
1573 /**
1574 * grl_registry_get_sources:
1575 * @registry: the registry instance
1576 * @ranked: whether the returned list shall be returned ordered by rank
1577 *
1578 * This function will return all the available sources in the @registry.
1579 *
1580 * If @ranked is %TRUE, the source list will be ordered by rank.
1581 *
1582 * Returns: (element-type GrlSource) (transfer container): a #GList of
1583 * available #GrlSource<!-- -->s. The content of the list should not be
1584 * modified or freed. Use g_list_free() when done using the list.
1585 *
1586 * Since: 0.2.0
1587 */
1588 GList *
grl_registry_get_sources(GrlRegistry * registry,gboolean ranked)1589 grl_registry_get_sources (GrlRegistry *registry,
1590 gboolean ranked)
1591 {
1592 GHashTableIter iter;
1593 GList *source_list = NULL;
1594 GrlSource *current_source;
1595
1596 g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
1597
1598 g_hash_table_iter_init (&iter, registry->priv->sources);
1599 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) ¤t_source)) {
1600 if (!SOURCE_IS_INVISIBLE(current_source))
1601 source_list = g_list_prepend (source_list, current_source);
1602 }
1603
1604 if (ranked) {
1605 source_list = g_list_sort (source_list, (GCompareFunc) compare_by_rank);
1606 }
1607
1608 return source_list;
1609 }
1610
1611 /**
1612 * grl_registry_get_sources_by_operations:
1613 * @registry: the registry instance
1614 * @ops: a bitwise mangle of the requested operations.
1615 * @ranked: whether the returned list shall be returned ordered by rank
1616 *
1617 * Give an array of all the available sources in the @registry capable of
1618 * perform the operations requested in @ops.
1619 *
1620 * If @ranked is %TRUE, the source list will be ordered by rank.
1621 *
1622 * Returns: (element-type GrlSource) (transfer container): a #GList of
1623 * available #GrlSource<!-- -->s. The content of the list should not be
1624 * modified or freed. Use g_list_free() when done using the list.
1625 *
1626 * Since: 0.2.0
1627 */
1628 GList *
grl_registry_get_sources_by_operations(GrlRegistry * registry,GrlSupportedOps ops,gboolean ranked)1629 grl_registry_get_sources_by_operations (GrlRegistry *registry,
1630 GrlSupportedOps ops,
1631 gboolean ranked)
1632 {
1633 GHashTableIter iter;
1634 GList *source_list = NULL;
1635 GrlSource *source;
1636
1637 g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
1638
1639 g_hash_table_iter_init (&iter, registry->priv->sources);
1640 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &source)) {
1641 GrlSupportedOps source_ops;
1642 source_ops =
1643 grl_source_supported_operations (source);
1644 if ((source_ops & ops) == ops &&
1645 !SOURCE_IS_INVISIBLE(source)) {
1646 source_list = g_list_prepend (source_list, source);
1647 }
1648 }
1649
1650 if (ranked) {
1651 source_list = g_list_sort (source_list, compare_by_rank);
1652 }
1653
1654 return source_list;
1655 }
1656
1657 /**
1658 * grl_registry_lookup_plugin:
1659 * @registry: the registry instance
1660 * @plugin_id: the id of a plugin
1661 *
1662 * This function will search and retrieve a plugin given its identifier.
1663 *
1664 * Returns: (transfer none): The plugin found
1665 *
1666 * Since: 0.2.0
1667 **/
1668 GrlPlugin *
grl_registry_lookup_plugin(GrlRegistry * registry,const gchar * plugin_id)1669 grl_registry_lookup_plugin (GrlRegistry *registry,
1670 const gchar *plugin_id)
1671 {
1672 g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
1673 g_return_val_if_fail (plugin_id, NULL);
1674
1675 return (GrlPlugin *) g_hash_table_lookup (registry->priv->plugins,
1676 plugin_id);
1677 }
1678
1679 /**
1680 * grl_registry_get_plugins:
1681 * @registry: the registry instance
1682 * @only_loaded: whether the returned list shall include only loaded plugins
1683 *
1684 * This function will return all the available plugins in the @registry.
1685 *
1686 * If @only_loaded is %TRUE, the plugin list will contain only plugins that are
1687 * loaded.
1688 *
1689 * Returns: (element-type GrlPlugin) (transfer container): a #GList of
1690 * available #GrlPlugin<!-- -->s. The content of the list should not be modified
1691 * or freed. Use g_list_free() when done using the list.
1692 *
1693 * Since: 0.2.0
1694 **/
1695 GList *
grl_registry_get_plugins(GrlRegistry * registry,gboolean only_loaded)1696 grl_registry_get_plugins (GrlRegistry *registry,
1697 gboolean only_loaded)
1698 {
1699 GList *plugin_list = NULL;
1700 GHashTableIter iter;
1701 GrlPlugin *current_plugin;
1702 gboolean is_loaded;
1703
1704 g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
1705
1706 if (only_loaded) {
1707 g_hash_table_iter_init (&iter, registry->priv->plugins);
1708 while (g_hash_table_iter_next (&iter, NULL, (gpointer *) ¤t_plugin)) {
1709 g_object_get (current_plugin, "loaded", &is_loaded, NULL);
1710 if (is_loaded) {
1711 plugin_list = g_list_prepend (plugin_list, current_plugin);
1712 }
1713 }
1714 } else {
1715 plugin_list = g_hash_table_get_values (registry->priv->plugins);
1716 }
1717
1718 return plugin_list;
1719 }
1720
1721 /**
1722 * grl_registry_unload_plugin:
1723 * @registry: the registry instance
1724 * @plugin_id: the identifier of the plugin
1725 * @error: error return location or @NULL to ignore
1726 *
1727 * Unload from memory a module identified by @plugin_id. This means call the
1728 * module's deinit function.
1729 *
1730 * Returns: %TRUE% on success.
1731 *
1732 * Since: 0.2.0
1733 */
1734 gboolean
grl_registry_unload_plugin(GrlRegistry * registry,const gchar * plugin_id,GError ** error)1735 grl_registry_unload_plugin (GrlRegistry *registry,
1736 const gchar *plugin_id,
1737 GError **error)
1738 {
1739 GrlPlugin *plugin;
1740 GList *sources = NULL;
1741 GList *sources_iter;
1742
1743 GRL_DEBUG ("%s: %s", __FUNCTION__, plugin_id);
1744
1745 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1746 g_return_val_if_fail (plugin_id != NULL, FALSE);
1747
1748 /* First check the plugin is valid */
1749 plugin = g_hash_table_lookup (registry->priv->plugins, plugin_id);
1750 if (!plugin) {
1751 GRL_WARNING ("Could not deinit plugin '%s'. Plugin not found.", plugin_id);
1752 g_set_error (error,
1753 GRL_CORE_ERROR,
1754 GRL_CORE_ERROR_UNLOAD_PLUGIN_FAILED,
1755 _("Plugin not found: “%s”"), plugin_id);
1756 return FALSE;
1757 }
1758
1759 /* Second, shut down any sources spawned by this plugin */
1760 GRL_DEBUG ("Shutting down sources spawned by '%s'", plugin_id);
1761 sources = grl_registry_get_sources (registry, FALSE);
1762
1763 for (sources_iter = sources; sources_iter;
1764 sources_iter = g_list_next (sources_iter)) {
1765 GrlSource *source = GRL_SOURCE (sources_iter->data);
1766 if (grl_source_get_plugin (source) == plugin) {
1767 grl_registry_unregister_source (registry, source, NULL);
1768 }
1769 }
1770 g_list_free (sources);
1771
1772 /* Third, shut down the plugin */
1773 shutdown_plugin (plugin);
1774
1775 return TRUE;
1776 }
1777
1778 /**
1779 * grl_registry_register_metadata_key:
1780 * @registry: The plugin registry
1781 * @param_spec: (transfer full): The definition of the key to register
1782 * @bind_key: The key the new key is bind to, or #GRL_METADATA_KEY_INVALID if it is not bound.
1783 * @error: error return location or @NULL to ignore
1784 *
1785 * Registers a new metadata key, creating a relation between the new key and
1786 * @bind_key.
1787 *
1788 * Two keys are related when the values of both keys are somehow related.
1789 *
1790 * One example of a relation would be the one between the URI of a media
1791 * resource and its mime-type: they are both tied together and one does not make
1792 * sense without the other.
1793 *
1794 * Relations between keys allow the framework to provide all the data that is
1795 * somehow related when any of the related keys are requested.
1796
1797 * Returns: The #GrlKeyID registered.
1798 *
1799 * Since: 0.3.0
1800 */
1801 GrlKeyID
grl_registry_register_metadata_key(GrlRegistry * registry,GParamSpec * param_spec,GrlKeyID bind_key,GError ** error)1802 grl_registry_register_metadata_key (GrlRegistry *registry,
1803 GParamSpec *param_spec,
1804 GrlKeyID bind_key,
1805 GError **error)
1806 {
1807 GrlKeyID key;
1808
1809 key = grl_registry_register_metadata_key_full (registry,
1810 param_spec,
1811 GRL_METADATA_KEY_INVALID,
1812 bind_key,
1813 error);
1814
1815 if (key != GRL_METADATA_KEY_INVALID) {
1816 g_signal_emit (registry, registry_signals[SIG_METADATA_KEY_ADDED],
1817 0,
1818 grl_metadata_key_get_name (key));
1819 }
1820
1821 return key;
1822 }
1823
1824 /*
1825 * grl_registry_register_metadata_key_system:
1826 *
1827 * This is an internal method only meant to be used to register core
1828 * keys.
1829 *
1830 * For internal use. Plugin developers should use
1831 * grl_registry_register_metadata_key().
1832 */
1833 GrlKeyID
grl_registry_register_metadata_key_system(GrlRegistry * registry,GParamSpec * param_spec,GrlKeyID key,GrlKeyID bind_key,GError ** error)1834 grl_registry_register_metadata_key_system (GrlRegistry *registry,
1835 GParamSpec *param_spec,
1836 GrlKeyID key,
1837 GrlKeyID bind_key,
1838 GError **error)
1839 {
1840 GrlKeyID registered_key;
1841
1842 registered_key = grl_registry_register_metadata_key_full (registry,
1843 param_spec,
1844 key,
1845 bind_key,
1846 error);
1847
1848 return registered_key;
1849 }
1850
1851 /**
1852 * grl_registry_lookup_metadata_key:
1853 * @registry: the registry instance
1854 * @key_name: the key name
1855 *
1856 * Look up for the metadata key with name @key_name.
1857 *
1858 * Returns: The metadata key, or GRL_METADATA_KEY_INVALID if not found
1859 *
1860 * Since: 0.2.0
1861 */
1862 GrlKeyID
grl_registry_lookup_metadata_key(GrlRegistry * registry,const gchar * key_name)1863 grl_registry_lookup_metadata_key (GrlRegistry *registry,
1864 const gchar *key_name)
1865 {
1866 g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
1867 g_return_val_if_fail (key_name, 0);
1868
1869 return key_id_handler_get_key (®istry->priv->key_id_handler, key_name);
1870 }
1871
1872 /**
1873 * grl_registry_lookup_metadata_key_name:
1874 * @registry: the registry instance
1875 * @key: a metadata key
1876 *
1877 * Returns @key name.
1878 *
1879 * Returns: metadata key name, or @NULL if not found
1880 *
1881 * Since: 0.2.0
1882 */
1883 const gchar *
grl_registry_lookup_metadata_key_name(GrlRegistry * registry,GrlKeyID key)1884 grl_registry_lookup_metadata_key_name (GrlRegistry *registry,
1885 GrlKeyID key)
1886 {
1887 g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
1888
1889 return key_id_handler_get_name (®istry->priv->key_id_handler, key);
1890 }
1891
1892 /**
1893 * grl_registry_lookup_metadata_key_desc:
1894 * @registry: the registry instance
1895 * @key: a metadata key
1896 *
1897 * Returns @key description.
1898 *
1899 * Returns: metadata key description, or @NULL if not found
1900 *
1901 * Since: 0.2.0
1902 */
1903 const gchar *
grl_registry_lookup_metadata_key_desc(GrlRegistry * registry,GrlKeyID key)1904 grl_registry_lookup_metadata_key_desc (GrlRegistry *registry,
1905 GrlKeyID key)
1906 {
1907 const gchar *key_name;
1908 GParamSpec *key_pspec;
1909
1910 g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
1911
1912 key_name = key_id_handler_get_name (®istry->priv->key_id_handler, key);
1913 if (!key_name) {
1914 return NULL;
1915 }
1916 key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
1917
1918 if (key_pspec) {
1919 return g_param_spec_get_blurb (key_pspec);
1920 } else {
1921 return NULL;
1922 }
1923 }
1924
1925 /**
1926 * grl_registry_lookup_metadata_key_type:
1927 * @registry: the registry instance
1928 * @key: a metadata key
1929 *
1930 * Returns @key expected value type.
1931 *
1932 * Returns: metadata key type, or @G_TYPE_INVALID if not found
1933 *
1934 * Since: 0.2.0
1935 */
1936 GType
grl_registry_lookup_metadata_key_type(GrlRegistry * registry,GrlKeyID key)1937 grl_registry_lookup_metadata_key_type (GrlRegistry *registry,
1938 GrlKeyID key)
1939 {
1940 const gchar *key_name;
1941 GParamSpec *key_pspec;
1942
1943 g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
1944
1945 key_name = key_id_handler_get_name (®istry->priv->key_id_handler, key);
1946 if (!key_name) {
1947 return G_TYPE_INVALID;
1948 }
1949 key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
1950
1951 if (key_pspec) {
1952 return G_PARAM_SPEC_VALUE_TYPE (key_pspec);
1953 } else {
1954 return G_TYPE_INVALID;
1955 }
1956 }
1957
1958 /**
1959 * grl_registry_metadata_key_validate:
1960 * @registry: the registry instance
1961 * @key: a metadata key
1962 * @value: value to be validate
1963 *
1964 * Validates @value content complies with the key specification. That is, it has
1965 * the expected type, and value are within the range specified in key (for
1966 * integer values).
1967 *
1968 * Returns: %TRUE if complies
1969 *
1970 * Since: 0.2.0
1971 **/
1972 gboolean
grl_registry_metadata_key_validate(GrlRegistry * registry,GrlKeyID key,GValue * value)1973 grl_registry_metadata_key_validate (GrlRegistry *registry,
1974 GrlKeyID key,
1975 GValue *value)
1976 {
1977 const gchar *key_name;
1978 GParamSpec *key_pspec;
1979
1980 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
1981 g_return_val_if_fail (G_IS_VALUE (value), FALSE);
1982
1983 key_name = key_id_handler_get_name (®istry->priv->key_id_handler, key);
1984 if (!key_name) {
1985 return FALSE;
1986 }
1987 key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
1988
1989 if (key_pspec) {
1990 return !g_param_value_validate (key_pspec, value);
1991 } else {
1992 return FALSE;
1993 }
1994 }
1995
1996 /**
1997 * grl_registry_lookup_metadata_key_relation:
1998 * @registry: the registry instance
1999 * @key: a metadata key
2000 *
2001 * Look up the list of keys that have a relation with @key.
2002 *
2003 * @key is included in that list.
2004 *
2005 * Returns: (element-type GrlKeyID) (transfer none): a #GList of
2006 * related keys, or @NULL if key is invalid.
2007 *
2008 * Since: 0.2.0
2009 **/
2010 const GList *
grl_registry_lookup_metadata_key_relation(GrlRegistry * registry,GrlKeyID key)2011 grl_registry_lookup_metadata_key_relation (GrlRegistry *registry,
2012 GrlKeyID key)
2013 {
2014 g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
2015
2016 return g_hash_table_lookup (registry->priv->related_keys, GRLKEYID_TO_POINTER (key));
2017 }
2018
2019 /**
2020 * grl_registry_get_metadata_keys:
2021 * @registry: the registry instance
2022 *
2023 * Returns a list with all registered keys in system.
2024 *
2025 * Returns: (transfer container) (element-type GrlKeyID): a #GList with all the available
2026 * #GrlKeyID<!-- -->s. The content of the list should not be modified or freed.
2027 * Use g_list_free() when done using the list.
2028 *
2029 * Since: 0.2.0
2030 **/
2031 GList *
grl_registry_get_metadata_keys(GrlRegistry * registry)2032 grl_registry_get_metadata_keys (GrlRegistry *registry)
2033 {
2034 return key_id_handler_get_all_keys (®istry->priv->key_id_handler);
2035 }
2036
2037 /**
2038 * grl_registry_add_config:
2039 * @registry: the registry instance
2040 * @config: (transfer full): a configuration set
2041 * @error: error return location or @NULL to ignore
2042 *
2043 * Add a configuration for a plugin/source.
2044 *
2045 * Returns: %TRUE on success
2046 *
2047 * Since: 0.2.0
2048 */
2049 gboolean
grl_registry_add_config(GrlRegistry * registry,GrlConfig * config,GError ** error)2050 grl_registry_add_config (GrlRegistry *registry,
2051 GrlConfig *config,
2052 GError **error)
2053 {
2054 gchar *plugin_id;
2055 GList *configs = NULL;
2056
2057 g_return_val_if_fail (config != NULL, FALSE);
2058 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
2059
2060 plugin_id = grl_config_get_plugin (config);
2061 if (!plugin_id) {
2062 GRL_WARNING ("Plugin configuration missed plugin information, ignoring...");
2063 g_set_error (error,
2064 GRL_CORE_ERROR,
2065 GRL_CORE_ERROR_CONFIG_FAILED,
2066 _("Plugin configuration does not contain “plugin-id” reference"));
2067 return FALSE;
2068 }
2069
2070 configs = g_hash_table_lookup (registry->priv->configs, plugin_id);
2071 if (configs) {
2072 /* Notice that we are using g_list_append on purpose to avoid
2073 having to insert again in the hash table */
2074 configs = g_list_append (configs, config);
2075 g_free (plugin_id);
2076 } else {
2077 configs = g_list_prepend (configs, config);
2078 g_hash_table_insert (registry->priv->configs,
2079 (gpointer) plugin_id,
2080 configs);
2081 }
2082
2083 return TRUE;
2084 }
2085
2086 static void
get_plugin_and_source_from_group_name(const gchar * group_name,gchar ** plugin_name,gchar ** source_name)2087 get_plugin_and_source_from_group_name (const gchar *group_name,
2088 gchar **plugin_name,
2089 gchar **source_name)
2090 {
2091 gchar *plugin = g_strdup (group_name);
2092 gchar **arr;
2093
2094 *plugin_name = *source_name = NULL;
2095
2096 plugin = g_strstrip (plugin);
2097 arr = g_strsplit (plugin, " ", 2);
2098 g_free (plugin);
2099
2100 *plugin_name = g_strstrip (arr[0]);
2101
2102 if (arr[1] != NULL)
2103 *source_name = g_strstrip (arr[1]);
2104
2105 g_free (arr);
2106 }
2107
2108 static void
add_config_from_keyfile(GKeyFile * keyfile,GrlRegistry * registry)2109 add_config_from_keyfile (GKeyFile *keyfile,
2110 GrlRegistry *registry)
2111 {
2112 GrlConfig *config;
2113 gchar **key;
2114 gchar **keys;
2115 gchar **groupname;
2116 gchar **plugins;
2117 gchar *value;
2118
2119 /* Look up for defined plugins */
2120 plugins = g_key_file_get_groups (keyfile, NULL);
2121 for (groupname = plugins; *groupname; groupname++) {
2122 gchar *plugin_name, *source_name;
2123
2124 get_plugin_and_source_from_group_name (*groupname, &plugin_name, &source_name);
2125
2126 config = grl_config_new (plugin_name, source_name);
2127
2128 /* Look up configuration keys for this plugin */
2129 keys = g_key_file_get_keys (keyfile, *groupname, NULL, NULL);
2130 for (key = keys; *key; key++) {
2131 value = g_key_file_get_string (keyfile, *groupname, *key, NULL);
2132 if (value) {
2133 GRL_DEBUG ("Config found: %s : %s : %s", plugin_name,
2134 source_name != NULL ? source_name : plugin_name,
2135 *key);
2136 grl_config_set_string (config, *key, value);
2137 g_free (value);
2138 }
2139 }
2140 grl_registry_add_config (registry, config, NULL);
2141 g_strfreev (keys);
2142 g_free (source_name);
2143 g_free (plugin_name);
2144 }
2145 g_strfreev (plugins);
2146 }
2147
2148 /**
2149 * grl_registry_add_config_from_file:
2150 * @registry: the registry instance
2151 * @config_file: a key-value file containing the configuration
2152 * @error: error return location or @NULL to ignore
2153 *
2154 * Load plugin configurations from a .ini-like config file.
2155 *
2156 * Returns: %TRUE on success
2157 *
2158 * Since: 0.2.0
2159 **/
2160 gboolean
grl_registry_add_config_from_file(GrlRegistry * registry,const gchar * config_file,GError ** error)2161 grl_registry_add_config_from_file (GrlRegistry *registry,
2162 const gchar *config_file,
2163 GError **error)
2164 {
2165 GError *load_error = NULL;
2166 GKeyFile *keyfile;
2167
2168 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
2169 g_return_val_if_fail (config_file, FALSE);
2170
2171 keyfile = g_key_file_new ();
2172
2173 if (g_key_file_load_from_file (keyfile,
2174 config_file,
2175 G_KEY_FILE_NONE,
2176 &load_error)) {
2177 add_config_from_keyfile (keyfile, registry);
2178 g_key_file_free (keyfile);
2179 return TRUE;
2180 } else {
2181 GRL_WARNING ("Unable to load configuration. %s", load_error->message);
2182 g_set_error_literal (error,
2183 GRL_CORE_ERROR,
2184 GRL_CORE_ERROR_CONFIG_LOAD_FAILED,
2185 load_error->message);
2186 g_error_free (load_error);
2187 g_key_file_free (keyfile);
2188 return FALSE;
2189 }
2190 }
2191
2192 /**
2193 * grl_registry_add_config_from_resource:
2194 * @registry: the registry instance
2195 * @resource_path: a key-value file containing the configuration
2196 * @error: error return location or @NULL to ignore
2197 *
2198 * Load plugin configurations from a .ini-like resource file.
2199 *
2200 * Returns: %TRUE on success
2201 *
2202 * Since: 0.2.8
2203 **/
2204 gboolean
grl_registry_add_config_from_resource(GrlRegistry * registry,const gchar * resource_path,GError ** error)2205 grl_registry_add_config_from_resource (GrlRegistry *registry,
2206 const gchar *resource_path,
2207 GError **error)
2208 {
2209 GError *load_error = NULL;
2210 GKeyFile *keyfile = NULL;
2211 GBytes *bytes;
2212 gboolean ret = FALSE;
2213
2214 g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
2215 g_return_val_if_fail (resource_path, FALSE);
2216
2217 bytes = g_resources_lookup_data (resource_path, 0, error);
2218 if (bytes == NULL)
2219 goto bail;
2220
2221 keyfile = g_key_file_new ();
2222
2223 if (g_key_file_load_from_data (keyfile,
2224 g_bytes_get_data (bytes, NULL),
2225 g_bytes_get_size (bytes),
2226 G_KEY_FILE_NONE,
2227 &load_error)) {
2228 add_config_from_keyfile (keyfile, registry);
2229 ret = TRUE;
2230 } else {
2231 GRL_WARNING ("Unable to load configuration. %s", load_error->message);
2232 g_set_error_literal (error,
2233 GRL_CORE_ERROR,
2234 GRL_CORE_ERROR_CONFIG_LOAD_FAILED,
2235 load_error->message);
2236 g_error_free (load_error);
2237 }
2238
2239 bail:
2240 g_clear_pointer (&keyfile, g_key_file_free);
2241 g_clear_pointer (&bytes, g_bytes_unref);
2242
2243 return ret;
2244 }
2245
2246
2247 G_GNUC_INTERNAL gboolean
grl_registry_metadata_key_get_limits(GrlRegistry * registry,GrlKeyID key,GValue * min,GValue * max)2248 grl_registry_metadata_key_get_limits(GrlRegistry *registry,
2249 GrlKeyID key,
2250 GValue *min,
2251 GValue *max)
2252 {
2253 GParamSpec *key_pspec;
2254 const gchar *key_name;
2255 GType key_type;
2256
2257 key_name = key_id_handler_get_name (®istry->priv->key_id_handler, key);
2258 if (!key_name) {
2259 return FALSE;
2260 }
2261
2262 key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
2263 if (!key_pspec) {
2264 return FALSE;
2265 }
2266
2267 key_type = G_PARAM_SPEC_VALUE_TYPE (key_pspec);
2268
2269 #define CHECK_NUMERIC_AND_SET_VALUE_LIMITS(value_type, numeric_type, getter, cast_type) { \
2270 if (value_type == numeric_type) { \
2271 g_value_init (min, numeric_type); \
2272 g_value_init (max, numeric_type); \
2273 g_value_set_##getter (min, (cast_type (key_pspec))->minimum); \
2274 g_value_set_##getter (max, (cast_type (key_pspec))->maximum); \
2275 return TRUE; \
2276 } \
2277 }
2278
2279 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_INT, int, G_PARAM_SPEC_INT);
2280 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_LONG, long, G_PARAM_SPEC_LONG);
2281 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_INT64, int64, G_PARAM_SPEC_INT64);
2282 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_CHAR, schar, G_PARAM_SPEC_CHAR);
2283 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_UINT, uint, G_PARAM_SPEC_UINT);
2284 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_ULONG, ulong, G_PARAM_SPEC_ULONG);
2285 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_UINT64, uint64, G_PARAM_SPEC_UINT64);
2286 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_UCHAR, uchar, G_PARAM_SPEC_UCHAR);
2287 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_FLOAT, float, G_PARAM_SPEC_FLOAT);
2288 CHECK_NUMERIC_AND_SET_VALUE_LIMITS (key_type, G_TYPE_DOUBLE, double, G_PARAM_SPEC_DOUBLE);
2289 return FALSE;
2290 }
2291
2292 G_GNUC_INTERNAL gboolean
grl_registry_metadata_key_clamp(GrlRegistry * registry,GrlKeyID key,GValue * min,GValue * value,GValue * max)2293 grl_registry_metadata_key_clamp(GrlRegistry *registry,
2294 GrlKeyID key,
2295 GValue *min,
2296 GValue *value,
2297 GValue *max)
2298 {
2299 const gchar *key_name;
2300
2301 key_name = key_id_handler_get_name (®istry->priv->key_id_handler, key);
2302 if (key_name) {
2303 GParamSpec *key_pspec;
2304
2305 key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
2306 if (key_pspec) {
2307 if (g_param_values_cmp(key_pspec, value, min) < 0) {
2308 GRL_DEBUG("reset value to min");
2309 g_value_transform(min, value);
2310 return TRUE;
2311 } else if (g_param_values_cmp(key_pspec, value, max) > 0) {
2312 GRL_DEBUG("reset value to max");
2313 g_value_transform(max, value);
2314 return TRUE;
2315 }
2316 }
2317 }
2318 return FALSE;
2319 }
2320