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