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(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)) {
570 if (APR_BUCKET_IS_EOS(b)) {
572 /*----------------------------------------------------------------------*/
574 /*----------------------------------------------------------------------*/
576 ctx = (mod_chxj_ctx*)f->ctx;
577 DBG1(r, "content_type=[%s]", r->content_type);
579 && *(char*)r->content_type == 't'
580 && strncmp(r->content_type, "text/html", 9) == 0) {
582 char* tmp = apr_palloc(r->pool, ctx->len + 1);
583 memset(tmp, 0, ctx->len + 1);
584 memcpy(tmp, ctx->buffer, ctx->len);
586 DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
588 ctx->buffer = chxj_exchange(r, (const char**)&tmp, (apr_size_t*)&ctx->len);
590 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
593 ctx->buffer = apr_psprintf(r->pool, "\n");
595 ctx->buffer = chxj_exchange(r,
596 (const char**)&ctx->buffer,
597 (apr_size_t*)&ctx->len);
603 && *(char*)r->content_type == 'i'
604 && strncmp(r->content_type, "image/", 6) == 0) {
606 char* tmp = apr_palloc(r->pool, ctx->len + 1);
607 memset(tmp, 0, ctx->len + 1);
608 memcpy(tmp, ctx->buffer, ctx->len);
610 DBG1(r, "input data=[%s]", tmp);
613 chxj_exchange_image(r, (const char**)&tmp,(apr_size_t*)&ctx->len);
614 if (ctx->buffer == NULL) {
618 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
622 contentLength = apr_psprintf(r->pool, "%d", ctx->len);
623 apr_table_setn(r->headers_out, "Content-Length", contentLength);
626 rv = pass_data_to_filter(f,
627 (const char*)ctx->buffer,
628 (apr_size_t)ctx->len);
635 DBG1(r, " SAVE COOKIE[%x]", entryp->action);
639 if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
640 DBG(r, "entryp->action == COOKIE_ON_BIT");
642 cookie_id = chxj_save_cookie(r);
645 * Location Header Check to add cookie parameter.
647 location_header = (char*)apr_table_get(r->headers_out, "Location");
648 if (location_header) {
649 DBG1(r, "Location Header=[%s]", location_header);
650 location_header = chxj_add_cookie_parameter(r,
653 apr_table_setn(r->headers_out, "Location", location_header);
654 DBG1(r, "Location Header=[%s]", location_header);
658 apr_table_setn(r->headers_out, "Content-Length", "0");
659 rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
665 if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
666 DBG2(r, "read data[%.*s]",len, data);
668 if (f->ctx == NULL) {
669 /*--------------------------------------------------------------------*/
671 /*--------------------------------------------------------------------*/
672 DBG(r, "new context");
673 ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
675 ctx->buffer = apr_palloc(r->pool, len);
676 memcpy(ctx->buffer, data, len);
679 ctx->buffer = apr_palloc(r->pool, 1);
686 /*--------------------------------------------------------------------*/
688 /*--------------------------------------------------------------------*/
690 DBG(r, "append data start");
691 ctx = (mod_chxj_ctx*)f->ctx;
694 tmp = apr_palloc(r->pool, ctx->len);
695 memcpy(tmp, ctx->buffer, ctx->len);
697 ctx->buffer = apr_palloc(r->pool, ctx->len + len);
699 memcpy(ctx->buffer, tmp, ctx->len);
700 memcpy(&ctx->buffer[ctx->len], data, len);
704 DBG(r, "append data end");
708 apr_brigade_destroy(bb);
710 DBG(r, "end of output filter");
716 * It is the main loop of the input filter.
718 * @param f [i/o] It is a filter.
719 * @param bb [i] brigade
720 * @param mode [i] mode
721 * @param block [i] block
722 * @param readbytes [i] readbyte
725 chxj_input_filter(ap_filter_t* f,
726 apr_bucket_brigade* bb,
727 ap_input_mode_t mode,
728 apr_read_type_e block,
731 request_rec* r = f->r;
733 conn_rec* c = r->connection;
735 /*--------------------------------------------------------------------------*/
736 /* It is the brigade area for output */
737 /*--------------------------------------------------------------------------*/
738 apr_bucket_brigade* ibb;
739 /*--------------------------------------------------------------------------*/
740 /* It is the brigade area for input */
741 /*--------------------------------------------------------------------------*/
742 apr_bucket_brigade* obb;
744 apr_bucket* tmp_heap;
752 mod_chxj_config* dconf;
753 chxjconvrule_entry* entryp;
755 DBG(r, "start of chxj_input_filter()");
757 data_brigade = qs_alloc_zero_byte_string(r);
760 ibb = apr_brigade_create(r->pool, c->bucket_alloc);
761 obb = apr_brigade_create(r->pool, c->bucket_alloc);
763 content_type = (char*)apr_table_get(r->headers_in, "Content-Type");
765 && strncasecmp("multipart/form-data", content_type, 19) == 0) {
767 DBG(r, "detect multipart/form-data");
768 ap_remove_input_filter(f);
770 return ap_get_brigade(f->next, bb, mode, block, readbytes);
773 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
775 entryp = chxj_apply_convrule(r, dconf->convrules);
776 if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
779 ap_remove_input_filter(f);
780 return ap_get_brigade(f->next, bb, mode, block, readbytes);
783 user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
784 spec = chxj_specified_device(r, user_agent);
786 switch(spec->html_spec_type) {
787 case CHXJ_SPEC_Chtml_1_0:
788 case CHXJ_SPEC_Chtml_2_0:
789 case CHXJ_SPEC_Chtml_3_0:
790 case CHXJ_SPEC_Chtml_4_0:
791 case CHXJ_SPEC_Chtml_5_0:
792 case CHXJ_SPEC_XHtml_Mobile_1_0:
794 case CHXJ_SPEC_Jhtml:
798 ap_remove_input_filter(f);
799 return ap_get_brigade(f->next, bb, mode, block, readbytes);
803 rv = ap_get_brigade(f->next, ibb, mode, block, readbytes);
804 if (rv != APR_SUCCESS) {
805 DBG(r, "ap_get_brigade() failed");
809 APR_BRIGADE_FOREACH(b, ibb) {
810 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
811 if (rv != APR_SUCCESS) {
812 DBG(r, "apr_bucket_read() failed");
817 data_bucket = apr_palloc(r->pool, len+1);
818 memset((void*)data_bucket, 0, len+1);
819 memcpy(data_bucket, data, len);
820 DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
822 data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
825 if (APR_BUCKET_IS_EOS(b)) {
829 apr_brigade_cleanup(ibb);
832 len = strlen(data_brigade);
834 DBG(r,"data_brigade length is 0");
835 DBG(r,"end of chxj_input_filter()");
836 ap_remove_input_filter(f);
837 return ap_get_brigade(f->next, bb, mode, block, readbytes);
839 data_brigade = chxj_input_convert(
841 (const char**)&data_brigade,
847 DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
849 obb = apr_brigade_create(r->pool, c->bucket_alloc);
851 tmp_heap = apr_bucket_heap_create(data_brigade,
855 eos = apr_bucket_eos_create(f->c->bucket_alloc);
857 APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
858 APR_BRIGADE_INSERT_TAIL(obb, eos);
859 APR_BRIGADE_CONCAT(bb, obb);
862 DBG(r, "end of chxj_input_filter()");
868 static mod_chxj_global_config*
869 chxj_global_config_create(apr_pool_t* pool, server_rec* s)
871 mod_chxj_global_config* conf;
873 SDBG(s, "start chxj_global_config_create()");
875 /*--------------------------------------------------------------------------*/
876 /* allocate an own subpool which survives server restarts */
877 /*--------------------------------------------------------------------------*/
878 conf = (mod_chxj_global_config*)apr_palloc(pool,
879 sizeof(mod_chxj_global_config));
881 conf->cookie_db_lock = NULL;
883 SDBG(s, "end chxj_global_config_create()");
889 * initialize chxj module
892 chxj_init_module(apr_pool_t *p,
897 mod_chxj_global_config* conf;
900 SDBG(s, "start chxj_init_module()");
902 apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
904 if (user_data == NULL) {
907 * dummy user_data set.
909 apr_pool_userdata_set(
912 apr_pool_cleanup_null,
914 SDBG(s, "end chxj_init_module()");
918 ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
922 conf = (mod_chxj_global_config *)ap_get_module_config(s->module_config,
925 if (apr_global_mutex_create(&(conf->cookie_db_lock),
926 NULL, APR_LOCK_DEFAULT, p) != APR_SUCCESS) {
927 SERR(s, "end chxj_init_module()");
928 return HTTP_INTERNAL_SERVER_ERROR;
931 #ifdef AP_NEED_SET_MUTEX_PERMS
932 if (unixd_set_global_mutex_perms(conf->cookie_db_lock) != APR_SUCCESS) {
933 SERR(s, "end chxj_init_module()");
934 return HTTP_INTERNAL_SERVER_ERROR;
940 SDBG(s, "end chxj_init_module()");
946 chxj_child_init(apr_pool_t *p, server_rec *s)
949 mod_chxj_global_config* conf;
952 SDBG(s, "start chxj_child_init()");
955 conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config,
958 if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p)
960 SERR(s, "Can't attach global mutex.");
965 SDBG(s, "end chxj_child_init()");
970 * A set structure of each server is generated.
976 chxj_config_server_create(apr_pool_t *p, server_rec *s)
978 mod_chxj_global_config *gc;
980 gc = chxj_global_config_create(p,s);
987 chxj_translate_name(request_rec *r)
989 return chxj_trans_name(r);
994 * The hook is registered.
999 chxj_register_hooks(apr_pool_t *p)
1001 ap_hook_post_config(chxj_init_module,
1004 APR_HOOK_REALLY_FIRST);
1005 ap_hook_child_init(chxj_child_init,
1008 APR_HOOK_REALLY_FIRST);
1009 ap_register_output_filter (
1010 "chxj_output_filter",
1014 ap_register_input_filter(
1015 "chxj_input_filter",
1019 ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1020 ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1021 ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1022 ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1027 * A set structure according to the directory is generated.
1033 chxj_create_per_dir_config(apr_pool_t *p, char *arg)
1035 mod_chxj_config* conf;
1037 conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1038 conf->device_data_file = NULL;
1039 conf->devices = NULL;
1040 conf->emoji_data_file = NULL;
1042 conf->emoji_tail = NULL;
1043 conf->image = CHXJ_IMG_OFF;
1044 conf->image_cache_dir = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1045 conf->server_side_encoding = NULL;
1050 conf->dir = apr_pcalloc(p, strlen(arg)+1);
1051 memset(conf->dir, 0, strlen(arg)+1);
1052 strcpy(conf->dir, arg);
1054 conf->convrules = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1056 /* Default is copyleft */
1057 conf->image_copyright = NULL;
1064 * Merge per-directory CHXJ configurations
1067 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1069 mod_chxj_config *base;
1070 mod_chxj_config *add;
1071 mod_chxj_config *mrg;
1073 base = (mod_chxj_config*)basev;
1074 add = (mod_chxj_config*)addv;
1075 mrg = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1077 mrg->device_data_file = NULL;
1078 mrg->devices = NULL;
1079 mrg->emoji_data_file = NULL;
1080 mrg->image = CHXJ_IMG_OFF;
1081 mrg->image_cache_dir = NULL;
1082 mrg->image_copyright = NULL;
1084 mrg->emoji_tail = NULL;
1086 mrg->dir = apr_pstrdup(p, add->dir);
1088 if (! add->device_data_file) {
1089 mrg->devices = base->devices;
1090 mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1093 mrg->devices = add->devices;
1094 mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1097 if (! add->emoji_data_file) {
1098 mrg->emoji = base->emoji;
1099 mrg->emoji_tail = base->emoji_tail;
1100 mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1103 mrg->emoji = add->emoji;
1104 mrg->emoji_tail = add->emoji_tail;
1105 mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1108 if (add->image == CHXJ_IMG_OFF)
1109 mrg->image = base->image;
1111 mrg->image = add->image;
1114 if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0)
1115 mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1117 mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1119 if (add->image_copyright)
1120 mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1122 mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1124 if (add->server_side_encoding) {
1125 mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1128 if (base->server_side_encoding) {
1129 mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1132 mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1135 mrg->convrules = apr_array_append(p, add->convrules, base->convrules);
1142 chxj_command_parse_take5(
1155 for (;*strp == ' '||*strp == '\t'; strp++) ;
1165 for (; *strp != '\0'; strp++) {
1166 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1167 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1172 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1173 || (isquoted && *strp == '"'))
1187 for (;*strp == ' '||*strp == '\t'; strp++) ;
1196 for (; *strp != '\0'; strp++) {
1197 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1198 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1203 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1204 || (isquoted && *strp == '"'))
1217 for (;*strp == ' '||*strp == '\t'; strp++);
1225 for (; *strp != '\0'; strp++) {
1226 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1227 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1232 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1233 || (isquoted && *strp == '"'))
1245 for (;*strp == ' '||*strp == '\t'; strp++);
1253 for (; *strp != '\0'; strp++) {
1254 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1255 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1260 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1261 || (isquoted && *strp == '"'))
1272 for (;*strp == ' '||*strp == '\t'; strp++);
1280 for (; *strp != '\0'; strp++) {
1281 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1282 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1287 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1288 || (isquoted && *strp == '"'))
1298 * The device definition file is loaded.
1300 * @param arg [i] The name of the device definition file is specified.
1301 * @param mconfig [i/o] The pointer to a set structure is specified.
1305 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg)
1307 mod_chxj_config* conf;
1312 if (strlen(arg) > 256)
1313 return "device data filename too long.";
1315 conf = (mod_chxj_config*)mconfig;
1316 conf->device_data_file = apr_pstrdup(parms->pool, arg);
1318 qs_init_malloc(&doc);
1319 qs_init_root_node(&doc);
1321 qs_parse_file((Doc*)&doc, (const char*)arg);
1322 chxj_load_device_data(&doc,parms->pool, conf);
1323 qs_all_free(&doc, QX_LOGMARK);
1329 * Device definition information is loaded.
1332 * @param arg [i] The name of the device definition file is specified.
1333 * @param mconfig [i/o] The pointer to a set structure is specified.
1337 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg)
1339 mod_chxj_config* conf;
1345 if (strlen(arg) > 256)
1346 return "emoji data filename too long.";
1348 conf = (mod_chxj_config*)mconfig;
1349 conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1350 qs_init_malloc(&doc);
1351 qs_init_root_node(&doc);
1353 qs_parse_file((Doc*)&doc, (const char*)arg);
1354 rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1355 qs_all_free(&doc, QX_LOGMARK);
1362 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg)
1364 mod_chxj_config* conf;
1370 if (strlen(arg) > 256)
1371 return "image uri is too long.";
1373 conf = (mod_chxj_config*)mconfig;
1374 if (strcasecmp("ON", arg) == 0)
1375 conf->image = CHXJ_IMG_ON;
1377 conf->image = CHXJ_IMG_OFF;
1384 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg)
1386 mod_chxj_config* conf;
1390 if (strlen(arg) > 256)
1391 return "cache dir name is too long.";
1393 conf = (mod_chxj_config*)mconfig;
1394 conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1400 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg)
1402 mod_chxj_config* conf;
1406 if (strlen(arg) > 256)
1407 return "Copyright Flag is too long.";
1409 conf = (mod_chxj_config*)mconfig;
1410 conf->image_copyright = apr_pstrdup(parms->pool, arg);
1416 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1418 mod_chxj_config* dconf;
1429 chxjconvrule_entry* newrule;
1431 dconf = (mod_chxj_config*)mconfig;
1433 if (strlen(arg) > 4096)
1434 return "ChxjConvertRule: is too long.";
1436 dconf = (mod_chxj_config*)mconfig;
1437 if (dconf->convrules == NULL)
1438 dconf->convrules = apr_array_make(cmd->pool, 2, sizeof(chxjconvrule_entry));
1441 newrule = apr_array_push(dconf->convrules);
1444 newrule->action = 0;
1446 if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1447 return "ChxjConvertRule: bad argument line";
1449 newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1453 if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1459 if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1460 newrule->action |= CONVRULE_ENGINE_ON_BIT;
1463 if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1464 newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1470 if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1471 newrule->action |= CONVRULE_COOKIE_ON_BIT;
1482 newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1486 mode = AP_REG_EXTENDED;
1487 if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL) {
1488 return "RewriteRule: cannot compile regular expression ";
1491 newrule->regexp = regexp;
1493 newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1495 newrule->encoding = apr_pstrdup(cmd->pool, "none");
1497 newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1499 if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1500 newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1502 newrule->user_agent = NULL;
1504 newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1510 static const command_rec cmds[] = {
1512 "ChxjLoadDeviceData",
1513 cmd_load_device_data,
1516 "Load Device Data"),
1518 "ChxjLoadEmojiData",
1519 cmd_load_emoji_data,
1525 cmd_set_image_engine,
1528 "Convert Target URI"),
1530 "ChxjImageCacheDir",
1531 cmd_set_image_cache_dir,
1534 "Image Cache Directory"),
1536 "ChxjImageCopyright",
1537 cmd_set_image_copyright,
1546 "an URL-applied regexp-pattern and a substitution URL"),
1551 /*----------------------------------------------------------------------------*/
1552 /* Dispatch list for API hooks */
1553 /*----------------------------------------------------------------------------*/
1554 module AP_MODULE_DECLARE_DATA chxj_module =
1556 STANDARD20_MODULE_STUFF,
1557 chxj_create_per_dir_config, /* create per-dir config structures */
1558 chxj_merge_per_dir_config, /* merge per-dir config structures */
1559 chxj_config_server_create, /* create per-server config structures */
1560 NULL, /* merge per-server config structures */
1561 cmds, /* table of config file commands */
1562 chxj_register_hooks /* register hooks */