Fixed a potential memory leak with IMPORT.
[pwmd.git] / src / xml.c
blob8170174752038d77235b790e50fc86e13c45f1ec
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_account(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 account element names. If there's a target attribute both
177 * literal and non-literal element names will be added.
179 gpg_error_t list_accounts(xmlDocPtr doc, GString **result)
181 xmlNodePtr n = NULL;
182 GSList *list = NULL;
183 gint total, i;
184 GString *string;
185 gpg_error_t rc = 0;
187 n = xmlDocGetRootElement(doc);
189 if (!n || !n->children)
190 return EPWMD_EMPTY_ELEMENT;
192 for (n = n->children; n; n = n->next) {
193 xmlAttrPtr a;
194 xmlChar *val, *target;
195 GSList *tlist;
196 gchar *tmp;
198 if (n->type != XML_ELEMENT_NODE)
199 continue;
201 a = xmlHasProp(n, (xmlChar *)"name");
203 if (!a || !a->children->content)
204 continue;
206 val = xmlNodeGetContent(a->children);
208 if (!val) {
209 rc = gpg_error_from_errno(ENOMEM);
210 goto fail;
213 tmp = g_strdup_printf("!%s", (gchar *)val);
215 if (!tmp) {
216 xmlFree(val);
217 rc = gpg_error_from_errno(ENOMEM);
218 goto fail;
221 tlist = g_slist_append(list, tmp);
223 if (!tlist) {
224 xmlFree(val);
225 rc = gpg_error_from_errno(ENOMEM);
226 goto fail;
229 list = tlist;
230 target = node_has_attribute(n, (xmlChar *)"target");
232 if (target) {
233 gchar *t = g_strdup((gchar *)val);
235 if (!t) {
236 xmlFree(val);
237 xmlFree(target);
238 rc = gpg_error_from_errno(ENOMEM);
239 goto fail;
242 tlist = g_slist_append(list, t);
244 if (!tlist) {
245 g_free(t);
246 xmlFree(target);
247 rc = gpg_error_from_errno(ENOMEM);
248 goto fail;
251 list = tlist;
254 xmlFree(val);
255 xmlFree(target);
258 total = g_slist_length(list);
260 if (!total)
261 return EPWMD_EMPTY_ELEMENT;
263 string = g_string_new(NULL);
265 if (!string) {
266 rc = gpg_error_from_errno(ENOMEM);
267 goto fail;
270 for (i = 0; i < total; i++) {
271 gchar *val = g_slist_nth_data(list, i);
273 g_string_append_printf(string, "%s\n", val);
276 string = g_string_truncate(string, string->len - 1);
277 *result = string;
279 fail:
280 total = g_slist_length(list);
282 for (i = 0; i < total; i++)
283 g_free(g_slist_nth_data(list, i));
285 g_slist_free(list);
286 return rc;
290 * Prevents a sibling element past the current element path with the same
291 * element name.
293 static xmlNodePtr find_stop_node(xmlNodePtr node)
295 xmlNodePtr n;
297 for (n = node->parent->children; n; n = n->next) {
298 if (n == node)
299 return n->next;
302 return NULL;
306 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
307 * content but as an element.
309 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
310 gpg_error_t *rc, void *data)
312 gint i;
313 char **req = path;
315 if (xmlStrEqual(node->name, (xmlChar *)*req))
316 req++;
318 for (i = 0; req[i]; i++) {
319 xmlNodePtr n;
321 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
322 (n && n->parent == node->parent)) {
323 is_literal_element(&req[i]);
325 if (!valid_xml_element((xmlChar *)req[i])) {
326 *rc = EPWMD_INVALID_ELEMENT;
327 return NULL;
330 n = xmlNewNode(NULL, (xmlChar *)req[i]);
332 if (!n) {
333 *rc = gpg_error_from_errno(ENOMEM);
334 return NULL;
337 node = xmlAddChild(node, n);
339 if (!node) {
340 *rc = gpg_error_from_errno(ENOMEM);
341 return NULL;
344 else
345 node = n;
348 return node;
351 xmlNodePtr find_text_node(xmlNodePtr node)
353 xmlNodePtr n = node;
355 if (n && n->type == XML_TEXT_NODE)
356 return n;
358 for (n = node; n; n = n->next) {
359 if (n->type == XML_TEXT_NODE)
360 return n;
363 return NULL;
366 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
367 gpg_error_t *rc, void *data)
369 gint i;
370 gchar **req = elements;
372 if (node->type == XML_TEXT_NODE)
373 node = node->parent;
375 if (node->name && xmlStrEqual(node->name, (xmlChar *)*req))
376 req++;
378 for (i = 0; req[i]; i++) {
379 xmlNodePtr n;
381 if (req[i+1]) {
383 * Strip the first '!' if needed. If there's another, it's an
384 * rc. The syntax has already been checked before calling this
385 * function.
387 is_literal_element(&req[i]);
391 * The value of the element tree.
393 if (!req[i+1]) {
394 n = find_text_node(node->children);
396 if (!n)
397 /* Use AddContent here to prevent overwriting any children. */
398 xmlNodeAddContent(node, (xmlChar *)req[i]);
399 else if (n && !*req[i])
400 xmlNodeSetContent(n, NULL);
401 else
402 xmlNodeSetContent(n, (xmlChar *)req[i]);
404 break;
407 n = find_element(node, req[i], find_stop_node(node));
410 * If the found element has the same parent as the current element,
411 * they are siblings and the new element needs to be created as a
412 * child of the current element (node).
414 if (n && n->parent == node->parent)
415 n = NULL;
417 if (!n) {
418 if (!valid_xml_element((xmlChar *)req[i])) {
419 *rc = EPWMD_INVALID_ELEMENT;
420 return NULL;
423 n = xmlNewNode(NULL, (xmlChar *)req[i]);
425 if (!n) {
426 *rc = gpg_error_from_errno(ENOMEM);
427 return NULL;
430 node = xmlAddChild(node, n);
432 if (!node) {
433 *rc = gpg_error_from_errno(ENOMEM);
434 return NULL;
437 else
438 node = n;
441 return node;
444 xmlNodePtr find_account(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
445 gboolean *target, gint recursion_depth, gboolean stop)
447 xmlNodePtr n = xmlDocGetRootElement(doc);
448 gint depth = 0;
449 gchar *account = g_strdup(*req[0]);
450 gboolean literal = is_literal_element(&account);
452 if (!account) {
453 *rc = gpg_error_from_errno(ENOMEM);
454 return NULL;
457 *rc = 0;
458 recursion_depth++;
460 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
461 xmlChar *t = xmlGetNodePath(n);
463 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
464 xmlFree(t);
465 *rc = EPWMD_LOOP;
466 return NULL;
469 while (n) {
470 if (n->type == XML_ELEMENT_NODE) {
471 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
472 n = n->children;
473 depth++;
474 continue;
477 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"root")) {
478 xmlChar *content = node_has_attribute(n, (xmlChar *)"name");
480 if (!content)
481 continue;
483 if (xmlStrEqual(content, (xmlChar *)account)) {
484 gchar **nreq, **tmp = NULL;
486 if (literal == TRUE) {
487 xmlFree(content);
488 g_free(account);
489 return n;
492 xmlFree(content);
493 content = node_has_attribute(n, (xmlChar *)"target");
495 if (!content || stop) {
496 if (content)
497 xmlFree(content);
499 g_free(account);
500 return n;
503 if (strchr((gchar *)content, '\t')) {
504 nreq = split_input_line((gchar *)content, "\t", 0);
505 xmlFree(content);
507 #if 0
509 * FIXME ENOMEM
511 if (!nreq) {
512 *rc = gpg_error_from_errno(ENOMEM);
513 return NULL;
515 #endif
517 tmp = *req;
518 tmp = strvcatv(nreq, tmp+1);
519 g_strfreev(nreq);
521 if (!tmp) {
522 g_free(account);
523 *rc = gpg_error_from_errno(ENOMEM);
524 return NULL;
527 g_strfreev(*req);
528 *req = tmp;
530 else {
531 if (strv_printf(&tmp, "%s", content) == FALSE) {
532 xmlFree(content);
533 g_free(account);
534 *rc = gpg_error_from_errno(ENOMEM);
535 return NULL;
538 xmlFree(content);
539 nreq = *req;
540 nreq = strvcatv(tmp, nreq+1);
541 g_strfreev(tmp);
543 if (!nreq) {
544 *rc = gpg_error_from_errno(ENOMEM);
545 g_free(account);
546 return NULL;
549 g_strfreev(*req);
550 *req = nreq;
553 if (target)
554 *target = TRUE;
556 g_free(account);
557 n = find_account(doc, req, rc, target, recursion_depth, FALSE);
558 return n;
561 xmlFree(content);
565 n = n->next;
568 g_free(account);
569 *rc = EPWMD_ELEMENT_NOT_FOUND;
570 return NULL;
573 static xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
575 xmlNodePtr n;
577 if (!node || !element)
578 return NULL;
580 for (n = node; n; n = n->next) {
581 if (n->type != XML_ELEMENT_NODE)
582 continue;
584 if (n == stop)
585 break;
587 if (xmlStrEqual(n->name, (xmlChar *)element))
588 return n;
591 return NULL;
594 static xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
596 xmlAttrPtr a = xmlHasProp(n, attr);
598 if (!a)
599 return NULL;
601 if (!a->children || !a->children->content)
602 return NULL;
604 return xmlGetProp(n, attr);
607 static gboolean element_to_literal(gchar **element)
609 gchar *p = g_strdup_printf("!%s", *element);
611 if (!p)
612 return FALSE;
614 g_free(*element);
615 *element = p;
616 return TRUE;
619 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
620 gchar **req, gpg_error_t *rc, gboolean *target,
621 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
622 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
623 gboolean is_list_command, gint recursion_depth, void *data)
625 xmlNodePtr n, last, last_node;
626 gchar **p;
627 gint found = 0;
629 *rc = 0;
630 recursion_depth++;
632 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
633 xmlChar *t = xmlGetNodePath(node);
635 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
636 xmlFree(t);
637 recursion_depth--;
638 *rc = EPWMD_LOOP;
639 return NULL;
642 for (last_node = last = n = node, p = req; *p; p++) {
643 xmlNodePtr tmp;
644 gchar *t = g_strdup(*p);
645 gboolean literal;
647 if (!t) {
648 *rc = gpg_error_from_errno(ENOMEM);
649 return NULL;
652 literal = is_literal_element(&t);
653 n = find_element(last, t, NULL);
654 g_free(t);
656 if (!n) {
657 if (not_found_fn)
658 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
660 *rc = EPWMD_ELEMENT_NOT_FOUND;
661 return NULL;
664 last = n->children;
665 last_node = n;
666 found = 1;
668 if (literal == FALSE) {
669 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
670 gchar **nreq = NULL, **nnreq;
672 if (!content) {
673 if (is_list_command == TRUE) {
674 if (element_to_literal(&(*p)) == FALSE) {
675 *rc = gpg_error_from_errno(ENOMEM);
676 return NULL;
680 continue;
683 if (strchr((gchar *)content, '\t') != NULL) {
684 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
685 xmlFree(content);
686 *rc = EPWMD_INVALID_ELEMENT;
687 return NULL;
690 else {
691 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
692 xmlFree(content);
693 *rc = EPWMD_INVALID_ELEMENT;
694 return NULL;
698 xmlFree(content);
699 tmp = find_account(doc, &nreq, rc, target, 0, FALSE);
701 if (!tmp) {
702 g_strfreev(nreq);
703 return NULL;
706 if (found_fn) {
707 found_fn(tmp, nreq, rc, p+1, data);
709 if (*rc) {
710 g_strfreev(nreq);
711 return NULL;
715 nnreq = strvcatv(nreq+1, p+1);
716 g_strfreev(nreq);
718 // FIXME ENOMEM
719 if (!nnreq || !*nnreq) {
720 if (nnreq)
721 g_strfreev(nnreq);
723 return tmp;
726 if (target)
727 *target = TRUE;
729 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
730 not_found_fn, is_list_command, recursion_depth, data);
732 if (*(p+1)) {
733 gchar **zz = p+1, **qq = nnreq;
735 if (g_strv_length(nnreq) > g_strv_length(p+1))
736 qq = nnreq+1;
738 for (; *qq && *zz; zz++) {
739 g_free(*zz);
740 *zz = g_strdup(*qq++);
742 if (!*zz) {
743 *rc = gpg_error_from_errno(ENOMEM);
744 n = NULL;
745 break;
750 g_strfreev(nnreq);
751 return n;
755 return n;
758 static gboolean update_element_list(struct element_list_s *elements)
760 gchar *line;
761 GSList *l;
763 if (!elements || !elements->elements)
764 return TRUE;
766 line = g_strjoinv("\t", elements->elements);
768 if (!line)
769 return FALSE;
771 g_strfreev(elements->elements);
772 elements->elements = NULL;
773 l = g_slist_append(elements->list, line);
775 if (!l)
776 return FALSE;
778 elements->list = l;
779 return TRUE;
782 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
783 struct element_list_s *elements)
785 gpg_error_t rc = 0;
786 xmlNodePtr n;
788 for (n = node; n; n = n->next) {
789 xmlChar *target = NULL;
791 if (n->type != XML_ELEMENT_NODE)
792 goto children;
794 if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, n->name) == FALSE)
795 return gpg_err_code_from_errno(ENOMEM);
797 if (update_element_list(elements) == FALSE)
798 return gpg_err_code_from_errno(ENOMEM);
800 target = node_has_attribute(n, (xmlChar *)"target");
802 if (target) {
803 gchar *tmp;
804 gchar *save = elements->prefix;
805 gboolean r = elements->resolving;
807 elements->depth++;
809 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
810 xmlChar *t = xmlGetNodePath(n);
811 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
812 xmlFree(t);
813 xmlFree(target);
814 return EPWMD_LOOP;
817 if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, n->name) == FALSE) {
818 xmlFree(target);
819 return gpg_err_code_from_errno(ENOMEM);
822 tmp = g_strjoinv("\t", elements->elements);
824 if (!tmp) {
825 xmlFree(target);
826 return gpg_err_code_from_errno(ENOMEM);
829 if (update_element_list(elements) == FALSE) {
830 xmlFree(target);
831 return gpg_err_code_from_errno(ENOMEM);
834 elements->prefix = tmp;
835 elements->resolving = TRUE;
836 rc = create_path_list(doc, elements, (gchar *)target);
837 xmlFree(target);
838 elements->resolving = r;
839 elements->depth--;
840 g_free(tmp);
841 elements->prefix = save;
843 if (rc)
844 return rc;
847 children:
848 if (n->children) {
849 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, n->name);
850 gchar *save = elements->prefix;
852 if (!tmp)
853 return gpg_err_code_from_errno(ENOMEM);
855 elements->prefix = tmp;
856 rc = path_list_recurse(doc, n->children, elements);
857 g_free(elements->prefix);
858 elements->prefix = save;
860 if (rc)
861 return rc;
865 return rc;
868 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
869 const gchar *value)
871 xmlAttrPtr a;
873 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
874 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
876 if (!a)
877 return EPWMD_LIBXML_ERROR;
879 else
880 xmlNodeSetContent(a->children, (xmlChar *)value);
882 return 0;
885 gpg_error_t update_timestamp(xmlDocPtr doc)
887 xmlNodePtr n = xmlDocGetRootElement(doc);
888 gchar *t = g_strdup_printf("%li", time(NULL));
889 gpg_error_t rc;
891 rc = add_attribute(n, "age", t);
892 g_free(t);
893 return rc;
897 * From the element path 'path', find sub-nodes and append them to the list.
899 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
900 gchar *path)
902 gpg_error_t rc;
903 gchar **req, **req_orig;
904 xmlNodePtr n;
905 gboolean a_target = FALSE;
907 req = split_input_line(path, "\t", 0);
909 if (!req) {
910 req = split_input_line(path, " ", 0);
912 if (!req)
913 return EPWMD_COMMAND_SYNTAX;
916 req_orig = g_strdupv(req);
918 if (!req_orig) {
919 rc = gpg_err_code_from_errno(ENOMEM);
920 goto fail;
923 n = find_account(doc, &req, &rc, &a_target, 0, FALSE);
925 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
926 rc = 0;
927 goto fail;
929 else if (!n)
930 goto fail;
932 if (a_target == TRUE) {
933 g_free(*req);
934 *req = g_strdup(*req_orig);
937 if (*(req+1)) {
938 gboolean e_target = FALSE;
940 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL);
942 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
943 rc = 0;
944 goto fail;
946 else if (!n)
947 goto fail;
950 if (!elements->prefix) {
952 * FIXME
954 * If any req_orig element contains no target the element should be
955 * prefixed with the literal character. Not really crucial if the
956 * client isn't human because child elements are prefixed for the
957 * current path. But may be confusing if editing by hand.
959 elements->prefix = g_strjoinv("\t", req_orig);
961 if (!elements->prefix) {
962 rc = gpg_err_code_from_errno(ENOMEM);
963 goto fail;
966 if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
967 rc = gpg_err_code_from_errno(ENOMEM);
968 goto fail;
971 if (update_element_list(elements) == FALSE) {
972 rc = gpg_err_code_from_errno(ENOMEM);
973 goto fail;
977 rc = path_list_recurse(doc, n->children, elements);
979 fail:
980 if (req_orig)
981 g_strfreev(req_orig);
983 g_strfreev(req);
984 return rc;
987 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
988 xmlChar *value, xmlBufferPtr *result)
990 gint i = value ? nodes->nodeNr - 1 : 0;
991 xmlBufferPtr buf;
993 buf = xmlBufferCreate();
995 if (!buf)
996 return gpg_err_code_from_errno(ENOMEM);
998 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
999 xmlNodePtr n = nodes->nodeTab[i];
1001 if (!n)
1002 continue;
1004 if (!value) {
1005 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1006 *result = buf;
1007 return EPWMD_LIBXML_ERROR;
1010 continue;
1013 xmlNodeSetContent(n, value);
1016 *result = buf;
1017 return 0;
1020 /* Updates the DTD and renames the root "accounts" and "account" elements. */
1021 gpg_error_t convert_xml(gchar **xml, goffset *len)
1023 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1024 xmlDocPtr doc, new = NULL;
1025 xmlNodePtr n;
1027 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1029 if (!doc)
1030 return EPWMD_LIBXML_ERROR;
1032 gcry_free(*xml);
1033 *xml = NULL;
1034 n = xmlDocGetRootElement(doc);
1035 xmlNodeSetName(n, (xmlChar *)"pwmd");
1037 for (n = n->children; n; n = n->next) {
1038 if (xmlStrcmp(n->name, (xmlChar *)"account") == 0)
1039 xmlNodeSetName(n, (xmlChar *)"root");
1042 new = create_dtd();
1044 if (!new)
1045 goto fail;
1047 n = xmlDocGetRootElement(doc);
1048 xmlDocSetRootElement(new, n);
1049 xmlDocDumpMemory(new, (xmlChar **)xml, (gint *)len);
1050 xmlDocSetRootElement(new, xmlCopyNode(n, 0));
1051 rc = 0;
1053 fail:
1054 if (new)
1055 xmlFreeDoc(new);
1057 xmlFreeDoc(doc);
1058 return rc;