xref: /plan9/sys/src/cmd/webfs/cookies.c (revision 2c23bbf7b5b71c88de0d92a1b1ea4a1a9e240601)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <ndb.h>
59a747e4fSDavid du Colombier #include <fcall.h>
69a747e4fSDavid du Colombier #include <thread.h>
79a747e4fSDavid du Colombier #include <9p.h>
89a747e4fSDavid du Colombier #include <ctype.h>
99a747e4fSDavid du Colombier #include "dat.h"
109a747e4fSDavid du Colombier #include "fns.h"
119a747e4fSDavid du Colombier 
129a747e4fSDavid du Colombier int cookiedebug;
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier typedef struct Cookie Cookie;
159a747e4fSDavid du Colombier typedef struct Jar Jar;
169a747e4fSDavid du Colombier 
179a747e4fSDavid du Colombier struct Cookie
189a747e4fSDavid du Colombier {
199a747e4fSDavid du Colombier 	/* external info */
209a747e4fSDavid du Colombier 	char*	name;
219a747e4fSDavid du Colombier 	char*	value;
229a747e4fSDavid du Colombier 	char*	dom;		/* starts with . */
239a747e4fSDavid du Colombier 	char*	path;
249a747e4fSDavid du Colombier 	char*	version;
259a747e4fSDavid du Colombier 	char*	comment;		/* optional, may be nil */
269a747e4fSDavid du Colombier 
279a747e4fSDavid du Colombier 	uint		expire;		/* time of expiration: ~0 means when webcookies dies */
289a747e4fSDavid du Colombier 	int		secure;
299a747e4fSDavid du Colombier 	int		explicitdom;	/* dom was explicitly set */
309a747e4fSDavid du Colombier 	int		explicitpath;	/* path was explicitly set */
319a747e4fSDavid du Colombier 	int		netscapestyle;
329a747e4fSDavid du Colombier 
339a747e4fSDavid du Colombier 	/* internal info */
349a747e4fSDavid du Colombier 	int		deleted;
359a747e4fSDavid du Colombier 	int		mark;
369a747e4fSDavid du Colombier 	int		ondisk;
379a747e4fSDavid du Colombier };
389a747e4fSDavid du Colombier 
399a747e4fSDavid du Colombier struct Jar
409a747e4fSDavid du Colombier {
419a747e4fSDavid du Colombier 	Cookie	*c;
429a747e4fSDavid du Colombier 	int		nc;
439a747e4fSDavid du Colombier 	int		mc;
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier 	Qid		qid;
469a747e4fSDavid du Colombier 	int		dirty;
479a747e4fSDavid du Colombier 	char		*file;
489a747e4fSDavid du Colombier 	char		*lockfile;
499a747e4fSDavid du Colombier };
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier struct {
529a747e4fSDavid du Colombier 	char *s;
539a747e4fSDavid du Colombier 	int	offset;
549a747e4fSDavid du Colombier 	int	ishttp;
559a747e4fSDavid du Colombier } stab[] = {
569a747e4fSDavid du Colombier 	"domain",		offsetof(Cookie, dom),		1,
579a747e4fSDavid du Colombier 	"path",		offsetof(Cookie, path),		1,
589a747e4fSDavid du Colombier 	"name",		offsetof(Cookie, name),		0,
599a747e4fSDavid du Colombier 	"value",		offsetof(Cookie, value),		0,
609a747e4fSDavid du Colombier 	"comment",	offsetof(Cookie, comment),	1,
619a747e4fSDavid du Colombier 	"version",		offsetof(Cookie, version),		1,
629a747e4fSDavid du Colombier };
639a747e4fSDavid du Colombier 
649a747e4fSDavid du Colombier struct {
659a747e4fSDavid du Colombier 	char *s;
669a747e4fSDavid du Colombier 	int	offset;
679a747e4fSDavid du Colombier } itab[] = {
689a747e4fSDavid du Colombier 	"expire",			offsetof(Cookie, expire),
699a747e4fSDavid du Colombier 	"secure",			offsetof(Cookie, secure),
709a747e4fSDavid du Colombier 	"explicitdomain",	offsetof(Cookie, explicitdom),
719a747e4fSDavid du Colombier 	"explicitpath",		offsetof(Cookie, explicitpath),
729a747e4fSDavid du Colombier 	"netscapestyle",	offsetof(Cookie, netscapestyle),
739a747e4fSDavid du Colombier };
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier #pragma varargck type "J"	Jar*
769a747e4fSDavid du Colombier #pragma varargck type "K"	Cookie*
779a747e4fSDavid du Colombier 
789a747e4fSDavid du Colombier /* HTTP format */
799a747e4fSDavid du Colombier static int
jarfmt(Fmt * fp)809a747e4fSDavid du Colombier jarfmt(Fmt *fp)
819a747e4fSDavid du Colombier {
829a747e4fSDavid du Colombier 	int i;
839a747e4fSDavid du Colombier 	Jar *jar;
849a747e4fSDavid du Colombier 
859a747e4fSDavid du Colombier 	jar = va_arg(fp->args, Jar*);
869a747e4fSDavid du Colombier 
879a747e4fSDavid du Colombier 	if(jar == nil || jar->nc == 0)
889a747e4fSDavid du Colombier 		return 0;
899a747e4fSDavid du Colombier 
909a747e4fSDavid du Colombier 	fmtstrcpy(fp, "Cookie: ");
919a747e4fSDavid du Colombier 	if(jar->c[0].version)
929a747e4fSDavid du Colombier 		fmtprint(fp, "$Version=%s; ", jar->c[0].version);
939a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)
949a747e4fSDavid du Colombier 		fmtprint(fp, "%s%s=%s", i ? "; ": "", jar->c[i].name, jar->c[i].value);
959a747e4fSDavid du Colombier 	fmtstrcpy(fp, "\r\n");
969a747e4fSDavid du Colombier 	return 0;
979a747e4fSDavid du Colombier }
989a747e4fSDavid du Colombier 
999a747e4fSDavid du Colombier /* individual cookie */
1009a747e4fSDavid du Colombier static int
cookiefmt(Fmt * fp)1019a747e4fSDavid du Colombier cookiefmt(Fmt *fp)
1029a747e4fSDavid du Colombier {
1039a747e4fSDavid du Colombier 	int j, k, first;
1049a747e4fSDavid du Colombier 	char *t;
1059a747e4fSDavid du Colombier 	Cookie *c;
1069a747e4fSDavid du Colombier 
1079a747e4fSDavid du Colombier 	c = va_arg(fp->args, Cookie*);
1089a747e4fSDavid du Colombier 
1099a747e4fSDavid du Colombier 	first = 1;
1109a747e4fSDavid du Colombier 	for(j=0; j<nelem(stab); j++){
1119dfc0cb2SDavid du Colombier 		t = *(char**)((uintptr)c+stab[j].offset);
1129a747e4fSDavid du Colombier 		if(t == nil)
1139a747e4fSDavid du Colombier 			continue;
1149a747e4fSDavid du Colombier 		if(first)
1159a747e4fSDavid du Colombier 			first = 0;
1169a747e4fSDavid du Colombier 		else
1179a747e4fSDavid du Colombier 			fmtstrcpy(fp, " ");
1189a747e4fSDavid du Colombier 		fmtprint(fp, "%s=%q", stab[j].s, t);
1199a747e4fSDavid du Colombier 	}
1209a747e4fSDavid du Colombier 	for(j=0; j<nelem(itab); j++){
1219dfc0cb2SDavid du Colombier 		k = *(int*)((uintptr)c+itab[j].offset);
1229a747e4fSDavid du Colombier 		if(k == 0)
1239a747e4fSDavid du Colombier 			continue;
1249a747e4fSDavid du Colombier 		if(first)
1259a747e4fSDavid du Colombier 			first = 0;
1269a747e4fSDavid du Colombier 		else
1279a747e4fSDavid du Colombier 			fmtstrcpy(fp, " ");
1289a747e4fSDavid du Colombier 		fmtprint(fp, "%s=%ud", itab[j].s, k);
1299a747e4fSDavid du Colombier 	}
1309a747e4fSDavid du Colombier 	return 0;
1319a747e4fSDavid du Colombier }
1329a747e4fSDavid du Colombier 
1339a747e4fSDavid du Colombier /*
1349a747e4fSDavid du Colombier  * sort cookies:
1359a747e4fSDavid du Colombier  *	- alpha by name
1369a747e4fSDavid du Colombier  *	- alpha by domain
1379a747e4fSDavid du Colombier  *	- longer paths first, then alpha by path (RFC2109 4.3.4)
1389a747e4fSDavid du Colombier  */
1399a747e4fSDavid du Colombier static int
cookiecmp(Cookie * a,Cookie * b)1409a747e4fSDavid du Colombier cookiecmp(Cookie *a, Cookie *b)
1419a747e4fSDavid du Colombier {
1429a747e4fSDavid du Colombier 	int i;
1439a747e4fSDavid du Colombier 
1449a747e4fSDavid du Colombier 	if((i = strcmp(a->name, b->name)) != 0)
1459a747e4fSDavid du Colombier 		return i;
1469a747e4fSDavid du Colombier 	if((i = cistrcmp(a->dom, b->dom)) != 0)
1479a747e4fSDavid du Colombier 		return i;
1489a747e4fSDavid du Colombier 	if((i = strlen(b->path) - strlen(a->path)) != 0)
1499a747e4fSDavid du Colombier 		return i;
1509a747e4fSDavid du Colombier 	if((i = strcmp(a->path, b->path)) != 0)
1519a747e4fSDavid du Colombier 		return i;
1529a747e4fSDavid du Colombier 	return 0;
1539a747e4fSDavid du Colombier }
1549a747e4fSDavid du Colombier 
1559a747e4fSDavid du Colombier static int
exactcookiecmp(Cookie * a,Cookie * b)1569a747e4fSDavid du Colombier exactcookiecmp(Cookie *a, Cookie *b)
1579a747e4fSDavid du Colombier {
1589a747e4fSDavid du Colombier 	int i;
1599a747e4fSDavid du Colombier 
1609a747e4fSDavid du Colombier 	if((i = cookiecmp(a, b)) != 0)
1619a747e4fSDavid du Colombier 		return i;
1629a747e4fSDavid du Colombier 	if((i = strcmp(a->value, b->value)) != 0)
1639a747e4fSDavid du Colombier 		return i;
1649a747e4fSDavid du Colombier 	if(a->version || b->version){
1659a747e4fSDavid du Colombier 		if(!a->version)
1669a747e4fSDavid du Colombier 			return -1;
1679a747e4fSDavid du Colombier 		if(!b->version)
1689a747e4fSDavid du Colombier 			return 1;
1699a747e4fSDavid du Colombier 		if((i = strcmp(a->version, b->version)) != 0)
1709a747e4fSDavid du Colombier 			return i;
1719a747e4fSDavid du Colombier 	}
1729a747e4fSDavid du Colombier 	if(a->comment || b->comment){
1739a747e4fSDavid du Colombier 		if(!a->comment)
1749a747e4fSDavid du Colombier 			return -1;
1759a747e4fSDavid du Colombier 		if(!b->comment)
1769a747e4fSDavid du Colombier 			return 1;
1779a747e4fSDavid du Colombier 		if((i = strcmp(a->comment, b->comment)) != 0)
1789a747e4fSDavid du Colombier 			return i;
1799a747e4fSDavid du Colombier 	}
1809a747e4fSDavid du Colombier 	if((i = b->expire - a->expire) != 0)
1819a747e4fSDavid du Colombier 		return i;
1829a747e4fSDavid du Colombier 	if((i = b->secure - a->secure) != 0)
1839a747e4fSDavid du Colombier 		return i;
1849a747e4fSDavid du Colombier 	if((i = b->explicitdom - a->explicitdom) != 0)
1859a747e4fSDavid du Colombier 		return i;
1869a747e4fSDavid du Colombier 	if((i = b->explicitpath - a->explicitpath) != 0)
1879a747e4fSDavid du Colombier 		return i;
1889a747e4fSDavid du Colombier 	if((i = b->netscapestyle - a->netscapestyle) != 0)
1899a747e4fSDavid du Colombier 		return i;
1909a747e4fSDavid du Colombier 
1919a747e4fSDavid du Colombier 	return 0;
1929a747e4fSDavid du Colombier }
1939a747e4fSDavid du Colombier 
1949a747e4fSDavid du Colombier static void
freecookie(Cookie * c)1959a747e4fSDavid du Colombier freecookie(Cookie *c)
1969a747e4fSDavid du Colombier {
1979a747e4fSDavid du Colombier 	int i;
1989a747e4fSDavid du Colombier 
1999a747e4fSDavid du Colombier 	for(i=0; i<nelem(stab); i++)
2009dfc0cb2SDavid du Colombier 		free(*(char**)((uintptr)c+stab[i].offset));
2019a747e4fSDavid du Colombier }
2029a747e4fSDavid du Colombier 
2039a747e4fSDavid du Colombier static void
copycookie(Cookie * c)2049a747e4fSDavid du Colombier copycookie(Cookie *c)
2059a747e4fSDavid du Colombier {
2069a747e4fSDavid du Colombier 	int i;
2079a747e4fSDavid du Colombier 	char **ps;
2089a747e4fSDavid du Colombier 
2099a747e4fSDavid du Colombier 	for(i=0; i<nelem(stab); i++){
2109dfc0cb2SDavid du Colombier 		ps = (char**)((uintptr)c+stab[i].offset);
2119a747e4fSDavid du Colombier 		if(*ps)
2129a747e4fSDavid du Colombier 			*ps = estrdup9p(*ps);
2139a747e4fSDavid du Colombier 	}
2149a747e4fSDavid du Colombier }
2159a747e4fSDavid du Colombier 
2169a747e4fSDavid du Colombier static void
delcookie(Jar * j,Cookie * c)2179a747e4fSDavid du Colombier delcookie(Jar *j, Cookie *c)
2189a747e4fSDavid du Colombier {
2199a747e4fSDavid du Colombier 	int i;
2209a747e4fSDavid du Colombier 
2219a747e4fSDavid du Colombier 	j->dirty = 1;
2229a747e4fSDavid du Colombier 	i = c - j->c;
2239a747e4fSDavid du Colombier 	if(i < 0 || i >= j->nc)
2249a747e4fSDavid du Colombier 		abort();
2259a747e4fSDavid du Colombier 	c->deleted = 1;
2269a747e4fSDavid du Colombier }
2279a747e4fSDavid du Colombier 
2289a747e4fSDavid du Colombier static void
addcookie(Jar * j,Cookie * c)2299a747e4fSDavid du Colombier addcookie(Jar *j, Cookie *c)
2309a747e4fSDavid du Colombier {
2319a747e4fSDavid du Colombier 	int i;
2329a747e4fSDavid du Colombier 
2339a747e4fSDavid du Colombier 	if(!c->name || !c->value || !c->path || !c->dom){
2349a747e4fSDavid du Colombier 		fprint(2, "not adding incomplete cookie\n");
2359a747e4fSDavid du Colombier 		return;
2369a747e4fSDavid du Colombier 	}
2379a747e4fSDavid du Colombier 
2389a747e4fSDavid du Colombier 	if(cookiedebug)
2399a747e4fSDavid du Colombier 		fprint(2, "add %K\n", c);
2409a747e4fSDavid du Colombier 
2419a747e4fSDavid du Colombier 	for(i=0; i<j->nc; i++)
2429a747e4fSDavid du Colombier 		if(cookiecmp(&j->c[i], c) == 0){
2439a747e4fSDavid du Colombier 			if(cookiedebug)
2449a747e4fSDavid du Colombier 				fprint(2, "cookie %K matches %K\n", &j->c[i], c);
2459a747e4fSDavid du Colombier 			if(exactcookiecmp(&j->c[i], c) == 0){
2469a747e4fSDavid du Colombier 				if(cookiedebug)
2479a747e4fSDavid du Colombier 					fprint(2, "\texactly\n");
2489a747e4fSDavid du Colombier 				j->c[i].mark = 0;
2499a747e4fSDavid du Colombier 				return;
2509a747e4fSDavid du Colombier 			}
2519a747e4fSDavid du Colombier 			delcookie(j, &j->c[i]);
2529a747e4fSDavid du Colombier 		}
2539a747e4fSDavid du Colombier 
2549a747e4fSDavid du Colombier 	j->dirty = 1;
2559a747e4fSDavid du Colombier 	if(j->nc == j->mc){
2569a747e4fSDavid du Colombier 		j->mc += 16;
2579a747e4fSDavid du Colombier 		j->c = erealloc9p(j->c, j->mc*sizeof(Cookie));
2589a747e4fSDavid du Colombier 	}
2599a747e4fSDavid du Colombier 	j->c[j->nc] = *c;
2609a747e4fSDavid du Colombier 	copycookie(&j->c[j->nc]);
2619a747e4fSDavid du Colombier 	j->nc++;
2629a747e4fSDavid du Colombier }
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier static void
purgejar(Jar * j)2659a747e4fSDavid du Colombier purgejar(Jar *j)
2669a747e4fSDavid du Colombier {
2679a747e4fSDavid du Colombier 	int i;
2689a747e4fSDavid du Colombier 
2699a747e4fSDavid du Colombier 	for(i=j->nc-1; i>=0; i--){
2709a747e4fSDavid du Colombier 		if(!j->c[i].deleted)
2719a747e4fSDavid du Colombier 			continue;
2729a747e4fSDavid du Colombier 		freecookie(&j->c[i]);
2739a747e4fSDavid du Colombier 		--j->nc;
2749a747e4fSDavid du Colombier 		j->c[i] = j->c[j->nc];
2759a747e4fSDavid du Colombier 	}
2769a747e4fSDavid du Colombier }
2779a747e4fSDavid du Colombier 
2789a747e4fSDavid du Colombier static void
addtojar(Jar * jar,char * line,int ondisk)2799a747e4fSDavid du Colombier addtojar(Jar *jar, char *line, int ondisk)
2809a747e4fSDavid du Colombier {
2819a747e4fSDavid du Colombier 	Cookie c;
2829a747e4fSDavid du Colombier 	int i, j, nf, *pint;
2839a747e4fSDavid du Colombier 	char *f[20], *attr, *val, **pstr;
2849a747e4fSDavid du Colombier 
2859a747e4fSDavid du Colombier 	memset(&c, 0, sizeof c);
2869a747e4fSDavid du Colombier 	c.expire = ~0;
2879a747e4fSDavid du Colombier 	c.ondisk = ondisk;
2889a747e4fSDavid du Colombier 	nf = tokenize(line, f, nelem(f));
2899a747e4fSDavid du Colombier 	for(i=0; i<nf; i++){
2909a747e4fSDavid du Colombier 		attr = f[i];
2919a747e4fSDavid du Colombier 		if((val = strchr(attr, '=')) != nil)
2929a747e4fSDavid du Colombier 			*val++ = '\0';
2939a747e4fSDavid du Colombier 		else
2949a747e4fSDavid du Colombier 			val = "";
2959a747e4fSDavid du Colombier 		/* string attributes */
2969a747e4fSDavid du Colombier 		for(j=0; j<nelem(stab); j++){
2979a747e4fSDavid du Colombier 			if(strcmp(stab[j].s, attr) == 0){
2989dfc0cb2SDavid du Colombier 				pstr = (char**)((uintptr)&c+stab[j].offset);
2999a747e4fSDavid du Colombier 				*pstr = val;
3009a747e4fSDavid du Colombier 			}
3019a747e4fSDavid du Colombier 		}
3029a747e4fSDavid du Colombier 		/* integer attributes */
3039a747e4fSDavid du Colombier 		for(j=0; j<nelem(itab); j++){
3049a747e4fSDavid du Colombier 			if(strcmp(itab[j].s, attr) == 0){
3059dfc0cb2SDavid du Colombier 				pint = (int*)((uintptr)&c+itab[j].offset);
3069a747e4fSDavid du Colombier 				if(val[0]=='\0')
3079a747e4fSDavid du Colombier 					*pint = 1;
3089a747e4fSDavid du Colombier 				else
3099a747e4fSDavid du Colombier 					*pint = strtoul(val, 0, 0);
3109a747e4fSDavid du Colombier 			}
3119a747e4fSDavid du Colombier 		}
3129a747e4fSDavid du Colombier 	}
3139a747e4fSDavid du Colombier 	if(c.name==nil || c.value==nil || c.dom==nil || c.path==nil){
3149a747e4fSDavid du Colombier 		if(cookiedebug)
3159a747e4fSDavid du Colombier 			fprint(2, "ignoring fractional cookie %K\n", &c);
3169a747e4fSDavid du Colombier 		return;
3179a747e4fSDavid du Colombier 	}
3189a747e4fSDavid du Colombier 	addcookie(jar, &c);
3199a747e4fSDavid du Colombier }
3209a747e4fSDavid du Colombier 
3219a747e4fSDavid du Colombier static Jar*
newjar(void)3229a747e4fSDavid du Colombier newjar(void)
3239a747e4fSDavid du Colombier {
3249a747e4fSDavid du Colombier 	Jar *jar;
3259a747e4fSDavid du Colombier 
3269a747e4fSDavid du Colombier 	jar = emalloc9p(sizeof(Jar));
3279a747e4fSDavid du Colombier 	return jar;
3289a747e4fSDavid du Colombier }
3299a747e4fSDavid du Colombier 
3309a747e4fSDavid du Colombier static int
expirejar(Jar * jar,int exiting)3319a747e4fSDavid du Colombier expirejar(Jar *jar, int exiting)
3329a747e4fSDavid du Colombier {
3339a747e4fSDavid du Colombier 	int i, n;
3349a747e4fSDavid du Colombier 	uint now;
3359a747e4fSDavid du Colombier 
3369a747e4fSDavid du Colombier 	now = time(0);
3379a747e4fSDavid du Colombier 	n = 0;
3389a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++){
3399a747e4fSDavid du Colombier 		if(jar->c[i].expire < now || (exiting && jar->c[i].expire==~0)){
3409a747e4fSDavid du Colombier 			delcookie(jar, &jar->c[i]);
3419a747e4fSDavid du Colombier 			n++;
3429a747e4fSDavid du Colombier 		}
3439a747e4fSDavid du Colombier 	}
3449a747e4fSDavid du Colombier 	return n;
3459a747e4fSDavid du Colombier }
3469a747e4fSDavid du Colombier 
3479a747e4fSDavid du Colombier static void
dumpjar(Jar * jar,char * desc)3489a747e4fSDavid du Colombier dumpjar(Jar *jar, char *desc)
3499a747e4fSDavid du Colombier {
3509a747e4fSDavid du Colombier 	int i;
3519a747e4fSDavid du Colombier 	Biobuf *b;
3529a747e4fSDavid du Colombier 	char *s;
3539a747e4fSDavid du Colombier 
3549a747e4fSDavid du Colombier 	print("%s\n", desc);
3559a747e4fSDavid du Colombier 	print("\tin memory:\n");
3569a747e4fSDavid du Colombier 
3579a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)
3589a747e4fSDavid du Colombier 		print("\t%K%s%s%s\n", &jar->c[i],
3599a747e4fSDavid du Colombier 			jar->c[i].ondisk ? " ondisk" : "",
3609a747e4fSDavid du Colombier 			jar->c[i].deleted ? " deleted" : "",
3619a747e4fSDavid du Colombier 			jar->c[i].mark ? " mark" : "");
3629a747e4fSDavid du Colombier 	print("\n\ton disk:\n");
3639a747e4fSDavid du Colombier 	if((b = Bopen(jar->file, OREAD)) == nil){
3649a747e4fSDavid du Colombier 		print("\tno file\n");
3659a747e4fSDavid du Colombier 	}else{
3669a747e4fSDavid du Colombier 		while((s = Brdstr(b, '\n', 1)) != nil){
3679a747e4fSDavid du Colombier 			print("\t%s\n", s);
3689a747e4fSDavid du Colombier 			free(s);
3699a747e4fSDavid du Colombier 		}
3709a747e4fSDavid du Colombier 		Bterm(b);
3719a747e4fSDavid du Colombier 	}
3729a747e4fSDavid du Colombier 	print("\n");
3739a747e4fSDavid du Colombier }
3749a747e4fSDavid du Colombier 
3759a747e4fSDavid du Colombier static int
syncjar(Jar * jar)3769a747e4fSDavid du Colombier syncjar(Jar *jar)
3779a747e4fSDavid du Colombier {
3789a747e4fSDavid du Colombier 	int i, fd;
3799a747e4fSDavid du Colombier 	char *line;
3809a747e4fSDavid du Colombier 	Dir *d;
3819a747e4fSDavid du Colombier 	Biobuf *b;
3829a747e4fSDavid du Colombier 	Qid q;
3839a747e4fSDavid du Colombier 
3849a747e4fSDavid du Colombier 	if(jar->file==nil)
3859a747e4fSDavid du Colombier 		return 0;
3869a747e4fSDavid du Colombier 
3879a747e4fSDavid du Colombier 	memset(&q, 0, sizeof q);
3889a747e4fSDavid du Colombier 	if((d = dirstat(jar->file)) != nil){
3899a747e4fSDavid du Colombier 		q = d->qid;
3909a747e4fSDavid du Colombier 		if(d->qid.path != jar->qid.path || d->qid.vers != jar->qid.vers)
3919a747e4fSDavid du Colombier 			jar->dirty = 1;
3929a747e4fSDavid du Colombier 		free(d);
3939a747e4fSDavid du Colombier 	}
3949a747e4fSDavid du Colombier 
3959a747e4fSDavid du Colombier 	if(jar->dirty == 0)
3969a747e4fSDavid du Colombier 		return 0;
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier 	fd = -1;
3999a747e4fSDavid du Colombier 	for(i=0; i<50; i++){
4009a747e4fSDavid du Colombier 		if((fd = create(jar->lockfile, OWRITE, DMEXCL|0666)) < 0){
4019a747e4fSDavid du Colombier 			sleep(100);
4029a747e4fSDavid du Colombier 			continue;
4039a747e4fSDavid du Colombier 		}
4049a747e4fSDavid du Colombier 		break;
4059a747e4fSDavid du Colombier 	}
4069a747e4fSDavid du Colombier 	if(fd < 0){
4079a747e4fSDavid du Colombier 		if(cookiedebug)
4089a747e4fSDavid du Colombier 			fprint(2, "open %s: %r", jar->lockfile);
4099a747e4fSDavid du Colombier 		werrstr("cannot acquire jar lock: %r");
4109a747e4fSDavid du Colombier 		return -1;
4119a747e4fSDavid du Colombier 	}
4129a747e4fSDavid du Colombier 
4139a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)	/* mark is cleared by addcookie */
4149a747e4fSDavid du Colombier 		jar->c[i].mark = jar->c[i].ondisk;
4159a747e4fSDavid du Colombier 
4169a747e4fSDavid du Colombier 	if((b = Bopen(jar->file, OREAD)) == nil){
4179a747e4fSDavid du Colombier 		if(cookiedebug)
4189a747e4fSDavid du Colombier 			fprint(2, "Bopen %s: %r", jar->file);
4199a747e4fSDavid du Colombier 		werrstr("cannot read cookie file %s: %r", jar->file);
4209a747e4fSDavid du Colombier 		close(fd);
4219a747e4fSDavid du Colombier 		return -1;
4229a747e4fSDavid du Colombier 	}
4239a747e4fSDavid du Colombier 	for(; (line = Brdstr(b, '\n', 1)) != nil; free(line)){
4249a747e4fSDavid du Colombier 		if(*line == '#')
4259a747e4fSDavid du Colombier 			continue;
4269a747e4fSDavid du Colombier 		addtojar(jar, line, 1);
4279a747e4fSDavid du Colombier 	}
4289a747e4fSDavid du Colombier 	Bterm(b);
4299a747e4fSDavid du Colombier 
4309a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)
4319a747e4fSDavid du Colombier 		if(jar->c[i].mark && jar->c[i].expire != ~0)
4329a747e4fSDavid du Colombier 			delcookie(jar, &jar->c[i]);
4339a747e4fSDavid du Colombier 
4349a747e4fSDavid du Colombier 	purgejar(jar);
4359a747e4fSDavid du Colombier 
4369a747e4fSDavid du Colombier 	b = Bopen(jar->file, OWRITE);
4379a747e4fSDavid du Colombier 	if(b == nil){
4389a747e4fSDavid du Colombier 		if(cookiedebug)
4399a747e4fSDavid du Colombier 			fprint(2, "Bopen write %s: %r", jar->file);
4409a747e4fSDavid du Colombier 		close(fd);
4419a747e4fSDavid du Colombier 		return -1;
4429a747e4fSDavid du Colombier 	}
4439a747e4fSDavid du Colombier 	Bprint(b, "# webcookies cookie jar\n");
4449a747e4fSDavid du Colombier 	Bprint(b, "# comments and non-standard fields will be lost\n");
4459a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++){
4469a747e4fSDavid du Colombier 		if(jar->c[i].expire == ~0)
4479a747e4fSDavid du Colombier 			continue;
4489a747e4fSDavid du Colombier 		Bprint(b, "%K\n", &jar->c[i]);
4499a747e4fSDavid du Colombier 		jar->c[i].ondisk = 1;
4509a747e4fSDavid du Colombier 	}
4519a747e4fSDavid du Colombier 	Bterm(b);
4529a747e4fSDavid du Colombier 
4539a747e4fSDavid du Colombier 	jar->dirty = 0;
4549a747e4fSDavid du Colombier 	close(fd);
455220e960cSDavid du Colombier 	if((d = dirstat(jar->file)) != nil){
456220e960cSDavid du Colombier 		jar->qid = d->qid;
457220e960cSDavid du Colombier 		free(d);
458220e960cSDavid du Colombier 	}
4599a747e4fSDavid du Colombier 	return 0;
4609a747e4fSDavid du Colombier }
4619a747e4fSDavid du Colombier 
4629a747e4fSDavid du Colombier static Jar*
readjar(char * file)4639a747e4fSDavid du Colombier readjar(char *file)
4649a747e4fSDavid du Colombier {
4659a747e4fSDavid du Colombier 	char *lock, *p;
4669a747e4fSDavid du Colombier 	Jar *jar;
4679a747e4fSDavid du Colombier 
4689a747e4fSDavid du Colombier 	jar = newjar();
4699a747e4fSDavid du Colombier 	lock = emalloc9p(strlen(file)+10);
4709a747e4fSDavid du Colombier 	strcpy(lock, file);
4719a747e4fSDavid du Colombier 	if((p = strrchr(lock, '/')) != nil)
4729a747e4fSDavid du Colombier 		p++;
4739a747e4fSDavid du Colombier 	else
4749a747e4fSDavid du Colombier 		p = lock;
4759a747e4fSDavid du Colombier 	memmove(p+2, p, strlen(p)+1);
4769a747e4fSDavid du Colombier 	p[0] = 'L';
4779a747e4fSDavid du Colombier 	p[1] = '.';
4789a747e4fSDavid du Colombier 	jar->lockfile = lock;
4799a747e4fSDavid du Colombier 	jar->file = file;
4809a747e4fSDavid du Colombier 	jar->dirty = 1;
4819a747e4fSDavid du Colombier 
4829a747e4fSDavid du Colombier 	if(syncjar(jar) < 0){
4839a747e4fSDavid du Colombier 		free(jar->file);
4849a747e4fSDavid du Colombier 		free(jar->lockfile);
4859a747e4fSDavid du Colombier 		free(jar);
4869a747e4fSDavid du Colombier 		return nil;
4879a747e4fSDavid du Colombier 	}
4889a747e4fSDavid du Colombier 	return jar;
4899a747e4fSDavid du Colombier }
4909a747e4fSDavid du Colombier 
4919a747e4fSDavid du Colombier static void
closejar(Jar * jar)4929a747e4fSDavid du Colombier closejar(Jar *jar)
4939a747e4fSDavid du Colombier {
4949a747e4fSDavid du Colombier 	int i;
4959a747e4fSDavid du Colombier 
4969a747e4fSDavid du Colombier 	if(jar == nil)
4979a747e4fSDavid du Colombier 		return;
4989a747e4fSDavid du Colombier 	expirejar(jar, 0);
4999a747e4fSDavid du Colombier 	if(syncjar(jar) < 0)
5009a747e4fSDavid du Colombier 		fprint(2, "warning: cannot rewrite cookie jar: %r\n");
5019a747e4fSDavid du Colombier 
5029a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)
5039a747e4fSDavid du Colombier 		freecookie(&jar->c[i]);
5049a747e4fSDavid du Colombier 
5059a747e4fSDavid du Colombier 	free(jar->file);
5069a747e4fSDavid du Colombier 	free(jar);
5079a747e4fSDavid du Colombier }
5089a747e4fSDavid du Colombier 
5099a747e4fSDavid du Colombier /*
5109a747e4fSDavid du Colombier  * Domain name matching is per RFC2109, section 2:
5119a747e4fSDavid du Colombier  *
5129a747e4fSDavid du Colombier  * Hosts names can be specified either as an IP address or a FQHN
5139a747e4fSDavid du Colombier  * string.  Sometimes we compare one host name with another.  Host A's
5149a747e4fSDavid du Colombier  * name domain-matches host B's if
5159a747e4fSDavid du Colombier  *
5169a747e4fSDavid du Colombier  * * both host names are IP addresses and their host name strings match
5179a747e4fSDavid du Colombier  *   exactly; or
5189a747e4fSDavid du Colombier  *
5199a747e4fSDavid du Colombier  * * both host names are FQDN strings and their host name strings match
5209a747e4fSDavid du Colombier  *   exactly; or
5219a747e4fSDavid du Colombier  *
5229a747e4fSDavid du Colombier  * * A is a FQDN string and has the form NB, where N is a non-empty name
5239a747e4fSDavid du Colombier  *   string, B has the form .B', and B' is a FQDN string.  (So, x.y.com
5249a747e4fSDavid du Colombier  *   domain-matches .y.com but not y.com.)
5259a747e4fSDavid du Colombier  *
5269a747e4fSDavid du Colombier  * Note that domain-match is not a commutative operation: a.b.c.com
5279a747e4fSDavid du Colombier  * domain-matches .c.com, but not the reverse.
5289a747e4fSDavid du Colombier  *
5299a747e4fSDavid du Colombier  * (This does not verify that IP addresses and FQDN's are well-formed.)
5309a747e4fSDavid du Colombier  */
5319a747e4fSDavid du Colombier static int
isdomainmatch(char * name,char * pattern)5329a747e4fSDavid du Colombier isdomainmatch(char *name, char *pattern)
5339a747e4fSDavid du Colombier {
5349a747e4fSDavid du Colombier 	int lname, lpattern;
5359a747e4fSDavid du Colombier 
5369a747e4fSDavid du Colombier 	if(cistrcmp(name, pattern)==0)
5379a747e4fSDavid du Colombier 		return 1;
5389a747e4fSDavid du Colombier 
5399a747e4fSDavid du Colombier 	if(strcmp(ipattr(name), "dom")==0 && pattern[0]=='.'){
5409a747e4fSDavid du Colombier 		lname = strlen(name);
5419a747e4fSDavid du Colombier 		lpattern = strlen(pattern);
5423b86f2f8SDavid du Colombier 		/* e.g., name: www.google.com && pattern: .google.com */
5439a747e4fSDavid du Colombier 		if(lname >= lpattern && cistrcmp(name+lname-lpattern, pattern)==0)
5449a747e4fSDavid du Colombier 			return 1;
5453b86f2f8SDavid du Colombier 		/* e.g., name: google.com && pattern: .google.com */
5463b86f2f8SDavid du Colombier 		if(lpattern > lname &&
5473b86f2f8SDavid du Colombier 		    cistrcmp(pattern+lpattern-lname, name) == 0)
5483b86f2f8SDavid du Colombier 			return 1;
5499a747e4fSDavid du Colombier 	}
5509a747e4fSDavid du Colombier 	return 0;
5519a747e4fSDavid du Colombier }
5529a747e4fSDavid du Colombier 
5539a747e4fSDavid du Colombier /*
5549a747e4fSDavid du Colombier  * RFC2109 4.3.4:
5559a747e4fSDavid du Colombier  *	- domain must match
5569a747e4fSDavid du Colombier  *	- path in cookie must be a prefix of request path
5579a747e4fSDavid du Colombier  *	- cookie must not have expired
5589a747e4fSDavid du Colombier  */
5599a747e4fSDavid du Colombier static int
iscookiematch(Cookie * c,char * dom,char * path,uint now)5609a747e4fSDavid du Colombier iscookiematch(Cookie *c, char *dom, char *path, uint now)
5619a747e4fSDavid du Colombier {
5629a747e4fSDavid du Colombier 	return isdomainmatch(dom, c->dom)
5639a747e4fSDavid du Colombier 		&& strncmp(c->path, path, strlen(c->path))==0
5643b86f2f8SDavid du Colombier 		&& (c->expire == 0 || c->expire >= now);
5659a747e4fSDavid du Colombier }
5669a747e4fSDavid du Colombier 
5679a747e4fSDavid du Colombier /*
5689a747e4fSDavid du Colombier  * Produce a subjar of matching cookies.
5699a747e4fSDavid du Colombier  * Secure cookies are only included if secure is set.
5709a747e4fSDavid du Colombier  */
5719a747e4fSDavid du Colombier static Jar*
cookiesearch(Jar * jar,char * dom,char * path,int issecure)5729a747e4fSDavid du Colombier cookiesearch(Jar *jar, char *dom, char *path, int issecure)
5739a747e4fSDavid du Colombier {
5749a747e4fSDavid du Colombier 	int i;
5759a747e4fSDavid du Colombier 	Jar *j;
5769a747e4fSDavid du Colombier 	uint now;
5779a747e4fSDavid du Colombier 
5789a747e4fSDavid du Colombier 	if(cookiedebug)
5799a747e4fSDavid du Colombier 		fprint(2, "cookiesearch %s %s %d\n", dom, path, issecure);
5809a747e4fSDavid du Colombier 	now = time(0);
5819a747e4fSDavid du Colombier 	j = newjar();
5829a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++){
5839a747e4fSDavid du Colombier 		if(cookiedebug)
5843b86f2f8SDavid du Colombier 			fprint(2, "\ttry %s %s %d %s\n", jar->c[i].dom,
5853b86f2f8SDavid du Colombier 				jar->c[i].path, jar->c[i].secure,
5863b86f2f8SDavid du Colombier 				jar->c[i].name);
587*2c23bbf7SDavid du Colombier 		if((issecure || !jar->c[i].secure) &&
5883b86f2f8SDavid du Colombier 		    iscookiematch(&jar->c[i], dom, path, now)){
5899a747e4fSDavid du Colombier 			if(cookiedebug)
5909a747e4fSDavid du Colombier 				fprint(2, "\tmatched\n");
5919a747e4fSDavid du Colombier 			addcookie(j, &jar->c[i]);
5929a747e4fSDavid du Colombier 		}
5939a747e4fSDavid du Colombier 	}
5949a747e4fSDavid du Colombier 	if(j->nc == 0){
5959a747e4fSDavid du Colombier 		closejar(j);
5969a747e4fSDavid du Colombier 		werrstr("no cookies found");
5979a747e4fSDavid du Colombier 		return nil;
5989a747e4fSDavid du Colombier 	}
5993b86f2f8SDavid du Colombier 	qsort(j->c, j->nc, sizeof(j->c[0]), (int(*)(void*, void*))cookiecmp);
6009a747e4fSDavid du Colombier 	return j;
6019a747e4fSDavid du Colombier }
6029a747e4fSDavid du Colombier 
6039a747e4fSDavid du Colombier /*
6049a747e4fSDavid du Colombier  * RFC2109 4.3.2 security checks
6059a747e4fSDavid du Colombier  */
6069a747e4fSDavid du Colombier static char*
isbadcookie(Cookie * c,char * dom,char * path)6079a747e4fSDavid du Colombier isbadcookie(Cookie *c, char *dom, char *path)
6089a747e4fSDavid du Colombier {
6093b86f2f8SDavid du Colombier 	int lcdom, ldom;
6103b86f2f8SDavid du Colombier 
6119a747e4fSDavid du Colombier 	if(strncmp(c->path, path, strlen(c->path)) != 0)
6129a747e4fSDavid du Colombier 		return "cookie path is not a prefix of the request path";
6139a747e4fSDavid du Colombier 
6143b86f2f8SDavid du Colombier 	/*
6153b86f2f8SDavid du Colombier 	 * fgb says omitting this test is necessary to get some sites to work,
6163b86f2f8SDavid du Colombier 	 * but it seems dubious.
6173b86f2f8SDavid du Colombier 	 */
618220e960cSDavid du Colombier 	if(c->explicitdom && c->dom[0] != '.')
6199a747e4fSDavid du Colombier 		return "cookie domain doesn't start with dot";
6209a747e4fSDavid du Colombier 
6213b86f2f8SDavid du Colombier 	lcdom = strlen(c->dom);
6223b86f2f8SDavid du Colombier 	if(memchr(c->dom+1, '.', lcdom-1-1) == nil)
6239a747e4fSDavid du Colombier 		return "cookie domain doesn't have embedded dots";
6249a747e4fSDavid du Colombier 
6259a747e4fSDavid du Colombier 	if(!isdomainmatch(dom, c->dom))
6269a747e4fSDavid du Colombier 		return "request host does not match cookie domain";
6279a747e4fSDavid du Colombier 
6283b86f2f8SDavid du Colombier 	ldom = strlen(dom);
6293b86f2f8SDavid du Colombier 	if(strcmp(ipattr(dom), "dom")==0 && lcdom > ldom &&
6303b86f2f8SDavid du Colombier 	    memchr(dom, '.', lcdom - ldom) != nil)
6319a747e4fSDavid du Colombier 		return "request host contains dots before cookie domain";
6329a747e4fSDavid du Colombier 
6339a747e4fSDavid du Colombier 	return 0;
6349a747e4fSDavid du Colombier }
6359a747e4fSDavid du Colombier 
6369a747e4fSDavid du Colombier /*
6379a747e4fSDavid du Colombier  * Sunday, 25-Jan-2002 12:24:36 GMT
6389a747e4fSDavid du Colombier  * Sunday, 25 Jan 2002 12:24:36 GMT
6399a747e4fSDavid du Colombier  * Sun, 25 Jan 02 12:24:36 GMT
6409a747e4fSDavid du Colombier  */
6419a747e4fSDavid du Colombier static int
isleap(int year)6429a747e4fSDavid du Colombier isleap(int year)
6439a747e4fSDavid du Colombier {
6449a747e4fSDavid du Colombier 	return year%4==0 && (year%100!=0 || year%400==0);
6459a747e4fSDavid du Colombier }
6469a747e4fSDavid du Colombier 
6479a747e4fSDavid du Colombier static uint
strtotime(char * s)6489a747e4fSDavid du Colombier strtotime(char *s)
6499a747e4fSDavid du Colombier {
6509a747e4fSDavid du Colombier 	char *os;
6519a747e4fSDavid du Colombier 	int i;
6529a747e4fSDavid du Colombier 	Tm tm;
6539a747e4fSDavid du Colombier 
6549a747e4fSDavid du Colombier 	static int mday[2][12] = {
6559a747e4fSDavid du Colombier 		31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
6569a747e4fSDavid du Colombier 		31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
6579a747e4fSDavid du Colombier 	};
6589a747e4fSDavid du Colombier 	static char *wday[] = {
6599a747e4fSDavid du Colombier 		"Sunday", "Monday", "Tuesday", "Wednesday",
6609a747e4fSDavid du Colombier 		"Thursday", "Friday", "Saturday",
6619a747e4fSDavid du Colombier 	};
6629a747e4fSDavid du Colombier 	static char *mon[] = {
6639a747e4fSDavid du Colombier 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
6649a747e4fSDavid du Colombier 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
6659a747e4fSDavid du Colombier 	};
6669a747e4fSDavid du Colombier 
6679a747e4fSDavid du Colombier 	os = s;
6689a747e4fSDavid du Colombier 	/* Sunday, */
6699a747e4fSDavid du Colombier 	for(i=0; i<nelem(wday); i++){
6709a747e4fSDavid du Colombier 		if(cistrncmp(s, wday[i], strlen(wday[i])) == 0){
6719a747e4fSDavid du Colombier 			s += strlen(wday[i]);
6729a747e4fSDavid du Colombier 			break;
6739a747e4fSDavid du Colombier 		}
6749a747e4fSDavid du Colombier 		if(cistrncmp(s, wday[i], 3) == 0){
6759a747e4fSDavid du Colombier 			s += 3;
6769a747e4fSDavid du Colombier 			break;
6779a747e4fSDavid du Colombier 		}
6789a747e4fSDavid du Colombier 	}
6799a747e4fSDavid du Colombier 	if(i==nelem(wday)){
6809a747e4fSDavid du Colombier 		if(cookiedebug)
6819a747e4fSDavid du Colombier 			fprint(2, "bad wday (%s)\n", os);
6829a747e4fSDavid du Colombier 		return -1;
6839a747e4fSDavid du Colombier 	}
6849a747e4fSDavid du Colombier 	if(*s++ != ',' || *s++ != ' '){
6859a747e4fSDavid du Colombier 		if(cookiedebug)
6869a747e4fSDavid du Colombier 			fprint(2, "bad wday separator (%s)\n", os);
6879a747e4fSDavid du Colombier 		return -1;
6889a747e4fSDavid du Colombier 	}
6899a747e4fSDavid du Colombier 
6909a747e4fSDavid du Colombier 	/* 25- */
6919a747e4fSDavid du Colombier 	if(!isdigit(s[0]) || !isdigit(s[1]) || (s[2]!='-' && s[2]!=' ')){
6929a747e4fSDavid du Colombier 		if(cookiedebug)
6939a747e4fSDavid du Colombier 			fprint(2, "bad day of month (%s)\n", os);
6949a747e4fSDavid du Colombier 		return -1;
6959a747e4fSDavid du Colombier 	}
6969a747e4fSDavid du Colombier 	tm.mday = strtol(s, 0, 10);
6979a747e4fSDavid du Colombier 	s += 3;
6989a747e4fSDavid du Colombier 
6999a747e4fSDavid du Colombier 	/* Jan- */
7009a747e4fSDavid du Colombier 	for(i=0; i<nelem(mon); i++)
7019a747e4fSDavid du Colombier 		if(cistrncmp(s, mon[i], 3) == 0){
7029a747e4fSDavid du Colombier 			tm.mon = i;
7039a747e4fSDavid du Colombier 			s += 3;
7049a747e4fSDavid du Colombier 			break;
7059a747e4fSDavid du Colombier 		}
7069a747e4fSDavid du Colombier 	if(i==nelem(mon)){
7079a747e4fSDavid du Colombier 		if(cookiedebug)
7089a747e4fSDavid du Colombier 			fprint(2, "bad month (%s)\n", os);
7099a747e4fSDavid du Colombier 		return -1;
7109a747e4fSDavid du Colombier 	}
7119a747e4fSDavid du Colombier 	if(s[0] != '-' && s[0] != ' '){
7129a747e4fSDavid du Colombier 		if(cookiedebug)
7139a747e4fSDavid du Colombier 			fprint(2, "bad month separator (%s)\n", os);
7149a747e4fSDavid du Colombier 		return -1;
7159a747e4fSDavid du Colombier 	}
7169a747e4fSDavid du Colombier 	s++;
7179a747e4fSDavid du Colombier 
7189a747e4fSDavid du Colombier 	/* 2002 */
7199a747e4fSDavid du Colombier 	if(!isdigit(s[0]) || !isdigit(s[1])){
7209a747e4fSDavid du Colombier 		if(cookiedebug)
7219a747e4fSDavid du Colombier 			fprint(2, "bad year (%s)\n", os);
7229a747e4fSDavid du Colombier 		return -1;
7239a747e4fSDavid du Colombier 	}
7249a747e4fSDavid du Colombier 	tm.year = strtol(s, 0, 10);
7259a747e4fSDavid du Colombier 	s += 2;
7269a747e4fSDavid du Colombier 	if(isdigit(s[0]) && isdigit(s[1]))
7279a747e4fSDavid du Colombier 		s += 2;
7289a747e4fSDavid du Colombier 	else{
7299a747e4fSDavid du Colombier 		if(tm.year <= 68)
7309a747e4fSDavid du Colombier 			tm.year += 2000;
7319a747e4fSDavid du Colombier 		else
7329a747e4fSDavid du Colombier 			tm.year += 1900;
7339a747e4fSDavid du Colombier 	}
7349a747e4fSDavid du Colombier 	if(tm.mday==0 || tm.mday > mday[isleap(tm.year)][tm.mon]){
7359a747e4fSDavid du Colombier 		if(cookiedebug)
7369a747e4fSDavid du Colombier 			fprint(2, "invalid day of month (%s)\n", os);
7379a747e4fSDavid du Colombier 		return -1;
7389a747e4fSDavid du Colombier 	}
7399a747e4fSDavid du Colombier 	tm.year -= 1900;
7409a747e4fSDavid du Colombier 	if(*s++ != ' '){
7419a747e4fSDavid du Colombier 		if(cookiedebug)
7429a747e4fSDavid du Colombier 			fprint(2, "bad year separator (%s)\n", os);
7439a747e4fSDavid du Colombier 		return -1;
7449a747e4fSDavid du Colombier 	}
7459a747e4fSDavid du Colombier 
7469a747e4fSDavid du Colombier 	if(!isdigit(s[0]) || !isdigit(s[1]) || s[2]!=':'
7479a747e4fSDavid du Colombier 	|| !isdigit(s[3]) || !isdigit(s[4]) || s[5]!=':'
7489a747e4fSDavid du Colombier 	|| !isdigit(s[6]) || !isdigit(s[7]) || s[8]!=' '){
7499a747e4fSDavid du Colombier 		if(cookiedebug)
7509a747e4fSDavid du Colombier 			fprint(2, "bad time (%s)\n", os);
7519a747e4fSDavid du Colombier 		return -1;
7529a747e4fSDavid du Colombier 	}
7539a747e4fSDavid du Colombier 
7549a747e4fSDavid du Colombier 	tm.hour = atoi(s);
7559a747e4fSDavid du Colombier 	tm.min = atoi(s+3);
7569a747e4fSDavid du Colombier 	tm.sec = atoi(s+6);
7579a747e4fSDavid du Colombier 	if(tm.hour >= 24 || tm.min >= 60 || tm.sec >= 60){
7589a747e4fSDavid du Colombier 		if(cookiedebug)
7599a747e4fSDavid du Colombier 			fprint(2, "invalid time (%s)\n", os);
7609a747e4fSDavid du Colombier 		return -1;
7619a747e4fSDavid du Colombier 	}
7629a747e4fSDavid du Colombier 	s += 9;
7639a747e4fSDavid du Colombier 
7649a747e4fSDavid du Colombier 	if(cistrcmp(s, "GMT") != 0){
7659a747e4fSDavid du Colombier 		if(cookiedebug)
7669a747e4fSDavid du Colombier 			fprint(2, "time zone not GMT (%s)\n", os);
7679a747e4fSDavid du Colombier 		return -1;
7689a747e4fSDavid du Colombier 	}
7699a747e4fSDavid du Colombier 	strcpy(tm.zone, "GMT");
7706b6b9ac8SDavid du Colombier 	tm.yday = 0;
7719a747e4fSDavid du Colombier 	return tm2sec(&tm);
7729a747e4fSDavid du Colombier }
7739a747e4fSDavid du Colombier 
7749a747e4fSDavid du Colombier /*
7759a747e4fSDavid du Colombier  * skip linear whitespace.  we're a bit more lenient than RFC2616 2.2.
7769a747e4fSDavid du Colombier  */
7779a747e4fSDavid du Colombier static char*
skipspace(char * s)7789a747e4fSDavid du Colombier skipspace(char *s)
7799a747e4fSDavid du Colombier {
7809a747e4fSDavid du Colombier 	while(*s=='\r' || *s=='\n' || *s==' ' || *s=='\t')
7819a747e4fSDavid du Colombier 		s++;
7829a747e4fSDavid du Colombier 	return s;
7839a747e4fSDavid du Colombier }
7849a747e4fSDavid du Colombier 
7859a747e4fSDavid du Colombier /*
7869a747e4fSDavid du Colombier  * Try to identify old netscape headers.
7879a747e4fSDavid du Colombier  * The old headers:
7889a747e4fSDavid du Colombier  *	- didn't allow spaces around the '='
7899a747e4fSDavid du Colombier  *	- used an 'Expires' attribute
7909a747e4fSDavid du Colombier  *	- had no 'Version' attribute
7919a747e4fSDavid du Colombier  *	- had no quotes
7929a747e4fSDavid du Colombier  *	- allowed whitespace in values
7939a747e4fSDavid du Colombier  *	- apparently separated attr/value pairs with ';' exclusively
7949a747e4fSDavid du Colombier  */
7959a747e4fSDavid du Colombier static int
isnetscape(char * hdr)7969a747e4fSDavid du Colombier isnetscape(char *hdr)
7979a747e4fSDavid du Colombier {
7989a747e4fSDavid du Colombier 	char *s;
7999a747e4fSDavid du Colombier 
8009a747e4fSDavid du Colombier 	for(s=hdr; (s=strchr(s, '=')) != nil; s++){
8019a747e4fSDavid du Colombier 		if(isspace(s[1]) || (s > hdr && isspace(s[-1])))
8029a747e4fSDavid du Colombier 			return 0;
8039a747e4fSDavid du Colombier 		if(s[1]=='"')
8049a747e4fSDavid du Colombier 			return 0;
8059a747e4fSDavid du Colombier 	}
8069a747e4fSDavid du Colombier 	if(cistrstr(hdr, "version="))
8079a747e4fSDavid du Colombier 		return 0;
8089a747e4fSDavid du Colombier 	return 1;
8099a747e4fSDavid du Colombier }
8109a747e4fSDavid du Colombier 
8119a747e4fSDavid du Colombier /*
8129a747e4fSDavid du Colombier  * Parse HTTP response headers, adding cookies to jar.
813220e960cSDavid du Colombier  * Overwrites the headers.  May overwrite path.
8149a747e4fSDavid du Colombier  */
8159a747e4fSDavid du Colombier static char* parsecookie(Cookie*, char*, char**, int, char*, char*);
8169a747e4fSDavid du Colombier static int
parsehttp(Jar * jar,char * hdr,char * dom,char * path)8179a747e4fSDavid du Colombier parsehttp(Jar *jar, char *hdr, char *dom, char *path)
8189a747e4fSDavid du Colombier {
8199a747e4fSDavid du Colombier 	static char setcookie[] = "Set-Cookie:";
8209a747e4fSDavid du Colombier 	char *e, *p, *nextp;
8219a747e4fSDavid du Colombier 	Cookie c;
8229a747e4fSDavid du Colombier 	int isns, n;
8239a747e4fSDavid du Colombier 
8249a747e4fSDavid du Colombier 	isns = isnetscape(hdr);
8259a747e4fSDavid du Colombier 	n = 0;
8269a747e4fSDavid du Colombier 	for(p=hdr; p; p=nextp){
8279a747e4fSDavid du Colombier 		p = skipspace(p);
8289a747e4fSDavid du Colombier 		if(*p == '\0')
8299a747e4fSDavid du Colombier 			break;
8309a747e4fSDavid du Colombier 		nextp = strchr(p, '\n');
8319a747e4fSDavid du Colombier 		if(nextp != nil)
8329a747e4fSDavid du Colombier 			*nextp++ = '\0';
8339a747e4fSDavid du Colombier 		if(cistrncmp(p, setcookie, strlen(setcookie)) != 0)
8349a747e4fSDavid du Colombier 			continue;
8359a747e4fSDavid du Colombier 		if(cookiedebug)
8369a747e4fSDavid du Colombier 			fprint(2, "%s\n", p);
8379a747e4fSDavid du Colombier 		p = skipspace(p+strlen(setcookie));
8389a747e4fSDavid du Colombier 		for(; *p; p=skipspace(p)){
8399a747e4fSDavid du Colombier 			if((e = parsecookie(&c, p, &p, isns, dom, path)) != nil){
8409a747e4fSDavid du Colombier 				if(cookiedebug)
8419a747e4fSDavid du Colombier 					fprint(2, "parse cookie: %s\n", e);
8429a747e4fSDavid du Colombier 				break;
8439a747e4fSDavid du Colombier 			}
8449a747e4fSDavid du Colombier 			if((e = isbadcookie(&c, dom, path)) != nil){
8459a747e4fSDavid du Colombier 				if(cookiedebug)
8469a747e4fSDavid du Colombier 					fprint(2, "reject cookie; %s\n", e);
8479a747e4fSDavid du Colombier 				continue;
8489a747e4fSDavid du Colombier 			}
8499a747e4fSDavid du Colombier 			addcookie(jar, &c);
8509a747e4fSDavid du Colombier 			n++;
8519a747e4fSDavid du Colombier 		}
8529a747e4fSDavid du Colombier 	}
8539a747e4fSDavid du Colombier 	return n;
8549a747e4fSDavid du Colombier }
8559a747e4fSDavid du Colombier 
8569a747e4fSDavid du Colombier static char*
skipquoted(char * s)8579a747e4fSDavid du Colombier skipquoted(char *s)
8589a747e4fSDavid du Colombier {
8599a747e4fSDavid du Colombier 	/*
8609a747e4fSDavid du Colombier 	 * Sec 2.2 of RFC2616 defines a "quoted-string" as:
8619a747e4fSDavid du Colombier 	 *
8629a747e4fSDavid du Colombier 	 *  quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
8639a747e4fSDavid du Colombier 	 *  qdtext         = <any TEXT except <">>
8649a747e4fSDavid du Colombier 	 *  quoted-pair    = "\" CHAR
8659a747e4fSDavid du Colombier 	 *
8669a747e4fSDavid du Colombier 	 * TEXT is any octet except CTLs, but including LWS;
8679a747e4fSDavid du Colombier 	 * LWS is [CR LF] 1*(SP | HT);
8689a747e4fSDavid du Colombier 	 * CHARs are ASCII octets 0-127;  (NOTE: we reject 0's)
8699a747e4fSDavid du Colombier 	 * CTLs are octets 0-31 and 127;
8709a747e4fSDavid du Colombier 	 */
8719a747e4fSDavid du Colombier 	if(*s != '"')
8729a747e4fSDavid du Colombier 		return s;
8739a747e4fSDavid du Colombier 
8749a747e4fSDavid du Colombier 	for(s++; 32 <= *s && *s < 127 && *s != '"'; s++)
8759a747e4fSDavid du Colombier 		if(*s == '\\' && *(s+1) != '\0')
8769a747e4fSDavid du Colombier 			s++;
8779a747e4fSDavid du Colombier 	return s;
8789a747e4fSDavid du Colombier }
8799a747e4fSDavid du Colombier 
8809a747e4fSDavid du Colombier static char*
skiptoken(char * s)8819a747e4fSDavid du Colombier skiptoken(char *s)
8829a747e4fSDavid du Colombier {
8839a747e4fSDavid du Colombier 	/*
8849a747e4fSDavid du Colombier 	 * Sec 2.2 of RFC2616 defines a "token" as
8859a747e4fSDavid du Colombier  	 *  1*<any CHAR except CTLs or separators>;
8869a747e4fSDavid du Colombier 	 * CHARs are ASCII octets 0-127;
8879a747e4fSDavid du Colombier 	 * CTLs are octets 0-31 and 127;
8889a747e4fSDavid du Colombier 	 * separators are "()<>@,;:\/[]?={}", double-quote, SP (32), and HT (9)
8899a747e4fSDavid du Colombier 	 */
8909a747e4fSDavid du Colombier 	while(32 <= *s && *s < 127 && strchr("()<>@,;:[]?={}\" \t\\", *s)==nil)
8919a747e4fSDavid du Colombier 		s++;
8929a747e4fSDavid du Colombier 
8939a747e4fSDavid du Colombier 	return s;
8949a747e4fSDavid du Colombier }
8959a747e4fSDavid du Colombier 
8969a747e4fSDavid du Colombier static char*
skipvalue(char * s,int isns)8979a747e4fSDavid du Colombier skipvalue(char *s, int isns)
8989a747e4fSDavid du Colombier {
8999a747e4fSDavid du Colombier 	char *t;
9009a747e4fSDavid du Colombier 
9019a747e4fSDavid du Colombier 	/*
9029a747e4fSDavid du Colombier 	 * An RFC2109 value is an HTTP token or an HTTP quoted string.
9039a747e4fSDavid du Colombier 	 * Netscape servers ignore the spec and rely on semicolons, apparently.
9049a747e4fSDavid du Colombier 	 */
9059a747e4fSDavid du Colombier 	if(isns){
9069a747e4fSDavid du Colombier 		if((t = strchr(s, ';')) == nil)
9079a747e4fSDavid du Colombier 			t = s+strlen(s);
9089a747e4fSDavid du Colombier 		return t;
9099a747e4fSDavid du Colombier 	}
9109a747e4fSDavid du Colombier 	if(*s == '"')
9119a747e4fSDavid du Colombier 		return skipquoted(s);
9129a747e4fSDavid du Colombier 	return skiptoken(s);
9139a747e4fSDavid du Colombier }
9149a747e4fSDavid du Colombier 
9159a747e4fSDavid du Colombier /*
9169a747e4fSDavid du Colombier  * RMID=80b186bb64c03c65fab767f8; expires=Monday, 10-Feb-2003 04:44:39 GMT;
9179a747e4fSDavid du Colombier  *	path=/; domain=.nytimes.com
9189a747e4fSDavid du Colombier  */
9199a747e4fSDavid du Colombier static char*
parsecookie(Cookie * c,char * p,char ** e,int isns,char * dom,char * path)9209a747e4fSDavid du Colombier parsecookie(Cookie *c, char *p, char **e, int isns, char *dom, char *path)
9219a747e4fSDavid du Colombier {
9229a747e4fSDavid du Colombier 	int i, done;
9239a747e4fSDavid du Colombier 	char *t, *u, *attr, *val;
9249a747e4fSDavid du Colombier 
925220e960cSDavid du Colombier 	c->expire = ~0;
9269a747e4fSDavid du Colombier 	memset(c, 0, sizeof *c);
9279a747e4fSDavid du Colombier 
9289a747e4fSDavid du Colombier 	/* NAME=VALUE */
9299a747e4fSDavid du Colombier 	t = skiptoken(p);
9309a747e4fSDavid du Colombier 	c->name = p;
9319a747e4fSDavid du Colombier 	p = skipspace(t);
9329a747e4fSDavid du Colombier 	if(*p != '='){
9339a747e4fSDavid du Colombier 	Badname:
9349a747e4fSDavid du Colombier 		return "malformed cookie: no NAME=VALUE";
9359a747e4fSDavid du Colombier 	}
9369a747e4fSDavid du Colombier 	*t = '\0';
9379a747e4fSDavid du Colombier 	p = skipspace(p+1);
9389a747e4fSDavid du Colombier 	t = skipvalue(p, isns);
9399a747e4fSDavid du Colombier 	if(*t)
9409a747e4fSDavid du Colombier 		*t++ = '\0';
9419a747e4fSDavid du Colombier 	c->value = p;
9429a747e4fSDavid du Colombier 	p = skipspace(t);
9439a747e4fSDavid du Colombier 	if(c->name[0]=='\0' || c->value[0]=='\0')
9449a747e4fSDavid du Colombier 		goto Badname;
9459a747e4fSDavid du Colombier 
9469a747e4fSDavid du Colombier 	done = 0;
9479a747e4fSDavid du Colombier 	for(; *p && !done; p=skipspace(p)){
9489a747e4fSDavid du Colombier 		attr = p;
9499a747e4fSDavid du Colombier 		t = skiptoken(p);
9509a747e4fSDavid du Colombier 		u = skipspace(t);
9519a747e4fSDavid du Colombier 		switch(*u){
952220e960cSDavid du Colombier 		case '\0':
953220e960cSDavid du Colombier 			*t = '\0';
954220e960cSDavid du Colombier 			val = p = u;
955220e960cSDavid du Colombier 			break;
9569a747e4fSDavid du Colombier 		case ';':
9579a747e4fSDavid du Colombier 			*t = '\0';
9589a747e4fSDavid du Colombier 			val = "";
9599a747e4fSDavid du Colombier 			p = u+1;
9609a747e4fSDavid du Colombier 			break;
9619a747e4fSDavid du Colombier 		case '=':
9629a747e4fSDavid du Colombier 			*t = '\0';
9639a747e4fSDavid du Colombier 			val = skipspace(u+1);
9649a747e4fSDavid du Colombier 			p = skipvalue(val, isns);
9659a747e4fSDavid du Colombier 			if(*p==',')
9669a747e4fSDavid du Colombier 				done = 1;
9679a747e4fSDavid du Colombier 			if(*p)
9689a747e4fSDavid du Colombier 				*p++ = '\0';
9699a747e4fSDavid du Colombier 			break;
9709a747e4fSDavid du Colombier 		case ',':
9719a747e4fSDavid du Colombier 			if(!isns){
9729a747e4fSDavid du Colombier 				val = "";
9739a747e4fSDavid du Colombier 				p = u;
9749a747e4fSDavid du Colombier 				*p++ = '\0';
9759a747e4fSDavid du Colombier 				done = 1;
9769a747e4fSDavid du Colombier 				break;
9779a747e4fSDavid du Colombier 			}
9789a747e4fSDavid du Colombier 		default:
9799a747e4fSDavid du Colombier 			if(cookiedebug)
9809a747e4fSDavid du Colombier 				fprint(2, "syntax: %s\n", p);
9819a747e4fSDavid du Colombier 			return "syntax error";
9829a747e4fSDavid du Colombier 		}
9839a747e4fSDavid du Colombier 		for(i=0; i<nelem(stab); i++)
9849a747e4fSDavid du Colombier 			if(stab[i].ishttp && cistrcmp(stab[i].s, attr)==0)
9859dfc0cb2SDavid du Colombier 				*(char**)((uintptr)c+stab[i].offset) = val;
9869a747e4fSDavid du Colombier 		if(cistrcmp(attr, "expires") == 0){
9879a747e4fSDavid du Colombier 			if(!isns)
9889a747e4fSDavid du Colombier 				return "non-netscape cookie has Expires tag";
9899a747e4fSDavid du Colombier 			if(!val[0])
9909a747e4fSDavid du Colombier 				return "bad expires tag";
9919a747e4fSDavid du Colombier 			c->expire = strtotime(val);
9929a747e4fSDavid du Colombier 			if(c->expire == ~0)
9939a747e4fSDavid du Colombier 				return "cannot parse netscape expires tag";
9949a747e4fSDavid du Colombier 		}
9959a747e4fSDavid du Colombier 		if(cistrcmp(attr, "max-age") == 0)
9969a747e4fSDavid du Colombier 			c->expire = time(0)+atoi(val);
9979a747e4fSDavid du Colombier 		if(cistrcmp(attr, "secure") == 0)
9989a747e4fSDavid du Colombier 			c->secure = 1;
9999a747e4fSDavid du Colombier 	}
10009a747e4fSDavid du Colombier 
10019a747e4fSDavid du Colombier 	if(c->dom)
10029a747e4fSDavid du Colombier 		c->explicitdom = 1;
10039a747e4fSDavid du Colombier 	else
10049a747e4fSDavid du Colombier 		c->dom = dom;
10059a747e4fSDavid du Colombier 	if(c->path)
10069a747e4fSDavid du Colombier 		c->explicitpath = 1;
1007220e960cSDavid du Colombier 	else{
10089a747e4fSDavid du Colombier 		c->path = path;
1009220e960cSDavid du Colombier 		if((t = strchr(c->path, '?')) != 0)
1010220e960cSDavid du Colombier 			*t = '\0';
1011220e960cSDavid du Colombier 		if((t = strrchr(c->path, '/')) != 0)
1012220e960cSDavid du Colombier 			*t = '\0';
1013220e960cSDavid du Colombier 	}
10149a747e4fSDavid du Colombier 	c->netscapestyle = isns;
10159a747e4fSDavid du Colombier 	*e = p;
10169a747e4fSDavid du Colombier 
10179a747e4fSDavid du Colombier 	return nil;
10189a747e4fSDavid du Colombier }
10199a747e4fSDavid du Colombier 
10209a747e4fSDavid du Colombier Jar *jar;
10219a747e4fSDavid du Colombier 
10229a747e4fSDavid du Colombier typedef struct Aux Aux;
10239a747e4fSDavid du Colombier struct Aux
10249a747e4fSDavid du Colombier {
10259a747e4fSDavid du Colombier 	char *dom;
10269a747e4fSDavid du Colombier 	char *path;
10279a747e4fSDavid du Colombier 	char *inhttp;
10289a747e4fSDavid du Colombier 	char *outhttp;
10299a747e4fSDavid du Colombier 	char *ctext;
10309a747e4fSDavid du Colombier 	int rdoff;
10319a747e4fSDavid du Colombier };
10329a747e4fSDavid du Colombier enum
10339a747e4fSDavid du Colombier {
10349a747e4fSDavid du Colombier 	AuxBuf = 4096,
10359a747e4fSDavid du Colombier 	MaxCtext = 16*1024*1024,
10369a747e4fSDavid du Colombier };
10379a747e4fSDavid du Colombier 
10389a747e4fSDavid du Colombier void
cookieopen(Req * r)10399a747e4fSDavid du Colombier cookieopen(Req *r)
10409a747e4fSDavid du Colombier {
10419a747e4fSDavid du Colombier 	char *s, *es;
10429a747e4fSDavid du Colombier 	int i, sz;
10439a747e4fSDavid du Colombier 	Aux *a;
10449a747e4fSDavid du Colombier 
10459a747e4fSDavid du Colombier 	syncjar(jar);
10469a747e4fSDavid du Colombier 	a = emalloc9p(sizeof(Aux));
10479a747e4fSDavid du Colombier 	r->fid->aux = a;
10489a747e4fSDavid du Colombier 	if(r->ifcall.mode&OTRUNC){
10499a747e4fSDavid du Colombier 		a->ctext = emalloc9p(1);
10509a747e4fSDavid du Colombier 		a->ctext[0] = '\0';
10519a747e4fSDavid du Colombier 	}else{
10529a747e4fSDavid du Colombier 		sz = 256*jar->nc+1024;	/* BUG should do better */
10539a747e4fSDavid du Colombier 		a->ctext = emalloc9p(sz);
10549a747e4fSDavid du Colombier 		a->ctext[0] = '\0';
10559a747e4fSDavid du Colombier 		s = a->ctext;
10569a747e4fSDavid du Colombier 		es = s+sz;
10579a747e4fSDavid du Colombier 		for(i=0; i<jar->nc; i++)
10589a747e4fSDavid du Colombier 			s = seprint(s, es, "%K\n", &jar->c[i]);
10599a747e4fSDavid du Colombier 	}
10609a747e4fSDavid du Colombier 	respond(r, nil);
10619a747e4fSDavid du Colombier }
10629a747e4fSDavid du Colombier 
10639a747e4fSDavid du Colombier void
cookieread(Req * r)10649a747e4fSDavid du Colombier cookieread(Req *r)
10659a747e4fSDavid du Colombier {
10669a747e4fSDavid du Colombier 	Aux *a;
10679a747e4fSDavid du Colombier 
10689a747e4fSDavid du Colombier 	a = r->fid->aux;
10699a747e4fSDavid du Colombier 	readstr(r, a->ctext);
10709a747e4fSDavid du Colombier 	respond(r, nil);
10719a747e4fSDavid du Colombier }
10729a747e4fSDavid du Colombier 
10739a747e4fSDavid du Colombier void
cookiewrite(Req * r)10749a747e4fSDavid du Colombier cookiewrite(Req *r)
10759a747e4fSDavid du Colombier {
10769a747e4fSDavid du Colombier 	Aux *a;
10779a747e4fSDavid du Colombier 	int sz;
10789a747e4fSDavid du Colombier 
10799a747e4fSDavid du Colombier 	a = r->fid->aux;
10809a747e4fSDavid du Colombier 	sz = r->ifcall.count+r->ifcall.offset;
10819a747e4fSDavid du Colombier 	if(sz > strlen(a->ctext)){
10829a747e4fSDavid du Colombier 		if(sz >= MaxCtext){
10839a747e4fSDavid du Colombier 			respond(r, "cookie file too large");
10849a747e4fSDavid du Colombier 			return;
10859a747e4fSDavid du Colombier 		}
10869a747e4fSDavid du Colombier 		a->ctext = erealloc9p(a->ctext, sz+1);
10879a747e4fSDavid du Colombier 		a->ctext[sz] = '\0';
10889a747e4fSDavid du Colombier 	}
10899a747e4fSDavid du Colombier 	memmove(a->ctext+r->ifcall.offset, r->ifcall.data, r->ifcall.count);
10909a747e4fSDavid du Colombier 	r->ofcall.count = r->ifcall.count;
10919a747e4fSDavid du Colombier 	respond(r, nil);
10929a747e4fSDavid du Colombier }
10939a747e4fSDavid du Colombier 
10949a747e4fSDavid du Colombier void
cookieclunk(Fid * fid)10959a747e4fSDavid du Colombier cookieclunk(Fid *fid)
10969a747e4fSDavid du Colombier {
10979a747e4fSDavid du Colombier 	char *p, *nextp;
10989a747e4fSDavid du Colombier 	Aux *a;
10999a747e4fSDavid du Colombier 	int i;
11009a747e4fSDavid du Colombier 
11019a747e4fSDavid du Colombier 	a = fid->aux;
11029a747e4fSDavid du Colombier 	if(a == nil)
11039a747e4fSDavid du Colombier 		return;
11049a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)
11059a747e4fSDavid du Colombier 		jar->c[i].mark = 1;
11069a747e4fSDavid du Colombier 	for(p=a->ctext; *p; p=nextp){
11079a747e4fSDavid du Colombier 		if((nextp = strchr(p, '\n')) != nil)
11089a747e4fSDavid du Colombier 			*nextp++ = '\0';
11099a747e4fSDavid du Colombier 		else
11109a747e4fSDavid du Colombier 			nextp = "";
11119a747e4fSDavid du Colombier 		addtojar(jar, p, 0);
11129a747e4fSDavid du Colombier 	}
11139a747e4fSDavid du Colombier 	for(i=0; i<jar->nc; i++)
11149a747e4fSDavid du Colombier 		if(jar->c[i].mark)
11159a747e4fSDavid du Colombier 			delcookie(jar, &jar->c[i]);
11169a747e4fSDavid du Colombier 	syncjar(jar);
11179a747e4fSDavid du Colombier 	free(a->dom);
11189a747e4fSDavid du Colombier 	free(a->path);
11199a747e4fSDavid du Colombier 	free(a->inhttp);
11209a747e4fSDavid du Colombier 	free(a->outhttp);
11219a747e4fSDavid du Colombier 	free(a->ctext);
11229a747e4fSDavid du Colombier 	free(a);
11239a747e4fSDavid du Colombier }
11249a747e4fSDavid du Colombier 
11259a747e4fSDavid du Colombier void
closecookies(void)11269a747e4fSDavid du Colombier closecookies(void)
11279a747e4fSDavid du Colombier {
11289a747e4fSDavid du Colombier 	closejar(jar);
11299a747e4fSDavid du Colombier }
11309a747e4fSDavid du Colombier 
11319a747e4fSDavid du Colombier void
initcookies(char * file)11329a747e4fSDavid du Colombier initcookies(char *file)
11339a747e4fSDavid du Colombier {
11349a747e4fSDavid du Colombier 	char *home;
11359a747e4fSDavid du Colombier 
11369a747e4fSDavid du Colombier 	fmtinstall('J', jarfmt);
11379a747e4fSDavid du Colombier 	fmtinstall('K', cookiefmt);
11389a747e4fSDavid du Colombier 
11399a747e4fSDavid du Colombier 	if(file == nil){
11409a747e4fSDavid du Colombier 		home = getenv("home");
11419a747e4fSDavid du Colombier 		if(home == nil)
11429a747e4fSDavid du Colombier 			sysfatal("no cookie file specified and no $home");
11439a747e4fSDavid du Colombier 		file = emalloc9p(strlen(home)+30);
11449a747e4fSDavid du Colombier 		strcpy(file, home);
11459a747e4fSDavid du Colombier 		strcat(file, "/lib/webcookies");
11469a747e4fSDavid du Colombier 	}
11479a747e4fSDavid du Colombier 	jar = readjar(file);
11489a747e4fSDavid du Colombier 	if(jar == nil)
11499a747e4fSDavid du Colombier 		sysfatal("readjar: %r");
11509a747e4fSDavid du Colombier }
11519a747e4fSDavid du Colombier 
11529a747e4fSDavid du Colombier void
httpsetcookie(char * hdr,char * dom,char * path)11539a747e4fSDavid du Colombier httpsetcookie(char *hdr, char *dom, char *path)
11549a747e4fSDavid du Colombier {
1155d9306527SDavid du Colombier 	if(path == nil)
1156d9306527SDavid du Colombier 		path = "/";
1157d9306527SDavid du Colombier 
11589a747e4fSDavid du Colombier 	parsehttp(jar, hdr, dom, path);
11599a747e4fSDavid du Colombier 	syncjar(jar);
11609a747e4fSDavid du Colombier }
11619a747e4fSDavid du Colombier 
11629a747e4fSDavid du Colombier char*
httpcookies(char * dom,char * path,int issecure)11639a747e4fSDavid du Colombier httpcookies(char *dom, char *path, int issecure)
11649a747e4fSDavid du Colombier {
11659a747e4fSDavid du Colombier 	char buf[1024];
11669a747e4fSDavid du Colombier 	Jar *j;
11679a747e4fSDavid du Colombier 
11689a747e4fSDavid du Colombier 	syncjar(jar);
11699a747e4fSDavid du Colombier 	j = cookiesearch(jar, dom, path, issecure);
11709a747e4fSDavid du Colombier 	snprint(buf, sizeof buf, "%J", j);
11719a747e4fSDavid du Colombier 	closejar(j);
11729a747e4fSDavid du Colombier 	return estrdup(buf);
11739a747e4fSDavid du Colombier }
1174