* Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA #define CONNECTION_CLOSE_TIMEOUT 15 emit_error(PurpleSslConnection *gsc, int error_code) if (gsc->error_cb != NULL) gsc->error_cb(gsc, error_code, gsc->connect_cb_data); tls_handshake_cb(GObject *source, GAsyncResult *res, gpointer user_data) PurpleSslConnection *gsc = user_data; if (!g_tls_connection_handshake_finish(G_TLS_CONNECTION(source), res, if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { /* Connection already closed/freed. Escape. */ } else if (g_error_matches(error, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) { /* In Gio, a handshake error is because of the cert */ emit_error(gsc, PURPLE_SSL_CERTIFICATE_INVALID); /* Report any other errors as handshake failing */ emit_error(gsc, PURPLE_SSL_HANDSHAKE_FAILED); gsc->connect_cb(gsc->connect_cb_data, gsc, PURPLE_INPUT_READ); tls_connect(PurpleSslConnection *gsc) GSocketConnectable *identity; g_return_val_if_fail(gsc->conn == NULL, FALSE); socket = g_socket_new_from_fd(gsc->fd, &error); purple_debug_warning("sslconn", "Error creating socket from fd (%u): %s", gsc->fd, error->message); conn = g_socket_connection_factory_create_connection(socket); identity = g_network_address_new(gsc->host, gsc->port); tls_conn = g_tls_client_connection_new(G_IO_STREAM(conn), identity, g_object_unref(identity); purple_debug_warning("sslconn", "Error creating TLS client connection: %s", gsc->conn = G_TLS_CONNECTION(tls_conn); gsc->cancellable = g_cancellable_new(); g_tls_connection_handshake_async(gsc->conn, G_PRIORITY_DEFAULT, gsc->cancellable, tls_handshake_cb, gsc); purple_ssl_connect_cb(gpointer data, gint source, const gchar *error_message) PurpleSslConnection *gsc; gsc->connect_data = NULL; emit_error(gsc, PURPLE_SSL_CONNECT_FAILED); emit_error(gsc, PURPLE_SSL_CONNECT_FAILED); purple_ssl_connect(PurpleAccount *account, const char *host, int port, PurpleSslInputFunction func, PurpleSslErrorFunction error_func, return purple_ssl_connect_with_ssl_cn(account, host, port, func, error_func, purple_ssl_connect_with_ssl_cn(PurpleAccount *account, const char *host, int port, PurpleSslInputFunction func, PurpleSslErrorFunction error_func, const char *ssl_cn, void *data) PurpleSslConnection *gsc; g_return_val_if_fail(host != NULL, NULL); g_return_val_if_fail(port != 0 && port != -1, NULL); g_return_val_if_fail(func != NULL, NULL); gsc = g_new0(PurpleSslConnection, 1); gsc->host = ssl_cn ? g_strdup(ssl_cn) : g_strdup(host); gsc->connect_cb_data = data; gsc->error_cb = error_func; gsc->connect_data = purple_proxy_connect(NULL, account, host, port, purple_ssl_connect_cb, gsc); if (gsc->connect_data == NULL) return (PurpleSslConnection *)gsc; recv_cb(GObject *source, gpointer data) PurpleSslConnection *gsc = data; gsc->recv_cb(gsc->recv_cb_data, gsc, PURPLE_INPUT_READ); purple_ssl_input_add(PurpleSslConnection *gsc, PurpleSslInputFunction func, g_return_if_fail(func != NULL); g_return_if_fail(gsc->conn != NULL); purple_ssl_input_remove(gsc); gsc->recv_cb_data = data; input = g_io_stream_get_input_stream(G_IO_STREAM(gsc->conn)); /* Pass NULL for cancellable as we don't want it notified on cancel */ source = g_pollable_input_stream_create_source( G_POLLABLE_INPUT_STREAM(input), NULL); g_source_set_callback(source, (GSourceFunc)recv_cb, gsc, NULL); gsc->inpa = g_source_attach(source, NULL); purple_ssl_input_remove(PurpleSslConnection *gsc) g_source_remove(gsc->inpa); purple_ssl_strerror(PurpleSslErrorType error) case PURPLE_SSL_CONNECT_FAILED: return _("SSL Connection Failed"); case PURPLE_SSL_HANDSHAKE_FAILED: return _("SSL Handshake Failed"); case PURPLE_SSL_CERTIFICATE_INVALID: return _("SSL peer presented an invalid certificate"); purple_debug_warning("sslconn", "Unknown SSL error code %d\n", error); return _("Unknown SSL error"); purple_ssl_connect_with_host_fd(PurpleAccount *account, int fd, PurpleSslInputFunction func, PurpleSslErrorFunction error_func, PurpleSslConnection *gsc; g_return_val_if_fail(fd != -1, NULL); g_return_val_if_fail(func != NULL, NULL); gsc = g_new0(PurpleSslConnection, 1); gsc->connect_cb_data = data; gsc->error_cb = error_func; gsc->host = g_strdup(host); gsc->cancellable = g_cancellable_new(); emit_error(gsc, PURPLE_SSL_CONNECT_FAILED); g_clear_pointer(&gsc, purple_ssl_close); return (PurpleSslConnection *)gsc; connection_closed_cb(GObject *stream, GAsyncResult *result, g_source_remove(GPOINTER_TO_UINT(timeout_id)); g_io_stream_close_finish(G_IO_STREAM(stream), result, &error); purple_debug_info("sslconn", "Connection close error: %s", purple_debug_info("sslconn", "Connection closed."); cleanup_cancellable_cb(gpointer data, GObject *where_the_object_was) g_object_unref(G_CANCELLABLE(data)); purple_ssl_close(PurpleSslConnection *gsc) g_return_if_fail(gsc != NULL); purple_request_close_with_handle(gsc); purple_notify_close_with_handle(gsc); if (gsc->connect_data != NULL) purple_proxy_connect_cancel(gsc->connect_data); purple_input_remove(gsc->inpa); /* Stop any pending operations */ if (G_IS_CANCELLABLE(gsc->cancellable)) { g_cancellable_cancel(gsc->cancellable); g_clear_object(&gsc->cancellable); GCancellable *cancellable; cancellable = g_cancellable_new(); g_object_weak_ref(G_OBJECT(gsc->conn), cleanup_cancellable_cb, timer_id = g_timeout_add_seconds(CONNECTION_CLOSE_TIMEOUT, (GSourceFunc)g_cancellable_cancel, cancellable); g_io_stream_close_async(G_IO_STREAM(gsc->conn), G_PRIORITY_DEFAULT, cancellable, GUINT_TO_POINTER(timer_id)); g_clear_object(&gsc->conn); purple_ssl_read(PurpleSslConnection *gsc, void *data, size_t len) g_return_val_if_fail(gsc != NULL, 0); g_return_val_if_fail(data != NULL, 0); g_return_val_if_fail(len > 0, 0); g_return_val_if_fail(gsc->conn != NULL, 0); input = g_io_stream_get_input_stream(G_IO_STREAM(gsc->conn)); outlen = g_pollable_input_stream_read_nonblocking( G_POLLABLE_INPUT_STREAM(input), data, len, gsc->cancellable, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { purple_ssl_write(PurpleSslConnection *gsc, const void *data, size_t len) g_return_val_if_fail(gsc != NULL, 0); g_return_val_if_fail(data != NULL, 0); g_return_val_if_fail(len > 0, 0); g_return_val_if_fail(gsc->conn != NULL, 0); output = g_io_stream_get_output_stream(G_IO_STREAM(gsc->conn)); outlen = g_pollable_output_stream_write_nonblocking( G_POLLABLE_OUTPUT_STREAM(output), data, len, gsc->cancellable, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { purple_ssl_get_peer_certificates(PurpleSslConnection *gsc) GTlsCertificate *certificate; g_return_val_if_fail(gsc != NULL, NULL); g_return_val_if_fail(gsc->conn != NULL, NULL); certificate = g_tls_connection_get_peer_certificate(gsc->conn); return certificate != NULL ? g_list_append(NULL, certificate) : NULL;