Added the SET and UNSET command which replace the OPTION command.
[pwmd.git] / src / commands.c
blobfdfa6537a700deaf85abbad0b4a3d33cb3751aa7
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2009 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <gcrypt.h>
30 #include <zlib.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #include "mem.h"
37 #include "xml.h"
38 #include "common.h"
40 #ifdef WITH_PINENTRY
41 #include "pinentry.h"
42 #endif
44 #include "pwmd_error.h"
45 #include "cache.h"
46 #include "misc.h"
47 #include "commands.h"
48 #include "lock.h"
50 struct gz_s {
51 z_stream z;
52 gpointer out;
53 gboolean done;
54 status_msg_t which;
57 static void *z_alloc(void *data, unsigned items, unsigned size)
59 return gcry_calloc(items, size);
62 static void z_free(void *data, void *p)
64 gcry_free(p);
67 static gpg_error_t file_modified(struct client_s *client)
69 struct stat st;
70 gpg_error_t rc;
72 if (client->state != STATE_OPEN)
73 return EPWMD_NO_FILE;
75 rc = lock_file_mutex(client);
77 if (rc)
78 return rc;
80 if (lstat(client->filename, &st) == 0 && client->mtime) {
81 if (client->mtime != st.st_mtime)
82 return EPWMD_FILE_MODIFIED;
85 pth_cancel_point();
86 return 0;
89 static gpg_error_t parse_xml(assuan_context_t ctx)
91 struct client_s *client = assuan_get_pointer(ctx);
93 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
95 if (!client->doc)
96 return EPWMD_LIBXML_ERROR;
98 return 0;
101 void unlock_file_mutex(struct client_s *client)
103 pth_mutex_t *m;
105 #ifdef WITH_PINENTRY
106 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
107 #else
108 if (client->has_lock == FALSE)
109 #endif
110 return;
112 CACHE_LOCK(client->ctx);
114 if (cache_get_mutex(client->md5file, &m) == FALSE) {
115 CACHE_UNLOCK;
116 return;
119 CACHE_UNLOCK;
120 MUTEX_UNLOCK(m);
121 client->has_lock = client->is_lock_cmd = FALSE;
124 gpg_error_t lock_file_mutex(struct client_s *client)
126 pth_mutex_t *m;
127 gpg_error_t rc = 0;
129 if (client->has_lock == TRUE)
130 return 0;
132 CACHE_LOCK(client->ctx);
134 if (cache_get_mutex(client->md5file, &m) == FALSE) {
135 CACHE_UNLOCK;
136 return 0;
139 CACHE_UNLOCK;
140 MUTEX_TRYLOCK(client->ctx, m, rc);
142 if (!rc)
143 client->has_lock = TRUE;
145 return rc;
148 void free_client(struct client_s *client)
150 if (client->doc)
151 xmlFreeDoc(client->doc);
153 if (client->xml)
154 gcry_free(client->xml);
156 if (client->filename)
157 g_free(client->filename);
159 if (client->crypto)
160 cleanup_crypto(&client->crypto);
162 if (client->xml_error)
163 xmlResetError(client->xml_error);
166 void cleanup_client(struct client_s *client)
168 assuan_context_t ctx = client->ctx;
169 struct client_thread_s *thd = client->thd;
170 gboolean has_lock = client->has_lock;
171 #ifdef WITH_PINENTRY
172 struct pinentry_s *pin = client->pinentry;
173 #endif
175 unlock_file_mutex(client);
176 CACHE_LOCK(client->ctx);
177 cache_decr_refcount(client->md5file);
180 * This may be a new file so don't use a cache slot. save_command() will
181 * set this to FALSE on success.
183 if (client->new == TRUE)
184 cache_clear(client->md5file, 1);
186 CACHE_UNLOCK;
187 free_client(client);
188 memset(client, 0, sizeof(struct client_s));
189 client->state = STATE_CONNECTED;
190 client->ctx = ctx;
191 client->thd = thd;
192 client->freed = TRUE;
193 #ifdef WITH_PINENTRY
194 client->pinentry = pin;
195 #endif
196 client->has_lock = has_lock;
199 static void gz_cleanup(void *arg)
201 struct gz_s **gz = (struct gz_s **)arg;
203 if (!gz)
204 return;
206 if (!(*gz)->done && (*gz)->out)
207 gcry_free((*gz)->out);
210 if ((*gz)->which == STATUS_COMPRESS) {
211 if ((*gz)->z.zalloc)
212 deflateEnd(&(*gz)->z);
214 else {
215 if ((*gz)->z.zalloc)
216 inflateEnd(&(*gz)->z);
219 g_free(*gz);
220 *gz = NULL;
223 gboolean do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
224 gpointer *out, gulong *outsize, gint *rc)
226 struct gz_s *gz;
227 gz_header h;
228 gchar buf[17];
230 gz = g_malloc0(sizeof(struct gz_s));
232 if (!gz) {
233 *rc = gpg_error_from_errno(ENOMEM);
234 return FALSE;
237 gz->which = STATUS_DECOMPRESS;
238 gz->z.zalloc = z_alloc;
239 gz->z.zfree = z_free;
240 gz->z.next_in = in;
241 gz->z.avail_in = (uInt)insize;
242 gz->z.avail_out = zlib_bufsize;
243 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
245 if (!gz->out) {
246 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
247 *rc = Z_MEM_ERROR;
248 gz_cleanup(&gz);
249 return FALSE;
252 *rc = inflateInit2(&gz->z, 47);
254 if (*rc != Z_OK) {
255 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
256 gz_cleanup(&gz);
257 return FALSE;
260 memset(&h, 0, sizeof(gz_header));
261 h.comment = (guchar *)buf;
262 h.comm_max = sizeof(buf);
263 *rc = inflateGetHeader(&gz->z, &h);
265 if (*rc != Z_OK) {
266 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
267 gz_cleanup(&gz);
268 return FALSE;
271 *rc = inflate(&gz->z, Z_BLOCK);
273 if (*rc != Z_OK) {
274 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
275 gz_cleanup(&gz);
276 return FALSE;
279 if (h.comment)
280 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
282 do {
283 gpointer p;
285 *rc = inflate(&gz->z, Z_FINISH);
287 switch (*rc) {
288 case Z_OK:
289 break;
290 case Z_BUF_ERROR:
291 if (!gz->z.avail_out) {
292 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
294 if (!p) {
295 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
296 *rc = Z_MEM_ERROR;
297 goto fail;
300 gz->out = p;
301 gz->z.next_out = gz->out + gz->z.total_out;
302 gz->z.avail_out = zlib_bufsize;
303 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
304 gz->z.total_out, insize);
306 if (*rc)
307 goto fail;
309 break;
310 case Z_STREAM_END:
311 break;
312 default:
313 goto fail;
314 break;
316 } while (*rc != Z_STREAM_END);
318 pth_cancel_point();
319 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", gz->z.total_out,
320 insize);
321 pth_cancel_point();
323 if (*rc)
324 goto fail;
326 *out = gz->out;
327 *outsize = gz->z.total_out;
328 gz->done = TRUE;
329 gz_cleanup(&gz);
330 *rc = 0;
331 return TRUE;
333 fail:
334 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
335 gz_cleanup(&gz);
336 return FALSE;
339 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
340 gpg_error_t *rc)
342 gint fd;
343 gsize len;
344 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
345 gsize fh_size;
347 *rc = 0;
349 if (!fh) {
350 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
351 *rc = gpg_error_from_errno(ENOMEM);
352 return NULL;
355 fh_size = v1 ? sizeof(fh->fh1) : sizeof(fh->fh2);
357 if (lstat(filename, &fh->st) == -1) {
358 *rc = gpg_error_from_syserror();
359 g_free(fh);
360 return NULL;
363 if (!S_ISREG(fh->st.st_mode)) {
364 *rc = GPG_ERR_ENOANO;
365 g_free(fh);
366 return NULL;
369 fd = open(filename, O_RDONLY);
371 if (fd == -1) {
372 *rc = gpg_error_from_errno(errno);
373 g_free(fh);
374 return NULL;
377 if (v1)
378 len = pth_read(fd, &fh->fh1, fh_size);
379 else
380 len = pth_read(fd, &fh->fh2, fh_size);
382 if (len != fh_size) {
383 gint n = errno;
384 close(fd);
385 g_free(fh);
386 *rc = gpg_error_from_errno(n);
387 return NULL;
390 fh->v1 = v1;
391 fh->fd = fd;
392 return fh;
395 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *key,
396 gboolean cached)
398 struct client_s *client = assuan_get_pointer(ctx);
399 gpg_error_t rc;
400 gint timeout;
402 /* New file. */
403 if (!client->crypto->fh) {
404 if (key[0])
405 goto update_cache;
407 goto done;
410 rc = try_xml_decrypt(ctx, key, client->crypto, NULL, NULL);
412 if (rc) {
413 cleanup_client(client);
414 return send_error(ctx, rc);
417 update_cache:
418 CACHE_LOCK(client->ctx);
420 if (cached == FALSE) {
421 if (cache_update_key(client->md5file, key) == FALSE) {
422 cleanup_client(client);
423 CACHE_UNLOCK;
424 return send_syserror(ctx, ENOMEM);
427 timeout = get_key_file_integer(client->filename, "cache_timeout");
428 cache_reset_timeout(client->md5file, timeout);
430 else
431 cache_set_timeout(client->md5file, -2);
433 CACHE_UNLOCK;
435 done:
436 rc = parse_xml(ctx);
438 if (client->xml) {
439 gcry_free(client->xml);
440 client->xml = NULL;
443 if (!rc) {
444 if (client->new == FALSE)
445 send_status_all(STATUS_CACHE);
447 client->state = STATE_OPEN;
450 if (!rc && client->new == FALSE &&
451 client->crypto->fh->fh2.iter != (guint64)get_key_file_integer(client->filename, "iterations")) {
452 MUTEX_LOCK(&rcfile_mutex);
453 g_key_file_set_integer(keyfileh, client->filename, "iterations",
454 client->crypto->fh->fh2.iter);
455 MUTEX_UNLOCK(&rcfile_mutex);
456 send_status_all(STATUS_CONFIG);
459 if (!rc)
460 log_write("OPEN '%s'", client->filename);
462 cleanup_crypto(&client->crypto);
463 return send_error(ctx, rc);
466 static void req_cleanup(void *arg)
468 if (!arg)
469 return;
471 g_strfreev((gchar **)arg);
474 static int open_command(assuan_context_t ctx, char *line)
476 gboolean cached = FALSE;
477 gpg_error_t rc;
478 struct client_s *client = assuan_get_pointer(ctx);
479 gchar **req;
480 gchar *filename = NULL;
482 if ((req = split_input_line(line, " ", 2)) != NULL)
483 filename = req[0];
485 if (!filename || !*filename) {
486 g_strfreev(req);
487 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
490 if (valid_filename(filename) == FALSE) {
491 g_strfreev(req);
492 return send_error(ctx, EPWMD_INVALID_FILENAME);
495 if (client->state == STATE_OPEN)
496 cleanup_client(client);
498 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
499 CACHE_LOCK(client->ctx);
501 if (cache_has_file(client->md5file) == FALSE) {
502 if (cache_add_file(client->md5file, NULL) == FALSE) {
503 g_strfreev(req);
504 CACHE_UNLOCK;
505 return send_syserror(ctx, ENOMEM);
509 cache_incr_refcount(client->md5file);
510 CACHE_UNLOCK;
511 rc = lock_file_mutex(client);
513 if (rc) {
514 g_strfreev(req);
515 return send_error(ctx, rc);
518 client->freed = FALSE;
519 client->crypto = init_client_crypto();
521 if (!client->crypto) {
522 g_strfreev(req);
523 cleanup_client(client);
524 return send_syserror(ctx, ENOMEM);
527 client->crypto->key = gcry_malloc(gcrykeysize);
529 if (!client->crypto->key) {
530 g_strfreev(req);
531 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
532 gpg_error_from_errno(ENOMEM));
533 cleanup_client(client);
534 return send_syserror(ctx, ENOMEM);
537 memset(client->crypto->key, 0, gcrykeysize);
538 client->crypto->fh = read_file_header(filename, FALSE, &rc);
539 pth_cancel_point();
541 if (!client->crypto->fh) {
542 if (gpg_err_code_to_errno(rc) != ENOENT) {
543 log_write("%s: %s", filename, pwmd_strerror(rc));
544 g_strfreev(req);
545 cleanup_client(client);
546 return send_error(ctx, rc);
550 * New files don't need a key.
552 if ((client->xml = new_document()) == NULL) {
553 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
554 g_strfreev(req);
555 cleanup_client(client);
556 return send_syserror(ctx, ENOMEM);
559 client->len = xmlStrlen(client->xml);
560 client->new = TRUE;
561 client->filename = g_strdup(filename);
563 if (!client->filename) {
564 g_strfreev(req);
565 cleanup_client(client);
566 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
567 return send_syserror(ctx, ENOMEM);
570 if (req[1] && *req[1])
571 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
572 strlen(req[1]));
574 g_strfreev(req);
575 #ifdef WITH_PINENTRY
576 client->pinentry->filename = g_strdup(client->filename);
578 if (!client->pinentry->filename) {
579 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
580 cleanup_client(client);
581 return send_syserror(ctx, ENOMEM);
583 #endif
584 return open_command_finalize(ctx, client->crypto->key, cached);
586 else
587 client->mtime = client->crypto->fh->st.st_mtime;
589 client->filename = g_strdup(filename);
591 if (!client->filename) {
592 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
593 g_strfreev(req);
594 cleanup_client(client);
595 return send_syserror(ctx, ENOMEM);
598 #ifdef WITH_PINENTRY
599 client->pinentry->filename = g_strdup(client->filename);
601 if (!client->pinentry->filename) {
602 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
603 g_strfreev(req);
604 cleanup_client(client);
605 return send_syserror(ctx, ENOMEM);
607 #endif
609 if (client->crypto->fh->fh2.iter <= 0)
610 goto done;
612 CACHE_LOCK(client->ctx);
613 cached = cache_get_key(client->md5file, client->crypto->key);
614 CACHE_UNLOCK;
616 if (cached == FALSE) {
617 gchar *tmp = get_key_file_string(filename, "key_file");
619 if (tmp) {
620 g_free(tmp);
621 cleanup_client(client);
622 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
626 * No key specified and no matching filename found in the cache. Use
627 * pinentry to retrieve the key. Cannot return assuan_process_done()
628 * here otherwise the command will be interrupted. The event loop in
629 * client_thread() will poll the file descriptor waiting for it to
630 * become ready to read a pinentry_key_s which will contain the
631 * entered key or an error code. It will then call
632 * open_command_finalize() to to finish the command.
634 if (!req[1] || !*req[1]) {
635 #ifdef WITH_PINENTRY
636 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
638 /* From set_pinentry_defaults(). */
639 if (client->pinentry->enable == FALSE ||
640 (client->pinentry->enable == -1 && b == FALSE)) {
641 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
642 goto done;
645 g_strfreev(req);
646 rc = lock_pin_mutex(client);
648 if (rc) {
649 unlock_pin_mutex(client->pinentry);
650 cleanup_client(client);
651 return send_error(ctx, rc);
654 client->pinentry->which = PINENTRY_OPEN;
655 rc = pinentry_fork(ctx);
657 if (rc) {
658 unlock_pin_mutex(client->pinentry);
659 cleanup_client(client);
660 return send_error(ctx, rc);
663 // Called from pinentry iterate.
664 client->pinentry->cb = open_command_finalize;
665 client->pinentry->status = PINENTRY_INIT;
666 return 0;
667 #else
668 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
669 goto done;
670 #endif
673 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
674 strlen(req[1]));
677 done:
678 req_cleanup(req);
679 return open_command_finalize(ctx, client->crypto->key, cached);
682 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
683 gulong size, gpointer *out, gulong *outsize, gint *rc)
685 struct gz_s *gz;
686 gz_header h;
687 gchar buf[17];
688 gint cmd = Z_NO_FLUSH;
690 gz = g_malloc0(sizeof(struct gz_s));
692 if (!gz) {
693 *rc = gpg_error_from_errno(ENOMEM);
694 return FALSE;
697 gz->which = STATUS_COMPRESS;
698 gz->z.zalloc = z_alloc;
699 gz->z.zfree = z_free;
700 gz->z.next_in = data;
701 gz->z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
702 gz->z.avail_out = (uInt)zlib_bufsize;
703 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
705 if (!gz->out) {
706 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
707 *rc = Z_MEM_ERROR;
708 gz_cleanup(&gz);
709 return FALSE;
712 *rc = deflateInit2(&gz->z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
714 if (*rc != Z_OK) {
715 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
716 gz_cleanup(&gz);
717 return FALSE;
720 /* Rather than store the size of the uncompressed data in the file header,
721 * store it in the comment field of the gzip header. Don't give anyone too
722 * much information. Not sure why really, but it seems the right way. :)
724 memset(&h, 0, sizeof(gz_header));
725 g_snprintf(buf, sizeof(buf), "%li", size);
726 h.comment = (guchar *)buf;
727 *rc = deflateSetHeader(&gz->z, &h);
729 if (*rc != Z_OK) {
730 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
731 gz_cleanup(&gz);
732 return FALSE;
735 do {
736 gpointer p;
738 *rc = deflate(&gz->z, cmd);
740 switch (*rc) {
741 case Z_OK:
742 break;
743 case Z_BUF_ERROR:
744 if (!gz->z.avail_out) {
745 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
747 if (!p) {
748 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
749 *rc = Z_MEM_ERROR;
750 goto fail;
753 gz->out = p;
754 gz->z.next_out = gz->out + gz->z.total_out;
755 gz->z.avail_out = zlib_bufsize;
758 if (!gz->z.avail_in && gz->z.total_in < size) {
759 if (gz->z.total_in + zlib_bufsize > size)
760 gz->z.avail_in = size - gz->z.total_in;
761 else
762 gz->z.avail_in = zlib_bufsize;
764 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li",
765 gz->z.total_in, size);
767 if (*rc)
768 goto fail;
771 if (gz->z.total_in >= size)
772 cmd = Z_FINISH;
774 break;
775 case Z_STREAM_END:
776 break;
777 default:
778 goto fail;
780 } while (*rc != Z_STREAM_END);
782 pth_cancel_point();
783 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li", gz->z.total_in, size);
784 pth_cancel_point();
786 if (*rc)
787 goto fail;
789 *out = gz->out;
790 *outsize = gz->z.total_out;
791 *rc = 0;
792 gz->done = TRUE;
793 gz_cleanup(&gz);
794 return TRUE;
796 fail:
797 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
798 gz_cleanup(&gz);
799 return FALSE;
802 #define CRYPTO_BLOCKSIZE (gcryblocksize * 1024)
804 static gpg_error_t iterate_crypto_once(struct client_s *client,
805 struct client_crypto_s *crypto, status_msg_t which)
807 gpg_error_t rc = 0;
808 goffset len = CRYPTO_BLOCKSIZE;
809 gpointer p = gcry_malloc(len);
810 goffset total = 0;
811 gpointer inbuf;
813 if (!p)
814 return gpg_err_code_from_errno(ENOMEM);
816 if (crypto->insize < CRYPTO_BLOCKSIZE)
817 len = crypto->insize;
819 for (;;) {
820 inbuf = crypto->inbuf + total;
821 guchar *tmp;
823 if (len + total > crypto->insize)
824 len = gcryblocksize;
826 if (which == STATUS_ENCRYPT)
827 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
828 else
829 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
831 if (rc)
832 goto done;
834 tmp = crypto->inbuf+total;
835 memmove(tmp, p, len);
836 total += len;
838 if (total >= crypto->insize)
839 break;
841 pth_cancel_point();
844 done:
845 gcry_free(p);
846 return rc;
849 /* The crypto struct must be setup for iterations and .key. */
850 gpg_error_t do_xml_encrypt(struct client_s *client,
851 struct client_crypto_s *crypto, const gchar *filename)
853 goffset len = crypto->insize;
854 gpointer inbuf;
855 gchar *p;
856 gpg_error_t rc;
857 guint64 iter_progress = 0, n_iter = 0, xiter = 0;
858 gchar tmp[FILENAME_MAX];
859 struct stat st;
860 mode_t mode = 0;
862 if (!crypto->fh->fh2.iter) {
864 * cache_file_count() needs both .used == TRUE and a valid key in
865 * order for it to count as a used cache entry. Fixes CACHE status
866 * messages.
868 memset(crypto->key, '!', gcrykeysize);
869 goto write_file;
873 * Resize the existing xml buffer to the block size required by gcrypt
874 * rather than duplicating it and wasting memory.
876 if (crypto->insize / gcryblocksize) {
877 len = (crypto->insize / gcryblocksize) * gcryblocksize;
879 if (crypto->insize % gcryblocksize)
880 len += gcryblocksize;
883 inbuf = gcry_realloc(crypto->inbuf, len);
885 if (!inbuf) {
886 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
887 return gpg_error_from_errno(ENOMEM);
890 crypto->inbuf = inbuf;
891 crypto->insize = len;
892 gcry_create_nonce(crypto->fh->fh2.iv, sizeof(crypto->fh->fh2.iv));
893 crypto->tkey = gcry_malloc(gcrykeysize);
895 if (!crypto->tkey) {
896 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
897 return gpg_error_from_errno(ENOMEM);
900 memcpy(crypto->tkey, crypto->key, gcrykeysize);
901 guchar *tkey = crypto->tkey;
902 tkey[0] ^= 1;
904 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
905 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
906 return rc;
909 iter_progress = (guint64)get_key_file_integer(
910 client ? client->filename : "global", "iteration_progress");
912 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
913 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
914 "0 %llu", crypto->fh->fh2.iter);
915 pth_cancel_point();
917 if (rc)
918 return rc;
921 while (xiter < crypto->fh->fh2.iter-1) {
922 if (iter_progress > 0 && xiter >= iter_progress) {
923 if (!(xiter % iter_progress)) {
924 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
925 "%llu %llu", ++n_iter * iter_progress,
926 crypto->fh->fh2.iter);
927 pth_cancel_point();
929 if (rc)
930 return rc;
934 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
935 sizeof(crypto->fh->fh2.iv)))) {
936 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
937 return rc;
940 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
942 if (rc) {
943 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
944 return rc;
947 xiter++;
950 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
951 sizeof(crypto->fh->fh2.iv)))) {
952 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
953 return rc;
956 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, gcrykeysize))) {
957 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
958 return rc;
961 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
963 if (rc)
964 return rc;
966 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
967 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
968 "%llu %llu", crypto->fh->fh2.iter, crypto->fh->fh2.iter);
969 pth_cancel_point();
971 if (rc)
972 return rc;
975 write_file:
976 if (filename) {
977 if (!client && !g_strcmp0(filename, "-")) {
978 crypto->fh->fd = STDOUT_FILENO;
979 goto do_write_file;
982 if (lstat(filename, &st) == 0) {
983 pth_cancel_point();
984 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
987 * FIXME What if the file has an ACL?
989 if (!(mode & S_IWUSR))
990 return gpg_error_from_errno(EACCES);
992 else {
993 pth_cancel_point();
994 if (errno != ENOENT)
995 return gpg_error_from_errno(errno);
998 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
999 crypto->fh->fd = mkstemp(tmp);
1001 if (crypto->fh->fd == -1) {
1002 rc = errno;
1003 p = strrchr(tmp, '/');
1004 p++;
1005 log_write("%s: %s", p, strerror(rc));
1006 return gpg_error_from_errno(rc);
1009 else
1011 * xml_import() or convert_file() from command line.
1013 crypto->fh->fd = STDOUT_FILENO;
1015 do_write_file:
1016 crypto->fh->fh2.magic[0] = '\177';
1017 crypto->fh->fh2.magic[1] = 'P';
1018 crypto->fh->fh2.magic[2] = 'W';
1019 crypto->fh->fh2.magic[3] = 'M';
1020 crypto->fh->fh2.magic[4] = 'D';
1021 crypto->fh->fh2.version = VERSION_HEX;
1022 len = pth_write(crypto->fh->fd, &crypto->fh->fh2, sizeof(crypto->fh->fh2));
1023 pth_cancel_point();
1025 if (len != sizeof(crypto->fh->fh2)) {
1026 len = errno;
1028 if (filename && g_strcmp0(filename, "-"))
1029 unlink(tmp);
1031 return gpg_error_from_errno(len);
1034 len = pth_write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1035 pth_cancel_point();
1037 if (len != crypto->insize) {
1038 pth_cancel_point();
1039 len = errno;
1041 if (filename && g_strcmp0(filename, "-")) {
1042 unlink(tmp);
1043 pth_cancel_point();
1046 return gpg_error_from_errno(len);
1049 if (fsync(crypto->fh->fd) == -1) {
1050 pth_cancel_point();
1051 len = errno;
1053 if (filename && g_strcmp0(filename, "-"))
1054 unlink(tmp);
1056 return gpg_error_from_errno(len);
1059 if (filename && g_strcmp0(filename, "-")) {
1060 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1061 gchar tmp2[FILENAME_MAX];
1063 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1065 if (rename(filename, tmp2) == -1) {
1066 pth_cancel_point();
1067 unlink(tmp);
1068 len = errno;
1069 return gpg_error_from_errno(len);
1073 if (rename(tmp, filename) == -1) {
1074 pth_cancel_point();
1075 len = errno;
1076 unlink(tmp);
1077 return gpg_error_from_errno(len);
1080 if (mode) {
1081 chmod(filename, mode);
1082 pth_cancel_point();
1086 if (client && lstat(filename, &st) == 0)
1087 client->mtime = st.st_mtime;
1089 pth_cancel_point();
1090 return 0;
1093 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1094 gboolean cached)
1096 struct client_s *client = assuan_get_pointer(ctx);
1097 gpointer xmlbuf;
1098 gulong len, outsize = 0;
1099 guint iter;
1100 gint timeout;
1101 gpointer outbuf;
1102 gint zrc;
1103 gpg_error_t rc;
1105 if (client->crypto->key && client->crypto->key != key)
1106 gcry_free(client->crypto->key);
1108 client->crypto->key = key;
1109 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1110 iter = (guint)get_key_file_integer(client->filename, "compression_level");
1112 if (iter < 0)
1113 iter = 0;
1115 if (do_compress(ctx, (gint)iter, xmlbuf, len, &outbuf, &outsize, &zrc)
1116 == FALSE) {
1117 xmlFree(xmlbuf);
1118 cleanup_crypto(&client->crypto);
1120 if (zrc == Z_MEM_ERROR)
1121 return send_syserror(ctx, ENOMEM);
1122 else
1123 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1125 else {
1126 gcry_free(xmlbuf);
1127 xmlbuf = outbuf;
1128 len = outsize;
1131 client->crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1133 if (!client->crypto->fh) {
1134 cleanup_crypto(&client->crypto);
1135 gcry_free(outbuf);
1136 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1137 return send_syserror(ctx, ENOMEM);
1140 iter = get_key_file_integer(client->filename, "iterations");
1141 client->crypto->fh->fh2.iter = iter < 0 ? 0 : iter;
1142 client->crypto->inbuf = xmlbuf;
1143 client->crypto->insize = len;
1144 rc = do_xml_encrypt(client, client->crypto, client->filename);
1146 if (rc) {
1147 cleanup_crypto(&client->crypto);
1148 return send_error(ctx, rc);
1151 timeout = get_key_file_integer(client->filename, "cache_timeout");
1152 CACHE_LOCK(client->ctx);
1154 if (cached) {
1155 cache_reset_timeout(client->md5file, timeout);
1156 CACHE_UNLOCK;
1158 if (client->new == TRUE)
1159 send_status_all(STATUS_CACHE);
1161 client->new = FALSE;
1162 cleanup_crypto(&client->crypto);
1163 return send_error(ctx, 0);
1166 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1167 CACHE_UNLOCK;
1168 cleanup_crypto(&client->crypto);
1169 return send_syserror(ctx, ENOMEM);
1172 client->new = FALSE;
1173 cache_reset_timeout(client->md5file, timeout);
1174 CACHE_UNLOCK;
1175 send_status_all(STATUS_CACHE);
1176 cleanup_crypto(&client->crypto);
1177 return send_error(ctx, 0);
1180 static int save_command(assuan_context_t ctx, char *line)
1182 gboolean cached = FALSE;
1183 struct stat st;
1184 struct client_s *client = assuan_get_pointer(ctx);
1185 gpg_error_t rc;
1187 rc = lock_file_mutex(client);
1189 if (rc)
1190 return send_error(ctx, rc);
1192 rc = file_modified(client);
1194 if (rc)
1195 return send_error(ctx, rc);
1197 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1198 return send_syserror(ctx, errno);
1200 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1201 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1202 return send_error(ctx, GPG_ERR_ENOANO);
1205 CACHE_LOCK(ctx);
1206 cached = cache_iscached(client->md5file);
1207 CACHE_UNLOCK;
1210 * If a cache entry doesn't exist for this file and the file has a
1211 * "key_file" or "key" parameter, then it's an error. The reason is that
1212 * cache expiration would be useless.
1214 if (cached == FALSE) {
1215 gchar *tmp = get_key_file_string(client->filename, "key_file");
1217 if (tmp) {
1218 g_free(tmp);
1219 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1223 cached = FALSE;
1224 client->crypto = init_client_crypto();
1226 if (!client->crypto) {
1227 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1228 return send_syserror(ctx, ENOMEM);
1231 client->crypto->key = gcry_malloc(gcrykeysize);
1233 if (!client->crypto->key) {
1234 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1235 cleanup_crypto(&client->crypto);
1236 return send_syserror(ctx, ENOMEM);
1239 memset(client->crypto->key, '!', gcrykeysize);
1241 if (get_key_file_integer(client->filename, "iterations") <= 0)
1242 goto done;
1244 if (!line || !*line) {
1245 client->crypto->tkey = gcry_malloc(gcrykeysize);
1247 if (!client->crypto->tkey) {
1248 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1249 cleanup_crypto(&client->crypto);
1250 return send_syserror(ctx, ENOMEM);
1253 memset(client->crypto->tkey, '!', gcrykeysize);
1254 CACHE_LOCK(ctx);
1256 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1257 memcmp(client->crypto->key, client->crypto->tkey,
1258 gcrykeysize) == 0) {
1259 CACHE_UNLOCK;
1261 #ifdef WITH_PINENTRY
1262 if (client->pinentry->enable == FALSE ||
1263 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1264 /* Empty keys are allowed. */
1265 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1266 goto done;
1269 lock_pin_mutex(client);
1270 client->pinentry->which = PINENTRY_SAVE;
1271 rc = pinentry_fork(ctx);
1273 if (rc) {
1274 unlock_pin_mutex(client->pinentry);
1275 return send_error(ctx, rc);
1278 client->pinentry->cb = save_command_finalize;
1279 client->pinentry->status = PINENTRY_INIT;
1280 return 0;
1281 #else
1282 /* Empty keys are allowed. */
1283 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1284 goto done;
1285 #endif
1287 else {
1288 CACHE_UNLOCK;
1289 cached = TRUE;
1292 else {
1293 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1294 strlen(line));
1295 memset(line, 0, strlen(line));
1298 done:
1299 return save_command_finalize(ctx, client->crypto->key, cached);
1302 static int delete_command(assuan_context_t ctx, char *line)
1304 struct client_s *client = assuan_get_pointer(ctx);
1305 gchar **req;
1306 gpg_error_t rc;
1307 xmlNodePtr n;
1309 rc = file_modified(client);
1311 if (rc)
1312 return send_error(ctx, rc);
1314 if (strchr(line, '\t'))
1315 req = split_input_line(line, "\t", -1);
1316 else
1317 req = split_input_line(line, " ", -1);
1319 if (!req || !*req)
1320 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1322 n = find_account(client->doc, &req, &rc, NULL, 0);
1324 if (!n) {
1325 g_strfreev(req);
1326 return send_error(ctx, rc);
1330 * No sub-node defined. Remove the entire node (account).
1332 if (!req[1]) {
1333 if (n) {
1334 xmlUnlinkNode(n);
1335 xmlFreeNode(n);
1338 g_strfreev(req);
1339 return send_error(ctx, 0);
1342 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1343 g_strfreev(req);
1345 if (!n)
1346 return send_error(ctx, rc);
1348 if (n) {
1349 xmlUnlinkNode(n);
1350 xmlFreeNode(n);
1353 return send_error(ctx, 0);
1357 * Don't return with assuan_process_done() here. This has been called from
1358 * assuan_process_next() and the command should be finished in
1359 * client_thread().
1361 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1362 gsize len)
1364 assuan_context_t ctx = data;
1365 struct client_s *client = assuan_get_pointer(ctx);
1366 gchar **req;
1367 xmlNodePtr n;
1368 gpg_error_t rc = file_modified(client);
1370 if (assuan_rc || rc) {
1371 if (line)
1372 xfree(line);
1373 return assuan_rc ? assuan_rc : rc;
1376 req = split_input_line((gchar *)line, "\t", 0);
1377 xfree(line);
1379 if (!req || !*req)
1380 return EPWMD_COMMAND_SYNTAX;
1382 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1383 g_strfreev(req);
1384 return EPWMD_INVALID_ELEMENT;
1387 if (valid_element_path(req+1, TRUE) == FALSE) {
1388 g_strfreev(req);
1389 return EPWMD_INVALID_ELEMENT;
1392 again:
1393 n = find_account(client->doc, &req, &rc, NULL, 0);
1395 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1396 rc = new_account(client->doc, *req);
1398 if (rc) {
1399 g_strfreev(req);
1400 return rc;
1403 goto again;
1406 if (!n) {
1407 g_strfreev(req);
1408 return rc;
1411 if (req[1]) {
1412 if (!n->children)
1413 create_elements_cb(n, req+1, &rc, NULL);
1414 else
1415 find_elements(client->doc, n->children, req+1, &rc,
1416 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1419 g_strfreev(req);
1420 client->inquire_status = INQUIRE_DONE;
1421 return rc;
1424 static int store_command(assuan_context_t ctx, char *line)
1426 struct client_s *client = assuan_get_pointer(ctx);
1427 gpg_error_t rc = file_modified(client);
1429 if (rc)
1430 return send_error(ctx, rc);
1432 pth_cancel_point();
1433 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1434 pth_cancel_point();
1436 if (rc)
1437 return send_error(ctx, rc);
1439 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1440 client->inquire_status = INQUIRE_BUSY;
1441 return 0;
1444 static void *send_data_cb(void *arg)
1446 struct assuan_cmd_s *data = arg;
1447 gpg_error_t rc = assuan_send_data(data->ctx, data->line, data->line_len);
1448 pth_exit((void *)rc);
1449 return NULL;
1452 /* For every assuan command that needs to be sent to the client, a timeout is
1453 * needed to determine if the client lost the connection. The timeout is the
1454 * same as the "keepalive" configuration parameter or a default if unset.
1456 gpg_error_t do_assuan_command(assuan_context_t ctx,
1457 void *(*cb)(void *data), void *data)
1459 pth_attr_t attr = pth_attr_new();
1460 pth_t tid;
1461 gint n;
1462 gint to = get_key_file_integer("global", "keepalive");
1463 pth_event_t ev, tev;
1464 pth_status_t st;
1465 gpg_error_t rc;
1467 pth_attr_init(attr);
1468 pth_attr_set(attr, PTH_ATTR_JOINABLE, TRUE);
1469 tid = pth_spawn(attr, cb, data);
1470 n = errno;
1471 pth_attr_destroy(attr);
1473 if (!tid) {
1474 log_write("%s(%i): pth_spawn(): %s", __FILE__, __LINE__,
1475 _gpg_strerror(gpg_error_from_errno(n)));
1476 return gpg_error_from_errno(n);
1479 to = to <= 0 ? DEFAULT_KEEPALIVE_TO : to;
1480 ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, tid);
1481 tev = pth_event(PTH_EVENT_TIME, pth_timeout(to, 0));
1482 ev = pth_event_concat(ev, tev, NULL);
1483 pth_yield(tid);
1484 pth_wait(ev);
1485 st = pth_event_status(ev);
1487 if (st == PTH_STATUS_FAILED) {
1488 pth_cancel(tid);
1489 rc = GPG_ERR_ASS_WRITE_ERROR;
1491 else if (st == PTH_STATUS_OCCURRED) {
1492 pth_join(tid, (void **)&rc);
1494 else {
1495 st = pth_event_status(tev);
1497 if (st == PTH_STATUS_OCCURRED) {
1498 pth_cancel(tid);
1499 rc = GPG_ERR_ASS_WRITE_ERROR;
1503 pth_event_free(ev, PTH_FREE_THIS);
1504 pth_event_free(tev, PTH_FREE_THIS);
1505 return rc;
1508 static gpg_error_t xfer_data(assuan_context_t ctx, const gchar *line,
1509 gint total)
1511 int to_send;
1512 int sent = 0;
1513 gpg_error_t rc;
1514 struct assuan_cmd_s data;
1515 int progress = get_key_file_integer("global", "xfer_progress");
1516 int flush = 0;
1518 progress = progress>0 ? (progress/ASSUAN_LINELENGTH)*ASSUAN_LINELENGTH : 0;
1520 if (total < ASSUAN_LINELENGTH)
1521 to_send = total;
1522 else
1523 to_send = ASSUAN_LINELENGTH;
1525 data.ctx = ctx;
1526 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1528 if (rc)
1529 return rc;
1531 again:
1532 do {
1533 if (sent + to_send > total)
1534 to_send = total - sent;
1536 data.line = flush ? NULL : (gchar *)line+sent;
1537 data.line_len = flush ? 0 : to_send;
1538 rc = do_assuan_command(ctx, send_data_cb, &data);
1540 if (!rc) {
1541 sent += flush ? 0 : to_send;
1543 if ((progress && !(sent % progress) && sent != total) ||
1544 (sent == total && flush))
1545 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1547 if (!flush && !rc && sent == total) {
1548 flush = 1;
1549 goto again;
1552 } while (!rc && sent < total);
1554 return rc;
1557 static int get_command(assuan_context_t ctx, char *line)
1559 struct client_s *client = assuan_get_pointer(ctx);
1560 gchar **req;
1561 gpg_error_t rc;
1562 xmlNodePtr n;
1564 rc = file_modified(client);
1566 if (rc)
1567 return send_error(ctx, rc);
1569 req = split_input_line(line, "\t", -1);
1571 if (!req || !*req) {
1572 g_strfreev(req);
1573 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1576 n = find_account(client->doc, &req, &rc, NULL, 0);
1578 if (!n) {
1579 g_strfreev(req);
1580 return send_error(ctx, rc);
1583 if (req[1])
1584 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1586 g_strfreev(req);
1588 if (rc)
1589 return send_error(ctx, rc);
1591 if (!n || !n->children)
1592 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1594 n = find_text_node(n->children);
1596 if (!n || !n->content || !*n->content)
1597 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1599 pth_cancel_point();
1600 rc = xfer_data(ctx, (gchar *)n->content, xmlStrlen(n->content));
1601 return send_error(ctx, rc);
1604 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1605 gpg_error_t *rc, gchar **req_orig, void *data)
1607 gchar *path = *(gchar **)data;
1608 gchar *tmp = NULL, *result;
1610 if (path) {
1611 g_free(path);
1612 *(gchar **)data = NULL;
1615 path = g_strjoinv("\t", target);
1617 if (!path) {
1618 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1619 *rc = gpg_error_from_errno(ENOMEM);
1620 return NULL;
1623 if (req_orig) {
1624 tmp = g_strjoinv("\t", req_orig);
1626 if (!tmp) {
1627 g_free(path);
1628 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1629 *rc = gpg_error_from_errno(ENOMEM);
1630 return NULL;
1634 if (tmp && *tmp)
1635 result = g_strdup_printf("%s\t%s", path, tmp);
1636 else
1637 result = g_strdup(path);
1639 if (!result) {
1640 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1641 *rc = gpg_error_from_errno(ENOMEM);
1642 g_free(path);
1643 g_free(tmp);
1644 return NULL;
1647 g_free(path);
1648 g_free(tmp);
1649 *(gchar **)data = result;
1650 return node;
1653 static void list_command_cleanup1(void *arg);
1654 static int realpath_command(assuan_context_t ctx, char *line)
1656 gpg_error_t rc;
1657 struct client_s *client = assuan_get_pointer(ctx);
1658 gchar **req;
1659 gchar *t;
1660 gint i;
1661 xmlNodePtr n;
1662 GString *string;
1663 gchar *rp = NULL;
1665 rc = file_modified(client);
1667 if (rc)
1668 return send_error(ctx, rc);
1670 if (strchr(line, '\t') != NULL) {
1671 if ((req = split_input_line(line, "\t", 0)) == NULL)
1672 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1674 else {
1675 if ((req = split_input_line(line, " ", 0)) == NULL)
1676 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1679 n = find_account(client->doc, &req, &rc, NULL, 0);
1681 if (!n) {
1682 g_strfreev(req);
1683 return send_error(ctx, rc);
1686 rp = g_strjoinv("\t", req);
1688 if (!rp) {
1689 g_strfreev(req);
1690 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1691 return send_syserror(ctx, ENOMEM);
1694 if (req[1]) {
1695 n = find_elements(client->doc, n->children, req+1, &rc,
1696 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1698 if (!n) {
1699 g_free(rp);
1700 g_strfreev(req);
1701 return send_error(ctx, rc);
1705 string = g_string_new(rp);
1706 g_free(rp);
1707 g_strfreev(req);
1709 if (!string) {
1710 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1711 return send_syserror(ctx, ENOMEM);
1714 again:
1715 for (i = 0, t = string->str + i; *t; t++, i++) {
1716 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1717 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1718 goto again;
1722 rc = xfer_data(ctx, string->str, string->len);
1723 list_command_cleanup1(string);
1724 return send_error(ctx, rc);
1727 static void list_command_cleanup1(void *arg)
1729 g_string_free((GString *)arg, TRUE);
1732 static void list_command_cleanup2(void *arg)
1734 struct element_list_s *elements = arg;
1736 if (elements) {
1737 gint total = g_slist_length(elements->list);
1738 gint i;
1740 for (i = 0; i < total; i++) {
1741 gchar *tmp = g_slist_nth_data(elements->list, i);
1742 g_free(tmp);
1745 g_slist_free(elements->list);
1747 if (elements->prefix)
1748 g_free(elements->prefix);
1750 g_free(elements);
1754 static int list_command(assuan_context_t ctx, char *line)
1756 struct client_s *client = assuan_get_pointer(ctx);
1757 gpg_error_t rc;
1758 struct element_list_s *elements = NULL;
1759 gchar *tmp;
1761 if (disable_list_and_dump == TRUE)
1762 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1764 rc = file_modified(client);
1766 if (rc)
1767 return send_error(ctx, rc);
1769 if (!*line) {
1770 GString *str;
1772 rc = list_accounts(client->doc, &str);
1774 if (rc)
1775 return send_error(ctx, rc);
1777 rc = xfer_data(ctx, str->str, str->len);
1778 list_command_cleanup1(str);
1779 return send_error(ctx, rc);
1782 elements = g_malloc0(sizeof(struct element_list_s));
1784 if (!elements) {
1785 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1786 rc = gpg_err_code_from_errno(ENOMEM);
1787 goto fail;
1790 rc = create_path_list(client->doc, elements, line);
1792 if (rc)
1793 goto fail;
1795 if (elements) {
1796 gint total = g_slist_length(elements->list);
1797 gint i;
1798 GString *str;
1800 if (!total) {
1801 rc = EPWMD_EMPTY_ELEMENT;
1802 goto fail;
1805 str = g_string_new(NULL);
1807 if (!str) {
1808 rc = gpg_err_code_from_errno(ENOMEM);
1809 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1810 goto fail;
1813 for (i = 0; i < total; i++) {
1814 tmp = g_slist_nth_data(elements->list, i);
1815 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1818 rc = xfer_data(ctx, str->str, str->len);
1819 list_command_cleanup1(str);
1821 else
1822 rc = EPWMD_EMPTY_ELEMENT;
1824 fail:
1825 list_command_cleanup2(elements);
1826 return send_error(ctx, rc);
1829 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1830 const gchar *value)
1832 xmlAttrPtr a;
1834 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1835 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1837 if (!a)
1838 return EPWMD_LIBXML_ERROR;
1840 else
1841 xmlNodeSetContent(a->children, (xmlChar *)value);
1843 return 0;
1847 * req[0] - element path
1849 static int attribute_list(assuan_context_t ctx, gchar **req)
1851 struct client_s *client = assuan_get_pointer(ctx);
1852 gchar **attrlist = NULL;
1853 gint i = 0;
1854 gchar **path = NULL;
1855 xmlAttrPtr a;
1856 xmlNodePtr n, an;
1857 gchar *line;
1858 gpg_error_t rc;
1860 if (!req || !req[0])
1861 return EPWMD_COMMAND_SYNTAX;
1863 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1865 * The first argument may be only an account.
1867 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1868 return EPWMD_COMMAND_SYNTAX;
1871 n = find_account(client->doc, &path, &rc, NULL, 0);
1873 if (!n) {
1874 g_strfreev(path);
1875 return rc;
1878 if (path[1]) {
1879 n = find_elements(client->doc, n->children, path+1, &rc,
1880 NULL, NULL, NULL, FALSE, 0, NULL);
1882 if (!n) {
1883 g_strfreev(path);
1884 return rc;
1888 g_strfreev(path);
1890 for (a = n->properties; a; a = a->next) {
1891 gchar **pa;
1893 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1894 if (attrlist)
1895 g_strfreev(attrlist);
1897 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1898 return gpg_error_from_errno(ENOMEM);
1901 attrlist = pa;
1902 an = a->children;
1903 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1905 if (!attrlist[i]) {
1906 g_strfreev(attrlist);
1907 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1908 return gpg_error_from_errno(ENOMEM);
1911 attrlist[++i] = NULL;
1914 if (!attrlist)
1915 return EPWMD_EMPTY_ELEMENT;
1917 line = g_strjoinv("\n", attrlist);
1919 if (!line) {
1920 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1921 g_strfreev(attrlist);
1922 return gpg_error_from_errno(ENOMEM);
1925 rc = xfer_data(ctx, line, strlen(line));
1926 g_free(line);
1927 req_cleanup(attrlist);
1928 return rc;
1932 * req[0] - attribute
1933 * req[1] - element path
1935 static int attribute_delete(struct client_s *client, gchar **req)
1937 xmlAttrPtr a;
1938 xmlNodePtr n;
1939 gchar **path = NULL;
1940 gpg_error_t rc;
1942 if (!req || !req[0] || !req[1])
1943 return EPWMD_COMMAND_SYNTAX;
1945 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1947 * The first argument may be only an account.
1949 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1950 return EPWMD_COMMAND_SYNTAX;
1954 * Don't remove the "name" attribute for the account element. To remove an
1955 * account use DELETE <account>.
1957 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1958 rc = EPWMD_ATTR_SYNTAX;
1959 goto fail;
1962 n = find_account(client->doc, &path, &rc, NULL, 0);
1964 if (!n)
1965 goto fail;
1967 if (path[1]) {
1968 n = find_elements(client->doc, n->children, path+1, &rc,
1969 NULL, NULL, NULL, FALSE, 0, NULL);
1971 if (!n)
1972 goto fail;
1975 g_strfreev(path);
1977 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1978 return EPWMD_ATTR_NOT_FOUND;
1980 if (xmlRemoveProp(a) == -1)
1981 return EPWMD_LIBXML_ERROR;
1983 return 0;
1985 fail:
1986 g_strfreev(path);
1987 return rc;
1990 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1991 gpg_error_t *rc)
1993 gchar **src = *path;
1994 gchar **src_orig = g_strdupv(src);
1995 xmlNodePtr n = NULL;
1997 *rc = 0;
1999 if (!src_orig) {
2000 *rc = gpg_error_from_errno(ENOMEM);
2001 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2002 goto fail;
2005 again:
2006 n = find_account(client->doc, &src, rc, NULL, 0);
2008 if (!n) {
2009 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
2010 *rc = new_account(client->doc, src[0]);
2012 if (*rc)
2013 goto fail;
2015 goto again;
2017 else
2018 goto fail;
2021 if (src[1]) {
2022 if (!n->children)
2023 n = create_target_elements_cb(n, src+1, rc, NULL);
2024 else
2025 n = find_elements(client->doc, n->children, src+1, rc,
2026 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2028 if (!n)
2029 goto fail;
2032 * Reset the position of the element tree now that the elements
2033 * have been created.
2035 g_strfreev(src);
2036 src = src_orig;
2037 src_orig = NULL;
2038 n = find_account(client->doc, &src, rc, NULL, 0);
2040 if (!n)
2041 goto fail;
2043 n = find_elements(client->doc, n->children, src+1, rc,
2044 NULL, NULL, NULL, FALSE, 0, NULL);
2046 if (!n)
2047 goto fail;
2050 fail:
2051 if (src_orig)
2052 g_strfreev(src_orig);
2054 *path = src;
2055 return n;
2059 * Creates a "target" attribute. When other commands encounter an element with
2060 * this attribute, the element path is modified to the target value. If the
2061 * source element path doesn't exist when using 'ATTR SET target', it is
2062 * created, but the destination element path must exist.
2064 * req[0] - source element path
2065 * req[1] - destination element path
2067 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2069 gchar **src, **dst, *line = NULL;
2070 gpg_error_t rc;
2071 xmlNodePtr n;
2073 if (!req || !req[0] || !req[1])
2074 return EPWMD_COMMAND_SYNTAX;
2076 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2078 * The first argument may be only an account.
2080 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2081 return EPWMD_COMMAND_SYNTAX;
2084 if (valid_element_path(src, FALSE) == FALSE) {
2085 g_strfreev(src);
2086 return EPWMD_INVALID_ELEMENT;
2089 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2091 * The first argument may be only an account.
2093 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2094 rc = EPWMD_COMMAND_SYNTAX;
2095 goto fail;
2099 n = find_account(client->doc, &dst, &rc, NULL, 0);
2102 * Make sure the destination element path exists.
2104 if (!n)
2105 goto fail;
2107 if (dst[1]) {
2108 n = find_elements(client->doc, n->children, dst+1, &rc,
2109 NULL, NULL, NULL, FALSE, 0, NULL);
2111 if (!n)
2112 goto fail;
2115 n = create_element_path(client, &src, &rc);
2117 if (rc)
2118 goto fail;
2120 line = g_strjoinv("\t", dst);
2122 if (!line) {
2123 rc = gpg_error_from_errno(ENOMEM);
2124 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2125 goto fail;
2128 rc = add_attribute(n, "target", line);
2130 fail:
2131 g_free(line);
2132 g_strfreev(src);
2133 g_strfreev(dst);
2134 return rc;
2138 * req[0] - account name
2139 * req[1] - new name
2141 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2143 gpg_error_t rc;
2144 gchar **tmp;
2145 xmlNodePtr n;
2147 tmp = g_strdupv(req);
2149 if (!tmp) {
2150 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2151 return gpg_error_from_errno(ENOMEM);
2154 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2155 g_strfreev(tmp);
2157 if (!n)
2158 return rc;
2160 if (g_utf8_collate(req[0], req[1]) == 0)
2161 return 0;
2164 * Will not overwrite an existing account.
2166 tmp = g_strdupv(req+1);
2168 if (!tmp) {
2169 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2170 return gpg_error_from_errno(ENOMEM);
2173 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2174 g_strfreev(tmp);
2176 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2177 return rc;
2179 if (n)
2180 return EPWMD_ACCOUNT_EXISTS;
2183 * Whitespace not allowed in account names.
2185 if (contains_whitespace(req[1]) == TRUE)
2186 return EPWMD_ATTR_SYNTAX;
2188 tmp = g_strdupv(req);
2190 if (!tmp) {
2191 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2192 return gpg_error_from_errno(ENOMEM);
2195 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2196 g_strfreev(tmp);
2198 if (!n)
2199 return EPWMD_ELEMENT_NOT_FOUND;
2201 return add_attribute(n, "name", req[1]);
2205 * req[0] - attribute
2206 * req[1] - element path
2208 static int attribute_get(assuan_context_t ctx, gchar **req)
2210 struct client_s *client = assuan_get_pointer(ctx);
2211 xmlNodePtr n;
2212 xmlChar *a;
2213 gchar **path= NULL;
2214 gpg_error_t rc;
2216 if (!req || !req[0] || !req[1])
2217 return EPWMD_COMMAND_SYNTAX;
2219 if (strchr(req[1], '\t')) {
2220 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2221 return EPWMD_COMMAND_SYNTAX;
2223 else {
2224 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2225 return EPWMD_COMMAND_SYNTAX;
2228 n = find_account(client->doc, &path, &rc, NULL, 0);
2230 if (!n)
2231 goto fail;
2233 if (path[1]) {
2234 n = find_elements(client->doc, n->children, path+1, &rc,
2235 NULL, NULL, NULL, FALSE, 0, NULL);
2237 if (!n)
2238 goto fail;
2241 g_strfreev(path);
2243 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2244 return EPWMD_ATTR_NOT_FOUND;
2246 rc = xfer_data(ctx, (gchar *)a, xmlStrlen(a));
2247 xmlFree(a);
2248 return rc;
2250 fail:
2251 g_strfreev(path);
2252 return rc;
2256 * req[0] - attribute
2257 * req[1] - element path
2258 * req[2] - value
2260 static int attribute_set(struct client_s *client, gchar **req)
2262 gchar **path = NULL;
2263 gpg_error_t rc;
2264 xmlNodePtr n;
2266 if (!req || !req[0] || !req[1] || !req[2])
2267 return EPWMD_COMMAND_SYNTAX;
2270 * Reserved attribute names.
2272 if (g_utf8_collate(req[0], "name") == 0) {
2274 * Only reserved for the account element. Not the rest of the
2275 * document.
2277 if (strchr(req[1], '\t') == NULL)
2278 return name_attribute(client, req + 1);
2280 else if (g_utf8_collate(req[0], "target") == 0)
2281 return target_attribute(client, req + 1);
2283 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2285 * The first argument may be only an account.
2287 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2288 return EPWMD_COMMAND_SYNTAX;
2291 n = find_account(client->doc, &path, &rc, NULL, 0);
2293 if (!n)
2294 goto fail;
2296 if (path[1]) {
2297 n = find_elements(client->doc, n->children, path+1, &rc,
2298 NULL, NULL, NULL, FALSE, 0, NULL);
2300 if (!n)
2301 goto fail;
2304 g_strfreev(path);
2305 return add_attribute(n, req[0], req[2]);
2307 fail:
2308 g_strfreev(path);
2309 return rc;
2313 * req[0] - command
2314 * req[1] - attribute name or element path if command is LIST
2315 * req[2] - element path
2316 * req[2] - element path or value
2318 static int attr_command(assuan_context_t ctx, char *line)
2320 struct client_s *client = assuan_get_pointer(ctx);
2321 gchar **req;
2322 gpg_error_t rc = 0;
2324 rc = file_modified(client);
2326 if (rc)
2327 return send_error(ctx, rc);
2329 req = split_input_line(line, " ", 4);
2331 if (!req || !req[0] || !req[1]) {
2332 g_strfreev(req);
2333 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2336 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2337 rc = attribute_set(client, req+1);
2338 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2339 rc = attribute_get(ctx, req+1);
2340 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2341 rc = attribute_delete(client, req+1);
2342 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2343 rc = attribute_list(ctx, req+1);
2344 else
2345 rc = EPWMD_COMMAND_SYNTAX;
2347 req_cleanup(req);
2348 return send_error(ctx, rc);
2351 static int iscached_command(assuan_context_t ctx, char *line)
2353 gchar **req = split_input_line(line, " ", 0);
2354 guchar md5file[16];
2355 gchar *path, *tmp;
2357 if (!req || !*req) {
2358 g_strfreev(req);
2359 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2362 if (!valid_filename(req[0])) {
2363 g_strfreev(req);
2364 return EPWMD_INVALID_FILENAME;
2367 tmp = get_key_file_string("global", "data_directory");
2369 if (!tmp) {
2370 g_strfreev(req);
2371 return gpg_error_from_errno(ENOMEM);
2374 path = expand_homedir(tmp);
2376 if (!path) {
2377 g_strfreev(req);
2378 g_free(tmp);
2379 return gpg_error_from_errno(ENOMEM);
2382 g_free(tmp);
2383 tmp = path;
2384 path = g_strdup_printf("%s/%s", tmp, req[0]);
2385 g_free(tmp);
2387 if (!path) {
2388 g_strfreev(req);
2389 return gpg_error_from_errno(ENOMEM);
2392 if (access(path, R_OK) == -1) {
2393 gpg_error_t rc = gpg_error_from_syserror();
2395 g_free(path);
2396 g_strfreev(req);
2397 return send_error(ctx, rc);
2400 g_free(path);
2401 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2402 g_strfreev(req);
2403 CACHE_LOCK(ctx);
2405 if (cache_iscached(md5file) == FALSE) {
2406 CACHE_UNLOCK;
2407 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2410 CACHE_UNLOCK;
2411 return send_error(ctx, 0);
2414 static int clearcache_command(assuan_context_t ctx, char *line)
2416 struct client_s *client = assuan_get_pointer(ctx);
2417 gchar **req = split_input_line(line, " ", 0);
2418 guchar md5file[16];
2420 CACHE_LOCK(ctx);
2422 if (!req || !*req) {
2423 g_strfreev(req);
2424 cache_clear(client->md5file, 2);
2425 CACHE_UNLOCK;
2426 return send_error(ctx, 0);
2429 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2430 g_strfreev(req);
2432 if (cache_clear(md5file, 1) == FALSE) {
2433 CACHE_UNLOCK;
2434 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2437 CACHE_UNLOCK;
2438 return send_error(ctx, 0);
2441 static int cachetimeout_command(assuan_context_t ctx, char *line)
2443 guchar md5file[16];
2444 glong timeout;
2445 gchar **req = split_input_line(line, " ", 0);
2446 gchar *p;
2448 if (!req || !*req || !req[1]) {
2449 g_strfreev(req);
2450 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2453 errno = 0;
2454 timeout = strtol(req[0], &p, 10);
2456 if (errno != 0 || *p != 0) {
2457 g_strfreev(req);
2458 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2461 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2462 CACHE_LOCK(client->ctx);
2464 if (cache_set_timeout(md5file, timeout) == FALSE) {
2465 CACHE_UNLOCK;
2466 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2469 CACHE_UNLOCK;
2470 return send_error(ctx, 0);
2473 static int dump_command(assuan_context_t ctx, char *line)
2475 xmlChar *xml;
2476 gssize len;
2477 struct client_s *client = assuan_get_pointer(ctx);
2478 gpg_error_t rc;
2480 if (disable_list_and_dump == TRUE)
2481 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2483 rc = file_modified(client);
2485 if (rc)
2486 return send_error(ctx, rc);
2488 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2490 if (!xml) {
2491 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2492 return send_syserror(ctx, ENOMEM);
2495 rc = xfer_data(ctx, (gchar *)xml, len);
2496 xmlFree(xml);
2497 return send_error(ctx, rc);
2500 static int getconfig_command(assuan_context_t ctx, gchar *line)
2502 struct client_s *client = assuan_get_pointer(ctx);
2503 gpg_error_t rc = 0;
2504 gchar filename[255]={0}, param[747]={0};
2505 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2507 if (strchr(line, ' ')) {
2508 sscanf(line, " %254[^ ] %746c", filename, param);
2509 fp = filename;
2510 paramp = param;
2513 if (fp && !valid_filename(fp))
2514 return send_error(ctx, EPWMD_INVALID_FILENAME);
2516 paramp = g_ascii_strdown(paramp, -1);
2518 if (!paramp) {
2519 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2520 return send_syserror(ctx, ENOMEM);
2523 if (fp && !g_strcmp0(paramp, "iterations")) {
2524 if (!(client->opts & OPT_ITERATIONS) || fp != client->filename) {
2525 file_header_internal_t *fh = read_file_header(fp, FALSE, &rc);
2527 if (!fh && rc != GPG_ERR_ENOENT)
2528 return send_error(ctx, rc);
2530 if (!rc) {
2531 g_free(paramp);
2532 p = g_strdup_printf("%llu", fh->fh2.iter);
2533 close_file_header(fh);
2535 if (!p) {
2536 log_write("%s(%i): %s", __FILE__, __LINE__,
2537 strerror(ENOMEM));
2538 return send_syserror(ctx, ENOMEM);
2541 goto done;
2546 p = get_key_file_string(fp ? fp : "global", paramp);
2547 g_free(paramp);
2549 if (!p)
2550 return send_error(ctx, GPG_ERR_NO_VALUE);
2552 tmp = expand_homedir(p);
2553 g_free(p);
2555 if (!tmp) {
2556 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2557 return send_syserror(ctx, ENOMEM);
2560 p = tmp;
2561 done:
2562 rc = xfer_data(ctx, p, strlen(p));
2563 g_free(p);
2564 return send_error(ctx, rc);
2567 struct xpath_s {
2568 xmlXPathContextPtr xp;
2569 xmlXPathObjectPtr result;
2570 xmlBufferPtr buf;
2571 gchar **req;
2574 static void xpath_command_cleanup(void *arg)
2576 struct xpath_s *xpath = arg;
2578 req_cleanup(xpath->req);
2580 if (xpath->buf)
2581 xmlBufferFree(xpath->buf);
2583 if (xpath->result)
2584 xmlXPathFreeObject(xpath->result);
2586 if (xpath->xp)
2587 xmlXPathFreeContext(xpath->xp);
2590 static int xpath_command(assuan_context_t ctx, gchar *line)
2592 struct client_s *client = assuan_get_pointer(ctx);
2593 gpg_error_t rc;
2594 struct xpath_s xpath;
2596 if (disable_list_and_dump == TRUE)
2597 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2599 rc = file_modified(client);
2601 if (rc)
2602 return send_error(ctx, rc);
2604 if (!line || !*line)
2605 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2607 memset(&xpath, 0, sizeof(struct xpath_s));
2609 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2610 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2611 return send_syserror(ctx, ENOMEM);
2614 xpath.xp = xmlXPathNewContext(client->doc);
2616 if (!xpath.xp) {
2617 xpath_command_cleanup(&xpath);
2618 return send_error(ctx, EPWMD_LIBXML_ERROR);
2621 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2623 if (!xpath.result) {
2624 xpath_command_cleanup(&xpath);
2625 return send_error(ctx, EPWMD_LIBXML_ERROR);
2628 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2629 rc = EPWMD_EMPTY_ELEMENT;
2630 goto fail;
2633 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2634 (xmlChar *)xpath.req[1], &xpath.buf);
2636 if (rc)
2637 goto fail;
2638 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2639 rc = EPWMD_EMPTY_ELEMENT;
2640 goto fail;
2642 else if (xpath.req[1])
2643 goto fail;
2645 pth_cancel_point();
2646 rc = xfer_data(ctx, (gchar *)xmlBufferContent(xpath.buf),
2647 xmlBufferLength(xpath.buf));
2649 fail:
2650 xpath_command_cleanup(&xpath);
2651 return send_error(ctx, rc);
2654 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2655 gsize len)
2657 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2658 gpg_error_t rc = file_modified(client);
2659 gchar **req, **path = NULL, **path_orig = NULL, *content;
2660 xmlDocPtr doc;
2661 xmlNodePtr n, root, copy;
2663 if (assuan_rc || rc) {
2664 if (line)
2665 xfree(line);
2666 return assuan_rc ? assuan_rc : rc;
2669 req = split_input_line((gchar *)line, " ", 2);
2670 xfree(line);
2672 if (!req || !*req)
2673 return EPWMD_COMMAND_SYNTAX;
2675 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2676 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2677 return EPWMD_COMMAND_SYNTAX;
2680 content = req[1];
2682 if (!content || !*content) {
2683 rc = EPWMD_COMMAND_SYNTAX;
2684 goto fail;
2687 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2688 rc = EPWMD_INVALID_ELEMENT;
2689 goto fail;
2692 if (valid_element_path(path+1, FALSE) == FALSE) {
2693 rc = EPWMD_INVALID_ELEMENT;
2694 goto fail;
2697 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2699 if (!doc) {
2700 rc = EPWMD_LIBXML_ERROR;
2701 goto fail;
2704 root = xmlDocGetRootElement(doc);
2705 path_orig = g_strdupv(path);
2707 if (!path_orig) {
2708 xmlFreeDoc(doc);
2709 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2710 rc = gpg_error_from_errno(ENOMEM);
2711 goto fail;
2714 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2715 g_strfreev(path_orig);
2716 xmlFreeDoc(doc);
2717 rc = gpg_error_from_errno(ENOMEM);
2718 goto fail;
2721 n = find_account(client->doc, &path, &rc, NULL, 0);
2723 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2724 g_strfreev(path_orig);
2725 xmlFreeDoc(doc);
2726 goto fail;
2728 else if (!rc) {
2729 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2731 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2732 g_strfreev(path_orig);
2733 xmlFreeDoc(doc);
2734 goto fail;
2736 else if (!rc) {
2737 xmlNodePtr parent = n->parent;
2739 xmlUnlinkNode(n);
2740 xmlFreeNode(n);
2741 n = parent;
2745 g_strfreev(path);
2746 path = path_orig;
2748 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2749 n = create_element_path(client, &path, &rc);
2751 if (rc) {
2752 xmlFreeDoc(doc);
2753 goto fail;
2757 copy = xmlCopyNode(root, 1);
2758 n = xmlAddChild(n, copy);
2759 xmlFreeDoc(doc);
2761 if (!n)
2762 rc = EPWMD_LIBXML_ERROR;
2764 fail:
2765 g_strfreev(path);
2766 g_strfreev(req);
2767 client->inquire_status = INQUIRE_DONE;
2768 return rc;
2771 static int import_command(assuan_context_t ctx, gchar *line)
2773 gpg_error_t rc;
2774 struct client_s *client = assuan_get_pointer(ctx);
2776 rc = file_modified(client);
2778 if (rc)
2779 return send_error(ctx, rc);
2781 pth_cancel_point();
2782 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2783 pth_cancel_point();
2785 if (rc)
2786 return send_error(ctx, rc);
2788 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2789 client->inquire_status = INQUIRE_BUSY;
2790 return 0;
2793 static int lock_command(assuan_context_t ctx, gchar *line)
2795 gpg_error_t rc;
2796 struct client_s *client = assuan_get_pointer(ctx);
2798 rc = file_modified(client);
2800 if (rc)
2801 return send_error(ctx, rc);
2803 rc = lock_file_mutex(client);
2805 if (!rc)
2806 client->is_lock_cmd = TRUE;
2808 return send_error(ctx, rc);
2811 static int unlock_command(assuan_context_t ctx, gchar *line)
2813 struct client_s *client = assuan_get_pointer(ctx);
2814 gpg_error_t rc = file_modified(client);
2816 if (rc)
2817 return send_error(ctx, rc);
2819 unlock_file_mutex(client);
2820 return send_error(ctx, 0);
2823 static int getpid_command(assuan_context_t ctx, gchar *line)
2825 gpg_error_t rc;
2826 gchar buf[32];
2827 pid_t pid = getpid();
2829 print_fmt(buf, sizeof(buf), "%i", pid);
2830 pth_cancel_point();
2831 rc = xfer_data(ctx, buf, strlen(buf));
2832 pth_cancel_point();
2833 return send_error(ctx, rc);
2836 static int version_command(assuan_context_t ctx, gchar *line)
2838 gpg_error_t rc;
2839 gchar buf[32];
2841 print_fmt(buf, sizeof(buf), "%s", PACKAGE_VERSION);
2842 pth_cancel_point();
2843 rc = xfer_data(ctx, buf, strlen(buf));
2844 pth_cancel_point();
2845 return send_error(ctx, rc);
2848 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *name,
2849 const gchar *value)
2851 struct client_s *cl = assuan_get_pointer(ctx);
2853 if (g_ascii_strcasecmp(name, (gchar *)"NAME") == 0) {
2854 pth_attr_t attr = pth_attr_of(pth_self());
2855 gchar buf[41];
2857 if (!value) {
2858 pth_attr_destroy(attr);
2859 goto done;
2862 print_fmt(buf, sizeof(buf), "%s", value);
2863 pth_attr_set(attr, PTH_ATTR_NAME, buf);
2864 pth_attr_destroy(attr);
2865 #ifdef WITH_PINENTRY
2866 if (cl->pinentry->name)
2867 g_free(cl->pinentry->name);
2869 cl->pinentry->name = g_strdup(buf);
2871 if (!cl->pinentry->name)
2872 return send_error(ctx, gpg_error_from_errno(ENOMEM));
2873 #endif
2875 else
2876 return send_error(ctx,
2877 gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION));
2879 done:
2880 log_write("%s CLIENT %s%s%s", value ? "SET" : "UNSET", name,
2881 value ? "=" : "", value ? value : "");
2882 return send_error(ctx, 0);
2885 static void set_option_value(gchar **opt, const gchar *value)
2887 if (opt)
2888 g_free(*opt);
2890 *opt = NULL;
2892 if (value)
2893 *opt = g_strdup(value);
2896 static int set_unset_common(assuan_context_t ctx, const gchar *name,
2897 const gchar *value)
2899 struct client_s *client = assuan_get_pointer(ctx);
2901 if (g_ascii_strcasecmp(name, (gchar *)"iterations") == 0) {
2902 long n;
2903 gchar *p = NULL;
2905 if (!client->filename)
2906 return send_error(ctx, EPWMD_NO_FILE);
2908 if (!value) {
2909 g_key_file_set_integer(keyfileh, client->filename, "iterations",
2910 get_key_file_integer("global", "iterations"));
2911 goto done;
2914 errno = 0;
2915 n = strtol(value, &p, 10);
2917 if (errno || (p && *p) || n < 0)
2918 return send_error(ctx,
2919 gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE));
2921 MUTEX_LOCK(&rcfile_mutex);
2922 g_key_file_set_integer(keyfileh,
2923 client->filename ? client->filename : "global", "iterations",
2924 (guint)n);
2925 MUTEX_UNLOCK(&rcfile_mutex);
2927 if (client->filename)
2928 client->opts |= OPT_ITERATIONS;
2930 #ifdef WITH_PINENTRY
2931 else if (g_ascii_strcasecmp(name, (gchar *)"lc_messages") == 0)
2932 set_option_value(&client->pinentry->lcmessages, value);
2933 else if (g_ascii_strcasecmp(name, (gchar *)"lc_ctype") == 0)
2934 set_option_value(&client->pinentry->lcctype, value);
2935 else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0)
2936 set_option_value(&client->pinentry->ttyname, value);
2937 else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0)
2938 set_option_value(&client->pinentry->ttytype, value);
2939 else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0)
2940 set_option_value(&client->pinentry->display, value);
2941 else if (g_ascii_strcasecmp(name, (gchar *)"path") == 0)
2942 set_option_value(&client->pinentry->path, value);
2943 else if (g_ascii_strcasecmp(name, (gchar *)"title") == 0)
2944 set_option_value(&client->pinentry->title, value);
2945 else if (g_ascii_strcasecmp(name, (gchar *)"prompt") == 0)
2946 set_option_value(&client->pinentry->prompt, value);
2947 else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0)
2948 set_option_value(&client->pinentry->desc, value);
2949 else if (g_ascii_strcasecmp(name, (gchar *)"timeout") == 0) {
2950 gchar *p = NULL;
2951 gint n;
2953 if (!value) {
2954 client->pinentry->timeout =
2955 get_key_file_integer("global", "pinentry_timeout");
2956 goto done;
2959 n = strtol(value, &p, 10);
2961 if (*p || n < 0)
2962 return send_error(ctx,
2963 gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE));
2965 client->pinentry->timeout = n;
2967 else if (g_ascii_strcasecmp(name, (gchar *)"pinentry") == 0) {
2968 gchar *p = NULL;
2969 gint n;
2971 if (!value) {
2972 client->pinentry->enable = get_key_file_boolean("global",
2973 "enable_pinentry");
2974 goto done;
2977 n = strtol(value, &p, 10);
2979 if (*p || n < 0 || n > 1)
2980 return send_error(ctx,
2981 gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE));
2983 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2985 #endif
2986 else
2987 return send_error(ctx,
2988 gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION));
2990 done:
2991 log_write("%s %s%s%s", value ? "SET" : "UNSET", name,
2992 value ? "=" : "", value ? value : "");
2993 return send_error(ctx, 0);
2996 static int unset_command(assuan_context_t ctx, gchar *line)
2998 gchar *p = line;
3000 while (g_ascii_isspace(*p))
3001 p++;
3003 if (!g_ascii_strncasecmp(p, "CLIENT", 6)) {
3004 p += 6;
3006 while (g_ascii_isspace(*p))
3007 p++;
3009 return parse_client_option(ctx, p, NULL);
3012 return set_unset_common(ctx, p, NULL);
3015 static int set_command(assuan_context_t ctx, gchar *line)
3017 gchar name[64] = {0}, value[256] = {0};
3018 gchar *p = line;
3019 gboolean c = 0;
3021 while (g_ascii_isspace(*p))
3022 p++;
3024 if (!g_ascii_strncasecmp(p, "CLIENT", 6)) {
3025 p += 6;
3026 c = TRUE;
3029 if (sscanf(p, " %63[a-zA-Z] = %255c", name, value) != 2)
3030 return send_error(ctx, gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX));
3032 if (c)
3033 return parse_client_option(ctx, name, value);
3035 return set_unset_common(ctx, name, value);
3038 static void bye_notify(assuan_context_t ctx)
3040 struct client_s *cl = assuan_get_pointer(ctx);
3042 /* This will let assuan_process_next() return. */
3043 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
3046 static void reset_notify(assuan_context_t ctx)
3048 struct client_s *cl = assuan_get_pointer(ctx);
3050 if (cl)
3051 cleanup_client(cl);
3054 gpg_error_t register_commands(assuan_context_t ctx)
3056 static struct {
3057 const gchar *name;
3058 gint (*handler)(assuan_context_t, gchar *line);
3059 } table[] = {
3060 { "OPEN", open_command },
3061 { "SAVE", save_command },
3062 { "LIST", list_command },
3063 { "REALPATH", realpath_command },
3064 { "STORE", store_command },
3065 { "DELETE", delete_command },
3066 { "GET", get_command },
3067 { "ATTR", attr_command },
3068 { "ISCACHED", iscached_command },
3069 { "CLEARCACHE", clearcache_command },
3070 { "CACHETIMEOUT", cachetimeout_command },
3071 { "GETCONFIG", getconfig_command },
3072 { "DUMP", dump_command },
3073 { "XPATH", xpath_command },
3074 { "IMPORT", import_command },
3075 { "LOCK", lock_command },
3076 { "UNLOCK", unlock_command },
3077 { "GETPID", getpid_command },
3078 { "VERSION", version_command },
3079 { "SET", set_command },
3080 { "UNSET", unset_command },
3081 { "INPUT", NULL },
3082 { "OUTPUT", NULL },
3083 { NULL, NULL }
3085 gint i, rc;
3087 for (i=0; table[i].name; i++) {
3088 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
3090 if (rc)
3091 return rc;
3094 rc = assuan_register_bye_notify(ctx, bye_notify);
3096 if (rc)
3097 return rc;
3099 rc = assuan_register_reset_notify(ctx, reset_notify);
3101 if (rc)
3102 return rc;
3104 return assuan_register_post_cmd_notify(ctx, command_finalize);
3107 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
3108 struct client_crypto_s *crypto, gpointer *dst, goffset *dst_len)
3110 goffset insize, len;
3111 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
3112 guint64 iter = 0, n_iter = 0, iter_progress = 0;
3113 gint zrc = 0;
3114 gulong outsize = 0;
3115 gpg_error_t rc;
3116 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->fh1) : sizeof(crypto->fh->fh2);
3117 guint64 fh_iter = crypto->fh->v1 ? crypto->fh->fh1.iter : crypto->fh->fh2.iter;
3119 lseek(crypto->fh->fd, fh_size, SEEK_SET);
3120 insize = crypto->fh->st.st_size - fh_size;
3121 crypto->iv = gcry_malloc(gcryblocksize);
3123 if (!crypto->iv) {
3124 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3125 return gpg_error_from_errno(ENOMEM);
3128 /* No encryption iterations. This is a plain (gzipped) file. */
3129 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0)) {
3131 * cache_file_count() needs both .used == TRUE and a valid key in
3132 * order for it to count as a used cache entry. Fixes CACHE status
3133 * messages.
3135 memset(key, '!', gcrykeysize);
3138 if (crypto->fh->v1)
3139 memcpy(crypto->iv, crypto->fh->fh1.iv, gcryblocksize);
3140 else
3141 memcpy(crypto->iv, crypto->fh->fh2.iv, gcryblocksize);
3143 crypto->inbuf = gcry_malloc(insize);
3145 if (!crypto->inbuf) {
3146 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3147 return gpg_error_from_errno(ENOMEM);
3150 crypto->insize = insize;
3151 len = pth_read(crypto->fh->fd, crypto->inbuf, crypto->insize);
3152 pth_cancel_point();
3154 if (len != crypto->insize)
3155 return GPG_ERR_INV_LENGTH;
3157 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0))
3158 goto decompress;
3160 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3161 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3162 return rc;
3165 if ((rc = gcry_cipher_setkey(crypto->gh, key, gcrykeysize))) {
3166 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3167 return rc;
3170 iter_progress = (guint64)get_key_file_integer(client && client->filename ?
3171 client->filename : "global", "iteration_progress");
3173 if (iter_progress > 0 && fh_iter >= iter_progress) {
3174 rc = send_status(ctx, STATUS_DECRYPT, "0 %llu", fh_iter);
3175 pth_cancel_point();
3177 if (rc)
3178 return rc;
3181 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3183 if (rc)
3184 return rc;
3186 crypto->tkey = gcry_malloc(gcrykeysize);
3188 if (!crypto->tkey) {
3189 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3190 return gpg_error_from_errno(ENOMEM);
3193 memcpy(crypto->tkey, key, gcrykeysize);
3194 guchar *tkey = crypto->tkey;
3195 tkey[0] ^= 1;
3197 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
3198 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3199 return rc;
3202 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3203 if (iter_progress > 0 && iter >= iter_progress) {
3204 if (!(iter % iter_progress)) {
3205 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu",
3206 ++n_iter * iter_progress, fh_iter);
3207 pth_cancel_point();
3209 if (rc)
3210 return rc;
3214 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3215 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3216 return rc;
3219 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3221 if (rc) {
3222 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3223 return rc;
3226 iter++;
3229 if (iter_progress && fh_iter >= iter_progress) {
3230 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu", fh_iter, fh_iter);
3231 pth_cancel_point();
3233 if (rc)
3234 return rc;
3237 decompress:
3238 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3239 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3240 if (zrc == Z_MEM_ERROR)
3241 return gpg_error_from_errno(ENOMEM);
3242 else
3243 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3246 if (g_strncasecmp(crypto->outbuf, "<?xml version=", 14) != 0) {
3247 gcry_free(crypto->outbuf);
3248 crypto->outbuf = NULL;
3249 return EPWMD_BADKEY;
3252 if (ctx) {
3253 client->xml = crypto->outbuf;
3254 client->len = outsize;
3255 crypto->outbuf = NULL;
3257 else if (dst) {
3258 *dst = crypto->outbuf;
3259 *dst_len = outsize;
3260 crypto->outbuf = NULL;
3263 /* The calling function should free the crypto struct. */
3264 return 0;
3268 * This is called after every Assuan command.
3270 void command_finalize(assuan_context_t ctx, gint rc)
3272 struct client_s *client = assuan_get_pointer(ctx);
3274 if (!client->is_lock_cmd)
3275 unlock_file_mutex(client);