changeset 15681:14d85ee22d78

Added a new GaimEventLoopUiOps item, input_get_error(). This function allows the UI to return the current error status on a socket/input. If the UI does not implement it (as Pidgin and gntgaim do not, since glib's handling of sockets is sane), it is just a wrapper around getsockopt(). Implemented or not, its return values should match those of getsockopt() with a level of SOL_SOCKET and an option of SO_ERROR. For curious souls, Adium will be using this to provide a working version of getsockopt(); the CoreFoundation CFSocket class which is used for socket read/write calls getsockopt() with SO_ERROR itself, thereby clearing the error flag [as documented in getsockopt()'s behavior], so depending upon it for determining if an error occurred leads to significant misbehavior.
author Evan Schoenberg <evan.s@dreskin.net>
date Fri, 23 Feb 2007 05:17:20 +0000
parents 868c9d2cb818
children f99e06ae143e 70e4796455ba
files console/gntgaim.c libpurple/eventloop.c libpurple/eventloop.h libpurple/proxy.c pidgin/gtkeventloop.c
diffstat 5 files changed, 69 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/console/gntgaim.c	Fri Feb 23 05:12:54 2007 +0000
+++ b/console/gntgaim.c	Fri Feb 23 05:17:20 2007 +0000
@@ -148,7 +148,8 @@
 	g_timeout_add,
 	g_source_remove,
 	gnt_input_add,
-	g_source_remove
+	g_source_remove,
+	NULL /* input_get_error */
 };
 
 static GaimEventLoopUiOps *
--- a/libpurple/eventloop.c	Fri Feb 23 05:12:54 2007 +0000
+++ b/libpurple/eventloop.c	Fri Feb 23 05:17:20 2007 +0000
@@ -23,6 +23,7 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 #include "eventloop.h"
+#include "internal.h"
 
 static GaimEventLoopUiOps *eventloop_ui_ops = NULL;
 
@@ -58,6 +59,27 @@
 	return ops->input_remove(tag);
 }
 
+int
+gaim_input_get_error(int fd, int *error)
+{
+	GaimEventLoopUiOps *ops = gaim_eventloop_get_ui_ops();
+
+	if (ops->input_get_error)
+	{
+		errno = 0;
+		int ret = ops->input_get_error(fd, error);
+		errno = *error;
+		return ret;
+	}
+	else
+	{
+		socklen_t len;
+		len = sizeof(*error);
+
+		return getsockopt(fd, SOL_SOCKET, SO_ERROR, error, &len);
+	}
+}
+
 void
 gaim_eventloop_set_ui_ops(GaimEventLoopUiOps *ops)
 {
--- a/libpurple/eventloop.h	Fri Feb 23 05:12:54 2007 +0000
+++ b/libpurple/eventloop.h	Fri Feb 23 05:17:20 2007 +0000
@@ -71,6 +71,16 @@
 	 * @see gaim_input_remove, g_source_remove
 	 */
 	gboolean (*input_remove)(guint handle);
+	
+	
+	/**
+	 * Get the current error status for an input.
+	 * Implementation of this UI op is optional. Implement it if the UI's sockets
+	 * or event loop needs to customize determination of socket error status.
+	 * @see gaim_input_get_error, getsockopt
+	 */
+	int (*input_get_error)(int fd, int *error);
+
 };
 
 /**************************************************************************/
@@ -119,7 +129,22 @@
  * @param handle The handle of the input handler. Note that this is the return
  * value from gaim_input_add, <i>not</i> the file descriptor.
  */
-gboolean gaim_input_remove(guint handle);
+guint gaim_input_remove(guint handle);
+
+/**
+ * Get the current error status for an input.
+ * The return value and error follow getsockopt() with a level of SOL_SOCKET and an
+ * option name of SO_ERROR, and this is how the error is determined if the UI does not
+ * implement the input_get_error UI op.
+ *
+ * @param fd        The input file descriptor.
+ * @param errno		A pointer to an int which on return will have the error, or 0 if no error.
+ *
+ * @return 0 if there is no error; -1 if there is an error, in which case errno will be set.
+ */
+int
+gaim_input_get_error(int fd, int *errno);
+
 
 /*@}*/
 
