Set the target parameter to find_root_element() and find_elements() after
[pwmd.git] / src / xml.c
blobc779129a0706c7e1eb00afc9a57443da017efcd1
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2010 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 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <gcrypt.h>
30 #include <libxml/xmlwriter.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #include "pwmd_error.h"
37 #include "misc.h"
38 #include "xml.h"
40 static xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop);
41 static xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr);
44 * 'element' must be allocated.
46 gboolean is_literal_element(gchar **element)
48 gchar *p;
50 if (!element || !*element)
51 return FALSE;
53 if (*(*element) == '!') {
54 gchar *c;
56 for (p = *element, c = p+1; *c; c++)
57 *p++ = *c;
59 *p = 0;
60 return TRUE;
63 return FALSE;
67 * Fails if 'element' begins with punctuation or digit or contains whitespace.
69 * I'm not sure about using g_unichar_isspace() rather than isspace()?
71 gboolean valid_xml_element(xmlChar *element)
73 gunichar c;
74 glong len;
75 gchar *p = (gchar *)element;
77 if (!element || !*element)
78 return FALSE;
80 len = g_utf8_strlen(p, -1) - 1;
81 c = g_utf8_get_char(p++);
83 if (g_unichar_ispunct(c) == TRUE || g_unichar_isdigit(c) == TRUE ||
84 g_unichar_isspace(c) == TRUE)
85 return FALSE;
87 while (*p && len--) {
88 c = g_utf8_get_char(p++);
90 if (g_unichar_isspace(c))
91 return FALSE;
94 return TRUE;
97 gpg_error_t new_root_element(xmlDocPtr doc, gchar *name)
99 xmlNodePtr root = xmlDocGetRootElement(doc);
100 xmlAttrPtr a;
101 xmlNodePtr n;
102 gchar *p = name;
104 if (!p || !root)
105 return EPWMD_LIBXML_ERROR;
107 if (*p == '!')
108 p++;
110 if (!valid_xml_element((xmlChar *)p))
111 return EPWMD_INVALID_ELEMENT;
113 n = xmlNewNode(NULL, (xmlChar *)"root");
114 n = xmlAddChild(root, n);
115 a = xmlNewProp(n, (xmlChar *)"name", (xmlChar *)p);
116 return 0;
119 xmlDocPtr create_dtd()
121 xmlDocPtr doc;
122 xmlTextWriterPtr wr = xmlNewTextWriterDoc(&doc, 0);
124 if (!wr)
125 return NULL;
127 if (xmlTextWriterStartDocument(wr, NULL, NULL, NULL))
128 goto fail;
130 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
131 goto fail;
133 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
134 (xmlChar *)"(root)") == -1)
135 goto fail;
137 xmlTextWriterEndDTDElement(wr);
139 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"root",
140 (xmlChar *)"name CDATA #REQUIRED") == -1)
141 goto fail;
143 xmlTextWriterEndDTDAttlist(wr);
144 xmlTextWriterEndDTD(wr);
146 if (xmlTextWriterStartElement(wr, (xmlChar *)"pwmd"))
147 goto fail;
149 xmlTextWriterEndElement(wr);
150 xmlTextWriterEndDocument(wr);
151 xmlFreeTextWriter(wr);
152 return doc;
154 fail:
155 xmlTextWriterEndDocument(wr);
156 xmlFreeTextWriter(wr);
157 xmlFreeDoc(doc);
158 return NULL;
161 xmlChar *new_document()
163 xmlChar *xml;
164 gint len;
165 xmlDocPtr doc = create_dtd();
167 if (!doc)
168 return NULL;
170 xmlDocDumpMemory(doc, &xml, &len);
171 xmlFreeDoc(doc);
172 return xml;
176 * Lists root element names; the value of the attribute "name" of an element
177 * "root". If there's a target attribute both literal and non-literal element
178 * names will be added. This is the primary reason why XML entities cannot be
179 * used. There wouldn't be a way to get the literal an non-literal element
180 * paths.
182 gpg_error_t list_root_elements(xmlDocPtr doc, GString **result)
184 xmlNodePtr n = NULL;
185 GSList *list = NULL;
186 gint total, i;
187 GString *string;
188 gpg_error_t rc = 0;
190 n = xmlDocGetRootElement(doc);
192 if (!n || !n->children)
193 return EPWMD_EMPTY_ELEMENT;
195 for (n = n->children; n; n = n->next) {
196 xmlAttrPtr a;
197 xmlChar *val, *target;
198 GSList *tlist;
199 gchar *tmp;
201 if (n->type != XML_ELEMENT_NODE)
202 continue;
204 a = xmlHasProp(n, (xmlChar *)"name");
206 if (!a || !a->children->content)
207 continue;
209 val = xmlNodeGetContent(a->children);
211 if (!val) {
212 rc = gpg_error_from_errno(ENOMEM);
213 goto fail;
216 tmp = g_strdup_printf("!%s", (gchar *)val);
218 if (!tmp) {
219 xmlFree(val);
220 rc = gpg_error_from_errno(ENOMEM);
221 goto fail;
224 tlist = g_slist_append(list, tmp);
226 if (!tlist) {
227 xmlFree(val);
228 rc = gpg_error_from_errno(ENOMEM);
229 goto fail;
232 list = tlist;
233 target = node_has_attribute(n, (xmlChar *)"target");
235 if (target) {
236 gchar *t = g_strdup((gchar *)val);
238 if (!t) {
239 xmlFree(val);
240 xmlFree(target);
241 rc = gpg_error_from_errno(ENOMEM);
242 goto fail;
245 tlist = g_slist_append(list, t);
247 if (!tlist) {
248 g_free(t);
249 xmlFree(target);
250 rc = gpg_error_from_errno(ENOMEM);
251 goto fail;
254 list = tlist;
257 xmlFree(val);
258 xmlFree(target);
261 total = g_slist_length(list);
263 if (!total)
264 return EPWMD_EMPTY_ELEMENT;
266 string = g_string_new(NULL);
268 if (!string) {
269 rc = gpg_error_from_errno(ENOMEM);
270 goto fail;
273 for (i = 0; i < total; i++) {
274 gchar *val = g_slist_nth_data(list, i);
276 g_string_append_printf(string, "%s\n", val);
279 string = g_string_truncate(string, string->len - 1);
280 *result = string;
282 fail:
283 total = g_slist_length(list);
285 for (i = 0; i < total; i++)
286 g_free(g_slist_nth_data(list, i));
288 g_slist_free(list);
289 return rc;
293 * Prevents a sibling element past the current element path with the same
294 * element name.
296 static xmlNodePtr find_stop_node(xmlNodePtr node)
298 xmlNodePtr n;
300 for (n = node->parent->children; n; n = n->next) {
301 if (n == node)
302 return n->next;
305 return NULL;
309 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
310 * content but as an element.
312 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
313 gpg_error_t *rc, void *data)
315 gint i;
316 char **req = path;
318 if (xmlStrEqual(node->name, (xmlChar *)*req))
319 req++;
321 for (i = 0; req[i]; i++) {
322 xmlNodePtr n;
324 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
325 (n && n->parent == node->parent)) {
326 is_literal_element(&req[i]);
328 if (!valid_xml_element((xmlChar *)req[i])) {
329 *rc = EPWMD_INVALID_ELEMENT;
330 return NULL;
333 n = xmlNewNode(NULL, (xmlChar *)req[i]);
335 if (!n) {
336 *rc = gpg_error_from_errno(ENOMEM);
337 return NULL;
340 node = xmlAddChild(node, n);
342 if (!node) {
343 *rc = gpg_error_from_errno(ENOMEM);
344 return NULL;
347 else
348 node = n;
351 return node;
354 xmlNodePtr find_text_node(xmlNodePtr node)
356 xmlNodePtr n = node;
358 if (n && n->type == XML_TEXT_NODE)
359 return n;
361 for (n = node; n; n = n->next) {
362 if (n->type == XML_TEXT_NODE)
363 return n;
366 return NULL;
369 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
370 gpg_error_t *rc, void *data)
372 gint i;
373 gchar **req = elements;
375 if (node->type == XML_TEXT_NODE)
376 node = node->parent;
378 if (node->name && xmlStrEqual(node->name, (xmlChar *)*req))
379 req++;
381 for (i = 0; req[i]; i++) {
382 xmlNodePtr n;
384 if (req[i+1]) {
386 * Strip the first '!' if needed. If there's another, it's an
387 * rc. The syntax has already been checked before calling this
388 * function.
390 is_literal_element(&req[i]);
394 * The value of the element tree.
396 if (!req[i+1]) {
397 n = find_text_node(node->children);
399 if (!n)
400 /* Use AddContent here to prevent overwriting any children. */
401 xmlNodeAddContent(node, (xmlChar *)req[i]);
402 else if (n && !*req[i])
403 xmlNodeSetContent(n, NULL);
404 else
405 xmlNodeSetContent(n, (xmlChar *)req[i]);
407 break;
410 n = find_element(node, req[i], find_stop_node(node));
413 * If the found element has the same parent as the current element,
414 * they are siblings and the new element needs to be created as a
415 * child of the current element (node).
417 if (n && n->parent == node->parent)
418 n = NULL;
420 if (!n) {
421 if (!valid_xml_element((xmlChar *)req[i])) {
422 *rc = EPWMD_INVALID_ELEMENT;
423 return NULL;
426 n = xmlNewNode(NULL, (xmlChar *)req[i]);
428 if (!n) {
429 *rc = gpg_error_from_errno(ENOMEM);
430 return NULL;
433 node = xmlAddChild(node, n);
435 if (!node) {
436 *rc = gpg_error_from_errno(ENOMEM);
437 return NULL;
440 else
441 node = n;
444 return node;
447 /* The root element is really req[0]. It is need as a pointer in case there is
448 * a target attribute so it can be updated. */
449 xmlNodePtr find_root_element(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
450 gboolean *target, gint recursion_depth, gboolean stop)
452 xmlNodePtr n = xmlDocGetRootElement(doc);
453 gint depth = 0;
454 gchar *root = g_strdup(*req[0]);
455 gboolean literal = is_literal_element(&root);
457 if (!root) {
458 *rc = gpg_error_from_errno(ENOMEM);
459 return NULL;
462 *rc = 0;
463 recursion_depth++;
465 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
466 xmlChar *t = xmlGetNodePath(n);
468 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
469 xmlFree(t);
470 g_free(root);
471 *rc = EPWMD_LOOP;
472 return NULL;
475 while (n) {
476 if (n->type == XML_ELEMENT_NODE) {
477 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
478 n = n->children;
479 depth++;
480 continue;
483 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"root")) {
484 xmlChar *content = node_has_attribute(n, (xmlChar *)"name");
486 if (!content)
487 continue;
489 if (xmlStrEqual(content, (xmlChar *)root)) {
490 gchar **nreq, **tmp = NULL;
492 if (literal == TRUE) {
493 xmlFree(content);
494 g_free(root);
495 return n;
498 xmlFree(content);
499 content = node_has_attribute(n, (xmlChar *)"target");
501 if (target)
502 *target = TRUE;
504 if (!content || stop) {
505 if (content)
506 xmlFree(content);
508 g_free(root);
509 return n;
512 if (strchr((gchar *)content, '\t')) {
513 nreq = split_input_line((gchar *)content, "\t", 0);
514 xmlFree(content);
516 #if 0
518 * FIXME ENOMEM
520 if (!nreq) {
521 *rc = gpg_error_from_errno(ENOMEM);
522 return NULL;
524 #endif
526 tmp = *req;
527 tmp = strvcatv(nreq, tmp+1);
528 g_strfreev(nreq);
530 if (!tmp) {
531 g_free(root);
532 *rc = gpg_error_from_errno(ENOMEM);
533 return NULL;
536 g_strfreev(*req);
537 *req = tmp;
539 else {
540 if (strv_printf(&tmp, "%s", content) == FALSE) {
541 xmlFree(content);
542 g_free(root);
543 *rc = gpg_error_from_errno(ENOMEM);
544 return NULL;
547 xmlFree(content);
548 nreq = *req;
549 nreq = strvcatv(tmp, nreq+1);
550 g_strfreev(tmp);
552 if (!nreq) {
553 *rc = gpg_error_from_errno(ENOMEM);
554 g_free(root);
555 return NULL;
558 g_strfreev(*req);
559 *req = nreq;
562 g_free(root);
563 n = find_root_element(doc, req, rc, target, recursion_depth, FALSE);
564 return n;
567 xmlFree(content);
571 n = n->next;
574 g_free(root);
575 *rc = EPWMD_ELEMENT_NOT_FOUND;
576 return NULL;
579 static xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
581 xmlNodePtr n;
583 if (!node || !element)
584 return NULL;
586 for (n = node; n; n = n->next) {
587 if (n->type != XML_ELEMENT_NODE)
588 continue;
590 if (n == stop)
591 break;
593 if (xmlStrEqual(n->name, (xmlChar *)element))
594 return n;
597 return NULL;
600 static xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
602 xmlAttrPtr a = xmlHasProp(n, attr);
604 if (!a)
605 return NULL;
607 if (!a->children || !a->children->content)
608 return NULL;
610 return xmlGetProp(n, attr);
613 static gboolean element_to_literal(gchar **element)
615 gchar *p = g_strdup_printf("!%s", *element);
617 if (!p)
618 return FALSE;
620 g_free(*element);
621 *element = p;
622 return TRUE;
625 /* Resolves elements in 'req' one at a time. It's recursive in case of
626 * "target" attributes. */
627 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
628 gchar **req, gpg_error_t *rc, gboolean *target,
629 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
630 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
631 gboolean is_list_command, gint recursion_depth, void *data, gboolean stop)
633 xmlNodePtr n, last, last_node;
634 gchar **p;
635 gint found = 0;
637 *rc = 0;
638 recursion_depth++;
640 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
641 xmlChar *t = xmlGetNodePath(node);
643 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
644 xmlFree(t);
645 recursion_depth--;
646 *rc = EPWMD_LOOP;
647 return NULL;
650 for (last_node = last = n = node, p = req; *p; p++) {
651 xmlNodePtr tmp;
652 gchar *t = g_strdup(*p);
653 gboolean literal;
655 if (!t) {
656 *rc = gpg_error_from_errno(ENOMEM);
657 return NULL;
660 literal = is_literal_element(&t);
661 n = find_element(last, t, NULL);
662 g_free(t);
664 if (!n) {
665 if (not_found_fn)
666 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
668 *rc = EPWMD_ELEMENT_NOT_FOUND;
669 return NULL;
672 last = n->children;
673 last_node = n;
674 found = 1;
676 if (literal == FALSE) {
677 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
678 gchar **nreq = NULL, **nnreq;
680 if (!content) {
681 if (is_list_command == TRUE) {
682 if (element_to_literal(&(*p)) == FALSE) {
683 *rc = gpg_error_from_errno(ENOMEM);
684 return NULL;
688 continue;
691 if (target)
692 *target = TRUE;
694 if (!*(p+1) && stop) {
695 xmlFree(content);
696 return n;
699 if (strchr((gchar *)content, '\t') != NULL) {
700 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
701 xmlFree(content);
702 *rc = EPWMD_INVALID_ELEMENT;
703 return NULL;
706 else {
707 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
708 xmlFree(content);
709 *rc = EPWMD_INVALID_ELEMENT;
710 return NULL;
714 xmlFree(content);
715 tmp = find_root_element(doc, &nreq, rc, target, 0, FALSE);
717 if (!tmp) {
718 g_strfreev(nreq);
719 return NULL;
722 if (found_fn) {
723 found_fn(tmp, nreq, rc, p+1, data);
725 if (*rc) {
726 g_strfreev(nreq);
727 return NULL;
731 nnreq = strvcatv(nreq+1, p+1);
732 g_strfreev(nreq);
734 // FIXME ENOMEM
735 if (!nnreq || !*nnreq) {
736 if (nnreq)
737 g_strfreev(nnreq);
739 return tmp;
742 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
743 not_found_fn, is_list_command, recursion_depth, data, stop);
745 if (*(p+1)) {
746 gchar **zz = p+1, **qq = nnreq;
748 if (g_strv_length(nnreq) > g_strv_length(p+1))
749 qq = nnreq+1;
751 for (; *qq && *zz; zz++) {
752 g_free(*zz);
753 *zz = g_strdup(*qq++);
755 if (!*zz) {
756 *rc = gpg_error_from_errno(ENOMEM);
757 n = NULL;
758 break;
763 g_strfreev(nnreq);
764 return n;
768 return n;
771 static gboolean update_element_list(struct element_list_s *elements)
773 gchar *line;
774 GSList *l;
776 if (!elements || !elements->elements)
777 return TRUE;
779 line = g_strjoinv("\t", elements->elements);
781 if (!line)
782 return FALSE;
784 g_strfreev(elements->elements);
785 elements->elements = NULL;
786 l = g_slist_append(elements->list, line);
788 if (!l)
789 return FALSE;
791 elements->list = l;
792 return TRUE;
795 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
796 struct element_list_s *elements)
798 gpg_error_t rc = 0;
799 xmlNodePtr n;
801 for (n = node; n; n = n->next) {
802 xmlChar *target = NULL;
804 if (n->type != XML_ELEMENT_NODE)
805 goto children;
807 if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, n->name) == FALSE)
808 return gpg_err_code_from_errno(ENOMEM);
810 if (update_element_list(elements) == FALSE)
811 return gpg_err_code_from_errno(ENOMEM);
813 target = node_has_attribute(n, (xmlChar *)"target");
815 if (target) {
816 gchar *tmp;
817 gchar *save = elements->prefix;
818 gboolean r = elements->resolving;
820 elements->depth++;
822 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
823 xmlChar *t = xmlGetNodePath(n);
824 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
825 xmlFree(t);
826 xmlFree(target);
827 return EPWMD_LOOP;
830 if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, n->name) == FALSE) {
831 xmlFree(target);
832 return gpg_err_code_from_errno(ENOMEM);
835 tmp = g_strjoinv("\t", elements->elements);
837 if (!tmp) {
838 xmlFree(target);
839 return gpg_err_code_from_errno(ENOMEM);
842 if (update_element_list(elements) == FALSE) {
843 xmlFree(target);
844 return gpg_err_code_from_errno(ENOMEM);
847 elements->prefix = tmp;
848 elements->resolving = TRUE;
849 rc = create_path_list(doc, elements, (gchar *)target);
850 xmlFree(target);
851 elements->resolving = r;
852 elements->depth--;
853 g_free(tmp);
854 elements->prefix = save;
856 if (rc)
857 return rc;
860 children:
861 if (n->children) {
862 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, n->name);
863 gchar *save = elements->prefix;
865 if (!tmp)
866 return gpg_err_code_from_errno(ENOMEM);
868 elements->prefix = tmp;
869 rc = path_list_recurse(doc, n->children, elements);
870 g_free(elements->prefix);
871 elements->prefix = save;
873 if (rc)
874 return rc;
878 return rc;
881 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
882 const gchar *value)
884 xmlAttrPtr a;
886 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
887 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
889 if (!a)
890 return EPWMD_LIBXML_ERROR;
892 else
893 xmlNodeSetContent(a->children, (xmlChar *)value);
895 return 0;
898 gpg_error_t update_timestamp(xmlDocPtr doc)
900 xmlNodePtr n = xmlDocGetRootElement(doc);
901 gchar *t = g_strdup_printf("%li", time(NULL));
902 gpg_error_t rc;
904 rc = add_attribute(n, "age", t);
905 g_free(t);
906 return rc;
910 * From the element path 'path', find sub-nodes and append them to the list.
912 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
913 gchar *path)
915 gpg_error_t rc;
916 gchar **req, **req_orig;
917 xmlNodePtr n;
918 gboolean a_target = FALSE;
920 req = split_input_line(path, "\t", 0);
922 if (!req) {
923 req = split_input_line(path, " ", 0);
925 if (!req)
926 return EPWMD_COMMAND_SYNTAX;
929 req_orig = g_strdupv(req);
931 if (!req_orig) {
932 rc = gpg_err_code_from_errno(ENOMEM);
933 goto fail;
936 n = find_root_element(doc, &req, &rc, &a_target, 0, FALSE);
938 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
939 rc = 0;
940 goto fail;
942 else if (!n)
943 goto fail;
945 if (a_target == TRUE) {
946 g_free(*req);
947 *req = g_strdup(*req_orig);
950 if (*(req+1)) {
951 gboolean e_target = FALSE;
953 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL, FALSE);
955 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
956 rc = 0;
957 goto fail;
959 else if (!n)
960 goto fail;
963 if (!elements->prefix) {
965 * FIXME
967 * If any req_orig element contains no target the element should be
968 * prefixed with the literal character. Not really crucial if the
969 * client isn't human because child elements are prefixed for the
970 * current path. But may be confusing if editing by hand.
972 elements->prefix = g_strjoinv("\t", req_orig);
974 if (!elements->prefix) {
975 rc = gpg_err_code_from_errno(ENOMEM);
976 goto fail;
979 if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
980 rc = gpg_err_code_from_errno(ENOMEM);
981 goto fail;
984 if (update_element_list(elements) == FALSE) {
985 rc = gpg_err_code_from_errno(ENOMEM);
986 goto fail;
990 rc = path_list_recurse(doc, n->children, elements);
992 fail:
993 if (req_orig)
994 g_strfreev(req_orig);
996 g_strfreev(req);
997 return rc;
1000 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1001 xmlChar *value, xmlBufferPtr *result)
1003 gint i = value ? nodes->nodeNr - 1 : 0;
1004 xmlBufferPtr buf;
1006 buf = xmlBufferCreate();
1008 if (!buf)
1009 return gpg_err_code_from_errno(ENOMEM);
1011 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1012 xmlNodePtr n = nodes->nodeTab[i];
1014 if (!n)
1015 continue;
1017 if (!value) {
1018 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1019 *result = buf;
1020 return EPWMD_LIBXML_ERROR;
1023 continue;
1026 xmlNodeSetContent(n, value);
1029 *result = buf;
1030 return 0;
1033 /* Updates the DTD and renames the root "accounts" and "account" elements. */
1034 gpg_error_t convert_xml(gchar **xml, goffset *len)
1036 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1037 xmlDocPtr doc, new = NULL;
1038 xmlNodePtr n;
1040 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1042 if (!doc)
1043 return EPWMD_LIBXML_ERROR;
1045 gcry_free(*xml);
1046 *xml = NULL;
1047 n = xmlDocGetRootElement(doc);
1048 xmlNodeSetName(n, (xmlChar *)"pwmd");
1050 for (n = n->children; n; n = n->next) {
1051 if (xmlStrcmp(n->name, (xmlChar *)"account") == 0)
1052 xmlNodeSetName(n, (xmlChar *)"root");
1055 new = create_dtd();
1057 if (!new)
1058 goto fail;
1060 n = xmlDocGetRootElement(doc);
1061 xmlDocSetRootElement(new, n);
1062 xmlDocDumpMemory(new, (xmlChar **)xml, (gint *)len);
1063 xmlDocSetRootElement(new, xmlCopyNode(n, 0));
1064 rc = 0;
1066 fail:
1067 if (new)
1068 xmlFreeDoc(new);
1070 xmlFreeDoc(doc);
1071 return rc;