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>
39 static gint session_close (Session *session);
41 static gchar *session_recv_msg (Session *session);
43 static guchar *session_read_data (Session *session,
46 static gint session_send_data_to_sock (Session *session,
49 static guchar *session_recv_data_from_sock (Session *session,
52 gboolean session_parent_input_cb (GIOChannel *source,
53 GIOCondition condition,
56 gboolean session_child_input (Session *session);
59 gint session_connect(Session *session, const gchar *server, gushort port)
62 gint pipe_fds1[2], pipe_fds2[2];
66 session->server = g_strdup(server);
69 if (pipe(pipe_fds1) < 0) {
73 if (pipe(pipe_fds2) < 0) {
80 if ((pid = fork()) < 0) {
86 session->child_pid = pid;
87 session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
88 session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
91 session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
92 session_parent_input_cb,
99 session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
100 session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
104 g_print("child: connecting to %s:%d ...\n", server, port);
106 if ((sock = sock_connect(server, port)) == NULL) {
107 session_send_msg(session, SESSION_MSG_ERROR,
108 "can't connect to server.");
109 session_close(session);
114 if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
115 session_send_msg(session, SESSION_MSG_ERROR,
116 "can't initialize SSL.");
117 session_close(session);
122 g_print("child: connected\n");
124 session->sock = sock;
125 session->state = SESSION_RECV;
127 if ((str = sock_getline(sock)) == NULL) {
128 session_send_msg(session, SESSION_MSG_ERROR,
129 "can't get server response.");
130 session_close(session);
134 session_send_msg(session, SESSION_MSG_NORMAL, str);
137 while (session_child_input(session) == TRUE)
140 session_close(session);
142 g_print("child: disconnected\n");
147 gint session_disconnect(Session *session)
149 g_print("%s: session_disconnect()\n", session->child_pid == 0 ? "child" : "parent");
150 session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
154 void session_destroy(Session *session)
156 g_return_if_fail(session != NULL);
157 g_return_if_fail(session->destroy != NULL);
159 g_print("session_destroy()\n");
160 session_close(session);
161 session->destroy(session);
162 g_free(session->server);
166 void session_set_recv_message_notify(Session *session,
167 RecvMsgNotify notify_func, gpointer data)
169 session->recv_msg_notify = notify_func;
170 session->recv_msg_notify_data = data;
173 void session_set_recv_data_progressive_notify
175 RecvDataProgressiveNotify notify_func,
178 session->recv_data_progressive_notify = notify_func,
179 session->recv_data_progressive_notify_data = data;
182 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
185 session->recv_data_notify = notify_func;
186 session->recv_data_notify_data = data;
189 void session_set_send_data_progressive_notify
191 SendDataProgressiveNotify notify_func,
194 session->send_data_progressive_notify = notify_func;
195 session->send_data_progressive_notify_data = data;
198 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
201 session->send_data_notify = notify_func;
202 session->send_data_notify_data = data;
205 static gint session_close(Session *session)
207 g_return_val_if_fail(session != NULL, -1);
209 g_print("%s: session_close()\n", session->child_pid == 0 ? "child" : "parent");
211 if (session->read_tag > 0) {
212 g_source_remove(session->read_tag);
213 session->read_tag = 0;
216 if (session->read_ch) {
217 g_io_channel_close(session->read_ch);
218 g_io_channel_unref(session->read_ch);
219 session->read_ch = NULL;
221 if (session->write_ch) {
222 g_io_channel_close(session->write_ch);
223 g_io_channel_unref(session->write_ch);
224 session->write_ch = NULL;
228 sock_close(session->sock);
229 session->sock = NULL;
230 session->state = SESSION_DISCONNECTED;
233 if (session->child_pid) {
234 kill(session->child_pid, SIGTERM);
235 waitpid(session->child_pid, NULL, 0);
236 session->child_pid = 0;
242 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
250 case SESSION_MSG_NORMAL:
251 prefix = "MESSAGE"; break;
252 case SESSION_MSG_SEND_DATA:
253 prefix = "SENDDATA"; break;
254 case SESSION_MSG_CONTROL:
255 prefix = "CONTROL"; break;
256 case SESSION_MSG_ERROR:
257 prefix = "ERROR"; break;
262 str = g_strdup_printf("%s %s\n", prefix, msg);
263 g_print("%s: sending message: %s", session->child_pid == 0 ? "child" : "parent", str);
267 if (g_io_channel_write(session->write_ch, str, size,
269 != G_IO_ERROR_NONE || bytes_written == 0) {
270 g_warning("%s: sending message failed.\n",
271 session->child_pid == 0 ? "child" : "parent");
274 size -= bytes_written;
280 static gchar *session_recv_msg(Session *session)
288 if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
290 != G_IO_ERROR_NONE || bytes_read == 0) {
291 g_warning("%s: receiving message failed.\n",
292 session->child_pid == 0 ? "child" : "parent");
299 buf[bytes_read] = '\0';
304 str = g_realloc(str, size);
307 if (str[size - 2] == '\n') {
308 str[size - 2] = '\0';
310 g_print("%s: received message: %s\n", session->child_pid == 0 ? "child" : "parent", str);
320 gint session_start_tls(Session *session)
324 session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
325 ctl_msg = session_recv_msg(session);
326 if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
336 gint session_send_data(Session *session, const guchar *data, guint size)
342 session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
343 if ((msg = session_recv_msg(session)) == NULL)
348 if ((err = g_io_channel_write(session->write_ch, (guchar *)data,
349 size, &bytes_written))
350 != G_IO_ERROR_NONE || bytes_written == 0) {
351 g_warning("%s: sending data failed: %d\n",
352 session->child_pid == 0 ? "child" : "parent",
356 size -= bytes_written;
357 g_print("%s: sent %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_written);
363 static guchar *session_read_data(Session *session, guint size)
370 cur = data = g_malloc(size);
373 if ((err = g_io_channel_read(session->read_ch, cur, size,
375 != G_IO_ERROR_NONE || bytes_read == 0) {
376 g_warning("%s: reading data failed: %d\n",
377 session->child_pid == 0 ? "child" : "parent",
384 g_print("%s: received %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_read);
390 #define MAX_CHUNK_SIZE 4096
392 static gint session_send_data_to_sock(Session *session, const guchar *data,
395 const guchar *cur = data;
397 gint total_write_len = 0;
403 bytes_written = sock_write(session->sock, cur,
404 MIN(left, MAX_CHUNK_SIZE));
405 if (bytes_written <= 0)
407 left -= bytes_written;
408 cur += bytes_written;
409 total_write_len += bytes_written;
411 g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
412 total_write_len, size);
413 session_send_msg(session, SESSION_MSG_CONTROL, buf);
414 if ((msg = session_recv_msg(session)) == NULL)
423 static guchar *session_recv_data_from_sock(Session *session, guint size)
428 gint total_read_len = 0;
433 cur = data = g_malloc(size);
436 bytes_read = sock_read(session->sock, cur, left);
437 if (bytes_read <= 0) {
441 g_print("child: received %d bytes of data from sock\n", bytes_read);
444 total_read_len += bytes_read;
445 g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
446 total_read_len, size);
447 session_send_msg(session, SESSION_MSG_CONTROL, buf);
448 if ((msg = session_recv_msg(session)) == NULL) {
458 static SessionMsgType session_get_msg_type(const gchar *str)
460 if (!strncmp(str, "MESSAGE ", 8))
461 return SESSION_MSG_NORMAL;
462 else if (!strncmp(str, "SENDDATA ", 9))
463 return SESSION_MSG_SEND_DATA;
464 else if (!strncmp(str, "RECVDATA ", 9))
465 return SESSION_MSG_RECV_DATA;
466 else if (!strncmp(str, "CONTROL ", 8))
467 return SESSION_MSG_CONTROL;
468 else if (!strncmp(str, "ERROR ", 6))
469 return SESSION_MSG_ERROR;
471 return SESSION_MSG_UNKNOWN;
474 gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
477 Session *session = SESSION(data);
486 if ((msg = session_recv_msg(session)) == NULL) {
487 session->state = SESSION_ERROR;
491 switch (session_get_msg_type(msg)) {
492 case SESSION_MSG_NORMAL:
493 msg_data = msg + strlen("MESSAGE ");
494 ret = session->recv_msg(session, msg_data);
496 session->recv_msg_notify(session, msg_data,
497 session->recv_msg_notify_data);
499 session_send_msg(session, SESSION_MSG_CONTROL,
502 session->state = SESSION_ERROR;
507 case SESSION_MSG_SEND_DATA:
508 msg_data = msg + strlen("SENDDATA ");
509 size = atoi(msg_data);
510 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
511 recv_data = session_read_data(session, size);
513 session->state = SESSION_ERROR;
517 session->recv_data_finished(session, recv_data, size);
520 case SESSION_MSG_RECV_DATA:
522 case SESSION_MSG_CONTROL:
523 msg_data = msg + strlen("CONTROL ");
524 if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
525 ret = sscanf(msg_data,
526 "DATARECVINPROG %d %d", &len, &total);
528 g_warning("wrong control message: %s\n", msg);
529 session->state = SESSION_ERROR;
533 session_send_msg(session, SESSION_MSG_CONTROL,
535 session->recv_data_progressive_notify
536 (session, len, total,
537 session->recv_data_progressive_notify_data);
538 } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
539 ret = sscanf(msg_data,
540 "DATASENDINPROG %d %d", &len, &total);
542 g_warning("wrong control message: %s\n", msg);
543 session->state = SESSION_ERROR;
547 session_send_msg(session, SESSION_MSG_CONTROL,
549 session->send_data_progressive_notify
550 (session, len, total,
551 session->send_data_progressive_notify_data);
552 } else if (!strncmp(msg_data, "DATASENT ", 9)) {
553 len = atoi(msg_data + 9);
554 ret = session->send_data_finished(session, len);
555 session->send_data_notify
556 (session, len, session->send_data_notify_data);
557 } else if (!strcmp(msg_data, "DISCONNECTED")) {
558 session->state = SESSION_DISCONNECTED;
562 g_warning("wrong control message: %s\n", msg);
563 session->state = SESSION_ERROR;
568 case SESSION_MSG_ERROR:
570 g_warning("error from child: %s\n", msg + strlen("ERROR "));
571 session->state = SESSION_ERROR;
580 gboolean session_child_input(Session *session)
590 if ((msg = session_recv_msg(session)) == NULL) {
591 session_send_msg(session, SESSION_MSG_ERROR,
592 "receiving message failed.");
593 session_close(session);
594 session->state = SESSION_ERROR;
598 switch (session_get_msg_type(msg)) {
599 case SESSION_MSG_NORMAL:
600 msg_data = msg + strlen("MESSAGE ");
601 session->state = SESSION_SEND;
602 sock_puts(session->sock, msg_data);
603 session->state = SESSION_RECV;
604 str = sock_getline(session->sock);
606 session_send_msg(session, SESSION_MSG_ERROR,
607 "receiving message failed.");
608 session_close(session);
609 session->state = SESSION_ERROR;
614 session_send_msg(session, SESSION_MSG_NORMAL, str);
617 case SESSION_MSG_SEND_DATA:
618 msg_data = msg + strlen("SENDDATA ");
619 size = atoi(msg_data);
620 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
621 send_data = session_read_data(session, size);
623 session_send_msg(session, SESSION_MSG_ERROR,
624 "sending data failed.");
625 session_close(session);
626 session->state = SESSION_ERROR;
630 session->state = SESSION_SEND;
631 if (session_send_data_to_sock(session, send_data, size) < 0) {
632 session_send_msg(session, SESSION_MSG_ERROR,
633 "sending data failed.");
634 session_close(session);
635 session->state = SESSION_ERROR;
641 g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
642 session_send_msg(session, SESSION_MSG_CONTROL, buf);
644 case SESSION_MSG_RECV_DATA:
645 msg_data = msg + strlen("RECVDATA ");
646 size = atoi(msg_data);
647 session->state = SESSION_RECV;
648 recv_data = session_recv_data_from_sock(session, size);
650 session_send_msg(session, SESSION_MSG_ERROR,
651 "receiving data failed.");
652 session_close(session);
653 session->state = SESSION_ERROR;
657 if (session_send_data(session, recv_data, size) < 0) {
658 session_close(session);
659 session->state = SESSION_ERROR;
666 case SESSION_MSG_CONTROL:
667 msg_data = msg + strlen("CONTROL ");
668 if (!strcmp(msg_data, "CONTINUE")) {
669 session->state = SESSION_RECV;
670 str = sock_getline(session->sock);
672 session_send_msg(session, SESSION_MSG_ERROR,
673 "receiving message failed.");
674 session_close(session);
675 session->state = SESSION_ERROR;
680 session_send_msg(session, SESSION_MSG_NORMAL, str);
684 } else if (!strcmp(msg_data, "STARTTLS")) {
685 if (!ssl_init_socket_with_method(session->sock,
687 session_send_msg(session, SESSION_MSG_ERROR,
688 "can't start TLS session.");
689 session_close(session);
690 session->state = SESSION_ERROR;
694 session_send_msg(session, SESSION_MSG_CONTROL,
698 } else if (!strcmp(msg_data, "DISCONNECT")) {
699 sock_close(session->sock);
700 session->sock = NULL;
701 session->state = SESSION_DISCONNECTED;
702 session_send_msg(session, SESSION_MSG_CONTROL,
707 session_send_msg(session, SESSION_MSG_ERROR,
708 "wrong control message.");
709 session_close(session);
710 session->state = SESSION_ERROR;
715 case SESSION_MSG_ERROR:
717 session_send_msg(session, SESSION_MSG_ERROR,
718 "error received from parent.");
719 session_close(session);
720 session->state = SESSION_ERROR;