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