""" Network Utilities (from web.py) """ import datetime import re import socket import time try: from urllib.parse import quote except ImportError: from urllib import quote __all__ = [ "validipaddr", "validip6addr", "validipport", "validip", "validaddr", "urlquote", "httpdate", "parsehttpdate", "htmlquote", "htmlunquote", "websafe", ] def validip6addr(address): """ Returns True if `address` is a valid IPv6 address. >>> validip6addr('::') True >>> validip6addr('aaaa:bbbb:cccc:dddd::1') True >>> validip6addr('1:2:3:4:5:6:7:8:9:10') False >>> validip6addr('12:10') False """ try: socket.inet_pton(socket.AF_INET6, address) except (OSError, AttributeError, ValueError): return False return True def validipaddr(address): """ Returns True if `address` is a valid IPv4 address. >>> validipaddr('192.168.1.1') True >>> validipaddr('192.168. 1.1') False >>> validipaddr('192.168.1.800') False >>> validipaddr('192.168.1') False """ try: octets = address.split(".") if len(octets) != 4: return False for x in octets: if " " in x: return False if not (0 <= int(x) <= 255): return False except ValueError: return False return True def validipport(port): """ Returns True if `port` is a valid IPv4 port. >>> validipport('9000') True >>> validipport('foo') False >>> validipport('1000000') False """ try: if not (0 <= int(port) <= 65535): return False except ValueError: return False return True def validip(ip, defaultaddr="0.0.0.0", defaultport=8080): """ Returns `(ip_address, port)` from string `ip_addr_port` >>> validip('1.2.3.4') ('1.2.3.4', 8080) >>> validip('80') ('0.0.0.0', 80) >>> validip('192.168.0.1:85') ('192.168.0.1', 85) >>> validip('::') ('::', 8080) >>> validip('[::]:88') ('::', 88) >>> validip('[::1]:80') ('::1', 80) """ addr = defaultaddr port = defaultport # Matt Boswell's code to check for ipv6 first match = re.search(r"^\[([^]]+)\](?::(\d+))?$", ip) # check for [ipv6]:port if match: if validip6addr(match.group(1)): if match.group(2): if validipport(match.group(2)): return (match.group(1), int(match.group(2))) else: return (match.group(1), port) else: if validip6addr(ip): return (ip, port) # end ipv6 code ip = ip.split(":", 1) if len(ip) == 1: if not ip[0]: pass elif validipaddr(ip[0]): addr = ip[0] elif validipport(ip[0]): port = int(ip[0]) else: raise ValueError(":".join(ip) + " is not a valid IP address/port") elif len(ip) == 2: addr, port = ip if not validipaddr(addr) or not validipport(port): raise ValueError(":".join(ip) + " is not a valid IP address/port") port = int(port) else: raise ValueError(":".join(ip) + " is not a valid IP address/port") return (addr, port) def validaddr(string_): """ Returns either (ip_address, port) or "/path/to/socket" from string_ >>> validaddr('/path/to/socket') '/path/to/socket' >>> validaddr('8000') ('0.0.0.0', 8000) >>> validaddr('127.0.0.1') ('127.0.0.1', 8080) >>> validaddr('127.0.0.1:8000') ('127.0.0.1', 8000) >>> validip('[::1]:80') ('::1', 80) >>> validaddr('fff') Traceback (most recent call last): ... ValueError: fff is not a valid IP address/port """ if "/" in string_: return string_ else: return validip(string_) def urlquote(val): """ Quotes a string for use in a URL. >>> urlquote('://?f=1&j=1') '%3A//%3Ff%3D1%26j%3D1' >>> urlquote(None) '' >>> urlquote(u'\u203d') '%E2%80%BD' """ if val is None: return "" val = str(val).encode("utf-8") return quote(val) def httpdate(date_obj): """ Formats a datetime object for use in HTTP headers. >>> import datetime >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1)) 'Thu, 01 Jan 1970 01:01:01 GMT' """ return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT") def parsehttpdate(string_): """ Parses an HTTP date into a datetime object. >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT') datetime.datetime(1970, 1, 1, 1, 1, 1) """ try: t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z") except ValueError: return None return datetime.datetime(*t[:6]) def htmlquote(text): r""" Encodes `text` for raw use in HTML. >>> htmlquote(u"<'&\">") u'<'&">' """ text = text.replace("&", "&") # Must be done first! text = text.replace("<", "<") text = text.replace(">", ">") text = text.replace("'", "'") text = text.replace('"', """) return text def htmlunquote(text): r""" Decodes `text` that's HTML quoted. >>> htmlunquote(u'<'&">') u'<\'&">' """ text = text.replace(""", '"') text = text.replace("'", "'") text = text.replace(">", ">") text = text.replace("<", "<") text = text.replace("&", "&") # Must be done last! return text def websafe(val): r""" Converts `val` so that it is safe for use in Unicode HTML. >>> websafe("<'&\">") u'<'&">' >>> websafe(None) u'' >>> websafe(u'\u203d') == u'\u203d' True """ if val is None: return "" if isinstance(val, bytes): val = val.decode("utf-8") elif not isinstance(val, str): val = str(val) return htmlquote(val) if __name__ == "__main__": import doctest doctest.testmod()