Mercurial > hg > nginx-mail
comparison src/http/modules/ngx_http_charset_filter.c @ 0:f0b350454894 NGINX_0_1_0
nginx 0.1.0
*) The first public version.
author | Igor Sysoev <http://sysoev.ru> |
---|---|
date | Mon, 04 Oct 2004 00:00:00 +0400 |
parents | |
children | 4b2dafa26fe2 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f0b350454894 |
---|---|
1 | |
2 /* | |
3 * Copyright (C) Igor Sysoev | |
4 */ | |
5 | |
6 | |
7 #include <ngx_config.h> | |
8 #include <ngx_core.h> | |
9 #include <ngx_http.h> | |
10 | |
11 | |
12 typedef struct { | |
13 char **tables; | |
14 ngx_str_t name; | |
15 unsigned server; | |
16 } ngx_http_charset_t; | |
17 | |
18 | |
19 typedef struct { | |
20 ngx_int_t src; | |
21 ngx_int_t dst; | |
22 char *src2dst; | |
23 char *dst2src; | |
24 } ngx_http_charset_tables_t; | |
25 | |
26 | |
27 typedef struct { | |
28 ngx_array_t charsets; /* ngx_http_charset_t */ | |
29 ngx_array_t tables; /* ngx_http_charset_tables_t */ | |
30 } ngx_http_charset_main_conf_t; | |
31 | |
32 | |
33 typedef struct { | |
34 ngx_flag_t enable; | |
35 ngx_flag_t autodetect; | |
36 | |
37 ngx_int_t default_charset; | |
38 ngx_int_t source_charset; | |
39 } ngx_http_charset_loc_conf_t; | |
40 | |
41 | |
42 typedef struct { | |
43 ngx_int_t server; | |
44 ngx_int_t client; | |
45 } ngx_http_charset_ctx_t; | |
46 | |
47 | |
48 static void ngx_charset_recode(ngx_buf_t *b, char *table); | |
49 | |
50 static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, | |
51 void *conf); | |
52 static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf); | |
53 | |
54 static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
55 void *conf); | |
56 static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name); | |
57 | |
58 static ngx_int_t ngx_http_charset_filter_init(ngx_cycle_t *cycle); | |
59 | |
60 static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf); | |
61 static char *ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf); | |
62 static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf); | |
63 static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, | |
64 void *parent, void *child); | |
65 | |
66 | |
67 static ngx_command_t ngx_http_charset_filter_commands[] = { | |
68 | |
69 { ngx_string("charset_map"), | |
70 NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2, | |
71 ngx_charset_map_block, | |
72 NGX_HTTP_MAIN_CONF_OFFSET, | |
73 0, | |
74 NULL }, | |
75 | |
76 { ngx_string("default_charset"), | |
77 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
78 ngx_http_set_charset_slot, | |
79 NGX_HTTP_LOC_CONF_OFFSET, | |
80 offsetof(ngx_http_charset_loc_conf_t, default_charset), | |
81 NULL }, | |
82 | |
83 { ngx_string("source_charset"), | |
84 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, | |
85 ngx_http_set_charset_slot, | |
86 NGX_HTTP_LOC_CONF_OFFSET, | |
87 offsetof(ngx_http_charset_loc_conf_t, source_charset), | |
88 NULL }, | |
89 | |
90 { ngx_string("charset"), | |
91 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
92 ngx_conf_set_flag_slot, | |
93 NGX_HTTP_LOC_CONF_OFFSET, | |
94 offsetof(ngx_http_charset_loc_conf_t, enable), | |
95 NULL }, | |
96 | |
97 { ngx_string("autodetect_charset"), | |
98 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, | |
99 ngx_conf_set_flag_slot, | |
100 NGX_HTTP_LOC_CONF_OFFSET, | |
101 offsetof(ngx_http_charset_loc_conf_t, autodetect), | |
102 NULL }, | |
103 | |
104 ngx_null_command | |
105 }; | |
106 | |
107 | |
108 static ngx_http_module_t ngx_http_charset_filter_module_ctx = { | |
109 NULL, /* pre conf */ | |
110 | |
111 ngx_http_charset_create_main_conf, /* create main configuration */ | |
112 ngx_http_charset_init_main_conf, /* init main configuration */ | |
113 | |
114 NULL, /* create server configuration */ | |
115 NULL, /* merge server configuration */ | |
116 | |
117 ngx_http_charset_create_loc_conf, /* create location configuration */ | |
118 ngx_http_charset_merge_loc_conf /* merge location configuration */ | |
119 }; | |
120 | |
121 | |
122 ngx_module_t ngx_http_charset_filter_module = { | |
123 NGX_MODULE, | |
124 &ngx_http_charset_filter_module_ctx, /* module context */ | |
125 ngx_http_charset_filter_commands, /* module directives */ | |
126 NGX_HTTP_MODULE, /* module type */ | |
127 ngx_http_charset_filter_init, /* init module */ | |
128 NULL /* init child */ | |
129 }; | |
130 | |
131 | |
132 static ngx_http_output_header_filter_pt ngx_http_next_header_filter; | |
133 static ngx_http_output_body_filter_pt ngx_http_next_body_filter; | |
134 | |
135 | |
136 static ngx_int_t ngx_http_charset_header_filter(ngx_http_request_t *r) | |
137 { | |
138 ngx_http_charset_t *charsets; | |
139 ngx_http_charset_ctx_t *ctx; | |
140 ngx_http_charset_loc_conf_t *lcf; | |
141 ngx_http_charset_main_conf_t *mcf; | |
142 | |
143 mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); | |
144 lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); | |
145 | |
146 if (lcf->enable == 0) { | |
147 return ngx_http_next_header_filter(r); | |
148 } | |
149 | |
150 #if 0 | |
151 if (lcf->default_charset.len == 0) { | |
152 return ngx_http_next_header_filter(r); | |
153 } | |
154 #endif | |
155 | |
156 if (r->headers_out.content_type == NULL) { | |
157 return ngx_http_next_header_filter(r); | |
158 } | |
159 | |
160 if (ngx_strncasecmp(r->headers_out.content_type->value.data, | |
161 "text/", 5) != 0 | |
162 && ngx_strncasecmp(r->headers_out.content_type->value.data, | |
163 "application/x-javascript", 24) != 0) | |
164 { | |
165 return ngx_http_next_header_filter(r); | |
166 } | |
167 | |
168 if (ngx_strstr(r->headers_out.content_type->value.data, "charset") != NULL) | |
169 { | |
170 return ngx_http_next_header_filter(r); | |
171 } | |
172 | |
173 if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY | |
174 && r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY) | |
175 { | |
176 /* | |
177 * do not set charset for the redirect because NN 4.x uses this | |
178 * charset instead of the next page charset | |
179 */ | |
180 | |
181 r->headers_out.charset.len = 0; | |
182 return ngx_http_next_header_filter(r); | |
183 } | |
184 | |
185 if (r->headers_out.charset.len) { | |
186 return ngx_http_next_header_filter(r); | |
187 } | |
188 | |
189 charsets = mcf->charsets.elts; | |
190 r->headers_out.charset = charsets[lcf->default_charset].name; | |
191 | |
192 if (lcf->default_charset == lcf->source_charset) { | |
193 return ngx_http_next_header_filter(r); | |
194 } | |
195 | |
196 ngx_http_create_ctx(r, ctx, ngx_http_charset_filter_module, | |
197 sizeof(ngx_http_charset_ctx_t), NGX_ERROR); | |
198 | |
199 r->filter_need_in_memory = 1; | |
200 | |
201 return ngx_http_next_header_filter(r); | |
202 } | |
203 | |
204 | |
205 static ngx_int_t ngx_http_charset_body_filter(ngx_http_request_t *r, | |
206 ngx_chain_t *in) | |
207 { | |
208 char *table; | |
209 ngx_chain_t *cl; | |
210 ngx_http_charset_t *charsets; | |
211 ngx_http_charset_ctx_t *ctx; | |
212 ngx_http_charset_loc_conf_t *lcf; | |
213 ngx_http_charset_main_conf_t *mcf; | |
214 | |
215 ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module); | |
216 | |
217 if (ctx == NULL) { | |
218 return ngx_http_next_body_filter(r, in); | |
219 } | |
220 | |
221 mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module); | |
222 lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module); | |
223 | |
224 charsets = mcf->charsets.elts; | |
225 table = charsets[lcf->source_charset].tables[lcf->default_charset]; | |
226 | |
227 for (cl = in; cl; cl = cl->next) { | |
228 ngx_charset_recode(cl->buf, table); | |
229 } | |
230 | |
231 return ngx_http_next_body_filter(r, in); | |
232 } | |
233 | |
234 | |
235 static void ngx_charset_recode(ngx_buf_t *b, char *table) | |
236 { | |
237 u_char *p, c; | |
238 | |
239 for (p = b->pos; p < b->last; p++) { | |
240 c = *p; | |
241 *p = table[c]; | |
242 } | |
243 } | |
244 | |
245 | |
246 static char *ngx_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, | |
247 void *conf) | |
248 { | |
249 ngx_http_charset_main_conf_t *mcf = conf; | |
250 | |
251 char *rv; | |
252 ngx_int_t src, dst; | |
253 ngx_uint_t i; | |
254 ngx_str_t *value; | |
255 ngx_conf_t pvcf; | |
256 ngx_http_charset_tables_t *table; | |
257 | |
258 value = cf->args->elts; | |
259 | |
260 src = ngx_http_add_charset(&mcf->charsets, &value[1]); | |
261 if (src == NGX_ERROR) { | |
262 return NGX_CONF_ERROR; | |
263 } | |
264 | |
265 dst = ngx_http_add_charset(&mcf->charsets, &value[2]); | |
266 if (dst == NGX_ERROR) { | |
267 return NGX_CONF_ERROR; | |
268 } | |
269 | |
270 if (src == dst) { | |
271 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
272 "\"charset_map\" between the same charsets " | |
273 "\"%s\" and \"%s\"", | |
274 value[1].data, value[2].data); | |
275 return NGX_CONF_ERROR; | |
276 } | |
277 | |
278 table = mcf->tables.elts; | |
279 for (i = 0; i < mcf->tables.nelts; i++) { | |
280 if ((src == table->src && dst == table->dst) | |
281 || (src == table->dst && dst == table->src)) | |
282 { | |
283 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
284 "duplicate \"charset_map\" between " | |
285 "\"%s\" and \"%s\"", | |
286 value[1].data, value[2].data); | |
287 return NGX_CONF_ERROR; | |
288 } | |
289 } | |
290 | |
291 if (!(table = ngx_push_array(&mcf->tables))) { | |
292 return NGX_CONF_ERROR; | |
293 } | |
294 | |
295 table->src = src; | |
296 table->dst = dst; | |
297 | |
298 if (!(table->src2dst = ngx_palloc(cf->pool, 256))) { | |
299 return NGX_CONF_ERROR; | |
300 } | |
301 | |
302 if (!(table->dst2src = ngx_palloc(cf->pool, 256))) { | |
303 return NGX_CONF_ERROR; | |
304 } | |
305 | |
306 for (i = 0; i < 128; i++) { | |
307 table->src2dst[i] = (char) i; | |
308 table->dst2src[i] = (char) i; | |
309 } | |
310 | |
311 for (/* void */; i < 256; i++) { | |
312 table->src2dst[i] = '?'; | |
313 table->dst2src[i] = '?'; | |
314 } | |
315 | |
316 pvcf = *cf; | |
317 cf->ctx = table; | |
318 cf->handler = ngx_charset_map; | |
319 cf->handler_conf = conf; | |
320 rv = ngx_conf_parse(cf, NULL); | |
321 *cf = pvcf; | |
322 | |
323 return rv; | |
324 } | |
325 | |
326 | |
327 static char *ngx_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf) | |
328 { | |
329 ngx_int_t src, dst; | |
330 ngx_str_t *value; | |
331 ngx_http_charset_tables_t *table; | |
332 | |
333 if (cf->args->nelts != 2) { | |
334 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameters number"); | |
335 return NGX_CONF_ERROR; | |
336 } | |
337 | |
338 value = cf->args->elts; | |
339 | |
340 src = ngx_hextoi(value[0].data, value[0].len); | |
341 if (src == NGX_ERROR || src > 255) { | |
342 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
343 "invalid value \"%s\"", value[0].data); | |
344 return NGX_CONF_ERROR; | |
345 } | |
346 | |
347 dst = ngx_hextoi(value[1].data, value[1].len); | |
348 if (dst == NGX_ERROR || dst > 255) { | |
349 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, | |
350 "invalid value \"%s\"", value[1].data); | |
351 return NGX_CONF_ERROR; | |
352 } | |
353 | |
354 table = cf->ctx; | |
355 | |
356 table->src2dst[src] = (char) dst; | |
357 table->dst2src[dst] = (char) src; | |
358 | |
359 return NGX_CONF_OK; | |
360 } | |
361 | |
362 | |
363 static char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, | |
364 void *conf) | |
365 { | |
366 char *p = conf; | |
367 | |
368 ngx_int_t *cp; | |
369 ngx_str_t *value; | |
370 ngx_http_charset_t *charset; | |
371 ngx_http_charset_main_conf_t *mcf; | |
372 | |
373 cp = (ngx_int_t *) (p + cmd->offset); | |
374 | |
375 if (*cp != NGX_CONF_UNSET) { | |
376 return "is duplicate"; | |
377 } | |
378 | |
379 mcf = ngx_http_conf_get_module_main_conf(cf, | |
380 ngx_http_charset_filter_module); | |
381 | |
382 value = cf->args->elts; | |
383 | |
384 *cp = ngx_http_add_charset(&mcf->charsets, &value[1]); | |
385 if (*cp == NGX_ERROR) { | |
386 return NGX_CONF_ERROR; | |
387 } | |
388 | |
389 if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, source_charset)) { | |
390 charset = mcf->charsets.elts; | |
391 charset[*cp].server = 1; | |
392 } | |
393 | |
394 return NGX_CONF_OK; | |
395 } | |
396 | |
397 | |
398 static ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name) | |
399 { | |
400 ngx_uint_t i; | |
401 ngx_http_charset_t *c; | |
402 | |
403 c = charsets->elts; | |
404 for (i = 0; i < charsets->nelts; i++) { | |
405 if (name->len != c[i].name.len) { | |
406 continue; | |
407 } | |
408 | |
409 if (ngx_strcasecmp(name->data, c[i].name.data) == 0) { | |
410 break; | |
411 } | |
412 } | |
413 | |
414 if (i < charsets->nelts) { | |
415 return i; | |
416 } | |
417 | |
418 if (!(c = ngx_push_array(charsets))) { | |
419 return NGX_ERROR; | |
420 } | |
421 | |
422 c->name = *name; | |
423 | |
424 return i; | |
425 } | |
426 | |
427 | |
428 static ngx_int_t ngx_http_charset_filter_init(ngx_cycle_t *cycle) | |
429 { | |
430 ngx_http_next_header_filter = ngx_http_top_header_filter; | |
431 ngx_http_top_header_filter = ngx_http_charset_header_filter; | |
432 | |
433 ngx_http_next_body_filter = ngx_http_top_body_filter; | |
434 ngx_http_top_body_filter = ngx_http_charset_body_filter; | |
435 | |
436 return NGX_OK; | |
437 } | |
438 | |
439 | |
440 static void *ngx_http_charset_create_main_conf(ngx_conf_t *cf) | |
441 { | |
442 ngx_http_charset_main_conf_t *mcf; | |
443 | |
444 if (!(mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t)))) { | |
445 return NGX_CONF_ERROR; | |
446 } | |
447 | |
448 ngx_init_array(mcf->charsets, cf->pool, 5, sizeof(ngx_http_charset_t), | |
449 NGX_CONF_ERROR); | |
450 | |
451 ngx_init_array(mcf->tables, cf->pool, 10, sizeof(ngx_http_charset_tables_t), | |
452 NGX_CONF_ERROR); | |
453 | |
454 return mcf; | |
455 } | |
456 | |
457 | |
458 static char *ngx_http_charset_init_main_conf(ngx_conf_t *cf, void *conf) | |
459 { | |
460 ngx_http_charset_main_conf_t *mcf = conf; | |
461 | |
462 ngx_uint_t i, n; | |
463 ngx_http_charset_t *charset; | |
464 ngx_http_charset_tables_t *tables; | |
465 | |
466 tables = mcf->tables.elts; | |
467 charset = mcf->charsets.elts; | |
468 | |
469 for (i = 0; i < mcf->charsets.nelts; i++) { | |
470 if (!charset[i].server) { | |
471 continue; | |
472 } | |
473 | |
474 charset[i].tables = ngx_pcalloc(cf->pool, | |
475 sizeof(char *) * mcf->charsets.nelts); | |
476 | |
477 if (charset[i].tables == NULL) { | |
478 return NGX_CONF_ERROR; | |
479 } | |
480 | |
481 for (n = 0; n < mcf->tables.nelts; n++) { | |
482 if ((ngx_int_t) i == tables[n].src) { | |
483 charset[i].tables[tables[n].dst] = tables[n].src2dst; | |
484 continue; | |
485 } | |
486 | |
487 if ((ngx_int_t) i == tables[n].dst) { | |
488 charset[i].tables[tables[n].src] = tables[n].dst2src; | |
489 } | |
490 } | |
491 } | |
492 | |
493 for (i = 0; i < mcf->charsets.nelts; i++) { | |
494 if (!charset[i].server) { | |
495 continue; | |
496 } | |
497 | |
498 for (n = 0; n < mcf->charsets.nelts; n++) { | |
499 if (i == n) { | |
500 continue; | |
501 } | |
502 | |
503 if (charset[i].tables[n]) { | |
504 continue; | |
505 } | |
506 | |
507 ngx_log_error(NGX_LOG_EMERG, cf->log, 0, | |
508 " no \"charset_map\" between the charsets " | |
509 "\"%s\" and \"%s\"", | |
510 charset[i].name.data, charset[n].name.data); | |
511 return NGX_CONF_ERROR; | |
512 } | |
513 } | |
514 | |
515 return NGX_CONF_OK; | |
516 } | |
517 | |
518 | |
519 static void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf) | |
520 { | |
521 ngx_http_charset_loc_conf_t *lcf; | |
522 | |
523 if (!(lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t)))) { | |
524 return NGX_CONF_ERROR; | |
525 } | |
526 | |
527 lcf->enable = NGX_CONF_UNSET; | |
528 lcf->autodetect = NGX_CONF_UNSET; | |
529 lcf->default_charset = NGX_CONF_UNSET; | |
530 lcf->source_charset = NGX_CONF_UNSET; | |
531 | |
532 return lcf; | |
533 } | |
534 | |
535 | |
536 static char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf, | |
537 void *parent, void *child) | |
538 { | |
539 ngx_http_charset_loc_conf_t *prev = parent; | |
540 ngx_http_charset_loc_conf_t *conf = child; | |
541 | |
542 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
543 ngx_conf_merge_value(conf->autodetect, prev->autodetect, 0); | |
544 | |
545 if (conf->source_charset == NGX_CONF_UNSET) { | |
546 conf->source_charset = prev->source_charset; | |
547 } | |
548 | |
549 ngx_conf_merge_value(conf->default_charset, prev->default_charset, | |
550 conf->source_charset); | |
551 | |
552 return NGX_CONF_OK; | |
553 } |