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 *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
));
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
));
63 static gboolean
parse_xml(struct client_s
*client
)
65 switch (open_xml(client
->xml
, client
->len
, &client
->doc
, &client
->reader
)) {
67 send_error(client
, EPWMD_LIBXML_ERROR
);
70 send_error(client
, EPWMD_LIBXML_ERROR
);
79 static gboolean
valid_filename(const gchar
*filename
)
83 if (!filename
|| !*filename
)
86 for (p
= filename
; *p
; p
++) {
87 if (g_ascii_isalnum(*p
) == FALSE
)
94 static gboolean
cache_update_key(const guchar
*md5filename
, const guchar
*shakey
)
100 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
101 memcpy(&f
, p
, sizeof(file_cache_t
));
103 if (f
.used
== TRUE
) {
104 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
105 memcpy(&f
.key
, shakey
, sizeof(f
.key
));
106 memcpy(p
, &f
, sizeof(file_cache_t
));
111 p
+= sizeof(file_cache_t
);
112 len
+= sizeof(file_cache_t
);
114 if (len
+ sizeof(file_cache_t
) > cache_size
)
118 return cache_add_file(md5filename
, shakey
);
121 static gint
cache_valid_key(const guchar
*key
, gsize len
)
125 for (b
= 0; b
< len
; b
++) {
134 static gboolean
cache_get_key(const guchar
*md5file
, guchar
*shakey
)
140 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
141 memcpy(&f
, p
, sizeof(file_cache_t
));
144 * The slot may be used but not yet contain a key.
146 if (f
.used
== TRUE
) {
147 if (memcmp(&f
.filename
, md5file
, sizeof(f
.filename
)) == 0) {
148 if (cache_valid_key(f
.key
, sizeof(f
.key
)) == FALSE
)
151 memcpy(shakey
, &f
.key
, sizeof(f
.key
));
156 p
+= sizeof(file_cache_t
);
157 len
+= sizeof(file_cache_t
);
159 if (len
+ sizeof(file_cache_t
) > cache_size
)
166 gint
open_file(const gchar
*filename
, struct stat
*st
)
170 if ((fd
= open(filename
, O_RDONLY
)) == -1)
173 if (stat(filename
, st
) == -1) {
181 gboolean
open_command(struct client_s
*client
, gchar
**req
)
186 const gchar
*filename
= req
[0];
187 guchar shakey
[gcrykeysize
];
188 guchar tkey
[gcrykeysize
];
192 struct file_header_s
{
194 guchar iv
[gcryblocksize
];
197 if (!filename
|| !*filename
) {
198 send_error(client
, EPWMD_COMMAND_SYNTAX
);
202 if (valid_filename(filename
) == FALSE
) {
203 send_error(client
, EPWMD_INVALID_FILENAME
);
207 if (stat(filename
, &st
) == 0) {
208 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
209 send_to_client(client
, "ERR %03i %s\n", EPWMD_NOT_A_FILE
, pwmd_strerror(EPWMD_NOT_A_FILE
));
214 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
217 * New files don't need a key.
219 if (access(filename
, R_OK
|W_OK
) != 0) {
220 if (errno
!= ENOENT
) {
221 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
,
226 if ((client
->xml
= new_document()) == NULL
) {
227 send_to_client(client
, "ERR %03i malloc(): %s\n", EPWMD_ERROR
,
232 client
->len
= strlen(client
->xml
);
234 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
235 send_error(client
, EPWMD_MAX_SLOTS
);
239 client
->filename
= g_strdup(filename
);
240 return parse_xml(client
);
243 if ((fd
= open_file(filename
, &st
)) == -1) {
244 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(errno
));
251 if (cache_get_key(client
->md5file
, shakey
) == TRUE
)
255 * No key specified and no matching filename found in the cache.
257 if (!req
[1] || !*req
[1]) {
259 send_error(client
, EPWMD_KEY
);
264 insize
= st
.st_size
- sizeof(struct file_header_s
);
265 read(fd
, &file_header
, sizeof(struct file_header_s
));
266 inbuf
= gcry_malloc(insize
);
267 read(fd
, inbuf
, insize
);
272 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
273 memset(req
[1], 0, strlen(req
[1]));
276 memcpy(tkey
, shakey
, sizeof(tkey
));
279 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
280 sizeof(file_header
.iv
)))) {
282 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
283 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
287 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, shakey
, gcrykeysize
))) {
289 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
290 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
294 if (decrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
301 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
305 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, tkey
, gcrykeysize
))) {
307 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
308 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
312 iter
= file_header
.iter
;
315 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
316 sizeof(file_header
.iv
)))) {
318 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
319 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
323 if (decrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
330 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
336 client
->len
= insize
;
338 if (g_strncasecmp(client
->xml
, "<?xml version=\"1.0\"?>", 21) != 0) {
339 send_error(client
, EPWMD_BADKEY
);
344 if (cache_add_file(client
->md5file
, shakey
) == FALSE
) {
345 send_error(client
, EPWMD_MAX_SLOTS
);
350 client
->filename
= g_strdup(filename
);
351 return parse_xml(client
);
355 * client->reader should be at the position in the document where the element
356 * search should start. It won't search past the closing account element.
358 static gboolean
find_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
359 gchar
**req
, gint quiet
)
363 if (!req
|| !req
[0]) {
365 send_error(client
, EPWMD_COMMAND_SYNTAX
);
369 for (i
= 0; req
[i
]; i
++) {
370 if (find_element(reader
, req
[i
], req
[i
+1] != NULL
) == FALSE
) {
372 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
381 gboolean
save_command(struct client_s
*client
, const gchar
*filename
, gchar
*key
)
389 guchar shakey
[gcrykeysize
];
390 guchar tkey
[gcrykeysize
];
391 gint iter
= client
->iter
;
392 struct file_header_s
{
394 guchar iv
[gcryblocksize
];
398 if (cache_get_key(client
->md5file
, shakey
) == FALSE
) {
399 send_error(client
, EPWMD_KEY
);
406 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, key
, strlen(key
));
407 memset(key
, 0, strlen(key
));
410 xmlDocDumpFormatMemory(client
->doc
, &xmlbuf
, &insize
, 0);
413 if (insize
/ gcryblocksize
) {
414 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
416 if (insize
% gcryblocksize
)
417 len
+= gcryblocksize
;
420 inbuf
= gcry_calloc(1, len
);
421 memcpy(inbuf
, xmlbuf
, insize
);
424 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
425 memcpy(tkey
, shakey
, sizeof(tkey
));
428 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, tkey
, gcrykeysize
))) {
430 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
431 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
435 file_header
.iter
= iter
;
438 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
439 sizeof(file_header
.iv
)))) {
441 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
442 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
446 if (encrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0)
449 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
454 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
455 sizeof(file_header
.iv
)))) {
457 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
458 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
462 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, shakey
, gcrykeysize
))) {
464 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
465 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
469 if (encrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0)
472 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
476 if ((fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
478 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(errno
));
482 write(fd
, &file_header
, sizeof(struct file_header_s
));
483 write(fd
, inbuf
, insize
);
490 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
491 send_error(client
, EPWMD_MAX_SLOTS
);
498 static gboolean
contains_whitespace(const gchar
*str
)
500 const gchar
*p
= str
;
503 if (g_ascii_isspace(*p
) == TRUE
) {
512 * the 'reader' should be at the element of the document where the elements
513 * 'req' are to be created.
515 static gboolean
create_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
516 gchar
**req
, gint novalue
)
522 r
= xmlTextReaderCurrentNode(reader
);
524 if (xmlTextReaderDepth(reader
) > 1)
527 for (i
= 0; req
[i
]; i
++) {
531 * Whitespace is allowed in values or attributes but not in element
534 if (req
[i
+1] && valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
535 send_error(client
, EPWMD_INVALID_ELEMENT
);
540 * The value of the element tree.
542 if (!req
[i
+1] && !novalue
) {
544 * Prevent creating 'text' elements in the root of the account.
547 send_error(client
, EPWMD_ROOT_TEXT_ELEMENT
);
552 * FIXME ?? overwriting an element tree with a text element:
554 * STORE account element element2 value
555 * STORE account element value
557 * would remove the existing element tree. This may be a bug or
560 xmlNodeSetContent(r
, (xmlChar
*)req
[i
]);
564 if ((n
= find_node(r
, (xmlChar
*)req
[i
])) == NULL
) {
565 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
566 r
= xmlAddChild(r
, n
);
571 if (!req
[i
+1] && novalue
)
579 * FIXME reuse reader handle
581 static gboolean
reset_reader(xmlDocPtr doc
, xmlTextReaderPtr
*reader
)
584 xmlFreeTextReader(*reader
);
588 if ((*reader
= xmlReaderWalker(doc
)) == NULL
)
594 static void delete_node(xmlNodePtr n
)
600 gboolean
delete_command(struct client_s
*client
, gchar
**req
)
605 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
606 send_error(client
, EPWMD_LIBXML_ERROR
);
610 if (find_account(client
->reader
, req
[0]) == FALSE
) {
611 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
615 n
= xmlTextReaderCurrentNode(client
->reader
);
618 * No sub-node defined. Remove the entire node (account).
627 * Remove matching sub-nodes starting from the root of the account.
629 while (req
[i
] && find_element(client
->reader
, req
[i
++], req
[i
] != NULL
) == TRUE
)
630 n
= xmlTextReaderCurrentNode(client
->reader
);
632 if (n
&& xmlStrcmp(n
->name
, (xmlChar
*)req
[i
-1]) == 0 &&
633 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_ELEMENT
)
636 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
643 gboolean
store_command(struct client_s
*client
, gchar
**req
)
646 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
647 send_error(client
, EPWMD_LIBXML_ERROR
);
652 send_error(client
, EPWMD_COMMAND_SYNTAX
);
656 if (find_account(client
->reader
, req
[0]) == FALSE
) {
657 if (contains_whitespace(req
[0]) == TRUE
) {
658 send_error(client
, EPWMD_INVALID_ELEMENT
);
662 if (new_account(client
->doc
, req
[0]) == FALSE
) {
663 send_error(client
, EPWMD_ERROR
);
670 xmlTextReaderNext(client
->reader
);
671 return create_elements(client
, client
->reader
, req
+1, 0);
674 static gboolean
do_get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
675 gchar
**req
, xmlChar
**content
, gint quiet
, gint list
)
683 if (reset_reader(client
->doc
, reader
) == FALSE
) {
685 send_error(client
, EPWMD_LIBXML_ERROR
);
689 if (find_account(*reader
, req
[0]) == FALSE
) {
691 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
696 * May be an account with only a TARGET attribute.
699 if (find_elements(client
, *reader
, req
+ 1, (list
) ? 1 : quiet
) == FALSE
)
703 if ((n
= xmlTextReaderCurrentNode(*reader
)) == NULL
) {
705 send_error(client
, EPWMD_LIBXML_ERROR
);
710 * If the current element has a TARGET attribute, the value of the
711 * attribute is an element path somewhere else in the document. Use this
712 * value and not any TEXT element value. The value may be another element
713 * with a TARGET attribute and this function will recurse until a non-TARGET
714 * attribute in the element is found.
716 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
717 if ((p
= xmlNodeGetContent(a
->children
)) != NULL
) {
718 if (strchr((gchar
*)p
, '\t') != NULL
) {
719 if ((nreq
= split_input_line((gchar
*)p
, "\t", 0)) == NULL
) {
723 send_error(client
, EPWMD_INVALID_ELEMENT
);
728 if ((nreq
= split_input_line((gchar
*)p
, " ", 0)) == NULL
) {
732 send_error(client
, EPWMD_INVALID_ELEMENT
);
738 ret
= do_get_command(client
, reader
, nreq
, content
, quiet
, list
);
744 switch (xmlTextReaderNext(*reader
)) {
747 send_error(client
, EPWMD_LIBXML_ERROR
);
751 send_error(client
, EPWMD_EMPTY_ELEMENT
);
757 switch (xmlTextReaderNodeType(*reader
)) {
758 case XML_READER_TYPE_END_ELEMENT
:
760 * May be an empty element after an ATTR DELETE TARGET command.
763 case XML_READER_TYPE_TEXT
:
767 send_error(client
, EPWMD_LIBXML_ERROR
);
772 if (n
->children
&& !list
)
773 send_error(client
, EPWMD_TRAILING_ELEMENT
);
774 else if (!n
->children
&& !list
)
775 send_error(client
, EPWMD_INVALID_ELEMENT
);
780 if ((*content
= xmlNodeGetContent(n
)) == NULL
) {
782 send_error(client
, EPWMD_EMPTY_ELEMENT
);
790 * Retrieves the value associated with the element tree 'req'.
792 gboolean
get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
793 gchar
**req
, gint quiet
)
795 xmlChar
*content
= NULL
;
797 if (do_get_command(client
, reader
, req
, &content
, quiet
, 0) == FALSE
)
802 send_error(client
, EPWMD_EMPTY_ELEMENT
);
807 send_to_client(client
, "BEGIN %li\n%s\nOK \n", xmlStrlen(content
), content
);
813 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
,
814 const xmlChar
*content
)
816 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
826 for (n
= 0; *p
&& n
< 3; p
++) {
831 if (strstr((gchar
*)p
, "text()") != NULL
)
832 p
[xmlStrlen(p
) - 7] = 0;
834 for (n
= 0; p
[n
]; n
++) {
840 buf
= g_strdup_printf("%s\t%s\t%s", account
, p
, content
);
842 buf
= g_strdup_printf("%s\t%s", account
, p
);
847 static gboolean
append_to_array(gchar
***array
, gint
*total
, const gchar
*str
)
852 if ((a
= g_realloc(*array
, (t
+ 2) * sizeof(gchar
*))) == NULL
)
855 a
[t
++] = g_strdup(str
);
862 gboolean
list_command(struct client_s
*client
, gchar
*str
)
866 gchar
**elements
= NULL
;
867 gint pwmd_errno
= -1;
870 xmlChar
*path
= NULL
;
874 xmlTextReaderPtr r
= NULL
;
875 gchar
**req
= NULL
, **nreq
;
880 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
881 send_error(client
, EPWMD_LIBXML_ERROR
);
885 if (strchr(p
, ' ') == NULL
) {
887 if (list_accounts(client
->reader
, &dst
, &pwmd_errno
) == FALSE
) {
888 send_error(client
, pwmd_errno
);
892 send_to_client(client
, "BEGIN %i\n%s\nOK \n",
893 g_utf8_strlen(dst
, -1), dst
);
894 memset(dst
, 0, strlen(dst
));
903 while (*p
&& isspace(*p
))
909 if (strchr(p
, '\t') != NULL
) {
910 if ((req
= split_input_line(p
, "\t", 0)) == NULL
) {
911 send_error(client
, EPWMD_COMMAND_SYNTAX
);
915 if (find_account(client
->reader
, req
[0]) == FALSE
) {
916 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
920 if (find_elements(client
, client
->reader
, req
+ 1, 0) == FALSE
) {
926 if (find_account(client
->reader
, p
) == FALSE
) {
927 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
932 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
933 send_error(client
, EPWMD_LIBXML_ERROR
);
937 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
938 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
939 send_error(client
, EPWMD_LIBXML_ERROR
);
943 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
944 if (find_account(client
->reader
, (gchar
*)content
) == FALSE
) {
946 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
954 account
= (req
) ? g_strdup(req
[0]) : g_strdup(p
);
959 while (xmlTextReaderNext(client
->reader
) == 1) {
961 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
962 send_error(client
, EPWMD_LIBXML_ERROR
);
966 if (xmlTextReaderDepth(client
->reader
) == 1 &&
967 xmlStrcmp(n
->name
, (xmlChar
*)"account") == 0 &&
968 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_END_ELEMENT
)
972 * If the current element has a TARGET attribute, the value of the
973 * attribute is an element path somewhere else in the document. Use this
974 * value and not any TEXT element value.
976 type
= xmlTextReaderNodeType(client
->reader
);
977 a
= xmlHasProp(n
, (xmlChar
*)"target");
979 if (type
== XML_READER_TYPE_ELEMENT
&& a
) {
980 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
981 path
= xmlGetNodePath(n
);
983 if ((nreq
= split_input_line((gchar
*)content
, "\t", 0)) == NULL
) {
985 g_strfreev(elements
);
989 send_error(client
, EPWMD_INVALID_ELEMENT
);
996 if ((ret
= do_get_command(client
, &r
, nreq
, &content
, 0, 1)) == TRUE
) {
997 if (content
&& *content
) {
999 line
= element_path_to_req(account
, path
, content
);
1001 line
= element_path_to_req(account
, path
);
1005 if (append_to_array(&elements
, &total
, line
) == FALSE
) {
1007 g_strfreev(elements
);
1010 memset(line
, 0, g_utf8_strlen(line
, -1));
1013 xmlFreeTextReader(r
);
1014 send_error(client
, EPWMD_ERROR
);
1018 memset(line
, 0, g_utf8_strlen(line
, -1));
1022 if (xmlTextReaderNext(client
->reader
) == 1) {
1023 if (xmlTextReaderNodeType(client
->reader
) !=
1024 XML_READER_TYPE_TEXT
) {
1026 xmlFreeTextReader(r
);
1034 xmlFreeTextReader(r
);
1040 if (type
== XML_READER_TYPE_TEXT
) {
1041 xmlChar
*np
= xmlGetNodePath(n
);
1044 content
= xmlNodeGetContent(n
);
1045 line
= element_path_to_req(account
, np
, content
);
1048 line
= element_path_to_req(account
, np
);
1051 append_to_array(&elements
, &total
, line
);
1052 memset(line
, 0, g_utf8_strlen(line
, -1));
1058 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1064 line
= g_strjoinv("\n", elements
);
1065 send_to_client(client
, "BEGIN %li\n%s\nOK \n",
1066 g_utf8_strlen(line
, -1), line
);
1067 g_strfreev(elements
);
1073 * The client->reader handle should be at the element in the document where
1074 * the attribute will be created or modified.
1076 static gboolean
add_attribute(struct client_s
*client
, const gchar
*name
, const gchar
*value
)
1081 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1082 send_error(client
, EPWMD_LIBXML_ERROR
);
1086 if ((a
= xmlHasProp(n
, (xmlChar
*)name
)) == NULL
)
1087 a
= xmlNewProp(n
, (xmlChar
*)name
, (xmlChar
*)value
);
1089 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1095 * req[0] - element path
1097 static gboolean
attribute_list(struct client_s
*client
, gchar
**req
)
1099 gchar
**attrlist
= NULL
;
1101 gchar
**epath
= NULL
;
1106 if (!req
|| !req
[0]) {
1107 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1111 if ((epath
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1113 * The first argument may be only an account.
1115 if ((epath
= split_input_line(req
[0], " ", 0)) == NULL
) {
1116 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1121 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1122 send_error(client
, EPWMD_LIBXML_ERROR
);
1126 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1127 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1132 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1136 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1137 send_error(client
, EPWMD_LIBXML_ERROR
);
1141 for (a
= n
->properties
; a
; a
= a
->next
) {
1142 if ((attrlist
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1143 send_error(client
, EPWMD_ERROR
);
1148 attrlist
[i
++] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1153 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1157 line
= g_strjoinv("\n", attrlist
);
1158 send_to_client(client
, "BEGIN %li\n%s\n", g_utf8_strlen(line
, -1), line
);
1161 g_strfreev(attrlist
);
1169 * req[0] - attribute
1170 * req[1] - element path
1172 static gboolean
attribute_delete(struct client_s
*client
, gchar
**req
)
1176 gchar
**epath
= NULL
;
1178 if (!req
|| !req
[0] || !req
[1]) {
1179 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1183 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1185 * The first argument may be only an account.
1187 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1188 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1194 * Don't remove the NAME attribute for the account element.
1196 if (!epath
[1] && g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1197 send_error(client
, EPWMD_ATTR_SYNTAX
);
1201 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1202 send_error(client
, EPWMD_LIBXML_ERROR
);
1206 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1207 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1212 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1216 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1217 send_error(client
, EPWMD_LIBXML_ERROR
);
1221 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1222 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1226 if (xmlRemoveProp(a
) == -1) {
1227 send_error(client
, EPWMD_LIBXML_ERROR
);
1239 * req[0] - source element path
1240 * req[1] - destination element path
1242 static gboolean
target_attribute(struct client_s
*client
, gchar
**req
)
1244 gchar
**src
, **dst
, *line
;
1246 if (!req
|| !req
[0] || !req
[1]) {
1247 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1251 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1253 * The first argument may be only an account.
1255 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
) {
1256 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1261 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1263 * The first argument may be only an account.
1265 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1266 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1273 * Prevent an element tree pointing to only and account. Accounts require
1274 * at least one element. Accounts pointing to accounts are allowed.
1276 if ((!src
[1] && dst
[1]) || (!dst
[1] && src
[1])) {
1277 send_error(client
, EPWMD_ATTR_SYNTAX
);
1283 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1284 send_error(client
, EPWMD_LIBXML_ERROR
);
1291 * Make sure the destination element path exists.
1293 if (find_account(client
->reader
, dst
[0]) == FALSE
) {
1294 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1301 if (find_elements(client
, client
->reader
, dst
+1, 0) == FALSE
) {
1308 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1309 send_error(client
, EPWMD_LIBXML_ERROR
);
1316 * If the source element tree doesn't exist, create it.
1318 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1319 if (new_account(client
->doc
, src
[0]) == FALSE
) {
1320 send_error(client
, EPWMD_LIBXML_ERROR
);
1326 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1327 send_error(client
, EPWMD_LIBXML_ERROR
);
1333 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1334 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1342 if (find_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1343 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1344 send_error(client
, EPWMD_LIBXML_ERROR
);
1350 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1351 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1357 xmlTextReaderNext(client
->reader
);
1359 if (create_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1365 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1366 send_error(client
, EPWMD_LIBXML_ERROR
);
1372 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1373 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1379 if (find_elements(client
, client
->reader
, src
+1, 0) == FALSE
) {
1387 line
= g_strjoinv("\t", dst
);
1389 if (add_attribute(client
, "target", line
) == FALSE
) {
1407 * req[0] - account name
1410 static gboolean
name_attribute(struct client_s
*client
, gchar
**req
)
1412 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1413 send_error(client
, EPWMD_LIBXML_ERROR
);
1417 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1418 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1422 if (strcmp(req
[0], req
[1]) == 0)
1425 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1426 send_error(client
, EPWMD_LIBXML_ERROR
);
1431 * Will not overwrite an existing account.
1433 if (find_account(client
->reader
, req
[1]) == TRUE
) {
1434 send_error(client
, EPWMD_ACCOUNT_EXISTS
);
1438 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1439 send_error(client
, EPWMD_LIBXML_ERROR
);
1443 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1444 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1449 * Whitespace not allowed in account names.
1451 if (contains_whitespace(req
[1]) == TRUE
) {
1452 send_error(client
, EPWMD_ATTR_SYNTAX
);
1456 return add_attribute(client
, "name", req
[1]);
1460 * req[0] - attribute
1461 * req[1] - element path
1463 * If the element has a "target" attribute it won't be "followed".
1465 static gboolean
attribute_get(struct client_s
*client
, gchar
**req
)
1469 gchar
**nreq
= NULL
;
1471 if (!req
|| !req
[0] || !req
[1]) {
1472 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1476 if (strchr(req
[1], '\t')) {
1477 if ((nreq
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1478 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1483 if ((nreq
= split_input_line(req
[1], " ", 0)) == NULL
) {
1484 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1489 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1491 send_error(client
, EPWMD_LIBXML_ERROR
);
1495 if (find_account(client
->reader
, nreq
[0]) == FALSE
) {
1497 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1502 if (find_elements(client
, client
->reader
, nreq
+ 1, 0) == FALSE
) {
1510 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1511 send_error(client
, EPWMD_LIBXML_ERROR
);
1515 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1516 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1520 send_to_client(client
, "BEGIN %li\n%s\n", xmlStrlen(a
), a
);
1526 * req[0] - attribute
1527 * req[1] - element path
1530 static gboolean
attribute_set(struct client_s
*client
, gchar
**req
)
1532 gchar
**epath
= NULL
;
1534 if (!req
|| !req
[0] || !req
[1] || !req
[2]) {
1535 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1540 * Reserved attribute names.
1542 if (g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1544 * Only reserved for the account element. Not the rest of the
1547 if (strchr(req
[1], '\t') == NULL
)
1548 return name_attribute(client
, req
+ 1);
1550 else if (g_ascii_strcasecmp(req
[0], "TARGET") == 0)
1551 return target_attribute(client
, req
+ 1);
1553 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1555 * The first argument may be only an account.
1557 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1558 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1563 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1564 send_error(client
, EPWMD_LIBXML_ERROR
);
1568 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1569 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1574 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1579 return add_attribute(client
, req
[0], req
[2]);
1587 * req[1] - attribute name or element path if command is LIST
1588 * req[2] - element path
1589 * req[2] - element path or value
1591 gboolean
attr_command(struct client_s
*client
, gchar
**req
)
1593 if (!req
|| !req
[0] || !req
[1]) {
1594 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1598 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
1599 return attribute_set(client
, req
+1);
1600 if (g_ascii_strcasecmp(req
[0], "GET") == 0)
1601 return attribute_get(client
, req
+1);
1602 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
1603 return attribute_delete(client
, req
+1);
1604 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
1605 return attribute_list(client
, req
+1);
1607 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1612 static gboolean
cache_test(const guchar
*md5filename
, gint reset
)
1618 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
1619 memcpy(&f
, p
, sizeof(file_cache_t
));
1622 memset(&f
, 0, sizeof(file_cache_t
));
1623 memcpy(p
, &f
, sizeof(file_cache_t
));
1624 p
+= sizeof(file_cache_t
);
1625 len
+= sizeof(file_cache_t
);
1627 if (len
+ sizeof(file_cache_t
) > cache_size
)
1632 if (f
.used
== TRUE
) {
1633 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
1635 memset(&f
, 0, sizeof(file_cache_t
));
1636 memcpy(p
, &f
, sizeof(file_cache_t
));
1640 return (f
.key
[0]) ? TRUE
: FALSE
;
1644 p
+= sizeof(file_cache_t
);
1645 len
+= sizeof(file_cache_t
);
1647 if (len
+ sizeof(file_cache_t
) > cache_size
)
1651 return (reset
== 2) ? TRUE
: FALSE
;
1654 static gboolean
file_exists(const gchar
*filename
)
1658 if (access(filename
, R_OK
) == -1)
1661 stat(filename
, &st
);
1663 if (st
.st_size
== 0)
1669 gboolean
cache_command(struct client_s
*client
, gchar
**req
)
1673 if (g_ascii_strcasecmp(req
[0], "clear") == 0) {
1675 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1679 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1681 if (cache_test(md5file
, 1) == FALSE
) {
1682 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1688 else if (g_ascii_strcasecmp(req
[0], "clearall") == 0) {
1689 cache_test(client
->md5file
, 2);
1692 else if (g_ascii_strcasecmp(req
[0], "iscached") == 0) {
1694 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1698 if (file_exists(req
[1]) == FALSE
) {
1699 send_error(client
, EPWMD_FILE_NOT_FOUND
);
1703 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1705 if (cache_test(md5file
, 0) == FALSE
) {
1706 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1713 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1718 gboolean
help_command(struct client_s
*client
, const gchar
*what
)
1722 if (!what
|| !*what
)
1724 "NFO Try 'HELP COMMAND' for command help\n"
1725 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
1726 else if (g_ascii_strcasecmp(what
, "GET") == 0)
1728 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
1729 "NFO <account> is the account to work on and <element>\n"
1730 "NFO is the element wanted.\n"
1732 "NFO Example: GET isp <TAB> imap <TAB> port\n"
1733 "NFO GET isp <TAB> username\n";
1734 else if (g_ascii_strcasecmp(what
, "QUIT") == 0)
1736 "NFO syntax: QUIT\n"
1737 "NFO close the connection\n";
1738 else if (g_ascii_strcasecmp(what
, "DELETE") == 0)
1740 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
1741 else if (g_ascii_strcasecmp(what
, "STORE") == 0)
1743 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
1744 "NFO <account> is the account to work on and <element>\n"
1745 "NFO is the element to create or modify\n"
1747 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
1748 "NFO STORE isp <TAB> username <TAB> someuser\n";
1749 else if (g_ascii_strcasecmp(what
, "OPEN") == 0)
1751 "NFO syntax: OPEN <filename> [<key>]\n"
1752 "NFO opens a (new) file\n";
1753 else if (g_ascii_strcasecmp(what
, "LIST") == 0)
1755 "NFO syntax: LIST [account]\n"
1756 "NFO shows available accounts or account elements\n";
1757 else if (g_ascii_strcasecmp(what
, "ATTR") == 0)
1759 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
1760 "NFO ATTR SET NAME account value\n"
1761 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
1762 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
1763 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
1764 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
1765 "NFO ATTR LIST account[<TAB>element[...]]\n";
1766 else if (g_ascii_strcasecmp(what
, "SAVE") == 0)
1768 "NFO syntax: SAVE [<key>]\n"
1769 "NFO save any changes to the opened file using <key>\n";
1770 else if (g_ascii_strcasecmp(what
, "CACHE") == 0)
1772 "NFO syntax: CACHE [CLEARALL | CLEAR <filename> | ISCACHED <filename>]\n"
1773 "NFO tests or clears the cache entry for <filename>\n";
1774 else if (g_ascii_strcasecmp(what
, "DUMP") == 0)
1776 "NFO syntax: DUMP\n"
1777 "NFO shows the in memory XML document\n";
1779 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1783 send_to_client(client
, "%sOK \n", line
);
1787 gboolean
dump_command(struct client_s
*client
)
1792 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
1793 send_to_client(client
, "BEGIN %li\n%s", len
, xml
);