OSDN Git Service

* updated changelog.
[modchxj/mod_chxj.git] / src / mod_chxj.c
index 9627b90..a1bba8d 100644 (file)
@@ -1,6 +1,6 @@
 /*
+ * Copyright (C) 2005-2008 Atsushi Konno All rights reserved.
  * Copyright (C) 2005 QSDN,Inc. All rights reserved.
- * Copyright (C) 2005 Atsushi Konno All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
  */
 #include <unistd.h>
 #include <string.h>
+#include <limits.h>
+#include <errno.h>
 
 #include "httpd.h"
 #include "http_config.h"
 #include "chxj_apply_convrule.h"
 #include "chxj_cookie.h"
 #include "chxj_url_encode.h"
+#include "chxj_str_util.h"
+#if defined(USE_MYSQL_COOKIE)
+#  include "chxj_mysql.h"
+#endif
 
 
 #define CHXJ_VERSION_PREFIX PACKAGE_NAME "/"
@@ -114,9 +120,6 @@ converter_t convert_routine[] = {
     .converter = NULL,
     .encoder  = NULL,
   },
-  {
-    NULL
-  }
 };
 
 static int chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp);
@@ -187,15 +190,16 @@ chxj_headers_fixup(request_rec *r)
  * @param len [i/o] It is length of former HTML character string. 
  */
 static char* 
-chxj_exchange(request_rec *r, const char** src, apr_size_t* len)
+chxj_exchange(request_rec *r, const char** src, apr_size_t* len, device_table *spec, const char *ua, cookie_t **cookiep)
 {
-  char*               user_agent;
-  char*               dst;
-  char*               tmp;
-  cookie_t*           cookie;
-  mod_chxj_config*    dconf; 
-  chxjconvrule_entry* entryp;
-
+  char                *user_agent;
+  char                *dst;
+  char                *tmp;
+  cookie_t            *cookie;
+  mod_chxj_config     *dconf; 
+  chxjconvrule_entry  *entryp;
+
+  DBG(r,"start of chxj_exchange() input:[%.*s]", (int)*len, *src);
   dst  = apr_pstrcat(r->pool, (char*)*src, NULL);
 
   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
@@ -215,18 +219,21 @@ chxj_exchange(request_rec *r, const char** src, apr_size_t* len)
   else
     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
 
-  DBG1(r,"User-Agent:[%s]", user_agent);
-  DBG(r, "start chxj_exchange()");
-  DBG1(r,"content type is %s", r->content_type);
+  DBG(r,"User-Agent:[%s]", user_agent);
+  DBG(r,"content type is %s", r->content_type);
 
 
-  if (*(char*)r->content_type == 't' 
-  && strncmp(r->content_type, "text/html",   9) != 0) {
-    DBG1(r,"content type is %s", r->content_type);
+  if (! STRNCASEEQ('t','T', "text/html", r->content_type, sizeof("text/html")-1)
+  &&  ! STRNCASEEQ('a','A', "application/xhtml+xml", r->content_type, sizeof("application/xhtml+xml")-1)) {
+    DBG(r,"no convert. content type is %s", r->content_type);
+    DBG(r,"end of chxj_exchange()");
     return (char*)*src;
   }
 
-  device_table* spec = chxj_specified_device(r, user_agent);
+  if (ua && user_agent && strcasecmp(user_agent, ua) != 0) {
+    /* again */
+    spec = chxj_specified_device(r, user_agent);
+  }
 
   /*
    * save cookie.
@@ -280,8 +287,11 @@ chxj_exchange(request_rec *r, const char** src, apr_size_t* len)
     *len = 1;
   }
   dst[*len] = 0;
+  if (cookie) {
+    *cookiep = cookie;
+  }
 
-  DBG(r, "end chxj_exchange()");
+  DBG(r, "end of chxj_exchange()");
 
   return dst;
 }
@@ -297,6 +307,7 @@ chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
 {
 
   char*      buff;
+  char*      buff_pre;
   apr_size_t urilen;
   char*      result;
   char*      pair;
@@ -305,6 +316,7 @@ chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
   char*      pstate;
   char*      vstate;
   cookie_t*  cookie;
+  int        no_update_flag = 0;
 
   DBG(r, "start chxj_convert_input_header()");
 
@@ -317,8 +329,29 @@ chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
 
   result = qs_alloc_zero_byte_string(r);
 
+  buff_pre = apr_pstrdup(r->pool, r->args);
+
+  for (;;) {
+    char* pair_sv;
+
+    pair = apr_strtok(buff_pre, "&", &pstate);
+    if (pair == NULL)
+      break;
+
+    buff_pre = NULL;
+
+    pair_sv = apr_pstrdup(r->pool, pair);
+
+    name  = apr_strtok(pair, "=", &vstate);
+    value = apr_strtok(NULL, "=", &vstate);
+    if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
+      DBG(r, "found cookie no update parameter");
+      no_update_flag++;
+    }
+  }
+
   buff = apr_pstrdup(r->pool, r->args);
-  DBG1(r, "r->args=[%s]", buff);
+  DBG(r, "r->args=[%s]", buff);
 
   /* _chxj_dmy */
   /* _chxj_c_ */
@@ -347,12 +380,12 @@ chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
 
         dlen   = strlen(value);
         value = chxj_url_decode(r, value);
-        DBG1(r, "************ before encoding[%s]", value);
+        DBG(r, "************ before encoding[%s]", value);
 
         dvalue = chxj_rencoding(r, value, &dlen);
         dvalue = chxj_url_encode(r, dvalue);
 
-        DBG1(r, "************ after encoding[%s]", dvalue);
+        DBG(r, "************ after encoding[%s]", dvalue);
 
         result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
       }
@@ -380,21 +413,18 @@ chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
     }
     else
     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
-      DBG1(r, "found cookie parameter[%s]", value);
+      DBG(r, "found cookie parameter[%s]", value);
       DBG(r, "call start chxj_load_cookie()");
       cookie = chxj_load_cookie(r, value);
       DBG(r, "call end   chxj_load_cookie()");
-      if (! strstr(r->uri, ".jpg")  
-      &&  ! strstr(r->uri, ".jpeg")
-      &&  ! strstr(r->uri, ".gif")
-      &&  ! strstr(r->uri, ".png")) {
+      if (! no_update_flag && cookie) {
         chxj_update_cookie(r, cookie);
       }
     }
   }
   r->args = result;
 
