static void ngx_mail_throttle_ip_add
    (ngx_str_t *ip, throttle_callback_t *callback)
{
    ngx_pool_t     *pool    = callback->pool;
    ngx_log_t      *log     = callback->log;
    ngx_mail_session_t  *s  = callback->session;
    ngx_mail_throttle_srv_conf_t * tscf;
    mc_work_t       w;
    ngx_str_t       k, value;
    ngx_str_t      *key;

    ngx_log_error (NGX_LOG_INFO, log, 0, "counter for %V not found, "
                        "create ip throttle counter", ip);

    w.ctx = callback;
    w.request_code = mcreq_add;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_ip_add_success_handler;
    w.on_failure = ngx_mail_throttle_ip_add_failure_handler;

    /* use ttl for discrete time sampling of ip login hits */
    tscf = ngx_mail_get_module_srv_conf(s, ngx_mail_throttle_module);

    k = ngx_mail_throttle_get_ip_throttle_key(pool, log, *ip);

    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
               "allowing ip %V login because of internal error "
               "in ip throttle control (generate key for add)", ip);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
               "allowing ip %V login because of internal error "
               "in ip throttle control (deep copy key for add)", ip);
    }

    ngx_str_set(&value, "1");

    callback->value = ngx_pstrcpy (pool, &value);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
               "allowing ip %V login because of internal error "
               "in ip throttle control (deep copy key for add)", ip);
    }
    callback->key = key;
    callback->ip = ip;
    callback->ttl = &tscf->mail_login_ip_ttl_text;

    ngx_memcache_post_with_ttl(&w, *key, value, tscf->mail_login_ip_ttl_text,/* pool */ NULL, log);
}
/* add a throttle counter for a user, here user might be an alias or a fqn */
static void ngx_mail_throttle_user_add
    (ngx_str_t *user, throttle_callback_t *callback)
{
    ngx_pool_t             *pool    = callback->pool;
    ngx_log_t              *log     = callback->log;
    ngx_mail_session_t     *s       = callback->session;
    ngx_mail_throttle_srv_conf_t *tscf;
    mc_work_t               w;
    ngx_str_t               k;
    ngx_str_t              *value, *key;

    ngx_log_error (NGX_LOG_INFO, log, 0, "create a throttle counter for user %V", user);
    w.ctx = callback;
    w.request_code = mcreq_add;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_user_add_success_handler;
    w.on_failure = ngx_mail_throttle_user_add_failure_handler;

    k = ngx_mail_throttle_get_user_throttle_key(pool, log, *user);
    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (generate key for add)", user);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (deep copy key for add)", user);
        callback->on_allow(callback);
        return;
    }

    tscf = ngx_mail_get_module_srv_conf (s, ngx_mail_throttle_module);

    value = ngx_palloc (pool, sizeof(ngx_str_t));
    if (value == NULL) {
       ngx_log_error (NGX_LOG_ERR, log, 0,
               "allowing user %V login because of internal error"
               "in user throttle control (alloc mem for add value)", user);
       callback->on_allow(callback);
       return;
    }

    ngx_str_set(value, "1");

    callback->key = key;
    callback->value = value;
    callback->user = user;
    callback->ttl = &tscf->mail_login_user_ttl_text;

    ngx_memcache_post_with_ttl(&w, *key, *value, tscf->mail_login_user_ttl_text,
            /* pool */ NULL, log);
    return;
}
/* add a throttle counter for a user, here user might be an alias or a fqn */
static void ngx_mail_throttle_user_add
    (ngx_str_t *user, throttle_callback_t *callback)
{
    ngx_pool_t             *pool    = callback->pool;
    ngx_log_t              *log     = callback->log;
    ngx_mail_session_t     *s       = callback->session;
    ngx_mail_throttle_srv_conf_t *tscf;
    mc_work_t               w;
    size_t                  l;
    ngx_str_t               k;
    ngx_str_t              *pdu, *key;
    u_char                 *p;
    ngx_uint_t              ttl;
    size_t                  ttld;

    ngx_log_error (NGX_LOG_INFO, log, 0, "create a throttle counter for user %V", user);
    w.ctx = callback;
    w.request_code = mcreq_add;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_user_add_success_handler;
    w.on_failure = ngx_mail_throttle_user_add_failure_handler;

    k = ngx_memcache_get_user_throttle_key(pool, log, *user);
    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (generate key for add)", user);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (deep copy key for add)", user);
        callback->on_allow(callback);
        return;
    }

    tscf = ngx_mail_get_module_srv_conf (s, ngx_mail_throttle_module);
    ttl = tscf->mail_login_user_ttl / 1000;              /* msec => sec */
    if(tscf->mail_login_user_ttl % 1000 > 0) { ++ttl; }  /* round upward */
    ttld = serialize_number(NULL, ttl);

    l = sizeof("add ") - 1 +
        k.len +
        sizeof(" ") - 1 +
        sizeof("0 ") - 1 +
        ttld + sizeof(" ") - 1 +
        sizeof("1") - 1 + sizeof(CRLF) - 1 +
        1 + sizeof(CRLF) - 1;

    pdu = ngx_palloc (pool, sizeof(ngx_str_t));
    if (pdu == NULL) {
       ngx_log_error (NGX_LOG_ERR, log, 0,
               "allowing user %V login because of internal error"
               "in user throttle control (alloc mem for add pdu)", user);
       callback->on_allow(callback);
       return;
   }

    pdu->data = ngx_palloc(pool, l);
    if (pdu->data == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error "
                "in user throttle control (alloc mem for add pdu data)", user);
        callback->on_allow(callback);
        return;
    }

    p = pdu->data;
    p = ngx_cpymem(p, "add ", sizeof("add ") - 1);
    p = ngx_cpymem(p, k.data, k.len);
    *p++ = ' ';
    *p++ = '0';
    *p++ = ' ';
    p+= serialize_number(p, ttl);
    *p++ = ' ';
    *p++ = '1';
    *p++ = CR;
    *p++ = LF;
    *p++ = '1';
    *p++ = CR;
    *p++ = LF;
    pdu->len = p - pdu->data;

    callback->key = key;
    callback->pdu = pdu;
    callback->user = user;

    ngx_memcache_post(&w, *key, *pdu,/* pool */ NULL, log);
    return;
}
/* same as ngx_mail_throttle_user, but works on a fully qualified user name */
static void ngx_mail_throttle_quser (ngx_str_t * quser, throttle_callback_t *callback)
{
    ngx_log_t           *log;
    ngx_pool_t          *pool;
    mc_work_t            w;
    ngx_str_t            k;
    ngx_str_t           *pdu, *key;
    size_t               l;
    u_char              *p;
    ngx_mail_session_t  *s;
    ngx_flag_t           check_only;

    pool = callback->pool;
    log = callback->log;
    s = callback->session;
    check_only = callback->check_only;

    k = ngx_memcache_get_user_throttle_key (pool, log, *quser);
    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (generate key for get)", quser);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (deep copy check user key)", quser);
        callback->on_allow(callback);
    }

    if (check_only == 0)
    {   // try to increment the counter for this user
        ngx_log_error (NGX_LOG_INFO, log, 0, "check user throttle:%V", quser);
        w.ctx = callback;
        w.request_code = mcreq_incr;
        w.response_code = mcres_unknown;
        w.on_success = ngx_mail_throttle_quser_success_handler;
        w.on_failure = ngx_mail_throttle_quser_failure_handler;

        l = sizeof("incr ") - 1 +
            k.len +
            sizeof(" ") - 1 +
            sizeof("1") - 1 +
            sizeof(CRLF) - 1;
        pdu = ngx_palloc (pool, sizeof(ngx_str_t));
        if (pdu == NULL) {
            ngx_log_error (NGX_LOG_ERR, log, 0,
                    "allowing user %V login because of internal error"
                    "in ip throttle control (alloc mem for incr pdu)", quser);
            callback->on_allow(callback);
            return;
        }
        pdu->data = ngx_palloc (pool, l);
        if (pdu->data == NULL) {
            ngx_log_error (NGX_LOG_ERR, log, 0,
                    "allowing user %V login because of internal error "
                    "in user throttle control (alloc mem for incr pdu data)", quser);
            callback->on_allow(callback);
            return;
        }

        p = pdu->data;
        p = ngx_cpymem(p, "incr ", sizeof("incr ") - 1);
        p = ngx_cpymem(p, k.data, k.len);
        *p++ = ' ';
        *p++ = '1';
        *p++ = CR;
        *p++ = LF;
        pdu->len = p - pdu->data;
    }
    else
    {   // just check the counter
        ngx_log_error (NGX_LOG_INFO, log, 0, "check user throttle:%V, check only", quser);
        w.ctx = callback;
        w.request_code = mcreq_get;
        w.response_code = mcres_unknown;
        w.on_success = ngx_mail_throttle_quser_success_handler;
        w.on_failure = ngx_mail_throttle_quser_failure_handler;

        l = sizeof("get ") - 1 +
            k.len +
            sizeof(CRLF) - 1;

        pdu = ngx_palloc (pool, sizeof(ngx_str_t));
        if (pdu == NULL) {
            ngx_log_error (NGX_LOG_ERR, log, 0,
                    "allowing ip %V login because of internal error"
                    "in user throttle control (alloc mem for get pdu)", quser);
            callback->on_allow(callback);
            return;
        }
        pdu->data = ngx_palloc (pool, l);
        if (pdu->data == NULL) {
            ngx_log_error (NGX_LOG_ERR, log, 0,
                    "allowing user %V login because of internal error "
                    "in user throttle control (alloc mem for get pdu data)", quser);
            callback->on_allow(callback);
            return;
        }

        p = pdu->data;
        p = ngx_cpymem(p, "get ", sizeof("get ") - 1);
        p = ngx_cpymem(p, k.data, k.len);
        *p++ = CR;
        *p++ = LF;
        pdu->len = p - pdu->data;
    }

    callback->key = key;
    callback->pdu = pdu;
    callback->user = quser;

    ngx_memcache_post(&w, *key, *pdu,/* pool */ NULL, log);
}
/* check whether the client ip should be allowed to proceed, or whether
   the connection should be throttled
 */
void ngx_mail_throttle_user (ngx_str_t user, throttle_callback_t *callback)
{
    ngx_pool_t          *pool;
    ngx_log_t           *log;
    ngx_connection_t    *c;
    ngx_str_t           *cusr, *pdu;
    ngx_mail_session_t  *s;
    mc_work_t            w;
    size_t               l;
    u_char              *p;
    ngx_str_t            proxyip;

    pool = callback->pool;
    log = callback->log;
    c = callback->connection;
    s = callback->session;

    ngx_log_error (NGX_LOG_INFO, log, 0,
        "user throttle: lookup alias, user:%V", &user);

    /* save a copy of the user name */
    cusr = ngx_pstrcpy(pool, &user);
    if (cusr == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error "
                "in user throttle control (deep copy user for incr)", &user);
        callback->on_allow(callback);
        return;
    }

    if (s->vlogin) {
        /* user alias has already been looked up */
        ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, log, 0,
            "user throttle: skip alias lookup, user:%V", &user);
        ngx_mail_throttle_quser(cusr, callback);
        return;
    }

    w.ctx = callback;
    w.request_code = mcreq_get;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_user_success_handler;
    w.on_failure = ngx_mail_throttle_user_failure_handler;

    /* GSSAPI workaround: don't lookup aliases for GSSAPI */
    if (s->auth_method == NGX_MAIL_AUTH_GSSAPI) {
        ngx_log_error(NGX_LOG_NOTICE, log, 0,
            "not looking up cached aliases for auth=gssapi");
        callback->on_deny(callback);
        return;
    }

    /* first stringify the proxy-ip address */
    proxyip = ngx_mail_get_local_addr4 (pool, c->fd);

    s->key_alias = ngx_memcache_get_alias_key(
            pool,
            log,
            *cusr,
            proxyip
        );

    if (s->key_alias.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error "
                "in user throttle control (create alias key)", &user);
        callback->on_allow(callback);
        return;
    }

    l = sizeof("get ") - 1 +
        s->key_alias.len +
        sizeof(CRLF) - 1;

    pdu = ngx_palloc(pool, sizeof(ngx_str_t));
    if (pdu == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error"
                "in ip throttle control (alloc mem for get alias pdu)", &user);
        callback->on_allow(callback);
        return;
    }
    pdu->data = ngx_palloc(pool, l);
    if (pdu->data == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error "
                "in user throttle control (alloc mem for get alias pdu data)", &user);
        callback->on_allow(callback);
        return;
    }

    p = ngx_cpymem(pdu->data, "get ", sizeof("get ") - 1);
    p = ngx_cpymem(p, s->key_alias.data, s->key_alias.len);
    *p++ = CR;
    *p++ = LF;
    pdu->len = p - pdu->data;

    callback->key = &s->key_alias;
    callback->pdu = pdu;
    callback->user = cusr;

    ngx_memcache_post(&w, s->key_alias, *pdu,/* pool */ NULL, log);
}
/* check whether the client ip should be allowed to proceed, or whether
   the connection should be throttled
 */
void ngx_mail_throttle_ip (ngx_str_t ip, throttle_callback_t *callback)
{
    ngx_log_t       *log;
    ngx_pool_t      *pool;
    mc_work_t        w;
    ngx_str_t        k;
    ngx_str_t       *eip, *key, *pdu;
    size_t           l;
    u_char          *p;
    ngx_mail_session_t  *s;

    pool = callback->pool;
    log = callback->log;
    s = callback->session;

    ngx_log_error (NGX_LOG_INFO, log, 0, "check ip throttle:%V", &ip);

    w.ctx = callback;
    w.request_code = mcreq_incr;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_ip_success_handler;
    w.on_failure = ngx_mail_throttle_ip_failure_handler;

    k = ngx_memcache_get_ip_throttle_key(pool, log, ip);

    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (generate key for incr)", &ip);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (deep copy key for incr)", &ip);
        callback->on_allow(callback);
    }

    l = sizeof("incr ") - 1 +
        k.len +
        sizeof(" ") - 1 +
        sizeof("1") - 1 +
        sizeof(CRLF) - 1;

    pdu = ngx_palloc (pool, sizeof(ngx_str_t));
    if (pdu == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (alloc mem for incr pdu)", &ip);
        callback->on_allow(callback);
        return;
    }

    pdu->data = ngx_palloc (pool, l);
    if (pdu->data == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (alloc mem for incr pdu data)", &ip);
        callback->on_allow(callback);
        return;
    }

    /* make a copy of the input IP address for callback reference */
    eip = ngx_pstrcpy (pool, &ip);
    if (eip == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (deep copy ip for incr)", &ip);
        callback->on_allow(callback);
        return;
    }

    p = pdu->data;
    p = ngx_cpymem(p, "incr ", sizeof("incr ") - 1);
    p = ngx_cpymem(p, k.data, k.len);
    *p++ = ' ';
    *p++ = '1';
    *p++ = CR;
    *p++ = LF;
    pdu->len = p - pdu->data;

    callback->ip = eip;
    callback->pdu = pdu;
    callback->key = key;

    ngx_memcache_post(&w, *key, *pdu, /* pool */ NULL, log);
}
/* same as ngx_mail_throttle_user, but works on a fully qualified user name */
static void ngx_mail_throttle_quser (ngx_str_t * quser, throttle_callback_t *callback)
{
    ngx_log_t           *log;
    ngx_pool_t          *pool;
    mc_work_t            w;
    ngx_str_t            k;
    ngx_str_t           *value, *key;
    ngx_flag_t           check_only;

    pool = callback->pool;
    log = callback->log;
    check_only = callback->check_only;

    k = ngx_mail_throttle_get_user_throttle_key(pool, log, *quser);
    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (generate key for get)", quser);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
            "allowing user %V login because of internal error "
            "in user throttle control (deep copy check user key)", quser);
        callback->on_allow(callback);
    }

    if (check_only == 0)
    {   // try to increment the counter for this user
        ngx_log_error (NGX_LOG_INFO, log, 0, "check user throttle:%V", quser);
        w.ctx = callback;
        w.request_code = mcreq_incr;
        w.response_code = mcres_unknown;
        w.on_success = ngx_mail_throttle_quser_success_handler;
        w.on_failure = ngx_mail_throttle_quser_failure_handler;

        value = ngx_palloc (pool, sizeof(ngx_str_t));
        if (value == NULL) {
            ngx_log_error (NGX_LOG_ERR, log, 0,
                    "allowing user %V login because of internal error"
                    "in ip throttle control (alloc mem for incr value)", quser);
            callback->on_allow(callback);
            return;
        }

        ngx_str_set(value, "1");
    }
    else
    {   // just check the counter
        ngx_log_error (NGX_LOG_INFO, log, 0, "check user throttle:%V, check only", quser);
        w.ctx = callback;
        w.request_code = mcreq_get;
        w.response_code = mcres_unknown;
        w.on_success = ngx_mail_throttle_quser_success_handler;
        w.on_failure = ngx_mail_throttle_quser_failure_handler;

        value = ngx_palloc (pool, sizeof(ngx_str_t));
        if (value == NULL) {
            ngx_log_error (NGX_LOG_ERR, log, 0,
                    "allowing ip %V login because of internal error"
                    "in user throttle control (alloc mem for get value)", quser);
            callback->on_allow(callback);
            return;
        }

        ngx_str_null (value);
    }

    callback->key = key;
    callback->value = value;
    callback->user = quser;

    ngx_memcache_post(&w, *key, *value,/* pool */ NULL, log);
}
/* check whether the client user name should be allowed to proceed, or whether
   the connection should be throttled
 */
void ngx_mail_throttle_user (ngx_str_t user, throttle_callback_t *callback)
{
    ngx_pool_t          *pool;
    ngx_log_t           *log;
    ngx_connection_t    *c;
    ngx_str_t           *cusr;
    ngx_mail_session_t  *s;
    mc_work_t            w;
    ngx_str_t            proxyip;
    ngx_str_t           *dummy_value;

    pool = callback->pool;
    log = callback->log;
    c = callback->connection;
    s = callback->session;

    ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, log, 0,
        "user throttle: lookup alias, user:%V", &user);

    /* save a copy of the user name */
    cusr = ngx_pstrcpy(pool, &user);
    if (cusr == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error "
                "in user throttle control (deep copy user for incr)", &user);
        callback->on_allow(callback);
        return;
    }

    if (s->vlogin) {
        /* user alias has already been looked up */
        ngx_log_debug1 (NGX_LOG_DEBUG_MAIL, log, 0,
            "user throttle: skip alias lookup, user:%V", &user);
        ngx_mail_throttle_quser(cusr, callback);
        return;
    }

    w.ctx = callback;
    w.request_code = mcreq_get;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_user_success_handler;
    w.on_failure = ngx_mail_throttle_user_failure_handler;

    /* GSSAPI workaround: don't lookup aliases for GSSAPI */
    if (s->auth_method == NGX_MAIL_AUTH_GSSAPI) {
        ngx_log_error(NGX_LOG_INFO, log, 0,
            "not looking up cached aliases for auth=gssapi");
        ngx_mail_throttle_quser(cusr, callback);
        return;
    }

    /* first stringify the proxy-ip address */
    proxyip = ngx_mail_get_socket_local_addr_str (pool, c->fd);

    s->key_alias = ngx_zm_lookup_get_mail_alias_key(
            pool,
            log,
            *cusr,
            proxyip
        );

    if (s->key_alias.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing user %V login because of internal error "
                "in user throttle control (create alias key)", &user);
        callback->on_allow(callback);
        return;
    }

    dummy_value = ngx_palloc (pool, sizeof (ngx_str_t));

    ngx_str_null (dummy_value);

    callback->key = &s->key_alias;
    callback->value = dummy_value;
    callback->user = cusr;

    ngx_memcache_post(&w, s->key_alias, *dummy_value,/* pool */ NULL, log);
}
/* check whether the client ip should be allowed to proceed, or whether
   the connection should be throttled
 */
