sync with 0.8.11cvs12
[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                                                          guint   *actual_size);
56
57 gboolean session_parent_input_cb        (GIOChannel     *source,
58                                          GIOCondition    condition,
59                                          gpointer        data);
60
61 gboolean session_child_input            (Session        *session);
62
63
64 void session_init(Session *session)
65 {
66         session->type = 0;
67         session->sock = NULL;
68
69         session->server = NULL;
70         session->port = 0;
71         session->state = SESSION_READY;
72         session->last_access_time = time(NULL);
73         session->data = NULL;
74
75         session->read_ch = NULL;
76         session->write_ch = NULL;
77 }
78
79 gint session_connect(Session *session, const gchar *server, gushort port)
80 {
81         pid_t pid;
82         gint pipe_fds1[2], pipe_fds2[2];
83         SockInfo *sock;
84         gchar *str;
85
86         session->server = g_strdup(server);
87         session->port = port;
88
89         if (pipe(pipe_fds1) < 0) {
90                 perror("pipe");
91                 return -1;
92         }
93         if (pipe(pipe_fds2) < 0) {
94                 perror("pipe");
95                 close(pipe_fds1[0]);
96                 close(pipe_fds1[1]);
97                 return -1;
98         }
99
100         if ((pid = fork()) < 0) {
101                 perror("fork");
102                 return -1;
103         }
104
105         if (pid != 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]);
109                 close(pipe_fds1[0]);
110                 close(pipe_fds2[1]);
111                 session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
112                                                    session_parent_input_cb,
113                                                    session);
114                 return 0;
115         }
116
117         /* child process */
118
119         session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
120         session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
121         close(pipe_fds1[1]);
122         close(pipe_fds2[0]);
123
124         g_print("child: connecting to %s:%d ...\n", server, port);
125
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);
130                 _exit(1);
131         }
132
133 #if USE_OPENSSL
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);
138                 _exit(1);
139         }
140 #endif
141
142         g_print("child: connected\n");
143
144         session->sock = sock;
145         session->state = SESSION_RECV;
146
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);
151                 _exit(1);
152         }
153         strretchomp(str);
154         session_send_msg(session, SESSION_MSG_NORMAL, str);
155         g_free(str);
156
157         while (session_child_input(session) == TRUE)
158                 ;
159
160         session_close(session);
161
162         g_print("child: disconnected\n");
163
164         _exit(0);
165 }
166
167 gint session_disconnect(Session *session)
168 {
169         g_print("%s: session_disconnect()\n", session->child_pid == 0 ? "child" : "parent");
170         session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
171         return 0;
172 }
173
174 void session_destroy(Session *session)
175 {
176         g_return_if_fail(session != NULL);
177         g_return_if_fail(session->destroy != NULL);
178
179         g_print("session_destroy()\n");
180         session_close(session);
181         session->destroy(session);
182         g_free(session->server);
183         g_free(session);
184 }
185
186 void session_set_recv_message_notify(Session *session,
187                                      RecvMsgNotify notify_func, gpointer data)
188 {
189         session->recv_msg_notify = notify_func;
190         session->recv_msg_notify_data = data;
191 }
192
193 void session_set_recv_data_progressive_notify
194                                         (Session *session,
195                                          RecvDataProgressiveNotify notify_func,
196                                          gpointer data)
197 {
198         session->recv_data_progressive_notify = notify_func,
199         session->recv_data_progressive_notify_data = data;
200 }
201
202 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
203                                   gpointer data)
204 {
205         session->recv_data_notify = notify_func;
206         session->recv_data_notify_data = data;
207 }
208
209 void session_set_send_data_progressive_notify
210                                         (Session *session,
211                                          SendDataProgressiveNotify notify_func,
212                                          gpointer data)
213 {
214         session->send_data_progressive_notify = notify_func;
215         session->send_data_progressive_notify_data = data;
216 }
217
218 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
219                                   gpointer data)
220 {
221         session->send_data_notify = notify_func;
222         session->send_data_notify_data = data;
223 }
224
225 static gint session_close(Session *session)
226 {
227         g_return_val_if_fail(session != NULL, -1);
228
229         g_print("%s: session_close()\n", session->child_pid == 0 ? "child" : "parent");
230
231         if (session->read_tag > 0) {
232                 g_source_remove(session->read_tag);
233                 session->read_tag = 0;
234         }
235
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;
240         }
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;
245         }
246
247         if (session->sock) {
248                 sock_close(session->sock);
249                 session->sock = NULL;
250                 session->state = SESSION_DISCONNECTED;
251         }
252
253         if (session->child_pid) {
254                 kill(session->child_pid, SIGTERM);
255                 waitpid(session->child_pid, NULL, 0);
256                 session->child_pid = 0;
257         }
258
259         return 0;
260 }
261
262 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
263 {
264         gchar *prefix;
265         gchar *str;
266         guint size;
267         guint bytes_written;
268
269         switch (type) {
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;
280         default:
281                 return -1;
282         }
283
284         str = g_strdup_printf("%s %s\n", prefix, msg);
285         /* g_print("%s: sending message: %s", session->child_pid == 0 ? "child" : "parent", str); */
286         size = strlen(str);
287
288         while (size > 0) {
289                 if (g_io_channel_write(session->write_ch, str, size,
290                                        &bytes_written)
291                     != G_IO_ERROR_NONE || bytes_written == 0) {
292                         g_warning("%s: sending message failed.\n",
293                                   session->child_pid == 0 ? "child" : "parent");
294                         return -1;
295                 }
296                 size -= bytes_written;
297         }
298
299         return 0;
300 }
301
302 static gchar *session_recv_msg(Session *session)
303 {
304         gchar buf[BUFFSIZE];
305         gchar *str = NULL;
306         guint size = 1;
307         guint bytes_read;
308
309         for (;;) {
310                 if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
311                                       &bytes_read)
312                     != G_IO_ERROR_NONE || bytes_read == 0) {
313                         g_warning("%s: receiving message failed.\n",
314                                   session->child_pid == 0 ? "child" : "parent");
315                         g_free(str);
316                         str = NULL;
317                         break;
318                 }
319
320                 size += bytes_read;
321                 buf[bytes_read] = '\0';
322
323                 if (!str)
324                         str = g_strdup(buf);
325                 else {
326                         str = g_realloc(str, size);
327                         strcat(str, buf);
328                 }
329                 if (str[size - 2] == '\n') {
330                         str[size - 2] = '\0';
331
332                         g_print("%s: received message: %s\n", session->child_pid == 0 ? "child" : "parent", str);
333
334                         break;
335                 }
336         }
337
338         return str;
339 }
340
341 #if USE_OPENSSL
342 gint session_start_tls(Session *session)
343 {
344         gchar *ctl_msg;
345
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) {
349                 g_free(ctl_msg);
350                 return -1;
351         }
352         g_free(ctl_msg);
353
354         return 0;
355 }
356 #endif
357
358 gint session_send_data(Session *session, const guchar *data, guint size)
359 {
360         gchar *msg;
361         guint bytes_written;
362         GIOError err;
363
364         session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
365         if ((msg = session_recv_msg(session)) == NULL)
366                 return -1;
367         g_free(msg);
368
369         while (size > 0) {
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",
375                                   err);
376                         return -1;
377                 }
378                 size -= bytes_written;
379                 g_print("%s: sent %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_written);
380         }
381
382         return 0;
383 }
384
385 gint session_recv_data(Session *session, guint size, gboolean unescape_dot)
386 {
387         if (unescape_dot) {
388                 gchar buf[BUFFSIZE];
389
390                 g_snprintf(buf, sizeof(buf), "%d UNESCAPE", size);
391                 session_send_msg(session, SESSION_MSG_RECV_DATA, buf);
392         } else
393                 session_send_msg(session, SESSION_MSG_RECV_DATA, itos(size));
394         return 0;
395 }
396
397 static guchar *session_read_data(Session *session, guint size)
398 {
399         guchar *data;
400         guchar *cur;
401         guint bytes_read;
402         GIOError err;
403
404         cur = data = g_malloc(size);
405
406         while (size > 0) {
407                 if ((err = g_io_channel_read(session->read_ch, cur, size,
408                                              &bytes_read))
409                     != G_IO_ERROR_NONE || bytes_read == 0) {
410                         g_warning("%s: reading data failed: %d\n",
411                                   session->child_pid == 0 ? "child" : "parent",
412                                   err);
413                         g_free(data);
414                         return NULL;
415                 }
416                 size -= bytes_read;
417                 cur += bytes_read;
418                 g_print("%s: received %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_read);
419         }
420
421         return data;
422 }
423
424 #define MAX_CHUNK_SIZE 4096
425
426 static gint session_send_data_to_sock(Session *session, const guchar *data,
427                                       guint size)
428 {
429         const guchar *cur = data;
430         gint bytes_written;
431         gint total_write_len = 0;
432         guint left = size;
433         gchar buf[BUFFSIZE];
434         gchar *msg;
435         struct timeval tv_prev, tv_cur;
436
437         gettimeofday(&tv_prev, NULL);
438
439         while (1) {
440                 bytes_written = sock_write(session->sock, cur,
441                                            MIN(left, MAX_CHUNK_SIZE));
442                 if (bytes_written <= 0)
443                         return -1;
444                 left -= bytes_written;
445                 cur += bytes_written;
446                 total_write_len += bytes_written;
447                 if (left == 0)
448                         break;
449
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)
457                                 return -1;
458                         g_free(msg);
459                         gettimeofday(&tv_prev, NULL);
460                 }
461         }
462
463         return 0;
464 }
465
466 static guchar *session_recv_data_from_sock(Session *session, guint size)
467 {
468         guchar *data;
469         guchar *cur;
470         gint bytes_read;
471         gint total_read_len = 0;
472         guint left = size;
473         gchar buf[BUFFSIZE];
474         gchar *msg;
475         struct timeval tv_prev, tv_cur;
476
477         gettimeofday(&tv_prev, NULL);
478
479         cur = data = g_malloc(size);
480
481         while (1) {
482                 bytes_read = sock_read(session->sock, cur, left);
483                 if (bytes_read <= 0) {
484                         g_free(data);
485                         return NULL;
486                 }
487                 g_print("child: received %d bytes of data from sock\n", bytes_read);
488                 left -= bytes_read;
489                 cur += bytes_read;
490                 total_read_len += bytes_read;
491                 if (left == 0)
492                         break;
493
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) {
501                                 g_free(data);
502                                 return NULL;
503                         }
504                         g_free(msg);
505                         gettimeofday(&tv_prev, NULL);
506                 }
507         }
508
509         return data;
510 }
511
512 static guchar *session_recv_data_from_sock_unescape(Session *session,
513                                                     guint size,
514                                                     guint *actual_size)
515 {
516         GString *data;
517         guchar *ret_data;
518         gint bytes_read;
519         gchar buf[BUFFSIZE];
520         gchar *msg;
521         struct timeval tv_prev, tv_cur;
522
523         gettimeofday(&tv_prev, NULL);
524
525         data = g_string_sized_new(size + 1);
526         *actual_size = 0;
527
528         while (1) {
529                 bytes_read = sock_gets(session->sock, buf, sizeof(buf));
530                 if (bytes_read <= 0) {
531                         g_string_free(data, TRUE);
532                         return NULL;
533                 }
534
535                 if (buf[0] == '.' && buf[1] == '\r' && buf[2] == '\n')
536                         break;
537                 if (buf[0] == '.' && buf[1] == '.')
538                         g_string_append(data, buf + 1);
539                 else
540                         g_string_append(data, buf);
541
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);
550                                 return NULL;
551                         }
552                         g_free(msg);
553                         gettimeofday(&tv_prev, NULL);
554                 }
555         }
556
557         ret_data = data->str;
558         *actual_size = data->len;
559         g_string_free(data, FALSE);
560
561         return ret_data;
562 }
563
564 static SessionMsgType session_get_msg_type(const gchar *str)
565 {
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;
576         else
577                 return SESSION_MSG_UNKNOWN;
578 }
579
580 gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
581                                  gpointer data)
582 {
583         Session *session = SESSION(data);
584         gchar *msg;
585         gchar *msg_data;
586         gint len;
587         gint total;
588         guchar *recv_data;
589         guint size;
590         gint ret;
591
592         if ((msg = session_recv_msg(session)) == NULL) {
593                 session->state = SESSION_ERROR;
594                 return FALSE;
595         }
596
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);
603                 if (ret > 0)
604                         session_send_msg(session, SESSION_MSG_CONTROL,
605                                          "CONTINUE");
606                 else if (ret < 0) {
607                         session->state = SESSION_ERROR;
608                         g_free(msg);
609                         return FALSE;
610                 }
611                 break;
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);
617                 if (!recv_data) {
618                         session->state = SESSION_ERROR;
619                         g_free(msg);
620                         return FALSE;
621                 }
622                 ret = session->recv_data_finished(session, recv_data, size);
623                 g_free(recv_data);
624                 session->recv_data_notify(session, size,
625                                           session->recv_data_notify_data);
626                 if (ret > 0)
627                         session_send_msg(session, SESSION_MSG_CONTROL,
628                                          "CONTINUE");
629                 else if (ret < 0) {
630                         session->state = SESSION_ERROR;
631                         g_free(msg);
632                         return FALSE;
633                 }
634                 break;
635         case SESSION_MSG_RECV_DATA:
636                 break;
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);
642                         if (ret != 2) {
643                                 g_warning("wrong control message: %s\n", msg);
644                                 session->state = SESSION_ERROR;
645                                 g_free(msg);
646                                 return FALSE;
647                         }
648                         session_send_msg(session, SESSION_MSG_CONTROL,
649                                          "CONTINUE");
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);
656                         if (ret != 2) {
657                                 g_warning("wrong control message: %s\n", msg);
658                                 session->state = SESSION_ERROR;
659                                 g_free(msg);
660                                 return FALSE;
661                         }
662                         session_send_msg(session, SESSION_MSG_CONTROL,
663                                          "CONTINUE");
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;
674                         g_free(msg);
675                         return FALSE;
676                 } else {
677                         g_warning("wrong control message: %s\n", msg);
678                         session->state = SESSION_ERROR;
679                         g_free(msg);
680                         return FALSE;
681                 }
682                 break;
683         case SESSION_MSG_ERROR:
684         default:
685                 g_warning("error from child: %s\n", msg + strlen("ERROR "));
686                 session->state = SESSION_ERROR;
687                 g_free(msg);
688                 return FALSE;
689         }
690
691         g_free(msg);
692         return TRUE;
693 }
694
695 gboolean session_child_input(Session *session)
696 {
697         gchar buf[BUFFSIZE];
698         gchar *msg;
699         gchar *msg_data;
700         gchar *str;
701         guchar *send_data;
702         guchar *recv_data;
703         guint size;
704         guint actual_size;
705
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;
711                 return FALSE;
712         }
713
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);
721                 if (!str) {
722                         session_send_msg(session, SESSION_MSG_ERROR,
723                                          "receiving message failed.");
724                         session_close(session);
725                         session->state = SESSION_ERROR;
726                         g_free(msg);
727                         return FALSE;
728                 }
729                 strretchomp(str);
730                 session_send_msg(session, SESSION_MSG_NORMAL, str);
731                 g_free(str);
732                 break;
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);
738                 if (!send_data) {
739                         session_send_msg(session, SESSION_MSG_ERROR,
740                                          "sending data failed.");
741                         session_close(session);
742                         session->state = SESSION_ERROR;
743                         g_free(msg);
744                         return FALSE;
745                 }
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;
752                         g_free(send_data);
753                         g_free(msg);
754                         return FALSE;
755                 }
756                 g_free(send_data);
757                 g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
758                 session_send_msg(session, SESSION_MSG_CONTROL, buf);
759                 break;
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);
767                         size = actual_size;
768                 } else
769                         recv_data = session_recv_data_from_sock(session, size);
770                 if (!recv_data) {
771                         session_send_msg(session, SESSION_MSG_ERROR,
772                                          "receiving data failed.");
773                         session_close(session);
774                         session->state = SESSION_ERROR;
775                         g_free(msg);
776                         return FALSE;
777                 }
778                 if (session_send_data(session, recv_data, size) < 0) {
779                         session_close(session);
780                         session->state = SESSION_ERROR;
781                         g_free(recv_data);
782                         g_free(msg);
783                         return FALSE;
784                 }
785                 g_free(recv_data);
786                 break;
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);
792                         if (!str) {
793                                 session_send_msg(session, SESSION_MSG_ERROR,
794                                                  "receiving message failed.");
795                                 session_close(session);
796                                 session->state = SESSION_ERROR;
797                                 g_free(msg);
798                                 return FALSE;
799                         }
800                         strretchomp(str);
801                         session_send_msg(session, SESSION_MSG_NORMAL, str);
802                         g_free(str);
803                         break;
804 #if USE_OPENSSL
805                 } else if (!strcmp(msg_data, "STARTTLS")) {
806                         if (!ssl_init_socket_with_method(session->sock,
807                                                          SSL_METHOD_TLSv1)) {
808                                 session_send_msg(session, SESSION_MSG_ERROR,
809                                                  "can't start TLS session.");
810                                 session_close(session);
811                                 session->state = SESSION_ERROR;
812                                 g_free(msg);
813                                 return FALSE;
814                         }
815                         session_send_msg(session, SESSION_MSG_CONTROL,
816                                          "STARTTLSOK");
817                         break;
818 #endif
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,
824                                          "DISCONNECTED");
825                         g_free(msg);
826                         return FALSE;
827                 } else {
828                         session_send_msg(session, SESSION_MSG_ERROR,
829                                          "wrong control message.");
830                         session_close(session);
831                         session->state = SESSION_ERROR;
832                         g_free(msg);
833                         return FALSE;
834                 }
835                 break;
836         case SESSION_MSG_ERROR:
837         default:
838                 session_send_msg(session, SESSION_MSG_ERROR,
839                                  "error received from parent.");
840                 session_close(session);
841                 session->state = SESSION_ERROR;
842                 g_free(msg);
843                 return FALSE;
844         }
845
846         g_free(msg);
847         return TRUE;
848 }