-  DBG1(r, "result r->args=[%s]", r->args);
+  DBG(r, "result r->args=[%s]", r->args);
   DBG(r, "end   chxj_convert_input_header()");
   return 0;
 }
@@ -422,12 +452,34 @@ chxj_input_convert(
   char* s;
   char* result;
   cookie_t* cookie;
+  char* buff_pre;
+  int   no_update_flag = 0;
 
-  s = apr_pstrdup(r->pool, *src);
+  s        = apr_pstrdup(r->pool, *src);
+  buff_pre = apr_pstrdup(r->pool, *src);
 
   result = qs_alloc_zero_byte_string(r);
 
-  DBG1(r, "BEFORE input convert source = [%s]", s);
+  DBG(r, "BEFORE input convert source = [%s]", s);
+
+  for (;;) {
+    char* pair_sv;
+
+    pair = apr_strtok(buff_pre, "&", &pstate);
+    if (pair == NULL)
+      break;
+
+    buff_pre = NULL;
+
+    pair_sv = apr_pstrdup(r->pool, pair);
+
+    name  = apr_strtok(pair, "=", &vstate);
+    value = apr_strtok(NULL, "=", &vstate);
+    if (strcasecmp(name, CHXJ_COOKIE_NOUPDATE_PARAM) == 0) {
+      DBG(r, "found cookie no update parameter");
+      no_update_flag++;
+    }
+  }
 
   /* _chxj_dmy */
   /* _chxj_c_ */
@@ -452,12 +504,12 @@ chxj_input_convert(
 
         dlen   = strlen(value);
         value = chxj_url_decode(r, value);
-        DBG1(r, "************ before encoding[%s]", value);
+        DBG(r, "************ before encoding[%s]", value);
 
         dvalue = chxj_rencoding(r, value, &dlen);
         dvalue = chxj_url_encode(r,dvalue);
 
-        DBG1(r, "************ after encoding[%s]", dvalue);
+        DBG(r, "************ after encoding[%s]", dvalue);
 
         result = apr_pstrcat(r->pool, result, name, "=", dvalue, NULL);
 
@@ -485,12 +537,12 @@ chxj_input_convert(
 
         dlen   = strlen(value);
         value = chxj_url_decode(r, value);
-        DBG1(r, "************ before encoding[%s]", value);
+        DBG(r, "************ before encoding[%s]", value);
 
         dvalue = chxj_rencoding(r, value, &dlen);
         dvalue = chxj_url_encode(r,dvalue);
 
-        DBG1(r, "************ after encoding[%s]", dvalue);
+        DBG(r, "************ after encoding[%s]", dvalue);
 
         result = apr_pstrcat(r->pool, result, &name[8], "=", dvalue, NULL);
 
@@ -501,16 +553,18 @@ chxj_input_convert(
     }
     else
     if (strcasecmp(name, CHXJ_COOKIE_PARAM) == 0) {
-      DBG1(r, "found cookie parameter[%s]", value);
+      DBG(r, "found cookie parameter[%s]", value);
       DBG(r, "call start chxj_load_cookie()");
       cookie = chxj_load_cookie(r, value);
       DBG(r, "call end   chxj_load_cookie()");
-      chxj_update_cookie(r, cookie);
+      if (! no_update_flag && cookie) {
+        chxj_update_cookie(r, cookie);
+      }
     }
   }
   *len = strlen(result);
 
-  DBG1(r, "AFTER input convert result = [%s]", result);
+  DBG(r, "AFTER input convert result = [%s]", result);
 
   return result;
 }
@@ -568,17 +622,18 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
   apr_bucket*         b;
   const char*         data;
   char*               contentLength;
+  char*               user_agent = NULL;
   apr_size_t          len;
-  mod_chxj_ctx*       ctx;
-  cookie_t*           cookie;
+  mod_chxj_ctx*       ctx = (mod_chxj_ctx *)f->ctx;
+  cookie_t*           cookie = NULL;
   char*               location_header;
   mod_chxj_config*    dconf;
-  chxjconvrule_entry* entryp;
+  chxjconvrule_entry  *entryp = NULL;
+  device_table        *spec = NULL;
 
 
 
   DBG(f->r, "start of chxj_output_filter()");
-
   r  = f->r;
   rv = APR_SUCCESS;
 
@@ -588,30 +643,105 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
     &&  !f->r->prev) 
       f->r->chunked = 1;
   }
+  if (r->content_type) {
+    if (! STRNCASEEQ('t','T',"text/html",r->content_type, sizeof("text/html")-1)
+    &&  ! STRNCASEEQ('t','T',"text/xml", r->content_type, sizeof("text/xml")-1)
+    &&  ! STRNCASEEQ('a','A',"application/xhtml+xml", r->content_type, sizeof("application/xhtml+xml")-1)
+    &&  ! (STRNCASEEQ('i','I',"image/",  r->content_type, sizeof("image/") -1)
+          && ( STRCASEEQ('j','J',"jpeg",            &r->content_type[6])         /* JPEG */
+            || STRCASEEQ('j','J',"jp2",             &r->content_type[6])         /* JPEG2000 */
+            || STRCASEEQ('j','J',"jpeg2000",        &r->content_type[6])         /* JPEG2000 */
+            || STRCASEEQ('j','J',"jpeg2000-image",  &r->content_type[6])         /* JPEG2000 */
+            || STRCASEEQ('x','X',"x-jpeg2000-image",&r->content_type[6])         /* JPEG2000 */
+            || STRCASEEQ('p','P',"png",             &r->content_type[6])         /* PNG */
+            || STRCASEEQ('x','X',"x-png",           &r->content_type[6])         /* PNG */
+            || STRCASEEQ('g','G',"gif",             &r->content_type[6])))) {     /* GIF */
+      
+      DBG(r, "not convert content-type:[%s]", r->content_type);
+      ap_pass_brigade(f->next, bb);
+      return APR_SUCCESS;
+    }
+  }
+  else {
+    DBG(r, "not convert content-type:[(null)]");
+    ap_pass_brigade(f->next, bb);
+    return APR_SUCCESS;
+  }
 
