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
));
213 client
->mtime
= st
.st_mtime
;
216 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
219 * New files don't need a key.
221 if (access(filename
, R_OK
|W_OK
) != 0) {
222 if (errno
!= ENOENT
) {
223 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
,
228 if ((client
->xml
= new_document()) == NULL
) {
229 send_to_client(client
, "ERR %03i malloc(): %s\n", EPWMD_ERROR
,
234 client
->len
= strlen(client
->xml
);
236 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
237 send_error(client
, EPWMD_MAX_SLOTS
);
241 client
->filename
= g_strdup(filename
);
242 return parse_xml(client
);
245 if ((fd
= open_file(filename
, &st
)) == -1) {
246 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(errno
));
253 if (cache_get_key(client
->md5file
, shakey
) == TRUE
)
257 * No key specified and no matching filename found in the cache.
259 if (!req
[1] || !*req
[1]) {
261 send_error(client
, EPWMD_KEY
);
266 insize
= st
.st_size
- sizeof(struct file_header_s
);
267 read(fd
, &file_header
, sizeof(struct file_header_s
));
268 inbuf
= gcry_malloc(insize
);
269 read(fd
, inbuf
, insize
);
274 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
275 memset(req
[1], 0, strlen(req
[1]));
278 memcpy(tkey
, shakey
, sizeof(tkey
));
281 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
282 sizeof(file_header
.iv
)))) {
284 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
285 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
289 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, shakey
, gcrykeysize
))) {
291 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
292 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
296 if (decrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
303 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
307 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, tkey
, gcrykeysize
))) {
309 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
310 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
314 iter
= file_header
.iter
;
317 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
318 sizeof(file_header
.iv
)))) {
320 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
321 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
325 if (decrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
332 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
338 client
->len
= insize
;
340 if (g_strncasecmp(client
->xml
, "<?xml version=\"1.0\"?>", 21) != 0) {
341 send_error(client
, EPWMD_BADKEY
);
346 if (cache_add_file(client
->md5file
, shakey
) == FALSE
) {
347 send_error(client
, EPWMD_MAX_SLOTS
);
352 client
->filename
= g_strdup(filename
);
353 return parse_xml(client
);
357 * client->reader should be at the position in the document where the element
358 * search should start. It won't search past the closing account element.
360 static gboolean
find_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
361 gchar
**req
, gint quiet
)
365 if (!req
|| !req
[0]) {
367 send_error(client
, EPWMD_COMMAND_SYNTAX
);
371 for (i
= 0; req
[i
]; i
++) {
372 if (find_element(reader
, req
[i
], req
[i
+1] != NULL
) == FALSE
) {
374 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
383 gboolean
save_command(struct client_s
*client
, const gchar
*filename
, gchar
*key
)
391 guchar shakey
[gcrykeysize
];
392 guchar tkey
[gcrykeysize
];
393 gint iter
= client
->iter
;
395 struct file_header_s
{
397 guchar iv
[gcryblocksize
];
400 if (stat(filename
, &st
) == 0 && client
->mtime
) {
401 if (client
->mtime
!= st
.st_mtime
) {
402 send_error(client
, EPWMD_FILE_MODIFIED
);
408 if (cache_get_key(client
->md5file
, shakey
) == FALSE
) {
409 send_error(client
, EPWMD_KEY
);
416 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, key
, strlen(key
));
417 memset(key
, 0, strlen(key
));
420 xmlDocDumpFormatMemory(client
->doc
, &xmlbuf
, &insize
, 0);
423 if (insize
/ gcryblocksize
) {
424 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
426 if (insize
% gcryblocksize
)
427 len
+= gcryblocksize
;
430 inbuf
= gcry_calloc(1, len
);
431 memcpy(inbuf
, xmlbuf
, insize
);
434 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
435 memcpy(tkey
, shakey
, sizeof(tkey
));
438 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, tkey
, gcrykeysize
))) {
440 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
441 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
445 file_header
.iter
= iter
;
448 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
449 sizeof(file_header
.iv
)))) {
451 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
452 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
456 if (encrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0)
459 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
464 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
465 sizeof(file_header
.iv
)))) {
467 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
468 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
472 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, shakey
, gcrykeysize
))) {
474 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
475 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
479 if (encrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0)
482 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
486 if ((fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
488 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(errno
));
492 write(fd
, &file_header
, sizeof(struct file_header_s
));
493 write(fd
, inbuf
, insize
);
498 client
->mtime
= st
.st_mtime
;
503 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
504 send_error(client
, EPWMD_MAX_SLOTS
);
511 static gboolean
contains_whitespace(const gchar
*str
)
513 const gchar
*p
= str
;
516 if (g_ascii_isspace(*p
) == TRUE
) {
525 * the 'reader' should be at the element of the document where the elements
526 * 'req' are to be created.
528 static gboolean
create_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
529 gchar
**req
, gint novalue
)
535 r
= xmlTextReaderCurrentNode(reader
);
537 if (xmlTextReaderDepth(reader
) > 1)
540 for (i
= 0; req
[i
]; i
++) {
544 * Whitespace is allowed in values or attributes but not in element
547 if (req
[i
+1] && valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
548 send_error(client
, EPWMD_INVALID_ELEMENT
);
553 * The value of the element tree.
555 if (!req
[i
+1] && !novalue
) {
557 * Prevent creating 'text' elements in the root of the account.
560 send_error(client
, EPWMD_ROOT_TEXT_ELEMENT
);
565 * FIXME ?? overwriting an element tree with a text element:
567 * STORE account element element2 value
568 * STORE account element value
570 * would remove the existing element tree. This may be a bug or
573 xmlNodeSetContent(r
, (xmlChar
*)req
[i
]);
577 if ((n
= find_node(r
, (xmlChar
*)req
[i
])) == NULL
) {
578 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
579 r
= xmlAddChild(r
, n
);
584 if (!req
[i
+1] && novalue
)
592 * FIXME reuse reader handle
594 static gboolean
reset_reader(xmlDocPtr doc
, xmlTextReaderPtr
*reader
)
597 xmlFreeTextReader(*reader
);
601 if ((*reader
= xmlReaderWalker(doc
)) == NULL
)
607 static void delete_node(xmlNodePtr n
)
613 gboolean
delete_command(struct client_s
*client
, gchar
**req
)
618 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
619 send_error(client
, EPWMD_LIBXML_ERROR
);
623 if (find_account(client
->reader
, req
[0]) == FALSE
) {
624 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
628 n
= xmlTextReaderCurrentNode(client
->reader
);
631 * No sub-node defined. Remove the entire node (account).
640 * Remove matching sub-nodes starting from the root of the account.
642 while (req
[i
] && find_element(client
->reader
, req
[i
++], req
[i
] != NULL
) == TRUE
)
643 n
= xmlTextReaderCurrentNode(client
->reader
);
645 if (n
&& xmlStrcmp(n
->name
, (xmlChar
*)req
[i
-1]) == 0 &&
646 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_ELEMENT
)
649 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
656 gboolean
store_command(struct client_s
*client
, gchar
**req
)
659 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
660 send_error(client
, EPWMD_LIBXML_ERROR
);
665 send_error(client
, EPWMD_COMMAND_SYNTAX
);
669 if (find_account(client
->reader
, req
[0]) == FALSE
) {
670 if (contains_whitespace(req
[0]) == TRUE
) {
671 send_error(client
, EPWMD_INVALID_ELEMENT
);
675 if (new_account(client
->doc
, req
[0]) == FALSE
) {
676 send_error(client
, EPWMD_ERROR
);
683 xmlTextReaderNext(client
->reader
);
684 return create_elements(client
, client
->reader
, req
+1, 0);
687 static gboolean
do_get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
688 gchar
**req
, xmlChar
**content
, gint quiet
, gint list
)
696 if (reset_reader(client
->doc
, reader
) == FALSE
) {
698 send_error(client
, EPWMD_LIBXML_ERROR
);
702 if (find_account(*reader
, req
[0]) == FALSE
) {
704 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
709 * May be an account with only a TARGET attribute.
712 if (find_elements(client
, *reader
, req
+ 1, (list
) ? 1 : quiet
) == FALSE
)
716 if ((n
= xmlTextReaderCurrentNode(*reader
)) == NULL
) {
718 send_error(client
, EPWMD_LIBXML_ERROR
);
723 * If the current element has a TARGET attribute, the value of the
724 * attribute is an element path somewhere else in the document. Use this
725 * value and not any TEXT element value. The value may be another element
726 * with a TARGET attribute and this function will recurse until a non-TARGET
727 * attribute in the element is found.
729 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
730 if ((p
= xmlNodeGetContent(a
->children
)) != NULL
) {
731 if (strchr((gchar
*)p
, '\t') != NULL
) {
732 if ((nreq
= split_input_line((gchar
*)p
, "\t", 0)) == NULL
) {
736 send_error(client
, EPWMD_INVALID_ELEMENT
);
741 if ((nreq
= split_input_line((gchar
*)p
, " ", 0)) == NULL
) {
745 send_error(client
, EPWMD_INVALID_ELEMENT
);
751 ret
= do_get_command(client
, reader
, nreq
, content
, quiet
, list
);
757 switch (xmlTextReaderNext(*reader
)) {
760 send_error(client
, EPWMD_LIBXML_ERROR
);
764 send_error(client
, EPWMD_EMPTY_ELEMENT
);
770 switch (xmlTextReaderNodeType(*reader
)) {
771 case XML_READER_TYPE_END_ELEMENT
:
773 * May be an empty element after an ATTR DELETE TARGET command.
776 case XML_READER_TYPE_TEXT
:
780 send_error(client
, EPWMD_LIBXML_ERROR
);
785 if (n
->children
&& !list
)
786 send_error(client
, EPWMD_TRAILING_ELEMENT
);
787 else if (!n
->children
&& !list
)
788 send_error(client
, EPWMD_INVALID_ELEMENT
);
793 if ((*content
= xmlNodeGetContent(n
)) == NULL
) {
795 send_error(client
, EPWMD_EMPTY_ELEMENT
);
803 * Retrieves the value associated with the element tree 'req'.
805 gboolean
get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
806 gchar
**req
, gint quiet
)
808 xmlChar
*content
= NULL
;
810 if (do_get_command(client
, reader
, req
, &content
, quiet
, 0) == FALSE
)
815 send_error(client
, EPWMD_EMPTY_ELEMENT
);
820 send_to_client(client
, "BEGIN %li\n%s\nOK \n", xmlStrlen(content
), content
);
826 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
,
827 const xmlChar
*content
)
829 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
839 for (n
= 0; *p
&& n
< 3; p
++) {
844 if (strstr((gchar
*)p
, "text()") != NULL
)
845 p
[xmlStrlen(p
) - 7] = 0;
847 for (n
= 0; p
[n
]; n
++) {
853 buf
= g_strdup_printf("%s\t%s\t%s", account
, p
, content
);
855 buf
= g_strdup_printf("%s\t%s", account
, p
);
860 static gboolean
append_to_array(gchar
***array
, gint
*total
, const gchar
*str
)
865 if ((a
= g_realloc(*array
, (t
+ 2) * sizeof(gchar
*))) == NULL
)
868 a
[t
++] = g_strdup(str
);
875 gboolean
list_command(struct client_s
*client
, gchar
*str
)
879 gchar
**elements
= NULL
;
880 gint pwmd_errno
= -1;
883 xmlChar
*path
= NULL
;
887 xmlTextReaderPtr r
= NULL
;
888 gchar
**req
= NULL
, **nreq
;
893 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
894 send_error(client
, EPWMD_LIBXML_ERROR
);
898 if (strchr(p
, ' ') == NULL
) {
900 if (list_accounts(client
->reader
, &dst
, &pwmd_errno
) == FALSE
) {
901 send_error(client
, pwmd_errno
);
905 send_to_client(client
, "BEGIN %i\n%s\nOK \n",
906 g_utf8_strlen(dst
, -1), dst
);
907 memset(dst
, 0, strlen(dst
));
916 while (*p
&& isspace(*p
))
922 if (strchr(p
, '\t') != NULL
) {
923 if ((req
= split_input_line(p
, "\t", 0)) == NULL
) {
924 send_error(client
, EPWMD_COMMAND_SYNTAX
);
928 if (find_account(client
->reader
, req
[0]) == FALSE
) {
929 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
933 if (find_elements(client
, client
->reader
, req
+ 1, 0) == FALSE
) {
939 if (find_account(client
->reader
, p
) == FALSE
) {
940 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
945 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
946 send_error(client
, EPWMD_LIBXML_ERROR
);
950 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
951 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
952 send_error(client
, EPWMD_LIBXML_ERROR
);
956 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
957 if (find_account(client
->reader
, (gchar
*)content
) == FALSE
) {
959 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
967 account
= (req
) ? g_strdup(req
[0]) : g_strdup(p
);
972 while (xmlTextReaderNext(client
->reader
) == 1) {
974 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
975 send_error(client
, EPWMD_LIBXML_ERROR
);
979 if (xmlTextReaderDepth(client
->reader
) == 1 &&
980 xmlStrcmp(n
->name
, (xmlChar
*)"account") == 0 &&
981 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_END_ELEMENT
)
985 * If the current element has a TARGET attribute, the value of the
986 * attribute is an element path somewhere else in the document. Use this
987 * value and not any TEXT element value.
989 type
= xmlTextReaderNodeType(client
->reader
);
990 a
= xmlHasProp(n
, (xmlChar
*)"target");
992 if (type
== XML_READER_TYPE_ELEMENT
&& a
) {
993 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
994 path
= xmlGetNodePath(n
);
996 if ((nreq
= split_input_line((gchar
*)content
, "\t", 0)) == NULL
) {
998 g_strfreev(elements
);
1002 send_error(client
, EPWMD_INVALID_ELEMENT
);
1009 if ((ret
= do_get_command(client
, &r
, nreq
, &content
, 0, 1)) == TRUE
) {
1010 if (content
&& *content
) {
1012 line
= element_path_to_req(account
, path
, content
);
1014 line
= element_path_to_req(account
, path
);
1018 if (append_to_array(&elements
, &total
, line
) == FALSE
) {
1020 g_strfreev(elements
);
1023 memset(line
, 0, g_utf8_strlen(line
, -1));
1026 xmlFreeTextReader(r
);
1027 send_error(client
, EPWMD_ERROR
);
1031 memset(line
, 0, g_utf8_strlen(line
, -1));
1035 if (xmlTextReaderNext(client
->reader
) == 1) {
1036 if (xmlTextReaderNodeType(client
->reader
) !=
1037 XML_READER_TYPE_TEXT
) {
1039 xmlFreeTextReader(r
);
1047 xmlFreeTextReader(r
);
1053 if (type
== XML_READER_TYPE_TEXT
) {
1054 xmlChar
*np
= xmlGetNodePath(n
);
1057 content
= xmlNodeGetContent(n
);
1058 line
= element_path_to_req(account
, np
, content
);
1061 line
= element_path_to_req(account
, np
);
1064 append_to_array(&elements
, &total
, line
);
1065 memset(line
, 0, g_utf8_strlen(line
, -1));
1071 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1077 line
= g_strjoinv("\n", elements
);
1078 send_to_client(client
, "BEGIN %li\n%s\nOK \n",
1079 g_utf8_strlen(line
, -1), line
);
1080 g_strfreev(elements
);
1086 * The client->reader handle should be at the element in the document where
1087 * the attribute will be created or modified.
1089 static gboolean
add_attribute(struct client_s
*client
, const gchar
*name
, const gchar
*value
)
1094 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1095 send_error(client
, EPWMD_LIBXML_ERROR
);
1099 if ((a
= xmlHasProp(n
, (xmlChar
*)name
)) == NULL
)
1100 a
= xmlNewProp(n
, (xmlChar
*)name
, (xmlChar
*)value
);
1102 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1108 * req[0] - element path
1110 static gboolean
attribute_list(struct client_s
*client
, gchar
**req
)
1112 gchar
**attrlist
= NULL
;
1114 gchar
**epath
= NULL
;
1119 if (!req
|| !req
[0]) {
1120 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1124 if ((epath
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1126 * The first argument may be only an account.
1128 if ((epath
= split_input_line(req
[0], " ", 0)) == NULL
) {
1129 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1134 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1135 send_error(client
, EPWMD_LIBXML_ERROR
);
1139 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1140 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1145 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1149 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1150 send_error(client
, EPWMD_LIBXML_ERROR
);
1154 for (a
= n
->properties
; a
; a
= a
->next
) {
1155 if ((attrlist
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1156 send_error(client
, EPWMD_ERROR
);
1161 attrlist
[i
++] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1166 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1170 line
= g_strjoinv("\n", attrlist
);
1171 send_to_client(client
, "BEGIN %li\n%s\n", g_utf8_strlen(line
, -1), line
);
1174 g_strfreev(attrlist
);
1182 * req[0] - attribute
1183 * req[1] - element path
1185 static gboolean
attribute_delete(struct client_s
*client
, gchar
**req
)
1189 gchar
**epath
= NULL
;
1191 if (!req
|| !req
[0] || !req
[1]) {
1192 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1196 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1198 * The first argument may be only an account.
1200 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1201 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1207 * Don't remove the NAME attribute for the account element.
1209 if (!epath
[1] && g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1210 send_error(client
, EPWMD_ATTR_SYNTAX
);
1214 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1215 send_error(client
, EPWMD_LIBXML_ERROR
);
1219 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1220 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1225 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1229 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1230 send_error(client
, EPWMD_LIBXML_ERROR
);
1234 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1235 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1239 if (xmlRemoveProp(a
) == -1) {
1240 send_error(client
, EPWMD_LIBXML_ERROR
);
1252 * req[0] - source element path
1253 * req[1] - destination element path
1255 static gboolean
target_attribute(struct client_s
*client
, gchar
**req
)
1257 gchar
**src
, **dst
, *line
;
1259 if (!req
|| !req
[0] || !req
[1]) {
1260 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1264 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1266 * The first argument may be only an account.
1268 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
) {
1269 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1274 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1276 * The first argument may be only an account.
1278 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1279 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1286 * Prevent an element tree pointing to only and account. Accounts require
1287 * at least one element. Accounts pointing to accounts are allowed.
1289 if ((!src
[1] && dst
[1]) || (!dst
[1] && src
[1])) {
1290 send_error(client
, EPWMD_ATTR_SYNTAX
);
1296 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1297 send_error(client
, EPWMD_LIBXML_ERROR
);
1304 * Make sure the destination element path exists.
1306 if (find_account(client
->reader
, dst
[0]) == FALSE
) {
1307 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1314 if (find_elements(client
, client
->reader
, dst
+1, 0) == FALSE
) {
1321 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1322 send_error(client
, EPWMD_LIBXML_ERROR
);
1329 * If the source element tree doesn't exist, create it.
1331 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1332 if (new_account(client
->doc
, src
[0]) == FALSE
) {
1333 send_error(client
, EPWMD_LIBXML_ERROR
);
1339 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1340 send_error(client
, EPWMD_LIBXML_ERROR
);
1346 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1347 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1355 if (find_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1356 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1357 send_error(client
, EPWMD_LIBXML_ERROR
);
1363 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1364 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1370 xmlTextReaderNext(client
->reader
);
1372 if (create_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1378 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1379 send_error(client
, EPWMD_LIBXML_ERROR
);
1385 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1386 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1392 if (find_elements(client
, client
->reader
, src
+1, 0) == FALSE
) {
1400 line
= g_strjoinv("\t", dst
);
1402 if (add_attribute(client
, "target", line
) == FALSE
) {
1420 * req[0] - account name
1423 static gboolean
name_attribute(struct client_s
*client
, gchar
**req
)
1425 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1426 send_error(client
, EPWMD_LIBXML_ERROR
);
1430 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1431 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1435 if (strcmp(req
[0], req
[1]) == 0)
1438 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1439 send_error(client
, EPWMD_LIBXML_ERROR
);
1444 * Will not overwrite an existing account.
1446 if (find_account(client
->reader
, req
[1]) == TRUE
) {
1447 send_error(client
, EPWMD_ACCOUNT_EXISTS
);
1451 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1452 send_error(client
, EPWMD_LIBXML_ERROR
);
1456 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1457 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1462 * Whitespace not allowed in account names.
1464 if (contains_whitespace(req
[1]) == TRUE
) {
1465 send_error(client
, EPWMD_ATTR_SYNTAX
);
1469 return add_attribute(client
, "name", req
[1]);
1473 * req[0] - attribute
1474 * req[1] - element path
1476 * If the element has a "target" attribute it won't be "followed".
1478 static gboolean
attribute_get(struct client_s
*client
, gchar
**req
)
1482 gchar
**nreq
= NULL
;
1484 if (!req
|| !req
[0] || !req
[1]) {
1485 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1489 if (strchr(req
[1], '\t')) {
1490 if ((nreq
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1491 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1496 if ((nreq
= split_input_line(req
[1], " ", 0)) == NULL
) {
1497 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1502 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1504 send_error(client
, EPWMD_LIBXML_ERROR
);
1508 if (find_account(client
->reader
, nreq
[0]) == FALSE
) {
1510 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1515 if (find_elements(client
, client
->reader
, nreq
+ 1, 0) == FALSE
) {
1523 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1524 send_error(client
, EPWMD_LIBXML_ERROR
);
1528 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1529 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1533 send_to_client(client
, "BEGIN %li\n%s\n", xmlStrlen(a
), a
);
1539 * req[0] - attribute
1540 * req[1] - element path
1543 static gboolean
attribute_set(struct client_s
*client
, gchar
**req
)
1545 gchar
**epath
= NULL
;
1547 if (!req
|| !req
[0] || !req
[1] || !req
[2]) {
1548 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1553 * Reserved attribute names.
1555 if (g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1557 * Only reserved for the account element. Not the rest of the
1560 if (strchr(req
[1], '\t') == NULL
)
1561 return name_attribute(client
, req
+ 1);
1563 else if (g_ascii_strcasecmp(req
[0], "TARGET") == 0)
1564 return target_attribute(client
, req
+ 1);
1566 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1568 * The first argument may be only an account.
1570 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1571 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1576 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1577 send_error(client
, EPWMD_LIBXML_ERROR
);
1581 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1582 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1587 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1592 return add_attribute(client
, req
[0], req
[2]);
1600 * req[1] - attribute name or element path if command is LIST
1601 * req[2] - element path
1602 * req[2] - element path or value
1604 gboolean
attr_command(struct client_s
*client
, gchar
**req
)
1606 if (!req
|| !req
[0] || !req
[1]) {
1607 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1611 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
1612 return attribute_set(client
, req
+1);
1613 if (g_ascii_strcasecmp(req
[0], "GET") == 0)
1614 return attribute_get(client
, req
+1);
1615 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
1616 return attribute_delete(client
, req
+1);
1617 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
1618 return attribute_list(client
, req
+1);
1620 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1625 static gboolean
cache_test(const guchar
*md5filename
, gint reset
)
1631 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
1632 memcpy(&f
, p
, sizeof(file_cache_t
));
1635 memset(&f
, 0, sizeof(file_cache_t
));
1636 memcpy(p
, &f
, sizeof(file_cache_t
));
1637 p
+= sizeof(file_cache_t
);
1638 len
+= sizeof(file_cache_t
);
1640 if (len
+ sizeof(file_cache_t
) > cache_size
)
1645 if (f
.used
== TRUE
) {
1646 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
1648 memset(&f
, 0, sizeof(file_cache_t
));
1649 memcpy(p
, &f
, sizeof(file_cache_t
));
1653 return (f
.key
[0]) ? TRUE
: FALSE
;
1657 p
+= sizeof(file_cache_t
);
1658 len
+= sizeof(file_cache_t
);
1660 if (len
+ sizeof(file_cache_t
) > cache_size
)
1664 return (reset
== 2) ? TRUE
: FALSE
;
1667 static gboolean
file_exists(const gchar
*filename
)
1671 if (access(filename
, R_OK
) == -1)
1674 stat(filename
, &st
);
1676 if (st
.st_size
== 0)
1682 gboolean
cache_command(struct client_s
*client
, gchar
**req
)
1686 if (g_ascii_strcasecmp(req
[0], "clear") == 0) {
1688 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1692 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1694 if (cache_test(md5file
, 1) == FALSE
) {
1695 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1701 else if (g_ascii_strcasecmp(req
[0], "clearall") == 0) {
1702 cache_test(client
->md5file
, 2);
1705 else if (g_ascii_strcasecmp(req
[0], "iscached") == 0) {
1707 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1711 if (file_exists(req
[1]) == FALSE
) {
1712 send_error(client
, EPWMD_FILE_NOT_FOUND
);
1716 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1718 if (cache_test(md5file
, 0) == FALSE
) {
1719 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1726 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1731 gboolean
help_command(struct client_s
*client
, const gchar
*what
)
1735 if (!what
|| !*what
)
1737 "NFO Try 'HELP COMMAND' for command help\n"
1738 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
1739 else if (g_ascii_strcasecmp(what
, "GET") == 0)
1741 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
1742 "NFO <account> is the account to work on and <element>\n"
1743 "NFO is the element wanted.\n"
1745 "NFO Example: GET isp <TAB> imap <TAB> port\n"
1746 "NFO GET isp <TAB> username\n";
1747 else if (g_ascii_strcasecmp(what
, "QUIT") == 0)
1749 "NFO syntax: QUIT\n"
1750 "NFO close the connection\n";
1751 else if (g_ascii_strcasecmp(what
, "DELETE") == 0)
1753 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
1754 else if (g_ascii_strcasecmp(what
, "STORE") == 0)
1756 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
1757 "NFO <account> is the account to work on and <element>\n"
1758 "NFO is the element to create or modify\n"
1760 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
1761 "NFO STORE isp <TAB> username <TAB> someuser\n";
1762 else if (g_ascii_strcasecmp(what
, "OPEN") == 0)
1764 "NFO syntax: OPEN <filename> [<key>]\n"
1765 "NFO opens a (new) file\n";
1766 else if (g_ascii_strcasecmp(what
, "LIST") == 0)
1768 "NFO syntax: LIST [account]\n"
1769 "NFO shows available accounts or account elements\n";
1770 else if (g_ascii_strcasecmp(what
, "ATTR") == 0)
1772 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
1773 "NFO ATTR SET NAME account value\n"
1774 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
1775 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
1776 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
1777 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
1778 "NFO ATTR LIST account[<TAB>element[...]]\n";
1779 else if (g_ascii_strcasecmp(what
, "SAVE") == 0)
1781 "NFO syntax: SAVE [<key>]\n"
1782 "NFO save any changes to the opened file using <key>\n";
1783 else if (g_ascii_strcasecmp(what
, "CACHE") == 0)
1785 "NFO syntax: CACHE [CLEARALL | CLEAR <filename> | ISCACHED <filename>]\n"
1786 "NFO tests or clears the cache entry for <filename>\n";
1787 else if (g_ascii_strcasecmp(what
, "DUMP") == 0)
1789 "NFO syntax: DUMP\n"
1790 "NFO shows the in memory XML document\n";
1792 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1796 send_to_client(client
, "%sOK \n", line
);
1800 gboolean
dump_command(struct client_s
*client
)
1805 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
1806 send_to_client(client
, "BEGIN %li\n%s", len
, xml
);