Mercurial > pidgin.yaz
comparison libpurple/protocols/jabber/jabber.c @ 25772:c4eb9f10ecb5
propagate from branch 'im.pidgin.pidgin' (head 11ea1799b1892059650fa7dcf6e848a020045e50)
to branch 'org.darkrain42.pidgin.xmpp' (head 5b18884cb593c048171bfce406c09d70ddf03f02)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sat, 29 Nov 2008 18:41:39 +0000 |
parents | 5cd0188892f6 e1f363f8fd6b |
children | 514051f3b6cf |
comparison
equal
deleted
inserted
replaced
24508:5cd0188892f6 | 25772:c4eb9f10ecb5 |
---|---|
37 #include "server.h" | 37 #include "server.h" |
38 #include "util.h" | 38 #include "util.h" |
39 #include "version.h" | 39 #include "version.h" |
40 #include "xmlnode.h" | 40 #include "xmlnode.h" |
41 | 41 |
42 #include "caps.h" | |
42 #include "auth.h" | 43 #include "auth.h" |
43 #include "buddy.h" | 44 #include "buddy.h" |
44 #include "chat.h" | 45 #include "chat.h" |
45 #include "data.h" | 46 #include "data.h" |
46 #include "disco.h" | 47 #include "disco.h" |
60 | 61 |
61 | 62 |
62 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) | 63 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) |
63 | 64 |
64 static PurplePlugin *my_protocol = NULL; | 65 static PurplePlugin *my_protocol = NULL; |
66 | |
65 GList *jabber_features = NULL; | 67 GList *jabber_features = NULL; |
68 GList *jabber_identities = NULL; | |
66 | 69 |
67 static void jabber_unregister_account_cb(JabberStream *js); | 70 static void jabber_unregister_account_cb(JabberStream *js); |
68 static void try_srv_connect(JabberStream *js); | 71 static void try_srv_connect(JabberStream *js); |
69 | 72 |
70 static void jabber_stream_init(JabberStream *js) | 73 static void jabber_stream_init(JabberStream *js) |
167 hostname[sizeof(hostname) - 1] = '\0'; | 170 hostname[sizeof(hostname) - 1] = '\0'; |
168 | 171 |
169 return purple_strreplace(input, "__HOSTNAME__", hostname); | 172 return purple_strreplace(input, "__HOSTNAME__", hostname); |
170 } | 173 } |
171 | 174 |
172 static void jabber_stream_features_parse(JabberStream *js, xmlnode *packet) | 175 void jabber_stream_features_parse(JabberStream *js, xmlnode *packet) |
173 { | 176 { |
174 if(xmlnode_get_child(packet, "starttls")) { | 177 if(xmlnode_get_child(packet, "starttls")) { |
175 if(jabber_process_starttls(js, packet)) | 178 if(jabber_process_starttls(js, packet)) |
176 | 179 |
177 return; | 180 return; |
383 } | 386 } |
384 return; | 387 return; |
385 } | 388 } |
386 #endif | 389 #endif |
387 | 390 |
388 do_jabber_send_raw(js, data, len); | 391 if (len == -1) |
392 len = strlen(data); | |
393 | |
394 if (js->use_bosh) { | |
395 xmlnode *xnode = xmlnode_from_str(data, len); | |
396 if (xnode) jabber_bosh_connection_send(&(js->bosh), xnode); | |
397 else { | |
398 purple_connection_error_reason(js->gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, | |
399 _("Someone tried to send non-XML in a Jabber world.")); | |
400 } | |
401 } else { | |
402 do_jabber_send_raw(js, data, len); | |
403 } | |
389 } | 404 } |
390 | 405 |
391 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len) | 406 int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len) |
392 { | 407 { |
393 JabberStream *js = (JabberStream*)gc->proto_data; | 408 JabberStream *js = (JabberStream*)gc->proto_data; |
546 | 561 |
547 /* Tell the app that we're doing encryption */ | 562 /* Tell the app that we're doing encryption */ |
548 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); | 563 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
549 } | 564 } |
550 | 565 |
566 static void | |
567 jabber_bosh_login_callback(PurpleBOSHConnection *conn) | |
568 { | |
569 purple_debug_info("jabber","YAY...BOSH connection established.\n"); | |
570 } | |
571 | |
572 static void | |
573 txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) | |
574 { | |
575 PurpleConnection *gc = data; | |
576 JabberStream *js = gc->proto_data; | |
577 int n; | |
578 | |
579 if (results == 0) { | |
580 gchar *tmp; | |
581 tmp = g_strdup_printf(_("Could not find alternative XMPP connection methods after failing to connect directly.\n")); | |
582 purple_connection_error_reason (gc, | |
583 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); | |
584 g_free(tmp); | |
585 return; | |
586 } | |
587 | |
588 for (n = 0; n < results; n++) { | |
589 gchar **token; | |
590 token = g_strsplit(resp[n].content, "=", 2); | |
591 if (!strcmp(token[0], "_xmpp-client-xbosh")) { | |
592 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); | |
593 jabber_bosh_connection_init(&(js->bosh), gc->account, js, token[1]); | |
594 g_strfreev(token); | |
595 break; | |
596 } | |
597 g_strfreev(token); | |
598 } | |
599 if (js->bosh.host) { | |
600 js->bosh.userdata = gc; | |
601 jabber_bosh_connection_connect(&(js->bosh)); | |
602 } else { | |
603 purple_debug_info("jabber","Didn't find an alternative connection method.\n"); | |
604 } | |
605 } | |
551 | 606 |
552 static void | 607 static void |
553 jabber_login_callback(gpointer data, gint source, const gchar *error) | 608 jabber_login_callback(gpointer data, gint source, const gchar *error) |
554 { | 609 { |
555 PurpleConnection *gc = data; | 610 PurpleConnection *gc = data; |
558 if (source < 0) { | 613 if (source < 0) { |
559 if (js->srv_rec != NULL) { | 614 if (js->srv_rec != NULL) { |
560 purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error); | 615 purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error); |
561 try_srv_connect(js); | 616 try_srv_connect(js); |
562 } else { | 617 } else { |
563 gchar *tmp; | 618 purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); |
564 tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), | 619 purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc); |
565 error); | |
566 purple_connection_error_reason(gc, | |
567 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); | |
568 g_free(tmp); | |
569 } | 620 } |
570 return; | 621 return; |
571 } | 622 } |
572 | 623 |
573 g_free(js->srv_rec); | 624 g_free(js->srv_rec); |
729 } | 780 } |
730 | 781 |
731 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll | 782 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
732 * invoke the magic of SRV lookups, to figure out host and port */ | 783 * invoke the magic of SRV lookups, to figure out host and port */ |
733 if(!js->gsc) { | 784 if(!js->gsc) { |
734 if(connect_server[0]) { | 785 if(connect_server[0]) { |
735 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); | 786 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); |
736 } else { | 787 } else { |
737 js->srv_query_data = purple_srv_resolve("xmpp-client", | 788 js->srv_query_data = purple_srv_resolve("xmpp-client", |
738 "tcp", js->user->domain, srv_resolved_cb, js); | 789 "tcp", js->user->domain, srv_resolved_cb, js); |
739 } | 790 } |
1339 | 1390 |
1340 g_free(js->stream_id); | 1391 g_free(js->stream_id); |
1341 if(js->user) | 1392 if(js->user) |
1342 jabber_id_free(js->user); | 1393 jabber_id_free(js->user); |
1343 g_free(js->avatar_hash); | 1394 g_free(js->avatar_hash); |
1395 g_free(js->caps_hash); | |
1344 | 1396 |
1345 purple_circ_buffer_destroy(js->write_buffer); | 1397 purple_circ_buffer_destroy(js->write_buffer); |
1346 if(js->writeh) | 1398 if(js->writeh) |
1347 purple_input_remove(js->writeh); | 1399 purple_input_remove(js->writeh); |
1348 #ifdef HAVE_CYRUS_SASL | 1400 #ifdef HAVE_CYRUS_SASL |
1440 JabberStream *js = gc->proto_data; | 1492 JabberStream *js = gc->proto_data; |
1441 | 1493 |
1442 js->idle = idle ? time(NULL) - idle : idle; | 1494 js->idle = idle ? time(NULL) - idle : idle; |
1443 } | 1495 } |
1444 | 1496 |
1445 void jabber_add_feature(const char *shortname, const char *namespace, JabberFeatureEnabled cb) { | 1497 void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb) { |
1446 JabberFeature *feat; | 1498 JabberFeature *feat; |
1447 | 1499 |
1448 g_return_if_fail(shortname != NULL); | |
1449 g_return_if_fail(namespace != NULL); | 1500 g_return_if_fail(namespace != NULL); |
1450 | 1501 |
1451 feat = g_new0(JabberFeature,1); | 1502 feat = g_new0(JabberFeature,1); |
1452 feat->shortname = g_strdup(shortname); | |
1453 feat->namespace = g_strdup(namespace); | 1503 feat->namespace = g_strdup(namespace); |
1454 feat->is_enabled = cb; | 1504 feat->is_enabled = cb; |
1455 | 1505 |
1456 /* try to remove just in case it already exists in the list */ | 1506 /* try to remove just in case it already exists in the list */ |
1457 jabber_remove_feature(shortname); | 1507 jabber_remove_feature(namespace); |
1458 | 1508 |
1459 jabber_features = g_list_append(jabber_features, feat); | 1509 jabber_features = g_list_append(jabber_features, feat); |
1460 } | 1510 } |
1461 | 1511 |
1462 void jabber_remove_feature(const char *shortname) { | 1512 void jabber_remove_feature(const char *namespace) { |
1463 GList *feature; | 1513 GList *feature; |
1464 for(feature = jabber_features; feature; feature = feature->next) { | 1514 for(feature = jabber_features; feature; feature = feature->next) { |
1465 JabberFeature *feat = (JabberFeature*)feature->data; | 1515 JabberFeature *feat = (JabberFeature*)feature->data; |
1466 if(!strcmp(feat->shortname, shortname)) { | 1516 if(!strcmp(feat->namespace, namespace)) { |
1467 g_free(feat->shortname); | |
1468 g_free(feat->namespace); | 1517 g_free(feat->namespace); |
1469 | |
1470 g_free(feature->data); | 1518 g_free(feature->data); |
1471 jabber_features = g_list_delete_link(jabber_features, feature); | 1519 jabber_features = g_list_delete_link(jabber_features, feature); |
1472 break; | 1520 break; |
1473 } | 1521 } |
1522 } | |
1523 } | |
1524 | |
1525 static void jabber_features_destroy(void) | |
1526 { | |
1527 while (jabber_features) { | |
1528 JabberFeature *feature = jabber_features->data; | |
1529 g_free(feature->namespace); | |
1530 g_free(feature); | |
1531 jabber_features = g_list_remove_link(jabber_features, jabber_features); | |
1532 } | |
1533 } | |
1534 | |
1535 void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name) { | |
1536 GList *identity; | |
1537 JabberIdentity *ident; | |
1538 /* both required according to XEP-0030 */ | |
1539 g_return_if_fail(category != NULL); | |
1540 g_return_if_fail(type != NULL); | |
1541 | |
1542 for(identity = jabber_identities; identity; identity = identity->next) { | |
1543 JabberIdentity *ident = (JabberIdentity*)identity->data; | |
1544 if (!strcmp(ident->category, category) && | |
1545 !strcmp(ident->type, type) && | |
1546 ((!ident->lang && !lang) || (ident->lang && lang && !strcmp(ident->lang, lang)))) { | |
1547 return; | |
1548 } | |
1549 } | |
1550 | |
1551 ident = g_new0(JabberIdentity, 1); | |
1552 ident->category = g_strdup(category); | |
1553 ident->type = g_strdup(type); | |
1554 ident->lang = g_strdup(lang); | |
1555 ident->name = g_strdup(name); | |
1556 jabber_identities = g_list_append(jabber_identities, ident); | |
1557 } | |
1558 | |
1559 static void jabber_identities_destroy(void) | |
1560 { | |
1561 while (jabber_identities) { | |
1562 JabberIdentity *id = jabber_identities->data; | |
1563 g_free(id->category); | |
1564 g_free(id->type); | |
1565 g_free(id->lang); | |
1566 g_free(id->name); | |
1567 g_free(id); | |
1568 jabber_identities = g_list_remove_link(jabber_identities, jabber_identities); | |
1474 } | 1569 } |
1475 } | 1570 } |
1476 | 1571 |
1477 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) | 1572 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) |
1478 { | 1573 { |
2297 | 2392 |
2298 static gboolean _jabber_send_buzz(JabberStream *js, const char *username, char **error) { | 2393 static gboolean _jabber_send_buzz(JabberStream *js, const char *username, char **error) { |
2299 | 2394 |
2300 JabberBuddy *jb; | 2395 JabberBuddy *jb; |
2301 JabberBuddyResource *jbr; | 2396 JabberBuddyResource *jbr; |
2302 GList *iter; | |
2303 | 2397 |
2304 if(!username) | 2398 if(!username) |
2305 return FALSE; | 2399 return FALSE; |
2306 | 2400 |
2307 jb = jabber_buddy_find(js, username, FALSE); | 2401 jb = jabber_buddy_find(js, username, FALSE); |
2314 if(!jbr) { | 2408 if(!jbr) { |
2315 *error = g_strdup_printf(_("Unable to buzz, because user %s might be offline."), username); | 2409 *error = g_strdup_printf(_("Unable to buzz, because user %s might be offline."), username); |
2316 return FALSE; | 2410 return FALSE; |
2317 } | 2411 } |
2318 | 2412 |
2413 /* Is this message sufficiently useful to not just fold it in with the tail error condition below? */ | |
2319 if(!jbr->caps) { | 2414 if(!jbr->caps) { |
2320 *error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), username); | 2415 *error = g_strdup_printf(_("Unable to buzz, because there is nothing known about user %s."), username); |
2321 return FALSE; | 2416 return FALSE; |
2322 } | 2417 } |
2323 | 2418 |
2324 for(iter = jbr->caps->features; iter; iter = g_list_next(iter)) { | 2419 if (jabber_resource_has_capability(jbr, "http://www.xmpp.org/extensions/xep-0224.html#ns")) { |
2325 if(!strcmp(iter->data, "http://www.xmpp.org/extensions/xep-0224.html#ns")) { | 2420 xmlnode *buzz, *msg = xmlnode_new("message"); |
2326 xmlnode *buzz, *msg = xmlnode_new("message"); | 2421 gchar *to; |
2327 gchar *to; | 2422 |
2328 | 2423 to = g_strdup_printf("%s/%s", username, jbr->name); |
2329 to = g_strdup_printf("%s/%s", username, jbr->name); | 2424 xmlnode_set_attrib(msg, "to", to); |
2330 xmlnode_set_attrib(msg, "to", to); | 2425 g_free(to); |
2331 g_free(to); | 2426 |
2332 | 2427 /* avoid offline storage */ |
2333 /* avoid offline storage */ | 2428 xmlnode_set_attrib(msg, "type", "headline"); |
2334 xmlnode_set_attrib(msg, "type", "headline"); | 2429 |
2335 | 2430 buzz = xmlnode_new_child(msg, "attention"); |
2336 buzz = xmlnode_new_child(msg, "attention"); | 2431 xmlnode_set_namespace(buzz, "http://www.xmpp.org/extensions/xep-0224.html#ns"); |
2337 xmlnode_set_namespace(buzz, "http://www.xmpp.org/extensions/xep-0224.html#ns"); | 2432 |
2338 | 2433 jabber_send(js, msg); |
2339 jabber_send(js, msg); | 2434 xmlnode_free(msg); |
2340 xmlnode_free(msg); | 2435 |
2341 | 2436 return TRUE; |
2342 return TRUE; | |
2343 } | |
2344 } | 2437 } |
2345 | 2438 |
2346 *error = g_strdup_printf(_("Unable to buzz, because the user %s does not support it."), username); | 2439 *error = g_strdup_printf(_("Unable to buzz, because the user %s does not support it."), username); |
2347 return FALSE; | 2440 return FALSE; |
2348 } | 2441 } |
2472 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, | 2565 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, |
2473 "prpl-jabber", jabber_cmd_buzz, | 2566 "prpl-jabber", jabber_cmd_buzz, |
2474 _("buzz: Buzz a user to get their attention"), NULL); | 2567 _("buzz: Buzz a user to get their attention"), NULL); |
2475 } | 2568 } |
2476 | 2569 |
2570 /* IPC functions */ | |
2571 | |
2572 /** | |
2573 * IPC function for determining if a contact supports a certain feature. | |
2574 * | |
2575 * @param account The PurpleAccount | |
2576 * @param jid The full JID of the contact. | |
2577 * @param feature The feature's namespace. | |
2578 * | |
2579 * @return TRUE if supports feature; else FALSE. | |
2580 */ | |
2581 static gboolean | |
2582 jabber_ipc_contact_has_feature(PurpleAccount *account, const gchar *jid, | |
2583 const gchar *feature) | |
2584 { | |
2585 PurpleConnection *gc = purple_account_get_connection(account); | |
2586 JabberStream *js; | |
2587 JabberBuddy *jb; | |
2588 JabberBuddyResource *jbr; | |
2589 gchar *resource; | |
2590 | |
2591 if (!purple_account_is_connected(account)) | |
2592 return FALSE; | |
2593 js = gc->proto_data; | |
2594 | |
2595 if (!(resource = jabber_get_resource(jid)) || | |
2596 !(jb = jabber_buddy_find(js, jid, FALSE)) || | |
2597 !(jbr = jabber_buddy_find_resource(jb, resource))) { | |
2598 g_free(resource); | |
2599 return FALSE; | |
2600 } | |
2601 | |
2602 g_free(resource); | |
2603 | |
2604 return jabber_resource_has_capability(jbr, feature); | |
2605 } | |
2606 | |
2607 static void | |
2608 jabber_ipc_add_feature(const gchar *feature) | |
2609 { | |
2610 if (!feature) | |
2611 return; | |
2612 jabber_add_feature(feature, 0); | |
2613 | |
2614 /* send presence with new caps info for all connected accounts */ | |
2615 jabber_caps_broadcast_change(); | |
2616 } | |
2617 | |
2477 void | 2618 void |
2478 jabber_init_plugin(PurplePlugin *plugin) | 2619 jabber_init_plugin(PurplePlugin *plugin) |
2479 { | 2620 { |
2480 my_protocol = plugin; | 2621 my_protocol = plugin; |
2481 } | 2622 |
2623 jabber_add_identity("client", "pc", NULL, PACKAGE); | |
2624 | |
2625 /* initialize jabber_features list */ | |
2626 jabber_add_feature("jabber:iq:last", 0); | |
2627 jabber_add_feature("jabber:iq:oob", 0); | |
2628 jabber_add_feature("jabber:iq:time", 0); | |
2629 jabber_add_feature("xmpp:urn:time", 0); | |
2630 jabber_add_feature("jabber:iq:version", 0); | |
2631 jabber_add_feature("jabber:x:conference", 0); | |
2632 jabber_add_feature("http://jabber.org/protocol/bytestreams", 0); | |
2633 jabber_add_feature("http://jabber.org/protocol/disco#info", 0); | |
2634 jabber_add_feature("http://jabber.org/protocol/disco#items", 0); | |
2635 #if 0 | |
2636 jabber_add_feature("http://jabber.org/protocol/ibb", 0); | |
2637 #endif | |
2638 jabber_add_feature("http://jabber.org/protocol/muc", 0); | |
2639 jabber_add_feature("http://jabber.org/protocol/muc#user", 0); | |
2640 jabber_add_feature("http://jabber.org/protocol/si", 0); | |
2641 jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0); | |
2642 jabber_add_feature("http://jabber.org/protocol/xhtml-im", 0); | |
2643 jabber_add_feature("urn:xmpp:ping", 0); | |
2644 | |
2645 /* IPC functions */ | |
2646 purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), | |
2647 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER, | |
2648 purple_value_new(PURPLE_TYPE_BOOLEAN), 3, | |
2649 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), | |
2650 purple_value_new(PURPLE_TYPE_STRING), | |
2651 purple_value_new(PURPLE_TYPE_STRING)); | |
2652 purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature), | |
2653 purple_marshal_VOID__POINTER, | |
2654 NULL, 1, | |
2655 purple_value_new(PURPLE_TYPE_STRING)); | |
2656 } | |
2657 | |
2658 void | |
2659 jabber_uninit_plugin(void) | |
2660 { | |
2661 jabber_features_destroy(); | |
2662 jabber_identities_destroy(); | |
2663 } |