summaryrefslogtreecommitdiff
path: root/appl/svc/webget/wgutils.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/svc/webget/wgutils.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/svc/webget/wgutils.b')
-rw-r--r--appl/svc/webget/wgutils.b298
1 files changed, 298 insertions, 0 deletions
diff --git a/appl/svc/webget/wgutils.b b/appl/svc/webget/wgutils.b
new file mode 100644
index 00000000..fdf5c375
--- /dev/null
+++ b/appl/svc/webget/wgutils.b
@@ -0,0 +1,298 @@
+implement WebgetUtils;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "string.m";
+
+include "bufio.m";
+
+include "imagefile.m";
+ readgif, readjpg, readxbitmap: RImagefile;
+
+include "image2enc.m";
+ image2enc: Image2enc;
+
+include "message.m";
+
+include "url.m";
+
+include "wgutils.m";
+ Iobuf: import B;
+
+include "strinttab.m";
+ T: StringIntTab;
+
+Msg, Nameval: import M;
+ParsedUrl: import U;
+
+logfd: ref Sys->FD;
+
+# return from acceptmatch; and conv arg to readbody
+BadConv, NoConv, Gif2xcompressed, Jpeg2xcompressed, Xbm2xcompressed: con iota;
+
+# Both extensions and Content-Types can be in same table.
+# This array should be kept sorted
+mtypes := array[] of { T->StringInt
+ ("ai", ApplPostscript),
+ ("application/html", TextHtml),
+ ("application/pdf", ApplPdf),
+ ("application/postscript", ApplPostscript),
+ ("application/rtf", ApplRtf),
+ ("application/x-html", TextHtml),
+ ("au", AudioBasic),
+ ("audio/au", AudioBasic),
+ ("audio/basic", AudioBasic),
+ ("bit", ImageXCompressed),
+ ("bit2", ImageXCompressed2),
+ ("eps", ApplPostscript),
+ ("gif", ImageGif),
+ ("htm", TextHtml),
+ ("html", TextHtml),
+ ("image/gif", ImageGif),
+ ("image/ief", ImageIef),
+ ("image/jpeg", ImageJpeg),
+ ("image/tiff", ImageTiff),
+ ("image/x-compressed", ImageXCompressed),
+ ("image/x-compressed2", ImageXCompressed2),
+ ("image/x-xbitmap", ImageXXBitmap),
+ ("jpe", ImageJpeg),
+ ("jpeg", ImageJpeg),
+ ("jpg", ImageJpeg),
+ ("pdf", ApplPdf),
+ ("ps", ApplPostscript),
+ ("text", TextPlain),
+ ("text/html", TextHtml),
+ ("text/plain", TextPlain),
+ ("text/x-html", TextHtml),
+ ("tif", ImageTiff),
+ ("tiff", ImageTiff),
+ ("txt", TextPlain),
+ ("video/mpeg", VideoMpeg),
+ ("video/quicktime", VideoQuicktime),
+};
+
+# following array must track media type def in wgutils.m
+mnames := array[] of {
+ "application/x-unknown",
+ "text/plain",
+ "text/html",
+ "application/postscript",
+ "application/rtf",
+ "application/pdf",
+ "image/jpeg",
+ "image/gif",
+ "image/ief",
+ "image/tiff",
+ "image/x-compressed",
+ "image/x-compressed2",
+ "image/x-xbitmap",
+ "audio/basic",
+ "video/mpeg",
+ "video/quicktime"
+};
+
+init(m: Message, s: String, b: Bufio, u: Url, lfd: ref Sys->FD)
+{
+ sys = load Sys Sys->PATH;
+
+ M = m;
+ S = s;
+ B = b;
+ U = u;
+ logfd = lfd;
+ T = load StringIntTab StringIntTab->PATH;
+ readgif = load RImagefile RImagefile->READGIFPATH;
+ readjpg = load RImagefile RImagefile->READJPGPATH;
+ readxbitmap = load RImagefile RImagefile->READXBMPATH;
+ image2enc = load Image2enc Image2enc->PATH;
+ if(T == nil || readgif == nil || readjpg == nil || readxbitmap == nil || image2enc == nil) {
+ sys->fprint(sys->fildes(2), "webget: failed to load T, readgif, readjpg, readxbitmap, or imageremap: %r\n");
+ return;
+ }
+ readgif->init(B);
+ readjpg->init(B);
+ readxbitmap->init(B);
+}
+
+# Return msg with error provoked by bad user action
+usererr(r: ref Req, msg: string) : ref Msg
+{
+ m := Msg.newmsg();
+ m.prefixline = sys->sprint("ERROR %s %s", r.reqid, msg);
+ m.bodylen = 0;
+ return m;
+}
+
+okprefix(r: ref Req, mrep: ref Msg)
+{
+ (nil, sctype) := mrep.fieldval("content-type");
+ if(sctype == "")
+ sctype = "text/html";
+ else
+ sctype = canon_mtype(sctype);
+ (nil, cloc) := mrep.fieldval("content-location");
+ if(cloc == "")
+ cloc = "unknown";
+ mrep.prefixline = "OK " + string mrep.bodylen + " " + r.reqid + " " + sctype + " " + cloc;
+}
+
+canon_mtype(s: string) : string
+{
+ # lowercase, and remove possible parameter
+ ls := S->tolower(s);
+ (ts, nil) := S->splitl(ls, "; ");
+ return ts;
+}
+
+mediatype(s: string, dflt: int) : int
+{
+ (fnd, val) := T->lookup(mtypes, canon_mtype(s));
+ if(!fnd)
+ val = dflt;
+ return val;
+}
+
+acceptmatch(ctype: int, accept: string) : int
+{
+ conv := BadConv;
+ (nil,l) := sys->tokenize(accept, ",");
+ while(l != nil) {
+ a := S->drop(hd l, " \t");
+ mt := mediatype(a, -1);
+ match := (ctype == mt) || (a == "*/*")
+ || ((ctype == ImageXCompressed || ctype == ImageXCompressed2)
+ && (mt == ImageJpeg || mt == ImageGif || mt == ImageXXBitmap));
+ if(match) {
+ if(ctype == ImageGif)
+ conv = Gif2xcompressed;
+ else if(ctype == ImageJpeg)
+ conv = Jpeg2xcompressed;
+ else if(ctype == ImageXXBitmap)
+ conv = Xbm2xcompressed;
+ else
+ conv = NoConv;
+ break;
+ }
+ l = tl l;
+ }
+ return conv;
+}
+
+# Get the body of the message whose header is in mrep,
+# if io != nil.
+# First check that the content type is acceptable.
+# Image types will get converted into Inferno compressed format.
+# If there is an error, return error string, else "".
+# If no error, mrep will contain content-encoding, content-location,
+# and content-type fields (guessed if they weren't orignally there).
+
+getdata(io: ref Iobuf, m: ref Msg, accept: string, url: ref ParsedUrl) : string
+{
+ (efnd, etype) := m.fieldval("content-encoding");
+ if(efnd)
+ return "content is encoded: " + etype;
+ ctype := UnknownType;
+ (tfnd, sctype) := m.fieldval("content-type");
+ if(tfnd)
+ ctype = mediatype(sctype, UnknownType);
+ else {
+ # try to guess type from extension
+ sctype = "(unknown)";
+ (nil, name) := S->splitr(url.path, "/");
+ if(name != "") {
+ (f, ext) := S->splitr(name, ".");
+ if(f != "" && ext != "") {
+ ctype = mediatype(ext, UnknownType);
+ if(ctype != UnknownType) {
+ sctype = mnames[ctype];
+ m.update("content-type", sctype);
+ }
+ }
+ }
+ }
+ transform := acceptmatch(ctype, accept);
+ if(transform == BadConv)
+ return "Unacceptable media type: " + sctype;
+ (clfnd, cloc) := m.fieldval("content-location");
+ if(!clfnd) {
+ cloc = url.tostring();
+ m.update("content-location", cloc);
+ }
+ if(transform != NoConv) {
+ rawimg: ref RImagefile->Rawimage;
+ err: string;
+ if(transform == Gif2xcompressed)
+ (rawimg, err) = readgif->read(io);
+ else if(transform == Jpeg2xcompressed)
+ (rawimg, err) = readjpg->read(io);
+ else if(transform == Xbm2xcompressed)
+ (rawimg, err) = readxbitmap->read(io);
+ # if gif file has multiple images, we are supposed to animate,
+ # but the first one should be there
+ if(err != "" && err != "ReadGIF: can't handle multiple images in file")
+ return "error converting image file: " + err;
+ (data, mask, e) := image2enc->image2enc(rawimg, 1);
+ if(e != "")
+ return "error remapping and encoding image file: " + e;
+ if(mask == nil)
+ sctype = "image/x-compressed";
+ else {
+ sctype = "image/x-compressed2";
+ newdata := array[len data + len mask] of byte;
+ newdata[0:] = data[0:];
+ newdata[len data:] = mask[0:];
+ data = newdata;
+ }
+ m.body = data;
+ m.bodylen = len data;
+ m.update("content-type", sctype);
+ m.update("content-length", string m.bodylen);
+ }
+ else {
+ # io will be nil if m came from cache
+ if(io != nil) {
+ e := m.readbody(io);
+ if(e != "")
+ return "reading body: " + e;
+ }
+ }
+ return "";
+}
+
+# Change an accept spec from webget client into one we can send
+# to http server. This means image/x-compressed must be
+# changed into image formats we can handle: i.e., gif or jpeg
+fixaccept(a: string) : string
+{
+ (nil,l) := sys->tokenize(a, ",");
+ ans := "";
+ sep := "";
+ while(l != nil) {
+ s := S->drop(hd l, " \t");
+ if(s == "image/x-compressed")
+ ans += sep + "image/gif,image/jpeg,image/x-xbitmap";
+ else
+ ans += sep + s;
+ sep = ",";
+ l = tl l;
+ }
+ if(ans == "")
+ ans = "*/*";
+ return ans;
+}
+
+log(c: ref Fid, msg: string)
+{
+ if(logfd != nil) {
+ # don't use print in case msg is longer than buf
+ s := "";
+ if(c != nil)
+ s += (string c.fid) + ": ";
+ s += msg + "\n";
+ b := array of byte s;
+ sys->write(logfd, b, len b);
+ }
+}