OSDN Git Service

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