A couple UTF8 fixes. Don't calculate bytes as characters.
[pwmd.git] / src / commands.c
blob801e9b69a283695456543e3b5f6d4d9c1036f08a
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 gpg_error_t rc;
88 if ((rc = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
89 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
90 return FALSE;
93 return TRUE;
96 gpg_error_t decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
97 void *inbuf, gsize insize)
99 gpg_error_t rc;
101 if ((rc = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize)))
102 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
104 return rc;
107 static gpg_error_t parse_xml(assuan_context_t ctx)
109 struct client_s *client = assuan_get_pointer(ctx);
111 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
113 if (!client->doc)
114 return EPWMD_LIBXML_ERROR;
116 return 0;
119 gboolean valid_filename(const gchar *filename)
121 const gchar *p;
123 if (!filename || !*filename)
124 return FALSE;
126 for (p = filename; *p; p++) {
127 if (g_ascii_isalnum(*p) == FALSE && *p != '-' && *p != '_' && *p != '.')
128 return FALSE;
131 return TRUE;
134 gint open_file(const gchar *filename, struct stat *st)
136 gint fd;
138 if ((fd = open(filename, O_RDONLY)) == -1)
139 return -1;
141 if (stat(filename, st) == -1) {
142 close(fd);
143 return -1;
146 return fd;
149 static void cleanup_client(struct client_s *client)
151 assuan_context_t ctx = client->ctx;
154 * This may be a new file so don't use a cache slot. save_command() will
155 * set this to FALSE on success.
157 if (client->new == TRUE)
158 cache_clear(client->md5file, 1);
160 if (client->doc)
161 xmlFreeDoc(client->doc);
163 if (client->xml)
164 gcry_free(client->xml);
166 if (client->filename)
167 g_free(client->filename);
169 gcry_cipher_close(client->gh);
170 memset(client, 0, sizeof(struct client_s));
171 client->state = STATE_CONNECTED;
172 client->ctx = ctx;
173 client->freed = TRUE;
176 static gchar *print_fmt(const char *fmt, ...)
178 va_list ap;
179 static gchar buf[ASSUAN_LINELENGTH] = {0};
181 va_start(ap, fmt);
182 vsnprintf(buf, sizeof(buf), fmt, ap);
183 va_end(ap);
184 return buf;
187 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
188 gpointer *out, glong *outsize, gint *error)
190 z_stream z;
191 gint ret;
192 gpointer pout;
193 gz_header h;
194 gchar buf[17];
195 gpg_error_t rc;
197 z.zalloc = z_alloc;
198 z.zfree = z_free;
199 z.next_in = in;
200 z.avail_in = insize;
201 z.avail_out = zlib_bufsize;
202 z.next_out = pout = g_malloc(zlib_bufsize);
204 if (!pout) {
205 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
206 *error = Z_MEM_ERROR;
207 return FALSE;
210 ret = inflateInit2(&z, 47);
212 if (ret != Z_OK) {
213 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
214 g_free(pout);
215 return FALSE;
218 memset(&h, 0, sizeof(gz_header));
219 h.comment = (guchar *)buf;
220 h.comm_max = sizeof(buf);
221 ret = inflateGetHeader(&z, &h);
223 if (ret != Z_OK) {
224 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
225 g_free(pout);
226 inflateEnd(&z);
227 return FALSE;
230 ret = inflate(&z, Z_BLOCK);
232 if (ret != Z_OK) {
233 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
234 g_free(pout);
235 inflateEnd(&z);
236 return FALSE;
239 if (h.comment)
240 insize = atoi((gchar *)h.comment);
242 do {
243 gpointer p;
245 ret = inflate(&z, Z_FINISH);
247 switch (ret) {
248 case Z_OK:
249 break;
250 case Z_BUF_ERROR:
251 if (!z.avail_out) {
252 p = g_realloc(pout, z.total_out + zlib_bufsize);
254 if (!p) {
255 ret = Z_MEM_ERROR;
256 goto fail;
259 pout = p;
260 z.next_out = pout + z.total_out;
261 z.avail_out = zlib_bufsize;
263 if (ctx) {
264 rc = assuan_write_status(ctx, "DECOMPRESS",
265 print_fmt("%i %i", z.total_out, insize));
267 if (rc) {
268 ret = rc;
269 goto fail;
273 break;
274 case Z_STREAM_END:
275 break;
276 default:
277 goto fail;
278 break;
281 pth_yield(NULL);
282 } while (ret != Z_STREAM_END);
284 if (ctx) {
285 rc = assuan_write_status(ctx, "DECOMPRESS",
286 print_fmt("%i %i", z.total_out, insize));
288 if (rc) {
289 ret = rc;
290 goto fail;
294 *out = pout;
295 *outsize = z.total_out;
296 inflateEnd(&z);
297 return TRUE;
299 fail:
300 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
301 *error = ret;
302 g_free(pout);
303 inflateEnd(&z);
304 return FALSE;
307 static int open_command(assuan_context_t ctx, char *line)
309 gint fd;
310 struct stat st;
311 guchar shakey[gcrykeysize];
312 gint cached = 0;
313 gint timeout;
314 gpg_error_t error;
315 struct client_s *client = assuan_get_pointer(ctx);
316 gchar **req;
317 gchar *filename = NULL;
319 CACHE_LOCK(ctx, NULL);
321 if ((req = split_input_line(line, " ", 2)) != NULL)
322 filename = req[0];
324 if (!filename || !*filename) {
325 g_strfreev(req);
326 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
329 if (valid_filename(filename) == FALSE) {
330 g_strfreev(req);
331 return send_error(ctx, EPWMD_INVALID_FILENAME);
334 if (client->state == STATE_OPEN)
335 cleanup_client(client);
337 client->freed = FALSE;
339 if ((error = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
340 g_strfreev(req);
341 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
342 cleanup_client(client);
343 return send_error(ctx, error);
346 if (stat(filename, &st) == 0) {
347 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
348 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
349 g_strfreev(req);
350 cleanup_client(client);
351 return send_error(ctx, EPWMD_INVALID_FILENAME);
354 client->mtime = st.st_mtime;
357 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
360 * New files don't need a key.
362 if (access(filename, R_OK|W_OK) != 0) {
363 if (errno != ENOENT) {
364 error = errno;
365 log_write("%s: %s", filename, strerror(errno));
366 g_strfreev(req);
367 cleanup_client(client);
368 return send_syserror(ctx, error);
370 new_doc:
371 if ((client->xml = new_document()) == NULL) {
372 log_write("%s", strerror(ENOMEM));
373 g_strfreev(req);
374 cleanup_client(client);
375 return send_syserror(ctx, ENOMEM);
378 client->len = xmlStrlen(client->xml);
380 if (cache_has_file(client->md5file) == TRUE)
381 cache_clear(client->md5file, 1);
383 if (cache_add_file(client->md5file, NULL) == FALSE) {
384 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
385 g_strfreev(req);
386 cleanup_client(client);
387 return send_error(ctx, EPWMD_MAX_SLOTS);
390 client->new = TRUE;
391 client->filename = g_strdup(filename);
393 if (!client->filename) {
394 g_strfreev(req);
395 cleanup_client(client);
396 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
397 return send_syserror(ctx, ENOMEM);
400 if (req[1] && *req[1]) {
401 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
402 memset(req[1], 0, strlen(req[1]));
403 goto update_cache;
406 goto done;
409 if ((fd = open_file(filename, &st)) == -1) {
410 error = errno;
411 log_write("%s: %s", filename, strerror(errno));
412 g_strfreev(req);
413 cleanup_client(client);
414 return send_syserror(ctx, error);
417 if (st.st_size == 0)
418 goto new_doc;
420 if (cache_get_key(client->md5file, shakey) == TRUE)
421 cached = 1;
422 else {
424 * No key specified and no matching filename found in the cache.
426 if (!req[1] || !*req[1]) {
427 close(fd);
428 g_strfreev(req);
429 cleanup_client(client);
430 return send_error(ctx, EPWMD_KEY);
434 if (!cached) {
435 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
436 memset(req[1], 0, strlen(req[1]));
439 error = try_xml_decrypt(ctx, fd, st, shakey);
441 if (error) {
442 close(fd);
443 memset(shakey, 0, sizeof(shakey));
444 g_strfreev(req);
445 cleanup_client(client);
446 return send_error(ctx, error);
449 close(fd);
450 client->filename = g_strdup(filename);
452 if (!client->filename) {
453 memset(shakey, 0, sizeof(shakey));
454 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
455 g_strfreev(req);
456 cleanup_client(client);
457 return send_syserror(ctx, ENOMEM);
460 update_cache:
461 if (!cached) {
462 if (cache_update_key(client->md5file, shakey) == FALSE) {
463 memset(shakey, 0, sizeof(shakey));
464 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
465 g_strfreev(req);
466 cleanup_client(client);
467 return send_error(ctx, EPWMD_MAX_SLOTS);
470 timeout = get_key_file_integer(client->filename, "cache_timeout");
471 cache_reset_timeout(client->md5file, timeout);
473 else
474 cache_set_timeout(client->md5file, -2);
476 memset(shakey, 0, sizeof(shakey));
478 done:
479 g_strfreev(req);
480 error = parse_xml(ctx);
482 if (client->xml) {
483 gcry_free(client->xml);
484 client->xml = NULL;
487 if (error == 0)
488 client->state = STATE_OPEN;
490 return send_error(ctx, error);
493 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
494 gint size, gpointer *out, glong *outsize, gint *error)
496 z_stream z;
497 gpointer pout, pin;
498 gint ret;
499 gz_header h;
500 gchar buf[17];
501 gint cmd = Z_NO_FLUSH;
502 gpg_error_t rc;
504 z.zalloc = z_alloc;
505 z.zfree = z_free;
506 z.next_in = pin = data;
507 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
508 z.avail_out = zlib_bufsize;
509 z.next_out = pout = g_malloc(zlib_bufsize);
511 if (!pout) {
512 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
513 *error = Z_MEM_ERROR;
514 return FALSE;
517 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
519 if (ret != Z_OK) {
520 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
521 *error = ret;
522 g_free(pout);
523 return FALSE;
526 memset(&h, 0, sizeof(gz_header));
527 snprintf(buf, sizeof(buf), "%i", size);
528 h.comment = (guchar *)buf;
529 ret = deflateSetHeader(&z, &h);
531 if (ret != Z_OK) {
532 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
533 *error = ret;
534 g_free(pout);
535 deflateEnd(&z);
536 return FALSE;
539 do {
540 gpointer p;
542 ret = deflate(&z, cmd);
544 switch (ret) {
545 case Z_OK:
546 break;
547 case Z_BUF_ERROR:
548 if (!z.avail_out) {
549 p = g_realloc(pout, z.total_out + zlib_bufsize);
551 if (!p) {
552 ret = Z_MEM_ERROR;
553 goto fail;
556 pout = p;
557 z.next_out = pout + z.total_out;
558 z.avail_out = zlib_bufsize;
561 if (!z.avail_in && z.total_in < size) {
562 if (z.total_in + zlib_bufsize > size)
563 z.avail_in = size - z.total_in;
564 else
565 z.avail_in = zlib_bufsize;
567 if (ctx) {
568 rc = assuan_write_status(ctx, "COMPRESS",
569 print_fmt("%i %i", z.total_in, size));
571 if (rc) {
572 ret = rc;
573 goto fail;
578 if (z.total_in >= size)
579 cmd = Z_FINISH;
581 break;
582 case Z_STREAM_END:
583 break;
584 default:
585 goto fail;
588 pth_yield(NULL);
589 } while (ret != Z_STREAM_END);
591 if (ctx) {
592 rc = assuan_write_status(ctx, "COMPRESS",
593 print_fmt("%i %i", z.total_in, size));
595 if (rc) {
596 ret = rc;
597 goto fail;
601 *out = pout;
602 *outsize = z.total_out;
603 deflateEnd(&z);
604 return TRUE;
606 fail:
607 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
608 *error = ret;
609 g_free(pout);
610 deflateEnd(&z);
611 return FALSE;
614 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
615 const gchar *filename, gpointer data, size_t insize,
616 const guchar *shakey, guint iter)
618 gsize len = insize;
619 gint fd;
620 gpointer inbuf;
621 guchar tkey[gcrykeysize];
622 gchar *p;
623 gint error;
624 gpg_error_t rc;
625 guint iter_progress = 0, n_iter = 0, xiter = 0;
626 gchar tmp[FILENAME_MAX];
627 struct file_header_s {
628 guint iter;
629 guchar iv[gcryblocksize];
630 } file_header;
632 if (insize / gcryblocksize) {
633 len = (insize / gcryblocksize) * gcryblocksize;
635 if (insize % gcryblocksize)
636 len += gcryblocksize;
640 * Resize the existing xml buffer to the block size required by gcrypt
641 * rather than duplicating it and wasting memory.
643 inbuf = gcry_realloc(data, len);
645 if (!inbuf)
646 return gpg_error_from_errno(ENOMEM);
648 insize = len;
649 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
650 memcpy(tkey, shakey, sizeof(tkey));
651 tkey[0] ^= 1;
653 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
654 gcry_free(inbuf);
655 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
656 return rc;
659 file_header.iter = iter;
661 if (client)
662 iter_progress = get_key_file_integer("default", "iteration_progress");
664 if (client && iter_progress && file_header.iter >= iter_progress) {
665 error = assuan_write_status(client->ctx, "ENCRYPT", "0");
667 if (error) {
668 gcry_free(inbuf);
669 return error;
673 while (xiter < file_header.iter) {
674 if (client && iter_progress > 0 && xiter >= iter_progress) {
675 if (!(xiter % iter_progress)) {
676 error = assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
677 ++n_iter * iter_progress));
679 if (error) {
680 gcry_free(inbuf);
681 return error;
686 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
687 sizeof(file_header.iv)))) {
688 gcry_free(inbuf);
689 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
690 return rc;
693 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
694 == FALSE) {
695 gcry_free(inbuf);
696 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
697 return rc;
700 xiter++;
701 pth_yield(NULL);
704 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
705 sizeof(file_header.iv)))) {
706 gcry_free(inbuf);
707 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
708 return rc;
711 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
712 gcry_free(inbuf);
713 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
714 return rc;
717 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
718 gcry_free(inbuf);
719 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
720 return rc;
723 if (client && iter_progress && file_header.iter >= iter_progress) {
724 error = assuan_write_status(client->ctx, "ENCRYPT",
725 print_fmt("%i", file_header.iter));
727 if (error) {
728 gcry_free(inbuf);
729 return error;
733 if (filename) {
734 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
736 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
737 error = errno;
738 gcry_free(inbuf);
739 p = strrchr(tmp, '/');
740 p++;
741 log_write("%s: %s", p, strerror(errno));
742 return gpg_error_from_errno(error);
745 else
747 * xml_import() from command line.
749 fd = STDOUT_FILENO;
751 len = pth_write(fd, &file_header, sizeof(struct file_header_s));
753 if (len != sizeof(file_header)) {
754 len = errno;
756 if (filename)
757 close(fd);
759 gcry_free(inbuf);
760 return gpg_error_from_errno(len);
763 len = pth_write(fd, inbuf, insize);
765 if (len != insize) {
766 len = errno;
768 if (filename)
769 close(fd);
771 gcry_free(inbuf);
772 return gpg_error_from_errno(len);
775 if (fsync(fd) == -1) {
776 len = errno;
777 close(fd);
778 gcry_free(inbuf);
779 return gpg_error_from_errno(len);
782 if (filename) {
783 struct stat st;
784 mode_t mode = 0;
786 close(fd);
788 if (stat(filename, &st) == 0)
789 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
791 if (rename(tmp, filename) == -1) {
792 len = errno;
793 gcry_free(inbuf);
794 return gpg_error_from_errno(len);
797 if (mode)
798 chmod(filename, mode);
801 gcry_free(inbuf);
802 return 0;
805 static int save_command(assuan_context_t ctx, char *line)
807 gpointer xmlbuf;
808 xmlChar *p;
809 gint len;
810 gint cached = 0;
811 guchar shakey[gcrykeysize];
812 gint iter;
813 struct stat st;
814 struct client_s *client = assuan_get_pointer(ctx);
815 gpg_error_t error;
816 gint timeout;
817 gpointer outbuf;
818 glong outsize = 0;
819 gint zerror;
821 CACHE_LOCK(ctx, NULL);
822 error = file_modified(client);
824 if (error) {
825 log_write("%s: %s", client->filename ? client->filename : "",
826 pwmd_strerror(error));
827 return send_error(ctx, error);
830 if (stat(client->filename, &st) == -1 && errno != ENOENT)
831 return send_syserror(ctx, errno);
833 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
834 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
835 return send_error(ctx, EPWMD_INVALID_FILENAME);
838 if (!line || !*line) {
839 if (cache_get_key(client->md5file, shakey) == FALSE)
840 return send_error(ctx, EPWMD_KEY);
842 cached = 1;
844 else {
845 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
846 memset(line, 0, strlen(line));
849 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
850 xmlbuf = p;
852 iter = get_key_file_integer(client->filename, "compression_level");
854 if (iter < 0)
855 iter = 0;
857 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
858 memset(shakey, 0, sizeof(shakey));
859 xmlFree(xmlbuf);
861 if (zerror == Z_MEM_ERROR) {
862 return send_syserror(ctx, ENOMEM);
864 else
865 return send_error(ctx, GPG_ERR_COMPR_ALGO);
867 else {
868 gcry_free(xmlbuf);
869 xmlbuf = outbuf;
870 len = outsize;
873 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
874 iter = 0;
876 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
878 if (error) {
879 memset(shakey, 0, sizeof(shakey));
880 return send_error(ctx, error);
883 stat(client->filename, &st);
884 client->mtime = st.st_mtime;
885 timeout = get_key_file_integer(client->filename, "cache_timeout");
887 if (cached) {
888 memset(shakey, 0, sizeof(shakey));
889 cache_reset_timeout(client->md5file, timeout);
890 client->new = FALSE;
891 return send_error(ctx, 0);
894 if (cache_update_key(client->md5file, shakey) == FALSE) {
895 memset(shakey, 0, sizeof(shakey));
896 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
897 return send_error(ctx, EPWMD_MAX_SLOTS);
900 client->new = FALSE;
901 memset(shakey, 0, sizeof(shakey));
902 cache_reset_timeout(client->md5file, timeout);
903 return send_error(ctx, 0);
906 static gboolean contains_whitespace(const gchar *str)
908 const gchar *p = str;
909 gunichar c;
910 glong len;
912 len = g_utf8_strlen(p++, -1) -1;
914 while (len--) {
915 c = g_utf8_get_char(p++);
917 if (g_unichar_isspace(c))
918 return TRUE;
921 return FALSE;
924 static int delete_command(assuan_context_t ctx, char *line)
926 struct client_s *client = assuan_get_pointer(ctx);
927 gchar **req;
928 gpg_error_t error;
929 xmlNodePtr n;
931 error = file_modified(client);
933 if (error) {
934 log_write("%s: %s", client->filename ? client->filename : "",
935 pwmd_strerror(error));
936 return send_error(ctx, error);
939 if (strchr(line, '\t'))
940 req = split_input_line(line, "\t", -1);
941 else
942 req = split_input_line(line, " ", -1);
944 if (!req || !*req)
945 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
947 n = find_account(client->doc, &req, &error, NULL, 0);
949 if (!n) {
950 g_strfreev(req);
951 return send_error(ctx, error);
955 * No sub-node defined. Remove the entire node (account).
957 if (!req[1]) {
958 if (n) {
959 xmlUnlinkNode(n);
960 xmlFreeNode(n);
963 g_strfreev(req);
964 return send_error(ctx, 0);
967 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
968 g_strfreev(req);
970 if (!n)
971 return send_error(ctx, error);
973 if (n) {
974 xmlUnlinkNode(n);
975 xmlFreeNode(n);
978 return send_error(ctx, 0);
982 * Don't return with assuan_process_done() here. This has been called from
983 * assuan_process_next() and the command should be finished in
984 * client_thread().
986 #define INQUIRE_RETURN(client, rc) {client->inquire_error = rc; return rc;}
988 static int store_command_finalize(gpointer data, gint rc, guchar *line,
989 gsize len)
991 assuan_context_t ctx = data;
992 struct client_s *client = assuan_get_pointer(ctx);
993 gchar **req;
994 guchar *result = line;
995 xmlNodePtr n;
996 gpg_error_t error = file_modified(client);
998 if (rc) {
999 if (line)
1000 #ifndef MEM_DEBUG
1001 xfree(line);
1002 #else
1003 free(line);
1004 #endif
1005 return rc;
1008 req = split_input_line((gchar *)result, "\t", 0);
1009 #ifndef MEM_DEBUG
1010 xfree(line);
1011 #else
1012 free(line);
1013 #endif
1015 if (rc) {
1016 if (req)
1017 g_strfreev(req);
1019 INQUIRE_RETURN(client, rc);
1022 if (!req || !*req)
1023 INQUIRE_RETURN(client, EPWMD_COMMAND_SYNTAX);
1025 again:
1026 n = find_account(client->doc, &req, &error, NULL, 0);
1028 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1029 if (contains_whitespace(*req) == TRUE) {
1030 g_strfreev(req);
1031 INQUIRE_RETURN(client, EPWMD_INVALID_ELEMENT);
1034 error = new_account(client->doc, *req);
1036 if (error) {
1037 g_strfreev(req);
1038 INQUIRE_RETURN(client, error);
1041 goto again;
1044 if (!n) {
1045 g_strfreev(req);
1046 INQUIRE_RETURN(client, error);
1049 if (req[1]) {
1050 if (!n->children)
1051 create_elements_cb(n, req+1, &error, NULL);
1052 else
1053 find_elements(client->doc, n->children, req+1, &error,
1054 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1057 g_strfreev(req);
1058 client->inquire = INQUIRE_OK;
1059 INQUIRE_RETURN(client, error);
1062 static int store_command(assuan_context_t ctx, char *line)
1064 struct client_s *client = assuan_get_pointer(ctx);
1065 gpg_error_t error = file_modified(client);
1067 if (error) {
1068 log_write("%s: %s", client->filename ? client->filename : "",
1069 pwmd_strerror(error));
1070 return send_error(ctx, error);
1073 error = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1075 if (error)
1076 return send_error(ctx, error);
1078 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1079 client->inquire = INQUIRE_INPROGRESS;
1080 return 0;
1083 static int get_command(assuan_context_t ctx, char *line)
1085 struct client_s *client = assuan_get_pointer(ctx);
1086 gchar **req;
1087 gpg_error_t error;
1088 xmlNodePtr n;
1090 error = file_modified(client);
1092 if (error) {
1093 log_write("%s: %s", client->filename ? client->filename : "",
1094 pwmd_strerror(error));
1095 return send_error(ctx, error);
1098 req = split_input_line(line, "\t", -1);
1100 if (!req || !*req) {
1101 g_strfreev(req);
1102 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1105 n = find_account(client->doc, &req, &error, NULL, 0);
1107 if (!n) {
1108 g_strfreev(req);
1109 return send_error(ctx, error);
1112 if (req[1])
1113 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1115 g_strfreev(req);
1117 if (error)
1118 return send_error(ctx, error);
1120 if (!n || !n->children)
1121 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1123 n = n->children;
1125 if (!n || !n->content || !*n->content)
1126 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1128 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1129 return send_error(ctx, error);
1132 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1134 xmlChar *p = path;
1135 gint n;
1136 gchar *buf;
1138 if (!p)
1139 return NULL;
1141 for (n = 0; *p && n < 3; p++) {
1142 if (*p == '/')
1143 n++;
1146 if (strstr((gchar *)p, "text()") != NULL)
1147 p[xmlStrlen(p) - 7] = 0;
1149 for (n = 0; p[n]; n++) {
1150 if (p[n] == '/')
1151 p[n] = '\t';
1154 buf = g_strdup_printf("%s\t%s", account, p);
1155 return buf;
1158 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1160 gchar **a;
1161 va_list ap;
1162 gchar *buf;
1163 gint len = *array ? g_strv_length(*array) : 0;
1164 gint ret;
1166 if (!fmt)
1167 return FALSE;
1169 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1170 return FALSE;
1172 va_start(ap, fmt);
1173 ret = g_vasprintf(&buf, fmt, ap);
1174 va_end(ap);
1176 if (ret == -1)
1177 return FALSE;
1179 a[len++] = buf;
1180 a[len] = NULL;
1181 *array = a;
1182 return TRUE;
1185 struct realpath_s {
1186 gchar *account;
1189 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1190 gpg_error_t *error, void *data)
1192 struct realpath_s *rp = data;
1194 if (rp->account)
1195 g_free(rp->account);
1197 rp->account = g_strdup(req[0]);
1199 if (!rp->account) {
1200 *error = gpg_error_from_errno(ENOMEM);
1201 return NULL;
1204 return node;
1207 static int realpath_command(assuan_context_t ctx, char *line)
1209 gpg_error_t error;
1210 struct client_s *client = assuan_get_pointer(ctx);
1211 xmlChar *p;
1212 gchar **req;
1213 gchar *result, *t;
1214 gint i;
1215 xmlNodePtr n;
1216 struct realpath_s *rp;
1217 GString *string;
1219 error = file_modified(client);
1221 if (error) {
1222 log_write("%s: %s", client->filename ? client->filename : "",
1223 pwmd_strerror(error));
1224 return send_error(ctx, error);
1227 if (strchr(line, '\t') != NULL) {
1228 if ((req = split_input_line(line, "\t", 0)) == NULL)
1229 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1231 else {
1232 if ((req = split_input_line(line, " ", 0)) == NULL)
1233 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1236 n = find_account(client->doc, &req, &error, NULL, 0);
1238 if (!n) {
1239 g_strfreev(req);
1240 return send_error(ctx, error);
1243 rp = g_malloc(sizeof(struct realpath_s));
1245 if (!rp) {
1246 g_strfreev(req);
1247 return send_syserror(ctx, ENOMEM);
1250 rp->account = g_strdup(req[0]);
1252 if (!rp->account) {
1253 g_strfreev(req);
1254 return send_syserror(ctx, ENOMEM);
1257 if (req[1]) {
1258 n = find_elements(client->doc, n->children, req+1, &error,
1259 NULL, realpath_elements_cb, NULL, FALSE, 0, rp);
1261 if (!n) {
1262 g_free(rp->account);
1263 g_free(rp);
1264 g_strfreev(req);
1265 return send_error(ctx, error);
1269 p = xmlGetNodePath(n);
1270 result = element_path_to_req(rp->account, p);
1272 if (!result) {
1273 g_free(result);
1274 g_free(rp->account);
1275 g_free(rp);
1276 g_strfreev(req);
1277 xmlFree(p);
1278 return send_syserror(ctx, ENOMEM);
1281 string = g_string_new(result);
1282 g_free(result);
1283 g_free(rp->account);
1284 g_free(rp);
1285 g_strfreev(req);
1286 xmlFree(p);
1287 i = 0;
1289 again:
1290 for (t = string->str + i; *t; t++, i++) {
1291 if (!i || *t == '\t') {
1292 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1293 goto again;
1297 error = assuan_send_data(ctx, string->str, string->len);
1298 g_string_free(string, TRUE);
1299 return send_error(ctx, error);
1302 struct list_element_s {
1303 GSList *list;
1304 gchar **elements;
1307 static gboolean append_to_element_list(struct list_element_s *elements)
1309 gchar *tmp;
1310 gint i, total;
1311 gchar *a;
1312 GSList *list;
1314 if (!elements || !elements->elements)
1315 return TRUE;
1317 tmp = g_strjoinv("\t", elements->elements);
1319 if (!tmp)
1320 return FALSE;
1322 g_strfreev(elements->elements);
1323 elements->elements = NULL;
1324 total = g_slist_length(elements->list);
1325 a = g_utf8_collate_key(tmp, -1);
1327 if (!a) {
1328 g_free(tmp);
1329 return FALSE;
1333 * Removes duplicate element paths from the list. This is needed when
1334 * appending an element tree from list_command(). The glib docs recommend
1335 * using g_utf8_collate_key() for a large number of strings.
1337 for (i = 0; i < total; i++) {
1338 gchar *p = g_slist_nth_data(elements->list, i);
1339 gchar *b = g_utf8_collate_key(p, -1);
1341 if (!b) {
1342 g_free(a);
1343 g_free(tmp);
1344 return FALSE;
1347 if (strcmp(a, b) == 0) {
1348 g_free(a);
1349 g_free(b);
1350 g_free(tmp);
1351 return TRUE;
1354 g_free(b);
1357 g_free(a);
1358 list = g_slist_append(elements->list, tmp);
1360 if (!list)
1361 return FALSE;
1363 elements->list = list;
1364 return TRUE;
1367 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1368 struct list_element_s *elements, gchar *prefix)
1370 xmlNodePtr n;
1371 gpg_error_t error;
1373 if (append_to_element_list(elements) == FALSE)
1374 return gpg_error_from_errno(ENOMEM);
1376 for (n = node; n; n = n->next) {
1377 if (n->type == XML_ELEMENT_NODE) {
1378 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1379 gchar *tmp;
1381 if (content) {
1382 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1383 return gpg_error_from_errno(ENOMEM);
1385 if (append_to_element_list(elements) == FALSE)
1386 return gpg_error_from_errno(ENOMEM);
1389 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1391 if (!tmp)
1392 return gpg_error_from_errno(ENOMEM);
1394 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1395 g_free(tmp);
1396 return gpg_error_from_errno(ENOMEM);
1399 if (n->children) {
1400 error = do_list_recurse(doc, n->children, elements, tmp);
1401 g_free(tmp);
1403 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1404 return error;
1406 else
1407 g_free(tmp);
1409 if (append_to_element_list(elements) == FALSE)
1410 return gpg_error_from_errno(ENOMEM);
1414 return 0;
1417 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1418 struct list_element_s *elements, char *line)
1420 gchar *prefix = NULL, *account;
1421 gchar **req = NULL, **oreq = NULL, *tmp;
1422 xmlNodePtr n;
1423 gboolean account_is_literal, account_has_target = FALSE;
1424 gint which = 0;
1425 gchar **p;
1426 gpg_error_t error;
1428 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1429 if ((req = split_input_line(line, " ", 0)) == NULL)
1430 return EPWMD_COMMAND_SYNTAX;
1433 prefix = g_strdup(*req);
1435 if (!prefix) {
1436 g_strfreev(req);
1437 return gpg_error_from_errno(ENOMEM);
1440 account = g_strdup(*req);
1442 if (!account) {
1443 g_free(prefix);
1444 g_strfreev(req);
1445 return gpg_error_from_errno(ENOMEM);
1448 oreq = g_strdupv(req);
1450 if (!oreq) {
1451 g_free(prefix);
1452 g_free(account);
1453 g_strfreev(req);
1454 return gpg_error_from_errno(ENOMEM);
1457 p = req;
1458 again:
1459 account_has_target = FALSE;
1460 account_is_literal = is_literal_element_str(prefix);
1461 n = find_account(doc, &p, &error, &account_has_target, 0);
1463 if (which)
1464 oreq = p;
1465 else
1466 req = p;
1468 if (!n)
1469 goto fail;
1471 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1472 tmp = g_strdup_printf("!%s", prefix);
1474 if (!tmp) {
1475 error = gpg_error_from_errno(ENOMEM);
1476 goto fail;
1479 g_free(prefix);
1480 prefix = tmp;
1483 if (*(p+1)) {
1484 gchar *t;
1486 n = find_elements(doc, n->children, p+1, &error, NULL, NULL, NULL,
1487 TRUE, 0, NULL);
1489 if (error)
1490 goto fail;
1492 tmp = g_strjoinv("\t", p+1);
1493 if (!tmp) {
1494 error = gpg_error_from_errno(ENOMEM);
1495 goto fail;
1498 t = g_strdup_printf("%s\t%s", prefix, tmp);
1499 if (!t) {
1500 error = gpg_error_from_errno(ENOMEM);
1501 goto fail;
1504 g_free(prefix);
1505 prefix = t;
1506 g_free(tmp);
1509 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1510 error = gpg_error_from_errno(ENOMEM);
1511 goto fail;
1514 if (node_has_child_element(n->children) == FALSE) {
1515 if (append_to_element_list(elements) == FALSE) {
1516 error = gpg_error_from_errno(ENOMEM);
1517 goto fail;
1520 else
1521 error = do_list_recurse(doc, n->children, elements, prefix);
1523 if (error)
1524 goto fail;
1526 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1527 g_free(*oreq);
1528 *oreq = g_strdup_printf("!%s", account);
1530 if (!*oreq) {
1531 error = gpg_error_from_errno(ENOMEM);
1532 goto fail;
1535 p = oreq;
1536 g_free(prefix);
1537 prefix = g_strdup(*oreq);
1539 if (!prefix) {
1540 error = gpg_error_from_errno(ENOMEM);
1541 goto fail;
1544 goto again;
1547 fail:
1548 g_free(prefix);
1549 g_free(account);
1550 g_strfreev(req);
1552 if (oreq)
1553 g_strfreev(oreq);
1555 return error;
1559 * This could be faster especially when finding "target" attributes.
1561 static int list_command(assuan_context_t ctx, char *line)
1563 struct client_s *client = assuan_get_pointer(ctx);
1564 gpg_error_t error;
1565 struct list_element_s *elements = NULL;
1566 GString *string;
1567 gchar *tmp;
1568 gint i, total;
1570 if (disable_list_and_dump == TRUE)
1571 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1573 error = file_modified(client);
1575 if (error) {
1576 log_write("%s: %s", client->filename, pwmd_strerror(error));
1577 return send_error(ctx, error);
1580 if (!*line) {
1581 GString *str;
1583 error = list_accounts(client->doc, &str);
1585 if (error)
1586 return send_error(ctx, error);
1588 error = assuan_send_data(ctx, str->str, str->len);
1589 g_string_free(str, TRUE);
1590 return send_error(ctx, error);
1593 elements = g_malloc0(sizeof(struct list_element_s));
1595 if (!elements) {
1596 error = gpg_error_from_errno(ENOMEM);
1597 goto fail;
1600 error = do_list_command(ctx, client->doc, elements, line);
1602 if (error)
1603 goto fail;
1605 if (!error) {
1606 total = g_slist_length(elements->list);
1608 if (!total) {
1609 error = EPWMD_EMPTY_ELEMENT;
1610 goto fail;
1614 * Find element paths with a target and append those element trees to
1615 * the list.
1617 for (i = 0; i < total; i++) {
1618 gchar **req;
1620 tmp = g_slist_nth_data(elements->list, i);
1621 req = split_input_line(tmp, "\t", 0);
1623 if (!req) {
1624 if (g_str_has_prefix(tmp, "!") == TRUE) {
1625 g_strfreev(req);
1626 continue;
1629 else {
1630 gchar **p;
1632 for (p = req; *p; p++) {
1633 if (g_str_has_prefix(*p, "!") == FALSE)
1634 break;
1637 if (!*p) {
1638 g_strfreev(req);
1639 continue;
1643 g_strfreev(req);
1644 error = do_list_command(ctx, client->doc, elements, tmp);
1646 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1647 goto fail;
1649 total = g_slist_length(elements->list);
1653 string = g_string_new(NULL);
1655 for (i = 0; i < total; i++) {
1656 tmp = g_slist_nth_data(elements->list, i);
1657 g_string_append_printf(string, "%s\n", tmp);
1658 g_free(tmp);
1661 string = g_string_truncate(string, string->len - 1);
1662 error = assuan_send_data(ctx, string->str, string->len);
1663 g_string_free(string, TRUE);
1665 fail:
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, 0);
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, FALSE, 0, 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, strlen(line));
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, 0);
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, FALSE, 0, 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, 0);
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, FALSE, 0, NULL);
1887 if (!n)
1888 goto fail;
1891 again:
1892 n = find_account(client->doc, &src, &error, NULL, 0);
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, FALSE, 0, 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, 0);
1923 if (!n)
1924 goto fail;
1926 n = find_elements(client->doc, n->children, src+1, &error,
1927 NULL, NULL, NULL, FALSE, 0, 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, 0);
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, 0);
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, 0);
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, 0);
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, FALSE, 0, 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, 0);
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, FALSE, 0, 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 (strcmp(line, "key") == 0 || strcmp(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, strlen(p));
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 error = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2371 if (error)
2372 return error;
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 ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2429 if (!ctx) {
2430 gcry_cipher_close(gh);
2431 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2433 else
2434 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2436 gcry_free(inbuf);
2437 gcry_free(iv);
2438 return error;
2441 if ((error = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2442 if (!ctx) {
2443 gcry_cipher_close(gh);
2444 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2446 else
2447 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2449 gcry_free(inbuf);
2450 gcry_free(iv);
2452 if (!ctx)
2453 gcry_cipher_close(gh);
2455 return error;
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 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2472 if (error) {
2473 gcry_free(inbuf);
2474 gcry_free(iv);
2475 return error;
2478 if ((error = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2479 if (!ctx) {
2480 gcry_cipher_close(gh);
2481 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2483 else
2484 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2486 gcry_free(inbuf);
2487 gcry_free(iv);
2488 return error;
2491 while (iter < file_header.iter) {
2492 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2493 if (!(iter % iter_progress)) {
2494 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2495 ++n_iter * iter_progress));
2497 if (error) {
2498 gcry_free(inbuf);
2499 gcry_free(iv);
2500 return error;
2505 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2506 if (!ctx) {
2507 gcry_cipher_close(gh);
2508 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2510 else
2511 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2513 gcry_free(inbuf);
2514 gcry_free(iv);
2515 return error;
2518 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2520 if (error) {
2521 if (!ctx) {
2522 gcry_cipher_close(gh);
2523 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2525 else
2526 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2528 gcry_free(inbuf);
2529 gcry_free(iv);
2530 return error;
2533 iter++;
2534 pth_yield(NULL);
2537 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2538 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2540 if (error) {
2541 gcry_free(inbuf);
2542 gcry_free(iv);
2543 return error;
2547 gcry_free(iv);
2549 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2551 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2553 if (zerror == Z_MEM_ERROR) {
2554 gcry_free(inbuf);
2555 return gpg_error_from_errno(ENOMEM);
2557 else if (zerror != Z_DATA_ERROR) {
2558 gcry_free(inbuf);
2560 if (!ctx)
2561 gcry_cipher_close(gh);
2563 return EPWMD_BADKEY;
2566 else {
2567 gcry_free(inbuf);
2568 inbuf = outbuf;
2569 insize = outsize;
2572 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2573 gcry_free(inbuf);
2575 if (!ctx)
2576 gcry_cipher_close(gh);
2578 return EPWMD_BADKEY;
2581 if (ctx) {
2582 client->xml = inbuf;
2583 client->len = insize;
2585 else {
2586 gcry_cipher_close(gh);
2587 gcry_free(inbuf);
2590 return 0;
2594 * This is called after every Assuan command.
2596 void command_finalize(assuan_context_t ctx, gint error)
2598 CACHE_UNLOCK;