Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2011 Collabora Ltd.
5 : : *
6 : : * This program is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU Lesser General Public License as
8 : : * published by the Free Software Foundation; either version 2.1 of
9 : : * the License, or (at your option) any later version.
10 : : *
11 : : * This program is distributed in the hope that it will be useful, but
12 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : : * Lesser General Public License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public
17 : : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : : *
19 : : * Author: Stef Walter <stefw@collabora.co.uk>
20 : : */
21 : :
22 : : #include "config.h"
23 : :
24 : : #include "gcr-importer.h"
25 : : #include "gcr-internal.h"
26 : : #include "gcr-gnupg-importer.h"
27 : : #include "gcr-parser.h"
28 : : #include "gcr-pkcs11-importer.h"
29 : :
30 : : #include "gcr/gcr-marshal.h"
31 : :
32 : : #include <glib/gi18n-lib.h>
33 : :
34 : : /**
35 : : * GcrImporter:
36 : : *
37 : : * An interface which allows importing of certificates and keys. Each importer
38 : : * is registered with a set of PKCS#11 attributes to match stuff that it can
39 : : * import.
40 : : *
41 : : * An importer gets passed a [class@Parser] and accesses the currently parsed
42 : : * item. To create a set of importers that can import the currently parsed
43 : : * item in a parser, use [func@Importer.create_for_parsed]. The list of
44 : : * importers returned has the parsed item queued for import.
45 : : *
46 : : * To queue additional items with a importer use
47 : : * [method@Importer.queue_for_parsed]. In addition you can try and queue an
48 : : * additional item with a set of importers using the
49 : : * [func@Importer.queue_and_filter_for_parsed].
50 : : *
51 : : * To start the import, use [method@Importer.import_async].
52 : : */
53 : :
54 : : /**
55 : : * GcrImporterInterface:
56 : : * @parent: parent interface
57 : : * @create_for_parsed: implementation of gcr_importer_create_for_parsed(), required
58 : : * @queue_for_parsed: implementation of gcr_importer_queue_for_parsed(), required
59 : : * @import_async: implementation of [method@Importer.import_async], required
60 : : * @import_finish: implementation of [method@Importer.import_finish]
61 : : *
62 : : * Interface implemented for a #GcrImporter.
63 : : */
64 : :
65 [ # # # # : 0 : G_DEFINE_INTERFACE (GcrImporter, gcr_importer, G_TYPE_OBJECT);
# # ]
66 : :
67 : : typedef struct _GcrRegistered {
68 : : GckAttributes *attrs;
69 : : GType importer_type;
70 : : } GcrRegistered;
71 : :
72 : : static GArray *registered_importers = NULL;
73 : : static gboolean registered_sorted = FALSE;
74 : :
75 : : static void
76 : 0 : gcr_importer_default_init (GcrImporterInterface *iface)
77 : : {
78 : : static size_t initialized = 0;
79 : :
80 [ # # # # : 0 : if (g_once_init_enter (&initialized)) {
# # ]
81 : :
82 : : /**
83 : : * GcrImporter:label:
84 : : *
85 : : * The label for the importer.
86 : : */
87 : 0 : g_object_interface_install_property (iface,
88 : : g_param_spec_string ("label", "Label", "The label for the importer",
89 : : "",
90 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
91 : :
92 : : /**
93 : : * GcrImporter:interaction:
94 : : *
95 : : * The interaction for the importer.
96 : : */
97 : 0 : g_object_interface_install_property (iface,
98 : : g_param_spec_object ("interaction", "Interaction",
99 : : "Interaction for prompts",
100 : : G_TYPE_TLS_INTERACTION,
101 : : G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
102 : :
103 : : /**
104 : : * GcrImporter:uri:
105 : : *
106 : : * The URI of the location imported to.
107 : : */
108 : 0 : g_object_interface_install_property (iface,
109 : : g_param_spec_string ("uri", "URI", "URI of location",
110 : : NULL,
111 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
112 : :
113 : 0 : g_once_init_leave (&initialized, 1);
114 : : }
115 : 0 : }
116 : :
117 : : /**
118 : : * gcr_importer_register:
119 : : * @importer_type: the GType of the importer being registered
120 : : * @attrs: (transfer full): the attributes that this importer is compatible with
121 : : *
122 : : * Register an importer to handle parsed items that match the given attributes.
123 : : */
124 : : void
125 : 0 : gcr_importer_register (GType importer_type,
126 : : GckAttributes *attrs)
127 : : {
128 : : GcrRegistered registered;
129 : :
130 [ # # ]: 0 : if (!registered_importers)
131 : 0 : registered_importers = g_array_new (FALSE, FALSE, sizeof (GcrRegistered));
132 : :
133 : 0 : registered.importer_type = importer_type;
134 : 0 : registered.attrs = attrs;
135 : 0 : g_array_append_val (registered_importers, registered);
136 : 0 : registered_sorted = FALSE;
137 : 0 : }
138 : :
139 : : static gint
140 : 0 : sort_registered_by_n_attrs (gconstpointer a, gconstpointer b)
141 : : {
142 : 0 : const GcrRegistered *ra = a;
143 : 0 : const GcrRegistered *rb = b;
144 : : gulong na, nb;
145 : :
146 [ # # ]: 0 : g_assert (a);
147 [ # # ]: 0 : g_assert (b);
148 : :
149 : 0 : na = gck_attributes_count (ra->attrs);
150 : 0 : nb = gck_attributes_count (rb->attrs);
151 : :
152 : : /* Note we're sorting in reverse order */
153 [ # # ]: 0 : if (na < nb)
154 : 0 : return 1;
155 [ # # ]: 0 : return (na == nb) ? 0 : -1;
156 : : }
157 : :
158 : : static gboolean
159 : 0 : check_if_seen_or_add (GHashTable *seen,
160 : : gpointer key)
161 : : {
162 [ # # ]: 0 : if (g_hash_table_lookup (seen, key))
163 : 0 : return TRUE;
164 : 0 : g_hash_table_insert (seen, key, key);
165 : 0 : return FALSE;
166 : : }
167 : :
168 : : /**
169 : : * gcr_importer_create_for_parsed:
170 : : * @parsed: a parser with a parsed item to import
171 : : *
172 : : * Create a set of importers which can import this parsed item.
173 : : * The parsed item is represented by the state of the GcrParser at the
174 : : * time of calling this method.
175 : : *
176 : : * Returns: (element-type Gcr.Importer) (transfer full): a list of importers
177 : : * which can import the parsed item, which should be freed with
178 : : * g_object_unref(), or %NULL if no types of importers can be created
179 : : */
180 : : GList *
181 : 0 : gcr_importer_create_for_parsed (GcrParsed *parsed)
182 : : {
183 : : GcrRegistered *registered;
184 : : GcrImporterInterface *iface;
185 : : gpointer instance_class;
186 : : GckAttributes *attrs;
187 : : gboolean matched;
188 : : gulong n_attrs;
189 : 0 : GList *results = NULL;
190 : : GHashTable *seen;
191 : : gulong j;
192 : : gsize i;
193 : :
194 [ # # ]: 0 : g_return_val_if_fail (parsed != NULL, NULL);
195 : :
196 : 0 : gcr_importer_register_well_known ();
197 : :
198 [ # # ]: 0 : if (!registered_importers)
199 : 0 : return NULL;
200 : :
201 [ # # ]: 0 : if (!registered_sorted) {
202 : 0 : g_array_sort (registered_importers, sort_registered_by_n_attrs);
203 : 0 : registered_sorted = TRUE;
204 : : }
205 : :
206 : 0 : attrs = gcr_parsed_get_attributes (parsed);
207 [ # # ]: 0 : if (attrs != NULL)
208 : 0 : gck_attributes_ref (attrs);
209 : : else
210 : 0 : attrs = gck_attributes_new_empty (GCK_INVALID);
211 : :
212 : 0 : seen = g_hash_table_new (g_direct_hash, g_direct_equal);
213 : :
214 : 0 : gchar *a = gck_attributes_to_string (attrs);
215 : 0 : g_debug ("looking for importer for: %s", a);
216 : 0 : g_free (a);
217 : :
218 [ # # ]: 0 : for (i = 0; i < registered_importers->len; ++i) {
219 : 0 : registered = &(g_array_index (registered_importers, GcrRegistered, i));
220 : 0 : n_attrs = gck_attributes_count (registered->attrs);
221 : :
222 : 0 : matched = TRUE;
223 : :
224 [ # # ]: 0 : for (j = 0; j < n_attrs; ++j) {
225 [ # # ]: 0 : if (!gck_attributes_contains (attrs, gck_attributes_at (registered->attrs, j))) {
226 : 0 : matched = FALSE;
227 : 0 : break;
228 : : }
229 : : }
230 : :
231 : 0 : gchar *a = gck_attributes_to_string (registered->attrs);
232 [ # # ]: 0 : g_debug ("importer %s %s: %s", g_type_name (registered->importer_type),
233 : : matched ? "matched" : "didn't match", a);
234 : 0 : g_free (a);
235 : :
236 [ # # ]: 0 : if (matched) {
237 [ # # ]: 0 : if (check_if_seen_or_add (seen, GUINT_TO_POINTER (registered->importer_type)))
238 : 0 : continue;
239 : :
240 : 0 : instance_class = g_type_class_ref (registered->importer_type);
241 : :
242 : 0 : iface = g_type_interface_peek (instance_class, GCR_TYPE_IMPORTER);
243 [ # # ]: 0 : g_return_val_if_fail (iface != NULL, NULL);
244 [ # # ]: 0 : g_return_val_if_fail (iface->create_for_parsed, NULL);
245 : 0 : results = g_list_concat (results, (iface->create_for_parsed) (parsed));
246 : :
247 : 0 : g_type_class_unref (instance_class);
248 : : }
249 : : }
250 : :
251 : 0 : g_hash_table_unref (seen);
252 : 0 : gck_attributes_unref (attrs);
253 : 0 : return results;
254 : : }
255 : :
256 : : /**
257 : : * gcr_importer_queue_for_parsed:
258 : : * @importer: an importer to add additional items to
259 : : * @parsed: a parsed item to import
260 : : *
261 : : * Queues an additional item to be imported. The parsed item is represented
262 : : * by the state of the [class@Parser] at the time of calling this method.
263 : : *
264 : : * If the parsed item is incompatible with the importer, then this will
265 : : * fail and the item will not be queued.
266 : : *
267 : : * Returns: whether the item was queued or not
268 : : */
269 : : gboolean
270 : 0 : gcr_importer_queue_for_parsed (GcrImporter *importer,
271 : : GcrParsed *parsed)
272 : : {
273 : : GcrImporterInterface *iface;
274 : :
275 [ # # ]: 0 : g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE);
276 [ # # ]: 0 : g_return_val_if_fail (parsed != NULL, FALSE);
277 : :
278 : 0 : iface = GCR_IMPORTER_GET_IFACE (importer);
279 [ # # ]: 0 : g_return_val_if_fail (iface != NULL, FALSE);
280 [ # # ]: 0 : g_return_val_if_fail (iface->queue_for_parsed != NULL, FALSE);
281 : :
282 : 0 : return (iface->queue_for_parsed) (importer, parsed);
283 : : }
284 : :
285 : : /**
286 : : * gcr_importer_queue_and_filter_for_parsed:
287 : : * @importers: (element-type Gcr.Importer): a set of importers
288 : : * @parsed: a parsed item
289 : : *
290 : : * Queues an additional item to be imported in all compattible importers
291 : : * in the set. The parsed item is represented by the state of the #GcrParser
292 : : * at the time of calling this method.
293 : : *
294 : : * If the parsed item is incompatible with an importer, then that the item
295 : : * will not be queued on that importer.
296 : : *
297 : : * Returns: (transfer full) (element-type Gcr.Importer): a new set of importers
298 : : * that queued the item.
299 : : */
300 : : GList *
301 : 0 : gcr_importer_queue_and_filter_for_parsed (GList *importers,
302 : : GcrParsed *parsed)
303 : : {
304 : 0 : GList *results = NULL;
305 : : GList *l;
306 : :
307 [ # # # # ]: 0 : for (l = importers; l != NULL; l = g_list_next (l)) {
308 [ # # ]: 0 : if (gcr_importer_queue_for_parsed (l->data, parsed))
309 : 0 : results = g_list_prepend (results, g_object_ref (l->data));
310 : : }
311 : :
312 : 0 : return g_list_reverse (results);
313 : : }
314 : :
315 : : /**
316 : : * gcr_importer_import_async:
317 : : * @importer: the importer
318 : : * @cancellable: a #GCancellable, or %NULL
319 : : * @callback: called when the operation completes
320 : : * @user_data: data to be passed to the callback
321 : : *
322 : : * Import the queued items in the importer. This function returns immediately
323 : : * and completes asynchronously.
324 : : */
325 : : void
326 : 0 : gcr_importer_import_async (GcrImporter *importer,
327 : : GCancellable *cancellable,
328 : : GAsyncReadyCallback callback,
329 : : gpointer user_data)
330 : : {
331 : : GcrImporterInterface *iface;
332 : :
333 [ # # ]: 0 : g_return_if_fail (GCR_IS_IMPORTER (importer));
334 [ # # # # : 0 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
# # # # #
# ]
335 : :
336 : 0 : iface = GCR_IMPORTER_GET_IFACE (importer);
337 [ # # ]: 0 : g_return_if_fail (iface != NULL);
338 [ # # ]: 0 : g_return_if_fail (iface->import_async != NULL);
339 : :
340 : 0 : return (iface->import_async) (importer, cancellable, callback, user_data);
341 : : }
342 : :
343 : : /**
344 : : * gcr_importer_import_finish:
345 : : * @importer: the importer
346 : : * @result: an asynchronous result
347 : : * @error: the location to place an error on failure, or %NULL
348 : : *
349 : : * Complete an asynchronous operation to import queued items.
350 : : *
351 : : * Returns: whether the import succeeded or failed
352 : : */
353 : : gboolean
354 : 0 : gcr_importer_import_finish (GcrImporter *importer,
355 : : GAsyncResult *result,
356 : : GError **error)
357 : : {
358 : : GcrImporterInterface *iface;
359 : :
360 [ # # ]: 0 : g_return_val_if_fail (GCR_IS_IMPORTER (importer), FALSE);
361 [ # # # # : 0 : g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
# # # # ]
362 [ # # # # ]: 0 : g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
363 : :
364 : 0 : iface = GCR_IMPORTER_GET_IFACE (importer);
365 [ # # ]: 0 : g_return_val_if_fail (iface != NULL, FALSE);
366 [ # # ]: 0 : g_return_val_if_fail (iface->import_finish != NULL, FALSE);
367 : :
368 : 0 : return (iface->import_finish) (importer, result, error);
369 : : }
370 : :
371 : : /**
372 : : * gcr_importer_get_interaction:
373 : : * @importer: the importer
374 : : *
375 : : * Get the interaction used to prompt the user when needed by this
376 : : * importer.
377 : : *
378 : : * Returns: (transfer none) (nullable): the interaction or %NULL
379 : : */
380 : : GTlsInteraction *
381 : 0 : gcr_importer_get_interaction (GcrImporter *importer)
382 : : {
383 : 0 : GTlsInteraction *interaction = NULL;
384 : :
385 [ # # ]: 0 : g_return_val_if_fail (GCR_IS_IMPORTER (importer), NULL);
386 : :
387 : 0 : g_object_get (importer, "interaction", &interaction, NULL);
388 : :
389 [ # # ]: 0 : if (interaction != NULL)
390 : 0 : g_object_unref (interaction);
391 : :
392 : 0 : return interaction;
393 : : }
394 : :
395 : : /**
396 : : * gcr_importer_set_interaction:
397 : : * @importer: the importer
398 : : * @interaction: the interaction used by the importer
399 : : *
400 : : * Set the interaction used to prompt the user when needed by this
401 : : * importer.
402 : : */
403 : : void
404 : 0 : gcr_importer_set_interaction (GcrImporter *importer,
405 : : GTlsInteraction *interaction)
406 : : {
407 [ # # ]: 0 : g_return_if_fail (GCR_IS_IMPORTER (importer));
408 : 0 : g_object_set (importer, "interaction", interaction, NULL);
409 : : }
410 : :
411 : : /**
412 : : * gcr_importer_register_well_known:
413 : : *
414 : : * Register built-in PKCS#11 and GnuPG importers.
415 : : */
416 : : void
417 : 0 : gcr_importer_register_well_known (void)
418 : : {
419 : 0 : g_type_class_unref (g_type_class_ref (GCR_TYPE_PKCS11_IMPORTER));
420 : 0 : g_type_class_unref (g_type_class_ref (GCR_TYPE_GNUPG_IMPORTER));
421 : 0 : }
|