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"
38 void log_write(const gchar
*fmt
, ...);
39 gboolean
strv_printf(gchar
***array
, const gchar
*fmt
, ...);
41 gboolean
is_literal_element_str(gchar
*element
)
43 if (!element
|| !*element
)
46 return *element
== '!' ? TRUE
: FALSE
;
50 * 'element' must be allocated.
52 gboolean
is_literal_element(gchar
**element
)
56 if (!element
|| !*element
)
59 if (*(*element
) == '!') {
60 p
= g_strdup(*element
+1);
70 * libxml doesn't seem to mind funny element names so we won't either.
72 gboolean
valid_xml_element(xmlChar
*element
)
74 if (!element
|| !*element
)
80 gpg_error_t
new_account(xmlDocPtr doc
, gchar
*name
)
82 xmlNodePtr root
= xmlDocGetRootElement(doc
);
87 return EPWMD_LIBXML_ERROR
;
90 * ! as the first character of an account name is bad. This character is
91 * used to get an element path reguardless if there is a "target"
95 return EPWMD_INVALID_ELEMENT
;
97 n
= xmlNewNode(NULL
, (xmlChar
*)"account");
98 n
= xmlAddChild(root
, n
);
99 a
= xmlNewProp(n
, (xmlChar
*)"name", (xmlChar
*)name
);
103 gchar
*new_document()
107 "<?xml version=\"1.0\"?>\n"
108 "<!DOCTYPE accounts [\n"
109 "<!ELEMENT accounts (account*)>\n"
110 "<!ATTLIST account name CDATA #REQUIRED>\n"
114 if ((buf
= gcry_malloc(strlen(line
) + 1)) == NULL
)
121 gint
sort_element_list(gconstpointer a
, gconstpointer b
)
126 ka
= g_utf8_collate_key(a
, -1);
127 kb
= g_utf8_collate_key(b
, -1);
135 * The "target" attribute is ignored here.
137 gpg_error_t
list_accounts(xmlDocPtr doc
, GString
**result
)
139 xmlNodePtr n
= NULL
, t
;
145 for (t
= doc
->children
; t
; t
= t
->next
) {
146 if (t
->type
== XML_ELEMENT_NODE
) {
147 if (xmlStrEqual(t
->name
, (xmlChar
*)"accounts")) {
154 if (!n
|| !n
->children
)
155 return EPWMD_EMPTY_ELEMENT
;
157 for (n
= n
->children
; n
; n
= n
->next
) {
160 if (n
->type
!= XML_ELEMENT_NODE
)
163 a
= xmlHasProp(n
, (xmlChar
*)"name");
165 if (!a
|| !a
->children
->content
)
168 list
= g_slist_append(list
, g_strdup(a
->children
->content
));
171 total
= g_slist_length(list
);
174 return EPWMD_EMPTY_ELEMENT
;
176 list
= g_slist_sort(list
, sort_element_list
);
177 string
= g_string_new(NULL
);
179 for (i
= 0; i
< total
; i
++) {
180 gchar
*tmp
= g_slist_nth_data(list
, i
);
182 g_string_append_printf(string
, "%s\n", tmp
);
191 gchar
**split_input_line(gchar
*str
, gchar
*delim
, gint n
)
196 return g_strsplit(str
, delim
, n
);
199 static gchar
**append_element_path(gchar
**dst
, gchar
**src
)
209 i
= g_strv_length(d
);
211 for (p
= src
; *p
; p
++) {
212 d
= g_realloc(d
, (i
+ 2) * sizeof(gchar
*));
213 d
[i
++] = g_strdup(*p
);
221 * Alot like create_elements_cb() but doesn't use the last element of 'req' as
222 * content but as an element.
224 xmlNodePtr
create_target_elements_cb(xmlNodePtr node
, gchar
**path
,
225 gpg_error_t
*error
, void *data
)
230 if (xmlStrEqual(node
->name
, (xmlChar
*)*req
))
233 for (i
= 0; req
[i
]; i
++) {
237 * Whitespace is allowed in values or attributes but not in element
240 if (valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
241 *error
= EPWMD_INVALID_ELEMENT
;
245 if ((n
= find_element(node
, (xmlChar
*)req
[i
])) == NULL
) {
246 n
= xmlNewNode(NULL
, (xmlChar
*)req
[i
]);
247 node
= xmlAddChild(node
, n
);
256 xmlNodePtr
create_elements_cb(xmlNodePtr node
, gchar
**elements
,
257 gpg_error_t
*error
, void *data
)
260 gchar
**req
= elements
;
262 if (node
->type
== XML_TEXT_NODE
)
265 if (node
->name
&& xmlStrEqual(node
->name
, (xmlChar
*)*req
))
268 for (i
= 0; req
[i
]; i
++) {
272 * Whitespace is allowed in values or attributes but not in element
275 if (req
[i
+1] && valid_xml_element((xmlChar
*)req
[i
]) == FALSE
) {
276 *error
= EPWMD_INVALID_ELEMENT
;
281 * The value of the element tree.
284 if (node
->children
) {
285 n
= node
->children
->next
? xmlCopyNode(node
->children
->next
, 1) :
286 xmlCopyNode(node
->children
, 1);
297 xmlNodeSetContent(node
, req
[i
]);
298 else if (node
->content
) {
299 xmlFree(node
->content
);
300 node
->content
= NULL
;
304 xmlAddChild(node
, n
);
309 n
= find_element(node
, req
[i
]);
312 n
= xmlNewNode(NULL
, req
[i
]);
313 node
= xmlAddChild(node
, n
);
322 xmlNodePtr
find_account(xmlDocPtr doc
, gchar
***req
, gpg_error_t
*error
,
327 gchar
*account
= g_strdup(*req
[0]);
328 gboolean literal
= is_literal_element(&account
);
332 for (n
= doc
->children
; n
;) {
333 if (n
->type
== XML_ELEMENT_NODE
) {
334 if (depth
== 0 && xmlStrEqual(n
->name
, (xmlChar
*)"accounts")) {
340 if (depth
== 1 && xmlStrEqual(n
->name
, (xmlChar
*)"account")) {
341 xmlChar
*content
= node_has_attribute(n
, "name");
346 if (xmlStrEqual(content
, account
)) {
347 gchar
**nreq
, **tmp
= NULL
;
349 if (literal
== TRUE
) {
354 content
= node_has_attribute(n
, "target");
361 if (strchr((gchar
*)content
, '\t')) {
362 nreq
= split_input_line((gchar
*)content
, "\t", 0);
364 tmp
= append_element_path(nreq
, tmp
+1);
370 strv_printf(&tmp
, "%s", content
);
372 nreq
= append_element_path(tmp
, nreq
+1);
381 return find_account(doc
, req
, error
, target
);
390 *error
= EPWMD_ELEMENT_NOT_FOUND
;
394 xmlNodePtr
find_sibling(xmlNodePtr node
, gchar
*element
)
398 if (!node
|| !element
)
401 for (n
= node
; n
; n
= n
->next
) {
402 if (n
->type
!= XML_ELEMENT_NODE
)
405 if (xmlStrEqual(n
->name
, (xmlChar
*)element
))
412 xmlNodePtr
find_element(xmlNodePtr node
, gchar
*element
)
414 if (!node
|| !element
)
417 return find_sibling(node
, element
);
420 static void element_to_literal(gchar
**element
)
422 gchar
*p
= g_strdup_printf("!%s", *element
);
427 xmlChar
*node_has_attribute(xmlNodePtr n
, xmlChar
*attr
)
429 xmlAttrPtr a
= xmlHasProp(n
, attr
);
434 if (!a
->children
|| !a
->children
->content
)
437 return a
->children
->content
;
440 xmlNodePtr
find_elements(xmlDocPtr doc
, xmlNodePtr node
,
441 gchar
**req
, gpg_error_t
*error
, gboolean
*target
,
442 xmlNodePtr (*found_fn
)(xmlNodePtr
, gchar
**, gpg_error_t
*, void *),
443 xmlNodePtr (*not_found_fn
)(xmlNodePtr
, gchar
**, gpg_error_t
*, void *),
446 xmlNodePtr n
, last
, last_node
;
452 for (last_node
= last
= n
= node
, p
= req
; *p
; p
++) {
454 gchar
*t
= g_strdup(*p
);
455 gboolean literal
= is_literal_element(&t
);
457 n
= find_element(last
, t
);
462 return not_found_fn(found
? last_node
: last_node
->parent
, p
, error
, data
);
464 *error
= EPWMD_ELEMENT_NOT_FOUND
;
472 if (literal
== FALSE
) {
473 xmlChar
*content
= node_has_attribute(n
, "target");
474 gchar
**nreq
= NULL
, **nnreq
;
477 element_to_literal(&(*p
));
481 if (strchr((gchar
*)content
, '\t') != NULL
) {
482 if ((nreq
= split_input_line((gchar
*)content
, "\t", 0)) == NULL
) {
483 *error
= EPWMD_INVALID_ELEMENT
;
488 if ((nreq
= split_input_line((gchar
*)content
, " ", 0)) == NULL
) {
489 *error
= EPWMD_INVALID_ELEMENT
;
494 tmp
= find_account(doc
, &nreq
, error
, target
);
502 found_fn(n
, nreq
, error
, data
);
504 nnreq
= append_element_path(nreq
+1, p
+1);
507 if (!nnreq
|| !*nnreq
)
513 n
= find_elements(doc
, tmp
->children
, nnreq
, error
, NULL
, found_fn
,
517 gchar
**zz
= p
+1, **qq
= nnreq
;
519 if (g_strv_length(nnreq
) > g_strv_length(p
+1))
522 for (; *qq
&& *zz
; zz
++) {
524 *zz
= g_strdup(*qq
++);
536 gboolean
node_has_child_element(xmlNodePtr node
)
543 for (n
= node
; n
; n
= n
->next
) {
544 if (n
->type
== XML_ELEMENT_NODE
)
548 return node_has_child_element(n
->children
);