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 2 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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)
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;
103 *\brief Set up parent and child process
104 * Childloop: Read commands from parent,
105 * send to server, get answer, pass to parent
107 *\param session Contains session information
108 * server to connect to
112 * -1 : pipe / fork errors (parent)
113 * 1 : connection error (child)
115 gint session_connect(Session *session, const gchar *server, gushort port)
118 session->server = g_strdup(server);
119 session->port = port;
121 session->conn_id = sock_connect_async(server, port, session_connect_cb,
123 if (session->conn_id < 0) {
124 g_warning("can't connect to server.");
125 session_close(session);
133 session->server = g_strdup(server);
134 session->port = port;
136 sock = sock_connect(server, port);
138 g_warning("can't connect to server.");
139 session_close(session);
143 return session_connect_cb(sock, session);
147 static gint session_connect_cb(SockInfo *sock, gpointer data)
149 Session *session = SESSION(data);
151 session->conn_id = 0;
154 g_warning("can't connect to server.");
155 session->state = SESSION_ERROR;
159 session->sock = sock;
162 if (session->ssl_type == SSL_TUNNEL) {
163 sock_set_nonblocking_mode(sock, FALSE);
164 if (!ssl_init_socket(sock)) {
165 g_warning("can't initialize SSL.");
166 log_error(LOG_PROTOCOL, _("SSL handshake failed\n"));
167 session->state = SESSION_ERROR;
173 sock_set_nonblocking_mode(sock, session->nonblocking);
175 debug_print("session (%p): connected\n", session);
177 session->state = SESSION_RECV;
178 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
186 *\brief child and parent: send DISCONNECT message to other process
188 *\param session Contains session information
192 gint session_disconnect(Session *session)
194 session_close(session);
201 *\param session Contains session information
203 void session_destroy(Session *session)
205 g_return_if_fail(session != NULL);
206 g_return_if_fail(session->destroy != NULL);
208 session_close(session);
209 session->destroy(session);
210 g_free(session->server);
211 g_string_free(session->read_msg_buf, TRUE);
212 g_byte_array_free(session->read_data_buf, TRUE);
213 g_free(session->read_data_terminator);
214 g_free(session->write_buf);
216 debug_print("session (%p): destroyed\n", session);
221 gboolean session_is_connected(Session *session)
223 return (session->state == SESSION_READY ||
224 session->state == SESSION_SEND ||
225 session->state == SESSION_RECV);
228 void session_set_access_time(Session *session)
230 session->last_access_time = time(NULL);
233 void session_set_timeout(Session *session, guint interval)
235 if (session->timeout_tag > 0)
236 g_source_remove(session->timeout_tag);
238 session->timeout_interval = interval;
240 session->timeout_tag =
241 g_timeout_add(interval, session_timeout_cb, session);
243 session->timeout_tag = 0;
246 static gboolean session_timeout_cb(gpointer data)
248 Session *session = SESSION(data);
250 g_warning("session timeout.\n");
252 if (session->io_tag > 0) {
253 g_source_remove(session->io_tag);
257 session->timeout_tag = 0;
258 session->state = SESSION_TIMEOUT;
263 void session_set_recv_message_notify(Session *session,
264 RecvMsgNotify notify_func, gpointer data)
266 session->recv_msg_notify = notify_func;
267 session->recv_msg_notify_data = data;
270 void session_set_recv_data_progressive_notify
272 RecvDataProgressiveNotify notify_func,
275 session->recv_data_progressive_notify = notify_func,
276 session->recv_data_progressive_notify_data = data;
279 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
282 session->recv_data_notify = notify_func;
283 session->recv_data_notify_data = data;
286 void session_set_send_data_progressive_notify
288 SendDataProgressiveNotify notify_func,
291 session->send_data_progressive_notify = notify_func;
292 session->send_data_progressive_notify_data = data;
295 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
298 session->send_data_notify = notify_func;
299 session->send_data_notify_data = data;
303 *\brief child and parent cleanup (child closes first)
305 *\param session Contains session information
309 static gint session_close(Session *session)
311 g_return_val_if_fail(session != NULL, -1);
314 if (session->conn_id > 0) {
315 sock_connect_async_cancel(session->conn_id);
316 session->conn_id = 0;
317 debug_print("session (%p): connection cancelled\n", session);
321 session_set_timeout(session, 0);
323 if (session->io_tag > 0) {
324 g_source_remove(session->io_tag);
329 sock_close(session->sock);
330 session->sock = NULL;
331 session->state = SESSION_DISCONNECTED;
332 debug_print("session (%p): closed\n", session);
339 gint session_start_tls(Session *session)
343 nb_mode = sock_is_nonblocking_mode(session->sock);
346 sock_set_nonblocking_mode(session->sock, FALSE);
348 if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
349 g_warning("couldn't start TLS session.\n");
351 sock_set_nonblocking_mode(session->sock, TRUE);
356 sock_set_nonblocking_mode(session->sock, session->nonblocking);
362 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
366 g_return_val_if_fail(session->write_buf == NULL, -1);
367 g_return_val_if_fail(msg != NULL, -1);
368 g_return_val_if_fail(msg[0] != '\0', -1);
370 session->state = SESSION_SEND;
371 session->write_buf = g_strconcat(msg, "\r\n", NULL);
372 session->write_buf_p = session->write_buf;
373 session->write_buf_len = strlen(msg) + 2;
375 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
378 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
379 session_write_msg_cb, session);
380 else if (session->state == SESSION_ERROR)
386 gint session_recv_msg(Session *session)
388 g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
390 session->state = SESSION_RECV;
392 if (session->read_buf_len > 0)
393 g_idle_add(session_recv_msg_idle_cb, session);
395 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
396 session_read_msg_cb, session);
401 static gboolean session_recv_msg_idle_cb(gpointer data)
403 Session *session = SESSION(data);
406 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
409 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
410 session_read_msg_cb, session);
416 *\brief parent (child?): send data to other process
418 *\param session Contains session information
425 gint session_send_data(Session *session, const guchar *data, guint size)
429 g_return_val_if_fail(session->write_data == NULL, -1);
430 g_return_val_if_fail(data != NULL, -1);
431 g_return_val_if_fail(size != 0, -1);
433 session->state = SESSION_SEND;
435 session->write_data = data;
436 session->write_data_p = session->write_data;
437 session->write_data_len = size;
438 g_get_current_time(&session->tv_prev);
440 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
443 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
444 session_write_data_cb,
446 else if (session->state == SESSION_ERROR)
452 gint session_recv_data(Session *session, guint size, const gchar *terminator)
454 g_return_val_if_fail(session->read_data_buf->len == 0, -1);
456 session->state = SESSION_RECV;
458 g_free(session->read_data_terminator);
459 session->read_data_terminator = g_strdup(terminator);
460 g_get_current_time(&session->tv_prev);
462 if (session->read_buf_len > 0)
463 g_idle_add(session_recv_data_idle_cb, session);
465 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
466 session_read_data_cb, session);
471 static gboolean session_recv_data_idle_cb(gpointer data)
473 Session *session = SESSION(data);
476 ret = session_read_data_cb(session->sock, G_IO_IN, session);
479 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
480 session_read_data_cb, session);
485 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
488 Session *session = SESSION(data);
489 gchar buf[SESSION_BUFFSIZE];
495 g_return_val_if_fail(condition == G_IO_IN, FALSE);
497 session_set_timeout(session, session->timeout_interval);
499 if (session->read_buf_len == 0) {
502 read_len = sock_read(session->sock, session->read_buf,
503 SESSION_BUFFSIZE - 1);
505 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
506 g_warning ("sock_read: session disconnected\n");
507 if (session->io_tag > 0) {
508 g_source_remove(session->io_tag);
515 g_warning("sock_read: received EOF\n");
516 session->state = SESSION_EOF;
525 g_warning("sock_read: %s\n", g_strerror(errno));
526 session->state = SESSION_ERROR;
531 session->read_buf_len = read_len;
534 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
536 line_len = newline - session->read_buf_p + 1;
538 line_len = session->read_buf_len;
543 memcpy(buf, session->read_buf_p, line_len);
544 buf[line_len] = '\0';
546 g_string_append(session->read_msg_buf, buf);
548 session->read_buf_len -= line_len;
549 if (session->read_buf_len == 0)
550 session->read_buf_p = session->read_buf;
552 session->read_buf_p += line_len;
554 /* incomplete read */
555 if (buf[line_len - 1] != '\n')
559 if (session->io_tag > 0) {
560 g_source_remove(session->io_tag);
565 msg = g_strdup(session->read_msg_buf->str);
567 g_string_truncate(session->read_msg_buf, 0);
569 ret = session->recv_msg(session, msg);
570 session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
575 session->state = SESSION_ERROR;
580 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
583 Session *session = SESSION(data);
584 GByteArray *data_buf;
586 gboolean complete = FALSE;
590 g_return_val_if_fail(condition == G_IO_IN, FALSE);
592 session_set_timeout(session, session->timeout_interval);
594 if (session->read_buf_len == 0) {
597 read_len = sock_read(session->sock, session->read_buf,
601 g_warning("sock_read: received EOF\n");
602 session->state = SESSION_EOF;
611 g_warning("sock_read: %s\n", g_strerror(errno));
612 session->state = SESSION_ERROR;
617 session->read_buf_len = read_len;
620 data_buf = session->read_data_buf;
621 terminator_len = strlen(session->read_data_terminator);
623 if (session->read_buf_len == 0)
626 g_byte_array_append(data_buf, session->read_buf_p,
627 session->read_buf_len);
629 session->read_buf_len = 0;
630 session->read_buf_p = session->read_buf;
632 /* check if data is terminated */
633 if (data_buf->len >= terminator_len) {
634 if (memcmp(data_buf->data, session->read_data_terminator,
635 terminator_len) == 0)
637 else if (data_buf->len >= terminator_len + 2 &&
638 memcmp(data_buf->data + data_buf->len -
639 (terminator_len + 2), "\r\n", 2) == 0 &&
640 memcmp(data_buf->data + data_buf->len -
641 terminator_len, session->read_data_terminator,
642 terminator_len) == 0)
646 /* incomplete read */
650 g_get_current_time(&tv_cur);
651 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
652 tv_cur.tv_usec - session->tv_prev.tv_usec >
653 UI_REFRESH_INTERVAL) {
654 session->recv_data_progressive_notify
655 (session, data_buf->len, 0,
656 session->recv_data_progressive_notify_data);
657 g_get_current_time(&session->tv_prev);
663 if (session->io_tag > 0) {
664 g_source_remove(session->io_tag);
668 data_len = data_buf->len - terminator_len;
671 ret = session->recv_data_finished(session, (gchar *)data_buf->data,
674 g_byte_array_set_size(data_buf, 0);
676 session->recv_data_notify(session, data_len,
677 session->recv_data_notify_data);
680 session->state = SESSION_ERROR;
685 static gint session_write_buf(Session *session)
690 g_return_val_if_fail(session->write_buf != NULL, -1);
691 g_return_val_if_fail(session->write_buf_p != NULL, -1);
692 g_return_val_if_fail(session->write_buf_len > 0, -1);
694 to_write_len = session->write_buf_len -
695 (session->write_buf_p - session->write_buf);
696 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
698 write_len = sock_write(session->sock, session->write_buf_p,
707 g_warning("sock_write: %s\n", g_strerror(errno));
708 session->state = SESSION_ERROR;
713 /* incomplete write */
714 if (session->write_buf_p - session->write_buf + write_len <
715 session->write_buf_len) {
716 session->write_buf_p += write_len;
720 g_free(session->write_buf);
721 session->write_buf = NULL;
722 session->write_buf_p = NULL;
723 session->write_buf_len = 0;
728 static gint session_write_data(Session *session)
733 g_return_val_if_fail(session->write_data != NULL, -1);
734 g_return_val_if_fail(session->write_data_p != NULL, -1);
735 g_return_val_if_fail(session->write_data_len > 0, -1);
737 to_write_len = session->write_data_len -
738 (session->write_data_p - session->write_data);
739 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
741 write_len = sock_write(session->sock, session->write_data_p,
750 g_warning("sock_write: %s\n", g_strerror(errno));
751 session->state = SESSION_ERROR;
756 /* incomplete write */
757 if (session->write_data_p - session->write_data + write_len <
758 session->write_data_len) {
759 session->write_data_p += write_len;
763 session->write_data = NULL;
764 session->write_data_p = NULL;
765 session->write_data_len = 0;
770 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
773 Session *session = SESSION(data);
776 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
777 g_return_val_if_fail(session->write_buf != NULL, FALSE);
778 g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
779 g_return_val_if_fail(session->write_buf_len > 0, FALSE);
781 ret = session_write_buf(session);
784 session->state = SESSION_ERROR;
789 if (session->io_tag > 0) {
790 g_source_remove(session->io_tag);
794 session_recv_msg(session);
799 static gboolean session_write_data_cb(SockInfo *source,
800 GIOCondition condition, gpointer data)
802 Session *session = SESSION(data);
803 guint write_data_len;
806 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
807 g_return_val_if_fail(session->write_data != NULL, FALSE);
808 g_return_val_if_fail(session->write_data_p != NULL, FALSE);
809 g_return_val_if_fail(session->write_data_len > 0, FALSE);
811 write_data_len = session->write_data_len;
813 ret = session_write_data(session);
816 session->state = SESSION_ERROR;
818 } else if (ret > 0) {
821 g_get_current_time(&tv_cur);
822 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
823 tv_cur.tv_usec - session->tv_prev.tv_usec >
824 UI_REFRESH_INTERVAL) {
825 session_set_timeout(session, session->timeout_interval);
826 session->send_data_progressive_notify
828 session->write_data_p - session->write_data,
830 session->send_data_progressive_notify_data);
831 g_get_current_time(&session->tv_prev);
836 if (session->io_tag > 0) {
837 g_source_remove(session->io_tag);
842 ret = session->send_data_finished(session, write_data_len);
843 session->send_data_notify(session, write_data_len,
844 session->send_data_notify_data);