| |
1 /* |
| |
2 * purple - Jabber XML parser stuff |
| |
3 * |
| |
4 * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> |
| |
5 * |
| |
6 * This program is free software; you can redistribute it and/or modify |
| |
7 * it under the terms of the GNU General Public License as published by |
| |
8 * the Free Software Foundation; either version 2 of the License, or |
| |
9 * (at your option) any later version. |
| |
10 * |
| |
11 * This program is distributed in the hope that it will be useful, |
| |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
14 * GNU General Public License for more details. |
| |
15 * |
| |
16 * You should have received a copy of the GNU General Public License |
| |
17 * along with this program; if not, write to the Free Software |
| |
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
19 * |
| |
20 */ |
| |
21 #include "internal.h" |
| |
22 |
| |
23 #include <libxml/parser.h> |
| |
24 |
| |
25 #include "connection.h" |
| |
26 #include "debug.h" |
| |
27 #include "jabber.h" |
| |
28 #include "parser.h" |
| |
29 #include "util.h" |
| |
30 #include "xmlnode.h" |
| |
31 |
| |
32 static void |
| |
33 jabber_parser_element_start_libxml(void *user_data, |
| |
34 const xmlChar *element_name, const xmlChar *prefix, const xmlChar *namespace, |
| |
35 int nb_namespaces, const xmlChar **namespaces, |
| |
36 int nb_attributes, int nb_defaulted, const xmlChar **attributes) |
| |
37 { |
| |
38 JabberStream *js = user_data; |
| |
39 xmlnode *node; |
| |
40 int i; |
| |
41 |
| |
42 if(!element_name) { |
| |
43 return; |
| |
44 } else if(!xmlStrcmp(element_name, (xmlChar*) "stream")) { |
| |
45 js->protocol_version = JABBER_PROTO_0_9; |
| |
46 for(i=0; i < nb_attributes * 5; i += 5) { |
| |
47 int attrib_len = attributes[i+4] - attributes[i+3]; |
| |
48 char *attrib = g_malloc(attrib_len + 1); |
| |
49 memcpy(attrib, attributes[i+3], attrib_len); |
| |
50 attrib[attrib_len] = '\0'; |
| |
51 |
| |
52 if(!xmlStrcmp(attributes[i], (xmlChar*) "version") |
| |
53 && !strcmp(attrib, "1.0")) { |
| |
54 js->protocol_version = JABBER_PROTO_1_0; |
| |
55 g_free(attrib); |
| |
56 } else if(!xmlStrcmp(attributes[i], (xmlChar*) "id")) { |
| |
57 g_free(js->stream_id); |
| |
58 js->stream_id = attrib; |
| |
59 } else { |
| |
60 g_free(attrib); |
| |
61 } |
| |
62 } |
| |
63 if(js->protocol_version == JABBER_PROTO_0_9) |
| |
64 js->auth_type = JABBER_AUTH_IQ_AUTH; |
| |
65 |
| |
66 if(js->state == JABBER_STREAM_INITIALIZING) |
| |
67 jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING); |
| |
68 } else { |
| |
69 |
| |
70 if(js->current) |
| |
71 node = xmlnode_new_child(js->current, (const char*) element_name); |
| |
72 else |
| |
73 node = xmlnode_new((const char*) element_name); |
| |
74 xmlnode_set_namespace(node, (const char*) namespace); |
| |
75 |
| |
76 for(i=0; i < nb_attributes * 5; i+=5) { |
| |
77 char *txt; |
| |
78 int attrib_len = attributes[i+4] - attributes[i+3]; |
| |
79 char *attrib = g_malloc(attrib_len + 1); |
| |
80 char *attrib_ns = NULL; |
| |
81 |
| |
82 if (attributes[i+2]) { |
| |
83 attrib_ns = g_strdup((char*)attributes[i+2]);; |
| |
84 } |
| |
85 |
| |
86 memcpy(attrib, attributes[i+3], attrib_len); |
| |
87 attrib[attrib_len] = '\0'; |
| |
88 |
| |
89 txt = attrib; |
| |
90 attrib = purple_unescape_html(txt); |
| |
91 g_free(txt); |
| |
92 xmlnode_set_attrib_with_namespace(node, (const char*) attributes[i], attrib_ns, attrib); |
| |
93 g_free(attrib); |
| |
94 g_free(attrib_ns); |
| |
95 } |
| |
96 |
| |
97 js->current = node; |
| |
98 } |
| |
99 } |
| |
100 |
| |
101 static void |
| |
102 jabber_parser_element_end_libxml(void *user_data, const xmlChar *element_name, |
| |
103 const xmlChar *prefix, const xmlChar *namespace) |
| |
104 { |
| |
105 JabberStream *js = user_data; |
| |
106 |
| |
107 if(!js->current) |
| |
108 return; |
| |
109 |
| |
110 if(js->current->parent) { |
| |
111 if(!xmlStrcmp((xmlChar*) js->current->name, element_name)) |
| |
112 js->current = js->current->parent; |
| |
113 } else { |
| |
114 xmlnode *packet = js->current; |
| |
115 js->current = NULL; |
| |
116 jabber_process_packet(js, packet); |
| |
117 xmlnode_free(packet); |
| |
118 } |
| |
119 } |
| |
120 |
| |
121 static void |
| |
122 jabber_parser_element_text_libxml(void *user_data, const xmlChar *text, int text_len) |
| |
123 { |
| |
124 JabberStream *js = user_data; |
| |
125 |
| |
126 if(!js->current) |
| |
127 return; |
| |
128 |
| |
129 if(!text || !text_len) |
| |
130 return; |
| |
131 |
| |
132 xmlnode_insert_data(js->current, (const char*) text, text_len); |
| |
133 } |
| |
134 |
| |
135 static xmlSAXHandler jabber_parser_libxml = { |
| |
136 .internalSubset = NULL, |
| |
137 .isStandalone = NULL, |
| |
138 .hasInternalSubset = NULL, |
| |
139 .hasExternalSubset = NULL, |
| |
140 .resolveEntity = NULL, |
| |
141 .getEntity = NULL, |
| |
142 .entityDecl = NULL, |
| |
143 .notationDecl = NULL, |
| |
144 .attributeDecl = NULL, |
| |
145 .elementDecl = NULL, |
| |
146 .unparsedEntityDecl = NULL, |
| |
147 .setDocumentLocator = NULL, |
| |
148 .startDocument = NULL, |
| |
149 .endDocument = NULL, |
| |
150 .startElement = NULL, |
| |
151 .endElement = NULL, |
| |
152 .reference = NULL, |
| |
153 .characters = jabber_parser_element_text_libxml, |
| |
154 .ignorableWhitespace = NULL, |
| |
155 .processingInstruction = NULL, |
| |
156 .comment = NULL, |
| |
157 .warning = NULL, |
| |
158 .error = NULL, |
| |
159 .fatalError = NULL, |
| |
160 .getParameterEntity = NULL, |
| |
161 .cdataBlock = NULL, |
| |
162 .externalSubset = NULL, |
| |
163 .initialized = XML_SAX2_MAGIC, |
| |
164 ._private = NULL, |
| |
165 .startElementNs = jabber_parser_element_start_libxml, |
| |
166 .endElementNs = jabber_parser_element_end_libxml, |
| |
167 .serror = NULL |
| |
168 }; |
| |
169 |
| |
170 void |
| |
171 jabber_parser_setup(JabberStream *js) |
| |
172 { |
| |
173 /* This seems backwards, but it makes sense. The libxml code creates |
| |
174 * the parser context when you try to use it (this way, it can figure |
| |
175 * out the encoding at creation time. So, setting up the parser is |
| |
176 * just a matter of destroying any current parser. */ |
| |
177 if (js->context) { |
| |
178 xmlParseChunk(js->context, NULL,0,1); |
| |
179 xmlFreeParserCtxt(js->context); |
| |
180 js->context = NULL; |
| |
181 } |
| |
182 } |
| |
183 |
| |
184 |
| |
185 void jabber_parser_process(JabberStream *js, const char *buf, int len) |
| |
186 { |
| |
187 if (js->context == NULL) { |
| |
188 /* libxml inconsistently starts parsing on creating the |
| |
189 * parser, so do a ParseChunk right afterwards to force it. */ |
| |
190 js->context = xmlCreatePushParserCtxt(&jabber_parser_libxml, js, buf, len, NULL); |
| |
191 xmlParseChunk(js->context, "", 0, 0); |
| |
192 } else if (xmlParseChunk(js->context, buf, len, 0) < 0) { |
| |
193 purple_connection_error(js->gc, _("XML Parse error")); |
| |
194 } |
| |
195 } |
| |
196 |