| 1 | import time |
| 2 | |
| 3 | statusinfo = { |
| 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."), |
| 12 | } |
| 13 | |
| 14 | def httpdate(ts): |
| 15 | return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(ts)) |
| 16 | |
| 17 | def phttpdate(dstr): |
| 18 | tz = dstr[-6:] |
| 19 | dstr = dstr[:-6] |
| 20 | if tz[0] != " " or (tz[1] != "+" and tz[1] != "-") or not tz[2:].isdigit(): |
| 21 | return None |
| 22 | tz = int(tz[1:]) |
| 23 | tz = (((tz / 100) * 60) + (tz % 100)) * 60 |
| 24 | return time.mktime(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz - time.altzone |
| 25 | |
| 26 | def pmimehead(hstr): |
| 27 | def pws(p): |
| 28 | while p < len(hstr) and hstr[p].isspace(): |
| 29 | p += 1 |
| 30 | return p |
| 31 | def token(p, sep): |
| 32 | buf = "" |
| 33 | p = pws(p) |
| 34 | if p >= len(hstr): |
| 35 | return "", p |
| 36 | if hstr[p] == '"': |
| 37 | p += 1 |
| 38 | while p < len(hstr): |
| 39 | if hstr[p] == '\\': |
| 40 | p += 1 |
| 41 | if p < len(hstr): |
| 42 | buf += hstr[p] |
| 43 | p += 1 |
| 44 | else: |
| 45 | break |
| 46 | elif hstr[p] == '"': |
| 47 | p += 1 |
| 48 | break |
| 49 | else: |
| 50 | buf += hstr[p] |
| 51 | p += 1 |
| 52 | return buf, pws(p) |
| 53 | else: |
| 54 | while p < len(hstr): |
| 55 | if hstr[p] in sep: |
| 56 | break |
| 57 | buf += hstr[p] |
| 58 | p += 1 |
| 59 | return buf.strip(), pws(p) |
| 60 | p = 0 |
| 61 | val, p = token(p, ";") |
| 62 | pars = {} |
| 63 | while p < len(hstr): |
| 64 | if hstr[p] != ';': |
| 65 | break |
| 66 | p += 1 |
| 67 | k, p = token(p, "=") |
| 68 | if k == "" or hstr[p:p + 1] != '=': |
| 69 | break |
| 70 | p += 1 |
| 71 | v, p = token(p, ';') |
| 72 | pars[k.lower()] = v |
| 73 | return val, pars |
| 74 | |
| 75 | def htmlq(html): |
| 76 | ret = "" |
| 77 | for c in html: |
| 78 | if c == "&": |
| 79 | ret += "&" |
| 80 | elif c == "<": |
| 81 | ret += "<" |
| 82 | elif c == ">": |
| 83 | ret += ">" |
| 84 | else: |
| 85 | ret += c |
| 86 | return ret |
| 87 | |
| 88 | def urlq(url): |
| 89 | ret = "" |
| 90 | for c in url: |
| 91 | if c == "&" or c == "=" or c == "#" or c == "?" or c == "/" or (ord(c) <= 32): |
| 92 | ret += "%%%02X" % ord(c) |
| 93 | else: |
| 94 | ret += c |
| 95 | return ret |
| 96 | |
| 97 | class urlerror(ValueError): |
| 98 | pass |
| 99 | |
| 100 | def parseurl(url): |
| 101 | p = url.find("://") |
| 102 | if p < 0: |
| 103 | raise urlerror("Protocol not found in absolute URL `%s'" % url) |
| 104 | proto = url[:p] |
| 105 | l = url.find("/", p + 3) |
| 106 | if l < 0: |
| 107 | raise urlerror("Local part not found in absolute URL `%s'" % url) |
| 108 | host = url[p + 3:l] |
| 109 | local = url[l:] |
| 110 | q = local.find("?") |
| 111 | if q < 0: |
| 112 | query = "" |
| 113 | else: |
| 114 | query = local[q + 1:] |
| 115 | local = local[:q] |
| 116 | return proto, host, local, query |
| 117 | |
| 118 | def consurl(proto, host, local, query=""): |
| 119 | if len(local) < 1 and local[0] != '/': |
| 120 | raise urlerror("Local part of URL must begin with a slash") |
| 121 | ret = "%s://%s%s" % (proto, host, local) |
| 122 | if len(query) > 0: |
| 123 | ret += "?" + query |
| 124 | return ret |
| 125 | |
| 126 | def appendurl(url, other): |
| 127 | if "://" in other: |
| 128 | return other |
| 129 | proto, host, local, query = parseurl(url) |
| 130 | if len(other) > 0 and other[0] == '/': |
| 131 | return consurl(proto, host, other) |
| 132 | else: |
| 133 | p = local.rfind('/') |
| 134 | return consurl(proto, host, local[:p + 1] + other) |
| 135 | |
| 136 | def requrl(req): |
| 137 | host = req.ihead.get("Host", None) |
| 138 | if host is None: |
| 139 | raise Exception("Could not reconstruct URL because no Host header was sent") |
| 140 | proto = "http" |
| 141 | if req.https: |
| 142 | proto = "https" |
| 143 | if req.uri[0] != '/': |
| 144 | raise Exception("Malformed local part when reconstructing URL") |
| 145 | return "%s://%s%s" % (proto, host, req.uri) |
| 146 | |
| 147 | def parstring(pars={}, **augment): |
| 148 | buf = "" |
| 149 | for key in pars: |
| 150 | if key in augment: |
| 151 | val = augment[key] |
| 152 | del augment[key] |
| 153 | else: |
| 154 | val = pars[key] |
| 155 | if buf != "": buf += "&" |
| 156 | buf += urlq(key) + "=" + urlq(str(val)) |
| 157 | for key in augment: |
| 158 | if buf != "": buf += "&" |
| 159 | buf += urlq(key) + "=" + urlq(str(augment[key])) |
| 160 | return buf |
| 161 | |
| 162 | def parurl(url, pars={}, **augment): |
| 163 | qs = parstring(pars, **augment) |
| 164 | if qs != "": |
| 165 | return url + "?" + qs |
| 166 | else: |
| 167 | return url |