OSDN Git Service

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