| 141 g_free(iq->id); |
141 g_free(iq->id); |
| 142 xmlnode_free(iq->node); |
142 xmlnode_free(iq->node); |
| 143 g_free(iq); |
143 g_free(iq); |
| 144 } |
144 } |
| 145 |
145 |
| 146 static void jabber_iq_last_parse(JabberStream *js, xmlnode *packet) |
146 static void jabber_iq_last_parse(JabberStream *js, const char *from, |
| |
147 JabberIqType type, const char *id, |
| |
148 xmlnode *packet) |
| 147 { |
149 { |
| 148 JabberIq *iq; |
150 JabberIq *iq; |
| 149 const char *type; |
|
| 150 const char *from; |
|
| 151 const char *id; |
|
| 152 xmlnode *query; |
151 xmlnode *query; |
| 153 char *idle_time; |
152 char *idle_time; |
| 154 |
153 |
| 155 type = xmlnode_get_attrib(packet, "type"); |
154 if(type == JABBER_IQ_GET) { |
| 156 from = xmlnode_get_attrib(packet, "from"); |
|
| 157 id = xmlnode_get_attrib(packet, "id"); |
|
| 158 |
|
| 159 if(type && !strcmp(type, "get")) { |
|
| 160 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:last"); |
155 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:last"); |
| 161 jabber_iq_set_id(iq, id); |
156 jabber_iq_set_id(iq, id); |
| 162 xmlnode_set_attrib(iq->node, "to", from); |
157 if (from) |
| |
158 xmlnode_set_attrib(iq->node, "to", from); |
| 163 |
159 |
| 164 query = xmlnode_get_child(iq->node, "query"); |
160 query = xmlnode_get_child(iq->node, "query"); |
| 165 |
161 |
| 166 idle_time = g_strdup_printf("%ld", js->idle ? time(NULL) - js->idle : 0); |
162 idle_time = g_strdup_printf("%ld", js->idle ? time(NULL) - js->idle : 0); |
| 167 xmlnode_set_attrib(query, "seconds", idle_time); |
163 xmlnode_set_attrib(query, "seconds", idle_time); |
| 169 |
165 |
| 170 jabber_iq_send(iq); |
166 jabber_iq_send(iq); |
| 171 } |
167 } |
| 172 } |
168 } |
| 173 |
169 |
| 174 static void jabber_iq_time_parse(JabberStream *js, xmlnode *packet) |
170 static void jabber_iq_time_parse(JabberStream *js, const char *from, |
| 175 { |
171 JabberIqType type, const char *id, |
| 176 const char *type, *from, *id, *xmlns; |
172 xmlnode *child) |
| |
173 { |
| |
174 const char *xmlns; |
| |
175 JabberIq *iq; |
| |
176 time_t now_t; |
| |
177 struct tm now_local; |
| |
178 struct tm now_utc; |
| |
179 struct tm *now; |
| |
180 |
| |
181 time(&now_t); |
| |
182 now = localtime(&now_t); |
| |
183 memcpy(&now_local, now, sizeof(struct tm)); |
| |
184 now = gmtime(&now_t); |
| |
185 memcpy(&now_utc, now, sizeof(struct tm)); |
| |
186 |
| |
187 xmlns = xmlnode_get_namespace(child); |
| |
188 |
| |
189 if(type == JABBER_IQ_GET) { |
| |
190 xmlnode *utc; |
| |
191 const char *date, *tz, *display; |
| |
192 |
| |
193 iq = jabber_iq_new(js, JABBER_IQ_RESULT); |
| |
194 jabber_iq_set_id(iq, id); |
| |
195 if (from) |
| |
196 xmlnode_set_attrib(iq->node, "to", from); |
| |
197 |
| |
198 child = xmlnode_new_child(iq->node, child->name); |
| |
199 xmlnode_set_namespace(child, xmlns); |
| |
200 utc = xmlnode_new_child(child, "utc"); |
| |
201 |
| |
202 if(!strcmp("urn:xmpp:time", xmlns)) { |
| |
203 tz = purple_get_tzoff_str(&now_local, TRUE); |
| |
204 xmlnode_insert_data(xmlnode_new_child(child, "tzo"), tz, -1); |
| |
205 |
| |
206 date = purple_utf8_strftime("%FT%TZ", &now_utc); |
| |
207 xmlnode_insert_data(utc, date, -1); |
| |
208 } else { /* jabber:iq:time */ |
| |
209 tz = purple_utf8_strftime("%Z", &now_local); |
| |
210 xmlnode_insert_data(xmlnode_new_child(child, "tz"), tz, -1); |
| |
211 |
| |
212 date = purple_utf8_strftime("%Y%m%dT%T", &now_utc); |
| |
213 xmlnode_insert_data(utc, date, -1); |
| |
214 |
| |
215 display = purple_utf8_strftime("%d %b %Y %T", &now_local); |
| |
216 xmlnode_insert_data(xmlnode_new_child(child, "display"), display, -1); |
| |
217 } |
| |
218 |
| |
219 jabber_iq_send(iq); |
| |
220 } |
| |
221 } |
| |
222 |
| |
223 static void jabber_iq_version_parse(JabberStream *js, const char *from, |
| |
224 JabberIqType type, const char *id, |
| |
225 xmlnode *packet) |
| |
226 { |
| 177 JabberIq *iq; |
227 JabberIq *iq; |
| 178 xmlnode *query; |
228 xmlnode *query; |
| 179 time_t now_t; |
229 |
| 180 struct tm *now; |
230 if(type == JABBER_IQ_GET) { |
| 181 |
|
| 182 time(&now_t); |
|
| 183 now = localtime(&now_t); |
|
| 184 |
|
| 185 type = xmlnode_get_attrib(packet, "type"); |
|
| 186 from = xmlnode_get_attrib(packet, "from"); |
|
| 187 id = xmlnode_get_attrib(packet, "id"); |
|
| 188 |
|
| 189 /* we're gonna throw this away in a moment, but we need it |
|
| 190 * to get the xmlns, so we can figure out if this is |
|
| 191 * jabber:iq:time or urn:xmpp:time */ |
|
| 192 query = xmlnode_get_child(packet, "query"); |
|
| 193 xmlns = xmlnode_get_namespace(query); |
|
| 194 |
|
| 195 if(type && !strcmp(type, "get")) { |
|
| 196 xmlnode *utc; |
|
| 197 const char *date; |
|
| 198 |
|
| 199 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, xmlns); |
|
| 200 jabber_iq_set_id(iq, id); |
|
| 201 xmlnode_set_attrib(iq->node, "to", from); |
|
| 202 |
|
| 203 query = xmlnode_get_child(iq->node, "query"); |
|
| 204 |
|
| 205 date = purple_utf8_strftime("%Y%m%dT%T", now); |
|
| 206 utc = xmlnode_new_child(query, "utc"); |
|
| 207 xmlnode_insert_data(utc, date, -1); |
|
| 208 |
|
| 209 if(!strcmp("urn:xmpp:time", xmlns)) { |
|
| 210 xmlnode_insert_data(utc, "Z", 1); /* of COURSE the thing that is the same is different */ |
|
| 211 |
|
| 212 date = purple_get_tzoff_str(now, TRUE); |
|
| 213 xmlnode_insert_data(xmlnode_new_child(query, "tzo"), date, -1); |
|
| 214 } else { /* jabber:iq:time */ |
|
| 215 date = purple_utf8_strftime("%Z", now); |
|
| 216 xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1); |
|
| 217 |
|
| 218 date = purple_utf8_strftime("%d %b %Y %T", now); |
|
| 219 xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1); |
|
| 220 } |
|
| 221 |
|
| 222 jabber_iq_send(iq); |
|
| 223 } |
|
| 224 } |
|
| 225 |
|
| 226 static void urn_xmpp_ping_parse(JabberStream *js, xmlnode *packet) |
|
| 227 { |
|
| 228 const char *type, *id, *from; |
|
| 229 JabberIq *iq; |
|
| 230 |
|
| 231 type = xmlnode_get_attrib(packet, "type"); |
|
| 232 from = xmlnode_get_attrib(packet, "from"); |
|
| 233 id = xmlnode_get_attrib(packet, "id"); |
|
| 234 |
|
| 235 if(type && !strcmp(type, "get")) { |
|
| 236 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "urn:xmpp:ping"); |
|
| 237 |
|
| 238 jabber_iq_set_id(iq, id); |
|
| 239 xmlnode_set_attrib(iq->node, "to", from); |
|
| 240 |
|
| 241 jabber_iq_send(iq); |
|
| 242 } else { |
|
| 243 /* XXX: error */ |
|
| 244 } |
|
| 245 } |
|
| 246 |
|
| 247 static void jabber_iq_version_parse(JabberStream *js, xmlnode *packet) |
|
| 248 { |
|
| 249 JabberIq *iq; |
|
| 250 const char *type, *from, *id; |
|
| 251 xmlnode *query; |
|
| 252 |
|
| 253 type = xmlnode_get_attrib(packet, "type"); |
|
| 254 |
|
| 255 if(type && !strcmp(type, "get")) { |
|
| 256 GHashTable *ui_info; |
231 GHashTable *ui_info; |
| 257 const char *ui_name = NULL, *ui_version = NULL; |
232 const char *ui_name = NULL, *ui_version = NULL; |
| 258 #if 0 |
233 #if 0 |
| 259 char *os = NULL; |
234 char *os = NULL; |
| 260 if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) { |
235 if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) { |
| 308 } |
282 } |
| 309 |
283 |
| 310 void jabber_iq_parse(JabberStream *js, xmlnode *packet) |
284 void jabber_iq_parse(JabberStream *js, xmlnode *packet) |
| 311 { |
285 { |
| 312 JabberCallbackData *jcd; |
286 JabberCallbackData *jcd; |
| 313 xmlnode *query, *error, *x; |
287 xmlnode *child, *error, *x; |
| 314 const char *xmlns; |
288 const char *xmlns; |
| 315 const char *type, *id, *from; |
289 const char *iq_type, *id, *from; |
| 316 JabberIqHandler *jih; |
290 JabberIqType type = JABBER_IQ_NONE; |
| 317 |
291 |
| 318 query = xmlnode_get_child(packet, "query"); |
292 /* |
| 319 type = xmlnode_get_attrib(packet, "type"); |
293 * child will be either the first tag child or NULL if there is no child. |
| |
294 * Historically, we used just the 'query' subchild, but newer XEPs use |
| |
295 * differently named children. Grabbing the first child is (for the time |
| |
296 * being) sufficient. |
| |
297 */ |
| |
298 for (child = packet->child; child; child = child->next) { |
| |
299 if (child->type == XMLNODE_TYPE_TAG) |
| |
300 break; |
| |
301 } |
| |
302 |
| |
303 iq_type = xmlnode_get_attrib(packet, "type"); |
| 320 from = xmlnode_get_attrib(packet, "from"); |
304 from = xmlnode_get_attrib(packet, "from"); |
| 321 id = xmlnode_get_attrib(packet, "id"); |
305 id = xmlnode_get_attrib(packet, "id"); |
| 322 |
306 |
| 323 if(type == NULL || !(!strcmp(type, "get") || !strcmp(type, "set") |
307 if (iq_type) { |
| 324 || !strcmp(type, "result") || !strcmp(type, "error"))) { |
308 if (!strcmp(iq_type, "get")) |
| |
309 type = JABBER_IQ_GET; |
| |
310 else if (!strcmp(iq_type, "set")) |
| |
311 type = JABBER_IQ_SET; |
| |
312 else if (!strcmp(iq_type, "result")) |
| |
313 type = JABBER_IQ_RESULT; |
| |
314 else if (!strcmp(iq_type, "error")) |
| |
315 type = JABBER_IQ_ERROR; |
| |
316 } |
| |
317 |
| |
318 if (type == JABBER_IQ_NONE) { |
| 325 purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n", |
319 purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n", |
| 326 type ? type : "(null)"); |
320 iq_type ? iq_type : "(null)"); |
| 327 return; |
321 return; |
| 328 } |
322 } |
| 329 |
323 |
| 330 /* All IQs must have an ID, so send an error for a set/get that doesn't */ |
324 /* All IQs must have an ID, so send an error for a set/get that doesn't */ |
| 331 if(!id || !*id) { |
325 if(!id || !*id) { |
| 332 |
326 |
| 333 if(!strcmp(type, "set") || !strcmp(type, "get")) { |
327 if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { |
| 334 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); |
328 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); |
| 335 |
329 |
| 336 xmlnode_free(iq->node); |
330 xmlnode_free(iq->node); |
| 337 iq->node = xmlnode_copy(packet); |
331 iq->node = xmlnode_copy(packet); |
| 338 xmlnode_set_attrib(iq->node, "to", from); |
332 if (from) { |
| 339 xmlnode_remove_attrib(iq->node, "from"); |
333 xmlnode_set_attrib(iq->node, "to", from); |
| |
334 xmlnode_remove_attrib(iq->node, "from"); |
| |
335 } |
| |
336 |
| 340 xmlnode_set_attrib(iq->node, "type", "error"); |
337 xmlnode_set_attrib(iq->node, "type", "error"); |
| 341 /* This id is clearly not useful, but we must put something there for a valid stanza */ |
338 /* This id is clearly not useful, but we must put something there for a valid stanza */ |
| 342 iq->id = jabber_get_next_id(js); |
339 iq->id = jabber_get_next_id(js); |
| 343 xmlnode_set_attrib(iq->node, "id", iq->id); |
340 xmlnode_set_attrib(iq->node, "id", iq->id); |
| 344 error = xmlnode_new_child(iq->node, "error"); |
341 error = xmlnode_new_child(iq->node, "error"); |
| 346 x = xmlnode_new_child(error, "bad-request"); |
343 x = xmlnode_new_child(error, "bad-request"); |
| 347 xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas"); |
344 xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas"); |
| 348 |
345 |
| 349 jabber_iq_send(iq); |
346 jabber_iq_send(iq); |
| 350 } else |
347 } else |
| 351 purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", type); |
348 purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", |
| |
349 iq_type); |
| 352 |
350 |
| 353 return; |
351 return; |
| 354 } |
352 } |
| 355 |
353 |
| 356 /* First, lets see if a special callback got registered */ |
354 /* First, lets see if a special callback got registered */ |
| 357 |
355 if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) { |
| 358 if(!strcmp(type, "result") || !strcmp(type, "error")) { |
356 if((jcd = g_hash_table_lookup(js->iq_callbacks, id))) { |
| 359 if(id && *id && (jcd = g_hash_table_lookup(js->iq_callbacks, id))) { |
|
| 360 jcd->callback(js, packet, jcd->data); |
357 jcd->callback(js, packet, jcd->data); |
| 361 jabber_iq_remove_callback_by_id(js, id); |
358 jabber_iq_remove_callback_by_id(js, id); |
| 362 return; |
359 return; |
| 363 } |
360 } |
| 364 } |
361 } |
| 365 |
362 |
| 366 /* Apparently not, so lets see if we have a pre-defined handler */ |
363 /* Apparently not, so lets see if we have a pre-defined handler */ |
| 367 |
364 if(child && (xmlns = xmlnode_get_namespace(child))) { |
| 368 if(query && (xmlns = xmlnode_get_namespace(query))) { |
365 char *key = g_strdup_printf("%s %s", child->name, xmlns); |
| 369 if((jih = g_hash_table_lookup(iq_handlers, xmlns))) { |
366 JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key); |
| 370 jih(js, packet); |
367 g_free(key); |
| |
368 |
| |
369 if(jih) { |
| |
370 jih(js, from, type, id, child); |
| 371 return; |
371 return; |
| 372 } |
372 } |
| 373 } |
373 } |
| 374 |
374 |
| 375 if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) { |
|
| 376 jabber_si_parse(js, packet); |
|
| 377 return; |
|
| 378 } |
|
| 379 |
|
| 380 if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) { |
|
| 381 jabber_gmail_poke(js, packet); |
|
| 382 return; |
|
| 383 } |
|
| 384 |
|
| 385 purple_debug_info("jabber", "jabber_iq_parse\n"); |
375 purple_debug_info("jabber", "jabber_iq_parse\n"); |
| 386 |
376 |
| 387 if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) { |
|
| 388 jabber_ping_parse(js, packet); |
|
| 389 return; |
|
| 390 } |
|
| 391 |
|
| 392 if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) { |
|
| 393 jabber_data_parse(js, packet); |
|
| 394 return; |
|
| 395 } |
|
| 396 |
|
| 397 if (xmlnode_get_child_with_namespace(packet, "data", XEP_0047_NAMESPACE) |
|
| 398 || xmlnode_get_child_with_namespace(packet, "close", XEP_0047_NAMESPACE) |
|
| 399 || xmlnode_get_child_with_namespace(packet, "open", XEP_0047_NAMESPACE)) { |
|
| 400 jabber_ibb_parse(js, packet); |
|
| 401 return; |
|
| 402 } |
|
| 403 |
|
| 404 /* If we get here, send the default error reply mandated by XMPP-CORE */ |
377 /* If we get here, send the default error reply mandated by XMPP-CORE */ |
| 405 if(!strcmp(type, "set") || !strcmp(type, "get")) { |
378 if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) { |
| 406 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); |
379 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR); |
| 407 |
380 |
| 408 xmlnode_free(iq->node); |
381 xmlnode_free(iq->node); |
| 409 iq->node = xmlnode_copy(packet); |
382 iq->node = xmlnode_copy(packet); |
| 410 xmlnode_set_attrib(iq->node, "to", from); |
383 if (from) { |
| 411 xmlnode_remove_attrib(iq->node, "from"); |
384 xmlnode_set_attrib(iq->node, "to", from); |
| |
385 xmlnode_remove_attrib(iq->node, "from"); |
| |
386 } |
| |
387 |
| 412 xmlnode_set_attrib(iq->node, "type", "error"); |
388 xmlnode_set_attrib(iq->node, "type", "error"); |
| 413 error = xmlnode_new_child(iq->node, "error"); |
389 error = xmlnode_new_child(iq->node, "error"); |
| 414 xmlnode_set_attrib(error, "type", "cancel"); |
390 xmlnode_set_attrib(error, "type", "cancel"); |
| 415 xmlnode_set_attrib(error, "code", "501"); |
391 xmlnode_set_attrib(error, "code", "501"); |
| 416 x = xmlnode_new_child(error, "feature-not-implemented"); |
392 x = xmlnode_new_child(error, "feature-not-implemented"); |
| 418 |
394 |
| 419 jabber_iq_send(iq); |
395 jabber_iq_send(iq); |
| 420 } |
396 } |
| 421 } |
397 } |
| 422 |
398 |
| 423 void jabber_iq_register_handler(const char *xmlns, JabberIqHandler *handlerfunc) |
399 void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc) |
| 424 { |
400 { |
| 425 g_hash_table_replace(iq_handlers, g_strdup(xmlns), handlerfunc); |
401 /* |
| |
402 * This is valid because nodes nor namespaces cannot have spaces in them |
| |
403 * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and |
| |
404 * http://www.w3.org/TR/REC-xml-names/) |
| |
405 */ |
| |
406 char *key = g_strdup_printf("%s %s", node, xmlns); |
| |
407 g_hash_table_replace(iq_handlers, key, handlerfunc); |
| 426 } |
408 } |
| 427 |
409 |
| 428 void jabber_iq_init(void) |
410 void jabber_iq_init(void) |
| 429 { |
411 { |
| 430 iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
412 iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); |
| 431 |
413 |
| 432 jabber_iq_register_handler("jabber:iq:roster", jabber_roster_parse); |
414 jabber_iq_register_handler("mailbox", "google:mail:notify", jabber_gmail_poke); |
| 433 jabber_iq_register_handler("jabber:iq:oob", jabber_oob_parse); |
415 jabber_iq_register_handler("new-mail", "google:mail:notify", jabber_gmail_poke); |
| 434 jabber_iq_register_handler("http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse); |
416 jabber_iq_register_handler("query", "http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse); |
| 435 jabber_iq_register_handler("jabber:iq:last", jabber_iq_last_parse); |
417 jabber_iq_register_handler("query", "http://jabber.org/protocol/disco#info", jabber_disco_info_parse); |
| 436 jabber_iq_register_handler("jabber:iq:time", jabber_iq_time_parse); |
418 jabber_iq_register_handler("query", "http://jabber.org/protocol/disco#items", jabber_disco_items_parse); |
| 437 jabber_iq_register_handler("urn:xmpp:time", jabber_iq_time_parse); |
419 jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse); |
| 438 jabber_iq_register_handler("jabber:iq:version", jabber_iq_version_parse); |
420 jabber_iq_register_handler("query", "jabber:iq:last", jabber_iq_last_parse); |
| 439 jabber_iq_register_handler("http://jabber.org/protocol/disco#info", jabber_disco_info_parse); |
421 jabber_iq_register_handler("query", "jabber:iq:oob", jabber_oob_parse); |
| 440 jabber_iq_register_handler("http://jabber.org/protocol/disco#items", jabber_disco_items_parse); |
422 jabber_iq_register_handler("query", "jabber:iq:register", jabber_register_parse); |
| 441 jabber_iq_register_handler("jabber:iq:register", jabber_register_parse); |
423 jabber_iq_register_handler("query", "jabber:iq:roster", jabber_roster_parse); |
| 442 jabber_iq_register_handler("urn:xmpp:ping", urn_xmpp_ping_parse); |
424 jabber_iq_register_handler("query", "jabber:iq:time", jabber_iq_time_parse); |
| |
425 jabber_iq_register_handler("query", "jabber:iq:version", jabber_iq_version_parse); |
| |
426 jabber_iq_register_handler("data", XEP_0231_NAMESPACE, jabber_data_parse); |
| |
427 jabber_iq_register_handler("ping", "urn:xmpp:ping", jabber_ping_parse); |
| |
428 jabber_iq_register_handler("time", "urn:xmpp:time", jabber_iq_time_parse); |
| 443 } |
429 } |
| 444 |
430 |
| 445 void jabber_iq_uninit(void) |
431 void jabber_iq_uninit(void) |
| 446 { |
432 { |
| 447 g_hash_table_destroy(iq_handlers); |
433 g_hash_table_destroy(iq_handlers); |
| 448 iq_handlers = NULL; |
434 iq_handlers = NULL; |
| 449 } |
435 } |
| 450 |
|