OSDN Git Service

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