Create an _mtime attribute for new root elements too.
[pwmd.git] / src / xml.c
blob00ecbd8df86da9661485f47e8263d7a8c12935cf
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"
41 * 'element' must be allocated.
43 gboolean is_literal_element(gchar **element)
45 gchar *p;
47 if (!element || !*element)
48 return FALSE;
50 if (*(*element) == '!') {
51 gchar *c;
53 for (p = *element, c = p+1; *c; c++)
54 *p++ = *c;
56 *p = 0;
57 return TRUE;
60 return FALSE;
63 gboolean valid_xml_element(xmlChar *element)
65 if (!element)
66 return FALSE;
68 if (strchr((gchar *)element, '\t') || strchr((gchar *)element, ' ') ||
69 *element == '!')
70 return FALSE;
72 return TRUE;
75 gpg_error_t new_root_element(xmlDocPtr doc, gchar *name)
77 xmlNodePtr root = xmlDocGetRootElement(doc);
78 xmlNodePtr n;
79 gchar *p = name;
81 if (!p || !root)
82 return EPWMD_LIBXML_ERROR;
84 if (*p == '!')
85 p++;
87 if (!valid_xml_element((xmlChar *)p))
88 return GPG_ERR_INV_VALUE;
90 n = xmlNewNode(NULL, (xmlChar *)"element");
91 n = xmlAddChild(root, n);
92 return add_attribute(n, "_name", p);
95 xmlDocPtr create_dtd()
97 xmlDocPtr doc;
98 xmlTextWriterPtr wr = xmlNewTextWriterDoc(&doc, 0);
100 if (!wr)
101 return NULL;
103 if (xmlTextWriterStartDocument(wr, NULL, NULL, NULL))
104 goto fail;
106 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
107 goto fail;
109 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
110 (xmlChar *)"(element)") == -1)
111 goto fail;
113 xmlTextWriterEndDTDElement(wr);
115 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"element",
116 (xmlChar *)"_name CDATA #REQUIRED") == -1)
117 goto fail;
119 xmlTextWriterEndDTDAttlist(wr);
120 xmlTextWriterEndDTD(wr);
122 if (xmlTextWriterStartElement(wr, (xmlChar *)"pwmd"))
123 goto fail;
125 xmlTextWriterEndElement(wr);
126 xmlTextWriterEndDocument(wr);
127 xmlFreeTextWriter(wr);
128 return doc;
130 fail:
131 xmlTextWriterEndDocument(wr);
132 xmlFreeTextWriter(wr);
133 xmlFreeDoc(doc);
134 return NULL;
137 xmlChar *new_document()
139 xmlChar *xml;
140 gint len;
141 xmlDocPtr doc = create_dtd();
143 if (!doc)
144 return NULL;
146 xmlDocDumpMemory(doc, &xml, &len);
147 xmlFreeDoc(doc);
148 return xml;
151 xmlNodePtr find_element_node(xmlNodePtr node)
153 xmlNodePtr n = node;
155 if (n && n->type == XML_ELEMENT_NODE)
156 return n;
158 for (n = node; n; n = n->next) {
159 if (n->type == XML_ELEMENT_NODE)
160 return n;
163 return NULL;
167 * Lists root element names; the value of the attribute "_name" of an element
168 * "element". If there's a target attribute both literal and non-literal
169 * element names will be added. This is the primary reason why XML entities
170 * cannot be used. There wouldn't be a way to get the literal an non-literal
171 * element paths.
173 gpg_error_t list_root_elements(xmlDocPtr doc, GString **result,
174 gboolean verbose)
176 xmlNodePtr n = NULL;
177 GSList *list = NULL;
178 gint total, i;
179 GString *string;
180 gpg_error_t rc = 0;
182 n = xmlDocGetRootElement(doc);
184 if (!n || !n->children)
185 return GPG_ERR_NO_VALUE;
187 for (n = n->children; n; n = n->next) {
188 xmlAttrPtr a;
189 xmlChar *val, *target;
190 GSList *tlist;
191 gchar *tmp;
193 if (n->type != XML_ELEMENT_NODE)
194 continue;
196 a = xmlHasProp(n, (xmlChar *)"_name");
198 if (!a || !a->children->content)
199 continue;
201 val = xmlNodeGetContent(a->children);
203 if (!val) {
204 rc = gpg_error_from_errno(ENOMEM);
205 goto fail;
208 tmp = g_strdup_printf("!%s%s", (gchar *)val, verbose ? find_element_node(n->children) ? " 1" : " 0" : "");
210 if (!tmp) {
211 xmlFree(val);
212 rc = gpg_error_from_errno(ENOMEM);
213 goto fail;
216 tlist = g_slist_append(list, tmp);
218 if (!tlist) {
219 xmlFree(val);
220 rc = gpg_error_from_errno(ENOMEM);
221 goto fail;
224 list = tlist;
225 target = node_has_attribute(n, (xmlChar *)"target");
227 if (target) {
228 gchar *t;
230 if (verbose) {
231 gchar **req = split_input_line((gchar *)target, "\t", 0);
232 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
234 if (tnode && req[1]) {
235 tnode = find_elements(doc, tnode->children, req+1, &rc,
236 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
239 g_strfreev(req);
240 t = g_strdup_printf("%s%s", (gchar *)val,
241 tnode && find_element_node(tnode->children) ? " 1" : " 0");
243 else
244 t = g_strdup((gchar *)val);
246 if (!t) {
247 xmlFree(val);
248 xmlFree(target);
249 rc = gpg_error_from_errno(ENOMEM);
250 goto fail;
253 tlist = g_slist_append(list, t);
255 if (!tlist) {
256 g_free(t);
257 xmlFree(target);
258 rc = gpg_error_from_errno(ENOMEM);
259 goto fail;
262 list = tlist;
265 xmlFree(val);
266 xmlFree(target);
269 total = g_slist_length(list);
271 if (!total)
272 return GPG_ERR_NO_VALUE;
274 string = g_string_new(NULL);
276 if (!string) {
277 rc = gpg_error_from_errno(ENOMEM);
278 goto fail;
281 for (i = 0; i < total; i++) {
282 gchar *val = g_slist_nth_data(list, i);
284 g_string_append_printf(string, "%s\n", val);
287 string = g_string_truncate(string, string->len - 1);
288 *result = string;
290 fail:
291 total = g_slist_length(list);
293 for (i = 0; i < total; i++)
294 g_free(g_slist_nth_data(list, i));
296 g_slist_free(list);
297 return rc;
301 * Prevents a sibling element past the current element path with the same
302 * element name.
304 static xmlNodePtr find_stop_node(xmlNodePtr node)
306 xmlNodePtr n;
308 for (n = node->parent->children; n; n = n->next) {
309 if (n == node)
310 return n->next;
313 return NULL;
317 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
318 * content but as an element.
320 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
321 gpg_error_t *rc, void *data)
323 gint i;
324 char **req = path;
326 for (i = 0; req[i]; i++) {
327 xmlNodePtr n;
329 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
330 (n && n->parent == node->parent)) {
331 is_literal_element(&req[i]);
333 if (!valid_xml_element((xmlChar *)req[i])) {
334 *rc = GPG_ERR_INV_VALUE;
335 return NULL;
338 //n = xmlNewNode(NULL, (xmlChar *)req[i]);
339 n = xmlNewNode(NULL, (xmlChar *)"element");
341 if (!n) {
342 *rc = gpg_error_from_errno(ENOMEM);
343 return NULL;
346 add_attribute(n, "_name", req[i]);
347 node = xmlAddChild(node, n);
349 if (!node) {
350 *rc = gpg_error_from_errno(ENOMEM);
351 return NULL;
354 else
355 node = n;
358 return node;
361 xmlNodePtr find_text_node(xmlNodePtr node)
363 xmlNodePtr n = node;
365 if (n && n->type == XML_TEXT_NODE)
366 return n;
368 for (n = node; n; n = n->next) {
369 if (n->type == XML_TEXT_NODE)
370 return n;
373 return NULL;
376 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
377 gpg_error_t *rc, void *data)
379 gint i;
380 gchar **req = elements;
382 if (node->type == XML_TEXT_NODE)
383 node = node->parent;
385 xmlChar *a = node_has_attribute(node, (xmlChar *)"_name");
387 if (a)
388 xmlFree(a);
390 for (i = 0; req[i]; i++) {
391 xmlNodePtr n;
393 if (req[i+1]) {
395 * Strip the first '!' if needed. If there's another, it's an
396 * rc. The syntax has already been checked before calling this
397 * function.
399 is_literal_element(&req[i]);
403 * The value of the element tree.
405 if (!req[i+1]) {
406 n = find_text_node(node->children);
408 if (!n)
409 /* Use AddContent here to prevent overwriting any children. */
410 xmlNodeAddContent(node, (xmlChar *)req[i]);
411 else if (n && !*req[i])
412 xmlNodeSetContent(n, NULL);
413 else
414 xmlNodeSetContent(n, (xmlChar *)req[i]);
416 break;
419 n = find_element(node, req[i], find_stop_node(node));
422 * If the found element has the same parent as the current element,
423 * they are siblings and the new element needs to be created as a
424 * child of the current element (node).
426 if (n && n->parent == node->parent)
427 n = NULL;
429 if (!n) {
430 if (!valid_xml_element((xmlChar *)req[i])) {
431 *rc = GPG_ERR_INV_VALUE;
432 return NULL;
435 //n = xmlNewNode(NULL, (xmlChar *)req[i]);
436 n = xmlNewNode(NULL, (xmlChar *)"element");
438 if (!n) {
439 *rc = gpg_error_from_errno(ENOMEM);
440 return NULL;
443 *rc = add_attribute(n, "_name", req[i]);
445 if (*rc)
446 return NULL;
448 node = xmlAddChild(node, n);
450 if (!node) {
451 *rc = gpg_error_from_errno(ENOMEM);
452 return NULL;
455 else
456 node = n;
459 return node;
462 /* The root element is really req[0]. It is need as a pointer in case there is
463 * a target attribute so it can be updated. */
464 xmlNodePtr find_root_element(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
465 gboolean *target, gint recursion_depth, gboolean stop)
467 xmlNodePtr n = xmlDocGetRootElement(doc);
468 gint depth = 0;
469 gchar *root = g_strdup(*req[0]);
470 gboolean literal = is_literal_element(&root);
472 if (!root) {
473 *rc = gpg_error_from_errno(ENOMEM);
474 return NULL;
477 *rc = 0;
478 recursion_depth++;
480 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
481 xmlChar *t = xmlGetNodePath(n);
483 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
484 xmlFree(t);
485 g_free(root);
486 *rc = GPG_ERR_ELOOP;
487 return NULL;
490 while (n) {
491 if (n->type == XML_ELEMENT_NODE) {
492 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
493 n = n->children;
494 depth++;
495 continue;
498 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"element")) {
499 xmlChar *content = node_has_attribute(n, (xmlChar *)"_name");
501 if (!content)
502 continue;
504 if (xmlStrEqual(content, (xmlChar *)root)) {
505 gchar **nreq, **tmp = NULL;
507 if (literal == TRUE) {
508 xmlFree(content);
509 g_free(root);
510 return n;
513 xmlFree(content);
514 content = node_has_attribute(n, (xmlChar *)"target");
516 if (target)
517 *target = TRUE;
519 if (!content || stop) {
520 if (content)
521 xmlFree(content);
523 g_free(root);
524 return n;
527 if (strchr((gchar *)content, '\t')) {
528 nreq = split_input_line((gchar *)content, "\t", 0);
529 xmlFree(content);
531 #if 0
533 * FIXME ENOMEM
535 if (!nreq) {
536 *rc = gpg_error_from_errno(ENOMEM);
537 return NULL;
539 #endif
541 tmp = *req;
542 tmp = strvcatv(nreq, tmp+1);
543 g_strfreev(nreq);
545 if (!tmp) {
546 g_free(root);
547 *rc = gpg_error_from_errno(ENOMEM);
548 return NULL;
551 g_strfreev(*req);
552 *req = tmp;
554 else {
555 if (strv_printf(&tmp, "%s", content) == FALSE) {
556 xmlFree(content);
557 g_free(root);
558 *rc = gpg_error_from_errno(ENOMEM);
559 return NULL;
562 xmlFree(content);
563 nreq = *req;
564 nreq = strvcatv(tmp, nreq+1);
565 g_strfreev(tmp);
567 if (!nreq) {
568 *rc = gpg_error_from_errno(ENOMEM);
569 g_free(root);
570 return NULL;
573 g_strfreev(*req);
574 *req = nreq;
577 g_free(root);
578 n = find_root_element(doc, req, rc, target, recursion_depth, FALSE);
579 return n;
582 xmlFree(content);
586 n = n->next;
589 g_free(root);
590 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
591 return NULL;
594 xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
596 xmlNodePtr n;
598 if (!node || !element)
599 return NULL;
601 for (n = node; n; n = n->next) {
602 if (n->type != XML_ELEMENT_NODE)
603 continue;
605 if (n == stop)
606 break;
608 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
610 if (a && xmlStrEqual(a, (xmlChar *)element)) {
611 xmlFree(a);
612 return n;
615 xmlFree(a);
618 return NULL;
621 xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
623 xmlAttrPtr a = xmlHasProp(n, attr);
625 if (!a)
626 return NULL;
628 if (!a->children || !a->children->content)
629 return NULL;
631 return xmlGetProp(n, attr);
634 static gboolean element_to_literal(gchar **element)
636 gchar *p = g_strdup_printf("!%s", *element);
638 if (!p)
639 return FALSE;
641 g_free(*element);
642 *element = p;
643 return TRUE;
646 /* Resolves elements in 'req' one at a time. It's recursive in case of
647 * "target" attributes. */
648 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
649 gchar **req, gpg_error_t *rc, gboolean *target,
650 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
651 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
652 gboolean is_list_command, gint recursion_depth, void *data, gboolean stop)
654 xmlNodePtr n, last, last_node;
655 gchar **p;
656 gint found = 0;
658 *rc = 0;
659 recursion_depth++;
661 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
662 xmlChar *t = xmlGetNodePath(node);
664 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
665 xmlFree(t);
666 recursion_depth--;
667 *rc = GPG_ERR_ELOOP;
668 return NULL;
671 for (last_node = last = n = node, p = req; *p; p++) {
672 xmlNodePtr tmp;
673 gchar *t = g_strdup(*p);
674 gboolean literal;
676 if (!t) {
677 *rc = gpg_error_from_errno(ENOMEM);
678 return NULL;
681 literal = is_literal_element(&t);
682 n = find_element(last, t, NULL);
683 g_free(t);
685 if (!n) {
686 if (not_found_fn)
687 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
689 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
690 return NULL;
693 last = n->children;
694 last_node = n;
695 found = 1;
697 if (literal == FALSE) {
698 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
699 gchar **nreq = NULL, **nnreq;
701 if (!content) {
702 if (is_list_command == TRUE) {
703 if (element_to_literal(&(*p)) == FALSE) {
704 *rc = gpg_error_from_errno(ENOMEM);
705 return NULL;
709 continue;
712 if (target)
713 *target = TRUE;
715 if (!*(p+1) && stop) {
716 xmlFree(content);
717 return n;
720 if (strchr((gchar *)content, '\t') != NULL) {
721 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
722 xmlFree(content);
723 *rc = GPG_ERR_INV_VALUE;
724 return NULL;
727 else {
728 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
729 xmlFree(content);
730 *rc = GPG_ERR_INV_VALUE;
731 return NULL;
735 xmlFree(content);
736 tmp = find_root_element(doc, &nreq, rc, target, 0, FALSE);
738 if (!tmp) {
739 g_strfreev(nreq);
740 return NULL;
743 if (found_fn) {
744 found_fn(tmp, nreq, rc, p+1, data);
746 if (*rc) {
747 g_strfreev(nreq);
748 return NULL;
752 nnreq = strvcatv(nreq+1, p+1);
753 g_strfreev(nreq);
755 // FIXME ENOMEM
756 if (!nnreq || !*nnreq) {
757 if (nnreq)
758 g_strfreev(nnreq);
760 return tmp;
763 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
764 not_found_fn, is_list_command, recursion_depth, data, stop);
766 if (*(p+1)) {
767 gchar **zz = p+1, **qq = nnreq;
769 if (g_strv_length(nnreq) > g_strv_length(p+1))
770 qq = nnreq+1;
772 for (; *qq && *zz; zz++) {
773 g_free(*zz);
774 *zz = g_strdup(*qq++);
776 if (!*zz) {
777 *rc = gpg_error_from_errno(ENOMEM);
778 n = NULL;
779 break;
784 g_strfreev(nnreq);
785 return n;
789 return n;
792 static gboolean update_element_list(struct element_list_s *elements)
794 gchar *line;
795 GSList *l;
797 if (!elements || !elements->elements)
798 return TRUE;
800 line = g_strjoinv("\t", elements->elements);
802 if (!line)
803 return FALSE;
805 g_strfreev(elements->elements);
806 elements->elements = NULL;
807 l = g_slist_append(elements->list, line);
809 if (!l)
810 return FALSE;
812 elements->list = l;
813 return TRUE;
816 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
817 struct element_list_s *elements)
819 gpg_error_t rc = 0;
820 xmlNodePtr n;
822 for (n = node; n; n = n->next) {
823 xmlChar *target = NULL;
824 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
826 if (!a)
827 continue;
829 if (n->type != XML_ELEMENT_NODE)
830 goto children;
832 if (elements->verbose) {
833 if (strv_printf(&elements->elements, "%s\t!%s%s", elements->prefix, a, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
834 xmlFree(a);
835 return gpg_err_code_from_errno(ENOMEM);
838 else if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, a) == FALSE) {
839 xmlFree(a);
840 return gpg_err_code_from_errno(ENOMEM);
843 if (update_element_list(elements) == FALSE) {
844 xmlFree(a);
845 return gpg_err_code_from_errno(ENOMEM);
848 target = node_has_attribute(n, (xmlChar *)"target");
850 if (target) {
851 gchar *tmp;
852 gchar *save = elements->prefix;
853 gboolean r = elements->resolving;
855 elements->depth++;
857 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
858 xmlChar *t = xmlGetNodePath(n);
860 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
861 xmlFree(t);
862 xmlFree(target);
863 xmlFree(a);
864 return GPG_ERR_ELOOP;
867 if (elements->verbose) {
868 gchar **req = split_input_line((gchar *)target, "\t", 0);
869 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
871 if (!tnode) {
872 g_strfreev(req);
873 xmlFree(a);
874 xmlFree(target);
875 return rc;
878 tnode = find_elements(doc, tnode->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
879 g_strfreev(req);
881 if (!tnode) {
882 xmlFree(a);
883 xmlFree(target);
884 return rc;
887 if (strv_printf(&elements->elements, "%s\t%s%s", elements->prefix, a, find_element_node(tnode->children) ? " 1" : " 0") == FALSE) {
888 xmlFree(a);
889 xmlFree(target);
890 return gpg_err_code_from_errno(ENOMEM);
893 else if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, a) == FALSE) {
894 xmlFree(a);
895 xmlFree(target);
896 return gpg_err_code_from_errno(ENOMEM);
899 tmp = g_strjoinv("\t", elements->elements);
901 if (!tmp) {
902 xmlFree(a);
903 xmlFree(target);
904 return gpg_err_code_from_errno(ENOMEM);
907 if (update_element_list(elements) == FALSE) {
908 g_free(tmp);
909 xmlFree(a);
910 xmlFree(target);
911 return gpg_err_code_from_errno(ENOMEM);
914 if (elements->recurse) {
915 if (elements->verbose)
916 tmp[strlen(tmp)-2] = 0;
918 elements->prefix = tmp;
919 elements->resolving = TRUE;
920 rc = create_path_list(doc, elements, (gchar *)target);
921 xmlFree(target);
922 elements->resolving = r;
923 elements->depth--;
924 g_free(tmp);
925 elements->prefix = save;
927 if (rc) {
928 xmlFree(a);
929 return rc;
934 children:
935 if (n->children && elements->recurse) {
936 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, a);
937 gchar *save = elements->prefix;
939 if (!tmp) {
940 xmlFree(a);
941 return gpg_err_code_from_errno(ENOMEM);
944 elements->prefix = tmp;
945 rc = path_list_recurse(doc, n->children, elements);
946 g_free(elements->prefix);
947 elements->prefix = save;
949 if (rc) {
950 xmlFree(a);
951 return rc;
955 xmlFree(a);
958 return rc;
961 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
962 const gchar *value)
964 gchar *buf;
965 gpg_error_t rc;
967 if (name && !xmlSetProp(node, (xmlChar *)name, (xmlChar *)value))
968 return EPWMD_LIBXML_ERROR;
970 if (name && xmlStrEqual((xmlChar *)name, (xmlChar *)"_mtime"))
971 return 0;
973 buf = g_strdup_printf("%li", time(NULL));
974 rc = add_attribute(node, "_mtime", buf);
975 g_free(buf);
976 return rc;
980 * From the element path 'path', find sub-nodes and append them to the list.
982 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
983 gchar *path)
985 gpg_error_t rc;
986 gchar **req, **req_orig;
987 xmlNodePtr n;
988 gboolean a_target = FALSE;
990 req = split_input_line(path, "\t", 0);
992 if (!req) {
993 req = split_input_line(path, " ", 0);
995 if (!req)
996 return GPG_ERR_SYNTAX;
999 req_orig = g_strdupv(req);
1001 if (!req_orig) {
1002 rc = gpg_err_code_from_errno(ENOMEM);
1003 goto fail;
1006 n = find_root_element(doc, &req, &rc, &a_target, 0, FALSE);
1008 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1009 rc = 0;
1010 goto fail;
1012 else if (!n)
1013 goto fail;
1015 if (a_target == TRUE) {
1016 g_free(*req);
1017 *req = g_strdup(*req_orig);
1020 if (*(req+1)) {
1021 gboolean e_target = FALSE;
1023 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL, FALSE);
1025 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1026 rc = 0;
1027 goto fail;
1029 else if (!n)
1030 goto fail;
1033 if (!elements->prefix) {
1035 * FIXME
1037 * If any req_orig element contains no target the element should be
1038 * prefixed with the literal character. Not really crucial if the
1039 * client isn't human because child elements are prefixed for the
1040 * current path. But may be confusing if editing by hand.
1042 elements->prefix = g_strjoinv("\t", req_orig);
1044 if (!elements->prefix) {
1045 rc = gpg_err_code_from_errno(ENOMEM);
1046 goto fail;
1049 if (elements->verbose) {
1050 if (strv_printf(&elements->elements, "%s%s", elements->prefix, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
1051 rc = gpg_err_code_from_errno(ENOMEM);
1052 goto fail;
1055 else if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
1056 rc = gpg_err_code_from_errno(ENOMEM);
1057 goto fail;
1060 if (update_element_list(elements) == FALSE) {
1061 rc = gpg_err_code_from_errno(ENOMEM);
1062 goto fail;
1066 rc = path_list_recurse(doc, n->children, elements);
1068 fail:
1069 if (req_orig)
1070 g_strfreev(req_orig);
1072 g_strfreev(req);
1073 return rc;
1076 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1077 xmlChar *value, xmlBufferPtr *result, gboolean cmd, const xmlChar *attr)
1079 gint i = value ? nodes->nodeNr - 1 : 0;
1080 xmlBufferPtr buf;
1082 buf = xmlBufferCreate();
1084 if (!buf)
1085 return gpg_err_code_from_errno(ENOMEM);
1087 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1088 xmlNodePtr n = nodes->nodeTab[i];
1089 gpg_error_t rc;
1091 if (!n)
1092 continue;
1094 if (!value && !attr) {
1095 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1096 *result = buf;
1097 return EPWMD_LIBXML_ERROR;
1100 continue;
1103 if (!attr) {
1104 xmlNodeSetContent(n, value);
1105 rc = update_element_mtime(n);
1107 if (rc)
1108 return rc;
1110 else {
1111 if (!cmd)
1112 rc = add_attribute(n, (gchar *)attr, (gchar *)value);
1113 else
1114 rc = delete_attribute(n, attr);
1116 if (rc)
1117 return rc;
1121 *result = buf;
1122 return 0;
1125 static gpg_error_t convert_root_element(xmlNodePtr n)
1127 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1128 gpg_error_t rc;
1130 if (a) {
1131 xmlFree(a);
1132 xmlChar *t = xmlGetNodePath(n);
1134 log_write(N_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1135 xmlFree(t);
1136 return GPG_ERR_AMBIGUOUS_NAME;
1139 a = xmlGetProp(n, (xmlChar *)"name");
1141 if (a) {
1142 rc = add_attribute(n, "_name", (gchar *)a);
1143 xmlFree(a);
1145 if (rc)
1146 return rc;
1148 rc = delete_attribute(n, (xmlChar *)"name");
1150 if (rc)
1151 return rc;
1153 xmlNodeSetName(n, (xmlChar *)"element");
1156 return 0;
1159 /* Updates the DTD and renames the root "accounts" and "account" elements. */
1160 gpg_error_t convert_xml(gchar **xml, goffset *len)
1162 gpg_error_t rc = EPWMD_LIBXML_ERROR;
1163 xmlDocPtr doc, new = NULL;
1164 xmlNodePtr n;
1166 doc = xmlReadMemory(*xml, *len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1168 if (!doc)
1169 return EPWMD_LIBXML_ERROR;
1171 gcry_free(*xml);
1172 *xml = NULL;
1173 n = xmlDocGetRootElement(doc);
1174 xmlNodeSetName(n, (xmlChar *)"pwmd");
1176 for (n = n->children; n; n = n->next) {
1177 if (xmlStrEqual(n->name, (xmlChar *)"account")) {
1178 rc = convert_root_element(n);
1180 if (rc) {
1181 xmlFreeDoc(doc);
1182 return rc;
1187 new = create_dtd();
1189 if (!new)
1190 goto fail;
1192 n = xmlDocGetRootElement(doc);
1193 xmlDocSetRootElement(new, n);
1194 xmlDocDumpMemory(new, (xmlChar **)xml, (gint *)len);
1195 xmlDocSetRootElement(new, xmlCopyNode(n, 0));
1196 rc = 0;
1198 fail:
1199 if (new)
1200 xmlFreeDoc(new);
1202 xmlFreeDoc(doc);
1203 return rc;
1206 gpg_error_t delete_attribute(xmlNodePtr n, const xmlChar *name)
1208 xmlAttrPtr a;
1210 if ((a = xmlHasProp(n, name)) == NULL)
1211 return GPG_ERR_NOT_FOUND;
1213 if (xmlRemoveProp(a) == -1)
1214 return EPWMD_LIBXML_ERROR;
1216 return update_element_mtime(n);
1219 static gpg_error_t convert_elements_recurse(xmlDocPtr doc, xmlNodePtr n,
1220 guint depth)
1222 gpg_error_t rc;
1224 depth++;
1226 for (n = n->children; n; n = n->next) {
1227 if (n->type == XML_ELEMENT_NODE) {
1228 xmlChar *a = NULL;
1230 if (depth > 1) {
1231 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1232 xmlChar *t = xmlGetNodePath(n);
1234 log_write(N_("An existing \"element\" already exists. Please rename this element before converting. Path is: %s"), t);
1235 xmlFree(t);
1236 return GPG_ERR_AMBIGUOUS_NAME;
1239 a = xmlGetProp(n, (xmlChar *)"_name");
1241 if (a) {
1242 xmlFree(a);
1243 xmlChar *t = xmlGetNodePath(n);
1245 log_write(N_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1246 xmlFree(t);
1247 return GPG_ERR_AMBIGUOUS_NAME;
1250 xmlChar *tmp = xmlStrdup(n->name);
1252 if (!tmp)
1253 return gpg_error_from_errno(ENOMEM);
1255 xmlNodeSetName(n, (xmlChar *)"element");
1256 rc = add_attribute(n, "_name", (gchar *)tmp);
1257 xmlFree(tmp);
1259 if (rc)
1260 return rc;
1262 else {
1263 rc = convert_root_element(n);
1265 if (rc)
1266 return rc;
1270 if (n->children) {
1271 rc = convert_elements_recurse(doc, n, depth);
1273 if (rc)
1274 return rc;
1278 return 0;
1281 /* Renames ALL elements to the new "element" name. Existing element names are
1282 * stored as an attribute "_name". This was introduced in pwmd 2.12 so
1283 * elements can contain common characters that the XML parser barfs on (an
1284 * email address for example. */
1285 gpg_error_t convert_elements(xmlDocPtr doc)
1287 xmlNodePtr n = xmlDocGetRootElement(doc);
1288 gpg_error_t rc;
1290 log_write(N_("Converting pre 2.12 data file..."));
1291 rc = convert_elements_recurse(doc, n, 0);
1293 if (!rc)
1294 log_write(N_("Finished converting. Please SAVE to update."));
1296 return rc;
1299 gpg_error_t validate_import(xmlNodePtr node)
1301 gpg_error_t rc;
1303 if (!node)
1304 return 0;
1306 for (xmlNodePtr n = node; n; n = n->next) {
1307 if (n->type == XML_ELEMENT_NODE) {
1308 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1309 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1311 if (!a) {
1312 xmlChar *t = xmlGetNodePath(n);
1314 log_write(N_("Missing attribute '_name' at %s."), t);
1315 xmlFree(t);
1316 return GPG_ERR_INV_VALUE;
1319 if (!valid_xml_element(a)) {
1320 xmlChar *t = xmlGetNodePath(n);
1322 log_write(N_("'%s' is not a valid element name at %s."), a, t);
1323 xmlFree(a);
1324 xmlFree(t);
1325 return GPG_ERR_INV_VALUE;
1328 xmlFree(a);
1330 else {
1331 xmlChar *t = xmlGetNodePath(n);
1333 log_write(N_("Warning: unknown element '%s' at %s. Ignoring."), n->name, t);
1334 xmlFree(t);
1335 continue;
1339 if (n->children) {
1340 rc = validate_import(n->children);
1342 if (rc)
1343 return rc;
1347 return rc;
1350 gpg_error_t update_element_mtime(xmlNodePtr n)
1352 return add_attribute(n, NULL, NULL);