OSDN Git Service

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