libpurple/protocols/jabber/iq.c

branch
cpw.darkrain42.xmpp.iq-handlers
changeset 26088
65218dd1d42e
parent 26042
4dabdb5fe213
parent 25831
1ff20e8f93b5
child 26687
1e799151fabe
equal deleted inserted replaced
26071:b4bbfacd7f20 26088:65218dd1d42e
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")) {
263 uname(&osinfo); 238 uname(&osinfo);
264 os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release, 239 os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
265 osinfo.machine); 240 osinfo.machine);
266 } 241 }
267 #endif 242 #endif
268 from = xmlnode_get_attrib(packet, "from");
269 id = xmlnode_get_attrib(packet, "id");
270 243
271 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version"); 244 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version");
272 xmlnode_set_attrib(iq->node, "to", from); 245 if (from)
246 xmlnode_set_attrib(iq->node, "to", from);
273 jabber_iq_set_id(iq, id); 247 jabber_iq_set_id(iq, id);
274 248
275 query = xmlnode_get_child(iq->node, "query"); 249 query = xmlnode_get_child(iq->node, "query");
276 250
277 ui_info = purple_core_get_ui_info(); 251 ui_info = purple_core_get_ui_info();
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

mercurial