Skip to content
pop3.c 46.3 KiB
Newer Older
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 * RFC1734 POP3 Authentication
 * RFC2195 CRAM-MD5 authentication
 * RFC2449 POP3 Extension Mechanism
 * RFC2595 Using TLS with IMAP, POP3 and ACAP
 * RFC2831 DIGEST-MD5 authentication
 * RFC4616 PLAIN authentication
 *
 ***************************************************************************/

#include "setup.h"

#ifndef CURL_DISABLE_POP3

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_UTSNAME_H
#include <sys/utsname.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#include <in.h>
#include <inet.h>
#endif

#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
#undef in_addr_t
#define in_addr_t unsigned long
#endif

#include <curl/curl.h>
#include "urldata.h"
#include "sendf.h"
#include "if2ip.h"
#include "hostip.h"
#include "progress.h"
#include "transfer.h"
#include "escape.h"
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h"
#include "pop3.h"

#include "strtoofft.h"
#include "strequal.h"
#include "sslgen.h"
#include "connect.h"
#include "strerror.h"
#include "select.h"
#include "multiif.h"
#include "url.h"
#include "rawstr.h"

#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>

#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"

/* Local API functions */
static CURLcode pop3_parse_url_path(struct connectdata *conn);
static CURLcode pop3_parse_custom_request(struct connectdata *conn);
static CURLcode pop3_regular_transfer(struct connectdata *conn, bool *done);
static CURLcode pop3_do(struct connectdata *conn, bool *done);
static CURLcode pop3_done(struct connectdata *conn,
                          CURLcode, bool premature);
static CURLcode pop3_connect(struct connectdata *conn, bool *done);
static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
static int pop3_getsock(struct connectdata *conn,
                        curl_socket_t *socks,
                        int numsocks);
static CURLcode pop3_doing(struct connectdata *conn,
                           bool *dophase_done);
static CURLcode pop3_setup_connection(struct connectdata * conn);

/*
 * POP3 protocol handler.
 */

const struct Curl_handler Curl_handler_pop3 = {
  "POP3",                           /* scheme */
  pop3_setup_connection,            /* setup_connection */
  pop3_do,                          /* do_it */
  pop3_done,                        /* done */
  ZERO_NULL,                        /* do_more */
  pop3_connect,                     /* connect_it */
  pop3_multi_statemach,             /* connecting */
  pop3_doing,                       /* doing */
  pop3_getsock,                     /* proto_getsock */
  pop3_getsock,                     /* doing_getsock */
  ZERO_NULL,                        /* domore_getsock */
  ZERO_NULL,                        /* perform_getsock */
  pop3_disconnect,                  /* disconnect */
  ZERO_NULL,                        /* readwrite */
  CURLPROTO_POP3,                   /* protocol */
  PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
};

#ifdef USE_SSL
/*
 * POP3S protocol handler.
 */

const struct Curl_handler Curl_handler_pop3s = {
  "POP3S",                          /* scheme */
  pop3_setup_connection,            /* setup_connection */
  pop3_do,                          /* do_it */
  pop3_done,                        /* done */
  ZERO_NULL,                        /* do_more */
  pop3_connect,                     /* connect_it */
  pop3_multi_statemach,             /* connecting */
  pop3_doing,                       /* doing */
  pop3_getsock,                     /* proto_getsock */
  pop3_getsock,                     /* doing_getsock */
  ZERO_NULL,                        /* domore_getsock */
  ZERO_NULL,                        /* perform_getsock */
  pop3_disconnect,                  /* disconnect */
  ZERO_NULL,                        /* readwrite */
  CURLPROTO_POP3 | CURLPROTO_POP3S, /* protocol */
  PROTOPT_CLOSEACTION | PROTOPT_SSL
  | PROTOPT_NOURLQUERY              /* flags */
};
#endif

#ifndef CURL_DISABLE_HTTP
/*
 * HTTP-proxyed POP3 protocol handler.
 */

