OSDN Git Service

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