Annotation of embedaddon/nginx/src/http/modules/ngx_http_charset_filter_module.c, revision 1.1.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>