OSDN Git Service

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