137da2899SCharles.Forsythimplement WebgetUtils; 237da2899SCharles.Forsyth 337da2899SCharles.Forsythinclude "sys.m"; 437da2899SCharles.Forsyth sys: Sys; 537da2899SCharles.Forsyth 637da2899SCharles.Forsythinclude "draw.m"; 737da2899SCharles.Forsyth 837da2899SCharles.Forsythinclude "string.m"; 937da2899SCharles.Forsyth 1037da2899SCharles.Forsythinclude "bufio.m"; 1137da2899SCharles.Forsyth 12*fbc1184cSCharles Forsythinclude "dial.m"; 13*fbc1184cSCharles Forsyth 1437da2899SCharles.Forsythinclude "imagefile.m"; 1537da2899SCharles.Forsyth readgif, readjpg, readxbitmap: RImagefile; 1637da2899SCharles.Forsyth 1737da2899SCharles.Forsythinclude "image2enc.m"; 1837da2899SCharles.Forsyth image2enc: Image2enc; 1937da2899SCharles.Forsyth 2037da2899SCharles.Forsythinclude "message.m"; 2137da2899SCharles.Forsyth 2237da2899SCharles.Forsythinclude "url.m"; 2337da2899SCharles.Forsyth 2437da2899SCharles.Forsythinclude "wgutils.m"; 2537da2899SCharles.Forsyth Iobuf: import B; 2637da2899SCharles.Forsyth 2737da2899SCharles.Forsythinclude "strinttab.m"; 2837da2899SCharles.Forsyth T: StringIntTab; 2937da2899SCharles.Forsyth 3037da2899SCharles.ForsythMsg, Nameval: import M; 3137da2899SCharles.ForsythParsedUrl: import U; 3237da2899SCharles.Forsyth 3337da2899SCharles.Forsythlogfd: ref Sys->FD; 3437da2899SCharles.Forsyth 3537da2899SCharles.Forsyth# return from acceptmatch; and conv arg to readbody 3637da2899SCharles.ForsythBadConv, NoConv, Gif2xcompressed, Jpeg2xcompressed, Xbm2xcompressed: con iota; 3737da2899SCharles.Forsyth 3837da2899SCharles.Forsyth# Both extensions and Content-Types can be in same table. 3937da2899SCharles.Forsyth# This array should be kept sorted 4037da2899SCharles.Forsythmtypes := array[] of { T->StringInt 4137da2899SCharles.Forsyth ("ai", ApplPostscript), 4237da2899SCharles.Forsyth ("application/html", TextHtml), 4337da2899SCharles.Forsyth ("application/pdf", ApplPdf), 4437da2899SCharles.Forsyth ("application/postscript", ApplPostscript), 4537da2899SCharles.Forsyth ("application/rtf", ApplRtf), 466e425a9dSCharles.Forsyth ("application/soap+xml", TextPlain), 4737da2899SCharles.Forsyth ("application/x-html", TextHtml), 4837da2899SCharles.Forsyth ("au", AudioBasic), 4937da2899SCharles.Forsyth ("audio/au", AudioBasic), 5037da2899SCharles.Forsyth ("audio/basic", AudioBasic), 5137da2899SCharles.Forsyth ("bit", ImageXCompressed), 5237da2899SCharles.Forsyth ("bit2", ImageXCompressed2), 5337da2899SCharles.Forsyth ("eps", ApplPostscript), 5437da2899SCharles.Forsyth ("gif", ImageGif), 5537da2899SCharles.Forsyth ("htm", TextHtml), 5637da2899SCharles.Forsyth ("html", TextHtml), 5737da2899SCharles.Forsyth ("image/gif", ImageGif), 5837da2899SCharles.Forsyth ("image/ief", ImageIef), 5937da2899SCharles.Forsyth ("image/jpeg", ImageJpeg), 6037da2899SCharles.Forsyth ("image/tiff", ImageTiff), 6137da2899SCharles.Forsyth ("image/x-compressed", ImageXCompressed), 6237da2899SCharles.Forsyth ("image/x-compressed2", ImageXCompressed2), 6337da2899SCharles.Forsyth ("image/x-xbitmap", ImageXXBitmap), 6437da2899SCharles.Forsyth ("jpe", ImageJpeg), 6537da2899SCharles.Forsyth ("jpeg", ImageJpeg), 6637da2899SCharles.Forsyth ("jpg", ImageJpeg), 6737da2899SCharles.Forsyth ("pdf", ApplPdf), 6837da2899SCharles.Forsyth ("ps", ApplPostscript), 6937da2899SCharles.Forsyth ("text", TextPlain), 7037da2899SCharles.Forsyth ("text/html", TextHtml), 7137da2899SCharles.Forsyth ("text/plain", TextPlain), 7237da2899SCharles.Forsyth ("text/x-html", TextHtml), 736e425a9dSCharles.Forsyth ("text/xml", TextXml), 7437da2899SCharles.Forsyth ("tif", ImageTiff), 7537da2899SCharles.Forsyth ("tiff", ImageTiff), 7637da2899SCharles.Forsyth ("txt", TextPlain), 7737da2899SCharles.Forsyth ("video/mpeg", VideoMpeg), 7837da2899SCharles.Forsyth ("video/quicktime", VideoQuicktime), 7937da2899SCharles.Forsyth}; 8037da2899SCharles.Forsyth 8137da2899SCharles.Forsyth# following array must track media type def in wgutils.m 8237da2899SCharles.Forsythmnames := array[] of { 8337da2899SCharles.Forsyth "application/x-unknown", 8437da2899SCharles.Forsyth "text/plain", 8537da2899SCharles.Forsyth "text/html", 8637da2899SCharles.Forsyth "application/postscript", 8737da2899SCharles.Forsyth "application/rtf", 8837da2899SCharles.Forsyth "application/pdf", 8937da2899SCharles.Forsyth "image/jpeg", 9037da2899SCharles.Forsyth "image/gif", 9137da2899SCharles.Forsyth "image/ief", 9237da2899SCharles.Forsyth "image/tiff", 9337da2899SCharles.Forsyth "image/x-compressed", 9437da2899SCharles.Forsyth "image/x-compressed2", 9537da2899SCharles.Forsyth "image/x-xbitmap", 9637da2899SCharles.Forsyth "audio/basic", 9737da2899SCharles.Forsyth "video/mpeg", 986e425a9dSCharles.Forsyth "video/quicktime", 996e425a9dSCharles.Forsyth "application/soap+xml", 1006e425a9dSCharles.Forsyth "text/xml" 10137da2899SCharles.Forsyth}; 10237da2899SCharles.Forsyth 103*fbc1184cSCharles Forsythinit(m: Message, s: String, b: Bufio, u: Url, di: Dial, lfd: ref Sys->FD) 10437da2899SCharles.Forsyth{ 10537da2899SCharles.Forsyth sys = load Sys Sys->PATH; 10637da2899SCharles.Forsyth 10737da2899SCharles.Forsyth M = m; 10837da2899SCharles.Forsyth S = s; 10937da2899SCharles.Forsyth B = b; 11037da2899SCharles.Forsyth U = u; 111*fbc1184cSCharles Forsyth DI = di; 11237da2899SCharles.Forsyth logfd = lfd; 11337da2899SCharles.Forsyth T = load StringIntTab StringIntTab->PATH; 11437da2899SCharles.Forsyth readgif = load RImagefile RImagefile->READGIFPATH; 11537da2899SCharles.Forsyth readjpg = load RImagefile RImagefile->READJPGPATH; 11637da2899SCharles.Forsyth readxbitmap = load RImagefile RImagefile->READXBMPATH; 11737da2899SCharles.Forsyth image2enc = load Image2enc Image2enc->PATH; 11837da2899SCharles.Forsyth if(T == nil || readgif == nil || readjpg == nil || readxbitmap == nil || image2enc == nil) { 11937da2899SCharles.Forsyth sys->fprint(sys->fildes(2), "webget: failed to load T, readgif, readjpg, readxbitmap, or imageremap: %r\n"); 12037da2899SCharles.Forsyth return; 12137da2899SCharles.Forsyth } 12237da2899SCharles.Forsyth readgif->init(B); 12337da2899SCharles.Forsyth readjpg->init(B); 12437da2899SCharles.Forsyth readxbitmap->init(B); 12537da2899SCharles.Forsyth} 12637da2899SCharles.Forsyth 12737da2899SCharles.Forsyth# Return msg with error provoked by bad user action 12837da2899SCharles.Forsythusererr(r: ref Req, msg: string) : ref Msg 12937da2899SCharles.Forsyth{ 13037da2899SCharles.Forsyth m := Msg.newmsg(); 1316e425a9dSCharles.Forsyth m.prefixline = sys->sprint("ERROR %s %s\n", r.reqid, msg); 13237da2899SCharles.Forsyth m.bodylen = 0; 13337da2899SCharles.Forsyth return m; 13437da2899SCharles.Forsyth} 13537da2899SCharles.Forsyth 13637da2899SCharles.Forsythokprefix(r: ref Req, mrep: ref Msg) 13737da2899SCharles.Forsyth{ 13837da2899SCharles.Forsyth (nil, sctype) := mrep.fieldval("content-type"); 13937da2899SCharles.Forsyth if(sctype == "") 14037da2899SCharles.Forsyth sctype = "text/html"; 14137da2899SCharles.Forsyth else 14237da2899SCharles.Forsyth sctype = canon_mtype(sctype); 14337da2899SCharles.Forsyth (nil, cloc) := mrep.fieldval("content-location"); 14437da2899SCharles.Forsyth if(cloc == "") 14537da2899SCharles.Forsyth cloc = "unknown"; 1466e425a9dSCharles.Forsyth mrep.prefixline = "OK " + string mrep.bodylen + " " + r.reqid + " " + sctype + " " + cloc +"\n"; 14737da2899SCharles.Forsyth} 14837da2899SCharles.Forsyth 14937da2899SCharles.Forsythcanon_mtype(s: string) : string 15037da2899SCharles.Forsyth{ 15137da2899SCharles.Forsyth # lowercase, and remove possible parameter 15237da2899SCharles.Forsyth ls := S->tolower(s); 15337da2899SCharles.Forsyth (ts, nil) := S->splitl(ls, "; "); 15437da2899SCharles.Forsyth return ts; 15537da2899SCharles.Forsyth} 15637da2899SCharles.Forsyth 15737da2899SCharles.Forsythmediatype(s: string, dflt: int) : int 15837da2899SCharles.Forsyth{ 15937da2899SCharles.Forsyth (fnd, val) := T->lookup(mtypes, canon_mtype(s)); 16037da2899SCharles.Forsyth if(!fnd) 16137da2899SCharles.Forsyth val = dflt; 16237da2899SCharles.Forsyth return val; 16337da2899SCharles.Forsyth} 16437da2899SCharles.Forsyth 16537da2899SCharles.Forsythacceptmatch(ctype: int, accept: string) : int 16637da2899SCharles.Forsyth{ 16737da2899SCharles.Forsyth conv := BadConv; 16837da2899SCharles.Forsyth (nil,l) := sys->tokenize(accept, ","); 16937da2899SCharles.Forsyth while(l != nil) { 17037da2899SCharles.Forsyth a := S->drop(hd l, " \t"); 17137da2899SCharles.Forsyth mt := mediatype(a, -1); 17237da2899SCharles.Forsyth match := (ctype == mt) || (a == "*/*") 17337da2899SCharles.Forsyth || ((ctype == ImageXCompressed || ctype == ImageXCompressed2) 17437da2899SCharles.Forsyth && (mt == ImageJpeg || mt == ImageGif || mt == ImageXXBitmap)); 17537da2899SCharles.Forsyth if(match) { 17637da2899SCharles.Forsyth if(ctype == ImageGif) 17737da2899SCharles.Forsyth conv = Gif2xcompressed; 17837da2899SCharles.Forsyth else if(ctype == ImageJpeg) 17937da2899SCharles.Forsyth conv = Jpeg2xcompressed; 18037da2899SCharles.Forsyth else if(ctype == ImageXXBitmap) 18137da2899SCharles.Forsyth conv = Xbm2xcompressed; 18237da2899SCharles.Forsyth else 18337da2899SCharles.Forsyth conv = NoConv; 18437da2899SCharles.Forsyth break; 18537da2899SCharles.Forsyth } 18637da2899SCharles.Forsyth l = tl l; 18737da2899SCharles.Forsyth } 18837da2899SCharles.Forsyth return conv; 18937da2899SCharles.Forsyth} 19037da2899SCharles.Forsyth 19137da2899SCharles.Forsyth# Get the body of the message whose header is in mrep, 19237da2899SCharles.Forsyth# if io != nil. 19337da2899SCharles.Forsyth# First check that the content type is acceptable. 19437da2899SCharles.Forsyth# Image types will get converted into Inferno compressed format. 19537da2899SCharles.Forsyth# If there is an error, return error string, else "". 19637da2899SCharles.Forsyth# If no error, mrep will contain content-encoding, content-location, 19737da2899SCharles.Forsyth# and content-type fields (guessed if they weren't orignally there). 19837da2899SCharles.Forsyth 19937da2899SCharles.Forsythgetdata(io: ref Iobuf, m: ref Msg, accept: string, url: ref ParsedUrl) : string 20037da2899SCharles.Forsyth{ 20137da2899SCharles.Forsyth (efnd, etype) := m.fieldval("content-encoding"); 20237da2899SCharles.Forsyth if(efnd) 20337da2899SCharles.Forsyth return "content is encoded: " + etype; 20437da2899SCharles.Forsyth ctype := UnknownType; 20537da2899SCharles.Forsyth (tfnd, sctype) := m.fieldval("content-type"); 20637da2899SCharles.Forsyth if(tfnd) 20737da2899SCharles.Forsyth ctype = mediatype(sctype, UnknownType); 20837da2899SCharles.Forsyth else { 20937da2899SCharles.Forsyth # try to guess type from extension 21037da2899SCharles.Forsyth sctype = "(unknown)"; 21137da2899SCharles.Forsyth (nil, name) := S->splitr(url.path, "/"); 21237da2899SCharles.Forsyth if(name != "") { 21337da2899SCharles.Forsyth (f, ext) := S->splitr(name, "."); 21437da2899SCharles.Forsyth if(f != "" && ext != "") { 21537da2899SCharles.Forsyth ctype = mediatype(ext, UnknownType); 21637da2899SCharles.Forsyth if(ctype != UnknownType) { 21737da2899SCharles.Forsyth sctype = mnames[ctype]; 21837da2899SCharles.Forsyth m.update("content-type", sctype); 21937da2899SCharles.Forsyth } 22037da2899SCharles.Forsyth } 22137da2899SCharles.Forsyth } 22237da2899SCharles.Forsyth } 22337da2899SCharles.Forsyth transform := acceptmatch(ctype, accept); 22437da2899SCharles.Forsyth if(transform == BadConv) 22537da2899SCharles.Forsyth return "Unacceptable media type: " + sctype; 22637da2899SCharles.Forsyth (clfnd, cloc) := m.fieldval("content-location"); 22737da2899SCharles.Forsyth if(!clfnd) { 22837da2899SCharles.Forsyth cloc = url.tostring(); 22937da2899SCharles.Forsyth m.update("content-location", cloc); 23037da2899SCharles.Forsyth } 23137da2899SCharles.Forsyth if(transform != NoConv) { 23237da2899SCharles.Forsyth rawimg: ref RImagefile->Rawimage; 23337da2899SCharles.Forsyth err: string; 23437da2899SCharles.Forsyth if(transform == Gif2xcompressed) 23537da2899SCharles.Forsyth (rawimg, err) = readgif->read(io); 23637da2899SCharles.Forsyth else if(transform == Jpeg2xcompressed) 23737da2899SCharles.Forsyth (rawimg, err) = readjpg->read(io); 23837da2899SCharles.Forsyth else if(transform == Xbm2xcompressed) 23937da2899SCharles.Forsyth (rawimg, err) = readxbitmap->read(io); 24037da2899SCharles.Forsyth # if gif file has multiple images, we are supposed to animate, 24137da2899SCharles.Forsyth # but the first one should be there 24237da2899SCharles.Forsyth if(err != "" && err != "ReadGIF: can't handle multiple images in file") 24337da2899SCharles.Forsyth return "error converting image file: " + err; 24437da2899SCharles.Forsyth (data, mask, e) := image2enc->image2enc(rawimg, 1); 24537da2899SCharles.Forsyth if(e != "") 24637da2899SCharles.Forsyth return "error remapping and encoding image file: " + e; 24737da2899SCharles.Forsyth if(mask == nil) 24837da2899SCharles.Forsyth sctype = "image/x-compressed"; 24937da2899SCharles.Forsyth else { 25037da2899SCharles.Forsyth sctype = "image/x-compressed2"; 25137da2899SCharles.Forsyth newdata := array[len data + len mask] of byte; 25237da2899SCharles.Forsyth newdata[0:] = data[0:]; 25337da2899SCharles.Forsyth newdata[len data:] = mask[0:]; 25437da2899SCharles.Forsyth data = newdata; 25537da2899SCharles.Forsyth } 25637da2899SCharles.Forsyth m.body = data; 25737da2899SCharles.Forsyth m.bodylen = len data; 25837da2899SCharles.Forsyth m.update("content-type", sctype); 25937da2899SCharles.Forsyth m.update("content-length", string m.bodylen); 26037da2899SCharles.Forsyth } 26137da2899SCharles.Forsyth else { 26237da2899SCharles.Forsyth # io will be nil if m came from cache 26337da2899SCharles.Forsyth if(io != nil) { 26437da2899SCharles.Forsyth e := m.readbody(io); 26537da2899SCharles.Forsyth if(e != "") 26637da2899SCharles.Forsyth return "reading body: " + e; 26737da2899SCharles.Forsyth } 26837da2899SCharles.Forsyth } 26937da2899SCharles.Forsyth return ""; 27037da2899SCharles.Forsyth} 27137da2899SCharles.Forsyth 27237da2899SCharles.Forsyth# Change an accept spec from webget client into one we can send 27337da2899SCharles.Forsyth# to http server. This means image/x-compressed must be 27437da2899SCharles.Forsyth# changed into image formats we can handle: i.e., gif or jpeg 27537da2899SCharles.Forsythfixaccept(a: string) : string 27637da2899SCharles.Forsyth{ 27737da2899SCharles.Forsyth (nil,l) := sys->tokenize(a, ","); 27837da2899SCharles.Forsyth ans := ""; 27937da2899SCharles.Forsyth sep := ""; 28037da2899SCharles.Forsyth while(l != nil) { 28137da2899SCharles.Forsyth s := S->drop(hd l, " \t"); 28237da2899SCharles.Forsyth if(s == "image/x-compressed") 28337da2899SCharles.Forsyth ans += sep + "image/gif,image/jpeg,image/x-xbitmap"; 28437da2899SCharles.Forsyth else 28537da2899SCharles.Forsyth ans += sep + s; 28637da2899SCharles.Forsyth sep = ","; 28737da2899SCharles.Forsyth l = tl l; 28837da2899SCharles.Forsyth } 28937da2899SCharles.Forsyth if(ans == "") 29037da2899SCharles.Forsyth ans = "*/*"; 29137da2899SCharles.Forsyth return ans; 29237da2899SCharles.Forsyth} 29337da2899SCharles.Forsyth 29437da2899SCharles.Forsythlog(c: ref Fid, msg: string) 29537da2899SCharles.Forsyth{ 29637da2899SCharles.Forsyth if(logfd != nil) { 29737da2899SCharles.Forsyth # don't use print in case msg is longer than buf 29837da2899SCharles.Forsyth s := ""; 29937da2899SCharles.Forsyth if(c != nil) 30037da2899SCharles.Forsyth s += (string c.fid) + ": "; 30137da2899SCharles.Forsyth s += msg + "\n"; 30237da2899SCharles.Forsyth b := array of byte s; 30337da2899SCharles.Forsyth sys->write(logfd, b, len b); 30437da2899SCharles.Forsyth } 30537da2899SCharles.Forsyth} 306