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