Changed the PROGRESS status message to either ENCRYPT or DECRYPT.
[pwmd.git] / src / commands.c
blobf4d917b810e21686660171325d9c2a50761c8ac2
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 #ifdef HAVE_ZLIB_H
51 static void *z_alloc(void *data, unsigned items, unsigned size)
53 #ifndef MEM_DEBUG
54 return gcry_calloc(items, size);
55 #else
56 return calloc(items, size);
57 #endif
60 static void z_free(void *data, void *p)
62 #ifndef MEM_DEBUG
63 gcry_free(p);
64 #else
65 free(p);
66 #endif
68 #endif
70 static gpg_error_t file_modified(struct client_s *client)
72 struct stat st;
74 if (client->state != STATE_OPEN)
75 return EPWMD_NO_FILE;
77 if (stat(client->filename, &st) == 0 && client->mtime) {
78 if (client->mtime != st.st_mtime)
79 return EPWMD_FILE_MODIFIED;
82 return 0;
85 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
86 void *inbuf, gsize insize)
88 if ((gcryerrno = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
89 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
90 return FALSE;
93 return TRUE;
96 gboolean decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
97 void *inbuf, gsize insize)
99 if ((gcryerrno = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize))) {
100 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
101 return FALSE;
104 return TRUE;
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 static void cleanup(struct client_s *client)
151 assuan_context_t ctx = client->ctx;
154 * This may be a new file so don't use a cache slot. save_command() will
155 * set this to FALSE on success.
157 if (client->new == TRUE)
158 cache_clear(client->md5file, 1);
160 if (client->doc)
161 xmlFreeDoc(client->doc);
163 if (client->xml)
164 gcry_free(client->xml);
166 if (client->filename)
167 g_free(client->filename);
169 gcry_cipher_close(client->gh);
170 memset(client, 0, sizeof(struct client_s));
171 client->state = STATE_CONNECTED;
172 client->ctx = ctx;
173 client->freed = TRUE;
176 static gchar *print_fmt(const char *fmt, ...)
178 va_list ap;
179 static gchar buf[ASSUAN_LINELENGTH] = {0};
181 va_start(ap, fmt);
182 vsnprintf(buf, sizeof(buf), fmt, ap);
183 va_end(ap);
184 return buf;
187 #ifdef HAVE_ZLIB_H
188 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
189 gpointer *out, glong *outsize, gint *error)
191 z_stream z;
192 gint ret;
193 gpointer pout;
194 gint cmd = Z_NO_FLUSH;
195 gint total;
197 z.zalloc = z_alloc;
198 z.zfree = z_free;
199 z.next_in = in;
200 z.avail_in = total = insize < zlib_bufsize ? insize : zlib_bufsize;
201 z.avail_out = zlib_bufsize;
202 z.next_out = pout = g_malloc(zlib_bufsize);
204 if (!pout) {
205 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
206 *error = Z_MEM_ERROR;
207 return FALSE;
210 ret = inflateInit(&z);
212 if (ret != Z_OK) {
213 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
214 g_free(pout);
215 return FALSE;
218 do {
219 gpointer p;
221 ret = inflate(&z, cmd);
223 switch (ret) {
224 case Z_OK:
225 break;
226 case Z_BUF_ERROR:
227 if (!z.avail_out) {
228 p = g_realloc(pout, z.total_out + zlib_bufsize);
230 if (!p) {
231 ret = Z_MEM_ERROR;
232 goto fail;
235 pout = p;
236 z.next_out = pout + z.total_out;
237 z.avail_out = zlib_bufsize;
240 if (!z.avail_in && total < insize) {
241 if (total + zlib_bufsize > insize)
242 z.avail_in = insize - total;
243 else
244 z.avail_in = zlib_bufsize;
246 z.next_in = in + total;
248 if (ctx)
249 assuan_write_status(ctx, "DECOMPRESS",
250 print_fmt("%i %i", total, insize));
252 total += z.avail_in;
254 else if (!z.avail_in)
255 cmd = Z_FINISH;
257 break;
258 case Z_STREAM_END:
259 break;
260 default:
261 goto fail;
262 break;
264 } while (ret != Z_STREAM_END);
266 if (ctx)
267 assuan_write_status(ctx, "DECOMPRESS",
268 print_fmt("%i %i", total, insize));
270 *out = pout;
271 *outsize = z.total_out;
272 inflateEnd(&z);
273 return TRUE;
275 fail:
276 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
277 *error = ret;
278 g_free(pout);
279 inflateEnd(&z);
280 return FALSE;
282 #endif
284 static int open_command(assuan_context_t ctx, char *line)
286 gint fd;
287 struct stat st;
288 guchar shakey[gcrykeysize];
289 gint cached = 0;
290 gint timeout;
291 gpg_error_t error;
292 struct client_s *client = assuan_get_pointer(ctx);
293 gchar **req;
294 gchar *filename = NULL;
296 if ((req = split_input_line(line, " ", 2)) != NULL)
297 filename = req[0];
299 if (!filename || !*filename) {
300 g_strfreev(req);
301 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
304 if (valid_filename(filename) == FALSE) {
305 g_strfreev(req);
306 return send_error(ctx, EPWMD_INVALID_FILENAME);
309 if (client->state == STATE_OPEN)
310 cleanup(client);
312 client->freed = FALSE;
314 if ((gcryerrno = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
315 g_strfreev(req);
316 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
317 cleanup(client);
318 return send_error(ctx, gcryerrno);
321 if (stat(filename, &st) == 0) {
322 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
323 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
324 g_strfreev(req);
325 cleanup(client);
326 return send_error(ctx, EPWMD_INVALID_FILENAME);
329 client->mtime = st.st_mtime;
332 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
335 * New files don't need a key.
337 if (access(filename, R_OK|W_OK) != 0) {
338 if (errno != ENOENT) {
339 error = errno;
340 log_write("%s: %s", filename, strerror(errno));
341 g_strfreev(req);
342 cleanup(client);
343 return send_syserror(ctx, error);
345 new_doc:
346 if ((client->xml = new_document()) == NULL) {
347 error = errno;
348 log_write("%s", strerror(errno));
349 g_strfreev(req);
350 cleanup(client);
351 return send_syserror(ctx, error);
354 client->len = xmlStrlen(client->xml);
356 if (cache_has_file(client->md5file) == TRUE)
357 cache_clear(client->md5file, 1);
359 if (cache_add_file(client->md5file, NULL) == FALSE) {
360 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
361 g_strfreev(req);
362 cleanup(client);
363 return send_error(ctx, EPWMD_MAX_SLOTS);
366 client->new = TRUE;
367 client->filename = g_strdup(filename);
369 if (!client->filename) {
370 g_strfreev(req);
371 cleanup(client);
372 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
373 return send_syserror(ctx, ENOMEM);
376 if (req[1] && *req[1]) {
377 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
378 memset(req[1], 0, strlen(req[1]));
379 goto update_cache;
382 goto done;
385 if ((fd = open_file(filename, &st)) == -1) {
386 error = errno;
387 log_write("%s: %s", filename, strerror(errno));
388 g_strfreev(req);
389 cleanup(client);
390 return send_syserror(ctx, error);
393 if (st.st_size == 0)
394 goto new_doc;
396 if (cache_get_key(client->md5file, shakey) == TRUE)
397 cached = 1;
398 else {
400 * No key specified and no matching filename found in the cache.
402 if (!req[1] || !*req[1]) {
403 close(fd);
404 g_strfreev(req);
405 cleanup(client);
406 return send_error(ctx, EPWMD_KEY);
410 if (!cached) {
411 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
412 memset(req[1], 0, strlen(req[1]));
415 error = try_xml_decrypt(ctx, fd, st, shakey);
417 if (error) {
418 close(fd);
419 memset(shakey, 0, sizeof(shakey));
420 g_strfreev(req);
421 cleanup(client);
422 return send_error(ctx, error);
425 close(fd);
426 client->filename = g_strdup(filename);
428 if (!client->filename) {
429 memset(shakey, 0, sizeof(shakey));
430 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
431 g_strfreev(req);
432 cleanup(client);
433 return send_syserror(ctx, ENOMEM);
436 update_cache:
437 if (!cached) {
438 if (cache_update_key(client->md5file, shakey) == FALSE) {
439 memset(shakey, 0, sizeof(shakey));
440 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
441 g_strfreev(req);
442 cleanup(client);
443 return send_error(ctx, EPWMD_MAX_SLOTS);
446 timeout = get_key_file_integer(client->filename, "cache_timeout");
447 cache_reset_timeout(client->md5file, timeout);
449 else
450 cache_set_timeout(client->md5file, -2);
452 memset(shakey, 0, sizeof(shakey));
454 done:
455 g_strfreev(req);
456 error = parse_xml(ctx);
458 if (client->xml) {
459 gcry_free(client->xml);
460 client->xml = NULL;
463 if (error == 0) {
464 client->state = STATE_OPEN;
465 send_cache_status(ctx);
468 return send_error(ctx, error);
471 #ifdef HAVE_ZLIB_H
472 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
473 gint size, gpointer *out, glong *outsize, gint *error)
475 z_stream z;
476 gpointer pout, pin;
477 gint ret;
478 gint total = 0;
479 gint cmd = Z_NO_FLUSH;
481 z.zalloc = z_alloc;
482 z.zfree = z_free;
483 z.next_in = pin = data;
484 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
485 z.avail_out = zlib_bufsize;
486 z.next_out = pout = g_malloc(zlib_bufsize);
488 if (!pout) {
489 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
490 *error = Z_MEM_ERROR;
491 return FALSE;
494 ret = deflateInit(&z, level);
496 if (ret != Z_OK) {
497 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
498 *error = ret;
499 g_free(pout);
500 return FALSE;
503 do {
504 gpointer p;
506 ret = deflate(&z, cmd);
508 switch (ret) {
509 case Z_OK:
510 break;
511 case Z_BUF_ERROR:
512 if (!z.avail_out) {
513 p = g_realloc(pout, z.total_out + zlib_bufsize);
515 if (!p) {
516 ret = Z_MEM_ERROR;
517 goto fail;
520 pout = p;
521 z.next_out = pout + z.total_out;
522 z.avail_out = zlib_bufsize;
525 if (!z.avail_in) {
526 if (total >= size) {
527 cmd = Z_FINISH;
528 break;
531 if (total + zlib_bufsize > size) {
532 z.avail_in = size - total;
533 total += size - total;
535 else {
536 total += zlib_bufsize;
537 z.avail_in = zlib_bufsize;
540 pin += zlib_bufsize;
541 z.next_in = pin;
543 if (ctx)
544 assuan_write_status(ctx, "COMPRESS",
545 print_fmt("%i %i", total, size));
548 break;
549 case Z_STREAM_END:
550 break;
551 default:
552 goto fail;
554 } while (ret != Z_STREAM_END);
556 *out = pout;
557 *outsize = z.total_out;
558 deflateEnd(&z);
559 return TRUE;
561 fail:
562 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
563 *error = ret;
564 g_free(pout);
565 deflateEnd(&z);
566 return FALSE;
568 #endif
570 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
571 const gchar *filename, gpointer data, size_t insize,
572 const guchar *shakey, guint iter)
574 gsize len = insize;
575 gint fd;
576 gpointer inbuf;
577 guchar tkey[gcrykeysize];
578 gchar *p;
579 gint error;
580 guint iter_progress = 0, n_iter = 0;
581 struct file_header_s {
582 guint iter;
583 guchar iv[gcryblocksize];
584 } file_header;
586 if (insize / gcryblocksize) {
587 len = (insize / gcryblocksize) * gcryblocksize;
589 if (insize % gcryblocksize)
590 len += gcryblocksize;
594 * Resize the existing xml buffer to the block size required by gcrypt
595 * rather than duplicating it and wasting memory.
597 inbuf = gcry_realloc(data, len);
599 if (!inbuf)
600 return gpg_error_from_errno(ENOMEM);
602 insize = len;
603 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
604 memcpy(tkey, shakey, sizeof(tkey));
605 tkey[0] ^= 1;
607 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
608 gcry_free(inbuf);
609 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
610 return gcryerrno;
613 file_header.iter = iter;
615 if (client)
616 iter_progress = get_key_file_integer("default", "iteration_progress");
618 while (iter > 0) {
619 if (client && iter_progress > 0 && iter_progress <= iter) {
620 if (!(iter % iter_progress))
621 assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
622 ++n_iter * iter_progress));
625 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
626 sizeof(file_header.iv)))) {
627 gcry_free(inbuf);
628 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
629 return gcryerrno;
632 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
633 == FALSE) {
634 gcry_free(inbuf);
635 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
636 return gcryerrno;
639 iter--;
642 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
643 sizeof(file_header.iv)))) {
644 gcry_free(inbuf);
645 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
646 return gcryerrno;
649 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
650 gcry_free(inbuf);
651 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
652 return gcryerrno;
655 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
656 gcry_free(inbuf);
657 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
658 return gcryerrno;
661 if (client && iter_progress && file_header.iter >= iter_progress) {
662 if ((file_header.iter % iter_progress))
663 assuan_write_status(client->ctx, "ENCRYPT",
664 print_fmt("%i", file_header.iter));
667 if (filename) {
668 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
669 error = errno;
670 gcry_free(inbuf);
671 p = strrchr(filename, '/');
672 p++;
673 log_write("%s: %s", p, strerror(errno));
674 return gpg_error_from_errno(error);
677 else
679 * xml_import() from command line.
681 fd = STDOUT_FILENO;
683 write(fd, &file_header, sizeof(struct file_header_s));
684 write(fd, inbuf, insize);
686 if (filename)
687 close(fd);
689 gcry_free(inbuf);
690 return 0;
693 static int save_command(assuan_context_t ctx, char *line)
695 gpointer xmlbuf;
696 xmlChar *p;
697 gint len;
698 gint cached = 0;
699 guchar shakey[gcrykeysize];
700 gint iter;
701 struct stat st;
702 struct client_s *client = assuan_get_pointer(ctx);
703 gpg_error_t error;
704 gint timeout;
705 #ifdef HAVE_ZLIB_H
706 gpointer outbuf;
707 glong outsize = 0;
708 #endif
710 error = file_modified(client);
712 if (error) {
713 log_write("%s: %s", client->filename ? client->filename : "",
714 pwmd_strerror(error));
715 return send_error(ctx, error);
718 if (stat(client->filename, &st) == -1 && errno != ENOENT)
719 return send_syserror(ctx, errno);
721 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
722 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
723 return send_error(ctx, EPWMD_INVALID_FILENAME);
726 if (!line || !*line) {
727 if (cache_get_key(client->md5file, shakey) == FALSE)
728 return send_error(ctx, EPWMD_KEY);
730 cached = 1;
732 else {
733 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
734 memset(line, 0, strlen(line));
737 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
738 xmlbuf = p;
740 #ifdef HAVE_ZLIB_H
741 iter = get_key_file_integer(client->filename, "compression_level");
743 if (iter > 0) {
744 gint zerror;
746 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
747 memset(shakey, 0, sizeof(shakey));
748 xmlFree(xmlbuf);
750 if (zerror == Z_MEM_ERROR)
751 return send_syserror(ctx, ENOMEM);
752 else
753 return send_error(ctx, GPG_ERR_COMPR_ALGO);
755 else {
756 gcry_free(xmlbuf);
757 xmlbuf = outbuf;
758 len = outsize;
761 #endif
763 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
764 iter = 0;
766 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
768 if (error) {
769 memset(shakey, 0, sizeof(shakey));
770 return send_error(ctx, error);
773 stat(client->filename, &st);
774 client->mtime = st.st_mtime;
775 timeout = get_key_file_integer(client->filename, "cache_timeout");
777 if (cached) {
778 memset(shakey, 0, sizeof(shakey));
779 cache_reset_timeout(client->md5file, timeout);
780 client->new = FALSE;
781 return 0;
784 if (cache_update_key(client->md5file, shakey) == FALSE) {
785 memset(shakey, 0, sizeof(shakey));
786 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
787 return send_error(ctx, EPWMD_MAX_SLOTS);
790 client->new = FALSE;
791 memset(shakey, 0, sizeof(shakey));
792 cache_reset_timeout(client->md5file, timeout);
793 return 0;
796 static gboolean contains_whitespace(const gchar *str)
798 const gchar *p = str;
799 gunichar c;
800 glong len;
802 len = g_utf8_strlen(p++, -1) -1;
804 while (len--) {
805 c = g_utf8_get_char(p++);
807 if (g_unichar_isspace(c))
808 return TRUE;
811 return FALSE;
814 static int delete_command(assuan_context_t ctx, char *line)
816 struct client_s *client = assuan_get_pointer(ctx);
817 gchar **req;
818 gpg_error_t error;
819 xmlNodePtr n;
821 error = file_modified(client);
823 if (error) {
824 log_write("%s: %s", client->filename ? client->filename : "",
825 pwmd_strerror(error));
826 return send_error(ctx, error);
829 if (strchr(line, '\t'))
830 req = split_input_line(line, "\t", -1);
831 else
832 req = split_input_line(line, " ", -1);
834 if (!req || !*req)
835 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
837 n = find_account(client->doc, &req, &error, NULL);
839 if (!n) {
840 g_strfreev(req);
841 return send_error(ctx, error);
845 * No sub-node defined. Remove the entire node (account).
847 if (!req[1]) {
848 if (n) {
849 xmlUnlinkNode(n);
850 xmlFreeNode(n);
853 g_strfreev(req);
854 return 0;
857 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
858 g_strfreev(req);
860 if (!n)
861 return send_error(ctx, error);
863 if (n) {
864 xmlUnlinkNode(n);
865 xmlFreeNode(n);
868 return 0;
871 static int store_command(assuan_context_t ctx, char *line)
873 struct client_s *client = assuan_get_pointer(ctx);
874 gchar **req;
875 gpg_error_t error;
876 guchar *result;
877 gsize len;
878 xmlNodePtr n;
880 error = file_modified(client);
882 if (error) {
883 log_write("%s: %s", client->filename ? client->filename : "",
884 pwmd_strerror(error));
885 return send_error(ctx, error);
888 error = assuan_inquire(ctx, "STORE", &result, &len, 0);
890 if (error)
891 return send_error(ctx, error);
893 req = split_input_line((gchar *)result, "\t", 0);
894 #ifndef MEM_DEBUG
895 xfree(result);
896 #else
897 free(result);
898 #endif
900 if (!req || !*req)
901 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
903 again:
904 n = find_account(client->doc, &req, &error, NULL);
906 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
907 if (contains_whitespace(*req) == TRUE) {
908 g_strfreev(req);
909 return send_error(ctx, EPWMD_INVALID_ELEMENT);
912 error = new_account(client->doc, *req);
914 if (error) {
915 g_strfreev(req);
916 return send_error(ctx, error);
919 goto again;
922 if (!n) {
923 g_strfreev(req);
924 return send_error(ctx, error);
927 if (req[1]) {
928 if (!n->children)
929 create_elements_cb(n, req+1, &error, NULL);
930 else
931 find_elements(client->doc, n->children, req+1, &error,
932 NULL, NULL, create_elements_cb, NULL);
935 g_strfreev(req);
936 return send_error(ctx, error);
939 static int get_command(assuan_context_t ctx, char *line)
941 struct client_s *client = assuan_get_pointer(ctx);
942 gchar **req;
943 gpg_error_t error;
944 xmlNodePtr n;
946 error = file_modified(client);
948 if (error) {
949 log_write("%s: %s", client->filename ? client->filename : "",
950 pwmd_strerror(error));
951 return send_error(ctx, error);
954 req = split_input_line(line, "\t", -1);
956 if (!req || !*req) {
957 g_strfreev(req);
958 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
961 n = find_account(client->doc, &req, &error, NULL);
963 if (!n) {
964 g_strfreev(req);
965 return send_error(ctx, error);
968 if (req[1])
969 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
971 g_strfreev(req);
973 if (error)
974 return send_error(ctx, error);
976 if (!n || !n->children)
977 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
979 n = n->children;
981 if (!n || !n->content || !*n->content)
982 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
984 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
985 return send_error(ctx, error);
988 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
990 xmlChar *p = path;
991 gint n;
992 gchar *buf;
994 if (!p)
995 return NULL;
997 for (n = 0; *p && n < 3; p++) {
998 if (*p == '/')
999 n++;
1002 if (strstr((gchar *)p, "text()") != NULL)
1003 p[xmlStrlen(p) - 7] = 0;
1005 for (n = 0; p[n]; n++) {
1006 if (p[n] == '/')
1007 p[n] = '\t';
1010 buf = g_strdup_printf("%s\t%s", account, p);
1011 return buf;
1014 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1016 gchar **a;
1017 va_list ap;
1018 gchar *buf;
1019 gint len = *array ? g_strv_length(*array) : 0;
1021 if (!fmt)
1022 return FALSE;
1024 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1025 return FALSE;
1027 va_start(ap, fmt);
1028 g_vasprintf(&buf, fmt, ap);
1029 va_end(ap);
1031 a[len++] = buf;
1032 a[len] = NULL;
1033 *array = a;
1034 return TRUE;
1037 struct realpath_s {
1038 gchar *account;
1041 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1042 gpg_error_t *error, void *data)
1044 struct realpath_s *rp = data;
1046 if (rp->account)
1047 g_free(rp->account);
1049 rp->account = g_strdup(req[0]);
1051 if (!rp->account) {
1052 *error = gpg_error_from_errno(ENOMEM);
1053 return NULL;
1056 return node;
1059 static int realpath_command(assuan_context_t ctx, char *line)
1061 gpg_error_t error;
1062 struct client_s *client = assuan_get_pointer(ctx);
1063 xmlChar *p;
1064 gchar **req;
1065 gchar *result, *t;
1066 gint i;
1067 xmlNodePtr n;
1068 struct realpath_s *rp;
1069 GString *string;
1071 error = file_modified(client);
1073 if (error) {
1074 log_write("%s: %s", client->filename ? client->filename : "",
1075 pwmd_strerror(error));
1076 return send_error(ctx, error);
1079 if (strchr(line, '\t') != NULL) {
1080 if ((req = split_input_line(line, "\t", 0)) == NULL)
1081 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1083 else {
1084 if ((req = split_input_line(line, " ", 0)) == NULL)
1085 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1088 n = find_account(client->doc, &req, &error, NULL);
1090 if (!n) {
1091 g_strfreev(req);
1092 return send_error(ctx, error);
1095 rp = g_malloc(sizeof(struct realpath_s));
1097 if (!rp) {
1098 g_strfreev(req);
1099 return send_syserror(ctx, ENOMEM);
1102 rp->account = g_strdup(req[0]);
1104 if (!rp->account) {
1105 g_strfreev(req);
1106 return send_syserror(ctx, ENOMEM);
1109 if (req[1]) {
1110 n = find_elements(client->doc, n->children, req+1, &error,
1111 NULL, realpath_elements_cb, NULL, rp);
1113 if (!n) {
1114 g_free(rp->account);
1115 g_free(rp);
1116 g_strfreev(req);
1117 return send_error(ctx, error);
1121 p = xmlGetNodePath(n);
1122 result = element_path_to_req(rp->account, p);
1124 if (!result) {
1125 g_free(result);
1126 g_free(rp->account);
1127 g_free(rp);
1128 g_strfreev(req);
1129 xmlFree(p);
1130 return send_syserror(ctx, ENOMEM);
1133 string = g_string_new(result);
1134 g_free(result);
1135 g_free(rp->account);
1136 g_free(rp);
1137 g_strfreev(req);
1138 xmlFree(p);
1139 i = 0;
1141 again:
1142 for (t = string->str + i; *t; t++, i++) {
1143 if (!i || *t == '\t') {
1144 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1145 goto again;
1149 error = assuan_send_data(ctx, string->str, string->len);
1150 g_string_free(string, TRUE);
1151 return send_error(ctx, error);
1154 struct list_element_s {
1155 GSList *list;
1156 gchar **elements;
1159 static gboolean append_to_element_list(struct list_element_s *elements)
1161 gchar *tmp;
1162 gint i, total;
1163 gchar *a;
1164 GSList *list;
1166 if (!elements || !elements->elements)
1167 return TRUE;
1169 tmp = g_strjoinv("\t", elements->elements);
1171 if (!tmp)
1172 return FALSE;
1174 g_strfreev(elements->elements);
1175 elements->elements = NULL;
1176 total = g_slist_length(elements->list);
1177 a = g_utf8_collate_key(tmp, -1);
1179 if (!a) {
1180 g_free(tmp);
1181 return FALSE;
1185 * Removes duplicate element paths from the list. This is needed when
1186 * appending an element tree from list_command(). The glib docs recommend
1187 * using g_utf8_collate_key() for a large number of strings.
1189 for (i = 0; i < total; i++) {
1190 gchar *p = g_slist_nth_data(elements->list, i);
1191 gchar *b = g_utf8_collate_key(p, -1);
1193 if (!b) {
1194 g_free(a);
1195 g_free(tmp);
1196 return FALSE;
1199 if (strcmp(a, b) == 0) {
1200 g_free(a);
1201 g_free(b);
1202 g_free(tmp);
1203 return TRUE;
1206 g_free(b);
1209 g_free(a);
1210 list = g_slist_append(elements->list, tmp);
1212 if (!list)
1213 return FALSE;
1215 elements->list = list;
1216 return TRUE;
1219 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1220 struct list_element_s *elements, gchar *prefix)
1222 xmlNodePtr n;
1223 gpg_error_t error;
1225 if (append_to_element_list(elements) == FALSE)
1226 return gpg_error_from_errno(ENOMEM);
1228 for (n = node; n; n = n->next) {
1229 if (n->type == XML_ELEMENT_NODE) {
1230 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1231 gchar *tmp;
1233 if (content) {
1234 strv_printf(&elements->elements, "%s\t%s", prefix, n->name);
1236 if (append_to_element_list(elements) == FALSE)
1237 return gpg_error_from_errno(ENOMEM);
1240 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1242 if (!tmp)
1243 return gpg_error_from_errno(ENOMEM);
1245 strv_printf(&elements->elements, "%s", tmp);
1247 if (n->children) {
1248 error = do_list_recurse(doc, n->children, elements, tmp);
1249 g_free(tmp);
1251 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1252 return error;
1254 else
1255 g_free(tmp);
1257 if (append_to_element_list(elements) == FALSE)
1258 return gpg_error_from_errno(ENOMEM);
1262 return 0;
1265 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1266 struct list_element_s *elements, char *line)
1268 gchar *prefix = NULL, *account;
1269 gchar **req = NULL, **oreq = NULL, *tmp;
1270 xmlNodePtr n;
1271 gboolean account_is_literal, account_has_target = FALSE;
1272 gint which = 0;
1273 gchar **p;
1274 gpg_error_t error;
1276 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1277 if ((req = split_input_line(line, " ", 0)) == NULL)
1278 return EPWMD_COMMAND_SYNTAX;
1281 prefix = g_strdup(*req);
1283 if (!prefix) {
1284 g_strfreev(req);
1285 return gpg_error_from_errno(ENOMEM);
1288 account = g_strdup(*req);
1290 if (!account) {
1291 g_free(prefix);
1292 g_strfreev(req);
1293 return gpg_error_from_errno(ENOMEM);
1296 oreq = g_strdupv(req);
1298 if (!oreq) {
1299 g_free(prefix);
1300 g_free(account);
1301 g_strfreev(req);
1302 return gpg_error_from_errno(ENOMEM);
1305 p = req;
1306 again:
1307 account_has_target = FALSE;
1308 account_is_literal = is_literal_element_str(prefix);
1309 n = find_account(doc, &p, &error, &account_has_target);
1311 if (which)
1312 oreq = p;
1313 else
1314 req = p;
1316 if (!n)
1317 goto fail;
1319 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1320 tmp = g_strdup_printf("!%s", prefix);
1322 if (!tmp) {
1323 error = gpg_error_from_errno(ENOMEM);
1324 goto fail;
1327 g_free(prefix);
1328 prefix = tmp;
1331 if (*(p+1)) {
1332 gchar *t;
1334 n = find_elements(doc, n->children, p+1, &error,
1335 NULL, NULL, NULL, NULL);
1337 if (error)
1338 goto fail;
1340 tmp = g_strjoinv("\t", p+1);
1341 if (!tmp) {
1342 error = gpg_error_from_errno(ENOMEM);
1343 goto fail;
1346 t = g_strdup_printf("%s\t%s", prefix, tmp);
1347 if (!t) {
1348 error = gpg_error_from_errno(ENOMEM);
1349 goto fail;
1352 g_free(prefix);
1353 prefix = t;
1354 g_free(tmp);
1357 strv_printf(&elements->elements, "%s", prefix);
1359 if (node_has_child_element(n->children) == FALSE) {
1360 if (append_to_element_list(elements) == FALSE) {
1361 error = gpg_error_from_errno(ENOMEM);
1362 goto fail;
1365 else
1366 error = do_list_recurse(doc, n->children, elements, prefix);
1368 if (error)
1369 goto fail;
1371 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1372 g_free(*oreq);
1373 *oreq = g_strdup_printf("!%s", account);
1375 if (!*oreq) {
1376 error = gpg_error_from_errno(ENOMEM);
1377 goto fail;
1380 p = oreq;
1381 g_free(prefix);
1382 prefix = g_strdup(*oreq);
1384 if (!prefix) {
1385 error = gpg_error_from_errno(ENOMEM);
1386 goto fail;
1389 goto again;
1392 fail:
1393 g_free(prefix);
1394 g_free(account);
1395 g_strfreev(req);
1397 if (oreq)
1398 g_strfreev(oreq);
1400 return error;
1404 * This could be faster especially when finding "target" attributes.
1406 static int list_command(assuan_context_t ctx, char *line)
1408 struct client_s *client = assuan_get_pointer(ctx);
1409 gpg_error_t error;
1410 struct list_element_s *elements = NULL;
1411 GString *string;
1412 gchar *tmp;
1413 gint i, total;
1415 if (disable_list_and_dump == TRUE)
1416 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1418 error = file_modified(client);
1420 if (error) {
1421 log_write("%s: %s", client->filename, pwmd_strerror(error));
1422 return send_error(ctx, error);
1425 if (!*line) {
1426 GString *str;
1428 error = list_accounts(client->doc, &str);
1430 if (error)
1431 return send_error(ctx, error);
1433 error = assuan_send_data(ctx, str->str, str->len);
1434 g_string_free(str, TRUE);
1435 return send_error(ctx, error);
1438 elements = g_malloc0(sizeof(struct list_element_s));
1440 if (!elements) {
1441 error = gpg_error_from_errno(ENOMEM);
1442 goto fail;
1445 error = do_list_command(ctx, client->doc, elements, line);
1447 if (error)
1448 goto fail;
1450 if (!error) {
1451 total = g_slist_length(elements->list);
1453 if (!total) {
1454 error = EPWMD_EMPTY_ELEMENT;
1455 goto fail;
1459 * Find element paths with a target and append those element trees to
1460 * the list.
1462 for (i = 0; i < total; i++) {
1463 gchar **req;
1465 tmp = g_slist_nth_data(elements->list, i);
1466 req = split_input_line(tmp, "\t", 0);
1468 if (!req) {
1469 if (g_str_has_prefix(tmp, "!") == TRUE) {
1470 g_strfreev(req);
1471 continue;
1474 else {
1475 gchar **p;
1477 for (p = req; *p; p++) {
1478 if (g_str_has_prefix(*p, "!") == FALSE)
1479 break;
1482 if (!*p) {
1483 g_strfreev(req);
1484 continue;
1488 g_strfreev(req);
1489 error = do_list_command(ctx, client->doc, elements, tmp);
1491 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1492 goto fail;
1494 total = g_slist_length(elements->list);
1498 string = g_string_new(NULL);
1500 for (i = 0; i < total; i++) {
1501 tmp = g_slist_nth_data(elements->list, i);
1502 g_string_append_printf(string, "%s\n", tmp);
1503 g_free(tmp);
1506 string = g_string_truncate(string, string->len - 1);
1507 error = assuan_send_data(ctx, string->str, string->len);
1508 g_string_free(string, TRUE);
1510 fail:
1511 if (elements) {
1512 if (elements->list)
1513 g_slist_free(elements->list);
1515 if (elements->elements)
1516 g_strfreev(elements->elements);
1518 g_free(elements);
1521 return send_error(ctx, error);
1524 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1525 const gchar *value)
1527 xmlAttrPtr a;
1529 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1530 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1532 if (!a)
1533 return EPWMD_LIBXML_ERROR;
1535 else
1536 xmlNodeSetContent(a->children, (xmlChar *)value);
1538 return 0;
1542 * req[0] - element path
1544 static int attribute_list(assuan_context_t ctx, gchar **req)
1546 struct client_s *client = assuan_get_pointer(ctx);
1547 gchar **attrlist = NULL;
1548 gint i = 0;
1549 gchar **path = NULL;
1550 xmlAttrPtr a;
1551 xmlNodePtr n, an;
1552 gchar *line;
1553 gpg_error_t error;
1555 if (!req || !req[0])
1556 return EPWMD_COMMAND_SYNTAX;
1558 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1560 * The first argument may be only an account.
1562 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1563 return EPWMD_COMMAND_SYNTAX;
1566 n = find_account(client->doc, &path, &error, NULL);
1568 if (!n) {
1569 g_strfreev(path);
1570 return error;
1573 if (path[1]) {
1574 n = find_elements(client->doc, n->children, path+1, &error,
1575 NULL, NULL, NULL, NULL);
1577 if (!n) {
1578 g_strfreev(path);
1579 return error;
1583 g_strfreev(path);
1585 for (a = n->properties; a; a = a->next) {
1586 gchar **pa;
1588 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1589 if (attrlist)
1590 g_strfreev(attrlist);
1592 error = errno;
1593 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1594 return gpg_error_from_errno(error);
1597 attrlist = pa;
1598 an = a->children;
1599 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1601 if (!attrlist[i]) {
1602 g_strfreev(attrlist);
1603 return gpg_error_from_errno(ENOMEM);
1606 attrlist[++i] = NULL;
1609 if (!attrlist)
1610 return EPWMD_EMPTY_ELEMENT;
1612 line = g_strjoinv("\n", attrlist);
1614 if (!line) {
1615 g_strfreev(attrlist);
1616 return gpg_error_from_errno(ENOMEM);
1619 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1620 g_free(line);
1621 g_strfreev(attrlist);
1622 return error;
1626 * req[0] - attribute
1627 * req[1] - element path
1629 static int attribute_delete(struct client_s *client, gchar **req)
1631 xmlAttrPtr a;
1632 xmlNodePtr n;
1633 gchar **path = NULL;
1634 gpg_error_t error;
1636 if (!req || !req[0] || !req[1])
1637 return EPWMD_COMMAND_SYNTAX;
1639 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1641 * The first argument may be only an account.
1643 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1644 return EPWMD_COMMAND_SYNTAX;
1648 * Don't remove the "name" attribute for the account element. To remove an
1649 * account use DELETE <account>.
1651 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1652 error = EPWMD_ATTR_SYNTAX;
1653 goto fail;
1656 n = find_account(client->doc, &path, &error, NULL);
1658 if (!n)
1659 goto fail;
1661 if (path[1]) {
1662 n = find_elements(client->doc, n->children, path+1, &error,
1663 NULL, NULL, NULL, NULL);
1665 if (!n)
1666 goto fail;
1669 g_strfreev(path);
1671 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1672 return EPWMD_ATTR_NOT_FOUND;
1674 if (xmlRemoveProp(a) == -1)
1675 return EPWMD_LIBXML_ERROR;
1677 return 0;
1679 fail:
1680 g_strfreev(path);
1681 return error;
1685 * Creates a "target" attribute. When other commands encounter an element with
1686 * this attribute, the element path is modified to the target value. If the
1687 * source element path doesn't exist when using 'ATTR SET target', it is
1688 * created, but the destination element path must exist.
1690 * req[0] - source element path
1691 * req[1] - destination element path
1693 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1695 gchar **src, **dst, *line;
1696 gpg_error_t error;
1697 xmlNodePtr n;
1699 if (!req || !req[0] || !req[1])
1700 return EPWMD_COMMAND_SYNTAX;
1702 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1704 * The first argument may be only an account.
1706 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1707 return EPWMD_COMMAND_SYNTAX;
1710 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1712 * The first argument may be only an account.
1714 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1715 error = EPWMD_COMMAND_SYNTAX;
1716 goto fail;
1720 n = find_account(client->doc, &dst, &error, NULL);
1723 * Make sure the destination element path exists.
1725 if (!n)
1726 goto fail;
1728 if (dst[1]) {
1729 n = find_elements(client->doc, n->children, dst+1, &error,
1730 NULL, NULL, NULL, NULL);
1732 if (!n)
1733 goto fail;
1736 again:
1737 n = find_account(client->doc, &src, &error, NULL);
1739 if (!n) {
1740 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1741 error = new_account(client->doc, src[0]);
1743 if (error)
1744 goto fail;
1746 goto again;
1748 else
1749 goto fail;
1752 if (src[1]) {
1753 if (!n->children)
1754 n = create_target_elements_cb(n, src+1, &error, NULL);
1755 else
1756 n = find_elements(client->doc, n->children, src+1, &error,
1757 NULL, NULL, create_target_elements_cb, NULL);
1759 if (!n)
1760 goto fail;
1763 * Reset the position of the element tree now that the elements
1764 * have been created.
1766 n = find_account(client->doc, &src, &error, NULL);
1768 if (!n)
1769 goto fail;
1771 n = find_elements(client->doc, n->children, src+1, &error,
1772 NULL, NULL, NULL, NULL);
1774 if (!n)
1775 goto fail;
1778 line = g_strjoinv("\t", dst);
1779 error = add_attribute(n, "target", line);
1781 if (error) {
1782 g_free(line);
1783 goto fail;
1786 g_free(line);
1787 g_strfreev(src);
1788 g_strfreev(dst);
1789 return 0;
1791 fail:
1792 g_strfreev(src);
1793 g_strfreev(dst);
1794 return error;
1798 * req[0] - account name
1799 * req[1] - new name
1801 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1803 gpg_error_t error;
1804 gchar **tmp;
1805 xmlNodePtr n;
1807 tmp = g_strdupv(req);
1809 if (!tmp)
1810 return gpg_error_from_errno(ENOMEM);
1812 n = find_account(client->doc, &tmp, &error, NULL);
1813 g_strfreev(tmp);
1815 if (!n)
1816 return error;
1818 if (g_utf8_collate(req[0], req[1]) == 0)
1819 return 0;
1822 * Will not overwrite an existing account.
1824 tmp = g_strdupv(req+1);
1826 if (!tmp)
1827 return gpg_error_from_errno(ENOMEM);
1829 n = find_account(client->doc, &tmp, &error, NULL);
1830 g_strfreev(tmp);
1832 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1833 return error;
1835 if (n)
1836 return EPWMD_ACCOUNT_EXISTS;
1839 * Whitespace not allowed in account names.
1841 if (contains_whitespace(req[1]) == TRUE)
1842 return EPWMD_ATTR_SYNTAX;
1844 tmp = g_strdupv(req);
1846 if (!tmp)
1847 return gpg_error_from_errno(ENOMEM);
1849 n = find_account(client->doc, &tmp, &error, NULL);
1850 g_strfreev(tmp);
1852 if (!n)
1853 return EPWMD_ELEMENT_NOT_FOUND;
1855 return add_attribute(n, "name", req[1]);
1859 * req[0] - attribute
1860 * req[1] - element path
1862 static int attribute_get(assuan_context_t ctx, gchar **req)
1864 struct client_s *client = assuan_get_pointer(ctx);
1865 xmlNodePtr n;
1866 xmlChar *a;
1867 gchar **path= NULL;
1868 gpg_error_t error;
1870 if (!req || !req[0] || !req[1])
1871 return EPWMD_COMMAND_SYNTAX;
1873 if (strchr(req[1], '\t')) {
1874 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1875 return EPWMD_COMMAND_SYNTAX;
1877 else {
1878 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1879 return EPWMD_COMMAND_SYNTAX;
1882 n = find_account(client->doc, &path, &error, NULL);
1884 if (!n)
1885 goto fail;
1887 if (path[1]) {
1888 n = find_elements(client->doc, n->children, path+1, &error,
1889 NULL, NULL, NULL, NULL);
1891 if (!n)
1892 goto fail;
1895 g_strfreev(path);
1897 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1898 return EPWMD_ATTR_NOT_FOUND;
1900 error = assuan_send_data(ctx, a, xmlStrlen(a));
1901 xmlFree(a);
1902 return error;
1904 fail:
1905 g_strfreev(path);
1906 return error;
1910 * req[0] - attribute
1911 * req[1] - element path
1912 * req[2] - value
1914 static int attribute_set(struct client_s *client, gchar **req)
1916 gchar **path = NULL;
1917 gpg_error_t error;
1918 xmlNodePtr n;
1920 if (!req || !req[0] || !req[1] || !req[2])
1921 return EPWMD_COMMAND_SYNTAX;
1924 * Reserved attribute names.
1926 if (g_utf8_collate(req[0], "name") == 0) {
1928 * Only reserved for the account element. Not the rest of the
1929 * document.
1931 if (strchr(req[1], '\t') == NULL)
1932 return name_attribute(client, req + 1);
1934 else if (g_utf8_collate(req[0], "target") == 0)
1935 return target_attribute(client, req + 1);
1937 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1939 * The first argument may be only an account.
1941 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1942 return EPWMD_COMMAND_SYNTAX;
1945 n = find_account(client->doc, &path, &error, NULL);
1947 if (!n)
1948 goto fail;
1950 if (path[1]) {
1951 n = find_elements(client->doc, n->children, path+1, &error,
1952 NULL, NULL, NULL, NULL);
1954 if (!n)
1955 goto fail;
1958 g_strfreev(path);
1959 return add_attribute(n, req[0], req[2]);
1961 fail:
1962 g_strfreev(path);
1963 return error;
1967 * req[0] - command
1968 * req[1] - attribute name or element path if command is LIST
1969 * req[2] - element path
1970 * req[2] - element path or value
1972 static int attr_command(assuan_context_t ctx, char *line)
1974 struct client_s *client = assuan_get_pointer(ctx);
1975 gchar **req = split_input_line(line, " ", 4);
1976 gpg_error_t error = 0;
1978 error = file_modified(client);
1980 if (error) {
1981 log_write("%s: %s", client->filename ? client->filename : "",
1982 pwmd_strerror(error));
1983 g_strfreev(req);
1984 return send_error(ctx, error);
1987 if (!req || !req[0] || !req[1]) {
1988 g_strfreev(req);
1989 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1992 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1993 error = attribute_set(client, req+1);
1994 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
1995 error = attribute_get(ctx, req+1);
1996 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1997 error = attribute_delete(client, req+1);
1998 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1999 error = attribute_list(ctx, req+1);
2000 else
2001 error = EPWMD_COMMAND_SYNTAX;
2003 g_strfreev(req);
2004 return send_error(ctx, error);
2007 static int iscached_command(assuan_context_t ctx, char *line)
2009 gchar **req = split_input_line(line, " ", 0);
2010 guchar md5file[16];
2012 if (!req || !*req) {
2013 g_strfreev(req);
2014 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2017 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2018 g_strfreev(req);
2020 if (cache_iscached(md5file) == FALSE)
2021 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2023 return 0;
2026 void send_cache_status(assuan_context_t ctx)
2028 char *tmp = print_fmt("%li %li",
2029 cache_file_count(), cache_size / sizeof(file_cache_t) - cache_file_count());
2031 if (ctx)
2032 assuan_write_status(ctx, "CACHE", tmp);
2035 static int clearcache_command(assuan_context_t ctx, char *line)
2037 struct client_s *client = assuan_get_pointer(ctx);
2038 gchar **req = split_input_line(line, " ", 0);
2039 guchar md5file[16];
2041 if (!req || !*req) {
2042 g_strfreev(req);
2043 cache_clear(client->md5file, 2);
2044 send_cache_status(ctx);
2045 return 0;
2048 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2049 g_strfreev(req);
2050 cache_clear(md5file, 1);
2051 send_cache_status(ctx);
2052 return 0;
2055 static int cachetimeout_command(assuan_context_t ctx, char *line)
2057 guchar md5file[16];
2058 glong timeout;
2059 gchar **req = split_input_line(line, " ", 0);
2060 gchar *p;
2062 if (!req || !*req || !req[1]) {
2063 g_strfreev(req);
2064 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2067 errno = 0;
2068 timeout = strtol(req[0], &p, 10);
2070 if (errno != 0 || *p != 0) {
2071 g_strfreev(req);
2072 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2075 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2076 g_strfreev(req);
2078 if (cache_set_timeout(md5file, timeout) == FALSE)
2079 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2081 send_cache_status(ctx);
2082 return 0;
2085 static int dump_command(assuan_context_t ctx, char *line)
2087 xmlChar *xml;
2088 gssize len;
2089 struct client_s *client = assuan_get_pointer(ctx);
2090 gpg_error_t error;
2092 if (disable_list_and_dump == TRUE)
2093 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2095 error = file_modified(client);
2097 if (error) {
2098 log_write("%s: %s", client->filename ? client->filename : "",
2099 pwmd_strerror(error));
2100 return send_error(ctx, error);
2103 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2104 error = assuan_send_data(ctx, xml, len);
2105 xmlFree(xml);
2106 return error;
2109 static int getconfig_command(assuan_context_t ctx, gchar *line)
2111 struct client_s *client = assuan_get_pointer(ctx);
2112 gpg_error_t error = 0;
2113 gchar *p, *tmp;
2115 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2116 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2118 p = get_key_file_string(client->filename ? client->filename : "default", line);
2120 if (!p)
2121 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2123 tmp = expand_homedir(p);
2124 g_free(p);
2125 p = tmp;
2126 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2127 g_free(p);
2128 return send_error(ctx, error);
2131 void cleanup_assuan(assuan_context_t ctx)
2133 struct client_s *cl = assuan_get_pointer(ctx);
2135 cleanup(cl);
2138 gpg_error_t register_commands(assuan_context_t ctx)
2140 static struct {
2141 const char *name;
2142 int (*handler)(assuan_context_t, char *line);
2143 } table[] = {
2144 { "OPEN", open_command },
2145 { "SAVE", save_command },
2146 { "LIST", list_command },
2147 { "REALPATH", realpath_command },
2148 { "STORE", store_command },
2149 { "DELETE", delete_command },
2150 { "GET", get_command },
2151 { "ATTR", attr_command },
2152 { "ISCACHED", iscached_command },
2153 { "CLEARCACHE", clearcache_command },
2154 { "CACHETIMEOUT", cachetimeout_command },
2155 { "GETCONFIG", getconfig_command },
2156 { "DUMP", dump_command },
2157 { "INPUT", NULL },
2158 { "OUTPUT", NULL },
2159 { NULL, NULL }
2161 int i, rc;
2163 for (i=0; table[i].name; i++) {
2164 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2166 if (rc)
2167 return rc;
2170 return assuan_register_bye_notify(ctx, cleanup_assuan);
2173 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2174 guchar *key)
2176 guchar *iv;
2177 void *inbuf;
2178 gsize insize;
2179 guchar tkey[gcrykeysize];
2180 struct file_header_s {
2181 guint iter;
2182 guchar iv[gcryblocksize];
2183 } file_header;
2184 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2185 gcry_cipher_hd_t gh;
2186 guint iter, n_iter = 0;
2187 gint iter_progress;
2188 #ifdef HAVE_ZLIB_H
2189 void *outbuf = NULL;
2190 gint zerror = 0;
2191 glong outsize = 0;
2192 #endif
2194 if (!ctx) {
2195 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2197 if (gcryerrno)
2198 return gcryerrno;
2200 else
2201 gh = client->gh;
2203 lseek(fd, 0, SEEK_SET);
2204 insize = st.st_size - sizeof(struct file_header_s);
2205 iv = gcry_malloc(gcryblocksize);
2207 if (!iv) {
2208 gcry_cipher_close(gh);
2209 return gpg_error_from_errno(ENOMEM);
2212 read(fd, &file_header, sizeof(struct file_header_s));
2213 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2214 inbuf = gcry_malloc(insize);
2216 if (!inbuf) {
2217 gcry_cipher_close(gh);
2218 gcry_free(iv);
2219 return gpg_error_from_errno(ENOMEM);
2222 read(fd, inbuf, insize);
2223 memcpy(tkey, key, sizeof(tkey));
2224 tkey[0] ^= 1;
2226 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2227 if (!ctx) {
2228 gcry_cipher_close(gh);
2229 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2231 else
2232 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2234 gcry_free(inbuf);
2235 gcry_free(iv);
2236 return gcryerrno;
2239 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2240 if (!ctx) {
2241 gcry_cipher_close(gh);
2242 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2244 else
2245 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2247 gcry_free(inbuf);
2248 gcry_free(iv);
2250 if (!ctx)
2251 gcry_cipher_close(gh);
2253 return gcryerrno;
2256 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2257 if (!ctx) {
2258 gcry_cipher_close(gh);
2259 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2261 else
2262 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2264 gcry_free(inbuf);
2265 gcry_free(iv);
2266 return gcryerrno;
2269 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2270 if (!ctx) {
2271 gcry_cipher_close(gh);
2272 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2274 else
2275 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2277 gcry_free(inbuf);
2278 gcry_free(iv);
2279 return gcryerrno;
2282 iter = file_header.iter;
2283 iter_progress = get_key_file_integer("default", "iteration_progress");
2285 while (iter > 0) {
2286 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2287 if (!(iter % iter_progress))
2288 assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2289 ++n_iter * iter_progress));
2292 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2293 if (!ctx) {
2294 gcry_cipher_close(gh);
2295 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2297 else
2298 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2300 gcry_free(inbuf);
2301 gcry_free(iv);
2302 return gcryerrno;
2305 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2306 if (!ctx) {
2307 gcry_cipher_close(gh);
2308 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2310 else
2311 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2313 gcry_free(inbuf);
2314 gcry_free(iv);
2315 return gcryerrno;
2318 iter--;
2321 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2322 if ((file_header.iter % iter_progress))
2323 assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2326 gcry_free(iv);
2328 #ifdef HAVE_ZLIB_H
2329 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2331 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2333 if (zerror == Z_MEM_ERROR) {
2334 gcry_free(inbuf);
2335 return gpg_error_from_errno(ENOMEM);
2337 else if (zerror != Z_DATA_ERROR) {
2338 gcry_free(inbuf);
2340 if (!ctx) {
2341 gcry_cipher_close(gh);
2342 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gpg_strerror(GPG_ERR_COMPR_ALGO));
2344 else
2345 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gpg_strerror(GPG_ERR_COMPR_ALGO));
2347 return GPG_ERR_COMPR_ALGO;
2350 else {
2351 gcry_free(inbuf);
2352 inbuf = outbuf;
2353 insize = outsize;
2355 #endif
2357 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2358 gcry_free(inbuf);
2360 if (!ctx)
2361 gcry_cipher_close(gh);
2363 return EPWMD_BADKEY;
2366 if (ctx) {
2367 client->xml = inbuf;
2368 client->len = insize;
2370 else {
2371 gcry_cipher_close(gh);
2372 gcry_free(inbuf);
2375 return 0;