Return EPWMD_BADKEY when do_decompress fails with an incorrect header.
[pwmd.git] / src / commands.c
blob87dcd317ddbdf4286e34ae51146ce84d45547a7d
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 gz_header h;
195 gchar buf[17];
197 z.zalloc = z_alloc;
198 z.zfree = z_free;
199 z.next_in = in;
200 z.avail_in = insize;
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 = inflateInit2(&z, 47);
212 if (ret != Z_OK) {
213 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
214 g_free(pout);
215 return FALSE;
218 memset(&h, 0, sizeof(gz_header));
219 h.comment = (guchar *)buf;
220 h.comm_max = sizeof(buf);
221 ret = inflateGetHeader(&z, &h);
223 if (ret != Z_OK) {
224 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
225 g_free(pout);
226 inflateEnd(&z);
227 return FALSE;
230 ret = inflate(&z, Z_BLOCK);
232 if (ret != Z_OK) {
233 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
234 g_free(pout);
235 inflateEnd(&z);
236 return FALSE;
239 if (h.comment)
240 insize = atoi((gchar *)h.comment);
242 do {
243 gpointer p;
245 ret = inflate(&z, Z_FINISH);
247 switch (ret) {
248 case Z_OK:
249 break;
250 case Z_BUF_ERROR:
251 if (!z.avail_out) {
252 p = g_realloc(pout, z.total_out + zlib_bufsize);
254 if (!p) {
255 ret = Z_MEM_ERROR;
256 goto fail;
259 pout = p;
260 z.next_out = pout + z.total_out;
261 z.avail_out = zlib_bufsize;
263 if (ctx)
264 assuan_write_status(ctx, "DECOMPRESS",
265 print_fmt("%i %i", z.total_out, insize));
267 break;
268 case Z_STREAM_END:
269 break;
270 default:
271 goto fail;
272 break;
274 } while (ret != Z_STREAM_END);
276 if (ctx)
277 assuan_write_status(ctx, "DECOMPRESS",
278 print_fmt("%i %i", z.total_out, insize));
280 *out = pout;
281 *outsize = z.total_out;
282 inflateEnd(&z);
283 return TRUE;
285 fail:
286 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
287 *error = ret;
288 g_free(pout);
289 inflateEnd(&z);
290 return FALSE;
292 #endif
294 static int open_command(assuan_context_t ctx, char *line)
296 gint fd;
297 struct stat st;
298 guchar shakey[gcrykeysize];
299 gint cached = 0;
300 gint timeout;
301 gpg_error_t error;
302 struct client_s *client = assuan_get_pointer(ctx);
303 gchar **req;
304 gchar *filename = NULL;
306 if ((req = split_input_line(line, " ", 2)) != NULL)
307 filename = req[0];
309 if (!filename || !*filename) {
310 g_strfreev(req);
311 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
314 if (valid_filename(filename) == FALSE) {
315 g_strfreev(req);
316 return send_error(ctx, EPWMD_INVALID_FILENAME);
319 if (client->state == STATE_OPEN)
320 cleanup(client);
322 client->freed = FALSE;
324 if ((gcryerrno = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
325 g_strfreev(req);
326 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
327 cleanup(client);
328 return send_error(ctx, gcryerrno);
331 if (stat(filename, &st) == 0) {
332 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
333 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
334 g_strfreev(req);
335 cleanup(client);
336 return send_error(ctx, EPWMD_INVALID_FILENAME);
339 client->mtime = st.st_mtime;
342 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
345 * New files don't need a key.
347 if (access(filename, R_OK|W_OK) != 0) {
348 if (errno != ENOENT) {
349 error = errno;
350 log_write("%s: %s", filename, strerror(errno));
351 g_strfreev(req);
352 cleanup(client);
353 return send_syserror(ctx, error);
355 new_doc:
356 if ((client->xml = new_document()) == NULL) {
357 log_write("%s", strerror(ENOMEM));
358 g_strfreev(req);
359 cleanup(client);
360 return send_syserror(ctx, ENOMEM);
363 client->len = xmlStrlen(client->xml);
365 if (cache_has_file(client->md5file) == TRUE)
366 cache_clear(client->md5file, 1);
368 if (cache_add_file(client->md5file, NULL) == FALSE) {
369 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
370 g_strfreev(req);
371 cleanup(client);
372 return send_error(ctx, EPWMD_MAX_SLOTS);
375 client->new = TRUE;
376 client->filename = g_strdup(filename);
378 if (!client->filename) {
379 g_strfreev(req);
380 cleanup(client);
381 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
382 return send_syserror(ctx, ENOMEM);
385 if (req[1] && *req[1]) {
386 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
387 memset(req[1], 0, strlen(req[1]));
388 goto update_cache;
391 goto done;
394 if ((fd = open_file(filename, &st)) == -1) {
395 error = errno;
396 log_write("%s: %s", filename, strerror(errno));
397 g_strfreev(req);
398 cleanup(client);
399 return send_syserror(ctx, error);
402 if (st.st_size == 0)
403 goto new_doc;
405 if (cache_get_key(client->md5file, shakey) == TRUE)
406 cached = 1;
407 else {
409 * No key specified and no matching filename found in the cache.
411 if (!req[1] || !*req[1]) {
412 close(fd);
413 g_strfreev(req);
414 cleanup(client);
415 return send_error(ctx, EPWMD_KEY);
419 if (!cached) {
420 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
421 memset(req[1], 0, strlen(req[1]));
424 error = try_xml_decrypt(ctx, fd, st, shakey);
426 if (error) {
427 close(fd);
428 memset(shakey, 0, sizeof(shakey));
429 g_strfreev(req);
430 cleanup(client);
431 return send_error(ctx, error);
434 close(fd);
435 client->filename = g_strdup(filename);
437 if (!client->filename) {
438 memset(shakey, 0, sizeof(shakey));
439 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
440 g_strfreev(req);
441 cleanup(client);
442 return send_syserror(ctx, ENOMEM);
445 update_cache:
446 if (!cached) {
447 if (cache_update_key(client->md5file, shakey) == FALSE) {
448 memset(shakey, 0, sizeof(shakey));
449 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
450 g_strfreev(req);
451 cleanup(client);
452 return send_error(ctx, EPWMD_MAX_SLOTS);
455 timeout = get_key_file_integer(client->filename, "cache_timeout");
456 cache_reset_timeout(client->md5file, timeout);
458 else
459 cache_set_timeout(client->md5file, -2);
461 memset(shakey, 0, sizeof(shakey));
463 done:
464 g_strfreev(req);
465 error = parse_xml(ctx);
467 if (client->xml) {
468 gcry_free(client->xml);
469 client->xml = NULL;
472 if (error == 0) {
473 client->state = STATE_OPEN;
474 send_cache_status(ctx);
477 return send_error(ctx, error);
480 #ifdef HAVE_ZLIB_H
481 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
482 gint size, gpointer *out, glong *outsize, gint *error)
484 z_stream z;
485 gpointer pout, pin;
486 gint ret;
487 gz_header h;
488 gchar buf[17];
489 gint cmd = Z_NO_FLUSH;
491 z.zalloc = z_alloc;
492 z.zfree = z_free;
493 z.next_in = pin = data;
494 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
495 z.avail_out = zlib_bufsize;
496 z.next_out = pout = g_malloc(zlib_bufsize);
498 if (!pout) {
499 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
500 *error = Z_MEM_ERROR;
501 return FALSE;
504 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
506 if (ret != Z_OK) {
507 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
508 *error = ret;
509 g_free(pout);
510 return FALSE;
513 memset(&h, 0, sizeof(gz_header));
514 snprintf(buf, sizeof(buf), "%i", size);
515 h.comment = (guchar *)buf;
516 ret = deflateSetHeader(&z, &h);
518 if (ret != Z_OK) {
519 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
520 *error = ret;
521 g_free(pout);
522 deflateEnd(&z);
523 return FALSE;
526 do {
527 gpointer p;
529 ret = deflate(&z, cmd);
531 switch (ret) {
532 case Z_OK:
533 break;
534 case Z_BUF_ERROR:
535 if (!z.avail_out) {
536 p = g_realloc(pout, z.total_out + zlib_bufsize);
538 if (!p) {
539 ret = Z_MEM_ERROR;
540 goto fail;
543 pout = p;
544 z.next_out = pout + z.total_out;
545 z.avail_out = zlib_bufsize;
548 if (!z.avail_in && z.total_in < size) {
549 if (z.total_in + zlib_bufsize > size)
550 z.avail_in = size - z.total_in;
551 else
552 z.avail_in = zlib_bufsize;
554 if (ctx)
555 assuan_write_status(ctx, "COMPRESS",
556 print_fmt("%i %i", z.total_in, size));
559 if (z.total_in >= size)
560 cmd = Z_FINISH;
562 break;
563 case Z_STREAM_END:
564 break;
565 default:
566 goto fail;
568 } while (ret != Z_STREAM_END);
570 if (ctx)
571 assuan_write_status(ctx, "COMPRESS",
572 print_fmt("%i %i", z.total_in, size));
574 *out = pout;
575 *outsize = z.total_out;
576 deflateEnd(&z);
577 return TRUE;
579 fail:
580 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
581 *error = ret;
582 g_free(pout);
583 deflateEnd(&z);
584 return FALSE;
586 #endif
588 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
589 const gchar *filename, gpointer data, size_t insize,
590 const guchar *shakey, guint iter)
592 gsize len = insize;
593 gint fd;
594 gpointer inbuf;
595 guchar tkey[gcrykeysize];
596 gchar *p;
597 gint error;
598 guint iter_progress = 0, n_iter = 0, xiter = 0;
599 struct file_header_s {
600 guint iter;
601 guchar iv[gcryblocksize];
602 } file_header;
604 if (insize / gcryblocksize) {
605 len = (insize / gcryblocksize) * gcryblocksize;
607 if (insize % gcryblocksize)
608 len += gcryblocksize;
612 * Resize the existing xml buffer to the block size required by gcrypt
613 * rather than duplicating it and wasting memory.
615 inbuf = gcry_realloc(data, len);
617 if (!inbuf)
618 return gpg_error_from_errno(ENOMEM);
620 insize = len;
621 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
622 memcpy(tkey, shakey, sizeof(tkey));
623 tkey[0] ^= 1;
625 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
626 gcry_free(inbuf);
627 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
628 return gcryerrno;
631 file_header.iter = iter;
633 if (client)
634 iter_progress = get_key_file_integer("default", "iteration_progress");
636 if (client && iter_progress && file_header.iter >= iter_progress)
637 assuan_write_status(client->ctx, "ENCRYPT", "0");
639 while (xiter < file_header.iter) {
640 if (client && iter_progress > 0 && xiter >= iter_progress) {
641 if (!(xiter % iter_progress))
642 assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
643 ++n_iter * iter_progress));
646 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
647 sizeof(file_header.iv)))) {
648 gcry_free(inbuf);
649 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
650 return gcryerrno;
653 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
654 == FALSE) {
655 gcry_free(inbuf);
656 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
657 return gcryerrno;
660 xiter++;
663 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
664 sizeof(file_header.iv)))) {
665 gcry_free(inbuf);
666 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
667 return gcryerrno;
670 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
671 gcry_free(inbuf);
672 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
673 return gcryerrno;
676 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
677 gcry_free(inbuf);
678 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
679 return gcryerrno;
682 if (client && iter_progress && file_header.iter >= iter_progress)
683 assuan_write_status(client->ctx, "ENCRYPT",
684 print_fmt("%i", file_header.iter));
686 if (filename) {
687 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
688 error = errno;
689 gcry_free(inbuf);
690 p = strrchr(filename, '/');
691 p++;
692 log_write("%s: %s", p, strerror(errno));
693 return gpg_error_from_errno(error);
696 else
698 * xml_import() from command line.
700 fd = STDOUT_FILENO;
702 len = write(fd, &file_header, sizeof(struct file_header_s));
704 if (len != sizeof(file_header)) {
705 len = errno;
707 if (filename)
708 close(fd);
710 gcry_free(inbuf);
711 return gpg_error_from_errno(len);
714 len = write(fd, inbuf, insize);
716 if (len != insize) {
717 len = errno;
719 if (filename)
720 close(fd);
722 gcry_free(inbuf);
723 return gpg_error_from_errno(len);
726 if (filename)
727 close(fd);
729 gcry_free(inbuf);
730 return 0;
733 static int save_command(assuan_context_t ctx, char *line)
735 gpointer xmlbuf;
736 xmlChar *p;
737 gint len;
738 gint cached = 0;
739 guchar shakey[gcrykeysize];
740 gint iter;
741 struct stat st;
742 struct client_s *client = assuan_get_pointer(ctx);
743 gpg_error_t error;
744 gint timeout;
745 #ifdef HAVE_ZLIB_H
746 gpointer outbuf;
747 glong outsize = 0;
748 gint zerror;
749 #endif
751 error = file_modified(client);
753 if (error) {
754 log_write("%s: %s", client->filename ? client->filename : "",
755 pwmd_strerror(error));
756 return send_error(ctx, error);
759 if (stat(client->filename, &st) == -1 && errno != ENOENT)
760 return send_syserror(ctx, errno);
762 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
763 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
764 return send_error(ctx, EPWMD_INVALID_FILENAME);
767 if (!line || !*line) {
768 if (cache_get_key(client->md5file, shakey) == FALSE)
769 return send_error(ctx, EPWMD_KEY);
771 cached = 1;
773 else {
774 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
775 memset(line, 0, strlen(line));
778 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
779 xmlbuf = p;
781 #ifdef HAVE_ZLIB_H
782 iter = get_key_file_integer(client->filename, "compression_level");
784 if (iter < 0)
785 iter = 0;
787 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
788 memset(shakey, 0, sizeof(shakey));
789 xmlFree(xmlbuf);
791 if (zerror == Z_MEM_ERROR)
792 return send_syserror(ctx, ENOMEM);
793 else
794 return send_error(ctx, GPG_ERR_COMPR_ALGO);
796 else {
797 gcry_free(xmlbuf);
798 xmlbuf = outbuf;
799 len = outsize;
801 #endif
803 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
804 iter = 0;
806 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
808 if (error) {
809 memset(shakey, 0, sizeof(shakey));
810 return send_error(ctx, error);
813 stat(client->filename, &st);
814 client->mtime = st.st_mtime;
815 timeout = get_key_file_integer(client->filename, "cache_timeout");
817 if (cached) {
818 memset(shakey, 0, sizeof(shakey));
819 cache_reset_timeout(client->md5file, timeout);
820 client->new = FALSE;
821 return 0;
824 if (cache_update_key(client->md5file, shakey) == FALSE) {
825 memset(shakey, 0, sizeof(shakey));
826 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
827 return send_error(ctx, EPWMD_MAX_SLOTS);
830 client->new = FALSE;
831 memset(shakey, 0, sizeof(shakey));
832 cache_reset_timeout(client->md5file, timeout);
833 return 0;
836 static gboolean contains_whitespace(const gchar *str)
838 const gchar *p = str;
839 gunichar c;
840 glong len;
842 len = g_utf8_strlen(p++, -1) -1;
844 while (len--) {
845 c = g_utf8_get_char(p++);
847 if (g_unichar_isspace(c))
848 return TRUE;
851 return FALSE;
854 static int delete_command(assuan_context_t ctx, char *line)
856 struct client_s *client = assuan_get_pointer(ctx);
857 gchar **req;
858 gpg_error_t error;
859 xmlNodePtr n;
861 error = file_modified(client);
863 if (error) {
864 log_write("%s: %s", client->filename ? client->filename : "",
865 pwmd_strerror(error));
866 return send_error(ctx, error);
869 if (strchr(line, '\t'))
870 req = split_input_line(line, "\t", -1);
871 else
872 req = split_input_line(line, " ", -1);
874 if (!req || !*req)
875 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
877 n = find_account(client->doc, &req, &error, NULL);
879 if (!n) {
880 g_strfreev(req);
881 return send_error(ctx, error);
885 * No sub-node defined. Remove the entire node (account).
887 if (!req[1]) {
888 if (n) {
889 xmlUnlinkNode(n);
890 xmlFreeNode(n);
893 g_strfreev(req);
894 return 0;
897 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
898 g_strfreev(req);
900 if (!n)
901 return send_error(ctx, error);
903 if (n) {
904 xmlUnlinkNode(n);
905 xmlFreeNode(n);
908 return 0;
911 static int store_command(assuan_context_t ctx, char *line)
913 struct client_s *client = assuan_get_pointer(ctx);
914 gchar **req;
915 gpg_error_t error;
916 guchar *result;
917 gsize len;
918 xmlNodePtr n;
920 error = file_modified(client);
922 if (error) {
923 log_write("%s: %s", client->filename ? client->filename : "",
924 pwmd_strerror(error));
925 return send_error(ctx, error);
928 error = assuan_inquire(ctx, "STORE", &result, &len, 0);
930 if (error)
931 return send_error(ctx, error);
933 req = split_input_line((gchar *)result, "\t", 0);
934 #ifndef MEM_DEBUG
935 xfree(result);
936 #else
937 free(result);
938 #endif
940 if (!req || !*req)
941 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
943 again:
944 n = find_account(client->doc, &req, &error, NULL);
946 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
947 if (contains_whitespace(*req) == TRUE) {
948 g_strfreev(req);
949 return send_error(ctx, EPWMD_INVALID_ELEMENT);
952 error = new_account(client->doc, *req);
954 if (error) {
955 g_strfreev(req);
956 return send_error(ctx, error);
959 goto again;
962 if (!n) {
963 g_strfreev(req);
964 return send_error(ctx, error);
967 if (req[1]) {
968 if (!n->children)
969 create_elements_cb(n, req+1, &error, NULL);
970 else
971 find_elements(client->doc, n->children, req+1, &error,
972 NULL, NULL, create_elements_cb, NULL);
975 g_strfreev(req);
976 return send_error(ctx, error);
979 static int get_command(assuan_context_t ctx, char *line)
981 struct client_s *client = assuan_get_pointer(ctx);
982 gchar **req;
983 gpg_error_t error;
984 xmlNodePtr n;
986 error = file_modified(client);
988 if (error) {
989 log_write("%s: %s", client->filename ? client->filename : "",
990 pwmd_strerror(error));
991 return send_error(ctx, error);
994 req = split_input_line(line, "\t", -1);
996 if (!req || !*req) {
997 g_strfreev(req);
998 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1001 n = find_account(client->doc, &req, &error, NULL);
1003 if (!n) {
1004 g_strfreev(req);
1005 return send_error(ctx, error);
1008 if (req[1])
1009 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
1011 g_strfreev(req);
1013 if (error)
1014 return send_error(ctx, error);
1016 if (!n || !n->children)
1017 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1019 n = n->children;
1021 if (!n || !n->content || !*n->content)
1022 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1024 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1025 return send_error(ctx, error);
1028 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1030 xmlChar *p = path;
1031 gint n;
1032 gchar *buf;
1034 if (!p)
1035 return NULL;
1037 for (n = 0; *p && n < 3; p++) {
1038 if (*p == '/')
1039 n++;
1042 if (strstr((gchar *)p, "text()") != NULL)
1043 p[xmlStrlen(p) - 7] = 0;
1045 for (n = 0; p[n]; n++) {
1046 if (p[n] == '/')
1047 p[n] = '\t';
1050 buf = g_strdup_printf("%s\t%s", account, p);
1051 return buf;
1054 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1056 gchar **a;
1057 va_list ap;
1058 gchar *buf;
1059 gint len = *array ? g_strv_length(*array) : 0;
1060 gint ret;
1062 if (!fmt)
1063 return FALSE;
1065 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1066 return FALSE;
1068 va_start(ap, fmt);
1069 ret = g_vasprintf(&buf, fmt, ap);
1070 va_end(ap);
1072 if (ret == -1)
1073 return FALSE;
1075 a[len++] = buf;
1076 a[len] = NULL;
1077 *array = a;
1078 return TRUE;
1081 struct realpath_s {
1082 gchar *account;
1085 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1086 gpg_error_t *error, void *data)
1088 struct realpath_s *rp = data;
1090 if (rp->account)
1091 g_free(rp->account);
1093 rp->account = g_strdup(req[0]);
1095 if (!rp->account) {
1096 *error = gpg_error_from_errno(ENOMEM);
1097 return NULL;
1100 return node;
1103 static int realpath_command(assuan_context_t ctx, char *line)
1105 gpg_error_t error;
1106 struct client_s *client = assuan_get_pointer(ctx);
1107 xmlChar *p;
1108 gchar **req;
1109 gchar *result, *t;
1110 gint i;
1111 xmlNodePtr n;
1112 struct realpath_s *rp;
1113 GString *string;
1115 error = file_modified(client);
1117 if (error) {
1118 log_write("%s: %s", client->filename ? client->filename : "",
1119 pwmd_strerror(error));
1120 return send_error(ctx, error);
1123 if (strchr(line, '\t') != NULL) {
1124 if ((req = split_input_line(line, "\t", 0)) == NULL)
1125 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1127 else {
1128 if ((req = split_input_line(line, " ", 0)) == NULL)
1129 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1132 n = find_account(client->doc, &req, &error, NULL);
1134 if (!n) {
1135 g_strfreev(req);
1136 return send_error(ctx, error);
1139 rp = g_malloc(sizeof(struct realpath_s));
1141 if (!rp) {
1142 g_strfreev(req);
1143 return send_syserror(ctx, ENOMEM);
1146 rp->account = g_strdup(req[0]);
1148 if (!rp->account) {
1149 g_strfreev(req);
1150 return send_syserror(ctx, ENOMEM);
1153 if (req[1]) {
1154 n = find_elements(client->doc, n->children, req+1, &error,
1155 NULL, realpath_elements_cb, NULL, rp);
1157 if (!n) {
1158 g_free(rp->account);
1159 g_free(rp);
1160 g_strfreev(req);
1161 return send_error(ctx, error);
1165 p = xmlGetNodePath(n);
1166 result = element_path_to_req(rp->account, p);
1168 if (!result) {
1169 g_free(result);
1170 g_free(rp->account);
1171 g_free(rp);
1172 g_strfreev(req);
1173 xmlFree(p);
1174 return send_syserror(ctx, ENOMEM);
1177 string = g_string_new(result);
1178 g_free(result);
1179 g_free(rp->account);
1180 g_free(rp);
1181 g_strfreev(req);
1182 xmlFree(p);
1183 i = 0;
1185 again:
1186 for (t = string->str + i; *t; t++, i++) {
1187 if (!i || *t == '\t') {
1188 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1189 goto again;
1193 error = assuan_send_data(ctx, string->str, string->len);
1194 g_string_free(string, TRUE);
1195 return send_error(ctx, error);
1198 struct list_element_s {
1199 GSList *list;
1200 gchar **elements;
1203 static gboolean append_to_element_list(struct list_element_s *elements)
1205 gchar *tmp;
1206 gint i, total;
1207 gchar *a;
1208 GSList *list;
1210 if (!elements || !elements->elements)
1211 return TRUE;
1213 tmp = g_strjoinv("\t", elements->elements);
1215 if (!tmp)
1216 return FALSE;
1218 g_strfreev(elements->elements);
1219 elements->elements = NULL;
1220 total = g_slist_length(elements->list);
1221 a = g_utf8_collate_key(tmp, -1);
1223 if (!a) {
1224 g_free(tmp);
1225 return FALSE;
1229 * Removes duplicate element paths from the list. This is needed when
1230 * appending an element tree from list_command(). The glib docs recommend
1231 * using g_utf8_collate_key() for a large number of strings.
1233 for (i = 0; i < total; i++) {
1234 gchar *p = g_slist_nth_data(elements->list, i);
1235 gchar *b = g_utf8_collate_key(p, -1);
1237 if (!b) {
1238 g_free(a);
1239 g_free(tmp);
1240 return FALSE;
1243 if (strcmp(a, b) == 0) {
1244 g_free(a);
1245 g_free(b);
1246 g_free(tmp);
1247 return TRUE;
1250 g_free(b);
1253 g_free(a);
1254 list = g_slist_append(elements->list, tmp);
1256 if (!list)
1257 return FALSE;
1259 elements->list = list;
1260 return TRUE;
1263 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1264 struct list_element_s *elements, gchar *prefix)
1266 xmlNodePtr n;
1267 gpg_error_t error;
1269 if (append_to_element_list(elements) == FALSE)
1270 return gpg_error_from_errno(ENOMEM);
1272 for (n = node; n; n = n->next) {
1273 if (n->type == XML_ELEMENT_NODE) {
1274 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1275 gchar *tmp;
1277 if (content) {
1278 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1279 return gpg_error_from_errno(ENOMEM);
1281 if (append_to_element_list(elements) == FALSE)
1282 return gpg_error_from_errno(ENOMEM);
1285 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1287 if (!tmp)
1288 return gpg_error_from_errno(ENOMEM);
1290 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1291 g_free(tmp);
1292 return gpg_error_from_errno(ENOMEM);
1295 if (n->children) {
1296 error = do_list_recurse(doc, n->children, elements, tmp);
1297 g_free(tmp);
1299 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1300 return error;
1302 else
1303 g_free(tmp);
1305 if (append_to_element_list(elements) == FALSE)
1306 return gpg_error_from_errno(ENOMEM);
1310 return 0;
1313 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1314 struct list_element_s *elements, char *line)
1316 gchar *prefix = NULL, *account;
1317 gchar **req = NULL, **oreq = NULL, *tmp;
1318 xmlNodePtr n;
1319 gboolean account_is_literal, account_has_target = FALSE;
1320 gint which = 0;
1321 gchar **p;
1322 gpg_error_t error;
1324 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1325 if ((req = split_input_line(line, " ", 0)) == NULL)
1326 return EPWMD_COMMAND_SYNTAX;
1329 prefix = g_strdup(*req);
1331 if (!prefix) {
1332 g_strfreev(req);
1333 return gpg_error_from_errno(ENOMEM);
1336 account = g_strdup(*req);
1338 if (!account) {
1339 g_free(prefix);
1340 g_strfreev(req);
1341 return gpg_error_from_errno(ENOMEM);
1344 oreq = g_strdupv(req);
1346 if (!oreq) {
1347 g_free(prefix);
1348 g_free(account);
1349 g_strfreev(req);
1350 return gpg_error_from_errno(ENOMEM);
1353 p = req;
1354 again:
1355 account_has_target = FALSE;
1356 account_is_literal = is_literal_element_str(prefix);
1357 n = find_account(doc, &p, &error, &account_has_target);
1359 if (which)
1360 oreq = p;
1361 else
1362 req = p;
1364 if (!n)
1365 goto fail;
1367 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1368 tmp = g_strdup_printf("!%s", prefix);
1370 if (!tmp) {
1371 error = gpg_error_from_errno(ENOMEM);
1372 goto fail;
1375 g_free(prefix);
1376 prefix = tmp;
1379 if (*(p+1)) {
1380 gchar *t;
1382 n = find_elements(doc, n->children, p+1, &error,
1383 NULL, NULL, NULL, NULL);
1385 if (error)
1386 goto fail;
1388 tmp = g_strjoinv("\t", p+1);
1389 if (!tmp) {
1390 error = gpg_error_from_errno(ENOMEM);
1391 goto fail;
1394 t = g_strdup_printf("%s\t%s", prefix, tmp);
1395 if (!t) {
1396 error = gpg_error_from_errno(ENOMEM);
1397 goto fail;
1400 g_free(prefix);
1401 prefix = t;
1402 g_free(tmp);
1405 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1406 error = gpg_error_from_errno(ENOMEM);
1407 goto fail;
1410 if (node_has_child_element(n->children) == FALSE) {
1411 if (append_to_element_list(elements) == FALSE) {
1412 error = gpg_error_from_errno(ENOMEM);
1413 goto fail;
1416 else
1417 error = do_list_recurse(doc, n->children, elements, prefix);
1419 if (error)
1420 goto fail;
1422 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1423 g_free(*oreq);
1424 *oreq = g_strdup_printf("!%s", account);
1426 if (!*oreq) {
1427 error = gpg_error_from_errno(ENOMEM);
1428 goto fail;
1431 p = oreq;
1432 g_free(prefix);
1433 prefix = g_strdup(*oreq);
1435 if (!prefix) {
1436 error = gpg_error_from_errno(ENOMEM);
1437 goto fail;
1440 goto again;
1443 fail:
1444 g_free(prefix);
1445 g_free(account);
1446 g_strfreev(req);
1448 if (oreq)
1449 g_strfreev(oreq);
1451 return error;
1455 * This could be faster especially when finding "target" attributes.
1457 static int list_command(assuan_context_t ctx, char *line)
1459 struct client_s *client = assuan_get_pointer(ctx);
1460 gpg_error_t error;
1461 struct list_element_s *elements = NULL;
1462 GString *string;
1463 gchar *tmp;
1464 gint i, total;
1466 if (disable_list_and_dump == TRUE)
1467 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1469 error = file_modified(client);
1471 if (error) {
1472 log_write("%s: %s", client->filename, pwmd_strerror(error));
1473 return send_error(ctx, error);
1476 if (!*line) {
1477 GString *str;
1479 error = list_accounts(client->doc, &str);
1481 if (error)
1482 return send_error(ctx, error);
1484 error = assuan_send_data(ctx, str->str, str->len);
1485 g_string_free(str, TRUE);
1486 return send_error(ctx, error);
1489 elements = g_malloc0(sizeof(struct list_element_s));
1491 if (!elements) {
1492 error = gpg_error_from_errno(ENOMEM);
1493 goto fail;
1496 is_list_command = TRUE;
1497 error = do_list_command(ctx, client->doc, elements, line);
1499 if (error)
1500 goto fail;
1502 if (!error) {
1503 total = g_slist_length(elements->list);
1505 if (!total) {
1506 error = EPWMD_EMPTY_ELEMENT;
1507 goto fail;
1511 * Find element paths with a target and append those element trees to
1512 * the list.
1514 for (i = 0; i < total; i++) {
1515 gchar **req;
1517 tmp = g_slist_nth_data(elements->list, i);
1518 req = split_input_line(tmp, "\t", 0);
1520 if (!req) {
1521 if (g_str_has_prefix(tmp, "!") == TRUE) {
1522 g_strfreev(req);
1523 continue;
1526 else {
1527 gchar **p;
1529 for (p = req; *p; p++) {
1530 if (g_str_has_prefix(*p, "!") == FALSE)
1531 break;
1534 if (!*p) {
1535 g_strfreev(req);
1536 continue;
1540 g_strfreev(req);
1541 error = do_list_command(ctx, client->doc, elements, tmp);
1543 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1544 goto fail;
1546 total = g_slist_length(elements->list);
1550 string = g_string_new(NULL);
1552 for (i = 0; i < total; i++) {
1553 tmp = g_slist_nth_data(elements->list, i);
1554 g_string_append_printf(string, "%s\n", tmp);
1555 g_free(tmp);
1558 string = g_string_truncate(string, string->len - 1);
1559 error = assuan_send_data(ctx, string->str, string->len);
1560 g_string_free(string, TRUE);
1562 fail:
1563 is_list_command = FALSE;
1565 if (elements) {
1566 if (elements->list)
1567 g_slist_free(elements->list);
1569 if (elements->elements)
1570 g_strfreev(elements->elements);
1572 g_free(elements);
1575 return send_error(ctx, error);
1578 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1579 const gchar *value)
1581 xmlAttrPtr a;
1583 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1584 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1586 if (!a)
1587 return EPWMD_LIBXML_ERROR;
1589 else
1590 xmlNodeSetContent(a->children, (xmlChar *)value);
1592 return 0;
1596 * req[0] - element path
1598 static int attribute_list(assuan_context_t ctx, gchar **req)
1600 struct client_s *client = assuan_get_pointer(ctx);
1601 gchar **attrlist = NULL;
1602 gint i = 0;
1603 gchar **path = NULL;
1604 xmlAttrPtr a;
1605 xmlNodePtr n, an;
1606 gchar *line;
1607 gpg_error_t error;
1609 if (!req || !req[0])
1610 return EPWMD_COMMAND_SYNTAX;
1612 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1614 * The first argument may be only an account.
1616 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1617 return EPWMD_COMMAND_SYNTAX;
1620 n = find_account(client->doc, &path, &error, NULL);
1622 if (!n) {
1623 g_strfreev(path);
1624 return error;
1627 if (path[1]) {
1628 n = find_elements(client->doc, n->children, path+1, &error,
1629 NULL, NULL, NULL, NULL);
1631 if (!n) {
1632 g_strfreev(path);
1633 return error;
1637 g_strfreev(path);
1639 for (a = n->properties; a; a = a->next) {
1640 gchar **pa;
1642 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1643 if (attrlist)
1644 g_strfreev(attrlist);
1646 error = errno;
1647 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1648 return gpg_error_from_errno(error);
1651 attrlist = pa;
1652 an = a->children;
1653 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1655 if (!attrlist[i]) {
1656 g_strfreev(attrlist);
1657 return gpg_error_from_errno(ENOMEM);
1660 attrlist[++i] = NULL;
1663 if (!attrlist)
1664 return EPWMD_EMPTY_ELEMENT;
1666 line = g_strjoinv("\n", attrlist);
1668 if (!line) {
1669 g_strfreev(attrlist);
1670 return gpg_error_from_errno(ENOMEM);
1673 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1674 g_free(line);
1675 g_strfreev(attrlist);
1676 return error;
1680 * req[0] - attribute
1681 * req[1] - element path
1683 static int attribute_delete(struct client_s *client, gchar **req)
1685 xmlAttrPtr a;
1686 xmlNodePtr n;
1687 gchar **path = NULL;
1688 gpg_error_t error;
1690 if (!req || !req[0] || !req[1])
1691 return EPWMD_COMMAND_SYNTAX;
1693 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1695 * The first argument may be only an account.
1697 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1698 return EPWMD_COMMAND_SYNTAX;
1702 * Don't remove the "name" attribute for the account element. To remove an
1703 * account use DELETE <account>.
1705 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1706 error = EPWMD_ATTR_SYNTAX;
1707 goto fail;
1710 n = find_account(client->doc, &path, &error, NULL);
1712 if (!n)
1713 goto fail;
1715 if (path[1]) {
1716 n = find_elements(client->doc, n->children, path+1, &error,
1717 NULL, NULL, NULL, NULL);
1719 if (!n)
1720 goto fail;
1723 g_strfreev(path);
1725 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1726 return EPWMD_ATTR_NOT_FOUND;
1728 if (xmlRemoveProp(a) == -1)
1729 return EPWMD_LIBXML_ERROR;
1731 return 0;
1733 fail:
1734 g_strfreev(path);
1735 return error;
1739 * Creates a "target" attribute. When other commands encounter an element with
1740 * this attribute, the element path is modified to the target value. If the
1741 * source element path doesn't exist when using 'ATTR SET target', it is
1742 * created, but the destination element path must exist.
1744 * req[0] - source element path
1745 * req[1] - destination element path
1747 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1749 gchar **src, **dst, *line;
1750 gpg_error_t error;
1751 xmlNodePtr n;
1753 if (!req || !req[0] || !req[1])
1754 return EPWMD_COMMAND_SYNTAX;
1756 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1758 * The first argument may be only an account.
1760 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1761 return EPWMD_COMMAND_SYNTAX;
1764 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1766 * The first argument may be only an account.
1768 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1769 error = EPWMD_COMMAND_SYNTAX;
1770 goto fail;
1774 n = find_account(client->doc, &dst, &error, NULL);
1777 * Make sure the destination element path exists.
1779 if (!n)
1780 goto fail;
1782 if (dst[1]) {
1783 n = find_elements(client->doc, n->children, dst+1, &error,
1784 NULL, NULL, NULL, NULL);
1786 if (!n)
1787 goto fail;
1790 again:
1791 n = find_account(client->doc, &src, &error, NULL);
1793 if (!n) {
1794 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1795 error = new_account(client->doc, src[0]);
1797 if (error)
1798 goto fail;
1800 goto again;
1802 else
1803 goto fail;
1806 if (src[1]) {
1807 if (!n->children)
1808 n = create_target_elements_cb(n, src+1, &error, NULL);
1809 else
1810 n = find_elements(client->doc, n->children, src+1, &error,
1811 NULL, NULL, create_target_elements_cb, NULL);
1813 if (!n)
1814 goto fail;
1817 * Reset the position of the element tree now that the elements
1818 * have been created.
1820 n = find_account(client->doc, &src, &error, NULL);
1822 if (!n)
1823 goto fail;
1825 n = find_elements(client->doc, n->children, src+1, &error,
1826 NULL, NULL, NULL, NULL);
1828 if (!n)
1829 goto fail;
1832 line = g_strjoinv("\t", dst);
1833 error = add_attribute(n, "target", line);
1835 if (error) {
1836 g_free(line);
1837 goto fail;
1840 g_free(line);
1841 g_strfreev(src);
1842 g_strfreev(dst);
1843 return 0;
1845 fail:
1846 g_strfreev(src);
1847 g_strfreev(dst);
1848 return error;
1852 * req[0] - account name
1853 * req[1] - new name
1855 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1857 gpg_error_t error;
1858 gchar **tmp;
1859 xmlNodePtr n;
1861 tmp = g_strdupv(req);
1863 if (!tmp)
1864 return gpg_error_from_errno(ENOMEM);
1866 n = find_account(client->doc, &tmp, &error, NULL);
1867 g_strfreev(tmp);
1869 if (!n)
1870 return error;
1872 if (g_utf8_collate(req[0], req[1]) == 0)
1873 return 0;
1876 * Will not overwrite an existing account.
1878 tmp = g_strdupv(req+1);
1880 if (!tmp)
1881 return gpg_error_from_errno(ENOMEM);
1883 n = find_account(client->doc, &tmp, &error, NULL);
1884 g_strfreev(tmp);
1886 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1887 return error;
1889 if (n)
1890 return EPWMD_ACCOUNT_EXISTS;
1893 * Whitespace not allowed in account names.
1895 if (contains_whitespace(req[1]) == TRUE)
1896 return EPWMD_ATTR_SYNTAX;
1898 tmp = g_strdupv(req);
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 (!n)
1907 return EPWMD_ELEMENT_NOT_FOUND;
1909 return add_attribute(n, "name", req[1]);
1913 * req[0] - attribute
1914 * req[1] - element path
1916 static int attribute_get(assuan_context_t ctx, gchar **req)
1918 struct client_s *client = assuan_get_pointer(ctx);
1919 xmlNodePtr n;
1920 xmlChar *a;
1921 gchar **path= NULL;
1922 gpg_error_t error;
1924 if (!req || !req[0] || !req[1])
1925 return EPWMD_COMMAND_SYNTAX;
1927 if (strchr(req[1], '\t')) {
1928 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1929 return EPWMD_COMMAND_SYNTAX;
1931 else {
1932 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1933 return EPWMD_COMMAND_SYNTAX;
1936 n = find_account(client->doc, &path, &error, NULL);
1938 if (!n)
1939 goto fail;
1941 if (path[1]) {
1942 n = find_elements(client->doc, n->children, path+1, &error,
1943 NULL, NULL, NULL, NULL);
1945 if (!n)
1946 goto fail;
1949 g_strfreev(path);
1951 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1952 return EPWMD_ATTR_NOT_FOUND;
1954 error = assuan_send_data(ctx, a, xmlStrlen(a));
1955 xmlFree(a);
1956 return error;
1958 fail:
1959 g_strfreev(path);
1960 return error;
1964 * req[0] - attribute
1965 * req[1] - element path
1966 * req[2] - value
1968 static int attribute_set(struct client_s *client, gchar **req)
1970 gchar **path = NULL;
1971 gpg_error_t error;
1972 xmlNodePtr n;
1974 if (!req || !req[0] || !req[1] || !req[2])
1975 return EPWMD_COMMAND_SYNTAX;
1978 * Reserved attribute names.
1980 if (g_utf8_collate(req[0], "name") == 0) {
1982 * Only reserved for the account element. Not the rest of the
1983 * document.
1985 if (strchr(req[1], '\t') == NULL)
1986 return name_attribute(client, req + 1);
1988 else if (g_utf8_collate(req[0], "target") == 0)
1989 return target_attribute(client, req + 1);
1991 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1993 * The first argument may be only an account.
1995 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1996 return EPWMD_COMMAND_SYNTAX;
1999 n = find_account(client->doc, &path, &error, NULL);
2001 if (!n)
2002 goto fail;
2004 if (path[1]) {
2005 n = find_elements(client->doc, n->children, path+1, &error,
2006 NULL, NULL, NULL, NULL);
2008 if (!n)
2009 goto fail;
2012 g_strfreev(path);
2013 return add_attribute(n, req[0], req[2]);
2015 fail:
2016 g_strfreev(path);
2017 return error;
2021 * req[0] - command
2022 * req[1] - attribute name or element path if command is LIST
2023 * req[2] - element path
2024 * req[2] - element path or value
2026 static int attr_command(assuan_context_t ctx, char *line)
2028 struct client_s *client = assuan_get_pointer(ctx);
2029 gchar **req = split_input_line(line, " ", 4);
2030 gpg_error_t error = 0;
2032 error = file_modified(client);
2034 if (error) {
2035 log_write("%s: %s", client->filename ? client->filename : "",
2036 pwmd_strerror(error));
2037 g_strfreev(req);
2038 return send_error(ctx, error);
2041 if (!req || !req[0] || !req[1]) {
2042 g_strfreev(req);
2043 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2046 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2047 error = attribute_set(client, req+1);
2048 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2049 error = attribute_get(ctx, req+1);
2050 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2051 error = attribute_delete(client, req+1);
2052 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2053 error = attribute_list(ctx, req+1);
2054 else
2055 error = EPWMD_COMMAND_SYNTAX;
2057 g_strfreev(req);
2058 return send_error(ctx, error);
2061 static int iscached_command(assuan_context_t ctx, char *line)
2063 gchar **req = split_input_line(line, " ", 0);
2064 guchar md5file[16];
2066 if (!req || !*req) {
2067 g_strfreev(req);
2068 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2071 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2072 g_strfreev(req);
2074 if (cache_iscached(md5file) == FALSE)
2075 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2077 return 0;
2080 void send_cache_status(assuan_context_t ctx)
2082 char *tmp = print_fmt("%li %li",
2083 cache_file_count(), cache_size / sizeof(file_cache_t) - cache_file_count());
2085 if (ctx)
2086 assuan_write_status(ctx, "CACHE", tmp);
2089 static int clearcache_command(assuan_context_t ctx, char *line)
2091 struct client_s *client = assuan_get_pointer(ctx);
2092 gchar **req = split_input_line(line, " ", 0);
2093 guchar md5file[16];
2095 if (!req || !*req) {
2096 g_strfreev(req);
2097 cache_clear(client->md5file, 2);
2098 send_cache_status(ctx);
2099 return 0;
2102 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2103 g_strfreev(req);
2104 cache_clear(md5file, 1);
2105 send_cache_status(ctx);
2106 return 0;
2109 static int cachetimeout_command(assuan_context_t ctx, char *line)
2111 guchar md5file[16];
2112 glong timeout;
2113 gchar **req = split_input_line(line, " ", 0);
2114 gchar *p;
2116 if (!req || !*req || !req[1]) {
2117 g_strfreev(req);
2118 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2121 errno = 0;
2122 timeout = strtol(req[0], &p, 10);
2124 if (errno != 0 || *p != 0) {
2125 g_strfreev(req);
2126 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2129 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2130 g_strfreev(req);
2132 if (cache_set_timeout(md5file, timeout) == FALSE)
2133 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2135 send_cache_status(ctx);
2136 return 0;
2139 static int dump_command(assuan_context_t ctx, char *line)
2141 xmlChar *xml;
2142 gssize len;
2143 struct client_s *client = assuan_get_pointer(ctx);
2144 gpg_error_t error;
2146 if (disable_list_and_dump == TRUE)
2147 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2149 error = file_modified(client);
2151 if (error) {
2152 log_write("%s: %s", client->filename ? client->filename : "",
2153 pwmd_strerror(error));
2154 return send_error(ctx, error);
2157 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2158 error = assuan_send_data(ctx, xml, len);
2159 xmlFree(xml);
2160 return error;
2163 static int getconfig_command(assuan_context_t ctx, gchar *line)
2165 struct client_s *client = assuan_get_pointer(ctx);
2166 gpg_error_t error = 0;
2167 gchar *p, *tmp;
2169 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2170 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2172 p = get_key_file_string(client->filename ? client->filename : "default", line);
2174 if (!p)
2175 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2177 tmp = expand_homedir(p);
2178 g_free(p);
2179 p = tmp;
2180 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2181 g_free(p);
2182 return send_error(ctx, error);
2185 void cleanup_assuan(assuan_context_t ctx)
2187 struct client_s *cl = assuan_get_pointer(ctx);
2189 cleanup(cl);
2192 gpg_error_t register_commands(assuan_context_t ctx)
2194 static struct {
2195 const char *name;
2196 int (*handler)(assuan_context_t, char *line);
2197 } table[] = {
2198 { "OPEN", open_command },
2199 { "SAVE", save_command },
2200 { "LIST", list_command },
2201 { "REALPATH", realpath_command },
2202 { "STORE", store_command },
2203 { "DELETE", delete_command },
2204 { "GET", get_command },
2205 { "ATTR", attr_command },
2206 { "ISCACHED", iscached_command },
2207 { "CLEARCACHE", clearcache_command },
2208 { "CACHETIMEOUT", cachetimeout_command },
2209 { "GETCONFIG", getconfig_command },
2210 { "DUMP", dump_command },
2211 { "INPUT", NULL },
2212 { "OUTPUT", NULL },
2213 { NULL, NULL }
2215 int i, rc;
2217 for (i=0; table[i].name; i++) {
2218 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2220 if (rc)
2221 return rc;
2224 return assuan_register_bye_notify(ctx, cleanup_assuan);
2227 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2228 guchar *key)
2230 guchar *iv;
2231 void *inbuf;
2232 gsize insize, len;
2233 guchar tkey[gcrykeysize];
2234 struct file_header_s {
2235 guint iter;
2236 guchar iv[gcryblocksize];
2237 } file_header;
2238 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2239 gcry_cipher_hd_t gh;
2240 guint iter = 0, n_iter = 0;
2241 gint iter_progress;
2242 #ifdef HAVE_ZLIB_H
2243 void *outbuf = NULL;
2244 gint zerror = 0;
2245 glong outsize = 0;
2246 #endif
2248 if (!ctx) {
2249 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2251 if (gcryerrno)
2252 return gcryerrno;
2254 else
2255 gh = client->gh;
2257 lseek(fd, 0, SEEK_SET);
2258 insize = st.st_size - sizeof(struct file_header_s);
2259 iv = gcry_malloc(gcryblocksize);
2261 if (!iv) {
2262 if (!ctx)
2263 gcry_cipher_close(gh);
2265 return gpg_error_from_errno(ENOMEM);
2268 len = read(fd, &file_header, sizeof(struct file_header_s));
2270 if (len != sizeof(file_header)) {
2271 len = errno;
2273 if (!ctx)
2274 gcry_cipher_close(gh);
2276 gcry_free(iv);
2277 errno = len;
2278 return gpg_error_from_errno(errno);
2281 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2282 inbuf = gcry_malloc(insize);
2284 if (!inbuf) {
2285 if (!ctx)
2286 gcry_cipher_close(gh);
2288 gcry_free(iv);
2289 return gpg_error_from_errno(ENOMEM);
2292 len = read(fd, inbuf, insize);
2294 if (len != insize) {
2295 len = errno;
2297 if (!ctx)
2298 gcry_cipher_close(gh);
2300 gcry_free(iv);
2301 errno = len;
2302 return gpg_error_from_errno(errno);
2305 memcpy(tkey, key, sizeof(tkey));
2306 tkey[0] ^= 1;
2308 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2309 if (!ctx) {
2310 gcry_cipher_close(gh);
2311 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2313 else
2314 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2316 gcry_free(inbuf);
2317 gcry_free(iv);
2318 return gcryerrno;
2321 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2322 if (!ctx) {
2323 gcry_cipher_close(gh);
2324 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2326 else
2327 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2329 gcry_free(inbuf);
2330 gcry_free(iv);
2332 if (!ctx)
2333 gcry_cipher_close(gh);
2335 return gcryerrno;
2338 iter_progress = get_key_file_integer("default", "iteration_progress");
2340 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress)
2341 assuan_write_status(client->ctx, "DECRYPT", "0");
2343 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2344 if (!ctx) {
2345 gcry_cipher_close(gh);
2346 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2348 else
2349 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2351 gcry_free(inbuf);
2352 gcry_free(iv);
2353 return gcryerrno;
2356 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2357 if (!ctx) {
2358 gcry_cipher_close(gh);
2359 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2361 else
2362 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2364 gcry_free(inbuf);
2365 gcry_free(iv);
2366 return gcryerrno;
2369 while (iter < file_header.iter) {
2370 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2371 if (!(iter % iter_progress))
2372 assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2373 ++n_iter * iter_progress));
2376 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2377 if (!ctx) {
2378 gcry_cipher_close(gh);
2379 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2381 else
2382 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2384 gcry_free(inbuf);
2385 gcry_free(iv);
2386 return gcryerrno;
2389 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2390 if (!ctx) {
2391 gcry_cipher_close(gh);
2392 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2394 else
2395 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2397 gcry_free(inbuf);
2398 gcry_free(iv);
2399 return gcryerrno;
2402 iter++;
2405 if (ctx && iter_progress && file_header.iter >= iter_progress)
2406 assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2408 gcry_free(iv);
2410 #ifdef HAVE_ZLIB_H
2411 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2413 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2415 if (zerror == Z_MEM_ERROR) {
2416 gcry_free(inbuf);
2417 return gpg_error_from_errno(ENOMEM);
2419 else if (zerror != Z_DATA_ERROR) {
2420 gcry_free(inbuf);
2422 if (!ctx)
2423 gcry_cipher_close(gh);
2425 return EPWMD_BADKEY;
2428 else {
2429 gcry_free(inbuf);
2430 inbuf = outbuf;
2431 insize = outsize;
2433 #endif
2435 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2436 gcry_free(inbuf);
2438 if (!ctx)
2439 gcry_cipher_close(gh);
2441 return EPWMD_BADKEY;
2444 if (ctx) {
2445 client->xml = inbuf;
2446 client->len = insize;
2448 else {
2449 gcry_cipher_close(gh);
2450 gcry_free(inbuf);
2453 return 0;