Another INQUIRE fix for the STORE command. This is basically the same
[pwmd.git] / src / commands.c
blobb850fb697eef457db28e98e8a453e662e37acb4a
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 again:
1120 n = find_account(client->doc, &req, &error, NULL, 0);
1122 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1123 if (contains_whitespace(*req) == TRUE) {
1124 g_strfreev(req);
1125 return EPWMD_INVALID_ELEMENT;
1128 error = new_account(client->doc, *req);
1130 if (error) {
1131 g_strfreev(req);
1132 return error;
1135 goto again;
1138 if (!n) {
1139 g_strfreev(req);
1140 return error;
1143 if (req[1]) {
1144 if (!n->children)
1145 create_elements_cb(n, req+1, &error, NULL);
1146 else
1147 find_elements(client->doc, n->children, req+1, &error,
1148 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1151 g_strfreev(req);
1152 client->inquire_status = INQUIRE_DONE;
1153 return error;
1156 static int store_command(assuan_context_t ctx, char *line)
1158 struct client_s *client = assuan_get_pointer(ctx);
1159 gpg_error_t error = file_modified(client);
1161 if (error) {
1162 log_write("%s: %s", client->filename ? client->filename : "",
1163 pwmd_strerror(error));
1164 return send_error(ctx, error);
1167 error = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1169 if (error)
1170 return send_error(ctx, error);
1172 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1173 client->inquire_status = INQUIRE_BUSY;
1174 return 0;
1177 static int get_command(assuan_context_t ctx, char *line)
1179 struct client_s *client = assuan_get_pointer(ctx);
1180 gchar **req;
1181 gpg_error_t error;
1182 xmlNodePtr n;
1184 error = file_modified(client);
1186 if (error) {
1187 log_write("%s: %s", client->filename ? client->filename : "",
1188 pwmd_strerror(error));
1189 return send_error(ctx, error);
1192 req = split_input_line(line, "\t", -1);
1194 if (!req || !*req) {
1195 g_strfreev(req);
1196 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1199 n = find_account(client->doc, &req, &error, NULL, 0);
1201 if (!n) {
1202 g_strfreev(req);
1203 return send_error(ctx, error);
1206 if (req[1])
1207 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1209 g_strfreev(req);
1211 if (error)
1212 return send_error(ctx, error);
1214 if (!n || !n->children)
1215 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1217 n = n->children;
1219 if (!n || !n->content || !*n->content)
1220 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1222 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1223 return send_error(ctx, error);
1226 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1228 xmlChar *p = path;
1229 gint n;
1230 gchar *buf;
1232 if (!p)
1233 return NULL;
1235 for (n = 0; *p && n < 3; p++) {
1236 if (*p == '/')
1237 n++;
1240 if (strstr((gchar *)p, "text()") != NULL)
1241 p[xmlStrlen(p) - 7] = 0;
1243 for (n = 0; p[n]; n++) {
1244 if (p[n] == '/')
1245 p[n] = '\t';
1248 buf = g_strdup_printf("%s\t%s", account, p);
1249 return buf;
1252 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1254 gchar **a;
1255 va_list ap;
1256 gchar *buf;
1257 gint len = *array ? g_strv_length(*array) : 0;
1258 gint ret;
1260 if (!fmt)
1261 return FALSE;
1263 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1264 return FALSE;
1266 va_start(ap, fmt);
1267 ret = g_vasprintf(&buf, fmt, ap);
1268 va_end(ap);
1270 if (ret == -1)
1271 return FALSE;
1273 a[len++] = buf;
1274 a[len] = NULL;
1275 *array = a;
1276 return TRUE;
1279 struct realpath_s {
1280 gchar *account;
1283 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1284 gpg_error_t *error, void *data)
1286 struct realpath_s *rp = data;
1288 if (rp->account)
1289 g_free(rp->account);
1291 rp->account = g_strdup(req[0]);
1293 if (!rp->account) {
1294 *error = gpg_error_from_errno(ENOMEM);
1295 return NULL;
1298 return node;
1301 static int realpath_command(assuan_context_t ctx, char *line)
1303 gpg_error_t error;
1304 struct client_s *client = assuan_get_pointer(ctx);
1305 xmlChar *p;
1306 gchar **req;
1307 gchar *result, *t;
1308 gint i;
1309 xmlNodePtr n;
1310 struct realpath_s *rp;
1311 GString *string;
1313 error = file_modified(client);
1315 if (error) {
1316 log_write("%s: %s", client->filename ? client->filename : "",
1317 pwmd_strerror(error));
1318 return send_error(ctx, error);
1321 if (strchr(line, '\t') != NULL) {
1322 if ((req = split_input_line(line, "\t", 0)) == NULL)
1323 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1325 else {
1326 if ((req = split_input_line(line, " ", 0)) == NULL)
1327 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1330 n = find_account(client->doc, &req, &error, NULL, 0);
1332 if (!n) {
1333 g_strfreev(req);
1334 return send_error(ctx, error);
1337 rp = g_malloc(sizeof(struct realpath_s));
1339 if (!rp) {
1340 g_strfreev(req);
1341 return send_syserror(ctx, ENOMEM);
1344 rp->account = g_strdup(req[0]);
1346 if (!rp->account) {
1347 g_strfreev(req);
1348 return send_syserror(ctx, ENOMEM);
1351 if (req[1]) {
1352 n = find_elements(client->doc, n->children, req+1, &error,
1353 NULL, realpath_elements_cb, NULL, FALSE, 0, rp);
1355 if (!n) {
1356 g_free(rp->account);
1357 g_free(rp);
1358 g_strfreev(req);
1359 return send_error(ctx, error);
1363 p = xmlGetNodePath(n);
1364 result = element_path_to_req(rp->account, p);
1366 if (!result) {
1367 g_free(result);
1368 g_free(rp->account);
1369 g_free(rp);
1370 g_strfreev(req);
1371 xmlFree(p);
1372 return send_syserror(ctx, ENOMEM);
1375 string = g_string_new(result);
1376 g_free(result);
1377 g_free(rp->account);
1378 g_free(rp);
1379 g_strfreev(req);
1380 xmlFree(p);
1381 i = 0;
1383 again:
1384 for (t = string->str + i; *t; t++, i++) {
1385 if (!i || *t == '\t') {
1386 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1387 goto again;
1391 error = assuan_send_data(ctx, string->str, string->len);
1392 g_string_free(string, TRUE);
1393 return send_error(ctx, error);
1396 struct list_element_s {
1397 GSList *list;
1398 gchar **elements;
1401 static gboolean append_to_element_list(struct list_element_s *elements)
1403 gchar *tmp;
1404 gint i, total;
1405 gchar *a;
1406 GSList *list;
1408 if (!elements || !elements->elements)
1409 return TRUE;
1411 tmp = g_strjoinv("\t", elements->elements);
1413 if (!tmp)
1414 return FALSE;
1416 g_strfreev(elements->elements);
1417 elements->elements = NULL;
1418 total = g_slist_length(elements->list);
1419 a = g_utf8_collate_key(tmp, -1);
1421 if (!a) {
1422 g_free(tmp);
1423 return FALSE;
1427 * Removes duplicate element paths from the list. This is needed when
1428 * appending an element tree from list_command(). The glib docs recommend
1429 * using g_utf8_collate_key() for a large number of strings.
1431 for (i = 0; i < total; i++) {
1432 gchar *p = g_slist_nth_data(elements->list, i);
1433 gchar *b = g_utf8_collate_key(p, -1);
1435 if (!b) {
1436 g_free(a);
1437 g_free(tmp);
1438 return FALSE;
1441 if (strcmp(a, b) == 0) {
1442 g_free(a);
1443 g_free(b);
1444 g_free(tmp);
1445 return TRUE;
1448 g_free(b);
1451 g_free(a);
1452 list = g_slist_append(elements->list, tmp);
1454 if (!list)
1455 return FALSE;
1457 elements->list = list;
1458 return TRUE;
1461 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1462 struct list_element_s *elements, gchar *prefix)
1464 xmlNodePtr n;
1465 gpg_error_t error;
1467 if (append_to_element_list(elements) == FALSE)
1468 return gpg_error_from_errno(ENOMEM);
1470 for (n = node; n; n = n->next) {
1471 if (n->type == XML_ELEMENT_NODE) {
1472 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1473 gchar *tmp;
1475 if (content) {
1476 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1477 return gpg_error_from_errno(ENOMEM);
1479 if (append_to_element_list(elements) == FALSE)
1480 return gpg_error_from_errno(ENOMEM);
1483 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1485 if (!tmp)
1486 return gpg_error_from_errno(ENOMEM);
1488 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1489 g_free(tmp);
1490 return gpg_error_from_errno(ENOMEM);
1493 if (n->children) {
1494 error = do_list_recurse(doc, n->children, elements, tmp);
1495 g_free(tmp);
1497 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1498 return error;
1500 else
1501 g_free(tmp);
1503 if (append_to_element_list(elements) == FALSE)
1504 return gpg_error_from_errno(ENOMEM);
1508 return 0;
1511 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1512 struct list_element_s *elements, char *line)
1514 gchar *prefix = NULL, *account;
1515 gchar **req = NULL, **oreq = NULL, *tmp;
1516 xmlNodePtr n;
1517 gboolean account_is_literal, account_has_target = FALSE;
1518 gint which = 0;
1519 gchar **p;
1520 gpg_error_t error;
1522 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1523 if ((req = split_input_line(line, " ", 0)) == NULL)
1524 return EPWMD_COMMAND_SYNTAX;
1527 prefix = g_strdup(*req);
1529 if (!prefix) {
1530 g_strfreev(req);
1531 return gpg_error_from_errno(ENOMEM);
1534 account = g_strdup(*req);
1536 if (!account) {
1537 g_free(prefix);
1538 g_strfreev(req);
1539 return gpg_error_from_errno(ENOMEM);
1542 oreq = g_strdupv(req);
1544 if (!oreq) {
1545 g_free(prefix);
1546 g_free(account);
1547 g_strfreev(req);
1548 return gpg_error_from_errno(ENOMEM);
1551 p = req;
1552 again:
1553 account_has_target = FALSE;
1554 account_is_literal = is_literal_element_str(prefix);
1555 n = find_account(doc, &p, &error, &account_has_target, 0);
1557 if (which)
1558 oreq = p;
1559 else
1560 req = p;
1562 if (!n)
1563 goto fail;
1565 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1566 tmp = g_strdup_printf("!%s", prefix);
1568 if (!tmp) {
1569 error = gpg_error_from_errno(ENOMEM);
1570 goto fail;
1573 g_free(prefix);
1574 prefix = tmp;
1577 if (*(p+1)) {
1578 gchar *t;
1580 n = find_elements(doc, n->children, p+1, &error, NULL, NULL, NULL,
1581 TRUE, 0, NULL);
1583 if (error)
1584 goto fail;
1586 tmp = g_strjoinv("\t", p+1);
1587 if (!tmp) {
1588 error = gpg_error_from_errno(ENOMEM);
1589 goto fail;
1592 t = g_strdup_printf("%s\t%s", prefix, tmp);
1593 if (!t) {
1594 error = gpg_error_from_errno(ENOMEM);
1595 goto fail;
1598 g_free(prefix);
1599 prefix = t;
1600 g_free(tmp);
1603 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1604 error = gpg_error_from_errno(ENOMEM);
1605 goto fail;
1608 if (node_has_child_element(n->children) == FALSE) {
1609 if (append_to_element_list(elements) == FALSE) {
1610 error = gpg_error_from_errno(ENOMEM);
1611 goto fail;
1614 else
1615 error = do_list_recurse(doc, n->children, elements, prefix);
1617 if (error)
1618 goto fail;
1620 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1621 g_free(*oreq);
1622 *oreq = g_strdup_printf("!%s", account);
1624 if (!*oreq) {
1625 error = gpg_error_from_errno(ENOMEM);
1626 goto fail;
1629 p = oreq;
1630 g_free(prefix);
1631 prefix = g_strdup(*oreq);
1633 if (!prefix) {
1634 error = gpg_error_from_errno(ENOMEM);
1635 goto fail;
1638 goto again;
1641 fail:
1642 g_free(prefix);
1643 g_free(account);
1644 g_strfreev(req);
1646 if (oreq)
1647 g_strfreev(oreq);
1649 return error;
1653 * This could be faster especially when finding "target" attributes.
1655 static int list_command(assuan_context_t ctx, char *line)
1657 struct client_s *client = assuan_get_pointer(ctx);
1658 gpg_error_t error;
1659 struct list_element_s *elements = NULL;
1660 GString *string;
1661 gchar *tmp;
1662 gint i, total;
1664 if (disable_list_and_dump == TRUE)
1665 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1667 error = file_modified(client);
1669 if (error) {
1670 log_write("%s: %s", client->filename, pwmd_strerror(error));
1671 return send_error(ctx, error);
1674 if (!*line) {
1675 GString *str;
1677 error = list_accounts(client->doc, &str);
1679 if (error)
1680 return send_error(ctx, error);
1682 error = assuan_send_data(ctx, str->str, str->len);
1683 g_string_free(str, TRUE);
1684 return send_error(ctx, error);
1687 elements = g_malloc0(sizeof(struct list_element_s));
1689 if (!elements) {
1690 error = gpg_error_from_errno(ENOMEM);
1691 goto fail;
1694 error = do_list_command(ctx, client->doc, elements, line);
1696 if (error)
1697 goto fail;
1699 if (!error) {
1700 total = g_slist_length(elements->list);
1702 if (!total) {
1703 error = EPWMD_EMPTY_ELEMENT;
1704 goto fail;
1708 * Find element paths with a target and append those element trees to
1709 * the list.
1711 for (i = 0; i < total; i++) {
1712 gchar **req;
1714 tmp = g_slist_nth_data(elements->list, i);
1715 req = split_input_line(tmp, "\t", 0);
1717 if (!req) {
1718 if (g_str_has_prefix(tmp, "!") == TRUE) {
1719 g_strfreev(req);
1720 continue;
1723 else {
1724 gchar **p;
1726 for (p = req; *p; p++) {
1727 if (g_str_has_prefix(*p, "!") == FALSE)
1728 break;
1731 if (!*p) {
1732 g_strfreev(req);
1733 continue;
1737 g_strfreev(req);
1738 error = do_list_command(ctx, client->doc, elements, tmp);
1740 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1741 goto fail;
1743 total = g_slist_length(elements->list);
1747 string = g_string_new(NULL);
1749 for (i = 0; i < total; i++) {
1750 tmp = g_slist_nth_data(elements->list, i);
1751 g_string_append_printf(string, "%s\n", tmp);
1752 g_free(tmp);
1755 string = g_string_truncate(string, string->len - 1);
1756 error = assuan_send_data(ctx, string->str, string->len);
1757 g_string_free(string, TRUE);
1759 fail:
1760 if (elements) {
1761 if (elements->list)
1762 g_slist_free(elements->list);
1764 if (elements->elements)
1765 g_strfreev(elements->elements);
1767 g_free(elements);
1770 return send_error(ctx, error);
1773 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1774 const gchar *value)
1776 xmlAttrPtr a;
1778 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1779 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1781 if (!a)
1782 return EPWMD_LIBXML_ERROR;
1784 else
1785 xmlNodeSetContent(a->children, (xmlChar *)value);
1787 return 0;
1791 * req[0] - element path
1793 static int attribute_list(assuan_context_t ctx, gchar **req)
1795 struct client_s *client = assuan_get_pointer(ctx);
1796 gchar **attrlist = NULL;
1797 gint i = 0;
1798 gchar **path = NULL;
1799 xmlAttrPtr a;
1800 xmlNodePtr n, an;
1801 gchar *line;
1802 gpg_error_t error;
1804 if (!req || !req[0])
1805 return EPWMD_COMMAND_SYNTAX;
1807 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1809 * The first argument may be only an account.
1811 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1812 return EPWMD_COMMAND_SYNTAX;
1815 n = find_account(client->doc, &path, &error, NULL, 0);
1817 if (!n) {
1818 g_strfreev(path);
1819 return error;
1822 if (path[1]) {
1823 n = find_elements(client->doc, n->children, path+1, &error,
1824 NULL, NULL, NULL, FALSE, 0, NULL);
1826 if (!n) {
1827 g_strfreev(path);
1828 return error;
1832 g_strfreev(path);
1834 for (a = n->properties; a; a = a->next) {
1835 gchar **pa;
1837 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1838 if (attrlist)
1839 g_strfreev(attrlist);
1841 error = errno;
1842 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1843 return gpg_error_from_errno(error);
1846 attrlist = pa;
1847 an = a->children;
1848 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1850 if (!attrlist[i]) {
1851 g_strfreev(attrlist);
1852 return gpg_error_from_errno(ENOMEM);
1855 attrlist[++i] = NULL;
1858 if (!attrlist)
1859 return EPWMD_EMPTY_ELEMENT;
1861 line = g_strjoinv("\n", attrlist);
1863 if (!line) {
1864 g_strfreev(attrlist);
1865 return gpg_error_from_errno(ENOMEM);
1868 error = assuan_send_data(ctx, line, strlen(line));
1869 g_free(line);
1870 g_strfreev(attrlist);
1871 return error;
1875 * req[0] - attribute
1876 * req[1] - element path
1878 static int attribute_delete(struct client_s *client, gchar **req)
1880 xmlAttrPtr a;
1881 xmlNodePtr n;
1882 gchar **path = NULL;
1883 gpg_error_t error;
1885 if (!req || !req[0] || !req[1])
1886 return EPWMD_COMMAND_SYNTAX;
1888 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1890 * The first argument may be only an account.
1892 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1893 return EPWMD_COMMAND_SYNTAX;
1897 * Don't remove the "name" attribute for the account element. To remove an
1898 * account use DELETE <account>.
1900 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1901 error = EPWMD_ATTR_SYNTAX;
1902 goto fail;
1905 n = find_account(client->doc, &path, &error, NULL, 0);
1907 if (!n)
1908 goto fail;
1910 if (path[1]) {
1911 n = find_elements(client->doc, n->children, path+1, &error,
1912 NULL, NULL, NULL, FALSE, 0, NULL);
1914 if (!n)
1915 goto fail;
1918 g_strfreev(path);
1920 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1921 return EPWMD_ATTR_NOT_FOUND;
1923 if (xmlRemoveProp(a) == -1)
1924 return EPWMD_LIBXML_ERROR;
1926 return 0;
1928 fail:
1929 g_strfreev(path);
1930 return error;
1934 * Creates a "target" attribute. When other commands encounter an element with
1935 * this attribute, the element path is modified to the target value. If the
1936 * source element path doesn't exist when using 'ATTR SET target', it is
1937 * created, but the destination element path must exist.
1939 * req[0] - source element path
1940 * req[1] - destination element path
1942 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1944 gchar **src, **dst, *line;
1945 gpg_error_t error;
1946 xmlNodePtr n;
1948 if (!req || !req[0] || !req[1])
1949 return EPWMD_COMMAND_SYNTAX;
1951 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1953 * The first argument may be only an account.
1955 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1956 return EPWMD_COMMAND_SYNTAX;
1959 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1961 * The first argument may be only an account.
1963 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1964 error = EPWMD_COMMAND_SYNTAX;
1965 goto fail;
1969 n = find_account(client->doc, &dst, &error, NULL, 0);
1972 * Make sure the destination element path exists.
1974 if (!n)
1975 goto fail;
1977 if (dst[1]) {
1978 n = find_elements(client->doc, n->children, dst+1, &error,
1979 NULL, NULL, NULL, FALSE, 0, NULL);
1981 if (!n)
1982 goto fail;
1985 again:
1986 n = find_account(client->doc, &src, &error, NULL, 0);
1988 if (!n) {
1989 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1990 error = new_account(client->doc, src[0]);
1992 if (error)
1993 goto fail;
1995 goto again;
1997 else
1998 goto fail;
2001 if (src[1]) {
2002 if (!n->children)
2003 n = create_target_elements_cb(n, src+1, &error, NULL);
2004 else
2005 n = find_elements(client->doc, n->children, src+1, &error,
2006 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2008 if (!n)
2009 goto fail;
2012 * Reset the position of the element tree now that the elements
2013 * have been created.
2015 n = find_account(client->doc, &src, &error, NULL, 0);
2017 if (!n)
2018 goto fail;
2020 n = find_elements(client->doc, n->children, src+1, &error,
2021 NULL, NULL, NULL, FALSE, 0, NULL);
2023 if (!n)
2024 goto fail;
2027 line = g_strjoinv("\t", dst);
2028 error = add_attribute(n, "target", line);
2030 if (error) {
2031 g_free(line);
2032 goto fail;
2035 g_free(line);
2036 g_strfreev(src);
2037 g_strfreev(dst);
2038 return 0;
2040 fail:
2041 g_strfreev(src);
2042 g_strfreev(dst);
2043 return error;
2047 * req[0] - account name
2048 * req[1] - new name
2050 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2052 gpg_error_t error;
2053 gchar **tmp;
2054 xmlNodePtr n;
2056 tmp = g_strdupv(req);
2058 if (!tmp)
2059 return gpg_error_from_errno(ENOMEM);
2061 n = find_account(client->doc, &tmp, &error, NULL, 0);
2062 g_strfreev(tmp);
2064 if (!n)
2065 return error;
2067 if (g_utf8_collate(req[0], req[1]) == 0)
2068 return 0;
2071 * Will not overwrite an existing account.
2073 tmp = g_strdupv(req+1);
2075 if (!tmp)
2076 return gpg_error_from_errno(ENOMEM);
2078 n = find_account(client->doc, &tmp, &error, NULL, 0);
2079 g_strfreev(tmp);
2081 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
2082 return error;
2084 if (n)
2085 return EPWMD_ACCOUNT_EXISTS;
2088 * Whitespace not allowed in account names.
2090 if (contains_whitespace(req[1]) == TRUE)
2091 return EPWMD_ATTR_SYNTAX;
2093 tmp = g_strdupv(req);
2095 if (!tmp)
2096 return gpg_error_from_errno(ENOMEM);
2098 n = find_account(client->doc, &tmp, &error, NULL, 0);
2099 g_strfreev(tmp);
2101 if (!n)
2102 return EPWMD_ELEMENT_NOT_FOUND;
2104 return add_attribute(n, "name", req[1]);
2108 * req[0] - attribute
2109 * req[1] - element path
2111 static int attribute_get(assuan_context_t ctx, gchar **req)
2113 struct client_s *client = assuan_get_pointer(ctx);
2114 xmlNodePtr n;
2115 xmlChar *a;
2116 gchar **path= NULL;
2117 gpg_error_t error;
2119 if (!req || !req[0] || !req[1])
2120 return EPWMD_COMMAND_SYNTAX;
2122 if (strchr(req[1], '\t')) {
2123 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2124 return EPWMD_COMMAND_SYNTAX;
2126 else {
2127 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2128 return EPWMD_COMMAND_SYNTAX;
2131 n = find_account(client->doc, &path, &error, NULL, 0);
2133 if (!n)
2134 goto fail;
2136 if (path[1]) {
2137 n = find_elements(client->doc, n->children, path+1, &error,
2138 NULL, NULL, NULL, FALSE, 0, NULL);
2140 if (!n)
2141 goto fail;
2144 g_strfreev(path);
2146 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2147 return EPWMD_ATTR_NOT_FOUND;
2149 error = assuan_send_data(ctx, a, xmlStrlen(a));
2150 xmlFree(a);
2151 return error;
2153 fail:
2154 g_strfreev(path);
2155 return error;
2159 * req[0] - attribute
2160 * req[1] - element path
2161 * req[2] - value
2163 static int attribute_set(struct client_s *client, gchar **req)
2165 gchar **path = NULL;
2166 gpg_error_t error;
2167 xmlNodePtr n;
2169 if (!req || !req[0] || !req[1] || !req[2])
2170 return EPWMD_COMMAND_SYNTAX;
2173 * Reserved attribute names.
2175 if (g_utf8_collate(req[0], "name") == 0) {
2177 * Only reserved for the account element. Not the rest of the
2178 * document.
2180 if (strchr(req[1], '\t') == NULL)
2181 return name_attribute(client, req + 1);
2183 else if (g_utf8_collate(req[0], "target") == 0)
2184 return target_attribute(client, req + 1);
2186 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2188 * The first argument may be only an account.
2190 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2191 return EPWMD_COMMAND_SYNTAX;
2194 n = find_account(client->doc, &path, &error, NULL, 0);
2196 if (!n)
2197 goto fail;
2199 if (path[1]) {
2200 n = find_elements(client->doc, n->children, path+1, &error,
2201 NULL, NULL, NULL, FALSE, 0, NULL);
2203 if (!n)
2204 goto fail;
2207 g_strfreev(path);
2208 return add_attribute(n, req[0], req[2]);
2210 fail:
2211 g_strfreev(path);
2212 return error;
2216 * req[0] - command
2217 * req[1] - attribute name or element path if command is LIST
2218 * req[2] - element path
2219 * req[2] - element path or value
2221 static int attr_command(assuan_context_t ctx, char *line)
2223 struct client_s *client = assuan_get_pointer(ctx);
2224 gchar **req = split_input_line(line, " ", 4);
2225 gpg_error_t error = 0;
2227 error = file_modified(client);
2229 if (error) {
2230 log_write("%s: %s", client->filename ? client->filename : "",
2231 pwmd_strerror(error));
2232 g_strfreev(req);
2233 return send_error(ctx, error);
2236 if (!req || !req[0] || !req[1]) {
2237 g_strfreev(req);
2238 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2241 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2242 error = attribute_set(client, req+1);
2243 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2244 error = attribute_get(ctx, req+1);
2245 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2246 error = attribute_delete(client, req+1);
2247 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2248 error = attribute_list(ctx, req+1);
2249 else
2250 error = EPWMD_COMMAND_SYNTAX;
2252 g_strfreev(req);
2253 return send_error(ctx, error);
2256 static int iscached_command(assuan_context_t ctx, char *line)
2258 gchar **req = split_input_line(line, " ", 0);
2259 guchar md5file[16];
2261 if (!req || !*req) {
2262 g_strfreev(req);
2263 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2266 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2267 g_strfreev(req);
2268 CACHE_LOCK(ctx);
2270 if (cache_iscached(md5file) == FALSE) {
2271 CACHE_UNLOCK;
2272 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2275 CACHE_UNLOCK;
2276 return send_error(ctx, 0);
2279 gpg_error_t send_cache_status(assuan_context_t ctx)
2281 gchar *tmp;
2282 struct client_s *client = assuan_get_pointer(ctx);
2284 CACHE_LOCK(client->ctx);
2285 tmp = print_fmt("%i %i",
2286 cache_file_count(),
2287 (cache_size / sizeof(file_cache_t)) - cache_file_count());
2288 CACHE_UNLOCK;
2290 return assuan_write_status(ctx, "CACHE", tmp);
2293 static int clearcache_command(assuan_context_t ctx, char *line)
2295 struct client_s *client = assuan_get_pointer(ctx);
2296 gchar **req = split_input_line(line, " ", 0);
2297 guchar md5file[16];
2299 CACHE_LOCK(ctx);
2301 if (!req || !*req) {
2302 g_strfreev(req);
2303 cache_clear(client->md5file, 2);
2304 CACHE_UNLOCK;
2305 return send_error(ctx, 0);
2308 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2309 g_strfreev(req);
2311 if (cache_clear(md5file, 1) == FALSE) {
2312 CACHE_UNLOCK;
2313 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2316 CACHE_UNLOCK;
2317 return send_error(ctx, 0);
2320 static int cachetimeout_command(assuan_context_t ctx, char *line)
2322 guchar md5file[16];
2323 glong timeout;
2324 gchar **req = split_input_line(line, " ", 0);
2325 gchar *p;
2326 struct client_s *client = assuan_get_pointer(ctx);
2328 if (!req || !*req || !req[1]) {
2329 g_strfreev(req);
2330 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2333 errno = 0;
2334 timeout = strtol(req[0], &p, 10);
2336 if (errno != 0 || *p != 0) {
2337 g_strfreev(req);
2338 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2341 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2342 g_strfreev(req);
2343 CACHE_LOCK(client->ctx);
2345 if (cache_set_timeout(md5file, timeout) == FALSE) {
2346 CACHE_UNLOCK;
2347 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2350 CACHE_UNLOCK;
2351 send_cache_status_all();
2352 return send_error(ctx, 0);
2355 static int dump_command(assuan_context_t ctx, char *line)
2357 xmlChar *xml;
2358 gssize len;
2359 struct client_s *client = assuan_get_pointer(ctx);
2360 gpg_error_t error;
2362 if (disable_list_and_dump == TRUE)
2363 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2365 error = file_modified(client);
2367 if (error) {
2368 log_write("%s: %s", client->filename ? client->filename : "",
2369 pwmd_strerror(error));
2370 return send_error(ctx, error);
2373 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2374 error = assuan_send_data(ctx, xml, len);
2375 xmlFree(xml);
2376 return send_error(ctx, error);
2379 static int getconfig_command(assuan_context_t ctx, gchar *line)
2381 struct client_s *client = assuan_get_pointer(ctx);
2382 gpg_error_t error = 0;
2383 gchar *p, *tmp;
2385 if (strcmp(line, "key") == 0 || strcmp(line, "key_file") == 0)
2386 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2388 p = get_key_file_string(client->filename ? client->filename : "default", line);
2390 if (!p)
2391 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2393 tmp = expand_homedir(p);
2394 g_free(p);
2395 p = tmp;
2396 error = assuan_send_data(ctx, p, strlen(p));
2397 g_free(p);
2398 return send_error(ctx, error);
2401 void cleanup_assuan(assuan_context_t ctx)
2403 struct client_s *cl = assuan_get_pointer(ctx);
2405 cleanup_client(cl);
2408 static void reset_notify(assuan_context_t ctx)
2410 struct client_s *cl = assuan_get_pointer(ctx);
2412 cleanup_client(cl);
2415 gpg_error_t register_commands(assuan_context_t ctx)
2417 static struct {
2418 const char *name;
2419 int (*handler)(assuan_context_t, char *line);
2420 } table[] = {
2421 { "OPEN", open_command },
2422 { "SAVE", save_command },
2423 { "LIST", list_command },
2424 { "REALPATH", realpath_command },
2425 { "STORE", store_command },
2426 { "DELETE", delete_command },
2427 { "GET", get_command },
2428 { "ATTR", attr_command },
2429 { "ISCACHED", iscached_command },
2430 { "CLEARCACHE", clearcache_command },
2431 { "CACHETIMEOUT", cachetimeout_command },
2432 { "GETCONFIG", getconfig_command },
2433 { "DUMP", dump_command },
2434 { "INPUT", NULL },
2435 { "OUTPUT", NULL },
2436 { NULL, NULL }
2438 int i, rc;
2440 for (i=0; table[i].name; i++) {
2441 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2443 if (rc)
2444 return rc;
2447 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2448 if (rc)
2449 return rc;
2451 return assuan_register_reset_notify(ctx, reset_notify);
2454 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2455 guchar *key)
2457 guchar *iv;
2458 void *inbuf;
2459 gsize insize, len;
2460 guchar tkey[gcrykeysize];
2461 struct file_header_s {
2462 guint iter;
2463 guchar iv[gcryblocksize];
2464 } file_header;
2465 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2466 gcry_cipher_hd_t gh;
2467 guint iter = 0, n_iter = 0;
2468 gint iter_progress;
2469 void *outbuf = NULL;
2470 gint zerror = 0;
2471 glong outsize = 0;
2472 gpg_error_t error;
2474 if (!ctx) {
2475 error = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2477 if (error)
2478 return error;
2480 else
2481 gh = client->gh;
2483 lseek(fd, 0, SEEK_SET);
2484 insize = st.st_size - sizeof(struct file_header_s);
2485 iv = gcry_malloc(gcryblocksize);
2487 if (!iv) {
2488 if (!ctx)
2489 gcry_cipher_close(gh);
2491 return gpg_error_from_errno(ENOMEM);
2494 len = pth_read(fd, &file_header, sizeof(struct file_header_s));
2496 if (len != sizeof(file_header)) {
2497 len = errno;
2499 if (!ctx)
2500 gcry_cipher_close(gh);
2502 gcry_free(iv);
2503 errno = len;
2504 return gpg_error_from_errno(errno);
2507 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2508 inbuf = gcry_malloc(insize);
2510 if (!inbuf) {
2511 if (!ctx)
2512 gcry_cipher_close(gh);
2514 gcry_free(iv);
2515 return gpg_error_from_errno(ENOMEM);
2518 len = pth_read(fd, inbuf, insize);
2520 if (len != insize) {
2521 len = errno;
2523 if (!ctx)
2524 gcry_cipher_close(gh);
2526 gcry_free(iv);
2527 errno = len;
2528 return gpg_error_from_errno(errno);
2531 memcpy(tkey, key, sizeof(tkey));
2532 tkey[0] ^= 1;
2534 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2535 if (!ctx) {
2536 gcry_cipher_close(gh);
2537 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2539 else
2540 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2542 gcry_free(inbuf);
2543 gcry_free(iv);
2544 return error;
2547 if ((error = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2548 if (!ctx) {
2549 gcry_cipher_close(gh);
2550 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2552 else
2553 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2555 gcry_free(inbuf);
2556 gcry_free(iv);
2558 if (!ctx)
2559 gcry_cipher_close(gh);
2561 return error;
2564 iter_progress = get_key_file_integer("default", "iteration_progress");
2566 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2567 error = assuan_write_status(client->ctx, "DECRYPT", "0");
2569 if (error) {
2570 gcry_free(inbuf);
2571 gcry_free(iv);
2572 return error;
2576 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2578 if (error) {
2579 gcry_free(inbuf);
2580 gcry_free(iv);
2581 return error;
2584 if ((error = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2585 if (!ctx) {
2586 gcry_cipher_close(gh);
2587 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2589 else
2590 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2592 gcry_free(inbuf);
2593 gcry_free(iv);
2594 return error;
2597 while (iter < file_header.iter) {
2598 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2599 if (!(iter % iter_progress)) {
2600 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2601 ++n_iter * iter_progress));
2603 if (error) {
2604 gcry_free(inbuf);
2605 gcry_free(iv);
2606 return error;
2611 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2612 if (!ctx) {
2613 gcry_cipher_close(gh);
2614 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2616 else
2617 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2619 gcry_free(inbuf);
2620 gcry_free(iv);
2621 return error;
2624 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2626 if (error) {
2627 if (!ctx) {
2628 gcry_cipher_close(gh);
2629 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2631 else
2632 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2634 gcry_free(inbuf);
2635 gcry_free(iv);
2636 return error;
2639 iter++;
2640 pth_yield(NULL);
2643 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2644 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2646 if (error) {
2647 gcry_free(inbuf);
2648 gcry_free(iv);
2649 return error;
2653 gcry_free(iv);
2655 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2657 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2659 if (zerror == Z_MEM_ERROR) {
2660 gcry_free(inbuf);
2661 return gpg_error_from_errno(ENOMEM);
2663 else if (zerror != Z_DATA_ERROR) {
2664 gcry_free(inbuf);
2666 if (!ctx)
2667 gcry_cipher_close(gh);
2669 return EPWMD_BADKEY;
2672 else {
2673 gcry_free(inbuf);
2674 inbuf = outbuf;
2675 insize = outsize;
2678 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2679 gcry_free(inbuf);
2681 if (!ctx)
2682 gcry_cipher_close(gh);
2684 return EPWMD_BADKEY;
2687 if (ctx) {
2688 client->xml = inbuf;
2689 client->len = insize;
2691 else {
2692 gcry_cipher_close(gh);
2693 gcry_free(inbuf);
2696 return 0;
2700 * This is called after every Assuan command.
2702 void command_finalize(assuan_context_t ctx, gint error)
2704 struct client_s *client = assuan_get_pointer(ctx);
2706 unlock_file_mutex(client);