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