After saving the temporary file and renaming it to the original,
[pwmd.git] / src / commands.c
blob6d96ff26c56f543c85b4390e20cb40ac722f228f
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 if ((gcryerrno = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
87 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
88 return FALSE;
91 return TRUE;
94 gboolean decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
95 void *inbuf, gsize insize)
97 if ((gcryerrno = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize))) {
98 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
99 return FALSE;
102 return TRUE;
105 static gpg_error_t parse_xml(assuan_context_t ctx)
107 struct client_s *client = assuan_get_pointer(ctx);
109 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
111 if (!client->doc)
112 return EPWMD_LIBXML_ERROR;
114 return 0;
117 gboolean valid_filename(const gchar *filename)
119 const gchar *p;
121 if (!filename || !*filename)
122 return FALSE;
124 for (p = filename; *p; p++) {
125 if (g_ascii_isalnum(*p) == FALSE && *p != '-' && *p != '_' && *p != '.')
126 return FALSE;
129 return TRUE;
132 gint open_file(const gchar *filename, struct stat *st)
134 gint fd;
136 if ((fd = open(filename, O_RDONLY)) == -1)
137 return -1;
139 if (stat(filename, st) == -1) {
140 close(fd);
141 return -1;
144 return fd;
147 static void cleanup_client(struct client_s *client)
149 assuan_context_t ctx = client->ctx;
152 * This may be a new file so don't use a cache slot. save_command() will
153 * set this to FALSE on success.
155 if (client->new == TRUE)
156 cache_clear(client->md5file, 1);
158 if (client->doc)
159 xmlFreeDoc(client->doc);
161 if (client->xml)
162 gcry_free(client->xml);
164 if (client->filename)
165 g_free(client->filename);
167 gcry_cipher_close(client->gh);
168 memset(client, 0, sizeof(struct client_s));
169 client->state = STATE_CONNECTED;
170 client->ctx = ctx;
171 client->freed = TRUE;
174 static gchar *print_fmt(const char *fmt, ...)
176 va_list ap;
177 static gchar buf[ASSUAN_LINELENGTH] = {0};
179 va_start(ap, fmt);
180 vsnprintf(buf, sizeof(buf), fmt, ap);
181 va_end(ap);
182 return buf;
185 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
186 gpointer *out, glong *outsize, gint *error)
188 z_stream z;
189 gint ret;
190 gpointer pout;
191 gz_header h;
192 gchar buf[17];
194 z.zalloc = z_alloc;
195 z.zfree = z_free;
196 z.next_in = in;
197 z.avail_in = insize;
198 z.avail_out = zlib_bufsize;
199 z.next_out = pout = g_malloc(zlib_bufsize);
201 if (!pout) {
202 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
203 *error = Z_MEM_ERROR;
204 return FALSE;
207 ret = inflateInit2(&z, 47);
209 if (ret != Z_OK) {
210 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
211 g_free(pout);
212 return FALSE;
215 memset(&h, 0, sizeof(gz_header));
216 h.comment = (guchar *)buf;
217 h.comm_max = sizeof(buf);
218 ret = inflateGetHeader(&z, &h);
220 if (ret != Z_OK) {
221 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
222 g_free(pout);
223 inflateEnd(&z);
224 return FALSE;
227 ret = inflate(&z, Z_BLOCK);
229 if (ret != Z_OK) {
230 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
231 g_free(pout);
232 inflateEnd(&z);
233 return FALSE;
236 if (h.comment)
237 insize = atoi((gchar *)h.comment);
239 do {
240 gpointer p;
242 ret = inflate(&z, Z_FINISH);
244 switch (ret) {
245 case Z_OK:
246 break;
247 case Z_BUF_ERROR:
248 if (!z.avail_out) {
249 p = g_realloc(pout, z.total_out + zlib_bufsize);
251 if (!p) {
252 ret = Z_MEM_ERROR;
253 goto fail;
256 pout = p;
257 z.next_out = pout + z.total_out;
258 z.avail_out = zlib_bufsize;
260 if (ctx)
261 assuan_write_status(ctx, "DECOMPRESS",
262 print_fmt("%i %i", z.total_out, insize));
264 break;
265 case Z_STREAM_END:
266 break;
267 default:
268 goto fail;
269 break;
271 } while (ret != Z_STREAM_END);
273 if (ctx)
274 assuan_write_status(ctx, "DECOMPRESS",
275 print_fmt("%i %i", z.total_out, insize));
277 *out = pout;
278 *outsize = z.total_out;
279 inflateEnd(&z);
280 return TRUE;
282 fail:
283 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
284 *error = ret;
285 g_free(pout);
286 inflateEnd(&z);
287 return FALSE;
290 static int open_command(assuan_context_t ctx, char *line)
292 gint fd;
293 struct stat st;
294 guchar shakey[gcrykeysize];
295 gint cached = 0;
296 gint timeout;
297 gpg_error_t error;
298 struct client_s *client = assuan_get_pointer(ctx);
299 gchar **req;
300 gchar *filename = NULL;
302 MUTEX_LOCK(ctx);
304 if ((req = split_input_line(line, " ", 2)) != NULL)
305 filename = req[0];
307 if (!filename || !*filename) {
308 g_strfreev(req);
309 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
312 if (valid_filename(filename) == FALSE) {
313 g_strfreev(req);
314 return send_error(ctx, EPWMD_INVALID_FILENAME);
317 if (client->state == STATE_OPEN)
318 cleanup_client(client);
320 client->freed = FALSE;
322 if ((gcryerrno = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
323 g_strfreev(req);
324 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
325 cleanup_client(client);
326 return send_error(ctx, gcryerrno);
329 if (stat(filename, &st) == 0) {
330 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
331 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
332 g_strfreev(req);
333 cleanup_client(client);
334 return send_error(ctx, EPWMD_INVALID_FILENAME);
337 client->mtime = st.st_mtime;
340 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
343 * New files don't need a key.
345 if (access(filename, R_OK|W_OK) != 0) {
346 if (errno != ENOENT) {
347 error = errno;
348 log_write("%s: %s", filename, strerror(errno));
349 g_strfreev(req);
350 cleanup_client(client);
351 return send_syserror(ctx, error);
353 new_doc:
354 if ((client->xml = new_document()) == NULL) {
355 log_write("%s", strerror(ENOMEM));
356 g_strfreev(req);
357 cleanup_client(client);
358 return send_syserror(ctx, ENOMEM);
361 client->len = xmlStrlen(client->xml);
363 if (cache_has_file(client->md5file) == TRUE)
364 cache_clear(client->md5file, 1);
366 if (cache_add_file(client->md5file, NULL) == FALSE) {
367 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
368 g_strfreev(req);
369 cleanup_client(client);
370 return send_error(ctx, EPWMD_MAX_SLOTS);
373 client->new = TRUE;
374 client->filename = g_strdup(filename);
376 if (!client->filename) {
377 g_strfreev(req);
378 cleanup_client(client);
379 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
380 return send_syserror(ctx, ENOMEM);
383 if (req[1] && *req[1]) {
384 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
385 memset(req[1], 0, strlen(req[1]));
386 goto update_cache;
389 goto done;
392 if ((fd = open_file(filename, &st)) == -1) {
393 error = errno;
394 log_write("%s: %s", filename, strerror(errno));
395 g_strfreev(req);
396 cleanup_client(client);
397 return send_syserror(ctx, error);
400 if (st.st_size == 0)
401 goto new_doc;
403 if (cache_get_key(client->md5file, shakey) == TRUE)
404 cached = 1;
405 else {
407 * No key specified and no matching filename found in the cache.
409 if (!req[1] || !*req[1]) {
410 close(fd);
411 g_strfreev(req);
412 cleanup_client(client);
413 return send_error(ctx, EPWMD_KEY);
417 if (!cached) {
418 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
419 memset(req[1], 0, strlen(req[1]));
422 error = try_xml_decrypt(ctx, fd, st, shakey);
424 if (error) {
425 close(fd);
426 memset(shakey, 0, sizeof(shakey));
427 g_strfreev(req);
428 cleanup_client(client);
429 return send_error(ctx, error);
432 close(fd);
433 client->filename = g_strdup(filename);
435 if (!client->filename) {
436 memset(shakey, 0, sizeof(shakey));
437 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
438 g_strfreev(req);
439 cleanup_client(client);
440 return send_syserror(ctx, ENOMEM);
443 update_cache:
444 if (!cached) {
445 if (cache_update_key(client->md5file, shakey) == FALSE) {
446 memset(shakey, 0, sizeof(shakey));
447 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
448 g_strfreev(req);
449 cleanup_client(client);
450 return send_error(ctx, EPWMD_MAX_SLOTS);
453 timeout = get_key_file_integer(client->filename, "cache_timeout");
454 cache_reset_timeout(client->md5file, timeout);
456 else
457 cache_set_timeout(client->md5file, -2);
459 memset(shakey, 0, sizeof(shakey));
461 done:
462 g_strfreev(req);
463 error = parse_xml(ctx);
465 if (client->xml) {
466 gcry_free(client->xml);
467 client->xml = NULL;
470 if (error == 0) {
471 client->state = STATE_OPEN;
472 send_cache_status(ctx);
475 return send_error(ctx, error);
478 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
479 gint size, gpointer *out, glong *outsize, gint *error)
481 z_stream z;
482 gpointer pout, pin;
483 gint ret;
484 gz_header h;
485 gchar buf[17];
486 gint cmd = Z_NO_FLUSH;
488 z.zalloc = z_alloc;
489 z.zfree = z_free;
490 z.next_in = pin = data;
491 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
492 z.avail_out = zlib_bufsize;
493 z.next_out = pout = g_malloc(zlib_bufsize);
495 if (!pout) {
496 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
497 *error = Z_MEM_ERROR;
498 return FALSE;
501 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
503 if (ret != Z_OK) {
504 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
505 *error = ret;
506 g_free(pout);
507 return FALSE;
510 memset(&h, 0, sizeof(gz_header));
511 snprintf(buf, sizeof(buf), "%i", size);
512 h.comment = (guchar *)buf;
513 ret = deflateSetHeader(&z, &h);
515 if (ret != Z_OK) {
516 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
517 *error = ret;
518 g_free(pout);
519 deflateEnd(&z);
520 return FALSE;
523 do {
524 gpointer p;
526 ret = deflate(&z, cmd);
528 switch (ret) {
529 case Z_OK:
530 break;
531 case Z_BUF_ERROR:
532 if (!z.avail_out) {
533 p = g_realloc(pout, z.total_out + zlib_bufsize);
535 if (!p) {
536 ret = Z_MEM_ERROR;
537 goto fail;
540 pout = p;
541 z.next_out = pout + z.total_out;
542 z.avail_out = zlib_bufsize;
545 if (!z.avail_in && z.total_in < size) {
546 if (z.total_in + zlib_bufsize > size)
547 z.avail_in = size - z.total_in;
548 else
549 z.avail_in = zlib_bufsize;
551 if (ctx)
552 assuan_write_status(ctx, "COMPRESS",
553 print_fmt("%i %i", z.total_in, size));
556 if (z.total_in >= size)
557 cmd = Z_FINISH;
559 break;
560 case Z_STREAM_END:
561 break;
562 default:
563 goto fail;
565 } while (ret != Z_STREAM_END);
567 if (ctx)
568 assuan_write_status(ctx, "COMPRESS",
569 print_fmt("%i %i", z.total_in, size));
571 *out = pout;
572 *outsize = z.total_out;
573 deflateEnd(&z);
574 return TRUE;
576 fail:
577 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
578 *error = ret;
579 g_free(pout);
580 deflateEnd(&z);
581 return FALSE;
584 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
585 const gchar *filename, gpointer data, size_t insize,
586 const guchar *shakey, guint iter)
588 gsize len = insize;
589 gint fd;
590 gpointer inbuf;
591 guchar tkey[gcrykeysize];
592 gchar *p;
593 gint error;
594 guint iter_progress = 0, n_iter = 0, xiter = 0;
595 gchar tmp[FILENAME_MAX];
596 struct file_header_s {
597 guint iter;
598 guchar iv[gcryblocksize];
599 } file_header;
601 if (insize / gcryblocksize) {
602 len = (insize / gcryblocksize) * gcryblocksize;
604 if (insize % gcryblocksize)
605 len += gcryblocksize;
609 * Resize the existing xml buffer to the block size required by gcrypt
610 * rather than duplicating it and wasting memory.
612 inbuf = gcry_realloc(data, len);
614 if (!inbuf)
615 return gpg_error_from_errno(ENOMEM);
617 insize = len;
618 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
619 memcpy(tkey, shakey, sizeof(tkey));
620 tkey[0] ^= 1;
622 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
623 gcry_free(inbuf);
624 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
625 return gcryerrno;
628 file_header.iter = iter;
630 if (client)
631 iter_progress = get_key_file_integer("default", "iteration_progress");
633 if (client && iter_progress && file_header.iter >= iter_progress)
634 assuan_write_status(client->ctx, "ENCRYPT", "0");
636 while (xiter < file_header.iter) {
637 if (client && iter_progress > 0 && xiter >= iter_progress) {
638 if (!(xiter % iter_progress))
639 assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
640 ++n_iter * iter_progress));
643 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
644 sizeof(file_header.iv)))) {
645 gcry_free(inbuf);
646 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
647 return gcryerrno;
650 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
651 == FALSE) {
652 gcry_free(inbuf);
653 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
654 return gcryerrno;
657 xiter++;
660 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
661 sizeof(file_header.iv)))) {
662 gcry_free(inbuf);
663 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
664 return gcryerrno;
667 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
668 gcry_free(inbuf);
669 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
670 return gcryerrno;
673 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
674 gcry_free(inbuf);
675 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
676 return gcryerrno;
679 if (client && iter_progress && file_header.iter >= iter_progress)
680 assuan_write_status(client->ctx, "ENCRYPT",
681 print_fmt("%i", file_header.iter));
683 if (filename) {
684 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
686 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
687 error = errno;
688 gcry_free(inbuf);
689 p = strrchr(tmp, '/');
690 p++;
691 log_write("%s: %s", p, strerror(errno));
692 return gpg_error_from_errno(error);
695 else
697 * xml_import() from command line.
699 fd = STDOUT_FILENO;
701 len = write(fd, &file_header, sizeof(struct file_header_s));
703 if (len != sizeof(file_header)) {
704 len = errno;
706 if (filename)
707 close(fd);
709 gcry_free(inbuf);
710 return gpg_error_from_errno(len);
713 len = write(fd, inbuf, insize);
715 if (len != insize) {
716 len = errno;
718 if (filename)
719 close(fd);
721 gcry_free(inbuf);
722 return gpg_error_from_errno(len);
725 if (fsync(fd) == -1) {
726 len = errno;
727 close(fd);
728 gcry_free(inbuf);
729 return gpg_error_from_errno(len);
732 if (filename) {
733 struct stat st;
734 mode_t mode = 0;
736 close(fd);
738 if (stat(filename, &st) == 0)
739 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
741 if (rename(tmp, filename) == -1) {
742 len = errno;
743 gcry_free(inbuf);
744 return gpg_error_from_errno(len);
747 if (mode)
748 chmod(filename, mode);
751 gcry_free(inbuf);
752 return 0;
755 static int save_command(assuan_context_t ctx, char *line)
757 gpointer xmlbuf;
758 xmlChar *p;
759 gint len;
760 gint cached = 0;
761 guchar shakey[gcrykeysize];
762 gint iter;
763 struct stat st;
764 struct client_s *client = assuan_get_pointer(ctx);
765 gpg_error_t error;
766 gint timeout;
767 gpointer outbuf;
768 glong outsize = 0;
769 gint zerror;
771 MUTEX_LOCK(ctx);
772 error = file_modified(client);
774 if (error) {
775 log_write("%s: %s", client->filename ? client->filename : "",
776 pwmd_strerror(error));
777 return send_error(ctx, error);
780 if (stat(client->filename, &st) == -1 && errno != ENOENT)
781 return send_syserror(ctx, errno);
783 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
784 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
785 return send_error(ctx, EPWMD_INVALID_FILENAME);
788 if (!line || !*line) {
789 if (cache_get_key(client->md5file, shakey) == FALSE)
790 return send_error(ctx, EPWMD_KEY);
792 cached = 1;
794 else {
795 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
796 memset(line, 0, strlen(line));
799 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
800 xmlbuf = p;
802 iter = get_key_file_integer(client->filename, "compression_level");
804 if (iter < 0)
805 iter = 0;
807 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
808 memset(shakey, 0, sizeof(shakey));
809 xmlFree(xmlbuf);
811 if (zerror == Z_MEM_ERROR) {
812 return send_syserror(ctx, ENOMEM);
814 else
815 return send_error(ctx, GPG_ERR_COMPR_ALGO);
817 else {
818 gcry_free(xmlbuf);
819 xmlbuf = outbuf;
820 len = outsize;
823 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
824 iter = 0;
826 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
828 if (error) {
829 memset(shakey, 0, sizeof(shakey));
830 return send_error(ctx, error);
833 stat(client->filename, &st);
834 client->mtime = st.st_mtime;
835 timeout = get_key_file_integer(client->filename, "cache_timeout");
837 if (cached) {
838 memset(shakey, 0, sizeof(shakey));
839 cache_reset_timeout(client->md5file, timeout);
840 client->new = FALSE;
841 return 0;
844 if (cache_update_key(client->md5file, shakey) == FALSE) {
845 memset(shakey, 0, sizeof(shakey));
846 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
847 return send_error(ctx, EPWMD_MAX_SLOTS);
850 client->new = FALSE;
851 memset(shakey, 0, sizeof(shakey));
852 cache_reset_timeout(client->md5file, timeout);
853 return 0;
856 static gboolean contains_whitespace(const gchar *str)
858 const gchar *p = str;
859 gunichar c;
860 glong len;
862 len = g_utf8_strlen(p++, -1) -1;
864 while (len--) {
865 c = g_utf8_get_char(p++);
867 if (g_unichar_isspace(c))
868 return TRUE;
871 return FALSE;
874 static int delete_command(assuan_context_t ctx, char *line)
876 struct client_s *client = assuan_get_pointer(ctx);
877 gchar **req;
878 gpg_error_t error;
879 xmlNodePtr n;
881 error = file_modified(client);
883 if (error) {
884 log_write("%s: %s", client->filename ? client->filename : "",
885 pwmd_strerror(error));
886 return send_error(ctx, error);
889 if (strchr(line, '\t'))
890 req = split_input_line(line, "\t", -1);
891 else
892 req = split_input_line(line, " ", -1);
894 if (!req || !*req)
895 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
897 n = find_account(client->doc, &req, &error, NULL);
899 if (!n) {
900 g_strfreev(req);
901 return send_error(ctx, error);
905 * No sub-node defined. Remove the entire node (account).
907 if (!req[1]) {
908 if (n) {
909 xmlUnlinkNode(n);
910 xmlFreeNode(n);
913 g_strfreev(req);
914 return 0;
917 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
918 g_strfreev(req);
920 if (!n)
921 return send_error(ctx, error);
923 if (n) {
924 xmlUnlinkNode(n);
925 xmlFreeNode(n);
928 return 0;
931 static int store_command(assuan_context_t ctx, char *line)
933 struct client_s *client = assuan_get_pointer(ctx);
934 gchar **req;
935 gpg_error_t error;
936 guchar *result;
937 gsize len;
938 xmlNodePtr n;
940 error = file_modified(client);
942 if (error) {
943 log_write("%s: %s", client->filename ? client->filename : "",
944 pwmd_strerror(error));
945 return send_error(ctx, error);
948 error = assuan_inquire(ctx, "STORE", &result, &len, 0);
950 if (error)
951 return send_error(ctx, error);
953 req = split_input_line((gchar *)result, "\t", 0);
954 #ifndef MEM_DEBUG
955 xfree(result);
956 #else
957 free(result);
958 #endif
960 if (!req || !*req)
961 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
963 again:
964 n = find_account(client->doc, &req, &error, NULL);
966 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
967 if (contains_whitespace(*req) == TRUE) {
968 g_strfreev(req);
969 return send_error(ctx, EPWMD_INVALID_ELEMENT);
972 error = new_account(client->doc, *req);
974 if (error) {
975 g_strfreev(req);
976 return send_error(ctx, error);
979 goto again;
982 if (!n) {
983 g_strfreev(req);
984 return send_error(ctx, error);
987 if (req[1]) {
988 if (!n->children)
989 create_elements_cb(n, req+1, &error, NULL);
990 else
991 find_elements(client->doc, n->children, req+1, &error,
992 NULL, NULL, create_elements_cb, NULL);
995 g_strfreev(req);
996 return send_error(ctx, error);
999 static int get_command(assuan_context_t ctx, char *line)
1001 struct client_s *client = assuan_get_pointer(ctx);
1002 gchar **req;
1003 gpg_error_t error;
1004 xmlNodePtr n;
1006 error = file_modified(client);
1008 if (error) {
1009 log_write("%s: %s", client->filename ? client->filename : "",
1010 pwmd_strerror(error));
1011 return send_error(ctx, error);
1014 req = split_input_line(line, "\t", -1);
1016 if (!req || !*req) {
1017 g_strfreev(req);
1018 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1021 n = find_account(client->doc, &req, &error, NULL);
1023 if (!n) {
1024 g_strfreev(req);
1025 return send_error(ctx, error);
1028 if (req[1])
1029 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
1031 g_strfreev(req);
1033 if (error)
1034 return send_error(ctx, error);
1036 if (!n || !n->children)
1037 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1039 n = n->children;
1041 if (!n || !n->content || !*n->content)
1042 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1044 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1045 return send_error(ctx, error);
1048 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1050 xmlChar *p = path;
1051 gint n;
1052 gchar *buf;
1054 if (!p)
1055 return NULL;
1057 for (n = 0; *p && n < 3; p++) {
1058 if (*p == '/')
1059 n++;
1062 if (strstr((gchar *)p, "text()") != NULL)
1063 p[xmlStrlen(p) - 7] = 0;
1065 for (n = 0; p[n]; n++) {
1066 if (p[n] == '/')
1067 p[n] = '\t';
1070 buf = g_strdup_printf("%s\t%s", account, p);
1071 return buf;
1074 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1076 gchar **a;
1077 va_list ap;
1078 gchar *buf;
1079 gint len = *array ? g_strv_length(*array) : 0;
1080 gint ret;
1082 if (!fmt)
1083 return FALSE;
1085 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1086 return FALSE;
1088 va_start(ap, fmt);
1089 ret = g_vasprintf(&buf, fmt, ap);
1090 va_end(ap);
1092 if (ret == -1)
1093 return FALSE;
1095 a[len++] = buf;
1096 a[len] = NULL;
1097 *array = a;
1098 return TRUE;
1101 struct realpath_s {
1102 gchar *account;
1105 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1106 gpg_error_t *error, void *data)
1108 struct realpath_s *rp = data;
1110 if (rp->account)
1111 g_free(rp->account);
1113 rp->account = g_strdup(req[0]);
1115 if (!rp->account) {
1116 *error = gpg_error_from_errno(ENOMEM);
1117 return NULL;
1120 return node;
1123 static int realpath_command(assuan_context_t ctx, char *line)
1125 gpg_error_t error;
1126 struct client_s *client = assuan_get_pointer(ctx);
1127 xmlChar *p;
1128 gchar **req;
1129 gchar *result, *t;
1130 gint i;
1131 xmlNodePtr n;
1132 struct realpath_s *rp;
1133 GString *string;
1135 error = file_modified(client);
1137 if (error) {
1138 log_write("%s: %s", client->filename ? client->filename : "",
1139 pwmd_strerror(error));
1140 return send_error(ctx, error);
1143 if (strchr(line, '\t') != NULL) {
1144 if ((req = split_input_line(line, "\t", 0)) == NULL)
1145 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1147 else {
1148 if ((req = split_input_line(line, " ", 0)) == NULL)
1149 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1152 n = find_account(client->doc, &req, &error, NULL);
1154 if (!n) {
1155 g_strfreev(req);
1156 return send_error(ctx, error);
1159 rp = g_malloc(sizeof(struct realpath_s));
1161 if (!rp) {
1162 g_strfreev(req);
1163 return send_syserror(ctx, ENOMEM);
1166 rp->account = g_strdup(req[0]);
1168 if (!rp->account) {
1169 g_strfreev(req);
1170 return send_syserror(ctx, ENOMEM);
1173 if (req[1]) {
1174 n = find_elements(client->doc, n->children, req+1, &error,
1175 NULL, realpath_elements_cb, NULL, rp);
1177 if (!n) {
1178 g_free(rp->account);
1179 g_free(rp);
1180 g_strfreev(req);
1181 return send_error(ctx, error);
1185 p = xmlGetNodePath(n);
1186 result = element_path_to_req(rp->account, p);
1188 if (!result) {
1189 g_free(result);
1190 g_free(rp->account);
1191 g_free(rp);
1192 g_strfreev(req);
1193 xmlFree(p);
1194 return send_syserror(ctx, ENOMEM);
1197 string = g_string_new(result);
1198 g_free(result);
1199 g_free(rp->account);
1200 g_free(rp);
1201 g_strfreev(req);
1202 xmlFree(p);
1203 i = 0;
1205 again:
1206 for (t = string->str + i; *t; t++, i++) {
1207 if (!i || *t == '\t') {
1208 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1209 goto again;
1213 error = assuan_send_data(ctx, string->str, string->len);
1214 g_string_free(string, TRUE);
1215 return send_error(ctx, error);
1218 struct list_element_s {
1219 GSList *list;
1220 gchar **elements;
1223 static gboolean append_to_element_list(struct list_element_s *elements)
1225 gchar *tmp;
1226 gint i, total;
1227 gchar *a;
1228 GSList *list;
1230 if (!elements || !elements->elements)
1231 return TRUE;
1233 tmp = g_strjoinv("\t", elements->elements);
1235 if (!tmp)
1236 return FALSE;
1238 g_strfreev(elements->elements);
1239 elements->elements = NULL;
1240 total = g_slist_length(elements->list);
1241 a = g_utf8_collate_key(tmp, -1);
1243 if (!a) {
1244 g_free(tmp);
1245 return FALSE;
1249 * Removes duplicate element paths from the list. This is needed when
1250 * appending an element tree from list_command(). The glib docs recommend
1251 * using g_utf8_collate_key() for a large number of strings.
1253 for (i = 0; i < total; i++) {
1254 gchar *p = g_slist_nth_data(elements->list, i);
1255 gchar *b = g_utf8_collate_key(p, -1);
1257 if (!b) {
1258 g_free(a);
1259 g_free(tmp);
1260 return FALSE;
1263 if (strcmp(a, b) == 0) {
1264 g_free(a);
1265 g_free(b);
1266 g_free(tmp);
1267 return TRUE;
1270 g_free(b);
1273 g_free(a);
1274 list = g_slist_append(elements->list, tmp);
1276 if (!list)
1277 return FALSE;
1279 elements->list = list;
1280 return TRUE;
1283 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1284 struct list_element_s *elements, gchar *prefix)
1286 xmlNodePtr n;
1287 gpg_error_t error;
1289 if (append_to_element_list(elements) == FALSE)
1290 return gpg_error_from_errno(ENOMEM);
1292 for (n = node; n; n = n->next) {
1293 if (n->type == XML_ELEMENT_NODE) {
1294 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1295 gchar *tmp;
1297 if (content) {
1298 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1299 return gpg_error_from_errno(ENOMEM);
1301 if (append_to_element_list(elements) == FALSE)
1302 return gpg_error_from_errno(ENOMEM);
1305 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1307 if (!tmp)
1308 return gpg_error_from_errno(ENOMEM);
1310 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1311 g_free(tmp);
1312 return gpg_error_from_errno(ENOMEM);
1315 if (n->children) {
1316 error = do_list_recurse(doc, n->children, elements, tmp);
1317 g_free(tmp);
1319 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1320 return error;
1322 else
1323 g_free(tmp);
1325 if (append_to_element_list(elements) == FALSE)
1326 return gpg_error_from_errno(ENOMEM);
1330 return 0;
1333 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1334 struct list_element_s *elements, char *line)
1336 gchar *prefix = NULL, *account;
1337 gchar **req = NULL, **oreq = NULL, *tmp;
1338 xmlNodePtr n;
1339 gboolean account_is_literal, account_has_target = FALSE;
1340 gint which = 0;
1341 gchar **p;
1342 gpg_error_t error;
1344 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1345 if ((req = split_input_line(line, " ", 0)) == NULL)
1346 return EPWMD_COMMAND_SYNTAX;
1349 prefix = g_strdup(*req);
1351 if (!prefix) {
1352 g_strfreev(req);
1353 return gpg_error_from_errno(ENOMEM);
1356 account = g_strdup(*req);
1358 if (!account) {
1359 g_free(prefix);
1360 g_strfreev(req);
1361 return gpg_error_from_errno(ENOMEM);
1364 oreq = g_strdupv(req);
1366 if (!oreq) {
1367 g_free(prefix);
1368 g_free(account);
1369 g_strfreev(req);
1370 return gpg_error_from_errno(ENOMEM);
1373 p = req;
1374 again:
1375 account_has_target = FALSE;
1376 account_is_literal = is_literal_element_str(prefix);
1377 n = find_account(doc, &p, &error, &account_has_target);
1379 if (which)
1380 oreq = p;
1381 else
1382 req = p;
1384 if (!n)
1385 goto fail;
1387 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1388 tmp = g_strdup_printf("!%s", prefix);
1390 if (!tmp) {
1391 error = gpg_error_from_errno(ENOMEM);
1392 goto fail;
1395 g_free(prefix);
1396 prefix = tmp;
1399 if (*(p+1)) {
1400 gchar *t;
1402 n = find_elements(doc, n->children, p+1, &error,
1403 NULL, NULL, NULL, NULL);
1405 if (error)
1406 goto fail;
1408 tmp = g_strjoinv("\t", p+1);
1409 if (!tmp) {
1410 error = gpg_error_from_errno(ENOMEM);
1411 goto fail;
1414 t = g_strdup_printf("%s\t%s", prefix, tmp);
1415 if (!t) {
1416 error = gpg_error_from_errno(ENOMEM);
1417 goto fail;
1420 g_free(prefix);
1421 prefix = t;
1422 g_free(tmp);
1425 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1426 error = gpg_error_from_errno(ENOMEM);
1427 goto fail;
1430 if (node_has_child_element(n->children) == FALSE) {
1431 if (append_to_element_list(elements) == FALSE) {
1432 error = gpg_error_from_errno(ENOMEM);
1433 goto fail;
1436 else
1437 error = do_list_recurse(doc, n->children, elements, prefix);
1439 if (error)
1440 goto fail;
1442 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1443 g_free(*oreq);
1444 *oreq = g_strdup_printf("!%s", account);
1446 if (!*oreq) {
1447 error = gpg_error_from_errno(ENOMEM);
1448 goto fail;
1451 p = oreq;
1452 g_free(prefix);
1453 prefix = g_strdup(*oreq);
1455 if (!prefix) {
1456 error = gpg_error_from_errno(ENOMEM);
1457 goto fail;
1460 goto again;
1463 fail:
1464 g_free(prefix);
1465 g_free(account);
1466 g_strfreev(req);
1468 if (oreq)
1469 g_strfreev(oreq);
1471 return error;
1475 * This could be faster especially when finding "target" attributes.
1477 static int list_command(assuan_context_t ctx, char *line)
1479 struct client_s *client = assuan_get_pointer(ctx);
1480 gpg_error_t error;
1481 struct list_element_s *elements = NULL;
1482 GString *string;
1483 gchar *tmp;
1484 gint i, total;
1486 if (disable_list_and_dump == TRUE)
1487 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1489 error = file_modified(client);
1491 if (error) {
1492 log_write("%s: %s", client->filename, pwmd_strerror(error));
1493 return send_error(ctx, error);
1496 if (!*line) {
1497 GString *str;
1499 error = list_accounts(client->doc, &str);
1501 if (error)
1502 return send_error(ctx, error);
1504 error = assuan_send_data(ctx, str->str, str->len);
1505 g_string_free(str, TRUE);
1506 return send_error(ctx, error);
1509 elements = g_malloc0(sizeof(struct list_element_s));
1511 if (!elements) {
1512 error = gpg_error_from_errno(ENOMEM);
1513 goto fail;
1516 is_list_command = TRUE;
1517 error = do_list_command(ctx, client->doc, elements, line);
1519 if (error)
1520 goto fail;
1522 if (!error) {
1523 total = g_slist_length(elements->list);
1525 if (!total) {
1526 error = EPWMD_EMPTY_ELEMENT;
1527 goto fail;
1531 * Find element paths with a target and append those element trees to
1532 * the list.
1534 for (i = 0; i < total; i++) {
1535 gchar **req;
1537 tmp = g_slist_nth_data(elements->list, i);
1538 req = split_input_line(tmp, "\t", 0);
1540 if (!req) {
1541 if (g_str_has_prefix(tmp, "!") == TRUE) {
1542 g_strfreev(req);
1543 continue;
1546 else {
1547 gchar **p;
1549 for (p = req; *p; p++) {
1550 if (g_str_has_prefix(*p, "!") == FALSE)
1551 break;
1554 if (!*p) {
1555 g_strfreev(req);
1556 continue;
1560 g_strfreev(req);
1561 error = do_list_command(ctx, client->doc, elements, tmp);
1563 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1564 goto fail;
1566 total = g_slist_length(elements->list);
1570 string = g_string_new(NULL);
1572 for (i = 0; i < total; i++) {
1573 tmp = g_slist_nth_data(elements->list, i);
1574 g_string_append_printf(string, "%s\n", tmp);
1575 g_free(tmp);
1578 string = g_string_truncate(string, string->len - 1);
1579 error = assuan_send_data(ctx, string->str, string->len);
1580 g_string_free(string, TRUE);
1582 fail:
1583 is_list_command = FALSE;
1585 if (elements) {
1586 if (elements->list)
1587 g_slist_free(elements->list);
1589 if (elements->elements)
1590 g_strfreev(elements->elements);
1592 g_free(elements);
1595 return send_error(ctx, error);
1598 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1599 const gchar *value)
1601 xmlAttrPtr a;
1603 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1604 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1606 if (!a)
1607 return EPWMD_LIBXML_ERROR;
1609 else
1610 xmlNodeSetContent(a->children, (xmlChar *)value);
1612 return 0;
1616 * req[0] - element path
1618 static int attribute_list(assuan_context_t ctx, gchar **req)
1620 struct client_s *client = assuan_get_pointer(ctx);
1621 gchar **attrlist = NULL;
1622 gint i = 0;
1623 gchar **path = NULL;
1624 xmlAttrPtr a;
1625 xmlNodePtr n, an;
1626 gchar *line;
1627 gpg_error_t error;
1629 if (!req || !req[0])
1630 return EPWMD_COMMAND_SYNTAX;
1632 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1634 * The first argument may be only an account.
1636 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1637 return EPWMD_COMMAND_SYNTAX;
1640 n = find_account(client->doc, &path, &error, NULL);
1642 if (!n) {
1643 g_strfreev(path);
1644 return error;
1647 if (path[1]) {
1648 n = find_elements(client->doc, n->children, path+1, &error,
1649 NULL, NULL, NULL, NULL);
1651 if (!n) {
1652 g_strfreev(path);
1653 return error;
1657 g_strfreev(path);
1659 for (a = n->properties; a; a = a->next) {
1660 gchar **pa;
1662 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1663 if (attrlist)
1664 g_strfreev(attrlist);
1666 error = errno;
1667 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1668 return gpg_error_from_errno(error);
1671 attrlist = pa;
1672 an = a->children;
1673 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1675 if (!attrlist[i]) {
1676 g_strfreev(attrlist);
1677 return gpg_error_from_errno(ENOMEM);
1680 attrlist[++i] = NULL;
1683 if (!attrlist)
1684 return EPWMD_EMPTY_ELEMENT;
1686 line = g_strjoinv("\n", attrlist);
1688 if (!line) {
1689 g_strfreev(attrlist);
1690 return gpg_error_from_errno(ENOMEM);
1693 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1694 g_free(line);
1695 g_strfreev(attrlist);
1696 return error;
1700 * req[0] - attribute
1701 * req[1] - element path
1703 static int attribute_delete(struct client_s *client, gchar **req)
1705 xmlAttrPtr a;
1706 xmlNodePtr n;
1707 gchar **path = NULL;
1708 gpg_error_t error;
1710 if (!req || !req[0] || !req[1])
1711 return EPWMD_COMMAND_SYNTAX;
1713 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1715 * The first argument may be only an account.
1717 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1718 return EPWMD_COMMAND_SYNTAX;
1722 * Don't remove the "name" attribute for the account element. To remove an
1723 * account use DELETE <account>.
1725 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1726 error = EPWMD_ATTR_SYNTAX;
1727 goto fail;
1730 n = find_account(client->doc, &path, &error, NULL);
1732 if (!n)
1733 goto fail;
1735 if (path[1]) {
1736 n = find_elements(client->doc, n->children, path+1, &error,
1737 NULL, NULL, NULL, NULL);
1739 if (!n)
1740 goto fail;
1743 g_strfreev(path);
1745 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1746 return EPWMD_ATTR_NOT_FOUND;
1748 if (xmlRemoveProp(a) == -1)
1749 return EPWMD_LIBXML_ERROR;
1751 return 0;
1753 fail:
1754 g_strfreev(path);
1755 return error;
1759 * Creates a "target" attribute. When other commands encounter an element with
1760 * this attribute, the element path is modified to the target value. If the
1761 * source element path doesn't exist when using 'ATTR SET target', it is
1762 * created, but the destination element path must exist.
1764 * req[0] - source element path
1765 * req[1] - destination element path
1767 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1769 gchar **src, **dst, *line;
1770 gpg_error_t error;
1771 xmlNodePtr n;
1773 if (!req || !req[0] || !req[1])
1774 return EPWMD_COMMAND_SYNTAX;
1776 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1778 * The first argument may be only an account.
1780 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1781 return EPWMD_COMMAND_SYNTAX;
1784 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1786 * The first argument may be only an account.
1788 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1789 error = EPWMD_COMMAND_SYNTAX;
1790 goto fail;
1794 n = find_account(client->doc, &dst, &error, NULL);
1797 * Make sure the destination element path exists.
1799 if (!n)
1800 goto fail;
1802 if (dst[1]) {
1803 n = find_elements(client->doc, n->children, dst+1, &error,
1804 NULL, NULL, NULL, NULL);
1806 if (!n)
1807 goto fail;
1810 again:
1811 n = find_account(client->doc, &src, &error, NULL);
1813 if (!n) {
1814 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1815 error = new_account(client->doc, src[0]);
1817 if (error)
1818 goto fail;
1820 goto again;
1822 else
1823 goto fail;
1826 if (src[1]) {
1827 if (!n->children)
1828 n = create_target_elements_cb(n, src+1, &error, NULL);
1829 else
1830 n = find_elements(client->doc, n->children, src+1, &error,
1831 NULL, NULL, create_target_elements_cb, NULL);
1833 if (!n)
1834 goto fail;
1837 * Reset the position of the element tree now that the elements
1838 * have been created.
1840 n = find_account(client->doc, &src, &error, NULL);
1842 if (!n)
1843 goto fail;
1845 n = find_elements(client->doc, n->children, src+1, &error,
1846 NULL, NULL, NULL, NULL);
1848 if (!n)
1849 goto fail;
1852 line = g_strjoinv("\t", dst);
1853 error = add_attribute(n, "target", line);
1855 if (error) {
1856 g_free(line);
1857 goto fail;
1860 g_free(line);
1861 g_strfreev(src);
1862 g_strfreev(dst);
1863 return 0;
1865 fail:
1866 g_strfreev(src);
1867 g_strfreev(dst);
1868 return error;
1872 * req[0] - account name
1873 * req[1] - new name
1875 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1877 gpg_error_t error;
1878 gchar **tmp;
1879 xmlNodePtr n;
1881 tmp = g_strdupv(req);
1883 if (!tmp)
1884 return gpg_error_from_errno(ENOMEM);
1886 n = find_account(client->doc, &tmp, &error, NULL);
1887 g_strfreev(tmp);
1889 if (!n)
1890 return error;
1892 if (g_utf8_collate(req[0], req[1]) == 0)
1893 return 0;
1896 * Will not overwrite an existing account.
1898 tmp = g_strdupv(req+1);
1900 if (!tmp)
1901 return gpg_error_from_errno(ENOMEM);
1903 n = find_account(client->doc, &tmp, &error, NULL);
1904 g_strfreev(tmp);
1906 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1907 return error;
1909 if (n)
1910 return EPWMD_ACCOUNT_EXISTS;
1913 * Whitespace not allowed in account names.
1915 if (contains_whitespace(req[1]) == TRUE)
1916 return EPWMD_ATTR_SYNTAX;
1918 tmp = g_strdupv(req);
1920 if (!tmp)
1921 return gpg_error_from_errno(ENOMEM);
1923 n = find_account(client->doc, &tmp, &error, NULL);
1924 g_strfreev(tmp);
1926 if (!n)
1927 return EPWMD_ELEMENT_NOT_FOUND;
1929 return add_attribute(n, "name", req[1]);
1933 * req[0] - attribute
1934 * req[1] - element path
1936 static int attribute_get(assuan_context_t ctx, gchar **req)
1938 struct client_s *client = assuan_get_pointer(ctx);
1939 xmlNodePtr n;
1940 xmlChar *a;
1941 gchar **path= NULL;
1942 gpg_error_t error;
1944 if (!req || !req[0] || !req[1])
1945 return EPWMD_COMMAND_SYNTAX;
1947 if (strchr(req[1], '\t')) {
1948 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1949 return EPWMD_COMMAND_SYNTAX;
1951 else {
1952 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1953 return EPWMD_COMMAND_SYNTAX;
1956 n = find_account(client->doc, &path, &error, NULL);
1958 if (!n)
1959 goto fail;
1961 if (path[1]) {
1962 n = find_elements(client->doc, n->children, path+1, &error,
1963 NULL, NULL, NULL, NULL);
1965 if (!n)
1966 goto fail;
1969 g_strfreev(path);
1971 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1972 return EPWMD_ATTR_NOT_FOUND;
1974 error = assuan_send_data(ctx, a, xmlStrlen(a));
1975 xmlFree(a);
1976 return error;
1978 fail:
1979 g_strfreev(path);
1980 return error;
1984 * req[0] - attribute
1985 * req[1] - element path
1986 * req[2] - value
1988 static int attribute_set(struct client_s *client, gchar **req)
1990 gchar **path = NULL;
1991 gpg_error_t error;
1992 xmlNodePtr n;
1994 if (!req || !req[0] || !req[1] || !req[2])
1995 return EPWMD_COMMAND_SYNTAX;
1998 * Reserved attribute names.
2000 if (g_utf8_collate(req[0], "name") == 0) {
2002 * Only reserved for the account element. Not the rest of the
2003 * document.
2005 if (strchr(req[1], '\t') == NULL)
2006 return name_attribute(client, req + 1);
2008 else if (g_utf8_collate(req[0], "target") == 0)
2009 return target_attribute(client, req + 1);
2011 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2013 * The first argument may be only an account.
2015 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2016 return EPWMD_COMMAND_SYNTAX;
2019 n = find_account(client->doc, &path, &error, NULL);
2021 if (!n)
2022 goto fail;
2024 if (path[1]) {
2025 n = find_elements(client->doc, n->children, path+1, &error,
2026 NULL, NULL, NULL, NULL);
2028 if (!n)
2029 goto fail;
2032 g_strfreev(path);
2033 return add_attribute(n, req[0], req[2]);
2035 fail:
2036 g_strfreev(path);
2037 return error;
2041 * req[0] - command
2042 * req[1] - attribute name or element path if command is LIST
2043 * req[2] - element path
2044 * req[2] - element path or value
2046 static int attr_command(assuan_context_t ctx, char *line)
2048 struct client_s *client = assuan_get_pointer(ctx);
2049 gchar **req = split_input_line(line, " ", 4);
2050 gpg_error_t error = 0;
2052 error = file_modified(client);
2054 if (error) {
2055 log_write("%s: %s", client->filename ? client->filename : "",
2056 pwmd_strerror(error));
2057 g_strfreev(req);
2058 return send_error(ctx, error);
2061 if (!req || !req[0] || !req[1]) {
2062 g_strfreev(req);
2063 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2066 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2067 error = attribute_set(client, req+1);
2068 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2069 error = attribute_get(ctx, req+1);
2070 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2071 error = attribute_delete(client, req+1);
2072 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2073 error = attribute_list(ctx, req+1);
2074 else
2075 error = EPWMD_COMMAND_SYNTAX;
2077 g_strfreev(req);
2078 return send_error(ctx, error);
2081 static int iscached_command(assuan_context_t ctx, char *line)
2083 gchar **req = split_input_line(line, " ", 0);
2084 guchar md5file[16];
2086 MUTEX_LOCK(ctx);
2088 if (!req || !*req) {
2089 g_strfreev(req);
2090 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2093 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2094 g_strfreev(req);
2096 if (cache_iscached(md5file) == FALSE)
2097 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2099 return 0;
2102 void send_cache_status(assuan_context_t ctx)
2104 gchar *tmp;
2105 gint i = MUTEX_OFFSET;
2107 tmp = print_fmt("%i %i",
2108 cache_file_count(),
2109 (i / sizeof(file_cache_t)) - cache_file_count());
2111 if (ctx)
2112 assuan_write_status(ctx, "CACHE", tmp);
2115 static int clearcache_command(assuan_context_t ctx, char *line)
2117 struct client_s *client = assuan_get_pointer(ctx);
2118 gchar **req = split_input_line(line, " ", 0);
2119 guchar md5file[16];
2121 MUTEX_LOCK(ctx);
2123 if (!req || !*req) {
2124 g_strfreev(req);
2125 cache_clear(client->md5file, 2);
2126 send_cache_status(ctx);
2127 return 0;
2130 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2131 g_strfreev(req);
2132 cache_clear(md5file, 1);
2133 send_cache_status(ctx);
2134 return 0;
2137 static int cachetimeout_command(assuan_context_t ctx, char *line)
2139 guchar md5file[16];
2140 glong timeout;
2141 gchar **req = split_input_line(line, " ", 0);
2142 gchar *p;
2144 MUTEX_LOCK(ctx);
2146 if (!req || !*req || !req[1]) {
2147 g_strfreev(req);
2148 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2151 errno = 0;
2152 timeout = strtol(req[0], &p, 10);
2154 if (errno != 0 || *p != 0) {
2155 g_strfreev(req);
2156 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2159 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2160 g_strfreev(req);
2162 if (cache_set_timeout(md5file, timeout) == FALSE)
2163 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2165 send_cache_status(ctx);
2166 return 0;
2169 static int dump_command(assuan_context_t ctx, char *line)
2171 xmlChar *xml;
2172 gssize len;
2173 struct client_s *client = assuan_get_pointer(ctx);
2174 gpg_error_t error;
2176 if (disable_list_and_dump == TRUE)
2177 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2179 error = file_modified(client);
2181 if (error) {
2182 log_write("%s: %s", client->filename ? client->filename : "",
2183 pwmd_strerror(error));
2184 return send_error(ctx, error);
2187 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2188 error = assuan_send_data(ctx, xml, len);
2189 xmlFree(xml);
2190 return error;
2193 static int getconfig_command(assuan_context_t ctx, gchar *line)
2195 struct client_s *client = assuan_get_pointer(ctx);
2196 gpg_error_t error = 0;
2197 gchar *p, *tmp;
2199 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2200 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2202 p = get_key_file_string(client->filename ? client->filename : "default", line);
2204 if (!p)
2205 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2207 tmp = expand_homedir(p);
2208 g_free(p);
2209 p = tmp;
2210 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2211 g_free(p);
2212 return send_error(ctx, error);
2215 void cleanup_assuan(assuan_context_t ctx)
2217 struct client_s *cl = assuan_get_pointer(ctx);
2219 cleanup_client(cl);
2220 MUTEX_UNLOCK;
2223 gpg_error_t register_commands(assuan_context_t ctx)
2225 static struct {
2226 const char *name;
2227 int (*handler)(assuan_context_t, char *line);
2228 } table[] = {
2229 { "OPEN", open_command },
2230 { "SAVE", save_command },
2231 { "LIST", list_command },
2232 { "REALPATH", realpath_command },
2233 { "STORE", store_command },
2234 { "DELETE", delete_command },
2235 { "GET", get_command },
2236 { "ATTR", attr_command },
2237 { "ISCACHED", iscached_command },
2238 { "CLEARCACHE", clearcache_command },
2239 { "CACHETIMEOUT", cachetimeout_command },
2240 { "GETCONFIG", getconfig_command },
2241 { "DUMP", dump_command },
2242 { "INPUT", NULL },
2243 { "OUTPUT", NULL },
2244 { NULL, NULL }
2246 int i, rc;
2248 for (i=0; table[i].name; i++) {
2249 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2251 if (rc)
2252 return rc;
2255 return assuan_register_bye_notify(ctx, cleanup_assuan);
2258 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2259 guchar *key)
2261 guchar *iv;
2262 void *inbuf;
2263 gsize insize, len;
2264 guchar tkey[gcrykeysize];
2265 struct file_header_s {
2266 guint iter;
2267 guchar iv[gcryblocksize];
2268 } file_header;
2269 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2270 gcry_cipher_hd_t gh;
2271 guint iter = 0, n_iter = 0;
2272 gint iter_progress;
2273 void *outbuf = NULL;
2274 gint zerror = 0;
2275 glong outsize = 0;
2277 if (!ctx) {
2278 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2280 if (gcryerrno)
2281 return gcryerrno;
2283 else
2284 gh = client->gh;
2286 lseek(fd, 0, SEEK_SET);
2287 insize = st.st_size - sizeof(struct file_header_s);
2288 iv = gcry_malloc(gcryblocksize);
2290 if (!iv) {
2291 if (!ctx)
2292 gcry_cipher_close(gh);
2294 return gpg_error_from_errno(ENOMEM);
2297 len = read(fd, &file_header, sizeof(struct file_header_s));
2299 if (len != sizeof(file_header)) {
2300 len = errno;
2302 if (!ctx)
2303 gcry_cipher_close(gh);
2305 gcry_free(iv);
2306 errno = len;
2307 return gpg_error_from_errno(errno);
2310 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2311 inbuf = gcry_malloc(insize);
2313 if (!inbuf) {
2314 if (!ctx)
2315 gcry_cipher_close(gh);
2317 gcry_free(iv);
2318 return gpg_error_from_errno(ENOMEM);
2321 len = read(fd, inbuf, insize);
2323 if (len != insize) {
2324 len = errno;
2326 if (!ctx)
2327 gcry_cipher_close(gh);
2329 gcry_free(iv);
2330 errno = len;
2331 return gpg_error_from_errno(errno);
2334 memcpy(tkey, key, sizeof(tkey));
2335 tkey[0] ^= 1;
2337 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2338 if (!ctx) {
2339 gcry_cipher_close(gh);
2340 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2342 else
2343 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2345 gcry_free(inbuf);
2346 gcry_free(iv);
2347 return gcryerrno;
2350 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2351 if (!ctx) {
2352 gcry_cipher_close(gh);
2353 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2355 else
2356 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2358 gcry_free(inbuf);
2359 gcry_free(iv);
2361 if (!ctx)
2362 gcry_cipher_close(gh);
2364 return gcryerrno;
2367 iter_progress = get_key_file_integer("default", "iteration_progress");
2369 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress)
2370 assuan_write_status(client->ctx, "DECRYPT", "0");
2372 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2373 if (!ctx) {
2374 gcry_cipher_close(gh);
2375 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2377 else
2378 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2380 gcry_free(inbuf);
2381 gcry_free(iv);
2382 return gcryerrno;
2385 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2386 if (!ctx) {
2387 gcry_cipher_close(gh);
2388 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2390 else
2391 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2393 gcry_free(inbuf);
2394 gcry_free(iv);
2395 return gcryerrno;
2398 while (iter < file_header.iter) {
2399 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2400 if (!(iter % iter_progress))
2401 assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2402 ++n_iter * iter_progress));
2405 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2406 if (!ctx) {
2407 gcry_cipher_close(gh);
2408 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2410 else
2411 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2413 gcry_free(inbuf);
2414 gcry_free(iv);
2415 return gcryerrno;
2418 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2419 if (!ctx) {
2420 gcry_cipher_close(gh);
2421 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2423 else
2424 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2426 gcry_free(inbuf);
2427 gcry_free(iv);
2428 return gcryerrno;
2431 iter++;
2434 if (ctx && iter_progress && file_header.iter >= iter_progress)
2435 assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2437 gcry_free(iv);
2439 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2441 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2443 if (zerror == Z_MEM_ERROR) {
2444 gcry_free(inbuf);
2445 return gpg_error_from_errno(ENOMEM);
2447 else if (zerror != Z_DATA_ERROR) {
2448 gcry_free(inbuf);
2450 if (!ctx)
2451 gcry_cipher_close(gh);
2453 return EPWMD_BADKEY;
2456 else {
2457 gcry_free(inbuf);
2458 inbuf = outbuf;
2459 insize = outsize;
2462 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2463 gcry_free(inbuf);
2465 if (!ctx)
2466 gcry_cipher_close(gh);
2468 return EPWMD_BADKEY;
2471 if (ctx) {
2472 client->xml = inbuf;
2473 client->len = insize;
2475 else {
2476 gcry_cipher_close(gh);
2477 gcry_free(inbuf);
2480 return 0;