1 import time, calendar, collections, binascii, base64
4 400: ("Bad Request", "Invalid HTTP request."),
5 401: ("Unauthorized", "Authentication must be provided for the requested resource."),
6 403: ("Forbidden", "You are not authorized to request the requested resource."),
7 404: ("Not Found", "The requested resource was not found."),
8 405: ("Method Not Allowed", "The request method is not recognized or permitted by the requested resource."),
9 500: ("Server Error", "An internal error occurred."),
10 501: ("Not Implemented", "The requested functionality has not been implemented."),
11 503: ("Service Unavailable", "Service is being denied at this time."),
15 return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(ts))
20 if tz[0] != " " or (tz[1] != "+" and tz[1] != "-") or not tz[2:].isdigit():
23 tz = (((tz / 100) * 60) + (tz % 100)) * 60
24 return calendar.timegm(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz
28 while p < len(hstr) and hstr[p].isspace():
59 return buf.strip(), pws(p)
61 val, p = token(p, ";")
68 if k == "" or hstr[p:p + 1] != '=':
88 def simpleerror(env, startreq, code, title, msg):
89 buf = """<?xml version="1.0" encoding="US-ASCII"?>
90 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
91 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">
100 """ % (title, title, htmlq(msg))
101 buf = buf.encode("us-ascii")
102 startreq("%i %s" % (code, title), [("Content-Type", "text/html"), ("Content-Length", str(len(buf)))])
106 if isinstance(url, str):
107 url = url.encode("utf-8")
109 invalid = b"%;&=#?/\"'"
111 if c in invalid or (c <= 32) or (c >= 128):
117 class urlerror(ValueError):
123 raise urlerror("Protocol not found in absolute URL `%s'" % url)
125 l = url.find("/", p + 3)
127 raise urlerror("Local part not found in absolute URL `%s'" % url)
134 query = local[q + 1:]
136 return proto, host, local, query
138 def consurl(proto, host, local, query=""):
139 if len(local) < 1 and local[0] != '/':
140 raise urlerror("Local part of URL must begin with a slash")
141 ret = "%s://%s%s" % (proto, host, local)
146 def appendurl(url, other):
149 proto, host, local, query = parseurl(url)
150 if len(other) > 0 and other[0] == '/':
151 return consurl(proto, host, other)
154 return consurl(proto, host, local[:p + 1] + other)
157 host = req.ihead.get("Host", None)
159 raise Exception("Could not reconstruct URL because no Host header was sent")
163 return "%s://%s/" % (proto, host)
167 if req.uriname[0] != '/':
168 raise Exception("Malformed local part when reconstructing URL")
169 return siteurl(req) + req.uriname[1:]
171 def requrl(req, qs=True):
173 if req.uri[0] != '/':
174 raise Exception("Malformed local part when reconstructing URL")
180 return siteurl(req) + pf
182 def parstring(pars={}, **augment):
190 if buf != "": buf += "&"
191 buf += urlq(key) + "=" + urlq(str(val))
193 if buf != "": buf += "&"
194 buf += urlq(key) + "=" + urlq(str(augment[key]))
197 def parurl(url, pars={}, **augment):
198 qs = parstring(pars, **augment)
200 return url + ("&" if "?" in url else "?") + qs
204 # Wrap these, since binascii is a bit funky. :P
206 return base64.b16encode(bs).decode("us-ascii")
208 if not isinstance(es, collections.ByteString):
210 es = es.encode("us-ascii")
212 raise binascii.Error("non-ascii character in hex-string")
213 return base64.b16decode(es)
215 return base64.b32encode(bs).decode("us-ascii")
217 if not isinstance(es, collections.ByteString):
219 es = es.encode("us-ascii")
221 raise binascii.Error("non-ascii character in base32-string")
222 if (len(es) % 8) != 0:
223 es += b"=" * (8 - (len(es) % 8))
224 es = es.upper() # The whole point of Base32 is that it's case-insensitive :P
225 return base64.b32decode(es)
227 return base64.b64encode(bs).decode("us-ascii")
229 if not isinstance(es, collections.ByteString):
231 es = es.encode("us-ascii")
233 raise binascii.Error("non-ascii character in base64-string")
234 if (len(es) % 4) != 0:
235 es += b"=" * (4 - (len(es) % 4))
236 return base64.b64decode(es)