2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
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.
38 static gint session_connect_cb (SockInfo *sock,
40 static gint session_close (Session *session);
42 static gboolean session_timeout_cb (gpointer data);
44 static gboolean session_recv_msg_idle_cb (gpointer data);
45 static gboolean session_recv_data_idle_cb (gpointer data);
47 static gboolean session_read_msg_cb (SockInfo *source,
48 GIOCondition condition,
50 static gboolean session_read_data_cb (SockInfo *source,
51 GIOCondition condition,
53 static gboolean session_write_msg_cb (SockInfo *source,
54 GIOCondition condition,
56 static gboolean session_write_data_cb (SockInfo *source,
57 GIOCondition condition,
61 void session_init(Session *session)
63 session->type = SESSION_UNKNOWN;
65 session->server = NULL;
68 session->ssl_type = SSL_NONE;
70 session->nonblocking = TRUE;
71 session->state = SESSION_READY;
72 session->last_access_time = time(NULL);
74 g_get_current_time(&session->tv_prev);
80 session->read_buf_p = session->read_buf;
81 session->read_buf_len = 0;
83 session->read_msg_buf = g_string_sized_new(1024);
84 session->read_data_buf = g_byte_array_new();
86 session->write_buf = NULL;
87 session->write_buf_p = NULL;
88 session->write_buf_len = 0;
90 session->write_data = NULL;
91 session->write_data_p = NULL;
92 session->write_data_len = 0;
94 session->timeout_tag = 0;
95 session->timeout_interval = 0;
101 *\brief Set up parent and child process
102 * Childloop: Read commands from parent,
103 * send to server, get answer, pass to parent
105 *\param session Contains session information
106 * server to connect to
110 * -1 : pipe / fork errors (parent)
111 * 1 : connection error (child)
113 gint session_connect(Session *session, const gchar *server, gushort port)
116 session->server = g_strdup(server);
117 session->port = port;
119 session->conn_id = sock_connect_async(server, port, session_connect_cb,
121 if (session->conn_id < 0) {
122 g_warning("can't connect to server.");
123 session_close(session);
131 session->server = g_strdup(server);
132 session->port = port;
134 sock = sock_connect(server, port);
136 g_warning("can't connect to server.");
137 session_close(session);
141 return session_connect_cb(sock, session);
145 static gint session_connect_cb(SockInfo *sock, gpointer data)
147 Session *session = SESSION(data);
149 session->conn_id = 0;
152 g_warning("can't connect to server.");
153 session->state = SESSION_ERROR;
157 session->sock = sock;
160 if (session->ssl_type == SSL_TUNNEL) {
161 sock_set_nonblocking_mode(sock, FALSE);
162 if (!ssl_init_socket(sock)) {
163 g_warning("can't initialize SSL.");
164 session->state = SESSION_ERROR;
170 sock_set_nonblocking_mode(sock, session->nonblocking);
172 debug_print("session (%p): connected\n", session);
174 session->state = SESSION_RECV;
175 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
183 *\brief child and parent: send DISCONNECT message to other process
185 *\param session Contains session information
189 gint session_disconnect(Session *session)
191 session_close(session);
198 *\param session Contains session information
200 void session_destroy(Session *session)
202 g_return_if_fail(session != NULL);
203 g_return_if_fail(session->destroy != NULL);
205 session_close(session);
206 session->destroy(session);
207 g_free(session->server);
208 g_string_free(session->read_msg_buf, TRUE);
209 g_byte_array_free(session->read_data_buf, TRUE);
210 g_free(session->read_data_terminator);
211 g_free(session->write_buf);
213 debug_print("session (%p): destroyed\n", session);
218 gboolean session_is_connected(Session *session)
220 return (session->state == SESSION_READY ||
221 session->state == SESSION_SEND ||
222 session->state == SESSION_RECV);
225 void session_set_access_time(Session *session)
227 session->last_access_time = time(NULL);
230 void session_set_timeout(Session *session, guint interval)
232 if (session->timeout_tag > 0)
233 g_source_remove(session->timeout_tag);
235 session->timeout_interval = interval;
237 session->timeout_tag =
238 g_timeout_add(interval, session_timeout_cb, session);
240 session->timeout_tag = 0;
243 static gboolean session_timeout_cb(gpointer data)
245 Session *session = SESSION(data);
247 g_warning("session timeout.\n");
249 if (session->io_tag > 0) {
250 g_source_remove(session->io_tag);
254 session->timeout_tag = 0;
255 session->state = SESSION_TIMEOUT;
260 void session_set_recv_message_notify(Session *session,
261 RecvMsgNotify notify_func, gpointer data)
263 session->recv_msg_notify = notify_func;
264 session->recv_msg_notify_data = data;
267 void session_set_recv_data_progressive_notify
269 RecvDataProgressiveNotify notify_func,
272 session->recv_data_progressive_notify = notify_func,
273 session->recv_data_progressive_notify_data = data;
276 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
279 session->recv_data_notify = notify_func;
280 session->recv_data_notify_data = data;
283 void session_set_send_data_progressive_notify
285 SendDataProgressiveNotify notify_func,
288 session->send_data_progressive_notify = notify_func;
289 session->send_data_progressive_notify_data = data;
292 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
295 session->send_data_notify = notify_func;
296 session->send_data_notify_data = data;
300 *\brief child and parent cleanup (child closes first)
302 *\param session Contains session information
306 static gint session_close(Session *session)
308 g_return_val_if_fail(session != NULL, -1);
311 if (session->conn_id > 0) {
312 sock_connect_async_cancel(session->conn_id);
313 session->conn_id = 0;
314 debug_print("session (%p): connection cancelled\n", session);
318 session_set_timeout(session, 0);
320 if (session->io_tag > 0) {
321 g_source_remove(session->io_tag);
326 sock_close(session->sock);
327 session->sock = NULL;
328 session->state = SESSION_DISCONNECTED;
329 debug_print("session (%p): closed\n", session);
336 gint session_start_tls(Session *session)
340 nb_mode = sock_is_nonblocking_mode(session->sock);
343 sock_set_nonblocking_mode(session->sock, FALSE);
345 if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
346 g_warning("can't start TLS session.\n");
348 sock_set_nonblocking_mode(session->sock, TRUE);
353 sock_set_nonblocking_mode(session->sock, session->nonblocking);
359 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
363 g_return_val_if_fail(session->write_buf == NULL, -1);
364 g_return_val_if_fail(msg != NULL, -1);
365 g_return_val_if_fail(msg[0] != '\0', -1);
367 session->state = SESSION_SEND;
368 session->write_buf = g_strconcat(msg, "\r\n", NULL);
369 session->write_buf_p = session->write_buf;
370 session->write_buf_len = strlen(msg) + 2;
372 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
375 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
376 session_write_msg_cb, session);
377 else if (session->state == SESSION_ERROR)
383 gint session_recv_msg(Session *session)
385 g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
387 session->state = SESSION_RECV;
389 if (session->read_buf_len > 0)
390 g_idle_add(session_recv_msg_idle_cb, session);
392 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
393 session_read_msg_cb, session);
398 static gboolean session_recv_msg_idle_cb(gpointer data)
400 Session *session = SESSION(data);
403 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
406 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
407 session_read_msg_cb, session);
413 *\brief parent (child?): send data to other process
415 *\param session Contains session information
422 gint session_send_data(Session *session, const guchar *data, guint size)
426 g_return_val_if_fail(session->write_data == NULL, -1);
427 g_return_val_if_fail(data != NULL, -1);
428 g_return_val_if_fail(size != 0, -1);
430 session->state = SESSION_SEND;
432 session->write_data = data;
433 session->write_data_p = session->write_data;
434 session->write_data_len = size;
435 g_get_current_time(&session->tv_prev);
437 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
440 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
441 session_write_data_cb,
443 else if (session->state == SESSION_ERROR)
449 gint session_recv_data(Session *session, guint size, const gchar *terminator)
451 g_return_val_if_fail(session->read_data_buf->len == 0, -1);
453 session->state = SESSION_RECV;
455 g_free(session->read_data_terminator);
456 session->read_data_terminator = g_strdup(terminator);
457 g_get_current_time(&session->tv_prev);
459 if (session->read_buf_len > 0)
460 g_idle_add(session_recv_data_idle_cb, session);
462 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
463 session_read_data_cb, session);
468 static gboolean session_recv_data_idle_cb(gpointer data)
470 Session *session = SESSION(data);
473 ret = session_read_data_cb(session->sock, G_IO_IN, session);
476 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
477 session_read_data_cb, session);
482 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
485 Session *session = SESSION(data);
486 gchar buf[SESSION_BUFFSIZE];
492 g_return_val_if_fail(condition == G_IO_IN, FALSE);
494 session_set_timeout(session, session->timeout_interval);
496 if (session->read_buf_len == 0) {
499 read_len = sock_read(session->sock, session->read_buf,
500 SESSION_BUFFSIZE - 1);
502 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
503 g_warning ("sock_read: session disconnected\n");
504 if (session->io_tag > 0) {
505 g_source_remove(session->io_tag);
512 g_warning("sock_read: received EOF\n");
513 session->state = SESSION_EOF;
522 g_warning("sock_read: %s\n", g_strerror(errno));
523 session->state = SESSION_ERROR;
528 session->read_buf_len = read_len;
531 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
533 line_len = newline - session->read_buf_p + 1;
535 line_len = session->read_buf_len;
540 memcpy(buf, session->read_buf_p, line_len);
541 buf[line_len] = '\0';
543 g_string_append(session->read_msg_buf, buf);
545 session->read_buf_len -= line_len;
546 if (session->read_buf_len == 0)
547 session->read_buf_p = session->read_buf;
549 session->read_buf_p += line_len;
551 /* incomplete read */
552 if (buf[line_len - 1] != '\n')
556 if (session->io_tag > 0) {
557 g_source_remove(session->io_tag);
562 msg = g_strdup(session->read_msg_buf->str);
564 g_string_truncate(session->read_msg_buf, 0);
566 ret = session->recv_msg(session, msg);
567 session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
572 session->state = SESSION_ERROR;
577 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
580 Session *session = SESSION(data);
581 GByteArray *data_buf;
583 gboolean complete = FALSE;
587 g_return_val_if_fail(condition == G_IO_IN, FALSE);
589 session_set_timeout(session, session->timeout_interval);
591 if (session->read_buf_len == 0) {
594 read_len = sock_read(session->sock, session->read_buf,
598 g_warning("sock_read: received EOF\n");
599 session->state = SESSION_EOF;
608 g_warning("sock_read: %s\n", g_strerror(errno));
609 session->state = SESSION_ERROR;
614 session->read_buf_len = read_len;
617 data_buf = session->read_data_buf;
618 terminator_len = strlen(session->read_data_terminator);
620 if (session->read_buf_len == 0)
623 g_byte_array_append(data_buf, session->read_buf_p,
624 session->read_buf_len);
626 session->read_buf_len = 0;
627 session->read_buf_p = session->read_buf;
629 /* check if data is terminated */
630 if (data_buf->len >= terminator_len) {
631 if (memcmp(data_buf->data, session->read_data_terminator,
632 terminator_len) == 0)
634 else if (data_buf->len >= terminator_len + 2 &&
635 memcmp(data_buf->data + data_buf->len -
636 (terminator_len + 2), "\r\n", 2) == 0 &&
637 memcmp(data_buf->data + data_buf->len -
638 terminator_len, session->read_data_terminator,
639 terminator_len) == 0)
643 /* incomplete read */
647 g_get_current_time(&tv_cur);
648 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
649 tv_cur.tv_usec - session->tv_prev.tv_usec >
650 UI_REFRESH_INTERVAL) {
651 session->recv_data_progressive_notify
652 (session, data_buf->len, 0,
653 session->recv_data_progressive_notify_data);
654 g_get_current_time(&session->tv_prev);
660 if (session->io_tag > 0) {
661 g_source_remove(session->io_tag);
665 data_len = data_buf->len - terminator_len;
668 ret = session->recv_data_finished(session, (gchar *)data_buf->data,
671 g_byte_array_set_size(data_buf, 0);
673 session->recv_data_notify(session, data_len,
674 session->recv_data_notify_data);
677 session->state = SESSION_ERROR;
682 static gint session_write_buf(Session *session)
687 g_return_val_if_fail(session->write_buf != NULL, -1);
688 g_return_val_if_fail(session->write_buf_p != NULL, -1);
689 g_return_val_if_fail(session->write_buf_len > 0, -1);
691 to_write_len = session->write_buf_len -
692 (session->write_buf_p - session->write_buf);
693 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
695 write_len = sock_write(session->sock, session->write_buf_p,
704 g_warning("sock_write: %s\n", g_strerror(errno));
705 session->state = SESSION_ERROR;
710 /* incomplete write */
711 if (session->write_buf_p - session->write_buf + write_len <
712 session->write_buf_len) {
713 session->write_buf_p += write_len;
717 g_free(session->write_buf);
718 session->write_buf = NULL;
719 session->write_buf_p = NULL;
720 session->write_buf_len = 0;
725 static gint session_write_data(Session *session)
730 g_return_val_if_fail(session->write_data != NULL, -1);
731 g_return_val_if_fail(session->write_data_p != NULL, -1);
732 g_return_val_if_fail(session->write_data_len > 0, -1);
734 to_write_len = session->write_data_len -
735 (session->write_data_p - session->write_data);
736 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
738 write_len = sock_write(session->sock, session->write_data_p,
747 g_warning("sock_write: %s\n", g_strerror(errno));
748 session->state = SESSION_ERROR;
753 /* incomplete write */
754 if (session->write_data_p - session->write_data + write_len <
755 session->write_data_len) {
756 session->write_data_p += write_len;
760 session->write_data = NULL;
761 session->write_data_p = NULL;
762 session->write_data_len = 0;
767 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
770 Session *session = SESSION(data);
773 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
774 g_return_val_if_fail(session->write_buf != NULL, FALSE);
775 g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
776 g_return_val_if_fail(session->write_buf_len > 0, FALSE);
778 ret = session_write_buf(session);
781 session->state = SESSION_ERROR;
786 if (session->io_tag > 0) {
787 g_source_remove(session->io_tag);
791 session_recv_msg(session);
796 static gboolean session_write_data_cb(SockInfo *source,
797 GIOCondition condition, gpointer data)
799 Session *session = SESSION(data);
800 guint write_data_len;
803 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
804 g_return_val_if_fail(session->write_data != NULL, FALSE);
805 g_return_val_if_fail(session->write_data_p != NULL, FALSE);
806 g_return_val_if_fail(session->write_data_len > 0, FALSE);
808 write_data_len = session->write_data_len;
810 ret = session_write_data(session);
813 session->state = SESSION_ERROR;
815 } else if (ret > 0) {
818 g_get_current_time(&tv_cur);
819 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
820 tv_cur.tv_usec - session->tv_prev.tv_usec >
821 UI_REFRESH_INTERVAL) {
822 session_set_timeout(session, session->timeout_interval);
823 session->send_data_progressive_notify
825 session->write_data_p - session->write_data,
827 session->send_data_progressive_notify_data);
828 g_get_current_time(&session->tv_prev);
833 if (session->io_tag > 0) {
834 g_source_remove(session->io_tag);
839 ret = session->send_data_finished(session, write_data_len);
840 session->send_data_notify(session, write_data_len,
841 session->send_data_notify_data);