Removed a debugging line.
[pwmd.git] / src / commands.c
blob9e04d06e3368523df715dedf545445624b85043e
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 #include "xml.h"
37 #include "common.h"
38 #include "pwmd_error.h"
39 #include "commands.h"
41 gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
42 void *inbuf, gsize insize)
44 if ((gcryerrno = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
45 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
46 return FALSE;
49 return TRUE;
52 gboolean decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
53 void *inbuf, gsize insize)
55 if ((gcryerrno = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize))) {
56 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
57 return FALSE;
60 return TRUE;
63 static gboolean parse_xml(struct client_s *client)
65 xmlErrorPtr xml_error;
67 switch (open_xml(client->xml, client->len, &client->doc, &client->reader)) {
68 case 1:
69 xml_error = xmlGetLastError();
70 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
71 send_error(client, EPWMD_LIBXML_ERROR);
72 return FALSE;
73 case 2:
74 xml_error = xmlGetLastError();
75 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
76 send_error(client, EPWMD_LIBXML_ERROR);
77 return FALSE;
78 default:
79 break;
82 return TRUE;
85 static gboolean valid_filename(const gchar *filename)
87 const gchar *p;
89 if (!filename || !*filename)
90 return FALSE;
92 for (p = filename; *p; p++) {
93 if (g_ascii_isalnum(*p) == FALSE)
94 return FALSE;
97 return TRUE;
100 static gboolean cache_reset_timeout(const guchar *md5filename)
102 void *p;
103 file_cache_t f;
104 glong len;
106 for (p = shm_data, len = 0; len <= cache_size;) {
107 memcpy(&f, p, sizeof(file_cache_t));
109 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
110 f.elapsed = 0;
111 f.when = f.timeout;
112 memcpy(p, &f, sizeof(file_cache_t));
113 return TRUE;
116 p += sizeof(file_cache_t);
117 len += sizeof(file_cache_t);
119 if (len + sizeof(file_cache_t) > cache_size)
120 break;
123 return FALSE;
126 gboolean cache_set_timeout(const guchar *md5filename, glong timeout)
128 void *p;
129 file_cache_t f;
130 glong len;
132 for (p = shm_data, len = 0; len <= cache_size;) {
133 memcpy(&f, p, sizeof(file_cache_t));
135 if (md5filename) {
136 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
137 f.elapsed = 0;
138 f.when = f.timeout = timeout;
139 memcpy(p, &f, sizeof(file_cache_t));
140 return TRUE;
143 else {
144 f.elapsed = 0;
145 f.when = f.timeout = timeout;
146 memcpy(p, &f, sizeof(file_cache_t));
149 p += sizeof(file_cache_t);
150 len += sizeof(file_cache_t);
152 if (len + sizeof(file_cache_t) > cache_size)
153 break;
156 return (md5filename) ? FALSE : TRUE;
159 static gboolean cache_update_key(const guchar *md5filename, const guchar *shakey)
161 void *p;
162 file_cache_t f;
163 glong len;
165 for (p = shm_data, len = 0; len <= cache_size;) {
166 memcpy(&f, p, sizeof(file_cache_t));
168 if (f.used == TRUE) {
169 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
170 memcpy(&f.key, shakey, sizeof(f.key));
171 memcpy(p, &f, sizeof(file_cache_t));
172 return TRUE;
176 p += sizeof(file_cache_t);
177 len += sizeof(file_cache_t);
179 if (len + sizeof(file_cache_t) > cache_size)
180 break;
183 return cache_add_file(md5filename, shakey);
186 static gint cache_valid_key(const guchar *key, gsize len)
188 gint b;
190 for (b = 0; b < len; b++) {
191 if (key[b])
192 return TRUE;
195 return FALSE;
198 static gboolean cache_get_key(const guchar *md5file, guchar *shakey)
200 void *p;
201 file_cache_t f;
202 glong len;
204 for (p = shm_data, len = 0; len <= cache_size;) {
205 memcpy(&f, p, sizeof(file_cache_t));
208 * The slot may be used but not yet contain a key.
210 if (f.used == TRUE) {
211 if (memcmp(&f.filename, md5file, sizeof(f.filename)) == 0) {
212 if (cache_valid_key(f.key, sizeof(f.key)) == FALSE)
213 return FALSE;
215 memcpy(shakey, &f.key, sizeof(f.key));
216 return TRUE;
220 p += sizeof(file_cache_t);
221 len += sizeof(file_cache_t);
223 if (len + sizeof(file_cache_t) > cache_size)
224 break;
227 return FALSE;
230 gint open_file(const gchar *filename, struct stat *st)
232 gint fd;
234 if ((fd = open(filename, O_RDONLY)) == -1)
235 return -1;
237 if (stat(filename, st) == -1) {
238 close(fd);
239 return -1;
242 return fd;
245 gboolean open_command(struct client_s *client, gchar **req)
247 gint fd;
248 struct stat st;
249 gchar *inbuf;
250 const gchar *filename = req[0];
251 guchar shakey[gcrykeysize];
252 guchar tkey[gcrykeysize];
253 gint cached = 0;
254 gsize insize = 0;
255 guint iter;
256 gint timeout;
257 gchar filebuf[PATH_MAX], *p, *p2;
258 gint error;
259 struct file_header_s {
260 guint iter;
261 guchar iv[gcryblocksize];
262 } file_header;
264 if (!filename || !*filename) {
265 send_error(client, EPWMD_COMMAND_SYNTAX);
266 return FALSE;
269 if (valid_filename(filename) == FALSE) {
270 send_error(client, EPWMD_INVALID_FILENAME);
271 return FALSE;
274 p = get_key_file_string("default", "data_directory");
275 p2 = expand_homedir(p);
276 g_free(p);
277 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
278 g_free(p2);
280 if (stat(filebuf, &st) == 0) {
281 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
282 log_write("%s: %s", filename, pwmd_strerror(EPWMD_NOT_A_FILE));
283 send_to_client(client, "ERR %03i %s\n", EPWMD_NOT_A_FILE, pwmd_strerror(EPWMD_NOT_A_FILE));
284 return FALSE;
287 client->mtime = st.st_mtime;
290 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
293 * New files don't need a key.
295 if (access(filebuf, R_OK|W_OK) != 0) {
296 if (errno != ENOENT) {
297 error = errno;
298 log_write("%s: %s", filename, strerror(errno));
299 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename,
300 strerror(error));
301 return FALSE;
303 new_doc:
304 if ((client->xml = new_document()) == NULL) {
305 error = errno;
306 log_write("%s", strerror(errno));
307 send_to_client(client, "ERR %03i malloc(): %s\n", EPWMD_ERROR,
308 strerror(error));
309 return FALSE;
312 client->len = strlen(client->xml);
314 if (cache_add_file(client->md5file, NULL) == FALSE) {
315 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
316 send_error(client, EPWMD_MAX_SLOTS);
317 return FALSE;
320 client->filename = g_strdup(filename);
321 return parse_xml(client);
324 if ((fd = open_file(filebuf, &st)) == -1) {
325 error = errno;
326 log_write("%s: %s", filename, strerror(errno));
327 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(error));
328 return FALSE;
331 if (st.st_size == 0)
332 goto new_doc;
334 if (cache_get_key(client->md5file, shakey) == TRUE)
335 cached = 1;
336 else {
338 * No key specified and no matching filename found in the cache.
340 if (!req[1] || !*req[1]) {
341 close(fd);
342 send_error(client, EPWMD_KEY);
343 return FALSE;
347 insize = st.st_size - sizeof(struct file_header_s);
348 read(fd, &file_header, sizeof(struct file_header_s));
349 inbuf = gcry_malloc(insize);
350 read(fd, inbuf, insize);
351 close(fd);
353 again:
354 if (!cached) {
355 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
356 memset(req[1], 0, strlen(req[1]));
359 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
360 sizeof(file_header.iv)))) {
361 gcry_free(inbuf);
362 memset(shakey, 0, sizeof(shakey));
363 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
364 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
365 return FALSE;
368 if ((gcryerrno = gcry_cipher_setkey(client->gh, shakey, gcrykeysize))) {
369 gcry_free(inbuf);
370 memset(shakey, 0, sizeof(shakey));
371 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
372 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
373 return FALSE;
376 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
377 if (cached) {
378 cached = 0;
379 goto again;
382 memset(shakey, 0, sizeof(shakey));
383 gcry_free(inbuf);
384 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
385 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
386 return FALSE;
389 memcpy(tkey, shakey, sizeof(tkey));
390 tkey[0] ^= 1;
392 if ((gcryerrno = gcry_cipher_setkey(client->gh, tkey, gcrykeysize))) {
393 memset(tkey, 0, sizeof(tkey));
394 gcry_free(inbuf);
395 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
396 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
397 return FALSE;
400 iter = file_header.iter;
402 while (iter-- > 0) {
403 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
404 sizeof(file_header.iv)))) {
405 memset(tkey, 0, sizeof(tkey));
406 gcry_free(inbuf);
407 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
408 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
409 return FALSE;
412 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
413 if (cached) {
414 cached = 0;
415 goto again;
418 memset(tkey, 0, sizeof(tkey));
419 gcry_free(inbuf);
420 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
421 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
422 return FALSE;
426 memset(tkey, 0, sizeof(tkey));
427 client->xml = inbuf;
428 client->len = insize;
430 if (g_strncasecmp(client->xml, "<?xml version=\"1.0\"?>", 21) != 0) {
431 send_error(client, EPWMD_BADKEY);
432 return FALSE;
435 if (!cached) {
436 if (cache_add_file(client->md5file, shakey) == FALSE) {
437 memset(shakey, 0, sizeof(shakey));
438 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
439 send_error(client, EPWMD_MAX_SLOTS);
440 return FALSE;
444 memset(shakey, 0, sizeof(shakey));
445 client->filename = g_strdup(filename);
447 if (!cached) {
448 timeout = get_key_file_integer(client->filename, "cache_timeout");
449 cache_set_timeout(client->md5file, timeout);
452 return parse_xml(client);
456 * client->reader should be at the position in the document where the element
457 * search should start. It won't search past the closing account element.
459 static gboolean find_elements(struct client_s *client, xmlTextReaderPtr reader,
460 gchar **req, gint quiet)
462 gint i;
464 if (!req || !req[0]) {
465 if (!quiet)
466 send_error(client, EPWMD_COMMAND_SYNTAX);
467 return FALSE;
470 for (i = 0; req[i]; i++) {
471 if (find_element(reader, req[i], req[i+1] != NULL) == FALSE) {
472 if (!quiet)
473 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
475 return FALSE;
479 return TRUE;
482 gboolean do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
483 const gchar *filename, const xmlChar *data, size_t insize,
484 const guchar *shakey, guint iter)
486 gsize len = insize;
487 gint fd;
488 void *inbuf;
489 guchar tkey[gcrykeysize];
490 gchar *p;
491 gint error;
492 struct file_header_s {
493 guint iter;
494 guchar iv[gcryblocksize];
495 } file_header;
497 if (insize / gcryblocksize) {
498 len = (insize / gcryblocksize) * gcryblocksize;
500 if (insize % gcryblocksize)
501 len += gcryblocksize;
504 inbuf = gcry_calloc(1, len);
505 memcpy(inbuf, data, insize);
506 insize = len;
507 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
508 memcpy(tkey, shakey, sizeof(tkey));
509 tkey[0] ^= 1;
511 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
512 gcry_free(inbuf);
513 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
514 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
515 return FALSE;
518 file_header.iter = iter;
520 while (iter-- > 0) {
521 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
522 sizeof(file_header.iv)))) {
523 gcry_free(inbuf);
524 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
525 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
526 return FALSE;
529 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
530 == FALSE) {
531 gcry_free(inbuf);
532 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
533 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
534 return FALSE;
538 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
539 sizeof(file_header.iv)))) {
540 gcry_free(inbuf);
541 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
542 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
543 return FALSE;
546 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
547 gcry_free(inbuf);
548 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
549 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
550 return FALSE;
553 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
554 == FALSE) {
555 gcry_free(inbuf);
556 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
557 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
558 return FALSE;
561 if (filename) {
562 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
563 error = errno;
564 gcry_free(inbuf);
565 p = strrchr(filename, '/');
566 p++;
567 log_write("%s: %s", p, strerror(errno));
568 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, p, strerror(error));
569 return FALSE;
572 else
574 * xml_import() from command line.
576 fd = STDOUT_FILENO;
578 write(fd, &file_header, sizeof(struct file_header_s));
579 write(fd, inbuf, insize);
581 if (filename)
582 close(fd);
584 gcry_free(inbuf);
585 return TRUE;
588 gboolean save_command(struct client_s *client, const gchar *filename, gchar *key)
590 xmlChar *xmlbuf;
591 gint len;
592 gint update = 1;
593 guchar shakey[gcrykeysize];
594 gint iter;
595 struct stat st;
596 gchar filebuf[PATH_MAX], *p, *p2;
597 gboolean reset_timeout;
599 p = get_key_file_string("default", "data_directory");
600 p2 = expand_homedir(p);
601 g_free(p);
602 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
603 g_free(p2);
605 if (stat(filebuf, &st) == 0 && client->mtime) {
606 if (client->mtime != st.st_mtime) {
607 log_write("%s: %s", filename, pwmd_strerror(EPWMD_FILE_MODIFIED));
608 send_error(client, EPWMD_FILE_MODIFIED);
609 return FALSE;
613 if (!key || !key) {
614 if (cache_get_key(client->md5file, shakey) == FALSE) {
615 send_error(client, EPWMD_KEY);
616 return FALSE;
619 update = 0;
621 else {
622 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
623 memset(key, 0, strlen(key));
626 xmlDocDumpFormatMemory(client->doc, &xmlbuf, &len, 0);
628 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
629 iter = 0;
631 if (do_xml_encrypt(client, client->gh, filebuf, xmlbuf, len, shakey,
632 iter) == FALSE) {
633 memset(shakey, 0, sizeof(shakey));
634 xmlFree(xmlbuf);
635 return FALSE;
638 xmlFree(xmlbuf);
639 stat(filebuf, &st);
640 client->mtime = st.st_mtime;
642 if (!update) {
643 memset(shakey, 0, sizeof(shakey));
645 if ((reset_timeout = get_key_file_boolean(client->filename,
646 "cache_reset_timeout")) == TRUE)
647 cache_reset_timeout(client->md5file);
649 return TRUE;
652 if (cache_update_key(client->md5file, shakey) == FALSE) {
653 memset(shakey, 0, sizeof(shakey));
654 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
655 send_error(client, EPWMD_MAX_SLOTS);
656 return FALSE;
659 memset(shakey, 0, sizeof(shakey));
660 cache_reset_timeout(client->md5file);
661 return TRUE;
664 static gboolean contains_whitespace(const gchar *str)
666 const gchar *p = str;
668 for (; *p; p++) {
669 if (g_ascii_isspace(*p) == TRUE) {
670 return TRUE;
674 return FALSE;
678 * the 'reader' should be at the element of the document where the elements
679 * 'req' are to be created.
681 static gboolean create_elements(struct client_s *client, xmlTextReaderPtr reader,
682 gchar **req, gint novalue)
684 gint i;
685 gboolean ret = TRUE;
686 xmlNodePtr r;
688 r = xmlTextReaderCurrentNode(reader);
690 if (xmlTextReaderDepth(reader) > 1)
691 r = r->parent;
693 for (i = 0; req[i]; i++) {
694 xmlNodePtr n;
697 * Whitespace is allowed in values or attributes but not in element
698 * names.
700 if (req[i+1] && valid_xml_element((xmlChar *)req[i]) == FALSE) {
701 send_error(client, EPWMD_INVALID_ELEMENT);
702 return FALSE;
706 * The value of the element tree.
708 if (!req[i+1] && !novalue) {
710 * Prevent creating 'text' elements in the root of the account.
712 if (i < 1) {
713 send_error(client, EPWMD_ROOT_TEXT_ELEMENT);
714 return FALSE;
718 * FIXME ?? overwriting an element tree with a text element:
720 * STORE account element element2 value
721 * STORE account element value
723 * would remove the existing element tree. This may be a bug or
724 * feature.
726 xmlNodeSetContent(r, (xmlChar *)req[i]);
727 break;
730 if ((n = find_node(r, (xmlChar *)req[i])) == NULL) {
731 n = xmlNewNode(NULL, (xmlChar *)req[i]);
732 r = xmlAddChild(r, n);
734 else
735 r = n;
737 if (!req[i+1] && novalue)
738 return TRUE;
741 return ret;
745 * FIXME reuse reader handle
747 static gboolean reset_reader(xmlDocPtr doc, xmlTextReaderPtr *reader)
749 if (reader) {
750 xmlFreeTextReader(*reader);
751 *reader = NULL;
754 if ((*reader = xmlReaderWalker(doc)) == NULL)
755 return FALSE;
757 return TRUE;
760 static void delete_node(xmlNodePtr n)
762 xmlUnlinkNode(n);
763 xmlFreeNode(n);
766 gboolean delete_command(struct client_s *client, gchar **req)
768 gint i = 1;
769 xmlNodePtr n;
770 xmlErrorPtr xml_error;
772 if (reset_reader(client->doc, &client->reader) == FALSE) {
773 xml_error = xmlGetLastError();
774 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
775 send_error(client, EPWMD_LIBXML_ERROR);
776 return FALSE;
779 if (find_account(client->reader, req[0]) == FALSE) {
780 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
781 return FALSE;
784 n = xmlTextReaderCurrentNode(client->reader);
787 * No sub-node defined. Remove the entire node (account).
789 if (!req[i]) {
790 if (n)
791 delete_node(n);
792 return TRUE;
796 * Remove matching sub-nodes starting from the root of the account.
798 while (req[i] && find_element(client->reader, req[i++], req[i] != NULL) == TRUE)
799 n = xmlTextReaderCurrentNode(client->reader);
801 if (n && xmlStrcmp(n->name, (xmlChar *)req[i-1]) == 0 &&
802 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_ELEMENT)
803 delete_node(n);
804 else {
805 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
806 return FALSE;
809 return TRUE;
812 gboolean store_command(struct client_s *client, gchar **req)
814 xmlErrorPtr xml_error;
816 again:
817 if (reset_reader(client->doc, &client->reader) == FALSE) {
818 xml_error = xmlGetLastError();
819 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
820 send_error(client, EPWMD_LIBXML_ERROR);
821 return FALSE;
824 if (!req[0]) {
825 send_error(client, EPWMD_COMMAND_SYNTAX);
826 return FALSE;
829 if (find_account(client->reader, req[0]) == FALSE) {
830 if (contains_whitespace(req[0]) == TRUE) {
831 send_error(client, EPWMD_INVALID_ELEMENT);
832 return FALSE;
835 if (new_account(client->doc, req[0]) == FALSE) {
836 send_error(client, EPWMD_ERROR);
837 return FALSE;
840 goto again;
843 xmlTextReaderNext(client->reader);
844 return create_elements(client, client->reader, req+1, 0);
847 static gboolean do_get_command(struct client_s *client, xmlTextReaderPtr *reader,
848 gchar **req, xmlChar **content, gint quiet, gint list)
850 xmlNodePtr n;
851 xmlAttrPtr a;
852 gchar **nreq;
853 gboolean ret;
854 xmlChar *p;
855 xmlErrorPtr xml_error;
857 if (reset_reader(client->doc, reader) == FALSE) {
858 if (!quiet) {
859 xml_error = xmlGetLastError();
860 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
861 send_error(client, EPWMD_LIBXML_ERROR);
864 return FALSE;
867 if (find_account(*reader, req[0]) == FALSE) {
868 if (!quiet && !list)
869 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
870 return FALSE;
874 * May be an account with only a TARGET attribute.
876 if (req[1]) {
877 if (find_elements(client, *reader, req + 1, (list) ? 1 : quiet) == FALSE)
878 return FALSE;
881 if ((n = xmlTextReaderCurrentNode(*reader)) == NULL) {
882 if (!quiet) {
883 xml_error = xmlGetLastError();
884 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
885 send_error(client, EPWMD_LIBXML_ERROR);
888 return FALSE;
892 * If the current element has a TARGET attribute, the value of the
893 * attribute is an element path somewhere else in the document. Use this
894 * value and not any TEXT element value. The value may be another element
895 * with a TARGET attribute and this function will recurse until a non-TARGET
896 * attribute in the element is found.
898 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
899 if ((p = xmlNodeGetContent(a->children)) != NULL) {
900 if (strchr((gchar *)p, '\t') != NULL) {
901 if ((nreq = split_input_line((gchar *)p, "\t", 0)) == NULL) {
902 xmlFree(p);
904 if (!quiet)
905 send_error(client, EPWMD_INVALID_ELEMENT);
906 return FALSE;
909 else {
910 if ((nreq = split_input_line((gchar *)p, " ", 0)) == NULL) {
911 xmlFree(p);
913 if (!quiet)
914 send_error(client, EPWMD_INVALID_ELEMENT);
915 return FALSE;
919 xmlFree(p);
920 ret = do_get_command(client, reader, nreq, content, quiet, list);
921 g_strfreev(nreq);
922 return ret;
926 switch (xmlTextReaderNext(*reader)) {
927 case -1:
928 if (!quiet) {
929 xml_error = xmlGetLastError();
930 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
931 send_error(client, EPWMD_LIBXML_ERROR);
934 return FALSE;
935 case 0:
936 if (!quiet)
937 send_error(client, EPWMD_EMPTY_ELEMENT);
938 return FALSE;
939 default:
940 break;
943 switch (xmlTextReaderNodeType(*reader)) {
944 case XML_READER_TYPE_END_ELEMENT:
946 * May be an empty element after an ATTR DELETE TARGET command.
948 return TRUE;
949 case XML_READER_TYPE_TEXT:
950 break;
951 case -1:
952 if (!quiet) {
953 xml_error = xmlGetLastError();
954 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
955 send_error(client, EPWMD_LIBXML_ERROR);
958 return FALSE;
959 default:
960 if (!quiet) {
961 if (n->children && !list)
962 send_error(client, EPWMD_TRAILING_ELEMENT);
963 else if (!n->children && !list)
964 send_error(client, EPWMD_INVALID_ELEMENT);
966 return FALSE;
969 if ((*content = xmlNodeGetContent(n)) == NULL) {
970 if (!quiet)
971 send_error(client, EPWMD_EMPTY_ELEMENT);
972 return FALSE;
975 return TRUE;
979 * Retrieves the value associated with the element tree 'req'.
981 gboolean get_command(struct client_s *client, xmlTextReaderPtr *reader,
982 gchar **req, gint quiet)
984 xmlChar *content = NULL;
986 if (do_get_command(client, reader, req, &content, quiet, 0) == FALSE)
987 return FALSE;
989 if (!content) {
990 if (!quiet)
991 send_error(client, EPWMD_EMPTY_ELEMENT);
993 return FALSE;
996 send_to_client(client, "BEGIN %li\n%s\nOK \n", xmlStrlen(content), content);
997 xmlFree(content);
998 return TRUE;
1001 #ifdef DEBUG
1002 static gchar *element_path_to_req(const gchar *account, xmlChar *path,
1003 const xmlChar *content)
1004 #else
1005 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1006 #endif
1008 xmlChar *p = path;
1009 gint n;
1010 gchar *buf;
1012 if (!p)
1013 return NULL;
1015 for (n = 0; *p && n < 3; p++) {
1016 if (*p == '/')
1017 n++;
1020 if (strstr((gchar *)p, "text()") != NULL)
1021 p[xmlStrlen(p) - 7] = 0;
1023 for (n = 0; p[n]; n++) {
1024 if (p[n] == '/')
1025 p[n] = '\t';
1028 #ifdef DEBUG
1029 buf = g_strdup_printf("%s\t%s\t%s", account, p, content);
1030 #else
1031 buf = g_strdup_printf("%s\t%s", account, p);
1032 #endif
1033 return buf;
1036 static gboolean append_to_array(gchar ***array, gint *total, const gchar *str)
1038 gchar **a;
1039 gint t = *total;
1041 if ((a = g_realloc(*array, (t + 2) * sizeof(gchar *))) == NULL)
1042 return FALSE;
1044 a[t++] = g_strdup(str);
1045 a[t] = NULL;
1046 *total = t;
1047 *array = a;
1048 return TRUE;
1051 gboolean list_command(struct client_s *client, gchar *str)
1053 gchar *dst = NULL;
1054 gchar *p = str;
1055 gchar **elements = NULL;
1056 gint pwmd_errno = -1;
1057 gchar *account;
1058 gint total = 0;
1059 xmlChar *path = NULL;
1060 xmlAttrPtr a;
1061 xmlNodePtr n;
1062 xmlChar *content;
1063 xmlTextReaderPtr r = NULL;
1064 gchar **req = NULL, **nreq;
1065 gboolean ret;
1066 gint type;
1067 gchar *line;
1068 xmlErrorPtr xml_error;
1069 gint depth = 0;
1071 if (reset_reader(client->doc, &client->reader) == FALSE) {
1072 xml_error = xmlGetLastError();
1073 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1074 send_error(client, EPWMD_LIBXML_ERROR);
1075 return FALSE;
1078 if (strchr(p, ' ') == NULL) {
1079 list_only:
1080 if (list_accounts(client->reader, &dst, &pwmd_errno) == FALSE) {
1081 send_error(client, pwmd_errno);
1082 return FALSE;
1084 else {
1085 send_to_client(client, "BEGIN %i\n%s\nOK \n",
1086 g_utf8_strlen(dst, -1), dst);
1087 memset(dst, 0, strlen(dst));
1088 g_free(dst);
1091 return TRUE;
1094 p = str + 5;
1096 while (*p && isspace(*p))
1097 p++;
1099 if (!*p)
1100 goto list_only;
1102 if (strchr(p, '\t') != NULL) {
1103 if ((req = split_input_line(p, "\t", 0)) == NULL) {
1104 send_error(client, EPWMD_COMMAND_SYNTAX);
1105 return FALSE;
1108 if (find_account(client->reader, req[0]) == FALSE) {
1109 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1110 return FALSE;
1113 if (find_elements(client, client->reader, req + 1, 0) == FALSE) {
1114 g_strfreev(req);
1115 return FALSE;
1118 depth = xmlTextReaderDepth(client->reader);
1120 else {
1121 if (find_account(client->reader, p) == FALSE) {
1122 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1123 return FALSE;
1127 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1128 xml_error = xmlGetLastError();
1129 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1130 send_error(client, EPWMD_LIBXML_ERROR);
1131 return FALSE;
1134 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
1135 if (reset_reader(client->doc, &client->reader) == FALSE) {
1136 send_error(client, EPWMD_LIBXML_ERROR);
1137 return FALSE;
1140 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1141 if (find_account(client->reader, (gchar *)content) == FALSE) {
1142 xmlFree(content);
1143 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1144 return FALSE;
1147 xmlFree(content);
1151 account = (req) ? g_strdup(req[0]) : g_strdup(p);
1153 if (req)
1154 g_strfreev(req);
1156 while (xmlTextReaderNext(client->reader) == 1) {
1157 again:
1158 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1159 xml_error = xmlGetLastError();
1160 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1161 send_error(client, EPWMD_LIBXML_ERROR);
1162 return FALSE;
1165 if (xmlTextReaderDepth(client->reader) == 1 &&
1166 xmlStrcmp(n->name, (xmlChar *)"account") == 0 &&
1167 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1168 break;
1170 if (depth && depth == xmlTextReaderDepth(client->reader) &&
1171 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1172 break;
1175 * If the current element has a TARGET attribute, the value of the
1176 * attribute is an element path somewhere else in the document. Use this
1177 * value and not any TEXT element value.
1179 type = xmlTextReaderNodeType(client->reader);
1180 a = xmlHasProp(n, (xmlChar *)"target");
1182 if (type == XML_READER_TYPE_ELEMENT && a) {
1183 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1184 path = xmlGetNodePath(n);
1186 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
1187 if (elements)
1188 g_strfreev(elements);
1190 xmlFree(path);
1191 xmlFree(content);
1192 send_error(client, EPWMD_INVALID_ELEMENT);
1193 return FALSE;
1196 xmlFree(content);
1197 r = NULL;
1199 if ((ret = do_get_command(client, &r, nreq, &content, 0, 1)) == TRUE) {
1200 if (content && *content) {
1201 #ifdef DEBUG
1202 line = element_path_to_req(account, path, content);
1203 #else
1204 line = element_path_to_req(account, path);
1205 #endif
1206 xmlFree(content);
1208 if (append_to_array(&elements, &total, line) == FALSE) {
1209 if (elements)
1210 g_strfreev(elements);
1212 xmlFree(path);
1213 memset(line, 0, g_utf8_strlen(line, -1));
1214 g_free(line);
1215 g_strfreev(nreq);
1216 xmlFreeTextReader(r);
1217 send_error(client, EPWMD_ERROR);
1218 return FALSE;
1221 memset(line, 0, g_utf8_strlen(line, -1));
1222 g_free(line);
1225 if (xmlTextReaderNext(client->reader) == 1) {
1226 if (xmlTextReaderNodeType(client->reader) !=
1227 XML_READER_TYPE_TEXT) {
1228 g_strfreev(nreq);
1229 xmlFreeTextReader(r);
1230 xmlFree(path);
1231 goto again;
1236 g_strfreev(nreq);
1237 xmlFreeTextReader(r);
1238 xmlFree(path);
1239 continue;
1243 if (type == XML_READER_TYPE_TEXT) {
1244 xmlChar *np = xmlGetNodePath(n);
1246 #ifdef DEBUG
1247 content = xmlNodeGetContent(n);
1248 line = element_path_to_req(account, np, content);
1249 xmlFree(content);
1250 #else
1251 line = element_path_to_req(account, np);
1252 #endif
1253 xmlFree(np);
1254 append_to_array(&elements, &total, line);
1255 memset(line, 0, g_utf8_strlen(line, -1));
1256 g_free(line);
1260 if (!elements) {
1261 send_error(client, EPWMD_EMPTY_ELEMENT);
1262 g_free(account);
1263 return FALSE;
1266 g_free(account);
1267 line = g_strjoinv("\n", elements);
1268 send_to_client(client, "BEGIN %li\n%s\nOK \n",
1269 g_utf8_strlen(line, -1), line);
1270 g_strfreev(elements);
1271 g_free(line);
1272 return TRUE;
1276 * The client->reader handle should be at the element in the document where
1277 * the attribute will be created or modified.
1279 static gboolean add_attribute(struct client_s *client, const gchar *name, const gchar *value)
1281 xmlAttrPtr a;
1282 xmlNodePtr n;
1283 xmlErrorPtr xml_error;
1285 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1286 xml_error = xmlGetLastError();
1287 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1288 send_error(client, EPWMD_LIBXML_ERROR);
1289 return FALSE;
1292 if ((a = xmlHasProp(n, (xmlChar *)name)) == NULL)
1293 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)value);
1294 else
1295 xmlNodeSetContent(a->children, (xmlChar *)value);
1297 return TRUE;
1301 * req[0] - element path
1303 static gboolean attribute_list(struct client_s *client, gchar **req)
1305 gchar **attrlist = NULL;
1306 gint i = 0;
1307 gchar **epath = NULL;
1308 xmlAttrPtr a;
1309 xmlNodePtr n, an;
1310 gchar *line;
1311 xmlErrorPtr xml_error;
1313 if (!req || !req[0]) {
1314 send_error(client, EPWMD_COMMAND_SYNTAX);
1315 return FALSE;
1318 if ((epath = split_input_line(req[0], "\t", 0)) == NULL) {
1320 * The first argument may be only an account.
1322 if ((epath = split_input_line(req[0], " ", 0)) == NULL) {
1323 send_error(client, EPWMD_COMMAND_SYNTAX);
1324 return FALSE;
1328 if (reset_reader(client->doc, &client->reader) == FALSE) {
1329 xml_error = xmlGetLastError();
1330 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1331 send_error(client, EPWMD_LIBXML_ERROR);
1332 goto blah;
1335 if (find_account(client->reader, epath[0]) == FALSE) {
1336 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1337 goto blah;
1340 if (epath[1]) {
1341 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1342 goto blah;
1345 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1346 xml_error = xmlGetLastError();
1347 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1348 send_error(client, EPWMD_LIBXML_ERROR);
1349 goto blah;
1352 for (a = n->properties; a; a = a->next) {
1353 if ((attrlist = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1354 log_write("%s(%i): g_realloc() failed", __FILE__, __LINE__);
1355 send_error(client, EPWMD_ERROR);
1356 goto blah;
1359 an = a->children;
1360 attrlist[i++] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1361 attrlist[i] = NULL;
1364 if (!attrlist) {
1365 send_error(client, EPWMD_EMPTY_ELEMENT);
1366 goto blah;
1369 line = g_strjoinv("\n", attrlist);
1370 send_to_client(client, "BEGIN %li\n%s\n", g_utf8_strlen(line, -1), line);
1371 g_free(line);
1372 g_strfreev(epath);
1373 g_strfreev(attrlist);
1374 return TRUE;
1375 blah:
1376 g_strfreev(epath);
1377 return FALSE;
1381 * req[0] - attribute
1382 * req[1] - element path
1384 static gboolean attribute_delete(struct client_s *client, gchar **req)
1386 xmlAttrPtr a;
1387 xmlNodePtr n;
1388 gchar **epath = NULL;
1389 xmlErrorPtr xml_error;
1391 if (!req || !req[0] || !req[1]) {
1392 send_error(client, EPWMD_COMMAND_SYNTAX);
1393 return FALSE;
1396 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1398 * The first argument may be only an account.
1400 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1401 send_error(client, EPWMD_COMMAND_SYNTAX);
1402 return FALSE;
1407 * Don't remove the NAME attribute for the account element.
1409 if (!epath[1] && g_ascii_strcasecmp(req[0], "NAME") == 0) {
1410 send_error(client, EPWMD_ATTR_SYNTAX);
1411 goto blah;
1414 if (reset_reader(client->doc, &client->reader) == FALSE) {
1415 xml_error = xmlGetLastError();
1416 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1417 send_error(client, EPWMD_LIBXML_ERROR);
1418 goto blah;
1421 if (find_account(client->reader, epath[0]) == FALSE) {
1422 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1423 goto blah;
1426 if (epath[1]) {
1427 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1428 goto blah;
1431 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1432 xml_error = xmlGetLastError();
1433 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1434 send_error(client, EPWMD_LIBXML_ERROR);
1435 goto blah;
1438 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL) {
1439 send_error(client, EPWMD_ATTR_NOT_FOUND);
1440 goto blah;
1443 if (xmlRemoveProp(a) == -1) {
1444 xml_error = xmlGetLastError();
1445 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1446 send_error(client, EPWMD_LIBXML_ERROR);
1447 goto blah;
1450 g_strfreev(epath);
1451 return TRUE;
1452 blah:
1453 g_strfreev(epath);
1454 return FALSE;
1458 * req[0] - source element path
1459 * req[1] - destination element path
1461 static gboolean target_attribute(struct client_s *client, gchar **req)
1463 gchar **src, **dst, *line;
1464 xmlErrorPtr xml_error;
1466 if (!req || !req[0] || !req[1]) {
1467 send_error(client, EPWMD_COMMAND_SYNTAX);
1468 return FALSE;
1471 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1473 * The first argument may be only an account.
1475 if ((src = split_input_line(req[0], " ", 0)) == NULL) {
1476 send_error(client, EPWMD_COMMAND_SYNTAX);
1477 return FALSE;
1481 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1483 * The first argument may be only an account.
1485 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1486 send_error(client, EPWMD_COMMAND_SYNTAX);
1487 g_strfreev(src);
1488 goto blah;
1493 * Prevent an element tree pointing to only and account. Accounts require
1494 * at least one element. Accounts pointing to accounts are allowed.
1496 if ((!src[1] && dst[1]) || (!dst[1] && src[1])) {
1497 send_error(client, EPWMD_ATTR_SYNTAX);
1498 g_strfreev(src);
1499 g_strfreev(dst);
1500 goto blah;
1503 if (reset_reader(client->doc, &client->reader) == FALSE) {
1504 xml_error = xmlGetLastError();
1505 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1506 send_error(client, EPWMD_LIBXML_ERROR);
1507 g_strfreev(src);
1508 g_strfreev(dst);
1509 goto blah;
1513 * Make sure the destination element path exists.
1515 if (find_account(client->reader, dst[0]) == FALSE) {
1516 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1517 g_strfreev(src);
1518 g_strfreev(dst);
1519 goto blah;
1522 if (dst[1]) {
1523 if (find_elements(client, client->reader, dst+1, 0) == FALSE) {
1524 g_strfreev(src);
1525 g_strfreev(dst);
1526 goto blah;
1530 if (reset_reader(client->doc, &client->reader) == FALSE) {
1531 xml_error = xmlGetLastError();
1532 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1533 send_error(client, EPWMD_LIBXML_ERROR);
1534 g_strfreev(src);
1535 g_strfreev(dst);
1536 goto blah;
1540 * If the source element tree doesn't exist, create it.
1542 if (find_account(client->reader, src[0]) == FALSE) {
1543 if (new_account(client->doc, src[0]) == FALSE) {
1544 xml_error = xmlGetLastError();
1545 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1546 send_error(client, EPWMD_LIBXML_ERROR);
1547 g_strfreev(src);
1548 g_strfreev(dst);
1549 goto blah;
1552 if (reset_reader(client->doc, &client->reader) == FALSE) {
1553 xml_error = xmlGetLastError();
1554 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1555 send_error(client, EPWMD_LIBXML_ERROR);
1556 g_strfreev(src);
1557 g_strfreev(dst);
1558 goto blah;
1561 if (find_account(client->reader, src[0]) == FALSE) {
1562 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1563 g_strfreev(src);
1564 g_strfreev(dst);
1565 goto blah;
1569 if (src[1]) {
1570 if (find_elements(client, client->reader, src+1, 1) == FALSE) {
1571 if (reset_reader(client->doc, &client->reader) == FALSE) {
1572 xml_error = xmlGetLastError();
1573 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1574 send_error(client, EPWMD_LIBXML_ERROR);
1575 g_strfreev(src);
1576 g_strfreev(dst);
1577 goto blah;
1580 if (find_account(client->reader, src[0]) == FALSE) {
1581 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1582 g_strfreev(src);
1583 g_strfreev(dst);
1584 goto blah;
1587 xmlTextReaderNext(client->reader);
1589 if (create_elements(client, client->reader, src+1, 1) == FALSE) {
1590 g_strfreev(src);
1591 g_strfreev(dst);
1592 goto blah;
1595 if (reset_reader(client->doc, &client->reader) == FALSE) {
1596 xml_error = xmlGetLastError();
1597 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1598 send_error(client, EPWMD_LIBXML_ERROR);
1599 g_strfreev(src);
1600 g_strfreev(dst);
1601 goto blah;
1604 if (find_account(client->reader, src[0]) == FALSE) {
1605 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1606 g_strfreev(src);
1607 g_strfreev(dst);
1608 goto blah;
1611 if (find_elements(client, client->reader, src+1, 0) == FALSE) {
1612 g_strfreev(src);
1613 g_strfreev(dst);
1614 goto blah;
1619 line = g_strjoinv("\t", dst);
1621 if (add_attribute(client, "target", line) == FALSE) {
1622 g_free(line);
1623 g_strfreev(src);
1624 g_strfreev(dst);
1625 goto blah;
1628 g_strfreev(src);
1629 g_strfreev(dst);
1630 g_free(line);
1631 return TRUE;
1632 blah:
1633 g_strfreev(src);
1634 g_strfreev(dst);
1635 return FALSE;
1639 * req[0] - account name
1640 * req[1] - new name
1642 static gboolean name_attribute(struct client_s *client, gchar **req)
1644 xmlErrorPtr xml_error;
1646 if (reset_reader(client->doc, &client->reader) == FALSE) {
1647 xml_error = xmlGetLastError();
1648 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1649 send_error(client, EPWMD_LIBXML_ERROR);
1650 return FALSE;
1653 if (find_account(client->reader, req[0]) == FALSE) {
1654 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1655 return FALSE;
1658 if (strcmp(req[0], req[1]) == 0)
1659 return TRUE;
1661 if (reset_reader(client->doc, &client->reader) == FALSE) {
1662 xml_error = xmlGetLastError();
1663 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1664 send_error(client, EPWMD_LIBXML_ERROR);
1665 return FALSE;
1669 * Will not overwrite an existing account.
1671 if (find_account(client->reader, req[1]) == TRUE) {
1672 send_error(client, EPWMD_ACCOUNT_EXISTS);
1673 return FALSE;
1676 if (reset_reader(client->doc, &client->reader) == FALSE) {
1677 xml_error = xmlGetLastError();
1678 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1679 send_error(client, EPWMD_LIBXML_ERROR);
1680 return FALSE;
1683 if (find_account(client->reader, req[0]) == FALSE) {
1684 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1685 return FALSE;
1689 * Whitespace not allowed in account names.
1691 if (contains_whitespace(req[1]) == TRUE) {
1692 send_error(client, EPWMD_ATTR_SYNTAX);
1693 return FALSE;
1696 return add_attribute(client, "name", req[1]);
1700 * req[0] - attribute
1701 * req[1] - element path
1703 * If the element has a "target" attribute it won't be "followed".
1705 static gboolean attribute_get(struct client_s *client, gchar **req)
1707 xmlNodePtr n;
1708 xmlChar *a;
1709 gchar **nreq = NULL;
1710 xmlErrorPtr xml_error;
1712 if (!req || !req[0] || !req[1]) {
1713 send_error(client, EPWMD_COMMAND_SYNTAX);
1714 return FALSE;
1717 if (strchr(req[1], '\t')) {
1718 if ((nreq = split_input_line(req[1], "\t", 0)) == NULL) {
1719 send_error(client, EPWMD_COMMAND_SYNTAX);
1720 return FALSE;
1723 else {
1724 if ((nreq = split_input_line(req[1], " ", 0)) == NULL) {
1725 send_error(client, EPWMD_COMMAND_SYNTAX);
1726 return FALSE;
1730 if (reset_reader(client->doc, &client->reader) == FALSE) {
1731 xml_error = xmlGetLastError();
1732 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1733 g_strfreev(nreq);
1734 send_error(client, EPWMD_LIBXML_ERROR);
1735 return FALSE;
1738 if (find_account(client->reader, nreq[0]) == FALSE) {
1739 g_strfreev(nreq);
1740 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1741 return FALSE;
1744 if (nreq[1]) {
1745 if (find_elements(client, client->reader, nreq + 1, 0) == FALSE) {
1746 g_strfreev(nreq);
1747 return FALSE;
1751 g_strfreev(nreq);
1753 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1754 xml_error = xmlGetLastError();
1755 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1756 send_error(client, EPWMD_LIBXML_ERROR);
1757 return FALSE;
1760 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL) {
1761 send_error(client, EPWMD_ATTR_NOT_FOUND);
1762 return FALSE;
1765 send_to_client(client, "BEGIN %li\n%s\n", xmlStrlen(a), a);
1766 xmlFree(a);
1767 return TRUE;
1771 * req[0] - attribute
1772 * req[1] - element path
1773 * req[2] - value
1775 static gboolean attribute_set(struct client_s *client, gchar **req)
1777 gchar **epath = NULL;
1778 xmlErrorPtr xml_error;
1780 if (!req || !req[0] || !req[1] || !req[2]) {
1781 send_error(client, EPWMD_COMMAND_SYNTAX);
1782 return FALSE;
1786 * Reserved attribute names.
1788 if (g_ascii_strcasecmp(req[0], "NAME") == 0) {
1790 * Only reserved for the account element. Not the rest of the
1791 * document.
1793 if (strchr(req[1], '\t') == NULL)
1794 return name_attribute(client, req + 1);
1796 else if (g_ascii_strcasecmp(req[0], "TARGET") == 0)
1797 return target_attribute(client, req + 1);
1799 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1801 * The first argument may be only an account.
1803 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1804 send_error(client, EPWMD_COMMAND_SYNTAX);
1805 return FALSE;
1809 if (reset_reader(client->doc, &client->reader) == FALSE) {
1810 xml_error = xmlGetLastError();
1811 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1812 send_error(client, EPWMD_LIBXML_ERROR);
1813 goto blah;
1816 if (find_account(client->reader, epath[0]) == FALSE) {
1817 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1818 goto blah;
1821 if (epath[1]) {
1822 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1823 goto blah;
1826 g_strfreev(epath);
1827 return add_attribute(client, req[0], req[2]);
1828 blah:
1829 g_strfreev(epath);
1830 return FALSE;
1834 * req[0] - command
1835 * req[1] - attribute name or element path if command is LIST
1836 * req[2] - element path
1837 * req[2] - element path or value
1839 gboolean attr_command(struct client_s *client, gchar **req)
1841 if (!req || !req[0] || !req[1]) {
1842 send_error(client, EPWMD_COMMAND_SYNTAX);
1843 return FALSE;
1846 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1847 return attribute_set(client, req+1);
1848 if (g_ascii_strcasecmp(req[0], "GET") == 0)
1849 return attribute_get(client, req+1);
1850 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1851 return attribute_delete(client, req+1);
1852 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1853 return attribute_list(client, req+1);
1854 else
1855 send_error(client, EPWMD_COMMAND_SYNTAX);
1857 return FALSE;
1860 gboolean cache_test(const guchar *md5filename, gint reset)
1862 void *p;
1863 file_cache_t f;
1864 glong len;
1866 for (p = shm_data, len = 0; len <= cache_size;) {
1867 memcpy(&f, p, sizeof(file_cache_t));
1869 if (reset == 2) {
1870 memset(&f, 0, sizeof(file_cache_t));
1871 memcpy(p, &f, sizeof(file_cache_t));
1872 p += sizeof(file_cache_t);
1873 len += sizeof(file_cache_t);
1875 if (len + sizeof(file_cache_t) > cache_size)
1876 break;
1877 continue;
1880 if (f.used == TRUE) {
1881 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
1882 if (reset == 1) {
1883 memset(&f, 0, sizeof(file_cache_t));
1884 memcpy(p, &f, sizeof(file_cache_t));
1885 return TRUE;
1888 return cache_valid_key(f.key, sizeof(f.key));
1892 p += sizeof(file_cache_t);
1893 len += sizeof(file_cache_t);
1895 if (len + sizeof(file_cache_t) > cache_size)
1896 break;
1899 return (reset == 2) ? TRUE : FALSE;
1902 static gboolean file_exists(const gchar *filename)
1904 struct stat st;
1905 gchar filebuf[PATH_MAX], *p, *p2;
1907 p = get_key_file_string("default", "data_directory");
1908 p2 = expand_homedir(p);
1909 g_free(p);
1910 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
1911 g_free(p2);
1913 if (access(filebuf, R_OK) == -1)
1914 return FALSE;
1916 stat(filebuf, &st);
1918 if (st.st_size == 0)
1919 return FALSE;
1921 return TRUE;
1924 gboolean cache_command(struct client_s *client, gchar **req)
1926 guchar md5file[16];
1927 glong timeout;
1928 gchar *p;
1930 if (g_ascii_strcasecmp(req[0], "CLEAR") == 0) {
1931 if (!req[1]) {
1932 send_error(client, EPWMD_COMMAND_SYNTAX);
1933 return FALSE;
1936 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1938 if (cache_test(md5file, 1) == FALSE) {
1939 send_error(client, EPWMD_CACHE_NOT_FOUND);
1940 return FALSE;
1943 return TRUE;
1945 else if (g_ascii_strcasecmp(req[0], "CLEARALL") == 0) {
1946 cache_test(client->md5file, 2);
1947 return TRUE;
1949 else if (g_ascii_strcasecmp(req[0], "ISCACHED") == 0) {
1950 if (!req[1]) {
1951 send_error(client, EPWMD_COMMAND_SYNTAX);
1952 return FALSE;
1955 if (file_exists(req[1]) == FALSE) {
1956 send_error(client, EPWMD_FILE_NOT_FOUND);
1957 return FALSE;
1960 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1962 if (cache_test(md5file, 0) == FALSE) {
1963 send_error(client, EPWMD_CACHE_NOT_FOUND);
1964 return FALSE;
1967 return TRUE;
1969 else if (g_ascii_strcasecmp(req[0], "TIMEOUT") == 0) {
1970 if (!req[1] || !req[2]) {
1971 send_error(client, EPWMD_COMMAND_SYNTAX);
1972 return FALSE;
1975 errno = 0;
1976 timeout = strtol(req[1], &p, 10);
1978 if (errno != 0 || *p != 0) {
1979 send_error(client, EPWMD_COMMAND_SYNTAX);
1980 return FALSE;
1983 if (file_exists(req[2]) == FALSE) {
1984 send_error(client, EPWMD_FILE_NOT_FOUND);
1985 return FALSE;
1988 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[2], strlen(req[2]));
1990 if (cache_set_timeout(md5file, timeout) == FALSE) {
1991 send_error(client, EPWMD_CACHE_NOT_FOUND);
1992 return FALSE;
1995 return TRUE;
1997 else
1998 send_error(client, EPWMD_COMMAND_SYNTAX);
2000 return FALSE;
2003 gboolean help_command(struct client_s *client, const gchar *what)
2005 gchar *line;
2007 if (!what || !*what)
2008 line =
2009 "NFO Try 'HELP COMMAND' for command help\n"
2010 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
2011 else if (g_ascii_strcasecmp(what, "GET") == 0)
2012 line =
2013 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
2014 "NFO <account> is the account to work on and <element>\n"
2015 "NFO is the element wanted.\n"
2016 "NFO -\n"
2017 "NFO Example: GET isp <TAB> imap <TAB> port\n"
2018 "NFO GET isp <TAB> username\n";
2019 else if (g_ascii_strcasecmp(what, "QUIT") == 0)
2020 line =
2021 "NFO syntax: QUIT\n"
2022 "NFO close the connection\n";
2023 else if (g_ascii_strcasecmp(what, "DELETE") == 0)
2024 line =
2025 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
2026 else if (g_ascii_strcasecmp(what, "STORE") == 0)
2027 line =
2028 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
2029 "NFO <account> is the account to work on and <element>\n"
2030 "NFO is the element to create or modify\n"
2031 "NFO -\n"
2032 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
2033 "NFO STORE isp <TAB> username <TAB> someuser\n";
2034 else if (g_ascii_strcasecmp(what, "OPEN") == 0)
2035 line =
2036 "NFO syntax: OPEN <filename> [<key>]\n"
2037 "NFO opens a (new) file\n";
2038 else if (g_ascii_strcasecmp(what, "LIST") == 0)
2039 line =
2040 "NFO syntax: LIST [account]\n"
2041 "NFO shows available accounts or account elements\n";
2042 else if (g_ascii_strcasecmp(what, "ATTR") == 0)
2043 line =
2044 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
2045 "NFO ATTR SET NAME account value\n"
2046 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
2047 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
2048 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
2049 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
2050 "NFO ATTR LIST account[<TAB>element[...]]\n";
2051 else if (g_ascii_strcasecmp(what, "SAVE") == 0)
2052 line =
2053 "NFO syntax: SAVE [<key>]\n"
2054 "NFO save any changes to the opened file using <key>\n";
2055 else if (g_ascii_strcasecmp(what, "CACHE") == 0)
2056 line =
2057 "NFO syntax: CACHE [CLEARALL | [CLEAR | ISCACHED <filename>] |\n"
2058 "NFO TIMEOUT <seconds> <filename>]\n"
2059 "NFO tests or clears the cache entry for <filename>\n";
2060 else if (g_ascii_strcasecmp(what, "DUMP") == 0)
2061 line =
2062 "NFO syntax: DUMP\n"
2063 "NFO shows the in memory XML document\n";
2064 else {
2065 send_error(client, EPWMD_COMMAND_SYNTAX);
2066 return FALSE;
2069 send_to_client(client, "%sOK \n", line);
2070 return TRUE;
2073 gboolean dump_command(struct client_s *client)
2075 xmlChar *xml;
2076 gssize len;
2078 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2079 send_to_client(client, "BEGIN %li\n%s", len, xml);
2080 xmlFree(xml);
2081 return TRUE;