Strip the literal element character before finding and creating target
[pwmd.git] / src / xml.c
blob7d74c379d25809b1c7eda5ea83970429d4c4bddf
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011
4 Ben Kibbey <bjk@luxsci.net>
6 This file is part of pwmd.
8 Pwmd is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
13 Pwmd is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <err.h>
29 #include <string.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <glib.h>
34 #include <libxml/xmlwriter.h>
36 #include "pwmd-error.h"
37 #include "misc.h"
38 #include "xml.h"
39 #include "mem.h"
40 #include "rcfile.h"
42 extern void log_write(const gchar *fmt, ...);
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;
67 gboolean valid_xml_element(xmlChar *element)
69 gchar *p = (gchar *)element;
71 if (!element || !*element || *element == '!')
72 return FALSE;
74 for (; *p; p++) {
75 if (g_ascii_isspace(*p))
76 return FALSE;
79 return TRUE;
82 gboolean valid_element_path(gchar **path, gboolean with_content)
84 gchar **dup = NULL, **p;
85 gint i, t;
87 if (!path || !*path)
88 return FALSE;
90 /* Save some memory by not duplicating the element content. */
91 if (with_content) {
92 t = g_strv_length(path);
93 for (i = 0; i < t-1; i++) {
94 gchar **tmp = g_realloc(dup, (i+2)*sizeof(gchar *));
96 if (!tmp) {
97 g_strfreev(dup);
98 return FALSE;
101 dup = tmp;
102 dup[i] = g_strdup(path[i]);
103 dup[i+1] = NULL;
106 else
107 dup = g_strdupv(path);
109 if (!dup)
110 return FALSE;
112 for (p = dup; *p && *(*p); p++) {
113 is_literal_element(&(*p));
114 if (!valid_xml_element((xmlChar *)*p)) {
115 g_strfreev(dup);
116 return FALSE;
120 g_strfreev(dup);
121 return TRUE;
124 gpg_error_t attr_ctime(xmlNodePtr n)
126 gchar *buf = g_strdup_printf("%li", time(NULL));
127 gpg_error_t rc;
129 if (!buf)
130 return GPG_ERR_ENOMEM;
132 rc = add_attribute(n, "_ctime", buf);
133 g_free(buf);
134 return rc;
137 static gpg_error_t create_new_element(xmlNodePtr parent, const gchar *name,
138 xmlNodePtr *result)
140 xmlNodePtr n = xmlNewNode(NULL, (xmlChar *)"element");
141 gpg_error_t rc;
143 if (!n)
144 return GPG_ERR_ENOMEM;
146 rc = add_attribute(n, "_name", name);
147 if (!rc)
148 rc = attr_ctime(n);
150 if (!rc) {
151 if (result)
152 *result = xmlAddChild(parent, n);
153 else
154 (void)xmlAddChild(parent, n);
156 else
157 xmlFreeNode(n);
159 return rc;
162 gpg_error_t new_root_element(xmlDocPtr doc, gchar *name)
164 xmlNodePtr root = xmlDocGetRootElement(doc);
165 gchar *p = name;
167 if (!p || !root)
168 return GPG_ERR_BAD_DATA;
170 if (*p == '!')
171 p++;
173 if (!valid_xml_element((xmlChar *)p))
174 return GPG_ERR_INV_VALUE;
176 return create_new_element(root, p, NULL);
179 static xmlDocPtr create_dtd()
181 xmlDocPtr doc;
182 xmlTextWriterPtr wr = xmlNewTextWriterDoc(&doc, 0);
184 if (!wr)
185 return NULL;
187 if (xmlTextWriterStartDocument(wr, NULL, NULL, NULL))
188 goto fail;
190 if (xmlTextWriterStartDTD(wr, (xmlChar *)"pwmd", NULL, NULL) == -1)
191 goto fail;
193 if (xmlTextWriterWriteDTDElement(wr, (xmlChar *)"pwmd",
194 (xmlChar *)"(element)") == -1)
195 goto fail;
197 xmlTextWriterEndDTDElement(wr);
199 if (xmlTextWriterWriteDTDAttlist(wr, (xmlChar *)"element",
200 (xmlChar *)"_name CDATA #REQUIRED") == -1)
201 goto fail;
203 xmlTextWriterEndDTDAttlist(wr);
204 xmlTextWriterEndDTD(wr);
206 if (xmlTextWriterStartElement(wr, (xmlChar *)"pwmd"))
207 goto fail;
209 xmlTextWriterEndElement(wr);
210 xmlTextWriterEndDocument(wr);
211 xmlFreeTextWriter(wr);
212 return doc;
214 fail:
215 xmlTextWriterEndDocument(wr);
216 xmlFreeTextWriter(wr);
217 xmlFreeDoc(doc);
218 return NULL;
221 xmlDocPtr new_document()
223 return create_dtd();
226 xmlNodePtr find_element_node(xmlNodePtr node)
228 xmlNodePtr n = node;
230 if (n && n->type == XML_ELEMENT_NODE)
231 return n;
233 for (n = node; n; n = n->next) {
234 if (n->type == XML_ELEMENT_NODE)
235 return n;
238 return NULL;
242 * Lists root element names; the value of the attribute "_name" of an element
243 * "element". If there's a target attribute both literal and non-literal
244 * element names will be added. This is the primary reason why XML entities
245 * cannot be used. There wouldn't be a way to get the literal an non-literal
246 * element paths.
248 gpg_error_t list_root_elements(xmlDocPtr doc, GString **result,
249 gboolean verbose)
251 xmlNodePtr n = NULL;
252 GSList *list = NULL;
253 gint total, i;
254 GString *string;
255 gpg_error_t rc = 0;
257 n = xmlDocGetRootElement(doc);
259 if (!n || !n->children)
260 return GPG_ERR_NO_DATA;
262 for (n = n->children; n; n = n->next) {
263 xmlAttrPtr a;
264 xmlChar *val, *target;
265 GSList *tlist;
266 gchar *tmp;
268 if (n->type != XML_ELEMENT_NODE)
269 continue;
271 a = xmlHasProp(n, (xmlChar *)"_name");
273 if (!a || !a->children->content)
274 continue;
276 val = xmlNodeGetContent(a->children);
278 if (!val) {
279 rc = GPG_ERR_ENOMEM;
280 goto fail;
283 tmp = g_strdup_printf("!%s%s", (gchar *)val, verbose ? find_element_node(n->children) ? " 1" : " 0" : "");
285 if (!tmp) {
286 xmlFree(val);
287 rc = GPG_ERR_ENOMEM;
288 goto fail;
291 tlist = g_slist_append(list, tmp);
293 if (!tlist) {
294 xmlFree(val);
295 rc = GPG_ERR_ENOMEM;
296 goto fail;
299 list = tlist;
300 target = node_has_attribute(n, (xmlChar *)"target");
302 if (target) {
303 gchar *t;
305 if (verbose) {
306 gchar **req = split_input_line((gchar *)target, "\t", 0);
307 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
309 if (tnode && req[1]) {
310 tnode = find_elements(doc, tnode->children, req+1, &rc,
311 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
314 g_strfreev(req);
315 t = g_strdup_printf("%s%s", (gchar *)val,
316 tnode && find_element_node(tnode->children) ? " 1" : " 0");
318 else
319 t = g_strdup((gchar *)val);
321 if (!t) {
322 xmlFree(val);
323 xmlFree(target);
324 rc = GPG_ERR_ENOMEM;
325 goto fail;
328 tlist = g_slist_append(list, t);
330 if (!tlist) {
331 g_free(t);
332 xmlFree(target);
333 rc = GPG_ERR_ENOMEM;
334 goto fail;
337 list = tlist;
340 xmlFree(val);
341 xmlFree(target);
344 total = g_slist_length(list);
346 if (!total)
347 return GPG_ERR_NO_DATA;
349 string = g_string_new(NULL);
351 if (!string) {
352 rc = GPG_ERR_ENOMEM;
353 goto fail;
356 for (i = 0; i < total; i++) {
357 gchar *val = g_slist_nth_data(list, i);
359 g_string_append_printf(string, "%s\n", val);
362 string = g_string_truncate(string, string->len - 1);
363 *result = string;
365 fail:
366 total = g_slist_length(list);
368 for (i = 0; i < total; i++)
369 g_free(g_slist_nth_data(list, i));
371 g_slist_free(list);
372 return rc;
376 * Prevents a sibling element past the current element path with the same
377 * element name.
379 static xmlNodePtr find_stop_node(xmlNodePtr node)
381 xmlNodePtr n;
383 for (n = node->parent->children; n; n = n->next) {
384 if (n == node)
385 return n->next;
388 return NULL;
391 xmlNodePtr create_target_elements_cb(xmlNodePtr node, gchar **path,
392 gpg_error_t *rc, void *data)
394 gint i;
395 gchar **req = path;
396 xmlNodePtr parent = data;
398 for (i = 0; req[i] && *req[i]; i++) {
399 xmlNodePtr n;
401 if (parent && node == parent) {
402 *rc = GPG_ERR_CONFLICT;
403 return NULL;
406 is_literal_element(&req[i]);
408 if ((n = find_element(node, req[i], find_stop_node(node))) == NULL ||
409 (n && n->parent == node->parent)) {
411 *rc = create_new_element(node, req[i], &node);
412 if (*rc)
413 return NULL;
415 else
416 node = n;
419 return node;
422 xmlNodePtr find_text_node(xmlNodePtr node)
424 xmlNodePtr n = node;
426 if (n && n->type == XML_TEXT_NODE)
427 return n;
429 for (n = node; n; n = n->next) {
430 if (n->type == XML_TEXT_NODE)
431 return n;
434 return NULL;
437 xmlNodePtr create_elements_cb(xmlNodePtr node, gchar **elements,
438 gpg_error_t *rc, void *data)
440 gint i;
441 gchar **req = elements;
443 if (node->type == XML_TEXT_NODE)
444 node = node->parent;
446 for (i = 0; req[i] && *req[i]; i++) {
447 xmlNodePtr n;
450 * Strip the first '!' if needed. If there's another, it's an
451 * rc. The syntax has already been checked before calling this
452 * function.
454 is_literal_element(&req[i]);
455 n = find_element(node, req[i], find_stop_node(node));
458 * If the found element has the same parent as the current element,
459 * they are siblings and the new element needs to be created as a
460 * child of the current element (node).
462 if (n && n->parent == node->parent)
463 n = NULL;
465 if (!n) {
466 *rc = create_new_element(node, req[i], &node);
467 if (*rc)
468 return NULL;
470 else
471 node = n;
474 return node;
477 /* The root element is really req[0]. It is need as a pointer in case there is
478 * a target attribute so it can be updated. */
479 xmlNodePtr find_root_element(xmlDocPtr doc, gchar ***req, gpg_error_t *rc,
480 gboolean *target, gint recursion_depth, gboolean stop)
482 xmlNodePtr n = xmlDocGetRootElement(doc);
483 gint depth = 0;
484 gchar *root = g_strdup(*req[0]);
485 gboolean literal = is_literal_element(&root);
487 if (!root) {
488 *rc = GPG_ERR_ENOMEM;
489 return NULL;
492 *rc = 0;
493 recursion_depth++;
495 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
496 xmlChar *t = xmlGetNodePath(n);
498 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
499 xmlFree(t);
500 g_free(root);
501 *rc = GPG_ERR_ELOOP;
502 return NULL;
505 while (n) {
506 if (n->type == XML_ELEMENT_NODE) {
507 if (depth == 0 && xmlStrEqual(n->name, (xmlChar *)"pwmd")) {
508 n = n->children;
509 depth++;
510 continue;
513 if (depth == 1 && xmlStrEqual(n->name, (xmlChar *)"element")) {
514 xmlChar *content = node_has_attribute(n, (xmlChar *)"_name");
516 if (!content)
517 continue;
519 if (xmlStrEqual(content, (xmlChar *)root)) {
520 gchar **nreq, **tmp = NULL;
522 if (literal == TRUE) {
523 xmlFree(content);
524 g_free(root);
525 return n;
528 xmlFree(content);
529 content = node_has_attribute(n, (xmlChar *)"target");
531 if (target)
532 *target = TRUE;
534 if (!content || stop) {
535 if (content)
536 xmlFree(content);
538 g_free(root);
539 return n;
542 if (strchr((gchar *)content, '\t')) {
543 nreq = split_input_line((gchar *)content, "\t", 0);
544 xmlFree(content);
546 #if 0
548 * FIXME ENOMEM
550 if (!nreq) {
551 *rc = GPG_ERR_ENOMEM;
552 return NULL;
554 #endif
556 tmp = *req;
557 tmp = strvcatv(nreq, tmp+1);
558 g_strfreev(nreq);
560 if (!tmp) {
561 g_free(root);
562 *rc = GPG_ERR_ENOMEM;
563 return NULL;
566 g_strfreev(*req);
567 *req = tmp;
569 else {
570 if (strv_printf(&tmp, "%s", content) == FALSE) {
571 xmlFree(content);
572 g_free(root);
573 *rc = GPG_ERR_ENOMEM;
574 return NULL;
577 xmlFree(content);
578 nreq = *req;
579 nreq = strvcatv(tmp, nreq+1);
580 g_strfreev(tmp);
582 if (!nreq) {
583 *rc = GPG_ERR_ENOMEM;
584 g_free(root);
585 return NULL;
588 g_strfreev(*req);
589 *req = nreq;
592 g_free(root);
593 n = find_root_element(doc, req, rc, target, recursion_depth, FALSE);
594 return n;
597 xmlFree(content);
601 n = n->next;
604 g_free(root);
605 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
606 return NULL;
609 xmlNodePtr find_element(xmlNodePtr node, gchar *element, xmlNodePtr stop)
611 xmlNodePtr n;
613 if (!node || !element)
614 return NULL;
616 for (n = node; n; n = n->next) {
617 if (n->type != XML_ELEMENT_NODE)
618 continue;
620 if (n == stop)
621 break;
623 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
625 if (a && xmlStrEqual(a, (xmlChar *)element)) {
626 xmlFree(a);
627 return n;
630 xmlFree(a);
633 return NULL;
636 xmlChar *node_has_attribute(xmlNodePtr n, xmlChar *attr)
638 xmlAttrPtr a = xmlHasProp(n, attr);
640 if (!a)
641 return NULL;
643 if (!a->children || !a->children->content)
644 return NULL;
646 return xmlGetProp(n, attr);
649 static gboolean element_to_literal(gchar **element)
651 gchar *p = g_strdup_printf("!%s", *element);
653 if (!p)
654 return FALSE;
656 g_free(*element);
657 *element = p;
658 return TRUE;
661 /* Resolves elements in 'req' one at a time. It's recursive in case of
662 * "target" attributes. */
663 xmlNodePtr find_elements(xmlDocPtr doc, xmlNodePtr node,
664 gchar **req, gpg_error_t *rc, gboolean *target,
665 xmlNodePtr (*found_fn)(xmlNodePtr, gchar **, gpg_error_t *, gchar **, void *),
666 xmlNodePtr (*not_found_fn)(xmlNodePtr, gchar **, gpg_error_t *, void *),
667 gboolean is_list_command, gint recursion_depth, void *data, gboolean stop)
669 xmlNodePtr n, last, last_node;
670 gchar **p;
671 gint found = 0;
673 *rc = 0;
674 recursion_depth++;
676 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth) {
677 xmlChar *t = xmlGetNodePath(node);
679 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
680 xmlFree(t);
681 recursion_depth--;
682 *rc = GPG_ERR_ELOOP;
683 return NULL;
686 for (last_node = last = n = node, p = req; *p && *(*p); p++) {
687 xmlNodePtr tmp;
688 gchar *t = g_strdup(*p);
689 gboolean literal;
691 if (!t) {
692 *rc = GPG_ERR_ENOMEM;
693 return NULL;
696 literal = is_literal_element(&t);
697 n = find_element(last, t, NULL);
698 g_free(t);
700 if (!n) {
701 if (not_found_fn)
702 return not_found_fn(found ? last_node : last_node->parent, p, rc, data);
704 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
705 return NULL;
708 last = n->children;
709 last_node = n;
710 found = 1;
712 if (literal == FALSE) {
713 xmlChar *content = node_has_attribute(n, (xmlChar *)"target");
714 gchar **nreq = NULL, **nnreq;
716 if (!content) {
717 if (is_list_command == TRUE) {
718 if (element_to_literal(&(*p)) == FALSE) {
719 *rc = GPG_ERR_ENOMEM;
720 return NULL;
724 continue;
727 if (target)
728 *target = TRUE;
730 if (!*(p+1) && stop) {
731 xmlFree(content);
732 return n;
735 if (strchr((gchar *)content, '\t') != NULL) {
736 if ((nreq = split_input_line((gchar *)content, "\t", 0)) == NULL) {
737 xmlFree(content);
738 *rc = GPG_ERR_INV_VALUE;
739 return NULL;
742 else {
743 if ((nreq = split_input_line((gchar *)content, " ", 0)) == NULL) {
744 xmlFree(content);
745 *rc = GPG_ERR_INV_VALUE;
746 return NULL;
750 xmlFree(content);
751 tmp = find_root_element(doc, &nreq, rc, target, 0, FALSE);
753 if (!tmp) {
754 g_strfreev(nreq);
755 return NULL;
758 if (found_fn) {
759 found_fn(tmp, nreq, rc, p+1, data);
761 if (*rc) {
762 g_strfreev(nreq);
763 return NULL;
767 nnreq = strvcatv(nreq+1, p+1);
768 g_strfreev(nreq);
770 // FIXME ENOMEM
771 if (!nnreq || !*nnreq) {
772 if (nnreq)
773 g_strfreev(nnreq);
775 return tmp;
778 if (tmp->children)
779 n = find_elements(doc, tmp->children, nnreq, rc, NULL, found_fn,
780 not_found_fn, is_list_command, recursion_depth, data, stop);
781 else {
782 g_strfreev(nnreq);
784 if (not_found_fn)
785 return not_found_fn(tmp, p+1, rc, data);
787 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
788 return NULL;
791 if (*(p+1)) {
792 gchar **zz = p+1, **qq = nnreq;
794 if (g_strv_length(nnreq) > g_strv_length(p+1))
795 qq = nnreq+1;
797 for (; *qq && *zz; zz++) {
798 g_free(*zz);
799 *zz = g_strdup(*qq++);
801 if (!*zz) {
802 *rc = GPG_ERR_ENOMEM;
803 n = NULL;
804 break;
809 g_strfreev(nnreq);
810 return n;
814 return n;
817 static gboolean update_element_list(struct element_list_s *elements)
819 gchar *line;
820 GSList *l;
822 if (!elements || !elements->elements)
823 return TRUE;
825 line = g_strjoinv("\t", elements->elements);
827 if (!line)
828 return FALSE;
830 g_strfreev(elements->elements);
831 elements->elements = NULL;
832 l = g_slist_append(elements->list, line);
834 if (!l)
835 return FALSE;
837 elements->list = l;
838 return TRUE;
841 static gpg_error_t path_list_recurse(xmlDocPtr doc, xmlNodePtr node,
842 struct element_list_s *elements)
844 gpg_error_t rc = 0;
845 xmlNodePtr n;
847 for (n = node; n; n = n->next) {
848 xmlChar *target = NULL;
849 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
851 if (!a)
852 continue;
854 if (n->type != XML_ELEMENT_NODE)
855 goto children;
857 if (elements->verbose) {
858 if (strv_printf(&elements->elements, "%s\t!%s%s", elements->prefix, a, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
859 xmlFree(a);
860 return GPG_ERR_ENOMEM;
863 else if (strv_printf(&elements->elements, "%s\t!%s", elements->prefix, a) == FALSE) {
864 xmlFree(a);
865 return GPG_ERR_ENOMEM;
868 if (update_element_list(elements) == FALSE) {
869 xmlFree(a);
870 return GPG_ERR_ENOMEM;
873 target = node_has_attribute(n, (xmlChar *)"target");
875 if (target) {
876 gchar *tmp;
877 gchar *save = elements->prefix;
878 gboolean r = elements->resolving;
880 elements->depth++;
882 if (max_recursion_depth >= 1 && elements->depth > max_recursion_depth) {
883 xmlChar *t = xmlGetNodePath(n);
885 log_write("%s: %s", pwmd_strerror(GPG_ERR_ELOOP), t);
886 xmlFree(t);
887 xmlFree(target);
888 xmlFree(a);
889 return GPG_ERR_ELOOP;
892 if (elements->verbose) {
893 gchar **req = split_input_line((gchar *)target, "\t", 0);
894 xmlNodePtr tnode = find_root_element(doc, &req, &rc, NULL, 0, FALSE);
896 if (!tnode) {
897 g_strfreev(req);
898 xmlFree(a);
899 xmlFree(target);
900 return rc;
903 tnode = find_elements(doc, tnode->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
904 g_strfreev(req);
906 if (!tnode) {
907 xmlFree(a);
908 xmlFree(target);
909 return rc;
912 if (strv_printf(&elements->elements, "%s\t%s%s", elements->prefix, a, find_element_node(tnode->children) ? " 1" : " 0") == FALSE) {
913 xmlFree(a);
914 xmlFree(target);
915 return GPG_ERR_ENOMEM;
918 else if (strv_printf(&elements->elements, "%s\t%s", elements->prefix, a) == FALSE) {
919 xmlFree(a);
920 xmlFree(target);
921 return GPG_ERR_ENOMEM;
924 tmp = g_strjoinv("\t", elements->elements);
926 if (!tmp) {
927 xmlFree(a);
928 xmlFree(target);
929 return GPG_ERR_ENOMEM;
932 if (update_element_list(elements) == FALSE) {
933 g_free(tmp);
934 xmlFree(a);
935 xmlFree(target);
936 return GPG_ERR_ENOMEM;
939 if (elements->recurse) {
940 if (elements->verbose)
941 tmp[strlen(tmp)-2] = 0;
943 elements->prefix = tmp;
944 elements->resolving = TRUE;
945 rc = create_path_list(doc, elements, (gchar *)target);
946 xmlFree(target);
947 elements->resolving = r;
948 elements->depth--;
949 g_free(tmp);
950 elements->prefix = save;
952 if (rc) {
953 xmlFree(a);
954 return rc;
959 children:
960 if (n->children && elements->recurse) {
961 gchar *tmp = g_strdup_printf("%s\t!%s", elements->prefix, a);
962 gchar *save = elements->prefix;
964 if (!tmp) {
965 xmlFree(a);
966 return GPG_ERR_ENOMEM;
969 elements->prefix = tmp;
970 rc = path_list_recurse(doc, n->children, elements);
971 g_free(elements->prefix);
972 elements->prefix = save;
974 if (rc) {
975 xmlFree(a);
976 return rc;
980 xmlFree(a);
983 return rc;
986 gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
987 const gchar *value)
989 gchar *buf;
990 gpg_error_t rc;
992 if (name && !xmlSetProp(node, (xmlChar *)name, (xmlChar *)value))
993 return GPG_ERR_BAD_DATA;
995 if (name && xmlStrEqual((xmlChar *)name, (xmlChar *)"_mtime"))
996 return 0;
998 buf = g_strdup_printf("%li", time(NULL));
999 rc = add_attribute(node, "_mtime", buf);
1000 g_free(buf);
1001 return rc;
1005 * From the element path 'path', find sub-nodes and append them to the list.
1007 gpg_error_t create_path_list(xmlDocPtr doc, struct element_list_s *elements,
1008 gchar *path)
1010 gpg_error_t rc;
1011 gchar **req, **req_orig;
1012 xmlNodePtr n;
1013 gboolean a_target = FALSE;
1015 req = split_input_line(path, "\t", 0);
1017 if (!req) {
1018 req = split_input_line(path, " ", 0);
1020 if (!req)
1021 return GPG_ERR_SYNTAX;
1024 req_orig = g_strdupv(req);
1026 if (!req_orig) {
1027 rc = GPG_ERR_ENOMEM;
1028 goto fail;
1031 n = find_root_element(doc, &req, &rc, &a_target, 0, FALSE);
1033 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1034 rc = 0;
1035 goto fail;
1037 else if (!n)
1038 goto fail;
1040 if (a_target == TRUE) {
1041 g_free(*req);
1042 *req = g_strdup(*req_orig);
1045 if (*(req+1)) {
1046 gboolean e_target = FALSE;
1048 n = find_elements(doc, n->children, req+1, &rc, &e_target, NULL, NULL, TRUE, 0, NULL, FALSE);
1050 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == TRUE) {
1051 rc = 0;
1052 goto fail;
1054 else if (!n)
1055 goto fail;
1058 if (!elements->prefix) {
1060 * FIXME
1062 * If any req_orig element contains no target the element should be
1063 * prefixed with the literal character. Not really crucial if the
1064 * client isn't human because child elements are prefixed for the
1065 * current path. But may be confusing if editing by hand.
1067 elements->prefix = g_strjoinv("\t", req_orig);
1069 if (!elements->prefix) {
1070 rc = GPG_ERR_ENOMEM;
1071 goto fail;
1074 if (elements->verbose) {
1075 if (strv_printf(&elements->elements, "%s%s", elements->prefix, find_element_node(n->children) ? " 1" : " 0") == FALSE) {
1076 rc = GPG_ERR_ENOMEM;
1077 goto fail;
1080 else if (strv_printf(&elements->elements, "%s", elements->prefix) == FALSE) {
1081 rc = GPG_ERR_ENOMEM;
1082 goto fail;
1085 if (update_element_list(elements) == FALSE) {
1086 rc = GPG_ERR_ENOMEM;
1087 goto fail;
1091 rc = path_list_recurse(doc, n->children, elements);
1093 fail:
1094 if (req_orig)
1095 g_strfreev(req_orig);
1097 g_strfreev(req);
1098 return rc;
1101 gpg_error_t recurse_xpath_nodeset(xmlDocPtr doc, xmlNodeSetPtr nodes,
1102 xmlChar *value, xmlBufferPtr *result, gboolean cmd, const xmlChar *attr)
1104 gint i = value ? nodes->nodeNr - 1 : 0;
1105 xmlBufferPtr buf;
1107 buf = xmlBufferCreate();
1109 if (!buf)
1110 return GPG_ERR_ENOMEM;
1112 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++) {
1113 xmlNodePtr n = nodes->nodeTab[i];
1114 gpg_error_t rc;
1116 if (!n)
1117 continue;
1119 if (!value && !attr) {
1120 if (xmlNodeDump(buf, doc, n, 0, 0) == -1) {
1121 *result = buf;
1122 return GPG_ERR_BAD_DATA;
1125 continue;
1128 if (!attr) {
1129 xmlNodeSetContent(n, value);
1130 rc = update_element_mtime(n);
1132 if (rc)
1133 return rc;
1135 else {
1136 if (!cmd)
1137 rc = add_attribute(n, (gchar *)attr, (gchar *)value);
1138 else
1139 rc = delete_attribute(n, attr);
1141 if (rc)
1142 return rc;
1146 *result = buf;
1147 return 0;
1150 static gpg_error_t convert_root_element(xmlNodePtr n)
1152 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1153 gpg_error_t rc;
1155 if (a) {
1156 xmlFree(a);
1157 xmlChar *t = xmlGetNodePath(n);
1159 log_write(_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1160 xmlFree(t);
1161 return GPG_ERR_AMBIGUOUS_NAME;
1164 a = xmlGetProp(n, (xmlChar *)"name");
1166 if (a) {
1167 rc = add_attribute(n, "_name", (gchar *)a);
1168 xmlFree(a);
1170 if (rc)
1171 return rc;
1173 rc = delete_attribute(n, (xmlChar *)"name");
1175 if (rc)
1176 return rc;
1178 xmlNodeSetName(n, (xmlChar *)"element");
1181 return 0;
1184 gpg_error_t delete_attribute(xmlNodePtr n, const xmlChar *name)
1186 xmlAttrPtr a;
1188 if ((a = xmlHasProp(n, name)) == NULL)
1189 return GPG_ERR_NOT_FOUND;
1191 if (xmlRemoveProp(a) == -1)
1192 return GPG_ERR_BAD_DATA;
1194 return update_element_mtime(n);
1197 static gpg_error_t convert_elements_recurse(xmlDocPtr doc, xmlNodePtr n,
1198 guint depth)
1200 gpg_error_t rc;
1202 depth++;
1204 for (n = n->children; n; n = n->next) {
1205 if (n->type == XML_ELEMENT_NODE) {
1206 xmlChar *a = NULL;
1208 if (depth > 1) {
1209 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1210 xmlChar *t = xmlGetNodePath(n);
1212 log_write(_("An existing \"element\" already exists. Please rename this element before converting. Path is: %s"), t);
1213 xmlFree(t);
1214 return GPG_ERR_AMBIGUOUS_NAME;
1217 a = xmlGetProp(n, (xmlChar *)"_name");
1219 if (a) {
1220 xmlFree(a);
1221 xmlChar *t = xmlGetNodePath(n);
1223 log_write(_("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"), t);
1224 xmlFree(t);
1225 return GPG_ERR_AMBIGUOUS_NAME;
1228 xmlChar *tmp = xmlStrdup(n->name);
1230 if (!tmp)
1231 return GPG_ERR_ENOMEM;
1233 xmlNodeSetName(n, (xmlChar *)"element");
1234 rc = add_attribute(n, "_name", (gchar *)tmp);
1235 xmlFree(tmp);
1237 if (rc)
1238 return rc;
1240 else {
1241 rc = convert_root_element(n);
1243 if (rc)
1244 return rc;
1248 if (n->children) {
1249 rc = convert_elements_recurse(doc, n, depth);
1251 if (rc)
1252 return rc;
1256 return 0;
1259 /* Renames ALL elements to the new "element" name. Existing element names are
1260 * stored as an attribute "_name". This was introduced in pwmd 2.12 so
1261 * elements can contain common characters that the XML parser barfs on (an
1262 * email address for example. */
1263 gpg_error_t convert_pre_212_elements(xmlDocPtr doc)
1265 xmlNodePtr n = xmlDocGetRootElement(doc);
1267 log_write(_("Converting pre 2.12 data file..."));
1268 return convert_elements_recurse(doc, n, 0);
1271 gpg_error_t validate_import(xmlNodePtr node)
1273 gpg_error_t rc;
1275 if (!node)
1276 return 0;
1278 for (xmlNodePtr n = node; n; n = n->next) {
1279 if (n->type == XML_ELEMENT_NODE) {
1280 if (xmlStrEqual(n->name, (xmlChar *)"element")) {
1281 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
1283 if (!a) {
1284 xmlChar *t = xmlGetNodePath(n);
1286 log_write(_("Missing attribute '_name' at %s."), t);
1287 xmlFree(t);
1288 return GPG_ERR_INV_VALUE;
1291 if (!valid_xml_element(a)) {
1292 xmlChar *t = xmlGetNodePath(n);
1294 log_write(_("'%s' is not a valid element name at %s."), a, t);
1295 xmlFree(a);
1296 xmlFree(t);
1297 return GPG_ERR_INV_VALUE;
1300 xmlFree(a);
1301 a = xmlGetProp(n, (xmlChar *)"_ctime");
1302 if (!a)
1303 attr_ctime(n);
1305 xmlFree(a);
1306 a = xmlGetProp(n, (xmlChar *)"_mtime");
1307 if (!a)
1308 update_element_mtime(n);
1309 xmlFree(a);
1311 else {
1312 xmlChar *t = xmlGetNodePath(n);
1314 log_write(_("Warning: unknown element '%s' at %s. Ignoring."), n->name, t);
1315 xmlFree(t);
1316 continue;
1320 if (n->children) {
1321 rc = validate_import(n->children);
1323 if (rc)
1324 return rc;
1328 return rc;
1331 gpg_error_t update_element_mtime(xmlNodePtr n)
1333 return add_attribute(n, NULL, NULL);
1336 gpg_error_t unlink_node(xmlNodePtr n)
1338 gpg_error_t rc = 0;
1340 if (!n)
1341 return rc;
1343 if (n->parent)
1344 rc = update_element_mtime(n->parent);
1346 xmlUnlinkNode(n);
1347 return rc;
1350 xmlDocPtr parse_doc(const gchar *xml, gsize len)
1352 return xmlReadMemory(xml, len, NULL, "UTF-8", XML_PARSE_NOBLANKS);