sync with 0.9.2cvs5
authorPaul Mangan <paul@claws-mail.org>
Thu, 26 Jun 2003 09:03:12 +0000 (09:03 +0000)
committerPaul Mangan <paul@claws-mail.org>
Thu, 26 Jun 2003 09:03:12 +0000 (09:03 +0000)
13 files changed:
ChangeLog
ChangeLog.claws
ChangeLog.jp
configure.ac
src/common/nntp.c
src/common/session.c
src/common/session.h
src/common/smtp.c
src/common/socket.c
src/common/socket.h
src/inc.c
src/pop.c
src/send_message.c

index b57415a..ca5421e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2003-06-26
+
+       * src/session.c: added missing #include <errno.h>.
+
+2003-06-25
+
+       * src/session.[ch]: reimplemented the Session system with non-blocking
+         I/O to solve the performance and the code design issue.
+       * src/socket.[ch]: added sock_connect_async() and
+         sock_connect_async_cancel() to enable asynchronous connection.
+         sock_peek(): modified the implementation.
+       * src/pop.c: optimized the receiving of the responses of
+         UIDL/LIST/RETR.
+
+2003-06-18
+
+       * src/procmsg.c: procmsg_get_thread_tree(): prevent threading problems
+         when a node is an ancestor (parent / great parent), which mostly
+         happens with circular references (thanks to Phillipe Gramoulle
+         for finding an sample set of messages, and Alfons for the patch).
+
 2003-06-16
 
        * src/pop3.c: pop3_get_uidl_table(): return empty hash table even if
          folders as the argument and returns their status.
          folder_count_total_msgs(): removed.
 
+2003-06-06
+
+       * version 0.9.2
+
 2003-06-06
 
        * src/filesel.c
index add496a..82910e3 100644 (file)
@@ -1,3 +1,8 @@
+2003-06-26 [paul]      0.9.0claws63
+
+       * sync with 0.9.2cvs5
+               see ChangeLog 2003-06-25 and 2003-06-26
+
 2003-06-25 [match]     0.9.0claws62
 
        * src/addr_compl.c
index eebea87..b043122 100644 (file)
@@ -1,3 +1,23 @@
+2003-06-26
+
+       * src/session.c: #include <errno.h> ¤¬Â­¤ê¤Ê¤«¤Ã¤¿¤Î¤ÇÄɲá£
+
+2003-06-25
+
+       * src/session.[ch]: À­Ç½¤È¥³¡¼¥É¥Ç¥¶¥¤¥ó¤ÎÌäÂê¤ò²ò·è¤¹¤ë¤¿¤á¤Ë
+         Session ¥·¥¹¥Æ¥à¤òÈó¥Ö¥í¥Ã¥­¥ó¥° I/O ¤ÇºÆ¼ÂÁõ¡£
+       * src/socket.[ch]: sock_connect_async() ¤È sock_connect_async_cancel()
+         ¤òÄɲä·¡¢ÈóƱ´üÀܳ¤ò²Äǽ¤Ë¤·¤¿¡£
+         sock_peek(): ¼ÂÁõ¤òÊѹ¹¡£
+       * src/pop.c: UIDL/LIST/RETR ¤Î±þÅú¤Î¼õ¿®¤òºÇŬ²½¡£
+
+2003-06-18
+
+       * src/procmsg.c: procmsg_get_thread_tree(): ¥Î¡¼¥É¤¬ÁÄÀè(parent /
+         great parent)¤Î¾ì¹ç(¼ç¤Ë½Û´Ä»²¾È¤Î¾ì¹ç¤Ëµ¯¤³¤ë) (¥á¥Ã¥»¡¼¥¸·²¤Î
+         ¥µ¥ó¥×¥ë¤òȯ¸«¤·¤¿ Phillipe Gramoulle ¤µ¤ó¤È¥Ñ¥Ã¥Á¤òÄ󶡤·¤¿
+         Alfons ¤µ¤ó thanks)¡£
+
 2003-06-16
 
        * src/pop.c: pop3_get_uidl_table(): UIDL ¥ê¥¹¥È¤¬¸«¤Ä¤«¤é¤Ê¤«¤Ã¤¿¾ì¹ç
          ¤È¤·¤Æ¼õ¤±¼è¤ê¡¢¤½¤ì¤é¤Î¥¹¥Æ¡¼¥¿¥¹¤òÊÖ¤¹¡£
          folder_count_total_msgs(): ºï½ü¡£
 
+2003-06-06
+
+       * version 0.9.2
+
 2003-06-06
 
        * src/filesel.c
index 86b2c1f..2c4e951 100644 (file)
@@ -11,7 +11,7 @@ MINOR_VERSION=9
 MICRO_VERSION=0
 INTERFACE_AGE=0
 BINARY_AGE=0
-EXTRA_VERSION=claws62
+EXTRA_VERSION=claws63
 VERSION=$MAJOR_VERSION.$MINOR_VERSION.$MICRO_VERSION$EXTRA_VERSION
 
 dnl set $target
index 3c25887..bd0023c 100644 (file)
@@ -83,6 +83,9 @@ Session *nntp_session_new(const gchar *server, gushort port, gchar *buf,
        }
 
        session = g_new0(NNTPSession, 1);
+
+       session_init(SESSION(session));
+
        SESSION(session)->type                  = SESSION_NEWS;
        SESSION(session)->server                = g_strdup(server);
        SESSION(session)->sock                  = sock;
index f92ddae..7ecc573 100644 (file)
 #include <sys/signal.h>
 #include <sys/wait.h>
 #include <sys/time.h>
+#include <errno.h>
 
 #include "session.h"
 #include "utils.h"
 
+static gint session_connect_cb         (SockInfo       *sock,
+                                        gpointer        data);
 static gint session_close              (Session        *session);
 
-static gchar *session_recv_msg         (Session        *session);
-
-static guchar *session_read_data       (Session        *session,
-                                        guint           size);
-
-static gint session_send_data_to_sock          (Session        *session,
-                                                const guchar   *data,
-                                                guint           size);
-static guchar *session_recv_data_from_sock     (Session        *session,
-                                                guint           size);
-
-static guchar *session_recv_data_from_sock_unescape    (Session *session,
-                                                        guint    size,
-                                                        guint   *actual_size);
-
-gboolean session_parent_input_cb       (GIOChannel     *source,
+static gboolean session_read_msg_cb    (GIOChannel     *source,
+                                        GIOCondition    condition,
+                                        gpointer        data);
+static gboolean session_read_data_cb   (GIOChannel     *source,
+                                        GIOCondition    condition,
+                                        gpointer        data);
+static gboolean session_write_msg_cb   (GIOChannel     *source,
+                                        GIOCondition    condition,
+                                        gpointer        data);
+static gboolean session_write_data_cb  (GIOChannel     *source,
                                         GIOCondition    condition,
                                         gpointer        data);
-
-gboolean session_child_input           (Session        *session);
 
 
-/*!
- *\brief       init session members to zero
- *
- *\param       session to be initialized
- */
 void session_init(Session *session)
 {
-       session->type = 0;
+       session->type = SESSION_UNKNOWN;
        session->sock = NULL;
-
        session->server = NULL;
        session->port = 0;
+#if USE_OPENSSL
+       session->ssl_type = SSL_NONE;
+#endif
        session->state = SESSION_READY;
        session->last_access_time = time(NULL);
-       session->data = NULL;
 
-       session->read_ch = NULL;
-       session->write_ch = NULL;
+       gettimeofday(&session->tv_prev, NULL);
+
+       session->conn_id = 0;
+
+       session->sock_ch = NULL;
+       session->io_tag = 0;
+
+       session->read_buf = g_string_sized_new(1024);
+       session->read_data_buf = g_byte_array_new();
+       session->write_buf = NULL;
+       session->write_buf_p = NULL;
+       session->write_buf_len = 0;
+
+       session->data = NULL;
 }
 
 /*!
@@ -96,90 +99,54 @@ void session_init(Session *session)
  */
 gint session_connect(Session *session, const gchar *server, gushort port)
 {
-       pid_t pid;
-       gint pipe_fds1[2], pipe_fds2[2];
-       SockInfo *sock;
-       gchar *str;
-
        session->server = g_strdup(server);
        session->port = port;
 
-       if (pipe(pipe_fds1) < 0) {
-               perror("pipe");
-               return -1;
-       }
-       if (pipe(pipe_fds2) < 0) {
-               perror("pipe");
-               close(pipe_fds1[0]);
-               close(pipe_fds1[1]);
-               return -1;
-       }
-
-       if ((pid = fork()) < 0) {
-               perror("fork");
+       session->conn_id = sock_connect_async(server, port, session_connect_cb,
+                                             session);
+       if (session->conn_id < 0) {
+               g_warning("can't connect to server.");
+               session_close(session);
                return -1;
        }
 
-       if (pid != 0) {
-               session->child_pid = pid;
-               session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
-               session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
-               close(pipe_fds1[0]);
-               close(pipe_fds2[1]);
-               session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
-                                                  session_parent_input_cb,
-                                                  session);
-               return 0;
-       }
-
-       /* child process */
+       return 0;
+}
 
-       session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
-       session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
-       close(pipe_fds1[1]);
-       close(pipe_fds2[0]);
+static gint session_connect_cb(SockInfo *sock, gpointer data)
+{
+       Session *session = SESSION(data);
 
-       debug_print("session: child: connecting to %s:%d ...\n", server, port);
+       session->conn_id = 0;
 
-       if ((sock = sock_connect(server, port)) == NULL) {
-               session_send_msg(session, SESSION_MSG_ERROR,
-                                "can't connect to server.");
-               session_close(session);
-               _exit(1);
+       if (!sock) {
+               g_warning("can't connect to server.");
+               session->state = SESSION_ERROR;
+               return -1;
        }
 
+       session->sock = sock;
+
 #if USE_OPENSSL
+       sock_set_nonblocking_mode(sock, FALSE);
        if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
-               session_send_msg(session, SESSION_MSG_ERROR,
-                                "can't initialize SSL.");
-               session_close(session);
-               _exit(1);
+               g_warning("can't initialize SSL.");
+               session->state = SESSION_ERROR;
+               return -1;
        }
 #endif
 
        debug_print("session: child: connected\n");
 
-       session->sock = sock;
-       session->state = SESSION_RECV;
-
-       if ((str = sock_getline(sock)) == NULL) {
-               session_send_msg(session, SESSION_MSG_ERROR,
-                                "can't get server response.");
-               session_close(session);
-               _exit(1);
-       }
-       strretchomp(str);
-       session_send_msg(session, SESSION_MSG_NORMAL, str);
-       g_free(str);
-
-       while (session_child_input(session) == TRUE)
-               ;
+       debug_print("session: connected\n");
 
-       session_close(session);
-
-       debug_print("session: child: disconnected\n");
+       session->state = SESSION_RECV;
+       session->sock_ch = g_io_channel_unix_new(sock->sock);
+       session->io_tag = g_io_add_watch(session->sock_ch, G_IO_IN,
+                                        session_read_msg_cb,
+                                        session);
 
-       _exit(0);
+       return 0;
 }
 
 /*!
@@ -191,9 +158,7 @@ gint session_connect(Session *session, const gchar *server, gushort port)
  */
 gint session_disconnect(Session *session)
 {
-       debug_print("session: %s: session_disconnect()\n",
-                   session->child_pid == 0 ? "child" : "parent");
-       session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
+       session_close(session);
        return 0;
 }
 
@@ -211,6 +176,10 @@ void session_destroy(Session *session)
        session_close(session);
        session->destroy(session);
        g_free(session->server);
+       g_string_free(session->read_buf, TRUE);
+       g_byte_array_free(session->read_data_buf, TRUE);
+       g_free(session->read_data_terminator);
+       g_free(session->write_buf);
        g_free(session);
 }
 
