OSDN Git Service

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