-  dconf  = ap_get_module_config(r->per_dir_config, &chxj_module);
-  entryp = chxj_apply_convrule(r, dconf->convrules);
+  dconf      = ap_get_module_config(r->per_dir_config, &chxj_module);
+  user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
+  if (ctx && ctx->entryp) entryp = ctx->entryp;
+  else                    entryp = chxj_apply_convrule(r, dconf->convrules);
+  if (ctx && ctx->spec)   spec   = ctx->spec;
+  else                    spec   = chxj_specified_device(r, user_agent);
 
 
   for (b = APR_BRIGADE_FIRST(bb);
        b != APR_BRIGADE_SENTINEL(bb); 
        b = APR_BUCKET_NEXT(b)) {
 
+    if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
+      DBG(r, "read data[%.*s]",(int)len, data);
+
+      if (f->ctx == NULL) {
+        /*--------------------------------------------------------------------*/
+        /* Start                                                              */
+        /*--------------------------------------------------------------------*/
+        DBG(r, "new context");
+        ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
+        if (len > 0) {
+          ctx->buffer = apr_palloc(r->pool, len);
+          memcpy(ctx->buffer, data, len);
+        }
+        else {
+          ctx->buffer = apr_palloc(r->pool, 1);
+          ctx->buffer = '\0';
+        }
+        ctx->len = len;
+        f->ctx = (void*)ctx;
+        ctx->entryp = entryp;
+        ctx->spec   = spec;
+      }
+      else {
+        /*--------------------------------------------------------------------*/
+        /* append data                                                        */
+        /*--------------------------------------------------------------------*/
+        char* tmp;
+        DBG(r, "append data start");
+        ctx = (mod_chxj_ctx*)f->ctx;
+
+        if (len > 0) {
+          tmp = apr_palloc(r->pool, ctx->len);
+          memcpy(tmp, ctx->buffer, ctx->len);
+
+          ctx->buffer = apr_palloc(r->pool, ctx->len + len);
+
+          memcpy(ctx->buffer, tmp, ctx->len);
+          memcpy(&ctx->buffer[ctx->len], data, len);
+
+          ctx->len += len;
+        }
+        DBG(r, "append data end");
+      }
+    }
+
     if (APR_BUCKET_IS_EOS(b)) {
 
       DBG(r, "eos");
       /*----------------------------------------------------------------------*/
       /* End Of File                                                          */
       /*----------------------------------------------------------------------*/
-      if (f->ctx) {
+      if (ctx) {
 
         ctx = (mod_chxj_ctx*)f->ctx;
 
-        DBG1(r, "content_type=[%s]", r->content_type);
+        DBG(r, "content_type=[%s]", r->content_type);
 
-        if (r->content_type 
-        && *(char*)r->content_type == 't' 
-        && strncmp(r->content_type, "text/html",   9) == 0) {
+        if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN 
+            && r->content_type 
+            && (STRNCASEEQ('a','A',"application/xhtml+xml", r->content_type, sizeof("application/xhtml+xml")-1)
+            ||  STRNCASEEQ('t','T',"text/html", r->content_type, sizeof("text/html")-1))) {
+          DBG(r, "detect exchange target:[%s]", r->content_type);
 
           if (ctx->len) {
             char* tmp;
@@ -622,15 +752,17 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
             memcpy(tmp, ctx->buffer, ctx->len);
 
 #if 0
-            DBG2(r, "input data=[%s] len=[%d]", tmp, ctx->len);
+            DBG(r, "input data=[%s] len=[%d]", tmp, ctx->len);
 #endif
 
             ctx->buffer = chxj_exchange(r, 
                                         (const char**)&tmp, 
-                                        (apr_size_t*)&ctx->len);
+                                        (apr_size_t*)&ctx->len,
+                                        spec,
+                                        user_agent, &cookie);
 
 #if 0
-            DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
+            DBG(r, "output data=[%.*s]", ctx->len,ctx->buffer);
 #endif
           }
           else {
@@ -638,14 +770,73 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
             ctx->len += 1;
             ctx->buffer = chxj_exchange(r, 
                                         (const char**)&ctx->buffer, 
-                                        (apr_size_t*)&ctx->len);
+                                        (apr_size_t*)&ctx->len,
+                                        spec,
+                                        user_agent, &cookie);
 
           }
         }
+        if (r->content_type
+            && *(char*)r->content_type == 't'
+            && strncmp(r->content_type, "text/xml",   8) == 0) {
+          DBG(r, "text/XML");
+
+          Doc       doc;
+          Node*     root;
+          Node*     child;
+          qr_code_t qrcode;
+          int       sts;
+      
+          memset(&doc,    0, sizeof(Doc));
+          memset(&qrcode, 0, sizeof(qr_code_t));
+          doc.r = r;
+          doc.parse_mode  = PARSE_MODE_CHTML;
+          qrcode.doc      = &doc;
+          qrcode.r        = r;
+      
+          qs_init_malloc(&doc);
+      
+          root = qs_parse_string(&doc, ctx->buffer, ctx->len);
+
+          sts = 0;
+          for (child = qs_get_child_node(&doc,root);
+               child ;
+               child = qs_get_next_node(&doc,child)) {
+        
+            char* name;
+        
+            name = qs_get_node_name(&doc,child);
+        
+            if (strcasecmp("qrcode",name) == 0) {
+              sts++;
+              break;
+            }
+          }
+          qs_all_free(&doc,QX_LOGMARK);
+          if (sts) {
+            r->handler = apr_psprintf(r->pool, "chxj-qrcode");
+            chxj_qrcode_node_to_qrcode(&qrcode, root);
+            sts = chxj_qrcode_create_image_data(&qrcode, &ctx->buffer, &ctx->len);
+            if (sts != OK) {
+              ERR(r, "qrcode create failed.");
+              return sts;
+            }
+            r->content_type = apr_psprintf(r->pool, "image/jpg");
+          }
+        }
 
-        if (r->content_type 
-        && *(char*)r->content_type == 'i' 
-        && strncmp(r->content_type, "image/", 6) == 0) {
+        if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN 
+            && r->content_type 
+            && ( *r->content_type == 'i' || *r->content_type == 'I')
+            && strncasecmp("image/", r->content_type, 6) == 0
+            && ( STRCASEEQ('j','J',"jpeg",            &r->content_type[6])         /* JPEG */
+              || STRCASEEQ('j','J',"jp2",             &r->content_type[6])         /* JPEG2000 */
+              || STRCASEEQ('j','J',"jpeg2000",        &r->content_type[6])         /* JPEG2000 */
+              || STRCASEEQ('j','J',"jpeg2000-image",  &r->content_type[6])         /* JPEG2000 */
+              || STRCASEEQ('x','X',"x-jpeg2000-image",&r->content_type[6])         /* JPEG2000 */
+              || STRCASEEQ('p','P',"png",             &r->content_type[6])         /* PNG */
+              || STRCASEEQ('x','X',"x-png",           &r->content_type[6])         /* PNG */
+              || STRCASEEQ('g','G',"gif",             &r->content_type[6]))) {     /* GIF */
           if (ctx->len) {
             char* tmp;
 
@@ -655,7 +846,7 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
             memcpy(tmp, ctx->buffer, ctx->len);
 
 #if 0
-            DBG1(r, "input data=[%s]", tmp);
+            DBG(r, "input data=[%s]", tmp);
 #endif
 
 
@@ -668,15 +859,25 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
               ctx->buffer = tmp;
 
 #if 0
-            DBG2(r, "output data=[%.*s]", ctx->len,ctx->buffer);
+            DBG(r, "output data=[%.*s]", ctx->len,ctx->buffer);
 #endif
           }
         }
 
-        contentLength = apr_psprintf(r->pool, "%d", ctx->len);
+        contentLength = apr_psprintf(r->pool, "%d", (int)ctx->len);
         apr_table_setn(r->headers_out, "Content-Length", contentLength);
         
         if (ctx->len > 0) {
+          DBG(r, "call pass_data_to_filter()");
+          location_header = (char*)apr_table_get(r->headers_out, "Location");
+          if (cookie && location_header) {
+            DBG(r, "Location Header=[%s]", location_header);
+            location_header = chxj_add_cookie_parameter(r,
+                                                        location_header,
+                                                        cookie);
+            apr_table_setn(r->headers_out, "Location", location_header);
+            DBG(r, "Location Header=[%s]", location_header);
+          }
           rv = pass_data_to_filter(f, 
                                    (const char*)ctx->buffer, 
                                    (apr_size_t)ctx->len);
@@ -686,7 +887,7 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
         return rv;
       }
       else {
-        DBG1(r, " SAVE COOKIE[%x]", entryp->action);
+        DBG(r, " SAVE COOKIE[%x]", entryp->action);
 
         /*
          * save cookie.
@@ -710,13 +911,13 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
              * Location Header Check to add cookie parameter.
              */
             location_header = (char*)apr_table_get(r->headers_out, "Location");
-            if (location_header) {
-              DBG1(r, "Location Header=[%s]", location_header);
+            if (cookie && location_header) {
+              DBG(r, "Location Header=[%s]", location_header);
               location_header = chxj_add_cookie_parameter(r,
                                                           location_header,
                                                           cookie);
               apr_table_setn(r->headers_out, "Location", location_header);
-              DBG1(r, "Location Header=[%s]", location_header);
+              DBG(r, "Location Header=[%s]", location_header);
             }
             break;
 
@@ -726,54 +927,12 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
         }
 
         apr_table_setn(r->headers_out, "Content-Length", "0");
+        DBG(r, "call pass_data_to_filter()");
         rv = pass_data_to_filter(f, (const char*)"", (apr_size_t)0);
 
         return rv;
       }
     }
