/pidgin/main: f681d6d805c6: PurpleSocket implementation

Tomasz Wasilczyk twasilczyk at pidgin.im
Thu Aug 29 04:25:59 EDT 2013


Changeset: f681d6d805c6e09431ea94d233bdf9ef0ed1b459
Author:	 Tomasz Wasilczyk <twasilczyk at pidgin.im>
Date:	 2013-08-29 10:25 +0200
Branch:	 default
URL: https://hg.pidgin.im/pidgin/main/rev/f681d6d805c6

Description:

PurpleSocket implementation

diffstat:

 libpurple/Makefile.am     |    2 +
 libpurple/Makefile.mingw  |    1 +
 libpurple/purple-socket.c |  330 ++++++++++++++++++++++++++++++++++++++++++++++
 libpurple/purple-socket.h |  179 ++++++++++++++++++++++++
 4 files changed, 512 insertions(+), 0 deletions(-)

diffs (truncated from 549 to 300 lines):

diff --git a/libpurple/Makefile.am b/libpurple/Makefile.am
--- a/libpurple/Makefile.am
+++ b/libpurple/Makefile.am
@@ -76,6 +76,7 @@ purple_coresources = \
 	privacy.c \
 	proxy.c \
 	prpl.c \
+	purple-socket.c \
 	request.c \
 	roomlist.c \
 	savedstatuses.c \
@@ -141,6 +142,7 @@ purple_coreheaders = \
 	privacy.h \
 	proxy.h \
 	prpl.h \
+	purple-socket.h \
 	request.h \
 	roomlist.h \
 	savedstatuses.h \
diff --git a/libpurple/Makefile.mingw b/libpurple/Makefile.mingw
--- a/libpurple/Makefile.mingw
+++ b/libpurple/Makefile.mingw
@@ -104,6 +104,7 @@ C_SRC =	\
 			privacy.c \
 			proxy.c \
 			prpl.c \
+			purple-socket.c \
 			request.c \
 			roomlist.c \
 			savedstatuses.c \
