OSDN Git Service

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