Added RESET command handling. It'll free all the resources of the
[pwmd.git] / src / commands.c
bloba443dce0886ce8182106a64fc397d4f1810ae242
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];
193 gpg_error_t rc;
195 z.zalloc = z_alloc;
196 z.zfree = z_free;
197 z.next_in = in;
198 z.avail_in = insize;
199 z.avail_out = zlib_bufsize;
200 z.next_out = pout = g_malloc(zlib_bufsize);
202 if (!pout) {
203 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
204 *error = Z_MEM_ERROR;
205 return FALSE;
208 ret = inflateInit2(&z, 47);
210 if (ret != Z_OK) {
211 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
212 g_free(pout);
213 return FALSE;
216 memset(&h, 0, sizeof(gz_header));
217 h.comment = (guchar *)buf;
218 h.comm_max = sizeof(buf);
219 ret = inflateGetHeader(&z, &h);
221 if (ret != Z_OK) {
222 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
223 g_free(pout);
224 inflateEnd(&z);
225 return FALSE;
228 ret = inflate(&z, Z_BLOCK);
230 if (ret != Z_OK) {
231 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
232 g_free(pout);
233 inflateEnd(&z);
234 return FALSE;
237 if (h.comment)
238 insize = atoi((gchar *)h.comment);
240 do {
241 gpointer p;
243 ret = inflate(&z, Z_FINISH);
245 switch (ret) {
246 case Z_OK:
247 break;
248 case Z_BUF_ERROR:
249 if (!z.avail_out) {
250 p = g_realloc(pout, z.total_out + zlib_bufsize);
252 if (!p) {
253 ret = Z_MEM_ERROR;
254 goto fail;
257 pout = p;
258 z.next_out = pout + z.total_out;
259 z.avail_out = zlib_bufsize;
261 if (ctx) {
262 rc = assuan_write_status(ctx, "DECOMPRESS",
263 print_fmt("%i %i", z.total_out, insize));
265 if (rc) {
266 ret = rc;
267 goto fail;
271 break;
272 case Z_STREAM_END:
273 break;
274 default:
275 goto fail;
276 break;
279 pth_yield(NULL);
280 } while (ret != Z_STREAM_END);
282 if (ctx) {
283 rc = assuan_write_status(ctx, "DECOMPRESS",
284 print_fmt("%i %i", z.total_out, insize));
286 if (rc) {
287 ret = rc;
288 goto fail;
292 *out = pout;
293 *outsize = z.total_out;
294 inflateEnd(&z);
295 return TRUE;
297 fail:
298 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
299 *error = ret;
300 g_free(pout);
301 inflateEnd(&z);
302 return FALSE;
305 static int open_command(assuan_context_t ctx, char *line)
307 gint fd;
308 struct stat st;
309 guchar shakey[gcrykeysize];
310 gint cached = 0;
311 gint timeout;
312 gpg_error_t error;
313 struct client_s *client = assuan_get_pointer(ctx);
314 gchar **req;
315 gchar *filename = NULL;
317 CACHE_LOCK(ctx, NULL);
319 if ((req = split_input_line(line, " ", 2)) != NULL)
320 filename = req[0];
322 if (!filename || !*filename) {
323 g_strfreev(req);
324 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
327 if (valid_filename(filename) == FALSE) {
328 g_strfreev(req);
329 return send_error(ctx, EPWMD_INVALID_FILENAME);
332 if (client->state == STATE_OPEN)
333 cleanup_client(client);
335 client->freed = FALSE;
337 if ((gcryerrno = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
338 g_strfreev(req);
339 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
340 cleanup_client(client);
341 return send_error(ctx, gcryerrno);
344 if (stat(filename, &st) == 0) {
345 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
346 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
347 g_strfreev(req);
348 cleanup_client(client);
349 return send_error(ctx, EPWMD_INVALID_FILENAME);
352 client->mtime = st.st_mtime;
355 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
358 * New files don't need a key.
360 if (access(filename, R_OK|W_OK) != 0) {
361 if (errno != ENOENT) {
362 error = errno;
363 log_write("%s: %s", filename, strerror(errno));
364 g_strfreev(req);
365 cleanup_client(client);
366 return send_syserror(ctx, error);
368 new_doc:
369 if ((client->xml = new_document()) == NULL) {
370 log_write("%s", strerror(ENOMEM));
371 g_strfreev(req);
372 cleanup_client(client);
373 return send_syserror(ctx, ENOMEM);
376 client->len = xmlStrlen(client->xml);
378 if (cache_has_file(client->md5file) == TRUE)
379 cache_clear(client->md5file, 1);
381 if (cache_add_file(client->md5file, NULL) == FALSE) {
382 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
383 g_strfreev(req);
384 cleanup_client(client);
385 return send_error(ctx, EPWMD_MAX_SLOTS);
388 client->new = TRUE;
389 client->filename = g_strdup(filename);
391 if (!client->filename) {
392 g_strfreev(req);
393 cleanup_client(client);
394 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
395 return send_syserror(ctx, ENOMEM);
398 if (req[1] && *req[1]) {
399 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
400 memset(req[1], 0, strlen(req[1]));
401 goto update_cache;
404 goto done;
407 if ((fd = open_file(filename, &st)) == -1) {
408 error = errno;
409 log_write("%s: %s", filename, strerror(errno));
410 g_strfreev(req);
411 cleanup_client(client);
412 return send_syserror(ctx, error);
415 if (st.st_size == 0)
416 goto new_doc;
418 if (cache_get_key(client->md5file, shakey) == TRUE)
419 cached = 1;
420 else {
422 * No key specified and no matching filename found in the cache.
424 if (!req[1] || !*req[1]) {
425 close(fd);
426 g_strfreev(req);
427 cleanup_client(client);
428 return send_error(ctx, EPWMD_KEY);
432 if (!cached) {
433 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
434 memset(req[1], 0, strlen(req[1]));
437 error = try_xml_decrypt(ctx, fd, st, shakey);
439 if (error) {
440 close(fd);
441 memset(shakey, 0, sizeof(shakey));
442 g_strfreev(req);
443 cleanup_client(client);
444 return send_error(ctx, error);
447 close(fd);
448 client->filename = g_strdup(filename);
450 if (!client->filename) {
451 memset(shakey, 0, sizeof(shakey));
452 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
453 g_strfreev(req);
454 cleanup_client(client);
455 return send_syserror(ctx, ENOMEM);
458 update_cache:
459 if (!cached) {
460 if (cache_update_key(client->md5file, shakey) == FALSE) {
461 memset(shakey, 0, sizeof(shakey));
462 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
463 g_strfreev(req);
464 cleanup_client(client);
465 return send_error(ctx, EPWMD_MAX_SLOTS);
468 timeout = get_key_file_integer(client->filename, "cache_timeout");
469 cache_reset_timeout(client->md5file, timeout);
471 else
472 cache_set_timeout(client->md5file, -2);
474 memset(shakey, 0, sizeof(shakey));
476 done:
477 g_strfreev(req);
478 error = parse_xml(ctx);
480 if (client->xml) {
481 gcry_free(client->xml);
482 client->xml = NULL;
485 if (error == 0)
486 client->state = STATE_OPEN;
488 return send_error(ctx, error);
491 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
492 gint size, gpointer *out, glong *outsize, gint *error)
494 z_stream z;
495 gpointer pout, pin;
496 gint ret;
497 gz_header h;
498 gchar buf[17];
499 gint cmd = Z_NO_FLUSH;
500 gpg_error_t rc;
502 z.zalloc = z_alloc;
503 z.zfree = z_free;
504 z.next_in = pin = data;
505 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
506 z.avail_out = zlib_bufsize;
507 z.next_out = pout = g_malloc(zlib_bufsize);
509 if (!pout) {
510 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
511 *error = Z_MEM_ERROR;
512 return FALSE;
515 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
517 if (ret != Z_OK) {
518 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
519 *error = ret;
520 g_free(pout);
521 return FALSE;
524 memset(&h, 0, sizeof(gz_header));
525 snprintf(buf, sizeof(buf), "%i", size);
526 h.comment = (guchar *)buf;
527 ret = deflateSetHeader(&z, &h);
529 if (ret != Z_OK) {
530 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
531 *error = ret;
532 g_free(pout);
533 deflateEnd(&z);
534 return FALSE;
537 do {
538 gpointer p;
540 ret = deflate(&z, cmd);
542 switch (ret) {
543 case Z_OK:
544 break;
545 case Z_BUF_ERROR:
546 if (!z.avail_out) {
547 p = g_realloc(pout, z.total_out + zlib_bufsize);
549 if (!p) {
550 ret = Z_MEM_ERROR;
551 goto fail;
554 pout = p;
555 z.next_out = pout + z.total_out;
556 z.avail_out = zlib_bufsize;
559 if (!z.avail_in && z.total_in < size) {
560 if (z.total_in + zlib_bufsize > size)
561 z.avail_in = size - z.total_in;
562 else
563 z.avail_in = zlib_bufsize;
565 if (ctx) {
566 rc = assuan_write_status(ctx, "COMPRESS",
567 print_fmt("%i %i", z.total_in, size));
569 if (rc) {
570 ret = rc;
571 goto fail;
576 if (z.total_in >= size)
577 cmd = Z_FINISH;
579 break;
580 case Z_STREAM_END:
581 break;
582 default:
583 goto fail;
586 pth_yield(NULL);
587 } while (ret != Z_STREAM_END);
589 if (ctx) {
590 rc = assuan_write_status(ctx, "COMPRESS",
591 print_fmt("%i %i", z.total_in, size));
593 if (rc) {
594 ret = rc;
595 goto fail;
599 *out = pout;
600 *outsize = z.total_out;
601 deflateEnd(&z);
602 return TRUE;
604 fail:
605 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
606 *error = ret;
607 g_free(pout);
608 deflateEnd(&z);
609 return FALSE;
612 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
613 const gchar *filename, gpointer data, size_t insize,
614 const guchar *shakey, guint iter)
616 gsize len = insize;
617 gint fd;
618 gpointer inbuf;
619 guchar tkey[gcrykeysize];
620 gchar *p;
621 gint error;
622 guint iter_progress = 0, n_iter = 0, xiter = 0;
623 gchar tmp[FILENAME_MAX];
624 struct file_header_s {
625 guint iter;
626 guchar iv[gcryblocksize];
627 } file_header;
629 if (insize / gcryblocksize) {
630 len = (insize / gcryblocksize) * gcryblocksize;
632 if (insize % gcryblocksize)
633 len += gcryblocksize;
637 * Resize the existing xml buffer to the block size required by gcrypt
638 * rather than duplicating it and wasting memory.
640 inbuf = gcry_realloc(data, len);
642 if (!inbuf)
643 return gpg_error_from_errno(ENOMEM);
645 insize = len;
646 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
647 memcpy(tkey, shakey, sizeof(tkey));
648 tkey[0] ^= 1;
650 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
651 gcry_free(inbuf);
652 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
653 return gcryerrno;
656 file_header.iter = iter;
658 if (client)
659 iter_progress = get_key_file_integer("default", "iteration_progress");
661 if (client && iter_progress && file_header.iter >= iter_progress) {
662 error = assuan_write_status(client->ctx, "ENCRYPT", "0");
664 if (error) {
665 gcry_free(inbuf);
666 return error;
670 while (xiter < file_header.iter) {
671 if (client && iter_progress > 0 && xiter >= iter_progress) {
672 if (!(xiter % iter_progress)) {
673 error = assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
674 ++n_iter * iter_progress));
676 if (error) {
677 gcry_free(inbuf);
678 return error;
683 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
684 sizeof(file_header.iv)))) {
685 gcry_free(inbuf);
686 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
687 return gcryerrno;
690 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
691 == FALSE) {
692 gcry_free(inbuf);
693 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
694 return gcryerrno;
697 xiter++;
698 pth_yield(NULL);
701 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
702 sizeof(file_header.iv)))) {
703 gcry_free(inbuf);
704 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
705 return gcryerrno;
708 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
709 gcry_free(inbuf);
710 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
711 return gcryerrno;
714 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
715 gcry_free(inbuf);
716 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
717 return gcryerrno;
720 if (client && iter_progress && file_header.iter >= iter_progress) {
721 error = assuan_write_status(client->ctx, "ENCRYPT",
722 print_fmt("%i", file_header.iter));
724 if (error) {
725 gcry_free(inbuf);
726 return error;
730 if (filename) {
731 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
733 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
734 error = errno;
735 gcry_free(inbuf);
736 p = strrchr(tmp, '/');
737 p++;
738 log_write("%s: %s", p, strerror(errno));
739 return gpg_error_from_errno(error);
742 else
744 * xml_import() from command line.
746 fd = STDOUT_FILENO;
748 len = pth_write(fd, &file_header, sizeof(struct file_header_s));
750 if (len != sizeof(file_header)) {
751 len = errno;
753 if (filename)
754 close(fd);
756 gcry_free(inbuf);
757 return gpg_error_from_errno(len);
760 len = pth_write(fd, inbuf, insize);
762 if (len != insize) {
763 len = errno;
765 if (filename)
766 close(fd);
768 gcry_free(inbuf);
769 return gpg_error_from_errno(len);
772 if (fsync(fd) == -1) {
773 len = errno;
774 close(fd);
775 gcry_free(inbuf);
776 return gpg_error_from_errno(len);
779 if (filename) {
780 struct stat st;
781 mode_t mode = 0;
783 close(fd);
785 if (stat(filename, &st) == 0)
786 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
788 if (rename(tmp, filename) == -1) {
789 len = errno;
790 gcry_free(inbuf);
791 return gpg_error_from_errno(len);
794 if (mode)
795 chmod(filename, mode);
798 gcry_free(inbuf);
799 return 0;
802 static int save_command(assuan_context_t ctx, char *line)
804 gpointer xmlbuf;
805 xmlChar *p;
806 gint len;
807 gint cached = 0;
808 guchar shakey[gcrykeysize];
809 gint iter;
810 struct stat st;
811 struct client_s *client = assuan_get_pointer(ctx);
812 gpg_error_t error;
813 gint timeout;
814 gpointer outbuf;
815 glong outsize = 0;
816 gint zerror;
818 CACHE_LOCK(ctx, NULL);
819 error = file_modified(client);
821 if (error) {
822 log_write("%s: %s", client->filename ? client->filename : "",
823 pwmd_strerror(error));
824 return send_error(ctx, error);
827 if (stat(client->filename, &st) == -1 && errno != ENOENT)
828 return send_syserror(ctx, errno);
830 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
831 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
832 return send_error(ctx, EPWMD_INVALID_FILENAME);
835 if (!line || !*line) {
836 if (cache_get_key(client->md5file, shakey) == FALSE)
837 return send_error(ctx, EPWMD_KEY);
839 cached = 1;
841 else {
842 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
843 memset(line, 0, strlen(line));
846 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
847 xmlbuf = p;
849 iter = get_key_file_integer(client->filename, "compression_level");
851 if (iter < 0)
852 iter = 0;
854 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
855 memset(shakey, 0, sizeof(shakey));
856 xmlFree(xmlbuf);
858 if (zerror == Z_MEM_ERROR) {
859 return send_syserror(ctx, ENOMEM);
861 else
862 return send_error(ctx, GPG_ERR_COMPR_ALGO);
864 else {
865 gcry_free(xmlbuf);
866 xmlbuf = outbuf;
867 len = outsize;
870 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
871 iter = 0;
873 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
875 if (error) {
876 memset(shakey, 0, sizeof(shakey));
877 return send_error(ctx, error);
880 stat(client->filename, &st);
881 client->mtime = st.st_mtime;
882 timeout = get_key_file_integer(client->filename, "cache_timeout");
884 if (cached) {
885 memset(shakey, 0, sizeof(shakey));
886 cache_reset_timeout(client->md5file, timeout);
887 client->new = FALSE;
888 return send_error(ctx, 0);
891 if (cache_update_key(client->md5file, shakey) == FALSE) {
892 memset(shakey, 0, sizeof(shakey));
893 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
894 return send_error(ctx, EPWMD_MAX_SLOTS);
897 client->new = FALSE;
898 memset(shakey, 0, sizeof(shakey));
899 cache_reset_timeout(client->md5file, timeout);
900 return send_error(ctx, 0);
903 static gboolean contains_whitespace(const gchar *str)
905 const gchar *p = str;
906 gunichar c;
907 glong len;
909 len = g_utf8_strlen(p++, -1) -1;
911 while (len--) {
912 c = g_utf8_get_char(p++);
914 if (g_unichar_isspace(c))
915 return TRUE;
918 return FALSE;
921 static int delete_command(assuan_context_t ctx, char *line)
923 struct client_s *client = assuan_get_pointer(ctx);
924 gchar **req;
925 gpg_error_t error;
926 xmlNodePtr n;
928 error = file_modified(client);
930 if (error) {
931 log_write("%s: %s", client->filename ? client->filename : "",
932 pwmd_strerror(error));
933 return send_error(ctx, error);
936 if (strchr(line, '\t'))
937 req = split_input_line(line, "\t", -1);
938 else
939 req = split_input_line(line, " ", -1);
941 if (!req || !*req)
942 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
944 n = find_account(client->doc, &req, &error, NULL);
946 if (!n) {
947 g_strfreev(req);
948 return send_error(ctx, error);
952 * No sub-node defined. Remove the entire node (account).
954 if (!req[1]) {
955 if (n) {
956 xmlUnlinkNode(n);
957 xmlFreeNode(n);
960 g_strfreev(req);
961 return send_error(ctx, 0);
964 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
965 g_strfreev(req);
967 if (!n)
968 return send_error(ctx, error);
970 if (n) {
971 xmlUnlinkNode(n);
972 xmlFreeNode(n);
975 return send_error(ctx, 0);
979 * Don't return with assuan_process_done() here. This has been called from
980 * assuan_process_next() and the command should be finished in
981 * client_thread().
983 #define INQUIRE_RETURN(client, rc) {client->inquire_error = rc; return rc;}
985 static int store_command_finalize(gpointer data, gint rc, guchar *line,
986 gsize len)
988 assuan_context_t ctx = data;
989 struct client_s *client = assuan_get_pointer(ctx);
990 gchar **req;
991 guchar *result = line;
992 xmlNodePtr n;
993 gpg_error_t error = file_modified(client);
995 if (rc) {
996 if (line)
997 #ifndef MEM_DEBUG
998 xfree(line);
999 #else
1000 free(line);
1001 #endif
1002 return rc;
1005 req = split_input_line((gchar *)result, "\t", 0);
1006 #ifndef MEM_DEBUG
1007 xfree(line);
1008 #else
1009 free(line);
1010 #endif
1012 if (rc) {
1013 if (req)
1014 g_strfreev(req);
1016 INQUIRE_RETURN(client, rc);
1019 if (!req || !*req)
1020 INQUIRE_RETURN(client, EPWMD_COMMAND_SYNTAX);
1022 again:
1023 n = find_account(client->doc, &req, &error, NULL);
1025 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1026 if (contains_whitespace(*req) == TRUE) {
1027 g_strfreev(req);
1028 INQUIRE_RETURN(client, EPWMD_INVALID_ELEMENT);
1031 error = new_account(client->doc, *req);
1033 if (error) {
1034 g_strfreev(req);
1035 INQUIRE_RETURN(client, error);
1038 goto again;
1041 if (!n) {
1042 g_strfreev(req);
1043 INQUIRE_RETURN(client, error);
1046 if (req[1]) {
1047 if (!n->children)
1048 create_elements_cb(n, req+1, &error, NULL);
1049 else
1050 find_elements(client->doc, n->children, req+1, &error,
1051 NULL, NULL, create_elements_cb, NULL);
1054 g_strfreev(req);
1055 client->inquire = INQUIRE_OK;
1056 INQUIRE_RETURN(client, error);
1059 static int store_command(assuan_context_t ctx, char *line)
1061 struct client_s *client = assuan_get_pointer(ctx);
1062 gpg_error_t error = file_modified(client);
1064 if (error) {
1065 log_write("%s: %s", client->filename ? client->filename : "",
1066 pwmd_strerror(error));
1067 return send_error(ctx, error);
1070 error = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1072 if (error)
1073 return send_error(ctx, error);
1075 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1076 client->inquire = INQUIRE_INPROGRESS;
1077 return 0;
1080 static int get_command(assuan_context_t ctx, char *line)
1082 struct client_s *client = assuan_get_pointer(ctx);
1083 gchar **req;
1084 gpg_error_t error;
1085 xmlNodePtr n;
1087 error = file_modified(client);
1089 if (error) {
1090 log_write("%s: %s", client->filename ? client->filename : "",
1091 pwmd_strerror(error));
1092 return send_error(ctx, error);
1095 req = split_input_line(line, "\t", -1);
1097 if (!req || !*req) {
1098 g_strfreev(req);
1099 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1102 n = find_account(client->doc, &req, &error, NULL);
1104 if (!n) {
1105 g_strfreev(req);
1106 return send_error(ctx, error);
1109 if (req[1])
1110 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
1112 g_strfreev(req);
1114 if (error)
1115 return send_error(ctx, error);
1117 if (!n || !n->children)
1118 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1120 n = n->children;
1122 if (!n || !n->content || !*n->content)
1123 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1125 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1126 return send_error(ctx, error);
1129 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1131 xmlChar *p = path;
1132 gint n;
1133 gchar *buf;
1135 if (!p)
1136 return NULL;
1138 for (n = 0; *p && n < 3; p++) {
1139 if (*p == '/')
1140 n++;
1143 if (strstr((gchar *)p, "text()") != NULL)
1144 p[xmlStrlen(p) - 7] = 0;
1146 for (n = 0; p[n]; n++) {
1147 if (p[n] == '/')
1148 p[n] = '\t';
1151 buf = g_strdup_printf("%s\t%s", account, p);
1152 return buf;
1155 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1157 gchar **a;
1158 va_list ap;
1159 gchar *buf;
1160 gint len = *array ? g_strv_length(*array) : 0;
1161 gint ret;
1163 if (!fmt)
1164 return FALSE;
1166 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1167 return FALSE;
1169 va_start(ap, fmt);
1170 ret = g_vasprintf(&buf, fmt, ap);
1171 va_end(ap);
1173 if (ret == -1)
1174 return FALSE;
1176 a[len++] = buf;
1177 a[len] = NULL;
1178 *array = a;
1179 return TRUE;
1182 struct realpath_s {
1183 gchar *account;
1186 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1187 gpg_error_t *error, void *data)
1189 struct realpath_s *rp = data;
1191 if (rp->account)
1192 g_free(rp->account);
1194 rp->account = g_strdup(req[0]);
1196 if (!rp->account) {
1197 *error = gpg_error_from_errno(ENOMEM);
1198 return NULL;
1201 return node;
1204 static int realpath_command(assuan_context_t ctx, char *line)
1206 gpg_error_t error;
1207 struct client_s *client = assuan_get_pointer(ctx);
1208 xmlChar *p;
1209 gchar **req;
1210 gchar *result, *t;
1211 gint i;
1212 xmlNodePtr n;
1213 struct realpath_s *rp;
1214 GString *string;
1216 error = file_modified(client);
1218 if (error) {
1219 log_write("%s: %s", client->filename ? client->filename : "",
1220 pwmd_strerror(error));
1221 return send_error(ctx, error);
1224 if (strchr(line, '\t') != NULL) {
1225 if ((req = split_input_line(line, "\t", 0)) == NULL)
1226 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1228 else {
1229 if ((req = split_input_line(line, " ", 0)) == NULL)
1230 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1233 n = find_account(client->doc, &req, &error, NULL);
1235 if (!n) {
1236 g_strfreev(req);
1237 return send_error(ctx, error);
1240 rp = g_malloc(sizeof(struct realpath_s));
1242 if (!rp) {
1243 g_strfreev(req);
1244 return send_syserror(ctx, ENOMEM);
1247 rp->account = g_strdup(req[0]);
1249 if (!rp->account) {
1250 g_strfreev(req);
1251 return send_syserror(ctx, ENOMEM);
1254 if (req[1]) {
1255 n = find_elements(client->doc, n->children, req+1, &error,
1256 NULL, realpath_elements_cb, NULL, rp);
1258 if (!n) {
1259 g_free(rp->account);
1260 g_free(rp);
1261 g_strfreev(req);
1262 return send_error(ctx, error);
1266 p = xmlGetNodePath(n);
1267 result = element_path_to_req(rp->account, p);
1269 if (!result) {
1270 g_free(result);
1271 g_free(rp->account);
1272 g_free(rp);
1273 g_strfreev(req);
1274 xmlFree(p);
1275 return send_syserror(ctx, ENOMEM);
1278 string = g_string_new(result);
1279 g_free(result);
1280 g_free(rp->account);
1281 g_free(rp);
1282 g_strfreev(req);
1283 xmlFree(p);
1284 i = 0;
1286 again:
1287 for (t = string->str + i; *t; t++, i++) {
1288 if (!i || *t == '\t') {
1289 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1290 goto again;
1294 error = assuan_send_data(ctx, string->str, string->len);
1295 g_string_free(string, TRUE);
1296 return send_error(ctx, error);
1299 struct list_element_s {
1300 GSList *list;
1301 gchar **elements;
1304 static gboolean append_to_element_list(struct list_element_s *elements)
1306 gchar *tmp;
1307 gint i, total;
1308 gchar *a;
1309 GSList *list;
1311 if (!elements || !elements->elements)
1312 return TRUE;
1314 tmp = g_strjoinv("\t", elements->elements);
1316 if (!tmp)
1317 return FALSE;
1319 g_strfreev(elements->elements);
1320 elements->elements = NULL;
1321 total = g_slist_length(elements->list);
1322 a = g_utf8_collate_key(tmp, -1);
1324 if (!a) {
1325 g_free(tmp);
1326 return FALSE;
1330 * Removes duplicate element paths from the list. This is needed when
1331 * appending an element tree from list_command(). The glib docs recommend
1332 * using g_utf8_collate_key() for a large number of strings.
1334 for (i = 0; i < total; i++) {
1335 gchar *p = g_slist_nth_data(elements->list, i);
1336 gchar *b = g_utf8_collate_key(p, -1);
1338 if (!b) {
1339 g_free(a);
1340 g_free(tmp);
1341 return FALSE;
1344 if (strcmp(a, b) == 0) {
1345 g_free(a);
1346 g_free(b);
1347 g_free(tmp);
1348 return TRUE;
1351 g_free(b);
1354 g_free(a);
1355 list = g_slist_append(elements->list, tmp);
1357 if (!list)
1358 return FALSE;
1360 elements->list = list;
1361 return TRUE;
1364 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1365 struct list_element_s *elements, gchar *prefix)
1367 xmlNodePtr n;
1368 gpg_error_t error;
1370 if (append_to_element_list(elements) == FALSE)
1371 return gpg_error_from_errno(ENOMEM);
1373 for (n = node; n; n = n->next) {
1374 if (n->type == XML_ELEMENT_NODE) {
1375 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1376 gchar *tmp;
1378 if (content) {
1379 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1380 return gpg_error_from_errno(ENOMEM);
1382 if (append_to_element_list(elements) == FALSE)
1383 return gpg_error_from_errno(ENOMEM);
1386 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1388 if (!tmp)
1389 return gpg_error_from_errno(ENOMEM);
1391 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1392 g_free(tmp);
1393 return gpg_error_from_errno(ENOMEM);
1396 if (n->children) {
1397 error = do_list_recurse(doc, n->children, elements, tmp);
1398 g_free(tmp);
1400 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1401 return error;
1403 else
1404 g_free(tmp);
1406 if (append_to_element_list(elements) == FALSE)
1407 return gpg_error_from_errno(ENOMEM);
1411 return 0;
1414 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1415 struct list_element_s *elements, char *line)
1417 gchar *prefix = NULL, *account;
1418 gchar **req = NULL, **oreq = NULL, *tmp;
1419 xmlNodePtr n;
1420 gboolean account_is_literal, account_has_target = FALSE;
1421 gint which = 0;
1422 gchar **p;
1423 gpg_error_t error;
1425 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1426 if ((req = split_input_line(line, " ", 0)) == NULL)
1427 return EPWMD_COMMAND_SYNTAX;
1430 prefix = g_strdup(*req);
1432 if (!prefix) {
1433 g_strfreev(req);
1434 return gpg_error_from_errno(ENOMEM);
1437 account = g_strdup(*req);
1439 if (!account) {
1440 g_free(prefix);
1441 g_strfreev(req);
1442 return gpg_error_from_errno(ENOMEM);
1445 oreq = g_strdupv(req);
1447 if (!oreq) {
1448 g_free(prefix);
1449 g_free(account);
1450 g_strfreev(req);
1451 return gpg_error_from_errno(ENOMEM);
1454 p = req;
1455 again:
1456 account_has_target = FALSE;
1457 account_is_literal = is_literal_element_str(prefix);
1458 n = find_account(doc, &p, &error, &account_has_target);
1460 if (which)
1461 oreq = p;
1462 else
1463 req = p;
1465 if (!n)
1466 goto fail;
1468 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1469 tmp = g_strdup_printf("!%s", prefix);
1471 if (!tmp) {
1472 error = gpg_error_from_errno(ENOMEM);
1473 goto fail;
1476 g_free(prefix);
1477 prefix = tmp;
1480 if (*(p+1)) {
1481 gchar *t;
1483 n = find_elements(doc, n->children, p+1, &error,
1484 NULL, NULL, NULL, NULL);
1486 if (error)
1487 goto fail;
1489 tmp = g_strjoinv("\t", p+1);
1490 if (!tmp) {
1491 error = gpg_error_from_errno(ENOMEM);
1492 goto fail;
1495 t = g_strdup_printf("%s\t%s", prefix, tmp);
1496 if (!t) {
1497 error = gpg_error_from_errno(ENOMEM);
1498 goto fail;
1501 g_free(prefix);
1502 prefix = t;
1503 g_free(tmp);
1506 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1507 error = gpg_error_from_errno(ENOMEM);
1508 goto fail;
1511 if (node_has_child_element(n->children) == FALSE) {
1512 if (append_to_element_list(elements) == FALSE) {
1513 error = gpg_error_from_errno(ENOMEM);
1514 goto fail;
1517 else
1518 error = do_list_recurse(doc, n->children, elements, prefix);
1520 if (error)
1521 goto fail;
1523 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1524 g_free(*oreq);
1525 *oreq = g_strdup_printf("!%s", account);
1527 if (!*oreq) {
1528 error = gpg_error_from_errno(ENOMEM);
1529 goto fail;
1532 p = oreq;
1533 g_free(prefix);
1534 prefix = g_strdup(*oreq);
1536 if (!prefix) {
1537 error = gpg_error_from_errno(ENOMEM);
1538 goto fail;
1541 goto again;
1544 fail:
1545 g_free(prefix);
1546 g_free(account);
1547 g_strfreev(req);
1549 if (oreq)
1550 g_strfreev(oreq);
1552 return error;
1556 * This could be faster especially when finding "target" attributes.
1558 static int list_command(assuan_context_t ctx, char *line)
1560 struct client_s *client = assuan_get_pointer(ctx);
1561 gpg_error_t error;
1562 struct list_element_s *elements = NULL;
1563 GString *string;
1564 gchar *tmp;
1565 gint i, total;
1567 if (disable_list_and_dump == TRUE)
1568 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1570 error = file_modified(client);
1572 if (error) {
1573 log_write("%s: %s", client->filename, pwmd_strerror(error));
1574 return send_error(ctx, error);
1577 if (!*line) {
1578 GString *str;
1580 error = list_accounts(client->doc, &str);
1582 if (error)
1583 return send_error(ctx, error);
1585 error = assuan_send_data(ctx, str->str, str->len);
1586 g_string_free(str, TRUE);
1587 return send_error(ctx, error);
1590 elements = g_malloc0(sizeof(struct list_element_s));
1592 if (!elements) {
1593 error = gpg_error_from_errno(ENOMEM);
1594 goto fail;
1597 is_list_command = TRUE;
1598 error = do_list_command(ctx, client->doc, elements, line);
1600 if (error)
1601 goto fail;
1603 if (!error) {
1604 total = g_slist_length(elements->list);
1606 if (!total) {
1607 error = EPWMD_EMPTY_ELEMENT;
1608 goto fail;
1612 * Find element paths with a target and append those element trees to
1613 * the list.
1615 for (i = 0; i < total; i++) {
1616 gchar **req;
1618 tmp = g_slist_nth_data(elements->list, i);
1619 req = split_input_line(tmp, "\t", 0);
1621 if (!req) {
1622 if (g_str_has_prefix(tmp, "!") == TRUE) {
1623 g_strfreev(req);
1624 continue;
1627 else {
1628 gchar **p;
1630 for (p = req; *p; p++) {
1631 if (g_str_has_prefix(*p, "!") == FALSE)
1632 break;
1635 if (!*p) {
1636 g_strfreev(req);
1637 continue;
1641 g_strfreev(req);
1642 error = do_list_command(ctx, client->doc, elements, tmp);
1644 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1645 goto fail;
1647 total = g_slist_length(elements->list);
1651 string = g_string_new(NULL);
1653 for (i = 0; i < total; i++) {
1654 tmp = g_slist_nth_data(elements->list, i);
1655 g_string_append_printf(string, "%s\n", tmp);
1656 g_free(tmp);
1659 string = g_string_truncate(string, string->len - 1);
1660 error = assuan_send_data(ctx, string->str, string->len);
1661 g_string_free(string, TRUE);
1663 fail:
1664 is_list_command = FALSE;
1666 if (elements) {
1667 if (elements->list)
1668 g_slist_free(elements->list);
1670 if (elements->elements)
1671 g_strfreev(elements->elements);
1673 g_free(elements);
1676 return send_error(ctx, error);
1679 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1680 const gchar *value)
1682 xmlAttrPtr a;
1684 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1685 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1687 if (!a)
1688 return EPWMD_LIBXML_ERROR;
1690 else
1691 xmlNodeSetContent(a->children, (xmlChar *)value);
1693 return 0;
1697 * req[0] - element path
1699 static int attribute_list(assuan_context_t ctx, gchar **req)
1701 struct client_s *client = assuan_get_pointer(ctx);
1702 gchar **attrlist = NULL;
1703 gint i = 0;
1704 gchar **path = NULL;
1705 xmlAttrPtr a;
1706 xmlNodePtr n, an;
1707 gchar *line;
1708 gpg_error_t error;
1710 if (!req || !req[0])
1711 return EPWMD_COMMAND_SYNTAX;
1713 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1715 * The first argument may be only an account.
1717 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1718 return EPWMD_COMMAND_SYNTAX;
1721 n = find_account(client->doc, &path, &error, NULL);
1723 if (!n) {
1724 g_strfreev(path);
1725 return error;
1728 if (path[1]) {
1729 n = find_elements(client->doc, n->children, path+1, &error,
1730 NULL, NULL, NULL, NULL);
1732 if (!n) {
1733 g_strfreev(path);
1734 return error;
1738 g_strfreev(path);
1740 for (a = n->properties; a; a = a->next) {
1741 gchar **pa;
1743 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1744 if (attrlist)
1745 g_strfreev(attrlist);
1747 error = errno;
1748 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1749 return gpg_error_from_errno(error);
1752 attrlist = pa;
1753 an = a->children;
1754 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1756 if (!attrlist[i]) {
1757 g_strfreev(attrlist);
1758 return gpg_error_from_errno(ENOMEM);
1761 attrlist[++i] = NULL;
1764 if (!attrlist)
1765 return EPWMD_EMPTY_ELEMENT;
1767 line = g_strjoinv("\n", attrlist);
1769 if (!line) {
1770 g_strfreev(attrlist);
1771 return gpg_error_from_errno(ENOMEM);
1774 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1775 g_free(line);
1776 g_strfreev(attrlist);
1777 return error;
1781 * req[0] - attribute
1782 * req[1] - element path
1784 static int attribute_delete(struct client_s *client, gchar **req)
1786 xmlAttrPtr a;
1787 xmlNodePtr n;
1788 gchar **path = NULL;
1789 gpg_error_t error;
1791 if (!req || !req[0] || !req[1])
1792 return EPWMD_COMMAND_SYNTAX;
1794 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1796 * The first argument may be only an account.
1798 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1799 return EPWMD_COMMAND_SYNTAX;
1803 * Don't remove the "name" attribute for the account element. To remove an
1804 * account use DELETE <account>.
1806 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1807 error = EPWMD_ATTR_SYNTAX;
1808 goto fail;
1811 n = find_account(client->doc, &path, &error, NULL);
1813 if (!n)
1814 goto fail;
1816 if (path[1]) {
1817 n = find_elements(client->doc, n->children, path+1, &error,
1818 NULL, NULL, NULL, NULL);
1820 if (!n)
1821 goto fail;
1824 g_strfreev(path);
1826 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1827 return EPWMD_ATTR_NOT_FOUND;
1829 if (xmlRemoveProp(a) == -1)
1830 return EPWMD_LIBXML_ERROR;
1832 return 0;
1834 fail:
1835 g_strfreev(path);
1836 return error;
1840 * Creates a "target" attribute. When other commands encounter an element with
1841 * this attribute, the element path is modified to the target value. If the
1842 * source element path doesn't exist when using 'ATTR SET target', it is
1843 * created, but the destination element path must exist.
1845 * req[0] - source element path
1846 * req[1] - destination element path
1848 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1850 gchar **src, **dst, *line;
1851 gpg_error_t error;
1852 xmlNodePtr n;
1854 if (!req || !req[0] || !req[1])
1855 return EPWMD_COMMAND_SYNTAX;
1857 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1859 * The first argument may be only an account.
1861 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1862 return EPWMD_COMMAND_SYNTAX;
1865 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1867 * The first argument may be only an account.
1869 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1870 error = EPWMD_COMMAND_SYNTAX;
1871 goto fail;
1875 n = find_account(client->doc, &dst, &error, NULL);
1878 * Make sure the destination element path exists.
1880 if (!n)
1881 goto fail;
1883 if (dst[1]) {
1884 n = find_elements(client->doc, n->children, dst+1, &error,
1885 NULL, NULL, NULL, NULL);
1887 if (!n)
1888 goto fail;
1891 again:
1892 n = find_account(client->doc, &src, &error, NULL);
1894 if (!n) {
1895 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1896 error = new_account(client->doc, src[0]);
1898 if (error)
1899 goto fail;
1901 goto again;
1903 else
1904 goto fail;
1907 if (src[1]) {
1908 if (!n->children)
1909 n = create_target_elements_cb(n, src+1, &error, NULL);
1910 else
1911 n = find_elements(client->doc, n->children, src+1, &error,
1912 NULL, NULL, create_target_elements_cb, NULL);
1914 if (!n)
1915 goto fail;
1918 * Reset the position of the element tree now that the elements
1919 * have been created.
1921 n = find_account(client->doc, &src, &error, NULL);
1923 if (!n)
1924 goto fail;
1926 n = find_elements(client->doc, n->children, src+1, &error,
1927 NULL, NULL, NULL, NULL);
1929 if (!n)
1930 goto fail;
1933 line = g_strjoinv("\t", dst);
1934 error = add_attribute(n, "target", line);
1936 if (error) {
1937 g_free(line);
1938 goto fail;
1941 g_free(line);
1942 g_strfreev(src);
1943 g_strfreev(dst);
1944 return 0;
1946 fail:
1947 g_strfreev(src);
1948 g_strfreev(dst);
1949 return error;
1953 * req[0] - account name
1954 * req[1] - new name
1956 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1958 gpg_error_t error;
1959 gchar **tmp;
1960 xmlNodePtr n;
1962 tmp = g_strdupv(req);
1964 if (!tmp)
1965 return gpg_error_from_errno(ENOMEM);
1967 n = find_account(client->doc, &tmp, &error, NULL);
1968 g_strfreev(tmp);
1970 if (!n)
1971 return error;
1973 if (g_utf8_collate(req[0], req[1]) == 0)
1974 return 0;
1977 * Will not overwrite an existing account.
1979 tmp = g_strdupv(req+1);
1981 if (!tmp)
1982 return gpg_error_from_errno(ENOMEM);
1984 n = find_account(client->doc, &tmp, &error, NULL);
1985 g_strfreev(tmp);
1987 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1988 return error;
1990 if (n)
1991 return EPWMD_ACCOUNT_EXISTS;
1994 * Whitespace not allowed in account names.
1996 if (contains_whitespace(req[1]) == TRUE)
1997 return EPWMD_ATTR_SYNTAX;
1999 tmp = g_strdupv(req);
2001 if (!tmp)
2002 return gpg_error_from_errno(ENOMEM);
2004 n = find_account(client->doc, &tmp, &error, NULL);
2005 g_strfreev(tmp);
2007 if (!n)
2008 return EPWMD_ELEMENT_NOT_FOUND;
2010 return add_attribute(n, "name", req[1]);
2014 * req[0] - attribute
2015 * req[1] - element path
2017 static int attribute_get(assuan_context_t ctx, gchar **req)
2019 struct client_s *client = assuan_get_pointer(ctx);
2020 xmlNodePtr n;
2021 xmlChar *a;
2022 gchar **path= NULL;
2023 gpg_error_t error;
2025 if (!req || !req[0] || !req[1])
2026 return EPWMD_COMMAND_SYNTAX;
2028 if (strchr(req[1], '\t')) {
2029 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2030 return EPWMD_COMMAND_SYNTAX;
2032 else {
2033 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2034 return EPWMD_COMMAND_SYNTAX;
2037 n = find_account(client->doc, &path, &error, NULL);
2039 if (!n)
2040 goto fail;
2042 if (path[1]) {
2043 n = find_elements(client->doc, n->children, path+1, &error,
2044 NULL, NULL, NULL, NULL);
2046 if (!n)
2047 goto fail;
2050 g_strfreev(path);
2052 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2053 return EPWMD_ATTR_NOT_FOUND;
2055 error = assuan_send_data(ctx, a, xmlStrlen(a));
2056 xmlFree(a);
2057 return error;
2059 fail:
2060 g_strfreev(path);
2061 return error;
2065 * req[0] - attribute
2066 * req[1] - element path
2067 * req[2] - value
2069 static int attribute_set(struct client_s *client, gchar **req)
2071 gchar **path = NULL;
2072 gpg_error_t error;
2073 xmlNodePtr n;
2075 if (!req || !req[0] || !req[1] || !req[2])
2076 return EPWMD_COMMAND_SYNTAX;
2079 * Reserved attribute names.
2081 if (g_utf8_collate(req[0], "name") == 0) {
2083 * Only reserved for the account element. Not the rest of the
2084 * document.
2086 if (strchr(req[1], '\t') == NULL)
2087 return name_attribute(client, req + 1);
2089 else if (g_utf8_collate(req[0], "target") == 0)
2090 return target_attribute(client, req + 1);
2092 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2094 * The first argument may be only an account.
2096 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2097 return EPWMD_COMMAND_SYNTAX;
2100 n = find_account(client->doc, &path, &error, NULL);
2102 if (!n)
2103 goto fail;
2105 if (path[1]) {
2106 n = find_elements(client->doc, n->children, path+1, &error,
2107 NULL, NULL, NULL, NULL);
2109 if (!n)
2110 goto fail;
2113 g_strfreev(path);
2114 return add_attribute(n, req[0], req[2]);
2116 fail:
2117 g_strfreev(path);
2118 return error;
2122 * req[0] - command
2123 * req[1] - attribute name or element path if command is LIST
2124 * req[2] - element path
2125 * req[2] - element path or value
2127 static int attr_command(assuan_context_t ctx, char *line)
2129 struct client_s *client = assuan_get_pointer(ctx);
2130 gchar **req = split_input_line(line, " ", 4);
2131 gpg_error_t error = 0;
2133 error = file_modified(client);
2135 if (error) {
2136 log_write("%s: %s", client->filename ? client->filename : "",
2137 pwmd_strerror(error));
2138 g_strfreev(req);
2139 return send_error(ctx, error);
2142 if (!req || !req[0] || !req[1]) {
2143 g_strfreev(req);
2144 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2147 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2148 error = attribute_set(client, req+1);
2149 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2150 error = attribute_get(ctx, req+1);
2151 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2152 error = attribute_delete(client, req+1);
2153 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2154 error = attribute_list(ctx, req+1);
2155 else
2156 error = EPWMD_COMMAND_SYNTAX;
2158 g_strfreev(req);
2159 return send_error(ctx, error);
2162 static int iscached_command(assuan_context_t ctx, char *line)
2164 gchar **req = split_input_line(line, " ", 0);
2165 guchar md5file[16];
2167 CACHE_LOCK(ctx, NULL);
2169 if (!req || !*req) {
2170 g_strfreev(req);
2171 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2174 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2175 g_strfreev(req);
2177 if (cache_iscached(md5file) == FALSE)
2178 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2180 return send_error(ctx, 0);
2183 gpg_error_t send_cache_status(assuan_context_t ctx)
2185 gchar *tmp;
2187 tmp = print_fmt("%i %i",
2188 cache_file_count(),
2189 (cache_size / sizeof(file_cache_t)) - cache_file_count());
2191 return assuan_write_status(ctx, "CACHE", tmp);
2194 static int clearcache_command(assuan_context_t ctx, char *line)
2196 struct client_s *client = assuan_get_pointer(ctx);
2197 gchar **req = split_input_line(line, " ", 0);
2198 guchar md5file[16];
2200 CACHE_LOCK(ctx, NULL);
2202 if (!req || !*req) {
2203 g_strfreev(req);
2204 cache_clear(client->md5file, 2);
2205 return send_error(ctx, 0);
2208 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2209 g_strfreev(req);
2211 if (cache_clear(md5file, 1) == FALSE)
2212 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2214 return send_error(ctx, 0);
2217 static int cachetimeout_command(assuan_context_t ctx, char *line)
2219 guchar md5file[16];
2220 glong timeout;
2221 gchar **req = split_input_line(line, " ", 0);
2222 gchar *p;
2224 CACHE_LOCK(ctx, NULL);
2226 if (!req || !*req || !req[1]) {
2227 g_strfreev(req);
2228 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2231 errno = 0;
2232 timeout = strtol(req[0], &p, 10);
2234 if (errno != 0 || *p != 0) {
2235 g_strfreev(req);
2236 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2239 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2240 g_strfreev(req);
2242 if (cache_set_timeout(md5file, timeout) == FALSE)
2243 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2245 send_cache_status_all();
2246 return send_error(ctx, 0);
2249 static int dump_command(assuan_context_t ctx, char *line)
2251 xmlChar *xml;
2252 gssize len;
2253 struct client_s *client = assuan_get_pointer(ctx);
2254 gpg_error_t error;
2256 if (disable_list_and_dump == TRUE)
2257 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2259 error = file_modified(client);
2261 if (error) {
2262 log_write("%s: %s", client->filename ? client->filename : "",
2263 pwmd_strerror(error));
2264 return send_error(ctx, error);
2267 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2268 error = assuan_send_data(ctx, xml, len);
2269 xmlFree(xml);
2270 return send_error(ctx, error);
2273 static int getconfig_command(assuan_context_t ctx, gchar *line)
2275 struct client_s *client = assuan_get_pointer(ctx);
2276 gpg_error_t error = 0;
2277 gchar *p, *tmp;
2279 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2280 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2282 p = get_key_file_string(client->filename ? client->filename : "default", line);
2284 if (!p)
2285 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2287 tmp = expand_homedir(p);
2288 g_free(p);
2289 p = tmp;
2290 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2291 g_free(p);
2292 return send_error(ctx, error);
2295 void cleanup_assuan(assuan_context_t ctx)
2297 struct client_s *cl = assuan_get_pointer(ctx);
2299 cleanup_client(cl);
2302 static void reset_notify(assuan_context_t ctx)
2304 struct client_s *cl = assuan_get_pointer(ctx);
2306 cleanup_client(cl);
2309 gpg_error_t register_commands(assuan_context_t ctx)
2311 static struct {
2312 const char *name;
2313 int (*handler)(assuan_context_t, char *line);
2314 } table[] = {
2315 { "OPEN", open_command },
2316 { "SAVE", save_command },
2317 { "LIST", list_command },
2318 { "REALPATH", realpath_command },
2319 { "STORE", store_command },
2320 { "DELETE", delete_command },
2321 { "GET", get_command },
2322 { "ATTR", attr_command },
2323 { "ISCACHED", iscached_command },
2324 { "CLEARCACHE", clearcache_command },
2325 { "CACHETIMEOUT", cachetimeout_command },
2326 { "GETCONFIG", getconfig_command },
2327 { "DUMP", dump_command },
2328 { "INPUT", NULL },
2329 { "OUTPUT", NULL },
2330 { NULL, NULL }
2332 int i, rc;
2334 for (i=0; table[i].name; i++) {
2335 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2337 if (rc)
2338 return rc;
2341 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2342 if (rc)
2343 return rc;
2345 return assuan_register_reset_notify(ctx, reset_notify);
2348 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2349 guchar *key)
2351 guchar *iv;
2352 void *inbuf;
2353 gsize insize, len;
2354 guchar tkey[gcrykeysize];
2355 struct file_header_s {
2356 guint iter;
2357 guchar iv[gcryblocksize];
2358 } file_header;
2359 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2360 gcry_cipher_hd_t gh;
2361 guint iter = 0, n_iter = 0;
2362 gint iter_progress;
2363 void *outbuf = NULL;
2364 gint zerror = 0;
2365 glong outsize = 0;
2366 gpg_error_t error;
2368 if (!ctx) {
2369 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2371 if (gcryerrno)
2372 return gcryerrno;
2374 else
2375 gh = client->gh;
2377 lseek(fd, 0, SEEK_SET);
2378 insize = st.st_size - sizeof(struct file_header_s);
2379 iv = gcry_malloc(gcryblocksize);
2381 if (!iv) {
2382 if (!ctx)
2383 gcry_cipher_close(gh);
2385 return gpg_error_from_errno(ENOMEM);
2388 len = pth_read(fd, &file_header, sizeof(struct file_header_s));
2390 if (len != sizeof(file_header)) {
2391 len = errno;
2393 if (!ctx)
2394 gcry_cipher_close(gh);
2396 gcry_free(iv);
2397 errno = len;
2398 return gpg_error_from_errno(errno);
2401 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2402 inbuf = gcry_malloc(insize);
2404 if (!inbuf) {
2405 if (!ctx)
2406 gcry_cipher_close(gh);
2408 gcry_free(iv);
2409 return gpg_error_from_errno(ENOMEM);
2412 len = pth_read(fd, inbuf, insize);
2414 if (len != insize) {
2415 len = errno;
2417 if (!ctx)
2418 gcry_cipher_close(gh);
2420 gcry_free(iv);
2421 errno = len;
2422 return gpg_error_from_errno(errno);
2425 memcpy(tkey, key, sizeof(tkey));
2426 tkey[0] ^= 1;
2428 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2429 if (!ctx) {
2430 gcry_cipher_close(gh);
2431 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2433 else
2434 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2436 gcry_free(inbuf);
2437 gcry_free(iv);
2438 return gcryerrno;
2441 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2442 if (!ctx) {
2443 gcry_cipher_close(gh);
2444 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2446 else
2447 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2449 gcry_free(inbuf);
2450 gcry_free(iv);
2452 if (!ctx)
2453 gcry_cipher_close(gh);
2455 return gcryerrno;
2458 iter_progress = get_key_file_integer("default", "iteration_progress");
2460 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2461 error = assuan_write_status(client->ctx, "DECRYPT", "0");
2463 if (error) {
2464 gcry_free(inbuf);
2465 gcry_free(iv);
2466 return error;
2470 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2471 if (!ctx) {
2472 gcry_cipher_close(gh);
2473 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2475 else
2476 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2478 gcry_free(inbuf);
2479 gcry_free(iv);
2480 return gcryerrno;
2483 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2484 if (!ctx) {
2485 gcry_cipher_close(gh);
2486 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2488 else
2489 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2491 gcry_free(inbuf);
2492 gcry_free(iv);
2493 return gcryerrno;
2496 while (iter < file_header.iter) {
2497 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2498 if (!(iter % iter_progress)) {
2499 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2500 ++n_iter * iter_progress));
2502 if (error) {
2503 gcry_free(inbuf);
2504 gcry_free(iv);
2505 return error;
2510 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2511 if (!ctx) {
2512 gcry_cipher_close(gh);
2513 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2515 else
2516 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2518 gcry_free(inbuf);
2519 gcry_free(iv);
2520 return gcryerrno;
2523 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2524 if (!ctx) {
2525 gcry_cipher_close(gh);
2526 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2528 else
2529 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2531 gcry_free(inbuf);
2532 gcry_free(iv);
2533 return gcryerrno;
2536 iter++;
2537 pth_yield(NULL);
2540 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2541 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2543 if (error) {
2544 gcry_free(inbuf);
2545 gcry_free(iv);
2546 return error;
2550 gcry_free(iv);
2552 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2554 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2556 if (zerror == Z_MEM_ERROR) {
2557 gcry_free(inbuf);
2558 return gpg_error_from_errno(ENOMEM);
2560 else if (zerror != Z_DATA_ERROR) {
2561 gcry_free(inbuf);
2563 if (!ctx)
2564 gcry_cipher_close(gh);
2566 return EPWMD_BADKEY;
2569 else {
2570 gcry_free(inbuf);
2571 inbuf = outbuf;
2572 insize = outsize;
2575 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2576 gcry_free(inbuf);
2578 if (!ctx)
2579 gcry_cipher_close(gh);
2581 return EPWMD_BADKEY;
2584 if (ctx) {
2585 client->xml = inbuf;
2586 client->len = insize;
2588 else {
2589 gcry_cipher_close(gh);
2590 gcry_free(inbuf);
2593 return 0;