OSDN Git Service

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