OSDN Git Service

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