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