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 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 xmlErrorPtr xml_error
;
67 switch (open_xml(client
->xml
, client
->len
, &client
->doc
, &client
->reader
)) {
69 xml_error
= xmlGetLastError();
70 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
71 send_error(client
, EPWMD_LIBXML_ERROR
);
74 xml_error
= xmlGetLastError();
75 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
76 send_error(client
, EPWMD_LIBXML_ERROR
);
85 static gboolean
valid_filename(const gchar
*filename
)
89 if (!filename
|| !*filename
)
92 for (p
= filename
; *p
; p
++) {
93 if (g_ascii_isalnum(*p
) == FALSE
)
100 static gboolean
cache_reset_timeout(const guchar
*md5filename
)
106 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
107 memcpy(&f
, p
, sizeof(file_cache_t
));
109 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
111 memcpy(p
, &f
, sizeof(file_cache_t
));
115 p
+= sizeof(file_cache_t
);
116 len
+= sizeof(file_cache_t
);
118 if (len
+ sizeof(file_cache_t
) > cache_size
)
125 gboolean
cache_set_timeout(const guchar
*md5filename
, glong timeout
)
131 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
132 memcpy(&f
, p
, sizeof(file_cache_t
));
135 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
136 f
.when
= f
.timeout
= timeout
;
137 memcpy(p
, &f
, sizeof(file_cache_t
));
142 f
.when
= f
.timeout
= timeout
;
143 memcpy(p
, &f
, sizeof(file_cache_t
));
146 p
+= sizeof(file_cache_t
);
147 len
+= sizeof(file_cache_t
);
149 if (len
+ sizeof(file_cache_t
) > cache_size
)
153 return (md5filename
) ? FALSE
: TRUE
;
156 static gboolean
cache_update_key(const guchar
*md5filename
, const guchar
*shakey
)
162 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
163 memcpy(&f
, p
, sizeof(file_cache_t
));
165 if (f
.used
== TRUE
) {
166 if (memcmp((gchar
*)f
.filename
, (gchar
*)md5filename
, sizeof(f
.filename
)) == 0) {
167 memcpy(&f
.key
, shakey
, sizeof(f
.key
));
168 memcpy(p
, &f
, sizeof(file_cache_t
));
173 p
+= sizeof(file_cache_t
);
174 len
+= sizeof(file_cache_t
);
176 if (len
+ sizeof(file_cache_t
) > cache_size
)
180 return cache_add_file(md5filename
, shakey
);
183 static gint
cache_valid_key(const guchar
*key
, gsize len
)
187 for (b
= 0; b
< len
; b
++) {
195 static gboolean
cache_get_key(const guchar
*md5file
, guchar
*shakey
)
201 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
202 memcpy(&f
, p
, sizeof(file_cache_t
));
205 * The slot may be used but not yet contain a key.
207 if (f
.used
== TRUE
) {
208 if (memcmp(&f
.filename
, md5file
, sizeof(f
.filename
)) == 0) {
209 if (cache_valid_key(f
.key
, sizeof(f
.key
)) == FALSE
)
212 memcpy(shakey
, &f
.key
, sizeof(f
.key
));
217 p
+= sizeof(file_cache_t
);
218 len
+= sizeof(file_cache_t
);
220 if (len
+ sizeof(file_cache_t
) > cache_size
)
227 gint
open_file(const gchar
*filename
, struct stat
*st
)
231 if ((fd
= open(filename
, O_RDONLY
)) == -1)
234 if (stat(filename
, st
) == -1) {
242 gboolean
open_command(struct client_s
*client
, gchar
**req
)
247 const gchar
*filename
= req
[0];
248 guchar shakey
[gcrykeysize
];
249 guchar tkey
[gcrykeysize
];
254 gchar filebuf
[PATH_MAX
], *p
, *p2
;
256 struct file_header_s
{
258 guchar iv
[gcryblocksize
];
261 if (!filename
|| !*filename
) {
262 send_error(client
, EPWMD_COMMAND_SYNTAX
);
266 if (valid_filename(filename
) == FALSE
) {
267 send_error(client
, EPWMD_INVALID_FILENAME
);
271 p
= get_key_file_string("default", "data_directory");
272 p2
= expand_homedir(p
);
274 snprintf(filebuf
, sizeof(filebuf
), "%s/%s", p2
, filename
);
277 if (stat(filebuf
, &st
) == 0) {
278 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
279 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_NOT_A_FILE
));
280 send_to_client(client
, "ERR %03i %s\n", EPWMD_NOT_A_FILE
, pwmd_strerror(EPWMD_NOT_A_FILE
));
284 client
->mtime
= st
.st_mtime
;
287 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
290 * New files don't need a key.
292 if (access(filebuf
, R_OK
|W_OK
) != 0) {
293 if (errno
!= ENOENT
) {
295 log_write("%s: %s", filename
, strerror(errno
));
296 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
,
301 if ((client
->xml
= new_document()) == NULL
) {
303 log_write("%s", strerror(errno
));
304 send_to_client(client
, "ERR %03i malloc(): %s\n", EPWMD_ERROR
,
309 client
->len
= strlen(client
->xml
);
311 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
312 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
313 send_error(client
, EPWMD_MAX_SLOTS
);
317 client
->filename
= g_strdup(filename
);
318 return parse_xml(client
);
321 if ((fd
= open_file(filebuf
, &st
)) == -1) {
323 log_write("%s: %s", filename
, strerror(errno
));
324 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, filename
, strerror(error
));
331 if (cache_get_key(client
->md5file
, shakey
) == TRUE
)
335 * No key specified and no matching filename found in the cache.
337 if (!req
[1] || !*req
[1]) {
339 send_error(client
, EPWMD_KEY
);
344 insize
= st
.st_size
- sizeof(struct file_header_s
);
345 read(fd
, &file_header
, sizeof(struct file_header_s
));
346 inbuf
= gcry_malloc(insize
);
347 read(fd
, inbuf
, insize
);
352 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
353 memset(req
[1], 0, strlen(req
[1]));
356 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
357 sizeof(file_header
.iv
)))) {
359 memset(shakey
, 0, sizeof(shakey
));
360 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
361 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
365 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, shakey
, gcrykeysize
))) {
367 memset(shakey
, 0, sizeof(shakey
));
368 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
369 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
373 if (decrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
379 memset(shakey
, 0, sizeof(shakey
));
381 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
382 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
386 memcpy(tkey
, shakey
, sizeof(tkey
));
389 if ((gcryerrno
= gcry_cipher_setkey(client
->gh
, tkey
, gcrykeysize
))) {
390 memset(tkey
, 0, sizeof(tkey
));
392 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
393 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
397 iter
= file_header
.iter
;
400 if ((gcryerrno
= gcry_cipher_setiv(client
->gh
, file_header
.iv
,
401 sizeof(file_header
.iv
)))) {
402 memset(tkey
, 0, sizeof(tkey
));
404 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
405 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
409 if (decrypt_xml(client
->gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
415 memset(tkey
, 0, sizeof(tkey
));
417 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
418 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
423 memset(tkey
, 0, sizeof(tkey
));
425 client
->len
= insize
;
427 if (g_strncasecmp(client
->xml
, "<?xml version=\"1.0\"?>", 21) != 0) {
428 send_error(client
, EPWMD_BADKEY
);
433 if (cache_add_file(client
->md5file
, shakey
) == FALSE
) {
434 memset(shakey
, 0, sizeof(shakey
));
435 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
436 send_error(client
, EPWMD_MAX_SLOTS
);
441 memset(shakey
, 0, sizeof(shakey
));
442 client
->filename
= g_strdup(filename
);
445 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
446 cache_set_timeout(client
->md5file
, timeout
);
449 return parse_xml(client
);
453 * client->reader should be at the position in the document where the element
454 * search should start. It won't search past the closing account element.
456 static gboolean
find_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
457 gchar
**req
, gint quiet
)
461 if (!req
|| !req
[0]) {
463 send_error(client
, EPWMD_COMMAND_SYNTAX
);
467 for (i
= 0; req
[i
]; i
++) {
468 if (find_element(reader
, req
[i
], req
[i
+1] != NULL
) == FALSE
) {
470 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
479 gboolean
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
480 const gchar
*filename
, const xmlChar
*data
, size_t insize
,
481 const guchar
*shakey
, guint iter
)
486 guchar tkey
[gcrykeysize
];
489 struct file_header_s
{
491 guchar iv
[gcryblocksize
];
494 if (insize
/ gcryblocksize
) {
495 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
497 if (insize
% gcryblocksize
)
498 len
+= gcryblocksize
;
501 inbuf
= gcry_calloc(1, len
);
502 memcpy(inbuf
, data
, insize
);
504 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
505 memcpy(tkey
, shakey
, sizeof(tkey
));
508 if ((gcryerrno
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
510 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
511 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
515 file_header
.iter
= iter
;
518 if ((gcryerrno
= gcry_cipher_setiv(gh
, file_header
.iv
,
519 sizeof(file_header
.iv
)))) {
521 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
522 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
526 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
529 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
530 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
535 if ((gcryerrno
= gcry_cipher_setiv(gh
, file_header
.iv
,
536 sizeof(file_header
.iv
)))) {
538 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
539 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
543 if ((gcryerrno
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
545 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
546 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
550 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
553 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
554 send_to_client(client
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
559 if ((fd
= open(filename
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
562 p
= strrchr(filename
, '/');
564 log_write("%s: %s", p
, strerror(errno
));
565 send_to_client(client
, "ERR %03i %s: %s\n", EPWMD_ERROR
, p
, strerror(error
));
571 * xml_import() from command line.
575 write(fd
, &file_header
, sizeof(struct file_header_s
));
576 write(fd
, inbuf
, insize
);
585 gboolean
save_command(struct client_s
*client
, const gchar
*filename
, gchar
*key
)
590 guchar shakey
[gcrykeysize
];
593 gchar filebuf
[PATH_MAX
], *p
, *p2
;
594 gboolean reset_timeout
;
596 p
= get_key_file_string("default", "data_directory");
597 p2
= expand_homedir(p
);
599 snprintf(filebuf
, sizeof(filebuf
), "%s/%s", p2
, filename
);
602 if (stat(filebuf
, &st
) == 0 && client
->mtime
) {
603 if (client
->mtime
!= st
.st_mtime
) {
604 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_FILE_MODIFIED
));
605 send_error(client
, EPWMD_FILE_MODIFIED
);
611 if (cache_get_key(client
->md5file
, shakey
) == FALSE
) {
612 send_error(client
, EPWMD_KEY
);
619 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, key
, strlen(key
));
620 memset(key
, 0, strlen(key
));
623 xmlDocDumpFormatMemory(client
->doc
, &xmlbuf
, &len
, 0);
625 if ((iter
= get_key_file_integer(client
->filename
, "iterations")) == -1)
628 if (do_xml_encrypt(client
, client
->gh
, filebuf
, xmlbuf
, len
, shakey
,
630 memset(shakey
, 0, sizeof(shakey
));
637 client
->mtime
= st
.st_mtime
;
640 memset(shakey
, 0, sizeof(shakey
));
642 if ((reset_timeout
= get_key_file_boolean(client
->filename
,
643 "cache_reset_timeout")) == TRUE
)
644 cache_reset_timeout(client
->md5file
);
649 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
650 memset(shakey
, 0, sizeof(shakey
));
651 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
652 send_error(client
, EPWMD_MAX_SLOTS
);
656 memset(shakey
, 0, sizeof(shakey
));
657 cache_reset_timeout(client
->md5file
);
661 static gboolean
contains_whitespace(const gchar
*str
)
663 const gchar
*p
= str
;
666 if (g_ascii_isspace(*p
) == TRUE
) {
675 * the 'reader' should be at the element of the document where the elements
676 * 'req' are to be created.
678 static gboolean
create_elements(struct client_s
*client
, xmlTextReaderPtr reader
,
679 gchar
**req
, gint novalue
)
685 r
= xmlTextReaderCurrentNode(reader
);
687 if (xmlTextReaderDepth(reader
) > 1)
690 for (i
= 0; req
[i
]; i
++) {
694 * Whitespace is allowed in values or attributes but not in element
697 if (req
[i
+1] && valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
698 send_error(client
, EPWMD_INVALID_ELEMENT
);
703 * The value of the element tree.
705 if (!req
[i
+1] && !novalue
) {
707 * Prevent creating 'text' elements in the root of the account.
710 send_error(client
, EPWMD_ROOT_TEXT_ELEMENT
);
715 * FIXME ?? overwriting an element tree with a text element:
717 * STORE account element element2 value
718 * STORE account element value
720 * would remove the existing element tree. This may be a bug or
723 xmlNodeSetContent(r
, (xmlChar
*)req
[i
]);
727 if ((n
= find_node(r
, (xmlChar
*)req
[i
])) == NULL
) {
728 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
729 r
= xmlAddChild(r
, n
);
734 if (!req
[i
+1] && novalue
)
742 * FIXME reuse reader handle
744 static gboolean
reset_reader(xmlDocPtr doc
, xmlTextReaderPtr
*reader
)
747 xmlFreeTextReader(*reader
);
751 if ((*reader
= xmlReaderWalker(doc
)) == NULL
)
757 static void delete_node(xmlNodePtr n
)
763 gboolean
delete_command(struct client_s
*client
, gchar
**req
)
767 xmlErrorPtr xml_error
;
769 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
770 xml_error
= xmlGetLastError();
771 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
772 send_error(client
, EPWMD_LIBXML_ERROR
);
776 if (find_account(client
->reader
, req
[0]) == FALSE
) {
777 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
781 n
= xmlTextReaderCurrentNode(client
->reader
);
784 * No sub-node defined. Remove the entire node (account).
793 * Remove matching sub-nodes starting from the root of the account.
795 while (req
[i
] && find_element(client
->reader
, req
[i
++], req
[i
] != NULL
) == TRUE
)
796 n
= xmlTextReaderCurrentNode(client
->reader
);
798 if (n
&& xmlStrcmp(n
->name
, (xmlChar
*)req
[i
-1]) == 0 &&
799 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_ELEMENT
)
802 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
809 gboolean
store_command(struct client_s
*client
, gchar
**req
)
811 xmlErrorPtr xml_error
;
814 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
815 xml_error
= xmlGetLastError();
816 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
817 send_error(client
, EPWMD_LIBXML_ERROR
);
822 send_error(client
, EPWMD_COMMAND_SYNTAX
);
826 if (find_account(client
->reader
, req
[0]) == FALSE
) {
827 if (contains_whitespace(req
[0]) == TRUE
) {
828 send_error(client
, EPWMD_INVALID_ELEMENT
);
832 if (new_account(client
->doc
, req
[0]) == FALSE
) {
833 send_error(client
, EPWMD_ERROR
);
840 xmlTextReaderNext(client
->reader
);
841 return create_elements(client
, client
->reader
, req
+1, 0);
844 static gboolean
do_get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
845 gchar
**req
, xmlChar
**content
, gint quiet
, gint list
)
852 xmlErrorPtr xml_error
;
854 if (reset_reader(client
->doc
, reader
) == FALSE
) {
856 xml_error
= xmlGetLastError();
857 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
858 send_error(client
, EPWMD_LIBXML_ERROR
);
864 if (find_account(*reader
, req
[0]) == FALSE
) {
866 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
871 * May be an account with only a TARGET attribute.
874 if (find_elements(client
, *reader
, req
+ 1, (list
) ? 1 : quiet
) == FALSE
)
878 if ((n
= xmlTextReaderCurrentNode(*reader
)) == NULL
) {
880 xml_error
= xmlGetLastError();
881 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
882 send_error(client
, EPWMD_LIBXML_ERROR
);
889 * If the current element has a TARGET attribute, the value of the
890 * attribute is an element path somewhere else in the document. Use this
891 * value and not any TEXT element value. The value may be another element
892 * with a TARGET attribute and this function will recurse until a non-TARGET
893 * attribute in the element is found.
895 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
896 if ((p
= xmlNodeGetContent(a
->children
)) != NULL
) {
897 if (strchr((gchar
*)p
, '\t') != NULL
) {
898 if ((nreq
= split_input_line((gchar
*)p
, "\t", 0)) == NULL
) {
902 send_error(client
, EPWMD_INVALID_ELEMENT
);
907 if ((nreq
= split_input_line((gchar
*)p
, " ", 0)) == NULL
) {
911 send_error(client
, EPWMD_INVALID_ELEMENT
);
917 ret
= do_get_command(client
, reader
, nreq
, content
, quiet
, list
);
923 switch (xmlTextReaderNext(*reader
)) {
926 xml_error
= xmlGetLastError();
927 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
928 send_error(client
, EPWMD_LIBXML_ERROR
);
934 send_error(client
, EPWMD_EMPTY_ELEMENT
);
940 switch (xmlTextReaderNodeType(*reader
)) {
941 case XML_READER_TYPE_END_ELEMENT
:
943 * May be an empty element after an ATTR DELETE TARGET command.
946 case XML_READER_TYPE_TEXT
:
950 xml_error
= xmlGetLastError();
951 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
952 send_error(client
, EPWMD_LIBXML_ERROR
);
958 if (n
->children
&& !list
)
959 send_error(client
, EPWMD_TRAILING_ELEMENT
);
960 else if (!n
->children
&& !list
)
961 send_error(client
, EPWMD_INVALID_ELEMENT
);
966 if ((*content
= xmlNodeGetContent(n
)) == NULL
) {
968 send_error(client
, EPWMD_EMPTY_ELEMENT
);
976 * Retrieves the value associated with the element tree 'req'.
978 gboolean
get_command(struct client_s
*client
, xmlTextReaderPtr
*reader
,
979 gchar
**req
, gint quiet
)
981 xmlChar
*content
= NULL
;
983 if (do_get_command(client
, reader
, req
, &content
, quiet
, 0) == FALSE
)
988 send_error(client
, EPWMD_EMPTY_ELEMENT
);
993 send_to_client(client
, "BEGIN %li\n%s\nOK \n", xmlStrlen(content
), content
);
999 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
,
1000 const xmlChar
*content
)
1002 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
1012 for (n
= 0; *p
&& n
< 3; p
++) {
1017 if (strstr((gchar
*)p
, "text()") != NULL
)
1018 p
[xmlStrlen(p
) - 7] = 0;
1020 for (n
= 0; p
[n
]; n
++) {
1026 buf
= g_strdup_printf("%s\t%s\t%s", account
, p
, content
);
1028 buf
= g_strdup_printf("%s\t%s", account
, p
);
1033 static gboolean
append_to_array(gchar
***array
, gint
*total
, const gchar
*str
)
1038 if ((a
= g_realloc(*array
, (t
+ 2) * sizeof(gchar
*))) == NULL
)
1041 a
[t
++] = g_strdup(str
);
1048 gboolean
list_command(struct client_s
*client
, gchar
*str
)
1052 gchar
**elements
= NULL
;
1053 gint pwmd_errno
= -1;
1056 xmlChar
*path
= NULL
;
1060 xmlTextReaderPtr r
= NULL
;
1061 gchar
**req
= NULL
, **nreq
;
1065 xmlErrorPtr xml_error
;
1068 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1069 xml_error
= xmlGetLastError();
1070 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1071 send_error(client
, EPWMD_LIBXML_ERROR
);
1075 if (strchr(p
, ' ') == NULL
) {
1077 if (list_accounts(client
->reader
, &dst
, &pwmd_errno
) == FALSE
) {
1078 send_error(client
, pwmd_errno
);
1082 send_to_client(client
, "BEGIN %i\n%s\nOK \n",
1083 g_utf8_strlen(dst
, -1), dst
);
1084 memset(dst
, 0, strlen(dst
));
1093 while (*p
&& isspace(*p
))
1099 if (strchr(p
, '\t') != NULL
) {
1100 if ((req
= split_input_line(p
, "\t", 0)) == NULL
) {
1101 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1105 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1106 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1110 if (find_elements(client
, client
->reader
, req
+ 1, 0) == FALSE
) {
1115 depth
= xmlTextReaderDepth(client
->reader
);
1118 if (find_account(client
->reader
, p
) == FALSE
) {
1119 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1124 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1125 xml_error
= xmlGetLastError();
1126 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1127 send_error(client
, EPWMD_LIBXML_ERROR
);
1131 if ((a
= xmlHasProp(n
, (xmlChar
*)"target")) != NULL
) {
1132 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1133 send_error(client
, EPWMD_LIBXML_ERROR
);
1137 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
1138 if (find_account(client
->reader
, (gchar
*)content
) == FALSE
) {
1140 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1148 account
= (req
) ? g_strdup(req
[0]) : g_strdup(p
);
1153 while (xmlTextReaderNext(client
->reader
) == 1) {
1155 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1156 xml_error
= xmlGetLastError();
1157 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1158 send_error(client
, EPWMD_LIBXML_ERROR
);
1162 if (xmlTextReaderDepth(client
->reader
) == 1 &&
1163 xmlStrcmp(n
->name
, (xmlChar
*)"account") == 0 &&
1164 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_END_ELEMENT
)
1167 if (depth
&& depth
== xmlTextReaderDepth(client
->reader
) &&
1168 xmlTextReaderNodeType(client
->reader
) == XML_READER_TYPE_END_ELEMENT
)
1172 * If the current element has a TARGET attribute, the value of the
1173 * attribute is an element path somewhere else in the document. Use this
1174 * value and not any TEXT element value.
1176 type
= xmlTextReaderNodeType(client
->reader
);
1177 a
= xmlHasProp(n
, (xmlChar
*)"target");
1179 if (type
== XML_READER_TYPE_ELEMENT
&& a
) {
1180 if ((content
= xmlNodeGetContent(a
->children
)) != NULL
) {
1181 path
= xmlGetNodePath(n
);
1183 if ((nreq
= split_input_line((gchar
*)content
, "\t", 0)) == NULL
) {
1185 g_strfreev(elements
);
1189 send_error(client
, EPWMD_INVALID_ELEMENT
);
1196 if ((ret
= do_get_command(client
, &r
, nreq
, &content
, 0, 1)) == TRUE
) {
1197 if (content
&& *content
) {
1199 line
= element_path_to_req(account
, path
, content
);
1201 line
= element_path_to_req(account
, path
);
1205 if (append_to_array(&elements
, &total
, line
) == FALSE
) {
1207 g_strfreev(elements
);
1210 memset(line
, 0, g_utf8_strlen(line
, -1));
1213 xmlFreeTextReader(r
);
1214 send_error(client
, EPWMD_ERROR
);
1218 memset(line
, 0, g_utf8_strlen(line
, -1));
1222 if (xmlTextReaderNext(client
->reader
) == 1) {
1223 if (xmlTextReaderNodeType(client
->reader
) !=
1224 XML_READER_TYPE_TEXT
) {
1226 xmlFreeTextReader(r
);
1234 xmlFreeTextReader(r
);
1240 if (type
== XML_READER_TYPE_TEXT
) {
1241 xmlChar
*np
= xmlGetNodePath(n
);
1244 content
= xmlNodeGetContent(n
);
1245 line
= element_path_to_req(account
, np
, content
);
1248 line
= element_path_to_req(account
, np
);
1251 append_to_array(&elements
, &total
, line
);
1252 memset(line
, 0, g_utf8_strlen(line
, -1));
1258 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1264 line
= g_strjoinv("\n", elements
);
1265 send_to_client(client
, "BEGIN %li\n%s\nOK \n",
1266 g_utf8_strlen(line
, -1), line
);
1267 g_strfreev(elements
);
1273 * The client->reader handle should be at the element in the document where
1274 * the attribute will be created or modified.
1276 static gboolean
add_attribute(struct client_s
*client
, const gchar
*name
, const gchar
*value
)
1280 xmlErrorPtr xml_error
;
1282 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1283 xml_error
= xmlGetLastError();
1284 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1285 send_error(client
, EPWMD_LIBXML_ERROR
);
1289 if ((a
= xmlHasProp(n
, (xmlChar
*)name
)) == NULL
)
1290 a
= xmlNewProp(n
, (xmlChar
*)name
, (xmlChar
*)value
);
1292 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1298 * req[0] - element path
1300 static gboolean
attribute_list(struct client_s
*client
, gchar
**req
)
1302 gchar
**attrlist
= NULL
;
1304 gchar
**epath
= NULL
;
1308 xmlErrorPtr xml_error
;
1310 if (!req
|| !req
[0]) {
1311 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1315 if ((epath
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1317 * The first argument may be only an account.
1319 if ((epath
= split_input_line(req
[0], " ", 0)) == NULL
) {
1320 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1325 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1326 xml_error
= xmlGetLastError();
1327 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1328 send_error(client
, EPWMD_LIBXML_ERROR
);
1332 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1333 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1338 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1342 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1343 xml_error
= xmlGetLastError();
1344 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1345 send_error(client
, EPWMD_LIBXML_ERROR
);
1349 for (a
= n
->properties
; a
; a
= a
->next
) {
1350 if ((attrlist
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1351 log_write("%s(%i): g_realloc() failed", __FILE__
, __LINE__
);
1352 send_error(client
, EPWMD_ERROR
);
1357 attrlist
[i
++] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1362 send_error(client
, EPWMD_EMPTY_ELEMENT
);
1366 line
= g_strjoinv("\n", attrlist
);
1367 send_to_client(client
, "BEGIN %li\n%s\n", g_utf8_strlen(line
, -1), line
);
1370 g_strfreev(attrlist
);
1378 * req[0] - attribute
1379 * req[1] - element path
1381 static gboolean
attribute_delete(struct client_s
*client
, gchar
**req
)
1385 gchar
**epath
= NULL
;
1386 xmlErrorPtr xml_error
;
1388 if (!req
|| !req
[0] || !req
[1]) {
1389 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1393 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1395 * The first argument may be only an account.
1397 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1398 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1404 * Don't remove the NAME attribute for the account element.
1406 if (!epath
[1] && g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1407 send_error(client
, EPWMD_ATTR_SYNTAX
);
1411 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1412 xml_error
= xmlGetLastError();
1413 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1414 send_error(client
, EPWMD_LIBXML_ERROR
);
1418 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1419 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1424 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1428 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1429 xml_error
= xmlGetLastError();
1430 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1431 send_error(client
, EPWMD_LIBXML_ERROR
);
1435 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1436 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1440 if (xmlRemoveProp(a
) == -1) {
1441 xml_error
= xmlGetLastError();
1442 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1443 send_error(client
, EPWMD_LIBXML_ERROR
);
1455 * req[0] - source element path
1456 * req[1] - destination element path
1458 static gboolean
target_attribute(struct client_s
*client
, gchar
**req
)
1460 gchar
**src
, **dst
, *line
;
1461 xmlErrorPtr xml_error
;
1463 if (!req
|| !req
[0] || !req
[1]) {
1464 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1468 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1470 * The first argument may be only an account.
1472 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
) {
1473 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1478 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1480 * The first argument may be only an account.
1482 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1483 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1490 * Prevent an element tree pointing to only and account. Accounts require
1491 * at least one element. Accounts pointing to accounts are allowed.
1493 if ((!src
[1] && dst
[1]) || (!dst
[1] && src
[1])) {
1494 send_error(client
, EPWMD_ATTR_SYNTAX
);
1500 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1501 xml_error
= xmlGetLastError();
1502 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1503 send_error(client
, EPWMD_LIBXML_ERROR
);
1510 * Make sure the destination element path exists.
1512 if (find_account(client
->reader
, dst
[0]) == FALSE
) {
1513 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1520 if (find_elements(client
, client
->reader
, dst
+1, 0) == FALSE
) {
1527 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1528 xml_error
= xmlGetLastError();
1529 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1530 send_error(client
, EPWMD_LIBXML_ERROR
);
1537 * If the source element tree doesn't exist, create it.
1539 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1540 if (new_account(client
->doc
, src
[0]) == FALSE
) {
1541 xml_error
= xmlGetLastError();
1542 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1543 send_error(client
, EPWMD_LIBXML_ERROR
);
1549 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1550 xml_error
= xmlGetLastError();
1551 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1552 send_error(client
, EPWMD_LIBXML_ERROR
);
1558 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1559 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1567 if (find_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1568 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1569 xml_error
= xmlGetLastError();
1570 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1571 send_error(client
, EPWMD_LIBXML_ERROR
);
1577 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1578 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1584 xmlTextReaderNext(client
->reader
);
1586 if (create_elements(client
, client
->reader
, src
+1, 1) == FALSE
) {
1592 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1593 xml_error
= xmlGetLastError();
1594 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1595 send_error(client
, EPWMD_LIBXML_ERROR
);
1601 if (find_account(client
->reader
, src
[0]) == FALSE
) {
1602 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1608 if (find_elements(client
, client
->reader
, src
+1, 0) == FALSE
) {
1616 line
= g_strjoinv("\t", dst
);
1618 if (add_attribute(client
, "target", line
) == FALSE
) {
1636 * req[0] - account name
1639 static gboolean
name_attribute(struct client_s
*client
, gchar
**req
)
1641 xmlErrorPtr xml_error
;
1643 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1644 xml_error
= xmlGetLastError();
1645 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1646 send_error(client
, EPWMD_LIBXML_ERROR
);
1650 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1651 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1655 if (strcmp(req
[0], req
[1]) == 0)
1658 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1659 xml_error
= xmlGetLastError();
1660 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1661 send_error(client
, EPWMD_LIBXML_ERROR
);
1666 * Will not overwrite an existing account.
1668 if (find_account(client
->reader
, req
[1]) == TRUE
) {
1669 send_error(client
, EPWMD_ACCOUNT_EXISTS
);
1673 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1674 xml_error
= xmlGetLastError();
1675 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1676 send_error(client
, EPWMD_LIBXML_ERROR
);
1680 if (find_account(client
->reader
, req
[0]) == FALSE
) {
1681 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1686 * Whitespace not allowed in account names.
1688 if (contains_whitespace(req
[1]) == TRUE
) {
1689 send_error(client
, EPWMD_ATTR_SYNTAX
);
1693 return add_attribute(client
, "name", req
[1]);
1697 * req[0] - attribute
1698 * req[1] - element path
1700 * If the element has a "target" attribute it won't be "followed".
1702 static gboolean
attribute_get(struct client_s
*client
, gchar
**req
)
1706 gchar
**nreq
= NULL
;
1707 xmlErrorPtr xml_error
;
1709 if (!req
|| !req
[0] || !req
[1]) {
1710 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1714 if (strchr(req
[1], '\t')) {
1715 if ((nreq
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1716 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1721 if ((nreq
= split_input_line(req
[1], " ", 0)) == NULL
) {
1722 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1727 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1728 xml_error
= xmlGetLastError();
1729 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1731 send_error(client
, EPWMD_LIBXML_ERROR
);
1735 if (find_account(client
->reader
, nreq
[0]) == FALSE
) {
1737 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1742 if (find_elements(client
, client
->reader
, nreq
+ 1, 0) == FALSE
) {
1750 if ((n
= xmlTextReaderCurrentNode(client
->reader
)) == NULL
) {
1751 xml_error
= xmlGetLastError();
1752 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1753 send_error(client
, EPWMD_LIBXML_ERROR
);
1757 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
) {
1758 send_error(client
, EPWMD_ATTR_NOT_FOUND
);
1762 send_to_client(client
, "BEGIN %li\n%s\n", xmlStrlen(a
), a
);
1768 * req[0] - attribute
1769 * req[1] - element path
1772 static gboolean
attribute_set(struct client_s
*client
, gchar
**req
)
1774 gchar
**epath
= NULL
;
1775 xmlErrorPtr xml_error
;
1777 if (!req
|| !req
[0] || !req
[1] || !req
[2]) {
1778 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1783 * Reserved attribute names.
1785 if (g_ascii_strcasecmp(req
[0], "NAME") == 0) {
1787 * Only reserved for the account element. Not the rest of the
1790 if (strchr(req
[1], '\t') == NULL
)
1791 return name_attribute(client
, req
+ 1);
1793 else if (g_ascii_strcasecmp(req
[0], "TARGET") == 0)
1794 return target_attribute(client
, req
+ 1);
1796 if ((epath
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1798 * The first argument may be only an account.
1800 if ((epath
= split_input_line(req
[1], " ", 0)) == NULL
) {
1801 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1806 if (reset_reader(client
->doc
, &client
->reader
) == FALSE
) {
1807 xml_error
= xmlGetLastError();
1808 log_write("%s(%i): %s", __FILE__
, __LINE__
, xml_error
->message
);
1809 send_error(client
, EPWMD_LIBXML_ERROR
);
1813 if (find_account(client
->reader
, epath
[0]) == FALSE
) {
1814 send_error(client
, EPWMD_ELEMENT_NOT_FOUND
);
1819 if ((find_elements(client
, client
->reader
, epath
+1, 0)) == FALSE
)
1824 return add_attribute(client
, req
[0], req
[2]);
1832 * req[1] - attribute name or element path if command is LIST
1833 * req[2] - element path
1834 * req[2] - element path or value
1836 gboolean
attr_command(struct client_s
*client
, gchar
**req
)
1838 if (!req
|| !req
[0] || !req
[1]) {
1839 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1843 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
1844 return attribute_set(client
, req
+1);
1845 if (g_ascii_strcasecmp(req
[0], "GET") == 0)
1846 return attribute_get(client
, req
+1);
1847 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
1848 return attribute_delete(client
, req
+1);
1849 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
1850 return attribute_list(client
, req
+1);
1852 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1857 gboolean
cache_clear(const guchar
*md5filename
, gint which
)
1863 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
1864 memcpy(&f
, p
, sizeof(file_cache_t
));
1867 memset(&f
, 0, sizeof(file_cache_t
));
1868 memcpy(p
, &f
, sizeof(file_cache_t
));
1870 else if (f
.used
== TRUE
&& which
== 1) {
1871 if (memcmp(&f
.filename
, md5filename
, sizeof(f
.filename
)) == 0) {
1872 memset(&f
, 0, sizeof(file_cache_t
));
1873 memcpy(p
, &f
, sizeof(file_cache_t
));
1878 p
+= sizeof(file_cache_t
);
1879 len
+= sizeof(file_cache_t
);
1881 if (len
+ sizeof(file_cache_t
) > cache_size
)
1885 return (which
== 2) ? TRUE
: FALSE
;
1888 gboolean
cache_iscached(const guchar
*md5filename
)
1894 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
1895 memcpy(&f
, p
, sizeof(file_cache_t
));
1897 if (f
.used
== TRUE
) {
1898 if (memcmp(&f
.filename
, md5filename
, sizeof(f
.filename
)) == 0)
1899 return cache_valid_key(f
.key
, sizeof(f
.key
));
1902 p
+= sizeof(file_cache_t
);
1903 len
+= sizeof(file_cache_t
);
1905 if (len
+ sizeof(file_cache_t
) > cache_size
)
1912 gboolean
cache_reset(const guchar
*md5filename
, gint which
)
1917 guchar md5file
[sizeof(f
.filename
)];
1920 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
1921 memcpy(&f
, p
, sizeof(file_cache_t
));
1922 timeout
= f
.timeout
;
1925 if (f
.used
== TRUE
) {
1926 memcpy(&md5file
, f
.filename
, sizeof(md5file
));
1927 memset(&f
, 0, sizeof(file_cache_t
));
1928 memcpy(&f
.filename
, &md5file
, sizeof(md5file
));
1929 f
.timeout
= f
.when
= timeout
;
1931 memcpy(p
, &f
, sizeof(file_cache_t
));
1934 else if (f
.used
== TRUE
&& which
== 1) {
1935 if (memcmp(&f
.filename
, md5filename
, sizeof(f
.filename
)) == 0) {
1936 memcpy(&md5file
, f
.filename
, sizeof(md5file
));
1937 memset(&f
, 0, sizeof(file_cache_t
));
1938 memcpy(&f
.filename
, &md5file
, sizeof(md5file
));
1939 f
.timeout
= f
.when
= timeout
;
1941 memcpy(p
, &f
, sizeof(file_cache_t
));
1946 p
+= sizeof(file_cache_t
);
1947 len
+= sizeof(file_cache_t
);
1949 if (len
+ sizeof(file_cache_t
) > cache_size
)
1953 return (which
== 2) ? TRUE
: FALSE
;
1956 static gboolean
file_exists(const gchar
*filename
)
1959 gchar filebuf
[PATH_MAX
], *p
, *p2
;
1961 p
= get_key_file_string("default", "data_directory");
1962 p2
= expand_homedir(p
);
1964 snprintf(filebuf
, sizeof(filebuf
), "%s/%s", p2
, filename
);
1967 if (access(filebuf
, R_OK
) == -1)
1972 if (st
.st_size
== 0)
1978 gboolean
cache_command(struct client_s
*client
, gchar
**req
)
1984 if (g_ascii_strcasecmp(req
[0], "CLEAR") == 0) {
1986 send_error(client
, EPWMD_COMMAND_SYNTAX
);
1990 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
1992 if (cache_clear(md5file
, 1) == FALSE
) {
1993 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
1999 else if (g_ascii_strcasecmp(req
[0], "CLEARALL") == 0) {
2000 cache_clear(client
->md5file
, 2);
2003 if (g_ascii_strcasecmp(req
[0], "RESET") == 0) {
2005 send_error(client
, EPWMD_COMMAND_SYNTAX
);
2009 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2011 if (cache_reset(md5file
, 1) == FALSE
) {
2012 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
2018 else if (g_ascii_strcasecmp(req
[0], "RESETALL") == 0) {
2019 cache_reset(client
->md5file
, 2);
2022 else if (g_ascii_strcasecmp(req
[0], "ISCACHED") == 0) {
2024 send_error(client
, EPWMD_COMMAND_SYNTAX
);
2028 if (file_exists(req
[1]) == FALSE
) {
2029 send_error(client
, EPWMD_FILE_NOT_FOUND
);
2033 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2035 if (cache_iscached(md5file
) == FALSE
) {
2036 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
2042 else if (g_ascii_strcasecmp(req
[0], "TIMEOUT") == 0) {
2043 if (!req
[1] || !req
[2]) {
2044 send_error(client
, EPWMD_COMMAND_SYNTAX
);
2049 timeout
= strtol(req
[1], &p
, 10);
2051 if (errno
!= 0 || *p
!= 0) {
2052 send_error(client
, EPWMD_COMMAND_SYNTAX
);
2056 if (file_exists(req
[2]) == FALSE
) {
2057 send_error(client
, EPWMD_FILE_NOT_FOUND
);
2061 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[2], strlen(req
[2]));
2063 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2064 send_error(client
, EPWMD_CACHE_NOT_FOUND
);
2071 send_error(client
, EPWMD_COMMAND_SYNTAX
);
2076 gboolean
help_command(struct client_s
*client
, const gchar
*what
)
2080 if (!what
|| !*what
)
2082 "NFO Try 'HELP COMMAND' for command help\n"
2083 "NFO OPEN LIST GET STORE DELETE ATTR CACHE SAVE DUMP QUIT\n";
2084 else if (g_ascii_strcasecmp(what
, "GET") == 0)
2086 "NFO syntax: GET account <TAB> element [<TAB> element ...]\n"
2087 "NFO <account> is the account to work on and <element>\n"
2088 "NFO is the element wanted.\n"
2090 "NFO Example: GET isp <TAB> imap <TAB> port\n"
2091 "NFO GET isp <TAB> username\n";
2092 else if (g_ascii_strcasecmp(what
, "QUIT") == 0)
2094 "NFO syntax: QUIT\n"
2095 "NFO close the connection\n";
2096 else if (g_ascii_strcasecmp(what
, "DELETE") == 0)
2098 "NFO syntax: DELETE account <TAB> element [<TAB> element ...]\n";
2099 else if (g_ascii_strcasecmp(what
, "STORE") == 0)
2101 "NFO syntax: STORE account <TAB> element [<TAB> element ...] <TAB> value\n"
2102 "NFO <account> is the account to work on and <element>\n"
2103 "NFO is the element to create or modify\n"
2105 "NFO Example: STORE isp <TAB> imap <TAB> port <TAB> 993\n"
2106 "NFO STORE isp <TAB> username <TAB> someuser\n";
2107 else if (g_ascii_strcasecmp(what
, "OPEN") == 0)
2109 "NFO syntax: OPEN <filename> [<key>]\n"
2110 "NFO opens a (new) file\n";
2111 else if (g_ascii_strcasecmp(what
, "LIST") == 0)
2113 "NFO syntax: LIST [account]\n"
2114 "NFO shows available accounts or account elements\n";
2115 else if (g_ascii_strcasecmp(what
, "ATTR") == 0)
2117 "NFO syntax: ATTR SET|GET|DELETE|LIST [ATTRIBUTE] arg1 [arg2]\n"
2118 "NFO ATTR SET NAME account value\n"
2119 "NFO ATTR SET TARGET account[<TAB>element[...]] account[<TAB>element[...]\n"
2120 "NFO ATTR SET attribute account[<TAB>element[...]] attribute_value\n"
2121 "NFO ATTR DELETE attribute account[<TAB>element[...]]\n"
2122 "NFO ATTR GET attribute account[<TAB>element[...]]\n"
2123 "NFO ATTR LIST account[<TAB>element[...]]\n";
2124 else if (g_ascii_strcasecmp(what
, "SAVE") == 0)
2126 "NFO syntax: SAVE [<key>]\n"
2127 "NFO save any changes to the opened file using <key>\n";
2128 else if (g_ascii_strcasecmp(what
, "CACHE") == 0)
2130 "NFO syntax: CACHE [ISCACHED <filename>] |\n"
2131 "NFO [RESETALL] | [RESET <filename>] |\n"
2132 "NFO [CLEARALL] | [CLEAR <filename>] |\n"
2133 "NFO [TIMEOUT <seconds> <filename>]\n";
2134 else if (g_ascii_strcasecmp(what
, "DUMP") == 0)
2136 "NFO syntax: DUMP\n"
2137 "NFO shows the in memory XML document\n";
2139 send_error(client
, EPWMD_COMMAND_SYNTAX
);
2143 send_to_client(client
, "%sOK \n", line
);
2147 gboolean
dump_command(struct client_s
*client
)
2152 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2153 send_to_client(client
, "BEGIN %li\n%s", len
, xml
);