static const struct Curl_handler Curl_handler_pop3_proxy = {
  "POP3",                               /* scheme */
  ZERO_NULL,                            /* setup_connection */
  Curl_http,                            /* do_it */
  Curl_http_done,                       /* done */
  ZERO_NULL,                            /* do_more */
  ZERO_NULL,                            /* connect_it */
  ZERO_NULL,                            /* connecting */
  ZERO_NULL,                            /* doing */
  ZERO_NULL,                            /* proto_getsock */
  ZERO_NULL,                            /* doing_getsock */
  ZERO_NULL,                            /* domore_getsock */
  ZERO_NULL,                            /* perform_getsock */
  ZERO_NULL,                            /* disconnect */
  ZERO_NULL,                            /* readwrite */
  CURLPROTO_HTTP,                       /* protocol */
  PROTOPT_NONE                          /* flags */
};

#ifdef USE_SSL
/*
 * HTTP-proxyed POP3S protocol handler.
 */

static const struct Curl_handler Curl_handler_pop3s_proxy = {
  "POP3S",                              /* scheme */
  ZERO_NULL,                            /* setup_connection */
  Curl_http,                            /* do_it */
  Curl_http_done,                       /* done */
  ZERO_NULL,                            /* do_more */
  ZERO_NULL,                            /* connect_it */
  ZERO_NULL,                            /* connecting */
  ZERO_NULL,                            /* doing */
  ZERO_NULL,                            /* proto_getsock */
  ZERO_NULL,                            /* doing_getsock */
  ZERO_NULL,                            /* domore_getsock */
  ZERO_NULL,                            /* perform_getsock */
  ZERO_NULL,                            /* disconnect */
  ZERO_NULL,                            /* readwrite */
  CURLPROTO_HTTP,                       /* protocol */
  PROTOPT_NONE                          /* flags */
/* Function that checks for an ending pop3 status code at the start of the
   given string, but also detects the supported authentication types as well
   as the allowed SASL authentication mechanisms within the CAPA response. */
static int pop3_endofresp(struct pingpong *pp, int *resp)
  size_t len = strlen(pp->linestart_resp);
  struct connectdata *conn = pp->conn;
  struct pop3_conn *pop3c = &conn->proto.pop3c;
  size_t wordlen;
  /* Do we have an error response? */
  if(len >= 4 && !memcmp("-ERR", line, 4)) {
    *resp = '-';
  /* Are we processing reponses to our CAPA command? */
  if(pop3c->state == POP3_CAPA) {
    /* Do we have the terminating character? */
    if(len >= 1 && !memcmp(line, ".", 1)) {
      *resp = '+';

      return TRUE;
    }

    /* Does the server support clear text? */
    if(len >= 4 && !memcmp(line, "USER", 4)) {
      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
    /* Does the server support APOP? */
    if(len >= 4 && !memcmp(line, "APOP", 4)) {
      pop3c->authtypes |= POP3_TYPE_APOP;
      return FALSE;
    }

    /* Does the server support SASL? */
    if(len < 4 || memcmp(line, "SASL", 4))
      return FALSE;

    pop3c->authtypes |= POP3_TYPE_SASL;

    /* Advance past the SASL keyword */
    line += 4;
    len -= 4;

    /* Loop through the data line */
    for(;;) {
      while(len &&
            (*line == ' ' || *line == '\t' ||
             *line == '\r' || *line == '\n')) {
      for(wordlen = 0; wordlen < len && line[wordlen] != ' ' &&
            line[wordlen] != '\t' && line[wordlen] != '\r' &&
            line[wordlen] != '\n';)
        wordlen++;

      /* Test the word for a matching authentication mechanism */
      if(wordlen == 5 && !memcmp(line, "LOGIN", 5))
        pop3c->authmechs |= SASL_MECH_LOGIN;
      else if(wordlen == 5 && !memcmp(line, "PLAIN", 5))
        pop3c->authmechs |= SASL_MECH_PLAIN;
      else if(wordlen == 8 && !memcmp(line, "CRAM-MD5", 8))
        pop3c->authmechs |= SASL_MECH_CRAM_MD5;
      else if(wordlen == 10 && !memcmp(line, "DIGEST-MD5", 10))
        pop3c->authmechs |= SASL_MECH_DIGEST_MD5;
      else if(wordlen == 6 && !memcmp(line, "GSSAPI", 6))
        pop3c->authmechs |= SASL_MECH_GSSAPI;
      else if(wordlen == 8 && !memcmp(line, "EXTERNAL", 8))
        pop3c->authmechs |= SASL_MECH_EXTERNAL;
      else if(wordlen == 4 && !memcmp(line, "NTLM", 4))
        pop3c->authmechs |= SASL_MECH_NTLM;
  if((len < 1 || memcmp("+", line, 1)) &&
     (len < 3 || memcmp("+OK", line, 3)))
  return FALSE; /* Nothing for us */

  /* Otherwise it's a positive response */
  *resp = '+';

}

/* This is the ONLY way to change POP3 state! */
static void state(struct connectdata *conn, pop3state newstate)
{
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
  /* for debug purposes */
  static const char * const names[] = {
    "AUTH_LOGIN",
    "AUTH_LOGIN_PASSWD",
    "AUTH_DIGESTMD5",
    "AUTH_DIGESTMD5_RESP",
    "AUTH_NTLM",
    "AUTH_NTLM_TYPE2MSG",
    "QUIT",
    /* LAST */
  };
#endif
  struct pop3_conn *pop3c = &conn->proto.pop3c;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
  if(pop3c->state != newstate)
    infof(conn->data, "POP3 %p state change from %s to %s\n",
          pop3c, names[pop3c->state], names[newstate]);
#endif
  pop3c->state = newstate;
}

static CURLcode pop3_state_capa(struct connectdata *conn)
  struct pop3_conn *pop3c = &conn->proto.pop3c;

  pop3c->authmechs = 0;         /* No known authentication mechanisms yet */
  pop3c->authused = 0;          /* Clear the authentication mechanism used */
  /* Check we have a username and password to authenticate with and end the
     connect phase if we don't */
  if(!conn->bits.user_passwd) {
    state(conn, POP3_STOP);
  /* Send the CAPA command */
  result = Curl_pp_sendf(&pop3c->pp, "CAPA");
static CURLcode pop3_state_user(struct connectdata *conn)
{
  CURLcode result;
  struct FTP *pop3 = conn->data->state.proto.pop3;

  /* Send the USER command */
  result = Curl_pp_sendf(&conn->proto.pop3c.pp, "USER %s",
Steve Holme's avatar
Steve Holme committed
                         pop3->user ? pop3->user : "");
  if(result)
    return result;

  state(conn, POP3_USER);

  return CURLE_OK;
}

static CURLcode pop3_authenticate(struct connectdata *conn)
{
  CURLcode result = CURLE_OK;
  struct pop3_conn *pop3c = &conn->proto.pop3c;
  const char *mech = NULL;
  pop3state authstate = POP3_STOP;

  /* Check supported authentication mechanisms by decreasing order of
     security */
#ifndef CURL_DISABLE_CRYPTO_AUTH
  if(pop3c->authmechs & SASL_MECH_DIGEST_MD5) {
    mech = "DIGEST-MD5";
    authstate = POP3_AUTH_DIGESTMD5;
    pop3c->authused = SASL_MECH_DIGEST_MD5;
  else if(pop3c->authmechs & SASL_MECH_CRAM_MD5) {
    mech = "CRAM-MD5";
    authstate = POP3_AUTH_CRAMMD5;
    pop3c->authused = SASL_MECH_CRAM_MD5;
  if(pop3c->authmechs & SASL_MECH_NTLM) {
    mech = "NTLM";
    authstate = POP3_AUTH_NTLM;
    pop3c->authused = SASL_MECH_NTLM;
  if(pop3c->authmechs & SASL_MECH_LOGIN) {
    mech = "LOGIN";
    authstate = POP3_AUTH_LOGIN;
    pop3c->authused = SASL_MECH_LOGIN;
  else if(pop3c->authmechs & SASL_MECH_PLAIN) {
    mech = "PLAIN";
    authstate = POP3_AUTH_PLAIN;
    pop3c->authused = SASL_MECH_PLAIN;
    infof(conn->data, "No known SASL authentication mechanisms supported!\n");
Steve Holme's avatar
Steve Holme committed
    result = CURLE_LOGIN_DENIED; /* Other mechanisms not supported */
  }

  if(!result) {
    result = Curl_pp_sendf(&pop3c->pp, "AUTH %s", mech);

    if(!result)
      state(conn, authstate);
  }

  return result;
}

/* For the POP3 "protocol connect" and "doing" phases only */
static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
                        int numsocks)
{
  return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
}

#ifdef USE_SSL
static void pop3_to_pop3s(struct connectdata *conn)
{
  conn->handler = &Curl_handler_pop3s;
}
#else
#define pop3_to_pop3s(x) Curl_nop_stmt
/* For the initial server greeting */
static CURLcode pop3_state_servergreet_resp(struct connectdata *conn,
                                            int pop3code,
                                            pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct pop3_conn *pop3c = &conn->proto.pop3c;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Got unexpected pop3-server response");
    return CURLE_FTP_WEIRD_SERVER_REPLY;
  }

  if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
    /* We don't have a SSL/TLS connection yet, but SSL is requested. Switch
       to TLS connection now */
    result = Curl_pp_sendf(&pop3c->pp, "STLS");
    state(conn, POP3_STARTTLS);
  }
  else
    result = pop3_state_capa(conn);
static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
                                         int pop3code,
                                         pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  if(pop3code != '+') {
    if(data->set.use_ssl != CURLUSESSL_TRY) {
      failf(data, "STARTTLS denied. %c", pop3code);
      result = CURLE_USE_SSL_FAILED;
      state(conn, POP3_STOP);
    }
    else
      result = pop3_state_capa(conn);
  }
  else {
    /* Curl_ssl_connect is BLOCKING */
    result = Curl_ssl_connect(conn, FIRSTSOCKET);
    if(CURLE_OK == result) {
      pop3_to_pop3s(conn);
      result = pop3_state_capa(conn);
      /* End of connect phase */
/* For CAPA responses */
static CURLcode pop3_state_capa_resp(struct connectdata *conn,
                                     int pop3code,
                                     pop3state instate)
  (void)instate; /* no use for this yet */

  if(pop3code != '+')
    result = pop3_state_user(conn);
  else {
    /* Check supported authentication types by decreasing order of security */
    if(conn->proto.pop3c.authtypes & POP3_TYPE_SASL)
      result = pop3_authenticate(conn);
    else if(conn->proto.pop3c.authtypes & POP3_TYPE_CLEARTEXT)
      result = pop3_state_user(conn);
    else {
      infof(conn->data, "No known authentication types supported!\n");
      result = CURLE_LOGIN_DENIED; /* Other types not supported */
    }
  }

  return result;
}

/* For AUTH PLAIN responses */
static CURLcode pop3_state_auth_plain_resp(struct connectdata *conn,
                                           int pop3code,
                                           pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  size_t len = 0;
  char *plainauth = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied. %c", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
  else {
    /* Create the authorisation message */
    result = Curl_sasl_create_plain_message(data, conn->user, conn->passwd,
                                            &plainauth, &len);
    /* Send the message */
    if(!result) {
      if(plainauth) {
        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", plainauth);

        if(!result)
          state(conn, POP3_AUTH);
/* For AUTH LOGIN responses */
static CURLcode pop3_state_auth_login_resp(struct connectdata *conn,
                                           int pop3code,
                                           pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  size_t len = 0;
  char *authuser = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied: %d", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
  else {
    /* Create the user message */
    result = Curl_sasl_create_login_message(data, conn->user,
    /* Send the user */
    if(!result) {
      if(authuser) {
        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authuser);

        if(!result)
          state(conn, POP3_AUTH_LOGIN_PASSWD);
      }
      Curl_safefree(authuser);
    }
  }

  return result;
}

/* For AUTH LOGIN user entry responses */
static CURLcode pop3_state_auth_login_password_resp(struct connectdata *conn,
                                                    int pop3code,
                                                    pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  size_t len = 0;
  char *authpasswd = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied: %d", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
  else {
    /* Create the password message */
    result = Curl_sasl_create_login_message(data, conn->passwd,
    /* Send the password */
    if(!result) {
      if(authpasswd) {
        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", authpasswd);

        if(!result)
          state(conn, POP3_AUTH);
      }
      Curl_safefree(authpasswd);
    }
  }

  return result;
}

#ifndef CURL_DISABLE_CRYPTO_AUTH
/* For AUTH CRAM-MD5 responses */
static CURLcode pop3_state_auth_cram_resp(struct connectdata *conn,
                                          int pop3code,
                                          pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  char *chlg64 = data->state.buffer;
  size_t len = 0;
  char *rplyb64 = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied: %d", pop3code);
    return CURLE_LOGIN_DENIED;
  }

  /* Get the challenge */
  for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
    ;

  /* Terminate the challenge */
  if(*chlg64 != '=') {
    for(len = strlen(chlg64); len--;)
      if(chlg64[len] != '\r' && chlg64[len] != '\n' && chlg64[len] != ' ' &&
         chlg64[len] != '\t')
        break;

    if(++len) {
      chlg64[len] = '\0';
    }
  }

  /* Create the response message */
  result = Curl_sasl_create_cram_md5_message(data, chlg64, conn->user,
                                             conn->passwd, &rplyb64, &len);

  /* Send the response */
  if(!result) {
    if(rplyb64) {
      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);

      if(!result)
        state(conn, POP3_AUTH);
    }
    Curl_safefree(rplyb64);
  }

  return result;
}

/* For AUTH DIGEST-MD5 challenge responses */
static CURLcode pop3_state_auth_digest_resp(struct connectdata *conn,
                                            int pop3code,
                                            pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  char *chlg64 = data->state.buffer;
  size_t len = 0;
  char *rplyb64 = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied: %d", pop3code);
    return CURLE_LOGIN_DENIED;
  }

  /* Get the challenge */
  for(chlg64 += 2; *chlg64 == ' ' || *chlg64 == '\t'; chlg64++)
    ;

  /* Create the response message */
  result = Curl_sasl_create_digest_md5_message(data, chlg64, conn->user,
                                               conn->passwd, "pop",
                                               &rplyb64, &len);

  /* Send the response */
  if(!result) {
    if(rplyb64) {
      result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", rplyb64);

      if(!result)
        state(conn, POP3_AUTH_DIGESTMD5_RESP);
    }

    Curl_safefree(rplyb64);
  }

  return result;
}

/* For AUTH DIGEST-MD5 challenge-response responses */
static CURLcode pop3_state_auth_digest_resp_resp(struct connectdata *conn,
                                                 int pop3code,
                                                 pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Authentication failed: %d", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
  else {
    /* Send an empty response */
    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "");

    if(!result)
      state(conn, POP3_AUTH);
#ifdef USE_NTLM
/* For AUTH NTLM responses */
static CURLcode pop3_state_auth_ntlm_resp(struct connectdata *conn,
                                          int pop3code,
                                          pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  size_t len = 0;
  char *type1msg = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied: %d", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
  else {
    /* Create the type-1 message */
    result = Curl_sasl_create_ntlm_type1_message(conn->user, conn->passwd,
                                                 &conn->ntlm,
                                                 &type1msg, &len);
    /* Send the message */
    if(!result) {
      if(type1msg) {
        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type1msg);

        if(!result)
          state(conn, POP3_AUTH_NTLM_TYPE2MSG);
      }

      Curl_safefree(type1msg);
    }
  }

  return result;
}

/* For NTLM type-2 responses (sent in reponse to our type-1 message) */
static CURLcode pop3_state_auth_ntlm_type2msg_resp(struct connectdata *conn,
                                                   int pop3code,
                                                   pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  size_t len = 0;
  char *type3msg = NULL;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied: %d", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
  else {
    /* Create the type-3 message */
    result = Curl_sasl_create_ntlm_type3_message(data,
                                                 data->state.buffer + 2,
                                                 conn->user, conn->passwd,
                                                 &conn->ntlm,
                                                 &type3msg, &len);
    /* Send the message */
    if(!result) {
      if(type3msg) {
        result = Curl_pp_sendf(&conn->proto.pop3c.pp, "%s", type3msg);

        if(!result)
          state(conn, POP3_AUTH);
/* For final responses to the AUTH sequence */
static CURLcode pop3_state_auth_final_resp(struct connectdata *conn,
                                           int pop3code,
                                           pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Authentication failed: %d", pop3code);
    result = CURLE_LOGIN_DENIED;
  }

Steve Holme's avatar
Steve Holme committed
  /* End of connect phase */
  state(conn, POP3_STOP);
static CURLcode pop3_state_user_resp(struct connectdata *conn,
                                     int pop3code,
                                     pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct FTP *pop3 = data->state.proto.pop3;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    failf(data, "Access denied. %c", pop3code);
    result = CURLE_LOGIN_DENIED;
  }
    /* Send the PASS command */
    result = Curl_pp_sendf(&conn->proto.pop3c.pp, "PASS %s",
Steve Holme's avatar
Steve Holme committed
                           pop3->passwd ? pop3->passwd : "");
  if(result)
    return result;

  state(conn, POP3_PASS);
static CURLcode pop3_state_pass_resp(struct connectdata *conn,
                                     int pop3code,
                                     pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  if(pop3code != '+') {
    failf(data, "Access denied. %c", pop3code);
    result = CURLE_LOGIN_DENIED;
  }

Steve Holme's avatar
Steve Holme committed
  /* End of connect phase */
  state(conn, POP3_STOP);
/* For command responses */
static CURLcode pop3_state_command_resp(struct connectdata *conn,
                                        int pop3code,
                                        pop3state instate)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  struct FTP *pop3 = data->state.proto.pop3;
  struct pop3_conn *pop3c = &conn->proto.pop3c;
  struct pingpong *pp = &pop3c->pp;

  (void)instate; /* no use for this yet */

  if(pop3code != '+') {
    state(conn, POP3_STOP);
    return CURLE_RECV_ERROR;
  }

  /* This 'OK' line ends with a CR LF pair which is the two first bytes of the
     EOB string so count this is two matching bytes. This is necessary to make
     the code detect the EOB if the only data than comes now is %2e CR LF like
     when there is no body to return. */
  pop3c->eob = 2;

  /* But since this initial CR LF pair is not part of the actual body, we set
     the strip counter here so that these bytes won't be delivered. */
  pop3c->strip = 2;

  /* POP3 download */
  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, pop3->bytecountp,
                      -1, NULL); /* no upload here */
    /* The header "cache" contains a bunch of data that is actually body
       content so send it as such. Note that there may even be additional
       "headers" after the body */
    if(!data->set.opt_no_body) {
      result = Curl_pop3_write(conn, pp->cache, pp->cache_size);
      if(result)
        return result;
    }
    /* Free the cache */
    Curl_safefree(pp->cache);

    /* Reset the cache size */
Steve Holme's avatar
Steve Holme committed
  /* End of do phase */
  state(conn, POP3_STOP);
/* Start the DO phase for the command */
static CURLcode pop3_command(struct connectdata *conn)
{
  CURLcode result = CURLE_OK;
  struct pop3_conn *pop3c = &conn->proto.pop3c;
  const char *command = NULL;
  /* Calculate the default command */
  if(pop3c->mailbox[0] == '\0' || conn->data->set.ftp_list_only) {
    command = "LIST";