mark PurpleImageClass as private
[pidgin-git.git] / libpurple / protocols / bonjour / parser.c
blobeaef50a76aea0b3c77daecfbb389c65234c96c1c
1 /*
2 * purple - Bonjour Jabber XML parser stuff
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "internal.h"
25 #include <libxml/parser.h>
27 #include "connection.h"
28 #include "debug.h"
29 #include "jabber.h"
30 #include "parser.h"
31 #include "util.h"
32 #include "xmlnode.h"
34 static gboolean
35 parse_from_attrib_and_find_buddy(BonjourJabberConversation *bconv, int nb_attributes, const xmlChar **attributes) {
36 int i;
38 /* If the "from" attribute is specified, attach it to the conversation. */
39 for(i=0; i < nb_attributes * 5; i+=5) {
40 if(!xmlStrcmp(attributes[i], (xmlChar*) "from")) {
41 int len = attributes[i+4] - attributes[i+3];
42 bconv->buddy_name = g_strndup((char *)attributes[i+3], len);
43 bonjour_jabber_conv_match_by_name(bconv);
45 return (bconv->pb != NULL);
49 return FALSE;
52 static void
53 bonjour_parser_element_start_libxml(void *user_data,
54 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace,
55 int nb_namespaces, const xmlChar **namespaces,
56 int nb_attributes, int nb_defaulted, const xmlChar **attributes)
58 BonjourJabberConversation *bconv = user_data;
60 PurpleXmlNode *node;
61 int i;
63 g_return_if_fail(element_name != NULL);
65 if(!xmlStrcmp(element_name, (xmlChar*) "stream")) {
66 if(!bconv->recv_stream_start) {
67 bconv->recv_stream_start = TRUE;
69 if (bconv->pb == NULL)
70 parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes);
72 bonjour_jabber_stream_started(bconv);
74 } else {
76 /* If we haven't yet attached a buddy and this isn't "<stream:features />",
77 * try to get a "from" attribute as a last resort to match our buddy. */
78 if(bconv->pb == NULL
79 && !(prefix && !xmlStrcmp(prefix, (xmlChar*) "stream")
80 && !xmlStrcmp(element_name, (xmlChar*) "features"))
81 && !parse_from_attrib_and_find_buddy(bconv, nb_attributes, attributes))
82 /* We've run out of options for finding who the conversation is from
83 using explicitly specified stuff; see if we can make a good match
84 by using the IP */
85 bonjour_jabber_conv_match_by_ip(bconv);
87 if(bconv->current)
88 node = purple_xmlnode_new_child(bconv->current, (const char*) element_name);
89 else
90 node = purple_xmlnode_new((const char*) element_name);
91 purple_xmlnode_set_namespace(node, (const char*) namespace);
93 for(i=0; i < nb_attributes * 5; i+=5) {
94 const char *name = (const char *)attributes[i];
95 const char *prefix = (const char *)attributes[i+1];
96 const char *attrib_ns = (const char *)attributes[i+2];
97 char *txt;
98 int attrib_len = attributes[i+4] - attributes[i+3];
99 char *attrib = g_malloc(attrib_len + 1);
101 memcpy(attrib, attributes[i+3], attrib_len);
102 attrib[attrib_len] = '\0';
104 txt = attrib;
105 attrib = purple_unescape_text(txt);
106 g_free(txt);
107 purple_xmlnode_set_attrib_full(node, name, attrib_ns, prefix, attrib);
108 g_free(attrib);
111 bconv->current = node;
115 static void
116 bonjour_parser_element_end_libxml(void *user_data, const xmlChar *element_name,
117 const xmlChar *prefix, const xmlChar *namespace)
119 BonjourJabberConversation *bconv = user_data;
121 if(!bconv->current) {
122 /* We don't keep a reference to the start stream PurpleXmlNode,
123 * so we have to check for it here to close the conversation */
124 if(!xmlStrcmp(element_name, (xmlChar*) "stream"))
125 /* Asynchronously close the conversation to prevent bonjour_parser_setup()
126 * being called from within this context */
127 async_bonjour_jabber_close_conversation(bconv);
128 return;
131 if(bconv->current->parent) {
132 if(!xmlStrcmp((xmlChar*) bconv->current->name, element_name))
133 bconv->current = bconv->current->parent;
134 } else {
135 PurpleXmlNode *packet = bconv->current;
136 bconv->current = NULL;
137 bonjour_jabber_process_packet(bconv->pb, packet);
138 purple_xmlnode_free(packet);
142 static void
143 bonjour_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len)
145 BonjourJabberConversation *bconv = user_data;
147 if(!bconv->current)
148 return;
150 if(!text || !text_len)
151 return;
153 purple_xmlnode_insert_data(bconv->current, (const char*) text, text_len);
156 static void
157 bonjour_parser_structured_error_handler(void *user_data, xmlErrorPtr error)
159 BonjourJabberConversation *bconv = user_data;
161 purple_debug_error("jabber", "XML parser error for BonjourJabberConversation %p: "
162 "Domain %i, code %i, level %i: %s",
163 bconv,
164 error->domain, error->code, error->level,
165 (error->message ? error->message : "(null)\n"));
168 static xmlSAXHandler bonjour_parser_libxml = {
169 NULL, /*internalSubset*/
170 NULL, /*isStandalone*/
171 NULL, /*hasInternalSubset*/
172 NULL, /*hasExternalSubset*/
173 NULL, /*resolveEntity*/
174 NULL, /*getEntity*/
175 NULL, /*entityDecl*/
176 NULL, /*notationDecl*/
177 NULL, /*attributeDecl*/
178 NULL, /*elementDecl*/
179 NULL, /*unparsedEntityDecl*/
180 NULL, /*setDocumentLocator*/
181 NULL, /*startDocument*/
182 NULL, /*endDocument*/
183 NULL, /*startElement*/
184 NULL, /*endElement*/
185 NULL, /*reference*/
186 bonjour_parser_element_text_libxml, /*characters*/
187 NULL, /*ignorableWhitespace*/
188 NULL, /*processingInstruction*/
189 NULL, /*comment*/
190 NULL, /*warning*/
191 NULL, /*error*/
192 NULL, /*fatalError*/
193 NULL, /*getParameterEntity*/
194 NULL, /*cdataBlock*/
195 NULL, /*externalSubset*/
196 XML_SAX2_MAGIC, /*initialized*/
197 NULL, /*_private*/
198 bonjour_parser_element_start_libxml, /*startElementNs*/
199 bonjour_parser_element_end_libxml, /*endElementNs*/
200 bonjour_parser_structured_error_handler /*serror*/
203 void
204 bonjour_parser_setup(BonjourJabberConversation *bconv)
207 /* This seems backwards, but it makes sense. The libxml code creates
208 * the parser context when you try to use it (this way, it can figure
209 * out the encoding at creation time. So, setting up the parser is
210 * just a matter of destroying any current parser. */
211 if (bconv->context) {
212 xmlParseChunk(bconv->context, NULL,0,1);
213 xmlFreeParserCtxt(bconv->context);
214 bconv->context = NULL;
219 void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len)
222 if (bconv->context == NULL) {
223 /* libxml inconsistently starts parsing on creating the
224 * parser, so do a ParseChunk right afterwards to force it. */
225 bconv->context = xmlCreatePushParserCtxt(&bonjour_parser_libxml, bconv, buf, len, NULL);
226 xmlParseChunk(bconv->context, "", 0, 0);
227 } else if (xmlParseChunk(bconv->context, buf, len, 0) < 0)
228 /* TODO: What should we do here - I assume we should display an error or something (maybe just print something to the conv?) */
229 purple_debug_error("bonjour", "Error parsing xml.\n");