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.
12 #include <lib-lib/lib-lib.h>
14 #include <netinet/in.h>
16 #include <sys/socket.h>
18 #include <lib-ui/lib-ui.h>
24 #include "mutt_socket.h"
25 #include "mutt_tunnel.h"
26 #include "mutt_signal.h"
31 #include "mutt_idna.h"
33 /* support for multiple socket connections */
34 static CONNECTION *Connections = NULL;
37 int mutt_socket_open (CONNECTION * conn)
41 if (m_strlen(Preconnect)) {
42 rc = mutt_system (Preconnect);
44 mutt_perror (_("Preconnect command failed."));
50 return conn->conn_open(conn);
53 int mutt_socket_close (CONNECTION * conn)
58 rc = conn->conn_close (conn);
66 int mutt_socket_read (CONNECTION * conn, char *buf, ssize_t len)
74 rc = conn->conn_read (conn, buf, len);
77 mutt_error (_("Connection to %s closed"), conn->account.host);
81 mutt_socket_close (conn);
86 int mutt_socket_write(CONNECTION * conn, const char *buf)
96 if ((rc = conn->conn_write (conn, buf, len)) < 0) {
97 mutt_socket_close (conn);
105 /* simple read buffering to speed things up. */
106 int mutt_socket_readchar (CONNECTION * conn, char *c)
108 if (conn->bufpos >= conn->available) {
111 conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf));
116 if (conn->available == 0) {
117 mutt_error (_("Connection to %s closed"), conn->account.host);
120 if (conn->available <= 0) {
121 mutt_socket_close (conn);
125 *c = conn->inbuf[conn->bufpos];
130 int mutt_socket_readln(char *buf, ssize_t buflen, CONNECTION * conn)
135 for (i = 0; i < buflen - 1; i++) {
136 if (mutt_socket_readchar (conn, &ch) != 1) {
146 /* strip \r from \r\n termination */
147 if (i && buf[i - 1] == '\r')
152 /* number of bytes read, not m_strlen*/
156 int mutt_socket_readln2(buffer_t *buf, CONNECTION *conn)
160 while (mutt_socket_readchar(conn, &ch) == 1) {
162 if (buf->len && buf->data[buf->len - 1] == '\r') {
163 buf->data[--buf->len] = '\0';
167 buffer_addch(buf, ch);
173 CONNECTION *mutt_socket_head (void)
178 /* mutt_socket_free: remove connection from connection list and free it */
179 void mutt_socket_free (CONNECTION * conn)
186 /* head is special case, doesn't need prev updated */
188 Connections = iter->next;
194 if (iter->next == conn) {
196 iter->next = tmp->next;
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)
213 conn = start ? start->next : Connections;
215 if (mutt_account_match (account, &(conn->account)))
220 conn = p_new(CONNECTION, 1);
222 conn->account = *account;
223 conn->next = Connections;
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);
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;
243 /* socket_connect: set up to connect to a socket fd. */
244 static int socket_connect (int fd, struct sockaddr *sa)
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);
257 if (ConnectTimeout > 0)
258 alarm (ConnectTimeout);
260 mutt_allow_interrupt (1);
264 if (connect (fd, sa, sa_size) < 0) {
266 SigInt = 0; /* reset in case we caught SIGINTR while in connect() */
269 if (ConnectTimeout > 0)
271 mutt_allow_interrupt (0);
276 int raw_socket_close (CONNECTION * conn)
278 return close (conn->fd);
281 int raw_socket_read (CONNECTION * conn, char *buf, ssize_t len)
285 if ((rc = read (conn->fd, buf, len)) == -1) {
286 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
294 int raw_socket_write (CONNECTION * conn, const char *buf, ssize_t count)
298 if ((rc = write (conn->fd, buf, count)) == -1) {
299 mutt_error (_("Error talking to %s (%s)"), conn->account.host,
307 int raw_socket_open (CONNECTION * conn)
312 char *host_idna = NULL;
316 struct addrinfo hints;
317 struct addrinfo *res;
318 struct addrinfo *cur;
320 /* we accept v4 or v6 STREAM sockets */
323 hints.ai_family = AF_UNSPEC;
324 hints.ai_socktype = SOCK_STREAM;
326 snprintf (port, sizeof (port), "%d", conn->account.port);
329 if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) {
330 mutt_error (_("Bad IDN \"%s\"."), conn->account.host);
334 host_idna = conn->account.host;
337 mutt_message (_("Looking up %s..."), conn->account.host);
339 rc = getaddrinfo (host_idna, port, &hints, &res);
342 p_delete(&host_idna);
346 mutt_error (_("Could not find the host \"%s\""), conn->account.host);
351 mutt_message (_("Connecting to %s..."), conn->account.host);
354 for (cur = res; cur != NULL; cur = cur->ai_next) {
355 fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol);
357 if ((rc = socket_connect (fd, cur->ai_addr)) == 0) {
358 fcntl (fd, F_SETFD, FD_CLOEXEC);
370 mutt_error (_("Could not connect to %s (%s)."), conn->account.host,
371 (rc > 0) ? strerror (rc) : _("unknown error"));