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);
229 device_table* spec = chxj_specified_device(r, user_agent);
235 if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
236 switch(spec->html_spec_type) {
237 case CHXJ_SPEC_Chtml_1_0:
238 case CHXJ_SPEC_Chtml_2_0:
239 case CHXJ_SPEC_Chtml_3_0:
240 case CHXJ_SPEC_Chtml_4_0:
241 case CHXJ_SPEC_Chtml_5_0:
242 case CHXJ_SPEC_Jhtml:
243 cookie = chxj_save_cookie(r);
250 if (!r->header_only) {
253 if (convert_routine[spec->html_spec_type].encoder)
254 tmp = convert_routine[spec->html_spec_type].encoder(r,
258 if (convert_routine[spec->html_spec_type].converter) {
260 dst = convert_routine[spec->html_spec_type].converter(r,
268 dst = convert_routine[spec->html_spec_type].converter(r,
279 dst = apr_psprintf(r->pool, "\n");
284 DBG(r, "end chxj_exchange()");
291 * It converts it from HEADER.
296 chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
309 int no_update_flag = 0;
311 DBG(r, "start chxj_convert_input_header()");
314 DBG(r, "r->args=[null]");
315 DBG(r, "end chxj_convert_input_header()");
318 urilen = strlen(r->args);
320 result = qs_alloc_zero_byte_string(r);
322 buff_pre = apr_pstrdup(r->pool, r->args);
327 pair = apr_strtok(buff_pre, "&", &pstate);
333 pair_sv = apr_pstrdup(r->pool, pair);
335 name = apr_strtok(pair, "=", &vstate);
336 value = apr_strtok(NULL, "=", &vstate);
337 if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
338 DBG(r, "found cookie no update parameter");
343 buff = apr_pstrdup(r->pool, r->args);
344 DBG1(r, "r->args=[%s]", buff);
353 pair = apr_strtok(buff, "&", &pstate);
359 pair_sv = apr_pstrdup(r->pool, pair);
361 name = apr_strtok(pair, "=", &vstate);
362 value = apr_strtok(NULL, "=", &vstate);
363 if (strncasecmp(name, "_chxj", 5) != 0) {
364 if (strlen(result) != 0)
365 result = apr_pstrcat(r->pool, result, "&", NULL);
367 if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
371 dlen = strlen(value);
372 value = chxj_url_decode(r, value);
373 DBG1(r, "************ before encoding[%s]", value);
375 dvalue = chxj_rencoding(r, value, &dlen);
376 dvalue = chxj_url_encode(r, dvalue);
378 DBG1(r, "************ after encoding[%s]", dvalue);
380 result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
383 if (strcmp(name, pair_sv) != 0)
384 result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
386 result = apr_pstrcat(r->pool, result, name, NULL);
390 if (strncasecmp(name, "_chxj_c_", 8) == 0
391 || strncasecmp(name, "_chxj_r_", 8) == 0
392 || strncasecmp(name, "_chxj_s_", 8) == 0) {
396 if (strlen(value) == 0)
399 if (strlen(result) != 0)
400 result = apr_pstrcat(r->pool, result, "&", NULL);
402 result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
405 if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
406 DBG1(r, "found cookie parameter[%s]", value);
407 DBG(r, "call start chxj_load_cookie()");
408 cookie = chxj_load_cookie(r, value);
409 DBG(r, "call end chxj_load_cookie()");
410 if (! no_update_flag) {
411 chxj_update_cookie(r, cookie);
417 DBG1(r, "result r->args=[%s]", r->args);
418 DBG(r, "end chxj_convert_input_header()");
424 * It converts it from POSTDATA .
427 * @param src [i] It is POSTDATA character string.
428 * @param len [i/o] It is length of former HTML character string.
435 chxjconvrule_entry* entryp)
446 int no_update_flag = 0;
448 s = apr_pstrdup(r->pool, *src);
449 buff_pre = apr_pstrdup(r->pool, *src);
451 result = qs_alloc_zero_byte_string(r);
453 DBG1(r, "BEFORE input convert source = [%s]", s);
458 pair = apr_strtok(buff_pre, "&", &pstate);
464 pair_sv = apr_pstrdup(r->pool, pair);
466 name = apr_strtok(pair, "=", &vstate);
467 value = apr_strtok(NULL, "=", &vstate);
468 if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
469 DBG(r, "found cookie no update parameter");
479 pair = apr_strtok(s, "&", &pstate);
484 name = apr_strtok(pair, "=", &vstate);
485 value = apr_strtok(NULL, "=", &vstate);
486 if (strncasecmp(name, "_chxj", 5) != 0) {
487 if (strlen(result) != 0)
488 result = apr_pstrcat(r->pool, result, "&", NULL);
490 if (strcasecmp(entryp->encoding, "NONE") != 0
491 && value && strlen(value)) {
495 dlen = strlen(value);
496 value = chxj_url_decode(r, value);
497 DBG1(r, "************ before encoding[%s]", value);
499 dvalue = chxj_rencoding(r, value, &dlen);
500 dvalue = chxj_url_encode(r,dvalue);
502 DBG1(r, "************ after encoding[%s]", dvalue);
504 result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
508 result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
512 if (strncasecmp(name, "_chxj_c_", 8) == 0
513 || strncasecmp(name, "_chxj_r_", 8) == 0
514 || strncasecmp(name, "_chxj_s_", 8) == 0) {
518 if (strlen(value) == 0)
521 if (strlen(result) != 0)
522 result = apr_pstrcat(r->pool, result, "&", NULL);
524 if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
528 dlen = strlen(value);
529 value = chxj_url_decode(r, value);
530 DBG1(r, "************ before encoding[%s]", value);
532 dvalue = chxj_rencoding(r, value, &dlen);
533 dvalue = chxj_url_encode(r,dvalue);
535 DBG1(r, "************ after encoding[%s]", dvalue);
537 result = apr_pstrcat(r->pool, result, &name[8], "=", dvalue, NULL);
541 result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
545 if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
546 DBG1(r, "found cookie parameter[%s]", value);
547 DBG(r, "call start chxj_load_cookie()");
548 cookie = chxj_load_cookie(r, value);
549 DBG(r, "call end chxj_load_cookie()");
550 if (! no_update_flag) {
551 chxj_update_cookie(r, cookie);
555 *len = strlen(result);
557 DBG1(r, "AFTER input convert result = [%s]", result);
564 * The received data is returned to the filter.
566 * @param f [i/o] It is a filter.
567 * @param data [i] It is data returned to the filter.
568 * @param len [i] It is length of the data returned to the filter.
571 pass_data_to_filter(ap_filter_t *f, const char *data,
574 request_rec* r = f->r;
575 conn_rec* c = r->connection;
577 apr_bucket_brigade* bb;
580 DBG(r, "start pass_data_to_filter()");
582 bb = apr_brigade_create(r->pool, c->bucket_alloc);
583 b = apr_bucket_transient_create(data, len, c->bucket_alloc);
585 APR_BRIGADE_INSERT_TAIL(bb, b);
586 b = apr_bucket_eos_create(f->c->bucket_alloc);
587 APR_BRIGADE_INSERT_TAIL(bb, b);
589 rv = ap_pass_brigade(f->next, bb);
590 if (rv != APR_SUCCESS) {
591 DBG(r, "ap_pass_brigade()");
595 DBG(r, "end pass_data_to_filter()");
602 * It is the main loop of the output filter.
604 * @param f [i/o] It is a filter.
608 chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
618 char* location_header;
619 mod_chxj_config* dconf;
620 chxjconvrule_entry* entryp;
624 DBG(f->r, "start of chxj_output_filter()");
630 if ((f->r->proto_num >= 1001)
636 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
637 entryp = chxj_apply_convrule(r, dconf->convrules);
640 for (b = APR_BRIGADE_FIRST(bb);
641 b != APR_BRIGADE_SENTINEL(bb);
642 b = APR_BUCKET_NEXT(b)) {
644 if (APR_BUCKET_IS_EOS(b)) {
647 /*----------------------------------------------------------------------*/
649 /*----------------------------------------------------------------------*/
652 ctx = (mod_chxj_ctx*)f->ctx;
654 DBG1(r, "content_type=[%s]", r->content_type);
657 && *(char*)r->content_type == 't'
658 && strncmp(r->content_type, "text/html", 9) == 0) {
663 tmp = apr_palloc(r->pool, ctx->len + 1);
665 memset(tmp, 0, ctx->len + 1);
666 memcpy(tmp, ctx->buffer, ctx->len);
669 DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
672 ctx->buffer = chxj_exchange(r,
674 (apr_size_t*)&ctx->len);
677 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
681 ctx->buffer = apr_psprintf(r->pool, "\n");
683 ctx->buffer = chxj_exchange(r,
684 (const char**)&ctx->buffer,
685 (apr_size_t*)&ctx->len);
690 && *(char*)r->content_type == 't'
691 && strncmp(r->content_type, "text/xml", 8) == 0) {
700 memset(&doc, 0, sizeof(Doc));
701 memset(&qrcode, 0, sizeof(qr_code_t));
703 doc.parse_mode = PARSE_MODE_CHTML;
707 qs_init_malloc(&doc);
709 root = qs_parse_string(&doc, ctx->buffer, ctx->len);
712 for (child = qs_get_child_node(&doc,root);
714 child = qs_get_next_node(&doc,child)) {
718 name = qs_get_node_name(&doc,child);
720 if (strcasecmp("qrcode",name) == 0) {
725 qs_all_free(&doc,QX_LOGMARK);
727 r->handler = apr_psprintf(r->pool, "chxj-qrcode");
728 chxj_qrcode_node_to_qrcode(&qrcode, root);
729 sts = chxj_qrcode_create_image_data(&qrcode, &ctx->buffer, &ctx->len);
731 ERR(r, "qrcode create failed.");
734 r->content_type = apr_psprintf(r->pool, "image/jpg");
739 && *(char*)r->content_type == 'i'
740 && strncmp(r->content_type, "image/", 6) == 0) {
744 tmp = apr_palloc(r->pool, ctx->len + 1);
746 memset(tmp, 0, ctx->len + 1);
747 memcpy(tmp, ctx->buffer, ctx->len);
750 DBG1(r, "input data=[%s]", tmp);
755 chxj_exchange_image(r,
757 (apr_size_t*)&ctx->len);
759 if (ctx->buffer == NULL)
763 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
768 contentLength = apr_psprintf(r->pool, "%d", ctx->len);
769 apr_table_setn(r->headers_out, "Content-Length", contentLength);
772 rv = pass_data_to_filter(f,
773 (const char*)ctx->buffer,
774 (apr_size_t)ctx->len);
781 DBG1(r, " SAVE COOKIE[%x]", entryp->action);
786 if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
787 DBG(r, "entryp->action == COOKIE_ON_BIT");
788 char* user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
789 device_table* spec = chxj_specified_device(r, user_agent);
791 switch(spec->html_spec_type) {
792 case CHXJ_SPEC_Chtml_1_0:
793 case CHXJ_SPEC_Chtml_2_0:
794 case CHXJ_SPEC_Chtml_3_0:
795 case CHXJ_SPEC_Chtml_4_0:
796 case CHXJ_SPEC_Chtml_5_0:
797 case CHXJ_SPEC_Jhtml:
799 cookie = chxj_save_cookie(r);
802 * Location Header Check to add cookie parameter.
804 location_header = (char*)apr_table_get(r->headers_out, "Location");
805 if (location_header) {
806 DBG1(r, "Location Header=[%s]", location_header);
807 location_header = chxj_add_cookie_parameter(r,
810 apr_table_setn(r->headers_out, "Location", location_header);
811 DBG1(r, "Location Header=[%s]", location_header);
820 apr_table_setn(r->headers_out, "Content-Length", "0");
821 rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
827 if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
828 DBG2(r, "read data[%.*s]",len, data);
830 if (f->ctx == NULL) {
831 /*--------------------------------------------------------------------*/
833 /*--------------------------------------------------------------------*/
834 DBG(r, "new context");
835 ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
837 ctx->buffer = apr_palloc(r->pool, len);
838 memcpy(ctx->buffer, data, len);
841 ctx->buffer = apr_palloc(r->pool, 1);
848 /*--------------------------------------------------------------------*/
850 /*--------------------------------------------------------------------*/
852 DBG(r, "append data start");
853 ctx = (mod_chxj_ctx*)f->ctx;
856 tmp = apr_palloc(r->pool, ctx->len);
857 memcpy(tmp, ctx->buffer, ctx->len);
859 ctx->buffer = apr_palloc(r->pool, ctx->len + len);
861 memcpy(ctx->buffer, tmp, ctx->len);
862 memcpy(&ctx->buffer[ctx->len], data, len);
866 DBG(r, "append data end");
870 apr_brigade_destroy(bb);
872 DBG(r, "end of output filter");
879 * It is the main loop of the input filter.
881 * @param f [i/o] It is a filter.
882 * @param bb [i] brigade
883 * @param mode [i] mode
884 * @param block [i] block
885 * @param readbytes [i] readbyte
888 chxj_input_filter(ap_filter_t* f,
889 apr_bucket_brigade* bb,
890 ap_input_mode_t mode,
891 apr_read_type_e block,
898 /*--------------------------------------------------------------------------*/
899 /* It is the brigade area for output */
900 /*--------------------------------------------------------------------------*/
901 apr_bucket_brigade* ibb;
902 /*--------------------------------------------------------------------------*/
903 /* It is the brigade area for input */
904 /*--------------------------------------------------------------------------*/
905 apr_bucket_brigade* obb;
907 apr_bucket* tmp_heap;
915 mod_chxj_config* dconf;
916 chxjconvrule_entry* entryp;
921 DBG(r, "start of chxj_input_filter()");
923 data_brigade = qs_alloc_zero_byte_string(r);
926 ibb = apr_brigade_create(r->pool, c->bucket_alloc);
927 obb = apr_brigade_create(r->pool, c->bucket_alloc);
929 content_type = (char*)apr_table_get(r->headers_in, "Content-Type");
931 && strncasecmp("multipart/form-data", content_type, 19) == 0) {
933 DBG(r, "detect multipart/form-data");
934 ap_remove_input_filter(f);
936 return ap_get_brigade(f->next, bb, mode, block, readbytes);
939 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
941 entryp = chxj_apply_convrule(r, dconf->convrules);
942 if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
945 ap_remove_input_filter(f);
946 return ap_get_brigade(f->next, bb, mode, block, readbytes);
949 user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
950 spec = chxj_specified_device(r, user_agent);
952 switch(spec->html_spec_type) {
953 case CHXJ_SPEC_Chtml_1_0:
954 case CHXJ_SPEC_Chtml_2_0:
955 case CHXJ_SPEC_Chtml_3_0:
956 case CHXJ_SPEC_Chtml_4_0:
957 case CHXJ_SPEC_Chtml_5_0:
958 case CHXJ_SPEC_XHtml_Mobile_1_0:
960 case CHXJ_SPEC_Jhtml:
964 ap_remove_input_filter(f);
965 return ap_get_brigade(f->next, bb, mode, block, readbytes);
969 rv = ap_get_brigade(f->next, ibb, mode, block, readbytes);
970 if (rv != APR_SUCCESS) {
971 DBG(r, "ap_get_brigade() failed");
975 APR_BRIGADE_FOREACH(b, ibb) {
976 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
977 if (rv != APR_SUCCESS) {
978 DBG(r, "apr_bucket_read() failed");
983 data_bucket = apr_palloc(r->pool, len+1);
984 memset((void*)data_bucket, 0, len+1);
985 memcpy(data_bucket, data, len);
986 DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
988 data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
991 if (APR_BUCKET_IS_EOS(b)) {
995 apr_brigade_cleanup(ibb);
998 len = strlen(data_brigade);
1000 DBG(r,"data_brigade length is 0");
1001 DBG(r,"end of chxj_input_filter()");
1002 ap_remove_input_filter(f);
1003 return ap_get_brigade(f->next, bb, mode, block, readbytes);
1006 data_brigade = chxj_input_convert(
1008 (const char**)&data_brigade,
1014 DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
1016 obb = apr_brigade_create(r->pool, c->bucket_alloc);
1018 tmp_heap = apr_bucket_heap_create(data_brigade,
1021 f->c->bucket_alloc);
1022 eos = apr_bucket_eos_create(f->c->bucket_alloc);
1024 APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
1025 APR_BRIGADE_INSERT_TAIL(obb, eos);
1026 APR_BRIGADE_CONCAT(bb, obb);
1029 DBG(r, "end of chxj_input_filter()");
1035 static mod_chxj_global_config*
1036 chxj_global_config_create(apr_pool_t* pool, server_rec* s)
1038 mod_chxj_global_config* conf;
1040 SDBG(s, "start chxj_global_config_create()");
1042 /*--------------------------------------------------------------------------*/
1043 /* allocate an own subpool which survives server restarts */
1044 /*--------------------------------------------------------------------------*/
1045 conf = (mod_chxj_global_config*)apr_palloc(pool,
1046 sizeof(mod_chxj_global_config));
1048 conf->cookie_db_lock = NULL;
1050 SDBG(s, "end chxj_global_config_create()");
1057 * initialize chxj module
1060 chxj_init_module(apr_pool_t *p,
1066 mod_chxj_global_config* conf;
1070 SDBG(s, "start chxj_init_module()");
1072 apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
1074 if (user_data == NULL) {
1077 * dummy user_data set.
1079 apr_pool_userdata_set(
1081 CHXJ_MOD_CONFIG_KEY,
1082 apr_pool_cleanup_null,
1084 SDBG(s, "end chxj_init_module()");
1088 ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
1092 conf = (mod_chxj_global_config *)ap_get_module_config(s->module_config,
1095 if (apr_global_mutex_create(&(conf->cookie_db_lock),
1096 NULL, APR_LOCK_DEFAULT, p) != APR_SUCCESS) {
1097 SERR(s, "end chxj_init_module()");
1098 return HTTP_INTERNAL_SERVER_ERROR;
1101 #ifdef AP_NEED_SET_MUTEX_PERMS
1102 if (unixd_set_global_mutex_perms(conf->cookie_db_lock) != APR_SUCCESS) {
1103 SERR(s, "end chxj_init_module()");
1104 return HTTP_INTERNAL_SERVER_ERROR;
1109 SDBG(s, "end chxj_init_module()");
1116 chxj_child_init(apr_pool_t *p, server_rec *s)
1119 mod_chxj_global_config* conf;
1122 SDBG(s, "start chxj_child_init()");
1125 conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config,
1128 if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p)
1130 SERR(s, "Can't attach global mutex.");
1135 SDBG(s, "end chxj_child_init()");
1140 * A set structure of each server is generated.
1146 chxj_config_server_create(apr_pool_t *p, server_rec *s)
1148 mod_chxj_global_config *gc;
1150 gc = chxj_global_config_create(p,s);
1157 chxj_translate_name(request_rec *r)
1159 return chxj_trans_name(r);
1164 chxj_insert_filter(request_rec *r)
1168 mod_chxj_config* dconf;
1169 chxjconvrule_entry* entryp;
1170 DBG(r, "start chxj_insert_filter()");
1172 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
1174 user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1175 spec = chxj_specified_device(r, user_agent);
1178 entryp = chxj_apply_convrule(r, dconf->convrules);
1180 DBG(r, "end chxj_insert_filter()");
1184 if (entryp->action & CONVRULE_ENGINE_ON_BIT) {
1185 ap_add_input_filter("chxj_input_filter", NULL, r, r->connection);
1186 ap_add_output_filter("chxj_output_filter", NULL, r, r->connection);
1189 DBG(r, "end chxj_insert_filter()");
1194 * The hook is registered.
1199 chxj_register_hooks(apr_pool_t *p)
1201 ap_hook_post_config(chxj_init_module,
1204 APR_HOOK_REALLY_FIRST);
1205 ap_hook_child_init(chxj_child_init,
1208 APR_HOOK_REALLY_FIRST);
1209 ap_register_output_filter (
1210 "chxj_output_filter",
1214 ap_register_input_filter(
1215 "chxj_input_filter",
1219 ap_hook_insert_filter(chxj_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
1220 ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1221 ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1222 ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1223 ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1228 * A set structure according to the directory is generated.
1234 chxj_create_per_dir_config(apr_pool_t *p, char *arg)
1236 mod_chxj_config* conf;
1238 conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1239 conf->device_data_file = NULL;
1240 conf->devices = NULL;
1241 conf->emoji_data_file = NULL;
1243 conf->emoji_tail = NULL;
1244 conf->image = CHXJ_IMG_OFF;
1245 conf->image_cache_dir = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1246 conf->server_side_encoding = NULL;
1247 conf->cookie_db_dir = NULL;
1248 conf->cookie_timeout = 0;
1254 conf->dir = apr_pcalloc(p, strlen(arg)+1);
1255 memset(conf->dir, 0, strlen(arg)+1);
1256 strcpy(conf->dir, arg);
1258 conf->convrules = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1260 /* Default is copyleft */
1261 conf->image_copyright = NULL;
1268 * Merge per-directory CHXJ configurations
1271 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1273 mod_chxj_config *base;
1274 mod_chxj_config *add;
1275 mod_chxj_config *mrg;
1277 base = (mod_chxj_config*)basev;
1278 add = (mod_chxj_config*)addv;
1279 mrg = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1281 mrg->device_data_file = NULL;
1282 mrg->devices = NULL;
1283 mrg->emoji_data_file = NULL;
1284 mrg->image = CHXJ_IMG_OFF;
1285 mrg->image_cache_dir = NULL;
1286 mrg->image_copyright = NULL;
1288 mrg->emoji_tail = NULL;
1290 mrg->dir = apr_pstrdup(p, add->dir);
1292 if (! add->device_data_file) {
1293 mrg->devices = base->devices;
1294 mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1297 mrg->devices = add->devices;
1298 mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1301 if (! add->emoji_data_file) {
1302 mrg->emoji = base->emoji;
1303 mrg->emoji_tail = base->emoji_tail;
1304 mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1307 mrg->emoji = add->emoji;
1308 mrg->emoji_tail = add->emoji_tail;
1309 mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1312 if (add->image == CHXJ_IMG_OFF)
1313 mrg->image = base->image;
1315 mrg->image = add->image;
1318 if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0)
1319 mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1321 mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1323 if (add->image_copyright)
1324 mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1326 mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1328 if (add->server_side_encoding) {
1329 mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1332 if (base->server_side_encoding) {
1333 mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1336 mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1339 mrg->convrules = apr_array_append(p, add->convrules, base->convrules);
1341 if (add->cookie_db_dir) {
1342 mrg->cookie_db_dir = apr_pstrdup(p, add->cookie_db_dir);
1345 if (base->cookie_db_dir) {
1346 mrg->cookie_db_dir = apr_pstrdup(p, base->cookie_db_dir);
1349 mrg->cookie_db_dir = NULL;
1352 if (add->cookie_timeout) {
1353 mrg->cookie_timeout = add->cookie_timeout;
1356 if (base->cookie_db_dir) {
1357 mrg->cookie_timeout = base->cookie_timeout;
1360 mrg->cookie_timeout = 0;
1368 chxj_command_parse_take5(
1381 for (;*strp == ' '||*strp == '\t'; strp++) ;
1391 for (; *strp != '\0'; strp++) {
1392 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1393 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1398 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1399 || (isquoted && *strp == '"'))
1413 for (;*strp == ' '||*strp == '\t'; strp++) ;
1422 for (; *strp != '\0'; strp++) {
1423 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1424 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1429 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1430 || (isquoted && *strp == '"'))
1443 for (;*strp == ' '||*strp == '\t'; strp++);
1451 for (; *strp != '\0'; strp++) {
1452 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1453 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1458 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1459 || (isquoted && *strp == '"'))
1471 for (;*strp == ' '||*strp == '\t'; strp++);
1479 for (; *strp != '\0'; strp++) {
1480 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1481 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1486 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1487 || (isquoted && *strp == '"'))
1498 for (;*strp == ' '||*strp == '\t'; strp++);
1506 for (; *strp != '\0'; strp++) {
1507 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1508 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1513 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1514 || (isquoted && *strp == '"'))
1524 * The device definition file is loaded.
1526 * @param arg [i] The name of the device definition file is specified.
1527 * @param mconfig [i/o] The pointer to a set structure is specified.
1531 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg)
1533 mod_chxj_config* conf;
1538 if (strlen(arg) > 256)
1539 return "mod_chxj: device data filename too long.";
1541 conf = (mod_chxj_config*)mconfig;
1542 conf->device_data_file = apr_pstrdup(parms->pool, arg);
1544 qs_init_malloc(&doc);
1545 qs_init_root_node(&doc);
1547 qs_parse_file((Doc*)&doc, (const char*)arg);
1548 chxj_load_device_data(&doc,parms->pool, conf);
1549 qs_all_free(&doc, QX_LOGMARK);
1556 * Device definition information is loaded.
1559 * @param arg [i] The name of the device definition file is specified.
1560 * @param mconfig [i/o] The pointer to a set structure is specified.
1564 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg)
1566 mod_chxj_config* conf;
1573 if (strlen(arg) > 256)
1574 return "mod_chxj: emoji data filename too long.";
1576 conf = (mod_chxj_config*)mconfig;
1577 conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1578 qs_init_malloc(&doc);
1579 qs_init_root_node(&doc);
1581 qs_parse_file((Doc*)&doc, (const char*)arg);
1583 rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1585 qs_all_free(&doc, QX_LOGMARK);
1593 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg)
1595 mod_chxj_config* conf;
1600 if (strlen(arg) > 256)
1601 return "image uri is too long.";
1603 conf = (mod_chxj_config*)mconfig;
1604 if (strcasecmp("ON", arg) == 0)
1605 conf->image = CHXJ_IMG_ON;
1607 conf->image = CHXJ_IMG_OFF;
1614 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg)
1616 mod_chxj_config* conf;
1621 if (strlen(arg) > 256)
1622 return "cache dir name is too long.";
1624 conf = (mod_chxj_config*)mconfig;
1625 conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1632 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg)
1634 mod_chxj_config* conf;
1639 if (strlen(arg) > 256)
1640 return "Copyright Flag is too long.";
1642 conf = (mod_chxj_config*)mconfig;
1643 conf->image_copyright = apr_pstrdup(parms->pool, arg);
1650 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1654 mod_chxj_config* dconf;
1655 chxjconvrule_entry* newrule;
1665 dconf = (mod_chxj_config*)mconfig;
1667 if (strlen(arg) > 4096)
1668 return "mod_chxj: ChxjConvertRule: is too long.";
1670 dconf = (mod_chxj_config*)mconfig;
1671 if (dconf->convrules == NULL)
1672 dconf->convrules = apr_array_make(cmd->pool,
1674 sizeof(chxjconvrule_entry));
1676 newrule = apr_array_push(dconf->convrules);
1679 newrule->action = 0;
1681 if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1682 return "ChxjConvertRule: bad argument line";
1684 newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1688 if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1694 if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1695 newrule->action |= CONVRULE_ENGINE_ON_BIT;
1698 if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1699 newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1705 if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1706 newrule->action |= CONVRULE_COOKIE_ON_BIT;
1716 newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1720 mode = AP_REG_EXTENDED;
1721 if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL)
1722 return "RewriteRule: cannot compile regular expression ";
1724 newrule->regexp = regexp;
1726 newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1728 newrule->encoding = apr_pstrdup(cmd->pool, "none");
1730 newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1732 if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1733 newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1735 newrule->user_agent = NULL;
1737 newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1749 mod_chxj_config* dconf;
1752 if (strlen(arg) > 4096)
1753 return "mod_chxj: ChxjCookieDir is too long.";
1755 dconf = (mod_chxj_config*)mconfig;
1757 dconf->cookie_db_dir = apr_pstrdup(cmd->pool, arg);
1764 cmd_set_cookie_timeout(
1773 static const command_rec cmds[] = {
1775 "ChxjLoadDeviceData",
1776 cmd_load_device_data,
1779 "Load Device Data"),
1781 "ChxjLoadEmojiData",
1782 cmd_load_emoji_data,
1788 cmd_set_image_engine,
1791 "Convert Target URI"),
1793 "ChxjImageCacheDir",
1794 cmd_set_image_cache_dir,
1797 "Image Cache Directory"),
1799 "ChxjImageCopyright",
1800 cmd_set_image_copyright,
1809 "an URL-applied regexp-pattern and a substitution URL"),
1815 "save cookie.db directory."),
1817 "ChxjCookieTimeout",
1818 cmd_set_cookie_timeout,
1821 "The compulsion time-out time of the cookie is specified. "),
1826 /*----------------------------------------------------------------------------*/
1827 /* Dispatch list for API hooks */
1828 /*----------------------------------------------------------------------------*/
1829 module AP_MODULE_DECLARE_DATA chxj_module = {
1830 STANDARD20_MODULE_STUFF,
1831 chxj_create_per_dir_config, /* create per-dir config structures */
1832 chxj_merge_per_dir_config, /* merge per-dir config structures */
1833 chxj_config_server_create, /* create per-server config structures */
1834 NULL, /* merge per-server config structures */
1835 cmds, /* table of config file commands */
1836 chxj_register_hooks /* register hooks */