Each cache slot now contains a mutex. This mutex is locked in the OPEN
[pwmd.git] / src / commands.c
blobf4c654cea6145b8b2da3401b8e536c2181f44d12
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_UNLOCK;
407 error = lock_file_mutex(client);
409 if (error) {
410 g_strfreev(req);
411 return send_error(ctx, error);
414 client->freed = FALSE;
416 if ((error = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
417 g_strfreev(req);
418 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
419 cleanup_client(client);
420 return send_error(ctx, error);
423 if (stat(filename, &st) == 0) {
424 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
425 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
426 g_strfreev(req);
427 cleanup_client(client);
428 return send_error(ctx, EPWMD_INVALID_FILENAME);
431 client->mtime = st.st_mtime;
435 * New files don't need a key.
437 if (access(filename, R_OK|W_OK) != 0) {
438 if (errno != ENOENT) {
439 error = errno;
440 log_write("%s: %s", filename, strerror(errno));
441 g_strfreev(req);
442 cleanup_client(client);
443 return send_syserror(ctx, error);
445 new_doc:
446 if ((client->xml = new_document()) == NULL) {
447 log_write("%s", strerror(ENOMEM));
448 g_strfreev(req);
449 cleanup_client(client);
450 return send_syserror(ctx, ENOMEM);
453 client->len = xmlStrlen(client->xml);
454 client->new = TRUE;
455 client->filename = g_strdup(filename);
457 if (!client->filename) {
458 g_strfreev(req);
459 cleanup_client(client);
460 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
461 return send_syserror(ctx, ENOMEM);
464 if (req[1] && *req[1]) {
465 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
466 memset(req[1], 0, strlen(req[1]));
467 goto update_cache;
470 goto done;
473 if ((fd = open_file(filename, &st)) == -1) {
474 error = errno;
475 log_write("%s: %s", filename, strerror(errno));
476 g_strfreev(req);
477 cleanup_client(client);
478 return send_syserror(ctx, error);
481 if (st.st_size == 0)
482 goto new_doc;
484 CACHE_LOCK(client->ctx);
486 if (cache_get_key(client->md5file, shakey) == TRUE)
487 cached = 1;
488 else {
490 * No key specified and no matching filename found in the cache.
492 if (!req[1] || !*req[1]) {
493 close(fd);
494 g_strfreev(req);
495 cleanup_client(client);
496 CACHE_UNLOCK;
497 return send_error(ctx, EPWMD_KEY);
501 CACHE_UNLOCK;
503 if (!cached) {
504 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
505 memset(req[1], 0, strlen(req[1]));
508 error = try_xml_decrypt(ctx, fd, st, shakey);
510 if (error) {
511 close(fd);
512 memset(shakey, 0, sizeof(shakey));
513 g_strfreev(req);
514 cleanup_client(client);
515 return send_error(ctx, error);
518 close(fd);
519 client->filename = g_strdup(filename);
521 if (!client->filename) {
522 memset(shakey, 0, sizeof(shakey));
523 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
524 g_strfreev(req);
525 cleanup_client(client);
526 return send_syserror(ctx, ENOMEM);
529 update_cache:
530 CACHE_LOCK(client->ctx);
532 if (!cached) {
533 if (cache_update_key(client->md5file, shakey) == FALSE) {
534 memset(shakey, 0, sizeof(shakey));
535 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
536 g_strfreev(req);
537 cleanup_client(client);
538 CACHE_UNLOCK;
539 return send_error(ctx, EPWMD_MAX_SLOTS);
542 timeout = get_key_file_integer(client->filename, "cache_timeout");
543 cache_reset_timeout(client->md5file, timeout);
545 else
546 cache_set_timeout(client->md5file, -2);
548 CACHE_UNLOCK;
549 memset(shakey, 0, sizeof(shakey));
551 done:
552 g_strfreev(req);
553 error = parse_xml(ctx);
555 if (client->xml) {
556 gcry_free(client->xml);
557 client->xml = NULL;
560 if (error == 0)
561 client->state = STATE_OPEN;
563 return send_error(ctx, error);
566 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
567 gint size, gpointer *out, glong *outsize, gint *error)
569 z_stream z;
570 gpointer pout, pin;
571 gint ret;
572 gz_header h;
573 gchar buf[17];
574 gint cmd = Z_NO_FLUSH;
575 gpg_error_t rc;
577 z.zalloc = z_alloc;
578 z.zfree = z_free;
579 z.next_in = pin = data;
580 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
581 z.avail_out = zlib_bufsize;
582 z.next_out = pout = g_malloc(zlib_bufsize);
584 if (!pout) {
585 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
586 *error = Z_MEM_ERROR;
587 return FALSE;
590 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
592 if (ret != Z_OK) {
593 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
594 *error = ret;
595 g_free(pout);
596 return FALSE;
599 memset(&h, 0, sizeof(gz_header));
600 snprintf(buf, sizeof(buf), "%i", size);
601 h.comment = (guchar *)buf;
602 ret = deflateSetHeader(&z, &h);
604 if (ret != Z_OK) {
605 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
606 *error = ret;
607 g_free(pout);
608 deflateEnd(&z);
609 return FALSE;
612 do {
613 gpointer p;
615 ret = deflate(&z, cmd);
617 switch (ret) {
618 case Z_OK:
619 break;
620 case Z_BUF_ERROR:
621 if (!z.avail_out) {
622 p = g_realloc(pout, z.total_out + zlib_bufsize);
624 if (!p) {
625 ret = Z_MEM_ERROR;
626 goto fail;
629 pout = p;
630 z.next_out = pout + z.total_out;
631 z.avail_out = zlib_bufsize;
634 if (!z.avail_in && z.total_in < size) {
635 if (z.total_in + zlib_bufsize > size)
636 z.avail_in = size - z.total_in;
637 else
638 z.avail_in = zlib_bufsize;
640 if (ctx) {
641 rc = assuan_write_status(ctx, "COMPRESS",
642 print_fmt("%i %i", z.total_in, size));
644 if (rc) {
645 ret = rc;
646 goto fail;
651 if (z.total_in >= size)
652 cmd = Z_FINISH;
654 break;
655 case Z_STREAM_END:
656 break;
657 default:
658 goto fail;
661 pth_yield(NULL);
662 } while (ret != Z_STREAM_END);
664 if (ctx) {
665 rc = assuan_write_status(ctx, "COMPRESS",
666 print_fmt("%i %i", z.total_in, size));
668 if (rc) {
669 ret = rc;
670 goto fail;
674 *out = pout;
675 *outsize = z.total_out;
676 deflateEnd(&z);
677 return TRUE;
679 fail:
680 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
681 *error = ret;
682 g_free(pout);
683 deflateEnd(&z);
684 return FALSE;
687 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
688 const gchar *filename, gpointer data, size_t insize,
689 const guchar *shakey, guint iter)
691 gsize len = insize;
692 gint fd;
693 gpointer inbuf;
694 guchar tkey[gcrykeysize];
695 gchar *p;
696 gint error;
697 gpg_error_t rc;
698 guint iter_progress = 0, n_iter = 0, xiter = 0;
699 gchar tmp[FILENAME_MAX];
700 struct file_header_s {
701 guint iter;
702 guchar iv[gcryblocksize];
703 } file_header;
705 if (insize / gcryblocksize) {
706 len = (insize / gcryblocksize) * gcryblocksize;
708 if (insize % gcryblocksize)
709 len += gcryblocksize;
713 * Resize the existing xml buffer to the block size required by gcrypt
714 * rather than duplicating it and wasting memory.
716 inbuf = gcry_realloc(data, len);
718 if (!inbuf)
719 return gpg_error_from_errno(ENOMEM);
721 insize = len;
722 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
723 memcpy(tkey, shakey, sizeof(tkey));
724 tkey[0] ^= 1;
726 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
727 gcry_free(inbuf);
728 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
729 return rc;
732 file_header.iter = iter;
734 if (client)
735 iter_progress = get_key_file_integer("default", "iteration_progress");
737 if (client && iter_progress && file_header.iter >= iter_progress) {
738 error = assuan_write_status(client->ctx, "ENCRYPT", "0");
740 if (error) {
741 gcry_free(inbuf);
742 return error;
746 while (xiter < file_header.iter) {
747 if (client && iter_progress > 0 && xiter >= iter_progress) {
748 if (!(xiter % iter_progress)) {
749 error = assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
750 ++n_iter * iter_progress));
752 if (error) {
753 gcry_free(inbuf);
754 return error;
759 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
760 sizeof(file_header.iv)))) {
761 gcry_free(inbuf);
762 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
763 return rc;
766 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
767 == FALSE) {
768 gcry_free(inbuf);
769 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
770 return rc;
773 xiter++;
774 pth_yield(NULL);
777 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
778 sizeof(file_header.iv)))) {
779 gcry_free(inbuf);
780 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
781 return rc;
784 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
785 gcry_free(inbuf);
786 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
787 return rc;
790 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
791 gcry_free(inbuf);
792 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
793 return rc;
796 if (client && iter_progress && file_header.iter >= iter_progress) {
797 error = assuan_write_status(client->ctx, "ENCRYPT",
798 print_fmt("%i", file_header.iter));
800 if (error) {
801 gcry_free(inbuf);
802 return error;
806 if (filename) {
807 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
809 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
810 error = errno;
811 gcry_free(inbuf);
812 p = strrchr(tmp, '/');
813 p++;
814 log_write("%s: %s", p, strerror(errno));
815 return gpg_error_from_errno(error);
818 else
820 * xml_import() from command line.
822 fd = STDOUT_FILENO;
824 len = pth_write(fd, &file_header, sizeof(struct file_header_s));
826 if (len != sizeof(file_header)) {
827 len = errno;
829 if (filename)
830 close(fd);
832 gcry_free(inbuf);
833 return gpg_error_from_errno(len);
836 len = pth_write(fd, inbuf, insize);
838 if (len != insize) {
839 len = errno;
841 if (filename)
842 close(fd);
844 gcry_free(inbuf);
845 return gpg_error_from_errno(len);
848 if (fsync(fd) == -1) {
849 len = errno;
850 close(fd);
851 gcry_free(inbuf);
852 return gpg_error_from_errno(len);
855 if (filename) {
856 struct stat st;
857 mode_t mode = 0;
859 close(fd);
861 if (stat(filename, &st) == 0)
862 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
864 if (rename(tmp, filename) == -1) {
865 len = errno;
866 gcry_free(inbuf);
867 return gpg_error_from_errno(len);
870 if (mode)
871 chmod(filename, mode);
874 gcry_free(inbuf);
875 return 0;
878 static int save_command(assuan_context_t ctx, char *line)
880 gpointer xmlbuf;
881 xmlChar *p;
882 gint len;
883 gint cached = 0;
884 guchar shakey[gcrykeysize];
885 gint iter;
886 struct stat st;
887 struct client_s *client = assuan_get_pointer(ctx);
888 gpg_error_t error;
889 gint timeout;
890 gpointer outbuf;
891 glong outsize = 0;
892 gint zerror;
894 error = file_modified(client);
896 if (error) {
897 log_write("%s: %s", client->filename ? client->filename : "",
898 pwmd_strerror(error));
899 return send_error(ctx, error);
902 error = lock_file_mutex(client);
904 if (error)
905 return send_error(ctx, error);
907 if (stat(client->filename, &st) == -1 && errno != ENOENT)
908 return send_syserror(ctx, errno);
910 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
911 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
912 return send_error(ctx, EPWMD_INVALID_FILENAME);
915 if (!line || !*line) {
916 CACHE_LOCK(ctx);
918 if (cache_get_key(client->md5file, shakey) == FALSE) {
919 CACHE_UNLOCK;
920 return send_error(ctx, EPWMD_KEY);
923 CACHE_UNLOCK;
924 cached = 1;
926 else {
927 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
928 memset(line, 0, strlen(line));
931 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
932 xmlbuf = p;
934 iter = get_key_file_integer(client->filename, "compression_level");
936 if (iter < 0)
937 iter = 0;
939 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
940 memset(shakey, 0, sizeof(shakey));
941 xmlFree(xmlbuf);
943 if (zerror == Z_MEM_ERROR) {
944 return send_syserror(ctx, ENOMEM);
946 else
947 return send_error(ctx, GPG_ERR_COMPR_ALGO);
949 else {
950 gcry_free(xmlbuf);
951 xmlbuf = outbuf;
952 len = outsize;
955 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
956 iter = 0;
958 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
960 if (error) {
961 memset(shakey, 0, sizeof(shakey));
962 return send_error(ctx, error);
965 stat(client->filename, &st);
966 client->mtime = st.st_mtime;
967 timeout = get_key_file_integer(client->filename, "cache_timeout");
968 CACHE_LOCK(client->ctx);
970 if (cached) {
971 memset(shakey, 0, sizeof(shakey));
972 cache_reset_timeout(client->md5file, timeout);
973 client->new = FALSE;
974 CACHE_UNLOCK;
975 return send_error(ctx, 0);
978 if (cache_update_key(client->md5file, shakey) == FALSE) {
979 memset(shakey, 0, sizeof(shakey));
980 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
981 CACHE_UNLOCK;
982 return send_error(ctx, EPWMD_MAX_SLOTS);
985 client->new = FALSE;
986 memset(shakey, 0, sizeof(shakey));
987 cache_reset_timeout(client->md5file, timeout);
988 CACHE_UNLOCK;
989 return send_error(ctx, 0);
992 static gboolean contains_whitespace(const gchar *str)
994 const gchar *p = str;
995 gunichar c;
996 glong len;
998 len = g_utf8_strlen(p++, -1) -1;
1000 while (len--) {
1001 c = g_utf8_get_char(p++);
1003 if (g_unichar_isspace(c))
1004 return TRUE;
1007 return FALSE;
1010 static int delete_command(assuan_context_t ctx, char *line)
1012 struct client_s *client = assuan_get_pointer(ctx);
1013 gchar **req;
1014 gpg_error_t error;
1015 xmlNodePtr n;
1017 error = file_modified(client);
1019 if (error) {
1020 log_write("%s: %s", client->filename ? client->filename : "",
1021 pwmd_strerror(error));
1022 return send_error(ctx, error);
1025 if (strchr(line, '\t'))
1026 req = split_input_line(line, "\t", -1);
1027 else
1028 req = split_input_line(line, " ", -1);
1030 if (!req || !*req)
1031 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1033 n = find_account(client->doc, &req, &error, NULL, 0);
1035 if (!n) {
1036 g_strfreev(req);
1037 return send_error(ctx, error);
1041 * No sub-node defined. Remove the entire node (account).
1043 if (!req[1]) {
1044 if (n) {
1045 xmlUnlinkNode(n);
1046 xmlFreeNode(n);
1049 g_strfreev(req);
1050 return send_error(ctx, 0);
1053 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1054 g_strfreev(req);
1056 if (!n)
1057 return send_error(ctx, error);
1059 if (n) {
1060 xmlUnlinkNode(n);
1061 xmlFreeNode(n);
1064 return send_error(ctx, 0);
1068 * Don't return with assuan_process_done() here. This has been called from
1069 * assuan_process_next() and the command should be finished in
1070 * client_thread().
1072 static int store_command_finalize(gpointer data, gint rc, guchar *line,
1073 gsize len)
1075 assuan_context_t ctx = data;
1076 struct client_s *client = assuan_get_pointer(ctx);
1077 gchar **req;
1078 guchar *result = line;
1079 xmlNodePtr n;
1080 gpg_error_t error = file_modified(client);
1082 if (rc) {
1083 if (line)
1084 #ifndef MEM_DEBUG
1085 xfree(line);
1086 #else
1087 free(line);
1088 #endif
1089 return rc;
1092 req = split_input_line((gchar *)result, "\t", 0);
1093 #ifndef MEM_DEBUG
1094 xfree(line);
1095 #else
1096 free(line);
1097 #endif
1099 if (rc) {
1100 if (req)
1101 g_strfreev(req);
1103 return rc;
1106 if (!req || !*req)
1107 return EPWMD_COMMAND_SYNTAX;
1109 again:
1110 n = find_account(client->doc, &req, &error, NULL, 0);
1112 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1113 if (contains_whitespace(*req) == TRUE) {
1114 g_strfreev(req);
1115 return EPWMD_INVALID_ELEMENT;
1118 error = new_account(client->doc, *req);
1120 if (error) {
1121 g_strfreev(req);
1122 return error;
1125 goto again;
1128 if (!n) {
1129 g_strfreev(req);
1130 return error;
1133 if (req[1]) {
1134 if (!n->children)
1135 create_elements_cb(n, req+1, &error, NULL);
1136 else
1137 find_elements(client->doc, n->children, req+1, &error,
1138 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1141 g_strfreev(req);
1142 return send_error(ctx, error);
1145 static int store_command(assuan_context_t ctx, char *line)
1147 struct client_s *client = assuan_get_pointer(ctx);
1148 gpg_error_t error = file_modified(client);
1150 if (error) {
1151 log_write("%s: %s", client->filename ? client->filename : "",
1152 pwmd_strerror(error));
1153 return send_error(ctx, error);
1156 error = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1158 if (error)
1159 return send_error(ctx, error);
1161 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1162 return 0;
1165 static int get_command(assuan_context_t ctx, char *line)
1167 struct client_s *client = assuan_get_pointer(ctx);
1168 gchar **req;
1169 gpg_error_t error;
1170 xmlNodePtr n;
1172 error = file_modified(client);
1174 if (error) {
1175 log_write("%s: %s", client->filename ? client->filename : "",
1176 pwmd_strerror(error));
1177 return send_error(ctx, error);
1180 req = split_input_line(line, "\t", -1);
1182 if (!req || !*req) {
1183 g_strfreev(req);
1184 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1187 n = find_account(client->doc, &req, &error, NULL, 0);
1189 if (!n) {
1190 g_strfreev(req);
1191 return send_error(ctx, error);
1194 if (req[1])
1195 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1197 g_strfreev(req);
1199 if (error)
1200 return send_error(ctx, error);
1202 if (!n || !n->children)
1203 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1205 n = n->children;
1207 if (!n || !n->content || !*n->content)
1208 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1210 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1211 return send_error(ctx, error);
1214 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1216 xmlChar *p = path;
1217 gint n;
1218 gchar *buf;
1220 if (!p)
1221 return NULL;
1223 for (n = 0; *p && n < 3; p++) {
1224 if (*p == '/')
1225 n++;
1228 if (strstr((gchar *)p, "text()") != NULL)
1229 p[xmlStrlen(p) - 7] = 0;
1231 for (n = 0; p[n]; n++) {
1232 if (p[n] == '/')
1233 p[n] = '\t';
1236 buf = g_strdup_printf("%s\t%s", account, p);
1237 return buf;
1240 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1242 gchar **a;
1243 va_list ap;
1244 gchar *buf;
1245 gint len = *array ? g_strv_length(*array) : 0;
1246 gint ret;
1248 if (!fmt)
1249 return FALSE;
1251 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1252 return FALSE;
1254 va_start(ap, fmt);
1255 ret = g_vasprintf(&buf, fmt, ap);
1256 va_end(ap);
1258 if (ret == -1)
1259 return FALSE;
1261 a[len++] = buf;
1262 a[len] = NULL;
1263 *array = a;
1264 return TRUE;
1267 struct realpath_s {
1268 gchar *account;
1271 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1272 gpg_error_t *error, void *data)
1274 struct realpath_s *rp = data;
1276 if (rp->account)
1277 g_free(rp->account);
1279 rp->account = g_strdup(req[0]);
1281 if (!rp->account) {
1282 *error = gpg_error_from_errno(ENOMEM);
1283 return NULL;
1286 return node;
1289 static int realpath_command(assuan_context_t ctx, char *line)
1291 gpg_error_t error;
1292 struct client_s *client = assuan_get_pointer(ctx);
1293 xmlChar *p;
1294 gchar **req;
1295 gchar *result, *t;
1296 gint i;
1297 xmlNodePtr n;
1298 struct realpath_s *rp;
1299 GString *string;
1301 error = file_modified(client);
1303 if (error) {
1304 log_write("%s: %s", client->filename ? client->filename : "",
1305 pwmd_strerror(error));
1306 return send_error(ctx, error);
1309 if (strchr(line, '\t') != NULL) {
1310 if ((req = split_input_line(line, "\t", 0)) == NULL)
1311 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1313 else {
1314 if ((req = split_input_line(line, " ", 0)) == NULL)
1315 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1318 n = find_account(client->doc, &req, &error, NULL, 0);
1320 if (!n) {
1321 g_strfreev(req);
1322 return send_error(ctx, error);
1325 rp = g_malloc(sizeof(struct realpath_s));
1327 if (!rp) {
1328 g_strfreev(req);
1329 return send_syserror(ctx, ENOMEM);
1332 rp->account = g_strdup(req[0]);
1334 if (!rp->account) {
1335 g_strfreev(req);
1336 return send_syserror(ctx, ENOMEM);
1339 if (req[1]) {
1340 n = find_elements(client->doc, n->children, req+1, &error,
1341 NULL, realpath_elements_cb, NULL, FALSE, 0, rp);
1343 if (!n) {
1344 g_free(rp->account);
1345 g_free(rp);
1346 g_strfreev(req);
1347 return send_error(ctx, error);
1351 p = xmlGetNodePath(n);
1352 result = element_path_to_req(rp->account, p);
1354 if (!result) {
1355 g_free(result);
1356 g_free(rp->account);
1357 g_free(rp);
1358 g_strfreev(req);
1359 xmlFree(p);
1360 return send_syserror(ctx, ENOMEM);
1363 string = g_string_new(result);
1364 g_free(result);
1365 g_free(rp->account);
1366 g_free(rp);
1367 g_strfreev(req);
1368 xmlFree(p);
1369 i = 0;
1371 again:
1372 for (t = string->str + i; *t; t++, i++) {
1373 if (!i || *t == '\t') {
1374 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1375 goto again;
1379 error = assuan_send_data(ctx, string->str, string->len);
1380 g_string_free(string, TRUE);
1381 return send_error(ctx, error);
1384 struct list_element_s {
1385 GSList *list;
1386 gchar **elements;
1389 static gboolean append_to_element_list(struct list_element_s *elements)
1391 gchar *tmp;
1392 gint i, total;
1393 gchar *a;
1394 GSList *list;
1396 if (!elements || !elements->elements)
1397 return TRUE;
1399 tmp = g_strjoinv("\t", elements->elements);
1401 if (!tmp)
1402 return FALSE;
1404 g_strfreev(elements->elements);
1405 elements->elements = NULL;
1406 total = g_slist_length(elements->list);
1407 a = g_utf8_collate_key(tmp, -1);
1409 if (!a) {
1410 g_free(tmp);
1411 return FALSE;
1415 * Removes duplicate element paths from the list. This is needed when
1416 * appending an element tree from list_command(). The glib docs recommend
1417 * using g_utf8_collate_key() for a large number of strings.
1419 for (i = 0; i < total; i++) {
1420 gchar *p = g_slist_nth_data(elements->list, i);
1421 gchar *b = g_utf8_collate_key(p, -1);
1423 if (!b) {
1424 g_free(a);
1425 g_free(tmp);
1426 return FALSE;
1429 if (strcmp(a, b) == 0) {
1430 g_free(a);
1431 g_free(b);
1432 g_free(tmp);
1433 return TRUE;
1436 g_free(b);
1439 g_free(a);
1440 list = g_slist_append(elements->list, tmp);
1442 if (!list)
1443 return FALSE;
1445 elements->list = list;
1446 return TRUE;
1449 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1450 struct list_element_s *elements, gchar *prefix)
1452 xmlNodePtr n;
1453 gpg_error_t error;
1455 if (append_to_element_list(elements) == FALSE)
1456 return gpg_error_from_errno(ENOMEM);
1458 for (n = node; n; n = n->next) {
1459 if (n->type == XML_ELEMENT_NODE) {
1460 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1461 gchar *tmp;
1463 if (content) {
1464 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1465 return gpg_error_from_errno(ENOMEM);
1467 if (append_to_element_list(elements) == FALSE)
1468 return gpg_error_from_errno(ENOMEM);
1471 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1473 if (!tmp)
1474 return gpg_error_from_errno(ENOMEM);
1476 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1477 g_free(tmp);
1478 return gpg_error_from_errno(ENOMEM);
1481 if (n->children) {
1482 error = do_list_recurse(doc, n->children, elements, tmp);
1483 g_free(tmp);
1485 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1486 return error;
1488 else
1489 g_free(tmp);
1491 if (append_to_element_list(elements) == FALSE)
1492 return gpg_error_from_errno(ENOMEM);
1496 return 0;
1499 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1500 struct list_element_s *elements, char *line)
1502 gchar *prefix = NULL, *account;
1503 gchar **req = NULL, **oreq = NULL, *tmp;
1504 xmlNodePtr n;
1505 gboolean account_is_literal, account_has_target = FALSE;
1506 gint which = 0;
1507 gchar **p;
1508 gpg_error_t error;
1510 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1511 if ((req = split_input_line(line, " ", 0)) == NULL)
1512 return EPWMD_COMMAND_SYNTAX;
1515 prefix = g_strdup(*req);
1517 if (!prefix) {
1518 g_strfreev(req);
1519 return gpg_error_from_errno(ENOMEM);
1522 account = g_strdup(*req);
1524 if (!account) {
1525 g_free(prefix);
1526 g_strfreev(req);
1527 return gpg_error_from_errno(ENOMEM);
1530 oreq = g_strdupv(req);
1532 if (!oreq) {
1533 g_free(prefix);
1534 g_free(account);
1535 g_strfreev(req);
1536 return gpg_error_from_errno(ENOMEM);
1539 p = req;
1540 again:
1541 account_has_target = FALSE;
1542 account_is_literal = is_literal_element_str(prefix);
1543 n = find_account(doc, &p, &error, &account_has_target, 0);
1545 if (which)
1546 oreq = p;
1547 else
1548 req = p;
1550 if (!n)
1551 goto fail;
1553 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1554 tmp = g_strdup_printf("!%s", prefix);
1556 if (!tmp) {
1557 error = gpg_error_from_errno(ENOMEM);
1558 goto fail;
1561 g_free(prefix);
1562 prefix = tmp;
1565 if (*(p+1)) {
1566 gchar *t;
1568 n = find_elements(doc, n->children, p+1, &error, NULL, NULL, NULL,
1569 TRUE, 0, NULL);
1571 if (error)
1572 goto fail;
1574 tmp = g_strjoinv("\t", p+1);
1575 if (!tmp) {
1576 error = gpg_error_from_errno(ENOMEM);
1577 goto fail;
1580 t = g_strdup_printf("%s\t%s", prefix, tmp);
1581 if (!t) {
1582 error = gpg_error_from_errno(ENOMEM);
1583 goto fail;
1586 g_free(prefix);
1587 prefix = t;
1588 g_free(tmp);
1591 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1592 error = gpg_error_from_errno(ENOMEM);
1593 goto fail;
1596 if (node_has_child_element(n->children) == FALSE) {
1597 if (append_to_element_list(elements) == FALSE) {
1598 error = gpg_error_from_errno(ENOMEM);
1599 goto fail;
1602 else
1603 error = do_list_recurse(doc, n->children, elements, prefix);
1605 if (error)
1606 goto fail;
1608 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1609 g_free(*oreq);
1610 *oreq = g_strdup_printf("!%s", account);
1612 if (!*oreq) {
1613 error = gpg_error_from_errno(ENOMEM);
1614 goto fail;
1617 p = oreq;
1618 g_free(prefix);
1619 prefix = g_strdup(*oreq);
1621 if (!prefix) {
1622 error = gpg_error_from_errno(ENOMEM);
1623 goto fail;
1626 goto again;
1629 fail:
1630 g_free(prefix);
1631 g_free(account);
1632 g_strfreev(req);
1634 if (oreq)
1635 g_strfreev(oreq);
1637 return error;
1641 * This could be faster especially when finding "target" attributes.
1643 static int list_command(assuan_context_t ctx, char *line)
1645 struct client_s *client = assuan_get_pointer(ctx);
1646 gpg_error_t error;
1647 struct list_element_s *elements = NULL;
1648 GString *string;
1649 gchar *tmp;
1650 gint i, total;
1652 if (disable_list_and_dump == TRUE)
1653 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1655 error = file_modified(client);
1657 if (error) {
1658 log_write("%s: %s", client->filename, pwmd_strerror(error));
1659 return send_error(ctx, error);
1662 if (!*line) {
1663 GString *str;
1665 error = list_accounts(client->doc, &str);
1667 if (error)
1668 return send_error(ctx, error);
1670 error = assuan_send_data(ctx, str->str, str->len);
1671 g_string_free(str, TRUE);
1672 return send_error(ctx, error);
1675 elements = g_malloc0(sizeof(struct list_element_s));
1677 if (!elements) {
1678 error = gpg_error_from_errno(ENOMEM);
1679 goto fail;
1682 error = do_list_command(ctx, client->doc, elements, line);
1684 if (error)
1685 goto fail;
1687 if (!error) {
1688 total = g_slist_length(elements->list);
1690 if (!total) {
1691 error = EPWMD_EMPTY_ELEMENT;
1692 goto fail;
1696 * Find element paths with a target and append those element trees to
1697 * the list.
1699 for (i = 0; i < total; i++) {
1700 gchar **req;
1702 tmp = g_slist_nth_data(elements->list, i);
1703 req = split_input_line(tmp, "\t", 0);
1705 if (!req) {
1706 if (g_str_has_prefix(tmp, "!") == TRUE) {
1707 g_strfreev(req);
1708 continue;
1711 else {
1712 gchar **p;
1714 for (p = req; *p; p++) {
1715 if (g_str_has_prefix(*p, "!") == FALSE)
1716 break;
1719 if (!*p) {
1720 g_strfreev(req);
1721 continue;
1725 g_strfreev(req);
1726 error = do_list_command(ctx, client->doc, elements, tmp);
1728 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1729 goto fail;
1731 total = g_slist_length(elements->list);
1735 string = g_string_new(NULL);
1737 for (i = 0; i < total; i++) {
1738 tmp = g_slist_nth_data(elements->list, i);
1739 g_string_append_printf(string, "%s\n", tmp);
1740 g_free(tmp);
1743 string = g_string_truncate(string, string->len - 1);
1744 error = assuan_send_data(ctx, string->str, string->len);
1745 g_string_free(string, TRUE);
1747 fail:
1748 if (elements) {
1749 if (elements->list)
1750 g_slist_free(elements->list);
1752 if (elements->elements)
1753 g_strfreev(elements->elements);
1755 g_free(elements);
1758 return send_error(ctx, error);
1761 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1762 const gchar *value)
1764 xmlAttrPtr a;
1766 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1767 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1769 if (!a)
1770 return EPWMD_LIBXML_ERROR;
1772 else
1773 xmlNodeSetContent(a->children, (xmlChar *)value);
1775 return 0;
1779 * req[0] - element path
1781 static int attribute_list(assuan_context_t ctx, gchar **req)
1783 struct client_s *client = assuan_get_pointer(ctx);
1784 gchar **attrlist = NULL;
1785 gint i = 0;
1786 gchar **path = NULL;
1787 xmlAttrPtr a;
1788 xmlNodePtr n, an;
1789 gchar *line;
1790 gpg_error_t error;
1792 if (!req || !req[0])
1793 return EPWMD_COMMAND_SYNTAX;
1795 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1797 * The first argument may be only an account.
1799 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1800 return EPWMD_COMMAND_SYNTAX;
1803 n = find_account(client->doc, &path, &error, NULL, 0);
1805 if (!n) {
1806 g_strfreev(path);
1807 return error;
1810 if (path[1]) {
1811 n = find_elements(client->doc, n->children, path+1, &error,
1812 NULL, NULL, NULL, FALSE, 0, NULL);
1814 if (!n) {
1815 g_strfreev(path);
1816 return error;
1820 g_strfreev(path);
1822 for (a = n->properties; a; a = a->next) {
1823 gchar **pa;
1825 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1826 if (attrlist)
1827 g_strfreev(attrlist);
1829 error = errno;
1830 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1831 return gpg_error_from_errno(error);
1834 attrlist = pa;
1835 an = a->children;
1836 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1838 if (!attrlist[i]) {
1839 g_strfreev(attrlist);
1840 return gpg_error_from_errno(ENOMEM);
1843 attrlist[++i] = NULL;
1846 if (!attrlist)
1847 return EPWMD_EMPTY_ELEMENT;
1849 line = g_strjoinv("\n", attrlist);
1851 if (!line) {
1852 g_strfreev(attrlist);
1853 return gpg_error_from_errno(ENOMEM);
1856 error = assuan_send_data(ctx, line, strlen(line));
1857 g_free(line);
1858 g_strfreev(attrlist);
1859 return error;
1863 * req[0] - attribute
1864 * req[1] - element path
1866 static int attribute_delete(struct client_s *client, gchar **req)
1868 xmlAttrPtr a;
1869 xmlNodePtr n;
1870 gchar **path = NULL;
1871 gpg_error_t error;
1873 if (!req || !req[0] || !req[1])
1874 return EPWMD_COMMAND_SYNTAX;
1876 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1878 * The first argument may be only an account.
1880 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1881 return EPWMD_COMMAND_SYNTAX;
1885 * Don't remove the "name" attribute for the account element. To remove an
1886 * account use DELETE <account>.
1888 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1889 error = EPWMD_ATTR_SYNTAX;
1890 goto fail;
1893 n = find_account(client->doc, &path, &error, NULL, 0);
1895 if (!n)
1896 goto fail;
1898 if (path[1]) {
1899 n = find_elements(client->doc, n->children, path+1, &error,
1900 NULL, NULL, NULL, FALSE, 0, NULL);
1902 if (!n)
1903 goto fail;
1906 g_strfreev(path);
1908 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1909 return EPWMD_ATTR_NOT_FOUND;
1911 if (xmlRemoveProp(a) == -1)
1912 return EPWMD_LIBXML_ERROR;
1914 return 0;
1916 fail:
1917 g_strfreev(path);
1918 return error;
1922 * Creates a "target" attribute. When other commands encounter an element with
1923 * this attribute, the element path is modified to the target value. If the
1924 * source element path doesn't exist when using 'ATTR SET target', it is
1925 * created, but the destination element path must exist.
1927 * req[0] - source element path
1928 * req[1] - destination element path
1930 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1932 gchar **src, **dst, *line;
1933 gpg_error_t error;
1934 xmlNodePtr n;
1936 if (!req || !req[0] || !req[1])
1937 return EPWMD_COMMAND_SYNTAX;
1939 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1941 * The first argument may be only an account.
1943 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1944 return EPWMD_COMMAND_SYNTAX;
1947 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1949 * The first argument may be only an account.
1951 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1952 error = EPWMD_COMMAND_SYNTAX;
1953 goto fail;
1957 n = find_account(client->doc, &dst, &error, NULL, 0);
1960 * Make sure the destination element path exists.
1962 if (!n)
1963 goto fail;
1965 if (dst[1]) {
1966 n = find_elements(client->doc, n->children, dst+1, &error,
1967 NULL, NULL, NULL, FALSE, 0, NULL);
1969 if (!n)
1970 goto fail;
1973 again:
1974 n = find_account(client->doc, &src, &error, NULL, 0);
1976 if (!n) {
1977 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1978 error = new_account(client->doc, src[0]);
1980 if (error)
1981 goto fail;
1983 goto again;
1985 else
1986 goto fail;
1989 if (src[1]) {
1990 if (!n->children)
1991 n = create_target_elements_cb(n, src+1, &error, NULL);
1992 else
1993 n = find_elements(client->doc, n->children, src+1, &error,
1994 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1996 if (!n)
1997 goto fail;
2000 * Reset the position of the element tree now that the elements
2001 * have been created.
2003 n = find_account(client->doc, &src, &error, NULL, 0);
2005 if (!n)
2006 goto fail;
2008 n = find_elements(client->doc, n->children, src+1, &error,
2009 NULL, NULL, NULL, FALSE, 0, NULL);
2011 if (!n)
2012 goto fail;
2015 line = g_strjoinv("\t", dst);
2016 error = add_attribute(n, "target", line);
2018 if (error) {
2019 g_free(line);
2020 goto fail;
2023 g_free(line);
2024 g_strfreev(src);
2025 g_strfreev(dst);
2026 return 0;
2028 fail:
2029 g_strfreev(src);
2030 g_strfreev(dst);
2031 return error;
2035 * req[0] - account name
2036 * req[1] - new name
2038 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2040 gpg_error_t error;
2041 gchar **tmp;
2042 xmlNodePtr n;
2044 tmp = g_strdupv(req);
2046 if (!tmp)
2047 return gpg_error_from_errno(ENOMEM);
2049 n = find_account(client->doc, &tmp, &error, NULL, 0);
2050 g_strfreev(tmp);
2052 if (!n)
2053 return error;
2055 if (g_utf8_collate(req[0], req[1]) == 0)
2056 return 0;
2059 * Will not overwrite an existing account.
2061 tmp = g_strdupv(req+1);
2063 if (!tmp)
2064 return gpg_error_from_errno(ENOMEM);
2066 n = find_account(client->doc, &tmp, &error, NULL, 0);
2067 g_strfreev(tmp);
2069 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
2070 return error;
2072 if (n)
2073 return EPWMD_ACCOUNT_EXISTS;
2076 * Whitespace not allowed in account names.
2078 if (contains_whitespace(req[1]) == TRUE)
2079 return EPWMD_ATTR_SYNTAX;
2081 tmp = g_strdupv(req);
2083 if (!tmp)
2084 return gpg_error_from_errno(ENOMEM);
2086 n = find_account(client->doc, &tmp, &error, NULL, 0);
2087 g_strfreev(tmp);
2089 if (!n)
2090 return EPWMD_ELEMENT_NOT_FOUND;
2092 return add_attribute(n, "name", req[1]);
2096 * req[0] - attribute
2097 * req[1] - element path
2099 static int attribute_get(assuan_context_t ctx, gchar **req)
2101 struct client_s *client = assuan_get_pointer(ctx);
2102 xmlNodePtr n;
2103 xmlChar *a;
2104 gchar **path= NULL;
2105 gpg_error_t error;
2107 if (!req || !req[0] || !req[1])
2108 return EPWMD_COMMAND_SYNTAX;
2110 if (strchr(req[1], '\t')) {
2111 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2112 return EPWMD_COMMAND_SYNTAX;
2114 else {
2115 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2116 return EPWMD_COMMAND_SYNTAX;
2119 n = find_account(client->doc, &path, &error, NULL, 0);
2121 if (!n)
2122 goto fail;
2124 if (path[1]) {
2125 n = find_elements(client->doc, n->children, path+1, &error,
2126 NULL, NULL, NULL, FALSE, 0, NULL);
2128 if (!n)
2129 goto fail;
2132 g_strfreev(path);
2134 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2135 return EPWMD_ATTR_NOT_FOUND;
2137 error = assuan_send_data(ctx, a, xmlStrlen(a));
2138 xmlFree(a);
2139 return error;
2141 fail:
2142 g_strfreev(path);
2143 return error;
2147 * req[0] - attribute
2148 * req[1] - element path
2149 * req[2] - value
2151 static int attribute_set(struct client_s *client, gchar **req)
2153 gchar **path = NULL;
2154 gpg_error_t error;
2155 xmlNodePtr n;
2157 if (!req || !req[0] || !req[1] || !req[2])
2158 return EPWMD_COMMAND_SYNTAX;
2161 * Reserved attribute names.
2163 if (g_utf8_collate(req[0], "name") == 0) {
2165 * Only reserved for the account element. Not the rest of the
2166 * document.
2168 if (strchr(req[1], '\t') == NULL)
2169 return name_attribute(client, req + 1);
2171 else if (g_utf8_collate(req[0], "target") == 0)
2172 return target_attribute(client, req + 1);
2174 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2176 * The first argument may be only an account.
2178 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2179 return EPWMD_COMMAND_SYNTAX;
2182 n = find_account(client->doc, &path, &error, NULL, 0);
2184 if (!n)
2185 goto fail;
2187 if (path[1]) {
2188 n = find_elements(client->doc, n->children, path+1, &error,
2189 NULL, NULL, NULL, FALSE, 0, NULL);
2191 if (!n)
2192 goto fail;
2195 g_strfreev(path);
2196 return add_attribute(n, req[0], req[2]);
2198 fail:
2199 g_strfreev(path);
2200 return error;
2204 * req[0] - command
2205 * req[1] - attribute name or element path if command is LIST
2206 * req[2] - element path
2207 * req[2] - element path or value
2209 static int attr_command(assuan_context_t ctx, char *line)
2211 struct client_s *client = assuan_get_pointer(ctx);
2212 gchar **req = split_input_line(line, " ", 4);
2213 gpg_error_t error = 0;
2215 error = file_modified(client);
2217 if (error) {
2218 log_write("%s: %s", client->filename ? client->filename : "",
2219 pwmd_strerror(error));
2220 g_strfreev(req);
2221 return send_error(ctx, error);
2224 if (!req || !req[0] || !req[1]) {
2225 g_strfreev(req);
2226 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2229 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2230 error = attribute_set(client, req+1);
2231 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2232 error = attribute_get(ctx, req+1);
2233 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2234 error = attribute_delete(client, req+1);
2235 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2236 error = attribute_list(ctx, req+1);
2237 else
2238 error = EPWMD_COMMAND_SYNTAX;
2240 g_strfreev(req);
2241 return send_error(ctx, error);
2244 static int iscached_command(assuan_context_t ctx, char *line)
2246 gchar **req = split_input_line(line, " ", 0);
2247 guchar md5file[16];
2249 if (!req || !*req) {
2250 g_strfreev(req);
2251 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2254 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2255 g_strfreev(req);
2256 CACHE_LOCK(ctx);
2258 if (cache_iscached(md5file) == FALSE) {
2259 CACHE_UNLOCK;
2260 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2263 CACHE_UNLOCK;
2264 return send_error(ctx, 0);
2267 gpg_error_t send_cache_status(assuan_context_t ctx)
2269 gchar *tmp;
2270 struct client_s *client = assuan_get_pointer(ctx);
2272 CACHE_LOCK(client->ctx);
2273 tmp = print_fmt("%i %i",
2274 cache_file_count(),
2275 (cache_size / sizeof(file_cache_t)) - cache_file_count());
2276 CACHE_UNLOCK;
2278 return assuan_write_status(ctx, "CACHE", tmp);
2281 static int clearcache_command(assuan_context_t ctx, char *line)
2283 struct client_s *client = assuan_get_pointer(ctx);
2284 gchar **req = split_input_line(line, " ", 0);
2285 guchar md5file[16];
2287 CACHE_LOCK(ctx);
2289 if (!req || !*req) {
2290 g_strfreev(req);
2291 cache_clear(client->md5file, 2);
2292 CACHE_UNLOCK;
2293 return send_error(ctx, 0);
2296 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2297 g_strfreev(req);
2299 if (cache_clear(md5file, 1) == FALSE) {
2300 CACHE_UNLOCK;
2301 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2304 CACHE_UNLOCK;
2305 return send_error(ctx, 0);
2308 static int cachetimeout_command(assuan_context_t ctx, char *line)
2310 guchar md5file[16];
2311 glong timeout;
2312 gchar **req = split_input_line(line, " ", 0);
2313 gchar *p;
2314 struct client_s *client = assuan_get_pointer(ctx);
2316 if (!req || !*req || !req[1]) {
2317 g_strfreev(req);
2318 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2321 errno = 0;
2322 timeout = strtol(req[0], &p, 10);
2324 if (errno != 0 || *p != 0) {
2325 g_strfreev(req);
2326 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2329 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2330 g_strfreev(req);
2331 CACHE_LOCK(client->ctx);
2333 if (cache_set_timeout(md5file, timeout) == FALSE) {
2334 CACHE_UNLOCK;
2335 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2338 CACHE_UNLOCK;
2339 send_cache_status_all();
2340 return send_error(ctx, 0);
2343 static int dump_command(assuan_context_t ctx, char *line)
2345 xmlChar *xml;
2346 gssize len;
2347 struct client_s *client = assuan_get_pointer(ctx);
2348 gpg_error_t error;
2350 if (disable_list_and_dump == TRUE)
2351 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2353 error = file_modified(client);
2355 if (error) {
2356 log_write("%s: %s", client->filename ? client->filename : "",
2357 pwmd_strerror(error));
2358 return send_error(ctx, error);
2361 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2362 error = assuan_send_data(ctx, xml, len);
2363 xmlFree(xml);
2364 return send_error(ctx, error);
2367 static int getconfig_command(assuan_context_t ctx, gchar *line)
2369 struct client_s *client = assuan_get_pointer(ctx);
2370 gpg_error_t error = 0;
2371 gchar *p, *tmp;
2373 if (strcmp(line, "key") == 0 || strcmp(line, "key_file") == 0)
2374 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2376 p = get_key_file_string(client->filename ? client->filename : "default", line);
2378 if (!p)
2379 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2381 tmp = expand_homedir(p);
2382 g_free(p);
2383 p = tmp;
2384 error = assuan_send_data(ctx, p, strlen(p));
2385 g_free(p);
2386 return send_error(ctx, error);
2389 void cleanup_assuan(assuan_context_t ctx)
2391 struct client_s *cl = assuan_get_pointer(ctx);
2393 cleanup_client(cl);
2396 static void reset_notify(assuan_context_t ctx)
2398 struct client_s *cl = assuan_get_pointer(ctx);
2400 cleanup_client(cl);
2403 gpg_error_t register_commands(assuan_context_t ctx)
2405 static struct {
2406 const char *name;
2407 int (*handler)(assuan_context_t, char *line);
2408 } table[] = {
2409 { "OPEN", open_command },
2410 { "SAVE", save_command },
2411 { "LIST", list_command },
2412 { "REALPATH", realpath_command },
2413 { "STORE", store_command },
2414 { "DELETE", delete_command },
2415 { "GET", get_command },
2416 { "ATTR", attr_command },
2417 { "ISCACHED", iscached_command },
2418 { "CLEARCACHE", clearcache_command },
2419 { "CACHETIMEOUT", cachetimeout_command },
2420 { "GETCONFIG", getconfig_command },
2421 { "DUMP", dump_command },
2422 { "INPUT", NULL },
2423 { "OUTPUT", NULL },
2424 { NULL, NULL }
2426 int i, rc;
2428 for (i=0; table[i].name; i++) {
2429 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2431 if (rc)
2432 return rc;
2435 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2436 if (rc)
2437 return rc;
2439 return assuan_register_reset_notify(ctx, reset_notify);
2442 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2443 guchar *key)
2445 guchar *iv;
2446 void *inbuf;
2447 gsize insize, len;
2448 guchar tkey[gcrykeysize];
2449 struct file_header_s {
2450 guint iter;
2451 guchar iv[gcryblocksize];
2452 } file_header;
2453 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2454 gcry_cipher_hd_t gh;
2455 guint iter = 0, n_iter = 0;
2456 gint iter_progress;
2457 void *outbuf = NULL;
2458 gint zerror = 0;
2459 glong outsize = 0;
2460 gpg_error_t error;
2462 if (!ctx) {
2463 error = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2465 if (error)
2466 return error;
2468 else
2469 gh = client->gh;
2471 lseek(fd, 0, SEEK_SET);
2472 insize = st.st_size - sizeof(struct file_header_s);
2473 iv = gcry_malloc(gcryblocksize);
2475 if (!iv) {
2476 if (!ctx)
2477 gcry_cipher_close(gh);
2479 return gpg_error_from_errno(ENOMEM);
2482 len = pth_read(fd, &file_header, sizeof(struct file_header_s));
2484 if (len != sizeof(file_header)) {
2485 len = errno;
2487 if (!ctx)
2488 gcry_cipher_close(gh);
2490 gcry_free(iv);
2491 errno = len;
2492 return gpg_error_from_errno(errno);
2495 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2496 inbuf = gcry_malloc(insize);
2498 if (!inbuf) {
2499 if (!ctx)
2500 gcry_cipher_close(gh);
2502 gcry_free(iv);
2503 return gpg_error_from_errno(ENOMEM);
2506 len = pth_read(fd, inbuf, insize);
2508 if (len != insize) {
2509 len = errno;
2511 if (!ctx)
2512 gcry_cipher_close(gh);
2514 gcry_free(iv);
2515 errno = len;
2516 return gpg_error_from_errno(errno);
2519 memcpy(tkey, key, sizeof(tkey));
2520 tkey[0] ^= 1;
2522 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2523 if (!ctx) {
2524 gcry_cipher_close(gh);
2525 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2527 else
2528 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2530 gcry_free(inbuf);
2531 gcry_free(iv);
2532 return error;
2535 if ((error = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2536 if (!ctx) {
2537 gcry_cipher_close(gh);
2538 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2540 else
2541 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2543 gcry_free(inbuf);
2544 gcry_free(iv);
2546 if (!ctx)
2547 gcry_cipher_close(gh);
2549 return error;
2552 iter_progress = get_key_file_integer("default", "iteration_progress");
2554 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2555 error = assuan_write_status(client->ctx, "DECRYPT", "0");
2557 if (error) {
2558 gcry_free(inbuf);
2559 gcry_free(iv);
2560 return error;
2564 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2566 if (error) {
2567 gcry_free(inbuf);
2568 gcry_free(iv);
2569 return error;
2572 if ((error = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2573 if (!ctx) {
2574 gcry_cipher_close(gh);
2575 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2577 else
2578 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2580 gcry_free(inbuf);
2581 gcry_free(iv);
2582 return error;
2585 while (iter < file_header.iter) {
2586 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2587 if (!(iter % iter_progress)) {
2588 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2589 ++n_iter * iter_progress));
2591 if (error) {
2592 gcry_free(inbuf);
2593 gcry_free(iv);
2594 return error;
2599 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2600 if (!ctx) {
2601 gcry_cipher_close(gh);
2602 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2604 else
2605 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2607 gcry_free(inbuf);
2608 gcry_free(iv);
2609 return error;
2612 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2614 if (error) {
2615 if (!ctx) {
2616 gcry_cipher_close(gh);
2617 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2619 else
2620 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2622 gcry_free(inbuf);
2623 gcry_free(iv);
2624 return error;
2627 iter++;
2628 pth_yield(NULL);
2631 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2632 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2634 if (error) {
2635 gcry_free(inbuf);
2636 gcry_free(iv);
2637 return error;
2641 gcry_free(iv);
2643 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2645 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2647 if (zerror == Z_MEM_ERROR) {
2648 gcry_free(inbuf);
2649 return gpg_error_from_errno(ENOMEM);
2651 else if (zerror != Z_DATA_ERROR) {
2652 gcry_free(inbuf);
2654 if (!ctx)
2655 gcry_cipher_close(gh);
2657 return EPWMD_BADKEY;
2660 else {
2661 gcry_free(inbuf);
2662 inbuf = outbuf;
2663 insize = outsize;
2666 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2667 gcry_free(inbuf);
2669 if (!ctx)
2670 gcry_cipher_close(gh);
2672 return EPWMD_BADKEY;
2675 if (ctx) {
2676 client->xml = inbuf;
2677 client->len = insize;
2679 else {
2680 gcry_cipher_close(gh);
2681 gcry_free(inbuf);
2684 return 0;
2688 * This is called after every Assuan command.
2690 void command_finalize(assuan_context_t ctx, gint error)
2692 struct client_s *client = assuan_get_pointer(ctx);
2694 unlock_file_mutex(client);