OSDN Git Service

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