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 #ifdef USE_SSL
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 #else
261     mutt_error _("SSL is unavailable.");
262     mutt_sleep (2);
263     mutt_socket_free (conn);
264
265     return NULL;
266 #endif
267   }
268   else
269   {
270     conn->read = raw_socket_read;
271     conn->write = raw_socket_write;
272     conn->open = raw_socket_open;
273     conn->close = raw_socket_close;
274   }
275
276   return conn;
277 }
278
279 static int socket_preconnect (void)
280 {
281   int rc;
282   int save_errno;
283
284   if (mutt_strlen (Preconnect))
285   {
286     dprint (2, (debugfile, "Executing preconnect: %s\n", Preconnect));
287     rc = mutt_system (Preconnect);
288     dprint (2, (debugfile, "Preconnect result: %d\n", rc));
289     if (rc)
290     {
291       save_errno = errno;
292       mutt_perror (_("Preconnect command failed."));
293       mutt_sleep (1);
294       
295       return save_errno;
296     }
297   }
298
299   return 0;
300 }
301
302 /* socket_connect: set up to connect to a socket fd. */
303 static int socket_connect (int fd, struct sockaddr* sa)
304 {
305   int sa_size;
306   int save_errno;
307
308   if (sa->sa_family == AF_INET)
309     sa_size = sizeof (struct sockaddr_in);
310 #ifdef HAVE_GETADDRINFO
311   else if (sa->sa_family == AF_INET6)
312     sa_size = sizeof (struct sockaddr_in6);
313 #endif
314   else
315   {
316     dprint (1, (debugfile, "Unknown address family!\n"));
317     return -1;
318   }
319   
320   if (ConnectTimeout > 0)
321       alarm (ConnectTimeout);
322
323   mutt_allow_interrupt (1);
324
325   save_errno = 0;
326
327   if (connect (fd, sa, sa_size) < 0)
328   {
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   {
363     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
364                 strerror (errno));
365     mutt_sleep (2);
366   }
367
368   return rc;
369 }
370
371 int raw_socket_write (CONNECTION* conn, const char* buf, size_t count)
372 {
373   int rc;
374
375   if ((rc = write (conn->fd, buf, count)) == -1)
376   {
377     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
378                 strerror (errno));
379     mutt_sleep (2);
380   }
381
382   return rc;
383 }
384
385 int raw_socket_open (CONNECTION* conn)
386 {
387   int rc;
388   int fd;
389
390   char *host_idna = NULL;
391   
392 #ifdef HAVE_GETADDRINFO
393 /* --- IPv4/6 --- */
394
395   /* "65536\0" */
396   char port[6];
397   struct addrinfo hints;
398   struct addrinfo* res;
399   struct addrinfo* cur;
400
401   /* we accept v4 or v6 STREAM sockets */
402   memset (&hints, 0, sizeof (hints));
403
404   if (option (OPTUSEIPV6))
405     hints.ai_family = AF_UNSPEC;
406   else
407     hints.ai_family = AF_INET;
408
409   hints.ai_socktype = SOCK_STREAM;
410
411   snprintf (port, sizeof (port), "%d", conn->account.port);
412   
413 # ifdef HAVE_LIBIDN
414   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
415   {
416     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
417     return -1;
418   }
419 # else
420   host_idna = conn->account.host;
421 # endif
422
423   mutt_message (_("Looking up %s..."), conn->account.host);
424
425   
426   rc = getaddrinfo (host_idna, port, &hints, &res);
427
428 # ifdef HAVE_LIBIDN
429   FREE (&host_idna);
430 # endif
431
432   if (rc)
433   {
434     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
435     return -1;
436   }
437
438   mutt_message (_("Connecting to %s..."), conn->account.host); 
439
440   rc = -1;
441   for (cur = res; cur != NULL; cur = cur->ai_next)
442   {
443     fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
444     if (fd >= 0)
445     {
446       if ((rc = socket_connect (fd, cur->ai_addr)) == 0)
447       {
448         fcntl (fd, F_SETFD, FD_CLOEXEC);
449         conn->fd = fd;
450         break;
451       }
452       else
453         close (fd);
454     }
455   }
456
457   freeaddrinfo (res);
458
459 #else
460   /* --- IPv4 only --- */
461
462   struct sockaddr_in sin;
463   struct hostent* he;
464   int i;
465
466   memset (&sin, 0, sizeof (sin));
467   sin.sin_port = htons (conn->account.port);
468   sin.sin_family = AF_INET;
469
470 # ifdef HAVE_LIBIDN
471   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS)
472   {
473     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
474     return -1;
475   }
476 # else
477   host_idna = conn->account.host;
478 # endif
479
480   mutt_message (_("Looking up %s..."), conn->account.host);
481
482   if ((he = gethostbyname (host_idna)) == NULL)
483   {
484 # ifdef HAVE_LIBIDN
485     FREE (&host_idna);
486 # endif
487     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
488         
489     return -1;
490   }
491
492 # ifdef HAVE_LIBIDN
493   FREE (&host_idna);
494 # endif
495
496   mutt_message (_("Connecting to %s..."), conn->account.host); 
497
498   rc = -1;
499   for (i = 0; he->h_addr_list[i] != NULL; i++)
500   {
501     memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length);
502     fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP);
503
504     if (fd >= 0)
505     {
506       if ((rc = socket_connect (fd, (struct sockaddr*) &sin)) == 0)
507       {
508         conn->fd = fd;
509         break;
510       }
511       else
512         close (fd);
513     }
514   }
515
516 #endif
517   if (rc)
518   {
519     mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
520             (rc > 0) ? strerror (rc) : _("unknown error"));
521     mutt_sleep (2);
522     return -1;
523   }
524   
525   return 0;
526 }