f92ddae5610afcf003c296e138671ae47e65dc3e
[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 /*!
65  *\brief        init session members to zero
66  *
67  *\param        session to be initialized
68  */
69 void session_init(Session *session)
70 {
71         session->type = 0;
72         session->sock = NULL;
73
74         session->server = NULL;
75         session->port = 0;
76         session->state = SESSION_READY;
77         session->last_access_time = time(NULL);
78         session->data = NULL;
79
80         session->read_ch = NULL;
81         session->write_ch = NULL;
82 }
83
84 /*!
85  *\brief        Set up parent and child process
86  *              Childloop: Read commands from parent,
87  *              send to server, get answer, pass to parent
88  *
89  *\param        session Contains session information
90  *              server to connect to
91  *              port to connect to
92  *
93  *\return        0 : success
94  *              -1 : pipe / fork errors (parent)
95  *               1 : connection error (child)
96  */
97 gint session_connect(Session *session, const gchar *server, gushort port)
98 {
99         pid_t pid;
100         gint pipe_fds1[2], pipe_fds2[2];
101         SockInfo *sock;
102         gchar *str;
103
104         session->server = g_strdup(server);
105         session->port = port;
106
107         if (pipe(pipe_fds1) < 0) {
108                 perror("pipe");
109                 return -1;
110         }
111         if (pipe(pipe_fds2) < 0) {
112                 perror("pipe");
113                 close(pipe_fds1[0]);
114                 close(pipe_fds1[1]);
115                 return -1;
116         }
117
118         if ((pid = fork()) < 0) {
119                 perror("fork");
120                 return -1;
121         }
122
123         if (pid != 0) {
124                 session->child_pid = pid;
125                 session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
126                 session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
127                 close(pipe_fds1[0]);
128                 close(pipe_fds2[1]);
129                 session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
130                                                    session_parent_input_cb,
131                                                    session);
132                 return 0;
133         }
134
135         /* child process */
136
137         session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
138         session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
139         close(pipe_fds1[1]);
140         close(pipe_fds2[0]);
141
142         debug_print("session: child: connecting to %s:%d ...\n", server, port);
143
144         if ((sock = sock_connect(server, port)) == NULL) {
145                 session_send_msg(session, SESSION_MSG_ERROR,
146                                  "can't connect to server.");
147                 session_close(session);
148                 _exit(1);
149         }
150
151 #if USE_OPENSSL
152         if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
153                 session_send_msg(session, SESSION_MSG_ERROR,
154                                  "can't initialize SSL.");
155                 session_close(session);
156                 _exit(1);
157         }
158 #endif
159
160         debug_print("session: child: connected\n");
161
162         session->sock = sock;
163         session->state = SESSION_RECV;
164
165         if ((str = sock_getline(sock)) == NULL) {
166                 session_send_msg(session, SESSION_MSG_ERROR,
167                                  "can't get server response.");
168                 session_close(session);
169                 _exit(1);
170         }
171         strretchomp(str);
172         session_send_msg(session, SESSION_MSG_NORMAL, str);
173         g_free(str);
174
175         while (session_child_input(session) == TRUE)
176                 ;
177
178         session_close(session);
179
180         debug_print("session: child: disconnected\n");
181
182         _exit(0);
183 }
184
185 /*!
186  *\brief        child and parent: send DISCONNECT message to other process
187  *
188  *\param        session Contains session information
189  *
190  *\return        0 : success
191  */
192 gint session_disconnect(Session *session)
193 {
194         debug_print("session: %s: session_disconnect()\n",
195                     session->child_pid == 0 ? "child" : "parent");
196         session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
197         return 0;
198 }
199
200 /*!
201  *\brief        parent ?
202  *
203  *\param        session Contains session information
204  */
205 void session_destroy(Session *session)
206 {
207         g_return_if_fail(session != NULL);
208         g_return_if_fail(session->destroy != NULL);
209
210         debug_print("session: session_destroy()\n");
211         session_close(session);
212         session->destroy(session);
213         g_free(session->server);
214         g_free(session);
215 }
216
217 void session_set_recv_message_notify(Session *session,
218                                      RecvMsgNotify notify_func, gpointer data)
219 {
220         session->recv_msg_notify = notify_func;
221         session->recv_msg_notify_data = data;
222 }
223
224 void session_set_recv_data_progressive_notify
225                                         (Session *session,
226                                          RecvDataProgressiveNotify notify_func,
227                                          gpointer data)
228 {
229         session->recv_data_progressive_notify = notify_func,
230         session->recv_data_progressive_notify_data = data;
231 }
232
233 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
234                                   gpointer data)
235 {
236         session->recv_data_notify = notify_func;
237         session->recv_data_notify_data = data;
238 }
239
240 void session_set_send_data_progressive_notify
241                                         (Session *session,
242                                          SendDataProgressiveNotify notify_func,
243                                          gpointer data)
244 {
245         session->send_data_progressive_notify = notify_func;
246         session->send_data_progressive_notify_data = data;
247 }
248
249 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
250                                   gpointer data)
251 {
252         session->send_data_notify = notify_func;
253         session->send_data_notify_data = data;
254 }
255
256 /*!
257  *\brief        child and parent cleanup (child closes first)
258  *
259  *\param        session Contains session information
260  *
261  *\return        0 : success
262  */
263 static gint session_close(Session *session)
264 {
265         g_return_val_if_fail(session != NULL, -1);
266
267         debug_print("session: %s: session_close()\n",
268                     session->child_pid == 0 ? "child" : "parent");
269
270         if (session->read_tag > 0) {
271                 g_source_remove(session->read_tag);
272                 session->read_tag = 0;
273         }
274         
275         if (session->read_ch) {
276                 g_io_channel_close(session->read_ch);
277                 g_io_channel_unref(session->read_ch);
278                 session->read_ch = NULL;
279         }
280         
281         if (session->write_ch) {
282                 g_io_channel_close(session->write_ch);
283                 g_io_channel_unref(session->write_ch);
284                 session->write_ch = NULL;
285         }
286
287         if (session->sock) {
288                 sock_close(session->sock);
289                 session->sock = NULL;
290                 session->state = SESSION_DISCONNECTED;
291         }
292
293         if (session->child_pid) {
294                 if (session->state != SESSION_DISCONNECTED)
295                         kill(session->child_pid, SIGTERM);
296                 waitpid(session->child_pid, NULL, 0);
297                 session->child_pid = 0;
298         }
299
300         return 0;
301 }
302
303 /*!
304  *\brief        child and parent: send control message to other process
305  *
306  *\param        session Contains session information
307  *              type Kind of data (commands or message data)
308  *              msg Data
309  *
310  *\return        0 : success
311  *              -1 : error
312  */
313 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
314 {
315         gchar *prefix;
316         gchar *str;
317         gchar *cur;
318         guint size;
319         guint bytes_written;
320
321         switch (type) {
322         case SESSION_MSG_NORMAL:
323                 prefix = "MESSAGE"; break;
324         case SESSION_MSG_SEND_DATA:
325                 prefix = "SENDDATA"; break;
326         case SESSION_MSG_RECV_DATA:
327                 prefix = "RECVDATA"; break;
328         case SESSION_MSG_CONTROL:
329                 prefix = "CONTROL"; break;
330         case SESSION_MSG_ERROR:
331                 prefix = "ERROR"; break;
332         default:
333                 return -1;
334         }
335
336         cur = str = g_strdup_printf("%s %s\n", prefix, msg);
337         size = strlen(str);
338
339         while (size > 0) {
340                 if (g_io_channel_write(session->write_ch, cur, size,
341                                        &bytes_written)
342                     != G_IO_ERROR_NONE || bytes_written == 0) {
343                         g_warning("%s: sending message failed.\n",
344                                   session->child_pid == 0 ? "child" : "parent");
345                         g_free(str);
346                         return -1;
347                 }
348                 size -= bytes_written;
349                 cur += bytes_written;
350         }
351
352         g_free(str);
353
354         return 0;
355 }
356
357 /*!
358  *\brief        child and parent receive function
359  *
360  *\param        session Contains session information
361  *
362  *\return       Message read by current session
363  */
364 static gchar *session_recv_msg(Session *session)
365 {
366         gchar buf[BUFFSIZE];
367         gchar *str = NULL;
368         guint size = 1;
369         guint bytes_read;
370
371         for (;;) {
372                 if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
373                                       &bytes_read)
374                     != G_IO_ERROR_NONE || bytes_read == 0) {
375                         g_warning("%s: receiving message failed.\n",
376                                   session->child_pid == 0 ? "child" : "parent");
377                         g_free(str);
378                         str = NULL;
379                         break;
380                 }
381
382                 size += bytes_read;
383                 buf[bytes_read] = '\0';
384
385                 if (!str)
386                         str = g_strdup(buf);
387                 else {
388                         str = g_realloc(str, size);
389                         strcat(str, buf);
390                 }
391                 if (str[size - 2] == '\n') {
392                         str[size - 2] = '\0';
393                         break;
394                 }
395         }
396
397         return str;
398 }
399
400 #if USE_OPENSSL
401 gint session_start_tls(Session *session)
402 {
403         gchar *ctl_msg;
404
405         session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
406         ctl_msg = session_recv_msg(session);
407         if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
408                 g_free(ctl_msg);
409                 return -1;
410         }
411         g_free(ctl_msg);
412
413         return 0;
414 }
415 #endif
416
417 /*!
418  *\brief        parent (child?): send data to other process
419  *
420  *\param        session Contains session information
421  *              data Data to send
422  *              size Bytes to send
423  *
424  *\return        0 : success
425  *              -1 : error
426  */
427 gint session_send_data(Session *session, const guchar *data, guint size)
428 {
429         gchar *msg;
430         const guchar *cur = data;
431         guint bytes_written;
432         GIOError err;
433
434         session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
435         if ((msg = session_recv_msg(session)) == NULL)
436                 return -1;
437         g_free(msg);
438
439         while (size > 0) {
440                 if ((err = g_io_channel_write(session->write_ch, (guchar *)cur,
441                                               size, &bytes_written))
442                     != G_IO_ERROR_NONE || bytes_written == 0) {
443                         g_warning("%s: sending data failed: %d\n",
444                                   session->child_pid == 0 ? "child" : "parent",
445                                   err);
446                         return -1;
447                 }
448                 size -= bytes_written;
449                 cur += bytes_written;
450                 debug_print("session: %s: sent %d bytes of data\n",
451                             session->child_pid == 0 ? "child" : "parent",
452                             bytes_written);
453         }
454
455         return 0;
456 }
457
458 gint session_recv_data(Session *session, guint size, gboolean unescape_dot)
459 {
460         if (unescape_dot) {
461                 gchar buf[BUFFSIZE];
462
463                 g_snprintf(buf, sizeof(buf), "%d UNESCAPE", size);
464                 session_send_msg(session, SESSION_MSG_RECV_DATA, buf);
465         } else
466                 session_send_msg(session, SESSION_MSG_RECV_DATA, itos(size));
467         return 0;
468 }
469
470 /*!
471  *\brief        child (parent?): read data from other process
472  *
473  *\param        session Contains session information
474  *              size Bytes to read
475  *
476  *\return       data read from session
477  */
478 static guchar *session_read_data(Session *session, guint size)
479 {
480         guchar *data;
481         guchar *cur;
482         guint bytes_read;
483         GIOError err;
484
485         cur = data = g_malloc(size);
486
487         while (size > 0) {
488                 if ((err = g_io_channel_read(session->read_ch, cur, size,
489                                              &bytes_read))
490                     != G_IO_ERROR_NONE || bytes_read == 0) {
491                         g_warning("%s: reading data failed: %d\n",
492                                   session->child_pid == 0 ? "child" : "parent",
493                                   err);
494                         g_free(data);
495                         return NULL;
496                 }
497                 size -= bytes_read;
498                 cur += bytes_read;
499                 debug_print("session: %s: received %d bytes of data\n",
500                             session->child_pid == 0 ? "child" : "parent",
501                             bytes_read);
502         }
503
504         return data;
505 }
506
507 #define MAX_CHUNK_SIZE 4096
508
509 /*!
510  *\brief        child: Send session data to server
511  *
512  *\param        session Contains session information
513  *              data Data to send to server
514  *              size Bytes to send
515  *
516  *\return        0 : success
517  *              -1 : error
518  */
519 static gint session_send_data_to_sock(Session *session, const guchar *data,
520                                       guint size)
521 {
522         const guchar *cur = data;
523         gint bytes_written;
524         gint total_write_len = 0;
525         guint left = size;
526         gchar buf[BUFFSIZE];
527         gchar *msg;
528         struct timeval tv_prev, tv_cur;
529
530         gettimeofday(&tv_prev, NULL);
531
532         while (1) {
533                 bytes_written = sock_write(session->sock, cur,
534                                            MIN(left, MAX_CHUNK_SIZE));
535                 if (bytes_written <= 0)
536                         return -1;
537                 left -= bytes_written;
538                 cur += bytes_written;
539                 total_write_len += bytes_written;
540                 if (left == 0)
541                         break;
542
543                 gettimeofday(&tv_cur, NULL);
544                 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
545                     tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
546                         g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
547                                    total_write_len, size);
548                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
549                         if ((msg = session_recv_msg(session)) == NULL)
550                                 return -1;
551                         g_free(msg);
552                         gettimeofday(&tv_prev, NULL);
553                 }
554         }
555
556         return 0;
557 }
558
559 /*!
560  *\brief        child: Read answer/data from server
561  *
562  *\param        session Contains session information
563  *              size Max bytes to receive
564  *
565  *\return       Server answer
566  */
567 static guchar *session_recv_data_from_sock(Session *session, guint size)
568 {
569         guchar *data;
570         guchar *cur;
571         gint bytes_read;
572         gint total_read_len = 0;
573         guint left = size;
574         gchar buf[BUFFSIZE];
575         gchar *msg;
576         struct timeval tv_prev, tv_cur;
577
578         gettimeofday(&tv_prev, NULL);
579
580         cur = data = g_malloc(size);
581
582         while (1) {
583                 bytes_read = sock_read(session->sock, cur, left);
584                 if (bytes_read <= 0) {
585                         g_free(data);
586                         return NULL;
587                 }
588                 debug_print("session: child: "
589                             "received %d bytes of data from sock\n",
590                             bytes_read);
591                 left -= bytes_read;
592                 cur += bytes_read;
593                 total_read_len += bytes_read;
594                 if (left == 0)
595                         break;
596
597                 gettimeofday(&tv_cur, NULL);
598                 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
599                     tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
600                         g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
601                                    total_read_len, size);
602                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
603                         if ((msg = session_recv_msg(session)) == NULL) {
604                                 g_free(data);
605                                 return NULL;
606                         }
607                         g_free(msg);
608                         gettimeofday(&tv_prev, NULL);
609                 }
610         }
611
612         return data;
613 }
614
615 static guchar *session_recv_data_from_sock_unescape(Session *session,
616                                                     guint size,
617                                                     guint *actual_size)
618 {
619         GString *data;
620         guchar *ret_data;
621         gint bytes_read;
622         gchar buf[BUFFSIZE];
623         gchar *msg;
624         struct timeval tv_prev, tv_cur;
625
626         gettimeofday(&tv_prev, NULL);
627
628         data = g_string_sized_new(size + 1);
629         *actual_size = 0;
630
631         while (1) {
632                 bytes_read = sock_gets(session->sock, buf, sizeof(buf));
633                 if (bytes_read <= 0) {
634                         g_string_free(data, TRUE);
635                         return NULL;
636                 }
637
638                 if (buf[0] == '.' && buf[1] == '\r' && buf[2] == '\n')
639                         break;
640                 if (buf[0] == '.' && buf[1] == '.')
641                         g_string_append(data, buf + 1);
642                 else
643                         g_string_append(data, buf);
644
645                 gettimeofday(&tv_cur, NULL);
646                 if (tv_cur.tv_sec - tv_prev.tv_sec > 0 ||
647                     tv_cur.tv_usec - tv_prev.tv_usec > UI_REFRESH_INTERVAL) {
648                         g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
649                                    data->len, MAX(data->len, size));
650                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
651                         if ((msg = session_recv_msg(session)) == NULL) {
652                                 g_string_free(data, TRUE);
653                                 return NULL;
654                         }
655                         g_free(msg);
656                         gettimeofday(&tv_prev, NULL);
657                 }
658         }
659
660         ret_data = data->str;
661         *actual_size = data->len;
662         g_string_free(data, FALSE);
663
664         return ret_data;
665 }
666
667 /*!
668  *\brief        Return if message is an internal command or server data
669  *
670  *\param        str Message to analyze
671  *
672  *\return       Type of message
673  */
674 static SessionMsgType session_get_msg_type(const gchar *str)
675 {
676         if (!strncmp(str, "MESSAGE ", 8))
677                 return SESSION_MSG_NORMAL;
678         else if (!strncmp(str, "SENDDATA ", 9))
679                 return SESSION_MSG_SEND_DATA;
680         else if (!strncmp(str, "RECVDATA ", 9))
681                 return SESSION_MSG_RECV_DATA;
682         else if (!strncmp(str, "CONTROL ", 8))
683                 return SESSION_MSG_CONTROL;
684         else if (!strncmp(str, "ERROR ", 6))
685                 return SESSION_MSG_ERROR;
686         else
687                 return SESSION_MSG_UNKNOWN;
688 }
689
690 /*!
691  *\brief        parent: Received data from child
692  *
693  *\param        source Channel watching child pipe
694  *              condition Unused (IN, HUP, OUT)
695  *              data Contains session information
696  *
697  *\return       FALSE to remove watching channel
698  */
699 gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
700                                  gpointer data)
701 {
702         Session *session = SESSION(data);
703         gchar *msg;
704         gchar *msg_data;
705         gint len;
706         gint total;
707         guchar *recv_data;
708         guint size;
709         gint ret;
710
711         if ((msg = session_recv_msg(session)) == NULL) {
712                 session->state = SESSION_ERROR;
713                 return FALSE;
714         }
715
716         switch (session_get_msg_type(msg)) {
717         case SESSION_MSG_NORMAL:
718                 msg_data = msg + strlen("MESSAGE ");
719                 ret = session->recv_msg(session, msg_data);
720                 session->recv_msg_notify(session, msg_data,
721                                          session->recv_msg_notify_data);
722                 if (ret > 0)
723                         session_send_msg(session, SESSION_MSG_CONTROL,
724                                          "CONTINUE");
725                 else if (ret < 0) {
726                         session->state = SESSION_ERROR;
727                         g_free(msg);
728                         return FALSE;
729                 }
730                 break;
731         case SESSION_MSG_SEND_DATA:
732                 msg_data = msg + strlen("SENDDATA ");
733                 size = atoi(msg_data);
734                 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
735                 recv_data = session_read_data(session, size);
736                 if (!recv_data) {
737                         session->state = SESSION_ERROR;
738                         g_free(msg);
739                         return FALSE;
740                 }
741                 ret = session->recv_data_finished(session, recv_data, size);
742                 g_free(recv_data);
743                 session->recv_data_notify(session, size,
744                                           session->recv_data_notify_data);
745                 if (ret > 0)
746                         session_send_msg(session, SESSION_MSG_CONTROL,
747                                          "CONTINUE");
748                 else if (ret < 0) {
749                         session->state = SESSION_ERROR;
750                         g_free(msg);
751                         return FALSE;
752                 }
753                 break;
754         case SESSION_MSG_RECV_DATA:
755                 break;
756         case SESSION_MSG_CONTROL:
757                 msg_data = msg + strlen("CONTROL ");
758                 if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
759                         ret = sscanf(msg_data,
760                                      "DATARECVINPROG %d %d", &len, &total);
761                         if (ret != 2) {
762                                 g_warning("wrong control message: %s\n", msg);
763                                 session->state = SESSION_ERROR;
764                                 g_free(msg);
765                                 return FALSE;
766                         }
767                         session_send_msg(session, SESSION_MSG_CONTROL,
768                                          "CONTINUE");
769                         session->recv_data_progressive_notify
770                                 (session, len, total,
771                                  session->recv_data_progressive_notify_data);
772                 } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
773                         ret = sscanf(msg_data,
774                                      "DATASENDINPROG %d %d", &len, &total);
775                         if (ret != 2) {
776                                 g_warning("wrong control message: %s\n", msg);
777                                 session->state = SESSION_ERROR;
778                                 g_free(msg);
779                                 return FALSE;
780                         }
781                         session_send_msg(session, SESSION_MSG_CONTROL,
782                                          "CONTINUE");
783                         session->send_data_progressive_notify
784                                 (session, len, total,
785                                  session->send_data_progressive_notify_data);
786                 } else if (!strncmp(msg_data, "DATASENT ", 9)) {
787                         len = atoi(msg_data + 9);
788                         ret = session->send_data_finished(session, len);
789                         session->send_data_notify
790                                 (session, len, session->send_data_notify_data);
791                 } else if (!strcmp(msg_data, "DISCONNECTED")) {
792                         session->state = SESSION_DISCONNECTED;
793                         g_free(msg);
794                         return FALSE;
795                 } else {
796                         g_warning("wrong control message: %s\n", msg);
797                         session->state = SESSION_ERROR;
798                         g_free(msg);
799                         return FALSE;
800                 }
801                 break;
802         case SESSION_MSG_ERROR:
803         default:
804                 g_warning("error from child: %s\n", msg + strlen("ERROR "));
805                 session->state = SESSION_ERROR;
806                 g_free(msg);
807                 return FALSE;
808         }
809
810         g_free(msg);
811         return TRUE;
812 }
813
814 /*!
815  *\brief        child: Receive control message from parent,
816  *              transfer data from/to server
817  *
818  *\param        session Contains session information
819  *
820  *\return       TRUE if more data is available
821  */
822 gboolean session_child_input(Session *session)
823 {
824         gchar buf[BUFFSIZE];
825         gchar *msg;
826         gchar *msg_data;
827         gchar *str;
828         guchar *send_data;
829         guchar *recv_data;
830         guint size;
831         guint actual_size;
832
833         if ((msg = session_recv_msg(session)) == NULL) {
834                 session_send_msg(session, SESSION_MSG_ERROR,
835                                  "receiving message failed.");
836                 session->state = SESSION_ERROR;
837                 return FALSE;
838         }
839
840         switch (session_get_msg_type(msg)) {
841         case SESSION_MSG_NORMAL:
842                 msg_data = msg + strlen("MESSAGE ");
843                 session->state = SESSION_SEND;
844                 sock_puts(session->sock, msg_data);
845                 session->state = SESSION_RECV;
846                 str = sock_getline(session->sock);
847                 if (!str) {
848                         session_send_msg(session, SESSION_MSG_ERROR,
849                                          "receiving message failed.");
850                         session->state = SESSION_ERROR;
851                         g_free(msg);
852                         return FALSE;
853                 }
854                 strretchomp(str);
855                 session_send_msg(session, SESSION_MSG_NORMAL, str);
856                 g_free(str);
857                 break;
858         case SESSION_MSG_SEND_DATA:
859                 msg_data = msg + strlen("SENDDATA ");
860                 size = atoi(msg_data);
861                 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
862                 send_data = session_read_data(session, size);
863                 if (!send_data) {
864                         session_send_msg(session, SESSION_MSG_ERROR,
865                                          "sending data failed.");
866                         session->state = SESSION_ERROR;
867                         g_free(msg);
868                         return FALSE;
869                 }
870                 session->state = SESSION_SEND;
871                 if (session_send_data_to_sock(session, send_data, size) < 0) {
872                         session_send_msg(session, SESSION_MSG_ERROR,
873                                          "sending data failed.");
874                         session->state = SESSION_ERROR;
875                         g_free(send_data);
876                         g_free(msg);
877                         return FALSE;
878                 }
879                 g_free(send_data);
880                 g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
881                 session_send_msg(session, SESSION_MSG_CONTROL, buf);
882                 break;
883         case SESSION_MSG_RECV_DATA:
884                 msg_data = msg + strlen("RECVDATA ");
885                 size = atoi(msg_data);
886                 session->state = SESSION_RECV;
887                 if (strstr(msg_data, "UNESCAPE") != NULL) {
888                         recv_data = session_recv_data_from_sock_unescape
889                                 (session, size, &actual_size);
890                         size = actual_size;
891                 } else
892                         recv_data = session_recv_data_from_sock(session, size);
893                 if (!recv_data) {
894                         session_send_msg(session, SESSION_MSG_ERROR,
895                                          "receiving data failed.");
896                         session->state = SESSION_ERROR;
897                         g_free(msg);
898                         return FALSE;
899                 }
900                 if (session_send_data(session, recv_data, size) < 0) {
901                         session->state = SESSION_ERROR;
902                         g_free(recv_data);
903                         g_free(msg);
904                         return FALSE;
905                 }
906                 g_free(recv_data);
907                 break;
908         case SESSION_MSG_CONTROL:
909                 msg_data = msg + strlen("CONTROL ");
910                 if (!strcmp(msg_data, "CONTINUE")) {
911                         session->state = SESSION_RECV;
912                         str = sock_getline(session->sock);
913                         if (!str) {
914                                 session_send_msg(session, SESSION_MSG_ERROR,
915                                                  "receiving message failed.");
916                                 session->state = SESSION_ERROR;
917                                 g_free(msg);
918                                 return FALSE;
919                         }
920                         strretchomp(str);
921                         session_send_msg(session, SESSION_MSG_NORMAL, str);
922                         g_free(str);
923                         break;
924 #if USE_OPENSSL
925                 } else if (!strcmp(msg_data, "STARTTLS")) {
926                         if (!ssl_init_socket_with_method(session->sock,
927                                                          SSL_METHOD_TLSv1)) {
928                                 session_send_msg(session, SESSION_MSG_ERROR,
929                                                  "can't start TLS session.");
930                                 session->state = SESSION_ERROR;
931                                 g_free(msg);
932                                 return FALSE;
933                         }
934                         session_send_msg(session, SESSION_MSG_CONTROL,
935                                          "STARTTLSOK");
936                         break;
937 #endif
938                 } else if (!strcmp(msg_data, "DISCONNECT")) {
939                         sock_close(session->sock);
940                         session->sock = NULL;
941                         session->state = SESSION_DISCONNECTED;
942                         session_send_msg(session, SESSION_MSG_CONTROL,
943                                          "DISCONNECTED");
944                         g_free(msg);
945                         return FALSE;
946                 } else {
947                         session_send_msg(session, SESSION_MSG_ERROR,
948                                          "wrong control message.");
949                         session->state = SESSION_ERROR;
950                         g_free(msg);
951                         return FALSE;
952                 }
953                 break;
954         case SESSION_MSG_ERROR:
955         default:
956                 session_send_msg(session, SESSION_MSG_ERROR,
957                                  "error received from parent.");
958                 session->state = SESSION_ERROR;
959                 g_free(msg);
960                 return FALSE;
961         }
962
963         g_free(msg);
964         return TRUE;
965 }