OSDN Git Service

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