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