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