diff --git a/libpurple/purple-socket.c b/libpurple/purple-socket.c
new file mode 100644
--- /dev/null
+++ b/libpurple/purple-socket.c
@@ -0,0 +1,330 @@
+/**
+ * @file purple-socket.c Generic sockets
+ * @ingroup core
+ */
+
+/* purple
+ *
+ * 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
+ * source distribution.
+ *
+ * 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
+ */
+
+#include "purple-socket.h"
+
+#include "internal.h"
+
+#include "debug.h"
+#include "proxy.h"
+#include "sslconn.h"
+
+typedef enum {
+	PURPLE_SOCKET_STATE_DISCONNECTED = 0,
+	PURPLE_SOCKET_STATE_CONNECTING,
+	PURPLE_SOCKET_STATE_CONNECTED,
+	PURPLE_SOCKET_STATE_ERROR
+} PurpleSocketState;
+
+struct _PurpleSocket
+{
+	PurpleConnection *gc;
+	gchar *host;
+	int port;
+	gboolean is_tls;
+
+	PurpleSocketState state;
+
+	PurpleSslConnection *tls_connection;
+	PurpleProxyConnectData *raw_connection;
+	int fd;
+	guint inpa;
+
+	PurpleSocketConnectCb cb;
+	gpointer cb_data;
+};
+
+PurpleSocket *
+purple_socket_new(PurpleConnection *gc)
+{
+	PurpleSocket *ps = g_new0(PurpleSocket, 1);
+
+	ps->gc = gc;
+	ps->fd = -1;
+	ps->port = -1;
+
+	return ps;
+}
+
+PurpleConnection *
+purple_socket_get_connection(PurpleSocket *ps)
+{
+	g_return_val_if_fail(ps != NULL, NULL);
+
+	return ps->gc;
+}
+
+static gboolean
+purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state)
+{
+	g_return_val_if_fail(ps != NULL, FALSE);
+
+	if (ps->state == wanted_state)
+		return TRUE;
+
+	purple_debug_error("socket", "invalid state: %d (should be: %d)",
+		ps->state, wanted_state);
+	ps->state = PURPLE_SOCKET_STATE_ERROR;
+	return FALSE;
+}
+
+void
+purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls)
+{
+	g_return_if_fail(ps != NULL);
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+		return;
+
+	ps->is_tls = is_tls;
+}
+
+void
+purple_socket_set_host(PurpleSocket *ps, const gchar *host)
+{
+	g_return_if_fail(ps != NULL);
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+		return;
+
+	g_free(ps->host);
+	ps->host = g_strdup(host);
+}
+
+void
+purple_socket_set_port(PurpleSocket *ps, int port)
+{
+	g_return_if_fail(ps != NULL);
+	g_return_if_fail(port >= 0);
+	g_return_if_fail(port <= 65535);
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+		return;
+
+	ps->port = port;
+}
+
+static void
+_purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message)
+{
+	PurpleSocket *ps = _ps;
+
+	ps->raw_connection = NULL;
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
+		if (fd > 0)
+			close(fd);
+		ps->cb(ps, _("Invalid socket state"), ps->cb_data);
+		return;
+	}
+
+	if (fd <= 0 || error_message != NULL) {
+		if (error_message == NULL)
+			error_message = _("Unknown error");
+		ps->fd = -1;
+		ps->state = PURPLE_SOCKET_STATE_ERROR;
+		ps->cb(ps, error_message, ps->cb_data);
+		return;
+	}
+
+	ps->state = PURPLE_SOCKET_STATE_CONNECTED;
+	ps->fd = fd;
+	ps->cb(ps, NULL, ps->cb_data);
+}
+
+static void
+_purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection,
+	PurpleInputCondition cond)
+{
+	PurpleSocket *ps = _ps;
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
+		purple_ssl_close(tls_connection);
+		ps->tls_connection = NULL;
+		ps->cb(ps, _("Invalid socket state"), ps->cb_data);
+		return;
+	}
+
+	if (ps->tls_connection->fd <= 0) {
+		ps->state = PURPLE_SOCKET_STATE_ERROR;
+		purple_ssl_close(tls_connection);
+		ps->tls_connection = NULL;
+		ps->cb(ps, _("Invalid file descriptor"), ps->cb_data);
+		return;
+	}
+
+	ps->state = PURPLE_SOCKET_STATE_CONNECTED;
+	ps->fd = ps->tls_connection->fd;
+	ps->cb(ps, NULL, ps->cb_data);
+}
+
+static void
+_purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection,
+	PurpleSslErrorType error, gpointer _ps)
+{
+	PurpleSocket *ps = _ps;
+
+	ps->state = PURPLE_SOCKET_STATE_ERROR;
+	ps->tls_connection = NULL;
+	ps->cb(ps, purple_ssl_strerror(error), ps->cb_data);
+}
+
+gboolean
+purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
+	gpointer user_data)
+{
+	PurpleAccount *account = NULL;
+
+	g_return_val_if_fail(ps != NULL, FALSE);
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
+		return FALSE;
+	ps->state = PURPLE_SOCKET_STATE_CONNECTING;
+
+	if (ps->host == NULL || ps->port < 0) {
+		purple_debug_error("socket", "Host or port is not specified");
+		ps->state = PURPLE_SOCKET_STATE_ERROR;
+		return FALSE;
+	}
+
+	if (ps->gc != NULL)
+		account = purple_connection_get_account(ps->gc);
+
+	ps->cb = cb;
+	ps->cb_data = user_data;
+
+	if (ps->is_tls) {
+		if (!purple_ssl_is_supported()) {
+			purple_debug_error("socket", "TLS is not supported");
+			ps->state = PURPLE_SOCKET_STATE_ERROR;
+			return FALSE;
+		}
+
+		ps->tls_connection = purple_ssl_connect(account, ps->host,
+			ps->port, _purple_socket_connected_tls,
+			_purple_socket_connected_tls_error, ps);
+	} else {
+		ps->raw_connection = purple_proxy_connect(ps->gc, account,
+			ps->host, ps->port, _purple_socket_connected_raw, ps);
+	}
+
+	if (ps->tls_connection == NULL &&
+		ps->raw_connection == NULL)
+	{
+		ps->state = PURPLE_SOCKET_STATE_ERROR;
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+gssize
+purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
+{
+	g_return_val_if_fail(ps != NULL, -1);
+	g_return_val_if_fail(buf != NULL, -1);
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
+		return -1;
+
+	if (ps->is_tls)
+		return purple_ssl_read(ps->tls_connection, buf, len);
+	else
+		return read(ps->fd, buf, len);
+}
+
+gssize
+purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len)
+{
+	g_return_val_if_fail(ps != NULL, -1);
+	g_return_val_if_fail(buf != NULL, -1);
+
+	if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))



More information about the Commits mailing list