static void ngx_mail_throttle_user_failure_handler (mc_work_t *w)
{
    throttle_callback_t  *callback = w->ctx;
    ngx_str_t            *user = callback->user;
    ngx_mail_session_t   *s = callback->session;
    ngx_log_t            *log = callback->log;

    if (w->response_code == mcres_failure_normal) {
        //NOT_FOUND
        ngx_log_error(NGX_LOG_INFO, log, 0,
            "user throttle: no alias for user:%V",
            user);
        s->vlogin = 1;  /* avoid duplicate lookups for alias */
        ngx_mail_throttle_quser (callback->user, callback);

    } else if(w->response_code == mcres_failure_again) {
        mc_work_t nw;
        nw.ctx = callback;
        nw.request_code = mcreq_get;
        nw.response_code = mcres_unknown;
        nw.on_success = ngx_mail_throttle_user_success_handler;
        nw.on_failure = ngx_mail_throttle_user_failure_handler;
        ngx_log_error (NGX_LOG_NOTICE, callback->log, 0,
                "retry to lookup alias %V before user throttle", callback->user);
        ngx_memcache_post(&nw, *callback->key, *callback->pdu,
                            /* pool */ NULL, log);

    } else { /* mcres_failure_unavailable */
        ngx_log_error (NGX_LOG_ERR, log, 0,
                "throttle allowing access from user %V because "
                "memcache service is unavailable when try to "
                "perform alias lookup", callback->user);
        callback->on_allow(callback);
    }
}
/* callback to replace login user name with an alias, if any */
static void ngx_mail_throttle_user_success_handler (mc_work_t *w)
{
    throttle_callback_t     *callback = w->ctx;
    ngx_mail_session_t      *s = callback->session;
    ngx_str_t                login; //full qualified name

    /* deep copy w->payload onto s->login (pool is callback->pool) */
    login.data = ngx_pstrdup (callback->pool, &w->payload);
    if (login.data != NULL)
    {
        login.len = w->payload.len;
        s->vlogin = 2; // lookup alias and found
        s->qlogin = login;
        ngx_log_error (NGX_LOG_INFO, callback->log, 0,
            "user throttle: alias %V replaced by %V",
            callback->user, &s->login);
        ngx_mail_throttle_quser (&s->login, callback);
    } else {
        ngx_mail_throttle_quser (callback->user, callback);
    }
}
/* 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 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);
}