6 * Copyright (C) 2010 SIPE Project <http://sipe.sourceforge.net/>
8 * This program 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 * This program 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 this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 * This code is loosely based on libpurple xmlnode.c
31 #include "libxml/parser.h"
34 #include "sipe-backend.h"
35 #include "sipe-utils.h"
45 GHashTable
*attributes
;
54 static void callback_start_element(void *user_data
, const xmlChar
*name
, const xmlChar
**attrs
)
56 struct _parser_data
*pd
= user_data
;
60 if (!name
|| pd
->error
) return;
62 node
= g_new0(sipe_xml
, 1);
64 if ((tmp
= strchr((char *)name
, ':')) != NULL
) {
65 name
= (xmlChar
*)tmp
+ 1;
67 node
->name
= g_strdup((gchar
*)name
);
72 sipe_xml
*current
= pd
->current
;
74 node
->parent
= current
;
76 current
->last
->sibling
= node
;
78 current
->first
= node
;
86 node
->attributes
= g_hash_table_new_full(g_str_hash
,
87 (GEqualFunc
) sipe_strcase_equal
,
89 while ((key
= *attrs
++) != NULL
) {
90 if ((tmp
= strchr((char *)key
, ':')) != NULL
) {
91 key
= (xmlChar
*)tmp
+ 1;
93 /* libxml2 decodes all entities except &.
94 & is replaced by the equivalent & */
95 g_hash_table_insert(node
->attributes
,
96 g_strdup((gchar
*) key
),
97 replace((gchar
*) *attrs
++, "&", "&"));
104 static void callback_end_element(void *user_data
, const xmlChar
*name
)
106 struct _parser_data
*pd
= user_data
;
108 if (!name
|| !pd
->current
|| pd
->error
) return;
110 if (pd
->current
->parent
)
111 pd
->current
= pd
->current
->parent
;
114 static void callback_characters(void *user_data
, const xmlChar
*text
, int text_len
)
116 struct _parser_data
*pd
= user_data
;
119 if (!pd
->current
|| pd
->error
|| !text
|| !text_len
) return;
123 node
->data
= g_string_append_len(node
->data
, (gchar
*)text
, text_len
);
125 node
->data
= g_string_new_len((gchar
*)text
, text_len
);
128 static void callback_error(void *user_data
, const char *msg
, ...)
130 struct _parser_data
*pd
= user_data
;
137 errmsg
= g_strdup_vprintf(msg
, args
);
140 SIPE_DEBUG_ERROR("error parsing xml string: %s", errmsg
);
144 static void callback_serror(void *user_data
, xmlErrorPtr error
)
146 struct _parser_data
*pd
= user_data
;
148 if (error
&& (error
->level
== XML_ERR_ERROR
||
149 error
->level
== XML_ERR_FATAL
)) {
151 SIPE_DEBUG_ERROR("XML parser error: Domain %i, code %i, level %i: %s",
152 error
->domain
, error
->code
, error
->level
,
153 error
->message
? error
->message
: "(null)");
155 SIPE_DEBUG_WARNING("XML parser error: Domain %i, code %i, level %i: %s",
156 error
->domain
, error
->code
, error
->level
,
157 error
->message
? error
->message
: "(null)");
159 /* *sigh* macro expects at least two parameters */
160 SIPE_DEBUG_WARNING_NOFORMAT("XML parser error");
164 /* API doesn't accept const data structure */
165 static xmlSAXHandler parser
= {
166 NULL
, /* internalSubset */
167 NULL
, /* isStandalone */
168 NULL
, /* hasInternalSubset */
169 NULL
, /* hasExternalSubset */
170 NULL
, /* resolveEntity */
171 NULL
, /* getEntity */
172 NULL
, /* entityDecl */
173 NULL
, /* notationDecl */
174 NULL
, /* attributeDecl */
175 NULL
, /* elementDecl */
176 NULL
, /* unparsedEntityDecl */
177 NULL
, /* setDocumentLocator */
178 NULL
, /* startDocument */
179 NULL
, /* endDocument */
180 callback_start_element
, /* startElement */
181 callback_end_element
, /* endElement */
182 NULL
, /* reference */
183 callback_characters
, /* characters */
184 NULL
, /* ignorableWhitespace */
185 NULL
, /* processingInstruction */
188 callback_error
, /* error */
189 NULL
, /* fatalError */
190 NULL
, /* getParameterEntity */
191 NULL
, /* cdataBlock */
192 NULL
, /* externalSubset */
193 XML_SAX2_MAGIC
, /* initialized */
195 NULL
, /* startElementNs */
196 NULL
, /* endElementNs */
197 callback_serror
, /* serror */
200 sipe_xml
*sipe_xml_parse(const gchar
*string
, gsize length
)
202 sipe_xml
*result
= NULL
;
204 if (string
&& length
) {
205 struct _parser_data
*pd
= g_new0(struct _parser_data
, 1);
207 if (xmlSAXUserParseMemory(&parser
, pd
, string
, length
))
211 sipe_xml_free(pd
->root
);
222 void sipe_xml_free(sipe_xml
*node
)
228 /* we don't support partial tree deletion */
229 if (node
->parent
!= NULL
) {
230 SIPE_DEBUG_ERROR_NOFORMAT("sipe_xml_free: partial delete attempt! Expect crash or memory leaks...");
236 sipe_xml
*tmp
= child
->sibling
;
237 child
->parent
= NULL
; /* detach from tree, see above */
238 sipe_xml_free(child
);
244 if (node
->data
) g_string_free(node
->data
, TRUE
);
245 if (node
->attributes
) g_hash_table_destroy(node
->attributes
);
249 static void sipe_xml_stringify_attribute(gpointer key
, gpointer value
,
252 g_string_append_printf(user_data
, " %s=\"%s\"",
253 (const gchar
*) key
, (const gchar
*) value
);
256 static void sipe_xml_stringify_node(GString
*s
, const sipe_xml
*node
)
258 g_string_append_printf(s
, "<%s", node
->name
);
260 if (node
->attributes
) {
261 g_hash_table_foreach(node
->attributes
,
262 (GHFunc
) sipe_xml_stringify_attribute
,
266 if (node
->data
|| node
->first
) {
267 const sipe_xml
*child
;
269 g_string_append_printf(s
, ">%s",
270 node
->data
? node
->data
->str
: "");
272 for (child
= node
->first
; child
; child
= child
->sibling
)
273 sipe_xml_stringify_node(s
, child
);
275 g_string_append_printf(s
, "</%s>", node
->name
);
277 g_string_append(s
, "/>");
281 gchar
*sipe_xml_stringify(const sipe_xml
*node
)
285 if (!node
) return NULL
;
287 s
= g_string_new("");
288 sipe_xml_stringify_node(s
, node
);
289 return g_string_free(s
, FALSE
);
292 const sipe_xml
*sipe_xml_child(const sipe_xml
*parent
, const gchar
*name
)
295 const sipe_xml
*child
= NULL
;
297 if (!parent
|| !name
) return NULL
;
300 /* 1: trailing path (optional) */
301 names
= g_strsplit(name
, "/", 2);
303 for (child
= parent
->first
; child
; child
= child
->sibling
) {
304 if (sipe_strequal(names
[0], child
->name
))
308 /* recurse into path */
309 if (child
&& names
[1])
310 child
= sipe_xml_child(child
, names
[1]);
316 const sipe_xml
*sipe_xml_twin(const sipe_xml
*node
)
320 if (!node
) return NULL
;
322 for (sibling
= node
->sibling
; sibling
; sibling
= sibling
->sibling
) {
323 if (sipe_strequal(node
->name
, sibling
->name
))
329 const gchar
*sipe_xml_name(const sipe_xml
*node
)
331 return(node
? node
->name
: NULL
);
334 const gchar
*sipe_xml_attribute(const sipe_xml
*node
, const gchar
*attr
)
336 if (!node
|| !attr
|| !node
->attributes
) return NULL
;
337 return(g_hash_table_lookup(node
->attributes
, attr
));
340 guint
sipe_xml_int_attribute(const sipe_xml
*node
, const gchar
*attr
,
343 const gchar
*value
= sipe_xml_attribute(node
, attr
);
344 return(value
? g_ascii_strtoll(value
, NULL
, 10) : fallback
);
347 gchar
*sipe_xml_data(const sipe_xml
*node
)
349 if (!node
|| !node
->data
|| !node
->data
->str
) return NULL
;
350 return g_strdup(node
->data
->str
);