From: Fredrik Tolf Date: Wed, 14 Mar 2012 20:34:22 +0000 (+0100) Subject: Merge branch 'master' into python3 X-Git-Url: http://dolda2000.com/gitweb/?p=wrw.git;a=commitdiff_plain;h=8b7cf2787e8cf2ee716c709d904f66c651cfed89;hp=c035fcc645f6bc177fe8da86058b21d24d253f72 Merge branch 'master' into python3 Conflicts: wrw/__init__.py --- diff --git a/wrw/__init__.py b/wrw/__init__.py index d4b0a63..6458f64 100644 --- a/wrw/__init__.py +++ b/wrw/__init__.py @@ -1,5 +1,6 @@ __all__ = ["wsgiwrap", "restart", "cookie", "formdata"] +from . import proto from .util import wsgiwrap, formparams, persession, sessiondata, autodirty, manudirty from .dispatch import restart from . import cookie diff --git a/wrw/form.py b/wrw/form.py index dd1d5e9..d089fd7 100644 --- a/wrw/form.py +++ b/wrw/form.py @@ -1,4 +1,5 @@ import cgi +from . import proto __all__ = ["formdata"] @@ -36,5 +37,143 @@ class formwrap(object): def values(self): return [val for key, val in self.items()] +class badmultipart(Exception): + pass + +class formpart(object): + def __init__(self, form): + self.form = form + self.buf = "" + self.eof = False + self.head = {} + + def parsehead(self): + pass + + def fillbuf(self, sz): + req = self.form.req + mboundary = "\r\n--" + self.form.boundary + "\r\n" + lboundary = "\r\n--" + self.form.boundary + "--\r\n" + while not self.eof: + p = self.form.buf.find(mboundary) + if p >= 0: + self.buf += self.form.buf[:p] + self.form.buf = self.form.buf[p + len(mboundary):] + self.eof = True + break + p = self.form.buf.find(lboundary) + if p >= 0: + self.buf += self.form.buf[:p] + self.form.buf = self.form.buf[p + len(lboundary):] + self.eof = True + self.form.eof = True + break + self.buf += self.form.buf[:-len(lboundary)] + self.form.buf = self.form.buf[-len(lboundary):] + if sz >= 0 and len(self.buf) >= sz: + break + while len(self.form.buf) <= len(lboundary): + ret = req.env["wsgi.input"].read(8192) + if ret == "": + raise badmultipart("Missing last multipart boundary") + self.form.buf += ret + + def read(self, limit = -1): + self.fillbuf(limit) + if limit >= 0: + ret = self.buf[:limit] + self.buf = self.buf[limit:] + else: + ret = self.buf + self.buf = "" + return ret + + def readline(self, limit = -1): + last = 0 + while True: + p = self.buf.find('\n', last) + if p < 0: + if self.eof: + ret = self.buf + self.buf = "" + return ret + last = len(self.buf) + self.fillbuf(last + 128) + else: + ret = self.buf[:p + 1] + self.buf = self.buf[p + 1:] + return ret + + def close(self): + self.fillbuf(-1) + + def __enter__(self): + return self + + def __exit__(self, *excinfo): + return False + + def parsehead(self): + def headline(): + ln = self.readline(256) + if ln[-1] != '\n': + raise badmultipart("Too long header line in part") + return ln.rstrip() + + ln = headline() + while True: + if ln == "": + break + buf = ln + while True: + ln = headline() + if not ln[1:].isspace(): + break + buf += ln.lstrip() + p = buf.find(':') + if p < 0: + raise badmultipart("Malformed multipart header line") + self.head[buf[:p].strip().lower()] = buf[p + 1:].lstrip() + + val, par = proto.pmimehead(self.head.get("content-disposition", "")) + if val != "form-data": + raise badmultipart("Unexpected Content-Disposition in form part: %r" % val) + if not "name" in par: + raise badmultipart("Missing name in form part") + self.name = par["name"] + self.filename = par.get("filename") + val, par = proto.pmimehead(self.head.get("content-type", "")) + self.ctype = val + self.charset = par.get("charset") + encoding = self.head.get("content-transfer-encoding", "binary") + if encoding != "binary": + raise badmultipart("Form part uses unexpected transfer encoding: %r" % encoding) + +class multipart(object): + def __init__(self, req): + val, par = proto.pmimehead(req.ihead.get("Content-Type", "")) + if req.method != "POST" or val != "multipart/form-data": + raise badmultipart("Request is not a multipart form") + if "boundary" not in par: + raise badmultipart("Multipart form lacks boundary") + self.boundary = par["boundary"] + self.req = req + self.buf = "\r\n" + self.eof = False + self.lastpart = formpart(self) + self.lastpart.close() + + def __iter__(self): + return self + + def next(self): + if not self.lastpart.eof: + raise RuntimeError("All form parts must be read entirely") + if self.eof: + raise StopIteration() + self.lastpart = formpart(self) + self.lastpart.parsehead() + return self.lastpart + def formdata(req): return req.item(formwrap) diff --git a/wrw/proto.py b/wrw/proto.py index 6b43396..7df66ff 100644 --- a/wrw/proto.py +++ b/wrw/proto.py @@ -17,6 +17,55 @@ def phttpdate(dstr): tz = (((tz / 100) * 60) + (tz % 100)) * 60 return time.mktime(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz - time.altzone +def pmimehead(hstr): + def pws(p): + while p < len(hstr) and hstr[p].isspace(): + p += 1 + return p + def token(p, sep): + buf = "" + p = pws(p) + if p >= len(hstr): + return "", p + if hstr[p] == '"': + p += 1 + while p < len(hstr): + if hstr[p] == '\\': + p += 1 + if p < len(hstr): + buf += hstr[p] + p += 1 + else: + break + elif hstr[p] == '"': + p += 1 + break + else: + buf += hstr[p] + p += 1 + return buf, pws(p) + else: + while p < len(hstr): + if hstr[p] in sep: + break + buf += hstr[p] + p += 1 + return buf.strip(), pws(p) + p = 0 + val, p = token(p, ";") + pars = {} + while p < len(hstr): + if hstr[p] != ';': + break + p += 1 + k, p = token(p, "=") + if k == "" or hstr[p:p + 1] != '=': + break + p += 1 + v, p = token(p, ';') + pars[k.lower()] = v + return val, pars + def htmlq(html): ret = "" for c in html: diff --git a/wrw/req.py b/wrw/req.py index 0689cbd..82a1f84 100644 --- a/wrw/req.py +++ b/wrw/req.py @@ -62,6 +62,7 @@ class request(object): class origrequest(request): def __init__(self, env): self.env = env + self.method = env["REQUEST_METHOD"].upper() self.uriname = env["SCRIPT_NAME"] self.filename = env.get("SCRIPT_FILENAME") self.uri = env["REQUEST_URI"] @@ -136,6 +137,7 @@ class copyrequest(request): self.parent = p self.top = p.topreq() self.env = p.env + self.method = p.method self.uriname = p.uriname self.filename = p.filename self.uri = p.uri