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