Annotation of embedaddon/nginx/src/http/modules/ngx_http_charset_filter_module.c, revision 1.1

1.1     ! misho       1: 
        !             2: /*
        !             3:  * Copyright (C) Igor Sysoev
        !             4:  * Copyright (C) Nginx, Inc.
        !             5:  */
        !             6: 
        !             7: 
        !             8: #include <ngx_config.h>
        !             9: #include <ngx_core.h>
        !            10: #include <ngx_http.h>
        !            11: 
        !            12: 
        !            13: #define NGX_HTTP_CHARSET_OFF    -2
        !            14: #define NGX_HTTP_NO_CHARSET     -3
        !            15: #define NGX_HTTP_CHARSET_VAR    0x10000
        !            16: 
        !            17: /* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */
        !            18: #define NGX_UTF_LEN             4
        !            19: 
        !            20: #define NGX_HTML_ENTITY_LEN     (sizeof("&#1114111;") - 1)
        !            21: 
        !            22: 
        !            23: typedef struct {
        !            24:     u_char                    **tables;
        !            25:     ngx_str_t                   name;
        !            26: 
        !            27:     unsigned                    length:16;
        !            28:     unsigned                    utf8:1;
        !            29: } ngx_http_charset_t;
        !            30: 
        !            31: 
        !            32: typedef struct {
        !            33:     ngx_int_t                   src;
        !            34:     ngx_int_t                   dst;
        !            35: } ngx_http_charset_recode_t;
        !            36: 
        !            37: 
        !            38: typedef struct {
        !            39:     ngx_int_t                   src;
        !            40:     ngx_int_t                   dst;
        !            41:     u_char                     *src2dst;
        !            42:     u_char                     *dst2src;
        !            43: } ngx_http_charset_tables_t;
        !            44: 
        !            45: 
        !            46: typedef struct {
        !            47:     ngx_array_t                 charsets;       /* ngx_http_charset_t */
        !            48:     ngx_array_t                 tables;         /* ngx_http_charset_tables_t */
        !            49:     ngx_array_t                 recodes;        /* ngx_http_charset_recode_t */
        !            50: } ngx_http_charset_main_conf_t;
        !            51: 
        !            52: 
        !            53: typedef struct {
        !            54:     ngx_int_t                   charset;
        !            55:     ngx_int_t                   source_charset;
        !            56:     ngx_flag_t                  override_charset;
        !            57: 
        !            58:     ngx_hash_t                  types;
        !            59:     ngx_array_t                *types_keys;
        !            60: } ngx_http_charset_loc_conf_t;
        !            61: 
        !            62: 
        !            63: typedef struct {
        !            64:     u_char                     *table;
        !            65:     ngx_int_t                   charset;
        !            66:     ngx_str_t                   charset_name;
        !            67: 
        !            68:     ngx_chain_t                *busy;
        !            69:     ngx_chain_t                *free_bufs;
        !            70:     ngx_chain_t                *free_buffers;
        !            71: 
        !            72:     size_t                      saved_len;
        !            73:     u_char                      saved[NGX_UTF_LEN];
        !            74: 
        !            75:     unsigned                    length:16;
        !            76:     unsigned                    from_utf8:1;
        !            77:     unsigned                    to_utf8:1;
        !            78: } ngx_http_charset_ctx_t;
        !            79: 
        !            80: 
        !            81: typedef struct {
        !            82:     ngx_http_charset_tables_t  *table;
        !            83:     ngx_http_charset_t         *charset;
        !            84:     ngx_uint_t                  characters;
        !            85: } ngx_http_charset_conf_ctx_t;
        !            86: 
        !            87: 
        !            88: static ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,
        !            89:     ngx_str_t *name);
        !            90: static ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,
        !            91:     ngx_str_t *name);
        !            92: static ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,
        !            93:     ngx_str_t *name);
        !            94: static ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);
        !            95: static ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,
        !            96:     ngx_str_t *charset);
        !            97: static ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,
        !            98:     ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);
        !            99: static ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);
        !           100: static ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,
        !           101:     ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
        !           102: static ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,
        !           103:     ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);
        !           104: 
        !           105: static ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,
        !           106:     ngx_http_charset_ctx_t *ctx);
        !           107: static ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,
        !           108:     ngx_http_charset_ctx_t *ctx, size_t size);
        !           109: 
        !           110: static char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,
        !           111:     void *conf);
        !           112: static char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,
        !           113:     void *conf);
        !           114: 
        !           115: static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,
        !           116:     void *conf);
        !           117: static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);
        !           118: 
        !           119: static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);
        !           120: static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);
        !           121: static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,
        !           122:     void *parent, void *child);
        !           123: static ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);
        !           124: 
        !           125: 
        !           126: ngx_str_t  ngx_http_charset_default_types[] = {
        !           127:     ngx_string("text/html"),
        !           128:     ngx_string("text/xml"),
        !           129:     ngx_string("text/plain"),
        !           130:     ngx_string("text/vnd.wap.wml"),
        !           131:     ngx_string("application/x-javascript"),
        !           132:     ngx_string("application/rss+xml"),
        !           133:     ngx_null_string
        !           134: };
        !           135: 
        !           136: 
        !           137: static ngx_command_t  ngx_http_charset_filter_commands[] = {
        !           138: 
        !           139:     { ngx_string("charset"),
        !           140:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
        !           141:                         |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
        !           142:       ngx_http_set_charset_slot,
        !           143:       NGX_HTTP_LOC_CONF_OFFSET,
        !           144:       offsetof(ngx_http_charset_loc_conf_t, charset),
        !           145:       NULL },
        !           146: 
        !           147:     { ngx_string("source_charset"),
        !           148:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
        !           149:                         |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
        !           150:       ngx_http_set_charset_slot,
        !           151:       NGX_HTTP_LOC_CONF_OFFSET,
        !           152:       offsetof(ngx_http_charset_loc_conf_t, source_charset),
        !           153:       NULL },
        !           154: 
        !           155:     { ngx_string("override_charset"),
        !           156:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF
        !           157:                         |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,
        !           158:       ngx_conf_set_flag_slot,
        !           159:       NGX_HTTP_LOC_CONF_OFFSET,
        !           160:       offsetof(ngx_http_charset_loc_conf_t, override_charset),
        !           161:       NULL },
        !           162: 
        !           163:     { ngx_string("charset_types"),
        !           164:       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
        !           165:       ngx_http_types_slot,
        !           166:       NGX_HTTP_LOC_CONF_OFFSET,
        !           167:       offsetof(ngx_http_charset_loc_conf_t, types_keys),
        !           168:       &ngx_http_charset_default_types[0] },
        !           169: 
        !           170:     { ngx_string("charset_map"),
        !           171:       NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,
        !           172:       ngx_http_charset_map_block,
        !           173:       NGX_HTTP_MAIN_CONF_OFFSET,
        !           174:       0,
        !           175:       NULL },
        !           176: 
        !           177:       ngx_null_command
        !           178: };
        !           179: 
        !           180: 
        !           181: static ngx_http_module_t  ngx_http_charset_filter_module_ctx = {
        !           182:     NULL,                                  /* preconfiguration */
        !           183:     ngx_http_charset_postconfiguration,    /* postconfiguration */
        !           184: 
        !           185:     ngx_http_charset_create_main_conf,     /* create main configuration */
        !           186:     NULL,                                  /* init main configuration */
        !           187: 
        !           188:     NULL,                                  /* create server configuration */
        !           189:     NULL,                                  /* merge server configuration */
        !           190: 
        !           191:     ngx_http_charset_create_loc_conf,      /* create location configuration */
        !           192:     ngx_http_charset_merge_loc_conf        /* merge location configuration */
        !           193: };
        !           194: 
        !           195: 
        !           196: ngx_module_t  ngx_http_charset_filter_module = {
        !           197:     NGX_MODULE_V1,
        !           198:     &ngx_http_charset_filter_module_ctx,   /* module context */
        !           199:     ngx_http_charset_filter_commands,      /* module directives */
        !           200:     NGX_HTTP_MODULE,                       /* module type */
        !           201:     NULL,                                  /* init master */
        !           202:     NULL,                                  /* init module */
        !           203:     NULL,                                  /* init process */
        !           204:     NULL,                                  /* init thread */
        !           205:     NULL,                                  /* exit thread */
        !           206:     NULL,                                  /* exit process */
        !           207:     NULL,                                  /* exit master */
        !           208:     NGX_MODULE_V1_PADDING
        !           209: };
        !           210: 
        !           211: 
        !           212: static ngx_http_output_header_filter_pt  ngx_http_next_header_filter;
        !           213: static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;
        !           214: 
        !           215: 
        !           216: static ngx_int_t
        !           217: ngx_http_charset_header_filter(ngx_http_request_t *r)
        !           218: {
        !           219:     ngx_int_t                      charset, source_charset;
        !           220:     ngx_str_t                      dst, src;
        !           221:     ngx_http_charset_t            *charsets;
        !           222:     ngx_http_charset_main_conf_t  *mcf;
        !           223: 
        !           224:     if (r == r->main) {
        !           225:         charset = ngx_http_destination_charset(r, &dst);
        !           226: 
        !           227:     } else {
        !           228:         charset = ngx_http_main_request_charset(r, &dst);
        !           229:     }
        !           230: 
        !           231:     if (charset == NGX_ERROR) {
        !           232:         return NGX_ERROR;
        !           233:     }
        !           234: 
        !           235:     if (charset == NGX_DECLINED) {
        !           236:         return ngx_http_next_header_filter(r);
        !           237:     }
        !           238: 
        !           239:     /* charset: charset index or NGX_HTTP_NO_CHARSET */
        !           240: 
        !           241:     source_charset = ngx_http_source_charset(r, &src);
        !           242: 
        !           243:     if (source_charset == NGX_ERROR) {
        !           244:         return NGX_ERROR;
        !           245:     }
        !           246: 
        !           247:     /*
        !           248:      * source_charset: charset index, NGX_HTTP_NO_CHARSET,
        !           249:      *                 or NGX_HTTP_CHARSET_OFF
        !           250:      */
        !           251: 
        !           252:     ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
        !           253:                    "charset: \"%V\" > \"%V\"", &src, &dst);
        !           254: 
        !           255:     if (source_charset == NGX_HTTP_CHARSET_OFF) {
        !           256:         ngx_http_set_charset(r, &dst);
        !           257: 
        !           258:         return ngx_http_next_header_filter(r);
        !           259:     }
        !           260: 
        !           261:     if (charset == NGX_HTTP_NO_CHARSET
        !           262:         || source_charset == NGX_HTTP_NO_CHARSET)
        !           263:     {
        !           264:         if (source_charset != charset
        !           265:             || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
        !           266:         {
        !           267:             goto no_charset_map;
        !           268:         }
        !           269: 
        !           270:         ngx_http_set_charset(r, &dst);
        !           271: 
        !           272:         return ngx_http_next_header_filter(r);
        !           273:     }
        !           274: 
        !           275:     mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
        !           276:     charsets = mcf->charsets.elts;
        !           277: 
        !           278:     if (source_charset != charset
        !           279:         && (charsets[source_charset].tables == NULL
        !           280:             || charsets[source_charset].tables[charset] == NULL))
        !           281:     {
        !           282:         goto no_charset_map;
        !           283:     }
        !           284: 
        !           285:     r->headers_out.content_type.len = r->headers_out.content_type_len;
        !           286: 
        !           287:     ngx_http_set_charset(r, &dst);
        !           288: 
        !           289:     if (source_charset != charset) {
        !           290:         return ngx_http_charset_ctx(r, charsets, charset, source_charset);
        !           291:     }
        !           292: 
        !           293:     return ngx_http_next_header_filter(r);
        !           294: 
        !           295: no_charset_map:
        !           296: 
        !           297:     ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
        !           298:                   "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
        !           299:                   &src, &dst);
        !           300: 
        !           301:     return ngx_http_next_header_filter(r);
        !           302: }
        !           303: 
        !           304: 
        !           305: static ngx_int_t
        !           306: ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
        !           307: {
        !           308:     ngx_int_t                      charset;
        !           309:     ngx_http_charset_t            *charsets;
        !           310:     ngx_http_variable_value_t     *vv;
        !           311:     ngx_http_charset_loc_conf_t   *mlcf;
        !           312:     ngx_http_charset_main_conf_t  *mcf;
        !           313: 
        !           314:     if (!r->ignore_content_encoding
        !           315:         && r->headers_out.content_encoding
        !           316:         && r->headers_out.content_encoding->value.len)
        !           317:     {
        !           318:         return NGX_DECLINED;
        !           319:     }
        !           320: 
        !           321:     if (r->headers_out.content_type.len == 0) {
        !           322:         return NGX_DECLINED;
        !           323:     }
        !           324: 
        !           325:     if (r->headers_out.override_charset
        !           326:         && r->headers_out.override_charset->len)
        !           327:     {
        !           328:         *name = *r->headers_out.override_charset;
        !           329: 
        !           330:         charset = ngx_http_get_charset(r, name);
        !           331: 
        !           332:         if (charset != NGX_HTTP_NO_CHARSET) {
        !           333:             return charset;
        !           334:         }
        !           335: 
        !           336:         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
        !           337:                       "unknown charset \"%V\" to override", name);
        !           338: 
        !           339:         return NGX_DECLINED;
        !           340:     }
        !           341: 
        !           342:     mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
        !           343:     charset = mlcf->charset;
        !           344: 
        !           345:     if (charset == NGX_HTTP_CHARSET_OFF) {
        !           346:         return NGX_DECLINED;
        !           347:     }
        !           348: 
        !           349:     if (r->headers_out.charset.len) {
        !           350:         if (mlcf->override_charset == 0) {
        !           351:             return NGX_DECLINED;
        !           352:         }
        !           353: 
        !           354:     } else {
        !           355:         if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
        !           356:             return NGX_DECLINED;
        !           357:         }
        !           358:     }
        !           359: 
        !           360:     if (charset < NGX_HTTP_CHARSET_VAR) {
        !           361:         mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
        !           362:         charsets = mcf->charsets.elts;
        !           363:         *name = charsets[charset].name;
        !           364:         return charset;
        !           365:     }
        !           366: 
        !           367:     vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
        !           368: 
        !           369:     if (vv == NULL || vv->not_found) {
        !           370:         return NGX_ERROR;
        !           371:     }
        !           372: 
        !           373:     name->len = vv->len;
        !           374:     name->data = vv->data;
        !           375: 
        !           376:     return ngx_http_get_charset(r, name);
        !           377: }
        !           378: 
        !           379: 
        !           380: static ngx_int_t
        !           381: ngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)
        !           382: {
        !           383:     ngx_int_t                charset;
        !           384:     ngx_str_t               *main_charset;
        !           385:     ngx_http_charset_ctx_t  *ctx;
        !           386: 
        !           387:     ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);
        !           388: 
        !           389:     if (ctx) {
        !           390:         *src = ctx->charset_name;
        !           391:         return ctx->charset;
        !           392:     }
        !           393: 
        !           394:     main_charset = &r->main->headers_out.charset;
        !           395: 
        !           396:     if (main_charset->len == 0) {
        !           397:         return NGX_DECLINED;
        !           398:     }
        !           399: 
        !           400:     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
        !           401:     if (ctx == NULL) {
        !           402:         return NGX_ERROR;
        !           403:     }
        !           404: 
        !           405:     ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);
        !           406: 
        !           407:     charset = ngx_http_get_charset(r, main_charset);
        !           408: 
        !           409:     ctx->charset = charset;
        !           410:     ctx->charset_name = *main_charset;
        !           411:     *src = *main_charset;
        !           412: 
        !           413:     return charset;
        !           414: }
        !           415: 
        !           416: 
        !           417: static ngx_int_t
        !           418: ngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)
        !           419: {
        !           420:     ngx_int_t                      charset;
        !           421:     ngx_http_charset_t            *charsets;
        !           422:     ngx_http_variable_value_t     *vv;
        !           423:     ngx_http_charset_loc_conf_t   *lcf;
        !           424:     ngx_http_charset_main_conf_t  *mcf;
        !           425: 
        !           426:     if (r->headers_out.charset.len) {
        !           427:         *name = r->headers_out.charset;
        !           428:         return ngx_http_get_charset(r, name);
        !           429:     }
        !           430: 
        !           431:     lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
        !           432: 
        !           433:     charset = lcf->source_charset;
        !           434: 
        !           435:     if (charset == NGX_HTTP_CHARSET_OFF) {
        !           436:         name->len = 0;
        !           437:         return charset;
        !           438:     }
        !           439: 
        !           440:     if (charset < NGX_HTTP_CHARSET_VAR) {
        !           441:         mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
        !           442:         charsets = mcf->charsets.elts;
        !           443:         *name = charsets[charset].name;
        !           444:         return charset;
        !           445:     }
        !           446: 
        !           447:     vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
        !           448: 
        !           449:     if (vv == NULL || vv->not_found) {
        !           450:         return NGX_ERROR;
        !           451:     }
        !           452: 
        !           453:     name->len = vv->len;
        !           454:     name->data = vv->data;
        !           455: 
        !           456:     return ngx_http_get_charset(r, name);
        !           457: }
        !           458: 
        !           459: 
        !           460: static ngx_int_t
        !           461: ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)
        !           462: {
        !           463:     ngx_uint_t                     i, n;
        !           464:     ngx_http_charset_t            *charset;
        !           465:     ngx_http_charset_main_conf_t  *mcf;
        !           466: 
        !           467:     mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
        !           468: 
        !           469:     charset = mcf->charsets.elts;
        !           470:     n = mcf->charsets.nelts;
        !           471: 
        !           472:     for (i = 0; i < n; i++) {
        !           473:         if (charset[i].name.len != name->len) {
        !           474:             continue;
        !           475:         }
        !           476: 
        !           477:         if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {
        !           478:             return i;
        !           479:         }
        !           480:     }
        !           481: 
        !           482:     return NGX_HTTP_NO_CHARSET;
        !           483: }
        !           484: 
        !           485: 
        !           486: static ngx_inline void
        !           487: ngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)
        !           488: {
        !           489:     if (r != r->main) {
        !           490:         return;
        !           491:     }
        !           492: 
        !           493:     if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY
        !           494:         || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)
        !           495:     {
        !           496:         /*
        !           497:          * do not set charset for the redirect because NN 4.x
        !           498:          * use this charset instead of the next page charset
        !           499:          */
        !           500: 
        !           501:         r->headers_out.charset.len = 0;
        !           502:         return;
        !           503:     }
        !           504: 
        !           505:     r->headers_out.charset = *charset;
        !           506: }
        !           507: 
        !           508: 
        !           509: static ngx_int_t
        !           510: ngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,
        !           511:     ngx_int_t charset, ngx_int_t source_charset)
        !           512: {
        !           513:     ngx_http_charset_ctx_t  *ctx;
        !           514: 
        !           515:     ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));
        !           516:     if (ctx == NULL) {
        !           517:         return NGX_ERROR;
        !           518:     }
        !           519: 
        !           520:     ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);
        !           521: 
        !           522:     ctx->table = charsets[source_charset].tables[charset];
        !           523:     ctx->charset = charset;
        !           524:     ctx->charset_name = charsets[charset].name;
        !           525:     ctx->length = charsets[charset].length;
        !           526:     ctx->from_utf8 = charsets[source_charset].utf8;
        !           527:     ctx->to_utf8 = charsets[charset].utf8;
        !           528: 
        !           529:     r->filter_need_in_memory = 1;
        !           530: 
        !           531:     if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {
        !           532:         ngx_http_clear_content_length(r);
        !           533: 
        !           534:     } else {
        !           535:         r->filter_need_temporary = 1;
        !           536:     }
        !           537: 
        !           538:     return ngx_http_next_header_filter(r);
        !           539: }
        !           540: 
        !           541: 
        !           542: static ngx_int_t
        !           543: ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
        !           544: {
        !           545:     ngx_int_t                rc;
        !           546:     ngx_buf_t               *b;
        !           547:     ngx_chain_t             *cl, *out, **ll;
        !           548:     ngx_http_charset_ctx_t  *ctx;
        !           549: 
        !           550:     ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);
        !           551: 
        !           552:     if (ctx == NULL || ctx->table == NULL) {
        !           553:         return ngx_http_next_body_filter(r, in);
        !           554:     }
        !           555: 
        !           556:     if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {
        !           557: 
        !           558:         out = NULL;
        !           559:         ll = &out;
        !           560: 
        !           561:         for (cl = in; cl; cl = cl->next) {
        !           562:             b = cl->buf;
        !           563: 
        !           564:             if (ngx_buf_size(b) == 0) {
        !           565: 
        !           566:                 *ll = ngx_alloc_chain_link(r->pool);
        !           567:                 if (*ll == NULL) {
        !           568:                     return NGX_ERROR;
        !           569:                 }
        !           570: 
        !           571:                 (*ll)->buf = b;
        !           572:                 (*ll)->next = NULL;
        !           573: 
        !           574:                 ll = &(*ll)->next;
        !           575: 
        !           576:                 continue;
        !           577:             }
        !           578: 
        !           579:             if (ctx->to_utf8) {
        !           580:                 *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);
        !           581: 
        !           582:             } else {
        !           583:                 *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);
        !           584:             }
        !           585: 
        !           586:             if (*ll == NULL) {
        !           587:                 return NGX_ERROR;
        !           588:             }
        !           589: 
        !           590:             while (*ll) {
        !           591:                 ll = &(*ll)->next;
        !           592:             }
        !           593:         }
        !           594: 
        !           595:         rc = ngx_http_next_body_filter(r, out);
        !           596: 
        !           597:         if (out) {
        !           598:             if (ctx->busy == NULL) {
        !           599:                 ctx->busy = out;
        !           600: 
        !           601:             } else {
        !           602:                 for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }
        !           603:                 cl->next = out;
        !           604:             }
        !           605:         }
        !           606: 
        !           607:         while (ctx->busy) {
        !           608: 
        !           609:             cl = ctx->busy;
        !           610:             b = cl->buf;
        !           611: 
        !           612:             if (ngx_buf_size(b) != 0) {
        !           613:                 break;
        !           614:             }
        !           615: 
        !           616:             ctx->busy = cl->next;
        !           617: 
        !           618:             if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {
        !           619:                 continue;
        !           620:             }
        !           621: 
        !           622:             if (b->shadow) {
        !           623:                 b->shadow->pos = b->shadow->last;
        !           624:             }
        !           625: 
        !           626:             if (b->pos) {
        !           627:                 cl->next = ctx->free_buffers;
        !           628:                 ctx->free_buffers = cl;
        !           629:                 continue;
        !           630:             }
        !           631: 
        !           632:             cl->next = ctx->free_bufs;
        !           633:             ctx->free_bufs = cl;
        !           634:         }
        !           635: 
        !           636:         return rc;
        !           637:     }
        !           638: 
        !           639:     for (cl = in; cl; cl = cl->next) {
        !           640:         (void) ngx_http_charset_recode(cl->buf, ctx->table);
        !           641:     }
        !           642: 
        !           643:     return ngx_http_next_body_filter(r, in);
        !           644: }
        !           645: 
        !           646: 
        !           647: static ngx_uint_t
        !           648: ngx_http_charset_recode(ngx_buf_t *b, u_char *table)
        !           649: {
        !           650:     u_char  *p, *last;
        !           651: 
        !           652:     last = b->last;
        !           653: 
        !           654:     for (p = b->pos; p < last; p++) {
        !           655: 
        !           656:         if (*p != table[*p]) {
        !           657:             goto recode;
        !           658:         }
        !           659:     }
        !           660: 
        !           661:     return 0;
        !           662: 
        !           663: recode:
        !           664: 
        !           665:     do {
        !           666:         if (*p != table[*p]) {
        !           667:             *p = table[*p];
        !           668:         }
        !           669: 
        !           670:         p++;
        !           671: 
        !           672:     } while (p < last);
        !           673: 
        !           674:     b->in_file = 0;
        !           675: 
        !           676:     return 1;
        !           677: }
        !           678: 
        !           679: 
        !           680: static ngx_chain_t *
        !           681: ngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
        !           682:     ngx_http_charset_ctx_t *ctx)
        !           683: {
        !           684:     size_t        len, size;
        !           685:     u_char        c, *p, *src, *dst, *saved, **table;
        !           686:     uint32_t      n;
        !           687:     ngx_buf_t    *b;
        !           688:     ngx_uint_t    i;
        !           689:     ngx_chain_t  *out, *cl, **ll;
        !           690: 
        !           691:     src = buf->pos;
        !           692: 
        !           693:     if (ctx->saved_len == 0) {
        !           694: 
        !           695:         for ( /* void */ ; src < buf->last; src++) {
        !           696: 
        !           697:             if (*src < 0x80) {
        !           698:                 continue;
        !           699:             }
        !           700: 
        !           701:             len = src - buf->pos;
        !           702: 
        !           703:             if (len > 512) {
        !           704:                 out = ngx_http_charset_get_buf(pool, ctx);
        !           705:                 if (out == NULL) {
        !           706:                     return NULL;
        !           707:                 }
        !           708: 
        !           709:                 b = out->buf;
        !           710: 
        !           711:                 b->temporary = buf->temporary;
        !           712:                 b->memory = buf->memory;
        !           713:                 b->mmap = buf->mmap;
        !           714:                 b->flush = buf->flush;
        !           715: 
        !           716:                 b->pos = buf->pos;
        !           717:                 b->last = src;
        !           718: 
        !           719:                 out->buf = b;
        !           720:                 out->next = NULL;
        !           721: 
        !           722:                 size = buf->last - src;
        !           723: 
        !           724:                 saved = src;
        !           725:                 n = ngx_utf8_decode(&saved, size);
        !           726: 
        !           727:                 if (n == 0xfffffffe) {
        !           728:                     /* incomplete UTF-8 symbol */
        !           729: 
        !           730:                     ngx_memcpy(ctx->saved, src, size);
        !           731:                     ctx->saved_len = size;
        !           732: 
        !           733:                     b->shadow = buf;
        !           734: 
        !           735:                     return out;
        !           736:                 }
        !           737: 
        !           738:             } else {
        !           739:                 out = NULL;
        !           740:                 size = len + buf->last - src;
        !           741:                 src = buf->pos;
        !           742:             }
        !           743: 
        !           744:             if (size < NGX_HTML_ENTITY_LEN) {
        !           745:                 size += NGX_HTML_ENTITY_LEN;
        !           746:             }
        !           747: 
        !           748:             cl = ngx_http_charset_get_buffer(pool, ctx, size);
        !           749:             if (cl == NULL) {
        !           750:                 return NULL;
        !           751:             }
        !           752: 
        !           753:             if (out) {
        !           754:                 out->next = cl;
        !           755: 
        !           756:             } else {
        !           757:                 out = cl;
        !           758:             }
        !           759: 
        !           760:             b = cl->buf;
        !           761:             dst = b->pos;
        !           762: 
        !           763:             goto recode;
        !           764:         }
        !           765: 
        !           766:         out = ngx_alloc_chain_link(pool);
        !           767:         if (out == NULL) {
        !           768:             return NULL;
        !           769:         }
        !           770: 
        !           771:         out->buf = buf;
        !           772:         out->next = NULL;
        !           773: 
        !           774:         return out;
        !           775:     }
        !           776: 
        !           777:     /* process incomplete UTF sequence from previous buffer */
        !           778: 
        !           779:     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,
        !           780:                    "http charset utf saved: %z", ctx->saved_len);
        !           781: 
        !           782:     p = src;
        !           783: 
        !           784:     for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {
        !           785:         ctx->saved[i] = *p++;
        !           786: 
        !           787:         if (p == buf->last) {
        !           788:             break;
        !           789:         }
        !           790:     }
        !           791: 
        !           792:     saved = ctx->saved;
        !           793:     n = ngx_utf8_decode(&saved, i);
        !           794: 
        !           795:     c = '\0';
        !           796: 
        !           797:     if (n < 0x10000) {
        !           798:         table = (u_char **) ctx->table;
        !           799:         p = table[n >> 8];
        !           800: 
        !           801:         if (p) {
        !           802:             c = p[n & 0xff];
        !           803:         }
        !           804: 
        !           805:     } else if (n == 0xfffffffe) {
        !           806: 
        !           807:         /* incomplete UTF-8 symbol */
        !           808: 
        !           809:         if (i < NGX_UTF_LEN) {
        !           810:             out = ngx_http_charset_get_buf(pool, ctx);
        !           811:             if (out == NULL) {
        !           812:                 return NULL;
        !           813:             }
        !           814: 
        !           815:             b = out->buf;
        !           816: 
        !           817:             b->pos = buf->pos;
        !           818:             b->last = buf->last;
        !           819:             b->sync = 1;
        !           820:             b->shadow = buf;
        !           821: 
        !           822:             ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);
        !           823:             ctx->saved_len += i;
        !           824: 
        !           825:             return out;
        !           826:         }
        !           827:     }
        !           828: 
        !           829:     size = buf->last - buf->pos;
        !           830: 
        !           831:     if (size < NGX_HTML_ENTITY_LEN) {
        !           832:         size += NGX_HTML_ENTITY_LEN;
        !           833:     }
        !           834: 
        !           835:     cl = ngx_http_charset_get_buffer(pool, ctx, size);
        !           836:     if (cl == NULL) {
        !           837:         return NULL;
        !           838:     }
        !           839: 
        !           840:     out = cl;
        !           841: 
        !           842:     b = cl->buf;
        !           843:     dst = b->pos;
        !           844: 
        !           845:     if (c) {
        !           846:         *dst++ = c;
        !           847: 
        !           848:     } else if (n == 0xfffffffe) {
        !           849:         *dst++ = '?';
        !           850: 
        !           851:         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
        !           852:                        "http charset invalid utf 0");
        !           853: 
        !           854:         saved = &ctx->saved[NGX_UTF_LEN];
        !           855: 
        !           856:     } else if (n > 0x10ffff) {
        !           857:         *dst++ = '?';
        !           858: 
        !           859:         ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
        !           860:                        "http charset invalid utf 1");
        !           861: 
        !           862:     } else {
        !           863:         dst = ngx_sprintf(dst, "&#%uD;", n);
        !           864:     }
        !           865: 
        !           866:     src += (saved - ctx->saved) - ctx->saved_len;
        !           867:     ctx->saved_len = 0;
        !           868: 
        !           869: recode:
        !           870: 
        !           871:     ll = &cl->next;
        !           872: 
        !           873:     table = (u_char **) ctx->table;
        !           874: 
        !           875:     while (src < buf->last) {
        !           876: 
        !           877:         if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {
        !           878:             b->last = dst;
        !           879: 
        !           880:             size = buf->last - src + NGX_HTML_ENTITY_LEN;
        !           881: 
        !           882:             cl = ngx_http_charset_get_buffer(pool, ctx, size);
        !           883:             if (cl == NULL) {
        !           884:                 return NULL;
        !           885:             }
        !           886: 
        !           887:             *ll = cl;
        !           888:             ll = &cl->next;
        !           889: 
        !           890:             b = cl->buf;
        !           891:             dst = b->pos;
        !           892:         }
        !           893: 
        !           894:         if (*src < 0x80) {
        !           895:             *dst++ = *src++;
        !           896:             continue;
        !           897:         }
        !           898: 
        !           899:         len = buf->last - src;
        !           900: 
        !           901:         n = ngx_utf8_decode(&src, len);
        !           902: 
        !           903:         if (n < 0x10000) {
        !           904: 
        !           905:             p = table[n >> 8];
        !           906: 
        !           907:             if (p) {
        !           908:                 c = p[n & 0xff];
        !           909: 
        !           910:                 if (c) {
        !           911:                     *dst++ = c;
        !           912:                     continue;
        !           913:                 }
        !           914:             }
        !           915: 
        !           916:             dst = ngx_sprintf(dst, "&#%uD;", n);
        !           917: 
        !           918:             continue;
        !           919:         }
        !           920: 
        !           921:         if (n == 0xfffffffe) {
        !           922:             /* incomplete UTF-8 symbol */
        !           923: 
        !           924:             ngx_memcpy(ctx->saved, src, len);
        !           925:             ctx->saved_len = len;
        !           926: 
        !           927:             if (b->pos == dst) {
        !           928:                 b->sync = 1;
        !           929:                 b->temporary = 0;
        !           930:             }
        !           931: 
        !           932:             break;
        !           933:         }
        !           934: 
        !           935:         if (n > 0x10ffff) {
        !           936:             *dst++ = '?';
        !           937: 
        !           938:             ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,
        !           939:                            "http charset invalid utf 2");
        !           940: 
        !           941:             continue;
        !           942:         }
        !           943: 
        !           944:         /* n > 0xffff */
        !           945: 
        !           946:         dst = ngx_sprintf(dst, "&#%uD;", n);
        !           947:     }
        !           948: 
        !           949:     b->last = dst;
        !           950: 
        !           951:     b->last_buf = buf->last_buf;
        !           952:     b->last_in_chain = buf->last_in_chain;
        !           953:     b->flush = buf->flush;
        !           954: 
        !           955:     b->shadow = buf;
        !           956: 
        !           957:     return out;
        !           958: }
        !           959: 
        !           960: 
        !           961: static ngx_chain_t *
        !           962: ngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,
        !           963:     ngx_http_charset_ctx_t *ctx)
        !           964: {
        !           965:     size_t        len, size;
        !           966:     u_char       *p, *src, *dst, *table;
        !           967:     ngx_buf_t    *b;
        !           968:     ngx_chain_t  *out, *cl, **ll;
        !           969: 
        !           970:     table = ctx->table;
        !           971: 
        !           972:     for (src = buf->pos; src < buf->last; src++) {
        !           973:         if (table[*src * NGX_UTF_LEN] == '\1') {
        !           974:             continue;
        !           975:         }
        !           976: 
        !           977:         goto recode;
        !           978:     }
        !           979: 
        !           980:     out = ngx_alloc_chain_link(pool);
        !           981:     if (out == NULL) {
        !           982:         return NULL;
        !           983:     }
        !           984: 
        !           985:     out->buf = buf;
        !           986:     out->next = NULL;
        !           987: 
        !           988:     return out;
        !           989: 
        !           990: recode:
        !           991: 
        !           992:     /*
        !           993:      * we assume that there are about half of characters to be recoded,
        !           994:      * so we preallocate "size / 2 + size / 2 * ctx->length"
        !           995:      */
        !           996: 
        !           997:     len = src - buf->pos;
        !           998: 
        !           999:     if (len > 512) {
        !          1000:         out = ngx_http_charset_get_buf(pool, ctx);
        !          1001:         if (out == NULL) {
        !          1002:             return NULL;
        !          1003:         }
        !          1004: 
        !          1005:         b = out->buf;
        !          1006: 
        !          1007:         b->temporary = buf->temporary;
        !          1008:         b->memory = buf->memory;
        !          1009:         b->mmap = buf->mmap;
        !          1010:         b->flush = buf->flush;
        !          1011: 
        !          1012:         b->pos = buf->pos;
        !          1013:         b->last = src;
        !          1014: 
        !          1015:         out->buf = b;
        !          1016:         out->next = NULL;
        !          1017: 
        !          1018:         size = buf->last - src;
        !          1019:         size = size / 2 + size / 2 * ctx->length;
        !          1020: 
        !          1021:     } else {
        !          1022:         out = NULL;
        !          1023: 
        !          1024:         size = buf->last - src;
        !          1025:         size = len + size / 2 + size / 2 * ctx->length;
        !          1026: 
        !          1027:         src = buf->pos;
        !          1028:     }
        !          1029: 
        !          1030:     cl = ngx_http_charset_get_buffer(pool, ctx, size);
        !          1031:     if (cl == NULL) {
        !          1032:         return NULL;
        !          1033:     }
        !          1034: 
        !          1035:     if (out) {
        !          1036:         out->next = cl;
        !          1037: 
        !          1038:     } else {
        !          1039:         out = cl;
        !          1040:     }
        !          1041: 
        !          1042:     ll = &cl->next;
        !          1043: 
        !          1044:     b = cl->buf;
        !          1045:     dst = b->pos;
        !          1046: 
        !          1047:     while (src < buf->last) {
        !          1048: 
        !          1049:         p = &table[*src++ * NGX_UTF_LEN];
        !          1050:         len = *p++;
        !          1051: 
        !          1052:         if ((size_t) (b->end - dst) < len) {
        !          1053:             b->last = dst;
        !          1054: 
        !          1055:             size = buf->last - src;
        !          1056:             size = len + size / 2 + size / 2 * ctx->length;
        !          1057: 
        !          1058:             cl = ngx_http_charset_get_buffer(pool, ctx, size);
        !          1059:             if (cl == NULL) {
        !          1060:                 return NULL;
        !          1061:             }
        !          1062: 
        !          1063:             *ll = cl;
        !          1064:             ll = &cl->next;
        !          1065: 
        !          1066:             b = cl->buf;
        !          1067:             dst = b->pos;
        !          1068:         }
        !          1069: 
        !          1070:         while (len) {
        !          1071:             *dst++ = *p++;
        !          1072:             len--;
        !          1073:         }
        !          1074:     }
        !          1075: 
        !          1076:     b->last = dst;
        !          1077: 
        !          1078:     b->last_buf = buf->last_buf;
        !          1079:     b->last_in_chain = buf->last_in_chain;
        !          1080:     b->flush = buf->flush;
        !          1081: 
        !          1082:     b->shadow = buf;
        !          1083: 
        !          1084:     return out;
        !          1085: }
        !          1086: 
        !          1087: 
        !          1088: static ngx_chain_t *
        !          1089: ngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)
        !          1090: {
        !          1091:     ngx_chain_t  *cl;
        !          1092: 
        !          1093:     cl = ctx->free_bufs;
        !          1094: 
        !          1095:     if (cl) {
        !          1096:         ctx->free_bufs = cl->next;
        !          1097: 
        !          1098:         cl->buf->shadow = NULL;
        !          1099:         cl->next = NULL;
        !          1100: 
        !          1101:         return cl;
        !          1102:     }
        !          1103: 
        !          1104:     cl = ngx_alloc_chain_link(pool);
        !          1105:     if (cl == NULL) {
        !          1106:         return NULL;
        !          1107:     }
        !          1108: 
        !          1109:     cl->buf = ngx_calloc_buf(pool);
        !          1110:     if (cl->buf == NULL) {
        !          1111:         return NULL;
        !          1112:     }
        !          1113: 
        !          1114:     cl->next = NULL;
        !          1115: 
        !          1116:     cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
        !          1117: 
        !          1118:     return cl;
        !          1119: }
        !          1120: 
        !          1121: 
        !          1122: static ngx_chain_t *
        !          1123: ngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,
        !          1124:     size_t size)
        !          1125: {
        !          1126:     ngx_buf_t    *b;
        !          1127:     ngx_chain_t  *cl, **ll;
        !          1128: 
        !          1129:     for (ll = &ctx->free_buffers, cl = ctx->free_buffers;
        !          1130:          cl;
        !          1131:          ll = &cl->next, cl = cl->next)
        !          1132:     {
        !          1133:         b = cl->buf;
        !          1134: 
        !          1135:         if ((size_t) (b->end - b->start) >= size) {
        !          1136:             *ll = cl->next;
        !          1137:             cl->next = NULL;
        !          1138: 
        !          1139:             b->pos = b->start;
        !          1140:             b->temporary = 1;
        !          1141:             b->shadow = NULL;
        !          1142: 
        !          1143:             return cl;
        !          1144:         }
        !          1145:     }
        !          1146: 
        !          1147:     cl = ngx_alloc_chain_link(pool);
        !          1148:     if (cl == NULL) {
        !          1149:         return NULL;
        !          1150:     }
        !          1151: 
        !          1152:     cl->buf = ngx_create_temp_buf(pool, size);
        !          1153:     if (cl->buf == NULL) {
        !          1154:         return NULL;
        !          1155:     }
        !          1156: 
        !          1157:     cl->next = NULL;
        !          1158: 
        !          1159:     cl->buf->temporary = 1;
        !          1160:     cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;
        !          1161: 
        !          1162:     return cl;
        !          1163: }
        !          1164: 
        !          1165: 
        !          1166: static char *
        !          1167: ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
        !          1168: {
        !          1169:     ngx_http_charset_main_conf_t  *mcf = conf;
        !          1170: 
        !          1171:     char                         *rv;
        !          1172:     u_char                       *p, *dst2src, **pp;
        !          1173:     ngx_int_t                     src, dst;
        !          1174:     ngx_uint_t                    i, n;
        !          1175:     ngx_str_t                    *value;
        !          1176:     ngx_conf_t                    pvcf;
        !          1177:     ngx_http_charset_t           *charset;
        !          1178:     ngx_http_charset_tables_t    *table;
        !          1179:     ngx_http_charset_conf_ctx_t   ctx;
        !          1180: 
        !          1181:     value = cf->args->elts;
        !          1182: 
        !          1183:     src = ngx_http_add_charset(&mcf->charsets, &value[1]);
        !          1184:     if (src == NGX_ERROR) {
        !          1185:         return NGX_CONF_ERROR;
        !          1186:     }
        !          1187: 
        !          1188:     dst = ngx_http_add_charset(&mcf->charsets, &value[2]);
        !          1189:     if (dst == NGX_ERROR) {
        !          1190:         return NGX_CONF_ERROR;
        !          1191:     }
        !          1192: 
        !          1193:     if (src == dst) {
        !          1194:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !          1195:                            "\"charset_map\" between the same charsets "
        !          1196:                            "\"%V\" and \"%V\"", &value[1], &value[2]);
        !          1197:         return NGX_CONF_ERROR;
        !          1198:     }
        !          1199: 
        !          1200:     table = mcf->tables.elts;
        !          1201:     for (i = 0; i < mcf->tables.nelts; i++) {
        !          1202:         if ((src == table->src && dst == table->dst)
        !          1203:              || (src == table->dst && dst == table->src))
        !          1204:         {
        !          1205:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !          1206:                                "duplicate \"charset_map\" between "
        !          1207:                                "\"%V\" and \"%V\"", &value[1], &value[2]);
        !          1208:             return NGX_CONF_ERROR;
        !          1209:         }
        !          1210:     }
        !          1211: 
        !          1212:     table = ngx_array_push(&mcf->tables);
        !          1213:     if (table == NULL) {
        !          1214:         return NGX_CONF_ERROR;
        !          1215:     }
        !          1216: 
        !          1217:     table->src = src;
        !          1218:     table->dst = dst;
        !          1219: 
        !          1220:     if (ngx_strcasecmp(value[2].data, (u_char *) "utf-8") == 0) {
        !          1221:         table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);
        !          1222:         if (table->src2dst == NULL) {
        !          1223:             return NGX_CONF_ERROR;
        !          1224:         }
        !          1225: 
        !          1226:         table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));
        !          1227:         if (table->dst2src == NULL) {
        !          1228:             return NGX_CONF_ERROR;
        !          1229:         }
        !          1230: 
        !          1231:         dst2src = ngx_pcalloc(cf->pool, 256);
        !          1232:         if (dst2src == NULL) {
        !          1233:             return NGX_CONF_ERROR;
        !          1234:         }
        !          1235: 
        !          1236:         pp = (u_char **) &table->dst2src[0];
        !          1237:         pp[0] = dst2src;
        !          1238: 
        !          1239:         for (i = 0; i < 128; i++) {
        !          1240:             p = &table->src2dst[i * NGX_UTF_LEN];
        !          1241:             p[0] = '\1';
        !          1242:             p[1] = (u_char) i;
        !          1243:             dst2src[i] = (u_char) i;
        !          1244:         }
        !          1245: 
        !          1246:         for (/* void */; i < 256; i++) {
        !          1247:             p = &table->src2dst[i * NGX_UTF_LEN];
        !          1248:             p[0] = '\1';
        !          1249:             p[1] = '?';
        !          1250:         }
        !          1251: 
        !          1252:     } else {
        !          1253:         table->src2dst = ngx_palloc(cf->pool, 256);
        !          1254:         if (table->src2dst == NULL) {
        !          1255:             return NGX_CONF_ERROR;
        !          1256:         }
        !          1257: 
        !          1258:         table->dst2src = ngx_palloc(cf->pool, 256);
        !          1259:         if (table->dst2src == NULL) {
        !          1260:             return NGX_CONF_ERROR;
        !          1261:         }
        !          1262: 
        !          1263:         for (i = 0; i < 128; i++) {
        !          1264:             table->src2dst[i] = (u_char) i;
        !          1265:             table->dst2src[i] = (u_char) i;
        !          1266:         }
        !          1267: 
        !          1268:         for (/* void */; i < 256; i++) {
        !          1269:             table->src2dst[i] = '?';
        !          1270:             table->dst2src[i] = '?';
        !          1271:         }
        !          1272:     }
        !          1273: 
        !          1274:     charset = mcf->charsets.elts;
        !          1275: 
        !          1276:     ctx.table = table;
        !          1277:     ctx.charset = &charset[dst];
        !          1278:     ctx.characters = 0;
        !          1279: 
        !          1280:     pvcf = *cf;
        !          1281:     cf->ctx = &ctx;
        !          1282:     cf->handler = ngx_http_charset_map;
        !          1283:     cf->handler_conf = conf;
        !          1284: 
        !          1285:     rv = ngx_conf_parse(cf, NULL);
        !          1286: 
        !          1287:     *cf = pvcf;
        !          1288: 
        !          1289:     if (ctx.characters) {
        !          1290:         n = ctx.charset->length;
        !          1291:         ctx.charset->length /= ctx.characters;
        !          1292: 
        !          1293:         if (((n * 10) / ctx.characters) % 10 > 4) {
        !          1294:             ctx.charset->length++;
        !          1295:         }
        !          1296:     }
        !          1297: 
        !          1298:     return rv;
        !          1299: }
        !          1300: 
        !          1301: 
        !          1302: static char *
        !          1303: ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)
        !          1304: {
        !          1305:     u_char                       *p, *dst2src, **pp;
        !          1306:     uint32_t                      n;
        !          1307:     ngx_int_t                     src, dst;
        !          1308:     ngx_str_t                    *value;
        !          1309:     ngx_uint_t                    i;
        !          1310:     ngx_http_charset_tables_t    *table;
        !          1311:     ngx_http_charset_conf_ctx_t  *ctx;
        !          1312: 
        !          1313:     if (cf->args->nelts != 2) {
        !          1314:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number");
        !          1315:         return NGX_CONF_ERROR;
        !          1316:     }
        !          1317: 
        !          1318:     value = cf->args->elts;
        !          1319: 
        !          1320:     src = ngx_hextoi(value[0].data, value[0].len);
        !          1321:     if (src == NGX_ERROR || src > 255) {
        !          1322:         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !          1323:                            "invalid value \"%V\"", &value[0]);
        !          1324:         return NGX_CONF_ERROR;
        !          1325:     }
        !          1326: 
        !          1327:     ctx = cf->ctx;
        !          1328:     table = ctx->table;
        !          1329: 
        !          1330:     if (ctx->charset->utf8) {
        !          1331:         p = &table->src2dst[src * NGX_UTF_LEN];
        !          1332: 
        !          1333:         *p++ = (u_char) (value[1].len / 2);
        !          1334: 
        !          1335:         for (i = 0; i < value[1].len; i += 2) {
        !          1336:             dst = ngx_hextoi(&value[1].data[i], 2);
        !          1337:             if (dst == NGX_ERROR || dst > 255) {
        !          1338:                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !          1339:                                    "invalid value \"%V\"", &value[1]);
        !          1340:                 return NGX_CONF_ERROR;
        !          1341:             }
        !          1342: 
        !          1343:             *p++ = (u_char) dst;
        !          1344:         }
        !          1345: 
        !          1346:         i /= 2;
        !          1347: 
        !          1348:         ctx->charset->length += i;
        !          1349:         ctx->characters++;
        !          1350: 
        !          1351:         p = &table->src2dst[src * NGX_UTF_LEN] + 1;
        !          1352: 
        !          1353:         n = ngx_utf8_decode(&p, i);
        !          1354: 
        !          1355:         if (n > 0xffff) {
        !          1356:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !          1357:                                "invalid value \"%V\"", &value[1]);
        !          1358:             return NGX_CONF_ERROR;
        !          1359:         }
        !          1360: 
        !          1361:         pp = (u_char **) &table->dst2src[0];
        !          1362: 
        !          1363:         dst2src = pp[n >> 8];
        !          1364: 
        !          1365:         if (dst2src == NULL) {
        !          1366:             dst2src = ngx_pcalloc(cf->pool, 256);
        !          1367:             if (dst2src == NULL) {
        !          1368:                 return NGX_CONF_ERROR;
        !          1369:             }
        !          1370: 
        !          1371:             pp[n >> 8] = dst2src;
        !          1372:         }
        !          1373: 
        !          1374:         dst2src[n & 0xff] = (u_char) src;
        !          1375: 
        !          1376:     } else {
        !          1377:         dst = ngx_hextoi(value[1].data, value[1].len);
        !          1378:         if (dst == NGX_ERROR || dst > 255) {
        !          1379:             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
        !          1380:                                "invalid value \"%V\"", &value[1]);
        !          1381:             return NGX_CONF_ERROR;
        !          1382:         }
        !          1383: 
        !          1384:         table->src2dst[src] = (u_char) dst;
        !          1385:         table->dst2src[dst] = (u_char) src;
        !          1386:     }
        !          1387: 
        !          1388:     return NGX_CONF_OK;
        !          1389: }
        !          1390: 
        !          1391: 
        !          1392: static char *
        !          1393: ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
        !          1394: {
        !          1395:     char  *p = conf;
        !          1396: 
        !          1397:     ngx_int_t                     *cp;
        !          1398:     ngx_str_t                     *value, var;
        !          1399:     ngx_http_charset_main_conf_t  *mcf;
        !          1400: 
        !          1401:     cp = (ngx_int_t *) (p + cmd->offset);
        !          1402: 
        !          1403:     if (*cp != NGX_CONF_UNSET) {
        !          1404:         return "is duplicate";
        !          1405:     }
        !          1406: 
        !          1407:     value = cf->args->elts;
        !          1408: 
        !          1409:     if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)
        !          1410:         && ngx_strcmp(value[1].data, "off") == 0)
        !          1411:     {
        !          1412:         *cp = NGX_HTTP_CHARSET_OFF;
        !          1413:         return NGX_CONF_OK;
        !          1414:     }
        !          1415: 
        !          1416: 
        !          1417:     if (value[1].data[0] == '$') {
        !          1418:         var.len = value[1].len - 1;
        !          1419:         var.data = value[1].data + 1;
        !          1420: 
        !          1421:         *cp = ngx_http_get_variable_index(cf, &var);
        !          1422: 
        !          1423:         if (*cp == NGX_ERROR) {
        !          1424:             return NGX_CONF_ERROR;
        !          1425:         }
        !          1426: 
        !          1427:         *cp += NGX_HTTP_CHARSET_VAR;
        !          1428: 
        !          1429:         return NGX_CONF_OK;
        !          1430:     }
        !          1431: 
        !          1432:     mcf = ngx_http_conf_get_module_main_conf(cf,
        !          1433:                                              ngx_http_charset_filter_module);
        !          1434: 
        !          1435:     *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);
        !          1436:     if (*cp == NGX_ERROR) {
        !          1437:         return NGX_CONF_ERROR;
        !          1438:     }
        !          1439: 
        !          1440:     return NGX_CONF_OK;
        !          1441: }
        !          1442: 
        !          1443: 
        !          1444: static ngx_int_t
        !          1445: ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)
        !          1446: {
        !          1447:     ngx_uint_t           i;
        !          1448:     ngx_http_charset_t  *c;
        !          1449: 
        !          1450:     c = charsets->elts;
        !          1451:     for (i = 0; i < charsets->nelts; i++) {
        !          1452:         if (name->len != c[i].name.len) {
        !          1453:             continue;
        !          1454:         }
        !          1455: 
        !          1456:         if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {
        !          1457:             break;
        !          1458:         }
        !          1459:     }
        !          1460: 
        !          1461:     if (i < charsets->nelts) {
        !          1462:         return i;
        !          1463:     }
        !          1464: 
        !          1465:     c = ngx_array_push(charsets);
        !          1466:     if (c == NULL) {
        !          1467:         return NGX_ERROR;
        !          1468:     }
        !          1469: 
        !          1470:     c->tables = NULL;
        !          1471:     c->name = *name;
        !          1472:     c->length = 0;
        !          1473: 
        !          1474:     if (ngx_strcasecmp(name->data, (u_char *) "utf-8") == 0) {
        !          1475:         c->utf8 = 1;
        !          1476: 
        !          1477:     } else {
        !          1478:         c->utf8 = 0;
        !          1479:     }
        !          1480: 
        !          1481:     return i;
        !          1482: }
        !          1483: 
        !          1484: 
        !          1485: static void *
        !          1486: ngx_http_charset_create_main_conf(ngx_conf_t *cf)
        !          1487: {
        !          1488:     ngx_http_charset_main_conf_t  *mcf;
        !          1489: 
        !          1490:     mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));
        !          1491:     if (mcf == NULL) {
        !          1492:         return NULL;
        !          1493:     }
        !          1494: 
        !          1495:     if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))
        !          1496:         != NGX_OK)
        !          1497:     {
        !          1498:         return NULL;
        !          1499:     }
        !          1500: 
        !          1501:     if (ngx_array_init(&mcf->tables, cf->pool, 1,
        !          1502:                        sizeof(ngx_http_charset_tables_t))
        !          1503:         != NGX_OK)
        !          1504:     {
        !          1505:         return NULL;
        !          1506:     }
        !          1507: 
        !          1508:     if (ngx_array_init(&mcf->recodes, cf->pool, 2,
        !          1509:                        sizeof(ngx_http_charset_recode_t))
        !          1510:         != NGX_OK)
        !          1511:     {
        !          1512:         return NULL;
        !          1513:     }
        !          1514: 
        !          1515:     return mcf;
        !          1516: }
        !          1517: 
        !          1518: 
        !          1519: static void *
        !          1520: ngx_http_charset_create_loc_conf(ngx_conf_t *cf)
        !          1521: {
        !          1522:     ngx_http_charset_loc_conf_t  *lcf;
        !          1523: 
        !          1524:     lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));
        !          1525:     if (lcf == NULL) {
        !          1526:         return NULL;
        !          1527:     }
        !          1528: 
        !          1529:     /*
        !          1530:      * set by ngx_pcalloc():
        !          1531:      *
        !          1532:      *     lcf->types = { NULL };
        !          1533:      *     lcf->types_keys = NULL;
        !          1534:      */
        !          1535: 
        !          1536:     lcf->charset = NGX_CONF_UNSET;
        !          1537:     lcf->source_charset = NGX_CONF_UNSET;
        !          1538:     lcf->override_charset = NGX_CONF_UNSET;
        !          1539: 
        !          1540:     return lcf;
        !          1541: }
        !          1542: 
        !          1543: 
        !          1544: static char *
        !          1545: ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
        !          1546: {
        !          1547:     ngx_http_charset_loc_conf_t *prev = parent;
        !          1548:     ngx_http_charset_loc_conf_t *conf = child;
        !          1549: 
        !          1550:     ngx_uint_t                     i;
        !          1551:     ngx_http_charset_recode_t     *recode;
        !          1552:     ngx_http_charset_main_conf_t  *mcf;
        !          1553: 
        !          1554:     if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,
        !          1555:                              &prev->types_keys, &prev->types,
        !          1556:                              ngx_http_charset_default_types)
        !          1557:         != NGX_OK)
        !          1558:     {
        !          1559:         return NGX_CONF_ERROR;
        !          1560:     }
        !          1561: 
        !          1562:     ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);
        !          1563:     ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);
        !          1564:     ngx_conf_merge_value(conf->source_charset, prev->source_charset,
        !          1565:                          NGX_HTTP_CHARSET_OFF);
        !          1566: 
        !          1567:     if (conf->charset == NGX_HTTP_CHARSET_OFF
        !          1568:         || conf->source_charset == NGX_HTTP_CHARSET_OFF
        !          1569:         || conf->charset == conf->source_charset)
        !          1570:     {
        !          1571:         return NGX_CONF_OK;
        !          1572:     }
        !          1573: 
        !          1574:     if (conf->source_charset >= NGX_HTTP_CHARSET_VAR
        !          1575:         || conf->charset >= NGX_HTTP_CHARSET_VAR)
        !          1576:     {
        !          1577:         return NGX_CONF_OK;
        !          1578:     }
        !          1579: 
        !          1580:     mcf = ngx_http_conf_get_module_main_conf(cf,
        !          1581:                                              ngx_http_charset_filter_module);
        !          1582:     recode = mcf->recodes.elts;
        !          1583:     for (i = 0; i < mcf->recodes.nelts; i++) {
        !          1584:         if (conf->source_charset == recode[i].src
        !          1585:             && conf->charset == recode[i].dst)
        !          1586:         {
        !          1587:             return NGX_CONF_OK;
        !          1588:         }
        !          1589:     }
        !          1590: 
        !          1591:     recode = ngx_array_push(&mcf->recodes);
        !          1592:     if (recode == NULL) {
        !          1593:         return NGX_CONF_ERROR;
        !          1594:     }
        !          1595: 
        !          1596:     recode->src = conf->source_charset;
        !          1597:     recode->dst = conf->charset;
        !          1598: 
        !          1599:     return NGX_CONF_OK;
        !          1600: }
        !          1601: 
        !          1602: 
        !          1603: static ngx_int_t
        !          1604: ngx_http_charset_postconfiguration(ngx_conf_t *cf)
        !          1605: {
        !          1606:     u_char                       **src, **dst;
        !          1607:     ngx_int_t                      c;
        !          1608:     ngx_uint_t                     i, t;
        !          1609:     ngx_http_charset_t            *charset;
        !          1610:     ngx_http_charset_recode_t     *recode;
        !          1611:     ngx_http_charset_tables_t     *tables;
        !          1612:     ngx_http_charset_main_conf_t  *mcf;
        !          1613: 
        !          1614:     mcf = ngx_http_conf_get_module_main_conf(cf,
        !          1615:                                              ngx_http_charset_filter_module);
        !          1616: 
        !          1617:     recode = mcf->recodes.elts;
        !          1618:     tables = mcf->tables.elts;
        !          1619:     charset = mcf->charsets.elts;
        !          1620: 
        !          1621:     for (i = 0; i < mcf->recodes.nelts; i++) {
        !          1622: 
        !          1623:         c = recode[i].src;
        !          1624: 
        !          1625:         for (t = 0; t < mcf->tables.nelts; t++) {
        !          1626: 
        !          1627:             if (c == tables[t].src && recode[i].dst == tables[t].dst) {
        !          1628:                 goto next;
        !          1629:             }
        !          1630: 
        !          1631:             if (c == tables[t].dst && recode[i].dst == tables[t].src) {
        !          1632:                 goto next;
        !          1633:             }
        !          1634:         }
        !          1635: 
        !          1636:         ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
        !          1637:                    "no \"charset_map\" between the charsets \"%V\" and \"%V\"",
        !          1638:                    &charset[c].name, &charset[recode[i].dst].name);
        !          1639:         return NGX_ERROR;
        !          1640: 
        !          1641:     next:
        !          1642:         continue;
        !          1643:     }
        !          1644: 
        !          1645: 
        !          1646:     for (t = 0; t < mcf->tables.nelts; t++) {
        !          1647: 
        !          1648:         src = charset[tables[t].src].tables;
        !          1649: 
        !          1650:         if (src == NULL) {
        !          1651:             src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
        !          1652:             if (src == NULL) {
        !          1653:                 return NGX_ERROR;
        !          1654:             }
        !          1655: 
        !          1656:             charset[tables[t].src].tables = src;
        !          1657:         }
        !          1658: 
        !          1659:         dst = charset[tables[t].dst].tables;
        !          1660: 
        !          1661:         if (dst == NULL) {
        !          1662:             dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);
        !          1663:             if (dst == NULL) {
        !          1664:                 return NGX_ERROR;
        !          1665:             }
        !          1666: 
        !          1667:             charset[tables[t].dst].tables = dst;
        !          1668:         }
        !          1669: 
        !          1670:         src[tables[t].dst] = tables[t].src2dst;
        !          1671:         dst[tables[t].src] = tables[t].dst2src;
        !          1672:     }
        !          1673: 
        !          1674:     ngx_http_next_header_filter = ngx_http_top_header_filter;
        !          1675:     ngx_http_top_header_filter = ngx_http_charset_header_filter;
        !          1676: 
        !          1677:     ngx_http_next_body_filter = ngx_http_top_body_filter;
        !          1678:     ngx_http_top_body_filter = ngx_http_charset_body_filter;
        !          1679: 
        !          1680:     return NGX_OK;
        !          1681: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>