Updated the copyright year.
[pwmd.git] / src / xml.c
blob3c1151bd66ab6b7a185a42f529e9ce31949081b6
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2011 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 02110-1301 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <err.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <ctype.h>
31 #include <glib.h>
32 #include <gcrypt.h>
33 #include <libxml/xmlwriter.h>
34 #include "pwmd_error.h"
35 #include "misc.h"
36 #include "xml.h"
38 extern void log_write(const gchar *fmt, ...);
41 * 'element' must be allocated.
43 gboolean is_literal_element(gchar **element)
45 gchar *p;
47 if (!element || !*element)
48 return FALSE;
50 if (*(*element) == '!') {
51 gchar *c;
53 for (p = *element, c = p+1; *c; c++)
54 *p++ = *c;
56 *p = 0;
57 return TRUE;
60 return FALSE;
63 gboolean valid_xml_element(xmlChar *element)
65 gchar *p = (gchar *)element;
67 if (!element || *element == '!')
68 return FALSE;
70 for (; *p; p++) {
71 if (g_ascii_isspace(*p))
72 return FALSE;
75 return TRUE;
78 gpg_error_t new_root_element(xmlDocPtr doc, gchar *name)
80 xmlNodePtr root = xmlDocGetRootElement(doc);
81 xmlNodePtr n;
82 gchar *p = name;
84 if (!p || !root)
85 return EPWMD_LIBXML_ERROR;
87 if (*p == '!')
88 p++;
90 if (!valid_xml_element((xmlChar *)p))
91 return GPG_ERR_INV_VALUE;
93 n = xmlNewNode(NULL, (xmlChar *)"element");
94 n = xmlAddChild(root, n);
95 return add_attribute(n, "_name", p);
98 xmlDocPtr create_dtd()
100 xmlDocPtr doc;
101 xmlTextWriterPtr wr = xmlNewTextWriterDoc(&doc, 0);
103 if (!wr)
104 return NULL;
106 if (xmlTextWriterStartDocument(wr, NULL, NULL, NULL))
107 goto fail;
109 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
110 goto fail;
112 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
113 (xmlChar *)"(element)") == -1)
114 goto fail;
116 xmlTextWriterEndDTDElement(wr);
118 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"element",
119 (xmlChar *)"_name CDATA #REQUIRED") == -1)
120 goto fail;
122 xmlTextWriterEndDTDAttlist(wr);
123 xmlTextWriterEndDTD(wr);
125 if (xmlTextWriterStartElement(wr, (xmlChar *)"pwmd"))
126 goto fail;
128 xmlTextWriterEndElement(wr);
129 xmlTextWriterEndDocument(wr);
130 xmlFreeTextWriter(wr);
131 return doc;
133 fail:
134 xmlTextWriterEndDocument(wr);
135 xmlFreeTextWriter(wr);
136 xmlFreeDoc(doc);
137 return NULL;
140 xmlChar *new_document()
142 xmlChar *xml;
143 gint len;
144 xmlDocPtr doc = create_dtd();
146 if (!doc)
147 return NULL;
149 xmlDocDumpMemory(doc, &xml, &len);
150 xmlFreeDoc(doc);
151 return xml;
154 xmlNodePtr find_element_node(xmlNodePtr node)
156 xmlNodePtr n = node;
158 if (n && n->type == XML_ELEMENT_NODE)
159 return n;
161 for (n = node; n; n = n->next) {
162 if (n->type == XML_ELEMENT_NODE)
163 return n;
166 return NULL;
170 * Lists root element names; the value of the attribute "_name" of an element
171 * "element". If there's a target attribute both literal and non-literal
172 * element names will be added. This is the primary reason why XML entities
173 * cannot be used. There wouldn't be a way to get the literal an non-literal
174 * element paths.
176 gpg_error_t list_root_elements(xmlDocPtr doc, GString **result,
177 gboolean verbose)
179 xmlNodePtr n = NULL;
180 GSList *list = NULL;
181 gint total, i;
182 GString *string;
183 gpg_error_t rc = 0;
185 n = xmlDocGetRootElement(doc);
187 if (!n || !n->children)
188 return GPG_ERR_NO_VALUE;
190 for (n = n->children; n; n = n->next) {
191 xmlAttrPtr a;
192 xmlChar *val, *target;
193 GSList *tlist;
194 gchar *tmp;
196 if (n->type != XML_ELEMENT_NODE)
197 continue;
199 a = xmlHasProp(n, (xmlChar *)"_name");
201 if (!a || !a->children->content)
202 continue;
204 val = xmlNodeGetContent(a->children);
206 if (!val) {
207 rc = GPG_ERR_ENOMEM;
208 goto fail;
211 tmp = g_strdup_printf("!%s%s", (gchar *)val, verbose ? find_element_node(n->children) ? " 1" : " 0" : "");
213 if (!tmp) {
214 xmlFree(val);
215 rc = GPG_ERR_ENOMEM;
216 goto fail;
219 tlist = g_slist_append(list, tmp);
221 if (!tlist) {
222 xmlFree(val);
223 rc = GPG_ERR_ENOMEM;
224 goto fail;
227 list = tlist;
228 target = node_has_attribute(n, (xmlChar *)"target");
230 if (target) {
231 gchar *t;
233 if (verbose) {
234 gchar **req = split_input_line((gchar *)target, "\t", 0);
235 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
237 if (tnode && req[1]) {
238 tnode = find_elements(doc, tnode->children, req+1, &rc,
239 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
242 g_strfreev(req);
243 t = g_strdup_printf("%s%s", (gchar *)val,
244 tnode && find_element_node(tnode->children) ? " 1" : " 0");
246 else
247 t = g_strdup((gchar *)val);
249 if (!t) {
250 xmlFree(val);
251 xmlFree(target);
252 rc = GPG_ERR_ENOMEM;
253 goto fail;
256 tlist = g_slist_append(list, t);
258 if (!tlist) {
259 g_free(t);
260 xmlFree(target);
261 rc = GPG_ERR_ENOMEM;
262 goto fail;
265 list = tlist;
268 xmlFree(val);
269 xmlFree(target);
272 total = g_slist_length(list);
274 if (!total)
275 return GPG_ERR_NO_VALUE;
277 string = g_string_new(NULL);
279 if (!string) {
280 rc = GPG_ERR_ENOMEM;
281 goto fail;
284 for (i = 0; i < total; i++) {
285 gchar *val = g_slist_nth_data(list, i);
287 g_string_append_printf(string, "%s\n", val);
290 string = g_string_truncate(string, string->len - 1);
291 *result = string;
293 fail:
294 total = g_slist_length(list);
296 for (i = 0; i < total; i++)
297 g_free(g_slist_nth_data(list, i));
299 g_slist_free(list);
300 return rc;
304 * Prevents a sibling element past the current element path with the same
305 * element name.
307 static xmlNodePtr find_stop_node(xmlNodePtr node)
309 xmlNodePtr n;
311 for (n = node->parent->children; n; n = n->next) {
312 if (n == node)
313 return n->next;
316 return NULL;
320 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
321 * content but as an element.
323 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
324 gpg_error_t *rc, void *data)
326 gint i;
327 char **req = path;
329 for (i = 0; req[i]; i++) {
330 xmlNodePtr n;
332 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
333 (n && n->parent == node->parent)) {
334 is_literal_element(&req[i]);
336 if (!valid_xml_element((xmlChar *)req[i])) {
337 *rc = GPG_ERR_INV_VALUE;
338 return NULL;
341 //n = xmlNewNode(NULL, (xmlChar *)req[i]);
342 n = xmlNewNode(NULL, (xmlChar *)"element");
344 if (!n) {
345 *rc = GPG_ERR_ENOMEM;
346 return NULL;
349 add_attribute(n, "_name", req[i]);
350 node = xmlAddChild(node, n);
352 if (!node) {
353 *rc = GPG_ERR_ENOMEM;
354 return NULL;
357 else
358 node = n;
361 return node;
364 xmlNodePtr find_text_node(xmlNodePtr node)
366 xmlNodePtr n = node;
368 if (n && n->type == XML_TEXT_NODE)
369 return n;
371 for (n = node; n; n = n->next) {
372 if (n->type == XML_TEXT_NODE)
373 return n;
376 return NULL;
379 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
380 gpg_error_t *rc, void *data)
382 gint i;
383 gchar **req = elements;
385 if (node->type == XML_TEXT_NODE)
386 node = node->parent;
388 xmlChar *a = node_has_attribute(node, (xmlChar *)"_name");
390 if (a)
391 xmlFree(a);
393 for (i = 0; req[i]; i++) {
394 xmlNodePtr n;
396 if (req[i+1]) {
398 * Strip the first '!' if needed. If there's another, it's an
399 * rc. The syntax has already been checked before calling this
400 * function.
402 is_literal_element(&req[i]);
406 * The value of the element tree.
408 if (!req[i+1]) {
409 n = find_text_node(node->children);
411 if (!n)
412 /* Use AddContent here to prevent overwriting any children. */
413 xmlNodeAddContent(node, (xmlChar *)req[i]);
414 else if (n && !*req[i])
415 xmlNodeSetContent(n, NULL);
416 else
417 xmlNodeSetContent(n, (xmlChar *)req[i]);
419 break;
422 n = find_element(node, req[i], find_stop_node(node));
425 * If the found element has the same parent as the current element,
426 * they are siblings and the new element needs to be created as a
427 * child of the current element (node).
429 if (n && n->parent == node->parent)
430 n = NULL;
432 if (!n) {
433 if (!valid_xml_element((xmlChar *)req[i])) {
434 *rc = GPG_ERR_INV_VALUE;
435 return NULL;
438 //n = xmlNewNode(NULL, (xmlChar *)req[i]);
439 n = xmlNewNode(NULL, (xmlChar *)"element");
441 if (!n) {
442 *rc = GPG_ERR_ENOMEM;
443 return NULL;
446 *rc = add_attribute(n, "_name", req[i]);
448 if (*rc)
449 return NULL;
451 node = xmlAddChild(node, n);
453 if (!node) {
454 *rc = GPG_ERR_ENOMEM;
455 return NULL;
458 else
459 node = n;
462 return node;
465 /* The root element is really req[0]. It is need as a pointer in case there is
466 * a target attribute so it can be updated. */
467 xmlNodePtr find_root_element(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
468 gboolean *target, gint recursion_depth, gboolean stop)
470 xmlNodePtr n = xmlDocGetRootElement(doc);
471 gint depth = 0;
472 gchar *root = g_strdup(*req[0]);
473 gboolean literal = is_literal_element(&root);
475 if (!root) {
476 *rc = GPG_ERR_ENOMEM;
477 return NULL;
480 *rc = 0;
481 recursion_depth++;
483 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
484 xmlChar *t = xmlGetNodePath(n);
486 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
487 xmlFree(t);
488 g_free(root);
489 *rc = GPG_ERR_ELOOP;
490 return NULL;
493 while (n) {
494 if (n->type == XML_ELEMENT_NODE) {
495 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
496 n = n->children;
497 depth++;
498 continue;
501 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"element")) {
502 xmlChar *content = node_has_attribute(n, (xmlChar *)"_name");
504 if (!content)
505 continue;
507 if (xmlStrEqual(content, (xmlChar *)root)) {
508 gchar **nreq, **tmp = NULL;
510 if (literal == TRUE) {
511 xmlFree(content);
512 g_free(root);
513 return n;
516 xmlFree(content);
517 content = node_has_attribute(n, (xmlChar *)"target");
519 if (target)
520 *target = TRUE;
522 if (!content || stop) {
523 if (content)
524 xmlFree(content);
526 g_free(root);
527 return n;
530 if (strchr((gchar *)content, '\t')) {
531 nreq = split_input_line((gchar *)content, "\t", 0);
532 xmlFree(content);
534 #if 0
536 * FIXME ENOMEM
538 if (!nreq) {
539 *rc = GPG_ERR_ENOMEM;
540 return NULL;
542 #endif
544 tmp = *req;
545 tmp = strvcatv(nreq, tmp+1);
546 g_strfreev(nreq);
548 if (!tmp) {
549 g_free(root);
550 *rc = GPG_ERR_ENOMEM;
551 return NULL;
554 g_strfreev(*req);
555 *req = tmp;
557 else {
558 if (strv_printf(&tmp, "%s", content) == FALSE) {
559 xmlFree(content);
560 g_free(root);
561 *rc = GPG_ERR_ENOMEM;
562 return NULL;
565 xmlFree(content);
566 nreq = *req;
567 nreq = strvcatv(tmp, nreq+1);
568 g_strfreev(tmp);
570 if (!nreq) {
571 *rc = GPG_ERR_ENOMEM;
572 g_free(root);
573 return NULL;
576 g_strfreev(*req);
577 *req = nreq;
580 g_free(root);
581 n = find_root_element(doc, req, rc, target, recursion_depth, FALSE);
582 return n;
585 xmlFree(content);
589 n = n->next;
592 g_free(root);
593 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
594 return NULL;
597 xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
599 xmlNodePtr n;
601 if (!node || !element)
602 return NULL;
604 for (n = node; n; n = n->next) {
605 if (n->type != XML_ELEMENT_NODE)
606 continue;
608 if (n == stop)
609 break;
611 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
613 if (a && xmlStrEqual(a, (xmlChar *)element)) {
614 xmlFree(a);
615 return n;
618 xmlFree(a);
621 return NULL;
624 xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
626 xmlAttrPtr a = xmlHasProp(n, attr);
628 if (!a)
629 return NULL;
631 if (!a->children || !a->children->content)
632 return NULL;
634 return xmlGetProp(n, attr);
637 static gboolean element_to_literal(gchar **element)
639 gchar *p = g_strdup_printf("!%s", *element);
641 if (!p)
642 return FALSE;
644 g_free(*element);
645 *element = p;
646 return TRUE;
649 /* Resolves elements in 'req' one at a time. It's recursive in case of
650 * "target" attributes. */
651 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
652 gchar **req, gpg_error_t *rc, gboolean *target,
653 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
654 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
655 gboolean is_list_command, gint recursion_depth, void *data, gboolean stop)
657 xmlNodePtr n, last, last_node;
658 gchar **p;
659 gint found = 0;
661 *rc = 0;
662 recursion_depth++;
664 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
665 xmlChar *t = xmlGetNodePath(node);
667 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
668 xmlFree(t);
669 recursion_depth--;
670 *rc = GPG_ERR_ELOOP;
671 return NULL;
674 for (last_node = last = n = node, p = req; *p; p++) {
675 xmlNodePtr tmp;
676 gchar *t = g_strdup(*p);
677 gboolean literal;
679 if (!t) {
680 *rc = GPG_ERR_ENOMEM;
681 return NULL;
684 literal = is_literal_element(&t);
685 n = find_element(last, t, NULL);
686 g_free(t);
688 if (!n) {
689 if (not_found_fn)
690 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
692 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
693 return NULL;
696 last = n->children;
697 last_node = n;
698 found = 1;
700 if (literal == FALSE) {
701 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
702 gchar **nreq = NULL, **nnreq;
704 if (!content) {
705 if (is_list_command == TRUE) {
706 if (element_to_literal(&(*p)) == FALSE) {
707 *rc = GPG_ERR_ENOMEM;
708 return NULL;
712 continue;
715 if (target)
716 *target = TRUE;
718 if (!*(p+1) && stop) {
719 xmlFree(content);
720 return n;
723 if (strchr((gchar *)content, '\t') != NULL) {
724 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
725 xmlFree(content);
726 *rc = GPG_ERR_INV_VALUE;
727 return NULL;
730 else {
731 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
732 xmlFree(content);
733 *rc = GPG_ERR_INV_VALUE;
734 return NULL;
738 xmlFree(content);
739 tmp = find_root_element(doc, &nreq, rc, target, 0, FALSE);
741 if (!tmp) {
742 g_strfreev(nreq);
743 return NULL;
746 if (found_fn) {
747 found_fn(tmp, nreq, rc, p+1, data);
749 if (*rc) {
750 g_strfreev(nreq);
751 return NULL;
755 nnreq = strvcatv(nreq+1, p+1);
756 g_strfreev(nreq);
758 // FIXME ENOMEM
759 if (!nnreq || !*nnreq) {
760 if (nnreq)
761 g_strfreev(nnreq);
763 return tmp;
766 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
767 not_found_fn, is_list_command, recursion_depth, data, stop);
769 if (*(p+1)) {
770 gchar **zz = p+1, **qq = nnreq;
772 if (g_strv_length(nnreq) > g_strv_length(p+1))
773 qq = nnreq+1;
775 for (; *qq && *zz; zz++) {
776 g_free(*zz);
777 *zz = g_strdup(*qq++);
779 if (!*zz) {
780 *rc = GPG_ERR_ENOMEM;
781 n = NULL;
782 break;
787 g_strfreev(nnreq);
788 return n;
792 return n;
795 static gboolean update_element_list(struct element_list_s *elements)
797 gchar *line;
798 GSList *l;
800 if (!elements || !elements->elements)
801 return TRUE;
803 line = g_strjoinv("\t", elements->elements);
805 if (!line)
806 return FALSE;
808 g_strfreev(elements->elements);
809 elements->elements = NULL;
810 l = g_slist_append(elements->list, line);
812 if (!l)
813 return FALSE;
815 elements->list = l;
816 return TRUE;
819 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
820 struct element_list_s *elements)
822 gpg_error_t rc = 0;
823 xmlNodePtr n;
825 for (n = node; n; n = n->next) {
826 xmlChar *target = NULL;
827 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
829 if (!a)
830 continue;
832 if (n->type != XML_ELEMENT_NODE)
833 goto children;
835 if (elements->verbose) {
836 if (strv_printf(&elements->elements, "%s\t!%s%s", elements->prefix, a, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
837 xmlFree(a);
838 return GPG_ERR_ENOMEM;
841 else if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, a) == FALSE) {
842 xmlFree(a);
843 return GPG_ERR_ENOMEM;
846 if (update_element_list(elements) == FALSE) {
847 xmlFree(a);
848 return GPG_ERR_ENOMEM;
851 target = node_has_attribute(n, (xmlChar *)"target");
853 if (target) {
854 gchar *tmp;
855 gchar *save = elements->prefix;
856 gboolean r = elements->resolving;
858 elements->depth++;
860 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
861 xmlChar *t = xmlGetNodePath(n);
863 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
864 xmlFree(t);
865 xmlFree(target);
866 xmlFree(a);
867 return GPG_ERR_ELOOP;
870 if (elements->verbose) {
871 gchar **req = split_input_line((gchar *)target, "\t", 0);
872 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
874 if (!tnode) {
875 g_strfreev(req);
876 xmlFree(a);
877 xmlFree(target);
878 return rc;
881 tnode = find_elements(doc, tnode->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
882 g_strfreev(req);
884 if (!tnode) {
885 xmlFree(a);
886 xmlFree(target);
887 return rc;
890 if (strv_printf(&elements->elements, "%s\t%s%s", elements->prefix, a, find_element_node(tnode->children) ? " 1" : " 0") == FALSE) {
891 xmlFree(a);
892 xmlFree(target);
893 return GPG_ERR_ENOMEM;
896 else if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, a) == FALSE) {
897 xmlFree(a);
898 xmlFree(target);
899 return GPG_ERR_ENOMEM;
902 tmp = g_strjoinv("\t", elements->elements);
904 if (!tmp) {
905 xmlFree(a);
906 xmlFree(target);
907 return GPG_ERR_ENOMEM;
910 if (update_element_list(elements) == FALSE) {
911 g_free(tmp);
912 xmlFree(a);
913 xmlFree(target);
914 return GPG_ERR_ENOMEM;
917 if (elements->recurse) {
918 if (elements->verbose)
919 tmp[strlen(tmp)-2] = 0;
921 elements->prefix = tmp;
922 elements->resolving = TRUE;
923 rc = create_path_list(doc, elements, (gchar *)target);
924 xmlFree(target);
925 elements->resolving = r;
926 elements->depth--;
927 g_free(tmp);
928 elements->prefix = save;
930 if (rc) {
931 xmlFree(a);
932 return rc;
937 children:
938 if (n->children && elements->recurse) {
939 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, a);
940 gchar *save = elements->prefix;
942 if (!tmp) {
943 xmlFree(a);
944 return GPG_ERR_ENOMEM;
947 elements->prefix = tmp;
948 rc = path_list_recurse(doc, n->children, elements);
949 g_free(elements->prefix);
950 elements->prefix = save;
952 if (rc) {
953 xmlFree(a);
954 return rc;
958 xmlFree(a);
961 return rc;
964 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
965 const gchar *value)
967 gchar *buf;
968 gpg_error_t rc;
970 if (name && !xmlSetProp(node, (xmlChar *)name, (xmlChar *)value))
971 return EPWMD_LIBXML_ERROR;
973 if (name && xmlStrEqual((xmlChar *)name, (xmlChar *)"_mtime"))
974 return 0;
976 buf = g_strdup_printf("%li", time(NULL));
977 rc = add_attribute(node, "_mtime", buf);
978 g_free(buf);
979 return rc;
983 * From the element path 'path', find sub-nodes and append them to the list.
985 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
986 gchar *path)
988 gpg_error_t rc;
989 gchar **req, **req_orig;
990 xmlNodePtr n;
991 gboolean a_target = FALSE;
993 req = split_input_line(path, "\t", 0);
995 if (!req) {
996 req = split_input_line(path, " ", 0);
998 if (!req)
999 return GPG_ERR_SYNTAX;
1002 req_orig = g_strdupv(req);
1004 if (!req_orig) {
1005 rc = GPG_ERR_ENOMEM;
1006 goto fail;
1009 n = find_root_element(doc, &req, &rc, &a_target, 0, FALSE);
1011 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1012 rc = 0;
1013 goto fail;
1015 else if (!n)
1016 goto fail;
1018 if (a_target == TRUE) {
1019 g_free(*req);
1020 *req = g_strdup(*req_orig);
1023 if (*(req+1)) {
1024 gboolean e_target = FALSE;
1026 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL, FALSE);
1028 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1029 rc = 0;
1030 goto fail;
1032 else if (!n)
1033 goto fail;
1036 if (!elements->prefix) {
1038 * FIXME
1040 * If any req_orig element contains no target the element should be
1041 * prefixed with the literal character. Not really crucial if the
1042 * client isn't human because child elements are prefixed for the
1043 * current path. But may be confusing if editing by hand.
1045 elements->prefix = g_strjoinv("\t", req_orig);
1047 if (!elements->prefix) {
1048 rc = GPG_ERR_ENOMEM;
1049 goto fail;
1052 if (elements->verbose) {
1053 if (strv_printf(&elements->elements, "%s%s", elements->prefix, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
1054 rc = GPG_ERR_ENOMEM;
1055 goto fail;
1058 else if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
1059 rc = GPG_ERR_ENOMEM;
1060 goto fail;
1063 if (update_element_list(elements) == FALSE) {
1064 rc = GPG_ERR_ENOMEM;
1065 goto fail;
1069 rc = path_list_recurse(doc, n->children, elements);
1071 fail:
1072 if (req_orig)
1073 g_strfreev(req_orig);
1075 g_strfreev(req);
1076 return rc;
1079 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1080 xmlChar *value, xmlBufferPtr *result, gboolean cmd, const xmlChar *attr)
1082 gint i = value ? nodes->nodeNr - 1 : 0;
1083 xmlBufferPtr buf;
1085 buf = xmlBufferCreate();
1087 if (!buf)
1088 return GPG_ERR_ENOMEM;
1090 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1091 xmlNodePtr n = nodes->nodeTab[i];
1092 gpg_error_t rc;
1094 if (!n)
1095 continue;
1097 if (!value && !attr) {
1098 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1099 *result = buf;
1100 return EPWMD_LIBXML_ERROR;
1103 continue;
1106 if (!attr) {
1107 xmlNodeSetContent(n, value);
1108 rc = update_element_mtime(n);
1110 if (rc)
1111 return rc;
1113 else {
1114 if (!cmd)
1115 rc = add_attribute(n, (gchar *)attr, (gchar *)value);
1116 else
1117 rc = delete_attribute(n, attr);
1119 if (rc)
1120 return rc;
1124 *result = buf;
1125 return 0;
1128 /* Common between a version 1 data file and version < 2.12. */
1129 static gpg_error_t convert_root_element(xmlNodePtr n)
1131 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1132 gpg_error_t rc;
1134 if (a) {
1135 xmlFree(a);
1136 xmlChar *t = xmlGetNodePath(n);
1138 log_write(N_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1139 xmlFree(t);
1140 return GPG_ERR_AMBIGUOUS_NAME;
1143 a = xmlGetProp(n, (xmlChar *)"name");
1145 if (a) {
1146 rc = add_attribute(n, "_name", (gchar *)a);
1147 xmlFree(a);
1149 if (rc)
1150 return rc;
1152 rc = delete_attribute(n, (xmlChar *)"name");
1154 if (rc)
1155 return rc;
1157 xmlNodeSetName(n, (xmlChar *)"element");
1160 return 0;
1163 /* For a version 1 data file. */
1164 static gpg_error_t convert_elements_recurse_v1(xmlNodePtr n)
1166 for (; n; n = n->next) {
1167 gpg_error_t rc;
1169 if (n->type == XML_ELEMENT_NODE &&
1170 !xmlStrEqual(n->name, (xmlChar *)"_element")) {
1171 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1173 if (a) {
1174 xmlFree(a);
1175 xmlChar *t = xmlGetNodePath(n);
1177 log_write(N_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1178 xmlFree(t);
1179 return GPG_ERR_AMBIGUOUS_NAME;
1182 rc = add_attribute(n, "_name", (gchar *)n->name);
1184 if (rc)
1185 return rc;
1187 xmlNodeSetName(n, (xmlChar *)"element");
1190 rc = convert_elements_recurse_v1(n->children);
1192 if (rc)
1193 return rc;
1196 return 0;
1199 /* Updates the DTD and renames the root "accounts" and "account" elements from
1200 * a version 1 data file. */
1201 gpg_error_t convert_xml(gchar **xml, goffset *len)
1203 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1204 xmlDocPtr doc, new = NULL;
1205 xmlNodePtr n;
1207 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1209 if (!doc)
1210 return EPWMD_LIBXML_ERROR;
1212 gcry_free(*xml);
1213 *xml = NULL;
1214 n = xmlDocGetRootElement(doc);
1215 xmlNodeSetName(n, (xmlChar *)"pwmd");
1217 for (n = n->children; n; n = n->next) {
1218 if (xmlStrEqual(n->name, (xmlChar *)"account")) {
1219 rc = convert_root_element(n);
1221 if (rc) {
1222 xmlFreeDoc(doc);
1223 return rc;
1226 rc = convert_elements_recurse_v1(n->children);
1228 if (rc) {
1229 xmlFreeDoc(doc);
1230 return rc;
1235 new = create_dtd();
1237 if (!new)
1238 goto fail;
1240 n = xmlDocGetRootElement(doc);
1241 xmlDocSetRootElement(new, n);
1242 xmlDocDumpMemory(new, (xmlChar **)xml, (gint *)len);
1243 xmlDocSetRootElement(new, xmlCopyNode(n, 0));
1244 rc = 0;
1246 fail:
1247 if (new)
1248 xmlFreeDoc(new);
1250 xmlFreeDoc(doc);
1251 return rc;
1254 gpg_error_t delete_attribute(xmlNodePtr n, const xmlChar *name)
1256 xmlAttrPtr a;
1258 if ((a = xmlHasProp(n, name)) == NULL)
1259 return GPG_ERR_NOT_FOUND;
1261 if (xmlRemoveProp(a) == -1)
1262 return EPWMD_LIBXML_ERROR;
1264 return update_element_mtime(n);
1267 static gpg_error_t convert_elements_recurse(xmlDocPtr doc, xmlNodePtr n,
1268 guint depth)
1270 gpg_error_t rc;
1272 depth++;
1274 for (n = n->children; n; n = n->next) {
1275 if (n->type == XML_ELEMENT_NODE) {
1276 xmlChar *a = NULL;
1278 if (depth > 1) {
1279 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1280 xmlChar *t = xmlGetNodePath(n);
1282 log_write(N_("An existing \"element\" already exists. Please rename this element before converting. Path is: %s"), t);
1283 xmlFree(t);
1284 return GPG_ERR_AMBIGUOUS_NAME;
1287 a = xmlGetProp(n, (xmlChar *)"_name");
1289 if (a) {
1290 xmlFree(a);
1291 xmlChar *t = xmlGetNodePath(n);
1293 log_write(N_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1294 xmlFree(t);
1295 return GPG_ERR_AMBIGUOUS_NAME;
1298 xmlChar *tmp = xmlStrdup(n->name);
1300 if (!tmp)
1301 return GPG_ERR_ENOMEM;
1303 xmlNodeSetName(n, (xmlChar *)"element");
1304 rc = add_attribute(n, "_name", (gchar *)tmp);
1305 xmlFree(tmp);
1307 if (rc)
1308 return rc;
1310 else {
1311 rc = convert_root_element(n);
1313 if (rc)
1314 return rc;
1318 if (n->children) {
1319 rc = convert_elements_recurse(doc, n, depth);
1321 if (rc)
1322 return rc;
1326 return 0;
1329 /* Renames ALL elements to the new "element" name. Existing element names are
1330 * stored as an attribute "_name". This was introduced in pwmd 2.12 so
1331 * elements can contain common characters that the XML parser barfs on (an
1332 * email address for example. */
1333 gpg_error_t convert_elements(xmlDocPtr doc)
1335 xmlNodePtr n = xmlDocGetRootElement(doc);
1336 gpg_error_t rc;
1338 log_write(N_("Converting pre 2.12 data file..."));
1339 rc = convert_elements_recurse(doc, n, 0);
1341 if (!rc)
1342 log_write(N_("Finished converting. Please SAVE to update."));
1344 return rc;
1347 gpg_error_t validate_import(xmlNodePtr node)
1349 gpg_error_t rc;
1351 if (!node)
1352 return 0;
1354 for (xmlNodePtr n = node; n; n = n->next) {
1355 if (n->type == XML_ELEMENT_NODE) {
1356 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1357 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1359 if (!a) {
1360 xmlChar *t = xmlGetNodePath(n);
1362 log_write(N_("Missing attribute '_name' at %s."), t);
1363 xmlFree(t);
1364 return GPG_ERR_INV_VALUE;
1367 if (!valid_xml_element(a)) {
1368 xmlChar *t = xmlGetNodePath(n);
1370 log_write(N_("'%s' is not a valid element name at %s."), a, t);
1371 xmlFree(a);
1372 xmlFree(t);
1373 return GPG_ERR_INV_VALUE;
1376 xmlFree(a);
1378 else {
1379 xmlChar *t = xmlGetNodePath(n);
1381 log_write(N_("Warning: unknown element '%s' at %s. Ignoring."), n->name, t);
1382 xmlFree(t);
1383 continue;
1387 if (n->children) {
1388 rc = validate_import(n->children);
1390 if (rc)
1391 return rc;
1395 return rc;
1398 gpg_error_t update_element_mtime(xmlNodePtr n)
1400 return add_attribute(n, NULL, NULL);
1403 gpg_error_t unlink_node(xmlNodePtr n)
1405 gpg_error_t rc = 0;
1407 if (!n)
1408 return rc;
1410 if (n->parent)
1411 rc = update_element_mtime(n->parent);
1413 xmlUnlinkNode(n);
1414 return rc;