--- a/libpurple/proxy.c	Fri Feb 23 05:12:54 2007 +0000
+++ b/libpurple/proxy.c	Fri Feb 23 05:17:20 2007 +0000
@@ -386,14 +386,14 @@
 socket_ready_cb(gpointer data, gint source, GaimInputCondition cond)
 {
 	GaimProxyConnectData *connect_data = data;
-	socklen_t len;
 	int error = 0;
 	int ret;
 
-	gaim_debug_info("proxy", "Connected.\n");
+	gaim_debug_info("proxy", "Connected to %s:%d.\n",
+					connect_data->host, connect_data->port);
 
 	/*
-	 * getsockopt after a non-blocking connect returns -1 if something is
+	 * gaim_input_get_error after a non-blocking connect returns -1 if something is
 	 * really messed up (bad descriptor, usually). Otherwise, it returns 0 and
 	 * error holds what connect would have returned if it blocked until now.
 	 * Thus, error == 0 is success, error == EINPROGRESS means "try again",
@@ -403,17 +403,21 @@
 	 * be overly optimistic sometimes. select is just a hint that you might be
 	 * able to do something.)
 	 */
-	len = sizeof(error);
-	ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+	ret = gaim_input_get_error(connect_data->fd, &error);
 
-	if (ret == 0 && error == EINPROGRESS)
+	if (ret == 0 && error == EINPROGRESS) {
 		/* No worries - we'll be called again later */
 		/* TODO: Does this ever happen? */
+		gaim_debug_info("proxy", "(ret == 0 && error == EINPROGRESS)");
 		return;
+	}
 
 	if (ret != 0 || error != 0) {
 		if (ret != 0)
 			error = errno;
+		gaim_debug_info("proxy", "Error connecting to %s:%d (%s).\n",
+						connect_data->host, connect_data->port, strerror(error));
+
 		gaim_proxy_connect_data_disconnect(connect_data, strerror(error));
 		return;
 	}
@@ -466,14 +470,12 @@
 		/*
 		 * The connection happened IMMEDIATELY... strange, but whatever.
 		 */
-		socklen_t len;
 		int error = ETIMEDOUT;
 		int ret;
 
 		gaim_debug_info("proxy", "Connected immediately.\n");
 
-		len = sizeof(error);
-		ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+		ret = gaim_input_get_error(connect_data->fd, &error);
 		if ((ret != 0) || (error != 0))
 		{
 			if (ret != 0)
@@ -783,13 +785,13 @@
 {
 	GString *request;
 	GaimProxyConnectData *connect_data;
-	socklen_t len;
 	int error = ETIMEDOUT;
 	int ret;
 
-	gaim_debug_info("proxy", "Connected.\n");
+	connect_data = data;
 
-	connect_data = data;
+	gaim_debug_info("proxy", "Connected to %s:%d.\n",
+		connect_data->host, connect_data->port);
 
 	if (connect_data->inpa > 0)
 	{
@@ -797,8 +799,7 @@
 		connect_data->inpa = 0;
 	}
 
-	len = sizeof(error);
-	ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+	ret = gaim_input_get_error(connect_data->fd, &error);
 	if ((ret != 0) || (error != 0))
 	{
 		if (ret != 0)
@@ -947,7 +948,6 @@
 	unsigned char packet[9];
 	struct hostent *hp;
 	GaimProxyConnectData *connect_data = data;
-	socklen_t len;
 	int error = ETIMEDOUT;
 	int ret;
 
@@ -959,8 +959,7 @@
 		connect_data->inpa = 0;
 	}
 
-	len = sizeof(error);
-	ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+	ret = gaim_input_get_error(connect_data->fd, &error);
 	if ((ret != 0) || (error != 0))
 	{
 		if (ret != 0)
@@ -1515,7 +1514,6 @@
 	unsigned char buf[5];
 	int i;
 	GaimProxyConnectData *connect_data = data;
-	socklen_t len;
 	int error = ETIMEDOUT;
 	int ret;
 
@@ -1527,8 +1525,7 @@
 		connect_data->inpa = 0;
 	}
 
-	len = sizeof(error);
-	ret = getsockopt(connect_data->fd, SOL_SOCKET, SO_ERROR, &error, &len);
+	ret = gaim_input_get_error(connect_data->fd, &error);
 	if ((ret != 0) || (error != 0))
 	{
 		if (ret != 0)
--- a/pidgin/gtkeventloop.c	Fri Feb 23 05:12:54 2007 +0000
+++ b/pidgin/gtkeventloop.c	Fri Feb 23 05:17:20 2007 +0000
@@ -118,7 +118,8 @@
 	g_timeout_add,
 	g_source_remove,
 	pidgin_input_add,
-	g_source_remove
+	g_source_remove,
+	NULL /* input_get_error */
 };
 
 GaimEventLoopUiOps *