Fixed a memory leak create_dtd().
[pwmd.git] / src / xml.c
blob3b516192c9796adb205af3523fa8ecf4cbeeeee7
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2008 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 void log_write(const gchar *fmt, ...);
41 static xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop);
42 static xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr);
45 * 'element' must be allocated.
47 gboolean is_literal_element(gchar **element)
49 gchar *p;
51 if (!element || !*element)
52 return FALSE;
54 if (*(*element) == '!') {
55 gchar *c;
57 for (p = *element, c = p+1; *c; c++)
58 *p++ = *c;
60 *p = 0;
61 return TRUE;
64 return FALSE;
68 * Fails if 'element' begins with punctuation or digit or contains whitespace.
70 * I'm not sure about using g_unichar_isspace() rather than isspace()?
72 gboolean valid_xml_element(xmlChar *element)
74 gunichar c;
75 glong len;
76 gchar *p = (gchar *)element;
78 if (!element || !*element)
79 return FALSE;
81 if (*p == '!')
82 p++;
84 len = g_utf8_strlen(p, -1) - 1;
85 c = g_utf8_get_char(p++);
87 if (g_unichar_ispunct(c) == TRUE || g_unichar_isdigit(c) == TRUE ||
88 g_unichar_isspace(c) == TRUE)
89 return FALSE;
91 while (*p && len--) {
92 c = g_utf8_get_char(p++);
94 if (g_unichar_isspace(c))
95 return FALSE;
98 return TRUE;
101 gboolean valid_element_path(gchar **path, gboolean has_value)
103 gchar **p;
105 for (p = path; *p; p++) {
107 * An empty element is valid and don't check the syntax of the
108 * content.
110 if (has_value == TRUE && (!*(p+1) || !*p[0]))
111 break;
113 if (valid_xml_element((xmlChar *)*p) == FALSE)
114 return FALSE;
117 return TRUE;
120 gpg_error_t new_account(xmlDocPtr doc, gchar *name)
122 xmlNodePtr root = xmlDocGetRootElement(doc);
123 xmlAttrPtr a;
124 xmlNodePtr n;
125 gchar *p = name;
127 if (!p || !root)
128 return EPWMD_LIBXML_ERROR;
130 if (*p == '!')
131 p++;
133 n = xmlNewNode(NULL, (xmlChar *)"root");
134 n = xmlAddChild(root, n);
135 a = xmlNewProp(n, (xmlChar *)"name", (xmlChar *)p);
136 return 0;
139 xmlDocPtr create_dtd(xmlDocPtr doc)
141 xmlNodePtr n = NULL;
142 xmlTextWriterPtr wr;
143 gboolean is_new = TRUE;
145 if (doc) {
146 n = xmlDocGetRootElement(doc);
147 is_new = FALSE;
150 if (n)
151 wr = xmlNewTextWriterTree(doc, n, 0);
152 else {
153 if (doc)
154 xmlFreeDoc(doc);
156 wr = xmlNewTextWriterDoc(&doc, 0);
157 is_new = TRUE;
160 if (!wr)
161 return NULL;
163 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
164 goto fail;
166 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
167 (xmlChar *)"(root)") == -1)
168 goto fail;
170 xmlTextWriterEndDTDElement(wr);
172 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"root",
173 (xmlChar *)"name CDATA #REQUIRED") == -1)
174 goto fail;
176 xmlTextWriterEndDTDAttlist(wr);
177 xmlTextWriterEndDTD(wr);
179 if (is_new) {
180 xmlTextWriterStartElement(wr, (xmlChar *)"pwmd");
181 xmlTextWriterEndElement(wr);
184 xmlTextWriterEndDocument(wr);
185 xmlFreeTextWriter(wr);
186 return doc;
188 fail:
189 xmlTextWriterEndDocument(wr);
190 xmlFreeTextWriter(wr);
191 return NULL;
194 xmlChar *new_document()
196 xmlChar *xml;
197 gint len;
198 xmlDocPtr doc = create_dtd(NULL);
200 if (!doc)
201 return NULL;
203 #ifdef MEM_DEBUG
204 /* The memory allocators set in main() all use xmalloc,xfree,etc. When
205 * MEM_DEBUG is set, a segfault will occur do to an invalid free if this
206 * code isn't included.
209 xmlChar *tmp;
210 gint tmp_len;
212 xmlDocDumpMemory(doc, &tmp, &tmp_len);
213 xml = gcry_calloc(1, tmp_len+1);
215 if (!xml) {
216 xmlFree(tmp);
217 return NULL;
220 memcpy(xml, tmp, tmp_len);
221 xmlFree(tmp);
223 #else
224 xmlDocDumpMemory(doc, &xml, &len);
225 #endif
226 xmlFreeDoc(doc);
227 return xml;
231 * Lists root account element names. If there's a target attribute both
232 * literal and non-literal element names will be added.
234 gpg_error_t list_accounts(xmlDocPtr doc, GString **result)
236 xmlNodePtr n = NULL;
237 GSList *list = NULL;
238 gint total, i;
239 GString *string;
240 gpg_error_t rc = 0;
242 n = xmlDocGetRootElement(doc);
244 if (!n || !n->children)
245 return EPWMD_EMPTY_ELEMENT;
247 for (n = n->children; n; n = n->next) {
248 xmlAttrPtr a;
249 xmlChar *val, *target;
250 GSList *tlist;
251 gchar *tmp;
253 if (n->type != XML_ELEMENT_NODE)
254 continue;
256 a = xmlHasProp(n, (xmlChar *)"name");
258 if (!a || !a->children->content)
259 continue;
261 val = xmlNodeGetContent(a->children);
263 if (!val) {
264 rc = gpg_error_from_errno(ENOMEM);
265 goto fail;
268 tmp = g_strdup_printf("!%s", (gchar *)val);
270 if (!tmp) {
271 xmlFree(val);
272 rc = gpg_error_from_errno(ENOMEM);
273 goto fail;
276 tlist = g_slist_append(list, tmp);
278 if (!tlist) {
279 xmlFree(val);
280 rc = gpg_error_from_errno(ENOMEM);
281 goto fail;
284 list = tlist;
285 target = node_has_attribute(n, (xmlChar *)"target");
287 if (target) {
288 gchar *t = g_strdup((gchar *)val);
290 if (!t) {
291 xmlFree(val);
292 xmlFree(target);
293 rc = gpg_error_from_errno(ENOMEM);
294 goto fail;
297 tlist = g_slist_append(list, t);
299 if (!tlist) {
300 g_free(t);
301 xmlFree(target);
302 rc = gpg_error_from_errno(ENOMEM);
303 goto fail;
306 list = tlist;
309 xmlFree(val);
310 xmlFree(target);
313 total = g_slist_length(list);
315 if (!total)
316 return EPWMD_EMPTY_ELEMENT;
318 string = g_string_new(NULL);
320 if (!string) {
321 rc = gpg_error_from_errno(ENOMEM);
322 goto fail;
325 for (i = 0; i < total; i++) {
326 gchar *val = g_slist_nth_data(list, i);
328 g_string_append_printf(string, "%s\n", val);
331 string = g_string_truncate(string, string->len - 1);
332 *result = string;
334 fail:
335 total = g_slist_length(list);
337 for (i = 0; i < total; i++)
338 g_free(g_slist_nth_data(list, i));
340 g_slist_free(list);
341 return rc;
344 // FIXME return a gboolean in case of memory allocation failure
345 gchar **split_input_line(gchar *str, gchar *delim, gint n)
347 if (!str || !*str)
348 return NULL;
350 return g_strsplit(str, delim, n);
354 * Prevents a sibling element past the current element path with the same
355 * element name.
357 static xmlNodePtr find_stop_node(xmlNodePtr node)
359 xmlNodePtr n;
361 for (n = node->parent->children; n; n = n->next) {
362 if (n == node)
363 return n->next;
366 return NULL;
370 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
371 * content but as an element.
373 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
374 gpg_error_t *rc, void *data)
376 gint i;
377 char **req = path;
379 if (xmlStrEqual(node->name, (xmlChar *)*req))
380 req++;
382 for (i = 0; req[i]; i++) {
383 xmlNodePtr n;
385 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
386 (n && n->parent == node->parent)) {
387 is_literal_element(&req[i]);
388 n = xmlNewNode(NULL, (xmlChar *)req[i]);
390 if (!n) {
391 *rc = gpg_error_from_errno(ENOMEM);
392 return NULL;
395 node = xmlAddChild(node, n);
397 if (!node) {
398 *rc = gpg_error_from_errno(ENOMEM);
399 return NULL;
402 else
403 node = n;
406 return node;
409 xmlNodePtr find_text_node(xmlNodePtr node)
411 xmlNodePtr n = node;
413 if (n && n->type == XML_TEXT_NODE)
414 return n;
416 for (n = node; n; n = n->next) {
417 if (n->type == XML_TEXT_NODE)
418 return n;
421 return NULL;
424 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
425 gpg_error_t *rc, void *data)
427 gint i;
428 gchar **req = elements;
430 if (node->type == XML_TEXT_NODE)
431 node = node->parent;
433 if (node->name && xmlStrEqual(node->name, (xmlChar *)*req))
434 req++;
436 for (i = 0; req[i]; i++) {
437 xmlNodePtr n;
439 if (req[i+1]) {
441 * Strip the first '!' if needed. If there's another, it's an
442 * rc. The syntax has already been checked before calling this
443 * function.
445 is_literal_element(&req[i]);
449 * The value of the element tree.
451 if (!req[i+1]) {
452 n = find_text_node(node->children);
454 if (!n)
455 /* Use AddContent here to prevent overwriting any children. */
456 xmlNodeAddContent(node, (xmlChar *)req[i]);
457 else if (n && !*req[i])
458 xmlNodeSetContent(n, NULL);
459 else
460 xmlNodeSetContent(n, (xmlChar *)req[i]);
462 break;
465 n = find_element(node, req[i], find_stop_node(node));
468 * If the found element has the same parent as the current element,
469 * they are siblings and the new element needs to be created as a
470 * child of the current element (node).
472 if (n && n->parent == node->parent)
473 n = NULL;
475 if (!n) {
476 n = xmlNewNode(NULL, (xmlChar *)req[i]);
478 if (!n) {
479 *rc = gpg_error_from_errno(ENOMEM);
480 return NULL;
483 node = xmlAddChild(node, n);
485 if (!node) {
486 *rc = gpg_error_from_errno(ENOMEM);
487 return NULL;
490 else
491 node = n;
494 return node;
497 xmlNodePtr find_account(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
498 gboolean *target, gint recursion_depth)
500 xmlNodePtr n = xmlDocGetRootElement(doc);
501 gint depth = 0;
502 gchar *account = g_strdup(*req[0]);
503 gboolean literal = is_literal_element(&account);
505 if (!account) {
506 *rc = gpg_error_from_errno(ENOMEM);
507 return NULL;
510 *rc = 0;
511 recursion_depth++;
513 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
514 xmlChar *t = xmlGetNodePath(n);
516 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
517 xmlFree(t);
518 *rc = EPWMD_LOOP;
519 return NULL;
522 while (n) {
523 if (n->type == XML_ELEMENT_NODE) {
524 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
525 n = n->children;
526 depth++;
527 continue;
530 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"root")) {
531 xmlChar *content = node_has_attribute(n, (xmlChar *)"name");
533 if (!content)
534 continue;
536 if (xmlStrEqual(content, (xmlChar *)account)) {
537 gchar **nreq, **tmp = NULL;
539 if (literal == TRUE) {
540 xmlFree(content);
541 g_free(account);
542 return n;
545 xmlFree(content);
546 content = node_has_attribute(n, (xmlChar *)"target");
548 if (!content) {
549 g_free(account);
550 return n;
553 if (strchr((gchar *)content, '\t')) {
554 nreq = split_input_line((gchar *)content, "\t", 0);
555 xmlFree(content);
557 #if 0
559 * FIXME ENOMEM
561 if (!nreq) {
562 *rc = gpg_error_from_errno(ENOMEM);
563 return NULL;
565 #endif
567 tmp = *req;
568 tmp = strvcatv(nreq, tmp+1);
569 g_strfreev(nreq);
571 if (!tmp) {
572 *rc = gpg_error_from_errno(ENOMEM);
573 return NULL;
576 g_strfreev(*req);
577 *req = tmp;
579 else {
580 if (strv_printf(&tmp, "%s", content) == FALSE) {
581 xmlFree(content);
582 *rc = gpg_error_from_errno(ENOMEM);
583 return NULL;
586 xmlFree(content);
587 nreq = *req;
588 nreq = strvcatv(tmp, nreq+1);
589 g_strfreev(tmp);
591 if (!nreq) {
592 *rc = gpg_error_from_errno(ENOMEM);
593 return NULL;
596 g_strfreev(*req);
597 *req = nreq;
600 if (target)
601 *target = TRUE;
603 g_free(account);
604 n = find_account(doc, req, rc, target, recursion_depth);
605 return n;
610 n = n->next;
613 g_free(account);
614 *rc = EPWMD_ELEMENT_NOT_FOUND;
615 return NULL;
618 static xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
620 xmlNodePtr n;
622 if (!node || !element)
623 return NULL;
625 for (n = node; n; n = n->next) {
626 if (n->type != XML_ELEMENT_NODE)
627 continue;
629 if (n == stop)
630 break;
632 if (xmlStrEqual(n->name, (xmlChar *)element))
633 return n;
636 return NULL;
639 static xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
641 xmlAttrPtr a = xmlHasProp(n, attr);
643 if (!a)
644 return NULL;
646 if (!a->children || !a->children->content)
647 return NULL;
649 return xmlGetProp(n, attr);
652 static gboolean element_to_literal(gchar **element)
654 gchar *p = g_strdup_printf("!%s", *element);
656 if (!p)
657 return FALSE;
659 g_free(*element);
660 *element = p;
661 return TRUE;
664 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
665 gchar **req, gpg_error_t *rc, gboolean *target,
666 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
667 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
668 gboolean is_list_command, gint recursion_depth, void *data)
670 xmlNodePtr n, last, last_node;
671 gchar **p;
672 gint found = 0;
674 *rc = 0;
675 recursion_depth++;
677 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
678 xmlChar *t = xmlGetNodePath(node);
680 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
681 xmlFree(t);
682 recursion_depth--;
683 *rc = EPWMD_LOOP;
684 return NULL;
687 for (last_node = last = n = node, p = req; *p; p++) {
688 xmlNodePtr tmp;
689 gchar *t = g_strdup(*p);
690 gboolean literal;
692 if (!t) {
693 *rc = gpg_error_from_errno(ENOMEM);
694 return NULL;
697 literal = is_literal_element(&t);
698 n = find_element(last, t, NULL);
699 g_free(t);
701 if (!n) {
702 if (not_found_fn)
703 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
705 *rc = EPWMD_ELEMENT_NOT_FOUND;
706 return NULL;
709 last = n->children;
710 last_node = n;
711 found = 1;
713 if (literal == FALSE) {
714 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
715 gchar **nreq = NULL, **nnreq;
717 if (!content) {
718 if (is_list_command == TRUE) {
719 if (element_to_literal(&(*p)) == FALSE) {
720 *rc = gpg_error_from_errno(ENOMEM);
721 return NULL;
725 continue;
728 if (strchr((gchar *)content, '\t') != NULL) {
729 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
730 xmlFree(content);
731 *rc = EPWMD_INVALID_ELEMENT;
732 return NULL;
735 else {
736 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
737 xmlFree(content);
738 *rc = EPWMD_INVALID_ELEMENT;
739 return NULL;
743 xmlFree(content);
744 tmp = find_account(doc, &nreq, rc, target, 0);
746 if (!tmp) {
747 g_strfreev(nreq);
748 return NULL;
751 if (found_fn) {
752 found_fn(tmp, nreq, rc, p+1, data);
754 if (*rc) {
755 g_strfreev(nreq);
756 return NULL;
760 nnreq = strvcatv(nreq+1, p+1);
761 g_strfreev(nreq);
763 // FIXME ENOMEM
764 if (!nnreq || !*nnreq) {
765 if (nnreq)
766 g_strfreev(nnreq);
768 return tmp;
771 if (target)
772 *target = TRUE;
774 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
775 not_found_fn, is_list_command, recursion_depth, data);
777 if (*(p+1)) {
778 gchar **zz = p+1, **qq = nnreq;
780 if (g_strv_length(nnreq) > g_strv_length(p+1))
781 qq = nnreq+1;
783 for (; *qq && *zz; zz++) {
784 g_free(*zz);
785 *zz = g_strdup(*qq++);
787 if (!*zz) {
788 *rc = gpg_error_from_errno(ENOMEM);
789 n = NULL;
790 break;
795 g_strfreev(nnreq);
796 return n;
800 return n;
803 static gboolean update_element_list(struct element_list_s *elements)
805 gchar *line;
806 GSList *l;
808 if (!elements || !elements->elements)
809 return TRUE;
811 line = g_strjoinv("\t", elements->elements);
813 if (!line)
814 return FALSE;
816 g_strfreev(elements->elements);
817 elements->elements = NULL;
818 l = g_slist_append(elements->list, line);
820 if (!l)
821 return FALSE;
823 elements->list = l;
824 return TRUE;
827 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
828 struct element_list_s *elements)
830 gpg_error_t rc = 0;
831 xmlNodePtr n;
833 for (n = node; n; n = n->next) {
834 xmlChar *target = NULL;
836 if (n->type != XML_ELEMENT_NODE)
837 goto children;
839 if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, n->name) == FALSE)
840 return gpg_err_code_from_errno(ENOMEM);
842 if (update_element_list(elements) == FALSE)
843 return gpg_err_code_from_errno(ENOMEM);
845 target = node_has_attribute(n, (xmlChar *)"target");
847 if (target) {
848 gchar *tmp;
849 gchar *save = elements->prefix;
850 gboolean r = elements->resolving;
852 elements->depth++;
854 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
855 xmlChar *t = xmlGetNodePath(n);
856 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
857 xmlFree(t);
858 xmlFree(target);
859 return EPWMD_LOOP;
862 if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, n->name) == FALSE) {
863 xmlFree(target);
864 return gpg_err_code_from_errno(ENOMEM);
867 tmp = g_strjoinv("\t", elements->elements);
869 if (!tmp) {
870 xmlFree(target);
871 return gpg_err_code_from_errno(ENOMEM);
874 if (update_element_list(elements) == FALSE) {
875 xmlFree(target);
876 return gpg_err_code_from_errno(ENOMEM);
879 elements->prefix = tmp;
880 elements->resolving = TRUE;
881 rc = create_path_list(doc, elements, (gchar *)target);
882 xmlFree(target);
883 elements->resolving = r;
884 elements->depth--;
885 g_free(tmp);
886 elements->prefix = save;
888 if (rc)
889 return rc;
892 children:
893 if (n->children) {
894 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, n->name);
895 gchar *save = elements->prefix;
897 if (!tmp)
898 return gpg_err_code_from_errno(ENOMEM);
900 elements->prefix = tmp;
901 rc = path_list_recurse(doc, n->children, elements);
902 g_free(elements->prefix);
903 elements->prefix = save;
905 if (rc)
906 return rc;
910 return rc;
914 * From the element path 'path', find sub-nodes and append them to the list.
916 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
917 gchar *path)
919 gpg_error_t rc;
920 gchar **req, **req_orig;
921 xmlNodePtr n;
922 gboolean a_target = FALSE;
924 req = split_input_line(path, "\t", 0);
926 if (!req) {
927 req = split_input_line(path, " ", 0);
929 if (!req)
930 return EPWMD_COMMAND_SYNTAX;
933 req_orig = g_strdupv(req);
935 if (!req_orig) {
936 rc = gpg_err_code_from_errno(ENOMEM);
937 goto fail;
940 n = find_account(doc, &req, &rc, &a_target, 0);
942 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
943 rc = 0;
944 goto fail;
946 else if (!n)
947 goto fail;
949 if (a_target == TRUE) {
950 g_free(*req);
951 *req = g_strdup(*req_orig);
954 if (*(req+1)) {
955 gboolean e_target = FALSE;
957 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL);
959 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
960 rc = 0;
961 goto fail;
963 else if (!n)
964 goto fail;
967 if (!elements->prefix) {
969 * FIXME
971 * If any req_orig element contains no target the element should be
972 * prefixed with the literal character. Not really crucial if the
973 * client isn't human because child elements are prefixed for the
974 * current path. But may be confusing if editing by hand.
976 elements->prefix = g_strjoinv("\t", req_orig);
978 if (!elements->prefix) {
979 rc = gpg_err_code_from_errno(ENOMEM);
980 goto fail;
983 if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
984 rc = gpg_err_code_from_errno(ENOMEM);
985 goto fail;
988 if (update_element_list(elements) == FALSE) {
989 rc = gpg_err_code_from_errno(ENOMEM);
990 goto fail;
994 rc = path_list_recurse(doc, n->children, elements);
996 fail:
997 if (req_orig)
998 g_strfreev(req_orig);
1000 g_strfreev(req);
1001 return rc;
1004 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1005 xmlChar *value, xmlBufferPtr *result)
1007 gint i = value ? nodes->nodeNr - 1 : 0;
1008 xmlBufferPtr buf;
1010 buf = xmlBufferCreate();
1012 if (!buf)
1013 return gpg_err_code_from_errno(ENOMEM);
1015 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1016 xmlNodePtr n = nodes->nodeTab[i];
1018 if (!n)
1019 continue;
1021 if (!value) {
1022 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1023 *result = buf;
1024 return EPWMD_LIBXML_ERROR;
1027 continue;
1030 xmlNodeSetContent(n, value);
1033 *result = buf;
1034 return 0;
1037 /* Updates the DTD and renames the root "accounts" and "account" elements. */
1038 gpg_error_t convert_xml(gchar **xml, gsize *len)
1040 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1041 xmlDocPtr doc;
1042 xmlNodePtr n;
1044 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1046 if (!doc)
1047 return EPWMD_LIBXML_ERROR;
1049 doc = create_dtd(doc);
1051 if (!doc)
1052 goto fail;
1054 n = xmlDocGetRootElement(doc);
1055 xmlNodeSetName(n, (xmlChar *)"pwmd");
1057 for (n = n->children; n; n = n->next) {
1058 if (xmlStrcmp(n->name, (xmlChar *)"account") == 0)
1059 xmlNodeSetName(n, (xmlChar *)"root");
1062 gcry_free(*xml);
1063 #ifdef MEM_DEBUG
1064 /* The memory allocators set in main() all use xmalloc,xfree,etc. When
1065 * MEM_DEBUG is set, a segfault will occur do to an invalid free if this
1066 * code isn't included.
1069 xmlChar *tmp;
1070 gint tmp_len;
1072 xmlDocDumpMemory(doc, &tmp, &tmp_len);
1073 *xml = gcry_calloc(1, tmp_len+1);
1075 if (!*xml) {
1076 rc = gpg_error_from_errno(ENOMEM);
1077 goto fail;
1080 memcpy(*xml, tmp, tmp_len);
1081 xmlFree(tmp);
1082 *len = tmp_len;
1084 #else
1085 xmlDocDumpMemory(doc, (xmlChar **)xml, (gint *)len);
1086 #endif
1087 rc = 0;
1089 fail:
1090 xmlFreeDoc(doc);
1091 return rc;