1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2007 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 02111-1307 USA
35 #include "pwmd_error.h"
39 void log_write(const gchar
*fmt
, ...);
40 gboolean
strv_printf(gchar
***array
, const gchar
*fmt
, ...);
42 gboolean
is_literal_element_str(gchar
*element
)
44 if (!element
|| !*element
)
47 return *element
== '!' ? TRUE
: FALSE
;
51 * 'element' must be allocated.
53 gboolean
is_literal_element(gchar
**element
)
57 if (!element
|| !*element
)
60 if (*(*element
) == '!') {
63 for (p
= *element
, c
= p
+1; *c
; c
++)
74 * Fails if 'element' begins with punctuation or digit or contains whitespace.
75 * If the element is a literal element which was prefixed with '!', it should
76 * be trimmed before calling this function.
78 * I'm not sure about using g_unichar_isspace() rather than isspace()?
80 gboolean
valid_xml_element(xmlChar
*element
)
84 gchar
*p
= (gchar
*)element
;
86 if (!element
|| !*element
)
89 len
= g_utf8_strlen(p
, -1) - 1;
90 c
= g_utf8_get_char(p
++);
92 if (g_unichar_ispunct(c
) == TRUE
|| g_unichar_isdigit(c
) == TRUE
)
96 c
= g_utf8_get_char(p
++);
98 if (g_unichar_isspace(c
))
105 gpg_error_t
new_account(xmlDocPtr doc
, gchar
*name
)
107 xmlNodePtr root
= xmlDocGetRootElement(doc
);
113 return EPWMD_LIBXML_ERROR
;
115 if (is_literal_element_str(p
)) {
119 * Event though the account name is an attribute and not an element,
120 * element syntax still applies.
122 if (valid_xml_element((xmlChar
*)p
) == FALSE
)
123 return EPWMD_INVALID_ELEMENT
;
126 n
= xmlNewNode(NULL
, (xmlChar
*)"account");
127 n
= xmlAddChild(root
, n
);
128 a
= xmlNewProp(n
, (xmlChar
*)"name", (xmlChar
*)p
);
132 xmlChar
*new_document()
135 const xmlChar
*line
= (xmlChar
*)
136 "<?xml version=\"1.0\"?>\n"
137 "<!DOCTYPE accounts [\n"
138 "<!ELEMENT accounts (account*)>\n"
139 "<!ATTLIST account name CDATA #REQUIRED>\n"
143 buf
= gcry_calloc(1, xmlStrlen(line
) + 1);
144 return buf
? xmlStrcat(buf
, line
) : NULL
;
148 * The "target" attribute is ignored here.
150 gpg_error_t
list_accounts(xmlDocPtr doc
, GString
**result
)
152 xmlNodePtr n
= NULL
, t
;
157 for (t
= doc
->children
; t
; t
= t
->next
) {
158 if (t
->type
== XML_ELEMENT_NODE
) {
159 if (xmlStrEqual(t
->name
, (xmlChar
*)"accounts")) {
166 if (!n
|| !n
->children
)
167 return EPWMD_EMPTY_ELEMENT
;
169 for (n
= n
->children
; n
; n
= n
->next
) {
174 if (n
->type
!= XML_ELEMENT_NODE
)
177 a
= xmlHasProp(n
, (xmlChar
*)"name");
179 if (!a
|| !a
->children
->content
)
182 tmp
= g_strdup((gchar
*)a
->children
->content
);
186 return gpg_error_from_errno(ENOMEM
);
189 tlist
= g_slist_append(list
, tmp
);
193 return gpg_error_from_errno(ENOMEM
);
199 total
= g_slist_length(list
);
202 return EPWMD_EMPTY_ELEMENT
;
204 string
= g_string_new(NULL
);
208 return gpg_error_from_errno(ENOMEM
);
211 for (i
= 0; i
< total
; i
++) {
212 gchar
*tmp
= g_slist_nth_data(list
, i
);
214 g_string_append_printf(string
, "%s\n", tmp
);
218 string
= g_string_truncate(string
, string
->len
- 1);
224 gchar
**split_input_line(gchar
*str
, gchar
*delim
, gint n
)
229 return g_strsplit(str
, delim
, n
);
232 static gchar
**append_element_path(gchar
**dst
, gchar
**src
)
246 i
= g_strv_length(d
);
248 for (p
= src
; *p
; p
++) {
251 pa
= g_realloc(d
, (i
+ 2) * sizeof(gchar
*));
273 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
274 * content but as an element.
276 xmlNodePtr
create_target_elements_cb(xmlNodePtr node
, gchar
**path
,
277 gpg_error_t
*error
, void *data
)
282 if (xmlStrEqual(node
->name
, (xmlChar
*)*req
))
285 for (i
= 0; req
[i
]; i
++) {
288 if (valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
289 *error
= EPWMD_INVALID_ELEMENT
;
293 if ((n
= find_element(node
, req
[i
])) == NULL
) {
294 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
297 *error
= gpg_error_from_errno(ENOMEM
);
301 node
= xmlAddChild(node
, n
);
304 *error
= gpg_error_from_errno(ENOMEM
);
315 xmlNodePtr
create_elements_cb(xmlNodePtr node
, gchar
**elements
,
316 gpg_error_t
*error
, void *data
)
319 gchar
**req
= elements
;
321 if (node
->type
== XML_TEXT_NODE
)
324 if (node
->name
&& xmlStrEqual(node
->name
, (xmlChar
*)*req
))
327 for (i
= 0; req
[i
]; i
++) {
332 * Strip the first '!' if needed. If there's another, it's an
335 is_literal_element(&req
[i
]);
337 if (valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
338 *error
= EPWMD_INVALID_ELEMENT
;
344 * The value of the element tree.
347 if (node
->children
) {
348 n
= node
->children
->next
? xmlCopyNode(node
->children
->next
, 1) :
349 xmlCopyNode(node
->children
, 1);
352 *error
= gpg_error_from_errno(ENOMEM
);
365 xmlNodeSetContent(node
, (xmlChar
*)req
[i
]);
366 else if (node
->content
) {
367 xmlFree(node
->content
);
368 node
->content
= NULL
;
372 if (!xmlAddChild(node
, n
)) {
373 *error
= gpg_error_from_errno(ENOMEM
);
381 n
= find_element(node
, req
[i
]);
384 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
387 *error
= gpg_error_from_errno(ENOMEM
);
391 node
= xmlAddChild(node
, n
);
394 *error
= gpg_error_from_errno(ENOMEM
);
405 xmlNodePtr
find_account(xmlDocPtr doc
, gchar
***req
, gpg_error_t
*error
,
410 gchar
*account
= g_strdup(*req
[0]);
411 gboolean literal
= is_literal_element(&account
);
412 static int recursion_depth
;
415 *error
= gpg_error_from_errno(ENOMEM
);
422 if (max_recursion_depth
>= 1 && recursion_depth
> max_recursion_depth
) {
428 for (n
= doc
->children
; n
;) {
429 if (n
->type
== XML_ELEMENT_NODE
) {
430 if (depth
== 0 && xmlStrEqual(n
->name
, (xmlChar
*)"accounts")) {
436 if (depth
== 1 && xmlStrEqual(n
->name
, (xmlChar
*)"account")) {
437 xmlChar
*content
= node_has_attribute(n
, (xmlChar
*)"name");
442 if (xmlStrEqual(content
, (xmlChar
*)account
)) {
443 gchar
**nreq
, **tmp
= NULL
;
445 if (literal
== TRUE
) {
451 content
= node_has_attribute(n
, (xmlChar
*)"target");
459 if (strchr((gchar
*)content
, '\t')) {
460 nreq
= split_input_line((gchar
*)content
, "\t", 0);
467 *error
= gpg_error_from_errno(ENOMEM
);
473 tmp
= append_element_path(nreq
, tmp
+1);
477 *error
= gpg_error_from_errno(ENOMEM
);
485 if (strv_printf(&tmp
, "%s", content
) == FALSE
) {
486 *error
= gpg_error_from_errno(ENOMEM
);
491 nreq
= append_element_path(tmp
, nreq
+1);
495 *error
= gpg_error_from_errno(ENOMEM
);
507 n
= find_account(doc
, req
, error
, target
);
518 *error
= EPWMD_ELEMENT_NOT_FOUND
;
523 xmlNodePtr
find_sibling(xmlNodePtr node
, gchar
*element
)
527 if (!node
|| !element
)
530 for (n
= node
; n
; n
= n
->next
) {
531 if (n
->type
!= XML_ELEMENT_NODE
)
534 if (xmlStrEqual(n
->name
, (xmlChar
*)element
))
541 xmlNodePtr
find_element(xmlNodePtr node
, gchar
*element
)
543 if (!node
|| !element
)
546 return find_sibling(node
, element
);
549 xmlChar
*node_has_attribute(xmlNodePtr n
, xmlChar
*attr
)
551 xmlAttrPtr a
= xmlHasProp(n
, attr
);
556 if (!a
->children
|| !a
->children
->content
)
559 return a
->children
->content
;
562 static gboolean
element_to_literal(gchar
**element
)
564 gchar
*p
= g_strdup_printf("!%s", *element
);
574 xmlNodePtr
find_elements(xmlDocPtr doc
, xmlNodePtr node
,
575 gchar
**req
, gpg_error_t
*error
, gboolean
*target
,
576 xmlNodePtr (*found_fn
)(xmlNodePtr
, gchar
**, gpg_error_t
*, void *),
577 xmlNodePtr (*not_found_fn
)(xmlNodePtr
, gchar
**, gpg_error_t
*, void *),
580 xmlNodePtr n
, last
, last_node
;
583 static int recursion_depth
;
588 if (max_recursion_depth
>= 1 && recursion_depth
> max_recursion_depth
) {
594 for (last_node
= last
= n
= node
, p
= req
; *p
; p
++) {
596 gchar
*t
= g_strdup(*p
);
600 *error
= gpg_error_from_errno(ENOMEM
);
605 literal
= is_literal_element(&t
);
606 n
= find_element(last
, t
);
613 return not_found_fn(found
? last_node
: last_node
->parent
, p
, error
, data
);
615 *error
= EPWMD_ELEMENT_NOT_FOUND
;
623 if (literal
== FALSE
) {
624 xmlChar
*content
= node_has_attribute(n
, (xmlChar
*)"target");
625 gchar
**nreq
= NULL
, **nnreq
;
628 if (is_list_command
== TRUE
) {
629 if (element_to_literal(&(*p
)) == FALSE
) {
630 *error
= gpg_error_from_errno(ENOMEM
);
639 if (strchr((gchar
*)content
, '\t') != NULL
) {
640 if ((nreq
= split_input_line((gchar
*)content
, "\t", 0)) == NULL
) {
641 *error
= EPWMD_INVALID_ELEMENT
;
647 if ((nreq
= split_input_line((gchar
*)content
, " ", 0)) == NULL
) {
648 *error
= EPWMD_INVALID_ELEMENT
;
654 tmp
= find_account(doc
, &nreq
, error
, target
);
663 found_fn(n
, nreq
, error
, data
);
665 nnreq
= append_element_path(nreq
+1, p
+1);
669 if (!nnreq
|| !*nnreq
) {
680 n
= find_elements(doc
, tmp
->children
, nnreq
, error
, NULL
, found_fn
,
684 gchar
**zz
= p
+1, **qq
= nnreq
;
686 if (g_strv_length(nnreq
) > g_strv_length(p
+1))
689 for (; *qq
&& *zz
; zz
++) {
691 *zz
= g_strdup(*qq
++);
694 *error
= gpg_error_from_errno(ENOMEM
);
711 gboolean
node_has_child_element(xmlNodePtr node
)
718 for (n
= node
; n
; n
= n
->next
) {
719 if (n
->type
== XML_ELEMENT_NODE
)
723 return node_has_child_element(n
->children
);