Non Linux 2.6 systems need to specify --disable-locking to configure.
[pwmd.git] / src / commands.c
blobfbf468ad5e053014a395743114ed93cb333ea6d5
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2007 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <glib/gprintf.h>
30 #include <gcrypt.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifdef HAVE_ZLIB_H
37 #include <zlib.h>
38 #endif
40 #ifndef MEM_DEBUG
41 #include "mem.h"
42 #endif
44 #include "xml.h"
45 #include "common.h"
46 #include "pwmd_error.h"
47 #include "cache.h"
48 #include "commands.h"
50 static void *z_alloc(void *data, unsigned items, unsigned size)
52 #ifndef MEM_DEBUG
53 return gcry_calloc(items, size);
54 #else
55 return calloc(items, size);
56 #endif
59 static void z_free(void *data, void *p)
61 #ifndef MEM_DEBUG
62 gcry_free(p);
63 #else
64 free(p);
65 #endif
68 static gpg_error_t file_modified(struct client_s *client)
70 struct stat st;
72 if (client->state != STATE_OPEN)
73 return EPWMD_NO_FILE;
75 if (stat(client->filename, &st) == 0 && client->mtime) {
76 if (client->mtime != st.st_mtime)
77 return EPWMD_FILE_MODIFIED;
80 return 0;
83 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
84 void *inbuf, gsize insize)
86 if ((gcryerrno = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
87 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
88 return FALSE;
91 return TRUE;
94 gboolean decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
95 void *inbuf, gsize insize)
97 if ((gcryerrno = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize))) {
98 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
99 return FALSE;
102 return TRUE;
105 static gpg_error_t parse_xml(assuan_context_t ctx)
107 struct client_s *client = assuan_get_pointer(ctx);
109 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
111 if (!client->doc)
112 return EPWMD_LIBXML_ERROR;
114 return 0;
117 gboolean valid_filename(const gchar *filename)
119 const gchar *p;
121 if (!filename || !*filename)
122 return FALSE;
124 for (p = filename; *p; p++) {
125 if (g_ascii_isalnum(*p) == FALSE && *p != '-' && *p != '_' && *p != '.')
126 return FALSE;
129 return TRUE;
132 gint open_file(const gchar *filename, struct stat *st)
134 gint fd;
136 if ((fd = open(filename, O_RDONLY)) == -1)
137 return -1;
139 if (stat(filename, st) == -1) {
140 close(fd);
141 return -1;
144 return fd;
147 static void cleanup_client(struct client_s *client)
149 assuan_context_t ctx = client->ctx;
152 * This may be a new file so don't use a cache slot. save_command() will
153 * set this to FALSE on success.
155 if (client->new == TRUE)
156 cache_clear(client->md5file, 1);
158 if (client->doc)
159 xmlFreeDoc(client->doc);
161 if (client->xml)
162 gcry_free(client->xml);
164 if (client->filename)
165 g_free(client->filename);
167 gcry_cipher_close(client->gh);
168 memset(client, 0, sizeof(struct client_s));
169 client->state = STATE_CONNECTED;
170 client->ctx = ctx;
171 client->freed = TRUE;
174 static gchar *print_fmt(const char *fmt, ...)
176 va_list ap;
177 static gchar buf[ASSUAN_LINELENGTH] = {0};
179 va_start(ap, fmt);
180 vsnprintf(buf, sizeof(buf), fmt, ap);
181 va_end(ap);
182 return buf;
185 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
186 gpointer *out, glong *outsize, gint *error)
188 z_stream z;
189 gint ret;
190 gpointer pout;
191 gz_header h;
192 gchar buf[17];
194 z.zalloc = z_alloc;
195 z.zfree = z_free;
196 z.next_in = in;
197 z.avail_in = insize;
198 z.avail_out = zlib_bufsize;
199 z.next_out = pout = g_malloc(zlib_bufsize);
201 if (!pout) {
202 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
203 *error = Z_MEM_ERROR;
204 return FALSE;
207 ret = inflateInit2(&z, 47);
209 if (ret != Z_OK) {
210 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
211 g_free(pout);
212 return FALSE;
215 memset(&h, 0, sizeof(gz_header));
216 h.comment = (guchar *)buf;
217 h.comm_max = sizeof(buf);
218 ret = inflateGetHeader(&z, &h);
220 if (ret != Z_OK) {
221 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
222 g_free(pout);
223 inflateEnd(&z);
224 return FALSE;
227 ret = inflate(&z, Z_BLOCK);
229 if (ret != Z_OK) {
230 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
231 g_free(pout);
232 inflateEnd(&z);
233 return FALSE;
236 if (h.comment)
237 insize = atoi((gchar *)h.comment);
239 do {
240 gpointer p;
242 ret = inflate(&z, Z_FINISH);
244 switch (ret) {
245 case Z_OK:
246 break;
247 case Z_BUF_ERROR:
248 if (!z.avail_out) {
249 p = g_realloc(pout, z.total_out + zlib_bufsize);
251 if (!p) {
252 ret = Z_MEM_ERROR;
253 goto fail;
256 pout = p;
257 z.next_out = pout + z.total_out;
258 z.avail_out = zlib_bufsize;
260 if (ctx)
261 assuan_write_status(ctx, "DECOMPRESS",
262 print_fmt("%i %i", z.total_out, insize));
264 break;
265 case Z_STREAM_END:
266 break;
267 default:
268 goto fail;
269 break;
271 } while (ret != Z_STREAM_END);
273 if (ctx)
274 assuan_write_status(ctx, "DECOMPRESS",
275 print_fmt("%i %i", z.total_out, insize));
277 *out = pout;
278 *outsize = z.total_out;
279 inflateEnd(&z);
280 return TRUE;
282 fail:
283 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
284 *error = ret;
285 g_free(pout);
286 inflateEnd(&z);
287 return FALSE;
290 static int open_command(assuan_context_t ctx, char *line)
292 gint fd;
293 struct stat st;
294 guchar shakey[gcrykeysize];
295 gint cached = 0;
296 gint timeout;
297 gpg_error_t error;
298 struct client_s *client = assuan_get_pointer(ctx);
299 gchar **req;
300 gchar *filename = NULL;
302 #ifdef WORKING_PTHREADS
303 MUTEX_LOCK(ctx);
304 #endif
306 if ((req = split_input_line(line, " ", 2)) != NULL)
307 filename = req[0];
309 if (!filename || !*filename) {
310 g_strfreev(req);
311 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
314 if (valid_filename(filename) == FALSE) {
315 g_strfreev(req);
316 return send_error(ctx, EPWMD_INVALID_FILENAME);
319 if (client->state == STATE_OPEN)
320 cleanup_client(client);
322 client->freed = FALSE;
324 if ((gcryerrno = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
325 g_strfreev(req);
326 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
327 cleanup_client(client);
328 return send_error(ctx, gcryerrno);
331 if (stat(filename, &st) == 0) {
332 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
333 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
334 g_strfreev(req);
335 cleanup_client(client);
336 return send_error(ctx, EPWMD_INVALID_FILENAME);
339 client->mtime = st.st_mtime;
342 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
345 * New files don't need a key.
347 if (access(filename, R_OK|W_OK) != 0) {
348 if (errno != ENOENT) {
349 error = errno;
350 log_write("%s: %s", filename, strerror(errno));
351 g_strfreev(req);
352 cleanup_client(client);
353 return send_syserror(ctx, error);
355 new_doc:
356 if ((client->xml = new_document()) == NULL) {
357 log_write("%s", strerror(ENOMEM));
358 g_strfreev(req);
359 cleanup_client(client);
360 return send_syserror(ctx, ENOMEM);
363 client->len = xmlStrlen(client->xml);
365 if (cache_has_file(client->md5file) == TRUE)
366 cache_clear(client->md5file, 1);
368 if (cache_add_file(client->md5file, NULL) == FALSE) {
369 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
370 g_strfreev(req);
371 cleanup_client(client);
372 return send_error(ctx, EPWMD_MAX_SLOTS);
375 client->new = TRUE;
376 client->filename = g_strdup(filename);
378 if (!client->filename) {
379 g_strfreev(req);
380 cleanup_client(client);
381 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
382 return send_syserror(ctx, ENOMEM);
385 if (req[1] && *req[1]) {
386 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
387 memset(req[1], 0, strlen(req[1]));
388 goto update_cache;
391 goto done;
394 if ((fd = open_file(filename, &st)) == -1) {
395 error = errno;
396 log_write("%s: %s", filename, strerror(errno));
397 g_strfreev(req);
398 cleanup_client(client);
399 return send_syserror(ctx, error);
402 if (st.st_size == 0)
403 goto new_doc;
405 if (cache_get_key(client->md5file, shakey) == TRUE)
406 cached = 1;
407 else {
409 * No key specified and no matching filename found in the cache.
411 if (!req[1] || !*req[1]) {
412 close(fd);
413 g_strfreev(req);
414 cleanup_client(client);
415 return send_error(ctx, EPWMD_KEY);
419 if (!cached) {
420 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
421 memset(req[1], 0, strlen(req[1]));
424 error = try_xml_decrypt(ctx, fd, st, shakey);
426 if (error) {
427 close(fd);
428 memset(shakey, 0, sizeof(shakey));
429 g_strfreev(req);
430 cleanup_client(client);
431 return send_error(ctx, error);
434 close(fd);
435 client->filename = g_strdup(filename);
437 if (!client->filename) {
438 memset(shakey, 0, sizeof(shakey));
439 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
440 g_strfreev(req);
441 cleanup_client(client);
442 return send_syserror(ctx, ENOMEM);
445 update_cache:
446 if (!cached) {
447 if (cache_update_key(client->md5file, shakey) == FALSE) {
448 memset(shakey, 0, sizeof(shakey));
449 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
450 g_strfreev(req);
451 cleanup_client(client);
452 return send_error(ctx, EPWMD_MAX_SLOTS);
455 timeout = get_key_file_integer(client->filename, "cache_timeout");
456 cache_reset_timeout(client->md5file, timeout);
458 else
459 cache_set_timeout(client->md5file, -2);
461 memset(shakey, 0, sizeof(shakey));
463 done:
464 g_strfreev(req);
465 error = parse_xml(ctx);
467 if (client->xml) {
468 gcry_free(client->xml);
469 client->xml = NULL;
472 if (error == 0) {
473 client->state = STATE_OPEN;
474 send_cache_status(ctx);
477 return send_error(ctx, error);
480 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
481 gint size, gpointer *out, glong *outsize, gint *error)
483 z_stream z;
484 gpointer pout, pin;
485 gint ret;
486 gz_header h;
487 gchar buf[17];
488 gint cmd = Z_NO_FLUSH;
490 z.zalloc = z_alloc;
491 z.zfree = z_free;
492 z.next_in = pin = data;
493 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
494 z.avail_out = zlib_bufsize;
495 z.next_out = pout = g_malloc(zlib_bufsize);
497 if (!pout) {
498 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
499 *error = Z_MEM_ERROR;
500 return FALSE;
503 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
505 if (ret != Z_OK) {
506 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
507 *error = ret;
508 g_free(pout);
509 return FALSE;
512 memset(&h, 0, sizeof(gz_header));
513 snprintf(buf, sizeof(buf), "%i", size);
514 h.comment = (guchar *)buf;
515 ret = deflateSetHeader(&z, &h);
517 if (ret != Z_OK) {
518 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
519 *error = ret;
520 g_free(pout);
521 deflateEnd(&z);
522 return FALSE;
525 do {
526 gpointer p;
528 ret = deflate(&z, cmd);
530 switch (ret) {
531 case Z_OK:
532 break;
533 case Z_BUF_ERROR:
534 if (!z.avail_out) {
535 p = g_realloc(pout, z.total_out + zlib_bufsize);
537 if (!p) {
538 ret = Z_MEM_ERROR;
539 goto fail;
542 pout = p;
543 z.next_out = pout + z.total_out;
544 z.avail_out = zlib_bufsize;
547 if (!z.avail_in && z.total_in < size) {
548 if (z.total_in + zlib_bufsize > size)
549 z.avail_in = size - z.total_in;
550 else
551 z.avail_in = zlib_bufsize;
553 if (ctx)
554 assuan_write_status(ctx, "COMPRESS",
555 print_fmt("%i %i", z.total_in, size));
558 if (z.total_in >= size)
559 cmd = Z_FINISH;
561 break;
562 case Z_STREAM_END:
563 break;
564 default:
565 goto fail;
567 } while (ret != Z_STREAM_END);
569 if (ctx)
570 assuan_write_status(ctx, "COMPRESS",
571 print_fmt("%i %i", z.total_in, size));
573 *out = pout;
574 *outsize = z.total_out;
575 deflateEnd(&z);
576 return TRUE;
578 fail:
579 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
580 *error = ret;
581 g_free(pout);
582 deflateEnd(&z);
583 return FALSE;
586 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
587 const gchar *filename, gpointer data, size_t insize,
588 const guchar *shakey, guint iter)
590 gsize len = insize;
591 gint fd;
592 gpointer inbuf;
593 guchar tkey[gcrykeysize];
594 gchar *p;
595 gint error;
596 guint iter_progress = 0, n_iter = 0, xiter = 0;
597 gchar tmp[FILENAME_MAX];
598 struct file_header_s {
599 guint iter;
600 guchar iv[gcryblocksize];
601 } file_header;
603 if (insize / gcryblocksize) {
604 len = (insize / gcryblocksize) * gcryblocksize;
606 if (insize % gcryblocksize)
607 len += gcryblocksize;
611 * Resize the existing xml buffer to the block size required by gcrypt
612 * rather than duplicating it and wasting memory.
614 inbuf = gcry_realloc(data, len);
616 if (!inbuf)
617 return gpg_error_from_errno(ENOMEM);
619 insize = len;
620 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
621 memcpy(tkey, shakey, sizeof(tkey));
622 tkey[0] ^= 1;
624 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
625 gcry_free(inbuf);
626 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
627 return gcryerrno;
630 file_header.iter = iter;
632 if (client)
633 iter_progress = get_key_file_integer("default", "iteration_progress");
635 if (client && iter_progress && file_header.iter >= iter_progress)
636 assuan_write_status(client->ctx, "ENCRYPT", "0");
638 while (xiter < file_header.iter) {
639 if (client && iter_progress > 0 && xiter >= iter_progress) {
640 if (!(xiter % iter_progress))
641 assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
642 ++n_iter * iter_progress));
645 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
646 sizeof(file_header.iv)))) {
647 gcry_free(inbuf);
648 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
649 return gcryerrno;
652 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
653 == FALSE) {
654 gcry_free(inbuf);
655 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
656 return gcryerrno;
659 xiter++;
662 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
663 sizeof(file_header.iv)))) {
664 gcry_free(inbuf);
665 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
666 return gcryerrno;
669 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
670 gcry_free(inbuf);
671 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
672 return gcryerrno;
675 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
676 gcry_free(inbuf);
677 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
678 return gcryerrno;
681 if (client && iter_progress && file_header.iter >= iter_progress)
682 assuan_write_status(client->ctx, "ENCRYPT",
683 print_fmt("%i", file_header.iter));
685 if (filename) {
686 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
688 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
689 error = errno;
690 gcry_free(inbuf);
691 p = strrchr(tmp, '/');
692 p++;
693 log_write("%s: %s", p, strerror(errno));
694 return gpg_error_from_errno(error);
697 else
699 * xml_import() from command line.
701 fd = STDOUT_FILENO;
703 len = write(fd, &file_header, sizeof(struct file_header_s));
705 if (len != sizeof(file_header)) {
706 len = errno;
708 if (filename)
709 close(fd);
711 gcry_free(inbuf);
712 return gpg_error_from_errno(len);
715 len = write(fd, inbuf, insize);
717 if (len != insize) {
718 len = errno;
720 if (filename)
721 close(fd);
723 gcry_free(inbuf);
724 return gpg_error_from_errno(len);
727 if (fsync(fd) == -1) {
728 len = errno;
729 close(fd);
730 gcry_free(inbuf);
731 return gpg_error_from_errno(len);
734 if (filename) {
735 struct stat st;
736 mode_t mode = 0;
738 close(fd);
740 if (stat(filename, &st) == 0)
741 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
743 if (rename(tmp, filename) == -1) {
744 len = errno;
745 gcry_free(inbuf);
746 return gpg_error_from_errno(len);
749 if (mode)
750 chmod(filename, mode);
753 gcry_free(inbuf);
754 return 0;
757 static int save_command(assuan_context_t ctx, char *line)
759 gpointer xmlbuf;
760 xmlChar *p;
761 gint len;
762 gint cached = 0;
763 guchar shakey[gcrykeysize];
764 gint iter;
765 struct stat st;
766 struct client_s *client = assuan_get_pointer(ctx);
767 gpg_error_t error;
768 gint timeout;
769 gpointer outbuf;
770 glong outsize = 0;
771 gint zerror;
773 #ifdef WORKING_PTHREADS
774 MUTEX_LOCK(ctx);
775 #endif
776 error = file_modified(client);
778 if (error) {
779 log_write("%s: %s", client->filename ? client->filename : "",
780 pwmd_strerror(error));
781 return send_error(ctx, error);
784 if (stat(client->filename, &st) == -1 && errno != ENOENT)
785 return send_syserror(ctx, errno);
787 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
788 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
789 return send_error(ctx, EPWMD_INVALID_FILENAME);
792 if (!line || !*line) {
793 if (cache_get_key(client->md5file, shakey) == FALSE)
794 return send_error(ctx, EPWMD_KEY);
796 cached = 1;
798 else {
799 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
800 memset(line, 0, strlen(line));
803 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
804 xmlbuf = p;
806 iter = get_key_file_integer(client->filename, "compression_level");
808 if (iter < 0)
809 iter = 0;
811 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
812 memset(shakey, 0, sizeof(shakey));
813 xmlFree(xmlbuf);
815 if (zerror == Z_MEM_ERROR) {
816 return send_syserror(ctx, ENOMEM);
818 else
819 return send_error(ctx, GPG_ERR_COMPR_ALGO);
821 else {
822 gcry_free(xmlbuf);
823 xmlbuf = outbuf;
824 len = outsize;
827 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
828 iter = 0;
830 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
832 if (error) {
833 memset(shakey, 0, sizeof(shakey));
834 return send_error(ctx, error);
837 stat(client->filename, &st);
838 client->mtime = st.st_mtime;
839 timeout = get_key_file_integer(client->filename, "cache_timeout");
841 if (cached) {
842 memset(shakey, 0, sizeof(shakey));
843 cache_reset_timeout(client->md5file, timeout);
844 client->new = FALSE;
845 return 0;
848 if (cache_update_key(client->md5file, shakey) == FALSE) {
849 memset(shakey, 0, sizeof(shakey));
850 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
851 return send_error(ctx, EPWMD_MAX_SLOTS);
854 client->new = FALSE;
855 memset(shakey, 0, sizeof(shakey));
856 cache_reset_timeout(client->md5file, timeout);
857 return 0;
860 static gboolean contains_whitespace(const gchar *str)
862 const gchar *p = str;
863 gunichar c;
864 glong len;
866 len = g_utf8_strlen(p++, -1) -1;
868 while (len--) {
869 c = g_utf8_get_char(p++);
871 if (g_unichar_isspace(c))
872 return TRUE;
875 return FALSE;
878 static int delete_command(assuan_context_t ctx, char *line)
880 struct client_s *client = assuan_get_pointer(ctx);
881 gchar **req;
882 gpg_error_t error;
883 xmlNodePtr n;
885 error = file_modified(client);
887 if (error) {
888 log_write("%s: %s", client->filename ? client->filename : "",
889 pwmd_strerror(error));
890 return send_error(ctx, error);
893 if (strchr(line, '\t'))
894 req = split_input_line(line, "\t", -1);
895 else
896 req = split_input_line(line, " ", -1);
898 if (!req || !*req)
899 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
901 n = find_account(client->doc, &req, &error, NULL);
903 if (!n) {
904 g_strfreev(req);
905 return send_error(ctx, error);
909 * No sub-node defined. Remove the entire node (account).
911 if (!req[1]) {
912 if (n) {
913 xmlUnlinkNode(n);
914 xmlFreeNode(n);
917 g_strfreev(req);
918 return 0;
921 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
922 g_strfreev(req);
924 if (!n)
925 return send_error(ctx, error);
927 if (n) {
928 xmlUnlinkNode(n);
929 xmlFreeNode(n);
932 return 0;
935 static int store_command(assuan_context_t ctx, char *line)
937 struct client_s *client = assuan_get_pointer(ctx);
938 gchar **req;
939 gpg_error_t error;
940 guchar *result;
941 gsize len;
942 xmlNodePtr n;
944 error = file_modified(client);
946 if (error) {
947 log_write("%s: %s", client->filename ? client->filename : "",
948 pwmd_strerror(error));
949 return send_error(ctx, error);
952 error = assuan_inquire(ctx, "STORE", &result, &len, 0);
954 if (error)
955 return send_error(ctx, error);
957 req = split_input_line((gchar *)result, "\t", 0);
958 #ifndef MEM_DEBUG
959 xfree(result);
960 #else
961 free(result);
962 #endif
964 if (!req || !*req)
965 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
967 again:
968 n = find_account(client->doc, &req, &error, NULL);
970 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
971 if (contains_whitespace(*req) == TRUE) {
972 g_strfreev(req);
973 return send_error(ctx, EPWMD_INVALID_ELEMENT);
976 error = new_account(client->doc, *req);
978 if (error) {
979 g_strfreev(req);
980 return send_error(ctx, error);
983 goto again;
986 if (!n) {
987 g_strfreev(req);
988 return send_error(ctx, error);
991 if (req[1]) {
992 if (!n->children)
993 create_elements_cb(n, req+1, &error, NULL);
994 else
995 find_elements(client->doc, n->children, req+1, &error,
996 NULL, NULL, create_elements_cb, NULL);
999 g_strfreev(req);
1000 return send_error(ctx, error);
1003 static int get_command(assuan_context_t ctx, char *line)
1005 struct client_s *client = assuan_get_pointer(ctx);
1006 gchar **req;
1007 gpg_error_t error;
1008 xmlNodePtr n;
1010 error = file_modified(client);
1012 if (error) {
1013 log_write("%s: %s", client->filename ? client->filename : "",
1014 pwmd_strerror(error));
1015 return send_error(ctx, error);
1018 req = split_input_line(line, "\t", -1);
1020 if (!req || !*req) {
1021 g_strfreev(req);
1022 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1025 n = find_account(client->doc, &req, &error, NULL);
1027 if (!n) {
1028 g_strfreev(req);
1029 return send_error(ctx, error);
1032 if (req[1])
1033 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
1035 g_strfreev(req);
1037 if (error)
1038 return send_error(ctx, error);
1040 if (!n || !n->children)
1041 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1043 n = n->children;
1045 if (!n || !n->content || !*n->content)
1046 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1048 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1049 return send_error(ctx, error);
1052 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1054 xmlChar *p = path;
1055 gint n;
1056 gchar *buf;
1058 if (!p)
1059 return NULL;
1061 for (n = 0; *p && n < 3; p++) {
1062 if (*p == '/')
1063 n++;
1066 if (strstr((gchar *)p, "text()") != NULL)
1067 p[xmlStrlen(p) - 7] = 0;
1069 for (n = 0; p[n]; n++) {
1070 if (p[n] == '/')
1071 p[n] = '\t';
1074 buf = g_strdup_printf("%s\t%s", account, p);
1075 return buf;
1078 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1080 gchar **a;
1081 va_list ap;
1082 gchar *buf;
1083 gint len = *array ? g_strv_length(*array) : 0;
1084 gint ret;
1086 if (!fmt)
1087 return FALSE;
1089 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1090 return FALSE;
1092 va_start(ap, fmt);
1093 ret = g_vasprintf(&buf, fmt, ap);
1094 va_end(ap);
1096 if (ret == -1)
1097 return FALSE;
1099 a[len++] = buf;
1100 a[len] = NULL;
1101 *array = a;
1102 return TRUE;
1105 struct realpath_s {
1106 gchar *account;
1109 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1110 gpg_error_t *error, void *data)
1112 struct realpath_s *rp = data;
1114 if (rp->account)
1115 g_free(rp->account);
1117 rp->account = g_strdup(req[0]);
1119 if (!rp->account) {
1120 *error = gpg_error_from_errno(ENOMEM);
1121 return NULL;
1124 return node;
1127 static int realpath_command(assuan_context_t ctx, char *line)
1129 gpg_error_t error;
1130 struct client_s *client = assuan_get_pointer(ctx);
1131 xmlChar *p;
1132 gchar **req;
1133 gchar *result, *t;
1134 gint i;
1135 xmlNodePtr n;
1136 struct realpath_s *rp;
1137 GString *string;
1139 error = file_modified(client);
1141 if (error) {
1142 log_write("%s: %s", client->filename ? client->filename : "",
1143 pwmd_strerror(error));
1144 return send_error(ctx, error);
1147 if (strchr(line, '\t') != NULL) {
1148 if ((req = split_input_line(line, "\t", 0)) == NULL)
1149 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1151 else {
1152 if ((req = split_input_line(line, " ", 0)) == NULL)
1153 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1156 n = find_account(client->doc, &req, &error, NULL);
1158 if (!n) {
1159 g_strfreev(req);
1160 return send_error(ctx, error);
1163 rp = g_malloc(sizeof(struct realpath_s));
1165 if (!rp) {
1166 g_strfreev(req);
1167 return send_syserror(ctx, ENOMEM);
1170 rp->account = g_strdup(req[0]);
1172 if (!rp->account) {
1173 g_strfreev(req);
1174 return send_syserror(ctx, ENOMEM);
1177 if (req[1]) {
1178 n = find_elements(client->doc, n->children, req+1, &error,
1179 NULL, realpath_elements_cb, NULL, rp);
1181 if (!n) {
1182 g_free(rp->account);
1183 g_free(rp);
1184 g_strfreev(req);
1185 return send_error(ctx, error);
1189 p = xmlGetNodePath(n);
1190 result = element_path_to_req(rp->account, p);
1192 if (!result) {
1193 g_free(result);
1194 g_free(rp->account);
1195 g_free(rp);
1196 g_strfreev(req);
1197 xmlFree(p);
1198 return send_syserror(ctx, ENOMEM);
1201 string = g_string_new(result);
1202 g_free(result);
1203 g_free(rp->account);
1204 g_free(rp);
1205 g_strfreev(req);
1206 xmlFree(p);
1207 i = 0;
1209 again:
1210 for (t = string->str + i; *t; t++, i++) {
1211 if (!i || *t == '\t') {
1212 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1213 goto again;
1217 error = assuan_send_data(ctx, string->str, string->len);
1218 g_string_free(string, TRUE);
1219 return send_error(ctx, error);
1222 struct list_element_s {
1223 GSList *list;
1224 gchar **elements;
1227 static gboolean append_to_element_list(struct list_element_s *elements)
1229 gchar *tmp;
1230 gint i, total;
1231 gchar *a;
1232 GSList *list;
1234 if (!elements || !elements->elements)
1235 return TRUE;
1237 tmp = g_strjoinv("\t", elements->elements);
1239 if (!tmp)
1240 return FALSE;
1242 g_strfreev(elements->elements);
1243 elements->elements = NULL;
1244 total = g_slist_length(elements->list);
1245 a = g_utf8_collate_key(tmp, -1);
1247 if (!a) {
1248 g_free(tmp);
1249 return FALSE;
1253 * Removes duplicate element paths from the list. This is needed when
1254 * appending an element tree from list_command(). The glib docs recommend
1255 * using g_utf8_collate_key() for a large number of strings.
1257 for (i = 0; i < total; i++) {
1258 gchar *p = g_slist_nth_data(elements->list, i);
1259 gchar *b = g_utf8_collate_key(p, -1);
1261 if (!b) {
1262 g_free(a);
1263 g_free(tmp);
1264 return FALSE;
1267 if (strcmp(a, b) == 0) {
1268 g_free(a);
1269 g_free(b);
1270 g_free(tmp);
1271 return TRUE;
1274 g_free(b);
1277 g_free(a);
1278 list = g_slist_append(elements->list, tmp);
1280 if (!list)
1281 return FALSE;
1283 elements->list = list;
1284 return TRUE;
1287 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1288 struct list_element_s *elements, gchar *prefix)
1290 xmlNodePtr n;
1291 gpg_error_t error;
1293 if (append_to_element_list(elements) == FALSE)
1294 return gpg_error_from_errno(ENOMEM);
1296 for (n = node; n; n = n->next) {
1297 if (n->type == XML_ELEMENT_NODE) {
1298 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1299 gchar *tmp;
1301 if (content) {
1302 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1303 return gpg_error_from_errno(ENOMEM);
1305 if (append_to_element_list(elements) == FALSE)
1306 return gpg_error_from_errno(ENOMEM);
1309 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1311 if (!tmp)
1312 return gpg_error_from_errno(ENOMEM);
1314 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1315 g_free(tmp);
1316 return gpg_error_from_errno(ENOMEM);
1319 if (n->children) {
1320 error = do_list_recurse(doc, n->children, elements, tmp);
1321 g_free(tmp);
1323 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1324 return error;
1326 else
1327 g_free(tmp);
1329 if (append_to_element_list(elements) == FALSE)
1330 return gpg_error_from_errno(ENOMEM);
1334 return 0;
1337 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1338 struct list_element_s *elements, char *line)
1340 gchar *prefix = NULL, *account;
1341 gchar **req = NULL, **oreq = NULL, *tmp;
1342 xmlNodePtr n;
1343 gboolean account_is_literal, account_has_target = FALSE;
1344 gint which = 0;
1345 gchar **p;
1346 gpg_error_t error;
1348 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1349 if ((req = split_input_line(line, " ", 0)) == NULL)
1350 return EPWMD_COMMAND_SYNTAX;
1353 prefix = g_strdup(*req);
1355 if (!prefix) {
1356 g_strfreev(req);
1357 return gpg_error_from_errno(ENOMEM);
1360 account = g_strdup(*req);
1362 if (!account) {
1363 g_free(prefix);
1364 g_strfreev(req);
1365 return gpg_error_from_errno(ENOMEM);
1368 oreq = g_strdupv(req);
1370 if (!oreq) {
1371 g_free(prefix);
1372 g_free(account);
1373 g_strfreev(req);
1374 return gpg_error_from_errno(ENOMEM);
1377 p = req;
1378 again:
1379 account_has_target = FALSE;
1380 account_is_literal = is_literal_element_str(prefix);
1381 n = find_account(doc, &p, &error, &account_has_target);
1383 if (which)
1384 oreq = p;
1385 else
1386 req = p;
1388 if (!n)
1389 goto fail;
1391 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1392 tmp = g_strdup_printf("!%s", prefix);
1394 if (!tmp) {
1395 error = gpg_error_from_errno(ENOMEM);
1396 goto fail;
1399 g_free(prefix);
1400 prefix = tmp;
1403 if (*(p+1)) {
1404 gchar *t;
1406 n = find_elements(doc, n->children, p+1, &error,
1407 NULL, NULL, NULL, NULL);
1409 if (error)
1410 goto fail;
1412 tmp = g_strjoinv("\t", p+1);
1413 if (!tmp) {
1414 error = gpg_error_from_errno(ENOMEM);
1415 goto fail;
1418 t = g_strdup_printf("%s\t%s", prefix, tmp);
1419 if (!t) {
1420 error = gpg_error_from_errno(ENOMEM);
1421 goto fail;
1424 g_free(prefix);
1425 prefix = t;
1426 g_free(tmp);
1429 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1430 error = gpg_error_from_errno(ENOMEM);
1431 goto fail;
1434 if (node_has_child_element(n->children) == FALSE) {
1435 if (append_to_element_list(elements) == FALSE) {
1436 error = gpg_error_from_errno(ENOMEM);
1437 goto fail;
1440 else
1441 error = do_list_recurse(doc, n->children, elements, prefix);
1443 if (error)
1444 goto fail;
1446 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1447 g_free(*oreq);
1448 *oreq = g_strdup_printf("!%s", account);
1450 if (!*oreq) {
1451 error = gpg_error_from_errno(ENOMEM);
1452 goto fail;
1455 p = oreq;
1456 g_free(prefix);
1457 prefix = g_strdup(*oreq);
1459 if (!prefix) {
1460 error = gpg_error_from_errno(ENOMEM);
1461 goto fail;
1464 goto again;
1467 fail:
1468 g_free(prefix);
1469 g_free(account);
1470 g_strfreev(req);
1472 if (oreq)
1473 g_strfreev(oreq);
1475 return error;
1479 * This could be faster especially when finding "target" attributes.
1481 static int list_command(assuan_context_t ctx, char *line)
1483 struct client_s *client = assuan_get_pointer(ctx);
1484 gpg_error_t error;
1485 struct list_element_s *elements = NULL;
1486 GString *string;
1487 gchar *tmp;
1488 gint i, total;
1490 if (disable_list_and_dump == TRUE)
1491 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1493 error = file_modified(client);
1495 if (error) {
1496 log_write("%s: %s", client->filename, pwmd_strerror(error));
1497 return send_error(ctx, error);
1500 if (!*line) {
1501 GString *str;
1503 error = list_accounts(client->doc, &str);
1505 if (error)
1506 return send_error(ctx, error);
1508 error = assuan_send_data(ctx, str->str, str->len);
1509 g_string_free(str, TRUE);
1510 return send_error(ctx, error);
1513 elements = g_malloc0(sizeof(struct list_element_s));
1515 if (!elements) {
1516 error = gpg_error_from_errno(ENOMEM);
1517 goto fail;
1520 is_list_command = TRUE;
1521 error = do_list_command(ctx, client->doc, elements, line);
1523 if (error)
1524 goto fail;
1526 if (!error) {
1527 total = g_slist_length(elements->list);
1529 if (!total) {
1530 error = EPWMD_EMPTY_ELEMENT;
1531 goto fail;
1535 * Find element paths with a target and append those element trees to
1536 * the list.
1538 for (i = 0; i < total; i++) {
1539 gchar **req;
1541 tmp = g_slist_nth_data(elements->list, i);
1542 req = split_input_line(tmp, "\t", 0);
1544 if (!req) {
1545 if (g_str_has_prefix(tmp, "!") == TRUE) {
1546 g_strfreev(req);
1547 continue;
1550 else {
1551 gchar **p;
1553 for (p = req; *p; p++) {
1554 if (g_str_has_prefix(*p, "!") == FALSE)
1555 break;
1558 if (!*p) {
1559 g_strfreev(req);
1560 continue;
1564 g_strfreev(req);
1565 error = do_list_command(ctx, client->doc, elements, tmp);
1567 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1568 goto fail;
1570 total = g_slist_length(elements->list);
1574 string = g_string_new(NULL);
1576 for (i = 0; i < total; i++) {
1577 tmp = g_slist_nth_data(elements->list, i);
1578 g_string_append_printf(string, "%s\n", tmp);
1579 g_free(tmp);
1582 string = g_string_truncate(string, string->len - 1);
1583 error = assuan_send_data(ctx, string->str, string->len);
1584 g_string_free(string, TRUE);
1586 fail:
1587 is_list_command = FALSE;
1589 if (elements) {
1590 if (elements->list)
1591 g_slist_free(elements->list);
1593 if (elements->elements)
1594 g_strfreev(elements->elements);
1596 g_free(elements);
1599 return send_error(ctx, error);
1602 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1603 const gchar *value)
1605 xmlAttrPtr a;
1607 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1608 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1610 if (!a)
1611 return EPWMD_LIBXML_ERROR;
1613 else
1614 xmlNodeSetContent(a->children, (xmlChar *)value);
1616 return 0;
1620 * req[0] - element path
1622 static int attribute_list(assuan_context_t ctx, gchar **req)
1624 struct client_s *client = assuan_get_pointer(ctx);
1625 gchar **attrlist = NULL;
1626 gint i = 0;
1627 gchar **path = NULL;
1628 xmlAttrPtr a;
1629 xmlNodePtr n, an;
1630 gchar *line;
1631 gpg_error_t error;
1633 if (!req || !req[0])
1634 return EPWMD_COMMAND_SYNTAX;
1636 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1638 * The first argument may be only an account.
1640 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1641 return EPWMD_COMMAND_SYNTAX;
1644 n = find_account(client->doc, &path, &error, NULL);
1646 if (!n) {
1647 g_strfreev(path);
1648 return error;
1651 if (path[1]) {
1652 n = find_elements(client->doc, n->children, path+1, &error,
1653 NULL, NULL, NULL, NULL);
1655 if (!n) {
1656 g_strfreev(path);
1657 return error;
1661 g_strfreev(path);
1663 for (a = n->properties; a; a = a->next) {
1664 gchar **pa;
1666 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1667 if (attrlist)
1668 g_strfreev(attrlist);
1670 error = errno;
1671 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1672 return gpg_error_from_errno(error);
1675 attrlist = pa;
1676 an = a->children;
1677 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1679 if (!attrlist[i]) {
1680 g_strfreev(attrlist);
1681 return gpg_error_from_errno(ENOMEM);
1684 attrlist[++i] = NULL;
1687 if (!attrlist)
1688 return EPWMD_EMPTY_ELEMENT;
1690 line = g_strjoinv("\n", attrlist);
1692 if (!line) {
1693 g_strfreev(attrlist);
1694 return gpg_error_from_errno(ENOMEM);
1697 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1698 g_free(line);
1699 g_strfreev(attrlist);
1700 return error;
1704 * req[0] - attribute
1705 * req[1] - element path
1707 static int attribute_delete(struct client_s *client, gchar **req)
1709 xmlAttrPtr a;
1710 xmlNodePtr n;
1711 gchar **path = NULL;
1712 gpg_error_t error;
1714 if (!req || !req[0] || !req[1])
1715 return EPWMD_COMMAND_SYNTAX;
1717 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1719 * The first argument may be only an account.
1721 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1722 return EPWMD_COMMAND_SYNTAX;
1726 * Don't remove the "name" attribute for the account element. To remove an
1727 * account use DELETE <account>.
1729 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1730 error = EPWMD_ATTR_SYNTAX;
1731 goto fail;
1734 n = find_account(client->doc, &path, &error, NULL);
1736 if (!n)
1737 goto fail;
1739 if (path[1]) {
1740 n = find_elements(client->doc, n->children, path+1, &error,
1741 NULL, NULL, NULL, NULL);
1743 if (!n)
1744 goto fail;
1747 g_strfreev(path);
1749 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1750 return EPWMD_ATTR_NOT_FOUND;
1752 if (xmlRemoveProp(a) == -1)
1753 return EPWMD_LIBXML_ERROR;
1755 return 0;
1757 fail:
1758 g_strfreev(path);
1759 return error;
1763 * Creates a "target" attribute. When other commands encounter an element with
1764 * this attribute, the element path is modified to the target value. If the
1765 * source element path doesn't exist when using 'ATTR SET target', it is
1766 * created, but the destination element path must exist.
1768 * req[0] - source element path
1769 * req[1] - destination element path
1771 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1773 gchar **src, **dst, *line;
1774 gpg_error_t error;
1775 xmlNodePtr n;
1777 if (!req || !req[0] || !req[1])
1778 return EPWMD_COMMAND_SYNTAX;
1780 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1782 * The first argument may be only an account.
1784 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1785 return EPWMD_COMMAND_SYNTAX;
1788 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1790 * The first argument may be only an account.
1792 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1793 error = EPWMD_COMMAND_SYNTAX;
1794 goto fail;
1798 n = find_account(client->doc, &dst, &error, NULL);
1801 * Make sure the destination element path exists.
1803 if (!n)
1804 goto fail;
1806 if (dst[1]) {
1807 n = find_elements(client->doc, n->children, dst+1, &error,
1808 NULL, NULL, NULL, NULL);
1810 if (!n)
1811 goto fail;
1814 again:
1815 n = find_account(client->doc, &src, &error, NULL);
1817 if (!n) {
1818 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1819 error = new_account(client->doc, src[0]);
1821 if (error)
1822 goto fail;
1824 goto again;
1826 else
1827 goto fail;
1830 if (src[1]) {
1831 if (!n->children)
1832 n = create_target_elements_cb(n, src+1, &error, NULL);
1833 else
1834 n = find_elements(client->doc, n->children, src+1, &error,
1835 NULL, NULL, create_target_elements_cb, NULL);
1837 if (!n)
1838 goto fail;
1841 * Reset the position of the element tree now that the elements
1842 * have been created.
1844 n = find_account(client->doc, &src, &error, NULL);
1846 if (!n)
1847 goto fail;
1849 n = find_elements(client->doc, n->children, src+1, &error,
1850 NULL, NULL, NULL, NULL);
1852 if (!n)
1853 goto fail;
1856 line = g_strjoinv("\t", dst);
1857 error = add_attribute(n, "target", line);
1859 if (error) {
1860 g_free(line);
1861 goto fail;
1864 g_free(line);
1865 g_strfreev(src);
1866 g_strfreev(dst);
1867 return 0;
1869 fail:
1870 g_strfreev(src);
1871 g_strfreev(dst);
1872 return error;
1876 * req[0] - account name
1877 * req[1] - new name
1879 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1881 gpg_error_t error;
1882 gchar **tmp;
1883 xmlNodePtr n;
1885 tmp = g_strdupv(req);
1887 if (!tmp)
1888 return gpg_error_from_errno(ENOMEM);
1890 n = find_account(client->doc, &tmp, &error, NULL);
1891 g_strfreev(tmp);
1893 if (!n)
1894 return error;
1896 if (g_utf8_collate(req[0], req[1]) == 0)
1897 return 0;
1900 * Will not overwrite an existing account.
1902 tmp = g_strdupv(req+1);
1904 if (!tmp)
1905 return gpg_error_from_errno(ENOMEM);
1907 n = find_account(client->doc, &tmp, &error, NULL);
1908 g_strfreev(tmp);
1910 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1911 return error;
1913 if (n)
1914 return EPWMD_ACCOUNT_EXISTS;
1917 * Whitespace not allowed in account names.
1919 if (contains_whitespace(req[1]) == TRUE)
1920 return EPWMD_ATTR_SYNTAX;
1922 tmp = g_strdupv(req);
1924 if (!tmp)
1925 return gpg_error_from_errno(ENOMEM);
1927 n = find_account(client->doc, &tmp, &error, NULL);
1928 g_strfreev(tmp);
1930 if (!n)
1931 return EPWMD_ELEMENT_NOT_FOUND;
1933 return add_attribute(n, "name", req[1]);
1937 * req[0] - attribute
1938 * req[1] - element path
1940 static int attribute_get(assuan_context_t ctx, gchar **req)
1942 struct client_s *client = assuan_get_pointer(ctx);
1943 xmlNodePtr n;
1944 xmlChar *a;
1945 gchar **path= NULL;
1946 gpg_error_t error;
1948 if (!req || !req[0] || !req[1])
1949 return EPWMD_COMMAND_SYNTAX;
1951 if (strchr(req[1], '\t')) {
1952 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1953 return EPWMD_COMMAND_SYNTAX;
1955 else {
1956 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1957 return EPWMD_COMMAND_SYNTAX;
1960 n = find_account(client->doc, &path, &error, NULL);
1962 if (!n)
1963 goto fail;
1965 if (path[1]) {
1966 n = find_elements(client->doc, n->children, path+1, &error,
1967 NULL, NULL, NULL, NULL);
1969 if (!n)
1970 goto fail;
1973 g_strfreev(path);
1975 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1976 return EPWMD_ATTR_NOT_FOUND;
1978 error = assuan_send_data(ctx, a, xmlStrlen(a));
1979 xmlFree(a);
1980 return error;
1982 fail:
1983 g_strfreev(path);
1984 return error;
1988 * req[0] - attribute
1989 * req[1] - element path
1990 * req[2] - value
1992 static int attribute_set(struct client_s *client, gchar **req)
1994 gchar **path = NULL;
1995 gpg_error_t error;
1996 xmlNodePtr n;
1998 if (!req || !req[0] || !req[1] || !req[2])
1999 return EPWMD_COMMAND_SYNTAX;
2002 * Reserved attribute names.
2004 if (g_utf8_collate(req[0], "name") == 0) {
2006 * Only reserved for the account element. Not the rest of the
2007 * document.
2009 if (strchr(req[1], '\t') == NULL)
2010 return name_attribute(client, req + 1);
2012 else if (g_utf8_collate(req[0], "target") == 0)
2013 return target_attribute(client, req + 1);
2015 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2017 * The first argument may be only an account.
2019 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2020 return EPWMD_COMMAND_SYNTAX;
2023 n = find_account(client->doc, &path, &error, NULL);
2025 if (!n)
2026 goto fail;
2028 if (path[1]) {
2029 n = find_elements(client->doc, n->children, path+1, &error,
2030 NULL, NULL, NULL, NULL);
2032 if (!n)
2033 goto fail;
2036 g_strfreev(path);
2037 return add_attribute(n, req[0], req[2]);
2039 fail:
2040 g_strfreev(path);
2041 return error;
2045 * req[0] - command
2046 * req[1] - attribute name or element path if command is LIST
2047 * req[2] - element path
2048 * req[2] - element path or value
2050 static int attr_command(assuan_context_t ctx, char *line)
2052 struct client_s *client = assuan_get_pointer(ctx);
2053 gchar **req = split_input_line(line, " ", 4);
2054 gpg_error_t error = 0;
2056 error = file_modified(client);
2058 if (error) {
2059 log_write("%s: %s", client->filename ? client->filename : "",
2060 pwmd_strerror(error));
2061 g_strfreev(req);
2062 return send_error(ctx, error);
2065 if (!req || !req[0] || !req[1]) {
2066 g_strfreev(req);
2067 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2070 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2071 error = attribute_set(client, req+1);
2072 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2073 error = attribute_get(ctx, req+1);
2074 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2075 error = attribute_delete(client, req+1);
2076 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2077 error = attribute_list(ctx, req+1);
2078 else
2079 error = EPWMD_COMMAND_SYNTAX;
2081 g_strfreev(req);
2082 return send_error(ctx, error);
2085 static int iscached_command(assuan_context_t ctx, char *line)
2087 gchar **req = split_input_line(line, " ", 0);
2088 guchar md5file[16];
2090 #ifdef WORKING_PTHREADS
2091 MUTEX_LOCK(ctx);
2092 #endif
2094 if (!req || !*req) {
2095 g_strfreev(req);
2096 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2099 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2100 g_strfreev(req);
2102 if (cache_iscached(md5file) == FALSE)
2103 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2105 return 0;
2108 void send_cache_status(assuan_context_t ctx)
2110 gchar *tmp;
2111 gint i = MUTEX_OFFSET;
2113 tmp = print_fmt("%i %i",
2114 cache_file_count(),
2115 (i / sizeof(file_cache_t)) - cache_file_count());
2117 if (ctx)
2118 assuan_write_status(ctx, "CACHE", tmp);
2121 static int clearcache_command(assuan_context_t ctx, char *line)
2123 struct client_s *client = assuan_get_pointer(ctx);
2124 gchar **req = split_input_line(line, " ", 0);
2125 guchar md5file[16];
2127 #ifdef WORKING_PTHREADS
2128 MUTEX_LOCK(ctx);
2129 #endif
2131 if (!req || !*req) {
2132 g_strfreev(req);
2133 cache_clear(client->md5file, 2);
2134 send_cache_status(ctx);
2135 return 0;
2138 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2139 g_strfreev(req);
2140 cache_clear(md5file, 1);
2141 send_cache_status(ctx);
2142 return 0;
2145 static int cachetimeout_command(assuan_context_t ctx, char *line)
2147 guchar md5file[16];
2148 glong timeout;
2149 gchar **req = split_input_line(line, " ", 0);
2150 gchar *p;
2152 #ifdef WORKING_PTHREADS
2153 MUTEX_LOCK(ctx);
2154 #endif
2156 if (!req || !*req || !req[1]) {
2157 g_strfreev(req);
2158 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2161 errno = 0;
2162 timeout = strtol(req[0], &p, 10);
2164 if (errno != 0 || *p != 0) {
2165 g_strfreev(req);
2166 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2169 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2170 g_strfreev(req);
2172 if (cache_set_timeout(md5file, timeout) == FALSE)
2173 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2175 send_cache_status(ctx);
2176 return 0;
2179 static int dump_command(assuan_context_t ctx, char *line)
2181 xmlChar *xml;
2182 gssize len;
2183 struct client_s *client = assuan_get_pointer(ctx);
2184 gpg_error_t error;
2186 if (disable_list_and_dump == TRUE)
2187 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2189 error = file_modified(client);
2191 if (error) {
2192 log_write("%s: %s", client->filename ? client->filename : "",
2193 pwmd_strerror(error));
2194 return send_error(ctx, error);
2197 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2198 error = assuan_send_data(ctx, xml, len);
2199 xmlFree(xml);
2200 return error;
2203 static int getconfig_command(assuan_context_t ctx, gchar *line)
2205 struct client_s *client = assuan_get_pointer(ctx);
2206 gpg_error_t error = 0;
2207 gchar *p, *tmp;
2209 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2210 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2212 p = get_key_file_string(client->filename ? client->filename : "default", line);
2214 if (!p)
2215 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2217 tmp = expand_homedir(p);
2218 g_free(p);
2219 p = tmp;
2220 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2221 g_free(p);
2222 return send_error(ctx, error);
2225 void cleanup_assuan(assuan_context_t ctx)
2227 struct client_s *cl = assuan_get_pointer(ctx);
2229 cleanup_client(cl);
2230 #ifdef WORKING_PTHREADS
2231 MUTEX_UNLOCK;
2232 #endif
2235 gpg_error_t register_commands(assuan_context_t ctx)
2237 static struct {
2238 const char *name;
2239 int (*handler)(assuan_context_t, char *line);
2240 } table[] = {
2241 { "OPEN", open_command },
2242 { "SAVE", save_command },
2243 { "LIST", list_command },
2244 { "REALPATH", realpath_command },
2245 { "STORE", store_command },
2246 { "DELETE", delete_command },
2247 { "GET", get_command },
2248 { "ATTR", attr_command },
2249 { "ISCACHED", iscached_command },
2250 { "CLEARCACHE", clearcache_command },
2251 { "CACHETIMEOUT", cachetimeout_command },
2252 { "GETCONFIG", getconfig_command },
2253 { "DUMP", dump_command },
2254 { "INPUT", NULL },
2255 { "OUTPUT", NULL },
2256 { NULL, NULL }
2258 int i, rc;
2260 for (i=0; table[i].name; i++) {
2261 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2263 if (rc)
2264 return rc;
2267 return assuan_register_bye_notify(ctx, cleanup_assuan);
2270 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2271 guchar *key)
2273 guchar *iv;
2274 void *inbuf;
2275 gsize insize, len;
2276 guchar tkey[gcrykeysize];
2277 struct file_header_s {
2278 guint iter;
2279 guchar iv[gcryblocksize];
2280 } file_header;
2281 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2282 gcry_cipher_hd_t gh;
2283 guint iter = 0, n_iter = 0;
2284 gint iter_progress;
2285 void *outbuf = NULL;
2286 gint zerror = 0;
2287 glong outsize = 0;
2289 if (!ctx) {
2290 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2292 if (gcryerrno)
2293 return gcryerrno;
2295 else
2296 gh = client->gh;
2298 lseek(fd, 0, SEEK_SET);
2299 insize = st.st_size - sizeof(struct file_header_s);
2300 iv = gcry_malloc(gcryblocksize);
2302 if (!iv) {
2303 if (!ctx)
2304 gcry_cipher_close(gh);
2306 return gpg_error_from_errno(ENOMEM);
2309 len = read(fd, &file_header, sizeof(struct file_header_s));
2311 if (len != sizeof(file_header)) {
2312 len = errno;
2314 if (!ctx)
2315 gcry_cipher_close(gh);
2317 gcry_free(iv);
2318 errno = len;
2319 return gpg_error_from_errno(errno);
2322 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2323 inbuf = gcry_malloc(insize);
2325 if (!inbuf) {
2326 if (!ctx)
2327 gcry_cipher_close(gh);
2329 gcry_free(iv);
2330 return gpg_error_from_errno(ENOMEM);
2333 len = read(fd, inbuf, insize);
2335 if (len != insize) {
2336 len = errno;
2338 if (!ctx)
2339 gcry_cipher_close(gh);
2341 gcry_free(iv);
2342 errno = len;
2343 return gpg_error_from_errno(errno);
2346 memcpy(tkey, key, sizeof(tkey));
2347 tkey[0] ^= 1;
2349 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2350 if (!ctx) {
2351 gcry_cipher_close(gh);
2352 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2354 else
2355 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2357 gcry_free(inbuf);
2358 gcry_free(iv);
2359 return gcryerrno;
2362 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2363 if (!ctx) {
2364 gcry_cipher_close(gh);
2365 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2367 else
2368 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2370 gcry_free(inbuf);
2371 gcry_free(iv);
2373 if (!ctx)
2374 gcry_cipher_close(gh);
2376 return gcryerrno;
2379 iter_progress = get_key_file_integer("default", "iteration_progress");
2381 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress)
2382 assuan_write_status(client->ctx, "DECRYPT", "0");
2384 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2385 if (!ctx) {
2386 gcry_cipher_close(gh);
2387 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2389 else
2390 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2392 gcry_free(inbuf);
2393 gcry_free(iv);
2394 return gcryerrno;
2397 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2398 if (!ctx) {
2399 gcry_cipher_close(gh);
2400 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2402 else
2403 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2405 gcry_free(inbuf);
2406 gcry_free(iv);
2407 return gcryerrno;
2410 while (iter < file_header.iter) {
2411 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2412 if (!(iter % iter_progress))
2413 assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2414 ++n_iter * iter_progress));
2417 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2418 if (!ctx) {
2419 gcry_cipher_close(gh);
2420 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2422 else
2423 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2425 gcry_free(inbuf);
2426 gcry_free(iv);
2427 return gcryerrno;
2430 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2431 if (!ctx) {
2432 gcry_cipher_close(gh);
2433 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2435 else
2436 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2438 gcry_free(inbuf);
2439 gcry_free(iv);
2440 return gcryerrno;
2443 iter++;
2446 if (ctx && iter_progress && file_header.iter >= iter_progress)
2447 assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2449 gcry_free(iv);
2451 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2453 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2455 if (zerror == Z_MEM_ERROR) {
2456 gcry_free(inbuf);
2457 return gpg_error_from_errno(ENOMEM);
2459 else if (zerror != Z_DATA_ERROR) {
2460 gcry_free(inbuf);
2462 if (!ctx)
2463 gcry_cipher_close(gh);
2465 return EPWMD_BADKEY;
2468 else {
2469 gcry_free(inbuf);
2470 inbuf = outbuf;
2471 insize = outsize;
2474 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2475 gcry_free(inbuf);
2477 if (!ctx)
2478 gcry_cipher_close(gh);
2480 return EPWMD_BADKEY;
2483 if (ctx) {
2484 client->xml = inbuf;
2485 client->len = insize;
2487 else {
2488 gcry_cipher_close(gh);
2489 gcry_free(inbuf);
2492 return 0;