2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2012 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/>.
22 #include "claws-features.h"
28 #include <glib/gi18n.h>
41 static gint session_connect_cb (SockInfo *sock,
43 static gint session_close (Session *session);
45 static gboolean session_timeout_cb (gpointer data);
47 static gboolean session_recv_msg_idle_cb (gpointer data);
48 static gboolean session_recv_data_idle_cb (gpointer data);
50 static gboolean session_read_msg_cb (SockInfo *source,
51 GIOCondition condition,
53 static gboolean session_read_data_cb (SockInfo *source,
54 GIOCondition condition,
56 static gboolean session_write_msg_cb (SockInfo *source,
57 GIOCondition condition,
59 static gboolean session_write_data_cb (SockInfo *source,
60 GIOCondition condition,
64 void session_init(Session *session, const void *prefs_account, gboolean is_smtp)
66 session->type = SESSION_UNKNOWN;
68 session->server = NULL;
71 session->ssl_type = SSL_NONE;
72 session->use_tls_sni = TRUE;
74 session->nonblocking = TRUE;
75 session->state = SESSION_READY;
76 session->last_access_time = time(NULL);
78 #if GLIB_CHECK_VERSION(2,61,2)
79 session->tv_prev = g_date_time_new_now_local();
81 g_get_current_time(&session->tv_prev);
88 session->read_buf_p = session->read_buf;
89 session->read_buf_len = 0;
91 session->read_msg_buf = g_string_sized_new(1024);
92 session->read_data_buf = g_byte_array_new();
94 session->write_buf = NULL;
95 session->write_buf_p = NULL;
96 session->write_buf_len = 0;
98 session->write_data = NULL;
99 session->write_data_p = NULL;
100 session->write_data_len = 0;
102 session->timeout_tag = 0;
103 session->timeout_interval = 0;
105 session->data = NULL;
106 session->account = prefs_account;
107 session->is_smtp = is_smtp;
109 session->ping_tag = -1;
111 session->proxy_info = NULL;
115 *\brief Set up parent and child process
116 * Childloop: Read commands from parent,
117 * send to server, get answer, pass to parent
119 *\param session Contains session information
120 * server to connect to
124 * -1 : pipe / fork errors (parent)
125 * 1 : connection error (child)
127 gint session_connect(Session *session, const gchar *server, gushort port)
129 session->server = g_strdup(server);
130 session->port = port;
132 if (session->proxy_info) {
133 server = session->proxy_info->proxy_host;
134 port = session->proxy_info->proxy_port;
138 session->conn_id = sock_connect_async(server, port, session_connect_cb,
140 if (session->conn_id < 0) {
141 g_warning("can't connect to server.");
142 session_close(session);
143 if (session->connect_finished)
144 session->connect_finished(session, FALSE);
152 sock = sock_connect(server, port);
154 g_warning("can't connect to server.");
155 session_close(session);
156 if (session->connect_finished)
157 session->connect_finished(session, FALSE);
160 sock->is_smtp = session->is_smtp;
162 return session_connect_cb(sock, session);
166 static gint session_connect_cb(SockInfo *sock, gpointer data)
168 Session *session = SESSION(data);
170 session->conn_id = 0;
173 g_warning("can't connect to server.");
174 session->state = SESSION_ERROR;
175 if (session->connect_finished)
176 session->connect_finished(session, FALSE);
180 session->sock = sock;
181 sock->account = session->account;
182 sock->is_smtp = session->is_smtp;
183 sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
185 if (session->proxy_info) {
186 debug_print("connecting through socks\n");
187 sock_set_nonblocking_mode(sock, FALSE);
188 if (proxy_connect(sock, session->server, session->port,
189 session->proxy_info) < 0) {
190 g_warning("can't establish SOCKS connection.");
191 session->state = SESSION_ERROR;
198 sock->gnutls_priority = session->gnutls_priority;
199 sock->use_tls_sni = session->use_tls_sni;
201 if (session->ssl_type == SSL_TUNNEL) {
202 sock_set_nonblocking_mode(sock, FALSE);
203 if (!ssl_init_socket(sock)) {
204 g_warning("can't initialize SSL/TLS.");
205 log_error(LOG_PROTOCOL, _("SSL/TLS handshake failed\n"));
206 session->state = SESSION_ERROR;
207 if (session->connect_finished)
208 session->connect_finished(session, FALSE);
214 /* we could have gotten a timeout while waiting for user input in
215 * an SSL certificate dialog */
216 if (session->state == SESSION_TIMEOUT) {
217 if (session->connect_finished)
218 session->connect_finished(session, FALSE);
222 sock_set_nonblocking_mode(sock, session->nonblocking);
224 debug_print("session (%p): connected\n", session);
226 session->state = SESSION_RECV;
227 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
231 if (session->connect_finished)
232 session->connect_finished(session, TRUE);
237 *\brief child and parent: send DISCONNECT message to other process
239 *\param session Contains session information
243 gint session_disconnect(Session *session)
245 session_close(session);
252 *\param session Contains session information
254 void session_destroy(Session *session)
256 cm_return_if_fail(session != NULL);
257 cm_return_if_fail(session->destroy != NULL);
259 session_register_ping(session, NULL);
261 session_close(session);
262 session->destroy(session);
263 g_free(session->server);
264 g_string_free(session->read_msg_buf, TRUE);
265 g_byte_array_free(session->read_data_buf, TRUE);
266 g_free(session->read_data_terminator);
267 g_free(session->write_buf);
269 g_free(session->gnutls_priority);
271 #if GLIB_CHECK_VERSION(2,61,2)
272 g_date_time_unref(session->tv_prev);
274 debug_print("session (%p): destroyed\n", session);
279 gboolean session_is_running(Session *session)
281 return (session->state == SESSION_READY ||
282 session->state == SESSION_SEND ||
283 session->state == SESSION_RECV);
286 gboolean session_is_connected(Session *session)
288 return (session->state == SESSION_SEND ||
289 session->state == SESSION_RECV);
292 void session_set_access_time(Session *session)
294 session->last_access_time = time(NULL);
297 void session_set_timeout(Session *session, guint interval)
299 if (session->timeout_tag > 0)
300 g_source_remove(session->timeout_tag);
302 session->timeout_interval = interval;
304 if (interval % 1000 == 0)
305 session->timeout_tag =
306 g_timeout_add_seconds(interval/1000, session_timeout_cb, session);
308 session->timeout_tag =
309 g_timeout_add(interval, session_timeout_cb, session);
311 session->timeout_tag = 0;
314 static gboolean session_timeout_cb(gpointer data)
316 Session *session = SESSION(data);
318 g_warning("session timeout.");
320 if (session->io_tag > 0) {
321 g_source_remove(session->io_tag);
325 session->timeout_tag = 0;
326 session->state = SESSION_TIMEOUT;
331 void session_set_recv_message_notify(Session *session,
332 RecvMsgNotify notify_func, gpointer data)
334 session->recv_msg_notify = notify_func;
335 session->recv_msg_notify_data = data;
338 void session_set_recv_data_progressive_notify
340 RecvDataProgressiveNotify notify_func,
343 session->recv_data_progressive_notify = notify_func,
344 session->recv_data_progressive_notify_data = data;
347 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
350 session->recv_data_notify = notify_func;
351 session->recv_data_notify_data = data;
354 void session_set_send_data_progressive_notify
356 SendDataProgressiveNotify notify_func,
359 session->send_data_progressive_notify = notify_func;
360 session->send_data_progressive_notify_data = data;
363 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
366 session->send_data_notify = notify_func;
367 session->send_data_notify_data = data;
371 *\brief child and parent cleanup (child closes first)
373 *\param session Contains session information
377 static gint session_close(Session *session)
379 cm_return_val_if_fail(session != NULL, -1);
382 if (session->conn_id > 0) {
383 sock_connect_async_cancel(session->conn_id);
384 session->conn_id = 0;
385 debug_print("session (%p): connection cancelled\n", session);
389 session_set_timeout(session, 0);
391 if (session->io_tag > 0) {
392 g_source_remove(session->io_tag);
397 sock_close(session->sock, TRUE);
398 session->sock = NULL;
399 session->state = SESSION_DISCONNECTED;
400 debug_print("session (%p): closed\n", session);
407 gint session_start_tls(Session *session)
411 nb_mode = sock_is_nonblocking_mode(session->sock);
413 session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
414 session->sock->gnutls_priority = session->gnutls_priority;
415 session->sock->use_tls_sni = session->use_tls_sni;
418 sock_set_nonblocking_mode(session->sock, FALSE);
420 if (!ssl_init_socket(session->sock)) {
421 g_warning("couldn't start STARTTLS session.");
423 sock_set_nonblocking_mode(session->sock, session->nonblocking);
428 sock_set_nonblocking_mode(session->sock, session->nonblocking);
434 gint session_send_msg(Session *session, const gchar *msg)
438 cm_return_val_if_fail(session->write_buf == NULL, -1);
439 cm_return_val_if_fail(msg != NULL, -1);
441 session->state = SESSION_SEND;
442 session->write_buf = g_strconcat((strlen(msg) > 0 ? msg : ""), "\r\n", NULL);
443 session->write_buf_p = session->write_buf;
444 session->write_buf_len = strlen(msg) + 2;
446 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
449 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
450 session_write_msg_cb, session);
451 else if (session->state == SESSION_ERROR)
457 gint session_recv_msg(Session *session)
459 cm_return_val_if_fail(session->read_msg_buf->len == 0, -1);
461 session->state = SESSION_RECV;
463 if (session->read_buf_len > 0)
464 g_idle_add(session_recv_msg_idle_cb, session);
466 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
467 session_read_msg_cb, session);
472 static gboolean session_recv_msg_idle_cb(gpointer data)
474 Session *session = SESSION(data);
477 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
480 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
481 session_read_msg_cb, session);
487 *\brief parent (child?): send data to other process
489 *\param session Contains session information
496 gint session_send_data(Session *session, const guchar *data, guint size)
500 cm_return_val_if_fail(session->write_data == NULL, -1);
501 cm_return_val_if_fail(data != NULL, -1);
502 cm_return_val_if_fail(size != 0, -1);
504 session->state = SESSION_SEND;
506 session->write_data = data;
507 session->write_data_p = session->write_data;
508 session->write_data_len = size;
509 #if GLIB_CHECK_VERSION(2,61,2)
510 g_date_time_unref(session->tv_prev);
511 session->tv_prev = g_date_time_new_now_local();
513 g_get_current_time(&session->tv_prev);
516 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
519 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
520 session_write_data_cb,
522 else if (session->state == SESSION_ERROR)
528 gint session_recv_data(Session *session, guint size, const gchar *terminator)
530 cm_return_val_if_fail(session->read_data_buf->len == 0, -1);
532 session->state = SESSION_RECV;
534 g_free(session->read_data_terminator);
535 session->read_data_terminator = g_strdup(terminator);
536 #if GLIB_CHECK_VERSION(2,61,2)
537 g_date_time_unref(session->tv_prev);
538 session->tv_prev = g_date_time_new_now_local();
540 g_get_current_time(&session->tv_prev);
543 if (session->read_buf_len > 0)
544 g_idle_add(session_recv_data_idle_cb, session);
546 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
547 session_read_data_cb, session);
552 static gboolean session_recv_data_idle_cb(gpointer data)
554 Session *session = SESSION(data);
557 ret = session_read_data_cb(session->sock, G_IO_IN, session);
560 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
561 session_read_data_cb, session);
566 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
569 Session *session = SESSION(data);
570 gchar buf[SESSION_BUFFSIZE];
576 cm_return_val_if_fail(condition == G_IO_IN, FALSE);
578 session_set_timeout(session, session->timeout_interval);
580 if (session->read_buf_len == 0) {
584 read_len = sock_read(session->sock, session->read_buf,
585 SESSION_BUFFSIZE - 1);
587 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
588 g_warning ("sock_read: session disconnected");
589 if (session->io_tag > 0) {
590 g_source_remove(session->io_tag);
597 g_warning("sock_read: received EOF");
598 session->state = SESSION_EOF;
607 g_warning("sock_read: %s", g_strerror(errno));
608 session->state = SESSION_ERROR;
613 session->read_buf_len = read_len;
616 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
618 line_len = newline - session->read_buf_p + 1;
620 line_len = session->read_buf_len;
625 memcpy(buf, session->read_buf_p, line_len);
626 buf[line_len] = '\0';
628 g_string_append(session->read_msg_buf, buf);
630 session->read_buf_len -= line_len;
631 if (session->read_buf_len == 0)
632 session->read_buf_p = session->read_buf;
634 session->read_buf_p += line_len;
636 /* incomplete read */
637 if (buf[line_len - 1] != '\n')
641 if (session->io_tag > 0) {
642 g_source_remove(session->io_tag);
647 msg = g_strdup(session->read_msg_buf->str);
649 g_string_truncate(session->read_msg_buf, 0);
651 ret = session->recv_msg(session, msg);
652 session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
657 session->state = SESSION_ERROR;
662 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
665 Session *session = SESSION(data);
666 GByteArray *data_buf;
668 gboolean complete = FALSE;
672 cm_return_val_if_fail(condition == G_IO_IN, FALSE);
674 session_set_timeout(session, session->timeout_interval);
676 if (session->read_buf_len == 0) {
679 read_len = sock_read(session->sock, session->read_buf,
683 g_warning("sock_read: received EOF");
684 session->state = SESSION_EOF;
693 g_warning("sock_read: %s", g_strerror(errno));
694 session->state = SESSION_ERROR;
699 session->read_buf_len = read_len;
702 data_buf = session->read_data_buf;
703 terminator_len = strlen(session->read_data_terminator);
705 if (session->read_buf_len == 0)
708 g_byte_array_append(data_buf, session->read_buf_p,
709 session->read_buf_len);
711 session->read_buf_len = 0;
712 session->read_buf_p = session->read_buf;
714 /* check if data is terminated */
715 if (data_buf->len >= terminator_len) {
716 if (memcmp(data_buf->data, session->read_data_terminator,
717 terminator_len) == 0)
719 else if (data_buf->len >= terminator_len + 2 &&
720 memcmp(data_buf->data + data_buf->len -
721 (terminator_len + 2), "\r\n", 2) == 0 &&
722 memcmp(data_buf->data + data_buf->len -
723 terminator_len, session->read_data_terminator,
724 terminator_len) == 0)
728 /* incomplete read */
730 #if GLIB_CHECK_VERSION(2,61,2)
731 GDateTime *tv_cur = g_date_time_new_now_local();
733 GTimeSpan ts = g_date_time_difference(tv_cur, session->tv_prev);
734 if (1000 - ts < 0 || ts > UI_REFRESH_INTERVAL) {
735 session->recv_data_progressive_notify
736 (session, data_buf->len, 0,
737 session->recv_data_progressive_notify_data);
738 g_date_time_unref(session->tv_prev);
739 session->tv_prev = g_date_time_new_now_local();
741 g_date_time_unref(tv_cur);
745 g_get_current_time(&tv_cur);
746 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
747 tv_cur.tv_usec - session->tv_prev.tv_usec >
748 UI_REFRESH_INTERVAL) {
749 session->recv_data_progressive_notify
750 (session, data_buf->len, 0,
751 session->recv_data_progressive_notify_data);
752 g_get_current_time(&session->tv_prev);
759 if (session->io_tag > 0) {
760 g_source_remove(session->io_tag);
764 data_len = data_buf->len - terminator_len;
767 ret = session->recv_data_finished(session, (gchar *)data_buf->data,
770 g_byte_array_set_size(data_buf, 0);
772 session->recv_data_notify(session, data_len,
773 session->recv_data_notify_data);
776 session->state = SESSION_ERROR;
781 static gint session_write_buf(Session *session)
786 cm_return_val_if_fail(session->write_buf != NULL, -1);
787 cm_return_val_if_fail(session->write_buf_p != NULL, -1);
788 cm_return_val_if_fail(session->write_buf_len > 0, -1);
790 to_write_len = session->write_buf_len -
791 (session->write_buf_p - session->write_buf);
792 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
794 write_len = sock_write(session->sock, session->write_buf_p,
803 g_warning("sock_write: %s", g_strerror(errno));
804 session->state = SESSION_ERROR;
809 /* incomplete write */
810 if (session->write_buf_p - session->write_buf + write_len <
811 session->write_buf_len) {
812 session->write_buf_p += write_len;
816 g_free(session->write_buf);
817 session->write_buf = NULL;
818 session->write_buf_p = NULL;
819 session->write_buf_len = 0;
824 static gint session_write_data(Session *session)
829 cm_return_val_if_fail(session->write_data != NULL, -1);
830 cm_return_val_if_fail(session->write_data_p != NULL, -1);
831 cm_return_val_if_fail(session->write_data_len > 0, -1);
833 to_write_len = session->write_data_len -
834 (session->write_data_p - session->write_data);
835 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
837 write_len = sock_write(session->sock, session->write_data_p,
846 g_warning("sock_write: %s", g_strerror(errno));
847 session->state = SESSION_ERROR;
852 /* incomplete write */
853 if (session->write_data_p - session->write_data + write_len <
854 session->write_data_len) {
855 session->write_data_p += write_len;
859 session->write_data = NULL;
860 session->write_data_p = NULL;
861 session->write_data_len = 0;
866 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
869 Session *session = SESSION(data);
872 cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
873 cm_return_val_if_fail(session->write_buf != NULL, FALSE);
874 cm_return_val_if_fail(session->write_buf_p != NULL, FALSE);
875 cm_return_val_if_fail(session->write_buf_len > 0, FALSE);
877 ret = session_write_buf(session);
880 session->state = SESSION_ERROR;
885 if (session->io_tag > 0) {
886 g_source_remove(session->io_tag);
890 session_recv_msg(session);
895 static gboolean session_write_data_cb(SockInfo *source,
896 GIOCondition condition, gpointer data)
898 Session *session = SESSION(data);
899 guint write_data_len;
902 cm_return_val_if_fail(condition == G_IO_OUT, FALSE);
903 cm_return_val_if_fail(session->write_data != NULL, FALSE);
904 cm_return_val_if_fail(session->write_data_p != NULL, FALSE);
905 cm_return_val_if_fail(session->write_data_len > 0, FALSE);
907 write_data_len = session->write_data_len;
909 ret = session_write_data(session);
912 session->state = SESSION_ERROR;
914 } else if (ret > 0) {
915 #if GLIB_CHECK_VERSION(2,61,2)
916 GDateTime *tv_cur = g_date_time_new_now_local();
918 GTimeSpan ts = g_date_time_difference(tv_cur, session->tv_prev);
919 if (1000 - ts < 0 || ts > UI_REFRESH_INTERVAL) {
920 session_set_timeout(session, session->timeout_interval);
921 session->send_data_progressive_notify
923 session->write_data_p - session->write_data,
925 session->send_data_progressive_notify_data);
926 g_date_time_unref(session->tv_prev);
927 session->tv_prev = g_date_time_new_now_local();
929 g_date_time_unref(tv_cur);
933 g_get_current_time(&tv_cur);
934 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
935 tv_cur.tv_usec - session->tv_prev.tv_usec >
936 UI_REFRESH_INTERVAL) {
937 session_set_timeout(session, session->timeout_interval);
938 session->send_data_progressive_notify
940 session->write_data_p - session->write_data,
942 session->send_data_progressive_notify_data);
943 g_get_current_time(&session->tv_prev);
950 if (session->io_tag > 0) {
951 g_source_remove(session->io_tag);
956 ret = session->send_data_finished(session, write_data_len);
957 session->send_data_notify(session, write_data_len,
958 session->send_data_notify_data);
963 void session_register_ping(Session *session, gboolean (*ping_cb)(gpointer data))
967 if (session->ping_tag > -1)
968 g_source_remove(session->ping_tag);
970 session->ping_tag = -1;
973 session->ping_tag = g_timeout_add_seconds(60, ping_cb, session);