| 763 /* Tell the app that we're doing encryption */ |
761 /* Tell the app that we're doing encryption */ |
| 764 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
762 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION); |
| 765 } |
763 } |
| 766 |
764 |
| 767 static void |
765 static void |
| 768 txt_resolved_cb(GList *responses, gpointer data) |
766 txt_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data) |
| 769 { |
767 { |
| |
768 GError *error = NULL; |
| |
769 GList *records = NULL, *l = NULL; |
| 770 JabberStream *js = data; |
770 JabberStream *js = data; |
| 771 gboolean found = FALSE; |
771 gboolean found = FALSE; |
| 772 |
772 |
| 773 js->srv_query_data = NULL; |
773 records = g_resolver_lookup_service_finish(g_resolver_get_default(), result, &error); |
| 774 |
774 if(error) { |
| 775 while (responses) { |
775 purple_debug_warning("jabber", "Unable to find alternative XMPP connection " |
| 776 PurpleTxtResponse *resp = responses->data; |
776 "methods after failing to connect directly. : %s\n", |
| 777 gchar **token; |
777 error->message); |
| 778 token = g_strsplit(purple_txt_response_get_content(resp), "=", 2); |
778 |
| 779 if (!strcmp(token[0], "_xmpp-client-xbosh")) { |
779 purple_connection_error(js->gc, |
| 780 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); |
780 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
| 781 js->bosh = jabber_bosh_connection_new(js, token[1]); |
781 _("Unable to connect")); |
| |
782 |
| |
783 g_error_free(error); |
| |
784 |
| |
785 return; |
| |
786 } |
| |
787 |
| |
788 for(l = records; l; l = l->next) { |
| |
789 GVariantIter *iter = NULL; |
| |
790 gchar *str = NULL; |
| |
791 |
| |
792 g_variant_get((GVariant *)l->data, "(as)", &iter); |
| |
793 while(g_variant_iter_loop(iter, "s", &str)) { |
| |
794 gchar **token = g_strsplit(str, "=", 2); |
| |
795 |
| |
796 if(!g_ascii_strcasecmp(token[0], "_xmpp-client-xbosh")) { |
| |
797 purple_debug_info("jabber","Found alternative connection method using %s at %s.\n", token[0], token[1]); |
| |
798 |
| |
799 js->bosh = jabber_bosh_connection_new(js, token[1]); |
| |
800 |
| |
801 g_strfreev(token); |
| |
802 |
| |
803 break; |
| |
804 } |
| |
805 |
| 782 g_strfreev(token); |
806 g_strfreev(token); |
| 783 break; |
807 } |
| 784 } |
808 |
| 785 g_strfreev(token); |
809 g_variant_iter_free(iter); |
| 786 purple_txt_response_destroy(resp); |
810 } |
| 787 responses = g_list_delete_link(responses, responses); |
811 |
| 788 } |
812 g_list_free_full(records, (GDestroyNotify)g_variant_unref); |
| 789 |
813 |
| 790 if (js->bosh) |
814 if (js->bosh) |
| 791 found = TRUE; |
815 found = TRUE; |
| 792 |
816 |
| 793 if (!found) { |
817 if (!found) { |
| 796 purple_connection_error(js->gc, |
820 purple_connection_error(js->gc, |
| 797 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
821 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
| 798 _("Unable to connect")); |
822 _("Unable to connect")); |
| 799 return; |
823 return; |
| 800 } |
824 } |
| 801 |
|
| 802 if (responses) { |
|
| 803 g_list_foreach(responses, (GFunc)purple_txt_response_destroy, NULL); |
|
| 804 g_list_free(responses); |
|
| 805 } |
|
| 806 } |
825 } |
| 807 |
826 |
| 808 static void |
827 static void |
| 809 jabber_login_callback(gpointer data, gint source, const gchar *error) |
828 jabber_login_callback(gpointer data, gint source, const gchar *error) |
| 810 { |
829 { |
| 811 PurpleConnection *gc = data; |
830 PurpleConnection *gc = data; |
| 812 JabberStream *js = purple_connection_get_protocol_data(gc); |
831 JabberStream *js = purple_connection_get_protocol_data(gc); |
| 813 |
832 |
| 814 if (source < 0) { |
833 if (source < 0) { |
| 815 if (js->srv_rec != NULL) { |
834 gchar *name = g_strdup_printf("_xmppconnect.%s", js->user->domain); |
| 816 purple_debug_error("jabber", "Unable to connect to server: %s. Trying next SRV record or connecting directly.\n", error); |
835 |
| 817 try_srv_connect(js); |
836 purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); |
| 818 } else { |
837 |
| 819 purple_debug_info("jabber","Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); |
838 g_resolver_lookup_records_async(g_resolver_get_default(), |
| 820 js->srv_query_data = purple_txt_resolve( |
839 name, |
| 821 purple_connection_get_account(gc), "_xmppconnect", |
840 G_RESOLVER_RECORD_TXT, |
| 822 js->user->domain, txt_resolved_cb, js); |
841 js->cancellable, |
| 823 } |
842 txt_resolved_cb, |
| |
843 js); |
| |
844 g_free(name); |
| |
845 |
| 824 return; |
846 return; |
| 825 } |
847 } |
| 826 |
|
| 827 g_free(js->srv_rec); |
|
| 828 js->srv_rec = NULL; |
|
| 829 |
848 |
| 830 js->fd = source; |
849 js->fd = source; |
| 831 |
850 |
| 832 if(js->state == JABBER_STREAM_CONNECTING) |
851 if(js->state == JABBER_STREAM_CONNECTING) |
| 833 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
852 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
| 886 } |
905 } |
| 887 |
906 |
| 888 return TRUE; |
907 return TRUE; |
| 889 } |
908 } |
| 890 |
909 |
| 891 static void try_srv_connect(JabberStream *js) |
910 static void |
| 892 { |
911 srv_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data) |
| 893 while (js->srv_rec != NULL && js->srv_rec_idx < js->max_srv_rec_idx) { |
912 { |
| 894 PurpleSrvResponse *tmp_resp = js->srv_rec + (js->srv_rec_idx++); |
913 GError *error = NULL; |
| 895 if (jabber_login_connect(js, tmp_resp->hostname, tmp_resp->hostname, tmp_resp->port, FALSE)) |
914 GList *targets = NULL, *l = NULL; |
| 896 return; |
|
| 897 } |
|
| 898 |
|
| 899 g_free(js->srv_rec); |
|
| 900 js->srv_rec = NULL; |
|
| 901 |
|
| 902 /* Fall back to the defaults (I'm not sure if we should actually do this) */ |
|
| 903 jabber_login_connect(js, js->user->domain, js->user->domain, |
|
| 904 purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), |
|
| 905 TRUE); |
|
| 906 } |
|
| 907 |
|
| 908 static void srv_resolved_cb(PurpleSrvResponse *resp, int results, gpointer data) |
|
| 909 { |
|
| 910 JabberStream *js = data; |
915 JabberStream *js = data; |
| 911 js->srv_query_data = NULL; |
916 |
| 912 |
917 targets = g_resolver_lookup_service_finish(g_resolver_get_default(), result, &error); |
| 913 if(results) { |
918 if(error) { |
| 914 js->srv_rec = resp; |
919 purple_debug_warning("jabber", |
| 915 js->srv_rec_idx = 0; |
920 "SRV lookup failed, proceeding with normal connection : %s", |
| 916 js->max_srv_rec_idx = results; |
921 error->message); |
| 917 try_srv_connect(js); |
922 |
| 918 } else { |
923 g_error_free(error); |
| |
924 |
| 919 jabber_login_connect(js, js->user->domain, js->user->domain, |
925 jabber_login_connect(js, js->user->domain, js->user->domain, |
| 920 purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), |
926 purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), |
| 921 TRUE); |
927 TRUE); |
| |
928 |
| |
929 } else { |
| |
930 for(l = targets; l; l = l->next) { |
| |
931 GSrvTarget *target = (GSrvTarget *)l->data; |
| |
932 const gchar *hostname = g_srv_target_get_hostname(target); |
| |
933 guint port = g_srv_target_get_port(target); |
| |
934 |
| |
935 if(jabber_login_connect(js, hostname, hostname, port, FALSE)) { |
| |
936 g_resolver_free_targets(targets); |
| |
937 |
| |
938 return; |
| |
939 } |
| |
940 } |
| |
941 |
| |
942 g_resolver_free_targets(targets); |
| |
943 |
| |
944 jabber_login_connect(js, js->user->domain, js->user->domain, |
| |
945 purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222), |
| |
946 TRUE); |
| 922 } |
947 } |
| 923 } |
948 } |
| 924 |
949 |
| 925 static JabberStream * |
950 static JabberStream * |
| 926 jabber_stream_new(PurpleAccount *account) |
951 jabber_stream_new(PurpleAccount *account) |
| 934 js = g_new0(JabberStream, 1); |
959 js = g_new0(JabberStream, 1); |
| 935 purple_connection_set_protocol_data(gc, js); |
960 purple_connection_set_protocol_data(gc, js); |
| 936 js->gc = gc; |
961 js->gc = gc; |
| 937 js->fd = -1; |
962 js->fd = -1; |
| 938 js->http_conns = purple_http_connection_set_new(); |
963 js->http_conns = purple_http_connection_set_new(); |
| |
964 |
| |
965 /* we might want to expose this at some point */ |
| |
966 js->cancellable = g_cancellable_new(); |
| 939 |
967 |
| 940 user = g_strdup(purple_account_get_username(account)); |
968 user = g_strdup(purple_account_get_username(account)); |
| 941 /* jabber_id_new doesn't accept "user@domain/" as valid */ |
969 /* jabber_id_new doesn't accept "user@domain/" as valid */ |
| 942 slash = strchr(user, '/'); |
970 slash = strchr(user, '/'); |
| 943 if (slash && *(slash + 1) == '\0') |
971 if (slash && *(slash + 1) == '\0') |
| 1005 js->protocol_version.major = 1; |
1033 js->protocol_version.major = 1; |
| 1006 js->protocol_version.minor = 0; |
1034 js->protocol_version.minor = 0; |
| 1007 js->sessions = NULL; |
1035 js->sessions = NULL; |
| 1008 js->stun_ip = NULL; |
1036 js->stun_ip = NULL; |
| 1009 js->stun_port = 0; |
1037 js->stun_port = 0; |
| 1010 js->stun_query = NULL; |
|
| 1011 js->google_relay_token = NULL; |
1038 js->google_relay_token = NULL; |
| 1012 js->google_relay_host = NULL; |
1039 js->google_relay_host = NULL; |
| 1013 |
1040 |
| 1014 /* if we are idle, set idle-ness on the stream (this could happen if we get |
1041 /* if we are idle, set idle-ness on the stream (this could happen if we get |
| 1015 disconnected and the reconnects while being idle. I don't think it makes |
1042 disconnected and the reconnects while being idle. I don't think it makes |
| 1073 * invoke the magic of SRV lookups, to figure out host and port */ |
1100 * invoke the magic of SRV lookups, to figure out host and port */ |
| 1074 if(connect_server[0]) { |
1101 if(connect_server[0]) { |
| 1075 jabber_login_connect(js, js->user->domain, connect_server, |
1102 jabber_login_connect(js, js->user->domain, connect_server, |
| 1076 purple_account_get_int(account, "port", 5222), TRUE); |
1103 purple_account_get_int(account, "port", 5222), TRUE); |
| 1077 } else { |
1104 } else { |
| 1078 js->srv_query_data = purple_srv_resolve(account, "xmpp-client", |
1105 g_resolver_lookup_service_async(g_resolver_get_default(), |
| 1079 "tcp", js->user->domain, srv_resolved_cb, js); |
1106 "xmpp-client", |
| |
1107 "tcp", |
| |
1108 js->user->domain, |
| |
1109 js->cancellable, |
| |
1110 srv_resolved_cb, |
| |
1111 js); |
| 1080 } |
1112 } |
| 1081 } |
1113 } |
| 1082 |
1114 |
| 1083 void |
1115 void |
| 1084 jabber_login(PurpleAccount *account) |
1116 jabber_login(PurpleAccount *account) |
| 1605 if (js->bosh) { |
1637 if (js->bosh) { |
| 1606 jabber_bosh_connection_destroy(js->bosh); |
1638 jabber_bosh_connection_destroy(js->bosh); |
| 1607 js->bosh = NULL; |
1639 js->bosh = NULL; |
| 1608 } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0) |
1640 } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0) |
| 1609 jabber_send_raw(js, "</stream:stream>", -1); |
1641 jabber_send_raw(js, "</stream:stream>", -1); |
| 1610 |
|
| 1611 if (js->srv_query_data) |
|
| 1612 purple_srv_txt_query_destroy(js->srv_query_data); |
|
| 1613 |
1642 |
| 1614 if(js->gsc) { |
1643 if(js->gsc) { |
| 1615 purple_ssl_close(js->gsc); |
1644 purple_ssl_close(js->gsc); |
| 1616 } else if (js->fd > 0) { |
1645 } else if (js->fd > 0) { |
| 1617 if(js->inpa) { |
1646 if(js->inpa) { |
| 1704 if (js->inactivity_timer != 0) |
1733 if (js->inactivity_timer != 0) |
| 1705 purple_timeout_remove(js->inactivity_timer); |
1734 purple_timeout_remove(js->inactivity_timer); |
| 1706 if (js->conn_close_timeout != 0) |
1735 if (js->conn_close_timeout != 0) |
| 1707 purple_timeout_remove(js->conn_close_timeout); |
1736 purple_timeout_remove(js->conn_close_timeout); |
| 1708 |
1737 |
| 1709 g_free(js->srv_rec); |
1738 g_cancellable_cancel(js->cancellable); |
| 1710 js->srv_rec = NULL; |
1739 g_object_unref(G_OBJECT(js->cancellable)); |
| 1711 |
1740 |
| 1712 g_free(js->stun_ip); |
1741 g_free(js->stun_ip); |
| 1713 js->stun_ip = NULL; |
|
| 1714 |
|
| 1715 /* cancel DNS query for STUN, if one is ongoing */ |
|
| 1716 if (js->stun_query) { |
|
| 1717 purple_dnsquery_destroy(js->stun_query); |
|
| 1718 js->stun_query = NULL; |
|
| 1719 } |
|
| 1720 |
1742 |
| 1721 /* remove Google relay-related stuff */ |
1743 /* remove Google relay-related stuff */ |
| 1722 g_free(js->google_relay_token); |
1744 g_free(js->google_relay_token); |
| 1723 g_free(js->google_relay_host); |
1745 g_free(js->google_relay_host); |
| 1724 |
1746 |