📄 webcookies.c
字号:
fprint(2, "bad wday separator (%s)\n", os); return -1; } /* 25- */ if(!isdigit(s[0]) || !isdigit(s[1]) || (s[2]!='-' && s[2]!=' ')){ if(debug) fprint(2, "bad day of month (%s)\n", os); return -1; } tm.mday = strtol(s, 0, 10); s += 3; /* Jan- */ for(i=0; i<nelem(mon); i++) if(cistrncmp(s, mon[i], 3) == 0){ tm.mon = i; s += 3; break; } if(i==nelem(mon)){ if(debug) fprint(2, "bad month (%s)\n", os); return -1; } if(s[0] != '-' && s[0] != ' '){ if(debug) fprint(2, "bad month separator (%s)\n", os); return -1; } s++; /* 2002 */ if(!isdigit(s[0]) || !isdigit(s[1])){ if(debug) fprint(2, "bad year (%s)\n", os); return -1; } tm.year = strtol(s, 0, 10); s += 2; if(isdigit(s[0]) && isdigit(s[1])) s += 2; else{ if(tm.year <= 68) tm.year += 2000; else tm.year += 1900; } if(tm.mday==0 || tm.mday > mday[isleap(tm.year)][tm.mon]){ if(debug) fprint(2, "invalid day of month (%s)\n", os); return -1; } tm.year -= 1900; if(*s++ != ' '){ if(debug) fprint(2, "bad year separator (%s)\n", os); return -1; } if(!isdigit(s[0]) || !isdigit(s[1]) || s[2]!=':' || !isdigit(s[3]) || !isdigit(s[4]) || s[5]!=':' || !isdigit(s[6]) || !isdigit(s[7]) || s[8]!=' '){ if(debug) fprint(2, "bad time (%s)\n", os); return -1; } tm.hour = atoi(s); tm.min = atoi(s+3); tm.sec = atoi(s+6); if(tm.hour >= 24 || tm.min >= 60 || tm.sec >= 60){ if(debug) fprint(2, "invalid time (%s)\n", os); return -1; } s += 9; if(cistrcmp(s, "GMT") != 0){ if(debug) fprint(2, "time zone not GMT (%s)\n", os); return -1; } strcpy(tm.zone, "GMT"); tm.yday = 0; return tm2sec(&tm);}/* * skip linear whitespace. we're a bit more lenient than RFC2616 2.2. */char*skipspace(char *s){ while(*s=='\r' || *s=='\n' || *s==' ' || *s=='\t') s++; return s;}/* * Try to identify old netscape headers. * The old headers: * - didn't allow spaces around the '=' * - used an 'Expires' attribute * - had no 'Version' attribute * - had no quotes * - allowed whitespace in values * - apparently separated attr/value pairs with ';' exclusively */intisnetscape(char *hdr){ char *s; for(s=hdr; (s=strchr(s, '=')) != nil; s++){ if(isspace(s[1]) || (s > hdr && isspace(s[-1]))) return 0; if(s[1]=='"') return 0; } if(cistrstr(hdr, "version=")) return 0; return 1;}/* * Parse HTTP response headers, adding cookies to jar. * Overwrites the headers. May overwrite path. */char* parsecookie(Cookie*, char*, char**, int, char*, char*);intparsehttp(Jar *jar, char *hdr, char *dom, char *path){ static char setcookie[] = "Set-Cookie:"; char *e, *p, *nextp; Cookie c; int isns, n; isns = isnetscape(hdr); n = 0; for(p=hdr; p; p=nextp){ p = skipspace(p); if(*p == '\0') break; nextp = strchr(p, '\n'); if(nextp != nil) *nextp++ = '\0'; if(debug) fprint(2, "?%s\n", p); if(cistrncmp(p, setcookie, strlen(setcookie)) != 0) continue; if(debug) fprint(2, "%s\n", p); p = skipspace(p+strlen(setcookie)); for(; *p; p=skipspace(p)){ if((e = parsecookie(&c, p, &p, isns, dom, path)) != nil){ if(debug) fprint(2, "parse cookie: %s\n", e); break; } if((e = isbadcookie(&c, dom, path)) != nil){ if(debug) fprint(2, "reject cookie; %s\n", e); continue; } addcookie(jar, &c); n++; } } return n;}static char*skipquoted(char *s){ /* * Sec 2.2 of RFC2616 defines a "quoted-string" as: * * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) * qdtext = <any TEXT except <">> * quoted-pair = "\" CHAR * * TEXT is any octet except CTLs, but including LWS; * LWS is [CR LF] 1*(SP | HT); * CHARs are ASCII octets 0-127; (NOTE: we reject 0's) * CTLs are octets 0-31 and 127; */ if(*s != '"') return s; for(s++; 32 <= *s && *s < 127 && *s != '"'; s++) if(*s == '\\' && *(s+1) != '\0') s++; return s;}static char*skiptoken(char *s){ /* * Sec 2.2 of RFC2616 defines a "token" as * 1*<any CHAR except CTLs or separators>; * CHARs are ASCII octets 0-127; * CTLs are octets 0-31 and 127; * separators are "()<>@,;:\/[]?={}", double-quote, SP (32), and HT (9) */ while(32 <= *s && *s < 127 && strchr("()<>@,;:[]?={}\" \t\\", *s)==nil) s++; return s;}static char*skipvalue(char *s, int isns){ char *t; /* * An RFC2109 value is an HTTP token or an HTTP quoted string. * Netscape servers ignore the spec and rely on semicolons, apparently. */ if(isns){ if((t = strchr(s, ';')) == nil) t = s+strlen(s); return t; } if(*s == '"') return skipquoted(s); return skiptoken(s);}/* * RMID=80b186bb64c03c65fab767f8; expires=Monday, 10-Feb-2003 04:44:39 GMT; * path=/; domain=.nytimes.com */char*parsecookie(Cookie *c, char *p, char **e, int isns, char *dom, char *path){ int i, done; char *t, *u, *attr, *val; memset(c, 0, sizeof *c); c->expire = ~0; /* NAME=VALUE */ t = skiptoken(p); c->name = p; p = skipspace(t); if(*p != '='){ Badname: return "malformed cookie: no NAME=VALUE"; } *t = '\0'; p = skipspace(p+1); t = skipvalue(p, isns); if(*t) *t++ = '\0'; c->value = p; p = skipspace(t); if(c->name[0]=='\0' || c->value[0]=='\0') goto Badname; done = 0; for(; *p && !done; p=skipspace(p)){ attr = p; t = skiptoken(p); u = skipspace(t); switch(*u){ case '\0': *t = '\0'; p = val = u; break; case ';': *t = '\0'; val = ""; p = u+1; break; case '=': *t = '\0'; val = skipspace(u+1); p = skipvalue(val, isns); if(*p==',') done = 1; if(*p) *p++ = '\0'; break; case ',': if(!isns){ val = ""; p = u; *p++ = '\0'; done = 1; break; } default: if(debug) fprint(2, "syntax: %s\n", p); return "syntax error"; } for(i=0; i<nelem(stab); i++) if(stab[i].ishttp && cistrcmp(stab[i].s, attr)==0) *(char**)((char*)c+stab[i].offset) = val; if(cistrcmp(attr, "expires") == 0){ if(!isns) return "non-netscape cookie has Expires tag"; if(!val[0]) return "bad expires tag"; c->expire = strtotime(val); if(c->expire == ~0) return "cannot parse netscape expires tag"; } if(cistrcmp(attr, "max-age") == 0) c->expire = time(0)+atoi(val); if(cistrcmp(attr, "secure") == 0) c->secure = 1; } if(c->dom) c->explicitdom = 1; else c->dom = dom; if(c->path) c->explicitpath = 1; else{ c->path = path; if((t = strchr(c->path, '?')) != 0) *t = '\0'; if((t = strrchr(c->path, '/')) != 0) *t = '\0'; } c->netscapestyle = isns; *e = p; return nil;}Jar *jar;enum{ Xhttp = 1, Xcookies, NeedUrl = 0, HaveUrl,};typedef struct Aux Aux;struct Aux{ int state; char *dom; char *path; char *inhttp; char *outhttp; char *ctext; int rdoff;};enum{ AuxBuf = 4096, MaxCtext = 16*1024*1024,};voidfsopen(Req *r){ char *s, *es; int i, sz; Aux *a; switch((uintptr)r->fid->file->aux){ case Xhttp: syncjar(jar); a = emalloc9p(sizeof(Aux)); r->fid->aux = a; a->inhttp = emalloc9p(AuxBuf); a->outhttp = emalloc9p(AuxBuf); break; case Xcookies: syncjar(jar); a = emalloc9p(sizeof(Aux)); r->fid->aux = a; if(r->ifcall.mode&OTRUNC){ a->ctext = emalloc9p(1); a->ctext[0] = '\0'; }else{ sz = 256*jar->nc+1024; /* BUG should do better */ a->ctext = emalloc9p(sz); a->ctext[0] = '\0'; s = a->ctext; es = s+sz; for(i=0; i<jar->nc; i++) s = seprint(s, es, "%K\n", &jar->c[i]); } break; } respond(r, nil);}voidfsread(Req *r){ Aux *a; a = r->fid->aux; switch((uintptr)r->fid->file->aux){ case Xhttp: if(a->state == NeedUrl){ respond(r, "must write url before read"); return; } r->ifcall.offset = a->rdoff; readstr(r, a->outhttp); a->rdoff += r->ofcall.count; respond(r, nil); return; case Xcookies: readstr(r, a->ctext); respond(r, nil); return; default: respond(r, "bug in webcookies"); return; }}voidfswrite(Req *r){ Aux *a; int i, sz, hlen, issecure; char buf[1024], *p; Jar *j; a = r->fid->aux; switch((uintptr)r->fid->file->aux){ case Xhttp: if(a->state == NeedUrl){ if(r->ifcall.count >= sizeof buf){ respond(r, "url too long"); return; } memmove(buf, r->ifcall.data, r->ifcall.count); buf[r->ifcall.count] = '\0'; issecure = 0; if(cistrncmp(buf, "http://", 7) == 0) hlen = 7; else if(cistrncmp(buf, "https://", 8) == 0){ hlen = 8; issecure = 1; }else{ respond(r, "url must begin http:// or https://"); return; } if(buf[hlen]=='/'){ respond(r, "url without host name"); return; } p = strchr(buf+hlen, '/'); if(p == nil) a->path = estrdup9p("/"); else{ a->path = estrdup9p(p); *p = '\0'; } a->dom = estrdup9p(buf+hlen); a->state = HaveUrl; j = cookiesearch(jar, a->dom, a->path, issecure); if(debug){ fprint(2, "search %s %s got %p\n", a->dom, a->path, j); if(j){ fprint(2, "%d cookies\n", j->nc); for(i=0; i<j->nc; i++) fprint(2, "%K\n", &j->c[i]); } } snprint(a->outhttp, AuxBuf, "%J", j); if(j) closejar(j); }else{ if(strlen(a->inhttp)+r->ifcall.count >= AuxBuf){ respond(r, "http headers too large"); return; } memmove(a->inhttp+strlen(a->inhttp), r->ifcall.data, r->ifcall.count); } r->ofcall.count = r->ifcall.count; respond(r, nil); return; case Xcookies: sz = r->ifcall.count+r->ifcall.offset; if(sz > strlen(a->ctext)){ if(sz >= MaxCtext){ respond(r, "cookie file too large"); return; } a->ctext = erealloc9p(a->ctext, sz+1); a->ctext[sz] = '\0'; } memmove(a->ctext+r->ifcall.offset, r->ifcall.data, r->ifcall.count); r->ofcall.count = r->ifcall.count; respond(r, nil); return; default: respond(r, "bug in webcookies"); return; }}voidfsdestroyfid(Fid *fid){ char *p, *nextp; Aux *a; int i; a = fid->aux; if(a == nil) return; switch((uintptr)fid->file->aux){ case Xhttp: parsehttp(jar, a->inhttp, a->dom, a->path); break; case Xcookies: for(i=0; i<jar->nc; i++) jar->c[i].mark = 1; for(p=a->ctext; *p; p=nextp){ if((nextp = strchr(p, '\n')) != nil) *nextp++ = '\0'; else nextp = ""; addtojar(jar, p, 0); } for(i=0; i<jar->nc; i++) if(jar->c[i].mark) delcookie(jar, &jar->c[i]); break; } syncjar(jar); free(a->dom); free(a->path); free(a->inhttp); free(a->outhttp); free(a->ctext); free(a);}voidfsend(Srv*){ closejar(jar);}Srv fs = {.open= fsopen,.read= fsread,.write= fswrite,.destroyfid= fsdestroyfid,.end= fsend,};voidusage(void){ fprint(2, "usage: webcookies [-f file] [-m mtpt] [-s service]\n"); exits("usage");} voidmain(int argc, char **argv){ char *file, *mtpt, *home, *srv; file = nil; srv = nil; mtpt = "/mnt/webcookies"; ARGBEGIN{ case 'D': chatty9p++; break; case 'd': debug = 1; break; case 'f': file = EARGF(usage()); break; case 's': srv = EARGF(usage()); break; case 'm': mtpt = EARGF(usage()); break; default: usage(); }ARGEND if(argc != 0) usage(); quotefmtinstall(); fmtinstall('J', jarfmt); fmtinstall('K', cookiefmt); if(file == nil){ home = getenv("home"); if(home == nil) sysfatal("no cookie file specified and no $home"); file = emalloc9p(strlen(home)+30); strcpy(file, home); strcat(file, "/lib/webcookies"); } if(access(file, AEXIST) < 0) close(create(file, OWRITE, 0666)); jar = readjar(file); if(jar == nil) sysfatal("readjar: %r"); fs.tree = alloctree("cookie", "cookie", DMDIR|0555, nil); closefile(createfile(fs.tree->root, "http", "cookie", 0666, (void*)Xhttp)); closefile(createfile(fs.tree->root, "cookies", "cookie", 0666, (void*)Xcookies)); postmountsrv(&fs, srv, mtpt, MREPL); exits(nil);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -