static ngx_int_t
ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_str_t  *arg;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    if (s->args.nelts != 1) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }

    arg = s->args.elts;
    s->login.len = arg[0].len;
    s->login.data = ngx_pnalloc(c->pool, s->login.len);
    if (s->login.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
                   "pop3 login: \"%V\"", &s->login);

    s->mail_state = ngx_pop3_user;

    return NGX_OK;
}
static ngx_int_t
ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_int_t                  rc;
    ngx_mail_core_srv_conf_t  *cscf;
    ngx_mail_imap_srv_conf_t  *iscf;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    rc = ngx_mail_auth_parse(s, c);

    switch (rc) {

    case NGX_MAIL_AUTH_LOGIN:

        s->out.len = sizeof(imap_username) - 1;
        s->out.data = imap_username;
        s->mail_state = ngx_imap_auth_login_username;

        return NGX_OK;

    case NGX_MAIL_AUTH_PLAIN:

        s->out.len = sizeof(imap_plain_next) - 1;
        s->out.data = imap_plain_next;
        s->mail_state = ngx_imap_auth_plain;

        return NGX_OK;

    case NGX_MAIL_AUTH_CRAM_MD5:

        iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);

        if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_COMMAND;
        }

        if (s->salt.data == NULL) {
            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

            if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
                return NGX_ERROR;
            }
        }

        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
            s->mail_state = ngx_imap_auth_cram_md5;
            return NGX_OK;
        }

        return NGX_ERROR;
    }

    return rc;
}
static ngx_int_t
ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_int_t                  rc;
    ngx_mail_core_srv_conf_t  *cscf;
    ngx_mail_smtp_srv_conf_t  *sscf;
#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c))
    {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif
    if (s->args.nelts == 0)
    {
        ngx_str_set(&s->out, smtp_invalid_argument);
        s->state = 0;
        return NGX_OK;
    }
    rc = ngx_mail_auth_parse(s, c);
    switch (rc)
    {
    case NGX_MAIL_AUTH_LOGIN:
        ngx_str_set(&s->out, smtp_username);
        s->mail_state = ngx_smtp_auth_login_username;
        return NGX_OK;
    case NGX_MAIL_AUTH_LOGIN_USERNAME:
        ngx_str_set(&s->out, smtp_password);
        s->mail_state = ngx_smtp_auth_login_password;
        return ngx_mail_auth_login_username(s, c, 1);
    case NGX_MAIL_AUTH_PLAIN:
        ngx_str_set(&s->out, smtp_next);
        s->mail_state = ngx_smtp_auth_plain;
        return NGX_OK;
    case NGX_MAIL_AUTH_CRAM_MD5:
        sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
        if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED))
        {
            return NGX_MAIL_PARSE_INVALID_COMMAND;
        }
        if (s->salt.data == NULL)
        {
            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
            if (ngx_mail_salt(s, c, cscf) != NGX_OK)
            {
                return NGX_ERROR;
            }
        }
        if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK)
        {
            s->mail_state = ngx_smtp_auth_cram_md5;
            return NGX_OK;
        }
        return NGX_ERROR;
    }
    return rc;
}
ngx_int_t
ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_str_t                 *arg;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    arg = s->args.elts;

    if (arg[0].len == 5) {

        if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {

            if (s->args.nelts == 1) {
                return NGX_MAIL_AUTH_LOGIN;
            }

            if (s->args.nelts == 2) {
                return NGX_MAIL_AUTH_LOGIN_USERNAME;
            }

            return NGX_MAIL_PARSE_INVALID_COMMAND;
        }

        if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {

            if (s->args.nelts == 1) {
                return NGX_MAIL_AUTH_PLAIN;
            }

            if (s->args.nelts == 2) {
                return ngx_mail_auth_plain(s, c, 1);
            }
        }

        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }

    if (arg[0].len == 8) {

        if (s->args.nelts != 1) {
            return NGX_MAIL_PARSE_INVALID_COMMAND;
        }

        if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
            return NGX_MAIL_AUTH_CRAM_MD5;
        }
    }

    return NGX_MAIL_PARSE_INVALID_COMMAND;
}
static ngx_int_t
ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_str_t  *arg;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        ngx_str_set(&s->text, imap_nocleartext);
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    arg = s->args.elts;

    if (s->args.nelts != 2 || arg[0].len == 0) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }

    if (arg[0].len > NGX_MAIL_MAX_LOGIN_LEN) {
        return NGX_MAIL_LOGIN_FAILED;
    }
    s->login.len = arg[0].len;
    s->login.data = ngx_pnalloc(c->pool, s->login.len);
    if (s->login.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    if (arg[1].len > NGX_MAIL_MAX_PASSWORD_LEN) {
        return NGX_MAIL_LOGIN_FAILED;
    }
    s->passwd.len = arg[1].len;
    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
    if (s->passwd.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);

#if (NGX_DEBUG_MAIL_PASSWD)
    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
                   "imap login:\"%V\" passwd:\"%V\"",
                   &s->login, &s->passwd);
#else
    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
                   "imap login:\"%V\"", &s->login);
