Added libXML memory handlers: xmlfree(), xmlmalloc(), xmlrealloc() and
[pwmd.git] / src / commands.c
blobcff393df14661097c6102156ea6c191947f328bf
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 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *key, gsize keysize,
42 guchar *iv, void *outbuf, gsize outsize, void *inbuf, gsize insize)
44 if ((gcryerrno = gcry_cipher_reset(gh))) {
45 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
46 return FALSE;
49 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
50 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
51 return FALSE;
54 if ((gcryerrno = gcry_cipher_setkey(gh, key, keysize))) {
55 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
56 return FALSE;
59 if ((gcryerrno = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
60 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
61 return FALSE;
64 return TRUE;
67 gboolean decrypt_xml(gcry_cipher_hd_t gh, void *key, gsize keysize,
68 guchar *iv, void *outbuf, gsize outsize, void *inbuf, gsize insize)
70 if ((gcryerrno = gcry_cipher_reset(gh))) {
71 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
72 return FALSE;
75 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
76 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
77 return FALSE;
80 if ((gcryerrno = gcry_cipher_setkey(gh, key, keysize))) {
81 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
82 return FALSE;
85 if ((gcryerrno = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize))) {
86 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
87 return FALSE;
90 return TRUE;
93 static gboolean parse_xml(struct client_s *client)
95 switch (open_xml(client->xml, client->len, &client->doc, &client->reader)) {
96 case 1:
97 send_error(client, EPWMD_LIBXML_ERROR);
98 return FALSE;
99 case 2:
100 send_error(client, EPWMD_LIBXML_ERROR);
101 return FALSE;
102 default:
103 break;
106 return TRUE;
109 static gboolean valid_filename(const gchar *filename)
111 const gchar *p;
113 if (!filename || !*filename)
114 return FALSE;
116 for (p = filename; *p; p++) {
117 if (g_ascii_isalnum(*p) == FALSE)
118 return FALSE;
121 return TRUE;
124 static gboolean cache_update_key(const guchar *md5filename, const guchar *shakey)
126 void *p;
127 file_cache_t f;
128 glong len;
130 for (p = shm_data, len = 0; len <= cache_size;) {
131 memcpy(&f, p, sizeof(file_cache_t));
133 if (f.used == TRUE) {
134 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
135 memcpy(&f.key, shakey, sizeof(f.key));
136 memcpy(p, &f, sizeof(file_cache_t));
137 return TRUE;
141 p += sizeof(file_cache_t);
142 len += sizeof(file_cache_t);
144 if (len + sizeof(file_cache_t) > cache_size)
145 break;
148 return cache_add_file(md5filename, shakey);
151 static gint cache_valid_key(const guchar *key, gsize len)
153 gint b;
155 for (b = 0; b < len; b++) {
156 if (key[b])
157 return TRUE;
160 return FALSE;
163 // FIXME stat info
164 static gboolean cache_get_key(const guchar *md5file, guchar *shakey)
166 void *p;
167 file_cache_t f;
168 glong len;
170 for (p = shm_data, len = 0; len <= cache_size;) {
171 memcpy(&f, p, sizeof(file_cache_t));
174 * The slot may be used but not yet contain a key.
176 if (f.used == TRUE) {
177 if (memcmp(&f.filename, md5file, sizeof(f.filename)) == 0) {
178 if (cache_valid_key(f.key, sizeof(f.key)) == FALSE)
179 return FALSE;
181 memcpy(shakey, &f.key, sizeof(f.key));
182 return TRUE;
186 p += sizeof(file_cache_t);
187 len += sizeof(file_cache_t);
189 if (len + sizeof(file_cache_t) > cache_size)
190 break;
193 return FALSE;
196 gint open_file(const gchar *filename, struct stat *st)
198 gint fd;
200 if ((fd = open(filename, O_RDONLY)) == -1)
201 return -1;
203 if (stat(filename, st) == -1) {
204 close(fd);
205 return -1;
208 return fd;
211 gboolean open_command(struct client_s *client, gchar **req)
213 gint fd;
214 struct stat st;
215 gchar *inbuf;
216 const gchar *filename = req[0];
217 guchar *iv;
218 guchar shakey[gcrykeysize];
219 gint cached = 0;
221 if (!filename || !*filename) {
222 send_error(client, EPWMD_COMMAND_SYNTAX);
223 return FALSE;
226 if (valid_filename(filename) == FALSE) {
227 send_error(client, EPWMD_INVALID_FILENAME);
228 return FALSE;
231 if (stat(filename, &st) == 0) {
232 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
233 send_to_client(client, "ERR %03i %s\n", EPWMD_NOT_A_FILE, pwmd_strerror(EPWMD_NOT_A_FILE));
234 return FALSE;
238 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
241 * New files don't need a key.
243 if (access(filename, R_OK|W_OK) != 0) {
244 if (errno != ENOENT) {
245 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename,
246 strerror(errno));
247 return FALSE;
249 new_doc:
250 if ((client->xml = new_document()) == NULL) {
251 send_to_client(client, "ERR %03i malloc(): %s\n", EPWMD_ERROR,
252 strerror(errno));
253 return FALSE;
256 client->len = strlen(client->xml);
258 if (cache_add_file(client->md5file, NULL) == FALSE) {
259 send_error(client, EPWMD_MAX_SLOTS);
260 return FALSE;
263 client->filename = g_strdup(filename);
264 return parse_xml(client);
267 if (cache_get_key(client->md5file, shakey) == TRUE)
268 cached = 1;
269 else {
271 * No key specified and no matching filename found in the cache.
273 if (!req[1] || !*req[1]) {
274 send_error(client, EPWMD_KEY);
275 return FALSE;
279 if ((fd = open_file(filename, &st)) == -1) {
280 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(errno));
281 return FALSE;
284 if (st.st_size == 0)
285 goto new_doc;
287 inbuf = gcry_malloc(st.st_size);
288 read(fd, inbuf, st.st_size);
289 close(fd);
290 iv = gcry_malloc(gcryblocksize);
291 memcpy(iv, inbuf, gcryblocksize);
293 again:
294 if (!cached) {
295 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
296 memset(req[1], 0, strlen(req[1]));
299 if (decrypt_xml(client->gh, shakey, gcrykeysize, iv, inbuf + gcryblocksize,
300 st.st_size - gcryblocksize, NULL, 0) == FALSE) {
301 if (cached) {
302 cached = 0;
303 goto again;
306 memset(inbuf, 0, st.st_size);
307 gcry_free(inbuf);
308 gcry_free(iv);
309 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
310 return FALSE;
313 gcry_free(iv);
314 memmove(inbuf, inbuf + gcryblocksize, st.st_size - gcryblocksize);
315 client->xml = inbuf;
316 client->len = st.st_size - gcryblocksize;
318 if (g_strncasecmp(client->xml, "<?xml version=\"1.0\"?>", 21) != 0) {
319 send_error(client, EPWMD_BADKEY);
320 return FALSE;
323 if (!cached) {
324 if (cache_add_file(client->md5file, shakey) == FALSE) {
325 send_error(client, EPWMD_MAX_SLOTS);
326 return FALSE;
330 client->filename = g_strdup(filename);
331 return parse_xml(client);
335 * client->reader should be at the position in the document where the element
336 * search should start. It won't search past the closing account element.
338 static gboolean get_elements(struct client_s *client, xmlTextReaderPtr reader,
339 gchar **req, gint quiet)
341 gint i;
343 if (!req || !req[0]) {
344 if (!quiet)
345 send_error(client, EPWMD_COMMAND_SYNTAX);
346 return FALSE;
349 for (i = 0; req[i]; i++) {
350 if (find_element(reader, req[i], req[i+1] != NULL) == FALSE) {
351 if (!quiet)
352 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
354 return FALSE;
358 return TRUE;
361 gboolean save_command(struct client_s *client, const gchar *filename, gchar *key)
363 xmlChar *xmlbuf;
364 gint insize;
365 void *outbuf, *inbuf;
366 gsize len = 0;
367 gint fd;
368 guchar *iv;
369 gint update = 1;
370 guchar shakey[gcrykeysize];
372 if (!key || !key) {
373 if (cache_get_key(client->md5file, shakey) == FALSE) {
374 send_error(client, EPWMD_KEY);
375 return FALSE;
378 update = 0;
380 else {
381 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
382 memset(key, 0, strlen(key));
385 xmlDocDumpFormatMemory(client->doc, &xmlbuf, &insize, 0);
386 len = insize;
388 if (insize / gcryblocksize) {
389 len = (insize / gcryblocksize) * gcryblocksize;
391 if (insize % gcryblocksize)
392 len += gcryblocksize;
395 inbuf = gcry_calloc(1, len);
396 memcpy(inbuf, xmlbuf, insize);
397 memset(xmlbuf, 0, insize);
398 xmlFree(xmlbuf);
399 insize = len;
400 outbuf = gcry_malloc(insize);
401 iv = gcry_malloc(gcryblocksize);
402 gcry_create_nonce(iv, gcryblocksize);
404 if (encrypt_xml(client->gh, shakey, gcrykeysize, iv, outbuf, insize, inbuf, insize)
405 == FALSE) {
406 gcry_free(iv);
407 memset(inbuf, 0, insize);
408 gcry_free(inbuf);
409 memset(outbuf, 0, insize);
410 gcry_free(outbuf);
411 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
412 return FALSE;
415 memset(inbuf, 0, insize);
416 gcry_free(inbuf);
418 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
419 gcry_free(iv);
420 memset(outbuf, 0, insize);
421 gcry_free(outbuf);
422 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(errno));
423 return FALSE;
426 write(fd, iv, gcryblocksize);
427 write(fd, outbuf, insize);
428 close(fd);
429 gcry_free(iv);
430 memset(outbuf, 0, insize);
431 gcry_free(outbuf);
433 if (!update)
434 return TRUE;
436 if (cache_update_key(client->md5file, shakey) == FALSE) {
437 send_error(client, EPWMD_MAX_SLOTS);
438 return FALSE;
441 return TRUE;
444 static gboolean contains_whitespace(const gchar *str)
446 const gchar *p = str;
448 for (; *p; p++) {
449 if (g_ascii_isspace(*p) == TRUE) {
450 return TRUE;
454 return FALSE;
458 * the 'reader' should be at the element of the document where the elements
459 * 'req' are to be created.
461 static gboolean create_elements(struct client_s *client, xmlTextReaderPtr reader,
462 gchar **req, gint novalue)
464 gint i;
465 gboolean ret = TRUE;
466 xmlNodePtr r;
468 r = xmlTextReaderCurrentNode(reader);
470 if (xmlTextReaderDepth(reader) > 1)
471 r = r->parent;
473 for (i = 0; req[i]; i++) {
474 xmlNodePtr n;
477 * Whitespace is allowed in values or attributes but not in element
478 * names.
480 if (req[i+1] && valid_xml_element((xmlChar *)req[i]) == FALSE) {
481 send_error(client, EPWMD_INVALID_ELEMENT);
482 return FALSE;
486 * The value of the element tree.
488 if (!req[i+1] && !novalue) {
490 * Prevent creating 'text' elements in the root of the account.
492 if (i < 1) {
493 send_error(client, EPWMD_ROOT_TEXT_ELEMENT);
494 return FALSE;
498 * FIXME ?? overwriting an element tree with a text element:
500 * STORE account element element2 value
501 * STORE account element value
503 * would remove the existing element tree. This may be a bug or
504 * feature.
506 xmlNodeSetContent(r, (xmlChar *)req[i]);
507 break;
510 if ((n = find_node(r, (xmlChar *)req[i])) == NULL) {
511 n = xmlNewNode(NULL, (xmlChar *)req[i]);
512 r = xmlAddChild(r, n);
514 else
515 r = n;
517 if (!req[i+1] && novalue)
518 return TRUE;
521 return ret;
525 * FIXME reuse reader handle
527 static gboolean reset_reader(xmlDocPtr doc, xmlTextReaderPtr *reader)
529 if (reader) {
530 xmlFreeTextReader(*reader);
531 *reader = NULL;
534 if ((*reader = xmlReaderWalker(doc)) == NULL)
535 return FALSE;
537 return TRUE;
540 static void delete_node(xmlNodePtr n)
542 xmlUnlinkNode(n);
543 xmlFreeNode(n);
546 gboolean delete_command(struct client_s *client, gchar **req)
548 gint i = 1;
549 xmlNodePtr n;
551 if (reset_reader(client->doc, &client->reader) == FALSE) {
552 send_error(client, EPWMD_LIBXML_ERROR);
553 return FALSE;
556 if (find_account(client->reader, req[0]) == FALSE) {
557 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
558 return FALSE;
561 n = xmlTextReaderCurrentNode(client->reader);
564 * No sub-node defined. Remove the entire node (account).
566 if (!req[i]) {
567 if (n)
568 delete_node(n);
569 return TRUE;
573 * Remove matching sub-nodes starting from the root of the account.
575 while (req[i] && find_element(client->reader, req[i++], req[i] != NULL) == TRUE)
576 n = xmlTextReaderCurrentNode(client->reader);
578 if (n && xmlStrcmp(n->name, (xmlChar *)req[i-1]) == 0 &&
579 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_ELEMENT)
580 delete_node(n);
581 else {
582 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
583 return FALSE;
586 return TRUE;
589 gboolean store_command(struct client_s *client, gchar **req)
591 again:
592 if (reset_reader(client->doc, &client->reader) == FALSE) {
593 send_error(client, EPWMD_LIBXML_ERROR);
594 return FALSE;
597 if (!req[0]) {
598 send_error(client, EPWMD_COMMAND_SYNTAX);
599 return FALSE;
602 if (find_account(client->reader, req[0]) == FALSE) {
603 if (contains_whitespace(req[0]) == TRUE) {
604 send_error(client, EPWMD_INVALID_ELEMENT);
605 return FALSE;
608 if (new_account(client->doc, req[0]) == FALSE) {
609 send_error(client, EPWMD_ERROR);
610 return FALSE;
613 goto again;
616 xmlTextReaderNext(client->reader);
617 return create_elements(client, client->reader, req+1, 0);
620 static gboolean do_get_command(struct client_s *client, xmlTextReaderPtr *reader,
621 gchar **req, xmlChar **content, gint quiet, gint list)
623 xmlNodePtr n;
624 xmlAttrPtr a;
625 gchar **nreq;
626 gboolean ret;
627 xmlChar *p;
629 if (reset_reader(client->doc, reader) == FALSE) {
630 if (!quiet)
631 send_error(client, EPWMD_LIBXML_ERROR);
632 return FALSE;
635 if (find_account(*reader, req[0]) == FALSE) {
636 if (!quiet && !list)
637 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
638 return FALSE;
642 * May be an account with only a TARGET attribute.
644 if (req[1]) {
645 if (get_elements(client, *reader, req + 1, (list) ? 1 : quiet) == FALSE)
646 return FALSE;
649 if ((n = xmlTextReaderCurrentNode(*reader)) == NULL) {
650 if (!quiet)
651 send_error(client, EPWMD_LIBXML_ERROR);
652 return FALSE;
656 * If the current element has a TARGET attribute, the value of the
657 * attribute is an element path somewhere else in the document. Use this
658 * value and not any TEXT element value. The value may be another element
659 * with a TARGET attribute and this function will recurse until a non-TARGET
660 * attribute in the element is found.
662 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
663 if ((p = xmlNodeGetContent(a->children)) != NULL) {
664 if (strchr((gchar *)p, '\t') != NULL) {
665 if ((nreq = split_input_line((gchar *)p, "\t", 0)) == NULL) {
666 xmlFree(p);
668 if (!quiet)
669 send_error(client, EPWMD_INVALID_ELEMENT);
670 return FALSE;
673 else {
674 if ((nreq = split_input_line((gchar *)p, " ", 0)) == NULL) {
675 xmlFree(p);
677 if (!quiet)
678 send_error(client, EPWMD_INVALID_ELEMENT);
679 return FALSE;
683 xmlFree(p);
684 ret = do_get_command(client, reader, nreq, content, quiet, list);
685 g_strfreev(nreq);
686 return ret;
690 switch (xmlTextReaderNext(*reader)) {
691 case -1:
692 if (!quiet)
693 send_error(client, EPWMD_LIBXML_ERROR);
694 return FALSE;
695 case 0:
696 if (!quiet)
697 send_error(client, EPWMD_EMPTY_ELEMENT);
698 return FALSE;
699 default:
700 break;
703 switch (xmlTextReaderNodeType(*reader)) {
704 case XML_READER_TYPE_END_ELEMENT:
706 * May be an empty element after an ATTR DELETE TARGET command.
708 return TRUE;
709 case XML_READER_TYPE_TEXT:
710 break;
711 case -1:
712 if (!quiet)
713 send_error(client, EPWMD_LIBXML_ERROR);
715 return FALSE;
716 default:
717 if (!quiet) {
718 if (n->children && !list)
719 send_error(client, EPWMD_TRAILING_ELEMENT);
720 else if (!n->children && !list)
721 send_error(client, EPWMD_INVALID_ELEMENT);
723 return FALSE;
726 if ((*content = xmlNodeGetContent(n)) == NULL) {
727 if (!quiet)
728 send_error(client, EPWMD_EMPTY_ELEMENT);
729 return FALSE;
732 return TRUE;
736 * Retrieves the value associated with the element tree 'req'.
738 gboolean get_command(struct client_s *client, xmlTextReaderPtr *reader,
739 gchar **req, gint quiet)
741 xmlChar *content = NULL;
743 if (do_get_command(client, reader, req, &content, quiet, 0) == FALSE)
744 return FALSE;
746 if (!content) {
747 if (!quiet)
748 send_error(client, EPWMD_EMPTY_ELEMENT);
750 return FALSE;
753 send_to_client(client, "BEGIN %li\n%s\nOK \n", xmlStrlen(content), content);
754 memset(content, 0, xmlStrlen(content));
755 xmlFree(content);
756 return TRUE;
759 #ifdef DEBUG
760 static gchar *element_path_to_req(const gchar *account, xmlChar *path,
761 const xmlChar *content)
762 #else
763 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
764 #endif
766 xmlChar *p = path;
767 gint n;
768 gchar *buf;
770 if (!p)
771 return NULL;
773 for (n = 0; *p && n < 3; p++) {
774 if (*p == '/')
775 n++;
778 if (strstr((gchar *)p, "text()") != NULL)
779 p[xmlStrlen(p) - 7] = 0;
781 for (n = 0; p[n]; n++) {
782 if (p[n] == '/')
783 p[n] = '\t';
786 #ifdef DEBUG
787 buf = g_strdup_printf("%s\t%s\t%s", account, p, content);
788 #else
789 buf = g_strdup_printf("%s\t%s", account, p);
790 #endif
791 return buf;
794 static gboolean append_to_array(gchar ***array, gint *total, const gchar *str)
796 gchar **a;
797 gint t = *total;
799 if ((a = g_realloc(*array, (t + 2) * sizeof(gchar *))) == NULL)
800 return FALSE;
802 a[t++] = g_strdup(str);
803 a[t] = NULL;
804 *total = t;
805 *array = a;
806 return TRUE;
809 gboolean list_command(struct client_s *client, gchar *str)
811 gchar *dst = NULL;
812 gchar *p = str;
813 gchar **elements = NULL;
814 gint pwmd_errno = -1;
815 gchar *account;
816 gint total = 0;
817 xmlChar *path = NULL;
818 xmlAttrPtr a;
819 xmlNodePtr n;
820 xmlChar *content;
821 xmlTextReaderPtr r = NULL;
822 gchar **nreq;
823 gboolean ret;
824 gint type;
825 gchar *line;
827 if (reset_reader(client->doc, &client->reader) == FALSE) {
828 send_error(client, EPWMD_LIBXML_ERROR);
829 return FALSE;
832 if (strchr(p, ' ') == NULL) {
833 list_only:
834 if (list_accounts(client->reader, &dst, &pwmd_errno) == FALSE) {
835 send_error(client, pwmd_errno);
836 return FALSE;
838 else {
839 send_to_client(client, "BEGIN %i\n%s\nOK \n",
840 g_utf8_strlen(dst, -1), dst);
841 memset(dst, 0, strlen(dst));
842 g_free(dst);
845 return TRUE;
848 p = str + 5;
850 while (*p && isspace(*p))
851 p++;
853 if (!*p)
854 goto list_only;
856 if (find_account(client->reader, p) == FALSE) {
857 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
858 return FALSE;
861 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
862 send_error(client, EPWMD_LIBXML_ERROR);
863 return FALSE;
866 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
867 if (reset_reader(client->doc, &client->reader) == FALSE) {
868 send_error(client, EPWMD_LIBXML_ERROR);
869 return FALSE;
872 if ((content = xmlNodeGetContent(a->children)) != NULL) {
873 if (find_account(client->reader, (gchar *)content) == FALSE) {
874 xmlFree(content);
875 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
876 return FALSE;
879 xmlFree(content);
883 account = g_strdup(p);
885 while (xmlTextReaderNext(client->reader) == 1) {
886 again:
887 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
888 send_error(client, EPWMD_LIBXML_ERROR);
889 return FALSE;
892 if (xmlTextReaderDepth(client->reader) == 1 &&
893 xmlStrcmp(n->name, (xmlChar *)"account") == 0 &&
894 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
895 break;
898 * If the current element has a TARGET attribute, the value of the
899 * attribute is an element path somewhere else in the document. Use this
900 * value and not any TEXT element value.
902 type = xmlTextReaderNodeType(client->reader);
903 a = xmlHasProp(n, (xmlChar *)"target");
905 if (type == XML_READER_TYPE_ELEMENT && a) {
906 if ((content = xmlNodeGetContent(a->children)) != NULL) {
907 path = xmlGetNodePath(n);
909 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
910 if (elements)
911 g_strfreev(elements);
913 xmlFree(path);
914 xmlFree(content);
915 send_error(client, EPWMD_INVALID_ELEMENT);
916 return FALSE;
919 xmlFree(content);
920 r = NULL;
922 if ((ret = do_get_command(client, &r, nreq, &content, 0, 1)) == TRUE) {
923 if (content && *content) {
924 #ifdef DEBUG
925 line = element_path_to_req(account, path, content);
926 #else
927 line = element_path_to_req(account, path);
928 #endif
929 memset(content, 0, xmlStrlen(content));
930 xmlFree(content);
932 if (append_to_array(&elements, &total, line) == FALSE) {
933 if (elements)
934 g_strfreev(elements);
936 xmlFree(path);
937 memset(line, 0, g_utf8_strlen(line, -1));
938 g_free(line);
939 g_strfreev(nreq);
940 xmlFreeTextReader(r);
941 send_error(client, EPWMD_ERROR);
942 return FALSE;
945 memset(line, 0, g_utf8_strlen(line, -1));
946 g_free(line);
949 if (xmlTextReaderNext(client->reader) == 1) {
950 if (xmlTextReaderNodeType(client->reader) !=
951 XML_READER_TYPE_TEXT) {
952 g_strfreev(nreq);
953 xmlFreeTextReader(r);
954 xmlFree(path);
955 goto again;
960 g_strfreev(nreq);
961 xmlFreeTextReader(r);
962 xmlFree(path);
963 continue;
967 if (type == XML_READER_TYPE_TEXT) {
968 xmlChar *np = xmlGetNodePath(n);
970 #ifdef DEBUG
971 content = xmlNodeGetContent(n);
972 line = element_path_to_req(account, np, content);
973 xmlFree(content);
974 #else
975 line = element_path_to_req(account, np);
976 #endif
977 xmlFree(np);
978 append_to_array(&elements, &total, line);
979 memset(line, 0, g_utf8_strlen(line, -1));
980 g_free(line);
984 if (!elements) {
985 send_error(client, EPWMD_EMPTY_ELEMENT);
986 g_free(account);
987 return FALSE;
990 g_free(account);
991 line = g_strjoinv("\n", elements);
992 send_to_client(client, "BEGIN %li\n%s\nOK \n",
993 g_utf8_strlen(line, -1), line);
994 g_strfreev(elements);
995 g_free(line);
996 return TRUE;
1000 * The client->reader handle should be at the element in the document where
1001 * the attribute will be created or modified.
1003 static gboolean add_attribute(struct client_s *client, const gchar *name, const gchar *value)
1005 xmlAttrPtr a;
1006 xmlNodePtr n;
1008 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1009 send_error(client, EPWMD_LIBXML_ERROR);
1010 return FALSE;
1013 if ((a = xmlHasProp(n, (xmlChar *)name)) == NULL)
1014 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)value);
1015 else
1016 xmlNodeSetContent(a->children, (xmlChar *)value);
1018 return TRUE;
1022 * req[0] - element path
1024 static gboolean attribute_list(struct client_s *client, gchar **req)
1026 gchar **attrlist = NULL;
1027 gint i = 0;
1028 gchar **epath = NULL;
1029 xmlAttrPtr a;
1030 xmlNodePtr n, an;
1031 gchar *line;
1033 if (!req || !req[0]) {
1034 send_error(client, EPWMD_COMMAND_SYNTAX);
1035 return FALSE;
1038 if ((epath = split_input_line(req[0], "\t", 0)) == NULL) {
1040 * The first argument may be only an account.
1042 if ((epath = split_input_line(req[0], " ", 0)) == NULL) {
1043 send_error(client, EPWMD_COMMAND_SYNTAX);
1044 return FALSE;
1048 if (reset_reader(client->doc, &client->reader) == FALSE) {
1049 send_error(client, EPWMD_LIBXML_ERROR);
1050 goto blah;
1053 if (find_account(client->reader, epath[0]) == FALSE) {
1054 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1055 goto blah;
1058 if (epath[1]) {
1059 if ((get_elements(client, client->reader, epath+1, 0)) == FALSE)
1060 goto blah;
1063 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1064 send_error(client, EPWMD_LIBXML_ERROR);
1065 goto blah;
1068 for (a = n->properties; a; a = a->next) {
1069 if ((attrlist = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1070 send_error(client, EPWMD_ERROR);
1071 goto blah;
1074 an = a->children;
1075 attrlist[i++] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1076 attrlist[i] = NULL;
1079 if (!attrlist) {
1080 send_error(client, EPWMD_EMPTY_ELEMENT);
1081 goto blah;
1084 line = g_strjoinv("\n", attrlist);
1085 send_to_client(client, "BEGIN %li\n%s\n", g_utf8_strlen(line, -1), line);
1086 g_free(line);
1087 g_strfreev(epath);
1088 g_strfreev(attrlist);
1089 return TRUE;
1090 blah:
1091 g_strfreev(epath);
1092 return FALSE;
1096 * req[0] - attribute
1097 * req[1] - element path
1099 static gboolean attribute_delete(struct client_s *client, gchar **req)
1101 xmlAttrPtr a;
1102 xmlNodePtr n;
1103 gchar **epath = NULL;
1105 if (!req || !req[0] || !req[1]) {
1106 send_error(client, EPWMD_COMMAND_SYNTAX);
1107 return FALSE;
1110 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1112 * The first argument may be only an account.
1114 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1115 send_error(client, EPWMD_COMMAND_SYNTAX);
1116 return FALSE;
1121 * Don't remove the NAME attribute for the account element.
1123 if (!epath[1] && g_ascii_strcasecmp(req[0], "NAME") == 0) {
1124 send_error(client, EPWMD_ATTR_SYNTAX);
1125 goto blah;
1128 if (reset_reader(client->doc, &client->reader) == FALSE) {
1129 send_error(client, EPWMD_LIBXML_ERROR);
1130 goto blah;
1133 if (find_account(client->reader, epath[0]) == FALSE) {
1134 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1135 goto blah;
1138 if (epath[1]) {
1139 if ((get_elements(client, client->reader, epath+1, 0)) == FALSE)
1140 goto blah;
1143 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1144 send_error(client, EPWMD_LIBXML_ERROR);
1145 goto blah;
1148 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL) {
1149 send_error(client, EPWMD_ATTR_NOT_FOUND);
1150 goto blah;
1153 if (xmlRemoveProp(a) == -1) {
1154 send_error(client, EPWMD_LIBXML_ERROR);
1155 goto blah;
1158 g_strfreev(epath);
1159 return TRUE;
1160 blah:
1161 g_strfreev(epath);
1162 return FALSE;
1166 * req[0] - source element path
1167 * req[1] - destination element path
1169 static gboolean target_attribute(struct client_s *client, gchar **req)
1171 gchar **src, **dst, *line;
1173 if (!req || !req[0] || !req[1]) {
1174 send_error(client, EPWMD_COMMAND_SYNTAX);
1175 return FALSE;
1178 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1180 * The first argument may be only an account.
1182 if ((src = split_input_line(req[0], " ", 0)) == NULL) {
1183 send_error(client, EPWMD_COMMAND_SYNTAX);
1184 return FALSE;
1188 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1190 * The first argument may be only an account.
1192 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1193 send_error(client, EPWMD_COMMAND_SYNTAX);
1194 g_strfreev(src);
1195 goto blah;
1200 * Prevent an element tree pointing to only and account. Accounts require
1201 * at least one element. Accounts pointing to accounts are allowed.
1203 if ((!src[1] && dst[1]) || (!dst[1] && src[1])) {
1204 send_error(client, EPWMD_ATTR_SYNTAX);
1205 g_strfreev(src);
1206 g_strfreev(dst);
1207 goto blah;
1210 if (reset_reader(client->doc, &client->reader) == FALSE) {
1211 send_error(client, EPWMD_LIBXML_ERROR);
1212 g_strfreev(src);
1213 g_strfreev(dst);
1214 goto blah;
1218 * Make sure the destination element path exists.
1220 if (find_account(client->reader, dst[0]) == FALSE) {
1221 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1222 g_strfreev(src);
1223 g_strfreev(dst);
1224 goto blah;
1227 if (dst[1]) {
1228 if (get_elements(client, client->reader, dst+1, 0) == FALSE) {
1229 g_strfreev(src);
1230 g_strfreev(dst);
1231 goto blah;
1235 if (reset_reader(client->doc, &client->reader) == FALSE) {
1236 send_error(client, EPWMD_LIBXML_ERROR);
1237 g_strfreev(src);
1238 g_strfreev(dst);
1239 goto blah;
1243 * If the source element tree doesn't exist, create it.
1245 if (find_account(client->reader, src[0]) == FALSE) {
1246 if (new_account(client->doc, src[0]) == FALSE) {
1247 send_error(client, EPWMD_LIBXML_ERROR);
1248 g_strfreev(src);
1249 g_strfreev(dst);
1250 goto blah;
1253 if (reset_reader(client->doc, &client->reader) == FALSE) {
1254 send_error(client, EPWMD_LIBXML_ERROR);
1255 g_strfreev(src);
1256 g_strfreev(dst);
1257 goto blah;
1260 if (find_account(client->reader, src[0]) == FALSE) {
1261 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1262 g_strfreev(src);
1263 g_strfreev(dst);
1264 goto blah;
1268 if (src[1]) {
1269 if (get_elements(client, client->reader, src+1, 1) == FALSE) {
1270 if (reset_reader(client->doc, &client->reader) == FALSE) {
1271 send_error(client, EPWMD_LIBXML_ERROR);
1272 g_strfreev(src);
1273 g_strfreev(dst);
1274 goto blah;
1277 if (find_account(client->reader, src[0]) == FALSE) {
1278 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1279 g_strfreev(src);
1280 g_strfreev(dst);
1281 goto blah;
1284 xmlTextReaderNext(client->reader);
1286 if (create_elements(client, client->reader, src+1, 1) == FALSE) {
1287 g_strfreev(src);
1288 g_strfreev(dst);
1289 goto blah;
1292 if (reset_reader(client->doc, &client->reader) == FALSE) {
1293 send_error(client, EPWMD_LIBXML_ERROR);
1294 g_strfreev(src);
1295 g_strfreev(dst);
1296 goto blah;
1299 if (find_account(client->reader, src[0]) == FALSE) {
1300 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1301 g_strfreev(src);
1302 g_strfreev(dst);
1303 goto blah;
1306 if (get_elements(client, client->reader, src+1, 0) == FALSE) {
1307 g_strfreev(src);
1308 g_strfreev(dst);
1309 goto blah;
1314 line = g_strjoinv("\t", dst);
1316 if (add_attribute(client, "target", line) == FALSE) {
1317 g_free(line);
1318 g_strfreev(src);
1319 g_strfreev(dst);
1320 goto blah;
1323 g_strfreev(src);
1324 g_strfreev(dst);
1325 g_free(line);
1326 return TRUE;
1327 blah:
1328 g_strfreev(src);
1329 g_strfreev(dst);
1330 return FALSE;
1334 * req[0] - account name
1335 * req[1] - new name
1337 static gboolean name_attribute(struct client_s *client, gchar **req)
1339 if (reset_reader(client->doc, &client->reader) == FALSE) {
1340 send_error(client, EPWMD_LIBXML_ERROR);
1341 return FALSE;
1344 if (find_account(client->reader, req[0]) == FALSE) {
1345 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1346 return FALSE;
1349 if (strcmp(req[0], req[1]) == 0)
1350 return TRUE;
1352 if (reset_reader(client->doc, &client->reader) == FALSE) {
1353 send_error(client, EPWMD_LIBXML_ERROR);
1354 return FALSE;
1358 * Will not overwrite an existing account.
1360 if (find_account(client->reader, req[1]) == TRUE) {
1361 send_error(client, EPWMD_ACCOUNT_EXISTS);
1362 return FALSE;
1365 if (reset_reader(client->doc, &client->reader) == FALSE) {
1366 send_error(client, EPWMD_LIBXML_ERROR);
1367 return FALSE;
1370 if (find_account(client->reader, req[0]) == FALSE) {
1371 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1372 return FALSE;
1376 * Whitespace not allowed in account names.
1378 if (contains_whitespace(req[1]) == TRUE) {
1379 send_error(client, EPWMD_ATTR_SYNTAX);
1380 return FALSE;
1383 return add_attribute(client, "name", req[1]);
1387 * req[0] - attribute
1388 * req[1] - element path
1390 * If the element has a "target" attribute it won't be "followed".
1392 static gboolean attribute_get(struct client_s *client, gchar **req)
1394 xmlNodePtr n;
1395 xmlChar *a;
1396 gchar **nreq = NULL;
1398 if (!req || !req[0] || !req[1]) {
1399 send_error(client, EPWMD_COMMAND_SYNTAX);
1400 return FALSE;
1403 if (strchr(req[1], '\t')) {
1404 if ((nreq = split_input_line(req[1], "\t", 0)) == NULL) {
1405 send_error(client, EPWMD_COMMAND_SYNTAX);
1406 return FALSE;
1409 else {
1410 if ((nreq = split_input_line(req[1], " ", 0)) == NULL) {
1411 send_error(client, EPWMD_COMMAND_SYNTAX);
1412 return FALSE;
1416 if (reset_reader(client->doc, &client->reader) == FALSE) {
1417 g_strfreev(nreq);
1418 send_error(client, EPWMD_LIBXML_ERROR);
1419 return FALSE;
1422 if (find_account(client->reader, nreq[0]) == FALSE) {
1423 g_strfreev(nreq);
1424 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1425 return FALSE;
1428 if (nreq[1]) {
1429 if (get_elements(client, client->reader, nreq + 1, 0) == FALSE) {
1430 g_strfreev(nreq);
1431 return FALSE;
1435 g_strfreev(nreq);
1437 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1438 send_error(client, EPWMD_LIBXML_ERROR);
1439 return FALSE;
1442 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL) {
1443 send_error(client, EPWMD_ATTR_NOT_FOUND);
1444 return FALSE;
1447 send_to_client(client, "BEGIN %li\n%s\n", xmlStrlen(a), a);
1448 xmlFree(a);
1449 return TRUE;
1453 * req[0] - attribute
1454 * req[1] - element path
1455 * req[2] - value
1457 static gboolean attribute_set(struct client_s *client, gchar **req)
1459 gchar **epath = NULL;
1461 if (!req || !req[0] || !req[1] || !req[2]) {
1462 send_error(client, EPWMD_COMMAND_SYNTAX);
1463 return FALSE;
1467 * Reserved attribute names.
1469 if (g_ascii_strcasecmp(req[0], "NAME") == 0) {
1471 * Only reserved for the account element. Not the rest of the
1472 * document.
1474 if (strchr(req[1], '\t') == NULL)
1475 return name_attribute(client, req + 1);
1477 else if (g_ascii_strcasecmp(req[0], "TARGET") == 0)
1478 return target_attribute(client, req + 1);
1480 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1482 * The first argument may be only an account.
1484 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1485 send_error(client, EPWMD_COMMAND_SYNTAX);
1486 return FALSE;
1490 if (reset_reader(client->doc, &client->reader) == FALSE) {
1491 send_error(client, EPWMD_LIBXML_ERROR);
1492 goto blah;
1495 if (find_account(client->reader, epath[0]) == FALSE) {
1496 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1497 goto blah;
1500 if (epath[1]) {
1501 if ((get_elements(client, client->reader, epath+1, 0)) == FALSE)
1502 goto blah;
1505 return add_attribute(client, req[0], req[2]);
1506 blah:
1507 g_strfreev(epath);
1508 return FALSE;
1512 * req[0] - command
1513 * req[1] - attribute name or element path if command is LIST
1514 * req[2] - element path
1515 * req[2] - element path or value
1517 gboolean attr_command(struct client_s *client, gchar **req)
1519 if (!req || !req[0] || !req[1]) {
1520 send_error(client, EPWMD_COMMAND_SYNTAX);
1521 return FALSE;
1524 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1525 return attribute_set(client, req+1);
1526 if (g_ascii_strcasecmp(req[0], "GET") == 0)
1527 return attribute_get(client, req+1);
1528 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1529 return attribute_delete(client, req+1);
1530 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1531 return attribute_list(client, req+1);
1532 else
1533 send_error(client, EPWMD_COMMAND_SYNTAX);
1535 return FALSE;
1538 static gboolean cache_test(const guchar *md5filename, gint reset)
1540 void *p;
1541 file_cache_t f;
1542 glong len;
1544 for (p = shm_data, len = 0; len <= cache_size;) {
1545 memcpy(&f, p, sizeof(file_cache_t));
1547 if (reset == 2) {
1548 memset(&f, 0, sizeof(file_cache_t));
1549 memcpy(p, &f, sizeof(file_cache_t));
1550 p += sizeof(file_cache_t);
1551 len += sizeof(file_cache_t);
1553 if (len + sizeof(file_cache_t) > cache_size)
1554 break;
1555 continue;
1558 if (f.used == TRUE) {
1559 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
1560 if (reset == 1) {
1561 memset(&f, 0, sizeof(file_cache_t));
1562 memcpy(p, &f, sizeof(file_cache_t));
1563 return TRUE;
1566 return (f.key[0]) ? TRUE : FALSE;
1570 p += sizeof(file_cache_t);
1571 len += sizeof(file_cache_t);
1573 if (len + sizeof(file_cache_t) > cache_size)
1574 break;
1577 return (reset == 2) ? TRUE : FALSE;
1580 static gboolean file_exists(const gchar *filename)
1582 if (access(filename, R_OK) == -1)
1583 return FALSE;
1585 return TRUE;
1588 gboolean cache_command(struct client_s *client, gchar **req)
1590 guchar md5file[16];
1592 if (g_ascii_strcasecmp(req[0], "clear") == 0) {
1593 if (!req[1]) {
1594 send_error(client, EPWMD_COMMAND_SYNTAX);
1595 return FALSE;
1598 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1600 if (cache_test(md5file, 1) == FALSE) {
1601 send_error(client, EPWMD_CACHE_NOT_FOUND);
1602 return FALSE;
1605 return TRUE;
1607 else if (g_ascii_strcasecmp(req[0], "clearall") == 0) {
1608 cache_test(client->md5file, 2);
1609 return TRUE;
1611 else if (g_ascii_strcasecmp(req[0], "iscached") == 0) {
1612 if (!req[1]) {
1613 send_error(client, EPWMD_COMMAND_SYNTAX);
1614 return FALSE;
1617 if (file_exists(req[1]) == FALSE) {
1618 send_error(client, EPWMD_FILE_NOT_FOUND);
1619 return FALSE;
1622 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1624 if (cache_test(md5file, 0) == FALSE) {
1625 send_error(client, EPWMD_CACHE_NOT_FOUND);
1626 return FALSE;
1629 return TRUE;
1631 else
1632 send_error(client, EPWMD_COMMAND_SYNTAX);
1634 return FALSE;
1637 gboolean help_command(struct client_s *client, const gchar *what)
1639 gchar *line;
1641 if (!what || !*what)
1642 line =
1643 "NFO Try 'HELP COMMAND' for command help\n"
1644 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
1645 else if (g_ascii_strcasecmp(what, "GET") == 0)
1646 line =
1647 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
1648 "NFO <account> is the account to work on and <element>\n"
1649 "NFO is the element wanted.\n"
1650 "NFO -\n"
1651 "NFO Example: GET isp <TAB> imap <TAB> port\n"
1652 "NFO GET isp <TAB> username\n";
1653 else if (g_ascii_strcasecmp(what, "QUIT") == 0)
1654 line =
1655 "NFO syntax: QUIT\n"
1656 "NFO close the connection\n";
1657 else if (g_ascii_strcasecmp(what, "DELETE") == 0)
1658 line =
1659 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
1660 else if (g_ascii_strcasecmp(what, "STORE") == 0)
1661 line =
1662 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
1663 "NFO <account> is the account to work on and <element>\n"
1664 "NFO is the element to create or modify\n"
1665 "NFO -\n"
1666 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
1667 "NFO STORE isp <TAB> username <TAB> someuser\n";
1668 else if (g_ascii_strcasecmp(what, "OPEN") == 0)
1669 line =
1670 "NFO syntax: OPEN <filename> [<key>]\n"
1671 "NFO opens a (new) file\n";
1672 else if (g_ascii_strcasecmp(what, "LIST") == 0)
1673 line =
1674 "NFO syntax: LIST [account]\n"
1675 "NFO shows available accounts or account elements\n";
1676 else if (g_ascii_strcasecmp(what, "ATTR") == 0)
1677 line =
1678 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
1679 "NFO ATTR SET NAME account value\n"
1680 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
1681 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
1682 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
1683 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
1684 "NFO ATTR LIST account[<TAB>element[...]]\n";
1685 else if (g_ascii_strcasecmp(what, "SAVE") == 0)
1686 line =
1687 "NFO syntax: SAVE [<key>]\n"
1688 "NFO save any changes to the opened file using <key>\n";
1689 else if (g_ascii_strcasecmp(what, "CACHE") == 0)
1690 line =
1691 "NFO syntax: CACHE [CLEARALL | CLEAR <filename> | ISCACHED <filename>]\n"
1692 "NFO tests or clears the cache entry for <filename>\n";
1693 else if (g_ascii_strcasecmp(what, "DUMP") == 0)
1694 line =
1695 "NFO syntax: DUMP\n"
1696 "NFO shows the in memory XML document\n";
1697 else {
1698 send_error(client, EPWMD_COMMAND_SYNTAX);
1699 return FALSE;
1702 send_to_client(client, "%sOK \n", line);
1703 return TRUE;
1706 gboolean dump_command(struct client_s *client)
1708 xmlChar *xml;
1709 gssize len;
1711 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
1712 send_to_client(client, "BEGIN %li\n%s", len, xml);
1713 memset(xml, 0, len);
1714 xmlFree(xml);
1715 return TRUE;