Branch data Line data Source code
1 : : /* libsecret - GLib wrapper for Secret Service
2 : : *
3 : : * Copyright 2012 Red Hat Inc.
4 : : *
5 : : * This program is free software: you can redistribute it and/or modify
6 : : * it under the terms of the GNU Lesser General Public License as published
7 : : * by the Free Software Foundation; either version 2.1 of the licence or (at
8 : : * your option) any later version.
9 : : *
10 : : * See the included COPYING file for more information.
11 : : *
12 : : * Author: Stef Walter <stefw@gnome.org>
13 : : */
14 : :
15 : : #include "config.h"
16 : :
17 : : #include "libsecret/secret-item.h"
18 : : #include "libsecret/secret-password.h"
19 : : #include "libsecret/secret-retrievable.h"
20 : : #include "libsecret/secret-value.h"
21 : : #include "libsecret/secret-service.h"
22 : : #include "libsecret/secret-paths.h"
23 : :
24 : : #include <glib/gi18n.h>
25 : :
26 : : #include <errno.h>
27 : : #include <locale.h>
28 : : #include <limits.h>
29 : : #include <stdlib.h>
30 : : #include <string.h>
31 : :
32 : : #define SECRET_ALIAS_PREFIX "/org/freedesktop/secrets/aliases/"
33 : : #define SECRET_COLLECTION_PREFIX "/org/freedesktop/secrets/collection/"
34 : :
35 : : static gchar **attribute_args = NULL;
36 : : static gchar *store_label = NULL;
37 : : static gchar *store_collection = NULL;
38 : :
39 : : /* secret-tool store --label="blah" --collection="xxxx" name:xxxx name:yyyy */
40 : : static const GOptionEntry STORE_OPTIONS[] = {
41 : : { "label", 'l', 0, G_OPTION_ARG_STRING, &store_label,
42 : : N_("the label for the new stored item"), NULL },
43 : : { "collection", 'c', 0, G_OPTION_ARG_STRING, &store_collection,
44 : : N_("the collection in which to place the stored item"), NULL },
45 : : { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
46 : : N_("attribute value pairs of item to lookup"), NULL },
47 : : { NULL }
48 : : };
49 : :
50 : : /* secret-tool lookup name:xxxx yyyy:zzzz */
51 : : static const GOptionEntry LOOKUP_OPTIONS[] = {
52 : : { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
53 : : N_("attribute value pairs of item to lookup"), NULL },
54 : : { NULL }
55 : : };
56 : :
57 : : /* secret-tool clear name:xxxx yyyy:zzzz */
58 : : static const GOptionEntry CLEAR_OPTIONS[] = {
59 : : { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
60 : : N_("attribute value pairs which match items to clear"), NULL },
61 : : { NULL }
62 : : };
63 : :
64 : : /* secret-tool lock collections="xxxx" */
65 : : static const GOptionEntry COLLECTION_OPTIONS[] = {
66 : : { "collection", 'c', 0, G_OPTION_ARG_STRING, &store_collection,
67 : : N_("collection in which to lock"), NULL },
68 : : { NULL }
69 : : };
70 : :
71 : : typedef int (* SecretToolAction) (int argc, char *argv[]);
72 : :
73 : : static void usage (void) G_GNUC_NORETURN;
74 : :
75 : : static void
76 : 0 : usage (void)
77 : : {
78 : 0 : g_printerr ("usage: secret-tool store --label='label' attribute value ...\n");
79 : 0 : g_printerr (" secret-tool lookup attribute value ...\n");
80 : 0 : g_printerr (" secret-tool clear attribute value ...\n");
81 : 0 : g_printerr (" secret-tool search [--all] [--unlock] attribute value ...\n");
82 : 0 : g_printerr (" secret-tool lock --collection='collection'\n");
83 : 0 : exit (2);
84 : : }
85 : :
86 : : static gboolean
87 : 2 : is_password_value (SecretValue *value)
88 : : {
89 : : const gchar *content_type;
90 : : const gchar *data;
91 : : gsize length;
92 : :
93 : 2 : content_type = secret_value_get_content_type (value);
94 [ + - + - ]: 2 : if (content_type && g_str_equal (content_type, "text/plain"))
95 : 2 : return TRUE;
96 : :
97 : 0 : data = secret_value_get (value, &length);
98 : : /* gnome-keyring-daemon used to return passwords like this, so support this, but validate */
99 [ # # # # ]: 0 : if (!content_type || g_str_equal (content_type, "application/octet-stream"))
100 : 0 : return g_utf8_validate (data, length, NULL);
101 : :
102 : 0 : return FALSE;
103 : : }
104 : :
105 : : static GHashTable *
106 : 12 : attributes_from_arguments (gchar **args)
107 : : {
108 : : GHashTable *attributes;
109 : :
110 [ + - - + ]: 12 : if (args == NULL || args[0] == NULL) {
111 : 0 : g_printerr ("%s: must specify attribute and value pairs\n", g_get_prgname ());
112 : 0 : usage ();
113 : : }
114 : :
115 : 12 : attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
116 : :
117 [ + + ]: 26 : while (args[0] != NULL) {
118 [ - + ]: 14 : if (args[1] == NULL) {
119 : 0 : g_printerr ("%s: must specify attributes and values in pairs\n", g_get_prgname ());
120 : 0 : usage ();
121 : : }
122 : :
123 : 28 : g_hash_table_insert (attributes, g_strdup (args[0]), g_strdup (args[1]));
124 : 14 : args += 2;
125 : : }
126 : :
127 : 12 : return attributes;
128 : : }
129 : :
130 : : static int
131 : 2 : secret_tool_action_clear (int argc,
132 : : char *argv[])
133 : : {
134 : 2 : GError *error = NULL;
135 : : GOptionContext *context;
136 : : GHashTable *attributes;
137 : : gboolean ret;
138 : :
139 : 2 : context = g_option_context_new ("attribute value ...");
140 : 2 : g_option_context_add_main_entries (context, CLEAR_OPTIONS, GETTEXT_PACKAGE);
141 [ - + ]: 2 : if (!g_option_context_parse (context, &argc, &argv, &error)) {
142 : 0 : g_printerr ("%s\n", error->message);
143 : 0 : usage();
144 : : }
145 : :
146 : 2 : g_option_context_free (context);
147 : :
148 : 2 : attributes = attributes_from_arguments (attribute_args);
149 : 2 : g_strfreev (attribute_args);
150 : :
151 : 2 : ret = secret_password_clearv_sync (NULL, attributes, NULL, &error);
152 : :
153 : 2 : g_hash_table_unref (attributes);
154 : :
155 [ - + ]: 2 : if (!ret) {
156 [ # # ]: 0 : if (error != NULL) {
157 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
158 : 0 : g_error_free (error);
159 : : }
160 : 0 : return 1;
161 : : }
162 : :
163 : 2 : return 0;
164 : : }
165 : :
166 : : static void
167 : 8 : write_password_data (SecretValue *value)
168 : : {
169 : : const gchar *at;
170 : : gsize length;
171 : : int r;
172 : :
173 : 8 : at = secret_value_get (value, &length);
174 : :
175 [ + + ]: 16 : while (length > 0) {
176 : 8 : r = write (1, at, length);
177 [ - + ]: 8 : if (r == -1) {
178 [ # # # # ]: 0 : if (errno != EAGAIN && errno != EINTR) {
179 : 0 : g_printerr ("%s: couldn't write password: %s\n",
180 : 0 : g_get_prgname (), g_strerror (errno));
181 : 0 : exit (1);
182 : : }
183 : : } else {
184 : 8 : at += r;
185 : 8 : length -= r;
186 : : }
187 : : }
188 : 8 : }
189 : :
190 : : static void
191 : 2 : write_password_stdout (SecretValue *value)
192 : : {
193 [ - + ]: 2 : if (!is_password_value (value)) {
194 : 0 : g_printerr ("%s: secret does not contain a textual password\n", g_get_prgname ());
195 : 0 : exit (1);
196 : : }
197 : :
198 : 2 : write_password_data (value);
199 : :
200 : : /* Add a new line if we're writing out to a tty */
201 [ - + ]: 2 : if (isatty (1))
202 : 0 : write (1, "\n", 1);
203 : 2 : }
204 : :
205 : : static int
206 : 2 : secret_tool_action_lookup (int argc,
207 : : char *argv[])
208 : : {
209 : 2 : GError *error = NULL;
210 : : GOptionContext *context;
211 : : GHashTable *attributes;
212 : 2 : SecretValue *value = NULL;
213 : :
214 : 2 : context = g_option_context_new ("attribute value ...");
215 : 2 : g_option_context_add_main_entries (context, LOOKUP_OPTIONS, GETTEXT_PACKAGE);
216 [ - + ]: 2 : if (!g_option_context_parse (context, &argc, &argv, &error)) {
217 : 0 : g_printerr ("%s\n", error->message);
218 : 0 : usage();
219 : : }
220 : :
221 : 2 : g_option_context_free (context);
222 : :
223 : 2 : attributes = attributes_from_arguments (attribute_args);
224 : 2 : g_strfreev (attribute_args);
225 : :
226 : 2 : value = secret_password_lookupv_binary_sync (NULL, attributes, NULL, &error);
227 : :
228 : 2 : g_hash_table_unref (attributes);
229 : :
230 [ - + ]: 2 : if (error != NULL) {
231 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
232 : 0 : g_error_free (error);
233 : 0 : return 1;
234 : : }
235 : :
236 [ - + ]: 2 : if (value == NULL)
237 : 0 : return 1;
238 : :
239 : 2 : write_password_stdout (value);
240 : 2 : secret_value_unref (value);
241 : 2 : return 0;
242 : : }
243 : :
244 : : static SecretValue *
245 : 4 : read_password_stdin (void)
246 : : {
247 : : gchar *password;
248 : : gchar *at;
249 : 4 : gsize length = 0;
250 : 4 : gsize remaining = 8192;
251 : : int r;
252 : :
253 : 4 : at = password = g_malloc0 (remaining + 1);
254 : :
255 : : for (;;) {
256 : 8 : r = read (0, at, remaining);
257 [ + + ]: 8 : if (r == 0) {
258 : 4 : break;
259 [ - + ]: 4 : } else if (r < 0) {
260 [ # # # # ]: 0 : if (errno != EAGAIN && errno != EINTR) {
261 : 0 : g_printerr ("%s: couldn't read password: %s\n",
262 : 0 : g_get_prgname (), g_strerror (errno));
263 : 0 : exit (1);
264 : : }
265 : : } else {
266 : : /* TODO: This restriction is due purely to laziness. */
267 [ - + ]: 4 : if (r == remaining)
268 : 0 : g_printerr ("%s: password is too long\n", g_get_prgname ());
269 : 4 : at += r;
270 : 4 : remaining -= r;
271 : 4 : length += r;
272 : : }
273 : : }
274 : :
275 [ + - ]: 4 : if (g_utf8_validate (password, -1, NULL)) {
276 : 4 : return secret_value_new_full (password, length, "text/plain",
277 : : (GDestroyNotify)secret_password_free);
278 : : } else {
279 : 0 : g_printerr ("%s: password not valid UTF-8\n", g_get_prgname ());
280 : 0 : exit (1);
281 : : }
282 : : }
283 : :
284 : : static SecretValue *
285 : 0 : read_password_tty (void)
286 : : {
287 : : gchar *password;
288 : :
289 : 0 : password = getpass ("Password: ");
290 : 0 : return secret_value_new_full (password, -1, "text/plain",
291 : : (GDestroyNotify)secret_password_wipe);
292 : : }
293 : :
294 : : static int
295 : 4 : secret_tool_action_store (int argc,
296 : : char *argv[])
297 : : {
298 : 4 : GError *error = NULL;
299 : : GOptionContext *context;
300 : : GHashTable *attributes;
301 : : SecretValue *value;
302 : 4 : gchar *collection = NULL;
303 : : gboolean ret;
304 : :
305 : 4 : context = g_option_context_new ("attribute value ...");
306 : 4 : g_option_context_add_main_entries (context, STORE_OPTIONS, GETTEXT_PACKAGE);
307 [ - + ]: 4 : if (!g_option_context_parse (context, &argc, &argv, &error)) {
308 : 0 : g_printerr ("%s\n", error->message);
309 : 0 : usage();
310 : : }
311 : :
312 : 4 : g_option_context_free (context);
313 : :
314 [ - + ]: 4 : if (store_label == NULL) {
315 : 0 : g_printerr ("%s: must specify a label for the new item\n", g_get_prgname ());
316 : 0 : usage ();
317 : : }
318 : :
319 : 4 : attributes = attributes_from_arguments (attribute_args);
320 : 4 : g_strfreev (attribute_args);
321 : :
322 [ - + ]: 4 : if (store_collection) {
323 : : /* TODO: Verify that the collection is a valid path or path element */
324 [ # # # # : 0 : if (g_str_has_prefix (store_collection, "/"))
# # # # ]
325 : 0 : collection = g_strdup (store_collection);
326 : : else
327 : 0 : collection = g_strconcat (SECRET_ALIAS_PREFIX, store_collection, NULL);
328 : : }
329 : :
330 [ - + ]: 4 : if (isatty (0))
331 : 0 : value = read_password_tty ();
332 : : else
333 : 4 : value = read_password_stdin ();
334 : :
335 : 4 : ret = secret_password_storev_binary_sync (NULL, attributes, collection, store_label, value, NULL, &error);
336 : 4 : secret_value_unref (value);
337 : :
338 : 4 : g_hash_table_unref (attributes);
339 : 4 : g_free (store_label);
340 : 4 : g_free (store_collection);
341 : 4 : g_free (collection);
342 : :
343 [ - + ]: 4 : if (!ret) {
344 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
345 : 0 : return 1;
346 : : }
347 : :
348 : 4 : return 0;
349 : : }
350 : :
351 : : static void
352 : 12 : print_item_when (const char *field,
353 : : guint64 when)
354 : : {
355 : : GDateTime *dt;
356 : : gchar *value;
357 : :
358 [ - + ]: 12 : if (!when) {
359 : 0 : value = g_strdup ("");
360 : : } else {
361 : 12 : dt = g_date_time_new_from_unix_utc (when);
362 : 12 : value = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S");
363 : 12 : g_date_time_unref (dt);
364 : : }
365 : :
366 : 12 : g_print ("%s = %s\n", field, value);
367 : 12 : g_free (value);
368 : 12 : }
369 : :
370 : : static void
371 : 6 : on_retrieve_secret (GObject *source_object,
372 : : GAsyncResult *res,
373 : : gpointer user_data)
374 : : {
375 : 6 : SecretRetrievable *item = SECRET_RETRIEVABLE (source_object);
376 : 6 : GMainLoop *loop = user_data;
377 : : SecretValue *secret;
378 : : GHashTableIter iter;
379 : : GHashTable *attributes;
380 : : const gchar *value, *key;
381 : : guint64 when;
382 : : gchar *label;
383 : : const gchar *part;
384 : : const gchar *path;
385 : : GError *error;
386 : :
387 : 6 : error = NULL;
388 : 6 : secret = secret_retrievable_retrieve_secret_finish (item, res, &error);
389 [ - + ]: 6 : if (!secret) {
390 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
391 : 0 : g_clear_error (&error);
392 : : }
393 : :
394 [ - + + - : 6 : if (G_IS_DBUS_PROXY (item)) {
- + - + ]
395 : 0 : path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (item));
396 [ # # ]: 0 : g_return_if_fail (path != NULL);
397 : :
398 : : /* The item identifier */
399 : 0 : part = strrchr (path, '/');
400 [ # # ]: 0 : if (part == NULL)
401 : 0 : part = path;
402 : 0 : g_print ("[%s]\n", part);
403 : : } else {
404 : 6 : g_print ("[no path]\n");
405 : : }
406 : :
407 : : /* The label */
408 : 6 : label = secret_retrievable_get_label (item);
409 : 6 : g_print ("label = %s\n", label);
410 : 6 : g_free (label);
411 : :
412 [ + - ]: 6 : if (secret) {
413 : : /* The secret value */
414 : 6 : g_print ("secret = ");
415 [ + - ]: 6 : if (secret != NULL) {
416 : 6 : write_password_data (secret);
417 : 6 : secret_value_unref (secret);
418 : : }
419 : 6 : g_print ("\n");
420 : : }
421 : :
422 : : /* The dates */
423 : 6 : when = secret_retrievable_get_created (item);
424 : 6 : print_item_when ("created", when);
425 : 6 : when = secret_retrievable_get_modified (item);
426 : 6 : print_item_when ("modified", when);
427 : :
428 : : /* The attributes */
429 : 6 : attributes = secret_retrievable_get_attributes (item);
430 : 6 : value = g_hash_table_lookup (attributes, "xdg:schema");
431 [ - + ]: 6 : if (value)
432 : 0 : g_print ("schema = %s\n", value);
433 : :
434 : 6 : g_hash_table_iter_init (&iter, attributes);
435 [ + + ]: 20 : while (g_hash_table_iter_next (&iter, (void **)&key, (void **)&value)) {
436 [ + - ]: 8 : if (strcmp (key, "xdg:schema") != 0)
437 : 8 : g_printerr ("attribute.%s = %s\n", key, value);
438 : : }
439 : 6 : g_hash_table_unref (attributes);
440 : 6 : g_main_loop_quit (loop);
441 : : }
442 : :
443 : : static int
444 : 4 : secret_tool_action_search (int argc,
445 : : char *argv[])
446 : : {
447 : 4 : GError *error = NULL;
448 : : GOptionContext *context;
449 : : GHashTable *attributes;
450 : : SecretSearchFlags flags;
451 : 4 : gboolean flag_all = FALSE;
452 : 4 : gboolean flag_unlock = FALSE;
453 : : GList *items, *l;
454 : :
455 : : /* secret-tool lookup name xxxx yyyy zzzz */
456 : 4 : const GOptionEntry lookup_options[] = {
457 : : { "all", 'a', 0, G_OPTION_ARG_NONE, &flag_all,
458 : : N_("return all results, instead of just first one"), NULL },
459 : : { "unlock", 'a', 0, G_OPTION_ARG_NONE, &flag_unlock,
460 : : N_("unlock item results if necessary"), NULL },
461 : : { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &attribute_args,
462 : : N_("attribute value pairs of item to lookup"), NULL },
463 : : { NULL }
464 : : };
465 : :
466 : 4 : context = g_option_context_new ("attribute value ...");
467 : 4 : g_option_context_add_main_entries (context, lookup_options, GETTEXT_PACKAGE);
468 [ - + ]: 4 : if (!g_option_context_parse (context, &argc, &argv, &error)) {
469 : 0 : g_printerr ("%s\n", error->message);
470 : 0 : usage();
471 : : }
472 : :
473 : 4 : g_option_context_free (context);
474 : :
475 : 4 : attributes = attributes_from_arguments (attribute_args);
476 : 4 : g_strfreev (attribute_args);
477 : :
478 : 4 : flags = SECRET_SEARCH_LOAD_SECRETS;
479 [ - + ]: 4 : if (flag_all)
480 : 0 : flags |= SECRET_SEARCH_ALL;
481 [ - + ]: 4 : if (flag_unlock)
482 : 0 : flags |= SECRET_SEARCH_UNLOCK;
483 : 4 : items = secret_password_searchv_sync (NULL, attributes, flags, NULL, &error);
484 [ + - ]: 4 : if (error == NULL) {
485 : 4 : GMainLoop *loop = g_main_loop_new (NULL, FALSE);
486 : :
487 [ + - + + ]: 10 : for (l = items; l != NULL; l = g_list_next (l)) {
488 : 6 : SecretRetrievable *retrievable = SECRET_RETRIEVABLE (l->data);
489 : 6 : secret_retrievable_retrieve_secret (retrievable,
490 : : NULL,
491 : : on_retrieve_secret,
492 : : loop);
493 : 6 : g_main_loop_run (loop);
494 : : }
495 : :
496 : 4 : g_list_free_full (items, g_object_unref);
497 : 4 : g_main_loop_unref (loop);
498 : : }
499 : :
500 : 4 : g_hash_table_unref (attributes);
501 : :
502 [ - + ]: 4 : if (error != NULL) {
503 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
504 : 0 : g_error_free (error);
505 : 0 : return 1;
506 : : }
507 : :
508 : 4 : return 0;
509 : : }
510 : :
511 : : static int
512 : 0 : secret_tool_action_lock (int argc,
513 : : char *argv[])
514 : : {
515 : 0 : int ret = 0;
516 : 0 : SecretService *service = NULL;
517 : 0 : GError *error = NULL;
518 : 0 : GOptionContext *context = NULL;
519 : 0 : GList *locked = NULL;
520 : 0 : GList *collections = NULL;
521 : 0 : SecretCollection *collection = NULL;
522 : :
523 : 0 : service = secret_service_get_sync (SECRET_SERVICE_LOAD_COLLECTIONS, NULL, &error);
524 [ # # ]: 0 : if (error != NULL) {
525 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
526 : 0 : ret = 1;
527 : 0 : goto out;
528 : : }
529 : :
530 : 0 : context = g_option_context_new ("collections");
531 : 0 : g_option_context_add_main_entries (context, COLLECTION_OPTIONS, GETTEXT_PACKAGE);
532 : 0 : g_option_context_parse (context, &argc, &argv, &error);
533 : :
534 [ # # ]: 0 : if (store_collection != NULL) {
535 : 0 : char *collection_path = NULL;
536 : :
537 : 0 : collection_path = g_strconcat (SECRET_COLLECTION_PREFIX, store_collection, NULL);
538 : 0 : collection = secret_collection_new_for_dbus_path_sync (service, collection_path, SECRET_COLLECTION_NONE, NULL, &error);
539 : 0 : g_free (store_collection);
540 : 0 : g_free (collection_path);
541 : :
542 [ # # ]: 0 : if (error != NULL) {
543 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
544 : 0 : ret = 1;
545 : 0 : goto out;
546 : : }
547 : :
548 [ # # # # ]: 0 : if (secret_collection_get_locked (collection) || collection == NULL) {
549 : 0 : ret = 1;
550 : 0 : goto out;
551 : : }
552 : :
553 : 0 : collections = g_list_append (collections,
554 : : g_steal_pointer (&collection));
555 : : } else {
556 : 0 : collections = secret_service_get_collections (service);
557 : : }
558 : :
559 : 0 : secret_service_lock_sync (NULL, collections, 0, &locked, &error);
560 [ # # ]: 0 : if (error != NULL) {
561 : 0 : g_printerr ("%s: %s\n", g_get_prgname (), error->message);
562 : 0 : ret = 1;
563 : 0 : goto out;
564 : : }
565 : :
566 : 0 : out:
567 [ # # ]: 0 : g_clear_object (&collection);
568 [ # # ]: 0 : g_clear_list (&collections, g_object_unref);
569 [ # # ]: 0 : g_clear_list (&locked, g_object_unref);
570 [ # # ]: 0 : g_clear_pointer (&context, g_option_context_free);
571 : 0 : g_clear_error (&error);
572 [ # # ]: 0 : g_clear_object (&service);
573 : 0 : return ret;
574 : : }
575 : :
576 : : int
577 : 12 : main (int argc,
578 : : char *argv[])
579 : : {
580 : : SecretToolAction action;
581 : :
582 : 12 : setlocale (LC_ALL, "");
583 : :
584 : : #ifdef ENABLE_NLS
585 : : bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
586 : : bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
587 : : textdomain (GETTEXT_PACKAGE);
588 : : #endif
589 : :
590 [ - + ]: 12 : if (argc < 2)
591 : 0 : usage();
592 : :
593 [ + + ]: 12 : if (g_str_equal (argv[1], "store")) {
594 : 4 : action = secret_tool_action_store;
595 [ + + ]: 8 : } else if (g_str_equal (argv[1], "lookup")) {
596 : 2 : action = secret_tool_action_lookup;
597 [ + + ]: 6 : } else if (g_str_equal (argv[1], "clear")) {
598 : 2 : action = secret_tool_action_clear;
599 [ + - ]: 4 : } else if (g_str_equal (argv[1], "search")) {
600 : 4 : action = secret_tool_action_search;
601 [ # # ]: 0 : } else if (g_str_equal (argv[1], "lock")) {
602 : 0 : action = secret_tool_action_lock;
603 : : } else {
604 : 0 : usage ();
605 : : }
606 : :
607 : 12 : argv[1] = argv[0];
608 : 12 : return (action) (argc - 1, argv + 1);
609 : : }
|