| 821 _("Unable to connect")); |
821 _("Unable to connect")); |
| 822 return; |
822 return; |
| 823 } |
823 } |
| 824 } |
824 } |
| 825 |
825 |
| 826 static void |
|
| 827 jabber_login_callback(gpointer data, gint source, const gchar *error) |
|
| 828 { |
|
| 829 PurpleConnection *gc = data; |
|
| 830 JabberStream *js = purple_connection_get_protocol_data(gc); |
|
| 831 |
|
| 832 if (source < 0) { |
|
| 833 GResolver *resolver = g_resolver_get_default(); |
|
| 834 gchar *name = g_strdup_printf("_xmppconnect.%s", js->user->domain); |
|
| 835 |
|
| 836 purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain); |
|
| 837 |
|
| 838 g_resolver_lookup_records_async(resolver, |
|
| 839 name, |
|
| 840 G_RESOLVER_RECORD_TXT, |
|
| 841 js->cancellable, |
|
| 842 txt_resolved_cb, |
|
| 843 js); |
|
| 844 g_free(name); |
|
| 845 g_object_unref(resolver); |
|
| 846 |
|
| 847 return; |
|
| 848 } |
|
| 849 |
|
| 850 js->fd = source; |
|
| 851 |
|
| 852 if(js->state == JABBER_STREAM_CONNECTING) |
|
| 853 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
|
| 854 |
|
| 855 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING); |
|
| 856 js->inpa = purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, gc); |
|
| 857 } |
|
| 858 |
|
| 859 /* Grabbed duplicate_fd() from GLib's testcases (gio/tests/socket.c). |
826 /* Grabbed duplicate_fd() from GLib's testcases (gio/tests/socket.c). |
| 860 * Can be dropped once this prpl has been fully converted to Gio. |
827 * Can be dropped once this prpl has been fully converted to Gio. |
| 861 */ |
828 */ |
| 862 static int |
829 static int |
| 863 duplicate_fd(int fd) |
830 duplicate_fd(int fd) |
| 958 jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc); |
924 jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc); |
| 959 /* The fd is no longer our concern */ |
925 /* The fd is no longer our concern */ |
| 960 js->fd = -1; |
926 js->fd = -1; |
| 961 } |
927 } |
| 962 |
928 |
| 963 static gboolean |
|
| 964 jabber_login_connect(JabberStream *js, const char *host, int port, |
|
| 965 gboolean fatal_failure) |
|
| 966 { |
|
| 967 if (purple_proxy_connect(js->gc, purple_connection_get_account(js->gc), |
|
| 968 host, port, jabber_login_callback, js->gc) == NULL) { |
|
| 969 if (fatal_failure) { |
|
| 970 purple_connection_error(js->gc, |
|
| 971 PURPLE_CONNECTION_ERROR_NETWORK_ERROR, |
|
| 972 _("Unable to connect")); |
|
| 973 } |
|
| 974 |
|
| 975 return FALSE; |
|
| 976 } |
|
| 977 |
|
| 978 return TRUE; |
|
| 979 } |
|
| 980 |
|
| 981 static void |
929 static void |
| 982 srv_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data) |
930 srv_resolved_cb(GObject *source_object, GAsyncResult *result, gpointer data) |
| 983 { |
931 { |
| |
932 GSocketClient *client = G_SOCKET_CLIENT(source_object); |
| 984 GError *error = NULL; |
933 GError *error = NULL; |
| 985 GList *targets = NULL, *l = NULL; |
934 GSocketConnection *conn; |
| |
935 GSocket *socket; |
| 986 JabberStream *js = data; |
936 JabberStream *js = data; |
| 987 |
937 |
| 988 targets = g_resolver_lookup_service_finish(G_RESOLVER(sender), |
938 conn = g_socket_client_connect_to_service_finish(client, result, &error); |
| 989 result, &error); |
939 if (error) { |
| 990 if(error) { |
940 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { |
| 991 purple_debug_warning("jabber", |
941 /* Do nothing; cancelled. */ |
| 992 "SRV lookup failed, proceeding with normal connection : %s", |
942 |
| 993 error->message); |
943 } else if (g_error_matches(error, G_RESOLVER_ERROR, |
| |
944 G_RESOLVER_ERROR_NOT_FOUND)) { |
| |
945 /* If there was no response, then attempt fallback behaviour of XMPP |
| |
946 * Core 3.2.2. */ |
| |
947 purple_debug_warning( |
| |
948 "jabber", |
| |
949 "SRV lookup failed, proceeding with normal connection : %s", |
| |
950 error->message); |
| |
951 |
| |
952 g_socket_client_connect_to_host_async( |
| |
953 js->client, js->user->domain, |
| |
954 purple_account_get_int( |
| |
955 purple_connection_get_account(js->gc), "port", |
| |
956 5222), |
| |
957 js->cancellable, jabber_login_callback, js); |
| |
958 |
| |
959 } else { |
| |
960 /* If resolving failed or connecting failed, then just error out, as |
| |
961 * in XMPP Core 3.2.1 step 8. */ |
| |
962 purple_connection_g_error(js->gc, error); |
| |
963 } |
| 994 |
964 |
| 995 g_error_free(error); |
965 g_error_free(error); |
| 996 |
966 return; |
| 997 jabber_login_connect( |
967 } |
| 998 js, js->user->domain, |
968 |
| 999 purple_account_get_int(purple_connection_get_account(js->gc), |
969 socket = g_socket_connection_get_socket(conn); |
| 1000 "port", 5222), |
970 g_assert(socket != NULL); |
| 1001 TRUE); |
971 |
| 1002 |
972 /* Duplicate the file descriptor, and then free the connection. |
| 1003 } else { |
973 * libpurple's proxy code doesn't keep an object around for the |
| 1004 for(l = targets; l; l = l->next) { |
974 * lifetime of the connection. Therefore, in order to not leak |
| 1005 GSrvTarget *target = (GSrvTarget *)l->data; |
975 * memory, the GSocketConnection must be freed here. In order |
| 1006 const gchar *hostname = g_srv_target_get_hostname(target); |
976 * to avoid the double close/free of the file descriptor, the |
| 1007 guint port = g_srv_target_get_port(target); |
977 * file descriptor is duplicated. |
| 1008 |
978 */ |
| 1009 if (jabber_login_connect(js, hostname, port, FALSE)) { |
979 js->fd = duplicate_fd(g_socket_get_fd(socket)); |
| 1010 g_resolver_free_targets(targets); |
980 g_object_unref(conn); |
| 1011 |
981 |
| 1012 return; |
982 if (js->state == JABBER_STREAM_CONNECTING) { |
| 1013 } |
983 jabber_send_raw(js, "<?xml version='1.0' ?>", -1); |
| 1014 } |
984 } |
| 1015 |
985 |
| 1016 g_resolver_free_targets(targets); |
986 jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING); |
| 1017 |
987 js->inpa = |
| 1018 jabber_login_connect( |
988 purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, js->gc); |
| 1019 js, js->user->domain, |
|
| 1020 purple_account_get_int(purple_connection_get_account(js->gc), |
|
| 1021 "port", 5222), |
|
| 1022 TRUE); |
|
| 1023 } |
|
| 1024 } |
989 } |
| 1025 |
990 |
| 1026 static JabberStream * |
991 static JabberStream * |
| 1027 jabber_stream_new(PurpleAccount *account) |
992 jabber_stream_new(PurpleAccount *account) |
| 1028 { |
993 { |
| 1189 * invoke the magic of SRV lookups, to figure out host and port */ |
1154 * invoke the magic of SRV lookups, to figure out host and port */ |
| 1190 if(connect_server[0]) { |
1155 if(connect_server[0]) { |
| 1191 g_socket_client_connect_to_host_async( |
1156 g_socket_client_connect_to_host_async( |
| 1192 js->client, connect_server, |
1157 js->client, connect_server, |
| 1193 purple_account_get_int(account, "port", 5222), js->cancellable, |
1158 purple_account_get_int(account, "port", 5222), js->cancellable, |
| 1194 jabber_login_callback_async, js); |
1159 jabber_login_callback, js); |
| 1195 } else { |
1160 } else { |
| 1196 GResolver *resolver = g_resolver_get_default(); |
1161 g_socket_client_connect_to_service_async(js->client, js->user->domain, |
| 1197 g_resolver_lookup_service_async(resolver, |
1162 "xmpp-client", js->cancellable, |
| 1198 "xmpp-client", |
1163 srv_resolved_cb, js); |
| 1199 "tcp", |
|
| 1200 js->user->domain, |
|
| 1201 js->cancellable, |
|
| 1202 srv_resolved_cb, |
|
| 1203 js); |
|
| 1204 g_object_unref(resolver); |
|
| 1205 } |
1164 } |
| 1206 } |
1165 } |
| 1207 |
1166 |
| 1208 void |
1167 void |
| 1209 jabber_login(PurpleAccount *account) |
1168 jabber_login(PurpleAccount *account) |