OSDN Git Service

implement send condition check. but not tested.
[bbk/bchan.git] / src / cookiedb.c
1 /*
2  * cookiedb.c
3  *
4  * Copyright (c) 2011 project bchan
5  *
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.
9  *
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:
13  *
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.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  *    misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source
23  *    distribution.
24  *
25  */
26
27 #include    "cookiedb.h"
28 #include    "httpdateparser.h"
29
30 #include        <basic.h>
31 #include        <bstdio.h>
32 #include        <bstdlib.h>
33 #include        <bstring.h>
34 #include        <bsys/queue.h>
35
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)
39 #else
40 # define DP(arg) /**/
41 # define DP_ER(msg, err) /**/
42 #endif
43
44 struct ascstr_t_ {
45         UB *str;
46         W len;
47 };
48 typedef struct ascstr_t_ ascstr_t;
49
50 EXPORT W ascstr_appendstr(ascstr_t *astr, UB *apd, W apd_len)
51 {
52         UB *str;
53
54         str = realloc(astr->str, astr->len + apd_len + 1);
55         if (str == NULL) {
56                 return -1;
57         }
58         astr->str = str;
59
60         memcpy(astr->str + astr->len, apd, apd_len);
61         astr->len += apd_len;
62         astr->str[astr->len] = '\0';
63
64         return 0;
65 }
66
67 EXPORT W ascstr_initialize(ascstr_t *astr)
68 {
69         astr->str = malloc(sizeof(UB));
70         if (astr->str == NULL) {
71                 return -1;
72         }
73         astr->str[0] = '\0';
74         astr->len = 0;
75         return 0;
76 }
77
78 EXPORT VOID ascstr_finalize(ascstr_t *astr)
79 {
80         free(astr->str);
81 }
82
83 struct httpcookie_t_ {
84         QUEUE que;
85         /* requested host */
86         ascstr_t host;
87         /* Set-Cookie value */
88         ascstr_t attr; /* NAME string */
89         ascstr_t name; /* VALUE string */
90         ascstr_t comment;
91         ascstr_t domain;
92         Bool persistent;
93         STIME expires;
94         ascstr_t path;
95         ascstr_t version;
96         Bool secure;
97 };
98 typedef struct httpcookie_t_ httpcookie_t;
99
100 LOCAL Bool httpcookie_isvalueset(httpcookie_t *cookie)
101 {
102         if (cookie->attr.len == 0) {
103                 return False;
104         }
105         if (cookie->name.len == 0) {
106                 return False;
107         }
108         return True;
109 }
110
111 LOCAL httpcookie_t* httpcookie_nextnode(httpcookie_t *cookie)
112 {
113         return (httpcookie_t*)cookie->que.next;
114 }
115
116 LOCAL VOID httpcookie_setexpires(httpcookie_t *cookie, STIME expires)
117 {
118         cookie->expires = expires;
119 }
120
121 LOCAL VOID httpcookie_QueRemove(httpcookie_t *cookie)
122 {
123         QueRemove(&cookie->que);
124         QueInit(&cookie->que);
125 }
126
127 LOCAL W httpcookie_initialize(httpcookie_t *cookie, UB *host, W host_len)
128 {
129         W err;
130
131         QueInit(&cookie->que);
132         err = ascstr_initialize(&cookie->host);
133         if (err < 0) {
134                 goto error_host;
135         }
136         ascstr_appendstr(&cookie->host, host, host_len);
137         err = ascstr_initialize(&cookie->attr);
138         if (err < 0) {
139                 goto error_attr;
140         }
141         err = ascstr_initialize(&cookie->name);
142         if (err < 0) {
143                 goto error_name;
144         }
145         err = ascstr_initialize(&cookie->comment);
146         if (err < 0) {
147                 goto error_comment;
148         }
149         err = ascstr_initialize(&cookie->domain);
150         if (err < 0) {
151                 goto error_domain;
152         }
153         err = ascstr_initialize(&cookie->path);
154         if (err < 0) {
155                 goto error_path;
156         }
157         err = ascstr_initialize(&cookie->version);
158         if (err < 0) {
159                 goto error_version;
160         }
161
162         cookie->persistent = False;
163         cookie->secure = False;
164         cookie->expires = 0;
165
166         return 0;
167
168         ascstr_finalize(&cookie->version);
169 error_version:
170         ascstr_finalize(&cookie->path);
171 error_path:
172         ascstr_finalize(&cookie->domain);
173 error_domain:
174         ascstr_finalize(&cookie->comment);
175 error_comment:
176         ascstr_finalize(&cookie->name);
177 error_name:
178         ascstr_finalize(&cookie->attr);
179 error_attr:
180         ascstr_finalize(&cookie->host);
181 error_host:
182         return err;
183 }
184
185 LOCAL VOID httpcookie_finalize(httpcookie_t *cookie)
186 {
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);
195 }
196
197 LOCAL httpcookie_t* httpcookie_new(UB *host, W host_len)
198 {
199         httpcookie_t *cookie;
200         W err;
201
202         cookie = malloc(sizeof(httpcookie_t));
203         if (cookie == NULL) {
204                 return NULL;
205         }
206         err = httpcookie_initialize(cookie, host, host_len);
207         if (err < 0) {
208                 free(cookie);
209                 return NULL;
210         }
211         return cookie;
212 }
213
214 LOCAL VOID httpcookie_delete(httpcookie_t *cookie)
215 {
216         httpcookie_finalize(cookie);
217         free(cookie);
218 }
219
220 struct cookie_persistentdb_t_ {
221         QUEUE sentinel;
222         LINK *lnk;
223 };
224 typedef struct cookie_persistentdb_t_ cookie_persistentdb_t;
225
226 struct cookie_volatiledb_t_ {
227         QUEUE sentinel;
228 };
229 typedef struct cookie_volatiledb_t_ cookie_volatiledb_t;
230
231 struct cookiedb_t_ {
232         cookie_volatiledb_t vdb;
233         cookie_persistentdb_t pdb;
234 };
235
236 LOCAL W cookie_persistentdb_writefile(cookie_persistentdb_t *db)
237 {
238         /* TODO */
239         return -1;
240 }
241
242 LOCAL W cookie_persistentdb_readfile(cookie_persistentdb_t *db)
243 {
244         /* TODO */
245         return -1;
246 }
247
248 LOCAL VOID cookie_persistentdb_clear(cookie_persistentdb_t *db)
249 {
250         httpcookie_t *cookie;
251         Bool empty;
252
253         for (;;) {
254                 empty = isQueEmpty(&db->sentinel);
255                 if (empty == True) {
256                         break;
257                 }
258                 cookie = (httpcookie_t*)db->sentinel.prev;
259                 httpcookie_delete(cookie);
260         }
261
262         /* TODO clear file */
263 }
264
265 LOCAL httpcookie_t* cookie_persistentdb_sentinelnode(cookie_persistentdb_t *db)
266 {
267         return (httpcookie_t*)&db->sentinel;
268 }
269
270 LOCAL VOID cookie_persistentdb_insertcookie(cookie_persistentdb_t *db, httpcookie_t *cookie)
271 {
272         QueInsert(&cookie->que, &db->sentinel);
273 }
274
275 LOCAL W cookie_persistentdb_initialize(cookie_persistentdb_t *db, LINK *db_lnk)
276 {
277         QueInit(&db->sentinel);
278         db->lnk = db_lnk;
279         return 0;
280 }
281
282 LOCAL VOID cookie_persistentdb_finalize(cookie_persistentdb_t *db)
283 {
284         httpcookie_t *cookie;
285         Bool empty;
286
287         for (;;) {
288                 empty = isQueEmpty(&db->sentinel);
289                 if (empty == True) {
290                         break;
291                 }
292                 cookie = (httpcookie_t*)db->sentinel.prev;
293                 httpcookie_delete(cookie);
294         }
295 }
296
297 LOCAL VOID cookie_volatiledb_clear(cookie_volatiledb_t *db)
298 {
299         httpcookie_t *cookie;
300         Bool empty;
301
302         for (;;) {
303                 empty = isQueEmpty(&db->sentinel);
304                 if (empty == True) {
305                         break;
306                 }
307                 cookie = (httpcookie_t*)db->sentinel.prev;
308                 httpcookie_delete(cookie);
309         }
310 }
311
312 LOCAL httpcookie_t* cookie_volatiledb_sentinelnode(cookie_volatiledb_t *db)
313 {
314         return (httpcookie_t*)&db->sentinel;
315 }
316
317 LOCAL VOID cookie_volatiledb_insertcookie(cookie_volatiledb_t *db, httpcookie_t *cookie)
318 {
319         QueInsert(&cookie->que, &db->sentinel);
320 }
321
322 LOCAL W cookie_volatiledb_initialize(cookie_volatiledb_t *db)
323 {
324         QueInit(&db->sentinel);
325         return 0;
326 }
327
328 LOCAL VOID cookie_volatiledb_finalize(cookie_volatiledb_t *db)
329 {
330         httpcookie_t *cookie;
331         Bool empty;
332
333         for (;;) {
334                 empty = isQueEmpty(&db->sentinel);
335                 if (empty == True) {
336                         break;
337                 }
338                 cookie = (httpcookie_t*)db->sentinel.prev;
339                 httpcookie_delete(cookie);
340         }
341 }
342
343 struct cookiedb_writeiterator_t_ {
344         cookiedb_t *origin;
345         ascstr_t host;
346         ascstr_t path;
347         Bool secure;
348         STIME time;
349         httpcookie_t *current;
350         enum {
351                 COOKIEDB_WRITEITERATOR_STATE_VDB,
352                 COOKIEDB_WRITEITERATOR_STATE_PDB,
353         } state;
354 };
355 typedef struct cookiedb_writeiterator_t_ cookiedb_writeiterator_t;
356
357 LOCAL W count_priod(ascstr_t *str)
358 {
359         W i,count;
360
361         count = 0;
362         for (i = 0; i < str->len; i++) {
363                 if (str->str[i] == '.') {
364                         count++;
365                 }
366         }
367
368         return count;
369 }
370
371 LOCAL Bool check_specified_TLD(ascstr_t *domain)
372 {
373         if (strncmp(domain->str + domain->len - 4, ".net", 4) == 0) {
374                 return True;
375         }
376         if (strncmp(domain->str + domain->len - 4, ".com", 4) == 0) {
377                 return True;
378         }
379         if (strncmp(domain->str + domain->len - 4, ".edu", 4) == 0) {
380                 return True;
381         }
382         if (strncmp(domain->str + domain->len - 4, ".org", 4) == 0) {
383                 return True;
384         }
385         if (strncmp(domain->str + domain->len - 4, ".gov", 4) == 0) {
386                 return True;
387         }
388         if (strncmp(domain->str + domain->len - 4, ".mil", 4) == 0) {
389                 return True;
390         }
391         if (strncmp(domain->str + domain->len - 4, ".int", 4) == 0) {
392                 return True;
393         }
394         /* TODO: other TLDs. */
395         return False;
396 }
397
398 LOCAL Bool cookiedb_writeiterator_domaincheck(ascstr_t *send_host, ascstr_t *origin_host)
399 {
400         W count;
401         Bool ok;
402
403         if (origin_host->len < send_host->len) {
404                 return False;
405         }
406         if (strncmp(origin_host->str + origin_host->len - send_host->len, send_host->str, send_host->len) != 0) {
407                 return False;
408         }
409         count = count_priod(send_host);
410         ok = check_specified_TLD(send_host);
411         if (ok == True) {
412                 if (count < 2) {
413                         return False;
414                 }
415         } else {
416                 if (count < 3) {
417                         return False;
418                 }
419         }
420
421         return True;
422 }
423
424 LOCAL Bool cookiedb_writeitereator_pathcheck(ascstr_t *send_path, ascstr_t *origin_path)
425 {
426         if (origin_path->len < send_path->len) {
427                 return False;
428         }
429         if (strncmp(origin_path->str, send_path->str, send_path->len) != 0) {
430                 return False;
431         }
432         return True;
433 }
434
435 LOCAL Bool cookiedb_writeiterator_checksendcondition(cookiedb_writeiterator_t *iter, httpcookie_t *cookie)
436 {
437         Bool ok;
438
439         if (cookie->secure == True) {
440                 if (iter->secure != True) {
441                         return False;
442                 }
443         }
444         if (cookie->persistent == True) {
445                 if (cookie->expires < iter->time) {
446                         return False;
447                 }
448         }
449         if (cookie->domain.len != 0) {
450                 ok = cookiedb_writeiterator_domaincheck(&iter->host, &cookie->domain);
451         } else {
452                 ok = cookiedb_writeiterator_domaincheck(&iter->host, &cookie->host);
453         }
454         if (ok == False) {
455                 return False;
456         }
457         if (cookie->path.len != 0) {
458                 ok = cookiedb_writeitereator_pathcheck(&iter->path, &cookie->path);
459         } else {
460                 /* TODO */
461         }
462         if (ok == False) {
463                 return False;
464         }
465
466         return True;
467 }
468
469 LOCAL Bool cookiedb_writeiterator_next(cookiedb_writeiterator_t *iter, httpcookie_t **cookie)
470 {
471         httpcookie_t *senti;
472         Bool send;
473
474         if (iter->state == COOKIEDB_WRITEITERATOR_STATE_VDB) {
475                 senti = cookie_volatiledb_sentinelnode(&iter->origin->vdb);
476                 for (;;) {
477                         if (iter->current == senti) {
478                                 break;
479                         }
480                         send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
481                         if (send == True) {
482                                 break;
483                         }
484                         iter->current = httpcookie_nextnode(iter->current);
485                 }
486                 if (iter->current != senti) {
487                         *cookie = iter->current;
488                         iter->current = httpcookie_nextnode(iter->current);
489                         return True;
490                 } else {
491                         iter->state = COOKIEDB_WRITEITERATOR_STATE_PDB;
492                         senti = cookie_persistentdb_sentinelnode(&iter->origin->pdb);
493                         iter->current = httpcookie_nextnode(senti);
494                 }
495         }
496         if (iter->state == COOKIEDB_WRITEITERATOR_STATE_PDB) {
497                 senti = cookie_persistentdb_sentinelnode(&iter->origin->pdb);
498                 for (;;) {
499                         if (iter->current == senti) {
500                                 break;
501                         }
502                         send = cookiedb_writeiterator_checksendcondition(iter, iter->current);
503                         if (send == True) {
504                                 break;
505                         }
506                         iter->current = httpcookie_nextnode(iter->current);
507                 }
508                 if (iter->current != senti) {
509                         *cookie = iter->current;
510                         iter->current = httpcookie_nextnode(iter->current);
511                         return True;
512                 }
513         }
514
515         return False;
516 }
517
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)
519 {
520         W err;
521         httpcookie_t *senti;
522
523         err = ascstr_initialize(&iter->host);
524         if (err < 0) {
525                 return err;
526         }
527         err = ascstr_appendstr(&iter->host, host, host_len);
528         if (err < 0) {
529                 ascstr_finalize(&iter->host);
530                 return err;
531         }
532         err = ascstr_initialize(&iter->path);
533         if (err < 0) {
534                 ascstr_finalize(&iter->host);
535                 return err;
536         }
537         err = ascstr_appendstr(&iter->path, path, path_len);
538         if (err < 0) {
539                 ascstr_finalize(&iter->path);
540                 ascstr_finalize(&iter->host);
541                 return err;
542         }
543
544         iter->origin = db;
545         iter->secure = secure;
546         iter->time = time;
547         iter->state = COOKIEDB_WRITEITERATOR_STATE_VDB;
548         senti = cookie_volatiledb_sentinelnode(&db->vdb);
549         iter->current = httpcookie_nextnode(senti);
550
551         return 0;
552 }
553
554 LOCAL VOID cookiedb_writeiterator_finalize(cookiedb_writeiterator_t *iter)
555 {
556         ascstr_finalize(&iter->path);
557         ascstr_finalize(&iter->host);
558 }
559
560 struct cookiedb_writeheadercontext_t_ {
561         cookiedb_writeiterator_t iter;
562         enum {
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,
570         } state;
571         httpcookie_t *current;
572 };
573
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";
578
579 EXPORT Bool cookiedb_writeheadercontext_makeheader(cookiedb_writeheadercontext_t *context, UB **str, W *len)
580 {
581         Bool cont;
582
583         switch (context->state) {
584         case COOKIEDB_WRITEITERATORCONTEXT_STATE_START:
585                 cont = cookiedb_writeiterator_next(&context->iter, &context->current);
586                 if (cont == False) {
587                         return False;
588                 }
589                 *str = cookiedb_writeheader_context_headername;
590                 *len = 8;
591                 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME;
592                 return True;
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;
597                 return True;
598         case COOKIEDB_WRITEITERATORCONTEXT_STATE_EQUAL:
599                 *str = cookiedb_writeheader_context_equal;
600                 *len = 1;
601                 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_VALUE;
602                 return True;
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;
607                 return True;
608         case COOKIEDB_WRITEITERATORCONTEXT_STATE_COLON:
609                 *str = cookiedb_writeheader_context_colon;
610                 *len = 2;
611                 cont = cookiedb_writeiterator_next(&context->iter, &context->current);
612                 if (cont == False) {
613                         context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF;
614                 } else {
615                         context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_NAME;
616                 }
617                 return True;
618         case COOKIEDB_WRITEITERATORCONTEXT_STATE_CRLF:
619                 *str = cookiedb_writeheader_context_crlf;
620                 *len = 2;
621                 context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_END;
622                 return True;
623         case COOKIEDB_WRITEITERATORCONTEXT_STATE_END:
624                 return False;
625         }
626
627         return False;
628 }
629
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)
631 {
632         cookiedb_writeheadercontext_t *context;
633         W err;
634
635         context = (cookiedb_writeheadercontext_t*)malloc(sizeof(cookiedb_writeheadercontext_t));
636         if (context == NULL) {
637                 return NULL;
638         }
639         err = cookiedb_writeiterator_initialize(&context->iter, db, host, host_len, path, path_len, secure, time);
640         if (err < 0) {
641                 free(context);
642                 return NULL;
643         }
644         context->state = COOKIEDB_WRITEITERATORCONTEXT_STATE_START;
645
646         return context;
647 }
648
649 LOCAL VOID cookiedb_writeheadercontext_delete(cookiedb_writeheadercontext_t *context)
650 {
651         cookiedb_writeiterator_finalize(&context->iter);
652         free(context);
653 }
654
655 EXPORT cookiedb_writeheadercontext_t* cookiedb_startheaderwrite(cookiedb_t *db, UB *host, W host_len, UB *path, W path_len, Bool secure, STIME time)
656 {
657         cookiedb_writeheadercontext_t *context;
658
659         context = cookiedb_writeheadercontext_new(db, host, host_len, path, path_len, secure, time);
660         if (context == NULL) {
661                 return NULL;
662         }
663
664         return context;
665 }
666
667 EXPORT VOID cookiedb_endheaderwrite(cookiedb_t *db, cookiedb_writeheadercontext_t *context)
668 {
669         cookiedb_writeheadercontext_delete(context);
670 }
671
672 struct cookiedb_readheadercontext_t_ {
673         ascstr_t host;
674         STIME current;
675         QUEUE sentinel;
676         enum {
677                 COOKIEDB_READHEADERCONTEXT_STATE_START,
678                 COOKIEDB_READHEADERCONTEXT_STATE_NAMEATTR,
679                 COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL,
680         } state;
681         httpcookie_t *reading;
682 };
683
684 LOCAL VOID cookiedb_readheadercontext_insertcookie(cookiedb_readheadercontext_t *context, httpcookie_t *cookie)
685 {
686         QueInsert(&cookie->que, &context->sentinel);
687 }
688
689 EXPORT W cookiedb_readheadercontext_appendchar_attr(cookiedb_readheadercontext_t *context, UB ch)
690 {
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);
695                 }
696                 context->reading = httpcookie_new(context->host.str, context->host.len);
697                 if (context->reading == NULL) {
698                         return -1; /* TODO: error value */
699                 }
700         }
701         return ascstr_appendstr(&context->reading->attr, &ch, 1);
702 }
703
704 LOCAL W cookiedb_readheadercontext_appdatechar_common(cookiedb_readheadercontext_t *context, ascstr_t *target, UB ch)
705 {
706         if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
707                 return 0;
708         }
709         context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
710         return ascstr_appendstr(target, &ch, 1);
711 }
712
713 EXPORT W cookiedb_readheadercontext_appendchar_name(cookiedb_readheadercontext_t *context, UB ch)
714 {
715         return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->name, ch);
716 }
717
718 EXPORT W cookiedb_readheadercontext_appendchar_comment(cookiedb_readheadercontext_t *context, UB ch)
719 {
720         return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->comment, ch);
721 }
722
723 EXPORT W cookiedb_readheadercontext_appendchar_domain(cookiedb_readheadercontext_t *context, UB ch)
724 {
725         return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->domain, ch);
726 }
727
728 EXPORT W cookiedb_readheadercontext_appendchar_path(cookiedb_readheadercontext_t *context, UB ch)
729 {
730         return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->path, ch);
731 }
732
733 EXPORT W cookiedb_readheadercontext_appendchar_version(cookiedb_readheadercontext_t *context, UB ch)
734 {
735         return cookiedb_readheadercontext_appdatechar_common(context, &context->reading->version, ch);
736 }
737
738 EXPORT W cookiedb_readheadercontext_setsecure(cookiedb_readheadercontext_t *context)
739 {
740         if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
741                 return 0;
742         }
743         context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
744
745         context->reading->secure = True;
746
747         return 0;
748 }
749
750 EXPORT W cookiedb_readheadercontext_setexpires(cookiedb_readheadercontext_t *context, STIME expires)
751 {
752         if (context->state == COOKIEDB_READHEADERCONTEXT_STATE_START) {
753                 return 0;
754         }
755         context->state = COOKIEDB_READHEADERCONTEXT_STATE_OTHERVAL;
756
757         httpcookie_setexpires(context->reading, expires);
758
759         return 0;
760 }
761
762 EXPORT W cookiedb_readheadercontext_setmaxage(cookiedb_readheadercontext_t *context, W seconds)
763 {
764         if (seconds <= 0) {
765                 return 0;
766         }
767
768         httpcookie_setexpires(context->reading, context->current + seconds);
769
770         return 0;
771 }
772
773 LOCAL httpcookie_t* cookiedb_readheadercontext_prevcookie(cookiedb_readheadercontext_t *context)
774 {
775         Bool empty;
776         empty = isQueEmpty(&context->sentinel);
777         if (empty == True) {
778                 return NULL;
779         }
780         return (httpcookie_t*)context->sentinel.prev;
781 }
782
783 LOCAL cookiedb_readheadercontext_t* cookiedb_readheadercontext_new(UB *host, W host_len, STIME time)
784 {
785         cookiedb_readheadercontext_t *context;
786         W err;
787
788         context = (cookiedb_readheadercontext_t*)malloc(sizeof(cookiedb_readheadercontext_t));
789         if (context == NULL) {
790                 return NULL;
791         }
792         err = ascstr_initialize(&context->host);
793         if (err < 0) {
794                 free(context);
795                 return NULL;
796         }
797         err = ascstr_appendstr(&context->host, host, host_len);
798         if (err < 0) {
799                 ascstr_finalize(&context->host);
800                 free(context);
801                 return NULL;
802         }
803         context->reading = NULL;
804         QueInit(&context->sentinel);
805         context->state = COOKIEDB_READHEADERCONTEXT_STATE_START;
806
807         return context;
808 }
809
810 LOCAL VOID cookiedb_readheadercontext_delete(cookiedb_readheadercontext_t *context)
811 {
812         httpcookie_t *cookie;
813
814         for (;;) {
815                 cookie = cookiedb_readheadercontext_prevcookie(context);
816                 if (cookie == NULL) {
817                         break;
818                 }
819                 httpcookie_delete(cookie);
820         }
821         if (context->reading != NULL) {
822                 httpcookie_delete(context->reading);
823         }
824
825         ascstr_finalize(&context->host);
826         free(context);
827 }
828
829 EXPORT cookiedb_readheadercontext_t* cookiedb_startheaderread(cookiedb_t *db, UB *host, W host_len, STIME time)
830 {
831         cookiedb_readheadercontext_t *context;
832
833         context = cookiedb_readheadercontext_new(host, host_len, time);
834         if (context == NULL) {
835                 return NULL;
836         }
837
838         return context;
839 }
840
841 LOCAL VOID cookiedb_inserteachdb(cookiedb_t *db, httpcookie_t *cookie, STIME current)
842 {
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);
850         }
851 }
852
853 EXPORT VOID cookiedb_endheaderread(cookiedb_t *db, cookiedb_readheadercontext_t *context)
854 {
855         httpcookie_t *cookie;
856         Bool ok;
857
858         for (;;) {
859                 cookie = cookiedb_readheadercontext_prevcookie(context);
860                 if (cookie == NULL) {
861                         break;
862                 }
863                 httpcookie_QueRemove(cookie);
864                 cookiedb_inserteachdb(db, cookie, context->current);
865         }
866         if (context->reading != NULL) {
867                 ok = httpcookie_isvalueset(context->reading);
868                 if (ok == True) {
869                         httpcookie_QueRemove(context->reading);
870                         cookiedb_inserteachdb(db, context->reading, context->current);
871                 } else {
872                         httpcookie_delete(context->reading);
873                 }
874                 context->reading = NULL;
875         }
876
877         cookiedb_readheadercontext_delete(context);
878 }
879
880 EXPORT VOID cookiedb_clearallcookie(cookiedb_t *db)
881 {
882         cookie_volatiledb_clear(&db->vdb);
883         cookie_persistentdb_clear(&db->pdb);
884 }
885
886 EXPORT W cookiedb_writefile(cookiedb_t *db)
887 {
888         return cookie_persistentdb_writefile(&db->pdb);
889 }
890
891 EXPORT W cookiedb_readfile(cookiedb_t *db)
892 {
893         return cookie_persistentdb_readfile(&db->pdb);
894 }
895
896 LOCAL W cookiedb_initialize(cookiedb_t *db, LINK *db_lnk)
897 {
898         W err;
899
900         err = cookie_volatiledb_initialize(&db->vdb);
901         if (err < 0) {
902                 return err;
903         }
904         err = cookie_persistentdb_initialize(&db->pdb, db_lnk);
905         if (err < 0) {
906                 cookie_volatiledb_finalize(&db->vdb);
907                 return err;
908         }
909
910         return 0;
911 }
912
913 LOCAL VOID cookiedb_finalize(cookiedb_t *db)
914 {
915         cookie_persistentdb_finalize(&db->pdb);
916         cookie_volatiledb_finalize(&db->vdb);
917 }
918
919 EXPORT cookiedb_t* cookiedb_new(LINK *db_lnk)
920 {
921         cookiedb_t *db;
922         W err;
923
924         db = malloc(sizeof(cookiedb_t));
925         if (db == NULL) {
926                 return NULL;
927         }
928         err = cookiedb_initialize(db, db_lnk);
929         if (err < 0) {
930                 free(db);
931                 return NULL;
932         }
933
934         return db;
935 }
936
937 EXPORT VOID cookiedb_delete(cookiedb_t *db)
938 {
939         cookiedb_finalize(db);
940         free(db);
941 }