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