OSDN Git Service

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