1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright (c) 2013 Canonical Limited
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Written by: Ryan Lortie <desrt@desrt.ca>
*/
#include "config.h"
#include "daemon.h"
#include <gio/gio.h>
#include <string.h>
static void
daemon_maybe_add_extension_interface (GHashTable *ifaces,
GDBusInterfaceInfo *iface)
{
gint i;
/* We visit the XDG data dirs in precedence order, so if we
* already have this one, we should not add it again.
*/
if (g_hash_table_contains (ifaces, iface->name))
return;
/* Only accept interfaces with only properties */
if ((iface->methods && iface->methods[0]) || (iface->signals && iface->signals[0]))
return;
/* Add it only if we can find the annotation */
for (i = 0; iface->annotations && iface->annotations[i]; i++) {
if (g_str_equal (iface->annotations[i]->key, "org.freedesktop.Accounts.VendorExtension")) {
g_hash_table_insert (ifaces, g_strdup (iface->name), g_dbus_interface_info_ref (iface));
return;
}
}
}
static void
daemon_read_extension_file (GHashTable *ifaces,
const gchar *filename)
{
GError *error = NULL;
GDBusNodeInfo *node;
gchar *contents;
gint i;
if (!g_file_get_contents (filename, &contents, NULL, &error)) {
g_warning ("Unable to read extension file %s: %s. Ignoring.", filename, error->message);
g_error_free (error);
return;
}
node = g_dbus_node_info_new_for_xml (contents, &error);
if (node) {
for (i = 0; node->interfaces && node->interfaces[i]; i++)
daemon_maybe_add_extension_interface (ifaces, node->interfaces[i]);
g_dbus_node_info_unref (node);
} else {
g_warning ("Failed to parse file %s: %s", filename, error->message);
g_error_free (error);
}
g_free (contents);
}
static void
daemon_read_extension_directory (GHashTable *ifaces,
const gchar *path)
{
const gchar *name;
GDir *dir;
dir = g_dir_open (path, 0, NULL);
if (!dir)
return;
while ((name = g_dir_read_name (dir))) {
gchar *filename;
gchar *symlink;
/* Extensions are installed as normal D-Bus interface
* files with an annotation.
*
* D-Bus interface files are supposed to be installed in
* $(datadir)/dbus-1/interfaces/ but we don't want to
* scan all of the files there looking for interfaces
* that may contain our annotation.
*
* The solution is to install a symlink into a directory
* private to accountsservice and point it at the file,
* as installed in the usual D-Bus directory.
*
* This ensures compatibility with the future if we ever
* decide to check the interfaces directory directly
* (which might be a reasonable thing to do if we ever
* get an efficient cache of the contents of this
* directory).
*
* By introducing such a restrictive way of doing this
* now we ensure that everyone will do it in this
* forwards-compatible way.
*/
filename = g_build_filename (path, name, NULL);
symlink = g_file_read_link (filename, NULL);
if (!symlink) {
g_warning ("Found accounts service vendor extension file %s, but file must be a symlink to "
"'../../dbus-1/interfaces/%s' for forwards-compatibility reasons.", filename, name);
g_free (filename);
continue;
}
/* Ensure it looks like "../../dbus-1/interfaces/${name}" */
const gchar * const prefix = "../../dbus-1/interfaces/";
if (g_str_has_prefix (symlink, prefix) && g_str_equal (symlink + strlen (prefix), name)) {
daemon_read_extension_file (ifaces, filename);
}
else {
g_warning ("Found accounts service vendor extension symlink %s, but it must be exactly "
"equal to '../../dbus-1/interfaces/%s' for forwards-compatibility reasons.",
filename, name);
}
g_free (filename);
g_free (symlink);
}
g_dir_close (dir);
}
GHashTable *
daemon_read_extension_ifaces (void)
{
const gchar * const *data_dirs;
GHashTable *ifaces;
gint i;
ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_dbus_interface_info_unref);
data_dirs = g_get_system_data_dirs ();
for (i = 0; data_dirs[i]; i++) {
gchar *path = g_build_filename (data_dirs[i], "accountsservice/interfaces", NULL);
daemon_read_extension_directory (ifaces, path);
g_free (path);
}
return ifaces;
}
|