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