2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 #include <glib/gi18n.h>
40 static gint session_connect_cb (SockInfo *sock,
42 static gint session_close (Session *session);
44 static gboolean session_timeout_cb (gpointer data);
46 static gboolean session_recv_msg_idle_cb (gpointer data);
47 static gboolean session_recv_data_idle_cb (gpointer data);
49 static gboolean session_read_msg_cb (SockInfo *source,
50 GIOCondition condition,
52 static gboolean session_read_data_cb (SockInfo *source,
53 GIOCondition condition,
55 static gboolean session_write_msg_cb (SockInfo *source,
56 GIOCondition condition,
58 static gboolean session_write_data_cb (SockInfo *source,
59 GIOCondition condition,
63 void session_init(Session *session, const void *prefs_account, gboolean is_smtp)
65 session->type = SESSION_UNKNOWN;
67 session->server = NULL;
69 #if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
70 session->ssl_type = SSL_NONE;
72 session->nonblocking = TRUE;
73 session->state = SESSION_READY;
74 session->last_access_time = time(NULL);
76 g_get_current_time(&session->tv_prev);
82 session->read_buf_p = session->read_buf;
83 session->read_buf_len = 0;
85 session->read_msg_buf = g_string_sized_new(1024);
86 session->read_data_buf = g_byte_array_new();
88 session->write_buf = NULL;
89 session->write_buf_p = NULL;
90 session->write_buf_len = 0;
92 session->write_data = NULL;
93 session->write_data_p = NULL;
94 session->write_data_len = 0;
96 session->timeout_tag = 0;
97 session->timeout_interval = 0;
100 session->account = prefs_account;
101 session->is_smtp = is_smtp;
105 *\brief Set up parent and child process
106 * Childloop: Read commands from parent,
107 * send to server, get answer, pass to parent
109 *\param session Contains session information
110 * server to connect to
114 * -1 : pipe / fork errors (parent)
115 * 1 : connection error (child)
117 gint session_connect(Session *session, const gchar *server, gushort port)
120 session->server = g_strdup(server);
121 session->port = port;
123 session->conn_id = sock_connect_async(server, port, session_connect_cb,
125 if (session->conn_id < 0) {
126 g_warning("can't connect to server.");
127 session_close(session);
135 session->server = g_strdup(server);
136 session->port = port;
138 sock = sock_connect(server, port);
140 g_warning("can't connect to server.");
141 session_close(session);
144 sock->is_smtp = session->is_smtp;
146 return session_connect_cb(sock, session);
150 static gint session_connect_cb(SockInfo *sock, gpointer data)
152 Session *session = SESSION(data);
154 session->conn_id = 0;
157 g_warning("can't connect to server.");
158 session->state = SESSION_ERROR;
162 session->sock = sock;
163 sock->account = session->account;
164 sock->is_smtp = session->is_smtp;
165 #if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
166 if (session->ssl_type == SSL_TUNNEL) {
167 sock_set_nonblocking_mode(sock, FALSE);
168 if (!ssl_init_socket(sock)) {
169 g_warning("can't initialize SSL.");
170 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
171 session->state = SESSION_ERROR;
177 sock_set_nonblocking_mode(sock, session->nonblocking);
179 debug_print("session (%p): connected\n", session);
181 session->state = SESSION_RECV;
182 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
190 *\brief child and parent: send DISCONNECT message to other process
192 *\param session Contains session information
196 gint session_disconnect(Session *session)
198 session_close(session);
205 *\param session Contains session information
207 void session_destroy(Session *session)
209 g_return_if_fail(session != NULL);
210 g_return_if_fail(session->destroy != NULL);
212 session_close(session);
213 session->destroy(session);
214 g_free(session->server);
215 g_string_free(session->read_msg_buf, TRUE);
216 g_byte_array_free(session->read_data_buf, TRUE);
217 g_free(session->read_data_terminator);
218 g_free(session->write_buf);
220 debug_print("session (%p): destroyed\n", session);
225 gboolean session_is_connected(Session *session)
227 return (session->state == SESSION_READY ||
228 session->state == SESSION_SEND ||
229 session->state == SESSION_RECV);
232 void session_set_access_time(Session *session)
234 session->last_access_time = time(NULL);
237 void session_set_timeout(Session *session, guint interval)
239 if (session->timeout_tag > 0)
240 g_source_remove(session->timeout_tag);
242 session->timeout_interval = interval;
244 #if GLIB_CHECK_VERSION(2,14,0)
245 if (interval % 1000 == 0)
246 session->timeout_tag =
247 g_timeout_add_seconds(interval/1000, session_timeout_cb, session);
250 session->timeout_tag =
251 g_timeout_add(interval, session_timeout_cb, session);
253 session->timeout_tag = 0;
256 static gboolean session_timeout_cb(gpointer data)
258 Session *session = SESSION(data);
260 g_warning("session timeout.\n");
262 if (session->io_tag > 0) {
263 g_source_remove(session->io_tag);
267 session->timeout_tag = 0;
268 session->state = SESSION_TIMEOUT;
273 void session_set_recv_message_notify(Session *session,
274 RecvMsgNotify notify_func, gpointer data)
276 session->recv_msg_notify = notify_func;
277 session->recv_msg_notify_data = data;
280 void session_set_recv_data_progressive_notify
282 RecvDataProgressiveNotify notify_func,
285 session->recv_data_progressive_notify = notify_func,
286 session->recv_data_progressive_notify_data = data;
289 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
292 session->recv_data_notify = notify_func;
293 session->recv_data_notify_data = data;
296 void session_set_send_data_progressive_notify
298 SendDataProgressiveNotify notify_func,
301 session->send_data_progressive_notify = notify_func;
302 session->send_data_progressive_notify_data = data;
305 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
308 session->send_data_notify = notify_func;
309 session->send_data_notify_data = data;
313 *\brief child and parent cleanup (child closes first)
315 *\param session Contains session information
319 static gint session_close(Session *session)
321 g_return_val_if_fail(session != NULL, -1);
324 if (session->conn_id > 0) {
325 sock_connect_async_cancel(session->conn_id);
326 session->conn_id = 0;
327 debug_print("session (%p): connection cancelled\n", session);
331 session_set_timeout(session, 0);
333 if (session->io_tag > 0) {
334 g_source_remove(session->io_tag);
339 sock_close(session->sock);
340 session->sock = NULL;
341 session->state = SESSION_DISCONNECTED;
342 debug_print("session (%p): closed\n", session);
348 #if (defined(USE_OPENSSL) || defined (USE_GNUTLS))
349 gint session_start_tls(Session *session)
353 nb_mode = sock_is_nonblocking_mode(session->sock);
356 sock_set_nonblocking_mode(session->sock, FALSE);
358 if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
359 g_warning("couldn't start TLS session.\n");
361 sock_set_nonblocking_mode(session->sock, session->nonblocking);
366 sock_set_nonblocking_mode(session->sock, session->nonblocking);
372 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
376 g_return_val_if_fail(session->write_buf == NULL, -1);
377 g_return_val_if_fail(msg != NULL, -1);
378 g_return_val_if_fail(msg[0] != '\0', -1);
380 session->state = SESSION_SEND;
381 session->write_buf = g_strconcat(msg, "\r\n", NULL);
382 session->write_buf_p = session->write_buf;
383 session->write_buf_len = strlen(msg) + 2;
385 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
388 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
389 session_write_msg_cb, session);
390 else if (session->state == SESSION_ERROR)
396 gint session_recv_msg(Session *session)
398 g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
400 session->state = SESSION_RECV;
402 if (session->read_buf_len > 0)
403 g_idle_add(session_recv_msg_idle_cb, session);
405 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
406 session_read_msg_cb, session);
411 static gboolean session_recv_msg_idle_cb(gpointer data)
413 Session *session = SESSION(data);
416 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
419 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
420 session_read_msg_cb, session);
426 *\brief parent (child?): send data to other process
428 *\param session Contains session information
435 gint session_send_data(Session *session, const guchar *data, guint size)
439 g_return_val_if_fail(session->write_data == NULL, -1);
440 g_return_val_if_fail(data != NULL, -1);
441 g_return_val_if_fail(size != 0, -1);
443 session->state = SESSION_SEND;
445 session->write_data = data;
446 session->write_data_p = session->write_data;
447 session->write_data_len = size;
448 g_get_current_time(&session->tv_prev);
450 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
453 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
454 session_write_data_cb,
456 else if (session->state == SESSION_ERROR)
462 gint session_recv_data(Session *session, guint size, const gchar *terminator)
464 g_return_val_if_fail(session->read_data_buf->len == 0, -1);
466 session->state = SESSION_RECV;
468 g_free(session->read_data_terminator);
469 session->read_data_terminator = g_strdup(terminator);
470 g_get_current_time(&session->tv_prev);
472 if (session->read_buf_len > 0)
473 g_idle_add(session_recv_data_idle_cb, session);
475 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
476 session_read_data_cb, session);
481 static gboolean session_recv_data_idle_cb(gpointer data)
483 Session *session = SESSION(data);
486 ret = session_read_data_cb(session->sock, G_IO_IN, session);
489 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
490 session_read_data_cb, session);
495 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
498 Session *session = SESSION(data);
499 gchar buf[SESSION_BUFFSIZE];
505 g_return_val_if_fail(condition == G_IO_IN, FALSE);
507 session_set_timeout(session, session->timeout_interval);
509 if (session->read_buf_len == 0) {
512 read_len = sock_read(session->sock, session->read_buf,
513 SESSION_BUFFSIZE - 1);
515 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
516 g_warning ("sock_read: session disconnected\n");
517 if (session->io_tag > 0) {
518 g_source_remove(session->io_tag);
525 g_warning("sock_read: received EOF\n");
526 session->state = SESSION_EOF;
535 g_warning("sock_read: %s\n", g_strerror(errno));
536 session->state = SESSION_ERROR;
541 session->read_buf_len = read_len;
544 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
546 line_len = newline - session->read_buf_p + 1;
548 line_len = session->read_buf_len;
553 memcpy(buf, session->read_buf_p, line_len);
554 buf[line_len] = '\0';
556 g_string_append(session->read_msg_buf, buf);
558 session->read_buf_len -= line_len;
559 if (session->read_buf_len == 0)
560 session->read_buf_p = session->read_buf;
562 session->read_buf_p += line_len;
564 /* incomplete read */
565 if (buf[line_len - 1] != '\n')
569 if (session->io_tag > 0) {
570 g_source_remove(session->io_tag);
575 msg = g_strdup(session->read_msg_buf->str);
577 g_string_truncate(session->read_msg_buf, 0);
579 ret = session->recv_msg(session, msg);
580 session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
585 session->state = SESSION_ERROR;
590 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
593 Session *session = SESSION(data);
594 GByteArray *data_buf;
596 gboolean complete = FALSE;
600 g_return_val_if_fail(condition == G_IO_IN, FALSE);
602 session_set_timeout(session, session->timeout_interval);
604 if (session->read_buf_len == 0) {
607 read_len = sock_read(session->sock, session->read_buf,
611 g_warning("sock_read: received EOF\n");
612 session->state = SESSION_EOF;
621 g_warning("sock_read: %s\n", g_strerror(errno));
622 session->state = SESSION_ERROR;
627 session->read_buf_len = read_len;
630 data_buf = session->read_data_buf;
631 terminator_len = strlen(session->read_data_terminator);
633 if (session->read_buf_len == 0)
636 g_byte_array_append(data_buf, session->read_buf_p,
637 session->read_buf_len);
639 session->read_buf_len = 0;
640 session->read_buf_p = session->read_buf;
642 /* check if data is terminated */
643 if (data_buf->len >= terminator_len) {
644 if (memcmp(data_buf->data, session->read_data_terminator,
645 terminator_len) == 0)
647 else if (data_buf->len >= terminator_len + 2 &&
648 memcmp(data_buf->data + data_buf->len -
649 (terminator_len + 2), "\r\n", 2) == 0 &&
650 memcmp(data_buf->data + data_buf->len -
651 terminator_len, session->read_data_terminator,
652 terminator_len) == 0)
656 /* incomplete read */
660 g_get_current_time(&tv_cur);
661 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
662 tv_cur.tv_usec - session->tv_prev.tv_usec >
663 UI_REFRESH_INTERVAL) {
664 session->recv_data_progressive_notify
665 (session, data_buf->len, 0,
666 session->recv_data_progressive_notify_data);
667 g_get_current_time(&session->tv_prev);
673 if (session->io_tag > 0) {
674 g_source_remove(session->io_tag);
678 data_len = data_buf->len - terminator_len;
681 ret = session->recv_data_finished(session, (gchar *)data_buf->data,
684 g_byte_array_set_size(data_buf, 0);
686 session->recv_data_notify(session, data_len,
687 session->recv_data_notify_data);
690 session->state = SESSION_ERROR;
695 static gint session_write_buf(Session *session)
700 g_return_val_if_fail(session->write_buf != NULL, -1);
701 g_return_val_if_fail(session->write_buf_p != NULL, -1);
702 g_return_val_if_fail(session->write_buf_len > 0, -1);
704 to_write_len = session->write_buf_len -
705 (session->write_buf_p - session->write_buf);
706 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
708 write_len = sock_write(session->sock, session->write_buf_p,
717 g_warning("sock_write: %s\n", g_strerror(errno));
718 session->state = SESSION_ERROR;
723 /* incomplete write */
724 if (session->write_buf_p - session->write_buf + write_len <
725 session->write_buf_len) {
726 session->write_buf_p += write_len;
730 g_free(session->write_buf);
731 session->write_buf = NULL;
732 session->write_buf_p = NULL;
733 session->write_buf_len = 0;
738 static gint session_write_data(Session *session)
743 g_return_val_if_fail(session->write_data != NULL, -1);
744 g_return_val_if_fail(session->write_data_p != NULL, -1);
745 g_return_val_if_fail(session->write_data_len > 0, -1);
747 to_write_len = session->write_data_len -
748 (session->write_data_p - session->write_data);
749 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
751 write_len = sock_write(session->sock, session->write_data_p,
760 g_warning("sock_write: %s\n", g_strerror(errno));
761 session->state = SESSION_ERROR;
766 /* incomplete write */
767 if (session->write_data_p - session->write_data + write_len <
768 session->write_data_len) {
769 session->write_data_p += write_len;
773 session->write_data = NULL;
774 session->write_data_p = NULL;
775 session->write_data_len = 0;
780 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
783 Session *session = SESSION(data);
786 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
787 g_return_val_if_fail(session->write_buf != NULL, FALSE);
788 g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
789 g_return_val_if_fail(session->write_buf_len > 0, FALSE);
791 ret = session_write_buf(session);
794 session->state = SESSION_ERROR;
799 if (session->io_tag > 0) {
800 g_source_remove(session->io_tag);
804 session_recv_msg(session);
809 static gboolean session_write_data_cb(SockInfo *source,
810 GIOCondition condition, gpointer data)
812 Session *session = SESSION(data);
813 guint write_data_len;
816 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
817 g_return_val_if_fail(session->write_data != NULL, FALSE);
818 g_return_val_if_fail(session->write_data_p != NULL, FALSE);
819 g_return_val_if_fail(session->write_data_len > 0, FALSE);
821 write_data_len = session->write_data_len;
823 ret = session_write_data(session);
826 session->state = SESSION_ERROR;
828 } else if (ret > 0) {
831 g_get_current_time(&tv_cur);
832 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
833 tv_cur.tv_usec - session->tv_prev.tv_usec >
834 UI_REFRESH_INTERVAL) {
835 session_set_timeout(session, session->timeout_interval);
836 session->send_data_progressive_notify
838 session->write_data_p - session->write_data,
840 session->send_data_progressive_notify_data);
841 g_get_current_time(&session->tv_prev);
846 if (session->io_tag > 0) {
847 g_source_remove(session->io_tag);
852 ret = session->send_data_finished(session, write_data_len);
853 session->send_data_notify(session, write_data_len,
854 session->send_data_notify_data);