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