1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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
24 #include <sys/types.h>
29 #include <glib/gprintf.h>
38 #include "pwmd_error.h"
41 static gboolean
encrypt_xml(gcry_cipher_hd_t gh
, void *key
, gsize keysize
,
42 guchar
*iv
, void *outbuf
, gsize outsize
, void *inbuf
, gsize insize
)
44 if ((gcryerrno
= gcry_cipher_reset(gh
))) {
45 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
49 if ((gcryerrno
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
50 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
54 if ((gcryerrno
= gcry_cipher_setkey(gh
, key
, keysize
))) {
55 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
59 if ((gcryerrno
= gcry_cipher_encrypt(gh
, outbuf
, outsize
, inbuf
, insize
))) {
60 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
67 gboolean
decrypt_xml(gcry_cipher_hd_t gh
, void *key
, gsize keysize
,
68 guchar
*iv
, void *outbuf
, gsize outsize
, void *inbuf
, gsize insize
)
70 if ((gcryerrno
= gcry_cipher_reset(gh
))) {
71 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
75 if ((gcryerrno
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
76 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
80 if ((gcryerrno
= gcry_cipher_setkey(gh
, key
, keysize
))) {
81 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
85 if ((gcryerrno
= gcry_cipher_decrypt(gh
, outbuf
, outsize
, inbuf
, insize
))) {
86 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
93 static gboolean
parse_xml(struct client_s
*client
)
95 switch (open_xml(client
->xml
, client
->len
, &client
->doc
, &client
->reader
)) {
97 send_error(client
, EPWMD_LIBXML_ERROR
);
100 send_error(client
, EPWMD_LIBXML_ERROR
);
109 static gboolean
valid_filename(const gchar
*filename
)
113 if (!filename
|| !*filename
)
116 for (p
= filename
; *p
; p
++) {
117 if (g_ascii_isalnum(*p
) == FALSE
)
124 static gboolean
cache_update_key(const guchar
*md5filename
, const guchar
*shakey
)
130 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
131 memcpy(&f
, p
, sizeof(file_cache_t
));
133 if (f
.used
== TRUE
) {
134 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
135 memcpy(&f
.key
, shakey
, sizeof(f
.key
));
136 memcpy(p
, &f
, sizeof(file_cache_t
));
141 p
+= sizeof(file_cache_t
);
142 len
+= sizeof(file_cache_t
);
144 if (len
+ sizeof(file_cache_t
) > cache_size
)
148 return cache_add_file(md5filename
, shakey
);
151 static gint
cache_valid_key(const guchar
*key
, gsize len
)
155 for (b
= 0; b
< len
; b
++) {
164 static gboolean
cache_get_key(const guchar
*md5file
, guchar
*shakey
)
170 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
171 memcpy(&f
, p
, sizeof(file_cache_t
));
174 * The slot may be used but not yet contain a key.
176 if (f
.used
== TRUE
) {
177 if (memcmp(&f
.filename
, md5file
, sizeof(f
.filename
)) == 0) {
178 if (cache_valid_key(f
.key
, sizeof(f
.key
)) == FALSE
)
181 memcpy(shakey
, &f
.key
, sizeof(f
.key
));
186 p
+= sizeof(file_cache_t
);
187 len
+= sizeof(file_cache_t
);
189 if (len
+ sizeof(file_cache_t
) > cache_size
)
196 gint
open_file(const gchar
*filename
, struct stat
*st
)
200 if ((fd
= open(filename
, O_RDONLY
)) == -1)
203 if (stat(filename
, st
) == -1) {
211 gboolean
open_command(struct client_s
*client
, gchar
**req
)
216 const gchar
*filename
= req
[0];
218 guchar shakey
[gcrykeysize
];
221 if (!filename
|| !*filename
) {
222 send_error(client
, EPWMD_COMMAND_SYNTAX
);
226 if (valid_filename(filename
) == FALSE
) {
227 send_error(client
, EPWMD_INVALID_FILENAME
);
231 if (stat(filename
, &st
) == 0) {
232 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
233 send_to_client(client
, "ERR %03i %s\n", EPWMD_NOT_A_FILE
, pwmd_strerror(EPWMD_NOT_A_FILE
));
238 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
241 * New files don't need a key.
243 if (access(filename
, R_OK
|W_OK
) != 0) {
244 if (errno
!= ENOENT
) {
245 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
,
250 if ((client
->xml
= new_document()) == NULL
) {
251 send_to_client(client
, "ERR %03i malloc(): %s\n", EPWMD_ERROR
,
256 client
->len
= strlen(client
->xml
);
258 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
259 send_error(client
, EPWMD_MAX_SLOTS
);
263 client
->filename
= g_strdup(filename
);
264 return parse_xml(client
);
267 if (cache_get_key(client
->md5file
, shakey
) == TRUE
)
271 * No key specified and no matching filename found in the cache.
273 if (!req
[1] || !*req
[1]) {
274 send_error(client
, EPWMD_KEY
);
279 if ((fd
= open_file(filename
, &st
)) == -1) {
280 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(errno
));
287 inbuf
= gcry_malloc(st
.st_size
);
288 read(fd
, inbuf
, st
.st_size
);
290 iv
= gcry_malloc(gcryblocksize
);
291 memcpy(iv
, inbuf
, gcryblocksize
);
295 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
296 memset(req
[1], 0, strlen(req
[1]));
299 if (decrypt_xml(client
->gh
, shakey
, gcrykeysize
, iv
, inbuf
+ gcryblocksize
,
300 st
.st_size
- gcryblocksize
, NULL
, 0) == FALSE
) {
306 memset(inbuf
, 0, st
.st_size
);
309 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
314 memmove(inbuf
, inbuf
+ gcryblocksize
, st
.st_size
- gcryblocksize
);
316 client
->len
= st
.st_size
- gcryblocksize
;
318 if (g_strncasecmp(client
->xml
, "<?xml version=\"1.0\"?>", 21) != 0) {
319 send_error(client
, EPWMD_BADKEY
);
324 if (cache_add_file(client
->md5file
, shakey
) == FALSE
) {
325 send_error(client
, EPWMD_MAX_SLOTS
);
330 client
->filename
= g_strdup(filename
);
331 return parse_xml(client
);
335 * client->reader should be at the position in the document where the element
336 * search should start. It won't search past the closing account element.
338 static gboolean
get_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
339 gchar
**req
, gint quiet
)
343 if (!req
|| !req
[0]) {
345 send_error(client
, EPWMD_COMMAND_SYNTAX
);
349 for (i
= 0; req
[i
]; i
++) {
350 if (find_element(reader
, req
[i
], req
[i
+1] != NULL
) == FALSE
) {
352 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
361 gboolean
save_command(struct client_s
*client
, const gchar
*filename
, gchar
*key
)
365 void *outbuf
, *inbuf
;
370 guchar shakey
[gcrykeysize
];
373 if (cache_get_key(client
->md5file
, shakey
) == FALSE
) {
374 send_error(client
, EPWMD_KEY
);
381 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, key
, strlen(key
));
382 memset(key
, 0, strlen(key
));
385 xmlDocDumpFormatMemory(client
->doc
, &xmlbuf
, &insize
, 0);
388 if (insize
/ gcryblocksize
) {
389 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
391 if (insize
% gcryblocksize
)
392 len
+= gcryblocksize
;
395 inbuf
= gcry_calloc(1, len
);
396 memcpy(inbuf
, xmlbuf
, insize
);
397 memset(xmlbuf
, 0, insize
);
400 outbuf
= gcry_malloc(insize
);
401 iv
= gcry_malloc(gcryblocksize
);
402 gcry_create_nonce(iv
, gcryblocksize
);
404 if (encrypt_xml(client
->gh
, shakey
, gcrykeysize
, iv
, outbuf
, insize
, inbuf
, insize
)
407 memset(inbuf
, 0, insize
);
409 memset(outbuf
, 0, insize
);
411 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
415 memset(inbuf
, 0, insize
);
418 if ((fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
420 memset(outbuf
, 0, insize
);
422 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(errno
));
426 write(fd
, iv
, gcryblocksize
);
427 write(fd
, outbuf
, insize
);
430 memset(outbuf
, 0, insize
);
436 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
437 send_error(client
, EPWMD_MAX_SLOTS
);
444 static gboolean
contains_whitespace(const gchar
*str
)
446 const gchar
*p
= str
;
449 if (g_ascii_isspace(*p
) == TRUE
) {
458 * the 'reader' should be at the element of the document where the elements
459 * 'req' are to be created.
461 static gboolean
create_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
462 gchar
**req
, gint novalue
)
468 r
= xmlTextReaderCurrentNode(reader
);
470 if (xmlTextReaderDepth(reader
) > 1)
473 for (i
= 0; req
[i
]; i
++) {
477 * Whitespace is allowed in values or attributes but not in element
480 if (req
[i
+1] && valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
481 send_error(client
, EPWMD_INVALID_ELEMENT
);
486 * The value of the element tree.
488 if (!req
[i
+1] && !novalue
) {
490 * Prevent creating 'text' elements in the root of the account.
493 send_error(client
, EPWMD_ROOT_TEXT_ELEMENT
);
498 * FIXME ?? overwriting an element tree with a text element:
500 * STORE account element element2 value
501 * STORE account element value
503 * would remove the existing element tree. This may be a bug or
506 xmlNodeSetContent(r
, (xmlChar
*)req
[i
]);
510 if ((n
= find_node(r
, (xmlChar
*)req
[i
])) == NULL
) {
511 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
512 r
= xmlAddChild(r
, n
);
517 if (!req
[i
+1] && novalue
)
525 * FIXME reuse reader handle
527 static gboolean
reset_reader(xmlDocPtr doc
, xmlTextReaderPtr
*reader
)
530 xmlFreeTextReader(*reader
);
534 if ((*reader
= xmlReaderWalker(doc
)) == NULL
)
540 static void delete_node(xmlNodePtr n
)
546 gboolean
delete_command(struct client_s
*client
, gchar
**req
)
551 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
552 send_error(client
, EPWMD_LIBXML_ERROR
);
556 if (find_account(client
->reader
, req
[0]) == FALSE
) {
557 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
561 n
= xmlTextReaderCurrentNode(client
->reader
);
564 * No sub-node defined. Remove the entire node (account).
573 * Remove matching sub-nodes starting from the root of the account.
575 while (req
[i
] && find_element(client
->reader
, req
[i
++], req
[i
] != NULL
) == TRUE
)
576 n
= xmlTextReaderCurrentNode(client
->reader
);
578 if (n
&& xmlStrcmp(n
->name
, (xmlChar
*)req
[i
-1]) == 0 &&
579 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_ELEMENT
)
582 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
589 gboolean
store_command(struct client_s
*client
, gchar
**req
)
592 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
593 send_error(client
, EPWMD_LIBXML_ERROR
);
598 send_error(client
, EPWMD_COMMAND_SYNTAX
);
602 if (find_account(client
->reader
, req
[0]) == FALSE
) {
603 if (contains_whitespace(req
[0]) == TRUE
) {
604 send_error(client
, EPWMD_INVALID_ELEMENT
);
608 if (new_account(client
->doc
, req
[0]) == FALSE
) {
609 send_error(client
, EPWMD_ERROR
);
616 xmlTextReaderNext(client
->reader
);
617 return create_elements(client
, client
->reader
, req
+1, 0);
620 static gboolean
do_get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
621 gchar
**req
, xmlChar
**content
, gint quiet
, gint list
)
629 if (reset_reader(client
->doc
, reader
) == FALSE
) {
631 send_error(client
, EPWMD_LIBXML_ERROR
);
635 if (find_account(*reader
, req
[0]) == FALSE
) {
637 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
642 * May be an account with only a TARGET attribute.
645 if (get_elements(client
, *reader
, req
+ 1, (list
) ? 1 : quiet
) == FALSE
)
649 if ((n
= xmlTextReaderCurrentNode(*reader
)) == NULL
) {
651 send_error(client
, EPWMD_LIBXML_ERROR
);
656 * If the current element has a TARGET attribute, the value of the
657 * attribute is an element path somewhere else in the document. Use this
658 * value and not any TEXT element value. The value may be another element
659 * with a TARGET attribute and this function will recurse until a non-TARGET
660 * attribute in the element is found.
662 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
663 if ((p
= xmlNodeGetContent(a
->children
)) != NULL
) {
664 if (strchr((gchar
*)p
, '\t') != NULL
) {
665 if ((nreq
= split_input_line((gchar
*)p
, "\t", 0)) == NULL
) {
669 send_error(client
, EPWMD_INVALID_ELEMENT
);
674 if ((nreq
= split_input_line((gchar
*)p
, " ", 0)) == NULL
) {
678 send_error(client
, EPWMD_INVALID_ELEMENT
);
684 ret
= do_get_command(client
, reader
, nreq
, content
, quiet
, list
);
690 switch (xmlTextReaderNext(*reader
)) {
693 send_error(client
, EPWMD_LIBXML_ERROR
);
697 send_error(client
, EPWMD_EMPTY_ELEMENT
);
703 switch (xmlTextReaderNodeType(*reader
)) {
704 case XML_READER_TYPE_END_ELEMENT
:
706 * May be an empty element after an ATTR DELETE TARGET command.
709 case XML_READER_TYPE_TEXT
:
713 send_error(client
, EPWMD_LIBXML_ERROR
);
718 if (n
->children
&& !list
)
719 send_error(client
, EPWMD_TRAILING_ELEMENT
);
720 else if (!n
->children
&& !list
)
721 send_error(client
, EPWMD_INVALID_ELEMENT
);
726 if ((*content
= xmlNodeGetContent(n
)) == NULL
) {
728 send_error(client
, EPWMD_EMPTY_ELEMENT
);
736 * Retrieves the value associated with the element tree 'req'.
738 gboolean
get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
739 gchar
**req
, gint quiet
)
741 xmlChar
*content
= NULL
;
743 if (do_get_command(client
, reader
, req
, &content
, quiet
, 0) == FALSE
)
748 send_error(client
, EPWMD_EMPTY_ELEMENT
);
753 send_to_client(client
, "BEGIN %li\n%s\nOK \n", xmlStrlen(content
), content
);
754 memset(content
, 0, xmlStrlen(content
));
760 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
,
761 const xmlChar
*content
)
763 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
773 for (n
= 0; *p
&& n
< 3; p
++) {
778 if (strstr((gchar
*)p
, "text()") != NULL
)
779 p
[xmlStrlen(p
) - 7] = 0;
781 for (n
= 0; p
[n
]; n
++) {
787 buf
= g_strdup_printf("%s\t%s\t%s", account
, p
, content
);
789 buf
= g_strdup_printf("%s\t%s", account
, p
);
794 static gboolean
append_to_array(gchar
***array
, gint
*total
, const gchar
*str
)
799 if ((a
= g_realloc(*array
, (t
+ 2) * sizeof(gchar
*))) == NULL
)
802 a
[t
++] = g_strdup(str
);
809 gboolean
list_command(struct client_s
*client
, gchar
*str
)
813 gchar
**elements
= NULL
;
814 gint pwmd_errno
= -1;
817 xmlChar
*path
= NULL
;
821 xmlTextReaderPtr r
= NULL
;
827 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
828 send_error(client
, EPWMD_LIBXML_ERROR
);
832 if (strchr(p
, ' ') == NULL
) {
834 if (list_accounts(client
->reader
, &dst
, &pwmd_errno
) == FALSE
) {
835 send_error(client
, pwmd_errno
);
839 send_to_client(client
, "BEGIN %i\n%s\nOK \n",
840 g_utf8_strlen(dst
, -1), dst
);
841 memset(dst
, 0, strlen(dst
));
850 while (*p
&& isspace(*p
))
856 if (find_account(client
->reader
, p
) == FALSE
) {
857 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
861 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
862 send_error(client
, EPWMD_LIBXML_ERROR
);
866 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
867 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
868 send_error(client
, EPWMD_LIBXML_ERROR
);
872 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
873 if (find_account(client
->reader
, (gchar
*)content
) == FALSE
) {
875 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
883 account
= g_strdup(p
);
885 while (xmlTextReaderNext(client
->reader
) == 1) {
887 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
888 send_error(client
, EPWMD_LIBXML_ERROR
);
892 if (xmlTextReaderDepth(client
->reader
) == 1 &&
893 xmlStrcmp(n
->name
, (xmlChar
*)"account") == 0 &&
894 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_END_ELEMENT
)
898 * If the current element has a TARGET attribute, the value of the
899 * attribute is an element path somewhere else in the document. Use this
900 * value and not any TEXT element value.
902 type
= xmlTextReaderNodeType(client
->reader
);
903 a
= xmlHasProp(n
, (xmlChar
*)"target");
905 if (type
== XML_READER_TYPE_ELEMENT
&& a
) {
906 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
907 path
= xmlGetNodePath(n
);
909 if ((nreq
= split_input_line((gchar
*)content
, "\t", 0)) == NULL
) {
911 g_strfreev(elements
);
915 send_error(client
, EPWMD_INVALID_ELEMENT
);
922 if ((ret
= do_get_command(client
, &r
, nreq
, &content
, 0, 1)) == TRUE
) {
923 if (content
&& *content
) {
925 line
= element_path_to_req(account
, path
, content
);
927 line
= element_path_to_req(account
, path
);
929 memset(content
, 0, xmlStrlen(content
));
932 if (append_to_array(&elements
, &total
, line
) == FALSE
) {
934 g_strfreev(elements
);
937 memset(line
, 0, g_utf8_strlen(line
, -1));
940 xmlFreeTextReader(r
);
941 send_error(client
, EPWMD_ERROR
);
945 memset(line
, 0, g_utf8_strlen(line
, -1));
949 if (xmlTextReaderNext(client
->reader
) == 1) {
950 if (xmlTextReaderNodeType(client
->reader
) !=
951 XML_READER_TYPE_TEXT
) {
953 xmlFreeTextReader(r
);
961 xmlFreeTextReader(r
);
967 if (type
== XML_READER_TYPE_TEXT
) {
968 xmlChar
*np
= xmlGetNodePath(n
);
971 content
= xmlNodeGetContent(n
);
972 line
= element_path_to_req(account
, np
, content
);
975 line
= element_path_to_req(account
, np
);
978 append_to_array(&elements
, &total
, line
);
979 memset(line
, 0, g_utf8_strlen(line
, -1));
985 send_error(client
, EPWMD_EMPTY_ELEMENT
);
991 line
= g_strjoinv("\n", elements
);
992 send_to_client(client
, "BEGIN %li\n%s\nOK \n",
993 g_utf8_strlen(line
, -1), line
);
994 g_strfreev(elements
);
1000 * The client->reader handle should be at the element in the document where
1001 * the attribute will be created or modified.
1003 static gboolean
add_attribute(struct client_s
*client
, const gchar
*name
, const gchar
*value
)
1008 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1009 send_error(client
, EPWMD_LIBXML_ERROR
);
1013 if ((a
= xmlHasProp(n
, (xmlChar
*)name
)) == NULL
)
1014 a
= xmlNewProp(n
, (xmlChar
*)name
, (xmlChar
*)value
);
1016 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1022 * req[0] - element path
1024 static gboolean
attribute_list(struct client_s
*client
, gchar
**req
)
1026 gchar
**attrlist
= NULL
;
1028 gchar
**epath
= NULL
;
1033 if (!req
|| !req
[0]) {
1034 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1038 if ((epath
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1040 * The first argument may be only an account.
1042 if ((epath
= split_input_line(req
[0], " ", 0)) == NULL
) {
1043 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1048 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1049 send_error(client
, EPWMD_LIBXML_ERROR
);
1053 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1054 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1059 if ((get_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1063 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1064 send_error(client
, EPWMD_LIBXML_ERROR
);
1068 for (a
= n
->properties
; a
; a
= a
->next
) {
1069 if ((attrlist
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1070 send_error(client
, EPWMD_ERROR
);
1075 attrlist
[i
++] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1080 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1084 line
= g_strjoinv("\n", attrlist
);
1085 send_to_client(client
, "BEGIN %li\n%s\n", g_utf8_strlen(line
, -1), line
);
1088 g_strfreev(attrlist
);
1096 * req[0] - attribute
1097 * req[1] - element path
1099 static gboolean
attribute_delete(struct client_s
*client
, gchar
**req
)
1103 gchar
**epath
= NULL
;
1105 if (!req
|| !req
[0] || !req
[1]) {
1106 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1110 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1112 * The first argument may be only an account.
1114 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1115 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1121 * Don't remove the NAME attribute for the account element.
1123 if (!epath
[1] && g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1124 send_error(client
, EPWMD_ATTR_SYNTAX
);
1128 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1129 send_error(client
, EPWMD_LIBXML_ERROR
);
1133 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1134 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1139 if ((get_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1143 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1144 send_error(client
, EPWMD_LIBXML_ERROR
);
1148 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1149 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1153 if (xmlRemoveProp(a
) == -1) {
1154 send_error(client
, EPWMD_LIBXML_ERROR
);
1166 * req[0] - source element path
1167 * req[1] - destination element path
1169 static gboolean
target_attribute(struct client_s
*client
, gchar
**req
)
1171 gchar
**src
, **dst
, *line
;
1173 if (!req
|| !req
[0] || !req
[1]) {
1174 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1178 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1180 * The first argument may be only an account.
1182 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
) {
1183 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1188 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1190 * The first argument may be only an account.
1192 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1193 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1200 * Prevent an element tree pointing to only and account. Accounts require
1201 * at least one element. Accounts pointing to accounts are allowed.
1203 if ((!src
[1] && dst
[1]) || (!dst
[1] && src
[1])) {
1204 send_error(client
, EPWMD_ATTR_SYNTAX
);
1210 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1211 send_error(client
, EPWMD_LIBXML_ERROR
);
1218 * Make sure the destination element path exists.
1220 if (find_account(client
->reader
, dst
[0]) == FALSE
) {
1221 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1228 if (get_elements(client
, client
->reader
, dst
+1, 0) == FALSE
) {
1235 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1236 send_error(client
, EPWMD_LIBXML_ERROR
);
1243 * If the source element tree doesn't exist, create it.
1245 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1246 if (new_account(client
->doc
, src
[0]) == FALSE
) {
1247 send_error(client
, EPWMD_LIBXML_ERROR
);
1253 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1254 send_error(client
, EPWMD_LIBXML_ERROR
);
1260 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1261 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1269 if (get_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1270 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1271 send_error(client
, EPWMD_LIBXML_ERROR
);
1277 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1278 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1284 xmlTextReaderNext(client
->reader
);
1286 if (create_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1292 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1293 send_error(client
, EPWMD_LIBXML_ERROR
);
1299 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1300 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1306 if (get_elements(client
, client
->reader
, src
+1, 0) == FALSE
) {
1314 line
= g_strjoinv("\t", dst
);
1316 if (add_attribute(client
, "target", line
) == FALSE
) {
1334 * req[0] - account name
1337 static gboolean
name_attribute(struct client_s
*client
, gchar
**req
)
1339 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1340 send_error(client
, EPWMD_LIBXML_ERROR
);
1344 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1345 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1349 if (strcmp(req
[0], req
[1]) == 0)
1352 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1353 send_error(client
, EPWMD_LIBXML_ERROR
);
1358 * Will not overwrite an existing account.
1360 if (find_account(client
->reader
, req
[1]) == TRUE
) {
1361 send_error(client
, EPWMD_ACCOUNT_EXISTS
);
1365 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1366 send_error(client
, EPWMD_LIBXML_ERROR
);
1370 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1371 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1376 * Whitespace not allowed in account names.
1378 if (contains_whitespace(req
[1]) == TRUE
) {
1379 send_error(client
, EPWMD_ATTR_SYNTAX
);
1383 return add_attribute(client
, "name", req
[1]);
1387 * req[0] - attribute
1388 * req[1] - element path
1390 * If the element has a "target" attribute it won't be "followed".
1392 static gboolean
attribute_get(struct client_s
*client
, gchar
**req
)
1396 gchar
**nreq
= NULL
;
1398 if (!req
|| !req
[0] || !req
[1]) {
1399 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1403 if (strchr(req
[1], '\t')) {
1404 if ((nreq
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1405 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1410 if ((nreq
= split_input_line(req
[1], " ", 0)) == NULL
) {
1411 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1416 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1418 send_error(client
, EPWMD_LIBXML_ERROR
);
1422 if (find_account(client
->reader
, nreq
[0]) == FALSE
) {
1424 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1429 if (get_elements(client
, client
->reader
, nreq
+ 1, 0) == FALSE
) {
1437 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1438 send_error(client
, EPWMD_LIBXML_ERROR
);
1442 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1443 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1447 send_to_client(client
, "BEGIN %li\n%s\n", xmlStrlen(a
), a
);
1453 * req[0] - attribute
1454 * req[1] - element path
1457 static gboolean
attribute_set(struct client_s
*client
, gchar
**req
)
1459 gchar
**epath
= NULL
;
1461 if (!req
|| !req
[0] || !req
[1] || !req
[2]) {
1462 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1467 * Reserved attribute names.
1469 if (g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1471 * Only reserved for the account element. Not the rest of the
1474 if (strchr(req
[1], '\t') == NULL
)
1475 return name_attribute(client
, req
+ 1);
1477 else if (g_ascii_strcasecmp(req
[0], "TARGET") == 0)
1478 return target_attribute(client
, req
+ 1);
1480 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1482 * The first argument may be only an account.
1484 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1485 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1490 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1491 send_error(client
, EPWMD_LIBXML_ERROR
);
1495 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1496 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1501 if ((get_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1505 return add_attribute(client
, req
[0], req
[2]);
1513 * req[1] - attribute name or element path if command is LIST
1514 * req[2] - element path
1515 * req[2] - element path or value
1517 gboolean
attr_command(struct client_s
*client
, gchar
**req
)
1519 if (!req
|| !req
[0] || !req
[1]) {
1520 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1524 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
1525 return attribute_set(client
, req
+1);
1526 if (g_ascii_strcasecmp(req
[0], "GET") == 0)
1527 return attribute_get(client
, req
+1);
1528 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
1529 return attribute_delete(client
, req
+1);
1530 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
1531 return attribute_list(client
, req
+1);
1533 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1538 static gboolean
cache_test(const guchar
*md5filename
, gint reset
)
1544 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
1545 memcpy(&f
, p
, sizeof(file_cache_t
));
1548 memset(&f
, 0, sizeof(file_cache_t
));
1549 memcpy(p
, &f
, sizeof(file_cache_t
));
1550 p
+= sizeof(file_cache_t
);
1551 len
+= sizeof(file_cache_t
);
1553 if (len
+ sizeof(file_cache_t
) > cache_size
)
1558 if (f
.used
== TRUE
) {
1559 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
1561 memset(&f
, 0, sizeof(file_cache_t
));
1562 memcpy(p
, &f
, sizeof(file_cache_t
));
1566 return (f
.key
[0]) ? TRUE
: FALSE
;
1570 p
+= sizeof(file_cache_t
);
1571 len
+= sizeof(file_cache_t
);
1573 if (len
+ sizeof(file_cache_t
) > cache_size
)
1577 return (reset
== 2) ? TRUE
: FALSE
;
1580 static gboolean
file_exists(const gchar
*filename
)
1582 if (access(filename
, R_OK
) == -1)
1588 gboolean
cache_command(struct client_s
*client
, gchar
**req
)
1592 if (g_ascii_strcasecmp(req
[0], "clear") == 0) {
1594 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1598 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1600 if (cache_test(md5file
, 1) == FALSE
) {
1601 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1607 else if (g_ascii_strcasecmp(req
[0], "clearall") == 0) {
1608 cache_test(client
->md5file
, 2);
1611 else if (g_ascii_strcasecmp(req
[0], "iscached") == 0) {
1613 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1617 if (file_exists(req
[1]) == FALSE
) {
1618 send_error(client
, EPWMD_FILE_NOT_FOUND
);
1622 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1624 if (cache_test(md5file
, 0) == FALSE
) {
1625 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1632 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1637 gboolean
help_command(struct client_s
*client
, const gchar
*what
)
1641 if (!what
|| !*what
)
1643 "NFO Try 'HELP COMMAND' for command help\n"
1644 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
1645 else if (g_ascii_strcasecmp(what
, "GET") == 0)
1647 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
1648 "NFO <account> is the account to work on and <element>\n"
1649 "NFO is the element wanted.\n"
1651 "NFO Example: GET isp <TAB> imap <TAB> port\n"
1652 "NFO GET isp <TAB> username\n";
1653 else if (g_ascii_strcasecmp(what
, "QUIT") == 0)
1655 "NFO syntax: QUIT\n"
1656 "NFO close the connection\n";
1657 else if (g_ascii_strcasecmp(what
, "DELETE") == 0)
1659 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
1660 else if (g_ascii_strcasecmp(what
, "STORE") == 0)
1662 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
1663 "NFO <account> is the account to work on and <element>\n"
1664 "NFO is the element to create or modify\n"
1666 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
1667 "NFO STORE isp <TAB> username <TAB> someuser\n";
1668 else if (g_ascii_strcasecmp(what
, "OPEN") == 0)
1670 "NFO syntax: OPEN <filename> [<key>]\n"
1671 "NFO opens a (new) file\n";
1672 else if (g_ascii_strcasecmp(what
, "LIST") == 0)
1674 "NFO syntax: LIST [account]\n"
1675 "NFO shows available accounts or account elements\n";
1676 else if (g_ascii_strcasecmp(what
, "ATTR") == 0)
1678 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
1679 "NFO ATTR SET NAME account value\n"
1680 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
1681 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
1682 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
1683 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
1684 "NFO ATTR LIST account[<TAB>element[...]]\n";
1685 else if (g_ascii_strcasecmp(what
, "SAVE") == 0)
1687 "NFO syntax: SAVE [<key>]\n"
1688 "NFO save any changes to the opened file using <key>\n";
1689 else if (g_ascii_strcasecmp(what
, "CACHE") == 0)
1691 "NFO syntax: CACHE [CLEARALL | CLEAR <filename> | ISCACHED <filename>]\n"
1692 "NFO tests or clears the cache entry for <filename>\n";
1693 else if (g_ascii_strcasecmp(what
, "DUMP") == 0)
1695 "NFO syntax: DUMP\n"
1696 "NFO shows the in memory XML document\n";
1698 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1702 send_to_client(client
, "%sOK \n", line
);
1706 gboolean
dump_command(struct client_s
*client
)
1711 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
1712 send_to_client(client
, "BEGIN %li\n%s", len
, xml
);
1713 memset(xml
, 0, len
);