*/
#include "cookiedb.h"
+#include "httpdateparser.h"
#include <basic.h>
#include <bstdio.h>
+#include <bstdlib.h>
+#include <bstring.h>
+#include <bsys/queue.h>
#ifdef BCHAN_CONFIG_DEBUG
# define DP(arg) printf arg
# define DP(arg) /**/
# define DP_ER(msg, err) /**/
#endif
+
+struct ascstr_t_ {
+ UB *str;
+ W len;
+};
+typedef struct ascstr_t_ ascstr_t;
+
+EXPORT W ascstr_appendstr(ascstr_t *astr, UB *apd, W apd_len)
+{
+ UB *str;
+
+ str = realloc(astr->str, astr->len + apd_len + 1);
+ if (str == NULL) {
+ return -1;
+ }
+ astr->str = str;
+
+ memcpy(astr->str + astr->len, apd, apd_len);
+ astr->len += apd_len;
+ astr->str[astr->len] = '\0';
+
+ 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));
+ if (astr->str == NULL) {
+ return -1;
+ }
+ astr->str[0] = '\0';
+ astr->len = 0;
+ return 0;
+}
+
+EXPORT VOID ascstr_finalize(ascstr_t *astr)
+{
+ free(astr->str);
+}
+
+struct httpcookie_t_ {
+ QUEUE que;
+ /* 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 */
+ ascstr_t comment;
+ ascstr_t domain;
+ Bool persistent;
+ STIME expires;
+ ascstr_t path;
+ ascstr_t version;
+ Bool secure;
+};
+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;
+}
+
+LOCAL VOID httpcookie_setexpires(httpcookie_t *cookie, STIME expires)
+{
+ cookie->expires = 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, UB *path, W path_len)
+{
+ W err;
+
+ QueInit(&cookie->que);
+ err = ascstr_initialize(&cookie->origin_host);
+ if (err < 0) {
+ 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;
+ }
+ err = ascstr_initialize(&cookie->attr);
+ if (err < 0) {
+ goto error_attr;
+ }
+ err = ascstr_initialize(&cookie->name);
+ if (err < 0) {
+ goto error_name;
+ }
+ err = ascstr_initialize(&cookie->comment);
+ if (err < 0) {
+ goto error_comment;
+ }
+ err = ascstr_initialize(&cookie->domain);
+ if (err < 0) {
+ goto error_domain;
+ }
+ err = ascstr_initialize(&cookie->path);
+ if (err < 0) {
+ goto error_path;
+ }
+ err = ascstr_initialize(&cookie->version);
+ if (err < 0) {
+ goto error_version;
+ }
+
+ cookie->persistent = False;
+ cookie->secure = False;
+ cookie->expires = 0;
+
+ return 0;
+
+ ascstr_finalize(&cookie->version);
+error_version:
+ ascstr_finalize(&cookie->path);
+error_path:
+ ascstr_finalize(&cookie->domain);
+error_domain:
+ ascstr_finalize(&cookie->comment);
+error_comment:
+ ascstr_finalize(&cookie->name);
+error_name:
+ ascstr_finalize(&cookie->attr);
+error_attr:
+error_originpath_append:
+ ascstr_finalize(&cookie->origin_path);
+error_originpath:
+error_originhost_append:
+ ascstr_finalize(&cookie->origin_host);
+error_originhost:
+ return err;
+}
+
+LOCAL VOID httpcookie_finalize(httpcookie_t *cookie)
+{
+ ascstr_finalize(&cookie->version);
+ ascstr_finalize(&cookie->path);
+ ascstr_finalize(&cookie->domain);
+ ascstr_finalize(&cookie->comment);
+ ascstr_finalize(&cookie->name);
+ ascstr_finalize(&cookie->attr);
+ ascstr_finalize(&cookie->origin_path);
+ ascstr_finalize(&cookie->origin_host);
+ QueRemove(&cookie->que);
+}
+
+LOCAL httpcookie_t* httpcookie_new(UB *host, W host_len, UB *path, W path_len)
+{
+ httpcookie_t *cookie;
+ W err;
+
+ cookie = malloc(sizeof(httpcookie_t));
+ if (cookie == NULL) {
+ return NULL;
+ }
+ err = httpcookie_initialize(cookie, host, host_len, path, path_len);
+ if (err < 0) {
+ free(cookie);
+ return NULL;
+ }
+ return cookie;
+}
+
+LOCAL VOID httpcookie_delete(httpcookie_t *cookie)
+{
+ httpcookie_finalize(cookie);
+ free(cookie);
+}
+
+struct cookiedb_t_ {
+ QUEUE sentinel;
+ LINK *lnk;
+};
+
+LOCAL httpcookie_t* cookiedb_sentinelnode(cookiedb_t *db)
+{
+ return (httpcookie_t*)&db->sentinel;
+}
+
+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 W count_priod(ascstr_t *str)
+{
+ W i,count;
+
+ count = 0;
+ for (i = 0; i < str->len; i++) {
+ if (str->str[i] == '.') {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+LOCAL Bool check_specified_TLD(ascstr_t *domain)
+{
+ 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 Bool cookiedb_writeiterator_checksendcondition_domaincheck(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
+{
+ 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 Bool cookiedb_writeitereator_pathcheck(ascstr_t *send_path, ascstr_t *origin_path)
+{
+ 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 Bool cookiedb_writeiterator_checksendcondition(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
+{
+ Bool ok;
+
+ if (cookie->secure == True) {
+ if (iter->secure != True) {
+ return False;
+ }
+ }
+ 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;
+ }
+
+ return True;
+}
+
+LOCAL Bool cookiedb_writeiterator_next(cookiedb_writeiterator_t *iter, httpcookie_t **cookie)
+{
+ httpcookie_t *senti;
+ Bool send;
+
+ senti = cookiedb_sentinelnode(iter->origin);
+ 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;
+ }
+
+ return False;
+}
+
+LOCAL W cookiedb_writeiterator_initialize(cookiedb_writeiterator_t *iter, cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, Bool secure, STIME time)
+{
+ W err;
+ httpcookie_t *senti;
+
+ err = ascstr_initialize(&iter->host);
+ if (err < 0) {
+ return err;
+ }
+ err = ascstr_appendstr(&iter->host, host, host_len);
+ if (err < 0) {
+ ascstr_finalize(&iter->host);
+ return err;
+ }
+ err = ascstr_initialize(&iter->path);
+ if (err < 0) {
+ ascstr_finalize(&iter->host);
+ return err;
+ }
+ err = ascstr_appendstr(&iter->path, path, path_len);
+ if (err < 0) {
+ ascstr_finalize(&iter->path);
+ ascstr_finalize(&iter->host);
+ return err;
+ }
+
+ iter->origin = db;
+ iter->secure = secure;
+ iter->time = time;
+ senti = cookiedb_sentinelnode(db);
+ iter->current = httpcookie_nextnode(senti);
+
+ return 0;
+}
+
+LOCAL VOID cookiedb_writeiterator_finalize(cookiedb_writeiterator_t *iter)
+{
+ ascstr_finalize(&iter->path);
+ ascstr_finalize(&iter->host);
+}
+
+struct cookiedb_writeheadercontext_t_ {
+ cookiedb_writeiterator_t iter;
+ enum {
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_START,
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME,
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL,
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE,
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON,
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF,
+ COOKIEDB_WRITEITERATORCONTEXT_STATE_END,
+ } state;
+ httpcookie_t *current;
+};
+
+LOCAL UB cookiedb_writeheader_context_headername[] = "Cookie: ";
+LOCAL UB cookiedb_writeheader_context_equal[] = "=";
+LOCAL UB cookiedb_writeheader_context_colon[] = "; ";
+LOCAL UB cookiedb_writeheader_context_crlf[] = "\r\n";
+
+EXPORT Bool cookiedb_writeheadercontext_makeheader(cookiedb_writeheadercontext_t *context, UB **str, W *len)
+{
+ Bool cont;
+
+ switch (context->state) {
+ case COOKIEDB_WRITEITERATORCONTEXT_STATE_START:
+ cont = cookiedb_writeiterator_next(&context->iter, &context->current);
+ if (cont == False) {
+ return False;
+ }
+ *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;
+ *len = context->current->attr.len;
+ context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL;
+ return True;
+ case COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL:
+ *str = cookiedb_writeheader_context_equal;
+ *len = 1;
+ context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE;
+ return True;
+ case COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE:
+ *str = context->current->name.str;
+ *len = context->current->name.len;
+ context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON;
+ return True;
+ case COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON:
+ *str = cookiedb_writeheader_context_colon;
+ 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;
+ }
+
+ return False;
+}
+
+LOCAL cookiedb_writeheadercontext_t* cookiedb_writeheadercontext_new(cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, Bool secure, STIME time)
+{
+ cookiedb_writeheadercontext_t *context;
+ W err;
+
+ context = (cookiedb_writeheadercontext_t*)malloc(sizeof(cookiedb_writeheadercontext_t));
+ if (context == NULL) {
+ return NULL;
+ }
+ err = cookiedb_writeiterator_initialize(&context->iter, db, host, host_len, path, path_len, secure, time);
+ if (err < 0) {
+ free(context);
+ return NULL;
+ }
+ context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_START;
+
+ return context;
+}
+
+LOCAL VOID cookiedb_writeheadercontext_delete(cookiedb_writeheadercontext_t *context)
+{
+ cookiedb_writeiterator_finalize(&context->iter);
+ free(context);
+}
+
+EXPORT cookiedb_writeheadercontext_t* cookiedb_startheaderwrite(cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, Bool secure, STIME time)
+{
+ cookiedb_writeheadercontext_t *context;
+
+ context = cookiedb_writeheadercontext_new(db, host, host_len, path, path_len, secure, time);
+ if (context == NULL) {
+ return NULL;
+ }
+
+ return context;
+}
+
+EXPORT VOID cookiedb_endheaderwrite(cookiedb_t *db, cookiedb_writeheadercontext_t *context)
+{
+ cookiedb_writeheadercontext_delete(context);
+}
+
+struct cookiedb_readheadercontext_t_ {
+ ascstr_t host;
+ ascstr_t path;
+ STIME current;
+ QUEUE sentinel;
+ enum {
+ COOKIEDB_READHEADERCONTEXT_STATE_START,
+ COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR,
+ COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL,
+ } state;
+ httpcookie_t *reading;
+};
+
+LOCAL VOID cookiedb_readheadercontext_insertcookie(cookiedb_readheadercontext_t *context, httpcookie_t *cookie)
+{
+ QueInsert(&cookie->que, &context->sentinel);
+}
+
+EXPORT W cookiedb_readheadercontext_appendchar_attr(cookiedb_readheadercontext_t *context, UB ch)
+{
+ if (context->state != COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR) {
+ context->state = COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR;
+ if (context->reading != NULL) {
+ cookiedb_readheadercontext_insertcookie(context, context->reading);
+ }
+ 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 */
+ }
+ }
+ return ascstr_appendstr(&context->reading->attr, &ch, 1);
+}
+
+LOCAL W cookiedb_readheadercontext_appdatechar_common(cookiedb_readheadercontext_t *context, ascstr_t *target, UB ch)
+{
+ if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
+ return 0;
+ }
+ context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
+ return ascstr_appendstr(target, &ch, 1);
+}
+
+EXPORT W cookiedb_readheadercontext_appendchar_name(cookiedb_readheadercontext_t *context, UB ch)
+{
+ return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->name, ch);
+}
+
+EXPORT W cookiedb_readheadercontext_appendchar_comment(cookiedb_readheadercontext_t *context, UB ch)
+{
+ return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->comment, ch);
+}
+
+EXPORT W cookiedb_readheadercontext_appendchar_domain(cookiedb_readheadercontext_t *context, UB ch)
+{
+ return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->domain, ch);
+}
+
+EXPORT W cookiedb_readheadercontext_appendchar_path(cookiedb_readheadercontext_t *context, UB ch)
+{
+ return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->path, ch);
+}
+
+EXPORT W cookiedb_readheadercontext_appendchar_version(cookiedb_readheadercontext_t *context, UB ch)
+{
+ return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->version, ch);
+}
+
+EXPORT W cookiedb_readheadercontext_setsecure(cookiedb_readheadercontext_t *context)
+{
+ if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
+ return 0;
+ }
+ context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
+
+ context->reading->secure = True;
+
+ return 0;
+}
+
+EXPORT W cookiedb_readheadercontext_setexpires(cookiedb_readheadercontext_t *context, STIME expires)
+{
+ if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
+ return 0;
+ }
+ context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
+
+ httpcookie_setexpires(context->reading, expires);
+
+ return 0;
+}
+
+EXPORT W cookiedb_readheadercontext_setmaxage(cookiedb_readheadercontext_t *context, W seconds)
+{
+ if (seconds <= 0) {
+ return 0;
+ }
+
+ httpcookie_setexpires(context->reading, context->current + seconds);
+
+ return 0;
+}
+
+LOCAL httpcookie_t* cookiedb_readheadercontext_prevcookie(cookiedb_readheadercontext_t *context)
+{
+ Bool empty;
+ empty = isQueEmpty(&context->sentinel);
+ if (empty == True) {
+ return NULL;
+ }
+ return (httpcookie_t*)context->sentinel.prev;
+}
+
+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;
+
+ context = (cookiedb_readheadercontext_t*)malloc(sizeof(cookiedb_readheadercontext_t));
+ if (context == NULL) {
+ return NULL;
+ }
+ err = ascstr_initialize(&context->host);
+ if (err < 0) {
+ free(context);
+ return NULL;
+ }
+ err = ascstr_appendstr(&context->host, host, host_len);
+ if (err < 0) {
+ ascstr_finalize(&context->host);
+ free(context);
+ return 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;
+ }
+ context->reading = NULL;
+ QueInit(&context->sentinel);
+ context->state = COOKIEDB_READHEADERCONTEXT_STATE_START;
+
+ return context;
+}
+
+LOCAL VOID cookiedb_readheadercontext_delete(cookiedb_readheadercontext_t *context)
+{
+ httpcookie_t *cookie;
+
+ for (;;) {
+ cookie = cookiedb_readheadercontext_prevcookie(context);
+ if (cookie == NULL) {
+ break;
+ }
+ httpcookie_delete(cookie);
+ }
+ 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, UB *path, W path_len, STIME time)
+{
+ cookiedb_readheadercontext_t *context;
+
+ context = cookiedb_readheadercontext_new(host, host_len, path, path_len, time);
+ if (context == NULL) {
+ return NULL;
+ }
+
+ return context;
+}
+
+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) {
+ 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);
+ }
+}
+
+EXPORT VOID cookiedb_endheaderread(cookiedb_t *db, cookiedb_readheadercontext_t *context)
+{
+ httpcookie_t *cookie;
+ Bool ok;
+
+ for (;;) {
+ cookie = cookiedb_readheadercontext_prevcookie(context);
+ if (cookie == NULL) {
+ break;
+ }
+ httpcookie_QueRemove(cookie);
+ cookiedb_insertwithcheck(db, cookie, context->current);
+ }
+ if (context->reading != NULL) {
+ 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 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 */
+}
+
+EXPORT W cookiedb_writefile(cookiedb_t *db)
+{
+ /* TODO */
+ return -1;
+}
+
+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)
+{
+ 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)
+{
+ cookiedb_t *db;
+ W err;
+
+ db = malloc(sizeof(cookiedb_t));
+ if (db == NULL) {
+ return NULL;
+ }
+ err = cookiedb_initialize(db, db_lnk);
+ if (err < 0) {
+ free(db);
+ return NULL;
+ }
+
+ return db;
+}
+
+EXPORT VOID cookiedb_delete(cookiedb_t *db)
+{
+ cookiedb_finalize(db);
+ free(db);
+}