OSDN Git Service

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