1*0e96539fSCharles.Forsythimplement RFC822; 2*0e96539fSCharles.Forsyth 3*0e96539fSCharles.Forsythinclude "sys.m"; 4*0e96539fSCharles.Forsyth sys: Sys; 5*0e96539fSCharles.Forsyth 6*0e96539fSCharles.Forsythinclude "bufio.m"; 7*0e96539fSCharles.Forsyth bufio: Bufio; 8*0e96539fSCharles.Forsyth Iobuf: import bufio; 9*0e96539fSCharles.Forsyth 10*0e96539fSCharles.Forsythinclude "rfc822.m"; 11*0e96539fSCharles.Forsyth 12*0e96539fSCharles.Forsythinclude "string.m"; 13*0e96539fSCharles.Forsyth str: String; 14*0e96539fSCharles.Forsyth 15*0e96539fSCharles.Forsythinclude "daytime.m"; 16*0e96539fSCharles.Forsyth daytime: Daytime; 17*0e96539fSCharles.Forsyth Tm: import daytime; 18*0e96539fSCharles.Forsyth 19*0e96539fSCharles.ForsythMinrequest: con 512; # more than enough for most requests 20*0e96539fSCharles.Forsyth 21*0e96539fSCharles.ForsythSuffix: adt { 22*0e96539fSCharles.Forsyth suffix: string; 23*0e96539fSCharles.Forsyth generic: string; 24*0e96539fSCharles.Forsyth specific: string; 25*0e96539fSCharles.Forsyth encoding: string; 26*0e96539fSCharles.Forsyth}; 27*0e96539fSCharles.Forsyth 28*0e96539fSCharles.ForsythSuffixFile: con "/lib/mimetype"; 29*0e96539fSCharles.Forsythmtime := 0; 30*0e96539fSCharles.Forsythqid: Sys->Qid; 31*0e96539fSCharles.Forsyth 32*0e96539fSCharles.Forsythsuffixes: list of ref Suffix; 33*0e96539fSCharles.Forsyth 34*0e96539fSCharles.Forsythnomod(s: string) 35*0e96539fSCharles.Forsyth{ 36*0e96539fSCharles.Forsyth raise sys->sprint("internal: can't load %s: %r", s); 37*0e96539fSCharles.Forsyth} 38*0e96539fSCharles.Forsyth 39*0e96539fSCharles.Forsythinit(b: Bufio) 40*0e96539fSCharles.Forsyth{ 41*0e96539fSCharles.Forsyth sys = load Sys Sys->PATH; 42*0e96539fSCharles.Forsyth bufio = b; 43*0e96539fSCharles.Forsyth str = load String String->PATH; 44*0e96539fSCharles.Forsyth if(str == nil) 45*0e96539fSCharles.Forsyth nomod(String->PATH); 46*0e96539fSCharles.Forsyth daytime = load Daytime Daytime->PATH; 47*0e96539fSCharles.Forsyth if(daytime == nil) 48*0e96539fSCharles.Forsyth nomod(Daytime->PATH); 49*0e96539fSCharles.Forsyth readsuffixfile(); 50*0e96539fSCharles.Forsyth} 51*0e96539fSCharles.Forsyth 52*0e96539fSCharles.Forsythreadheaders(fd: ref Iobuf, limit: int): array of (string, array of byte) 53*0e96539fSCharles.Forsyth{ 54*0e96539fSCharles.Forsyth n := 0; 55*0e96539fSCharles.Forsyth s := 0; 56*0e96539fSCharles.Forsyth b := array[Minrequest] of byte; 57*0e96539fSCharles.Forsyth nline := 0; 58*0e96539fSCharles.Forsyth lines: list of array of byte; 59*0e96539fSCharles.Forsyth while((c := fd.getb()) >= 0){ 60*0e96539fSCharles.Forsyth if(c == '\r'){ 61*0e96539fSCharles.Forsyth c = fd.getb(); 62*0e96539fSCharles.Forsyth if(c < 0) 63*0e96539fSCharles.Forsyth break; 64*0e96539fSCharles.Forsyth if(c != '\n'){ 65*0e96539fSCharles.Forsyth fd.ungetb(); 66*0e96539fSCharles.Forsyth c = '\r'; 67*0e96539fSCharles.Forsyth } 68*0e96539fSCharles.Forsyth } 69*0e96539fSCharles.Forsyth if(n >= len b){ 70*0e96539fSCharles.Forsyth if(len b >= limit) 71*0e96539fSCharles.Forsyth return nil; 72*0e96539fSCharles.Forsyth ab := array[n+512] of byte; 73*0e96539fSCharles.Forsyth ab[0:] = b; 74*0e96539fSCharles.Forsyth b = ab; 75*0e96539fSCharles.Forsyth } 76*0e96539fSCharles.Forsyth b[n++] = byte c; 77*0e96539fSCharles.Forsyth if(c == '\n'){ 78*0e96539fSCharles.Forsyth if(n == 1 || b[n-2] == byte '\n') 79*0e96539fSCharles.Forsyth break; # empty line 80*0e96539fSCharles.Forsyth c = fd.getb(); 81*0e96539fSCharles.Forsyth if(c < 0) 82*0e96539fSCharles.Forsyth break; 83*0e96539fSCharles.Forsyth if(c != ' ' && c != '\t'){ # not continued 84*0e96539fSCharles.Forsyth fd.ungetb(); 85*0e96539fSCharles.Forsyth lines = b[s: n] :: lines; 86*0e96539fSCharles.Forsyth nline++; 87*0e96539fSCharles.Forsyth s = n; 88*0e96539fSCharles.Forsyth }else 89*0e96539fSCharles.Forsyth b[n-1] = byte ' '; 90*0e96539fSCharles.Forsyth } 91*0e96539fSCharles.Forsyth } 92*0e96539fSCharles.Forsyth if(n == 0) 93*0e96539fSCharles.Forsyth return nil; 94*0e96539fSCharles.Forsyth b = b[0: n]; 95*0e96539fSCharles.Forsyth if(n != s){ 96*0e96539fSCharles.Forsyth lines = b[s:n] :: lines; 97*0e96539fSCharles.Forsyth nline++; 98*0e96539fSCharles.Forsyth } 99*0e96539fSCharles.Forsyth a := array[nline] of (string, array of byte); 100*0e96539fSCharles.Forsyth for(; lines != nil; lines = tl lines){ 101*0e96539fSCharles.Forsyth b = hd lines; 102*0e96539fSCharles.Forsyth name := ""; 103*0e96539fSCharles.Forsyth for(i := 0; i < len b; i++) 104*0e96539fSCharles.Forsyth if(b[i] == byte ':'){ 105*0e96539fSCharles.Forsyth name = str->tolower(string b[0:i]); 106*0e96539fSCharles.Forsyth b = b[i+1:]; 107*0e96539fSCharles.Forsyth break; 108*0e96539fSCharles.Forsyth } 109*0e96539fSCharles.Forsyth a[--nline] = (name, b); 110*0e96539fSCharles.Forsyth } 111*0e96539fSCharles.Forsyth return a; 112*0e96539fSCharles.Forsyth} 113*0e96539fSCharles.Forsyth 114*0e96539fSCharles.Forsyth# 115*0e96539fSCharles.Forsyth# *(";" parameter) used in transfer-extension, media-type and media-range 116*0e96539fSCharles.Forsyth# parameter = attribute "=" value 117*0e96539fSCharles.Forsyth# attribute = token 118*0e96539fSCharles.Forsyth# value = token | quoted-string 119*0e96539fSCharles.Forsyth# 120*0e96539fSCharles.Forsythparseparams(ps: ref Rfclex): list of (string, string) 121*0e96539fSCharles.Forsyth{ 122*0e96539fSCharles.Forsyth l: list of (string, string); 123*0e96539fSCharles.Forsyth do{ 124*0e96539fSCharles.Forsyth if(ps.lex() != Word) 125*0e96539fSCharles.Forsyth break; 126*0e96539fSCharles.Forsyth attr := ps.wordval; 127*0e96539fSCharles.Forsyth if(ps.lex() != '=' || ps.lex() != Word && ps.tok != QString) 128*0e96539fSCharles.Forsyth break; 129*0e96539fSCharles.Forsyth l = (attr, ps.wordval) :: l; 130*0e96539fSCharles.Forsyth }while(ps.lex() == ';'); 131*0e96539fSCharles.Forsyth ps.unlex(); 132*0e96539fSCharles.Forsyth return rev(l); 133*0e96539fSCharles.Forsyth} 134*0e96539fSCharles.Forsyth 135*0e96539fSCharles.Forsyth# 136*0e96539fSCharles.Forsyth# 1#transfer-coding 137*0e96539fSCharles.Forsyth# 138*0e96539fSCharles.Forsythmimefields(ps: ref Rfclex): list of (string, list of (string, string)) 139*0e96539fSCharles.Forsyth{ 140*0e96539fSCharles.Forsyth rf: list of (string, list of (string, string)); 141*0e96539fSCharles.Forsyth do{ 142*0e96539fSCharles.Forsyth if(ps.lex() == Word){ 143*0e96539fSCharles.Forsyth w := ps.wordval; 144*0e96539fSCharles.Forsyth if(ps.lex() == ';'){ 145*0e96539fSCharles.Forsyth rf = (w, parseparams(ps)) :: rf; 146*0e96539fSCharles.Forsyth ps.lex(); 147*0e96539fSCharles.Forsyth }else 148*0e96539fSCharles.Forsyth rf = (w, nil) :: rf; 149*0e96539fSCharles.Forsyth } 150*0e96539fSCharles.Forsyth }while(ps.tok == ','); 151*0e96539fSCharles.Forsyth ps.unlex(); 152*0e96539fSCharles.Forsyth f: list of (string, list of (string, string)); 153*0e96539fSCharles.Forsyth for(; rf != nil; rf = tl rf) 154*0e96539fSCharles.Forsyth f = hd rf :: f; 155*0e96539fSCharles.Forsyth return f; 156*0e96539fSCharles.Forsyth} 157*0e96539fSCharles.Forsyth 158*0e96539fSCharles.Forsyth# #(media-type | (media-range [accept-params])) ; Content-Type and Accept 159*0e96539fSCharles.Forsyth# 160*0e96539fSCharles.Forsyth# media-type = type "/" subtype *( ";" parameter ) 161*0e96539fSCharles.Forsyth# type = token 162*0e96539fSCharles.Forsyth# subtype = token 163*0e96539fSCharles.Forsyth# LWS must not be used between type and subtype, nor between attribute and value (in parameter) 164*0e96539fSCharles.Forsyth# 165*0e96539fSCharles.Forsyth# media-range = ("*/*" | type "/*" | type "/" subtype ) *(";' parameter) 166*0e96539fSCharles.Forsyth# accept-params = ";" "q" "=" qvalue *( accept-extension ) 167*0e96539fSCharles.Forsyth# accept-extension = ";" token [ "=" ( token | quoted-string ) ] 168*0e96539fSCharles.Forsyth# 169*0e96539fSCharles.Forsyth# 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] ) ; Accept-Charset 170*0e96539fSCharles.Forsyth# 1#( codings [ ";" "q" "=" qvalue ] ) ; Accept-Encoding 171*0e96539fSCharles.Forsyth# 1#( language-range [ ";" "q" "=" qvalue ] ) ; Accept-Language 172*0e96539fSCharles.Forsyth# 173*0e96539fSCharles.Forsyth# codings = ( content-coding | "*" ) 174*0e96539fSCharles.Forsyth# 175*0e96539fSCharles.Forsythparsecontent(ps: ref Rfclex, multipart: int, head: list of ref Content): list of ref Content 176*0e96539fSCharles.Forsyth{ 177*0e96539fSCharles.Forsyth do{ 178*0e96539fSCharles.Forsyth if(ps.lex() == Word){ 179*0e96539fSCharles.Forsyth generic := ps.wordval; 180*0e96539fSCharles.Forsyth specific := "*"; 181*0e96539fSCharles.Forsyth if(ps.lex() == '/'){ 182*0e96539fSCharles.Forsyth if(ps.lex() != Word) 183*0e96539fSCharles.Forsyth break; 184*0e96539fSCharles.Forsyth specific = ps.wordval; 185*0e96539fSCharles.Forsyth if(!multipart && specific != "*") 186*0e96539fSCharles.Forsyth break; 187*0e96539fSCharles.Forsyth }else if(multipart) 188*0e96539fSCharles.Forsyth break; # syntax error 189*0e96539fSCharles.Forsyth else 190*0e96539fSCharles.Forsyth ps.unlex(); 191*0e96539fSCharles.Forsyth params: list of (string, string) = nil; 192*0e96539fSCharles.Forsyth if(ps.lex() == ';'){ 193*0e96539fSCharles.Forsyth params = parseparams(ps); 194*0e96539fSCharles.Forsyth ps.lex(); 195*0e96539fSCharles.Forsyth } 196*0e96539fSCharles.Forsyth head = Content.mk(generic, specific, params) :: head; # order reversed, but doesn't matter 197*0e96539fSCharles.Forsyth } 198*0e96539fSCharles.Forsyth }while(ps.tok == ','); 199*0e96539fSCharles.Forsyth ps.unlex(); 200*0e96539fSCharles.Forsyth return head; 201*0e96539fSCharles.Forsyth} 202*0e96539fSCharles.Forsyth 203*0e96539fSCharles.Forsythrev(l: list of (string, string)): list of (string, string) 204*0e96539fSCharles.Forsyth{ 205*0e96539fSCharles.Forsyth rl: list of (string, string); 206*0e96539fSCharles.Forsyth for(; l != nil; l = tl l) 207*0e96539fSCharles.Forsyth rl = hd l :: rl; 208*0e96539fSCharles.Forsyth return rl; 209*0e96539fSCharles.Forsyth} 210*0e96539fSCharles.Forsyth 211*0e96539fSCharles.ForsythRfclex.mk(a: array of byte): ref Rfclex 212*0e96539fSCharles.Forsyth{ 213*0e96539fSCharles.Forsyth ps := ref Rfclex; 214*0e96539fSCharles.Forsyth ps.fd = bufio->aopen(a); 215*0e96539fSCharles.Forsyth ps.tok = '\n'; 216*0e96539fSCharles.Forsyth ps.eof = 0; 217*0e96539fSCharles.Forsyth return ps; 218*0e96539fSCharles.Forsyth} 219*0e96539fSCharles.Forsyth 220*0e96539fSCharles.ForsythRfclex.getc(ps: self ref Rfclex): int 221*0e96539fSCharles.Forsyth{ 222*0e96539fSCharles.Forsyth c := ps.fd.getb(); 223*0e96539fSCharles.Forsyth if(c < 0) 224*0e96539fSCharles.Forsyth ps.eof = 1; 225*0e96539fSCharles.Forsyth return c; 226*0e96539fSCharles.Forsyth} 227*0e96539fSCharles.Forsyth 228*0e96539fSCharles.ForsythRfclex.ungetc(ps: self ref Rfclex) 229*0e96539fSCharles.Forsyth{ 230*0e96539fSCharles.Forsyth if(!ps.eof) 231*0e96539fSCharles.Forsyth ps.fd.ungetb(); 232*0e96539fSCharles.Forsyth} 233*0e96539fSCharles.Forsyth 234*0e96539fSCharles.ForsythRfclex.lex(ps: self ref Rfclex): int 235*0e96539fSCharles.Forsyth{ 236*0e96539fSCharles.Forsyth if(ps.seen != nil){ 237*0e96539fSCharles.Forsyth (ps.tok, ps.wordval) = hd ps.seen; 238*0e96539fSCharles.Forsyth ps.seen = tl ps.seen; 239*0e96539fSCharles.Forsyth }else 240*0e96539fSCharles.Forsyth ps.tok = lex1(ps, 0); 241*0e96539fSCharles.Forsyth return ps.tok; 242*0e96539fSCharles.Forsyth} 243*0e96539fSCharles.Forsyth 244*0e96539fSCharles.ForsythRfclex.unlex(ps: self ref Rfclex) 245*0e96539fSCharles.Forsyth{ 246*0e96539fSCharles.Forsyth ps.seen = (ps.tok, ps.wordval) :: ps.seen; 247*0e96539fSCharles.Forsyth} 248*0e96539fSCharles.Forsyth 249*0e96539fSCharles.ForsythRfclex.skipws(ps: self ref Rfclex): int 250*0e96539fSCharles.Forsyth{ 251*0e96539fSCharles.Forsyth return lex1(ps, 1); 252*0e96539fSCharles.Forsyth} 253*0e96539fSCharles.Forsyth 254*0e96539fSCharles.Forsyth# 255*0e96539fSCharles.Forsyth# rfc 2822/rfc 1521 lexical analyzer 256*0e96539fSCharles.Forsyth# 257*0e96539fSCharles.Forsythlex1(ps: ref Rfclex, skipwhite: int): int 258*0e96539fSCharles.Forsyth{ 259*0e96539fSCharles.Forsyth ps.wordval = nil; 260*0e96539fSCharles.Forsyth while((c := ps.getc()) >= 0){ 261*0e96539fSCharles.Forsyth case c { 262*0e96539fSCharles.Forsyth '(' => 263*0e96539fSCharles.Forsyth level := 1; 264*0e96539fSCharles.Forsyth while((c = ps.getc()) != Bufio->EOF && c != '\n'){ 265*0e96539fSCharles.Forsyth if(c == '\\'){ 266*0e96539fSCharles.Forsyth c = ps.getc(); 267*0e96539fSCharles.Forsyth if(c == Bufio->EOF) 268*0e96539fSCharles.Forsyth return '\n'; 269*0e96539fSCharles.Forsyth continue; 270*0e96539fSCharles.Forsyth } 271*0e96539fSCharles.Forsyth if(c == '(') 272*0e96539fSCharles.Forsyth level++; 273*0e96539fSCharles.Forsyth else if(c == ')' && --level == 0) 274*0e96539fSCharles.Forsyth break; 275*0e96539fSCharles.Forsyth } 276*0e96539fSCharles.Forsyth ' ' or '\t' or '\r' or 0 => 277*0e96539fSCharles.Forsyth ; 278*0e96539fSCharles.Forsyth '\n' => 279*0e96539fSCharles.Forsyth return '\n'; 280*0e96539fSCharles.Forsyth ')' or '<' or '>' or '[' or ']' or '@' or '/' or ',' or 281*0e96539fSCharles.Forsyth ';' or ':' or '?' or '=' => 282*0e96539fSCharles.Forsyth if(skipwhite){ 283*0e96539fSCharles.Forsyth ps.ungetc(); 284*0e96539fSCharles.Forsyth return c; 285*0e96539fSCharles.Forsyth } 286*0e96539fSCharles.Forsyth return c; 287*0e96539fSCharles.Forsyth 288*0e96539fSCharles.Forsyth '"' => 289*0e96539fSCharles.Forsyth if(skipwhite){ 290*0e96539fSCharles.Forsyth ps.ungetc(); 291*0e96539fSCharles.Forsyth return c; 292*0e96539fSCharles.Forsyth } 293*0e96539fSCharles.Forsyth word(ps,"\""); 294*0e96539fSCharles.Forsyth ps.getc(); # skip the closing quote 295*0e96539fSCharles.Forsyth return QString; 296*0e96539fSCharles.Forsyth 297*0e96539fSCharles.Forsyth * => 298*0e96539fSCharles.Forsyth ps.ungetc(); 299*0e96539fSCharles.Forsyth if(skipwhite) 300*0e96539fSCharles.Forsyth return c; 301*0e96539fSCharles.Forsyth word(ps,"\"()<>@,;:/[]?={}\r\n \t"); 302*0e96539fSCharles.Forsyth return Word; 303*0e96539fSCharles.Forsyth } 304*0e96539fSCharles.Forsyth } 305*0e96539fSCharles.Forsyth return '\n'; 306*0e96539fSCharles.Forsyth} 307*0e96539fSCharles.Forsyth 308*0e96539fSCharles.Forsyth# return the rest of an rfc 822 line, not including \r or \n 309*0e96539fSCharles.Forsyth# do not map to lower case 310*0e96539fSCharles.Forsyth 311*0e96539fSCharles.ForsythRfclex.line(ps: self ref Rfclex): string 312*0e96539fSCharles.Forsyth{ 313*0e96539fSCharles.Forsyth s := ""; 314*0e96539fSCharles.Forsyth while((c := ps.getc()) != Bufio->EOF && c != '\n' && c != '\r'){ 315*0e96539fSCharles.Forsyth if(c == '\\'){ 316*0e96539fSCharles.Forsyth c = ps.getc(); 317*0e96539fSCharles.Forsyth if(c == Bufio->EOF) 318*0e96539fSCharles.Forsyth break; 319*0e96539fSCharles.Forsyth } 320*0e96539fSCharles.Forsyth s[len s] = c; 321*0e96539fSCharles.Forsyth } 322*0e96539fSCharles.Forsyth ps.tok = '\n'; 323*0e96539fSCharles.Forsyth ps.wordval = s; 324*0e96539fSCharles.Forsyth return s; 325*0e96539fSCharles.Forsyth} 326*0e96539fSCharles.Forsyth 327*0e96539fSCharles.Forsythword(ps: ref Rfclex, stop: string) 328*0e96539fSCharles.Forsyth{ 329*0e96539fSCharles.Forsyth w := ""; 330*0e96539fSCharles.Forsyth while((c := ps.getc()) != Bufio->EOF){ 331*0e96539fSCharles.Forsyth if(c == '\r') 332*0e96539fSCharles.Forsyth c = ' '; 333*0e96539fSCharles.Forsyth if(c == '\\'){ 334*0e96539fSCharles.Forsyth c = ps.getc(); 335*0e96539fSCharles.Forsyth if(c == Bufio->EOF) 336*0e96539fSCharles.Forsyth break; 337*0e96539fSCharles.Forsyth }else if(str->in(c,stop)){ 338*0e96539fSCharles.Forsyth ps.ungetc(); 339*0e96539fSCharles.Forsyth break; 340*0e96539fSCharles.Forsyth } 341*0e96539fSCharles.Forsyth if(c >= 'A' && c <= 'Z') 342*0e96539fSCharles.Forsyth c += 'a' - 'A'; 343*0e96539fSCharles.Forsyth w[len w] = c; 344*0e96539fSCharles.Forsyth } 345*0e96539fSCharles.Forsyth ps.wordval = w; 346*0e96539fSCharles.Forsyth} 347*0e96539fSCharles.Forsyth 348*0e96539fSCharles.Forsythreadsuffixfile(): string 349*0e96539fSCharles.Forsyth{ 350*0e96539fSCharles.Forsyth iob := bufio->open(SuffixFile, Bufio->OREAD); 351*0e96539fSCharles.Forsyth if(iob == nil) 352*0e96539fSCharles.Forsyth return sys->sprint("cannot open %s: %r", SuffixFile); 353*0e96539fSCharles.Forsyth for(n := 1; (line := iob.gets('\n')) != nil; n++){ 354*0e96539fSCharles.Forsyth (s, nil) := parsesuffix(line); 355*0e96539fSCharles.Forsyth if(s != nil) 356*0e96539fSCharles.Forsyth suffixes = s :: suffixes; 357*0e96539fSCharles.Forsyth } 358*0e96539fSCharles.Forsyth return nil; 359*0e96539fSCharles.Forsyth} 360*0e96539fSCharles.Forsyth 361*0e96539fSCharles.Forsythparsesuffix(line: string): (ref Suffix, string) 362*0e96539fSCharles.Forsyth{ 363*0e96539fSCharles.Forsyth (line, nil) = str->splitstrl(line, "#"); 364*0e96539fSCharles.Forsyth if(line == nil) 365*0e96539fSCharles.Forsyth return (nil, nil); 366*0e96539fSCharles.Forsyth (n, slist) := sys->tokenize(line,"\n\t "); 367*0e96539fSCharles.Forsyth if(n == 0) 368*0e96539fSCharles.Forsyth return (nil, nil); 369*0e96539fSCharles.Forsyth if(n < 4) 370*0e96539fSCharles.Forsyth return (nil, "too few fields"); 371*0e96539fSCharles.Forsyth s := ref Suffix; 372*0e96539fSCharles.Forsyth s.suffix = hd slist; 373*0e96539fSCharles.Forsyth slist = tl slist; 374*0e96539fSCharles.Forsyth s.generic = hd slist; 375*0e96539fSCharles.Forsyth if (s.generic == "-") 376*0e96539fSCharles.Forsyth s.generic = ""; 377*0e96539fSCharles.Forsyth slist = tl slist; 378*0e96539fSCharles.Forsyth s.specific = hd slist; 379*0e96539fSCharles.Forsyth if (s.specific == "-") 380*0e96539fSCharles.Forsyth s.specific = ""; 381*0e96539fSCharles.Forsyth slist = tl slist; 382*0e96539fSCharles.Forsyth s.encoding = hd slist; 383*0e96539fSCharles.Forsyth if (s.encoding == "-") 384*0e96539fSCharles.Forsyth s.encoding = ""; 385*0e96539fSCharles.Forsyth if((s.generic == nil || s.specific == nil) && s.encoding == nil) 386*0e96539fSCharles.Forsyth return (nil, nil); 387*0e96539fSCharles.Forsyth return (s, nil); 388*0e96539fSCharles.Forsyth} 389*0e96539fSCharles.Forsyth 390*0e96539fSCharles.Forsyth# 391*0e96539fSCharles.Forsyth# classify by file suffix 392*0e96539fSCharles.Forsyth# 393*0e96539fSCharles.Forsythsuffixclass(name: string): (ref Content, ref Content) 394*0e96539fSCharles.Forsyth{ 395*0e96539fSCharles.Forsyth typ, enc: ref Content; 396*0e96539fSCharles.Forsyth 397*0e96539fSCharles.Forsyth p := str->splitstrr(name, "/").t1; 398*0e96539fSCharles.Forsyth if(p != nil) 399*0e96539fSCharles.Forsyth name = p; 400*0e96539fSCharles.Forsyth 401*0e96539fSCharles.Forsyth for(;;){ 402*0e96539fSCharles.Forsyth (name, p) = suffix(name); # TO DO: match below is case sensitive 403*0e96539fSCharles.Forsyth if(p == nil) 404*0e96539fSCharles.Forsyth break; 405*0e96539fSCharles.Forsyth for(l := suffixes; l != nil; l = tl l){ 406*0e96539fSCharles.Forsyth s := hd l; 407*0e96539fSCharles.Forsyth if(p == s.suffix){ 408*0e96539fSCharles.Forsyth if(s.generic != nil && typ == nil) 409*0e96539fSCharles.Forsyth typ = Content.mk(s.generic, s.specific, nil); 410*0e96539fSCharles.Forsyth if(s.encoding != nil && enc == nil) 411*0e96539fSCharles.Forsyth enc = Content.mk(s.encoding, "", nil); 412*0e96539fSCharles.Forsyth if(typ != nil && enc != nil) 413*0e96539fSCharles.Forsyth break; 414*0e96539fSCharles.Forsyth } 415*0e96539fSCharles.Forsyth } 416*0e96539fSCharles.Forsyth } 417*0e96539fSCharles.Forsyth return (typ, enc); 418*0e96539fSCharles.Forsyth} 419*0e96539fSCharles.Forsyth 420*0e96539fSCharles.Forsythsuffix(s: string): (string, string) 421*0e96539fSCharles.Forsyth{ 422*0e96539fSCharles.Forsyth for(n := len s; --n >= 0;) 423*0e96539fSCharles.Forsyth if(s[n] == '.') 424*0e96539fSCharles.Forsyth return (s[0: n], s[n:]); 425*0e96539fSCharles.Forsyth return (s, nil); 426*0e96539fSCharles.Forsyth} 427*0e96539fSCharles.Forsyth 428*0e96539fSCharles.Forsyth# 429*0e96539fSCharles.Forsyth# classify by initial contents of file 430*0e96539fSCharles.Forsyth# 431*0e96539fSCharles.Forsythdataclass(a: array of byte): (ref Content, ref Content) 432*0e96539fSCharles.Forsyth{ 433*0e96539fSCharles.Forsyth utf8 := 0; 434*0e96539fSCharles.Forsyth for(i := 0; i < len a;){ 435*0e96539fSCharles.Forsyth c := int a[i]; 436*0e96539fSCharles.Forsyth if(c < 16r80){ 437*0e96539fSCharles.Forsyth if(c < 32 && c != '\n' && c != '\r' && c != '\t' && c != '\v' && c != '\f') 438*0e96539fSCharles.Forsyth return (nil, nil); 439*0e96539fSCharles.Forsyth i++; 440*0e96539fSCharles.Forsyth }else{ 441*0e96539fSCharles.Forsyth utf8 = 1; 442*0e96539fSCharles.Forsyth (r, l, nil) := sys->byte2char(a, i); 443*0e96539fSCharles.Forsyth if(r == Sys->UTFerror) 444*0e96539fSCharles.Forsyth return (nil, nil); 445*0e96539fSCharles.Forsyth i += l; 446*0e96539fSCharles.Forsyth } 447*0e96539fSCharles.Forsyth } 448*0e96539fSCharles.Forsyth if(utf8) 449*0e96539fSCharles.Forsyth params := ("charset", "utf-8") :: nil; 450*0e96539fSCharles.Forsyth return (Content.mk("text", "plain", params), nil); 451*0e96539fSCharles.Forsyth} 452*0e96539fSCharles.Forsyth 453*0e96539fSCharles.ForsythContent.mk(generic, specific: string, params: list of (string, string)): ref Content 454*0e96539fSCharles.Forsyth{ 455*0e96539fSCharles.Forsyth c := ref Content; 456*0e96539fSCharles.Forsyth c.generic = generic; 457*0e96539fSCharles.Forsyth c.specific = specific; 458*0e96539fSCharles.Forsyth c.params = params; 459*0e96539fSCharles.Forsyth return c; 460*0e96539fSCharles.Forsyth} 461*0e96539fSCharles.Forsyth 462*0e96539fSCharles.ForsythContent.check(me: self ref Content, oks: list of ref Content): int 463*0e96539fSCharles.Forsyth{ 464*0e96539fSCharles.Forsyth if(oks == nil) 465*0e96539fSCharles.Forsyth return 1; 466*0e96539fSCharles.Forsyth g := str->tolower(me.generic); 467*0e96539fSCharles.Forsyth s := str->tolower(me.specific); 468*0e96539fSCharles.Forsyth for(; oks != nil; oks = tl oks){ 469*0e96539fSCharles.Forsyth ok := hd oks; 470*0e96539fSCharles.Forsyth if((ok.generic == g || ok.generic=="*") && 471*0e96539fSCharles.Forsyth (s == nil || ok.specific == s || ok.specific=="*")) 472*0e96539fSCharles.Forsyth return 1; 473*0e96539fSCharles.Forsyth } 474*0e96539fSCharles.Forsyth return 0; 475*0e96539fSCharles.Forsyth} 476*0e96539fSCharles.Forsyth 477*0e96539fSCharles.ForsythContent.text(c: self ref Content): string 478*0e96539fSCharles.Forsyth{ 479*0e96539fSCharles.Forsyth if((s := c.specific) != nil) 480*0e96539fSCharles.Forsyth s = c.generic+"/"+s; 481*0e96539fSCharles.Forsyth else 482*0e96539fSCharles.Forsyth s = c.generic; 483*0e96539fSCharles.Forsyth for(l := c.params; l != nil; l = tl l){ 484*0e96539fSCharles.Forsyth (n, v) := hd l; 485*0e96539fSCharles.Forsyth s += sys->sprint(";%s=%s", n, quote(v)); 486*0e96539fSCharles.Forsyth } 487*0e96539fSCharles.Forsyth return s; 488*0e96539fSCharles.Forsyth} 489*0e96539fSCharles.Forsyth 490*0e96539fSCharles.Forsyth# 491*0e96539fSCharles.Forsyth# should probably be in a Mime or HTTP module 492*0e96539fSCharles.Forsyth# 493*0e96539fSCharles.Forsyth 494*0e96539fSCharles.ForsythQuotable: con "()<>@,;:\\\"/[]?={} \t"; 495*0e96539fSCharles.Forsyth 496*0e96539fSCharles.Forsythquotable(s: string): int 497*0e96539fSCharles.Forsyth{ 498*0e96539fSCharles.Forsyth for(i := 0; i < len s; i++) 499*0e96539fSCharles.Forsyth if(str->in(s[i], Quotable)) 500*0e96539fSCharles.Forsyth return 1; 501*0e96539fSCharles.Forsyth return 0; 502*0e96539fSCharles.Forsyth} 503*0e96539fSCharles.Forsyth 504*0e96539fSCharles.Forsythquote(s: string): string 505*0e96539fSCharles.Forsyth{ 506*0e96539fSCharles.Forsyth if(!quotable(s)) 507*0e96539fSCharles.Forsyth return s; 508*0e96539fSCharles.Forsyth q := "\""; 509*0e96539fSCharles.Forsyth for(i := 0; i < len s; i++){ 510*0e96539fSCharles.Forsyth if(str->in(s[i], Quotable)) 511*0e96539fSCharles.Forsyth q[len q] = '\\'; 512*0e96539fSCharles.Forsyth q[len q] = s[i]; 513*0e96539fSCharles.Forsyth } 514*0e96539fSCharles.Forsyth q[len q] = '"'; 515*0e96539fSCharles.Forsyth return q; 516*0e96539fSCharles.Forsyth} 517*0e96539fSCharles.Forsyth 518*0e96539fSCharles.Forsythweekdays := array[] of { 519*0e96539fSCharles.Forsyth "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 520*0e96539fSCharles.Forsyth}; 521*0e96539fSCharles.Forsyth 522*0e96539fSCharles.Forsythmonths := array[] of { 523*0e96539fSCharles.Forsyth "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 524*0e96539fSCharles.Forsyth}; 525*0e96539fSCharles.Forsyth 526*0e96539fSCharles.Forsyth# print dates in the format 527*0e96539fSCharles.Forsyth# Wkd, DD Mon YYYY HH:MM:SS GMT 528*0e96539fSCharles.Forsyth 529*0e96539fSCharles.Forsythsec2date(t: int): string 530*0e96539fSCharles.Forsyth{ 531*0e96539fSCharles.Forsyth tm := daytime->gmt(t); 532*0e96539fSCharles.Forsyth return sys->sprint("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT", 533*0e96539fSCharles.Forsyth weekdays[tm.wday], tm.mday, months[tm.mon], tm.year+1900, 534*0e96539fSCharles.Forsyth tm.hour, tm.min, tm.sec); 535*0e96539fSCharles.Forsyth} 536*0e96539fSCharles.Forsyth 537*0e96539fSCharles.Forsyth# parse dates of formats 538*0e96539fSCharles.Forsyth# Wkd, DD Mon YYYY HH:MM:SS GMT 539*0e96539fSCharles.Forsyth# Weekday, DD-Mon-YY HH:MM:SS GMT 540*0e96539fSCharles.Forsyth# Wkd Mon ( D|DD) HH:MM:SS YYYY 541*0e96539fSCharles.Forsyth# plus anything similar 542*0e96539fSCharles.Forsyth 543*0e96539fSCharles.Forsythdate2sec(date: string): int 544*0e96539fSCharles.Forsyth{ 545*0e96539fSCharles.Forsyth tm := daytime->string2tm(date); 546*0e96539fSCharles.Forsyth if(tm == nil || tm.year < 70 || tm.zone != "GMT") 547*0e96539fSCharles.Forsyth t := 0; 548*0e96539fSCharles.Forsyth else 549*0e96539fSCharles.Forsyth t = daytime->tm2epoch(tm); 550*0e96539fSCharles.Forsyth return t; 551*0e96539fSCharles.Forsyth} 552*0e96539fSCharles.Forsyth 553*0e96539fSCharles.Forsythnow(): int 554*0e96539fSCharles.Forsyth{ 555*0e96539fSCharles.Forsyth return daytime->now(); 556*0e96539fSCharles.Forsyth} 557*0e96539fSCharles.Forsyth 558*0e96539fSCharles.Forsythtime(): string 559*0e96539fSCharles.Forsyth{ 560*0e96539fSCharles.Forsyth return sec2date(daytime->now()); 561*0e96539fSCharles.Forsyth} 562