Added option to strip querystring from requrl.
[wrw.git] / wrw / proto.py
index cc2edb8..80e2419 100644 (file)
@@ -1,4 +1,4 @@
-import time
+import time, calendar, collections, binascii, base64
 
 statusinfo = {
     400: ("Bad Request", "Invalid HTTP request."),
@@ -21,7 +21,7 @@ def phttpdate(dstr):
         return None
     tz = int(tz[1:])
     tz = (((tz / 100) * 60) + (tz % 100)) * 60
-    return time.mktime(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz - time.altzone
+    return calendar.timegm(time.strptime(dstr, "%a, %d %b %Y %H:%M:%S")) - tz
 
 def pmimehead(hstr):
     def pws(p):
@@ -106,7 +106,7 @@ def urlq(url):
     if isinstance(url, str):
         url = url.encode("utf-8")
     ret = ""
-    invalid = b"&=#?/\"'"
+    invalid = b"%;&=#?/\"'"
     for c in url:
         if c in invalid or (c <= 32) or (c >= 128):
             ret += "%%%02X" % c
@@ -168,11 +168,16 @@ def scripturl(req):
         raise Exception("Malformed local part when reconstructing URL")
     return siteurl(req) + req.uriname[1:]
 
-def requrl(req):
+def requrl(req, qs=True):
     s = siteurl(req)
     if req.uri[0] != '/':
         raise Exception("Malformed local part when reconstructing URL")
-    return siteurl(req) + req.uri[1:]
+    pf = req.uri[1:]
+    if not qs:
+        p = pf.find('?')
+        if not p < 0:
+            pf = pf[:p]
+    return siteurl(req) + pf
 
 def parstring(pars={}, **augment):
     buf = ""
@@ -192,6 +197,40 @@ def parstring(pars={}, **augment):
 def parurl(url, pars={}, **augment):
     qs = parstring(pars, **augment)
     if qs != "":
-        return url + "?" + qs
+        return url + ("&" if "?" in url else "?") + qs
     else:
         return url
+
+# Wrap these, since binascii is a bit funky. :P
+def enhex(bs):
+    return base64.b16encode(bs).decode("us-ascii")
+def unhex(es):
+    if not isinstance(es, collections.ByteString):
+        try:
+            es = es.encode("us-ascii")
+        except UnicodeError:
+            raise binascii.Error("non-ascii character in hex-string")
+    return base64.b16decode(es)
+def enb32(bs):
+    return base64.b32encode(bs).decode("us-ascii")
+def unb32(es):
+    if not isinstance(es, collections.ByteString):
+        try:
+            es = es.encode("us-ascii")
+        except UnicodeError:
+            raise binascii.Error("non-ascii character in base32-string")
+    if (len(es) % 8) != 0:
+        es += b"=" * (8 - (len(es) % 8))
+    es = es.upper()             # The whole point of Base32 is that it's case-insensitive :P
+    return base64.b32decode(es)
+def enb64(bs):
+    return base64.b64encode(bs).decode("us-ascii")
+def unb64(es):
+    if not isinstance(es, collections.ByteString):
+        try:
+            es = es.encode("us-ascii")
+        except UnicodeError:
+            raise binascii.Error("non-ascii character in base64-string")
+    if (len(es) % 4) != 0:
+        es += b"=" * (4 - (len(es) % 4))
+    return base64.b64decode(es)