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