Added LIST --all.
[pwmd.git] / src / commands.c
blob677549cb0650b717c3cc6ee58b28b2273e3dcfed
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of pwmd.
8 Pwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Pwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <err.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <dirent.h>
37 #include <pthread.h>
39 #ifdef WITH_LIBACL
40 #include <sys/acl.h>
41 #endif
43 #include "pwmd-error.h"
44 #include <gcrypt.h>
46 #include "mem.h"
47 #include "xml.h"
48 #include "common.h"
49 #include "rcfile.h"
50 #include "cache.h"
51 #include "misc.h"
52 #include "commands.h"
53 #include "mutex.h"
55 /* These are flags that are set by commands. */
56 #define OPT_INQUIRE 0x0001
57 #define OPT_NO_PASSPHRASE 0x0002
58 #define OPT_RESET 0x0004
59 #define OPT_LIST_RECURSE 0x0008
60 #define OPT_LIST_VERBOSE 0x0010
61 #define OPT_LOCK 0x0020
62 #define OPT_LOCK_ON_OPEN 0x0040
63 #define OPT_SIGN 0x0080
64 #define OPT_LIST_ALL 0x0100
66 struct command_table_s {
67 const gchar *name;
68 gpg_error_t (*handler)(assuan_context_t, gchar *line);
69 const gchar *help;
70 gboolean ignore_startup;
71 gboolean unlock; // unlock the file mutex after validating the checksum
74 static struct command_table_s **command_table;
76 static gpg_error_t do_lock(struct client_s *client, gboolean add);
78 gpg_error_t unlock_file_mutex(struct client_s *client, gboolean remove)
80 gpg_error_t rc = 0;
82 // OPEN: keep the lock for the same file being reopened.
83 if (client->flags&FLAG_KEEP_LOCK && client->flags&FLAG_HAS_LOCK)
84 return 0;
86 if (!(client->flags&FLAG_HAS_LOCK))
87 return GPG_ERR_NOT_LOCKED;
89 rc = cache_unlock_mutex(client->md5file, remove);
90 if (rc)
91 rc = GPG_ERR_INV_STATE;
92 else
93 client->flags &= ~(FLAG_HAS_LOCK|FLAG_LOCK_CMD);
95 return rc;
98 gpg_error_t lock_file_mutex(struct client_s *client, gboolean add)
100 gpg_error_t rc = 0;
101 gint timeout = get_key_file_integer(client->filename, "cache_timeout");
103 if (client->flags&FLAG_HAS_LOCK)
104 return 0;
106 rc = cache_lock_mutex(client->ctx, client->md5file,
107 client->flags&FLAG_RC_ON_LOCKED, add, timeout);
108 if (!rc)
109 client->flags |= FLAG_HAS_LOCK;
111 return rc;
114 static gpg_error_t file_modified(struct client_s *client,
115 struct command_table_s *cmd)
117 gpg_error_t rc = 0;
119 if (!(client->flags&FLAG_OPEN))
120 return GPG_ERR_INV_STATE;
122 rc = lock_file_mutex(client, FALSE);
123 if (!rc || rc == GPG_ERR_NO_DATA) {
124 struct stat st;
126 rc = 0;
127 if (g_lstat(client->filename, &st) && errno != ENOENT)
128 rc = gpg_error_from_syserror();
129 else if (client->ctime && client->ctime != st.st_ctime)
130 rc = GPG_ERR_CHECKSUM;
133 if ((cmd->unlock && !(client->flags&FLAG_HAS_LOCK)) || rc)
134 unlock_file_mutex(client, FALSE);
136 return rc;
139 static gpg_error_t parse_xml(assuan_context_t ctx, gboolean new)
141 struct client_s *client = assuan_get_pointer(ctx);
142 gboolean cached = client->doc != NULL;
144 if (new)
145 client->doc = new_document();
146 else if (!cached)
147 client->doc = parse_doc((gchar *)client->crypto->plaintext,
148 client->crypto->plaintext_len);
150 if (client->doc) {
151 client->dupdoc = xmlCopyDoc(client->doc, 1);
152 if (!client->dupdoc) {
153 if (!cached)
154 xmlFreeDoc(client->doc);
155 client->doc = NULL;
159 return !client->doc ? GPG_ERR_ENOMEM : 0;
162 static void free_client(struct client_s *client)
164 /* client->doc is a pointer to the xml and is held in the cache. */
165 if (client->dupdoc)
166 xmlFreeDoc(client->dupdoc);
168 g_free(client->filename);
169 g_free(client->last_error);
171 if (client->crypto) {
172 cleanup_crypto_stage2(client->crypto);
173 if (client->crypto->pkey_sexp)
174 gcry_sexp_release(client->crypto->pkey_sexp);
176 client->crypto->pkey_sexp = NULL;
177 memset(client->crypto->sign_grip, 0, sizeof(client->crypto->sign_grip));
180 if (client->xml_error)
181 xmlResetError(client->xml_error);
184 void cleanup_client(struct client_s *client)
186 assuan_context_t ctx = client->ctx;
187 struct client_thread_s *thd = client->thd;
188 struct crypto_s *crypto = client->crypto;
190 unlock_file_mutex(client, client->flags&FLAG_NEW);
191 free_client(client);
192 memset(client, 0, sizeof(struct client_s));
193 client->crypto = crypto;
194 client->ctx = ctx;
195 client->thd = thd;
198 static gpg_error_t open_finalize(assuan_context_t ctx)
200 struct client_s *client = assuan_get_pointer(ctx);
201 gpg_error_t rc = 0;
202 struct cache_data_s *cdata = cache_get_data(client->md5file);
203 gboolean cached = FALSE;
205 client->crypto->filename = g_strdup(client->filename);
206 if (cdata || client->flags&FLAG_NEW) {
207 cached = cdata != NULL;
208 goto done;
211 rc = send_status(ctx, STATUS_DECRYPT, NULL);
212 if (rc)
213 goto done;
215 rc = decrypt_data(client->crypto);
217 done:
218 if (!rc) {
219 rc = parse_xml(ctx, client->flags&FLAG_NEW);
220 if (!rc) {
221 gint timeout = get_key_file_integer(client->filename,
222 "cache_timeout");
224 if (!cached) {
225 cdata = g_malloc0(sizeof(struct cache_data_s));
226 cdata->doc = client->doc;
228 if (!(client->flags&FLAG_NEW)) {
229 rc = gcry_sexp_build((gcry_sexp_t *)&cdata->pubkey, NULL,
230 "%S", client->crypto->pkey_sexp);
234 if (cdata->sigkey)
235 gcry_sexp_release(cdata->sigkey);
237 rc = gcry_sexp_build((gcry_sexp_t *)&cdata->sigkey, NULL,
238 "%S", client->crypto->sigpkey_sexp);
239 cdata->ctime = client->crypto->st.st_ctime;
241 if (!cache_add_file(client->md5file, (client->flags&FLAG_NEW)
242 ? NULL : client->crypto->grip, cdata, timeout)) {
243 if (!cached) {
244 g_free(cdata);
245 xmlFreeDoc(client->doc);
246 client->doc = NULL;
248 rc = GPG_ERR_ENOMEM;
251 if (!rc) {
252 client->flags |= FLAG_OPEN;
253 client->doc = client->dupdoc;
258 if (!rc && (client->opts&OPT_LOCK_ON_OPEN))
259 rc = do_lock(client, FALSE);
261 return rc;
264 static void req_cleanup(void *arg)
266 if (!arg)
267 return;
269 g_strfreev((gchar **)arg);
272 static gpg_error_t parse_open_opt_lock(gpointer data, gpointer value)
274 struct client_s *client = data;
276 client->opts |= OPT_LOCK_ON_OPEN;
277 return 0;
280 static gpg_error_t parse_opt_pinentry(gpointer data, gpointer value)
282 struct client_s *client = data;
284 client->flags |= FLAG_NO_PINENTRY;
285 return 0;
288 static gpg_error_t parse_opt_inquire(gpointer data, gpointer value)
290 struct client_s *client = data;
292 (void)value;
293 client->opts |= OPT_INQUIRE;
294 return 0;
297 static void update_checksum(struct client_s *client)
299 client->ctime = client->crypto->st.st_ctime;
302 static gpg_error_t validate_checksum(struct client_s *client, time_t ctime)
304 if (!g_lstat(client->filename, &client->crypto->st))
305 return ctime != client->crypto->st.st_ctime ? GPG_ERR_CHECKSUM : 0;
307 return gpg_error_from_syserror();
310 static gpg_error_t do_open(assuan_context_t ctx, const gchar *filename,
311 const gchar *password)
313 struct client_s *client = assuan_get_pointer(ctx);
314 struct cache_data_s *cdata;
315 gpg_error_t rc = 0;
316 gboolean done = FALSE;
318 if (!valid_filename(filename))
319 return GPG_ERR_INV_VALUE;
321 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
322 client->filename = g_strdup(filename);
323 if (!client->filename)
324 return GPG_ERR_ENOMEM;
326 // Cached document?
327 cache_lock();
328 pthread_cleanup_push(cleanup_cache_mutex, NULL);
329 cdata = cache_get_data(client->md5file);
330 if (cdata && cdata->doc) {
331 rc = validate_checksum(client, cdata->ctime);
332 /* This will check that the key is cached in the agent which needs to
333 * be determined for files that share a keygrip. */
334 if (!rc)
335 rc = cache_iscached(client->filename);
337 if (!rc && !password) {
338 rc = read_data_header(client->filename, &client->crypto->hdr,
339 NULL, NULL);
340 if (!rc) {
341 client->doc = cdata->doc;
342 gcry_sexp_build(&client->crypto->pkey_sexp, NULL, "%S",
343 cdata->pubkey);
344 gcry_pk_get_keygrip(client->crypto->pkey_sexp, client->crypto->grip);
345 gcry_sexp_build(&client->crypto->sigpkey_sexp, NULL, "%S",
346 cdata->sigkey);
347 gcry_pk_get_keygrip(client->crypto->sigpkey_sexp, client->crypto->sign_grip);
348 rc = open_finalize(ctx);
349 done = TRUE;
353 /* There was an error accessing the file so clear the cache entry. The
354 * real error will be returned from read_data_file() since the file
355 * may have only disappeared. */
356 if (!done)
357 rc = cache_clear(client->md5file);
360 pthread_cleanup_pop(1);
361 if (done || rc)
362 return rc;
364 rc = read_data_file(client->filename, client->crypto);
365 if (rc) {
366 if (gpg_err_code(rc) != GPG_ERR_ENOENT) {
367 log_write("%s: %s", client->filename, pwmd_strerror(rc));
368 return rc;
371 client->flags |= FLAG_NEW;
372 memset(client->crypto->grip, 0, sizeof(client->crypto->grip));
373 memset(client->crypto->sign_grip, 0, sizeof(client->crypto->sign_grip));
374 cache_lock();
375 pthread_cleanup_push(cleanup_cache_mutex, NULL);
376 rc = open_finalize(ctx);
377 pthread_cleanup_pop(1);
378 return rc;
381 update_checksum(client);
383 if (password) {
384 rc = set_agent_passphrase(client->crypto, password, strlen(password));
385 if (rc)
386 return rc;
389 cache_lock();
390 pthread_cleanup_push(cleanup_cache_mutex, NULL);
391 rc = open_finalize(ctx);
392 pthread_cleanup_pop(1);
393 return rc;
396 gpg_error_t kill_scd(struct agent_s *agent)
398 gpg_error_t rc = 0;
400 if (get_key_file_boolean(NULL, "kill_scd"))
401 rc = send_to_agent(agent, NULL, NULL, "SCD KILLSCD");
403 return rc;
406 static gpg_error_t open_command(assuan_context_t ctx, gchar *line)
408 gpg_error_t rc;
409 struct client_s *client = assuan_get_pointer(ctx);
410 gchar **req, *password = NULL, *filename;
411 guchar md5file[16];
412 gboolean same_file = FALSE;
413 struct argv_s *args[] = {
414 &(struct argv_s) { "lock", OPTION_TYPE_NOARG, parse_open_opt_lock },
415 &(struct argv_s) { "no-pinentry", OPTION_TYPE_NOARG,
416 parse_opt_pinentry },
417 NULL
420 client->flags &= ~(FLAG_NO_PINENTRY);
421 rc = parse_options(&line, args, client);
422 if (rc)
423 return send_error(ctx, rc);
425 req = split_input_line(line, " ", 2);
426 if (!req)
427 return send_error(ctx, GPG_ERR_SYNTAX);
429 pthread_cleanup_push(req_cleanup, req);
430 filename = req[0];
431 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, filename, strlen(filename));
432 /* This client may have locked a different file with ISCACHED --lock than
433 * the current filename. This will remove that lock. */
434 same_file = !memcmp(md5file, client->md5file, 16) ? TRUE : FALSE;
435 if (client->flags&FLAG_OPEN ||
436 (client->flags&FLAG_HAS_LOCK && !same_file)) {
437 typeof(client->opts) opts = client->opts;
438 typeof(client->flags) flags = client->flags;
440 if (same_file)
441 client->flags |= FLAG_KEEP_LOCK;
443 cleanup_client(client);
444 client->opts = opts;
445 client->flags |= flags;
446 client->flags &= ~(FLAG_LOCK_CMD|FLAG_NEW);
447 if (!same_file)
448 client->flags &= ~(FLAG_HAS_LOCK);
451 /* Need to lock the mutex here because file_modified() cannot without
452 * knowing the filename. */
453 memcpy(client->md5file, md5file, 16);
454 rc = lock_file_mutex(client, TRUE);
455 if (!rc) {
456 password = req[1] && *req[1] ? req[1] : NULL;
457 rc = set_pinentry_mode(client->crypto->agent,
458 (client->flags&FLAG_NO_PINENTRY) ? "loopback" : "ask");
459 if (!rc) {
460 rc = do_open(ctx, filename, password);
461 if (rc)
462 cleanup_client(client);
464 cleanup_crypto_stage1(client->crypto);
468 pthread_cleanup_pop(1);
470 if (!rc && client->flags & FLAG_NEW)
471 rc = send_status(ctx, STATUS_NEWFILE, NULL);
473 (void)kill_scd(client->crypto->agent);
474 return send_error(ctx, rc);
477 static gpg_error_t parse_save_opt_no_passphrase(gpointer data, gpointer value)
479 struct client_s *client = data;
481 (void)value;
482 client->opts |= OPT_NO_PASSPHRASE;
483 return 0;
486 static gpg_error_t parse_save_opt_cipher(gpointer data, gpointer value)
488 struct client_s *client = data;
489 gint algo = cipher_string_to_gcrypt((gchar *)value);
490 file_header_t *hdr = &client->crypto->save.hdr;
492 if (algo == -1)
493 return GPG_ERR_INV_VALUE;
495 hdr->flags = set_cipher_flag(hdr->flags, algo);
496 return 0;
499 static gpg_error_t parse_save_opt_keygrip(gpointer data, gpointer value)
501 struct client_s *client = data;
503 if (client->crypto->save.pkey)
504 gcry_sexp_release(client->crypto->save.pkey);
506 client->crypto->save.pkey = NULL;
507 return get_pubkey(client->crypto, value, &client->crypto->save.pkey);
510 static gpg_error_t parse_save_opt_sign_keygrip(gpointer data, gpointer value)
512 struct client_s *client = data;
514 if (client->crypto->save.sigpkey)
515 gcry_sexp_release(client->crypto->save.sigpkey);
517 client->crypto->save.sigpkey = NULL;
518 return get_pubkey(client->crypto, value, &client->crypto->save.sigpkey);
521 static gpg_error_t parse_opt_s2k_count(gpointer data, gpointer value)
523 struct client_s *client = data;
524 gchar *v = value;
526 if (!v || !*v)
527 return GPG_ERR_INV_VALUE;
529 client->crypto->save.s2k_count = strtoul(v, NULL, 10);
530 return 0;
533 static gpg_error_t save_finalize(assuan_context_t ctx)
535 struct client_s *client = assuan_get_pointer(ctx);
536 gpg_error_t rc;
537 xmlChar *buf = NULL;
538 gint buflen;
539 gcry_sexp_t pubkey = client->crypto->save.pkey;
540 gcry_sexp_t sigkey = client->crypto->save.sigpkey;
541 struct cache_data_s *cdata;
543 xmlDocDumpFormatMemory(client->doc, &buf, &buflen, 0);
544 if (!buf)
545 return GPG_ERR_ENOMEM;
547 if (!pubkey)
548 pubkey = client->crypto->pkey_sexp;
550 if (!sigkey) {
551 sigkey = client->crypto->sigpkey_sexp;
552 if (!sigkey) {
553 gcry_sexp_build(&client->crypto->save.sigpkey, 0, "%S", pubkey);
554 sigkey = client->crypto->save.sigpkey;
558 rc = send_status(ctx, STATUS_ENCRYPT, NULL);
559 if (rc) {
560 xmlFree(buf);
561 return rc;
564 pthread_cleanup_push(xmlFree, buf);
565 rc = encrypt_data_file(client->crypto, pubkey, sigkey, client->filename,
566 buf, buflen);
567 pthread_cleanup_pop(1);
568 if (pubkey == client->crypto->save.pkey) {
569 if (!rc) {
570 gcry_sexp_release(client->crypto->pkey_sexp);
571 client->crypto->pkey_sexp = client->crypto->save.pkey;
572 gcry_pk_get_keygrip(client->crypto->pkey_sexp, client->crypto->grip);
574 else
575 gcry_sexp_release(pubkey);
577 client->crypto->save.pkey = NULL;
580 if (!rc)
581 gcry_pk_get_keygrip(sigkey, client->crypto->sign_grip);
583 if (sigkey == client->crypto->save.sigpkey) {
584 if (!rc) {
585 if (client->crypto->sigpkey_sexp)
586 gcry_sexp_release(client->crypto->sigpkey_sexp);
588 client->crypto->sigpkey_sexp = client->crypto->save.sigpkey;
589 gcry_pk_get_keygrip(client->crypto->sigpkey_sexp, client->crypto->sign_grip);
591 else
592 gcry_sexp_release(sigkey);
594 client->crypto->save.sigpkey = NULL;
597 if (!rc) {
598 update_checksum(client);
599 cache_lock();
600 pthread_cleanup_push(cleanup_cache_mutex, NULL);
601 cdata = cache_get_data(client->md5file);
602 gboolean cached = cdata != NULL;
604 if (!cdata)
605 cdata = g_malloc0(sizeof(struct cache_data_s));
607 /* Update in case of any --keygrip argument */
608 if (cdata->pubkey)
609 gcry_sexp_release(cdata->pubkey);
611 gcry_sexp_build((gcry_sexp_t *)&cdata->pubkey, NULL, "%S",
612 client->crypto->pkey_sexp);
614 if (cdata->sigkey)
615 gcry_sexp_release(cdata->sigkey);
617 gcry_sexp_build((gcry_sexp_t *)&cdata->sigkey, NULL, "%S",
618 client->crypto->sigpkey_sexp);
620 xmlDocPtr doc = xmlCopyDoc(client->doc, 1);
621 if (doc) {
622 if (cdata->doc)
623 xmlFreeDoc(cdata->doc);
625 client->dupdoc = doc;
626 cdata->doc = client->doc;
627 client->doc = client->dupdoc;
628 cdata->ctime = client->ctime;
629 rc = cache_set_data(client->md5file, cdata, client->crypto->grip);
631 if (!rc && (!cached || (client->flags&FLAG_NEW)))
632 send_status_all(STATUS_CACHE, NULL);
634 if (!rc)
635 client->flags &= ~(FLAG_NEW);
637 else
638 rc = GPG_ERR_ENOMEM;
640 pthread_cleanup_pop(1);
643 return rc;
646 static gpg_error_t parse_opt_reset(gpointer data, gpointer value)
648 struct client_s *client = data;
650 (void)value;
651 client->opts |= OPT_RESET;
652 return 0;
655 static gpg_error_t save_command(assuan_context_t ctx, gchar *line)
657 struct client_s *client = assuan_get_pointer(ctx);
658 gpg_error_t rc;
659 struct stat st;
660 struct argv_s *args[] = {
661 &(struct argv_s) { "no-passphrase", OPTION_TYPE_NOARG,
662 parse_save_opt_no_passphrase },
663 &(struct argv_s) { "cipher", OPTION_TYPE_ARG, parse_save_opt_cipher },
664 &(struct argv_s) { "inquire-keyparam", OPTION_TYPE_NOARG, parse_opt_inquire },
665 &(struct argv_s) { "keygrip", OPTION_TYPE_ARG, parse_save_opt_keygrip },
666 &(struct argv_s) { "sign-keygrip", OPTION_TYPE_ARG,
667 parse_save_opt_sign_keygrip },
668 &(struct argv_s) { "s2k-count", OPTION_TYPE_ARG, parse_opt_s2k_count },
669 &(struct argv_s) { "reset", OPTION_TYPE_NOARG, parse_opt_reset },
670 NULL
673 cleanup_save(&client->crypto->save);
674 memcpy(&client->crypto->save.hdr, &client->crypto->hdr, sizeof(file_header_t));
675 client->crypto->save.s2k_count = get_key_file_ulong(client->filename, "s2k_count");
676 rc = parse_options(&line, args, client);
677 if (rc)
678 return send_error(ctx, rc);
680 if ((client->opts&OPT_NO_PASSPHRASE) && !(client->flags&FLAG_NEW)
681 && !(client->opts&OPT_INQUIRE))
682 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
684 if (g_lstat(client->filename, &st) == -1 && errno != ENOENT)
685 return send_error(ctx, gpg_error_from_syserror());
687 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
688 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
689 return send_error(ctx, GPG_ERR_ENOANO);
692 if (client->opts&OPT_RESET) {
693 rc = cache_clear(client->md5file);
694 if (rc)
695 return send_error(ctx, rc);
698 rc = update_element_mtime(xmlDocGetRootElement(client->doc));
699 if (rc)
700 return send_error(ctx, rc);
702 if (!rc && !client->crypto->save.pkey &&
703 (client->flags&FLAG_NEW || client->opts&OPT_INQUIRE)) {
704 rc = send_status(client->ctx, STATUS_GENKEY, NULL);
705 if (!rc) {
706 struct inquire_data_s idata = {0};
707 gchar *params = client->opts&OPT_INQUIRE ? NULL
708 : default_key_params(client->crypto);
710 pthread_cleanup_push(g_free, params);
711 idata.crypto = client->crypto;
713 if (params) {
714 idata.line = params;
715 idata.len = strlen(params);
717 else
718 idata.preset = TRUE;
720 client->crypto->agent->inquire_data = &idata;
721 client->crypto->agent->inquire_cb = NULL;
722 rc = generate_key(client->crypto, params,
723 (client->opts&OPT_NO_PASSPHRASE), TRUE);
724 pthread_cleanup_pop(1);
728 if (!rc)
729 rc = save_finalize(ctx);
731 cleanup_crypto_stage1(client->crypto);
732 (void)kill_scd(client->crypto->agent);
733 return send_error(ctx, rc);
736 static gpg_error_t do_delete(assuan_context_t ctx, gchar *line)
738 struct client_s *client = assuan_get_pointer(ctx);
739 gpg_error_t rc;
740 gchar **req;
741 xmlNodePtr n;
743 if (strchr(line, '\t'))
744 req = split_input_line(line, "\t", -1);
745 else
746 req = split_input_line(line, " ", -1);
748 if (!req || !*req)
749 return GPG_ERR_SYNTAX;
751 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
752 if (!n) {
753 g_strfreev(req);
754 return rc;
758 * No sub-node defined. Remove the entire node (root element).
760 if (!req[1]) {
761 if (n) {
762 rc = unlink_node(n);
763 xmlFreeNode(n);
766 g_strfreev(req);
767 return rc;
770 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
771 g_strfreev(req);
772 if (!n)
773 return rc;
775 if (n) {
776 rc = unlink_node(n);
777 xmlFreeNode(n);
780 return rc;
783 static gpg_error_t delete_command(assuan_context_t ctx, gchar *line)
785 struct client_s *client = assuan_get_pointer(ctx);
786 gpg_error_t rc;
787 struct argv_s *args[] = {
788 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
789 NULL
792 rc = parse_options(&line, args, client);
793 if (rc)
794 return send_error(ctx, rc);
796 if (client->opts&OPT_INQUIRE) {
797 guchar *result;
798 gsize len;
800 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
801 if (rc)
802 return send_error(ctx, rc);
804 line = (gchar *)result;
807 rc = do_delete(ctx, line);
809 if (client->opts&OPT_INQUIRE)
810 xfree(line);
812 return send_error(ctx, rc);
815 /* FIXME: storing an element content with the same value as a child element
816 * name. */
817 static gpg_error_t store_command(assuan_context_t ctx, gchar *line)
819 struct client_s *client = assuan_get_pointer(ctx);
820 gpg_error_t rc;
821 gsize len;
822 guchar *result;
823 gchar **req;
824 xmlNodePtr n, parent;
825 gboolean has_content;
826 gchar *content = NULL;
828 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
829 if (rc)
830 return send_error(ctx, rc);
832 req = split_input_line((gchar *)result, "\t", 0);
833 xfree(result);
835 if (!req || !*req)
836 return send_error(ctx, GPG_ERR_SYNTAX);
838 len = g_strv_length(req);
839 has_content = line[strlen(line)-1] != '\t' && len > 1;
840 if (*(req+1) && !valid_element_path(req, has_content)) {
841 g_strfreev(req);
842 return send_error(ctx, GPG_ERR_INV_VALUE);
845 if (has_content) {
846 content = req[len-1];
847 req[len-1] = NULL;
850 again:
851 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
852 if (rc && rc == GPG_ERR_ELEMENT_NOT_FOUND) {
853 rc = new_root_element(client->doc, *req);
854 if (rc) {
855 g_strfreev(req);
856 return send_error(ctx, GPG_ERR_SYNTAX);
859 goto again;
862 if (!n) {
863 g_strfreev(req);
864 return send_error(ctx, rc);
867 parent = n;
869 if (req[1] && *req[1]) {
870 if (!n->children)
871 parent = create_elements_cb(n, req+1, &rc, NULL);
872 else
873 parent = find_elements(client->doc, n->children, req+1, &rc,
874 NULL, NULL, create_elements_cb, FALSE, 0, NULL, FALSE);
877 if (!rc && len > 1) {
878 n = find_text_node(parent->children);
879 if (n)
880 xmlNodeSetContent(n, (xmlChar *)content);
881 else
882 xmlNodeAddContent(parent, (xmlChar *)content);
884 update_element_mtime(parent);
887 if (has_content)
888 req[len-1] = content;
890 g_strfreev(req);
891 return send_error(ctx, rc);
894 static gpg_error_t xfer_data(assuan_context_t ctx, const gchar *line,
895 gint total)
897 gint to_send;
898 gint sent = 0;
899 gpg_error_t rc;
900 gint progress = get_key_file_integer("global", "xfer_progress");
901 gint flush = 0;
903 progress = progress>0 ? (progress/ASSUAN_LINELENGTH)*ASSUAN_LINELENGTH : 0;
904 to_send = total < ASSUAN_LINELENGTH ? total : ASSUAN_LINELENGTH;
905 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
907 if (rc)
908 return rc;
910 again:
911 do {
912 if (sent + to_send > total)
913 to_send = total - sent;
915 rc = assuan_send_data(ctx, flush ? NULL : (gchar *)line+sent,
916 flush ? 0 : to_send);
917 if (!rc) {
918 sent += flush ? 0 : to_send;
920 if ((progress && !(sent % progress) && sent != total) ||
921 (sent == total && flush))
922 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
924 if (!flush && !rc && sent == total) {
925 flush = 1;
926 goto again;
929 } while (!rc && sent < total);
931 return rc;
934 static gpg_error_t do_get(assuan_context_t ctx, gchar *line)
936 struct client_s *client = assuan_get_pointer(ctx);
937 gpg_error_t rc;
938 gchar **req;
939 xmlNodePtr n;
941 req = split_input_line(line, "\t", -1);
943 if (!req || !*req) {
944 g_strfreev(req);
945 return GPG_ERR_SYNTAX;
948 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
949 if (!n) {
950 g_strfreev(req);
951 return rc;
954 if (req[1])
955 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
957 g_strfreev(req);
958 if (rc)
959 return rc;
961 if (!n || !n->children)
962 return GPG_ERR_NO_DATA;
964 n = find_text_node(n->children);
965 if (!n || !n->content || !*n->content)
966 return GPG_ERR_NO_DATA;
968 rc = xfer_data(ctx, (gchar *)n->content, xmlStrlen(n->content));
969 return rc;
972 static gpg_error_t get_command(assuan_context_t ctx, gchar *line)
974 struct client_s *client = assuan_get_pointer(ctx);
975 gpg_error_t rc;
976 struct argv_s *args[] = {
977 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
978 NULL
981 rc = parse_options(&line, args, client);
982 if (rc)
983 return send_error(ctx, rc);
985 if (client->opts&OPT_INQUIRE) {
986 guchar *result;
987 gsize len;
989 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
990 if (rc)
991 return send_error(ctx, rc);
993 line = (gchar *)result;
996 rc = do_get(ctx, line);
998 if (client->opts&OPT_INQUIRE)
999 xfree(line);
1001 return send_error(ctx, rc);
1004 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1005 gpg_error_t *rc, gchar **req_orig, void *data)
1007 gchar *path = *(gchar **)data;
1008 gchar *tmp = NULL, *result;
1010 if (path) {
1011 g_free(path);
1012 *(gchar **)data = NULL;
1015 path = g_strjoinv("\t", target);
1017 if (!path) {
1018 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1019 *rc = GPG_ERR_ENOMEM;
1020 return NULL;
1023 if (req_orig) {
1024 tmp = g_strjoinv("\t", req_orig);
1026 if (!tmp) {
1027 g_free(path);
1028 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1029 *rc = GPG_ERR_ENOMEM;
1030 return NULL;
1034 if (tmp && *tmp)
1035 result = g_strdup_printf("%s\t%s", path, tmp);
1036 else
1037 result = g_strdup(path);
1039 if (!result) {
1040 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1041 *rc = GPG_ERR_ENOMEM;
1042 g_free(path);
1043 g_free(tmp);
1044 return NULL;
1047 g_free(path);
1048 g_free(tmp);
1049 *(gchar **)data = result;
1050 return node;
1053 static void list_command_cleanup1(void *arg);
1054 static gpg_error_t do_realpath(assuan_context_t ctx, gchar *line)
1056 struct client_s *client = assuan_get_pointer(ctx);
1057 gpg_error_t rc;
1058 gchar **req;
1059 gchar *t;
1060 gint i;
1061 xmlNodePtr n;
1062 GString *string;
1063 gchar *rp = NULL;
1065 if (strchr(line, '\t') != NULL) {
1066 if ((req = split_input_line(line, "\t", 0)) == NULL)
1067 return GPG_ERR_SYNTAX;
1069 else {
1070 if ((req = split_input_line(line, " ", 0)) == NULL)
1071 return GPG_ERR_SYNTAX;
1074 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
1075 if (!n) {
1076 g_strfreev(req);
1077 return rc;
1080 rp = g_strjoinv("\t", req);
1081 if (!rp) {
1082 g_strfreev(req);
1083 return GPG_ERR_ENOMEM;
1086 if (req[1]) {
1087 n = find_elements(client->doc, n->children, req+1, &rc,
1088 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp, FALSE);
1089 if (!n) {
1090 g_free(rp);
1091 g_strfreev(req);
1092 return rc;
1096 string = g_string_new(rp);
1097 g_free(rp);
1098 g_strfreev(req);
1099 if (!string)
1100 return GPG_ERR_ENOMEM;
1102 again:
1103 for (i = 0, t = string->str + i; *t; t++, i++) {
1104 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1105 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1106 goto again;
1110 pthread_cleanup_push(list_command_cleanup1, string);
1111 rc = xfer_data(ctx, string->str, string->len);
1112 pthread_cleanup_pop(1);
1113 return rc;
1116 static gpg_error_t realpath_command(assuan_context_t ctx, gchar *line)
1118 gpg_error_t rc;
1119 struct client_s *client = assuan_get_pointer(ctx);
1120 struct argv_s *args[] = {
1121 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
1122 NULL
1125 rc = parse_options(&line, args, client);
1126 if (rc)
1127 return send_error(ctx, rc);
1129 if (client->opts&OPT_INQUIRE) {
1130 guchar *result;
1131 gsize len;
1133 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
1134 if (rc)
1135 return send_error(ctx, rc);
1137 line = (gchar *)result;
1140 rc = do_realpath(ctx, line);
1142 if (client->opts&OPT_INQUIRE)
1143 xfree(line);
1145 return send_error(ctx, rc);
1148 static void list_command_cleanup1(void *arg)
1150 g_string_free((GString *)arg, TRUE);
1153 static void list_command_cleanup2(void *arg)
1155 struct element_list_s *elements = arg;
1157 if (elements) {
1158 if (elements->list) {
1159 gint total = g_slist_length(elements->list);
1160 gint i;
1162 for (i = 0; i < total; i++) {
1163 gchar *tmp = g_slist_nth_data(elements->list, i);
1164 g_free(tmp);
1167 g_slist_free(elements->list);
1170 if (elements->prefix)
1171 g_free(elements->prefix);
1173 if (elements->req)
1174 g_strfreev(elements->req);
1176 g_free(elements);
1180 static gpg_error_t parse_list_opt_norecurse(gpointer data, gpointer value)
1182 struct client_s *client = data;
1184 client->opts &= ~(OPT_LIST_RECURSE);
1185 return 0;
1188 static gpg_error_t parse_list_opt_verbose(gpointer data, gpointer value)
1190 struct client_s *client = data;
1192 client->opts |= OPT_LIST_VERBOSE;
1193 return 0;
1196 static gpg_error_t parse_list_opt_all(gpointer data, gpointer value)
1198 struct client_s *client = data;
1200 client->opts |= OPT_LIST_ALL;
1201 return 0;
1204 static gpg_error_t list_path_once(struct client_s *client, gchar *line,
1205 struct element_list_s *elements, GString *result)
1207 gpg_error_t rc;
1209 elements->req = split_input_line(line, " ", 0);
1210 if (!elements->req)
1211 strv_printf(&elements->req, "%s", line);
1213 rc = create_path_list(client->doc, elements, *elements->req);
1214 if (!rc) {
1215 if (elements) {
1216 gint total = g_slist_length(elements->list);
1217 gint i;
1219 if (!total)
1220 rc = GPG_ERR_NO_DATA;
1222 if (!rc) {
1223 if (!rc) {
1224 for (i = 0; i < total; i++) {
1225 gchar *tmp = g_slist_nth_data(elements->list, i);
1227 g_string_append_printf(result, "%s%s", tmp,
1228 i+1 == total ? "" : "\n");
1233 else
1234 rc = GPG_ERR_NO_DATA;
1237 return rc;
1240 static gpg_error_t do_list(assuan_context_t ctx, gchar *line)
1242 struct client_s *client = assuan_get_pointer(ctx);
1243 gpg_error_t rc;
1244 struct element_list_s *elements = NULL;
1246 elements = g_malloc0(sizeof(struct element_list_s));
1247 if (!elements)
1248 return GPG_ERR_ENOMEM;
1250 elements->recurse = client->opts&OPT_LIST_RECURSE;
1251 elements->verbose = client->opts&OPT_LIST_VERBOSE;
1253 if (!line || !*line) {
1254 GString *str;
1256 pthread_cleanup_push(list_command_cleanup2, elements);
1257 rc = list_root_elements(client->doc, &str, elements->verbose);
1258 pthread_cleanup_pop(1);
1259 pthread_cleanup_push(list_command_cleanup1, str);
1261 if (!rc) {
1262 if (client->opts&OPT_LIST_ALL) {
1263 gchar **roots = split_input_line(str->str, "\n", 0);
1264 gchar **p;
1266 pthread_cleanup_push(req_cleanup, roots);
1267 g_string_truncate(str, 0);
1269 for (p = roots; *p; p++) {
1270 elements = g_malloc0(sizeof(struct element_list_s));
1271 if (!elements) {
1272 rc = GPG_ERR_ENOMEM;
1273 break;
1276 elements->recurse = client->opts&OPT_LIST_RECURSE;
1277 elements->verbose = client->opts&OPT_LIST_VERBOSE;
1278 pthread_cleanup_push(list_command_cleanup2, elements);
1279 rc = list_path_once(client, *p, elements, str);
1280 pthread_cleanup_pop(1);
1281 if (rc)
1282 break;
1284 if (*(p+1))
1285 g_string_append(str, "\n");
1288 pthread_cleanup_pop(1);
1291 if (!rc)
1292 rc = xfer_data(ctx, str->str, str->len);
1295 pthread_cleanup_pop(1);
1296 return rc;
1299 pthread_cleanup_push(list_command_cleanup2, elements);
1300 GString *str = g_string_new(NULL);
1301 pthread_cleanup_push(list_command_cleanup1, str);
1302 rc = list_path_once(client, line, elements, str);
1303 if (!rc)
1304 rc = xfer_data(ctx, str->str, str->len);
1306 pthread_cleanup_pop(1);
1307 pthread_cleanup_pop(1);
1308 return rc;
1311 static gpg_error_t list_command(assuan_context_t ctx, gchar *line)
1313 struct client_s *client = assuan_get_pointer(ctx);
1314 gpg_error_t rc;
1315 struct argv_s *args[] = {
1316 &(struct argv_s) { "no-recurse", OPTION_TYPE_NOARG, parse_list_opt_norecurse },
1317 &(struct argv_s) { "verbose", OPTION_TYPE_NOARG, parse_list_opt_verbose },
1318 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
1319 &(struct argv_s) { "all", OPTION_TYPE_NOARG, parse_list_opt_all },
1320 NULL
1323 if (disable_list_and_dump == TRUE)
1324 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1326 client->opts |= OPT_LIST_RECURSE;
1327 rc = parse_options(&line, args, client);
1328 if (rc)
1329 return send_error(ctx, rc);
1331 if (client->opts&OPT_INQUIRE) {
1332 guchar *result;
1333 gsize len;
1335 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
1336 if (rc)
1337 return send_error(ctx, rc);
1339 line = (gchar *)result;
1342 rc = do_list(ctx, line);
1344 if (client->opts&OPT_INQUIRE)
1345 xfree(line);
1347 return send_error(ctx, rc);
1351 * req[0] - element path
1353 static gpg_error_t attribute_list(assuan_context_t ctx, gchar **req)
1355 struct client_s *client = assuan_get_pointer(ctx);
1356 gchar **attrlist = NULL;
1357 gint i = 0;
1358 gchar **path = NULL;
1359 xmlAttrPtr a;
1360 xmlNodePtr n, an;
1361 gchar *line;
1362 gpg_error_t rc;
1364 if (!req || !req[0])
1365 return GPG_ERR_SYNTAX;
1367 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1369 * The first argument may be only a root element.
1371 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1372 return GPG_ERR_SYNTAX;
1375 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
1377 if (!n) {
1378 g_strfreev(path);
1379 return rc;
1382 if (path[1]) {
1383 n = find_elements(client->doc, n->children, path+1, &rc,
1384 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1386 if (!n) {
1387 g_strfreev(path);
1388 return rc;
1392 g_strfreev(path);
1394 for (a = n->properties; a; a = a->next) {
1395 gchar **pa;
1397 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1398 if (attrlist)
1399 g_strfreev(attrlist);
1401 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1402 return GPG_ERR_ENOMEM;
1405 attrlist = pa;
1406 an = a->children;
1407 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name,
1408 an && an->content ? (gchar *)an->content : "");
1410 if (!attrlist[i]) {
1411 g_strfreev(attrlist);
1412 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1413 return GPG_ERR_ENOMEM;
1416 attrlist[++i] = NULL;
1419 if (!attrlist)
1420 return GPG_ERR_NO_DATA;
1422 line = g_strjoinv("\n", attrlist);
1424 if (!line) {
1425 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1426 g_strfreev(attrlist);
1427 return GPG_ERR_ENOMEM;
1430 pthread_cleanup_push(g_free, line);
1431 pthread_cleanup_push(req_cleanup, attrlist);
1432 rc = xfer_data(ctx, line, strlen(line));
1433 pthread_cleanup_pop(1);
1434 pthread_cleanup_pop(1);
1435 return rc;
1439 * req[0] - attribute
1440 * req[1] - element path
1442 static gpg_error_t attribute_delete(struct client_s *client, gchar **req)
1444 xmlNodePtr n;
1445 gchar **path = NULL;
1446 gpg_error_t rc;
1448 if (!req || !req[0] || !req[1])
1449 return GPG_ERR_SYNTAX;
1451 if (!g_strcmp0(req[0], "_name"))
1452 return GPG_ERR_INV_ATTR;
1454 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1456 * The first argument may be only a root element.
1458 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1459 return GPG_ERR_SYNTAX;
1462 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
1463 if (!n)
1464 goto fail;
1466 if (path[1]) {
1467 n = find_elements(client->doc, n->children, path+1, &rc,
1468 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1469 if (!n)
1470 goto fail;
1473 rc = delete_attribute(n, (xmlChar *)req[0]);
1475 fail:
1476 g_strfreev(path);
1477 return rc;
1480 static xmlNodePtr create_element_path(struct client_s *client,
1481 gchar ***elements, gpg_error_t *rc, xmlNodePtr parent)
1483 gchar **req = *elements;
1484 gchar **req_orig = g_strdupv(req);
1485 xmlNodePtr n = NULL;
1487 *rc = 0;
1489 if (!req_orig) {
1490 *rc = GPG_ERR_ENOMEM;
1491 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1492 goto fail;
1495 again:
1496 n = find_root_element(client->doc, &req, rc, NULL, 0, FALSE);
1497 if (!n) {
1498 if (*rc != GPG_ERR_ELEMENT_NOT_FOUND)
1499 goto fail;
1501 *rc = new_root_element(client->doc, req[0]);
1502 if (*rc)
1503 goto fail;
1505 goto again;
1507 else if (n == parent) {
1508 *rc = GPG_ERR_CONFLICT;
1509 goto fail;
1512 if (req[1]) {
1513 if (!n->children)
1514 n = create_target_elements_cb(n, req+1, rc, NULL);
1515 else
1516 n = find_elements(client->doc, n->children, req+1, rc, NULL, NULL,
1517 create_target_elements_cb, FALSE, 0, parent, FALSE);
1519 if (!n)
1520 goto fail;
1523 * Reset the position of the element tree now that the elements
1524 * have been created.
1526 g_strfreev(req);
1527 req = req_orig;
1528 req_orig = NULL;
1529 n = find_root_element(client->doc, &req, rc, NULL, 0, FALSE);
1530 if (!n)
1531 goto fail;
1533 n = find_elements(client->doc, n->children, req+1, rc,
1534 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1535 if (!n)
1536 goto fail;
1539 fail:
1540 if (req_orig)
1541 g_strfreev(req_orig);
1543 *elements = req;
1544 return n;
1548 * Creates a "target" attribute. When other commands encounter an element with
1549 * this attribute, the element path is modified to the target value. If the
1550 * source element path doesn't exist when using 'ATTR SET target', it is
1551 * created, but the destination element path must exist.
1553 * req[0] - source element path
1554 * req[1] - destination element path
1556 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1558 gchar **src, **dst, *line = NULL, **odst = NULL;
1559 gpg_error_t rc;
1560 xmlNodePtr n;
1562 if (!req || !req[0] || !req[1])
1563 return GPG_ERR_SYNTAX;
1565 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1567 * The first argument may be only a root element.
1569 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1570 return GPG_ERR_SYNTAX;
1573 if (!valid_element_path(src, FALSE))
1574 return GPG_ERR_INV_VALUE;
1576 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1578 * The first argument may be only a root element.
1580 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1581 rc = GPG_ERR_SYNTAX;
1582 goto fail;
1586 odst = g_strdupv(dst);
1588 if (!odst) {
1589 rc = GPG_ERR_ENOMEM;
1590 goto fail;
1593 n = find_root_element(client->doc, &dst, &rc, NULL, 0, FALSE);
1596 * Make sure the destination element path exists.
1598 if (!n)
1599 goto fail;
1601 if (dst[1]) {
1602 n = find_elements(client->doc, n->children, dst+1, &rc,
1603 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1605 if (!n)
1606 goto fail;
1609 n = create_element_path(client, &src, &rc, NULL);
1611 if (rc)
1612 goto fail;
1614 line = g_strjoinv("\t", odst);
1616 if (!line) {
1617 rc = GPG_ERR_ENOMEM;
1618 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1619 goto fail;
1622 rc = add_attribute(n, "target", line);
1624 fail:
1625 g_free(line);
1626 g_strfreev(src);
1627 g_strfreev(dst);
1628 g_strfreev(odst);
1629 return rc;
1633 * req[0] - attribute
1634 * req[1] - element path
1636 static gpg_error_t attribute_get(assuan_context_t ctx, gchar **req)
1638 struct client_s *client = assuan_get_pointer(ctx);
1639 xmlNodePtr n;
1640 xmlChar *a;
1641 gchar **path= NULL;
1642 gpg_error_t rc;
1644 if (!req || !req[0] || !req[1])
1645 return GPG_ERR_SYNTAX;
1647 if (strchr(req[1], '\t')) {
1648 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1649 return GPG_ERR_SYNTAX;
1651 else {
1652 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1653 return GPG_ERR_SYNTAX;
1656 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
1658 if (!n)
1659 goto fail;
1661 if (path[1]) {
1662 n = find_elements(client->doc, n->children, path+1, &rc,
1663 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1665 if (!n)
1666 goto fail;
1669 g_strfreev(path);
1671 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1672 return GPG_ERR_NOT_FOUND;
1674 pthread_cleanup_push(xmlFree, a);
1676 if (*a)
1677 rc = xfer_data(ctx, (gchar *)a, xmlStrlen(a));
1678 else
1679 rc = GPG_ERR_NO_DATA;
1681 pthread_cleanup_pop(1);
1682 return rc;
1684 fail:
1685 g_strfreev(path);
1686 return rc;
1690 * req[0] - attribute
1691 * req[1] - element path
1692 * req[2] - value
1694 static gpg_error_t attribute_set(struct client_s *client, gchar **req)
1696 gchar **path = NULL;
1697 gpg_error_t rc;
1698 xmlNodePtr n;
1700 if (!req || !req[0] || !req[1])
1701 return GPG_ERR_SYNTAX;
1704 * Reserved attribute names.
1706 if (!g_strcmp0(req[0], "_name"))
1707 return GPG_ERR_INV_ATTR;
1708 else if (!g_strcmp0(req[0], "target"))
1709 return target_attribute(client, req + 1);
1711 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1713 * The first argument may be only a root element.
1715 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1716 return GPG_ERR_SYNTAX;
1719 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
1721 if (!n)
1722 goto fail;
1724 if (path[1]) {
1725 n = find_elements(client->doc, n->children, path+1, &rc,
1726 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1728 if (!n)
1729 goto fail;
1732 rc = add_attribute(n, req[0], req[2]);
1734 fail:
1735 g_strfreev(path);
1736 return rc;
1740 * req[0] - command
1741 * req[1] - attribute name or element path if command is LIST
1742 * req[2] - element path
1743 * req[2] - element path or value
1746 static gpg_error_t do_attr(assuan_context_t ctx, gchar *line)
1748 struct client_s *client = assuan_get_pointer(ctx);
1749 gpg_error_t rc = 0;
1750 gchar **req;
1752 req = split_input_line(line, " ", 4);
1753 if (!req || !req[0] || !req[1]) {
1754 g_strfreev(req);
1755 return GPG_ERR_SYNTAX;
1758 pthread_cleanup_push(req_cleanup, req);
1760 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1761 rc = attribute_set(client, req+1);
1762 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
1763 rc = attribute_get(ctx, req+1);
1764 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1765 rc = attribute_delete(client, req+1);
1766 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1767 rc = attribute_list(ctx, req+1);
1768 else
1769 rc = GPG_ERR_SYNTAX;
1771 pthread_cleanup_pop(1);
1772 return rc;
1775 static gpg_error_t attr_command(assuan_context_t ctx, gchar *line)
1777 struct client_s *client = assuan_get_pointer(ctx);
1778 gpg_error_t rc;
1779 struct argv_s *args[] = {
1780 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
1781 NULL
1784 rc = parse_options(&line, args, client);
1785 if (rc)
1786 return send_error(ctx, rc);
1788 if (client->opts&OPT_INQUIRE) {
1789 guchar *result;
1790 gsize len;
1792 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
1793 if (rc)
1794 return send_error(ctx, rc);
1796 line = (gchar *)result;
1799 rc = do_attr(ctx, line);
1801 if (client->opts&OPT_INQUIRE)
1802 xfree(line);
1804 return send_error(ctx, rc);
1807 static gpg_error_t parse_iscached_opt_lock(gpointer data, gpointer value)
1809 struct client_s *client = data;
1811 (void)value;
1812 client->opts |= OPT_LOCK;
1813 return 0;
1816 static gpg_error_t iscached_command(assuan_context_t ctx, gchar *line)
1818 struct client_s *client = assuan_get_pointer(ctx);
1819 gpg_error_t rc;
1820 guchar md5file[16];
1821 struct argv_s *args[] = {
1822 &(struct argv_s) { "lock", OPTION_TYPE_NOARG, parse_iscached_opt_lock },
1823 NULL
1826 if (!line || !*line)
1827 return send_error(ctx, GPG_ERR_SYNTAX);
1829 rc = parse_options(&line, args, client);
1830 if (rc)
1831 return send_error(ctx, rc);
1832 else if (!valid_filename(line))
1833 return send_error(ctx, GPG_ERR_INV_VALUE);
1835 rc = cache_iscached(line);
1836 if (client->opts&OPT_LOCK && (!rc || gpg_err_code(rc) == GPG_ERR_NO_DATA)) {
1837 gpg_error_t trc = rc;
1838 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, line, strlen(line));
1839 if (memcmp(md5file, client->md5file, 16))
1840 cleanup_client(client);
1842 memcpy(client->md5file, md5file, 16);
1843 rc = do_lock(client, TRUE);
1844 if (!rc)
1845 rc = trc;
1848 return send_error(ctx, rc);
1851 static gpg_error_t clearcache_command(assuan_context_t ctx, gchar *line)
1853 gpg_error_t rc;
1854 guchar md5file[16];
1856 if (!line || !*line)
1857 rc = cache_clear(NULL);
1858 else {
1859 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, line, strlen(line));
1860 rc = cache_clear(md5file);
1863 return send_error(ctx, rc);
1866 static gpg_error_t cachetimeout_command(assuan_context_t ctx, gchar *line)
1868 guchar md5file[16];
1869 gint timeout;
1870 gchar **req = split_input_line(line, " ", 0);
1871 gchar *p;
1872 gpg_error_t rc = 0;
1874 if (!req || !*req || !req[1]) {
1875 g_strfreev(req);
1876 return send_error(ctx, GPG_ERR_SYNTAX);
1879 errno = 0;
1880 timeout = g_ascii_strtoll(req[1], &p, 10);
1881 if (errno != 0 || *p != 0 || timeout < -1) {
1882 g_strfreev(req);
1883 return send_error(ctx, GPG_ERR_SYNTAX);
1886 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
1888 if (cache_set_timeout(md5file, timeout)) {
1889 MUTEX_LOCK(&rcfile_mutex);
1890 g_key_file_set_integer(keyfileh, req[0], "cache_timeout", timeout);
1891 MUTEX_UNLOCK(&rcfile_mutex);
1893 else
1894 rc = GPG_ERR_NOT_FOUND;
1896 g_strfreev(req);
1897 return send_error(ctx, rc);
1900 static gpg_error_t dump_command(assuan_context_t ctx, gchar *line)
1902 xmlChar *xml;
1903 gint len;
1904 struct client_s *client = assuan_get_pointer(ctx);
1905 gpg_error_t rc;
1907 if (disable_list_and_dump == TRUE)
1908 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1910 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
1912 if (!xml) {
1913 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1914 return send_error(ctx, GPG_ERR_ENOMEM);
1917 pthread_cleanup_push(xmlFree, xml);
1918 rc = xfer_data(ctx, (gchar *)xml, len);
1919 pthread_cleanup_pop(1);
1920 return send_error(ctx, rc);
1923 static gpg_error_t getconfig_command(assuan_context_t ctx, gchar *line)
1925 struct client_s *client = assuan_get_pointer(ctx);
1926 gpg_error_t rc = 0;
1927 gchar filename[255]={0}, param[747]={0};
1928 gchar *p, *tmp = NULL, *fp = client->filename, *paramp = line;
1930 if (!line || !*line)
1931 return send_error(ctx, GPG_ERR_SYNTAX);
1933 if (strchr(line, ' ')) {
1934 sscanf(line, " %254[^ ] %746c", filename, param);
1935 paramp = param;
1936 fp = filename;
1939 if (fp && !valid_filename(fp))
1940 return send_error(ctx, GPG_ERR_INV_VALUE);
1942 paramp = g_ascii_strdown(paramp, -1);
1943 if (!paramp) {
1944 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1945 return send_error(ctx, GPG_ERR_ENOMEM);
1948 if (!g_strcmp0(paramp, "cipher") && fp) {
1949 struct crypto_s *crypto;
1951 rc = init_client_crypto(&crypto);
1952 if (!rc) {
1953 rc = read_data_header(fp, &crypto->hdr, NULL, NULL);
1954 if (!rc) {
1955 const char *t = gcry_cipher_algo_name(cipher_to_gcrypt(crypto->hdr.flags));
1956 if (t)
1957 tmp = g_strdup(t);
1961 cleanup_crypto(&crypto);
1962 if (rc && gpg_err_code(rc) != GPG_ERR_ENOENT)
1963 return send_error(ctx, rc);
1965 if (!rc && tmp)
1966 goto done;
1969 p = get_key_file_string(fp ? fp : "global", paramp);
1970 g_free(paramp);
1971 if (!p)
1972 return send_error(ctx, GPG_ERR_UNKNOWN_OPTION);
1974 tmp = expand_homedir(p);
1975 g_free(p);
1976 if (!tmp) {
1977 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1978 return send_error(ctx, GPG_ERR_ENOMEM);
1981 done:
1982 p = tmp;
1983 pthread_cleanup_push(g_free, p);
1984 rc = xfer_data(ctx, p, strlen(p));
1985 pthread_cleanup_pop(1);
1986 return send_error(ctx, rc);
1989 struct xpath_s {
1990 xmlXPathContextPtr xp;
1991 xmlXPathObjectPtr result;
1992 xmlBufferPtr buf;
1993 gchar **req;
1996 static void xpath_command_cleanup(void *arg)
1998 struct xpath_s *xpath = arg;
2000 if (!xpath)
2001 return;
2003 req_cleanup(xpath->req);
2005 if (xpath->buf)
2006 xmlBufferFree(xpath->buf);
2008 if (xpath->result)
2009 xmlXPathFreeObject(xpath->result);
2011 if (xpath->xp)
2012 xmlXPathFreeContext(xpath->xp);
2015 static gpg_error_t do_xpath(assuan_context_t ctx, gchar *line)
2017 gpg_error_t rc;
2018 struct client_s *client = assuan_get_pointer(ctx);
2019 struct xpath_s _x = {0};
2020 struct xpath_s *xpath = &_x;
2022 if ((xpath->req = split_input_line(line, "\t", 2)) == NULL) {
2023 if (strv_printf(&xpath->req, "%s", line) == FALSE)
2024 return GPG_ERR_ENOMEM;
2027 xpath->xp = xmlXPathNewContext(client->doc);
2028 if (!xpath->xp) {
2029 rc = GPG_ERR_BAD_DATA;
2030 goto fail;
2033 xpath->result = xmlXPathEvalExpression((xmlChar *)xpath->req[0], xpath->xp);
2034 if (!xpath->result) {
2035 rc = GPG_ERR_BAD_DATA;
2036 goto fail;
2039 if (xmlXPathNodeSetIsEmpty(xpath->result->nodesetval)) {
2040 rc = GPG_ERR_ELEMENT_NOT_FOUND;
2041 goto fail;
2044 rc = recurse_xpath_nodeset(client->doc, xpath->result->nodesetval,
2045 (xmlChar *)xpath->req[1], &xpath->buf, 0, NULL);
2046 if (rc)
2047 goto fail;
2048 else if (!xpath->req[1] && !xmlBufferLength(xpath->buf)) {
2049 rc = GPG_ERR_NO_DATA;
2050 goto fail;
2052 else if (xpath->req[1]) {
2053 rc = 0;
2054 goto fail;
2057 pthread_cleanup_push(xpath_command_cleanup, &xpath);
2058 rc = xfer_data(ctx, (gchar *)xmlBufferContent(xpath->buf),
2059 xmlBufferLength(xpath->buf));
2060 pthread_cleanup_pop(0);
2061 fail:
2062 xpath_command_cleanup(xpath);
2063 return rc;
2066 static gpg_error_t xpath_command(assuan_context_t ctx, gchar *line)
2068 struct client_s *client = assuan_get_pointer(ctx);
2069 gpg_error_t rc;
2070 struct argv_s *args[] = {
2071 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
2072 NULL
2075 if (disable_list_and_dump == TRUE)
2076 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2078 rc = parse_options(&line, args, client);
2079 if (rc)
2080 return send_error(ctx, rc);
2082 if (client->opts&OPT_INQUIRE) {
2083 guchar *result;
2084 gsize len;
2086 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
2087 if (rc)
2088 return send_error(ctx, rc);
2090 line = (gchar *)result;
2093 if (!line || !*line)
2094 rc = GPG_ERR_SYNTAX;
2096 if (!rc)
2097 rc = do_xpath(ctx, line);
2099 if (client->opts&OPT_INQUIRE)
2100 xfree(line);
2102 return send_error(ctx, rc);
2105 static gpg_error_t do_xpathattr(assuan_context_t ctx, gchar *line)
2107 struct client_s *client = assuan_get_pointer(ctx);
2108 gpg_error_t rc;
2109 gchar **req = NULL;
2110 gboolean cmd = FALSE; //SET
2111 struct xpath_s _x = {0};
2112 struct xpath_s *xpath = &_x;
2114 if ((req = split_input_line(line, " ", 3)) == NULL)
2115 return GPG_ERR_ENOMEM;
2117 if (!req[0]) {
2118 rc = GPG_ERR_SYNTAX;
2119 goto fail;
2122 if (!g_ascii_strcasecmp(req[0], "SET"))
2123 cmd = FALSE;
2124 else if (!g_ascii_strcasecmp(req[0], "DELETE"))
2125 cmd = TRUE;
2126 else {
2127 rc = GPG_ERR_SYNTAX;
2128 goto fail;
2131 if (!req[1] || !req[2]) {
2132 rc = GPG_ERR_SYNTAX;
2133 goto fail;
2136 if ((xpath->req = split_input_line(req[2], "\t", 3)) == NULL) {
2137 rc = GPG_ERR_ENOMEM;
2138 goto fail;
2141 if (!xpath->req[0] || (!xpath->req[1] && !cmd) || (xpath->req[1] && cmd)) {
2142 rc = GPG_ERR_SYNTAX;
2143 goto fail;
2146 xpath->xp = xmlXPathNewContext(client->doc);
2147 if (!xpath->xp) {
2148 rc = GPG_ERR_BAD_DATA;
2149 goto fail;
2152 xpath->result = xmlXPathEvalExpression((xmlChar *)xpath->req[0], xpath->xp);
2153 if (!xpath->result) {
2154 rc = GPG_ERR_BAD_DATA;
2155 goto fail;
2158 if (xmlXPathNodeSetIsEmpty(xpath->result->nodesetval)) {
2159 rc = GPG_ERR_ELEMENT_NOT_FOUND;
2160 goto fail;
2163 rc = recurse_xpath_nodeset(client->doc, xpath->result->nodesetval,
2164 (xmlChar *)xpath->req[1], &xpath->buf, cmd, (xmlChar *)req[1]);
2166 fail:
2167 xpath_command_cleanup(xpath);
2168 g_strfreev(req);
2169 return rc;
2172 /* XPATHATTR SET|DELETE <name> <expression>[<TAB>[value]] */
2173 static gpg_error_t xpathattr_command(assuan_context_t ctx, gchar *line)
2175 struct client_s *client = assuan_get_pointer(ctx);
2176 gpg_error_t rc;
2177 struct argv_s *args[] = {
2178 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
2179 NULL
2182 if (disable_list_and_dump == TRUE)
2183 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2185 rc = parse_options(&line, args, client);
2186 if (rc)
2187 return send_error(ctx, rc);
2189 if (client->opts&OPT_INQUIRE) {
2190 guchar *result;
2191 gsize len;
2193 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
2194 if (rc)
2195 return send_error(ctx, rc);
2197 line = (gchar *)result;
2200 if (!line || !*line)
2201 rc = GPG_ERR_SYNTAX;
2203 if (!rc)
2204 rc = do_xpathattr(ctx, line);
2206 if (client->opts&OPT_INQUIRE)
2207 xfree(line);
2209 return send_error(ctx, rc);
2212 static gpg_error_t do_import(struct client_s *client, guchar *line)
2214 gchar **req, **path = NULL, **path_orig = NULL, *content;
2215 xmlDocPtr doc = NULL;
2216 xmlNodePtr n, root, copy;
2217 gpg_error_t rc;
2219 req = split_input_line((gchar *)line, "\t", 2);
2220 xfree(line);
2221 if (!req || !*req)
2222 return GPG_ERR_SYNTAX;
2224 content = req[0];
2225 path = split_input_line(req[1], "\t", 0);
2226 if (!content || !*content) {
2227 rc = GPG_ERR_SYNTAX;
2228 goto fail;
2231 if (path && !valid_element_path(path, FALSE)) {
2232 rc = GPG_ERR_INV_VALUE;
2233 goto fail;
2236 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2237 if (!doc) {
2238 rc = GPG_ERR_BAD_DATA;
2239 goto fail;
2242 root = xmlDocGetRootElement(doc);
2243 rc = validate_import(root);
2244 if (rc)
2245 goto fail;
2247 if (path) {
2248 path_orig = g_strdupv(path);
2249 if (!path_orig) {
2250 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2251 rc = GPG_ERR_ENOMEM;
2252 goto fail;
2255 xmlChar *a = xmlGetProp(root, (xmlChar *)"_name");
2256 if (!a) {
2257 g_strfreev(path_orig);
2258 rc = GPG_ERR_ENOMEM;
2259 goto fail;
2262 if (strv_printf(&path, "%s", (gchar *)a) == FALSE) {
2263 xmlFree(a);
2264 g_strfreev(path_orig);
2265 rc = GPG_ERR_ENOMEM;
2266 goto fail;
2269 xmlFree(a);
2270 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
2272 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
2273 g_strfreev(path_orig);
2274 goto fail;
2277 if (!rc) {
2278 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, TRUE);
2280 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
2281 g_strfreev(path_orig);
2282 goto fail;
2284 else if (!rc) {
2285 xmlNodePtr parent = n->parent;
2287 xmlUnlinkNode(n);
2288 xmlFreeNode(n);
2289 n = parent;
2293 g_strfreev(path);
2294 path = path_orig;
2296 if (rc == GPG_ERR_ELEMENT_NOT_FOUND) {
2297 n = create_element_path(client, &path, &rc, NULL);
2299 if (rc)
2300 goto fail;
2303 copy = xmlCopyNodeList(root);
2304 n = xmlAddChildList(n, copy);
2305 if (!n)
2306 rc = GPG_ERR_BAD_DATA;
2308 else {
2309 /* Check if the content root element can create a DTD root element. */
2310 if (!xmlStrEqual((xmlChar *)"element", root->name)) {
2311 rc = GPG_ERR_SYNTAX;
2312 goto fail;
2315 xmlChar *a;
2317 if ((a = xmlGetProp(root, (xmlChar *)"_name")) == NULL) {
2318 rc = GPG_ERR_SYNTAX;
2319 goto fail;
2322 gchar *tmp = g_strdup((gchar *)a);
2323 xmlFree(a);
2324 gboolean literal = is_literal_element(&tmp);
2326 if (!valid_xml_element((xmlChar *)tmp) || literal) {
2327 g_free(tmp);
2328 rc = GPG_ERR_INV_VALUE;
2329 goto fail;
2332 if (strv_printf(&path, "%s", tmp) == FALSE) {
2333 g_free(tmp);
2334 rc = GPG_ERR_ENOMEM;
2335 goto fail;
2338 g_free(tmp);
2339 n = find_root_element(client->doc, &path, &rc, NULL, 0, TRUE);
2341 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
2342 rc = GPG_ERR_BAD_DATA;
2343 goto fail;
2346 /* Overwriting the existing tree. */
2347 if (!rc) {
2348 xmlUnlinkNode(n);
2349 xmlFreeNodeList(n);
2352 rc = 0;
2353 xmlSetProp(root, (xmlChar *)"_name", (xmlChar *)path[0]);
2354 n = xmlCopyNode(root, 1);
2355 n = xmlAddChildList(xmlDocGetRootElement(client->doc), n);
2358 if (n && !rc)
2359 rc = update_element_mtime(n->parent);
2361 fail:
2362 if (doc)
2363 xmlFreeDoc(doc);
2365 if (path)
2366 g_strfreev(path);
2368 g_strfreev(req);
2369 return rc;
2372 static gpg_error_t import_command(assuan_context_t ctx, gchar *line)
2374 gpg_error_t rc;
2375 struct client_s *client = assuan_get_pointer(ctx);
2376 guchar *result;
2377 gsize len;
2379 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
2380 if (rc)
2381 return send_error(ctx, rc);
2383 rc = do_import(client, result);
2384 return send_error(ctx, rc);
2387 static gpg_error_t do_lock(struct client_s *client, gboolean add)
2389 gpg_error_t rc = lock_file_mutex(client, add);
2391 if (!rc)
2392 client->flags |= FLAG_LOCK_CMD;
2394 return rc;
2397 static gpg_error_t lock_command(assuan_context_t ctx, gchar *line)
2399 struct client_s *client = assuan_get_pointer(ctx);
2400 gpg_error_t rc = do_lock(client, FALSE);
2402 return send_error(ctx, rc);
2405 static gpg_error_t unlock_command(assuan_context_t ctx, gchar *line)
2407 struct client_s *client = assuan_get_pointer(ctx);
2408 gpg_error_t rc;
2410 rc = unlock_file_mutex(client, FALSE);
2411 return send_error(ctx, rc);
2414 static gpg_error_t option_command(assuan_context_t ctx, const gchar *name,
2415 const gchar *value)
2417 struct client_s *client = assuan_get_pointer(ctx);
2418 struct agent_s *agent = client->crypto->agent;
2419 gpg_error_t rc = 0;
2421 log_write1("OPTION name='%s' value='%s'", name, value);
2423 if (g_ascii_strcasecmp(name, (gchar *)"log_level") == 0) {
2424 glong l = 0;
2426 if (value) {
2427 l = strtol(value, NULL, 10);
2429 if (l < 0 || l > 2)
2430 return gpg_error(GPG_ERR_INV_VALUE);
2433 MUTEX_LOCK(&rcfile_mutex);
2434 g_key_file_set_integer(keyfileh, "global", "log_level", (gint)l);
2435 MUTEX_UNLOCK(&rcfile_mutex);
2436 goto done;
2438 else if (g_ascii_strcasecmp(name, (gchar *)"rc_on_locked") == 0) {
2439 glong l = 0;
2441 if (value) {
2442 l = strtol(value, NULL, 10);
2444 if (l < 0 || l > 1)
2445 return gpg_error(GPG_ERR_INV_VALUE);
2448 if (l)
2449 client->flags |= FLAG_RC_ON_LOCKED;
2450 else
2451 client->flags &= ~(FLAG_RC_ON_LOCKED);
2452 goto done;
2454 else if (g_ascii_strcasecmp(name, (gchar *)"NAME") == 0) {
2455 gchar *tmp = pthread_getspecific(thread_name_key);
2457 if (tmp)
2458 g_free(tmp);
2460 if (!value || !*value) {
2461 pthread_setspecific(thread_name_key, g_strdup(""));
2462 goto done;
2465 pthread_setspecific(thread_name_key, g_strdup(value));
2466 goto done;
2468 else if (g_ascii_strcasecmp(name, (gchar *)"lc-messages") == 0) {
2469 rc = set_agent_option(client->crypto->agent, "lc-messages", value);
2470 if (!rc) {
2471 g_free(agent->lc_messages);
2472 agent->lc_messages = g_strdup(value);
2475 else if (g_ascii_strcasecmp(name, (gchar *)"lc-ctype") == 0) {
2476 rc = set_agent_option(client->crypto->agent, "lc-ctype", value);
2477 if (!rc) {
2478 g_free(agent->lc_ctype);
2479 agent->lc_ctype = g_strdup(value);
2482 else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0) {
2483 rc = set_agent_option(client->crypto->agent, "ttyname", value);
2484 if (!rc) {
2485 g_free(agent->ttyname);
2486 agent->ttyname = g_strdup(value);
2489 else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0) {
2490 rc = set_agent_option(client->crypto->agent, "ttytype", value);
2491 if (!rc) {
2492 g_free(agent->ttytype);
2493 agent->ttytype = g_strdup(value);
2496 else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0) {
2497 rc = set_agent_option(client->crypto->agent, "display", value);
2498 if (!rc) {
2499 g_free(agent->display);
2500 agent->display = g_strdup(value);
2503 else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0) {
2504 if (client->crypto->agent->desc)
2505 g_free(client->crypto->agent->desc);
2507 client->crypto->agent->desc = g_strdup(value);
2508 if (!client->crypto->agent->desc)
2509 rc = GPG_ERR_ENOMEM;
2511 #if 0
2512 else if (g_ascii_strcasecmp(name, "pinentry_timeout") == 0) {
2513 gchar *p = NULL;
2514 gint n;
2516 if (!value)
2517 goto done;
2519 n = strtol(value, &p, 10);
2521 if (*p || n < 0)
2522 return gpg_error(GPG_ERR_INV_VALUE);
2524 MUTEX_LOCK(&rcfile_mutex);
2525 g_key_file_set_integer(keyfileh, client->filename ? client->filename :
2526 "global", "pinentry_timeout", n);
2527 MUTEX_UNLOCK(&rcfile_mutex);
2528 goto done;
2530 #endif
2531 else
2532 return gpg_error(GPG_ERR_UNKNOWN_OPTION);
2534 done:
2535 return rc;
2538 static gpg_error_t do_rename(assuan_context_t ctx, gchar *line)
2540 struct client_s *client = assuan_get_pointer(ctx);
2541 gpg_error_t rc;
2542 gchar **req, **src, *dst;
2543 xmlNodePtr n, ndst;
2545 req = split_input_line(line, " ", -1);
2547 if (!req || !req[0] || !req[1]) {
2548 g_strfreev(req);
2549 return GPG_ERR_SYNTAX;
2552 dst = req[1];
2553 is_literal_element(&dst);
2555 if (!valid_xml_element((xmlChar *)dst)) {
2556 g_strfreev(req);
2557 return GPG_ERR_INV_VALUE;
2560 if (strchr(req[0], '\t'))
2561 src = split_input_line(req[0], "\t", -1);
2562 else
2563 src = split_input_line(req[0], " ", -1);
2565 if (!src || !*src) {
2566 rc = GPG_ERR_SYNTAX;
2567 goto fail;
2570 n = find_root_element(client->doc, &src, &rc, NULL, 0, FALSE);
2571 if (src[1] && n)
2572 n = find_elements(client->doc, n->children, src+1, &rc, NULL, NULL,
2573 NULL, FALSE, 0, NULL, FALSE);
2575 if (!n)
2576 goto fail;
2578 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
2579 if (!a) {
2580 rc = GPG_ERR_ENOMEM;
2581 goto fail;
2584 /* To prevent unwanted effects:
2586 * <root name="a"><b/></root>
2588 * RENAME a<TAB>b b
2590 if (xmlStrEqual(a, (xmlChar *)dst)) {
2591 xmlFree(a);
2592 rc = GPG_ERR_AMBIGUOUS_NAME;
2593 goto fail;
2596 xmlFree(a);
2597 gchar **tmp = NULL;
2598 if (src[1]) {
2599 gchar **p;
2601 for (p = src; *p; p++) {
2602 if (!*(p+1))
2603 break;
2604 strv_printf(&tmp, "%s", *p);
2608 strv_printf(&tmp, "!%s", dst);
2609 ndst = find_root_element(client->doc, &tmp, &rc, NULL, 0, FALSE);
2610 if (!ndst && rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
2611 g_strfreev(tmp);
2612 goto fail;
2615 if (tmp[1] && ndst)
2616 ndst = find_elements(client->doc, ndst->children, tmp+1, &rc, NULL,
2617 NULL, NULL, FALSE, 0, NULL, FALSE);
2619 g_strfreev(tmp);
2620 if (!ndst && rc && rc != GPG_ERR_ELEMENT_NOT_FOUND)
2621 goto fail;
2623 rc = 0;
2625 /* Target may exist:
2627 * <root name="a"/>
2628 * <root name="b" target="a"/>
2630 * RENAME b a
2632 * Would need to do:
2633 * RENAME !b a
2635 if (ndst == n) {
2636 rc = GPG_ERR_AMBIGUOUS_NAME;
2637 goto fail;
2640 if (ndst) {
2641 unlink_node(ndst);
2642 xmlFreeNodeList(ndst);
2645 rc = add_attribute(n, "_name", dst);
2647 fail:
2648 g_strfreev(req);
2649 g_strfreev(src);
2650 return rc;
2653 static gpg_error_t rename_command(assuan_context_t ctx, gchar *line)
2655 struct client_s *client = assuan_get_pointer(ctx);
2656 gpg_error_t rc;
2657 struct argv_s *args[] = {
2658 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
2659 NULL
2662 rc = parse_options(&line, args, client);
2663 if (rc)
2664 return send_error(ctx, rc);
2666 if (client->opts&OPT_INQUIRE) {
2667 guchar *result;
2668 gsize len;
2670 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
2671 if (rc)
2672 return send_error(ctx, rc);
2674 line = (gchar *)result;
2677 rc = do_rename(ctx, line);
2679 if (client->opts&OPT_INQUIRE)
2680 xfree(line);
2682 return send_error(ctx, rc);
2685 static gpg_error_t do_copy(assuan_context_t ctx, gchar *line)
2687 struct client_s *client = assuan_get_pointer(ctx);
2688 gpg_error_t rc;
2689 gchar **req, **src = NULL, **dst = NULL;
2690 xmlNodePtr nsrc, ndst, new = NULL;
2692 req = split_input_line(line, " ", -1);
2693 if (!req || !req[0] || !req[1]) {
2694 g_strfreev(req);
2695 return GPG_ERR_SYNTAX;
2698 if (strchr(req[0], '\t'))
2699 src = split_input_line(req[0], "\t", -1);
2700 else
2701 src = split_input_line(req[0], " ", -1);
2703 if (!src || !*src) {
2704 rc = GPG_ERR_SYNTAX;
2705 goto fail;
2708 if (strchr(req[1], '\t'))
2709 dst = split_input_line(req[1], "\t", -1);
2710 else
2711 dst = split_input_line(req[1], " ", -1);
2713 if (!dst || !*dst) {
2714 rc = GPG_ERR_SYNTAX;
2715 goto fail;
2718 if (!valid_element_path(dst, FALSE)) {
2719 rc = GPG_ERR_INV_VALUE;
2720 goto fail;
2723 nsrc = find_root_element(client->doc, &src, &rc, NULL, 0, FALSE);
2724 if (nsrc && src[1])
2725 nsrc = find_elements(client->doc, nsrc->children, src+1, &rc, NULL,
2726 NULL, NULL, FALSE, 0, NULL, FALSE);
2728 if (!nsrc)
2729 goto fail;
2731 new = xmlCopyNodeList(nsrc);
2732 if (!new) {
2733 rc = GPG_ERR_ENOMEM;
2734 goto fail;
2737 gboolean create = FALSE;
2738 ndst = find_root_element(client->doc, &dst, &rc, NULL, 0, FALSE);
2739 if (ndst && dst[1]) {
2740 if (ndst->children)
2741 ndst = find_elements(client->doc, ndst->children, dst+1, &rc, NULL,
2742 NULL, create_target_elements_cb, FALSE, 0, NULL, FALSE);
2743 else
2744 create = TRUE;
2746 else
2747 create = TRUE;
2749 if (!ndst && rc != GPG_ERR_ELEMENT_NOT_FOUND)
2750 goto fail;
2751 else if (create) {
2752 ndst = create_element_path(client, &dst, &rc, NULL);
2753 if (!ndst)
2754 goto fail;
2757 /* Merge any attributes from the src node to the initial dst node. */
2758 for (xmlAttrPtr attr = new->properties; attr; attr = attr->next) {
2759 if (xmlStrEqual(attr->name, (xmlChar *)"_name"))
2760 continue;
2762 xmlAttrPtr a = xmlHasProp(ndst, attr->name);
2763 if (a)
2764 xmlRemoveProp(a);
2766 xmlChar *tmp = xmlNodeGetContent(attr->children);
2767 xmlNewProp(ndst, attr->name, tmp);
2768 xmlFree(tmp);
2769 rc = add_attribute(ndst, NULL, NULL);
2772 xmlNodePtr n = ndst->children;
2773 xmlUnlinkNode(n);
2774 xmlFreeNodeList(n);
2775 ndst->children = NULL;
2777 if (new->children) {
2778 n = xmlCopyNodeList(new->children);
2779 if (!n) {
2780 rc = GPG_ERR_ENOMEM;
2781 goto fail;
2784 n = xmlAddChildList(ndst, n);
2785 if (!n) {
2786 rc = GPG_ERR_ENOMEM;
2787 goto fail;
2790 rc = update_element_mtime(xmlDocGetRootElement(client->doc) ==
2791 ndst->parent ? ndst : ndst->parent);
2794 fail:
2795 if (new) {
2796 xmlUnlinkNode(new);
2797 xmlFreeNodeList(new);
2800 if (req)
2801 g_strfreev(req);
2803 if (src)
2804 g_strfreev(src);
2806 if (dst)
2807 g_strfreev(dst);
2809 return rc;
2812 static gpg_error_t copy_command(assuan_context_t ctx, gchar *line)
2814 struct client_s *client = assuan_get_pointer(ctx);
2815 gpg_error_t rc;
2816 struct argv_s *args[] = {
2817 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
2818 NULL
2821 rc = parse_options(&line, args, client);
2822 if (rc)
2823 return send_error(ctx, rc);
2825 if (client->opts&OPT_INQUIRE) {
2826 guchar *result;
2827 gsize len;
2829 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
2830 if (rc)
2831 return send_error(ctx, rc);
2833 line = (gchar *)result;
2836 rc = do_copy(ctx, line);
2838 if (client->opts&OPT_INQUIRE)
2839 xfree(line);
2841 return send_error(ctx, rc);
2844 static gpg_error_t do_move(assuan_context_t ctx, gchar *line)
2846 struct client_s *client = assuan_get_pointer(ctx);
2847 gpg_error_t rc;
2848 gchar **req, **src = NULL, **dst = NULL;
2849 xmlNodePtr nsrc, ndst = NULL;
2851 req = split_input_line(line, " ", -1);
2853 if (!req || !req[0] || !req[1]) {
2854 g_strfreev(req);
2855 return GPG_ERR_SYNTAX;
2858 if (strchr(req[0], '\t'))
2859 src = split_input_line(req[0], "\t", -1);
2860 else
2861 src = split_input_line(req[0], " ", -1);
2863 if (!src || !*src) {
2864 rc = GPG_ERR_SYNTAX;
2865 goto fail;
2868 if (strchr(req[1], '\t'))
2869 dst = split_input_line(req[1], "\t", -1);
2870 else
2871 dst = split_input_line(req[1], " ", -1);
2873 nsrc = find_root_element(client->doc, &src, &rc, NULL, 0, FALSE);
2874 if (nsrc && src[1])
2875 nsrc = find_elements(client->doc, nsrc->children, src+1, &rc, NULL,
2876 NULL, NULL, FALSE, 0, NULL, FALSE);
2878 if (!nsrc)
2879 goto fail;
2881 if (dst) {
2882 if (!valid_element_path(dst, FALSE)) {
2883 rc = GPG_ERR_INV_VALUE;
2884 goto fail;
2887 ndst = find_root_element(client->doc, &dst, &rc, NULL, 0, FALSE);
2888 if (ndst && dst[1])
2889 ndst = find_elements(client->doc, ndst->children, dst+1, &rc, NULL,
2890 NULL, NULL, FALSE, 0, NULL, FALSE);
2892 else
2893 ndst = xmlDocGetRootElement(client->doc);
2895 for (xmlNodePtr n = ndst; n; n = n->parent) {
2896 if (n == nsrc) {
2897 rc = GPG_ERR_CONFLICT;
2898 goto fail;
2902 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND)
2903 goto fail;
2905 rc = 0;
2907 if (ndst) {
2908 xmlChar *a = node_has_attribute(nsrc, (xmlChar *)"_name");
2909 xmlNodePtr dup = find_element(ndst->children, (gchar *)a, NULL);
2911 xmlFree(a);
2912 if (dup) {
2913 if (dup == nsrc)
2914 goto fail;
2916 if (ndst == xmlDocGetRootElement(client->doc)) {
2917 xmlNodePtr n = nsrc;
2918 gboolean match = FALSE;
2920 while (n->parent && n->parent != ndst)
2921 n = n->parent;
2923 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
2924 xmlChar *b = node_has_attribute(nsrc, (xmlChar *)"_name");
2926 if (xmlStrEqual(a, b)) {
2927 match = TRUE;
2928 xmlUnlinkNode(nsrc);
2929 xmlUnlinkNode(n);
2930 xmlFreeNodeList(n);
2933 xmlFree(a);
2934 xmlFree(b);
2936 if (!match) {
2937 xmlUnlinkNode(dup);
2938 xmlFreeNodeList(dup);
2941 else
2942 xmlUnlinkNode(dup);
2946 if (!ndst && dst) {
2947 xmlChar *name = node_has_attribute(nsrc, (xmlChar *)"_name");
2949 if (nsrc->parent == xmlDocGetRootElement(client->doc)
2950 && !g_strcmp0((gchar *)name, *dst)) {
2951 xmlFree(name);
2952 rc = GPG_ERR_CONFLICT;
2953 goto fail;
2956 xmlFree(name);
2957 ndst = create_element_path(client, &dst, &rc, nsrc);
2960 if (!ndst)
2961 goto fail;
2963 update_element_mtime(nsrc->parent);
2964 xmlUnlinkNode(nsrc);
2965 ndst = xmlAddChildList(ndst, nsrc);
2967 if (!ndst)
2968 rc = GPG_ERR_ENOMEM;
2970 update_element_mtime(ndst->parent);
2972 fail:
2973 if (req)
2974 g_strfreev(req);
2976 if (src)
2977 g_strfreev(src);
2979 if (dst)
2980 g_strfreev(dst);
2982 return rc;
2985 static gpg_error_t move_command(assuan_context_t ctx, gchar *line)
2987 struct client_s *client = assuan_get_pointer(ctx);
2988 gpg_error_t rc;
2989 struct argv_s *args[] = {
2990 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
2991 NULL
2994 rc = parse_options(&line, args, client);
2995 if (rc)
2996 return send_error(ctx, rc);
2998 if (client->opts&OPT_INQUIRE) {
2999 guchar *result;
3000 gsize len;
3002 rc = assuan_inquire(ctx, "DATA", &result, &len, 0);
3003 if (rc)
3004 return send_error(ctx, rc);
3006 line = (gchar *)result;
3009 rc = do_move(ctx, line);
3011 if (client->opts&OPT_INQUIRE)
3012 xfree(line);
3014 return send_error(ctx, rc);
3017 static gpg_error_t ls_command(assuan_context_t ctx, gchar *line)
3019 gpg_error_t rc;
3020 gchar *tmp = g_key_file_get_string(keyfileh, "global", "data_directory", NULL);
3021 gchar *dir = expand_homedir(tmp);
3022 DIR *d = opendir(dir);
3024 rc = gpg_error_from_syserror();
3025 g_free(tmp);
3027 if (!d) {
3028 g_free(dir);
3029 return send_error(ctx, rc);
3032 size_t len = offsetof(struct dirent, d_name)+pathconf(dir, _PC_NAME_MAX)+1;
3033 struct dirent *p = g_malloc(len), *cur = NULL;
3034 gchar *list = NULL;
3036 g_free(dir);
3037 rc = 0;
3039 while (!readdir_r(d, p, &cur) && cur) {
3040 if (cur->d_name[0] == '.' && cur->d_name[1] == '\0')
3041 continue;
3042 else if (cur->d_name[0] == '.' && cur->d_name[1] == '.' && cur->d_name[2] == '\0')
3043 continue;
3045 tmp = g_strdup_printf("%s%s\n", list ? list : "", cur->d_name);
3047 if (!tmp) {
3048 if (list)
3049 g_free(list);
3051 rc = GPG_ERR_ENOMEM;
3052 break;
3055 g_free(list);
3056 list = tmp;
3059 closedir(d);
3060 g_free(p);
3062 if (rc)
3063 return send_error(ctx, rc);
3065 if (!list)
3066 return send_error(ctx, GPG_ERR_NO_DATA);
3068 list[strlen(list)-1] = 0;
3069 rc = xfer_data(ctx, list, strlen(list));
3070 g_free(list);
3071 return send_error(ctx, rc);
3074 static gpg_error_t bye_notify(assuan_context_t ctx, gchar *line)
3076 struct client_s *cl = assuan_get_pointer(ctx);
3078 /* This will let assuan_process_next() return. */
3079 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
3080 cl->last_rc = 0; // BYE command result
3081 return 0;
3084 static gpg_error_t reset_notify(assuan_context_t ctx, gchar *line)
3086 struct client_s *client = assuan_get_pointer(ctx);
3088 if (client)
3089 cleanup_client(client);
3091 return 0;
3095 * This is called before every Assuan command.
3097 static gpg_error_t command_startup(assuan_context_t ctx, const gchar *name)
3099 struct client_s *client = assuan_get_pointer(ctx);
3100 gpg_error_t rc;
3101 struct command_table_s *cmd = NULL;
3103 log_write1("command='%s'", name);
3104 client->last_rc = client->opts = 0;
3106 for (int i = 0; command_table[i]; i++) {
3107 if (!g_ascii_strcasecmp(name, command_table[i]->name)) {
3108 if (command_table[i]->ignore_startup)
3109 return 0;
3110 cmd = command_table[i];
3111 break;
3115 client->last_rc = rc = gpg_error(file_modified(client, cmd));
3116 return rc;
3120 * This is called after every Assuan command.
3122 static void command_finalize(assuan_context_t ctx, gpg_error_t rc)
3124 struct client_s *client = assuan_get_pointer(ctx);
3126 if (!(client->flags&FLAG_LOCK_CMD))
3127 unlock_file_mutex(client, FALSE);
3129 log_write1(_("command completed: rc=%u"), client->last_rc);
3130 client->last_rc = gpg_error(GPG_ERR_UNKNOWN_COMMAND);
3133 static gpg_error_t help_command(assuan_context_t ctx, gchar *line)
3135 gpg_error_t rc;
3136 gint i;
3138 if (!line || !*line) {
3139 gchar *tmp;
3140 gchar *help = g_strdup(_(
3141 "Usage: HELP [<COMMAND>]\n"
3142 "For commands that take an element path as an argument, each element is "
3143 "separated with an ASCII @key{TAB} character (ASCII 0x09)."
3144 "\n"
3145 "COMMANDS:"));
3147 for (i = 0; command_table[i]; i++) {
3148 if (!command_table[i]->help)
3149 continue;
3151 tmp = g_strdup_printf("%s %s", help, command_table[i]->name);
3152 g_free(help);
3153 help = tmp;
3156 tmp = strip_texi_and_wrap(help);
3157 g_free(help);
3158 rc = xfer_data(ctx, tmp, strlen(tmp));
3159 g_free(tmp);
3160 return send_error(ctx, rc);
3163 for (i = 0; command_table[i]; i++) {
3164 if (!g_strcasecmp(line, command_table[i]->name)) {
3165 gchar *help, *tmp;
3167 if (!command_table[i]->help)
3168 break;
3170 help = strip_texi_and_wrap(command_table[i]->help);
3171 tmp = g_strdup_printf(_("Usage: %s"), help);
3172 g_free(help);
3173 rc = xfer_data(ctx, tmp, strlen(tmp));
3174 g_free(tmp);
3175 return send_error(ctx, rc);
3179 return send_error(ctx, GPG_ERR_INV_NAME);
3182 static void new_command(const gchar *name, gboolean ignore, gboolean unlock,
3183 gpg_error_t (*handler)(assuan_context_t, gchar *), const gchar *help)
3185 gint i = 0;
3187 if (command_table)
3188 for (i = 0; command_table[i]; i++);
3190 command_table = g_realloc(command_table, (i+2)*sizeof(struct command_table_s *));
3191 command_table[i] = g_malloc0(sizeof(struct command_table_s));
3192 command_table[i]->name = name;
3193 command_table[i]->handler = handler;
3194 command_table[i]->ignore_startup = ignore;
3195 command_table[i]->unlock = unlock;
3196 command_table[i++]->help = help;
3197 command_table[i] = NULL;
3200 void deinit_commands()
3202 gint i;
3204 for (i = 0; command_table[i]; i++)
3205 g_free(command_table[i]);
3207 g_free(command_table);
3210 static gint sort_commands(const void *arg1, const void *arg2)
3212 struct command_table_s* const *a = arg1;
3213 struct command_table_s* const *b = arg2;
3215 if (!*a || !*b)
3216 return 0;
3217 else if (*a && !*b)
3218 return 1;
3219 else if (!*a && *b)
3220 return -1;
3222 return g_strcmp0((*a)->name, (*b)->name);
3225 static gpg_error_t passwd_command(assuan_context_t ctx, gchar *line)
3227 struct client_s *client = assuan_get_pointer(ctx);
3228 gpg_error_t rc;
3229 struct argv_s *args[] = {
3230 &(struct argv_s) { "reset", OPTION_TYPE_NOARG, parse_opt_reset },
3231 &(struct argv_s) { "s2k-count", OPTION_TYPE_ARG, parse_opt_s2k_count },
3232 NULL
3235 if (client->flags&FLAG_NEW)
3236 return send_error(ctx, GPG_ERR_INV_STATE);
3238 client->crypto->save.s2k_count = get_key_file_ulong(client->filename, "s2k_count");
3239 rc = parse_options(&line, args, client);
3240 if (rc)
3241 return send_error(ctx, rc);
3243 if (!rc && client->opts&OPT_RESET)
3244 rc = cache_clear(client->md5file);
3246 if (!rc) {
3247 if (client->crypto->save.s2k_count)
3248 rc = send_to_agent(client->crypto->agent, NULL, NULL,
3249 "OPTION s2k-count=%lu", client->crypto->save.s2k_count);
3251 if (!rc)
3252 rc = agent_passwd(client->crypto);
3255 return send_error(ctx, rc);
3258 static gpg_error_t parse_keygrip_opt_sign(gpointer data, gpointer value)
3260 struct client_s *client = data;
3262 (void)value;
3263 client->opts |= OPT_SIGN;
3264 return 0;
3267 static gpg_error_t keygrip_command(assuan_context_t ctx, gchar *line)
3269 struct client_s *client = assuan_get_pointer(ctx);
3270 gpg_error_t rc;
3271 struct crypto_s *crypto;
3272 struct argv_s *args[] = {
3273 &(struct argv_s) { "sign", OPTION_TYPE_NOARG, parse_keygrip_opt_sign },
3274 NULL
3277 if (!line || !*line)
3278 return send_error(ctx, GPG_ERR_SYNTAX);
3280 rc = parse_options(&line, args, client);
3281 if (rc)
3282 return send_error(ctx, rc);
3284 if (!valid_filename(line))
3285 return send_error(ctx, GPG_ERR_INV_VALUE);
3287 rc = init_client_crypto(&crypto);
3288 if (rc)
3289 return send_error(ctx, rc);
3291 rc = read_data_file(line, crypto);
3292 if (!rc) {
3293 gchar *hexgrip = NULL;
3295 if (client->opts&OPT_SIGN) {
3296 if (valid_keygrip(crypto->sign_grip, sizeof(crypto->sign_grip)))
3297 hexgrip = bin2hex(crypto->sign_grip, sizeof(crypto->sign_grip));
3300 if (!hexgrip)
3301 hexgrip = bin2hex(crypto->grip, sizeof(crypto->grip));
3303 if (!hexgrip)
3304 rc = GPG_ERR_ENOMEM;
3305 else
3306 rc = xfer_data(ctx, hexgrip, strlen(hexgrip));
3308 g_free(hexgrip);
3311 cleanup_crypto(&crypto);
3312 return send_error(ctx, rc);
3315 static gpg_error_t getinfo_command(assuan_context_t ctx, gchar *line)
3317 struct client_s *client = assuan_get_pointer(ctx);
3318 gpg_error_t rc;
3320 if (!g_ascii_strcasecmp(line, "clients"))
3321 rc = send_status(ctx, STATUS_CLIENTS, NULL);
3322 else if (!g_ascii_strcasecmp(line, "cache"))
3323 rc = send_status(ctx, STATUS_CACHE, NULL);
3324 else if (!g_ascii_strcasecmp(line, "pid")) {
3325 gchar buf[32];
3326 pid_t pid = getpid();
3328 print_fmt(buf, sizeof(buf), "%i", pid);
3329 rc = xfer_data(ctx, buf, strlen(buf));
3331 else if (!g_ascii_strcasecmp(line, "version")) {
3332 gchar *buf = g_strdup_printf("0x%X %s", VERSION_HEX,
3333 #ifdef WITH_LIBACL
3334 "ACL "
3335 #endif
3336 "");
3337 rc = xfer_data(ctx, buf, strlen(buf));
3338 g_free(buf);
3340 else if (!g_ascii_strcasecmp(line, "last_error")) {
3341 if (client->last_error)
3342 rc = xfer_data(ctx, client->last_error, strlen(client->last_error));
3343 else
3344 rc = GPG_ERR_NO_DATA;
3346 else
3347 rc = gpg_error(GPG_ERR_SYNTAX);
3349 return send_error(ctx, rc);
3352 static gpg_error_t send_data_cb(gpointer user, const void *buf, size_t len)
3354 assuan_context_t ctx = user;
3356 return assuan_send_data(ctx, buf, len);
3359 static gpg_error_t send_status_cb(gpointer user, const gchar *line)
3361 assuan_context_t ctx = user;
3362 gchar keyword[200], *k;
3363 const gchar *p;
3365 for (p = line, k = keyword; *p; p++) {
3366 if (g_ascii_isspace(*p))
3367 break;
3369 *k++ = *p;
3372 *k = 0;
3373 if (*p == '#')
3374 p++;
3376 while (*p && g_ascii_isspace(*p))
3377 p++;
3379 return assuan_write_status(ctx, keyword, *p ? p : NULL);
3382 static gpg_error_t agent_command(assuan_context_t ctx, gchar *line)
3384 gpg_error_t rc;
3385 struct client_s *client = assuan_get_pointer(ctx);
3387 if (!line || !*line)
3388 return send_error(ctx, GPG_ERR_SYNTAX);
3390 assuan_set_flag(client->crypto->agent->ctx, ASSUAN_CONVEY_COMMENTS, 1);
3391 rc = assuan_transact(client->crypto->agent->ctx, line, send_data_cb,
3392 client->ctx, agent_loopback_cb, client->crypto, send_status_cb,
3393 client->ctx);
3394 if (gpg_err_code(rc) == GPG_ERR_ASS_CANCELED) {
3395 gchar *line;
3396 gsize len;
3398 rc = assuan_write_line(client->crypto->agent->ctx, "CAN");
3399 if (!rc) {
3400 rc = assuan_read_line(client->crypto->agent->ctx, &line, &len);
3401 if (!rc)
3402 rc = gpg_error(GPG_ERR_ASS_CANCELED);
3406 assuan_set_flag(client->crypto->agent->ctx, ASSUAN_CONVEY_COMMENTS, 0);
3407 return send_error(ctx, rc);
3410 void init_commands()
3412 /* !BEGIN-HELP-TEXT!
3414 * This comment is used as a marker to generate the offline documentation
3415 * for commands found in doc/COMMANDS.
3417 new_command("HELP", TRUE, TRUE, help_command, _(
3418 "HELP [<COMMAND>]\n"
3419 " Show available commands or command specific help text."
3422 new_command("AGENT", TRUE, TRUE, agent_command, _(
3423 "AGENT <command>\n"
3424 "Send a @command{gpg-agent} protocol @var{command} directly to the "
3425 "@command{gpg-agent}."
3427 new_command("GETINFO", TRUE, TRUE, getinfo_command, _(
3428 "GETINFO CACHE | CLIENTS | PID | LAST_ERROR | VERSION\n"
3429 "Get server and other information: @var{cache} returns the number of cached "
3430 "documents via a status message. @var{clients} returns the number of "
3431 "connected clients via a status message. @var{pid} returns the process ID "
3432 "number of the server via a data response. @var{VERSION} returns the server "
3433 "version number and compile-time features with a data response with each "
3434 "being space delimited. @var{LAST_ERROR} returns a detailed description of "
3435 "the last failed command when available. @xref{Status Messages}."
3438 new_command("PASSWD", FALSE, FALSE, passwd_command, _(
3439 "PASSWD [--reset] [--s2k-count=N]\n"
3440 "Changes the passphrase of the secret key required to open the current "
3441 "file. When the @option{--reset} option is passed then the cache entry for "
3442 "the current file will be reset and the passphrase, if any, will be required "
3443 "during the next @code{OPEN}. @xref{OPEN}."
3444 "\n"
3445 "The @option{--s2k-count} option sets number of hash iterations for a "
3446 "passphrase and must be either @code{0} to use the calibrated count of the "
3447 "machine (the default), or a value greater than or equal to @code{65536}. "
3448 "@xref{SAVE}."
3451 new_command("KEYGRIP", TRUE, TRUE, keygrip_command, _(
3452 "KEYGRIP [--sign] <filename>\n"
3453 "Returns the hex encoded keygrip of the specified @var{filename} with a "
3454 "data response."
3455 "\n"
3456 "When the @option{--sign} option is specified then the key used for signing "
3457 "of the specified @var{filename} will be returned."
3460 new_command("OPEN", TRUE, TRUE, open_command, _(
3461 "OPEN [--lock] [--no-pinentry] <filename> [<passphrase>]\n"
3462 "Opens @var{filename} using @var{passphrase}. When the filename is not "
3463 "found on the file-system then a new document will be created. If the file "
3464 "is found, it is looked for in the file cache. If cached and no "
3465 "@var{passphrase} was specified then the cached document is opened. When not "
3466 "cached, @cite{pinentry(1)} will be used to retrieve the passphrase to use "
3467 "for decryption unless @option{--no-pinentry} was specified (see below)."
3468 "\n"
3469 "When the @option{--lock} option is passed then the file mutex will be "
3470 "locked as if the @code{LOCK} command (@pxref{LOCK}) had been sent after the "
3471 "file has been opened."
3472 "\n"
3473 "By default, a pinentry is used to retrieve a passphrase when required. "
3474 "Passing @option{--no-pinentry} will disable pinentry use for the rest of "
3475 "the session. When pinentry use is disabled but required for some operation"
3476 "then a server @emph{INQUIRE} will be send to the client to retrieve the "
3477 "passphrase. See the @code{OPTION} command (@pxref{OPTION}), for pinentry "
3478 "specific options."
3481 new_command("SAVE", FALSE, FALSE, save_command, _(
3482 "SAVE [--no-passphrase] [--reset] [--s2k-count=N] [--cipher=<algo>] [--inquire-keyparam] [--keygrip=hexstring [--sign-keygrip=hexstring]]\n"
3483 "Writes the @abbr{XML} document to disk. The file written to is the file that "
3484 "was opened using the @code{OPEN} command (@pxref{OPEN}). If the file is a "
3485 "new one or the option @option{--inquire-keyparam} was passed, then a new "
3486 "keypair will be generated and a pinentry will be used to prompt for the "
3487 "passphrase to encrypt with unless the @option{--no-passphrase} option was "
3488 "passed, in which case the data file will not be passphrase protected."
3489 "\n"
3490 "The @option{--reset} option will clear the cache entry for the current file "
3491 "before saving."
3492 "\n"
3493 "The @option{--cipher} option can be used to encrypt the @abbr{XML} data to "
3494 "an alternate cipher. The default is @code{aes256}. See the Configuration "
3495 "(@pxref{Configuration}) for available ciphers."
3496 "\n"
3497 "The @option{--inquire-keyparam} option will send a server @emph{INQUIRE} to "
3498 "the client to obtain the key paramaters to use when generating a new "
3499 "keypair. The inquired data is expected to be an S-expression. If not "
3500 "specified then an @samp{RSA} key of @samp{2048} bits will be generated "
3501 "unless otherwise set in the configuration file (@pxref{Configuration}). Note "
3502 "that when this option is specified a new keypair will be generated "
3503 "reguardless if the file is a new one or not."
3504 "\n"
3505 "You can encrypt the data file to a public key other than the one that it "
3506 "was originally encrypted with by passing the @option{--keygrip} option with "
3507 "the hex encoded keygrip of the public key as its argument. The keygrip may "
3508 "be of any key that @command{gpg-agent} knows about. The "
3509 "@option{--sign-keygrip} option may also be used to sign with an alternate "
3510 "secret key. This option may be needed when using a smartcard."
3511 "\n"
3512 "The @option{--s2k-count} option sets number of hash iterations for a "
3513 "passphrase. A value less-than @code{65536} will use the machine calibrated "
3514 "value which is the default. This setting only affects new files. To change "
3515 "the setting, use the @code{PASSWD} command (@pxref{PASSWD})."
3518 new_command("ISCACHED", TRUE, FALSE, iscached_command, _(
3519 "ISCACHED [--lock] <filename>\n"
3520 "An @emph{OK} response is returned if the specified @var{filename} is found "
3521 "in the file cache. If not found in the cache but exists on the filesystem "
3522 "then @var{GPG_ERR_NO_DATA} is returned. Otherwise a filesystem error is "
3523 "returned."
3524 "\n"
3525 "The @option{lock} option will lock the file mutex of @var{filename} when the "
3526 "file exists; it does not need to be opened nor cached."
3529 new_command("CLEARCACHE", TRUE, TRUE, clearcache_command, _(
3530 "CLEARCACHE [<filename>]\n"
3531 "Clears a file cache entry or all or the specified @var{filename}. Always "
3532 "returns an @emph{OK} response."
3535 new_command("CACHETIMEOUT", TRUE, TRUE, cachetimeout_command, _(
3536 "CACHETIMEOUT <filename> <seconds>\n"
3537 "The time in @var{seconds} until @var{filename} will be removed from the "
3538 "cache. @code{-1} will keep the cache entry forever, @code{0} will require "
3539 "the passphrase for each @code{OPEN} command (@pxref{OPEN}). "
3540 "@xref{Configuration}, and the @code{cache_timeout} parameter."
3543 new_command("LIST", FALSE, TRUE, list_command, _(
3544 "LIST [--inquire] [--no-recurse] [--verbose] [--all] [[!]element[<TAB>[!]child[..]]]\n"
3545 "If no element path is given then a newline separated list of root elements "
3546 "is returned with a data response. If given, then all reachable elements "
3547 "of the specified element path are returned unless the @option{--no-recurse} "
3548 "option is specified. If specified, only the child elements of the element "
3549 "path are returned without recursing into grandchildren. Each resulting "
3550 "element is prefixed with the literal @code{!} character when the element "
3551 "contains no @code{target} attribute. @xref{Target Attribute}, for details."
3552 "\n"
3553 "When the @option{--verbose} option is passed then each element path "
3554 "returned will have a single space character followed by either a "
3555 "@code{0} or @code{1} appended to it. When @code{0}, the element path has no "
3556 "child elements, otherwise it does. When used with the @option{--no-recurse} "
3557 "option this may be useful to limit the amount of data transferred to the "
3558 "client."
3559 "\n"
3560 "The @option{--all} option lists the entire element tree for all root "
3561 "elements."
3562 "\n"
3563 "When the @option{--inquire} option is passed then all remaining non-option "
3564 "arguments are retrieved via a server @emph{INQUIRE}."
3567 new_command("REALPATH", FALSE, TRUE, realpath_command, _(
3568 "REALPATH [--inquire] [!]element[<TAB>[!]child[..]]\n"
3569 "Resolves all @code{target} attributes of the specified element path and "
3570 "returns the result with a data response. @xref{Target Attribute}, for details."
3571 "\n"
3572 "When the @option{--inquire} option is passed then all remaining non-option "
3573 "arguments are retrieved via a server @emph{INQUIRE}."
3576 new_command("STORE", FALSE, TRUE, store_command, _(
3577 "STORE [!]element[<TAB>[!]child[..]]<TAB>[content]\n"
3578 "This command uses a server @emph{INQUIRE} to retrieve data from the client."
3579 "\n"
3580 "Creates a new element path or modifies the @var{content} of an existing "
3581 "element. If only a single element is specified then a new root element is "
3582 "created. Otherwise, elements are @key{TAB} delimited and the content will be "
3583 "set to the final @key{TAB} delimited element. If no @var{content} is "
3584 "specified after the final @key{TAB}, then the content of the element will "
3585 "be removed, or empty when creating a new element."
3586 "\n"
3587 "The only restriction of an element name is that it not contain whitespace "
3588 "or begin with the literal element character @code{!} unless specifying a "
3589 "literal element (@pxref{Target Attribute}). There is no whitespace between "
3590 "the @key{TAB} delimited elements. It is recommended that the content of an "
3591 "element be base64 encoded when it contains control or @key{TAB} characters "
3592 "to prevent @abbr{XML} and @command{pwmd} parsing errors."
3595 new_command("RENAME", FALSE, TRUE, rename_command, _(
3596 "RENAME [--inquire] [!]element[<TAB>[!]child[..]] <value>\n"
3597 "Renames the specified @var{element} to the new @var{value}. If an element of "
3598 "the same name as the @var{value} already exists it will be overwritten."
3599 "\n"
3600 "When the @option{--inquire} option is passed then all remaining non-option "
3601 "arguments are retrieved via a server @emph{INQUIRE}."
3604 new_command("COPY", FALSE, TRUE, copy_command, _(
3605 "COPY [--inquire] [!]source[<TAB>[!]child[..]] [!]dest[<TAB>[!]child[..]]\n"
3606 "Copies the entire element tree starting from the child node of the source "
3607 "element, to the destination element path. If the destination element path "
3608 "does not exist then it will be created; otherwise it is overwritten."
3609 "\n"
3610 "Note that attributes from the source element are merged into the "
3611 "destination element when the destination element path exists. When an "
3612 "attribute of the same name exists in both the source and destination "
3613 "elements then the destination attribute will be updated to the source "
3614 "attribute value."
3615 "\n"
3616 "When the @option{--inquire} option is passed then all remaining non-option "
3617 "arguments are retrieved via a server @emph{INQUIRE}."
3620 new_command("MOVE", FALSE, TRUE, move_command, _(
3621 "MOVE [--inquire] [!]source[<TAB>[!]child[..]] [[!]dest[<TAB>[!]child[..]]]\n"
3622 "Moves the source element path to the destination element path. If the "
3623 "destination is not specified then it will be moved to the root node of the "
3624 "document. If the destination is specified and exists then it will be "
3625 "overwritten; otherwise it will be created."
3626 "\n"
3627 "When the @option{--inquire} option is passed then all remaining non-option "
3628 "arguments are retrieved via a server @emph{INQUIRE}."
3631 new_command("DELETE", FALSE, TRUE, delete_command, _(
3632 "DELETE [--inquire] [!]element[<TAB>[!]child[..]]\n"
3633 "Removes the specified element path and all of its children. This may break "
3634 "an element with a @code{target} attribute (@pxref{Target Attribute}) that "
3635 "refers to this element or any of its children."
3636 "\n"
3637 "When the @option{--inquire} option is passed then all remaining non-option "
3638 "arguments are retrieved via a server @emph{INQUIRE}."
3641 new_command("GET", FALSE, TRUE, get_command, _(
3642 "GET [--inquire] [!]element[<TAB>[!]child[..]]\n"
3643 "Retrieves the content of the specified element. The content is returned "
3644 "with a data response."
3645 "\n"
3646 "When the @option{--inquire} option is passed then all remaining non-option "
3647 "arguments are retrieved via a server @emph{INQUIRE}."
3650 new_command("ATTR", FALSE, TRUE, attr_command, _(
3651 "ATTR [--inquire] SET|GET|DELETE|LIST [<attribute>] [!]element[<TAB>[!]child[..]] ..\n"
3652 "@table @asis\n"
3653 "@item ATTR SET attribute [!]element[<TAB>[!]child[..]] [value]\n"
3654 "\n"
3655 " Stores or updates an @var{attribute} name and optional @var{value} of an "
3656 "element. When no @var{value} is specified any existing value will be removed."
3657 "\n"
3658 "@item ATTR DELETE attribute [!]element[<TAB>[!]child[..]]\n"
3659 "\n"
3660 " Removes an @var{attribute} from an element."
3661 "\n"
3662 "@item ATTR LIST [!]element[<TAB>[!]child[..]]\n"
3663 "\n"
3664 " Retrieves a newline separated list of attributes names and values "
3665 "from the specified element. Each attribute name and value is space delimited."
3666 "\n"
3667 "@item ATTR GET attribute [!]element[<TAB>[!]child[..]]\n"
3668 "\n"
3669 " Retrieves the value of an @var{attribute} from an element."
3670 "@end table\n"
3671 "\n"
3672 "The @code{_name} attribute (case sensitive) cannot be removed nor modified. "
3673 "Use the @code{DELETE} (@pxref{DELETE}) or @code{RENAME} (@pxref{RENAME}) "
3674 "commands instead.\n"
3675 "\n"
3676 "The @code{_mtime} attribute is updated each time an element is modified by "
3677 "either storing content, editing attributes or by deleting a child element. "
3678 "The @code{_ctime} attribute is created for each new element in an element "
3679 "path."
3680 "\n"
3681 "When the @option{--inquire} option is passed then all remaining non-option "
3682 "arguments are retrieved via a server @emph{INQUIRE}."
3683 "\n"
3684 "@xref{Target Attribute}, for details about this special attribute."
3687 new_command("XPATH", FALSE, TRUE, xpath_command, _(
3688 "XPATH [--inquire] <expression>[<TAB>[value]]\n"
3689 "Evaluates an XPath @var{expression}. If no @var{value} argument is "
3690 "specified, it is assumed the expression is a request to return a result. "
3691 "Otherwise, the result is set to the @var{value} argument and the document is "
3692 "updated. If there is no @var{value} after the @key{TAB} character, the value "
3693 "is assumed to be empty and the document is updated."
3694 "\n"
3695 "When the @option{--inquire} option is passed then all remaining non-option "
3696 "arguments are retrieved via a server @emph{INQUIRE}."
3699 new_command("XPATHATTR", FALSE, TRUE, xpathattr_command, _(
3700 "XPATHATTR [--inquire] SET|DELETE <name> <expression>[<TAB>[<value>]]\n"
3701 "Like the @code{XPATH} command (@pxref{XPATH}) but operates on element "
3702 "attributes and does not return a result. For the @var{SET} operation the "
3703 "@var{value} is optional but the field is required. If not specified then "
3704 "the attribute value will be empty."
3705 "\n"
3706 "When the @option{--inquire} option is passed then all remaining non-option "
3707 "arguments are retrieved via a server @emph{INQUIRE}."
3710 new_command("IMPORT", FALSE, TRUE, import_command, _(
3711 "IMPORT <content>[<TAB>[!]element[<TAB>[!]child[..]]]\n"
3712 "This command uses a server @emph{INQUIRE} to retrieve data from the client."
3713 "\n"
3714 "Like the @code{STORE} command (@pxref{STORE}), but the @var{content} "
3715 "argument is raw @abbr{XML} data. The content is created as a child of the "
3716 "specified element path and will overwrite an existing element of the same "
3717 "name. If an element of the element path does not exist then it will be "
3718 "created."
3719 "\n"
3720 "The content must begin with an @abbr{XML} element node. @xref{Introduction}, "
3721 "for details."
3724 new_command("DUMP", FALSE, TRUE, dump_command, _(
3725 "DUMP\n"
3726 "Shows the in memory @abbr{XML} document with indenting. @xref{XPATH}, for "
3727 "dumping a specific node."
3730 new_command("LOCK", FALSE, FALSE, lock_command, _(
3731 "LOCK\n"
3732 "Locks the mutex associated with the opened file. This prevents other clients "
3733 "from sending commands to the same opened file until the client "
3734 "that sent this command either disconnects or sends the @code{UNLOCK} "
3735 "command. @xref{UNLOCK}."
3738 new_command("UNLOCK", TRUE, FALSE, unlock_command, _(
3739 "UNLOCK\n"
3740 "Unlocks the file mutex which was locked with the @code{LOCK} command or "
3741 "a commands' @option{lock} option. @xref{LOCK}."
3744 new_command("GETCONFIG", TRUE, TRUE, getconfig_command, _(
3745 "GETCONFIG [filename] <parameter>\n"
3746 "Returns the value of a @command{pwmd} configuration @var{parameter} with a "
3747 "data response. If no file has been opened then the value for @var{filename} "
3748 "or the default from the @samp{global} section will be returned. If a file "
3749 "has been opened and no @var{filename} is specified, a value previously "
3750 "set with the @code{OPTION} command (@pxref{OPTION}) will be returned."
3753 new_command("OPTION", TRUE, TRUE, NULL, _(
3754 "OPTION <NAME>=<VALUE>\n"
3755 "Sets a client option @var{name} to @var{value}. The value for an option is "
3756 "kept for the duration of the connection."
3757 "\n"
3758 "@table @asis\n"
3759 "@item TTYNAME\n"
3760 " Passed to the @command{gpg-agent} and used for the @command{pinentry} dialog."
3761 "\n"
3762 "@item TTYTYPE\n"
3763 " Passed to the @command{gpg-agent} and used for the @command{pinentry} dialog."
3764 "\n"
3765 "@item DISPLAY\n"
3766 " Passed to the @command{gpg-agent} and used for the @command{pinentry} dialog."
3767 "\n"
3768 "@item DESC\n"
3769 " Sets the description string of the @command{gpg-agent} and @command{pinentry} dialog."
3770 "\n"
3771 "@item LC-CTYPE\n"
3772 " Passed to the @command{gpg-agent} and used for the @command{pinentry} dialog."
3773 "\n"
3774 "@item LC-MESSAGES\n"
3775 " Passed to the @command{gpg-agent} and used for the @command{pinentry} dialog."
3776 "\n"
3777 "@item NAME\n"
3778 " Associates the thread ID of the connection with the specified textual "
3779 "representation. Useful for debugging log messages."
3780 "\n"
3781 "@item RC_ON_LOCKED\n"
3782 " When @code{1}, a command will return an error code rather than a status "
3783 "message when a file mutex is locked by another thread.\n"
3784 "@end table\n"
3787 new_command("LS", TRUE, TRUE, ls_command, _(
3788 "LS\n"
3789 "Lists the contents of @code{data_directory}. @xref{Configuration}, for "
3790 "details. The result is a newline separated list of filenames."
3793 new_command("RESET", TRUE, TRUE, NULL, _(
3794 "RESET\n"
3795 "Closes the currently opened file but keeps any previously set client options."
3798 new_command("NOP", TRUE, TRUE, NULL, _(
3799 "NOP\n"
3800 "Does nothing. Always returns successfully."
3803 /* !END-HELP-TEXT! */
3804 new_command("CANCEL", TRUE, TRUE, NULL, NULL);
3805 new_command("END", TRUE, TRUE, NULL, NULL);
3806 new_command("BYE", TRUE, TRUE, NULL, NULL);
3808 gint i;
3809 for (i = 0; command_table[i]; i++);
3810 qsort(command_table, i-1, sizeof(struct command_table_s *), sort_commands);
3813 gpg_error_t register_commands(assuan_context_t ctx)
3815 gint i = 0, rc;
3817 for (; command_table[i]; i++) {
3818 if (!command_table[i]->handler)
3819 continue;
3821 rc = assuan_register_command (ctx, command_table[i]->name,
3822 command_table[i]->handler, command_table[i]->help);
3824 if (rc)
3825 return rc;
3828 rc = assuan_register_option_handler(ctx, option_command);
3829 if (rc)
3830 return rc;
3832 rc = assuan_register_bye_notify(ctx, bye_notify);
3833 if (rc)
3834 return rc;
3836 rc = assuan_register_reset_notify(ctx, reset_notify);
3837 if (rc)
3838 return rc;
3840 rc = assuan_register_pre_cmd_notify(ctx, command_startup);
3841 if (rc)
3842 return rc;
3844 return assuan_register_post_cmd_notify(ctx, command_finalize);