OSDN Git Service

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