add "603 Decline" response to undelivered message handling
[siplcs.git] / src / core / sipe-xml.c
blobf4e9d378a2acf432c0c929d27702d616db6752db
1 /**
2 * @file sipe-xml.c
4 * pidgin-sipe
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
27 #include <stdarg.h>
28 #include <string.h>
29 #include <time.h>
31 #include "libxml/parser.h"
32 #include "glib.h"
34 #include "sipe-backend.h"
35 #include "sipe-utils.h"
36 #include "sipe-xml.h"
38 struct _sipe_xml {
39 gchar *name;
40 sipe_xml *parent;
41 sipe_xml *sibling;
42 sipe_xml *first;
43 sipe_xml *last;
44 GString *data;
45 GHashTable *attributes;
48 struct _parser_data {
49 sipe_xml *root;
50 sipe_xml *current;
51 gboolean error;
54 static void callback_start_element(void *user_data, const xmlChar *name, const xmlChar **attrs)
56 struct _parser_data *pd = user_data;
57 const char *tmp;
58 sipe_xml *node;
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);
69 if (!pd->root) {
70 pd->root = node;
71 } else {
72 sipe_xml *current = pd->current;
74 node->parent = current;
75 if (current->last) {
76 current->last->sibling = node;
77 } else {
78 current->first = node;
80 current->last = node;
83 if (attrs) {
84 const xmlChar *key;
86 node->attributes = g_hash_table_new_full(g_str_hash,
87 (GEqualFunc) sipe_strcase_equal,
88 g_free, g_free);
89 while ((key = *attrs++) != NULL) {
90 if ((tmp = strchr((char *)key, ':')) != NULL) {
91 key = (xmlChar *)tmp + 1;
93 /* libxml2 decodes all entities except &amp;.
94 &amp; is replaced by the equivalent &#38; */
95 g_hash_table_insert(node->attributes,
96 g_strdup((gchar *) key),
97 replace((gchar *) *attrs++, "&#38;", "&"));
101 pd->current = node;
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;
117 sipe_xml *node;
119 if (!pd->current || pd->error || !text || !text_len) return;
121 node = pd->current;
122 if (node->data)
123 node->data = g_string_append_len(node->data, (gchar *)text, text_len);
124 else
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;
131 gchar *errmsg;
132 va_list args;
134 pd->error = TRUE;
136 va_start(args, msg);
137 errmsg = g_strdup_vprintf(msg, args);
138 va_end(args);
140 SIPE_DEBUG_ERROR("error parsing xml string: %s", errmsg);
141 g_free(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)) {
150 pd->error = TRUE;
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)");
154 } else if (error) {
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)");
158 } else {
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 */
186 NULL, /* comment */
187 NULL, /* warning */
188 callback_error, /* error */
189 NULL, /* fatalError */
190 NULL, /* getParameterEntity */
191 NULL, /* cdataBlock */
192 NULL, /* externalSubset */
193 XML_SAX2_MAGIC, /* initialized */
194 NULL, /* _private */
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))
208 pd->error = TRUE;
210 if (pd->error) {
211 sipe_xml_free(pd->root);
212 } else {
213 result = pd->root;
216 g_free(pd);
219 return result;
222 void sipe_xml_free(sipe_xml *node)
224 sipe_xml *child;
226 if (!node) return;
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...");
233 /* free children */
234 child = node->first;
235 while (child) {
236 sipe_xml *tmp = child->sibling;
237 child->parent = NULL; /* detach from tree, see above */
238 sipe_xml_free(child);
239 child = tmp;
242 /* free node */
243 g_free(node->name);
244 if (node->data) g_string_free(node->data, TRUE);
245 if (node->attributes) g_hash_table_destroy(node->attributes);
246 g_free(node);
249 static void sipe_xml_stringify_attribute(gpointer key, gpointer value,
250 gpointer user_data)
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);
276 } else {
277 g_string_append(s, "/>");
281 gchar *sipe_xml_stringify(const sipe_xml *node)
283 GString *s;
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)
294 gchar **names;
295 const sipe_xml *child = NULL;
297 if (!parent || !name) return NULL;
299 /* 0: child name */
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))
305 break;
308 /* recurse into path */
309 if (child && names[1])
310 child = sipe_xml_child(child, names[1]);
312 g_strfreev(names);
313 return child;
316 const sipe_xml *sipe_xml_twin(const sipe_xml *node)
318 sipe_xml *sibling;
320 if (!node) return NULL;
322 for (sibling = node->sibling; sibling; sibling = sibling->sibling) {
323 if (sipe_strequal(node->name, sibling->name))
324 return sibling;
326 return NULL;
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,
341 guint fallback)
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);
354 Local Variables:
355 mode: c
356 c-file-style: "bsd"
357 indent-tabs-mode: t
358 tab-width: 8
359 End: