static void
ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,
    ngx_mail_auth_http_ctx_t *ctx)
{
    u_char              *p;
    time_t               timer;
    size_t               len, size;
    ngx_int_t            rc, port, n;
    ngx_peer_addr_t     *peer;
    struct sockaddr_in  *sin;

    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
                   "mail auth http process headers");

    for ( ;; ) {
        rc = ngx_mail_auth_http_parse_header_line(s, ctx);

        if (rc == NGX_OK) {

#if (NGX_DEBUG)
            {
            ngx_str_t  key, value;

            key.len = ctx->header_name_end - ctx->header_name_start;
            key.data = ctx->header_name_start;
            value.len = ctx->header_end - ctx->header_start;
            value.data = ctx->header_start;

            ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
                           "mail auth http header: \"%V: %V\"",
                           &key, &value);
            }
#endif

            len = ctx->header_name_end - ctx->header_name_start;

            if (len == sizeof("Auth-Status") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-Status",
                                   sizeof("Auth-Status") - 1)
                   == 0)
            {
                len = ctx->header_end - ctx->header_start;

                if (len == 2
                    && ctx->header_start[0] == 'O'
                    && ctx->header_start[1] == 'K')
                {
                    continue;
                }

                if (len == 4
                    && ctx->header_start[0] == 'W'
                    && ctx->header_start[1] == 'A'
                    && ctx->header_start[2] == 'I'
                    && ctx->header_start[3] == 'T')
                {
                    s->auth_wait = 1;
                    continue;
                }

                ctx->errmsg.len = len;
                ctx->errmsg.data = ctx->header_start;

                switch (s->protocol) {

                case NGX_MAIL_POP3_PROTOCOL:
                    size = sizeof("-ERR ") - 1 + len + sizeof(CRLF) - 1;
                    break;

                case NGX_MAIL_IMAP_PROTOCOL:
                    size = s->tag.len + sizeof("NO ") - 1 + len
                           + sizeof(CRLF) - 1;
                    break;

                default: /* NGX_MAIL_SMTP_PROTOCOL */
                    ctx->err = ctx->errmsg;
                    continue;
                }

                p = ngx_pcalloc(s->connection->pool, size);
                if (p == NULL) {
                    ngx_close_connection(ctx->peer.connection);
                    ngx_destroy_pool(ctx->pool);
                    ngx_mail_session_internal_server_error(s);
                    return;
                }

                ctx->err.data = p;

                switch (s->protocol) {

                case NGX_MAIL_POP3_PROTOCOL:
                    *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
                    break;

                case NGX_MAIL_IMAP_PROTOCOL:
                    p = ngx_cpymem(p, s->tag.data, s->tag.len);
                    *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
                    break;

                default: /* NGX_MAIL_SMTP_PROTOCOL */
                    break;
                }

                p = ngx_cpymem(p, ctx->header_start, len);
                *p++ = CR; *p++ = LF;

                ctx->err.len = p - ctx->err.data;

                continue;
            }

            if (len == sizeof("Auth-Server") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-Server",
                                   sizeof("Auth-Server") - 1)
                    == 0)
            {
                ctx->addr.len = ctx->header_end - ctx->header_start;
                ctx->addr.data = ctx->header_start;

                continue;
            }

            if (len == sizeof("Auth-Port") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-Port",
                                   sizeof("Auth-Port") - 1)
                   == 0)
            {
                ctx->port.len = ctx->header_end - ctx->header_start;
                ctx->port.data = ctx->header_start;

                continue;
            }

            if (len == sizeof("Auth-User") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-User",
                                   sizeof("Auth-User") - 1)
                   == 0)
            {
                s->login.len = ctx->header_end - ctx->header_start;

                s->login.data = ngx_palloc(s->connection->pool, s->login.len);
                if (s->login.data == NULL) {
                    ngx_close_connection(ctx->peer.connection);
                    ngx_destroy_pool(ctx->pool);
                    ngx_mail_session_internal_server_error(s);
                    return;
                }

                ngx_memcpy(s->login.data, ctx->header_start, s->login.len);

                continue;
            }

            if (len == sizeof("Auth-Pass") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-Pass",
                                   sizeof("Auth-Pass") - 1)
                   == 0)
            {
                s->passwd.len = ctx->header_end - ctx->header_start;

                s->passwd.data = ngx_palloc(s->connection->pool, s->passwd.len);
                if (s->passwd.data == NULL) {
                    ngx_close_connection(ctx->peer.connection);
                    ngx_destroy_pool(ctx->pool);
                    ngx_mail_session_internal_server_error(s);
                    return;
                }

                ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);

                continue;
            }

            if (len == sizeof("Auth-Wait") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-Wait",
                                   sizeof("Auth-Wait") - 1)
                   == 0)
            {
                n = ngx_atoi(ctx->header_start,
                             ctx->header_end - ctx->header_start);

                if (n != NGX_ERROR) {
                    ctx->sleep = n;
                }

                continue;
            }

            if (len == sizeof("Auth-Error-Code") - 1
                && ngx_strncasecmp(ctx->header_name_start,
                                   (u_char *) "Auth-Error-Code",
                                   sizeof("Auth-Error-Code") - 1)
                   == 0)
            {
                ctx->errcode.len = ctx->header_end - ctx->header_start;

                ctx->errcode.data = ngx_palloc(s->connection->pool,
                                               ctx->errcode.len);
                if (ctx->errcode.data == NULL) {
                    ngx_close_connection(ctx->peer.connection);
                    ngx_destroy_pool(ctx->pool);
                    ngx_mail_session_internal_server_error(s);
                    return;
                }

                ngx_memcpy(ctx->errcode.data, ctx->header_start,
                           ctx->errcode.len);

                continue;
            }

            /* ignore other headers */

            continue;
        }

        if (rc == NGX_DONE) {
            ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
                           "mail auth http header done");

            ngx_close_connection(ctx->peer.connection);

            if (ctx->err.len) {

                ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
                              "client login failed: \"%V\"", &ctx->errmsg);

                if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {

                    if (ctx->errcode.len == 0) {
                        ctx->errcode = ngx_mail_smtp_errcode;
                    }

                    ctx->err.len = ctx->errcode.len + ctx->errmsg.len
                                   + sizeof(" " CRLF) - 1;

                    p = ngx_palloc(s->connection->pool, ctx->err.len);
                    if (p == NULL) {
                        ngx_close_connection(ctx->peer.connection);
                        ngx_destroy_pool(ctx->pool);
                        ngx_mail_session_internal_server_error(s);
                        return;
                    }

                    ctx->err.data = p;

                    p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);
                    *p++ = ' ';
                    p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);
                    *p++ = CR; *p = LF;
                }

                s->out = ctx->err;
                timer = ctx->sleep;

                ngx_destroy_pool(ctx->pool);

                if (timer == 0) {
                    s->quit = 1;
                    ngx_mail_send(s->connection->write);
                    return;
                }

                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));

                s->connection->read->handler = ngx_mail_auth_sleep_handler;

                return;
            }

            if (s->auth_wait) {
                timer = ctx->sleep;

                ngx_destroy_pool(ctx->pool);

                if (timer == 0) {
                    ngx_mail_auth_http_init(s);
                    return;
                }

                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));

                s->connection->read->handler = ngx_mail_auth_sleep_handler;

                return;
            }

            if (ctx->addr.len == 0 || ctx->port.len == 0) {
                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                              "auth http server %V did not send server or port",
                              ctx->peer.name);
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            if (s->passwd.data == NULL
                && s->protocol != NGX_MAIL_SMTP_PROTOCOL)
            {
                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                              "auth http server %V did not send password",
                              ctx->peer.name);
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_peer_addr_t));
            if (peer == NULL) {
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in));
            if (sin == NULL) {
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            sin->sin_family = AF_INET;

            port = ngx_atoi(ctx->port.data, ctx->port.len);
            if (port == NGX_ERROR || port < 1 || port > 65536) {
                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                              "auth http server %V sent invalid server "
                              "port:\"%V\"",
                              ctx->peer.name, &ctx->port);
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            sin->sin_port = htons((in_port_t) port);

            ctx->addr.data[ctx->addr.len] = '\0';
            sin->sin_addr.s_addr = inet_addr((char *) ctx->addr.data);
            if (sin->sin_addr.s_addr == INADDR_NONE) {
                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                              "auth http server %V sent invalid server "
                              "address:\"%V\"",
                              ctx->peer.name, &ctx->addr);
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            peer->sockaddr = (struct sockaddr *) sin;
            peer->socklen = sizeof(struct sockaddr_in);

            len = ctx->addr.len + 1 + ctx->port.len;

            peer->name.len = len;

            peer->name.data = ngx_palloc(s->connection->pool, len);
            if (peer->name.data == NULL) {
                ngx_destroy_pool(ctx->pool);
                ngx_mail_session_internal_server_error(s);
                return;
            }

            len = ctx->addr.len;

            ngx_memcpy(peer->name.data, ctx->addr.data, len);

            peer->name.data[len++] = ':';

            ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);

            ngx_destroy_pool(ctx->pool);
            ngx_mail_proxy_init(s, peer);

            return;
        }

        if (rc == NGX_AGAIN ) {
            return;
        }

        /* rc == NGX_ERROR */

        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                      "auth http server %V sent invalid header in response",
                      ctx->peer.name);
        ngx_close_connection(ctx->peer.connection);
        ngx_destroy_pool(ctx->pool);
        ngx_mail_session_internal_server_error(s);

        return;
    }
}
static void
ngx_mail_zmauth_lookup_result_handler(ngx_zm_lookup_work_t * work) {
    ngx_mail_session_t     *s;
    ngx_mail_zmauth_ctx_t  *ctx;
    size_t                  size;
    u_char                 *p;
    ngx_addr_t             *peer;
    ngx_str_t               errmsg;

    s = (ngx_mail_session_t *) work->data;
    ctx = (ngx_mail_zmauth_ctx_t *)ngx_mail_get_module_ctx(s, ngx_mail_zmauth_module);

    if (work->result == ZM_LOOKUP_SUCCESS) {
        ngx_mail_zmauth_unescape(&work->account_name);

        /* copy the lookup result from zmauth pool to s->connection pool */
        s->qlogin = *(ngx_pstrcpy(s->connection->pool, &work->account_name));
        s->key_alias = *(ngx_pstrcpy(s->connection->pool, &work->alias_key));
        s->key_route = *(ngx_pstrcpy(s->connection->pool, &work->route_key));
        if (s->auth_method == NGX_MAIL_AUTH_GSSAPI ||
            s->auth_method == NGX_MAIL_AUTH_GSSAPI_IR) {
            s->dusr = *(ngx_pstrcpy(s->connection->pool, &work->auth_id));
            s->dpasswd = *(ngx_pstrcpy(s->connection->pool, &work->zm_auth_token));
        }
        peer = ngx_palloc(s->connection->pool, sizeof(ngx_addr_t));

        peer->name = *(ngx_pstrcpy(s->connection->pool, &work->route->name));
        peer->socklen = work->route->socklen;
        peer->sockaddr = ngx_palloc(s->connection->pool, peer->socklen);
        if(peer->sockaddr == NULL) {
            return; /* NO MEM */
        }
        ngx_memcpy(peer->sockaddr, work->route->sockaddr, peer->socklen);

        switch (work->alias_check_stat) {
           case ZM_ALIAS_NOT_FOUND:
               s->vlogin = 1;
               break;
           case ZM_ALIAS_FOUND:
               s->vlogin = 2;
               break;
           default:
               break;
               /* do nothing */
           }

        ngx_destroy_pool(ctx->pool);
        ngx_mail_set_ctx(s, NULL, ngx_mail_zmauth_module);
        ngx_mail_proxy_init(s, peer);
        return;
    } else {
        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                      "An error occurred in mail zmauth: %V",
                       &work->err);

        /* construct error msg */
        if (work->result != ZM_LOOKUP_LOGIN_FAILED) {
            /* zmauth clean up will destroy the ctx->pool */
            ngx_mail_session_internal_server_error(s);
            return;
        }

        errmsg = LOGIN_FAILED; /* should we return the real err msg to user? */

        switch (s->protocol) {

        case NGX_MAIL_POP3_PROTOCOL:
            size = sizeof("-ERR ") - 1 + errmsg.len + sizeof(CRLF) - 1;
            if (s->command == NGX_POP3_AUTH) {
                size +=  AUTHENTICATION_FAILED.len;
            }
            break;

        case NGX_MAIL_IMAP_PROTOCOL:
            if (s->command == NGX_IMAP_AUTHENTICATE) {
                errmsg = AUTHENTICATE_FAILED;
            }
            size = s->tag.len + 1 /*for space*/+ sizeof("NO ") - 1 + errmsg.len
                   + sizeof(CRLF) - 1;
            break;

        default: /* NGX_MAIL_SMTP_PROTOCOL */
            ngx_log_error(NGX_LOG_CRIT, s->connection->log, 0, "smtp is not supported!!");
            return;
        }

        p = ngx_pnalloc(s->connection->pool, size);
        if (p == NULL) {
            ngx_destroy_pool(ctx->pool);
            ngx_mail_session_internal_server_error(s);
            return;
        }

        ctx->errmsg.data = p;

        switch (s->protocol) {

        case NGX_MAIL_POP3_PROTOCOL:
            *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';
            if (s->command == NGX_POP3_AUTH) {
                p = ngx_cpymem(p, AUTHENTICATION_FAILED.data, AUTHENTICATION_FAILED.len);
            }
            break;

        case NGX_MAIL_IMAP_PROTOCOL:
            p = ngx_cpymem(p, s->tag.data, s->tag.len);
            *p++ = ' '; *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
            break;

        default: /* NGX_MAIL_SMTP_PROTOCOL */
            break;
        }

        p = ngx_cpymem(p, errmsg.data, errmsg.len);
        *p++ = CR; *p++ = LF;

        ctx->errmsg.len = p - ctx->errmsg.data;
        s->out = ctx->errmsg;

        if (work->result == ZM_LOOKUP_LOGIN_FAILED && work->wait_time > 0) {
            ngx_add_timer(ctx->wait_ev, (ngx_msec_t) (work->wait_time * 1000));

            s->connection->read->handler = ngx_mail_zmauth_block_read;
        } else {
            s->quit = 1;
            ngx_mail_send(s->connection->write);
            ngx_mail_set_ctx(s, NULL, ngx_mail_zmauth_module);
            ngx_destroy_pool(ctx->pool);
        }
        return;
    }
}