src/http/modules/ngx_http_charset_filter_module.c - nginx-1.7.10

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_http.h>


  8. #define NGX_HTTP_CHARSET_OFF    -2
  9. #define NGX_HTTP_NO_CHARSET     -3
  10. #define NGX_HTTP_CHARSET_VAR    0x10000

  11. /* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */
  12. #define NGX_UTF_LEN             4

  13. #define NGX_HTML_ENTITY_LEN     (sizeof("&#1114111;") - 1)


  14. typedef struct {
  15.     u_char                    **tables;
  16.     ngx_str_t                   name;

  17.     unsigned                    length:16;
  18.     unsigned                    utf8:1;
  19. } ngx_http_charset_t;


  20. typedef struct {
  21.     ngx_int_t                   src;
  22.     ngx_int_t                   dst;
  23. } ngx_http_charset_recode_t;


  24. typedef struct {
  25.     ngx_int_t                   src;
  26.     ngx_int_t                   dst;
  27.     u_char                     *src2dst;
  28.     u_char                     *dst2src;
  29. } ngx_http_charset_tables_t;


  30. typedef struct {
  31.     ngx_array_t                 charsets;       /* ngx_http_charset_t */
  32.     ngx_array_t                 tables;         /* ngx_http_charset_tables_t */
  33.     ngx_array_t                 recodes;        /* ngx_http_charset_recode_t */
  34. } ngx_http_charset_main_conf_t;


  35. typedef struct {
  36.     ngx_int_t                   charset;
  37.     ngx_int_t                   source_charset;
  38.     ngx_flag_t                  override_charset;

  39.     ngx_hash_t                  types;
  40.     ngx_array_t                *types_keys;
  41. } ngx_http_charset_loc_conf_t;


  42. typedef struct {
  43.     u_char                     *table;
  44.     ngx_int_t                   charset;
  45.     ngx_str_t                   charset_name;

  46.     ngx_chain_t                *busy;
  47.     ngx_chain_t                *free_bufs;
  48.     ngx_chain_t                *free_buffers;

  49.     size_t                      saved_len;
  50.     u_char                      saved[NGX_UTF_LEN];

  51.     unsigned                    length:16;
  52.     unsigned                    from_utf8:1;
  53.     unsigned                    to_utf8:1;
  54. } ngx_http_charset_ctx_t;


  55. typedef struct {
  56.     ngx_http_charset_tables_t  *table;
  57.     ngx_http_charset_t         *charset;
  58.     ngx_uint_t                  characters;
  59. } ngx_http_charset_conf_ctx_t;


  60. static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,
  61.     ngx_str_t *name);
  62. static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,
  63.     ngx_str_t *name);
  64. static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,
  65.     ngx_str_t *name);
  66. static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);
  67. static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,
  68.     ngx_str_t *charset);
  69. static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,
  70.     ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);
  71. static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);
  72. static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,
  73.     ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
  74. static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,
  75.     ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);

  76. static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,
  77.     ngx_http_charset_ctx_t *ctx);
  78. static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,
  79.     ngx_http_charset_ctx_t *ctx, size_t size);

  80. static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
  81.     void *conf);
  82. static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,
  83.     void *conf);

  84. static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,
  85.     void *conf);
  86. static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);

  87. static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);
  88. static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);
  89. static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,
  90.     void *parent, void *child);
  91. static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);


  92. ngx_str_t  ngx_http_charset_default_types[] = {
  93.     ngx_string("text/html"),
  94.     ngx_string("text/xml"),
  95.     ngx_string("text/plain"),
  96.     ngx_string("text/vnd.wap.wml"),
  97.     ngx_string("application/javascript"),
  98.     ngx_string("application/rss+xml"),
  99.     ngx_null_string
  100. };


  101. static ngx_command_t  ngx_http_charset_filter_commands[] = {

  102.     { ngx_string("charset"),
  103.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
  104.                         |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
  105.       ngx_http_set_charset_slot,
  106.       NGX_HTTP_LOC_CONF_OFFSET,
  107.       offsetof(ngx_http_charset_loc_conf_t, charset),
  108.       NULL },

  109.     { ngx_string("source_charset"),
  110.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
  111.                         |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
  112.       ngx_http_set_charset_slot,
  113.       NGX_HTTP_LOC_CONF_OFFSET,
  114.       offsetof(ngx_http_charset_loc_conf_t, source_charset),
  115.       NULL },

  116.     { ngx_string("override_charset"),
  117.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
  118.                         |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
  119.       ngx_conf_set_flag_slot,
  120.       NGX_HTTP_LOC_CONF_OFFSET,
  121.       offsetof(ngx_http_charset_loc_conf_t, override_charset),
  122.       NULL },

  123.     { ngx_string("charset_types"),
  124.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
  125.       ngx_http_types_slot,
  126.       NGX_HTTP_LOC_CONF_OFFSET,
  127.       offsetof(ngx_http_charset_loc_conf_t, types_keys),
  128.       &ngx_http_charset_default_types[0] },

  129.     { ngx_string("charset_map"),
  130.       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
  131.       ngx_http_charset_map_block,
  132.       NGX_HTTP_MAIN_CONF_OFFSET,
  133.       0,
  134.       NULL },

  135.       ngx_null_command
  136. };


  137. static ngx_http_module_t  ngx_http_charset_filter_module_ctx = {
  138.     NULL,                                  /* preconfiguration */
  139.     ngx_http_charset_postconfiguration,    /* postconfiguration */

  140.     ngx_http_charset_create_main_conf,     /* create main configuration */
  141.     NULL,                                  /* init main configuration */

  142.     NULL,                                  /* create server configuration */
  143.     NULL,                                  /* merge server configuration */

  144.     ngx_http_charset_create_loc_conf,      /* create location configuration */
  145.     ngx_http_charset_merge_loc_conf        /* merge location configuration */
  146. };


  147. ngx_module_t  ngx_http_charset_filter_module = {
  148.     NGX_MODULE_V1,
  149.     &ngx_http_charset_filter_module_ctx,   /* module context */
  150.     ngx_http_charset_filter_commands,      /* module directives */
  151.     NGX_HTTP_MODULE,                       /* module type */
  152.     NULL,                                  /* init master */
  153.     NULL,                                  /* init module */
  154.     NULL,                                  /* init process */
  155.     NULL,                                  /* init thread */
  156.     NULL,                                  /* exit thread */
  157.     NULL,                                  /* exit process */
  158.     NULL,                                  /* exit master */
  159.     NGX_MODULE_V1_PADDING
  160. };


  161. static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
  162. static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;


  163. static ngx_int_t
  164. ngx_http_charset_header_filter(ngx_http_request_t *r)
  165. {
  166.     ngx_int_t                      charset, source_charset;
  167.     ngx_str_t                      dst, src;
  168.     ngx_http_charset_t            *charsets;
  169.     ngx_http_charset_main_conf_t  *mcf;

  170.     if (r == r->main) {
  171.         charset = ngx_http_destination_charset(r, &dst);

  172.     } else {
  173.         charset = ngx_http_main_request_charset(r, &dst);
  174.     }

  175.     if (charset == NGX_ERROR) {
  176.         return NGX_ERROR;
  177.     }

  178.     if (charset == NGX_DECLINED) {
  179.         return ngx_http_next_header_filter(r);
  180.     }

  181.     /* charset: charset index or NGX_HTTP_NO_CHARSET */

  182.     source_charset = ngx_http_source_charset(r, &src);

  183.     if (source_charset == NGX_ERROR) {
  184.         return NGX_ERROR;
  185.     }

  186.     /*
  187.      * source_charset: charset index, NGX_HTTP_NO_CHARSET,
  188.      *                 or NGX_HTTP_CHARSET_OFF
  189.      */

  190.     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
  191.                    "charset: \"%V\" > \"%V\"", &src, &dst);

  192.     if (source_charset == NGX_HTTP_CHARSET_OFF) {
  193.         ngx_http_set_charset(r, &dst);

  194.         return ngx_http_next_header_filter(r);
  195.     }

  196.     if (charset == NGX_HTTP_NO_CHARSET
  197.         || source_charset == NGX_HTTP_NO_CHARSET)
  198.     {
  199.         if (source_charset != charset
  200.             || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
  201.         {
  202.             goto no_charset_map;
  203.         }

  204.         ngx_http_set_charset(r, &dst);

  205.         return ngx_http_next_header_filter(r);
  206.     }

  207.     if (source_charset == charset) {
  208.         r->headers_out.content_type.len = r->headers_out.content_type_len;

  209.         ngx_http_set_charset(r, &dst);

  210.         return ngx_http_next_header_filter(r);
  211.     }

  212.     /* source_charset != charset */

  213.     if (r->headers_out.content_encoding
  214.         && r->headers_out.content_encoding->value.len)
  215.     {
  216.         return ngx_http_next_header_filter(r);
  217.     }

  218.     mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
  219.     charsets = mcf->charsets.elts;

  220.     if (charsets[source_charset].tables == NULL
  221.         || charsets[source_charset].tables[charset] == NULL)
  222.     {
  223.         goto no_charset_map;
  224.     }

  225.     r->headers_out.content_type.len = r->headers_out.content_type_len;

  226.     ngx_http_set_charset(r, &dst);

  227.     return ngx_http_charset_ctx(r, charsets, charset, source_charset);

  228. no_charset_map:

  229.     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  230.                   "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
  231.                   &src, &dst);

  232.     return ngx_http_next_header_filter(r);
  233. }


  234. static ngx_int_t
  235. ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
  236. {
  237.     ngx_int_t                      charset;
  238.     ngx_http_charset_t            *charsets;
  239.     ngx_http_variable_value_t     *vv;
  240.     ngx_http_charset_loc_conf_t   *mlcf;
  241.     ngx_http_charset_main_conf_t  *mcf;

  242.     if (r->headers_out.content_type.len == 0) {
  243.         return NGX_DECLINED;
  244.     }

  245.     if (r->headers_out.override_charset
  246.         && r->headers_out.override_charset->len)
  247.     {
  248.         *name = *r->headers_out.override_charset;

  249.         charset = ngx_http_get_charset(r, name);

  250.         if (charset != NGX_HTTP_NO_CHARSET) {
  251.             return charset;
  252.         }

  253.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
  254.                       "unknown charset \"%V\" to override", name);

  255.         return NGX_DECLINED;
  256.     }

  257.     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
  258.     charset = mlcf->charset;

  259.     if (charset == NGX_HTTP_CHARSET_OFF) {
  260.         return NGX_DECLINED;
  261.     }

  262.     if (r->headers_out.charset.len) {
  263.         if (mlcf->override_charset == 0) {
  264.             return NGX_DECLINED;
  265.         }

  266.     } else {
  267.         if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
  268.             return NGX_DECLINED;
  269.         }
  270.     }

  271.     if (charset < NGX_HTTP_CHARSET_VAR) {
  272.         mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
  273.         charsets = mcf->charsets.elts;
  274.         *name = charsets[charset].name;
  275.         return charset;
  276.     }

  277.     vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);

  278.     if (vv == NULL || vv->not_found) {
  279.         return NGX_ERROR;
  280.     }

  281.     name->len = vv->len;
  282.     name->data = vv->data;

  283.     return ngx_http_get_charset(r, name);
  284. }


  285. static ngx_int_t
  286. ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)
  287. {
  288.     ngx_int_t                charset;
  289.     ngx_str_t               *main_charset;
  290.     ngx_http_charset_ctx_t  *ctx;

  291.     ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);

  292.     if (ctx) {
  293.         *src = ctx->charset_name;
  294.         return ctx->charset;
  295.     }

  296.     main_charset = &r->main->headers_out.charset;

  297.     if (main_charset->len == 0) {
  298.         return NGX_DECLINED;
  299.     }

  300.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
  301.     if (ctx == NULL) {
  302.         return NGX_ERROR;
  303.     }

  304.     ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);

  305.     charset = ngx_http_get_charset(r, main_charset);

  306.     ctx->charset = charset;
  307.     ctx->charset_name = *main_charset;
  308.     *src = *main_charset;

  309.     return charset;
  310. }


  311. static ngx_int_t
  312. ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)
  313. {
  314.     ngx_int_t                      charset;
  315.     ngx_http_charset_t            *charsets;
  316.     ngx_http_variable_value_t     *vv;
  317.     ngx_http_charset_loc_conf_t   *lcf;
  318.     ngx_http_charset_main_conf_t  *mcf;

  319.     if (r->headers_out.charset.len) {
  320.         *name = r->headers_out.charset;
  321.         return ngx_http_get_charset(r, name);
  322.     }

  323.     lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);

  324.     charset = lcf->source_charset;

  325.     if (charset == NGX_HTTP_CHARSET_OFF) {
  326.         name->len = 0;
  327.         return charset;
  328.     }

  329.     if (charset < NGX_HTTP_CHARSET_VAR) {
  330.         mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
  331.         charsets = mcf->charsets.elts;
  332.         *name = charsets[charset].name;
  333.         return charset;
  334.     }

  335.     vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);

  336.     if (vv == NULL || vv->not_found) {
  337.         return NGX_ERROR;
  338.     }

  339.     name->len = vv->len;
  340.     name->data = vv->data;

  341.     return ngx_http_get_charset(r, name);
  342. }


  343. static ngx_int_t
  344. ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)
  345. {
  346.     ngx_uint_t                     i, n;
  347.     ngx_http_charset_t            *charset;
  348.     ngx_http_charset_main_conf_t  *mcf;

  349.     mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);

  350.     charset = mcf->charsets.elts;
  351.     n = mcf->charsets.nelts;

  352.     for (i = 0; i < n; i++) {
  353.         if (charset[i].name.len != name->len) {
  354.             continue;
  355.         }

  356.         if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {
  357.             return i;
  358.         }
  359.     }

  360.     return NGX_HTTP_NO_CHARSET;
  361. }


  362. static ngx_inline void
  363. ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)
  364. {
  365.     if (r != r->main) {
  366.         return;
  367.     }

  368.     if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
  369.         || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
  370.     {
  371.         /*
  372.          * do not set charset for the redirect because NN 4.x
  373.          * use this charset instead of the next page charset
  374.          */

  375.         r->headers_out.charset.len = 0;
  376.         return;
  377.     }

  378.     r->headers_out.charset = *charset;
  379. }


  380. static ngx_int_t
  381. ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,
  382.     ngx_int_t charset, ngx_int_t source_charset)
  383. {
  384.     ngx_http_charset_ctx_t  *ctx;

  385.     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
  386.     if (ctx == NULL) {
  387.         return NGX_ERROR;
  388.     }

  389.     ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);

  390.     ctx->table = charsets[source_charset].tables[charset];
  391.     ctx->charset = charset;
  392.     ctx->charset_name = charsets[charset].name;
  393.     ctx->length = charsets[charset].length;
  394.     ctx->from_utf8 = charsets[source_charset].utf8;
  395.     ctx->to_utf8 = charsets[charset].utf8;

  396.     r->filter_need_in_memory = 1;

  397.     if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {
  398.         ngx_http_clear_content_length(r);

  399.     } else {
  400.         r->filter_need_temporary = 1;
  401.     }

  402.     return ngx_http_next_header_filter(r);
  403. }


  404. static ngx_int_t
  405. ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
  406. {
  407.     ngx_int_t                rc;
  408.     ngx_buf_t               *b;
  409.     ngx_chain_t             *cl, *out, **ll;
  410.     ngx_http_charset_ctx_t  *ctx;

  411.     ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);

  412.     if (ctx == NULL || ctx->table == NULL) {
  413.         return ngx_http_next_body_filter(r, in);
  414.     }

  415.     if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {

  416.         out = NULL;
  417.         ll = &out;

  418.         for (cl = in; cl; cl = cl->next) {
  419.             b = cl->buf;

  420.             if (ngx_buf_size(b) == 0) {

  421.                 *ll = ngx_alloc_chain_link(r->pool);
  422.                 if (*ll == NULL) {
  423.                     return NGX_ERROR;
  424.                 }

  425.                 (*ll)->buf = b;
  426.                 (*ll)->next = NULL;

  427.                 ll = &(*ll)->next;

  428.                 continue;
  429.             }

  430.             if (ctx->to_utf8) {
  431.                 *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);

  432.             } else {
  433.                 *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);
  434.             }

  435.             if (*ll == NULL) {
  436.                 return NGX_ERROR;
  437.             }

  438.             while (*ll) {
  439.                 ll = &(*ll)->next;
  440.             }
  441.         }

  442.         rc = ngx_http_next_body_filter(r, out);

  443.         if (out) {
  444.             if (ctx->busy == NULL) {
  445.                 ctx->busy = out;

  446.             } else {
  447.                 for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
  448.                 cl->next = out;
  449.             }
  450.         }

  451.         while (ctx->busy) {

  452.             cl = ctx->busy;
  453.             b = cl->buf;

  454.             if (ngx_buf_size(b) != 0) {
  455.                 break;
  456.             }

  457.             ctx->busy = cl->next;

  458.             if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
  459.                 continue;
  460.             }

  461.             if (b->shadow) {
  462.                 b->shadow->pos = b->shadow->last;
  463.             }

  464.             if (b->pos) {
  465.                 cl->next = ctx->free_buffers;
  466.                 ctx->free_buffers = cl;
  467.                 continue;
  468.             }

  469.             cl->next = ctx->free_bufs;
  470.             ctx->free_bufs = cl;
  471.         }

  472.         return rc;
  473.     }

  474.     for (cl = in; cl; cl = cl->next) {
  475.         (void) ngx_http_charset_recode(cl->buf, ctx->table);
  476.     }

  477.     return ngx_http_next_body_filter(r, in);
  478. }


  479. static ngx_uint_t
  480. ngx_http_charset_recode(ngx_buf_t *b, u_char *table)
  481. {
  482.     u_char  *p, *last;

  483.     last = b->last;

  484.     for (p = b->pos; p < last; p++) {

  485.         if (*p != table[*p]) {
  486.             goto recode;
  487.         }
  488.     }

  489.     return 0;

  490. recode:

  491.     do {
  492.         if (*p != table[*p]) {
  493.             *p = table[*p];
  494.         }

  495.         p++;

  496.     } while (p < last);

  497.     b->in_file = 0;

  498.     return 1;
  499. }


  500. static ngx_chain_t *
  501. ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
  502.     ngx_http_charset_ctx_t *ctx)
  503. {
  504.     size_t        len, size;
  505.     u_char        c, *p, *src, *dst, *saved, **table;
  506.     uint32_t      n;
  507.     ngx_buf_t    *b;
  508.     ngx_uint_t    i;
  509.     ngx_chain_t  *out, *cl, **ll;

  510.     src = buf->pos;

  511.     if (ctx->saved_len == 0) {

  512.         for ( /* void */ ; src < buf->last; src++) {

  513.             if (*src < 0x80) {
  514.                 continue;
  515.             }

  516.             len = src - buf->pos;

  517.             if (len > 512) {
  518.                 out = ngx_http_charset_get_buf(pool, ctx);
  519.                 if (out == NULL) {
  520.                     return NULL;
  521.                 }

  522.                 b = out->buf;

  523.                 b->temporary = buf->temporary;
  524.                 b->memory = buf->memory;
  525.                 b->mmap = buf->mmap;
  526.                 b->flush = buf->flush;

  527.                 b->pos = buf->pos;
  528.                 b->last = src;

  529.                 out->buf = b;
  530.                 out->next = NULL;

  531.                 size = buf->last - src;

  532.                 saved = src;
  533.                 n = ngx_utf8_decode(&saved, size);

  534.                 if (n == 0xfffffffe) {
  535.                     /* incomplete UTF-8 symbol */

  536.                     ngx_memcpy(ctx->saved, src, size);
  537.                     ctx->saved_len = size;

  538.                     b->shadow = buf;

  539.                     return out;
  540.                 }

  541.             } else {
  542.                 out = NULL;
  543.                 size = len + buf->last - src;
  544.                 src = buf->pos;
  545.             }

  546.             if (size < NGX_HTML_ENTITY_LEN) {
  547.                 size += NGX_HTML_ENTITY_LEN;
  548.             }

  549.             cl = ngx_http_charset_get_buffer(pool, ctx, size);
  550.             if (cl == NULL) {
  551.                 return NULL;
  552.             }

  553.             if (out) {
  554.                 out->next = cl;

  555.             } else {
  556.                 out = cl;
  557.             }

  558.             b = cl->buf;
  559.             dst = b->pos;

  560.             goto recode;
  561.         }

  562.         out = ngx_alloc_chain_link(pool);
  563.         if (out == NULL) {
  564.             return NULL;
  565.         }

  566.         out->buf = buf;
  567.         out->next = NULL;

  568.         return out;
  569.     }

  570.     /* process incomplete UTF sequence from previous buffer */

  571.     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,
  572.                    "http charset utf saved: %z", ctx->saved_len);

  573.     p = src;

  574.     for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {
  575.         ctx->saved[i] = *p++;

  576.         if (p == buf->last) {
  577.             break;
  578.         }
  579.     }

  580.     saved = ctx->saved;
  581.     n = ngx_utf8_decode(&saved, i);

  582.     c = '\0';

  583.     if (n < 0x10000) {
  584.         table = (u_char **) ctx->table;
  585.         p = table[n >> 8];

  586.         if (p) {
  587.             c = p[n & 0xff];
  588.         }

  589.     } else if (n == 0xfffffffe) {

  590.         /* incomplete UTF-8 symbol */

  591.         if (i < NGX_UTF_LEN) {
  592.             out = ngx_http_charset_get_buf(pool, ctx);
  593.             if (out == NULL) {
  594.                 return NULL;
  595.             }

  596.             b = out->buf;

  597.             b->pos = buf->pos;
  598.             b->last = buf->last;
  599.             b->sync = 1;
  600.             b->shadow = buf;

  601.             ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);
  602.             ctx->saved_len += i;

  603.             return out;
  604.         }
  605.     }

  606.     size = buf->last - buf->pos;

  607.     if (size < NGX_HTML_ENTITY_LEN) {
  608.         size += NGX_HTML_ENTITY_LEN;
  609.     }

  610.     cl = ngx_http_charset_get_buffer(pool, ctx, size);
  611.     if (cl == NULL) {
  612.         return NULL;
  613.     }

  614.     out = cl;

  615.     b = cl->buf;
  616.     dst = b->pos;

  617.     if (c) {
  618.         *dst++ = c;

  619.     } else if (n == 0xfffffffe) {
  620.         *dst++ = '?';

  621.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
  622.                        "http charset invalid utf 0");

  623.         saved = &ctx->saved[NGX_UTF_LEN];

  624.     } else if (n > 0x10ffff) {
  625.         *dst++ = '?';

  626.         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
  627.                        "http charset invalid utf 1");

  628.     } else {
  629.         dst = ngx_sprintf(dst, "&#%uD;", n);
  630.     }

  631.     src += (saved - ctx->saved) - ctx->saved_len;
  632.     ctx->saved_len = 0;

  633. recode:

  634.     ll = &cl->next;

  635.     table = (u_char **) ctx->table;

  636.     while (src < buf->last) {

  637.         if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {
  638.             b->last = dst;

  639.             size = buf->last - src + NGX_HTML_ENTITY_LEN;

  640.             cl = ngx_http_charset_get_buffer(pool, ctx, size);
  641.             if (cl == NULL) {
  642.                 return NULL;
  643.             }

  644.             *ll = cl;
  645.             ll = &cl->next;

  646.             b = cl->buf;
  647.             dst = b->pos;
  648.         }

  649.         if (*src < 0x80) {
  650.             *dst++ = *src++;
  651.             continue;
  652.         }

  653.         len = buf->last - src;

  654.         n = ngx_utf8_decode(&src, len);

  655.         if (n < 0x10000) {

  656.             p = table[n >> 8];

  657.             if (p) {
  658.                 c = p[n & 0xff];

  659.                 if (c) {
  660.                     *dst++ = c;
  661.                     continue;
  662.                 }
  663.             }

  664.             dst = ngx_sprintf(dst, "&#%uD;", n);

  665.             continue;
  666.         }

  667.         if (n == 0xfffffffe) {
  668.             /* incomplete UTF-8 symbol */

  669.             ngx_memcpy(ctx->saved, src, len);
  670.             ctx->saved_len = len;

  671.             if (b->pos == dst) {
  672.                 b->sync = 1;
  673.                 b->temporary = 0;
  674.             }

  675.             break;
  676.         }

  677.         if (n > 0x10ffff) {
  678.             *dst++ = '?';

  679.             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
  680.                            "http charset invalid utf 2");

  681.             continue;
  682.         }

  683.         /* n > 0xffff */

  684.         dst = ngx_sprintf(dst, "&#%uD;", n);
  685.     }

  686.     b->last = dst;

  687.     b->last_buf = buf->last_buf;
  688.     b->last_in_chain = buf->last_in_chain;
  689.     b->flush = buf->flush;

  690.     b->shadow = buf;

  691.     return out;
  692. }


  693. static ngx_chain_t *
  694. ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
  695.     ngx_http_charset_ctx_t *ctx)
  696. {
  697.     size_t        len, size;
  698.     u_char       *p, *src, *dst, *table;
  699.     ngx_buf_t    *b;
  700.     ngx_chain_t  *out, *cl, **ll;

  701.     table = ctx->table;

  702.     for (src = buf->pos; src < buf->last; src++) {
  703.         if (table[*src * NGX_UTF_LEN] == '\1') {
  704.             continue;
  705.         }

  706.         goto recode;
  707.     }

  708.     out = ngx_alloc_chain_link(pool);
  709.     if (out == NULL) {
  710.         return NULL;
  711.     }

  712.     out->buf = buf;
  713.     out->next = NULL;

  714.     return out;

  715. recode:

  716.     /*
  717.      * we assume that there are about half of characters to be recoded,
  718.      * so we preallocate "size / 2 + size / 2 * ctx->length"
  719.      */

  720.     len = src - buf->pos;

  721.     if (len > 512) {
  722.         out = ngx_http_charset_get_buf(pool, ctx);
  723.         if (out == NULL) {
  724.             return NULL;
  725.         }

  726.         b = out->buf;

  727.         b->temporary = buf->temporary;
  728.         b->memory = buf->memory;
  729.         b->mmap = buf->mmap;
  730.         b->flush = buf->flush;

  731.         b->pos = buf->pos;
  732.         b->last = src;

  733.         out->buf = b;
  734.         out->next = NULL;

  735.         size = buf->last - src;
  736.         size = size / 2 + size / 2 * ctx->length;

  737.     } else {
  738.         out = NULL;

  739.         size = buf->last - src;
  740.         size = len + size / 2 + size / 2 * ctx->length;

  741.         src = buf->pos;
  742.     }

  743.     cl = ngx_http_charset_get_buffer(pool, ctx, size);
  744.     if (cl == NULL) {
  745.         return NULL;
  746.     }

  747.     if (out) {
  748.         out->next = cl;

  749.     } else {
  750.         out = cl;
  751.     }

  752.     ll = &cl->next;

  753.     b = cl->buf;
  754.     dst = b->pos;

  755.     while (src < buf->last) {

  756.         p = &table[*src++ * NGX_UTF_LEN];
  757.         len = *p++;

  758.         if ((size_t) (b->end - dst) < len) {
  759.             b->last = dst;

  760.             size = buf->last - src;
  761.             size = len + size / 2 + size / 2 * ctx->length;

  762.             cl = ngx_http_charset_get_buffer(pool, ctx, size);
  763.             if (cl == NULL) {
  764.                 return NULL;
  765.             }

  766.             *ll = cl;
  767.             ll = &cl->next;

  768.             b = cl->buf;
  769.             dst = b->pos;
  770.         }

  771.         while (len) {
  772.             *dst++ = *p++;
  773.             len--;
  774.         }
  775.     }

  776.     b->last = dst;

  777.     b->last_buf = buf->last_buf;
  778.     b->last_in_chain = buf->last_in_chain;
  779.     b->flush = buf->flush;

  780.     b->shadow = buf;

  781.     return out;
  782. }


  783. static ngx_chain_t *
  784. ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)
  785. {
  786.     ngx_chain_t  *cl;

  787.     cl = ctx->free_bufs;

  788.     if (cl) {
  789.         ctx->free_bufs = cl->next;

  790.         cl->buf->shadow = NULL;
  791.         cl->next = NULL;

  792.         return cl;
  793.     }

  794.     cl = ngx_alloc_chain_link(pool);
  795.     if (cl == NULL) {
  796.         return NULL;
  797.     }

  798.     cl->buf = ngx_calloc_buf(pool);
  799.     if (cl->buf == NULL) {
  800.         return NULL;
  801.     }

  802.     cl->next = NULL;

  803.     cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;

  804.     return cl;
  805. }


  806. static ngx_chain_t *
  807. ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,
  808.     size_t size)
  809. {
  810.     ngx_buf_t    *b;
  811.     ngx_chain_t  *cl, **ll;

  812.     for (ll = &ctx->free_buffers, cl = ctx->free_buffers;
  813.          cl;
  814.          ll = &cl->next, cl = cl->next)
  815.     {
  816.         b = cl->buf;

  817.         if ((size_t) (b->end - b->start) >= size) {
  818.             *ll = cl->next;
  819.             cl->next = NULL;

  820.             b->pos = b->start;
  821.             b->temporary = 1;
  822.             b->shadow = NULL;

  823.             return cl;
  824.         }
  825.     }

  826.     cl = ngx_alloc_chain_link(pool);
  827.     if (cl == NULL) {
  828.         return NULL;
  829.     }

  830.     cl->buf = ngx_create_temp_buf(pool, size);
  831.     if (cl->buf == NULL) {
  832.         return NULL;
  833.     }

  834.     cl->next = NULL;

  835.     cl->buf->temporary = 1;
  836.     cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;

  837.     return cl;
  838. }


  839. static char *
  840. ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  841. {
  842.     ngx_http_charset_main_conf_t  *mcf = conf;

  843.     char                         *rv;
  844.     u_char                       *p, *dst2src, **pp;
  845.     ngx_int_t                     src, dst;
  846.     ngx_uint_t                    i, n;
  847.     ngx_str_t                    *value;
  848.     ngx_conf_t                    pvcf;
  849.     ngx_http_charset_t           *charset;
  850.     ngx_http_charset_tables_t    *table;
  851.     ngx_http_charset_conf_ctx_t   ctx;

  852.     value = cf->args->elts;

  853.     src = ngx_http_add_charset(&mcf->charsets, &value[1]);
  854.     if (src == NGX_ERROR) {
  855.         return NGX_CONF_ERROR;
  856.     }

  857.     dst = ngx_http_add_charset(&mcf->charsets, &value[2]);
  858.     if (dst == NGX_ERROR) {
  859.         return NGX_CONF_ERROR;
  860.     }

  861.     if (src == dst) {
  862.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  863.                            "\"charset_map\" between the same charsets "
  864.                            "\"%V\" and \"%V\"", &value[1], &value[2]);
  865.         return NGX_CONF_ERROR;
  866.     }

  867.     table = mcf->tables.elts;
  868.     for (i = 0; i < mcf->tables.nelts; i++) {
  869.         if ((src == table->src && dst == table->dst)
  870.              || (src == table->dst && dst == table->src))
  871.         {
  872.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  873.                                "duplicate \"charset_map\" between "
  874.                                "\"%V\" and \"%V\"", &value[1], &value[2]);
  875.             return NGX_CONF_ERROR;
  876.         }
  877.     }

  878.     table = ngx_array_push(&mcf->tables);
  879.     if (table == NULL) {
  880.         return NGX_CONF_ERROR;
  881.     }

  882.     table->src = src;
  883.     table->dst = dst;

  884.     if (ngx_strcasecmp(value[2].data, (u_char *) "utf-8") == 0) {
  885.         table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);
  886.         if (table->src2dst == NULL) {
  887.             return NGX_CONF_ERROR;
  888.         }

  889.         table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));
  890.         if (table->dst2src == NULL) {
  891.             return NGX_CONF_ERROR;
  892.         }

  893.         dst2src = ngx_pcalloc(cf->pool, 256);
  894.         if (dst2src == NULL) {
  895.             return NGX_CONF_ERROR;
  896.         }

  897.         pp = (u_char **) &table->dst2src[0];
  898.         pp[0] = dst2src;

  899.         for (i = 0; i < 128; i++) {
  900.             p = &table->src2dst[i * NGX_UTF_LEN];
  901.             p[0] = '\1';
  902.             p[1] = (u_char) i;
  903.             dst2src[i] = (u_char) i;
  904.         }

  905.         for (/* void */; i < 256; i++) {
  906.             p = &table->src2dst[i * NGX_UTF_LEN];
  907.             p[0] = '\1';
  908.             p[1] = '?';
  909.         }

  910.     } else {
  911.         table->src2dst = ngx_palloc(cf->pool, 256);
  912.         if (table->src2dst == NULL) {
  913.             return NGX_CONF_ERROR;
  914.         }

  915.         table->dst2src = ngx_palloc(cf->pool, 256);
  916.         if (table->dst2src == NULL) {
  917.             return NGX_CONF_ERROR;
  918.         }

  919.         for (i = 0; i < 128; i++) {
  920.             table->src2dst[i] = (u_char) i;
  921.             table->dst2src[i] = (u_char) i;
  922.         }

  923.         for (/* void */; i < 256; i++) {
  924.             table->src2dst[i] = '?';
  925.             table->dst2src[i] = '?';
  926.         }
  927.     }

  928.     charset = mcf->charsets.elts;

  929.     ctx.table = table;
  930.     ctx.charset = &charset[dst];
  931.     ctx.characters = 0;

  932.     pvcf = *cf;
  933.     cf->ctx = &ctx;
  934.     cf->handler = ngx_http_charset_map;
  935.     cf->handler_conf = conf;

  936.     rv = ngx_conf_parse(cf, NULL);

  937.     *cf = pvcf;

  938.     if (ctx.characters) {
  939.         n = ctx.charset->length;
  940.         ctx.charset->length /= ctx.characters;

  941.         if (((n * 10) / ctx.characters) % 10 > 4) {
  942.             ctx.charset->length++;
  943.         }
  944.     }

  945.     return rv;
  946. }


  947. static char *
  948. ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
  949. {
  950.     u_char                       *p, *dst2src, **pp;
  951.     uint32_t                      n;
  952.     ngx_int_t                     src, dst;
  953.     ngx_str_t                    *value;
  954.     ngx_uint_t                    i;
  955.     ngx_http_charset_tables_t    *table;
  956.     ngx_http_charset_conf_ctx_t  *ctx;

  957.     if (cf->args->nelts != 2) {
  958.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");
  959.         return NGX_CONF_ERROR;
  960.     }

  961.     value = cf->args->elts;

  962.     src = ngx_hextoi(value[0].data, value[0].len);
  963.     if (src == NGX_ERROR || src > 255) {
  964.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  965.                            "invalid value \"%V\"", &value[0]);
  966.         return NGX_CONF_ERROR;
  967.     }

  968.     ctx = cf->ctx;
  969.     table = ctx->table;

  970.     if (ctx->charset->utf8) {
  971.         p = &table->src2dst[src * NGX_UTF_LEN];

  972.         *p++ = (u_char) (value[1].len / 2);

  973.         for (i = 0; i < value[1].len; i += 2) {
  974.             dst = ngx_hextoi(&value[1].data[i], 2);
  975.             if (dst == NGX_ERROR || dst > 255) {
  976.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  977.                                    "invalid value \"%V\"", &value[1]);
  978.                 return NGX_CONF_ERROR;
  979.             }

  980.             *p++ = (u_char) dst;
  981.         }

  982.         i /= 2;

  983.         ctx->charset->length += i;
  984.         ctx->characters++;

  985.         p = &table->src2dst[src * NGX_UTF_LEN] + 1;

  986.         n = ngx_utf8_decode(&p, i);

  987.         if (n > 0xffff) {
  988.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  989.                                "invalid value \"%V\"", &value[1]);
  990.             return NGX_CONF_ERROR;
  991.         }

  992.         pp = (u_char **) &table->dst2src[0];

  993.         dst2src = pp[n >> 8];

  994.         if (dst2src == NULL) {
  995.             dst2src = ngx_pcalloc(cf->pool, 256);
  996.             if (dst2src == NULL) {
  997.                 return NGX_CONF_ERROR;
  998.             }

  999.             pp[n >> 8] = dst2src;
  1000.         }

  1001.         dst2src[n & 0xff] = (u_char) src;

  1002.     } else {
  1003.         dst = ngx_hextoi(value[1].data, value[1].len);
  1004.         if (dst == NGX_ERROR || dst > 255) {
  1005.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  1006.                                "invalid value \"%V\"", &value[1]);
  1007.             return NGX_CONF_ERROR;
  1008.         }

  1009.         table->src2dst[src] = (u_char) dst;
  1010.         table->dst2src[dst] = (u_char) src;
  1011.     }

  1012.     return NGX_CONF_OK;
  1013. }


  1014. static char *
  1015. ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
  1016. {
  1017.     char  *p = conf;

  1018.     ngx_int_t                     *cp;
  1019.     ngx_str_t                     *value, var;
  1020.     ngx_http_charset_main_conf_t  *mcf;

  1021.     cp = (ngx_int_t *) (p + cmd->offset);

  1022.     if (*cp != NGX_CONF_UNSET) {
  1023.         return "is duplicate";
  1024.     }

  1025.     value = cf->args->elts;

  1026.     if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)
  1027.         && ngx_strcmp(value[1].data, "off") == 0)
  1028.     {
  1029.         *cp = NGX_HTTP_CHARSET_OFF;
  1030.         return NGX_CONF_OK;
  1031.     }


  1032.     if (value[1].data[0] == '$') {
  1033.         var.len = value[1].len - 1;
  1034.         var.data = value[1].data + 1;

  1035.         *cp = ngx_http_get_variable_index(cf, &var);

  1036.         if (*cp == NGX_ERROR) {
  1037.             return NGX_CONF_ERROR;
  1038.         }

  1039.         *cp += NGX_HTTP_CHARSET_VAR;

  1040.         return NGX_CONF_OK;
  1041.     }

  1042.     mcf = ngx_http_conf_get_module_main_conf(cf,
  1043.                                              ngx_http_charset_filter_module);

  1044.     *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);
  1045.     if (*cp == NGX_ERROR) {
  1046.         return NGX_CONF_ERROR;
  1047.     }

  1048.     return NGX_CONF_OK;
  1049. }


  1050. static ngx_int_t
  1051. ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)
  1052. {
  1053.     ngx_uint_t           i;
  1054.     ngx_http_charset_t  *c;

  1055.     c = charsets->elts;
  1056.     for (i = 0; i < charsets->nelts; i++) {
  1057.         if (name->len != c[i].name.len) {
  1058.             continue;
  1059.         }

  1060.         if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {
  1061.             break;
  1062.         }
  1063.     }

  1064.     if (i < charsets->nelts) {
  1065.         return i;
  1066.     }

  1067.     c = ngx_array_push(charsets);
  1068.     if (c == NULL) {
  1069.         return NGX_ERROR;
  1070.     }

  1071.     c->tables = NULL;
  1072.     c->name = *name;
  1073.     c->length = 0;

  1074.     if (ngx_strcasecmp(name->data, (u_char *) "utf-8") == 0) {
  1075.         c->utf8 = 1;

  1076.     } else {
  1077.         c->utf8 = 0;
  1078.     }

  1079.     return i;
  1080. }


  1081. static void *
  1082. ngx_http_charset_create_main_conf(ngx_conf_t *cf)
  1083. {
  1084.     ngx_http_charset_main_conf_t  *mcf;

  1085.     mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));
  1086.     if (mcf == NULL) {
  1087.         return NULL;
  1088.     }

  1089.     if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
  1090.         != NGX_OK)
  1091.     {
  1092.         return NULL;
  1093.     }

  1094.     if (ngx_array_init(&mcf->tables, cf->pool, 1,
  1095.                        sizeof(ngx_http_charset_tables_t))
  1096.         != NGX_OK)
  1097.     {
  1098.         return NULL;
  1099.     }

  1100.     if (ngx_array_init(&mcf->recodes, cf->pool, 2,
  1101.                        sizeof(ngx_http_charset_recode_t))
  1102.         != NGX_OK)
  1103.     {
  1104.         return NULL;
  1105.     }

  1106.     return mcf;
  1107. }


  1108. static void *
  1109. ngx_http_charset_create_loc_conf(ngx_conf_t *cf)
  1110. {
  1111.     ngx_http_charset_loc_conf_t  *lcf;

  1112.     lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));
  1113.     if (lcf == NULL) {
  1114.         return NULL;
  1115.     }

  1116.     /*
  1117.      * set by ngx_pcalloc():
  1118.      *
  1119.      *     lcf->types = { NULL };
  1120.      *     lcf->types_keys = NULL;
  1121.      */

  1122.     lcf->charset = NGX_CONF_UNSET;
  1123.     lcf->source_charset = NGX_CONF_UNSET;
  1124.     lcf->override_charset = NGX_CONF_UNSET;

  1125.     return lcf;
  1126. }


  1127. static char *
  1128. ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
  1129. {
  1130.     ngx_http_charset_loc_conf_t *prev = parent;
  1131.     ngx_http_charset_loc_conf_t *conf = child;

  1132.     ngx_uint_t                     i;
  1133.     ngx_http_charset_recode_t     *recode;
  1134.     ngx_http_charset_main_conf_t  *mcf;

  1135.     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
  1136.                              &prev->types_keys, &prev->types,
  1137.                              ngx_http_charset_default_types)
  1138.         != NGX_OK)
  1139.     {
  1140.         return NGX_CONF_ERROR;
  1141.     }

  1142.     ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);
  1143.     ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);
  1144.     ngx_conf_merge_value(conf->source_charset, prev->source_charset,
  1145.                          NGX_HTTP_CHARSET_OFF);

  1146.     if (conf->charset == NGX_HTTP_CHARSET_OFF
  1147.         || conf->source_charset == NGX_HTTP_CHARSET_OFF
  1148.         || conf->charset == conf->source_charset)
  1149.     {
  1150.         return NGX_CONF_OK;
  1151.     }

  1152.     if (conf->source_charset >= NGX_HTTP_CHARSET_VAR
  1153.         || conf->charset >= NGX_HTTP_CHARSET_VAR)
  1154.     {
  1155.         return NGX_CONF_OK;
  1156.     }

  1157.     mcf = ngx_http_conf_get_module_main_conf(cf,
  1158.                                              ngx_http_charset_filter_module);
  1159.     recode = mcf->recodes.elts;
  1160.     for (i = 0; i < mcf->recodes.nelts; i++) {
  1161.         if (conf->source_charset == recode[i].src
  1162.             && conf->charset == recode[i].dst)
  1163.         {
  1164.             return NGX_CONF_OK;
  1165.         }
  1166.     }

  1167.     recode = ngx_array_push(&mcf->recodes);
  1168.     if (recode == NULL) {
  1169.         return NGX_CONF_ERROR;
  1170.     }

  1171.     recode->src = conf->source_charset;
  1172.     recode->dst = conf->charset;

  1173.     return NGX_CONF_OK;
  1174. }


  1175. static ngx_int_t
  1176. ngx_http_charset_postconfiguration(ngx_conf_t *cf)
  1177. {
  1178.     u_char                       **src, **dst;
  1179.     ngx_int_t                      c;
  1180.     ngx_uint_t                     i, t;
  1181.     ngx_http_charset_t            *charset;
  1182.     ngx_http_charset_recode_t     *recode;
  1183.     ngx_http_charset_tables_t     *tables;
  1184.     ngx_http_charset_main_conf_t  *mcf;

  1185.     mcf = ngx_http_conf_get_module_main_conf(cf,
  1186.                                              ngx_http_charset_filter_module);

  1187.     recode = mcf->recodes.elts;
  1188.     tables = mcf->tables.elts;
  1189.     charset = mcf->charsets.elts;

  1190.     for (i = 0; i < mcf->recodes.nelts; i++) {

  1191.         c = recode[i].src;

  1192.         for (t = 0; t < mcf->tables.nelts; t++) {

  1193.             if (c == tables[t].src && recode[i].dst == tables[t].dst) {
  1194.                 goto next;
  1195.             }

  1196.             if (c == tables[t].dst && recode[i].dst == tables[t].src) {
  1197.                 goto next;
  1198.             }
  1199.         }

  1200.         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
  1201.                    "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
  1202.                    &charset[c].name, &charset[recode[i].dst].name);
  1203.         return NGX_ERROR;

  1204.     next:
  1205.         continue;
  1206.     }


  1207.     for (t = 0; t < mcf->tables.nelts; t++) {

  1208.         src = charset[tables[t].src].tables;

  1209.         if (src == NULL) {
  1210.             src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
  1211.             if (src == NULL) {
  1212.                 return NGX_ERROR;
  1213.             }

  1214.             charset[tables[t].src].tables = src;
  1215.         }

  1216.         dst = charset[tables[t].dst].tables;

  1217.         if (dst == NULL) {
  1218.             dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
  1219.             if (dst == NULL) {
  1220.                 return NGX_ERROR;
  1221.             }

  1222.             charset[tables[t].dst].tables = dst;
  1223.         }

  1224.         src[tables[t].dst] = tables[t].src2dst;
  1225.         dst[tables[t].src] = tables[t].dst2src;
  1226.     }

  1227.     ngx_http_next_header_filter = ngx_http_top_header_filter;
  1228.     ngx_http_top_header_filter = ngx_http_charset_header_filter;

  1229.     ngx_http_next_body_filter = ngx_http_top_body_filter;
  1230.     ngx_http_top_body_filter = ngx_http_charset_body_filter;

  1231.     return NGX_OK;
  1232. }