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