Removed .elapsed from file_cache_t. Deincrement .when.
[pwmd.git] / src / commands.c
blobe5c9150181207e5038320c7d90c27777ff6365fa
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 struct file_header_s {
257 guint iter;
258 guchar iv[gcryblocksize];
259 } file_header;
261 if (!filename || !*filename) {
262 send_error(client, EPWMD_COMMAND_SYNTAX);
263 return FALSE;
266 if (valid_filename(filename) == FALSE) {
267 send_error(client, EPWMD_INVALID_FILENAME);
268 return FALSE;
271 p = get_key_file_string("default", "data_directory");
272 p2 = expand_homedir(p);
273 g_free(p);
274 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
275 g_free(p2);
277 if (stat(filebuf, &st) == 0) {
278 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
279 log_write("%s: %s", filename, pwmd_strerror(EPWMD_NOT_A_FILE));
280 send_to_client(client, "ERR %03i %s\n", EPWMD_NOT_A_FILE, pwmd_strerror(EPWMD_NOT_A_FILE));
281 return FALSE;
284 client->mtime = st.st_mtime;
287 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
290 * New files don't need a key.
292 if (access(filebuf, R_OK|W_OK) != 0) {
293 if (errno != ENOENT) {
294 error = errno;
295 log_write("%s: %s", filename, strerror(errno));
296 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename,
297 strerror(error));
298 return FALSE;
300 new_doc:
301 if ((client->xml = new_document()) == NULL) {
302 error = errno;
303 log_write("%s", strerror(errno));
304 send_to_client(client, "ERR %03i malloc(): %s\n", EPWMD_ERROR,
305 strerror(error));
306 return FALSE;
309 client->len = strlen(client->xml);
311 if (cache_add_file(client->md5file, NULL) == FALSE) {
312 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
313 send_error(client, EPWMD_MAX_SLOTS);
314 return FALSE;
317 client->filename = g_strdup(filename);
318 return parse_xml(client);
321 if ((fd = open_file(filebuf, &st)) == -1) {
322 error = errno;
323 log_write("%s: %s", filename, strerror(errno));
324 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(error));
325 return FALSE;
328 if (st.st_size == 0)
329 goto new_doc;
331 if (cache_get_key(client->md5file, shakey) == TRUE)
332 cached = 1;
333 else {
335 * No key specified and no matching filename found in the cache.
337 if (!req[1] || !*req[1]) {
338 close(fd);
339 send_error(client, EPWMD_KEY);
340 return FALSE;
344 insize = st.st_size - sizeof(struct file_header_s);
345 read(fd, &file_header, sizeof(struct file_header_s));
346 inbuf = gcry_malloc(insize);
347 read(fd, inbuf, insize);
348 close(fd);
350 again:
351 if (!cached) {
352 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
353 memset(req[1], 0, strlen(req[1]));
356 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
357 sizeof(file_header.iv)))) {
358 gcry_free(inbuf);
359 memset(shakey, 0, sizeof(shakey));
360 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
361 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
362 return FALSE;
365 if ((gcryerrno = gcry_cipher_setkey(client->gh, shakey, gcrykeysize))) {
366 gcry_free(inbuf);
367 memset(shakey, 0, sizeof(shakey));
368 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
369 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
370 return FALSE;
373 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
374 if (cached) {
375 cached = 0;
376 goto again;
379 memset(shakey, 0, sizeof(shakey));
380 gcry_free(inbuf);
381 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
382 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
383 return FALSE;
386 memcpy(tkey, shakey, sizeof(tkey));
387 tkey[0] ^= 1;
389 if ((gcryerrno = gcry_cipher_setkey(client->gh, tkey, gcrykeysize))) {
390 memset(tkey, 0, sizeof(tkey));
391 gcry_free(inbuf);
392 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
393 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
394 return FALSE;
397 iter = file_header.iter;
399 while (iter-- > 0) {
400 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
401 sizeof(file_header.iv)))) {
402 memset(tkey, 0, sizeof(tkey));
403 gcry_free(inbuf);
404 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
405 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
406 return FALSE;
409 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
410 if (cached) {
411 cached = 0;
412 goto again;
415 memset(tkey, 0, sizeof(tkey));
416 gcry_free(inbuf);
417 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
418 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
419 return FALSE;
423 memset(tkey, 0, sizeof(tkey));
424 client->xml = inbuf;
425 client->len = insize;
427 if (g_strncasecmp(client->xml, "<?xml version=\"1.0\"?>", 21) != 0) {
428 send_error(client, EPWMD_BADKEY);
429 return FALSE;
432 if (!cached) {
433 if (cache_add_file(client->md5file, shakey) == FALSE) {
434 memset(shakey, 0, sizeof(shakey));
435 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
436 send_error(client, EPWMD_MAX_SLOTS);
437 return FALSE;
441 memset(shakey, 0, sizeof(shakey));
442 client->filename = g_strdup(filename);
444 if (!cached) {
445 timeout = get_key_file_integer(client->filename, "cache_timeout");
446 cache_set_timeout(client->md5file, timeout);
449 return parse_xml(client);
453 * client->reader should be at the position in the document where the element
454 * search should start. It won't search past the closing account element.
456 static gboolean find_elements(struct client_s *client, xmlTextReaderPtr reader,
457 gchar **req, gint quiet)
459 gint i;
461 if (!req || !req[0]) {
462 if (!quiet)
463 send_error(client, EPWMD_COMMAND_SYNTAX);
464 return FALSE;
467 for (i = 0; req[i]; i++) {
468 if (find_element(reader, req[i], req[i+1] != NULL) == FALSE) {
469 if (!quiet)
470 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
472 return FALSE;
476 return TRUE;
479 gboolean do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
480 const gchar *filename, const xmlChar *data, size_t insize,
481 const guchar *shakey, guint iter)
483 gsize len = insize;
484 gint fd;
485 void *inbuf;
486 guchar tkey[gcrykeysize];
487 gchar *p;
488 gint error;
489 struct file_header_s {
490 guint iter;
491 guchar iv[gcryblocksize];
492 } file_header;
494 if (insize / gcryblocksize) {
495 len = (insize / gcryblocksize) * gcryblocksize;
497 if (insize % gcryblocksize)
498 len += gcryblocksize;
501 inbuf = gcry_calloc(1, len);
502 memcpy(inbuf, data, insize);
503 insize = len;
504 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
505 memcpy(tkey, shakey, sizeof(tkey));
506 tkey[0] ^= 1;
508 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
509 gcry_free(inbuf);
510 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
511 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
512 return FALSE;
515 file_header.iter = iter;
517 while (iter-- > 0) {
518 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
519 sizeof(file_header.iv)))) {
520 gcry_free(inbuf);
521 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
522 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
523 return FALSE;
526 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
527 == FALSE) {
528 gcry_free(inbuf);
529 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
530 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
531 return FALSE;
535 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
536 sizeof(file_header.iv)))) {
537 gcry_free(inbuf);
538 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
539 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
540 return FALSE;
543 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
544 gcry_free(inbuf);
545 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
546 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
547 return FALSE;
550 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
551 == FALSE) {
552 gcry_free(inbuf);
553 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
554 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
555 return FALSE;
558 if (filename) {
559 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
560 error = errno;
561 gcry_free(inbuf);
562 p = strrchr(filename, '/');
563 p++;
564 log_write("%s: %s", p, strerror(errno));
565 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, p, strerror(error));
566 return FALSE;
569 else
571 * xml_import() from command line.
573 fd = STDOUT_FILENO;
575 write(fd, &file_header, sizeof(struct file_header_s));
576 write(fd, inbuf, insize);
578 if (filename)
579 close(fd);
581 gcry_free(inbuf);
582 return TRUE;
585 gboolean save_command(struct client_s *client, const gchar *filename, gchar *key)
587 xmlChar *xmlbuf;
588 gint len;
589 gint update = 1;
590 guchar shakey[gcrykeysize];
591 gint iter;
592 struct stat st;
593 gchar filebuf[PATH_MAX], *p, *p2;
594 gboolean reset_timeout;
596 p = get_key_file_string("default", "data_directory");
597 p2 = expand_homedir(p);
598 g_free(p);
599 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
600 g_free(p2);
602 if (stat(filebuf, &st) == 0 && client->mtime) {
603 if (client->mtime != st.st_mtime) {
604 log_write("%s: %s", filename, pwmd_strerror(EPWMD_FILE_MODIFIED));
605 send_error(client, EPWMD_FILE_MODIFIED);
606 return FALSE;
610 if (!key) {
611 if (cache_get_key(client->md5file, shakey) == FALSE) {
612 send_error(client, EPWMD_KEY);
613 return FALSE;
616 update = 0;
618 else {
619 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
620 memset(key, 0, strlen(key));
623 xmlDocDumpFormatMemory(client->doc, &xmlbuf, &len, 0);
625 if ((iter = get_key_file_integer(client->filename, "iterations")) == -1)
626 iter = 0;
628 if (do_xml_encrypt(client, client->gh, filebuf, xmlbuf, len, shakey,
629 iter) == FALSE) {
630 memset(shakey, 0, sizeof(shakey));
631 xmlFree(xmlbuf);
632 return FALSE;
635 xmlFree(xmlbuf);
636 stat(filebuf, &st);
637 client->mtime = st.st_mtime;
639 if (!update) {
640 memset(shakey, 0, sizeof(shakey));
642 if ((reset_timeout = get_key_file_boolean(client->filename,
643 "cache_reset_timeout")) == TRUE)
644 cache_reset_timeout(client->md5file);
646 return TRUE;
649 if (cache_update_key(client->md5file, shakey) == FALSE) {
650 memset(shakey, 0, sizeof(shakey));
651 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
652 send_error(client, EPWMD_MAX_SLOTS);
653 return FALSE;
656 memset(shakey, 0, sizeof(shakey));
657 cache_reset_timeout(client->md5file);
658 return TRUE;
661 static gboolean contains_whitespace(const gchar *str)
663 const gchar *p = str;
665 for (; *p; p++) {
666 if (g_ascii_isspace(*p) == TRUE) {
667 return TRUE;
671 return FALSE;
675 * the 'reader' should be at the element of the document where the elements
676 * 'req' are to be created.
678 static gboolean create_elements(struct client_s *client, xmlTextReaderPtr reader,
679 gchar **req, gint novalue)
681 gint i;
682 gboolean ret = TRUE;
683 xmlNodePtr r;
685 r = xmlTextReaderCurrentNode(reader);
687 if (xmlTextReaderDepth(reader) > 1)
688 r = r->parent;
690 for (i = 0; req[i]; i++) {
691 xmlNodePtr n;
694 * Whitespace is allowed in values or attributes but not in element
695 * names.
697 if (req[i+1] && valid_xml_element((xmlChar *)req[i]) == FALSE) {
698 send_error(client, EPWMD_INVALID_ELEMENT);
699 return FALSE;
703 * The value of the element tree.
705 if (!req[i+1] && !novalue) {
707 * Prevent creating 'text' elements in the root of the account.
709 if (i < 1) {
710 send_error(client, EPWMD_ROOT_TEXT_ELEMENT);
711 return FALSE;
715 * FIXME ?? overwriting an element tree with a text element:
717 * STORE account element element2 value
718 * STORE account element value
720 * would remove the existing element tree. This may be a bug or
721 * feature.
723 xmlNodeSetContent(r, (xmlChar *)req[i]);
724 break;
727 if ((n = find_node(r, (xmlChar *)req[i])) == NULL) {
728 n = xmlNewNode(NULL, (xmlChar *)req[i]);
729 r = xmlAddChild(r, n);
731 else
732 r = n;
734 if (!req[i+1] && novalue)
735 return TRUE;
738 return ret;
742 * FIXME reuse reader handle
744 static gboolean reset_reader(xmlDocPtr doc, xmlTextReaderPtr *reader)
746 if (reader) {
747 xmlFreeTextReader(*reader);
748 *reader = NULL;
751 if ((*reader = xmlReaderWalker(doc)) == NULL)
752 return FALSE;
754 return TRUE;
757 static void delete_node(xmlNodePtr n)
759 xmlUnlinkNode(n);
760 xmlFreeNode(n);
763 gboolean delete_command(struct client_s *client, gchar **req)
765 gint i = 1;
766 xmlNodePtr n;
767 xmlErrorPtr xml_error;
769 if (reset_reader(client->doc, &client->reader) == FALSE) {
770 xml_error = xmlGetLastError();
771 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
772 send_error(client, EPWMD_LIBXML_ERROR);
773 return FALSE;
776 if (find_account(client->reader, req[0]) == FALSE) {
777 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
778 return FALSE;
781 n = xmlTextReaderCurrentNode(client->reader);
784 * No sub-node defined. Remove the entire node (account).
786 if (!req[i]) {
787 if (n)
788 delete_node(n);
789 return TRUE;
793 * Remove matching sub-nodes starting from the root of the account.
795 while (req[i] && find_element(client->reader, req[i++], req[i] != NULL) == TRUE)
796 n = xmlTextReaderCurrentNode(client->reader);
798 if (n && xmlStrcmp(n->name, (xmlChar *)req[i-1]) == 0 &&
799 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_ELEMENT)
800 delete_node(n);
801 else {
802 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
803 return FALSE;
806 return TRUE;
809 gboolean store_command(struct client_s *client, gchar **req)
811 xmlErrorPtr xml_error;
813 again:
814 if (reset_reader(client->doc, &client->reader) == FALSE) {
815 xml_error = xmlGetLastError();
816 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
817 send_error(client, EPWMD_LIBXML_ERROR);
818 return FALSE;
821 if (!req[0]) {
822 send_error(client, EPWMD_COMMAND_SYNTAX);
823 return FALSE;
826 if (find_account(client->reader, req[0]) == FALSE) {
827 if (contains_whitespace(req[0]) == TRUE) {
828 send_error(client, EPWMD_INVALID_ELEMENT);
829 return FALSE;
832 if (new_account(client->doc, req[0]) == FALSE) {
833 send_error(client, EPWMD_ERROR);
834 return FALSE;
837 goto again;
840 xmlTextReaderNext(client->reader);
841 return create_elements(client, client->reader, req+1, 0);
844 static gboolean do_get_command(struct client_s *client, xmlTextReaderPtr *reader,
845 gchar **req, xmlChar **content, gint quiet, gint list)
847 xmlNodePtr n;
848 xmlAttrPtr a;
849 gchar **nreq;
850 gboolean ret;
851 xmlChar *p;
852 xmlErrorPtr xml_error;
854 if (reset_reader(client->doc, reader) == FALSE) {
855 if (!quiet) {
856 xml_error = xmlGetLastError();
857 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
858 send_error(client, EPWMD_LIBXML_ERROR);
861 return FALSE;
864 if (find_account(*reader, req[0]) == FALSE) {
865 if (!quiet && !list)
866 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
867 return FALSE;
871 * May be an account with only a TARGET attribute.
873 if (req[1]) {
874 if (find_elements(client, *reader, req + 1, (list) ? 1 : quiet) == FALSE)
875 return FALSE;
878 if ((n = xmlTextReaderCurrentNode(*reader)) == NULL) {
879 if (!quiet) {
880 xml_error = xmlGetLastError();
881 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
882 send_error(client, EPWMD_LIBXML_ERROR);
885 return FALSE;
889 * If the current element has a TARGET attribute, the value of the
890 * attribute is an element path somewhere else in the document. Use this
891 * value and not any TEXT element value. The value may be another element
892 * with a TARGET attribute and this function will recurse until a non-TARGET
893 * attribute in the element is found.
895 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
896 if ((p = xmlNodeGetContent(a->children)) != NULL) {
897 if (strchr((gchar *)p, '\t') != NULL) {
898 if ((nreq = split_input_line((gchar *)p, "\t", 0)) == NULL) {
899 xmlFree(p);
901 if (!quiet)
902 send_error(client, EPWMD_INVALID_ELEMENT);
903 return FALSE;
906 else {
907 if ((nreq = split_input_line((gchar *)p, " ", 0)) == NULL) {
908 xmlFree(p);
910 if (!quiet)
911 send_error(client, EPWMD_INVALID_ELEMENT);
912 return FALSE;
916 xmlFree(p);
917 ret = do_get_command(client, reader, nreq, content, quiet, list);
918 g_strfreev(nreq);
919 return ret;
923 switch (xmlTextReaderNext(*reader)) {
924 case -1:
925 if (!quiet) {
926 xml_error = xmlGetLastError();
927 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
928 send_error(client, EPWMD_LIBXML_ERROR);
931 return FALSE;
932 case 0:
933 if (!quiet)
934 send_error(client, EPWMD_EMPTY_ELEMENT);
935 return FALSE;
936 default:
937 break;
940 switch (xmlTextReaderNodeType(*reader)) {
941 case XML_READER_TYPE_END_ELEMENT:
943 * May be an empty element after an ATTR DELETE TARGET command.
945 return TRUE;
946 case XML_READER_TYPE_TEXT:
947 break;
948 case -1:
949 if (!quiet) {
950 xml_error = xmlGetLastError();
951 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
952 send_error(client, EPWMD_LIBXML_ERROR);
955 return FALSE;
956 default:
957 if (!quiet) {
958 if (n->children && !list)
959 send_error(client, EPWMD_TRAILING_ELEMENT);
960 else if (!n->children && !list)
961 send_error(client, EPWMD_INVALID_ELEMENT);
963 return FALSE;
966 if ((*content = xmlNodeGetContent(n)) == NULL) {
967 if (!quiet)
968 send_error(client, EPWMD_EMPTY_ELEMENT);
969 return FALSE;
972 return TRUE;
976 * Retrieves the value associated with the element tree 'req'.
978 gboolean get_command(struct client_s *client, xmlTextReaderPtr *reader,
979 gchar **req, gint quiet)
981 xmlChar *content = NULL;
983 if (do_get_command(client, reader, req, &content, quiet, 0) == FALSE)
984 return FALSE;
986 if (!content) {
987 if (!quiet)
988 send_error(client, EPWMD_EMPTY_ELEMENT);
990 return FALSE;
993 send_to_client(client, "BEGIN %li\n%s\nOK \n", xmlStrlen(content), content);
994 xmlFree(content);
995 return TRUE;
998 #ifdef DEBUG
999 static gchar *element_path_to_req(const gchar *account, xmlChar *path,
1000 const xmlChar *content)
1001 #else
1002 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
1003 #endif
1005 xmlChar *p = path;
1006 gint n;
1007 gchar *buf;
1009 if (!p)
1010 return NULL;
1012 for (n = 0; *p && n < 3; p++) {
1013 if (*p == '/')
1014 n++;
1017 if (strstr((gchar *)p, "text()") != NULL)
1018 p[xmlStrlen(p) - 7] = 0;
1020 for (n = 0; p[n]; n++) {
1021 if (p[n] == '/')
1022 p[n] = '\t';
1025 #ifdef DEBUG
1026 buf = g_strdup_printf("%s\t%s\t%s", account, p, content);
1027 #else
1028 buf = g_strdup_printf("%s\t%s", account, p);
1029 #endif
1030 return buf;
1033 static gboolean append_to_array(gchar ***array, gint *total, const gchar *str)
1035 gchar **a;
1036 gint t = *total;
1038 if ((a = g_realloc(*array, (t + 2) * sizeof(gchar *))) == NULL)
1039 return FALSE;
1041 a[t++] = g_strdup(str);
1042 a[t] = NULL;
1043 *total = t;
1044 *array = a;
1045 return TRUE;
1048 gboolean list_command(struct client_s *client, gchar *str)
1050 gchar *dst = NULL;
1051 gchar *p = str;
1052 gchar **elements = NULL;
1053 gint pwmd_errno = -1;
1054 gchar *account;
1055 gint total = 0;
1056 xmlChar *path = NULL;
1057 xmlAttrPtr a;
1058 xmlNodePtr n;
1059 xmlChar *content;
1060 xmlTextReaderPtr r = NULL;
1061 gchar **req = NULL, **nreq;
1062 gboolean ret;
1063 gint type;
1064 gchar *line;
1065 xmlErrorPtr xml_error;
1066 gint depth = 0;
1068 if (reset_reader(client->doc, &client->reader) == FALSE) {
1069 xml_error = xmlGetLastError();
1070 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1071 send_error(client, EPWMD_LIBXML_ERROR);
1072 return FALSE;
1075 if (strchr(p, ' ') == NULL) {
1076 list_only:
1077 if (list_accounts(client->reader, &dst, &pwmd_errno) == FALSE) {
1078 send_error(client, pwmd_errno);
1079 return FALSE;
1081 else {
1082 send_to_client(client, "BEGIN %i\n%s\nOK \n",
1083 g_utf8_strlen(dst, -1), dst);
1084 memset(dst, 0, strlen(dst));
1085 g_free(dst);
1088 return TRUE;
1091 p = str + 5;
1093 while (*p && isspace(*p))
1094 p++;
1096 if (!*p)
1097 goto list_only;
1099 if (strchr(p, '\t') != NULL) {
1100 if ((req = split_input_line(p, "\t", 0)) == NULL) {
1101 send_error(client, EPWMD_COMMAND_SYNTAX);
1102 return FALSE;
1105 if (find_account(client->reader, req[0]) == FALSE) {
1106 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1107 return FALSE;
1110 if (find_elements(client, client->reader, req + 1, 0) == FALSE) {
1111 g_strfreev(req);
1112 return FALSE;
1115 depth = xmlTextReaderDepth(client->reader);
1117 else {
1118 if (find_account(client->reader, p) == FALSE) {
1119 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1120 return FALSE;
1124 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1125 xml_error = xmlGetLastError();
1126 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1127 send_error(client, EPWMD_LIBXML_ERROR);
1128 return FALSE;
1131 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
1132 if (reset_reader(client->doc, &client->reader) == FALSE) {
1133 send_error(client, EPWMD_LIBXML_ERROR);
1134 return FALSE;
1137 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1138 if (find_account(client->reader, (gchar *)content) == FALSE) {
1139 xmlFree(content);
1140 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1141 return FALSE;
1144 xmlFree(content);
1148 account = (req) ? g_strdup(req[0]) : g_strdup(p);
1150 if (req)
1151 g_strfreev(req);
1153 while (xmlTextReaderNext(client->reader) == 1) {
1154 again:
1155 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1156 xml_error = xmlGetLastError();
1157 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1158 send_error(client, EPWMD_LIBXML_ERROR);
1159 return FALSE;
1162 if (xmlTextReaderDepth(client->reader) == 1 &&
1163 xmlStrcmp(n->name, (xmlChar *)"account") == 0 &&
1164 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1165 break;
1167 if (depth && depth == xmlTextReaderDepth(client->reader) &&
1168 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1169 break;
1172 * If the current element has a TARGET attribute, the value of the
1173 * attribute is an element path somewhere else in the document. Use this
1174 * value and not any TEXT element value.
1176 type = xmlTextReaderNodeType(client->reader);
1177 a = xmlHasProp(n, (xmlChar *)"target");
1179 if (type == XML_READER_TYPE_ELEMENT && a) {
1180 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1181 path = xmlGetNodePath(n);
1183 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
1184 if (elements)
1185 g_strfreev(elements);
1187 xmlFree(path);
1188 xmlFree(content);
1189 send_error(client, EPWMD_INVALID_ELEMENT);
1190 return FALSE;
1193 xmlFree(content);
1194 r = NULL;
1196 if ((ret = do_get_command(client, &r, nreq, &content, 0, 1)) == TRUE) {
1197 if (content && *content) {
1198 #ifdef DEBUG
1199 line = element_path_to_req(account, path, content);
1200 #else
1201 line = element_path_to_req(account, path);
1202 #endif
1203 xmlFree(content);
1205 if (append_to_array(&elements, &total, line) == FALSE) {
1206 if (elements)
1207 g_strfreev(elements);
1209 xmlFree(path);
1210 memset(line, 0, g_utf8_strlen(line, -1));
1211 g_free(line);
1212 g_strfreev(nreq);
1213 xmlFreeTextReader(r);
1214 send_error(client, EPWMD_ERROR);
1215 return FALSE;
1218 memset(line, 0, g_utf8_strlen(line, -1));
1219 g_free(line);
1222 if (xmlTextReaderNext(client->reader) == 1) {
1223 if (xmlTextReaderNodeType(client->reader) !=
1224 XML_READER_TYPE_TEXT) {
1225 g_strfreev(nreq);
1226 xmlFreeTextReader(r);
1227 xmlFree(path);
1228 goto again;
1233 g_strfreev(nreq);
1234 xmlFreeTextReader(r);
1235 xmlFree(path);
1236 continue;
1240 if (type == XML_READER_TYPE_TEXT) {
1241 xmlChar *np = xmlGetNodePath(n);
1243 #ifdef DEBUG
1244 content = xmlNodeGetContent(n);
1245 line = element_path_to_req(account, np, content);
1246 xmlFree(content);
1247 #else
1248 line = element_path_to_req(account, np);
1249 #endif
1250 xmlFree(np);
1251 append_to_array(&elements, &total, line);
1252 memset(line, 0, g_utf8_strlen(line, -1));
1253 g_free(line);
1257 if (!elements) {
1258 send_error(client, EPWMD_EMPTY_ELEMENT);
1259 g_free(account);
1260 return FALSE;
1263 g_free(account);
1264 line = g_strjoinv("\n", elements);
1265 send_to_client(client, "BEGIN %li\n%s\nOK \n",
1266 g_utf8_strlen(line, -1), line);
1267 g_strfreev(elements);
1268 g_free(line);
1269 return TRUE;
1273 * The client->reader handle should be at the element in the document where
1274 * the attribute will be created or modified.
1276 static gboolean add_attribute(struct client_s *client, const gchar *name, const gchar *value)
1278 xmlAttrPtr a;
1279 xmlNodePtr n;
1280 xmlErrorPtr xml_error;
1282 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1283 xml_error = xmlGetLastError();
1284 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1285 send_error(client, EPWMD_LIBXML_ERROR);
1286 return FALSE;
1289 if ((a = xmlHasProp(n, (xmlChar *)name)) == NULL)
1290 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)value);
1291 else
1292 xmlNodeSetContent(a->children, (xmlChar *)value);
1294 return TRUE;
1298 * req[0] - element path
1300 static gboolean attribute_list(struct client_s *client, gchar **req)
1302 gchar **attrlist = NULL;
1303 gint i = 0;
1304 gchar **epath = NULL;
1305 xmlAttrPtr a;
1306 xmlNodePtr n, an;
1307 gchar *line;
1308 xmlErrorPtr xml_error;
1310 if (!req || !req[0]) {
1311 send_error(client, EPWMD_COMMAND_SYNTAX);
1312 return FALSE;
1315 if ((epath = split_input_line(req[0], "\t", 0)) == NULL) {
1317 * The first argument may be only an account.
1319 if ((epath = split_input_line(req[0], " ", 0)) == NULL) {
1320 send_error(client, EPWMD_COMMAND_SYNTAX);
1321 return FALSE;
1325 if (reset_reader(client->doc, &client->reader) == FALSE) {
1326 xml_error = xmlGetLastError();
1327 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1328 send_error(client, EPWMD_LIBXML_ERROR);
1329 goto blah;
1332 if (find_account(client->reader, epath[0]) == FALSE) {
1333 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1334 goto blah;
1337 if (epath[1]) {
1338 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1339 goto blah;
1342 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1343 xml_error = xmlGetLastError();
1344 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1345 send_error(client, EPWMD_LIBXML_ERROR);
1346 goto blah;
1349 for (a = n->properties; a; a = a->next) {
1350 if ((attrlist = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1351 log_write("%s(%i): g_realloc() failed", __FILE__, __LINE__);
1352 send_error(client, EPWMD_ERROR);
1353 goto blah;
1356 an = a->children;
1357 attrlist[i++] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1358 attrlist[i] = NULL;
1361 if (!attrlist) {
1362 send_error(client, EPWMD_EMPTY_ELEMENT);
1363 goto blah;
1366 line = g_strjoinv("\n", attrlist);
1367 send_to_client(client, "BEGIN %li\n%s\n", g_utf8_strlen(line, -1), line);
1368 g_free(line);
1369 g_strfreev(epath);
1370 g_strfreev(attrlist);
1371 return TRUE;
1372 blah:
1373 g_strfreev(epath);
1374 return FALSE;
1378 * req[0] - attribute
1379 * req[1] - element path
1381 static gboolean attribute_delete(struct client_s *client, gchar **req)
1383 xmlAttrPtr a;
1384 xmlNodePtr n;
1385 gchar **epath = NULL;
1386 xmlErrorPtr xml_error;
1388 if (!req || !req[0] || !req[1]) {
1389 send_error(client, EPWMD_COMMAND_SYNTAX);
1390 return FALSE;
1393 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1395 * The first argument may be only an account.
1397 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1398 send_error(client, EPWMD_COMMAND_SYNTAX);
1399 return FALSE;
1404 * Don't remove the NAME attribute for the account element.
1406 if (!epath[1] && g_ascii_strcasecmp(req[0], "NAME") == 0) {
1407 send_error(client, EPWMD_ATTR_SYNTAX);
1408 goto blah;
1411 if (reset_reader(client->doc, &client->reader) == FALSE) {
1412 xml_error = xmlGetLastError();
1413 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1414 send_error(client, EPWMD_LIBXML_ERROR);
1415 goto blah;
1418 if (find_account(client->reader, epath[0]) == FALSE) {
1419 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1420 goto blah;
1423 if (epath[1]) {
1424 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1425 goto blah;
1428 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1429 xml_error = xmlGetLastError();
1430 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1431 send_error(client, EPWMD_LIBXML_ERROR);
1432 goto blah;
1435 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL) {
1436 send_error(client, EPWMD_ATTR_NOT_FOUND);
1437 goto blah;
1440 if (xmlRemoveProp(a) == -1) {
1441 xml_error = xmlGetLastError();
1442 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1443 send_error(client, EPWMD_LIBXML_ERROR);
1444 goto blah;
1447 g_strfreev(epath);
1448 return TRUE;
1449 blah:
1450 g_strfreev(epath);
1451 return FALSE;
1455 * req[0] - source element path
1456 * req[1] - destination element path
1458 static gboolean target_attribute(struct client_s *client, gchar **req)
1460 gchar **src, **dst, *line;
1461 xmlErrorPtr xml_error;
1463 if (!req || !req[0] || !req[1]) {
1464 send_error(client, EPWMD_COMMAND_SYNTAX);
1465 return FALSE;
1468 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1470 * The first argument may be only an account.
1472 if ((src = split_input_line(req[0], " ", 0)) == NULL) {
1473 send_error(client, EPWMD_COMMAND_SYNTAX);
1474 return FALSE;
1478 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1480 * The first argument may be only an account.
1482 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1483 send_error(client, EPWMD_COMMAND_SYNTAX);
1484 g_strfreev(src);
1485 goto blah;
1490 * Prevent an element tree pointing to only and account. Accounts require
1491 * at least one element. Accounts pointing to accounts are allowed.
1493 if ((!src[1] && dst[1]) || (!dst[1] && src[1])) {
1494 send_error(client, EPWMD_ATTR_SYNTAX);
1495 g_strfreev(src);
1496 g_strfreev(dst);
1497 goto blah;
1500 if (reset_reader(client->doc, &client->reader) == FALSE) {
1501 xml_error = xmlGetLastError();
1502 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1503 send_error(client, EPWMD_LIBXML_ERROR);
1504 g_strfreev(src);
1505 g_strfreev(dst);
1506 goto blah;
1510 * Make sure the destination element path exists.
1512 if (find_account(client->reader, dst[0]) == FALSE) {
1513 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1514 g_strfreev(src);
1515 g_strfreev(dst);
1516 goto blah;
1519 if (dst[1]) {
1520 if (find_elements(client, client->reader, dst+1, 0) == FALSE) {
1521 g_strfreev(src);
1522 g_strfreev(dst);
1523 goto blah;
1527 if (reset_reader(client->doc, &client->reader) == FALSE) {
1528 xml_error = xmlGetLastError();
1529 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1530 send_error(client, EPWMD_LIBXML_ERROR);
1531 g_strfreev(src);
1532 g_strfreev(dst);
1533 goto blah;
1537 * If the source element tree doesn't exist, create it.
1539 if (find_account(client->reader, src[0]) == FALSE) {
1540 if (new_account(client->doc, src[0]) == 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 (reset_reader(client->doc, &client->reader) == FALSE) {
1550 xml_error = xmlGetLastError();
1551 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1552 send_error(client, EPWMD_LIBXML_ERROR);
1553 g_strfreev(src);
1554 g_strfreev(dst);
1555 goto blah;
1558 if (find_account(client->reader, src[0]) == FALSE) {
1559 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1560 g_strfreev(src);
1561 g_strfreev(dst);
1562 goto blah;
1566 if (src[1]) {
1567 if (find_elements(client, client->reader, src+1, 1) == FALSE) {
1568 if (reset_reader(client->doc, &client->reader) == FALSE) {
1569 xml_error = xmlGetLastError();
1570 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1571 send_error(client, EPWMD_LIBXML_ERROR);
1572 g_strfreev(src);
1573 g_strfreev(dst);
1574 goto blah;
1577 if (find_account(client->reader, src[0]) == FALSE) {
1578 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1579 g_strfreev(src);
1580 g_strfreev(dst);
1581 goto blah;
1584 xmlTextReaderNext(client->reader);
1586 if (create_elements(client, client->reader, src+1, 1) == FALSE) {
1587 g_strfreev(src);
1588 g_strfreev(dst);
1589 goto blah;
1592 if (reset_reader(client->doc, &client->reader) == FALSE) {
1593 xml_error = xmlGetLastError();
1594 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1595 send_error(client, EPWMD_LIBXML_ERROR);
1596 g_strfreev(src);
1597 g_strfreev(dst);
1598 goto blah;
1601 if (find_account(client->reader, src[0]) == FALSE) {
1602 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1603 g_strfreev(src);
1604 g_strfreev(dst);
1605 goto blah;
1608 if (find_elements(client, client->reader, src+1, 0) == FALSE) {
1609 g_strfreev(src);
1610 g_strfreev(dst);
1611 goto blah;
1616 line = g_strjoinv("\t", dst);
1618 if (add_attribute(client, "target", line) == FALSE) {
1619 g_free(line);
1620 g_strfreev(src);
1621 g_strfreev(dst);
1622 goto blah;
1625 g_strfreev(src);
1626 g_strfreev(dst);
1627 g_free(line);
1628 return TRUE;
1629 blah:
1630 g_strfreev(src);
1631 g_strfreev(dst);
1632 return FALSE;
1636 * req[0] - account name
1637 * req[1] - new name
1639 static gboolean name_attribute(struct client_s *client, gchar **req)
1641 xmlErrorPtr xml_error;
1643 if (reset_reader(client->doc, &client->reader) == FALSE) {
1644 xml_error = xmlGetLastError();
1645 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1646 send_error(client, EPWMD_LIBXML_ERROR);
1647 return FALSE;
1650 if (find_account(client->reader, req[0]) == FALSE) {
1651 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1652 return FALSE;
1655 if (strcmp(req[0], req[1]) == 0)
1656 return TRUE;
1658 if (reset_reader(client->doc, &client->reader) == FALSE) {
1659 xml_error = xmlGetLastError();
1660 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1661 send_error(client, EPWMD_LIBXML_ERROR);
1662 return FALSE;
1666 * Will not overwrite an existing account.
1668 if (find_account(client->reader, req[1]) == TRUE) {
1669 send_error(client, EPWMD_ACCOUNT_EXISTS);
1670 return FALSE;
1673 if (reset_reader(client->doc, &client->reader) == FALSE) {
1674 xml_error = xmlGetLastError();
1675 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1676 send_error(client, EPWMD_LIBXML_ERROR);
1677 return FALSE;
1680 if (find_account(client->reader, req[0]) == FALSE) {
1681 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1682 return FALSE;
1686 * Whitespace not allowed in account names.
1688 if (contains_whitespace(req[1]) == TRUE) {
1689 send_error(client, EPWMD_ATTR_SYNTAX);
1690 return FALSE;
1693 return add_attribute(client, "name", req[1]);
1697 * req[0] - attribute
1698 * req[1] - element path
1700 * If the element has a "target" attribute it won't be "followed".
1702 static gboolean attribute_get(struct client_s *client, gchar **req)
1704 xmlNodePtr n;
1705 xmlChar *a;
1706 gchar **nreq = NULL;
1707 xmlErrorPtr xml_error;
1709 if (!req || !req[0] || !req[1]) {
1710 send_error(client, EPWMD_COMMAND_SYNTAX);
1711 return FALSE;
1714 if (strchr(req[1], '\t')) {
1715 if ((nreq = split_input_line(req[1], "\t", 0)) == NULL) {
1716 send_error(client, EPWMD_COMMAND_SYNTAX);
1717 return FALSE;
1720 else {
1721 if ((nreq = split_input_line(req[1], " ", 0)) == NULL) {
1722 send_error(client, EPWMD_COMMAND_SYNTAX);
1723 return FALSE;
1727 if (reset_reader(client->doc, &client->reader) == FALSE) {
1728 xml_error = xmlGetLastError();
1729 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1730 g_strfreev(nreq);
1731 send_error(client, EPWMD_LIBXML_ERROR);
1732 return FALSE;
1735 if (find_account(client->reader, nreq[0]) == FALSE) {
1736 g_strfreev(nreq);
1737 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1738 return FALSE;
1741 if (nreq[1]) {
1742 if (find_elements(client, client->reader, nreq + 1, 0) == FALSE) {
1743 g_strfreev(nreq);
1744 return FALSE;
1748 g_strfreev(nreq);
1750 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1751 xml_error = xmlGetLastError();
1752 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1753 send_error(client, EPWMD_LIBXML_ERROR);
1754 return FALSE;
1757 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL) {
1758 send_error(client, EPWMD_ATTR_NOT_FOUND);
1759 return FALSE;
1762 send_to_client(client, "BEGIN %li\n%s\n", xmlStrlen(a), a);
1763 xmlFree(a);
1764 return TRUE;
1768 * req[0] - attribute
1769 * req[1] - element path
1770 * req[2] - value
1772 static gboolean attribute_set(struct client_s *client, gchar **req)
1774 gchar **epath = NULL;
1775 xmlErrorPtr xml_error;
1777 if (!req || !req[0] || !req[1] || !req[2]) {
1778 send_error(client, EPWMD_COMMAND_SYNTAX);
1779 return FALSE;
1783 * Reserved attribute names.
1785 if (g_ascii_strcasecmp(req[0], "NAME") == 0) {
1787 * Only reserved for the account element. Not the rest of the
1788 * document.
1790 if (strchr(req[1], '\t') == NULL)
1791 return name_attribute(client, req + 1);
1793 else if (g_ascii_strcasecmp(req[0], "TARGET") == 0)
1794 return target_attribute(client, req + 1);
1796 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1798 * The first argument may be only an account.
1800 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1801 send_error(client, EPWMD_COMMAND_SYNTAX);
1802 return FALSE;
1806 if (reset_reader(client->doc, &client->reader) == FALSE) {
1807 xml_error = xmlGetLastError();
1808 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1809 send_error(client, EPWMD_LIBXML_ERROR);
1810 goto blah;
1813 if (find_account(client->reader, epath[0]) == FALSE) {
1814 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1815 goto blah;
1818 if (epath[1]) {
1819 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1820 goto blah;
1823 g_strfreev(epath);
1824 return add_attribute(client, req[0], req[2]);
1825 blah:
1826 g_strfreev(epath);
1827 return FALSE;
1831 * req[0] - command
1832 * req[1] - attribute name or element path if command is LIST
1833 * req[2] - element path
1834 * req[2] - element path or value
1836 gboolean attr_command(struct client_s *client, gchar **req)
1838 if (!req || !req[0] || !req[1]) {
1839 send_error(client, EPWMD_COMMAND_SYNTAX);
1840 return FALSE;
1843 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1844 return attribute_set(client, req+1);
1845 if (g_ascii_strcasecmp(req[0], "GET") == 0)
1846 return attribute_get(client, req+1);
1847 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1848 return attribute_delete(client, req+1);
1849 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1850 return attribute_list(client, req+1);
1851 else
1852 send_error(client, EPWMD_COMMAND_SYNTAX);
1854 return FALSE;
1857 gboolean cache_clear(const guchar *md5filename, gint which)
1859 void *p;
1860 file_cache_t f;
1861 glong len;
1863 for (p = shm_data, len = 0; len <= cache_size;) {
1864 memcpy(&f, p, sizeof(file_cache_t));
1866 if (which == 2) {
1867 memset(&f, 0, sizeof(file_cache_t));
1868 memcpy(p, &f, sizeof(file_cache_t));
1870 else if (f.used == TRUE && which == 1) {
1871 if (memcmp(&f.filename, md5filename, sizeof(f.filename)) == 0) {
1872 memset(&f, 0, sizeof(file_cache_t));
1873 memcpy(p, &f, sizeof(file_cache_t));
1874 return TRUE;
1878 p += sizeof(file_cache_t);
1879 len += sizeof(file_cache_t);
1881 if (len + sizeof(file_cache_t) > cache_size)
1882 break;
1885 return (which == 2) ? TRUE : FALSE;
1888 gboolean cache_iscached(const guchar *md5filename)
1890 void *p;
1891 file_cache_t f;
1892 glong len;
1894 for (p = shm_data, len = 0; len <= cache_size;) {
1895 memcpy(&f, p, sizeof(file_cache_t));
1897 if (f.used == TRUE) {
1898 if (memcmp(&f.filename, md5filename, sizeof(f.filename)) == 0)
1899 return cache_valid_key(f.key, sizeof(f.key));
1902 p += sizeof(file_cache_t);
1903 len += sizeof(file_cache_t);
1905 if (len + sizeof(file_cache_t) > cache_size)
1906 break;
1909 return FALSE;
1912 gboolean cache_reset(const guchar *md5filename, gint which)
1914 void *p;
1915 file_cache_t f;
1916 glong len;
1917 guchar md5file[sizeof(f.filename)];
1918 gint timeout;
1920 for (p = shm_data, len = 0; len <= cache_size;) {
1921 memcpy(&f, p, sizeof(file_cache_t));
1922 timeout = f.timeout;
1924 if (which == 2) {
1925 if (f.used == TRUE) {
1926 memcpy(&md5file, f.filename, sizeof(md5file));
1927 memset(&f, 0, sizeof(file_cache_t));
1928 memcpy(&f.filename, &md5file, sizeof(md5file));
1929 f.timeout = f.when = timeout;
1930 f.used = TRUE;
1931 memcpy(p, &f, sizeof(file_cache_t));
1934 else if (f.used == TRUE && which == 1) {
1935 if (memcmp(&f.filename, md5filename, sizeof(f.filename)) == 0) {
1936 memcpy(&md5file, f.filename, sizeof(md5file));
1937 memset(&f, 0, sizeof(file_cache_t));
1938 memcpy(&f.filename, &md5file, sizeof(md5file));
1939 f.timeout = f.when = timeout;
1940 f.used = TRUE;
1941 memcpy(p, &f, sizeof(file_cache_t));
1942 return TRUE;
1946 p += sizeof(file_cache_t);
1947 len += sizeof(file_cache_t);
1949 if (len + sizeof(file_cache_t) > cache_size)
1950 break;
1953 return (which == 2) ? TRUE : FALSE;
1956 static gboolean file_exists(const gchar *filename)
1958 struct stat st;
1959 gchar filebuf[PATH_MAX], *p, *p2;
1961 p = get_key_file_string("default", "data_directory");
1962 p2 = expand_homedir(p);
1963 g_free(p);
1964 snprintf(filebuf, sizeof(filebuf), "%s/%s", p2, filename);
1965 g_free(p2);
1967 if (access(filebuf, R_OK) == -1)
1968 return FALSE;
1970 stat(filebuf, &st);
1972 if (st.st_size == 0)
1973 return FALSE;
1975 return TRUE;
1978 gboolean cache_command(struct client_s *client, gchar **req)
1980 guchar md5file[16];
1981 glong timeout;
1982 gchar *p;
1984 if (g_ascii_strcasecmp(req[0], "CLEAR") == 0) {
1985 if (!req[1]) {
1986 send_error(client, EPWMD_COMMAND_SYNTAX);
1987 return FALSE;
1990 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1992 if (cache_clear(md5file, 1) == FALSE) {
1993 send_error(client, EPWMD_CACHE_NOT_FOUND);
1994 return FALSE;
1997 return TRUE;
1999 else if (g_ascii_strcasecmp(req[0], "CLEARALL") == 0) {
2000 cache_clear(client->md5file, 2);
2001 return TRUE;
2003 if (g_ascii_strcasecmp(req[0], "RESET") == 0) {
2004 if (!req[1]) {
2005 send_error(client, EPWMD_COMMAND_SYNTAX);
2006 return FALSE;
2009 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2011 if (cache_reset(md5file, 1) == FALSE) {
2012 send_error(client, EPWMD_CACHE_NOT_FOUND);
2013 return FALSE;
2016 return TRUE;
2018 else if (g_ascii_strcasecmp(req[0], "RESETALL") == 0) {
2019 cache_reset(client->md5file, 2);
2020 return TRUE;
2022 else if (g_ascii_strcasecmp(req[0], "ISCACHED") == 0) {
2023 if (!req[1]) {
2024 send_error(client, EPWMD_COMMAND_SYNTAX);
2025 return FALSE;
2028 if (file_exists(req[1]) == FALSE) {
2029 send_error(client, EPWMD_FILE_NOT_FOUND);
2030 return FALSE;
2033 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2035 if (cache_iscached(md5file) == FALSE) {
2036 send_error(client, EPWMD_CACHE_NOT_FOUND);
2037 return FALSE;
2040 return TRUE;
2042 else if (g_ascii_strcasecmp(req[0], "TIMEOUT") == 0) {
2043 if (!req[1] || !req[2]) {
2044 send_error(client, EPWMD_COMMAND_SYNTAX);
2045 return FALSE;
2048 errno = 0;
2049 timeout = strtol(req[1], &p, 10);
2051 if (errno != 0 || *p != 0) {
2052 send_error(client, EPWMD_COMMAND_SYNTAX);
2053 return FALSE;
2056 if (file_exists(req[2]) == FALSE) {
2057 send_error(client, EPWMD_FILE_NOT_FOUND);
2058 return FALSE;
2061 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[2], strlen(req[2]));
2063 if (cache_set_timeout(md5file, timeout) == FALSE) {
2064 send_error(client, EPWMD_CACHE_NOT_FOUND);
2065 return FALSE;
2068 return TRUE;
2070 else
2071 send_error(client, EPWMD_COMMAND_SYNTAX);
2073 return FALSE;
2076 gboolean help_command(struct client_s *client, const gchar *what)
2078 gchar *line;
2080 if (!what || !*what)
2081 line =
2082 "NFO Try 'HELP COMMAND' for command help\n"
2083 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
2084 else if (g_ascii_strcasecmp(what, "GET") == 0)
2085 line =
2086 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
2087 "NFO <account> is the account to work on and <element>\n"
2088 "NFO is the element wanted.\n"
2089 "NFO -\n"
2090 "NFO Example: GET isp <TAB> imap <TAB> port\n"
2091 "NFO GET isp <TAB> username\n";
2092 else if (g_ascii_strcasecmp(what, "QUIT") == 0)
2093 line =
2094 "NFO syntax: QUIT\n"
2095 "NFO close the connection\n";
2096 else if (g_ascii_strcasecmp(what, "DELETE") == 0)
2097 line =
2098 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
2099 else if (g_ascii_strcasecmp(what, "STORE") == 0)
2100 line =
2101 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
2102 "NFO <account> is the account to work on and <element>\n"
2103 "NFO is the element to create or modify\n"
2104 "NFO -\n"
2105 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
2106 "NFO STORE isp <TAB> username <TAB> someuser\n";
2107 else if (g_ascii_strcasecmp(what, "OPEN") == 0)
2108 line =
2109 "NFO syntax: OPEN <filename> [<key>]\n"
2110 "NFO opens a (new) file\n";
2111 else if (g_ascii_strcasecmp(what, "LIST") == 0)
2112 line =
2113 "NFO syntax: LIST [account]\n"
2114 "NFO shows available accounts or account elements\n";
2115 else if (g_ascii_strcasecmp(what, "ATTR") == 0)
2116 line =
2117 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
2118 "NFO ATTR SET NAME account value\n"
2119 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
2120 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
2121 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
2122 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
2123 "NFO ATTR LIST account[<TAB>element[...]]\n";
2124 else if (g_ascii_strcasecmp(what, "SAVE") == 0)
2125 line =
2126 "NFO syntax: SAVE [<key>]\n"
2127 "NFO save any changes to the opened file using <key>\n";
2128 else if (g_ascii_strcasecmp(what, "CACHE") == 0)
2129 line =
2130 "NFO syntax: CACHE [ISCACHED <filename>] |\n"
2131 "NFO [RESETALL] | [RESET <filename>] |\n"
2132 "NFO [CLEARALL] | [CLEAR <filename>] |\n"
2133 "NFO [TIMEOUT <seconds> <filename>]\n";
2134 else if (g_ascii_strcasecmp(what, "DUMP") == 0)
2135 line =
2136 "NFO syntax: DUMP\n"
2137 "NFO shows the in memory XML document\n";
2138 else {
2139 send_error(client, EPWMD_COMMAND_SYNTAX);
2140 return FALSE;
2143 send_to_client(client, "%sOK \n", line);
2144 return TRUE;
2147 gboolean dump_command(struct client_s *client)
2149 xmlChar *xml;
2150 gssize len;
2152 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2153 send_to_client(client, "BEGIN %li\n%s", len, xml);
2154 xmlFree(xml);
2155 return TRUE;