@@ -264,24 +233,21 @@ static gint session_close(Session *session)
 {
        g_return_val_if_fail(session != NULL, -1);
 
-       debug_print("session: %s: session_close()\n",
-                   session->child_pid == 0 ? "child" : "parent");
+       debug_print("session_close\n");
 
-       if (session->read_tag > 0) {
-               g_source_remove(session->read_tag);
-               session->read_tag = 0;
+       if (session->conn_id > 0) {
+               sock_connect_async_cancel(session->conn_id);
+               session->conn_id = 0;
        }
-       
-       if (session->read_ch) {
-               g_io_channel_close(session->read_ch);
-               g_io_channel_unref(session->read_ch);
-               session->read_ch = NULL;
+
+       if (session->io_tag > 0) {
+               g_source_remove(session->io_tag);
+               session->io_tag = 0;
        }
-       
-       if (session->write_ch) {
-               g_io_channel_close(session->write_ch);
-               g_io_channel_unref(session->write_ch);
-               session->write_ch = NULL;
+
+       if (session->sock_ch) {
+               g_io_channel_unref(session->sock_ch);
+               session->sock_ch = NULL;
        }
 
        if (session->sock) {
@@ -290,129 +256,68 @@ static gint session_close(Session *session)
                session->state = SESSION_DISCONNECTED;
        }
 
-       if (session->child_pid) {
-               if (session->state != SESSION_DISCONNECTED)
-                       kill(session->child_pid, SIGTERM);
-               waitpid(session->child_pid, NULL, 0);
-               session->child_pid = 0;
-       }
-
        return 0;
 }
 
-/*!
- *\brief       child and parent: send control message to other process
- *
- *\param       session Contains session information
- *             type Kind of data (commands or message data)
- *             msg Data
- *
- *\return       0 : success
- *             -1 : error
- */
-gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
+#if USE_OPENSSL
+gint session_start_tls(Session *session)
 {
-       gchar *prefix;
-       gchar *str;
-       gchar *cur;
-       guint size;
-       guint bytes_written;
-
-       switch (type) {
-       case SESSION_MSG_NORMAL:
-               prefix = "MESSAGE"; break;
-       case SESSION_MSG_SEND_DATA:
-               prefix = "SENDDATA"; break;
-       case SESSION_MSG_RECV_DATA:
-               prefix = "RECVDATA"; break;
-       case SESSION_MSG_CONTROL:
-               prefix = "CONTROL"; break;
-       case SESSION_MSG_ERROR:
-               prefix = "ERROR"; break;
-       default:
-               return -1;
-       }
+       gboolean nb_mode;
 
-       cur = str = g_strdup_printf("%s %s\n", prefix, msg);
-       size = strlen(str);
+       nb_mode = sock_is_nonblocking_mode(session->sock);
 
-       while (size > 0) {
-               if (g_io_channel_write(session->write_ch, cur, size,
-                                      &bytes_written)
-                   != G_IO_ERROR_NONE || bytes_written == 0) {
-                       g_warning("%s: sending message failed.\n",
-                                 session->child_pid == 0 ? "child" : "parent");
-                       g_free(str);
-                       return -1;
-               }
-               size -= bytes_written;
-               cur += bytes_written;
+       if (nb_mode)
+               sock_set_nonblocking_mode(session->sock, FALSE);
+
+       if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
+               g_warning("can't start TLS session.\n");
+               if (nb_mode)
+                       sock_set_nonblocking_mode(session->sock, TRUE);
+               return -1;
        }
 
-       g_free(str);
+       if (nb_mode)
+               sock_set_nonblocking_mode(session->sock, TRUE);
 
        return 0;
 }
+#endif
 
-/*!
- *\brief       child and parent receive function
- *
- *\param       session Contains session information
- *
- *\return      Message read by current session
- */
-static gchar *session_recv_msg(Session *session)
+gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
 {
-       gchar buf[BUFFSIZE];
-       gchar *str = NULL;
-       guint size = 1;
-       guint bytes_read;
-
-       for (;;) {
-               if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
-                                     &bytes_read)
-                   != G_IO_ERROR_NONE || bytes_read == 0) {
-                       g_warning("%s: receiving message failed.\n",
-                                 session->child_pid == 0 ? "child" : "parent");
-                       g_free(str);
-                       str = NULL;
-                       break;
-               }
+       gboolean ret;
 
-               size += bytes_read;
-               buf[bytes_read] = '\0';
+       g_return_val_if_fail(session->write_buf == NULL, -1);
+       g_return_val_if_fail(msg != NULL, -1);
+       g_return_val_if_fail(msg[0] != '\0', -1);
 
-               if (!str)
-                       str = g_strdup(buf);
-               else {
-                       str = g_realloc(str, size);
-                       strcat(str, buf);
-               }
-               if (str[size - 2] == '\n') {
-                       str[size - 2] = '\0';
-                       break;
-               }
-       }
+       session->state = SESSION_SEND;
+       session->write_buf = g_strconcat(msg, "\r\n", NULL);
+       session->write_buf_p = session->write_buf;
+       session->write_buf_len = strlen(msg) + 2;
 
-       return str;
+       ret = session_write_msg_cb(session->sock_ch, G_IO_OUT, session);
+
+       if (ret == TRUE)
+               session->io_tag = g_io_add_watch(session->sock_ch, G_IO_OUT,
+                                                session_write_msg_cb, session);
+       else if (session->state == SESSION_ERROR)
+               return -1;
+
+       return 0;
 }
 
-#if USE_OPENSSL
-gint session_start_tls(Session *session)
+gint session_recv_msg(Session *session)
 {
-       gchar *ctl_msg;
+       g_return_val_if_fail(session->read_buf->len == 0, -1);
 
-       session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
-       ctl_msg = session_recv_msg(session);
-       if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
-               g_free(ctl_msg);
-               return -1;
-       }
-       g_free(ctl_msg);
+       session->state = SESSION_RECV;
+
+       session->io_tag = g_io_add_watch(session->sock_ch, G_IO_IN,
+                                        session_read_msg_cb, session);
 
        return 0;
 }
-#endif
 
 /*!
  *\brief       parent (child?): send data to other process
@@ -426,540 +331,327 @@ gint session_start_tls(Session *session)
  */
 gint session_send_data(Session *session, const guchar *data, guint size)
 {
-       gchar *msg;
-       const guchar *cur = data;
-       guint bytes_written;
-       GIOError err;
+       gboolean ret;
 
-       session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
-       if ((msg = session_recv_msg(session)) == NULL)
-               return -1;
-       g_free(msg);
+       g_return_val_if_fail(session->write_buf == NULL, -1);
+       g_return_val_if_fail(data != NULL, -1);
+       g_return_val_if_fail(size != 0, -1);
 
-       while (size > 0) {
-               if ((err = g_io_channel_write(session->write_ch, (guchar *)cur,
-                                             size, &bytes_written))
-                   != G_IO_ERROR_NONE || bytes_written == 0) {
-                       g_warning("%s: sending data failed: %d\n",
-                                 session->child_pid == 0 ? "child" : "parent",
-                                 err);
-                       return -1;
-               }
-               size -= bytes_written;
-               cur += bytes_written;
-               debug_print("session: %s: sent %d bytes of data\n",
-                           session->child_pid == 0 ? "child" : "parent",
-                           bytes_written);
-       }
+       session->state = SESSION_SEND;
+
+       session->write_buf = g_malloc(size);
+       session->write_buf_p = session->write_buf;
+       memcpy(session->write_buf, data, size);
+       session->write_buf_len = size;
+       gettimeofday(&session->tv_prev, NULL);
+
+       ret = session_write_data_cb(session->sock_ch, G_IO_OUT, session);
+
+       if (ret == TRUE)
+               session->io_tag = g_io_add_watch(session->sock_ch, G_IO_OUT,
+                                                session_write_data_cb,
+                                                session);
+       else if (session->state == SESSION_ERROR)
+               return -1;
 
        return 0;
 }
 
