Commit | Line | Data |
---|---|---|
3ba5e96c FT |
1 | import time |
2 | ||
5ef9e488 | 3 | statusinfo = { |
001ec99e FT |
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."), | |
5ef9e488 | 7 | 404: ("Not Found", "The requested resource was not found."), |
001ec99e FT |
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."), | |
5ef9e488 FT |
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 | ||
eed3cf12 FT |
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, ';') | |
ea18777a | 72 | pars[k.lower()] = v |
eed3cf12 FT |
73 | return val, pars |
74 | ||
5ef9e488 FT |
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 | ||
bb80acbd FT |
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 | ||
5ef9e488 FT |
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 |