| 90 jabber_session_initialized_cb(JabberStream *js, const char *from, |
92 jabber_session_initialized_cb(JabberStream *js, const char *from, |
| 91 JabberIqType type, const char *id, |
93 JabberIqType type, const char *id, |
| 92 xmlnode *packet, gpointer data) |
94 xmlnode *packet, gpointer data) |
| 93 { |
95 { |
| 94 if (type == JABBER_IQ_RESULT) { |
96 if (type == JABBER_IQ_RESULT) { |
| 95 jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); |
97 jabber_disco_items_server(js); |
| 96 if(js->unregistration) |
98 if(js->unregistration) |
| 97 jabber_unregister_account_cb(js); |
99 jabber_unregister_account_cb(js); |
| 98 } else { |
100 } else { |
| 99 purple_connection_error_reason (js->gc, |
101 purple_connection_error_reason (js->gc, |
| 100 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
102 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
| 181 dot = '\0'; |
183 dot = '\0'; |
| 182 |
184 |
| 183 return purple_strreplace(input, "__HOSTNAME__", hostname); |
185 return purple_strreplace(input, "__HOSTNAME__", hostname); |
| 184 } |
186 } |
| 185 |
187 |
| 186 static void jabber_stream_features_parse(JabberStream *js, xmlnode *packet) |
188 void jabber_stream_features_parse(JabberStream *js, xmlnode *packet) |
| 187 { |
189 { |
| 188 if(xmlnode_get_child(packet, "starttls")) { |
190 if(xmlnode_get_child(packet, "starttls")) { |
| 189 if(jabber_process_starttls(js, packet)) |
191 if(jabber_process_starttls(js, packet)) |
| 190 |
192 |
| 191 return; |
193 return; |
| 192 } else if(purple_account_get_bool(js->gc->account, "require_tls", FALSE) && !js->gsc) { |
194 } else if(purple_account_get_bool(js->gc->account, "require_tls", FALSE) && !jabber_stream_is_ssl(js)) { |
| 193 purple_connection_error_reason (js->gc, |
195 purple_connection_error_reason (js->gc, |
| 194 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, |
196 PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, |
| 195 _("You require encryption, but it is not available on this server.")); |
197 _("You require encryption, but it is not available on this server.")); |
| 196 return; |
198 return; |
| 197 } |
199 } |
| 573 |
581 |
| 574 /* Tell the app that we're doing encryption */ |
582 /* Tell the app that we're doing encryption */ |
| 575 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
583 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
| 576 } |
584 } |
| 577 |
585 |
| |
586 static void |
| |
587 txt_resolved_cb(PurpleTxtResponse *resp, int results, gpointer data) |
| |
588 { |
| |
589 JabberStream *js = data; |
| |
590 int n; |
| |
591 |
| |
592 js->srv_query_data = NULL; |
| |
593 |
| |
594 if (results == 0) { |
| |
595 gchar *tmp; |
| |
596 tmp = g_strdup_printf(_("Could not find alternative XMPP connection methods after failing to connect directly.\n")); |
| |
597 purple_connection_error_reason (js->gc, |
| |
598 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); |
| |
599 g_free(tmp); |
| |
600 return; |
| |
601 } |
| |
602 |
| |
603 for (n = 0; n < results; n++) { |
| |
604 gchar **token; |
| |
605 token = g_strsplit(resp[n].content, "=", 2); |
| |
606 if (!strcmp(token[0], "_xmpp-client-xbosh")) { |
| |
607 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); |
| |
608 js->bosh = jabber_bosh_connection_init(js, token[1]); |
| |
609 js->use_bosh = TRUE; |
| |
610 g_strfreev(token); |
| |
611 break; |
| |
612 } |
| |
613 g_strfreev(token); |
| |
614 } |
| |
615 if (js->bosh) { |
| |
616 jabber_bosh_connection_connect(js->bosh); |
| |
617 } else { |
| |
618 purple_debug_info("jabber","Didn't find an alternative connection method.\n"); |
| |
619 } |
| |
620 } |
| 578 |
621 |
| 579 static void |
622 static void |
| 580 jabber_login_callback(gpointer data, gint source, const gchar *error) |
623 jabber_login_callback(gpointer data, gint source, const gchar *error) |
| 581 { |
624 { |
| 582 PurpleConnection *gc = data; |
625 PurpleConnection *gc = data; |
| 742 |
782 |
| 743 if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE))) |
783 if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE))) |
| 744 my_jb->subscription |= JABBER_SUB_BOTH; |
784 my_jb->subscription |= JABBER_SUB_BOTH; |
| 745 |
785 |
| 746 jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); |
786 jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); |
| |
787 |
| |
788 /* TODO: Just use purple_url_parse? */ |
| |
789 if (!g_ascii_strncasecmp(connect_server, "http://", 7) || !g_ascii_strncasecmp(connect_server, "https://", 8)) { |
| |
790 js->use_bosh = TRUE; |
| |
791 js->bosh = jabber_bosh_connection_init(js, connect_server); |
| |
792 if (!js->bosh) { |
| |
793 purple_connection_error_reason (js->gc, |
| |
794 PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, |
| |
795 _("Malformed BOSH Connect Server")); |
| |
796 return; |
| |
797 } |
| |
798 jabber_bosh_connection_connect(js->bosh); |
| |
799 return; |
| |
800 } else { |
| |
801 js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain); |
| |
802 } |
| 747 |
803 |
| 748 /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */ |
804 /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */ |
| 749 if(purple_account_get_bool(js->gc->account, "old_ssl", FALSE)) { |
805 if(purple_account_get_bool(js->gc->account, "old_ssl", FALSE)) { |
| 750 if(purple_ssl_is_supported()) { |
806 if(purple_ssl_is_supported()) { |
| 751 js->gsc = purple_ssl_connect(js->gc->account, |
807 js->gsc = purple_ssl_connect(js->gc->account, |
| 752 js->certificate_CN, |
808 js->certificate_CN, |
| 753 purple_account_get_int(account, "port", 5223), jabber_login_callback_ssl, |
809 purple_account_get_int(account, "port", 5223), jabber_login_callback_ssl, |
| 754 jabber_ssl_connect_failure, js->gc); |
810 jabber_ssl_connect_failure, js->gc); |
| |
811 if (!js->gsc) { |
| |
812 purple_connection_error_reason (js->gc, |
| |
813 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, |
| |
814 _("Unable to establish SSL connection")); |
| |
815 } |
| 755 } else { |
816 } else { |
| 756 purple_connection_error_reason (js->gc, |
817 purple_connection_error_reason (js->gc, |
| 757 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, |
818 PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, |
| 758 _("SSL support unavailable")); |
819 _("SSL support unavailable")); |
| 759 } |
820 } |
| |
821 |
| |
822 return; |
| 760 } |
823 } |
| 761 |
824 |
| 762 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
825 /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll |
| 763 * invoke the magic of SRV lookups, to figure out host and port */ |
826 * invoke the magic of SRV lookups, to figure out host and port */ |
| 764 if(!js->gsc) { |
827 if(connect_server[0]) { |
| 765 if(connect_server[0]) { |
828 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); |
| 766 jabber_login_connect(js, js->user->domain, connect_server, purple_account_get_int(account, "port", 5222), TRUE); |
829 } else { |
| 767 } else { |
830 js->srv_query_data = purple_srv_resolve("xmpp-client", |
| 768 js->srv_query_data = purple_srv_resolve("xmpp-client", |
831 "tcp", js->user->domain, srv_resolved_cb, js); |
| 769 "tcp", js->user->domain, srv_resolved_cb, js); |
|
| 770 } |
|
| 771 } |
832 } |
| 772 } |
833 } |
| 773 |
834 |
| 774 |
835 |
| 775 static gboolean |
836 static gboolean |
| 1609 xmlnode_set_attrib(item, "jid", who); |
1677 xmlnode_set_attrib(item, "jid", who); |
| 1610 |
1678 |
| 1611 jabber_iq_send(iq); |
1679 jabber_iq_send(iq); |
| 1612 } |
1680 } |
| 1613 |
1681 |
| 1614 void jabber_add_feature(const char *shortname, const char *namespace, JabberFeatureEnabled cb) { |
1682 void jabber_add_feature(const char *namespace, JabberFeatureEnabled cb) { |
| 1615 JabberFeature *feat; |
1683 JabberFeature *feat; |
| 1616 |
1684 |
| 1617 g_return_if_fail(shortname != NULL); |
|
| 1618 g_return_if_fail(namespace != NULL); |
1685 g_return_if_fail(namespace != NULL); |
| 1619 |
1686 |
| 1620 feat = g_new0(JabberFeature,1); |
1687 feat = g_new0(JabberFeature,1); |
| 1621 feat->shortname = g_strdup(shortname); |
|
| 1622 feat->namespace = g_strdup(namespace); |
1688 feat->namespace = g_strdup(namespace); |
| 1623 feat->is_enabled = cb; |
1689 feat->is_enabled = cb; |
| 1624 |
1690 |
| 1625 /* try to remove just in case it already exists in the list */ |
1691 /* try to remove just in case it already exists in the list */ |
| 1626 jabber_remove_feature(shortname); |
1692 jabber_remove_feature(namespace); |
| 1627 |
1693 |
| 1628 jabber_features = g_list_append(jabber_features, feat); |
1694 jabber_features = g_list_append(jabber_features, feat); |
| 1629 } |
1695 } |
| 1630 |
1696 |
| 1631 void jabber_remove_feature(const char *shortname) { |
1697 void jabber_remove_feature(const char *namespace) { |
| 1632 GList *feature; |
1698 GList *feature; |
| 1633 for(feature = jabber_features; feature; feature = feature->next) { |
1699 for(feature = jabber_features; feature; feature = feature->next) { |
| 1634 JabberFeature *feat = (JabberFeature*)feature->data; |
1700 JabberFeature *feat = (JabberFeature*)feature->data; |
| 1635 if(!strcmp(feat->shortname, shortname)) { |
1701 if(!strcmp(feat->namespace, namespace)) { |
| 1636 g_free(feat->shortname); |
|
| 1637 g_free(feat->namespace); |
1702 g_free(feat->namespace); |
| 1638 |
|
| 1639 g_free(feature->data); |
1703 g_free(feature->data); |
| 1640 jabber_features = g_list_delete_link(jabber_features, feature); |
1704 jabber_features = g_list_delete_link(jabber_features, feature); |
| 1641 break; |
1705 break; |
| 1642 } |
1706 } |
| 1643 } |
1707 } |
| |
1708 } |
| |
1709 |
| |
1710 static void jabber_features_destroy(void) |
| |
1711 { |
| |
1712 while (jabber_features) { |
| |
1713 JabberFeature *feature = jabber_features->data; |
| |
1714 g_free(feature->namespace); |
| |
1715 g_free(feature); |
| |
1716 jabber_features = g_list_remove_link(jabber_features, jabber_features); |
| |
1717 } |
| |
1718 } |
| |
1719 |
| |
1720 void jabber_add_identity(const gchar *category, const gchar *type, const gchar *lang, const gchar *name) { |
| |
1721 GList *identity; |
| |
1722 JabberIdentity *ident; |
| |
1723 /* both required according to XEP-0030 */ |
| |
1724 g_return_if_fail(category != NULL); |
| |
1725 g_return_if_fail(type != NULL); |
| |
1726 |
| |
1727 for(identity = jabber_identities; identity; identity = identity->next) { |
| |
1728 JabberIdentity *ident = (JabberIdentity*)identity->data; |
| |
1729 if (!strcmp(ident->category, category) && |
| |
1730 !strcmp(ident->type, type) && |
| |
1731 ((!ident->lang && !lang) || (ident->lang && lang && !strcmp(ident->lang, lang)))) { |
| |
1732 return; |
| |
1733 } |
| |
1734 } |
| |
1735 |
| |
1736 ident = g_new0(JabberIdentity, 1); |
| |
1737 ident->category = g_strdup(category); |
| |
1738 ident->type = g_strdup(type); |
| |
1739 ident->lang = g_strdup(lang); |
| |
1740 ident->name = g_strdup(name); |
| |
1741 jabber_identities = g_list_append(jabber_identities, ident); |
| |
1742 } |
| |
1743 |
| |
1744 static void jabber_identities_destroy(void) |
| |
1745 { |
| |
1746 while (jabber_identities) { |
| |
1747 JabberIdentity *id = jabber_identities->data; |
| |
1748 g_free(id->category); |
| |
1749 g_free(id->type); |
| |
1750 g_free(id->lang); |
| |
1751 g_free(id->name); |
| |
1752 g_free(id); |
| |
1753 jabber_identities = g_list_remove_link(jabber_identities, jabber_identities); |
| |
1754 } |
| |
1755 } |
| |
1756 |
| |
1757 gboolean jabber_stream_is_ssl(JabberStream *js) |
| |
1758 { |
| |
1759 return (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) || |
| |
1760 (!js->bosh && js->gsc); |
| 1644 } |
1761 } |
| 1645 |
1762 |
| 1646 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) |
1763 const char *jabber_list_icon(PurpleAccount *a, PurpleBuddy *b) |
| 1647 { |
1764 { |
| 1648 return "jabber"; |
1765 return "jabber"; |
| 2966 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, |
3107 PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, |
| 2967 "prpl-jabber", jabber_cmd_buzz, |
3108 "prpl-jabber", jabber_cmd_buzz, |
| 2968 _("buzz: Buzz a user to get their attention"), NULL); |
3109 _("buzz: Buzz a user to get their attention"), NULL); |
| 2969 } |
3110 } |
| 2970 |
3111 |
| |
3112 /* IPC functions */ |
| |
3113 |
| |
3114 /** |
| |
3115 * IPC function for determining if a contact supports a certain feature. |
| |
3116 * |
| |
3117 * @param account The PurpleAccount |
| |
3118 * @param jid The full JID of the contact. |
| |
3119 * @param feature The feature's namespace. |
| |
3120 * |
| |
3121 * @return TRUE if supports feature; else FALSE. |
| |
3122 */ |
| |
3123 static gboolean |
| |
3124 jabber_ipc_contact_has_feature(PurpleAccount *account, const gchar *jid, |
| |
3125 const gchar *feature) |
| |
3126 { |
| |
3127 PurpleConnection *gc = purple_account_get_connection(account); |
| |
3128 JabberStream *js; |
| |
3129 JabberBuddy *jb; |
| |
3130 JabberBuddyResource *jbr; |
| |
3131 gchar *resource; |
| |
3132 |
| |
3133 if (!purple_account_is_connected(account)) |
| |
3134 return FALSE; |
| |
3135 js = gc->proto_data; |
| |
3136 |
| |
3137 if (!(resource = jabber_get_resource(jid)) || |
| |
3138 !(jb = jabber_buddy_find(js, jid, FALSE)) || |
| |
3139 !(jbr = jabber_buddy_find_resource(jb, resource))) { |
| |
3140 g_free(resource); |
| |
3141 return FALSE; |
| |
3142 } |
| |
3143 |
| |
3144 g_free(resource); |
| |
3145 |
| |
3146 return jabber_resource_has_capability(jbr, feature); |
| |
3147 } |
| |
3148 |
| |
3149 static void |
| |
3150 jabber_ipc_add_feature(const gchar *feature) |
| |
3151 { |
| |
3152 if (!feature) |
| |
3153 return; |
| |
3154 jabber_add_feature(feature, 0); |
| |
3155 |
| |
3156 /* send presence with new caps info for all connected accounts */ |
| |
3157 jabber_caps_broadcast_change(); |
| |
3158 } |
| |
3159 |
| 2971 void |
3160 void |
| 2972 jabber_init_plugin(PurplePlugin *plugin) |
3161 jabber_init_plugin(PurplePlugin *plugin) |
| 2973 { |
3162 { |
| 2974 my_protocol = plugin; |
3163 my_protocol = plugin; |
| 2975 } |
3164 |
| |
3165 jabber_add_identity("client", "pc", NULL, PACKAGE); |
| |
3166 |
| |
3167 /* initialize jabber_features list */ |
| |
3168 jabber_add_feature("jabber:iq:last", 0); |
| |
3169 jabber_add_feature("jabber:iq:oob", 0); |
| |
3170 jabber_add_feature("jabber:iq:time", 0); |
| |
3171 jabber_add_feature("urn:xmpp:time", 0); |
| |
3172 jabber_add_feature("jabber:iq:version", 0); |
| |
3173 jabber_add_feature("jabber:x:conference", 0); |
| |
3174 jabber_add_feature("http://jabber.org/protocol/bytestreams", 0); |
| |
3175 jabber_add_feature("http://jabber.org/protocol/caps", 0); |
| |
3176 jabber_add_feature("http://jabber.org/protocol/chatstates", 0); |
| |
3177 jabber_add_feature("http://jabber.org/protocol/disco#info", 0); |
| |
3178 jabber_add_feature("http://jabber.org/protocol/disco#items", 0); |
| |
3179 jabber_add_feature("http://jabber.org/protocol/ibb", 0); |
| |
3180 jabber_add_feature("http://jabber.org/protocol/muc", 0); |
| |
3181 jabber_add_feature("http://jabber.org/protocol/muc#user", 0); |
| |
3182 jabber_add_feature("http://jabber.org/protocol/si", 0); |
| |
3183 jabber_add_feature("http://jabber.org/protocol/si/profile/file-transfer", 0); |
| |
3184 jabber_add_feature("http://jabber.org/protocol/xhtml-im", 0); |
| |
3185 jabber_add_feature("urn:xmpp:ping", 0); |
| |
3186 |
| |
3187 /* Jingle features! */ |
| |
3188 jabber_add_feature(JINGLE, 0); |
| |
3189 jabber_add_feature(JINGLE_TRANSPORT_RAWUDP, 0); |
| |
3190 |
| |
3191 #ifdef USE_VV |
| |
3192 jabber_add_feature("http://www.google.com/xmpp/protocol/session", jabber_audio_enabled); |
| |
3193 jabber_add_feature("http://www.google.com/xmpp/protocol/voice/v1", jabber_audio_enabled); |
| |
3194 jabber_add_feature(JINGLE_APP_RTP_SUPPORT_AUDIO, jabber_audio_enabled); |
| |
3195 jabber_add_feature(JINGLE_APP_RTP_SUPPORT_VIDEO, jabber_video_enabled); |
| |
3196 jabber_add_feature(JINGLE_TRANSPORT_ICEUDP, jabber_ice_transmitter_present); |
| |
3197 #endif |
| |
3198 |
| |
3199 /* IPC functions */ |
| |
3200 purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), |
| |
3201 purple_marshal_BOOLEAN__POINTER_POINTER_POINTER, |
| |
3202 purple_value_new(PURPLE_TYPE_BOOLEAN), 3, |
| |
3203 purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT), |
| |
3204 purple_value_new(PURPLE_TYPE_STRING), |
| |
3205 purple_value_new(PURPLE_TYPE_STRING)); |
| |
3206 purple_plugin_ipc_register(plugin, "add_feature", PURPLE_CALLBACK(jabber_ipc_add_feature), |
| |
3207 purple_marshal_VOID__POINTER, |
| |
3208 NULL, 1, |
| |
3209 purple_value_new(PURPLE_TYPE_STRING)); |
| |
3210 } |
| |
3211 |
| |
3212 void |
| |
3213 jabber_uninit_plugin(void) |
| |
3214 { |
| |
3215 jabber_features_destroy(); |
| |
3216 jabber_identities_destroy(); |
| |
3217 } |