OSDN Git Service

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