OSDN Git Service

Merge branch 'branch_0.12.0' 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           ctx->buffer = apr_pstrdup(pool, "");
1232           ctx->len    = 0;
1233           ap_set_content_length(r, (apr_off_t)ctx->len);
1234           chxj_cookie_unlock(r, lock);
1235           s_add_no_cache_headers(r, entryp);
1236           rv = pass_data_to_filter(f, 
1237                                    (const char *)ctx->buffer, 
1238                                    (apr_size_t)ctx->len);
1239         }
1240         DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1241         return rv;
1242       }
1243       else {
1244         DBG(r, "REQ[%X] SAVE COOKIE[%x]", (unsigned int)(apr_size_t)r, entryp->action);
1245
1246         /*
1247          * save cookie.
1248          */
1249         if (entryp->action & CONVRULE_COOKIE_ON_BIT) {
1250           cookie_lock_t *lock = NULL;
1251           DBG(r, "REQ[%X] entryp->action == COOKIE_ON_BIT", (unsigned int)(apr_size_t)r);
1252           switch(spec->html_spec_type) {
1253           case CHXJ_SPEC_Chtml_1_0:
1254           case CHXJ_SPEC_Chtml_2_0:
1255           case CHXJ_SPEC_Chtml_3_0:
1256           case CHXJ_SPEC_Chtml_4_0:
1257           case CHXJ_SPEC_Chtml_5_0:
1258           case CHXJ_SPEC_Chtml_6_0:
1259           case CHXJ_SPEC_Chtml_7_0:
1260           case CHXJ_SPEC_XHtml_Mobile_1_0:
1261           case CHXJ_SPEC_Jhtml:
1262             lock = chxj_cookie_lock(r);
1263             cookie = chxj_save_cookie(r);
1264             /*
1265              * Location Header Check to add cookie parameter.
1266              */
1267             s_add_cookie_id_if_has_location_header(r, cookie);
1268             chxj_cookie_unlock(r, lock);
1269             apr_table_unset(r->headers_out, "Set-Cookie");
1270             apr_table_unset(r->err_headers_out, "Set-Cookie");
1271             break;
1272
1273           default:
1274             break;
1275           }
1276         }
1277         if (apr_table_get(r->headers_out, "Location") || apr_table_get(r->err_headers_out, "Location")) {
1278           if (! ap_is_HTTP_REDIRECT(r->status)) {
1279             r->status = HTTP_MOVED_TEMPORARILY;
1280           }
1281         }
1282         apr_table_setn(r->headers_out, "Content-Length", "0");
1283         DBG(r, "REQ[%X] call pass_data_to_filter()", (unsigned int)(apr_size_t)r);
1284         s_add_no_cache_headers(r, entryp);
1285         rv = pass_data_to_filter(f, (const char *)"", (apr_size_t)0);
1286         DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1287         return rv;
1288       }
1289     }
1290   }
1291   apr_brigade_destroy(bb);
1292
1293   DBG(f->r, "REQ[%X] end of chxj_output_filter()", (unsigned int)(apr_size_t)r);
1294
1295   return APR_SUCCESS;
1296 }
1297
1298 /**
1299  * Add Cookie_id if it has location header.
1300  */
1301 static void
1302 s_add_cookie_id_if_has_location_header(request_rec *r, cookie_t *cookie)
1303 {
1304   char *location_header = (char *)apr_table_get(r->headers_out, "Location");
1305   if (! location_header) {
1306     location_header = (char *)apr_table_get(r->err_headers_out, "Location");
1307   }
1308   if (cookie && location_header) {
1309     DBG(r, "REQ[%X] Location Header=[%s]", (unsigned int)(apr_size_t)r, location_header);
1310     location_header = chxj_add_cookie_parameter(r,
1311                                                 location_header,
1312                                                 cookie);
1313     apr_table_unset(r->headers_out, "Location");
1314     apr_table_setn(r->headers_out, "Location", location_header);
1315     DBG(r, "REQ[%X] Location Header=[%s]", (unsigned int)(apr_size_t)r, location_header);
1316     if (!ap_is_HTTP_REDIRECT(r->status)) {
1317       r->status = HTTP_MOVED_TEMPORARILY;
1318     }
1319   }
1320 }
1321
1322 /**
1323  * It is the main loop of the input filter handler. 
1324  *
1325  */
1326 static apr_status_t
1327 chxj_input_handler(request_rec *r)
1328 {
1329   mod_chxj_config     *dconf;
1330   chxjconvrule_entry  *entryp = NULL;
1331   device_table        *spec   = NULL;
1332   char                *post_data = NULL;
1333   apr_size_t          post_data_len = 0;
1334   char                *response;
1335   char                *user_agent;
1336   apr_pool_t          *pool;
1337   apr_size_t          ii;
1338   int                 response_code = 0;
1339   
1340   DBG(r, "start of chxj_input_handler()");
1341
1342   if (strcasecmp(r->handler, "chxj-input-handler")) {
1343     DBG(r, "end chxj_input_handler()");
1344     return DECLINED;
1345   }
1346   apr_pool_create(&pool, r->pool);
1347
1348   dconf      = chxj_get_module_config(r->per_dir_config, &chxj_module);
1349   user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
1350   if (!user_agent) {
1351     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1352   }
1353   spec       = chxj_specified_device(r, user_agent);
1354   entryp     = chxj_apply_convrule(r, dconf->convrules);
1355
1356   post_data = apr_pstrdup(pool, "");
1357   if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK) == OK) {
1358     if (ap_should_client_block(r)) {
1359       while (post_data_len < CHXJ_POST_MAX) {
1360 #define BUFSZ (256)
1361         char buffer[BUFSZ];
1362         int read_bytes = ap_get_client_block(r, buffer, BUFSZ-1);
1363         if (read_bytes<=0) {
1364           break;
1365         }
1366         buffer[read_bytes] = '\0';
1367         post_data = apr_pstrcat(pool, post_data, buffer, NULL);
1368         post_data_len += read_bytes;
1369 #undef BUFSZ
1370       }
1371     }
1372   }
1373
1374   /* 
1375    * now convert.
1376    */
1377   if (post_data_len > 0) {
1378     post_data = chxj_input_convert(r, (const char**)&post_data, (apr_size_t*)&post_data_len, entryp, spec);
1379     DBG(r, "(in:exchange)POSTDATA:[%s]", post_data);
1380   }
1381
1382   char *url_path;
1383   if (dconf->forward_url_base) {
1384     url_path = apr_psprintf(pool, "%s%s", dconf->forward_url_base, r->uri);
1385   }
1386   else {
1387     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);
1388   }
1389   if (r->args) {
1390     url_path = apr_pstrcat(pool, url_path, "?", r->args, NULL);
1391   }
1392   DBG(r, "==> new url_path:[%s]", url_path);
1393
1394   apr_size_t res_len;
1395   apr_table_setn(r->headers_in, CHXJ_HEADER_ORIG_CLIENT_IP, r->connection->remote_ip);
1396   apr_table_unset(r->headers_in, "Content-Length");
1397   apr_table_setn(r->headers_in, "Content-Length", apr_psprintf(pool, "%" APR_SIZE_T_FMT, post_data_len));
1398   response = chxj_serf_post(r, pool, url_path, post_data, post_data_len, 1, &res_len, &response_code);
1399   DBG(r, "REQ[%X] -------------------------------------------------------", (unsigned int)(apr_size_t)r);
1400   DBG(r, "REQ[%X] response length:[%" APR_SIZE_T_FMT "]", (unsigned int)(apr_size_t)r, res_len);
1401   for (ii=0; ii<res_len/64; ii++) {
1402     DBG(r, "REQ[%X] response:[%.*s]", (unsigned int)(apr_size_t)r, 64, &response[ii*64]);
1403   }
1404   DBG(r, "REQ[%X] -------------------------------------------------------", (unsigned int)(apr_size_t)r);
1405
1406   char *chunked;
1407   if ((chunked = (char *)apr_table_get(r->headers_out, "Transfer-Encoding")) != NULL) {
1408     if (strcasecmp(chunked, "chunked") == 0) {
1409       r->chunked = 1;  
1410       apr_table_unset(r->headers_out, "Transfer-Encoding");
1411     }
1412   }
1413   if (ap_is_HTTP_ERROR(response_code)) {
1414     DBG(r, "REQ[%X] end of chxj_input_handler() (HTTP-ERROR received. response code:[%d])", (unsigned int)(apr_size_t)r, response_code);
1415     return response_code;
1416   }
1417   {
1418     apr_pool_t *wpool;
1419     apr_pool_create(&wpool, r->pool);
1420     apr_bucket_brigade *bb;
1421     apr_bucket *e;
1422     apr_status_t rv;
1423     conn_rec *c = r->connection;
1424     
1425     bb = apr_brigade_create(wpool, c->bucket_alloc);
1426     e  = apr_bucket_transient_create(response, res_len, c->bucket_alloc);
1427     APR_BRIGADE_INSERT_TAIL(bb, e);
1428     e = apr_bucket_eos_create(c->bucket_alloc);
1429     APR_BRIGADE_INSERT_TAIL(bb, e);
1430     if ((rv = ap_pass_brigade(r->output_filters, bb)) != APR_SUCCESS) {
1431       ERR(r, "REQ[%X] %s:%d failed ap_pass_brigade()", (unsigned int)(apr_size_t)r, APLOG_MARK);
1432       return rv;
1433     }
1434     apr_brigade_cleanup(bb);
1435   }
1436
1437   DBG(r, "REQ[%X] end of chxj_input_handler()", (unsigned int)(apr_size_t)r);
1438   return APR_SUCCESS;
1439 }
1440
1441 static mod_chxj_global_config *
1442 chxj_global_config_create(apr_pool_t *pool, server_rec *s)
1443 {
1444   mod_chxj_global_config *conf;
1445
1446   SDBG(s, "start chxj_global_config_create()");
1447
1448   /*--------------------------------------------------------------------------*/
1449   /* allocate an own subpool which survives server restarts                   */
1450   /*--------------------------------------------------------------------------*/
1451   conf = (mod_chxj_global_config *)apr_palloc(pool, 
1452                   sizeof(mod_chxj_global_config));
1453 #if 0
1454   conf->cookie_db_lock = NULL;
1455 #endif
1456   SDBG(s, "end   chxj_global_config_create()");
1457
1458   return conf;
1459 }
1460
1461
1462 /**
1463  * initialize chxj module
1464  */
1465 static int 
1466 chxj_init_module(apr_pool_t *p, 
1467                  apr_pool_t *UNUSED(plog), 
1468                  apr_pool_t *UNUSED(ptemp), 
1469                  server_rec *s)
1470 {
1471   void *user_data;
1472   apr_status_t rv;
1473
1474   SDBG(s, "start chxj_init_module()");
1475
1476   apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
1477   SDBG(s, " ");
1478   if (user_data == NULL) {
1479     SDBG(s, " ");
1480     /*
1481      * dummy user_data set.
1482      */
1483     apr_pool_userdata_set(
1484       (const void *)(1), 
1485       CHXJ_MOD_CONFIG_KEY, 
1486       apr_pool_cleanup_null, 
1487       s->process->pool);
1488     SDBG(s, "end  chxj_init_module()");
1489     return OK;
1490   }
1491
1492   ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
1493
1494   if ((rv = apr_proc_mutex_create(&global_cookie_mutex, NULL,  APR_LOCK_FCNTL, s->process->pool)) != APR_SUCCESS) {
1495     char errstr[255];
1496     SERR(s, "%s:%d end chxj_init_module(). mutex create failure.(%d:%s)",APLOG_MARK, rv,apr_strerror(rv,errstr,255));
1497     return HTTP_INTERNAL_SERVER_ERROR;
1498   }
1499
1500   SDBG(s, "end  chxj_init_module()");
1501
1502   return OK;
1503 }
1504
1505
1506 static void 
1507 chxj_child_init(apr_pool_t *UNUSED(p), server_rec *s)
1508 {
1509   apr_status_t rv;
1510   SDBG(s, "start chxj_child_init()");
1511   if ((rv = apr_proc_mutex_child_init(&global_cookie_mutex, NULL, s->process->pool)) != APR_SUCCESS) {
1512     char errstr[255];
1513     SERR(s, "%s:%d ERROR end chxj_init_module(). mutex create failure.(%d:%s)", APLOG_MARK, rv,apr_strerror(rv,errstr,255));
1514   }
1515   SDBG(s, "end   chxj_child_init()");
1516 }
1517
1518
1519 /**
1520  * A set structure of each server is generated. 
1521  * 
1522  * @param p
1523  * @param s
1524  */
1525 static void *
1526 chxj_config_server_create(apr_pool_t *p, server_rec *s)
1527 {
1528   mod_chxj_global_config *gc;
1529
1530   gc = chxj_global_config_create(p,s);
1531
1532   return gc;
1533 }
1534
1535
1536 static int
1537 chxj_translate_name(request_rec *r)
1538 {
1539   DBG(r, "REQ[%X] =======================================================================", (unsigned int)(apr_size_t)r);
1540   DBG(r, "REQ[%X] ", (unsigned int)(apr_size_t)r);
1541   DBG(r, "REQ[%X] START REQUEST (uri:[%s] args:[%s])", (unsigned int)(apr_size_t)r, r->unparsed_uri, r->args ? r->args : "");
1542   DBG(r, "REQ[%X] ", (unsigned int)(apr_size_t)r);
1543   DBG(r, "REQ[%X] =======================================================================", (unsigned int)(apr_size_t)r);
1544   return chxj_trans_name(r);
1545 }
1546
1547
1548 static void 
1549 chxj_insert_filter(request_rec *r)
1550 {
1551   char                *user_agent;
1552   device_table        *spec;
1553   mod_chxj_config     *dconf;
1554   chxjconvrule_entry  *entryp;
1555   mod_chxj_ctx        *ctx;
1556   apr_status_t        rv;
1557   char                *contentType;
1558
1559   DBG(r, "REQ[%X] start chxj_insert_filter()", (apr_size_t)(unsigned int)r);
1560
1561   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1562
1563   /* we get User-Agent from CHXJ_HTTP_USER_AGENT header if any */
1564   user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
1565   if (!user_agent) {
1566     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1567   }
1568
1569   contentType = (char *)apr_table_get(r->headers_in, "Content-Type");
1570   if (contentType
1571       && strncasecmp("multipart/form-data", contentType, 19) == 0) {
1572     DBG(r, "REQ[%X] detect multipart/form-data ==> no target", (apr_size_t)(unsigned int)r);
1573     DBG(r, "REQ[%X] end chxj_insert_filter()", (apr_size_t)(unsigned int)r);
1574     return;
1575   }
1576
1577   spec = chxj_specified_device(r, user_agent);
1578   entryp = chxj_apply_convrule(r, dconf->convrules);
1579   if (!entryp) {
1580     DBG(r, "REQ[%X] end chxj_insert_filter()", (apr_size_t)(unsigned int)r);
1581     return;
1582   }
1583   ctx = apr_palloc(r->pool, sizeof(*ctx));
1584   memset(ctx, 0, sizeof(*ctx));
1585   if ((rv = apr_pool_create(&ctx->pool, r->pool)) != APR_SUCCESS) {
1586     ERR(r, "%s:%d: failed: new pool create. rv:[%d]", __FILE__,__LINE__,rv);
1587     DBG(r, "REQ:[%X] end chxj_insert_filter()", (apr_size_t)(unsigned int)r);
1588     return;
1589   }
1590   ctx->entryp = entryp;
1591   ctx->spec   = spec;
1592   ctx->buffer = apr_palloc(ctx->pool, 1);
1593   ctx->buffer[0] = 0;
1594
1595   if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
1596     DBG(r,"REQ[%X] EngineOff", (apr_size_t)(unsigned int)r);
1597     DBG(r, "REQ[%X] end chxj_insert_filter()", (apr_size_t)(unsigned int)r);
1598     return;
1599   }
1600
1601   switch(spec->html_spec_type) {
1602   case CHXJ_SPEC_Chtml_1_0:
1603   case CHXJ_SPEC_Chtml_2_0:
1604   case CHXJ_SPEC_Chtml_3_0:
1605   case CHXJ_SPEC_Chtml_4_0:
1606   case CHXJ_SPEC_Chtml_5_0:
1607   case CHXJ_SPEC_Chtml_6_0:
1608   case CHXJ_SPEC_Chtml_7_0:
1609   case CHXJ_SPEC_XHtml_Mobile_1_0:
1610   case CHXJ_SPEC_Hdml:
1611   case CHXJ_SPEC_Jhtml:
1612   case CHXJ_SPEC_Jxhtml:
1613     break;
1614
1615   default:
1616     DBG(r, "REQ[%X] end chxj_insert_filter() Unknown spec type.", (apr_size_t)(unsigned int)r);
1617     return;
1618   }
1619
1620
1621   if (! apr_table_get(r->headers_in, "X-Chxj-Forward")) {
1622     ap_add_output_filter("chxj_output_filter", ctx, r, r->connection);
1623     DBG(r, "REQ[%X] added Output Filter", (apr_size_t)(unsigned int)r);
1624   }
1625
1626   DBG(r, "REQ[%X] end chxj_insert_filter()", (apr_size_t)(unsigned int)r);
1627 }
1628
1629
1630 /**
1631  * The hook is registered.
1632  *
1633  * @param p
1634  */
1635 static void 
1636 chxj_register_hooks(apr_pool_t *UNUSED(p))
1637 {
1638   ap_hook_post_config(chxj_init_module,
1639                       NULL,
1640                       NULL,
1641                       APR_HOOK_REALLY_FIRST);
1642   ap_hook_child_init(chxj_child_init, 
1643                      NULL, 
1644                      NULL, 
1645                      APR_HOOK_REALLY_FIRST);
1646   ap_register_output_filter (
1647                       "chxj_output_filter", 
1648                       chxj_output_filter, 
1649                       NULL, 
1650                       AP_FTYPE_RESOURCE);
1651   ap_hook_insert_filter(chxj_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
1652   ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1653   ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1654   ap_hook_handler(chxj_input_handler, NULL, NULL, APR_HOOK_MIDDLE);
1655   ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1656   ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_FIRST);
1657 }
1658
1659
1660 /**
1661  * A set structure according to the directory is generated. 
1662  *
1663  * @param p
1664  * @param arg
1665  */
1666 static void * 
1667 chxj_create_per_dir_config(apr_pool_t *p, char *arg) 
1668 {
1669   mod_chxj_config *conf;
1670
1671   conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1672   conf->device_data_file = NULL;
1673   conf->devices          = NULL;
1674   conf->emoji_data_file  = NULL;
1675   conf->emoji            = NULL;
1676   conf->emoji_tail       = NULL;
1677   conf->image            = CHXJ_IMG_NONE;
1678   conf->image_cache_dir  = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1679   conf->image_cache_limit = 0;
1680   conf->server_side_encoding = NULL;
1681   conf->cookie_db_dir    = NULL;
1682   conf->cookie_timeout   = 0;
1683   conf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
1684   conf->cookie_lazy_mode  = 0;
1685 #if defined(USE_MYSQL_COOKIE)
1686   memset((void *)&conf->mysql, 0, sizeof(mysql_t));
1687   conf->mysql.port       = MYSQL_PORT;
1688   conf->mysql.host       = NULL;
1689 #endif
1690 #if defined(USE_MEMCACHE_COOKIE)
1691   memset((void *)&conf->memcache, 0, sizeof(memcache_t));
1692   conf->memcache.host    = NULL;
1693   conf->memcache.port    = 0;
1694 #endif
1695   conf->forward_url_base = NULL;
1696   conf->forward_server_ip = NULL;
1697   conf->allowed_cookie_domain = NULL;
1698
1699   if (arg == NULL) {
1700     conf->dir                  = NULL;
1701   }
1702   else {
1703     conf->dir                  = apr_pcalloc(p, strlen(arg)+1);
1704     memset(conf->dir, 0, strlen(arg)+1);
1705     strcpy(conf->dir, arg);
1706   }
1707   conf->convrules   = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1708
1709   /* Default is copyleft */
1710   conf->image_copyright = NULL; 
1711
1712   return conf;
1713 }
1714
1715
1716 /*
1717  *  Merge per-directory CHXJ configurations
1718  */
1719 static void *
1720 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1721 {
1722   mod_chxj_config *base;
1723   mod_chxj_config *add;
1724   mod_chxj_config *mrg;
1725
1726   base = (mod_chxj_config *)basev;
1727   add  = (mod_chxj_config *)addv;
1728   mrg  = (mod_chxj_config *)apr_palloc(p, sizeof(mod_chxj_config));
1729
1730   mrg->device_data_file = NULL;
1731   mrg->devices          = NULL;
1732   mrg->emoji_data_file  = NULL;
1733   mrg->image            = CHXJ_IMG_NONE;
1734   mrg->image_cache_dir  = NULL;
1735   mrg->image_copyright  = NULL;
1736   mrg->image_cache_limit  = 0;
1737   mrg->emoji            = NULL;
1738   mrg->emoji_tail       = NULL;
1739   mrg->new_line_type    = NLTYPE_NIL;
1740   mrg->forward_url_base = NULL;
1741   mrg->forward_server_ip = NULL;
1742   mrg->allowed_cookie_domain = NULL;
1743
1744   mrg->dir = apr_pstrdup(p, add->dir);
1745
1746   if (! add->device_data_file) {
1747     mrg->devices = base->devices;
1748     mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1749   }
1750   else {
1751     mrg->devices = add->devices;
1752     mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1753   }
1754
1755   if (! add->emoji_data_file) {
1756     mrg->emoji = base->emoji;
1757     mrg->emoji_tail = base->emoji_tail;
1758     mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1759   }
1760   else {
1761     mrg->emoji = add->emoji;
1762     mrg->emoji_tail = add->emoji_tail;
1763     mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1764   }
1765
1766   if (add->image == CHXJ_IMG_NONE) {
1767     mrg->image = base->image;
1768   }
1769   else {
1770     mrg->image = add->image;
1771   }
1772
1773   if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) {
1774     mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1775   }
1776   else {
1777     mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1778   }
1779
1780   if (add->image_cache_limit) {
1781     mrg->image_cache_limit = add->image_cache_limit;
1782   }
1783   else {
1784     mrg->image_cache_limit = base->image_cache_limit;
1785   }
1786
1787   if (add->image_copyright) 
1788     mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1789   else
1790     mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1791
1792   if (add->server_side_encoding) {
1793     mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1794   }
1795   else 
1796   if (base->server_side_encoding) {
1797     mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1798   }
1799   else {
1800     mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1801   }
1802
1803   mrg->convrules    = apr_array_append(p, add->convrules, base->convrules);
1804
1805   if (add->cookie_db_dir) {
1806     mrg->cookie_db_dir = apr_pstrdup(p, add->cookie_db_dir);
1807   }
1808   else
1809   if (base->cookie_db_dir) {
1810     mrg->cookie_db_dir = apr_pstrdup(p, base->cookie_db_dir);
1811   }
1812   else {
1813     mrg->cookie_db_dir = NULL;
1814   }
1815
1816   if (add->cookie_timeout) {
1817     mrg->cookie_timeout   = add->cookie_timeout;
1818   }
1819   else
1820   if (base->cookie_db_dir) {
1821     mrg->cookie_timeout   = base->cookie_timeout;
1822   }
1823   else {
1824     mrg->cookie_timeout   = 0;
1825   }
1826
1827 #if defined(USE_MYSQL_COOKIE)
1828   if (add->mysql.host) {
1829     mrg->mysql.host = apr_pstrdup(p, add->mysql.host);
1830   }
1831   else if (base->mysql.host) {
1832     mrg->mysql.host = apr_pstrdup(p, base->mysql.host);
1833   }
1834   else {
1835     mrg->mysql.host = NULL;
1836   }
1837   if (add->mysql.port) {
1838     mrg->mysql.port = add->mysql.port;
1839   }
1840   else if (base->mysql.port) {
1841     mrg->mysql.port = base->mysql.port;
1842   }
1843   else {
1844     mrg->mysql.port = 0;
1845   }
1846
1847   if (add->mysql.database) {
1848     mrg->mysql.database = apr_pstrdup(p, add->mysql.database);
1849   }
1850   else if (base->mysql.database) {
1851     mrg->mysql.database = apr_pstrdup(p, base->mysql.database);
1852   }
1853   else {
1854     mrg->mysql.database = NULL;
1855   }
1856
1857   if (add->mysql.username) {
1858     mrg->mysql.username = apr_pstrdup(p, add->mysql.username);
1859   }
1860   else if (base->mysql.username) {
1861     mrg->mysql.username = apr_pstrdup(p, base->mysql.username);
1862   }
1863   else {
1864     mrg->mysql.username = NULL;
1865   }
1866
1867   if (add->mysql.password) {
1868     mrg->mysql.password = apr_pstrdup(p, add->mysql.password);
1869   }
1870   else if (base->mysql.password) {
1871     mrg->mysql.password = apr_pstrdup(p, base->mysql.password);
1872   }
1873   else {
1874     mrg->mysql.password = NULL;
1875   }
1876
1877   if (add->mysql.tablename) {
1878     mrg->mysql.tablename = apr_pstrdup(p, add->mysql.tablename);
1879   }
1880   else if (base->mysql.tablename) {
1881     mrg->mysql.tablename = apr_pstrdup(p, base->mysql.tablename);
1882   }
1883   else {
1884     mrg->mysql.tablename = NULL;
1885   }
1886
1887   if (add->mysql.socket_path) {
1888     mrg->mysql.socket_path = apr_pstrdup(p, add->mysql.socket_path);
1889   }
1890   else if (base->mysql.socket_path) {
1891     mrg->mysql.socket_path = apr_pstrdup(p, base->mysql.socket_path);
1892   }
1893   else {
1894     mrg->mysql.socket_path = NULL;
1895   }
1896
1897   if (add->mysql.charset) {
1898     mrg->mysql.charset = apr_pstrdup(p, add->mysql.charset);
1899   }
1900   else if (base->mysql.charset) {
1901     mrg->mysql.charset = apr_pstrdup(p, base->mysql.charset);
1902   }
1903   else {
1904     mrg->mysql.charset = NULL;
1905   }
1906 #endif
1907 #if defined(USE_MEMCACHE_COOKIE)
1908   if (add->memcache.host) {
1909     mrg->memcache.host = apr_pstrdup(p, add->memcache.host);
1910   }
1911   else if (base->memcache.host) {
1912     mrg->memcache.host = apr_pstrdup(p, base->memcache.host);
1913   }
1914   else {
1915     mrg->memcache.host = NULL;
1916   }
1917   if (add->memcache.port) {
1918     mrg->memcache.port = add->memcache.port;
1919   }
1920   else if (base->memcache.port) {
1921     mrg->memcache.port = base->memcache.port;
1922   }
1923   else {
1924     mrg->memcache.port = 0;
1925   }
1926 #endif
1927   if (add->cookie_store_type) {
1928     mrg->cookie_store_type = add->cookie_store_type;
1929   }
1930   else if (base->cookie_store_type) {
1931     mrg->cookie_store_type = base->cookie_store_type;
1932   }
1933   else {
1934     mrg->cookie_store_type = COOKIE_STORE_TYPE_NONE;
1935   }
1936   if (add->cookie_lazy_mode) {
1937     mrg->cookie_lazy_mode = add->cookie_lazy_mode;
1938   }
1939   else if (base->cookie_lazy_mode) {
1940     mrg->cookie_lazy_mode = base->cookie_lazy_mode;
1941   }
1942   else {
1943     mrg->cookie_lazy_mode = 0;
1944   }
1945   if (add->new_line_type) {
1946     mrg->new_line_type = add->new_line_type;
1947   }
1948   else if (base->new_line_type) {
1949     mrg->new_line_type = base->new_line_type;
1950   }
1951   else {
1952     mrg->new_line_type = NLTYPE_NIL;
1953   }
1954
1955   if (add->forward_url_base) {
1956     mrg->forward_url_base = add->forward_url_base;
1957   }
1958   else if (base->forward_url_base) {
1959     mrg->forward_url_base = base->forward_url_base;
1960   }
1961
1962   if (add->allowed_cookie_domain) {
1963     mrg->allowed_cookie_domain = add->allowed_cookie_domain;
1964   }
1965   else {
1966     mrg->allowed_cookie_domain = base->allowed_cookie_domain;
1967   }
1968   return mrg;
1969 }
1970
1971
1972 static int
1973 chxj_command_parse_take5(
1974   const char *arg, 
1975   char       **prm1, 
1976   char       **prm2, 
1977   char       **prm3, 
1978   char       **prm4, 
1979   char       **prm5)
1980 {
1981   int  isquoted;
1982   char *strp;
1983
1984   strp = (char *)arg;
1985
1986   for (;*strp == ' '||*strp == '\t'; strp++) ;
1987
1988   isquoted = 0; 
1989   if (*strp == '"') { 
1990     isquoted = 1;
1991     strp++;
1992   }
1993
1994   *prm1 = strp;
1995
1996   for (; *strp != '\0'; strp++) {
1997     if ((isquoted && (*strp == ' ' || *strp == '\t'))
1998     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
1999       strp++;
2000       continue;
2001     }
2002
2003     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2004     ||  (isquoted  && *strp == '"'))
2005       break;
2006   }
2007
2008   if (! *strp) {
2009     *prm2 = strp;
2010     *prm3 = strp;
2011     *prm4 = strp;
2012     *prm5 = strp;
2013     return 1;
2014   }
2015
2016   *strp++ = '\0';
2017
2018   for (;*strp == ' '||*strp == '\t'; strp++) ;
2019
2020   isquoted = 0; 
2021   if (*strp == '"') { 
2022     isquoted = 1;
2023     strp++;
2024   }
2025
2026   *prm2 = strp;
2027   for (; *strp != '\0'; strp++) {
2028     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2029     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2030       strp++;
2031       continue;
2032     }
2033
2034     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2035     ||  (isquoted  && *strp == '"'))
2036       break;
2037   }
2038
2039   if (! *strp) {
2040     *prm3 = strp;
2041     *prm4 = strp;
2042     *prm5 = strp;
2043     return 0;
2044   }
2045
2046   *strp++ = '\0';
2047
2048   for (;*strp == ' '||*strp == '\t'; strp++);
2049
2050   isquoted = 0; 
2051   if (*strp == '"') { 
2052     isquoted = 1;
2053     strp++;
2054   }
2055   *prm3 = strp;
2056   for (; *strp != '\0'; strp++) {
2057     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2058     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2059       strp++;
2060       continue;
2061     }
2062
2063     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2064     ||  (isquoted  && *strp == '"'))
2065       break;
2066   }
2067
2068   if (! *strp) {
2069     *prm4 = strp;
2070     *prm5 = strp;
2071     return 0;
2072   }
2073
2074   *strp++ = '\0';
2075
2076   for (;*strp == ' '||*strp == '\t'; strp++);
2077
2078   isquoted = 0; 
2079   if (*strp == '"') { 
2080     isquoted = 1;
2081     strp++;
2082   }
2083   *prm4 = strp;
2084   for (; *strp != '\0'; strp++) {
2085     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2086     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2087       strp++;
2088       continue;
2089     }
2090
2091     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2092     ||  (isquoted  && *strp == '"'))
2093       break;
2094   }
2095
2096   if (! *strp) {
2097     *prm5 = strp;
2098     return 0;
2099   }
2100
2101   *strp++ = '\0';
2102
2103   for (;*strp == ' '||*strp == '\t'; strp++);
2104
2105   isquoted = 0; 
2106   if (*strp == '"') { 
2107     isquoted = 1;
2108     strp++;
2109   }
2110   *prm5 = strp;
2111   for (; *strp != '\0'; strp++) {
2112     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2113     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2114       strp++;
2115       continue;
2116     }
2117
2118     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2119     ||  (isquoted  && *strp == '"'))
2120       break;
2121   }
2122   *strp = '\0';
2123
2124   return 0;
2125 }
2126
2127
2128 /**
2129  * The device definition file is loaded. 
2130  *
2131  * @param arg     [i]   The name of the device definition file is specified.
2132  * @param mconfig [i/o] The pointer to a set structure is specified. 
2133  * @param parms   [i]   
2134  */
2135 static const char * 
2136 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char *arg) 
2137 {
2138   mod_chxj_config  *conf;
2139   Doc              doc;
2140
2141   doc.r = NULL;
2142
2143   if (strlen(arg) > 256) 
2144     return "mod_chxj: device data filename too long.";
2145
2146   conf = (mod_chxj_config *)mconfig;
2147   conf->device_data_file = apr_pstrdup(parms->pool, arg);
2148
2149   qs_init_malloc(&doc);
2150   qs_init_root_node(&doc);
2151
2152   qs_parse_file((Doc *)&doc, (const char *)arg);
2153   chxj_load_device_data(&doc,parms->pool, conf);
2154   qs_all_free(&doc, QX_LOGMARK);
2155
2156   return NULL;
2157 }
2158
2159
2160 /**
2161  * Device definition information is loaded. 
2162  *
2163  * @param parms 
2164  * @param arg     [i]   The name of the device definition file is specified. 
2165  * @param mconfig [i/o] The pointer to a set structure is specified. 
2166  * @return 
2167  */
2168 static const char * 
2169 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char *arg) 
2170 {
2171   mod_chxj_config *conf;
2172   char            *rtn;
2173   Doc              doc;
2174
2175   doc.r = NULL;
2176
2177
2178   if (strlen(arg) > 256) 
2179     return "mod_chxj: emoji data filename too long.";
2180
2181   conf = (mod_chxj_config *)mconfig;
2182   conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
2183   qs_init_malloc(&doc);
2184   qs_init_root_node(&doc);
2185
2186   qs_parse_file((Doc *)&doc, (const char *)arg);
2187
2188   rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
2189
2190   qs_all_free(&doc, QX_LOGMARK);
2191
2192
2193   return rtn;
2194 }
2195
2196
2197 static const char * 
2198 cmd_set_image_engine(cmd_parms * UNUSED(parms), void *mconfig, const char *arg) 
2199 {
2200   mod_chxj_config *conf;
2201   Doc              doc;
2202
2203   doc.r = NULL;
2204
2205   if (strlen(arg) > 256) 
2206     return "image uri is too long.";
2207
2208   conf = (mod_chxj_config*)mconfig;
2209   if (strcasecmp("ON", arg) == 0) {
2210     conf->image = CHXJ_IMG_ON;
2211   }
2212   else {
2213     conf->image = CHXJ_IMG_OFF;
2214   }
2215
2216   return NULL;
2217 }
2218
2219
2220 static const char * 
2221 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char *arg) 
2222 {
2223   mod_chxj_config *conf;
2224   Doc              doc;
2225
2226   doc.r = NULL;
2227
2228   if (strlen(arg) > 256) 
2229     return "cache dir name is too long.";
2230
2231   conf = (mod_chxj_config *)mconfig;
2232   conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
2233
2234   return NULL;
2235 }
2236
2237
2238 static const char * 
2239 cmd_set_image_cache_limit(cmd_parms *parms, void *mconfig, const char *arg) 
2240 {
2241   mod_chxj_config *conf;
2242   Doc              doc;
2243
2244   doc.r = NULL;
2245
2246   if (strlen(arg) > IMAGE_CACHE_LIMIT_FMT_LEN) 
2247     return "cache size is too long.";
2248
2249   conf = (mod_chxj_config *)mconfig;
2250   errno = 0;
2251   /* 
2252    * I use strtol function because strtoul is not portable function. 
2253    */
2254   conf->image_cache_limit = (unsigned long)strtol(arg, NULL, 10);
2255   switch (errno) {
2256   case EINVAL:
2257     return apr_psprintf(parms->pool, "ChxjImageCacheLimit invalid value [%s] errno:[%d]", arg, errno);
2258   case ERANGE:
2259     return apr_psprintf(parms->pool, "ChxjImageCacheLimit Out of range [%s] errno:[%d]", arg, errno);
2260   default:
2261     break;
2262   }
2263   return NULL;
2264 }
2265
2266
2267 static const char * 
2268 cmd_set_image_copyright(cmd_parms *parms, void *mconfig, const char *arg) 
2269 {
2270   mod_chxj_config *conf;
2271   Doc              doc;
2272
2273   doc.r = NULL;
2274
2275   if (strlen(arg) > 256) 
2276     return "Copyright Flag is too long.";
2277
2278   conf = (mod_chxj_config *)mconfig;
2279   conf->image_copyright = apr_pstrdup(parms->pool, arg);
2280
2281   return NULL;
2282 }
2283
2284
2285 static const char *
2286 cmd_convert_rule(cmd_parms *cmd, void *mconfig, const char *arg)
2287 {
2288   int                 mode;
2289   ap_regex_t          *regexp;
2290   mod_chxj_config     *dconf;
2291   chxjconvrule_entry  *newrule;
2292   char                *prm1;
2293   char                *prm2;
2294   char                *prm3;
2295   char                *prm4;
2296   char                *prm5;
2297   char                *pstate;
2298   char                *action;
2299   char                *pp;
2300
2301   dconf = (mod_chxj_config *)mconfig;
2302
2303   if (strlen(arg) > 4096) 
2304     return "mod_chxj: ChxjConvertRule: is too long.";
2305
2306   dconf = (mod_chxj_config *)mconfig;
2307   if (dconf->convrules == NULL)
2308     dconf->convrules   = apr_array_make(cmd->pool, 
2309                                         2, 
2310                                         sizeof(chxjconvrule_entry));
2311
2312   newrule = apr_array_push(dconf->convrules);
2313
2314   newrule->flags  = 0;
2315   newrule->action = 0;
2316
2317   if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
2318     return "ChxjConvertRule: bad argument line";
2319
2320   newrule->pattern = apr_pstrdup(cmd->pool, prm1);
2321
2322   /* Parse action */
2323   for (;;) {
2324     if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
2325       break;
2326     prm2 = NULL;
2327     switch(*action) {
2328     case 'e':
2329     case 'E':
2330       if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
2331         newrule->action |= CONVRULE_ENGINE_ON_BIT;
2332       }
2333       else
2334       if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
2335         newrule->action |= CONVRULE_ENGINE_OFF_BIT;
2336       }
2337       else
2338       if (strcasecmp(CONVRULE_EMOJI_ONLY_CMD, action) == 0) {
2339         newrule->action |= CONVRULE_EMOJI_ONLY_BIT;
2340       }
2341       else
2342       if (strcasecmp(CONVRULE_ENVINFO_ONLY_CMD, action) == 0) {
2343         newrule->action |= CONVRULE_ENVINFO_ONLY_BIT;
2344       }
2345       break;
2346
2347     case 'C':
2348     case 'c':
2349       if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
2350         newrule->action |= CONVRULE_COOKIE_ON_BIT;
2351       }
2352       else if (strcasecmp(CONVRULE_COOKIE_OFF_CMD, action) == 0) {
2353         newrule->action &= (0xffffffff ^ CONVRULE_COOKIE_ON_BIT);
2354       }
2355       else if (strcasecmp(CONVRULE_CSS_ON_CMD, action) == 0) {
2356         newrule->action |= CONVRULE_CSS_ON_BIT;
2357       }
2358       else if (strcasecmp(CONVRULE_CSS_OFF_CMD, action) == 0) {
2359         newrule->action &= (0xffffffff ^ CONVRULE_CSS_ON_BIT);
2360       }
2361       break;
2362
2363     case 'J':
2364     case 'j':
2365       if (strcasecmp(CONVRULE_JRCONV_OFF_CMD, action) == 0) {
2366         newrule->action |= CONVRULE_JRCONV_OFF_BIT;
2367       }
2368       break;
2369
2370     case 'N':
2371     case 'n':
2372       if (strcasecmp(CONVRULE_NOCACHE_ON_CMD, action) == 0) {
2373         newrule->action |= CONVRULE_NOCACHE_ON_BIT;
2374       }
2375       break;
2376
2377     case 'Q':
2378     case 'q':
2379       if (strcasecmp(CONVRULE_QSCONV_OFF_CMD, action) == 0) {
2380         newrule->action |= CONVRULE_QSCONV_OFF_BIT;
2381       }
2382       break;
2383
2384     case 'Z':
2385     case 'z':
2386       if (strcasecmp(CONVRULE_Z2H_ON_CMD, action) == 0) {
2387         newrule->action |= CONVRULE_Z2H_ON_BIT;
2388       }
2389       else
2390       if (strcasecmp(CONVRULE_Z2H_OFF_CMD, action) == 0) {
2391         newrule->action |= CONVRULE_Z2H_OFF_BIT;
2392       }
2393       else
2394       if (strcasecmp(CONVRULE_Z2H_ALPHA_ON_CMD, action) == 0) {
2395         newrule->action |= CONVRULE_Z2H_ALPHA_ON_BIT;
2396       }
2397       else
2398       if (strcasecmp(CONVRULE_Z2H_ALPHA_OFF_CMD, action) == 0) {
2399         newrule->action |= CONVRULE_Z2H_ALPHA_OFF_BIT;
2400       }
2401       else
2402       if (strcasecmp(CONVRULE_Z2H_NUM_ON_CMD, action) == 0) {
2403         newrule->action |= CONVRULE_Z2H_NUM_ON_BIT;
2404       }
2405       else
2406       if (strcasecmp(CONVRULE_Z2H_NUM_OFF_CMD, action) == 0) {
2407         newrule->action |= CONVRULE_Z2H_NUM_OFF_BIT;
2408       }
2409       else
2410       if (strcasecmp(CONVRULE_Z2H_ALL_ON_CMD, action) == 0) {
2411         newrule->action |= CONVRULE_Z2H_ON_BIT | CONVRULE_Z2H_ALPHA_ON_BIT | CONVRULE_Z2H_NUM_ON_BIT;
2412       }
2413       else
2414       if (strcasecmp(CONVRULE_Z2H_NUM_OFF_CMD, action) == 0) {
2415         newrule->action |= CONVRULE_Z2H_OFF_BIT | CONVRULE_Z2H_ALPHA_OFF_BIT | CONVRULE_Z2H_NUM_OFF_BIT;
2416       }
2417       break;
2418
2419     default:
2420       break;
2421     }
2422   }
2423   
2424   pp = prm1;
2425   if (*pp == '!') {
2426     newrule->flags |= CONVRULE_FLAG_NOTMATCH;
2427     pp++;
2428   }
2429
2430   mode = AP_REG_EXTENDED;
2431   if ((regexp = ap_pregcomp((apr_pool_t *)cmd->pool, (const char *)pp, mode)) == NULL)
2432     return "RewriteRule: cannot compile regular expression ";
2433
2434   newrule->regexp = regexp;
2435   if (*prm3)
2436     newrule->encoding = apr_pstrdup(cmd->pool, prm3);
2437   else
2438     newrule->encoding = apr_pstrdup(cmd->pool, "none");
2439
2440   newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
2441   if (*prm4)
2442     if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
2443       newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
2444
2445   newrule->user_agent = NULL;
2446   if (*prm5)
2447     newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
2448     
2449   return NULL;
2450 }
2451
2452
2453 static const char *
2454 cmd_set_cookie_dir(
2455   cmd_parms   *cmd, 
2456   void        *mconfig, 
2457   const char  *arg)
2458 {
2459   mod_chxj_config *dconf;
2460
2461
2462   if (strlen(arg) > 4096) 
2463     return "mod_chxj: ChxjCookieDir is too long.";
2464
2465   dconf = (mod_chxj_config *)mconfig;
2466
2467   dconf->cookie_db_dir = apr_pstrdup(cmd->pool, arg);
2468
2469   return NULL;
2470 }
2471
2472
2473 static const char *
2474 cmd_set_cookie_timeout(
2475   cmd_parms   *UNUSED(cmd), 
2476   void        *mconfig, 
2477   const char  *arg)
2478 {
2479   mod_chxj_config *dconf;
2480
2481   if (strlen(arg) > 4096) 
2482     return "mod_chxj: ChxjCookieTimeout is too long.";
2483
2484   if (chxj_chk_numeric(arg) != 0)
2485     return "mod_chxj: ChxjCookieTimeout is not numeric.";
2486
2487   dconf = (mod_chxj_config *)mconfig;
2488
2489   dconf->cookie_timeout = atoi(arg);
2490
2491   return NULL;
2492 }
2493
2494
2495 #if defined(USE_MYSQL_COOKIE)
2496 static const char *
2497 cmd_set_cookie_mysql_database(
2498   cmd_parms   *cmd, 
2499   void        *mconfig, 
2500   const char  *arg)
2501 {
2502   mod_chxj_config  *dconf;
2503
2504   if (strlen(arg) > 255) 
2505     return "mod_chxj: ChxjCookieMysqlDatabase is too long.";
2506
2507   dconf = (mod_chxj_config *)mconfig;
2508
2509   dconf->mysql.database = apr_pstrdup(cmd->pool, arg);
2510
2511   return NULL;
2512 }
2513
2514
2515 static const char *
2516 cmd_set_cookie_mysql_username(
2517   cmd_parms   *cmd, 
2518   void        *mconfig, 
2519   const char  *arg)
2520 {
2521   mod_chxj_config  *dconf;
2522
2523   if (strlen(arg) > 255) 
2524     return "mod_chxj: ChxjCookieMysqlUsername is too long.";
2525
2526   dconf = (mod_chxj_config *)mconfig;
2527
2528   dconf->mysql.username = apr_pstrdup(cmd->pool, arg);
2529
2530   return NULL;
2531 }
2532
2533
2534 static const char *
2535 cmd_set_cookie_mysql_password(
2536   cmd_parms   *cmd, 
2537   void        *mconfig, 
2538   const char  *arg)
2539 {
2540   mod_chxj_config  *dconf;
2541
2542   if (strlen(arg) > 255) 
2543     return "mod_chxj: ChxjCookieMysqlPassword is too long.";
2544
2545   dconf = (mod_chxj_config *)mconfig;
2546
2547   dconf->mysql.password = apr_pstrdup(cmd->pool, arg);
2548
2549   return NULL;
2550 }
2551
2552
2553 static const char *
2554 cmd_set_cookie_mysql_table_name(
2555   cmd_parms   *cmd, 
2556   void        *mconfig, 
2557   const char  *arg)
2558 {
2559   mod_chxj_config  *dconf;
2560
2561   if (strlen(arg) > 255) 
2562     return "mod_chxj: ChxjCookieMysqlTableName is too long.";
2563
2564   dconf = (mod_chxj_config *)mconfig;
2565
2566   dconf->mysql.tablename = apr_pstrdup(cmd->pool, arg);
2567
2568   return NULL;
2569 }
2570
2571 static const char *
2572 cmd_set_cookie_mysql_port(
2573   cmd_parms   *UNUSED(cmd), 
2574   void        *mconfig, 
2575   const char  *arg)
2576 {
2577   mod_chxj_config *dconf;
2578
2579   if (strlen(arg) > 255) 
2580     return "mod_chxj: ChxjCookieMysqlPort is too long.";
2581
2582   dconf = (mod_chxj_config *)mconfig;
2583
2584   if (chxj_chk_numeric(arg) != 0)
2585     return "mod_chxj: ChxjCookieMysqlPort is not numeric.";
2586
2587   dconf = (mod_chxj_config *)mconfig;
2588
2589   dconf->mysql.port = chxj_atoi(arg);
2590
2591   return NULL;
2592 }
2593
2594
2595 static const char *
2596 cmd_set_cookie_mysql_host(
2597   cmd_parms   *cmd, 
2598   void        *mconfig, 
2599   const char  *arg)
2600 {
2601   mod_chxj_config  *dconf;
2602
2603   if (strlen(arg) > 255) 
2604     return "mod_chxj: ChxjCookieMysqlHost is too long.";
2605
2606   dconf = (mod_chxj_config *)mconfig;
2607
2608   dconf->mysql.host = apr_pstrdup(cmd->pool, arg);
2609
2610   return NULL;
2611 }
2612
2613
2614 static const char *
2615 cmd_set_cookie_mysql_socket_path(
2616   cmd_parms   *cmd, 
2617   void        *mconfig, 
2618   const char  *arg)
2619 {
2620   mod_chxj_config  *dconf;
2621
2622   if (strlen(arg) > 4096) 
2623     return "mod_chxj: ChxjCookieMysqlSocketPath is too long.";
2624
2625   dconf = (mod_chxj_config *)mconfig;
2626
2627   dconf->mysql.socket_path = apr_pstrdup(cmd->pool, arg);
2628
2629   return NULL;
2630 }
2631
2632
2633 static const char *
2634 cmd_set_cookie_mysql_charset(
2635   cmd_parms   *cmd, 
2636   void        *mconfig, 
2637   const char  *arg)
2638 {
2639   mod_chxj_config  *dconf;
2640
2641   if (strlen(arg) > 255) 
2642     return "mod_chxj: ChxjCookieMysqlCharset is too long.";
2643
2644   dconf = (mod_chxj_config *)mconfig;
2645
2646   dconf->mysql.charset = apr_pstrdup(cmd->pool, arg);
2647
2648   return NULL;
2649 }
2650 #endif
2651 #if defined(USE_MEMCACHE_COOKIE)
2652 static const char *
2653 cmd_set_cookie_memcache_port(
2654   cmd_parms   *UNUSED(cmd), 
2655   void        *mconfig, 
2656   const char  *arg)
2657 {
2658   mod_chxj_config *dconf;
2659
2660   if (strlen(arg) > 255) 
2661     return "mod_chxj: ChxjCookieMemcachePort is too long.";
2662
2663   dconf = (mod_chxj_config *)mconfig;
2664
2665   if (chxj_chk_numeric(arg) != 0)
2666     return "mod_chxj: ChxjCookieMemcachePort is not numeric.";
2667
2668   dconf = (mod_chxj_config *)mconfig;
2669
2670   dconf->memcache.port = (apr_port_t)chxj_atoi(arg);
2671
2672   return NULL;
2673 }
2674
2675
2676 static const char *
2677 cmd_set_cookie_memcache_host(
2678   cmd_parms   *cmd, 
2679   void        *mconfig, 
2680   const char  *arg)
2681 {
2682   mod_chxj_config  *dconf;
2683
2684   if (strlen(arg) > 255) 
2685     return "mod_chxj: ChxjCookieMemcacheHost is too long.";
2686
2687   dconf = (mod_chxj_config *)mconfig;
2688
2689   dconf->memcache.host = apr_pstrdup(cmd->pool, arg);
2690
2691   return NULL;
2692 }
2693 #endif
2694
2695 static const char *
2696 cmd_set_cookie_lazy_mode(
2697   cmd_parms   *UNUSED(cmd), 
2698   void        *mconfig, 
2699   const char  *arg)
2700 {
2701   mod_chxj_config  *dconf;
2702
2703   if (strlen(arg) > 255) 
2704     return "mod_chxj: ChxjCookieLazyMode is too long.";
2705
2706   dconf = (mod_chxj_config *)mconfig;
2707
2708   if (strcasecmp("TRUE",arg) == 0) {
2709     dconf->cookie_lazy_mode = COOKIE_LAZY_ON;
2710   }
2711   else {
2712     dconf->cookie_lazy_mode = COOKIE_LAZY_OFF;
2713   }
2714
2715   return NULL;
2716 }
2717
2718 static const char *
2719 cmd_set_cookie_store_type(
2720   cmd_parms   *UNUSED(cmd), 
2721   void        *mconfig, 
2722   const char  *arg)
2723 {
2724   mod_chxj_config  *dconf;
2725
2726   if (strlen(arg) > 255) 
2727     return "mod_chxj: ChxjCookieStoreType is too long.";
2728
2729   dconf = (mod_chxj_config *)mconfig;
2730
2731   if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_DBM, arg) == 0) {
2732     dconf->cookie_store_type = COOKIE_STORE_TYPE_DBM;
2733   }
2734   else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MYSQL, arg) == 0) {
2735     dconf->cookie_store_type = COOKIE_STORE_TYPE_MYSQL;
2736   }
2737   else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MEMCACHE, arg) == 0) {
2738     dconf->cookie_store_type = COOKIE_STORE_TYPE_MEMCACHE;
2739   }
2740   else {
2741     dconf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
2742   }
2743
2744   return NULL;
2745 }
2746
2747 static const char *
2748 cmd_set_forward_url_base(
2749   cmd_parms   *cmd,
2750   void        *mconfig,
2751   const char  *arg)
2752 {
2753  mod_chxj_config *dconf;
2754
2755   if (strlen(arg) > 255)
2756     return "mod_chxj: ChxjForwardUrlBase is too long.";
2757
2758   dconf = (mod_chxj_config *)mconfig;
2759
2760   dconf->forward_url_base = apr_pstrdup(cmd->pool, arg);
2761
2762   return NULL;
2763 }
2764
2765 static const char *
2766 cmd_set_forward_server_ip(
2767   cmd_parms   *cmd,
2768   void        *mconfig,
2769   const char  *arg)
2770 {
2771   mod_chxj_config *dconf;
2772
2773   if (strlen(arg) > 255)
2774     return "mod_chxj: ChxjForwardServerIp is too long.";
2775
2776   dconf = (mod_chxj_config *)mconfig;
2777
2778   dconf->forward_server_ip = apr_pstrdup(cmd->pool, arg);
2779
2780   return NULL;
2781 }
2782
2783 static const char *
2784 cmd_allowed_cookie_domain(
2785   cmd_parms   *cmd,
2786   void        *mconfig,
2787   const char  *arg)
2788 {
2789   mod_chxj_config *dconf;
2790
2791   if (strlen(arg) > 255)
2792     return "mod_chxj: ChxjAllowedCookieDomain is too long.";
2793
2794   dconf = (mod_chxj_config *)mconfig;
2795
2796   dconf->allowed_cookie_domain = apr_pstrdup(cmd->pool, arg);
2797
2798   return NULL;
2799 }
2800
2801 static const char *
2802 cmd_set_new_line_type(
2803   cmd_parms   *UNUSED(cmd), 
2804   void        *mconfig, 
2805   const char  *arg)
2806 {
2807   mod_chxj_config  *dconf;
2808   if (strlen(arg) > 255)
2809     return "mod_chxj: ChxjNewLineType is too long.";
2810
2811   dconf = (mod_chxj_config *)mconfig;
2812
2813   if (strcasecmp(CHXJ_NEW_LINE_TYPE_CRLF, arg) == 0) {
2814     dconf->new_line_type = NLTYPE_CRLF;
2815   }
2816   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_LF, arg) == 0) {
2817     dconf->new_line_type = NLTYPE_LF;
2818   }
2819   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_CR, arg) == 0) {
2820     dconf->new_line_type = NLTYPE_CR;
2821   }
2822   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_NONE, arg) == 0) {
2823     dconf->new_line_type = NLTYPE_NONE;
2824   }
2825   else {
2826     return "mod_chxj: invalid value (ChxjNewLineType)";
2827   }
2828   return NULL;
2829 }
2830
2831
2832 static const command_rec cmds[] = {
2833   AP_INIT_TAKE1(
2834     "ChxjLoadDeviceData",
2835     cmd_load_device_data,
2836     NULL,
2837     OR_ALL,
2838     "Load Device Data"),
2839   AP_INIT_TAKE1(
2840     "ChxjLoadEmojiData",
2841     cmd_load_emoji_data,
2842     NULL,
2843     OR_ALL,
2844     "Load Emoji Data"),
2845   AP_INIT_TAKE1(
2846     "ChxjImageEngine",
2847     cmd_set_image_engine,
2848     NULL,
2849     OR_ALL,
2850     "Convert Target URI"),
2851   AP_INIT_TAKE1(
2852     "ChxjImageCacheDir",
2853     cmd_set_image_cache_dir,
2854     NULL,
2855     OR_ALL,
2856     "Image Cache Directory"),
2857   AP_INIT_TAKE1(
2858     "ChxjImageCacheLimit",
2859     cmd_set_image_cache_limit,
2860     NULL,
2861     OR_ALL,
2862     "Image Cache Limit"),
2863   AP_INIT_TAKE1(
2864     "ChxjImageCopyright",
2865     cmd_set_image_copyright,
2866     NULL,
2867     OR_ALL,
2868     "Copyright Flag"),
2869   AP_INIT_RAW_ARGS(
2870     "ChxjConvertRule",
2871     cmd_convert_rule,
2872     NULL, 
2873     OR_FILEINFO,
2874     "an URL-applied regexp-pattern and a substitution URL"),
2875   AP_INIT_TAKE1(
2876     "ChxjCookieDir",
2877     cmd_set_cookie_dir,
2878     NULL,
2879     OR_ALL,
2880     "save cookie.db directory."),
2881   AP_INIT_TAKE1(
2882     "ChxjCookieTimeout",
2883     cmd_set_cookie_timeout,
2884     NULL,
2885     OR_ALL,
2886     "The compulsion time-out time of the cookie is specified. "),
2887   AP_INIT_TAKE1(
2888     "ChxjCookieStoreType",
2889     cmd_set_cookie_store_type,
2890     NULL,
2891     OR_ALL,
2892     "It specifies preserving of the cookie ahead. (DBM/MYSQL/MEMCACHE)"),
2893   AP_INIT_TAKE1(
2894     "ChxjCookieLazyMode",
2895     cmd_set_cookie_lazy_mode,
2896     NULL,
2897     OR_ALL,
2898     "OneTimeID is negligently done. (TRUE/FALSE)"),
2899 #if defined(USE_MYSQL_COOKIE)
2900   AP_INIT_TAKE1(
2901     "ChxjCookieMysqlHost",
2902     cmd_set_cookie_mysql_host,
2903     NULL,
2904     OR_ALL,
2905     "The MySQL database host used by saving Cookie"),
2906   AP_INIT_TAKE1(
2907     "ChxjCookieMysqlPort",
2908     cmd_set_cookie_mysql_port,
2909     NULL,
2910     OR_ALL,
2911     "The MySQL database port used by saving Cookie"),
2912   AP_INIT_TAKE1(
2913     "ChxjCookieMysqlDatabase",
2914     cmd_set_cookie_mysql_database,
2915     NULL,
2916     OR_ALL,
2917     "The MySQL database name used by saving Cookie"),
2918   AP_INIT_TAKE1(
2919     "ChxjCookieMysqlUsername",
2920     cmd_set_cookie_mysql_username,
2921     NULL,
2922     OR_ALL,
2923     "The MySQL username used by saving Cookie"),
2924   AP_INIT_TAKE1(
2925     "ChxjCookieMysqlPassword",
2926     cmd_set_cookie_mysql_password,
2927     NULL,
2928     OR_ALL,
2929     "The MySQL password used by saving Cookie"),
2930   AP_INIT_TAKE1(
2931     "ChxjCookieMysqlTableName",
2932     cmd_set_cookie_mysql_table_name,
2933     NULL,
2934     OR_ALL,
2935     "The MySQL table name used by saving Cookie"),
2936   AP_INIT_TAKE1(
2937     "ChxjCookieMysqlSocketPath",
2938     cmd_set_cookie_mysql_socket_path,
2939     NULL,
2940     OR_ALL,
2941     "The MySQL socket path used by saving Cookie"),
2942   AP_INIT_TAKE1(
2943     "ChxjCookieMysqlCharset",
2944     cmd_set_cookie_mysql_charset,
2945     NULL,
2946     OR_ALL,
2947     "The MySQL charset used by saving Cookie"),
2948 #endif
2949 #if defined(USE_MEMCACHE_COOKIE)
2950   AP_INIT_TAKE1(
2951     "ChxjCookieMemcacheHost",
2952     cmd_set_cookie_memcache_host,
2953     NULL,
2954     OR_ALL,
2955     "The Memcached host used by saving Cookie"),
2956   AP_INIT_TAKE1(
2957     "ChxjCookieMemcachePort",
2958     cmd_set_cookie_memcache_port,
2959     NULL,
2960     OR_ALL,
2961     "The Memcached port used by saving Cookie"),
2962 #endif
2963   AP_INIT_TAKE1(
2964     "ChxjNewLineType",
2965     cmd_set_new_line_type,
2966     NULL,
2967     OR_ALL,
2968     "HTML new line type (NONE|CRLF|LF|CR). default is CRLF"),
2969   AP_INIT_TAKE1(
2970     "ChxjForwardUrlBase",
2971     cmd_set_forward_url_base,
2972     NULL,
2973     OR_ALL,
2974     "The forward url base(default: {request protocol}://{this server}:{this server port}"),
2975   AP_INIT_TAKE1(
2976     "ChxjForwardServerIp",
2977     cmd_set_forward_server_ip,
2978     NULL,
2979     OR_ALL,
2980     "The forward server ip(default: this server ip)"),
2981   AP_INIT_TAKE1(
2982     "ChxjAllowedCookieDomain",
2983     cmd_allowed_cookie_domain,
2984     NULL,
2985     OR_ALL,
2986     "Domain that permits parameter addition for cookie besides hostname.(Default:hostname Only)"),
2987   {NULL}
2988 };
2989
2990
2991 /*----------------------------------------------------------------------------*/
2992 /* Dispatch list for API hooks                                                */
2993 /*----------------------------------------------------------------------------*/
2994 module AP_MODULE_DECLARE_DATA chxj_module = {
2995   STANDARD20_MODULE_STUFF, 
2996   chxj_create_per_dir_config,          /* create per-dir    config structures */
2997   chxj_merge_per_dir_config,           /* merge  per-dir    config structures */
2998   chxj_config_server_create,           /* create per-server config structures */
2999   NULL,                                /* merge  per-server config structures */
3000   cmds,                                /* table of config file commands       */
3001   chxj_register_hooks                  /* register hooks                      */
3002 };
3003 /*
3004  * vim:ts=2 et
3005  */