Merge branch 'pthreads'
[pwmd.git] / src / commands.c
blob04a92bbc7ca7328c8db8f144ee13cc2241aef5f0
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 struct file_header_s {
596 guint iter;
597 guchar iv[gcryblocksize];
598 } file_header;
600 if (insize / gcryblocksize) {
601 len = (insize / gcryblocksize) * gcryblocksize;
603 if (insize % gcryblocksize)
604 len += gcryblocksize;
608 * Resize the existing xml buffer to the block size required by gcrypt
609 * rather than duplicating it and wasting memory.
611 inbuf = gcry_realloc(data, len);
613 if (!inbuf)
614 return gpg_error_from_errno(ENOMEM);
616 insize = len;
617 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
618 memcpy(tkey, shakey, sizeof(tkey));
619 tkey[0] ^= 1;
621 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
622 gcry_free(inbuf);
623 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
624 return gcryerrno;
627 file_header.iter = iter;
629 if (client)
630 iter_progress = get_key_file_integer("default", "iteration_progress");
632 if (client && iter_progress && file_header.iter >= iter_progress)
633 assuan_write_status(client->ctx, "ENCRYPT", "0");
635 while (xiter < file_header.iter) {
636 if (client && iter_progress > 0 && xiter >= iter_progress) {
637 if (!(xiter % iter_progress))
638 assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
639 ++n_iter * iter_progress));
642 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
643 sizeof(file_header.iv)))) {
644 gcry_free(inbuf);
645 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
646 return gcryerrno;
649 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
650 == FALSE) {
651 gcry_free(inbuf);
652 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
653 return gcryerrno;
656 xiter++;
659 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
660 sizeof(file_header.iv)))) {
661 gcry_free(inbuf);
662 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
663 return gcryerrno;
666 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
667 gcry_free(inbuf);
668 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
669 return gcryerrno;
672 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
673 gcry_free(inbuf);
674 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
675 return gcryerrno;
678 if (client && iter_progress && file_header.iter >= iter_progress)
679 assuan_write_status(client->ctx, "ENCRYPT",
680 print_fmt("%i", file_header.iter));
682 if (filename) {
683 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
684 error = errno;
685 gcry_free(inbuf);
686 p = strrchr(filename, '/');
687 p++;
688 log_write("%s: %s", p, strerror(errno));
689 return gpg_error_from_errno(error);
692 else
694 * xml_import() from command line.
696 fd = STDOUT_FILENO;
698 len = write(fd, &file_header, sizeof(struct file_header_s));
700 if (len != sizeof(file_header)) {
701 len = errno;
703 if (filename)
704 close(fd);
706 gcry_free(inbuf);
707 return gpg_error_from_errno(len);
710 len = write(fd, inbuf, insize);
712 if (len != insize) {
713 len = errno;
715 if (filename)
716 close(fd);
718 gcry_free(inbuf);
719 return gpg_error_from_errno(len);
722 if (filename)
723 close(fd);
725 gcry_free(inbuf);
726 return 0;
729 static int save_command(assuan_context_t ctx, char *line)
731 gpointer xmlbuf;
732 xmlChar *p;
733 gint len;
734 gint cached = 0;
735 guchar shakey[gcrykeysize];
736 gint iter;
737 struct stat st;
738 struct client_s *client = assuan_get_pointer(ctx);
739 gpg_error_t error;
740 gint timeout;
741 gpointer outbuf;
742 glong outsize = 0;
743 gint zerror;
745 MUTEX_LOCK(ctx);
746 error = file_modified(client);
748 if (error) {
749 log_write("%s: %s", client->filename ? client->filename : "",
750 pwmd_strerror(error));
751 return send_error(ctx, error);
754 if (stat(client->filename, &st) == -1 && errno != ENOENT)
755 return send_syserror(ctx, errno);
757 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
758 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
759 return send_error(ctx, EPWMD_INVALID_FILENAME);
762 if (!line || !*line) {
763 if (cache_get_key(client->md5file, shakey) == FALSE)
764 return send_error(ctx, EPWMD_KEY);
766 cached = 1;
768 else {
769 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
770 memset(line, 0, strlen(line));
773 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
774 xmlbuf = p;
776 iter = get_key_file_integer(client->filename, "compression_level");
778 if (iter < 0)
779 iter = 0;
781 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
782 memset(shakey, 0, sizeof(shakey));
783 xmlFree(xmlbuf);
785 if (zerror == Z_MEM_ERROR) {
786 return send_syserror(ctx, ENOMEM);
788 else
789 return send_error(ctx, GPG_ERR_COMPR_ALGO);
791 else {
792 gcry_free(xmlbuf);
793 xmlbuf = outbuf;
794 len = outsize;
797 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
798 iter = 0;
800 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
802 if (error) {
803 memset(shakey, 0, sizeof(shakey));
804 return send_error(ctx, error);
807 stat(client->filename, &st);
808 client->mtime = st.st_mtime;
809 timeout = get_key_file_integer(client->filename, "cache_timeout");
811 if (cached) {
812 memset(shakey, 0, sizeof(shakey));
813 cache_reset_timeout(client->md5file, timeout);
814 client->new = FALSE;
815 return 0;
818 if (cache_update_key(client->md5file, shakey) == FALSE) {
819 memset(shakey, 0, sizeof(shakey));
820 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
821 return send_error(ctx, EPWMD_MAX_SLOTS);
824 client->new = FALSE;
825 memset(shakey, 0, sizeof(shakey));
826 cache_reset_timeout(client->md5file, timeout);
827 return 0;
830 static gboolean contains_whitespace(const gchar *str)
832 const gchar *p = str;
833 gunichar c;
834 glong len;
836 len = g_utf8_strlen(p++, -1) -1;
838 while (len--) {
839 c = g_utf8_get_char(p++);
841 if (g_unichar_isspace(c))
842 return TRUE;
845 return FALSE;
848 static int delete_command(assuan_context_t ctx, char *line)
850 struct client_s *client = assuan_get_pointer(ctx);
851 gchar **req;
852 gpg_error_t error;
853 xmlNodePtr n;
855 error = file_modified(client);
857 if (error) {
858 log_write("%s: %s", client->filename ? client->filename : "",
859 pwmd_strerror(error));
860 return send_error(ctx, error);
863 if (strchr(line, '\t'))
864 req = split_input_line(line, "\t", -1);
865 else
866 req = split_input_line(line, " ", -1);
868 if (!req || !*req)
869 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
871 n = find_account(client->doc, &req, &error, NULL);
873 if (!n) {
874 g_strfreev(req);
875 return send_error(ctx, error);
879 * No sub-node defined. Remove the entire node (account).
881 if (!req[1]) {
882 if (n) {
883 xmlUnlinkNode(n);
884 xmlFreeNode(n);
887 g_strfreev(req);
888 return 0;
891 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
892 g_strfreev(req);
894 if (!n)
895 return send_error(ctx, error);
897 if (n) {
898 xmlUnlinkNode(n);
899 xmlFreeNode(n);
902 return 0;
905 static int store_command(assuan_context_t ctx, char *line)
907 struct client_s *client = assuan_get_pointer(ctx);
908 gchar **req;
909 gpg_error_t error;
910 guchar *result;
911 gsize len;
912 xmlNodePtr n;
914 error = file_modified(client);
916 if (error) {
917 log_write("%s: %s", client->filename ? client->filename : "",
918 pwmd_strerror(error));
919 return send_error(ctx, error);
922 error = assuan_inquire(ctx, "STORE", &result, &len, 0);
924 if (error)
925 return send_error(ctx, error);
927 req = split_input_line((gchar *)result, "\t", 0);
928 #ifndef MEM_DEBUG
929 xfree(result);
930 #else
931 free(result);
932 #endif
934 if (!req || !*req)
935 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
937 again:
938 n = find_account(client->doc, &req, &error, NULL);
940 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
941 if (contains_whitespace(*req) == TRUE) {
942 g_strfreev(req);
943 return send_error(ctx, EPWMD_INVALID_ELEMENT);
946 error = new_account(client->doc, *req);
948 if (error) {
949 g_strfreev(req);
950 return send_error(ctx, error);
953 goto again;
956 if (!n) {
957 g_strfreev(req);
958 return send_error(ctx, error);
961 if (req[1]) {
962 if (!n->children)
963 create_elements_cb(n, req+1, &error, NULL);
964 else
965 find_elements(client->doc, n->children, req+1, &error,
966 NULL, NULL, create_elements_cb, NULL);
969 g_strfreev(req);
970 return send_error(ctx, error);
973 static int get_command(assuan_context_t ctx, char *line)
975 struct client_s *client = assuan_get_pointer(ctx);
976 gchar **req;
977 gpg_error_t error;
978 xmlNodePtr n;
980 error = file_modified(client);
982 if (error) {
983 log_write("%s: %s", client->filename ? client->filename : "",
984 pwmd_strerror(error));
985 return send_error(ctx, error);
988 req = split_input_line(line, "\t", -1);
990 if (!req || !*req) {
991 g_strfreev(req);
992 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
995 n = find_account(client->doc, &req, &error, NULL);
997 if (!n) {
998 g_strfreev(req);
999 return send_error(ctx, error);
1002 if (req[1])
1003 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
1005 g_strfreev(req);
1007 if (error)
1008 return send_error(ctx, error);
1010 if (!n || !n->children)
1011 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1013 n = n->children;
1015 if (!n || !n->content || !*n->content)
1016 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1018 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1019 return send_error(ctx, error);
1022 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1024 xmlChar *p = path;
1025 gint n;
1026 gchar *buf;
1028 if (!p)
1029 return NULL;
1031 for (n = 0; *p && n < 3; p++) {
1032 if (*p == '/')
1033 n++;
1036 if (strstr((gchar *)p, "text()") != NULL)
1037 p[xmlStrlen(p) - 7] = 0;
1039 for (n = 0; p[n]; n++) {
1040 if (p[n] == '/')
1041 p[n] = '\t';
1044 buf = g_strdup_printf("%s\t%s", account, p);
1045 return buf;
1048 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1050 gchar **a;
1051 va_list ap;
1052 gchar *buf;
1053 gint len = *array ? g_strv_length(*array) : 0;
1054 gint ret;
1056 if (!fmt)
1057 return FALSE;
1059 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1060 return FALSE;
1062 va_start(ap, fmt);
1063 ret = g_vasprintf(&buf, fmt, ap);
1064 va_end(ap);
1066 if (ret == -1)
1067 return FALSE;
1069 a[len++] = buf;
1070 a[len] = NULL;
1071 *array = a;
1072 return TRUE;
1075 struct realpath_s {
1076 gchar *account;
1079 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1080 gpg_error_t *error, void *data)
1082 struct realpath_s *rp = data;
1084 if (rp->account)
1085 g_free(rp->account);
1087 rp->account = g_strdup(req[0]);
1089 if (!rp->account) {
1090 *error = gpg_error_from_errno(ENOMEM);
1091 return NULL;
1094 return node;
1097 static int realpath_command(assuan_context_t ctx, char *line)
1099 gpg_error_t error;
1100 struct client_s *client = assuan_get_pointer(ctx);
1101 xmlChar *p;
1102 gchar **req;
1103 gchar *result, *t;
1104 gint i;
1105 xmlNodePtr n;
1106 struct realpath_s *rp;
1107 GString *string;
1109 error = file_modified(client);
1111 if (error) {
1112 log_write("%s: %s", client->filename ? client->filename : "",
1113 pwmd_strerror(error));
1114 return send_error(ctx, error);
1117 if (strchr(line, '\t') != NULL) {
1118 if ((req = split_input_line(line, "\t", 0)) == NULL)
1119 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1121 else {
1122 if ((req = split_input_line(line, " ", 0)) == NULL)
1123 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1126 n = find_account(client->doc, &req, &error, NULL);
1128 if (!n) {
1129 g_strfreev(req);
1130 return send_error(ctx, error);
1133 rp = g_malloc(sizeof(struct realpath_s));
1135 if (!rp) {
1136 g_strfreev(req);
1137 return send_syserror(ctx, ENOMEM);
1140 rp->account = g_strdup(req[0]);
1142 if (!rp->account) {
1143 g_strfreev(req);
1144 return send_syserror(ctx, ENOMEM);
1147 if (req[1]) {
1148 n = find_elements(client->doc, n->children, req+1, &error,
1149 NULL, realpath_elements_cb, NULL, rp);
1151 if (!n) {
1152 g_free(rp->account);
1153 g_free(rp);
1154 g_strfreev(req);
1155 return send_error(ctx, error);
1159 p = xmlGetNodePath(n);
1160 result = element_path_to_req(rp->account, p);
1162 if (!result) {
1163 g_free(result);
1164 g_free(rp->account);
1165 g_free(rp);
1166 g_strfreev(req);
1167 xmlFree(p);
1168 return send_syserror(ctx, ENOMEM);
1171 string = g_string_new(result);
1172 g_free(result);
1173 g_free(rp->account);
1174 g_free(rp);
1175 g_strfreev(req);
1176 xmlFree(p);
1177 i = 0;
1179 again:
1180 for (t = string->str + i; *t; t++, i++) {
1181 if (!i || *t == '\t') {
1182 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1183 goto again;
1187 error = assuan_send_data(ctx, string->str, string->len);
1188 g_string_free(string, TRUE);
1189 return send_error(ctx, error);
1192 struct list_element_s {
1193 GSList *list;
1194 gchar **elements;
1197 static gboolean append_to_element_list(struct list_element_s *elements)
1199 gchar *tmp;
1200 gint i, total;
1201 gchar *a;
1202 GSList *list;
1204 if (!elements || !elements->elements)
1205 return TRUE;
1207 tmp = g_strjoinv("\t", elements->elements);
1209 if (!tmp)
1210 return FALSE;
1212 g_strfreev(elements->elements);
1213 elements->elements = NULL;
1214 total = g_slist_length(elements->list);
1215 a = g_utf8_collate_key(tmp, -1);
1217 if (!a) {
1218 g_free(tmp);
1219 return FALSE;
1223 * Removes duplicate element paths from the list. This is needed when
1224 * appending an element tree from list_command(). The glib docs recommend
1225 * using g_utf8_collate_key() for a large number of strings.
1227 for (i = 0; i < total; i++) {
1228 gchar *p = g_slist_nth_data(elements->list, i);
1229 gchar *b = g_utf8_collate_key(p, -1);
1231 if (!b) {
1232 g_free(a);
1233 g_free(tmp);
1234 return FALSE;
1237 if (strcmp(a, b) == 0) {
1238 g_free(a);
1239 g_free(b);
1240 g_free(tmp);
1241 return TRUE;
1244 g_free(b);
1247 g_free(a);
1248 list = g_slist_append(elements->list, tmp);
1250 if (!list)
1251 return FALSE;
1253 elements->list = list;
1254 return TRUE;
1257 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1258 struct list_element_s *elements, gchar *prefix)
1260 xmlNodePtr n;
1261 gpg_error_t error;
1263 if (append_to_element_list(elements) == FALSE)
1264 return gpg_error_from_errno(ENOMEM);
1266 for (n = node; n; n = n->next) {
1267 if (n->type == XML_ELEMENT_NODE) {
1268 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1269 gchar *tmp;
1271 if (content) {
1272 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1273 return gpg_error_from_errno(ENOMEM);
1275 if (append_to_element_list(elements) == FALSE)
1276 return gpg_error_from_errno(ENOMEM);
1279 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1281 if (!tmp)
1282 return gpg_error_from_errno(ENOMEM);
1284 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1285 g_free(tmp);
1286 return gpg_error_from_errno(ENOMEM);
1289 if (n->children) {
1290 error = do_list_recurse(doc, n->children, elements, tmp);
1291 g_free(tmp);
1293 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1294 return error;
1296 else
1297 g_free(tmp);
1299 if (append_to_element_list(elements) == FALSE)
1300 return gpg_error_from_errno(ENOMEM);
1304 return 0;
1307 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1308 struct list_element_s *elements, char *line)
1310 gchar *prefix = NULL, *account;
1311 gchar **req = NULL, **oreq = NULL, *tmp;
1312 xmlNodePtr n;
1313 gboolean account_is_literal, account_has_target = FALSE;
1314 gint which = 0;
1315 gchar **p;
1316 gpg_error_t error;
1318 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1319 if ((req = split_input_line(line, " ", 0)) == NULL)
1320 return EPWMD_COMMAND_SYNTAX;
1323 prefix = g_strdup(*req);
1325 if (!prefix) {
1326 g_strfreev(req);
1327 return gpg_error_from_errno(ENOMEM);
1330 account = g_strdup(*req);
1332 if (!account) {
1333 g_free(prefix);
1334 g_strfreev(req);
1335 return gpg_error_from_errno(ENOMEM);
1338 oreq = g_strdupv(req);
1340 if (!oreq) {
1341 g_free(prefix);
1342 g_free(account);
1343 g_strfreev(req);
1344 return gpg_error_from_errno(ENOMEM);
1347 p = req;
1348 again:
1349 account_has_target = FALSE;
1350 account_is_literal = is_literal_element_str(prefix);
1351 n = find_account(doc, &p, &error, &account_has_target);
1353 if (which)
1354 oreq = p;
1355 else
1356 req = p;
1358 if (!n)
1359 goto fail;
1361 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1362 tmp = g_strdup_printf("!%s", prefix);
1364 if (!tmp) {
1365 error = gpg_error_from_errno(ENOMEM);
1366 goto fail;
1369 g_free(prefix);
1370 prefix = tmp;
1373 if (*(p+1)) {
1374 gchar *t;
1376 n = find_elements(doc, n->children, p+1, &error,
1377 NULL, NULL, NULL, NULL);
1379 if (error)
1380 goto fail;
1382 tmp = g_strjoinv("\t", p+1);
1383 if (!tmp) {
1384 error = gpg_error_from_errno(ENOMEM);
1385 goto fail;
1388 t = g_strdup_printf("%s\t%s", prefix, tmp);
1389 if (!t) {
1390 error = gpg_error_from_errno(ENOMEM);
1391 goto fail;
1394 g_free(prefix);
1395 prefix = t;
1396 g_free(tmp);
1399 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1400 error = gpg_error_from_errno(ENOMEM);
1401 goto fail;
1404 if (node_has_child_element(n->children) == FALSE) {
1405 if (append_to_element_list(elements) == FALSE) {
1406 error = gpg_error_from_errno(ENOMEM);
1407 goto fail;
1410 else
1411 error = do_list_recurse(doc, n->children, elements, prefix);
1413 if (error)
1414 goto fail;
1416 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1417 g_free(*oreq);
1418 *oreq = g_strdup_printf("!%s", account);
1420 if (!*oreq) {
1421 error = gpg_error_from_errno(ENOMEM);
1422 goto fail;
1425 p = oreq;
1426 g_free(prefix);
1427 prefix = g_strdup(*oreq);
1429 if (!prefix) {
1430 error = gpg_error_from_errno(ENOMEM);
1431 goto fail;
1434 goto again;
1437 fail:
1438 g_free(prefix);
1439 g_free(account);
1440 g_strfreev(req);
1442 if (oreq)
1443 g_strfreev(oreq);
1445 return error;
1449 * This could be faster especially when finding "target" attributes.
1451 static int list_command(assuan_context_t ctx, char *line)
1453 struct client_s *client = assuan_get_pointer(ctx);
1454 gpg_error_t error;
1455 struct list_element_s *elements = NULL;
1456 GString *string;
1457 gchar *tmp;
1458 gint i, total;
1460 if (disable_list_and_dump == TRUE)
1461 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1463 error = file_modified(client);
1465 if (error) {
1466 log_write("%s: %s", client->filename, pwmd_strerror(error));
1467 return send_error(ctx, error);
1470 if (!*line) {
1471 GString *str;
1473 error = list_accounts(client->doc, &str);
1475 if (error)
1476 return send_error(ctx, error);
1478 error = assuan_send_data(ctx, str->str, str->len);
1479 g_string_free(str, TRUE);
1480 return send_error(ctx, error);
1483 elements = g_malloc0(sizeof(struct list_element_s));
1485 if (!elements) {
1486 error = gpg_error_from_errno(ENOMEM);
1487 goto fail;
1490 is_list_command = TRUE;
1491 error = do_list_command(ctx, client->doc, elements, line);
1493 if (error)
1494 goto fail;
1496 if (!error) {
1497 total = g_slist_length(elements->list);
1499 if (!total) {
1500 error = EPWMD_EMPTY_ELEMENT;
1501 goto fail;
1505 * Find element paths with a target and append those element trees to
1506 * the list.
1508 for (i = 0; i < total; i++) {
1509 gchar **req;
1511 tmp = g_slist_nth_data(elements->list, i);
1512 req = split_input_line(tmp, "\t", 0);
1514 if (!req) {
1515 if (g_str_has_prefix(tmp, "!") == TRUE) {
1516 g_strfreev(req);
1517 continue;
1520 else {
1521 gchar **p;
1523 for (p = req; *p; p++) {
1524 if (g_str_has_prefix(*p, "!") == FALSE)
1525 break;
1528 if (!*p) {
1529 g_strfreev(req);
1530 continue;
1534 g_strfreev(req);
1535 error = do_list_command(ctx, client->doc, elements, tmp);
1537 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1538 goto fail;
1540 total = g_slist_length(elements->list);
1544 string = g_string_new(NULL);
1546 for (i = 0; i < total; i++) {
1547 tmp = g_slist_nth_data(elements->list, i);
1548 g_string_append_printf(string, "%s\n", tmp);
1549 g_free(tmp);
1552 string = g_string_truncate(string, string->len - 1);
1553 error = assuan_send_data(ctx, string->str, string->len);
1554 g_string_free(string, TRUE);
1556 fail:
1557 is_list_command = FALSE;
1559 if (elements) {
1560 if (elements->list)
1561 g_slist_free(elements->list);
1563 if (elements->elements)
1564 g_strfreev(elements->elements);
1566 g_free(elements);
1569 return send_error(ctx, error);
1572 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1573 const gchar *value)
1575 xmlAttrPtr a;
1577 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1578 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1580 if (!a)
1581 return EPWMD_LIBXML_ERROR;
1583 else
1584 xmlNodeSetContent(a->children, (xmlChar *)value);
1586 return 0;
1590 * req[0] - element path
1592 static int attribute_list(assuan_context_t ctx, gchar **req)
1594 struct client_s *client = assuan_get_pointer(ctx);
1595 gchar **attrlist = NULL;
1596 gint i = 0;
1597 gchar **path = NULL;
1598 xmlAttrPtr a;
1599 xmlNodePtr n, an;
1600 gchar *line;
1601 gpg_error_t error;
1603 if (!req || !req[0])
1604 return EPWMD_COMMAND_SYNTAX;
1606 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1608 * The first argument may be only an account.
1610 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1611 return EPWMD_COMMAND_SYNTAX;
1614 n = find_account(client->doc, &path, &error, NULL);
1616 if (!n) {
1617 g_strfreev(path);
1618 return error;
1621 if (path[1]) {
1622 n = find_elements(client->doc, n->children, path+1, &error,
1623 NULL, NULL, NULL, NULL);
1625 if (!n) {
1626 g_strfreev(path);
1627 return error;
1631 g_strfreev(path);
1633 for (a = n->properties; a; a = a->next) {
1634 gchar **pa;
1636 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1637 if (attrlist)
1638 g_strfreev(attrlist);
1640 error = errno;
1641 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1642 return gpg_error_from_errno(error);
1645 attrlist = pa;
1646 an = a->children;
1647 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1649 if (!attrlist[i]) {
1650 g_strfreev(attrlist);
1651 return gpg_error_from_errno(ENOMEM);
1654 attrlist[++i] = NULL;
1657 if (!attrlist)
1658 return EPWMD_EMPTY_ELEMENT;
1660 line = g_strjoinv("\n", attrlist);
1662 if (!line) {
1663 g_strfreev(attrlist);
1664 return gpg_error_from_errno(ENOMEM);
1667 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1668 g_free(line);
1669 g_strfreev(attrlist);
1670 return error;
1674 * req[0] - attribute
1675 * req[1] - element path
1677 static int attribute_delete(struct client_s *client, gchar **req)
1679 xmlAttrPtr a;
1680 xmlNodePtr n;
1681 gchar **path = NULL;
1682 gpg_error_t error;
1684 if (!req || !req[0] || !req[1])
1685 return EPWMD_COMMAND_SYNTAX;
1687 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1689 * The first argument may be only an account.
1691 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1692 return EPWMD_COMMAND_SYNTAX;
1696 * Don't remove the "name" attribute for the account element. To remove an
1697 * account use DELETE <account>.
1699 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1700 error = EPWMD_ATTR_SYNTAX;
1701 goto fail;
1704 n = find_account(client->doc, &path, &error, NULL);
1706 if (!n)
1707 goto fail;
1709 if (path[1]) {
1710 n = find_elements(client->doc, n->children, path+1, &error,
1711 NULL, NULL, NULL, NULL);
1713 if (!n)
1714 goto fail;
1717 g_strfreev(path);
1719 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1720 return EPWMD_ATTR_NOT_FOUND;
1722 if (xmlRemoveProp(a) == -1)
1723 return EPWMD_LIBXML_ERROR;
1725 return 0;
1727 fail:
1728 g_strfreev(path);
1729 return error;
1733 * Creates a "target" attribute. When other commands encounter an element with
1734 * this attribute, the element path is modified to the target value. If the
1735 * source element path doesn't exist when using 'ATTR SET target', it is
1736 * created, but the destination element path must exist.
1738 * req[0] - source element path
1739 * req[1] - destination element path
1741 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1743 gchar **src, **dst, *line;
1744 gpg_error_t error;
1745 xmlNodePtr n;
1747 if (!req || !req[0] || !req[1])
1748 return EPWMD_COMMAND_SYNTAX;
1750 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1752 * The first argument may be only an account.
1754 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1755 return EPWMD_COMMAND_SYNTAX;
1758 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1760 * The first argument may be only an account.
1762 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1763 error = EPWMD_COMMAND_SYNTAX;
1764 goto fail;
1768 n = find_account(client->doc, &dst, &error, NULL);
1771 * Make sure the destination element path exists.
1773 if (!n)
1774 goto fail;
1776 if (dst[1]) {
1777 n = find_elements(client->doc, n->children, dst+1, &error,
1778 NULL, NULL, NULL, NULL);
1780 if (!n)
1781 goto fail;
1784 again:
1785 n = find_account(client->doc, &src, &error, NULL);
1787 if (!n) {
1788 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1789 error = new_account(client->doc, src[0]);
1791 if (error)
1792 goto fail;
1794 goto again;
1796 else
1797 goto fail;
1800 if (src[1]) {
1801 if (!n->children)
1802 n = create_target_elements_cb(n, src+1, &error, NULL);
1803 else
1804 n = find_elements(client->doc, n->children, src+1, &error,
1805 NULL, NULL, create_target_elements_cb, NULL);
1807 if (!n)
1808 goto fail;
1811 * Reset the position of the element tree now that the elements
1812 * have been created.
1814 n = find_account(client->doc, &src, &error, NULL);
1816 if (!n)
1817 goto fail;
1819 n = find_elements(client->doc, n->children, src+1, &error,
1820 NULL, NULL, NULL, NULL);
1822 if (!n)
1823 goto fail;
1826 line = g_strjoinv("\t", dst);
1827 error = add_attribute(n, "target", line);
1829 if (error) {
1830 g_free(line);
1831 goto fail;
1834 g_free(line);
1835 g_strfreev(src);
1836 g_strfreev(dst);
1837 return 0;
1839 fail:
1840 g_strfreev(src);
1841 g_strfreev(dst);
1842 return error;
1846 * req[0] - account name
1847 * req[1] - new name
1849 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1851 gpg_error_t error;
1852 gchar **tmp;
1853 xmlNodePtr n;
1855 tmp = g_strdupv(req);
1857 if (!tmp)
1858 return gpg_error_from_errno(ENOMEM);
1860 n = find_account(client->doc, &tmp, &error, NULL);
1861 g_strfreev(tmp);
1863 if (!n)
1864 return error;
1866 if (g_utf8_collate(req[0], req[1]) == 0)
1867 return 0;
1870 * Will not overwrite an existing account.
1872 tmp = g_strdupv(req+1);
1874 if (!tmp)
1875 return gpg_error_from_errno(ENOMEM);
1877 n = find_account(client->doc, &tmp, &error, NULL);
1878 g_strfreev(tmp);
1880 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1881 return error;
1883 if (n)
1884 return EPWMD_ACCOUNT_EXISTS;
1887 * Whitespace not allowed in account names.
1889 if (contains_whitespace(req[1]) == TRUE)
1890 return EPWMD_ATTR_SYNTAX;
1892 tmp = g_strdupv(req);
1894 if (!tmp)
1895 return gpg_error_from_errno(ENOMEM);
1897 n = find_account(client->doc, &tmp, &error, NULL);
1898 g_strfreev(tmp);
1900 if (!n)
1901 return EPWMD_ELEMENT_NOT_FOUND;
1903 return add_attribute(n, "name", req[1]);
1907 * req[0] - attribute
1908 * req[1] - element path
1910 static int attribute_get(assuan_context_t ctx, gchar **req)
1912 struct client_s *client = assuan_get_pointer(ctx);
1913 xmlNodePtr n;
1914 xmlChar *a;
1915 gchar **path= NULL;
1916 gpg_error_t error;
1918 if (!req || !req[0] || !req[1])
1919 return EPWMD_COMMAND_SYNTAX;
1921 if (strchr(req[1], '\t')) {
1922 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1923 return EPWMD_COMMAND_SYNTAX;
1925 else {
1926 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1927 return EPWMD_COMMAND_SYNTAX;
1930 n = find_account(client->doc, &path, &error, NULL);
1932 if (!n)
1933 goto fail;
1935 if (path[1]) {
1936 n = find_elements(client->doc, n->children, path+1, &error,
1937 NULL, NULL, NULL, NULL);
1939 if (!n)
1940 goto fail;
1943 g_strfreev(path);
1945 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1946 return EPWMD_ATTR_NOT_FOUND;
1948 error = assuan_send_data(ctx, a, xmlStrlen(a));
1949 xmlFree(a);
1950 return error;
1952 fail:
1953 g_strfreev(path);
1954 return error;
1958 * req[0] - attribute
1959 * req[1] - element path
1960 * req[2] - value
1962 static int attribute_set(struct client_s *client, gchar **req)
1964 gchar **path = NULL;
1965 gpg_error_t error;
1966 xmlNodePtr n;
1968 if (!req || !req[0] || !req[1] || !req[2])
1969 return EPWMD_COMMAND_SYNTAX;
1972 * Reserved attribute names.
1974 if (g_utf8_collate(req[0], "name") == 0) {
1976 * Only reserved for the account element. Not the rest of the
1977 * document.
1979 if (strchr(req[1], '\t') == NULL)
1980 return name_attribute(client, req + 1);
1982 else if (g_utf8_collate(req[0], "target") == 0)
1983 return target_attribute(client, req + 1);
1985 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1987 * The first argument may be only an account.
1989 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1990 return EPWMD_COMMAND_SYNTAX;
1993 n = find_account(client->doc, &path, &error, NULL);
1995 if (!n)
1996 goto fail;
1998 if (path[1]) {
1999 n = find_elements(client->doc, n->children, path+1, &error,
2000 NULL, NULL, NULL, NULL);
2002 if (!n)
2003 goto fail;
2006 g_strfreev(path);
2007 return add_attribute(n, req[0], req[2]);
2009 fail:
2010 g_strfreev(path);
2011 return error;
2015 * req[0] - command
2016 * req[1] - attribute name or element path if command is LIST
2017 * req[2] - element path
2018 * req[2] - element path or value
2020 static int attr_command(assuan_context_t ctx, char *line)
2022 struct client_s *client = assuan_get_pointer(ctx);
2023 gchar **req = split_input_line(line, " ", 4);
2024 gpg_error_t error = 0;
2026 error = file_modified(client);
2028 if (error) {
2029 log_write("%s: %s", client->filename ? client->filename : "",
2030 pwmd_strerror(error));
2031 g_strfreev(req);
2032 return send_error(ctx, error);
2035 if (!req || !req[0] || !req[1]) {
2036 g_strfreev(req);
2037 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2040 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2041 error = attribute_set(client, req+1);
2042 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2043 error = attribute_get(ctx, req+1);
2044 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2045 error = attribute_delete(client, req+1);
2046 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2047 error = attribute_list(ctx, req+1);
2048 else
2049 error = EPWMD_COMMAND_SYNTAX;
2051 g_strfreev(req);
2052 return send_error(ctx, error);
2055 static int iscached_command(assuan_context_t ctx, char *line)
2057 gchar **req = split_input_line(line, " ", 0);
2058 guchar md5file[16];
2060 MUTEX_LOCK(ctx);
2062 if (!req || !*req) {
2063 g_strfreev(req);
2064 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2067 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2068 g_strfreev(req);
2070 if (cache_iscached(md5file) == FALSE)
2071 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2073 return 0;
2076 void send_cache_status(assuan_context_t ctx)
2078 gchar *tmp;
2079 gint i = MUTEX_OFFSET;
2081 tmp = print_fmt("%i %i",
2082 cache_file_count(),
2083 (i / 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 MUTEX_LOCK(ctx);
2097 if (!req || !*req) {
2098 g_strfreev(req);
2099 cache_clear(client->md5file, 2);
2100 send_cache_status(ctx);
2101 return 0;
2104 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2105 g_strfreev(req);
2106 cache_clear(md5file, 1);
2107 send_cache_status(ctx);
2108 return 0;
2111 static int cachetimeout_command(assuan_context_t ctx, char *line)
2113 guchar md5file[16];
2114 glong timeout;
2115 gchar **req = split_input_line(line, " ", 0);
2116 gchar *p;
2118 MUTEX_LOCK(ctx);
2120 if (!req || !*req || !req[1]) {
2121 g_strfreev(req);
2122 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2125 errno = 0;
2126 timeout = strtol(req[0], &p, 10);
2128 if (errno != 0 || *p != 0) {
2129 g_strfreev(req);
2130 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2133 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2134 g_strfreev(req);
2136 if (cache_set_timeout(md5file, timeout) == FALSE)
2137 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2139 send_cache_status(ctx);
2140 return 0;
2143 static int dump_command(assuan_context_t ctx, char *line)
2145 xmlChar *xml;
2146 gssize len;
2147 struct client_s *client = assuan_get_pointer(ctx);
2148 gpg_error_t error;
2150 if (disable_list_and_dump == TRUE)
2151 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2153 error = file_modified(client);
2155 if (error) {
2156 log_write("%s: %s", client->filename ? client->filename : "",
2157 pwmd_strerror(error));
2158 return send_error(ctx, error);
2161 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2162 error = assuan_send_data(ctx, xml, len);
2163 xmlFree(xml);
2164 return error;
2167 static int getconfig_command(assuan_context_t ctx, gchar *line)
2169 struct client_s *client = assuan_get_pointer(ctx);
2170 gpg_error_t error = 0;
2171 gchar *p, *tmp;
2173 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2174 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2176 p = get_key_file_string(client->filename ? client->filename : "default", line);
2178 if (!p)
2179 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2181 tmp = expand_homedir(p);
2182 g_free(p);
2183 p = tmp;
2184 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2185 g_free(p);
2186 return send_error(ctx, error);
2189 void cleanup_assuan(assuan_context_t ctx)
2191 struct client_s *cl = assuan_get_pointer(ctx);
2193 cleanup_client(cl);
2194 MUTEX_UNLOCK;
2197 gpg_error_t register_commands(assuan_context_t ctx)
2199 static struct {
2200 const char *name;
2201 int (*handler)(assuan_context_t, char *line);
2202 } table[] = {
2203 { "OPEN", open_command },
2204 { "SAVE", save_command },
2205 { "LIST", list_command },
2206 { "REALPATH", realpath_command },
2207 { "STORE", store_command },
2208 { "DELETE", delete_command },
2209 { "GET", get_command },
2210 { "ATTR", attr_command },
2211 { "ISCACHED", iscached_command },
2212 { "CLEARCACHE", clearcache_command },
2213 { "CACHETIMEOUT", cachetimeout_command },
2214 { "GETCONFIG", getconfig_command },
2215 { "DUMP", dump_command },
2216 { "INPUT", NULL },
2217 { "OUTPUT", NULL },
2218 { NULL, NULL }
2220 int i, rc;
2222 for (i=0; table[i].name; i++) {
2223 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2225 if (rc)
2226 return rc;
2229 return assuan_register_bye_notify(ctx, cleanup_assuan);
2232 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2233 guchar *key)
2235 guchar *iv;
2236 void *inbuf;
2237 gsize insize, len;
2238 guchar tkey[gcrykeysize];
2239 struct file_header_s {
2240 guint iter;
2241 guchar iv[gcryblocksize];
2242 } file_header;
2243 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2244 gcry_cipher_hd_t gh;
2245 guint iter = 0, n_iter = 0;
2246 gint iter_progress;
2247 void *outbuf = NULL;
2248 gint zerror = 0;
2249 glong outsize = 0;
2251 if (!ctx) {
2252 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2254 if (gcryerrno)
2255 return gcryerrno;
2257 else
2258 gh = client->gh;
2260 lseek(fd, 0, SEEK_SET);
2261 insize = st.st_size - sizeof(struct file_header_s);
2262 iv = gcry_malloc(gcryblocksize);
2264 if (!iv) {
2265 if (!ctx)
2266 gcry_cipher_close(gh);
2268 return gpg_error_from_errno(ENOMEM);
2271 len = read(fd, &file_header, sizeof(struct file_header_s));
2273 if (len != sizeof(file_header)) {
2274 len = errno;
2276 if (!ctx)
2277 gcry_cipher_close(gh);
2279 gcry_free(iv);
2280 errno = len;
2281 return gpg_error_from_errno(errno);
2284 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2285 inbuf = gcry_malloc(insize);
2287 if (!inbuf) {
2288 if (!ctx)
2289 gcry_cipher_close(gh);
2291 gcry_free(iv);
2292 return gpg_error_from_errno(ENOMEM);
2295 len = read(fd, inbuf, insize);
2297 if (len != insize) {
2298 len = errno;
2300 if (!ctx)
2301 gcry_cipher_close(gh);
2303 gcry_free(iv);
2304 errno = len;
2305 return gpg_error_from_errno(errno);
2308 memcpy(tkey, key, sizeof(tkey));
2309 tkey[0] ^= 1;
2311 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2312 if (!ctx) {
2313 gcry_cipher_close(gh);
2314 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2316 else
2317 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2319 gcry_free(inbuf);
2320 gcry_free(iv);
2321 return gcryerrno;
2324 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2325 if (!ctx) {
2326 gcry_cipher_close(gh);
2327 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2329 else
2330 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2332 gcry_free(inbuf);
2333 gcry_free(iv);
2335 if (!ctx)
2336 gcry_cipher_close(gh);
2338 return gcryerrno;
2341 iter_progress = get_key_file_integer("default", "iteration_progress");
2343 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress)
2344 assuan_write_status(client->ctx, "DECRYPT", "0");
2346 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2347 if (!ctx) {
2348 gcry_cipher_close(gh);
2349 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2351 else
2352 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2354 gcry_free(inbuf);
2355 gcry_free(iv);
2356 return gcryerrno;
2359 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2360 if (!ctx) {
2361 gcry_cipher_close(gh);
2362 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2364 else
2365 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2367 gcry_free(inbuf);
2368 gcry_free(iv);
2369 return gcryerrno;
2372 while (iter < file_header.iter) {
2373 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2374 if (!(iter % iter_progress))
2375 assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2376 ++n_iter * iter_progress));
2379 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2380 if (!ctx) {
2381 gcry_cipher_close(gh);
2382 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2384 else
2385 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2387 gcry_free(inbuf);
2388 gcry_free(iv);
2389 return gcryerrno;
2392 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2393 if (!ctx) {
2394 gcry_cipher_close(gh);
2395 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2397 else
2398 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2400 gcry_free(inbuf);
2401 gcry_free(iv);
2402 return gcryerrno;
2405 iter++;
2408 if (ctx && iter_progress && file_header.iter >= iter_progress)
2409 assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2411 gcry_free(iv);
2413 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2415 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2417 if (zerror == Z_MEM_ERROR) {
2418 gcry_free(inbuf);
2419 return gpg_error_from_errno(ENOMEM);
2421 else if (zerror != Z_DATA_ERROR) {
2422 gcry_free(inbuf);
2424 if (!ctx)
2425 gcry_cipher_close(gh);
2427 return EPWMD_BADKEY;
2430 else {
2431 gcry_free(inbuf);
2432 inbuf = outbuf;
2433 insize = outsize;
2436 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2437 gcry_free(inbuf);
2439 if (!ctx)
2440 gcry_cipher_close(gh);
2442 return EPWMD_BADKEY;
2445 if (ctx) {
2446 client->xml = inbuf;
2447 client->len = insize;
2449 else {
2450 gcry_cipher_close(gh);
2451 gcry_free(inbuf);
2454 return 0;