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("") - 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>