OSDN Git Service

implement cookie domain and host check.
[bbk/bchan.git] / src / cookiedb.c
index 90b03b9..c0a1e83 100644 (file)
@@ -30,6 +30,7 @@
 #include       <basic.h>
 #include       <bstdio.h>
 #include       <bstdlib.h>
+#include       <bstring.h>
 #include       <bsys/queue.h>
 
 #ifdef BCHAN_CONFIG_DEBUG
@@ -63,6 +64,46 @@ EXPORT W ascstr_appendstr(ascstr_t *astr, UB *apd, W apd_len)
        return 0;
 }
 
+EXPORT W ascstr_cmp(ascstr_t *s1, ascstr_t *s2)
+{
+       W i,len;
+
+       len = s1->len > s2->len ? s2->len : s1->len;
+       for (i = 0; i < len; i++) {
+               if (s1->str[i] > s2->str[i]) {
+                       return 1;
+               } else if (s1->str[i] < s2->str[i]) {
+                       return -1;
+               }
+       }
+
+       /* check terminate char: '\0' */
+       if (s1->str[i] > s2->str[i]) {
+               return 1;
+       } else if (s1->str[i] < s2->str[i]) {
+               return -1;
+       }
+       return 0; /* same length */
+}
+
+/*
+ * match example
+ *  astr:    XXXXYYYYZZZZ
+ *  suffix:       YYYZZZZ
+ *
+ */
+EXPORT Bool ascstr_suffixcmp(ascstr_t *astr, ascstr_t *suffix)
+{
+       if (astr->len < suffix->len) {
+               return False;
+       }
+       if (strncmp(astr->str + astr->len - suffix->len, suffix->str, suffix->len) != 0) {
+               return False;
+       }
+
+       return True;
+}
+
 EXPORT W ascstr_initialize(ascstr_t *astr)
 {
        astr->str = malloc(sizeof(UB));
@@ -81,8 +122,9 @@ EXPORT VOID ascstr_finalize(ascstr_t *astr)
 
 struct httpcookie_t_ {
        QUEUE que;
-       /* requested host */
-       ascstr_t host;
+       /* requested host and path */
+       ascstr_t origin_host;
+       ascstr_t origin_path;
        /* Set-Cookie value */
        ascstr_t attr; /* NAME string */
        ascstr_t name; /* VALUE string */
@@ -96,6 +138,52 @@ struct httpcookie_t_ {
 };
 typedef struct httpcookie_t_ httpcookie_t;
 
+LOCAL ascstr_t* httpcookie_getvaliddomain(httpcookie_t *cookie)
+{
+       if (cookie->domain.len != 0) {
+               return &cookie->domain;
+       }
+       return &cookie->origin_host;
+}
+
+LOCAL ascstr_t* httpcookie_getvalidpath(httpcookie_t *cookie)
+{
+       if (cookie->path.len != 0) {
+               return &cookie->path;
+       }
+       return &cookie->origin_path;
+}
+
+LOCAL Bool httpcookie_isvalueset(httpcookie_t *cookie)
+{
+       if (cookie->attr.len == 0) {
+               return False;
+       }
+       if (cookie->name.len == 0) {
+               return False;
+       }
+       return True;
+}
+
+/* check replace condition. */
+LOCAL Bool httpcookie_issamekey(httpcookie_t *cookie1, httpcookie_t *cookie2)
+{
+       W cmp;
+       ascstr_t *as1, *as2;
+
+       cmp = ascstr_cmp(&cookie1->attr, &cookie2->attr);
+       if (cmp != 0) {
+               return False;
+       }
+       as1 = httpcookie_getvaliddomain(cookie1);
+       as2 = httpcookie_getvaliddomain(cookie2);
+       cmp = ascstr_cmp(as1, as2);
+       if (cmp != 0) {
+               return False;
+       }
+       return True;
+}
+
 LOCAL httpcookie_t* httpcookie_nextnode(httpcookie_t *cookie)
 {
        return (httpcookie_t*)cookie->que.next;
@@ -109,18 +197,35 @@ LOCAL VOID httpcookie_setexpires(httpcookie_t *cookie, STIME expires)
 LOCAL VOID httpcookie_QueRemove(httpcookie_t *cookie)
 {
        QueRemove(&cookie->que);
+       QueInit(&cookie->que);
+}
+
+LOCAL VOID httpcookie_QueInsert(httpcookie_t *entry, httpcookie_t *que)
+{
+       QueInsert(&entry->que, &que->que);
 }
 
-LOCAL W httpcookie_initialize(httpcookie_t *cookie, UB *host, W host_len)
+LOCAL W httpcookie_initialize(httpcookie_t *cookie, UB *host, W host_len, UB *path, W path_len)
 {
        W err;
 
        QueInit(&cookie->que);
-       err = ascstr_initialize(&cookie->host);
+       err = ascstr_initialize(&cookie->origin_host);
        if (err < 0) {
-               goto error_host;
+               goto error_originhost;
+       }
+       err = ascstr_appendstr(&cookie->origin_host, host, host_len);
+       if (err < 0) {
+               goto error_originhost_append;
+       }
+       err = ascstr_initialize(&cookie->origin_path);
+       if (err < 0) {
+               goto error_originpath;
+       }
+       err = ascstr_appendstr(&cookie->origin_path, path, path_len);
+       if (err < 0) {
+               goto error_originpath_append;
        }
-       ascstr_appendstr(&cookie->host, host, host_len);
        err = ascstr_initialize(&cookie->attr);
        if (err < 0) {
                goto error_attr;
@@ -164,8 +269,12 @@ error_comment:
 error_name:
        ascstr_finalize(&cookie->attr);
 error_attr:
-       ascstr_finalize(&cookie->host);
-error_host:
+error_originpath_append:
+       ascstr_finalize(&cookie->origin_path);
+error_originpath:
+error_originhost_append:
+       ascstr_finalize(&cookie->origin_host);
+error_originhost:
        return err;
 }
 
@@ -177,11 +286,12 @@ LOCAL VOID httpcookie_finalize(httpcookie_t *cookie)
        ascstr_finalize(&cookie->comment);
        ascstr_finalize(&cookie->name);
        ascstr_finalize(&cookie->attr);
-       ascstr_finalize(&cookie->host);
+       ascstr_finalize(&cookie->origin_path);
+       ascstr_finalize(&cookie->origin_host);
        QueRemove(&cookie->que);
 }
 
-LOCAL httpcookie_t* httpcookie_new(UB *host, W host_len)
+LOCAL httpcookie_t* httpcookie_new(UB *host, W host_len, UB *path, W path_len)
 {
        httpcookie_t *cookie;
        W err;
@@ -190,7 +300,7 @@ LOCAL httpcookie_t* httpcookie_new(UB *host, W host_len)
        if (cookie == NULL) {
                return NULL;
        }
-       err = httpcookie_initialize(cookie, host, host_len);
+       err = httpcookie_initialize(cookie, host, host_len, path, path_len);
        if (err < 0) {
                free(cookie);
                return NULL;
@@ -204,102 +314,137 @@ LOCAL VOID httpcookie_delete(httpcookie_t *cookie)
        free(cookie);
 }
 
-struct cookie_persistentdb_t_ {
+struct cookiedb_t_ {
        QUEUE sentinel;
        LINK *lnk;
 };
-typedef struct cookie_persistentdb_t_ cookie_persistentdb_t;
-
-struct cookie_volatiledb_t_ {
-       QUEUE sentinel;
-};
-typedef struct cookie_volatiledb_t_ cookie_volatiledb_t;
-
-struct cookiedb_t_ {
-       cookie_volatiledb_t vdb;
-       cookie_persistentdb_t pdb;
-};
 
-LOCAL httpcookie_t* cookie_persistentdb_sentinelnode(cookie_persistentdb_t *db)
+LOCAL httpcookie_t* cookiedb_sentinelnode(cookiedb_t *db)
 {
        return (httpcookie_t*)&db->sentinel;
 }
 
-LOCAL VOID cookie_persistentdb_insertcookie(cookie_persistentdb_t *db, httpcookie_t *cookie)
-{
-       QueInsert(&cookie->que, &db->sentinel);
-}
-
-LOCAL W cookie_persistentdb_initialize(cookie_persistentdb_t *db, LINK *db_lnk)
-{
-       QueInit(&db->sentinel);
-       db->lnk = db_lnk;
-       return 0;
-}
+struct cookiedb_writeiterator_t_ {
+       cookiedb_t *origin;
+       ascstr_t host;
+       ascstr_t path;
+       Bool secure;
+       STIME time;
+       httpcookie_t *current;
+};
+typedef struct cookiedb_writeiterator_t_ cookiedb_writeiterator_t;
 
-LOCAL VOID cookie_persistentdb_finalize(cookie_persistentdb_t *db)
+LOCAL W count_priod(ascstr_t *str)
 {
-       httpcookie_t *cookie;
-       Bool empty;
+       W i,count;
 
-       for (;;) {
-               empty = isQueEmpty(&db->sentinel);
-               if (empty == True) {
-                       break;
+       count = 0;
+       for (i = 0; i < str->len; i++) {
+               if (str->str[i] == '.') {
+                       count++;
                }
-               cookie = (httpcookie_t*)db->sentinel.prev;
-               httpcookie_delete(cookie);
        }
+
+       return count;
 }
 
-LOCAL httpcookie_t* cookie_volatiledb_sentinelnode(cookie_volatiledb_t *db)
+LOCAL Bool check_specified_TLD(ascstr_t *domain)
 {
-       return (httpcookie_t*)&db->sentinel;
+       if (strncmp(domain->str + domain->len - 4, ".net", 4) == 0) {
+               return True;
+       }
+       if (strncmp(domain->str + domain->len - 4, ".com", 4) == 0) {
+               return True;
+       }
+       if (strncmp(domain->str + domain->len - 4, ".edu", 4) == 0) {
+               return True;
+       }
+       if (strncmp(domain->str + domain->len - 4, ".org", 4) == 0) {
+               return True;
+       }
+       if (strncmp(domain->str + domain->len - 4, ".gov", 4) == 0) {
+               return True;
+       }
+       if (strncmp(domain->str + domain->len - 4, ".mil", 4) == 0) {
+               return True;
+       }
+       if (strncmp(domain->str + domain->len - 4, ".int", 4) == 0) {
+               return True;
+       }
+       /* TODO: other TLDs. */
+       return False;
 }
 
-LOCAL VOID cookie_volatiledb_insertcookie(cookie_volatiledb_t *db, httpcookie_t *cookie)
+LOCAL Bool cookiedb_writeiterator_checksendcondition_domaincheck(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
 {
-       QueInsert(&cookie->que, &db->sentinel);
+       Bool ok;
+       W count;
+
+       if (cookie->domain.len != 0) {
+               ok = ascstr_suffixcmp(&iter->host, &cookie->domain);
+               if (ok == False) {
+                       return False;
+               }
+               count = count_priod(&cookie->domain);
+               ok = check_specified_TLD(&cookie->domain);
+               if (ok == True) {
+                       if (count < 2) {
+                               return False;
+                       }
+               } else {
+                       if (count < 3) {
+                               return False;
+                       }
+               }
+       } else {
+               ok = ascstr_suffixcmp(&iter->host, &cookie->origin_host);
+               if (ok == False) {
+                       return False;
+               }
+       }
+
+       return True;
 }
 
-LOCAL W cookie_volatiledb_initialize(cookie_volatiledb_t *db)
+LOCAL Bool cookiedb_writeitereator_pathcheck(ascstr_t *send_path, ascstr_t *origin_path)
 {
-       QueInit(&db->sentinel);
-       return 0;
+       if (origin_path->len < send_path->len) {
+               return False;
+       }
+       if (strncmp(origin_path->str, send_path->str, send_path->len) != 0) {
+               return False;
+       }
+       return True;
 }
 
-LOCAL VOID cookie_volatiledb_finalize(cookie_volatiledb_t *db)
+LOCAL Bool cookiedb_writeiterator_checksendcondition(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
 {
-       httpcookie_t *cookie;
-       Bool empty;
+       Bool ok;
 
-       for (;;) {
-               empty = isQueEmpty(&db->sentinel);
-               if (empty == True) {
-                       break;
+       if (cookie->secure == True) {
+               if (iter->secure != True) {
+                       return False;
                }
-               cookie = (httpcookie_t*)db->sentinel.prev;
-               httpcookie_delete(cookie);
        }
-}
-
-struct cookiedb_writeiterator_t_ {
-       cookiedb_t *origin;
-       ascstr_t host;
-       ascstr_t path;
-       Bool secure;
-       STIME time;
-       httpcookie_t *current;
-       enum {
-               COOKIEDB_WRITEITERATOR_STATE_VDB,
-               COOKIEDB_WRITEITERATOR_STATE_PDB,
-       } state;
-};
-typedef struct cookiedb_writeiterator_t_ cookiedb_writeiterator_t;
+       if (cookie->persistent == True) {
+               if (cookie->expires < iter->time) {
+                       return False;
+               }
+       }
+       ok = cookiedb_writeiterator_checksendcondition_domaincheck(iter, cookie);
+       if (ok == False) {
+               return False;
+       }
+       if (cookie->path.len != 0) {
+               ok = cookiedb_writeitereator_pathcheck(&iter->path, &cookie->path);
+       } else {
+               ok = cookiedb_writeitereator_pathcheck(&iter->path, &cookie->origin_path);
+       }
+       if (ok == False) {
+               return False;
+       }
 
-LOCAL Bool cookiedb_writeiterator_checksendcondition(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
-{
-       return False;
+       return True;
 }
 
 LOCAL Bool cookiedb_writeiterator_next(cookiedb_writeiterator_t *iter, httpcookie_t **cookie)
@@ -307,45 +452,21 @@ LOCAL Bool cookiedb_writeiterator_next(cookiedb_writeiterator_t *iter, httpcooki
        httpcookie_t *senti;
        Bool send;
 
-       if (iter->state == COOKIEDB_WRITEITERATOR_STATE_VDB) {
-               senti = cookie_volatiledb_sentinelnode(&iter->origin->vdb);
-               for (;;) {
-                       if (iter->current == senti) {
-                               break;
-                       }
-                       send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
-                       if (send == True) {
-                               break;
-                       }
-                       iter->current = httpcookie_nextnode(iter->current);
+       senti = cookiedb_sentinelnode(iter->origin);
+       for (;;) {
+               if (iter->current == senti) {
+                       break;
                }
-               if (iter->current != senti) {
-                       *cookie = iter->current;
-                       iter->current = httpcookie_nextnode(iter->current);
-                       return True;
-               } else {
-                       iter->state = COOKIEDB_WRITEITERATOR_STATE_PDB;
-                       senti = cookie_persistentdb_sentinelnode(&iter->origin->pdb);
-                       iter->current = httpcookie_nextnode(senti);
+               send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
+               if (send == True) {
+                       break;
                }
+               iter->current = httpcookie_nextnode(iter->current);
        }
-       if (iter->state == COOKIEDB_WRITEITERATOR_STATE_PDB) {
-               senti = cookie_persistentdb_sentinelnode(&iter->origin->pdb);
-               for (;;) {
-                       if (iter->current == senti) {
-                               break;
-                       }
-                       send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
-                       if (send == True) {
-                               break;
-                       }
-                       iter->current = httpcookie_nextnode(iter->current);
-               }
-               if (iter->current != senti) {
-                       *cookie = iter->current;
-                       iter->current = httpcookie_nextnode(iter->current);
-                       return True;
-               }
+       if (iter->current != senti) {
+               *cookie = iter->current;
+               iter->current = httpcookie_nextnode(iter->current);
+               return True;
        }
 
        return False;
@@ -380,8 +501,7 @@ LOCAL W cookiedb_writeiterator_initialize(cookiedb_writeiterator_t *iter, cookie
        iter->origin = db;
        iter->secure = secure;
        iter->time = time;
-       iter->state = COOKIEDB_WRITEITERATOR_STATE_VDB;
-       senti = cookie_volatiledb_sentinelnode(&db->vdb);
+       senti = cookiedb_sentinelnode(db);
        iter->current = httpcookie_nextnode(senti);
 
        return 0;
@@ -402,6 +522,7 @@ struct cookiedb_writeheadercontext_t_ {
                COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE,
                COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON,
                COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF,
+               COOKIEDB_WRITEITERATORCONTEXT_STATE_END,
        } state;
        httpcookie_t *current;
 };
@@ -423,6 +544,7 @@ EXPORT Bool cookiedb_writeheadercontext_makeheader(cookiedb_writeheadercontext_t
                }
                *str = cookiedb_writeheader_context_headername;
                *len = 8;
+               context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME;
                return True;
        case COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME:
                *str = context->current->attr.str;
@@ -441,17 +563,21 @@ EXPORT Bool cookiedb_writeheadercontext_makeheader(cookiedb_writeheadercontext_t
                return True;
        case COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON:
                *str = cookiedb_writeheader_context_colon;
-               *len = 2;
                cont = cookiedb_writeiterator_next(&context->iter, &context->current);
                if (cont == False) {
+                       *len = 1;
                        context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF;
                } else {
+                       *len = 2;
                        context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME;
                }
                return True;
        case COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF:
                *str = cookiedb_writeheader_context_crlf;
                *len = 2;
+               context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_END;
+               return True;
+       case COOKIEDB_WRITEITERATORCONTEXT_STATE_END:
                return False;
        }
 
@@ -502,6 +628,7 @@ EXPORT VOID cookiedb_endheaderwrite(cookiedb_t *db, cookiedb_writeheadercontext_
 
 struct cookiedb_readheadercontext_t_ {
        ascstr_t host;
+       ascstr_t path;
        STIME current;
        QUEUE sentinel;
        enum {
@@ -524,7 +651,7 @@ EXPORT W cookiedb_readheadercontext_appendchar_attr(cookiedb_readheadercontext_t
                if (context->reading != NULL) {
                        cookiedb_readheadercontext_insertcookie(context, context->reading);
                }
-               context->reading = httpcookie_new(context->host.str, context->host.len);
+               context->reading = httpcookie_new(context->host.str, context->host.len, context->path.str, context->path.len);
                if (context->reading == NULL) {
                        return -1; /* TODO: error value */
                }
@@ -611,7 +738,7 @@ LOCAL httpcookie_t* cookiedb_readheadercontext_prevcookie(cookiedb_readheadercon
        return (httpcookie_t*)context->sentinel.prev;
 }
 
-LOCAL cookiedb_readheadercontext_t* cookiedb_readheadercontext_new(UB *host, W host_len, STIME time)
+LOCAL cookiedb_readheadercontext_t* cookiedb_readheadercontext_new(UB *host, W host_len, UB *path, W path_len, STIME time)
 {
        cookiedb_readheadercontext_t *context;
        W err;
@@ -631,14 +758,21 @@ LOCAL cookiedb_readheadercontext_t* cookiedb_readheadercontext_new(UB *host, W h
                free(context);
                return NULL;
        }
-       context->reading = httpcookie_new(host, host_len);
-       if (context->reading == NULL) {
+       err = ascstr_initialize(&context->path);
+       if (err < 0) {
+               ascstr_finalize(&context->host);
+               free(context);
+               return NULL;
+       }
+       err = ascstr_appendstr(&context->path, path, path_len);
+       if (err < 0) {
+               ascstr_finalize(&context->path);
                ascstr_finalize(&context->host);
                free(context);
                return NULL;
        }
-       QueInit(&context->sentinel);
        context->reading = NULL;
+       QueInit(&context->sentinel);
        context->state = COOKIEDB_READHEADERCONTEXT_STATE_START;
 
        return context;
@@ -655,19 +789,20 @@ LOCAL VOID cookiedb_readheadercontext_delete(cookiedb_readheadercontext_t *conte
                }
                httpcookie_delete(cookie);
        }
-       if (context->reading == NULL) {
+       if (context->reading != NULL) {
                httpcookie_delete(context->reading);
        }
 
+       ascstr_finalize(&context->path);
        ascstr_finalize(&context->host);
        free(context);
 }
 
-EXPORT cookiedb_readheadercontext_t* cookiedb_startheaderread(cookiedb_t *db, UB *host, W host_len, STIME time)
+EXPORT cookiedb_readheadercontext_t* cookiedb_startheaderread(cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, STIME time)
 {
        cookiedb_readheadercontext_t *context;
 
-       context = cookiedb_readheadercontext_new(host, host_len, time);
+       context = cookiedb_readheadercontext_new(host, host_len, path, path_len, time);
        if (context == NULL) {
                return NULL;
        }
@@ -675,13 +810,95 @@ EXPORT cookiedb_readheadercontext_t* cookiedb_startheaderread(cookiedb_t *db, UB
        return context;
 }
 
-LOCAL VOID cookiedb_inserteachdb(cookiedb_t *db, httpcookie_t *cookie, STIME current)
+LOCAL VOID cookiedb_insertcookie(cookiedb_t *db, httpcookie_t *cookie)
 {
+       httpcookie_t *senti, *node;
+       ascstr_t *as_a, *as_n;
+       W cmp;
+       Bool samekey;
+
+       senti = cookiedb_sentinelnode(db);
+       node = httpcookie_nextnode(senti);
+       for (;;) {
+               if (node == senti) {
+                       httpcookie_QueInsert(cookie, senti);
+                       break;
+               }
+               as_n = httpcookie_getvalidpath(node);
+               as_a = httpcookie_getvalidpath(cookie);
+               cmp = ascstr_cmp(as_n, as_a);
+               if (cmp == 0) {
+                       samekey = httpcookie_issamekey(node, cookie);
+                       if (samekey == True) {
+                               httpcookie_QueInsert(cookie, node);
+                               httpcookie_delete(node);
+                               break;
+                       }
+               } else if (cmp < 0) {
+                       httpcookie_QueInsert(cookie, node);
+                       break;
+               }
+               node = httpcookie_nextnode(node);
+       }
+}
+
+LOCAL Bool cookiedb_checkinsertioncondition(cookiedb_t *db, httpcookie_t *cookie, STIME current)
+{
+       W count;
+       Bool ok;
+
+       /* domain check */
+       if (cookie->domain.len != 0) {
+               if (cookie->domain.str[0] != '.') { /* is not error and add period? */
+                       return False;
+               }
+               /* same as cookiedb_writeiterator_checksendcondition */
+               count = count_priod(&cookie->domain);
+               ok = check_specified_TLD(&cookie->domain);
+               if (ok == True) {
+                       if (count < 2) {
+                               return False;
+                       }
+               } else {
+                       if (count < 3) {
+                               return False;
+                       }
+               }
+               /* domain and request host check */
+               if (cookie->domain.len == (cookie->origin_host.len + 1)) {
+                       /* for
+                        *  domain = .xxx.yyy.zzz
+                        *  origin =  xxx.yyy.zzz
+                        */
+                       if (strncmp(cookie->domain.str + 1, cookie->origin_host.str, cookie->origin_host.len) != 0) {
+                               return False;
+                       }
+               } else {
+                       ok = ascstr_suffixcmp(&cookie->origin_host, &cookie->domain);
+                       if (ok == False) {
+                               return False;
+                       }
+               }
+       }
+
+       /* expire check */
        if (cookie->expires == 0) {
-               cookie_volatiledb_insertcookie(&db->vdb, cookie);
-       } if (cookie->expires < current) {
-               cookie_persistentdb_insertcookie(&db->pdb, cookie);
-       } else { /* cookie->expires >= current */
+               return True;
+       }
+       if (cookie->expires < current) {
+               return True;
+       }
+
+       return False;
+}
+
+LOCAL VOID cookiedb_insertwithcheck(cookiedb_t *db, httpcookie_t *cookie, STIME current)
+{
+       Bool save;
+       save = cookiedb_checkinsertioncondition(db, cookie, current);
+       if (save == True) {
+               cookiedb_insertcookie(db, cookie);
+       } else {
                httpcookie_delete(cookie);
        }
 }
@@ -689,6 +906,7 @@ LOCAL VOID cookiedb_inserteachdb(cookiedb_t *db, httpcookie_t *cookie, STIME cur
 EXPORT VOID cookiedb_endheaderread(cookiedb_t *db, cookiedb_readheadercontext_t *context)
 {
        httpcookie_t *cookie;
+       Bool ok;
 
        for (;;) {
                cookie = cookiedb_readheadercontext_prevcookie(context);
@@ -696,42 +914,71 @@ EXPORT VOID cookiedb_endheaderread(cookiedb_t *db, cookiedb_readheadercontext_t
                        break;
                }
                httpcookie_QueRemove(cookie);
-               cookiedb_inserteachdb(db, cookie, context->current);
+               cookiedb_insertwithcheck(db, cookie, context->current);
        }
        if (context->reading != NULL) {
-               httpcookie_QueRemove(context->reading);
-               cookiedb_inserteachdb(db, context->reading, context->current);
+               ok = httpcookie_isvalueset(context->reading);
+               if (ok == True) {
+                       httpcookie_QueRemove(context->reading);
+                       cookiedb_insertwithcheck(db, context->reading, context->current);
+               } else {
+                       httpcookie_delete(context->reading);
+               }
                context->reading = NULL;
        }
 
        cookiedb_readheadercontext_delete(context);
 }
 
-EXPORT W cookiedb_clearallcookie(cookiedb_t *db)
+EXPORT VOID cookiedb_clearallcookie(cookiedb_t *db)
 {
+       httpcookie_t *cookie;
+       Bool empty;
+
+       for (;;) {
+               empty = isQueEmpty(&db->sentinel);
+               if (empty == True) {
+                       break;
+               }
+               cookie = (httpcookie_t*)db->sentinel.prev;
+               httpcookie_delete(cookie);
+       }
+
+       /* TODO clear file */
 }
 
-LOCAL W cookiedb_initialize(cookiedb_t *db, LINK *db_lnk)
+EXPORT W cookiedb_writefile(cookiedb_t *db)
 {
-       W err;
+       /* TODO */
+       return -1;
+}
 
-       err = cookie_volatiledb_initialize(&db->vdb);
-       if (err < 0) {
-               return err;
-       }
-       err = cookie_persistentdb_initialize(&db->pdb, db_lnk);
-       if (err < 0) {
-               cookie_volatiledb_finalize(&db->vdb);
-               return err;
-       }
+EXPORT W cookiedb_readfile(cookiedb_t *db)
+{
+       /* TODO */
+       return -1;
+}
 
+LOCAL W cookiedb_initialize(cookiedb_t *db, LINK *db_lnk)
+{
+       QueInit(&db->sentinel);
+       db->lnk = db_lnk;
        return 0;
 }
 
 LOCAL VOID cookiedb_finalize(cookiedb_t *db)
 {
-       cookie_persistentdb_finalize(&db->pdb);
-       cookie_volatiledb_finalize(&db->vdb);
+       httpcookie_t *cookie;
+       Bool empty;
+
+       for (;;) {
+               empty = isQueEmpty(&db->sentinel);
+               if (empty == True) {
+                       break;
+               }
+               cookie = (httpcookie_t*)db->sentinel.prev;
+               httpcookie_delete(cookie);
+       }
 }
 
 EXPORT cookiedb_t* cookiedb_new(LINK *db_lnk)