4 * Copyright (c) 2011 project bchan
6 * This software is provided 'as-is', without any express or implied
7 * warranty. In no event will the authors be held liable for any damages
8 * arising from the use of this software.
10 * Permission is granted to anyone to use this software for any purpose,
11 * including commercial applications, and to alter it and redistribute it
12 * freely, subject to the following restrictions:
14 * 1. The origin of this software must not be misrepresented; you must not
15 * claim that you wrote the original software. If you use this software
16 * in a product, an acknowledgment in the product documentation would be
17 * appreciated but is not required.
19 * 2. Altered source versions must be plainly marked as such, and must not be
20 * misrepresented as being the original software.
22 * 3. This notice may not be removed or altered from any source
28 #include "httpdateparser.h"
34 #include <bsys/queue.h>
36 #ifdef BCHAN_CONFIG_DEBUG
37 # define DP(arg) printf arg
38 # define DP_ER(msg, err) printf("%s (%d/%x)\n", msg, err>>16, err)
41 # define DP_ER(msg, err) /**/
48 typedef struct ascstr_t_ ascstr_t;
50 EXPORT W ascstr_appendstr(ascstr_t *astr, UB *apd, W apd_len)
54 str = realloc(astr->str, astr->len + apd_len + 1);
60 memcpy(astr->str + astr->len, apd, apd_len);
62 astr->str[astr->len] = '\0';
67 EXPORT W ascstr_initialize(ascstr_t *astr)
69 astr->str = malloc(sizeof(UB));
70 if (astr->str == NULL) {
78 EXPORT VOID ascstr_finalize(ascstr_t *astr)
83 struct httpcookie_t_ {
87 /* Set-Cookie value */
88 ascstr_t attr; /* NAME string */
89 ascstr_t name; /* VALUE string */
98 typedef struct httpcookie_t_ httpcookie_t;
100 LOCAL Bool httpcookie_isvalueset(httpcookie_t *cookie)
102 if (cookie->attr.len == 0) {
105 if (cookie->name.len == 0) {
111 LOCAL httpcookie_t* httpcookie_nextnode(httpcookie_t *cookie)
113 return (httpcookie_t*)cookie->que.next;
116 LOCAL VOID httpcookie_setexpires(httpcookie_t *cookie, STIME expires)
118 cookie->expires = expires;
121 LOCAL VOID httpcookie_QueRemove(httpcookie_t *cookie)
123 QueRemove(&cookie->que);
124 QueInit(&cookie->que);
127 LOCAL W httpcookie_initialize(httpcookie_t *cookie, UB *host, W host_len)
131 QueInit(&cookie->que);
132 err = ascstr_initialize(&cookie->host);
136 ascstr_appendstr(&cookie->host, host, host_len);
137 err = ascstr_initialize(&cookie->attr);
141 err = ascstr_initialize(&cookie->name);
145 err = ascstr_initialize(&cookie->comment);
149 err = ascstr_initialize(&cookie->domain);
153 err = ascstr_initialize(&cookie->path);
157 err = ascstr_initialize(&cookie->version);
162 cookie->persistent = False;
163 cookie->secure = False;
168 ascstr_finalize(&cookie->version);
170 ascstr_finalize(&cookie->path);
172 ascstr_finalize(&cookie->domain);
174 ascstr_finalize(&cookie->comment);
176 ascstr_finalize(&cookie->name);
178 ascstr_finalize(&cookie->attr);
180 ascstr_finalize(&cookie->host);
185 LOCAL VOID httpcookie_finalize(httpcookie_t *cookie)
187 ascstr_finalize(&cookie->version);
188 ascstr_finalize(&cookie->path);
189 ascstr_finalize(&cookie->domain);
190 ascstr_finalize(&cookie->comment);
191 ascstr_finalize(&cookie->name);
192 ascstr_finalize(&cookie->attr);
193 ascstr_finalize(&cookie->host);
194 QueRemove(&cookie->que);
197 LOCAL httpcookie_t* httpcookie_new(UB *host, W host_len)
199 httpcookie_t *cookie;
202 cookie = malloc(sizeof(httpcookie_t));
203 if (cookie == NULL) {
206 err = httpcookie_initialize(cookie, host, host_len);
214 LOCAL VOID httpcookie_delete(httpcookie_t *cookie)
216 httpcookie_finalize(cookie);
220 struct cookie_persistentdb_t_ {
224 typedef struct cookie_persistentdb_t_ cookie_persistentdb_t;
226 struct cookie_volatiledb_t_ {
229 typedef struct cookie_volatiledb_t_ cookie_volatiledb_t;
232 cookie_volatiledb_t vdb;
233 cookie_persistentdb_t pdb;
236 LOCAL W cookie_persistentdb_writefile(cookie_persistentdb_t *db)
242 LOCAL W cookie_persistentdb_readfile(cookie_persistentdb_t *db)
248 LOCAL VOID cookie_persistentdb_clear(cookie_persistentdb_t *db)
250 httpcookie_t *cookie;
254 empty = isQueEmpty(&db->sentinel);
258 cookie = (httpcookie_t*)db->sentinel.prev;
259 httpcookie_delete(cookie);
262 /* TODO clear file */
265 LOCAL httpcookie_t* cookie_persistentdb_sentinelnode(cookie_persistentdb_t *db)
267 return (httpcookie_t*)&db->sentinel;
270 LOCAL VOID cookie_persistentdb_insertcookie(cookie_persistentdb_t *db, httpcookie_t *cookie)
272 QueInsert(&cookie->que, &db->sentinel);
275 LOCAL W cookie_persistentdb_initialize(cookie_persistentdb_t *db, LINK *db_lnk)
277 QueInit(&db->sentinel);
282 LOCAL VOID cookie_persistentdb_finalize(cookie_persistentdb_t *db)
284 httpcookie_t *cookie;
288 empty = isQueEmpty(&db->sentinel);
292 cookie = (httpcookie_t*)db->sentinel.prev;
293 httpcookie_delete(cookie);
297 LOCAL VOID cookie_volatiledb_clear(cookie_volatiledb_t *db)
299 httpcookie_t *cookie;
303 empty = isQueEmpty(&db->sentinel);
307 cookie = (httpcookie_t*)db->sentinel.prev;
308 httpcookie_delete(cookie);
312 LOCAL httpcookie_t* cookie_volatiledb_sentinelnode(cookie_volatiledb_t *db)
314 return (httpcookie_t*)&db->sentinel;
317 LOCAL VOID cookie_volatiledb_insertcookie(cookie_volatiledb_t *db, httpcookie_t *cookie)
319 QueInsert(&cookie->que, &db->sentinel);
322 LOCAL W cookie_volatiledb_initialize(cookie_volatiledb_t *db)
324 QueInit(&db->sentinel);
328 LOCAL VOID cookie_volatiledb_finalize(cookie_volatiledb_t *db)
330 httpcookie_t *cookie;
334 empty = isQueEmpty(&db->sentinel);
338 cookie = (httpcookie_t*)db->sentinel.prev;
339 httpcookie_delete(cookie);
343 struct cookiedb_writeiterator_t_ {
349 httpcookie_t *current;
351 COOKIEDB_WRITEITERATOR_STATE_VDB,
352 COOKIEDB_WRITEITERATOR_STATE_PDB,
355 typedef struct cookiedb_writeiterator_t_ cookiedb_writeiterator_t;
357 LOCAL W count_priod(ascstr_t *str)
362 for (i = 0; i < str->len; i++) {
363 if (str->str[i] == '.') {
371 LOCAL Bool check_specified_TLD(ascstr_t *domain)
373 if (strncmp(domain->str + domain->len - 4, ".net", 4) == 0) {
376 if (strncmp(domain->str + domain->len - 4, ".com", 4) == 0) {
379 if (strncmp(domain->str + domain->len - 4, ".edu", 4) == 0) {
382 if (strncmp(domain->str + domain->len - 4, ".org", 4) == 0) {
385 if (strncmp(domain->str + domain->len - 4, ".gov", 4) == 0) {
388 if (strncmp(domain->str + domain->len - 4, ".mil", 4) == 0) {
391 if (strncmp(domain->str + domain->len - 4, ".int", 4) == 0) {
394 /* TODO: other TLDs. */
398 LOCAL Bool cookiedb_writeiterator_domaincheck(ascstr_t *send_host, ascstr_t *origin_host)
403 if (origin_host->len < send_host->len) {
406 if (strncmp(origin_host->str + origin_host->len - send_host->len, send_host->str, send_host->len) != 0) {
409 count = count_priod(send_host);
410 ok = check_specified_TLD(send_host);
424 LOCAL Bool cookiedb_writeitereator_pathcheck(ascstr_t *send_path, ascstr_t *origin_path)
426 if (origin_path->len < send_path->len) {
429 if (strncmp(origin_path->str, send_path->str, send_path->len) != 0) {
435 LOCAL Bool cookiedb_writeiterator_checksendcondition(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
439 if (cookie->secure == True) {
440 if (iter->secure != True) {
444 if (cookie->persistent == True) {
445 if (cookie->expires < iter->time) {
449 if (cookie->domain.len != 0) {
450 ok = cookiedb_writeiterator_domaincheck(&iter->host, &cookie->domain);
452 ok = cookiedb_writeiterator_domaincheck(&iter->host, &cookie->host);
457 if (cookie->path.len != 0) {
458 ok = cookiedb_writeitereator_pathcheck(&iter->path, &cookie->path);
469 LOCAL Bool cookiedb_writeiterator_next(cookiedb_writeiterator_t *iter, httpcookie_t **cookie)
474 if (iter->state == COOKIEDB_WRITEITERATOR_STATE_VDB) {
475 senti = cookie_volatiledb_sentinelnode(&iter->origin->vdb);
477 if (iter->current == senti) {
480 send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
484 iter->current = httpcookie_nextnode(iter->current);
486 if (iter->current != senti) {
487 *cookie = iter->current;
488 iter->current = httpcookie_nextnode(iter->current);
491 iter->state = COOKIEDB_WRITEITERATOR_STATE_PDB;
492 senti = cookie_persistentdb_sentinelnode(&iter->origin->pdb);
493 iter->current = httpcookie_nextnode(senti);
496 if (iter->state == COOKIEDB_WRITEITERATOR_STATE_PDB) {
497 senti = cookie_persistentdb_sentinelnode(&iter->origin->pdb);
499 if (iter->current == senti) {
502 send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
506 iter->current = httpcookie_nextnode(iter->current);
508 if (iter->current != senti) {
509 *cookie = iter->current;
510 iter->current = httpcookie_nextnode(iter->current);
518 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)
523 err = ascstr_initialize(&iter->host);
527 err = ascstr_appendstr(&iter->host, host, host_len);
529 ascstr_finalize(&iter->host);
532 err = ascstr_initialize(&iter->path);
534 ascstr_finalize(&iter->host);
537 err = ascstr_appendstr(&iter->path, path, path_len);
539 ascstr_finalize(&iter->path);
540 ascstr_finalize(&iter->host);
545 iter->secure = secure;
547 iter->state = COOKIEDB_WRITEITERATOR_STATE_VDB;
548 senti = cookie_volatiledb_sentinelnode(&db->vdb);
549 iter->current = httpcookie_nextnode(senti);
554 LOCAL VOID cookiedb_writeiterator_finalize(cookiedb_writeiterator_t *iter)
556 ascstr_finalize(&iter->path);
557 ascstr_finalize(&iter->host);
560 struct cookiedb_writeheadercontext_t_ {
561 cookiedb_writeiterator_t iter;
563 COOKIEDB_WRITEITERATORCONTEXT_STATE_START,
564 COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME,
565 COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL,
566 COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE,
567 COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON,
568 COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF,
569 COOKIEDB_WRITEITERATORCONTEXT_STATE_END,
571 httpcookie_t *current;
574 LOCAL UB cookiedb_writeheader_context_headername[] = "Cookie: ";
575 LOCAL UB cookiedb_writeheader_context_equal[] = "=";
576 LOCAL UB cookiedb_writeheader_context_colon[] = "; ";
577 LOCAL UB cookiedb_writeheader_context_crlf[] = "\r\n";
579 EXPORT Bool cookiedb_writeheadercontext_makeheader(cookiedb_writeheadercontext_t *context, UB **str, W *len)
583 switch (context->state) {
584 case COOKIEDB_WRITEITERATORCONTEXT_STATE_START:
585 cont = cookiedb_writeiterator_next(&context->iter, &context->current);
589 *str = cookiedb_writeheader_context_headername;
591 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME;
593 case COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME:
594 *str = context->current->attr.str;
595 *len = context->current->attr.len;
596 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL;
598 case COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL:
599 *str = cookiedb_writeheader_context_equal;
601 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE;
603 case COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE:
604 *str = context->current->name.str;
605 *len = context->current->name.len;
606 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON;
608 case COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON:
609 *str = cookiedb_writeheader_context_colon;
611 cont = cookiedb_writeiterator_next(&context->iter, &context->current);
613 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF;
615 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME;
618 case COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF:
619 *str = cookiedb_writeheader_context_crlf;
621 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_END;
623 case COOKIEDB_WRITEITERATORCONTEXT_STATE_END:
630 LOCAL cookiedb_writeheadercontext_t* cookiedb_writeheadercontext_new(cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, Bool secure, STIME time)
632 cookiedb_writeheadercontext_t *context;
635 context = (cookiedb_writeheadercontext_t*)malloc(sizeof(cookiedb_writeheadercontext_t));
636 if (context == NULL) {
639 err = cookiedb_writeiterator_initialize(&context->iter, db, host, host_len, path, path_len, secure, time);
644 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_START;
649 LOCAL VOID cookiedb_writeheadercontext_delete(cookiedb_writeheadercontext_t *context)
651 cookiedb_writeiterator_finalize(&context->iter);
655 EXPORT cookiedb_writeheadercontext_t* cookiedb_startheaderwrite(cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, Bool secure, STIME time)
657 cookiedb_writeheadercontext_t *context;
659 context = cookiedb_writeheadercontext_new(db, host, host_len, path, path_len, secure, time);
660 if (context == NULL) {
667 EXPORT VOID cookiedb_endheaderwrite(cookiedb_t *db, cookiedb_writeheadercontext_t *context)
669 cookiedb_writeheadercontext_delete(context);
672 struct cookiedb_readheadercontext_t_ {
677 COOKIEDB_READHEADERCONTEXT_STATE_START,
678 COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR,
679 COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL,
681 httpcookie_t *reading;
684 LOCAL VOID cookiedb_readheadercontext_insertcookie(cookiedb_readheadercontext_t *context, httpcookie_t *cookie)
686 QueInsert(&cookie->que, &context->sentinel);
689 EXPORT W cookiedb_readheadercontext_appendchar_attr(cookiedb_readheadercontext_t *context, UB ch)
691 if (context->state != COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR) {
692 context->state = COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR;
693 if (context->reading != NULL) {
694 cookiedb_readheadercontext_insertcookie(context, context->reading);
696 context->reading = httpcookie_new(context->host.str, context->host.len);
697 if (context->reading == NULL) {
698 return -1; /* TODO: error value */
701 return ascstr_appendstr(&context->reading->attr, &ch, 1);
704 LOCAL W cookiedb_readheadercontext_appdatechar_common(cookiedb_readheadercontext_t *context, ascstr_t *target, UB ch)
706 if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
709 context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
710 return ascstr_appendstr(target, &ch, 1);
713 EXPORT W cookiedb_readheadercontext_appendchar_name(cookiedb_readheadercontext_t *context, UB ch)
715 return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->name, ch);
718 EXPORT W cookiedb_readheadercontext_appendchar_comment(cookiedb_readheadercontext_t *context, UB ch)
720 return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->comment, ch);
723 EXPORT W cookiedb_readheadercontext_appendchar_domain(cookiedb_readheadercontext_t *context, UB ch)
725 return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->domain, ch);
728 EXPORT W cookiedb_readheadercontext_appendchar_path(cookiedb_readheadercontext_t *context, UB ch)
730 return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->path, ch);
733 EXPORT W cookiedb_readheadercontext_appendchar_version(cookiedb_readheadercontext_t *context, UB ch)
735 return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->version, ch);
738 EXPORT W cookiedb_readheadercontext_setsecure(cookiedb_readheadercontext_t *context)
740 if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
743 context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
745 context->reading->secure = True;
750 EXPORT W cookiedb_readheadercontext_setexpires(cookiedb_readheadercontext_t *context, STIME expires)
752 if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
755 context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
757 httpcookie_setexpires(context->reading, expires);
762 EXPORT W cookiedb_readheadercontext_setmaxage(cookiedb_readheadercontext_t *context, W seconds)
768 httpcookie_setexpires(context->reading, context->current + seconds);
773 LOCAL httpcookie_t* cookiedb_readheadercontext_prevcookie(cookiedb_readheadercontext_t *context)
776 empty = isQueEmpty(&context->sentinel);
780 return (httpcookie_t*)context->sentinel.prev;
783 LOCAL cookiedb_readheadercontext_t* cookiedb_readheadercontext_new(UB *host, W host_len, STIME time)
785 cookiedb_readheadercontext_t *context;
788 context = (cookiedb_readheadercontext_t*)malloc(sizeof(cookiedb_readheadercontext_t));
789 if (context == NULL) {
792 err = ascstr_initialize(&context->host);
797 err = ascstr_appendstr(&context->host, host, host_len);
799 ascstr_finalize(&context->host);
803 context->reading = NULL;
804 QueInit(&context->sentinel);
805 context->state = COOKIEDB_READHEADERCONTEXT_STATE_START;
810 LOCAL VOID cookiedb_readheadercontext_delete(cookiedb_readheadercontext_t *context)
812 httpcookie_t *cookie;
815 cookie = cookiedb_readheadercontext_prevcookie(context);
816 if (cookie == NULL) {
819 httpcookie_delete(cookie);
821 if (context->reading != NULL) {
822 httpcookie_delete(context->reading);
825 ascstr_finalize(&context->host);
829 EXPORT cookiedb_readheadercontext_t* cookiedb_startheaderread(cookiedb_t *db, UB *host, W host_len, STIME time)
831 cookiedb_readheadercontext_t *context;
833 context = cookiedb_readheadercontext_new(host, host_len, time);
834 if (context == NULL) {
841 LOCAL VOID cookiedb_inserteachdb(cookiedb_t *db, httpcookie_t *cookie, STIME current)
843 /* TODO: domain chack */
844 if (cookie->expires == 0) {
845 cookie_volatiledb_insertcookie(&db->vdb, cookie);
846 } else if (cookie->expires < current) {
847 cookie_persistentdb_insertcookie(&db->pdb, cookie);
848 } else { /* cookie->expires >= current */
849 httpcookie_delete(cookie);
853 EXPORT VOID cookiedb_endheaderread(cookiedb_t *db, cookiedb_readheadercontext_t *context)
855 httpcookie_t *cookie;
859 cookie = cookiedb_readheadercontext_prevcookie(context);
860 if (cookie == NULL) {
863 httpcookie_QueRemove(cookie);
864 cookiedb_inserteachdb(db, cookie, context->current);
866 if (context->reading != NULL) {
867 ok = httpcookie_isvalueset(context->reading);
869 httpcookie_QueRemove(context->reading);
870 cookiedb_inserteachdb(db, context->reading, context->current);
872 httpcookie_delete(context->reading);
874 context->reading = NULL;
877 cookiedb_readheadercontext_delete(context);
880 EXPORT VOID cookiedb_clearallcookie(cookiedb_t *db)
882 cookie_volatiledb_clear(&db->vdb);
883 cookie_persistentdb_clear(&db->pdb);
886 EXPORT W cookiedb_writefile(cookiedb_t *db)
888 return cookie_persistentdb_writefile(&db->pdb);
891 EXPORT W cookiedb_readfile(cookiedb_t *db)
893 return cookie_persistentdb_readfile(&db->pdb);
896 LOCAL W cookiedb_initialize(cookiedb_t *db, LINK *db_lnk)
900 err = cookie_volatiledb_initialize(&db->vdb);
904 err = cookie_persistentdb_initialize(&db->pdb, db_lnk);
906 cookie_volatiledb_finalize(&db->vdb);
913 LOCAL VOID cookiedb_finalize(cookiedb_t *db)
915 cookie_persistentdb_finalize(&db->pdb);
916 cookie_volatiledb_finalize(&db->vdb);
919 EXPORT cookiedb_t* cookiedb_new(LINK *db_lnk)
924 db = malloc(sizeof(cookiedb_t));
928 err = cookiedb_initialize(db, db_lnk);
937 EXPORT VOID cookiedb_delete(cookiedb_t *db)
939 cookiedb_finalize(db);