#endif

    s->auth_method = NGX_MAIL_AUTH_PASSWD;
    s->usedauth = 0;
    return NGX_DONE;
}
static ngx_int_t
ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_str_t                 *arg;
    ngx_mail_pop3_srv_conf_t  *pscf;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    if (s->args.nelts != 2) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }

    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

    if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }

    arg = s->args.elts;

    s->login.len = arg[0].len;
    s->login.data = ngx_pnalloc(c->pool, s->login.len);
    if (s->login.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    s->passwd.len = arg[1].len;
    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
    if (s->passwd.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);

    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
                   "pop3 apop: \"%V\" \"%V\"", &s->login, &s->passwd);

    s->auth_method = NGX_MAIL_AUTH_APOP;

    return NGX_DONE;
}
static ngx_int_t
ngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_str_t  *arg;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    arg = s->args.elts;

    if (s->args.nelts != 2 || arg[0].len == 0) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }

    s->login.len = arg[0].len;
    s->login.data = ngx_pnalloc(c->pool, s->login.len);
    if (s->login.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->login.data, arg[0].data, s->login.len);

    s->passwd.len = arg[1].len;
    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);
    if (s->passwd.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);

#if (NGX_DEBUG_MAIL_PASSWD)
    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
                   "imap login:\"%V\" passwd:\"%V\"",
                   &s->login, &s->passwd);
#else
    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
                   "imap login:\"%V\"", &s->login);
#endif

    return NGX_DONE;
}
static ngx_int_t
ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_int_t                  rc;
    ngx_mail_pop3_srv_conf_t  *pscf;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);

    if (s->args.nelts == 0) {
        s->out = pscf->auth_capability;
        s->state = 0;

        return NGX_OK;
    }

    rc = ngx_mail_auth_parse(s, c);

    switch (rc) {

    case NGX_MAIL_AUTH_LOGIN:

        ngx_str_set(&s->out, pop3_username);
        s->mail_state = ngx_pop3_auth_login_username;

        return NGX_OK;

    case NGX_MAIL_AUTH_LOGIN_USERNAME:

        ngx_str_set(&s->out, pop3_password);
        s->mail_state = ngx_pop3_auth_login_password;

        return ngx_mail_auth_login_username(s, c, 1);

    case NGX_MAIL_AUTH_PLAIN:

        ngx_str_set(&s->out, pop3_next);
        s->mail_state = ngx_pop3_auth_plain;

        return NGX_OK;

    case NGX_MAIL_AUTH_CRAM_MD5:

        if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_COMMAND;
        }

        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
            s->mail_state = ngx_pop3_auth_cram_md5;
            return NGX_OK;
        }

        return NGX_ERROR;
    }

    return rc;
}
static ngx_int_t
ngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)
{
    ngx_int_t                  rc, res;
    ngx_mail_core_srv_conf_t  *cscf;
    ngx_mail_imap_srv_conf_t  *iscf;

#if (NGX_MAIL_SSL)
    if (ngx_mail_starttls_only(s, c)) {
        return NGX_MAIL_PARSE_INVALID_COMMAND;
    }
#endif

    rc = ngx_mail_auth_parse(s, c);
    iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);

    switch (rc) {

    case NGX_MAIL_AUTH_LOGIN:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }
        ngx_str_set(&s->out, imap_username);
        s->mail_state = ngx_imap_auth_login_username;

        return NGX_MAIL_AUTH_ARGUMENT;

    case NGX_MAIL_AUTH_LOGIN_USERNAME:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_LOGIN_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }

        res = ngx_mail_auth_login_username(s, c, 1);
        if (res == NGX_MAIL_AUTH_ARGUMENT) {
            ngx_str_set(&s->out, imap_password);
            s->mail_state = ngx_imap_auth_login_password;
            return NGX_MAIL_AUTH_ARGUMENT;
        } else {
            return res;
        }

    case NGX_MAIL_AUTH_PLAIN:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }
        ngx_str_set(&s->out, imap_plain_next);
        s->mail_state = ngx_imap_auth_plain;

        return NGX_MAIL_AUTH_ARGUMENT;

    case NGX_MAIL_AUTH_PLAIN_IR:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }
        return ngx_mail_auth_plain(s, c, 1);

    case NGX_MAIL_AUTH_GSSAPI:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }
        ngx_str_set(&s->out, imap_gssapi_next);
        s->mail_state = ngx_imap_auth_gssapi;

        return NGX_MAIL_AUTH_ARGUMENT;

    case NGX_MAIL_AUTH_GSSAPI_IR:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_GSSAPI_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }
        s->mail_state = ngx_imap_auth_gssapi;
        ngx_str_t output;
        ngx_str_set(&output, "");
        res = ngx_mail_auth_gssapi(s, c, &output);
        if(res == NGX_MAIL_AUTH_ARGUMENT) {
            s->out = output;
            return NGX_MAIL_AUTH_ARGUMENT;
        } else {
            return res;
        }

    case NGX_MAIL_AUTH_CRAM_MD5:
        if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {
            return NGX_MAIL_PARSE_INVALID_AUTH_MECH;
        }

        if (s->salt.data == NULL) {
            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);

            if (ngx_mail_salt(s, c, cscf) != NGX_OK) {
                return NGX_ERROR;
            }
        }

        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
            s->mail_state = ngx_imap_auth_cram_md5;
            return NGX_MAIL_AUTH_ARGUMENT;
        }

        return NGX_ERROR;
    }

    return rc;
}