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