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