Blame src/grl-registry.c

Packit 67b98c
/*
Packit 67b98c
 * Copyright (C) 2010, 2011 Igalia S.L.
Packit 67b98c
 * Copyright (C) 2011 Intel Corporation.
Packit 67b98c
 *
Packit 67b98c
 * Contact: Iago Toral Quiroga <itoral@igalia.com>
Packit 67b98c
 *
Packit 67b98c
 * This library is free software; you can redistribute it and/or
Packit 67b98c
 * modify it under the terms of the GNU Lesser General Public License
Packit 67b98c
 * as published by the Free Software Foundation; version 2.1 of
Packit 67b98c
 * the License, or (at your option) any later version.
Packit 67b98c
 *
Packit 67b98c
 * This library is distributed in the hope that it will be useful, but
Packit 67b98c
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 67b98c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit 67b98c
 * Lesser General Public License for more details.
Packit 67b98c
 *
Packit 67b98c
 * You should have received a copy of the GNU Lesser General Public
Packit 67b98c
 * License along with this library; if not, write to the Free Software
Packit 67b98c
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
Packit 67b98c
 * 02110-1301 USA
Packit 67b98c
 *
Packit 67b98c
 */
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * SECTION:grl-registry
Packit 67b98c
 * @short_description: Grilo plugins loader and manager
Packit 67b98c
 * @see_also: #GrlPlugin, #GrlSource
Packit 67b98c
 *
Packit 67b98c
 * The registry holds the metadata of a set of plugins.
Packit 67b98c
 *
Packit 67b98c
 * The #GrlRegistry object is a list of plugins and some functions
Packit 67b98c
 * for dealing with them. Each #GrlPlugin is matched 1-1 with a file
Packit 67b98c
 * on disk, and may or may not be loaded a given time. There only can be
Packit 67b98c
 * a single instance of #GrlRegistry (singleton pattern).
Packit 67b98c
 *
Packit 67b98c
 * A #GrlPlugin can hold several data #GrlSource sources, and #GrlRegistry
Packit 67b98c
 * shall register each one of them.
Packit 67b98c
 */
Packit 67b98c
Packit 67b98c
#include "grl-registry.h"
Packit 67b98c
Packit 67b98c
#ifdef HAVE_CONFIG_H
Packit 67b98c
#include "config.h"
Packit 67b98c
#endif
Packit 67b98c
Packit 67b98c
#include "grl-registry-priv.h"
Packit 67b98c
#include "grl-plugin-priv.h"
Packit 67b98c
#include "grl-log.h"
Packit 67b98c
#include "grl-error.h"
Packit 67b98c
Packit 67b98c
#include <glib/gi18n-lib.h>
Packit 67b98c
#include <string.h>
Packit 67b98c
#include <gmodule.h>
Packit 67b98c
#include <libxml/parser.h>
Packit 67b98c
Packit 67b98c
#define GRL_LOG_DOMAIN_DEFAULT  registry_log_domain
Packit 67b98c
GRL_LOG_DOMAIN(registry_log_domain);
Packit 67b98c
Packit 67b98c
#define XML_ROOT_ELEMENT_NAME "plugin"
Packit 67b98c
Packit 67b98c
#define GRL_PLUGIN_INFO_SUFFIX "xml"
Packit 67b98c
Packit 67b98c
#define GRL_PLUGIN_INFO_MODULE "module"
Packit 67b98c
Packit 67b98c
#define LOCAL_NET_TAG      "net:local"
Packit 67b98c
#define INTERNET_NET_TAG   "net:internet"
Packit 67b98c
Packit 67b98c
#define SET_INVISIBLE_SOURCE(src, val)                          \
Packit 67b98c
  g_object_set_data(G_OBJECT(src), "invisible", GINT_TO_POINTER(val))
Packit 67b98c
#define SOURCE_IS_INVISIBLE(src)                                \
Packit 67b98c
  GPOINTER_TO_INT(g_object_get_data(G_OBJECT(src), "invisible"))
Packit 67b98c
Packit 67b98c
/* GQuark-like implementation, where we manually assign the first IDs. */
Packit 67b98c
struct KeyIDHandler {
Packit 67b98c
  GHashTable *string_to_id;
Packit 67b98c
  GArray *id_to_string;
Packit 67b98c
  gint last_id;
Packit 67b98c
};
Packit 67b98c
Packit 67b98c
struct _GrlRegistryPrivate {
Packit 67b98c
  GHashTable *configs;
Packit 67b98c
  GHashTable *plugins;
Packit 67b98c
  GHashTable *sources;
Packit 67b98c
  GHashTable *related_keys;
Packit 67b98c
  GHashTable *system_keys;
Packit 67b98c
  GHashTable *ranks;
Packit 67b98c
  GSList *plugins_dir;
Packit 67b98c
  GSList *allowed_plugins;
Packit 67b98c
  gboolean all_plugins_preloaded;
Packit 67b98c
  struct KeyIDHandler key_id_handler;
Packit 67b98c
  GNetworkMonitor *netmon;
Packit 67b98c
};
Packit 67b98c
Packit 67b98c
static void grl_registry_setup_ranks (GrlRegistry *registry);
Packit 67b98c
Packit 67b98c
static void key_id_handler_init (struct KeyIDHandler *handler);
Packit 67b98c
Packit 67b98c
static void key_id_handler_free (struct KeyIDHandler *handler);
Packit 67b98c
Packit 67b98c
static GrlKeyID key_id_handler_get_key (struct KeyIDHandler *handler,
Packit 67b98c
                                        const gchar *key_name);
Packit 67b98c
Packit 67b98c
static const gchar *key_id_handler_get_name (struct KeyIDHandler *handler,
Packit 67b98c
                                             GrlKeyID key);
Packit 67b98c
Packit 67b98c
static GrlKeyID key_id_handler_add (struct KeyIDHandler *handler,
Packit 67b98c
                                    GrlKeyID key, const gchar *key_name);
Packit 67b98c
Packit 67b98c
static gboolean param_spec_is_equal (GParamSpec *curr, GParamSpec *new);
Packit 67b98c
Packit 67b98c
static void shutdown_plugin (GrlPlugin *plugin);
Packit 67b98c
Packit 67b98c
static void configs_free (GList *configs);
Packit 67b98c
Packit 67b98c
static GrlPlugin *grl_registry_prepare_plugin (GrlRegistry *registry,
Packit 67b98c
                                               const gchar *library_filename,
Packit 67b98c
                                               GError **error);
Packit 67b98c
Packit 67b98c
/* ================ GrlRegistry GObject ================ */
Packit 67b98c
Packit 67b98c
enum {
Packit 67b98c
  SIG_SOURCE_ADDED,
Packit 67b98c
  SIG_SOURCE_REMOVED,
Packit 67b98c
  SIG_METADATA_KEY_ADDED,
Packit 67b98c
  SIG_LAST
Packit 67b98c
};
Packit 67b98c
static gint registry_signals[SIG_LAST];
Packit 67b98c
Packit 67b98c
G_DEFINE_TYPE_WITH_PRIVATE (GrlRegistry, grl_registry, G_TYPE_OBJECT);
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
grl_registry_class_init (GrlRegistryClass *klass)
Packit 67b98c
{
Packit 67b98c
  /**
Packit 67b98c
   * GrlRegistry::source-added:
Packit 67b98c
   * @registry: the registry
Packit 67b98c
   * @source: the source that has been added
Packit 67b98c
   *
Packit 67b98c
   * Signals that a source has been added to the registry.
Packit 67b98c
   *
Packit 67b98c
   * Since: 0.2.0
Packit 67b98c
   */
Packit 67b98c
  registry_signals[SIG_SOURCE_ADDED] =
Packit 67b98c
    g_signal_new("source-added",
Packit 67b98c
		 G_TYPE_FROM_CLASS(klass),
Packit 67b98c
		 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
Packit 67b98c
		 0,
Packit 67b98c
		 NULL,
Packit 67b98c
		 NULL,
Packit 67b98c
		 g_cclosure_marshal_VOID__OBJECT,
Packit 67b98c
		 G_TYPE_NONE, 1, GRL_TYPE_SOURCE);
Packit 67b98c
Packit 67b98c
  /**
Packit 67b98c
   * GrlRegistry::source-removed:
Packit 67b98c
   * @registry: the registry
Packit 67b98c
   * @source: the source that has been removed
Packit 67b98c
   *
Packit 67b98c
   * Signals that a source has been removed from the registry.
Packit 67b98c
   *
Packit 67b98c
   * Since: 0.2.0
Packit 67b98c
   */
Packit 67b98c
  registry_signals[SIG_SOURCE_REMOVED] =
Packit 67b98c
    g_signal_new("source-removed",
Packit 67b98c
		 G_TYPE_FROM_CLASS(klass),
Packit 67b98c
		 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
Packit 67b98c
		 0,
Packit 67b98c
		 NULL,
Packit 67b98c
		 NULL,
Packit 67b98c
		 g_cclosure_marshal_VOID__OBJECT,
Packit 67b98c
		 G_TYPE_NONE, 1, GRL_TYPE_SOURCE);
Packit 67b98c
Packit 67b98c
  /**
Packit 67b98c
   * GrlRegistry::metadata-key-added:
Packit 67b98c
   * @registry: the registry
Packit 67b98c
   * @key: the name of the new key added
Packit 67b98c
   *
Packit 67b98c
   * Signals that a new metadata key has been registered.
Packit 67b98c
   *
Packit 67b98c
   * Since: 0.2.10
Packit 67b98c
   */
Packit 67b98c
  registry_signals[SIG_METADATA_KEY_ADDED] =
Packit 67b98c
    g_signal_new("metadata-key-added",
Packit 67b98c
                 G_TYPE_FROM_CLASS(klass),
Packit 67b98c
                 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
Packit 67b98c
                 0,
Packit 67b98c
                 NULL,
Packit 67b98c
                 NULL,
Packit 67b98c
                 g_cclosure_marshal_VOID__STRING,
Packit 67b98c
                 G_TYPE_NONE, 1, G_TYPE_STRING);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
get_connectivity (GrlRegistry          *registry,
Packit 67b98c
                  GNetworkConnectivity *connectivity,
Packit 67b98c
                  gboolean             *network_available)
Packit 67b98c
{
Packit 67b98c
  g_assert (connectivity != NULL);
Packit 67b98c
  g_assert (network_available != NULL);
Packit 67b98c
Packit 67b98c
  if (g_getenv("GRL_NET_MOCKED") != NULL) {
Packit 67b98c
    GRL_DEBUG ("Mocked network, assuming network is available and connectivity "
Packit 67b98c
               "level is FULL");
Packit 67b98c
    *connectivity = G_NETWORK_CONNECTIVITY_FULL;
Packit 67b98c
    *network_available = TRUE;
Packit 67b98c
  } else {
Packit 67b98c
    g_object_get (G_OBJECT (registry->priv->netmon),
Packit 67b98c
                  "connectivity", connectivity,
Packit 67b98c
                  "network-available", network_available,
Packit 67b98c
                  NULL);
Packit 67b98c
Packit 67b98c
    GRL_DEBUG ("Connectivity level is %d, Network is %s",
Packit 67b98c
               *connectivity, *network_available ? "available" : "unavailable");
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
network_changed_cb (GObject     *gobject,
Packit 67b98c
                    GParamSpec  *pspec,
Packit 67b98c
                    GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  GNetworkConnectivity connectivity;
Packit 67b98c
  gboolean network_available;
Packit 67b98c
  GHashTableIter iter;
Packit 67b98c
  GrlSource *current_source;
Packit 67b98c
Packit 67b98c
  GRL_DEBUG ("Network availability changed");
Packit 67b98c
  get_connectivity (registry, &connectivity, &network_available);
Packit 67b98c
Packit 67b98c
  if (!network_available) {
Packit 67b98c
    g_hash_table_iter_init (&iter, registry->priv->sources);
Packit 67b98c
Packit 67b98c
    while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &current_source)) {
Packit 67b98c
      const char **tags = grl_source_get_tags (current_source);
Packit 67b98c
Packit 67b98c
      if (!tags)
Packit 67b98c
        continue;
Packit 67b98c
Packit 67b98c
      if ((g_strv_contains (tags, LOCAL_NET_TAG) ||
Packit 67b98c
           g_strv_contains (tags, INTERNET_NET_TAG)) &&
Packit 67b98c
          !SOURCE_IS_INVISIBLE(current_source)) {
Packit 67b98c
        GRL_DEBUG ("Network isn't available for '%s', hiding",
Packit 67b98c
                   grl_source_get_id (current_source));
Packit 67b98c
        SET_INVISIBLE_SOURCE(current_source, TRUE);
Packit 67b98c
        g_signal_emit (registry, registry_signals[SIG_SOURCE_REMOVED], 0, current_source);
Packit 67b98c
      }
Packit 67b98c
    }
Packit 67b98c
  } else {
Packit 67b98c
    g_hash_table_iter_init (&iter, registry->priv->sources);
Packit 67b98c
    while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &current_source)) {
Packit 67b98c
      const char **tags = grl_source_get_tags (current_source);
Packit 67b98c
Packit 67b98c
      if (!tags)
Packit 67b98c
        continue;
Packit 67b98c
Packit 67b98c
      if (g_strv_contains (tags, LOCAL_NET_TAG) &&
Packit 67b98c
          SOURCE_IS_INVISIBLE(current_source)) {
Packit 67b98c
        GRL_DEBUG ("Local network became available for '%s', showing",
Packit 67b98c
                   grl_source_get_id (current_source));
Packit 67b98c
        SET_INVISIBLE_SOURCE(current_source, FALSE);
Packit 67b98c
        g_signal_emit (registry, registry_signals[SIG_SOURCE_ADDED], 0, current_source);
Packit 67b98c
      }
Packit 67b98c
Packit 67b98c
      if (g_strv_contains (tags, INTERNET_NET_TAG) &&
Packit 67b98c
          connectivity == G_NETWORK_CONNECTIVITY_FULL &&
Packit 67b98c
          SOURCE_IS_INVISIBLE(current_source)) {
Packit 67b98c
        GRL_DEBUG ("Internet became available for '%s', showing",
Packit 67b98c
                   grl_source_get_id (current_source));
Packit 67b98c
        SET_INVISIBLE_SOURCE(current_source, FALSE);
Packit 67b98c
        g_signal_emit (registry, registry_signals[SIG_SOURCE_ADDED], 0, current_source);
Packit 67b98c
      }
Packit 67b98c
Packit 67b98c
      if (g_strv_contains (tags, INTERNET_NET_TAG) &&
Packit 67b98c
          connectivity != G_NETWORK_CONNECTIVITY_FULL &&
Packit 67b98c
          !SOURCE_IS_INVISIBLE(current_source)) {
Packit 67b98c
        GRL_DEBUG ("Internet became unavailable for '%s', hiding",
Packit 67b98c
                   grl_source_get_id (current_source));
Packit 67b98c
        SET_INVISIBLE_SOURCE(current_source, TRUE);
Packit 67b98c
        g_signal_emit (registry, registry_signals[SIG_SOURCE_REMOVED], 0, current_source);
Packit 67b98c
      }
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
grl_registry_init (GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  registry->priv = grl_registry_get_instance_private (registry);
Packit 67b98c
Packit 67b98c
  registry->priv->configs =
Packit 67b98c
    g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) configs_free);
Packit 67b98c
  registry->priv->plugins =
Packit 67b98c
    g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
Packit 67b98c
  registry->priv->sources =
Packit 67b98c
    g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
Packit 67b98c
  registry->priv->related_keys =
Packit 67b98c
    g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
Packit 67b98c
  registry->priv->system_keys =
Packit 67b98c
    g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_param_spec_unref);
