OSDN Git Service

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