OSDN Git Service

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