fefb9f16ed407b74c1cf119ab5a4ec236670a39d
[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
36 #include "session.h"
37 #include "utils.h"
38
39 static gint session_close               (Session        *session);
40
41 static gchar *session_recv_msg          (Session        *session);
42
43 static guchar *session_read_data        (Session        *session,
44                                          guint           size);
45
46 static gint session_send_data_to_sock           (Session        *session,
47                                                  const guchar   *data,
48                                                  guint           size);
49 static guchar *session_recv_data_from_sock      (Session        *session,
50                                                  guint           size);
51
52 gboolean session_parent_input_cb        (GIOChannel     *source,
53                                          GIOCondition    condition,
54                                          gpointer        data);
55
56 gboolean session_child_input            (Session        *session);
57
58
59 void session_init(Session *session)
60 {
61         session->type = 0;
62         session->sock = NULL;
63
64         session->server = NULL;
65         session->port = 0;
66         session->state = SESSION_READY;
67         session->last_access_time = time(NULL);
68         session->data = NULL;
69
70         session->read_ch = NULL;
71         session->write_ch = NULL;
72 }
73
74 gint session_connect(Session *session, const gchar *server, gushort port)
75 {
76         pid_t pid;
77         gint pipe_fds1[2], pipe_fds2[2];
78         SockInfo *sock;
79         gchar *str;
80
81         session->server = g_strdup(server);
82         session->port = port;
83
84         if (pipe(pipe_fds1) < 0) {
85                 perror("pipe");
86                 return -1;
87         }
88         if (pipe(pipe_fds2) < 0) {
89                 perror("pipe");
90                 close(pipe_fds1[0]);
91                 close(pipe_fds1[1]);
92                 return -1;
93         }
94
95         if ((pid = fork()) < 0) {
96                 perror("fork");
97                 return -1;
98         }
99
100         if (pid != 0) {
101                 session->child_pid = pid;
102                 session->read_ch = g_io_channel_unix_new(pipe_fds2[0]);
103                 session->write_ch = g_io_channel_unix_new(pipe_fds1[1]);
104                 close(pipe_fds1[0]);
105                 close(pipe_fds2[1]);
106                 session->read_tag = g_io_add_watch(session->read_ch, G_IO_IN,
107                                                    session_parent_input_cb,
108                                                    session);
109                 return 0;
110         }
111
112         /* child process */
113
114         session->read_ch = g_io_channel_unix_new(pipe_fds1[0]);
115         session->write_ch = g_io_channel_unix_new(pipe_fds2[1]);
116         close(pipe_fds1[1]);
117         close(pipe_fds2[0]);
118
119         g_print("child: connecting to %s:%d ...\n", server, port);
120
121         if ((sock = sock_connect(server, port)) == NULL) {
122                 session_send_msg(session, SESSION_MSG_ERROR,
123                                  "can't connect to server.");
124                 session_close(session);
125                 _exit(1);
126         }
127
128 #if USE_OPENSSL
129         if (session->ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
130                 session_send_msg(session, SESSION_MSG_ERROR,
131                                  "can't initialize SSL.");
132                 session_close(session);
133                 _exit(1);
134         }
135 #endif
136
137         g_print("child: connected\n");
138
139         session->sock = sock;
140         session->state = SESSION_RECV;
141
142         if ((str = sock_getline(sock)) == NULL) {
143                 session_send_msg(session, SESSION_MSG_ERROR,
144                                  "can't get server response.");
145                 session_close(session);
146                 _exit(1);
147         }
148         strretchomp(str);
149         session_send_msg(session, SESSION_MSG_NORMAL, str);
150         g_free(str);
151
152         while (session_child_input(session) == TRUE)
153                 ;
154
155         session_close(session);
156
157         g_print("child: disconnected\n");
158
159         _exit(0);
160 }
161
162 gint session_disconnect(Session *session)
163 {
164         g_print("%s: session_disconnect()\n", session->child_pid == 0 ? "child" : "parent");
165         session_send_msg(session, SESSION_MSG_CONTROL, "DISCONNECT");
166         return 0;
167 }
168
169 void session_destroy(Session *session)
170 {
171         g_return_if_fail(session != NULL);
172         g_return_if_fail(session->destroy != NULL);
173
174         g_print("session_destroy()\n");
175         session_close(session);
176         session->destroy(session);
177         g_free(session->server);
178         g_free(session);
179 }
180
181 void session_set_recv_message_notify(Session *session,
182                                      RecvMsgNotify notify_func, gpointer data)
183 {
184         session->recv_msg_notify = notify_func;
185         session->recv_msg_notify_data = data;
186 }
187
188 void session_set_recv_data_progressive_notify
189                                         (Session *session,
190                                          RecvDataProgressiveNotify notify_func,
191                                          gpointer data)
192 {
193         session->recv_data_progressive_notify = notify_func,
194         session->recv_data_progressive_notify_data = data;
195 }
196
197 void session_set_recv_data_notify(Session *session, RecvDataNotify notify_func,
198                                   gpointer data)
199 {
200         session->recv_data_notify = notify_func;
201         session->recv_data_notify_data = data;
202 }
203
204 void session_set_send_data_progressive_notify
205                                         (Session *session,
206                                          SendDataProgressiveNotify notify_func,
207                                          gpointer data)
208 {
209         session->send_data_progressive_notify = notify_func;
210         session->send_data_progressive_notify_data = data;
211 }
212
213 void session_set_send_data_notify(Session *session, SendDataNotify notify_func,
214                                   gpointer data)
215 {
216         session->send_data_notify = notify_func;
217         session->send_data_notify_data = data;
218 }
219
220 static gint session_close(Session *session)
221 {
222         g_return_val_if_fail(session != NULL, -1);
223
224         g_print("%s: session_close()\n", session->child_pid == 0 ? "child" : "parent");
225
226         if (session->read_tag > 0) {
227                 g_source_remove(session->read_tag);
228                 session->read_tag = 0;
229         }
230
231         if (session->read_ch) {
232                 g_io_channel_close(session->read_ch);
233                 g_io_channel_unref(session->read_ch);
234                 session->read_ch = NULL;
235         }
236         if (session->write_ch) {
237                 g_io_channel_close(session->write_ch);
238                 g_io_channel_unref(session->write_ch);
239                 session->write_ch = NULL;
240         }
241
242         if (session->sock) {
243                 sock_close(session->sock);
244                 session->sock = NULL;
245                 session->state = SESSION_DISCONNECTED;
246         }
247
248         if (session->child_pid) {
249                 kill(session->child_pid, SIGTERM);
250                 waitpid(session->child_pid, NULL, 0);
251                 session->child_pid = 0;
252         }
253
254         return 0;
255 }
256
257 gint session_send_msg(Session *session, SessionMsgType type, const gchar *msg)
258 {
259         gchar *prefix;
260         gchar *str;
261         guint size;
262         guint bytes_written;
263
264         switch (type) {
265         case SESSION_MSG_NORMAL:
266                 prefix = "MESSAGE"; break;
267         case SESSION_MSG_SEND_DATA:
268                 prefix = "SENDDATA"; break;
269         case SESSION_MSG_CONTROL:
270                 prefix = "CONTROL"; break;
271         case SESSION_MSG_ERROR:
272                 prefix = "ERROR"; break;
273         default:
274                 return -1;
275         }
276
277         str = g_strdup_printf("%s %s\n", prefix, msg);
278         g_print("%s: sending message: %s", session->child_pid == 0 ? "child" : "parent", str);
279         size = strlen(str);
280
281         while (size > 0) {
282                 if (g_io_channel_write(session->write_ch, str, size,
283                                        &bytes_written)
284                     != G_IO_ERROR_NONE || bytes_written == 0) {
285                         g_warning("%s: sending message failed.\n",
286                                   session->child_pid == 0 ? "child" : "parent");
287                         return -1;
288                 }
289                 size -= bytes_written;
290         }
291
292         return 0;
293 }
294
295 static gchar *session_recv_msg(Session *session)
296 {
297         gchar buf[BUFFSIZE];
298         gchar *str = NULL;
299         guint size = 1;
300         guint bytes_read;
301
302         for (;;) {
303                 if (g_io_channel_read(session->read_ch, buf, sizeof(buf) - 1,
304                                       &bytes_read)
305                     != G_IO_ERROR_NONE || bytes_read == 0) {
306                         g_warning("%s: receiving message failed.\n",
307                                   session->child_pid == 0 ? "child" : "parent");
308                         g_free(str);
309                         str = NULL;
310                         break;
311                 }
312
313                 size += bytes_read;
314                 buf[bytes_read] = '\0';
315
316                 if (!str)
317                         str = g_strdup(buf);
318                 else {
319                         str = g_realloc(str, size);
320                         strcat(str, buf);
321                 }
322                 if (str[size - 2] == '\n') {
323                         str[size - 2] = '\0';
324
325                         g_print("%s: received message: %s\n", session->child_pid == 0 ? "child" : "parent", str);
326
327                         break;
328                 }
329         }
330
331         return str;
332 }
333
334 #if USE_OPENSSL
335 gint session_start_tls(Session *session)
336 {
337         gchar *ctl_msg;
338
339         session_send_msg(session, SESSION_MSG_CONTROL, "STARTTLS");
340         ctl_msg = session_recv_msg(session);
341         if (!ctl_msg || strcmp(ctl_msg, "CONTROL STARTTLSOK") != 0) {
342                 g_free(ctl_msg);
343                 return -1;
344         }
345         g_free(ctl_msg);
346
347         return 0;
348 }
349 #endif
350
351 gint session_send_data(Session *session, const guchar *data, guint size)
352 {
353         gchar *msg;
354         guint bytes_written;
355         GIOError err;
356
357         session_send_msg(session, SESSION_MSG_SEND_DATA, itos(size));
358         if ((msg = session_recv_msg(session)) == NULL)
359                 return -1;
360         g_free(msg);
361
362         while (size > 0) {
363                 if ((err = g_io_channel_write(session->write_ch, (guchar *)data,
364                                               size, &bytes_written))
365                     != G_IO_ERROR_NONE || bytes_written == 0) {
366                         g_warning("%s: sending data failed: %d\n",
367                                   session->child_pid == 0 ? "child" : "parent",
368                                   err);
369                         return -1;
370                 }
371                 size -= bytes_written;
372                 g_print("%s: sent %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_written);
373         }
374
375         return 0;
376 }
377
378 static guchar *session_read_data(Session *session, guint size)
379 {
380         guchar *data;
381         guchar *cur;
382         guint bytes_read;
383         GIOError err;
384
385         cur = data = g_malloc(size);
386
387         while (size > 0) {
388                 if ((err = g_io_channel_read(session->read_ch, cur, size,
389                                              &bytes_read))
390                     != G_IO_ERROR_NONE || bytes_read == 0) {
391                         g_warning("%s: reading data failed: %d\n",
392                                   session->child_pid == 0 ? "child" : "parent",
393                                   err);
394                         g_free(data);
395                         return NULL;
396                 }
397                 size -= bytes_read;
398                 cur += bytes_read;
399                 g_print("%s: received %d bytes of data\n", session->child_pid == 0 ? "child" : "parent", bytes_read);
400         }
401
402         return data;
403 }
404
405 #define MAX_CHUNK_SIZE 4096
406
407 static gint session_send_data_to_sock(Session *session, const guchar *data,
408                                       guint size)
409 {
410         const guchar *cur = data;
411         gint bytes_written;
412         gint total_write_len = 0;
413         guint left = size;
414         gchar buf[BUFFSIZE];
415         gchar *msg;
416
417         while (left > 0) {
418                 bytes_written = sock_write(session->sock, cur,
419                                            MIN(left, MAX_CHUNK_SIZE));
420                 if (bytes_written <= 0)
421                         return -1;
422                 left -= bytes_written;
423                 cur += bytes_written;
424                 total_write_len += bytes_written;
425                 if (left > 0) {
426                         g_snprintf(buf, sizeof(buf), "DATASENDINPROG %d %d",
427                                    total_write_len, size);
428                         session_send_msg(session, SESSION_MSG_CONTROL, buf);
429                         if ((msg = session_recv_msg(session)) == NULL)
430                                 return -1;
431                         g_free(msg);
432                 }
433         }
434
435         return 0;
436 }
437
438 static guchar *session_recv_data_from_sock(Session *session, guint size)
439 {
440         guchar *data;
441         guchar *cur;
442         gint bytes_read;
443         gint total_read_len = 0;
444         guint left = size;
445         gchar buf[BUFFSIZE];
446         gchar *msg;
447
448         cur = data = g_malloc(size);
449
450         while (left > 0) {
451                 bytes_read = sock_read(session->sock, cur, left);
452                 if (bytes_read <= 0) {
453                         g_free(data);
454                         return NULL;
455                 }
456                 g_print("child: received %d bytes of data from sock\n", bytes_read);
457                 left -= bytes_read;
458                 cur += bytes_read;
459                 total_read_len += bytes_read;
460                 g_snprintf(buf, sizeof(buf), "DATARECVINPROG %d %d",
461                            total_read_len, size);
462                 session_send_msg(session, SESSION_MSG_CONTROL, buf);
463                 if ((msg = session_recv_msg(session)) == NULL) {
464                         g_free(data);
465                         return NULL;
466                 }
467                 g_free(msg);
468         }
469
470         return data;
471 }
472
473 static SessionMsgType session_get_msg_type(const gchar *str)
474 {
475         if (!strncmp(str, "MESSAGE ", 8))
476                 return SESSION_MSG_NORMAL;
477         else if (!strncmp(str, "SENDDATA ", 9))
478                 return SESSION_MSG_SEND_DATA;
479         else if (!strncmp(str, "RECVDATA ", 9))
480                 return SESSION_MSG_RECV_DATA;
481         else if (!strncmp(str, "CONTROL ", 8))
482                 return SESSION_MSG_CONTROL;
483         else if (!strncmp(str, "ERROR ", 6))
484                 return SESSION_MSG_ERROR;
485         else
486                 return SESSION_MSG_UNKNOWN;
487 }
488
489 gboolean session_parent_input_cb(GIOChannel *source, GIOCondition condition,
490                                  gpointer data)
491 {
492         Session *session = SESSION(data);
493         gchar *msg;
494         gchar *msg_data;
495         gint len;
496         gint total;
497         guchar *recv_data;
498         guint size;
499         gint ret;
500
501         if ((msg = session_recv_msg(session)) == NULL) {
502                 session->state = SESSION_ERROR;
503                 return FALSE;
504         }
505
506         switch (session_get_msg_type(msg)) {
507         case SESSION_MSG_NORMAL:
508                 msg_data = msg + strlen("MESSAGE ");
509                 ret = session->recv_msg(session, msg_data);
510                 if (ret <= 0)
511                         session->recv_msg_notify(session, msg_data,
512                                                  session->recv_msg_notify_data);
513                 else
514                         session_send_msg(session, SESSION_MSG_CONTROL,
515                                          "CONTINUE");
516                 if (ret < 0) {
517                         session->state = SESSION_ERROR;
518                         g_free(msg);
519                         return FALSE;
520                 }
521                 break;
522         case SESSION_MSG_SEND_DATA:
523                 msg_data = msg + strlen("SENDDATA ");
524                 size = atoi(msg_data);
525                 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
526                 recv_data = session_read_data(session, size);
527                 if (!recv_data) {
528                         session->state = SESSION_ERROR;
529                         g_free(msg);
530                         return FALSE;
531                 }
532                 session->recv_data_finished(session, recv_data, size);
533                 g_free(recv_data);
534                 break;
535         case SESSION_MSG_RECV_DATA:
536                 break;
537         case SESSION_MSG_CONTROL:
538                 msg_data = msg + strlen("CONTROL ");
539                 if (!strncmp(msg_data, "DATARECVINPROG ", 15)) {
540                         ret = sscanf(msg_data,
541                                      "DATARECVINPROG %d %d", &len, &total);
542                         if (ret != 2) {
543                                 g_warning("wrong control message: %s\n", msg);
544                                 session->state = SESSION_ERROR;
545                                 g_free(msg);
546                                 return FALSE;
547                         }
548                         session_send_msg(session, SESSION_MSG_CONTROL,
549                                          "CONTINUE");
550                         session->recv_data_progressive_notify
551                                 (session, len, total,
552                                  session->recv_data_progressive_notify_data);
553                 } else if (!strncmp(msg_data, "DATASENDINPROG ", 15)) {
554                         ret = sscanf(msg_data,
555                                      "DATASENDINPROG %d %d", &len, &total);
556                         if (ret != 2) {
557                                 g_warning("wrong control message: %s\n", msg);
558                                 session->state = SESSION_ERROR;
559                                 g_free(msg);
560                                 return FALSE;
561                         }
562                         session_send_msg(session, SESSION_MSG_CONTROL,
563                                          "CONTINUE");
564                         session->send_data_progressive_notify
565                                 (session, len, total,
566                                  session->send_data_progressive_notify_data);
567                 } else if (!strncmp(msg_data, "DATASENT ", 9)) {
568                         len = atoi(msg_data + 9);
569                         ret = session->send_data_finished(session, len);
570                         session->send_data_notify
571                                 (session, len, session->send_data_notify_data);
572                 } else if (!strcmp(msg_data, "DISCONNECTED")) {
573                         session->state = SESSION_DISCONNECTED;
574                         g_free(msg);
575                         return FALSE;
576                 } else {
577                         g_warning("wrong control message: %s\n", msg);
578                         session->state = SESSION_ERROR;
579                         g_free(msg);
580                         return FALSE;
581                 }
582                 break;
583         case SESSION_MSG_ERROR:
584         default:
585                 g_warning("error from child: %s\n", msg + strlen("ERROR "));
586                 session->state = SESSION_ERROR;
587                 g_free(msg);
588                 return FALSE;
589         }
590
591         g_free(msg);
592         return TRUE;
593 }
594
595 gboolean session_child_input(Session *session)
596 {
597         gchar buf[BUFFSIZE];
598         gchar *msg;
599         gchar *msg_data;
600         gchar *str;
601         guchar *send_data;
602         guchar *recv_data;
603         guint size;
604
605         if ((msg = session_recv_msg(session)) == NULL) {
606                 session_send_msg(session, SESSION_MSG_ERROR,
607                                  "receiving message failed.");
608                 session_close(session);
609                 session->state = SESSION_ERROR;
610                 return FALSE;
611         }
612
613         switch (session_get_msg_type(msg)) {
614         case SESSION_MSG_NORMAL:
615                 msg_data = msg + strlen("MESSAGE ");
616                 session->state = SESSION_SEND;
617                 sock_puts(session->sock, msg_data);
618                 session->state = SESSION_RECV;
619                 str = sock_getline(session->sock);
620                 if (!str) {
621                         session_send_msg(session, SESSION_MSG_ERROR,
622                                          "receiving message failed.");
623                         session_close(session);
624                         session->state = SESSION_ERROR;
625                         g_free(msg);
626                         return FALSE;
627                 }
628                 strretchomp(str);
629                 session_send_msg(session, SESSION_MSG_NORMAL, str);
630                 g_free(str);
631                 break;
632         case SESSION_MSG_SEND_DATA:
633                 msg_data = msg + strlen("SENDDATA ");
634                 size = atoi(msg_data);
635                 session_send_msg(session, SESSION_MSG_CONTROL, "ACCEPTDATA");
636                 send_data = session_read_data(session, size);
637                 if (!send_data) {
638                         session_send_msg(session, SESSION_MSG_ERROR,
639                                          "sending data failed.");
640                         session_close(session);
641                         session->state = SESSION_ERROR;
642                         g_free(msg);
643                         return FALSE;
644                 }
645                 session->state = SESSION_SEND;
646                 if (session_send_data_to_sock(session, send_data, size) < 0) {
647                         session_send_msg(session, SESSION_MSG_ERROR,
648                                          "sending data failed.");
649                         session_close(session);
650                         session->state = SESSION_ERROR;
651                         g_free(send_data);
652                         g_free(msg);
653                         return FALSE;
654                 }
655                 g_free(send_data);
656                 g_snprintf(buf, sizeof(buf), "DATASENT %d", size);
657                 session_send_msg(session, SESSION_MSG_CONTROL, buf);
658                 break;
659         case SESSION_MSG_RECV_DATA:
660                 msg_data = msg + strlen("RECVDATA ");
661                 size = atoi(msg_data);
662                 session->state = SESSION_RECV;
663                 recv_data = session_recv_data_from_sock(session, size);
664                 if (!recv_data) {
665                         session_send_msg(session, SESSION_MSG_ERROR,
666                                          "receiving data failed.");
667                         session_close(session);
668                         session->state = SESSION_ERROR;
669                         g_free(msg);
670                         return FALSE;
671                 }
672                 if (session_send_data(session, recv_data, size) < 0) {
673                         session_close(session);
674                         session->state = SESSION_ERROR;
675                         g_free(recv_data);
676                         g_free(msg);
677                         return FALSE;
678                 }
679                 g_free(recv_data);
680                 break;
681         case SESSION_MSG_CONTROL:
682                 msg_data = msg + strlen("CONTROL ");
683                 if (!strcmp(msg_data, "CONTINUE")) {
684                         session->state = SESSION_RECV;
685                         str = sock_getline(session->sock);
686                         if (!str) {
687                                 session_send_msg(session, SESSION_MSG_ERROR,
688                                                  "receiving message failed.");
689                                 session_close(session);
690                                 session->state = SESSION_ERROR;
691                                 g_free(msg);
692                                 return FALSE;
693                         }
694                         strretchomp(str);
695                         session_send_msg(session, SESSION_MSG_NORMAL, str);
696                         g_free(str);
697                         break;
698 #if USE_OPENSSL
699                 } else if (!strcmp(msg_data, "STARTTLS")) {
700                         if (!ssl_init_socket_with_method(session->sock,
701                                                          SSL_METHOD_TLSv1)) {
702                                 session_send_msg(session, SESSION_MSG_ERROR,
703                                                  "can't start TLS session.");
704                                 session_close(session);
705                                 session->state = SESSION_ERROR;
706                                 g_free(msg);
707                                 return FALSE;
708                         }
709                         session_send_msg(session, SESSION_MSG_CONTROL,
710                                          "STARTTLSOK");
711                         break;
712 #endif
713                 } else if (!strcmp(msg_data, "DISCONNECT")) {
714                         sock_close(session->sock);
715                         session->sock = NULL;
716                         session->state = SESSION_DISCONNECTED;
717                         session_send_msg(session, SESSION_MSG_CONTROL,
718                                          "DISCONNECTED");
719                         g_free(msg);
720                         return FALSE;
721                 } else {
722                         session_send_msg(session, SESSION_MSG_ERROR,
723                                          "wrong control message.");
724                         session_close(session);
725                         session->state = SESSION_ERROR;
726                         g_free(msg);
727                         return FALSE;
728                 }
729                 break;
730         case SESSION_MSG_ERROR:
731         default:
732                 session_send_msg(session, SESSION_MSG_ERROR,
733                                  "error received from parent.");
734                 session_close(session);
735                 session->state = SESSION_ERROR;
736                 g_free(msg);
737                 return FALSE;
738         }
739
740         g_free(msg);
741         return TRUE;
742 }