2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2003 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>
40 static gint session_close (Session *session);
42 static gchar *session_recv_msg (Session *session);
44 static guchar *session_read_data (Session *session,
47 static gint session_send_data_to_sock (Session *session,
50 static guchar *session_recv_data_from_sock (Session *session,
53 static guchar *session_recv_data_from_sock_unescape (Session *session,
57 gboolean session_parent_input_cb (GIOChannel *source,
58 GIOCondition condition,
61 gboolean session_child_input (Session *session);
64 void session_init(Session *session)
69 session->server = NULL;
71 session->state = SESSION_READY;
72 session->last_access_time = time(NULL);
75 session->read_ch = NULL;
76 session->write_ch = NULL;
79 gint session_connect(Session *session, const gchar *server, gushort port)
82 gint pipe_fds1[2], pipe_fds2[2];
86 session->server = g_strdup(server);
89 if (pipe(pipe_fds1) < 0) {
93 if (pipe(pipe_fds2) < 0) {
100 if ((pid = fork()) < 0) {
106 session->child_pid = pid;
107 session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
108 session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
111 session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
112 session_parent_input_cb,
119 session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
120 session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
124 g_print("child: connecting to %s:%d ...\n", server, port);
126 if ((sock = sock_connect(server, port)) == NULL) {
127 session_send_msg(session, SESSION_MSG_ERROR,
128 "can't connect to server.");
129 session_close(session);
134 if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
135 session_send_msg(session, SESSION_MSG_ERROR,
136 "can't initialize SSL.");
137 session_close(session);
142 g_print("child: connected\n");
144 session->sock = sock;
145 session->state = SESSION_RECV;
147 if ((str = sock_getline(sock)) == NULL) {
148 session_send_msg(session, SESSION_MSG_ERROR,
149 "can't get server response.");
150 session_close(session);
154 session_send_msg(session, SESSION_MSG_NORMAL, str);
157 while (session_child_input(session) == TRUE)
160 session_close(session);
162 g_print("child: disconnected\n");
167 gint session_disconnect(Session *session)
169 g_print("%s: session_disconnect()\n", session->child_pid == 0 ? "child" : "parent");
170 session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
174 void session_destroy(Session *session)
176 g_return_if_fail(session != NULL);
177 g_return_if_fail(session->destroy != NULL);
179 g_print("session_destroy()\n");
180 session_close(session);
181 session->destroy(session);
182 g_free(session->server);
186 void session_set_recv_message_notify(Session *session,
187 RecvMsgNotify notify_func, gpointer data)
189 session->recv_msg_notify = notify_func;
190 session->recv_msg_notify_data = data;
193 void session_set_recv_data_progressive_notify
195 RecvDataProgressiveNotify notify_func,
198 session->recv_data_progressive_notify = notify_func,
199 session->recv_data_progressive_notify_data = data;
202 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
205 session->recv_data_notify = notify_func;
206 session->recv_data_notify_data = data;
209 void session_set_send_data_progressive_notify
211 SendDataProgressiveNotify notify_func,
214 session->send_data_progressive_notify = notify_func;
215 session->send_data_progressive_notify_data = data;
218 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
221 session->send_data_notify = notify_func;
222 session->send_data_notify_data = data;
225 static gint session_close(Session *session)
227 g_return_val_if_fail(session != NULL, -1);
229 g_print("%s: session_close()\n", session->child_pid == 0 ? "child" : "parent");
231 if (session->read_tag > 0) {
232 g_source_remove(session->read_tag);
233 session->read_tag = 0;
236 if (session->read_ch) {
237 g_io_channel_close(session->read_ch);
238 g_io_channel_unref(session->read_ch);
239 session->read_ch = NULL;
241 if (session->write_ch) {
242 g_io_channel_close(session->write_ch);
243 g_io_channel_unref(session->write_ch);
244 session->write_ch = NULL;
248 sock_close(session->sock);
249 session->sock = NULL;
250 session->state = SESSION_DISCONNECTED;
253 if (session->child_pid) {
254 kill(session->child_pid, SIGTERM);
255 waitpid(session->child_pid, NULL, 0);
256 session->child_pid = 0;
262 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
270 case SESSION_MSG_NORMAL:
271 prefix = "MESSAGE"; break;
272 case SESSION_MSG_SEND_DATA:
273 prefix = "SENDDATA"; break;
274 case SESSION_MSG_RECV_DATA:
275 prefix = "RECVDATA"; break;
276 case SESSION_MSG_CONTROL:
277 prefix = "CONTROL"; break;
278 case SESSION_MSG_ERROR:
279 prefix = "ERROR"; break;
284 str = g_strdup_printf("%s %s\n", prefix, msg);
285 /* g_print("%s: sending message: %s", session->child_pid == 0 ? "child" : "parent", str); */
289 if (g_io_channel_write(session->write_ch, str, size,
291 != G_IO_ERROR_NONE || bytes_written == 0) {
292 g_warning("%s: sending message failed.\n",
293 session->child_pid == 0 ? "child" : "parent");
296 size -= bytes_written;
302 static gchar *session_recv_msg(Session *session)
310 if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
312 != G_IO_ERROR_NONE || bytes_read == 0) {
313 g_warning("%s: receiving message failed.\n",
314 session->child_pid == 0 ? "child" : "parent");
321 buf[bytes_read] = '\0';
326 str = g_realloc(str, size);
329 if (str[size - 2] == '\n') {
330 str[size - 2] = '\0';
332 g_print("%s: received message: %s\n", session->child_pid == 0 ? "child" : "parent", str);
342 gint session_start_tls(Session *session)
346 session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
347 ctl_msg = session_recv_msg(session);
348 if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
358 gint session_send_data(Session *session, const guchar *data, guint size)
364 session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
365 if ((msg = session_recv_msg(session)) == NULL)
370 if ((err = g_io_channel_write(session->write_ch, (guchar *)data,
371 size, &bytes_written))
372 != G_IO_ERROR_NONE || bytes_written == 0) {
373 g_warning("%s: sending data failed: %d\n",
374 session->child_pid == 0 ? "child" : "parent",
378 size -= bytes_written;
379 g_print("%s: sent %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_written);
385 gint session_recv_data(Session *session, guint size, gboolean unescape_dot)
390 g_snprintf(buf, sizeof(buf), "%d UNESCAPE", size);
391 session_send_msg(session, SESSION_MSG_RECV_DATA, buf);
393 session_send_msg(session, SESSION_MSG_RECV_DATA, itos(size));
397 static guchar *session_read_data(Session *session, guint size)
404 cur = data = g_malloc(size);
407 if ((err = g_io_channel_read(session->read_ch, cur, size,
409 != G_IO_ERROR_NONE || bytes_read == 0) {
410 g_warning("%s: reading data failed: %d\n",
411 session->child_pid == 0 ? "child" : "parent",
418 g_print("%s: received %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_read);
424 #define MAX_CHUNK_SIZE 4096
426 static gint session_send_data_to_sock(Session *session, const guchar *data,
429 const guchar *cur = data;
431 gint total_write_len = 0;
435 struct timeval tv_prev, tv_cur;
437 gettimeofday(&tv_prev, NULL);
440 bytes_written = sock_write(session->sock, cur,
441 MIN(left, MAX_CHUNK_SIZE));
442 if (bytes_written <= 0)
444 left -= bytes_written;
445 cur += bytes_written;
446 total_write_len += bytes_written;
450 gettimeofday(&tv_cur, NULL);
451 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
452 tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
453 g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
454 total_write_len, size);
455 session_send_msg(session, SESSION_MSG_CONTROL, buf);
456 if ((msg = session_recv_msg(session)) == NULL)
459 gettimeofday(&tv_prev, NULL);
466 static guchar *session_recv_data_from_sock(Session *session, guint size)
471 gint total_read_len = 0;
475 struct timeval tv_prev, tv_cur;
477 gettimeofday(&tv_prev, NULL);
479 cur = data = g_malloc(size);
482 bytes_read = sock_read(session->sock, cur, left);
483 if (bytes_read <= 0) {
487 g_print("child: received %d bytes of data from sock\n", bytes_read);
490 total_read_len += bytes_read;
494 gettimeofday(&tv_cur, NULL);
495 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
496 tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
497 g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
498 total_read_len, size);
499 session_send_msg(session, SESSION_MSG_CONTROL, buf);
500 if ((msg = session_recv_msg(session)) == NULL) {
505 gettimeofday(&tv_prev, NULL);
512 static guchar *session_recv_data_from_sock_unescape(Session *session,
521 struct timeval tv_prev, tv_cur;
523 gettimeofday(&tv_prev, NULL);
525 data = g_string_sized_new(size + 1);
529 bytes_read = sock_gets(session->sock, buf, sizeof(buf));
530 if (bytes_read <= 0) {
531 g_string_free(data, TRUE);
535 if (buf[0] == '.' && buf[1] == '\r' && buf[2] == '\n')
537 if (buf[0] == '.' && buf[1] == '.')
538 g_string_append(data, buf + 1);
540 g_string_append(data, buf);
542 gettimeofday(&tv_cur, NULL);
543 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
544 tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
545 g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
546 data->len, MAX(data->len, size));
547 session_send_msg(session, SESSION_MSG_CONTROL, buf);
548 if ((msg = session_recv_msg(session)) == NULL) {
549 g_string_free(data, TRUE);
553 gettimeofday(&tv_prev, NULL);
557 ret_data = data->str;
558 *actual_size = data->len;
559 g_string_free(data, FALSE);
564 static SessionMsgType session_get_msg_type(const gchar *str)
566 if (!strncmp(str, "MESSAGE ", 8))
567 return SESSION_MSG_NORMAL;
568 else if (!strncmp(str, "SENDDATA ", 9))
569 return SESSION_MSG_SEND_DATA;
570 else if (!strncmp(str, "RECVDATA ", 9))
571 return SESSION_MSG_RECV_DATA;
572 else if (!strncmp(str, "CONTROL ", 8))
573 return SESSION_MSG_CONTROL;
574 else if (!strncmp(str, "ERROR ", 6))
575 return SESSION_MSG_ERROR;
577 return SESSION_MSG_UNKNOWN;
580 gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
583 Session *session = SESSION(data);
592 if ((msg = session_recv_msg(session)) == NULL) {
593 session->state = SESSION_ERROR;
597 switch (session_get_msg_type(msg)) {
598 case SESSION_MSG_NORMAL:
599 msg_data = msg + strlen("MESSAGE ");
600 ret = session->recv_msg(session, msg_data);
601 session->recv_msg_notify(session, msg_data,
602 session->recv_msg_notify_data);
604 session_send_msg(session, SESSION_MSG_CONTROL,
607 session->state = SESSION_ERROR;
612 case SESSION_MSG_SEND_DATA:
613 msg_data = msg + strlen("SENDDATA ");
614 size = atoi(msg_data);
615 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
616 recv_data = session_read_data(session, size);
618 session->state = SESSION_ERROR;
622 ret = session->recv_data_finished(session, recv_data, size);
624 session->recv_data_notify(session, size,
625 session->recv_data_notify_data);
627 session_send_msg(session, SESSION_MSG_CONTROL,
630 session->state = SESSION_ERROR;
635 case SESSION_MSG_RECV_DATA:
637 case SESSION_MSG_CONTROL:
638 msg_data = msg + strlen("CONTROL ");
639 if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
640 ret = sscanf(msg_data,
641 "DATARECVINPROG %d %d", &len, &total);
643 g_warning("wrong control message: %s\n", msg);
644 session->state = SESSION_ERROR;
648 session_send_msg(session, SESSION_MSG_CONTROL,
650 session->recv_data_progressive_notify
651 (session, len, total,
652 session->recv_data_progressive_notify_data);
653 } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
654 ret = sscanf(msg_data,
655 "DATASENDINPROG %d %d", &len, &total);
657 g_warning("wrong control message: %s\n", msg);
658 session->state = SESSION_ERROR;
662 session_send_msg(session, SESSION_MSG_CONTROL,
664 session->send_data_progressive_notify
665 (session, len, total,
666 session->send_data_progressive_notify_data);
667 } else if (!strncmp(msg_data, "DATASENT ", 9)) {
668 len = atoi(msg_data + 9);
669 ret = session->send_data_finished(session, len);
670 session->send_data_notify
671 (session, len, session->send_data_notify_data);
672 } else if (!strcmp(msg_data, "DISCONNECTED")) {
673 session->state = SESSION_DISCONNECTED;
677 g_warning("wrong control message: %s\n", msg);
678 session->state = SESSION_ERROR;
683 case SESSION_MSG_ERROR:
685 g_warning("error from child: %s\n", msg + strlen("ERROR "));
686 session->state = SESSION_ERROR;
695 gboolean session_child_input(Session *session)
706 if ((msg = session_recv_msg(session)) == NULL) {
707 session_send_msg(session, SESSION_MSG_ERROR,
708 "receiving message failed.");
709 session_close(session);
710 session->state = SESSION_ERROR;
714 switch (session_get_msg_type(msg)) {
715 case SESSION_MSG_NORMAL:
716 msg_data = msg + strlen("MESSAGE ");
717 session->state = SESSION_SEND;
718 sock_puts(session->sock, msg_data);
719 session->state = SESSION_RECV;
720 str = sock_getline(session->sock);
722 session_send_msg(session, SESSION_MSG_ERROR,
723 "receiving message failed.");
724 session_close(session);
725 session->state = SESSION_ERROR;
730 session_send_msg(session, SESSION_MSG_NORMAL, str);
733 case SESSION_MSG_SEND_DATA:
734 msg_data = msg + strlen("SENDDATA ");
735 size = atoi(msg_data);
736 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
737 send_data = session_read_data(session, size);
739 session_send_msg(session, SESSION_MSG_ERROR,
740 "sending data failed.");
741 session_close(session);
742 session->state = SESSION_ERROR;
746 session->state = SESSION_SEND;
747 if (session_send_data_to_sock(session, send_data, size) < 0) {
748 session_send_msg(session, SESSION_MSG_ERROR,
749 "sending data failed.");
750 session_close(session);
751 session->state = SESSION_ERROR;
757 g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
758 session_send_msg(session, SESSION_MSG_CONTROL, buf);
760 case SESSION_MSG_RECV_DATA:
761 msg_data = msg + strlen("RECVDATA ");
762 size = atoi(msg_data);
763 session->state = SESSION_RECV;
764 if (strstr(msg_data, "UNESCAPE") != NULL) {
765 recv_data = session_recv_data_from_sock_unescape
766 (session, size, &actual_size);
769 recv_data = session_recv_data_from_sock(session, size);
771 session_send_msg(session, SESSION_MSG_ERROR,
772 "receiving data failed.");
773 session_close(session);
774 session->state = SESSION_ERROR;
778 if (session_send_data(session, recv_data, size) < 0) {
779 session_close(session);
780 session->state = SESSION_ERROR;
787 case SESSION_MSG_CONTROL:
788 msg_data = msg + strlen("CONTROL ");
789 if (!strcmp(msg_data, "CONTINUE")) {
790 session->state = SESSION_RECV;
791 str = sock_getline(session->sock);
793 session_send_msg(session, SESSION_MSG_ERROR,
794 "receiving message failed.");
795 session_close(session);
796 session->state = SESSION_ERROR;
801 session_send_msg(session, SESSION_MSG_NORMAL, str);
805 } else if (!strcmp(msg_data, "STARTTLS")) {
806 if (!ssl_init_socket_with_method(session->sock,
808 session_send_msg(session, SESSION_MSG_ERROR,
809 "can't start TLS session.");
810 session_close(session);
811 session->state = SESSION_ERROR;
815 session_send_msg(session, SESSION_MSG_CONTROL,
819 } else if (!strcmp(msg_data, "DISCONNECT")) {
820 sock_close(session->sock);
821 session->sock = NULL;
822 session->state = SESSION_DISCONNECTED;
823 session_send_msg(session, SESSION_MSG_CONTROL,
828 session_send_msg(session, SESSION_MSG_ERROR,
829 "wrong control message.");
830 session_close(session);
831 session->state = SESSION_ERROR;
836 case SESSION_MSG_ERROR:
838 session_send_msg(session, SESSION_MSG_ERROR,
839 "error received from parent.");
840 session_close(session);
841 session->state = SESSION_ERROR;