-    else
-    if (apr_bucket_read(b, &data, &len, APR_BLOCK_READ) == APR_SUCCESS) {
-      DBG2(r, "read data[%.*s]",len, data);
-
-      if (f->ctx == NULL) {
-        /*--------------------------------------------------------------------*/
-        /* Start                                                              */
-        /*--------------------------------------------------------------------*/
-        DBG(r, "new context");
-        ctx = (mod_chxj_ctx*)apr_palloc(r->pool, sizeof(mod_chxj_ctx));
-        if (len > 0) {
-          ctx->buffer = apr_palloc(r->pool, len);
-          memcpy(ctx->buffer, data, len);
-        }
-        else {
-          ctx->buffer = apr_palloc(r->pool, 1);
-          ctx->buffer = '\0';
-        }
-        ctx->len = len;
-        f->ctx = (void*)ctx;
-      }
-      else {
-        /*--------------------------------------------------------------------*/
-        /* append data                                                        */
-        /*--------------------------------------------------------------------*/
-        char* tmp;
-        DBG(r, "append data start");
-        ctx = (mod_chxj_ctx*)f->ctx;
-
-        if (len > 0) {
-          tmp = apr_palloc(r->pool, ctx->len);
-          memcpy(tmp, ctx->buffer, ctx->len);
-
-          ctx->buffer = apr_palloc(r->pool, ctx->len + len);
-
-          memcpy(ctx->buffer, tmp, ctx->len);
-          memcpy(&ctx->buffer[ctx->len], data, len);
-
-          ctx->len += len;
-        }
-        DBG(r, "append data end");
-      }
-    }
   }
   apr_brigade_destroy(bb);
 
