Ported to libPth. This removes the --disable-locking configure option
[pwmd.git] / src / commands.c
blob73f7816c1f0c7c0f9436e21c8d56ba5e315d88a4
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2007 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <glib/gprintf.h>
30 #include <gcrypt.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifdef HAVE_ZLIB_H
37 #include <zlib.h>
38 #endif
40 #ifndef MEM_DEBUG
41 #include "mem.h"
42 #endif
44 #include "xml.h"
45 #include "common.h"
46 #include "pwmd_error.h"
47 #include "cache.h"
48 #include "commands.h"
50 static void *z_alloc(void *data, unsigned items, unsigned size)
52 #ifndef MEM_DEBUG
53 return gcry_calloc(items, size);
54 #else
55 return calloc(items, size);
56 #endif
59 static void z_free(void *data, void *p)
61 #ifndef MEM_DEBUG
62 gcry_free(p);
63 #else
64 free(p);
65 #endif
68 static gpg_error_t file_modified(struct client_s *client)
70 struct stat st;
72 if (client->state != STATE_OPEN)
73 return EPWMD_NO_FILE;
75 if (stat(client->filename, &st) == 0 && client->mtime) {
76 if (client->mtime != st.st_mtime)
77 return EPWMD_FILE_MODIFIED;
80 return 0;
83 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
84 void *inbuf, gsize insize)
86 if ((gcryerrno = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
87 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
88 return FALSE;
91 return TRUE;
94 gboolean decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
95 void *inbuf, gsize insize)
97 if ((gcryerrno = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize))) {
98 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
99 return FALSE;
102 return TRUE;
105 static gpg_error_t parse_xml(assuan_context_t ctx)
107 struct client_s *client = assuan_get_pointer(ctx);
109 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
111 if (!client->doc)
112 return EPWMD_LIBXML_ERROR;
114 return 0;
117 gboolean valid_filename(const gchar *filename)
119 const gchar *p;
121 if (!filename || !*filename)
122 return FALSE;
124 for (p = filename; *p; p++) {
125 if (g_ascii_isalnum(*p) == FALSE && *p != '-' && *p != '_' && *p != '.')
126 return FALSE;
129 return TRUE;
132 gint open_file(const gchar *filename, struct stat *st)
134 gint fd;
136 if ((fd = open(filename, O_RDONLY)) == -1)
137 return -1;
139 if (stat(filename, st) == -1) {
140 close(fd);
141 return -1;
144 return fd;
147 static void cleanup_client(struct client_s *client)
149 assuan_context_t ctx = client->ctx;
152 * This may be a new file so don't use a cache slot. save_command() will
153 * set this to FALSE on success.
155 if (client->new == TRUE)
156 cache_clear(client->md5file, 1);
158 if (client->doc)
159 xmlFreeDoc(client->doc);
161 if (client->xml)
162 gcry_free(client->xml);
164 if (client->filename)
165 g_free(client->filename);
167 gcry_cipher_close(client->gh);
168 memset(client, 0, sizeof(struct client_s));
169 client->state = STATE_CONNECTED;
170 client->ctx = ctx;
171 client->freed = TRUE;
174 static gchar *print_fmt(const char *fmt, ...)
176 va_list ap;
177 static gchar buf[ASSUAN_LINELENGTH] = {0};
179 va_start(ap, fmt);
180 vsnprintf(buf, sizeof(buf), fmt, ap);
181 va_end(ap);
182 return buf;
185 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
186 gpointer *out, glong *outsize, gint *error)
188 z_stream z;
189 gint ret;
190 gpointer pout;
191 gz_header h;
192 gchar buf[17];
193 gpg_error_t rc;
195 z.zalloc = z_alloc;
196 z.zfree = z_free;
197 z.next_in = in;
198 z.avail_in = insize;
199 z.avail_out = zlib_bufsize;
200 z.next_out = pout = g_malloc(zlib_bufsize);
202 if (!pout) {
203 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
204 *error = Z_MEM_ERROR;
205 return FALSE;
208 ret = inflateInit2(&z, 47);
210 if (ret != Z_OK) {
211 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
212 g_free(pout);
213 return FALSE;
216 memset(&h, 0, sizeof(gz_header));
217 h.comment = (guchar *)buf;
218 h.comm_max = sizeof(buf);
219 ret = inflateGetHeader(&z, &h);
221 if (ret != Z_OK) {
222 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
223 g_free(pout);
224 inflateEnd(&z);
225 return FALSE;
228 ret = inflate(&z, Z_BLOCK);
230 if (ret != Z_OK) {
231 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
232 g_free(pout);
233 inflateEnd(&z);
234 return FALSE;
237 if (h.comment)
238 insize = atoi((gchar *)h.comment);
240 do {
241 gpointer p;
243 ret = inflate(&z, Z_FINISH);
245 switch (ret) {
246 case Z_OK:
247 break;
248 case Z_BUF_ERROR:
249 if (!z.avail_out) {
250 p = g_realloc(pout, z.total_out + zlib_bufsize);
252 if (!p) {
253 ret = Z_MEM_ERROR;
254 goto fail;
257 pout = p;
258 z.next_out = pout + z.total_out;
259 z.avail_out = zlib_bufsize;
261 if (ctx) {
262 rc = assuan_write_status(ctx, "DECOMPRESS",
263 print_fmt("%i %i", z.total_out, insize));
265 if (rc) {
266 ret = rc;
267 goto fail;
271 break;
272 case Z_STREAM_END:
273 break;
274 default:
275 goto fail;
276 break;
279 pth_yield(NULL);
280 } while (ret != Z_STREAM_END);
282 if (ctx) {
283 rc = assuan_write_status(ctx, "DECOMPRESS",
284 print_fmt("%i %i", z.total_out, insize));
286 if (rc) {
287 ret = rc;
288 goto fail;
292 *out = pout;
293 *outsize = z.total_out;
294 inflateEnd(&z);
295 return TRUE;
297 fail:
298 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
299 *error = ret;
300 g_free(pout);
301 inflateEnd(&z);
302 return FALSE;
305 static int open_command(assuan_context_t ctx, char *line)
307 gint fd;
308 struct stat st;
309 guchar shakey[gcrykeysize];
310 gint cached = 0;
311 gint timeout;
312 gpg_error_t error;
313 struct client_s *client = assuan_get_pointer(ctx);
314 gchar **req;
315 gchar *filename = NULL;
317 MUTEX_LOCK(ctx, PTH_RWLOCK_RW, NULL);
319 if ((req = split_input_line(line, " ", 2)) != NULL)
320 filename = req[0];
322 if (!filename || !*filename) {
323 g_strfreev(req);
324 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
327 if (valid_filename(filename) == FALSE) {
328 g_strfreev(req);
329 return send_error(ctx, EPWMD_INVALID_FILENAME);
332 if (client->state == STATE_OPEN)
333 cleanup_client(client);
335 client->freed = FALSE;
337 if ((gcryerrno = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
338 g_strfreev(req);
339 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
340 cleanup_client(client);
341 return send_error(ctx, gcryerrno);
344 if (stat(filename, &st) == 0) {
345 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
346 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
347 g_strfreev(req);
348 cleanup_client(client);
349 return send_error(ctx, EPWMD_INVALID_FILENAME);
352 client->mtime = st.st_mtime;
355 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
358 * New files don't need a key.
360 if (access(filename, R_OK|W_OK) != 0) {
361 if (errno != ENOENT) {
362 error = errno;
363 log_write("%s: %s", filename, strerror(errno));
364 g_strfreev(req);
365 cleanup_client(client);
366 return send_syserror(ctx, error);
368 new_doc:
369 if ((client->xml = new_document()) == NULL) {
370 log_write("%s", strerror(ENOMEM));
371 g_strfreev(req);
372 cleanup_client(client);
373 return send_syserror(ctx, ENOMEM);
376 client->len = xmlStrlen(client->xml);
378 if (cache_has_file(client->md5file) == TRUE)
379 cache_clear(client->md5file, 1);
381 if (cache_add_file(client->md5file, NULL) == FALSE) {
382 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
383 g_strfreev(req);
384 cleanup_client(client);
385 return send_error(ctx, EPWMD_MAX_SLOTS);
388 client->new = TRUE;
389 client->filename = g_strdup(filename);
391 if (!client->filename) {
392 g_strfreev(req);
393 cleanup_client(client);
394 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
395 return send_syserror(ctx, ENOMEM);
398 if (req[1] && *req[1]) {
399 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
400 memset(req[1], 0, strlen(req[1]));
401 goto update_cache;
404 goto done;
407 if ((fd = open_file(filename, &st)) == -1) {
408 error = errno;
409 log_write("%s: %s", filename, strerror(errno));
410 g_strfreev(req);
411 cleanup_client(client);
412 return send_syserror(ctx, error);
415 if (st.st_size == 0)
416 goto new_doc;
418 if (cache_get_key(client->md5file, shakey) == TRUE)
419 cached = 1;
420 else {
422 * No key specified and no matching filename found in the cache.
424 if (!req[1] || !*req[1]) {
425 close(fd);
426 g_strfreev(req);
427 cleanup_client(client);
428 return send_error(ctx, EPWMD_KEY);
432 if (!cached) {
433 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
434 memset(req[1], 0, strlen(req[1]));
437 error = try_xml_decrypt(ctx, fd, st, shakey);
439 if (error) {
440 close(fd);
441 memset(shakey, 0, sizeof(shakey));
442 g_strfreev(req);
443 cleanup_client(client);
444 return send_error(ctx, error);
447 close(fd);
448 client->filename = g_strdup(filename);
450 if (!client->filename) {
451 memset(shakey, 0, sizeof(shakey));
452 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
453 g_strfreev(req);
454 cleanup_client(client);
455 return send_syserror(ctx, ENOMEM);
458 update_cache:
459 if (!cached) {
460 if (cache_update_key(client->md5file, shakey) == FALSE) {
461 memset(shakey, 0, sizeof(shakey));
462 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
463 g_strfreev(req);
464 cleanup_client(client);
465 return send_error(ctx, EPWMD_MAX_SLOTS);
468 timeout = get_key_file_integer(client->filename, "cache_timeout");
469 cache_reset_timeout(client->md5file, timeout);
471 else
472 cache_set_timeout(client->md5file, -2);
474 memset(shakey, 0, sizeof(shakey));
476 done:
477 g_strfreev(req);
478 error = parse_xml(ctx);
480 if (client->xml) {
481 gcry_free(client->xml);
482 client->xml = NULL;
485 if (error == 0) {
486 client->state = STATE_OPEN;
487 error = send_cache_status(ctx);
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 guint iter_progress = 0, n_iter = 0, xiter = 0;
625 gchar tmp[FILENAME_MAX];
626 struct file_header_s {
627 guint iter;
628 guchar iv[gcryblocksize];
629 } file_header;
631 if (insize / gcryblocksize) {
632 len = (insize / gcryblocksize) * gcryblocksize;
634 if (insize % gcryblocksize)
635 len += gcryblocksize;
639 * Resize the existing xml buffer to the block size required by gcrypt
640 * rather than duplicating it and wasting memory.
642 inbuf = gcry_realloc(data, len);
644 if (!inbuf)
645 return gpg_error_from_errno(ENOMEM);
647 insize = len;
648 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
649 memcpy(tkey, shakey, sizeof(tkey));
650 tkey[0] ^= 1;
652 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
653 gcry_free(inbuf);
654 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
655 return gcryerrno;
658 file_header.iter = iter;
660 if (client)
661 iter_progress = get_key_file_integer("default", "iteration_progress");
663 if (client && iter_progress && file_header.iter >= iter_progress) {
664 error = assuan_write_status(client->ctx, "ENCRYPT", "0");
666 if (error) {
667 gcry_free(inbuf);
668 return error;
672 while (xiter < file_header.iter) {
673 if (client && iter_progress > 0 && xiter >= iter_progress) {
674 if (!(xiter % iter_progress)) {
675 error = assuan_write_status(client->ctx, "ENCRYPT", print_fmt("%i",
676 ++n_iter * iter_progress));
678 if (error) {
679 gcry_free(inbuf);
680 return error;
685 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
686 sizeof(file_header.iv)))) {
687 gcry_free(inbuf);
688 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
689 return gcryerrno;
692 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
693 == FALSE) {
694 gcry_free(inbuf);
695 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
696 return gcryerrno;
699 xiter++;
700 pth_yield(NULL);
703 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
704 sizeof(file_header.iv)))) {
705 gcry_free(inbuf);
706 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
707 return gcryerrno;
710 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
711 gcry_free(inbuf);
712 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
713 return gcryerrno;
716 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
717 gcry_free(inbuf);
718 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
719 return gcryerrno;
722 if (client && iter_progress && file_header.iter >= iter_progress) {
723 error = assuan_write_status(client->ctx, "ENCRYPT",
724 print_fmt("%i", file_header.iter));
726 if (error) {
727 gcry_free(inbuf);
728 return error;
732 if (filename) {
733 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
735 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
736 error = errno;
737 gcry_free(inbuf);
738 p = strrchr(tmp, '/');
739 p++;
740 log_write("%s: %s", p, strerror(errno));
741 return gpg_error_from_errno(error);
744 else
746 * xml_import() from command line.
748 fd = STDOUT_FILENO;
750 len = pth_write(fd, &file_header, sizeof(struct file_header_s));
752 if (len != sizeof(file_header)) {
753 len = errno;
755 if (filename)
756 close(fd);
758 gcry_free(inbuf);
759 return gpg_error_from_errno(len);
762 len = pth_write(fd, inbuf, insize);
764 if (len != insize) {
765 len = errno;
767 if (filename)
768 close(fd);
770 gcry_free(inbuf);
771 return gpg_error_from_errno(len);
774 if (fsync(fd) == -1) {
775 len = errno;
776 close(fd);
777 gcry_free(inbuf);
778 return gpg_error_from_errno(len);
781 if (filename) {
782 struct stat st;
783 mode_t mode = 0;
785 close(fd);
787 if (stat(filename, &st) == 0)
788 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
790 if (rename(tmp, filename) == -1) {
791 len = errno;
792 gcry_free(inbuf);
793 return gpg_error_from_errno(len);
796 if (mode)
797 chmod(filename, mode);
800 gcry_free(inbuf);
801 return 0;
804 static int save_command(assuan_context_t ctx, char *line)
806 gpointer xmlbuf;
807 xmlChar *p;
808 gint len;
809 gint cached = 0;
810 guchar shakey[gcrykeysize];
811 gint iter;
812 struct stat st;
813 struct client_s *client = assuan_get_pointer(ctx);
814 gpg_error_t error;
815 gint timeout;
816 gpointer outbuf;
817 glong outsize = 0;
818 gint zerror;
820 MUTEX_LOCK(ctx, PTH_RWLOCK_RW, NULL);
821 error = file_modified(client);
823 if (error) {
824 log_write("%s: %s", client->filename ? client->filename : "",
825 pwmd_strerror(error));
826 return send_error(ctx, error);
829 if (stat(client->filename, &st) == -1 && errno != ENOENT)
830 return send_syserror(ctx, errno);
832 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
833 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
834 return send_error(ctx, EPWMD_INVALID_FILENAME);
837 if (!line || !*line) {
838 if (cache_get_key(client->md5file, shakey) == FALSE)
839 return send_error(ctx, EPWMD_KEY);
841 cached = 1;
843 else {
844 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
845 memset(line, 0, strlen(line));
848 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
849 xmlbuf = p;
851 iter = get_key_file_integer(client->filename, "compression_level");
853 if (iter < 0)
854 iter = 0;
856 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
857 memset(shakey, 0, sizeof(shakey));
858 xmlFree(xmlbuf);
860 if (zerror == Z_MEM_ERROR) {
861 return send_syserror(ctx, ENOMEM);
863 else
864 return send_error(ctx, GPG_ERR_COMPR_ALGO);
866 else {
867 gcry_free(xmlbuf);
868 xmlbuf = outbuf;
869 len = outsize;
872 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
873 iter = 0;
875 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
877 if (error) {
878 memset(shakey, 0, sizeof(shakey));
879 return send_error(ctx, error);
882 stat(client->filename, &st);
883 client->mtime = st.st_mtime;
884 timeout = get_key_file_integer(client->filename, "cache_timeout");
886 if (cached) {
887 memset(shakey, 0, sizeof(shakey));
888 cache_reset_timeout(client->md5file, timeout);
889 client->new = FALSE;
890 return send_error(ctx, 0);
893 if (cache_update_key(client->md5file, shakey) == FALSE) {
894 memset(shakey, 0, sizeof(shakey));
895 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
896 return send_error(ctx, EPWMD_MAX_SLOTS);
899 client->new = FALSE;
900 memset(shakey, 0, sizeof(shakey));
901 cache_reset_timeout(client->md5file, timeout);
902 return send_error(ctx, 0);
905 static gboolean contains_whitespace(const gchar *str)
907 const gchar *p = str;
908 gunichar c;
909 glong len;
911 len = g_utf8_strlen(p++, -1) -1;
913 while (len--) {
914 c = g_utf8_get_char(p++);
916 if (g_unichar_isspace(c))
917 return TRUE;
920 return FALSE;
923 static int delete_command(assuan_context_t ctx, char *line)
925 struct client_s *client = assuan_get_pointer(ctx);
926 gchar **req;
927 gpg_error_t error;
928 xmlNodePtr n;
930 error = file_modified(client);
932 if (error) {
933 log_write("%s: %s", client->filename ? client->filename : "",
934 pwmd_strerror(error));
935 return send_error(ctx, error);
938 if (strchr(line, '\t'))
939 req = split_input_line(line, "\t", -1);
940 else
941 req = split_input_line(line, " ", -1);
943 if (!req || !*req)
944 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
946 n = find_account(client->doc, &req, &error, NULL);
948 if (!n) {
949 g_strfreev(req);
950 return send_error(ctx, error);
954 * No sub-node defined. Remove the entire node (account).
956 if (!req[1]) {
957 if (n) {
958 xmlUnlinkNode(n);
959 xmlFreeNode(n);
962 g_strfreev(req);
963 return send_error(ctx, 0);
966 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
967 g_strfreev(req);
969 if (!n)
970 return send_error(ctx, error);
972 if (n) {
973 xmlUnlinkNode(n);
974 xmlFreeNode(n);
977 return send_error(ctx, 0);
980 static int store_command_finalize(gpointer data, gint rc)
982 assuan_context_t ctx = data;
983 struct client_s *client = assuan_get_pointer(ctx);
984 gchar **req;
985 guchar *result = client->inquire_result;
986 xmlNodePtr n;
987 gpg_error_t error = file_modified(client);
989 req = split_input_line((gchar *)result, "\t", 0);
990 #ifndef MEM_DEBUG
991 xfree(result);
992 #else
993 free(result);
994 #endif
995 client->inquire_result = NULL;
996 client->inquire_len = 0;
998 if (rc) {
999 if (req)
1000 g_strfreev(req);
1002 return send_error(ctx, rc);
1005 if (!req || !*req)
1006 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1008 again:
1009 n = find_account(client->doc, &req, &error, NULL);
1011 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1012 if (contains_whitespace(*req) == TRUE) {
1013 g_strfreev(req);
1014 return send_error(ctx, EPWMD_INVALID_ELEMENT);
1017 error = new_account(client->doc, *req);
1019 if (error) {
1020 g_strfreev(req);
1021 return send_error(ctx, error);
1024 goto again;
1027 if (!n) {
1028 g_strfreev(req);
1029 return send_error(ctx, error);
1032 if (req[1]) {
1033 if (!n->children)
1034 create_elements_cb(n, req+1, &error, NULL);
1035 else
1036 find_elements(client->doc, n->children, req+1, &error,
1037 NULL, NULL, create_elements_cb, NULL);
1040 g_strfreev(req);
1041 return send_error(ctx, error);
1044 static int store_command(assuan_context_t ctx, char *line)
1046 struct client_s *client = assuan_get_pointer(ctx);
1047 gpg_error_t error = file_modified(client);
1049 if (error) {
1050 log_write("%s: %s", client->filename ? client->filename : "",
1051 pwmd_strerror(error));
1052 return send_error(ctx, error);
1055 error = assuan_inquire_ext(ctx, "STORE", &client->inquire_result,
1056 &client->inquire_len, 0, store_command_finalize, ctx);
1058 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1059 return assuan_set_error(ctx, gpg_err_code(error), pwmd_strerror(error));
1062 static int get_command(assuan_context_t ctx, char *line)
1064 struct client_s *client = assuan_get_pointer(ctx);
1065 gchar **req;
1066 gpg_error_t error;
1067 xmlNodePtr n;
1069 error = file_modified(client);
1071 if (error) {
1072 log_write("%s: %s", client->filename ? client->filename : "",
1073 pwmd_strerror(error));
1074 return send_error(ctx, error);
1077 req = split_input_line(line, "\t", -1);
1079 if (!req || !*req) {
1080 g_strfreev(req);
1081 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1084 n = find_account(client->doc, &req, &error, NULL);
1086 if (!n) {
1087 g_strfreev(req);
1088 return send_error(ctx, error);
1091 if (req[1])
1092 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, NULL);
1094 g_strfreev(req);
1096 if (error)
1097 return send_error(ctx, error);
1099 if (!n || !n->children)
1100 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1102 n = n->children;
1104 if (!n || !n->content || !*n->content)
1105 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1107 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1108 return send_error(ctx, error);
1111 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1113 xmlChar *p = path;
1114 gint n;
1115 gchar *buf;
1117 if (!p)
1118 return NULL;
1120 for (n = 0; *p && n < 3; p++) {
1121 if (*p == '/')
1122 n++;
1125 if (strstr((gchar *)p, "text()") != NULL)
1126 p[xmlStrlen(p) - 7] = 0;
1128 for (n = 0; p[n]; n++) {
1129 if (p[n] == '/')
1130 p[n] = '\t';
1133 buf = g_strdup_printf("%s\t%s", account, p);
1134 return buf;
1137 gboolean strv_printf(gchar ***array, const gchar *fmt, ...)
1139 gchar **a;
1140 va_list ap;
1141 gchar *buf;
1142 gint len = *array ? g_strv_length(*array) : 0;
1143 gint ret;
1145 if (!fmt)
1146 return FALSE;
1148 if ((a = g_realloc(*array, (len + 2) * sizeof(gchar *))) == NULL)
1149 return FALSE;
1151 va_start(ap, fmt);
1152 ret = g_vasprintf(&buf, fmt, ap);
1153 va_end(ap);
1155 if (ret == -1)
1156 return FALSE;
1158 a[len++] = buf;
1159 a[len] = NULL;
1160 *array = a;
1161 return TRUE;
1164 struct realpath_s {
1165 gchar *account;
1168 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1169 gpg_error_t *error, void *data)
1171 struct realpath_s *rp = data;
1173 if (rp->account)
1174 g_free(rp->account);
1176 rp->account = g_strdup(req[0]);
1178 if (!rp->account) {
1179 *error = gpg_error_from_errno(ENOMEM);
1180 return NULL;
1183 return node;
1186 static int realpath_command(assuan_context_t ctx, char *line)
1188 gpg_error_t error;
1189 struct client_s *client = assuan_get_pointer(ctx);
1190 xmlChar *p;
1191 gchar **req;
1192 gchar *result, *t;
1193 gint i;
1194 xmlNodePtr n;
1195 struct realpath_s *rp;
1196 GString *string;
1198 error = file_modified(client);
1200 if (error) {
1201 log_write("%s: %s", client->filename ? client->filename : "",
1202 pwmd_strerror(error));
1203 return send_error(ctx, error);
1206 if (strchr(line, '\t') != NULL) {
1207 if ((req = split_input_line(line, "\t", 0)) == NULL)
1208 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1210 else {
1211 if ((req = split_input_line(line, " ", 0)) == NULL)
1212 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1215 n = find_account(client->doc, &req, &error, NULL);
1217 if (!n) {
1218 g_strfreev(req);
1219 return send_error(ctx, error);
1222 rp = g_malloc(sizeof(struct realpath_s));
1224 if (!rp) {
1225 g_strfreev(req);
1226 return send_syserror(ctx, ENOMEM);
1229 rp->account = g_strdup(req[0]);
1231 if (!rp->account) {
1232 g_strfreev(req);
1233 return send_syserror(ctx, ENOMEM);
1236 if (req[1]) {
1237 n = find_elements(client->doc, n->children, req+1, &error,
1238 NULL, realpath_elements_cb, NULL, rp);
1240 if (!n) {
1241 g_free(rp->account);
1242 g_free(rp);
1243 g_strfreev(req);
1244 return send_error(ctx, error);
1248 p = xmlGetNodePath(n);
1249 result = element_path_to_req(rp->account, p);
1251 if (!result) {
1252 g_free(result);
1253 g_free(rp->account);
1254 g_free(rp);
1255 g_strfreev(req);
1256 xmlFree(p);
1257 return send_syserror(ctx, ENOMEM);
1260 string = g_string_new(result);
1261 g_free(result);
1262 g_free(rp->account);
1263 g_free(rp);
1264 g_strfreev(req);
1265 xmlFree(p);
1266 i = 0;
1268 again:
1269 for (t = string->str + i; *t; t++, i++) {
1270 if (!i || *t == '\t') {
1271 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1272 goto again;
1276 error = assuan_send_data(ctx, string->str, string->len);
1277 g_string_free(string, TRUE);
1278 return send_error(ctx, error);
1281 struct list_element_s {
1282 GSList *list;
1283 gchar **elements;
1286 static gboolean append_to_element_list(struct list_element_s *elements)
1288 gchar *tmp;
1289 gint i, total;
1290 gchar *a;
1291 GSList *list;
1293 if (!elements || !elements->elements)
1294 return TRUE;
1296 tmp = g_strjoinv("\t", elements->elements);
1298 if (!tmp)
1299 return FALSE;
1301 g_strfreev(elements->elements);
1302 elements->elements = NULL;
1303 total = g_slist_length(elements->list);
1304 a = g_utf8_collate_key(tmp, -1);
1306 if (!a) {
1307 g_free(tmp);
1308 return FALSE;
1312 * Removes duplicate element paths from the list. This is needed when
1313 * appending an element tree from list_command(). The glib docs recommend
1314 * using g_utf8_collate_key() for a large number of strings.
1316 for (i = 0; i < total; i++) {
1317 gchar *p = g_slist_nth_data(elements->list, i);
1318 gchar *b = g_utf8_collate_key(p, -1);
1320 if (!b) {
1321 g_free(a);
1322 g_free(tmp);
1323 return FALSE;
1326 if (strcmp(a, b) == 0) {
1327 g_free(a);
1328 g_free(b);
1329 g_free(tmp);
1330 return TRUE;
1333 g_free(b);
1336 g_free(a);
1337 list = g_slist_append(elements->list, tmp);
1339 if (!list)
1340 return FALSE;
1342 elements->list = list;
1343 return TRUE;
1346 static gpg_error_t do_list_recurse(xmlDocPtr doc, xmlNodePtr node,
1347 struct list_element_s *elements, gchar *prefix)
1349 xmlNodePtr n;
1350 gpg_error_t error;
1352 if (append_to_element_list(elements) == FALSE)
1353 return gpg_error_from_errno(ENOMEM);
1355 for (n = node; n; n = n->next) {
1356 if (n->type == XML_ELEMENT_NODE) {
1357 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
1358 gchar *tmp;
1360 if (content) {
1361 if (strv_printf(&elements->elements, "%s\t%s", prefix, n->name) == FALSE)
1362 return gpg_error_from_errno(ENOMEM);
1364 if (append_to_element_list(elements) == FALSE)
1365 return gpg_error_from_errno(ENOMEM);
1368 tmp = g_strdup_printf("%s\t!%s", prefix, n->name);
1370 if (!tmp)
1371 return gpg_error_from_errno(ENOMEM);
1373 if (strv_printf(&elements->elements, "%s", tmp) == FALSE) {
1374 g_free(tmp);
1375 return gpg_error_from_errno(ENOMEM);
1378 if (n->children) {
1379 error = do_list_recurse(doc, n->children, elements, tmp);
1380 g_free(tmp);
1382 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1383 return error;
1385 else
1386 g_free(tmp);
1388 if (append_to_element_list(elements) == FALSE)
1389 return gpg_error_from_errno(ENOMEM);
1393 return 0;
1396 static gpg_error_t do_list_command(assuan_context_t ctx, xmlDocPtr doc,
1397 struct list_element_s *elements, char *line)
1399 gchar *prefix = NULL, *account;
1400 gchar **req = NULL, **oreq = NULL, *tmp;
1401 xmlNodePtr n;
1402 gboolean account_is_literal, account_has_target = FALSE;
1403 gint which = 0;
1404 gchar **p;
1405 gpg_error_t error;
1407 if ((req = split_input_line(line, "\t", 0)) == NULL) {
1408 if ((req = split_input_line(line, " ", 0)) == NULL)
1409 return EPWMD_COMMAND_SYNTAX;
1412 prefix = g_strdup(*req);
1414 if (!prefix) {
1415 g_strfreev(req);
1416 return gpg_error_from_errno(ENOMEM);
1419 account = g_strdup(*req);
1421 if (!account) {
1422 g_free(prefix);
1423 g_strfreev(req);
1424 return gpg_error_from_errno(ENOMEM);
1427 oreq = g_strdupv(req);
1429 if (!oreq) {
1430 g_free(prefix);
1431 g_free(account);
1432 g_strfreev(req);
1433 return gpg_error_from_errno(ENOMEM);
1436 p = req;
1437 again:
1438 account_has_target = FALSE;
1439 account_is_literal = is_literal_element_str(prefix);
1440 n = find_account(doc, &p, &error, &account_has_target);
1442 if (which)
1443 oreq = p;
1444 else
1445 req = p;
1447 if (!n)
1448 goto fail;
1450 if (!which && account_is_literal == FALSE && account_has_target == FALSE) {
1451 tmp = g_strdup_printf("!%s", prefix);
1453 if (!tmp) {
1454 error = gpg_error_from_errno(ENOMEM);
1455 goto fail;
1458 g_free(prefix);
1459 prefix = tmp;
1462 if (*(p+1)) {
1463 gchar *t;
1465 n = find_elements(doc, n->children, p+1, &error,
1466 NULL, NULL, NULL, NULL);
1468 if (error)
1469 goto fail;
1471 tmp = g_strjoinv("\t", p+1);
1472 if (!tmp) {
1473 error = gpg_error_from_errno(ENOMEM);
1474 goto fail;
1477 t = g_strdup_printf("%s\t%s", prefix, tmp);
1478 if (!t) {
1479 error = gpg_error_from_errno(ENOMEM);
1480 goto fail;
1483 g_free(prefix);
1484 prefix = t;
1485 g_free(tmp);
1488 if (strv_printf(&elements->elements, "%s", prefix) == FALSE) {
1489 error = gpg_error_from_errno(ENOMEM);
1490 goto fail;
1493 if (node_has_child_element(n->children) == FALSE) {
1494 if (append_to_element_list(elements) == FALSE) {
1495 error = gpg_error_from_errno(ENOMEM);
1496 goto fail;
1499 else
1500 error = do_list_recurse(doc, n->children, elements, prefix);
1502 if (error)
1503 goto fail;
1505 if (!which++ && !*(p+1) && account_is_literal == FALSE && account_has_target == TRUE) {
1506 g_free(*oreq);
1507 *oreq = g_strdup_printf("!%s", account);
1509 if (!*oreq) {
1510 error = gpg_error_from_errno(ENOMEM);
1511 goto fail;
1514 p = oreq;
1515 g_free(prefix);
1516 prefix = g_strdup(*oreq);
1518 if (!prefix) {
1519 error = gpg_error_from_errno(ENOMEM);
1520 goto fail;
1523 goto again;
1526 fail:
1527 g_free(prefix);
1528 g_free(account);
1529 g_strfreev(req);
1531 if (oreq)
1532 g_strfreev(oreq);
1534 return error;
1538 * This could be faster especially when finding "target" attributes.
1540 static int list_command(assuan_context_t ctx, char *line)
1542 struct client_s *client = assuan_get_pointer(ctx);
1543 gpg_error_t error;
1544 struct list_element_s *elements = NULL;
1545 GString *string;
1546 gchar *tmp;
1547 gint i, total;
1549 if (disable_list_and_dump == TRUE)
1550 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1552 error = file_modified(client);
1554 if (error) {
1555 log_write("%s: %s", client->filename, pwmd_strerror(error));
1556 return send_error(ctx, error);
1559 if (!*line) {
1560 GString *str;
1562 error = list_accounts(client->doc, &str);
1564 if (error)
1565 return send_error(ctx, error);
1567 error = assuan_send_data(ctx, str->str, str->len);
1568 g_string_free(str, TRUE);
1569 return send_error(ctx, error);
1572 elements = g_malloc0(sizeof(struct list_element_s));
1574 if (!elements) {
1575 error = gpg_error_from_errno(ENOMEM);
1576 goto fail;
1579 is_list_command = TRUE;
1580 error = do_list_command(ctx, client->doc, elements, line);
1582 if (error)
1583 goto fail;
1585 if (!error) {
1586 total = g_slist_length(elements->list);
1588 if (!total) {
1589 error = EPWMD_EMPTY_ELEMENT;
1590 goto fail;
1594 * Find element paths with a target and append those element trees to
1595 * the list.
1597 for (i = 0; i < total; i++) {
1598 gchar **req;
1600 tmp = g_slist_nth_data(elements->list, i);
1601 req = split_input_line(tmp, "\t", 0);
1603 if (!req) {
1604 if (g_str_has_prefix(tmp, "!") == TRUE) {
1605 g_strfreev(req);
1606 continue;
1609 else {
1610 gchar **p;
1612 for (p = req; *p; p++) {
1613 if (g_str_has_prefix(*p, "!") == FALSE)
1614 break;
1617 if (!*p) {
1618 g_strfreev(req);
1619 continue;
1623 g_strfreev(req);
1624 error = do_list_command(ctx, client->doc, elements, tmp);
1626 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1627 goto fail;
1629 total = g_slist_length(elements->list);
1633 string = g_string_new(NULL);
1635 for (i = 0; i < total; i++) {
1636 tmp = g_slist_nth_data(elements->list, i);
1637 g_string_append_printf(string, "%s\n", tmp);
1638 g_free(tmp);
1641 string = g_string_truncate(string, string->len - 1);
1642 error = assuan_send_data(ctx, string->str, string->len);
1643 g_string_free(string, TRUE);
1645 fail:
1646 is_list_command = FALSE;
1648 if (elements) {
1649 if (elements->list)
1650 g_slist_free(elements->list);
1652 if (elements->elements)
1653 g_strfreev(elements->elements);
1655 g_free(elements);
1658 return send_error(ctx, error);
1661 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1662 const gchar *value)
1664 xmlAttrPtr a;
1666 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1667 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1669 if (!a)
1670 return EPWMD_LIBXML_ERROR;
1672 else
1673 xmlNodeSetContent(a->children, (xmlChar *)value);
1675 return 0;
1679 * req[0] - element path
1681 static int attribute_list(assuan_context_t ctx, gchar **req)
1683 struct client_s *client = assuan_get_pointer(ctx);
1684 gchar **attrlist = NULL;
1685 gint i = 0;
1686 gchar **path = NULL;
1687 xmlAttrPtr a;
1688 xmlNodePtr n, an;
1689 gchar *line;
1690 gpg_error_t error;
1692 if (!req || !req[0])
1693 return EPWMD_COMMAND_SYNTAX;
1695 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1697 * The first argument may be only an account.
1699 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1700 return EPWMD_COMMAND_SYNTAX;
1703 n = find_account(client->doc, &path, &error, NULL);
1705 if (!n) {
1706 g_strfreev(path);
1707 return error;
1710 if (path[1]) {
1711 n = find_elements(client->doc, n->children, path+1, &error,
1712 NULL, NULL, NULL, NULL);
1714 if (!n) {
1715 g_strfreev(path);
1716 return error;
1720 g_strfreev(path);
1722 for (a = n->properties; a; a = a->next) {
1723 gchar **pa;
1725 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1726 if (attrlist)
1727 g_strfreev(attrlist);
1729 error = errno;
1730 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1731 return gpg_error_from_errno(error);
1734 attrlist = pa;
1735 an = a->children;
1736 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1738 if (!attrlist[i]) {
1739 g_strfreev(attrlist);
1740 return gpg_error_from_errno(ENOMEM);
1743 attrlist[++i] = NULL;
1746 if (!attrlist)
1747 return EPWMD_EMPTY_ELEMENT;
1749 line = g_strjoinv("\n", attrlist);
1751 if (!line) {
1752 g_strfreev(attrlist);
1753 return gpg_error_from_errno(ENOMEM);
1756 error = assuan_send_data(ctx, line, g_utf8_strlen(line, -1));
1757 g_free(line);
1758 g_strfreev(attrlist);
1759 return error;
1763 * req[0] - attribute
1764 * req[1] - element path
1766 static int attribute_delete(struct client_s *client, gchar **req)
1768 xmlAttrPtr a;
1769 xmlNodePtr n;
1770 gchar **path = NULL;
1771 gpg_error_t error;
1773 if (!req || !req[0] || !req[1])
1774 return EPWMD_COMMAND_SYNTAX;
1776 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1778 * The first argument may be only an account.
1780 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1781 return EPWMD_COMMAND_SYNTAX;
1785 * Don't remove the "name" attribute for the account element. To remove an
1786 * account use DELETE <account>.
1788 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1789 error = EPWMD_ATTR_SYNTAX;
1790 goto fail;
1793 n = find_account(client->doc, &path, &error, NULL);
1795 if (!n)
1796 goto fail;
1798 if (path[1]) {
1799 n = find_elements(client->doc, n->children, path+1, &error,
1800 NULL, NULL, NULL, NULL);
1802 if (!n)
1803 goto fail;
1806 g_strfreev(path);
1808 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1809 return EPWMD_ATTR_NOT_FOUND;
1811 if (xmlRemoveProp(a) == -1)
1812 return EPWMD_LIBXML_ERROR;
1814 return 0;
1816 fail:
1817 g_strfreev(path);
1818 return error;
1822 * Creates a "target" attribute. When other commands encounter an element with
1823 * this attribute, the element path is modified to the target value. If the
1824 * source element path doesn't exist when using 'ATTR SET target', it is
1825 * created, but the destination element path must exist.
1827 * req[0] - source element path
1828 * req[1] - destination element path
1830 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1832 gchar **src, **dst, *line;
1833 gpg_error_t error;
1834 xmlNodePtr n;
1836 if (!req || !req[0] || !req[1])
1837 return EPWMD_COMMAND_SYNTAX;
1839 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1841 * The first argument may be only an account.
1843 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1844 return EPWMD_COMMAND_SYNTAX;
1847 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1849 * The first argument may be only an account.
1851 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1852 error = EPWMD_COMMAND_SYNTAX;
1853 goto fail;
1857 n = find_account(client->doc, &dst, &error, NULL);
1860 * Make sure the destination element path exists.
1862 if (!n)
1863 goto fail;
1865 if (dst[1]) {
1866 n = find_elements(client->doc, n->children, dst+1, &error,
1867 NULL, NULL, NULL, NULL);
1869 if (!n)
1870 goto fail;
1873 again:
1874 n = find_account(client->doc, &src, &error, NULL);
1876 if (!n) {
1877 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1878 error = new_account(client->doc, src[0]);
1880 if (error)
1881 goto fail;
1883 goto again;
1885 else
1886 goto fail;
1889 if (src[1]) {
1890 if (!n->children)
1891 n = create_target_elements_cb(n, src+1, &error, NULL);
1892 else
1893 n = find_elements(client->doc, n->children, src+1, &error,
1894 NULL, NULL, create_target_elements_cb, NULL);
1896 if (!n)
1897 goto fail;
1900 * Reset the position of the element tree now that the elements
1901 * have been created.
1903 n = find_account(client->doc, &src, &error, NULL);
1905 if (!n)
1906 goto fail;
1908 n = find_elements(client->doc, n->children, src+1, &error,
1909 NULL, NULL, NULL, NULL);
1911 if (!n)
1912 goto fail;
1915 line = g_strjoinv("\t", dst);
1916 error = add_attribute(n, "target", line);
1918 if (error) {
1919 g_free(line);
1920 goto fail;
1923 g_free(line);
1924 g_strfreev(src);
1925 g_strfreev(dst);
1926 return 0;
1928 fail:
1929 g_strfreev(src);
1930 g_strfreev(dst);
1931 return error;
1935 * req[0] - account name
1936 * req[1] - new name
1938 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1940 gpg_error_t error;
1941 gchar **tmp;
1942 xmlNodePtr n;
1944 tmp = g_strdupv(req);
1946 if (!tmp)
1947 return gpg_error_from_errno(ENOMEM);
1949 n = find_account(client->doc, &tmp, &error, NULL);
1950 g_strfreev(tmp);
1952 if (!n)
1953 return error;
1955 if (g_utf8_collate(req[0], req[1]) == 0)
1956 return 0;
1959 * Will not overwrite an existing account.
1961 tmp = g_strdupv(req+1);
1963 if (!tmp)
1964 return gpg_error_from_errno(ENOMEM);
1966 n = find_account(client->doc, &tmp, &error, NULL);
1967 g_strfreev(tmp);
1969 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1970 return error;
1972 if (n)
1973 return EPWMD_ACCOUNT_EXISTS;
1976 * Whitespace not allowed in account names.
1978 if (contains_whitespace(req[1]) == TRUE)
1979 return EPWMD_ATTR_SYNTAX;
1981 tmp = g_strdupv(req);
1983 if (!tmp)
1984 return gpg_error_from_errno(ENOMEM);
1986 n = find_account(client->doc, &tmp, &error, NULL);
1987 g_strfreev(tmp);
1989 if (!n)
1990 return EPWMD_ELEMENT_NOT_FOUND;
1992 return add_attribute(n, "name", req[1]);
1996 * req[0] - attribute
1997 * req[1] - element path
1999 static int attribute_get(assuan_context_t ctx, gchar **req)
2001 struct client_s *client = assuan_get_pointer(ctx);
2002 xmlNodePtr n;
2003 xmlChar *a;
2004 gchar **path= NULL;
2005 gpg_error_t error;
2007 if (!req || !req[0] || !req[1])
2008 return EPWMD_COMMAND_SYNTAX;
2010 if (strchr(req[1], '\t')) {
2011 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2012 return EPWMD_COMMAND_SYNTAX;
2014 else {
2015 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2016 return EPWMD_COMMAND_SYNTAX;
2019 n = find_account(client->doc, &path, &error, NULL);
2021 if (!n)
2022 goto fail;
2024 if (path[1]) {
2025 n = find_elements(client->doc, n->children, path+1, &error,
2026 NULL, NULL, NULL, NULL);
2028 if (!n)
2029 goto fail;
2032 g_strfreev(path);
2034 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2035 return EPWMD_ATTR_NOT_FOUND;
2037 error = assuan_send_data(ctx, a, xmlStrlen(a));
2038 xmlFree(a);
2039 return error;
2041 fail:
2042 g_strfreev(path);
2043 return error;
2047 * req[0] - attribute
2048 * req[1] - element path
2049 * req[2] - value
2051 static int attribute_set(struct client_s *client, gchar **req)
2053 gchar **path = NULL;
2054 gpg_error_t error;
2055 xmlNodePtr n;
2057 if (!req || !req[0] || !req[1] || !req[2])
2058 return EPWMD_COMMAND_SYNTAX;
2061 * Reserved attribute names.
2063 if (g_utf8_collate(req[0], "name") == 0) {
2065 * Only reserved for the account element. Not the rest of the
2066 * document.
2068 if (strchr(req[1], '\t') == NULL)
2069 return name_attribute(client, req + 1);
2071 else if (g_utf8_collate(req[0], "target") == 0)
2072 return target_attribute(client, req + 1);
2074 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2076 * The first argument may be only an account.
2078 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2079 return EPWMD_COMMAND_SYNTAX;
2082 n = find_account(client->doc, &path, &error, NULL);
2084 if (!n)
2085 goto fail;
2087 if (path[1]) {
2088 n = find_elements(client->doc, n->children, path+1, &error,
2089 NULL, NULL, NULL, NULL);
2091 if (!n)
2092 goto fail;
2095 g_strfreev(path);
2096 return add_attribute(n, req[0], req[2]);
2098 fail:
2099 g_strfreev(path);
2100 return error;
2104 * req[0] - command
2105 * req[1] - attribute name or element path if command is LIST
2106 * req[2] - element path
2107 * req[2] - element path or value
2109 static int attr_command(assuan_context_t ctx, char *line)
2111 struct client_s *client = assuan_get_pointer(ctx);
2112 gchar **req = split_input_line(line, " ", 4);
2113 gpg_error_t error = 0;
2115 error = file_modified(client);
2117 if (error) {
2118 log_write("%s: %s", client->filename ? client->filename : "",
2119 pwmd_strerror(error));
2120 g_strfreev(req);
2121 return send_error(ctx, error);
2124 if (!req || !req[0] || !req[1]) {
2125 g_strfreev(req);
2126 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2129 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2130 error = attribute_set(client, req+1);
2131 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2132 error = attribute_get(ctx, req+1);
2133 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2134 error = attribute_delete(client, req+1);
2135 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2136 error = attribute_list(ctx, req+1);
2137 else
2138 error = EPWMD_COMMAND_SYNTAX;
2140 g_strfreev(req);
2141 return send_error(ctx, error);
2144 static int iscached_command(assuan_context_t ctx, char *line)
2146 gchar **req = split_input_line(line, " ", 0);
2147 guchar md5file[16];
2149 MUTEX_LOCK(ctx, PTH_RWLOCK_RD, NULL);
2151 if (!req || !*req) {
2152 g_strfreev(req);
2153 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2156 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2157 g_strfreev(req);
2159 if (cache_iscached(md5file) == FALSE)
2160 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2162 return send_error(ctx, 0);
2165 gpg_error_t send_cache_status(assuan_context_t ctx)
2167 gchar *tmp;
2169 tmp = print_fmt("%i %i",
2170 cache_file_count(),
2171 (cache_size / sizeof(file_cache_t)) - cache_file_count());
2173 return assuan_write_status(ctx, "CACHE", tmp);
2176 static int clearcache_command(assuan_context_t ctx, char *line)
2178 struct client_s *client = assuan_get_pointer(ctx);
2179 gchar **req = split_input_line(line, " ", 0);
2180 guchar md5file[16];
2181 gpg_error_t error;
2183 MUTEX_LOCK(ctx, PTH_RWLOCK_RW, NULL);
2185 if (!req || !*req) {
2186 g_strfreev(req);
2187 cache_clear(client->md5file, 2);
2188 error = send_cache_status(ctx);
2189 return send_error(ctx, error);
2192 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2193 g_strfreev(req);
2194 cache_clear(md5file, 1);
2195 error = send_cache_status(ctx);
2196 return send_error(ctx, error);
2199 static int cachetimeout_command(assuan_context_t ctx, char *line)
2201 guchar md5file[16];
2202 glong timeout;
2203 gchar **req = split_input_line(line, " ", 0);
2204 gchar *p;
2205 gpg_error_t error;
2207 MUTEX_LOCK(ctx, PTH_RWLOCK_RW, NULL);
2209 if (!req || !*req || !req[1]) {
2210 g_strfreev(req);
2211 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2214 errno = 0;
2215 timeout = strtol(req[0], &p, 10);
2217 if (errno != 0 || *p != 0) {
2218 g_strfreev(req);
2219 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2222 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2223 g_strfreev(req);
2225 if (cache_set_timeout(md5file, timeout) == FALSE)
2226 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2228 error = send_cache_status(ctx);
2229 return send_error(ctx, error);
2232 static int dump_command(assuan_context_t ctx, char *line)
2234 xmlChar *xml;
2235 gssize len;
2236 struct client_s *client = assuan_get_pointer(ctx);
2237 gpg_error_t error;
2239 if (disable_list_and_dump == TRUE)
2240 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2242 error = file_modified(client);
2244 if (error) {
2245 log_write("%s: %s", client->filename ? client->filename : "",
2246 pwmd_strerror(error));
2247 return send_error(ctx, error);
2250 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2251 error = assuan_send_data(ctx, xml, len);
2252 xmlFree(xml);
2253 return send_error(ctx, error);
2256 static int getconfig_command(assuan_context_t ctx, gchar *line)
2258 struct client_s *client = assuan_get_pointer(ctx);
2259 gpg_error_t error = 0;
2260 gchar *p, *tmp;
2262 if (g_utf8_collate(line, "key") == 0 || g_utf8_collate(line, "key_file") == 0)
2263 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2265 p = get_key_file_string(client->filename ? client->filename : "default", line);
2267 if (!p)
2268 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2270 tmp = expand_homedir(p);
2271 g_free(p);
2272 p = tmp;
2273 error = assuan_send_data(ctx, p, g_utf8_strlen(p, -1));
2274 g_free(p);
2275 return send_error(ctx, error);
2278 void cleanup_assuan(assuan_context_t ctx)
2280 struct client_s *cl = assuan_get_pointer(ctx);
2282 cleanup_client(cl);
2283 #ifdef WORKING_PTHREADS
2284 MUTEX_UNLOCK;
2285 #endif
2288 gpg_error_t register_commands(assuan_context_t ctx)
2290 static struct {
2291 const char *name;
2292 int (*handler)(assuan_context_t, char *line);
2293 } table[] = {
2294 { "OPEN", open_command },
2295 { "SAVE", save_command },
2296 { "LIST", list_command },
2297 { "REALPATH", realpath_command },
2298 { "STORE", store_command },
2299 { "DELETE", delete_command },
2300 { "GET", get_command },
2301 { "ATTR", attr_command },
2302 { "ISCACHED", iscached_command },
2303 { "CLEARCACHE", clearcache_command },
2304 { "CACHETIMEOUT", cachetimeout_command },
2305 { "GETCONFIG", getconfig_command },
2306 { "DUMP", dump_command },
2307 { "INPUT", NULL },
2308 { "OUTPUT", NULL },
2309 { NULL, NULL }
2311 int i, rc;
2313 for (i=0; table[i].name; i++) {
2314 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2316 if (rc)
2317 return rc;
2320 return assuan_register_bye_notify(ctx, cleanup_assuan);
2323 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2324 guchar *key)
2326 guchar *iv;
2327 void *inbuf;
2328 gsize insize, len;
2329 guchar tkey[gcrykeysize];
2330 struct file_header_s {
2331 guint iter;
2332 guchar iv[gcryblocksize];
2333 } file_header;
2334 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2335 gcry_cipher_hd_t gh;
2336 guint iter = 0, n_iter = 0;
2337 gint iter_progress;
2338 void *outbuf = NULL;
2339 gint zerror = 0;
2340 glong outsize = 0;
2341 gpg_error_t error;
2343 if (!ctx) {
2344 gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2346 if (gcryerrno)
2347 return gcryerrno;
2349 else
2350 gh = client->gh;
2352 lseek(fd, 0, SEEK_SET);
2353 insize = st.st_size - sizeof(struct file_header_s);
2354 iv = gcry_malloc(gcryblocksize);
2356 if (!iv) {
2357 if (!ctx)
2358 gcry_cipher_close(gh);
2360 return gpg_error_from_errno(ENOMEM);
2363 len = pth_read(fd, &file_header, sizeof(struct file_header_s));
2365 if (len != sizeof(file_header)) {
2366 len = errno;
2368 if (!ctx)
2369 gcry_cipher_close(gh);
2371 gcry_free(iv);
2372 errno = len;
2373 return gpg_error_from_errno(errno);
2376 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2377 inbuf = gcry_malloc(insize);
2379 if (!inbuf) {
2380 if (!ctx)
2381 gcry_cipher_close(gh);
2383 gcry_free(iv);
2384 return gpg_error_from_errno(ENOMEM);
2387 len = pth_read(fd, inbuf, insize);
2389 if (len != insize) {
2390 len = errno;
2392 if (!ctx)
2393 gcry_cipher_close(gh);
2395 gcry_free(iv);
2396 errno = len;
2397 return gpg_error_from_errno(errno);
2400 memcpy(tkey, key, sizeof(tkey));
2401 tkey[0] ^= 1;
2403 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2404 if (!ctx) {
2405 gcry_cipher_close(gh);
2406 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2408 else
2409 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2411 gcry_free(inbuf);
2412 gcry_free(iv);
2413 return gcryerrno;
2416 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2417 if (!ctx) {
2418 gcry_cipher_close(gh);
2419 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2421 else
2422 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2424 gcry_free(inbuf);
2425 gcry_free(iv);
2427 if (!ctx)
2428 gcry_cipher_close(gh);
2430 return gcryerrno;
2433 iter_progress = get_key_file_integer("default", "iteration_progress");
2435 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2436 error = assuan_write_status(client->ctx, "DECRYPT", "0");
2438 if (error) {
2439 gcry_free(inbuf);
2440 gcry_free(iv);
2441 return error;
2445 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2446 if (!ctx) {
2447 gcry_cipher_close(gh);
2448 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2450 else
2451 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2453 gcry_free(inbuf);
2454 gcry_free(iv);
2455 return gcryerrno;
2458 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2459 if (!ctx) {
2460 gcry_cipher_close(gh);
2461 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2463 else
2464 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2466 gcry_free(inbuf);
2467 gcry_free(iv);
2468 return gcryerrno;
2471 while (iter < file_header.iter) {
2472 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2473 if (!(iter % iter_progress)) {
2474 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i",
2475 ++n_iter * iter_progress));
2477 if (error) {
2478 gcry_free(inbuf);
2479 gcry_free(iv);
2480 return error;
2485 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2486 if (!ctx) {
2487 gcry_cipher_close(gh);
2488 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2490 else
2491 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2493 gcry_free(inbuf);
2494 gcry_free(iv);
2495 return gcryerrno;
2498 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
2499 if (!ctx) {
2500 gcry_cipher_close(gh);
2501 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2503 else
2504 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
2506 gcry_free(inbuf);
2507 gcry_free(iv);
2508 return gcryerrno;
2511 iter++;
2512 pth_yield(NULL);
2515 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2516 error = assuan_write_status(ctx, "DECRYPT", print_fmt("%i", file_header.iter));
2518 if (error) {
2519 gcry_free(inbuf);
2520 gcry_free(iv);
2521 return error;
2525 gcry_free(iv);
2527 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2529 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2531 if (zerror == Z_MEM_ERROR) {
2532 gcry_free(inbuf);
2533 return gpg_error_from_errno(ENOMEM);
2535 else if (zerror != Z_DATA_ERROR) {
2536 gcry_free(inbuf);
2538 if (!ctx)
2539 gcry_cipher_close(gh);
2541 return EPWMD_BADKEY;
2544 else {
2545 gcry_free(inbuf);
2546 inbuf = outbuf;
2547 insize = outsize;
2550 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2551 gcry_free(inbuf);
2553 if (!ctx)
2554 gcry_cipher_close(gh);
2556 return EPWMD_BADKEY;
2559 if (ctx) {
2560 client->xml = inbuf;
2561 client->len = insize;
2563 else {
2564 gcry_cipher_close(gh);
2565 gcry_free(inbuf);
2568 return 0;