Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.11 ]​[ nginx-1.14.2 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 #define NGX_HTTP_CHARSET_OFF    -2
0014 #define NGX_HTTP_NO_CHARSET     -3
0015 #define NGX_HTTP_CHARSET_VAR    0x10000
0016 
0017 /* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */
0018 #define NGX_UTF_LEN             4
0019 
0020 #define NGX_HTML_ENTITY_LEN     (sizeof("&#1114111;") - 1)
0021 
0022 
0023 typedef struct {
0024     u_char                    **tables;
0025     ngx_str_t                   name;
0026 
0027     unsigned                    length:16;
0028     unsigned                    utf8:1;
0029 } ngx_http_charset_t;
0030 
0031 
0032 typedef struct {
0033     ngx_int_t                   src;
0034     ngx_int_t                   dst;
0035 } ngx_http_charset_recode_t;
0036 
0037 
0038 typedef struct {
0039     ngx_int_t                   src;
0040     ngx_int_t                   dst;
0041     u_char                     *src2dst;
0042     u_char                     *dst2src;
0043 } ngx_http_charset_tables_t;
0044 
0045 
0046 typedef struct {
0047     ngx_array_t                 charsets;       /* ngx_http_charset_t */
0048     ngx_array_t                 tables;         /* ngx_http_charset_tables_t */
0049     ngx_array_t                 recodes;        /* ngx_http_charset_recode_t */
0050 } ngx_http_charset_main_conf_t;
0051 
0052 
0053 typedef struct {
0054     ngx_int_t                   charset;
0055     ngx_int_t                   source_charset;
0056     ngx_flag_t                  override_charset;
0057 
0058     ngx_hash_t                  types;
0059     ngx_array_t                *types_keys;
0060 } ngx_http_charset_loc_conf_t;
0061 
0062 
0063 typedef struct {
0064     u_char                     *table;
0065     ngx_int_t                   charset;
0066     ngx_str_t                   charset_name;
0067 
0068     ngx_chain_t                *busy;
0069     ngx_chain_t                *free_bufs;
0070     ngx_chain_t                *free_buffers;
0071 
0072     size_t                      saved_len;
0073     u_char                      saved[NGX_UTF_LEN];
0074 
0075     unsigned                    length:16;
0076     unsigned                    from_utf8:1;
0077     unsigned                    to_utf8:1;
0078 } ngx_http_charset_ctx_t;
0079 
0080 
0081 typedef struct {
0082     ngx_http_charset_tables_t  *table;
0083     ngx_http_charset_t         *charset;
0084     ngx_uint_t                  characters;
0085 } ngx_http_charset_conf_ctx_t;
0086 
0087 
0088 static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,
0089     ngx_str_t *name);
0090 static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,
0091     ngx_str_t *name);
0092 static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,
0093     ngx_str_t *name);
0094 static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);
0095 static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,
0096     ngx_str_t *charset);
0097 static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,
0098     ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);
0099 static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);
0100 static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,
0101     ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
0102 static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,
0103     ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
0104 
0105 static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,
0106     ngx_http_charset_ctx_t *ctx);
0107 static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,
0108     ngx_http_charset_ctx_t *ctx, size_t size);
0109 
0110 static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
0111     void *conf);
0112 static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,
0113     void *conf);
0114 
0115 static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,
0116     void *conf);
0117 static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);
0118 
0119 static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);
0120 static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);
0121 static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,
0122     void *parent, void *child);
0123 static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);
0124 
0125 
0126 static ngx_str_t  ngx_http_charset_default_types[] = {
0127     ngx_string("text/html"),
0128     ngx_string("text/xml"),
0129     ngx_string("text/plain"),
0130     ngx_string("text/vnd.wap.wml"),
0131     ngx_string("application/javascript"),
0132     ngx_string("application/rss+xml"),
0133     ngx_null_string
0134 };
0135 
0136 
0137 static ngx_command_t  ngx_http_charset_filter_commands[] = {
0138 
0139     { ngx_string("charset"),
0140       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
0141                         |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
0142       ngx_http_set_charset_slot,
0143       NGX_HTTP_LOC_CONF_OFFSET,
0144       offsetof(ngx_http_charset_loc_conf_t, charset),
0145       NULL },
0146 
0147     { ngx_string("source_charset"),
0148       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
0149                         |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
0150       ngx_http_set_charset_slot,
0151       NGX_HTTP_LOC_CONF_OFFSET,
0152       offsetof(ngx_http_charset_loc_conf_t, source_charset),
0153       NULL },
0154 
0155     { ngx_string("override_charset"),
0156       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
0157                         |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
0158       ngx_conf_set_flag_slot,
0159       NGX_HTTP_LOC_CONF_OFFSET,
0160       offsetof(ngx_http_charset_loc_conf_t, override_charset),
0161       NULL },
0162 
0163     { ngx_string("charset_types"),
0164       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
0165       ngx_http_types_slot,
0166       NGX_HTTP_LOC_CONF_OFFSET,
0167       offsetof(ngx_http_charset_loc_conf_t, types_keys),
0168       &ngx_http_charset_default_types[0] },
0169 
0170     { ngx_string("charset_map"),
0171       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
0172       ngx_http_charset_map_block,
0173       NGX_HTTP_MAIN_CONF_OFFSET,
0174       0,
0175       NULL },
0176 
0177       ngx_null_command
0178 };
0179 
0180 
0181 static ngx_http_module_t  ngx_http_charset_filter_module_ctx = {
0182     NULL,                                  /* preconfiguration */
0183     ngx_http_charset_postconfiguration,    /* postconfiguration */
0184 
0185     ngx_http_charset_create_main_conf,     /* create main configuration */
0186     NULL,                                  /* init main configuration */
0187 
0188     NULL,                                  /* create server configuration */
0189     NULL,                                  /* merge server configuration */
0190 
0191     ngx_http_charset_create_loc_conf,      /* create location configuration */
0192     ngx_http_charset_merge_loc_conf        /* merge location configuration */
0193 };
0194 
0195 
0196 ngx_module_t  ngx_http_charset_filter_module = {
0197     NGX_MODULE_V1,
0198     &ngx_http_charset_filter_module_ctx,   /* module context */
0199     ngx_http_charset_filter_commands,      /* module directives */
0200     NGX_HTTP_MODULE,                       /* module type */
0201     NULL,                                  /* init master */
0202     NULL,                                  /* init module */
0203     NULL,                                  /* init process */
0204     NULL,                                  /* init thread */
0205     NULL,                                  /* exit thread */
0206     NULL,                                  /* exit process */
0207     NULL,                                  /* exit master */
0208     NGX_MODULE_V1_PADDING
0209 };
0210 
0211 
0212 static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
0213 static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
0214 
0215 
0216 static ngx_int_t
0217 ngx_http_charset_header_filter(ngx_http_request_t *r)
0218 {
0219     ngx_int_t                      charset, source_charset;
0220     ngx_str_t                      dst, src;
0221     ngx_http_charset_t            *charsets;
0222     ngx_http_charset_main_conf_t  *mcf;
0223 
0224     if (r == r->main) {
0225         charset = ngx_http_destination_charset(r, &dst);
0226 
0227     } else {
0228         charset = ngx_http_main_request_charset(r, &dst);
0229     }
0230 
0231     if (charset == NGX_ERROR) {
0232         return NGX_ERROR;
0233     }
0234 
0235     if (charset == NGX_DECLINED) {
0236         return ngx_http_next_header_filter(r);
0237     }
0238 
0239     /* charset: charset index or NGX_HTTP_NO_CHARSET */
0240 
0241     source_charset = ngx_http_source_charset(r, &src);
0242 
0243     if (source_charset == NGX_ERROR) {
0244         return NGX_ERROR;
0245     }
0246 
0247     /*
0248      * source_charset: charset index, NGX_HTTP_NO_CHARSET,
0249      *                 or NGX_HTTP_CHARSET_OFF
0250      */
0251 
0252     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0253                    "charset: \"%V\" > \"%V\"", &src, &dst);
0254 
0255     if (source_charset == NGX_HTTP_CHARSET_OFF) {
0256         ngx_http_set_charset(r, &dst);
0257 
0258         return ngx_http_next_header_filter(r);
0259     }
0260 
0261     if (charset == NGX_HTTP_NO_CHARSET
0262         || source_charset == NGX_HTTP_NO_CHARSET)
0263     {
0264         if (source_charset != charset
0265             || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
0266         {
0267             goto no_charset_map;
0268         }
0269 
0270         ngx_http_set_charset(r, &dst);
0271 
0272         return ngx_http_next_header_filter(r);
0273     }
0274 
0275     if (source_charset == charset) {
0276         r->headers_out.content_type.len = r->headers_out.content_type_len;
0277 
0278         ngx_http_set_charset(r, &dst);
0279 
0280         return ngx_http_next_header_filter(r);
0281     }
0282 
0283     /* source_charset != charset */
0284 
0285     if (r->headers_out.content_encoding
0286         && r->headers_out.content_encoding->value.len)
0287     {
0288         return ngx_http_next_header_filter(r);
0289     }
0290 
0291     mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
0292     charsets = mcf->charsets.elts;
0293 
0294     if (charsets[source_charset].tables == NULL
0295         || charsets[source_charset].tables[charset] == NULL)
0296     {
0297         goto no_charset_map;
0298     }
0299 
0300     r->headers_out.content_type.len = r->headers_out.content_type_len;
0301 
0302     ngx_http_set_charset(r, &dst);
0303 
0304     return ngx_http_charset_ctx(r, charsets, charset, source_charset);
0305 
0306 no_charset_map:
0307 
0308     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0309                   "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
0310                   &src, &dst);
0311 
0312     return ngx_http_next_header_filter(r);
0313 }
0314 
0315 
0316 static ngx_int_t
0317 ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
0318 {
0319     ngx_int_t                      charset;
0320     ngx_http_charset_t            *charsets;
0321     ngx_http_variable_value_t     *vv;
0322     ngx_http_charset_loc_conf_t   *mlcf;
0323     ngx_http_charset_main_conf_t  *mcf;
0324 
0325     if (r->headers_out.content_type.len == 0) {
0326         return NGX_DECLINED;
0327     }
0328 
0329     if (r->headers_out.override_charset
0330         && r->headers_out.override_charset->len)
0331     {
0332         *name = *r->headers_out.override_charset;
0333 
0334         charset = ngx_http_get_charset(r, name);
0335 
0336         if (charset != NGX_HTTP_NO_CHARSET) {
0337             return charset;
0338         }
0339 
0340         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
0341                       "unknown charset \"%V\" to override", name);
0342 
0343         return NGX_DECLINED;
0344     }
0345 
0346     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
0347     charset = mlcf->charset;
0348 
0349     if (charset == NGX_HTTP_CHARSET_OFF) {
0350         return NGX_DECLINED;
0351     }
0352 
0353     if (r->headers_out.charset.len) {
0354         if (mlcf->override_charset == 0) {
0355             return NGX_DECLINED;
0356         }
0357 
0358     } else {
0359         if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
0360             return NGX_DECLINED;
0361         }
0362     }
0363 
0364     if (charset < NGX_HTTP_CHARSET_VAR) {
0365         mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
0366         charsets = mcf->charsets.elts;
0367         *name = charsets[charset].name;
0368         return charset;
0369     }
0370 
0371     vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
0372 
0373     if (vv == NULL || vv->not_found) {
0374         return NGX_ERROR;
0375     }
0376 
0377     name->len = vv->len;
0378     name->data = vv->data;
0379 
0380     return ngx_http_get_charset(r, name);
0381 }
0382 
0383 
0384 static ngx_int_t
0385 ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)
0386 {
0387     ngx_int_t                charset;
0388     ngx_str_t               *main_charset;
0389     ngx_http_charset_ctx_t  *ctx;
0390 
0391     ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);
0392 
0393     if (ctx) {
0394         *src = ctx->charset_name;
0395         return ctx->charset;
0396     }
0397 
0398     main_charset = &r->main->headers_out.charset;
0399 
0400     if (main_charset->len == 0) {
0401         return NGX_DECLINED;
0402     }
0403 
0404     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
0405     if (ctx == NULL) {
0406         return NGX_ERROR;
0407     }
0408 
0409     ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);
0410 
0411     charset = ngx_http_get_charset(r, main_charset);
0412 
0413     ctx->charset = charset;
0414     ctx->charset_name = *main_charset;
0415     *src = *main_charset;
0416 
0417     return charset;
0418 }
0419 
0420 
0421 static ngx_int_t
0422 ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)
0423 {
0424     ngx_int_t                      charset;
0425     ngx_http_charset_t            *charsets;
0426     ngx_http_variable_value_t     *vv;
0427     ngx_http_charset_loc_conf_t   *lcf;
0428     ngx_http_charset_main_conf_t  *mcf;
0429 
0430     if (r->headers_out.charset.len) {
0431         *name = r->headers_out.charset;
0432         return ngx_http_get_charset(r, name);
0433     }
0434 
0435     lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
0436 
0437     charset = lcf->source_charset;
0438 
0439     if (charset == NGX_HTTP_CHARSET_OFF) {
0440         name->len = 0;
0441         return charset;
0442     }
0443 
0444     if (charset < NGX_HTTP_CHARSET_VAR) {
0445         mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
0446         charsets = mcf->charsets.elts;
0447         *name = charsets[charset].name;
0448         return charset;
0449     }
0450 
0451     vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
0452 
0453     if (vv == NULL || vv->not_found) {
0454         return NGX_ERROR;
0455     }
0456 
0457     name->len = vv->len;
0458     name->data = vv->data;
0459 
0460     return ngx_http_get_charset(r, name);
0461 }
0462 
0463 
0464 static ngx_int_t
0465 ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)
0466 {
0467     ngx_uint_t                     i, n;
0468     ngx_http_charset_t            *charset;
0469     ngx_http_charset_main_conf_t  *mcf;
0470 
0471     mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
0472 
0473     charset = mcf->charsets.elts;
0474     n = mcf->charsets.nelts;
0475 
0476     for (i = 0; i < n; i++) {
0477         if (charset[i].name.len != name->len) {
0478             continue;
0479         }
0480 
0481         if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {
0482             return i;
0483         }
0484     }
0485 
0486     return NGX_HTTP_NO_CHARSET;
0487 }
0488 
0489 
0490 static ngx_inline void
0491 ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)
0492 {
0493     if (r != r->main) {
0494         return;
0495     }
0496 
0497     if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
0498         || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
0499     {
0500         /*
0501          * do not set charset for the redirect because NN 4.x
0502          * use this charset instead of the next page charset
0503          */
0504 
0505         r->headers_out.charset.len = 0;
0506         return;
0507     }
0508 
0509     r->headers_out.charset = *charset;
0510 }
0511 
0512 
0513 static ngx_int_t
0514 ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,
0515     ngx_int_t charset, ngx_int_t source_charset)
0516 {
0517     ngx_http_charset_ctx_t  *ctx;
0518 
0519     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
0520     if (ctx == NULL) {
0521         return NGX_ERROR;
0522     }
0523 
0524     ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);
0525 
0526     ctx->table = charsets[source_charset].tables[charset];
0527     ctx->charset = charset;
0528     ctx->charset_name = charsets[charset].name;
0529     ctx->length = charsets[charset].length;
0530     ctx->from_utf8 = charsets[source_charset].utf8;
0531     ctx->to_utf8 = charsets[charset].utf8;
0532 
0533     r->filter_need_in_memory = 1;
0534 
0535     if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {
0536         ngx_http_clear_content_length(r);
0537 
0538     } else {
0539         r->filter_need_temporary = 1;
0540     }
0541 
0542     return ngx_http_next_header_filter(r);
0543 }
0544 
0545 
0546 static ngx_int_t
0547 ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
0548 {
0549     ngx_int_t                rc;
0550     ngx_buf_t               *b;
0551     ngx_chain_t             *cl, *out, **ll;
0552     ngx_http_charset_ctx_t  *ctx;
0553 
0554     ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);
0555 
0556     if (ctx == NULL || ctx->table == NULL) {
0557         return ngx_http_next_body_filter(r, in);
0558     }
0559 
0560     if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {
0561 
0562         out = NULL;
0563         ll = &out;
0564 
0565         for (cl = in; cl; cl = cl->next) {
0566             b = cl->buf;
0567 
0568             if (ngx_buf_size(b) == 0) {
0569 
0570                 *ll = ngx_alloc_chain_link(r->pool);
0571                 if (*ll == NULL) {
0572                     return NGX_ERROR;
0573                 }
0574 
0575                 (*ll)->buf = b;
0576                 (*ll)->next = NULL;
0577 
0578                 ll = &(*ll)->next;
0579 
0580                 continue;
0581             }
0582 
0583             if (ctx->to_utf8) {
0584                 *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);
0585 
0586             } else {
0587                 *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);
0588             }
0589 
0590             if (*ll == NULL) {
0591                 return NGX_ERROR;
0592             }
0593 
0594             while (*ll) {
0595                 ll = &(*ll)->next;
0596             }
0597         }
0598 
0599         rc = ngx_http_next_body_filter(r, out);
0600 
0601         if (out) {
0602             if (ctx->busy == NULL) {
0603                 ctx->busy = out;
0604 
0605             } else {
0606                 for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
0607                 cl->next = out;
0608             }
0609         }
0610 
0611         while (ctx->busy) {
0612 
0613             cl = ctx->busy;
0614             b = cl->buf;
0615 
0616             if (ngx_buf_size(b) != 0) {
0617                 break;
0618             }
0619 
0620             ctx->busy = cl->next;
0621 
0622             if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
0623                 continue;
0624             }
0625 
0626             if (b->shadow) {
0627                 b->shadow->pos = b->shadow->last;
0628             }
0629 
0630             if (b->pos) {
0631                 cl->next = ctx->free_buffers;
0632                 ctx->free_buffers = cl;
0633                 continue;
0634             }
0635 
0636             cl->next = ctx->free_bufs;
0637             ctx->free_bufs = cl;
0638         }
0639 
0640         return rc;
0641     }
0642 
0643     for (cl = in; cl; cl = cl->next) {
0644         (void) ngx_http_charset_recode(cl->buf, ctx->table);
0645     }
0646 
0647     return ngx_http_next_body_filter(r, in);
0648 }
0649 
0650 
0651 static ngx_uint_t
0652 ngx_http_charset_recode(ngx_buf_t *b, u_char *table)
0653 {
0654     u_char  *p, *last;
0655 
0656     last = b->last;
0657 
0658     for (p = b->pos; p < last; p++) {
0659 
0660         if (*p != table[*p]) {
0661             goto recode;
0662         }
0663     }
0664 
0665     return 0;
0666 
0667 recode:
0668 
0669     do {
0670         if (*p != table[*p]) {
0671             *p = table[*p];
0672         }
0673 
0674         p++;
0675 
0676     } while (p < last);
0677 
0678     b->in_file = 0;
0679 
0680     return 1;
0681 }
0682 
0683 
0684 static ngx_chain_t *
0685 ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
0686     ngx_http_charset_ctx_t *ctx)
0687 {
0688     size_t        len, size;
0689     u_char        c, *p, *src, *dst, *saved, **table;
0690     uint32_t      n;
0691     ngx_buf_t    *b;
0692     ngx_uint_t    i;
0693     ngx_chain_t  *out, *cl, **ll;
0694 
0695     src = buf->pos;
0696 
0697     if (ctx->saved_len == 0) {
0698 
0699         for ( /* void */ ; src < buf->last; src++) {
0700 
0701             if (*src < 0x80) {
0702                 continue;
0703             }
0704 
0705             len = src - buf->pos;
0706 
0707             if (len > 512) {
0708                 out = ngx_http_charset_get_buf(pool, ctx);
0709                 if (out == NULL) {
0710                     return NULL;
0711                 }
0712 
0713                 b = out->buf;
0714 
0715                 b->temporary = buf->temporary;
0716                 b->memory = buf->memory;
0717                 b->mmap = buf->mmap;
0718                 b->flush = buf->flush;
0719 
0720                 b->pos = buf->pos;
0721                 b->last = src;
0722 
0723                 out->buf = b;
0724                 out->next = NULL;
0725 
0726                 size = buf->last - src;
0727 
0728                 saved = src;
0729                 n = ngx_utf8_decode(&saved, size);
0730 
0731                 if (n == 0xfffffffe) {
0732                     /* incomplete UTF-8 symbol */
0733 
0734                     ngx_memcpy(ctx->saved, src, size);
0735                     ctx->saved_len = size;
0736 
0737                     b->shadow = buf;
0738 
0739                     return out;
0740                 }
0741 
0742             } else {
0743                 out = NULL;
0744                 size = len + buf->last - src;
0745                 src = buf->pos;
0746             }
0747 
0748             if (size < NGX_HTML_ENTITY_LEN) {
0749                 size += NGX_HTML_ENTITY_LEN;
0750             }
0751 
0752             cl = ngx_http_charset_get_buffer(pool, ctx, size);
0753             if (cl == NULL) {
0754                 return NULL;
0755             }
0756 
0757             if (out) {
0758                 out->next = cl;
0759 
0760             } else {
0761                 out = cl;
0762             }
0763 
0764             b = cl->buf;
0765             dst = b->pos;
0766 
0767             goto recode;
0768         }
0769 
0770         out = ngx_alloc_chain_link(pool);
0771         if (out == NULL) {
0772             return NULL;
0773         }
0774 
0775         out->buf = buf;
0776         out->next = NULL;
0777 
0778         return out;
0779     }
0780 
0781     /* process incomplete UTF sequence from previous buffer */
0782 
0783     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,
0784                    "http charset utf saved: %z", ctx->saved_len);
0785 
0786     p = src;
0787 
0788     for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {
0789         ctx->saved[i] = *p++;
0790 
0791         if (p == buf->last) {
0792             break;
0793         }
0794     }
0795 
0796     saved = ctx->saved;
0797     n = ngx_utf8_decode(&saved, i);
0798 
0799     c = '\0';
0800 
0801     if (n < 0x10000) {
0802         table = (u_char **) ctx->table;
0803         p = table[n >> 8];
0804 
0805         if (p) {
0806             c = p[n & 0xff];
0807         }
0808 
0809     } else if (n == 0xfffffffe) {
0810 
0811         /* incomplete UTF-8 symbol */
0812 
0813         if (i < NGX_UTF_LEN) {
0814             out = ngx_http_charset_get_buf(pool, ctx);
0815             if (out == NULL) {
0816                 return NULL;
0817             }
0818 
0819             b = out->buf;
0820 
0821             b->pos = buf->pos;
0822             b->last = buf->last;
0823             b->sync = 1;
0824             b->shadow = buf;
0825 
0826             ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);
0827             ctx->saved_len += i;
0828 
0829             return out;
0830         }
0831     }
0832 
0833     size = buf->last - buf->pos;
0834 
0835     if (size < NGX_HTML_ENTITY_LEN) {
0836         size += NGX_HTML_ENTITY_LEN;
0837     }
0838 
0839     cl = ngx_http_charset_get_buffer(pool, ctx, size);
0840     if (cl == NULL) {
0841         return NULL;
0842     }
0843 
0844     out = cl;
0845 
0846     b = cl->buf;
0847     dst = b->pos;
0848 
0849     if (c) {
0850         *dst++ = c;
0851 
0852     } else if (n == 0xfffffffe) {
0853         *dst++ = '?';
0854 
0855         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
0856                        "http charset invalid utf 0");
0857 
0858         saved = &ctx->saved[NGX_UTF_LEN];
0859 
0860     } else if (n > 0x10ffff) {
0861         *dst++ = '?';
0862 
0863         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
0864                        "http charset invalid utf 1");
0865 
0866     } else {
0867         dst = ngx_sprintf(dst, "&#%uD;", n);
0868     }
0869 
0870     src += (saved - ctx->saved) - ctx->saved_len;
0871     ctx->saved_len = 0;
0872 
0873 recode:
0874 
0875     ll = &cl->next;
0876 
0877     table = (u_char **) ctx->table;
0878 
0879     while (src < buf->last) {
0880 
0881         if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {
0882             b->last = dst;
0883 
0884             size = buf->last - src + NGX_HTML_ENTITY_LEN;
0885 
0886             cl = ngx_http_charset_get_buffer(pool, ctx, size);
0887             if (cl == NULL) {
0888                 return NULL;
0889             }
0890 
0891             *ll = cl;
0892             ll = &cl->next;
0893 
0894             b = cl->buf;
0895             dst = b->pos;
0896         }
0897 
0898         if (*src < 0x80) {
0899             *dst++ = *src++;
0900             continue;
0901         }
0902 
0903         len = buf->last - src;
0904 
0905         n = ngx_utf8_decode(&src, len);
0906 
0907         if (n < 0x10000) {
0908 
0909             p = table[n >> 8];
0910 
0911             if (p) {
0912                 c = p[n & 0xff];
0913 
0914                 if (c) {
0915                     *dst++ = c;
0916                     continue;
0917                 }
0918             }
0919 
0920             dst = ngx_sprintf(dst, "&#%uD;", n);
0921 
0922             continue;
0923         }
0924 
0925         if (n == 0xfffffffe) {
0926             /* incomplete UTF-8 symbol */
0927 
0928             ngx_memcpy(ctx->saved, src, len);
0929             ctx->saved_len = len;
0930 
0931             if (b->pos == dst) {
0932                 b->sync = 1;
0933                 b->temporary = 0;
0934             }
0935 
0936             break;
0937         }
0938 
0939         if (n > 0x10ffff) {
0940             *dst++ = '?';
0941 
0942             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
0943                            "http charset invalid utf 2");
0944 
0945             continue;
0946         }
0947 
0948         /* n > 0xffff */
0949 
0950         dst = ngx_sprintf(dst, "&#%uD;", n);
0951     }
0952 
0953     b->last = dst;
0954 
0955     b->last_buf = buf->last_buf;
0956     b->last_in_chain = buf->last_in_chain;
0957     b->flush = buf->flush;
0958 
0959     b->shadow = buf;
0960 
0961     return out;
0962 }
0963 
0964 
0965 static ngx_chain_t *
0966 ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
0967     ngx_http_charset_ctx_t *ctx)
0968 {
0969     size_t        len, size;
0970     u_char       *p, *src, *dst, *table;
0971     ngx_buf_t    *b;
0972     ngx_chain_t  *out, *cl, **ll;
0973 
0974     table = ctx->table;
0975 
0976     for (src = buf->pos; src < buf->last; src++) {
0977         if (table[*src * NGX_UTF_LEN] == '\1') {
0978             continue;
0979         }
0980 
0981         goto recode;
0982     }
0983 
0984     out = ngx_alloc_chain_link(pool);
0985     if (out == NULL) {
0986         return NULL;
0987     }
0988 
0989     out->buf = buf;
0990     out->next = NULL;
0991 
0992     return out;
0993 
0994 recode:
0995 
0996     /*
0997      * we assume that there are about half of characters to be recoded,
0998      * so we preallocate "size / 2 + size / 2 * ctx->length"
0999      */
1000 
1001     len = src - buf->pos;
1002 
1003     if (len > 512) {
1004         out = ngx_http_charset_get_buf(pool, ctx);
1005         if (out == NULL) {
1006             return NULL;
1007         }
1008 
1009         b = out->buf;
1010 
1011         b->temporary = buf->temporary;
1012         b->memory = buf->memory;
1013         b->mmap = buf->mmap;
1014         b->flush = buf->flush;
1015 
1016         b->pos = buf->pos;
1017         b->last = src;
1018 
1019         out->buf = b;
1020         out->next = NULL;
1021 
1022         size = buf->last - src;
1023         size = size / 2 + size / 2 * ctx->length;
1024 
1025     } else {
1026         out = NULL;
1027 
1028         size = buf->last - src;
1029         size = len + size / 2 + size / 2 * ctx->length;
1030 
1031         src = buf->pos;
1032     }
1033 
1034     cl = ngx_http_charset_get_buffer(pool, ctx, size);
1035     if (cl == NULL) {
1036         return NULL;
1037     }
1038 
1039     if (out) {
1040         out->next = cl;
1041 
1042     } else {
1043         out = cl;
1044     }
1045 
1046     ll = &cl->next;
1047 
1048     b = cl->buf;
1049     dst = b->pos;
1050 
1051     while (src < buf->last) {
1052 
1053         p = &table[*src++ * NGX_UTF_LEN];
1054         len = *p++;
1055 
1056         if ((size_t) (b->end - dst) < len) {
1057             b->last = dst;
1058 
1059             size = buf->last - src;
1060             size = len + size / 2 + size / 2 * ctx->length;
1061 
1062             cl = ngx_http_charset_get_buffer(pool, ctx, size);
1063             if (cl == NULL) {
1064                 return NULL;
1065             }
1066 
1067             *ll = cl;
1068             ll = &cl->next;
1069 
1070             b = cl->buf;
1071             dst = b->pos;
1072         }
1073 
1074         while (len) {
1075             *dst++ = *p++;
1076             len--;
1077         }
1078     }
1079 
1080     b->last = dst;
1081 
1082     b->last_buf = buf->last_buf;
1083     b->last_in_chain = buf->last_in_chain;
1084     b->flush = buf->flush;
1085 
1086     b->shadow = buf;
1087 
1088     return out;
1089 }
1090 
1091 
1092 static ngx_chain_t *
1093 ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)
1094 {
1095     ngx_chain_t  *cl;
1096 
1097     cl = ctx->free_bufs;
1098 
1099     if (cl) {
1100         ctx->free_bufs = cl->next;
1101 
1102         cl->buf->shadow = NULL;
1103         cl->next = NULL;
1104 
1105         return cl;
1106     }
1107 
1108     cl = ngx_alloc_chain_link(pool);
1109     if (cl == NULL) {
1110         return NULL;
1111     }
1112 
1113     cl->buf = ngx_calloc_buf(pool);
1114     if (cl->buf == NULL) {
1115         return NULL;
1116     }
1117 
1118     cl->next = NULL;
1119 
1120     cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
1121 
1122     return cl;
1123 }
1124 
1125 
1126 static ngx_chain_t *
1127 ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,
1128     size_t size)
1129 {
1130     ngx_buf_t    *b;
1131     ngx_chain_t  *cl, **ll;
1132 
1133     for (ll = &ctx->free_buffers, cl = ctx->free_buffers;
1134          cl;
1135          ll = &cl->next, cl = cl->next)
1136     {
1137         b = cl->buf;
1138 
1139         if ((size_t) (b->end - b->start) >= size) {
1140             *ll = cl->next;
1141             cl->next = NULL;
1142 
1143             b->pos = b->start;
1144             b->temporary = 1;
1145             b->shadow = NULL;
1146 
1147             return cl;
1148         }
1149     }
1150 
1151     cl = ngx_alloc_chain_link(pool);
1152     if (cl == NULL) {
1153         return NULL;
1154     }
1155 
1156     cl->buf = ngx_create_temp_buf(pool, size);
1157     if (cl->buf == NULL) {
1158         return NULL;
1159     }
1160 
1161     cl->next = NULL;
1162 
1163     cl->buf->temporary = 1;
1164     cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
1165 
1166     return cl;
1167 }
1168 
1169 
1170 static char *
1171 ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1172 {
1173     ngx_http_charset_main_conf_t  *mcf = conf;
1174 
1175     char                         *rv;
1176     u_char                       *p, *dst2src, **pp;
1177     ngx_int_t                     src, dst;
1178     ngx_uint_t                    i, n;
1179     ngx_str_t                    *value;
1180     ngx_conf_t                    pvcf;
1181     ngx_http_charset_t           *charset;
1182     ngx_http_charset_tables_t    *table;
1183     ngx_http_charset_conf_ctx_t   ctx;
1184 
1185     value = cf->args->elts;
1186 
1187     src = ngx_http_add_charset(&mcf->charsets, &value[1]);
1188     if (src == NGX_ERROR) {
1189         return NGX_CONF_ERROR;
1190     }
1191 
1192     dst = ngx_http_add_charset(&mcf->charsets, &value[2]);
1193     if (dst == NGX_ERROR) {
1194         return NGX_CONF_ERROR;
1195     }
1196 
1197     if (src == dst) {
1198         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1199                            "\"charset_map\" between the same charsets "
1200                            "\"%V\" and \"%V\"", &value[1], &value[2]);
1201         return NGX_CONF_ERROR;
1202     }
1203 
1204     table = mcf->tables.elts;
1205     for (i = 0; i < mcf->tables.nelts; i++) {
1206         if ((src == table->src && dst == table->dst)
1207              || (src == table->dst && dst == table->src))
1208         {
1209             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1210                                "duplicate \"charset_map\" between "
1211                                "\"%V\" and \"%V\"", &value[1], &value[2]);
1212             return NGX_CONF_ERROR;
1213         }
1214     }
1215 
1216     table = ngx_array_push(&mcf->tables);
1217     if (table == NULL) {
1218         return NGX_CONF_ERROR;
1219     }
1220 
1221     table->src = src;
1222     table->dst = dst;
1223 
1224     if (ngx_strcasecmp(value[2].data, (u_char *) "utf-8") == 0) {
1225         table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);
1226         if (table->src2dst == NULL) {
1227             return NGX_CONF_ERROR;
1228         }
1229 
1230         table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));
1231         if (table->dst2src == NULL) {
1232             return NGX_CONF_ERROR;
1233         }
1234 
1235         dst2src = ngx_pcalloc(cf->pool, 256);
1236         if (dst2src == NULL) {
1237             return NGX_CONF_ERROR;
1238         }
1239 
1240         pp = (u_char **) &table->dst2src[0];
1241         pp[0] = dst2src;
1242 
1243         for (i = 0; i < 128; i++) {
1244             p = &table->src2dst[i * NGX_UTF_LEN];
1245             p[0] = '\1';
1246             p[1] = (u_char) i;
1247             dst2src[i] = (u_char) i;
1248         }
1249 
1250         for (/* void */; i < 256; i++) {
1251             p = &table->src2dst[i * NGX_UTF_LEN];
1252             p[0] = '\1';
1253             p[1] = '?';
1254         }
1255 
1256     } else {
1257         table->src2dst = ngx_palloc(cf->pool, 256);
1258         if (table->src2dst == NULL) {
1259             return NGX_CONF_ERROR;
1260         }
1261 
1262         table->dst2src = ngx_palloc(cf->pool, 256);
1263         if (table->dst2src == NULL) {
1264             return NGX_CONF_ERROR;
1265         }
1266 
1267         for (i = 0; i < 128; i++) {
1268             table->src2dst[i] = (u_char) i;
1269             table->dst2src[i] = (u_char) i;
1270         }
1271 
1272         for (/* void */; i < 256; i++) {
1273             table->src2dst[i] = '?';
1274             table->dst2src[i] = '?';
1275         }
1276     }
1277 
1278     charset = mcf->charsets.elts;
1279 
1280     ctx.table = table;
1281     ctx.charset = &charset[dst];
1282     ctx.characters = 0;
1283 
1284     pvcf = *cf;
1285     cf->ctx = &ctx;
1286     cf->handler = ngx_http_charset_map;
1287     cf->handler_conf = conf;
1288 
1289     rv = ngx_conf_parse(cf, NULL);
1290 
1291     *cf = pvcf;
1292 
1293     if (ctx.characters) {
1294         n = ctx.charset->length;
1295         ctx.charset->length /= ctx.characters;
1296 
1297         if (((n * 10) / ctx.characters) % 10 > 4) {
1298             ctx.charset->length++;
1299         }
1300     }
1301 
1302     return rv;
1303 }
1304 
1305 
1306 static char *
1307 ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
1308 {
1309     u_char                       *p, *dst2src, **pp;
1310     uint32_t                      n;
1311     ngx_int_t                     src, dst;
1312     ngx_str_t                    *value;
1313     ngx_uint_t                    i;
1314     ngx_http_charset_tables_t    *table;
1315     ngx_http_charset_conf_ctx_t  *ctx;
1316 
1317     if (cf->args->nelts != 2) {
1318         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");
1319         return NGX_CONF_ERROR;
1320     }
1321 
1322     value = cf->args->elts;
1323 
1324     src = ngx_hextoi(value[0].data, value[0].len);
1325     if (src == NGX_ERROR || src > 255) {
1326         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1327                            "invalid value \"%V\"", &value[0]);
1328         return NGX_CONF_ERROR;
1329     }
1330 
1331     ctx = cf->ctx;
1332     table = ctx->table;
1333 
1334     if (ctx->charset->utf8) {
1335         p = &table->src2dst[src * NGX_UTF_LEN];
1336 
1337         *p++ = (u_char) (value[1].len / 2);
1338 
1339         for (i = 0; i < value[1].len; i += 2) {
1340             dst = ngx_hextoi(&value[1].data[i], 2);
1341             if (dst == NGX_ERROR || dst > 255) {
1342                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1343                                    "invalid value \"%V\"", &value[1]);
1344                 return NGX_CONF_ERROR;
1345             }
1346 
1347             *p++ = (u_char) dst;
1348         }
1349 
1350         i /= 2;
1351 
1352         ctx->charset->length += i;
1353         ctx->characters++;
1354 
1355         p = &table->src2dst[src * NGX_UTF_LEN] + 1;
1356 
1357         n = ngx_utf8_decode(&p, i);
1358 
1359         if (n > 0xffff) {
1360             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1361                                "invalid value \"%V\"", &value[1]);
1362             return NGX_CONF_ERROR;
1363         }
1364 
1365         pp = (u_char **) &table->dst2src[0];
1366 
1367         dst2src = pp[n >> 8];
1368 
1369         if (dst2src == NULL) {
1370             dst2src = ngx_pcalloc(cf->pool, 256);
1371             if (dst2src == NULL) {
1372                 return NGX_CONF_ERROR;
1373             }
1374 
1375             pp[n >> 8] = dst2src;
1376         }
1377 
1378         dst2src[n & 0xff] = (u_char) src;
1379 
1380     } else {
1381         dst = ngx_hextoi(value[1].data, value[1].len);
1382         if (dst == NGX_ERROR || dst > 255) {
1383             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1384                                "invalid value \"%V\"", &value[1]);
1385             return NGX_CONF_ERROR;
1386         }
1387 
1388         table->src2dst[src] = (u_char) dst;
1389         table->dst2src[dst] = (u_char) src;
1390     }
1391 
1392     return NGX_CONF_OK;
1393 }
1394 
1395 
1396 static char *
1397 ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1398 {
1399     char  *p = conf;
1400 
1401     ngx_int_t                     *cp;
1402     ngx_str_t                     *value, var;
1403     ngx_http_charset_main_conf_t  *mcf;
1404 
1405     cp = (ngx_int_t *) (p + cmd->offset);
1406 
1407     if (*cp != NGX_CONF_UNSET) {
1408         return "is duplicate";
1409     }
1410 
1411     value = cf->args->elts;
1412 
1413     if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)
1414         && ngx_strcmp(value[1].data, "off") == 0)
1415     {
1416         *cp = NGX_HTTP_CHARSET_OFF;
1417         return NGX_CONF_OK;
1418     }
1419 
1420 
1421     if (value[1].data[0] == '$') {
1422         var.len = value[1].len - 1;
1423         var.data = value[1].data + 1;
1424 
1425         *cp = ngx_http_get_variable_index(cf, &var);
1426 
1427         if (*cp == NGX_ERROR) {
1428             return NGX_CONF_ERROR;
1429         }
1430 
1431         *cp += NGX_HTTP_CHARSET_VAR;
1432 
1433         return NGX_CONF_OK;
1434     }
1435 
1436     mcf = ngx_http_conf_get_module_main_conf(cf,
1437                                              ngx_http_charset_filter_module);
1438 
1439     *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);
1440     if (*cp == NGX_ERROR) {
1441         return NGX_CONF_ERROR;
1442     }
1443 
1444     return NGX_CONF_OK;
1445 }
1446 
1447 
1448 static ngx_int_t
1449 ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)
1450 {
1451     ngx_uint_t           i;
1452     ngx_http_charset_t  *c;
1453 
1454     c = charsets->elts;
1455     for (i = 0; i < charsets->nelts; i++) {
1456         if (name->len != c[i].name.len) {
1457             continue;
1458         }
1459 
1460         if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {
1461             break;
1462         }
1463     }
1464 
1465     if (i < charsets->nelts) {
1466         return i;
1467     }
1468 
1469     c = ngx_array_push(charsets);
1470     if (c == NULL) {
1471         return NGX_ERROR;
1472     }
1473 
1474     c->tables = NULL;
1475     c->name = *name;
1476     c->length = 0;
1477 
1478     if (ngx_strcasecmp(name->data, (u_char *) "utf-8") == 0) {
1479         c->utf8 = 1;
1480 
1481     } else {
1482         c->utf8 = 0;
1483     }
1484 
1485     return i;
1486 }
1487 
1488 
1489 static void *
1490 ngx_http_charset_create_main_conf(ngx_conf_t *cf)
1491 {
1492     ngx_http_charset_main_conf_t  *mcf;
1493 
1494     mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));
1495     if (mcf == NULL) {
1496         return NULL;
1497     }
1498 
1499     if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
1500         != NGX_OK)
1501     {
1502         return NULL;
1503     }
1504 
1505     if (ngx_array_init(&mcf->tables, cf->pool, 1,
1506                        sizeof(ngx_http_charset_tables_t))
1507         != NGX_OK)
1508     {
1509         return NULL;
1510     }
1511 
1512     if (ngx_array_init(&mcf->recodes, cf->pool, 2,
1513                        sizeof(ngx_http_charset_recode_t))
1514         != NGX_OK)
1515     {
1516         return NULL;
1517     }
1518 
1519     return mcf;
1520 }
1521 
1522 
1523 static void *
1524 ngx_http_charset_create_loc_conf(ngx_conf_t *cf)
1525 {
1526     ngx_http_charset_loc_conf_t  *lcf;
1527 
1528     lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));
1529     if (lcf == NULL) {
1530         return NULL;
1531     }
1532 
1533     /*
1534      * set by ngx_pcalloc():
1535      *
1536      *     lcf->types = { NULL };
1537      *     lcf->types_keys = NULL;
1538      */
1539 
1540     lcf->charset = NGX_CONF_UNSET;
1541     lcf->source_charset = NGX_CONF_UNSET;
1542     lcf->override_charset = NGX_CONF_UNSET;
1543 
1544     return lcf;
1545 }
1546 
1547 
1548 static char *
1549 ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1550 {
1551     ngx_http_charset_loc_conf_t *prev = parent;
1552     ngx_http_charset_loc_conf_t *conf = child;
1553 
1554     ngx_uint_t                     i;
1555     ngx_http_charset_recode_t     *recode;
1556     ngx_http_charset_main_conf_t  *mcf;
1557 
1558     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
1559                              &prev->types_keys, &prev->types,
1560                              ngx_http_charset_default_types)
1561         != NGX_OK)
1562     {
1563         return NGX_CONF_ERROR;
1564     }
1565 
1566     ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);
1567     ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);
1568     ngx_conf_merge_value(conf->source_charset, prev->source_charset,
1569                          NGX_HTTP_CHARSET_OFF);
1570 
1571     if (conf->charset == NGX_HTTP_CHARSET_OFF
1572         || conf->source_charset == NGX_HTTP_CHARSET_OFF
1573         || conf->charset == conf->source_charset)
1574     {
1575         return NGX_CONF_OK;
1576     }
1577 
1578     if (conf->source_charset >= NGX_HTTP_CHARSET_VAR
1579         || conf->charset >= NGX_HTTP_CHARSET_VAR)
1580     {
1581         return NGX_CONF_OK;
1582     }
1583 
1584     mcf = ngx_http_conf_get_module_main_conf(cf,
1585                                              ngx_http_charset_filter_module);
1586     recode = mcf->recodes.elts;
1587     for (i = 0; i < mcf->recodes.nelts; i++) {
1588         if (conf->source_charset == recode[i].src
1589             && conf->charset == recode[i].dst)
1590         {
1591             return NGX_CONF_OK;
1592         }
1593     }
1594 
1595     recode = ngx_array_push(&mcf->recodes);
1596     if (recode == NULL) {
1597         return NGX_CONF_ERROR;
1598     }
1599 
1600     recode->src = conf->source_charset;
1601     recode->dst = conf->charset;
1602 
1603     return NGX_CONF_OK;
1604 }
1605 
1606 
1607 static ngx_int_t
1608 ngx_http_charset_postconfiguration(ngx_conf_t *cf)
1609 {
1610     u_char                       **src, **dst;
1611     ngx_int_t                      c;
1612     ngx_uint_t                     i, t;
1613     ngx_http_charset_t            *charset;
1614     ngx_http_charset_recode_t     *recode;
1615     ngx_http_charset_tables_t     *tables;
1616     ngx_http_charset_main_conf_t  *mcf;
1617 
1618     mcf = ngx_http_conf_get_module_main_conf(cf,
1619                                              ngx_http_charset_filter_module);
1620 
1621     recode = mcf->recodes.elts;
1622     tables = mcf->tables.elts;
1623     charset = mcf->charsets.elts;
1624 
1625     for (i = 0; i < mcf->recodes.nelts; i++) {
1626 
1627         c = recode[i].src;
1628 
1629         for (t = 0; t < mcf->tables.nelts; t++) {
1630 
1631             if (c == tables[t].src && recode[i].dst == tables[t].dst) {
1632                 goto next;
1633             }
1634 
1635             if (c == tables[t].dst && recode[i].dst == tables[t].src) {
1636                 goto next;
1637             }
1638         }
1639 
1640         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
1641                    "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
1642                    &charset[c].name, &charset[recode[i].dst].name);
1643         return NGX_ERROR;
1644 
1645     next:
1646         continue;
1647     }
1648 
1649 
1650     for (t = 0; t < mcf->tables.nelts; t++) {
1651 
1652         src = charset[tables[t].src].tables;
1653 
1654         if (src == NULL) {
1655             src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
1656             if (src == NULL) {
1657                 return NGX_ERROR;
1658             }
1659 
1660             charset[tables[t].src].tables = src;
1661         }
1662 
1663         dst = charset[tables[t].dst].tables;
1664 
1665         if (dst == NULL) {
1666             dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
1667             if (dst == NULL) {
1668                 return NGX_ERROR;
1669             }
1670 
1671             charset[tables[t].dst].tables = dst;
1672         }
1673 
1674         src[tables[t].dst] = tables[t].src2dst;
1675         dst[tables[t].src] = tables[t].dst2src;
1676     }
1677 
1678     ngx_http_next_header_filter = ngx_http_top_header_filter;
1679     ngx_http_top_header_filter = ngx_http_charset_header_filter;
1680 
1681     ngx_http_next_body_filter = ngx_http_top_body_filter;
1682     ngx_http_top_body_filter = ngx_http_charset_body_filter;
1683 
1684     return NGX_OK;
1685 }