Fixed "cache_timeout" with "key" and "key_file".
[pwmd.git] / src / commands.c
blob89883b36f47086b2ce785061a86939c8e80606b3
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 log_write("%s\n", reset_timeout == FALSE?"ack":"ackack");
650 return TRUE;
653 if (cache_update_key(client->md5file, shakey) == FALSE) {
654 memset(shakey, 0, sizeof(shakey));
655 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
656 send_error(client, EPWMD_MAX_SLOTS);
657 return FALSE;
660 memset(shakey, 0, sizeof(shakey));
661 cache_reset_timeout(client->md5file);
662 return TRUE;
665 static gboolean contains_whitespace(const gchar *str)
667 const gchar *p = str;
669 for (; *p; p++) {
670 if (g_ascii_isspace(*p) == TRUE) {
671 return TRUE;
675 return FALSE;
679 * the 'reader' should be at the element of the document where the elements
680 * 'req' are to be created.
682 static gboolean create_elements(struct client_s *client, xmlTextReaderPtr reader,
683 gchar **req, gint novalue)
685 gint i;
686 gboolean ret = TRUE;
687 xmlNodePtr r;
689 r = xmlTextReaderCurrentNode(reader);
691 if (xmlTextReaderDepth(reader) > 1)
692 r = r->parent;
694 for (i = 0; req[i]; i++) {
695 xmlNodePtr n;
698 * Whitespace is allowed in values or attributes but not in element
699 * names.
701 if (req[i+1] && valid_xml_element((xmlChar *)req[i]) == FALSE) {
702 send_error(client, EPWMD_INVALID_ELEMENT);
703 return FALSE;
707 * The value of the element tree.
709 if (!req[i+1] && !novalue) {
711 * Prevent creating 'text' elements in the root of the account.
713 if (i < 1) {
714 send_error(client, EPWMD_ROOT_TEXT_ELEMENT);
715 return FALSE;
719 * FIXME ?? overwriting an element tree with a text element:
721 * STORE account element element2 value
722 * STORE account element value
724 * would remove the existing element tree. This may be a bug or
725 * feature.
727 xmlNodeSetContent(r, (xmlChar *)req[i]);
728 break;
731 if ((n = find_node(r, (xmlChar *)req[i])) == NULL) {
732 n = xmlNewNode(NULL, (xmlChar *)req[i]);
733 r = xmlAddChild(r, n);
735 else
736 r = n;
738 if (!req[i+1] && novalue)
739 return TRUE;
742 return ret;
746 * FIXME reuse reader handle
748 static gboolean reset_reader(xmlDocPtr doc, xmlTextReaderPtr *reader)
750 if (reader) {
751 xmlFreeTextReader(*reader);
752 *reader = NULL;
755 if ((*reader = xmlReaderWalker(doc)) == NULL)
756 return FALSE;
758 return TRUE;
761 static void delete_node(xmlNodePtr n)
763 xmlUnlinkNode(n);
764 xmlFreeNode(n);
767 gboolean delete_command(struct client_s *client, gchar **req)
769 gint i = 1;
770 xmlNodePtr n;
771 xmlErrorPtr xml_error;
773 if (reset_reader(client->doc, &client->reader) == FALSE) {
774 xml_error = xmlGetLastError();
775 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
776 send_error(client, EPWMD_LIBXML_ERROR);
777 return FALSE;
780 if (find_account(client->reader, req[0]) == FALSE) {
781 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
782 return FALSE;
785 n = xmlTextReaderCurrentNode(client->reader);
788 * No sub-node defined. Remove the entire node (account).
790 if (!req[i]) {
791 if (n)
792 delete_node(n);
793 return TRUE;
797 * Remove matching sub-nodes starting from the root of the account.
799 while (req[i] && find_element(client->reader, req[i++], req[i] != NULL) == TRUE)
800 n = xmlTextReaderCurrentNode(client->reader);
802 if (n && xmlStrcmp(n->name, (xmlChar *)req[i-1]) == 0 &&
803 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_ELEMENT)
804 delete_node(n);
805 else {
806 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
807 return FALSE;
810 return TRUE;
813 gboolean store_command(struct client_s *client, gchar **req)
815 xmlErrorPtr xml_error;
817 again:
818 if (reset_reader(client->doc, &client->reader) == FALSE) {
819 xml_error = xmlGetLastError();
820 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
821 send_error(client, EPWMD_LIBXML_ERROR);
822 return FALSE;
825 if (!req[0]) {
826 send_error(client, EPWMD_COMMAND_SYNTAX);
827 return FALSE;
830 if (find_account(client->reader, req[0]) == FALSE) {
831 if (contains_whitespace(req[0]) == TRUE) {
832 send_error(client, EPWMD_INVALID_ELEMENT);
833 return FALSE;
836 if (new_account(client->doc, req[0]) == FALSE) {
837 send_error(client, EPWMD_ERROR);
838 return FALSE;
841 goto again;
844 xmlTextReaderNext(client->reader);
845 return create_elements(client, client->reader, req+1, 0);
848 static gboolean do_get_command(struct client_s *client, xmlTextReaderPtr *reader,
849 gchar **req, xmlChar **content, gint quiet, gint list)
851 xmlNodePtr n;
852 xmlAttrPtr a;
853 gchar **nreq;
854 gboolean ret;
855 xmlChar *p;
856 xmlErrorPtr xml_error;
858 if (reset_reader(client->doc, reader) == FALSE) {
859 if (!quiet) {
860 xml_error = xmlGetLastError();
861 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
862 send_error(client, EPWMD_LIBXML_ERROR);
865 return FALSE;
868 if (find_account(*reader, req[0]) == FALSE) {
869 if (!quiet && !list)
870 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
871 return FALSE;
875 * May be an account with only a TARGET attribute.
877 if (req[1]) {
878 if (find_elements(client, *reader, req + 1, (list) ? 1 : quiet) == FALSE)
879 return FALSE;
882 if ((n = xmlTextReaderCurrentNode(*reader)) == NULL) {
883 if (!quiet) {
884 xml_error = xmlGetLastError();
885 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
886 send_error(client, EPWMD_LIBXML_ERROR);
889 return FALSE;
893 * If the current element has a TARGET attribute, the value of the
894 * attribute is an element path somewhere else in the document. Use this
895 * value and not any TEXT element value. The value may be another element
896 * with a TARGET attribute and this function will recurse until a non-TARGET
897 * attribute in the element is found.
899 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
900 if ((p = xmlNodeGetContent(a->children)) != NULL) {
901 if (strchr((gchar *)p, '\t') != NULL) {
902 if ((nreq = split_input_line((gchar *)p, "\t", 0)) == NULL) {
903 xmlFree(p);
905 if (!quiet)
906 send_error(client, EPWMD_INVALID_ELEMENT);
907 return FALSE;
910 else {
911 if ((nreq = split_input_line((gchar *)p, " ", 0)) == NULL) {
912 xmlFree(p);
914 if (!quiet)
915 send_error(client, EPWMD_INVALID_ELEMENT);
916 return FALSE;
920 xmlFree(p);
921 ret = do_get_command(client, reader, nreq, content, quiet, list);
922 g_strfreev(nreq);
923 return ret;
927 switch (xmlTextReaderNext(*reader)) {
928 case -1:
929 if (!quiet) {
930 xml_error = xmlGetLastError();
931 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
932 send_error(client, EPWMD_LIBXML_ERROR);
935 return FALSE;
936 case 0:
937 if (!quiet)
938 send_error(client, EPWMD_EMPTY_ELEMENT);
939 return FALSE;
940 default:
941 break;
944 switch (xmlTextReaderNodeType(*reader)) {
945 case XML_READER_TYPE_END_ELEMENT:
947 * May be an empty element after an ATTR DELETE TARGET command.
949 return TRUE;
950 case XML_READER_TYPE_TEXT:
951 break;
952 case -1:
953 if (!quiet) {
954 xml_error = xmlGetLastError();
955 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
956 send_error(client, EPWMD_LIBXML_ERROR);
959 return FALSE;
960 default:
961 if (!quiet) {
962 if (n->children && !list)
963 send_error(client, EPWMD_TRAILING_ELEMENT);
964 else if (!n->children && !list)
965 send_error(client, EPWMD_INVALID_ELEMENT);
967 return FALSE;
970 if ((*content = xmlNodeGetContent(n)) == NULL) {
971 if (!quiet)
972 send_error(client, EPWMD_EMPTY_ELEMENT);
973 return FALSE;
976 return TRUE;
980 * Retrieves the value associated with the element tree 'req'.
982 gboolean get_command(struct client_s *client, xmlTextReaderPtr *reader,
983 gchar **req, gint quiet)
985 xmlChar *content = NULL;
987 if (do_get_command(client, reader, req, &content, quiet, 0) == FALSE)
988 return FALSE;
990 if (!content) {
991 if (!quiet)
992 send_error(client, EPWMD_EMPTY_ELEMENT);
994 return FALSE;
997 send_to_client(client, "BEGIN %li\n%s\nOK \n", xmlStrlen(content), content);
998 xmlFree(content);
999 return TRUE;
1002 #ifdef DEBUG
1003 static gchar *element_path_to_req(const gchar *account, xmlChar *path,
1004 const xmlChar *content)
1005 #else
1006 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1007 #endif
1009 xmlChar *p = path;
1010 gint n;
1011 gchar *buf;
1013 if (!p)
1014 return NULL;
1016 for (n = 0; *p && n < 3; p++) {
1017 if (*p == '/')
1018 n++;
1021 if (strstr((gchar *)p, "text()") != NULL)
1022 p[xmlStrlen(p) - 7] = 0;
1024 for (n = 0; p[n]; n++) {
1025 if (p[n] == '/')
1026 p[n] = '\t';
1029 #ifdef DEBUG
1030 buf = g_strdup_printf("%s\t%s\t%s", account, p, content);
1031 #else
1032 buf = g_strdup_printf("%s\t%s", account, p);
1033 #endif
1034 return buf;
1037 static gboolean append_to_array(gchar ***array, gint *total, const gchar *str)
1039 gchar **a;
1040 gint t = *total;
1042 if ((a = g_realloc(*array, (t + 2) * sizeof(gchar *))) == NULL)
1043 return FALSE;
1045 a[t++] = g_strdup(str);
1046 a[t] = NULL;
1047 *total = t;
1048 *array = a;
1049 return TRUE;
1052 gboolean list_command(struct client_s *client, gchar *str)
1054 gchar *dst = NULL;
1055 gchar *p = str;
1056 gchar **elements = NULL;
1057 gint pwmd_errno = -1;
1058 gchar *account;
1059 gint total = 0;
1060 xmlChar *path = NULL;
1061 xmlAttrPtr a;
1062 xmlNodePtr n;
1063 xmlChar *content;
1064 xmlTextReaderPtr r = NULL;
1065 gchar **req = NULL, **nreq;
1066 gboolean ret;
1067 gint type;
1068 gchar *line;
1069 xmlErrorPtr xml_error;
1070 gint depth = 0;
1072 if (reset_reader(client->doc, &client->reader) == FALSE) {
1073 xml_error = xmlGetLastError();
1074 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1075 send_error(client, EPWMD_LIBXML_ERROR);
1076 return FALSE;
1079 if (strchr(p, ' ') == NULL) {
1080 list_only:
1081 if (list_accounts(client->reader, &dst, &pwmd_errno) == FALSE) {
1082 send_error(client, pwmd_errno);
1083 return FALSE;
1085 else {
1086 send_to_client(client, "BEGIN %i\n%s\nOK \n",
1087 g_utf8_strlen(dst, -1), dst);
1088 memset(dst, 0, strlen(dst));
1089 g_free(dst);
1092 return TRUE;
1095 p = str + 5;
1097 while (*p && isspace(*p))
1098 p++;
1100 if (!*p)
1101 goto list_only;
1103 if (strchr(p, '\t') != NULL) {
1104 if ((req = split_input_line(p, "\t", 0)) == NULL) {
1105 send_error(client, EPWMD_COMMAND_SYNTAX);
1106 return FALSE;
1109 if (find_account(client->reader, req[0]) == FALSE) {
1110 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1111 return FALSE;
1114 if (find_elements(client, client->reader, req + 1, 0) == FALSE) {
1115 g_strfreev(req);
1116 return FALSE;
1119 depth = xmlTextReaderDepth(client->reader);
1121 else {
1122 if (find_account(client->reader, p) == FALSE) {
1123 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1124 return FALSE;
1128 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1129 xml_error = xmlGetLastError();
1130 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1131 send_error(client, EPWMD_LIBXML_ERROR);
1132 return FALSE;
1135 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
1136 if (reset_reader(client->doc, &client->reader) == FALSE) {
1137 send_error(client, EPWMD_LIBXML_ERROR);
1138 return FALSE;
1141 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1142 if (find_account(client->reader, (gchar *)content) == FALSE) {
1143 xmlFree(content);
1144 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1145 return FALSE;
1148 xmlFree(content);
1152 account = (req) ? g_strdup(req[0]) : g_strdup(p);
1154 if (req)
1155 g_strfreev(req);
1157 while (xmlTextReaderNext(client->reader) == 1) {
1158 again:
1159 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1160 xml_error = xmlGetLastError();
1161 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1162 send_error(client, EPWMD_LIBXML_ERROR);
1163 return FALSE;
1166 if (xmlTextReaderDepth(client->reader) == 1 &&
1167 xmlStrcmp(n->name, (xmlChar *)"account") == 0 &&
1168 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1169 break;
1171 if (depth && depth == xmlTextReaderDepth(client->reader) &&
1172 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1173 break;
1176 * If the current element has a TARGET attribute, the value of the
1177 * attribute is an element path somewhere else in the document. Use this
1178 * value and not any TEXT element value.
1180 type = xmlTextReaderNodeType(client->reader);
1181 a = xmlHasProp(n, (xmlChar *)"target");
1183 if (type == XML_READER_TYPE_ELEMENT && a) {
1184 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1185 path = xmlGetNodePath(n);
1187 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
1188 if (elements)
1189 g_strfreev(elements);
1191 xmlFree(path);
1192 xmlFree(content);
1193 send_error(client, EPWMD_INVALID_ELEMENT);
1194 return FALSE;
1197 xmlFree(content);
1198 r = NULL;
1200 if ((ret = do_get_command(client, &r, nreq, &content, 0, 1)) == TRUE) {
1201 if (content && *content) {
1202 #ifdef DEBUG
1203 line = element_path_to_req(account, path, content);
1204 #else
1205 line = element_path_to_req(account, path);
1206 #endif
1207 xmlFree(content);
1209 if (append_to_array(&elements, &total, line) == FALSE) {
1210 if (elements)
1211 g_strfreev(elements);
1213 xmlFree(path);
1214 memset(line, 0, g_utf8_strlen(line, -1));
1215 g_free(line);
1216 g_strfreev(nreq);
1217 xmlFreeTextReader(r);
1218 send_error(client, EPWMD_ERROR);
1219 return FALSE;
1222 memset(line, 0, g_utf8_strlen(line, -1));
1223 g_free(line);
1226 if (xmlTextReaderNext(client->reader) == 1) {
1227 if (xmlTextReaderNodeType(client->reader) !=
1228 XML_READER_TYPE_TEXT) {
1229 g_strfreev(nreq);
1230 xmlFreeTextReader(r);
1231 xmlFree(path);
1232 goto again;
1237 g_strfreev(nreq);
1238 xmlFreeTextReader(r);
1239 xmlFree(path);
1240 continue;
1244 if (type == XML_READER_TYPE_TEXT) {
1245 xmlChar *np = xmlGetNodePath(n);
1247 #ifdef DEBUG
1248 content = xmlNodeGetContent(n);
1249 line = element_path_to_req(account, np, content);
1250 xmlFree(content);
1251 #else
1252 line = element_path_to_req(account, np);
1253 #endif
1254 xmlFree(np);
1255 append_to_array(&elements, &total, line);
1256 memset(line, 0, g_utf8_strlen(line, -1));
1257 g_free(line);
1261 if (!elements) {
1262 send_error(client, EPWMD_EMPTY_ELEMENT);
1263 g_free(account);
1264 return FALSE;
1267 g_free(account);
1268 line = g_strjoinv("\n", elements);
1269 send_to_client(client, "BEGIN %li\n%s\nOK \n",
1270 g_utf8_strlen(line, -1), line);
1271 g_strfreev(elements);
1272 g_free(line);
1273 return TRUE;
1277 * The client->reader handle should be at the element in the document where
1278 * the attribute will be created or modified.
1280 static gboolean add_attribute(struct client_s *client, const gchar *name, const gchar *value)
1282 xmlAttrPtr a;
1283 xmlNodePtr n;
1284 xmlErrorPtr xml_error;
1286 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1287 xml_error = xmlGetLastError();
1288 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1289 send_error(client, EPWMD_LIBXML_ERROR);
1290 return FALSE;
1293 if ((a = xmlHasProp(n, (xmlChar *)name)) == NULL)
1294 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)value);
1295 else
1296 xmlNodeSetContent(a->children, (xmlChar *)value);
1298 return TRUE;
1302 * req[0] - element path
1304 static gboolean attribute_list(struct client_s *client, gchar **req)
1306 gchar **attrlist = NULL;
1307 gint i = 0;
1308 gchar **epath = NULL;
1309 xmlAttrPtr a;
1310 xmlNodePtr n, an;
1311 gchar *line;
1312 xmlErrorPtr xml_error;
1314 if (!req || !req[0]) {
1315 send_error(client, EPWMD_COMMAND_SYNTAX);
1316 return FALSE;
1319 if ((epath = split_input_line(req[0], "\t", 0)) == NULL) {
1321 * The first argument may be only an account.
1323 if ((epath = split_input_line(req[0], " ", 0)) == NULL) {
1324 send_error(client, EPWMD_COMMAND_SYNTAX);
1325 return FALSE;
1329 if (reset_reader(client->doc, &client->reader) == FALSE) {
1330 xml_error = xmlGetLastError();
1331 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1332 send_error(client, EPWMD_LIBXML_ERROR);
1333 goto blah;
1336 if (find_account(client->reader, epath[0]) == FALSE) {
1337 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1338 goto blah;
1341 if (epath[1]) {
1342 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1343 goto blah;
1346 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1347 xml_error = xmlGetLastError();
1348 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1349 send_error(client, EPWMD_LIBXML_ERROR);
1350 goto blah;
1353 for (a = n->properties; a; a = a->next) {
1354 if ((attrlist = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1355 log_write("%s(%i): g_realloc() failed", __FILE__, __LINE__);
1356 send_error(client, EPWMD_ERROR);
1357 goto blah;
1360 an = a->children;
1361 attrlist[i++] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1362 attrlist[i] = NULL;
1365 if (!attrlist) {
1366 send_error(client, EPWMD_EMPTY_ELEMENT);
1367 goto blah;
1370 line = g_strjoinv("\n", attrlist);
1371 send_to_client(client, "BEGIN %li\n%s\n", g_utf8_strlen(line, -1), line);
1372 g_free(line);
1373 g_strfreev(epath);
1374 g_strfreev(attrlist);
1375 return TRUE;
1376 blah:
1377 g_strfreev(epath);
1378 return FALSE;
1382 * req[0] - attribute
1383 * req[1] - element path
1385 static gboolean attribute_delete(struct client_s *client, gchar **req)
1387 xmlAttrPtr a;
1388 xmlNodePtr n;
1389 gchar **epath = NULL;
1390 xmlErrorPtr xml_error;
1392 if (!req || !req[0] || !req[1]) {
1393 send_error(client, EPWMD_COMMAND_SYNTAX);
1394 return FALSE;
1397 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1399 * The first argument may be only an account.
1401 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1402 send_error(client, EPWMD_COMMAND_SYNTAX);
1403 return FALSE;
1408 * Don't remove the NAME attribute for the account element.
1410 if (!epath[1] && g_ascii_strcasecmp(req[0], "NAME") == 0) {
1411 send_error(client, EPWMD_ATTR_SYNTAX);
1412 goto blah;
1415 if (reset_reader(client->doc, &client->reader) == FALSE) {
1416 xml_error = xmlGetLastError();
1417 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1418 send_error(client, EPWMD_LIBXML_ERROR);
1419 goto blah;
1422 if (find_account(client->reader, epath[0]) == FALSE) {
1423 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1424 goto blah;
1427 if (epath[1]) {
1428 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1429 goto blah;
1432 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1433 xml_error = xmlGetLastError();
1434 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1435 send_error(client, EPWMD_LIBXML_ERROR);
1436 goto blah;
1439 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL) {
1440 send_error(client, EPWMD_ATTR_NOT_FOUND);
1441 goto blah;
1444 if (xmlRemoveProp(a) == -1) {
1445 xml_error = xmlGetLastError();
1446 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1447 send_error(client, EPWMD_LIBXML_ERROR);
1448 goto blah;
1451 g_strfreev(epath);
1452 return TRUE;
1453 blah:
1454 g_strfreev(epath);
1455 return FALSE;
1459 * req[0] - source element path
1460 * req[1] - destination element path
1462 static gboolean target_attribute(struct client_s *client, gchar **req)
1464 gchar **src, **dst, *line;
1465 xmlErrorPtr xml_error;
1467 if (!req || !req[0] || !req[1]) {
1468 send_error(client, EPWMD_COMMAND_SYNTAX);
1469 return FALSE;
1472 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1474 * The first argument may be only an account.
1476 if ((src = split_input_line(req[0], " ", 0)) == NULL) {
1477 send_error(client, EPWMD_COMMAND_SYNTAX);
1478 return FALSE;
1482 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1484 * The first argument may be only an account.
1486 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1487 send_error(client, EPWMD_COMMAND_SYNTAX);
1488 g_strfreev(src);
1489 goto blah;
1494 * Prevent an element tree pointing to only and account. Accounts require
1495 * at least one element. Accounts pointing to accounts are allowed.
1497 if ((!src[1] && dst[1]) || (!dst[1] && src[1])) {
1498 send_error(client, EPWMD_ATTR_SYNTAX);
1499 g_strfreev(src);
1500 g_strfreev(dst);
1501 goto blah;
1504 if (reset_reader(client->doc, &client->reader) == FALSE) {
1505 xml_error = xmlGetLastError();
1506 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1507 send_error(client, EPWMD_LIBXML_ERROR);
1508 g_strfreev(src);
1509 g_strfreev(dst);
1510 goto blah;
1514 * Make sure the destination element path exists.
1516 if (find_account(client->reader, dst[0]) == FALSE) {
1517 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1518 g_strfreev(src);
1519 g_strfreev(dst);
1520 goto blah;
1523 if (dst[1]) {
1524 if (find_elements(client, client->reader, dst+1, 0) == FALSE) {
1525 g_strfreev(src);
1526 g_strfreev(dst);
1527 goto blah;
1531 if (reset_reader(client->doc, &client->reader) == FALSE) {
1532 xml_error = xmlGetLastError();
1533 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1534 send_error(client, EPWMD_LIBXML_ERROR);
1535 g_strfreev(src);
1536 g_strfreev(dst);
1537 goto blah;
1541 * If the source element tree doesn't exist, create it.
1543 if (find_account(client->reader, src[0]) == FALSE) {
1544 if (new_account(client->doc, src[0]) == FALSE) {
1545 xml_error = xmlGetLastError();
1546 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1547 send_error(client, EPWMD_LIBXML_ERROR);
1548 g_strfreev(src);
1549 g_strfreev(dst);
1550 goto blah;
1553 if (reset_reader(client->doc, &client->reader) == FALSE) {
1554 xml_error = xmlGetLastError();
1555 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1556 send_error(client, EPWMD_LIBXML_ERROR);
1557 g_strfreev(src);
1558 g_strfreev(dst);
1559 goto blah;
1562 if (find_account(client->reader, src[0]) == FALSE) {
1563 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1564 g_strfreev(src);
1565 g_strfreev(dst);
1566 goto blah;
1570 if (src[1]) {
1571 if (find_elements(client, client->reader, src+1, 1) == FALSE) {
1572 if (reset_reader(client->doc, &client->reader) == FALSE) {
1573 xml_error = xmlGetLastError();
1574 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1575 send_error(client, EPWMD_LIBXML_ERROR);
1576 g_strfreev(src);
1577 g_strfreev(dst);
1578 goto blah;
1581 if (find_account(client->reader, src[0]) == FALSE) {
1582 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1583 g_strfreev(src);
1584 g_strfreev(dst);
1585 goto blah;
1588 xmlTextReaderNext(client->reader);
1590 if (create_elements(client, client->reader, src+1, 1) == FALSE) {
1591 g_strfreev(src);
1592 g_strfreev(dst);
1593 goto blah;
1596 if (reset_reader(client->doc, &client->reader) == FALSE) {
1597 xml_error = xmlGetLastError();
1598 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1599 send_error(client, EPWMD_LIBXML_ERROR);
1600 g_strfreev(src);
1601 g_strfreev(dst);
1602 goto blah;
1605 if (find_account(client->reader, src[0]) == FALSE) {
1606 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1607 g_strfreev(src);
1608 g_strfreev(dst);
1609 goto blah;
1612 if (find_elements(client, client->reader, src+1, 0) == FALSE) {
1613 g_strfreev(src);
1614 g_strfreev(dst);
1615 goto blah;
1620 line = g_strjoinv("\t", dst);
1622 if (add_attribute(client, "target", line) == FALSE) {
1623 g_free(line);
1624 g_strfreev(src);
1625 g_strfreev(dst);
1626 goto blah;
1629 g_strfreev(src);
1630 g_strfreev(dst);
1631 g_free(line);
1632 return TRUE;
1633 blah:
1634 g_strfreev(src);
1635 g_strfreev(dst);
1636 return FALSE;
1640 * req[0] - account name
1641 * req[1] - new name
1643 static gboolean name_attribute(struct client_s *client, gchar **req)
1645 xmlErrorPtr xml_error;
1647 if (reset_reader(client->doc, &client->reader) == FALSE) {
1648 xml_error = xmlGetLastError();
1649 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1650 send_error(client, EPWMD_LIBXML_ERROR);
1651 return FALSE;
1654 if (find_account(client->reader, req[0]) == FALSE) {
1655 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1656 return FALSE;
1659 if (strcmp(req[0], req[1]) == 0)
1660 return TRUE;
1662 if (reset_reader(client->doc, &client->reader) == FALSE) {
1663 xml_error = xmlGetLastError();
1664 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1665 send_error(client, EPWMD_LIBXML_ERROR);
1666 return FALSE;
1670 * Will not overwrite an existing account.
1672 if (find_account(client->reader, req[1]) == TRUE) {
1673 send_error(client, EPWMD_ACCOUNT_EXISTS);
1674 return FALSE;
1677 if (reset_reader(client->doc, &client->reader) == FALSE) {
1678 xml_error = xmlGetLastError();
1679 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1680 send_error(client, EPWMD_LIBXML_ERROR);
1681 return FALSE;
1684 if (find_account(client->reader, req[0]) == FALSE) {
1685 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1686 return FALSE;
1690 * Whitespace not allowed in account names.
1692 if (contains_whitespace(req[1]) == TRUE) {
1693 send_error(client, EPWMD_ATTR_SYNTAX);
1694 return FALSE;
1697 return add_attribute(client, "name", req[1]);
1701 * req[0] - attribute
1702 * req[1] - element path
1704 * If the element has a "target" attribute it won't be "followed".
1706 static gboolean attribute_get(struct client_s *client, gchar **req)
1708 xmlNodePtr n;
1709 xmlChar *a;
1710 gchar **nreq = NULL;
1711 xmlErrorPtr xml_error;
1713 if (!req || !req[0] || !req[1]) {
1714 send_error(client, EPWMD_COMMAND_SYNTAX);
1715 return FALSE;
1718 if (strchr(req[1], '\t')) {
1719 if ((nreq = split_input_line(req[1], "\t", 0)) == NULL) {
1720 send_error(client, EPWMD_COMMAND_SYNTAX);
1721 return FALSE;
1724 else {
1725 if ((nreq = split_input_line(req[1], " ", 0)) == NULL) {
1726 send_error(client, EPWMD_COMMAND_SYNTAX);
1727 return FALSE;
1731 if (reset_reader(client->doc, &client->reader) == FALSE) {
1732 xml_error = xmlGetLastError();
1733 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1734 g_strfreev(nreq);
1735 send_error(client, EPWMD_LIBXML_ERROR);
1736 return FALSE;
1739 if (find_account(client->reader, nreq[0]) == FALSE) {
1740 g_strfreev(nreq);
1741 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1742 return FALSE;
1745 if (nreq[1]) {
1746 if (find_elements(client, client->reader, nreq + 1, 0) == FALSE) {
1747 g_strfreev(nreq);
1748 return FALSE;
1752 g_strfreev(nreq);
1754 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1755 xml_error = xmlGetLastError();
1756 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1757 send_error(client, EPWMD_LIBXML_ERROR);
1758 return FALSE;
1761 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL) {
1762 send_error(client, EPWMD_ATTR_NOT_FOUND);
1763 return FALSE;
1766 send_to_client(client, "BEGIN %li\n%s\n", xmlStrlen(a), a);
1767 xmlFree(a);
1768 return TRUE;
1772 * req[0] - attribute
1773 * req[1] - element path
1774 * req[2] - value
1776 static gboolean attribute_set(struct client_s *client, gchar **req)
1778 gchar **epath = NULL;
1779 xmlErrorPtr xml_error;
1781 if (!req || !req[0] || !req[1] || !req[2]) {
1782 send_error(client, EPWMD_COMMAND_SYNTAX);
1783 return FALSE;
1787 * Reserved attribute names.
1789 if (g_ascii_strcasecmp(req[0], "NAME") == 0) {
1791 * Only reserved for the account element. Not the rest of the
1792 * document.
1794 if (strchr(req[1], '\t') == NULL)
1795 return name_attribute(client, req + 1);
1797 else if (g_ascii_strcasecmp(req[0], "TARGET") == 0)
1798 return target_attribute(client, req + 1);
1800 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1802 * The first argument may be only an account.
1804 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1805 send_error(client, EPWMD_COMMAND_SYNTAX);
1806 return FALSE;
1810 if (reset_reader(client->doc, &client->reader) == FALSE) {
1811 xml_error = xmlGetLastError();
1812 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1813 send_error(client, EPWMD_LIBXML_ERROR);
1814 goto blah;
1817 if (find_account(client->reader, epath[0]) == FALSE) {
1818 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1819 goto blah;
1822 if (epath[1]) {
1823 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1824 goto blah;
1827 g_strfreev(epath);
1828 return add_attribute(client, req[0], req[2]);
1829 blah:
1830 g_strfreev(epath);
1831 return FALSE;
1835 * req[0] - command
1836 * req[1] - attribute name or element path if command is LIST
1837 * req[2] - element path
1838 * req[2] - element path or value
1840 gboolean attr_command(struct client_s *client, gchar **req)
1842 if (!req || !req[0] || !req[1]) {
1843 send_error(client, EPWMD_COMMAND_SYNTAX);
1844 return FALSE;
1847 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1848 return attribute_set(client, req+1);
1849 if (g_ascii_strcasecmp(req[0], "GET") == 0)
1850 return attribute_get(client, req+1);
1851 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1852 return attribute_delete(client, req+1);
1853 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1854 return attribute_list(client, req+1);
1855 else
1856 send_error(client, EPWMD_COMMAND_SYNTAX);
1858 return FALSE;
1861 gboolean cache_test(const guchar *md5filename, gint reset)
1863 void *p;
1864 file_cache_t f;
1865 glong len;
1867 for (p = shm_data, len = 0; len <= cache_size;) {
1868 memcpy(&f, p, sizeof(file_cache_t));
1870 if (reset == 2) {
1871 memset(&f, 0, sizeof(file_cache_t));
1872 memcpy(p, &f, sizeof(file_cache_t));
1873 p += sizeof(file_cache_t);
1874 len += sizeof(file_cache_t);
1876 if (len + sizeof(file_cache_t) > cache_size)
1877 break;
1878 continue;
1881 if (f.used == TRUE) {
1882 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
1883 if (reset == 1) {
1884 memset(&f, 0, sizeof(file_cache_t));
1885 memcpy(p, &f, sizeof(file_cache_t));
1886 return TRUE;
1889 return cache_valid_key(f.key, sizeof(f.key));
1893 p += sizeof(file_cache_t);
1894 len += sizeof(file_cache_t);
1896 if (len + sizeof(file_cache_t) > cache_size)
1897 break;
1900 return (reset == 2) ? TRUE : FALSE;
1903 static gboolean file_exists(const gchar *filename)
1905 struct stat st;
1906 gchar filebuf[PATH_MAX], *p, *p2;
1908 p = get_key_file_string("default", "data_directory");
1909 p2 = expand_homedir(p);
1910 g_free(p);
1911 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
1912 g_free(p2);
1914 if (access(filebuf, R_OK) == -1)
1915 return FALSE;
1917 stat(filebuf, &st);
1919 if (st.st_size == 0)
1920 return FALSE;
1922 return TRUE;
1925 gboolean cache_command(struct client_s *client, gchar **req)
1927 guchar md5file[16];
1928 glong timeout;
1929 gchar *p;
1931 if (g_ascii_strcasecmp(req[0], "CLEAR") == 0) {
1932 if (!req[1]) {
1933 send_error(client, EPWMD_COMMAND_SYNTAX);
1934 return FALSE;
1937 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1939 if (cache_test(md5file, 1) == FALSE) {
1940 send_error(client, EPWMD_CACHE_NOT_FOUND);
1941 return FALSE;
1944 return TRUE;
1946 else if (g_ascii_strcasecmp(req[0], "CLEARALL") == 0) {
1947 cache_test(client->md5file, 2);
1948 return TRUE;
1950 else if (g_ascii_strcasecmp(req[0], "ISCACHED") == 0) {
1951 if (!req[1]) {
1952 send_error(client, EPWMD_COMMAND_SYNTAX);
1953 return FALSE;
1956 if (file_exists(req[1]) == FALSE) {
1957 send_error(client, EPWMD_FILE_NOT_FOUND);
1958 return FALSE;
1961 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1963 if (cache_test(md5file, 0) == FALSE) {
1964 send_error(client, EPWMD_CACHE_NOT_FOUND);
1965 return FALSE;
1968 return TRUE;
1970 else if (g_ascii_strcasecmp(req[0], "TIMEOUT") == 0) {
1971 if (!req[1] || !req[2]) {
1972 send_error(client, EPWMD_COMMAND_SYNTAX);
1973 return FALSE;
1976 errno = 0;
1977 timeout = strtol(req[1], &p, 10);
1979 if (errno != 0 || *p != 0) {
1980 send_error(client, EPWMD_COMMAND_SYNTAX);
1981 return FALSE;
1984 if (file_exists(req[2]) == FALSE) {
1985 send_error(client, EPWMD_FILE_NOT_FOUND);
1986 return FALSE;
1989 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[2], strlen(req[2]));
1991 if (cache_set_timeout(md5file, timeout) == FALSE) {
1992 send_error(client, EPWMD_CACHE_NOT_FOUND);
1993 return FALSE;
1996 return TRUE;
1998 else
1999 send_error(client, EPWMD_COMMAND_SYNTAX);
2001 return FALSE;
2004 gboolean help_command(struct client_s *client, const gchar *what)
2006 gchar *line;
2008 if (!what || !*what)
2009 line =
2010 "NFO Try 'HELP COMMAND' for command help\n"
2011 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
2012 else if (g_ascii_strcasecmp(what, "GET") == 0)
2013 line =
2014 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
2015 "NFO <account> is the account to work on and <element>\n"
2016 "NFO is the element wanted.\n"
2017 "NFO -\n"
2018 "NFO Example: GET isp <TAB> imap <TAB> port\n"
2019 "NFO GET isp <TAB> username\n";
2020 else if (g_ascii_strcasecmp(what, "QUIT") == 0)
2021 line =
2022 "NFO syntax: QUIT\n"
2023 "NFO close the connection\n";
2024 else if (g_ascii_strcasecmp(what, "DELETE") == 0)
2025 line =
2026 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
2027 else if (g_ascii_strcasecmp(what, "STORE") == 0)
2028 line =
2029 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
2030 "NFO <account> is the account to work on and <element>\n"
2031 "NFO is the element to create or modify\n"
2032 "NFO -\n"
2033 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
2034 "NFO STORE isp <TAB> username <TAB> someuser\n";
2035 else if (g_ascii_strcasecmp(what, "OPEN") == 0)
2036 line =
2037 "NFO syntax: OPEN <filename> [<key>]\n"
2038 "NFO opens a (new) file\n";
2039 else if (g_ascii_strcasecmp(what, "LIST") == 0)
2040 line =
2041 "NFO syntax: LIST [account]\n"
2042 "NFO shows available accounts or account elements\n";
2043 else if (g_ascii_strcasecmp(what, "ATTR") == 0)
2044 line =
2045 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
2046 "NFO ATTR SET NAME account value\n"
2047 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
2048 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
2049 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
2050 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
2051 "NFO ATTR LIST account[<TAB>element[...]]\n";
2052 else if (g_ascii_strcasecmp(what, "SAVE") == 0)
2053 line =
2054 "NFO syntax: SAVE [<key>]\n"
2055 "NFO save any changes to the opened file using <key>\n";
2056 else if (g_ascii_strcasecmp(what, "CACHE") == 0)
2057 line =
2058 "NFO syntax: CACHE [CLEARALL | [CLEAR | ISCACHED <filename>] |\n"
2059 "NFO TIMEOUT <seconds> <filename>]\n"
2060 "NFO tests or clears the cache entry for <filename>\n";
2061 else if (g_ascii_strcasecmp(what, "DUMP") == 0)
2062 line =
2063 "NFO syntax: DUMP\n"
2064 "NFO shows the in memory XML document\n";
2065 else {
2066 send_error(client, EPWMD_COMMAND_SYNTAX);
2067 return FALSE;
2070 send_to_client(client, "%sOK \n", line);
2071 return TRUE;
2074 gboolean dump_command(struct client_s *client)
2076 xmlChar *xml;
2077 gssize len;
2079 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2080 send_to_client(client, "BEGIN %li\n%s", len, xml);
2081 xmlFree(xml);
2082 return TRUE;