sync with 0.8.11cvs11
[claws.git] / src / common / session.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4  *
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.
9  *
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.
14  *
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.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <sys/signal.h>
34 #include <sys/wait.h>
35 #include <sys/time.h>
36
37 #include "session.h"
38 #include "utils.h"
39
40 static gint session_close               (Session        *session);
41
42 static gchar *session_recv_msg          (Session        *session);
43
44 static guchar *session_read_data        (Session        *session,
45                                          guint           size);
46
47 static gint session_send_data_to_sock           (Session        *session,
48                                                  const guchar   *data,
49                                                  guint           size);
50 static guchar *session_recv_data_from_sock      (Session        *session,
51                                                  guint           size);
52
53 static guchar *session_recv_data_from_sock_unescape     (Session *session,
54                                                          guint    size);
55
56 gboolean session_parent_input_cb        (GIOChannel     *source,
57                                          GIOCondition    condition,
58                                          gpointer        data);
59
60 gboolean session_child_input            (Session        *session);
61
62
63 void session_init(Session *session)
64 {
65         session->type = 0;
66         session->sock = NULL;
67
68         session->server = NULL;
69         session->port = 0;
70         session->state = SESSION_READY;
71         session->last_access_time = time(NULL);
72         session->data = NULL;
73
74         session->read_ch = NULL;
75         session->write_ch = NULL;
76 }
77
78 gint session_connect(Session *session, const gchar *server, gushort port)
79 {
80         pid_t pid;
81         gint pipe_fds1[2], pipe_fds2[2];
82         SockInfo *sock;
83         gchar *str;
84
85         session->server = g_strdup(server);
86         session->port = port;
87
88         if (pipe(pipe_fds1) < 0) {
89                 perror("pipe");
90                 return -1;
91         }
92         if (pipe(pipe_fds2) < 0) {
93                 perror("pipe");
94                 close(pipe_fds1[0]);
95                 close(pipe_fds1[1]);
96                 return -1;
97         }
98
99         if ((pid = fork()) < 0) {
100                 perror("fork");
101                 return -1;
102         }
103
104         if (pid != 0) {
105                 session->child_pid = pid;
106                 session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
107                 session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
108                 close(pipe_fds1[0]);
109                 close(pipe_fds2[1]);
110                 session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
111                                                    session_parent_input_cb,
112                                                    session);
113                 return 0;
114         }
115
116         /* child process */
117
118         session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
119         session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
120         close(pipe_fds1[1]);
121         close(pipe_fds2[0]);
122
123         g_print("child: connecting to %s:%d ...\n", server, port);
124
125         if ((sock = sock_connect(server, port)) == NULL) {
126                 session_send_msg(session, SESSION_MSG_ERROR,
127                                  "can't connect to server.");
128                 session_close(session);
129                 _exit(1);
130         }
131
132 #if USE_OPENSSL
133         if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
134                 session_send_msg(session, SESSION_MSG_ERROR,
135                                  "can't initialize SSL.");
136                 session_close(session);
137                 _exit(1);
138         }
139 #endif
140
141         g_print("child: connected\n");
142
143         session->sock = sock;
144         session->state = SESSION_RECV;
145
146         if ((str = sock_getline(sock)) == NULL) {
147                 session_send_msg(session, SESSION_MSG_ERROR,
148                                  "can't get server response.");
149                 session_close(session);
150                 _exit(1);
151         }
152         strretchomp(str);
153         session_send_msg(session, SESSION_MSG_NORMAL, str);
154         g_free(str);
155
156         while (session_child_input(session) == TRUE)
157                 ;
158
159         session_close(session);
160
161         g_print("child: disconnected\n");
162
163         _exit(0);
164 }
165
166 gint session_disconnect(Session *session)
167 {
168         g_print("%s: session_disconnect()\n", session->child_pid == 0 ? "child" : "parent");
169         session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
170         return 0;
171 }
172
173 void session_destroy(Session *session)
174 {
175         g_return_if_fail(session != NULL);
176         g_return_if_fail(session->destroy != NULL);
177
178         g_print("session_destroy()\n");
179         session_close(session);
180         session->destroy(session);
181         g_free(session->server);
182         g_free(session);
183 }
184
185 void session_set_recv_message_notify(Session *session,
186                                      RecvMsgNotify notify_func, gpointer data)
187 {
188         session->recv_msg_notify = notify_func;
189         session->recv_msg_notify_data = data;
190 }
191
192 void session_set_recv_data_progressive_notify
193                                         (Session *session,
194                                          RecvDataProgressiveNotify notify_func,
195                                          gpointer data)
196 {
197         session->recv_data_progressive_notify = notify_func,
198         session->recv_data_progressive_notify_data = data;
199 }
200
201 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
202                                   gpointer data)
203 {
204         session->recv_data_notify = notify_func;
205         session->recv_data_notify_data = data;
206 }
207
208 void session_set_send_data_progressive_notify
209                                         (Session *session,
210                                          SendDataProgressiveNotify notify_func,
211                                          gpointer data)
212 {
213         session->send_data_progressive_notify = notify_func;
214         session->send_data_progressive_notify_data = data;
215 }
216
217 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
218                                   gpointer data)
219 {
220         session->send_data_notify = notify_func;
221         session->send_data_notify_data = data;
222 }
223
224 static gint session_close(Session *session)
225 {
226         g_return_val_if_fail(session != NULL, -1);
227
228         g_print("%s: session_close()\n", session->child_pid == 0 ? "child" : "parent");
229
230         if (session->read_tag > 0) {
231                 g_source_remove(session->read_tag);
232                 session->read_tag = 0;
233         }
234
235         if (session->read_ch) {
236                 g_io_channel_close(session->read_ch);
237                 g_io_channel_unref(session->read_ch);
238                 session->read_ch = NULL;
239         }
240         if (session->write_ch) {
241                 g_io_channel_close(session->write_ch);
242                 g_io_channel_unref(session->write_ch);
243                 session->write_ch = NULL;
244         }
245
246         if (session->sock) {
247                 sock_close(session->sock);
248                 session->sock = NULL;
249                 session->state = SESSION_DISCONNECTED;
250         }
251
252         if (session->child_pid) {
253                 kill(session->child_pid, SIGTERM);
254                 waitpid(session->child_pid, NULL, 0);
255                 session->child_pid = 0;
256         }
257
258         return 0;
259 }
260
261 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
262 {
263         gchar *prefix;
264         gchar *str;
265         guint size;
266         guint bytes_written;
267
268         switch (type) {
269         case SESSION_MSG_NORMAL:
270                 prefix = "MESSAGE"; break;
271         case SESSION_MSG_SEND_DATA:
272                 prefix = "SENDDATA"; break;
273         case SESSION_MSG_RECV_DATA:
274                 prefix = "RECVDATA"; break;
275         case SESSION_MSG_CONTROL:
276                 prefix = "CONTROL"; break;
277         case SESSION_MSG_ERROR:
278                 prefix = "ERROR"; break;
279         default:
280                 return -1;
281         }
282
283         str = g_strdup_printf("%s %s\n", prefix, msg);
284         /* g_print("%s: sending message: %s", session->child_pid == 0 ? "child" : "parent", str); */
285         size = strlen(str);
286
287         while (size > 0) {
288                 if (g_io_channel_write(session->write_ch, str, size,
289                                        &bytes_written)
290                     != G_IO_ERROR_NONE || bytes_written == 0) {
291                         g_warning("%s: sending message failed.\n",
292                                   session->child_pid == 0 ? "child" : "parent");
293                         return -1;
294                 }
295                 size -= bytes_written;
296         }
297
298         return 0;
299 }
300
301 static gchar *session_recv_msg(Session *session)
302 {
303         gchar buf[BUFFSIZE];
304         gchar *str = NULL;
305         guint size = 1;
306         guint bytes_read;
307
308         for (;;) {
309                 if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
310                                       &bytes_read)
311                     != G_IO_ERROR_NONE || bytes_read == 0) {
312                         g_warning("%s: receiving message failed.\n",
313                                   session->child_pid == 0 ? "child" : "parent");
314                         g_free(str);
315                         str = NULL;
316                         break;
317                 }
318
319                 size += bytes_read;
320                 buf[bytes_read] = '\0';
321
322                 if (!str)
323                         str = g_strdup(buf);
324                 else {
325                         str = g_realloc(str, size);
326                         strcat(str, buf);
327                 }
328                 if (str[size - 2] == '\n') {
329                         str[size - 2] = '\0';
330
331                         g_print("%s: received message: %s\n", session->child_pid == 0 ? "child" : "parent", str);
332
333                         break;
334                 }
335         }
336
337         return str;
338 }
339
340 #if USE_OPENSSL
341 gint session_start_tls(Session *session)
342 {
343         gchar *ctl_msg;
344
345         session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
346         ctl_msg = session_recv_msg(session);
347         if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
348                 g_free(ctl_msg);
349                 return -1;
350         }
351         g_free(ctl_msg);
352
353         return 0;
354 }
355 #endif
356
357 gint session_send_data(Session *session, const guchar *data, guint size)
358 {
359         gchar *msg;
360         guint bytes_written;
361         GIOError err;
362
363         session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
364         if ((msg = session_recv_msg(session)) == NULL)
365                 return -1;
366         g_free(msg);
367
368         while (size > 0) {
369                 if ((err = g_io_channel_write(session->write_ch, (guchar *)data,
370                                               size, &bytes_written))
371                     != G_IO_ERROR_NONE || bytes_written == 0) {
372                         g_warning("%s: sending data failed: %d\n",
373                                   session->child_pid == 0 ? "child" : "parent",
374                                   err);
375                         return -1;
376                 }
377                 size -= bytes_written;
378                 g_print("%s: sent %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_written);
379         }
380
381         return 0;
382 }
383
384 gint session_recv_data(Session *session, guint size, gboolean unescape_dot)
385 {
386         if (unescape_dot) {
387                 gchar buf[BUFFSIZE];
388
389                 g_snprintf(buf, sizeof(buf), "%d UNESCAPE", size);
390                 session_send_msg(session, SESSION_MSG_RECV_DATA, buf);
391         } else
392                 session_send_msg(session, SESSION_MSG_RECV_DATA, itos(size));
393         return 0;
394 }
395
396 static guchar *session_read_data(Session *session, guint size)
397 {
398         guchar *data;
399         guchar *cur;
400         guint bytes_read;
401         GIOError err;
402
403         cur = data = g_malloc(size);
404
405         while (size > 0) {
406                 if ((err = g_io_channel_read(session->read_ch, cur, size,
407                                              &bytes_read))
408                     != G_IO_ERROR_NONE || bytes_read == 0) {
409                         g_warning("%s: reading data failed: %d\n",
410                                   session->child_pid == 0 ? "child" : "parent",
411                                   err);
412                         g_free(data);
413                         return NULL;
414                 }
415                 size -= bytes_read;
416                 cur += bytes_read;
417                 g_print("%s: received %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_read);
418         }
419
420         return data;
421 }
422
423 #define MAX_CHUNK_SIZE 4096
424
425 static gint session_send_data_to_sock(Session *session, const guchar *data,
426                                       guint size)
427 {
428         const guchar *cur = data;
429         gint bytes_written;
430         gint total_write_len = 0;
431         guint left = size;
432         gchar buf[BUFFSIZE];
433         gchar *msg;
434         struct timeval tv_prev, tv_cur;
435
436         gettimeofday(&tv_prev, NULL);
437
438         while (1) {
439                 bytes_written = sock_write(session->sock, cur,
440                                            MIN(left, MAX_CHUNK_SIZE));
441                 if (bytes_written <= 0)
442                         return -1;
443                 left -= bytes_written;
444                 cur += bytes_written;
445                 total_write_len += bytes_written;
446                 if (left == 0)
447                         break;
448
449                 gettimeofday(&tv_cur, NULL);
450                 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
451                     tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
452                         g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
453                                    total_write_len, size);
454                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
455                         if ((msg = session_recv_msg(session)) == NULL)
456                                 return -1;
457                         g_free(msg);
458                         gettimeofday(&tv_prev, NULL);
459                 }
460         }
461
462         return 0;
463 }
464
465 static guchar *session_recv_data_from_sock(Session *session, guint size)
466 {
467         guchar *data;
468         guchar *cur;
469         gint bytes_read;
470         gint total_read_len = 0;
471         guint left = size;
472         gchar buf[BUFFSIZE];
473         gchar *msg;
474         struct timeval tv_prev, tv_cur;
475
476         gettimeofday(&tv_prev, NULL);
477
478         cur = data = g_malloc(size);
479
480         while (1) {
481                 bytes_read = sock_read(session->sock, cur, left);
482                 if (bytes_read <= 0) {
483                         g_free(data);
484                         return NULL;
485                 }
486                 g_print("child: received %d bytes of data from sock\n", bytes_read);
487                 left -= bytes_read;
488                 cur += bytes_read;
489                 total_read_len += bytes_read;
490                 if (left == 0)
491                         break;
492
493                 gettimeofday(&tv_cur, NULL);
494                 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
495                     tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
496                         g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
497                                    total_read_len, size);
498                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
499                         if ((msg = session_recv_msg(session)) == NULL) {
500                                 g_free(data);
501                                 return NULL;
502                         }
503                         g_free(msg);
504                         gettimeofday(&tv_prev, NULL);
505                 }
506         }
507
508         return data;
509 }
510
511 static guchar *session_recv_data_from_sock_unescape(Session *session,
512                                                     guint size)
513 {
514         guchar *data;
515         guchar *cur;
516         gint bytes_read;
517         gint total_read_len = 0;
518         guint left = size;
519         gchar buf[BUFFSIZE];
520         gchar *msg;
521         struct timeval tv_prev, tv_cur;
522
523         gettimeofday(&tv_prev, NULL);
524
525         cur = data = g_malloc(size);
526
527         while (1) {
528                 bytes_read = sock_gets(session->sock, buf, sizeof(buf));
529                 if (bytes_read <= 0) {
530                         g_free(data);
531                         return NULL;
532                 }
533
534                 if (buf[0] == '.' && buf[1] == '.') {
535                         bytes_read--;
536                         if (left < bytes_read)
537                                 bytes_read = left;
538                         memcpy(cur, buf + 1, bytes_read);
539                 } else {
540                         if (left < bytes_read)
541                                 bytes_read = left;
542                         memcpy(cur, buf, bytes_read);
543                 }
544                 left -= bytes_read;
545                 cur += bytes_read;
546                 total_read_len += bytes_read;
547                 if (left == 0)
548                         break;
549
550                 gettimeofday(&tv_cur, NULL);
551                 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
552                     tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
553                         g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
554                                    total_read_len, size);
555                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
556                         if ((msg = session_recv_msg(session)) == NULL) {
557                                 g_free(data);
558                                 return NULL;
559                         }
560                         g_free(msg);
561                         gettimeofday(&tv_prev, NULL);
562                 }
563         }
564
565         return data;
566 }
567
568 static SessionMsgType session_get_msg_type(const gchar *str)
569 {
570         if (!strncmp(str, "MESSAGE ", 8))
571                 return SESSION_MSG_NORMAL;
572         else if (!strncmp(str, "SENDDATA ", 9))
573                 return SESSION_MSG_SEND_DATA;
574         else if (!strncmp(str, "RECVDATA ", 9))
575                 return SESSION_MSG_RECV_DATA;
576         else if (!strncmp(str, "CONTROL ", 8))
577                 return SESSION_MSG_CONTROL;
578         else if (!strncmp(str, "ERROR ", 6))
579                 return SESSION_MSG_ERROR;
580         else
581                 return SESSION_MSG_UNKNOWN;
582 }
583
584 gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
585                                  gpointer data)
586 {
587         Session *session = SESSION(data);
588         gchar *msg;
589         gchar *msg_data;
590         gint len;
591         gint total;
592         guchar *recv_data;
593         guint size;
594         gint ret;
595
596         if ((msg = session_recv_msg(session)) == NULL) {
597                 session->state = SESSION_ERROR;
598                 return FALSE;
599         }
600
601         switch (session_get_msg_type(msg)) {
602         case SESSION_MSG_NORMAL:
603                 msg_data = msg + strlen("MESSAGE ");
604                 ret = session->recv_msg(session, msg_data);
605                 session->recv_msg_notify(session, msg_data,
606                                          session->recv_msg_notify_data);
607                 if (ret > 0)
608                         session_send_msg(session, SESSION_MSG_CONTROL,
609                                          "CONTINUE");
610                 else if (ret < 0) {
611                         session->state = SESSION_ERROR;
612                         g_free(msg);
613                         return FALSE;
614                 }
615                 break;
616         case SESSION_MSG_SEND_DATA:
617                 msg_data = msg + strlen("SENDDATA ");
618                 size = atoi(msg_data);
619                 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
620                 recv_data = session_read_data(session, size);
621                 if (!recv_data) {
622                         session->state = SESSION_ERROR;
623                         g_free(msg);
624                         return FALSE;
625                 }
626                 ret = session->recv_data_finished(session, recv_data, size);
627                 g_free(recv_data);
628                 session->recv_data_notify(session, size,
629                                           session->recv_data_notify_data);
630                 if (ret > 0)
631                         session_send_msg(session, SESSION_MSG_CONTROL,
632                                          "CONTINUE");
633                 else if (ret < 0) {
634                         session->state = SESSION_ERROR;
635                         g_free(msg);
636                         return FALSE;
637                 }
638                 break;
639         case SESSION_MSG_RECV_DATA:
640                 break;
641         case SESSION_MSG_CONTROL:
642                 msg_data = msg + strlen("CONTROL ");
643                 if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
644                         ret = sscanf(msg_data,
645                                      "DATARECVINPROG %d %d", &len, &total);
646                         if (ret != 2) {
647                                 g_warning("wrong control message: %s\n", msg);
648                                 session->state = SESSION_ERROR;
649                                 g_free(msg);
650                                 return FALSE;
651                         }
652                         session_send_msg(session, SESSION_MSG_CONTROL,
653                                          "CONTINUE");
654                         session->recv_data_progressive_notify
655                                 (session, len, total,
656                                  session->recv_data_progressive_notify_data);
657                 } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
658                         ret = sscanf(msg_data,
659                                      "DATASENDINPROG %d %d", &len, &total);
660                         if (ret != 2) {
661                                 g_warning("wrong control message: %s\n", msg);
662                                 session->state = SESSION_ERROR;
663                                 g_free(msg);
664                                 return FALSE;
665                         }
666                         session_send_msg(session, SESSION_MSG_CONTROL,
667                                          "CONTINUE");
668                         session->send_data_progressive_notify
669                                 (session, len, total,
670                                  session->send_data_progressive_notify_data);
671                 } else if (!strncmp(msg_data, "DATASENT ", 9)) {
672                         len = atoi(msg_data + 9);
673                         ret = session->send_data_finished(session, len);
674                         session->send_data_notify
675                                 (session, len, session->send_data_notify_data);
676                 } else if (!strcmp(msg_data, "DISCONNECTED")) {
677                         session->state = SESSION_DISCONNECTED;
678                         g_free(msg);
679                         return FALSE;
680                 } else {
681                         g_warning("wrong control message: %s\n", msg);
682                         session->state = SESSION_ERROR;
683                         g_free(msg);
684                         return FALSE;
685                 }
686                 break;
687         case SESSION_MSG_ERROR:
688         default:
689                 g_warning("error from child: %s\n", msg + strlen("ERROR "));
690                 session->state = SESSION_ERROR;
691                 g_free(msg);
692                 return FALSE;
693         }
694
695         g_free(msg);
696         return TRUE;
697 }
698
699 gboolean session_child_input(Session *session)
700 {
701         gchar buf[BUFFSIZE];
702         gchar *msg;
703         gchar *msg_data;
704         gchar *str;
705         guchar *send_data;
706         guchar *recv_data;
707         guint size;
708
709         if ((msg = session_recv_msg(session)) == NULL) {
710                 session_send_msg(session, SESSION_MSG_ERROR,
711                                  "receiving message failed.");
712                 session_close(session);
713                 session->state = SESSION_ERROR;
714                 return FALSE;
715         }
716
717         switch (session_get_msg_type(msg)) {
718         case SESSION_MSG_NORMAL:
719                 msg_data = msg + strlen("MESSAGE ");
720                 session->state = SESSION_SEND;
721                 sock_puts(session->sock, msg_data);
722                 session->state = SESSION_RECV;
723                 str = sock_getline(session->sock);
724                 if (!str) {
725                         session_send_msg(session, SESSION_MSG_ERROR,
726                                          "receiving message failed.");
727                         session_close(session);
728                         session->state = SESSION_ERROR;
729                         g_free(msg);
730                         return FALSE;
731                 }
732                 strretchomp(str);
733                 session_send_msg(session, SESSION_MSG_NORMAL, str);
734                 g_free(str);
735                 break;
736         case SESSION_MSG_SEND_DATA:
737                 msg_data = msg + strlen("SENDDATA ");
738                 size = atoi(msg_data);
739                 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
740                 send_data = session_read_data(session, size);
741                 if (!send_data) {
742                         session_send_msg(session, SESSION_MSG_ERROR,
743                                          "sending data failed.");
744                         session_close(session);
745                         session->state = SESSION_ERROR;
746                         g_free(msg);
747                         return FALSE;
748                 }
749                 session->state = SESSION_SEND;
750                 if (session_send_data_to_sock(session, send_data, size) < 0) {
751                         session_send_msg(session, SESSION_MSG_ERROR,
752                                          "sending data failed.");
753                         session_close(session);
754                         session->state = SESSION_ERROR;
755                         g_free(send_data);
756                         g_free(msg);
757                         return FALSE;
758                 }
759                 g_free(send_data);
760                 g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
761                 session_send_msg(session, SESSION_MSG_CONTROL, buf);
762                 break;
763         case SESSION_MSG_RECV_DATA:
764                 msg_data = msg + strlen("RECVDATA ");
765                 size = atoi(msg_data);
766                 session->state = SESSION_RECV;
767                 if (strstr(msg_data, "UNESCAPE") != NULL)
768                         recv_data = session_recv_data_from_sock_unescape
769                                 (session, size);
770                 else
771                         recv_data = session_recv_data_from_sock(session, size);
772                 if (!recv_data) {
773                         session_send_msg(session, SESSION_MSG_ERROR,
774                                          "receiving data failed.");
775                         session_close(session);
776                         session->state = SESSION_ERROR;
777                         g_free(msg);
778                         return FALSE;
779                 }
780                 if (session_send_data(session, recv_data, size) < 0) {
781                         session_close(session);
782                         session->state = SESSION_ERROR;
783                         g_free(recv_data);
784                         g_free(msg);
785                         return FALSE;
786                 }
787                 g_free(recv_data);
788                 break;
789         case SESSION_MSG_CONTROL:
790                 msg_data = msg + strlen("CONTROL ");
791                 if (!strcmp(msg_data, "CONTINUE")) {
792                         session->state = SESSION_RECV;
793                         str = sock_getline(session->sock);
794                         if (!str) {
795                                 session_send_msg(session, SESSION_MSG_ERROR,
796                                                  "receiving message failed.");
797                                 session_close(session);
798                                 session->state = SESSION_ERROR;
799                                 g_free(msg);
800                                 return FALSE;
801                         }
802                         strretchomp(str);
803                         session_send_msg(session, SESSION_MSG_NORMAL, str);
804                         g_free(str);
805                         break;
806 #if USE_OPENSSL
807                 } else if (!strcmp(msg_data, "STARTTLS")) {
808                         if (!ssl_init_socket_with_method(session->sock,
809                                                          SSL_METHOD_TLSv1)) {
810                                 session_send_msg(session, SESSION_MSG_ERROR,
811                                                  "can't start TLS session.");
812                                 session_close(session);
813                                 session->state = SESSION_ERROR;
814                                 g_free(msg);
815                                 return FALSE;
816                         }
817                         session_send_msg(session, SESSION_MSG_CONTROL,
818                                          "STARTTLSOK");
819                         break;
820 #endif
821                 } else if (!strcmp(msg_data, "DISCONNECT")) {
822                         sock_close(session->sock);
823                         session->sock = NULL;
824                         session->state = SESSION_DISCONNECTED;
825                         session_send_msg(session, SESSION_MSG_CONTROL,
826                                          "DISCONNECTED");
827                         g_free(msg);
828                         return FALSE;
829                 } else {
830                         session_send_msg(session, SESSION_MSG_ERROR,
831                                          "wrong control message.");
832                         session_close(session);
833                         session->state = SESSION_ERROR;
834                         g_free(msg);
835                         return FALSE;
836                 }
837                 break;
838         case SESSION_MSG_ERROR:
839         default:
840                 session_send_msg(session, SESSION_MSG_ERROR,
841                                  "error received from parent.");
842                 session_close(session);
843                 session->state = SESSION_ERROR;
844                 g_free(msg);
845                 return FALSE;
846         }
847
848         g_free(msg);
849         return TRUE;
850 }