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