drop account-hooks for now.
[apps/madmutt.git] / lib-sys / mutt_socket.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1998 Michael R. Elkins <me@mutt.org>
4  * Copyright (C) 1999-2005 Brendan Cully <brendan@kublai.com>
5  * Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi>
6  *
7  * This file is part of mutt-ng, see http://www.muttng.org/.
8  * It's licensed under the GNU General Public License,
9  * please see the file GPL in the top level source directory.
10  */
11
12 #include <lib-lib/lib-lib.h>
13
14 #include <netinet/in.h>
15 #include <netdb.h>
16 #include <sys/socket.h>
17
18 #include <lib-ui/curses.h>
19
20 #include "mutt.h"
21 #include "globals.h"
22
23 #include "unix.h"
24 #include "mutt_socket.h"
25 #include "mutt_tunnel.h"
26 #include "mutt_signal.h"
27
28 #ifdef HAVE_LIBIDN
29 #include <idna.h>
30 #endif
31 #include "mutt_idna.h"
32
33 /* support for multiple socket connections */
34 static CONNECTION *Connections = NULL;
35
36 /* forward declarations */
37 static int socket_preconnect (void);
38 static int socket_connect (int fd, struct sockaddr *sa);
39 static CONNECTION *socket_new_conn (void);
40
41 /* Wrappers */
42 int mutt_socket_open (CONNECTION * conn)
43 {
44   if (socket_preconnect ())
45     return -1;
46
47   return conn->conn_open (conn);
48 }
49
50 int mutt_socket_close (CONNECTION * conn)
51 {
52   int rc = -1;
53
54   if (conn->fd >= 0)
55     rc = conn->conn_close (conn);
56
57   conn->fd = -1;
58   conn->ssf = 0;
59
60   return rc;
61 }
62
63 int mutt_socket_read (CONNECTION * conn, char *buf, ssize_t len)
64 {
65   int rc;
66
67   if (conn->fd < 0) {
68     return -1;
69   }
70
71   rc = conn->conn_read (conn, buf, len);
72   /* EOF */
73   if (rc == 0) {
74     mutt_error (_("Connection to %s closed"), conn->account.host);
75     mutt_sleep (2);
76   }
77   if (rc <= 0)
78     mutt_socket_close (conn);
79
80   return rc;
81 }
82
83 int mutt_socket_write(CONNECTION * conn, const char *buf)
84 {
85   int rc;
86   int len;
87
88   if (conn->fd < 0) {
89     return -1;
90   }
91
92   len = m_strlen(buf);
93   if ((rc = conn->conn_write (conn, buf, len)) < 0) {
94     mutt_socket_close (conn);
95
96     return -1;
97   }
98
99   return rc;
100 }
101
102 /* simple read buffering to speed things up. */
103 int mutt_socket_readchar (CONNECTION * conn, char *c)
104 {
105   if (conn->bufpos >= conn->available) {
106     if (conn->fd >= 0)
107       conn->available =
108         conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
109     else {
110       return -1;
111     }
112     conn->bufpos = 0;
113     if (conn->available == 0) {
114       mutt_error (_("Connection to %s closed"), conn->account.host);
115       mutt_sleep (2);
116     }
117     if (conn->available <= 0) {
118       mutt_socket_close (conn);
119       return -1;
120     }
121   }
122   *c = conn->inbuf[conn->bufpos];
123   conn->bufpos++;
124   return 1;
125 }
126
127 int mutt_socket_readln(char *buf, ssize_t buflen, CONNECTION * conn)
128 {
129   char ch;
130   ssize_t i;
131
132   for (i = 0; i < buflen - 1; i++) {
133     if (mutt_socket_readchar (conn, &ch) != 1) {
134       buf[i] = '\0';
135       return -1;
136     }
137
138     if (ch == '\n')
139       break;
140     buf[i] = ch;
141   }
142
143   /* strip \r from \r\n termination */
144   if (i && buf[i - 1] == '\r')
145     buf[--i] = '\0';
146   else
147     buf[i] = '\0';
148
149   /* number of bytes read, not m_strlen*/
150   return i + 1;
151 }
152
153 int mutt_socket_readln2(buffer_t *buf, CONNECTION *conn)
154 {
155     char ch;
156
157     while (mutt_socket_readchar(conn, &ch) == 1) {
158         if (ch == '\n') {
159             if (buf->len && buf->data[buf->len - 1] == '\r') {
160                 buf->data[--buf->len] = '\0';
161             }
162             return 0;
163         }
164         buffer_addch(buf, ch);
165     }
166
167     return -1;
168 }
169
170 CONNECTION *mutt_socket_head (void)
171 {
172   return Connections;
173 }
174
175 /* mutt_socket_free: remove connection from connection list and free it */
176 void mutt_socket_free (CONNECTION * conn)
177 {
178   CONNECTION *iter;
179   CONNECTION *tmp;
180
181   iter = Connections;
182
183   /* head is special case, doesn't need prev updated */
184   if (iter == conn) {
185     Connections = iter->next;
186     p_delete(&iter);
187     return;
188   }
189
190   while (iter->next) {
191     if (iter->next == conn) {
192       tmp = iter->next;
193       iter->next = tmp->next;
194       p_delete(&tmp);
195       return;
196     }
197     iter = iter->next;
198   }
199 }
200
201 /* mutt_conn_find: find a connection off the list of connections whose
202  *   account matches account. If start is not null, only search for
203  *   connections after the given connection (allows higher level socket code
204  *   to make more fine-grained searches than account info - eg in IMAP we may
205  *   wish to find a connection which is not in IMAP_SELECTED state) */
206 CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account)
207 {
208   CONNECTION *conn;
209
210   conn = start ? start->next : Connections;
211   while (conn) {
212     if (mutt_account_match (account, &(conn->account)))
213       return conn;
214     conn = conn->next;
215   }
216
217   conn = socket_new_conn ();
218   memcpy (&conn->account, account, sizeof (ACCOUNT));
219
220   conn->next = Connections;
221   Connections = conn;
222
223   if (Tunnel && *Tunnel)
224     mutt_tunnel_socket_setup (conn);
225   else if (account->has_ssl) {
226     if (mutt_ssl_socket_setup (conn) < 0) {
227       mutt_socket_free (conn);
228       return NULL;
229     }
230   } else {
231     conn->conn_read = raw_socket_read;
232     conn->conn_write = raw_socket_write;
233     conn->conn_open = raw_socket_open;
234     conn->conn_close = raw_socket_close;
235   }
236
237   return conn;
238 }
239
240 static int socket_preconnect (void)
241 {
242   int rc;
243   int save_errno;
244
245   if (m_strlen(Preconnect)) {
246     rc = mutt_system (Preconnect);
247     if (rc) {
248       save_errno = errno;
249       mutt_perror (_("Preconnect command failed."));
250       mutt_sleep (1);
251
252       return save_errno;
253     }
254   }
255
256   return 0;
257 }
258
259 /* socket_connect: set up to connect to a socket fd. */
260 static int socket_connect (int fd, struct sockaddr *sa)
261 {
262   int sa_size;
263   int save_errno;
264
265   if (sa->sa_family == AF_INET)
266     sa_size = sizeof (struct sockaddr_in);
267   else if (sa->sa_family == AF_INET6)
268     sa_size = sizeof (struct sockaddr_in6);
269   else {
270     return -1;
271   }
272
273   if (ConnectTimeout > 0)
274     alarm (ConnectTimeout);
275
276   mutt_allow_interrupt (1);
277
278   save_errno = 0;
279
280   if (connect (fd, sa, sa_size) < 0) {
281     save_errno = errno;
282     SigInt = 0;                 /* reset in case we caught SIGINTR while in connect() */
283   }
284
285   if (ConnectTimeout > 0)
286     alarm (0);
287   mutt_allow_interrupt (0);
288
289   return save_errno;
290 }
291
292 /* socket_new_conn: allocate and initialise a new connection. */
293 static CONNECTION *socket_new_conn (void)
294 {
295   CONNECTION *conn;
296
297   conn = p_new(CONNECTION, 1);
298   conn->fd = -1;
299
300   return conn;
301 }
302
303 int raw_socket_close (CONNECTION * conn)
304 {
305   return close (conn->fd);
306 }
307
308 int raw_socket_read (CONNECTION * conn, char *buf, ssize_t len)
309 {
310   int rc;
311
312   if ((rc = read (conn->fd, buf, len)) == -1) {
313     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
314                 strerror (errno));
315     mutt_sleep (2);
316   }
317
318   return rc;
319 }
320
321 int raw_socket_write (CONNECTION * conn, const char *buf, ssize_t count)
322 {
323   int rc;
324
325   if ((rc = write (conn->fd, buf, count)) == -1) {
326     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
327                 strerror (errno));
328     mutt_sleep (2);
329   }
330
331   return rc;
332 }
333
334 int raw_socket_open (CONNECTION * conn)
335 {
336   int rc;
337   int fd;
338
339   char *host_idna = NULL;
340
341   /* "65536\0" */
342   char port[6];
343   struct addrinfo hints;
344   struct addrinfo *res;
345   struct addrinfo *cur;
346
347   /* we accept v4 or v6 STREAM sockets */
348   p_clear(&hints, 1);
349
350   if (option (OPTUSEIPV6))
351     hints.ai_family = AF_UNSPEC;
352   else
353     hints.ai_family = AF_INET;
354
355   hints.ai_socktype = SOCK_STREAM;
356
357   snprintf (port, sizeof (port), "%d", conn->account.port);
358
359 # ifdef HAVE_LIBIDN
360   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
361     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
362     return -1;
363   }
364 # else
365   host_idna = conn->account.host;
366 # endif
367
368   mutt_message (_("Looking up %s..."), conn->account.host);
369
370   rc = getaddrinfo (host_idna, port, &hints, &res);
371
372 # ifdef HAVE_LIBIDN
373   p_delete(&host_idna);
374 # endif
375
376   if (rc) {
377     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
378     mutt_sleep (2);
379     return -1;
380   }
381
382   mutt_message (_("Connecting to %s..."), conn->account.host);
383
384   rc = -1;
385   for (cur = res; cur != NULL; cur = cur->ai_next) {
386     fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
387     if (fd >= 0) {
388       if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
389         fcntl (fd, F_SETFD, FD_CLOEXEC);
390         conn->fd = fd;
391         break;
392       }
393       else
394         close (fd);
395     }
396   }
397
398   freeaddrinfo (res);
399
400   if (rc) {
401     mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
402                 (rc > 0) ? strerror (rc) : _("unknown error"));
403     mutt_sleep (2);
404     return -1;
405   }
406
407   return 0;
408 }