++#if defined(USE_SSL)
++#include "mutt_ssl.h"
++#endif
++
++#ifdef HAVE_PGP
++#include "pgp.h"
++#endif
++
++#ifdef HAVE_SMIME
++#include "smime.h"
++#endif
++
++#if USE_HCACHE
++#include "hcache.h"
++#endif
++
++#include <unistd.h>
++#include <string.h>
++#include <ctype.h>
++#include <stdlib.h>
++
++#ifdef USE_SASL
++#include <sasl/sasl.h>
++#include <sasl/saslutil.h>
++
++#include "mutt_sasl.h"
++#endif
++
++static int nntp_connect_error (NNTP_SERVER *nserv)
++{
++ nserv->status = NNTP_NONE;
++ mutt_error _("Server closed connection!");
++ mutt_sleep (2);
++ return -1;
++}
++
++/* Get capabilities:
++ * -1 - error, connection is closed
++ * 0 - mode is reader, capabilities setted up
++ * 1 - need to switch to reader mode */
++static int nntp_capabilities (NNTP_SERVER *nserv)
++{
++ CONNECTION *conn = nserv->conn;
++ unsigned int mode_reader = 0;
++ char buf[LONG_STRING];
++ char authinfo[LONG_STRING] = "";
++
++ nserv->hasCAPABILITIES = 0;
++ nserv->hasSTARTTLS = 0;
++ nserv->hasDATE = 0;
++ nserv->hasLIST_NEWSGROUPS = 0;
++ nserv->hasLISTGROUP = 0;
++ nserv->hasLISTGROUPrange = 0;
++ nserv->hasOVER = 0;
++ FREE (&nserv->authenticators);
++
++ if (mutt_socket_write (conn, "CAPABILITIES\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++
++ /* no capabilities */
++ if (mutt_strncmp ("101", buf, 3))
++ return 1;
++ nserv->hasCAPABILITIES = 1;
++
++ /* parse capabilities */
++ do
++ {
++ if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (!mutt_strcmp ("STARTTLS", buf))
++ nserv->hasSTARTTLS = 1;
++ else if (!mutt_strcmp ("MODE-READER", buf))
++ mode_reader = 1;
++ else if (!mutt_strcmp ("READER", buf))
++ {
++ nserv->hasDATE = 1;
++ nserv->hasLISTGROUP = 1;
++ nserv->hasLISTGROUPrange = 1;
++ }
++ else if (!mutt_strncmp ("AUTHINFO ", buf, 9))
++ {
++ safe_strcat (buf, sizeof (buf), " ");
++ strfcpy (authinfo, buf + 8, sizeof (authinfo));
++ }
++#ifdef USE_SASL
++ else if (!mutt_strncmp ("SASL ", buf, 5))
++ {
++ char *p = buf + 5;
++ while (*p == ' ')
++ p++;
++ nserv->authenticators = safe_strdup (p);
++ }
++#endif
++ else if (!mutt_strcmp ("OVER", buf))
++ nserv->hasOVER = 1;
++ else if (!mutt_strncmp ("LIST ", buf, 5))
++ {
++ char *p = strstr (buf, " NEWSGROUPS");
++ if (p)
++ {
++ p += 11;
++ if (*p == '\0' || *p == ' ')
++ nserv->hasLIST_NEWSGROUPS = 1;
++ }
++ }
++ } while (mutt_strcmp (".", buf));
++ *buf = '\0';
++#ifdef USE_SASL
++ if (nserv->authenticators && strcasestr (authinfo, " SASL "))
++ strfcpy (buf, nserv->authenticators, sizeof (buf));
++#endif
++ if (strcasestr (authinfo, " USER "))
++ {
++ if (*buf)
++ safe_strcat (buf, sizeof (buf), " ");
++ safe_strcat (buf, sizeof (buf), "USER");
++ }
++ mutt_str_replace (&nserv->authenticators, buf);
++
++ /* current mode is reader */
++ if (nserv->hasDATE)
++ return 0;
++
++ /* server is mode-switching, need to switch to reader mode */
++ if (mode_reader)
++ return 1;
++
++ mutt_socket_close (conn);
++ nserv->status = NNTP_BYE;
++ mutt_error _("Server doesn't support reader mode.");
++ mutt_sleep (2);
++ return -1;
++}
++
++char *OverviewFmt =
++ "Subject:\0"
++ "From:\0"
++ "Date:\0"
++ "Message-ID:\0"
++ "References:\0"
++ "Content-Length:\0"
++ "Lines:\0"
++ "\0";
++
++/* Detect supported commands */
++static int nntp_attempt_features (NNTP_SERVER *nserv)
++{
++ CONNECTION *conn = nserv->conn;
++ char buf[LONG_STRING];
++
++ /* no CAPABILITIES, trying DATE, LISTGROUP, LIST NEWSGROUPS */
++ if (!nserv->hasCAPABILITIES)
++ {
++ if (mutt_socket_write (conn, "DATE\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (mutt_strncmp ("500", buf, 3))
++ nserv->hasDATE = 1;
++
++ if (mutt_socket_write (conn, "LISTGROUP\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (mutt_strncmp ("500", buf, 3))
++ nserv->hasLISTGROUP = 1;
++
++ if (mutt_socket_write (conn, "LIST NEWSGROUPS +\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (mutt_strncmp ("500", buf, 3))
++ nserv->hasLIST_NEWSGROUPS = 1;
++ if (!mutt_strncmp ("215", buf, 3))
++ {
++ do
++ {
++ if (mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ } while (mutt_strcmp (".", buf));
++ }
++ }
++
++ /* no LIST NEWSGROUPS, trying XGTITLE */
++ if (!nserv->hasLIST_NEWSGROUPS)
++ {
++ if (mutt_socket_write (conn, "XGTITLE\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (mutt_strncmp ("500", buf, 3))
++ nserv->hasXGTITLE = 1;
++ }
++
++ /* no OVER, trying XOVER */
++ if (!nserv->hasOVER)
++ {
++ if (mutt_socket_write (conn, "XOVER\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (mutt_strncmp ("500", buf, 3))
++ nserv->hasXOVER = 1;
++ }
++
++ /* trying LIST OVERVIEW.FMT */
++ if (nserv->hasOVER || nserv->hasXOVER)
++ {
++ if (mutt_socket_write (conn, "LIST OVERVIEW.FMT\r\n") < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ return nntp_connect_error (nserv);
++ if (mutt_strncmp ("215", buf, 3))
++ nserv->overview_fmt = OverviewFmt;
++ else
++ {
++ int chunk, cont = 0;
++ size_t buflen = 2 * LONG_STRING, off = 0, b = 0;
++
++ if (nserv->overview_fmt)
++ FREE (&nserv->overview_fmt);
++ nserv->overview_fmt = safe_malloc (buflen);
++
++ while (1)
++ {
++ if (buflen - off < LONG_STRING)
++ {
++ buflen *= 2;
++ safe_realloc (&nserv->overview_fmt, buflen);
++ }
++
++ chunk = mutt_socket_readln (nserv->overview_fmt + off,
++ buflen - off, conn);
++ if (chunk < 0)
++ {
++ FREE (&nserv->overview_fmt);
++ return nntp_connect_error (nserv);
++ }
++
++ if (!cont && !mutt_strcmp (".", nserv->overview_fmt + off))
++ break;
++
++ cont = chunk >= buflen - off ? 1 : 0;
++ off += strlen (nserv->overview_fmt + off);
++ if (!cont)
++ {
++ char *colon;
++
++ if (nserv->overview_fmt[b] == ':')
++ {
++ memmove (nserv->overview_fmt + b,
++ nserv->overview_fmt + b + 1, off - b - 1);
++ nserv->overview_fmt[off - 1] = ':';
++ }
++ colon = strchr (nserv->overview_fmt + b, ':');
++ if (!colon)
++ nserv->overview_fmt[off++] = ':';
++ else if (strcmp (colon + 1, "full"))
++ off = colon + 1 - nserv->overview_fmt;
++ if (!strcasecmp (nserv->overview_fmt + b, "Bytes:"))
++ {
++ strcpy (nserv->overview_fmt + b, "Content-Length:");
++ off = b + strlen (nserv->overview_fmt + b);
++ }
++ nserv->overview_fmt[off++] = '\0';
++ b = off;
++ }
++ }
++ nserv->overview_fmt[off++] = '\0';
++ safe_realloc (&nserv->overview_fmt, off);
++ }
++ }
++ return 0;
++}
++
++/* Get login, password and authenticate */
++static int nntp_auth (NNTP_SERVER *nserv)
++{
++ CONNECTION *conn = nserv->conn;
++ char buf[LONG_STRING];
++ char authenticators[LONG_STRING] = "USER";
++ char *method, *a, *p;
++ unsigned char flags = conn->account.flags;
++
++ while (1)
++ {
++ /* get login and password */
++ if (mutt_account_getuser (&conn->account) || !conn->account.user[0] ||
++ mutt_account_getpass (&conn->account) || !conn->account.pass[0])
++ break;
++
++ /* get list of authenticators */
++ if (NntpAuthenticators && *NntpAuthenticators)
++ strfcpy (authenticators, NntpAuthenticators, sizeof (authenticators));
++ else if (nserv->hasCAPABILITIES)
++ {
++ strfcpy (authenticators, NONULL (nserv->authenticators),
++ sizeof (authenticators));
++ p = authenticators;
++ while (*p)
++ {
++ if (*p == ' ')
++ *p = ':';
++ p++;
++ }
++ }
++ p = authenticators;
++ while (*p)
++ {
++ *p = ascii_toupper (*p);
++ p++;
++ }
++
++ dprint (1, (debugfile,
++ "nntp_auth: available methods: %s\n", nserv->authenticators));
++ a = authenticators;
++ while (1)
++ {
++ if (!a)
++ {
++ mutt_error _("No authenticators available");
++ mutt_sleep (2);
++ break;
++ }
++
++ method = a;
++ a = strchr (a, ':');
++ if (a)
++ *a++ = '\0';
++
++ /* check authenticator */
++ if (nserv->hasCAPABILITIES)
++ {
++ char *m;
++
++ if (!nserv->authenticators)
++ continue;
++ m = strcasestr (nserv->authenticators, method);
++ if (!m)
++ continue;
++ if (m > nserv->authenticators && *(m - 1) != ' ')
++ continue;
++ m += strlen (method);
++ if (*m != '\0' && *m != ' ')
++ continue;
++ }
++ dprint (1, (debugfile, "nntp_auth: trying method %s\n", method));
++
++ /* AUTHINFO USER authentication */
++ if (!strcmp (method, "USER"))
++ {
++ mutt_message (_("Authenticating (%s)..."), method);
++ snprintf (buf, sizeof (buf), "AUTHINFO USER %s\r\n", conn->account.user);
++ if (mutt_socket_write (conn, buf) < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ break;
++
++ /* authenticated, password is not required */
++ if (!mutt_strncmp ("281", buf, 3))
++ return 0;
++
++ /* username accepted, sending password */
++ if (!mutt_strncmp ("381", buf, 3))
++ {
++#ifdef DEBUG
++ if (debuglevel < MUTT_SOCK_LOG_FULL)
++ dprint (MUTT_SOCK_LOG_CMD, (debugfile,
++ "%d> AUTHINFO PASS *\n", conn->fd));
++#endif
++ snprintf (buf, sizeof (buf), "AUTHINFO PASS %s\r\n",
++ conn->account.pass);
++ if (mutt_socket_write_d (conn, buf, -1, MUTT_SOCK_LOG_FULL) < 0 ||
++ mutt_socket_readln (buf, sizeof (buf), conn) < 0)
++ break;
++
++ /* authenticated */
++ if (!mutt_strncmp ("281", buf, 3))
++ return 0;
++ }
++
++ /* server doesn't support AUTHINFO USER, trying next method */
++ if (*buf == '5')
++ continue;
++ }
++
++ else
++ {
++#ifdef USE_SASL
++ sasl_conn_t *saslconn;
++ sasl_interact_t *interaction = NULL;
++ int rc;
++ char inbuf[LONG_STRING] = "";
++ const char *mech;
++ const char *client_out = NULL;
++ unsigned int client_len, len;
++
++ if (mutt_sasl_client_new (conn, &saslconn) < 0)
++ {
++ dprint (1, (debugfile,
++ "nntp_auth: error allocating SASL connection.\n"));
++ continue;
++ }
++
++ while (1)
++ {
++ rc = sasl_client_start (saslconn, method, &interaction,
++ &client_out, &client_len, &mech);
++ if (rc != SASL_INTERACT)
++ break;
++ mutt_sasl_interact (interaction);
++ }
++ if (rc != SASL_OK && rc != SASL_CONTINUE)
++ {
++ sasl_dispose (&saslconn);
++ dprint (1, (debugfile,
++ "nntp_auth: error starting SASL authentication exchange.\n"));
++ continue;
++ }