1implement WebgetUtils; 2 3include "sys.m"; 4 sys: Sys; 5 6include "draw.m"; 7 8include "string.m"; 9 10include "bufio.m"; 11 12include "dial.m"; 13 14include "imagefile.m"; 15 readgif, readjpg, readxbitmap: RImagefile; 16 17include "image2enc.m"; 18 image2enc: Image2enc; 19 20include "message.m"; 21 22include "url.m"; 23 24include "wgutils.m"; 25 Iobuf: import B; 26 27include "strinttab.m"; 28 T: StringIntTab; 29 30Msg, Nameval: import M; 31ParsedUrl: import U; 32 33logfd: ref Sys->FD; 34 35# return from acceptmatch; and conv arg to readbody 36BadConv, NoConv, Gif2xcompressed, Jpeg2xcompressed, Xbm2xcompressed: con iota; 37 38# Both extensions and Content-Types can be in same table. 39# This array should be kept sorted 40mtypes := array[] of { T->StringInt 41 ("ai", ApplPostscript), 42 ("application/html", TextHtml), 43 ("application/pdf", ApplPdf), 44 ("application/postscript", ApplPostscript), 45 ("application/rtf", ApplRtf), 46 ("application/soap+xml", TextPlain), 47 ("application/x-html", TextHtml), 48 ("au", AudioBasic), 49 ("audio/au", AudioBasic), 50 ("audio/basic", AudioBasic), 51 ("bit", ImageXCompressed), 52 ("bit2", ImageXCompressed2), 53 ("eps", ApplPostscript), 54 ("gif", ImageGif), 55 ("htm", TextHtml), 56 ("html", TextHtml), 57 ("image/gif", ImageGif), 58 ("image/ief", ImageIef), 59 ("image/jpeg", ImageJpeg), 60 ("image/tiff", ImageTiff), 61 ("image/x-compressed", ImageXCompressed), 62 ("image/x-compressed2", ImageXCompressed2), 63 ("image/x-xbitmap", ImageXXBitmap), 64 ("jpe", ImageJpeg), 65 ("jpeg", ImageJpeg), 66 ("jpg", ImageJpeg), 67 ("pdf", ApplPdf), 68 ("ps", ApplPostscript), 69 ("text", TextPlain), 70 ("text/html", TextHtml), 71 ("text/plain", TextPlain), 72 ("text/x-html", TextHtml), 73 ("text/xml", TextXml), 74 ("tif", ImageTiff), 75 ("tiff", ImageTiff), 76 ("txt", TextPlain), 77 ("video/mpeg", VideoMpeg), 78 ("video/quicktime", VideoQuicktime), 79}; 80 81# following array must track media type def in wgutils.m 82mnames := array[] of { 83 "application/x-unknown", 84 "text/plain", 85 "text/html", 86 "application/postscript", 87 "application/rtf", 88 "application/pdf", 89 "image/jpeg", 90 "image/gif", 91 "image/ief", 92 "image/tiff", 93 "image/x-compressed", 94 "image/x-compressed2", 95 "image/x-xbitmap", 96 "audio/basic", 97 "video/mpeg", 98 "video/quicktime", 99 "application/soap+xml", 100 "text/xml" 101}; 102 103init(m: Message, s: String, b: Bufio, u: Url, di: Dial, lfd: ref Sys->FD) 104{ 105 sys = load Sys Sys->PATH; 106 107 M = m; 108 S = s; 109 B = b; 110 U = u; 111 DI = di; 112 logfd = lfd; 113 T = load StringIntTab StringIntTab->PATH; 114 readgif = load RImagefile RImagefile->READGIFPATH; 115 readjpg = load RImagefile RImagefile->READJPGPATH; 116 readxbitmap = load RImagefile RImagefile->READXBMPATH; 117 image2enc = load Image2enc Image2enc->PATH; 118 if(T == nil || readgif == nil || readjpg == nil || readxbitmap == nil || image2enc == nil) { 119 sys->fprint(sys->fildes(2), "webget: failed to load T, readgif, readjpg, readxbitmap, or imageremap: %r\n"); 120 return; 121 } 122 readgif->init(B); 123 readjpg->init(B); 124 readxbitmap->init(B); 125} 126 127# Return msg with error provoked by bad user action 128usererr(r: ref Req, msg: string) : ref Msg 129{ 130 m := Msg.newmsg(); 131 m.prefixline = sys->sprint("ERROR %s %s\n", r.reqid, msg); 132 m.bodylen = 0; 133 return m; 134} 135 136okprefix(r: ref Req, mrep: ref Msg) 137{ 138 (nil, sctype) := mrep.fieldval("content-type"); 139 if(sctype == "") 140 sctype = "text/html"; 141 else 142 sctype = canon_mtype(sctype); 143 (nil, cloc) := mrep.fieldval("content-location"); 144 if(cloc == "") 145 cloc = "unknown"; 146 mrep.prefixline = "OK " + string mrep.bodylen + " " + r.reqid + " " + sctype + " " + cloc +"\n"; 147} 148 149canon_mtype(s: string) : string 150{ 151 # lowercase, and remove possible parameter 152 ls := S->tolower(s); 153 (ts, nil) := S->splitl(ls, "; "); 154 return ts; 155} 156 157mediatype(s: string, dflt: int) : int 158{ 159 (fnd, val) := T->lookup(mtypes, canon_mtype(s)); 160 if(!fnd) 161 val = dflt; 162 return val; 163} 164 165acceptmatch(ctype: int, accept: string) : int 166{ 167 conv := BadConv; 168 (nil,l) := sys->tokenize(accept, ","); 169 while(l != nil) { 170 a := S->drop(hd l, " \t"); 171 mt := mediatype(a, -1); 172 match := (ctype == mt) || (a == "*/*") 173 || ((ctype == ImageXCompressed || ctype == ImageXCompressed2) 174 && (mt == ImageJpeg || mt == ImageGif || mt == ImageXXBitmap)); 175 if(match) { 176 if(ctype == ImageGif) 177 conv = Gif2xcompressed; 178 else if(ctype == ImageJpeg) 179 conv = Jpeg2xcompressed; 180 else if(ctype == ImageXXBitmap) 181 conv = Xbm2xcompressed; 182 else 183 conv = NoConv; 184 break; 185 } 186 l = tl l; 187 } 188 return conv; 189} 190 191# Get the body of the message whose header is in mrep, 192# if io != nil. 193# First check that the content type is acceptable. 194# Image types will get converted into Inferno compressed format. 195# If there is an error, return error string, else "". 196# If no error, mrep will contain content-encoding, content-location, 197# and content-type fields (guessed if they weren't orignally there). 198 199getdata(io: ref Iobuf, m: ref Msg, accept: string, url: ref ParsedUrl) : string 200{ 201 (efnd, etype) := m.fieldval("content-encoding"); 202 if(efnd) 203 return "content is encoded: " + etype; 204 ctype := UnknownType; 205 (tfnd, sctype) := m.fieldval("content-type"); 206 if(tfnd) 207 ctype = mediatype(sctype, UnknownType); 208 else { 209 # try to guess type from extension 210 sctype = "(unknown)"; 211 (nil, name) := S->splitr(url.path, "/"); 212 if(name != "") { 213 (f, ext) := S->splitr(name, "."); 214 if(f != "" && ext != "") { 215 ctype = mediatype(ext, UnknownType); 216 if(ctype != UnknownType) { 217 sctype = mnames[ctype]; 218 m.update("content-type", sctype); 219 } 220 } 221 } 222 } 223 transform := acceptmatch(ctype, accept); 224 if(transform == BadConv) 225 return "Unacceptable media type: " + sctype; 226 (clfnd, cloc) := m.fieldval("content-location"); 227 if(!clfnd) { 228 cloc = url.tostring(); 229 m.update("content-location", cloc); 230 } 231 if(transform != NoConv) { 232 rawimg: ref RImagefile->Rawimage; 233 err: string; 234 if(transform == Gif2xcompressed) 235 (rawimg, err) = readgif->read(io); 236 else if(transform == Jpeg2xcompressed) 237 (rawimg, err) = readjpg->read(io); 238 else if(transform == Xbm2xcompressed) 239 (rawimg, err) = readxbitmap->read(io); 240 # if gif file has multiple images, we are supposed to animate, 241 # but the first one should be there 242 if(err != "" && err != "ReadGIF: can't handle multiple images in file") 243 return "error converting image file: " + err; 244 (data, mask, e) := image2enc->image2enc(rawimg, 1); 245 if(e != "") 246 return "error remapping and encoding image file: " + e; 247 if(mask == nil) 248 sctype = "image/x-compressed"; 249 else { 250 sctype = "image/x-compressed2"; 251 newdata := array[len data + len mask] of byte; 252 newdata[0:] = data[0:]; 253 newdata[len data:] = mask[0:]; 254 data = newdata; 255 } 256 m.body = data; 257 m.bodylen = len data; 258 m.update("content-type", sctype); 259 m.update("content-length", string m.bodylen); 260 } 261 else { 262 # io will be nil if m came from cache 263 if(io != nil) { 264 e := m.readbody(io); 265 if(e != "") 266 return "reading body: " + e; 267 } 268 } 269 return ""; 270} 271 272# Change an accept spec from webget client into one we can send 273# to http server. This means image/x-compressed must be 274# changed into image formats we can handle: i.e., gif or jpeg 275fixaccept(a: string) : string 276{ 277 (nil,l) := sys->tokenize(a, ","); 278 ans := ""; 279 sep := ""; 280 while(l != nil) { 281 s := S->drop(hd l, " \t"); 282 if(s == "image/x-compressed") 283 ans += sep + "image/gif,image/jpeg,image/x-xbitmap"; 284 else 285 ans += sep + s; 286 sep = ","; 287 l = tl l; 288 } 289 if(ans == "") 290 ans = "*/*"; 291 return ans; 292} 293 294log(c: ref Fid, msg: string) 295{ 296 if(logfd != nil) { 297 # don't use print in case msg is longer than buf 298 s := ""; 299 if(c != nil) 300 s += (string c.fid) + ": "; 301 s += msg + "\n"; 302 b := array of byte s; 303 sys->write(logfd, b, len b); 304 } 305} 306