try new method for sending find peer requests
[oweals/gnunet.git] / src / upnp / upnp_xmlnode.c
1 /**
2  * @file upnp/upnp_xmlnode.c XML DOM functions
3  *
4  * gaim
5  *
6  * Gaim is the legal property of its developers, whose names are too numerous
7  * to list here.  Please refer to the COPYRIGHT file distributed with this
8  * source distribution.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 /* A lot of this code at least resembles the code in libxode, but since
26  * libxode uses memory pools that we simply have no need for, I decided to
27  * write my own stuff.  Also, re-writing this lets me be as lightweight
28  * as I want to be.  Thank you libxode for giving me a good starting point */
29
30 #include "platform.h"
31
32 #include "util.h"
33 #include "gnunet_util.h"
34 #include "xmlnode.h"
35
36 #include <libxml/parser.h>
37 #include <string.h>
38
39
40 #ifdef _WIN32
41 # define NEWLINE_S "\r\n"
42 #else
43 # define NEWLINE_S "\n"
44 #endif
45
46 #define TRUE GNUNET_YES
47 #define FALSE GNUNET_NO
48
49 #define g_return_if_fail(a) if(!(a)) return;
50 #define g_return_val_if_fail(a, val) if(!(a)) return (val);
51
52 /**
53  * The valid types for an xmlnode
54  */
55 typedef enum _XMLNodeType
56 {
57   XMLNODE_TYPE_TAG,     /**< Just a tag */
58   XMLNODE_TYPE_ATTRIB,          /**< Has attributes */
59   XMLNODE_TYPE_DATA     /**< Has data */
60 } XMLNodeType;
61
62 typedef struct
63 {
64   xmlnode *current;
65   xmlnode **nodes;
66   unsigned int pos;
67   unsigned int size;
68 } XMLNodePool;
69
70 struct _xmlnode
71 {
72   char *name;           /**< The name of the node. */
73   char *xmlns;          /**< The namespace of the node */
74   XMLNodeType type;     /**< The type of the node. */
75   char *data;           /**< The data for the node. */
76   size_t data_sz;               /**< The size of the data. */
77   struct _xmlnode *parent;  /**< The parent node or @c NULL.*/
78   struct _xmlnode *child;       /**< The child node or @c NULL.*/
79   struct _xmlnode *lastchild;  /**< The last child node or @c NULL.*/
80   struct _xmlnode *next;        /**< The next node or @c NULL. */
81   XMLNodePool *pool;
82   int free_pool;                /* set to GNUNET_YES for the root node, which must free the pool */
83 };
84
85
86 static void *
87 g_memdup (const void *data, size_t s)
88 {
89   void *ret;
90
91   ret = GNUNET_malloc (s);
92   memcpy (ret, data, s);
93   return ret;
94 }
95
96 static char *
97 g_string_append_len (char *prefix, const void *data, size_t s)
98 {
99   char *ret;
100
101   ret = g_strdup_printf ("%s%.*s", prefix, s, data);
102   GNUNET_free (prefix);
103   return ret;
104 }
105
106 static xmlnode *
107 new_node (const char *name, XMLNodeType type, void *user_data)
108 {
109   xmlnode *node = GNUNET_malloc (sizeof (xmlnode));
110
111   node->name = name == NULL ? NULL : GNUNET_strdup (name);
112   node->type = type;
113   node->pool = user_data;
114   if (node->pool->size == node->pool->pos)
115     GNUNET_array_grow (node->pool->nodes, node->pool->size,
116                        node->pool->size * 2 + 64);
117   node->pool->nodes[node->pool->pos++] = node;
118   node->free_pool = 0;
119   return node;
120 }
121
122 static xmlnode *
123 xmlnode_new (const char *name, void *user_data)
124 {
125   g_return_val_if_fail (name != NULL, NULL);
126   return new_node (name, XMLNODE_TYPE_TAG, user_data);
127 }
128
129 static void
130 xmlnode_insert_child (xmlnode * parent, xmlnode * child)
131 {
132   g_return_if_fail (parent != NULL);
133   g_return_if_fail (child != NULL);
134
135   child->parent = parent;
136   if (parent->lastchild)
137     parent->lastchild->next = child;
138   else
139     parent->child = child;
140   parent->lastchild = child;
141 }
142
143 static xmlnode *
144 xmlnode_new_child (xmlnode * parent, const char *name, void *user_data)
145 {
146   xmlnode *node;
147
148   g_return_val_if_fail (parent != NULL, NULL);
149   g_return_val_if_fail (name != NULL, NULL);
150   node = new_node (name, XMLNODE_TYPE_TAG, user_data);
151   xmlnode_insert_child (parent, node);
152   return node;
153 }
154
155 static void
156 xmlnode_insert_data (xmlnode * node,
157                      const char *data, int size, void *user_data)
158 {
159   xmlnode *child;
160   size_t real_size;
161
162   g_return_if_fail (node != NULL);
163   g_return_if_fail (data != NULL);
164   g_return_if_fail (size != 0);
165   real_size = size == -1 ? strlen (data) : size;
166   child = new_node (NULL, XMLNODE_TYPE_DATA, user_data);
167   child->data = g_memdup (data, real_size);
168   child->data_sz = real_size;
169   xmlnode_insert_child (node, child);
170 }
171
172 static void
173 xmlnode_remove_attrib (xmlnode * node, const char *attr)
174 {
175   xmlnode *attr_node, *sibling = NULL;
176
177   g_return_if_fail (node != NULL);
178   g_return_if_fail (attr != NULL);
179
180   for (attr_node = node->child; attr_node; attr_node = attr_node->next)
181     {
182       if (attr_node->type == XMLNODE_TYPE_ATTRIB &&
183           !strcmp (attr_node->name, attr))
184         {
185           if (node->child == attr_node)
186             {
187               node->child = attr_node->next;
188             }
189           else
190             {
191               sibling->next = attr_node->next;
192             }
193           if (node->lastchild == attr_node)
194             {
195               node->lastchild = sibling;
196             }
197           xmlnode_free (attr_node);
198           return;
199         }
200       sibling = attr_node;
201     }
202 }
203
204 static void
205 xmlnode_set_attrib (xmlnode * node,
206                     const char *attr, const char *value, void *user_data)
207 {
208   xmlnode *attrib_node;
209
210   g_return_if_fail (node != NULL);
211   g_return_if_fail (attr != NULL);
212   g_return_if_fail (value != NULL);
213   xmlnode_remove_attrib (node, attr);
214   attrib_node = new_node (attr, XMLNODE_TYPE_ATTRIB, user_data);
215   attrib_node->data = GNUNET_strdup (value);
216   xmlnode_insert_child (node, attrib_node);
217 }
218
219 static void
220 xmlnode_set_namespace (xmlnode * node, const char *xmlns)
221 {
222   g_return_if_fail (node != NULL);
223   GNUNET_free_non_null (node->xmlns);
224   node->xmlns = GNUNET_strdup (xmlns);
225 }
226
227 static const char *
228 xmlnode_get_namespace (xmlnode * node)
229 {
230   g_return_val_if_fail (node != NULL, NULL);
231   return node->xmlns;
232 }
233
234 static void
235 freePool (XMLNodePool * pool)
236 {
237   unsigned int i;
238   xmlnode *x;
239
240   for (i = 0; i < pool->pos; i++)
241     {
242       x = pool->nodes[i];
243       GNUNET_free_non_null (x->name);
244       GNUNET_free_non_null (x->data);
245       GNUNET_free_non_null (x->xmlns);
246       GNUNET_free (x);
247     }
248   GNUNET_array_grow (pool->nodes, pool->size, 0);
249   GNUNET_free (pool);
250 }
251
252 void
253 xmlnode_free (xmlnode * node)
254 {
255   g_return_if_fail (node != NULL);
256   if (node->free_pool != GNUNET_YES)
257     return;
258   freePool (node->pool);
259 }
260
261 static xmlnode *
262 xmlnode_get_child_with_namespace (const xmlnode * parent,
263                                   const char *name, const char *ns)
264 {
265   xmlnode *x;
266   xmlnode *ret = NULL;
267   char *parent_name;
268   char *child_name;
269
270   if (parent == NULL)
271     return NULL;
272   if (name == NULL)
273     return NULL;
274
275   parent_name = GNUNET_strdup (name);
276   child_name = strstr (parent_name, "/");
277   if (child_name != NULL)
278     {
279       child_name[0] = '\0';
280       child_name++;
281     }
282
283   for (x = parent->child; x; x = x->next)
284     {
285       const char *xmlns = NULL;
286       if (ns)
287         xmlns = xmlnode_get_namespace (x);
288
289       if (x->type == XMLNODE_TYPE_TAG && name
290           && !strcmp (parent_name, x->name) && (!ns
291                                                 || (xmlns
292                                                     && !strcmp (ns, xmlns))))
293         {
294           ret = x;
295           break;
296         }
297     }
298
299   if (child_name && ret)
300     ret = xmlnode_get_child (ret, child_name);
301
302   GNUNET_free (parent_name);
303   return ret;
304 }
305
306 xmlnode *
307 xmlnode_get_child (const xmlnode * parent, const char *name)
308 {
309   return xmlnode_get_child_with_namespace (parent, name, NULL);
310 }
311
312 char *
313 xmlnode_get_data (xmlnode * node)
314 {
315   char *str = NULL;
316   xmlnode *c;
317
318   if (node == NULL)
319     return NULL;
320   for (c = node->child; c; c = c->next)
321     {
322       if (c->type == XMLNODE_TYPE_DATA)
323         {
324           if (!str)
325             str = GNUNET_strdup ("");
326           str = g_string_append_len (str, c->data, c->data_sz);
327         }
328     }
329   if (str == NULL)
330     return NULL;
331
332   return str;
333 }
334
335 static void
336 xmlnode_parser_element_start_libxml (void *user_data,
337                                      const xmlChar * element_name,
338                                      const xmlChar * prefix,
339                                      const xmlChar * xmlns,
340                                      int nb_namespaces,
341                                      const xmlChar ** namespaces,
342                                      int nb_attributes,
343                                      int nb_defaulted,
344                                      const xmlChar ** attributes)
345 {
346   XMLNodePool *xpd = user_data;
347   xmlnode *node;
348   int i;
349
350   if (!element_name)
351     return;
352   if (xpd->current)
353     node =
354       xmlnode_new_child (xpd->current, (const char *) element_name,
355                          user_data);
356   else
357     node = xmlnode_new ((const char *) element_name, user_data);
358
359   xmlnode_set_namespace (node, (const char *) xmlns);
360
361   for (i = 0; i < nb_attributes * 5; i += 5)
362     {
363       char *txt;
364       int attrib_len = attributes[i + 4] - attributes[i + 3];
365       char *attrib = GNUNET_malloc (attrib_len + 1);
366       memcpy (attrib, attributes[i + 3], attrib_len);
367       attrib[attrib_len] = '\0';
368       txt = attrib;
369       attrib = gaim_unescape_html (txt);
370       GNUNET_free (txt);
371       xmlnode_set_attrib (node, (const char *) attributes[i], attrib,
372                           user_data);
373       GNUNET_free (attrib);
374     }
375   xpd->current = node;
376 }
377
378 static void
379 xmlnode_parser_element_end_libxml (void *user_data,
380                                    const xmlChar * element_name,
381                                    const xmlChar * prefix,
382                                    const xmlChar * xmlns)
383 {
384   XMLNodePool *xpd = user_data;
385
386   if (!element_name || !xpd->current)
387     return;
388   if (xpd->current->parent)
389     {
390       if (!xmlStrcmp ((xmlChar *) xpd->current->name, element_name))
391         xpd->current = xpd->current->parent;
392     }
393 }
394
395 static void
396 xmlnode_parser_element_text_libxml (void *user_data,
397                                     const xmlChar * text, int text_len)
398 {
399   XMLNodePool *xpd = user_data;
400
401   if (!xpd->current || !text || !text_len)
402     return;
403   xmlnode_insert_data (xpd->current,
404                        (const char *) text, text_len, user_data);
405 }
406
407 static xmlSAXHandler xmlnode_parser_libxml = {
408   .internalSubset = NULL,
409   .isStandalone = NULL,
410   .hasInternalSubset = NULL,
411   .hasExternalSubset = NULL,
412   .resolveEntity = NULL,
413   .getEntity = NULL,
414   .entityDecl = NULL,
415   .notationDecl = NULL,
416   .attributeDecl = NULL,
417   .elementDecl = NULL,
418   .unparsedEntityDecl = NULL,
419   .setDocumentLocator = NULL,
420   .startDocument = NULL,
421   .endDocument = NULL,
422   .startElement = NULL,
423   .endElement = NULL,
424   .reference = NULL,
425   .characters = xmlnode_parser_element_text_libxml,
426   .ignorableWhitespace = NULL,
427   .processingInstruction = NULL,
428   .comment = NULL,
429   .warning = NULL,
430   .error = NULL,
431   .fatalError = NULL,
432   .getParameterEntity = NULL,
433   .cdataBlock = NULL,
434   .externalSubset = NULL,
435   .initialized = XML_SAX2_MAGIC,
436   ._private = NULL,
437   .startElementNs = xmlnode_parser_element_start_libxml,
438   .endElementNs = xmlnode_parser_element_end_libxml,
439   .serror = NULL
440 };
441
442 xmlnode *
443 xmlnode_from_str (const char *str, int size)
444 {
445   XMLNodePool *xpd;
446   xmlnode *ret;
447   size_t real_size;
448
449   g_return_val_if_fail (str != NULL, NULL);
450
451   real_size = size < 0 ? strlen (str) : size;
452   xpd = GNUNET_malloc (sizeof (XMLNodePool));
453   memset (xpd, 0, sizeof (XMLNodePool));
454   if (xmlSAXUserParseMemory (&xmlnode_parser_libxml, xpd, str, real_size) < 0)
455     {
456       freePool (xpd);
457       return NULL;
458     }
459   ret = xpd->current;
460   ret->free_pool = GNUNET_YES;
461   return ret;
462 }
463
464 xmlnode *
465 xmlnode_get_next_twin (xmlnode * node)
466 {
467   xmlnode *sibling;
468   const char *ns = xmlnode_get_namespace (node);
469
470   g_return_val_if_fail (node != NULL, NULL);
471   g_return_val_if_fail (node->type == XMLNODE_TYPE_TAG, NULL);
472
473   for (sibling = node->next; sibling; sibling = sibling->next)
474     {
475       const char *xmlns = NULL;
476       if (ns)
477         xmlns = xmlnode_get_namespace (sibling);
478
479       if (sibling->type == XMLNODE_TYPE_TAG
480           && !strcmp (node->name, sibling->name) && (!ns
481                                                      || (xmlns
482                                                          && !strcmp (ns,
483                                                                      xmlns))))
484         return sibling;
485     }
486   return NULL;
487 }