OSDN Git Service

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