OSDN Git Service

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