OSDN Git Service

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