void ngx_mail_throttle_ip (ngx_str_t ip, throttle_callback_t *callback)
{
    ngx_log_t       *log;
    ngx_pool_t      *pool;
    mc_work_t        w;
    ngx_str_t        k;
    ngx_str_t       *value, *eip, *key;

    pool = callback->pool;
    log = callback->log;

    ngx_log_error (NGX_LOG_INFO, log, 0, "check ip throttle:[%V]", &ip);

    w.ctx = callback;
    w.request_code = mcreq_incr;
    w.response_code = mcres_unknown;
    w.on_success = ngx_mail_throttle_ip_success_handler;
    w.on_failure = ngx_mail_throttle_ip_failure_handler;

    k = ngx_mail_throttle_get_ip_throttle_key(pool, log, ip);

    if (k.len == 0) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (generate key for incr)", &ip);
        callback->on_allow(callback);
        return;
    }

    key = ngx_pstrcpy (pool, &k);
    if (key == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (deep copy key for incr)", &ip);
        callback->on_allow(callback);
    }

    value = ngx_palloc (pool, sizeof(ngx_str_t));
    if (value == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (alloc mem for incr value)", &ip);
        callback->on_allow(callback);
        return;
    }

    ngx_str_set(value, "1");

    /* make a copy of the input IP address for callback reference */
    eip = ngx_pstrcpy (pool, &ip);
    if (eip == NULL) {
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "allowing ip %V login because of internal error"
                "in ip throttle control (deep copy ip for incr)", &ip);
        callback->on_allow(callback);
        return;
    }

    callback->ip = eip;
    callback->value = value;
    callback->key = key;

    ngx_memcache_post(&w, *key, *value, /* pool */ NULL, log);
}
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;
    }
}