@@ -799,29 +958,31 @@ chxj_input_filter(ap_filter_t*        f,
                  apr_read_type_e      block,
                  apr_off_t            readbytes)
 {
-  request_rec*        r;
+  request_rec         *r;
   apr_status_t        rv;
-  conn_rec*           c;
-  apr_bucket*         b;
+  conn_rec            *c;
+  apr_bucket          *b;
   /*--------------------------------------------------------------------------*/
   /* It is the brigade area for output                                        */
   /*--------------------------------------------------------------------------*/
-  apr_bucket_brigadeibb;            
+  apr_bucket_brigade  *ibb;            
   /*--------------------------------------------------------------------------*/
   /* It is the brigade area for input                                         */
   /*--------------------------------------------------------------------------*/
-  apr_bucket_brigadeobb;
+  apr_bucket_brigade  *obb;
   apr_size_t          len;
-  apr_bucket*         tmp_heap;
-  apr_bucket*         eos;
-  const char*         data;
-  char*               data_bucket;
-  char*               data_brigade;
-  char*               content_type;
-  device_table*       spec ;
-  char*               user_agent;
-  mod_chxj_config*    dconf;
-  chxjconvrule_entry* entryp;
+  apr_bucket          *tmp_heap;
+  apr_bucket          *eos;
+  const char          *data;
+  char                *data_bucket;
+  char                *data_brigade;
+  char                *content_type;
+  device_table        *spec = NULL;
+  char                *user_agent = NULL;
+  mod_chxj_config     *dconf;
+  chxjconvrule_entry  *entryp = NULL;
+  mod_chxj_ctx        *ctx = (mod_chxj_ctx *)f->ctx;
+  int                 eos_flag = 0;
 
   r = f->r;
   c = r->connection;
@@ -836,7 +997,7 @@ chxj_input_filter(ap_filter_t*        f,
 
   content_type = (char*)apr_table_get(r->headers_in, "Content-Type");
   if (content_type 
-  && strncasecmp("multipart/form-data", content_type, 19) == 0) {
+      && strncasecmp("multipart/form-data", content_type, 19) == 0) {
 
     DBG(r, "detect multipart/form-data");
     ap_remove_input_filter(f);
@@ -845,8 +1006,14 @@ chxj_input_filter(ap_filter_t*        f,
   }
 
   dconf = ap_get_module_config(r->per_dir_config, &chxj_module);
+  user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
+
+  if (ctx && ctx->entryp) entryp = ctx->entryp;
+  else                    entryp = chxj_apply_convrule(r, dconf->convrules);
+
+  if (ctx && ctx->spec) spec = ctx->spec;
+  else                  spec = chxj_specified_device(r, user_agent);
 
-  entryp = chxj_apply_convrule(r, dconf->convrules);
   if (!entryp || !(entryp->action & CONVRULE_ENGINE_ON_BIT)) {
     DBG(r,"EngineOff");
 
@@ -854,8 +1021,6 @@ chxj_input_filter(ap_filter_t*        f,
     return ap_get_brigade(f->next, bb, mode, block, readbytes);
   }
 
-  user_agent = (char*)apr_table_get(r->headers_in, "User-Agent");
-  spec = chxj_specified_device(r, user_agent);
 
   switch(spec->html_spec_type) {
   case CHXJ_SPEC_Chtml_1_0:
@@ -879,8 +1044,24 @@ chxj_input_filter(ap_filter_t*        f,
     DBG(r, "ap_get_brigade() failed");
     return rv;
   }
+  if (!ctx) {
+    ctx = apr_palloc(r->pool, sizeof(*ctx));
+    memset(ctx, 0, sizeof(*ctx));
+    if ((rv = apr_pool_create(&ctx->pool, r->pool)) != APR_SUCCESS) {
+      ERR(r, "failed: new pool create. rv:[%d]", rv);
+      return rv;
+    }
+    ctx->entryp = entryp;
+    ctx->spec = spec;
+    ctx->buffer = apr_palloc(ctx->pool, 1);
+    ctx->buffer[0] = 0;
+    f->ctx = ctx;
+  }
+
+  for (b =  APR_BRIGADE_FIRST(ibb); 
+       b != APR_BRIGADE_SENTINEL(ibb);
+       b =  APR_BUCKET_NEXT(b)) {
 
-  APR_BRIGADE_FOREACH(b, ibb) {
     rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
     if (rv != APR_SUCCESS) {
       DBG(r, "apr_bucket_read() failed");
@@ -888,50 +1069,60 @@ chxj_input_filter(ap_filter_t*        f,
     }
 
     if (data != NULL) {
-      data_bucket = apr_palloc(r->pool, len+1);
+      ctx->len += len;
+      data_bucket = apr_palloc(ctx->pool, len+1);
       memset((void*)data_bucket, 0, len+1);
       memcpy(data_bucket, data, len);
-      DBG1(r, "(in)POSTDATA:[%s]", data_bucket);
-  
-      data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
+      DBG(r, "(in)POSTDATA:[%s]", data_bucket);
+      ctx->buffer = apr_pstrcat(ctx->pool, ctx->buffer, data_bucket, NULL);
     }
-
     if (APR_BUCKET_IS_EOS(b)) {
+      DBG(r, "eos");
+      eos_flag = 1;
       break;
     }
   }
   apr_brigade_cleanup(ibb);
 
-
-  len = strlen(data_brigade);
-  if (len == 0) {
+  if (ctx->len == 0) {
     DBG(r,"data_brigade length is 0");
     DBG(r,"end of chxj_input_filter()");
     ap_remove_input_filter(f);
     return ap_get_brigade(f->next, bb, mode, block, readbytes);
   }
 
-  data_brigade = chxj_input_convert(
-    r, 
-    (const char**)&data_brigade, 
-    (apr_size_t*)&len,
-    entryp
-    );
-
-  if (len > 0) {
-    DBG1(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
-
-    obb = apr_brigade_create(r->pool, c->bucket_alloc);
-
-    tmp_heap = apr_bucket_heap_create(data_brigade, 
-                                      len, 
-                                      NULL, 
-                                      f->c->bucket_alloc);
-    eos      = apr_bucket_eos_create(f->c->bucket_alloc);
-
-    APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
-    APR_BRIGADE_INSERT_TAIL(obb, eos);
-    APR_BRIGADE_CONCAT(bb, obb);
+  if (eos_flag) {
+    len = ctx->len;
+    data_brigade = chxj_input_convert(
+      r, 
+      (const char**)&ctx->buffer, 
+      (apr_size_t*)&len,
+      entryp
+      );
+  
+    if (len > 0) {
+      DBG(r, "(in:exchange)POSTDATA:[%s]", data_brigade);
+  
+      obb = apr_brigade_create(r->pool, c->bucket_alloc);
+  
+      tmp_heap = apr_bucket_heap_create(data_brigade, 
+                                        len, 
+                                        NULL, 
+                                        f->c->bucket_alloc);
+      eos = apr_bucket_eos_create(f->c->bucket_alloc);
+  
+      APR_BRIGADE_INSERT_TAIL(obb, tmp_heap);
+      APR_BRIGADE_INSERT_TAIL(obb, eos);
+      APR_BRIGADE_CONCAT(bb, obb);
+    }
+    else {
+      obb = apr_brigade_create(r->pool, c->bucket_alloc);
+      eos = apr_bucket_eos_create(f->c->bucket_alloc);
+      APR_BRIGADE_INSERT_TAIL(obb, eos);
+      APR_BRIGADE_CONCAT(bb, obb);
+    }
+    apr_pool_destroy(ctx->pool);
+    f->ctx = NULL;
   }
 
   DBG(r, "end of chxj_input_filter()");
@@ -966,8 +1157,8 @@ chxj_global_config_create(apr_pool_t* pool, server_rec* s)
  */
 static int 
 chxj_init_module(apr_pool_t *p, 
-                  apr_pool_t *plog
-                  apr_pool_t *ptemp
+                  apr_pool_t* UNUSED(plog)
+                  apr_pool_t* UNUSED(ptemp)
                   server_rec *s)
 {
 #if 0
@@ -1021,7 +1212,7 @@ chxj_init_module(apr_pool_t *p,
 
 
 static void 
-chxj_child_init(apr_pool_t *p, server_rec *s)
+chxj_child_init(apr_pool_t* UNUSED(p), server_rec *s)
 {
 #if 0
   mod_chxj_global_config* conf;
@@ -1033,8 +1224,7 @@ chxj_child_init(apr_pool_t *p, server_rec *s)
   conf = (mod_chxj_global_config*)ap_get_module_config(s->module_config, 
                                                        &chxj_module);
 
-  if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p) 
-  != APR_SUCCESS) {
+  if (apr_global_mutex_child_init(&conf->cookie_db_lock, NULL, p) != APR_SUCCESS) {
     SERR(s, "Can't attach global mutex.");
     return;
   }
@@ -1083,22 +1273,7 @@ DBG(r, "start chxj_insert_filter()");
   spec = chxj_specified_device(r, user_agent);
   entryp = NULL;
 
-  switch(spec->html_spec_type) {
-  case CHXJ_SPEC_Chtml_1_0:
-  case CHXJ_SPEC_Chtml_2_0:
-  case CHXJ_SPEC_Chtml_3_0:
-  case CHXJ_SPEC_Chtml_4_0:
-  case CHXJ_SPEC_Chtml_5_0:
-  case CHXJ_SPEC_XHtml_Mobile_1_0:
-  case CHXJ_SPEC_Hdml:
-  case CHXJ_SPEC_Jhtml:
-    entryp = chxj_apply_convrule(r, dconf->convrules);
-    break;
-  
-  default:
-    break;
-
-  }
+  entryp = chxj_apply_convrule(r, dconf->convrules);
   if (!entryp) {
     DBG(r, "end chxj_insert_filter()");
     return;
@@ -1119,7 +1294,7 @@ DBG(r, "end   chxj_insert_filter()");
  * @param p
  */
 static void 
-chxj_register_hooks(apr_pool_t *p)
+chxj_register_hooks(apr_pool_t* UNUSED(p))
 {
   ap_hook_post_config(chxj_init_module,
                       NULL,
@@ -1166,9 +1341,22 @@ chxj_create_per_dir_config(apr_pool_t *p, char *arg)
   conf->emoji_tail       = NULL;
   conf->image            = CHXJ_IMG_OFF;
   conf->image_cache_dir  = apr_psprintf(p, "%s",DEFAULT_IMAGE_CACHE_DIR);
+  conf->image_cache_limit = 0;
   conf->server_side_encoding = NULL;
   conf->cookie_db_dir    = NULL;
   conf->cookie_timeout   = 0;
+  conf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
+  conf->cookie_lazy_mode  = 0;
+#if defined(USE_MYSQL_COOKIE)
+  memset((void*)&conf->mysql, 0, sizeof(mysql_t));
+  conf->mysql.port       = MYSQL_PORT;
+  conf->mysql.host       = NULL;
+#endif
+#if defined(USE_MEMCACHE_COOKIE)
+  memset((void*)&conf->memcache, 0, sizeof(memcache_t));
+  conf->memcache.host    = NULL;
+  conf->memcache.port    = 0;
+#endif
 
   if (arg == NULL) {
     conf->dir                  = NULL;
@@ -1207,6 +1395,7 @@ chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
   mrg->image            = CHXJ_IMG_OFF;
   mrg->image_cache_dir  = NULL;
   mrg->image_copyright  = NULL;
+  mrg->image_cache_limit  = 0;
   mrg->emoji            = NULL;
   mrg->emoji_tail       = NULL;
 
@@ -1238,10 +1427,19 @@ chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
     mrg->image = add->image;
 
 
-  if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) 
+  if (strcasecmp(add->image_cache_dir ,DEFAULT_IMAGE_CACHE_DIR)==0) {
     mrg->image_cache_dir = apr_pstrdup(p, base->image_cache_dir);
-  else 
+  }
+  else {
     mrg->image_cache_dir = apr_pstrdup(p, add->image_cache_dir);
+  }
+
+  if (add->image_cache_limit) {
+    mrg->image_cache_limit = add->image_cache_limit;
+  }
+  else {
+    mrg->image_cache_limit = base->image_cache_limit;
+  }
 
   if (add->image_copyright) 
     mrg->image_copyright = apr_pstrdup(p, add->image_copyright);
@@ -1283,6 +1481,124 @@ chxj_merge_per_dir_config(apr_pool_t *p, void *basev, void *addv)
     mrg->cookie_timeout   = 0;
   }
 
+#if defined(USE_MYSQL_COOKIE)
+  if (add->mysql.host) {
+    mrg->mysql.host = apr_pstrdup(p, add->mysql.host);
+  }
+  else if (base->mysql.host) {
+    mrg->mysql.host = apr_pstrdup(p, base->mysql.host);
+  }
+  else {
+    mrg->mysql.host = NULL;
+  }
+  if (add->mysql.port) {
+    mrg->mysql.port = add->mysql.port;
+  }
+  else if (base->mysql.port) {
+    mrg->mysql.port = base->mysql.port;
+  }
+  else {
+    mrg->mysql.port = 0;
+  }
+
+  if (add->mysql.database) {
+    mrg->mysql.database = apr_pstrdup(p, add->mysql.database);
+  }
+  else if (base->mysql.database) {
+    mrg->mysql.database = apr_pstrdup(p, base->mysql.database);
+  }
+  else {
+    mrg->mysql.database = NULL;
+  }
+
+  if (add->mysql.username) {
+    mrg->mysql.username = apr_pstrdup(p, add->mysql.username);
+  }
+  else if (base->mysql.username) {
+    mrg->mysql.username = apr_pstrdup(p, base->mysql.username);
+  }
+  else {
+    mrg->mysql.username = NULL;
+  }
+
+  if (add->mysql.password) {
+    mrg->mysql.password = apr_pstrdup(p, add->mysql.password);
+  }
+  else if (base->mysql.password) {
+    mrg->mysql.password = apr_pstrdup(p, base->mysql.password);
+  }
+  else {
+    mrg->mysql.password = NULL;
+  }
+
+  if (add->mysql.tablename) {
+    mrg->mysql.tablename = apr_pstrdup(p, add->mysql.tablename);
+  }
+  else if (base->mysql.tablename) {
+    mrg->mysql.tablename = apr_pstrdup(p, base->mysql.tablename);
+  }
+  else {
+    mrg->mysql.tablename = NULL;
+  }
+
+  if (add->mysql.socket_path) {
+    mrg->mysql.socket_path = apr_pstrdup(p, add->mysql.socket_path);
+  }
+  else if (base->mysql.socket_path) {
+    mrg->mysql.socket_path = apr_pstrdup(p, base->mysql.socket_path);
+  }
+  else {
+    mrg->mysql.socket_path = NULL;
+  }
+
+  if (add->mysql.charset) {
+    mrg->mysql.charset = apr_pstrdup(p, add->mysql.charset);
+  }
+  else if (base->mysql.charset) {
+    mrg->mysql.charset = apr_pstrdup(p, base->mysql.charset);
+  }
+  else {
+    mrg->mysql.charset = NULL;
+  }
+#endif
+#if defined(USE_MEMCACHE_COOKIE)
+  if (add->memcache.host) {
+    mrg->memcache.host = apr_pstrdup(p, add->memcache.host);
+  }
+  else if (base->memcache.host) {
+    mrg->memcache.host = apr_pstrdup(p, base->memcache.host);
+  }
+  else {
+    mrg->memcache.host = NULL;
+  }
+  if (add->memcache.port) {
+    mrg->memcache.port = add->memcache.port;
+  }
+  else if (base->memcache.port) {
+    mrg->memcache.port = base->memcache.port;
+  }
+  else {
+    mrg->memcache.port = 0;
+  }
+#endif
+  if (add->cookie_store_type) {
+    mrg->cookie_store_type = add->cookie_store_type;
+  }
+  else if (base->cookie_store_type) {
+    mrg->cookie_store_type = base->cookie_store_type;
+  }
+  else {
+    mrg->cookie_store_type = COOKIE_STORE_TYPE_NONE;
+  }
+  if (add->cookie_lazy_mode) {
+    mrg->cookie_lazy_mode = add->cookie_lazy_mode;
+  }
+  else if (base->cookie_lazy_mode) {
+    mrg->cookie_lazy_mode = base->cookie_lazy_mode;
+  }
+  else {
+    mrg->cookie_lazy_mode = 0;
+  }
   return mrg;
 }
 
@@ -1513,7 +1829,7 @@ cmd_load_emoji_data(cmd_parms *parms, void *mconfig, const char* arg)
 
 
 static const char* 
-cmd_set_image_engine(cmd_parms *parms, void *mconfig, const char* arg) 
+cmd_set_image_engine(cmd_parms* UNUSED(parms), void *mconfig, const char* arg) 
 {
   mod_chxj_config* conf;
   Doc              doc;
@@ -1552,6 +1868,35 @@ cmd_set_image_cache_dir(cmd_parms *parms, void *mconfig, const char* arg)
 
 
 static const char* 
+cmd_set_image_cache_limit(cmd_parms *parms, void *mconfig, const char* arg) 
+{
+  mod_chxj_config* conf;
+  Doc              doc;
+
+  doc.r = NULL;
+
+  if (strlen(arg) > IMAGE_CACHE_LIMIT_FMT_LEN) 
+    return "cache size is too long.";
+
+  conf = (mod_chxj_config*)mconfig;
+  errno = 0;
+  /* 
+   * I use strtol function because strtoul is not portable function. 
+   */
+  conf->image_cache_limit = (unsigned long)strtol(arg, NULL, 10);
+  switch (errno) {
+  case EINVAL:
+    return apr_psprintf(parms->pool, "ChxjImageCacheLimit invalid value [%s] errno:[%d]", arg, errno);
+  case ERANGE:
+    return apr_psprintf(parms->pool, "ChxjImageCacheLimit Out of range [%s] errno:[%d]", arg, errno);
+  default:
+    break;
+  }
+  return NULL;
+}
+
+
+static const char* 
 cmd_set_image_copyright(cmd_parms *parms, void* mconfig, const char* arg) 
 {
   mod_chxj_config* conf;
@@ -1641,7 +1986,7 @@ cmd_convert_rule(cmd_parms *cmd, void* mconfig, const char *arg)
   }
 
   mode = AP_REG_EXTENDED;
-  if ((regexp = ap_pregcomp(cmd->pool, pp, mode)) == NULL)
+  if ((regexp = ap_pregcomp((apr_pool_t *)cmd->pool, (const char *)pp, mode)) == NULL)
     return "RewriteRule: cannot compile regular expression ";
 
   newrule->regexp = regexp;
@@ -1685,14 +2030,281 @@ cmd_set_cookie_dir(
 
 static const char*
 cmd_set_cookie_timeout(
-  cmd_parms *cmd
-  void* mconfig, 
-  const char *arg)
+  cmd_parms*  UNUSED(cmd)
+  void*       mconfig, 
+  const chararg)
 {
+  mod_chxj_config*    dconf;
+
+
+  if (strlen(arg) > 4096) 
+    return "mod_chxj: ChxjCookieTimeout is too long.";
+
+  if (chxj_chk_numeric(arg) != 0)
+    return "mod_chxj: ChxjCookieTimeout is not numeric.";
+
+  dconf = (mod_chxj_config*)mconfig;
+
+  dconf->cookie_timeout = atoi(arg);
+
+  return NULL;
+}
+
+
+#if defined(USE_MYSQL_COOKIE)
+static const char *
+cmd_set_cookie_mysql_database(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlDatabase is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.database = apr_pstrdup(cmd->pool, arg);
+
   return NULL;
 }
 
 
+static const char *
+cmd_set_cookie_mysql_username(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlUsername is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.username = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+
+
+static const char *
+cmd_set_cookie_mysql_password(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlPassword is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.password = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+
+
+static const char *
+cmd_set_cookie_mysql_table_name(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlTableName is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.tablename = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+
+static const char *
+cmd_set_cookie_mysql_port(
+  cmd_parms   *UNUSED(cmd), 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlPort is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  if (chxj_chk_numeric(arg) != 0)
+    return "mod_chxj: ChxjCookieMysqlPort is not numeric.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.port = chxj_atoi(arg);
+
+  return NULL;
+}
+
+
+static const char *
+cmd_set_cookie_mysql_host(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlHost is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.host = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+
+
+static const char *
+cmd_set_cookie_mysql_socket_path(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 4096) 
+    return "mod_chxj: ChxjCookieMysqlSocketPath is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.socket_path = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+
+
+static const char *
+cmd_set_cookie_mysql_charset(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMysqlCharset is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->mysql.charset = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+#endif
+#if defined(USE_MEMCACHE_COOKIE)
+static const char *
+cmd_set_cookie_memcache_port(
+  cmd_parms   *UNUSED(cmd), 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMemcachePort is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  if (chxj_chk_numeric(arg) != 0)
+    return "mod_chxj: ChxjCookieMemcachePort is not numeric.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->memcache.port = (apr_port_t)chxj_atoi(arg);
+
+  return NULL;
+}
+
+
+static const char *
+cmd_set_cookie_memcache_host(
+  cmd_parms   *cmd, 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieMemcacheHost is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  dconf->memcache.host = apr_pstrdup(cmd->pool, arg);
+
+  return NULL;
+}
+#endif
+
+static const char *
+cmd_set_cookie_lazy_mode(
+  cmd_parms   *UNUSED(cmd), 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieLazyMode is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  if (strcasecmp("TRUE",arg) == 0) {
+    dconf->cookie_lazy_mode = COOKIE_LAZY_ON;
+  }
+  else {
+    dconf->cookie_lazy_mode = COOKIE_LAZY_OFF;
+  }
+
+  return NULL;
+}
+
+static const char *
+cmd_set_cookie_store_type(
+  cmd_parms   *UNUSED(cmd), 
+  void        *mconfig, 
+  const char  *arg)
+{
+  mod_chxj_config  *dconf;
+
+  if (strlen(arg) > 255) 
+    return "mod_chxj: ChxjCookieStoreType is too long.";
+
+  dconf = (mod_chxj_config *)mconfig;
+
+  if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_DBM, arg) == 0) {
+    dconf->cookie_store_type = COOKIE_STORE_TYPE_DBM;
+  }
+  else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MYSQL, arg) == 0) {
+    dconf->cookie_store_type = COOKIE_STORE_TYPE_MYSQL;
+  }
+  else if (strcasecmp(CHXJ_COOKIE_STORE_TYPE_MEMCACHE, arg) == 0) {
+    dconf->cookie_store_type = COOKIE_STORE_TYPE_MEMCACHE;
+  }
+  else {
+    dconf->cookie_store_type = COOKIE_STORE_TYPE_NONE;
+  }
+
+  return NULL;
+}
+
+
+
 static const command_rec cmds[] = {
   AP_INIT_TAKE1(
     "ChxjLoadDeviceData",
@@ -1719,6 +2331,12 @@ static const command_rec cmds[] = {
     OR_ALL,
     "Image Cache Directory"),
   AP_INIT_TAKE1(
+    "ChxjImageCacheLimit",
+    cmd_set_image_cache_limit,
+    NULL,
+    OR_ALL,
+    "Image Cache Limit"),
+  AP_INIT_TAKE1(
     "ChxjImageCopyright",
     cmd_set_image_copyright,
     NULL,
@@ -1742,7 +2360,83 @@ static const command_rec cmds[] = {
     NULL,
     OR_ALL,
     "The compulsion time-out time of the cookie is specified. "),
-  { NULL },
+  AP_INIT_TAKE1(
+    "ChxjCookieStoreType",
+    cmd_set_cookie_store_type,
+    NULL,
+    OR_ALL,
+    "It specifies preserving of the cookie ahead. (DBM/MYSQL/MEMCACHE)"),
+  AP_INIT_TAKE1(
+    "ChxjCookieLazyMode",
+    cmd_set_cookie_lazy_mode,
+    NULL,
+    OR_ALL,
+    "OneTimeID is negligently done. (TRUE/FALSE)"),
+#if defined(USE_MYSQL_COOKIE)
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlHost",
+    cmd_set_cookie_mysql_host,
+    NULL,
+    OR_ALL,
+    "The MySQL database host used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlPort",
+    cmd_set_cookie_mysql_port,
+    NULL,
+    OR_ALL,
+    "The MySQL database port used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlDatabase",
+    cmd_set_cookie_mysql_database,
+    NULL,
+    OR_ALL,
+    "The MySQL database name used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlUsername",
+    cmd_set_cookie_mysql_username,
+    NULL,
+    OR_ALL,
+    "The MySQL username used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlPassword",
+    cmd_set_cookie_mysql_password,
+    NULL,
+    OR_ALL,
+    "The MySQL password used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlTableName",
+    cmd_set_cookie_mysql_table_name,
+    NULL,
+    OR_ALL,
+    "The MySQL table name used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlSocketPath",
+    cmd_set_cookie_mysql_socket_path,
+    NULL,
+    OR_ALL,
+    "The MySQL socket path used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMysqlCharset",
+    cmd_set_cookie_mysql_charset,
+    NULL,
+    OR_ALL,
+    "The MySQL charset used by saving Cookie"),
+#endif
+#if defined(USE_MEMCACHE_COOKIE)
+  AP_INIT_TAKE1(
+    "ChxjCookieMemcacheHost",
+    cmd_set_cookie_memcache_host,
+    NULL,
+    OR_ALL,
+    "The Memcached host used by saving Cookie"),
+  AP_INIT_TAKE1(
+    "ChxjCookieMemcachePort",
+    cmd_set_cookie_memcache_port,
+    NULL,
+    OR_ALL,
+    "The Memcached port used by saving Cookie"),
+#endif
+  {NULL}
 };