OSDN Git Service

add config for image rewrite funcs
[modchxj/mod_chxj.git] / src / mod_chxj.c
1 /*
2  * Copyright (C) 2005-2009 Atsushi Konno All rights reserved.
3  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  */
17 #include <unistd.h>
18 #include <string.h>
19 #include <limits.h>
20 #include <errno.h>
21
22 #include "httpd.h"
23 #include "http_config.h"
24 #include "http_core.h"
25 #include "http_protocol.h"
26 #include "http_log.h"
27 #include "ap_config.h"
28 #include "apr_strings.h"
29 #include "util_filter.h"
30 #include "apr_buckets.h"
31 #include "apr_lib.h"
32 #include "apr_tables.h"
33 #include "apr_dso.h"
34 #include "apr_general.h"
35 #include "apr_pools.h"
36 #include "apr_file_info.h"
37
38 #include "mod_chxj.h"
39 #include "chxj_encoding.h"
40 #include "qs_ignore_sp.h"
41 #include "qs_log.h"
42 #include "qs_malloc.h"
43 #include "qs_parse_attr.h"
44 #include "qs_parse_file.h"
45 #include "qs_parse_string.h"
46 #include "qs_parse_tag.h"
47 #include "chxj_load_device_data.h"
48 #include "chxj_load_emoji_data.h"
49 #include "chxj_specified_device.h"
50 #include "chxj_tag_util.h"
51 #include "chxj_xhtml_mobile_1_0.h"
52 #include "chxj_hdml.h"
53 #include "chxj_chtml10.h"
54 #include "chxj_chtml20.h"
55 #include "chxj_chtml30.h"
56 #include "chxj_chtml40.h"
57 #include "chxj_chtml50.h"
58 #include "chxj_ixhtml10.h"
59 #include "chxj_jhtml.h"
60 #include "chxj_jxhtml.h"
61 #include "chxj_img_conv_format.h"
62 #include "chxj_qr_code.h"
63 #include "chxj_encoding.h"
64 #include "chxj_apply_convrule.h"
65 #include "chxj_cookie.h"
66 #include "chxj_url_encode.h"
67 #include "chxj_str_util.h"
68 #include "chxj_dump_string.h"
69 #if defined(USE_MYSQL_COOKIE)
70 #  include "chxj_mysql.h"
71 #endif
72 #include "chxj_serf.h"
73 #include "chxj_add_device_env.h"
74 #include "chxj_header_inf.h"
75 #include "chxj_jreserved_tag.h"
76
77
78 #define CHXJ_VERSION_PREFIX PACKAGE_NAME "/"
79 #define CHXJ_VERSION        PACKAGE_VERSION
80 #define CHXJ_POST_MAX       (0x1000000)
81
82 converter_t convert_routine[] = {
83   {
84     /* CHXJ_SPEC_UNKNOWN          */
85     .converter            = NULL,
86     .encoder              = NULL,
87     .emoji_only_converter = NULL,
88   },
89   {
90     /* CHXJ_SPEC_Chtml_1_0        */
91     .converter            = chxj_convert_chtml10,
92     .encoder              = chxj_encoding,
93     .emoji_only_converter = chxj_chtml10_emoji_only_converter,
94   },
95   {
96     /* CHXJ_SPEC_Chtml_2_0        */
97     .converter            = chxj_convert_chtml20,
98     .encoder              = chxj_encoding,
99     .emoji_only_converter = chxj_chtml20_emoji_only_converter,
100   },
101   {
102     /* CHXJ_SPEC_Chtml_3_0        */
103     .converter            = chxj_convert_chtml30,
104     .encoder              = chxj_encoding,
105     .emoji_only_converter = chxj_chtml30_emoji_only_converter,
106   },
107   {
108     /* CHXJ_SPEC_Chtml_4_0        */
109     .converter            = chxj_convert_chtml40,
110     .encoder              = chxj_encoding,
111     .emoji_only_converter = chxj_chtml40_emoji_only_converter,
112   },
113   {
114     /* CHXJ_SPEC_Chtml_5_0        */
115     .converter            = chxj_convert_chtml50,
116     .encoder              = chxj_encoding,
117     .emoji_only_converter = chxj_chtml50_emoji_only_converter,
118   },
119   {
120     /* CHXJ_SPEC_Chtml_6_0        */
121     .converter = chxj_convert_ixhtml10,
122     .encoder  = chxj_encoding,
123     .emoji_only_converter = chxj_chtml50_emoji_only_converter, /* XXX: TODO */
124   },
125   {
126     /* CHXJ_SPEC_Chtml_7_0        */
127     .converter = chxj_convert_ixhtml10,
128     .encoder  = chxj_encoding,
129     .emoji_only_converter = chxj_chtml50_emoji_only_converter, /* XXX: TODO */
130   },
131   {
132     /* CHXJ_SPEC_XHtml_Mobile_1_0 */
133     .converter            = chxj_convert_xhtml_mobile_1_0,
134     .encoder              = chxj_encoding,
135     .emoji_only_converter = chxj_xhtml_emoji_only_converter,
136   },
137   {
138     /* CHXJ_SPEC_Hdml             */
139     .converter            = chxj_convert_hdml,
140     .encoder              = chxj_encoding,
141     .emoji_only_converter = NULL,
142   },
143   {
144     /* CHXJ_SPEC_Jhtml            */
145     .converter            = chxj_convert_jhtml,
146     .encoder              = chxj_encoding,
147     .emoji_only_converter = chxj_jhtml_emoji_only_converter,
148   },
149   {
150     /* CHXJ_SPEC_Jxhtml            */
151     .converter            = chxj_convert_jxhtml,
152     .encoder              = chxj_encoding,
153     .emoji_only_converter = chxj_jxhtml_emoji_only_converter,
154   },
155   {
156     /* CHXJ_SPEC_HTML             */
157     .converter = NULL,
158     .encoder  = NULL,
159     .emoji_only_converter = NULL,
160   },
161 };
162
163 static int chxj_convert_input_header(request_rec *r,chxjconvrule_entry *entryp, device_table *spec);
164 static void s_convert_guid_parameter_to_header(request_rec *r, const char *param, device_table *spec);
165 static void s_add_cookie_id_if_has_location_header(request_rec *r, cookie_t *cookie);
166 static void s_clear_cookie_header(request_rec *r, device_table *spec);
167 static void s_add_no_cache_headers(request_rec *r, chxjconvrule_entry  *entryp);
168
169 /**
170  * Only when User-Agent is specified, the User-Agent header is camouflaged. 
171  *
172  * @param r   [i]
173  */
174 static apr_status_t 
175 chxj_headers_fixup(request_rec *r)
176 {
177   mod_chxj_config*    dconf; 
178   chxjconvrule_entry* entryp;
179   char*               user_agent;
180   device_table*       spec;
181   char                *contentType;
182   char                *contentLength;
183
184   DBG(r, "REQ[%X] start chxj_headers_fixup()", (unsigned int)(apr_size_t)r);
185   if (r->main) {
186     DBG(r, "REQ[%X] detect internal redirect.", (unsigned int)(apr_size_t)r);
187     DBG(r, "REQ[%X] end chxj_headers_fixup()",  (unsigned int)(apr_size_t)r);
188     return DECLINED;
189   }
190
191   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
192
193   user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
194   spec = chxj_specified_device(r, user_agent);
195
196   contentType = (char *)apr_table_get(r->headers_in, "Content-Type");
197   if (contentType
198       && strncasecmp("multipart/form-data", contentType, 19) == 0) {
199     DBG(r, "REQ[%X] detect multipart/form-data ==> no target", (unsigned int)(apr_size_t)r);
200     DBG(r, "REQ[%X] end chxj_headers_fixup()", (unsigned int)(apr_size_t)r);
201     return DECLINED;
202   }
203
204
205   switch(spec->html_spec_type) {
206   case CHXJ_SPEC_Chtml_1_0:
207   case CHXJ_SPEC_Chtml_2_0:
208   case CHXJ_SPEC_Chtml_3_0:
209   case CHXJ_SPEC_Chtml_4_0:
210   case CHXJ_SPEC_Chtml_5_0:
211   case CHXJ_SPEC_Chtml_6_0:
212   case CHXJ_SPEC_Chtml_7_0:
213   case CHXJ_SPEC_XHtml_Mobile_1_0:
214   case CHXJ_SPEC_Hdml:
215   case CHXJ_SPEC_Jhtml:
216   case CHXJ_SPEC_Jxhtml:
217     entryp = chxj_apply_convrule(r, dconf->convrules);
218     if (! entryp) {
219       DBG(r, "REQ[%X] end chxj_headers_fixup() (no pattern)", (unsigned int)(apr_size_t)r);
220       return DECLINED;
221     }
222     if (!(entryp->action & CONVRULE_ENGINE_ON_BIT) && !(entryp->action & CONVRULE_COOKIE_ONLY_BIT)) {
223       DBG(r, "REQ[%X] end chxj_headers_fixup() (engine off)", (unsigned int)(apr_size_t)r);
224       return DECLINED;
225     }
226     if (entryp->action & CONVRULE_EMOJI_ONLY_BIT) {
227       DBG(r, "REQ[%X] end chxj_headers_fixup() (emoji only)", (unsigned int)(apr_size_t)r);
228       return DECLINED;
229     } 
230   
231     apr_table_setn(r->headers_in, 
232                    CHXJ_HTTP_USER_AGENT, 
233                    user_agent);
234   
235     if (entryp->user_agent)
236       apr_table_setn(r->headers_in, 
237                      HTTP_USER_AGENT, 
238                      entryp->user_agent);
239     /*
240      * this clear process must do before chxj_convert_input_header function.
241      */
242     if ((entryp->action & CONVRULE_COOKIE_ON_BIT) || (entryp->action & CONVRULE_COOKIE_ONLY_BIT)) {
243       if (r->method_number == M_POST) {
244         if (! apr_table_get(r->headers_in, "X-Chxj-Forward")) {
245           s_clear_cookie_header(r, spec);
246         }
247       }
248       else {
249         s_clear_cookie_header(r, spec);
250       }
251     }
252
253     chxj_convert_input_header(r,entryp, spec);
254
255     break;
256   
257   default:
258     DBG(r, "REQ[%X] end chxj_headers_fixup() (not mobile) spec->device_name[%s] User-Agent:[%s]", (unsigned int)(apr_size_t)r, spec->device_name, user_agent);
259     return DECLINED;
260
261   }
262
263
264   if (r->method_number == M_POST) {
265     if (! apr_table_get(r->headers_in, "X-Chxj-Forward")) {
266         DBG(r, "REQ[%X] set Input handler old:[%s] proxyreq:[%d] uri:[%s] filename:[%s]", (unsigned int)(apr_size_t)r, r->handler, r->proxyreq, r->uri, r->filename);
267         r->proxyreq = PROXYREQ_NONE;
268         r->handler = apr_psprintf(r->pool, "chxj-input-handler");
269     }
270     else {
271       char *client_ip = (char *)apr_table_get(r->headers_in, CHXJ_HEADER_ORIG_CLIENT_IP);
272       if (client_ip) {
273         if (dconf->post_log) {
274           apr_table_setn(r->subprocess_env, dconf->post_log, dconf->post_log);
275         }
276         else {
277           apr_table_setn(r->subprocess_env, "chxj-post-log", "chxj-post-log");
278         }
279         apr_sockaddr_t *address = NULL;
280         apr_status_t rv = apr_sockaddr_info_get(&address, ap_get_server_name(r), APR_UNSPEC, ap_get_server_port(r), 0, r->pool);
281         if (rv != APR_SUCCESS) {
282           char buf[256];
283           ERR(r, "REQ[%X] %s:%d apr_sockaddr_info_get() failed: rv:[%d|%s]", (unsigned int)(apr_size_t)r, APLOG_MARK, rv, apr_strerror(rv, buf, 256));
284           DBG(r, "REQ[%X] end chxj_headers_fixup()", (unsigned int)(apr_size_t)r);
285           return DECLINED;
286         }
287         char *addr;
288         if (dconf->forward_server_ip) {
289           addr = dconf->forward_server_ip;
290         }
291         else {
292           apr_sockaddr_ip_get(&addr, address);
293         }
294         DBG(r, "REQ[%X] Client IP:[%s] vs Orig Client IP:[%s] vs Server IP:[%s]", (unsigned int)(apr_size_t)r, r->connection->remote_ip, client_ip, addr);
295         if (strcmp(addr, r->connection->remote_ip) == 0) {
296           r->connection->remote_ip = apr_pstrdup(r->connection->pool, client_ip);
297         }
298         if (! apr_table_get(r->headers_in, "Content-Length")) {
299           contentLength = (char *)apr_table_get(r->headers_in, "X-Chxj-Content-Length");
300           if (contentLength) {
301             apr_table_set(r->headers_in, "Content-Length", contentLength);
302           }
303         }
304       }
305     }
306   }
307
308   chxj_add_device_env(r, spec);
309
310   DBG(r, "REQ[%X] end chxj_headers_fixup()", (unsigned int)(apr_size_t)r);
311
312   return DECLINED;
313 }
314
315
316 static void
317 s_clear_cookie_header(request_rec *r, device_table *spec)
318 {
319   DBG(r, "REQ[%X] start s_clear_cookie_header()", TO_ADDR(r));
320   switch(spec->html_spec_type) {
321   case CHXJ_SPEC_Chtml_1_0:
322   case CHXJ_SPEC_Chtml_2_0:
323   case CHXJ_SPEC_Chtml_3_0:
324   case CHXJ_SPEC_Chtml_4_0:
325   case CHXJ_SPEC_Chtml_5_0:
326   case CHXJ_SPEC_Chtml_6_0:
327   case CHXJ_SPEC_Chtml_7_0:
328   case CHXJ_SPEC_XHtml_Mobile_1_0:
329   case CHXJ_SPEC_Jhtml:
330     apr_table_unset(r->headers_in, "Cookie");
331     break;
332   default:
333     break;
334   }
335   DBG(r, "REQ[%X] end   s_clear_cookie_header()", TO_ADDR(r));
336 }
337
338
339 /**
340  * It converts it from CHTML into XXML corresponding to each model. 
341  *
342  * @param r   [i]
343  * @param src [i]   It is former HTML character string. 
344  * @param len [i/o] It is length of former HTML character string. 
345  */
346 static char * 
347 chxj_convert(request_rec *r, const char **src, apr_size_t *len, device_table *spec, const char *ua, cookie_t **cookiep)
348 {
349   char                *user_agent;
350   char                *dst;
351   char                *tmp;
352   cookie_t            *cookie;
353   mod_chxj_config     *dconf; 
354   chxjconvrule_entry  *entryp;
355
356   DBG(r,"REQ[%X] start of chxj_convert()", (unsigned int)(apr_size_t)r);
357
358   chxj_dump_string(r, APLOG_MARK, "INPUT Data", *src, *len);
359
360   dst  = apr_pstrcat(r->pool, (char *)*src, NULL);
361
362   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
363
364
365   entryp = chxj_apply_convrule(r, dconf->convrules);
366
367   if (!entryp || (!(entryp->action & CONVRULE_ENGINE_ON_BIT) && !(entryp->action & CONVRULE_COOKIE_ONLY_BIT))) {
368     DBG(r,"REQ[%X] end of chxj_convert()", (unsigned int)(apr_size_t)r);
369     return (char *)*src;
370   }
371
372
373   /*------------------------------------------------------------------------*/
374   /* get UserAgent from http header                                         */
375   /*------------------------------------------------------------------------*/
376   if (entryp->user_agent)
377     user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
378   else
379     user_agent = (char *)apr_table_get(r->headers_in, HTTP_USER_AGENT);
380
381   DBG(r,"REQ[%X] User-Agent:[%s]", (unsigned int)(apr_size_t)r, user_agent);
382   DBG(r,"REQ[%X] content type is %s", (unsigned int)(apr_size_t)r, r->content_type);
383
384
385   if (  ! STRNCASEEQ('t','T', "text/html", r->content_type, sizeof("text/html")-1)
386     &&  ! STRNCASEEQ('a','A', "application/xhtml+xml", r->content_type, sizeof("application/xhtml+xml")-1)) {
387     DBG(r,"REQ[%X] no convert. content type is %s", (unsigned int)(apr_size_t)r, r->content_type);
388     DBG(r,"REQ[%X] end of chxj_convert()", (unsigned int)(apr_size_t)r);
389     return (char *)*src;
390   }
391
392   if (ua && user_agent && strcasecmp(user_agent, ua) != 0) {
393     /* again */
394     spec = chxj_specified_device(r, user_agent);
395   }
396
397   /*
398    * save cookie.
399    */
400   cookie = NULL;
401   if ((entryp->action & CONVRULE_COOKIE_ON_BIT 
402       && !(entryp->action & CONVRULE_EMOJI_ONLY_BIT)
403       && !(entryp->action & CONVRULE_ENVINFO_ONLY_BIT))
404       || (entryp->action & CONVRULE_COOKIE_ONLY_BIT)) {
405     switch(spec->html_spec_type) {
406     case CHXJ_SPEC_Chtml_1_0:
407     case CHXJ_SPEC_Chtml_2_0:
408     case CHXJ_SPEC_Chtml_3_0:
409     case CHXJ_SPEC_Chtml_4_0:
410     case CHXJ_SPEC_Chtml_5_0:
411     case CHXJ_SPEC_Chtml_6_0:
412     case CHXJ_SPEC_Chtml_7_0:
413     case CHXJ_SPEC_XHtml_Mobile_1_0:
414     case CHXJ_SPEC_Jhtml:
415       cookie = chxj_save_cookie(r);
416       break;
417     default:
418       break;
419     }
420   }
421
422   if (!r->header_only) {
423
424     if ((entryp->action & CONVRULE_COOKIE_ONLY_BIT) && cookie) {
425       dst = chxj_cookie_only_mode(r, *src, (apr_size_t *)len, cookie);
426     }
427     else {
428       tmp = NULL;
429       if (convert_routine[spec->html_spec_type].encoder)
430         tmp = convert_routine[spec->html_spec_type].encoder(r, 
431                                                             *src, 
432                                                             (apr_size_t *)len);
433   
434       if (entryp->action & CONVRULE_EMOJI_ONLY_BIT) {
435         if (tmp) {
436           tmp = chxj_node_convert_chxjif_only(r, spec, (const char*)tmp, (apr_size_t *)len);
437         }
438         else {
439           tmp = chxj_node_convert_chxjif_only(r, spec, (const char *)*src, (apr_size_t *)len);
440         }
441         if (convert_routine[spec->html_spec_type].emoji_only_converter) {
442           dst = convert_routine[spec->html_spec_type].emoji_only_converter(r,spec, tmp,*len);
443           if (dst != NULL) {
444             *len = strlen(dst);
445           }
446           else {
447             dst = apr_palloc(r->pool, 1);
448             *dst = 0;
449             *len = 0;
450           }
451         }
452         DBG(r, "REQ[%X] end of chxj_convert()(emoji only)", (unsigned int)(apr_size_t)r);
453       }
454   
455       if (   !(entryp->action & CONVRULE_EMOJI_ONLY_BIT) 
456           && !(entryp->action & CONVRULE_ENVINFO_ONLY_BIT)) {
457         if (convert_routine[spec->html_spec_type].converter) {
458           if (tmp)
459             dst = convert_routine[spec->html_spec_type].converter(r, 
460                                                                   spec, 
461                                                                   tmp, 
462                                                                   *len, 
463                                                                   len, 
464                                                                   entryp, 
465                                                                   cookie);
466           else
467             dst = convert_routine[spec->html_spec_type].converter(r,
468                                                                   spec, 
469                                                                   *src, 
470                                                                   *len, 
471                                                                   len, 
472                                                                   entryp, 
473                                                                   cookie);
474         }
475       }
476     }
477   }
478   if (*len > 0){
479     if (strcasecmp(spec->output_encoding,"UTF-8") == 0){
480       dst = chxj_iconv(r,r->pool,dst,len,"CP932","UTF-8");
481     }
482   }
483
484
485   ap_set_content_length(r, *len);
486
487   if (*len == 0) {
488     dst = apr_psprintf(r->pool, "\n");
489     *len = 1;
490   }
491   dst[*len] = 0;
492   if (cookie) {
493     *cookiep = cookie;
494   }
495
496
497
498   DBG(r, "REQ[%X] end of chxj_convert()", (unsigned int)(apr_size_t)r);
499
500   return dst;
501 }
502
503
504 /**
505  * convert GUID parameter.
506  *
507  */
508 static void
509 s_convert_guid_parameter_to_header(request_rec *r, const char *param, device_table *spec)
510 {
511   DBG(r, "REQ[%X] start s_convert_guid_parameter() param:[%s]", (unsigned int)(apr_size_t)r, param);
512   if (strcasecmp(param, "guid") == 0) {
513     switch(spec->html_spec_type) {
514     case CHXJ_SPEC_XHtml_Mobile_1_0:
515       do {
516         char *x_up_subno = (char *)apr_table_get(r->headers_in, "x-up-subno");
517         if (x_up_subno) {
518           apr_table_setn(r->headers_in, "X-DCMGUID", x_up_subno);
519         }
520       } while(0);
521       break;
522     case CHXJ_SPEC_Jhtml:
523     case CHXJ_SPEC_Jxhtml:
524       do {
525         char *x_jphone_uid = (char *)apr_table_get(r->headers_in, "x-jphone-uid");
526         if (x_jphone_uid) {
527           apr_table_setn(r->headers_in, "X-DCMGUID", x_jphone_uid);
528         }
529       } while(0);
530       break;
531     default:
532       break;
533     }
534   }
535   DBG(r, "REQ[%X] end s_convert_guid_parameter()", (unsigned int)(apr_size_t)r);
536 }
537
538 /**
539  * It converts it from HEADER.
540  *
541  * @param r   [i]
542  */
543 static int
544 chxj_convert_input_header(request_rec *r,chxjconvrule_entry *entryp, device_table *spec) 
545 {
546
547   char       *buff;
548   char       *buff_pre;
549   apr_size_t urilen;
550   char       *result;
551   char       *pair;
552   char       *name;
553   char       *value;
554   char       *pstate;
555   char       *vstate;
556   cookie_t   *cookie = NULL;
557   int        no_update_flag = 0;
558
559   DBG(r, "REQ[%X] start chxj_convert_input_header()", (unsigned int)(apr_size_t)r);
560
561   if (! r->args) {
562     DBG(r, "REQ[%X] r->args=[null]", (unsigned int)(apr_size_t)r);
563     DBG(r, "REQ[%X] end   chxj_convert_input_header()", (unsigned int)(apr_size_t)r);
564     return 0;
565   }
566   urilen = strlen(r->args);
567
568   result = qs_alloc_zero_byte_string(r->pool);
569
570   buff_pre = apr_pstrdup(r->pool, r->args);
571
572   for (;;) {
573     char *pair_sv;
574
575     pair = apr_strtok(buff_pre, "&", &pstate);
576     if (pair == NULL)
577       break;
578
579     buff_pre = NULL;
580
581     pair_sv = apr_pstrdup(r->pool, pair);
582
583     name  = apr_strtok(pair, "=", &vstate);
584     value = apr_strtok(NULL, "=", &vstate);
585     if (! name) continue;
586     if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0 || strcasecmp(name, chxj_url_encode(r->pool, CHXJ_COOKIE_NOUPDATE_PARAM)) == 0) {
587       DBG(r, "REQ[%X] found cookie no update parameter", (unsigned int)(apr_size_t)r);
588       no_update_flag++;
589     }
590   }
591
592   buff = apr_pstrdup(r->pool, r->args);
593   DBG(r, "REQ[%X] r->args=[%s]", (unsigned int)(apr_size_t)r, buff);
594
595   /* _chxj_dmy */
596   /* _chxj_c_ */
597   /* _chxj_r_ */
598   /* _chxj_s_ */
599   for (;;) {
600     char *pair_sv;
601
602     pair = apr_strtok(buff, "&", &pstate);
603     if (pair == NULL)
604       break;
605
606     buff = NULL;
607
608     pair_sv = apr_pstrdup(r->pool, pair);
609
610     name  = apr_strtok(pair, "=", &vstate);
611     value = apr_strtok(NULL, "=", &vstate);
612     if (! name) continue;
613     name = chxj_safe_to_jreserved_tag(r, name);
614     if (strncasecmp(name, "_chxj", 5) != 0 && strncasecmp(name, "%5Fchxj", sizeof("%5Fchxj")-1) != 0) {
615       if (strlen(result) != 0) 
616         result = apr_pstrcat(r->pool, result, "&", NULL);
617
618       if (strcasecmp(entryp->encoding, "NONE") != 0) {
619         apr_size_t dlen;
620         char *dvalue;
621         char *dname;
622
623         if (value && *value != 0) {
624           value = chxj_url_decode(r->pool, value);
625           dlen   = strlen(value);
626           DBG(r, "************ before encoding[%s]", value);
627   
628           dvalue = chxj_rencoding(r, value, &dlen,spec->output_encoding);
629           dvalue = chxj_url_encode(r->pool, dvalue);
630   
631           DBG(r, "************ after encoding[%s]", dvalue);
632         }
633         else {
634           dvalue = "";
635         }
636
637         if (name && *name != 0) {
638           name = chxj_url_decode(r->pool, name);
639           dlen = strlen(name);
640           dname = chxj_rencoding(r, name, &dlen,spec->output_encoding);
641           dname = chxj_url_encode(r->pool, dname);
642         }
643         else {
644           dname = "";
645         }
646         s_convert_guid_parameter_to_header(r, dname, spec);
647         result = apr_pstrcat(r->pool, result, dname, "=", dvalue, NULL);
648       }
649       else {
650         s_convert_guid_parameter_to_header(r, name, spec);
651         if (strcmp(name, pair_sv) != 0) {
652           result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
653         } 
654         else {
655           result = apr_pstrcat(r->pool, result, name, NULL);
656         }
657       }
658     }
659     else
660     if ( (strncasecmp(name, "_chxj_c_", 8) == 0 || strncasecmp(name, "%5Fchxj%5Fc%5F", sizeof("%5Fchxj%5Fc%5F")-1) == 0)
661       || (strncasecmp(name, "_chxj_r_", 8) == 0 || strncasecmp(name, "%5Fchxj%5Fr%5F", sizeof("%5Fchxj%5Fr%5F")-1) == 0)
662       || (strncasecmp(name, "_chxj_s_", 8) == 0 || strncasecmp(name, "%5Fchxj%5Fs%5F", sizeof("%5Fchxj%5Fs%5F")-1) == 0)) {
663       if (value == NULL)
664         continue;
665
666       if (strlen(value) == 0)
667         continue;
668
669       if (strlen(result) != 0)
670         result = apr_pstrcat(r->pool, result, "&", NULL);
671
672       result = apr_pstrcat(r->pool, result, &name[8], "=", value, NULL);
673     }
674     else
675     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0 || strcasecmp(name, "%5Fchxj%5Fcc") == 0) {
676       if (! cookie) {
677         apr_table_unset(r->headers_in, "Cookie");
678         DBG(r, "REQ[%X] found cookie parameter[%s]",    (unsigned int)(apr_size_t)r, value);
679         DBG(r, "REQ[%X] call start chxj_load_cookie()", (unsigned int)(apr_size_t)r);
680         cookie_lock_t *lock = chxj_cookie_lock(r);
681         cookie = chxj_load_cookie(r, value);
682         DBG(r, "REQ[%X] call end   chxj_load_cookie()", (unsigned int)(apr_size_t)r);
683         if (! no_update_flag && cookie) {
684           cookie = chxj_update_cookie(r, cookie);
685         }
686         chxj_cookie_unlock(r, lock);
687       }
688       if (cookie && cookie->cookie_id) {
689         if (strlen(result) != 0)
690           result = apr_pstrcat(r->pool, result, "&", NULL);
691         result = apr_pstrcat(r->pool, result, name, "=", cookie->cookie_id, NULL);
692       }
693     }
694     else
695     if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
696       if (strlen(result) != 0)
697         result = apr_pstrcat(r->pool, result, "&", NULL);
698       result = apr_pstrcat(r->pool, result, name, "=", value, NULL);
699     }
700   }
701   apr_table_setn(r->headers_in, "X-Chxj-Cookie-No-Update", "true");
702   if (! no_update_flag) {
703     result = apr_pstrcat(r->pool, result, "&_chxj_nc=true", NULL);
704   }
705   r->args = result;
706
707   DBG(r, "REQ[%X] result r->args=[%s]",               (unsigned int)(apr_size_t)r, r->args);
708   DBG(r, "REQ[%X] end   chxj_convert_input_header()", (unsigned int)(apr_size_t)r);
709   return 0;
710 }
711
712
713 /**
714  * It converts it from POSTDATA .
715  *
716  * @param r   [i]
717  * @param src [i]   It is POSTDATA character string.
718  * @param len [i/o] It is length of former HTML character string.
719  */
720 static char *
721 chxj_input_convert(
722   request_rec         *r, 
723   const char          **src, 
724   apr_size_t          *len, 
725   chxjconvrule_entry  *entryp,
726   device_table        *spec)
727 {
728   char     *pair;
729   char     *name;
730   char     *value;
731   char     *pstate;
732   char     *vstate;
733   char     *s;
734   char     *result;
735   cookie_t *cookie = NULL;
736   char     *buff_pre;
737   int      no_update_flag = 0;
738   apr_size_t ilen = 0;
739   apr_pool_t *pool;
740
741   DBG(r, "REQ[%X] start of chxj_input_convert()", (unsigned int)(apr_size_t)r);
742
743   if (! *src) {
744     DBG(r, "REQ[%X] end of chxj_input_convert() (input is null)", (unsigned int)(apr_size_t)r);
745     return apr_pstrdup(r->pool, "");
746   }
747
748   apr_pool_create(&pool, r->pool);
749
750   s        = apr_pstrdup(pool, *src);
751   ilen     = strlen(s);
752   buff_pre = apr_pstrdup(pool, *src);
753
754   result   = qs_alloc_zero_byte_string(pool);
755
756   chxj_dump_string(r, APLOG_MARK, "BEFORE input convert source", s, ilen);
757
758   for (;;) {
759     char *pair_sv;
760
761     pair = apr_strtok(buff_pre, "&", &pstate);
762     if (pair == NULL)
763       break;
764
765     buff_pre = NULL;
766
767     pair_sv = apr_pstrdup(pool, pair);
768
769     name  = apr_strtok(pair, "=", &vstate);
770     value = apr_strtok(NULL, "=", &vstate);
771     if (! name) continue;
772     if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0 || strcasecmp(name, chxj_url_encode(r->pool, CHXJ_COOKIE_NOUPDATE_PARAM)) == 0) {
773       DBG(r, "REQ[%X] found cookie no update parameter", (unsigned int)(apr_size_t)r);
774       no_update_flag++;
775     }
776   }
777
778   /* _chxj_dmy */
779   /* _chxj_c_ */
780   /* _chxj_r_ */
781   /* _chxj_s_ */
782   for (;;) {
783     pair = apr_strtok(s, "&", &pstate);
784     if (pair == NULL)
785       break;
786     s = NULL;
787
788     name  = apr_strtok(pair, "=", &vstate);
789     value = apr_strtok(NULL, "=", &vstate);
790     if (! name) continue;
791     name  = chxj_safe_to_jreserved_tag(r, name);
792
793     if (strncasecmp(name, "_chxj", 5) != 0 && strncasecmp(name, "%5Fchxj", sizeof("%5Fchxj")-1) != 0) {
794       if (strlen(result) != 0) 
795         result = apr_pstrcat(pool, result, "&", NULL);
796
797       if (strcasecmp(entryp->encoding, "NONE") != 0) {
798         apr_size_t dlen;
799         char *dvalue;
800         char *dname;
801
802         if (value && *value != 0) {
803           value = chxj_url_decode(pool, value);
804           dlen   = strlen(value);
805           dvalue = chxj_rencoding(r, value, &dlen,spec->output_encoding);
806           dvalue = chxj_url_encode(pool, dvalue);
807         }
808         else {
809           dvalue = "";
810         }
811
812         if (name && *name != 0) {
813           name = chxj_url_decode(pool, name);
814           dlen = strlen(name);
815           dname = chxj_rencoding(r, name, &dlen,spec->output_encoding);
816           dname = chxj_url_encode(pool, dname);
817         }
818         else {
819           dname = "";
820         }
821
822
823         result = apr_pstrcat(pool, result, dname, "=", dvalue, NULL);
824       }
825       else {
826         result = apr_pstrcat(pool, result, name, "=", value, NULL);
827       }
828     }
829     else
830     if ( (strncasecmp(name, "_chxj_c_", 8) == 0 || strncasecmp(name, "%5Fchxj%5Fc%5F", sizeof("%5Fchxj%5Fc%5F")-1) == 0)
831       || (strncasecmp(name, "_chxj_r_", 8) == 0 || strncasecmp(name, "%5Fchxj%5Fr%5F", sizeof("%5Fchxj%5Fr%5F")-1) == 0)
832       || (strncasecmp(name, "_chxj_s_", 8) == 0 || strncasecmp(name, "%5Fchxj%5Fs%5F", sizeof("%5Fchxj%5Fs%5F")-1) == 0)) {
833       if (value == NULL)
834         continue;
835
836       if (strlen(value) == 0)
837         continue;
838
839       if (strlen(result) != 0)
840         result = apr_pstrcat(pool, result, "&", NULL);
841
842       if (strcasecmp(entryp->encoding, "NONE") != 0 && value && strlen(value)) {
843         apr_size_t dlen;
844         char       *dvalue;
845
846         dlen   = strlen(value);
847         value = chxj_url_decode(pool, value);
848         dvalue = chxj_rencoding(r, value, &dlen,spec->output_encoding);
849         dvalue = chxj_url_encode(pool,dvalue);
850         result = apr_pstrcat(pool, result, &name[8], "=", dvalue, NULL);
851
852       }
853       else {
854         result = apr_pstrcat(pool, result, &name[8], "=", value, NULL);
855       }
856     }
857     else
858     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0 || strcasecmp(name, "%5Fchxj%5Fcc") == 0) {
859       if (! cookie) {
860         apr_table_unset(r->headers_in, "Cookie");
861         DBG(r, "REQ[%X] found cookie parameter[%s]",    (unsigned int)(apr_size_t)r, value);
862         DBG(r, "REQ[%X] call start chxj_load_cookie()", (unsigned int)(apr_size_t)r);
863         cookie_lock_t *lock = chxj_cookie_lock(r);
864         cookie = chxj_load_cookie(r, value);
865         DBG(r, "REQ[%X] call end   chxj_load_cookie()", (unsigned int)(apr_size_t)r);
866         if (! no_update_flag && cookie) {
867           cookie = chxj_update_cookie(r, cookie);
868         }
869         chxj_cookie_unlock(r, lock);
870       }
871       if (cookie && cookie->cookie_id) {
872         if (strlen(result) != 0)
873           result = apr_pstrcat(pool, result, "&", NULL);
874         result = apr_pstrcat(pool, result, name, "=", cookie->cookie_id, NULL);
875       }
876     }
877     else
878     if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
879       if (strlen(result) != 0)
880         result = apr_pstrcat(pool, result, "&", NULL);
881       result = apr_pstrcat(pool, result, name, "=", value, NULL);
882     }
883     else
884     if ( strncasecmp(name, CHXJ_QUERY_STRING_PARAM_PREFIX,     sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX)-1) == 0) {
885       apr_size_t dlen = 0;
886       char*      dvalue;
887       if (value) {
888         dlen   = strlen(value);
889       }
890       if (dlen) {
891         value = chxj_url_decode(pool, value);
892         dvalue = chxj_rencoding(r, value, &dlen,spec->output_encoding);
893         dvalue = chxj_url_encode(pool,dvalue);
894         if (r->args && strlen(r->args) > 0) {
895           r->args = apr_pstrcat(pool, r->args, "&", &name[sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX)-1], "=", dvalue, NULL);
896         }
897         else {
898           r->args = apr_pstrcat(pool, &name[sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX)-1], "=", dvalue, NULL);
899         }
900         s_convert_guid_parameter_to_header(r, &name[sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX)-1], spec);
901       }
902     }
903     else
904     if (strncasecmp(name, CHXJ_QUERY_STRING_PARAM_PREFIX_ENC, sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX_ENC)-1) == 0) {
905       apr_size_t dlen;
906       char*      dvalue;
907       dlen   = strlen(value);
908       if (dlen && value) {
909         value = chxj_url_decode(pool, value);
910         dvalue = chxj_rencoding(r, value, &dlen,spec->output_encoding);
911         dvalue = chxj_url_encode(pool,dvalue);
912         if (r->args && strlen(r->args) > 0) {
913           r->args = apr_pstrcat(pool, r->args, "&", &name[sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX_ENC)-1], "=", dvalue, NULL);
914         }
915         else {
916           r->args = apr_pstrcat(pool, &name[sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX_ENC)-1], "=", dvalue, NULL);
917         }
918         s_convert_guid_parameter_to_header(r, &name[sizeof(CHXJ_QUERY_STRING_PARAM_PREFIX_ENC)-1], spec);
919       }
920     }
921     DBG(r, "REQ[%X] ************************ name:[%s]", (unsigned int)(apr_size_t)r, name);
922   }
923   *len = strlen(result);
924   apr_table_setn(r->headers_in, "X-Chxj-Cookie-No-Update", "true");
925   if (! no_update_flag) {
926     result = apr_pstrcat(pool, result, "&_chxj_nc=true", NULL);
927   }
928
929   DBG(r, "REQ[%X] AFTER input convert result = [%s]", (unsigned int)(apr_size_t)r, result);
930   DBG(r, "REQ[%X] end chxj_input_convert()", (unsigned int)(apr_size_t)r);
931
932   return result;
933 }
934
935
936 /**
937  * The received data is returned to the filter.
938  *
939  * @param f    [i/o] It is a filter. 
940  * @param data [i]   It is data returned to the filter. 
941  * @param len  [i]   It is length of the data returned to the filter. 
942  */
943 static apr_status_t 
944 pass_data_to_filter(ap_filter_t *f, const char *data, 
945                                         apr_size_t len)
946 {
947   request_rec         *r = f->r;
948   conn_rec            *c = r->connection;
949   apr_status_t        rv;
950   apr_bucket_brigade  *bb;
951   apr_bucket          *b;
952
953   DBG(r, "REQ[%X] start pass_data_to_filter()", (unsigned int)(apr_size_t)r);
954
955   chxj_header_inf_clear(r);
956
957   bb = apr_brigade_create(r->pool, c->bucket_alloc);
958   b  = apr_bucket_transient_create(data, len, c->bucket_alloc);
959
960   APR_BRIGADE_INSERT_TAIL(bb, b);
961   b = apr_bucket_eos_create(f->c->bucket_alloc);
962   APR_BRIGADE_INSERT_TAIL(bb, b);
963
964   rv = ap_pass_brigade(f->next, bb);
965   if (rv != APR_SUCCESS) {
966     DBG(r, "REQ[%X] end pass_data_to_filter() (apr_pass_brigade)", (unsigned int)(apr_size_t)r);
967     return rv;
968   }
969
970   DBG(r, "REQ[%X] end pass_data_to_filter()", (unsigned int)(apr_size_t)r);
971
972   return rv;
973 }
974
975 /**
976  * Add No Cache Header
977  */
978 static void
979 s_add_no_cache_headers(request_rec *r, chxjconvrule_entry  *entryp)
980 {
981   if (entryp->action & CONVRULE_NOCACHE_ON_BIT) {
982     apr_table_unset(r->headers_out,     "Pragma");
983     apr_table_unset(r->err_headers_out, "Pragma");
984     apr_table_unset(r->headers_out,     "Expires");
985     apr_table_unset(r->err_headers_out, "Expires");
986     apr_table_unset(r->headers_out,     "Cache-Control");
987     apr_table_unset(r->err_headers_out, "Cache-Control");
988
989     apr_table_setn(r->err_headers_out, "Pragma", "no-cache");
990     apr_table_setn(r->err_headers_out, "Expires", "Thu, 01 Jan 1970 00:00:00 GMT");
991     apr_table_setn(r->err_headers_out, "Cache-Control", "no-cache, no-store");
992   }
993 }
994
995
996 /**
997  * It is the main loop of the output filter. 
998  *
999  * @param f   [i/o] It is a filter.
1000  * @param bb  [i]   
1001  */
1002 static apr_status_t 
1003 chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
1004 {
1005   request_rec         *r;
1006   apr_status_t        rv;
1007   apr_bucket          *b;
1008   const char          *data;
1009   char                *user_agent = NULL;
1010   apr_size_t          len;
1011   mod_chxj_ctx        *ctx = (mod_chxj_ctx *)f->ctx;
1012   cookie_t            *cookie = NULL;
1013   mod_chxj_config     *dconf;
1014   chxjconvrule_entry  *entryp = NULL;
1015   device_table        *spec = NULL;
1016   apr_pool_t          *pool;
1017
1018   r  = f->r;
1019   DBG(f->r, "REQ[%X] start of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1020   rv = APR_SUCCESS;
1021
1022   apr_pool_create(&pool, r->pool);
1023
1024   entryp = ctx->entryp;
1025   spec   = ctx->spec;
1026   dconf  = chxj_get_module_config(r->per_dir_config, &chxj_module);
1027
1028
1029   if (apr_table_get(r->headers_out, "Location") || apr_table_get(r->err_headers_out, "Location")) {
1030     DBG(r, "REQ[%X] found Location header", TO_ADDR(r));
1031     if ((entryp->action & CONVRULE_COOKIE_ON_BIT) || (entryp->action & CONVRULE_COOKIE_ONLY_BIT)) {
1032       cookie_lock_t *lock = NULL;
1033       DBG(r, "REQ[%X] entryp->action == COOKIE_ON_BIT", TO_ADDR(r));
1034       switch(spec->html_spec_type) {
1035       case CHXJ_SPEC_Chtml_1_0:
1036       case CHXJ_SPEC_Chtml_2_0:
1037       case CHXJ_SPEC_Chtml_3_0:
1038       case CHXJ_SPEC_Chtml_4_0:
1039       case CHXJ_SPEC_Chtml_5_0:
1040       case CHXJ_SPEC_Chtml_6_0:
1041       case CHXJ_SPEC_Chtml_7_0:
1042       case CHXJ_SPEC_XHtml_Mobile_1_0:
1043       case CHXJ_SPEC_Jhtml:
1044         lock = chxj_cookie_lock(r);
1045         cookie = chxj_save_cookie(r);
1046         s_add_cookie_id_if_has_location_header(r, cookie);
1047         chxj_cookie_unlock(r, lock);
1048         break;
1049       default:
1050         break;
1051       }
1052     }
1053     if (! ap_is_HTTP_REDIRECT(r->status)) {
1054       r->status = HTTP_MOVED_TEMPORARILY;
1055     }
1056     s_add_no_cache_headers(r, entryp);
1057     /* must not send body. */
1058     rv = pass_data_to_filter(f, "", 0);
1059     DBG(f->r, "REQ[%X] end of chxj_output_filter()", TO_ADDR(r));
1060     return rv;
1061   }
1062
1063
1064   if (r->content_type) {
1065     if (! STRNCASEEQ('t','T',"text/html",r->content_type, sizeof("text/html")-1)
1066     &&  ! STRNCASEEQ('t','T',"text/xml", r->content_type, sizeof("text/xml")-1)
1067     &&  ! STRNCASEEQ('a','A',"application/xhtml+xml", r->content_type, sizeof("application/xhtml+xml")-1)
1068     &&  ! (dconf->image == CHXJ_IMG_ON
1069           && ! apr_table_get(r->headers_in, "CHXJ_IMG_CONV")
1070           && STRNCASEEQ('i','I',"image/",  r->content_type, sizeof("image/") -1)
1071           && ( STRCASEEQ('j','J',"jpeg",            &r->content_type[6])         /* JPEG */
1072             || STRCASEEQ('j','J',"jp2",             &r->content_type[6])         /* JPEG2000 */
1073             || STRCASEEQ('j','J',"jpeg2000",        &r->content_type[6])         /* JPEG2000 */
1074             || STRCASEEQ('j','J',"jpeg2000-image",  &r->content_type[6])         /* JPEG2000 */
1075             || STRCASEEQ('x','X',"x-jpeg2000-image",&r->content_type[6])         /* JPEG2000 */
1076             || STRCASEEQ('x','X',"x-ms-bmp",        &r->content_type[6])         /* BMP */
1077             || STRCASEEQ('p','P',"png",             &r->content_type[6])         /* PNG */
1078             || STRCASEEQ('x','X',"x-png",           &r->content_type[6])         /* PNG */
1079             || STRCASEEQ('g','G',"gif",             &r->content_type[6])))) {     /* GIF */
1080       
1081       DBG(r, "REQ[%X] not convert content-type:[%s] dconf->image:[%d]", (unsigned int)(apr_size_t)r, r->content_type, dconf->image);
1082       if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
1083         cookie_lock_t *lock = NULL;
1084         DBG(r, "REQ[%X] entryp->action == COOKIE_ON_BIT", (unsigned int)(apr_size_t)r);
1085         switch(spec->html_spec_type) {
1086         case CHXJ_SPEC_Chtml_1_0:
1087         case CHXJ_SPEC_Chtml_2_0:
1088         case CHXJ_SPEC_Chtml_3_0:
1089         case CHXJ_SPEC_Chtml_4_0:
1090         case CHXJ_SPEC_Chtml_5_0:
1091         case CHXJ_SPEC_Chtml_6_0:
1092         case CHXJ_SPEC_Chtml_7_0:
1093         case CHXJ_SPEC_XHtml_Mobile_1_0:
1094         case CHXJ_SPEC_Jhtml:
1095           lock = chxj_cookie_lock(r);
1096           cookie = chxj_save_cookie(r);
1097           s_add_cookie_id_if_has_location_header(r, cookie);
1098           chxj_cookie_unlock(r, lock);
1099           break;
1100         default:
1101           break;
1102         }
1103       }
1104       if (apr_table_get(r->headers_out, "Location") || apr_table_get(r->err_headers_out, "Location")) {
1105         if (! ap_is_HTTP_REDIRECT(r->status)) {
1106           r->status = HTTP_MOVED_TEMPORARILY;
1107         }
1108       }
1109       s_add_no_cache_headers(r, entryp);
1110       ap_pass_brigade(f->next, bb);
1111       DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1112       return APR_SUCCESS;
1113     }
1114   }
1115   else {
1116     DBG(r, "REQ[%X] not convert content-type:[(null)]", (unsigned int)(apr_size_t)r);
1117     ap_pass_brigade(f->next, bb);
1118     DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1119     return APR_SUCCESS;
1120   }
1121
1122
1123   for (b = APR_BRIGADE_FIRST(bb);
1124        b != APR_BRIGADE_SENTINEL(bb); 
1125        b = APR_BUCKET_NEXT(b)) {
1126
1127     if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
1128       chxj_dump_string(r, APLOG_MARK, "READ Data", data, len);
1129
1130       /*--------------------------------------------------------------------*/
1131       /* append data                                                        */
1132       /*--------------------------------------------------------------------*/
1133       char *tmp;
1134       DBG(r, "append data start");
1135       ctx = (mod_chxj_ctx *)f->ctx;
1136
1137       if (len > 0) {
1138         tmp = apr_palloc(r->pool, ctx->len);
1139         memcpy(tmp, ctx->buffer, ctx->len);
1140
1141         ctx->buffer = apr_palloc(pool, ctx->len + len);
1142
1143         memcpy(ctx->buffer, tmp, ctx->len);
1144         memcpy(&ctx->buffer[ctx->len], data, len);
1145
1146         ctx->len += len;
1147       }
1148       DBG(r, "REQ[%X] append data end", (unsigned int)(apr_size_t)r);
1149     }
1150
1151     if (APR_BUCKET_IS_EOS(b)) {
1152
1153       DBG(r, "REQ[%X] eos", (unsigned int)(apr_size_t)r);
1154       /*----------------------------------------------------------------------*/
1155       /* End Of File                                                          */
1156       /*----------------------------------------------------------------------*/
1157       if (ctx) {
1158         cookie_lock_t *lock = NULL;
1159         ctx = (mod_chxj_ctx *)f->ctx;
1160
1161         DBG(r, "REQ[%X] content_type=[%s]", (unsigned int)(apr_size_t)r, r->content_type);
1162         lock = chxj_cookie_lock(r);
1163
1164         if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN 
1165             && r->content_type 
1166             && (STRNCASEEQ('a','A',"application/xhtml+xml", r->content_type, sizeof("application/xhtml+xml")-1)
1167             ||  STRNCASEEQ('t','T',"text/html", r->content_type, sizeof("text/html")-1))) {
1168           DBG(r, "REQ[%X] detect convert target:[%s]", (unsigned int)(apr_size_t)r, r->content_type);
1169
1170           if (ctx->len) {
1171             char *tmp;
1172
1173             tmp = apr_palloc(pool, ctx->len + 1);
1174
1175             memset(tmp, 0, ctx->len + 1);
1176             memcpy(tmp, ctx->buffer, ctx->len);
1177
1178             ctx->buffer = chxj_convert(r, 
1179                                        (const char **)&tmp, 
1180                                        (apr_size_t *)&ctx->len,
1181                                        spec,
1182                                        user_agent, &cookie);
1183
1184           }
1185           else {
1186             ctx->buffer = apr_psprintf(r->pool, "\n");
1187             ctx->len += 1;
1188             ctx->buffer = chxj_convert(r, 
1189                                        (const char **)&ctx->buffer, 
1190                                        (apr_size_t *)&ctx->len,
1191                                        spec,
1192                                        user_agent, &cookie);
1193
1194           }
1195         }
1196         if (r->content_type
1197             && *(char *)r->content_type == 't'
1198             && strncmp(r->content_type, "text/xml",   8) == 0) {
1199           DBG(r, "REQ[%X] text/XML", (unsigned int)(apr_size_t)r);
1200
1201           Doc       doc;
1202           Node      *root;
1203           Node      *child;
1204           qr_code_t qrcode;
1205           int       sts;
1206       
1207           memset(&doc,    0, sizeof(Doc));
1208           memset(&qrcode, 0, sizeof(qr_code_t));
1209           doc.r = r;
1210           doc.parse_mode  = PARSE_MODE_CHTML;
1211           qrcode.doc      = &doc;
1212           qrcode.r        = r;
1213       
1214           qs_init_malloc(&doc);
1215       
1216           root = qs_parse_string(&doc, ctx->buffer, ctx->len);
1217
1218           sts = 0;
1219           for (child = qs_get_child_node(&doc,root);
1220                child ;
1221                child = qs_get_next_node(&doc,child)) {
1222             char *name = qs_get_node_name(&doc,child);
1223             if (strcasecmp("qrcode",name) == 0) {
1224               sts++;
1225               break;
1226             }
1227           }
1228           qs_all_free(&doc,QX_LOGMARK);
1229           if (sts) {
1230             r->handler = apr_psprintf(r->pool, "chxj-qrcode");
1231             chxj_qrcode_node_to_qrcode(&qrcode, root);
1232             sts = chxj_qrcode_create_image_data(&qrcode, &ctx->buffer, &ctx->len);
1233             if (sts != OK) {
1234               ERR(r, "REQ[%X] qrcode create failed.", (unsigned int)(apr_size_t)r);
1235               chxj_cookie_unlock(r, lock);
1236               DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1237               return sts;
1238             }
1239             r->content_type = apr_psprintf(r->pool, "image/jpeg");
1240           }
1241         }
1242
1243         if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN 
1244             && r->content_type 
1245             && ( *r->content_type == 'i' || *r->content_type == 'I')
1246             && dconf->image == CHXJ_IMG_ON
1247             && strncasecmp("image/", r->content_type, 6) == 0
1248             && ( STRCASEEQ('j','J',"jpeg",            &r->content_type[6])         /* JPEG */
1249               || STRCASEEQ('j','J',"jp2",             &r->content_type[6])         /* JPEG2000 */
1250               || STRCASEEQ('j','J',"jpeg2000",        &r->content_type[6])         /* JPEG2000 */
1251               || STRCASEEQ('j','J',"jpeg2000-image",  &r->content_type[6])         /* JPEG2000 */
1252               || STRCASEEQ('x','X',"x-jpeg2000-image",&r->content_type[6])         /* JPEG2000 */
1253               || STRCASEEQ('p','P',"png",             &r->content_type[6])         /* PNG */
1254               || STRCASEEQ('x','X',"x-png",           &r->content_type[6])         /* PNG */
1255               || STRCASEEQ('x','X',"x-ms-bmp",     &r->content_type[6])         /* BMP */
1256               || STRCASEEQ('g','G',"gif",             &r->content_type[6]))) {     /* GIF */
1257           DBG(r, "REQ[%X] detect convert target:[%s]", (unsigned int)(apr_size_t)r, r->content_type);
1258           if (ctx->len) {
1259             char *tmp;
1260
1261             tmp = apr_palloc(pool, ctx->len + 1);
1262
1263             memset(tmp, 0, ctx->len + 1);
1264             memcpy(tmp, ctx->buffer, ctx->len);
1265             ctx->buffer = 
1266               chxj_convert_image(r, 
1267                                   (const char **)&tmp,
1268                                   (apr_size_t *)&ctx->len);
1269             if (ctx->buffer == NULL) {
1270               ctx->buffer = tmp;
1271             }
1272           }
1273         }
1274
1275         apr_table_unset(r->headers_out, "Content-Length");
1276         apr_table_unset(r->err_headers_out, "Content-Length");
1277         ap_set_content_length(r, (apr_off_t)ctx->len);
1278
1279         
1280         if (ctx->len > 0) {
1281           DBG(r, "REQ[%X] call pass_data_to_filter()", (unsigned int)(apr_size_t)r);
1282           s_add_cookie_id_if_has_location_header(r, cookie);
1283           if (apr_table_get(r->headers_out, "Location") || apr_table_get(r->err_headers_out, "Location")) {
1284             if (! ap_is_HTTP_REDIRECT(r->status)) {
1285               r->status = HTTP_MOVED_TEMPORARILY;
1286             }
1287           }
1288           if (ctx->len && ap_is_HTTP_REDIRECT(r->status)) {
1289             ctx->buffer = apr_pstrdup(pool, "");
1290             ctx->len    = 0;
1291             ap_set_content_length(r, (apr_off_t)ctx->len);
1292           }
1293           chxj_cookie_unlock(r,lock);
1294           s_add_no_cache_headers(r, entryp);
1295           rv = pass_data_to_filter(f, 
1296                                    (const char *)ctx->buffer, 
1297                                    (apr_size_t)ctx->len);
1298         }
1299         else {
1300           ctx->buffer = apr_pstrdup(pool, "");
1301           ctx->len    = 0;
1302           ap_set_content_length(r, (apr_off_t)ctx->len);
1303           chxj_cookie_unlock(r, lock);
1304           s_add_no_cache_headers(r, entryp);
1305           rv = pass_data_to_filter(f, 
1306                                    (const char *)ctx->buffer, 
1307                                    (apr_size_t)ctx->len);
1308         }
1309         DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1310         return rv;
1311       }
1312       else {
1313         DBG(r, "REQ[%X] SAVE COOKIE[%x]", (unsigned int)(apr_size_t)r, entryp->action);
1314
1315         /*
1316          * save cookie.
1317          */
1318         if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
1319           cookie_lock_t *lock = NULL;
1320           DBG(r, "REQ[%X] entryp->action == COOKIE_ON_BIT", (unsigned int)(apr_size_t)r);
1321           switch(spec->html_spec_type) {
1322           case CHXJ_SPEC_Chtml_1_0:
1323           case CHXJ_SPEC_Chtml_2_0:
1324           case CHXJ_SPEC_Chtml_3_0:
1325           case CHXJ_SPEC_Chtml_4_0:
1326           case CHXJ_SPEC_Chtml_5_0:
1327           case CHXJ_SPEC_Chtml_6_0:
1328           case CHXJ_SPEC_Chtml_7_0:
1329           case CHXJ_SPEC_XHtml_Mobile_1_0:
1330           case CHXJ_SPEC_Jhtml:
1331             lock = chxj_cookie_lock(r);
1332             cookie = chxj_save_cookie(r);
1333             /*
1334              * Location Header Check to add cookie parameter.
1335              */
1336             s_add_cookie_id_if_has_location_header(r, cookie);
1337             chxj_cookie_unlock(r, lock);
1338             apr_table_unset(r->headers_out, "Set-Cookie");
1339             apr_table_unset(r->err_headers_out, "Set-Cookie");
1340             break;
1341
1342           default:
1343             break;
1344           }
1345         }
1346         if (apr_table_get(r->headers_out, "Location") || apr_table_get(r->err_headers_out, "Location")) {
1347           if (! ap_is_HTTP_REDIRECT(r->status)) {
1348             r->status = HTTP_MOVED_TEMPORARILY;
1349           }
1350         }
1351         apr_table_setn(r->headers_out, "Content-Length", "0");
1352         DBG(r, "REQ[%X] call pass_data_to_filter()", (unsigned int)(apr_size_t)r);
1353         s_add_no_cache_headers(r, entryp);
1354         rv = pass_data_to_filter(f, (const char *)"", (apr_size_t)0);
1355         DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1356         return rv;
1357       }
1358     }
1359   }
1360   apr_brigade_destroy(bb);
1361
1362   DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1363
1364   return APR_SUCCESS;
1365 }
1366
1367 /**
1368  * Add Cookie_id if it has location header.
1369  */
1370 static void
1371 s_add_cookie_id_if_has_location_header(request_rec *r, cookie_t *cookie)
1372 {
1373   char *location_header = (char *)apr_table_get(r->headers_out, "Location");
1374   if (! location_header) {
1375     location_header = (char *)apr_table_get(r->err_headers_out, "Location");
1376   }
1377   if (cookie && location_header) {
1378     DBG(r, "REQ[%X] Location Header=[%s]", (unsigned int)(apr_size_t)r, location_header);
1379     location_header = chxj_add_cookie_parameter(r,
1380                                                 location_header,
1381                                                 cookie);
1382     apr_table_unset(r->headers_out, "Location");
1383     apr_table_setn(r->headers_out, "Location", location_header);
1384     DBG(r, "REQ[%X] Location Header=[%s]", (unsigned int)(apr_size_t)r, location_header);
1385     if (!ap_is_HTTP_REDIRECT(r->status)) {
1386       r->status = HTTP_MOVED_TEMPORARILY;
1387     }
1388   }
1389 }
1390
1391 /**
1392  * It is the main loop of the input filter handler. 
1393  *
1394  */
1395 static apr_status_t
1396 chxj_input_handler(request_rec *r)
1397 {
1398   mod_chxj_config     *dconf;
1399   chxjconvrule_entry  *entryp = NULL;
1400   device_table        *spec   = NULL;
1401   char                *post_data = NULL;
1402   apr_size_t          post_data_len = 0;
1403   char                *response;
1404   char                *user_agent;
1405   apr_pool_t          *pool;
1406   apr_size_t          ii;
1407   int                 response_code = 0;
1408   
1409   DBG(r, "start of chxj_input_handler()");
1410
1411   if (strcasecmp(r->handler, "chxj-input-handler")) {
1412     DBG(r, "end chxj_input_handler()");
1413     return DECLINED;
1414   }
1415   apr_pool_create(&pool, r->pool);
1416
1417   dconf      = chxj_get_module_config(r->per_dir_config, &chxj_module);
1418   user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
1419   if (!user_agent) {
1420     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1421   }
1422   spec       = chxj_specified_device(r, user_agent);
1423   entryp     = chxj_apply_convrule(r, dconf->convrules);
1424
1425   post_data = apr_pstrdup(pool, "");
1426   if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK) == OK) {
1427     if (ap_should_client_block(r)) {
1428       while (post_data_len < CHXJ_POST_MAX) {
1429 #define BUFSZ (256)
1430         char buffer[BUFSZ];
1431         int read_bytes = ap_get_client_block(r, buffer, BUFSZ-1);
1432         if (read_bytes<=0) {
1433           break;
1434         }
1435         buffer[read_bytes] = '\0';
1436         post_data = apr_pstrcat(pool, post_data, buffer, NULL);
1437         post_data_len += read_bytes;
1438 #undef BUFSZ
1439       }
1440     }
1441   }
1442
1443   /* 
1444    * now convert.
1445    */
1446   if (post_data_len > 0) {
1447     post_data = chxj_input_convert(r, (const char**)&post_data, (apr_size_t*)&post_data_len, entryp, spec);
1448     DBG(r, "(in:exchange)POSTDATA:[%s]", post_data);
1449   }
1450
1451   char *url_path;
1452   if (dconf->forward_url_base) {
1453     url_path = apr_psprintf(pool, "%s%s", dconf->forward_url_base, r->uri);
1454   }
1455   else {
1456     url_path = apr_psprintf(pool, "%s://%s:%d%s", chxj_apache_run_http_scheme(r), ap_get_server_name(r), ap_get_server_port(r), r->uri);
1457   }
1458   if (r->args) {
1459     url_path = apr_pstrcat(pool, url_path, "?", r->args, NULL);
1460   }
1461   DBG(r, "==> new url_path:[%s]", url_path);
1462
1463   apr_size_t res_len;
1464   apr_table_setn(r->headers_in, CHXJ_HEADER_ORIG_CLIENT_IP, r->connection->remote_ip);
1465   apr_table_unset(r->headers_in, "Content-Length");
1466   apr_table_setn(r->headers_in, "Content-Length", apr_psprintf(pool, "%" APR_SIZE_T_FMT, post_data_len));
1467   response = chxj_serf_post(r, pool, url_path, post_data, post_data_len, 1, &res_len, &response_code);
1468   DBG(r, "REQ[%X] -------------------------------------------------------", (unsigned int)(apr_size_t)r);
1469   DBG(r, "REQ[%X] response length:[%" APR_SIZE_T_FMT "]", (unsigned int)(apr_size_t)r, res_len);
1470   for (ii=0; ii<res_len/64; ii++) {
1471     DBG(r, "REQ[%X] response:[%.*s]", (unsigned int)(apr_size_t)r, 64, &response[ii*64]);
1472   }
1473   DBG(r, "REQ[%X] -------------------------------------------------------", (unsigned int)(apr_size_t)r);
1474
1475   char *chunked;
1476   if ((chunked = (char *)apr_table_get(r->headers_out, "Transfer-Encoding")) != NULL) {
1477     if (strcasecmp(chunked, "chunked") == 0) {
1478       r->chunked = 1;  
1479       apr_table_unset(r->headers_out, "Transfer-Encoding");
1480     }
1481   }
1482   if (ap_is_HTTP_ERROR(response_code)) {
1483     DBG(r, "REQ[%X] end of chxj_input_handler() (HTTP-ERROR received. response code:[%d])", (unsigned int)(apr_size_t)r, response_code);
1484     return response_code;
1485   }
1486   {
1487     apr_pool_t *wpool;
1488     apr_pool_create(&wpool, r->pool);
1489     apr_bucket_brigade *bb;
1490     apr_bucket *e;
1491     apr_status_t rv;
1492     conn_rec *c = r->connection;
1493     
1494     bb = apr_brigade_create(wpool, c->bucket_alloc);
1495     e  = apr_bucket_transient_create(response, res_len, c->bucket_alloc);
1496     APR_BRIGADE_INSERT_TAIL(bb, e);
1497     e = apr_bucket_eos_create(c->bucket_alloc);
1498     APR_BRIGADE_INSERT_TAIL(bb, e);
1499     if ((rv = ap_pass_brigade(r->output_filters, bb)) != APR_SUCCESS) {
1500       ERR(r, "REQ[%X] %s:%d failed ap_pass_brigade()", (unsigned int)(apr_size_t)r, APLOG_MARK);
1501       return rv;
1502     }
1503     apr_brigade_cleanup(bb);
1504   }
1505
1506   DBG(r, "REQ[%X] end of chxj_input_handler()", (unsigned int)(apr_size_t)r);
1507   return APR_SUCCESS;
1508 }
1509
1510 static mod_chxj_global_config *
1511 chxj_global_config_create(apr_pool_t *pool, server_rec *s)
1512 {
1513   mod_chxj_global_config *conf;
1514
1515   SDBG(s, "start chxj_global_config_create()");
1516
1517   /*--------------------------------------------------------------------------*/
1518   /* allocate an own subpool which survives server restarts                   */
1519   /*--------------------------------------------------------------------------*/
1520   conf = (mod_chxj_global_config *)apr_palloc(pool, 
1521                   sizeof(mod_chxj_global_config));
1522 #if 0
1523   conf->cookie_db_lock = NULL;
1524 #endif
1525   SDBG(s, "end   chxj_global_config_create()");
1526
1527   return conf;
1528 }
1529
1530
1531 /**
1532  * initialize chxj module
1533  */
1534 static int 
1535 chxj_init_module(apr_pool_t *p, 
1536                  apr_pool_t *UNUSED(plog), 
1537                  apr_pool_t *UNUSED(ptemp), 
1538                  server_rec *s)
1539 {
1540   void *user_data;
1541   apr_status_t rv;
1542
1543   SDBG(s, "start chxj_init_module()");
1544
1545   apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
1546   SDBG(s, " ");
1547   if (user_data == NULL) {
1548     SDBG(s, " ");
1549     /*
1550      * dummy user_data set.
1551      */
1552     apr_pool_userdata_set(
1553       (const void *)(1), 
1554       CHXJ_MOD_CONFIG_KEY, 
1555       apr_pool_cleanup_null, 
1556       s->process->pool);
1557     SDBG(s, "end  chxj_init_module()");
1558     return OK;
1559   }
1560
1561   ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
1562
1563   if ((rv = apr_proc_mutex_create(&global_cookie_mutex, NULL,  APR_LOCK_FCNTL, s->process->pool)) != APR_SUCCESS) {
1564     char errstr[255];
1565     SERR(s, "%s:%d end chxj_init_module(). mutex create failure.(%d:%s)",APLOG_MARK, rv,apr_strerror(rv,errstr,255));
1566     return HTTP_INTERNAL_SERVER_ERROR;
1567   }
1568
1569   SDBG(s, "end  chxj_init_module()");
1570
1571   return OK;
1572 }
1573
1574
1575 static void 
1576 chxj_child_init(apr_pool_t *UNUSED(p), server_rec *s)
1577 {
1578   apr_status_t rv;
1579   SDBG(s, "start chxj_child_init()");
1580   if ((rv = apr_proc_mutex_child_init(&global_cookie_mutex, NULL, s->process->pool)) != APR_SUCCESS) {
1581     char errstr[255];
1582     SERR(s, "%s:%d ERROR end chxj_init_module(). mutex create failure.(%d:%s)", APLOG_MARK, rv,apr_strerror(rv,errstr,255));
1583   }
1584   SDBG(s, "end   chxj_child_init()");
1585 }
1586
1587
1588 /**
1589  * A set structure of each server is generated. 
1590  * 
1591  * @param p
1592  * @param s
1593  */
1594 static void *
1595 chxj_config_server_create(apr_pool_t *p, server_rec *s)
1596 {
1597   mod_chxj_global_config *gc;
1598
1599   gc = chxj_global_config_create(p,s);
1600
1601   return gc;
1602 }
1603
1604
1605 static int
1606 chxj_translate_name(request_rec *r)
1607 {
1608   DBG(r, "REQ[%X] =======================================================================", (unsigned int)(apr_size_t)r);
1609   DBG(r, "REQ[%X] ", (unsigned int)(apr_size_t)r);
1610   DBG(r, "REQ[%X] START REQUEST (uri:[%s] args:[%s])", (unsigned int)(apr_size_t)r, r->unparsed_uri, r->args ? r->args : "");
1611   DBG(r, "REQ[%X] METHOD [%s]", TO_ADDR(r), r->method);
1612   DBG(r, "REQ[%X] ", (unsigned int)(apr_size_t)r);
1613   DBG(r, "REQ[%X] =======================================================================", (unsigned int)(apr_size_t)r);
1614 #if 0
1615   return chxj_trans_name(r);
1616 #else
1617   return DECLINED;
1618 #endif
1619 }
1620
1621
1622 static void 
1623 chxj_insert_filter(request_rec *r)
1624 {
1625   char                *user_agent;
1626   device_table        *spec;
1627   mod_chxj_config     *dconf;
1628   chxjconvrule_entry  *entryp;
1629   mod_chxj_ctx        *ctx;
1630   apr_status_t        rv;
1631   char                *contentType;
1632
1633   DBG(r, "REQ[%X] start chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1634
1635   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1636
1637   /* we get User-Agent from CHXJ_HTTP_USER_AGENT header if any */
1638   user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
1639   if (!user_agent) {
1640     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1641   }
1642
1643   contentType = (char *)apr_table_get(r->headers_in, "Content-Type");
1644   if (contentType
1645       && strncasecmp("multipart/form-data", contentType, 19) == 0) {
1646     DBG(r, "REQ[%X] detect multipart/form-data ==> no target", (unsigned int)(apr_size_t)r);
1647     DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1648     return;
1649   }
1650
1651   spec = chxj_specified_device(r, user_agent);
1652   entryp = chxj_apply_convrule(r, dconf->convrules);
1653   if (!entryp) {
1654     DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1655     return;
1656   }
1657   ctx = apr_palloc(r->pool, sizeof(*ctx));
1658   memset(ctx, 0, sizeof(*ctx));
1659   if ((rv = apr_pool_create(&ctx->pool, r->pool)) != APR_SUCCESS) {
1660     ERR(r, "%s:%d: failed: new pool create. rv:[%d]", __FILE__,__LINE__,rv);
1661     DBG(r, "REQ:[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1662     return;
1663   }
1664   ctx->entryp = entryp;
1665   ctx->spec   = spec;
1666   ctx->buffer = apr_palloc(ctx->pool, 1);
1667   ctx->buffer[0] = 0;
1668
1669   if (!entryp || (!(entryp->action & CONVRULE_ENGINE_ON_BIT) && !(entryp->action & CONVRULE_COOKIE_ONLY_BIT))) {
1670     DBG(r,"REQ[%X] EngineOff", (unsigned int)(apr_size_t)r);
1671     DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1672     return;
1673   }
1674
1675   switch(spec->html_spec_type) {
1676   case CHXJ_SPEC_Chtml_1_0:
1677   case CHXJ_SPEC_Chtml_2_0:
1678   case CHXJ_SPEC_Chtml_3_0:
1679   case CHXJ_SPEC_Chtml_4_0:
1680   case CHXJ_SPEC_Chtml_5_0:
1681   case CHXJ_SPEC_Chtml_6_0:
1682   case CHXJ_SPEC_Chtml_7_0:
1683   case CHXJ_SPEC_XHtml_Mobile_1_0:
1684   case CHXJ_SPEC_Hdml:
1685   case CHXJ_SPEC_Jhtml:
1686   case CHXJ_SPEC_Jxhtml:
1687     break;
1688
1689   default:
1690     DBG(r, "REQ[%X] end chxj_insert_filter() Unknown spec type(%d).", (unsigned int)(apr_size_t)r, spec->html_spec_type);
1691     return;
1692   }
1693
1694
1695   if (! apr_table_get(r->headers_in, "X-Chxj-Forward")) {
1696     ap_add_output_filter("chxj_output_filter", ctx, r, r->connection);
1697     DBG(r, "REQ[%X] added Output Filter", (unsigned int)(apr_size_t)r);
1698   }
1699
1700   DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1701 }
1702
1703
1704 /**
1705  * The hook is registered.
1706  *
1707  * @param p
1708  */
1709 static void 
1710 chxj_register_hooks(apr_pool_t *UNUSED(p))
1711 {
1712   ap_hook_post_config(chxj_init_module,
1713                       NULL,
1714                       NULL,
1715                       APR_HOOK_REALLY_FIRST);
1716   ap_hook_child_init(chxj_child_init, 
1717                      NULL, 
1718                      NULL, 
1719                      APR_HOOK_REALLY_FIRST);
1720   ap_register_output_filter (
1721                       "chxj_output_filter", 
1722                       chxj_output_filter, 
1723                       NULL, 
1724                       AP_FTYPE_RESOURCE);
1725   ap_hook_insert_filter(chxj_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
1726   ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1727   ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1728   ap_hook_handler(chxj_input_handler, NULL, NULL, APR_HOOK_MIDDLE);
1729   ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1730   ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_FIRST);
1731 }
1732
1733
1734 /**
1735  * A set structure according to the directory is generated. 
1736  *
1737  * @param p
1738  * @param arg
1739  */
1740 static void * 
1741 chxj_create_per_dir_config(apr_pool_t *p, char *arg) 
1742 {
1743   mod_chxj_config *conf;
1744
1745   conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1746   conf->device_data_file = NULL;
1747   conf->devices          = NULL;
1748   conf->emoji_data_file  = NULL;
1749   conf->emoji            = NULL;
1750   conf->emoji_tail       = NULL;
1751   conf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_NONE;
1752   conf->image            = CHXJ_IMG_NONE;
1753   conf->image_cache_dir  = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1754   conf->image_cache_limit = 0;
1755   conf->server_side_encoding = NULL;
1756   conf->cookie_db_dir    = NULL;
1757   conf->cookie_timeout   = 0;
1758   conf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
1759   conf->cookie_lazy_mode  = 0;
1760   conf->cookie_dbm_type  = NULL;
1761   
1762   conf->detect_device_type = CHXJ_ADD_DETECT_DEVICE_TYPE_NONE;
1763   
1764 #if defined(USE_MYSQL_COOKIE)
1765   memset((void *)&conf->mysql, 0, sizeof(mysql_t));
1766   conf->mysql.port       = MYSQL_PORT;
1767   conf->mysql.host       = NULL;
1768 #endif
1769 #if defined(USE_MEMCACHE_COOKIE)
1770   memset((void *)&conf->memcache, 0, sizeof(memcache_t));
1771   conf->memcache.host    = NULL;
1772   conf->memcache.port    = 0;
1773 #endif
1774   conf->forward_url_base = NULL;
1775   conf->forward_server_ip = NULL;
1776   conf->allowed_cookie_domain = NULL;
1777   conf->post_log              = NULL;
1778
1779   if (arg == NULL) {
1780     conf->dir                  = NULL;
1781   }
1782   else {
1783     conf->dir                  = apr_pcalloc(p, strlen(arg)+1);
1784     memset(conf->dir, 0, strlen(arg)+1);
1785     strcpy(conf->dir, arg);
1786   }
1787   conf->convrules   = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1788
1789   /* Default is copyleft */
1790   conf->image_copyright = NULL; 
1791
1792   conf->image_rewrite = CHXJ_IMG_REWRITE_NONE;
1793   conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_NONE;
1794   conf->image_rewrite_url = NULL;
1795
1796   return conf;
1797 }
1798
1799
1800 /*
1801  *  Merge per-directory CHXJ configurations
1802  */
1803 static void *
1804 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1805 {
1806   mod_chxj_config *base;
1807   mod_chxj_config *add;
1808   mod_chxj_config *mrg;
1809
1810   base = (mod_chxj_config *)basev;
1811   add  = (mod_chxj_config *)addv;
1812   mrg  = (mod_chxj_config *)apr_palloc(p, sizeof(mod_chxj_config));
1813
1814   mrg->device_data_file = NULL;
1815   mrg->devices          = NULL;
1816   mrg->emoji_data_file  = NULL;
1817   mrg->image            = CHXJ_IMG_NONE;
1818   mrg->image_cache_dir  = NULL;
1819   mrg->image_copyright  = NULL;
1820   mrg->image_cache_limit  = 0;
1821   mrg->emoji            = NULL;
1822   mrg->emoji_tail       = NULL;
1823   mrg->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_NONE;
1824   mrg->new_line_type    = NLTYPE_NIL;
1825   mrg->forward_url_base = NULL;
1826   mrg->forward_server_ip = NULL;
1827   mrg->allowed_cookie_domain = NULL;
1828   mrg->post_log         = NULL;
1829   mrg->cookie_dbm_type  = NULL;
1830   
1831   mrg->device_keys      = NULL;
1832   mrg->device_hash      = NULL;
1833
1834   mrg->dir = apr_pstrdup(p, add->dir);
1835
1836   if (! add->device_data_file) {
1837     mrg->devices = base->devices;
1838     mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1839   }
1840   else {
1841     mrg->devices = add->devices;
1842     mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1843   }
1844
1845   if (! add->emoji_data_file) {
1846     mrg->emoji = base->emoji;
1847     mrg->emoji_tail = base->emoji_tail;
1848     mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1849   }
1850   else {
1851     mrg->emoji = add->emoji;
1852     mrg->emoji_tail = add->emoji_tail;
1853     mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1854   }
1855
1856   if (add->image == CHXJ_IMG_NONE) {
1857     mrg->image = base->image;
1858   }
1859   else {
1860     mrg->image = add->image;
1861   }
1862
1863   if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) {
1864     mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1865   }
1866   else {
1867     mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1868   }
1869
1870   if (add->image_cache_limit) {
1871     mrg->image_cache_limit = add->image_cache_limit;
1872   }
1873   else {
1874     mrg->image_cache_limit = base->image_cache_limit;
1875   }
1876
1877   if (add->image_copyright) 
1878     mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1879   else
1880     mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1881
1882   if (add->server_side_encoding) {
1883     mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1884   }
1885   else 
1886   if (base->server_side_encoding) {
1887     mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1888   }
1889   else {
1890     mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1891   }
1892
1893   mrg->convrules    = apr_array_append(p, add->convrules, base->convrules);
1894
1895   if (add->cookie_db_dir) {
1896     mrg->cookie_db_dir = apr_pstrdup(p, add->cookie_db_dir);
1897   }
1898   else
1899   if (base->cookie_db_dir) {
1900     mrg->cookie_db_dir = apr_pstrdup(p, base->cookie_db_dir);
1901   }
1902   else {
1903     mrg->cookie_db_dir = NULL;
1904   }
1905
1906   if (add->cookie_timeout) {
1907     mrg->cookie_timeout   = add->cookie_timeout;
1908   }
1909   else
1910   if (base->cookie_db_dir) {
1911     mrg->cookie_timeout   = base->cookie_timeout;
1912   }
1913   else {
1914     mrg->cookie_timeout   = 0;
1915   }
1916
1917 #if defined(USE_MYSQL_COOKIE)
1918   if (add->mysql.host) {
1919     mrg->mysql.host = apr_pstrdup(p, add->mysql.host);
1920   }
1921   else if (base->mysql.host) {
1922     mrg->mysql.host = apr_pstrdup(p, base->mysql.host);
1923   }
1924   else {
1925     mrg->mysql.host = NULL;
1926   }
1927   if (add->mysql.port) {
1928     mrg->mysql.port = add->mysql.port;
1929   }
1930   else if (base->mysql.port) {
1931     mrg->mysql.port = base->mysql.port;
1932   }
1933   else {
1934     mrg->mysql.port = 0;
1935   }
1936
1937   if (add->mysql.database) {
1938     mrg->mysql.database = apr_pstrdup(p, add->mysql.database);
1939   }
1940   else if (base->mysql.database) {
1941     mrg->mysql.database = apr_pstrdup(p, base->mysql.database);
1942   }
1943   else {
1944     mrg->mysql.database = NULL;
1945   }
1946
1947   if (add->mysql.username) {
1948     mrg->mysql.username = apr_pstrdup(p, add->mysql.username);
1949   }
1950   else if (base->mysql.username) {
1951     mrg->mysql.username = apr_pstrdup(p, base->mysql.username);
1952   }
1953   else {
1954     mrg->mysql.username = NULL;
1955   }
1956
1957   if (add->mysql.password) {
1958     mrg->mysql.password = apr_pstrdup(p, add->mysql.password);
1959   }
1960   else if (base->mysql.password) {
1961     mrg->mysql.password = apr_pstrdup(p, base->mysql.password);
1962   }
1963   else {
1964     mrg->mysql.password = NULL;
1965   }
1966
1967   if (add->mysql.tablename) {
1968     mrg->mysql.tablename = apr_pstrdup(p, add->mysql.tablename);
1969   }
1970   else if (base->mysql.tablename) {
1971     mrg->mysql.tablename = apr_pstrdup(p, base->mysql.tablename);
1972   }
1973   else {
1974     mrg->mysql.tablename = NULL;
1975   }
1976
1977   if (add->mysql.socket_path) {
1978     mrg->mysql.socket_path = apr_pstrdup(p, add->mysql.socket_path);
1979   }
1980   else if (base->mysql.socket_path) {
1981     mrg->mysql.socket_path = apr_pstrdup(p, base->mysql.socket_path);
1982   }
1983   else {
1984     mrg->mysql.socket_path = NULL;
1985   }
1986
1987   if (add->mysql.charset) {
1988     mrg->mysql.charset = apr_pstrdup(p, add->mysql.charset);
1989   }
1990   else if (base->mysql.charset) {
1991     mrg->mysql.charset = apr_pstrdup(p, base->mysql.charset);
1992   }
1993   else {
1994     mrg->mysql.charset = NULL;
1995   }
1996 #endif
1997 #if defined(USE_MEMCACHE_COOKIE)
1998   if (add->memcache.host) {
1999     mrg->memcache.host = apr_pstrdup(p, add->memcache.host);
2000   }
2001   else if (base->memcache.host) {
2002     mrg->memcache.host = apr_pstrdup(p, base->memcache.host);
2003   }
2004   else {
2005     mrg->memcache.host = NULL;
2006   }
2007   if (add->memcache.port) {
2008     mrg->memcache.port = add->memcache.port;
2009   }
2010   else if (base->memcache.port) {
2011     mrg->memcache.port = base->memcache.port;
2012   }
2013   else {
2014     mrg->memcache.port = 0;
2015   }
2016 #endif
2017   if (add->cookie_store_type) {
2018     mrg->cookie_store_type = add->cookie_store_type;
2019   }
2020   else if (base->cookie_store_type) {
2021     mrg->cookie_store_type = base->cookie_store_type;
2022   }
2023   else {
2024     mrg->cookie_store_type = COOKIE_STORE_TYPE_NONE;
2025   }
2026   if (add->cookie_lazy_mode) {
2027     mrg->cookie_lazy_mode = add->cookie_lazy_mode;
2028   }
2029   else if (base->cookie_lazy_mode) {
2030     mrg->cookie_lazy_mode = base->cookie_lazy_mode;
2031   }
2032   else {
2033     mrg->cookie_lazy_mode = 0;
2034   }
2035   if (add->new_line_type) {
2036     mrg->new_line_type = add->new_line_type;
2037   }
2038   else if (base->new_line_type) {
2039     mrg->new_line_type = base->new_line_type;
2040   }
2041   else {
2042     mrg->new_line_type = NLTYPE_NIL;
2043   }
2044
2045   if (add->forward_url_base) {
2046     mrg->forward_url_base = add->forward_url_base;
2047   }
2048   else if (base->forward_url_base) {
2049     mrg->forward_url_base = base->forward_url_base;
2050   }
2051
2052   if (add->allowed_cookie_domain) {
2053     mrg->allowed_cookie_domain = add->allowed_cookie_domain;
2054   }
2055   else {
2056     mrg->allowed_cookie_domain = base->allowed_cookie_domain;
2057   }
2058   if (add->post_log) {
2059     mrg->post_log = add->post_log;
2060   }
2061   else {
2062     mrg->post_log = base->post_log;
2063   }
2064   if (add->cookie_dbm_type) {
2065     mrg->cookie_dbm_type = add->cookie_dbm_type;
2066   }
2067   else {
2068     mrg->cookie_dbm_type = base->cookie_dbm_type;
2069   }
2070   
2071   if (add->imode_emoji_color == CHXJ_IMODE_EMOJI_COLOR_NONE) {
2072     mrg->imode_emoji_color = base->imode_emoji_color;
2073   }
2074   else {
2075     mrg->imode_emoji_color = add->imode_emoji_color;
2076   }
2077   
2078   if (add->detect_device_type == CHXJ_ADD_DETECT_DEVICE_TYPE_NONE) {
2079     mrg->detect_device_type = base->detect_device_type;
2080   }
2081   else {
2082     mrg->detect_device_type = add->detect_device_type;
2083   }
2084   
2085   if (add->device_keys) {
2086     mrg->device_keys = add->device_keys;
2087   }
2088   else{
2089     mrg->device_keys = base->device_keys;
2090   }
2091   
2092   if (add->device_hash) {
2093     mrg->device_hash = add->device_hash;
2094   }
2095   else{
2096     mrg->device_hash = base->device_hash;
2097   }
2098   
2099   if (add->image_rewrite == CHXJ_IMG_REWRITE_NONE){
2100     mrg->image_rewrite = base->image_rewrite;
2101   }
2102   else{
2103     mrg->image_rewrite = add->image_rewrite;
2104   }
2105
2106   if (add->image_rewrite_url) {
2107     mrg->image_rewrite_url = add->image_rewrite_url;
2108   }
2109   else{
2110     mrg->image_rewrite_url = base->image_rewrite_url;
2111   }
2112
2113   if (add->image_rewrite_mode == CHXJ_IMG_REWRITE_MODE_NONE){
2114     mrg->image_rewrite_mode = base->image_rewrite_mode;
2115   }
2116   else{
2117     mrg->image_rewrite_mode = add->image_rewrite_mode;
2118   }
2119
2120   return mrg;
2121 }
2122
2123
2124 static int
2125 chxj_command_parse_take5(
2126   const char *arg, 
2127   char       **prm1, 
2128   char       **prm2, 
2129   char       **prm3, 
2130   char       **prm4, 
2131   char       **prm5)
2132 {
2133   int  isquoted;
2134   char *strp;
2135
2136   strp = (char *)arg;
2137
2138   for (;*strp == ' '||*strp == '\t'; strp++) ;
2139
2140   isquoted = 0; 
2141   if (*strp == '"') { 
2142     isquoted = 1;
2143     strp++;
2144   }
2145
2146   *prm1 = strp;
2147
2148   for (; *strp != '\0'; strp++) {
2149     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2150     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2151       strp++;
2152       continue;
2153     }
2154
2155     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2156     ||  (isquoted  && *strp == '"'))
2157       break;
2158   }
2159
2160   if (! *strp) {
2161     *prm2 = strp;
2162     *prm3 = strp;
2163     *prm4 = strp;
2164     *prm5 = strp;
2165     return 1;
2166   }
2167
2168   *strp++ = '\0';
2169
2170   for (;*strp == ' '||*strp == '\t'; strp++) ;
2171
2172   isquoted = 0; 
2173   if (*strp == '"') { 
2174     isquoted = 1;
2175     strp++;
2176   }
2177
2178   *prm2 = strp;
2179   for (; *strp != '\0'; strp++) {
2180     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2181     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2182       strp++;
2183       continue;
2184     }
2185
2186     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2187     ||  (isquoted  && *strp == '"'))
2188       break;
2189   }
2190
2191   if (! *strp) {
2192     *prm3 = strp;
2193     *prm4 = strp;
2194     *prm5 = strp;
2195     return 0;
2196   }
2197
2198   *strp++ = '\0';
2199
2200   for (;*strp == ' '||*strp == '\t'; strp++);
2201
2202   isquoted = 0; 
2203   if (*strp == '"') { 
2204     isquoted = 1;
2205     strp++;
2206   }
2207   *prm3 = strp;
2208   for (; *strp != '\0'; strp++) {
2209     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2210     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2211       strp++;
2212       continue;
2213     }
2214
2215     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2216     ||  (isquoted  && *strp == '"'))
2217       break;
2218   }
2219
2220   if (! *strp) {
2221     *prm4 = strp;
2222     *prm5 = strp;
2223     return 0;
2224   }
2225
2226   *strp++ = '\0';
2227
2228   for (;*strp == ' '||*strp == '\t'; strp++);
2229
2230   isquoted = 0; 
2231   if (*strp == '"') { 
2232     isquoted = 1;
2233     strp++;
2234   }
2235   *prm4 = strp;
2236   for (; *strp != '\0'; strp++) {
2237     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2238     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2239       strp++;
2240       continue;
2241     }
2242
2243     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2244     ||  (isquoted  && *strp == '"'))
2245       break;
2246   }
2247
2248   if (! *strp) {
2249     *prm5 = strp;
2250     return 0;
2251   }
2252
2253   *strp++ = '\0';
2254
2255   for (;*strp == ' '||*strp == '\t'; strp++);
2256
2257   isquoted = 0; 
2258   if (*strp == '"') { 
2259     isquoted = 1;
2260     strp++;
2261   }
2262   *prm5 = strp;
2263   for (; *strp != '\0'; strp++) {
2264     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2265     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2266       strp++;
2267       continue;
2268     }
2269
2270     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2271     ||  (isquoted  && *strp == '"'))
2272       break;
2273   }
2274   *strp = '\0';
2275
2276   return 0;
2277 }
2278
2279
2280 /**
2281  * The device definition file is loaded. 
2282  *
2283  * @param arg     [i]   The name of the device definition file is specified.
2284  * @param mconfig [i/o] The pointer to a set structure is specified. 
2285  * @param parms   [i]   
2286  */
2287 static const char * 
2288 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char *arg) 
2289 {
2290   mod_chxj_config  *conf;
2291   Doc              doc;
2292
2293   doc.r = NULL;
2294
2295   if (strlen(arg) > 256) 
2296     return "mod_chxj: device data filename too long.";
2297
2298   conf = (mod_chxj_config *)mconfig;
2299   conf->device_data_file = apr_pstrdup(parms->pool, arg);
2300
2301   qs_init_malloc(&doc);
2302   qs_init_root_node(&doc);
2303
2304   qs_parse_file((Doc *)&doc, (const char *)arg);
2305   chxj_load_device_data(&doc,parms->pool, conf);
2306   qs_all_free(&doc, QX_LOGMARK);
2307
2308   return NULL;
2309 }
2310
2311
2312 /**
2313  * Device definition information is loaded. 
2314  *
2315  * @param parms 
2316  * @param arg     [i]   The name of the device definition file is specified. 
2317  * @param mconfig [i/o] The pointer to a set structure is specified. 
2318  * @return 
2319  */
2320 static const char * 
2321 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char *arg) 
2322 {
2323   mod_chxj_config *conf;
2324   char            *rtn;
2325   Doc              doc;
2326
2327   doc.r = NULL;
2328
2329
2330   if (strlen(arg) > 256) 
2331     return "mod_chxj: emoji data filename too long.";
2332
2333   conf = (mod_chxj_config *)mconfig;
2334   conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
2335   qs_init_malloc(&doc);
2336   qs_init_root_node(&doc);
2337
2338   qs_parse_file((Doc *)&doc, (const char *)arg);
2339
2340   rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
2341
2342   qs_all_free(&doc, QX_LOGMARK);
2343
2344
2345   return rtn;
2346 }
2347
2348
2349 static const char * 
2350 cmd_set_image_engine(cmd_parms * UNUSED(parms), void *mconfig, const char *arg) 
2351 {
2352   mod_chxj_config *conf;
2353   Doc              doc;
2354
2355   doc.r = NULL;
2356
2357   if (strlen(arg) > 256) 
2358     return "image uri is too long.";
2359
2360   conf = (mod_chxj_config*)mconfig;
2361   if (strcasecmp("ON", arg) == 0) {
2362     conf->image = CHXJ_IMG_ON;
2363   }
2364   else {
2365     conf->image = CHXJ_IMG_OFF;
2366   }
2367
2368   return NULL;
2369 }
2370
2371
2372 static const char * 
2373 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char *arg) 
2374 {
2375   mod_chxj_config *conf;
2376   Doc              doc;
2377
2378   doc.r = NULL;
2379
2380   if (strlen(arg) > 256) 
2381     return "cache dir name is too long.";
2382   
2383   apr_finfo_t info;
2384   apr_status_t res = apr_stat(&info,arg,APR_FINFO_TYPE,parms->pool);
2385   if(res != APR_SUCCESS){
2386     return apr_psprintf(parms->pool,"ChxjImageCacheDir [%s]: not found ",arg);
2387   }
2388   else{
2389     if(info.filetype != APR_DIR){
2390       return apr_psprintf(parms->pool,"ChxjImageCacheDir [%s]: is not directory ",arg);
2391     }
2392   }
2393
2394   conf = (mod_chxj_config *)mconfig;
2395   conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
2396
2397   return NULL;
2398 }
2399
2400
2401 static const char * 
2402 cmd_set_image_cache_limit(cmd_parms *parms, void *mconfig, const char *arg) 
2403 {
2404   mod_chxj_config *conf;
2405   Doc              doc;
2406
2407   doc.r = NULL;
2408
2409   if (strlen(arg) > IMAGE_CACHE_LIMIT_FMT_LEN) 
2410     return "cache size is too long.";
2411
2412   conf = (mod_chxj_config *)mconfig;
2413   errno = 0;
2414   /* 
2415    * I use strtol function because strtoul is not portable function. 
2416    */
2417   conf->image_cache_limit = (unsigned long)strtol(arg, NULL, 10);
2418   switch (errno) {
2419   case EINVAL:
2420     return apr_psprintf(parms->pool, "ChxjImageCacheLimit invalid value [%s] errno:[%d]", arg, errno);
2421   case ERANGE:
2422     return apr_psprintf(parms->pool, "ChxjImageCacheLimit Out of range [%s] errno:[%d]", arg, errno);
2423   default:
2424     break;
2425   }
2426   return NULL;
2427 }
2428
2429
2430 static const char * 
2431 cmd_set_image_copyright(cmd_parms *parms, void *mconfig, const char *arg) 
2432 {
2433   mod_chxj_config *conf;
2434   Doc              doc;
2435
2436   doc.r = NULL;
2437
2438   if (strlen(arg) > 256) 
2439     return "Copyright Flag is too long.";
2440
2441   conf = (mod_chxj_config *)mconfig;
2442   conf->image_copyright = apr_pstrdup(parms->pool, arg);
2443
2444   return NULL;
2445 }
2446
2447
2448 static const char *
2449 cmd_convert_rule(cmd_parms *cmd, void *mconfig, const char *arg)
2450 {
2451   int                 mode;
2452   ap_regex_t          *regexp;
2453   mod_chxj_config     *dconf;
2454   chxjconvrule_entry  *newrule;
2455   char                *prm1;
2456   char                *prm2;
2457   char                *prm3;
2458   char                *prm4;
2459   char                *prm5;
2460   char                *pstate;
2461   char                *action;
2462   char                *pp;
2463
2464   dconf = (mod_chxj_config *)mconfig;
2465
2466   if (strlen(arg) > 4096) 
2467     return "mod_chxj: ChxjConvertRule: is too long.";
2468
2469   dconf = (mod_chxj_config *)mconfig;
2470   if (dconf->convrules == NULL)
2471     dconf->convrules   = apr_array_make(cmd->pool, 
2472                                         2, 
2473                                         sizeof(chxjconvrule_entry));
2474
2475   newrule = apr_array_push(dconf->convrules);
2476
2477   newrule->flags  = 0;
2478   newrule->action = 0;
2479
2480   if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
2481     return "ChxjConvertRule: bad argument line";
2482
2483   newrule->pattern = apr_pstrdup(cmd->pool, prm1);
2484
2485   /* Parse action */
2486   for (;;) {
2487     if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
2488       break;
2489     prm2 = NULL;
2490     switch(*action) {
2491     case 'e':
2492     case 'E':
2493       if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
2494         newrule->action |= CONVRULE_ENGINE_ON_BIT;
2495       }
2496       else
2497       if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
2498         newrule->action |= CONVRULE_ENGINE_OFF_BIT;
2499       }
2500       else
2501       if (strcasecmp(CONVRULE_EMOJI_ONLY_CMD, action) == 0) {
2502         newrule->action |= CONVRULE_EMOJI_ONLY_BIT;
2503       }
2504       else
2505       if (strcasecmp(CONVRULE_ENVINFO_ONLY_CMD, action) == 0) {
2506         newrule->action |= CONVRULE_ENVINFO_ONLY_BIT;
2507       }
2508       break;
2509
2510     case 'C':
2511     case 'c':
2512       if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
2513         newrule->action |= CONVRULE_COOKIE_ON_BIT;
2514       }
2515       else if (strcasecmp(CONVRULE_COOKIE_OFF_CMD, action) == 0) {
2516         newrule->action &= (0xffffffff ^ CONVRULE_COOKIE_ON_BIT);
2517       }
2518       else if (strcasecmp(CONVRULE_CSS_ON_CMD, action) == 0) {
2519         newrule->action |= CONVRULE_CSS_ON_BIT;
2520       }
2521       else if (strcasecmp(CONVRULE_CSS_OFF_CMD, action) == 0) {
2522         newrule->action &= (0xffffffff ^ CONVRULE_CSS_ON_BIT);
2523       }
2524       else if (strcasecmp(CONVRULE_COOKIE_ONLY_CMD, action) == 0) {
2525         newrule->action |= CONVRULE_COOKIE_ONLY_BIT;
2526       }
2527       break;
2528
2529     case 'J':
2530     case 'j':
2531       if (strcasecmp(CONVRULE_JRCONV_OFF_CMD, action) == 0) {
2532         newrule->action |= CONVRULE_JRCONV_OFF_BIT;
2533       }
2534       break;
2535
2536     case 'N':
2537     case 'n':
2538       if (strcasecmp(CONVRULE_NOCACHE_ON_CMD, action) == 0) {
2539         newrule->action |= CONVRULE_NOCACHE_ON_BIT;
2540       }
2541       break;
2542
2543     case 'Q':
2544     case 'q':
2545       if (strcasecmp(CONVRULE_QSCONV_OFF_CMD, action) == 0) {
2546         newrule->action |= CONVRULE_QSCONV_OFF_BIT;
2547       }
2548       break;
2549
2550     case 'Z':
2551     case 'z':
2552       if (strcasecmp(CONVRULE_Z2H_ON_CMD, action) == 0) {
2553         newrule->action |= CONVRULE_Z2H_ON_BIT;
2554       }
2555       else
2556       if (strcasecmp(CONVRULE_Z2H_OFF_CMD, action) == 0) {
2557         newrule->action |= CONVRULE_Z2H_OFF_BIT;
2558       }
2559       else
2560       if (strcasecmp(CONVRULE_Z2H_ALPHA_ON_CMD, action) == 0) {
2561         newrule->action |= CONVRULE_Z2H_ALPHA_ON_BIT;
2562       }
2563       else
2564       if (strcasecmp(CONVRULE_Z2H_ALPHA_OFF_CMD, action) == 0) {
2565         newrule->action |= CONVRULE_Z2H_ALPHA_OFF_BIT;
2566       }
2567       else
2568       if (strcasecmp(CONVRULE_Z2H_NUM_ON_CMD, action) == 0) {
2569         newrule->action |= CONVRULE_Z2H_NUM_ON_BIT;
2570       }
2571       else
2572       if (strcasecmp(CONVRULE_Z2H_NUM_OFF_CMD, action) == 0) {
2573         newrule->action |= CONVRULE_Z2H_NUM_OFF_BIT;
2574       }
2575       else
2576       if (strcasecmp(CONVRULE_Z2H_ALL_ON_CMD, action) == 0) {
2577         newrule->action |= CONVRULE_Z2H_ON_BIT | CONVRULE_Z2H_ALPHA_ON_BIT | CONVRULE_Z2H_NUM_ON_BIT;
2578       }
2579       else
2580       if (strcasecmp(CONVRULE_Z2H_NUM_OFF_CMD, action) == 0) {
2581         newrule->action |= CONVRULE_Z2H_OFF_BIT | CONVRULE_Z2H_ALPHA_OFF_BIT | CONVRULE_Z2H_NUM_OFF_BIT;
2582       }
2583       break;
2584
2585     default:
2586       break;
2587     }
2588   }
2589   
2590   pp = prm1;
2591   if (*pp == '!') {
2592     newrule->flags |= CONVRULE_FLAG_NOTMATCH;
2593     pp++;
2594   }
2595
2596   mode = AP_REG_EXTENDED;
2597   if ((regexp = ap_pregcomp((apr_pool_t *)cmd->pool, (const char *)pp, mode)) == NULL)
2598     return "RewriteRule: cannot compile regular expression ";
2599
2600   newrule->regexp = regexp;
2601   if (*prm3)
2602     newrule->encoding = apr_pstrdup(cmd->pool, prm3);
2603   else
2604     newrule->encoding = apr_pstrdup(cmd->pool, "none");
2605
2606   newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
2607   if (*prm4)
2608     if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
2609       newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
2610
2611   newrule->user_agent = NULL;
2612   if (*prm5)
2613     newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
2614     
2615   return NULL;
2616 }
2617
2618
2619 static const char *
2620 cmd_set_cookie_dir(
2621   cmd_parms   *cmd, 
2622   void        *mconfig, 
2623   const char  *arg)
2624 {
2625   mod_chxj_config *dconf;
2626
2627
2628   if (strlen(arg) > 4096) 
2629     return "mod_chxj: ChxjCookieDir is too long.";
2630
2631   dconf = (mod_chxj_config *)mconfig;
2632
2633   dconf->cookie_db_dir = apr_pstrdup(cmd->pool, arg);
2634
2635   return NULL;
2636 }
2637
2638
2639 static const char *
2640 cmd_set_cookie_timeout(
2641   cmd_parms   *UNUSED(cmd), 
2642   void        *mconfig, 
2643   const char  *arg)
2644 {
2645   mod_chxj_config *dconf;
2646
2647   if (strlen(arg) > 4096) 
2648     return "mod_chxj: ChxjCookieTimeout is too long.";
2649
2650   if (chxj_chk_numeric(arg) != 0)
2651     return "mod_chxj: ChxjCookieTimeout is not numeric.";
2652
2653   dconf = (mod_chxj_config *)mconfig;
2654
2655   dconf->cookie_timeout = atoi(arg);
2656
2657   return NULL;
2658 }
2659
2660
2661 #if defined(USE_MYSQL_COOKIE)
2662 static const char *
2663 cmd_set_cookie_mysql_database(
2664   cmd_parms   *cmd, 
2665   void        *mconfig, 
2666   const char  *arg)
2667 {
2668   mod_chxj_config  *dconf;
2669
2670   if (strlen(arg) > 255) 
2671     return "mod_chxj: ChxjCookieMysqlDatabase is too long.";
2672
2673   dconf = (mod_chxj_config *)mconfig;
2674
2675   dconf->mysql.database = apr_pstrdup(cmd->pool, arg);
2676
2677   return NULL;
2678 }
2679
2680
2681 static const char *
2682 cmd_set_cookie_mysql_username(
2683   cmd_parms   *cmd, 
2684   void        *mconfig, 
2685   const char  *arg)
2686 {
2687   mod_chxj_config  *dconf;
2688
2689   if (strlen(arg) > 255) 
2690     return "mod_chxj: ChxjCookieMysqlUsername is too long.";
2691
2692   dconf = (mod_chxj_config *)mconfig;
2693
2694   dconf->mysql.username = apr_pstrdup(cmd->pool, arg);
2695
2696   return NULL;
2697 }
2698
2699
2700 static const char *
2701 cmd_set_cookie_mysql_password(
2702   cmd_parms   *cmd, 
2703   void        *mconfig, 
2704   const char  *arg)
2705 {
2706   mod_chxj_config  *dconf;
2707
2708   if (strlen(arg) > 255) 
2709     return "mod_chxj: ChxjCookieMysqlPassword is too long.";
2710
2711   dconf = (mod_chxj_config *)mconfig;
2712
2713   dconf->mysql.password = apr_pstrdup(cmd->pool, arg);
2714
2715   return NULL;
2716 }
2717
2718
2719 static const char *
2720 cmd_set_cookie_mysql_table_name(
2721   cmd_parms   *cmd, 
2722   void        *mconfig, 
2723   const char  *arg)
2724 {
2725   mod_chxj_config  *dconf;
2726
2727   if (strlen(arg) > 255) 
2728     return "mod_chxj: ChxjCookieMysqlTableName is too long.";
2729
2730   dconf = (mod_chxj_config *)mconfig;
2731
2732   dconf->mysql.tablename = apr_pstrdup(cmd->pool, arg);
2733
2734   return NULL;
2735 }
2736
2737 static const char *
2738 cmd_set_cookie_mysql_port(
2739   cmd_parms   *UNUSED(cmd), 
2740   void        *mconfig, 
2741   const char  *arg)
2742 {
2743   mod_chxj_config *dconf;
2744
2745   if (strlen(arg) > 255) 
2746     return "mod_chxj: ChxjCookieMysqlPort is too long.";
2747
2748   dconf = (mod_chxj_config *)mconfig;
2749
2750   if (chxj_chk_numeric(arg) != 0)
2751     return "mod_chxj: ChxjCookieMysqlPort is not numeric.";
2752
2753   dconf = (mod_chxj_config *)mconfig;
2754
2755   dconf->mysql.port = chxj_atoi(arg);
2756
2757   return NULL;
2758 }
2759
2760
2761 static const char *
2762 cmd_set_cookie_mysql_host(
2763   cmd_parms   *cmd, 
2764   void        *mconfig, 
2765   const char  *arg)
2766 {
2767   mod_chxj_config  *dconf;
2768
2769   if (strlen(arg) > 255) 
2770     return "mod_chxj: ChxjCookieMysqlHost is too long.";
2771
2772   dconf = (mod_chxj_config *)mconfig;
2773
2774   dconf->mysql.host = apr_pstrdup(cmd->pool, arg);
2775
2776   return NULL;
2777 }
2778
2779
2780 static const char *
2781 cmd_set_cookie_mysql_socket_path(
2782   cmd_parms   *cmd, 
2783   void        *mconfig, 
2784   const char  *arg)
2785 {
2786   mod_chxj_config  *dconf;
2787
2788   if (strlen(arg) > 4096) 
2789     return "mod_chxj: ChxjCookieMysqlSocketPath is too long.";
2790
2791   dconf = (mod_chxj_config *)mconfig;
2792
2793   dconf->mysql.socket_path = apr_pstrdup(cmd->pool, arg);
2794
2795   return NULL;
2796 }
2797
2798
2799 static const char *
2800 cmd_set_cookie_mysql_charset(
2801   cmd_parms   *cmd, 
2802   void        *mconfig, 
2803   const char  *arg)
2804 {
2805   mod_chxj_config  *dconf;
2806
2807   if (strlen(arg) > 255) 
2808     return "mod_chxj: ChxjCookieMysqlCharset is too long.";
2809
2810   dconf = (mod_chxj_config *)mconfig;
2811
2812   dconf->mysql.charset = apr_pstrdup(cmd->pool, arg);
2813
2814   return NULL;
2815 }
2816 #endif
2817 #if defined(USE_MEMCACHE_COOKIE)
2818 static const char *
2819 cmd_set_cookie_memcache_port(
2820   cmd_parms   *UNUSED(cmd), 
2821   void        *mconfig, 
2822   const char  *arg)
2823 {
2824   mod_chxj_config *dconf;
2825
2826   if (strlen(arg) > 255) 
2827     return "mod_chxj: ChxjCookieMemcachePort is too long.";
2828
2829   dconf = (mod_chxj_config *)mconfig;
2830
2831   if (chxj_chk_numeric(arg) != 0)
2832     return "mod_chxj: ChxjCookieMemcachePort is not numeric.";
2833
2834   dconf = (mod_chxj_config *)mconfig;
2835
2836   dconf->memcache.port = (apr_port_t)chxj_atoi(arg);
2837
2838   return NULL;
2839 }
2840
2841
2842 static const char *
2843 cmd_set_cookie_memcache_host(
2844   cmd_parms   *cmd, 
2845   void        *mconfig, 
2846   const char  *arg)
2847 {
2848   mod_chxj_config  *dconf;
2849
2850   if (strlen(arg) > 255) 
2851     return "mod_chxj: ChxjCookieMemcacheHost is too long.";
2852
2853   dconf = (mod_chxj_config *)mconfig;
2854
2855   dconf->memcache.host = apr_pstrdup(cmd->pool, arg);
2856
2857   return NULL;
2858 }
2859 #endif
2860
2861 static const char *
2862 cmd_set_cookie_lazy_mode(
2863   cmd_parms   *UNUSED(cmd), 
2864   void        *mconfig, 
2865   const char  *arg)
2866 {
2867   mod_chxj_config  *dconf;
2868
2869   if (strlen(arg) > 255) 
2870     return "mod_chxj: ChxjCookieLazyMode is too long.";
2871
2872   dconf = (mod_chxj_config *)mconfig;
2873
2874   if (strcasecmp("TRUE",arg) == 0) {
2875     dconf->cookie_lazy_mode = COOKIE_LAZY_ON;
2876   }
2877   else {
2878     dconf->cookie_lazy_mode = COOKIE_LAZY_OFF;
2879   }
2880
2881   return NULL;
2882 }
2883
2884 static const char *
2885 cmd_set_cookie_store_type(
2886   cmd_parms   *UNUSED(cmd), 
2887   void        *mconfig, 
2888   const char  *arg)
2889 {
2890   mod_chxj_config  *dconf;
2891
2892   if (strlen(arg) > 255) 
2893     return "mod_chxj: ChxjCookieStoreType is too long.";
2894
2895   dconf = (mod_chxj_config *)mconfig;
2896
2897   if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_DBM, arg) == 0) {
2898     dconf->cookie_store_type = COOKIE_STORE_TYPE_DBM;
2899   }
2900   else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MYSQL, arg) == 0) {
2901     dconf->cookie_store_type = COOKIE_STORE_TYPE_MYSQL;
2902   }
2903   else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MEMCACHE, arg) == 0) {
2904     dconf->cookie_store_type = COOKIE_STORE_TYPE_MEMCACHE;
2905   }
2906   else {
2907     dconf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
2908   }
2909
2910   return NULL;
2911 }
2912
2913 static const char *
2914 cmd_set_forward_url_base(
2915   cmd_parms   *cmd,
2916   void        *mconfig,
2917   const char  *arg)
2918 {
2919  mod_chxj_config *dconf;
2920
2921   if (strlen(arg) > 255)
2922     return "mod_chxj: ChxjForwardUrlBase is too long.";
2923
2924   dconf = (mod_chxj_config *)mconfig;
2925
2926   dconf->forward_url_base = apr_pstrdup(cmd->pool, arg);
2927
2928   return NULL;
2929 }
2930
2931 static const char *
2932 cmd_set_forward_server_ip(
2933   cmd_parms   *cmd,
2934   void        *mconfig,
2935   const char  *arg)
2936 {
2937   mod_chxj_config *dconf;
2938
2939   if (strlen(arg) > 255)
2940     return "mod_chxj: ChxjForwardServerIp is too long.";
2941
2942   dconf = (mod_chxj_config *)mconfig;
2943
2944   dconf->forward_server_ip = apr_pstrdup(cmd->pool, arg);
2945
2946   return NULL;
2947 }
2948
2949 static const char *
2950 cmd_allowed_cookie_domain(
2951   cmd_parms   *cmd,
2952   void        *mconfig,
2953   const char  *arg)
2954 {
2955   mod_chxj_config *dconf;
2956
2957   if (strlen(arg) > 255)
2958     return "mod_chxj: ChxjAllowedCookieDomain is too long.";
2959
2960   dconf = (mod_chxj_config *)mconfig;
2961
2962   dconf->allowed_cookie_domain = apr_pstrdup(cmd->pool, arg);
2963
2964   return NULL;
2965 }
2966
2967 static const char *
2968 cmd_set_new_line_type(
2969   cmd_parms   *UNUSED(cmd), 
2970   void        *mconfig, 
2971   const char  *arg)
2972 {
2973   mod_chxj_config  *dconf;
2974   if (strlen(arg) > 255)
2975     return "mod_chxj: ChxjNewLineType is too long.";
2976
2977   dconf = (mod_chxj_config *)mconfig;
2978
2979   if (strcasecmp(CHXJ_NEW_LINE_TYPE_CRLF, arg) == 0) {
2980     dconf->new_line_type = NLTYPE_CRLF;
2981   }
2982   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_LF, arg) == 0) {
2983     dconf->new_line_type = NLTYPE_LF;
2984   }
2985   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_CR, arg) == 0) {
2986     dconf->new_line_type = NLTYPE_CR;
2987   }
2988   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_NONE, arg) == 0) {
2989     dconf->new_line_type = NLTYPE_NONE;
2990   }
2991   else {
2992     return "mod_chxj: invalid value (ChxjNewLineType)";
2993   }
2994   return NULL;
2995 }
2996
2997 static const char *
2998 cmd_post_log_env(
2999   cmd_parms   *cmd, 
3000   void        *mconfig, 
3001   const char  *arg)
3002 {
3003   mod_chxj_config  *dconf;
3004   if (strlen(arg) > 255)
3005     return "mod_chxj: ChxjPostLogEnv is too long.";
3006
3007   dconf = (mod_chxj_config *)mconfig;
3008
3009   dconf->post_log = apr_pstrdup(cmd->pool, arg);
3010
3011   return NULL;
3012 }
3013
3014 static const char *
3015 cmd_cookie_dbm_type(
3016   cmd_parms   *cmd, 
3017   void        *mconfig, 
3018   const char  *arg)
3019 {
3020   mod_chxj_config  *dconf;
3021   if (strlen(arg) > 255)
3022     return "mod_chxj: ChxjCookieDbmType is too long.";
3023
3024   dconf = (mod_chxj_config *)mconfig;
3025
3026   dconf->cookie_dbm_type = apr_pstrdup(cmd->pool, arg);
3027
3028   return NULL;
3029 }
3030
3031 static const char *
3032 cmd_imode_emoji_color(
3033   cmd_parms   *cmd, 
3034   void        *mconfig, 
3035   const char  *arg)
3036 {
3037   mod_chxj_config  *dconf;
3038   
3039   if (strlen(arg) > 256) 
3040     return "imode emoji color is too long.";
3041
3042   dconf = (mod_chxj_config *)mconfig;
3043   if (strcasecmp("ON", arg) == 0) {
3044     dconf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_ON;
3045   }
3046   else if(strcasecmp("AUTO",arg) == 0) {
3047     dconf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_AUTO;
3048   }
3049   else {
3050     dconf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_OFF;
3051   }
3052   
3053   return NULL;
3054 }
3055
3056 static const char *
3057 cmd_add_device_data_tsv(cmd_parms *parms, void *mconfig, const char *arg) 
3058 {
3059   mod_chxj_config  *conf;
3060   
3061   if (strlen(arg) > 256) 
3062     return "mod_chxj: device tsv filename too long.";
3063
3064   conf = (mod_chxj_config *)mconfig;
3065   
3066   conf->detect_device_type = CHXJ_ADD_DETECT_DEVICE_TYPE_TSV;
3067   
3068   apr_finfo_t info;
3069   apr_status_t res = apr_stat(&info,arg,APR_FINFO_TYPE,parms->pool);
3070   if(res != APR_SUCCESS){
3071     return apr_psprintf(parms->pool,"ChxjDeviceTSV [%s]: not found ",arg);
3072   }
3073   else{
3074     if(info.filetype != APR_REG ){
3075       return apr_psprintf(parms->pool,"ChxjDeviceTSV [%s]: is not file ",arg);
3076     }
3077   }
3078   apr_file_t *fp;
3079   apr_file_open(&fp, arg, APR_READ|APR_BUFFERED, APR_OS_DEFAULT, parms->pool);
3080   
3081   chxj_load_device_tsv_data(fp,parms->pool,conf);
3082   
3083   apr_file_close(fp);
3084   return NULL;
3085 }
3086
3087 static const char *
3088 cmd_image_rewrite(cmd_parms *parms, void *mconfig, const char *arg)
3089 {
3090   mod_chxj_config *conf;
3091   if (strlen(arg) > 256){
3092     return "mod_chxj: set rewrite too long.";
3093   }
3094   conf = (mod_chxj_config *)mconfig;
3095   if (strcasecmp("ON", arg) == 0) {
3096     conf->image_rewrite = CHXJ_IMG_REWRITE_ON;
3097   }
3098   else if(strcasecmp("OFF",arg) == 0) {
3099     conf->image_rewrite = CHXJ_IMG_REWRITE_OFF;
3100   }
3101   else {
3102     conf->image_rewrite = CHXJ_IMG_REWRITE_NONE;
3103   }
3104   return NULL;
3105 }
3106
3107 static const char *
3108 cmd_image_rewrite_url(cmd_parms *parms, void *mconfig, const char *arg)
3109 {
3110   mod_chxj_config *conf;
3111   if (strlen(arg) > 256){
3112     return "mod_chxj: set rewrite url too long.";
3113   }
3114   conf = (mod_chxj_config *)mconfig;
3115   conf->image_rewrite_url = apr_pstrdup(parms->pool, arg);;
3116   return NULL;
3117 }
3118
3119 static const char *
3120 cmd_image_rewrite_mode(cmd_parms *parms, void *mconfig, const char *arg)
3121 {
3122   mod_chxj_config *conf;
3123   if (strlen(arg) > 256){
3124     return "mod_chxj: set rewrite mode is too long.";
3125   }
3126
3127   conf = (mod_chxj_config *)mconfig;
3128   if (strcasecmp("all",arg) == 0) {
3129     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_ALL;
3130   }
3131   else if (strcasecmp("user",arg) == 0) {
3132     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_USER;
3133   }
3134   else if (strcasecmp("tag",arg) == 0) {
3135     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_USER;
3136   }
3137   else{
3138     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_NONE;
3139   }
3140   return NULL;
3141 }
3142
3143 static const command_rec cmds[] = {
3144   AP_INIT_TAKE1(
3145     "ChxjLoadDeviceData",
3146     cmd_load_device_data,
3147     NULL,
3148     OR_ALL,
3149     "Load Device Data"),
3150   AP_INIT_TAKE1(
3151     "ChxjLoadEmojiData",
3152     cmd_load_emoji_data,
3153     NULL,
3154     OR_ALL,
3155     "Load Emoji Data"),
3156   AP_INIT_TAKE1(
3157     "ChxjImageEngine",
3158     cmd_set_image_engine,
3159     NULL,
3160     OR_ALL,
3161     "Convert Target URI"),
3162   AP_INIT_TAKE1(
3163     "ChxjImageCacheDir",
3164     cmd_set_image_cache_dir,
3165     NULL,
3166     OR_ALL,
3167     "Image Cache Directory"),
3168   AP_INIT_TAKE1(
3169     "ChxjImageCacheLimit",
3170     cmd_set_image_cache_limit,
3171     NULL,
3172     OR_ALL,
3173     "Image Cache Limit"),
3174   AP_INIT_TAKE1(
3175     "ChxjImageCopyright",
3176     cmd_set_image_copyright,
3177     NULL,
3178     OR_ALL,
3179     "Copyright Flag"),
3180   AP_INIT_RAW_ARGS(
3181     "ChxjConvertRule",
3182     cmd_convert_rule,
3183     NULL, 
3184     OR_FILEINFO,
3185     "an URL-applied regexp-pattern and a substitution URL"),
3186   AP_INIT_TAKE1(
3187     "ChxjCookieDir",
3188     cmd_set_cookie_dir,
3189     NULL,
3190     OR_ALL,
3191     "save cookie.db directory."),
3192   AP_INIT_TAKE1(
3193     "ChxjCookieTimeout",
3194     cmd_set_cookie_timeout,
3195     NULL,
3196     OR_ALL,
3197     "The compulsion time-out time of the cookie is specified. "),
3198   AP_INIT_TAKE1(
3199     "ChxjCookieStoreType",
3200     cmd_set_cookie_store_type,
3201     NULL,
3202     OR_ALL,
3203     "It specifies preserving of the cookie ahead. (DBM/MYSQL/MEMCACHE)"),
3204   AP_INIT_TAKE1(
3205     "ChxjCookieLazyMode",
3206     cmd_set_cookie_lazy_mode,
3207     NULL,
3208     OR_ALL,
3209     "OneTimeID is negligently done. (TRUE/FALSE)"),
3210 #if defined(USE_MYSQL_COOKIE)
3211   AP_INIT_TAKE1(
3212     "ChxjCookieMysqlHost",
3213     cmd_set_cookie_mysql_host,
3214     NULL,
3215     OR_ALL,
3216     "The MySQL database host used by saving Cookie"),
3217   AP_INIT_TAKE1(
3218     "ChxjCookieMysqlPort",
3219     cmd_set_cookie_mysql_port,
3220     NULL,
3221     OR_ALL,
3222     "The MySQL database port used by saving Cookie"),
3223   AP_INIT_TAKE1(
3224     "ChxjCookieMysqlDatabase",
3225     cmd_set_cookie_mysql_database,
3226     NULL,
3227     OR_ALL,
3228     "The MySQL database name used by saving Cookie"),
3229   AP_INIT_TAKE1(
3230     "ChxjCookieMysqlUsername",
3231     cmd_set_cookie_mysql_username,
3232     NULL,
3233     OR_ALL,
3234     "The MySQL username used by saving Cookie"),
3235   AP_INIT_TAKE1(
3236     "ChxjCookieMysqlPassword",
3237     cmd_set_cookie_mysql_password,
3238     NULL,
3239     OR_ALL,
3240     "The MySQL password used by saving Cookie"),
3241   AP_INIT_TAKE1(
3242     "ChxjCookieMysqlTableName",
3243     cmd_set_cookie_mysql_table_name,
3244     NULL,
3245     OR_ALL,
3246     "The MySQL table name used by saving Cookie"),
3247   AP_INIT_TAKE1(
3248     "ChxjCookieMysqlSocketPath",
3249     cmd_set_cookie_mysql_socket_path,
3250     NULL,
3251     OR_ALL,
3252     "The MySQL socket path used by saving Cookie"),
3253   AP_INIT_TAKE1(
3254     "ChxjCookieMysqlCharset",
3255     cmd_set_cookie_mysql_charset,
3256     NULL,
3257     OR_ALL,
3258     "The MySQL charset used by saving Cookie"),
3259 #endif
3260 #if defined(USE_MEMCACHE_COOKIE)
3261   AP_INIT_TAKE1(
3262     "ChxjCookieMemcacheHost",
3263     cmd_set_cookie_memcache_host,
3264     NULL,
3265     OR_ALL,
3266     "The Memcached host used by saving Cookie"),
3267   AP_INIT_TAKE1(
3268     "ChxjCookieMemcachePort",
3269     cmd_set_cookie_memcache_port,
3270     NULL,
3271     OR_ALL,
3272     "The Memcached port used by saving Cookie"),
3273 #endif
3274   AP_INIT_TAKE1(
3275     "ChxjNewLineType",
3276     cmd_set_new_line_type,
3277     NULL,
3278     OR_ALL,
3279     "HTML new line type (NONE|CRLF|LF|CR). default is CRLF"),
3280   AP_INIT_TAKE1(
3281     "ChxjForwardUrlBase",
3282     cmd_set_forward_url_base,
3283     NULL,
3284     OR_ALL,
3285     "The forward url base(default: {request protocol}://{this server}:{this server port}"),
3286   AP_INIT_TAKE1(
3287     "ChxjForwardServerIp",
3288     cmd_set_forward_server_ip,
3289     NULL,
3290     OR_ALL,
3291     "The forward server ip(default: this server ip)"),
3292   AP_INIT_TAKE1(
3293     "ChxjAllowedCookieDomain",
3294     cmd_allowed_cookie_domain,
3295     NULL,
3296     OR_ALL,
3297     "Domain that permits parameter addition for cookie besides hostname.(Default:hostname Only)"),
3298   AP_INIT_TAKE1(
3299     "ChxjPostLogEnv",
3300     cmd_post_log_env,
3301     NULL,
3302     OR_ALL,
3303     "for CustomLog directive. mod_chxj's internal POST log environment name.(Default:chxj-post-log)"),
3304   AP_INIT_TAKE1(
3305     "ChxjCookieDbmType",
3306     cmd_cookie_dbm_type,
3307     NULL,
3308     OR_ALL,
3309     "Kind of DBM used with Cookie simulator.(default|GDBM|SDBM|DB|NDBM)"),
3310   AP_INIT_TAKE1(
3311     "ChxjImodeEmojiColor",
3312     cmd_imode_emoji_color,
3313     NULL,
3314     OR_ALL,
3315     "Auto i-mode emoji color"),
3316   AP_INIT_TAKE1(
3317     "ChxjAddDeviceDataTSV",
3318     cmd_add_device_data_tsv,
3319     NULL,
3320     OR_ALL,
3321     "Additional devices TSV data"),
3322   AP_INIT_TAKE1(
3323     "ChxjImageRewrite",
3324     cmd_image_rewrite,
3325     NULL,
3326     OR_ALL,
3327     "Rewrite Image"
3328    ),
3329   AP_INIT_TAKE1(
3330     "ChxjImageRewriteUrl",
3331     cmd_image_rewrite_url,
3332     NULL,
3333     OR_ALL,
3334     "Set rewrite Image url"
3335    ),
3336   AP_INIT_TAKE1(
3337     "ChxjImageRewriteMode",
3338     cmd_image_rewrite_mode,
3339     NULL,
3340     OR_ALL,
3341     "Set rewrite Image rewrite url mode"
3342    ),
3343   {NULL,{NULL},NULL,0,0,NULL},
3344 };
3345
3346
3347 /*----------------------------------------------------------------------------*/
3348 /* Dispatch list for API hooks                                                */
3349 /*----------------------------------------------------------------------------*/
3350 module AP_MODULE_DECLARE_DATA chxj_module = {
3351   STANDARD20_MODULE_STUFF, 
3352   chxj_create_per_dir_config,          /* create per-dir    config structures */
3353   chxj_merge_per_dir_config,           /* merge  per-dir    config structures */
3354   chxj_config_server_create,           /* create per-server config structures */
3355   NULL,                                /* merge  per-server config structures */
3356   cmds,                                /* table of config file commands       */
3357   chxj_register_hooks                  /* register hooks                      */
3358 };
3359 /*
3360  * vim:ts=2 et
3361  */