OSDN Git Service

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