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