Nico Golde:
[apps/madmutt.git] / 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 #if HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #include "mutt.h"
17 #include "globals.h"
18 #include "mutt_socket.h"
19 #include "mutt_tunnel.h"
20 #if defined(USE_SSL) || defined(USE_GNUTLS) || defined(USE_NSS)
21 # include "mutt_ssl.h"
22 #endif
23
24 #include "mutt_idna.h"
25
26 #include "lib/mem.h"
27 #include "lib/intl.h"
28 #include "lib/str.h"
29
30 #include <unistd.h>
31 #include <netinet/in.h>
32 #include <netdb.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <string.h>
38 #include <errno.h>
39
40 /* support for multiple socket connections */
41 static CONNECTION *Connections = NULL;
42
43 /* forward declarations */
44 static int socket_preconnect (void);
45 static int socket_connect (int fd, struct sockaddr *sa);
46 static CONNECTION *socket_new_conn ();
47
48 /* Wrappers */
49 int mutt_socket_open (CONNECTION * conn)
50 {
51   if (socket_preconnect ())
52     return -1;
53
54   return conn->conn_open (conn);
55 }
56
57 int mutt_socket_close (CONNECTION * conn)
58 {
59   int rc = -1;
60
61   if (conn->fd < 0)
62     dprint (1,
63             (debugfile,
64              "mutt_socket_close: Attempt to close closed connection.\n"));
65   else
66     rc = conn->conn_close (conn);
67
68   conn->fd = -1;
69   conn->ssf = 0;
70
71   return rc;
72 }
73
74 int mutt_socket_read (CONNECTION * conn, char *buf, size_t len)
75 {
76   int rc;
77
78   if (conn->fd < 0) {
79     dprint (1,
80             (debugfile,
81              "mutt_socket_read: attempt to read from closed connection\n"));
82     return -1;
83   }
84
85   rc = conn->conn_read (conn, buf, len);
86   /* EOF */
87   if (rc == 0) {
88     mutt_error (_("Connection to %s closed"), conn->account.host);
89     mutt_sleep (2);
90   }
91   if (rc <= 0)
92     mutt_socket_close (conn);
93
94   return rc;
95 }
96
97 int mutt_socket_write_d (CONNECTION * conn, const char *buf, int dbg)
98 {
99   int rc;
100   int len;
101
102   dprint (dbg, (debugfile, "> %s", buf));
103
104   if (conn->fd < 0) {
105     dprint (1,
106             (debugfile,
107              "mutt_socket_write: attempt to write to closed connection\n"));
108     return -1;
109   }
110
111   len = safe_strlen (buf);
112   if ((rc = conn->conn_write (conn, buf, len)) < 0) {
113     dprint (1, (debugfile,
114                 "mutt_socket_write: error writing, closing socket\n"));
115     mutt_socket_close (conn);
116
117     return -1;
118   }
119
120   if (rc < len) {
121     dprint (1, (debugfile,
122                 "mutt_socket_write: ERROR: wrote %d of %d bytes!\n", rc,
123                 len));
124   }
125
126   return rc;
127 }
128
129 /* simple read buffering to speed things up. */
130 int mutt_socket_readchar (CONNECTION * conn, char *c)
131 {
132   if (conn->bufpos >= conn->available) {
133     if (conn->fd >= 0)
134       conn->available =
135         conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
136     else {
137       dprint (1,
138               (debugfile,
139                "mutt_socket_readchar: attempt to read from closed connection.\n"));
140       return -1;
141     }
142     conn->bufpos = 0;
143     if (conn->available == 0) {
144       mutt_error (_("Connection to %s closed"), conn->account.host);
145       mutt_sleep (2);
146     }
147     if (conn->available <= 0) {
148       mutt_socket_close (conn);
149       return -1;
150     }
151   }
152   *c = conn->inbuf[conn->bufpos];
153   conn->bufpos++;
154   return 1;
155 }
156
157 int mutt_socket_readln_d (char *buf, size_t buflen, CONNECTION * conn,
158                           int dbg)
159 {
160   char ch;
161   int i;
162
163   for (i = 0; i < buflen - 1; i++) {
164     if (mutt_socket_readchar (conn, &ch) != 1) {
165       buf[i] = '\0';
166       return -1;
167     }
168
169     if (ch == '\n')
170       break;
171     buf[i] = ch;
172   }
173
174   /* strip \r from \r\n termination */
175   if (i && buf[i - 1] == '\r')
176     buf[--i] = '\0';
177   else
178     buf[i] = '\0';
179
180   dprint (dbg, (debugfile, "< %s\n", buf));
181
182   /* number of bytes read, not safe_strlen */
183   return i + 1;
184 }
185
186 CONNECTION *mutt_socket_head (void)
187 {
188   return Connections;
189 }
190
191 /* mutt_socket_free: remove connection from connection list and free it */
192 void mutt_socket_free (CONNECTION * conn)
193 {
194   CONNECTION *iter;
195   CONNECTION *tmp;
196
197   iter = Connections;
198
199   /* head is special case, doesn't need prev updated */
200   if (iter == conn) {
201     Connections = iter->next;
202     FREE (&iter);
203     return;
204   }
205
206   while (iter->next) {
207     if (iter->next == conn) {
208       tmp = iter->next;
209       iter->next = tmp->next;
210       FREE (&tmp);
211       return;
212     }
213     iter = iter->next;
214   }
215 }
216
217 /* mutt_conn_find: find a connection off the list of connections whose
218  *   account matches account. If start is not null, only search for
219  *   connections after the given connection (allows higher level socket code
220  *   to make more fine-grained searches than account info - eg in IMAP we may
221  *   wish to find a connection which is not in IMAP_SELECTED state) */
222 CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account)
223 {
224   CONNECTION *conn;
225   ciss_url_t url;
226   char hook[LONG_STRING];
227
228   /* account isn't actually modified, since url isn't either */
229   mutt_account_tourl ((ACCOUNT *) account, &url);
230   url.path = NULL;
231   url_ciss_tostring (&url, hook, sizeof (hook), 0);
232   mutt_account_hook (hook);
233
234   conn = start ? start->next : Connections;
235   while (conn) {
236     if (mutt_account_match (account, &(conn->account)))
237       return conn;
238     conn = conn->next;
239   }
240
241   conn = socket_new_conn ();
242   memcpy (&conn->account, account, sizeof (ACCOUNT));
243
244   conn->next = Connections;
245   Connections = conn;
246
247   if (Tunnel && *Tunnel)
248     mutt_tunnel_socket_setup (conn);
249   else if (account->flags & M_ACCT_SSL) {
250 #ifdef USE_SSL
251     ssl_socket_setup (conn);
252 #elif USE_NSS
253     mutt_nss_socket_setup (conn);
254 #elif USE_GNUTLS
255     if (mutt_gnutls_socket_setup (conn) < 0) {
256       mutt_socket_free (conn);
257       return NULL;
258     }
259 #else
260     mutt_error _("SSL is unavailable.");
261
262     mutt_sleep (2);
263     mutt_socket_free (conn);
264
265     return NULL;
266 #endif
267   }
268   else {
269     conn->conn_read = raw_socket_read;
270     conn->conn_write = raw_socket_write;
271     conn->conn_open = raw_socket_open;
272     conn->conn_close = raw_socket_close;
273   }
274
275   return conn;
276 }
277
278 static int socket_preconnect (void)
279 {
280   int rc;
281   int save_errno;
282
283   if (safe_strlen (Preconnect)) {
284     dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
285     rc = mutt_system (Preconnect);
286     dprint (2, (debugfile, "Preconnect result: %d\n", rc));
287     if (rc) {
288       save_errno = errno;
289       mutt_perror (_("Preconnect command failed."));
290       mutt_sleep (1);
291
292       return save_errno;
293     }
294   }
295
296   return 0;
297 }
298
299 /* socket_connect: set up to connect to a socket fd. */
300 static int socket_connect (int fd, struct sockaddr *sa)
301 {
302   int sa_size;
303   int save_errno;
304
305   if (sa->sa_family == AF_INET)
306     sa_size = sizeof (struct sockaddr_in);
307 #ifdef HAVE_GETADDRINFO
308   else if (sa->sa_family == AF_INET6)
309     sa_size = sizeof (struct sockaddr_in6);
310 #endif
311   else {
312     dprint (1, (debugfile, "Unknown address family!\n"));
313     return -1;
314   }
315
316   if (ConnectTimeout > 0)
317     alarm (ConnectTimeout);
318
319   mutt_allow_interrupt (1);
320
321   save_errno = 0;
322
323   if (connect (fd, sa, sa_size) < 0) {
324     save_errno = errno;
325     dprint (2, (debugfile, "Connection failed. errno: %d...\n", errno));
326     SigInt = 0;                 /* reset in case we caught SIGINTR while in connect() */
327   }
328
329   if (ConnectTimeout > 0)
330     alarm (0);
331   mutt_allow_interrupt (0);
332
333   return save_errno;
334 }
335
336 /* socket_new_conn: allocate and initialise a new connection. */
337 static CONNECTION *socket_new_conn ()
338 {
339   CONNECTION *conn;
340
341   conn = (CONNECTION *) safe_calloc (1, sizeof (CONNECTION));
342   conn->fd = -1;
343
344   return conn;
345 }
346
347 int raw_socket_close (CONNECTION * conn)
348 {
349   return close (conn->fd);
350 }
351
352 int raw_socket_read (CONNECTION * conn, char *buf, size_t len)
353 {
354   int rc;
355
356   if ((rc = read (conn->fd, buf, len)) == -1) {
357     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
358                 strerror (errno));
359     mutt_sleep (2);
360   }
361
362   return rc;
363 }
364
365 int raw_socket_write (CONNECTION * conn, const char *buf, size_t count)
366 {
367   int rc;
368
369   if ((rc = write (conn->fd, buf, count)) == -1) {
370     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
371                 strerror (errno));
372     mutt_sleep (2);
373   }
374
375   return rc;
376 }
377
378 int raw_socket_open (CONNECTION * conn)
379 {
380   int rc;
381   int fd;
382
383   char *host_idna = NULL;
384
385 #ifdef HAVE_GETADDRINFO
386 /* --- IPv4/6 --- */
387
388   /* "65536\0" */
389   char port[6];
390   struct addrinfo hints;
391   struct addrinfo *res;
392   struct addrinfo *cur;
393
394   /* we accept v4 or v6 STREAM sockets */
395   memset (&hints, 0, sizeof (hints));
396
397   if (option (OPTUSEIPV6))
398     hints.ai_family = AF_UNSPEC;
399   else
400     hints.ai_family = AF_INET;
401
402   hints.ai_socktype = SOCK_STREAM;
403
404   snprintf (port, sizeof (port), "%d", conn->account.port);
405
406 # ifdef HAVE_LIBIDN
407   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
408     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
409     return -1;
410   }
411 # else
412   host_idna = conn->account.host;
413 # endif
414
415   mutt_message (_("Looking up %s..."), conn->account.host);
416
417
418   rc = getaddrinfo (host_idna, port, &hints, &res);
419
420 # ifdef HAVE_LIBIDN
421   FREE (&host_idna);
422 # endif
423
424   if (rc) {
425     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
426     return -1;
427   }
428
429   mutt_message (_("Connecting to %s..."), conn->account.host);
430
431   rc = -1;
432   for (cur = res; cur != NULL; cur = cur->ai_next) {
433     fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
434     if (fd >= 0) {
435       if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
436         fcntl (fd, F_SETFD, FD_CLOEXEC);
437         conn->fd = fd;
438         break;
439       }
440       else
441         close (fd);
442     }
443   }
444
445   freeaddrinfo (res);
446
447 #else
448   /* --- IPv4 only --- */
449
450   struct sockaddr_in sin;
451   struct hostent *he;
452   int i;
453
454   memset (&sin, 0, sizeof (sin));
455   sin.sin_port = htons (conn->account.port);
456   sin.sin_family = AF_INET;
457
458 # ifdef HAVE_LIBIDN
459   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
460     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
461     return -1;
462   }
463 # else
464   host_idna = conn->account.host;
465 # endif
466
467   mutt_message (_("Looking up %s..."), conn->account.host);
468
469   if ((he = gethostbyname (host_idna)) == NULL) {
470 # ifdef HAVE_LIBIDN
471     FREE (&host_idna);
472 # endif
473     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
474
475     return -1;
476   }
477
478 # ifdef HAVE_LIBIDN
479   FREE (&host_idna);
480 # endif
481
482   mutt_message (_("Connecting to %s..."), conn->account.host);
483
484   rc = -1;
485   for (i = 0; he->h_addr_list[i] != NULL; i++) {
486     memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
487     fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
488
489     if (fd >= 0) {
490       if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) {
491         conn->fd = fd;
492         break;
493       }
494       else
495         close (fd);
496     }
497   }
498
499 #endif
500   if (rc) {
501     mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
502                 (rc > 0) ? strerror (rc) : _("unknown error"));
503     mutt_sleep (2);
504     return -1;
505   }
506
507   return 0;
508 }