Close file descriptors 0, 1 and 2 before the daemon loop.
[pwmd.git] / src / commands.c
blob9d28ce19325f7ca39eecb943cce09cb2b5d4689f
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_update_key(const guchar *md5filename, const guchar *shakey)
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 (f.used == TRUE) {
110 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
111 memcpy(&f.key, shakey, sizeof(f.key));
112 memcpy(p, &f, sizeof(file_cache_t));
113 return TRUE;
117 p += sizeof(file_cache_t);
118 len += sizeof(file_cache_t);
120 if (len + sizeof(file_cache_t) > cache_size)
121 break;
124 return cache_add_file(md5filename, shakey);
127 static gint cache_valid_key(const guchar *key, gsize len)
129 gint b;
131 for (b = 0; b < len; b++) {
132 if (key[b])
133 return TRUE;
136 return FALSE;
139 static gboolean cache_get_key(const guchar *md5file, guchar *shakey)
141 void *p;
142 file_cache_t f;
143 glong len;
145 for (p = shm_data, len = 0; len <= cache_size;) {
146 memcpy(&f, p, sizeof(file_cache_t));
149 * The slot may be used but not yet contain a key.
151 if (f.used == TRUE) {
152 if (memcmp(&f.filename, md5file, sizeof(f.filename)) == 0) {
153 if (cache_valid_key(f.key, sizeof(f.key)) == FALSE)
154 return FALSE;
156 memcpy(shakey, &f.key, sizeof(f.key));
157 return TRUE;
161 p += sizeof(file_cache_t);
162 len += sizeof(file_cache_t);
164 if (len + sizeof(file_cache_t) > cache_size)
165 break;
168 return FALSE;
171 gint open_file(const gchar *filename, struct stat *st)
173 gint fd;
175 if ((fd = open(filename, O_RDONLY)) == -1)
176 return -1;
178 if (stat(filename, st) == -1) {
179 close(fd);
180 return -1;
183 return fd;
186 gboolean open_command(struct client_s *client, gchar **req)
188 gint fd;
189 struct stat st;
190 gchar *inbuf;
191 const gchar *filename = req[0];
192 guchar shakey[gcrykeysize];
193 guchar tkey[gcrykeysize];
194 gint cached = 0;
195 gsize insize = 0;
196 guint iter;
197 struct file_header_s {
198 guint iter;
199 guchar iv[gcryblocksize];
200 } file_header;
202 if (!filename || !*filename) {
203 send_error(client, EPWMD_COMMAND_SYNTAX);
204 return FALSE;
207 if (valid_filename(filename) == FALSE) {
208 send_error(client, EPWMD_INVALID_FILENAME);
209 return FALSE;
212 if (stat(filename, &st) == 0) {
213 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
214 log_write("%s: %s", filename, pwmd_strerror(EPWMD_NOT_A_FILE));
215 send_to_client(client, "ERR %03i %s\n", EPWMD_NOT_A_FILE, pwmd_strerror(EPWMD_NOT_A_FILE));
216 return FALSE;
219 client->mtime = st.st_mtime;
222 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
225 * New files don't need a key.
227 if (access(filename, R_OK|W_OK) != 0) {
228 if (errno != ENOENT) {
229 log_write("%s: %s", filename, strerror(errno));
230 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename,
231 strerror(errno));
232 return FALSE;
234 new_doc:
235 if ((client->xml = new_document()) == NULL) {
236 log_write("%s", strerror(errno));
237 send_to_client(client, "ERR %03i malloc(): %s\n", EPWMD_ERROR,
238 strerror(errno));
239 return FALSE;
242 client->len = strlen(client->xml);
244 if (cache_add_file(client->md5file, NULL) == FALSE) {
245 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
246 send_error(client, EPWMD_MAX_SLOTS);
247 return FALSE;
250 client->filename = g_strdup(filename);
251 return parse_xml(client);
254 if ((fd = open_file(filename, &st)) == -1) {
255 log_write("%s: %s", filename, strerror(errno));
256 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(errno));
257 return FALSE;
260 if (st.st_size == 0)
261 goto new_doc;
263 if (cache_get_key(client->md5file, shakey) == TRUE)
264 cached = 1;
265 else {
267 * No key specified and no matching filename found in the cache.
269 if (!req[1] || !*req[1]) {
270 close(fd);
271 send_error(client, EPWMD_KEY);
272 return FALSE;
276 insize = st.st_size - sizeof(struct file_header_s);
277 read(fd, &file_header, sizeof(struct file_header_s));
278 inbuf = gcry_malloc(insize);
279 read(fd, inbuf, insize);
280 close(fd);
282 again:
283 if (!cached) {
284 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
285 memset(req[1], 0, strlen(req[1]));
288 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
289 sizeof(file_header.iv)))) {
290 gcry_free(inbuf);
291 memset(shakey, 0, sizeof(shakey));
292 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
293 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
294 return FALSE;
297 if ((gcryerrno = gcry_cipher_setkey(client->gh, shakey, gcrykeysize))) {
298 gcry_free(inbuf);
299 memset(shakey, 0, sizeof(shakey));
300 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
301 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
302 return FALSE;
305 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
306 if (cached) {
307 cached = 0;
308 goto again;
311 memset(shakey, 0, sizeof(shakey));
312 gcry_free(inbuf);
313 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
314 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
315 return FALSE;
318 memcpy(tkey, shakey, sizeof(tkey));
319 tkey[0] ^= 1;
321 if ((gcryerrno = gcry_cipher_setkey(client->gh, tkey, gcrykeysize))) {
322 memset(tkey, 0, sizeof(tkey));
323 gcry_free(inbuf);
324 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
325 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
326 return FALSE;
329 iter = file_header.iter;
331 while (iter-- > 0) {
332 if ((gcryerrno = gcry_cipher_setiv(client->gh, file_header.iv,
333 sizeof(file_header.iv)))) {
334 memset(tkey, 0, sizeof(tkey));
335 gcry_free(inbuf);
336 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
337 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
338 return FALSE;
341 if (decrypt_xml(client->gh, inbuf, insize, NULL, 0) == FALSE) {
342 if (cached) {
343 cached = 0;
344 goto again;
347 memset(tkey, 0, sizeof(tkey));
348 gcry_free(inbuf);
349 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
350 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
351 return FALSE;
355 memset(tkey, 0, sizeof(tkey));
356 client->xml = inbuf;
357 client->len = insize;
359 if (g_strncasecmp(client->xml, "<?xml version=\"1.0\"?>", 21) != 0) {
360 send_error(client, EPWMD_BADKEY);
361 return FALSE;
364 if (!cached) {
365 if (cache_add_file(client->md5file, shakey) == FALSE) {
366 memset(shakey, 0, sizeof(shakey));
367 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
368 send_error(client, EPWMD_MAX_SLOTS);
369 return FALSE;
373 memset(shakey, 0, sizeof(shakey));
374 client->filename = g_strdup(filename);
375 return parse_xml(client);
379 * client->reader should be at the position in the document where the element
380 * search should start. It won't search past the closing account element.
382 static gboolean find_elements(struct client_s *client, xmlTextReaderPtr reader,
383 gchar **req, gint quiet)
385 gint i;
387 if (!req || !req[0]) {
388 if (!quiet)
389 send_error(client, EPWMD_COMMAND_SYNTAX);
390 return FALSE;
393 for (i = 0; req[i]; i++) {
394 if (find_element(reader, req[i], req[i+1] != NULL) == FALSE) {
395 if (!quiet)
396 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
398 return FALSE;
402 return TRUE;
405 gboolean do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
406 const gchar *filename, const xmlChar *data, size_t insize,
407 const guchar *shakey, guint iter)
409 gsize len = insize;
410 gint fd;
411 void *inbuf;
412 guchar tkey[gcrykeysize];
413 struct file_header_s {
414 guint iter;
415 guchar iv[gcryblocksize];
416 } file_header;
418 if (insize / gcryblocksize) {
419 len = (insize / gcryblocksize) * gcryblocksize;
421 if (insize % gcryblocksize)
422 len += gcryblocksize;
425 inbuf = gcry_calloc(1, len);
426 memcpy(inbuf, data, insize);
427 insize = len;
428 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
429 memcpy(tkey, shakey, sizeof(tkey));
430 tkey[0] ^= 1;
432 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
433 gcry_free(inbuf);
434 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
435 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
436 return FALSE;
439 file_header.iter = iter;
441 while (iter-- > 0) {
442 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
443 sizeof(file_header.iv)))) {
444 gcry_free(inbuf);
445 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
446 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
447 return FALSE;
450 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
451 == FALSE) {
452 gcry_free(inbuf);
453 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
454 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
455 return FALSE;
459 if ((gcryerrno = gcry_cipher_setiv(gh, file_header.iv,
460 sizeof(file_header.iv)))) {
461 gcry_free(inbuf);
462 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
463 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
464 return FALSE;
467 if ((gcryerrno = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
468 gcry_free(inbuf);
469 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
470 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
471 return FALSE;
474 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
475 == FALSE) {
476 gcry_free(inbuf);
477 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
478 send_to_client(client, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
479 return FALSE;
482 if (filename) {
483 if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
484 gcry_free(inbuf);
485 log_write("%s: %s", filename, strerror(errno));
486 send_to_client(client, "ERR %03i %s: %s\n", EPWMD_ERROR, filename, strerror(errno));
487 return FALSE;
490 else
492 * xml_import() from command line.
494 fd = STDOUT_FILENO;
496 write(fd, &file_header, sizeof(struct file_header_s));
497 write(fd, inbuf, insize);
499 if (filename)
500 close(fd);
502 gcry_free(inbuf);
503 return TRUE;
506 gboolean save_command(struct client_s *client, const gchar *filename, gchar *key)
508 xmlChar *xmlbuf;
509 gint len;
510 gint update = 1;
511 guchar shakey[gcrykeysize];
512 struct stat st;
514 if (stat(filename, &st) == 0 && client->mtime) {
515 if (client->mtime != st.st_mtime) {
516 log_write("%s: %s", filename, pwmd_strerror(EPWMD_FILE_MODIFIED));
517 send_error(client, EPWMD_FILE_MODIFIED);
518 return FALSE;
522 if (!key || !key) {
523 if (cache_get_key(client->md5file, shakey) == FALSE) {
524 send_error(client, EPWMD_KEY);
525 return FALSE;
528 update = 0;
530 else {
531 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
532 memset(key, 0, strlen(key));
535 xmlDocDumpFormatMemory(client->doc, &xmlbuf, &len, 0);
537 if (do_xml_encrypt(client, client->gh, filename, xmlbuf, len, shakey,
538 client->iter) == FALSE) {
539 memset(shakey, 0, sizeof(shakey));
540 xmlFree(xmlbuf);
541 return FALSE;
544 xmlFree(xmlbuf);
545 stat(filename, &st);
546 client->mtime = st.st_mtime;
548 if (!update) {
549 memset(shakey, 0, sizeof(shakey));
550 return TRUE;
553 if (cache_update_key(client->md5file, shakey) == FALSE) {
554 memset(shakey, 0, sizeof(shakey));
555 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
556 send_error(client, EPWMD_MAX_SLOTS);
557 return FALSE;
560 memset(shakey, 0, sizeof(shakey));
561 return TRUE;
564 static gboolean contains_whitespace(const gchar *str)
566 const gchar *p = str;
568 for (; *p; p++) {
569 if (g_ascii_isspace(*p) == TRUE) {
570 return TRUE;
574 return FALSE;
578 * the 'reader' should be at the element of the document where the elements
579 * 'req' are to be created.
581 static gboolean create_elements(struct client_s *client, xmlTextReaderPtr reader,
582 gchar **req, gint novalue)
584 gint i;
585 gboolean ret = TRUE;
586 xmlNodePtr r;
588 r = xmlTextReaderCurrentNode(reader);
590 if (xmlTextReaderDepth(reader) > 1)
591 r = r->parent;
593 for (i = 0; req[i]; i++) {
594 xmlNodePtr n;
597 * Whitespace is allowed in values or attributes but not in element
598 * names.
600 if (req[i+1] && valid_xml_element((xmlChar *)req[i]) == FALSE) {
601 send_error(client, EPWMD_INVALID_ELEMENT);
602 return FALSE;
606 * The value of the element tree.
608 if (!req[i+1] && !novalue) {
610 * Prevent creating 'text' elements in the root of the account.
612 if (i < 1) {
613 send_error(client, EPWMD_ROOT_TEXT_ELEMENT);
614 return FALSE;
618 * FIXME ?? overwriting an element tree with a text element:
620 * STORE account element element2 value
621 * STORE account element value
623 * would remove the existing element tree. This may be a bug or
624 * feature.
626 xmlNodeSetContent(r, (xmlChar *)req[i]);
627 break;
630 if ((n = find_node(r, (xmlChar *)req[i])) == NULL) {
631 n = xmlNewNode(NULL, (xmlChar *)req[i]);
632 r = xmlAddChild(r, n);
634 else
635 r = n;
637 if (!req[i+1] && novalue)
638 return TRUE;
641 return ret;
645 * FIXME reuse reader handle
647 static gboolean reset_reader(xmlDocPtr doc, xmlTextReaderPtr *reader)
649 if (reader) {
650 xmlFreeTextReader(*reader);
651 *reader = NULL;
654 if ((*reader = xmlReaderWalker(doc)) == NULL)
655 return FALSE;
657 return TRUE;
660 static void delete_node(xmlNodePtr n)
662 xmlUnlinkNode(n);
663 xmlFreeNode(n);
666 gboolean delete_command(struct client_s *client, gchar **req)
668 gint i = 1;
669 xmlNodePtr n;
670 xmlErrorPtr xml_error;
672 if (reset_reader(client->doc, &client->reader) == FALSE) {
673 xml_error = xmlGetLastError();
674 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
675 send_error(client, EPWMD_LIBXML_ERROR);
676 return FALSE;
679 if (find_account(client->reader, req[0]) == FALSE) {
680 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
681 return FALSE;
684 n = xmlTextReaderCurrentNode(client->reader);
687 * No sub-node defined. Remove the entire node (account).
689 if (!req[i]) {
690 if (n)
691 delete_node(n);
692 return TRUE;
696 * Remove matching sub-nodes starting from the root of the account.
698 while (req[i] && find_element(client->reader, req[i++], req[i] != NULL) == TRUE)
699 n = xmlTextReaderCurrentNode(client->reader);
701 if (n && xmlStrcmp(n->name, (xmlChar *)req[i-1]) == 0 &&
702 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_ELEMENT)
703 delete_node(n);
704 else {
705 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
706 return FALSE;
709 return TRUE;
712 gboolean store_command(struct client_s *client, gchar **req)
714 xmlErrorPtr xml_error;
716 again:
717 if (reset_reader(client->doc, &client->reader) == FALSE) {
718 xml_error = xmlGetLastError();
719 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
720 send_error(client, EPWMD_LIBXML_ERROR);
721 return FALSE;
724 if (!req[0]) {
725 send_error(client, EPWMD_COMMAND_SYNTAX);
726 return FALSE;
729 if (find_account(client->reader, req[0]) == FALSE) {
730 if (contains_whitespace(req[0]) == TRUE) {
731 send_error(client, EPWMD_INVALID_ELEMENT);
732 return FALSE;
735 if (new_account(client->doc, req[0]) == FALSE) {
736 send_error(client, EPWMD_ERROR);
737 return FALSE;
740 goto again;
743 xmlTextReaderNext(client->reader);
744 return create_elements(client, client->reader, req+1, 0);
747 static gboolean do_get_command(struct client_s *client, xmlTextReaderPtr *reader,
748 gchar **req, xmlChar **content, gint quiet, gint list)
750 xmlNodePtr n;
751 xmlAttrPtr a;
752 gchar **nreq;
753 gboolean ret;
754 xmlChar *p;
755 xmlErrorPtr xml_error;
757 if (reset_reader(client->doc, reader) == FALSE) {
758 if (!quiet) {
759 xml_error = xmlGetLastError();
760 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
761 send_error(client, EPWMD_LIBXML_ERROR);
764 return FALSE;
767 if (find_account(*reader, req[0]) == FALSE) {
768 if (!quiet && !list)
769 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
770 return FALSE;
774 * May be an account with only a TARGET attribute.
776 if (req[1]) {
777 if (find_elements(client, *reader, req + 1, (list) ? 1 : quiet) == FALSE)
778 return FALSE;
781 if ((n = xmlTextReaderCurrentNode(*reader)) == NULL) {
782 if (!quiet) {
783 xml_error = xmlGetLastError();
784 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
785 send_error(client, EPWMD_LIBXML_ERROR);
788 return FALSE;
792 * If the current element has a TARGET attribute, the value of the
793 * attribute is an element path somewhere else in the document. Use this
794 * value and not any TEXT element value. The value may be another element
795 * with a TARGET attribute and this function will recurse until a non-TARGET
796 * attribute in the element is found.
798 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
799 if ((p = xmlNodeGetContent(a->children)) != NULL) {
800 if (strchr((gchar *)p, '\t') != NULL) {
801 if ((nreq = split_input_line((gchar *)p, "\t", 0)) == NULL) {
802 xmlFree(p);
804 if (!quiet)
805 send_error(client, EPWMD_INVALID_ELEMENT);
806 return FALSE;
809 else {
810 if ((nreq = split_input_line((gchar *)p, " ", 0)) == NULL) {
811 xmlFree(p);
813 if (!quiet)
814 send_error(client, EPWMD_INVALID_ELEMENT);
815 return FALSE;
819 xmlFree(p);
820 ret = do_get_command(client, reader, nreq, content, quiet, list);
821 g_strfreev(nreq);
822 return ret;
826 switch (xmlTextReaderNext(*reader)) {
827 case -1:
828 if (!quiet) {
829 xml_error = xmlGetLastError();
830 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
831 send_error(client, EPWMD_LIBXML_ERROR);
834 return FALSE;
835 case 0:
836 if (!quiet)
837 send_error(client, EPWMD_EMPTY_ELEMENT);
838 return FALSE;
839 default:
840 break;
843 switch (xmlTextReaderNodeType(*reader)) {
844 case XML_READER_TYPE_END_ELEMENT:
846 * May be an empty element after an ATTR DELETE TARGET command.
848 return TRUE;
849 case XML_READER_TYPE_TEXT:
850 break;
851 case -1:
852 if (!quiet) {
853 xml_error = xmlGetLastError();
854 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
855 send_error(client, EPWMD_LIBXML_ERROR);
858 return FALSE;
859 default:
860 if (!quiet) {
861 if (n->children && !list)
862 send_error(client, EPWMD_TRAILING_ELEMENT);
863 else if (!n->children && !list)
864 send_error(client, EPWMD_INVALID_ELEMENT);
866 return FALSE;
869 if ((*content = xmlNodeGetContent(n)) == NULL) {
870 if (!quiet)
871 send_error(client, EPWMD_EMPTY_ELEMENT);
872 return FALSE;
875 return TRUE;
879 * Retrieves the value associated with the element tree 'req'.
881 gboolean get_command(struct client_s *client, xmlTextReaderPtr *reader,
882 gchar **req, gint quiet)
884 xmlChar *content = NULL;
886 if (do_get_command(client, reader, req, &content, quiet, 0) == FALSE)
887 return FALSE;
889 if (!content) {
890 if (!quiet)
891 send_error(client, EPWMD_EMPTY_ELEMENT);
893 return FALSE;
896 send_to_client(client, "BEGIN %li\n%s\nOK \n", xmlStrlen(content), content);
897 xmlFree(content);
898 return TRUE;
901 #ifdef DEBUG
902 static gchar *element_path_to_req(const gchar *account, xmlChar *path,
903 const xmlChar *content)
904 #else
905 static gchar *element_path_to_req(const gchar *account, xmlChar *path)
906 #endif
908 xmlChar *p = path;
909 gint n;
910 gchar *buf;
912 if (!p)
913 return NULL;
915 for (n = 0; *p && n < 3; p++) {
916 if (*p == '/')
917 n++;
920 if (strstr((gchar *)p, "text()") != NULL)
921 p[xmlStrlen(p) - 7] = 0;
923 for (n = 0; p[n]; n++) {
924 if (p[n] == '/')
925 p[n] = '\t';
928 #ifdef DEBUG
929 buf = g_strdup_printf("%s\t%s\t%s", account, p, content);
930 #else
931 buf = g_strdup_printf("%s\t%s", account, p);
932 #endif
933 return buf;
936 static gboolean append_to_array(gchar ***array, gint *total, const gchar *str)
938 gchar **a;
939 gint t = *total;
941 if ((a = g_realloc(*array, (t + 2) * sizeof(gchar *))) == NULL)
942 return FALSE;
944 a[t++] = g_strdup(str);
945 a[t] = NULL;
946 *total = t;
947 *array = a;
948 return TRUE;
951 gboolean list_command(struct client_s *client, gchar *str)
953 gchar *dst = NULL;
954 gchar *p = str;
955 gchar **elements = NULL;
956 gint pwmd_errno = -1;
957 gchar *account;
958 gint total = 0;
959 xmlChar *path = NULL;
960 xmlAttrPtr a;
961 xmlNodePtr n;
962 xmlChar *content;
963 xmlTextReaderPtr r = NULL;
964 gchar **req = NULL, **nreq;
965 gboolean ret;
966 gint type;
967 gchar *line;
968 xmlErrorPtr xml_error;
970 if (reset_reader(client->doc, &client->reader) == FALSE) {
971 xml_error = xmlGetLastError();
972 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
973 send_error(client, EPWMD_LIBXML_ERROR);
974 return FALSE;
977 if (strchr(p, ' ') == NULL) {
978 list_only:
979 if (list_accounts(client->reader, &dst, &pwmd_errno) == FALSE) {
980 send_error(client, pwmd_errno);
981 return FALSE;
983 else {
984 send_to_client(client, "BEGIN %i\n%s\nOK \n",
985 g_utf8_strlen(dst, -1), dst);
986 memset(dst, 0, strlen(dst));
987 g_free(dst);
990 return TRUE;
993 p = str + 5;
995 while (*p && isspace(*p))
996 p++;
998 if (!*p)
999 goto list_only;
1001 if (strchr(p, '\t') != NULL) {
1002 if ((req = split_input_line(p, "\t", 0)) == NULL) {
1003 send_error(client, EPWMD_COMMAND_SYNTAX);
1004 return FALSE;
1007 if (find_account(client->reader, req[0]) == FALSE) {
1008 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1009 return FALSE;
1012 if (find_elements(client, client->reader, req + 1, 0) == FALSE) {
1013 g_strfreev(req);
1014 return FALSE;
1017 else {
1018 if (find_account(client->reader, p) == FALSE) {
1019 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1020 return FALSE;
1024 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1025 xml_error = xmlGetLastError();
1026 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1027 send_error(client, EPWMD_LIBXML_ERROR);
1028 return FALSE;
1031 if ((a = xmlHasProp(n, (xmlChar *)"target")) != NULL) {
1032 if (reset_reader(client->doc, &client->reader) == FALSE) {
1033 send_error(client, EPWMD_LIBXML_ERROR);
1034 return FALSE;
1037 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1038 if (find_account(client->reader, (gchar *)content) == FALSE) {
1039 xmlFree(content);
1040 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1041 return FALSE;
1044 xmlFree(content);
1048 account = (req) ? g_strdup(req[0]) : g_strdup(p);
1050 if (req)
1051 g_strfreev(req);
1053 while (xmlTextReaderNext(client->reader) == 1) {
1054 again:
1055 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1056 xml_error = xmlGetLastError();
1057 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1058 send_error(client, EPWMD_LIBXML_ERROR);
1059 return FALSE;
1062 if (xmlTextReaderDepth(client->reader) == 1 &&
1063 xmlStrcmp(n->name, (xmlChar *)"account") == 0 &&
1064 xmlTextReaderNodeType(client->reader) == XML_READER_TYPE_END_ELEMENT)
1065 break;
1068 * If the current element has a TARGET attribute, the value of the
1069 * attribute is an element path somewhere else in the document. Use this
1070 * value and not any TEXT element value.
1072 type = xmlTextReaderNodeType(client->reader);
1073 a = xmlHasProp(n, (xmlChar *)"target");
1075 if (type == XML_READER_TYPE_ELEMENT && a) {
1076 if ((content = xmlNodeGetContent(a->children)) != NULL) {
1077 path = xmlGetNodePath(n);
1079 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
1080 if (elements)
1081 g_strfreev(elements);
1083 xmlFree(path);
1084 xmlFree(content);
1085 send_error(client, EPWMD_INVALID_ELEMENT);
1086 return FALSE;
1089 xmlFree(content);
1090 r = NULL;
1092 if ((ret = do_get_command(client, &r, nreq, &content, 0, 1)) == TRUE) {
1093 if (content && *content) {
1094 #ifdef DEBUG
1095 line = element_path_to_req(account, path, content);
1096 #else
1097 line = element_path_to_req(account, path);
1098 #endif
1099 xmlFree(content);
1101 if (append_to_array(&elements, &total, line) == FALSE) {
1102 if (elements)
1103 g_strfreev(elements);
1105 xmlFree(path);
1106 memset(line, 0, g_utf8_strlen(line, -1));
1107 g_free(line);
1108 g_strfreev(nreq);
1109 xmlFreeTextReader(r);
1110 send_error(client, EPWMD_ERROR);
1111 return FALSE;
1114 memset(line, 0, g_utf8_strlen(line, -1));
1115 g_free(line);
1118 if (xmlTextReaderNext(client->reader) == 1) {
1119 if (xmlTextReaderNodeType(client->reader) !=
1120 XML_READER_TYPE_TEXT) {
1121 g_strfreev(nreq);
1122 xmlFreeTextReader(r);
1123 xmlFree(path);
1124 goto again;
1129 g_strfreev(nreq);
1130 xmlFreeTextReader(r);
1131 xmlFree(path);
1132 continue;
1136 if (type == XML_READER_TYPE_TEXT) {
1137 xmlChar *np = xmlGetNodePath(n);
1139 #ifdef DEBUG
1140 content = xmlNodeGetContent(n);
1141 line = element_path_to_req(account, np, content);
1142 xmlFree(content);
1143 #else
1144 line = element_path_to_req(account, np);
1145 #endif
1146 xmlFree(np);
1147 append_to_array(&elements, &total, line);
1148 memset(line, 0, g_utf8_strlen(line, -1));
1149 g_free(line);
1153 if (!elements) {
1154 send_error(client, EPWMD_EMPTY_ELEMENT);
1155 g_free(account);
1156 return FALSE;
1159 g_free(account);
1160 line = g_strjoinv("\n", elements);
1161 send_to_client(client, "BEGIN %li\n%s\nOK \n",
1162 g_utf8_strlen(line, -1), line);
1163 g_strfreev(elements);
1164 g_free(line);
1165 return TRUE;
1169 * The client->reader handle should be at the element in the document where
1170 * the attribute will be created or modified.
1172 static gboolean add_attribute(struct client_s *client, const gchar *name, const gchar *value)
1174 xmlAttrPtr a;
1175 xmlNodePtr n;
1176 xmlErrorPtr xml_error;
1178 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1179 xml_error = xmlGetLastError();
1180 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1181 send_error(client, EPWMD_LIBXML_ERROR);
1182 return FALSE;
1185 if ((a = xmlHasProp(n, (xmlChar *)name)) == NULL)
1186 a = xmlNewProp(n, (xmlChar *)name, (xmlChar *)value);
1187 else
1188 xmlNodeSetContent(a->children, (xmlChar *)value);
1190 return TRUE;
1194 * req[0] - element path
1196 static gboolean attribute_list(struct client_s *client, gchar **req)
1198 gchar **attrlist = NULL;
1199 gint i = 0;
1200 gchar **epath = NULL;
1201 xmlAttrPtr a;
1202 xmlNodePtr n, an;
1203 gchar *line;
1204 xmlErrorPtr xml_error;
1206 if (!req || !req[0]) {
1207 send_error(client, EPWMD_COMMAND_SYNTAX);
1208 return FALSE;
1211 if ((epath = split_input_line(req[0], "\t", 0)) == NULL) {
1213 * The first argument may be only an account.
1215 if ((epath = split_input_line(req[0], " ", 0)) == NULL) {
1216 send_error(client, EPWMD_COMMAND_SYNTAX);
1217 return FALSE;
1221 if (reset_reader(client->doc, &client->reader) == FALSE) {
1222 xml_error = xmlGetLastError();
1223 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1224 send_error(client, EPWMD_LIBXML_ERROR);
1225 goto blah;
1228 if (find_account(client->reader, epath[0]) == FALSE) {
1229 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1230 goto blah;
1233 if (epath[1]) {
1234 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1235 goto blah;
1238 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1239 xml_error = xmlGetLastError();
1240 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1241 send_error(client, EPWMD_LIBXML_ERROR);
1242 goto blah;
1245 for (a = n->properties; a; a = a->next) {
1246 if ((attrlist = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1247 log_write("%s(%i): g_realloc() failed", __FILE__, __LINE__);
1248 send_error(client, EPWMD_ERROR);
1249 goto blah;
1252 an = a->children;
1253 attrlist[i++] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1254 attrlist[i] = NULL;
1257 if (!attrlist) {
1258 send_error(client, EPWMD_EMPTY_ELEMENT);
1259 goto blah;
1262 line = g_strjoinv("\n", attrlist);
1263 send_to_client(client, "BEGIN %li\n%s\n", g_utf8_strlen(line, -1), line);
1264 g_free(line);
1265 g_strfreev(epath);
1266 g_strfreev(attrlist);
1267 return TRUE;
1268 blah:
1269 g_strfreev(epath);
1270 return FALSE;
1274 * req[0] - attribute
1275 * req[1] - element path
1277 static gboolean attribute_delete(struct client_s *client, gchar **req)
1279 xmlAttrPtr a;
1280 xmlNodePtr n;
1281 gchar **epath = NULL;
1282 xmlErrorPtr xml_error;
1284 if (!req || !req[0] || !req[1]) {
1285 send_error(client, EPWMD_COMMAND_SYNTAX);
1286 return FALSE;
1289 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1291 * The first argument may be only an account.
1293 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1294 send_error(client, EPWMD_COMMAND_SYNTAX);
1295 return FALSE;
1300 * Don't remove the NAME attribute for the account element.
1302 if (!epath[1] && g_ascii_strcasecmp(req[0], "NAME") == 0) {
1303 send_error(client, EPWMD_ATTR_SYNTAX);
1304 goto blah;
1307 if (reset_reader(client->doc, &client->reader) == FALSE) {
1308 xml_error = xmlGetLastError();
1309 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1310 send_error(client, EPWMD_LIBXML_ERROR);
1311 goto blah;
1314 if (find_account(client->reader, epath[0]) == FALSE) {
1315 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1316 goto blah;
1319 if (epath[1]) {
1320 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1321 goto blah;
1324 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1325 xml_error = xmlGetLastError();
1326 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1327 send_error(client, EPWMD_LIBXML_ERROR);
1328 goto blah;
1331 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL) {
1332 send_error(client, EPWMD_ATTR_NOT_FOUND);
1333 goto blah;
1336 if (xmlRemoveProp(a) == -1) {
1337 xml_error = xmlGetLastError();
1338 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1339 send_error(client, EPWMD_LIBXML_ERROR);
1340 goto blah;
1343 g_strfreev(epath);
1344 return TRUE;
1345 blah:
1346 g_strfreev(epath);
1347 return FALSE;
1351 * req[0] - source element path
1352 * req[1] - destination element path
1354 static gboolean target_attribute(struct client_s *client, gchar **req)
1356 gchar **src, **dst, *line;
1357 xmlErrorPtr xml_error;
1359 if (!req || !req[0] || !req[1]) {
1360 send_error(client, EPWMD_COMMAND_SYNTAX);
1361 return FALSE;
1364 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1366 * The first argument may be only an account.
1368 if ((src = split_input_line(req[0], " ", 0)) == NULL) {
1369 send_error(client, EPWMD_COMMAND_SYNTAX);
1370 return FALSE;
1374 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1376 * The first argument may be only an account.
1378 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1379 send_error(client, EPWMD_COMMAND_SYNTAX);
1380 g_strfreev(src);
1381 goto blah;
1386 * Prevent an element tree pointing to only and account. Accounts require
1387 * at least one element. Accounts pointing to accounts are allowed.
1389 if ((!src[1] && dst[1]) || (!dst[1] && src[1])) {
1390 send_error(client, EPWMD_ATTR_SYNTAX);
1391 g_strfreev(src);
1392 g_strfreev(dst);
1393 goto blah;
1396 if (reset_reader(client->doc, &client->reader) == FALSE) {
1397 xml_error = xmlGetLastError();
1398 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1399 send_error(client, EPWMD_LIBXML_ERROR);
1400 g_strfreev(src);
1401 g_strfreev(dst);
1402 goto blah;
1406 * Make sure the destination element path exists.
1408 if (find_account(client->reader, dst[0]) == FALSE) {
1409 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1410 g_strfreev(src);
1411 g_strfreev(dst);
1412 goto blah;
1415 if (dst[1]) {
1416 if (find_elements(client, client->reader, dst+1, 0) == FALSE) {
1417 g_strfreev(src);
1418 g_strfreev(dst);
1419 goto blah;
1423 if (reset_reader(client->doc, &client->reader) == FALSE) {
1424 xml_error = xmlGetLastError();
1425 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1426 send_error(client, EPWMD_LIBXML_ERROR);
1427 g_strfreev(src);
1428 g_strfreev(dst);
1429 goto blah;
1433 * If the source element tree doesn't exist, create it.
1435 if (find_account(client->reader, src[0]) == FALSE) {
1436 if (new_account(client->doc, src[0]) == FALSE) {
1437 xml_error = xmlGetLastError();
1438 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1439 send_error(client, EPWMD_LIBXML_ERROR);
1440 g_strfreev(src);
1441 g_strfreev(dst);
1442 goto blah;
1445 if (reset_reader(client->doc, &client->reader) == FALSE) {
1446 xml_error = xmlGetLastError();
1447 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1448 send_error(client, EPWMD_LIBXML_ERROR);
1449 g_strfreev(src);
1450 g_strfreev(dst);
1451 goto blah;
1454 if (find_account(client->reader, src[0]) == FALSE) {
1455 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1456 g_strfreev(src);
1457 g_strfreev(dst);
1458 goto blah;
1462 if (src[1]) {
1463 if (find_elements(client, client->reader, src+1, 1) == FALSE) {
1464 if (reset_reader(client->doc, &client->reader) == FALSE) {
1465 xml_error = xmlGetLastError();
1466 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1467 send_error(client, EPWMD_LIBXML_ERROR);
1468 g_strfreev(src);
1469 g_strfreev(dst);
1470 goto blah;
1473 if (find_account(client->reader, src[0]) == FALSE) {
1474 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1475 g_strfreev(src);
1476 g_strfreev(dst);
1477 goto blah;
1480 xmlTextReaderNext(client->reader);
1482 if (create_elements(client, client->reader, src+1, 1) == FALSE) {
1483 g_strfreev(src);
1484 g_strfreev(dst);
1485 goto blah;
1488 if (reset_reader(client->doc, &client->reader) == FALSE) {
1489 xml_error = xmlGetLastError();
1490 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1491 send_error(client, EPWMD_LIBXML_ERROR);
1492 g_strfreev(src);
1493 g_strfreev(dst);
1494 goto blah;
1497 if (find_account(client->reader, src[0]) == FALSE) {
1498 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1499 g_strfreev(src);
1500 g_strfreev(dst);
1501 goto blah;
1504 if (find_elements(client, client->reader, src+1, 0) == FALSE) {
1505 g_strfreev(src);
1506 g_strfreev(dst);
1507 goto blah;
1512 line = g_strjoinv("\t", dst);
1514 if (add_attribute(client, "target", line) == FALSE) {
1515 g_free(line);
1516 g_strfreev(src);
1517 g_strfreev(dst);
1518 goto blah;
1521 g_strfreev(src);
1522 g_strfreev(dst);
1523 g_free(line);
1524 return TRUE;
1525 blah:
1526 g_strfreev(src);
1527 g_strfreev(dst);
1528 return FALSE;
1532 * req[0] - account name
1533 * req[1] - new name
1535 static gboolean name_attribute(struct client_s *client, gchar **req)
1537 xmlErrorPtr xml_error;
1539 if (reset_reader(client->doc, &client->reader) == FALSE) {
1540 xml_error = xmlGetLastError();
1541 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1542 send_error(client, EPWMD_LIBXML_ERROR);
1543 return FALSE;
1546 if (find_account(client->reader, req[0]) == FALSE) {
1547 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1548 return FALSE;
1551 if (strcmp(req[0], req[1]) == 0)
1552 return TRUE;
1554 if (reset_reader(client->doc, &client->reader) == FALSE) {
1555 xml_error = xmlGetLastError();
1556 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1557 send_error(client, EPWMD_LIBXML_ERROR);
1558 return FALSE;
1562 * Will not overwrite an existing account.
1564 if (find_account(client->reader, req[1]) == TRUE) {
1565 send_error(client, EPWMD_ACCOUNT_EXISTS);
1566 return FALSE;
1569 if (reset_reader(client->doc, &client->reader) == FALSE) {
1570 xml_error = xmlGetLastError();
1571 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1572 send_error(client, EPWMD_LIBXML_ERROR);
1573 return FALSE;
1576 if (find_account(client->reader, req[0]) == FALSE) {
1577 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1578 return FALSE;
1582 * Whitespace not allowed in account names.
1584 if (contains_whitespace(req[1]) == TRUE) {
1585 send_error(client, EPWMD_ATTR_SYNTAX);
1586 return FALSE;
1589 return add_attribute(client, "name", req[1]);
1593 * req[0] - attribute
1594 * req[1] - element path
1596 * If the element has a "target" attribute it won't be "followed".
1598 static gboolean attribute_get(struct client_s *client, gchar **req)
1600 xmlNodePtr n;
1601 xmlChar *a;
1602 gchar **nreq = NULL;
1603 xmlErrorPtr xml_error;
1605 if (!req || !req[0] || !req[1]) {
1606 send_error(client, EPWMD_COMMAND_SYNTAX);
1607 return FALSE;
1610 if (strchr(req[1], '\t')) {
1611 if ((nreq = split_input_line(req[1], "\t", 0)) == NULL) {
1612 send_error(client, EPWMD_COMMAND_SYNTAX);
1613 return FALSE;
1616 else {
1617 if ((nreq = split_input_line(req[1], " ", 0)) == NULL) {
1618 send_error(client, EPWMD_COMMAND_SYNTAX);
1619 return FALSE;
1623 if (reset_reader(client->doc, &client->reader) == FALSE) {
1624 xml_error = xmlGetLastError();
1625 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1626 g_strfreev(nreq);
1627 send_error(client, EPWMD_LIBXML_ERROR);
1628 return FALSE;
1631 if (find_account(client->reader, nreq[0]) == FALSE) {
1632 g_strfreev(nreq);
1633 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1634 return FALSE;
1637 if (nreq[1]) {
1638 if (find_elements(client, client->reader, nreq + 1, 0) == FALSE) {
1639 g_strfreev(nreq);
1640 return FALSE;
1644 g_strfreev(nreq);
1646 if ((n = xmlTextReaderCurrentNode(client->reader)) == NULL) {
1647 xml_error = xmlGetLastError();
1648 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1649 send_error(client, EPWMD_LIBXML_ERROR);
1650 return FALSE;
1653 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL) {
1654 send_error(client, EPWMD_ATTR_NOT_FOUND);
1655 return FALSE;
1658 send_to_client(client, "BEGIN %li\n%s\n", xmlStrlen(a), a);
1659 xmlFree(a);
1660 return TRUE;
1664 * req[0] - attribute
1665 * req[1] - element path
1666 * req[2] - value
1668 static gboolean attribute_set(struct client_s *client, gchar **req)
1670 gchar **epath = NULL;
1671 xmlErrorPtr xml_error;
1673 if (!req || !req[0] || !req[1] || !req[2]) {
1674 send_error(client, EPWMD_COMMAND_SYNTAX);
1675 return FALSE;
1679 * Reserved attribute names.
1681 if (g_ascii_strcasecmp(req[0], "NAME") == 0) {
1683 * Only reserved for the account element. Not the rest of the
1684 * document.
1686 if (strchr(req[1], '\t') == NULL)
1687 return name_attribute(client, req + 1);
1689 else if (g_ascii_strcasecmp(req[0], "TARGET") == 0)
1690 return target_attribute(client, req + 1);
1692 if ((epath = split_input_line(req[1], "\t", 0)) == NULL) {
1694 * The first argument may be only an account.
1696 if ((epath = split_input_line(req[1], " ", 0)) == NULL) {
1697 send_error(client, EPWMD_COMMAND_SYNTAX);
1698 return FALSE;
1702 if (reset_reader(client->doc, &client->reader) == FALSE) {
1703 xml_error = xmlGetLastError();
1704 log_write("%s(%i): %s", __FILE__, __LINE__, xml_error->message);
1705 send_error(client, EPWMD_LIBXML_ERROR);
1706 goto blah;
1709 if (find_account(client->reader, epath[0]) == FALSE) {
1710 send_error(client, EPWMD_ELEMENT_NOT_FOUND);
1711 goto blah;
1714 if (epath[1]) {
1715 if ((find_elements(client, client->reader, epath+1, 0)) == FALSE)
1716 goto blah;
1719 g_strfreev(epath);
1720 return add_attribute(client, req[0], req[2]);
1721 blah:
1722 g_strfreev(epath);
1723 return FALSE;
1727 * req[0] - command
1728 * req[1] - attribute name or element path if command is LIST
1729 * req[2] - element path
1730 * req[2] - element path or value
1732 gboolean attr_command(struct client_s *client, gchar **req)
1734 if (!req || !req[0] || !req[1]) {
1735 send_error(client, EPWMD_COMMAND_SYNTAX);
1736 return FALSE;
1739 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1740 return attribute_set(client, req+1);
1741 if (g_ascii_strcasecmp(req[0], "GET") == 0)
1742 return attribute_get(client, req+1);
1743 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1744 return attribute_delete(client, req+1);
1745 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1746 return attribute_list(client, req+1);
1747 else
1748 send_error(client, EPWMD_COMMAND_SYNTAX);
1750 return FALSE;
1753 static gboolean cache_test(const guchar *md5filename, gint reset)
1755 void *p;
1756 file_cache_t f;
1757 glong len;
1759 for (p = shm_data, len = 0; len <= cache_size;) {
1760 memcpy(&f, p, sizeof(file_cache_t));
1762 if (reset == 2) {
1763 memset(&f, 0, sizeof(file_cache_t));
1764 memcpy(p, &f, sizeof(file_cache_t));
1765 p += sizeof(file_cache_t);
1766 len += sizeof(file_cache_t);
1768 if (len + sizeof(file_cache_t) > cache_size)
1769 break;
1770 continue;
1773 if (f.used == TRUE) {
1774 if (memcmp((gchar *)f.filename, (gchar *)md5filename, sizeof(f.filename)) == 0) {
1775 if (reset == 1) {
1776 memset(&f, 0, sizeof(file_cache_t));
1777 memcpy(p, &f, sizeof(file_cache_t));
1778 return TRUE;
1781 return (f.key[0]) ? TRUE : FALSE;
1785 p += sizeof(file_cache_t);
1786 len += sizeof(file_cache_t);
1788 if (len + sizeof(file_cache_t) > cache_size)
1789 break;
1792 return (reset == 2) ? TRUE : FALSE;
1795 static gboolean file_exists(const gchar *filename)
1797 struct stat st;
1799 if (access(filename, R_OK) == -1)
1800 return FALSE;
1802 stat(filename, &st);
1804 if (st.st_size == 0)
1805 return FALSE;
1807 return TRUE;
1810 gboolean cache_command(struct client_s *client, gchar **req)
1812 guchar md5file[16];
1814 if (g_ascii_strcasecmp(req[0], "clear") == 0) {
1815 if (!req[1]) {
1816 send_error(client, EPWMD_COMMAND_SYNTAX);
1817 return FALSE;
1820 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1822 if (cache_test(md5file, 1) == FALSE) {
1823 send_error(client, EPWMD_CACHE_NOT_FOUND);
1824 return FALSE;
1827 return TRUE;
1829 else if (g_ascii_strcasecmp(req[0], "clearall") == 0) {
1830 cache_test(client->md5file, 2);
1831 return TRUE;
1833 else if (g_ascii_strcasecmp(req[0], "iscached") == 0) {
1834 if (!req[1]) {
1835 send_error(client, EPWMD_COMMAND_SYNTAX);
1836 return FALSE;
1839 if (file_exists(req[1]) == FALSE) {
1840 send_error(client, EPWMD_FILE_NOT_FOUND);
1841 return FALSE;
1844 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
1846 if (cache_test(md5file, 0) == FALSE) {
1847 send_error(client, EPWMD_CACHE_NOT_FOUND);
1848 return FALSE;
1851 return TRUE;
1853 else
1854 send_error(client, EPWMD_COMMAND_SYNTAX);
1856 return FALSE;
1859 gboolean help_command(struct client_s *client, const gchar *what)
1861 gchar *line;
1863 if (!what || !*what)
1864 line =
1865 "NFO Try 'HELP COMMAND' for command help\n"
1866 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
1867 else if (g_ascii_strcasecmp(what, "GET") == 0)
1868 line =
1869 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
1870 "NFO <account> is the account to work on and <element>\n"
1871 "NFO is the element wanted.\n"
1872 "NFO -\n"
1873 "NFO Example: GET isp <TAB> imap <TAB> port\n"
1874 "NFO GET isp <TAB> username\n";
1875 else if (g_ascii_strcasecmp(what, "QUIT") == 0)
1876 line =
1877 "NFO syntax: QUIT\n"
1878 "NFO close the connection\n";
1879 else if (g_ascii_strcasecmp(what, "DELETE") == 0)
1880 line =
1881 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
1882 else if (g_ascii_strcasecmp(what, "STORE") == 0)
1883 line =
1884 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
1885 "NFO <account> is the account to work on and <element>\n"
1886 "NFO is the element to create or modify\n"
1887 "NFO -\n"
1888 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
1889 "NFO STORE isp <TAB> username <TAB> someuser\n";
1890 else if (g_ascii_strcasecmp(what, "OPEN") == 0)
1891 line =
1892 "NFO syntax: OPEN <filename> [<key>]\n"
1893 "NFO opens a (new) file\n";
1894 else if (g_ascii_strcasecmp(what, "LIST") == 0)
1895 line =
1896 "NFO syntax: LIST [account]\n"
1897 "NFO shows available accounts or account elements\n";
1898 else if (g_ascii_strcasecmp(what, "ATTR") == 0)
1899 line =
1900 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
1901 "NFO ATTR SET NAME account value\n"
1902 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
1903 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
1904 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
1905 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
1906 "NFO ATTR LIST account[<TAB>element[...]]\n";
1907 else if (g_ascii_strcasecmp(what, "SAVE") == 0)
1908 line =
1909 "NFO syntax: SAVE [<key>]\n"
1910 "NFO save any changes to the opened file using <key>\n";
1911 else if (g_ascii_strcasecmp(what, "CACHE") == 0)
1912 line =
1913 "NFO syntax: CACHE [CLEARALL | CLEAR <filename> | ISCACHED <filename>]\n"
1914 "NFO tests or clears the cache entry for <filename>\n";
1915 else if (g_ascii_strcasecmp(what, "DUMP") == 0)
1916 line =
1917 "NFO syntax: DUMP\n"
1918 "NFO shows the in memory XML document\n";
1919 else {
1920 send_error(client, EPWMD_COMMAND_SYNTAX);
1921 return FALSE;
1924 send_to_client(client, "%sOK \n", line);
1925 return TRUE;
1928 gboolean dump_command(struct client_s *client)
1930 xmlChar *xml;
1931 gssize len;
1933 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
1934 send_to_client(client, "BEGIN %li\n%s", len, xml);
1935 xmlFree(xml);
1936 return TRUE;