view src/imap/ngx_imap_core_module.c @ 196:8759b346e431 NGINX_0_3_45

nginx 0.3.45 *) Feature: the "ssl_verify_client", "ssl_verify_depth", and "ssl_client_certificate" directives. *) Change: the $request_method variable now returns the main request method. *) Change: the ° symbol codes were changed in koi-win conversion table. *) Feature: the euro É N symbols were added to koi-win conversion table. *) Bugfix: if nginx distributed the requests among several backends and some backend failed, then requests intended for this backend was directed to one live backend only instead of being distributed among the rest.
author Igor Sysoev <http://sysoev.ru>
date Sat, 06 May 2006 00:00:00 +0400
parents 3689cd4e3228
children 56688ed172c8
line wrap: on
line source


/*
 * Copyright (C) Igor Sysoev
 */


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_imap.h>


static void *ngx_imap_core_create_main_conf(ngx_conf_t *cf);
static void *ngx_imap_core_create_srv_conf(ngx_conf_t *cf);
static char *ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
    void *child);
static char *ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf);
static char *ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf);
static char *ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd,
    void *conf);


static ngx_conf_enum_t  ngx_imap_core_procotol[] = {
    { ngx_string("pop3"), NGX_IMAP_POP3_PROTOCOL },
    { ngx_string("imap"), NGX_IMAP_IMAP_PROTOCOL },
    { ngx_null_string, 0 }
};


static ngx_str_t  ngx_pop3_default_capabilities[] = {
    ngx_string("TOP"),
    ngx_string("USER"),
    ngx_string("UIDL"),
    ngx_null_string
};


static ngx_str_t  ngx_imap_default_capabilities[] = {
    ngx_string("IMAP4"),
    ngx_string("IMAP4rev1"),
    ngx_string("UIDPLUS"),
    ngx_null_string
};


static ngx_command_t  ngx_imap_core_commands[] = {

    { ngx_string("server"),
      NGX_IMAP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
      ngx_imap_core_server,
      0,
      0,
      NULL },

    { ngx_string("listen"),
      NGX_IMAP_SRV_CONF|NGX_CONF_TAKE12,
      ngx_imap_core_listen,
      0,
      0,
      NULL },

    { ngx_string("protocol"),
      NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_enum_slot,
      NGX_IMAP_SRV_CONF_OFFSET,
      offsetof(ngx_imap_core_srv_conf_t, protocol),
      &ngx_imap_core_procotol },

    { ngx_string("imap_client_buffer"),
      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_size_slot,
      NGX_IMAP_SRV_CONF_OFFSET,
      offsetof(ngx_imap_core_srv_conf_t, imap_client_buffer_size),
      NULL },

    { ngx_string("so_keepalive"),
      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_IMAP_SRV_CONF_OFFSET,
      offsetof(ngx_imap_core_srv_conf_t, so_keepalive),
      NULL },

    { ngx_string("timeout"),
      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
      NGX_IMAP_SRV_CONF_OFFSET,
      offsetof(ngx_imap_core_srv_conf_t, timeout),
      NULL },

    { ngx_string("pop3_capabilities"),
      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE,
      ngx_imap_core_capability,
      NGX_IMAP_SRV_CONF_OFFSET,
      offsetof(ngx_imap_core_srv_conf_t, pop3_capabilities),
      NULL },

    { ngx_string("imap_capabilities"),
      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE,
      ngx_imap_core_capability,
      NGX_IMAP_SRV_CONF_OFFSET,
      offsetof(ngx_imap_core_srv_conf_t, imap_capabilities),
      NULL },

      ngx_null_command
};


static ngx_imap_module_t  ngx_imap_core_module_ctx = {
    ngx_imap_core_create_main_conf,        /* create main configuration */
    NULL,                                  /* init main configuration */

    ngx_imap_core_create_srv_conf,         /* create server configuration */
    ngx_imap_core_merge_srv_conf           /* merge server configuration */
};


