OSDN Git Service

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