Added some URL manipulation functions to wrw.proto.
[wrw.git] / wrw / proto.py
1 statusinfo = {
2     400: ("Bad Request", "Your issued HTTP request is invalid."),
3     403: ("Forbidden", "You are not authorized to view the requested resource."),
4     404: ("Not Found", "The requested resource was not found."),
5     500: ("Server Error", "An internal error occurred.")
6     }
7
8 def httpdate(ts):
9     return time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime(ts))
10
11 def phttpdate(dstr):
12     tz = dstr[-6:]
13     dstr = dstr[:-6]
14     if tz[0] != " " or (tz[1] != "+" and tz[1] != "-") or not tz[2:].isdigit():
15         return None
16     tz = int(tz[1:])
17     tz = (((tz / 100) * 60) + (tz % 100)) * 60
18     return time.mktime(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz - time.altzone
19
20 def htmlq(html):
21     ret = ""
22     for c in html:
23         if c == "&":
24             ret += "&"
25         elif c == "<":
26             ret += "&lt;"
27         elif c == ">":
28             ret += "&gt;"
29         else:
30             ret += c
31     return ret
32
33 def urlq(url):
34     ret = ""
35     for c in url:
36         if c == "&" or c == "=" or c == "#" or c == "?" or c == "/" or (ord(c) <= 32):
37             ret += "%%%02X" % ord(c)
38         else:
39             ret += c
40     return ret
41
42 class urlerror(ValueError):
43     pass
44
45 def parseurl(url):
46     p = url.find("://")
47     if p < 0:
48         raise urlerror("Protocol not found in absolute URL `%s'" % url)
49     proto = url[:p]
50     l = url.find("/", p + 3)
51     if l < 0:
52         raise urlerror("Local part not found in absolute URL `%s'" % url)
53     host = url[p + 3:l]
54     local = url[l:]
55     q = local.find("?")
56     if q < 0:
57         query = ""
58     else:
59         query = local[q + 1:]
60         local = local[:q]
61     return proto, host, local, query
62
63 def consurl(proto, host, local, query = ""):
64     if len(local) < 1 and local[0] != '/':
65         raise urlerror("Local part of URL must begin with a slash")
66     ret = "%s://%s%s" % (proto, host, local)
67     if len(query) > 0:
68         ret += "?" + query
69     return ret
70
71 def appendurl(url, other):
72     if "://" in other:
73         return other
74     proto, host, local, query = parseurl(url)
75     if len(other) > 0 and other[0] == '/':
76         return consurl(proto, host, other)
77     else:
78         p = local.rfind('/')
79         return consurl(proto, host, local[:p + 1] + other)
80
81 def requrl(req):
82     host = req.ihead.get("Host", None)
83     if host is None:
84         raise Exception("Could not reconstruct URL because no Host header was sent")
85     proto = "http"
86     if req.https:
87         proto = "https"
88     if req.uri[0] != '/':
89         raise Exception("Malformed local part when reconstructing URL")
90     return "%s://%s%s" % (proto, host, req.uri)
91
92 def parstring(pars = {}, **augment):
93     buf = ""
94     for key in pars:
95         if key in augment:
96             val = augment[key]
97             del augment[key]
98         else:
99             val = pars[key]
100         if buf != "": buf += "&"
101         buf += urlq(key) + "=" + urlq(str(val))
102     for key in augment:
103         if buf != "": buf += "&"
104         buf += urlq(key) + "=" + urlq(str(augment[key]))
105     return buf