OSDN Git Service

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