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()");
945 chxj_child_init(apr_pool_t *p, server_rec *s)
948 mod_chxj_global_config* conf;
951 SDBG(s, "start chxj_child_init()");
953 conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config, &chxj_module);
955 if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p)
957 SERR(s, "Can't attach global mutex.");
962 SDBG(s, "end chxj_child_init()");
967 * A set structure of each server is generated.
973 chxj_config_server_create(apr_pool_t *p, server_rec *s)
975 mod_chxj_global_config *gc;
977 gc = chxj_global_config_create(p,s);
984 chxj_translate_name(request_rec *r)
986 return chxj_trans_name(r);
990 * The hook is registered.
995 chxj_register_hooks(apr_pool_t *p)
997 ap_hook_post_config(chxj_init_module,
1000 APR_HOOK_REALLY_FIRST);
1001 ap_hook_child_init(chxj_child_init,
1004 APR_HOOK_REALLY_FIRST);
1005 ap_register_output_filter (
1006 "chxj_output_filter",
1010 ap_register_input_filter(
1011 "chxj_input_filter",
1015 ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1016 ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1017 ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1018 ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1022 * A set structure according to the directory is generated.
1028 chxj_create_per_dir_config(apr_pool_t *p, char *arg)
1030 mod_chxj_config* conf;
1032 conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1033 conf->device_data_file = NULL;
1034 conf->devices = NULL;
1035 conf->emoji_data_file = NULL;
1037 conf->emoji_tail = NULL;
1038 conf->image = CHXJ_IMG_OFF;
1039 conf->image_cache_dir = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1040 conf->server_side_encoding = NULL;
1045 conf->dir = apr_pcalloc(p, strlen(arg)+1);
1046 memset(conf->dir, 0, strlen(arg)+1);
1047 strcpy(conf->dir, arg);
1049 conf->convrules = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1051 /* Default is copyleft */
1052 conf->image_copyright = NULL;
1057 * Merge per-directory CHXJ configurations
1060 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1062 mod_chxj_config *base = (mod_chxj_config*)basev;
1063 mod_chxj_config *add = (mod_chxj_config*)addv;
1064 mod_chxj_config *mrg = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1066 mrg->device_data_file = NULL;
1067 mrg->devices = NULL;
1068 mrg->emoji_data_file = NULL;
1069 mrg->image = CHXJ_IMG_OFF;
1070 mrg->image_cache_dir = NULL;
1071 mrg->image_copyright = NULL;
1073 mrg->emoji_tail = NULL;
1075 mrg->dir = apr_pstrdup(p, add->dir);
1077 if (! add->device_data_file) {
1078 mrg->devices = base->devices;
1079 mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1082 mrg->devices = add->devices;
1083 mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1086 if (! add->emoji_data_file) {
1087 mrg->emoji = base->emoji;
1088 mrg->emoji_tail = base->emoji_tail;
1089 mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1092 mrg->emoji = add->emoji;
1093 mrg->emoji_tail = add->emoji_tail;
1094 mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1097 if (add->image == CHXJ_IMG_OFF)
1098 mrg->image = base->image;
1100 mrg->image = add->image;
1103 if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0)
1104 mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1106 mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1108 if (add->image_copyright)
1109 mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1111 mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1113 if (add->server_side_encoding) {
1114 mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1117 if (base->server_side_encoding) {
1118 mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1121 mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1124 mrg->convrules = apr_array_append(p, add->convrules, base->convrules);
1131 chxj_command_parse_take5(
1144 for (;*strp == ' '||*strp == '\t'; strp++) ;
1154 for (; *strp != '\0'; strp++) {
1155 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1156 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1161 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1162 || (isquoted && *strp == '"'))
1176 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 == '"'))
1206 for (;*strp == ' '||*strp == '\t'; strp++);
1214 for (; *strp != '\0'; strp++) {
1215 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1216 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1221 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1222 || (isquoted && *strp == '"'))
1234 for (;*strp == ' '||*strp == '\t'; strp++);
1242 for (; *strp != '\0'; strp++) {
1243 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1244 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1249 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1250 || (isquoted && *strp == '"'))
1261 for (;*strp == ' '||*strp == '\t'; strp++);
1269 for (; *strp != '\0'; strp++) {
1270 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1271 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1276 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1277 || (isquoted && *strp == '"'))
1286 * The device definition file is loaded.
1288 * @param arg [i] The name of the device definition file is specified.
1289 * @param mconfig [i/o] The pointer to a set structure is specified.
1293 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg)
1295 mod_chxj_config* conf;
1299 if (strlen(arg) > 256)
1300 return "device data filename too long.";
1302 conf = (mod_chxj_config*)mconfig;
1303 conf->device_data_file = apr_pstrdup(parms->pool, arg);
1305 qs_init_malloc(&doc);
1306 qs_init_root_node(&doc);
1308 qs_parse_file((Doc*)&doc, (const char*)arg);
1309 chxj_load_device_data(&doc,parms->pool, conf);
1310 qs_all_free(&doc, QX_LOGMARK);
1316 * Device definition information is loaded.
1319 * @param arg [i] The name of the device definition file is specified.
1320 * @param mconfig [i/o] The pointer to a set structure is specified.
1324 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg)
1326 mod_chxj_config* conf;
1332 if (strlen(arg) > 256)
1333 return "emoji data filename too long.";
1335 conf = (mod_chxj_config*)mconfig;
1336 conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1337 qs_init_malloc(&doc);
1338 qs_init_root_node(&doc);
1340 qs_parse_file((Doc*)&doc, (const char*)arg);
1341 rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1342 qs_all_free(&doc, QX_LOGMARK);
1349 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg)
1351 mod_chxj_config* conf;
1357 if (strlen(arg) > 256)
1358 return "image uri is too long.";
1360 conf = (mod_chxj_config*)mconfig;
1361 if (strcasecmp("ON", arg) == 0)
1362 conf->image = CHXJ_IMG_ON;
1364 conf->image = CHXJ_IMG_OFF;
1371 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg)
1373 mod_chxj_config* conf;
1377 if (strlen(arg) > 256)
1378 return "cache dir name is too long.";
1380 conf = (mod_chxj_config*)mconfig;
1381 conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1387 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg)
1389 mod_chxj_config* conf;
1393 if (strlen(arg) > 256)
1394 return "Copyright Flag is too long.";
1396 conf = (mod_chxj_config*)mconfig;
1397 conf->image_copyright = apr_pstrdup(parms->pool, arg);
1403 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1405 mod_chxj_config* dconf;
1416 chxjconvrule_entry* newrule;
1418 dconf = (mod_chxj_config*)mconfig;
1420 if (strlen(arg) > 4096)
1421 return "ChxjConvertRule: is too long.";
1423 dconf = (mod_chxj_config*)mconfig;
1424 if (dconf->convrules == NULL)
1425 dconf->convrules = apr_array_make(cmd->pool, 2, sizeof(chxjconvrule_entry));
1428 newrule = apr_array_push(dconf->convrules);
1431 newrule->action = 0;
1433 if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1434 return "ChxjConvertRule: bad argument line";
1436 newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1440 if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1446 if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1447 newrule->action |= CONVRULE_ENGINE_ON_BIT;
1450 if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1451 newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1457 if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1458 newrule->action |= CONVRULE_COOKIE_ON_BIT;
1469 newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1473 mode = AP_REG_EXTENDED;
1474 if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL) {
1475 return "RewriteRule: cannot compile regular expression ";
1478 newrule->regexp = regexp;
1480 newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1482 newrule->encoding = apr_pstrdup(cmd->pool, "none");
1484 newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1486 if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1487 newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1489 newrule->user_agent = NULL;
1491 newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1497 static const command_rec cmds[] = {
1499 "ChxjLoadDeviceData",
1500 cmd_load_device_data,
1503 "Load Device Data"),
1505 "ChxjLoadEmojiData",
1506 cmd_load_emoji_data,
1512 cmd_set_image_engine,
1515 "Convert Target URI"),
1517 "ChxjImageCacheDir",
1518 cmd_set_image_cache_dir,
1521 "Image Cache Directory"),
1523 "ChxjImageCopyright",
1524 cmd_set_image_copyright,
1533 "an URL-applied regexp-pattern and a substitution URL"),
1538 /*----------------------------------------------------------------------------*/
1539 /* Dispatch list for API hooks */
1540 /*----------------------------------------------------------------------------*/
1541 module AP_MODULE_DECLARE_DATA chxj_module =
1543 STANDARD20_MODULE_STUFF,
1544 chxj_create_per_dir_config, /* create per-dir config structures */
1545 chxj_merge_per_dir_config, /* merge per-dir config structures */
1546 chxj_config_server_create, /* create per-server config structures */
1547 NULL, /* merge per-server config structures */
1548 cmds, /* table of config file commands */
1549 chxj_register_hooks /* register hooks */