OSDN Git Service

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