OSDN Git Service

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