-gint session_recv_data(Session *session, guint size, gboolean unescape_dot)
+gint session_recv_data(Session *session, guint size, const gchar *terminator)
 {
-       if (unescape_dot) {
-               gchar buf[BUFFSIZE];
+       g_return_val_if_fail(session->read_data_buf->len == 0, -1);
+
+       session->state = SESSION_RECV;
+
+       g_free(session->read_data_terminator);
+       session->read_data_terminator = g_strdup(terminator);
+       gettimeofday(&session->tv_prev, NULL);
+
+       session->io_tag = g_io_add_watch(session->sock_ch, G_IO_IN,
+                                        session_read_data_cb, session);
 
-               g_snprintf(buf, sizeof(buf), "%d UNESCAPE", size);
-               session_send_msg(session, SESSION_MSG_RECV_DATA, buf);
-       } else
-               session_send_msg(session, SESSION_MSG_RECV_DATA, itos(size));
        return 0;
 }
 
-/*!
- *\brief       child (parent?): read data from other process
- *
- *\param       session Contains session information
- *             size Bytes to read
- *
- *\return      data read from session
- */
-static guchar *session_read_data(Session *session, guint size)
+static gboolean session_read_msg_cb(GIOChannel *source, GIOCondition condition,
+                                   gpointer data)
 {
-       guchar *data;
-       guchar *cur;
-       guint bytes_read;
-       GIOError err;
-
-       cur = data = g_malloc(size);
-
-       while (size > 0) {
-               if ((err = g_io_channel_read(session->read_ch, cur, size,
-                                            &bytes_read))
-                   != G_IO_ERROR_NONE || bytes_read == 0) {
-                       g_warning("%s: reading data failed: %d\n",
-                                 session->child_pid == 0 ? "child" : "parent",
-                                 err);
-                       g_free(data);
-                       return NULL;
+       Session *session = SESSION(data);
+       gchar buf[SESSION_BUFFSIZE];
+       gint read_len;
+       gint to_read_len;
+       gchar *newline;
+       gchar *msg;
+       gint ret;
+
+       g_return_val_if_fail(condition == G_IO_IN, FALSE);
+
+       read_len = sock_peek(session->sock, buf, sizeof(buf) - 1);
+
+       if (read_len < 0) {
+               switch (errno) {
+               case EAGAIN:
+                       g_print("session_read_msg_cb: %s\n", g_strerror(errno));
+                       return TRUE;
+               default:
+                       g_warning("sock_peek: %s\n", g_strerror(errno));
+                       session->state = SESSION_ERROR;
+                       return FALSE;
                }
-               size -= bytes_read;
-               cur += bytes_read;
-               debug_print("session: %s: received %d bytes of data\n",
-                           session->child_pid == 0 ? "child" : "parent",
-                           bytes_read);
        }
 
-       return data;
-}
+       if ((newline = memchr(buf, '\n', read_len)) != NULL)
+               to_read_len = newline - buf + 1;
+       else
+               to_read_len = read_len;
+
+       read_len = sock_read(session->sock, buf, to_read_len);
+
+       /* this should always succeed */
+       if (read_len < 0) {
+               switch (errno) {
+               case EAGAIN:
+                       g_print("session_read_msg_cb: %s\n", g_strerror(errno));
+                       return TRUE;
+               default:
+                       g_warning("sock_read: %s\n", g_strerror(errno));
+                       session->state = SESSION_ERROR;
+                       return FALSE;
+               }
+       }
 
-#define MAX_CHUNK_SIZE 4096
+       buf[read_len] = '\0';
 
-/*!
- *\brief       child: Send session data to server
- *
- *\param       session Contains session information
- *             data Data to send to server
- *             size Bytes to send
- *
- *\return       0 : success
- *             -1 : error
- */
-static gint session_send_data_to_sock(Session *session, const guchar *data,
-                                     guint size)
-{
-       const guchar *cur = data;
-       gint bytes_written;
-       gint total_write_len = 0;
-       guint left = size;
-       gchar buf[BUFFSIZE];
-       gchar *msg;
-       struct timeval tv_prev, tv_cur;
-
-       gettimeofday(&tv_prev, NULL);
+       /* incomplete read */
+       if (read_len == 0 || buf[read_len - 1] != '\n') {
+               g_string_append(session->read_buf, buf);
+               return TRUE;
+       }
 
-       while (1) {
-               bytes_written = sock_write(session->sock, cur,
-                                          MIN(left, MAX_CHUNK_SIZE));
-               if (bytes_written <= 0)
-                       return -1;
-               left -= bytes_written;
-               cur += bytes_written;
-               total_write_len += bytes_written;
-               if (left == 0)
-                       break;
+       /* complete */
+       strretchomp(buf);
+       g_string_append(session->read_buf, buf);
 
-               gettimeofday(&tv_cur, NULL);
-               if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
-                   tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
-                       g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
-                                  total_write_len, size);
-                       session_send_msg(session, SESSION_MSG_CONTROL, buf);
-                       if ((msg = session_recv_msg(session)) == NULL)
-                               return -1;
-                       g_free(msg);
-                       gettimeofday(&tv_prev, NULL);
-               }
+       if (session->io_tag > 0) {
+               g_source_remove(session->io_tag);
+               session->io_tag = 0;
        }
 
-       return 0;
+       /* callback */
+       msg = g_strdup(session->read_buf->str);
+       g_string_truncate(session->read_buf, 0);
+
+       ret = session->recv_msg(session, msg);
+       session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
+
+       g_free(msg);
+
+       if (ret < 0)
+               session->state = SESSION_ERROR;
+
+       return FALSE;
 }
 
-/*!
- *\brief       child: Read answer/data from server
- *
- *\param       session Contains session information
- *             size Max bytes to receive
- *
- *\return      Server answer
- */
-static guchar *session_recv_data_from_sock(Session *session, guint size)
+static gboolean session_read_data_cb(GIOChannel        *source, GIOCondition condition,
+                                    gpointer data)
 {
-       guchar *data;
-       guchar *cur;
-       gint bytes_read;
-       gint total_read_len = 0;
-       guint left = size;
-       gchar buf[BUFFSIZE];
-       gchar *msg;
-       struct timeval tv_prev, tv_cur;
+       Session *session = SESSION(data);
+       gchar buf[SESSION_BUFFSIZE];
+       GByteArray *data_buf;
+       gint read_len;
+       gint terminator_len;
+       gboolean complete = FALSE;
+       gint ret;
 
-       gettimeofday(&tv_prev, NULL);
+       g_return_val_if_fail(condition == G_IO_IN, FALSE);
 
-       cur = data = g_malloc(size);
+       read_len = sock_read(session->sock, buf, sizeof(buf));
 
-       while (1) {
-               bytes_read = sock_read(session->sock, cur, left);
-               if (bytes_read <= 0) {
-                       g_free(data);
-                       return NULL;
+       if (read_len < 0) {
+               switch (errno) {
+               case EAGAIN:
+                       g_print("session_read_data_cb: %s\n", g_strerror(errno));
+                       return TRUE;
+               default:
+                       g_warning("sock_read: %s\n", g_strerror(errno));
+                       session->state = SESSION_ERROR;
+                       return FALSE;
                }
-               debug_print("session: child: "
-                           "received %d bytes of data from sock\n",
-                           bytes_read);
-               left -= bytes_read;
-               cur += bytes_read;
-               total_read_len += bytes_read;
-               if (left == 0)
-                       break;
+       }
+
+       data_buf = session->read_data_buf;
+
+       g_byte_array_append(data_buf, buf, read_len);
+       terminator_len = strlen(session->read_data_terminator);
+
+       /* check if data is terminated */
+       if (read_len > 0 && data_buf->len >= terminator_len) {
+               if (memcmp(data_buf->data, session->read_data_terminator,
+                          terminator_len) == 0)
+                       complete = TRUE;
+               else if (data_buf->len >= terminator_len + 2 &&
+                        memcmp(data_buf->data + data_buf->len -
+                               (terminator_len + 2), "\r\n", 2) == 0 &&
+                        memcmp(data_buf->data + data_buf->len -
+                               terminator_len, session->read_data_terminator,
+                               terminator_len) == 0)
+                       complete = TRUE;
+       }
+
+       /* incomplete read */
+       if (!complete) {
+               struct timeval tv_cur;
 
                gettimeofday(&tv_cur, NULL);
-               if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
-                   tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
-                       g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
-                                  total_read_len, size);
-                       session_send_msg(session, SESSION_MSG_CONTROL, buf);
-                       if ((msg = session_recv_msg(session)) == NULL) {
-                               g_free(data);
-                               return NULL;
-                       }
-                       g_free(msg);
-                       gettimeofday(&tv_prev, NULL);
+               if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
+                   tv_cur.tv_usec - session->tv_prev.tv_usec >
+                   UI_REFRESH_INTERVAL) {
+                       session->recv_data_progressive_notify
+                               (session, data_buf->len, 0,
+                                session->recv_data_progressive_notify_data);
+                       gettimeofday(&session->tv_prev, NULL);
                }
+               return TRUE;
        }
 
-       return data;
+       /* complete */
+       if (session->io_tag > 0) {
+               g_source_remove(session->io_tag);
+               session->io_tag = 0;
+       }
+
+       /* callback */
+       ret = session->recv_data_finished(session, data_buf->data,
+                                         data_buf->len - terminator_len);
+       session->recv_data_notify(session, data_buf->len - terminator_len,
+                                 session->recv_data_notify_data);
+
+       g_byte_array_set_size(data_buf, 0);
+
+       if (ret < 0)
+               session->state = SESSION_ERROR;
+
+       return FALSE;
 }
 
-static guchar *session_recv_data_from_sock_unescape(Session *session,
-                                                   guint size,
-                                                   guint *actual_size)
+static gint session_write_buf(Session *session)
 {
-       GString *data;
-       guchar *ret_data;
-       gint bytes_read;
-       gchar buf[BUFFSIZE];
-       gchar *msg;
-       struct timeval tv_prev, tv_cur;
+       gint write_len;
+       gint to_write_len;
 
-       gettimeofday(&tv_prev, NULL);
+       g_return_val_if_fail(session->write_buf != NULL, -1);
+       g_return_val_if_fail(session->write_buf_p != NULL, -1);
+       g_return_val_if_fail(session->write_buf_len > 0, -1);
 
-       data = g_string_sized_new(size + 1);
-       *actual_size = 0;
+       to_write_len = session->write_buf_len -
+               (session->write_buf_p - session->write_buf);
+       to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
 
-       while (1) {
-               bytes_read = sock_gets(session->sock, buf, sizeof(buf));
-               if (bytes_read <= 0) {
-                       g_string_free(data, TRUE);
-                       return NULL;
-               }
+       write_len = sock_write(session->sock, session->write_buf_p,
+                              to_write_len);
 
-               if (buf[0] == '.' && buf[1] == '\r' && buf[2] == '\n')
+       if (write_len < 0) {
+               switch (errno) {
+               case EAGAIN:
+                       g_print("session_write_cb: %s\n", g_strerror(errno));
+                       write_len = 0;
                        break;
-               if (buf[0] == '.' && buf[1] == '.')
-                       g_string_append(data, buf + 1);
-               else
-                       g_string_append(data, buf);
-
-               gettimeofday(&tv_cur, NULL);
-               if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
-                   tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
-                       g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
-                                  data->len, MAX(data->len, size));
-                       session_send_msg(session, SESSION_MSG_CONTROL, buf);
-                       if ((msg = session_recv_msg(session)) == NULL) {
-                               g_string_free(data, TRUE);
-                               return NULL;
-                       }
-                       g_free(msg);
-                       gettimeofday(&tv_prev, NULL);
+               default:
+                       g_warning("sock_write: %s\n", g_strerror(errno));
+                       session->state = SESSION_ERROR;
+                       return -1;
                }
        }
 
-       ret_data = data->str;
-       *actual_size = data->len;
-       g_string_free(data, FALSE);
+       /* incomplete write */
+       if (session->write_buf_p - session->write_buf + write_len <
+           session->write_buf_len) {
+               session->write_buf_p += write_len;
+               return 1;
+       }
 
-       return ret_data;
-}
+       g_free(session->write_buf);
+       session->write_buf = NULL;
+       session->write_buf_p = NULL;
+       session->write_buf_len = 0;
 
-/*!
- *\brief       Return if message is an internal command or server data
- *
- *\param       str Message to analyze
- *
- *\return      Type of message
- */
-static SessionMsgType session_get_msg_type(const gchar *str)
-{
-       if (!strncmp(str, "MESSAGE ", 8))
-               return SESSION_MSG_NORMAL;
-       else if (!strncmp(str, "SENDDATA ", 9))
-               return SESSION_MSG_SEND_DATA;
-       else if (!strncmp(str, "RECVDATA ", 9))
-               return SESSION_MSG_RECV_DATA;
-       else if (!strncmp(str, "CONTROL ", 8))
-               return SESSION_MSG_CONTROL;
-       else if (!strncmp(str, "ERROR ", 6))
-               return SESSION_MSG_ERROR;
-       else
-               return SESSION_MSG_UNKNOWN;
+       return 0;
 }
 
-/*!
- *\brief       parent: Received data from child
- *
- *\param       source Channel watching child pipe
- *             condition Unused (IN, HUP, OUT)
- *             data Contains session information
- *
- *\return      FALSE to remove watching channel
- */
-gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
-                                gpointer data)
+static gboolean session_write_msg_cb(GIOChannel *source, GIOCondition condition,
+                                    gpointer data)
 {
        Session *session = SESSION(data);
-       gchar *msg;
-       gchar *msg_data;
-       gint len;
-       gint total;
-       guchar *recv_data;
-       guint size;
        gint ret;
 
-       if ((msg = session_recv_msg(session)) == NULL) {
-               session->state = SESSION_ERROR;
-               return FALSE;
-       }
+       g_return_val_if_fail(condition == G_IO_OUT, FALSE);
+       g_return_val_if_fail(session->write_buf != NULL, FALSE);
+       g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
+       g_return_val_if_fail(session->write_buf_len > 0, FALSE);
 
-       switch (session_get_msg_type(msg)) {
-       case SESSION_MSG_NORMAL:
-               msg_data = msg + strlen("MESSAGE ");
-               ret = session->recv_msg(session, msg_data);
-               session->recv_msg_notify(session, msg_data,
-                                        session->recv_msg_notify_data);
-               if (ret > 0)
-                       session_send_msg(session, SESSION_MSG_CONTROL,
-                                        "CONTINUE");
-               else if (ret < 0) {
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               break;
-       case SESSION_MSG_SEND_DATA:
-               msg_data = msg + strlen("SENDDATA ");
-               size = atoi(msg_data);
-               session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
-               recv_data = session_read_data(session, size);
-               if (!recv_data) {
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               ret = session->recv_data_finished(session, recv_data, size);
-               g_free(recv_data);
-               session->recv_data_notify(session, size,
-                                         session->recv_data_notify_data);
-               if (ret > 0)
-                       session_send_msg(session, SESSION_MSG_CONTROL,
-                                        "CONTINUE");
-               else if (ret < 0) {
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               break;
-       case SESSION_MSG_RECV_DATA:
-               break;
-       case SESSION_MSG_CONTROL:
-               msg_data = msg + strlen("CONTROL ");
-               if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
-                       ret = sscanf(msg_data,
-                                    "DATARECVINPROG %d %d", &len, &total);
-                       if (ret != 2) {
-                               g_warning("wrong control message: %s\n", msg);
-                               session->state = SESSION_ERROR;
-                               g_free(msg);
-                               return FALSE;
-                       }
-                       session_send_msg(session, SESSION_MSG_CONTROL,
-                                        "CONTINUE");
-                       session->recv_data_progressive_notify
-                               (session, len, total,
-                                session->recv_data_progressive_notify_data);
-               } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
-                       ret = sscanf(msg_data,
-                                    "DATASENDINPROG %d %d", &len, &total);
-                       if (ret != 2) {
-                               g_warning("wrong control message: %s\n", msg);
-                               session->state = SESSION_ERROR;
-                               g_free(msg);
-                               return FALSE;
-                       }
-                       session_send_msg(session, SESSION_MSG_CONTROL,
-                                        "CONTINUE");
-                       session->send_data_progressive_notify
-                               (session, len, total,
-                                session->send_data_progressive_notify_data);
-               } else if (!strncmp(msg_data, "DATASENT ", 9)) {
-                       len = atoi(msg_data + 9);
-                       ret = session->send_data_finished(session, len);
-                       session->send_data_notify
-                               (session, len, session->send_data_notify_data);
-               } else if (!strcmp(msg_data, "DISCONNECTED")) {
-                       session->state = SESSION_DISCONNECTED;
-                       g_free(msg);
-                       return FALSE;
-               } else {
-                       g_warning("wrong control message: %s\n", msg);
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               break;
-       case SESSION_MSG_ERROR:
-       default:
-               g_warning("error from child: %s\n", msg + strlen("ERROR "));
+       ret = session_write_buf(session);
+
+       if (ret < 0) {
                session->state = SESSION_ERROR;
-               g_free(msg);
                return FALSE;
+       } else if (ret > 0)
+               return TRUE;
+
+       if (session->io_tag > 0) {
+               g_source_remove(session->io_tag);
+               session->io_tag = 0;
        }
 
-       g_free(msg);
-       return TRUE;
+       session_recv_msg(session);
+
+       return FALSE;
 }
 
-/*!
- *\brief       child: Receive control message from parent,
- *             transfer data from/to server
- *
- *\param       session Contains session information
- *
- *\return      TRUE if more data is available
- */
-gboolean session_child_input(Session *session)
+static gboolean session_write_data_cb(GIOChannel *source,
+                                     GIOCondition condition, gpointer data)
 {
-       gchar buf[BUFFSIZE];
-       gchar *msg;
-       gchar *msg_data;
-       gchar *str;
-       guchar *send_data;
-       guchar *recv_data;
-       guint size;
-       guint actual_size;
-
-       if ((msg = session_recv_msg(session)) == NULL) {
-               session_send_msg(session, SESSION_MSG_ERROR,
-                                "receiving message failed.");
+       Session *session = SESSION(data);
+       guint write_buf_len;
+       gint ret;
+
+       g_return_val_if_fail(condition == G_IO_OUT, FALSE);
+       g_return_val_if_fail(session->write_buf != NULL, FALSE);
+       g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
+       g_return_val_if_fail(session->write_buf_len > 0, FALSE);
+
+       write_buf_len = session->write_buf_len;
+
+       ret = session_write_buf(session);
+
+       if (ret < 0) {
                session->state = SESSION_ERROR;
                return FALSE;
-       }
+       } else if (ret > 0) {
+               struct timeval tv_cur;
 
-       switch (session_get_msg_type(msg)) {
-       case SESSION_MSG_NORMAL:
-               msg_data = msg + strlen("MESSAGE ");
-               session->state = SESSION_SEND;
-               sock_puts(session->sock, msg_data);
-               session->state = SESSION_RECV;
-               str = sock_getline(session->sock);
-               if (!str) {
-                       session_send_msg(session, SESSION_MSG_ERROR,
-                                        "receiving message failed.");
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               strretchomp(str);
-               session_send_msg(session, SESSION_MSG_NORMAL, str);
-               g_free(str);
-               break;
-       case SESSION_MSG_SEND_DATA:
-               msg_data = msg + strlen("SENDDATA ");
-               size = atoi(msg_data);
-               session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
-               send_data = session_read_data(session, size);
-               if (!send_data) {
-                       session_send_msg(session, SESSION_MSG_ERROR,
-                                        "sending data failed.");
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               session->state = SESSION_SEND;
-               if (session_send_data_to_sock(session, send_data, size) < 0) {
-                       session_send_msg(session, SESSION_MSG_ERROR,
-                                        "sending data failed.");
-                       session->state = SESSION_ERROR;
-                       g_free(send_data);
-                       g_free(msg);
-                       return FALSE;
-               }
-               g_free(send_data);
-               g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
-               session_send_msg(session, SESSION_MSG_CONTROL, buf);
-               break;
-       case SESSION_MSG_RECV_DATA:
-               msg_data = msg + strlen("RECVDATA ");
-               size = atoi(msg_data);
-               session->state = SESSION_RECV;
-               if (strstr(msg_data, "UNESCAPE") != NULL) {
-                       recv_data = session_recv_data_from_sock_unescape
-                               (session, size, &actual_size);
-                       size = actual_size;
-               } else
-                       recv_data = session_recv_data_from_sock(session, size);
-               if (!recv_data) {
-                       session_send_msg(session, SESSION_MSG_ERROR,
-                                        "receiving data failed.");
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
-               }
-               if (session_send_data(session, recv_data, size) < 0) {
-                       session->state = SESSION_ERROR;
-                       g_free(recv_data);
-                       g_free(msg);
-                       return FALSE;
-               }
-               g_free(recv_data);
-               break;
-       case SESSION_MSG_CONTROL:
-               msg_data = msg + strlen("CONTROL ");
-               if (!strcmp(msg_data, "CONTINUE")) {
-                       session->state = SESSION_RECV;
-                       str = sock_getline(session->sock);
-                       if (!str) {
-                               session_send_msg(session, SESSION_MSG_ERROR,
-                                                "receiving message failed.");
-                               session->state = SESSION_ERROR;
-                               g_free(msg);
-                               return FALSE;
-                       }
-                       strretchomp(str);
-                       session_send_msg(session, SESSION_MSG_NORMAL, str);
-                       g_free(str);
-                       break;
-#if USE_OPENSSL
-               } else if (!strcmp(msg_data, "STARTTLS")) {
-                       if (!ssl_init_socket_with_method(session->sock,
-                                                        SSL_METHOD_TLSv1)) {
-                               session_send_msg(session, SESSION_MSG_ERROR,
-                                                "can't start TLS session.");
-                               session->state = SESSION_ERROR;
-                               g_free(msg);
-                               return FALSE;
-                       }
-                       session_send_msg(session, SESSION_MSG_CONTROL,
-                                        "STARTTLSOK");
-                       break;
-#endif
-               } else if (!strcmp(msg_data, "DISCONNECT")) {
-                       sock_close(session->sock);
-                       session->sock = NULL;
-                       session->state = SESSION_DISCONNECTED;
-                       session_send_msg(session, SESSION_MSG_CONTROL,
-                                        "DISCONNECTED");
-                       g_free(msg);
-                       return FALSE;
-               } else {
-                       session_send_msg(session, SESSION_MSG_ERROR,
-                                        "wrong control message.");
-                       session->state = SESSION_ERROR;
-                       g_free(msg);
-                       return FALSE;
+               gettimeofday(&tv_cur, NULL);
+               if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
+                   tv_cur.tv_usec - session->tv_prev.tv_usec >
+                   UI_REFRESH_INTERVAL) {
+                       session->send_data_progressive_notify
+                               (session,
+                                session->write_buf_p - session->write_buf,
+                                write_buf_len,
+                                session->send_data_progressive_notify_data);
+                       gettimeofday(&session->tv_prev, NULL);
                }
-               break;
-       case SESSION_MSG_ERROR:
-       default:
-               session_send_msg(session, SESSION_MSG_ERROR,
-                                "error received from parent.");
-               session->state = SESSION_ERROR;
-               g_free(msg);
-               return FALSE;
+               return TRUE;
        }
 
-       g_free(msg);
-       return TRUE;
+       if (session->io_tag > 0) {
+               g_source_remove(session->io_tag);
+               session->io_tag = 0;
+       }
+
+       /* callback */
+       ret = session->send_data_finished(session, write_buf_len);
+       session->send_data_notify(session, write_buf_len,
+                                 session->send_data_notify_data);
+
+       return FALSE;
 }
index 6988148..3f4a0e1 100644 (file)
 #include <glib.h>
 
 #include <time.h>
+#include <sys/time.h>
 #include <unistd.h>
 
 #include "socket.h"
 
+#define SESSION_BUFFSIZE       4096
+
 typedef struct _Session        Session;
 
 #define SESSION(obj)   ((Session *)obj)
 
 typedef enum {
+       SESSION_UNKNOWN,
        SESSION_IMAP,
        SESSION_NEWS,
        SESSION_SMTP,
@@ -94,14 +98,21 @@ struct _Session
        SessionState state;
 
        time_t last_access_time;
+       struct timeval tv_prev;
+
+       gint conn_id;
 
-       pid_t child_pid;
+       /* I/O channel for socket */
+       GIOChannel *sock_ch;
+       gint io_tag;
 
-       /* pipe I/O */
-       GIOChannel *read_ch;
-       GIOChannel *write_ch;
+       GString *read_buf;
+       GByteArray *read_data_buf;
+       gchar *read_data_terminator;
 
-       gint read_tag;
+       gchar *write_buf;
+       gchar *write_buf_p;
+       gint write_buf_len;
 
        gpointer data;
 
@@ -109,11 +120,11 @@ struct _Session
        gint (*recv_msg)                (Session        *session,
                                         const gchar    *msg);
 
+       gint (*send_data_finished)      (Session        *session,
+                                        guint           len);
        gint (*recv_data_finished)      (Session        *session,
                                         guchar         *data,
                                         guint           len);
-       gint (*send_data_finished)      (Session        *session,
-                                        guint           len);
 
        void (*destroy)                 (Session        *session);
 
@@ -156,18 +167,19 @@ void session_set_send_data_notify (Session        *session,
                                         SendDataNotify  notify_func,
                                         gpointer        data);
 
+#if USE_OPENSSL
+gint session_start_tls (Session        *session);
+#endif
+
 gint session_send_msg  (Session        *session,
                         SessionMsgType  type,
                         const gchar    *msg);
+gint session_recv_msg  (Session        *session);
 gint session_send_data (Session        *session,
                         const guchar   *data,
                         guint           size);
 gint session_recv_data (Session        *session,
                         guint           size,
-                        gboolean        unescape_dot);
-
-#if USE_OPENSSL
-gint session_start_tls (Session        *session);
-#endif
+                        const gchar    *terminator);
 
 #endif /* __SESSION_H__ */
index d87486c..647a727 100644 (file)
@@ -61,12 +61,10 @@ Session *smtp_session_new(void)
        SMTPSession *session;
 
        session = g_new0(SMTPSession, 1);
+
+       session_init(SESSION(session));
+
        SESSION(session)->type             = SESSION_SMTP;
-       SESSION(session)->server           = NULL;
-       SESSION(session)->port             = 0;
-       SESSION(session)->sock             = NULL;
-       SESSION(session)->state            = SESSION_READY;
-       SESSION(session)->data             = NULL;
 
        SESSION(session)->recv_msg         = smtp_session_recv_msg;
 
@@ -473,7 +471,7 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
 
        /* ignore all multiline responses except for EHLO */
        if (cont && smtp_session->state != SMTP_EHLO)
-               return 1;
+               return session_recv_msg(session);
 
        switch (smtp_session->state) {
        case SMTP_READY:
@@ -556,7 +554,7 @@ static gint smtp_session_recv_msg(Session *session, const gchar *msg)
        }
 
        if (cont)
-               return 1;
+               return session_recv_msg(session);
 
        return 0;
 }
index e468b18..65fc007 100644 (file)
@@ -24,6 +24,7 @@
 #include <glib.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <sys/socket.h>
 #include <sys/un.h>
 #include <netinet/in.h>
 
 #define BUFFSIZE       8192
 
-guint io_timeout = 60;
+typedef gint (*SockAddrFunc)   (GList          *addr_list,
+                                gpointer        data);
+
+typedef struct _SockConnectData        SockConnectData;
+typedef struct _SockLookupData SockLookupData;
+typedef struct _SockAddrData   SockAddrData;
+
+struct _SockConnectData {
+       gint id;
+       gchar *hostname;
+       gushort port;
+       GList *addr_list;
+       GList *cur_addr;
+       SockLookupData *lookup_data;
+       GIOChannel *channel;
+       guint io_tag;
+       SockConnectFunc func;
+       gpointer data;
+};
+
+struct _SockLookupData {
+       gchar *hostname;
+       pid_t child_pid;
+       GIOChannel *channel;
+       guint io_tag;
+       SockAddrFunc func;
+       gpointer data;
+};
+
+struct _SockAddrData {
+       gint addr_len;
+       gpointer addr_data;
+};
+
+static guint io_timeout = 60;
+
+static GList *sock_connect_data_list = NULL;
 
 static gint sock_connect_with_timeout  (gint                    sock,
                                         const struct sockaddr  *serv_addr,
@@ -73,6 +110,27 @@ static gint sock_connect_by_getaddrinfo     (const gchar    *hostname,
 static SockInfo *sockinfo_from_fd(const gchar *hostname,
                                  gushort port,
                                  gint sock);
+static void sock_address_list_free             (GList          *addr_list);
+
+static gboolean sock_connect_async_cb          (GIOChannel     *source,
+                                                GIOCondition    condition,
+                                                gpointer        data);
+static gint sock_connect_async_get_address_info_cb
+                                               (GList          *addr_list,
+                                                gpointer        data);
+
+static gint sock_connect_address_list_async    (SockConnectData *conn_data);
+
+static gboolean sock_get_address_info_async_cb (GIOChannel     *source,
+                                                GIOCondition    condition,
+                                                gpointer        data);
+static SockLookupData *sock_get_address_info_async
+                                               (const gchar    *hostname,
+                                                gushort         port,
+                                                SockAddrFunc    func,
+                                                gpointer        data);
+static gint sock_get_address_info_async_cancel (SockLookupData *lookup_data);
+
 
 gint sock_set_io_timeout(guint sec)
 {
@@ -195,6 +253,9 @@ static gint fd_check_io(gint fd, GIOCondition cond)
        struct timeval timeout;
        fd_set fds;
 
+       if (is_nonblocking_mode(fd))
+               return 0;
+
        timeout.tv_sec  = io_timeout;
        timeout.tv_usec = 0;
 
@@ -427,6 +488,454 @@ SockInfo *sock_connect(const gchar *hostname, gushort port)
        return sockinfo_from_fd(hostname, port, sock);
 }
 
+static void sock_address_list_free(GList *addr_list)
+{
+       GList *cur;
+
+       for (cur = addr_list; cur != NULL; cur = cur->next) {
+               SockAddrData *addr_data = (SockAddrData *)cur->data;
+               g_free(addr_data->addr_data);
+               g_free(addr_data);
+       }
+
+       g_list_free(addr_list);
+}
+
+/* asynchronous TCP connection */
+
+static gboolean sock_connect_async_cb(GIOChannel *source,
+                                     GIOCondition condition, gpointer data)
+{
+       SockConnectData *conn_data = (SockConnectData *)data;
+       gint fd;
+       gint val;
+       gint len;
+       SockInfo *sockinfo;
+
+       fd = g_io_channel_unix_get_fd(source);
+
+       conn_data->io_tag = 0;
+       conn_data->channel = NULL;
+       g_io_channel_unref(source);
+
+       len = sizeof(val);
+       if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len) < 0) {
+               perror("getsockopt");
+               close(fd);
+               sock_connect_address_list_async(conn_data);
+               return FALSE;
+       }
+
+       if (val != 0) {
+               close(fd);
+               sock_connect_address_list_async(conn_data);
+               return FALSE;
+       }
+
+       sockinfo = g_new0(SockInfo, 1);
+       sockinfo->sock = fd;
+       sockinfo->hostname = g_strdup(conn_data->hostname);
+       sockinfo->port = conn_data->port;
+       sockinfo->state = CONN_ESTABLISHED;
+
+       conn_data->func(sockinfo, conn_data->data);
+
+       sock_connect_async_cancel(conn_data->id);
+
+       return FALSE;
+}
+
+static gint sock_connect_async_get_address_info_cb(GList *addr_list,
+                                                  gpointer data)
+{
+       SockConnectData *conn_data = (SockConnectData *)data;
+
+       conn_data->addr_list = addr_list;
+       conn_data->cur_addr = addr_list;
+       conn_data->lookup_data = NULL;
+
+       return sock_connect_address_list_async(conn_data);
+}
+
+gint sock_connect_async(const gchar *hostname, gushort port,
+                       SockConnectFunc func, gpointer data)
+{
+       static gint id = 1;
+       SockConnectData *conn_data;
+
+       conn_data = g_new0(SockConnectData, 1);
+       conn_data->id = id++;
+       conn_data->hostname = g_strdup(hostname);
+       conn_data->port = port;
+       conn_data->addr_list = NULL;
+       conn_data->cur_addr = NULL;
+       conn_data->io_tag = 0;
+       conn_data->func = func;
+       conn_data->data = data;
+
+       conn_data->lookup_data = sock_get_address_info_async
+               (hostname, port, sock_connect_async_get_address_info_cb,
+                conn_data);
+
+       if (conn_data->lookup_data == NULL) {
+               g_free(conn_data->hostname);
+               g_free(conn_data);
+               return -1;
+       }
+
+       sock_connect_data_list = g_list_append(sock_connect_data_list,
+                                              conn_data);
+
+       return conn_data->id;
+}
+
+gint sock_connect_async_cancel(gint id)
+{
+       SockConnectData *conn_data = NULL;
+       GList *cur;
+
+       for (cur = sock_connect_data_list; cur != NULL; cur = cur->next) {
+               if (((SockConnectData *)cur->data)->id == id) {
+                       conn_data = (SockConnectData *)cur->data;
+                       break;
+               }
+       }
+
+       if (conn_data) {
+               sock_connect_data_list = g_list_remove(sock_connect_data_list,
+                                                      conn_data);
+
+               if (conn_data->lookup_data)
+                       sock_get_address_info_async_cancel
+                               (conn_data->lookup_data);
+
+               if (conn_data->io_tag > 0)
+                       g_source_remove(conn_data->io_tag);
+               if (conn_data->channel) {
+                       g_io_channel_close(conn_data->channel);
+                       g_io_channel_unref(conn_data->channel);
+               }
+
+               sock_address_list_free(conn_data->addr_list);
+               g_free(conn_data->hostname);
+               g_free(conn_data);
+       } else {
+               g_warning("sock_connect_async_cancel: id %d not found.\n", id);
+               return -1;
+       }
+
+       return 0;
+}
+
+static gint sock_connect_address_list_async(SockConnectData *conn_data)
+{
+       SockAddrData *addr_data;
+       struct sockaddr_in ad;
+#ifdef INET6
+       struct sockaddr_in6 ad6;
+#endif
+       struct sockaddr *sa;
+       gint sa_size;
+       gint sock = -1;
+
+       for (; conn_data->cur_addr != NULL;
+            conn_data->cur_addr = conn_data->cur_addr->next) {
+               addr_data = (SockAddrData *)conn_data->cur_addr->data;
+
+#ifdef INET6
+               if (addr_data->addr_len != 4 && addr_data->addr_len != 16)
+                       continue;
+
+               if (addr_data->addr_len == 4) {
+                       /* IPv4 address */
+                       if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+                               perror("socket");
+                               conn_data->cur_addr = NULL;
+                               break;
+                       }
+
+                       memset(&ad, 0, sizeof(ad));
+                       ad.sin_family = AF_INET;
+                       ad.sin_port = htons(conn_data->port);
+                       memcpy(&ad.sin_addr, addr_data->addr_data,
+                              addr_data->addr_len);
+
+                       sa = (struct sockaddr *)&ad;
+                       sa_size = sizeof(ad);
+               } else {
+                       /* IPv6 address */
+                       if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
+                               perror("socket");
+                               conn_data->cur_addr = NULL;
+                               break;
+                       }
+
+                       memset(&ad6, 0, sizeof(ad6));
+                       ad6.sin6_family = AF_INET6;
+                       ad6.sin6_port = htons(conn_data->port);
+                       memcpy(&ad6.sin6_addr, addr_data->addr_data,
+                              addr_data->addr_len);
+
+                       sa = (struct sockaddr *)&ad6;
+                       sa_size = sizeof(ad6);
+               }
+#else /* !INET6 */
+               /* IPv4 only */
+               if (addr_data->addr_len != 4)
+                       continue;
+
+               if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+                       perror("socket");
+                       conn_data->cur_addr = NULL;
+                       break;
+               }
+
+               memset(&ad, 0, sizeof(ad));
+               ad.sin_family = AF_INET;
+               ad.sin_port = htons(conn_data->port);
+               memcpy(&ad.sin_addr, addr_data->addr_data, addr_data->addr_len);
+
+               sa = (struct sockaddr *)&ad;
+               sa_size = sizeof(ad);
+#endif
+
+               set_nonblocking_mode(sock, TRUE);
+
+               if (connect(sock, sa, sa_size) < 0) {
+                       if (EINPROGRESS == errno) {
+                               break;
+                       } else {
+                               perror("connect");
+                               close(sock);
+                       }
+               } 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;
+       }
+
+       conn_data->cur_addr = conn_data->cur_addr->next;
+
+       conn_data->channel = g_io_channel_unix_new(sock);
+       conn_data->io_tag = g_io_add_watch(conn_data->channel, G_IO_IN|G_IO_OUT,
+                                          sock_connect_async_cb, conn_data);
+
+       return 0;
+}
+
+/* asynchronous DNS lookup */
+
+static gboolean sock_get_address_info_async_cb(GIOChannel *source,
+                                              GIOCondition condition,
+                                              gpointer data)
+{
+       SockLookupData *lookup_data = (SockLookupData *)data;
+       GList *addr_list = NULL;
+       SockAddrData *addr_data;
+       guint bytes_read;
+       guint8 addr_len;
+       gchar buf[16];
+
+       for (;;) {
+               if (g_io_channel_read(source, &addr_len, 1, &bytes_read)
+                   != G_IO_ERROR_NONE) {
+                       g_warning("sock_get_address_info_async_cb: "
+                                 "address length read error\n");
+                       break;
+               }
+
+               if (bytes_read == 0)
+                       break;
+
+               if (addr_len == 'e') {
+                       g_warning("DNS lookup failed\n");
+                       break;
+               } else if (addr_len != 4 && addr_len != 16) {
+                       g_warning("illegal address length: %d\n", addr_len);
+                       break;
+               }
+
+               if (g_io_channel_read(source, buf, addr_len, &bytes_read)
+                   != G_IO_ERROR_NONE) {
+                       g_warning("sock_get_address_info_async_cb: "
+                                 "address data read error\n");
+                       break;
+               }
+
+               if (bytes_read != addr_len) {
+                       g_warning("sock_get_address_info_async_cb: "
+                                 "incomplete address data\n");
+                       break;
+               }
+
+               addr_data = g_new0(SockAddrData, 1);
+               addr_data->addr_len = addr_len;
+               addr_data->addr_data = g_malloc(addr_len);
+               memcpy(addr_data->addr_data, buf, addr_len);
+
+               addr_list = g_list_append(addr_list, addr_data);
+       }
+
+       g_io_channel_close(source);
+       g_io_channel_unref(source);
+
+       kill(lookup_data->child_pid, SIGKILL);
+       waitpid(lookup_data->child_pid, NULL, 0);
+
+       lookup_data->func(addr_list, lookup_data->data);
+
+       g_free(lookup_data->hostname);
+       g_free(lookup_data);
+
+       return FALSE;
+}
+
+static SockLookupData *sock_get_address_info_async(const gchar *hostname,
+                                                  gushort port,
+                                                  SockAddrFunc func,
+                                                  gpointer data)
+{
+       SockLookupData *lookup_data = NULL;
+       gint pipe_fds[2];
+       pid_t pid;
+
+       if (pipe(pipe_fds) < 0) {
+               perror("pipe");
+               func(NULL, data);
+               return NULL;
+       }
+
+       if ((pid = fork()) < 0) {
+               perror("fork");
+               func(NULL, data);
+               return NULL;
+       }
+
+       /* child process */
+       if (pid == 0) {
+#ifdef INET6
+               gint gai_err;
+               struct addrinfo hints, *res, *ai;
+               gchar port_str[6];
+#else /* !INET6 */
+               struct hostent *hp;
+               gchar **addr_list_p;
+#endif /* INET6 */
+               guint8 addr_len;
+
+               close(pipe_fds[0]);
+
+#ifdef INET6
+               memset(&hints, 0, sizeof(hints));
+               /* hints.ai_flags = AI_CANONNAME; */
+               hints.ai_family = AF_UNSPEC;
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_protocol = IPPROTO_TCP;
+
+               g_snprintf(port_str, sizeof(port_str), "%d", port);
+
+               gai_err = getaddrinfo(hostname, port_str, &hints, &res);
+               if (gai_err != 0) {
+                       g_warning("getaddrinfo for %s:%s failed: %s\n",
+                                 hostname, port_str, gai_strerror(gai_err));
+                       fd_write(pipe_fds[1], "e", 1);
+                       close(pipe_fds[1]);
+                       _exit(1);
+               }
+
+               for (ai = res; ai != NULL; ai = ai->ai_next) {
+                       if (ai->ai_family == AF_INET) {
+                               struct sockaddr_in *ad;
+
+                               ad = (struct sockaddr_in *)ai->ai_addr;
+                               addr_len = sizeof(ad->sin_addr);
+
+                               fd_write(pipe_fds[1], &addr_len, 1);
+                               fd_write_all(pipe_fds[1],
+                                            (gchar *)&ad->sin_addr,
+                                            sizeof(ad->sin_addr));
+                       } else if (ai->ai_family == AF_INET6) {
+                               struct sockaddr_in6 *ad;
+
+                               ad = (struct sockaddr_in6 *)ai->ai_addr;
+                               addr_len = sizeof(ad->sin6_addr);
+
+                               fd_write(pipe_fds[1], &addr_len, 1);
+                               fd_write_all(pipe_fds[1],
+                                            (gchar *)&ad->sin6_addr,
+                                            sizeof(ad->sin6_addr));
+                       }
+               }
+
+               if (res != NULL)
+                       freeaddrinfo(res);
+#else /* !INET6 */
+               hp = my_gethostbyname(hostname);
+               if (hp == NULL) {
+                       fd_write(pipe_fds[1], "e", 1);
+                       close(pipe_fds[1]);
+                       _exit(1);
+               }
+
+               addr_len = (guint8)hp->h_length;
+
+               for (addr_list_p = hp->h_addr_list; *addr_list_p != NULL;
+                    addr_list_p++) {
+                       fd_write(pipe_fds[1], &addr_len, 1);
+                       fd_write_all(pipe_fds[1], *addr_list_p, hp->h_length);
+               }
+#endif /* INET6 */
+
+               close(pipe_fds[1]);
+
+               _exit(0);
+       } else {
+               close(pipe_fds[1]);
+
+               lookup_data = g_new0(SockLookupData, 1);
+               lookup_data->hostname = g_strdup(hostname);
+               lookup_data->child_pid = pid;
+               lookup_data->func = func;
+               lookup_data->data = data;
+
+               lookup_data->channel = g_io_channel_unix_new(pipe_fds[0]);
+               lookup_data->io_tag = g_io_add_watch
+                       (lookup_data->channel, G_IO_IN,
+                        sock_get_address_info_async_cb, lookup_data);
+       }
+
+       return lookup_data;
+}
+
+static gint sock_get_address_info_async_cancel(SockLookupData *lookup_data)
+{
+       if (lookup_data->io_tag > 0)
+               g_source_remove(lookup_data->io_tag);
+       if (lookup_data->channel) {
+               g_io_channel_close(lookup_data->channel);
+               g_io_channel_unref(lookup_data->channel);
+       }
+
+       if (lookup_data->child_pid > 0) {
+               kill(lookup_data->child_pid, SIGKILL);
+               waitpid(lookup_data->child_pid, NULL, 0);
+       }
+
+       g_free(lookup_data->hostname);
+       g_free(lookup_data);
+
+       g_print("DNS lookup cancelled\n");
+
+       return 0;
+}
+
 
 static SockInfo *sockinfo_from_fd(const gchar *hostname,
                                  gushort port,
@@ -728,33 +1237,16 @@ gint sock_puts(SockInfo *sock, const gchar *buf)
        return sock_write_all(sock, "\r\n", 2);
 }
 
-/* peek at the next socket character without actually reading it */
-gint sock_peek(SockInfo *sock)
+/* peek at the socket data without actually reading it */
+gint sock_peek(SockInfo *sock, gchar *buf, gint len)
 {
-       gint ret, n;
-       gchar ch;
-
        g_return_val_if_fail(sock != NULL, -1);
 
 #if USE_OPENSSL
-       if (sock->ssl) {
-               if ((n = SSL_peek(sock->ssl, &ch, 1)) < 0)
-                       ret = -1;
-               else
-                       ret = ch;
-       } else {
-#endif
-               if ((n = recv(sock->sock, &ch, 1, MSG_PEEK)) < 0)
-                       ret = -1;
-               else
-                       ret = ch;
-#if USE_OPENSSL
-       }
+       if (sock->ssl)
+               return SSL_peek(sock->ssl, buf, len);
 #endif
-
-       if (ret < 0)
-               sock->state = CONN_DISCONNECTED;
-       return ret;
+       return fd_recv(sock->sock, buf, len, MSG_PEEK);
 }
 
 gint sock_close(SockInfo *sock)
index bf4d7b4..9e4fe89 100644 (file)
@@ -43,6 +43,9 @@ typedef enum
        CONN_DISCONNECTED,
 } ConnectionState;
 
+typedef gint (*SockConnectFunc)                (SockInfo       *sock,
+                                        gpointer        data);
+
 struct _SockInfo
 {
 #if USE_GIO
@@ -70,6 +73,9 @@ struct hostent *my_gethostbyname      (const gchar *hostname);
 
 SockInfo *sock_connect                 (const gchar *hostname, gushort port);
 SockInfo *sock_connect_cmd             (const gchar *hostname, const gchar *tunnelcmd);
+gint sock_connect_async                        (const gchar *hostname, gushort port,
+                                        SockConnectFunc func, gpointer data);
+gint sock_connect_async_cancel         (gint id);
 
 gint sock_printf       (SockInfo *sock, const gchar *format, ...)
                         G_GNUC_PRINTF(2, 3);
@@ -79,6 +85,7 @@ gint sock_write_all   (SockInfo *sock, const gchar *buf, gint len);
 gint sock_gets         (SockInfo *sock, gchar *buf, gint len);
 gchar *sock_getline    (SockInfo *sock);
 gint sock_puts         (SockInfo *sock, const gchar *buf);
+gint sock_peek         (SockInfo *sock, gchar *buf, gint len);
 gint sock_close                (SockInfo *sock);
 
 /* Functions to directly work on FD.  They are needed for pipes */
index 652fe9e..d5b7b99 100644 (file)
--- a/src/inc.c
+++ b/src/inc.c
@@ -855,6 +855,11 @@ static gint inc_recv_data_progressive(Session *session, guint cur_len,
 
        g_return_val_if_fail(inc_session != NULL, -1);
 
+       if (pop3_session->state != POP3_RETR &&
+           pop3_session->state != POP3_RETR_RECV &&
+           pop3_session->state != POP3_DELETE &&
+           pop3_session->state != POP3_LOGOUT) return 0;
+
        inc_dialog = (IncProgressDialog *)inc_session->data;
        dialog = inc_dialog->dialog;
 
@@ -940,6 +945,11 @@ gint inc_drop_message(const gchar *file, Pop3Session *session)
 static void inc_put_error(IncState istate, const gchar *msg)
 {
        switch (istate) {
+       case INC_CONNECT_ERROR:
+               if (prefs_common.no_recv_err_panel)
+                       break;
+               alertpanel_error(_("Connection failed."));
+               break;
        case INC_ERROR:
                if (prefs_common.no_recv_err_panel)
                        break;
index 5c15b47..0f39391 100644 (file)
--- a/src/pop.c
+++ b/src/pop.c
@@ -58,10 +58,12 @@ static gint pop3_getrange_last_recv (Pop3Session *session,
                                         const gchar *msg);
 static gint pop3_getrange_uidl_send    (Pop3Session *session);
 static gint pop3_getrange_uidl_recv    (Pop3Session *session,
-                                        const gchar *msg);
+                                        const gchar *data,
+                                        guint        len);
 static gint pop3_getsize_list_send     (Pop3Session *session);
 static gint pop3_getsize_list_recv     (Pop3Session *session,
-                                        const gchar *msg);
+                                        const gchar *data,
+                                        guint        len);
 static gint pop3_retr_send             (Pop3Session *session);
 static gint pop3_retr_recv             (Pop3Session *session,
                                         const gchar *data,
@@ -226,41 +228,51 @@ static gint pop3_getrange_uidl_send(Pop3Session *session)
        return PS_SUCCESS;
 }
 
-static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *msg)
+static gint pop3_getrange_uidl_recv(Pop3Session *session, const gchar *data,
+                                   guint len)
 {
        gchar id[IDLEN + 1];
+       gchar buf[POPBUFSIZE];
+       gint buf_len;
        gint num;
        time_t recv_time;
+       const gchar *p = data;
+       const gchar *lastp = data + len;
+       const gchar *newline;
 
-       if (msg[0] == '.') {
-               session->uidl_is_valid = TRUE;
-               return PS_SUCCESS;
-       }
+       while (p < lastp) {
+               if ((newline = memchr(p, '\r', lastp - p)) == NULL)
+                       return -1;
+               buf_len = MIN(newline - p, sizeof(buf) - 1);
+               memcpy(buf, p, buf_len);
+               buf[buf_len] = '\0';
 
-       if (sscanf(msg, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
-               return -1;
-       if (num <= 0 || num > session->count)
-               return -1;
+               p = newline + 1;
+               if (p < lastp && *p == '\n') p++;
 
-       session->msg[num].uidl = g_strdup(id);
+               if (sscanf(buf, "%d %" Xstr(IDLEN) "s", &num, id) != 2)
+                       return -1;
+               if (num <= 0 || num > session->count)
+                       return -1;
 
-       if (!session->uidl_table)
-               return PS_CONTINUE;
+               session->msg[num].uidl = g_strdup(id);
 
-       recv_time = (time_t)g_hash_table_lookup(session->uidl_table, id);
-       session->msg[num].recv_time = recv_time;
+               recv_time = (time_t)g_hash_table_lookup(session->uidl_table, id);
+               session->msg[num].recv_time = recv_time;
 
-       if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE)
-               session->msg[num].received = TRUE;
+               if (!session->ac_prefs->getall && recv_time != RECV_TIME_NONE)
+                       session->msg[num].received = TRUE;
 
-       if (!session->new_msg_exist &&
-           (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
-            session->ac_prefs->rmmail)) {
-               session->cur_msg = num;
-               session->new_msg_exist = TRUE;
+               if (!session->new_msg_exist &&
+                   (session->ac_prefs->getall || recv_time == RECV_TIME_NONE ||
+                    session->ac_prefs->rmmail)) {
+                       session->cur_msg = num;
+                       session->new_msg_exist = TRUE;
+               }
        }
 
-       return PS_CONTINUE;
+       session->uidl_is_valid = TRUE;
+       return PS_SUCCESS;
 }
 
 static gint pop3_getsize_list_send(Pop3Session *session)
@@ -270,24 +282,38 @@ static gint pop3_getsize_list_send(Pop3Session *session)
        return PS_SUCCESS;
 }
 
-static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *msg)
+static gint pop3_getsize_list_recv(Pop3Session *session, const gchar *data,
+                                  guint len)
 {
+       gchar buf[POPBUFSIZE];
+       gint buf_len;
        guint num, size;
+       const gchar *p = data;
+       const gchar *lastp = data + len;
+       const gchar *newline;
+
+       while (p < lastp) {
+               if ((newline = memchr(p, '\r', lastp - p)) == NULL)
+                       return -1;
+               buf_len = MIN(newline - p, sizeof(buf) - 1);
+               memcpy(buf, p, buf_len);
+               buf[buf_len] = '\0';
 
-       if (msg[0] == '.')
-               return PS_SUCCESS;
+               p = newline + 1;
+               if (p < lastp && *p == '\n') p++;
 
-       if (sscanf(msg, "%u %u", &num, &size) != 2) {
-               session->error_val = PS_PROTOCOL;
-               return -1;
-       }
+               if (sscanf(buf, "%u %u", &num, &size) != 2) {
+                       session->error_val = PS_PROTOCOL;
+                       return -1;
+               }
 
-       if (num > 0 && num <= session->count)
-               session->msg[num].size = size;
-       if (num > 0 && num < session->cur_msg)
-               session->cur_total_bytes += size;
+               if (num > 0 && num <= session->count)
+                       session->msg[num].size = size;
+               if (num > 0 && num < session->cur_msg)
+                       session->cur_total_bytes += size;
+       }
 
-       return PS_CONTINUE;
+       return PS_SUCCESS;
 }
 
 static gint pop3_retr_send(Pop3Session *session)
@@ -381,12 +407,9 @@ Session *pop3_session_new(PrefsAccount *account)
 
        session = g_new0(Pop3Session, 1);
 
+       session_init(SESSION(session));
+
        SESSION(session)->type = SESSION_POP3;
-       SESSION(session)->server = NULL;
-       SESSION(session)->port = 0;
-       SESSION(session)->sock = NULL;
-       SESSION(session)->state = SESSION_READY;
-       SESSION(session)->data = NULL;
 
        SESSION(session)->recv_msg = pop3_session_recv_msg;
        SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
@@ -547,6 +570,9 @@ static gint pop3_write_msg_to_file(const gchar *file, const gchar *data,
                else
                        prev = cur + 1;
 
+               if (prev - data < len - 1 && *prev == '.' && *(prev + 1) == '.')
+                       prev++;
+
                if (prev - data >= len)
                        break;
        }
@@ -757,39 +783,16 @@ static gint pop3_session_recv_msg(Session *session, const gchar *msg)
                        pop3_getrange_last_send(pop3_session);
                } else {
                        pop3_session->state = POP3_GETRANGE_UIDL_RECV;
-                       return 1;
+                       session_recv_data(session, 0, ".\r\n");
                }
                break;
-       case POP3_GETRANGE_UIDL_RECV:
-               val = pop3_getrange_uidl_recv(pop3_session, body);
-               if (val == PS_CONTINUE)
-                       return 1;
-               else if (val == PS_SUCCESS) {
-                       if (pop3_session->new_msg_exist)
-                               pop3_getsize_list_send(pop3_session);
-                       else
-                               pop3_logout_send(pop3_session);
-               } else
-                       return -1;
-               break;
        case POP3_GETSIZE_LIST:
                pop3_session->state = POP3_GETSIZE_LIST_RECV;
-               return 1;
-       case POP3_GETSIZE_LIST_RECV:
-               val = pop3_getsize_list_recv(pop3_session, body);
-               if (val == PS_CONTINUE)
-                       return 1;
-               else if (val == PS_SUCCESS) {
-                       if (pop3_lookup_next(pop3_session) == POP3_ERROR)
-                               return -1;
-               } else
-                       return -1;
+               session_recv_data(session, 0, ".\r\n");
                break;
        case POP3_RETR:
                pop3_session->state = POP3_RETR_RECV;
-               session_recv_data
-                       (session,
-                        pop3_session->msg[pop3_session->cur_msg].size, TRUE);
+               session_recv_data(session, 0, ".\r\n");
                break;
        case POP3_DELETE:
                pop3_delete_recv(pop3_session);
@@ -816,24 +819,47 @@ static gint pop3_session_recv_data_finished(Session *session, guchar *data,
                                            guint len)
 {
        Pop3Session *pop3_session = POP3_SESSION(session);
+       Pop3ErrorValue val = PS_SUCCESS;
 
-       if (len == 0)
-               return -1;
+       switch (pop3_session->state) {
+       case POP3_GETRANGE_UIDL_RECV:
+               val = pop3_getrange_uidl_recv(pop3_session, data, len);
+               if (val == PS_SUCCESS) {
+                       if (pop3_session->new_msg_exist)
+                               pop3_getsize_list_send(pop3_session);
+                       else
+                               pop3_logout_send(pop3_session);
+               } else
+                       return -1;
+               break;
+       case POP3_GETSIZE_LIST_RECV:
+               val = pop3_getsize_list_recv(pop3_session, data, len);
+               if (val == PS_SUCCESS) {
+                       if (pop3_lookup_next(pop3_session) == POP3_ERROR)
+                               return -1;
+               } else
+                       return -1;
+               break;
+       case POP3_RETR_RECV:
+               if (pop3_retr_recv(pop3_session, data, len) < 0)
+                       return -1;
 
-       if (pop3_retr_recv(pop3_session, data, len) < 0)
+               if (pop3_session->ac_prefs->rmmail &&
+                   pop3_session->ac_prefs->msg_leave_time == 0 &&
+                   pop3_session->msg[pop3_session->cur_msg].recv_time
+                   != RECV_TIME_KEEP)
+                       pop3_delete_send(pop3_session);
+               else if (pop3_session->cur_msg == pop3_session->count)
+                       pop3_logout_send(pop3_session);
+               else {
+                       pop3_session->cur_msg++;
+                       if (pop3_lookup_next(pop3_session) == POP3_ERROR)
+                               return -1;
+               }
+               break;
+       case POP3_ERROR:
+       default:
                return -1;
-
-       if (pop3_session->ac_prefs->rmmail &&
-           pop3_session->ac_prefs->msg_leave_time == 0 &&
-           pop3_session->msg[pop3_session->cur_msg].recv_time
-           != RECV_TIME_KEEP)
-               pop3_delete_send(pop3_session);
-       else if (pop3_session->cur_msg == pop3_session->count)
-               pop3_logout_send(pop3_session);
-       else {
-               pop3_session->cur_msg++;
-               if (pop3_lookup_next(pop3_session) == POP3_ERROR)
-                       return -1;
        }
 
        return 0;
index 6e38266..ea9a105 100644 (file)
@@ -501,6 +501,10 @@ static gint send_send_data_progressive(Session *session, guint cur_len,
 
        g_return_val_if_fail(dialog != NULL, -1);
 
+       if (SMTP_SESSION(session)->state != SMTP_SEND_DATA &&
+           SMTP_SESSION(session)->state != SMTP_EOM)
+               return 0;
+
        g_snprintf(buf, sizeof(buf), _("Sending message (%d / %d bytes)"),
                   cur_len, total_len);
        progress_dialog_set_label(dialog->dialog, buf);