Added a libpwmd manual page.
[pwmd.git] / src / commands.c
blobc2c1cee0712bc5f00ecac873faa750640d4fa984
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.when = f.timeout;
111 memcpy(p, &f, sizeof(file_cache_t));
112 return TRUE;
115 p += sizeof(file_cache_t);
116 len += sizeof(file_cache_t);
118 if (len + sizeof(file_cache_t) > cache_size)
119 break;
122 return FALSE;
125 gboolean cache_set_timeout(const guchar *md5filename, glong timeout)
127 void *p;
128 file_cache_t f;
129 glong len;
131 for (p = shm_data, len = 0; len <= cache_size;) {
132 memcpy(&f, p, sizeof(file_cache_t));
134 if (md5filename) {
135 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
136 f.when = f.timeout = timeout;
137 memcpy(p, &f, sizeof(file_cache_t));
138 return TRUE;
141 else {
142 f.when = f.timeout = timeout;
143 memcpy(p, &f, sizeof(file_cache_t));
146 p += sizeof(file_cache_t);
147 len += sizeof(file_cache_t);
149 if (len + sizeof(file_cache_t) > cache_size)
150 break;
153 return (md5filename) ? FALSE : TRUE;
156 static gboolean cache_update_key(const guchar *md5filename, const guchar *shakey)
158 void *p;
159 file_cache_t f;
160 glong len;
162 for (p = shm_data, len = 0; len <= cache_size;) {
163 memcpy(&f, p, sizeof(file_cache_t));
165 if (f.used == TRUE) {
166 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
167 memcpy(&f.key, shakey, sizeof(f.key));
168 memcpy(p, &f, sizeof(file_cache_t));
169 return TRUE;
173 p += sizeof(file_cache_t);
174 len += sizeof(file_cache_t);
176 if (len + sizeof(file_cache_t) > cache_size)
177 break;
180 return cache_add_file(md5filename, shakey);
183 static gint cache_valid_key(const guchar *key, gsize len)
185 gint b;
187 for (b = 0; b < len; b++) {
188 if (key[b])
189 return TRUE;
192 return FALSE;
195 static gboolean cache_get_key(const guchar *md5file, guchar *shakey)
197 void *p;
198 file_cache_t f;
199 glong len;
201 for (p = shm_data, len = 0; len <= cache_size;) {
202 memcpy(&f, p, sizeof(file_cache_t));
205 * The slot may be used but not yet contain a key.
207 if (f.used == TRUE) {
208 if (memcmp(&f.filename, md5file, sizeof(f.filename)) == 0) {
209 if (cache_valid_key(f.key, sizeof(f.key)) == FALSE)
210 return FALSE;
212 memcpy(shakey, &f.key, sizeof(f.key));
213 return TRUE;
217 p += sizeof(file_cache_t);
218 len += sizeof(file_cache_t);
220 if (len + sizeof(file_cache_t) > cache_size)
221 break;
224 return FALSE;
227 gint open_file(const gchar *filename, struct stat *st)
229 gint fd;
231 if ((fd = open(filename, O_RDONLY)) == -1)
232 return -1;
234 if (stat(filename, st) == -1) {
235 close(fd);
236 return -1;
239 return fd;
242 gboolean open_command(struct client_s *client, gchar **req)
244 gint fd;
245 struct stat st;
246 gchar *inbuf;
247 const gchar *filename = req[0];
248 guchar shakey[gcrykeysize];
249 guchar tkey[gcrykeysize];
250 gint cached = 0;
251 gsize insize = 0;
252 guint iter;
253 gint timeout;
254 gchar filebuf[PATH_MAX], *p, *p2;
255 gint error;
256 gint reset_timeout;
257 struct file_header_s {
258 guint iter;
259 guchar iv[gcryblocksize];
260 } file_header;
262 if (!filename || !*filename) {
263 send_error(client, EPWMD_COMMAND_SYNTAX);
264 return FALSE;
267 if (valid_filename(filename) == FALSE) {
268 send_error(client, EPWMD_INVALID_FILENAME);
269 return FALSE;
272 p = get_key_file_string("default", "data_directory");
273 p2 = expand_homedir(p);
274 g_free(p);
275 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
276 g_free(p2);
278 if (stat(filebuf, &st) == 0) {
279 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
280 log_write("%s: %s", filename, pwmd_strerror(EPWMD_NOT_A_FILE));
281 send_to_client(client, "ERR %03i %s\n", EPWMD_NOT_A_FILE, pwmd_strerror(EPWMD_NOT_A_FILE));
282 return FALSE;
285 client->mtime = st.st_mtime;
288 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
291 * New files don't need a key.
293 if (access(filebuf, R_OK|W_OK) != 0) {
294 if (errno != ENOENT) {
295 error = errno;
296 log_write("%s: %s", filename, strerror(errno));
297 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename,
298 strerror(error));
299 return FALSE;
301 new_doc:
302 if ((client->xml = new_document()) == NULL) {
303 error = errno;
304 log_write("%s", strerror(errno));
305 send_to_client(client, "ERR %03i malloc(): %s\n", EPWMD_ERROR,
306 strerror(error));
307 return FALSE;
310 client->len = strlen(client->xml);
312 if (cache_add_file(client->md5file, NULL) == FALSE) {
313 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
314 send_error(client, EPWMD_MAX_SLOTS);
315 return FALSE;
318 client->filename = g_strdup(filename);
319 return parse_xml(client);
322 if ((fd = open_file(filebuf, &st)) == -1) {
323 error = errno;
324 log_write("%s: %s", filename, strerror(errno));
325 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(error));
326 return FALSE;
329 if (st.st_size == 0)
330 goto new_doc;
332 if (cache_get_key(client->md5file, shakey) == TRUE)
333 cached = 1;
334 else {
336 * No key specified and no matching filename found in the cache.
338 if (!req[1] || !*req[1]) {
339 close(fd);
340 send_error(client, EPWMD_KEY);
341 return FALSE;
345 insize = st.st_size - sizeof(struct file_header_s);
346 read(fd, &file_header, sizeof(struct file_header_s));
347 inbuf = gcry_malloc(insize);
348 read(fd, inbuf, insize);
349 close(fd);
351 again:
352 if (!cached) {
353 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
354 memset(req[1], 0, strlen(req[1]));
357 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
358 sizeof(file_header.iv)))) {
359 gcry_free(inbuf);
360 memset(shakey, 0, sizeof(shakey));
361 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
362 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
363 return FALSE;
366 if ((gcryerrno = gcry_cipher_setkey(client->gh, shakey, gcrykeysize))) {
367 gcry_free(inbuf);
368 memset(shakey, 0, sizeof(shakey));
369 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
370 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
371 return FALSE;
374 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
375 if (cached) {
376 cached = 0;
377 goto again;
380 memset(shakey, 0, sizeof(shakey));
381 gcry_free(inbuf);
382 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
383 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
384 return FALSE;
387 memcpy(tkey, shakey, sizeof(tkey));
388 tkey[0] ^= 1;
390 if ((gcryerrno = gcry_cipher_setkey(client->gh, tkey, gcrykeysize))) {
391 memset(tkey, 0, sizeof(tkey));
392 gcry_free(inbuf);
393 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
394 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
395 return FALSE;
398 iter = file_header.iter;
400 while (iter-- > 0) {
401 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
402 sizeof(file_header.iv)))) {
403 memset(tkey, 0, sizeof(tkey));
404 gcry_free(inbuf);
405 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
406 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
407 return FALSE;
410 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
411 if (cached) {
412 cached = 0;
413 goto again;
416 memset(tkey, 0, sizeof(tkey));
417 gcry_free(inbuf);
418 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
419 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
420 return FALSE;
424 memset(tkey, 0, sizeof(tkey));
425 client->xml = inbuf;
426 client->len = insize;
428 if (g_strncasecmp(client->xml, "<?xml version=\"1.0\"?>", 21) != 0) {
429 send_error(client, EPWMD_BADKEY);
430 return FALSE;
433 if (!cached) {
434 if (cache_add_file(client->md5file, shakey) == FALSE) {
435 memset(shakey, 0, sizeof(shakey));
436 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
437 send_error(client, EPWMD_MAX_SLOTS);
438 return FALSE;
442 memset(shakey, 0, sizeof(shakey));
443 client->filename = g_strdup(filename);
445 if (!cached) {
446 timeout = get_key_file_integer(client->filename, "cache_timeout");
447 cache_set_timeout(client->md5file, timeout);
449 else {
450 if ((reset_timeout = get_key_file_boolean(client->filename,
451 "cache_reset_timeout")) == TRUE)
452 cache_reset_timeout(client->md5file);
455 return parse_xml(client);
459 * client->reader should be at the position in the document where the element
460 * search should start. It won't search past the closing account element.
462 static gboolean find_elements(struct client_s *client, xmlTextReaderPtr reader,
463 gchar **req, gint quiet)
465 gint i;
467 if (!req || !req[0]) {
468 if (!quiet)
469 send_error(client, EPWMD_COMMAND_SYNTAX);
470 return FALSE;
473 for (i = 0; req[i]; i++) {
474 if (find_element(reader, req[i], req[i+1] != NULL) == FALSE) {
475 if (!quiet)
476 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
478 return FALSE;
482 return TRUE;
485 gboolean do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
486 const gchar *filename, const xmlChar *data, size_t insize,
487 const guchar *shakey, guint iter)
489 gsize len = insize;
490 gint fd;
491 void *inbuf;
492 guchar tkey[gcrykeysize];
493 gchar *p;
494 gint error;
495 struct file_header_s {
496 guint iter;
497 guchar iv[gcryblocksize];
498 } file_header;
500 if (insize / gcryblocksize) {
501 len = (insize / gcryblocksize) * gcryblocksize;
503 if (insize % gcryblocksize)
504 len += gcryblocksize;
507 inbuf = gcry_calloc(1, len);
508 memcpy(inbuf, data, insize);
509 insize = len;
510 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
511 memcpy(tkey, shakey, sizeof(tkey));
512 tkey[0] ^= 1;
514 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
515 gcry_free(inbuf);
516 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
517 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
518 return FALSE;
521 file_header.iter = iter;
523 while (iter-- > 0) {
524 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
525 sizeof(file_header.iv)))) {
526 gcry_free(inbuf);
527 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
528 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
529 return FALSE;
532 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
533 == FALSE) {
534 gcry_free(inbuf);
535 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
536 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
537 return FALSE;
541 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
542 sizeof(file_header.iv)))) {
543 gcry_free(inbuf);
544 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
545 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
546 return FALSE;
549 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
550 gcry_free(inbuf);
551 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
552 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
553 return FALSE;
556 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
557 == FALSE) {
558 gcry_free(inbuf);
559 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
560 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
561 return FALSE;
564 if (filename) {
565 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
566 error = errno;
567 gcry_free(inbuf);
568 p = strrchr(filename, '/');
569 p++;
570 log_write("%s: %s", p, strerror(errno));
571 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, p, strerror(error));
572 return FALSE;
575 else
577 * xml_import() from command line.
579 fd = STDOUT_FILENO;
581 write(fd, &file_header, sizeof(struct file_header_s));
582 write(fd, inbuf, insize);
584 if (filename)
585 close(fd);
587 gcry_free(inbuf);
588 return TRUE;
591 gboolean save_command(struct client_s *client, const gchar *filename, gchar *key)
593 xmlChar *xmlbuf;
594 gint len;
595 gint cached = 0;
596 guchar shakey[gcrykeysize];
597 gint iter;
598 struct stat st;
599 gchar filebuf[PATH_MAX], *p, *p2;
600 gboolean reset_timeout;
602 p = get_key_file_string("default", "data_directory");
603 p2 = expand_homedir(p);
604 g_free(p);
605 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
606 g_free(p2);
608 if (stat(filebuf, &st) == 0 && client->mtime) {
609 if (client->mtime != st.st_mtime) {
610 log_write("%s: %s", filename, pwmd_strerror(EPWMD_FILE_MODIFIED));
611 send_error(client, EPWMD_FILE_MODIFIED);
612 return FALSE;
616 if (!key) {
617 if (cache_get_key(client->md5file, shakey) == FALSE) {
618 send_error(client, EPWMD_KEY);
619 return FALSE;
622 cached = 1;
624 else {
625 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
626 memset(key, 0, strlen(key));
629 xmlDocDumpFormatMemory(client->doc, &xmlbuf, &len, 0);
631 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
632 iter = 0;
634 if (do_xml_encrypt(client, client->gh, filebuf, xmlbuf, len, shakey,
635 iter) == FALSE) {
636 memset(shakey, 0, sizeof(shakey));
637 xmlFree(xmlbuf);
638 return FALSE;
641 xmlFree(xmlbuf);
642 stat(filebuf, &st);
643 client->mtime = st.st_mtime;
645 if (cached) {
646 memset(shakey, 0, sizeof(shakey));
648 if ((reset_timeout = get_key_file_boolean(client->filename,
649 "cache_reset_timeout")) == TRUE)
650 cache_reset_timeout(client->md5file);
652 return TRUE;
655 if (cache_update_key(client->md5file, shakey) == FALSE) {
656 memset(shakey, 0, sizeof(shakey));
657 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
658 send_error(client, EPWMD_MAX_SLOTS);
659 return FALSE;
662 memset(shakey, 0, sizeof(shakey));
663 cache_reset_timeout(client->md5file);
664 return TRUE;
667 static gboolean contains_whitespace(const gchar *str)
669 const gchar *p = str;
671 for (; *p; p++) {
672 if (g_ascii_isspace(*p) == TRUE) {
673 return TRUE;
677 return FALSE;
681 * the 'reader' should be at the element of the document where the elements
682 * 'req' are to be created.
684 static gboolean create_elements(struct client_s *client, xmlTextReaderPtr reader,
685 gchar **req, gint novalue)
687 gint i;
688 gboolean ret = TRUE;
689 xmlNodePtr r;
691 r = xmlTextReaderCurrentNode(reader);
693 if (xmlTextReaderDepth(reader) > 1)
694 r = r->parent;
696 for (i = 0; req[i]; i++) {
697 xmlNodePtr n;
700 * Whitespace is allowed in values or attributes but not in element
701 * names.
703 if (req[i+1] && valid_xml_element((xmlChar *)req[i]) == FALSE) {
704 send_error(client, EPWMD_INVALID_ELEMENT);
705 return FALSE;
709 * The value of the element tree.
711 if (!req[i+1] && !novalue) {
713 * Prevent creating 'text' elements in the root of the account.
715 if (i < 1) {
716 send_error(client, EPWMD_ROOT_TEXT_ELEMENT);
717 return FALSE;
721 * FIXME ?? overwriting an element tree with a text element:
723 * STORE account element element2 value
724 * STORE account element value
726 * would remove the existing element tree. This may be a bug or
727 * feature.
729 xmlNodeSetContent(r, (xmlChar *)req[i]);
730 break;
733 if ((n = find_node(r, (xmlChar *)req[i])) == NULL) {
734 n = xmlNewNode(NULL, (xmlChar *)req[i]);
735 r = xmlAddChild(r, n);
737 else
738 r = n;
740 if (!req[i+1] && novalue)
741 return TRUE;
744 return ret;
748 * FIXME reuse reader handle
750 static gboolean reset_reader(xmlDocPtr doc, xmlTextReaderPtr *reader)
752 if (reader) {
753 xmlFreeTextReader(*reader);
754 *reader = NULL;
757 if ((*reader = xmlReaderWalker(doc)) == NULL)
758 return FALSE;
760 return TRUE;
763 static void delete_node(xmlNodePtr n)
765 xmlUnlinkNode(n);
766 xmlFreeNode(n);
769 gboolean delete_command(struct client_s *client, gchar **req)
771 gint i = 1;
772 xmlNodePtr n;
773 xmlErrorPtr xml_error;
775 if (reset_reader(client->doc, &client->reader) == FALSE) {
776 xml_error = xmlGetLastError();
777 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
778 send_error(client, EPWMD_LIBXML_ERROR);
779 return FALSE;
782 if (find_account(client->reader, req[0]) == FALSE) {
783 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
784 return FALSE;
787 n = xmlTextReaderCurrentNode(client->reader);
790 * No sub-node defined. Remove the entire node (account).
792 if (!req[i]) {
793 if (n)
794 delete_node(n);
795 return TRUE;
799 * Remove matching sub-nodes starting from the root of the account.
801 while (req[i] && find_element(client->reader, req[i++], req[i] != NULL) == TRUE)
802 n = xmlTextReaderCurrentNode(client->reader);
804 if (n && xmlStrcmp(n->name, (xmlChar *)req[i-1]) == 0 &&
805 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_ELEMENT)
806 delete_node(n);
807 else {
808 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
809 return FALSE;
812 return TRUE;
815 gboolean store_command(struct client_s *client, gchar **req)
817 xmlErrorPtr xml_error;
819 again:
820 if (reset_reader(client->doc, &client->reader) == FALSE) {
821 xml_error = xmlGetLastError();
822 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
823 send_error(client, EPWMD_LIBXML_ERROR);
824 return FALSE;
827 if (!req[0]) {
828 send_error(client, EPWMD_COMMAND_SYNTAX);
829 return FALSE;
832 if (find_account(client->reader, req[0]) == FALSE) {
833 if (contains_whitespace(req[0]) == TRUE) {
834 send_error(client, EPWMD_INVALID_ELEMENT);
835 return FALSE;
838 if (new_account(client->doc, req[0]) == FALSE) {
839 send_error(client, EPWMD_ERROR);
840 return FALSE;
843 goto again;
846 xmlTextReaderNext(client->reader);
847 return create_elements(client, client->reader, req+1, 0);
850 static gboolean do_get_command(struct client_s *client, xmlTextReaderPtr *reader,
851 gchar **req, xmlChar **content, gint quiet, gint list)
853 xmlNodePtr n;
854 xmlAttrPtr a;
855 gchar **nreq;
856 gboolean ret;
857 xmlChar *p;
858 xmlErrorPtr xml_error;
859 gint literal = 0;
861 if (reset_reader(client->doc, reader) == FALSE) {
862 if (!quiet) {
863 xml_error = xmlGetLastError();
864 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
865 send_error(client, EPWMD_LIBXML_ERROR);
868 return FALSE;
871 if (*req[0] == '!')
872 literal = 1;
874 if (find_account(*reader, (literal) ? req[0] + 1 : req[0]) == FALSE) {
875 if (!quiet && !list)
876 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
877 return FALSE;
881 * May be an account with only a "target" attribute.
883 if (req[1]) {
884 if (find_elements(client, *reader, req + 1, (list) ? 1 : quiet) == FALSE)
885 return FALSE;
888 if ((n = xmlTextReaderCurrentNode(*reader)) == NULL) {
889 if (!quiet) {
890 xml_error = xmlGetLastError();
891 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
892 send_error(client, EPWMD_LIBXML_ERROR);
895 return FALSE;
899 * If the current element has a "target" attribute, the value of the
900 * attribute is an element path somewhere else in the document. Use this
901 * value and not any TEXT element value. The value may be another element
902 * with a target attribute and this function will recurse until a
903 * non-target attribute in the element is found.
905 if (!literal && (a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
906 if ((p = xmlNodeGetContent(a->children)) != NULL) {
907 if (strchr((gchar *)p, '\t') != NULL) {
908 if ((nreq = split_input_line((gchar *)p, "\t", 0)) == NULL) {
909 xmlFree(p);
911 if (!quiet)
912 send_error(client, EPWMD_INVALID_ELEMENT);
913 return FALSE;
916 else {
917 if ((nreq = split_input_line((gchar *)p, " ", 0)) == NULL) {
918 xmlFree(p);
920 if (!quiet)
921 send_error(client, EPWMD_INVALID_ELEMENT);
922 return FALSE;
926 xmlFree(p);
927 ret = do_get_command(client, reader, nreq, content, quiet, list);
928 g_strfreev(nreq);
929 return ret;
933 switch (xmlTextReaderNext(*reader)) {
934 case -1:
935 if (!quiet) {
936 xml_error = xmlGetLastError();
937 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
938 send_error(client, EPWMD_LIBXML_ERROR);
941 return FALSE;
942 case 0:
943 if (!quiet)
944 send_error(client, EPWMD_EMPTY_ELEMENT);
945 return FALSE;
946 default:
947 break;
950 switch (xmlTextReaderNodeType(*reader)) {
951 case XML_READER_TYPE_END_ELEMENT:
953 * May be an empty element after an ATTR DELETE target command.
955 return TRUE;
956 case XML_READER_TYPE_TEXT:
957 break;
958 case -1:
959 if (!quiet) {
960 xml_error = xmlGetLastError();
961 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
962 send_error(client, EPWMD_LIBXML_ERROR);
965 return FALSE;
966 default:
967 if (!quiet) {
968 if (n->children && !list)
969 send_error(client, EPWMD_TRAILING_ELEMENT);
970 else if (!n->children && !list)
971 send_error(client, EPWMD_INVALID_ELEMENT);
973 return FALSE;
976 if ((*content = xmlNodeGetContent(n)) == NULL) {
977 if (!quiet)
978 send_error(client, EPWMD_EMPTY_ELEMENT);
979 return FALSE;
982 return TRUE;
986 * Retrieves the value associated with the element tree 'req'.
988 gboolean get_command(struct client_s *client, xmlTextReaderPtr *reader,
989 gchar **req, gint quiet)
991 xmlChar *content = NULL;
993 if (do_get_command(client, reader, req, &content, quiet, 0) == FALSE)
994 return FALSE;
996 if (!content) {
997 if (!quiet)
998 send_error(client, EPWMD_EMPTY_ELEMENT);
1000 return FALSE;
1003 send_to_client(client, "BEGIN %li\n%s\nOK \n", xmlStrlen(content), content);
1004 xmlFree(content);
1005 return TRUE;
1008 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1010 xmlChar *p = path;
1011 gint n;
1012 gchar *buf;
1014 if (!p)
1015 return NULL;
1017 for (n = 0; *p && n < 3; p++) {
1018 if (*p == '/')
1019 n++;
1022 if (strstr((gchar *)p, "text()") != NULL)
1023 p[xmlStrlen(p) - 7] = 0;
1025 for (n = 0; p[n]; n++) {
1026 if (p[n] == '/')
1027 p[n] = '\t';
1030 buf = g_strdup_printf("%s\t%s", account, p);
1031 return buf;
1034 static gboolean append_to_array(gchar ***array, gint *total, const gchar *str)
1036 gchar **a;
1037 gint t = *total;
1039 if ((a = g_realloc(*array, (t + 2) * sizeof(gchar *))) == NULL)
1040 return FALSE;
1042 a[t++] = g_strdup(str);
1043 a[t] = NULL;
1044 *total = t;
1045 *array = a;
1046 return TRUE;
1049 gboolean list_command(struct client_s *client, gchar *str)
1051 gchar *dst = NULL;
1052 gchar *p = str;
1053 gchar **elements = NULL;
1054 gint pwmd_errno = -1;
1055 gchar *account;
1056 gint total = 0;
1057 xmlChar *path = NULL;
1058 xmlAttrPtr a;
1059 xmlNodePtr n;
1060 xmlChar *content;
1061 xmlTextReaderPtr r = NULL;
1062 gchar **req = NULL, **nreq;
1063 gboolean ret;
1064 gint type;
1065 gchar *line;
1066 xmlErrorPtr xml_error;
1067 gint depth = 0;
1069 if (reset_reader(client->doc, &client->reader) == FALSE) {
1070 xml_error = xmlGetLastError();
1071 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1072 send_error(client, EPWMD_LIBXML_ERROR);
1073 return FALSE;
1076 if (strchr(p, ' ') == NULL) {
1077 list_only:
1078 if (list_accounts(client->reader, &dst, &pwmd_errno) == FALSE) {
1079 send_error(client, pwmd_errno);
1080 return FALSE;
1082 else {
1083 send_to_client(client, "BEGIN %i\n%s\nOK \n",
1084 g_utf8_strlen(dst, -1), dst);
1085 memset(dst, 0, strlen(dst));
1086 g_free(dst);
1089 return TRUE;
1092 p = str + 5;
1094 while (*p && isspace(*p))
1095 p++;
1097 if (!*p)
1098 goto list_only;
1100 if (strchr(p, '\t') != NULL) {
1101 if ((req = split_input_line(p, "\t", 0)) == NULL) {
1102 send_error(client, EPWMD_COMMAND_SYNTAX);
1103 return FALSE;
1106 if (find_account(client->reader, req[0]) == FALSE) {
1107 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1108 return FALSE;
1111 if (find_elements(client, client->reader, req + 1, 0) == FALSE) {
1112 g_strfreev(req);
1113 return FALSE;
1116 depth = xmlTextReaderDepth(client->reader);
1118 else {
1119 if (find_account(client->reader, p) == FALSE) {
1120 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1121 return FALSE;
1125 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1126 xml_error = xmlGetLastError();
1127 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1128 send_error(client, EPWMD_LIBXML_ERROR);
1129 return FALSE;
1132 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
1133 if (reset_reader(client->doc, &client->reader) == FALSE) {
1134 send_error(client, EPWMD_LIBXML_ERROR);
1135 return FALSE;
1138 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1139 if (find_account(client->reader, (gchar *)content) == FALSE) {
1140 xmlFree(content);
1141 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1142 return FALSE;
1145 xmlFree(content);
1149 account = (req) ? g_strdup(req[0]) : g_strdup(p);
1151 if (req)
1152 g_strfreev(req);
1154 while (xmlTextReaderNext(client->reader) == 1) {
1155 again:
1156 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1157 xml_error = xmlGetLastError();
1158 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1159 send_error(client, EPWMD_LIBXML_ERROR);
1160 return FALSE;
1163 if (xmlTextReaderDepth(client->reader) == 1 &&
1164 xmlStrcmp(n->name, (xmlChar *)"account") == 0 &&
1165 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1166 break;
1168 if (depth && depth == xmlTextReaderDepth(client->reader) &&
1169 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1170 break;
1173 * If the current element has a "target" attribute, the value of the
1174 * attribute is an element path somewhere else in the document. Use this
1175 * value and not any TEXT element value.
1177 type = xmlTextReaderNodeType(client->reader);
1178 a = xmlHasProp(n, (xmlChar *)"target");
1180 if (type == XML_READER_TYPE_ELEMENT && a) {
1181 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1182 path = xmlGetNodePath(n);
1184 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
1185 if (elements)
1186 g_strfreev(elements);
1188 xmlFree(path);
1189 xmlFree(content);
1190 send_error(client, EPWMD_INVALID_ELEMENT);
1191 return FALSE;
1194 xmlFree(content);
1195 r = NULL;
1197 if ((ret = do_get_command(client, &r, nreq, &content, 0, 1)) == TRUE) {
1198 if (content && *content) {
1199 line = element_path_to_req(account, path);
1200 xmlFree(content);
1202 if (append_to_array(&elements, &total, line) == FALSE) {
1203 if (elements)
1204 g_strfreev(elements);
1206 xmlFree(path);
1207 memset(line, 0, g_utf8_strlen(line, -1));
1208 g_free(line);
1209 g_strfreev(nreq);
1210 xmlFreeTextReader(r);
1211 send_error(client, EPWMD_ERROR);
1212 return FALSE;
1215 memset(line, 0, g_utf8_strlen(line, -1));
1216 g_free(line);
1219 if (xmlTextReaderNext(client->reader) == 1) {
1220 if (xmlTextReaderNodeType(client->reader) !=
1221 XML_READER_TYPE_TEXT) {
1222 g_strfreev(nreq);
1223 xmlFreeTextReader(r);
1224 xmlFree(path);
1225 goto again;
1230 g_strfreev(nreq);
1231 xmlFreeTextReader(r);
1232 xmlFree(path);
1233 continue;
1237 if (type == XML_READER_TYPE_TEXT) {
1238 xmlChar *np = xmlGetNodePath(n);
1240 line = element_path_to_req(account, np);
1241 xmlFree(np);
1242 append_to_array(&elements, &total, line);
1243 memset(line, 0, g_utf8_strlen(line, -1));
1244 g_free(line);
1248 if (!elements) {
1249 send_error(client, EPWMD_EMPTY_ELEMENT);
1250 g_free(account);
1251 return FALSE;
1254 g_free(account);
1255 line = g_strjoinv("\n", elements);
1256 send_to_client(client, "BEGIN %li\n%s\nOK \n",
1257 g_utf8_strlen(line, -1), line);
1258 g_strfreev(elements);
1259 g_free(line);
1260 return TRUE;
1264 * The client->reader handle should be at the element in the document where
1265 * the attribute will be created or modified.
1267 static gboolean add_attribute(struct client_s *client, const gchar *name, const gchar *value)
1269 xmlAttrPtr a;
1270 xmlNodePtr n;
1271 xmlErrorPtr xml_error;
1273 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1274 xml_error = xmlGetLastError();
1275 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1276 send_error(client, EPWMD_LIBXML_ERROR);
1277 return FALSE;
1280 if ((a = xmlHasProp(n, (xmlChar *)name)) == NULL)
1281 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)value);
1282 else
1283 xmlNodeSetContent(a->children, (xmlChar *)value);
1285 return TRUE;
1289 * req[0] - element path
1291 static gboolean attribute_list(struct client_s *client, gchar **req)
1293 gchar **attrlist = NULL;
1294 gint i = 0;
1295 gchar **epath = NULL;
1296 xmlAttrPtr a;
1297 xmlNodePtr n, an;
1298 gchar *line;
1299 xmlErrorPtr xml_error;
1301 if (!req || !req[0]) {
1302 send_error(client, EPWMD_COMMAND_SYNTAX);
1303 return FALSE;
1306 if ((epath = split_input_line(req[0], "\t", 0)) == NULL) {
1308 * The first argument may be only an account.
1310 if ((epath = split_input_line(req[0], " ", 0)) == NULL) {
1311 send_error(client, EPWMD_COMMAND_SYNTAX);
1312 return FALSE;
1316 if (reset_reader(client->doc, &client->reader) == FALSE) {
1317 xml_error = xmlGetLastError();
1318 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1319 send_error(client, EPWMD_LIBXML_ERROR);
1320 goto blah;
1323 if (find_account(client->reader, epath[0]) == FALSE) {
1324 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1325 goto blah;
1328 if (epath[1]) {
1329 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1330 goto blah;
1333 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1334 xml_error = xmlGetLastError();
1335 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1336 send_error(client, EPWMD_LIBXML_ERROR);
1337 goto blah;
1340 for (a = n->properties; a; a = a->next) {
1341 if ((attrlist = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1342 log_write("%s(%i): g_realloc() failed", __FILE__, __LINE__);
1343 send_error(client, EPWMD_ERROR);
1344 goto blah;
1347 an = a->children;
1348 attrlist[i++] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1349 attrlist[i] = NULL;
1352 if (!attrlist) {
1353 send_error(client, EPWMD_EMPTY_ELEMENT);
1354 goto blah;
1357 line = g_strjoinv("\n", attrlist);
1358 send_to_client(client, "BEGIN %li\n%s\n", g_utf8_strlen(line, -1), line);
1359 g_free(line);
1360 g_strfreev(epath);
1361 g_strfreev(attrlist);
1362 return TRUE;
1363 blah:
1364 g_strfreev(epath);
1365 return FALSE;
1369 * req[0] - attribute
1370 * req[1] - element path
1372 static gboolean attribute_delete(struct client_s *client, gchar **req)
1374 xmlAttrPtr a;
1375 xmlNodePtr n;
1376 gchar **epath = NULL;
1377 xmlErrorPtr xml_error;
1379 if (!req || !req[0] || !req[1]) {
1380 send_error(client, EPWMD_COMMAND_SYNTAX);
1381 return FALSE;
1384 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1386 * The first argument may be only an account.
1388 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1389 send_error(client, EPWMD_COMMAND_SYNTAX);
1390 return FALSE;
1395 * Don't remove the NAME attribute for the account element.
1397 if (!epath[1] && g_ascii_strcasecmp(req[0], "NAME") == 0) {
1398 send_error(client, EPWMD_ATTR_SYNTAX);
1399 goto blah;
1402 if (reset_reader(client->doc, &client->reader) == FALSE) {
1403 xml_error = xmlGetLastError();
1404 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1405 send_error(client, EPWMD_LIBXML_ERROR);
1406 goto blah;
1409 if (find_account(client->reader, epath[0]) == FALSE) {
1410 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1411 goto blah;
1414 if (epath[1]) {
1415 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1416 goto blah;
1419 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1420 xml_error = xmlGetLastError();
1421 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1422 send_error(client, EPWMD_LIBXML_ERROR);
1423 goto blah;
1426 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL) {
1427 send_error(client, EPWMD_ATTR_NOT_FOUND);
1428 goto blah;
1431 if (xmlRemoveProp(a) == -1) {
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 g_strfreev(epath);
1439 return TRUE;
1440 blah:
1441 g_strfreev(epath);
1442 return FALSE;
1446 * req[0] - source element path
1447 * req[1] - destination element path
1449 static gboolean target_attribute(struct client_s *client, gchar **req)
1451 gchar **src, **dst, *line;
1452 xmlErrorPtr xml_error;
1454 if (!req || !req[0] || !req[1]) {
1455 send_error(client, EPWMD_COMMAND_SYNTAX);
1456 return FALSE;
1459 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1461 * The first argument may be only an account.
1463 if ((src = split_input_line(req[0], " ", 0)) == NULL) {
1464 send_error(client, EPWMD_COMMAND_SYNTAX);
1465 return FALSE;
1469 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1471 * The first argument may be only an account.
1473 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1474 send_error(client, EPWMD_COMMAND_SYNTAX);
1475 g_strfreev(src);
1476 goto blah;
1481 * Prevent an element tree pointing to only and account. Accounts require
1482 * at least one element. Accounts pointing to accounts are allowed.
1484 if ((!src[1] && dst[1]) || (!dst[1] && src[1])) {
1485 send_error(client, EPWMD_ATTR_SYNTAX);
1486 g_strfreev(src);
1487 g_strfreev(dst);
1488 goto blah;
1491 if (reset_reader(client->doc, &client->reader) == FALSE) {
1492 xml_error = xmlGetLastError();
1493 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1494 send_error(client, EPWMD_LIBXML_ERROR);
1495 g_strfreev(src);
1496 g_strfreev(dst);
1497 goto blah;
1501 * Make sure the destination element path exists.
1503 if (find_account(client->reader, dst[0]) == FALSE) {
1504 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1505 g_strfreev(src);
1506 g_strfreev(dst);
1507 goto blah;
1510 if (dst[1]) {
1511 if (find_elements(client, client->reader, dst+1, 0) == FALSE) {
1512 g_strfreev(src);
1513 g_strfreev(dst);
1514 goto blah;
1518 if (reset_reader(client->doc, &client->reader) == FALSE) {
1519 xml_error = xmlGetLastError();
1520 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1521 send_error(client, EPWMD_LIBXML_ERROR);
1522 g_strfreev(src);
1523 g_strfreev(dst);
1524 goto blah;
1528 * If the source element tree doesn't exist, create it.
1530 if (find_account(client->reader, src[0]) == FALSE) {
1531 if (new_account(client->doc, src[0]) == 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;
1540 if (reset_reader(client->doc, &client->reader) == FALSE) {
1541 xml_error = xmlGetLastError();
1542 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1543 send_error(client, EPWMD_LIBXML_ERROR);
1544 g_strfreev(src);
1545 g_strfreev(dst);
1546 goto blah;
1549 if (find_account(client->reader, src[0]) == FALSE) {
1550 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1551 g_strfreev(src);
1552 g_strfreev(dst);
1553 goto blah;
1557 if (src[1]) {
1558 if (find_elements(client, client->reader, src+1, 1) == FALSE) {
1559 if (reset_reader(client->doc, &client->reader) == FALSE) {
1560 xml_error = xmlGetLastError();
1561 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1562 send_error(client, EPWMD_LIBXML_ERROR);
1563 g_strfreev(src);
1564 g_strfreev(dst);
1565 goto blah;
1568 if (find_account(client->reader, src[0]) == FALSE) {
1569 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1570 g_strfreev(src);
1571 g_strfreev(dst);
1572 goto blah;
1575 xmlTextReaderNext(client->reader);
1577 if (create_elements(client, client->reader, src+1, 1) == FALSE) {
1578 g_strfreev(src);
1579 g_strfreev(dst);
1580 goto blah;
1583 if (reset_reader(client->doc, &client->reader) == FALSE) {
1584 xml_error = xmlGetLastError();
1585 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1586 send_error(client, EPWMD_LIBXML_ERROR);
1587 g_strfreev(src);
1588 g_strfreev(dst);
1589 goto blah;
1592 if (find_account(client->reader, src[0]) == FALSE) {
1593 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1594 g_strfreev(src);
1595 g_strfreev(dst);
1596 goto blah;
1599 if (find_elements(client, client->reader, src+1, 0) == FALSE) {
1600 g_strfreev(src);
1601 g_strfreev(dst);
1602 goto blah;
1607 line = g_strjoinv("\t", dst);
1609 if (add_attribute(client, "target", line) == FALSE) {
1610 g_free(line);
1611 g_strfreev(src);
1612 g_strfreev(dst);
1613 goto blah;
1616 g_strfreev(src);
1617 g_strfreev(dst);
1618 g_free(line);
1619 return TRUE;
1620 blah:
1621 g_strfreev(src);
1622 g_strfreev(dst);
1623 return FALSE;
1627 * req[0] - account name
1628 * req[1] - new name
1630 static gboolean name_attribute(struct client_s *client, gchar **req)
1632 xmlErrorPtr xml_error;
1634 if (reset_reader(client->doc, &client->reader) == FALSE) {
1635 xml_error = xmlGetLastError();
1636 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1637 send_error(client, EPWMD_LIBXML_ERROR);
1638 return FALSE;
1641 if (find_account(client->reader, req[0]) == FALSE) {
1642 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1643 return FALSE;
1646 if (strcmp(req[0], req[1]) == 0)
1647 return TRUE;
1649 if (reset_reader(client->doc, &client->reader) == FALSE) {
1650 xml_error = xmlGetLastError();
1651 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1652 send_error(client, EPWMD_LIBXML_ERROR);
1653 return FALSE;
1657 * Will not overwrite an existing account.
1659 if (find_account(client->reader, req[1]) == TRUE) {
1660 send_error(client, EPWMD_ACCOUNT_EXISTS);
1661 return FALSE;
1664 if (reset_reader(client->doc, &client->reader) == FALSE) {
1665 xml_error = xmlGetLastError();
1666 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1667 send_error(client, EPWMD_LIBXML_ERROR);
1668 return FALSE;
1671 if (find_account(client->reader, req[0]) == FALSE) {
1672 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1673 return FALSE;
1677 * Whitespace not allowed in account names.
1679 if (contains_whitespace(req[1]) == TRUE) {
1680 send_error(client, EPWMD_ATTR_SYNTAX);
1681 return FALSE;
1684 return add_attribute(client, "name", req[1]);
1688 * req[0] - attribute
1689 * req[1] - element path
1691 * If the element has a "target" attribute it won't be "followed".
1693 static gboolean attribute_get(struct client_s *client, gchar **req)
1695 xmlNodePtr n;
1696 xmlChar *a;
1697 gchar **nreq = NULL;
1698 xmlErrorPtr xml_error;
1700 if (!req || !req[0] || !req[1]) {
1701 send_error(client, EPWMD_COMMAND_SYNTAX);
1702 return FALSE;
1705 if (strchr(req[1], '\t')) {
1706 if ((nreq = split_input_line(req[1], "\t", 0)) == NULL) {
1707 send_error(client, EPWMD_COMMAND_SYNTAX);
1708 return FALSE;
1711 else {
1712 if ((nreq = split_input_line(req[1], " ", 0)) == NULL) {
1713 send_error(client, EPWMD_COMMAND_SYNTAX);
1714 return FALSE;
1718 if (reset_reader(client->doc, &client->reader) == FALSE) {
1719 xml_error = xmlGetLastError();
1720 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1721 g_strfreev(nreq);
1722 send_error(client, EPWMD_LIBXML_ERROR);
1723 return FALSE;
1726 if (find_account(client->reader, nreq[0]) == FALSE) {
1727 g_strfreev(nreq);
1728 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1729 return FALSE;
1732 if (nreq[1]) {
1733 if (find_elements(client, client->reader, nreq + 1, 0) == FALSE) {
1734 g_strfreev(nreq);
1735 return FALSE;
1739 g_strfreev(nreq);
1741 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1742 xml_error = xmlGetLastError();
1743 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1744 send_error(client, EPWMD_LIBXML_ERROR);
1745 return FALSE;
1748 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL) {
1749 send_error(client, EPWMD_ATTR_NOT_FOUND);
1750 return FALSE;
1753 send_to_client(client, "BEGIN %li\n%s\n", xmlStrlen(a), a);
1754 xmlFree(a);
1755 return TRUE;
1759 * req[0] - attribute
1760 * req[1] - element path
1761 * req[2] - value
1763 static gboolean attribute_set(struct client_s *client, gchar **req)
1765 gchar **epath = NULL;
1766 xmlErrorPtr xml_error;
1768 if (!req || !req[0] || !req[1] || !req[2]) {
1769 send_error(client, EPWMD_COMMAND_SYNTAX);
1770 return FALSE;
1774 * Reserved attribute names.
1776 if (strcmp(req[0], "name") == 0) {
1778 * Only reserved for the account element. Not the rest of the
1779 * document.
1781 if (strchr(req[1], '\t') == NULL)
1782 return name_attribute(client, req + 1);
1784 else if (strcmp(req[0], "target") == 0)
1785 return target_attribute(client, req + 1);
1787 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1789 * The first argument may be only an account.
1791 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1792 send_error(client, EPWMD_COMMAND_SYNTAX);
1793 return FALSE;
1797 if (reset_reader(client->doc, &client->reader) == FALSE) {
1798 xml_error = xmlGetLastError();
1799 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1800 send_error(client, EPWMD_LIBXML_ERROR);
1801 goto blah;
1804 if (find_account(client->reader, epath[0]) == FALSE) {
1805 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1806 goto blah;
1809 if (epath[1]) {
1810 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1811 goto blah;
1814 g_strfreev(epath);
1815 return add_attribute(client, req[0], req[2]);
1816 blah:
1817 g_strfreev(epath);
1818 return FALSE;
1822 * req[0] - command
1823 * req[1] - attribute name or element path if command is LIST
1824 * req[2] - element path
1825 * req[2] - element path or value
1827 gboolean attr_command(struct client_s *client, gchar **req)
1829 if (!req || !req[0] || !req[1]) {
1830 send_error(client, EPWMD_COMMAND_SYNTAX);
1831 return FALSE;
1834 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1835 return attribute_set(client, req+1);
1836 if (g_ascii_strcasecmp(req[0], "GET") == 0)
1837 return attribute_get(client, req+1);
1838 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1839 return attribute_delete(client, req+1);
1840 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1841 return attribute_list(client, req+1);
1842 else
1843 send_error(client, EPWMD_COMMAND_SYNTAX);
1845 return FALSE;
1848 gboolean cache_clear(const guchar *md5filename, gint which)
1850 void *p;
1851 file_cache_t f;
1852 glong len;
1854 for (p = shm_data, len = 0; len <= cache_size;) {
1855 memcpy(&f, p, sizeof(file_cache_t));
1857 if (which == 2) {
1858 memset(&f, 0, sizeof(file_cache_t));
1859 memcpy(p, &f, sizeof(file_cache_t));
1861 else if (f.used == TRUE && which == 1) {
1862 if (memcmp(&f.filename, md5filename, sizeof(f.filename)) == 0) {
1863 memset(&f, 0, sizeof(file_cache_t));
1864 memcpy(p, &f, sizeof(file_cache_t));
1865 return TRUE;
1869 p += sizeof(file_cache_t);
1870 len += sizeof(file_cache_t);
1872 if (len + sizeof(file_cache_t) > cache_size)
1873 break;
1876 return (which == 2) ? TRUE : FALSE;
1879 gboolean cache_iscached(const guchar *md5filename)
1881 void *p;
1882 file_cache_t f;
1883 glong len;
1885 for (p = shm_data, len = 0; len <= cache_size;) {
1886 memcpy(&f, p, sizeof(file_cache_t));
1888 if (f.used == TRUE) {
1889 if (memcmp(&f.filename, md5filename, sizeof(f.filename)) == 0)
1890 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 FALSE;
1903 gboolean cache_reset(const guchar *md5filename, gint which)
1905 void *p;
1906 file_cache_t f;
1907 glong len;
1908 guchar md5file[sizeof(f.filename)];
1909 gint timeout;
1911 for (p = shm_data, len = 0; len <= cache_size;) {
1912 memcpy(&f, p, sizeof(file_cache_t));
1913 timeout = f.timeout;
1915 if (which == 2) {
1916 if (f.used == TRUE) {
1917 memcpy(&md5file, f.filename, sizeof(md5file));
1918 memset(&f, 0, sizeof(file_cache_t));
1919 memcpy(&f.filename, &md5file, sizeof(md5file));
1920 f.timeout = f.when = timeout;
1921 f.used = TRUE;
1922 memcpy(p, &f, sizeof(file_cache_t));
1925 else if (f.used == TRUE && which == 1) {
1926 if (memcmp(&f.filename, md5filename, sizeof(f.filename)) == 0) {
1927 memcpy(&md5file, f.filename, sizeof(md5file));
1928 memset(&f, 0, sizeof(file_cache_t));
1929 memcpy(&f.filename, &md5file, sizeof(md5file));
1930 f.timeout = f.when = timeout;
1931 f.used = TRUE;
1932 memcpy(p, &f, sizeof(file_cache_t));
1933 return TRUE;
1937 p += sizeof(file_cache_t);
1938 len += sizeof(file_cache_t);
1940 if (len + sizeof(file_cache_t) > cache_size)
1941 break;
1944 return (which == 2) ? TRUE : FALSE;
1947 static gboolean file_exists(const gchar *filename)
1949 struct stat st;
1950 gchar filebuf[PATH_MAX], *p, *p2;
1952 p = get_key_file_string("default", "data_directory");
1953 p2 = expand_homedir(p);
1954 g_free(p);
1955 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
1956 g_free(p2);
1958 if (access(filebuf, R_OK) == -1)
1959 return FALSE;
1961 stat(filebuf, &st);
1963 if (st.st_size == 0)
1964 return FALSE;
1966 return TRUE;
1969 gboolean cache_command(struct client_s *client, gchar **req)
1971 guchar md5file[16];
1972 glong timeout;
1973 gchar *p;
1975 if (g_ascii_strcasecmp(req[0], "CLEAR") == 0) {
1976 if (!req[1]) {
1977 send_error(client, EPWMD_COMMAND_SYNTAX);
1978 return FALSE;
1981 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1983 if (cache_clear(md5file, 1) == FALSE) {
1984 send_error(client, EPWMD_CACHE_NOT_FOUND);
1985 return FALSE;
1988 return TRUE;
1990 else if (g_ascii_strcasecmp(req[0], "CLEARALL") == 0) {
1991 cache_clear(client->md5file, 2);
1992 return TRUE;
1994 if (g_ascii_strcasecmp(req[0], "RESET") == 0) {
1995 if (!req[1]) {
1996 send_error(client, EPWMD_COMMAND_SYNTAX);
1997 return FALSE;
2000 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2002 if (cache_reset(md5file, 1) == FALSE) {
2003 send_error(client, EPWMD_CACHE_NOT_FOUND);
2004 return FALSE;
2007 return TRUE;
2009 else if (g_ascii_strcasecmp(req[0], "RESETALL") == 0) {
2010 cache_reset(client->md5file, 2);
2011 return TRUE;
2013 else if (g_ascii_strcasecmp(req[0], "ISCACHED") == 0) {
2014 if (!req[1]) {
2015 send_error(client, EPWMD_COMMAND_SYNTAX);
2016 return FALSE;
2019 if (file_exists(req[1]) == FALSE) {
2020 send_error(client, EPWMD_FILE_NOT_FOUND);
2021 return FALSE;
2024 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2026 if (cache_iscached(md5file) == FALSE) {
2027 send_error(client, EPWMD_CACHE_NOT_FOUND);
2028 return FALSE;
2031 return TRUE;
2033 else if (g_ascii_strcasecmp(req[0], "TIMEOUT") == 0) {
2034 if (!req[1] || !req[2]) {
2035 send_error(client, EPWMD_COMMAND_SYNTAX);
2036 return FALSE;
2039 errno = 0;
2040 timeout = strtol(req[1], &p, 10);
2042 if (errno != 0 || *p != 0) {
2043 send_error(client, EPWMD_COMMAND_SYNTAX);
2044 return FALSE;
2047 if (file_exists(req[2]) == FALSE) {
2048 send_error(client, EPWMD_FILE_NOT_FOUND);
2049 return FALSE;
2052 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[2], strlen(req[2]));
2054 if (cache_set_timeout(md5file, timeout) == FALSE) {
2055 send_error(client, EPWMD_CACHE_NOT_FOUND);
2056 return FALSE;
2059 return TRUE;
2061 else
2062 send_error(client, EPWMD_COMMAND_SYNTAX);
2064 return FALSE;
2067 gboolean help_command(struct client_s *client, const gchar *what)
2069 gchar *line;
2071 if (!what || !*what)
2072 line =
2073 "NFO Try 'HELP COMMAND' for command help\n"
2074 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
2075 else if (g_ascii_strcasecmp(what, "GET") == 0)
2076 line =
2077 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
2078 "NFO <account> is the account to work on and <element>\n"
2079 "NFO is the element wanted.\n"
2080 "NFO -\n"
2081 "NFO Example: GET isp <TAB> imap <TAB> port\n"
2082 "NFO GET isp <TAB> username\n";
2083 else if (g_ascii_strcasecmp(what, "QUIT") == 0)
2084 line =
2085 "NFO syntax: QUIT\n"
2086 "NFO close the connection\n";
2087 else if (g_ascii_strcasecmp(what, "DELETE") == 0)
2088 line =
2089 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
2090 else if (g_ascii_strcasecmp(what, "STORE") == 0)
2091 line =
2092 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
2093 "NFO <account> is the account to work on and <element>\n"
2094 "NFO is the element to create or modify\n"
2095 "NFO -\n"
2096 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
2097 "NFO STORE isp <TAB> username <TAB> someuser\n";
2098 else if (g_ascii_strcasecmp(what, "OPEN") == 0)
2099 line =
2100 "NFO syntax: OPEN <filename> [<key>]\n"
2101 "NFO opens a (new) file\n";
2102 else if (g_ascii_strcasecmp(what, "LIST") == 0)
2103 line =
2104 "NFO syntax: LIST [account]\n"
2105 "NFO shows available accounts or account elements\n";
2106 else if (g_ascii_strcasecmp(what, "ATTR") == 0)
2107 line =
2108 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
2109 "NFO ATTR SET name account value\n"
2110 "NFO ATTR SET target account[<TAB>element[...]] account[<TAB>element[...]\n"
2111 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
2112 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
2113 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
2114 "NFO ATTR LIST account[<TAB>element[...]]\n";
2115 else if (g_ascii_strcasecmp(what, "SAVE") == 0)
2116 line =
2117 "NFO syntax: SAVE [<key>]\n"
2118 "NFO save any changes to the opened file using <key>\n";
2119 else if (g_ascii_strcasecmp(what, "CACHE") == 0)
2120 line =
2121 "NFO syntax: CACHE [ISCACHED <filename>] |\n"
2122 "NFO [RESETALL] | [RESET <filename>] |\n"
2123 "NFO [CLEARALL] | [CLEAR <filename>] |\n"
2124 "NFO [TIMEOUT <seconds> <filename>]\n";
2125 else if (g_ascii_strcasecmp(what, "DUMP") == 0)
2126 line =
2127 "NFO syntax: DUMP\n"
2128 "NFO shows the in memory XML document\n";
2129 else {
2130 send_error(client, EPWMD_COMMAND_SYNTAX);
2131 return FALSE;
2134 send_to_client(client, "%sOK \n", line);
2135 return TRUE;
2138 gboolean dump_command(struct client_s *client)
2140 xmlChar *xml;
2141 gssize len;
2143 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2144 send_to_client(client, "BEGIN %li\n%s", len, xml);
2145 xmlFree(xml);
2146 return TRUE;