OSDN Git Service

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