2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #include <sys/types.h>
33 #include <sys/signal.h>
42 static gint session_connect_cb (SockInfo *sock,
44 static gint session_close (Session *session);
46 static gboolean session_timeout_cb (gpointer data);
48 static gboolean session_recv_msg_idle_cb (gpointer data);
49 static gboolean session_recv_data_idle_cb (gpointer data);
51 static gboolean session_read_msg_cb (SockInfo *source,
52 GIOCondition condition,
54 static gboolean session_read_data_cb (SockInfo *source,
55 GIOCondition condition,
57 static gboolean session_write_msg_cb (SockInfo *source,
58 GIOCondition condition,
60 static gboolean session_write_data_cb (SockInfo *source,
61 GIOCondition condition,
65 void session_init(Session *session)
67 session->type = SESSION_UNKNOWN;
69 session->server = NULL;
72 session->ssl_type = SSL_NONE;
74 session->nonblocking = TRUE;
75 session->state = SESSION_READY;
76 session->last_access_time = time(NULL);
78 gettimeofday(&session->tv_prev, NULL);
84 session->read_buf_p = session->read_buf;
85 session->read_buf_len = 0;
87 session->read_msg_buf = g_string_sized_new(1024);
88 session->read_data_buf = g_byte_array_new();
90 session->write_buf = NULL;
91 session->write_buf_p = NULL;
92 session->write_buf_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)
115 session->server = g_strdup(server);
116 session->port = port;
118 session->conn_id = sock_connect_async(server, port, session_connect_cb,
120 if (session->conn_id < 0) {
121 g_warning("can't connect to server.");
122 session_close(session);
129 static gint session_connect_cb(SockInfo *sock, gpointer data)
131 Session *session = SESSION(data);
133 session->conn_id = 0;
136 g_warning("can't connect to server.");
137 session->state = SESSION_ERROR;
141 session->sock = sock;
144 if (session->ssl_type == SSL_TUNNEL) {
145 sock_set_nonblocking_mode(sock, FALSE);
146 if (!ssl_init_socket(sock)) {
147 g_warning("can't initialize SSL.");
148 session->state = SESSION_ERROR;
154 sock_set_nonblocking_mode(sock, session->nonblocking);
156 debug_print("session (%p): connected\n", session);
158 session->state = SESSION_RECV;
159 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
167 *\brief child and parent: send DISCONNECT message to other process
169 *\param session Contains session information
173 gint session_disconnect(Session *session)
175 session_close(session);
182 *\param session Contains session information
184 void session_destroy(Session *session)
186 g_return_if_fail(session != NULL);
187 g_return_if_fail(session->destroy != NULL);
189 session_close(session);
190 session->destroy(session);
191 g_free(session->server);
192 g_string_free(session->read_msg_buf, TRUE);
193 g_byte_array_free(session->read_data_buf, TRUE);
194 g_free(session->read_data_terminator);
195 g_free(session->write_buf);
197 debug_print("session (%p): destroyed\n", session);
202 gboolean session_is_connected(Session *session)
204 return (session->state == SESSION_READY ||
205 session->state == SESSION_SEND ||
206 session->state == SESSION_RECV);
209 void session_set_access_time(Session *session)
211 session->last_access_time = time(NULL);
214 void session_set_timeout(Session *session, guint interval)
216 if (session->timeout_tag > 0)
217 g_source_remove(session->timeout_tag);
219 session->timeout_interval = interval;
221 session->timeout_tag =
222 g_timeout_add(interval, session_timeout_cb, session);
224 session->timeout_tag = 0;
227 static gboolean session_timeout_cb(gpointer data)
229 Session *session = SESSION(data);
231 g_warning("session timeout.\n");
233 if (session->io_tag > 0) {
234 g_source_remove(session->io_tag);
238 session->timeout_tag = 0;
239 session->state = SESSION_TIMEOUT;
244 void session_set_recv_message_notify(Session *session,
245 RecvMsgNotify notify_func, gpointer data)
247 session->recv_msg_notify = notify_func;
248 session->recv_msg_notify_data = data;
251 void session_set_recv_data_progressive_notify
253 RecvDataProgressiveNotify notify_func,
256 session->recv_data_progressive_notify = notify_func,
257 session->recv_data_progressive_notify_data = data;
260 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
263 session->recv_data_notify = notify_func;
264 session->recv_data_notify_data = data;
267 void session_set_send_data_progressive_notify
269 SendDataProgressiveNotify notify_func,
272 session->send_data_progressive_notify = notify_func;
273 session->send_data_progressive_notify_data = data;
276 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
279 session->send_data_notify = notify_func;
280 session->send_data_notify_data = data;
284 *\brief child and parent cleanup (child closes first)
286 *\param session Contains session information
290 static gint session_close(Session *session)
292 g_return_val_if_fail(session != NULL, -1);
294 if (session->conn_id > 0) {
295 sock_connect_async_cancel(session->conn_id);
296 session->conn_id = 0;
297 debug_print("session (%p): connection cancelled\n", session);
300 session_set_timeout(session, 0);
302 if (session->io_tag > 0) {
303 g_source_remove(session->io_tag);
308 sock_close(session->sock);
309 session->sock = NULL;
310 session->state = SESSION_DISCONNECTED;
311 debug_print("session (%p): closed\n", session);
318 gint session_start_tls(Session *session)
322 nb_mode = sock_is_nonblocking_mode(session->sock);
325 sock_set_nonblocking_mode(session->sock, FALSE);
327 if (!ssl_init_socket_with_method(session->sock, SSL_METHOD_TLSv1)) {
328 g_warning("can't start TLS session.\n");
330 sock_set_nonblocking_mode(session->sock, TRUE);
335 sock_set_nonblocking_mode(session->sock, session->nonblocking);
341 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
345 g_return_val_if_fail(session->write_buf == NULL, -1);
346 g_return_val_if_fail(msg != NULL, -1);
347 g_return_val_if_fail(msg[0] != '\0', -1);
349 session->state = SESSION_SEND;
350 session->write_buf = g_strconcat(msg, "\r\n", NULL);
351 session->write_buf_p = session->write_buf;
352 session->write_buf_len = strlen(msg) + 2;
354 ret = session_write_msg_cb(session->sock, G_IO_OUT, session);
357 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
358 session_write_msg_cb, session);
359 else if (session->state == SESSION_ERROR)
365 gint session_recv_msg(Session *session)
367 g_return_val_if_fail(session->read_msg_buf->len == 0, -1);
369 session->state = SESSION_RECV;
371 if (session->read_buf_len > 0)
372 g_idle_add(session_recv_msg_idle_cb, session);
374 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
375 session_read_msg_cb, session);
380 static gboolean session_recv_msg_idle_cb(gpointer data)
382 Session *session = SESSION(data);
385 ret = session_read_msg_cb(session->sock, G_IO_IN, session);
388 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
389 session_read_msg_cb, session);
395 *\brief parent (child?): send data to other process
397 *\param session Contains session information
404 gint session_send_data(Session *session, const guchar *data, guint size)
408 g_return_val_if_fail(session->write_buf == NULL, -1);
409 g_return_val_if_fail(data != NULL, -1);
410 g_return_val_if_fail(size != 0, -1);
412 session->state = SESSION_SEND;
414 session->write_buf = g_malloc(size);
415 session->write_buf_p = session->write_buf;
416 memcpy(session->write_buf, data, size);
417 session->write_buf_len = size;
418 gettimeofday(&session->tv_prev, NULL);
420 ret = session_write_data_cb(session->sock, G_IO_OUT, session);
423 session->io_tag = sock_add_watch(session->sock, G_IO_OUT,
424 session_write_data_cb,
426 else if (session->state == SESSION_ERROR)
432 gint session_recv_data(Session *session, guint size, const gchar *terminator)
434 g_return_val_if_fail(session->read_data_buf->len == 0, -1);
436 session->state = SESSION_RECV;
438 g_free(session->read_data_terminator);
439 session->read_data_terminator = g_strdup(terminator);
440 gettimeofday(&session->tv_prev, NULL);
442 if (session->read_buf_len > 0)
443 g_idle_add(session_recv_data_idle_cb, session);
445 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
446 session_read_data_cb, session);
451 static gboolean session_recv_data_idle_cb(gpointer data)
453 Session *session = SESSION(data);
456 ret = session_read_data_cb(session->sock, G_IO_IN, session);
459 session->io_tag = sock_add_watch(session->sock, G_IO_IN,
460 session_read_data_cb, session);
465 static gboolean session_read_msg_cb(SockInfo *source, GIOCondition condition,
468 Session *session = SESSION(data);
469 gchar buf[SESSION_BUFFSIZE];
475 g_return_val_if_fail(condition == G_IO_IN, FALSE);
477 session_set_timeout(session, session->timeout_interval);
479 if (session->read_buf_len == 0) {
482 read_len = sock_read(session->sock, session->read_buf,
483 SESSION_BUFFSIZE - 1);
485 if (read_len == -1 && session->state == SESSION_DISCONNECTED) {
486 g_warning ("sock_read: session disconnected\n");
487 if (session->io_tag > 0) {
488 g_source_remove(session->io_tag);
495 g_warning("sock_read: received EOF\n");
496 session->state = SESSION_EOF;
505 g_warning("sock_read: %s\n", g_strerror(errno));
506 session->state = SESSION_ERROR;
511 session->read_buf_len = read_len;
514 if ((newline = memchr(session->read_buf_p, '\n', session->read_buf_len))
516 line_len = newline - session->read_buf_p + 1;
518 line_len = session->read_buf_len;
523 memcpy(buf, session->read_buf_p, line_len);
524 buf[line_len] = '\0';
526 g_string_append(session->read_msg_buf, buf);
528 session->read_buf_len -= line_len;
529 if (session->read_buf_len == 0)
530 session->read_buf_p = session->read_buf;
532 session->read_buf_p += line_len;
534 /* incomplete read */
535 if (buf[line_len - 1] != '\n')
539 if (session->io_tag > 0) {
540 g_source_remove(session->io_tag);
545 msg = g_strdup(session->read_msg_buf->str);
547 g_string_truncate(session->read_msg_buf, 0);
549 ret = session->recv_msg(session, msg);
550 session->recv_msg_notify(session, msg, session->recv_msg_notify_data);
555 session->state = SESSION_ERROR;
560 static gboolean session_read_data_cb(SockInfo *source, GIOCondition condition,
563 Session *session = SESSION(data);
564 GByteArray *data_buf;
566 gboolean complete = FALSE;
570 g_return_val_if_fail(condition == G_IO_IN, FALSE);
572 session_set_timeout(session, session->timeout_interval);
574 if (session->read_buf_len == 0) {
577 read_len = sock_read(session->sock, session->read_buf,
581 g_warning("sock_read: received EOF\n");
582 session->state = SESSION_EOF;
591 g_warning("sock_read: %s\n", g_strerror(errno));
592 session->state = SESSION_ERROR;
597 session->read_buf_len = read_len;
600 data_buf = session->read_data_buf;
601 terminator_len = strlen(session->read_data_terminator);
603 if (session->read_buf_len == 0)
606 g_byte_array_append(data_buf, session->read_buf_p,
607 session->read_buf_len);
609 session->read_buf_len = 0;
610 session->read_buf_p = session->read_buf;
612 /* check if data is terminated */
613 if (data_buf->len >= terminator_len) {
614 if (memcmp(data_buf->data, session->read_data_terminator,
615 terminator_len) == 0)
617 else if (data_buf->len >= terminator_len + 2 &&
618 memcmp(data_buf->data + data_buf->len -
619 (terminator_len + 2), "\r\n", 2) == 0 &&
620 memcmp(data_buf->data + data_buf->len -
621 terminator_len, session->read_data_terminator,
622 terminator_len) == 0)
626 /* incomplete read */
628 struct timeval tv_cur;
630 gettimeofday(&tv_cur, NULL);
631 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
632 tv_cur.tv_usec - session->tv_prev.tv_usec >
633 UI_REFRESH_INTERVAL) {
634 session->recv_data_progressive_notify
635 (session, data_buf->len, 0,
636 session->recv_data_progressive_notify_data);
637 gettimeofday(&session->tv_prev, NULL);
643 if (session->io_tag > 0) {
644 g_source_remove(session->io_tag);
648 data_len = data_buf->len - terminator_len;
651 ret = session->recv_data_finished(session, (gchar *)data_buf->data,
654 g_byte_array_set_size(data_buf, 0);
656 session->recv_data_notify(session, data_len,
657 session->recv_data_notify_data);
660 session->state = SESSION_ERROR;
665 static gint session_write_buf(Session *session)
670 g_return_val_if_fail(session->write_buf != NULL, -1);
671 g_return_val_if_fail(session->write_buf_p != NULL, -1);
672 g_return_val_if_fail(session->write_buf_len > 0, -1);
674 to_write_len = session->write_buf_len -
675 (session->write_buf_p - session->write_buf);
676 to_write_len = MIN(to_write_len, SESSION_BUFFSIZE);
678 write_len = sock_write(session->sock, session->write_buf_p,
687 g_warning("sock_write: %s\n", g_strerror(errno));
688 session->state = SESSION_ERROR;
693 /* incomplete write */
694 if (session->write_buf_p - session->write_buf + write_len <
695 session->write_buf_len) {
696 session->write_buf_p += write_len;
700 g_free(session->write_buf);
701 session->write_buf = NULL;
702 session->write_buf_p = NULL;
703 session->write_buf_len = 0;
708 static gboolean session_write_msg_cb(SockInfo *source, GIOCondition condition,
711 Session *session = SESSION(data);
714 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
715 g_return_val_if_fail(session->write_buf != NULL, FALSE);
716 g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
717 g_return_val_if_fail(session->write_buf_len > 0, FALSE);
719 ret = session_write_buf(session);
722 session->state = SESSION_ERROR;
727 if (session->io_tag > 0) {
728 g_source_remove(session->io_tag);
732 session_recv_msg(session);
737 static gboolean session_write_data_cb(SockInfo *source,
738 GIOCondition condition, gpointer data)
740 Session *session = SESSION(data);
744 g_return_val_if_fail(condition == G_IO_OUT, FALSE);
745 g_return_val_if_fail(session->write_buf != NULL, FALSE);
746 g_return_val_if_fail(session->write_buf_p != NULL, FALSE);
747 g_return_val_if_fail(session->write_buf_len > 0, FALSE);
749 write_buf_len = session->write_buf_len;
751 ret = session_write_buf(session);
754 session->state = SESSION_ERROR;
756 } else if (ret > 0) {
757 struct timeval tv_cur;
759 gettimeofday(&tv_cur, NULL);
760 if (tv_cur.tv_sec - session->tv_prev.tv_sec > 0 ||
761 tv_cur.tv_usec - session->tv_prev.tv_usec >
762 UI_REFRESH_INTERVAL) {
763 session_set_timeout(session, session->timeout_interval);
764 session->send_data_progressive_notify
766 session->write_buf_p - session->write_buf,
768 session->send_data_progressive_notify_data);
769 gettimeofday(&session->tv_prev, NULL);
774 if (session->io_tag > 0) {
775 g_source_remove(session->io_tag);
780 ret = session->send_data_finished(session, write_buf_len);
781 session->send_data_notify(session, write_buf_len,
782 session->send_data_notify_data);