OSDN Git Service

* Fixed bug.
[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 = 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, 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   apr_size_t          ii;
1478   int                 response_code = 0;
1479   
1480   DBG(r, "start of chxj_input_handler()");
1481
1482   if (strcasecmp(r->handler, "chxj-input-handler")) {
1483     DBG(r, "end chxj_input_handler()");
1484     return DECLINED;
1485   }
1486   apr_pool_create(&pool, r->pool);
1487
1488   dconf      = chxj_get_module_config(r->per_dir_config, &chxj_module);
1489   user_agent = (char*)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
1490   if (!user_agent) {
1491     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1492   }
1493   spec       = chxj_specified_device(r, user_agent);
1494   entryp     = chxj_apply_convrule(r, dconf->convrules);
1495
1496   post_data = apr_pstrdup(pool, "");
1497   if (ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK) == OK) {
1498     if (ap_should_client_block(r)) {
1499       while (post_data_len < CHXJ_POST_MAX) {
1500 #define BUFSZ (256)
1501         char buffer[BUFSZ];
1502         int read_bytes = ap_get_client_block(r, buffer, BUFSZ-1);
1503         if (read_bytes<=0) {
1504           break;
1505         }
1506         buffer[read_bytes] = '\0';
1507         post_data = apr_pstrcat(pool, post_data, buffer, NULL);
1508         post_data_len += read_bytes;
1509 #undef BUFSZ
1510       }
1511     }
1512   }
1513
1514   /* 
1515    * now convert.
1516    */
1517   if (post_data_len > 0) {
1518     post_data = chxj_input_convert(r, (const char**)&post_data, (apr_size_t*)&post_data_len, entryp, spec);
1519     DBG(r, "(in:exchange)POSTDATA:[%s]", post_data);
1520   }
1521
1522   char *url_path;
1523   if (dconf->forward_url_base) {
1524     url_path = apr_psprintf(pool, "%s%s", dconf->forward_url_base, r->uri);
1525   }
1526   else {
1527     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);
1528   }
1529   if (r->args) {
1530     url_path = apr_pstrcat(pool, url_path, "?", r->args, NULL);
1531   }
1532   DBG(r, "==> new url_path:[%s]", url_path);
1533
1534   apr_size_t res_len;
1535   apr_table_setn(r->headers_in, CHXJ_HEADER_ORIG_CLIENT_IP, r->connection->remote_ip);
1536   char *x_client_type = apr_table_get(r->headers_in, "X-Client-Type");
1537   if (x_client_type) {
1538     apr_table_setn(r->headers_in, CHXJ_HEADER_ORIG_CLIENT_TYPE, x_client_type); /* for mod_cidr_lookup */
1539   }
1540   else {
1541     apr_table_unset(r->headers_in, "X-Client-Type");
1542   }
1543   apr_table_unset(r->headers_in, "Content-Length");
1544   apr_table_setn(r->headers_in, "Content-Length", apr_psprintf(pool, "%" APR_SIZE_T_FMT, post_data_len));
1545   response = chxj_serf_post(r, pool, url_path, post_data, post_data_len, 1, &res_len, &response_code);
1546 #if 0
1547   DBG(r, "REQ[%X] -------------------------------------------------------", (unsigned int)(apr_size_t)r);
1548   DBG(r, "REQ[%X] response length:[%" APR_SIZE_T_FMT "]", (unsigned int)(apr_size_t)r, res_len);
1549   for (ii=0; ii<res_len/64; ii++) {
1550     DBG(r, "REQ[%X] response:[%.*s]", (unsigned int)(apr_size_t)r, 64, &response[ii*64]);
1551   }
1552   DBG(r, "REQ[%X] -------------------------------------------------------", (unsigned int)(apr_size_t)r);
1553 #endif
1554
1555   char *chunked;
1556   if ((chunked = (char *)apr_table_get(r->headers_out, "Transfer-Encoding")) != NULL) {
1557     if (strcasecmp(chunked, "chunked") == 0) {
1558       r->chunked = 1;  
1559       apr_table_unset(r->headers_out, "Transfer-Encoding");
1560     }
1561   }
1562   if (ap_is_HTTP_ERROR(response_code)) {
1563     DBG(r, "REQ[%X] end of chxj_input_handler() (HTTP-ERROR received. response code:[%d])", (unsigned int)(apr_size_t)r, response_code);
1564     return response_code;
1565   }
1566   {
1567     apr_pool_t *wpool;
1568     apr_pool_create(&wpool, r->pool);
1569     apr_bucket_brigade *bb;
1570     apr_bucket *e;
1571     apr_status_t rv;
1572     conn_rec *c = r->connection;
1573     
1574     bb = apr_brigade_create(wpool, c->bucket_alloc);
1575     e  = apr_bucket_transient_create(response, res_len, c->bucket_alloc);
1576     APR_BRIGADE_INSERT_TAIL(bb, e);
1577     e = apr_bucket_eos_create(c->bucket_alloc);
1578     APR_BRIGADE_INSERT_TAIL(bb, e);
1579     if ((rv = ap_pass_brigade(r->output_filters, bb)) != APR_SUCCESS) {
1580       ERR(r, "REQ[%X] %s:%d failed ap_pass_brigade()", (unsigned int)(apr_size_t)r, APLOG_MARK);
1581       return rv;
1582     }
1583     apr_brigade_cleanup(bb);
1584   }
1585
1586   DBG(r, "REQ[%X] end of chxj_input_handler()", (unsigned int)(apr_size_t)r);
1587   return APR_SUCCESS;
1588 }
1589
1590 static mod_chxj_global_config *
1591 chxj_global_config_create(apr_pool_t *pool, server_rec *s)
1592 {
1593   mod_chxj_global_config *conf;
1594
1595   SDBG(s, "start chxj_global_config_create()");
1596
1597   /*--------------------------------------------------------------------------*/
1598   /* allocate an own subpool which survives server restarts                   */
1599   /*--------------------------------------------------------------------------*/
1600   conf = (mod_chxj_global_config *)apr_palloc(pool, 
1601                   sizeof(mod_chxj_global_config));
1602 #if 0
1603   conf->cookie_db_lock = NULL;
1604 #endif
1605   SDBG(s, "end   chxj_global_config_create()");
1606
1607   return conf;
1608 }
1609
1610
1611 /**
1612  * initialize chxj module
1613  */
1614 static int 
1615 chxj_init_module(apr_pool_t *p, 
1616                  apr_pool_t *UNUSED(plog), 
1617                  apr_pool_t *UNUSED(ptemp), 
1618                  server_rec *s)
1619 {
1620   void *user_data;
1621   apr_status_t rv;
1622
1623   SDBG(s, "start chxj_init_module()");
1624
1625   apr_pool_userdata_get(&user_data, CHXJ_MOD_CONFIG_KEY, s->process->pool);
1626   SDBG(s, " ");
1627   if (user_data == NULL) {
1628     SDBG(s, " ");
1629     /*
1630      * dummy user_data set.
1631      */
1632     apr_pool_userdata_set(
1633       (const void *)(1), 
1634       CHXJ_MOD_CONFIG_KEY, 
1635       apr_pool_cleanup_null, 
1636       s->process->pool);
1637     SDBG(s, "end  chxj_init_module()");
1638     return OK;
1639   }
1640
1641   ap_add_version_component(p, CHXJ_VERSION_PREFIX CHXJ_VERSION);
1642
1643   if ((rv = apr_proc_mutex_create(&global_cookie_mutex, NULL,  APR_LOCK_FCNTL, s->process->pool)) != APR_SUCCESS) {
1644     char errstr[255];
1645     SERR(s, "%s:%d end chxj_init_module(). mutex create failure.(%d:%s)",APLOG_MARK, rv,apr_strerror(rv,errstr,255));
1646     return HTTP_INTERNAL_SERVER_ERROR;
1647   }
1648
1649   SDBG(s, "end  chxj_init_module()");
1650
1651   return OK;
1652 }
1653
1654
1655 static void 
1656 chxj_child_init(apr_pool_t *UNUSED(p), server_rec *s)
1657 {
1658   apr_status_t rv;
1659   SDBG(s, "start chxj_child_init()");
1660   if ((rv = apr_proc_mutex_child_init(&global_cookie_mutex, NULL, s->process->pool)) != APR_SUCCESS) {
1661     char errstr[255];
1662     SERR(s, "%s:%d ERROR end chxj_init_module(). mutex create failure.(%d:%s)", APLOG_MARK, rv,apr_strerror(rv,errstr,255));
1663   }
1664   SDBG(s, "end   chxj_child_init()");
1665 }
1666
1667
1668 /**
1669  * A set structure of each server is generated. 
1670  * 
1671  * @param p
1672  * @param s
1673  */
1674 static void *
1675 chxj_config_server_create(apr_pool_t *p, server_rec *s)
1676 {
1677   mod_chxj_global_config *gc;
1678
1679   gc = chxj_global_config_create(p,s);
1680
1681   return gc;
1682 }
1683
1684
1685 static int
1686 chxj_translate_name(request_rec *r)
1687 {
1688   DBG(r, "REQ[%X] =======================================================================", (unsigned int)(apr_size_t)r);
1689   DBG(r, "REQ[%X] ", (unsigned int)(apr_size_t)r);
1690   DBG(r, "REQ[%X] START REQUEST (uri:[%s] args:[%s])", (unsigned int)(apr_size_t)r, r->unparsed_uri, r->args ? r->args : "");
1691   DBG(r, "REQ[%X] METHOD [%s]", TO_ADDR(r), r->method);
1692   DBG(r, "REQ[%X] ", (unsigned int)(apr_size_t)r);
1693   DBG(r, "REQ[%X] =======================================================================", (unsigned int)(apr_size_t)r);
1694
1695   mod_chxj_config *dconf;
1696   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1697   /*
1698   if (dconf->image_rewrite ==CHXJ_IMG_REWRITE_ON ){
1699     if(r->args && strcasecmp(r->args,"rewrite") == 0){
1700       DBG(r, "image rewrite is ON [%s] - %s", dconf->image_rewrite_url ,r->content_type);
1701       r->filename = apr_pstrcat(r->pool,dconf->image_rewrite_url,NULL);
1702       return OK;
1703     }
1704   }
1705   */
1706
1707 #if 0
1708   return chxj_trans_name(r);
1709 #else
1710   return DECLINED;
1711 #endif
1712 }
1713
1714
1715 static void 
1716 chxj_insert_filter(request_rec *r)
1717 {
1718   char                *user_agent;
1719   device_table        *spec;
1720   mod_chxj_config     *dconf;
1721   chxjconvrule_entry  *entryp;
1722   mod_chxj_ctx        *ctx;
1723   apr_status_t        rv;
1724   char                *contentType;
1725
1726   DBG(r, "REQ[%X] start chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1727
1728   dconf = chxj_get_module_config(r->per_dir_config, &chxj_module);
1729
1730   /* we get User-Agent from CHXJ_HTTP_USER_AGENT header if any */
1731   user_agent = (char *)apr_table_get(r->headers_in, CHXJ_HTTP_USER_AGENT);
1732   if (!user_agent) {
1733     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
1734   }
1735
1736   contentType = (char *)apr_table_get(r->headers_in, "Content-Type");
1737   if (contentType
1738       && strncasecmp("multipart/form-data", contentType, 19) == 0) {
1739     DBG(r, "REQ[%X] detect multipart/form-data ==> no target", (unsigned int)(apr_size_t)r);
1740     DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1741     return;
1742   }
1743
1744   spec = chxj_specified_device(r, user_agent);
1745   entryp = chxj_apply_convrule(r, dconf->convrules);
1746   if (!entryp) {
1747     DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1748     return;
1749   }
1750   ctx = apr_palloc(r->pool, sizeof(*ctx));
1751   memset(ctx, 0, sizeof(*ctx));
1752   if ((rv = apr_pool_create(&ctx->pool, r->pool)) != APR_SUCCESS) {
1753     ERR(r, "%s:%d: failed: new pool create. rv:[%d]", __FILE__,__LINE__,rv);
1754     DBG(r, "REQ:[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1755     return;
1756   }
1757   ctx->entryp = entryp;
1758   ctx->spec   = spec;
1759   ctx->buffer = apr_palloc(ctx->pool, 1);
1760   ctx->buffer[0] = 0;
1761
1762   if (!entryp || (!(entryp->action & CONVRULE_ENGINE_ON_BIT) && !(entryp->action & CONVRULE_COOKIE_ONLY_BIT))) {
1763     DBG(r,"REQ[%X] EngineOff", (unsigned int)(apr_size_t)r);
1764     DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1765     return;
1766   }
1767
1768   switch(spec->html_spec_type) {
1769   case CHXJ_SPEC_Chtml_1_0:
1770   case CHXJ_SPEC_Chtml_2_0:
1771   case CHXJ_SPEC_Chtml_3_0:
1772   case CHXJ_SPEC_Chtml_4_0:
1773   case CHXJ_SPEC_Chtml_5_0:
1774   case CHXJ_SPEC_Chtml_6_0:
1775   case CHXJ_SPEC_Chtml_7_0:
1776   case CHXJ_SPEC_XHtml_Mobile_1_0:
1777   case CHXJ_SPEC_Hdml:
1778   case CHXJ_SPEC_Jhtml:
1779   case CHXJ_SPEC_Jxhtml:
1780     break;
1781
1782   default:
1783     DBG(r, "REQ[%X] end chxj_insert_filter() Unknown spec type(%d).", (unsigned int)(apr_size_t)r, spec->html_spec_type);
1784     return;
1785   }
1786
1787
1788   if (! apr_table_get(r->headers_in, "X-Chxj-Forward")) {
1789     ap_add_output_filter("chxj_output_filter", ctx, r, r->connection);
1790     DBG(r, "REQ[%X] added Output Filter", (unsigned int)(apr_size_t)r);
1791   }
1792
1793   DBG(r, "REQ[%X] end chxj_insert_filter()", (unsigned int)(apr_size_t)r);
1794 }
1795
1796
1797 /**
1798  * The hook is registered.
1799  *
1800  * @param p
1801  */
1802 static void 
1803 chxj_register_hooks(apr_pool_t *UNUSED(p))
1804 {
1805   ap_hook_post_config(chxj_init_module,
1806                       NULL,
1807                       NULL,
1808                       APR_HOOK_REALLY_FIRST);
1809   ap_hook_child_init(chxj_child_init, 
1810                      NULL, 
1811                      NULL, 
1812                      APR_HOOK_REALLY_FIRST);
1813   ap_register_output_filter (
1814                       "chxj_output_filter", 
1815                       chxj_output_filter, 
1816                       NULL, 
1817                       AP_FTYPE_RESOURCE);
1818   ap_hook_insert_filter(chxj_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
1819   ap_hook_handler(chxj_img_conv_format_handler, NULL, NULL, APR_HOOK_MIDDLE);
1820   ap_hook_handler(chxj_qr_code_handler, NULL, NULL, APR_HOOK_MIDDLE);
1821   ap_hook_handler(chxj_input_handler, NULL, NULL, APR_HOOK_MIDDLE);
1822
1823   ap_hook_handler(chxj_image_redirect_handler, NULL, NULL, APR_HOOK_MIDDLE);
1824
1825   ap_hook_translate_name(chxj_translate_name, NULL, NULL, APR_HOOK_MIDDLE);
1826   ap_hook_fixups(chxj_headers_fixup, NULL, NULL, APR_HOOK_FIRST);
1827
1828 }
1829
1830
1831 /**
1832  * A set structure according to the directory is generated. 
1833  *
1834  * @param p
1835  * @param arg
1836  */
1837 static void * 
1838 chxj_create_per_dir_config(apr_pool_t *p, char *arg) 
1839 {
1840   mod_chxj_config *conf;
1841
1842   conf = apr_pcalloc(p, sizeof(mod_chxj_config));
1843   conf->device_data_file = NULL;
1844   conf->devices          = NULL;
1845   conf->emoji_data_file  = NULL;
1846   conf->emoji            = NULL;
1847   conf->emoji_tail       = NULL;
1848   conf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_NONE;
1849   conf->image            = CHXJ_IMG_NONE;
1850   conf->image_cache_dir  = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
1851   conf->image_cache_limit = 0;
1852   conf->server_side_encoding = NULL;
1853   conf->cookie_db_dir    = NULL;
1854   conf->cookie_timeout   = 0;
1855   conf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
1856   conf->cookie_lazy_mode  = 0;
1857   conf->cookie_dbm_type  = NULL;
1858   
1859   conf->detect_device_type = CHXJ_ADD_DETECT_DEVICE_TYPE_NONE;
1860   
1861 #if defined(USE_MYSQL_COOKIE)
1862   memset((void *)&conf->mysql, 0, sizeof(mysql_t));
1863   conf->mysql.port       = MYSQL_PORT;
1864   conf->mysql.host       = NULL;
1865 #endif
1866 #if defined(USE_MEMCACHE_COOKIE)
1867   memset((void *)&conf->memcache, 0, sizeof(memcache_t));
1868   conf->memcache.host    = NULL;
1869   conf->memcache.port    = 0;
1870 #endif
1871   conf->forward_url_base = NULL;
1872   conf->forward_server_ip = NULL;
1873   conf->allowed_cookie_domain = NULL;
1874   conf->post_log              = NULL;
1875
1876   if (arg == NULL) {
1877     conf->dir                  = NULL;
1878   }
1879   else {
1880     conf->dir                  = apr_pcalloc(p, strlen(arg)+1);
1881     memset(conf->dir, 0, strlen(arg)+1);
1882     strcpy(conf->dir, arg);
1883   }
1884   conf->convrules   = apr_array_make(p, 2, sizeof(chxjconvrule_entry));
1885
1886   /* Default is copyleft */
1887   conf->image_copyright = NULL; 
1888
1889   conf->image_rewrite = CHXJ_IMG_REWRITE_NONE;
1890   conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_NONE;
1891   conf->image_rewrite_url = NULL;
1892
1893   return conf;
1894 }
1895
1896
1897 /*
1898  *  Merge per-directory CHXJ configurations
1899  */
1900 static void *
1901 chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
1902 {
1903   mod_chxj_config *base;
1904   mod_chxj_config *add;
1905   mod_chxj_config *mrg;
1906
1907   base = (mod_chxj_config *)basev;
1908   add  = (mod_chxj_config *)addv;
1909   mrg  = (mod_chxj_config *)apr_palloc(p, sizeof(mod_chxj_config));
1910
1911   mrg->device_data_file = NULL;
1912   mrg->devices          = NULL;
1913   mrg->emoji_data_file  = NULL;
1914   mrg->image            = CHXJ_IMG_NONE;
1915   mrg->image_cache_dir  = NULL;
1916   mrg->image_copyright  = NULL;
1917   mrg->image_cache_limit  = 0;
1918   mrg->emoji            = NULL;
1919   mrg->emoji_tail       = NULL;
1920   mrg->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_NONE;
1921   mrg->new_line_type    = NLTYPE_NIL;
1922   mrg->forward_url_base = NULL;
1923   mrg->forward_server_ip = NULL;
1924   mrg->allowed_cookie_domain = NULL;
1925   mrg->post_log         = NULL;
1926   mrg->cookie_dbm_type  = NULL;
1927   
1928   mrg->device_keys      = NULL;
1929   mrg->device_hash      = NULL;
1930
1931   mrg->dir = apr_pstrdup(p, add->dir);
1932
1933   if (! add->device_data_file) {
1934     mrg->devices = base->devices;
1935     mrg->device_data_file = apr_pstrdup(p, base->device_data_file);
1936   }
1937   else {
1938     mrg->devices = add->devices;
1939     mrg->device_data_file = apr_pstrdup(p, add->device_data_file);
1940   }
1941
1942   if (! add->emoji_data_file) {
1943     mrg->emoji = base->emoji;
1944     mrg->emoji_tail = base->emoji_tail;
1945     mrg->emoji_data_file = apr_pstrdup(p, base->emoji_data_file);
1946   }
1947   else {
1948     mrg->emoji = add->emoji;
1949     mrg->emoji_tail = add->emoji_tail;
1950     mrg->emoji_data_file = apr_pstrdup(p, add->emoji_data_file);
1951   }
1952
1953   if (add->image == CHXJ_IMG_NONE) {
1954     mrg->image = base->image;
1955   }
1956   else {
1957     mrg->image = add->image;
1958   }
1959
1960   if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) {
1961     mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
1962   }
1963   else {
1964     mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
1965   }
1966
1967   if (add->image_cache_limit) {
1968     mrg->image_cache_limit = add->image_cache_limit;
1969   }
1970   else {
1971     mrg->image_cache_limit = base->image_cache_limit;
1972   }
1973
1974   if (add->image_copyright) 
1975     mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
1976   else
1977     mrg->image_copyright = apr_pstrdup(p, base->image_copyright);
1978
1979   if (add->server_side_encoding) {
1980     mrg->server_side_encoding = apr_pstrdup(p, add->server_side_encoding);
1981   }
1982   else 
1983   if (base->server_side_encoding) {
1984     mrg->server_side_encoding = apr_pstrdup(p, base->server_side_encoding);
1985   }
1986   else {
1987     mrg->server_side_encoding = apr_pstrdup(p, DEFAULT_SERVER_SIDE_ENCODING);
1988   }
1989
1990   mrg->convrules    = apr_array_append(p, add->convrules, base->convrules);
1991
1992   if (add->cookie_db_dir) {
1993     mrg->cookie_db_dir = apr_pstrdup(p, add->cookie_db_dir);
1994   }
1995   else
1996   if (base->cookie_db_dir) {
1997     mrg->cookie_db_dir = apr_pstrdup(p, base->cookie_db_dir);
1998   }
1999   else {
2000     mrg->cookie_db_dir = NULL;
2001   }
2002
2003   if (add->cookie_timeout) {
2004     mrg->cookie_timeout   = add->cookie_timeout;
2005   }
2006   else
2007   if (base->cookie_db_dir) {
2008     mrg->cookie_timeout   = base->cookie_timeout;
2009   }
2010   else {
2011     mrg->cookie_timeout   = 0;
2012   }
2013
2014 #if defined(USE_MYSQL_COOKIE)
2015   if (add->mysql.host) {
2016     mrg->mysql.host = apr_pstrdup(p, add->mysql.host);
2017   }
2018   else if (base->mysql.host) {
2019     mrg->mysql.host = apr_pstrdup(p, base->mysql.host);
2020   }
2021   else {
2022     mrg->mysql.host = NULL;
2023   }
2024   if (add->mysql.port) {
2025     mrg->mysql.port = add->mysql.port;
2026   }
2027   else if (base->mysql.port) {
2028     mrg->mysql.port = base->mysql.port;
2029   }
2030   else {
2031     mrg->mysql.port = 0;
2032   }
2033
2034   if (add->mysql.database) {
2035     mrg->mysql.database = apr_pstrdup(p, add->mysql.database);
2036   }
2037   else if (base->mysql.database) {
2038     mrg->mysql.database = apr_pstrdup(p, base->mysql.database);
2039   }
2040   else {
2041     mrg->mysql.database = NULL;
2042   }
2043
2044   if (add->mysql.username) {
2045     mrg->mysql.username = apr_pstrdup(p, add->mysql.username);
2046   }
2047   else if (base->mysql.username) {
2048     mrg->mysql.username = apr_pstrdup(p, base->mysql.username);
2049   }
2050   else {
2051     mrg->mysql.username = NULL;
2052   }
2053
2054   if (add->mysql.password) {
2055     mrg->mysql.password = apr_pstrdup(p, add->mysql.password);
2056   }
2057   else if (base->mysql.password) {
2058     mrg->mysql.password = apr_pstrdup(p, base->mysql.password);
2059   }
2060   else {
2061     mrg->mysql.password = NULL;
2062   }
2063
2064   if (add->mysql.tablename) {
2065     mrg->mysql.tablename = apr_pstrdup(p, add->mysql.tablename);
2066   }
2067   else if (base->mysql.tablename) {
2068     mrg->mysql.tablename = apr_pstrdup(p, base->mysql.tablename);
2069   }
2070   else {
2071     mrg->mysql.tablename = NULL;
2072   }
2073
2074   if (add->mysql.socket_path) {
2075     mrg->mysql.socket_path = apr_pstrdup(p, add->mysql.socket_path);
2076   }
2077   else if (base->mysql.socket_path) {
2078     mrg->mysql.socket_path = apr_pstrdup(p, base->mysql.socket_path);
2079   }
2080   else {
2081     mrg->mysql.socket_path = NULL;
2082   }
2083
2084   if (add->mysql.charset) {
2085     mrg->mysql.charset = apr_pstrdup(p, add->mysql.charset);
2086   }
2087   else if (base->mysql.charset) {
2088     mrg->mysql.charset = apr_pstrdup(p, base->mysql.charset);
2089   }
2090   else {
2091     mrg->mysql.charset = NULL;
2092   }
2093 #endif
2094 #if defined(USE_MEMCACHE_COOKIE)
2095   if (add->memcache.host) {
2096     mrg->memcache.host = apr_pstrdup(p, add->memcache.host);
2097   }
2098   else if (base->memcache.host) {
2099     mrg->memcache.host = apr_pstrdup(p, base->memcache.host);
2100   }
2101   else {
2102     mrg->memcache.host = NULL;
2103   }
2104   if (add->memcache.port) {
2105     mrg->memcache.port = add->memcache.port;
2106   }
2107   else if (base->memcache.port) {
2108     mrg->memcache.port = base->memcache.port;
2109   }
2110   else {
2111     mrg->memcache.port = 0;
2112   }
2113 #endif
2114   if (add->cookie_store_type) {
2115     mrg->cookie_store_type = add->cookie_store_type;
2116   }
2117   else if (base->cookie_store_type) {
2118     mrg->cookie_store_type = base->cookie_store_type;
2119   }
2120   else {
2121     mrg->cookie_store_type = COOKIE_STORE_TYPE_NONE;
2122   }
2123   if (add->cookie_lazy_mode) {
2124     mrg->cookie_lazy_mode = add->cookie_lazy_mode;
2125   }
2126   else if (base->cookie_lazy_mode) {
2127     mrg->cookie_lazy_mode = base->cookie_lazy_mode;
2128   }
2129   else {
2130     mrg->cookie_lazy_mode = 0;
2131   }
2132   if (add->new_line_type) {
2133     mrg->new_line_type = add->new_line_type;
2134   }
2135   else if (base->new_line_type) {
2136     mrg->new_line_type = base->new_line_type;
2137   }
2138   else {
2139     mrg->new_line_type = NLTYPE_NIL;
2140   }
2141
2142   if (add->forward_url_base) {
2143     mrg->forward_url_base = add->forward_url_base;
2144   }
2145   else if (base->forward_url_base) {
2146     mrg->forward_url_base = base->forward_url_base;
2147   }
2148
2149   if (add->allowed_cookie_domain) {
2150     mrg->allowed_cookie_domain = add->allowed_cookie_domain;
2151   }
2152   else {
2153     mrg->allowed_cookie_domain = base->allowed_cookie_domain;
2154   }
2155   if (add->post_log) {
2156     mrg->post_log = add->post_log;
2157   }
2158   else {
2159     mrg->post_log = base->post_log;
2160   }
2161   if (add->cookie_dbm_type) {
2162     mrg->cookie_dbm_type = add->cookie_dbm_type;
2163   }
2164   else {
2165     mrg->cookie_dbm_type = base->cookie_dbm_type;
2166   }
2167   
2168   if (add->imode_emoji_color == CHXJ_IMODE_EMOJI_COLOR_NONE) {
2169     mrg->imode_emoji_color = base->imode_emoji_color;
2170   }
2171   else {
2172     mrg->imode_emoji_color = add->imode_emoji_color;
2173   }
2174   
2175   if (add->detect_device_type == CHXJ_ADD_DETECT_DEVICE_TYPE_NONE) {
2176     mrg->detect_device_type = base->detect_device_type;
2177   }
2178   else {
2179     mrg->detect_device_type = add->detect_device_type;
2180   }
2181   
2182   if (add->device_keys) {
2183     mrg->device_keys = add->device_keys;
2184   }
2185   else{
2186     mrg->device_keys = base->device_keys;
2187   }
2188   
2189   if (add->device_hash) {
2190     mrg->device_hash = add->device_hash;
2191   }
2192   else{
2193     mrg->device_hash = base->device_hash;
2194   }
2195   
2196   if (add->image_rewrite == CHXJ_IMG_REWRITE_NONE){
2197     mrg->image_rewrite = base->image_rewrite;
2198   }
2199   else{
2200     mrg->image_rewrite = add->image_rewrite;
2201   }
2202
2203   if (add->image_rewrite_url) {
2204     mrg->image_rewrite_url = add->image_rewrite_url;
2205   }
2206   else{
2207     mrg->image_rewrite_url = base->image_rewrite_url;
2208   }
2209
2210   if (add->image_rewrite_mode == CHXJ_IMG_REWRITE_MODE_NONE){
2211     mrg->image_rewrite_mode = base->image_rewrite_mode;
2212   }
2213   else{
2214     mrg->image_rewrite_mode = add->image_rewrite_mode;
2215   }
2216
2217   return mrg;
2218 }
2219
2220
2221 static int
2222 chxj_command_parse_take5(
2223   const char *arg, 
2224   char       **prm1, 
2225   char       **prm2, 
2226   char       **prm3, 
2227   char       **prm4, 
2228   char       **prm5)
2229 {
2230   int  isquoted;
2231   char *strp;
2232
2233   strp = (char *)arg;
2234
2235   for (;*strp == ' '||*strp == '\t'; strp++) ;
2236
2237   isquoted = 0; 
2238   if (*strp == '"') { 
2239     isquoted = 1;
2240     strp++;
2241   }
2242
2243   *prm1 = strp;
2244
2245   for (; *strp != '\0'; strp++) {
2246     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2247     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2248       strp++;
2249       continue;
2250     }
2251
2252     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2253     ||  (isquoted  && *strp == '"'))
2254       break;
2255   }
2256
2257   if (! *strp) {
2258     *prm2 = strp;
2259     *prm3 = strp;
2260     *prm4 = strp;
2261     *prm5 = strp;
2262     return 1;
2263   }
2264
2265   *strp++ = '\0';
2266
2267   for (;*strp == ' '||*strp == '\t'; strp++) ;
2268
2269   isquoted = 0; 
2270   if (*strp == '"') { 
2271     isquoted = 1;
2272     strp++;
2273   }
2274
2275   *prm2 = strp;
2276   for (; *strp != '\0'; strp++) {
2277     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2278     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2279       strp++;
2280       continue;
2281     }
2282
2283     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2284     ||  (isquoted  && *strp == '"'))
2285       break;
2286   }
2287
2288   if (! *strp) {
2289     *prm3 = strp;
2290     *prm4 = strp;
2291     *prm5 = strp;
2292     return 0;
2293   }
2294
2295   *strp++ = '\0';
2296
2297   for (;*strp == ' '||*strp == '\t'; strp++);
2298
2299   isquoted = 0; 
2300   if (*strp == '"') { 
2301     isquoted = 1;
2302     strp++;
2303   }
2304   *prm3 = strp;
2305   for (; *strp != '\0'; strp++) {
2306     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2307     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2308       strp++;
2309       continue;
2310     }
2311
2312     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2313     ||  (isquoted  && *strp == '"'))
2314       break;
2315   }
2316
2317   if (! *strp) {
2318     *prm4 = strp;
2319     *prm5 = strp;
2320     return 0;
2321   }
2322
2323   *strp++ = '\0';
2324
2325   for (;*strp == ' '||*strp == '\t'; strp++);
2326
2327   isquoted = 0; 
2328   if (*strp == '"') { 
2329     isquoted = 1;
2330     strp++;
2331   }
2332   *prm4 = strp;
2333   for (; *strp != '\0'; strp++) {
2334     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2335     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2336       strp++;
2337       continue;
2338     }
2339
2340     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2341     ||  (isquoted  && *strp == '"'))
2342       break;
2343   }
2344
2345   if (! *strp) {
2346     *prm5 = strp;
2347     return 0;
2348   }
2349
2350   *strp++ = '\0';
2351
2352   for (;*strp == ' '||*strp == '\t'; strp++);
2353
2354   isquoted = 0; 
2355   if (*strp == '"') { 
2356     isquoted = 1;
2357     strp++;
2358   }
2359   *prm5 = strp;
2360   for (; *strp != '\0'; strp++) {
2361     if ((isquoted && (*strp == ' ' || *strp == '\t'))
2362     ||  (*strp == '\\' && (*(strp+1) == ' ' || *(strp+1) == '\t'))) {
2363       strp++;
2364       continue;
2365     }
2366
2367     if ((!isquoted && (*strp == ' ' || *strp == '\t'))
2368     ||  (isquoted  && *strp == '"'))
2369       break;
2370   }
2371   *strp = '\0';
2372
2373   return 0;
2374 }
2375
2376
2377 /**
2378  * The device definition file is loaded. 
2379  *
2380  * @param arg     [i]   The name of the device definition file is specified.
2381  * @param mconfig [i/o] The pointer to a set structure is specified. 
2382  * @param parms   [i]   
2383  */
2384 static const char * 
2385 cmd_load_device_data(cmd_parms *parms, void *mconfig, const char *arg) 
2386 {
2387   mod_chxj_config  *conf;
2388   Doc              doc;
2389
2390   doc.r = NULL;
2391
2392   if (strlen(arg) > 256) 
2393     return "mod_chxj: device data filename too long.";
2394
2395   conf = (mod_chxj_config *)mconfig;
2396   conf->device_data_file = apr_pstrdup(parms->pool, arg);
2397
2398   qs_init_malloc(&doc);
2399   qs_init_root_node(&doc);
2400
2401   qs_parse_file((Doc *)&doc, (const char *)arg);
2402   chxj_load_device_data(&doc,parms->pool, conf);
2403   qs_all_free(&doc, QX_LOGMARK);
2404
2405   return NULL;
2406 }
2407
2408
2409 /**
2410  * Device definition information is loaded. 
2411  *
2412  * @param parms 
2413  * @param arg     [i]   The name of the device definition file is specified. 
2414  * @param mconfig [i/o] The pointer to a set structure is specified. 
2415  * @return 
2416  */
2417 static const char * 
2418 cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char *arg) 
2419 {
2420   mod_chxj_config *conf;
2421   char            *rtn;
2422   Doc              doc;
2423
2424   doc.r = NULL;
2425
2426
2427   if (strlen(arg) > 256) 
2428     return "mod_chxj: emoji data filename too long.";
2429
2430   conf = (mod_chxj_config *)mconfig;
2431   conf->emoji_data_file = apr_pstrdup(parms->pool, arg);
2432   qs_init_malloc(&doc);
2433   qs_init_root_node(&doc);
2434
2435   qs_parse_file((Doc *)&doc, (const char *)arg);
2436
2437   rtn = chxj_load_emoji_data(&doc,parms->pool, conf);
2438
2439   qs_all_free(&doc, QX_LOGMARK);
2440
2441
2442   return rtn;
2443 }
2444
2445
2446 static const char * 
2447 cmd_set_image_engine(cmd_parms * UNUSED(parms), void *mconfig, const char *arg) 
2448 {
2449   mod_chxj_config *conf;
2450   Doc              doc;
2451
2452   doc.r = NULL;
2453
2454   if (strlen(arg) > 256) 
2455     return "image uri is too long.";
2456
2457   conf = (mod_chxj_config*)mconfig;
2458   if (strcasecmp("ON", arg) == 0) {
2459     conf->image = CHXJ_IMG_ON;
2460   }
2461   else {
2462     conf->image = CHXJ_IMG_OFF;
2463   }
2464
2465   return NULL;
2466 }
2467
2468
2469 static const char * 
2470 cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char *arg) 
2471 {
2472   mod_chxj_config *conf;
2473   Doc              doc;
2474
2475   doc.r = NULL;
2476
2477   if (strlen(arg) > 256) 
2478     return "cache dir name is too long.";
2479   
2480   apr_finfo_t info;
2481   apr_status_t res = apr_stat(&info,arg,APR_FINFO_TYPE,parms->pool);
2482   if(res != APR_SUCCESS){
2483     return apr_psprintf(parms->pool,"ChxjImageCacheDir [%s]: not found ",arg);
2484   }
2485   else{
2486     if(info.filetype != APR_DIR){
2487       return apr_psprintf(parms->pool,"ChxjImageCacheDir [%s]: is not directory ",arg);
2488     }
2489   }
2490
2491   conf = (mod_chxj_config *)mconfig;
2492   conf->image_cache_dir = apr_pstrdup(parms->pool, arg);
2493
2494   return NULL;
2495 }
2496
2497
2498 static const char * 
2499 cmd_set_image_cache_limit(cmd_parms *parms, void *mconfig, const char *arg) 
2500 {
2501   mod_chxj_config *conf;
2502   Doc              doc;
2503
2504   doc.r = NULL;
2505
2506   if (strlen(arg) > IMAGE_CACHE_LIMIT_FMT_LEN) 
2507     return "cache size is too long.";
2508
2509   conf = (mod_chxj_config *)mconfig;
2510   errno = 0;
2511   /* 
2512    * I use strtol function because strtoul is not portable function. 
2513    */
2514   conf->image_cache_limit = (unsigned long)strtol(arg, NULL, 10);
2515   switch (errno) {
2516   case EINVAL:
2517     return apr_psprintf(parms->pool, "ChxjImageCacheLimit invalid value [%s] errno:[%d]", arg, errno);
2518   case ERANGE:
2519     return apr_psprintf(parms->pool, "ChxjImageCacheLimit Out of range [%s] errno:[%d]", arg, errno);
2520   default:
2521     break;
2522   }
2523   return NULL;
2524 }
2525
2526
2527 static const char * 
2528 cmd_set_image_copyright(cmd_parms *parms, void *mconfig, const char *arg) 
2529 {
2530   mod_chxj_config *conf;
2531   Doc              doc;
2532
2533   doc.r = NULL;
2534
2535   if (strlen(arg) > 256) 
2536     return "Copyright Flag is too long.";
2537
2538   conf = (mod_chxj_config *)mconfig;
2539   conf->image_copyright = apr_pstrdup(parms->pool, arg);
2540
2541   return NULL;
2542 }
2543
2544
2545 static const char *
2546 cmd_convert_rule(cmd_parms *cmd, void *mconfig, const char *arg)
2547 {
2548   int                 mode;
2549   ap_regex_t          *regexp;
2550   mod_chxj_config     *dconf;
2551   chxjconvrule_entry  *newrule;
2552   char                *prm1;
2553   char                *prm2;
2554   char                *prm3;
2555   char                *prm4;
2556   char                *prm5;
2557   char                *pstate;
2558   char                *action;
2559   char                *pp;
2560
2561   dconf = (mod_chxj_config *)mconfig;
2562
2563   if (strlen(arg) > 4096) 
2564     return "mod_chxj: ChxjConvertRule: is too long.";
2565
2566   dconf = (mod_chxj_config *)mconfig;
2567   if (dconf->convrules == NULL)
2568     dconf->convrules   = apr_array_make(cmd->pool, 
2569                                         2, 
2570                                         sizeof(chxjconvrule_entry));
2571
2572   newrule = apr_array_push(dconf->convrules);
2573
2574   newrule->flags  = 0;
2575   newrule->action = 0;
2576
2577   if (chxj_command_parse_take5(arg, &prm1, &prm2, &prm3, &prm4, &prm5))
2578     return "ChxjConvertRule: bad argument line";
2579
2580   newrule->pattern = apr_pstrdup(cmd->pool, prm1);
2581
2582   /* Parse action */
2583   for (;;) {
2584     if ((action = apr_strtok(prm2, ",", &pstate)) == NULL)
2585       break;
2586     prm2 = NULL;
2587     switch(*action) {
2588     case 'e':
2589     case 'E':
2590       if (strcasecmp(CONVRULE_ENGINE_ON_CMD, action) == 0) {
2591         newrule->action |= CONVRULE_ENGINE_ON_BIT;
2592       }
2593       else
2594       if (strcasecmp(CONVRULE_ENGINE_OFF_CMD, action) == 0) {
2595         newrule->action |= CONVRULE_ENGINE_OFF_BIT;
2596       }
2597       else
2598       if (strcasecmp(CONVRULE_EMOJI_ONLY_CMD, action) == 0) {
2599         newrule->action |= CONVRULE_EMOJI_ONLY_BIT;
2600       }
2601       else
2602       if (strcasecmp(CONVRULE_ENVINFO_ONLY_CMD, action) == 0) {
2603         newrule->action |= CONVRULE_ENVINFO_ONLY_BIT;
2604       }
2605       break;
2606
2607     case 'O':
2608     case 'o':
2609       if (strcasecmp(CONVRULE_OVERWRITE_X_CLIENT_TYPE_CMD, action) == 0) {
2610         newrule->action |= CONVRULE_OVERWRITE_X_CLIENT_TYPE_BIT;
2611       }
2612       break;
2613
2614     case 'C':
2615     case 'c':
2616       if (strcasecmp(CONVRULE_COOKIE_ON_CMD, action) == 0) {
2617         newrule->action |= CONVRULE_COOKIE_ON_BIT;
2618       }
2619       else if (strcasecmp(CONVRULE_COOKIE_OFF_CMD, action) == 0) {
2620         newrule->action &= (0xffffffff ^ CONVRULE_COOKIE_ON_BIT);
2621       }
2622       else if (strcasecmp(CONVRULE_CSS_ON_CMD, action) == 0) {
2623         newrule->action |= CONVRULE_CSS_ON_BIT;
2624       }
2625       else if (strcasecmp(CONVRULE_CSS_OFF_CMD, action) == 0) {
2626         newrule->action &= (0xffffffff ^ CONVRULE_CSS_ON_BIT);
2627       }
2628       else if (strcasecmp(CONVRULE_COOKIE_ONLY_CMD, action) == 0) {
2629         newrule->action |= CONVRULE_COOKIE_ONLY_BIT;
2630       }
2631       break;
2632
2633     case 'J':
2634     case 'j':
2635       if (strcasecmp(CONVRULE_JRCONV_OFF_CMD, action) == 0) {
2636         newrule->action |= CONVRULE_JRCONV_OFF_BIT;
2637       }
2638       break;
2639
2640     case 'N':
2641     case 'n':
2642       if (strcasecmp(CONVRULE_NOCACHE_ON_CMD, action) == 0) {
2643         newrule->action |= CONVRULE_NOCACHE_ON_BIT;
2644       }
2645       break;
2646
2647     case 'Q':
2648     case 'q':
2649       if (strcasecmp(CONVRULE_QSCONV_OFF_CMD, action) == 0) {
2650         newrule->action |= CONVRULE_QSCONV_OFF_BIT;
2651       }
2652       break;
2653
2654     case 'Z':
2655     case 'z':
2656       if (strcasecmp(CONVRULE_Z2H_ON_CMD, action) == 0) {
2657         newrule->action |= CONVRULE_Z2H_ON_BIT;
2658       }
2659       else
2660       if (strcasecmp(CONVRULE_Z2H_OFF_CMD, action) == 0) {
2661         newrule->action |= CONVRULE_Z2H_OFF_BIT;
2662       }
2663       else
2664       if (strcasecmp(CONVRULE_Z2H_ALPHA_ON_CMD, action) == 0) {
2665         newrule->action |= CONVRULE_Z2H_ALPHA_ON_BIT;
2666       }
2667       else
2668       if (strcasecmp(CONVRULE_Z2H_ALPHA_OFF_CMD, action) == 0) {
2669         newrule->action |= CONVRULE_Z2H_ALPHA_OFF_BIT;
2670       }
2671       else
2672       if (strcasecmp(CONVRULE_Z2H_NUM_ON_CMD, action) == 0) {
2673         newrule->action |= CONVRULE_Z2H_NUM_ON_BIT;
2674       }
2675       else
2676       if (strcasecmp(CONVRULE_Z2H_NUM_OFF_CMD, action) == 0) {
2677         newrule->action |= CONVRULE_Z2H_NUM_OFF_BIT;
2678       }
2679       else
2680       if (strcasecmp(CONVRULE_Z2H_ALL_ON_CMD, action) == 0) {
2681         newrule->action |= CONVRULE_Z2H_ON_BIT | CONVRULE_Z2H_ALPHA_ON_BIT | CONVRULE_Z2H_NUM_ON_BIT;
2682       }
2683       else
2684       if (strcasecmp(CONVRULE_Z2H_NUM_OFF_CMD, action) == 0) {
2685         newrule->action |= CONVRULE_Z2H_OFF_BIT | CONVRULE_Z2H_ALPHA_OFF_BIT | CONVRULE_Z2H_NUM_OFF_BIT;
2686       }
2687       break;
2688
2689     default:
2690       break;
2691     }
2692   }
2693   
2694   pp = prm1;
2695   if (*pp == '!') {
2696     newrule->flags |= CONVRULE_FLAG_NOTMATCH;
2697     pp++;
2698   }
2699
2700   mode = AP_REG_EXTENDED;
2701   if ((regexp = ap_pregcomp((apr_pool_t *)cmd->pool, (const char *)pp, mode)) == NULL)
2702     return "RewriteRule: cannot compile regular expression ";
2703
2704   newrule->regexp = regexp;
2705   if (*prm3)
2706     newrule->encoding = apr_pstrdup(cmd->pool, prm3);
2707   else
2708     newrule->encoding = apr_pstrdup(cmd->pool, "none");
2709
2710   newrule->pc_flag = CONVRULE_PC_FLAG_OFF_BIT;
2711   if (*prm4)
2712     if (strcasecmp(CONVRULE_PC_FLAG_ON_CMD, prm4) == 0)
2713       newrule->pc_flag = CONVRULE_PC_FLAG_ON_BIT;
2714
2715   newrule->user_agent = NULL;
2716   if (*prm5)
2717     newrule->user_agent = apr_pstrdup(cmd->pool, prm5);
2718     
2719   return NULL;
2720 }
2721
2722
2723 static const char *
2724 cmd_set_cookie_dir(
2725   cmd_parms   *cmd, 
2726   void        *mconfig, 
2727   const char  *arg)
2728 {
2729   mod_chxj_config *dconf;
2730
2731
2732   if (strlen(arg) > 4096) 
2733     return "mod_chxj: ChxjCookieDir is too long.";
2734
2735   dconf = (mod_chxj_config *)mconfig;
2736
2737   dconf->cookie_db_dir = apr_pstrdup(cmd->pool, arg);
2738
2739   return NULL;
2740 }
2741
2742
2743 static const char *
2744 cmd_set_cookie_timeout(
2745   cmd_parms   *UNUSED(cmd), 
2746   void        *mconfig, 
2747   const char  *arg)
2748 {
2749   mod_chxj_config *dconf;
2750
2751   if (strlen(arg) > 4096) 
2752     return "mod_chxj: ChxjCookieTimeout is too long.";
2753
2754   if (chxj_chk_numeric(arg) != 0)
2755     return "mod_chxj: ChxjCookieTimeout is not numeric.";
2756
2757   dconf = (mod_chxj_config *)mconfig;
2758
2759   dconf->cookie_timeout = atoi(arg);
2760
2761   return NULL;
2762 }
2763
2764
2765 #if defined(USE_MYSQL_COOKIE)
2766 static const char *
2767 cmd_set_cookie_mysql_database(
2768   cmd_parms   *cmd, 
2769   void        *mconfig, 
2770   const char  *arg)
2771 {
2772   mod_chxj_config  *dconf;
2773
2774   if (strlen(arg) > 255) 
2775     return "mod_chxj: ChxjCookieMysqlDatabase is too long.";
2776
2777   dconf = (mod_chxj_config *)mconfig;
2778
2779   dconf->mysql.database = apr_pstrdup(cmd->pool, arg);
2780
2781   return NULL;
2782 }
2783
2784
2785 static const char *
2786 cmd_set_cookie_mysql_username(
2787   cmd_parms   *cmd, 
2788   void        *mconfig, 
2789   const char  *arg)
2790 {
2791   mod_chxj_config  *dconf;
2792
2793   if (strlen(arg) > 255) 
2794     return "mod_chxj: ChxjCookieMysqlUsername is too long.";
2795
2796   dconf = (mod_chxj_config *)mconfig;
2797
2798   dconf->mysql.username = apr_pstrdup(cmd->pool, arg);
2799
2800   return NULL;
2801 }
2802
2803
2804 static const char *
2805 cmd_set_cookie_mysql_password(
2806   cmd_parms   *cmd, 
2807   void        *mconfig, 
2808   const char  *arg)
2809 {
2810   mod_chxj_config  *dconf;
2811
2812   if (strlen(arg) > 255) 
2813     return "mod_chxj: ChxjCookieMysqlPassword is too long.";
2814
2815   dconf = (mod_chxj_config *)mconfig;
2816
2817   dconf->mysql.password = apr_pstrdup(cmd->pool, arg);
2818
2819   return NULL;
2820 }
2821
2822
2823 static const char *
2824 cmd_set_cookie_mysql_table_name(
2825   cmd_parms   *cmd, 
2826   void        *mconfig, 
2827   const char  *arg)
2828 {
2829   mod_chxj_config  *dconf;
2830
2831   if (strlen(arg) > 255) 
2832     return "mod_chxj: ChxjCookieMysqlTableName is too long.";
2833
2834   dconf = (mod_chxj_config *)mconfig;
2835
2836   dconf->mysql.tablename = apr_pstrdup(cmd->pool, arg);
2837
2838   return NULL;
2839 }
2840
2841 static const char *
2842 cmd_set_cookie_mysql_port(
2843   cmd_parms   *UNUSED(cmd), 
2844   void        *mconfig, 
2845   const char  *arg)
2846 {
2847   mod_chxj_config *dconf;
2848
2849   if (strlen(arg) > 255) 
2850     return "mod_chxj: ChxjCookieMysqlPort is too long.";
2851
2852   dconf = (mod_chxj_config *)mconfig;
2853
2854   if (chxj_chk_numeric(arg) != 0)
2855     return "mod_chxj: ChxjCookieMysqlPort is not numeric.";
2856
2857   dconf = (mod_chxj_config *)mconfig;
2858
2859   dconf->mysql.port = chxj_atoi(arg);
2860
2861   return NULL;
2862 }
2863
2864
2865 static const char *
2866 cmd_set_cookie_mysql_host(
2867   cmd_parms   *cmd, 
2868   void        *mconfig, 
2869   const char  *arg)
2870 {
2871   mod_chxj_config  *dconf;
2872
2873   if (strlen(arg) > 255) 
2874     return "mod_chxj: ChxjCookieMysqlHost is too long.";
2875
2876   dconf = (mod_chxj_config *)mconfig;
2877
2878   dconf->mysql.host = apr_pstrdup(cmd->pool, arg);
2879
2880   return NULL;
2881 }
2882
2883
2884 static const char *
2885 cmd_set_cookie_mysql_socket_path(
2886   cmd_parms   *cmd, 
2887   void        *mconfig, 
2888   const char  *arg)
2889 {
2890   mod_chxj_config  *dconf;
2891
2892   if (strlen(arg) > 4096) 
2893     return "mod_chxj: ChxjCookieMysqlSocketPath is too long.";
2894
2895   dconf = (mod_chxj_config *)mconfig;
2896
2897   dconf->mysql.socket_path = apr_pstrdup(cmd->pool, arg);
2898
2899   return NULL;
2900 }
2901
2902
2903 static const char *
2904 cmd_set_cookie_mysql_charset(
2905   cmd_parms   *cmd, 
2906   void        *mconfig, 
2907   const char  *arg)
2908 {
2909   mod_chxj_config  *dconf;
2910
2911   if (strlen(arg) > 255) 
2912     return "mod_chxj: ChxjCookieMysqlCharset is too long.";
2913
2914   dconf = (mod_chxj_config *)mconfig;
2915
2916   dconf->mysql.charset = apr_pstrdup(cmd->pool, arg);
2917
2918   return NULL;
2919 }
2920 #endif
2921 #if defined(USE_MEMCACHE_COOKIE)
2922 static const char *
2923 cmd_set_cookie_memcache_port(
2924   cmd_parms   *UNUSED(cmd), 
2925   void        *mconfig, 
2926   const char  *arg)
2927 {
2928   mod_chxj_config *dconf;
2929
2930   if (strlen(arg) > 255) 
2931     return "mod_chxj: ChxjCookieMemcachePort is too long.";
2932
2933   dconf = (mod_chxj_config *)mconfig;
2934
2935   if (chxj_chk_numeric(arg) != 0)
2936     return "mod_chxj: ChxjCookieMemcachePort is not numeric.";
2937
2938   dconf = (mod_chxj_config *)mconfig;
2939
2940   dconf->memcache.port = (apr_port_t)chxj_atoi(arg);
2941
2942   return NULL;
2943 }
2944
2945
2946 static const char *
2947 cmd_set_cookie_memcache_host(
2948   cmd_parms   *cmd, 
2949   void        *mconfig, 
2950   const char  *arg)
2951 {
2952   mod_chxj_config  *dconf;
2953
2954   if (strlen(arg) > 255) 
2955     return "mod_chxj: ChxjCookieMemcacheHost is too long.";
2956
2957   dconf = (mod_chxj_config *)mconfig;
2958
2959   dconf->memcache.host = apr_pstrdup(cmd->pool, arg);
2960
2961   return NULL;
2962 }
2963 #endif
2964
2965 static const char *
2966 cmd_set_cookie_lazy_mode(
2967   cmd_parms   *UNUSED(cmd), 
2968   void        *mconfig, 
2969   const char  *arg)
2970 {
2971   mod_chxj_config  *dconf;
2972
2973   if (strlen(arg) > 255) 
2974     return "mod_chxj: ChxjCookieLazyMode is too long.";
2975
2976   dconf = (mod_chxj_config *)mconfig;
2977
2978   if (strcasecmp("TRUE",arg) == 0) {
2979     dconf->cookie_lazy_mode = COOKIE_LAZY_ON;
2980   }
2981   else {
2982     dconf->cookie_lazy_mode = COOKIE_LAZY_OFF;
2983   }
2984
2985   return NULL;
2986 }
2987
2988 static const char *
2989 cmd_set_cookie_store_type(
2990   cmd_parms   *UNUSED(cmd), 
2991   void        *mconfig, 
2992   const char  *arg)
2993 {
2994   mod_chxj_config  *dconf;
2995
2996   if (strlen(arg) > 255) 
2997     return "mod_chxj: ChxjCookieStoreType is too long.";
2998
2999   dconf = (mod_chxj_config *)mconfig;
3000
3001   if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_DBM, arg) == 0) {
3002     dconf->cookie_store_type = COOKIE_STORE_TYPE_DBM;
3003   }
3004   else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MYSQL, arg) == 0) {
3005     dconf->cookie_store_type = COOKIE_STORE_TYPE_MYSQL;
3006   }
3007   else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MEMCACHE, arg) == 0) {
3008     dconf->cookie_store_type = COOKIE_STORE_TYPE_MEMCACHE;
3009   }
3010   else {
3011     dconf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
3012   }
3013
3014   return NULL;
3015 }
3016
3017 static const char *
3018 cmd_set_forward_url_base(
3019   cmd_parms   *cmd,
3020   void        *mconfig,
3021   const char  *arg)
3022 {
3023  mod_chxj_config *dconf;
3024
3025   if (strlen(arg) > 255)
3026     return "mod_chxj: ChxjForwardUrlBase is too long.";
3027
3028   dconf = (mod_chxj_config *)mconfig;
3029
3030   dconf->forward_url_base = apr_pstrdup(cmd->pool, arg);
3031
3032   return NULL;
3033 }
3034
3035 static const char *
3036 cmd_set_forward_server_ip(
3037   cmd_parms   *cmd,
3038   void        *mconfig,
3039   const char  *arg)
3040 {
3041   mod_chxj_config *dconf;
3042
3043   if (strlen(arg) > 255)
3044     return "mod_chxj: ChxjForwardServerIp is too long.";
3045
3046   dconf = (mod_chxj_config *)mconfig;
3047
3048   dconf->forward_server_ip = apr_pstrdup(cmd->pool, arg);
3049
3050   return NULL;
3051 }
3052
3053 static const char *
3054 cmd_allowed_cookie_domain(
3055   cmd_parms   *cmd,
3056   void        *mconfig,
3057   const char  *arg)
3058 {
3059   mod_chxj_config *dconf;
3060
3061   if (strlen(arg) > 255)
3062     return "mod_chxj: ChxjAllowedCookieDomain is too long.";
3063
3064   dconf = (mod_chxj_config *)mconfig;
3065
3066   dconf->allowed_cookie_domain = apr_pstrdup(cmd->pool, arg);
3067
3068   return NULL;
3069 }
3070
3071 static const char *
3072 cmd_set_new_line_type(
3073   cmd_parms   *UNUSED(cmd), 
3074   void        *mconfig, 
3075   const char  *arg)
3076 {
3077   mod_chxj_config  *dconf;
3078   if (strlen(arg) > 255)
3079     return "mod_chxj: ChxjNewLineType is too long.";
3080
3081   dconf = (mod_chxj_config *)mconfig;
3082
3083   if (strcasecmp(CHXJ_NEW_LINE_TYPE_CRLF, arg) == 0) {
3084     dconf->new_line_type = NLTYPE_CRLF;
3085   }
3086   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_LF, arg) == 0) {
3087     dconf->new_line_type = NLTYPE_LF;
3088   }
3089   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_CR, arg) == 0) {
3090     dconf->new_line_type = NLTYPE_CR;
3091   }
3092   else if (strcasecmp(CHXJ_NEW_LINE_TYPE_NONE, arg) == 0) {
3093     dconf->new_line_type = NLTYPE_NONE;
3094   }
3095   else {
3096     return "mod_chxj: invalid value (ChxjNewLineType)";
3097   }
3098   return NULL;
3099 }
3100
3101 static const char *
3102 cmd_post_log_env(
3103   cmd_parms   *cmd, 
3104   void        *mconfig, 
3105   const char  *arg)
3106 {
3107   mod_chxj_config  *dconf;
3108   if (strlen(arg) > 255)
3109     return "mod_chxj: ChxjPostLogEnv is too long.";
3110
3111   dconf = (mod_chxj_config *)mconfig;
3112
3113   dconf->post_log = apr_pstrdup(cmd->pool, arg);
3114
3115   return NULL;
3116 }
3117
3118 static const char *
3119 cmd_cookie_dbm_type(
3120   cmd_parms   *cmd, 
3121   void        *mconfig, 
3122   const char  *arg)
3123 {
3124   mod_chxj_config  *dconf;
3125   if (strlen(arg) > 255)
3126     return "mod_chxj: ChxjCookieDbmType is too long.";
3127
3128   dconf = (mod_chxj_config *)mconfig;
3129
3130   dconf->cookie_dbm_type = apr_pstrdup(cmd->pool, arg);
3131
3132   return NULL;
3133 }
3134
3135 static const char *
3136 cmd_imode_emoji_color(
3137   cmd_parms   *cmd, 
3138   void        *mconfig, 
3139   const char  *arg)
3140 {
3141   mod_chxj_config  *dconf;
3142   
3143   if (strlen(arg) > 256) 
3144     return "imode emoji color is too long.";
3145
3146   dconf = (mod_chxj_config *)mconfig;
3147   if (strcasecmp("ON", arg) == 0) {
3148     dconf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_ON;
3149   }
3150   else if(strcasecmp("AUTO",arg) == 0) {
3151     dconf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_AUTO;
3152   }
3153   else {
3154     dconf->imode_emoji_color = CHXJ_IMODE_EMOJI_COLOR_OFF;
3155   }
3156   
3157   return NULL;
3158 }
3159
3160 static const char *
3161 cmd_add_device_data_tsv(cmd_parms *parms, void *mconfig, const char *arg) 
3162 {
3163   mod_chxj_config  *conf;
3164   
3165   if (strlen(arg) > 256) 
3166     return "mod_chxj: device tsv filename too long.";
3167
3168   conf = (mod_chxj_config *)mconfig;
3169   
3170   conf->detect_device_type = CHXJ_ADD_DETECT_DEVICE_TYPE_TSV;
3171   
3172   apr_finfo_t info;
3173   apr_status_t res = apr_stat(&info,arg,APR_FINFO_TYPE,parms->pool);
3174   if(res != APR_SUCCESS){
3175     return apr_psprintf(parms->pool,"ChxjDeviceTSV [%s]: not found ",arg);
3176   }
3177   else{
3178     if(info.filetype != APR_REG ){
3179       return apr_psprintf(parms->pool,"ChxjDeviceTSV [%s]: is not file ",arg);
3180     }
3181   }
3182   apr_file_t *fp;
3183   apr_file_open(&fp, arg, APR_READ|APR_BUFFERED, APR_OS_DEFAULT, parms->pool);
3184   
3185   chxj_load_device_tsv_data(fp,parms->pool,conf);
3186   
3187   apr_file_close(fp);
3188   return NULL;
3189 }
3190
3191 static const char *
3192 cmd_image_rewrite(cmd_parms *parms, void *mconfig, const char *arg)
3193 {
3194   mod_chxj_config *conf;
3195   if (strlen(arg) > 256){
3196     return "mod_chxj: set rewrite too long.";
3197   }
3198   conf = (mod_chxj_config *)mconfig;
3199   if (strcasecmp("ON", arg) == 0) {
3200     conf->image_rewrite = CHXJ_IMG_REWRITE_ON;
3201   }
3202   else if(strcasecmp("OFF",arg) == 0) {
3203     conf->image_rewrite = CHXJ_IMG_REWRITE_OFF;
3204   }
3205   else {
3206     conf->image_rewrite = CHXJ_IMG_REWRITE_NONE;
3207   }
3208   return NULL;
3209 }
3210
3211 static const char *
3212 cmd_image_rewrite_url(cmd_parms *parms, void *mconfig, const char *arg)
3213 {
3214   mod_chxj_config *conf;
3215   if (strlen(arg) > 256){
3216     return "mod_chxj: set rewrite url too long.";
3217   }
3218   conf = (mod_chxj_config *)mconfig;
3219   conf->image_rewrite_url = apr_pstrdup(parms->pool, arg);;
3220   return NULL;
3221 }
3222
3223 static const char *
3224 cmd_image_rewrite_mode(cmd_parms *parms, void *mconfig, const char *arg)
3225 {
3226   mod_chxj_config *conf;
3227   if (strlen(arg) > 256){
3228     return "mod_chxj: set rewrite mode is too long.";
3229   }
3230
3231   conf = (mod_chxj_config *)mconfig;
3232   if (strcasecmp("all",arg) == 0) {
3233     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_ALL;
3234   }
3235   else if (strcasecmp("user",arg) == 0) {
3236     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_USER;
3237   }
3238   else if (strcasecmp("tag",arg) == 0) {
3239     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_TAG;
3240   }
3241   else{
3242     conf->image_rewrite_mode = CHXJ_IMG_REWRITE_MODE_NONE;
3243   }
3244   return NULL;
3245 }
3246
3247 static const command_rec cmds[] = {
3248   AP_INIT_TAKE1(
3249     "ChxjLoadDeviceData",
3250     cmd_load_device_data,
3251     NULL,
3252     OR_ALL,
3253     "Load Device Data"),
3254   AP_INIT_TAKE1(
3255     "ChxjLoadEmojiData",
3256     cmd_load_emoji_data,
3257     NULL,
3258     OR_ALL,
3259     "Load Emoji Data"),
3260   AP_INIT_TAKE1(
3261     "ChxjImageEngine",
3262     cmd_set_image_engine,
3263     NULL,
3264     OR_ALL,
3265     "Convert Target URI"),
3266   AP_INIT_TAKE1(
3267     "ChxjImageCacheDir",
3268     cmd_set_image_cache_dir,
3269     NULL,
3270     OR_ALL,
3271     "Image Cache Directory"),
3272   AP_INIT_TAKE1(
3273     "ChxjImageCacheLimit",
3274     cmd_set_image_cache_limit,
3275     NULL,
3276     OR_ALL,
3277     "Image Cache Limit"),
3278   AP_INIT_TAKE1(
3279     "ChxjImageCopyright",
3280     cmd_set_image_copyright,
3281     NULL,
3282     OR_ALL,
3283     "Copyright Flag"),
3284   AP_INIT_RAW_ARGS(
3285     "ChxjConvertRule",
3286     cmd_convert_rule,
3287     NULL, 
3288     OR_FILEINFO,
3289     "an URL-applied regexp-pattern and a substitution URL"),
3290   AP_INIT_TAKE1(
3291     "ChxjCookieDir",
3292     cmd_set_cookie_dir,
3293     NULL,
3294     OR_ALL,
3295     "save cookie.db directory."),
3296   AP_INIT_TAKE1(
3297     "ChxjCookieTimeout",
3298     cmd_set_cookie_timeout,
3299     NULL,
3300     OR_ALL,
3301     "The compulsion time-out time of the cookie is specified. "),
3302   AP_INIT_TAKE1(
3303     "ChxjCookieStoreType",
3304     cmd_set_cookie_store_type,
3305     NULL,
3306     OR_ALL,
3307     "It specifies preserving of the cookie ahead. (DBM/MYSQL/MEMCACHE)"),
3308   AP_INIT_TAKE1(
3309     "ChxjCookieLazyMode",
3310     cmd_set_cookie_lazy_mode,
3311     NULL,
3312     OR_ALL,
3313     "OneTimeID is negligently done. (TRUE/FALSE)"),
3314 #if defined(USE_MYSQL_COOKIE)
3315   AP_INIT_TAKE1(
3316     "ChxjCookieMysqlHost",
3317     cmd_set_cookie_mysql_host,
3318     NULL,
3319     OR_ALL,
3320     "The MySQL database host used by saving Cookie"),
3321   AP_INIT_TAKE1(
3322     "ChxjCookieMysqlPort",
3323     cmd_set_cookie_mysql_port,
3324     NULL,
3325     OR_ALL,
3326     "The MySQL database port used by saving Cookie"),
3327   AP_INIT_TAKE1(
3328     "ChxjCookieMysqlDatabase",
3329     cmd_set_cookie_mysql_database,
3330     NULL,
3331     OR_ALL,
3332     "The MySQL database name used by saving Cookie"),
3333   AP_INIT_TAKE1(
3334     "ChxjCookieMysqlUsername",
3335     cmd_set_cookie_mysql_username,
3336     NULL,
3337     OR_ALL,
3338     "The MySQL username used by saving Cookie"),
3339   AP_INIT_TAKE1(
3340     "ChxjCookieMysqlPassword",
3341     cmd_set_cookie_mysql_password,
3342     NULL,
3343     OR_ALL,
3344     "The MySQL password used by saving Cookie"),
3345   AP_INIT_TAKE1(
3346     "ChxjCookieMysqlTableName",
3347     cmd_set_cookie_mysql_table_name,
3348     NULL,
3349     OR_ALL,
3350     "The MySQL table name used by saving Cookie"),
3351   AP_INIT_TAKE1(
3352     "ChxjCookieMysqlSocketPath",
3353     cmd_set_cookie_mysql_socket_path,
3354     NULL,
3355     OR_ALL,
3356     "The MySQL socket path used by saving Cookie"),
3357   AP_INIT_TAKE1(
3358     "ChxjCookieMysqlCharset",
3359     cmd_set_cookie_mysql_charset,
3360     NULL,
3361     OR_ALL,
3362     "The MySQL charset used by saving Cookie"),
3363 #endif
3364 #if defined(USE_MEMCACHE_COOKIE)
3365   AP_INIT_TAKE1(
3366     "ChxjCookieMemcacheHost",
3367     cmd_set_cookie_memcache_host,
3368     NULL,
3369     OR_ALL,
3370     "The Memcached host used by saving Cookie"),
3371   AP_INIT_TAKE1(
3372     "ChxjCookieMemcachePort",
3373     cmd_set_cookie_memcache_port,
3374     NULL,
3375     OR_ALL,
3376     "The Memcached port used by saving Cookie"),
3377 #endif
3378   AP_INIT_TAKE1(
3379     "ChxjNewLineType",
3380     cmd_set_new_line_type,
3381     NULL,
3382     OR_ALL,
3383     "HTML new line type (NONE|CRLF|LF|CR). default is CRLF"),
3384   AP_INIT_TAKE1(
3385     "ChxjForwardUrlBase",
3386     cmd_set_forward_url_base,
3387     NULL,
3388     OR_ALL,
3389     "The forward url base(default: {request protocol}://{this server}:{this server port}"),
3390   AP_INIT_TAKE1(
3391     "ChxjForwardServerIp",
3392     cmd_set_forward_server_ip,
3393     NULL,
3394     OR_ALL,
3395     "The forward server ip(default: this server ip)"),
3396   AP_INIT_TAKE1(
3397     "ChxjAllowedCookieDomain",
3398     cmd_allowed_cookie_domain,
3399     NULL,
3400     OR_ALL,
3401     "Domain that permits parameter addition for cookie besides hostname.(Default:hostname Only)"),
3402   AP_INIT_TAKE1(
3403     "ChxjPostLogEnv",
3404     cmd_post_log_env,
3405     NULL,
3406     OR_ALL,
3407     "for CustomLog directive. mod_chxj's internal POST log environment name.(Default:chxj-post-log)"),
3408   AP_INIT_TAKE1(
3409     "ChxjCookieDbmType",
3410     cmd_cookie_dbm_type,
3411     NULL,
3412     OR_ALL,
3413     "Kind of DBM used with Cookie simulator.(default|GDBM|SDBM|DB|NDBM)"),
3414   AP_INIT_TAKE1(
3415     "ChxjImodeEmojiColor",
3416     cmd_imode_emoji_color,
3417     NULL,
3418     OR_ALL,
3419     "Auto i-mode emoji color"),
3420   AP_INIT_TAKE1(
3421     "ChxjAddDeviceDataTSV",
3422     cmd_add_device_data_tsv,
3423     NULL,
3424     OR_ALL,
3425     "Additional devices TSV data"),
3426   AP_INIT_TAKE1(
3427     "ChxjImageRewrite",
3428     cmd_image_rewrite,
3429     NULL,
3430     OR_ALL,
3431     "Rewrite Image"
3432    ),
3433   AP_INIT_TAKE1(
3434     "ChxjImageRewriteUrl",
3435     cmd_image_rewrite_url,
3436     NULL,
3437     OR_ALL,
3438     "Set rewrite Image url"
3439    ),
3440   AP_INIT_TAKE1(
3441     "ChxjImageRewriteMode",
3442     cmd_image_rewrite_mode,
3443     NULL,
3444     OR_ALL,
3445     "Set rewrite Image rewrite url mode"
3446    ),
3447   {NULL,{NULL},NULL,0,0,NULL},
3448 };
3449
3450
3451 /*----------------------------------------------------------------------------*/
3452 /* Dispatch list for API hooks                                                */
3453 /*----------------------------------------------------------------------------*/
3454 module AP_MODULE_DECLARE_DATA chxj_module = {
3455   STANDARD20_MODULE_STUFF, 
3456   chxj_create_per_dir_config,          /* create per-dir    config structures */
3457   chxj_merge_per_dir_config,           /* merge  per-dir    config structures */
3458   chxj_config_server_create,           /* create per-server config structures */
3459   NULL,                                /* merge  per-server config structures */
3460   cmds,                                /* table of config file commands       */
3461   chxj_register_hooks                  /* register hooks                      */
3462 };
3463 /*
3464  * vim:ts=2 et
3465  */