Fixed creating an empty account with an invalid name.
[pwmd.git] / src / commands.c
blobc3665845f2734a802379db606511f5416ec31bda
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2007 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 02111-1307 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 <glib/gprintf.h>
30 #include <gcrypt.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifdef HAVE_ZLIB_H
37 #include <zlib.h>
38 #endif
40 #ifndef MEM_DEBUG
41 #include "mem.h"
42 #endif
44 #include "xml.h"
45 #include "common.h"
46 #include "pwmd_error.h"
47 #include "cache.h"
48 #include "commands.h"
50 static void *z_alloc(void *data, unsigned items, unsigned size)
52 #ifndef MEM_DEBUG
53 return gcry_calloc(items, size);
54 #else
55 return calloc(items, size);
56 #endif
59 static void z_free(void *data, void *p)
61 #ifndef MEM_DEBUG
62 gcry_free(p);
63 #else
64 free(p);
65 #endif
68 static gpg_error_t file_modified(struct client_s *client)
70 struct stat st;
72 if (client->state != STATE_OPEN)
73 return EPWMD_NO_FILE;
75 if (stat(client->filename, &st) == 0 && client->mtime) {
76 if (client->mtime != st.st_mtime)
77 return EPWMD_FILE_MODIFIED;
80 return 0;
83 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
84 void *inbuf, gsize insize)
86 gpg_error_t rc;
88 if ((rc = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
89 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
90 return FALSE;
93 return TRUE;
96 gpg_error_t decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
97 void *inbuf, gsize insize)
99 gpg_error_t rc;
101 if ((rc = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize)))
102 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
104 return rc;
107 static gpg_error_t parse_xml(assuan_context_t ctx)
109 struct client_s *client = assuan_get_pointer(ctx);
111 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
113 if (!client->doc)
114 return EPWMD_LIBXML_ERROR;
116 return 0;
119 gboolean valid_filename(const gchar *filename)
121 const gchar *p;
123 if (!filename || !*filename)
124 return FALSE;
126 for (p = filename; *p; p++) {
127 if (g_ascii_isalnum(*p) == FALSE && *p != '-' && *p != '_' && *p != '.')
128 return FALSE;
131 return TRUE;
134 gint open_file(const gchar *filename, struct stat *st)
136 gint fd;
138 if ((fd = open(filename, O_RDONLY)) == -1)
139 return -1;
141 if (stat(filename, st) == -1) {
142 close(fd);
143 return -1;
146 return fd;
149 void unlock_file_mutex(struct client_s *client)
151 pth_mutex_t *m;
153 if (client->has_lock == FALSE)
154 return;
156 CACHE_LOCK(client->ctx);
158 if (cache_get_mutex(client->md5file, &m) == FALSE) {
159 CACHE_UNLOCK;
160 return;
163 CACHE_UNLOCK;
164 pth_mutex_release(m);
165 client->has_lock = FALSE;
168 gpg_error_t lock_file_mutex(struct client_s *client)
170 pth_mutex_t *m;
172 if (client->has_lock == TRUE)
173 return 0;
175 CACHE_LOCK(client->ctx);
177 if (cache_get_mutex(client->md5file, &m) == FALSE) {
178 CACHE_UNLOCK;
179 return 0;
182 CACHE_UNLOCK;
184 if (pth_mutex_acquire(m, TRUE, NULL) == FALSE) {
185 if (errno == EBUSY) {
186 if (client->ctx)
187 assuan_write_status(client->ctx, "LOCKED", N_("Waiting for lock"));
189 pth_mutex_acquire(m, FALSE, NULL);
191 else {
192 gint e = errno;
193 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(errno));
194 return gpg_error_from_errno(e);
198 client->has_lock = TRUE;
199 return 0;
202 static void cleanup_client(struct client_s *client)
204 assuan_context_t ctx = client->ctx;
206 unlock_file_mutex(client);
207 CACHE_LOCK(client->ctx);
208 cache_decr_refcount(client->md5file);
211 * This may be a new file so don't use a cache slot. save_command() will
212 * set this to FALSE on success.
214 if (client->new == TRUE)
215 cache_clear(client->md5file, 1);
217 if (client->doc)
218 xmlFreeDoc(client->doc);
220 if (client->xml)
221 gcry_free(client->xml);
223 if (client->filename)
224 g_free(client->filename);
226 if (client->gh)
227 gcry_cipher_close(client->gh);
229 memset(client, 0, sizeof(struct client_s));
230 client->state = STATE_CONNECTED;
231 client->ctx = ctx;
232 client->freed = TRUE;
233 CACHE_UNLOCK;
236 static gchar *print_fmt(const char *fmt, ...)
238 va_list ap;
239 static gchar buf[ASSUAN_LINELENGTH] = {0};
241 va_start(ap, fmt);
242 vsnprintf(buf, sizeof(buf), fmt, ap);
243 va_end(ap);
244 return buf;
247 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
248 gpointer *out, glong *outsize, gint *error)
250 z_stream z;
251 gint ret;
252 gpointer pout;
253 gz_header h;
254 gchar buf[17];
255 gpg_error_t rc;
257 z.zalloc = z_alloc;
258 z.zfree = z_free;
259 z.next_in = in;
260 z.avail_in = insize;
261 z.avail_out = zlib_bufsize;
262 z.next_out = pout = g_malloc(zlib_bufsize);
264 if (!pout) {
265 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
266 *error = Z_MEM_ERROR;
267 return FALSE;
270 ret = inflateInit2(&z, 47);
272 if (ret != Z_OK) {
273 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
274 g_free(pout);
275 return FALSE;
278 memset(&h, 0, sizeof(gz_header));
279 h.comment = (guchar *)buf;
280 h.comm_max = sizeof(buf);
281 ret = inflateGetHeader(&z, &h);
283 if (ret != Z_OK) {
284 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
285 g_free(pout);
286 inflateEnd(&z);
287 return FALSE;
290 ret = inflate(&z, Z_BLOCK);
292 if (ret != Z_OK) {
293 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
294 g_free(pout);
295 inflateEnd(&z);
296 return FALSE;
299 if (h.comment)
300 insize = atoi((gchar *)h.comment);
302 do {
303 gpointer p;
305 ret = inflate(&z, Z_FINISH);
307 switch (ret) {
308 case Z_OK:
309 break;
310 case Z_BUF_ERROR:
311 if (!z.avail_out) {
312 p = g_realloc(pout, z.total_out + zlib_bufsize);
314 if (!p) {
315 ret = Z_MEM_ERROR;
316 goto fail;
319 pout = p;
320 z.next_out = pout + z.total_out;
321 z.avail_out = zlib_bufsize;
323 if (ctx) {
324 rc = assuan_write_status(ctx, "DECOMPRESS",
325 print_fmt("%i %i", z.total_out, insize));
327 if (rc) {
328 ret = rc;
329 goto fail;
333 break;
334 case Z_STREAM_END:
335 break;
336 default:
337 goto fail;
338 break;
341 pth_yield(NULL);
342 } while (ret != Z_STREAM_END);
344 if (ctx) {
345 rc = assuan_write_status(ctx, "DECOMPRESS",
346 print_fmt("%i %i", z.total_out, insize));
348 if (rc) {
349 ret = rc;
350 goto fail;
354 *out = pout;
355 *outsize = z.total_out;
356 inflateEnd(&z);
357 return TRUE;
359 fail:
360 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
361 *error = ret;
362 g_free(pout);
363 inflateEnd(&z);
364 return FALSE;
367 static int open_command(assuan_context_t ctx, char *line)
369 gint fd;
370 struct stat st;
371 guchar shakey[gcrykeysize];
372 gint cached = 0;
373 gint timeout;
374 gpg_error_t error;
375 struct client_s *client = assuan_get_pointer(ctx);
376 gchar **req;
377 gchar *filename = NULL;
379 if ((req = split_input_line(line, " ", 2)) != NULL)
380 filename = req[0];
382 if (!filename || !*filename) {
383 g_strfreev(req);
384 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
387 if (valid_filename(filename) == FALSE) {
388 g_strfreev(req);
389 return send_error(ctx, EPWMD_INVALID_FILENAME);
392 if (client->state == STATE_OPEN)
393 cleanup_client(client);
395 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
396 CACHE_LOCK(client->ctx);
398 if (cache_has_file(client->md5file) == FALSE) {
399 if (cache_add_file(client->md5file, NULL) == FALSE) {
400 g_strfreev(req);
401 CACHE_UNLOCK;
402 return send_error(ctx, EPWMD_MAX_SLOTS);
406 cache_incr_refcount(client->md5file);
407 CACHE_UNLOCK;
408 error = lock_file_mutex(client);
410 if (error) {
411 g_strfreev(req);
412 return send_error(ctx, error);
415 client->freed = FALSE;
417 if ((error = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
418 g_strfreev(req);
419 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
420 cleanup_client(client);
421 return send_error(ctx, error);
424 if (stat(filename, &st) == 0) {
425 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
426 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
427 g_strfreev(req);
428 cleanup_client(client);
429 return send_error(ctx, EPWMD_INVALID_FILENAME);
432 client->mtime = st.st_mtime;
436 * New files don't need a key.
438 if (access(filename, R_OK|W_OK) != 0) {
439 if (errno != ENOENT) {
440 error = errno;
441 log_write("%s: %s", filename, strerror(errno));
442 g_strfreev(req);
443 cleanup_client(client);
444 return send_syserror(ctx, error);
446 new_doc:
447 if ((client->xml = new_document()) == NULL) {
448 log_write("%s", strerror(ENOMEM));
449 g_strfreev(req);
450 cleanup_client(client);
451 return send_syserror(ctx, ENOMEM);
454 client->len = xmlStrlen(client->xml);
455 client->new = TRUE;
456 client->filename = g_strdup(filename);
458 if (!client->filename) {
459 g_strfreev(req);
460 cleanup_client(client);
461 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
462 return send_syserror(ctx, ENOMEM);
465 if (req[1] && *req[1]) {
466 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
467 memset(req[1], 0, strlen(req[1]));
468 goto update_cache;
471 goto done;
474 if ((fd = open_file(filename, &st)) == -1) {
475 error = errno;
476 log_write("%s: %s", filename, strerror(errno));
477 g_strfreev(req);
478 cleanup_client(client);
479 return send_syserror(ctx, error);
482 if (st.st_size == 0)
483 goto new_doc;
485 CACHE_LOCK(client->ctx);
487 if (cache_get_key(client->md5file, shakey) == TRUE)
488 cached = 1;
489 else {
491 * No key specified and no matching filename found in the cache.
493 if (!req[1] || !*req[1]) {
494 close(fd);
495 g_strfreev(req);
496 cleanup_client(client);
497 CACHE_UNLOCK;
498 return send_error(ctx, EPWMD_KEY);
502 CACHE_UNLOCK;
504 if (!cached) {
505 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
506 memset(req[1], 0, strlen(req[1]));
509 error = try_xml_decrypt(ctx, fd, st, shakey);
511 if (error) {
512 close(fd);
513 memset(shakey, 0, sizeof(shakey));
514 g_strfreev(req);
515 cleanup_client(client);
516 return send_error(ctx, error);
519 close(fd);
520 client->filename = g_strdup(filename);
522 if (!client->filename) {
523 memset(shakey, 0, sizeof(shakey));
524 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
525 g_strfreev(req);
526 cleanup_client(client);
527 return send_syserror(ctx, ENOMEM);
530 update_cache:
531 CACHE_LOCK(client->ctx);
533 if (!cached) {
534 if (cache_update_key(client->md5file, shakey) == FALSE) {
535 memset(shakey, 0, sizeof(shakey));
536 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
537 g_strfreev(req);
538 cleanup_client(client);
539 CACHE_UNLOCK;
540 return send_error(ctx, EPWMD_MAX_SLOTS);
543 timeout = get_key_file_integer(client->filename, "cache_timeout");
544 cache_reset_timeout(client->md5file, timeout);
546 else
547 cache_set_timeout(client->md5file, -2);
549 CACHE_UNLOCK;
550 memset(shakey, 0, sizeof(shakey));
552 done:
553 g_strfreev(req);
554 error = parse_xml(ctx);
556 if (client->xml) {
557 gcry_free(client->xml);
558 client->xml = NULL;
561 if (error == 0) {
562 if (client->new == FALSE)
563 send_cache_status_all();
565 client->state = STATE_OPEN;
568 return send_error(ctx, error);
571 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
572 gint size, gpointer *out, glong *outsize, gint *error)
574 z_stream z;
575 gpointer pout, pin;
576 gint ret;
577 gz_header h;
578 gchar buf[17];
579 gint cmd = Z_NO_FLUSH;
580 gpg_error_t rc;
582 z.zalloc = z_alloc;
583 z.zfree = z_free;
584 z.next_in = pin = data;
585 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
586 z.avail_out = zlib_bufsize;
587 z.next_out = pout = g_malloc(zlib_bufsize);
589 if (!pout) {
590 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
591 *error = Z_MEM_ERROR;
592 return FALSE;
595 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
597 if (ret != Z_OK) {
598 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
599 *error = ret;
600 g_free(pout);
601 return FALSE;
604 memset(&h, 0, sizeof(gz_header));
605 snprintf(buf, sizeof(buf), "%i", size);
606 h.comment = (guchar *)buf;
607 ret = deflateSetHeader(&z, &h);
609 if (ret != Z_OK) {
610 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
611 *error = ret;
612 g_free(pout);
613 deflateEnd(&z);
614 return FALSE;
617 do {
618 gpointer p;
620 ret = deflate(&z, cmd);
622 switch (ret) {
623 case Z_OK:
624 break;
625 case Z_BUF_ERROR:
626 if (!z.avail_out) {
627 p = g_realloc(pout, z.total_out + zlib_bufsize);
629 if (!p) {
630 ret = Z_MEM_ERROR;
631 goto fail;
634 pout = p;
635 z.next_out = pout + z.total_out;
636 z.avail_out = zlib_bufsize;
639 if (!z.avail_in && z.total_in < size) {
640 if (z.total_in + zlib_bufsize > size)
641 z.avail_in = size - z.total_in;
642 else
643 z.avail_in = zlib_bufsize;
645 if (ctx) {
646 rc = assuan_write_status(ctx, "COMPRESS",
647 print_fmt("%i %i", z.total_in, size));
649 if (rc) {
650 ret = rc;
651 goto fail;
656 if (z.total_in >= size)
657 cmd = Z_FINISH;
659 break;
660 case Z_STREAM_END:
661 break;
662 default:
663 goto fail;
666 pth_yield(NULL);
667 } while (ret != Z_STREAM_END);
669 if (ctx) {
670 rc = assuan_write_status(ctx, "COMPRESS",
671 print_fmt("%i %i", z.total_in, size));
673 if (rc) {
674 ret = rc;
675 goto fail;
679 *out = pout;
680 *outsize = z.total_out;
681 deflateEnd(&z);
682 return TRUE;
684 fail:
685 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
686 *error = ret;
687 g_free(pout);
688 deflateEnd(&z);
689 return FALSE;
692 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
693 const gchar *filename, gpointer data, size_t insize,
694 const guchar *shakey, guint iter)
696 gsize len = insize;
697 gint fd;
698 gpointer inbuf;
699 guchar tkey[gcrykeysize];
700 gchar *p;
701 gint error;
702 gpg_error_t rc;
703 guint iter_progress = 0, n_iter = 0, xiter = 0;
704 gchar tmp[FILENAME_MAX];
705 struct file_header_s {
706 guint iter;
707 guchar iv[gcryblocksize];
708 } file_header;
710 if (insize / gcryblocksize) {
711 len = (insize / gcryblocksize) * gcryblocksize;
713 if (insize % gcryblocksize)
714 len += gcryblocksize;
718 * Resize the existing xml buffer to the block size required by gcrypt
719 * rather than duplicating it and wasting memory.
721 inbuf = gcry_realloc(data, len);
723 if (!inbuf)
724 return gpg_error_from_errno(ENOMEM);
726 insize = len;
727 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
728 memcpy(tkey, shakey, sizeof(tkey));
729 tkey[0] ^= 1;
731 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
732 gcry_free(inbuf);
733 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
734 return rc;
737 file_header.iter = iter;
739 if (client)
740 iter_progress = get_key_file_integer("default", "iteration_progress");
742 if (client && iter_progress && file_header.iter >= iter_progress) {
743 error = assuan_write_status(client->ctx, "ENCRYPT", "0");
745 if (error) {
746 gcry_free(inbuf);
747 return error;
751 while (xiter < file_header.iter) {
752 if (client && iter_progress > 0 && xiter >= iter_progress) {
753 if (!(xiter % iter_progress)) {
754 error = assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
755 ++n_iter * iter_progress));
757 if (error) {
758 gcry_free(inbuf);
759 return error;
764 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
765 sizeof(file_header.iv)))) {
766 gcry_free(inbuf);
767 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
768 return rc;
771 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
772 == FALSE) {
773 gcry_free(inbuf);
774 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
775 return rc;
778 xiter++;
779 pth_yield(NULL);
782 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
783 sizeof(file_header.iv)))) {
784 gcry_free(inbuf);
785 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
786 return rc;
789 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
790 gcry_free(inbuf);
791 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
792 return rc;
795 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
796 gcry_free(inbuf);
797 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
798 return rc;
801 if (client && iter_progress && file_header.iter >= iter_progress) {
802 error = assuan_write_status(client->ctx, "ENCRYPT",
803 print_fmt("%i", file_header.iter));
805 if (error) {
806 gcry_free(inbuf);
807 return error;
811 if (filename) {
812 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
814 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
815 error = errno;
816 gcry_free(inbuf);
817 p = strrchr(tmp, '/');
818 p++;
819 log_write("%s: %s", p, strerror(errno));
820 return gpg_error_from_errno(error);
823 else
825 * xml_import() from command line.
827 fd = STDOUT_FILENO;
829 len = pth_write(fd, &file_header, sizeof(struct file_header_s));
831 if (len != sizeof(file_header)) {
832 len = errno;
834 if (filename)
835 close(fd);
837 gcry_free(inbuf);
838 return gpg_error_from_errno(len);
841 len = pth_write(fd, inbuf, insize);
843 if (len != insize) {
844 len = errno;
846 if (filename)
847 close(fd);
849 gcry_free(inbuf);
850 return gpg_error_from_errno(len);
853 if (fsync(fd) == -1) {
854 len = errno;
855 close(fd);
856 gcry_free(inbuf);
857 return gpg_error_from_errno(len);
860 if (filename) {
861 struct stat st;
862 mode_t mode = 0;
864 close(fd);
866 if (stat(filename, &st) == 0)
867 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
869 if (rename(tmp, filename) == -1) {
870 len = errno;
871 gcry_free(inbuf);
872 return gpg_error_from_errno(len);
875 if (mode)
876 chmod(filename, mode);
879 gcry_free(inbuf);
880 return 0;
883 static int save_command(assuan_context_t ctx, char *line)
885 gpointer xmlbuf;
886 xmlChar *p;
887 gint len;
888 gint cached = 0;
889 guchar shakey[gcrykeysize];
890 gint iter;
891 struct stat st;
892 struct client_s *client = assuan_get_pointer(ctx);
893 gpg_error_t error;
894 gint timeout;
895 gpointer outbuf;
896 glong outsize = 0;
897 gint zerror;
899 error = file_modified(client);
901 if (error) {
902 log_write("%s: %s", client->filename ? client->filename : "",
903 pwmd_strerror(error));
904 return send_error(ctx, error);
907 error = lock_file_mutex(client);
909 if (error)
910 return send_error(ctx, error);
912 if (stat(client->filename, &st) == -1 && errno != ENOENT)
913 return send_syserror(ctx, errno);
915 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
916 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
917 return send_error(ctx, EPWMD_INVALID_FILENAME);
920 if (!line || !*line) {
921 CACHE_LOCK(ctx);
923 if (cache_get_key(client->md5file, shakey) == FALSE) {
924 CACHE_UNLOCK;
925 return send_error(ctx, EPWMD_KEY);
928 CACHE_UNLOCK;
929 cached = 1;
931 else {
932 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
933 memset(line, 0, strlen(line));
936 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
937 xmlbuf = p;
939 iter = get_key_file_integer(client->filename, "compression_level");
941 if (iter < 0)
942 iter = 0;
944 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
945 memset(shakey, 0, sizeof(shakey));
946 xmlFree(xmlbuf);
948 if (zerror == Z_MEM_ERROR) {
949 return send_syserror(ctx, ENOMEM);
951 else
952 return send_error(ctx, GPG_ERR_COMPR_ALGO);
954 else {
955 gcry_free(xmlbuf);
956 xmlbuf = outbuf;
957 len = outsize;
960 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
961 iter = 0;
963 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
965 if (error) {
966 memset(shakey, 0, sizeof(shakey));
967 return send_error(ctx, error);
970 stat(client->filename, &st);
971 client->mtime = st.st_mtime;
972 timeout = get_key_file_integer(client->filename, "cache_timeout");
973 CACHE_LOCK(client->ctx);
975 if (cached) {
976 memset(shakey, 0, sizeof(shakey));
977 cache_reset_timeout(client->md5file, timeout);
978 CACHE_UNLOCK;
980 if (client->new == TRUE)
981 send_cache_status_all();
983 client->new = FALSE;
984 return send_error(ctx, 0);
987 if (cache_update_key(client->md5file, shakey) == FALSE) {
988 memset(shakey, 0, sizeof(shakey));
989 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
990 CACHE_UNLOCK;
991 return send_error(ctx, EPWMD_MAX_SLOTS);
994 client->new = FALSE;
995 memset(shakey, 0, sizeof(shakey));
996 cache_reset_timeout(client->md5file, timeout);
997 CACHE_UNLOCK;
998 send_cache_status_all();
999 return send_error(ctx, 0);
1002 static gboolean contains_whitespace(const gchar *str)
1004 const gchar *p = str;
1005 gunichar c;
1006 glong len;
1008 len = g_utf8_strlen(p++, -1) -1;
1010 while (len--) {
1011 c = g_utf8_get_char(p++);
1013 if (g_unichar_isspace(c))
1014 return TRUE;
1017 return FALSE;
1020 static int delete_command(assuan_context_t ctx, char *line)
1022 struct client_s *client = assuan_get_pointer(ctx);
1023 gchar **req;
1024 gpg_error_t error;
1025 xmlNodePtr n;
1027 error = file_modified(client);
1029 if (error) {
1030 log_write("%s: %s", client->filename ? client->filename : "",
1031 pwmd_strerror(error));
1032 return send_error(ctx, error);
1035 if (strchr(line, '\t'))
1036 req = split_input_line(line, "\t", -1);
1037 else
1038 req = split_input_line(line, " ", -1);
1040 if (!req || !*req)
1041 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1043 n = find_account(client->doc, &req, &error, NULL, 0);
1045 if (!n) {
1046 g_strfreev(req);
1047 return send_error(ctx, error);
1051 * No sub-node defined. Remove the entire node (account).
1053 if (!req[1]) {
1054 if (n) {
1055 xmlUnlinkNode(n);
1056 xmlFreeNode(n);
1059 g_strfreev(req);
1060 return send_error(ctx, 0);
1063 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1064 g_strfreev(req);
1066 if (!n)
1067 return send_error(ctx, error);
1069 if (n) {
1070 xmlUnlinkNode(n);
1071 xmlFreeNode(n);
1074 return send_error(ctx, 0);
1078 * Don't return with assuan_process_done() here. This has been called from
1079 * assuan_process_next() and the command should be finished in
1080 * client_thread().
1082 static int store_command_finalize(gpointer data, gint rc, guchar *line,
1083 gsize len)
1085 assuan_context_t ctx = data;
1086 struct client_s *client = assuan_get_pointer(ctx);
1087 gchar **req;
1088 guchar *result = line;
1089 xmlNodePtr n;
1090 gpg_error_t error = file_modified(client);
1092 if (rc) {
1093 if (line)
1094 #ifndef MEM_DEBUG
1095 xfree(line);
1096 #else
1097 free(line);
1098 #endif
1099 return rc;
1102 req = split_input_line((gchar *)result, "\t", 0);
1103 #ifndef MEM_DEBUG
1104 xfree(line);
1105 #else
1106 free(line);
1107 #endif
1109 if (rc) {
1110 if (req)
1111 g_strfreev(req);
1113 return rc;
1116 if (!req || !*req)
1117 return EPWMD_COMMAND_SYNTAX;
1119 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1120 g_strfreev(req);
1121 return EPWMD_INVALID_ELEMENT;
1124 if (valid_element_path(req+1, TRUE) == FALSE) {
1125 g_strfreev(req);
1126 return EPWMD_INVALID_ELEMENT;
1129 again:
1130 n = find_account(client->doc, &req, &error, NULL, 0);
1132 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1133 error = new_account(client->doc, *req);
1135 if (error) {
1136 g_strfreev(req);
1137 return error;
1140 goto again;
1143 if (!n) {
1144 g_strfreev(req);
1145 return error;
1148 if (req[1]) {
1149 if (!n->children)
1150 create_elements_cb(n, req+1, &error, NULL);
1151 else
1152 find_elements(client->doc, n->children, req+1, &error,
1153 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1156 g_strfreev(req);
1157 client->inquire_status = INQUIRE_DONE;
1158 return error;
1161 static int store_command(assuan_context_t ctx, char *line)
1163 struct client_s *client = assuan_get_pointer(ctx);
1164 gpg_error_t error = file_modified(client);
1166 if (error) {
1167 log_write("%s: %s", client->filename ? client->filename : "",
1168 pwmd_strerror(error));
1169 return send_error(ctx, error);
1172 error = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1174 if (error)
1175 return send_error(ctx, error);
1177 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1178 client->inquire_status = INQUIRE_BUSY;
1179 return 0;
1182 static int get_command(assuan_context_t ctx, char *line)
1184 struct client_s *client = assuan_get_pointer(ctx);
1185 gchar **req;
1186 gpg_error_t error;
1187 xmlNodePtr n;
1189 error = file_modified(client);
1191 if (error) {
1192 log_write("%s: %s", client->filename ? client->filename : "",
1193 pwmd_strerror(error));
1194 return send_error(ctx, error);
1197 req = split_input_line(line, "\t", -1);
1199 if (!req || !*req) {
1200 g_strfreev(req);
1201 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1204 n = find_account(client->doc, &req, &error, NULL, 0);
1206 if (!n) {
1207 g_strfreev(req);
1208 return send_error(ctx, error);
1211 if (req[1])
1212 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1214 g_strfreev(req);
1216 if (error)
1217 return send_error(ctx, error);
1219 if (!n || !n->children)
1220 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1222 n = n->children;
1224 if (!n || !n->content || !*n->content)
1225 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1227 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1228 return send_error(ctx, error);
1231 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1233 xmlChar *p = path;
1234 gint n;
1235 gchar *buf;
1237 if (!p)
1238 return NULL;
1240 for (n = 0; *p && n < 3; p++) {
1241 if (*p == '/')
1242 n++;
1245 if (strstr((gchar *)p, "text()") != NULL)
1246 p[xmlStrlen(p) - 7] = 0;
1248 for (n = 0; p[n]; n++) {
1249 if (p[n] == '/')
1250 p[n] = '\t';
1253 buf = g_strdup_printf("%s\t%s", account, p);
1254 return buf;
1257 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1259 gchar **a;
1260 va_list ap;
1261 gchar *buf;
1262 gint len = *array ? g_strv_length(*array) : 0;
1263 gint ret;
1265 if (!fmt)
1266 return FALSE;
1268 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1269 return FALSE;
1271 va_start(ap, fmt);
1272 ret = g_vasprintf(&buf, fmt, ap);
1273 va_end(ap);
1275 if (ret == -1)
1276 return FALSE;
1278 a[len++] = buf;
1279 a[len] = NULL;
1280 *array = a;
1281 return TRUE;
1284 struct realpath_s {
1285 gchar *account;
1288 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1289 gpg_error_t *error, void *data)
1291 struct realpath_s *rp = data;
1293 if (rp->account)
1294 g_free(rp->account);
1296 rp->account = g_strdup(req[0]);
1298 if (!rp->account) {
1299 *error = gpg_error_from_errno(ENOMEM);
1300 return NULL;
1303 return node;
1306 static int realpath_command(assuan_context_t ctx, char *line)
1308 gpg_error_t error;
1309 struct client_s *client = assuan_get_pointer(ctx);
1310 xmlChar *p;
1311 gchar **req;
1312 gchar *result, *t;
1313 gint i;
1314 xmlNodePtr n;
1315 struct realpath_s *rp;
1316 GString *string;
1318 error = file_modified(client);
1320 if (error) {
1321 log_write("%s: %s", client->filename ? client->filename : "",
1322 pwmd_strerror(error));
1323 return send_error(ctx, error);
1326 if (strchr(line, '\t') != NULL) {
1327 if ((req = split_input_line(line, "\t", 0)) == NULL)
1328 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1330 else {
1331 if ((req = split_input_line(line, " ", 0)) == NULL)
1332 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1335 n = find_account(client->doc, &req, &error, NULL, 0);
1337 if (!n) {
1338 g_strfreev(req);
1339 return send_error(ctx, error);
1342 rp = g_malloc(sizeof(struct realpath_s));
1344 if (!rp) {
1345 g_strfreev(req);
1346 return send_syserror(ctx, ENOMEM);
1349 rp->account = g_strdup(req[0]);
1351 if (!rp->account) {
1352 g_strfreev(req);
1353 return send_syserror(ctx, ENOMEM);
1356 if (req[1]) {
1357 n = find_elements(client->doc, n->children, req+1, &error,
1358 NULL, realpath_elements_cb, NULL, FALSE, 0, rp);
1360 if (!n) {
1361 g_free(rp->account);
1362 g_free(rp);
1363 g_strfreev(req);
1364 return send_error(ctx, error);
1368 p = xmlGetNodePath(n);
1369 result = element_path_to_req(rp->account, p);
1371 if (!result) {
1372 g_free(result);
1373 g_free(rp->account);
1374 g_free(rp);
1375 g_strfreev(req);
1376 xmlFree(p);
1377 return send_syserror(ctx, ENOMEM);
1380 string = g_string_new(result);
1381 g_free(result);
1382 g_free(rp->account);
1383 g_free(rp);
1384 g_strfreev(req);
1385 xmlFree(p);
1386 i = 0;
1388 again:
1389 for (t = string->str + i; *t; t++, i++) {
1390 if (!i || *t == '\t') {
1391 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1392 goto again;
1396 error = assuan_send_data(ctx, string->str, string->len);
1397 g_string_free(string, TRUE);
1398 return send_error(ctx, error);
1401 struct list_element_s {
1402 GSList *list;
1403 gchar **elements;
1406 static gboolean append_to_element_list(struct list_element_s *elements)
1408 gchar *tmp;
1409 gint i, total;
1410 gchar *a;
1411 GSList *list;
1413 if (!elements || !elements->elements)
1414 return TRUE;
1416 tmp = g_strjoinv("\t", elements->elements);
1418 if (!tmp)
1419 return FALSE;
1421 g_strfreev(elements->elements);
1422 elements->elements = NULL;
1423 total = g_slist_length(elements->list);
1424 a = g_utf8_collate_key(tmp, -1);
1426 if (!a) {
1427 g_free(tmp);
1428 return FALSE;
1432 * Removes duplicate element paths from the list. This is needed when
1433 * appending an element tree from list_command(). The glib docs recommend
1434 * using g_utf8_collate_key() for a large number of strings.
1436 for (i = 0; i < total; i++) {
1437 gchar *p = g_slist_nth_data(elements->list, i);
1438 gchar *b = g_utf8_collate_key(p, -1);
1440 if (!b) {
1441 g_free(a);
1442 g_free(tmp);
1443 return FALSE;
1446 if (strcmp(a, b) == 0) {
1447 g_free(a);
1448 g_free(b);
1449 g_free(tmp);
1450 return TRUE;
1453 g_free(b);
1456 g_free(a);
1457 list = g_slist_append(elements->list, tmp);
1459 if (!list)
1460 return FALSE;
1462 elements->list = list;
1463 return TRUE;
1466 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1467 struct list_element_s *elements, gchar *prefix)
1469 xmlNodePtr n;
1470 gpg_error_t error;
1472 if (append_to_element_list(elements) == FALSE)
1473 return gpg_error_from_errno(ENOMEM);
1475 for (n = node; n; n = n->next) {
1476 if (n->type == XML_ELEMENT_NODE) {
1477 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1478 gchar *tmp;
1480 if (content) {
1481 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1482 return gpg_error_from_errno(ENOMEM);
1484 if (append_to_element_list(elements) == FALSE)
1485 return gpg_error_from_errno(ENOMEM);
1488 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1490 if (!tmp)
1491 return gpg_error_from_errno(ENOMEM);
1493 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1494 g_free(tmp);
1495 return gpg_error_from_errno(ENOMEM);
1498 if (n->children) {
1499 error = do_list_recurse(doc, n->children, elements, tmp);
1500 g_free(tmp);
1502 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1503 return error;
1505 else
1506 g_free(tmp);
1508 if (append_to_element_list(elements) == FALSE)
1509 return gpg_error_from_errno(ENOMEM);
1513 return 0;
1516 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1517 struct list_element_s *elements, char *line)
1519 gchar *prefix = NULL, *account;
1520 gchar **req = NULL, **oreq = NULL, *tmp;
1521 xmlNodePtr n;
1522 gboolean account_is_literal, account_has_target = FALSE;
1523 gint which = 0;
1524 gchar **p;
1525 gpg_error_t error;
1527 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1528 if ((req = split_input_line(line, " ", 0)) == NULL)
1529 return EPWMD_COMMAND_SYNTAX;
1532 prefix = g_strdup(*req);
1534 if (!prefix) {
1535 g_strfreev(req);
1536 return gpg_error_from_errno(ENOMEM);
1539 account = g_strdup(*req);
1541 if (!account) {
1542 g_free(prefix);
1543 g_strfreev(req);
1544 return gpg_error_from_errno(ENOMEM);
1547 oreq = g_strdupv(req);
1549 if (!oreq) {
1550 g_free(prefix);
1551 g_free(account);
1552 g_strfreev(req);
1553 return gpg_error_from_errno(ENOMEM);
1556 p = req;
1557 again:
1558 account_has_target = FALSE;
1559 account_is_literal = is_literal_element_str(prefix);
1560 n = find_account(doc, &p, &error, &account_has_target, 0);
1562 if (which)
1563 oreq = p;
1564 else
1565 req = p;
1567 if (!n)
1568 goto fail;
1570 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1571 tmp = g_strdup_printf("!%s", prefix);
1573 if (!tmp) {
1574 error = gpg_error_from_errno(ENOMEM);
1575 goto fail;
1578 g_free(prefix);
1579 prefix = tmp;
1582 if (*(p+1)) {
1583 gchar *t;
1585 n = find_elements(doc, n->children, p+1, &error, NULL, NULL, NULL,
1586 TRUE, 0, NULL);
1588 if (error)
1589 goto fail;
1591 tmp = g_strjoinv("\t", p+1);
1592 if (!tmp) {
1593 error = gpg_error_from_errno(ENOMEM);
1594 goto fail;
1597 t = g_strdup_printf("%s\t%s", prefix, tmp);
1598 if (!t) {
1599 error = gpg_error_from_errno(ENOMEM);
1600 goto fail;
1603 g_free(prefix);
1604 prefix = t;
1605 g_free(tmp);
1608 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1609 error = gpg_error_from_errno(ENOMEM);
1610 goto fail;
1613 if (node_has_child_element(n->children) == FALSE) {
1614 if (append_to_element_list(elements) == FALSE) {
1615 error = gpg_error_from_errno(ENOMEM);
1616 goto fail;
1619 else
1620 error = do_list_recurse(doc, n->children, elements, prefix);
1622 if (error)
1623 goto fail;
1625 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1626 g_free(*oreq);
1627 *oreq = g_strdup_printf("!%s", account);
1629 if (!*oreq) {
1630 error = gpg_error_from_errno(ENOMEM);
1631 goto fail;
1634 p = oreq;
1635 g_free(prefix);
1636 prefix = g_strdup(*oreq);
1638 if (!prefix) {
1639 error = gpg_error_from_errno(ENOMEM);
1640 goto fail;
1643 goto again;
1646 fail:
1647 g_free(prefix);
1648 g_free(account);
1649 g_strfreev(req);
1651 if (oreq)
1652 g_strfreev(oreq);
1654 return error;
1658 * This could be faster especially when finding "target" attributes.
1660 static int list_command(assuan_context_t ctx, char *line)
1662 struct client_s *client = assuan_get_pointer(ctx);
1663 gpg_error_t error;
1664 struct list_element_s *elements = NULL;
1665 GString *string;
1666 gchar *tmp;
1667 gint i, total;
1669 if (disable_list_and_dump == TRUE)
1670 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1672 error = file_modified(client);
1674 if (error) {
1675 log_write("%s: %s", client->filename, pwmd_strerror(error));
1676 return send_error(ctx, error);
1679 if (!*line) {
1680 GString *str;
1682 error = list_accounts(client->doc, &str);
1684 if (error)
1685 return send_error(ctx, error);
1687 error = assuan_send_data(ctx, str->str, str->len);
1688 g_string_free(str, TRUE);
1689 return send_error(ctx, error);
1692 elements = g_malloc0(sizeof(struct list_element_s));
1694 if (!elements) {
1695 error = gpg_error_from_errno(ENOMEM);
1696 goto fail;
1699 error = do_list_command(ctx, client->doc, elements, line);
1701 if (error)
1702 goto fail;
1704 if (!error) {
1705 total = g_slist_length(elements->list);
1707 if (!total) {
1708 error = EPWMD_EMPTY_ELEMENT;
1709 goto fail;
1713 * Find element paths with a target and append those element trees to
1714 * the list.
1716 for (i = 0; i < total; i++) {
1717 gchar **req;
1719 tmp = g_slist_nth_data(elements->list, i);
1720 req = split_input_line(tmp, "\t", 0);
1722 if (!req) {
1723 if (g_str_has_prefix(tmp, "!") == TRUE) {
1724 g_strfreev(req);
1725 continue;
1728 else {
1729 gchar **p;
1731 for (p = req; *p; p++) {
1732 if (g_str_has_prefix(*p, "!") == FALSE)
1733 break;
1736 if (!*p) {
1737 g_strfreev(req);
1738 continue;
1742 g_strfreev(req);
1743 error = do_list_command(ctx, client->doc, elements, tmp);
1745 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1746 goto fail;
1748 total = g_slist_length(elements->list);
1752 string = g_string_new(NULL);
1754 for (i = 0; i < total; i++) {
1755 tmp = g_slist_nth_data(elements->list, i);
1756 g_string_append_printf(string, "%s\n", tmp);
1757 g_free(tmp);
1760 string = g_string_truncate(string, string->len - 1);
1761 error = assuan_send_data(ctx, string->str, string->len);
1762 g_string_free(string, TRUE);
1764 fail:
1765 if (elements) {
1766 if (elements->list)
1767 g_slist_free(elements->list);
1769 if (elements->elements)
1770 g_strfreev(elements->elements);
1772 g_free(elements);
1775 return send_error(ctx, error);
1778 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1779 const gchar *value)
1781 xmlAttrPtr a;
1783 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1784 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1786 if (!a)
1787 return EPWMD_LIBXML_ERROR;
1789 else
1790 xmlNodeSetContent(a->children, (xmlChar *)value);
1792 return 0;
1796 * req[0] - element path
1798 static int attribute_list(assuan_context_t ctx, gchar **req)
1800 struct client_s *client = assuan_get_pointer(ctx);
1801 gchar **attrlist = NULL;
1802 gint i = 0;
1803 gchar **path = NULL;
1804 xmlAttrPtr a;
1805 xmlNodePtr n, an;
1806 gchar *line;
1807 gpg_error_t error;
1809 if (!req || !req[0])
1810 return EPWMD_COMMAND_SYNTAX;
1812 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1814 * The first argument may be only an account.
1816 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1817 return EPWMD_COMMAND_SYNTAX;
1820 n = find_account(client->doc, &path, &error, NULL, 0);
1822 if (!n) {
1823 g_strfreev(path);
1824 return error;
1827 if (path[1]) {
1828 n = find_elements(client->doc, n->children, path+1, &error,
1829 NULL, NULL, NULL, FALSE, 0, NULL);
1831 if (!n) {
1832 g_strfreev(path);
1833 return error;
1837 g_strfreev(path);
1839 for (a = n->properties; a; a = a->next) {
1840 gchar **pa;
1842 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1843 if (attrlist)
1844 g_strfreev(attrlist);
1846 error = errno;
1847 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1848 return gpg_error_from_errno(error);
1851 attrlist = pa;
1852 an = a->children;
1853 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1855 if (!attrlist[i]) {
1856 g_strfreev(attrlist);
1857 return gpg_error_from_errno(ENOMEM);
1860 attrlist[++i] = NULL;
1863 if (!attrlist)
1864 return EPWMD_EMPTY_ELEMENT;
1866 line = g_strjoinv("\n", attrlist);
1868 if (!line) {
1869 g_strfreev(attrlist);
1870 return gpg_error_from_errno(ENOMEM);
1873 error = assuan_send_data(ctx, line, strlen(line));
1874 g_free(line);
1875 g_strfreev(attrlist);
1876 return error;
1880 * req[0] - attribute
1881 * req[1] - element path
1883 static int attribute_delete(struct client_s *client, gchar **req)
1885 xmlAttrPtr a;
1886 xmlNodePtr n;
1887 gchar **path = NULL;
1888 gpg_error_t error;
1890 if (!req || !req[0] || !req[1])
1891 return EPWMD_COMMAND_SYNTAX;
1893 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1895 * The first argument may be only an account.
1897 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1898 return EPWMD_COMMAND_SYNTAX;
1902 * Don't remove the "name" attribute for the account element. To remove an
1903 * account use DELETE <account>.
1905 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1906 error = EPWMD_ATTR_SYNTAX;
1907 goto fail;
1910 n = find_account(client->doc, &path, &error, NULL, 0);
1912 if (!n)
1913 goto fail;
1915 if (path[1]) {
1916 n = find_elements(client->doc, n->children, path+1, &error,
1917 NULL, NULL, NULL, FALSE, 0, NULL);
1919 if (!n)
1920 goto fail;
1923 g_strfreev(path);
1925 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1926 return EPWMD_ATTR_NOT_FOUND;
1928 if (xmlRemoveProp(a) == -1)
1929 return EPWMD_LIBXML_ERROR;
1931 return 0;
1933 fail:
1934 g_strfreev(path);
1935 return error;
1939 * Creates a "target" attribute. When other commands encounter an element with
1940 * this attribute, the element path is modified to the target value. If the
1941 * source element path doesn't exist when using 'ATTR SET target', it is
1942 * created, but the destination element path must exist.
1944 * req[0] - source element path
1945 * req[1] - destination element path
1947 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1949 gchar **src, **dst, *line;
1950 gpg_error_t error;
1951 xmlNodePtr n;
1953 if (!req || !req[0] || !req[1])
1954 return EPWMD_COMMAND_SYNTAX;
1956 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1958 * The first argument may be only an account.
1960 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1961 return EPWMD_COMMAND_SYNTAX;
1964 if (valid_element_path(src, FALSE) == FALSE) {
1965 g_strfreev(src);
1966 return EPWMD_INVALID_ELEMENT;
1969 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1971 * The first argument may be only an account.
1973 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1974 error = EPWMD_COMMAND_SYNTAX;
1975 goto fail;
1979 n = find_account(client->doc, &dst, &error, NULL, 0);
1982 * Make sure the destination element path exists.
1984 if (!n)
1985 goto fail;
1987 if (dst[1]) {
1988 n = find_elements(client->doc, n->children, dst+1, &error,
1989 NULL, NULL, NULL, FALSE, 0, NULL);
1991 if (!n)
1992 goto fail;
1995 again:
1996 n = find_account(client->doc, &src, &error, NULL, 0);
1998 if (!n) {
1999 if (error == EPWMD_ELEMENT_NOT_FOUND) {
2000 error = new_account(client->doc, src[0]);
2002 if (error)
2003 goto fail;
2005 goto again;
2007 else
2008 goto fail;
2011 if (src[1]) {
2012 if (!n->children)
2013 n = create_target_elements_cb(n, src+1, &error, NULL);
2014 else
2015 n = find_elements(client->doc, n->children, src+1, &error,
2016 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2018 if (!n)
2019 goto fail;
2022 * Reset the position of the element tree now that the elements
2023 * have been created.
2025 n = find_account(client->doc, &src, &error, NULL, 0);
2027 if (!n)
2028 goto fail;
2030 n = find_elements(client->doc, n->children, src+1, &error,
2031 NULL, NULL, NULL, FALSE, 0, NULL);
2033 if (!n)
2034 goto fail;
2037 line = g_strjoinv("\t", dst);
2038 error = add_attribute(n, "target", line);
2040 if (error) {
2041 g_free(line);
2042 goto fail;
2045 g_free(line);
2046 g_strfreev(src);
2047 g_strfreev(dst);
2048 return 0;
2050 fail:
2051 g_strfreev(src);
2052 g_strfreev(dst);
2053 return error;
2057 * req[0] - account name
2058 * req[1] - new name
2060 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2062 gpg_error_t error;
2063 gchar **tmp;
2064 xmlNodePtr n;
2066 tmp = g_strdupv(req);
2068 if (!tmp)
2069 return gpg_error_from_errno(ENOMEM);
2071 n = find_account(client->doc, &tmp, &error, NULL, 0);
2072 g_strfreev(tmp);
2074 if (!n)
2075 return error;
2077 if (g_utf8_collate(req[0], req[1]) == 0)
2078 return 0;
2081 * Will not overwrite an existing account.
2083 tmp = g_strdupv(req+1);
2085 if (!tmp)
2086 return gpg_error_from_errno(ENOMEM);
2088 n = find_account(client->doc, &tmp, &error, NULL, 0);
2089 g_strfreev(tmp);
2091 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
2092 return error;
2094 if (n)
2095 return EPWMD_ACCOUNT_EXISTS;
2098 * Whitespace not allowed in account names.
2100 if (contains_whitespace(req[1]) == TRUE)
2101 return EPWMD_ATTR_SYNTAX;
2103 tmp = g_strdupv(req);
2105 if (!tmp)
2106 return gpg_error_from_errno(ENOMEM);
2108 n = find_account(client->doc, &tmp, &error, NULL, 0);
2109 g_strfreev(tmp);
2111 if (!n)
2112 return EPWMD_ELEMENT_NOT_FOUND;
2114 return add_attribute(n, "name", req[1]);
2118 * req[0] - attribute
2119 * req[1] - element path
2121 static int attribute_get(assuan_context_t ctx, gchar **req)
2123 struct client_s *client = assuan_get_pointer(ctx);
2124 xmlNodePtr n;
2125 xmlChar *a;
2126 gchar **path= NULL;
2127 gpg_error_t error;
2129 if (!req || !req[0] || !req[1])
2130 return EPWMD_COMMAND_SYNTAX;
2132 if (strchr(req[1], '\t')) {
2133 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2134 return EPWMD_COMMAND_SYNTAX;
2136 else {
2137 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2138 return EPWMD_COMMAND_SYNTAX;
2141 n = find_account(client->doc, &path, &error, NULL, 0);
2143 if (!n)
2144 goto fail;
2146 if (path[1]) {
2147 n = find_elements(client->doc, n->children, path+1, &error,
2148 NULL, NULL, NULL, FALSE, 0, NULL);
2150 if (!n)
2151 goto fail;
2154 g_strfreev(path);
2156 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2157 return EPWMD_ATTR_NOT_FOUND;
2159 error = assuan_send_data(ctx, a, xmlStrlen(a));
2160 xmlFree(a);
2161 return error;
2163 fail:
2164 g_strfreev(path);
2165 return error;
2169 * req[0] - attribute
2170 * req[1] - element path
2171 * req[2] - value
2173 static int attribute_set(struct client_s *client, gchar **req)
2175 gchar **path = NULL;
2176 gpg_error_t error;
2177 xmlNodePtr n;
2179 if (!req || !req[0] || !req[1] || !req[2])
2180 return EPWMD_COMMAND_SYNTAX;
2183 * Reserved attribute names.
2185 if (g_utf8_collate(req[0], "name") == 0) {
2187 * Only reserved for the account element. Not the rest of the
2188 * document.
2190 if (strchr(req[1], '\t') == NULL)
2191 return name_attribute(client, req + 1);
2193 else if (g_utf8_collate(req[0], "target") == 0)
2194 return target_attribute(client, req + 1);
2196 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2198 * The first argument may be only an account.
2200 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2201 return EPWMD_COMMAND_SYNTAX;
2204 n = find_account(client->doc, &path, &error, NULL, 0);
2206 if (!n)
2207 goto fail;
2209 if (path[1]) {
2210 n = find_elements(client->doc, n->children, path+1, &error,
2211 NULL, NULL, NULL, FALSE, 0, NULL);
2213 if (!n)
2214 goto fail;
2217 g_strfreev(path);
2218 return add_attribute(n, req[0], req[2]);
2220 fail:
2221 g_strfreev(path);
2222 return error;
2226 * req[0] - command
2227 * req[1] - attribute name or element path if command is LIST
2228 * req[2] - element path
2229 * req[2] - element path or value
2231 static int attr_command(assuan_context_t ctx, char *line)
2233 struct client_s *client = assuan_get_pointer(ctx);
2234 gchar **req = split_input_line(line, " ", 4);
2235 gpg_error_t error = 0;
2237 error = file_modified(client);
2239 if (error) {
2240 log_write("%s: %s", client->filename ? client->filename : "",
2241 pwmd_strerror(error));
2242 g_strfreev(req);
2243 return send_error(ctx, error);
2246 if (!req || !req[0] || !req[1]) {
2247 g_strfreev(req);
2248 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2251 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2252 error = attribute_set(client, req+1);
2253 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2254 error = attribute_get(ctx, req+1);
2255 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2256 error = attribute_delete(client, req+1);
2257 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2258 error = attribute_list(ctx, req+1);
2259 else
2260 error = EPWMD_COMMAND_SYNTAX;
2262 g_strfreev(req);
2263 return send_error(ctx, error);
2266 static int iscached_command(assuan_context_t ctx, char *line)
2268 gchar **req = split_input_line(line, " ", 0);
2269 guchar md5file[16];
2271 if (!req || !*req) {
2272 g_strfreev(req);
2273 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2276 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2277 g_strfreev(req);
2278 CACHE_LOCK(ctx);
2280 if (cache_iscached(md5file) == FALSE) {
2281 CACHE_UNLOCK;
2282 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2285 CACHE_UNLOCK;
2286 return send_error(ctx, 0);
2289 gpg_error_t send_cache_status(assuan_context_t ctx)
2291 gchar *tmp;
2292 struct client_s *client = assuan_get_pointer(ctx);
2294 CACHE_LOCK(client->ctx);
2295 tmp = print_fmt("%i %i",
2296 cache_file_count(),
2297 (cache_size / sizeof(file_cache_t)) - cache_file_count());
2298 CACHE_UNLOCK;
2300 return assuan_write_status(ctx, "CACHE", tmp);
2303 static int clearcache_command(assuan_context_t ctx, char *line)
2305 struct client_s *client = assuan_get_pointer(ctx);
2306 gchar **req = split_input_line(line, " ", 0);
2307 guchar md5file[16];
2309 CACHE_LOCK(ctx);
2311 if (!req || !*req) {
2312 g_strfreev(req);
2313 cache_clear(client->md5file, 2);
2314 CACHE_UNLOCK;
2315 return send_error(ctx, 0);
2318 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2319 g_strfreev(req);
2321 if (cache_clear(md5file, 1) == FALSE) {
2322 CACHE_UNLOCK;
2323 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2326 CACHE_UNLOCK;
2327 return send_error(ctx, 0);
2330 static int cachetimeout_command(assuan_context_t ctx, char *line)
2332 guchar md5file[16];
2333 glong timeout;
2334 gchar **req = split_input_line(line, " ", 0);
2335 gchar *p;
2336 struct client_s *client = assuan_get_pointer(ctx);
2338 if (!req || !*req || !req[1]) {
2339 g_strfreev(req);
2340 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2343 errno = 0;
2344 timeout = strtol(req[0], &p, 10);
2346 if (errno != 0 || *p != 0) {
2347 g_strfreev(req);
2348 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2351 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2352 g_strfreev(req);
2353 CACHE_LOCK(client->ctx);
2355 if (cache_set_timeout(md5file, timeout) == FALSE) {
2356 CACHE_UNLOCK;
2357 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2360 CACHE_UNLOCK;
2361 send_cache_status_all();
2362 return send_error(ctx, 0);
2365 static int dump_command(assuan_context_t ctx, char *line)
2367 xmlChar *xml;
2368 gssize len;
2369 struct client_s *client = assuan_get_pointer(ctx);
2370 gpg_error_t error;
2372 if (disable_list_and_dump == TRUE)
2373 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2375 error = file_modified(client);
2377 if (error) {
2378 log_write("%s: %s", client->filename ? client->filename : "",
2379 pwmd_strerror(error));
2380 return send_error(ctx, error);
2383 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2384 error = assuan_send_data(ctx, xml, len);
2385 xmlFree(xml);
2386 return send_error(ctx, error);
2389 static int getconfig_command(assuan_context_t ctx, gchar *line)
2391 struct client_s *client = assuan_get_pointer(ctx);
2392 gpg_error_t error = 0;
2393 gchar *p, *tmp;
2395 if (strcmp(line, "key") == 0 || strcmp(line, "key_file") == 0)
2396 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2398 p = get_key_file_string(client->filename ? client->filename : "default", line);
2400 if (!p)
2401 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2403 tmp = expand_homedir(p);
2404 g_free(p);
2405 p = tmp;
2406 error = assuan_send_data(ctx, p, strlen(p));
2407 g_free(p);
2408 return send_error(ctx, error);
2411 void cleanup_assuan(assuan_context_t ctx)
2413 struct client_s *cl = assuan_get_pointer(ctx);
2415 cleanup_client(cl);
2418 static void reset_notify(assuan_context_t ctx)
2420 struct client_s *cl = assuan_get_pointer(ctx);
2422 cleanup_client(cl);
2425 gpg_error_t register_commands(assuan_context_t ctx)
2427 static struct {
2428 const char *name;
2429 int (*handler)(assuan_context_t, char *line);
2430 } table[] = {
2431 { "OPEN", open_command },
2432 { "SAVE", save_command },
2433 { "LIST", list_command },
2434 { "REALPATH", realpath_command },
2435 { "STORE", store_command },
2436 { "DELETE", delete_command },
2437 { "GET", get_command },
2438 { "ATTR", attr_command },
2439 { "ISCACHED", iscached_command },
2440 { "CLEARCACHE", clearcache_command },
2441 { "CACHETIMEOUT", cachetimeout_command },
2442 { "GETCONFIG", getconfig_command },
2443 { "DUMP", dump_command },
2444 { "INPUT", NULL },
2445 { "OUTPUT", NULL },
2446 { NULL, NULL }
2448 int i, rc;
2450 for (i=0; table[i].name; i++) {
2451 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2453 if (rc)
2454 return rc;
2457 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2458 if (rc)
2459 return rc;
2461 return assuan_register_reset_notify(ctx, reset_notify);
2464 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2465 guchar *key)
2467 guchar *iv;
2468 void *inbuf;
2469 gsize insize, len;
2470 guchar tkey[gcrykeysize];
2471 struct file_header_s {
2472 guint iter;
2473 guchar iv[gcryblocksize];
2474 } file_header;
2475 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2476 gcry_cipher_hd_t gh;
2477 guint iter = 0, n_iter = 0;
2478 gint iter_progress;
2479 void *outbuf = NULL;
2480 gint zerror = 0;
2481 glong outsize = 0;
2482 gpg_error_t error;
2484 if (!ctx) {
2485 error = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2487 if (error)
2488 return error;
2490 else
2491 gh = client->gh;
2493 lseek(fd, 0, SEEK_SET);
2494 insize = st.st_size - sizeof(struct file_header_s);
2495 iv = gcry_malloc(gcryblocksize);
2497 if (!iv) {
2498 if (!ctx)
2499 gcry_cipher_close(gh);
2501 return gpg_error_from_errno(ENOMEM);
2504 len = pth_read(fd, &file_header, sizeof(struct file_header_s));
2506 if (len != sizeof(file_header)) {
2507 len = errno;
2509 if (!ctx)
2510 gcry_cipher_close(gh);
2512 gcry_free(iv);
2513 errno = len;
2514 return gpg_error_from_errno(errno);
2517 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2518 inbuf = gcry_malloc(insize);
2520 if (!inbuf) {
2521 if (!ctx)
2522 gcry_cipher_close(gh);
2524 gcry_free(iv);
2525 return gpg_error_from_errno(ENOMEM);
2528 len = pth_read(fd, inbuf, insize);
2530 if (len != insize) {
2531 len = errno;
2533 if (!ctx)
2534 gcry_cipher_close(gh);
2536 gcry_free(iv);
2537 errno = len;
2538 return gpg_error_from_errno(errno);
2541 memcpy(tkey, key, sizeof(tkey));
2542 tkey[0] ^= 1;
2544 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2545 if (!ctx) {
2546 gcry_cipher_close(gh);
2547 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2549 else
2550 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2552 gcry_free(inbuf);
2553 gcry_free(iv);
2554 return error;
2557 if ((error = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2558 if (!ctx) {
2559 gcry_cipher_close(gh);
2560 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2562 else
2563 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2565 gcry_free(inbuf);
2566 gcry_free(iv);
2568 if (!ctx)
2569 gcry_cipher_close(gh);
2571 return error;
2574 iter_progress = get_key_file_integer("default", "iteration_progress");
2576 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2577 error = assuan_write_status(client->ctx, "DECRYPT", "0");
2579 if (error) {
2580 gcry_free(inbuf);
2581 gcry_free(iv);
2582 return error;
2586 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2588 if (error) {
2589 gcry_free(inbuf);
2590 gcry_free(iv);
2591 return error;
2594 if ((error = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2595 if (!ctx) {
2596 gcry_cipher_close(gh);
2597 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2599 else
2600 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2602 gcry_free(inbuf);
2603 gcry_free(iv);
2604 return error;
2607 while (iter < file_header.iter) {
2608 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2609 if (!(iter % iter_progress)) {
2610 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2611 ++n_iter * iter_progress));
2613 if (error) {
2614 gcry_free(inbuf);
2615 gcry_free(iv);
2616 return error;
2621 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2622 if (!ctx) {
2623 gcry_cipher_close(gh);
2624 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2626 else
2627 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2629 gcry_free(inbuf);
2630 gcry_free(iv);
2631 return error;
2634 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2636 if (error) {
2637 if (!ctx) {
2638 gcry_cipher_close(gh);
2639 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2641 else
2642 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2644 gcry_free(inbuf);
2645 gcry_free(iv);
2646 return error;
2649 iter++;
2650 pth_yield(NULL);
2653 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2654 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2656 if (error) {
2657 gcry_free(inbuf);
2658 gcry_free(iv);
2659 return error;
2663 gcry_free(iv);
2665 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2667 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2669 if (zerror == Z_MEM_ERROR) {
2670 gcry_free(inbuf);
2671 return gpg_error_from_errno(ENOMEM);
2673 else if (zerror != Z_DATA_ERROR) {
2674 gcry_free(inbuf);
2676 if (!ctx)
2677 gcry_cipher_close(gh);
2679 return EPWMD_BADKEY;
2682 else {
2683 gcry_free(inbuf);
2684 inbuf = outbuf;
2685 insize = outsize;
2688 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2689 gcry_free(inbuf);
2691 if (!ctx)
2692 gcry_cipher_close(gh);
2694 return EPWMD_BADKEY;
2697 if (ctx) {
2698 client->xml = inbuf;
2699 client->len = insize;
2701 else {
2702 gcry_cipher_close(gh);
2703 gcry_free(inbuf);
2706 return 0;
2710 * This is called after every Assuan command.
2712 void command_finalize(assuan_context_t ctx, gint error)
2714 struct client_s *client = assuan_get_pointer(ctx);
2716 unlock_file_mutex(client);