Rocco Rutte:
[apps/madmutt.git] / mutt_tunnel.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 2000 Manoj Kasichainula <manoj@io.com>
4  * Copyright (C) 2001,2005 Brendan Cully <brendan@kublai.com>
5  *
6  * This file is part of mutt-ng, see http://www.muttng.org/.
7  * It's licensed under the GNU General Public License,
8  * please see the file GPL in the top level source directory.
9  */
10
11 #if HAVE_CONFIG_H
12 # include "config.h"
13 #endif
14
15 #include "mutt.h"
16 #include "mutt_socket.h"
17 #include "mutt_tunnel.h"
18
19 #include "lib/mem.h"
20 #include "lib/intl.h"
21 #include "lib/str.h"
22
23 #include <netinet/in.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/wait.h>
27 #include <fcntl.h>
28 #include <errno.h>
29
30 /* -- data types -- */
31 typedef struct {
32   pid_t pid;
33   int readfd;
34   int writefd;
35 } TUNNEL_DATA;
36
37 /* forward declarations */
38 static int tunnel_socket_open (CONNECTION *);
39 static int tunnel_socket_close (CONNECTION *);
40 static int tunnel_socket_read (CONNECTION * conn, char *buf, size_t len);
41 static int tunnel_socket_write (CONNECTION * conn, const char *buf,
42                                 size_t len);
43
44 /* -- public functions -- */
45 int mutt_tunnel_socket_setup (CONNECTION * conn)
46 {
47   conn->conn_open = tunnel_socket_open;
48   conn->conn_close = tunnel_socket_close;
49   conn->conn_read = tunnel_socket_read;
50   conn->conn_write = tunnel_socket_write;
51
52   return 0;
53 }
54
55 static int tunnel_socket_open (CONNECTION * conn)
56 {
57   TUNNEL_DATA *tunnel;
58   int pid;
59   int rc;
60   int pin[2], pout[2];
61
62   tunnel = (TUNNEL_DATA *) mem_malloc (sizeof (TUNNEL_DATA));
63   conn->sockdata = tunnel;
64
65   mutt_message (_("Connecting with \"%s\"..."), Tunnel);
66
67   if ((rc = pipe (pin)) == -1) {
68     mutt_perror ("pipe");
69     return -1;
70   }
71   if ((rc = pipe (pout)) == -1) {
72     mutt_perror ("pipe");
73     return -1;
74   }
75
76   mutt_block_signals_system ();
77   if ((pid = fork ()) == 0) {
78     mutt_unblock_signals_system (0);
79     if (dup2 (pout[0], STDIN_FILENO) < 0 || dup2 (pin[1], STDOUT_FILENO) < 0)
80       _exit (127);
81     close (pin[0]);
82     close (pin[1]);
83     close (pout[0]);
84     close (pout[1]);
85     close (STDERR_FILENO);
86
87     /* Don't let the subprocess think it can use the controlling tty */
88     setsid ();
89
90     execl (EXECSHELL, "sh", "-c", Tunnel, NULL);
91     _exit (127);
92   }
93   mutt_unblock_signals_system (1);
94
95   if (pid == -1) {
96     close (pin[0]);
97     close (pin[1]);
98     close (pout[0]);
99     close (pout[1]);
100     mutt_perror ("fork");
101     return -1;
102   }
103   if (close (pin[1]) < 0 || close (pout[0]) < 0)
104     mutt_perror ("close");
105
106   fcntl (pin[0], F_SETFD, FD_CLOEXEC);
107   fcntl (pout[1], F_SETFD, FD_CLOEXEC);
108
109   tunnel->readfd = pin[0];
110   tunnel->writefd = pout[1];
111   tunnel->pid = pid;
112
113   conn->fd = 42;                /* stupid hack */
114
115   return 0;
116 }
117
118 static int tunnel_socket_close (CONNECTION * conn)
119 {
120   TUNNEL_DATA *tunnel = (TUNNEL_DATA *) conn->sockdata;
121   int status;
122
123   close (tunnel->readfd);
124   close (tunnel->writefd);
125   waitpid (tunnel->pid, &status, 0);
126   if (!WIFEXITED(status) || WEXITSTATUS(status)) {
127     mutt_error(_("Tunnel to %s returned error %d (%s)"),
128                conn->account.host, WEXITSTATUS(status),
129                NONULL(mutt_strsysexit(WEXITSTATUS(status))));
130     mutt_sleep (2);
131   }
132   mem_free (&conn->sockdata);
133   return 0;
134 }
135
136 static int tunnel_socket_read (CONNECTION * conn, char *buf, size_t len)
137 {
138   TUNNEL_DATA *tunnel = (TUNNEL_DATA *) conn->sockdata;
139   int rc;
140
141   rc = read (tunnel->readfd, buf, len);
142   if (rc == -1) {
143     mutt_error (_("Tunnel error talking to %s: %s"), conn->account.host,
144                 strerror (errno));
145     mutt_sleep (1);
146   }
147
148   return rc;
149 }
150
151 static int tunnel_socket_write (CONNECTION * conn, const char *buf,
152                                 size_t len)
153 {
154   TUNNEL_DATA *tunnel = (TUNNEL_DATA *) conn->sockdata;
155   int rc;
156
157   rc = write (tunnel->writefd, buf, len);
158   if (rc == -1) {
159     mutt_error (_("Tunnel error talking to %s: %s"), conn->account.host,
160                 strerror (errno));
161     mutt_sleep (1);
162   }
163
164   return rc;
165 }