| 58 #include "adhoccommands.h" |
59 #include "adhoccommands.h" |
| 59 |
60 |
| 60 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) |
61 #define JABBER_CONNECT_STEPS (js->gsc ? 9 : 5) |
| 61 |
62 |
| 62 static PurplePlugin *my_protocol = NULL; |
63 static PurplePlugin *my_protocol = NULL; |
| |
64 |
| 63 GList *jabber_features = NULL; |
65 GList *jabber_features = NULL; |
| |
66 GList *jabber_identities = NULL; |
| |
67 |
| |
68 GHashTable *jabber_contact_info = NULL; |
| 64 |
69 |
| 65 static void jabber_unregister_account_cb(JabberStream *js); |
70 static void jabber_unregister_account_cb(JabberStream *js); |
| 66 static void try_srv_connect(JabberStream *js); |
71 static void try_srv_connect(JabberStream *js); |
| 67 |
72 |
| 68 static void jabber_stream_init(JabberStream *js) |
73 static void jabber_stream_init(JabberStream *js) |
| 511 |
516 |
| 512 /* Tell the app that we're doing encryption */ |
517 /* Tell the app that we're doing encryption */ |
| 513 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
518 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
| 514 } |
519 } |
| 515 |
520 |
| |
521 static void |
| |
522 txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) |
| |
523 { |
| |
524 PurpleConnection *gc = data; |
| |
525 JabberStream *js = gc->proto_data; |
| |
526 int n; |
| |
527 |
| |
528 if (results == 0) { |
| |
529 gchar *tmp; |
| |
530 tmp = g_strdup_printf(_("Could not find alternative XMPP connection methods after failing to connect directly.\n")); |
| |
531 purple_connection_error_reason (gc, |
| |
532 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
| |
533 g_free(tmp); |
| |
534 } |
| |
535 |
| |
536 for (n = 0; n < results; n++) { |
| |
537 gchar **token; |
| |
538 token = g_strsplit(resp[n].content, "=", 2); |
| |
539 if (!strcmp(token[0], "_xmpp-client-xbosh")) { |
| |
540 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); |
| |
541 js->bosh.url = g_strdup(token[1]); |
| |
542 g_strfreev(token); |
| |
543 break; |
| |
544 } |
| |
545 g_strfreev(token); |
| |
546 } |
| |
547 } |
| 516 |
548 |
| 517 static void |
549 static void |
| 518 jabber_login_callback(gpointer data, gint source, const gchar *error) |
550 jabber_login_callback(gpointer data, gint source, const gchar *error) |
| 519 { |
551 { |
| 520 PurpleConnection *gc = data; |
552 PurpleConnection *gc = data; |
| 523 if (source < 0) { |
555 if (source < 0) { |
| 524 if (js->srv_rec != NULL) { |
556 if (js->srv_rec != NULL) { |
| 525 purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error); |
557 purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record.\n", error); |
| 526 try_srv_connect(js); |
558 try_srv_connect(js); |
| 527 } else { |
559 } else { |
| 528 gchar *tmp; |
560 purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); |
| 529 tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), |
561 purple_txt_resolve("_xmppconnect", js->user->domain, txt_resolved_cb, gc); |
| 530 error); |
|
| 531 purple_connection_error_reason(gc, |
|
| 532 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
|
| 533 g_free(tmp); |
|
| 534 } |
562 } |
| 535 return; |
563 return; |
| 536 } |
564 } |
| 537 |
565 |
| 538 g_free(js->srv_rec); |
566 g_free(js->srv_rec); |
| 706 } |
734 } |
| 707 |
735 |
| 708 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
736 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
| 709 * invoke the magic of SRV lookups, to figure out host and port */ |
737 * invoke the magic of SRV lookups, to figure out host and port */ |
| 710 if(!js->gsc) { |
738 if(!js->gsc) { |
| 711 if(connect_server[0]) { |
739 if(connect_server[0]) { |
| 712 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); |
740 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); |
| 713 } else { |
741 } else { |
| 714 js->srv_query_data = purple_srv_resolve("xmpp-client", |
742 js->srv_query_data = purple_srv_resolve("xmpp-client", |
| 715 "tcp", js->user->domain, srv_resolved_cb, js); |
743 "tcp", js->user->domain, srv_resolved_cb, js); |
| 716 } |
744 } |
| 1425 JabberStream *js = gc->proto_data; |
1453 JabberStream *js = gc->proto_data; |
| 1426 |
1454 |
| 1427 js->idle = idle ? time(NULL) - idle : idle; |
1455 js->idle = idle ? time(NULL) - idle : idle; |
| 1428 } |
1456 } |
| 1429 |
1457 |
| 1430 void jabber_add_feature(const char *shortname, const char *namespace, JabberFeatureEnabled cb) { |
1458 void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb) { |
| 1431 JabberFeature *feat; |
1459 JabberFeature *feat; |
| 1432 |
1460 |
| 1433 g_return_if_fail(shortname != NULL); |
|
| 1434 g_return_if_fail(namespace != NULL); |
1461 g_return_if_fail(namespace != NULL); |
| 1435 |
1462 |
| 1436 feat = g_new0(JabberFeature,1); |
1463 feat = g_new0(JabberFeature,1); |
| 1437 feat->shortname = g_strdup(shortname); |
|
| 1438 feat->namespace = g_strdup(namespace); |
1464 feat->namespace = g_strdup(namespace); |
| 1439 feat->is_enabled = cb; |
1465 feat->is_enabled = cb; |
| 1440 |
1466 |
| 1441 /* try to remove just in case it already exists in the list */ |
1467 /* try to remove just in case it already exists in the list */ |
| 1442 jabber_remove_feature(shortname); |
1468 jabber_remove_feature(namespace); |
| 1443 |
1469 |
| 1444 jabber_features = g_list_append(jabber_features, feat); |
1470 jabber_features = g_list_append(jabber_features, feat); |
| 1445 } |
1471 } |
| 1446 |
1472 |
| 1447 void jabber_remove_feature(const char *shortname) { |
1473 void jabber_remove_feature(const char *namespace) { |
| 1448 GList *feature; |
1474 GList *feature; |
| 1449 for(feature = jabber_features; feature; feature = feature->next) { |
1475 for(feature = jabber_features; feature; feature = feature->next) { |
| 1450 JabberFeature *feat = (JabberFeature*)feature->data; |
1476 JabberFeature *feat = (JabberFeature*)feature->data; |
| 1451 if(!strcmp(feat->shortname, shortname)) { |
1477 if(!strcmp(feat->namespace, namespace)) { |
| 1452 g_free(feat->shortname); |
|
| 1453 g_free(feat->namespace); |
1478 g_free(feat->namespace); |
| 1454 |
|
| 1455 g_free(feature->data); |
1479 g_free(feature->data); |
| 1456 jabber_features = g_list_delete_link(jabber_features, feature); |
1480 jabber_features = g_list_delete_link(jabber_features, feature); |
| 1457 break; |
1481 break; |
| 1458 } |
1482 } |
| 1459 } |
1483 } |
| |
1484 } |
| |
1485 |
| |
1486 void jabber_add_identity(const gchar *category, const gchar *type, const gchar *name) { |
| |
1487 GList *identity; |
| |
1488 JabberIdentity *ident; |
| |
1489 /* both required according to XEP-0030 */ |
| |
1490 g_return_if_fail(category != NULL); |
| |
1491 g_return_if_fail(type != NULL); |
| |
1492 |
| |
1493 for(identity = jabber_identities; identity; identity = identity->next) { |
| |
1494 JabberIdentity *ident = (JabberIdentity*)identity->data; |
| |
1495 if(!strcmp(ident->category, category)) { |
| |
1496 if (!strcmp(ident->type, type)) return; |
| |
1497 } |
| |
1498 } |
| |
1499 |
| |
1500 ident = g_new0(JabberIdentity, 1); |
| |
1501 ident->category = g_strdup(category); |
| |
1502 ident->type = g_strdup(type); |
| |
1503 ident->name = g_strdup(name); |
| |
1504 jabber_identities = g_list_append(jabber_identities, ident); |
| 1460 } |
1505 } |
| 1461 |
1506 |
| 1462 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) |
1507 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) |
| 1463 { |
1508 { |
| 1464 return "jabber"; |
1509 return "jabber"; |
| 2457 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, |
2502 PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, |
| 2458 "prpl-jabber", jabber_cmd_buzz, |
2503 "prpl-jabber", jabber_cmd_buzz, |
| 2459 _("buzz: Buzz a user to get their attention"), NULL); |
2504 _("buzz: Buzz a user to get their attention"), NULL); |
| 2460 } |
2505 } |
| 2461 |
2506 |
| |
2507 /* IPC fucntions*/ |
| |
2508 |
| |
2509 /* |
| |
2510 * IPC function for checking wheather a client at a full JID supports a certain feature. |
| |
2511 * |
| |
2512 * @param fulljid The full JID of the client. |
| |
2513 * @param featrure The feature's namespace. |
| |
2514 * |
| |
2515 * @return TRUE if supports feature; else FALSE. |
| |
2516 */ |
| |
2517 static gboolean |
| |
2518 jabber_ipc_contact_has_feature(gchar *fulljid, gchar *feature) |
| |
2519 { |
| |
2520 JabberCapsKey *caps_info = NULL; |
| |
2521 JabberCapsValueExt *capabilities = NULL; |
| |
2522 |
| |
2523 caps_info = g_hash_table_lookup(jabber_contact_info, fulljid); |
| |
2524 |
| |
2525 if (!caps_info) return FALSE; |
| |
2526 capabilities = g_hash_table_lookup(capstable, caps_info); |
| |
2527 |
| |
2528 if (g_list_find_custom(capabilities->features, feature, strcmp) == NULL) return FALSE ; |
| |
2529 return TRUE; |
| |
2530 } |
| |
2531 |
| |
2532 static void |
| |
2533 jabber_ipc_add_feature(gchar *feature) |
| |
2534 { |
| |
2535 if (feature == 0) return; |
| |
2536 jabber_add_feature(feature, 0); |
| |
2537 |
| |
2538 // send presence with new caps info for all connected accounts |
| |
2539 jabber_caps_broadcast_change(); |
| |
2540 } |
| |
2541 |
| 2462 void |
2542 void |
| 2463 jabber_init_plugin(PurplePlugin *plugin) |
2543 jabber_init_plugin(PurplePlugin *plugin) |
| 2464 { |
2544 { |
| 2465 my_protocol = plugin; |
2545 my_protocol = plugin; |
| 2466 } |
2546 |
| |
2547 jabber_add_identity("client", "pc", PACKAGE); |
| |
2548 |
| |
2549 /* initialize jabber_features list */ |
| |
2550 jabber_add_feature("jabber:iq:last", 0); |
| |
2551 jabber_add_feature("jabber:iq:oob", 0); |
| |
2552 jabber_add_feature("jabber:iq:time", 0); |
| |
2553 jabber_add_feature("xmpp:urn:time", 0); |
| |
2554 jabber_add_feature("jabber:iq:version", 0); |
| |
2555 jabber_add_feature("jabber:x:conference", 0); |
| |
2556 jabber_add_feature("http://jabber.org/protocol/bytestreams", 0); |
| |
2557 jabber_add_feature("http://jabber.org/protocol/disco#info", 0); |
| |
2558 jabber_add_feature("http://jabber.org/protocol/disco#items", 0); |
| |
2559 #if 0 |
| |
2560 jabber_add_feature("http://jabber.org/protocol/ibb", 0); |
| |
2561 #endif |
| |
2562 jabber_add_feature("http://jabber.org/protocol/muc", 0); |
| |
2563 jabber_add_feature("http://jabber.org/protocol/muc#user", 0); |
| |
2564 jabber_add_feature("http://jabber.org/protocol/si", 0); |
| |
2565 jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0); |
| |
2566 jabber_add_feature("http://jabber.org/protocol/xhtml-im", 0); |
| |
2567 jabber_add_feature("urn:xmpp:ping", 0); |
| |
2568 |
| |
2569 jabber_contact_info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, jabber_caps_destroy_key); |
| |
2570 |
| |
2571 /* IPC functions */ |
| |
2572 purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), |
| |
2573 purple_marshal_BOOLEAN__POINTER_POINTER, |
| |
2574 purple_value_new(PURPLE_TYPE_BOOLEAN), 2, |
| |
2575 purple_value_new(PURPLE_TYPE_STRING), |
| |
2576 purple_value_new(PURPLE_TYPE_STRING)); |
| |
2577 purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature), |
| |
2578 purple_marshal_VOID__POINTER, |
| |
2579 NULL, 1, |
| |
2580 purple_value_new(PURPLE_TYPE_STRING)); |
| |
2581 } |