Fix compile time warnings.
[pwmd.git] / src / xml.c
blob2d0d52b79fef18ab5ea8dfcd3a3dc6d7d68391dd
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 <glib/gi18n.h>
33 #include <gcrypt.h>
34 #include <libxml/xmlwriter.h>
35 #include "pwmd_error.h"
36 #include "misc.h"
37 #include "xml.h"
39 extern void log_write(const gchar *fmt, ...);
42 * 'element' must be allocated.
44 gboolean is_literal_element(gchar **element)
46 gchar *p;
48 if (!element || !*element)
49 return FALSE;
51 if (*(*element) == '!') {
52 gchar *c;
54 for (p = *element, c = p+1; *c; c++)
55 *p++ = *c;
57 *p = 0;
58 return TRUE;
61 return FALSE;
64 gboolean valid_xml_element(xmlChar *element)
66 gchar *p = (gchar *)element;
68 if (!element || *element == '!')
69 return FALSE;
71 for (; *p; p++) {
72 if (g_ascii_isspace(*p))
73 return FALSE;
76 return TRUE;
79 gboolean valid_element_path(gchar **path, gboolean with_content)
81 gchar **dup = NULL, **p;
82 gint i, t;
84 if (!path || !*path)
85 return FALSE;
87 /* Save some memory by not duplicating the element content. */
88 if (with_content) {
89 t = g_strv_length(path);
90 for (i = 0; i < t-1; i++) {
91 gchar **tmp = g_realloc(dup, (i+2)*sizeof(gchar *));
93 if (!tmp) {
94 g_strfreev(dup);
95 return FALSE;
98 dup = tmp;
99 dup[i] = g_strdup(path[i]);
100 dup[i+1] = NULL;
103 else
104 dup = g_strdupv(path);
106 if (!dup)
107 return FALSE;
109 for (p = dup; *p && *(*p); p++) {
110 is_literal_element(&(*p));
111 if (!valid_xml_element((xmlChar *)*p)) {
112 g_strfreev(dup);
113 return FALSE;
117 g_strfreev(dup);
118 return TRUE;
121 static gpg_error_t attr_ctime(xmlNodePtr n)
123 gchar *buf = g_strdup_printf("%li", time(NULL));
124 gpg_error_t rc;
126 if (!buf)
127 return GPG_ERR_ENOMEM;
129 rc = add_attribute(n, "_ctime", buf);
130 g_free(buf);
131 return rc;
134 static gpg_error_t create_new_element(xmlNodePtr parent, const gchar *name,
135 xmlNodePtr *result)
137 xmlNodePtr n = xmlNewNode(NULL, (xmlChar *)"element");
138 gpg_error_t rc;
140 if (!n)
141 return GPG_ERR_ENOMEM;
143 rc = add_attribute(n, "_name", name);
144 if (!rc)
145 rc = attr_ctime(n);
147 if (!rc) {
148 if (result)
149 *result = xmlAddChild(parent, n);
150 else
151 (void)xmlAddChild(parent, n);
153 else
154 xmlFreeNode(n);
156 return rc;
159 gpg_error_t new_root_element(xmlDocPtr doc, gchar *name)
161 xmlNodePtr root = xmlDocGetRootElement(doc);
162 gchar *p = name;
164 if (!p || !root)
165 return EPWMD_LIBXML_ERROR;
167 if (*p == '!')
168 p++;
170 if (!valid_xml_element((xmlChar *)p))
171 return GPG_ERR_INV_VALUE;
173 return create_new_element(root, p, NULL);
176 xmlDocPtr create_dtd()
178 xmlDocPtr doc;
179 xmlTextWriterPtr wr = xmlNewTextWriterDoc(&doc, 0);
181 if (!wr)
182 return NULL;
184 if (xmlTextWriterStartDocument(wr, NULL, NULL, NULL))
185 goto fail;
187 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
188 goto fail;
190 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
191 (xmlChar *)"(element)") == -1)
192 goto fail;
194 xmlTextWriterEndDTDElement(wr);
196 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"element",
197 (xmlChar *)"_name CDATA #REQUIRED") == -1)
198 goto fail;
200 xmlTextWriterEndDTDAttlist(wr);
201 xmlTextWriterEndDTD(wr);
203 if (xmlTextWriterStartElement(wr, (xmlChar *)"pwmd"))
204 goto fail;
206 xmlTextWriterEndElement(wr);
207 xmlTextWriterEndDocument(wr);
208 xmlFreeTextWriter(wr);
209 return doc;
211 fail:
212 xmlTextWriterEndDocument(wr);
213 xmlFreeTextWriter(wr);
214 xmlFreeDoc(doc);
215 return NULL;
218 xmlChar *new_document()
220 xmlChar *xml;
221 gint len;
222 xmlDocPtr doc = create_dtd();
224 if (!doc)
225 return NULL;
227 xmlDocDumpMemory(doc, &xml, &len);
228 xmlFreeDoc(doc);
229 return xml;
232 xmlNodePtr find_element_node(xmlNodePtr node)
234 xmlNodePtr n = node;
236 if (n && n->type == XML_ELEMENT_NODE)
237 return n;
239 for (n = node; n; n = n->next) {
240 if (n->type == XML_ELEMENT_NODE)
241 return n;
244 return NULL;
248 * Lists root element names; the value of the attribute "_name" of an element
249 * "element". If there's a target attribute both literal and non-literal
250 * element names will be added. This is the primary reason why XML entities
251 * cannot be used. There wouldn't be a way to get the literal an non-literal
252 * element paths.
254 gpg_error_t list_root_elements(xmlDocPtr doc, GString **result,
255 gboolean verbose)
257 xmlNodePtr n = NULL;
258 GSList *list = NULL;
259 gint total, i;
260 GString *string;
261 gpg_error_t rc = 0;
263 n = xmlDocGetRootElement(doc);
265 if (!n || !n->children)
266 return GPG_ERR_NO_VALUE;
268 for (n = n->children; n; n = n->next) {
269 xmlAttrPtr a;
270 xmlChar *val, *target;
271 GSList *tlist;
272 gchar *tmp;
274 if (n->type != XML_ELEMENT_NODE)
275 continue;
277 a = xmlHasProp(n, (xmlChar *)"_name");
279 if (!a || !a->children->content)
280 continue;
282 val = xmlNodeGetContent(a->children);
284 if (!val) {
285 rc = GPG_ERR_ENOMEM;
286 goto fail;
289 tmp = g_strdup_printf("!%s%s", (gchar *)val, verbose ? find_element_node(n->children) ? " 1" : " 0" : "");
291 if (!tmp) {
292 xmlFree(val);
293 rc = GPG_ERR_ENOMEM;
294 goto fail;
297 tlist = g_slist_append(list, tmp);
299 if (!tlist) {
300 xmlFree(val);
301 rc = GPG_ERR_ENOMEM;
302 goto fail;
305 list = tlist;
306 target = node_has_attribute(n, (xmlChar *)"target");
308 if (target) {
309 gchar *t;
311 if (verbose) {
312 gchar **req = split_input_line((gchar *)target, "\t", 0);
313 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
315 if (tnode && req[1]) {
316 tnode = find_elements(doc, tnode->children, req+1, &rc,
317 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
320 g_strfreev(req);
321 t = g_strdup_printf("%s%s", (gchar *)val,
322 tnode && find_element_node(tnode->children) ? " 1" : " 0");
324 else
325 t = g_strdup((gchar *)val);
327 if (!t) {
328 xmlFree(val);
329 xmlFree(target);
330 rc = GPG_ERR_ENOMEM;
331 goto fail;
334 tlist = g_slist_append(list, t);
336 if (!tlist) {
337 g_free(t);
338 xmlFree(target);
339 rc = GPG_ERR_ENOMEM;
340 goto fail;
343 list = tlist;
346 xmlFree(val);
347 xmlFree(target);
350 total = g_slist_length(list);
352 if (!total)
353 return GPG_ERR_NO_VALUE;
355 string = g_string_new(NULL);
357 if (!string) {
358 rc = GPG_ERR_ENOMEM;
359 goto fail;
362 for (i = 0; i < total; i++) {
363 gchar *val = g_slist_nth_data(list, i);
365 g_string_append_printf(string, "%s\n", val);
368 string = g_string_truncate(string, string->len - 1);
369 *result = string;
371 fail:
372 total = g_slist_length(list);
374 for (i = 0; i < total; i++)
375 g_free(g_slist_nth_data(list, i));
377 g_slist_free(list);
378 return rc;
382 * Prevents a sibling element past the current element path with the same
383 * element name.
385 static xmlNodePtr find_stop_node(xmlNodePtr node)
387 xmlNodePtr n;
389 for (n = node->parent->children; n; n = n->next) {
390 if (n == node)
391 return n->next;
394 return NULL;
397 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
398 gpg_error_t *rc, void *data)
400 gint i;
401 gchar **req = path;
402 xmlNodePtr parent = data;
404 for (i = 0; req[i] && *req[i]; i++) {
405 xmlNodePtr n;
407 if (parent && node == parent) {
408 *rc = GPG_ERR_CONFLICT;
409 return NULL;
412 is_literal_element(&req[i]);
414 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
415 (n && n->parent == node->parent)) {
417 *rc = create_new_element(node, req[i], &node);
418 if (*rc)
419 return NULL;
421 else
422 node = n;
425 return node;
428 xmlNodePtr find_text_node(xmlNodePtr node)
430 xmlNodePtr n = node;
432 if (n && n->type == XML_TEXT_NODE)
433 return n;
435 for (n = node; n; n = n->next) {
436 if (n->type == XML_TEXT_NODE)
437 return n;
440 return NULL;
443 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
444 gpg_error_t *rc, void *data)
446 gint i;
447 gchar **req = elements;
449 if (node->type == XML_TEXT_NODE)
450 node = node->parent;
452 for (i = 0; req[i] && *req[i]; i++) {
453 xmlNodePtr n;
456 * Strip the first '!' if needed. If there's another, it's an
457 * rc. The syntax has already been checked before calling this
458 * function.
460 is_literal_element(&req[i]);
461 n = find_element(node, req[i], find_stop_node(node));
464 * If the found element has the same parent as the current element,
465 * they are siblings and the new element needs to be created as a
466 * child of the current element (node).
468 if (n && n->parent == node->parent)
469 n = NULL;
471 if (!n) {
472 *rc = create_new_element(node, req[i], &node);
473 if (*rc)
474 return NULL;
476 else
477 node = n;
480 return node;
483 /* The root element is really req[0]. It is need as a pointer in case there is
484 * a target attribute so it can be updated. */
485 xmlNodePtr find_root_element(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
486 gboolean *target, gint recursion_depth, gboolean stop)
488 xmlNodePtr n = xmlDocGetRootElement(doc);
489 gint depth = 0;
490 gchar *root = g_strdup(*req[0]);
491 gboolean literal = is_literal_element(&root);
493 if (!root) {
494 *rc = GPG_ERR_ENOMEM;
495 return NULL;
498 *rc = 0;
499 recursion_depth++;
501 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
502 xmlChar *t = xmlGetNodePath(n);
504 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
505 xmlFree(t);
506 g_free(root);
507 *rc = GPG_ERR_ELOOP;
508 return NULL;
511 while (n) {
512 if (n->type == XML_ELEMENT_NODE) {
513 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
514 n = n->children;
515 depth++;
516 continue;
519 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"element")) {
520 xmlChar *content = node_has_attribute(n, (xmlChar *)"_name");
522 if (!content)
523 continue;
525 if (xmlStrEqual(content, (xmlChar *)root)) {
526 gchar **nreq, **tmp = NULL;
528 if (literal == TRUE) {
529 xmlFree(content);
530 g_free(root);
531 return n;
534 xmlFree(content);
535 content = node_has_attribute(n, (xmlChar *)"target");
537 if (target)
538 *target = TRUE;
540 if (!content || stop) {
541 if (content)
542 xmlFree(content);
544 g_free(root);
545 return n;
548 if (strchr((gchar *)content, '\t')) {
549 nreq = split_input_line((gchar *)content, "\t", 0);
550 xmlFree(content);
552 #if 0
554 * FIXME ENOMEM
556 if (!nreq) {
557 *rc = GPG_ERR_ENOMEM;
558 return NULL;
560 #endif
562 tmp = *req;
563 tmp = strvcatv(nreq, tmp+1);
564 g_strfreev(nreq);
566 if (!tmp) {
567 g_free(root);
568 *rc = GPG_ERR_ENOMEM;
569 return NULL;
572 g_strfreev(*req);
573 *req = tmp;
575 else {
576 if (strv_printf(&tmp, "%s", content) == FALSE) {
577 xmlFree(content);
578 g_free(root);
579 *rc = GPG_ERR_ENOMEM;
580 return NULL;
583 xmlFree(content);
584 nreq = *req;
585 nreq = strvcatv(tmp, nreq+1);
586 g_strfreev(tmp);
588 if (!nreq) {
589 *rc = GPG_ERR_ENOMEM;
590 g_free(root);
591 return NULL;
594 g_strfreev(*req);
595 *req = nreq;
598 g_free(root);
599 n = find_root_element(doc, req, rc, target, recursion_depth, FALSE);
600 return n;
603 xmlFree(content);
607 n = n->next;
610 g_free(root);
611 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
612 return NULL;
615 xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
617 xmlNodePtr n;
619 if (!node || !element)
620 return NULL;
622 for (n = node; n; n = n->next) {
623 if (n->type != XML_ELEMENT_NODE)
624 continue;
626 if (n == stop)
627 break;
629 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
631 if (a && xmlStrEqual(a, (xmlChar *)element)) {
632 xmlFree(a);
633 return n;
636 xmlFree(a);
639 return NULL;
642 xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
644 xmlAttrPtr a = xmlHasProp(n, attr);
646 if (!a)
647 return NULL;
649 if (!a->children || !a->children->content)
650 return NULL;
652 return xmlGetProp(n, attr);
655 static gboolean element_to_literal(gchar **element)
657 gchar *p = g_strdup_printf("!%s", *element);
659 if (!p)
660 return FALSE;
662 g_free(*element);
663 *element = p;
664 return TRUE;
667 /* Resolves elements in 'req' one at a time. It's recursive in case of
668 * "target" attributes. */
669 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
670 gchar **req, gpg_error_t *rc, gboolean *target,
671 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
672 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
673 gboolean is_list_command, gint recursion_depth, void *data, gboolean stop)
675 xmlNodePtr n, last, last_node;
676 gchar **p;
677 gint found = 0;
679 *rc = 0;
680 recursion_depth++;
682 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
683 xmlChar *t = xmlGetNodePath(node);
685 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
686 xmlFree(t);
687 recursion_depth--;
688 *rc = GPG_ERR_ELOOP;
689 return NULL;
692 for (last_node = last = n = node, p = req; *p && *(*p); p++) {
693 xmlNodePtr tmp;
694 gchar *t = g_strdup(*p);
695 gboolean literal;
697 if (!t) {
698 *rc = GPG_ERR_ENOMEM;
699 return NULL;
702 literal = is_literal_element(&t);
703 n = find_element(last, t, NULL);
704 g_free(t);
706 if (!n) {
707 if (not_found_fn)
708 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
710 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
711 return NULL;
714 last = n->children;
715 last_node = n;
716 found = 1;
718 if (literal == FALSE) {
719 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
720 gchar **nreq = NULL, **nnreq;
722 if (!content) {
723 if (is_list_command == TRUE) {
724 if (element_to_literal(&(*p)) == FALSE) {
725 *rc = GPG_ERR_ENOMEM;
726 return NULL;
730 continue;
733 if (target)
734 *target = TRUE;
736 if (!*(p+1) && stop) {
737 xmlFree(content);
738 return n;
741 if (strchr((gchar *)content, '\t') != NULL) {
742 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
743 xmlFree(content);
744 *rc = GPG_ERR_INV_VALUE;
745 return NULL;
748 else {
749 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
750 xmlFree(content);
751 *rc = GPG_ERR_INV_VALUE;
752 return NULL;
756 xmlFree(content);
757 tmp = find_root_element(doc, &nreq, rc, target, 0, FALSE);
759 if (!tmp) {
760 g_strfreev(nreq);
761 return NULL;
764 if (found_fn) {
765 found_fn(tmp, nreq, rc, p+1, data);
767 if (*rc) {
768 g_strfreev(nreq);
769 return NULL;
773 nnreq = strvcatv(nreq+1, p+1);
774 g_strfreev(nreq);
776 // FIXME ENOMEM
777 if (!nnreq || !*nnreq) {
778 if (nnreq)
779 g_strfreev(nnreq);
781 return tmp;
784 if (tmp->children)
785 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
786 not_found_fn, is_list_command, recursion_depth, data, stop);
787 else {
788 g_strfreev(nnreq);
790 if (not_found_fn)
791 return not_found_fn(tmp, p+1, rc, data);
793 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
794 return NULL;
797 if (*(p+1)) {
798 gchar **zz = p+1, **qq = nnreq;
800 if (g_strv_length(nnreq) > g_strv_length(p+1))
801 qq = nnreq+1;
803 for (; *qq && *zz; zz++) {
804 g_free(*zz);
805 *zz = g_strdup(*qq++);
807 if (!*zz) {
808 *rc = GPG_ERR_ENOMEM;
809 n = NULL;
810 break;
815 g_strfreev(nnreq);
816 return n;
820 return n;
823 static gboolean update_element_list(struct element_list_s *elements)
825 gchar *line;
826 GSList *l;
828 if (!elements || !elements->elements)
829 return TRUE;
831 line = g_strjoinv("\t", elements->elements);
833 if (!line)
834 return FALSE;
836 g_strfreev(elements->elements);
837 elements->elements = NULL;
838 l = g_slist_append(elements->list, line);
840 if (!l)
841 return FALSE;
843 elements->list = l;
844 return TRUE;
847 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
848 struct element_list_s *elements)
850 gpg_error_t rc = 0;
851 xmlNodePtr n;
853 for (n = node; n; n = n->next) {
854 xmlChar *target = NULL;
855 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
857 if (!a)
858 continue;
860 if (n->type != XML_ELEMENT_NODE)
861 goto children;
863 if (elements->verbose) {
864 if (strv_printf(&elements->elements, "%s\t!%s%s", elements->prefix, a, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
865 xmlFree(a);
866 return GPG_ERR_ENOMEM;
869 else if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, a) == FALSE) {
870 xmlFree(a);
871 return GPG_ERR_ENOMEM;
874 if (update_element_list(elements) == FALSE) {
875 xmlFree(a);
876 return GPG_ERR_ENOMEM;
879 target = node_has_attribute(n, (xmlChar *)"target");
881 if (target) {
882 gchar *tmp;
883 gchar *save = elements->prefix;
884 gboolean r = elements->resolving;
886 elements->depth++;
888 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
889 xmlChar *t = xmlGetNodePath(n);
891 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
892 xmlFree(t);
893 xmlFree(target);
894 xmlFree(a);
895 return GPG_ERR_ELOOP;
898 if (elements->verbose) {
899 gchar **req = split_input_line((gchar *)target, "\t", 0);
900 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
902 if (!tnode) {
903 g_strfreev(req);
904 xmlFree(a);
905 xmlFree(target);
906 return rc;
909 tnode = find_elements(doc, tnode->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
910 g_strfreev(req);
912 if (!tnode) {
913 xmlFree(a);
914 xmlFree(target);
915 return rc;
918 if (strv_printf(&elements->elements, "%s\t%s%s", elements->prefix, a, find_element_node(tnode->children) ? " 1" : " 0") == FALSE) {
919 xmlFree(a);
920 xmlFree(target);
921 return GPG_ERR_ENOMEM;
924 else if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, a) == FALSE) {
925 xmlFree(a);
926 xmlFree(target);
927 return GPG_ERR_ENOMEM;
930 tmp = g_strjoinv("\t", elements->elements);
932 if (!tmp) {
933 xmlFree(a);
934 xmlFree(target);
935 return GPG_ERR_ENOMEM;
938 if (update_element_list(elements) == FALSE) {
939 g_free(tmp);
940 xmlFree(a);
941 xmlFree(target);
942 return GPG_ERR_ENOMEM;
945 if (elements->recurse) {
946 if (elements->verbose)
947 tmp[strlen(tmp)-2] = 0;
949 elements->prefix = tmp;
950 elements->resolving = TRUE;
951 rc = create_path_list(doc, elements, (gchar *)target);
952 xmlFree(target);
953 elements->resolving = r;
954 elements->depth--;
955 g_free(tmp);
956 elements->prefix = save;
958 if (rc) {
959 xmlFree(a);
960 return rc;
965 children:
966 if (n->children && elements->recurse) {
967 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, a);
968 gchar *save = elements->prefix;
970 if (!tmp) {
971 xmlFree(a);
972 return GPG_ERR_ENOMEM;
975 elements->prefix = tmp;
976 rc = path_list_recurse(doc, n->children, elements);
977 g_free(elements->prefix);
978 elements->prefix = save;
980 if (rc) {
981 xmlFree(a);
982 return rc;
986 xmlFree(a);
989 return rc;
992 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
993 const gchar *value)
995 gchar *buf;
996 gpg_error_t rc;
998 if (name && !xmlSetProp(node, (xmlChar *)name, (xmlChar *)value))
999 return EPWMD_LIBXML_ERROR;
1001 if (name && xmlStrEqual((xmlChar *)name, (xmlChar *)"_mtime"))
1002 return 0;
1004 buf = g_strdup_printf("%li", time(NULL));
1005 rc = add_attribute(node, "_mtime", buf);
1006 g_free(buf);
1007 return rc;
1011 * From the element path 'path', find sub-nodes and append them to the list.
1013 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
1014 gchar *path)
1016 gpg_error_t rc;
1017 gchar **req, **req_orig;
1018 xmlNodePtr n;
1019 gboolean a_target = FALSE;
1021 req = split_input_line(path, "\t", 0);
1023 if (!req) {
1024 req = split_input_line(path, " ", 0);
1026 if (!req)
1027 return GPG_ERR_SYNTAX;
1030 req_orig = g_strdupv(req);
1032 if (!req_orig) {
1033 rc = GPG_ERR_ENOMEM;
1034 goto fail;
1037 n = find_root_element(doc, &req, &rc, &a_target, 0, FALSE);
1039 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1040 rc = 0;
1041 goto fail;
1043 else if (!n)
1044 goto fail;
1046 if (a_target == TRUE) {
1047 g_free(*req);
1048 *req = g_strdup(*req_orig);
1051 if (*(req+1)) {
1052 gboolean e_target = FALSE;
1054 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL, FALSE);
1056 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1057 rc = 0;
1058 goto fail;
1060 else if (!n)
1061 goto fail;
1064 if (!elements->prefix) {
1066 * FIXME
1068 * If any req_orig element contains no target the element should be
1069 * prefixed with the literal character. Not really crucial if the
1070 * client isn't human because child elements are prefixed for the
1071 * current path. But may be confusing if editing by hand.
1073 elements->prefix = g_strjoinv("\t", req_orig);
1075 if (!elements->prefix) {
1076 rc = GPG_ERR_ENOMEM;
1077 goto fail;
1080 if (elements->verbose) {
1081 if (strv_printf(&elements->elements, "%s%s", elements->prefix, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
1082 rc = GPG_ERR_ENOMEM;
1083 goto fail;
1086 else if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
1087 rc = GPG_ERR_ENOMEM;
1088 goto fail;
1091 if (update_element_list(elements) == FALSE) {
1092 rc = GPG_ERR_ENOMEM;
1093 goto fail;
1097 rc = path_list_recurse(doc, n->children, elements);
1099 fail:
1100 if (req_orig)
1101 g_strfreev(req_orig);
1103 g_strfreev(req);
1104 return rc;
1107 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1108 xmlChar *value, xmlBufferPtr *result, gboolean cmd, const xmlChar *attr)
1110 gint i = value ? nodes->nodeNr - 1 : 0;
1111 xmlBufferPtr buf;
1113 buf = xmlBufferCreate();
1115 if (!buf)
1116 return GPG_ERR_ENOMEM;
1118 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1119 xmlNodePtr n = nodes->nodeTab[i];
1120 gpg_error_t rc;
1122 if (!n)
1123 continue;
1125 if (!value && !attr) {
1126 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1127 *result = buf;
1128 return EPWMD_LIBXML_ERROR;
1131 continue;
1134 if (!attr) {
1135 xmlNodeSetContent(n, value);
1136 rc = update_element_mtime(n);
1138 if (rc)
1139 return rc;
1141 else {
1142 if (!cmd)
1143 rc = add_attribute(n, (gchar *)attr, (gchar *)value);
1144 else
1145 rc = delete_attribute(n, attr);
1147 if (rc)
1148 return rc;
1152 *result = buf;
1153 return 0;
1156 /* Common between a version 1 data file and version < 2.12. */
1157 static gpg_error_t convert_root_element(xmlNodePtr n)
1159 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1160 gpg_error_t rc;
1162 if (a) {
1163 xmlFree(a);
1164 xmlChar *t = xmlGetNodePath(n);
1166 log_write(_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1167 xmlFree(t);
1168 return GPG_ERR_AMBIGUOUS_NAME;
1171 a = xmlGetProp(n, (xmlChar *)"name");
1173 if (a) {
1174 rc = add_attribute(n, "_name", (gchar *)a);
1175 xmlFree(a);
1177 if (rc)
1178 return rc;
1180 rc = delete_attribute(n, (xmlChar *)"name");
1182 if (rc)
1183 return rc;
1185 xmlNodeSetName(n, (xmlChar *)"element");
1188 return 0;
1191 /* For a version 1 data file. */
1192 static gpg_error_t convert_elements_recurse_v1(xmlNodePtr n)
1194 for (; n; n = n->next) {
1195 gpg_error_t rc;
1197 if (n->type == XML_ELEMENT_NODE &&
1198 !xmlStrEqual(n->name, (xmlChar *)"_element")) {
1199 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1201 if (a) {
1202 xmlFree(a);
1203 xmlChar *t = xmlGetNodePath(n);
1205 log_write(_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1206 xmlFree(t);
1207 return GPG_ERR_AMBIGUOUS_NAME;
1210 rc = add_attribute(n, "_name", (gchar *)n->name);
1212 if (rc)
1213 return rc;
1215 xmlNodeSetName(n, (xmlChar *)"element");
1218 rc = convert_elements_recurse_v1(n->children);
1220 if (rc)
1221 return rc;
1224 return 0;
1227 /* Updates the DTD and renames the root "accounts" and "account" elements from
1228 * a version 1 data file. */
1229 gpg_error_t convert_xml(gchar **xml, goffset *len)
1231 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1232 xmlDocPtr doc, new = NULL;
1233 xmlNodePtr n;
1235 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1237 if (!doc)
1238 return EPWMD_LIBXML_ERROR;
1240 gcry_free(*xml);
1241 *xml = NULL;
1242 n = xmlDocGetRootElement(doc);
1243 xmlNodeSetName(n, (xmlChar *)"pwmd");
1245 for (n = n->children; n; n = n->next) {
1246 if (xmlStrEqual(n->name, (xmlChar *)"account")) {
1247 rc = convert_root_element(n);
1249 if (rc) {
1250 xmlFreeDoc(doc);
1251 return rc;
1254 rc = convert_elements_recurse_v1(n->children);
1256 if (rc) {
1257 xmlFreeDoc(doc);
1258 return rc;
1263 new = create_dtd();
1265 if (!new)
1266 goto fail;
1268 n = xmlDocGetRootElement(doc);
1269 xmlDocSetRootElement(new, n);
1270 xmlDocDumpMemory(new, (xmlChar **)xml, (gint *)len);
1271 xmlDocSetRootElement(new, xmlCopyNode(n, 0));
1272 rc = 0;
1274 fail:
1275 if (new)
1276 xmlFreeDoc(new);
1278 xmlFreeDoc(doc);
1279 return rc;
1282 gpg_error_t delete_attribute(xmlNodePtr n, const xmlChar *name)
1284 xmlAttrPtr a;
1286 if ((a = xmlHasProp(n, name)) == NULL)
1287 return GPG_ERR_NOT_FOUND;
1289 if (xmlRemoveProp(a) == -1)
1290 return EPWMD_LIBXML_ERROR;
1292 return update_element_mtime(n);
1295 static gpg_error_t convert_elements_recurse(xmlDocPtr doc, xmlNodePtr n,
1296 guint depth)
1298 gpg_error_t rc;
1300 depth++;
1302 for (n = n->children; n; n = n->next) {
1303 if (n->type == XML_ELEMENT_NODE) {
1304 xmlChar *a = NULL;
1306 if (depth > 1) {
1307 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1308 xmlChar *t = xmlGetNodePath(n);
1310 log_write(_("An existing \"element\" already exists. Please rename this element before converting. Path is: %s"), t);
1311 xmlFree(t);
1312 return GPG_ERR_AMBIGUOUS_NAME;
1315 a = xmlGetProp(n, (xmlChar *)"_name");
1317 if (a) {
1318 xmlFree(a);
1319 xmlChar *t = xmlGetNodePath(n);
1321 log_write(_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1322 xmlFree(t);
1323 return GPG_ERR_AMBIGUOUS_NAME;
1326 xmlChar *tmp = xmlStrdup(n->name);
1328 if (!tmp)
1329 return GPG_ERR_ENOMEM;
1331 xmlNodeSetName(n, (xmlChar *)"element");
1332 rc = add_attribute(n, "_name", (gchar *)tmp);
1333 xmlFree(tmp);
1335 if (rc)
1336 return rc;
1338 else {
1339 rc = convert_root_element(n);
1341 if (rc)
1342 return rc;
1346 if (n->children) {
1347 rc = convert_elements_recurse(doc, n, depth);
1349 if (rc)
1350 return rc;
1354 return 0;
1357 /* Renames ALL elements to the new "element" name. Existing element names are
1358 * stored as an attribute "_name". This was introduced in pwmd 2.12 so
1359 * elements can contain common characters that the XML parser barfs on (an
1360 * email address for example. */
1361 gpg_error_t convert_elements(xmlDocPtr doc)
1363 xmlNodePtr n = xmlDocGetRootElement(doc);
1364 gpg_error_t rc;
1366 log_write(_("Converting pre 2.12 data file..."));
1367 rc = convert_elements_recurse(doc, n, 0);
1369 if (!rc)
1370 log_write(_("Finished converting. Please SAVE to update."));
1372 return rc;
1375 gpg_error_t validate_import(xmlNodePtr node)
1377 gpg_error_t rc;
1379 if (!node)
1380 return 0;
1382 for (xmlNodePtr n = node; n; n = n->next) {
1383 if (n->type == XML_ELEMENT_NODE) {
1384 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1385 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1387 if (!a) {
1388 xmlChar *t = xmlGetNodePath(n);
1390 log_write(_("Missing attribute '_name' at %s."), t);
1391 xmlFree(t);
1392 return GPG_ERR_INV_VALUE;
1395 if (!valid_xml_element(a)) {
1396 xmlChar *t = xmlGetNodePath(n);
1398 log_write(_("'%s' is not a valid element name at %s."), a, t);
1399 xmlFree(a);
1400 xmlFree(t);
1401 return GPG_ERR_INV_VALUE;
1404 xmlFree(a);
1406 else {
1407 xmlChar *t = xmlGetNodePath(n);
1409 log_write(_("Warning: unknown element '%s' at %s. Ignoring."), n->name, t);
1410 xmlFree(t);
1411 continue;
1415 if (n->children) {
1416 rc = validate_import(n->children);
1418 if (rc)
1419 return rc;
1423 return rc;
1426 gpg_error_t update_element_mtime(xmlNodePtr n)
1428 return add_attribute(n, NULL, NULL);
1431 gpg_error_t unlink_node(xmlNodePtr n)
1433 gpg_error_t rc = 0;
1435 if (!n)
1436 return rc;
1438 if (n->parent)
1439 rc = update_element_mtime(n->parent);
1441 xmlUnlinkNode(n);
1442 return rc;