Packit 67b98c
Packit 67b98c
  registry->priv->netmon = g_network_monitor_get_default ();
Packit 67b98c
  g_signal_connect (G_OBJECT (registry->priv->netmon), "notify::connectivity",
Packit 67b98c
                    G_CALLBACK (network_changed_cb), registry);
Packit 67b98c
  g_signal_connect (G_OBJECT (registry->priv->netmon), "notify::network-available",
Packit 67b98c
                    G_CALLBACK (network_changed_cb), registry);
Packit 67b98c
Packit 67b98c
  key_id_handler_init (&registry->priv->key_id_handler);
Packit 67b98c
Packit 67b98c
  grl_registry_setup_ranks (registry);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/* ================ Utitilies ================ */
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
configs_free (GList *configs)
Packit 67b98c
{
Packit 67b98c
  g_list_free_full (configs, g_object_unref);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
update_source_visibility (GrlRegistry *registry,
Packit 67b98c
                          GrlSource   *source)
Packit 67b98c
{
Packit 67b98c
  GNetworkConnectivity connectivity;
Packit 67b98c
  gboolean network_available;
Packit 67b98c
  const char **tags;
Packit 67b98c
  gboolean needs_local, needs_inet;
Packit 67b98c
Packit 67b98c
  tags = grl_source_get_tags (source);
Packit 67b98c
  if (!tags)
Packit 67b98c
    return;
Packit 67b98c
Packit 67b98c
  needs_local = g_strv_contains (tags, LOCAL_NET_TAG);
Packit 67b98c
  needs_inet = g_strv_contains (tags, INTERNET_NET_TAG);
Packit 67b98c
Packit 67b98c
  if (!needs_local &&
Packit 67b98c
      !needs_inet)
Packit 67b98c
    return;
Packit 67b98c
Packit 67b98c
  get_connectivity (registry, &connectivity, &network_available);
Packit 67b98c
Packit 67b98c
  GRL_DEBUG ("Source %s needs %s %s%s",
Packit 67b98c
             grl_source_get_id (source),
Packit 67b98c
             needs_local ? "local network" : "",
Packit 67b98c
             needs_inet && needs_local ? " and " : "",
Packit 67b98c
             needs_inet ? "Internet" : "");
Packit 67b98c
Packit 67b98c
  if (!network_available) {
Packit 67b98c
    if (needs_local || needs_inet) {
Packit 67b98c
      GRL_DEBUG ("Network isn't available for '%s', hiding",
Packit 67b98c
                 grl_source_get_id (source));
Packit 67b98c
      SET_INVISIBLE_SOURCE(source, TRUE);
Packit 67b98c
    }
Packit 67b98c
  } else {
Packit 67b98c
    if (connectivity != G_NETWORK_CONNECTIVITY_FULL) {
Packit 67b98c
      if (needs_inet) {
Packit 67b98c
        GRL_DEBUG ("Internet isn't available for '%s', hiding",
Packit 67b98c
                   grl_source_get_id (source));
Packit 67b98c
        SET_INVISIBLE_SOURCE(source, TRUE);
Packit 67b98c
      }
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
config_source_rank (GrlRegistry *registry,
Packit 67b98c
                    const gchar *source_id,
Packit 67b98c
                    gint rank)
Packit 67b98c
{
Packit 67b98c
  GRL_DEBUG ("Rank configuration, '%s:%d'", source_id, rank);
Packit 67b98c
  g_hash_table_insert (registry->priv->ranks,
Packit 67b98c
                       g_strdup (source_id),
Packit 67b98c
                       GINT_TO_POINTER (rank));
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
set_source_rank (GrlRegistry *registry, GrlSource *source)
Packit 67b98c
{
Packit 67b98c
  gint rank;
Packit 67b98c
Packit 67b98c
  rank =
Packit 67b98c
    GPOINTER_TO_INT (g_hash_table_lookup (registry->priv->ranks,
Packit 67b98c
                                          grl_source_get_id (source)));
Packit 67b98c
  if (!rank) {
Packit 67b98c
    rank = GRL_RANK_DEFAULT;
Packit 67b98c
  }
Packit 67b98c
  g_object_set (source, "rank", rank, NULL);
Packit 67b98c
  GRL_DEBUG ("Source rank '%s' : %d", grl_source_get_id (source), rank);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
grl_registry_setup_ranks (GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  const gchar *ranks_env;
Packit 67b98c
  gchar **rank_specs;
Packit 67b98c
  gchar **iter;
Packit 67b98c
Packit 67b98c
  registry->priv->ranks = g_hash_table_new_full (g_str_hash, g_str_equal,
Packit 67b98c
						 g_free, NULL);
Packit 67b98c
Packit 67b98c
  ranks_env = g_getenv (GRL_PLUGIN_RANKS_VAR);
Packit 67b98c
  if (!ranks_env) {
Packit 67b98c
    GRL_DEBUG ("$%s is not set, using default ranks.", GRL_PLUGIN_RANKS_VAR);
Packit 67b98c
    return;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  rank_specs = g_strsplit (ranks_env, ",", 0);
Packit 67b98c
  iter = rank_specs;
Packit 67b98c
Packit 67b98c
  while (*iter) {
Packit 67b98c
    gchar **rank_info = g_strsplit (*iter, ":", 2);
Packit 67b98c
    if (rank_info[0] && rank_info[1]) {
Packit 67b98c
      gchar *tmp;
Packit 67b98c
      gchar *id = rank_info[0];
Packit 67b98c
      gchar *srank = rank_info[1];
Packit 67b98c
      gint rank = (gint) g_ascii_strtoll (srank, &tmp, 10);
Packit 67b98c
      if (*tmp != '\0') {
Packit 67b98c
        GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
Packit 67b98c
      } else {
Packit 67b98c
        config_source_rank (registry, id, rank);
Packit 67b98c
      }
Packit 67b98c
    } else {
Packit 67b98c
      GRL_WARNING ("Incorrect ranking definition: '%s'. Skipping...", *iter);
Packit 67b98c
    }
Packit 67b98c
    g_strfreev (rank_info);
Packit 67b98c
    iter++;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  g_strfreev (rank_specs);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static gint
Packit 67b98c
compare_by_rank (gconstpointer a,
Packit 67b98c
                 gconstpointer b) {
Packit 67b98c
  gint rank_a;
Packit 67b98c
  gint rank_b;
Packit 67b98c
Packit 67b98c
  rank_a = grl_source_get_rank (GRL_SOURCE (a));
Packit 67b98c
  rank_b = grl_source_get_rank (GRL_SOURCE (b));
Packit 67b98c
Packit 67b98c
  return (rank_a < rank_b) - (rank_a > rank_b);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static gboolean
Packit 67b98c
register_keys_plugin (GrlRegistry *registry,
Packit 67b98c
                      GrlPlugin *plugin,
Packit 67b98c
                      GError **error)
Packit 67b98c
{
Packit 67b98c
  gboolean is_loaded;
Packit 67b98c
Packit 67b98c
  /* Check if plugin is already loaded */
Packit 67b98c
  g_object_get (plugin, "loaded", &is_loaded, NULL);
Packit 67b98c
  if (is_loaded) {
Packit 67b98c
    GRL_WARNING ("Plugin is already loaded: '%s'", grl_plugin_get_id (plugin));
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Plugin “%s” is already loaded"), grl_plugin_get_id (plugin));
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  grl_plugin_register_keys (plugin);
Packit 67b98c
Packit 67b98c
  return TRUE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static gboolean
Packit 67b98c
activate_plugin (GrlRegistry *registry,
Packit 67b98c
                 GrlPlugin *plugin,
Packit 67b98c
                 GError **error)
Packit 67b98c
{
Packit 67b98c
  GList *plugin_configs;
Packit 67b98c
Packit 67b98c
  plugin_configs = g_hash_table_lookup (registry->priv->configs,
Packit 67b98c
                                        grl_plugin_get_id (plugin));
Packit 67b98c
Packit 67b98c
  if (!grl_plugin_load (plugin, plugin_configs)) {
Packit 67b98c
    GRL_DEBUG ("Failed to initialize plugin from %s. Check if plugin is well configured", grl_plugin_get_filename (plugin));
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Failed to initialize plugin from %s"), grl_plugin_get_filename (plugin));
Packit 67b98c
    shutdown_plugin (plugin);
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  GRL_DEBUG ("Loaded plugin '%s' from '%s'",
Packit 67b98c
             grl_plugin_get_id (plugin),
Packit 67b98c
             grl_plugin_get_filename (plugin));
Packit 67b98c
Packit 67b98c
  return TRUE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static GrlKeyID
Packit 67b98c
grl_registry_register_metadata_key_full (GrlRegistry *registry,
Packit 67b98c
                                         GParamSpec *param_spec,
Packit 67b98c
                                         GrlKeyID key,
Packit 67b98c
                                         GrlKeyID bind_key,
Packit 67b98c
                                         GError **error)
Packit 67b98c
{
Packit 67b98c
  GList *bound_partners;
Packit 67b98c
  GList *partner;
Packit 67b98c
  const gchar *key_name;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
Packit 67b98c
  g_return_val_if_fail (G_IS_PARAM_SPEC (param_spec), 0);
Packit 67b98c
  GrlKeyID registered_key;
Packit 67b98c
Packit 67b98c
  key_name = g_param_spec_get_name (param_spec);
Packit 67b98c
  registered_key = key_id_handler_get_key (&registry->priv->key_id_handler, key_name);
Packit 67b98c
  if (registered_key != GRL_METADATA_KEY_INVALID) {
Packit 67b98c
    GParamSpec *key_spec = g_hash_table_lookup (registry->priv->system_keys,
Packit 67b98c
                                                (gpointer) key_name);
Packit 67b98c
    if (param_spec_is_equal (key_spec, param_spec)) {
Packit 67b98c
      /* Key registered */
Packit 67b98c
      GRL_DEBUG ("metadata key '%s' already registered with same spec", key_name);
Packit 67b98c
      g_param_spec_unref (param_spec);
Packit 67b98c
      return registered_key;
Packit 67b98c
    } else {
Packit 67b98c
      GRL_WARNING ("metadata key '%s' already exists", key_name);
Packit 67b98c
      g_set_error (error,
Packit 67b98c
                   GRL_CORE_ERROR,
Packit 67b98c
                   GRL_CORE_ERROR_REGISTER_METADATA_KEY_FAILED,
Packit 67b98c
                   _("Metadata key “%s” already registered in different format"),
Packit 67b98c
                   key_name);
Packit 67b98c
      return GRL_METADATA_KEY_INVALID;
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  registered_key = key_id_handler_add (&registry->priv->key_id_handler, key, key_name);
Packit 67b98c
Packit 67b98c
  if (registered_key == GRL_METADATA_KEY_INVALID) {
Packit 67b98c
    GRL_WARNING ("metadata key '%s' cannot be registered", key_name);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_REGISTER_METADATA_KEY_FAILED,
Packit 67b98c
                 _("Metadata key “%s” cannot be registered"),
Packit 67b98c
                 key_name);
Packit 67b98c
Packit 67b98c
    return GRL_METADATA_KEY_INVALID;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  g_hash_table_insert (registry->priv->system_keys,
Packit 67b98c
                       (gpointer) key_name,
Packit 67b98c
                       param_spec);
Packit 67b98c
Packit 67b98c
  if (bind_key == GRL_METADATA_KEY_INVALID) {
Packit 67b98c
  /* Key is only related to itself */
Packit 67b98c
    g_hash_table_insert (registry->priv->related_keys,
Packit 67b98c
                         GRLKEYID_TO_POINTER (registered_key),
Packit 67b98c
                         g_list_prepend (NULL,
Packit 67b98c
                                         GRLKEYID_TO_POINTER (registered_key)));
Packit 67b98c
  } else {
Packit 67b98c
    /* Add the new key to the partners */
Packit 67b98c
    bound_partners = g_hash_table_lookup (registry->priv->related_keys, GRLKEYID_TO_POINTER (bind_key));
Packit 67b98c
    bound_partners = g_list_append (bound_partners, GRLKEYID_TO_POINTER (registered_key));
Packit 67b98c
    for (partner = bound_partners;
Packit 67b98c
         partner;
Packit 67b98c
         partner = g_list_next (partner)) {
Packit 67b98c
      g_hash_table_insert (registry->priv->related_keys,
Packit 67b98c
                           partner->data,
Packit 67b98c
                           bound_partners);
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return registered_key;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
G_GNUC_INTERNAL GrlKeyID
Packit 67b98c
grl_registry_register_metadata_key_for_type (GrlRegistry *registry,
Packit 67b98c
                                             const gchar *key_name,
Packit 67b98c
                                             GType type)
Packit 67b98c
{
Packit 67b98c
  GParamSpec *spec;
Packit 67b98c
Packit 67b98c
  switch (type) {
Packit 67b98c
  case G_TYPE_INT:
Packit 67b98c
    spec = g_param_spec_int (key_name,
Packit 67b98c
                             key_name,
Packit 67b98c
                             key_name,
Packit 67b98c
                             0, G_MAXINT,
Packit 67b98c
                             0,
Packit 67b98c
                             G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
Packit 67b98c
    break;
Packit 67b98c
Packit 67b98c
  case G_TYPE_INT64:
Packit 67b98c
    spec = g_param_spec_int64 (key_name,
Packit 67b98c
                               key_name,
Packit 67b98c
                               key_name,
Packit 67b98c
                               -1, G_MAXINT64,
Packit 67b98c
                               -1,
Packit 67b98c
                               G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
Packit 67b98c
    break;
Packit 67b98c
Packit 67b98c
  case G_TYPE_STRING:
Packit 67b98c
    spec = g_param_spec_string (key_name,
Packit 67b98c
                                key_name,
Packit 67b98c
                                key_name,
Packit 67b98c
                                NULL,
Packit 67b98c
                                G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
Packit 67b98c
    break;
Packit 67b98c
Packit 67b98c
  case G_TYPE_BOOLEAN:
Packit 67b98c
    spec = g_param_spec_boolean (key_name,
Packit 67b98c
                                 key_name,
Packit 67b98c
                                 key_name,
Packit 67b98c
                                 FALSE,
Packit 67b98c
                                 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
Packit 67b98c
    break;
Packit 67b98c
Packit 67b98c
  case G_TYPE_FLOAT:
Packit 67b98c
    spec = g_param_spec_float (key_name,
Packit 67b98c
                               key_name,
Packit 67b98c
                               key_name,
Packit 67b98c
                               0, G_MAXFLOAT,
Packit 67b98c
                               0,
Packit 67b98c
                               G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
Packit 67b98c
    break;
Packit 67b98c
Packit 67b98c
  default:
Packit 67b98c
    if (type == G_TYPE_DATE_TIME) {
Packit 67b98c
        spec = g_param_spec_boxed (key_name,
Packit 67b98c
                                   key_name,
Packit 67b98c
                                   key_name,
Packit 67b98c
                                   G_TYPE_DATE_TIME,
Packit 67b98c
                                   G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
Packit 67b98c
    } else {
Packit 67b98c
      GRL_WARNING ("'%s' is being ignored as G_TYPE '%s' is not being handled",
Packit 67b98c
                   key_name, G_VALUE_TYPE_NAME (type));
Packit 67b98c
      return GRL_METADATA_KEY_INVALID;
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return grl_registry_register_metadata_key (registry, spec, GRL_METADATA_KEY_INVALID, NULL);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
key_id_handler_init (struct KeyIDHandler *handler)
Packit 67b98c
{
Packit 67b98c
  const gchar *null_string = NULL;
Packit 67b98c
  handler->string_to_id = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
Packit 67b98c
  handler->id_to_string = g_array_new (FALSE, /* zero terminated */
Packit 67b98c
                                       TRUE,  /* zero-initialised */
Packit 67b98c
                                       sizeof (const gchar *));
Packit 67b98c
  /* We want indices in ->id_to_string to start from 1, so we add a NULL entry
Packit 67b98c
   * for GRL_METADATA_KEY_INVALID (i.e. 0) */
Packit 67b98c
  g_array_insert_val (handler->id_to_string,
Packit 67b98c
                      GRL_METADATA_KEY_INVALID,
Packit 67b98c
                      null_string);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
key_id_handler_free (struct KeyIDHandler *handler)
Packit 67b98c
{
Packit 67b98c
  g_hash_table_unref (handler->string_to_id);
Packit 67b98c
  g_array_unref (handler->id_to_string);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static
Packit 67b98c
GrlKeyID key_id_handler_get_key (struct KeyIDHandler *handler, const gchar *key_name)
Packit 67b98c
{
Packit 67b98c
  gpointer val = g_hash_table_lookup (handler->string_to_id, key_name);
Packit 67b98c
  if (val == NULL)
Packit 67b98c
    return GRL_METADATA_KEY_INVALID;
Packit 67b98c
Packit 67b98c
  return GRLPOINTER_TO_KEYID (val);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static const gchar *
Packit 67b98c
key_id_handler_get_name (struct KeyIDHandler *handler, GrlKeyID key)
Packit 67b98c
{
Packit 67b98c
  if (key < handler->id_to_string->len)
Packit 67b98c
    return g_array_index (handler->id_to_string, const gchar *, key);
Packit 67b98c
Packit 67b98c
  return NULL;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/*
Packit 67b98c
 * key_id_handler_add:
Packit 67b98c
 * @handler: the handler
Packit 67b98c
 * @key: a specific key for system keys, or GRL_METADATA_KEY_INVALID for it to
Packit 67b98c
 * be assigned
Packit 67b98c
 * @name: the name of the key.
Packit 67b98c
 *
Packit 67b98c
 * Add a new key<->name correspondence.
Packit 67b98c
 *
Packit 67b98c
 * Returns: the new key number, or GRL_METADATA_KEY_INVALID if the key could
Packit 67b98c
 * not be created (typically if @key or @name is already registered).
Packit 67b98c
 */
Packit 67b98c
static GrlKeyID
Packit 67b98c
key_id_handler_add (struct KeyIDHandler *handler, GrlKeyID key, const gchar *name)
Packit 67b98c
{
Packit 67b98c
  GrlKeyID _key = key;
Packit 67b98c
Packit 67b98c
  if (_key == GRL_METADATA_KEY_INVALID) {
Packit 67b98c
    /* existing keys go from 1 to (id_to_string->len - 1), so the next
Packit 67b98c
     * available key is id_to_string->len, which will be incremented by
Packit 67b98c
     * g_array_insert_val() */
Packit 67b98c
    _key = handler->id_to_string->len;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (NULL != key_id_handler_get_name (handler, _key)) {
Packit 67b98c
    GRL_WARNING ("Cannot register %d:%s because key is already defined as %s",
Packit 67b98c
                 _key, name, key_id_handler_get_name (handler, _key));
Packit 67b98c
    return GRL_METADATA_KEY_INVALID;
Packit 67b98c
  } else if ( GRL_METADATA_KEY_INVALID != key_id_handler_get_key (handler, name)) {
Packit 67b98c
    /* _key or name is already in use! */
Packit 67b98c
    GRL_WARNING ("Cannot register %d:%s because name is already registered with key %d",
Packit 67b98c
                 _key, name, key_id_handler_get_key (handler, name));
Packit 67b98c
    return GRL_METADATA_KEY_INVALID;
Packit 67b98c
  } else {
Packit 67b98c
    /* name_copy is shared between handler->id_to_string and
Packit 67b98c
     * handler->string_to_id */
Packit 67b98c
    gchar *name_copy = g_strdup (name);
Packit 67b98c
Packit 67b98c
    if (_key >= handler->id_to_string->len)
Packit 67b98c
      g_array_set_size (handler->id_to_string, _key + 1);
Packit 67b98c
Packit 67b98c
    /* yes, g_array_index() is a macro that give you an lvalue */
Packit 67b98c
    g_array_index (handler->id_to_string, const gchar *, _key) = name_copy;
Packit 67b98c
    g_hash_table_insert (handler->string_to_id,
Packit 67b98c
                         name_copy, GRLKEYID_TO_POINTER (_key));
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return _key;
Packit 67b98c
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static GList *
Packit 67b98c
key_id_handler_get_all_keys (struct KeyIDHandler *handler)
Packit 67b98c
{
Packit 67b98c
  return g_hash_table_get_values (handler->string_to_id);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
#define CHECK_NUMERIC_PARAM_SPEC_LIMITS(is_type, cast_type, a, b) {     \
Packit 67b98c
  if (is_type) {                                                        \
Packit 67b98c
    if ((cast_type(a))->maximum != (cast_type(b))->maximum ||           \
Packit 67b98c
        (cast_type(a))->minimum != (cast_type(b))->minimum ||           \
Packit 67b98c
        (cast_type(a))->default_value != (cast_type(b))->default_value) \
Packit 67b98c
      return FALSE;                                                     \
Packit 67b98c
    return TRUE;                                                        \
Packit 67b98c
  }                                                                     \
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/* @curr: The current spec we have
Packit 67b98c
 * @new: The spec to match
Packit 67b98c
 *
Packit 67b98c
 * Returns: true if specs are the same, false otherwise.
Packit 67b98c
 */
Packit 67b98c
static gboolean
Packit 67b98c
param_spec_is_equal (GParamSpec *cur,
Packit 67b98c
                     GParamSpec *new)
Packit 67b98c
{
Packit 67b98c
  GType ctype = G_PARAM_SPEC_TYPE (cur);
Packit 67b98c
Packit 67b98c
  if (ctype != G_PARAM_SPEC_TYPE (new))
Packit 67b98c
    return FALSE;
Packit 67b98c
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_INT),
Packit 67b98c
                                   G_PARAM_SPEC_INT, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_LONG),
Packit 67b98c
                                   G_PARAM_SPEC_LONG, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_INT64),
Packit 67b98c
                                   G_PARAM_SPEC_INT64, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_CHAR),
Packit 67b98c
                                   G_PARAM_SPEC_CHAR, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_UINT),
Packit 67b98c
                                   G_PARAM_SPEC_UINT, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_ULONG),
Packit 67b98c
                                   G_PARAM_SPEC_ULONG, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_UINT64),
Packit 67b98c
                                   G_PARAM_SPEC_UINT64, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_UCHAR),
Packit 67b98c
                                   G_PARAM_SPEC_UCHAR, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_FLOAT),
Packit 67b98c
                                   G_PARAM_SPEC_FLOAT, cur, new);
Packit 67b98c
  CHECK_NUMERIC_PARAM_SPEC_LIMITS ((ctype == G_TYPE_PARAM_DOUBLE),
Packit 67b98c
                                   G_PARAM_SPEC_DOUBLE, cur, new);
Packit 67b98c
  if (ctype == G_TYPE_PARAM_STRING) {
Packit 67b98c
    GParamSpecString *c = G_PARAM_SPEC_STRING (cur);
Packit 67b98c
    GParamSpecString *n = G_PARAM_SPEC_STRING (new);
Packit 67b98c
    return (g_strcmp0 (c->default_value, n->default_value) == 0);
Packit 67b98c
  } else if (ctype == G_TYPE_PARAM_ENUM) {
Packit 67b98c
    GParamSpecEnum *c = G_PARAM_SPEC_ENUM (cur);
Packit 67b98c
    GParamSpecEnum *n = G_PARAM_SPEC_ENUM (new);
Packit 67b98c
    if (c->default_value != n->default_value ||
Packit 67b98c
        cur->value_type != new->value_type) {
Packit 67b98c
      GRL_DEBUG ("%s differ (values: %d and %d) (types: %s and %s)",
Packit 67b98c
                 g_type_name (ctype), c->default_value, n->default_value,
Packit 67b98c
                 g_type_name (cur->value_type), g_type_name (new->value_type));
Packit 67b98c
      return FALSE;
Packit 67b98c
    }
Packit 67b98c
  } else if (ctype == G_TYPE_PARAM_FLAGS) {
Packit 67b98c
    GParamSpecFlags *c = G_PARAM_SPEC_FLAGS (cur);
Packit 67b98c
    GParamSpecFlags *n = G_PARAM_SPEC_FLAGS (new);
Packit 67b98c
    if (c->default_value != n->default_value ||
Packit 67b98c
        cur->value_type != new->value_type) {
Packit 67b98c
      GRL_DEBUG ("%s differ (values: %d and %d) (types: %s and %s)",
Packit 67b98c
                 g_type_name (ctype), c->default_value, n->default_value,
Packit 67b98c
                 g_type_name (cur->value_type), g_type_name (new->value_type));
Packit 67b98c
      return FALSE;
Packit 67b98c
    }
Packit 67b98c
  } else if (ctype == G_TYPE_PARAM_BOOLEAN) {
Packit 67b98c
    GParamSpecBoolean *c = G_PARAM_SPEC_BOOLEAN (cur);
Packit 67b98c
    GParamSpecBoolean *n = G_PARAM_SPEC_BOOLEAN (new);
Packit 67b98c
    if (c->default_value != n->default_value) {
Packit 67b98c
      GRL_DEBUG ("%s type differ: %s != %s", g_type_name (ctype),
Packit 67b98c
                 g_type_name (cur->value_type), g_type_name (new->value_type));
Packit 67b98c
      return FALSE;
Packit 67b98c
    }
Packit 67b98c
  } else if (ctype == G_TYPE_PARAM_BOXED || ctype == G_TYPE_PARAM_OBJECT) {
Packit 67b98c
    if (cur->value_type != new->value_type) {
Packit 67b98c
      GRL_DEBUG ("%s type differ: %s != %s", g_type_name (ctype),
Packit 67b98c
                 g_type_name (cur->value_type), g_type_name (new->value_type));
Packit 67b98c
      return FALSE;
Packit 67b98c
    }
Packit 67b98c
  } else {
Packit 67b98c
    g_warn_if_reached();
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return TRUE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
#undef CHECK_NUMERIC_PARAM_SPEC_LIMITS
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
shutdown_plugin (GrlPlugin *plugin)
Packit 67b98c
{
Packit 67b98c
  GRL_DEBUG ("Unloading plugin '%s'", grl_plugin_get_id (plugin));
Packit 67b98c
  grl_plugin_unload (plugin);
Packit 67b98c
Packit 67b98c
  if (grl_plugin_get_module (plugin)) {
Packit 67b98c
    g_module_close (grl_plugin_get_module (plugin));
Packit 67b98c
    grl_plugin_set_module (plugin, NULL);
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/* ================ PRIVATE API ================ */
Packit 67b98c
Packit 67b98c
/*
Packit 67b98c
 * grl_registry_restrict_plugins:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @plugins: a @NULL-terminated array of plugins identifiers
Packit 67b98c
 *
Packit 67b98c
 * Restrict the plugins that application sees to this list.
Packit 67b98c
 *
Packit 67b98c
 * Other plugins will not be available for the application, unless it uses
Packit 67b98c
 * directly #grl_registry_load_plugin() function.
Packit 67b98c
 **/
Packit 67b98c
void
Packit 67b98c
grl_registry_restrict_plugins (GrlRegistry *registry,
Packit 67b98c
                               gchar **plugins)
Packit 67b98c
{
Packit 67b98c
  g_return_if_fail (GRL_IS_REGISTRY (registry));
Packit 67b98c
  g_return_if_fail (plugins);
Packit 67b98c
Packit 67b98c
  /* Free previous list */
Packit 67b98c
  if (registry->priv->allowed_plugins) {
Packit 67b98c
    g_slist_free_full (registry->priv->allowed_plugins, g_free);
Packit 67b98c
    registry->priv->allowed_plugins = NULL;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  while (*plugins) {
Packit 67b98c
    registry->priv->allowed_plugins = g_slist_prepend (registry->priv->allowed_plugins,
Packit 67b98c
                                                       g_strdup (*plugins));
Packit 67b98c
    plugins++;
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/*
Packit 67b98c
 * grl_registry_shutdown:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 *
Packit 67b98c
 * Frees all the resources in the registry and the registry itself.
Packit 67b98c
 **/
Packit 67b98c
void
Packit 67b98c
grl_registry_shutdown (GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  GHashTableIter iter;
Packit 67b98c
  GList *each_key;
Packit 67b98c
  GList *related_keys = NULL;
Packit 67b98c
  GrlPlugin *plugin = NULL;
Packit 67b98c
  GrlSource *source = NULL;
Packit 67b98c
Packit 67b98c
  if (registry->priv->plugins) {
Packit 67b98c
    g_hash_table_iter_init (&iter, registry->priv->plugins);
Packit 67b98c
    while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &plugin)) {
Packit 67b98c
      shutdown_plugin (plugin);
Packit 67b98c
    }
Packit 67b98c
    g_clear_pointer (&registry->priv->plugins, g_hash_table_unref);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (registry->priv->sources) {
Packit 67b98c
    g_hash_table_iter_init (&iter, registry->priv->sources);
Packit 67b98c
    while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &source)) {
Packit 67b98c
      g_object_unref (source);
Packit 67b98c
    }
Packit 67b98c
    g_clear_pointer (&registry->priv->sources, g_hash_table_unref);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  g_clear_pointer (&registry->priv->ranks, g_hash_table_unref);
Packit 67b98c
  g_clear_pointer (&registry->priv->configs, g_hash_table_unref);
Packit 67b98c
Packit 67b98c
  /* We need to free this table with care. Several keys can be pointing to the
Packit 67b98c
     same value, so we need to ensure that we only free the value once */
Packit 67b98c
  if (registry->priv->related_keys) {
Packit 67b98c
    while (TRUE) {
Packit 67b98c
      g_hash_table_iter_init (&iter, registry->priv->related_keys);
Packit 67b98c
      if (!g_hash_table_iter_next (&iter, NULL, (gpointer *) &related_keys)) {
Packit 67b98c
        break;
Packit 67b98c
      }
Packit 67b98c
      /* This will invalidate the iterator */
Packit 67b98c
      for (each_key = related_keys; each_key; each_key = g_list_next (each_key)) {
Packit 67b98c
        g_hash_table_remove (registry->priv->related_keys, GRLKEYID_TO_POINTER (each_key->data));
Packit 67b98c
      }
Packit 67b98c
      g_list_free (related_keys);
Packit 67b98c
    }
Packit 67b98c
    g_clear_pointer (&registry->priv->related_keys, g_hash_table_unref);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  g_slist_free_full (registry->priv->plugins_dir, (GDestroyNotify) g_free);
Packit 67b98c
  g_slist_free_full (registry->priv->allowed_plugins, (GDestroyNotify) g_free);
Packit 67b98c
Packit 67b98c
  key_id_handler_free (&registry->priv->key_id_handler);
Packit 67b98c
  g_clear_pointer (&registry->priv->system_keys, g_hash_table_unref);
Packit 67b98c
Packit 67b98c
  g_object_unref (registry);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/* ================ PUBLIC API ================ */
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_get_default:
Packit 67b98c
 *
Packit 67b98c
 * As the registry is designed to work as a singleton, this
Packit 67b98c
 * method is in charge of creating the only instance or
Packit 67b98c
 * returned it if it is already in memory.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (transfer none): a new or an already created instance of the registry.
Packit 67b98c
 *
Packit 67b98c
 * It is NOT MT-safe
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
GrlRegistry *
Packit 67b98c
grl_registry_get_default (void)
Packit 67b98c
{
Packit 67b98c
  static GrlRegistry *registry = NULL;
Packit 67b98c
Packit 67b98c
  if (!registry) {
Packit 67b98c
    registry = g_object_new (GRL_TYPE_REGISTRY, NULL);
Packit 67b98c
    g_object_add_weak_pointer (G_OBJECT (registry), (gpointer *) &registry);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return registry;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_register_source:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @plugin: the plugin which owns the source
Packit 67b98c
 * @source: (transfer full): the source to register
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Register a @source in the @registry with the given @plugin information
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if success, %FALSE% otherwise.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_register_source (GrlRegistry *registry,
Packit 67b98c
                              GrlPlugin *plugin,
Packit 67b98c
                              GrlSource *source,
Packit 67b98c
                              GError **error)
Packit 67b98c
{
Packit 67b98c
  gchar *id;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (GRL_IS_PLUGIN (plugin), FALSE);
Packit 67b98c
  g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
Packit 67b98c
Packit 67b98c
  g_object_get (source, "source-id", &id, NULL);
Packit 67b98c
  GRL_DEBUG ("New source available: '%s'", id);
Packit 67b98c
Packit 67b98c
  /* Take ownership of the source */
Packit 67b98c
  g_object_ref_sink (source);
Packit 67b98c
  g_object_unref (source);
Packit 67b98c
Packit 67b98c
  /* Do not free id, since g_hash_table_insert does not copy,
Packit 67b98c
     it will be freed when removed from the hash table */
Packit 67b98c
  g_hash_table_insert (registry->priv->sources, id, source);
Packit 67b98c
Packit 67b98c
  /* Set the plugin as owner of source */
Packit 67b98c
  g_object_set (source, "plugin", plugin, NULL);
Packit 67b98c
Packit 67b98c
  /* Set source rank */
Packit 67b98c
  set_source_rank (registry, source);
Packit 67b98c
Packit 67b98c
  /* Update whether it should be invisible */
Packit 67b98c
  update_source_visibility (registry, source);
Packit 67b98c
Packit 67b98c
  if (!SOURCE_IS_INVISIBLE(source))
Packit 67b98c
    g_signal_emit (registry, registry_signals[SIG_SOURCE_ADDED], 0, source);
Packit 67b98c
Packit 67b98c
  return TRUE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_unregister_source:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @source: the source to unregister
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Removes the @source from the @registry hash table
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if success, %FALSE% otherwise.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_unregister_source (GrlRegistry *registry,
Packit 67b98c
                                GrlSource *source,
Packit 67b98c
                                GError **error)
Packit 67b98c
{
Packit 67b98c
  gchar *id;
Packit 67b98c
  gboolean ret = TRUE;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (GRL_IS_SOURCE (source), FALSE);
Packit 67b98c
Packit 67b98c
  g_object_get (source, "source-id", &id, NULL);
Packit 67b98c
  GRL_DEBUG ("Unregistering source '%s'", id);
Packit 67b98c
Packit 67b98c
  if (g_hash_table_remove (registry->priv->sources, id)) {
Packit 67b98c
    GRL_DEBUG ("source '%s' is no longer available", id);
Packit 67b98c
    g_signal_emit (registry, registry_signals[SIG_SOURCE_REMOVED], 0, source);
Packit 67b98c
    g_object_unref (source);
Packit 67b98c
  } else {
Packit 67b98c
    GRL_WARNING ("source '%s' not found", id);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_UNREGISTER_SOURCE_FAILED,
Packit 67b98c
                 _("Source with id “%s” was not found"), id);
Packit 67b98c
    ret = FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  g_free (id);
Packit 67b98c
  return ret;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_add_directory:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @path: a path with plugins
Packit 67b98c
 *
Packit 67b98c
 * Set this path as part of default paths to load plugins.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
void
Packit 67b98c
grl_registry_add_directory (GrlRegistry *registry,
Packit 67b98c
                            const gchar *path)
Packit 67b98c
{
Packit 67b98c
  g_return_if_fail (GRL_IS_REGISTRY (registry));
Packit 67b98c
  g_return_if_fail (path);
Packit 67b98c
Packit 67b98c
  /* Use append instead of prepend so plugins are loaded in the same order as
Packit 67b98c
     they were added */
Packit 67b98c
  registry->priv->plugins_dir = g_slist_append (registry->priv->plugins_dir,
Packit 67b98c
                                                g_strdup (path));
Packit 67b98c
  registry->priv->all_plugins_preloaded = FALSE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static GrlPlugin *
Packit 67b98c
grl_registry_prepare_plugin_from_desc (GrlRegistry *registry,
Packit 67b98c
                                       GrlPluginDescriptor *plugin_desc)
Packit 67b98c
{
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
Packit 67b98c
  if (!plugin_desc->init ||
Packit 67b98c
      !plugin_desc->id) {
Packit 67b98c
    GRL_WARNING ("Plugin descriptor is not valid");
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  plugin = g_object_new (GRL_TYPE_PLUGIN, NULL);
Packit 67b98c
  grl_plugin_set_id (plugin, plugin_desc->id);
Packit 67b98c
  grl_plugin_set_filename (plugin, plugin_desc->id);
Packit 67b98c
Packit 67b98c
  grl_plugin_set_load_func (plugin, plugin_desc->init);
Packit 67b98c
  grl_plugin_set_unload_func (plugin, plugin_desc->deinit);
Packit 67b98c
  grl_plugin_set_register_keys_func (plugin, plugin_desc->register_keys);
Packit 67b98c
Packit 67b98c
  /* Insert plugin ID as part of plugin information */
Packit 67b98c
  grl_plugin_set_module_name (plugin, plugin_desc->id);
Packit 67b98c
Packit 67b98c
  return plugin;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static GrlPlugin *
Packit 67b98c
grl_registry_prepare_plugin (GrlRegistry *registry,
Packit 67b98c
                             const gchar *library_filename,
Packit 67b98c
                             GError **error)
Packit 67b98c
{
Packit 67b98c
  GModule *module;
Packit 67b98c
  GrlPluginDescriptor *plugin_desc;
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
Packit 67b98c
  module = g_module_open (library_filename, G_MODULE_BIND_LOCAL);
Packit 67b98c
  if (!module) {
Packit 67b98c
    GRL_WARNING ("Failed to open module: %s", g_module_error ());
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Failed to load plugin from %s"), library_filename);
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (!g_module_symbol (module, "GRL_PLUGIN_DESCRIPTOR", (gpointer) &plugin_desc)) {
Packit 67b98c
    GRL_WARNING ("Plugin descriptor not found in '%s'", library_filename);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Invalid plugin file %s"), library_filename);
Packit 67b98c
    g_module_close (module);
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (!plugin_desc->init ||
Packit 67b98c
      !plugin_desc->id) {
Packit 67b98c
    GRL_WARNING ("Plugin descriptor is not valid: '%s'", library_filename);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("“%s” is not a valid plugin file"), library_filename);
Packit 67b98c
    g_module_close (module);
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  /* Check if plugin is preloaded; if not, then create one */
Packit 67b98c
  plugin = g_hash_table_lookup (registry->priv->plugins,
Packit 67b98c
                                plugin_desc->id);
Packit 67b98c
Packit 67b98c
  if (plugin) {
Packit 67b98c
    g_module_close (module);
Packit 67b98c
    /* Check if the existent plugin is precisely this same plugin */
Packit 67b98c
    if (g_strcmp0 (grl_plugin_get_filename (plugin), library_filename) == 0) {
Packit 67b98c
      return plugin;
Packit 67b98c
    } else {
Packit 67b98c
      GRL_WARNING ("Plugin '%s' already exists", library_filename);
Packit 67b98c
      g_set_error (error,
Packit 67b98c
                   GRL_CORE_ERROR,
Packit 67b98c
                   GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                   _("Plugin “%s” already exists"), library_filename);
Packit 67b98c
      return NULL;
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  /* Check if plugin is allowed */
Packit 67b98c
  if (registry->priv->allowed_plugins &&
Packit 67b98c
      !g_slist_find_custom (registry->priv->allowed_plugins,
Packit 67b98c
                            plugin_desc->id,
Packit 67b98c
                            (GCompareFunc) g_strcmp0)) {
Packit 67b98c
    GRL_DEBUG ("Plugin '%s' not allowed; skipping", plugin_desc->id);
Packit 67b98c
    g_module_close (module);
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  plugin = g_object_new (GRL_TYPE_PLUGIN, NULL);
Packit 67b98c
  grl_plugin_set_desc (plugin, plugin_desc);
Packit 67b98c
  grl_plugin_set_module (plugin, module);
Packit 67b98c
  grl_plugin_set_filename (plugin, library_filename);
Packit 67b98c
Packit 67b98c
  /* Make plugin resident */
Packit 67b98c
  g_module_make_resident (module);
Packit 67b98c
Packit 67b98c
  g_hash_table_insert (registry->priv->plugins, g_strdup (plugin_desc->id), plugin);
Packit 67b98c
Packit 67b98c
  /* Register custom keys */
Packit 67b98c
  grl_plugin_register_keys (plugin);
Packit 67b98c
Packit 67b98c
  return plugin;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_activate_all_plugins:
Packit 67b98c
 * @registry: the registry instace
Packit 67b98c
 *
Packit 67b98c
 * Activate all the plugins loaded.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if some plugin has been activated
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.3.0
Packit 67b98c
 **/
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_activate_all_plugins (GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  GList *all_plugins;
Packit 67b98c
  GList *l;
Packit 67b98c
  gboolean plugin_activated = FALSE;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
Packit 67b98c
  all_plugins = g_hash_table_get_values (registry->priv->plugins);
Packit 67b98c
  for (l = all_plugins; l; l = l->next) {
Packit 67b98c
    GrlPlugin *plugin = l->data;
Packit 67b98c
    plugin_activated |= activate_plugin (registry, plugin, NULL);
Packit 67b98c
  }
Packit 67b98c
  g_list_free (all_plugins);
Packit 67b98c
Packit 67b98c
  return plugin_activated;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_load_plugin:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @library_filename: the path to the so file
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Loads a module from shared object file stored in @path
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if the module is loaded correctly
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_load_plugin (GrlRegistry *registry,
Packit 67b98c
                          const gchar *library_filename,
Packit 67b98c
                          GError **error)
Packit 67b98c
{
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
Packit 67b98c
  plugin = grl_registry_prepare_plugin (registry, library_filename, error);
Packit 67b98c
  if (!plugin)
Packit 67b98c
    return FALSE;
Packit 67b98c
Packit 67b98c
  return register_keys_plugin (registry, plugin, error);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_load_plugin_from_desc: (skip)
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @plugin_desc: the #GrlPluginDescriptor for the plugin
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Loads the grilo plugin defined by @plugin_desc. This is
Packit 67b98c
 * used to load plugins that aren't shared libraries, and are
Packit 67b98c
 * built into applications.
Packit 67b98c
 *
Packit 67b98c
 * <example>
Packit 67b98c
 *   Minimal example for loading a builtin plugin, in C.
Packit 67b98c
 *   <programlisting>
Packit 67b98c
 *     static GrlPluginDescriptor descriptor = {
Packit 67b98c
 *       .plugin_id = "grl-example",
Packit 67b98c
 *       .plugin_init = grl_example_plugin_init,
Packit 67b98c
 *     };
Packit 67b98c
 *
Packit 67b98c
 *     grl_registry_load_plugin_from_desc (registry, &descriptor, &error);
Packit 67b98c
 *   </programlisting>
Packit 67b98c
 * </example>
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if the plugin is initialised correctly
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.3.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_load_plugin_from_desc (GrlRegistry *registry,
Packit 67b98c
                                    GrlPluginDescriptor *plugin_desc,
Packit 67b98c
                                    GError **error)
Packit 67b98c
{
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
Packit 67b98c
  plugin = grl_registry_prepare_plugin_from_desc (registry, plugin_desc);
Packit 67b98c
  if (!plugin)
Packit 67b98c
    return FALSE;
Packit 67b98c
Packit 67b98c
  return register_keys_plugin (registry, plugin, error) &&
Packit 67b98c
         activate_plugin (registry, plugin, error);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_load_plugin_directory:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @path: the path to the directory
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Loads a set of modules from directory in @path which contains
Packit 67b98c
 * a group shared object files.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if the directory is valid.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_load_plugin_directory (GrlRegistry *registry,
Packit 67b98c
                                    const gchar *path,
Packit 67b98c
                                    GError **error)
Packit 67b98c
{
Packit 67b98c
  GDir *dir;
Packit 67b98c
  GError *dir_error = NULL;
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
  const gchar *entry;
Packit 67b98c
  gboolean plugin_loaded = FALSE;
Packit 67b98c
  gchar *filename;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (path, FALSE);
Packit 67b98c
Packit 67b98c
  dir = g_dir_open (path, 0, &dir_error);
Packit 67b98c
  if (!dir) {
Packit 67b98c
    GRL_WARNING ("Could not open directory '%s': %s",
Packit 67b98c
                 path,
Packit 67b98c
                 dir_error->message);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Invalid path %s"), path);
Packit 67b98c
    g_error_free (dir_error);
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  while ((entry = g_dir_read_name (dir)) != NULL) {
Packit 67b98c
    filename = g_build_filename (path, entry, NULL);
Packit 67b98c
    if (g_strrstr (filename, "." G_MODULE_SUFFIX) == NULL) {
Packit 67b98c
      g_free (filename);
Packit 67b98c
      continue;
Packit 67b98c
    }
Packit 67b98c
Packit 67b98c
    plugin = grl_registry_prepare_plugin (registry, filename, NULL);
Packit 67b98c
    plugin_loaded |= (plugin != NULL);
Packit 67b98c
    g_free (filename);
Packit 67b98c
  }
Packit 67b98c
  g_dir_close (dir);
Packit 67b98c
Packit 67b98c
  return plugin_loaded;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_load_all_plugins:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @activate: %TRUE if plugins must be activated after loading
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Load all the modules available in the default directory path.
Packit 67b98c
 *
Packit 67b98c
 * The default directory path can be changed through the environment
Packit 67b98c
 * variable %GRL_PLUGIN_PATH and it can contain several paths separated
Packit 67b98c
 * by ":"
Packit 67b98c
 *
Packit 67b98c
 * Returns: %FALSE% is all the configured plugin paths are invalid,
Packit 67b98c
 * %TRUE% otherwise.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_load_all_plugins (GrlRegistry *registry,
Packit 67b98c
                               gboolean activate,
Packit 67b98c
                               GError **error)
Packit 67b98c
{
Packit 67b98c
  GSList *plugin_dir;
Packit 67b98c
  gboolean loaded_one;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), TRUE);
Packit 67b98c
Packit 67b98c
  /* Preload all plugins */
Packit 67b98c
  if (!registry->priv->all_plugins_preloaded) {
Packit 67b98c
    for (plugin_dir = registry->priv->plugins_dir;
Packit 67b98c
         plugin_dir;
Packit 67b98c
         plugin_dir = g_slist_next (plugin_dir)) {
Packit 67b98c
      grl_registry_load_plugin_directory (registry,
Packit 67b98c
                                          plugin_dir->data,
Packit 67b98c
                                          NULL);
Packit 67b98c
    }
Packit 67b98c
    registry->priv->all_plugins_preloaded = TRUE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (activate) {
Packit 67b98c
    loaded_one = grl_registry_activate_all_plugins (registry);
Packit 67b98c
Packit 67b98c
    if (!loaded_one) {
Packit 67b98c
      g_set_error (error,
Packit 67b98c
                   GRL_CORE_ERROR,
Packit 67b98c
                   GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                   _("All configured plugin paths are invalid"));
Packit 67b98c
    }
Packit 67b98c
Packit 67b98c
    return loaded_one;
Packit 67b98c
  } else {
Packit 67b98c
    return TRUE;
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_activate_plugin_by_id:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @plugin_id: plugin identifier
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Activates plugin identified by @plugin_id.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if the plugin is loaded correctly
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.3.0
Packit 67b98c
 **/
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_activate_plugin_by_id (GrlRegistry *registry,
Packit 67b98c
                                    const gchar *plugin_id,
Packit 67b98c
                                    GError **error)
Packit 67b98c
{
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
  gboolean is_loaded;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (plugin_id, FALSE);
Packit 67b98c
Packit 67b98c
Packit 67b98c
  /* Check if plugin is available */
Packit 67b98c
  plugin = g_hash_table_lookup (registry->priv->plugins, plugin_id);
Packit 67b98c
  if (!plugin) {
Packit 67b98c
    GRL_WARNING ("Plugin '%s' not available", plugin_id);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Plugin “%s” not available"), plugin_id);
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  /* Check if plugin is already loaded */
Packit 67b98c
  g_object_get (plugin, "loaded", &is_loaded, NULL);
Packit 67b98c
  if (is_loaded) {
Packit 67b98c
    GRL_WARNING ("Plugin '%s' is already loaded", plugin_id);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_LOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Plugin “%s” is already loaded"), plugin_id);
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  /* activate plugin */
Packit 67b98c
  return activate_plugin (registry, plugin, error);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_source:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @source_id: the id of a source
Packit 67b98c
 *
Packit 67b98c
 * This function will search and retrieve a source given its identifier.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (transfer none): The source found.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
GrlSource *
Packit 67b98c
grl_registry_lookup_source (GrlRegistry *registry,
Packit 67b98c
                            const gchar *source_id)
Packit 67b98c
{
Packit 67b98c
  GrlSource *source;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
Packit 67b98c
  g_return_val_if_fail (source_id != NULL, NULL);
Packit 67b98c
Packit 67b98c
  source = (GrlSource *) g_hash_table_lookup (registry->priv->sources,
Packit 67b98c
                                              source_id);
Packit 67b98c
  if (source && !SOURCE_IS_INVISIBLE(source))
Packit 67b98c
    return source;
Packit 67b98c
  return NULL;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_get_sources:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @ranked: whether the returned list shall be returned ordered by rank
Packit 67b98c
 *
Packit 67b98c
 * This function will return all the available sources in the @registry.
Packit 67b98c
 *
Packit 67b98c
 * If @ranked is %TRUE, the source list will be ordered by rank.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (element-type GrlSource) (transfer container): a #GList of
Packit 67b98c
 * available #GrlSources. The content of the list should not be
Packit 67b98c
 * modified or freed. Use g_list_free() when done using the list.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
GList *
Packit 67b98c
grl_registry_get_sources (GrlRegistry *registry,
Packit 67b98c
                          gboolean ranked)
Packit 67b98c
{
Packit 67b98c
  GHashTableIter iter;
Packit 67b98c
  GList *source_list = NULL;
Packit 67b98c
  GrlSource *current_source;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
Packit 67b98c
Packit 67b98c
  g_hash_table_iter_init (&iter, registry->priv->sources);
Packit 67b98c
  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &current_source)) {
Packit 67b98c
    if (!SOURCE_IS_INVISIBLE(current_source))
Packit 67b98c
      source_list = g_list_prepend (source_list, current_source);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (ranked) {
Packit 67b98c
    source_list = g_list_sort (source_list, (GCompareFunc) compare_by_rank);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return source_list;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_get_sources_by_operations:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @ops: a bitwise mangle of the requested operations.
Packit 67b98c
 * @ranked: whether the returned list shall be returned ordered by rank
Packit 67b98c
 *
Packit 67b98c
 * Give an array of all the available sources in the @registry capable of
Packit 67b98c
 * perform the operations requested in @ops.
Packit 67b98c
 *
Packit 67b98c
 * If @ranked is %TRUE, the source list will be ordered by rank.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (element-type GrlSource) (transfer container): a #GList of
Packit 67b98c
 * available #GrlSources. The content of the list should not be
Packit 67b98c
 * modified or freed. Use g_list_free() when done using the list.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
GList *
Packit 67b98c
grl_registry_get_sources_by_operations (GrlRegistry *registry,
Packit 67b98c
                                        GrlSupportedOps ops,
Packit 67b98c
                                        gboolean ranked)
Packit 67b98c
{
Packit 67b98c
  GHashTableIter iter;
Packit 67b98c
  GList *source_list = NULL;
Packit 67b98c
  GrlSource *source;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
Packit 67b98c
Packit 67b98c
  g_hash_table_iter_init (&iter, registry->priv->sources);
Packit 67b98c
  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &source)) {
Packit 67b98c
    GrlSupportedOps source_ops;
Packit 67b98c
    source_ops =
Packit 67b98c
      grl_source_supported_operations (source);
Packit 67b98c
    if ((source_ops & ops) == ops &&
Packit 67b98c
        !SOURCE_IS_INVISIBLE(source)) {
Packit 67b98c
      source_list = g_list_prepend (source_list, source);
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  if (ranked) {
Packit 67b98c
    source_list = g_list_sort (source_list, compare_by_rank);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return source_list;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_plugin:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @plugin_id: the id of a plugin
Packit 67b98c
 *
Packit 67b98c
 * This function will search and retrieve a plugin given its identifier.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (transfer none): The plugin found
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
GrlPlugin *
Packit 67b98c
grl_registry_lookup_plugin (GrlRegistry *registry,
Packit 67b98c
                            const gchar *plugin_id)
Packit 67b98c
{
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
Packit 67b98c
  g_return_val_if_fail (plugin_id, NULL);
Packit 67b98c
Packit 67b98c
  return (GrlPlugin *) g_hash_table_lookup (registry->priv->plugins,
Packit 67b98c
                                            plugin_id);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_get_plugins:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @only_loaded: whether the returned list shall include only loaded plugins
Packit 67b98c
 *
Packit 67b98c
 * This function will return all the available plugins in the @registry.
Packit 67b98c
 *
Packit 67b98c
 * If @only_loaded is %TRUE, the plugin list will contain only plugins that are
Packit 67b98c
 * loaded.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (element-type GrlPlugin) (transfer container): a #GList of
Packit 67b98c
 * available #GrlPlugins. The content of the list should not be modified
Packit 67b98c
 * or freed. Use g_list_free() when done using the list.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
GList *
Packit 67b98c
grl_registry_get_plugins (GrlRegistry *registry,
Packit 67b98c
                          gboolean only_loaded)
Packit 67b98c
{
Packit 67b98c
  GList *plugin_list = NULL;
Packit 67b98c
  GHashTableIter iter;
Packit 67b98c
  GrlPlugin *current_plugin;
Packit 67b98c
  gboolean is_loaded;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
Packit 67b98c
Packit 67b98c
  if (only_loaded) {
Packit 67b98c
    g_hash_table_iter_init (&iter, registry->priv->plugins);
Packit 67b98c
    while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &current_plugin)) {
Packit 67b98c
      g_object_get (current_plugin, "loaded", &is_loaded, NULL);
Packit 67b98c
      if (is_loaded) {
Packit 67b98c
        plugin_list = g_list_prepend (plugin_list, current_plugin);
Packit 67b98c
      }
Packit 67b98c
    }
Packit 67b98c
  } else {
Packit 67b98c
    plugin_list = g_hash_table_get_keys (registry->priv->plugins);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return plugin_list;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_unload_plugin:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @plugin_id: the identifier of the plugin
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Unload from memory a module identified by @plugin_id. This means call the
Packit 67b98c
 * module's deinit function.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE% on success.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_unload_plugin (GrlRegistry *registry,
Packit 67b98c
                            const gchar *plugin_id,
Packit 67b98c
                            GError **error)
Packit 67b98c
{
Packit 67b98c
  GrlPlugin *plugin;
Packit 67b98c
  GList *sources = NULL;
Packit 67b98c
  GList *sources_iter;
Packit 67b98c
Packit 67b98c
  GRL_DEBUG ("%s: %s", __FUNCTION__, plugin_id);
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (plugin_id != NULL, FALSE);
Packit 67b98c
Packit 67b98c
  /* First check the plugin is valid  */
Packit 67b98c
  plugin = g_hash_table_lookup (registry->priv->plugins, plugin_id);
Packit 67b98c
  if (!plugin) {
Packit 67b98c
    GRL_WARNING ("Could not deinit plugin '%s'. Plugin not found.", plugin_id);
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_UNLOAD_PLUGIN_FAILED,
Packit 67b98c
                 _("Plugin not found: “%s”"), plugin_id);
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  /* Second, shut down any sources spawned by this plugin */
Packit 67b98c
  GRL_DEBUG ("Shutting down sources spawned by '%s'", plugin_id);
Packit 67b98c
  sources = grl_registry_get_sources (registry, FALSE);
Packit 67b98c
Packit 67b98c
  for (sources_iter = sources; sources_iter;
Packit 67b98c
      sources_iter = g_list_next (sources_iter)) {
Packit 67b98c
    GrlSource *source = GRL_SOURCE (sources_iter->data);
Packit 67b98c
    if (grl_source_get_plugin (source) == plugin) {
Packit 67b98c
      grl_registry_unregister_source (registry, source, NULL);
Packit 67b98c
    }
Packit 67b98c
  }
Packit 67b98c
  g_list_free (sources);
Packit 67b98c
Packit 67b98c
  /* Third, shut down the plugin */
Packit 67b98c
  shutdown_plugin (plugin);
Packit 67b98c
Packit 67b98c
  return TRUE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_register_metadata_key:
Packit 67b98c
 * @registry: The plugin registry
Packit 67b98c
 * @param_spec: (transfer full): The definition of the key to register
Packit 67b98c
 * @bind_key: The key the new key is bind to, or #GRL_METADATA_KEY_INVALID if it is not bound.
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Registers a new metadata key, creating a relation between the new key and
Packit 67b98c
 * @bind_key.
Packit 67b98c
 *
Packit 67b98c
 * Two keys are related when the values of both keys are somehow related.
Packit 67b98c
 *
Packit 67b98c
 * One example of a relation would be the one between the URI of a media
Packit 67b98c
 * resource and its mime-type: they are both tied together and one does not make
Packit 67b98c
 * sense without the other.
Packit 67b98c
 *
Packit 67b98c
 * Relations between keys allow the framework to provide all the data that is
Packit 67b98c
 * somehow related when any of the related keys are requested.
Packit 67b98c
Packit 67b98c
 * Returns: The #GrlKeyID registered.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.3.0
Packit 67b98c
 */
Packit 67b98c
GrlKeyID
Packit 67b98c
grl_registry_register_metadata_key (GrlRegistry *registry,
Packit 67b98c
                                    GParamSpec *param_spec,
Packit 67b98c
                                    GrlKeyID bind_key,
Packit 67b98c
                                    GError **error)
Packit 67b98c
{
Packit 67b98c
  GrlKeyID key;
Packit 67b98c
Packit 67b98c
  key = grl_registry_register_metadata_key_full (registry,
Packit 67b98c
                                                 param_spec,
Packit 67b98c
                                                 GRL_METADATA_KEY_INVALID,
Packit 67b98c
                                                 bind_key,
Packit 67b98c
                                                 error);
Packit 67b98c
Packit 67b98c
  if (key != GRL_METADATA_KEY_INVALID) {
Packit 67b98c
    g_signal_emit (registry, registry_signals[SIG_METADATA_KEY_ADDED],
Packit 67b98c
                   0,
Packit 67b98c
                   grl_metadata_key_get_name (key));
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return key;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/*
Packit 67b98c
 * grl_registry_register_metadata_key_system:
Packit 67b98c
 *
Packit 67b98c
 * This is an internal method only meant to be used to register core
Packit 67b98c
 * keys.
Packit 67b98c
 *
Packit 67b98c
 * For internal use. Plugin developers should use
Packit 67b98c
 * grl_registry_register_metadata_key().
Packit 67b98c
 */
Packit 67b98c
GrlKeyID
Packit 67b98c
grl_registry_register_metadata_key_system (GrlRegistry *registry,
Packit 67b98c
                                           GParamSpec *param_spec,
Packit 67b98c
                                           GrlKeyID key,
Packit 67b98c
                                           GrlKeyID bind_key,
Packit 67b98c
                                           GError **error)
Packit 67b98c
{
Packit 67b98c
  GrlKeyID registered_key;
Packit 67b98c
Packit 67b98c
  registered_key = grl_registry_register_metadata_key_full (registry,
Packit 67b98c
                                                            param_spec,
Packit 67b98c
                                                            key,
Packit 67b98c
                                                            bind_key,
Packit 67b98c
                                                            error);
Packit 67b98c
Packit 67b98c
  return registered_key;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_metadata_key:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @key_name: the key name
Packit 67b98c
 *
Packit 67b98c
 * Look up for the metadata key with name @key_name.
Packit 67b98c
 *
Packit 67b98c
 * Returns: The metadata key, or GRL_METADATA_KEY_INVALID if not found
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
GrlKeyID
Packit 67b98c
grl_registry_lookup_metadata_key (GrlRegistry *registry,
Packit 67b98c
                                  const gchar *key_name)
Packit 67b98c
{
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
Packit 67b98c
  g_return_val_if_fail (key_name, 0);
Packit 67b98c
Packit 67b98c
  return key_id_handler_get_key (&registry->priv->key_id_handler, key_name);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_metadata_key_name:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @key: a metadata key
Packit 67b98c
 *
Packit 67b98c
 * Returns @key name.
Packit 67b98c
 *
Packit 67b98c
 * Returns: metadata key name, or @NULL if not found
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
const gchar *
Packit 67b98c
grl_registry_lookup_metadata_key_name (GrlRegistry *registry,
Packit 67b98c
                                       GrlKeyID key)
Packit 67b98c
{
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
Packit 67b98c
Packit 67b98c
  return key_id_handler_get_name (&registry->priv->key_id_handler, key);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_metadata_key_desc:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @key: a metadata key
Packit 67b98c
 *
Packit 67b98c
 * Returns @key description.
Packit 67b98c
 *
Packit 67b98c
 * Returns: metadata key description, or @NULL if not found
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
const gchar *
Packit 67b98c
grl_registry_lookup_metadata_key_desc (GrlRegistry *registry,
Packit 67b98c
                                       GrlKeyID key)
Packit 67b98c
{
Packit 67b98c
  const gchar *key_name;
Packit 67b98c
  GParamSpec *key_pspec;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
Packit 67b98c
Packit 67b98c
  key_name = key_id_handler_get_name (&registry->priv->key_id_handler, key);
Packit 67b98c
  if (!key_name) {
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
  key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
Packit 67b98c
Packit 67b98c
  if (key_pspec) {
Packit 67b98c
    return g_param_spec_get_blurb (key_pspec);
Packit 67b98c
  } else {
Packit 67b98c
    return NULL;
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_metadata_key_type:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @key: a metadata key
Packit 67b98c
 *
Packit 67b98c
 * Returns @key expected value type.
Packit 67b98c
 *
Packit 67b98c
 * Returns: metadata key type, or @G_TYPE_INVALID if not found
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
GType
Packit 67b98c
grl_registry_lookup_metadata_key_type (GrlRegistry *registry,
Packit 67b98c
                                       GrlKeyID key)
Packit 67b98c
{
Packit 67b98c
  const gchar *key_name;
Packit 67b98c
  GParamSpec *key_pspec;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), 0);
Packit 67b98c
Packit 67b98c
  key_name = key_id_handler_get_name (&registry->priv->key_id_handler, key);
Packit 67b98c
  if (!key_name) {
Packit 67b98c
    return G_TYPE_INVALID;
Packit 67b98c
  }
Packit 67b98c
  key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
Packit 67b98c
Packit 67b98c
  if (key_pspec) {
Packit 67b98c
    return G_PARAM_SPEC_VALUE_TYPE (key_pspec);
Packit 67b98c
  } else {
Packit 67b98c
    return G_TYPE_INVALID;
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_metadata_key_validate:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @key: a metadata key
Packit 67b98c
 * @value: value to be validate
Packit 67b98c
 *
Packit 67b98c
 * Validates @value content complies with the key specification. That is, it has
Packit 67b98c
 * the expected type, and value are within the range specified in key (for
Packit 67b98c
 * integer values).
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE if complies
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_metadata_key_validate (GrlRegistry *registry,
Packit 67b98c
                                    GrlKeyID key,
Packit 67b98c
                                    GValue *value)
Packit 67b98c
{
Packit 67b98c
  const gchar *key_name;
Packit 67b98c
  GParamSpec *key_pspec;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (G_IS_VALUE (value), FALSE);
Packit 67b98c
Packit 67b98c
  key_name = key_id_handler_get_name (&registry->priv->key_id_handler, key);
Packit 67b98c
  if (!key_name) {
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
  key_pspec = g_hash_table_lookup (registry->priv->system_keys, key_name);
Packit 67b98c
Packit 67b98c
  if (key_pspec) {
Packit 67b98c
    return !g_param_value_validate (key_pspec, value);
Packit 67b98c
  } else {
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_lookup_metadata_key_relation:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @key: a metadata key
Packit 67b98c
 *
Packit 67b98c
 * Look up the list of keys that have a relation with @key.
Packit 67b98c
 *
Packit 67b98c
 * @key is included in that list.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (element-type GrlKeyID) (transfer none): a #GList of
Packit 67b98c
 * related keys, or @NULL if key is invalid.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
const GList *
Packit 67b98c
grl_registry_lookup_metadata_key_relation (GrlRegistry *registry,
Packit 67b98c
                                           GrlKeyID key)
Packit 67b98c
{
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), NULL);
Packit 67b98c
Packit 67b98c
  return g_hash_table_lookup (registry->priv->related_keys, GRLKEYID_TO_POINTER (key));
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_get_metadata_keys:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 *
Packit 67b98c
 * Returns a list with all registered keys in system.
Packit 67b98c
 *
Packit 67b98c
 * Returns: (transfer container) (element-type GrlKeyID): a #GList with all the available
Packit 67b98c
 * #GrlKeyIDs. The content of the list should not be modified or freed.
Packit 67b98c
 * Use g_list_free() when done using the list.
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
GList *
Packit 67b98c
grl_registry_get_metadata_keys (GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  return key_id_handler_get_all_keys (&registry->priv->key_id_handler);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_add_config:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @config: (transfer full): a configuration set
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Add a configuration for a plugin/source.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE on success
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 */
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_add_config (GrlRegistry *registry,
Packit 67b98c
                         GrlConfig *config,
Packit 67b98c
                         GError **error)
Packit 67b98c
{
Packit 67b98c
  gchar *plugin_id;
Packit 67b98c
  GList *configs = NULL;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (config != NULL, FALSE);
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
Packit 67b98c
  plugin_id = grl_config_get_plugin (config);
Packit 67b98c
  if (!plugin_id) {
Packit 67b98c
    GRL_WARNING ("Plugin configuration missed plugin information, ignoring...");
Packit 67b98c
    g_set_error (error,
Packit 67b98c
                 GRL_CORE_ERROR,
Packit 67b98c
                 GRL_CORE_ERROR_CONFIG_FAILED,
Packit 67b98c
                 _("Plugin configuration does not contain “plugin-id” reference"));
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  configs = g_hash_table_lookup (registry->priv->configs, plugin_id);
Packit 67b98c
  if (configs) {
Packit 67b98c
    /* Notice that we are using g_list_append on purpose to avoid
Packit 67b98c
       having to insert again in the hash table */
Packit 67b98c
    configs = g_list_append (configs, config);
Packit 67b98c
    g_free (plugin_id);
Packit 67b98c
  } else {
Packit 67b98c
    configs = g_list_prepend (configs, config);
Packit 67b98c
    g_hash_table_insert (registry->priv->configs,
Packit 67b98c
			 (gpointer) plugin_id,
Packit 67b98c
			 configs);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
  return TRUE;
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
static void
Packit 67b98c
add_config_from_keyfile (GKeyFile    *keyfile,
Packit 67b98c
			 GrlRegistry *registry)
Packit 67b98c
{
Packit 67b98c
  GrlConfig *config;
Packit 67b98c
  gchar **key;
Packit 67b98c
  gchar **keys;
Packit 67b98c
  gchar **plugin;
Packit 67b98c
  gchar **plugins;
Packit 67b98c
  gchar *value;
Packit 67b98c
Packit 67b98c
  /* Look up for defined plugins */
Packit 67b98c
  plugins = g_key_file_get_groups (keyfile, NULL);
Packit 67b98c
  for (plugin = plugins; *plugin; plugin++) {
Packit 67b98c
    config = grl_config_new (*plugin, NULL);
Packit 67b98c
Packit 67b98c
    /* Look up configuration keys for this plugin */
Packit 67b98c
    keys = g_key_file_get_keys (keyfile, *plugin, NULL, NULL);
Packit 67b98c
    for (key = keys; *key; key++) {
Packit 67b98c
      value = g_key_file_get_string (keyfile, *plugin, *key, NULL);
Packit 67b98c
      if (value) {
Packit 67b98c
        grl_config_set_string (config, *key, value);
Packit 67b98c
        g_free (value);
Packit 67b98c
      }
Packit 67b98c
    }
Packit 67b98c
    grl_registry_add_config (registry, config, NULL);
Packit 67b98c
    g_strfreev (keys);
Packit 67b98c
  }
Packit 67b98c
  g_strfreev (plugins);
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_add_config_from_file:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @config_file: a key-value file containing the configuration
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Load plugin configurations from a .ini-like config file.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE on success
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.0
Packit 67b98c
 **/
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_add_config_from_file (GrlRegistry *registry,
Packit 67b98c
                                   const gchar *config_file,
Packit 67b98c
                                   GError **error)
Packit 67b98c
{
Packit 67b98c
  GError *load_error = NULL;
Packit 67b98c
  GKeyFile *keyfile;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (config_file, FALSE);
Packit 67b98c
Packit 67b98c
  keyfile = g_key_file_new ();
Packit 67b98c
Packit 67b98c
  if (g_key_file_load_from_file (keyfile,
Packit 67b98c
                                 config_file,
Packit 67b98c
                                 G_KEY_FILE_NONE,
Packit 67b98c
                                 &load_error)) {
Packit 67b98c
    add_config_from_keyfile (keyfile, registry);
Packit 67b98c
    g_key_file_free (keyfile);
Packit 67b98c
    return TRUE;
Packit 67b98c
  } else {
Packit 67b98c
    GRL_WARNING ("Unable to load configuration. %s", load_error->message);
Packit 67b98c
    g_set_error_literal (error,
Packit 67b98c
                         GRL_CORE_ERROR,
Packit 67b98c
                         GRL_CORE_ERROR_CONFIG_LOAD_FAILED,
Packit 67b98c
                         load_error->message);
Packit 67b98c
    g_error_free (load_error);
Packit 67b98c
    g_key_file_free (keyfile);
Packit 67b98c
    return FALSE;
Packit 67b98c
  }
Packit 67b98c
}
Packit 67b98c
Packit 67b98c
/**
Packit 67b98c
 * grl_registry_add_config_from_resource:
Packit 67b98c
 * @registry: the registry instance
Packit 67b98c
 * @resource_path: a key-value file containing the configuration
Packit 67b98c
 * @error: error return location or @NULL to ignore
Packit 67b98c
 *
Packit 67b98c
 * Load plugin configurations from a .ini-like resource file.
Packit 67b98c
 *
Packit 67b98c
 * Returns: %TRUE on success
Packit 67b98c
 *
Packit 67b98c
 * Since: 0.2.8
Packit 67b98c
 **/
Packit 67b98c
gboolean
Packit 67b98c
grl_registry_add_config_from_resource (GrlRegistry *registry,
Packit 67b98c
                                       const gchar *resource_path,
Packit 67b98c
                                       GError **error)
Packit 67b98c
{
Packit 67b98c
  GError *load_error = NULL;
Packit 67b98c
  GKeyFile *keyfile = NULL;
Packit 67b98c
  GBytes *bytes;
Packit 67b98c
  gboolean ret = FALSE;
Packit 67b98c
Packit 67b98c
  g_return_val_if_fail (GRL_IS_REGISTRY (registry), FALSE);
Packit 67b98c
  g_return_val_if_fail (resource_path, FALSE);
Packit 67b98c
Packit 67b98c
  bytes = g_resources_lookup_data (resource_path, 0, error);
Packit 67b98c
  if (bytes == NULL)
Packit 67b98c
    goto bail;
Packit 67b98c
Packit 67b98c
  keyfile = g_key_file_new ();
Packit 67b98c
Packit 67b98c
  if (g_key_file_load_from_data (keyfile,
Packit 67b98c
                                 g_bytes_get_data (bytes, NULL),
Packit 67b98c
                                 g_bytes_get_size (bytes),
Packit 67b98c
                                 G_KEY_FILE_NONE,
Packit 67b98c
                                 &load_error)) {
Packit 67b98c
    add_config_from_keyfile (keyfile, registry);
Packit 67b98c
    ret = TRUE;
Packit 67b98c
  } else {
Packit 67b98c
    GRL_WARNING ("Unable to load configuration. %s", load_error->message);
Packit 67b98c
    g_set_error_literal (error,
Packit 67b98c
                         GRL_CORE_ERROR,
Packit 67b98c
                         GRL_CORE_ERROR_CONFIG_LOAD_FAILED,
Packit 67b98c
                         load_error->message);
Packit 67b98c
    g_error_free (load_error);
Packit 67b98c
  }
Packit 67b98c
Packit 67b98c
bail:
Packit 67b98c
  g_clear_pointer (&keyfile, g_key_file_free);
Packit 67b98c
  g_clear_pointer (&bytes, g_bytes_unref);
Packit 67b98c
Packit 67b98c
  return ret;
Packit 67b98c
}