implement Cgi; include "sys.m"; sys: Sys; sprint: import sys;include "bufio.m"; bufio: Bufio; Iobuf: import bufio;include "string.m"; str: String;include "env.m"; env: Env;include "dict.m"; dict: Dictionary; Dict: import dict;include "cgi.m"; init(){ sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; str = load String String->PATH; env = load Env Env->PATH; dict = load Dictionary Dictionary->PATH;} Fields.get(f: self ref Fields, name: string): string{ for(l := f.l; l != nil; l = tl l) { (k, v, nil) := hd l; if(k == name) return v; } return nil;} Fields.getbytes(f: self ref Fields, name: string): array of byte{ for(l := f.l; l != nil; l = tl l) { (k, nil, v) := hd l; if(k == name) return v; } return nil;} Fields.getdefault(f: self ref Fields, name, default: string): string{ for(l := f.l; l != nil; l = tl l) { (k, v, nil) := hd l; if(k == name) return v; } return default;} Fields.getlist(f: self ref Fields, name: string): list of string{ r: list of string; for(l := f.l; l != nil; l = tl l) { (k, v, nil) := hd l; if(k == name) r = v :: r; } return r;} Fields.all(f: self ref Fields): list of (string, string, array of byte){ return f.l;} Fields.has(f: self ref Fields, name: string): int{ for(l := f.l; l != nil; l = tl l) if((hd l).t0 == name) return 1; return 0;} hex(c: int): int{ if(c >= '0' && c <= '9') return c - '0'; if(c >= 'A' && c <= 'F') c += 'a' - 'A'; if(c >= 'a' && c <= 'f') return 10 + c - 'a'; return -1;} decodebytes(s: string): array of byte{ sa := array of byte s; ra := array[len sa] of byte; si := 0; ri := 0; while(si < len sa) { c: byte; if(sa[si] == byte '%') { if(si+2 < len sa) { h1 := hex(int sa[si+1]); h2 := hex(int sa[si+2]); if(h1 < 0 || h2 < 0) return nil; c = byte ((h1 << 4) | h2); si += 3; } else return nil; } else if(sa[si] == byte '+') { c = byte ' '; si += 1; } else { c = sa[si]; si += 1; } ra[ri++] = c; } return ra[:ri];} decode(s: string): string{ return string decodebytes(s);} encodepath(s: string): string{ return _encode(s, "/");} encode(s: string): string{ return _encode(s, "");} _encode(s: string, okayspecial: string): string{ a := array of byte s; r := ""; okay := "a-zA-Z0-9*_.-"; for(i := 0; i < len a; i++) { c := int a[i]; if(str->in(c, okay) || str->in(c, okayspecial)) r += sprint("%c", c); else if(c == ' ') r += "+"; else r += sprint("%%%02X", c); } return r;} pack(l: list of (string, string)): string{ r := ""; first := 1; while(l != nil) { (k, v) := hd l; if(!first) r += sprint("&"); r += sprint("%s=%s", encode(k), encode(v)); l = tl l; first = 0; } return r;} htmlescape(s: string): string{ r := ""; for(i := 0; i < len s; i++) case s[i] { '<' => r += "<"; '>' => r += ">"; '&' => r += "&"; '"' => r += """; * => r += s[i:i+1]; } return r;} tokenize(s, delims: string): list of string{ if(s == "") return nil; (token, remain) := str->splitstrl(s, delims); if(remain != "") remain = remain[1:]; return token :: tokenize(remain, delims);} readstr(fd: ref Sys->FD, n: int): string{ a := array[0] of byte; buf := array[1024] of byte; while(n > 0) { want := len buf; if(want > n) want = n; have := sys->read(fd, buf, want); if(have == 0) # premature eof return nil; if(have < 0) # xxx warn return nil; anew := array[len a+have] of byte; anew[:] = a; anew[len a:] = buf[:have]; a = anew; n -= have; } return string a;} # xxxreadall(fd: ref Sys->FD): string{ a := array[0] of byte; buf := array[1024] of byte; for(;;) { have := sys->read(fd, buf, len buf); if(have == 0) break; if(have < 0) # xxx error return nil; anew := array[len a+have] of byte; anew[:] = a; anew[len a:] = buf[:have]; a = anew; } return string a;} unpackenv(): ref Fields{ qs := ""; method := env->getenv("REQUEST_METHOD"); if(method == nil || method == "GET" || method == "HEAD") { qs = env->getenv("QUERY_STRING"); } else if(method == "POST") { content := env->getenv("CONTENT_TYPE"); if(content != nil && content != "application/x-www-form-urlencoded") # xxx it is probably multipart/form-data (used for files) return nil; length := env->getenv("CONTENT_LENGTH"); if(length != nil) qs = readstr(sys->fildes(0), int length); else qs = readall(sys->fildes(0)); } return unpack(qs);} unpack(s: string): ref Fields{ l: list of (string, string, array of byte); pairs := tokenize(s, "&"); while(pairs != nil) { pair := hd pairs; pairs = tl pairs; (k, v) := str->splitstrl(pair, "="); if(v == nil) continue; v = v[1:]; k = decode(k); vd := decodebytes(v); l = (k, string vd, vd)::l; } return ref Fields(l);}
implement Cgi;
include "sys.m";
sys: Sys;
sprint: import sys;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "string.m";
str: String;
include "env.m";
env: Env;
include "dict.m";
dict: Dictionary;
Dict: import dict;
include "cgi.m";
init()
{
sys = load Sys Sys->PATH;
bufio = load Bufio Bufio->PATH;
str = load String String->PATH;
env = load Env Env->PATH;
dict = load Dictionary Dictionary->PATH;
}
Fields.get(f: self ref Fields, name: string): string
{
for(l := f.l; l != nil; l = tl l) {
(k, v, nil) := hd l;
if(k == name)
return v;
}
return nil;
}
Fields.getbytes(f: self ref Fields, name: string): array of byte
{
for(l := f.l; l != nil; l = tl l) {
(k, nil, v) := hd l;
if(k == name)
return v;
}
return nil;
}
Fields.getdefault(f: self ref Fields, name, default: string): string
{
for(l := f.l; l != nil; l = tl l) {
(k, v, nil) := hd l;
if(k == name)
return v;
}
return default;
}
Fields.getlist(f: self ref Fields, name: string): list of string
{
r: list of string;
for(l := f.l; l != nil; l = tl l) {
(k, v, nil) := hd l;
if(k == name)
r = v :: r;
}
return r;
}
Fields.all(f: self ref Fields): list of (string, string, array of byte)
{
return f.l;
}
Fields.has(f: self ref Fields, name: string): int
{
for(l := f.l; l != nil; l = tl l)
if((hd l).t0 == name)
return 1;
return 0;
}
hex(c: int): int
{
if(c >= '0' && c <= '9')
return c - '0';
if(c >= 'A' && c <= 'F')
c += 'a' - 'A';
if(c >= 'a' && c <= 'f')
return 10 + c - 'a';
return -1;
}
decodebytes(s: string): array of byte
{
sa := array of byte s;
ra := array[len sa] of byte;
si := 0;
ri := 0;
while(si < len sa) {
c: byte;
if(sa[si] == byte '%') {
if(si+2 < len sa) {
h1 := hex(int sa[si+1]);
h2 := hex(int sa[si+2]);
if(h1 < 0 || h2 < 0)
return nil;
c = byte ((h1 << 4) | h2);
si += 3;
} else
return nil;
} else if(sa[si] == byte '+') {
c = byte ' ';
si += 1;
} else {
c = sa[si];
si += 1;
}
ra[ri++] = c;
}
return ra[:ri];
}
decode(s: string): string
{
return string decodebytes(s);
}
encodepath(s: string): string
{
return _encode(s, "/");
}
encode(s: string): string
{
return _encode(s, "");
}
_encode(s: string, okayspecial: string): string
{
a := array of byte s;
r := "";
okay := "a-zA-Z0-9*_.-";
for(i := 0; i < len a; i++) {
c := int a[i];
if(str->in(c, okay) || str->in(c, okayspecial))
r += sprint("%c", c);
else if(c == ' ')
r += "+";
else
r += sprint("%%%02X", c);
}
return r;
}
pack(l: list of (string, string)): string
{
r := "";
first := 1;
while(l != nil) {
(k, v) := hd l;
if(!first)
r += sprint("&");
r += sprint("%s=%s", encode(k), encode(v));
l = tl l;
first = 0;
}
return r;
}
htmlescape(s: string): string
{
r := "";
for(i := 0; i < len s; i++)
case s[i] {
'<' => r += "<";
'>' => r += ">";
'&' => r += "&";
'"' => r += """;
* => r += s[i:i+1];
}
return r;
}
tokenize(s, delims: string): list of string
{
if(s == "")
return nil;
(token, remain) := str->splitstrl(s, delims);
if(remain != "")
remain = remain[1:];
return token :: tokenize(remain, delims);
}
readstr(fd: ref Sys->FD, n: int): string
{
a := array[0] of byte;
buf := array[1024] of byte;
while(n > 0) {
want := len buf;
if(want > n)
want = n;
have := sys->read(fd, buf, want);
if(have == 0)
# premature eof
return nil;
if(have < 0)
# xxx warn
return nil;
anew := array[len a+have] of byte;
anew[:] = a;
anew[len a:] = buf[:have];
a = anew;
n -= have;
}
return string a;
}
# xxx
readall(fd: ref Sys->FD): string
{
a := array[0] of byte;
buf := array[1024] of byte;
for(;;) {
have := sys->read(fd, buf, len buf);
if(have == 0)
break;
if(have < 0)
# xxx error
return nil;
anew := array[len a+have] of byte;
anew[:] = a;
anew[len a:] = buf[:have];
a = anew;
}
return string a;
}
unpackenv(): ref Fields
{
qs := "";
method := env->getenv("REQUEST_METHOD");
if(method == nil || method == "GET" || method == "HEAD") {
qs = env->getenv("QUERY_STRING");
} else if(method == "POST") {
content := env->getenv("CONTENT_TYPE");
if(content != nil && content != "application/x-www-form-urlencoded")
# xxx it is probably multipart/form-data (used for files)
return nil;
length := env->getenv("CONTENT_LENGTH");
if(length != nil)
qs = readstr(sys->fildes(0), int length);
else
qs = readall(sys->fildes(0));
}
return unpack(qs);
}
unpack(s: string): ref Fields
{
l: list of (string, string, array of byte);
pairs := tokenize(s, "&");
while(pairs != nil) {
pair := hd pairs;
pairs = tl pairs;
(k, v) := str->splitstrl(pair, "=");
if(v == nil)
continue;
v = v[1:];
k = decode(k);
vd := decodebytes(v);
l = (k, string vd, vd)::l;
}
return ref Fields(l);
}