2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2011 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;
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;
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 /* we could have gotten a timeout while waiting for user input in
178 * an SSL certificate dialog */
179 if (session->state == SESSION_TIMEOUT)
182 sock_set_nonblocking_mode(sock, session->nonblocking);
184 debug_print("session (%p): connected\n", session);
186 session->state = SESSION_RECV;
187 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
195 *\brief child and parent: send DISCONNECT message to other process
197 *\param session Contains session information
201 gint session_disconnect(Session *session)
203 session_close(session);
210 *\param session Contains session information
212 void session_destroy(Session *session)
214 cm_return_if_fail(session != NULL);
215 cm_return_if_fail(session->destroy != NULL);
217 session_close(session);
218 session->destroy(session);
219 g_free(session->server);
220 g_string_free(session->read_msg_buf, TRUE);
221 g_byte_array_free(session->read_data_buf, TRUE);
222 g_free(session->read_data_terminator);
223 g_free(session->write_buf);
225 debug_print("session (%p): destroyed\n", session);
230 gboolean session_is_running(Session *session)
232 return (session->state == SESSION_READY ||
233 session->state == SESSION_SEND ||
234 session->state == SESSION_RECV);
237 gboolean session_is_connected(Session *session)
239 return (session->state == SESSION_SEND ||
240 session->state == SESSION_RECV);
243 void session_set_access_time(Session *session)
245 session->last_access_time = time(NULL);
248 void session_set_timeout(Session *session, guint interval)
250 if (session->timeout_tag > 0)
251 g_source_remove(session->timeout_tag);
253 session->timeout_interval = interval;
255 #if GLIB_CHECK_VERSION(2,14,0)
256 if (interval % 1000 == 0)
257 session->timeout_tag =
258 g_timeout_add_seconds(interval/1000, session_timeout_cb, session);
261 session->timeout_tag =
262 g_timeout_add(interval, session_timeout_cb, session);
264 session->timeout_tag = 0;
267 static gboolean session_timeout_cb(gpointer data)
269 Session *session = SESSION(data);
271 g_warning("session timeout.\n");
273 if (session->io_tag > 0) {
274 g_source_remove(session->io_tag);
278 session->timeout_tag = 0;
279 session->state = SESSION_TIMEOUT;
284 void session_set_recv_message_notify(Session *session,
285 RecvMsgNotify notify_func, gpointer data)
287 session->recv_msg_notify = notify_func;
288 session->recv_msg_notify_data = data;
291 void session_set_recv_data_progressive_notify
293 RecvDataProgressiveNotify notify_func,
296 session->recv_data_progressive_notify = notify_func,
297 session->recv_data_progressive_notify_data = data;
300 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
303 session->recv_data_notify = notify_func;
304 session->recv_data_notify_data = data;
307 void session_set_send_data_progressive_notify
309 SendDataProgressiveNotify notify_func,
312 session->send_data_progressive_notify = notify_func;
313 session->send_data_progressive_notify_data = data;
316 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
319 session->send_data_notify = notify_func;
320 session->send_data_notify_data = data;
324 *\brief child and parent cleanup (child closes first)
326 *\param session Contains session information
330 static gint session_close(Session *session)
332 cm_return_val_if_fail(session != NULL, -1);
335 if (session->conn_id > 0) {
336 sock_connect_async_cancel(session->conn_id);
337 session->conn_id = 0;
338 debug_print("session (%p): connection cancelled\n", session);
342 session_set_timeout(session, 0);
344 if (session->io_tag > 0) {
345 g_source_remove(session->io_tag);
350 sock_close(session->sock);
351 session->sock = NULL;
352 session->state = SESSION_DISCONNECTED;
353 debug_print("session (%p): closed\n", session);
360 gint session_start_tls(Session *session)
364 nb_mode = sock_is_nonblocking_mode(session->sock);
367 sock_set_nonblocking_mode(session->sock, FALSE);
369 if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
370 g_warning("couldn't start TLS session.\n");
372 sock_set_nonblocking_mode(session->sock, session->nonblocking);
377 sock_set_nonblocking_mode(session->sock, session->nonblocking);
383 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
387 cm_return_val_if_fail(session->write_buf == NULL, -1);
388 cm_return_val_if_fail(msg != NULL, -1);
389 cm_return_val_if_fail(msg[0] != '\0', -1);
391 session->state = SESSION_SEND;
392 session->write_buf = g_strconcat(msg, "\r\n", NULL);
393 session->write_buf_p = session->write_buf;
394 session->write_buf_len = strlen(msg) + 2;
396 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
399 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
400 session_write_msg_cb, session);
401 else if (session->state == SESSION_ERROR)
407 gint session_recv_msg(Session *session)
409 cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
411 session->state = SESSION_RECV;
413 if (session->read_buf_len > 0)
414 g_idle_add(session_recv_msg_idle_cb, session);
416 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
417 session_read_msg_cb, session);
422 static gboolean session_recv_msg_idle_cb(gpointer data)
424 Session *session = SESSION(data);
427 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
430 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
431 session_read_msg_cb, session);
437 *\brief parent (child?): send data to other process
439 *\param session Contains session information
446 gint session_send_data(Session *session, const guchar *data, guint size)
450 cm_return_val_if_fail(session->write_data == NULL, -1);
451 cm_return_val_if_fail(data != NULL, -1);
452 cm_return_val_if_fail(size != 0, -1);
454 session->state = SESSION_SEND;
456 session->write_data = data;
457 session->write_data_p = session->write_data;
458 session->write_data_len = size;
459 g_get_current_time(&session->tv_prev);
461 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
464 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
465 session_write_data_cb,
467 else if (session->state == SESSION_ERROR)
473 gint session_recv_data(Session *session, guint size, const gchar *terminator)
475 cm_return_val_if_fail(session->read_data_buf->len == 0, -1);
477 session->state = SESSION_RECV;
479 g_free(session->read_data_terminator);
480 session->read_data_terminator = g_strdup(terminator);
481 g_get_current_time(&session->tv_prev);
483 if (session->read_buf_len > 0)
484 g_idle_add(session_recv_data_idle_cb, session);
486 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
487 session_read_data_cb, session);
492 static gboolean session_recv_data_idle_cb(gpointer data)
494 Session *session = SESSION(data);
497 ret = session_read_data_cb(session->sock, G_IO_IN, session);
500 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
501 session_read_data_cb, session);
506 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
509 Session *session = SESSION(data);
510 gchar buf[SESSION_BUFFSIZE];
516 cm_return_val_if_fail(condition == G_IO_IN, FALSE);
518 session_set_timeout(session, session->timeout_interval);
520 if (session->read_buf_len == 0) {
523 read_len = sock_read(session->sock, session->read_buf,
524 SESSION_BUFFSIZE - 1);
526 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
527 g_warning ("sock_read: session disconnected\n");
528 if (session->io_tag > 0) {
529 g_source_remove(session->io_tag);
536 g_warning("sock_read: received EOF\n");
537 session->state = SESSION_EOF;
546 g_warning("sock_read: %s\n", g_strerror(errno));
547 session->state = SESSION_ERROR;
552 session->read_buf_len = read_len;
555 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
557 line_len = newline - session->read_buf_p + 1;
559 line_len = session->read_buf_len;
564 memcpy(buf, session->read_buf_p, line_len);
565 buf[line_len] = '\0';
567 g_string_append(session->read_msg_buf, buf);
569 session->read_buf_len -= line_len;
570 if (session->read_buf_len == 0)
571 session->read_buf_p = session->read_buf;
573 session->read_buf_p += line_len;
575 /* incomplete read */
576 if (buf[line_len - 1] != '\n')
580 if (session->io_tag > 0) {
581 g_source_remove(session->io_tag);
586 msg = g_strdup(session->read_msg_buf->str);
588 g_string_truncate(session->read_msg_buf, 0);
590 ret = session->recv_msg(session, msg);
591 session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
596 session->state = SESSION_ERROR;
601 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
604 Session *session = SESSION(data);
605 GByteArray *data_buf;
607 gboolean complete = FALSE;
611 cm_return_val_if_fail(condition == G_IO_IN, FALSE);
613 session_set_timeout(session, session->timeout_interval);
615 if (session->read_buf_len == 0) {
618 read_len = sock_read(session->sock, session->read_buf,
622 g_warning("sock_read: received EOF\n");
623 session->state = SESSION_EOF;
632 g_warning("sock_read: %s\n", g_strerror(errno));
633 session->state = SESSION_ERROR;
638 session->read_buf_len = read_len;
641 data_buf = session->read_data_buf;
642 terminator_len = strlen(session->read_data_terminator);
644 if (session->read_buf_len == 0)
647 g_byte_array_append(data_buf, session->read_buf_p,
648 session->read_buf_len);
650 session->read_buf_len = 0;
651 session->read_buf_p = session->read_buf;
653 /* check if data is terminated */
654 if (data_buf->len >= terminator_len) {
655 if (memcmp(data_buf->data, session->read_data_terminator,
656 terminator_len) == 0)
658 else if (data_buf->len >= terminator_len + 2 &&
659 memcmp(data_buf->data + data_buf->len -
660 (terminator_len + 2), "\r\n", 2) == 0 &&
661 memcmp(data_buf->data + data_buf->len -
662 terminator_len, session->read_data_terminator,
663 terminator_len) == 0)
667 /* incomplete read */
671 g_get_current_time(&tv_cur);
672 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
673 tv_cur.tv_usec - session->tv_prev.tv_usec >
674 UI_REFRESH_INTERVAL) {
675 session->recv_data_progressive_notify
676 (session, data_buf->len, 0,
677 session->recv_data_progressive_notify_data);
678 g_get_current_time(&session->tv_prev);
684 if (session->io_tag > 0) {
685 g_source_remove(session->io_tag);
689 data_len = data_buf->len - terminator_len;
692 ret = session->recv_data_finished(session, (gchar *)data_buf->data,
695 g_byte_array_set_size(data_buf, 0);
697 session->recv_data_notify(session, data_len,
698 session->recv_data_notify_data);
701 session->state = SESSION_ERROR;
706 static gint session_write_buf(Session *session)
711 cm_return_val_if_fail(session->write_buf != NULL, -1);
712 cm_return_val_if_fail(session->write_buf_p != NULL, -1);
713 cm_return_val_if_fail(session->write_buf_len > 0, -1);
715 to_write_len = session->write_buf_len -
716 (session->write_buf_p - session->write_buf);
717 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
719 write_len = sock_write(session->sock, session->write_buf_p,
728 g_warning("sock_write: %s\n", g_strerror(errno));
729 session->state = SESSION_ERROR;
734 /* incomplete write */
735 if (session->write_buf_p - session->write_buf + write_len <
736 session->write_buf_len) {
737 session->write_buf_p += write_len;
741 g_free(session->write_buf);
742 session->write_buf = NULL;
743 session->write_buf_p = NULL;
744 session->write_buf_len = 0;
749 static gint session_write_data(Session *session)
754 cm_return_val_if_fail(session->write_data != NULL, -1);
755 cm_return_val_if_fail(session->write_data_p != NULL, -1);
756 cm_return_val_if_fail(session->write_data_len > 0, -1);
758 to_write_len = session->write_data_len -
759 (session->write_data_p - session->write_data);
760 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
762 write_len = sock_write(session->sock, session->write_data_p,
771 g_warning("sock_write: %s\n", g_strerror(errno));
772 session->state = SESSION_ERROR;
777 /* incomplete write */
778 if (session->write_data_p - session->write_data + write_len <
779 session->write_data_len) {
780 session->write_data_p += write_len;
784 session->write_data = NULL;
785 session->write_data_p = NULL;
786 session->write_data_len = 0;
791 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
794 Session *session = SESSION(data);
797 cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
798 cm_return_val_if_fail(session->write_buf != NULL, FALSE);
799 cm_return_val_if_fail(session->write_buf_p != NULL, FALSE);
800 cm_return_val_if_fail(session->write_buf_len > 0, FALSE);
802 ret = session_write_buf(session);
805 session->state = SESSION_ERROR;
810 if (session->io_tag > 0) {
811 g_source_remove(session->io_tag);
815 session_recv_msg(session);
820 static gboolean session_write_data_cb(SockInfo *source,
821 GIOCondition condition, gpointer data)
823 Session *session = SESSION(data);
824 guint write_data_len;
827 cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
828 cm_return_val_if_fail(session->write_data != NULL, FALSE);
829 cm_return_val_if_fail(session->write_data_p != NULL, FALSE);
830 cm_return_val_if_fail(session->write_data_len > 0, FALSE);
832 write_data_len = session->write_data_len;
834 ret = session_write_data(session);
837 session->state = SESSION_ERROR;
839 } else if (ret > 0) {
842 g_get_current_time(&tv_cur);
843 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
844 tv_cur.tv_usec - session->tv_prev.tv_usec >
845 UI_REFRESH_INTERVAL) {
846 session_set_timeout(session, session->timeout_interval);
847 session->send_data_progressive_notify
849 session->write_data_p - session->write_data,
851 session->send_data_progressive_notify_data);
852 g_get_current_time(&session->tv_prev);
857 if (session->io_tag > 0) {
858 g_source_remove(session->io_tag);
863 ret = session->send_data_finished(session, write_data_len);
864 session->send_data_notify(session, write_data_len,
865 session->send_data_notify_data);