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)
537 request_rec* r = f->r;
538 apr_status_t rv = APR_SUCCESS;
545 char* location_header;
546 mod_chxj_config* dconf;
547 chxjconvrule_entry* entryp;
550 DBG(r, "start of chxj_output_filter()");
553 if ((f->r->proto_num >= 1001)
559 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
560 entryp = chxj_apply_convrule(r, dconf->convrules);
563 for (b = APR_BRIGADE_FIRST(bb);
564 b != APR_BRIGADE_SENTINEL(bb);
565 b = APR_BUCKET_NEXT(b)) {
566 if (APR_BUCKET_IS_EOS(b)) {
568 /*----------------------------------------------------------------------*/
570 /*----------------------------------------------------------------------*/
573 ctx = (mod_chxj_ctx*)f->ctx;
574 DBG1(r, "content_type=[%s]", r->content_type);
576 && *(char*)r->content_type == 't'
577 && strncmp(r->content_type, "text/html", 9) == 0) {
581 char* tmp = apr_palloc(r->pool, ctx->len + 1);
582 memset(tmp, 0, ctx->len + 1);
583 memcpy(tmp, ctx->buffer, ctx->len);
585 DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
587 ctx->buffer = chxj_exchange(r, (const char**)&tmp, (apr_size_t*)&ctx->len);
589 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);
604 && *(char*)r->content_type == 'i'
605 && strncmp(r->content_type, "image/", 6) == 0) {
609 char* tmp = apr_palloc(r->pool, ctx->len + 1);
610 memset(tmp, 0, ctx->len + 1);
611 memcpy(tmp, ctx->buffer, ctx->len);
613 DBG1(r, "input data=[%s]", tmp);
616 chxj_exchange_image(r, (const char**)&tmp,(apr_size_t*)&ctx->len);
617 if (ctx->buffer == NULL) {
621 DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
626 contentLength = apr_psprintf(r->pool, "%d", ctx->len);
627 apr_table_setn(r->headers_out, "Content-Length", contentLength);
631 rv = pass_data_to_filter(f, (const char*)ctx->buffer, (apr_size_t)ctx->len);
638 DBG1(r, " SAVE COOKIE[%x]", entryp->action);
642 if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
643 DBG(r, "entryp->action == COOKIE_ON_BIT");
645 cookie_id = chxj_save_cookie(r);
648 * Location Header Check to add cookie parameter.
650 location_header = (char*)apr_table_get(r->headers_out, "Location");
651 if (location_header) {
652 DBG1(r, "Location Header=[%s]", location_header);
653 location_header = chxj_add_cookie_parameter(r,
656 apr_table_setn(r->headers_out, "Location", location_header);
657 DBG1(r, "Location Header=[%s]", location_header);
661 apr_table_setn(r->headers_out, "Content-Length", "0");
662 rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
668 if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
669 DBG2(r, "read data[%.*s]",len, data);
671 if (f->ctx == NULL) {
672 /*--------------------------------------------------------------------*/
674 /*--------------------------------------------------------------------*/
675 DBG(r, "new context");
676 ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
678 ctx->buffer = apr_palloc(r->pool, len);
679 memcpy(ctx->buffer, data, len);
682 ctx->buffer = apr_palloc(r->pool, 1);
689 /*--------------------------------------------------------------------*/
691 /*--------------------------------------------------------------------*/
693 DBG(r, "append data start");
694 ctx = (mod_chxj_ctx*)f->ctx;
697 tmp = apr_palloc(r->pool, ctx->len);
698 memcpy(tmp, ctx->buffer, ctx->len);
699 ctx->buffer = apr_palloc(r->pool, ctx->len + len);
700 memcpy(ctx->buffer, tmp, ctx->len);
701 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");
764 if (content_type && strncasecmp("multipart/form-data", content_type, 19) == 0) {
765 DBG(r, "detect multipart/form-data");
766 ap_remove_input_filter(f);
767 return ap_get_brigade(f->next, bb, mode, block, readbytes);
770 dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
772 entryp = chxj_apply_convrule(r, dconf->convrules);
773 if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
775 ap_remove_input_filter(f);
776 return ap_get_brigade(f->next, bb, mode, block, readbytes);
779 user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
780 spec = chxj_specified_device(r, user_agent);
782 switch(spec->html_spec_type) {
783 case CHXJ_SPEC_Chtml_1_0:
784 case CHXJ_SPEC_Chtml_2_0:
785 case CHXJ_SPEC_Chtml_3_0:
786 case CHXJ_SPEC_Chtml_4_0:
787 case CHXJ_SPEC_Chtml_5_0:
788 case CHXJ_SPEC_XHtml_Mobile_1_0:
790 case CHXJ_SPEC_Jhtml:
794 ap_remove_input_filter(f);
795 return ap_get_brigade(f->next, bb, mode, block, readbytes);
799 rv = ap_get_brigade(f->next, ibb, mode, block, readbytes);
800 if (rv != APR_SUCCESS) {
801 DBG(r, "ap_get_brigade() failed");
805 APR_BRIGADE_FOREACH(b, ibb) {
806 rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
807 if (rv != APR_SUCCESS) {
808 DBG(r, "apr_bucket_read() failed");
813 data_bucket = apr_palloc(r->pool, len+1);
814 memset((void*)data_bucket, 0, len+1);
815 memcpy(data_bucket, data, len);
816 DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
818 data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
821 if (APR_BUCKET_IS_EOS(b)) {
825 apr_brigade_cleanup(ibb);
828 len = strlen(data_brigade);
830 DBG(r,"data_brigade length is 0");
831 DBG(r,"end of chxj_input_filter()");
832 ap_remove_input_filter(f);
833 return ap_get_brigade(f->next, bb, mode, block, readbytes);
835 data_brigade = chxj_input_convert(
837 (const char**)&data_brigade,
843 DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
845 obb = apr_brigade_create(r->pool, c->bucket_alloc);
846 tmp_heap = apr_bucket_heap_create(data_brigade, len, NULL, f->c->bucket_alloc);
847 eos = apr_bucket_eos_create(f->c->bucket_alloc);
848 APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
849 APR_BRIGADE_INSERT_TAIL(obb, eos);
850 APR_BRIGADE_CONCAT(bb, obb);
852 DBG(r, "end of chxj_input_filter()");
858 static mod_chxj_global_config*
859 chxj_global_config_create(apr_pool_t* pool, server_rec* s)
861 mod_chxj_global_config* conf;
863 SDBG(s, "start chxj_global_config_create()");
865 /*--------------------------------------------------------------------------*/
866 /* allocate an own subpool which survives server restarts */
867 /*--------------------------------------------------------------------------*/
868 conf = (mod_chxj_global_config*)apr_palloc(pool,
869 sizeof(mod_chxj_global_config));
871 conf->cookie_db_lock = NULL;
873 SDBG(s, "end chxj_global_config_create()");
879 * initialize chxj module
882 chxj_init_module(apr_pool_t *p,
887 mod_chxj_global_config* conf;
890 SDBG(s, "start chxj_init_module()");
892 apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
894 if (user_data == NULL) {
897 * dummy user_data set.
899 apr_pool_userdata_set(
902 apr_pool_cleanup_null,
904 SDBG(s, "end chxj_init_module()");
908 ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
910 conf = (mod_chxj_global_config *)ap_get_module_config(s->module_config, &chxj_module);
913 if (apr_global_mutex_create(&(conf->cookie_db_lock), NULL, APR_LOCK_DEFAULT, p) != APR_SUCCESS) {
914 SERR(s, "end chxj_init_module()");
915 return HTTP_INTERNAL_SERVER_ERROR;
917 #ifdef AP_NEED_SET_MUTEX_PERMS
918 if (unixd_set_global_mutex_perms(conf->cookie_db_lock) != APR_SUCCESS) {
919 SERR(s, "end chxj_init_module()");
920 return HTTP_INTERNAL_SERVER_ERROR;
927 SDBG(s, "end chxj_init_module()");
932 chxj_child_init(apr_pool_t *p, server_rec *s)
935 mod_chxj_global_config* conf;
938 SDBG(s, "start chxj_child_init()");
940 conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config, &chxj_module);
942 if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p)
944 SERR(s, "Can't attach global mutex.");
949 SDBG(s, "end chxj_child_init()");
954 * A set structure of each server is generated.
960 chxj_config_server_create(apr_pool_t *p, server_rec *s)
962 mod_chxj_global_config *gc;
964 gc = chxj_global_config_create(p,s);
971 chxj_translate_name(request_rec *r)
973 return chxj_trans_name(r);
977 * The hook is registered.
982 chxj_register_hooks(apr_pool_t *p)
984 ap_hook_post_config(chxj_init_module,
987 APR_HOOK_REALLY_FIRST);
988 ap_hook_child_init(chxj_child_init,
991 APR_HOOK_REALLY_FIRST);
992 ap_register_output_filter (
993 "chxj_output_filter",
997 ap_register_input_filter(
1002 ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1003 ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1004 ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1005 ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_LAST);
1009 * A set structure according to the directory is generated.
1015 chxj_create_per_dir_config(apr_pool_t *p, char *arg)
1017 mod_chxj_config* conf;
1019 conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1020 conf->device_data_file = NULL;
1021 conf->devices = NULL;
1022 conf->emoji_data_file = NULL;
1024 conf->emoji_tail = NULL;
1025 conf->image = CHXJ_IMG_OFF;
1026 conf->image_cache_dir = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1027 conf->server_side_encoding = NULL;
1032 conf->dir = apr_pcalloc(p, strlen(arg)+1);
1033 memset(conf->dir, 0, strlen(arg)+1);
1034 strcpy(conf->dir, arg);
1036 conf->convrules = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1038 /* Default is copyleft */
1039 conf->image_copyright = NULL;
1044 * Merge per-directory CHXJ configurations
1047 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1049 mod_chxj_config *base = (mod_chxj_config*)basev;
1050 mod_chxj_config *add = (mod_chxj_config*)addv;
1051 mod_chxj_config *mrg = (mod_chxj_config*)apr_palloc(p, sizeof(mod_chxj_config));
1053 mrg->device_data_file = NULL;
1054 mrg->devices = NULL;
1055 mrg->emoji_data_file = NULL;
1056 mrg->image = CHXJ_IMG_OFF;
1057 mrg->image_cache_dir = NULL;
1058 mrg->image_copyright = NULL;
1060 mrg->emoji_tail = NULL;
1062 mrg->dir = apr_pstrdup(p, add->dir);
1064 if (! add->device_data_file) {
1065 mrg->devices = base->devices;
1066 mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1069 mrg->devices = add->devices;
1070 mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1073 if (! add->emoji_data_file) {
1074 mrg->emoji = base->emoji;
1075 mrg->emoji_tail = base->emoji_tail;
1076 mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1079 mrg->emoji = add->emoji;
1080 mrg->emoji_tail = add->emoji_tail;
1081 mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1084 if (add->image == CHXJ_IMG_OFF)
1085 mrg->image = base->image;
1087 mrg->image = add->image;
1090 if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0)
1091 mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1093 mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1095 if (add->image_copyright)
1096 mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1098 mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1100 if (add->server_side_encoding) {
1101 mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1104 if (base->server_side_encoding) {
1105 mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1108 mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1111 mrg->convrules = apr_array_append(p, add->convrules, base->convrules);
1118 chxj_command_parse_take5(
1131 for (;*strp == ' '||*strp == '\t'; strp++) ;
1141 for (; *strp != '\0'; strp++) {
1142 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1143 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1148 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1149 || (isquoted && *strp == '"'))
1163 for (;*strp == ' '||*strp == '\t'; strp++) ;
1172 for (; *strp != '\0'; strp++) {
1173 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1174 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1179 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1180 || (isquoted && *strp == '"'))
1193 for (;*strp == ' '||*strp == '\t'; strp++);
1201 for (; *strp != '\0'; strp++) {
1202 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1203 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1208 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1209 || (isquoted && *strp == '"'))
1221 for (;*strp == ' '||*strp == '\t'; strp++);
1229 for (; *strp != '\0'; strp++) {
1230 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1231 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1236 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1237 || (isquoted && *strp == '"'))
1248 for (;*strp == ' '||*strp == '\t'; strp++);
1256 for (; *strp != '\0'; strp++) {
1257 if ((isquoted && (*strp == ' ' || *strp == '\t'))
1258 || (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1263 if ((!isquoted && (*strp == ' ' || *strp == '\t'))
1264 || (isquoted && *strp == '"'))
1273 * The device definition file is loaded.
1275 * @param arg [i] The name of the device definition file is specified.
1276 * @param mconfig [i/o] The pointer to a set structure is specified.
1280 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char* arg)
1282 mod_chxj_config* conf;
1286 if (strlen(arg) > 256)
1287 return "device data filename too long.";
1289 conf = (mod_chxj_config*)mconfig;
1290 conf->device_data_file = apr_pstrdup(parms->pool, arg);
1292 qs_init_malloc(&doc);
1293 qs_init_root_node(&doc);
1295 qs_parse_file((Doc*)&doc, (const char*)arg);
1296 chxj_load_device_data(&doc,parms->pool, conf);
1297 qs_all_free(&doc, QX_LOGMARK);
1303 * Device definition information is loaded.
1306 * @param arg [i] The name of the device definition file is specified.
1307 * @param mconfig [i/o] The pointer to a set structure is specified.
1311 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg)
1313 mod_chxj_config* conf;
1319 if (strlen(arg) > 256)
1320 return "emoji data filename too long.";
1322 conf = (mod_chxj_config*)mconfig;
1323 conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
1324 qs_init_malloc(&doc);
1325 qs_init_root_node(&doc);
1327 qs_parse_file((Doc*)&doc, (const char*)arg);
1328 rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
1329 qs_all_free(&doc, QX_LOGMARK);
1336 cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg)
1338 mod_chxj_config* conf;
1344 if (strlen(arg) > 256)
1345 return "image uri is too long.";
1347 conf = (mod_chxj_config*)mconfig;
1348 if (strcasecmp("ON", arg) == 0)
1349 conf->image = CHXJ_IMG_ON;
1351 conf->image = CHXJ_IMG_OFF;
1358 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg)
1360 mod_chxj_config* conf;
1364 if (strlen(arg) > 256)
1365 return "cache dir name is too long.";
1367 conf = (mod_chxj_config*)mconfig;
1368 conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
1374 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg)
1376 mod_chxj_config* conf;
1380 if (strlen(arg) > 256)
1381 return "Copyright Flag is too long.";
1383 conf = (mod_chxj_config*)mconfig;
1384 conf->image_copyright = apr_pstrdup(parms->pool, arg);
1390 cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
1392 mod_chxj_config* dconf;
1403 chxjconvrule_entry* newrule;
1405 dconf = (mod_chxj_config*)mconfig;
1407 if (strlen(arg) > 4096)
1408 return "ChxjConvertRule: is too long.";
1410 dconf = (mod_chxj_config*)mconfig;
1411 if (dconf->convrules == NULL)
1412 dconf->convrules = apr_array_make(cmd->pool, 2, sizeof(chxjconvrule_entry));
1415 newrule = apr_array_push(dconf->convrules);
1418 newrule->action = 0;
1420 if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
1421 return "ChxjConvertRule: bad argument line";
1423 newrule->pattern = apr_pstrdup(cmd->pool, prm1);
1427 if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
1433 if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
1434 newrule->action |= CONVRULE_ENGINE_ON_BIT;
1437 if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
1438 newrule->action |= CONVRULE_ENGINE_OFF_BIT;
1444 if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
1445 newrule->action |= CONVRULE_COOKIE_ON_BIT;
1456 newrule->flags |= CONVRULE_FLAG_NOTMATCH;
1460 mode = AP_REG_EXTENDED;
1461 if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL) {
1462 return "RewriteRule: cannot compile regular expression ";
1465 newrule->regexp = regexp;
1467 newrule->encoding = apr_pstrdup(cmd->pool, prm3);
1469 newrule->encoding = apr_pstrdup(cmd->pool, "none");
1471 newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
1473 if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
1474 newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
1476 newrule->user_agent = NULL;
1478 newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
1484 static const command_rec cmds[] = {
1486 "ChxjLoadDeviceData",
1487 cmd_load_device_data,
1490 "Load Device Data"),
1492 "ChxjLoadEmojiData",
1493 cmd_load_emoji_data,
1499 cmd_set_image_engine,
1502 "Convert Target URI"),
1504 "ChxjImageCacheDir",
1505 cmd_set_image_cache_dir,
1508 "Image Cache Directory"),
1510 "ChxjImageCopyright",
1511 cmd_set_image_copyright,
1520 "an URL-applied regexp-pattern and a substitution URL"),
1525 /*----------------------------------------------------------------------------*/
1526 /* Dispatch list for API hooks */
1527 /*----------------------------------------------------------------------------*/
1528 module AP_MODULE_DECLARE_DATA chxj_module =
1530 STANDARD20_MODULE_STUFF,
1531 chxj_create_per_dir_config, /* create per-dir config structures */
1532 chxj_merge_per_dir_config, /* merge per-dir config structures */
1533 chxj_config_server_create, /* create per-server config structures */
1534 NULL, /* merge per-server config structures */
1535 cmds, /* table of config file commands */
1536 chxj_register_hooks /* register hooks */