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>
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.
17 #include <netinet/in.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
26 #include <lib-lib/lib-lib.h>
27 #include <lib-ui/curses.h>
33 #include "mutt_socket.h"
34 #include "mutt_tunnel.h"
35 #include "mutt_signal.h"
38 #include "mutt_idna.h"
40 /* support for multiple socket connections */
41 static CONNECTION *Connections = NULL;
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);
49 int mutt_socket_open (CONNECTION * conn)
51 if (socket_preconnect ())
54 return conn->conn_open (conn);
57 int mutt_socket_close (CONNECTION * conn)
62 rc = conn->conn_close (conn);
70 int mutt_socket_read (CONNECTION * conn, char *buf, ssize_t len)
78 rc = conn->conn_read (conn, buf, len);
81 mutt_error (_("Connection to %s closed"), conn->account.host);
85 mutt_socket_close (conn);
90 int mutt_socket_write(CONNECTION * conn, const char *buf)
100 if ((rc = conn->conn_write (conn, buf, len)) < 0) {
101 mutt_socket_close (conn);
109 /* simple read buffering to speed things up. */
110 int mutt_socket_readchar (CONNECTION * conn, char *c)
112 if (conn->bufpos >= conn->available) {
115 conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
120 if (conn->available == 0) {
121 mutt_error (_("Connection to %s closed"), conn->account.host);
124 if (conn->available <= 0) {
125 mutt_socket_close (conn);
129 *c = conn->inbuf[conn->bufpos];
134 int mutt_socket_readln(char *buf, ssize_t buflen, CONNECTION * conn)
139 for (i = 0; i < buflen - 1; i++) {
140 if (mutt_socket_readchar (conn, &ch) != 1) {
150 /* strip \r from \r\n termination */
151 if (i && buf[i - 1] == '\r')
156 /* number of bytes read, not m_strlen*/
160 CONNECTION *mutt_socket_head (void)
165 /* mutt_socket_free: remove connection from connection list and free it */
166 void mutt_socket_free (CONNECTION * conn)
173 /* head is special case, doesn't need prev updated */
175 Connections = iter->next;
181 if (iter->next == conn) {
183 iter->next = tmp->next;
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)
200 char hook[LONG_STRING];
202 /* account isn't actually modified, since url isn't either */
203 mutt_account_tourl ((ACCOUNT *) account, &url);
205 url_ciss_tostring (&url, hook, sizeof (hook), 0);
206 mutt_account_hook (hook);
208 conn = start ? start->next : Connections;
210 if (mutt_account_match (account, &(conn->account)))
215 conn = socket_new_conn ();
216 memcpy (&conn->account, account, sizeof (ACCOUNT));
218 conn->next = Connections;
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);
230 mutt_error _("SSL is unavailable.");
233 mutt_socket_free (conn);
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;
248 static int socket_preconnect (void)
253 if (m_strlen(Preconnect)) {
254 rc = mutt_system (Preconnect);
257 mutt_perror (_("Preconnect command failed."));
267 /* socket_connect: set up to connect to a socket fd. */
268 static int socket_connect (int fd, struct sockaddr *sa)
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);
283 if (ConnectTimeout > 0)
284 alarm (ConnectTimeout);
286 mutt_allow_interrupt (1);
290 if (connect (fd, sa, sa_size) < 0) {
292 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
295 if (ConnectTimeout > 0)
297 mutt_allow_interrupt (0);
302 /* socket_new_conn: allocate and initialise a new connection. */
303 static CONNECTION *socket_new_conn (void)
307 conn = p_new(CONNECTION, 1);
313 int raw_socket_close (CONNECTION * conn)
315 return close (conn->fd);
318 int raw_socket_read (CONNECTION * conn, char *buf, ssize_t len)
322 if ((rc = read (conn->fd, buf, len)) == -1) {
323 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
331 int raw_socket_write (CONNECTION * conn, const char *buf, ssize_t count)
335 if ((rc = write (conn->fd, buf, count)) == -1) {
336 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
344 int raw_socket_open (CONNECTION * conn)
349 char *host_idna = NULL;
351 #ifdef HAVE_GETADDRINFO
356 struct addrinfo hints;
357 struct addrinfo *res;
358 struct addrinfo *cur;
360 /* we accept v4 or v6 STREAM sockets */
363 if (option (OPTUSEIPV6))
364 hints.ai_family = AF_UNSPEC;
366 hints.ai_family = AF_INET;
368 hints.ai_socktype = SOCK_STREAM;
370 snprintf (port, sizeof (port), "%d", conn->account.port);
373 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
374 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
378 host_idna = conn->account.host;
381 mutt_message (_("Looking up %s..."), conn->account.host);
384 rc = getaddrinfo (host_idna, port, &hints, &res);
387 p_delete(&host_idna);
391 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
396 mutt_message (_("Connecting to %s..."), conn->account.host);
399 for (cur = res; cur != NULL; cur = cur->ai_next) {
400 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
402 if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
403 fcntl (fd, F_SETFD, FD_CLOEXEC);
415 /* --- IPv4 only --- */
417 struct sockaddr_in sin;
422 sin.sin_port = htons (conn->account.port);
423 sin.sin_family = AF_INET;
426 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
427 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
431 host_idna = conn->account.host;
434 mutt_message (_("Looking up %s..."), conn->account.host);
436 if ((he = gethostbyname (host_idna)) == NULL) {
438 p_delete(&host_idna);
440 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
446 p_delete(&host_idna);
449 mutt_message (_("Connecting to %s..."), conn->account.host);
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);
457 if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) {
458 fcntl (fd, F_SETFD, FD_CLOEXEC);
469 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
470 (rc > 0) ? strerror (rc) : _("unknown error"));