OSDN Git Service

* updated changelog.
[modchxj/mod_chxj.git] / src / mod_chxj.c
index 586f086..a1bba8d 100644 (file)
@@ -61,6 +61,9 @@
 #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 "/"
@@ -187,7 +190,7 @@ 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, device_table *spec, const char *ua)
+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;
@@ -196,6 +199,7 @@ chxj_exchange(request_rec *r, const char** src, apr_size_t* len, device_table *s
   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);
@@ -216,13 +220,13 @@ chxj_exchange(request_rec *r, const char** src, apr_size_t* len, device_table *s
     user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
 
   DBG(r,"User-Agent:[%s]", user_agent);
-  DBG(r, "start chxj_exchange()");
   DBG(r,"content type is %s", r->content_type);
 
 
-  if (*(char*)r->content_type == 't' 
-  && strncmp(r->content_type, "text/html",   9) != 0) {
-    DBG(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;
   }
 
@@ -283,8 +287,11 @@ chxj_exchange(request_rec *r, const char** src, apr_size_t* len, device_table *s
     *len = 1;
   }
   dst[*len] = 0;
+  if (cookie) {
+    *cookiep = cookie;
+  }
 
-  DBG(r, "end chxj_exchange()");
+  DBG(r, "end of chxj_exchange()");
 
   return dst;
 }
@@ -410,7 +417,7 @@ chxj_convert_input_header(request_rec *r,chxjconvrule_entry* entryp)
       DBG(r, "call start chxj_load_cookie()");
       cookie = chxj_load_cookie(r, value);
       DBG(r, "call end   chxj_load_cookie()");
-      if (! no_update_flag) {
+      if (! no_update_flag && cookie) {
         chxj_update_cookie(r, cookie);
       }
     }
@@ -550,7 +557,7 @@ chxj_input_convert(
       DBG(r, "call start chxj_load_cookie()");
       cookie = chxj_load_cookie(r, value);
       DBG(r, "call end   chxj_load_cookie()");
-      if (! no_update_flag) {
+      if (! no_update_flag && cookie) {
         chxj_update_cookie(r, cookie);
       }
     }
@@ -615,14 +622,14 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
   apr_bucket*         b;
   const char*         data;
   char*               contentLength;
-  char*               user_agent;
+  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;
-  device_table        *spec;
+  chxjconvrule_entry  *entryp = NULL;
+  device_table        *spec = NULL;
 
 
 
@@ -636,24 +643,95 @@ 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);
   user_agent = (char*)apr_table_get(r->headers_in, HTTP_USER_AGENT);
-  spec       = chxj_specified_device(r, 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;
 
@@ -661,8 +739,9 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
 
         if (spec->html_spec_type != CHXJ_SPEC_UNKNOWN 
             && r->content_type 
-            && *(char*)r->content_type == 't' 
-            && strncmp(r->content_type, "text/html",   9) == 0) {
+            && (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;
@@ -680,7 +759,7 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
                                         (const char**)&tmp, 
                                         (apr_size_t*)&ctx->len,
                                         spec,
-                                        user_agent);
+                                        user_agent, &cookie);
 
 #if 0
             DBG(r, "output data=[%.*s]", ctx->len,ctx->buffer);
@@ -693,7 +772,7 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
                                         (const char**)&ctx->buffer, 
                                         (apr_size_t*)&ctx->len,
                                         spec,
-                                        user_agent);
+                                        user_agent, &cookie);
 
           }
         }
@@ -789,6 +868,16 @@ chxj_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
         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);
@@ -822,7 +911,7 @@ 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) {
+            if (cookie && location_header) {
               DBG(r, "Location Header=[%s]", location_header);
               location_header = chxj_add_cookie_parameter(r,
                                                           location_header,
@@ -838,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) {
-      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;
-      }
-      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);
 
@@ -911,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;
@@ -948,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);
@@ -957,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");
 
@@ -966,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:
@@ -991,10 +1044,20 @@ 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;
+  }
 
-#if 0
-  APR_BRIGADE_FOREACH(b, ibb) {
-#endif
   for (b =  APR_BRIGADE_FIRST(ibb); 
        b != APR_BRIGADE_SENTINEL(ibb);
        b =  APR_BUCKET_NEXT(b)) {
@@ -1006,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);
       DBG(r, "(in)POSTDATA:[%s]", data_bucket);
-  
-      data_brigade = apr_pstrcat(r->pool, data_brigade, data_bucket, NULL);
+      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) {
-    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);
+  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()");
@@ -1272,6 +1345,18 @@ chxj_create_per_dir_config(apr_pool_t *p, char *arg)
   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;
@@ -1396,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;
 }
 
@@ -1848,6 +2051,260 @@ cmd_set_cookie_timeout(
 }
 
 
+#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",
@@ -1903,6 +2360,82 @@ static const command_rec cmds[] = {
     NULL,
     OR_ALL,
     "The compulsion time-out time of the cookie is specified. "),
+  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}
 };