Fixed the COPY command to copy attributes and handle the "target"
[pwmd.git] / src / xml.c
blobd692eec7820ee0e552e8780721933da68d0efa01
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);
43 * 'element' must be allocated.
45 gboolean is_literal_element(gchar **element)
47 gchar *p;
49 if (!element || !*element)
50 return FALSE;
52 if (*(*element) == '!') {
53 gchar *c;
55 for (p = *element, c = p+1; *c; c++)
56 *p++ = *c;
58 *p = 0;
59 return TRUE;
62 return FALSE;
66 * Fails if 'element' begins with punctuation or digit or contains whitespace.
68 * I'm not sure about using g_unichar_isspace() rather than isspace()?
70 gboolean valid_xml_element(xmlChar *element)
72 gunichar c;
73 glong len;
74 gchar *p = (gchar *)element;
76 if (!element || !*element)
77 return FALSE;
79 len = g_utf8_strlen(p, -1) - 1;
80 c = g_utf8_get_char(p++);
82 if (g_unichar_ispunct(c) == TRUE || g_unichar_isdigit(c) == TRUE ||
83 g_unichar_isspace(c) == TRUE)
84 return FALSE;
86 while (*p && len--) {
87 c = g_utf8_get_char(p++);
89 if (g_unichar_isspace(c))
90 return FALSE;
93 return TRUE;
96 gpg_error_t new_root_element(xmlDocPtr doc, gchar *name)
98 xmlNodePtr root = xmlDocGetRootElement(doc);
99 xmlAttrPtr a;
100 xmlNodePtr n;
101 gchar *p = name;
103 if (!p || !root)
104 return EPWMD_LIBXML_ERROR;
106 if (*p == '!')
107 p++;
109 if (!valid_xml_element((xmlChar *)p))
110 return EPWMD_INVALID_ELEMENT;
112 n = xmlNewNode(NULL, (xmlChar *)"root");
113 n = xmlAddChild(root, n);
114 a = xmlNewProp(n, (xmlChar *)"name", (xmlChar *)p);
115 return 0;
118 xmlDocPtr create_dtd()
120 xmlDocPtr doc;
121 xmlTextWriterPtr wr = xmlNewTextWriterDoc(&doc, 0);
123 if (!wr)
124 return NULL;
126 if (xmlTextWriterStartDocument(wr, NULL, NULL, NULL))
127 goto fail;
129 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
130 goto fail;
132 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
133 (xmlChar *)"(root)") == -1)
134 goto fail;
136 xmlTextWriterEndDTDElement(wr);
138 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"root",
139 (xmlChar *)"name CDATA #REQUIRED") == -1)
140 goto fail;
142 xmlTextWriterEndDTDAttlist(wr);
143 xmlTextWriterEndDTD(wr);
145 if (xmlTextWriterStartElement(wr, (xmlChar *)"pwmd"))
146 goto fail;
148 xmlTextWriterEndElement(wr);
149 xmlTextWriterEndDocument(wr);
150 xmlFreeTextWriter(wr);
151 return doc;
153 fail:
154 xmlTextWriterEndDocument(wr);
155 xmlFreeTextWriter(wr);
156 xmlFreeDoc(doc);
157 return NULL;
160 xmlChar *new_document()
162 xmlChar *xml;
163 gint len;
164 xmlDocPtr doc = create_dtd();
166 if (!doc)
167 return NULL;
169 xmlDocDumpMemory(doc, &xml, &len);
170 xmlFreeDoc(doc);
171 return xml;
175 * Lists root element names; the value of the attribute "name" of an element
176 * "root". If there's a target attribute both literal and non-literal element
177 * names will be added. This is the primary reason why XML entities cannot be
178 * used. There wouldn't be a way to get the literal an non-literal element
179 * paths.
181 gpg_error_t list_root_elements(xmlDocPtr doc, GString **result)
183 xmlNodePtr n = NULL;
184 GSList *list = NULL;
185 gint total, i;
186 GString *string;
187 gpg_error_t rc = 0;
189 n = xmlDocGetRootElement(doc);
191 if (!n || !n->children)
192 return EPWMD_EMPTY_ELEMENT;
194 for (n = n->children; n; n = n->next) {
195 xmlAttrPtr a;
196 xmlChar *val, *target;
197 GSList *tlist;
198 gchar *tmp;
200 if (n->type != XML_ELEMENT_NODE)
201 continue;
203 a = xmlHasProp(n, (xmlChar *)"name");
205 if (!a || !a->children->content)
206 continue;
208 val = xmlNodeGetContent(a->children);
210 if (!val) {
211 rc = gpg_error_from_errno(ENOMEM);
212 goto fail;
215 tmp = g_strdup_printf("!%s", (gchar *)val);
217 if (!tmp) {
218 xmlFree(val);
219 rc = gpg_error_from_errno(ENOMEM);
220 goto fail;
223 tlist = g_slist_append(list, tmp);
225 if (!tlist) {
226 xmlFree(val);
227 rc = gpg_error_from_errno(ENOMEM);
228 goto fail;
231 list = tlist;
232 target = node_has_attribute(n, (xmlChar *)"target");
234 if (target) {
235 gchar *t = g_strdup((gchar *)val);
237 if (!t) {
238 xmlFree(val);
239 xmlFree(target);
240 rc = gpg_error_from_errno(ENOMEM);
241 goto fail;
244 tlist = g_slist_append(list, t);
246 if (!tlist) {
247 g_free(t);
248 xmlFree(target);
249 rc = gpg_error_from_errno(ENOMEM);
250 goto fail;
253 list = tlist;
256 xmlFree(val);
257 xmlFree(target);
260 total = g_slist_length(list);
262 if (!total)
263 return EPWMD_EMPTY_ELEMENT;
265 string = g_string_new(NULL);
267 if (!string) {
268 rc = gpg_error_from_errno(ENOMEM);
269 goto fail;
272 for (i = 0; i < total; i++) {
273 gchar *val = g_slist_nth_data(list, i);
275 g_string_append_printf(string, "%s\n", val);
278 string = g_string_truncate(string, string->len - 1);
279 *result = string;
281 fail:
282 total = g_slist_length(list);
284 for (i = 0; i < total; i++)
285 g_free(g_slist_nth_data(list, i));
287 g_slist_free(list);
288 return rc;
292 * Prevents a sibling element past the current element path with the same
293 * element name.
295 static xmlNodePtr find_stop_node(xmlNodePtr node)
297 xmlNodePtr n;
299 for (n = node->parent->children; n; n = n->next) {
300 if (n == node)
301 return n->next;
304 return NULL;
308 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
309 * content but as an element.
311 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
312 gpg_error_t *rc, void *data)
314 gint i;
315 char **req = path;
317 if (xmlStrEqual(node->name, (xmlChar *)*req))
318 req++;
320 for (i = 0; req[i]; i++) {
321 xmlNodePtr n;
323 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
324 (n && n->parent == node->parent)) {
325 is_literal_element(&req[i]);
327 if (!valid_xml_element((xmlChar *)req[i])) {
328 *rc = EPWMD_INVALID_ELEMENT;
329 return NULL;
332 n = xmlNewNode(NULL, (xmlChar *)req[i]);
334 if (!n) {
335 *rc = gpg_error_from_errno(ENOMEM);
336 return NULL;
339 node = xmlAddChild(node, n);
341 if (!node) {
342 *rc = gpg_error_from_errno(ENOMEM);
343 return NULL;
346 else
347 node = n;
350 return node;
353 xmlNodePtr find_text_node(xmlNodePtr node)
355 xmlNodePtr n = node;
357 if (n && n->type == XML_TEXT_NODE)
358 return n;
360 for (n = node; n; n = n->next) {
361 if (n->type == XML_TEXT_NODE)
362 return n;
365 return NULL;
368 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
369 gpg_error_t *rc, void *data)
371 gint i;
372 gchar **req = elements;
374 if (node->type == XML_TEXT_NODE)
375 node = node->parent;
377 if (node->name && xmlStrEqual(node->name, (xmlChar *)*req))
378 req++;
380 for (i = 0; req[i]; i++) {
381 xmlNodePtr n;
383 if (req[i+1]) {
385 * Strip the first '!' if needed. If there's another, it's an
386 * rc. The syntax has already been checked before calling this
387 * function.
389 is_literal_element(&req[i]);
393 * The value of the element tree.
395 if (!req[i+1]) {
396 n = find_text_node(node->children);
398 if (!n)
399 /* Use AddContent here to prevent overwriting any children. */
400 xmlNodeAddContent(node, (xmlChar *)req[i]);
401 else if (n && !*req[i])
402 xmlNodeSetContent(n, NULL);
403 else
404 xmlNodeSetContent(n, (xmlChar *)req[i]);
406 break;
409 n = find_element(node, req[i], find_stop_node(node));
412 * If the found element has the same parent as the current element,
413 * they are siblings and the new element needs to be created as a
414 * child of the current element (node).
416 if (n && n->parent == node->parent)
417 n = NULL;
419 if (!n) {
420 if (!valid_xml_element((xmlChar *)req[i])) {
421 *rc = EPWMD_INVALID_ELEMENT;
422 return NULL;
425 n = xmlNewNode(NULL, (xmlChar *)req[i]);
427 if (!n) {
428 *rc = gpg_error_from_errno(ENOMEM);
429 return NULL;
432 node = xmlAddChild(node, n);
434 if (!node) {
435 *rc = gpg_error_from_errno(ENOMEM);
436 return NULL;
439 else
440 node = n;
443 return node;
446 /* The root element is really req[0]. It is need as a pointer in case there is
447 * a target attribute so it can be updated. */
448 xmlNodePtr find_root_element(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
449 gboolean *target, gint recursion_depth, gboolean stop)
451 xmlNodePtr n = xmlDocGetRootElement(doc);
452 gint depth = 0;
453 gchar *root = g_strdup(*req[0]);
454 gboolean literal = is_literal_element(&root);
456 if (!root) {
457 *rc = gpg_error_from_errno(ENOMEM);
458 return NULL;
461 *rc = 0;
462 recursion_depth++;
464 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
465 xmlChar *t = xmlGetNodePath(n);
467 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
468 xmlFree(t);
469 g_free(root);
470 *rc = EPWMD_LOOP;
471 return NULL;
474 while (n) {
475 if (n->type == XML_ELEMENT_NODE) {
476 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
477 n = n->children;
478 depth++;
479 continue;
482 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"root")) {
483 xmlChar *content = node_has_attribute(n, (xmlChar *)"name");
485 if (!content)
486 continue;
488 if (xmlStrEqual(content, (xmlChar *)root)) {
489 gchar **nreq, **tmp = NULL;
491 if (literal == TRUE) {
492 xmlFree(content);
493 g_free(root);
494 return n;
497 xmlFree(content);
498 content = node_has_attribute(n, (xmlChar *)"target");
500 if (target)
501 *target = TRUE;
503 if (!content || stop) {
504 if (content)
505 xmlFree(content);
507 g_free(root);
508 return n;
511 if (strchr((gchar *)content, '\t')) {
512 nreq = split_input_line((gchar *)content, "\t", 0);
513 xmlFree(content);
515 #if 0
517 * FIXME ENOMEM
519 if (!nreq) {
520 *rc = gpg_error_from_errno(ENOMEM);
521 return NULL;
523 #endif
525 tmp = *req;
526 tmp = strvcatv(nreq, tmp+1);
527 g_strfreev(nreq);
529 if (!tmp) {
530 g_free(root);
531 *rc = gpg_error_from_errno(ENOMEM);
532 return NULL;
535 g_strfreev(*req);
536 *req = tmp;
538 else {
539 if (strv_printf(&tmp, "%s", content) == FALSE) {
540 xmlFree(content);
541 g_free(root);
542 *rc = gpg_error_from_errno(ENOMEM);
543 return NULL;
546 xmlFree(content);
547 nreq = *req;
548 nreq = strvcatv(tmp, nreq+1);
549 g_strfreev(tmp);
551 if (!nreq) {
552 *rc = gpg_error_from_errno(ENOMEM);
553 g_free(root);
554 return NULL;
557 g_strfreev(*req);
558 *req = nreq;
561 g_free(root);
562 n = find_root_element(doc, req, rc, target, recursion_depth, FALSE);
563 return n;
566 xmlFree(content);
570 n = n->next;
573 g_free(root);
574 *rc = EPWMD_ELEMENT_NOT_FOUND;
575 return NULL;
578 static xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
580 xmlNodePtr n;
582 if (!node || !element)
583 return NULL;
585 for (n = node; n; n = n->next) {
586 if (n->type != XML_ELEMENT_NODE)
587 continue;
589 if (n == stop)
590 break;
592 if (xmlStrEqual(n->name, (xmlChar *)element))
593 return n;
596 return NULL;
599 xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
601 xmlAttrPtr a = xmlHasProp(n, attr);
603 if (!a)
604 return NULL;
606 if (!a->children || !a->children->content)
607 return NULL;
609 return xmlGetProp(n, attr);
612 static gboolean element_to_literal(gchar **element)
614 gchar *p = g_strdup_printf("!%s", *element);
616 if (!p)
617 return FALSE;
619 g_free(*element);
620 *element = p;
621 return TRUE;
624 /* Resolves elements in 'req' one at a time. It's recursive in case of
625 * "target" attributes. */
626 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
627 gchar **req, gpg_error_t *rc, gboolean *target,
628 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
629 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
630 gboolean is_list_command, gint recursion_depth, void *data, gboolean stop)
632 xmlNodePtr n, last, last_node;
633 gchar **p;
634 gint found = 0;
636 *rc = 0;
637 recursion_depth++;
639 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
640 xmlChar *t = xmlGetNodePath(node);
642 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
643 xmlFree(t);
644 recursion_depth--;
645 *rc = EPWMD_LOOP;
646 return NULL;
649 for (last_node = last = n = node, p = req; *p; p++) {
650 xmlNodePtr tmp;
651 gchar *t = g_strdup(*p);
652 gboolean literal;
654 if (!t) {
655 *rc = gpg_error_from_errno(ENOMEM);
656 return NULL;
659 literal = is_literal_element(&t);
660 n = find_element(last, t, NULL);
661 g_free(t);
663 if (!n) {
664 if (not_found_fn)
665 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
667 *rc = EPWMD_ELEMENT_NOT_FOUND;
668 return NULL;
671 last = n->children;
672 last_node = n;
673 found = 1;
675 if (literal == FALSE) {
676 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
677 gchar **nreq = NULL, **nnreq;
679 if (!content) {
680 if (is_list_command == TRUE) {
681 if (element_to_literal(&(*p)) == FALSE) {
682 *rc = gpg_error_from_errno(ENOMEM);
683 return NULL;
687 continue;
690 if (target)
691 *target = TRUE;
693 if (!*(p+1) && stop) {
694 xmlFree(content);
695 return n;
698 if (strchr((gchar *)content, '\t') != NULL) {
699 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
700 xmlFree(content);
701 *rc = EPWMD_INVALID_ELEMENT;
702 return NULL;
705 else {
706 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
707 xmlFree(content);
708 *rc = EPWMD_INVALID_ELEMENT;
709 return NULL;
713 xmlFree(content);
714 tmp = find_root_element(doc, &nreq, rc, target, 0, FALSE);
716 if (!tmp) {
717 g_strfreev(nreq);
718 return NULL;
721 if (found_fn) {
722 found_fn(tmp, nreq, rc, p+1, data);
724 if (*rc) {
725 g_strfreev(nreq);
726 return NULL;
730 nnreq = strvcatv(nreq+1, p+1);
731 g_strfreev(nreq);
733 // FIXME ENOMEM
734 if (!nnreq || !*nnreq) {
735 if (nnreq)
736 g_strfreev(nnreq);
738 return tmp;
741 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
742 not_found_fn, is_list_command, recursion_depth, data, stop);
744 if (*(p+1)) {
745 gchar **zz = p+1, **qq = nnreq;
747 if (g_strv_length(nnreq) > g_strv_length(p+1))
748 qq = nnreq+1;
750 for (; *qq && *zz; zz++) {
751 g_free(*zz);
752 *zz = g_strdup(*qq++);
754 if (!*zz) {
755 *rc = gpg_error_from_errno(ENOMEM);
756 n = NULL;
757 break;
762 g_strfreev(nnreq);
763 return n;
767 return n;
770 static gboolean update_element_list(struct element_list_s *elements)
772 gchar *line;
773 GSList *l;
775 if (!elements || !elements->elements)
776 return TRUE;
778 line = g_strjoinv("\t", elements->elements);
780 if (!line)
781 return FALSE;
783 g_strfreev(elements->elements);
784 elements->elements = NULL;
785 l = g_slist_append(elements->list, line);
787 if (!l)
788 return FALSE;
790 elements->list = l;
791 return TRUE;
794 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
795 struct element_list_s *elements)
797 gpg_error_t rc = 0;
798 xmlNodePtr n;
800 for (n = node; n; n = n->next) {
801 xmlChar *target = NULL;
803 if (n->type != XML_ELEMENT_NODE)
804 goto children;
806 if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, n->name) == FALSE)
807 return gpg_err_code_from_errno(ENOMEM);
809 if (update_element_list(elements) == FALSE)
810 return gpg_err_code_from_errno(ENOMEM);
812 target = node_has_attribute(n, (xmlChar *)"target");
814 if (target) {
815 gchar *tmp;
816 gchar *save = elements->prefix;
817 gboolean r = elements->resolving;
819 elements->depth++;
821 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
822 xmlChar *t = xmlGetNodePath(n);
823 log_write("%s: %s", pwmd_strerror(EPWMD_LOOP), t);
824 xmlFree(t);
825 xmlFree(target);
826 return EPWMD_LOOP;
829 if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, n->name) == FALSE) {
830 xmlFree(target);
831 return gpg_err_code_from_errno(ENOMEM);
834 tmp = g_strjoinv("\t", elements->elements);
836 if (!tmp) {
837 xmlFree(target);
838 return gpg_err_code_from_errno(ENOMEM);
841 if (update_element_list(elements) == FALSE) {
842 xmlFree(target);
843 return gpg_err_code_from_errno(ENOMEM);
846 elements->prefix = tmp;
847 elements->resolving = TRUE;
848 rc = create_path_list(doc, elements, (gchar *)target);
849 xmlFree(target);
850 elements->resolving = r;
851 elements->depth--;
852 g_free(tmp);
853 elements->prefix = save;
855 if (rc)
856 return rc;
859 children:
860 if (n->children) {
861 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, n->name);
862 gchar *save = elements->prefix;
864 if (!tmp)
865 return gpg_err_code_from_errno(ENOMEM);
867 elements->prefix = tmp;
868 rc = path_list_recurse(doc, n->children, elements);
869 g_free(elements->prefix);
870 elements->prefix = save;
872 if (rc)
873 return rc;
877 return rc;
880 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
881 const gchar *value)
883 xmlAttrPtr a;
885 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
886 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
888 if (!a)
889 return EPWMD_LIBXML_ERROR;
891 else
892 xmlNodeSetContent(a->children, (xmlChar *)value);
894 return 0;
897 gpg_error_t update_timestamp(xmlDocPtr doc)
899 xmlNodePtr n = xmlDocGetRootElement(doc);
900 gchar *t = g_strdup_printf("%li", time(NULL));
901 gpg_error_t rc;
903 rc = add_attribute(n, "age", t);
904 g_free(t);
905 return rc;
909 * From the element path 'path', find sub-nodes and append them to the list.
911 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
912 gchar *path)
914 gpg_error_t rc;
915 gchar **req, **req_orig;
916 xmlNodePtr n;
917 gboolean a_target = FALSE;
919 req = split_input_line(path, "\t", 0);
921 if (!req) {
922 req = split_input_line(path, " ", 0);
924 if (!req)
925 return EPWMD_COMMAND_SYNTAX;
928 req_orig = g_strdupv(req);
930 if (!req_orig) {
931 rc = gpg_err_code_from_errno(ENOMEM);
932 goto fail;
935 n = find_root_element(doc, &req, &rc, &a_target, 0, FALSE);
937 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
938 rc = 0;
939 goto fail;
941 else if (!n)
942 goto fail;
944 if (a_target == TRUE) {
945 g_free(*req);
946 *req = g_strdup(*req_orig);
949 if (*(req+1)) {
950 gboolean e_target = FALSE;
952 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL, FALSE);
954 if (!n && rc == EPWMD_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
955 rc = 0;
956 goto fail;
958 else if (!n)
959 goto fail;
962 if (!elements->prefix) {
964 * FIXME
966 * If any req_orig element contains no target the element should be
967 * prefixed with the literal character. Not really crucial if the
968 * client isn't human because child elements are prefixed for the
969 * current path. But may be confusing if editing by hand.
971 elements->prefix = g_strjoinv("\t", req_orig);
973 if (!elements->prefix) {
974 rc = gpg_err_code_from_errno(ENOMEM);
975 goto fail;
978 if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
979 rc = gpg_err_code_from_errno(ENOMEM);
980 goto fail;
983 if (update_element_list(elements) == FALSE) {
984 rc = gpg_err_code_from_errno(ENOMEM);
985 goto fail;
989 rc = path_list_recurse(doc, n->children, elements);
991 fail:
992 if (req_orig)
993 g_strfreev(req_orig);
995 g_strfreev(req);
996 return rc;
999 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1000 xmlChar *value, xmlBufferPtr *result)
1002 gint i = value ? nodes->nodeNr - 1 : 0;
1003 xmlBufferPtr buf;
1005 buf = xmlBufferCreate();
1007 if (!buf)
1008 return gpg_err_code_from_errno(ENOMEM);
1010 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1011 xmlNodePtr n = nodes->nodeTab[i];
1013 if (!n)
1014 continue;
1016 if (!value) {
1017 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1018 *result = buf;
1019 return EPWMD_LIBXML_ERROR;
1022 continue;
1025 xmlNodeSetContent(n, value);
1028 *result = buf;
1029 return 0;
1032 /* Updates the DTD and renames the root "accounts" and "account" elements. */
1033 gpg_error_t convert_xml(gchar **xml, goffset *len)
1035 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1036 xmlDocPtr doc, new = NULL;
1037 xmlNodePtr n;
1039 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1041 if (!doc)
1042 return EPWMD_LIBXML_ERROR;
1044 gcry_free(*xml);
1045 *xml = NULL;
1046 n = xmlDocGetRootElement(doc);
1047 xmlNodeSetName(n, (xmlChar *)"pwmd");
1049 for (n = n->children; n; n = n->next) {
1050 if (xmlStrcmp(n->name, (xmlChar *)"account") == 0)
1051 xmlNodeSetName(n, (xmlChar *)"root");
1054 new = create_dtd();
1056 if (!new)
1057 goto fail;
1059 n = xmlDocGetRootElement(doc);
1060 xmlDocSetRootElement(new, n);
1061 xmlDocDumpMemory(new, (xmlChar **)xml, (gint *)len);
1062 xmlDocSetRootElement(new, xmlCopyNode(n, 0));
1063 rc = 0;
1065 fail:
1066 if (new)
1067 xmlFreeDoc(new);
1069 xmlFreeDoc(doc);
1070 return rc;