Add some useful macros.
[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/lib-ui.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
28 #ifdef HAVE_LIBIDN
29 #include <idna.h>
30 #endif
31 #include "mutt_idna.h"
32
33 /* support for multiple socket connections */
34 static CONNECTION *Connections = NULL;
35
36 /* Wrappers */
37 int mutt_socket_open (CONNECTION * conn)
38 {
39   int rc;
40
41   if (m_strlen(Preconnect)) {
42     rc = mutt_system (Preconnect);
43     if (rc) {
44       mutt_perror (_("Preconnect command failed."));
45       mutt_sleep (1);
46       return -1;
47     }
48   }
49
50   return conn->conn_open(conn);
51 }
52
53 int mutt_socket_close (CONNECTION * conn)
54 {
55   int rc = -1;
56
57   if (conn->fd >= 0)
58     rc = conn->conn_close (conn);
59
60   conn->fd = -1;
61   conn->ssf = 0;
62
63   return rc;
64 }
65
66 int mutt_socket_read (CONNECTION * conn, char *buf, ssize_t len)
67 {
68   int rc;
69
70   if (conn->fd < 0) {
71     return -1;
72   }
73
74   rc = conn->conn_read (conn, buf, len);
75   /* EOF */
76   if (rc == 0) {
77     mutt_error (_("Connection to %s closed"), conn->account.host);
78     mutt_sleep (2);
79   }
80   if (rc <= 0)
81     mutt_socket_close (conn);
82
83   return rc;
84 }
85
86 int mutt_socket_write(CONNECTION * conn, const char *buf)
87 {
88   int rc;
89   int len;
90
91   if (conn->fd < 0) {
92     return -1;
93   }
94
95   len = m_strlen(buf);
96   if ((rc = conn->conn_write (conn, buf, len)) < 0) {
97     mutt_socket_close (conn);
98
99     return -1;
100   }
101
102   return rc;
103 }
104
105 /* simple read buffering to speed things up. */
106 int mutt_socket_readchar (CONNECTION * conn, char *c)
107 {
108   if (conn->bufpos >= conn->available) {
109     if (conn->fd >= 0)
110       conn->available =
111         conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
112     else {
113       return -1;
114     }
115     conn->bufpos = 0;
116     if (conn->available == 0) {
117       mutt_error (_("Connection to %s closed"), conn->account.host);
118       mutt_sleep (2);
119     }
120     if (conn->available <= 0) {
121       mutt_socket_close (conn);
122       return -1;
123     }
124   }
125   *c = conn->inbuf[conn->bufpos];
126   conn->bufpos++;
127   return 1;
128 }
129
130 int mutt_socket_readln(char *buf, ssize_t buflen, CONNECTION * conn)
131 {
132   char ch;
133   ssize_t i;
134
135   for (i = 0; i < buflen - 1; i++) {
136     if (mutt_socket_readchar (conn, &ch) != 1) {
137       buf[i] = '\0';
138       return -1;
139     }
140
141     if (ch == '\n')
142       break;
143     buf[i] = ch;
144   }
145
146   /* strip \r from \r\n termination */
147   if (i && buf[i - 1] == '\r')
148     buf[--i] = '\0';
149   else
150     buf[i] = '\0';
151
152   /* number of bytes read, not m_strlen*/
153   return i + 1;
154 }
155
156 int mutt_socket_readln2(buffer_t *buf, CONNECTION *conn)
157 {
158     char ch;
159
160     while (mutt_socket_readchar(conn, &ch) == 1) {
161         if (ch == '\n') {
162             if (buf->len && buf->data[buf->len - 1] == '\r') {
163                 buf->data[--buf->len] = '\0';
164             }
165             return 0;
166         }
167         buffer_addch(buf, ch);
168     }
169
170     return -1;
171 }
172
173 CONNECTION *mutt_socket_head (void)
174 {
175   return Connections;
176 }
177
178 /* mutt_socket_free: remove connection from connection list and free it */
179 void mutt_socket_free (CONNECTION * conn)
180 {
181   CONNECTION *iter;
182   CONNECTION *tmp;
183
184   iter = Connections;
185
186   /* head is special case, doesn't need prev updated */
187   if (iter == conn) {
188     Connections = iter->next;
189     p_delete(&iter);
190     return;
191   }
192
193   while (iter->next) {
194     if (iter->next == conn) {
195       tmp = iter->next;
196       iter->next = tmp->next;
197       p_delete(&tmp);
198       return;
199     }
200     iter = iter->next;
201   }
202 }
203
204 /* mutt_conn_find: find a connection off the list of connections whose
205  *   account matches account. If start is not null, only search for
206  *   connections after the given connection (allows higher level socket code
207  *   to make more fine-grained searches than account info - eg in IMAP we may
208  *   wish to find a connection which is not in IMAP_SELECTED state) */
209 CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account)
210 {
211   CONNECTION *conn;
212
213   conn = start ? start->next : Connections;
214   while (conn) {
215     if (mutt_account_match (account, &(conn->account)))
216       return conn;
217     conn = conn->next;
218   }
219
220   conn = p_new(CONNECTION, 1);
221   conn->fd = -1;
222   conn->account = *account;
223   conn->next  = Connections;
224   Connections = conn;
225
226   if (Tunnel && *Tunnel)
227     mutt_tunnel_socket_setup (conn);
228   else if (account->has_ssl) {
229     if (mutt_ssl_socket_setup (conn) < 0) {
230       mutt_socket_free (conn);
231       return NULL;
232     }
233   } else {
234     conn->conn_read = raw_socket_read;
235     conn->conn_write = raw_socket_write;
236     conn->conn_open = raw_socket_open;
237     conn->conn_close = raw_socket_close;
238   }
239
240   return conn;
241 }
242
243 /* socket_connect: set up to connect to a socket fd. */
244 static int socket_connect (int fd, struct sockaddr *sa)
245 {
246   int sa_size;
247   int save_errno;
248
249   if (sa->sa_family == AF_INET)
250     sa_size = sizeof (struct sockaddr_in);
251   else if (sa->sa_family == AF_INET6)
252     sa_size = sizeof (struct sockaddr_in6);
253   else {
254     return -1;
255   }
256
257   if (ConnectTimeout > 0)
258     alarm (ConnectTimeout);
259
260   mutt_allow_interrupt (1);
261
262   save_errno = 0;
263
264   if (connect (fd, sa, sa_size) < 0) {
265     save_errno = errno;
266     SigInt = 0;                 /* reset in case we caught SIGINTR while in connect() */
267   }
268
269   if (ConnectTimeout > 0)
270     alarm (0);
271   mutt_allow_interrupt (0);
272
273   return save_errno;
274 }
275
276 int raw_socket_close (CONNECTION * conn)
277 {
278   return close (conn->fd);
279 }
280
281 int raw_socket_read (CONNECTION * conn, char *buf, ssize_t len)
282 {
283   int rc;
284
285   if ((rc = read (conn->fd, buf, len)) == -1) {
286     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
287                 strerror (errno));
288     mutt_sleep (2);
289   }
290
291   return rc;
292 }
293
294 int raw_socket_write (CONNECTION * conn, const char *buf, ssize_t count)
295 {
296   int rc;
297
298   if ((rc = write (conn->fd, buf, count)) == -1) {
299     mutt_error (_("Error talking to %s (%s)"), conn->account.host,
300                 strerror (errno));
301     mutt_sleep (2);
302   }
303
304   return rc;
305 }
306
307 int raw_socket_open (CONNECTION * conn)
308 {
309   int rc;
310   int fd;
311
312   char *host_idna = NULL;
313
314   /* "65536\0" */
315   char port[6];
316   struct addrinfo hints;
317   struct addrinfo *res;
318   struct addrinfo *cur;
319
320   /* we accept v4 or v6 STREAM sockets */
321   p_clear(&hints, 1);
322
323   hints.ai_family   = AF_UNSPEC;
324   hints.ai_socktype = SOCK_STREAM;
325
326   snprintf (port, sizeof (port), "%d", conn->account.port);
327
328 # ifdef HAVE_LIBIDN
329   if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
330     mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
331     return -1;
332   }
333 # else
334   host_idna = conn->account.host;
335 # endif
336
337   mutt_message (_("Looking up %s..."), conn->account.host);
338
339   rc = getaddrinfo (host_idna, port, &hints, &res);
340
341 # ifdef HAVE_LIBIDN
342   p_delete(&host_idna);
343 # endif
344
345   if (rc) {
346     mutt_error (_("Could not find the host \"%s\""), conn->account.host);
347     mutt_sleep (2);
348     return -1;
349   }
350
351   mutt_message (_("Connecting to %s..."), conn->account.host);
352
353   rc = -1;
354   for (cur = res; cur != NULL; cur = cur->ai_next) {
355     fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
356     if (fd >= 0) {
357       if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
358         fcntl (fd, F_SETFD, FD_CLOEXEC);
359         conn->fd = fd;
360         break;
361       }
362       else
363         close (fd);
364     }
365   }
366
367   freeaddrinfo (res);
368
369   if (rc) {
370     mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
371                 (rc > 0) ? strerror (rc) : _("unknown error"));
372     mutt_sleep (2);
373     return -1;
374   }
375
376   return 0;
377 }