X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=blobdiff_plain;f=src%2Fcommon%2Fsession.c;h=959c7a28e6785e6956dd765c893df4476fe371ad;hp=25618c19ac3c23d939302fbdf7ab4685a4404086;hb=8f81dc6211b3c5bf353e62d69d93317c59ce8d24;hpb=2a2594a50bf9af393c69ae5ddf9e83dea51a0834 diff --git a/src/common/session.c b/src/common/session.c index 25618c19a..959c7a28e 100644 --- a/src/common/session.c +++ b/src/common/session.c @@ -1,10 +1,10 @@ /* * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client - * Copyright (C) 1999-2003 Hiroyuki Yamamoto + * 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 - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -13,35 +13,40 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * along with this program. If not, see . + * */ #ifdef HAVE_CONFIG_H # include "config.h" +#include "claws-features.h" #endif #include "defs.h" #include +#include #include #include #include -#include #include -#include -#include -#include +#include #include #include "session.h" #include "utils.h" +#include "log.h" static gint session_connect_cb (SockInfo *sock, gpointer data); static gint session_close (Session *session); +static gboolean session_timeout_cb (gpointer data); + +static gboolean session_recv_msg_idle_cb (gpointer data); +static gboolean session_recv_data_idle_cb (gpointer data); + static gboolean session_read_msg_cb (SockInfo *source, GIOCondition condition, gpointer data); @@ -56,31 +61,47 @@ static gboolean session_write_data_cb (SockInfo *source, gpointer data); -void session_init(Session *session) +void session_init(Session *session, const void *prefs_account, gboolean is_smtp) { session->type = SESSION_UNKNOWN; session->sock = NULL; session->server = NULL; session->port = 0; -#if USE_OPENSSL +#ifdef USE_GNUTLS session->ssl_type = SSL_NONE; #endif + session->nonblocking = TRUE; session->state = SESSION_READY; session->last_access_time = time(NULL); - gettimeofday(&session->tv_prev, NULL); + g_get_current_time(&session->tv_prev); session->conn_id = 0; session->io_tag = 0; - session->read_buf = g_string_sized_new(1024); + session->read_buf_p = session->read_buf; + session->read_buf_len = 0; + + session->read_msg_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->write_data = NULL; + session->write_data_p = NULL; + session->write_data_len = 0; + + session->timeout_tag = 0; + session->timeout_interval = 0; + session->data = NULL; + session->account = prefs_account; + session->is_smtp = is_smtp; + + session->ping_tag = -1; } /*! @@ -98,6 +119,7 @@ void session_init(Session *session) */ gint session_connect(Session *session, const gchar *server, gushort port) { +#ifdef G_OS_UNIX session->server = g_strdup(server); session->port = port; @@ -110,6 +132,22 @@ gint session_connect(Session *session, const gchar *server, gushort port) } return 0; +#else + SockInfo *sock; + + session->server = g_strdup(server); + session->port = port; + + sock = sock_connect(server, port); + if (sock == NULL) { + g_warning("can't connect to server."); + session_close(session); + return -1; + } + sock->is_smtp = session->is_smtp; + + return session_connect_cb(sock, session); +#endif } static gint session_connect_cb(SockInfo *sock, gpointer data) @@ -125,19 +163,32 @@ static gint session_connect_cb(SockInfo *sock, gpointer data) } session->sock = sock; - -#if USE_OPENSSL - sock_set_nonblocking_mode(sock, FALSE); - if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) { - g_warning("can't initialize SSL."); - session->state = SESSION_ERROR; - return -1; + sock->account = session->account; + sock->is_smtp = session->is_smtp; + sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept; + +#ifdef USE_GNUTLS + sock->gnutls_priority = session->gnutls_priority; + + if (session->ssl_type == SSL_TUNNEL) { + sock_set_nonblocking_mode(sock, FALSE); + if (!ssl_init_socket(sock)) { + g_warning("can't initialize SSL."); + log_error(LOG_PROTOCOL, _("SSL handshake failed\n")); + session->state = SESSION_ERROR; + return -1; + } } #endif - sock_set_nonblocking_mode(sock, TRUE); + /* we could have gotten a timeout while waiting for user input in + * an SSL certificate dialog */ + if (session->state == SESSION_TIMEOUT) + return -1; + + sock_set_nonblocking_mode(sock, session->nonblocking); - debug_print("session: connected\n"); + debug_print("session (%p): connected\n", session); session->state = SESSION_RECV; session->io_tag = sock_add_watch(session->sock, G_IO_IN, @@ -167,21 +218,79 @@ gint session_disconnect(Session *session) */ void session_destroy(Session *session) { - g_return_if_fail(session != NULL); - g_return_if_fail(session->destroy != NULL); + cm_return_if_fail(session != NULL); + cm_return_if_fail(session->destroy != NULL); - debug_print("session: session_destroy()\n"); + session_register_ping(session, NULL); session_close(session); session->destroy(session); g_free(session->server); - g_string_free(session->read_buf, TRUE); + g_string_free(session->read_msg_buf, TRUE); g_byte_array_free(session->read_data_buf, TRUE); g_free(session->read_data_terminator); g_free(session->write_buf); +#ifdef USE_GNUTLS + g_free(session->gnutls_priority); +#endif + + debug_print("session (%p): destroyed\n", session); + g_free(session); } +gboolean session_is_running(Session *session) +{ + return (session->state == SESSION_READY || + session->state == SESSION_SEND || + session->state == SESSION_RECV); +} + +gboolean session_is_connected(Session *session) +{ + return (session->state == SESSION_SEND || + session->state == SESSION_RECV); +} + +void session_set_access_time(Session *session) +{ + session->last_access_time = time(NULL); +} + +void session_set_timeout(Session *session, guint interval) +{ + if (session->timeout_tag > 0) + g_source_remove(session->timeout_tag); + + session->timeout_interval = interval; + if (interval > 0) { + if (interval % 1000 == 0) + session->timeout_tag = + g_timeout_add_seconds(interval/1000, session_timeout_cb, session); + else + session->timeout_tag = + g_timeout_add(interval, session_timeout_cb, session); + } else + session->timeout_tag = 0; +} + +static gboolean session_timeout_cb(gpointer data) +{ + Session *session = SESSION(data); + + g_warning("session timeout.\n"); + + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + + session->timeout_tag = 0; + session->state = SESSION_TIMEOUT; + + return FALSE; +} + void session_set_recv_message_notify(Session *session, RecvMsgNotify notify_func, gpointer data) { @@ -230,14 +339,17 @@ void session_set_send_data_notify(Session *session, SendDataNotify notify_func, */ static gint session_close(Session *session) { - g_return_val_if_fail(session != NULL, -1); - - debug_print("session_close\n"); + cm_return_val_if_fail(session != NULL, -1); +#ifdef G_OS_UNIX if (session->conn_id > 0) { sock_connect_async_cancel(session->conn_id); session->conn_id = 0; + debug_print("session (%p): connection cancelled\n", session); } +#endif + + session_set_timeout(session, 0); if (session->io_tag > 0) { g_source_remove(session->io_tag); @@ -248,30 +360,33 @@ static gint session_close(Session *session) sock_close(session->sock); session->sock = NULL; session->state = SESSION_DISCONNECTED; + debug_print("session (%p): closed\n", session); } return 0; } -#if USE_OPENSSL +#ifdef USE_GNUTLS gint session_start_tls(Session *session) { gboolean nb_mode; nb_mode = sock_is_nonblocking_mode(session->sock); + session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept; + 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"); + g_warning("couldn't start TLS session.\n"); if (nb_mode) - sock_set_nonblocking_mode(session->sock, TRUE); + sock_set_nonblocking_mode(session->sock, session->nonblocking); return -1; } if (nb_mode) - sock_set_nonblocking_mode(session->sock, TRUE); + sock_set_nonblocking_mode(session->sock, session->nonblocking); return 0; } @@ -281,9 +396,9 @@ gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg) { gboolean ret; - 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); + cm_return_val_if_fail(session->write_buf == NULL, -1); + cm_return_val_if_fail(msg != NULL, -1); + cm_return_val_if_fail(msg[0] != '\0', -1); session->state = SESSION_SEND; session->write_buf = g_strconcat(msg, "\r\n", NULL); @@ -303,16 +418,33 @@ gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg) gint session_recv_msg(Session *session) { - g_return_val_if_fail(session->read_buf->len == 0, -1); + cm_return_val_if_fail(session->read_msg_buf->len == 0, -1); session->state = SESSION_RECV; - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_msg_cb, session); + if (session->read_buf_len > 0) + g_idle_add(session_recv_msg_idle_cb, session); + else + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_msg_cb, session); return 0; } +static gboolean session_recv_msg_idle_cb(gpointer data) +{ + Session *session = SESSION(data); + gboolean ret; + + ret = session_read_msg_cb(session->sock, G_IO_IN, session); + + if (ret == TRUE) + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_msg_cb, session); + + return FALSE; +} + /*! *\brief parent (child?): send data to other process * @@ -327,17 +459,16 @@ gint session_send_data(Session *session, const guchar *data, guint size) { gboolean ret; - 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); + cm_return_val_if_fail(session->write_data == NULL, -1); + cm_return_val_if_fail(data != NULL, -1); + cm_return_val_if_fail(size != 0, -1); 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); + session->write_data = data; + session->write_data_p = session->write_data; + session->write_data_len = size; + g_get_current_time(&session->tv_prev); ret = session_write_data_cb(session->sock, G_IO_OUT, session); @@ -353,80 +484,121 @@ gint session_send_data(Session *session, const guchar *data, guint size) gint session_recv_data(Session *session, guint size, const gchar *terminator) { - g_return_val_if_fail(session->read_data_buf->len == 0, -1); + cm_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); + g_get_current_time(&session->tv_prev); - session->io_tag = sock_add_watch(session->sock, G_IO_IN, - session_read_data_cb, session); + if (session->read_buf_len > 0) + g_idle_add(session_recv_data_idle_cb, session); + else + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_data_cb, session); return 0; } +static gboolean session_recv_data_idle_cb(gpointer data) +{ + Session *session = SESSION(data); + gboolean ret; + + ret = session_read_data_cb(session->sock, G_IO_IN, session); + + if (ret == TRUE) + session->io_tag = sock_add_watch(session->sock, G_IO_IN, + session_read_data_cb, session); + + return FALSE; +} + static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); gchar buf[SESSION_BUFFSIZE]; - gint read_len; - gint to_read_len; + gint line_len; gchar *newline; gchar *msg; gint ret; - g_return_val_if_fail(condition == G_IO_IN, FALSE); + cm_return_val_if_fail(condition == G_IO_IN, FALSE); - read_len = sock_peek(session->sock, buf, sizeof(buf) - 1); + session_set_timeout(session, session->timeout_interval); - if (read_len < 0) { - switch (errno) { - case EAGAIN: - return TRUE; - default: - g_warning("sock_peek: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; + if (session->read_buf_len == 0) { + gint read_len = -1; + + if (session->sock) + read_len = sock_read(session->sock, session->read_buf, + SESSION_BUFFSIZE - 1); + + if (read_len == -1 && session->state == SESSION_DISCONNECTED) { + g_warning ("sock_read: session disconnected\n"); + if (session->io_tag > 0) { + g_source_remove(session->io_tag); + session->io_tag = 0; + } + return FALSE; + } + + if (read_len == 0) { + g_warning("sock_read: received EOF\n"); + session->state = SESSION_EOF; return FALSE; } + + if (read_len < 0) { + switch (errno) { + case EAGAIN: + return TRUE; + default: + g_warning("sock_read: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return FALSE; + } + } + + session->read_buf_len = read_len; } - if ((newline = memchr(buf, '\n', read_len)) != NULL) - to_read_len = newline - buf + 1; + if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len)) + != NULL) + line_len = newline - session->read_buf_p + 1; else - to_read_len = read_len; + line_len = session->read_buf_len; + + if (line_len == 0) + return TRUE; - read_len = sock_read(session->sock, buf, to_read_len); + memcpy(buf, session->read_buf_p, line_len); + buf[line_len] = '\0'; - /* this should always succeed */ - if (read_len < 0) { - g_warning("sock_read: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; - return FALSE; - } + g_string_append(session->read_msg_buf, buf); - buf[read_len] = '\0'; + session->read_buf_len -= line_len; + if (session->read_buf_len == 0) + session->read_buf_p = session->read_buf; + else + session->read_buf_p += line_len; /* incomplete read */ - if (read_len == 0 || buf[read_len - 1] != '\n') { - g_string_append(session->read_buf, buf); + if (buf[line_len - 1] != '\n') return TRUE; - } /* complete */ - strretchomp(buf); - g_string_append(session->read_buf, buf); - if (session->io_tag > 0) { g_source_remove(session->io_tag); session->io_tag = 0; } /* callback */ - msg = g_strdup(session->read_buf->str); - g_string_truncate(session->read_buf, 0); + msg = g_strdup(session->read_msg_buf->str); + strretchomp(msg); + g_string_truncate(session->read_msg_buf, 0); ret = session->recv_msg(session, msg); session->recv_msg_notify(session, msg, session->recv_msg_notify_data); @@ -443,36 +615,56 @@ static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); - gchar buf[SESSION_BUFFSIZE]; GByteArray *data_buf; - gint read_len; gint terminator_len; gboolean complete = FALSE; guint data_len; gint ret; - g_return_val_if_fail(condition == G_IO_IN, FALSE); + cm_return_val_if_fail(condition == G_IO_IN, FALSE); - read_len = sock_read(session->sock, buf, sizeof(buf)); + session_set_timeout(session, session->timeout_interval); - if (read_len < 0) { - switch (errno) { - case EAGAIN: - return TRUE; - default: - g_warning("sock_read: %s\n", g_strerror(errno)); - session->state = SESSION_ERROR; + if (session->read_buf_len == 0) { + gint read_len; + + read_len = sock_read(session->sock, session->read_buf, + SESSION_BUFFSIZE); + + if (read_len == 0) { + g_warning("sock_read: received EOF\n"); + session->state = SESSION_EOF; return FALSE; } + + if (read_len < 0) { + switch (errno) { + case EAGAIN: + return TRUE; + default: + g_warning("sock_read: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return FALSE; + } + } + + session->read_buf_len = read_len; } data_buf = session->read_data_buf; - - g_byte_array_append(data_buf, buf, read_len); terminator_len = strlen(session->read_data_terminator); + if (session->read_buf_len == 0) + return TRUE; + + g_byte_array_append(data_buf, session->read_buf_p, + session->read_buf_len); + + session->read_buf_len = 0; + session->read_buf_p = session->read_buf; + /* check if data is terminated */ - if (read_len > 0 && data_buf->len >= terminator_len) { + if (data_buf->len >= terminator_len) { if (memcmp(data_buf->data, session->read_data_terminator, terminator_len) == 0) complete = TRUE; @@ -487,16 +679,16 @@ static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition, /* incomplete read */ if (!complete) { - struct timeval tv_cur; + GTimeVal tv_cur; - gettimeofday(&tv_cur, NULL); + g_get_current_time(&tv_cur); 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); + g_get_current_time(&session->tv_prev); } return TRUE; } @@ -529,9 +721,9 @@ static gint session_write_buf(Session *session) gint write_len; gint to_write_len; - 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); + cm_return_val_if_fail(session->write_buf != NULL, -1); + cm_return_val_if_fail(session->write_buf_p != NULL, -1); + cm_return_val_if_fail(session->write_buf_len > 0, -1); to_write_len = session->write_buf_len - (session->write_buf_p - session->write_buf); @@ -567,16 +759,58 @@ static gint session_write_buf(Session *session) return 0; } +static gint session_write_data(Session *session) +{ + gint write_len; + gint to_write_len; + + cm_return_val_if_fail(session->write_data != NULL, -1); + cm_return_val_if_fail(session->write_data_p != NULL, -1); + cm_return_val_if_fail(session->write_data_len > 0, -1); + + to_write_len = session->write_data_len - + (session->write_data_p - session->write_data); + to_write_len = MIN(to_write_len, SESSION_BUFFSIZE); + + write_len = sock_write(session->sock, session->write_data_p, + to_write_len); + + if (write_len < 0) { + switch (errno) { + case EAGAIN: + write_len = 0; + break; + default: + g_warning("sock_write: %s\n", g_strerror(errno)); + session->state = SESSION_ERROR; + return -1; + } + } + + /* incomplete write */ + if (session->write_data_p - session->write_data + write_len < + session->write_data_len) { + session->write_data_p += write_len; + return 1; + } + + session->write_data = NULL; + session->write_data_p = NULL; + session->write_data_len = 0; + + return 0; +} + static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); 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); + cm_return_val_if_fail(condition == G_IO_OUT, FALSE); + cm_return_val_if_fail(session->write_buf != NULL, FALSE); + cm_return_val_if_fail(session->write_buf_p != NULL, FALSE); + cm_return_val_if_fail(session->write_buf_len > 0, FALSE); ret = session_write_buf(session); @@ -600,34 +834,35 @@ static gboolean session_write_data_cb(SockInfo *source, GIOCondition condition, gpointer data) { Session *session = SESSION(data); - guint write_buf_len; + guint write_data_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); + cm_return_val_if_fail(condition == G_IO_OUT, FALSE); + cm_return_val_if_fail(session->write_data != NULL, FALSE); + cm_return_val_if_fail(session->write_data_p != NULL, FALSE); + cm_return_val_if_fail(session->write_data_len > 0, FALSE); - write_buf_len = session->write_buf_len; + write_data_len = session->write_data_len; - ret = session_write_buf(session); + ret = session_write_data(session); if (ret < 0) { session->state = SESSION_ERROR; return FALSE; } else if (ret > 0) { - struct timeval tv_cur; + GTimeVal tv_cur; - gettimeofday(&tv_cur, NULL); + g_get_current_time(&tv_cur); if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 || tv_cur.tv_usec - session->tv_prev.tv_usec > UI_REFRESH_INTERVAL) { + session_set_timeout(session, session->timeout_interval); session->send_data_progressive_notify (session, - session->write_buf_p - session->write_buf, - write_buf_len, + session->write_data_p - session->write_data, + write_data_len, session->send_data_progressive_notify_data); - gettimeofday(&session->tv_prev, NULL); + g_get_current_time(&session->tv_prev); } return TRUE; } @@ -638,9 +873,22 @@ static gboolean session_write_data_cb(SockInfo *source, } /* callback */ - ret = session->send_data_finished(session, write_buf_len); - session->send_data_notify(session, write_buf_len, + ret = session->send_data_finished(session, write_data_len); + session->send_data_notify(session, write_data_len, session->send_data_notify_data); return FALSE; } + +void session_register_ping(Session *session, gboolean (*ping_cb)(gpointer data)) +{ + if (!session) + return; + if (session->ping_tag > -1) + g_source_remove(session->ping_tag); + + session->ping_tag = -1; + + if (ping_cb != NULL) + session->ping_tag = g_timeout_add_seconds(60, ping_cb, session); +}