Groonga 3.0.9 Source Code Document
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ngx_http_charset_filter_module.c
Go to the documentation of this file.
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;
26 
27  unsigned length:16;
28  unsigned utf8:1;
30 
31 
32 typedef struct {
36 
37 
38 typedef struct {
41  u_char *src2dst;
42  u_char *dst2src;
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 */
51 
52 
53 typedef struct {
57 
61 
62 
63 typedef struct {
64  u_char *table;
67 
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;
79 
80 
81 typedef struct {
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,
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 
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"),
134 };
135 
136 
137 static ngx_command_t ngx_http_charset_filter_commands[] = {
138 
139  { ngx_string("charset"),
142  ngx_http_set_charset_slot,
144  offsetof(ngx_http_charset_loc_conf_t, charset),
145  NULL },
146 
147  { ngx_string("source_charset"),
150  ngx_http_set_charset_slot,
152  offsetof(ngx_http_charset_loc_conf_t, source_charset),
153  NULL },
154 
155  { ngx_string("override_charset"),
160  offsetof(ngx_http_charset_loc_conf_t, override_charset),
161  NULL },
162 
163  { ngx_string("charset_types"),
167  offsetof(ngx_http_charset_loc_conf_t, types_keys),
168  &ngx_http_charset_default_types[0] },
169 
170  { ngx_string("charset_map"),
172  ngx_http_charset_map_block,
174  0,
175  NULL },
176 
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 
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 */
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;
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 
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 
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 
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;
313 
317  {
318  return NGX_DECLINED;
319  }
320 
321  if (r->headers_out.content_type.len == 0) {
322  return NGX_DECLINED;
323  }
324 
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 
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 
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;
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;
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 
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;
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 
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 {
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) {
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;
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,
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 
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 
852  "http charset invalid utf 0");
853 
854  saved = &ctx->saved[NGX_UTF_LEN];
855 
856  } else if (n > 0x10ffff) {
857  *dst++ = '?';
858 
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 
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,
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;
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) {
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  {
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;
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) {
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) {
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) {
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) {
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;
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 
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 {
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 {
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;
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;
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 
1566 
1567  if (conf->charset == NGX_HTTP_CHARSET_OFF
1569  || conf->charset == conf->source_charset)
1570  {
1571  return NGX_CONF_OK;
1572  }
1573 
1575  || conf->charset >= NGX_HTTP_CHARSET_VAR)
1576  {
1577  return NGX_CONF_OK;
1578  }
1579 
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;
1613 
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 
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 }