OSDN Git Service

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