Fix bug #3139, "Mainwindow unresponsive due to a busy loop"
[claws.git] / src / common / socket.c
index 997ac4f1300cf3538a513eeabc5328a94c4e4603..1d6638d07afe6882493786a3e9a1b2c0c40c4947 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
+ * Copyright (C) 1999-2012 Hiroyuki Yamamoto and the Claws Mail team
  *
  * 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
@@ -19,6 +19,7 @@
 
 #ifdef HAVE_CONFIG_H
 #  include "config.h"
+#include "claws-features.h"
 #endif
 
 #if (defined (_XOPEN_SOURCE) && !defined (_BSD_SOURCE))
@@ -129,19 +130,21 @@ static guint io_timeout = 60;
 
 static GList *sock_connect_data_list = NULL;
 
-static gboolean sock_prepare           (GSource        *source,
+static gboolean ssl_sock_prepare       (GSource        *source,
                                         gint           *timeout);
-static gboolean sock_check             (GSource        *source);
-static gboolean sock_dispatch          (GSource        *source,
+static gboolean ssl_sock_check         (GSource        *source);
+static gboolean ssl_sock_dispatch      (GSource        *source,
                                         GSourceFunc     callback,
                                         gpointer        user_data);
 
-GSourceFuncs sock_watch_funcs = {
-       sock_prepare,
-       sock_check,
-       sock_dispatch,
+#ifdef USE_GNUTLS
+GSourceFuncs ssl_watch_funcs = {
+       ssl_sock_prepare,
+       ssl_sock_check,
+       ssl_sock_dispatch,
        NULL
 };
+#endif
 
 static gint sock_connect_with_timeout  (gint                    sock,
                                         const struct sockaddr  *serv_addr,
@@ -427,7 +430,7 @@ static gint set_nonblocking_mode(gint fd, gboolean nonblock)
 
 gint sock_set_nonblocking_mode(SockInfo *sock, gboolean nonblock)
 {
-       g_return_val_if_fail(sock != NULL, -1);
+       cm_return_val_if_fail(sock != NULL, -1);
 
        return set_nonblocking_mode(sock->sock, nonblock);
 }
@@ -451,28 +454,35 @@ static gboolean is_nonblocking_mode(gint fd)
 
 gboolean sock_is_nonblocking_mode(SockInfo *sock)
 {
-       g_return_val_if_fail(sock != NULL, FALSE);
+       cm_return_val_if_fail(sock != NULL, FALSE);
 
        return is_nonblocking_mode(sock->sock);
 }
 
 
-static gboolean sock_prepare(GSource *source, gint *timeout)
+#ifdef USE_GNUTLS
+static gboolean ssl_sock_prepare(GSource *source, gint *timeout)
 {
        *timeout = 1;
        return FALSE;
 }
 
-static gboolean sock_check(GSource *source)
+static gboolean ssl_sock_check(GSource *source)
 {
        SockInfo *sock = ((SockSource *)source)->sock;
        struct timeval timeout = {0, 0};
        fd_set fds;
-       GIOCondition condition = sock->condition;
+       GIOCondition condition = 0;
         
        if (!sock || !sock->sock)
                return FALSE;
 
+       condition = sock->condition;
+
+       if ((condition & G_IO_IN) == G_IO_IN &&
+           gnutls_record_check_pending(sock->ssl) != 0)
+               return TRUE;
+
        FD_ZERO(&fds);
        FD_SET(sock->sock, &fds);
 
@@ -484,7 +494,7 @@ static gboolean sock_check(GSource *source)
        return FD_ISSET(sock->sock, &fds) != 0;
 }
 
-static gboolean sock_dispatch(GSource *source, GSourceFunc callback,
+static gboolean ssl_sock_dispatch(GSource *source, GSourceFunc callback,
                              gpointer user_data)
 {
        SockInfo *sock = ((SockSource *)source)->sock;
@@ -494,6 +504,7 @@ static gboolean sock_dispatch(GSource *source, GSourceFunc callback,
 
        return sock->callback(sock, sock->condition, sock->data);
 }
+#endif
 
 static gboolean sock_watch_cb(GIOChannel *source, GIOCondition condition,
                              gpointer data)
@@ -519,7 +530,7 @@ guint sock_add_watch(SockInfo *sock, GIOCondition condition, SockFunc func,
 #ifdef USE_GNUTLS
        if (sock->ssl)
        {
-               GSource *source = g_source_new(&sock_watch_funcs,
+               GSource *source = g_source_new(&ssl_watch_funcs,
                                               sizeof(SockSource));
                ((SockSource *) source)->sock = sock;
                g_source_set_priority(source, G_PRIORITY_DEFAULT);
@@ -559,6 +570,7 @@ static gint fd_check_io(gint fd, GIOCondition cond)
                return 0;
        } else {
                g_warning("Socket IO timeout\n");
+               log_error(LOG_PROTOCOL, _("Socket IO timeout.\n"));
                return -1;
        }
 }
@@ -587,6 +599,7 @@ static gint sock_connect_with_timeout(gint sock,
                alarm(0);
                signal(SIGALRM, prev_handler);
                errno = ETIMEDOUT;
+               log_error(LOG_PROTOCOL, _("Connection timed out.\n"));
                return -1;
        }
        alarm(timeout_secs);
@@ -614,6 +627,7 @@ struct hostent *my_gethostbyname(const gchar *hostname)
                alarm(0);
                signal(SIGALRM, prev_handler);
                g_printerr("%s: host lookup timed out.\n", hostname);
+               log_error(LOG_PROTOCOL, _("%s: host lookup timed out.\n"), hostname);
                errno = 0;
                return NULL;
        }
@@ -626,6 +640,7 @@ struct hostent *my_gethostbyname(const gchar *hostname)
                signal(SIGALRM, prev_handler);
 #endif
                g_printerr("%s: unknown host.\n", hostname);
+               log_error(LOG_PROTOCOL, _("%s: unknown host.\n"), hostname);
                errno = 0;
                return NULL;
        }
@@ -813,6 +828,9 @@ static gboolean sock_connect_async_cb(GIOChannel *source,
 
        if (val != 0) {
                close(fd);
+               log_error(LOG_PROTOCOL, _("%s:%d: connection failed (%s).\n"),
+                         conn_data->hostname, conn_data->port,
+                         strerror(val));
                sock_connect_address_list_async(conn_data);
                return FALSE;
        }
@@ -937,6 +955,7 @@ static gint sock_connect_address_list_async(SockConnectData *conn_data)
                if ((sock = socket(addr_data->family, addr_data->socktype,
                                   addr_data->protocol)) < 0) {
                        perror("socket");
+
                        continue;
                }
 
@@ -949,14 +968,12 @@ static gint sock_connect_address_list_async(SockConnectData *conn_data)
                                perror("connect");
                                close(sock);
                        }
-               } else
+               } else {
                        break;
+               }
        }
 
        if (conn_data->cur_addr == NULL) {
-               g_warning("sock_connect_address_list_async: "
-                         "connection to %s:%d failed\n",
-                         conn_data->hostname, conn_data->port);
                conn_data->func(NULL, conn_data->data);
                sock_connect_async_cancel(conn_data->id);
                return -1;
@@ -1051,6 +1068,8 @@ static gboolean sock_get_address_info_async_cb(GIOChannel *source,
 
                if (ai_member[0] == AF_UNSPEC) {
                        g_warning("DNS lookup failed\n");
+                       log_error(LOG_PROTOCOL, _("%s:%d: unknown host.\n"),
+                               lookup_data->hostname, lookup_data->port);
                        break;
                }
 
@@ -1144,6 +1163,8 @@ static void address_info_async_child(void *opaque)
                gchar len = 0;
                 g_warning("getaddrinfo for %s:%s failed: %s\n",
                           parm->hostname, port_str, gai_strerror(gai_err));
+               log_error(LOG_PROTOCOL, _("%s:%s: host lookup failed (%s).\n"),
+                         parm->hostname, port_str, gai_strerror(gai_err));
                fd_write_all(parm->pipe_fds[1], &len,
                      sizeof(len));
                 fd_write_all(parm->pipe_fds[1], (gchar *)ai_member,
@@ -1367,7 +1388,7 @@ static gint fd_read(gint fd, gchar *buf, gint len)
 }
 
 #if USE_GNUTLS
-static gint ssl_read(gnutls_session ssl, gchar *buf, gint len)
+static gint ssl_read(gnutls_session_t ssl, gchar *buf, gint len)
 {
        gint r;
 
@@ -1396,6 +1417,8 @@ static gint ssl_read(gnutls_session ssl, gchar *buf, gint len)
                        return -1;
 
                default:
+                       debug_print("Unexpected SSL read result %d\n", r);
+                       errno = EIO;
                        return -1;
                }
        }
@@ -1407,7 +1430,7 @@ gint sock_read(SockInfo *sock, gchar *buf, gint len)
 {
        gint ret;
 
-       g_return_val_if_fail(sock != NULL, -1);
+       cm_return_val_if_fail(sock != NULL, -1);
 
 #ifdef USE_GNUTLS
        if (sock->ssl)
@@ -1432,7 +1455,7 @@ gint fd_write(gint fd, const gchar *buf, gint len)
 }
 
 #if USE_GNUTLS
-static gint ssl_write(gnutls_session ssl, const gchar *buf, gint len)
+static gint ssl_write(gnutls_session_t ssl, const gchar *buf, gint len)
 {
        gint ret;
 
@@ -1459,7 +1482,7 @@ gint sock_write(SockInfo *sock, const gchar *buf, gint len)
 {
        gint ret;
 
-       g_return_val_if_fail(sock != NULL, -1);
+       cm_return_val_if_fail(sock != NULL, -1);
 
 #ifdef USE_GNUTLS
        if (sock->ssl)
@@ -1501,7 +1524,7 @@ gint fd_write_all(gint fd, const gchar *buf, gint len)
 }
 
 #ifdef USE_GNUTLS
-static gint ssl_write_all(gnutls_session ssl, const gchar *buf, gint len)
+static gint ssl_write_all(gnutls_session_t ssl, const gchar *buf, gint len)
 {
        gint n, wrlen = 0;
 
@@ -1522,7 +1545,7 @@ gint sock_write_all(SockInfo *sock, const gchar *buf, gint len)
 {
        gint ret;
 
-       g_return_val_if_fail(sock != NULL, -1);
+       cm_return_val_if_fail(sock != NULL, -1);
 
 #ifdef USE_GNUTLS
        if (sock->ssl)
@@ -1553,6 +1576,7 @@ gint fd_gets(gint fd, gchar *buf, gint len)
                return -1;
 
 #ifdef G_OS_WIN32
+       fd_check_io(fd, G_IO_IN);
        do {
 /*
 XXX:tm try nonblock