ngx_module_t  ngx_imap_core_module = {
    NGX_MODULE_V1,
    &ngx_imap_core_module_ctx,             /* module context */
    ngx_imap_core_commands,                /* module directives */
    NGX_IMAP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};


static void *
ngx_imap_core_create_main_conf(ngx_conf_t *cf)
{
    ngx_imap_core_main_conf_t  *cmcf;

    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_main_conf_t));
    if (cmcf == NULL) {
        return NGX_CONF_ERROR;
    }

    if (ngx_array_init(&cmcf->servers, cf->pool, 4,
                       sizeof(ngx_imap_core_srv_conf_t *))
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_imap_listen_t))
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    return cmcf;
}


static void *
ngx_imap_core_create_srv_conf(ngx_conf_t *cf)
{
    ngx_imap_core_srv_conf_t  *cscf;

    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_srv_conf_t));
    if (cscf == NULL) {
        return NULL;
    }

    cscf->imap_client_buffer_size = NGX_CONF_UNSET_SIZE;
    cscf->protocol = NGX_CONF_UNSET_UINT;
    cscf->timeout = NGX_CONF_UNSET_MSEC;
    cscf->so_keepalive = NGX_CONF_UNSET;

    if (ngx_array_init(&cscf->pop3_capabilities, cf->pool, 4, sizeof(ngx_str_t))
        != NGX_OK)
    {
        return NULL;
    }

    if (ngx_array_init(&cscf->imap_capabilities, cf->pool, 4, sizeof(ngx_str_t))
        != NGX_OK)
    {
        return NULL;
    }

    return cscf;
}


static char *
ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_imap_core_srv_conf_t *prev = parent;
    ngx_imap_core_srv_conf_t *conf = child;

    u_char      *p;
    size_t       size;
    ngx_str_t   *c, *d;
    ngx_uint_t   i;

    ngx_conf_merge_size_value(conf->imap_client_buffer_size,
                              prev->imap_client_buffer_size,
                              (size_t) ngx_pagesize);
    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
    ngx_conf_merge_unsigned_value(conf->protocol, prev->protocol,
                              NGX_IMAP_IMAP_PROTOCOL);
    ngx_conf_merge_value(conf->so_keepalive, prev->so_keepalive, 0);


    if (conf->pop3_capabilities.nelts == 0) {
        conf->pop3_capabilities = prev->pop3_capabilities;
    }

    if (conf->pop3_capabilities.nelts == 0) {

        for (d = ngx_pop3_default_capabilities; d->len; d++) {
            c = ngx_array_push(&conf->pop3_capabilities);
            if (c == NULL) {
                return NGX_CONF_ERROR;
            }

            *c = *d;
        }
    }

    size = sizeof("+OK Capability list follows" CRLF) - 1
           + sizeof("." CRLF) - 1;

    c = conf->pop3_capabilities.elts;
    for (i = 0; i < conf->pop3_capabilities.nelts; i++) {
        size += c[i].len + sizeof(CRLF) - 1;
    }

    p = ngx_palloc(cf->pool, size);
    if (p == NULL) {
        return NGX_CONF_ERROR;
    }

    conf->pop3_capability.len = size;
    conf->pop3_capability.data = p;

    p = ngx_cpymem(p, "+OK Capability list follows" CRLF,
                   sizeof("+OK Capability list follows" CRLF) - 1);

    for (i = 0; i < conf->pop3_capabilities.nelts; i++) {
        p = ngx_cpymem(p, c[i].data, c[i].len);
        *p++ = CR; *p++ = LF;
    }

    *p++ = '.'; *p++ = CR; *p = LF;


    size += sizeof("STLS" CRLF) - 1;

    p = ngx_palloc(cf->pool, size);
    if (p == NULL) {
        return NGX_CONF_ERROR;
    }

    conf->pop3_starttls_capability.len = size;
    conf->pop3_starttls_capability.data = p;

    p = ngx_cpymem(p, conf->pop3_capability.data,
                   conf->pop3_capability.len - (sizeof("." CRLF) - 1));

    p = ngx_cpymem(p, "STLS" CRLF, sizeof("STLS" CRLF) - 1);
    *p++ = '.'; *p++ = CR; *p = LF;


    if (conf->imap_capabilities.nelts == 0) {
        conf->imap_capabilities = prev->imap_capabilities;
    }

    if (conf->imap_capabilities.nelts == 0) {

        for (d = ngx_imap_default_capabilities; d->len; d++) {
            c = ngx_array_push(&conf->imap_capabilities);
            if (c == NULL) {
                return NGX_CONF_ERROR;
            }

            *c = *d;
        }
    }

    size = sizeof("* CAPABILITY") - 1 + sizeof(CRLF) - 1;

    c = conf->imap_capabilities.elts;
    for (i = 0; i < conf->imap_capabilities.nelts; i++) {
        size += 1 + c[i].len;
    }

    p = ngx_palloc(cf->pool, size);
    if (p == NULL) {
        return NGX_CONF_ERROR;
    }

    conf->imap_capability.len = size;
    conf->imap_capability.data = p;

    p = ngx_cpymem(p, "* CAPABILITY", sizeof("* CAPABILITY") - 1);

    for (i = 0; i < conf->imap_capabilities.nelts; i++) {
        *p++ = ' ';
        p = ngx_cpymem(p, c[i].data, c[i].len);
    }

    *p++ = CR; *p = LF;


    size += sizeof(" STARTTLS") - 1;

    p = ngx_palloc(cf->pool, size);
    if (p == NULL) {
        return NGX_CONF_ERROR;
    }

    conf->imap_starttls_capability.len = size;
    conf->imap_starttls_capability.data = p;

    p = ngx_cpymem(p, conf->imap_capability.data,
                   conf->imap_capability.len - (sizeof(CRLF) - 1));
    p = ngx_cpymem(p, " STARTTLS", sizeof(" STARTTLS") - 1);
    *p++ = CR; *p = LF;


    size += sizeof(" LOGINDISABLED") - 1;

    p = ngx_palloc(cf->pool, size);
    if (p == NULL) {
        return NGX_CONF_ERROR;
    }

    conf->imap_starttls_only_capability.len = size;
    conf->imap_starttls_only_capability.data = p;

    p = ngx_cpymem(p, conf->imap_starttls_capability.data,
                   conf->imap_starttls_capability.len - (sizeof(CRLF) - 1));
    p = ngx_cpymem(p, " LOGINDISABLED", sizeof(" LOGINDISABLED") - 1);
    *p++ = CR; *p = LF;


    return NGX_CONF_OK;
}


static char *
ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                       *rv;
    void                       *mconf;
    ngx_uint_t                  m;
    ngx_conf_t                  pcf;
    ngx_imap_module_t          *module;
    ngx_imap_conf_ctx_t        *ctx, *imap_ctx;
    ngx_imap_core_srv_conf_t   *cscf, **cscfp;
    ngx_imap_core_main_conf_t  *cmcf;


    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_imap_conf_ctx_t));
    if (ctx == NULL) {
        return NGX_CONF_ERROR;
    }

    imap_ctx = cf->ctx;
    ctx->main_conf = imap_ctx->main_conf;

    /* the server{}'s srv_conf */

    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_imap_max_module);
    if (ctx->srv_conf == NULL) {
        return NGX_CONF_ERROR;
    }

    for (m = 0; ngx_modules[m]; m++) {
        if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
            continue;
        }

        module = ngx_modules[m]->ctx;

        if (module->create_srv_conf) {
            mconf = module->create_srv_conf(cf);
            if (mconf == NULL) {
                return NGX_CONF_ERROR;
            }

            ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
        }
    }

    /* the server configuration context */

    cscf = ctx->srv_conf[ngx_imap_core_module.ctx_index];
    cscf->ctx = ctx;

    cmcf = ctx->main_conf[ngx_imap_core_module.ctx_index];

    cscfp = ngx_array_push(&cmcf->servers);
    if (cscfp == NULL) {
        return NGX_CONF_ERROR;
    }

    *cscfp = cscf;


    /* parse inside server{} */

    pcf = *cf;
    cf->ctx = ctx;
    cf->cmd_type = NGX_IMAP_SRV_CONF;

    rv = ngx_conf_parse(cf, NULL);

    *cf = pcf;

    return rv;
}


/* AF_INET only */

static char *
ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char                       *err;
    ngx_str_t                  *value;
    in_addr_t                   in_addr;
    ngx_uint_t                  i;
    struct hostent             *h;
    ngx_imap_listen_t          *imls;
    ngx_inet_upstream_t         inet_upstream;
    ngx_imap_core_main_conf_t  *cmcf;

    value = cf->args->elts;

    ngx_memzero(&inet_upstream, sizeof(ngx_inet_upstream_t));

    inet_upstream.url = value[1];
    inet_upstream.port_only = 1;

    err = ngx_inet_parse_host_port(&inet_upstream);

    if (err) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "%s in \"%V\" of the \"listen\" directive",
                           err, &inet_upstream.url);
        return NGX_CONF_ERROR;
    }

    if (inet_upstream.host.len == 1 && inet_upstream.host.data[0] == '*') {
        inet_upstream.host.len = 0;
    }

    if (inet_upstream.host.len) {
        inet_upstream.host.data[inet_upstream.host.len] = '\0';

        in_addr = inet_addr((const char *) inet_upstream.host.data);

        if (in_addr == INADDR_NONE) {
            h = gethostbyname((const char *) inet_upstream.host.data);

            if (h == NULL || h->h_addr_list[0] == NULL) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "can not resolve host \"%s\" "
                                   "in the \"listen\" directive",
                                   inet_upstream.host.data);
                return NGX_CONF_ERROR;
            }

            in_addr = *(in_addr_t *)(h->h_addr_list[0]);
        }

    } else {
        in_addr = INADDR_ANY;
    }

    cmcf = ngx_imap_conf_get_module_main_conf(cf, ngx_imap_core_module);

    imls = cmcf->listen.elts;

    for (i = 0; i < cmcf->listen.nelts; i++) {

        if (imls[i].addr != in_addr || imls[i].port != inet_upstream.port) {
            continue;
        }

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "duplicate \"%V\" address and port pair",
                           &inet_upstream.url);
        return NGX_CONF_ERROR;
    }

    imls = ngx_array_push(&cmcf->listen);
    if (imls == NULL) {
        return NGX_CONF_ERROR;
    }

    ngx_memzero(imls, sizeof(ngx_imap_listen_t));

    imls->addr = in_addr;
    imls->port = inet_upstream.port;
    imls->family = AF_INET;
    imls->ctx = cf->ctx;

    if (cf->args->nelts == 2) {
        return NGX_CONF_OK;
    }

    if (ngx_strcmp(value[2].data, "bind") == 0) {
        imls->bind = 1;
        return NGX_CONF_OK;
    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "the invalid \"%V\" parameter", &value[2]);
    return NGX_CONF_ERROR;
}


static char *
ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;

    ngx_str_t    *c, *value;
    ngx_uint_t    i;
    ngx_array_t  *a;

    a = (ngx_array_t *) (p + cmd->offset);

    value = cf->args->elts;

    for (i = 1; i < cf->args->nelts; i++) {
        c = ngx_array_push(a);
        if (c == NULL) {
            return NGX_CONF_ERROR;
        }

        *c = value[i];
    }

    return NGX_CONF_OK;
}