OSDN Git Service

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