OSDN Git Service

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