OSDN Git Service

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