X-Git-Url: http://git.madism.org/?a=blobdiff_plain;f=lib-sys%2Fmutt_socket.c;fp=lib-sys%2Fmutt_socket.c;h=e3874d15614311dfddf3dc4eebafeef5f1685e1f;hb=ccf2b75a9ed50a79c4d8e5d6235c7313fcd3719a;hp=0000000000000000000000000000000000000000;hpb=7b8296cfa5c33fbc73c34c4fe1ff6d7bfbaaba01;p=apps%2Fmadmutt.git diff --git a/lib-sys/mutt_socket.c b/lib-sys/mutt_socket.c new file mode 100644 index 0000000..e3874d1 --- /dev/null +++ b/lib-sys/mutt_socket.c @@ -0,0 +1,497 @@ +/* + * Copyright notice from original mutt: + * Copyright (C) 1998 Michael R. Elkins + * Copyright (C) 1999-2005 Brendan Cully + * Copyright (C) 1999-2000 Tommi Komulainen + * + * This file is part of mutt-ng, see http://www.muttng.org/. + * It's licensed under the GNU General Public License, + * please see the file GPL in the top level source directory. + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include + +#include "mutt.h" +#include "globals.h" +#include "mutt_socket.h" +#include "mutt_tunnel.h" +#if defined(USE_SSL) || defined(USE_GNUTLS) +# include "mutt_ssl.h" +#endif + +#include "mutt_idna.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* support for multiple socket connections */ +static CONNECTION *Connections = NULL; + +/* forward declarations */ +static int socket_preconnect (void); +static int socket_connect (int fd, struct sockaddr *sa); +static CONNECTION *socket_new_conn (void); + +/* Wrappers */ +int mutt_socket_open (CONNECTION * conn) +{ + if (socket_preconnect ()) + return -1; + + return conn->conn_open (conn); +} + +int mutt_socket_close (CONNECTION * conn) +{ + int rc = -1; + + if (conn->fd < 0) + debug_print (1, ("Attempt to close closed connection.\n")); + else + rc = conn->conn_close (conn); + + conn->fd = -1; + conn->ssf = 0; + + return rc; +} + +int mutt_socket_read (CONNECTION * conn, char *buf, size_t len) +{ + int rc; + + if (conn->fd < 0) { + debug_print (1, ("attempt to read from closed connection\n")); + return -1; + } + + rc = conn->conn_read (conn, buf, len); + /* EOF */ + if (rc == 0) { + mutt_error (_("Connection to %s closed"), conn->account.host); + mutt_sleep (2); + } + if (rc <= 0) + mutt_socket_close (conn); + + return rc; +} + +int mutt_socket_write_d (CONNECTION * conn, const char *buf, int dbg) +{ + int rc; + int len; + + debug_print (dbg, ("> %s", buf)); + + if (conn->fd < 0) { + debug_print (1, ("attempt to write to closed connection\n")); + return -1; + } + + len = m_strlen(buf); + if ((rc = conn->conn_write (conn, buf, len)) < 0) { + debug_print (1, ("error writing, closing socket\n")); + mutt_socket_close (conn); + + return -1; + } + + if (rc < len) { + debug_print (1, ("ERROR: wrote %d of %d bytes!\n", rc, len)); + } + + return rc; +} + +/* simple read buffering to speed things up. */ +int mutt_socket_readchar (CONNECTION * conn, char *c) +{ + if (conn->bufpos >= conn->available) { + if (conn->fd >= 0) + conn->available = + conn->conn_read (conn, conn->inbuf, sizeof (conn->inbuf)); + else { + debug_print (1, ("attempt to read from closed connection.\n")); + return -1; + } + conn->bufpos = 0; + if (conn->available == 0) { + mutt_error (_("Connection to %s closed"), conn->account.host); + mutt_sleep (2); + } + if (conn->available <= 0) { + mutt_socket_close (conn); + return -1; + } + } + *c = conn->inbuf[conn->bufpos]; + conn->bufpos++; + return 1; +} + +int mutt_socket_readln_d (char *buf, ssize_t buflen, CONNECTION * conn, + int dbg) +{ + char ch; + ssize_t i; + + for (i = 0; i < buflen - 1; i++) { + if (mutt_socket_readchar (conn, &ch) != 1) { + buf[i] = '\0'; + return -1; + } + + if (ch == '\n') + break; + buf[i] = ch; + } + + /* strip \r from \r\n termination */ + if (i && buf[i - 1] == '\r') + buf[--i] = '\0'; + else + buf[i] = '\0'; + + debug_print (dbg, ("< %s\n", buf)); + + /* number of bytes read, not m_strlen*/ + return i + 1; +} + +CONNECTION *mutt_socket_head (void) +{ + return Connections; +} + +/* mutt_socket_free: remove connection from connection list and free it */ +void mutt_socket_free (CONNECTION * conn) +{ + CONNECTION *iter; + CONNECTION *tmp; + + iter = Connections; + + /* head is special case, doesn't need prev updated */ + if (iter == conn) { + Connections = iter->next; + p_delete(&iter); + return; + } + + while (iter->next) { + if (iter->next == conn) { + tmp = iter->next; + iter->next = tmp->next; + p_delete(&tmp); + return; + } + iter = iter->next; + } +} + +/* mutt_conn_find: find a connection off the list of connections whose + * account matches account. If start is not null, only search for + * connections after the given connection (allows higher level socket code + * to make more fine-grained searches than account info - eg in IMAP we may + * wish to find a connection which is not in IMAP_SELECTED state) */ +CONNECTION *mutt_conn_find (const CONNECTION * start, const ACCOUNT * account) +{ + CONNECTION *conn; + ciss_url_t url; + char hook[LONG_STRING]; + + /* account isn't actually modified, since url isn't either */ + mutt_account_tourl ((ACCOUNT *) account, &url); + url.path = NULL; + url_ciss_tostring (&url, hook, sizeof (hook), 0); + mutt_account_hook (hook); + + conn = start ? start->next : Connections; + while (conn) { + if (mutt_account_match (account, &(conn->account))) + return conn; + conn = conn->next; + } + + conn = socket_new_conn (); + memcpy (&conn->account, account, sizeof (ACCOUNT)); + + conn->next = Connections; + Connections = conn; + + if (Tunnel && *Tunnel) + mutt_tunnel_socket_setup (conn); + else if (account->flags & M_ACCT_SSL) { +#if defined (USE_SSL) || defined (USE_GNUTLS) + if (mutt_ssl_socket_setup (conn) < 0) { + mutt_socket_free (conn); + return NULL; + } +#else + mutt_error _("SSL is unavailable."); + + mutt_sleep (2); + mutt_socket_free (conn); + + return NULL; +#endif + } + else { + conn->conn_read = raw_socket_read; + conn->conn_write = raw_socket_write; + conn->conn_open = raw_socket_open; + conn->conn_close = raw_socket_close; + } + + return conn; +} + +static int socket_preconnect (void) +{ + int rc; + int save_errno; + + if (m_strlen(Preconnect)) { + debug_print (2, ("Executing preconnect: %s\n", Preconnect)); + rc = mutt_system (Preconnect); + debug_print (2, ("Preconnect result: %d\n", rc)); + if (rc) { + save_errno = errno; + mutt_perror (_("Preconnect command failed.")); + mutt_sleep (1); + + return save_errno; + } + } + + return 0; +} + +/* socket_connect: set up to connect to a socket fd. */ +static int socket_connect (int fd, struct sockaddr *sa) +{ + int sa_size; + int save_errno; + + if (sa->sa_family == AF_INET) + sa_size = sizeof (struct sockaddr_in); +#ifdef HAVE_GETADDRINFO + else if (sa->sa_family == AF_INET6) + sa_size = sizeof (struct sockaddr_in6); +#endif + else { + debug_print (1, ("Unknown address family!\n")); + return -1; + } + + if (ConnectTimeout > 0) + alarm (ConnectTimeout); + + mutt_allow_interrupt (1); + + save_errno = 0; + + if (connect (fd, sa, sa_size) < 0) { + save_errno = errno; + debug_print (2, ("Connection failed. errno: %d...\n", errno)); + SigInt = 0; /* reset in case we caught SIGINTR while in connect() */ + } + + if (ConnectTimeout > 0) + alarm (0); + mutt_allow_interrupt (0); + + return save_errno; +} + +/* socket_new_conn: allocate and initialise a new connection. */ +static CONNECTION *socket_new_conn (void) +{ + CONNECTION *conn; + + conn = p_new(CONNECTION, 1); + conn->fd = -1; + + return conn; +} + +int raw_socket_close (CONNECTION * conn) +{ + return close (conn->fd); +} + +int raw_socket_read (CONNECTION * conn, char *buf, size_t len) +{ + int rc; + + if ((rc = read (conn->fd, buf, len)) == -1) { + mutt_error (_("Error talking to %s (%s)"), conn->account.host, + strerror (errno)); + mutt_sleep (2); + } + + return rc; +} + +int raw_socket_write (CONNECTION * conn, const char *buf, size_t count) +{ + int rc; + + if ((rc = write (conn->fd, buf, count)) == -1) { + mutt_error (_("Error talking to %s (%s)"), conn->account.host, + strerror (errno)); + mutt_sleep (2); + } + + return rc; +} + +int raw_socket_open (CONNECTION * conn) +{ + int rc; + int fd; + + char *host_idna = NULL; + +#ifdef HAVE_GETADDRINFO +/* --- IPv4/6 --- */ + + /* "65536\0" */ + char port[6]; + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *cur; + + /* we accept v4 or v6 STREAM sockets */ + p_clear(&hints, 1); + + if (option (OPTUSEIPV6)) + hints.ai_family = AF_UNSPEC; + else + hints.ai_family = AF_INET; + + hints.ai_socktype = SOCK_STREAM; + + snprintf (port, sizeof (port), "%d", conn->account.port); + +# ifdef HAVE_LIBIDN + if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) { + mutt_error (_("Bad IDN \"%s\"."), conn->account.host); + return -1; + } +# else + host_idna = conn->account.host; +# endif + + mutt_message (_("Looking up %s..."), conn->account.host); + + + rc = getaddrinfo (host_idna, port, &hints, &res); + +# ifdef HAVE_LIBIDN + p_delete(&host_idna); +# endif + + if (rc) { + mutt_error (_("Could not find the host \"%s\""), conn->account.host); + mutt_sleep (2); + return -1; + } + + mutt_message (_("Connecting to %s..."), conn->account.host); + + rc = -1; + for (cur = res; cur != NULL; cur = cur->ai_next) { + fd = socket (cur->ai_family, cur->ai_socktype, cur->ai_protocol); + if (fd >= 0) { + if ((rc = socket_connect (fd, cur->ai_addr)) == 0) { + fcntl (fd, F_SETFD, FD_CLOEXEC); + conn->fd = fd; + break; + } + else + close (fd); + } + } + + freeaddrinfo (res); + +#else + /* --- IPv4 only --- */ + + struct sockaddr_in sin; + struct hostent *he; + int i; + + p_clear(&sin, 1); + sin.sin_port = htons (conn->account.port); + sin.sin_family = AF_INET; + +# ifdef HAVE_LIBIDN + if (idna_to_ascii_lz (conn->account.host, &host_idna, 1) != IDNA_SUCCESS) { + mutt_error (_("Bad IDN \"%s\"."), conn->account.host); + return -1; + } +# else + host_idna = conn->account.host; +# endif + + mutt_message (_("Looking up %s..."), conn->account.host); + + if ((he = gethostbyname (host_idna)) == NULL) { +# ifdef HAVE_LIBIDN + p_delete(&host_idna); +# endif + mutt_error (_("Could not find the host \"%s\""), conn->account.host); + + return -1; + } + +# ifdef HAVE_LIBIDN + p_delete(&host_idna); +# endif + + mutt_message (_("Connecting to %s..."), conn->account.host); + + rc = -1; + for (i = 0; he->h_addr_list[i] != NULL; i++) { + memcpy (&sin.sin_addr, he->h_addr_list[i], he->h_length); + fd = socket (PF_INET, SOCK_STREAM, IPPROTO_IP); + + if (fd >= 0) { + if ((rc = socket_connect (fd, (struct sockaddr *) &sin)) == 0) { + fcntl (fd, F_SETFD, FD_CLOEXEC); + conn->fd = fd; + break; + } + else + close (fd); + } + } + +#endif + if (rc) { + mutt_error (_("Could not connect to %s (%s)."), conn->account.host, + (rc > 0) ? strerror (rc) : _("unknown error")); + mutt_sleep (2); + return -1; + } + + return 0; +}