OSDN Git Service

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