2 * Copyright (C) 2005 QSDN,Inc. All rights reserved.
3 * Copyright (C) 2005 Atsushi Konno All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
21 #include "http_config.h"
22 #include "http_core.h"
23 #include "http_protocol.h"
25 #include "ap_config.h"
26 #include "apr_strings.h"
27 #include "util_filter.h"
28 #include "apr_buckets.h"
30 #include "apr_tables.h"
32 #include "apr_general.h"
33 #include "apr_pools.h"
36 #include "chxj_encoding.h"
37 #include "qs_ignore_sp.h"
39 #include "qs_malloc.h"
40 #include "qs_parse_attr.h"
41 #include "qs_parse_file.h"
42 #include "qs_parse_string.h"
43 #include "qs_parse_tag.h"
44 #include "chxj_load_device_data.h"
45 #include "chxj_load_emoji_data.h"
46 #include "chxj_specified_device.h"
47 #include "chxj_tag_util.h"
48 #include "chxj_xhtml_mobile_1_0.h"
49 #include "chxj_hdml.h"
50 #include "chxj_chtml10.h"
51 #include "chxj_chtml20.h"
52 #include "chxj_chtml30.h"
53 #include "chxj_jhtml.h"
55 #include "chxj_img_conv_format.h"
56 #include "chxj_qr_code.h"
57 #include "chxj_encoding.h"
58 #include "chxj_apply_convrule.h"
59 #include "chxj_cookie.h"
60 #include "chxj_url_encode.h"
63 #define CHXJ_VERSION_PREFIX PACKAGE_NAME "/"
64 #define CHXJ_VERSION PACKAGE_VERSION
66 converter_t convert_routine[] = {
68 /* CHXJ_SPEC_UNKNOWN */
73 /* CHXJ_SPEC_Chtml_1_0 */
74 .converter = chxj_exchange_chtml10,
75 .encoder = chxj_encoding,
78 /* CHXJ_SPEC_Chtml_2_0 */
79 .converter = chxj_exchange_chtml20,
80 .encoder = chxj_encoding,
83 /* CHXJ_SPEC_Chtml_3_0 */
84 .converter = chxj_exchange_chtml30,
85 .encoder = chxj_encoding,
88 /* CHXJ_SPEC_Chtml_4_0 */
89 .converter = chxj_exchange_chtml30,
90 .encoder = chxj_encoding,
93 /* CHXJ_SPEC_Chtml_5_0 */
94 .converter = chxj_exchange_chtml30,
95 .encoder = chxj_encoding,
98 /* CHXJ_SPEC_XHtml_Mobile_1_0 */
99 .converter = chxj_exchange_xhtml_mobile_1_0,
100 .encoder = chxj_encoding,
104 .converter = chxj_exchange_hdml,
105 .encoder = chxj_encoding,
108 /* CHXJ_SPEC_Jhtml */
109 .converter = chxj_exchange_jhtml,
110 .encoder = chxj_encoding,
122 static int chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp);
125 * Only when User-Agent is specified, the User-Agent header is camouflaged.
130 chxj_headers_fixup(request_rec *r)
132 mod_chxj_config* dconf;
133 chxjconvrule_entry* entryp;
137 DBG(r, "start chxj_headers_fixup()");
138 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
140 user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
141 spec = chxj_specified_device(r, user_agent);
143 switch(spec->html_spec_type) {
144 case CHXJ_SPEC_Chtml_1_0:
145 case CHXJ_SPEC_Chtml_2_0:
146 case CHXJ_SPEC_Chtml_3_0:
147 case CHXJ_SPEC_Chtml_4_0:
148 case CHXJ_SPEC_Chtml_5_0:
149 case CHXJ_SPEC_XHtml_Mobile_1_0:
151 case CHXJ_SPEC_Jhtml:
152 entryp = chxj_apply_convrule(r, dconf->convrules);
154 DBG(r, "end chxj_headers_fixup() no pattern");
158 apr_table_setn(r->headers_in,
159 CHXJ_HTTP_USER_AGENT,
162 if (entryp->user_agent)
163 apr_table_setn(r->headers_in,
167 chxj_convert_input_header(r,entryp);
176 DBG(r, "end chxj_headers_fixup()");
183 * It converts it from CHTML into XXML corresponding to each model.
186 * @param src [i] It is former HTML character string.
187 * @param len [i/o] It is length of former HTML character string.
190 chxj_exchange(request_rec *r, const char** src, apr_size_t* len)
196 mod_chxj_config* dconf;
197 chxjconvrule_entry* entryp;
199 dst = apr_pstrcat(r->pool, (char*)*src, NULL);
201 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
204 entryp = chxj_apply_convrule(r, dconf->convrules);
206 if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT))
210 /*------------------------------------------------------------------------*/
211 /* get UserAgent from http header */
212 /*------------------------------------------------------------------------*/
213 if (entryp->user_agent)
214 user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
216 user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
218 DBG1(r,"User-Agent:[%s]", user_agent);
219 DBG(r, "start chxj_exchange()");
220 DBG1(r,"content type is %s", r->content_type);
223 if (*(char*)r->content_type == 't'
224 && strncmp(r->content_type, "text/html", 9) != 0) {
225 DBG1(r,"content type is %s", r->content_type);
233 if (entryp->action & CONVRULE_COOKIE_ON_BIT)
234 cookie_id = chxj_save_cookie(r);
236 if (!r->header_only) {
237 device_table* spec = chxj_specified_device(r, user_agent);
240 if (convert_routine[spec->html_spec_type].encoder)
241 tmp = convert_routine[spec->html_spec_type].encoder(r,
245 if (convert_routine[spec->html_spec_type].converter) {
247 dst = convert_routine[spec->html_spec_type].converter(r,
255 dst = convert_routine[spec->html_spec_type].converter(r,
266 dst = apr_psprintf(r->pool, "\n");
271 DBG(r, "end chxj_exchange()");
278 * It converts it from HEADER.
283 chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
295 DBG(r, "start chxj_convert_input_header()");
298 DBG(r, "r->args=[null]");
299 DBG(r, "end chxj_convert_input_header()");
302 urilen = strlen(r->args);
304 result = qs_alloc_zero_byte_string(r);
306 buff = apr_pstrdup(r->pool, r->args);
307 DBG1(r, "r->args=[%s]", buff);
315 pair = apr_strtok(buff, "&", &pstate);
321 name = apr_strtok(pair, "=", &vstate);
322 value = apr_strtok(NULL, "=", &vstate);
323 if (strncasecmp(name, "_chxj", 5) != 0) {
324 if (strlen(result) != 0)
325 result = apr_pstrcat(r->pool, result, "&", NULL);
327 if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
331 dlen = strlen(value);
332 value = chxj_url_decode(r, value);
333 DBG1(r, "************ before encoding[%s]", value);
335 dvalue = chxj_rencoding(r, value, &dlen);
336 dvalue = chxj_url_encode(r, dvalue);
338 DBG1(r, "************ after encoding[%s]", dvalue);
340 result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
343 if (strcmp(name, pair) != 0)
344 result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
346 result = apr_pstrcat(r->pool, result, name, NULL);
350 if (strncasecmp(name, "_chxj_c_", 8) == 0
351 || strncasecmp(name, "_chxj_r_", 8) == 0
352 || strncasecmp(name, "_chxj_s_", 8) == 0) {
356 if (strlen(value) == 0)
359 if (strlen(result) != 0)
360 result = apr_pstrcat(r->pool, result, "&", NULL);
362 result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
365 if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
366 DBG1(r, "found cookie parameter[%s]", value);
367 chxj_load_cookie(r, value);
372 DBG1(r, "result r->args=[%s]", r->args);
373 DBG(r, "end chxj_convert_input_header()");
379 * It converts it from POSTDATA .
382 * @param src [i] It is POSTDATA character string.
383 * @param len [i/o] It is length of former HTML character string.
390 chxjconvrule_entry* entryp)
400 s = apr_pstrdup(r->pool, *src);
402 result = qs_alloc_zero_byte_string(r);
404 DBG1(r, "BEFORE input convert source = [%s]", s);
411 pair = apr_strtok(s, "&", &pstate);
416 name = apr_strtok(pair, "=", &vstate);
417 value = apr_strtok(NULL, "=", &vstate);
418 if (strncasecmp(name, "_chxj", 5) != 0) {
419 if (strlen(result) != 0)
420 result = apr_pstrcat(r->pool, result, "&", NULL);
422 if (strcasecmp(entryp->encoding, "NONE") != 0
423 && value && strlen(value)) {
427 dlen = strlen(value);
428 value = chxj_url_decode(r, value);
429 DBG1(r, "************ before encoding[%s]", value);
431 dvalue = chxj_rencoding(r, value, &dlen);
432 dvalue = chxj_url_encode(r,dvalue);
434 DBG1(r, "************ after encoding[%s]", dvalue);
436 result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
440 result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
444 if (strncasecmp(name, "_chxj_c_", 8) == 0
445 || strncasecmp(name, "_chxj_r_", 8) == 0
446 || strncasecmp(name, "_chxj_s_", 8) == 0) {
450 if (strlen(value) == 0)
453 if (strlen(result) != 0)
454 result = apr_pstrcat(r->pool, result, "&", NULL);
456 if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
460 dlen = strlen(value);
461 value = chxj_url_decode(r, value);
462 DBG1(r, "************ before encoding[%s]", value);
464 dvalue = chxj_rencoding(r, value, &dlen);
465 dvalue = chxj_url_encode(r,dvalue);
467 DBG1(r, "************ after encoding[%s]", dvalue);
469 result = apr_pstrcat(r->pool, result, &name[8], "=", dvalue, NULL);
473 result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
477 if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
478 DBG1(r, "found cookie parameter[%s]", value);
479 chxj_load_cookie(r, value);
482 *len = strlen(result);
484 DBG1(r, "AFTER input convert result = [%s]", result);
491 * The received data is returned to the filter.
493 * @param f [i/o] It is a filter.
494 * @param data [i] It is data returned to the filter.
495 * @param len [i] It is length of the data returned to the filter.
498 pass_data_to_filter(ap_filter_t *f, const char *data,
501 request_rec* r = f->r;
502 conn_rec* c = r->connection;
504 apr_bucket_brigade* bb;
507 DBG(r, "start pass_data_to_filter()");
509 bb = apr_brigade_create(r->pool, c->bucket_alloc);
510 b = apr_bucket_transient_create(data, len, c->bucket_alloc);
512 APR_BRIGADE_INSERT_TAIL(bb, b);
513 b = apr_bucket_eos_create(f->c->bucket_alloc);
514 APR_BRIGADE_INSERT_TAIL(bb, b);
516 rv = ap_pass_brigade(f->next, bb);
517 if (rv != APR_SUCCESS) {
518 DBG(r, "ap_pass_brigade()");
522 DBG(r, "end pass_data_to_filter()");
529 * It is the main loop of the output filter.
531 * @param f [i/o] It is a filter.
535 chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
545 char* location_header;
546 mod_chxj_config* dconf;
547 chxjconvrule_entry* entryp;
551 DBG(f->r, "start of chxj_output_filter()");
557 if ((f->r->proto_num >= 1001)
563 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
564 entryp = chxj_apply_convrule(r, dconf->convrules);
567 for (b = APR_BRIGADE_FIRST(bb);
568 b != APR_BRIGADE_SENTINEL(bb);
569 b = APR_BUCKET_NEXT(b)) {
571 if (APR_BUCKET_IS_EOS(b)) {
574 /*----------------------------------------------------------------------*/
576 /*----------------------------------------------------------------------*/
579 ctx = (mod_chxj_ctx*)f->ctx;
581 DBG1(r, "content_type=[%s]", r->content_type);
584 && *(char*)r->content_type == 't'
585 && strncmp(r->content_type, "text/html", 9) == 0) {
590 tmp = apr_palloc(r->pool, ctx->len + 1);
592 memset(tmp, 0, ctx->len + 1);
593 memcpy(tmp, ctx->buffer, ctx->len);
595 DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
597 ctx->buffer = chxj_exchange(r,
599 (apr_size_t*)&ctx->len);
601 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
604 ctx->buffer = apr_psprintf(r->pool, "\n");
606 ctx->buffer = chxj_exchange(r,
607 (const char**)&ctx->buffer,
608 (apr_size_t*)&ctx->len);
614 && *(char*)r->content_type == 'i'
615 && strncmp(r->content_type, "image/", 6) == 0) {
619 tmp = apr_palloc(r->pool, ctx->len + 1);
621 memset(tmp, 0, ctx->len + 1);
622 memcpy(tmp, ctx->buffer, ctx->len);
624 DBG1(r, "input data=[%s]", tmp);
627 chxj_exchange_image(r,
629 (apr_size_t*)&ctx->len);
631 if (ctx->buffer == NULL)
634 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
638 contentLength = apr_psprintf(r->pool, "%d", ctx->len);
639 apr_table_setn(r->headers_out, "Content-Length", contentLength);
642 rv = pass_data_to_filter(f,
643 (const char*)ctx->buffer,
644 (apr_size_t)ctx->len);
651 DBG1(r, " SAVE COOKIE[%x]", entryp->action);
656 if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
657 DBG(r, "entryp->action == COOKIE_ON_BIT");
659 cookie_id = chxj_save_cookie(r);
662 * Location Header Check to add cookie parameter.
664 location_header = (char*)apr_table_get(r->headers_out, "Location");
665 if (location_header) {
666 DBG1(r, "Location Header=[%s]", location_header);
667 location_header = chxj_add_cookie_parameter(r,
670 apr_table_setn(r->headers_out, "Location", location_header);
671 DBG1(r, "Location Header=[%s]", location_header);
675 apr_table_setn(r->headers_out, "Content-Length", "0");
676 rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
682 if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
683 DBG2(r, "read data[%.*s]",len, data);
685 if (f->ctx == NULL) {
686 /*--------------------------------------------------------------------*/
688 /*--------------------------------------------------------------------*/
689 DBG(r, "new context");
690 ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
692 ctx->buffer = apr_palloc(r->pool, len);
693 memcpy(ctx->buffer, data, len);
696 ctx->buffer = apr_palloc(r->pool, 1);
703 /*--------------------------------------------------------------------*/
705 /*--------------------------------------------------------------------*/
707 DBG(r, "append data start");
708 ctx = (mod_chxj_ctx*)f->ctx;
711 tmp = apr_palloc(r->pool, ctx->len);
712 memcpy(tmp, ctx->buffer, ctx->len);
714 ctx->buffer = apr_palloc(r->pool, ctx->len + len);
716 memcpy(ctx->buffer, tmp, ctx->len);
717 memcpy(&ctx->buffer[ctx->len], data, len);
721 DBG(r, "append data end");
725 apr_brigade_destroy(bb);
727 DBG(r, "end of output filter");
734 * It is the main loop of the input filter.
736 * @param f [i/o] It is a filter.
737 * @param bb [i] brigade
738 * @param mode [i] mode
739 * @param block [i] block
740 * @param readbytes [i] readbyte
743 chxj_input_filter(ap_filter_t* f,
744 apr_bucket_brigade* bb,
745 ap_input_mode_t mode,
746 apr_read_type_e block,
749 request_rec* r = f->r;
751 conn_rec* c = r->connection;
753 /*--------------------------------------------------------------------------*/
754 /* It is the brigade area for output */
755 /*--------------------------------------------------------------------------*/
756 apr_bucket_brigade* ibb;
757 /*--------------------------------------------------------------------------*/
758 /* It is the brigade area for input */
759 /*--------------------------------------------------------------------------*/
760 apr_bucket_brigade* obb;
762 apr_bucket* tmp_heap;
770 mod_chxj_config* dconf;
771 chxjconvrule_entry* entryp;
773 DBG(r, "start of chxj_input_filter()");
775 data_brigade = qs_alloc_zero_byte_string(r);
778 ibb = apr_brigade_create(r->pool, c->bucket_alloc);
779 obb = apr_brigade_create(r->pool, c->bucket_alloc);
781 content_type = (char*)apr_table_get(r->headers_in, "Content-Type");
783 && strncasecmp("multipart/form-data", content_type, 19) == 0) {
785 DBG(r, "detect multipart/form-data");
786 ap_remove_input_filter(f);
788 return ap_get_brigade(f->next, bb, mode, block, readbytes);
791 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
793 entryp = chxj_apply_convrule(r, dconf->convrules);
794 if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
797 ap_remove_input_filter(f);
798 return ap_get_brigade(f->next, bb, mode, block, readbytes);
801 user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
802 spec = chxj_specified_device(r, user_agent);
804 switch(spec->html_spec_type) {
805 case CHXJ_SPEC_Chtml_1_0:
806 case CHXJ_SPEC_Chtml_2_0:
807 case CHXJ_SPEC_Chtml_3_0:
808 case CHXJ_SPEC_Chtml_4_0:
809 case CHXJ_SPEC_Chtml_5_0:
810 case CHXJ_SPEC_XHtml_Mobile_1_0:
812 case CHXJ_SPEC_Jhtml:
816 ap_remove_input_filter(f);
817 return ap_get_brigade(f->next, bb, mode, block, readbytes);
821 rv = ap_get_brigade(f->next, ibb, mode, block, readbytes);
822 if (rv != APR_SUCCESS) {
823 DBG(r, "ap_get_brigade() failed");
827 APR_BRIGADE_FOREACH(b, ibb) {
828 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
829 if (rv != APR_SUCCESS) {
830 DBG(r, "apr_bucket_read() failed");
835 data_bucket = apr_palloc(r->pool, len+1);
836 memset((void*)data_bucket, 0, len+1);
837 memcpy(data_bucket, data, len);
838 DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
840 data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
843 if (APR_BUCKET_IS_EOS(b)) {
847 apr_brigade_cleanup(ibb);
850 len = strlen(data_brigade);
852 DBG(r,"data_brigade length is 0");
853 DBG(r,"end of chxj_input_filter()");
854 ap_remove_input_filter(f);
855 return ap_get_brigade(f->next, bb, mode, block, readbytes);
857 data_brigade = chxj_input_convert(
859 (const char**)&data_brigade,
865 DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
867 obb = apr_brigade_create(r->pool, c->bucket_alloc);
869 tmp_heap = apr_bucket_heap_create(data_brigade,
873 eos = apr_bucket_eos_create(f->c->bucket_alloc);
875 APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
876 APR_BRIGADE_INSERT_TAIL(obb, eos);
877 APR_BRIGADE_CONCAT(bb, obb);
880 DBG(r, "end of chxj_input_filter()");
886 static mod_chxj_global_config*
887 chxj_global_config_create(apr_pool_t* pool, server_rec* s)
889 mod_chxj_global_config* conf;
891 SDBG(s, "start chxj_global_config_create()");
893 /*--------------------------------------------------------------------------*/
894 /* allocate an own subpool which survives server restarts */
895 /*--------------------------------------------------------------------------*/
896 conf = (mod_chxj_global_config*)apr_palloc(pool,
897 sizeof(mod_chxj_global_config));
899 conf->cookie_db_lock = NULL;
901 SDBG(s, "end chxj_global_config_create()");
907 * initialize chxj module
910 chxj_init_module(apr_pool_t *p,
916 mod_chxj_global_config* conf;
920 SDBG(s, "start chxj_init_module()");
922 apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
924 if (user_data == NULL) {
927 * dummy user_data set.
929 apr_pool_userdata_set(
932 apr_pool_cleanup_null,
934 SDBG(s, "end chxj_init_module()");
938 ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
942 conf = (mod_chxj_global_config *)ap_get_module_config(s->module_config,
945 if (apr_global_mutex_create(&(conf->cookie_db_lock),
946 NULL, APR_LOCK_DEFAULT, p) != APR_SUCCESS) {
947 SERR(s, "end chxj_init_module()");
948 return HTTP_INTERNAL_SERVER_ERROR;
951 #ifdef AP_NEED_SET_MUTEX_PERMS
952 if (unixd_set_global_mutex_perms(conf->cookie_db_lock) != APR_SUCCESS) {
953 SERR(s, "end chxj_init_module()");
954 return HTTP_INTERNAL_SERVER_ERROR;
960 SDBG(s, "end chxj_init_module()");
966 chxj_child_init(apr_pool_t *p, server_rec *s)
969 mod_chxj_global_config* conf;
972 SDBG(s, "start chxj_child_init()");
975 conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config,
978 if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p)
980 SERR(s, "Can't attach global mutex.");
985 SDBG(s, "end chxj_child_init()");
990 * A set structure of each server is generated.
996 chxj_config_server_create(apr_pool_t *p, server_rec *s)
998 mod_chxj_global_config *gc;
1000 gc = chxj_global_config_create(p,s);
1007 chxj_translate_name(request_rec *r)
1009 return chxj_trans_name(r);
1014 * The hook is registered.
1019 chxj_register_hooks(apr_pool_t *p)
1021 ap_hook_post_config(chxj_init_module,
1024 APR_HOOK_REALLY_FIRST);
1025 ap_hook_child_init(chxj_child_init,
1028 APR_HOOK_REALLY_FIRST);
1029 ap_register_output_filter (
1030 "chxj_output_filter",
1034 ap_register_input_filter(
1035 "chxj_input_filter",
1039 ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1040 ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1041 ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1042 ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1047 * A set structure according to the directory is generated.
1053 chxj_create_per_dir_config(apr_pool_t *p, char *arg)
1055 mod_chxj_config* conf;
1057 conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1058 conf->device_data_file = NULL;
1059 conf->devices = NULL;
1060 conf->emoji_data_file = NULL;
1062 conf->emoji_tail = NULL;
1063 conf->image = CHXJ_IMG_OFF;
1064 conf->image_cache_dir = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1065 conf->server_side_encoding = NULL;
1070 conf->dir = apr_pcalloc(p, strlen(arg)+1);
1071 memset(conf->dir, 0, strlen(arg)+1);
1072 strcpy(conf->dir, arg);
1074 conf->convrules = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1076 /* Default is copyleft */
1077 conf->image_copyright = NULL;
1084 * Merge per-directory CHXJ configurations
1087 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1089 mod_chxj_config *base;
1090 mod_chxj_config *add;
1091 mod_chxj_config *mrg;
1093 base = (mod_chxj_config*)basev;
1094 add = (mod_chxj_config*)addv;
1095 mrg = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1097 mrg->device_data_file = NULL;
1098 mrg->devices = NULL;
1099 mrg->emoji_data_file = NULL;
1100 mrg->image = CHXJ_IMG_OFF;
1101 mrg->image_cache_dir = NULL;
1102 mrg->image_copyright = NULL;
1104 mrg->emoji_tail = NULL;
1106 mrg->dir = apr_pstrdup(p, add->dir);
1108 if (! add->device_data_file) {
1109 mrg->devices = base->devices;
1110 mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1113 mrg->devices = add->devices;
1114 mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1117 if (! add->emoji_data_file) {
1118 mrg->emoji = base->emoji;
1119 mrg->emoji_tail = base->emoji_tail;
1120 mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1123 mrg->emoji = add->emoji;
1124 mrg->emoji_tail = add->emoji_tail;
1125 mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1128 if (add->image == CHXJ_IMG_OFF)
1129 mrg->image = base->image;
1131 mrg->image = add->image;
1134 if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0)
1135 mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1137 mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1139 if (add->image_copyright)
1140 mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1142 mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1144 if (add->server_side_encoding) {
1145 mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1148 if (base->server_side_encoding) {
1149 mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1152 mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1155 mrg->convrules = apr_array_append(p, add->convrules, base->convrules);
1162 chxj_command_parse_take5(
1175 for (;*strp == ' '||*strp == '\t'; strp++) ;
1185 for (; *strp != '\0'; strp++) {
1186 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1187 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1192 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1193 || (isquoted && *strp == '"'))
1207 for (;*strp == ' '||*strp == '\t'; strp++) ;
1216 for (; *strp != '\0'; strp++) {
1217 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1218 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1223 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1224 || (isquoted && *strp == '"'))
1237 for (;*strp == ' '||*strp == '\t'; strp++);
1245 for (; *strp != '\0'; strp++) {
1246 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1247 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1252 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1253 || (isquoted && *strp == '"'))
1265 for (;*strp == ' '||*strp == '\t'; strp++);
1273 for (; *strp != '\0'; strp++) {
1274 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1275 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1280 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1281 || (isquoted && *strp == '"'))
1292 for (;*strp == ' '||*strp == '\t'; strp++);
1300 for (; *strp != '\0'; strp++) {
1301 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1302 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1307 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1308 || (isquoted && *strp == '"'))
1318 * The device definition file is loaded.
1320 * @param arg [i] The name of the device definition file is specified.
1321 * @param mconfig [i/o] The pointer to a set structure is specified.
1325 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg)
1327 mod_chxj_config* conf;
1332 if (strlen(arg) > 256)
1333 return "device data filename too long.";
1335 conf = (mod_chxj_config*)mconfig;
1336 conf->device_data_file = apr_pstrdup(parms->pool, arg);
1338 qs_init_malloc(&doc);
1339 qs_init_root_node(&doc);
1341 qs_parse_file((Doc*)&doc, (const char*)arg);
1342 chxj_load_device_data(&doc,parms->pool, conf);
1343 qs_all_free(&doc, QX_LOGMARK);
1350 * Device definition information is loaded.
1353 * @param arg [i] The name of the device definition file is specified.
1354 * @param mconfig [i/o] The pointer to a set structure is specified.
1358 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg)
1360 mod_chxj_config* conf;
1367 if (strlen(arg) > 256)
1368 return "emoji data filename too long.";
1370 conf = (mod_chxj_config*)mconfig;
1371 conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1372 qs_init_malloc(&doc);
1373 qs_init_root_node(&doc);
1375 qs_parse_file((Doc*)&doc, (const char*)arg);
1377 rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1379 qs_all_free(&doc, QX_LOGMARK);
1387 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg)
1389 mod_chxj_config* conf;
1394 if (strlen(arg) > 256)
1395 return "image uri is too long.";
1397 conf = (mod_chxj_config*)mconfig;
1398 if (strcasecmp("ON", arg) == 0)
1399 conf->image = CHXJ_IMG_ON;
1401 conf->image = CHXJ_IMG_OFF;
1408 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg)
1410 mod_chxj_config* conf;
1415 if (strlen(arg) > 256)
1416 return "cache dir name is too long.";
1418 conf = (mod_chxj_config*)mconfig;
1419 conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1426 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg)
1428 mod_chxj_config* conf;
1433 if (strlen(arg) > 256)
1434 return "Copyright Flag is too long.";
1436 conf = (mod_chxj_config*)mconfig;
1437 conf->image_copyright = apr_pstrdup(parms->pool, arg);
1444 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1448 mod_chxj_config* dconf;
1449 chxjconvrule_entry* newrule;
1459 dconf = (mod_chxj_config*)mconfig;
1461 if (strlen(arg) > 4096)
1462 return "ChxjConvertRule: is too long.";
1464 dconf = (mod_chxj_config*)mconfig;
1465 if (dconf->convrules == NULL)
1466 dconf->convrules = apr_array_make(cmd->pool,
1468 sizeof(chxjconvrule_entry));
1470 newrule = apr_array_push(dconf->convrules);
1473 newrule->action = 0;
1475 if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1476 return "ChxjConvertRule: bad argument line";
1478 newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1482 if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1488 if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1489 newrule->action |= CONVRULE_ENGINE_ON_BIT;
1492 if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1493 newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1499 if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1500 newrule->action |= CONVRULE_COOKIE_ON_BIT;
1510 newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1514 mode = AP_REG_EXTENDED;
1515 if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL)
1516 return "RewriteRule: cannot compile regular expression ";
1518 newrule->regexp = regexp;
1520 newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1522 newrule->encoding = apr_pstrdup(cmd->pool, "none");
1524 newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1526 if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1527 newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1529 newrule->user_agent = NULL;
1531 newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1537 static const command_rec cmds[] = {
1539 "ChxjLoadDeviceData",
1540 cmd_load_device_data,
1543 "Load Device Data"),
1545 "ChxjLoadEmojiData",
1546 cmd_load_emoji_data,
1552 cmd_set_image_engine,
1555 "Convert Target URI"),
1557 "ChxjImageCacheDir",
1558 cmd_set_image_cache_dir,
1561 "Image Cache Directory"),
1563 "ChxjImageCopyright",
1564 cmd_set_image_copyright,
1573 "an URL-applied regexp-pattern and a substitution URL"),
1578 /*----------------------------------------------------------------------------*/
1579 /* Dispatch list for API hooks */
1580 /*----------------------------------------------------------------------------*/
1581 module AP_MODULE_DECLARE_DATA chxj_module = {
1582 STANDARD20_MODULE_STUFF,
1583 chxj_create_per_dir_config, /* create per-dir config structures */
1584 chxj_merge_per_dir_config, /* merge per-dir config structures */
1585 chxj_config_server_create, /* create per-server config structures */
1586 NULL, /* merge per-server config structures */
1587 cmds, /* table of config file commands */
1588 chxj_register_hooks /* register hooks */