xref: /inferno-os/appl/svc/webget/wgutils.b (revision fbc1184c08d18d5ac0f8763a058e015e95353341)
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