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