Esempio n. 1
0
static int smtp_open (CONNECTION* conn)
{
        int rc;

        if (mutt_socket_open (conn))
                return -1;

/* get greeting string */
        if ((rc = smtp_get_resp (conn)))
                return rc;

        if ((rc = smtp_helo (conn)))
                return rc;

#ifdef USE_SSL
        if (conn->ssf)
                rc = M_NO;
        else if (option (OPTSSLFORCETLS))
                rc = M_YES;
        else if (mutt_bit_isset (Capabilities, STARTTLS) &&
                (rc = query_quadoption (OPT_SSLSTARTTLS,
                _("Secure connection with TLS?"))) == -1)
                return rc;

        if (rc == M_YES) {
                if (mutt_socket_write (conn, "STARTTLS\r\n") < 0)
                        return smtp_err_write;
                if ((rc = smtp_get_resp (conn)))
                        return rc;

                if (mutt_ssl_starttls (conn)) {
                        mutt_error (_("Could not negotiate TLS connection"));
                        mutt_sleep (1);
                        return -1;
                }

/* re-EHLO to get authentication mechanisms */
                if ((rc = smtp_helo (conn)))
                        return rc;
        }
#endif

        if (conn->account.flags & M_ACCT_USER) {
                if (!mutt_bit_isset (Capabilities, AUTH)) {
                        mutt_error (_("SMTP server does not support authentication"));
                        mutt_sleep (1);
                        return -1;
                }

#ifdef USE_SASL
                return smtp_auth (conn);
#else
                mutt_error (_("SMTP authentication requires SASL"));
                mutt_sleep (1);
                return -1;
#endif                            /* USE_SASL */
        }

        return 0;
}
Esempio n. 2
0
static int smtp_helo (CONNECTION* conn)
{
        char buf[LONG_STRING];
        const char* fqdn;

        memset (Capabilities, 0, sizeof (Capabilities));

        if (!Esmtp) {
/* if TLS or AUTH are requested, use EHLO */
                if (conn->account.flags & M_ACCT_USER)
                        Esmtp = 1;
#ifdef USE_SSL
                if (option (OPTSSLFORCETLS) || quadoption (OPT_SSLSTARTTLS) != M_NO)
                        Esmtp = 1;
#endif
        }

        if(!(fqdn = mutt_fqdn (0)))
                fqdn = NONULL (Hostname);

        snprintf (buf, sizeof (buf), "%s %s\r\n", Esmtp ? "EHLO" : "HELO", fqdn);
/* XXX there should probably be a wrapper in mutt_socket.c that
 * repeatedly calls conn->write until all data is sent.  This
 * currently doesn't check for a short write.
 */
        if (mutt_socket_write (conn, buf) == -1)
                return smtp_err_write;
        return smtp_get_resp (conn);
}
Esempio n. 3
0
static int
smtp_rcpt_to (CONNECTION * conn, const ADDRESS * a)
{
        char buf[1024];
        int r;

        while (a) {
/* weed out group mailboxes, since those are for display only */
                if (!a->mailbox || a->group) {
                        a = a->next;
                        continue;
                }
                if (mutt_bit_isset (Capabilities, DSN) && DsnNotify)
                        snprintf (buf, sizeof (buf), "RCPT TO:<%s> NOTIFY=%s\r\n",
                                a->mailbox, DsnNotify);
                else
                        snprintf (buf, sizeof (buf), "RCPT TO:<%s>\r\n", a->mailbox);
                if (mutt_socket_write (conn, buf) == -1)
                        return smtp_err_write;
                if ((r = smtp_get_resp (conn)))
                        return r;
                a = a->next;
        }

        return 0;
}
Esempio n. 4
0
/* this is basically a stripped-down version of the cram-md5 method. */
imap_auth_res_t imap_auth_anon (IMAP_DATA* idata, const char* method)
{
  int rc;

  if (!mutt_bit_isset (idata->capabilities, AUTH_ANON))
    return IMAP_AUTH_UNAVAIL;

  if (mutt_account_getuser (&idata->conn->account))
    return IMAP_AUTH_FAILURE;

  if (idata->conn->account.user[0] != '\0')
    return IMAP_AUTH_UNAVAIL;

  mutt_message _("Authenticating (anonymous)...");

  imap_cmd_start (idata, "AUTHENTICATE ANONYMOUS");

  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);

  if (rc != IMAP_CMD_RESPOND)
  {
    dprint (1, (debugfile, "Invalid response from server.\n"));
    goto bail;
  }

  mutt_socket_write (idata->conn, "ZHVtbXkK\r\n"); /* base64 ("dummy") */

  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);
  
  if (rc != IMAP_CMD_OK)
  {
    dprint (1, (debugfile, "Error receiving server response.\n"));
    goto bail;
  }

  if (imap_code (idata->buf))
    return IMAP_AUTH_SUCCESS;

 bail:
  mutt_error _("Anonymous authentication failed.");
  mutt_sleep (2);
  return IMAP_AUTH_FAILURE;
}
Esempio n. 5
0
/* imap_auth_sasl: Default authenticator if available. */
imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata, const char* method)
{
  sasl_conn_t* saslconn;
  sasl_interact_t* interaction = NULL;
  int rc, irc;
  char buf[HUGE_STRING];
  const char* mech;
  const char *pc = NULL;
  unsigned int len, olen;
  unsigned char client_start;

  if (mutt_sasl_client_new (idata->conn, &saslconn) < 0)
  {
    dprint (1, (debugfile,
      "imap_auth_sasl: Error allocating SASL connection.\n"));
    return IMAP_AUTH_FAILURE;
  }

  rc = SASL_FAIL;

  /* If the user hasn't specified a method, use any available */
  if (!method)
  {
    method = idata->capstr;

    /* hack for SASL ANONYMOUS support:
     * 1. Fetch username. If it's "" or "anonymous" then
     * 2. attempt sasl_client_start with only "AUTH=ANONYMOUS" capability
     * 3. if sasl_client_start fails, fall through... */

    if (mutt_account_getuser (&idata->conn->account))
      return IMAP_AUTH_FAILURE;

    if (mutt_bit_isset (idata->capabilities, AUTH_ANON) &&
	(!idata->conn->account.user[0] ||
	 !ascii_strncmp (idata->conn->account.user, "anonymous", 9)))
      rc = sasl_client_start (saslconn, "AUTH=ANONYMOUS", NULL, &pc, &olen, 
                              &mech);
  } else if (!ascii_strcasecmp ("login", method) &&
	!strstr (NONULL (idata->capstr), "AUTH=LOGIN"))
    /* do not use SASL login for regular IMAP login (#3556) */
    return IMAP_AUTH_UNAVAIL;
  
  if (rc != SASL_OK && rc != SASL_CONTINUE)
    do
    {
      rc = sasl_client_start (saslconn, method, &interaction,
        &pc, &olen, &mech);
      if (rc == SASL_INTERACT)
	mutt_sasl_interact (interaction);
    }
    while (rc == SASL_INTERACT);

  client_start = (olen > 0);

  if (rc != SASL_OK && rc != SASL_CONTINUE)
  {
    if (method)
      dprint (2, (debugfile, "imap_auth_sasl: %s unavailable\n", method));
    else
      dprint (1, (debugfile, "imap_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n"));
    /* SASL doesn't support LOGIN, so fall back */

    return IMAP_AUTH_UNAVAIL;
  }

  mutt_message (_("Authenticating (%s)..."), mech);

  snprintf (buf, sizeof (buf), "AUTHENTICATE %s", mech);
  if (mutt_bit_isset (idata->capabilities, SASL_IR) && client_start)
  {
    len = mutt_strlen (buf);
    buf[len++] = ' ';
    if (sasl_encode64 (pc, olen, buf + len, sizeof (buf) - len, &olen) != SASL_OK)
    {
      dprint (1, (debugfile, "imap_auth_sasl: error base64-encoding client response.\n"));
      goto bail;
    }
    client_start = olen = 0;
  }
  imap_cmd_start (idata, buf);
  irc = IMAP_CMD_CONTINUE;

  /* looping protocol */
  while (rc == SASL_CONTINUE || olen > 0)
  {
    do
      irc = imap_cmd_step (idata);
    while (irc == IMAP_CMD_CONTINUE);

    if (irc == IMAP_CMD_BAD || irc == IMAP_CMD_NO)
      goto bail;

    if (irc == IMAP_CMD_RESPOND)
    {
      /* Exchange incorrectly returns +\r\n instead of + \r\n */
      if (idata->buf[1] == '\0')
      {
	buf[0] = '\0';
	len = 0;
      }
      else if (sasl_decode64 (idata->buf+2, strlen (idata->buf+2), buf,
			      LONG_STRING-1, &len) != SASL_OK)
      {
	dprint (1, (debugfile, "imap_auth_sasl: error base64-decoding server response.\n"));
	goto bail;
      }
    }

    /* client-start is only available with the SASL-IR extension, but
     * SASL 2.1 seems to want to use it regardless, at least for DIGEST
     * fast reauth. Override if the server sent an initial continuation */
    if (!client_start || buf[0])
    {
      do
      {
	rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen);
	if (rc == SASL_INTERACT)
	  mutt_sasl_interact (interaction);
      }
      while (rc == SASL_INTERACT);
    }
    else
      client_start = 0;

    /* send out response, or line break if none needed */
    if (olen)
    {
      if (sasl_encode64 (pc, olen, buf, sizeof (buf), &olen) != SASL_OK)
      {
	dprint (1, (debugfile, "imap_auth_sasl: error base64-encoding client response.\n"));
	goto bail;
      }
    }
    
    if (irc == IMAP_CMD_RESPOND)
    {
      strfcpy (buf + olen, "\r\n", sizeof (buf) - olen);
      mutt_socket_write (idata->conn, buf);
    }

    /* If SASL has errored out, send an abort string to the server */
    if (rc < 0)
    {
      mutt_socket_write (idata->conn, "*\r\n");
      dprint (1, (debugfile, "imap_auth_sasl: sasl_client_step error %d\n",rc));
    }
	  
    olen = 0;
  }

  while (irc != IMAP_CMD_OK)
    if ((irc = imap_cmd_step (idata)) != IMAP_CMD_CONTINUE)
      break;

  if (rc != SASL_OK)
    goto bail;

  if (imap_code (idata->buf))
  {
    mutt_sasl_setup_conn (idata->conn, saslconn);
    return IMAP_AUTH_SUCCESS;
  }

 bail:
  sasl_dispose (&saslconn);

  if (method)
  {
    dprint (2, (debugfile, "imap_auth_sasl: %s failed\n", method));
    return IMAP_AUTH_UNAVAIL;
  }

  mutt_error _("SASL authentication failed.");
  mutt_sleep(2);

  return IMAP_AUTH_FAILURE;
}
Esempio n. 6
0
int imap_append_message (CONTEXT *ctx, MESSAGE *msg)
{
  IMAP_DATA* idata;
  FILE *fp;
  char buf[LONG_STRING];
  char mbox[LONG_STRING];
  char mailbox[LONG_STRING];
  char internaldate[IMAP_DATELEN];
  char imap_flags[SHORT_STRING];
  size_t len;
  progress_t progressbar;
  size_t sent;
  int c, last;
  IMAP_MBOX mx;
  int rc;

  idata = (IMAP_DATA*) ctx->data;

  if (imap_parse_path (ctx->path, &mx))
    return -1;

  imap_fix_path (idata, mx.mbox, mailbox, sizeof (mailbox));
  if (!*mailbox)
    strfcpy (mailbox, "INBOX", sizeof (mailbox));

  if ((fp = fopen (msg->path, "r")) == NULL)
  {
    mutt_perror (msg->path);
    goto fail;
  }

  /* currently we set the \Seen flag on all messages, but probably we
   * should scan the message Status header for flag info. Since we're
   * already rereading the whole file for length it isn't any more
   * expensive (it'd be nice if we had the file size passed in already
   * by the code that writes the file, but that's a lot of changes.
   * Ideally we'd have a HEADER structure with flag info here... */
  for (last = EOF, len = 0; (c = fgetc(fp)) != EOF; last = c)
  {
    if(c == '\n' && last != '\r')
      len++;

    len++;
  }
  rewind (fp);

  mutt_progress_init (&progressbar, _("Uploading message..."),
		      MUTT_PROGRESS_SIZE, NetInc, len);

  imap_munge_mbox_name (idata, mbox, sizeof (mbox), mailbox);
  imap_make_date (internaldate, msg->received);

  imap_flags[0] = imap_flags[1] = 0;
  if (msg->flags.read)
    safe_strcat (imap_flags, sizeof (imap_flags), " \\Seen");
  if (msg->flags.replied)
    safe_strcat (imap_flags, sizeof (imap_flags), " \\Answered");
  if (msg->flags.flagged)
    safe_strcat (imap_flags, sizeof (imap_flags), " \\Flagged");
  if (msg->flags.draft)
    safe_strcat (imap_flags, sizeof (imap_flags), " \\Draft");

  snprintf (buf, sizeof (buf), "APPEND %s (%s) \"%s\" {%lu}", mbox,
            imap_flags + 1,
	    internaldate,
	    (unsigned long) len);

  imap_cmd_start (idata, buf);

  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);

  if (rc != IMAP_CMD_RESPOND)
  {
    char *pc;

    dprint (1, (debugfile, "imap_append_message(): command failed: %s\n",
		idata->buf));

    pc = idata->buf + SEQLEN;
    SKIPWS (pc);
    pc = imap_next_word (pc);
    mutt_error ("%s", pc);
    mutt_sleep (1);
    safe_fclose (&fp);
    goto fail;
  }

  for (last = EOF, sent = len = 0; (c = fgetc(fp)) != EOF; last = c)
  {
    if (c == '\n' && last != '\r')
      buf[len++] = '\r';

    buf[len++] = c;

    if (len > sizeof(buf) - 3)
    {
      sent += len;
      flush_buffer(buf, &len, idata->conn);
      mutt_progress_update (&progressbar, sent, -1);
    }
  }

  if (len)
    flush_buffer(buf, &len, idata->conn);

  mutt_socket_write (idata->conn, "\r\n");
  safe_fclose (&fp);

  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);

  if (!imap_code (idata->buf))
  {
    char *pc;

    dprint (1, (debugfile, "imap_append_message(): command failed: %s\n",
		idata->buf));
    pc = idata->buf + SEQLEN;
    SKIPWS (pc);
    pc = imap_next_word (pc);
    mutt_error ("%s", pc);
    mutt_sleep (1);
    goto fail;
  }

  FREE (&mx.mbox);
  return 0;

 fail:
  FREE (&mx.mbox);
  return -1;
}
Esempio n. 7
0
static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist)
{
        sasl_conn_t* saslconn;
        sasl_interact_t* interaction = NULL;
        const char* mech;
        const char* data = NULL;
        unsigned int len;
        char buf[HUGE_STRING];
        int rc, saslrc;

        if (mutt_sasl_client_new (conn, &saslconn) < 0)
                return SMTP_AUTH_FAIL;

        do {
                rc = sasl_client_start (saslconn, mechlist, &interaction, &data, &len, &mech);
                if (rc == SASL_INTERACT)
                        mutt_sasl_interact (interaction);
        }
        while (rc == SASL_INTERACT);

        if (rc != SASL_OK && rc != SASL_CONTINUE) {
                dprint (2, (debugfile, "smtp_auth_sasl: %s unavailable\n", mech));
                sasl_dispose (&saslconn);
                return SMTP_AUTH_UNAVAIL;
        }

        if (!option(OPTNOCURSES))
                mutt_message (_("Authenticating (%s)..."), mech);

        snprintf (buf, sizeof (buf), "AUTH %s", mech);
        if (len) {
                safe_strcat (buf, sizeof (buf), " ");
                if (sasl_encode64 (data, len, buf + mutt_strlen (buf),
                sizeof (buf) - mutt_strlen (buf), &len) != SASL_OK) {
                        dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n"));
                        goto fail;
                }
        }
        safe_strcat (buf, sizeof (buf), "\r\n");

        do {
                if (mutt_socket_write (conn, buf) < 0)
                        goto fail;
                if ((rc = mutt_socket_readln (buf, sizeof (buf), conn)) < 0)
                        goto fail;
                if (smtp_code (buf, rc, &rc) < 0)
                        goto fail;

                if (rc != smtp_ready)
                        break;

                if (sasl_decode64 (buf+4, strlen (buf+4), buf, sizeof (buf), &len) != SASL_OK) {
                        dprint (1, (debugfile, "smtp_auth_sasl: error base64-decoding server response.\n"));
                        goto fail;
                }

                do {
                        saslrc = sasl_client_step (saslconn, buf, len, &interaction, &data, &len);
                        if (saslrc == SASL_INTERACT)
                                mutt_sasl_interact (interaction);
                }
                while (saslrc == SASL_INTERACT);

                if (len) {
                        if (sasl_encode64 (data, len, buf, sizeof (buf), &len) != SASL_OK) {
                                dprint (1, (debugfile, "smtp_auth_sasl: error base64-encoding client response.\n"));
                                goto fail;
                        }
                }
                strfcpy (buf + len, "\r\n", sizeof (buf) - len);
        } while (rc == smtp_ready && saslrc != SASL_FAIL);

        if (smtp_success (rc)) {
                mutt_sasl_setup_conn (conn, saslconn);
                return SMTP_AUTH_SUCCESS;
        }

        fail:
        sasl_dispose (&saslconn);
        return SMTP_AUTH_FAIL;
}
Esempio n. 8
0
int
mutt_smtp_send (const ADDRESS* from, const ADDRESS* to, const ADDRESS* cc,
const ADDRESS* bcc, const char *msgfile, int eightbit)
{
        CONNECTION *conn;
        ACCOUNT account;
        const char* envfrom;
        char buf[1024];
        int ret = -1;

/* it might be better to synthesize an envelope from from user and host
 * but this condition is most likely arrived at accidentally */
        if (EnvFrom)
                envfrom = EnvFrom->mailbox;
        else if (from)
                envfrom = from->mailbox;
        else {
                mutt_error (_("No from address given"));
                return -1;
        }

        if (smtp_fill_account (&account) < 0)
                return ret;

        if (!(conn = mutt_conn_find (NULL, &account)))
                return -1;

        Esmtp = eightbit;

        do {
/* send our greeting */
                if (( ret = smtp_open (conn)))
                        break;
                FREE (&AuthMechs);

/* send the sender's address */
                ret = snprintf (buf, sizeof (buf), "MAIL FROM:<%s>", envfrom);
                if (eightbit && mutt_bit_isset (Capabilities, EIGHTBITMIME)) {
                        safe_strncat (buf, sizeof (buf), " BODY=8BITMIME", 15);
                        ret += 14;
                }
                if (DsnReturn && mutt_bit_isset (Capabilities, DSN))
                        ret += snprintf (buf + ret, sizeof (buf) - ret, " RET=%s", DsnReturn);
                safe_strncat (buf, sizeof (buf), "\r\n", 3);
                if (mutt_socket_write (conn, buf) == -1) {
                        ret = smtp_err_write;
                        break;
                }
                if ((ret = smtp_get_resp (conn)))
                        break;

/* send the recipient list */
                if ((ret = smtp_rcpt_to (conn, to)) || (ret = smtp_rcpt_to (conn, cc))
                        || (ret = smtp_rcpt_to (conn, bcc)))
                        break;

/* send the message data */
                if ((ret = smtp_data (conn, msgfile)))
                        break;

                mutt_socket_write (conn, "QUIT\r\n");

                ret = 0;
        }
        while (0);

        if (conn)
                mutt_socket_close (conn);

        if (ret == smtp_err_read)
                mutt_error (_("SMTP session failed: read error"));
        else if (ret == smtp_err_write)
                mutt_error (_("SMTP session failed: write error"));
        else if (ret == smtp_err_code)
                mutt_error (_("Invalid server response"));

        return ret;
}
Esempio n. 9
0
static int
smtp_data (CONNECTION * conn, const char *msgfile)
{
        char buf[1024];
        FILE *fp = 0;
        progress_t progress;
        struct stat st;
        int r, term = 0;
        size_t buflen = 0;

        fp = fopen (msgfile, "r");
        if (!fp) {
                mutt_error (_("SMTP session failed: unable to open %s"), msgfile);
                return -1;
        }
        stat (msgfile, &st);
        unlink (msgfile);
        mutt_progress_init (&progress, _("Sending message..."), M_PROGRESS_SIZE,
                NetInc, st.st_size);

        snprintf (buf, sizeof (buf), "DATA\r\n");
        if (mutt_socket_write (conn, buf) == -1) {
                safe_fclose (&fp);
                return smtp_err_write;
        }
        if ((r = smtp_get_resp (conn))) {
                safe_fclose (&fp);
                return r;
        }

        while (fgets (buf, sizeof (buf) - 1, fp)) {
                buflen = mutt_strlen (buf);
                term = buf[buflen-1] == '\n';
                if (buflen && buf[buflen-1] == '\n'
                        && (buflen == 1 || buf[buflen - 2] != '\r'))
                        snprintf (buf + buflen - 1, sizeof (buf) - buflen + 1, "\r\n");
                if (buf[0] == '.') {
                        if (mutt_socket_write_d (conn, ".", -1, M_SOCK_LOG_FULL) == -1) {
                                safe_fclose (&fp);
                                return smtp_err_write;
                        }
                }
                if (mutt_socket_write_d (conn, buf, -1, M_SOCK_LOG_FULL) == -1) {
                        safe_fclose (&fp);
                        return smtp_err_write;
                }
                mutt_progress_update (&progress, ftell (fp), -1);
        }
        if (!term && buflen &&
        mutt_socket_write_d (conn, "\r\n", -1, M_SOCK_LOG_FULL) == -1) {
                safe_fclose (&fp);
                return smtp_err_write;
        }
        safe_fclose (&fp);

/* terminate the message body */
        if (mutt_socket_write (conn, ".\r\n") == -1)
                return smtp_err_write;

        if ((r = smtp_get_resp (conn)))
                return r;

        return 0;
}
Esempio n. 10
0
/* imap_auth_gss: AUTH=GSSAPI support. */
imap_auth_res_t imap_auth_gss (IMAP_DATA* idata, const char* method)
{
  gss_buffer_desc request_buf, send_token;
  gss_buffer_t sec_token;
  gss_name_t target_name;
  gss_ctx_id_t context;
#ifdef DEBUG
  gss_OID mech_name;
#endif
  gss_qop_t quality;
  int cflags;
  OM_uint32 maj_stat, min_stat;
  char buf1[GSS_BUFSIZE], buf2[GSS_BUFSIZE], server_conf_flags;
  unsigned long buf_size;
  int rc;

  if (!mutt_bit_isset (idata->capabilities, AGSSAPI))
    return IMAP_AUTH_UNAVAIL;

  if (mutt_account_getuser (&idata->conn->account))
    return IMAP_AUTH_FAILURE;
  
  /* get an IMAP service ticket for the server */
  snprintf (buf1, sizeof (buf1), "imap@%s", idata->conn->account.host);
  request_buf.value = buf1;
  request_buf.length = strlen (buf1) + 1;
  maj_stat = gss_import_name (&min_stat, &request_buf, gss_nt_service_name,
    &target_name);
  if (maj_stat != GSS_S_COMPLETE)
  {
    dprint (2, (debugfile, "Couldn't get service name for [%s]\n", buf1));
    return IMAP_AUTH_UNAVAIL;
  }
#ifdef DEBUG	
  else if (debuglevel >= 2)
  {
    maj_stat = gss_display_name (&min_stat, target_name, &request_buf,
      &mech_name);
    dprint (2, (debugfile, "Using service name [%s]\n",
      (char*) request_buf.value));
    maj_stat = gss_release_buffer (&min_stat, &request_buf);
  }
#endif
  /* Acquire initial credentials - without a TGT GSSAPI is UNAVAIL */
  sec_token = GSS_C_NO_BUFFER;
  context = GSS_C_NO_CONTEXT;

  /* build token */
  maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
    target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, 0, 
    GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL, &send_token,
    (unsigned int*) &cflags, NULL);
  if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
  {
    print_gss_error(maj_stat, min_stat);
    dprint (1, (debugfile, "Error acquiring credentials - no TGT?\n"));
    gss_release_name (&min_stat, &target_name);

    return IMAP_AUTH_UNAVAIL;
  }

  /* now begin login */
  mutt_message _("Authenticating (GSSAPI)...");

  imap_cmd_start (idata, "AUTHENTICATE GSSAPI");

  /* expect a null continuation response ("+") */
  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);

  if (rc != IMAP_CMD_RESPOND)
  {
    dprint (2, (debugfile, "Invalid response from server: %s\n", buf1));
    gss_release_name (&min_stat, &target_name);
    goto bail;
  }

  /* now start the security context initialisation loop... */
  dprint (2, (debugfile, "Sending credentials\n"));
  mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length,
    sizeof (buf1) - 2);
  gss_release_buffer (&min_stat, &send_token);
  safe_strcat (buf1, sizeof (buf1), "\r\n");
  mutt_socket_write (idata->conn, buf1);

  while (maj_stat == GSS_S_CONTINUE_NEEDED)
  {
    /* Read server data */
    do
      rc = imap_cmd_step (idata);
    while (rc == IMAP_CMD_CONTINUE);

    if (rc != IMAP_CMD_RESPOND)
    {
      dprint (1, (debugfile, "Error receiving server response.\n"));
      gss_release_name (&min_stat, &target_name);
      goto bail;
    }

    request_buf.length = mutt_from_base64 (buf2, idata->buf + 2);
    request_buf.value = buf2;
    sec_token = &request_buf;

    /* Write client data */
    maj_stat = gss_init_sec_context (&min_stat, GSS_C_NO_CREDENTIAL, &context,
      target_name, GSS_C_NO_OID, GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG, 0, 
      GSS_C_NO_CHANNEL_BINDINGS, sec_token, NULL, &send_token,
      (unsigned int*) &cflags, NULL);
    if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED)
    {
      print_gss_error(maj_stat, min_stat);
      dprint (1, (debugfile, "Error exchanging credentials\n"));
      gss_release_name (&min_stat, &target_name);

      goto err_abort_cmd;
    }
    mutt_to_base64 ((unsigned char*) buf1, send_token.value,
      send_token.length, sizeof (buf1) - 2);
    gss_release_buffer (&min_stat, &send_token);
    safe_strcat (buf1, sizeof (buf1), "\r\n");
    mutt_socket_write (idata->conn, buf1);
  }

  gss_release_name (&min_stat, &target_name);

  /* get security flags and buffer size */
  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);

  if (rc != IMAP_CMD_RESPOND)
  {
    dprint (1, (debugfile, "Error receiving server response.\n"));
    goto bail;
  }
  request_buf.length = mutt_from_base64 (buf2, idata->buf + 2);
  request_buf.value = buf2;

  maj_stat = gss_unwrap (&min_stat, context, &request_buf, &send_token,
    &cflags, &quality);
  if (maj_stat != GSS_S_COMPLETE)
  {
    print_gss_error(maj_stat, min_stat);
    dprint (2, (debugfile, "Couldn't unwrap security level data\n"));
    gss_release_buffer (&min_stat, &send_token);
    goto err_abort_cmd;
  }
  dprint (2, (debugfile, "Credential exchange complete\n"));

  /* first octet is security levels supported. We want NONE */
  server_conf_flags = ((char*) send_token.value)[0];
  if ( !(((char*) send_token.value)[0] & GSS_AUTH_P_NONE) )
  {
    dprint (2, (debugfile, "Server requires integrity or privacy\n"));
    gss_release_buffer (&min_stat, &send_token);
    goto err_abort_cmd;
  }

  /* we don't care about buffer size if we don't wrap content. But here it is */
  ((char*) send_token.value)[0] = 0;
  buf_size = ntohl (*((long *) send_token.value));
  gss_release_buffer (&min_stat, &send_token);
  dprint (2, (debugfile, "Unwrapped security level flags: %c%c%c\n",
    server_conf_flags & GSS_AUTH_P_NONE      ? 'N' : '-',
    server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
    server_conf_flags & GSS_AUTH_P_PRIVACY   ? 'P' : '-'));
  dprint (2, (debugfile, "Maximum GSS token size is %ld\n", buf_size));

  /* agree to terms (hack!) */
  buf_size = htonl (buf_size); /* not relevant without integrity/privacy */
  memcpy (buf1, &buf_size, 4);
  buf1[0] = GSS_AUTH_P_NONE;
  /* server decides if principal can log in as user */
  strncpy (buf1 + 4, idata->conn->account.user, sizeof (buf1) - 4);
  request_buf.value = buf1;
  request_buf.length = 4 + strlen (idata->conn->account.user) + 1;
  maj_stat = gss_wrap (&min_stat, context, 0, GSS_C_QOP_DEFAULT, &request_buf,
    &cflags, &send_token);
  if (maj_stat != GSS_S_COMPLETE)
  {
    dprint (2, (debugfile, "Error creating login request\n"));
    goto err_abort_cmd;
  }

  mutt_to_base64 ((unsigned char*) buf1, send_token.value, send_token.length,
		  sizeof (buf1) - 2);
  dprint (2, (debugfile, "Requesting authorisation as %s\n",
    idata->conn->account.user));
  safe_strcat (buf1, sizeof (buf1), "\r\n");
  mutt_socket_write (idata->conn, buf1);

  /* Joy of victory or agony of defeat? */
  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);
  if (rc == IMAP_CMD_RESPOND)
  {
    dprint (1, (debugfile, "Unexpected server continuation request.\n"));
    goto err_abort_cmd;
  }
  if (imap_code (idata->buf))
  {
    /* flush the security context */
    dprint (2, (debugfile, "Releasing GSS credentials\n"));
    maj_stat = gss_delete_sec_context (&min_stat, &context, &send_token);
    if (maj_stat != GSS_S_COMPLETE)
      dprint (1, (debugfile, "Error releasing credentials\n"));

    /* send_token may contain a notification to the server to flush
     * credentials. RFC 1731 doesn't specify what to do, and since this
     * support is only for authentication, we'll assume the server knows
     * enough to flush its own credentials */
    gss_release_buffer (&min_stat, &send_token);

    return IMAP_AUTH_SUCCESS;
  }
  else
    goto bail;

 err_abort_cmd:
  mutt_socket_write (idata->conn, "*\r\n");
  do
    rc = imap_cmd_step (idata);
  while (rc == IMAP_CMD_CONTINUE);

 bail:
  mutt_error _("GSSAPI authentication failed.");
  mutt_sleep (2);
  return IMAP_AUTH_FAILURE;
}