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