Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / lib / e2k-xml-utils.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /* Copyright (C) 2001-2004 Novell, Inc.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU Lesser General Public
7  * License as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "e2k-xml-utils.h"
25 #include <stdlib.h>
26 #include <libxml/HTMLparser.h>
27 #include <libxml/parserInternals.h>
28 #include <libxml/xmlmemory.h>
29
30 static void
31 my_xml_parser_error_handler (void *ctx, const char *msg, ...)
32 {
33         ;
34 }
35
36 /**
37  * e2k_parse_xml:
38  * @buf: the data to parse
39  * @len: the length of the buffer, or -1 if it is '\0'-terminated
40  *
41  * Parses the XML document in @buf.
42  *
43  * Return value: a pointer to an #xmlDoc
44  **/
45 xmlDoc *
46 e2k_parse_xml (const char *buf, int len)
47 {
48         static xmlSAXHandler *sax;
49         xmlParserCtxtPtr ctxt;
50         xmlDoc *doc;
51
52         g_return_val_if_fail (buf != NULL, NULL);
53
54         if (!sax) {
55                 xmlInitParser();
56                 sax = xmlMalloc (sizeof (xmlSAXHandler));
57 #if LIBXML_VERSION > 20600
58                 xmlSAXVersion (sax, 2);
59 #else
60                 memcpy (sax, &xmlDefaultSAXHandler, sizeof (xmlSAXHandler));
61 #endif
62                 sax->warning = my_xml_parser_error_handler;
63                 sax->error = my_xml_parser_error_handler;
64         }
65
66         if (len == -1)
67                 len = strlen (buf);
68         ctxt = xmlCreateMemoryParserCtxt (buf, len);
69         if (!ctxt)
70                 return NULL;
71
72         xmlFree (ctxt->sax);
73         ctxt->sax = sax;
74 #if LIBXML_VERSION > 20600
75         ctxt->sax2 = 1;
76         ctxt->str_xml = xmlDictLookup (ctxt->dict, BAD_CAST "xml", 3);
77         ctxt->str_xmlns = xmlDictLookup (ctxt->dict, BAD_CAST "xmlns", 5);
78         ctxt->str_xml_ns = xmlDictLookup (ctxt->dict, XML_XML_NAMESPACE, 36);
79 #endif
80
81         /* We set recover to TRUE because Exchange will let you
82          * put control-characters into data, which will make the
83          * XML be not well-formed.
84          */
85         ctxt->recovery = TRUE;
86         ctxt->vctxt.error = my_xml_parser_error_handler;
87         ctxt->vctxt.warning = my_xml_parser_error_handler;
88
89         xmlParseDocument (ctxt);
90
91         doc = ctxt->myDoc;
92         ctxt->sax = NULL;
93         xmlFreeParserCtxt (ctxt);
94
95         return doc;
96 }
97
98 /**
99  * e2k_parse_html:
100  * @buf: the data to parse
101  * @len: the length of the buffer, or -1 if it is '\0'-terminated
102  *
103  * Parses the HTML document in @buf.
104  *
105  * Return value: a pointer to an #xmlDoc
106  **/
107 xmlDoc *
108 e2k_parse_html (const char *buf, int len)
109 {
110         xmlDoc *doc;
111 #if LIBXML_VERSION > 20600
112         static xmlSAXHandler *sax;
113         htmlParserCtxtPtr ctxt;
114
115         g_return_val_if_fail (buf != NULL, NULL);
116
117         if (!sax) {
118                 xmlInitParser();
119                 sax = xmlMalloc (sizeof (htmlSAXHandler));
120                 memcpy (sax, &htmlDefaultSAXHandler, sizeof (xmlSAXHandlerV1));
121                 sax->warning = my_xml_parser_error_handler;
122                 sax->error = my_xml_parser_error_handler;
123         }
124
125         if (len == -1)
126                 len = strlen (buf);
127         ctxt = htmlCreateMemoryParserCtxt (buf, len);
128         if (!ctxt)
129                 return NULL;
130
131         xmlFree (ctxt->sax);
132         ctxt->sax = sax;
133         ctxt->vctxt.error = my_xml_parser_error_handler;
134         ctxt->vctxt.warning = my_xml_parser_error_handler;
135
136         htmlParseDocument (ctxt);
137         doc = ctxt->myDoc;
138
139         ctxt->sax = NULL;
140         htmlFreeParserCtxt (ctxt);
141
142 #else /* LIBXML_VERSION <= 20600 */
143         char *buf_copy = g_strndup (buf, len);
144
145         doc = htmlParseDoc (buf_copy, NULL);
146         g_free (buf_copy);
147 #endif
148
149         return doc;
150 }
151
152 /**
153  * e2k_g_string_append_xml_escaped:
154  * @string: a %GString containing XML data
155  * @value: data to append to @string
156  *
157  * Appends @value to @string, escaping any characters that can't appear
158  * unencoded in XML text (eg, "<").
159  **/
160 void
161 e2k_g_string_append_xml_escaped (GString *string, const char *value)
162 {
163         while (*value) {
164                 switch (*value) {
165                 case '<':
166                         g_string_append (string, "&lt;");
167                         break;
168                 case '>':
169                         g_string_append (string, "&gt;");
170                         break;
171                 case '&':
172                         g_string_append (string, "&amp;");
173                         break;
174                 case '"':
175                         g_string_append (string, "&quot;");
176                         break;
177
178                 default:
179                         g_string_append_c (string, *value);
180                         break;
181                 }
182                 value++;
183         }
184 }
185
186 /**
187  * e2k_xml_find:
188  * @node: a node of an xml document
189  * @name: the name of the element to find
190  *
191  * Starts or continues a pre-order depth-first search of an xml
192  * document for an element named @name. @node is used as the starting
193  * point of the search, but is not examined itself.
194  *
195  * To search the complete document, pass the root node of the document
196  * as @node on the first call, and then on each successive call,
197  * pass the previous match as @node.
198  *
199  * Return value: the first matching element after @node, or %NULL when
200  * there are no more matches.
201  **/
202 xmlNode *
203 e2k_xml_find (xmlNode *node, const char *name)
204 {
205         return e2k_xml_find_in (node, NULL, name);
206 }
207
208 /**
209  * e2k_xml_find_in:
210  * @node: a node of an xml document
211  * @top: top of the search space
212  * @name: the name of the element to find
213  *
214  * Starts or continues a pre-order depth-first search of a subset of
215  * an xml document for an element named @name. @node is used as the
216  * starting point of the search, but is not examined itself. @top is
217  * the upper-most node that will be examined.
218  *
219  * To search the complete tree under a given node, pass that node as
220  * both @node and @top on the first call, and then on each successive
221  * call, pass the previous match as @node (with the original node
222  * still as @top).
223  *
224  * Return value: the first matching element after @node, or %NULL when
225  * there are no more matches.
226  **/
227 xmlNode *
228 e2k_xml_find_in (xmlNode *node, xmlNode *top, const char *name)
229 {
230         g_return_val_if_fail (name != NULL, NULL);
231
232         while (node) {
233                 /* If the current node has children, then the first
234                  * child is the next node to examine. If it doesn't
235                  * have children but does have a younger sibling, then
236                  * that sibling is next up. Otherwise, climb back up
237                  * the tree until we find a node that does have a
238                  * younger sibling.
239                  */
240                 if (node->children)
241                         node = node->children;
242                 else {
243                         while (node && !node->next && node != top)
244                                 node = node->parent;
245                         if (!node || node == top)
246                                 return NULL;
247                         node = node->next;
248                 }
249
250                 if (node->name && !strcmp (node->name, name))
251                         return node;
252         }
253
254         return NULL;
255 }