Reimplement the KEEPALIVE status message.
[pwmd.git] / src / xml.c
blob533a532a06bf8d9ed55d31660c6688a32200dc9c
1 /*
2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of pwmd.
7 Pwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Pwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Pwmd. If not, see <http://www.gnu.org/licenses/>.
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <err.h>
28 #include <string.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include <libxml/xmlwriter.h>
34 #ifndef _
35 #include "gettext.h"
36 #define _(msgid) gettext(msgid)
37 #endif
39 #include "pwmd-error.h"
40 #include "util-misc.h"
41 #include "xml.h"
42 #include "mem.h"
43 #include "rcfile.h"
45 extern void log_write (const char *fmt, ...);
48 * 'element' must be allocated.
50 int
51 is_literal_element (char **element)
53 char *p;
55 if (!element || !*element)
56 return 0;
58 if (*(*element) == '!')
60 char *c;
62 for (p = *element, c = p + 1; *c; c++)
63 *p++ = *c;
65 *p = 0;
66 return 1;
69 return 0;
72 int
73 valid_xml_element (xmlChar * element)
75 char *p = (char *) element;
77 if (!element || !*element || *element == '!')
78 return 0;
80 for (; *p; p++)
82 if (isspace (*p))
83 return 0;
86 return 1;
89 int
90 valid_element_path (char **path, int with_content)
92 char **dup = NULL, **p;
93 int i, t;
95 if (!path || !*path)
96 return 0;
98 /* Save some memory by not duplicating the element content. */
99 if (with_content)
101 t = strv_length (path);
102 for (i = 0; i < t - 1; i++)
104 char **tmp = xrealloc (dup, (i + 2) * sizeof (char *));
106 if (!tmp)
108 strv_free (dup);
109 return 0;
112 dup = tmp;
113 dup[i] = str_dup (path[i]);
114 dup[i + 1] = NULL;
117 else
118 dup = strv_dup (path);
120 if (!dup)
121 return 0;
123 for (p = dup; *p && *(*p); p++)
125 is_literal_element (&(*p));
126 if (!valid_xml_element ((xmlChar *) * p))
128 strv_free (dup);
129 return 0;
133 strv_free (dup);
134 return 1;
137 gpg_error_t
138 attr_ctime (xmlNodePtr n)
140 char *buf = str_asprintf ("%li", time (NULL));
141 gpg_error_t rc;
143 if (!buf)
144 return GPG_ERR_ENOMEM;
146 rc = add_attribute (n, "_ctime", buf);
147 xfree (buf);
148 return rc;
151 static gpg_error_t
152 create_new_element (xmlNodePtr parent, const char *name, xmlNodePtr * result)
154 xmlNodePtr n = xmlNewNode (NULL, (xmlChar *) "element");
155 gpg_error_t rc;
157 if (!n)
158 return GPG_ERR_ENOMEM;
160 rc = add_attribute (n, "_name", name);
161 if (!rc)
162 rc = attr_ctime (n);
164 if (!rc)
166 if (result)
167 *result = xmlAddChild (parent, n);
168 else
169 (void) xmlAddChild (parent, n);
171 else
172 xmlFreeNode (n);
174 return rc;
177 gpg_error_t
178 new_root_element (xmlDocPtr doc, char *name)
180 xmlNodePtr root = xmlDocGetRootElement (doc);
181 char *p = name;
183 if (!p || !root)
184 return GPG_ERR_BAD_DATA;
186 if (*p == '!')
187 p++;
189 if (!valid_xml_element ((xmlChar *) p))
190 return GPG_ERR_INV_VALUE;
192 return create_new_element (root, p, NULL);
195 static xmlDocPtr
196 create_dtd ()
198 xmlDocPtr doc;
199 xmlTextWriterPtr wr = xmlNewTextWriterDoc (&doc, 0);
201 if (!wr)
202 return NULL;
204 if (xmlTextWriterStartDocument (wr, NULL, NULL, NULL))
205 goto fail;
207 if (xmlTextWriterStartDTD (wr, (xmlChar *) "pwmd", NULL, NULL) == -1)
208 goto fail;
210 if (xmlTextWriterWriteDTDElement (wr, (xmlChar *) "pwmd",
211 (xmlChar *) "(element)") == -1)
212 goto fail;
214 xmlTextWriterEndDTDElement (wr);
216 if (xmlTextWriterWriteDTDAttlist (wr, (xmlChar *) "element",
217 (xmlChar *) "_name CDATA #REQUIRED") ==
219 goto fail;
221 xmlTextWriterEndDTDAttlist (wr);
222 xmlTextWriterEndDTD (wr);
224 if (xmlTextWriterStartElement (wr, (xmlChar *) "pwmd"))
225 goto fail;
227 xmlTextWriterEndElement (wr);
228 xmlTextWriterEndDocument (wr);
229 xmlFreeTextWriter (wr);
230 return doc;
232 fail:
233 xmlTextWriterEndDocument (wr);
234 xmlFreeTextWriter (wr);
235 xmlFreeDoc (doc);
236 return NULL;
239 xmlDocPtr
240 new_document ()
242 return create_dtd ();
245 xmlNodePtr
246 find_element_node (xmlNodePtr node)
248 xmlNodePtr n = node;
250 if (n && n->type == XML_ELEMENT_NODE)
251 return n;
253 for (n = node; n; n = n->next)
255 if (n->type == XML_ELEMENT_NODE)
256 return n;
259 return NULL;
262 static xmlNodePtr
263 resolve_path (xmlDocPtr doc, xmlChar * path, char ***result, gpg_error_t * rc)
265 xmlNodePtr n;
266 char **req;
268 req = str_split ((char *) path, "\t", 0);
269 if (!req)
271 *rc = GPG_ERR_ENOMEM;
272 return NULL;
275 n = find_root_element (doc, &req, rc, NULL, 0, 0);
276 if (!n)
278 strv_free (req);
279 return NULL;
282 if (req[1])
283 n = find_elements (doc, n->children, req + 1, rc, NULL, NULL, NULL,
284 0, 0, NULL, 0);
286 if (*rc)
287 strv_free (req);
288 else
289 *result = req;
291 return n;
295 * Lists root element names; the value of the attribute "_name" of an element
296 * "element". If there's a target attribute both literal and non-literal
297 * element names will be added. This is the primary reason why XML entities
298 * cannot be used. There wouldn't be a way to get the literal an non-literal
299 * element paths.
301 gpg_error_t
302 list_root_elements (xmlDocPtr doc, struct string_s ** result,
303 int verbose, int with_target)
305 xmlNodePtr n = NULL;
306 struct slist_s *list = NULL;
307 int total, i;
308 struct string_s *string;
309 gpg_error_t rc = 0;
311 n = xmlDocGetRootElement (doc);
312 if (!n || !n->children)
313 return GPG_ERR_NO_DATA;
315 for (n = n->children; n; n = n->next)
317 xmlAttrPtr a;
318 xmlChar *val, *target;
319 struct slist_s *tlist;
320 char *tmp;
322 if (n->type != XML_ELEMENT_NODE)
323 continue;
325 a = xmlHasProp (n, (xmlChar *) "_name");
326 if (!a || !a->children->content)
327 continue;
329 val = xmlNodeGetContent (a->children);
330 if (!val)
332 rc = GPG_ERR_ENOMEM;
333 goto fail;
336 tmp =
337 str_asprintf ("!%s%s", (char *) val,
338 verbose ? find_element_node (n->children) ? " +" : "" :
339 "");
341 if (!tmp)
343 xmlFree (val);
344 rc = GPG_ERR_ENOMEM;
345 goto fail;
348 tlist = slist_append (list, tmp);
349 if (!tlist)
351 xmlFree (val);
352 rc = GPG_ERR_ENOMEM;
353 goto fail;
356 list = tlist;
357 target = node_has_attribute (n, (xmlChar *) "target");
358 if (target)
360 char *t = NULL;
362 if (verbose)
364 char **req = NULL;
365 xmlNodePtr tnode = resolve_path (doc, target, &req, &rc);
367 if (rc == GPG_ERR_ELEMENT_NOT_FOUND || rc == GPG_ERR_ELOOP)
369 t = str_asprintf ("%s %s", (char *) val,
370 rc == GPG_ERR_ELOOP ? "O" : "E");
371 rc = 0;
373 else if (!rc)
375 struct string_s *realpath = NULL;
377 if (with_target)
379 rc = build_realpath (doc, (char *) target, &realpath);
380 if (rc)
382 strv_free (req);
383 xmlFree (val);
384 xmlFree (target);
385 goto fail;
388 realpath = string_prepend (realpath, "T ");
391 t = str_asprintf ("%s%s%s%s", (char *) val,
392 (tnode
393 && find_element_node (tnode->children))
394 || realpath ? " " : "", tnode
395 && find_element_node (tnode->children) ?
396 "+" : "", realpath ? realpath->str : "");
398 if (realpath)
399 string_free (realpath, 1);
402 if (req)
403 strv_free (req);
405 else
406 t = str_dup ((char *) val);
408 if (!t || rc)
410 xmlFree (val);
411 xmlFree (target);
412 rc = rc ? rc : GPG_ERR_ENOMEM;
413 goto fail;
416 tlist = slist_append (list, t);
417 if (!tlist)
419 xmlFree (val);
420 xfree (t);
421 xmlFree (target);
422 rc = GPG_ERR_ENOMEM;
423 goto fail;
426 list = tlist;
429 xmlFree (val);
430 xmlFree (target);
433 total = slist_length (list);
434 if (!total)
435 return GPG_ERR_NO_DATA;
437 string = string_new (NULL);
438 if (!string)
440 rc = GPG_ERR_ENOMEM;
441 goto fail;
444 for (i = 0; i < total; i++)
446 char *val = slist_nth_data (list, i);
448 string_append_printf (string, "%s\n", val);
451 string = string_truncate (string, string->len - 1);
452 *result = string;
454 fail:
455 total = slist_length (list);
456 for (i = 0; i < total; i++)
457 xfree (slist_nth_data (list, i));
459 slist_free (list);
460 return rc;
464 * Prevents a sibling element past the current element path with the same
465 * element name.
467 static xmlNodePtr
468 find_stop_node (xmlNodePtr node)
470 xmlNodePtr n;
472 for (n = node->parent->children; n; n = n->next)
474 if (n == node)
475 return n->next;
478 return NULL;
481 xmlNodePtr
482 create_target_elements_cb (xmlNodePtr node, char **path,
483 gpg_error_t * rc, void *data)
485 int i;
486 char **req = path;
487 xmlNodePtr parent = data;
489 for (i = 0; req[i] && *req[i]; i++)
491 xmlNodePtr n;
493 if (parent && node == parent)
495 *rc = GPG_ERR_CONFLICT;
496 return NULL;
499 is_literal_element (&req[i]);
501 if ((n = find_element (node, req[i], find_stop_node (node))) == NULL ||
502 (n && n->parent == node->parent))
505 *rc = create_new_element (node, req[i], &node);
506 if (*rc)
507 return NULL;
509 else
510 node = n;
513 return node;
516 xmlNodePtr
517 find_text_node (xmlNodePtr node)
519 xmlNodePtr n = node;
521 if (n && n->type == XML_TEXT_NODE)
522 return n;
524 for (n = node; n; n = n->next)
526 if (n->type == XML_TEXT_NODE)
527 return n;
530 return NULL;
533 xmlNodePtr
534 create_elements_cb (xmlNodePtr node, char **elements,
535 gpg_error_t * rc, void *data)
537 int i;
538 char **req = elements;
540 if (node->type == XML_TEXT_NODE)
541 node = node->parent;
543 for (i = 0; req[i] && *req[i]; i++)
545 xmlNodePtr n;
548 * Strip the first '!' if needed. If there's another, it's an
549 * rc. The syntax has already been checked before calling this
550 * function.
552 is_literal_element (&req[i]);
553 n = find_element (node, req[i], find_stop_node (node));
556 * If the found element has the same parent as the current element,
557 * they are siblings and the new element needs to be created as a
558 * child of the current element (node).
560 if (n && n->parent == node->parent)
561 n = NULL;
563 if (!n)
565 *rc = create_new_element (node, req[i], &node);
566 if (*rc)
567 return NULL;
569 else
570 node = n;
573 return node;
576 /* The root element is really req[0]. It is need as a pointer in case there is
577 * a target attribute so it can be updated. */
578 xmlNodePtr
579 find_root_element (xmlDocPtr doc, char ***req, gpg_error_t * rc,
580 int *target, int recursion_depth, int stop)
582 xmlNodePtr n = xmlDocGetRootElement (doc);
583 int depth = 0;
584 char *root = str_dup (*req[0]);
585 int literal = is_literal_element (&root);
587 if (!root)
589 *rc = GPG_ERR_ENOMEM;
590 return NULL;
593 *rc = 0;
594 recursion_depth++;
596 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth)
598 xmlChar *t = xmlGetNodePath (n);
600 log_write ("%s: %s", pwmd_strerror (GPG_ERR_ELOOP), t);
601 xmlFree (t);
602 xfree (root);
603 *rc = GPG_ERR_ELOOP;
604 return NULL;
607 while (n)
609 if (n->type == XML_ELEMENT_NODE)
611 if (depth == 0 && xmlStrEqual (n->name, (xmlChar *) "pwmd"))
613 n = n->children;
614 depth++;
615 continue;
618 if (depth == 1 && xmlStrEqual (n->name, (xmlChar *) "element"))
620 xmlChar *content = node_has_attribute (n, (xmlChar *) "_name");
622 if (!content)
623 continue;
625 if (xmlStrEqual (content, (xmlChar *) root))
627 char **nreq, **tmp = NULL;
629 if (literal == 1)
631 xmlFree (content);
632 xfree (root);
633 return n;
636 xmlFree (content);
637 content = node_has_attribute (n, (xmlChar *) "target");
639 if (target)
640 *target = 1;
642 if (!content || stop)
644 if (content)
645 xmlFree (content);
647 xfree (root);
648 return n;
651 if (strchr ((char *) content, '\t'))
653 nreq = str_split ((char *) content, "\t", 0);
654 xmlFree (content);
656 #if 0
658 * FIXME ENOMEM
660 if (!nreq)
662 *rc = GPG_ERR_ENOMEM;
663 return NULL;
665 #endif
667 tmp = *req;
668 tmp = strv_catv (nreq, tmp + 1);
669 strv_free (nreq);
671 if (!tmp)
673 xfree (root);
674 *rc = GPG_ERR_ENOMEM;
675 return NULL;
678 strv_free (*req);
679 *req = tmp;
681 else
683 if (strv_printf (&tmp, "%s", content) == 0)
685 xmlFree (content);
686 xfree (root);
687 *rc = GPG_ERR_ENOMEM;
688 return NULL;
691 xmlFree (content);
692 nreq = *req;
693 nreq = strv_catv (tmp, nreq + 1);
694 strv_free (tmp);
696 if (!nreq)
698 *rc = GPG_ERR_ENOMEM;
699 xfree (root);
700 return NULL;
703 strv_free (*req);
704 *req = nreq;
707 xfree (root);
709 find_root_element (doc, req, rc, target, recursion_depth,
711 return n;
714 xmlFree (content);
718 n = n->next;
721 xfree (root);
722 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
723 return NULL;
726 xmlNodePtr
727 find_element (xmlNodePtr node, char *element, xmlNodePtr stop)
729 xmlNodePtr n;
731 if (!node || !element)
732 return NULL;
734 for (n = node; n; n = n->next)
736 if (n->type != XML_ELEMENT_NODE)
737 continue;
739 if (n == stop)
740 break;
742 xmlChar *a = node_has_attribute (n, (xmlChar *) "_name");
744 if (a && xmlStrEqual (a, (xmlChar *) element))
746 xmlFree (a);
747 return n;
750 xmlFree (a);
753 return NULL;
756 xmlChar *
757 node_has_attribute (xmlNodePtr n, xmlChar * attr)
759 xmlAttrPtr a = xmlHasProp (n, attr);
761 if (!a)
762 return NULL;
764 if (!a->children || !a->children->content)
765 return NULL;
767 return xmlGetProp (n, attr);
770 static int
771 element_to_literal (char **element)
773 char *p = str_asprintf ("!%s", *element);
775 if (!p)
776 return 0;
778 xfree (*element);
779 *element = p;
780 return 1;
783 /* Resolves elements in 'req' one at a time. It's recursive in case of
784 * "target" attributes. */
785 xmlNodePtr
786 find_elements (xmlDocPtr doc, xmlNodePtr node,
787 char **req, gpg_error_t * rc, int *target,
788 xmlNodePtr (*found_fn) (xmlNodePtr, char **, gpg_error_t *,
789 char **, void *),
790 xmlNodePtr (*not_found_fn) (xmlNodePtr, char **, gpg_error_t *,
791 void *), int is_list_command,
792 int recursion_depth, void *data, int stop)
794 xmlNodePtr n, last, last_node;
795 char **p;
796 int found = 0;
798 *rc = 0;
799 recursion_depth++;
801 if (max_recursion_depth >= 1 && recursion_depth > max_recursion_depth)
803 xmlChar *t = xmlGetNodePath (node);
805 log_write ("%s: %s", pwmd_strerror (GPG_ERR_ELOOP), t);
806 xmlFree (t);
807 recursion_depth--;
808 *rc = GPG_ERR_ELOOP;
809 return NULL;
812 for (last_node = last = n = node, p = req; *p; p++)
814 xmlNodePtr tmp;
815 char *t;
816 int literal;
818 if (!*(*p))
820 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
821 return NULL;
824 t = str_dup (*p);
825 if (!t)
827 *rc = GPG_ERR_ENOMEM;
828 return NULL;
831 literal = is_literal_element (&t);
832 n = find_element (last, t, NULL);
833 xfree (t);
835 if (!n)
837 if (not_found_fn)
838 return not_found_fn (found ? last_node : last_node->parent, p, rc,
839 data);
841 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
842 return NULL;
845 last = n->children;
846 last_node = n;
847 found = 1;
849 if (literal == 0)
851 xmlChar *content = node_has_attribute (n, (xmlChar *) "target");
852 char **nreq = NULL, **nnreq;
854 if (!content)
856 if (is_list_command == 1)
858 if (element_to_literal (&(*p)) == 0)
860 *rc = GPG_ERR_ENOMEM;
861 return NULL;
865 continue;
868 if (target)
869 *target = 1;
871 if (!*(p + 1) && stop)
873 xmlFree (content);
874 return n;
877 if (strchr ((char *) content, '\t') != NULL)
879 if ((nreq = str_split ((char *) content, "\t", 0)) == NULL)
881 xmlFree (content);
882 *rc = GPG_ERR_INV_VALUE;
883 return NULL;
886 else
888 if ((nreq = str_split ((char *) content, " ", 0)) == NULL)
890 xmlFree (content);
891 *rc = GPG_ERR_INV_VALUE;
892 return NULL;
896 xmlFree (content);
897 tmp = find_root_element (doc, &nreq, rc, target, 0, 0);
899 if (!tmp)
901 strv_free (nreq);
902 return NULL;
905 if (found_fn)
907 found_fn (tmp, nreq, rc, p + 1, data);
909 if (*rc)
911 strv_free (nreq);
912 return NULL;
916 if (!*(nreq + 1) && !*(p + 1))
918 strv_free (nreq);
919 return tmp;
922 nnreq = strv_catv (nreq + 1, p + 1);
923 strv_free (nreq);
925 // FIXME ENOMEM
926 if (!nnreq || !*nnreq)
928 if (nnreq)
929 strv_free (nnreq);
931 return tmp;
934 if (tmp->children)
935 n = find_elements (doc, tmp->children, nnreq, rc, NULL, found_fn,
936 not_found_fn, is_list_command, recursion_depth,
937 data, stop);
938 else
940 strv_free (nnreq);
942 if (not_found_fn)
943 return not_found_fn (tmp, p + 1, rc, data);
945 *rc = GPG_ERR_ELEMENT_NOT_FOUND;
946 return NULL;
949 if (*(p + 1))
951 char **zz = p + 1, **qq = nnreq;
953 if (strv_length (nnreq) > strv_length (p + 1))
954 qq = nnreq + 1;
956 for (; *qq && *zz; zz++)
958 xfree (*zz);
959 *zz = str_dup (*qq++);
961 if (!*zz)
963 *rc = GPG_ERR_ENOMEM;
964 n = NULL;
965 break;
970 strv_free (nnreq);
971 return n;
975 return n;
978 static int
979 update_element_list (struct element_list_s *elements)
981 char *line;
982 struct slist_s *l;
984 if (!elements || !elements->elements)
985 return 1;
987 line = strv_join ("\t", elements->elements);
989 if (!line)
990 return 0;
992 strv_free (elements->elements);
993 elements->elements = NULL;
994 l = slist_append (elements->list, line);
996 if (!l)
997 return 0;
999 elements->list = l;
1000 return 1;
1003 static gpg_error_t
1004 path_list_recurse (xmlDocPtr doc, xmlNodePtr node,
1005 struct element_list_s *elements)
1007 gpg_error_t rc = 0;
1008 xmlNodePtr n;
1009 gpg_error_t error_flag = 0;
1011 for (n = node; n; n = n->next)
1013 xmlChar *target = NULL;
1014 xmlChar *a = node_has_attribute (n, (xmlChar *) "_name");
1016 if (!a)
1017 continue;
1019 if (n->type != XML_ELEMENT_NODE)
1020 goto children;
1022 if (elements->verbose)
1024 if (strv_printf
1025 (&elements->elements, "%s\t!%s%s", elements->prefix, a,
1026 find_element_node (n->children) ? " +" : "") == 0)
1028 xmlFree (a);
1029 return GPG_ERR_ENOMEM;
1032 else
1033 if (strv_printf (&elements->elements, "%s\t!%s", elements->prefix, a)
1034 == 0)
1036 xmlFree (a);
1037 return GPG_ERR_ENOMEM;
1040 if (update_element_list (elements) == 0)
1042 xmlFree (a);
1043 return GPG_ERR_ENOMEM;
1046 target = node_has_attribute (n, (xmlChar *) "target");
1048 if (target)
1050 char *tmp;
1051 char *save = elements->prefix;
1052 int r = elements->resolving;
1053 char **req = NULL;
1054 xmlNodePtr tnode;
1055 struct string_s *realpath = NULL;
1057 tnode = resolve_path (doc, target, &req, &rc);
1058 if (rc == GPG_ERR_ELOOP || rc == GPG_ERR_ELEMENT_NOT_FOUND)
1060 if (rc == GPG_ERR_ELOOP)
1062 xmlChar *t = xmlGetNodePath (n);
1064 log_write ("%s: %s", pwmd_strerror (GPG_ERR_ELOOP), t);
1065 xmlFree (t);
1068 if (elements->verbose)
1070 strv_printf (&elements->elements, "%s\t%s %s",
1071 elements->prefix, a,
1072 rc == GPG_ERR_ELOOP ? "O" : "E");
1073 error_flag = rc;
1074 rc = 0;
1075 goto update;
1079 if (!elements->verbose && rc)
1081 xmlFree (a);
1082 xmlFree (target);
1083 return rc;
1086 if (elements->with_target)
1088 rc = build_realpath (doc, (char *) target, &realpath);
1089 if (rc)
1091 xmlFree (a);
1092 xmlFree (target);
1093 return rc;
1096 realpath = string_prepend (realpath, "T ");
1099 if (elements->verbose)
1101 if (!strv_printf (&elements->elements, "%s\t%s%s%s%s",
1102 elements->prefix, a,
1103 (tnode && find_element_node (tnode->children))
1104 || realpath ? " " : "", tnode
1105 && find_element_node (tnode->children) ? "+" :
1106 "", realpath ? realpath->str : ""))
1108 xmlFree (a);
1109 xmlFree (target);
1110 return GPG_ERR_ENOMEM;
1113 else
1114 if (!strv_printf
1115 (&elements->elements, "%s\t%s", elements->prefix, a))
1117 xmlFree (a);
1118 xmlFree (target);
1119 return GPG_ERR_ENOMEM;
1122 update:
1123 if (realpath)
1124 string_free (realpath, 1);
1126 tmp = strv_join ("\t", elements->elements);
1128 if (!tmp)
1130 xmlFree (a);
1131 xmlFree (target);
1132 return GPG_ERR_ENOMEM;
1135 if (update_element_list (elements) == 0)
1137 xfree (tmp);
1138 xmlFree (a);
1139 xmlFree (target);
1140 return GPG_ERR_ENOMEM;
1143 if (!error_flag && elements->recurse)
1145 /* Prune element flags. */
1146 if (elements->verbose && strchr (tmp, ' '))
1148 char *p;
1150 for (p = tmp; *p; p++)
1152 if (*p == ' ')
1154 *p = 0;
1155 break;
1160 elements->prefix = tmp;
1161 elements->resolving = 1;
1162 rc = create_path_list (doc, elements, (char *) target);
1163 xmlFree (target);
1164 elements->resolving = r;
1165 xfree (tmp);
1166 elements->prefix = save;
1168 if (rc)
1170 xmlFree (a);
1171 return rc;
1176 children:
1177 if (n->children && elements->recurse && error_flag != GPG_ERR_ELOOP)
1179 char *tmp = str_asprintf ("%s\t!%s", elements->prefix, a);
1180 char *save = elements->prefix;
1182 if (!tmp)
1184 xmlFree (a);
1185 return GPG_ERR_ENOMEM;
1188 elements->prefix = tmp;
1189 rc = path_list_recurse (doc, n->children, elements);
1190 xfree (elements->prefix);
1191 elements->prefix = save;
1193 if (rc)
1195 xmlFree (a);
1196 return rc;
1200 xmlFree (a);
1203 return error_flag == GPG_ERR_ELOOP ? error_flag : rc;
1206 gpg_error_t
1207 add_attribute (xmlNodePtr node, const char *name, const char *value)
1209 char *buf;
1210 gpg_error_t rc;
1212 if (name && !xmlSetProp (node, (xmlChar *) name, (xmlChar *) value))
1213 return GPG_ERR_BAD_DATA;
1215 if (name && xmlStrEqual ((xmlChar *) name, (xmlChar *) "_mtime"))
1216 return 0;
1218 buf = str_asprintf ("%li", time (NULL));
1219 rc = add_attribute (node, "_mtime", buf);
1220 xfree (buf);
1221 return rc;
1225 * From the element path 'path', find sub-nodes and append them to the list.
1227 gpg_error_t
1228 create_path_list (xmlDocPtr doc, struct element_list_s * elements, char *path)
1230 gpg_error_t rc;
1231 char **req, **req_orig;
1232 xmlNodePtr n;
1233 int a_target = 0;
1235 req = str_split (path, "\t", 0);
1236 if (!req)
1238 req = str_split (path, " ", 0);
1239 if (!req)
1240 return GPG_ERR_SYNTAX;
1243 req_orig = strv_dup (req);
1244 if (!req_orig)
1246 rc = GPG_ERR_ENOMEM;
1247 goto fail;
1250 n = find_root_element (doc, &req, &rc, &a_target, 0, 0);
1251 if ((rc == GPG_ERR_ELEMENT_NOT_FOUND || rc == GPG_ERR_ELOOP)
1252 && elements->verbose && a_target)
1254 rc = 0;
1255 goto done;
1258 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == 1)
1260 rc = 0;
1261 goto fail;
1263 else if (!n)
1264 goto fail;
1266 if (a_target == 1)
1268 xfree (*req);
1269 *req = str_dup (*req_orig);
1272 if (*(req + 1))
1274 int e_target = 0;
1277 find_elements (doc, n->children, req + 1, &rc, &e_target, NULL, NULL,
1278 1, 0, NULL, 0);
1280 if (!n && rc == GPG_ERR_ELEMENT_NOT_FOUND && elements->resolving == 1)
1282 rc = 0;
1283 goto fail;
1285 else if (!n)
1286 goto fail;
1289 done:
1290 if (!elements->prefix)
1293 * FIXME
1295 * If any req_orig element contains no target the element should be
1296 * prefixed with the literal character. Not really crucial if the
1297 * client isn't human because child elements are prefixed for the
1298 * current path. But may be confusing if editing by hand.
1300 elements->prefix = strv_join ("\t", req_orig);
1302 if (!elements->prefix)
1304 rc = GPG_ERR_ENOMEM;
1305 goto fail;
1308 if (elements->verbose)
1310 int ret;
1311 struct string_s *realpath = NULL;
1313 if (a_target && elements->with_target)
1315 rc = build_realpath (doc, path, &realpath);
1316 if (rc)
1317 goto fail;
1319 realpath = string_prepend (realpath, "T ");
1322 ret = strv_printf (&elements->elements, "%s%s%s%s",
1323 elements->prefix,
1324 find_element_node (n->children)
1325 || realpath ? " " : "",
1326 find_element_node (n->children) ? "+" : "",
1327 realpath ? realpath->str : "");
1328 if (!ret)
1330 rc = GPG_ERR_ENOMEM;
1331 goto fail;
1334 else if (strv_printf (&elements->elements, "%s", elements->prefix) == 0)
1336 rc = GPG_ERR_ENOMEM;
1337 goto fail;
1340 if (update_element_list (elements) == 0)
1342 rc = GPG_ERR_ENOMEM;
1343 goto fail;
1347 rc = path_list_recurse (doc, n->children, elements);
1349 fail:
1350 if (req_orig)
1351 strv_free (req_orig);
1353 strv_free (req);
1354 return rc;
1357 gpg_error_t
1358 recurse_xpath_nodeset (xmlDocPtr doc, xmlNodeSetPtr nodes,
1359 xmlChar * value, xmlBufferPtr * result, int cmd,
1360 const xmlChar * attr)
1362 int i = value ? nodes->nodeNr - 1 : 0;
1363 xmlBufferPtr buf;
1365 buf = xmlBufferCreate ();
1367 if (!buf)
1368 return GPG_ERR_ENOMEM;
1370 for (; value ? i >= 0 : i < nodes->nodeNr; value ? i-- : i++)
1372 xmlNodePtr n = nodes->nodeTab[i];
1373 gpg_error_t rc;
1375 if (!n)
1376 continue;
1378 if (!value && !attr)
1380 if (xmlNodeDump (buf, doc, n, 0, 0) == -1)
1382 *result = buf;
1383 return GPG_ERR_BAD_DATA;
1386 continue;
1389 if (!attr)
1391 xmlNodeSetContent (n, value);
1392 rc = update_element_mtime (n);
1394 if (rc)
1395 return rc;
1397 else
1399 if (!cmd)
1400 rc = add_attribute (n, (char *) attr, (char *) value);
1401 else
1402 rc = delete_attribute (n, attr);
1404 if (rc)
1405 return rc;
1409 *result = buf;
1410 return 0;
1413 static gpg_error_t
1414 convert_root_element (xmlNodePtr n)
1416 xmlChar *a = xmlGetProp (n, (xmlChar *) "_name");
1417 gpg_error_t rc;
1419 if (a)
1421 xmlFree (a);
1422 xmlChar *t = xmlGetNodePath (n);
1424 log_write (_
1425 ("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"),
1427 xmlFree (t);
1428 return GPG_ERR_AMBIGUOUS_NAME;
1431 a = xmlGetProp (n, (xmlChar *) "name");
1433 if (a)
1435 rc = add_attribute (n, "_name", (char *) a);
1436 xmlFree (a);
1438 if (rc)
1439 return rc;
1441 rc = delete_attribute (n, (xmlChar *) "name");
1443 if (rc)
1444 return rc;
1446 xmlNodeSetName (n, (xmlChar *) "element");
1449 return 0;
1452 gpg_error_t
1453 delete_attribute (xmlNodePtr n, const xmlChar * name)
1455 xmlAttrPtr a;
1457 if ((a = xmlHasProp (n, name)) == NULL)
1458 return GPG_ERR_NOT_FOUND;
1460 if (xmlRemoveProp (a) == -1)
1461 return GPG_ERR_BAD_DATA;
1463 return update_element_mtime (n);
1466 static gpg_error_t
1467 convert_elements_recurse (xmlDocPtr doc, xmlNodePtr n, unsigned depth)
1469 gpg_error_t rc;
1471 depth++;
1473 for (n = n->children; n; n = n->next)
1475 if (n->type == XML_ELEMENT_NODE)
1477 xmlChar *a = NULL;
1479 if (depth > 1)
1481 if (xmlStrEqual (n->name, (xmlChar *) "element"))
1483 xmlChar *t = xmlGetNodePath (n);
1485 log_write (_
1486 ("An existing \"element\" already exists. Please rename this element before converting. Path is: %s"),
1488 xmlFree (t);
1489 return GPG_ERR_AMBIGUOUS_NAME;
1492 a = xmlGetProp (n, (xmlChar *) "_name");
1494 if (a)
1496 xmlFree (a);
1497 xmlChar *t = xmlGetNodePath (n);
1499 log_write (_
1500 ("An existing \"_name\" attribute already exists. Please rename this attribute before converting. Path is: %s"),
1502 xmlFree (t);
1503 return GPG_ERR_AMBIGUOUS_NAME;
1506 xmlChar *tmp = xmlStrdup (n->name);
1508 if (!tmp)
1509 return GPG_ERR_ENOMEM;
1511 xmlNodeSetName (n, (xmlChar *) "element");
1512 rc = add_attribute (n, "_name", (char *) tmp);
1513 xmlFree (tmp);
1515 if (rc)
1516 return rc;
1518 else
1520 rc = convert_root_element (n);
1522 if (rc)
1523 return rc;
1527 if (n->children)
1529 rc = convert_elements_recurse (doc, n, depth);
1531 if (rc)
1532 return rc;
1536 return 0;
1539 /* Renames ALL elements to the new "element" name. Existing element names are
1540 * stored as an attribute "_name". This was introduced in pwmd 2.12 so
1541 * elements can contain common characters that the XML parser barfs on (an
1542 * email address for example. */
1543 gpg_error_t
1544 convert_pre_212_elements (xmlDocPtr doc)
1546 xmlNodePtr n = xmlDocGetRootElement (doc);
1548 log_write (_("Converting pre 2.12 data file..."));
1549 return convert_elements_recurse (doc, n, 0);
1552 gpg_error_t
1553 validate_import (xmlNodePtr node)
1555 gpg_error_t rc = 0;
1557 if (!node)
1558 return 0;
1560 for (xmlNodePtr n = node; n; n = n->next)
1562 if (n->type == XML_ELEMENT_NODE)
1564 if (xmlStrEqual (n->name, (xmlChar *) "element"))
1566 xmlChar *a = xmlGetProp (n, (xmlChar *) "_name");
1568 if (!a)
1570 xmlChar *t = xmlGetNodePath (n);
1572 log_write (_("Missing attribute '_name' at %s."), t);
1573 xmlFree (t);
1574 return GPG_ERR_INV_VALUE;
1577 if (!valid_xml_element (a))
1579 xmlChar *t = xmlGetNodePath (n);
1581 log_write (_("'%s' is not a valid element name at %s."), a,
1583 xmlFree (a);
1584 xmlFree (t);
1585 return GPG_ERR_INV_VALUE;
1588 xmlFree (a);
1589 a = xmlGetProp (n, (xmlChar *) "_ctime");
1590 if (!a)
1591 attr_ctime (n);
1593 xmlFree (a);
1594 a = xmlGetProp (n, (xmlChar *) "_mtime");
1595 if (!a)
1596 update_element_mtime (n);
1597 xmlFree (a);
1599 else
1601 xmlChar *t = xmlGetNodePath (n);
1603 log_write (_("Warning: unknown element '%s' at %s. Ignoring."),
1604 n->name, t);
1605 xmlFree (t);
1606 continue;
1610 if (n->children)
1612 rc = validate_import (n->children);
1614 if (rc)
1615 return rc;
1619 return rc;
1622 gpg_error_t
1623 update_element_mtime (xmlNodePtr n)
1625 return add_attribute (n, NULL, NULL);
1628 gpg_error_t
1629 unlink_node (xmlNodePtr n)
1631 gpg_error_t rc = 0;
1633 if (!n)
1634 return rc;
1636 if (n->parent)
1637 rc = update_element_mtime (n->parent);
1639 xmlUnlinkNode (n);
1640 return rc;
1643 xmlDocPtr
1644 parse_doc (const char *xml, size_t len)
1646 return xmlReadMemory (xml, len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
1649 static xmlNodePtr
1650 realpath_elements_cb (xmlNodePtr node, char **target,
1651 gpg_error_t * rc, char **req_orig, void *data)
1653 char *path = *(char **) data;
1654 char *tmp = NULL, *result;
1656 if (path)
1658 xfree (path);
1659 *(char **) data = NULL;
1662 path = strv_join ("\t", target);
1664 if (!path)
1666 *rc = GPG_ERR_ENOMEM;
1667 return NULL;
1670 if (req_orig)
1672 tmp = strv_join ("\t", req_orig);
1674 if (!tmp)
1676 xfree (path);
1677 *rc = GPG_ERR_ENOMEM;
1678 return NULL;
1682 if (tmp && *tmp)
1683 result = str_asprintf ("%s\t%s", path, tmp);
1684 else
1685 result = str_dup (path);
1687 if (!result)
1689 *rc = GPG_ERR_ENOMEM;
1690 xfree (path);
1691 xfree (tmp);
1692 return NULL;
1695 xfree (path);
1696 xfree (tmp);
1697 *(char **) data = result;
1698 return node;
1701 gpg_error_t
1702 build_realpath (xmlDocPtr doc, char *line, struct string_s ** result)
1704 gpg_error_t rc;
1705 char **req;
1706 char *t;
1707 int i;
1708 xmlNodePtr n;
1709 struct string_s *string;
1710 char *rp = NULL;
1712 if (strchr (line, '\t') != NULL)
1714 if ((req = str_split (line, "\t", 0)) == NULL)
1715 return GPG_ERR_SYNTAX;
1717 else
1719 if ((req = str_split (line, " ", 0)) == NULL)
1720 return GPG_ERR_SYNTAX;
1723 n = find_root_element (doc, &req, &rc, NULL, 0, 0);
1724 if (!n)
1726 strv_free (req);
1727 return rc;
1730 rp = strv_join ("\t", req);
1731 if (!rp)
1733 strv_free (req);
1734 return GPG_ERR_ENOMEM;
1737 if (req[1])
1739 n = find_elements (doc, n->children, req + 1, &rc,
1740 NULL, realpath_elements_cb, NULL, 0, 0, &rp, 0);
1741 if (!n)
1743 xfree (rp);
1744 strv_free (req);
1745 return rc;
1749 string = string_new (rp);
1750 xfree (rp);
1751 strv_free (req);
1752 if (!string)
1753 return GPG_ERR_ENOMEM;
1755 again:
1756 for (i = 0, t = string->str + i; *t; t++, i++)
1758 if ((!i && *t != '!') || (*t == '\t' && *(t + 1) && *(t + 1) != '!'))
1760 struct string_s *s = string_insert_c (string, !i ? i++ : ++i, '!');
1762 if (!s)
1764 string_free (string, 1);
1765 return GPG_ERR_ENOMEM;
1768 string = s;
1769 goto again;
1773 *result = string;
1774 return rc;
1777 #if 0
1778 static char *
1779 node_to_element_path (xmlNodePtr node)
1781 xmlNodePtr n;
1782 struct string_s *str = string_new ("");
1783 char *result;
1785 for (n = node; n; n = n->parent)
1787 xmlNodePtr child;
1789 for (child = n; child; child = child->next)
1791 if (child->type != XML_ELEMENT_NODE)
1792 continue;
1794 xmlChar *name = node_has_attribute (n, (xmlChar *) "_name");
1795 if (name)
1797 str = string_prepend (str, (char *) name);
1798 xmlFree (name);
1799 name = node_has_attribute (n, (xmlChar *) "target");
1800 if (name)
1801 str = string_prepend (str, "\t");
1802 else
1803 str = string_prepend (str, "\t!");
1804 xmlFree (name);
1806 break;
1810 str = string_erase (str, 0, 1);
1811 result = str->str;
1812 string_free (str, 0);
1813 return result;
1815 #endif
1818 * Recurse the element tree beginning at 'node' and find elements who point
1819 * back to 'src' or 'dst'. Also follows target attributes.
1821 static gpg_error_t
1822 find_child_to_target (xmlDocPtr doc, xmlNodePtr node,
1823 xmlNodePtr src, xmlNodePtr dst)
1825 xmlNodePtr n;
1826 gpg_error_t rc = 0;
1828 for (n = node; n; n = n->next)
1830 xmlChar *target;
1832 if (n->type != XML_ELEMENT_NODE)
1833 continue;
1835 if (n == src || n == dst)
1836 return GPG_ERR_ELOOP;
1838 target = node_has_attribute (n, (xmlChar *) "target");
1839 if (target)
1841 xmlNodePtr tmp;
1842 char **result = NULL;
1844 tmp = resolve_path (doc, target, &result, &rc);
1845 xmlFree (target);
1846 strv_free (result);
1847 if (!rc)
1849 rc = find_child_to_target (doc, tmp, src, dst);
1850 if (rc)
1851 return rc;
1854 else
1855 xmlFree (target);
1857 rc = find_child_to_target (doc, n->children, src, dst);
1858 if (rc)
1859 return rc;
1862 return rc;
1865 static gpg_error_t
1866 find_child_of_parent (xmlDocPtr doc, xmlNodePtr src, xmlNodePtr dst)
1868 xmlNodePtr n;
1869 gpg_error_t rc = 0;
1871 for (n = src; n; n = n->next)
1873 if (n->type != XML_ELEMENT_NODE)
1874 continue;
1876 if (n == dst)
1878 rc = GPG_ERR_ELOOP;
1879 break;
1882 rc = find_child_of_parent (doc, n->children, dst);
1885 return rc;
1888 static gpg_error_t
1889 find_parent_of_child (xmlDocPtr doc, xmlNodePtr node, xmlNodePtr dst)
1891 xmlNodePtr n;
1892 gpg_error_t rc = 0;
1894 for (n = node; n; n = n->parent)
1896 if (n->type != XML_ELEMENT_NODE)
1898 xmlNodePtr tmp;
1900 for (tmp = n->next; tmp; n = n->next)
1902 if (n->type != XML_ELEMENT_NODE)
1903 continue;
1905 if (tmp == dst)
1906 return GPG_ERR_ELOOP;
1910 if (n == dst)
1911 return GPG_ERR_ELOOP;
1914 return rc;
1917 gpg_error_t
1918 validate_target_attribute (xmlDocPtr doc, const char *src,
1919 xmlNodePtr dst_node)
1921 gpg_error_t rc;
1922 xmlNodePtr src_node;
1923 char **src_req = NULL;
1925 src_node = resolve_path (doc, (xmlChar *) src, &src_req, &rc);
1926 if (rc)
1927 goto fail;
1929 /* A destination element is a child of the source element. */
1930 rc = find_child_of_parent (doc, src_node->children, dst_node);
1931 if (rc)
1932 goto fail;
1934 /* The destination element is a parent of the source element. */
1935 rc = find_parent_of_child (doc, src_node->parent, dst_node);
1936 if (rc)
1937 goto fail;
1939 /* A destination child element contains a target to the source element. */
1940 rc = find_child_to_target (doc, dst_node->children, src_node, dst_node);
1941 if (rc)
1942 goto fail;
1944 fail:
1945 strv_free (src_req);
1946 return rc;