├── .gitignore ├── README.md ├── graphs └── .placeholder ├── ipaddress.py ├── lib.py ├── main.py ├── static ├── css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.css.map │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.css.map │ └── bootstrap.min.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── js │ ├── bootstrap.js │ └── bootstrap.min.js ├── templates ├── index.html └── result.html └── web.py-0.37 ├── PKG-INFO ├── build └── lib.linux-x86_64-2.7 │ └── web │ ├── __init__.py │ ├── application.py │ ├── browser.py │ ├── contrib │ ├── __init__.py │ └── template.py │ ├── db.py │ ├── debugerror.py │ ├── form.py │ ├── http.py │ ├── httpserver.py │ ├── net.py │ ├── python23.py │ ├── session.py │ ├── template.py │ ├── test.py │ ├── utils.py │ ├── webapi.py │ ├── webopenid.py │ ├── wsgi.py │ └── wsgiserver │ ├── __init__.py │ ├── ssl_builtin.py │ └── ssl_pyopenssl.py ├── setup.py └── web ├── __init__.py ├── application.py ├── browser.py ├── contrib ├── __init__.py └── template.py ├── db.py ├── debugerror.py ├── form.py ├── http.py ├── httpserver.py ├── net.py ├── python23.py ├── session.py ├── template.py ├── test.py ├── utils.py ├── webapi.py ├── webopenid.py ├── wsgi.py └── wsgiserver ├── __init__.py ├── ssl_builtin.py └── ssl_pyopenssl.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | *.png 4 | .idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cisco ACL Analyzer 2 | 3 | This is repository for project hosted on [acl.frenzy.cz](http://acl.frenzy.cz/). 4 | 5 | ## Why you want to use it? 6 | 7 | This web application allows you to check, which rule from your Cisco ACL affects your imaginary packet. 8 | 9 | I use it when I have ACL with a lot of rules and trying to discover which rule permit or deny my connection trough firewall. 10 | 11 | ## How to use it? 12 | 13 | Preffered workflow is: 14 | 15 | 1. Create imaginary packet: 16 | 1. Choose connection type from selectbox - TCP, UDP, ICMP 17 | 2. Enter source IP address 18 | 3. Enter source port or leave this field blank for generate random source port 19 | 4. Enter destination IP address 20 | 5. Enter destination packet or leave this field blank for generate random destination port 21 | 2. Paste your ACL to textbox 22 | - Copy it from CLI (*show ip access-list *) 23 | - Copy it from configuration file (*ip access-list extended *) 24 | 3. Click to Analyze 25 | 26 | ## What is result? 27 | 28 | The result is table with same rules as in your ACL but compared to your packet. Green rules permits packet and red rules denies packet. Any other rules with no special color has some mismatches between packet and rule definition and this mismatches is explained in last table column. 29 | 30 | ## Project status 31 | 32 | This project is in active development, but only for my needs. 33 | 34 | If you want some more features, issues or pull-requests are welcome. 35 | 36 | ## Author 37 | 38 | Lumír Balhar, [frenzy.madness@gmail.com](mailto:frenzy.madness@gmail.com), [@lumirbalhar](https://twitter.com/lumirbalhar) 39 | 40 | ## License 41 | 42 | GPL 43 | -------------------------------------------------------------------------------- /graphs/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenzymadness/Cisco-ACL-Analyzer/e01ea94705ccd6bfc3f18702056f2e814a7b46b6/graphs/.placeholder -------------------------------------------------------------------------------- /lib.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from ipaddress import * 3 | from random import randint 4 | import pygraphviz as pgv 5 | from hashlib import md5 6 | import os 7 | 8 | # Cisco port to names mapping dictionary 9 | portNames = { 10 | 'aol': 5190, 'bgp': 179, 'biff': 512, 'bootpc': 68, 'bootps': 67, 'chargen': 19, 'citrix-ica': 1494, 'cmd': 514, 11 | 'ctiqbe': 2748, 'daytime': 13, 'discard': 9, 'domain': 53, 'dnsix': 195, 'echo': 7, 'exec': 512, 'finger': 79, 12 | 'ftp': 21, 'ftp-data': 20, 'gopher': 70, 'https': 443, 'h323': 1720, 'hostname': 101, 'ident': 113, 'imap4': 143, 13 | 'irc': 194, 'isakmp': 500, 'kerberos': 750, 'klogin': 543, 'kshell': 544, 'ldap': 389, 'ldaps': 636, 'lpd': 515, 14 | 'login': 513, 'lotusnotes': 1352, 'mobile-ip': 434, 'nameserver': 42, 'netbios-ns': 137, 'netbios-dgm': 138, 15 | 'netbios-ssn': 139, 'nntp': 119, 'ntp': 123, 'pcanywhere-status': 5632, 'pcanywhere-data': 5631, 'pim-auto-rp': 496, 16 | 'pop2': 109, 'pop3': 110, 'pptp': 1723, 'radius': 1645, 'radius-acct': 1646, 'rip': 520, 'secureid-udp': 5510, 17 | 'smtp': 25, 'snmp': 161, 'snmptrap': 162, 'sqlnet': 1521, 'ssh': 22, 'sunrpc': 111, 'rpc': 111, 'syslog': 514, 18 | 'tacacs': 49, 'talk': 517, 'telnet': 23, 'tftp': 69, 'time': 37, 'uucp': 540, 'who': 513, 'whois': 43, 'www': 80, 19 | 'xdmcp': 177 20 | } 21 | 22 | # Make switched dict for translating from port num to name 23 | portNumbers = {number: name for name, number in portNames.iteritems()} 24 | 25 | 26 | # function to switch mask from 0.0.255.255 to 255.255.0.0 27 | def switch_mask(mask): 28 | """ 29 | >>> switch_mask('0.0.0.0') 30 | '255.255.255.255' 31 | >>> switch_mask('0.0.255.255') 32 | '255.255.0.0' 33 | >>> switch_mask('0.0.8.255') 34 | '255.255.247.0' 35 | >>> switch_mask('0.0.0.255') 36 | '255.255.255.0' 37 | """ 38 | return '.'.join([str(255 - int(part)) for part in mask.split('.')]) 39 | 40 | 41 | # function for generate human readable list of ports 42 | def ports_for_humans(ports): 43 | """" 44 | >>> ports_for_humans([22, 20, 'test', 8080]) 45 | 'ssh, ftp-data, test, 8080' 46 | >>> ports_for_humans([80, 443, 1000, 21]) 47 | 'www, https, 1000, ftp' 48 | """ 49 | if len(ports) == 0: 50 | return 'all' 51 | else: 52 | # Empty arr for result 53 | result = [] 54 | # for every port, try to translate it to name 55 | for port in ports: 56 | if port in portNumbers: 57 | result.append(portNumbers[port]) 58 | else: 59 | result.append(port) 60 | 61 | return ', '.join([str(part) for part in result]) 62 | 63 | 64 | # Rule object class for one rule in ACL 65 | class Rule: 66 | """ 67 | >>> r1 = Rule("permit tcp any host 10.10.10.10 eq 80 443") 68 | >>> print r1 69 | permit tcp any host 10.10.10.10 eq 80 443 70 | >>> r1.type 71 | 'permit' 72 | >>> r1.protocol 73 | 'tcp' 74 | >>> r1.srcIP 75 | IPv4Network('0.0.0.0/0') 76 | >>> r1.dstIP 77 | IPv4Address('10.10.10.10') 78 | >>> r1.dstPort 79 | [80, 443] 80 | >>> r1.srcPort 81 | [] 82 | >>> r2 = Rule("permit tcp host 10.1.1.1 eq 8000 8080 10.10.10.0 0.0.0.255 range 1000 1010") 83 | >>> print r2 84 | permit tcp host 10.1.1.1 eq 8000 8080 10.10.10.0 0.0.0.255 range 1000 1010 85 | >>> r2.protocol 86 | 'tcp' 87 | >>> r2.type 88 | 'permit' 89 | >>> r2.srcIP 90 | IPv4Address('10.1.1.1') 91 | >>> r2.dstIP 92 | IPv4Network('10.10.10.0/24') 93 | >>> r2.dstPort 94 | [1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009] 95 | >>> r2.srcPort 96 | [8000, 8080] 97 | """ 98 | def __init__(self, line): 99 | # txt representation of rule 100 | self.txt = line 101 | 102 | # parts of line 103 | parts = self.txt.split(' ') 104 | 105 | # rule type - load and del from parts 106 | self.type = parts[0] 107 | del parts[0] 108 | 109 | # rule protocol 110 | self.protocol = parts[0] 111 | del parts[0] 112 | 113 | # src IP address, src network or any 114 | if parts[0] == 'host': 115 | self.srcIP = ip_address(unicode(parts[1])) 116 | del parts[0:2] 117 | elif parts[0] == 'any': 118 | # set whole ips network 119 | self.srcIP = ip_network(u'0.0.0.0/0') 120 | del parts[0] 121 | else: 122 | # set source as network 123 | self.srcIP = ip_network(unicode(parts[0] + '/' + switch_mask(parts[1]))) 124 | del parts[0:2] 125 | 126 | # if we have srcPort definition 127 | self.srcPort = [] 128 | if parts[0] == 'eq': 129 | del parts[0] 130 | for port in parts: 131 | if port.isdigit(): 132 | self.srcPort.append(int(port)) 133 | elif port in portNames: 134 | self.srcPort.append(portNames[port]) 135 | else: 136 | break 137 | del parts[0:len(self.srcPort)] 138 | elif parts[0] == 'range': 139 | self.srcPort = range(int(parts[1]), int(parts[2])) 140 | del parts[0:3] 141 | 142 | # same for destination address 143 | if parts[0] == 'host': 144 | self.dstIP = ip_address(unicode(parts[1])) 145 | del parts[0:2] 146 | elif parts[0] == 'any': 147 | # set whole ips network 148 | self.dstIP = ip_network(u'0.0.0.0/0') 149 | del parts[0] 150 | else: 151 | # set source as network 152 | self.dstIP = ip_network(unicode(parts[0] + '/' + switch_mask(parts[1]))) 153 | del parts[0:2] 154 | 155 | # if we have more parts, continue 156 | self.dstPort = [] 157 | if len(parts) > 0: 158 | # if we have dstPort definition 159 | if parts[0] == 'eq': 160 | del parts[0] 161 | for port in parts: 162 | if port.isdigit(): 163 | self.dstPort.append(int(port)) 164 | elif port in portNames: 165 | self.dstPort.append(portNames[port]) 166 | else: 167 | break 168 | del parts[0:len(self.dstPort)] 169 | elif parts[0] == 'range': 170 | self.dstPort = range(int(parts[1]), int(parts[2])) 171 | del parts[0:3] 172 | 173 | def __str__(self): 174 | return self.txt 175 | 176 | 177 | # Class for representing whole ACL with rules 178 | class ACL: 179 | """ 180 | >>> acl = ACL('''ip access-list extended NC-ven 181 | ... permit tcp 10.1.1.0 0.0.0.255 host 158.196.147.23 eq 28518 182 | ... permit ip 10.1.1.0 0.0.0.255 host 158.196.147.23 183 | ... permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888 184 | ... permit udp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888 185 | ... permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 8888 186 | ... permit icmp 10.1.1.0 0.0.0.255 host 158.196.141.88 echo 187 | ... permit tcp 10.1.1.0 0.0.0.255 host 193.62.193.80 eq www 188 | ... permit tcp 10.1.1.0 0.0.0.255 host 193.144.127.203 eq www 189 | ... deny ip 10.1.1.0 0.0.0.255 any''') 190 | >>> acl.txt 191 | 'ip access-list extended NC-ven\\n permit tcp 10.1.1.0 0.0.0.255 host 158.196.147.23 eq 28518\\n permit ip 10.1.1.0 0.0.0.255 host 158.196.147.23\\n permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888\\n permit udp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888\\n permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 8888\\n permit icmp 10.1.1.0 0.0.0.255 host 158.196.141.88 echo\\n permit tcp 10.1.1.0 0.0.0.255 host 193.62.193.80 eq www\\n permit tcp 10.1.1.0 0.0.0.255 host 193.144.127.203 eq www\\n deny ip 10.1.1.0 0.0.0.255 any' 192 | >>> acl.lines 193 | ['ip access-list extended NC-ven', 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.147.23 eq 28518', 'permit ip 10.1.1.0 0.0.0.255 host 158.196.147.23', 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888', 'permit udp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888', 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 8888', 'permit icmp 10.1.1.0 0.0.0.255 host 158.196.141.88 echo', 'permit tcp 10.1.1.0 0.0.0.255 host 193.62.193.80 eq www', 'permit tcp 10.1.1.0 0.0.0.255 host 193.144.127.203 eq www', 'deny ip 10.1.1.0 0.0.0.255 any'] 194 | >>> packet1 = Packet(protocol='udp', src_ip='10.10.10.10', src_port='', dst_ip='10.10.10.1', dst_port='80') 195 | >>> acl.check_packet(packet1) 196 | [[False, 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.147.23 eq 28518', ['protocol mismatch', 'source IP mismatch', 'destination IP mismatch', 'destination port mismatch']], [False, 'permit ip 10.1.1.0 0.0.0.255 host 158.196.147.23', ['source IP mismatch', 'destination IP mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888', ['protocol mismatch', 'source IP mismatch', 'destination IP mismatch', 'destination port mismatch']], [False, 'permit udp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888', ['source IP mismatch', 'destination IP mismatch', 'destination port mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 8888', ['protocol mismatch', 'source IP mismatch', 'destination IP mismatch', 'destination port mismatch']], [False, 'permit icmp 10.1.1.0 0.0.0.255 host 158.196.141.88 echo', ['protocol mismatch', 'source IP mismatch', 'destination IP mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 193.62.193.80 eq www', ['protocol mismatch', 'source IP mismatch', 'destination IP mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 193.144.127.203 eq www', ['protocol mismatch', 'source IP mismatch', 'destination IP mismatch']], [False, 'deny ip 10.1.1.0 0.0.0.255 any', ['source IP mismatch']]] 197 | >>> packet2 = Packet(protocol='tcp', src_ip='10.1.1.1', src_port='', dst_ip='158.196.147.23', dst_port='28518') 198 | >>> acl.check_packet(packet2) 199 | [[True, 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.147.23 eq 28518', []], [True, 'permit ip 10.1.1.0 0.0.0.255 host 158.196.147.23', []], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888', ['destination IP mismatch', 'destination port mismatch']], [False, 'permit udp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 18888', ['protocol mismatch', 'destination IP mismatch', 'destination port mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 158.196.141.88 eq 8888', ['destination IP mismatch', 'destination port mismatch']], [False, 'permit icmp 10.1.1.0 0.0.0.255 host 158.196.141.88 echo', ['protocol mismatch', 'destination IP mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 193.62.193.80 eq www', ['destination IP mismatch', 'destination port mismatch']], [False, 'permit tcp 10.1.1.0 0.0.0.255 host 193.144.127.203 eq www', ['destination IP mismatch', 'destination port mismatch']], [True, 'deny ip 10.1.1.0 0.0.0.255 any', []]] 200 | >>> acl.generate_graph() # doctest: +ELLIPSIS 201 | '....png' 202 | """ 203 | def __init__(self, txt): 204 | # storing txt input 205 | self.txt = txt 206 | 207 | # Preparing text lines 208 | self.lines = [] 209 | for line in self.txt.split('\n'): 210 | self.lines.append(' '.join(line.split())) 211 | 212 | self.rules = [] 213 | 214 | # For every line in ACL 215 | for num, line in enumerate(self.lines): 216 | # Strip lines 217 | line = line.strip() 218 | 219 | # If line is ACL definition, continue 220 | if line.startswith('ip access-list') or line.startswith('Extended IP access list'): 221 | continue 222 | 223 | # If line starts with digit (copied from console), continue without numbers 224 | if line.split(' ')[0].isdigit(): 225 | line = ' '.join(line.split(' ')[1:]).strip() 226 | 227 | # If line ends with count of matches (also from CLI), remove this information (2 parts) 228 | if line.endswith('matches)'): 229 | line = ' '.join(line.split(' ')[:-2]).strip() 230 | 231 | # Also remove established tag - we can't handle with it 232 | if line.endswith('established'): 233 | line = ' '.join(line.split(' ')[:-1]).strip() 234 | 235 | try: 236 | self.rules.append(Rule(line)) 237 | except Exception as e: 238 | raise RuntimeError('Error parsing rule on line %d\nRule: %s\nError message:%s' % (num, line, e)) 239 | 240 | def check_packet(self, packet): 241 | result = [] 242 | for rule in self.rules: 243 | reasons = [] 244 | # Check protocol 245 | if not (packet.protocol == rule.protocol or rule.protocol == 'ip'): 246 | reasons.append('protocol mismatch') 247 | 248 | # Check source 249 | if not ((isinstance(rule.srcIP, IPv4Address) and rule.srcIP == packet.srcIP) 250 | or (isinstance(rule.srcIP, IPv4Network) and packet.srcIP in rule.srcIP)): 251 | reasons.append('source IP mismatch') 252 | 253 | # source port 254 | if not (len(rule.srcPort) == 0 or packet.srcPort in rule.srcPort): 255 | reasons.append('sorce port mismatch') 256 | 257 | # Check destination 258 | if not ((isinstance(rule.dstIP, IPv4Address) and rule.dstIP == packet.dstIP) 259 | or (isinstance(rule.dstIP, IPv4Network) and packet.dstIP in rule.dstIP)): 260 | reasons.append('destination IP mismatch') 261 | 262 | # destination port 263 | if not (len(rule.dstPort) == 0 or packet.dstPort in rule.dstPort): 264 | reasons.append('destination port mismatch') 265 | 266 | if len(reasons) > 0: 267 | status = False 268 | else: 269 | status = True 270 | 271 | result.append([status, rule.txt, reasons]) 272 | 273 | return result 274 | 275 | def generate_graph(self): 276 | # make graphname as hash from ACL content 277 | graphname = md5(self.txt).hexdigest() + '.png' 278 | 279 | graphfolder = os.path.abspath(os.path.dirname(__file__)) + '/graphs/' 280 | 281 | # if graph for same ACL exists, return its name 282 | if os.path.exists(graphfolder + graphname): 283 | return graphname 284 | else: 285 | # create graph 286 | graph = pgv.AGraph(strict=False, directed=True) 287 | # set default shape to box 288 | graph.node_attr['shape'] = 'box' 289 | 290 | # for every rule create a edge between two nodes 291 | for rule in self.rules: 292 | # Coloring edges based on rule type 293 | if rule.type == 'permit': 294 | color = 'green' 295 | else: 296 | color = 'red' 297 | # Add edge to graph 298 | graph.add_edge(rule.srcIP, rule.dstIP, width='4.0', len='3.0', color=color, label='%s --> %s' % ( 299 | ports_for_humans(rule.srcPort), ports_for_humans(rule.dstPort))) 300 | 301 | # initialize layout 302 | graph.layout() 303 | # write graph to image 304 | graph.draw(graphfolder + graphname) 305 | # return graph name 306 | return graphname 307 | 308 | 309 | # Class representing one packet 310 | class Packet: 311 | """ 312 | >>> packet1 = Packet(protocol='udp', src_ip='10.10.10.10', src_port='3333', dst_ip='10.10.10.1', dst_port='80') 313 | >>> print packet1 314 | udp - 10.10.10.10:3333 --> 10.10.10.1:80 315 | >>> packet1.srcPort 316 | 3333 317 | >>> packet1.srcIP 318 | IPv4Address('10.10.10.10') 319 | >>> packet1.dstPort 320 | 80 321 | >>> packet1.dstIP 322 | IPv4Address('10.10.10.1') 323 | """ 324 | def __init__(self, protocol, src_ip, src_port, dst_ip, dst_port): 325 | self.protocol = protocol 326 | self.srcIP = ip_address(unicode(src_ip)) 327 | 328 | # src port can be random number or name 329 | if src_port == '': 330 | self.srcPort = randint(1, 65536) 331 | elif not src_port.isdigit(): 332 | self.srcPort = portNames['src_port'] 333 | else: 334 | self.srcPort = int(src_port) 335 | 336 | self.dstIP = ip_address(unicode(dst_ip)) 337 | 338 | # dst port can be random number or name 339 | if dst_port == '': 340 | self.dstPort = randint(1, 65536) 341 | elif not dst_port.isdigit(): 342 | self.dstPort = portNames[dst_port] 343 | else: 344 | self.dstPort = int(dst_port) 345 | 346 | def __str__(self): 347 | return '%s - %s:%s --> %s:%s' % (self.protocol, self.srcIP, self.srcPort, self.dstIP, self.dstPort) 348 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import web 2 | import os 3 | import sys 4 | sys.path.append(os.path.abspath(os.path.dirname(__file__))) 5 | from lib import * 6 | 7 | # Root dir and render object with templates 8 | rootdir = os.path.abspath(os.path.dirname(__file__)) + '/' 9 | render = web.template.render(rootdir + 'templates/') 10 | 11 | urls = ( 12 | '/', 'index', 13 | '/result', 'result' 14 | ) 15 | 16 | 17 | # Main page 18 | class index: 19 | def GET(self): 20 | return render.index() 21 | 22 | 23 | class result: 24 | def POST(self): 25 | form = web.input() 26 | try: 27 | acl = ACL(form['acl']) 28 | except Exception as e: 29 | return e 30 | packet = Packet(form['protocol'], form['srcIP'], form['srcPort'], form['dstIP'], form['dstPort']) 31 | results = acl.check_packet(packet) 32 | print packet 33 | return render.result(results, acl.generate_graph()) 34 | 35 | app = web.application(urls, globals(), autoreload=True) 36 | application = app.wsgifunc() 37 | -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenzymadness/Cisco-ACL-Analyzer/e01ea94705ccd6bfc3f18702056f2e814a7b46b6/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenzymadness/Cisco-ACL-Analyzer/e01ea94705ccd6bfc3f18702056f2e814a7b46b6/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenzymadness/Cisco-ACL-Analyzer/e01ea94705ccd6bfc3f18702056f2e814a7b46b6/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Cisco ACL checker 9 | 10 | 11 | 12 |
13 |
14 |

Cisco ACL Analyzer

15 |

Create packet and paste your ACL to analyze which rule affects packet.

16 | If you like this project, you can find it on BitBucket or GitHub. 17 |
18 |
19 |
20 |
21 |
22 |
23 |

Packet source

24 | 29 | : 30 |
31 |
32 |
33 |
34 |
35 | 36 |
37 |
38 |

Packet destination

39 | : 40 |
41 |
42 |
43 |
44 |

ACL rules

45 |

46 | 47 | 48 |
49 |
50 |

Result

51 |
52 |
53 |
54 |
55 |
56 | 57 | 58 | 59 | 60 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /templates/result.html: -------------------------------------------------------------------------------- 1 | $def with (results, graphname) 2 | 3 | 4 | 5 | 6 | 7 | 8 | $for num, (status, rule, reasons) in enumerate(results): 9 | 10 | 11 | $if status == True: 12 | $if rule.startswith('permit'): 13 | 14 | $elif rule.startswith('deny'): 15 | 16 | $else: 17 | 18 | 19 | 20 |
#RuleReason
${num + 1}
$rule
$rule
$rule$', '.join(reasons)
21 |

Network vizualization

22 | 23 | -------------------------------------------------------------------------------- /web.py-0.37/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: web.py 3 | Version: 0.37 4 | Summary: web.py: makes web apps 5 | Home-page: http://webpy.org/ 6 | Author: Anand Chitipothu 7 | Author-email: anandology@gmail.com 8 | License: Public domain 9 | Description: Think about the ideal way to write a web app. Write the code to make it happen. 10 | Platform: any 11 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """web.py: makes web apps (http://webpy.org)""" 3 | 4 | from __future__ import generators 5 | 6 | __version__ = "0.37" 7 | __author__ = [ 8 | "Aaron Swartz ", 9 | "Anand Chitipothu " 10 | ] 11 | __license__ = "public domain" 12 | __contributors__ = "see http://webpy.org/changes" 13 | 14 | import utils, db, net, wsgi, http, webapi, httpserver, debugerror 15 | import template, form 16 | 17 | import session 18 | 19 | from utils import * 20 | from db import * 21 | from net import * 22 | from wsgi import * 23 | from http import * 24 | from webapi import * 25 | from httpserver import * 26 | from debugerror import * 27 | from application import * 28 | from browser import * 29 | try: 30 | import webopenid as openid 31 | except ImportError: 32 | pass # requires openid module 33 | 34 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/browser.py: -------------------------------------------------------------------------------- 1 | """Browser to test web applications. 2 | (from web.py) 3 | """ 4 | from utils import re_compile 5 | from net import htmlunquote 6 | 7 | import httplib, urllib, urllib2 8 | import copy 9 | from StringIO import StringIO 10 | 11 | DEBUG = False 12 | 13 | __all__ = [ 14 | "BrowserError", 15 | "Browser", "AppBrowser", 16 | "AppHandler" 17 | ] 18 | 19 | class BrowserError(Exception): 20 | pass 21 | 22 | class Browser: 23 | def __init__(self): 24 | import cookielib 25 | self.cookiejar = cookielib.CookieJar() 26 | self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar) 27 | self.form = None 28 | 29 | self.url = "http://0.0.0.0:8080/" 30 | self.path = "/" 31 | 32 | self.status = None 33 | self.data = None 34 | self._response = None 35 | self._forms = None 36 | 37 | def reset(self): 38 | """Clears all cookies and history.""" 39 | self.cookiejar.clear() 40 | 41 | def build_opener(self): 42 | """Builds the opener using urllib2.build_opener. 43 | Subclasses can override this function to prodive custom openers. 44 | """ 45 | return urllib2.build_opener() 46 | 47 | def do_request(self, req): 48 | if DEBUG: 49 | print 'requesting', req.get_method(), req.get_full_url() 50 | opener = self.build_opener() 51 | opener.add_handler(self._cookie_processor) 52 | try: 53 | self._response = opener.open(req) 54 | except urllib2.HTTPError, e: 55 | self._response = e 56 | 57 | self.url = self._response.geturl() 58 | self.path = urllib2.Request(self.url).get_selector() 59 | self.data = self._response.read() 60 | self.status = self._response.code 61 | self._forms = None 62 | self.form = None 63 | return self.get_response() 64 | 65 | def open(self, url, data=None, headers={}): 66 | """Opens the specified url.""" 67 | url = urllib.basejoin(self.url, url) 68 | req = urllib2.Request(url, data, headers) 69 | return self.do_request(req) 70 | 71 | def show(self): 72 | """Opens the current page in real web browser.""" 73 | f = open('page.html', 'w') 74 | f.write(self.data) 75 | f.close() 76 | 77 | import webbrowser, os 78 | url = 'file://' + os.path.abspath('page.html') 79 | webbrowser.open(url) 80 | 81 | def get_response(self): 82 | """Returns a copy of the current response.""" 83 | return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl()) 84 | 85 | def get_soup(self): 86 | """Returns beautiful soup of the current document.""" 87 | import BeautifulSoup 88 | return BeautifulSoup.BeautifulSoup(self.data) 89 | 90 | def get_text(self, e=None): 91 | """Returns content of e or the current document as plain text.""" 92 | e = e or self.get_soup() 93 | return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)]) 94 | 95 | def _get_links(self): 96 | soup = self.get_soup() 97 | return [a for a in soup.findAll(name='a')] 98 | 99 | def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None): 100 | """Returns all links in the document.""" 101 | return self._filter_links(self._get_links(), 102 | text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate) 103 | 104 | def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None): 105 | if link is None: 106 | links = self._filter_links(self.get_links(), 107 | text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate) 108 | link = links and links[0] 109 | 110 | if link: 111 | return self.open(link['href']) 112 | else: 113 | raise BrowserError("No link found") 114 | 115 | def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None): 116 | links = self._filter_links(self.get_links(), 117 | text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate) 118 | return links and links[0] or None 119 | 120 | def _filter_links(self, links, 121 | text=None, text_regex=None, 122 | url=None, url_regex=None, 123 | predicate=None): 124 | predicates = [] 125 | if text is not None: 126 | predicates.append(lambda link: link.string == text) 127 | if text_regex is not None: 128 | predicates.append(lambda link: re_compile(text_regex).search(link.string or '')) 129 | if url is not None: 130 | predicates.append(lambda link: link.get('href') == url) 131 | if url_regex is not None: 132 | predicates.append(lambda link: re_compile(url_regex).search(link.get('href', ''))) 133 | if predicate: 134 | predicate.append(predicate) 135 | 136 | def f(link): 137 | for p in predicates: 138 | if not p(link): 139 | return False 140 | return True 141 | 142 | return [link for link in links if f(link)] 143 | 144 | def get_forms(self): 145 | """Returns all forms in the current document. 146 | The returned form objects implement the ClientForm.HTMLForm interface. 147 | """ 148 | if self._forms is None: 149 | import ClientForm 150 | self._forms = ClientForm.ParseResponse(self.get_response(), backwards_compat=False) 151 | return self._forms 152 | 153 | def select_form(self, name=None, predicate=None, index=0): 154 | """Selects the specified form.""" 155 | forms = self.get_forms() 156 | 157 | if name is not None: 158 | forms = [f for f in forms if f.name == name] 159 | if predicate: 160 | forms = [f for f in forms if predicate(f)] 161 | 162 | if forms: 163 | self.form = forms[index] 164 | return self.form 165 | else: 166 | raise BrowserError("No form selected.") 167 | 168 | def submit(self, **kw): 169 | """submits the currently selected form.""" 170 | if self.form is None: 171 | raise BrowserError("No form selected.") 172 | req = self.form.click(**kw) 173 | return self.do_request(req) 174 | 175 | def __getitem__(self, key): 176 | return self.form[key] 177 | 178 | def __setitem__(self, key, value): 179 | self.form[key] = value 180 | 181 | class AppBrowser(Browser): 182 | """Browser interface to test web.py apps. 183 | 184 | b = AppBrowser(app) 185 | b.open('/') 186 | b.follow_link(text='Login') 187 | 188 | b.select_form(name='login') 189 | b['username'] = 'joe' 190 | b['password'] = 'secret' 191 | b.submit() 192 | 193 | assert b.path == '/' 194 | assert 'Welcome joe' in b.get_text() 195 | """ 196 | def __init__(self, app): 197 | Browser.__init__(self) 198 | self.app = app 199 | 200 | def build_opener(self): 201 | return urllib2.build_opener(AppHandler(self.app)) 202 | 203 | class AppHandler(urllib2.HTTPHandler): 204 | """urllib2 handler to handle requests using web.py application.""" 205 | handler_order = 100 206 | 207 | def __init__(self, app): 208 | self.app = app 209 | 210 | def http_open(self, req): 211 | result = self.app.request( 212 | localpart=req.get_selector(), 213 | method=req.get_method(), 214 | host=req.get_host(), 215 | data=req.get_data(), 216 | headers=dict(req.header_items()), 217 | https=req.get_type() == "https" 218 | ) 219 | return self._make_response(result, req.get_full_url()) 220 | 221 | def https_open(self, req): 222 | return self.http_open(req) 223 | 224 | try: 225 | https_request = urllib2.HTTPHandler.do_request_ 226 | except AttributeError: 227 | # for python 2.3 228 | pass 229 | 230 | def _make_response(self, result, url): 231 | data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items]) 232 | headers = httplib.HTTPMessage(StringIO(data)) 233 | response = urllib.addinfourl(StringIO(result.data), headers, url) 234 | code, msg = result.status.split(None, 1) 235 | response.code, response.msg = int(code), msg 236 | return response 237 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenzymadness/Cisco-ACL-Analyzer/e01ea94705ccd6bfc3f18702056f2e814a7b46b6/web.py-0.37/build/lib.linux-x86_64-2.7/web/contrib/__init__.py -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/contrib/template.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface to various templating engines. 3 | """ 4 | import os.path 5 | 6 | __all__ = [ 7 | "render_cheetah", "render_genshi", "render_mako", 8 | "cache", 9 | ] 10 | 11 | class render_cheetah: 12 | """Rendering interface to Cheetah Templates. 13 | 14 | Example: 15 | 16 | render = render_cheetah('templates') 17 | render.hello(name="cheetah") 18 | """ 19 | def __init__(self, path): 20 | # give error if Chetah is not installed 21 | from Cheetah.Template import Template 22 | self.path = path 23 | 24 | def __getattr__(self, name): 25 | from Cheetah.Template import Template 26 | path = os.path.join(self.path, name + ".html") 27 | 28 | def template(**kw): 29 | t = Template(file=path, searchList=[kw]) 30 | return t.respond() 31 | 32 | return template 33 | 34 | class render_genshi: 35 | """Rendering interface genshi templates. 36 | Example: 37 | 38 | for xml/html templates. 39 | 40 | render = render_genshi(['templates/']) 41 | render.hello(name='genshi') 42 | 43 | For text templates: 44 | 45 | render = render_genshi(['templates/'], type='text') 46 | render.hello(name='genshi') 47 | """ 48 | 49 | def __init__(self, *a, **kwargs): 50 | from genshi.template import TemplateLoader 51 | 52 | self._type = kwargs.pop('type', None) 53 | self._loader = TemplateLoader(*a, **kwargs) 54 | 55 | def __getattr__(self, name): 56 | # Assuming all templates are html 57 | path = name + ".html" 58 | 59 | if self._type == "text": 60 | from genshi.template import TextTemplate 61 | cls = TextTemplate 62 | type = "text" 63 | else: 64 | cls = None 65 | type = None 66 | 67 | t = self._loader.load(path, cls=cls) 68 | def template(**kw): 69 | stream = t.generate(**kw) 70 | if type: 71 | return stream.render(type) 72 | else: 73 | return stream.render() 74 | return template 75 | 76 | class render_jinja: 77 | """Rendering interface to Jinja2 Templates 78 | 79 | Example: 80 | 81 | render= render_jinja('templates') 82 | render.hello(name='jinja2') 83 | """ 84 | def __init__(self, *a, **kwargs): 85 | extensions = kwargs.pop('extensions', []) 86 | globals = kwargs.pop('globals', {}) 87 | 88 | from jinja2 import Environment,FileSystemLoader 89 | self._lookup = Environment(loader=FileSystemLoader(*a, **kwargs), extensions=extensions) 90 | self._lookup.globals.update(globals) 91 | 92 | def __getattr__(self, name): 93 | # Assuming all templates end with .html 94 | path = name + '.html' 95 | t = self._lookup.get_template(path) 96 | return t.render 97 | 98 | class render_mako: 99 | """Rendering interface to Mako Templates. 100 | 101 | Example: 102 | 103 | render = render_mako(directories=['templates']) 104 | render.hello(name="mako") 105 | """ 106 | def __init__(self, *a, **kwargs): 107 | from mako.lookup import TemplateLookup 108 | self._lookup = TemplateLookup(*a, **kwargs) 109 | 110 | def __getattr__(self, name): 111 | # Assuming all templates are html 112 | path = name + ".html" 113 | t = self._lookup.get_template(path) 114 | return t.render 115 | 116 | class cache: 117 | """Cache for any rendering interface. 118 | 119 | Example: 120 | 121 | render = cache(render_cheetah("templates/")) 122 | render.hello(name='cache') 123 | """ 124 | def __init__(self, render): 125 | self._render = render 126 | self._cache = {} 127 | 128 | def __getattr__(self, name): 129 | if name not in self._cache: 130 | self._cache[name] = getattr(self._render, name) 131 | return self._cache[name] 132 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/debugerror.py: -------------------------------------------------------------------------------- 1 | """ 2 | pretty debug errors 3 | (part of web.py) 4 | 5 | portions adapted from Django 6 | Copyright (c) 2005, the Lawrence Journal-World 7 | Used under the modified BSD license: 8 | http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 9 | """ 10 | 11 | __all__ = ["debugerror", "djangoerror", "emailerrors"] 12 | 13 | import sys, urlparse, pprint, traceback 14 | from template import Template 15 | from net import websafe 16 | from utils import sendmail, safestr 17 | import webapi as web 18 | 19 | import os, os.path 20 | whereami = os.path.join(os.getcwd(), __file__) 21 | whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1]) 22 | djangoerror_t = """\ 23 | $def with (exception_type, exception_value, frames) 24 | 25 | 26 | 27 | 28 | 29 | $exception_type at $ctx.path 30 | 79 | 127 | 128 | 129 | 130 | $def dicttable (d, kls='req', id=None): 131 | $ items = d and d.items() or [] 132 | $items.sort() 133 | $:dicttable_items(items, kls, id) 134 | 135 | $def dicttable_items(items, kls='req', id=None): 136 | $if items: 137 | 140 | 141 | $for k, v in items: 142 | 143 | 144 |
VariableValue
$k
$prettify(v)
145 | $else: 146 |

No data.

147 | 148 |
149 |

$exception_type at $ctx.path

150 |

$exception_value

151 | 152 | 153 | 154 | 155 | 156 | 157 |
Python$frames[0].filename in $frames[0].function, line $frames[0].lineno
Web$ctx.method $ctx.home$ctx.path
158 |
159 |
160 |

Traceback (innermost first)

161 |
    162 | $for frame in frames: 163 |
  • 164 | $frame.filename in $frame.function 165 | $if frame.context_line is not None: 166 |
    167 | $if frame.pre_context: 168 |
      169 | $for line in frame.pre_context: 170 |
    1. $line
    2. 171 |
    172 |
    1. $frame.context_line ...
    173 | $if frame.post_context: 174 |
      175 | $for line in frame.post_context: 176 |
    1. $line
    2. 177 |
    178 |
    179 | 180 | $if frame.vars: 181 |
    182 | Local vars 183 | $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame)) 184 |
    185 | $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id))) 186 |
  • 187 |
188 |
189 | 190 |
191 | $if ctx.output or ctx.headers: 192 |

Response so far

193 |

HEADERS

194 | $:dicttable_items(ctx.headers) 195 | 196 |

BODY

197 |

198 | $ctx.output 199 |

200 | 201 |

Request information

202 | 203 |

INPUT

204 | $:dicttable(web.input(_unicode=False)) 205 | 206 | 207 | $:dicttable(web.cookies()) 208 | 209 |

META

210 | $ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)] 211 | $:dicttable(dict(newctx)) 212 | 213 |

ENVIRONMENT

214 | $:dicttable(ctx.env) 215 |
216 | 217 |
218 |

219 | You're seeing this error because you have web.config.debug 220 | set to True. Set that to False if you don't want to see this. 221 |

222 |
223 | 224 | 225 | 226 | """ 227 | 228 | djangoerror_r = None 229 | 230 | def djangoerror(): 231 | def _get_lines_from_file(filename, lineno, context_lines): 232 | """ 233 | Returns context_lines before and after lineno from file. 234 | Returns (pre_context_lineno, pre_context, context_line, post_context). 235 | """ 236 | try: 237 | source = open(filename).readlines() 238 | lower_bound = max(0, lineno - context_lines) 239 | upper_bound = lineno + context_lines 240 | 241 | pre_context = \ 242 | [line.strip('\n') for line in source[lower_bound:lineno]] 243 | context_line = source[lineno].strip('\n') 244 | post_context = \ 245 | [line.strip('\n') for line in source[lineno + 1:upper_bound]] 246 | 247 | return lower_bound, pre_context, context_line, post_context 248 | except (OSError, IOError, IndexError): 249 | return None, [], None, [] 250 | 251 | exception_type, exception_value, tback = sys.exc_info() 252 | frames = [] 253 | while tback is not None: 254 | filename = tback.tb_frame.f_code.co_filename 255 | function = tback.tb_frame.f_code.co_name 256 | lineno = tback.tb_lineno - 1 257 | 258 | # hack to get correct line number for templates 259 | lineno += tback.tb_frame.f_locals.get("__lineoffset__", 0) 260 | 261 | pre_context_lineno, pre_context, context_line, post_context = \ 262 | _get_lines_from_file(filename, lineno, 7) 263 | 264 | if '__hidetraceback__' not in tback.tb_frame.f_locals: 265 | frames.append(web.storage({ 266 | 'tback': tback, 267 | 'filename': filename, 268 | 'function': function, 269 | 'lineno': lineno, 270 | 'vars': tback.tb_frame.f_locals, 271 | 'id': id(tback), 272 | 'pre_context': pre_context, 273 | 'context_line': context_line, 274 | 'post_context': post_context, 275 | 'pre_context_lineno': pre_context_lineno, 276 | })) 277 | tback = tback.tb_next 278 | frames.reverse() 279 | urljoin = urlparse.urljoin 280 | def prettify(x): 281 | try: 282 | out = pprint.pformat(x) 283 | except Exception, e: 284 | out = '[could not display: <' + e.__class__.__name__ + \ 285 | ': '+str(e)+'>]' 286 | return out 287 | 288 | global djangoerror_r 289 | if djangoerror_r is None: 290 | djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe) 291 | 292 | t = djangoerror_r 293 | globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify} 294 | t.t.func_globals.update(globals) 295 | return t(exception_type, exception_value, frames) 296 | 297 | def debugerror(): 298 | """ 299 | A replacement for `internalerror` that presents a nice page with lots 300 | of debug information for the programmer. 301 | 302 | (Based on the beautiful 500 page from [Django](http://djangoproject.com/), 303 | designed by [Wilson Miner](http://wilsonminer.com/).) 304 | """ 305 | return web._InternalError(djangoerror()) 306 | 307 | def emailerrors(to_address, olderror, from_address=None): 308 | """ 309 | Wraps the old `internalerror` handler (pass as `olderror`) to 310 | additionally email all errors to `to_address`, to aid in 311 | debugging production websites. 312 | 313 | Emails contain a normal text traceback as well as an 314 | attachment containing the nice `debugerror` page. 315 | """ 316 | from_address = from_address or to_address 317 | 318 | def emailerrors_internal(): 319 | error = olderror() 320 | tb = sys.exc_info() 321 | error_name = tb[0] 322 | error_value = tb[1] 323 | tb_txt = ''.join(traceback.format_exception(*tb)) 324 | path = web.ctx.path 325 | request = web.ctx.method + ' ' + web.ctx.home + web.ctx.fullpath 326 | 327 | message = "\n%s\n\n%s\n\n" % (request, tb_txt) 328 | 329 | sendmail( 330 | "your buggy site <%s>" % from_address, 331 | "the bugfixer <%s>" % to_address, 332 | "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(), 333 | message, 334 | attachments=[ 335 | dict(filename="bug.html", content=safestr(djangoerror())) 336 | ], 337 | ) 338 | return error 339 | 340 | return emailerrors_internal 341 | 342 | if __name__ == "__main__": 343 | urls = ( 344 | '/', 'index' 345 | ) 346 | from application import application 347 | app = application(urls, globals()) 348 | app.internalerror = debugerror 349 | 350 | class index: 351 | def GET(self): 352 | thisdoesnotexist 353 | 354 | app.run() 355 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/form.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTML forms 3 | (part of web.py) 4 | """ 5 | 6 | import copy, re 7 | import webapi as web 8 | import utils, net 9 | 10 | def attrget(obj, attr, value=None): 11 | try: 12 | if hasattr(obj, 'has_key') and obj.has_key(attr): 13 | return obj[attr] 14 | except TypeError: 15 | # Handle the case where has_key takes different number of arguments. 16 | # This is the case with Model objects on appengine. See #134 17 | pass 18 | if hasattr(obj, attr): 19 | return getattr(obj, attr) 20 | return value 21 | 22 | class Form(object): 23 | r""" 24 | HTML form. 25 | 26 | >>> f = Form(Textbox("x")) 27 | >>> f.render() 28 | u'\n \n
' 29 | """ 30 | def __init__(self, *inputs, **kw): 31 | self.inputs = inputs 32 | self.valid = True 33 | self.note = None 34 | self.validators = kw.pop('validators', []) 35 | 36 | def __call__(self, x=None): 37 | o = copy.deepcopy(self) 38 | if x: o.validates(x) 39 | return o 40 | 41 | def render(self): 42 | out = '' 43 | out += self.rendernote(self.note) 44 | out += '\n' 45 | 46 | for i in self.inputs: 47 | html = utils.safeunicode(i.pre) + i.render() + self.rendernote(i.note) + utils.safeunicode(i.post) 48 | if i.is_hidden(): 49 | out += ' \n' % (html) 50 | else: 51 | out += ' \n' % (i.id, net.websafe(i.description), html) 52 | out += "
%s
%s
" 53 | return out 54 | 55 | def render_css(self): 56 | out = [] 57 | out.append(self.rendernote(self.note)) 58 | for i in self.inputs: 59 | if not i.is_hidden(): 60 | out.append('' % (i.id, net.websafe(i.description))) 61 | out.append(i.pre) 62 | out.append(i.render()) 63 | out.append(self.rendernote(i.note)) 64 | out.append(i.post) 65 | out.append('\n') 66 | return ''.join(out) 67 | 68 | def rendernote(self, note): 69 | if note: return '%s' % net.websafe(note) 70 | else: return "" 71 | 72 | def validates(self, source=None, _validate=True, **kw): 73 | source = source or kw or web.input() 74 | out = True 75 | for i in self.inputs: 76 | v = attrget(source, i.name) 77 | if _validate: 78 | out = i.validate(v) and out 79 | else: 80 | i.set_value(v) 81 | if _validate: 82 | out = out and self._validate(source) 83 | self.valid = out 84 | return out 85 | 86 | def _validate(self, value): 87 | self.value = value 88 | for v in self.validators: 89 | if not v.valid(value): 90 | self.note = v.msg 91 | return False 92 | return True 93 | 94 | def fill(self, source=None, **kw): 95 | return self.validates(source, _validate=False, **kw) 96 | 97 | def __getitem__(self, i): 98 | for x in self.inputs: 99 | if x.name == i: return x 100 | raise KeyError, i 101 | 102 | def __getattr__(self, name): 103 | # don't interfere with deepcopy 104 | inputs = self.__dict__.get('inputs') or [] 105 | for x in inputs: 106 | if x.name == name: return x 107 | raise AttributeError, name 108 | 109 | def get(self, i, default=None): 110 | try: 111 | return self[i] 112 | except KeyError: 113 | return default 114 | 115 | def _get_d(self): #@@ should really be form.attr, no? 116 | return utils.storage([(i.name, i.get_value()) for i in self.inputs]) 117 | d = property(_get_d) 118 | 119 | class Input(object): 120 | def __init__(self, name, *validators, **attrs): 121 | self.name = name 122 | self.validators = validators 123 | self.attrs = attrs = AttributeList(attrs) 124 | 125 | self.description = attrs.pop('description', name) 126 | self.value = attrs.pop('value', None) 127 | self.pre = attrs.pop('pre', "") 128 | self.post = attrs.pop('post', "") 129 | self.note = None 130 | 131 | self.id = attrs.setdefault('id', self.get_default_id()) 132 | 133 | if 'class_' in attrs: 134 | attrs['class'] = attrs['class_'] 135 | del attrs['class_'] 136 | 137 | def is_hidden(self): 138 | return False 139 | 140 | def get_type(self): 141 | raise NotImplementedError 142 | 143 | def get_default_id(self): 144 | return self.name 145 | 146 | def validate(self, value): 147 | self.set_value(value) 148 | 149 | for v in self.validators: 150 | if not v.valid(value): 151 | self.note = v.msg 152 | return False 153 | return True 154 | 155 | def set_value(self, value): 156 | self.value = value 157 | 158 | def get_value(self): 159 | return self.value 160 | 161 | def render(self): 162 | attrs = self.attrs.copy() 163 | attrs['type'] = self.get_type() 164 | if self.value is not None: 165 | attrs['value'] = self.value 166 | attrs['name'] = self.name 167 | return '' % attrs 168 | 169 | def rendernote(self, note): 170 | if note: return '%s' % net.websafe(note) 171 | else: return "" 172 | 173 | def addatts(self): 174 | # add leading space for backward-compatibility 175 | return " " + str(self.attrs) 176 | 177 | class AttributeList(dict): 178 | """List of atributes of input. 179 | 180 | >>> a = AttributeList(type='text', name='x', value=20) 181 | >>> a 182 | 183 | """ 184 | def copy(self): 185 | return AttributeList(self) 186 | 187 | def __str__(self): 188 | return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()]) 189 | 190 | def __repr__(self): 191 | return '' % repr(str(self)) 192 | 193 | class Textbox(Input): 194 | """Textbox input. 195 | 196 | >>> Textbox(name='foo', value='bar').render() 197 | u'' 198 | >>> Textbox(name='foo', value=0).render() 199 | u'' 200 | """ 201 | def get_type(self): 202 | return 'text' 203 | 204 | class Password(Input): 205 | """Password input. 206 | 207 | >>> Password(name='password', value='secret').render() 208 | u'' 209 | """ 210 | 211 | def get_type(self): 212 | return 'password' 213 | 214 | class Textarea(Input): 215 | """Textarea input. 216 | 217 | >>> Textarea(name='foo', value='bar').render() 218 | u'' 219 | """ 220 | def render(self): 221 | attrs = self.attrs.copy() 222 | attrs['name'] = self.name 223 | value = net.websafe(self.value or '') 224 | return '' % (attrs, value) 225 | 226 | class Dropdown(Input): 227 | r"""Dropdown/select input. 228 | 229 | >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render() 230 | u'\n' 231 | >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render() 232 | u'\n' 233 | """ 234 | def __init__(self, name, args, *validators, **attrs): 235 | self.args = args 236 | super(Dropdown, self).__init__(name, *validators, **attrs) 237 | 238 | def render(self): 239 | attrs = self.attrs.copy() 240 | attrs['name'] = self.name 241 | 242 | x = '\n' 248 | return x 249 | 250 | def _render_option(self, arg, indent=' '): 251 | if isinstance(arg, (tuple, list)): 252 | value, desc= arg 253 | else: 254 | value, desc = arg, arg 255 | 256 | if self.value == value or (isinstance(self.value, list) and value in self.value): 257 | select_p = ' selected="selected"' 258 | else: 259 | select_p = '' 260 | return indent + '%s\n' % (select_p, net.websafe(value), net.websafe(desc)) 261 | 262 | 263 | class GroupedDropdown(Dropdown): 264 | r"""Grouped Dropdown/select input. 265 | 266 | >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render() 267 | u'\n' 268 | >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render() 269 | u'\n' 270 | 271 | """ 272 | def __init__(self, name, args, *validators, **attrs): 273 | self.args = args 274 | super(Dropdown, self).__init__(name, *validators, **attrs) 275 | 276 | def render(self): 277 | attrs = self.attrs.copy() 278 | attrs['name'] = self.name 279 | 280 | x = '\n' 289 | return x 290 | 291 | class Radio(Input): 292 | def __init__(self, name, args, *validators, **attrs): 293 | self.args = args 294 | super(Radio, self).__init__(name, *validators, **attrs) 295 | 296 | def render(self): 297 | x = '' 298 | for arg in self.args: 299 | if isinstance(arg, (tuple, list)): 300 | value, desc= arg 301 | else: 302 | value, desc = arg, arg 303 | attrs = self.attrs.copy() 304 | attrs['name'] = self.name 305 | attrs['type'] = 'radio' 306 | attrs['value'] = value 307 | if self.value == value: 308 | attrs['checked'] = 'checked' 309 | x += ' %s' % (attrs, net.websafe(desc)) 310 | x += '' 311 | return x 312 | 313 | class Checkbox(Input): 314 | """Checkbox input. 315 | 316 | >>> Checkbox('foo', value='bar', checked=True).render() 317 | u'' 318 | >>> Checkbox('foo', value='bar').render() 319 | u'' 320 | >>> c = Checkbox('foo', value='bar') 321 | >>> c.validate('on') 322 | True 323 | >>> c.render() 324 | u'' 325 | """ 326 | def __init__(self, name, *validators, **attrs): 327 | self.checked = attrs.pop('checked', False) 328 | Input.__init__(self, name, *validators, **attrs) 329 | 330 | def get_default_id(self): 331 | value = utils.safestr(self.value or "") 332 | return self.name + '_' + value.replace(' ', '_') 333 | 334 | def render(self): 335 | attrs = self.attrs.copy() 336 | attrs['type'] = 'checkbox' 337 | attrs['name'] = self.name 338 | attrs['value'] = self.value 339 | 340 | if self.checked: 341 | attrs['checked'] = 'checked' 342 | return '' % attrs 343 | 344 | def set_value(self, value): 345 | self.checked = bool(value) 346 | 347 | def get_value(self): 348 | return self.checked 349 | 350 | class Button(Input): 351 | """HTML Button. 352 | 353 | >>> Button("save").render() 354 | u'' 355 | >>> Button("action", value="save", html="Save Changes").render() 356 | u'' 357 | """ 358 | def __init__(self, name, *validators, **attrs): 359 | super(Button, self).__init__(name, *validators, **attrs) 360 | self.description = "" 361 | 362 | def render(self): 363 | attrs = self.attrs.copy() 364 | attrs['name'] = self.name 365 | if self.value is not None: 366 | attrs['value'] = self.value 367 | html = attrs.pop('html', None) or net.websafe(self.name) 368 | return '' % (attrs, html) 369 | 370 | class Hidden(Input): 371 | """Hidden Input. 372 | 373 | >>> Hidden(name='foo', value='bar').render() 374 | u'' 375 | """ 376 | def is_hidden(self): 377 | return True 378 | 379 | def get_type(self): 380 | return 'hidden' 381 | 382 | class File(Input): 383 | """File input. 384 | 385 | >>> File(name='f').render() 386 | u'' 387 | """ 388 | def get_type(self): 389 | return 'file' 390 | 391 | class Validator: 392 | def __deepcopy__(self, memo): return copy.copy(self) 393 | def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals()) 394 | def valid(self, value): 395 | try: return self.test(value) 396 | except: return False 397 | 398 | notnull = Validator("Required", bool) 399 | 400 | class regexp(Validator): 401 | def __init__(self, rexp, msg): 402 | self.rexp = re.compile(rexp) 403 | self.msg = msg 404 | 405 | def valid(self, value): 406 | return bool(self.rexp.match(value)) 407 | 408 | if __name__ == "__main__": 409 | import doctest 410 | doctest.testmod() 411 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/http.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP Utilities 3 | (from web.py) 4 | """ 5 | 6 | __all__ = [ 7 | "expires", "lastmodified", 8 | "prefixurl", "modified", 9 | "changequery", "url", 10 | "profiler", 11 | ] 12 | 13 | import sys, os, threading, urllib, urlparse 14 | try: import datetime 15 | except ImportError: pass 16 | import net, utils, webapi as web 17 | 18 | def prefixurl(base=''): 19 | """ 20 | Sorry, this function is really difficult to explain. 21 | Maybe some other time. 22 | """ 23 | url = web.ctx.path.lstrip('/') 24 | for i in xrange(url.count('/')): 25 | base += '../' 26 | if not base: 27 | base = './' 28 | return base 29 | 30 | def expires(delta): 31 | """ 32 | Outputs an `Expires` header for `delta` from now. 33 | `delta` is a `timedelta` object or a number of seconds. 34 | """ 35 | if isinstance(delta, (int, long)): 36 | delta = datetime.timedelta(seconds=delta) 37 | date_obj = datetime.datetime.utcnow() + delta 38 | web.header('Expires', net.httpdate(date_obj)) 39 | 40 | def lastmodified(date_obj): 41 | """Outputs a `Last-Modified` header for `datetime`.""" 42 | web.header('Last-Modified', net.httpdate(date_obj)) 43 | 44 | def modified(date=None, etag=None): 45 | """ 46 | Checks to see if the page has been modified since the version in the 47 | requester's cache. 48 | 49 | When you publish pages, you can include `Last-Modified` and `ETag` 50 | with the date the page was last modified and an opaque token for 51 | the particular version, respectively. When readers reload the page, 52 | the browser sends along the modification date and etag value for 53 | the version it has in its cache. If the page hasn't changed, 54 | the server can just return `304 Not Modified` and not have to 55 | send the whole page again. 56 | 57 | This function takes the last-modified date `date` and the ETag `etag` 58 | and checks the headers to see if they match. If they do, it returns 59 | `True`, or otherwise it raises NotModified error. It also sets 60 | `Last-Modified` and `ETag` output headers. 61 | """ 62 | try: 63 | from __builtin__ import set 64 | except ImportError: 65 | # for python 2.3 66 | from sets import Set as set 67 | 68 | n = set([x.strip('" ') for x in web.ctx.env.get('HTTP_IF_NONE_MATCH', '').split(',')]) 69 | m = net.parsehttpdate(web.ctx.env.get('HTTP_IF_MODIFIED_SINCE', '').split(';')[0]) 70 | validate = False 71 | if etag: 72 | if '*' in n or etag in n: 73 | validate = True 74 | if date and m: 75 | # we subtract a second because 76 | # HTTP dates don't have sub-second precision 77 | if date-datetime.timedelta(seconds=1) <= m: 78 | validate = True 79 | 80 | if date: lastmodified(date) 81 | if etag: web.header('ETag', '"' + etag + '"') 82 | if validate: 83 | raise web.notmodified() 84 | else: 85 | return True 86 | 87 | def urlencode(query, doseq=0): 88 | """ 89 | Same as urllib.urlencode, but supports unicode strings. 90 | 91 | >>> urlencode({'text':'foo bar'}) 92 | 'text=foo+bar' 93 | >>> urlencode({'x': [1, 2]}, doseq=True) 94 | 'x=1&x=2' 95 | """ 96 | def convert(value, doseq=False): 97 | if doseq and isinstance(value, list): 98 | return [convert(v) for v in value] 99 | else: 100 | return utils.safestr(value) 101 | 102 | query = dict([(k, convert(v, doseq)) for k, v in query.items()]) 103 | return urllib.urlencode(query, doseq=doseq) 104 | 105 | def changequery(query=None, **kw): 106 | """ 107 | Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return 108 | `/foo?a=3&b=2` -- the same URL but with the arguments you requested 109 | changed. 110 | """ 111 | if query is None: 112 | query = web.rawinput(method='get') 113 | for k, v in kw.iteritems(): 114 | if v is None: 115 | query.pop(k, None) 116 | else: 117 | query[k] = v 118 | out = web.ctx.path 119 | if query: 120 | out += '?' + urlencode(query, doseq=True) 121 | return out 122 | 123 | def url(path=None, doseq=False, **kw): 124 | """ 125 | Makes url by concatenating web.ctx.homepath and path and the 126 | query string created using the arguments. 127 | """ 128 | if path is None: 129 | path = web.ctx.path 130 | if path.startswith("/"): 131 | out = web.ctx.homepath + path 132 | else: 133 | out = path 134 | 135 | if kw: 136 | out += '?' + urlencode(kw, doseq=doseq) 137 | 138 | return out 139 | 140 | def profiler(app): 141 | """Outputs basic profiling information at the bottom of each response.""" 142 | from utils import profile 143 | def profile_internal(e, o): 144 | out, result = profile(app)(e, o) 145 | return list(out) + ['
' + net.websafe(result) + '
'] 146 | return profile_internal 147 | 148 | if __name__ == "__main__": 149 | import doctest 150 | doctest.testmod() 151 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/httpserver.py: -------------------------------------------------------------------------------- 1 | __all__ = ["runsimple"] 2 | 3 | import sys, os 4 | from SimpleHTTPServer import SimpleHTTPRequestHandler 5 | import urllib 6 | import posixpath 7 | 8 | import webapi as web 9 | import net 10 | import utils 11 | 12 | def runbasic(func, server_address=("0.0.0.0", 8080)): 13 | """ 14 | Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` 15 | is hosted statically. 16 | 17 | Based on [WsgiServer][ws] from [Colin Stewart][cs]. 18 | 19 | [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html 20 | [cs]: http://www.owlfish.com/ 21 | """ 22 | # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/) 23 | # Modified somewhat for simplicity 24 | # Used under the modified BSD license: 25 | # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 26 | 27 | import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse 28 | import socket, errno 29 | import traceback 30 | 31 | class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 32 | def run_wsgi_app(self): 33 | protocol, host, path, parameters, query, fragment = \ 34 | urlparse.urlparse('http://dummyhost%s' % self.path) 35 | 36 | # we only use path, query 37 | env = {'wsgi.version': (1, 0) 38 | ,'wsgi.url_scheme': 'http' 39 | ,'wsgi.input': self.rfile 40 | ,'wsgi.errors': sys.stderr 41 | ,'wsgi.multithread': 1 42 | ,'wsgi.multiprocess': 0 43 | ,'wsgi.run_once': 0 44 | ,'REQUEST_METHOD': self.command 45 | ,'REQUEST_URI': self.path 46 | ,'PATH_INFO': path 47 | ,'QUERY_STRING': query 48 | ,'CONTENT_TYPE': self.headers.get('Content-Type', '') 49 | ,'CONTENT_LENGTH': self.headers.get('Content-Length', '') 50 | ,'REMOTE_ADDR': self.client_address[0] 51 | ,'SERVER_NAME': self.server.server_address[0] 52 | ,'SERVER_PORT': str(self.server.server_address[1]) 53 | ,'SERVER_PROTOCOL': self.request_version 54 | } 55 | 56 | for http_header, http_value in self.headers.items(): 57 | env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \ 58 | http_value 59 | 60 | # Setup the state 61 | self.wsgi_sent_headers = 0 62 | self.wsgi_headers = [] 63 | 64 | try: 65 | # We have there environment, now invoke the application 66 | result = self.server.app(env, self.wsgi_start_response) 67 | try: 68 | try: 69 | for data in result: 70 | if data: 71 | self.wsgi_write_data(data) 72 | finally: 73 | if hasattr(result, 'close'): 74 | result.close() 75 | except socket.error, socket_err: 76 | # Catch common network errors and suppress them 77 | if (socket_err.args[0] in \ 78 | (errno.ECONNABORTED, errno.EPIPE)): 79 | return 80 | except socket.timeout, socket_timeout: 81 | return 82 | except: 83 | print >> web.debug, traceback.format_exc(), 84 | 85 | if (not self.wsgi_sent_headers): 86 | # We must write out something! 87 | self.wsgi_write_data(" ") 88 | return 89 | 90 | do_POST = run_wsgi_app 91 | do_PUT = run_wsgi_app 92 | do_DELETE = run_wsgi_app 93 | 94 | def do_GET(self): 95 | if self.path.startswith('/static/'): 96 | SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) 97 | else: 98 | self.run_wsgi_app() 99 | 100 | def wsgi_start_response(self, response_status, response_headers, 101 | exc_info=None): 102 | if (self.wsgi_sent_headers): 103 | raise Exception \ 104 | ("Headers already sent and start_response called again!") 105 | # Should really take a copy to avoid changes in the application.... 106 | self.wsgi_headers = (response_status, response_headers) 107 | return self.wsgi_write_data 108 | 109 | def wsgi_write_data(self, data): 110 | if (not self.wsgi_sent_headers): 111 | status, headers = self.wsgi_headers 112 | # Need to send header prior to data 113 | status_code = status[:status.find(' ')] 114 | status_msg = status[status.find(' ') + 1:] 115 | self.send_response(int(status_code), status_msg) 116 | for header, value in headers: 117 | self.send_header(header, value) 118 | self.end_headers() 119 | self.wsgi_sent_headers = 1 120 | # Send the data 121 | self.wfile.write(data) 122 | 123 | class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): 124 | def __init__(self, func, server_address): 125 | BaseHTTPServer.HTTPServer.__init__(self, 126 | server_address, 127 | WSGIHandler) 128 | self.app = func 129 | self.serverShuttingDown = 0 130 | 131 | print "http://%s:%d/" % server_address 132 | WSGIServer(func, server_address).serve_forever() 133 | 134 | # The WSGIServer instance. 135 | # Made global so that it can be stopped in embedded mode. 136 | server = None 137 | 138 | def runsimple(func, server_address=("0.0.0.0", 8080)): 139 | """ 140 | Runs [CherryPy][cp] WSGI server hosting WSGI app `func`. 141 | The directory `static/` is hosted statically. 142 | 143 | [cp]: http://www.cherrypy.org 144 | """ 145 | global server 146 | func = StaticMiddleware(func) 147 | func = LogMiddleware(func) 148 | 149 | server = WSGIServer(server_address, func) 150 | 151 | if server.ssl_adapter: 152 | print "https://%s:%d/" % server_address 153 | else: 154 | print "http://%s:%d/" % server_address 155 | 156 | try: 157 | server.start() 158 | except (KeyboardInterrupt, SystemExit): 159 | server.stop() 160 | server = None 161 | 162 | def WSGIServer(server_address, wsgi_app): 163 | """Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`. 164 | This function can be overwritten to customize the webserver or use a different webserver. 165 | """ 166 | import wsgiserver 167 | 168 | # Default values of wsgiserver.ssl_adapters uses cherrypy.wsgiserver 169 | # prefix. Overwriting it make it work with web.wsgiserver. 170 | wsgiserver.ssl_adapters = { 171 | 'builtin': 'web.wsgiserver.ssl_builtin.BuiltinSSLAdapter', 172 | 'pyopenssl': 'web.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter', 173 | } 174 | 175 | server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost") 176 | 177 | def create_ssl_adapter(cert, key): 178 | # wsgiserver tries to import submodules as cherrypy.wsgiserver.foo. 179 | # That doesn't work as not it is web.wsgiserver. 180 | # Patching sys.modules temporarily to make it work. 181 | import types 182 | cherrypy = types.ModuleType('cherrypy') 183 | cherrypy.wsgiserver = wsgiserver 184 | sys.modules['cherrypy'] = cherrypy 185 | sys.modules['cherrypy.wsgiserver'] = wsgiserver 186 | 187 | from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter 188 | adapter = pyOpenSSLAdapter(cert, key) 189 | 190 | # We are done with our work. Cleanup the patches. 191 | del sys.modules['cherrypy'] 192 | del sys.modules['cherrypy.wsgiserver'] 193 | 194 | return adapter 195 | 196 | # SSL backward compatibility 197 | if (server.ssl_adapter is None and 198 | getattr(server, 'ssl_certificate', None) and 199 | getattr(server, 'ssl_private_key', None)): 200 | server.ssl_adapter = create_ssl_adapter(server.ssl_certificate, server.ssl_private_key) 201 | 202 | server.nodelay = not sys.platform.startswith('java') # TCP_NODELAY isn't supported on the JVM 203 | return server 204 | 205 | class StaticApp(SimpleHTTPRequestHandler): 206 | """WSGI application for serving static files.""" 207 | def __init__(self, environ, start_response): 208 | self.headers = [] 209 | self.environ = environ 210 | self.start_response = start_response 211 | 212 | def send_response(self, status, msg=""): 213 | self.status = str(status) + " " + msg 214 | 215 | def send_header(self, name, value): 216 | self.headers.append((name, value)) 217 | 218 | def end_headers(self): 219 | pass 220 | 221 | def log_message(*a): pass 222 | 223 | def __iter__(self): 224 | environ = self.environ 225 | 226 | self.path = environ.get('PATH_INFO', '') 227 | self.client_address = environ.get('REMOTE_ADDR','-'), \ 228 | environ.get('REMOTE_PORT','-') 229 | self.command = environ.get('REQUEST_METHOD', '-') 230 | 231 | from cStringIO import StringIO 232 | self.wfile = StringIO() # for capturing error 233 | 234 | try: 235 | path = self.translate_path(self.path) 236 | etag = '"%s"' % os.path.getmtime(path) 237 | client_etag = environ.get('HTTP_IF_NONE_MATCH') 238 | self.send_header('ETag', etag) 239 | if etag == client_etag: 240 | self.send_response(304, "Not Modified") 241 | self.start_response(self.status, self.headers) 242 | raise StopIteration 243 | except OSError: 244 | pass # Probably a 404 245 | 246 | f = self.send_head() 247 | self.start_response(self.status, self.headers) 248 | 249 | if f: 250 | block_size = 16 * 1024 251 | while True: 252 | buf = f.read(block_size) 253 | if not buf: 254 | break 255 | yield buf 256 | f.close() 257 | else: 258 | value = self.wfile.getvalue() 259 | yield value 260 | 261 | class StaticMiddleware: 262 | """WSGI middleware for serving static files.""" 263 | def __init__(self, app, prefix='/static/'): 264 | self.app = app 265 | self.prefix = prefix 266 | 267 | def __call__(self, environ, start_response): 268 | path = environ.get('PATH_INFO', '') 269 | path = self.normpath(path) 270 | 271 | if path.startswith(self.prefix): 272 | return StaticApp(environ, start_response) 273 | else: 274 | return self.app(environ, start_response) 275 | 276 | def normpath(self, path): 277 | path2 = posixpath.normpath(urllib.unquote(path)) 278 | if path.endswith("/"): 279 | path2 += "/" 280 | return path2 281 | 282 | 283 | class LogMiddleware: 284 | """WSGI middleware for logging the status.""" 285 | def __init__(self, app): 286 | self.app = app 287 | self.format = '%s - - [%s] "%s %s %s" - %s' 288 | 289 | from BaseHTTPServer import BaseHTTPRequestHandler 290 | import StringIO 291 | f = StringIO.StringIO() 292 | 293 | class FakeSocket: 294 | def makefile(self, *a): 295 | return f 296 | 297 | # take log_date_time_string method from BaseHTTPRequestHandler 298 | self.log_date_time_string = BaseHTTPRequestHandler(FakeSocket(), None, None).log_date_time_string 299 | 300 | def __call__(self, environ, start_response): 301 | def xstart_response(status, response_headers, *args): 302 | out = start_response(status, response_headers, *args) 303 | self.log(status, environ) 304 | return out 305 | 306 | return self.app(environ, xstart_response) 307 | 308 | def log(self, status, environ): 309 | outfile = environ.get('wsgi.errors', web.debug) 310 | req = environ.get('PATH_INFO', '_') 311 | protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-') 312 | method = environ.get('REQUEST_METHOD', '-') 313 | host = "%s:%s" % (environ.get('REMOTE_ADDR','-'), 314 | environ.get('REMOTE_PORT','-')) 315 | 316 | time = self.log_date_time_string() 317 | 318 | msg = self.format % (host, time, protocol, method, req, status) 319 | print >> outfile, utils.safestr(msg) 320 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/net.py: -------------------------------------------------------------------------------- 1 | """ 2 | Network Utilities 3 | (from web.py) 4 | """ 5 | 6 | __all__ = [ 7 | "validipaddr", "validipport", "validip", "validaddr", 8 | "urlquote", 9 | "httpdate", "parsehttpdate", 10 | "htmlquote", "htmlunquote", "websafe", 11 | ] 12 | 13 | import urllib, time 14 | try: import datetime 15 | except ImportError: pass 16 | 17 | def validipaddr(address): 18 | """ 19 | Returns True if `address` is a valid IPv4 address. 20 | 21 | >>> validipaddr('192.168.1.1') 22 | True 23 | >>> validipaddr('192.168.1.800') 24 | False 25 | >>> validipaddr('192.168.1') 26 | False 27 | """ 28 | try: 29 | octets = address.split('.') 30 | if len(octets) != 4: 31 | return False 32 | for x in octets: 33 | if not (0 <= int(x) <= 255): 34 | return False 35 | except ValueError: 36 | return False 37 | return True 38 | 39 | def validipport(port): 40 | """ 41 | Returns True if `port` is a valid IPv4 port. 42 | 43 | >>> validipport('9000') 44 | True 45 | >>> validipport('foo') 46 | False 47 | >>> validipport('1000000') 48 | False 49 | """ 50 | try: 51 | if not (0 <= int(port) <= 65535): 52 | return False 53 | except ValueError: 54 | return False 55 | return True 56 | 57 | def validip(ip, defaultaddr="0.0.0.0", defaultport=8080): 58 | """Returns `(ip_address, port)` from string `ip_addr_port`""" 59 | addr = defaultaddr 60 | port = defaultport 61 | 62 | ip = ip.split(":", 1) 63 | if len(ip) == 1: 64 | if not ip[0]: 65 | pass 66 | elif validipaddr(ip[0]): 67 | addr = ip[0] 68 | elif validipport(ip[0]): 69 | port = int(ip[0]) 70 | else: 71 | raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' 72 | elif len(ip) == 2: 73 | addr, port = ip 74 | if not validipaddr(addr) and validipport(port): 75 | raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' 76 | port = int(port) 77 | else: 78 | raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' 79 | return (addr, port) 80 | 81 | def validaddr(string_): 82 | """ 83 | Returns either (ip_address, port) or "/path/to/socket" from string_ 84 | 85 | >>> validaddr('/path/to/socket') 86 | '/path/to/socket' 87 | >>> validaddr('8000') 88 | ('0.0.0.0', 8000) 89 | >>> validaddr('127.0.0.1') 90 | ('127.0.0.1', 8080) 91 | >>> validaddr('127.0.0.1:8000') 92 | ('127.0.0.1', 8000) 93 | >>> validaddr('fff') 94 | Traceback (most recent call last): 95 | ... 96 | ValueError: fff is not a valid IP address/port 97 | """ 98 | if '/' in string_: 99 | return string_ 100 | else: 101 | return validip(string_) 102 | 103 | def urlquote(val): 104 | """ 105 | Quotes a string for use in a URL. 106 | 107 | >>> urlquote('://?f=1&j=1') 108 | '%3A//%3Ff%3D1%26j%3D1' 109 | >>> urlquote(None) 110 | '' 111 | >>> urlquote(u'\u203d') 112 | '%E2%80%BD' 113 | """ 114 | if val is None: return '' 115 | if not isinstance(val, unicode): val = str(val) 116 | else: val = val.encode('utf-8') 117 | return urllib.quote(val) 118 | 119 | def httpdate(date_obj): 120 | """ 121 | Formats a datetime object for use in HTTP headers. 122 | 123 | >>> import datetime 124 | >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1)) 125 | 'Thu, 01 Jan 1970 01:01:01 GMT' 126 | """ 127 | return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT") 128 | 129 | def parsehttpdate(string_): 130 | """ 131 | Parses an HTTP date into a datetime object. 132 | 133 | >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT') 134 | datetime.datetime(1970, 1, 1, 1, 1, 1) 135 | """ 136 | try: 137 | t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z") 138 | except ValueError: 139 | return None 140 | return datetime.datetime(*t[:6]) 141 | 142 | def htmlquote(text): 143 | r""" 144 | Encodes `text` for raw use in HTML. 145 | 146 | >>> htmlquote(u"<'&\">") 147 | u'<'&">' 148 | """ 149 | text = text.replace(u"&", u"&") # Must be done first! 150 | text = text.replace(u"<", u"<") 151 | text = text.replace(u">", u">") 152 | text = text.replace(u"'", u"'") 153 | text = text.replace(u'"', u""") 154 | return text 155 | 156 | def htmlunquote(text): 157 | r""" 158 | Decodes `text` that's HTML quoted. 159 | 160 | >>> htmlunquote(u'<'&">') 161 | u'<\'&">' 162 | """ 163 | text = text.replace(u""", u'"') 164 | text = text.replace(u"'", u"'") 165 | text = text.replace(u">", u">") 166 | text = text.replace(u"<", u"<") 167 | text = text.replace(u"&", u"&") # Must be done last! 168 | return text 169 | 170 | def websafe(val): 171 | r"""Converts `val` so that it is safe for use in Unicode HTML. 172 | 173 | >>> websafe("<'&\">") 174 | u'<'&">' 175 | >>> websafe(None) 176 | u'' 177 | >>> websafe(u'\u203d') 178 | u'\u203d' 179 | >>> websafe('\xe2\x80\xbd') 180 | u'\u203d' 181 | """ 182 | if val is None: 183 | return u'' 184 | elif isinstance(val, str): 185 | val = val.decode('utf-8') 186 | elif not isinstance(val, unicode): 187 | val = unicode(val) 188 | 189 | return htmlquote(val) 190 | 191 | if __name__ == "__main__": 192 | import doctest 193 | doctest.testmod() 194 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/python23.py: -------------------------------------------------------------------------------- 1 | """Python 2.3 compatabilty""" 2 | import threading 3 | 4 | class threadlocal(object): 5 | """Implementation of threading.local for python2.3. 6 | """ 7 | def __getattribute__(self, name): 8 | if name == "__dict__": 9 | return threadlocal._getd(self) 10 | else: 11 | try: 12 | return object.__getattribute__(self, name) 13 | except AttributeError: 14 | try: 15 | return self.__dict__[name] 16 | except KeyError: 17 | raise AttributeError, name 18 | 19 | def __setattr__(self, name, value): 20 | self.__dict__[name] = value 21 | 22 | def __delattr__(self, name): 23 | try: 24 | del self.__dict__[name] 25 | except KeyError: 26 | raise AttributeError, name 27 | 28 | def _getd(self): 29 | t = threading.currentThread() 30 | if not hasattr(t, '_d'): 31 | # using __dict__ of thread as thread local storage 32 | t._d = {} 33 | 34 | _id = id(self) 35 | # there could be multiple instances of threadlocal. 36 | # use id(self) as key 37 | if _id not in t._d: 38 | t._d[_id] = {} 39 | return t._d[_id] 40 | 41 | if __name__ == '__main__': 42 | d = threadlocal() 43 | d.x = 1 44 | print d.__dict__ 45 | print d.x 46 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/session.py: -------------------------------------------------------------------------------- 1 | """ 2 | Session Management 3 | (from web.py) 4 | """ 5 | 6 | import os, time, datetime, random, base64 7 | import os.path 8 | from copy import deepcopy 9 | try: 10 | import cPickle as pickle 11 | except ImportError: 12 | import pickle 13 | try: 14 | import hashlib 15 | sha1 = hashlib.sha1 16 | except ImportError: 17 | import sha 18 | sha1 = sha.new 19 | 20 | import utils 21 | import webapi as web 22 | 23 | __all__ = [ 24 | 'Session', 'SessionExpired', 25 | 'Store', 'DiskStore', 'DBStore', 26 | ] 27 | 28 | web.config.session_parameters = utils.storage({ 29 | 'cookie_name': 'webpy_session_id', 30 | 'cookie_domain': None, 31 | 'cookie_path' : None, 32 | 'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds 33 | 'ignore_expiry': True, 34 | 'ignore_change_ip': True, 35 | 'secret_key': 'fLjUfxqXtfNoIldA0A0J', 36 | 'expired_message': 'Session expired', 37 | 'httponly': True, 38 | 'secure': False 39 | }) 40 | 41 | class SessionExpired(web.HTTPError): 42 | def __init__(self, message): 43 | web.HTTPError.__init__(self, '200 OK', {}, data=message) 44 | 45 | class Session(object): 46 | """Session management for web.py 47 | """ 48 | __slots__ = [ 49 | "store", "_initializer", "_last_cleanup_time", "_config", "_data", 50 | "__getitem__", "__setitem__", "__delitem__" 51 | ] 52 | 53 | def __init__(self, app, store, initializer=None): 54 | self.store = store 55 | self._initializer = initializer 56 | self._last_cleanup_time = 0 57 | self._config = utils.storage(web.config.session_parameters) 58 | self._data = utils.threadeddict() 59 | 60 | self.__getitem__ = self._data.__getitem__ 61 | self.__setitem__ = self._data.__setitem__ 62 | self.__delitem__ = self._data.__delitem__ 63 | 64 | if app: 65 | app.add_processor(self._processor) 66 | 67 | def __contains__(self, name): 68 | return name in self._data 69 | 70 | def __getattr__(self, name): 71 | return getattr(self._data, name) 72 | 73 | def __setattr__(self, name, value): 74 | if name in self.__slots__: 75 | object.__setattr__(self, name, value) 76 | else: 77 | setattr(self._data, name, value) 78 | 79 | def __delattr__(self, name): 80 | delattr(self._data, name) 81 | 82 | def _processor(self, handler): 83 | """Application processor to setup session for every request""" 84 | self._cleanup() 85 | self._load() 86 | 87 | try: 88 | return handler() 89 | finally: 90 | self._save() 91 | 92 | def _load(self): 93 | """Load the session from the store, by the id from cookie""" 94 | cookie_name = self._config.cookie_name 95 | cookie_domain = self._config.cookie_domain 96 | cookie_path = self._config.cookie_path 97 | httponly = self._config.httponly 98 | self.session_id = web.cookies().get(cookie_name) 99 | 100 | # protection against session_id tampering 101 | if self.session_id and not self._valid_session_id(self.session_id): 102 | self.session_id = None 103 | 104 | self._check_expiry() 105 | if self.session_id: 106 | d = self.store[self.session_id] 107 | self.update(d) 108 | self._validate_ip() 109 | 110 | if not self.session_id: 111 | self.session_id = self._generate_session_id() 112 | 113 | if self._initializer: 114 | if isinstance(self._initializer, dict): 115 | self.update(deepcopy(self._initializer)) 116 | elif hasattr(self._initializer, '__call__'): 117 | self._initializer() 118 | 119 | self.ip = web.ctx.ip 120 | 121 | def _check_expiry(self): 122 | # check for expiry 123 | if self.session_id and self.session_id not in self.store: 124 | if self._config.ignore_expiry: 125 | self.session_id = None 126 | else: 127 | return self.expired() 128 | 129 | def _validate_ip(self): 130 | # check for change of IP 131 | if self.session_id and self.get('ip', None) != web.ctx.ip: 132 | if not self._config.ignore_change_ip: 133 | return self.expired() 134 | 135 | def _save(self): 136 | if not self.get('_killed'): 137 | self._setcookie(self.session_id) 138 | self.store[self.session_id] = dict(self._data) 139 | else: 140 | self._setcookie(self.session_id, expires=-1) 141 | 142 | def _setcookie(self, session_id, expires='', **kw): 143 | cookie_name = self._config.cookie_name 144 | cookie_domain = self._config.cookie_domain 145 | cookie_path = self._config.cookie_path 146 | httponly = self._config.httponly 147 | secure = self._config.secure 148 | web.setcookie(cookie_name, session_id, expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, path=cookie_path) 149 | 150 | def _generate_session_id(self): 151 | """Generate a random id for session""" 152 | 153 | while True: 154 | rand = os.urandom(16) 155 | now = time.time() 156 | secret_key = self._config.secret_key 157 | session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key)) 158 | session_id = session_id.hexdigest() 159 | if session_id not in self.store: 160 | break 161 | return session_id 162 | 163 | def _valid_session_id(self, session_id): 164 | rx = utils.re_compile('^[0-9a-fA-F]+$') 165 | return rx.match(session_id) 166 | 167 | def _cleanup(self): 168 | """Cleanup the stored sessions""" 169 | current_time = time.time() 170 | timeout = self._config.timeout 171 | if current_time - self._last_cleanup_time > timeout: 172 | self.store.cleanup(timeout) 173 | self._last_cleanup_time = current_time 174 | 175 | def expired(self): 176 | """Called when an expired session is atime""" 177 | self._killed = True 178 | self._save() 179 | raise SessionExpired(self._config.expired_message) 180 | 181 | def kill(self): 182 | """Kill the session, make it no longer available""" 183 | del self.store[self.session_id] 184 | self._killed = True 185 | 186 | class Store: 187 | """Base class for session stores""" 188 | 189 | def __contains__(self, key): 190 | raise NotImplementedError 191 | 192 | def __getitem__(self, key): 193 | raise NotImplementedError 194 | 195 | def __setitem__(self, key, value): 196 | raise NotImplementedError 197 | 198 | def cleanup(self, timeout): 199 | """removes all the expired sessions""" 200 | raise NotImplementedError 201 | 202 | def encode(self, session_dict): 203 | """encodes session dict as a string""" 204 | pickled = pickle.dumps(session_dict) 205 | return base64.encodestring(pickled) 206 | 207 | def decode(self, session_data): 208 | """decodes the data to get back the session dict """ 209 | pickled = base64.decodestring(session_data) 210 | return pickle.loads(pickled) 211 | 212 | class DiskStore(Store): 213 | """ 214 | Store for saving a session on disk. 215 | 216 | >>> import tempfile 217 | >>> root = tempfile.mkdtemp() 218 | >>> s = DiskStore(root) 219 | >>> s['a'] = 'foo' 220 | >>> s['a'] 221 | 'foo' 222 | >>> time.sleep(0.01) 223 | >>> s.cleanup(0.01) 224 | >>> s['a'] 225 | Traceback (most recent call last): 226 | ... 227 | KeyError: 'a' 228 | """ 229 | def __init__(self, root): 230 | # if the storage root doesn't exists, create it. 231 | if not os.path.exists(root): 232 | os.makedirs( 233 | os.path.abspath(root) 234 | ) 235 | self.root = root 236 | 237 | def _get_path(self, key): 238 | if os.path.sep in key: 239 | raise ValueError, "Bad key: %s" % repr(key) 240 | return os.path.join(self.root, key) 241 | 242 | def __contains__(self, key): 243 | path = self._get_path(key) 244 | return os.path.exists(path) 245 | 246 | def __getitem__(self, key): 247 | path = self._get_path(key) 248 | if os.path.exists(path): 249 | pickled = open(path).read() 250 | return self.decode(pickled) 251 | else: 252 | raise KeyError, key 253 | 254 | def __setitem__(self, key, value): 255 | path = self._get_path(key) 256 | pickled = self.encode(value) 257 | try: 258 | f = open(path, 'w') 259 | try: 260 | f.write(pickled) 261 | finally: 262 | f.close() 263 | except IOError: 264 | pass 265 | 266 | def __delitem__(self, key): 267 | path = self._get_path(key) 268 | if os.path.exists(path): 269 | os.remove(path) 270 | 271 | def cleanup(self, timeout): 272 | now = time.time() 273 | for f in os.listdir(self.root): 274 | path = self._get_path(f) 275 | atime = os.stat(path).st_atime 276 | if now - atime > timeout : 277 | os.remove(path) 278 | 279 | class DBStore(Store): 280 | """Store for saving a session in database 281 | Needs a table with the following columns: 282 | 283 | session_id CHAR(128) UNIQUE NOT NULL, 284 | atime DATETIME NOT NULL default current_timestamp, 285 | data TEXT 286 | """ 287 | def __init__(self, db, table_name): 288 | self.db = db 289 | self.table = table_name 290 | 291 | def __contains__(self, key): 292 | data = self.db.select(self.table, where="session_id=$key", vars=locals()) 293 | return bool(list(data)) 294 | 295 | def __getitem__(self, key): 296 | now = datetime.datetime.now() 297 | try: 298 | s = self.db.select(self.table, where="session_id=$key", vars=locals())[0] 299 | self.db.update(self.table, where="session_id=$key", atime=now, vars=locals()) 300 | except IndexError: 301 | raise KeyError 302 | else: 303 | return self.decode(s.data) 304 | 305 | def __setitem__(self, key, value): 306 | pickled = self.encode(value) 307 | now = datetime.datetime.now() 308 | if key in self: 309 | self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals()) 310 | else: 311 | self.db.insert(self.table, False, session_id=key, data=pickled ) 312 | 313 | def __delitem__(self, key): 314 | self.db.delete(self.table, where="session_id=$key", vars=locals()) 315 | 316 | def cleanup(self, timeout): 317 | timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg 318 | last_allowed_time = datetime.datetime.now() - timeout 319 | self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals()) 320 | 321 | class ShelfStore: 322 | """Store for saving session using `shelve` module. 323 | 324 | import shelve 325 | store = ShelfStore(shelve.open('session.shelf')) 326 | 327 | XXX: is shelve thread-safe? 328 | """ 329 | def __init__(self, shelf): 330 | self.shelf = shelf 331 | 332 | def __contains__(self, key): 333 | return key in self.shelf 334 | 335 | def __getitem__(self, key): 336 | atime, v = self.shelf[key] 337 | self[key] = v # update atime 338 | return v 339 | 340 | def __setitem__(self, key, value): 341 | self.shelf[key] = time.time(), value 342 | 343 | def __delitem__(self, key): 344 | try: 345 | del self.shelf[key] 346 | except KeyError: 347 | pass 348 | 349 | def cleanup(self, timeout): 350 | now = time.time() 351 | for k in self.shelf.keys(): 352 | atime, v = self.shelf[k] 353 | if now - atime > timeout : 354 | del self[k] 355 | 356 | if __name__ == '__main__' : 357 | import doctest 358 | doctest.testmod() 359 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/test.py: -------------------------------------------------------------------------------- 1 | """test utilities 2 | (part of web.py) 3 | """ 4 | import unittest 5 | import sys, os 6 | import web 7 | 8 | TestCase = unittest.TestCase 9 | TestSuite = unittest.TestSuite 10 | 11 | def load_modules(names): 12 | return [__import__(name, None, None, "x") for name in names] 13 | 14 | def module_suite(module, classnames=None): 15 | """Makes a suite from a module.""" 16 | if classnames: 17 | return unittest.TestLoader().loadTestsFromNames(classnames, module) 18 | elif hasattr(module, 'suite'): 19 | return module.suite() 20 | else: 21 | return unittest.TestLoader().loadTestsFromModule(module) 22 | 23 | def doctest_suite(module_names): 24 | """Makes a test suite from doctests.""" 25 | import doctest 26 | suite = TestSuite() 27 | for mod in load_modules(module_names): 28 | suite.addTest(doctest.DocTestSuite(mod)) 29 | return suite 30 | 31 | def suite(module_names): 32 | """Creates a suite from multiple modules.""" 33 | suite = TestSuite() 34 | for mod in load_modules(module_names): 35 | suite.addTest(module_suite(mod)) 36 | return suite 37 | 38 | def runTests(suite): 39 | runner = unittest.TextTestRunner() 40 | return runner.run(suite) 41 | 42 | def main(suite=None): 43 | if not suite: 44 | main_module = __import__('__main__') 45 | # allow command line switches 46 | args = [a for a in sys.argv[1:] if not a.startswith('-')] 47 | suite = module_suite(main_module, args or None) 48 | 49 | result = runTests(suite) 50 | sys.exit(not result.wasSuccessful()) 51 | 52 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/webopenid.py: -------------------------------------------------------------------------------- 1 | """openid.py: an openid library for web.py 2 | 3 | Notes: 4 | 5 | - This will create a file called .openid_secret_key in the 6 | current directory with your secret key in it. If someone 7 | has access to this file they can log in as any user. And 8 | if the app can't find this file for any reason (e.g. you 9 | moved the app somewhere else) then each currently logged 10 | in user will get logged out. 11 | 12 | - State must be maintained through the entire auth process 13 | -- this means that if you have multiple web.py processes 14 | serving one set of URLs or if you restart your app often 15 | then log ins will fail. You have to replace sessions and 16 | store for things to work. 17 | 18 | - We set cookies starting with "openid_". 19 | 20 | """ 21 | 22 | import os 23 | import random 24 | import hmac 25 | import __init__ as web 26 | import openid.consumer.consumer 27 | import openid.store.memstore 28 | 29 | sessions = {} 30 | store = openid.store.memstore.MemoryStore() 31 | 32 | def _secret(): 33 | try: 34 | secret = file('.openid_secret_key').read() 35 | except IOError: 36 | # file doesn't exist 37 | secret = os.urandom(20) 38 | file('.openid_secret_key', 'w').write(secret) 39 | return secret 40 | 41 | def _hmac(identity_url): 42 | return hmac.new(_secret(), identity_url).hexdigest() 43 | 44 | def _random_session(): 45 | n = random.random() 46 | while n in sessions: 47 | n = random.random() 48 | n = str(n) 49 | return n 50 | 51 | def status(): 52 | oid_hash = web.cookies().get('openid_identity_hash', '').split(',', 1) 53 | if len(oid_hash) > 1: 54 | oid_hash, identity_url = oid_hash 55 | if oid_hash == _hmac(identity_url): 56 | return identity_url 57 | return None 58 | 59 | def form(openid_loc): 60 | oid = status() 61 | if oid: 62 | return ''' 63 |
64 | OpenID 65 | %s 66 | 67 | 68 | 69 |
''' % (openid_loc, oid, web.ctx.fullpath) 70 | else: 71 | return ''' 72 |
73 | 75 | 76 | 77 |
''' % (openid_loc, web.ctx.fullpath) 78 | 79 | def logout(): 80 | web.setcookie('openid_identity_hash', '', expires=-1) 81 | 82 | class host: 83 | def POST(self): 84 | # unlike the usual scheme of things, the POST is actually called 85 | # first here 86 | i = web.input(return_to='/') 87 | if i.get('action') == 'logout': 88 | logout() 89 | return web.redirect(i.return_to) 90 | 91 | i = web.input('openid', return_to='/') 92 | 93 | n = _random_session() 94 | sessions[n] = {'webpy_return_to': i.return_to} 95 | 96 | c = openid.consumer.consumer.Consumer(sessions[n], store) 97 | a = c.begin(i.openid) 98 | f = a.redirectURL(web.ctx.home, web.ctx.home + web.ctx.fullpath) 99 | 100 | web.setcookie('openid_session_id', n) 101 | return web.redirect(f) 102 | 103 | def GET(self): 104 | n = web.cookies('openid_session_id').openid_session_id 105 | web.setcookie('openid_session_id', '', expires=-1) 106 | return_to = sessions[n]['webpy_return_to'] 107 | 108 | c = openid.consumer.consumer.Consumer(sessions[n], store) 109 | a = c.complete(web.input(), web.ctx.home + web.ctx.fullpath) 110 | 111 | if a.status.lower() == 'success': 112 | web.setcookie('openid_identity_hash', _hmac(a.identity_url) + ',' + a.identity_url) 113 | 114 | del sessions[n] 115 | return web.redirect(return_to) 116 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI Utilities 3 | (from web.py) 4 | """ 5 | 6 | import os, sys 7 | 8 | import http 9 | import webapi as web 10 | from utils import listget 11 | from net import validaddr, validip 12 | import httpserver 13 | 14 | def runfcgi(func, addr=('localhost', 8000)): 15 | """Runs a WSGI function as a FastCGI server.""" 16 | import flup.server.fcgi as flups 17 | return flups.WSGIServer(func, multiplexed=True, bindAddress=addr, debug=False).run() 18 | 19 | def runscgi(func, addr=('localhost', 4000)): 20 | """Runs a WSGI function as an SCGI server.""" 21 | import flup.server.scgi as flups 22 | return flups.WSGIServer(func, bindAddress=addr, debug=False).run() 23 | 24 | def runwsgi(func): 25 | """ 26 | Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server, 27 | as appropriate based on context and `sys.argv`. 28 | """ 29 | 30 | if os.environ.has_key('SERVER_SOFTWARE'): # cgi 31 | os.environ['FCGI_FORCE_CGI'] = 'Y' 32 | 33 | if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi 34 | or os.environ.has_key('SERVER_SOFTWARE')): 35 | return runfcgi(func, None) 36 | 37 | if 'fcgi' in sys.argv or 'fastcgi' in sys.argv: 38 | args = sys.argv[1:] 39 | if 'fastcgi' in args: args.remove('fastcgi') 40 | elif 'fcgi' in args: args.remove('fcgi') 41 | if args: 42 | return runfcgi(func, validaddr(args[0])) 43 | else: 44 | return runfcgi(func, None) 45 | 46 | if 'scgi' in sys.argv: 47 | args = sys.argv[1:] 48 | args.remove('scgi') 49 | if args: 50 | return runscgi(func, validaddr(args[0])) 51 | else: 52 | return runscgi(func) 53 | 54 | return httpserver.runsimple(func, validip(listget(sys.argv, 1, ''))) 55 | 56 | def _is_dev_mode(): 57 | # Some embedded python interpreters won't have sys.arv 58 | # For details, see https://github.com/webpy/webpy/issues/87 59 | argv = getattr(sys, "argv", []) 60 | 61 | # quick hack to check if the program is running in dev mode. 62 | if os.environ.has_key('SERVER_SOFTWARE') \ 63 | or os.environ.has_key('PHP_FCGI_CHILDREN') \ 64 | or 'fcgi' in argv or 'fastcgi' in argv \ 65 | or 'mod_wsgi' in argv: 66 | return False 67 | return True 68 | 69 | # When running the builtin-server, enable debug mode if not already set. 70 | web.config.setdefault('debug', _is_dev_mode()) 71 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/wsgiserver/ssl_builtin.py: -------------------------------------------------------------------------------- 1 | """A library for integrating Python's builtin ``ssl`` library with CherryPy. 2 | 3 | The ssl module must be importable for SSL functionality. 4 | 5 | To use this module, set ``CherryPyWSGIServer.ssl_adapter`` to an instance of 6 | ``BuiltinSSLAdapter``. 7 | """ 8 | 9 | try: 10 | import ssl 11 | except ImportError: 12 | ssl = None 13 | 14 | from cherrypy import wsgiserver 15 | 16 | 17 | class BuiltinSSLAdapter(wsgiserver.SSLAdapter): 18 | """A wrapper for integrating Python's builtin ssl module with CherryPy.""" 19 | 20 | certificate = None 21 | """The filename of the server SSL certificate.""" 22 | 23 | private_key = None 24 | """The filename of the server's private key file.""" 25 | 26 | def __init__(self, certificate, private_key, certificate_chain=None): 27 | if ssl is None: 28 | raise ImportError("You must install the ssl module to use HTTPS.") 29 | self.certificate = certificate 30 | self.private_key = private_key 31 | self.certificate_chain = certificate_chain 32 | 33 | def bind(self, sock): 34 | """Wrap and return the given socket.""" 35 | return sock 36 | 37 | def wrap(self, sock): 38 | """Wrap and return the given socket, plus WSGI environ entries.""" 39 | try: 40 | s = ssl.wrap_socket(sock, do_handshake_on_connect=True, 41 | server_side=True, certfile=self.certificate, 42 | keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23) 43 | except ssl.SSLError, e: 44 | if e.errno == ssl.SSL_ERROR_EOF: 45 | # This is almost certainly due to the cherrypy engine 46 | # 'pinging' the socket to assert it's connectable; 47 | # the 'ping' isn't SSL. 48 | return None, {} 49 | elif e.errno == ssl.SSL_ERROR_SSL: 50 | if e.args[1].endswith('http request'): 51 | # The client is speaking HTTP to an HTTPS server. 52 | raise wsgiserver.NoSSLError 53 | raise 54 | return s, self.get_environ(s) 55 | 56 | # TODO: fill this out more with mod ssl env 57 | def get_environ(self, sock): 58 | """Create WSGI environ entries to be merged into each request.""" 59 | cipher = sock.cipher() 60 | ssl_environ = { 61 | "wsgi.url_scheme": "https", 62 | "HTTPS": "on", 63 | 'SSL_PROTOCOL': cipher[1], 64 | 'SSL_CIPHER': cipher[0] 65 | ## SSL_VERSION_INTERFACE string The mod_ssl program version 66 | ## SSL_VERSION_LIBRARY string The OpenSSL program version 67 | } 68 | return ssl_environ 69 | 70 | def makefile(self, sock, mode='r', bufsize=-1): 71 | return wsgiserver.CP_fileobject(sock, mode, bufsize) 72 | 73 | -------------------------------------------------------------------------------- /web.py-0.37/build/lib.linux-x86_64-2.7/web/wsgiserver/ssl_pyopenssl.py: -------------------------------------------------------------------------------- 1 | """A library for integrating pyOpenSSL with CherryPy. 2 | 3 | The OpenSSL module must be importable for SSL functionality. 4 | You can obtain it from http://pyopenssl.sourceforge.net/ 5 | 6 | To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of 7 | SSLAdapter. There are two ways to use SSL: 8 | 9 | Method One 10 | ---------- 11 | 12 | * ``ssl_adapter.context``: an instance of SSL.Context. 13 | 14 | If this is not None, it is assumed to be an SSL.Context instance, 15 | and will be passed to SSL.Connection on bind(). The developer is 16 | responsible for forming a valid Context object. This approach is 17 | to be preferred for more flexibility, e.g. if the cert and key are 18 | streams instead of files, or need decryption, or SSL.SSLv3_METHOD 19 | is desired instead of the default SSL.SSLv23_METHOD, etc. Consult 20 | the pyOpenSSL documentation for complete options. 21 | 22 | Method Two (shortcut) 23 | --------------------- 24 | 25 | * ``ssl_adapter.certificate``: the filename of the server SSL certificate. 26 | * ``ssl_adapter.private_key``: the filename of the server's private key file. 27 | 28 | Both are None by default. If ssl_adapter.context is None, but .private_key 29 | and .certificate are both given and valid, they will be read, and the 30 | context will be automatically created from them. 31 | """ 32 | 33 | import socket 34 | import threading 35 | import time 36 | 37 | from cherrypy import wsgiserver 38 | 39 | try: 40 | from OpenSSL import SSL 41 | from OpenSSL import crypto 42 | except ImportError: 43 | SSL = None 44 | 45 | 46 | class SSL_fileobject(wsgiserver.CP_fileobject): 47 | """SSL file object attached to a socket object.""" 48 | 49 | ssl_timeout = 3 50 | ssl_retry = .01 51 | 52 | def _safe_call(self, is_reader, call, *args, **kwargs): 53 | """Wrap the given call with SSL error-trapping. 54 | 55 | is_reader: if False EOF errors will be raised. If True, EOF errors 56 | will return "" (to emulate normal sockets). 57 | """ 58 | start = time.time() 59 | while True: 60 | try: 61 | return call(*args, **kwargs) 62 | except SSL.WantReadError: 63 | # Sleep and try again. This is dangerous, because it means 64 | # the rest of the stack has no way of differentiating 65 | # between a "new handshake" error and "client dropped". 66 | # Note this isn't an endless loop: there's a timeout below. 67 | time.sleep(self.ssl_retry) 68 | except SSL.WantWriteError: 69 | time.sleep(self.ssl_retry) 70 | except SSL.SysCallError, e: 71 | if is_reader and e.args == (-1, 'Unexpected EOF'): 72 | return "" 73 | 74 | errnum = e.args[0] 75 | if is_reader and errnum in wsgiserver.socket_errors_to_ignore: 76 | return "" 77 | raise socket.error(errnum) 78 | except SSL.Error, e: 79 | if is_reader and e.args == (-1, 'Unexpected EOF'): 80 | return "" 81 | 82 | thirdarg = None 83 | try: 84 | thirdarg = e.args[0][0][2] 85 | except IndexError: 86 | pass 87 | 88 | if thirdarg == 'http request': 89 | # The client is talking HTTP to an HTTPS server. 90 | raise wsgiserver.NoSSLError() 91 | 92 | raise wsgiserver.FatalSSLAlert(*e.args) 93 | except: 94 | raise 95 | 96 | if time.time() - start > self.ssl_timeout: 97 | raise socket.timeout("timed out") 98 | 99 | def recv(self, *args, **kwargs): 100 | buf = [] 101 | r = super(SSL_fileobject, self).recv 102 | while True: 103 | data = self._safe_call(True, r, *args, **kwargs) 104 | buf.append(data) 105 | p = self._sock.pending() 106 | if not p: 107 | return "".join(buf) 108 | 109 | def sendall(self, *args, **kwargs): 110 | return self._safe_call(False, super(SSL_fileobject, self).sendall, 111 | *args, **kwargs) 112 | 113 | def send(self, *args, **kwargs): 114 | return self._safe_call(False, super(SSL_fileobject, self).send, 115 | *args, **kwargs) 116 | 117 | 118 | class SSLConnection: 119 | """A thread-safe wrapper for an SSL.Connection. 120 | 121 | ``*args``: the arguments to create the wrapped ``SSL.Connection(*args)``. 122 | """ 123 | 124 | def __init__(self, *args): 125 | self._ssl_conn = SSL.Connection(*args) 126 | self._lock = threading.RLock() 127 | 128 | for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read', 129 | 'renegotiate', 'bind', 'listen', 'connect', 'accept', 130 | 'setblocking', 'fileno', 'close', 'get_cipher_list', 131 | 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 132 | 'makefile', 'get_app_data', 'set_app_data', 'state_string', 133 | 'sock_shutdown', 'get_peer_certificate', 'want_read', 134 | 'want_write', 'set_connect_state', 'set_accept_state', 135 | 'connect_ex', 'sendall', 'settimeout', 'gettimeout'): 136 | exec("""def %s(self, *args): 137 | self._lock.acquire() 138 | try: 139 | return self._ssl_conn.%s(*args) 140 | finally: 141 | self._lock.release() 142 | """ % (f, f)) 143 | 144 | def shutdown(self, *args): 145 | self._lock.acquire() 146 | try: 147 | # pyOpenSSL.socket.shutdown takes no args 148 | return self._ssl_conn.shutdown() 149 | finally: 150 | self._lock.release() 151 | 152 | 153 | class pyOpenSSLAdapter(wsgiserver.SSLAdapter): 154 | """A wrapper for integrating pyOpenSSL with CherryPy.""" 155 | 156 | context = None 157 | """An instance of SSL.Context.""" 158 | 159 | certificate = None 160 | """The filename of the server SSL certificate.""" 161 | 162 | private_key = None 163 | """The filename of the server's private key file.""" 164 | 165 | certificate_chain = None 166 | """Optional. The filename of CA's intermediate certificate bundle. 167 | 168 | This is needed for cheaper "chained root" SSL certificates, and should be 169 | left as None if not required.""" 170 | 171 | def __init__(self, certificate, private_key, certificate_chain=None): 172 | if SSL is None: 173 | raise ImportError("You must install pyOpenSSL to use HTTPS.") 174 | 175 | self.context = None 176 | self.certificate = certificate 177 | self.private_key = private_key 178 | self.certificate_chain = certificate_chain 179 | self._environ = None 180 | 181 | def bind(self, sock): 182 | """Wrap and return the given socket.""" 183 | if self.context is None: 184 | self.context = self.get_context() 185 | conn = SSLConnection(self.context, sock) 186 | self._environ = self.get_environ() 187 | return conn 188 | 189 | def wrap(self, sock): 190 | """Wrap and return the given socket, plus WSGI environ entries.""" 191 | return sock, self._environ.copy() 192 | 193 | def get_context(self): 194 | """Return an SSL.Context from self attributes.""" 195 | # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473 196 | c = SSL.Context(SSL.SSLv23_METHOD) 197 | c.use_privatekey_file(self.private_key) 198 | if self.certificate_chain: 199 | c.load_verify_locations(self.certificate_chain) 200 | c.use_certificate_file(self.certificate) 201 | return c 202 | 203 | def get_environ(self): 204 | """Return WSGI environ entries to be merged into each request.""" 205 | ssl_environ = { 206 | "HTTPS": "on", 207 | # pyOpenSSL doesn't provide access to any of these AFAICT 208 | ## 'SSL_PROTOCOL': 'SSLv2', 209 | ## SSL_CIPHER string The cipher specification name 210 | ## SSL_VERSION_INTERFACE string The mod_ssl program version 211 | ## SSL_VERSION_LIBRARY string The OpenSSL program version 212 | } 213 | 214 | if self.certificate: 215 | # Server certificate attributes 216 | cert = open(self.certificate, 'rb').read() 217 | cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) 218 | ssl_environ.update({ 219 | 'SSL_SERVER_M_VERSION': cert.get_version(), 220 | 'SSL_SERVER_M_SERIAL': cert.get_serial_number(), 221 | ## 'SSL_SERVER_V_START': Validity of server's certificate (start time), 222 | ## 'SSL_SERVER_V_END': Validity of server's certificate (end time), 223 | }) 224 | 225 | for prefix, dn in [("I", cert.get_issuer()), 226 | ("S", cert.get_subject())]: 227 | # X509Name objects don't seem to have a way to get the 228 | # complete DN string. Use str() and slice it instead, 229 | # because str(dn) == "" 230 | dnstr = str(dn)[18:-2] 231 | 232 | wsgikey = 'SSL_SERVER_%s_DN' % prefix 233 | ssl_environ[wsgikey] = dnstr 234 | 235 | # The DN should be of the form: /k1=v1/k2=v2, but we must allow 236 | # for any value to contain slashes itself (in a URL). 237 | while dnstr: 238 | pos = dnstr.rfind("=") 239 | dnstr, value = dnstr[:pos], dnstr[pos + 1:] 240 | pos = dnstr.rfind("/") 241 | dnstr, key = dnstr[:pos], dnstr[pos + 1:] 242 | if key and value: 243 | wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) 244 | ssl_environ[wsgikey] = value 245 | 246 | return ssl_environ 247 | 248 | def makefile(self, sock, mode='r', bufsize=-1): 249 | if SSL and isinstance(sock, SSL.ConnectionType): 250 | timeout = sock.gettimeout() 251 | f = SSL_fileobject(sock, mode, bufsize) 252 | f.ssl_timeout = timeout 253 | return f 254 | else: 255 | return wsgiserver.CP_fileobject(sock, mode, bufsize) 256 | 257 | -------------------------------------------------------------------------------- /web.py-0.37/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # ... 4 | 5 | from distutils.core import setup 6 | from web import __version__ 7 | 8 | setup(name='web.py', 9 | version=__version__, 10 | description='web.py: makes web apps', 11 | author='Aaron Swartz', 12 | author_email='me@aaronsw.com', 13 | maintainer='Anand Chitipothu', 14 | maintainer_email='anandology@gmail.com', 15 | url=' http://webpy.org/', 16 | packages=['web', 'web.wsgiserver', 'web.contrib'], 17 | long_description="Think about the ideal way to write a web app. Write the code to make it happen.", 18 | license="Public domain", 19 | platforms=["any"], 20 | ) 21 | -------------------------------------------------------------------------------- /web.py-0.37/web/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """web.py: makes web apps (http://webpy.org)""" 3 | 4 | from __future__ import generators 5 | 6 | __version__ = "0.37" 7 | __author__ = [ 8 | "Aaron Swartz ", 9 | "Anand Chitipothu " 10 | ] 11 | __license__ = "public domain" 12 | __contributors__ = "see http://webpy.org/changes" 13 | 14 | import utils, db, net, wsgi, http, webapi, httpserver, debugerror 15 | import template, form 16 | 17 | import session 18 | 19 | from utils import * 20 | from db import * 21 | from net import * 22 | from wsgi import * 23 | from http import * 24 | from webapi import * 25 | from httpserver import * 26 | from debugerror import * 27 | from application import * 28 | from browser import * 29 | try: 30 | import webopenid as openid 31 | except ImportError: 32 | pass # requires openid module 33 | 34 | -------------------------------------------------------------------------------- /web.py-0.37/web/browser.py: -------------------------------------------------------------------------------- 1 | """Browser to test web applications. 2 | (from web.py) 3 | """ 4 | from utils import re_compile 5 | from net import htmlunquote 6 | 7 | import httplib, urllib, urllib2 8 | import copy 9 | from StringIO import StringIO 10 | 11 | DEBUG = False 12 | 13 | __all__ = [ 14 | "BrowserError", 15 | "Browser", "AppBrowser", 16 | "AppHandler" 17 | ] 18 | 19 | class BrowserError(Exception): 20 | pass 21 | 22 | class Browser: 23 | def __init__(self): 24 | import cookielib 25 | self.cookiejar = cookielib.CookieJar() 26 | self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar) 27 | self.form = None 28 | 29 | self.url = "http://0.0.0.0:8080/" 30 | self.path = "/" 31 | 32 | self.status = None 33 | self.data = None 34 | self._response = None 35 | self._forms = None 36 | 37 | def reset(self): 38 | """Clears all cookies and history.""" 39 | self.cookiejar.clear() 40 | 41 | def build_opener(self): 42 | """Builds the opener using urllib2.build_opener. 43 | Subclasses can override this function to prodive custom openers. 44 | """ 45 | return urllib2.build_opener() 46 | 47 | def do_request(self, req): 48 | if DEBUG: 49 | print 'requesting', req.get_method(), req.get_full_url() 50 | opener = self.build_opener() 51 | opener.add_handler(self._cookie_processor) 52 | try: 53 | self._response = opener.open(req) 54 | except urllib2.HTTPError, e: 55 | self._response = e 56 | 57 | self.url = self._response.geturl() 58 | self.path = urllib2.Request(self.url).get_selector() 59 | self.data = self._response.read() 60 | self.status = self._response.code 61 | self._forms = None 62 | self.form = None 63 | return self.get_response() 64 | 65 | def open(self, url, data=None, headers={}): 66 | """Opens the specified url.""" 67 | url = urllib.basejoin(self.url, url) 68 | req = urllib2.Request(url, data, headers) 69 | return self.do_request(req) 70 | 71 | def show(self): 72 | """Opens the current page in real web browser.""" 73 | f = open('page.html', 'w') 74 | f.write(self.data) 75 | f.close() 76 | 77 | import webbrowser, os 78 | url = 'file://' + os.path.abspath('page.html') 79 | webbrowser.open(url) 80 | 81 | def get_response(self): 82 | """Returns a copy of the current response.""" 83 | return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl()) 84 | 85 | def get_soup(self): 86 | """Returns beautiful soup of the current document.""" 87 | import BeautifulSoup 88 | return BeautifulSoup.BeautifulSoup(self.data) 89 | 90 | def get_text(self, e=None): 91 | """Returns content of e or the current document as plain text.""" 92 | e = e or self.get_soup() 93 | return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)]) 94 | 95 | def _get_links(self): 96 | soup = self.get_soup() 97 | return [a for a in soup.findAll(name='a')] 98 | 99 | def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None): 100 | """Returns all links in the document.""" 101 | return self._filter_links(self._get_links(), 102 | text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate) 103 | 104 | def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None): 105 | if link is None: 106 | links = self._filter_links(self.get_links(), 107 | text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate) 108 | link = links and links[0] 109 | 110 | if link: 111 | return self.open(link['href']) 112 | else: 113 | raise BrowserError("No link found") 114 | 115 | def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None): 116 | links = self._filter_links(self.get_links(), 117 | text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate) 118 | return links and links[0] or None 119 | 120 | def _filter_links(self, links, 121 | text=None, text_regex=None, 122 | url=None, url_regex=None, 123 | predicate=None): 124 | predicates = [] 125 | if text is not None: 126 | predicates.append(lambda link: link.string == text) 127 | if text_regex is not None: 128 | predicates.append(lambda link: re_compile(text_regex).search(link.string or '')) 129 | if url is not None: 130 | predicates.append(lambda link: link.get('href') == url) 131 | if url_regex is not None: 132 | predicates.append(lambda link: re_compile(url_regex).search(link.get('href', ''))) 133 | if predicate: 134 | predicate.append(predicate) 135 | 136 | def f(link): 137 | for p in predicates: 138 | if not p(link): 139 | return False 140 | return True 141 | 142 | return [link for link in links if f(link)] 143 | 144 | def get_forms(self): 145 | """Returns all forms in the current document. 146 | The returned form objects implement the ClientForm.HTMLForm interface. 147 | """ 148 | if self._forms is None: 149 | import ClientForm 150 | self._forms = ClientForm.ParseResponse(self.get_response(), backwards_compat=False) 151 | return self._forms 152 | 153 | def select_form(self, name=None, predicate=None, index=0): 154 | """Selects the specified form.""" 155 | forms = self.get_forms() 156 | 157 | if name is not None: 158 | forms = [f for f in forms if f.name == name] 159 | if predicate: 160 | forms = [f for f in forms if predicate(f)] 161 | 162 | if forms: 163 | self.form = forms[index] 164 | return self.form 165 | else: 166 | raise BrowserError("No form selected.") 167 | 168 | def submit(self, **kw): 169 | """submits the currently selected form.""" 170 | if self.form is None: 171 | raise BrowserError("No form selected.") 172 | req = self.form.click(**kw) 173 | return self.do_request(req) 174 | 175 | def __getitem__(self, key): 176 | return self.form[key] 177 | 178 | def __setitem__(self, key, value): 179 | self.form[key] = value 180 | 181 | class AppBrowser(Browser): 182 | """Browser interface to test web.py apps. 183 | 184 | b = AppBrowser(app) 185 | b.open('/') 186 | b.follow_link(text='Login') 187 | 188 | b.select_form(name='login') 189 | b['username'] = 'joe' 190 | b['password'] = 'secret' 191 | b.submit() 192 | 193 | assert b.path == '/' 194 | assert 'Welcome joe' in b.get_text() 195 | """ 196 | def __init__(self, app): 197 | Browser.__init__(self) 198 | self.app = app 199 | 200 | def build_opener(self): 201 | return urllib2.build_opener(AppHandler(self.app)) 202 | 203 | class AppHandler(urllib2.HTTPHandler): 204 | """urllib2 handler to handle requests using web.py application.""" 205 | handler_order = 100 206 | 207 | def __init__(self, app): 208 | self.app = app 209 | 210 | def http_open(self, req): 211 | result = self.app.request( 212 | localpart=req.get_selector(), 213 | method=req.get_method(), 214 | host=req.get_host(), 215 | data=req.get_data(), 216 | headers=dict(req.header_items()), 217 | https=req.get_type() == "https" 218 | ) 219 | return self._make_response(result, req.get_full_url()) 220 | 221 | def https_open(self, req): 222 | return self.http_open(req) 223 | 224 | try: 225 | https_request = urllib2.HTTPHandler.do_request_ 226 | except AttributeError: 227 | # for python 2.3 228 | pass 229 | 230 | def _make_response(self, result, url): 231 | data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items]) 232 | headers = httplib.HTTPMessage(StringIO(data)) 233 | response = urllib.addinfourl(StringIO(result.data), headers, url) 234 | code, msg = result.status.split(None, 1) 235 | response.code, response.msg = int(code), msg 236 | return response 237 | -------------------------------------------------------------------------------- /web.py-0.37/web/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frenzymadness/Cisco-ACL-Analyzer/e01ea94705ccd6bfc3f18702056f2e814a7b46b6/web.py-0.37/web/contrib/__init__.py -------------------------------------------------------------------------------- /web.py-0.37/web/contrib/template.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface to various templating engines. 3 | """ 4 | import os.path 5 | 6 | __all__ = [ 7 | "render_cheetah", "render_genshi", "render_mako", 8 | "cache", 9 | ] 10 | 11 | class render_cheetah: 12 | """Rendering interface to Cheetah Templates. 13 | 14 | Example: 15 | 16 | render = render_cheetah('templates') 17 | render.hello(name="cheetah") 18 | """ 19 | def __init__(self, path): 20 | # give error if Chetah is not installed 21 | from Cheetah.Template import Template 22 | self.path = path 23 | 24 | def __getattr__(self, name): 25 | from Cheetah.Template import Template 26 | path = os.path.join(self.path, name + ".html") 27 | 28 | def template(**kw): 29 | t = Template(file=path, searchList=[kw]) 30 | return t.respond() 31 | 32 | return template 33 | 34 | class render_genshi: 35 | """Rendering interface genshi templates. 36 | Example: 37 | 38 | for xml/html templates. 39 | 40 | render = render_genshi(['templates/']) 41 | render.hello(name='genshi') 42 | 43 | For text templates: 44 | 45 | render = render_genshi(['templates/'], type='text') 46 | render.hello(name='genshi') 47 | """ 48 | 49 | def __init__(self, *a, **kwargs): 50 | from genshi.template import TemplateLoader 51 | 52 | self._type = kwargs.pop('type', None) 53 | self._loader = TemplateLoader(*a, **kwargs) 54 | 55 | def __getattr__(self, name): 56 | # Assuming all templates are html 57 | path = name + ".html" 58 | 59 | if self._type == "text": 60 | from genshi.template import TextTemplate 61 | cls = TextTemplate 62 | type = "text" 63 | else: 64 | cls = None 65 | type = None 66 | 67 | t = self._loader.load(path, cls=cls) 68 | def template(**kw): 69 | stream = t.generate(**kw) 70 | if type: 71 | return stream.render(type) 72 | else: 73 | return stream.render() 74 | return template 75 | 76 | class render_jinja: 77 | """Rendering interface to Jinja2 Templates 78 | 79 | Example: 80 | 81 | render= render_jinja('templates') 82 | render.hello(name='jinja2') 83 | """ 84 | def __init__(self, *a, **kwargs): 85 | extensions = kwargs.pop('extensions', []) 86 | globals = kwargs.pop('globals', {}) 87 | 88 | from jinja2 import Environment,FileSystemLoader 89 | self._lookup = Environment(loader=FileSystemLoader(*a, **kwargs), extensions=extensions) 90 | self._lookup.globals.update(globals) 91 | 92 | def __getattr__(self, name): 93 | # Assuming all templates end with .html 94 | path = name + '.html' 95 | t = self._lookup.get_template(path) 96 | return t.render 97 | 98 | class render_mako: 99 | """Rendering interface to Mako Templates. 100 | 101 | Example: 102 | 103 | render = render_mako(directories=['templates']) 104 | render.hello(name="mako") 105 | """ 106 | def __init__(self, *a, **kwargs): 107 | from mako.lookup import TemplateLookup 108 | self._lookup = TemplateLookup(*a, **kwargs) 109 | 110 | def __getattr__(self, name): 111 | # Assuming all templates are html 112 | path = name + ".html" 113 | t = self._lookup.get_template(path) 114 | return t.render 115 | 116 | class cache: 117 | """Cache for any rendering interface. 118 | 119 | Example: 120 | 121 | render = cache(render_cheetah("templates/")) 122 | render.hello(name='cache') 123 | """ 124 | def __init__(self, render): 125 | self._render = render 126 | self._cache = {} 127 | 128 | def __getattr__(self, name): 129 | if name not in self._cache: 130 | self._cache[name] = getattr(self._render, name) 131 | return self._cache[name] 132 | -------------------------------------------------------------------------------- /web.py-0.37/web/debugerror.py: -------------------------------------------------------------------------------- 1 | """ 2 | pretty debug errors 3 | (part of web.py) 4 | 5 | portions adapted from Django 6 | Copyright (c) 2005, the Lawrence Journal-World 7 | Used under the modified BSD license: 8 | http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 9 | """ 10 | 11 | __all__ = ["debugerror", "djangoerror", "emailerrors"] 12 | 13 | import sys, urlparse, pprint, traceback 14 | from template import Template 15 | from net import websafe 16 | from utils import sendmail, safestr 17 | import webapi as web 18 | 19 | import os, os.path 20 | whereami = os.path.join(os.getcwd(), __file__) 21 | whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1]) 22 | djangoerror_t = """\ 23 | $def with (exception_type, exception_value, frames) 24 | 25 | 26 | 27 | 28 | 29 | $exception_type at $ctx.path 30 | 79 | 127 | 128 | 129 | 130 | $def dicttable (d, kls='req', id=None): 131 | $ items = d and d.items() or [] 132 | $items.sort() 133 | $:dicttable_items(items, kls, id) 134 | 135 | $def dicttable_items(items, kls='req', id=None): 136 | $if items: 137 | 140 | 141 | $for k, v in items: 142 | 143 | 144 |
VariableValue
$k
$prettify(v)
145 | $else: 146 |

No data.

147 | 148 |
149 |

$exception_type at $ctx.path

150 |

$exception_value

151 | 152 | 153 | 154 | 155 | 156 | 157 |
Python$frames[0].filename in $frames[0].function, line $frames[0].lineno
Web$ctx.method $ctx.home$ctx.path
158 |
159 |
160 |

Traceback (innermost first)

161 |
    162 | $for frame in frames: 163 |
  • 164 | $frame.filename in $frame.function 165 | $if frame.context_line is not None: 166 |
    167 | $if frame.pre_context: 168 |
      169 | $for line in frame.pre_context: 170 |
    1. $line
    2. 171 |
    172 |
    1. $frame.context_line ...
    173 | $if frame.post_context: 174 |
      175 | $for line in frame.post_context: 176 |
    1. $line
    2. 177 |
    178 |
    179 | 180 | $if frame.vars: 181 |
    182 | Local vars 183 | $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame)) 184 |
    185 | $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id))) 186 |
  • 187 |
188 |
189 | 190 |
191 | $if ctx.output or ctx.headers: 192 |

Response so far

193 |

HEADERS

194 | $:dicttable_items(ctx.headers) 195 | 196 |

BODY

197 |

198 | $ctx.output 199 |

200 | 201 |

Request information

202 | 203 |

INPUT

204 | $:dicttable(web.input(_unicode=False)) 205 | 206 | 207 | $:dicttable(web.cookies()) 208 | 209 |

META

210 | $ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)] 211 | $:dicttable(dict(newctx)) 212 | 213 |

ENVIRONMENT

214 | $:dicttable(ctx.env) 215 |
216 | 217 |
218 |

219 | You're seeing this error because you have web.config.debug 220 | set to True. Set that to False if you don't want to see this. 221 |

222 |
223 | 224 | 225 | 226 | """ 227 | 228 | djangoerror_r = None 229 | 230 | def djangoerror(): 231 | def _get_lines_from_file(filename, lineno, context_lines): 232 | """ 233 | Returns context_lines before and after lineno from file. 234 | Returns (pre_context_lineno, pre_context, context_line, post_context). 235 | """ 236 | try: 237 | source = open(filename).readlines() 238 | lower_bound = max(0, lineno - context_lines) 239 | upper_bound = lineno + context_lines 240 | 241 | pre_context = \ 242 | [line.strip('\n') for line in source[lower_bound:lineno]] 243 | context_line = source[lineno].strip('\n') 244 | post_context = \ 245 | [line.strip('\n') for line in source[lineno + 1:upper_bound]] 246 | 247 | return lower_bound, pre_context, context_line, post_context 248 | except (OSError, IOError, IndexError): 249 | return None, [], None, [] 250 | 251 | exception_type, exception_value, tback = sys.exc_info() 252 | frames = [] 253 | while tback is not None: 254 | filename = tback.tb_frame.f_code.co_filename 255 | function = tback.tb_frame.f_code.co_name 256 | lineno = tback.tb_lineno - 1 257 | 258 | # hack to get correct line number for templates 259 | lineno += tback.tb_frame.f_locals.get("__lineoffset__", 0) 260 | 261 | pre_context_lineno, pre_context, context_line, post_context = \ 262 | _get_lines_from_file(filename, lineno, 7) 263 | 264 | if '__hidetraceback__' not in tback.tb_frame.f_locals: 265 | frames.append(web.storage({ 266 | 'tback': tback, 267 | 'filename': filename, 268 | 'function': function, 269 | 'lineno': lineno, 270 | 'vars': tback.tb_frame.f_locals, 271 | 'id': id(tback), 272 | 'pre_context': pre_context, 273 | 'context_line': context_line, 274 | 'post_context': post_context, 275 | 'pre_context_lineno': pre_context_lineno, 276 | })) 277 | tback = tback.tb_next 278 | frames.reverse() 279 | urljoin = urlparse.urljoin 280 | def prettify(x): 281 | try: 282 | out = pprint.pformat(x) 283 | except Exception, e: 284 | out = '[could not display: <' + e.__class__.__name__ + \ 285 | ': '+str(e)+'>]' 286 | return out 287 | 288 | global djangoerror_r 289 | if djangoerror_r is None: 290 | djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe) 291 | 292 | t = djangoerror_r 293 | globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify} 294 | t.t.func_globals.update(globals) 295 | return t(exception_type, exception_value, frames) 296 | 297 | def debugerror(): 298 | """ 299 | A replacement for `internalerror` that presents a nice page with lots 300 | of debug information for the programmer. 301 | 302 | (Based on the beautiful 500 page from [Django](http://djangoproject.com/), 303 | designed by [Wilson Miner](http://wilsonminer.com/).) 304 | """ 305 | return web._InternalError(djangoerror()) 306 | 307 | def emailerrors(to_address, olderror, from_address=None): 308 | """ 309 | Wraps the old `internalerror` handler (pass as `olderror`) to 310 | additionally email all errors to `to_address`, to aid in 311 | debugging production websites. 312 | 313 | Emails contain a normal text traceback as well as an 314 | attachment containing the nice `debugerror` page. 315 | """ 316 | from_address = from_address or to_address 317 | 318 | def emailerrors_internal(): 319 | error = olderror() 320 | tb = sys.exc_info() 321 | error_name = tb[0] 322 | error_value = tb[1] 323 | tb_txt = ''.join(traceback.format_exception(*tb)) 324 | path = web.ctx.path 325 | request = web.ctx.method + ' ' + web.ctx.home + web.ctx.fullpath 326 | 327 | message = "\n%s\n\n%s\n\n" % (request, tb_txt) 328 | 329 | sendmail( 330 | "your buggy site <%s>" % from_address, 331 | "the bugfixer <%s>" % to_address, 332 | "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(), 333 | message, 334 | attachments=[ 335 | dict(filename="bug.html", content=safestr(djangoerror())) 336 | ], 337 | ) 338 | return error 339 | 340 | return emailerrors_internal 341 | 342 | if __name__ == "__main__": 343 | urls = ( 344 | '/', 'index' 345 | ) 346 | from application import application 347 | app = application(urls, globals()) 348 | app.internalerror = debugerror 349 | 350 | class index: 351 | def GET(self): 352 | thisdoesnotexist 353 | 354 | app.run() 355 | -------------------------------------------------------------------------------- /web.py-0.37/web/form.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTML forms 3 | (part of web.py) 4 | """ 5 | 6 | import copy, re 7 | import webapi as web 8 | import utils, net 9 | 10 | def attrget(obj, attr, value=None): 11 | try: 12 | if hasattr(obj, 'has_key') and obj.has_key(attr): 13 | return obj[attr] 14 | except TypeError: 15 | # Handle the case where has_key takes different number of arguments. 16 | # This is the case with Model objects on appengine. See #134 17 | pass 18 | if hasattr(obj, attr): 19 | return getattr(obj, attr) 20 | return value 21 | 22 | class Form(object): 23 | r""" 24 | HTML form. 25 | 26 | >>> f = Form(Textbox("x")) 27 | >>> f.render() 28 | u'\n \n
' 29 | """ 30 | def __init__(self, *inputs, **kw): 31 | self.inputs = inputs 32 | self.valid = True 33 | self.note = None 34 | self.validators = kw.pop('validators', []) 35 | 36 | def __call__(self, x=None): 37 | o = copy.deepcopy(self) 38 | if x: o.validates(x) 39 | return o 40 | 41 | def render(self): 42 | out = '' 43 | out += self.rendernote(self.note) 44 | out += '\n' 45 | 46 | for i in self.inputs: 47 | html = utils.safeunicode(i.pre) + i.render() + self.rendernote(i.note) + utils.safeunicode(i.post) 48 | if i.is_hidden(): 49 | out += ' \n' % (html) 50 | else: 51 | out += ' \n' % (i.id, net.websafe(i.description), html) 52 | out += "
%s
%s
" 53 | return out 54 | 55 | def render_css(self): 56 | out = [] 57 | out.append(self.rendernote(self.note)) 58 | for i in self.inputs: 59 | if not i.is_hidden(): 60 | out.append('' % (i.id, net.websafe(i.description))) 61 | out.append(i.pre) 62 | out.append(i.render()) 63 | out.append(self.rendernote(i.note)) 64 | out.append(i.post) 65 | out.append('\n') 66 | return ''.join(out) 67 | 68 | def rendernote(self, note): 69 | if note: return '%s' % net.websafe(note) 70 | else: return "" 71 | 72 | def validates(self, source=None, _validate=True, **kw): 73 | source = source or kw or web.input() 74 | out = True 75 | for i in self.inputs: 76 | v = attrget(source, i.name) 77 | if _validate: 78 | out = i.validate(v) and out 79 | else: 80 | i.set_value(v) 81 | if _validate: 82 | out = out and self._validate(source) 83 | self.valid = out 84 | return out 85 | 86 | def _validate(self, value): 87 | self.value = value 88 | for v in self.validators: 89 | if not v.valid(value): 90 | self.note = v.msg 91 | return False 92 | return True 93 | 94 | def fill(self, source=None, **kw): 95 | return self.validates(source, _validate=False, **kw) 96 | 97 | def __getitem__(self, i): 98 | for x in self.inputs: 99 | if x.name == i: return x 100 | raise KeyError, i 101 | 102 | def __getattr__(self, name): 103 | # don't interfere with deepcopy 104 | inputs = self.__dict__.get('inputs') or [] 105 | for x in inputs: 106 | if x.name == name: return x 107 | raise AttributeError, name 108 | 109 | def get(self, i, default=None): 110 | try: 111 | return self[i] 112 | except KeyError: 113 | return default 114 | 115 | def _get_d(self): #@@ should really be form.attr, no? 116 | return utils.storage([(i.name, i.get_value()) for i in self.inputs]) 117 | d = property(_get_d) 118 | 119 | class Input(object): 120 | def __init__(self, name, *validators, **attrs): 121 | self.name = name 122 | self.validators = validators 123 | self.attrs = attrs = AttributeList(attrs) 124 | 125 | self.description = attrs.pop('description', name) 126 | self.value = attrs.pop('value', None) 127 | self.pre = attrs.pop('pre', "") 128 | self.post = attrs.pop('post', "") 129 | self.note = None 130 | 131 | self.id = attrs.setdefault('id', self.get_default_id()) 132 | 133 | if 'class_' in attrs: 134 | attrs['class'] = attrs['class_'] 135 | del attrs['class_'] 136 | 137 | def is_hidden(self): 138 | return False 139 | 140 | def get_type(self): 141 | raise NotImplementedError 142 | 143 | def get_default_id(self): 144 | return self.name 145 | 146 | def validate(self, value): 147 | self.set_value(value) 148 | 149 | for v in self.validators: 150 | if not v.valid(value): 151 | self.note = v.msg 152 | return False 153 | return True 154 | 155 | def set_value(self, value): 156 | self.value = value 157 | 158 | def get_value(self): 159 | return self.value 160 | 161 | def render(self): 162 | attrs = self.attrs.copy() 163 | attrs['type'] = self.get_type() 164 | if self.value is not None: 165 | attrs['value'] = self.value 166 | attrs['name'] = self.name 167 | return '' % attrs 168 | 169 | def rendernote(self, note): 170 | if note: return '%s' % net.websafe(note) 171 | else: return "" 172 | 173 | def addatts(self): 174 | # add leading space for backward-compatibility 175 | return " " + str(self.attrs) 176 | 177 | class AttributeList(dict): 178 | """List of atributes of input. 179 | 180 | >>> a = AttributeList(type='text', name='x', value=20) 181 | >>> a 182 | 183 | """ 184 | def copy(self): 185 | return AttributeList(self) 186 | 187 | def __str__(self): 188 | return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()]) 189 | 190 | def __repr__(self): 191 | return '' % repr(str(self)) 192 | 193 | class Textbox(Input): 194 | """Textbox input. 195 | 196 | >>> Textbox(name='foo', value='bar').render() 197 | u'' 198 | >>> Textbox(name='foo', value=0).render() 199 | u'' 200 | """ 201 | def get_type(self): 202 | return 'text' 203 | 204 | class Password(Input): 205 | """Password input. 206 | 207 | >>> Password(name='password', value='secret').render() 208 | u'' 209 | """ 210 | 211 | def get_type(self): 212 | return 'password' 213 | 214 | class Textarea(Input): 215 | """Textarea input. 216 | 217 | >>> Textarea(name='foo', value='bar').render() 218 | u'' 219 | """ 220 | def render(self): 221 | attrs = self.attrs.copy() 222 | attrs['name'] = self.name 223 | value = net.websafe(self.value or '') 224 | return '' % (attrs, value) 225 | 226 | class Dropdown(Input): 227 | r"""Dropdown/select input. 228 | 229 | >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render() 230 | u'\n' 231 | >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render() 232 | u'\n' 233 | """ 234 | def __init__(self, name, args, *validators, **attrs): 235 | self.args = args 236 | super(Dropdown, self).__init__(name, *validators, **attrs) 237 | 238 | def render(self): 239 | attrs = self.attrs.copy() 240 | attrs['name'] = self.name 241 | 242 | x = '\n' 248 | return x 249 | 250 | def _render_option(self, arg, indent=' '): 251 | if isinstance(arg, (tuple, list)): 252 | value, desc= arg 253 | else: 254 | value, desc = arg, arg 255 | 256 | if self.value == value or (isinstance(self.value, list) and value in self.value): 257 | select_p = ' selected="selected"' 258 | else: 259 | select_p = '' 260 | return indent + '%s\n' % (select_p, net.websafe(value), net.websafe(desc)) 261 | 262 | 263 | class GroupedDropdown(Dropdown): 264 | r"""Grouped Dropdown/select input. 265 | 266 | >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render() 267 | u'\n' 268 | >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render() 269 | u'\n' 270 | 271 | """ 272 | def __init__(self, name, args, *validators, **attrs): 273 | self.args = args 274 | super(Dropdown, self).__init__(name, *validators, **attrs) 275 | 276 | def render(self): 277 | attrs = self.attrs.copy() 278 | attrs['name'] = self.name 279 | 280 | x = '\n' 289 | return x 290 | 291 | class Radio(Input): 292 | def __init__(self, name, args, *validators, **attrs): 293 | self.args = args 294 | super(Radio, self).__init__(name, *validators, **attrs) 295 | 296 | def render(self): 297 | x = '' 298 | for arg in self.args: 299 | if isinstance(arg, (tuple, list)): 300 | value, desc= arg 301 | else: 302 | value, desc = arg, arg 303 | attrs = self.attrs.copy() 304 | attrs['name'] = self.name 305 | attrs['type'] = 'radio' 306 | attrs['value'] = value 307 | if self.value == value: 308 | attrs['checked'] = 'checked' 309 | x += ' %s' % (attrs, net.websafe(desc)) 310 | x += '' 311 | return x 312 | 313 | class Checkbox(Input): 314 | """Checkbox input. 315 | 316 | >>> Checkbox('foo', value='bar', checked=True).render() 317 | u'' 318 | >>> Checkbox('foo', value='bar').render() 319 | u'' 320 | >>> c = Checkbox('foo', value='bar') 321 | >>> c.validate('on') 322 | True 323 | >>> c.render() 324 | u'' 325 | """ 326 | def __init__(self, name, *validators, **attrs): 327 | self.checked = attrs.pop('checked', False) 328 | Input.__init__(self, name, *validators, **attrs) 329 | 330 | def get_default_id(self): 331 | value = utils.safestr(self.value or "") 332 | return self.name + '_' + value.replace(' ', '_') 333 | 334 | def render(self): 335 | attrs = self.attrs.copy() 336 | attrs['type'] = 'checkbox' 337 | attrs['name'] = self.name 338 | attrs['value'] = self.value 339 | 340 | if self.checked: 341 | attrs['checked'] = 'checked' 342 | return '' % attrs 343 | 344 | def set_value(self, value): 345 | self.checked = bool(value) 346 | 347 | def get_value(self): 348 | return self.checked 349 | 350 | class Button(Input): 351 | """HTML Button. 352 | 353 | >>> Button("save").render() 354 | u'' 355 | >>> Button("action", value="save", html="Save Changes").render() 356 | u'' 357 | """ 358 | def __init__(self, name, *validators, **attrs): 359 | super(Button, self).__init__(name, *validators, **attrs) 360 | self.description = "" 361 | 362 | def render(self): 363 | attrs = self.attrs.copy() 364 | attrs['name'] = self.name 365 | if self.value is not None: 366 | attrs['value'] = self.value 367 | html = attrs.pop('html', None) or net.websafe(self.name) 368 | return '' % (attrs, html) 369 | 370 | class Hidden(Input): 371 | """Hidden Input. 372 | 373 | >>> Hidden(name='foo', value='bar').render() 374 | u'' 375 | """ 376 | def is_hidden(self): 377 | return True 378 | 379 | def get_type(self): 380 | return 'hidden' 381 | 382 | class File(Input): 383 | """File input. 384 | 385 | >>> File(name='f').render() 386 | u'' 387 | """ 388 | def get_type(self): 389 | return 'file' 390 | 391 | class Validator: 392 | def __deepcopy__(self, memo): return copy.copy(self) 393 | def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals()) 394 | def valid(self, value): 395 | try: return self.test(value) 396 | except: return False 397 | 398 | notnull = Validator("Required", bool) 399 | 400 | class regexp(Validator): 401 | def __init__(self, rexp, msg): 402 | self.rexp = re.compile(rexp) 403 | self.msg = msg 404 | 405 | def valid(self, value): 406 | return bool(self.rexp.match(value)) 407 | 408 | if __name__ == "__main__": 409 | import doctest 410 | doctest.testmod() 411 | -------------------------------------------------------------------------------- /web.py-0.37/web/http.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP Utilities 3 | (from web.py) 4 | """ 5 | 6 | __all__ = [ 7 | "expires", "lastmodified", 8 | "prefixurl", "modified", 9 | "changequery", "url", 10 | "profiler", 11 | ] 12 | 13 | import sys, os, threading, urllib, urlparse 14 | try: import datetime 15 | except ImportError: pass 16 | import net, utils, webapi as web 17 | 18 | def prefixurl(base=''): 19 | """ 20 | Sorry, this function is really difficult to explain. 21 | Maybe some other time. 22 | """ 23 | url = web.ctx.path.lstrip('/') 24 | for i in xrange(url.count('/')): 25 | base += '../' 26 | if not base: 27 | base = './' 28 | return base 29 | 30 | def expires(delta): 31 | """ 32 | Outputs an `Expires` header for `delta` from now. 33 | `delta` is a `timedelta` object or a number of seconds. 34 | """ 35 | if isinstance(delta, (int, long)): 36 | delta = datetime.timedelta(seconds=delta) 37 | date_obj = datetime.datetime.utcnow() + delta 38 | web.header('Expires', net.httpdate(date_obj)) 39 | 40 | def lastmodified(date_obj): 41 | """Outputs a `Last-Modified` header for `datetime`.""" 42 | web.header('Last-Modified', net.httpdate(date_obj)) 43 | 44 | def modified(date=None, etag=None): 45 | """ 46 | Checks to see if the page has been modified since the version in the 47 | requester's cache. 48 | 49 | When you publish pages, you can include `Last-Modified` and `ETag` 50 | with the date the page was last modified and an opaque token for 51 | the particular version, respectively. When readers reload the page, 52 | the browser sends along the modification date and etag value for 53 | the version it has in its cache. If the page hasn't changed, 54 | the server can just return `304 Not Modified` and not have to 55 | send the whole page again. 56 | 57 | This function takes the last-modified date `date` and the ETag `etag` 58 | and checks the headers to see if they match. If they do, it returns 59 | `True`, or otherwise it raises NotModified error. It also sets 60 | `Last-Modified` and `ETag` output headers. 61 | """ 62 | try: 63 | from __builtin__ import set 64 | except ImportError: 65 | # for python 2.3 66 | from sets import Set as set 67 | 68 | n = set([x.strip('" ') for x in web.ctx.env.get('HTTP_IF_NONE_MATCH', '').split(',')]) 69 | m = net.parsehttpdate(web.ctx.env.get('HTTP_IF_MODIFIED_SINCE', '').split(';')[0]) 70 | validate = False 71 | if etag: 72 | if '*' in n or etag in n: 73 | validate = True 74 | if date and m: 75 | # we subtract a second because 76 | # HTTP dates don't have sub-second precision 77 | if date-datetime.timedelta(seconds=1) <= m: 78 | validate = True 79 | 80 | if date: lastmodified(date) 81 | if etag: web.header('ETag', '"' + etag + '"') 82 | if validate: 83 | raise web.notmodified() 84 | else: 85 | return True 86 | 87 | def urlencode(query, doseq=0): 88 | """ 89 | Same as urllib.urlencode, but supports unicode strings. 90 | 91 | >>> urlencode({'text':'foo bar'}) 92 | 'text=foo+bar' 93 | >>> urlencode({'x': [1, 2]}, doseq=True) 94 | 'x=1&x=2' 95 | """ 96 | def convert(value, doseq=False): 97 | if doseq and isinstance(value, list): 98 | return [convert(v) for v in value] 99 | else: 100 | return utils.safestr(value) 101 | 102 | query = dict([(k, convert(v, doseq)) for k, v in query.items()]) 103 | return urllib.urlencode(query, doseq=doseq) 104 | 105 | def changequery(query=None, **kw): 106 | """ 107 | Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return 108 | `/foo?a=3&b=2` -- the same URL but with the arguments you requested 109 | changed. 110 | """ 111 | if query is None: 112 | query = web.rawinput(method='get') 113 | for k, v in kw.iteritems(): 114 | if v is None: 115 | query.pop(k, None) 116 | else: 117 | query[k] = v 118 | out = web.ctx.path 119 | if query: 120 | out += '?' + urlencode(query, doseq=True) 121 | return out 122 | 123 | def url(path=None, doseq=False, **kw): 124 | """ 125 | Makes url by concatenating web.ctx.homepath and path and the 126 | query string created using the arguments. 127 | """ 128 | if path is None: 129 | path = web.ctx.path 130 | if path.startswith("/"): 131 | out = web.ctx.homepath + path 132 | else: 133 | out = path 134 | 135 | if kw: 136 | out += '?' + urlencode(kw, doseq=doseq) 137 | 138 | return out 139 | 140 | def profiler(app): 141 | """Outputs basic profiling information at the bottom of each response.""" 142 | from utils import profile 143 | def profile_internal(e, o): 144 | out, result = profile(app)(e, o) 145 | return list(out) + ['
' + net.websafe(result) + '
'] 146 | return profile_internal 147 | 148 | if __name__ == "__main__": 149 | import doctest 150 | doctest.testmod() 151 | -------------------------------------------------------------------------------- /web.py-0.37/web/httpserver.py: -------------------------------------------------------------------------------- 1 | __all__ = ["runsimple"] 2 | 3 | import sys, os 4 | from SimpleHTTPServer import SimpleHTTPRequestHandler 5 | import urllib 6 | import posixpath 7 | 8 | import webapi as web 9 | import net 10 | import utils 11 | 12 | def runbasic(func, server_address=("0.0.0.0", 8080)): 13 | """ 14 | Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` 15 | is hosted statically. 16 | 17 | Based on [WsgiServer][ws] from [Colin Stewart][cs]. 18 | 19 | [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html 20 | [cs]: http://www.owlfish.com/ 21 | """ 22 | # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/) 23 | # Modified somewhat for simplicity 24 | # Used under the modified BSD license: 25 | # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 26 | 27 | import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse 28 | import socket, errno 29 | import traceback 30 | 31 | class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 32 | def run_wsgi_app(self): 33 | protocol, host, path, parameters, query, fragment = \ 34 | urlparse.urlparse('http://dummyhost%s' % self.path) 35 | 36 | # we only use path, query 37 | env = {'wsgi.version': (1, 0) 38 | ,'wsgi.url_scheme': 'http' 39 | ,'wsgi.input': self.rfile 40 | ,'wsgi.errors': sys.stderr 41 | ,'wsgi.multithread': 1 42 | ,'wsgi.multiprocess': 0 43 | ,'wsgi.run_once': 0 44 | ,'REQUEST_METHOD': self.command 45 | ,'REQUEST_URI': self.path 46 | ,'PATH_INFO': path 47 | ,'QUERY_STRING': query 48 | ,'CONTENT_TYPE': self.headers.get('Content-Type', '') 49 | ,'CONTENT_LENGTH': self.headers.get('Content-Length', '') 50 | ,'REMOTE_ADDR': self.client_address[0] 51 | ,'SERVER_NAME': self.server.server_address[0] 52 | ,'SERVER_PORT': str(self.server.server_address[1]) 53 | ,'SERVER_PROTOCOL': self.request_version 54 | } 55 | 56 | for http_header, http_value in self.headers.items(): 57 | env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \ 58 | http_value 59 | 60 | # Setup the state 61 | self.wsgi_sent_headers = 0 62 | self.wsgi_headers = [] 63 | 64 | try: 65 | # We have there environment, now invoke the application 66 | result = self.server.app(env, self.wsgi_start_response) 67 | try: 68 | try: 69 | for data in result: 70 | if data: 71 | self.wsgi_write_data(data) 72 | finally: 73 | if hasattr(result, 'close'): 74 | result.close() 75 | except socket.error, socket_err: 76 | # Catch common network errors and suppress them 77 | if (socket_err.args[0] in \ 78 | (errno.ECONNABORTED, errno.EPIPE)): 79 | return 80 | except socket.timeout, socket_timeout: 81 | return 82 | except: 83 | print >> web.debug, traceback.format_exc(), 84 | 85 | if (not self.wsgi_sent_headers): 86 | # We must write out something! 87 | self.wsgi_write_data(" ") 88 | return 89 | 90 | do_POST = run_wsgi_app 91 | do_PUT = run_wsgi_app 92 | do_DELETE = run_wsgi_app 93 | 94 | def do_GET(self): 95 | if self.path.startswith('/static/'): 96 | SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) 97 | else: 98 | self.run_wsgi_app() 99 | 100 | def wsgi_start_response(self, response_status, response_headers, 101 | exc_info=None): 102 | if (self.wsgi_sent_headers): 103 | raise Exception \ 104 | ("Headers already sent and start_response called again!") 105 | # Should really take a copy to avoid changes in the application.... 106 | self.wsgi_headers = (response_status, response_headers) 107 | return self.wsgi_write_data 108 | 109 | def wsgi_write_data(self, data): 110 | if (not self.wsgi_sent_headers): 111 | status, headers = self.wsgi_headers 112 | # Need to send header prior to data 113 | status_code = status[:status.find(' ')] 114 | status_msg = status[status.find(' ') + 1:] 115 | self.send_response(int(status_code), status_msg) 116 | for header, value in headers: 117 | self.send_header(header, value) 118 | self.end_headers() 119 | self.wsgi_sent_headers = 1 120 | # Send the data 121 | self.wfile.write(data) 122 | 123 | class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): 124 | def __init__(self, func, server_address): 125 | BaseHTTPServer.HTTPServer.__init__(self, 126 | server_address, 127 | WSGIHandler) 128 | self.app = func 129 | self.serverShuttingDown = 0 130 | 131 | print "http://%s:%d/" % server_address 132 | WSGIServer(func, server_address).serve_forever() 133 | 134 | # The WSGIServer instance. 135 | # Made global so that it can be stopped in embedded mode. 136 | server = None 137 | 138 | def runsimple(func, server_address=("0.0.0.0", 8080)): 139 | """ 140 | Runs [CherryPy][cp] WSGI server hosting WSGI app `func`. 141 | The directory `static/` is hosted statically. 142 | 143 | [cp]: http://www.cherrypy.org 144 | """ 145 | global server 146 | func = StaticMiddleware(func) 147 | func = LogMiddleware(func) 148 | 149 | server = WSGIServer(server_address, func) 150 | 151 | if server.ssl_adapter: 152 | print "https://%s:%d/" % server_address 153 | else: 154 | print "http://%s:%d/" % server_address 155 | 156 | try: 157 | server.start() 158 | except (KeyboardInterrupt, SystemExit): 159 | server.stop() 160 | server = None 161 | 162 | def WSGIServer(server_address, wsgi_app): 163 | """Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`. 164 | This function can be overwritten to customize the webserver or use a different webserver. 165 | """ 166 | import wsgiserver 167 | 168 | # Default values of wsgiserver.ssl_adapters uses cherrypy.wsgiserver 169 | # prefix. Overwriting it make it work with web.wsgiserver. 170 | wsgiserver.ssl_adapters = { 171 | 'builtin': 'web.wsgiserver.ssl_builtin.BuiltinSSLAdapter', 172 | 'pyopenssl': 'web.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter', 173 | } 174 | 175 | server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost") 176 | 177 | def create_ssl_adapter(cert, key): 178 | # wsgiserver tries to import submodules as cherrypy.wsgiserver.foo. 179 | # That doesn't work as not it is web.wsgiserver. 180 | # Patching sys.modules temporarily to make it work. 181 | import types 182 | cherrypy = types.ModuleType('cherrypy') 183 | cherrypy.wsgiserver = wsgiserver 184 | sys.modules['cherrypy'] = cherrypy 185 | sys.modules['cherrypy.wsgiserver'] = wsgiserver 186 | 187 | from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter 188 | adapter = pyOpenSSLAdapter(cert, key) 189 | 190 | # We are done with our work. Cleanup the patches. 191 | del sys.modules['cherrypy'] 192 | del sys.modules['cherrypy.wsgiserver'] 193 | 194 | return adapter 195 | 196 | # SSL backward compatibility 197 | if (server.ssl_adapter is None and 198 | getattr(server, 'ssl_certificate', None) and 199 | getattr(server, 'ssl_private_key', None)): 200 | server.ssl_adapter = create_ssl_adapter(server.ssl_certificate, server.ssl_private_key) 201 | 202 | server.nodelay = not sys.platform.startswith('java') # TCP_NODELAY isn't supported on the JVM 203 | return server 204 | 205 | class StaticApp(SimpleHTTPRequestHandler): 206 | """WSGI application for serving static files.""" 207 | def __init__(self, environ, start_response): 208 | self.headers = [] 209 | self.environ = environ 210 | self.start_response = start_response 211 | 212 | def send_response(self, status, msg=""): 213 | self.status = str(status) + " " + msg 214 | 215 | def send_header(self, name, value): 216 | self.headers.append((name, value)) 217 | 218 | def end_headers(self): 219 | pass 220 | 221 | def log_message(*a): pass 222 | 223 | def __iter__(self): 224 | environ = self.environ 225 | 226 | self.path = environ.get('PATH_INFO', '') 227 | self.client_address = environ.get('REMOTE_ADDR','-'), \ 228 | environ.get('REMOTE_PORT','-') 229 | self.command = environ.get('REQUEST_METHOD', '-') 230 | 231 | from cStringIO import StringIO 232 | self.wfile = StringIO() # for capturing error 233 | 234 | try: 235 | path = self.translate_path(self.path) 236 | etag = '"%s"' % os.path.getmtime(path) 237 | client_etag = environ.get('HTTP_IF_NONE_MATCH') 238 | self.send_header('ETag', etag) 239 | if etag == client_etag: 240 | self.send_response(304, "Not Modified") 241 | self.start_response(self.status, self.headers) 242 | raise StopIteration 243 | except OSError: 244 | pass # Probably a 404 245 | 246 | f = self.send_head() 247 | self.start_response(self.status, self.headers) 248 | 249 | if f: 250 | block_size = 16 * 1024 251 | while True: 252 | buf = f.read(block_size) 253 | if not buf: 254 | break 255 | yield buf 256 | f.close() 257 | else: 258 | value = self.wfile.getvalue() 259 | yield value 260 | 261 | class StaticMiddleware: 262 | """WSGI middleware for serving static files.""" 263 | def __init__(self, app, prefix='/static/'): 264 | self.app = app 265 | self.prefix = prefix 266 | 267 | def __call__(self, environ, start_response): 268 | path = environ.get('PATH_INFO', '') 269 | path = self.normpath(path) 270 | 271 | if path.startswith(self.prefix): 272 | return StaticApp(environ, start_response) 273 | else: 274 | return self.app(environ, start_response) 275 | 276 | def normpath(self, path): 277 | path2 = posixpath.normpath(urllib.unquote(path)) 278 | if path.endswith("/"): 279 | path2 += "/" 280 | return path2 281 | 282 | 283 | class LogMiddleware: 284 | """WSGI middleware for logging the status.""" 285 | def __init__(self, app): 286 | self.app = app 287 | self.format = '%s - - [%s] "%s %s %s" - %s' 288 | 289 | from BaseHTTPServer import BaseHTTPRequestHandler 290 | import StringIO 291 | f = StringIO.StringIO() 292 | 293 | class FakeSocket: 294 | def makefile(self, *a): 295 | return f 296 | 297 | # take log_date_time_string method from BaseHTTPRequestHandler 298 | self.log_date_time_string = BaseHTTPRequestHandler(FakeSocket(), None, None).log_date_time_string 299 | 300 | def __call__(self, environ, start_response): 301 | def xstart_response(status, response_headers, *args): 302 | out = start_response(status, response_headers, *args) 303 | self.log(status, environ) 304 | return out 305 | 306 | return self.app(environ, xstart_response) 307 | 308 | def log(self, status, environ): 309 | outfile = environ.get('wsgi.errors', web.debug) 310 | req = environ.get('PATH_INFO', '_') 311 | protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-') 312 | method = environ.get('REQUEST_METHOD', '-') 313 | host = "%s:%s" % (environ.get('REMOTE_ADDR','-'), 314 | environ.get('REMOTE_PORT','-')) 315 | 316 | time = self.log_date_time_string() 317 | 318 | msg = self.format % (host, time, protocol, method, req, status) 319 | print >> outfile, utils.safestr(msg) 320 | -------------------------------------------------------------------------------- /web.py-0.37/web/net.py: -------------------------------------------------------------------------------- 1 | """ 2 | Network Utilities 3 | (from web.py) 4 | """ 5 | 6 | __all__ = [ 7 | "validipaddr", "validipport", "validip", "validaddr", 8 | "urlquote", 9 | "httpdate", "parsehttpdate", 10 | "htmlquote", "htmlunquote", "websafe", 11 | ] 12 | 13 | import urllib, time 14 | try: import datetime 15 | except ImportError: pass 16 | 17 | def validipaddr(address): 18 | """ 19 | Returns True if `address` is a valid IPv4 address. 20 | 21 | >>> validipaddr('192.168.1.1') 22 | True 23 | >>> validipaddr('192.168.1.800') 24 | False 25 | >>> validipaddr('192.168.1') 26 | False 27 | """ 28 | try: 29 | octets = address.split('.') 30 | if len(octets) != 4: 31 | return False 32 | for x in octets: 33 | if not (0 <= int(x) <= 255): 34 | return False 35 | except ValueError: 36 | return False 37 | return True 38 | 39 | def validipport(port): 40 | """ 41 | Returns True if `port` is a valid IPv4 port. 42 | 43 | >>> validipport('9000') 44 | True 45 | >>> validipport('foo') 46 | False 47 | >>> validipport('1000000') 48 | False 49 | """ 50 | try: 51 | if not (0 <= int(port) <= 65535): 52 | return False 53 | except ValueError: 54 | return False 55 | return True 56 | 57 | def validip(ip, defaultaddr="0.0.0.0", defaultport=8080): 58 | """Returns `(ip_address, port)` from string `ip_addr_port`""" 59 | addr = defaultaddr 60 | port = defaultport 61 | 62 | ip = ip.split(":", 1) 63 | if len(ip) == 1: 64 | if not ip[0]: 65 | pass 66 | elif validipaddr(ip[0]): 67 | addr = ip[0] 68 | elif validipport(ip[0]): 69 | port = int(ip[0]) 70 | else: 71 | raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' 72 | elif len(ip) == 2: 73 | addr, port = ip 74 | if not validipaddr(addr) and validipport(port): 75 | raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' 76 | port = int(port) 77 | else: 78 | raise ValueError, ':'.join(ip) + ' is not a valid IP address/port' 79 | return (addr, port) 80 | 81 | def validaddr(string_): 82 | """ 83 | Returns either (ip_address, port) or "/path/to/socket" from string_ 84 | 85 | >>> validaddr('/path/to/socket') 86 | '/path/to/socket' 87 | >>> validaddr('8000') 88 | ('0.0.0.0', 8000) 89 | >>> validaddr('127.0.0.1') 90 | ('127.0.0.1', 8080) 91 | >>> validaddr('127.0.0.1:8000') 92 | ('127.0.0.1', 8000) 93 | >>> validaddr('fff') 94 | Traceback (most recent call last): 95 | ... 96 | ValueError: fff is not a valid IP address/port 97 | """ 98 | if '/' in string_: 99 | return string_ 100 | else: 101 | return validip(string_) 102 | 103 | def urlquote(val): 104 | """ 105 | Quotes a string for use in a URL. 106 | 107 | >>> urlquote('://?f=1&j=1') 108 | '%3A//%3Ff%3D1%26j%3D1' 109 | >>> urlquote(None) 110 | '' 111 | >>> urlquote(u'\u203d') 112 | '%E2%80%BD' 113 | """ 114 | if val is None: return '' 115 | if not isinstance(val, unicode): val = str(val) 116 | else: val = val.encode('utf-8') 117 | return urllib.quote(val) 118 | 119 | def httpdate(date_obj): 120 | """ 121 | Formats a datetime object for use in HTTP headers. 122 | 123 | >>> import datetime 124 | >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1)) 125 | 'Thu, 01 Jan 1970 01:01:01 GMT' 126 | """ 127 | return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT") 128 | 129 | def parsehttpdate(string_): 130 | """ 131 | Parses an HTTP date into a datetime object. 132 | 133 | >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT') 134 | datetime.datetime(1970, 1, 1, 1, 1, 1) 135 | """ 136 | try: 137 | t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z") 138 | except ValueError: 139 | return None 140 | return datetime.datetime(*t[:6]) 141 | 142 | def htmlquote(text): 143 | r""" 144 | Encodes `text` for raw use in HTML. 145 | 146 | >>> htmlquote(u"<'&\">") 147 | u'<'&">' 148 | """ 149 | text = text.replace(u"&", u"&") # Must be done first! 150 | text = text.replace(u"<", u"<") 151 | text = text.replace(u">", u">") 152 | text = text.replace(u"'", u"'") 153 | text = text.replace(u'"', u""") 154 | return text 155 | 156 | def htmlunquote(text): 157 | r""" 158 | Decodes `text` that's HTML quoted. 159 | 160 | >>> htmlunquote(u'<'&">') 161 | u'<\'&">' 162 | """ 163 | text = text.replace(u""", u'"') 164 | text = text.replace(u"'", u"'") 165 | text = text.replace(u">", u">") 166 | text = text.replace(u"<", u"<") 167 | text = text.replace(u"&", u"&") # Must be done last! 168 | return text 169 | 170 | def websafe(val): 171 | r"""Converts `val` so that it is safe for use in Unicode HTML. 172 | 173 | >>> websafe("<'&\">") 174 | u'<'&">' 175 | >>> websafe(None) 176 | u'' 177 | >>> websafe(u'\u203d') 178 | u'\u203d' 179 | >>> websafe('\xe2\x80\xbd') 180 | u'\u203d' 181 | """ 182 | if val is None: 183 | return u'' 184 | elif isinstance(val, str): 185 | val = val.decode('utf-8') 186 | elif not isinstance(val, unicode): 187 | val = unicode(val) 188 | 189 | return htmlquote(val) 190 | 191 | if __name__ == "__main__": 192 | import doctest 193 | doctest.testmod() 194 | -------------------------------------------------------------------------------- /web.py-0.37/web/python23.py: -------------------------------------------------------------------------------- 1 | """Python 2.3 compatabilty""" 2 | import threading 3 | 4 | class threadlocal(object): 5 | """Implementation of threading.local for python2.3. 6 | """ 7 | def __getattribute__(self, name): 8 | if name == "__dict__": 9 | return threadlocal._getd(self) 10 | else: 11 | try: 12 | return object.__getattribute__(self, name) 13 | except AttributeError: 14 | try: 15 | return self.__dict__[name] 16 | except KeyError: 17 | raise AttributeError, name 18 | 19 | def __setattr__(self, name, value): 20 | self.__dict__[name] = value 21 | 22 | def __delattr__(self, name): 23 | try: 24 | del self.__dict__[name] 25 | except KeyError: 26 | raise AttributeError, name 27 | 28 | def _getd(self): 29 | t = threading.currentThread() 30 | if not hasattr(t, '_d'): 31 | # using __dict__ of thread as thread local storage 32 | t._d = {} 33 | 34 | _id = id(self) 35 | # there could be multiple instances of threadlocal. 36 | # use id(self) as key 37 | if _id not in t._d: 38 | t._d[_id] = {} 39 | return t._d[_id] 40 | 41 | if __name__ == '__main__': 42 | d = threadlocal() 43 | d.x = 1 44 | print d.__dict__ 45 | print d.x 46 | -------------------------------------------------------------------------------- /web.py-0.37/web/session.py: -------------------------------------------------------------------------------- 1 | """ 2 | Session Management 3 | (from web.py) 4 | """ 5 | 6 | import os, time, datetime, random, base64 7 | import os.path 8 | from copy import deepcopy 9 | try: 10 | import cPickle as pickle 11 | except ImportError: 12 | import pickle 13 | try: 14 | import hashlib 15 | sha1 = hashlib.sha1 16 | except ImportError: 17 | import sha 18 | sha1 = sha.new 19 | 20 | import utils 21 | import webapi as web 22 | 23 | __all__ = [ 24 | 'Session', 'SessionExpired', 25 | 'Store', 'DiskStore', 'DBStore', 26 | ] 27 | 28 | web.config.session_parameters = utils.storage({ 29 | 'cookie_name': 'webpy_session_id', 30 | 'cookie_domain': None, 31 | 'cookie_path' : None, 32 | 'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds 33 | 'ignore_expiry': True, 34 | 'ignore_change_ip': True, 35 | 'secret_key': 'fLjUfxqXtfNoIldA0A0J', 36 | 'expired_message': 'Session expired', 37 | 'httponly': True, 38 | 'secure': False 39 | }) 40 | 41 | class SessionExpired(web.HTTPError): 42 | def __init__(self, message): 43 | web.HTTPError.__init__(self, '200 OK', {}, data=message) 44 | 45 | class Session(object): 46 | """Session management for web.py 47 | """ 48 | __slots__ = [ 49 | "store", "_initializer", "_last_cleanup_time", "_config", "_data", 50 | "__getitem__", "__setitem__", "__delitem__" 51 | ] 52 | 53 | def __init__(self, app, store, initializer=None): 54 | self.store = store 55 | self._initializer = initializer 56 | self._last_cleanup_time = 0 57 | self._config = utils.storage(web.config.session_parameters) 58 | self._data = utils.threadeddict() 59 | 60 | self.__getitem__ = self._data.__getitem__ 61 | self.__setitem__ = self._data.__setitem__ 62 | self.__delitem__ = self._data.__delitem__ 63 | 64 | if app: 65 | app.add_processor(self._processor) 66 | 67 | def __contains__(self, name): 68 | return name in self._data 69 | 70 | def __getattr__(self, name): 71 | return getattr(self._data, name) 72 | 73 | def __setattr__(self, name, value): 74 | if name in self.__slots__: 75 | object.__setattr__(self, name, value) 76 | else: 77 | setattr(self._data, name, value) 78 | 79 | def __delattr__(self, name): 80 | delattr(self._data, name) 81 | 82 | def _processor(self, handler): 83 | """Application processor to setup session for every request""" 84 | self._cleanup() 85 | self._load() 86 | 87 | try: 88 | return handler() 89 | finally: 90 | self._save() 91 | 92 | def _load(self): 93 | """Load the session from the store, by the id from cookie""" 94 | cookie_name = self._config.cookie_name 95 | cookie_domain = self._config.cookie_domain 96 | cookie_path = self._config.cookie_path 97 | httponly = self._config.httponly 98 | self.session_id = web.cookies().get(cookie_name) 99 | 100 | # protection against session_id tampering 101 | if self.session_id and not self._valid_session_id(self.session_id): 102 | self.session_id = None 103 | 104 | self._check_expiry() 105 | if self.session_id: 106 | d = self.store[self.session_id] 107 | self.update(d) 108 | self._validate_ip() 109 | 110 | if not self.session_id: 111 | self.session_id = self._generate_session_id() 112 | 113 | if self._initializer: 114 | if isinstance(self._initializer, dict): 115 | self.update(deepcopy(self._initializer)) 116 | elif hasattr(self._initializer, '__call__'): 117 | self._initializer() 118 | 119 | self.ip = web.ctx.ip 120 | 121 | def _check_expiry(self): 122 | # check for expiry 123 | if self.session_id and self.session_id not in self.store: 124 | if self._config.ignore_expiry: 125 | self.session_id = None 126 | else: 127 | return self.expired() 128 | 129 | def _validate_ip(self): 130 | # check for change of IP 131 | if self.session_id and self.get('ip', None) != web.ctx.ip: 132 | if not self._config.ignore_change_ip: 133 | return self.expired() 134 | 135 | def _save(self): 136 | if not self.get('_killed'): 137 | self._setcookie(self.session_id) 138 | self.store[self.session_id] = dict(self._data) 139 | else: 140 | self._setcookie(self.session_id, expires=-1) 141 | 142 | def _setcookie(self, session_id, expires='', **kw): 143 | cookie_name = self._config.cookie_name 144 | cookie_domain = self._config.cookie_domain 145 | cookie_path = self._config.cookie_path 146 | httponly = self._config.httponly 147 | secure = self._config.secure 148 | web.setcookie(cookie_name, session_id, expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, path=cookie_path) 149 | 150 | def _generate_session_id(self): 151 | """Generate a random id for session""" 152 | 153 | while True: 154 | rand = os.urandom(16) 155 | now = time.time() 156 | secret_key = self._config.secret_key 157 | session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key)) 158 | session_id = session_id.hexdigest() 159 | if session_id not in self.store: 160 | break 161 | return session_id 162 | 163 | def _valid_session_id(self, session_id): 164 | rx = utils.re_compile('^[0-9a-fA-F]+$') 165 | return rx.match(session_id) 166 | 167 | def _cleanup(self): 168 | """Cleanup the stored sessions""" 169 | current_time = time.time() 170 | timeout = self._config.timeout 171 | if current_time - self._last_cleanup_time > timeout: 172 | self.store.cleanup(timeout) 173 | self._last_cleanup_time = current_time 174 | 175 | def expired(self): 176 | """Called when an expired session is atime""" 177 | self._killed = True 178 | self._save() 179 | raise SessionExpired(self._config.expired_message) 180 | 181 | def kill(self): 182 | """Kill the session, make it no longer available""" 183 | del self.store[self.session_id] 184 | self._killed = True 185 | 186 | class Store: 187 | """Base class for session stores""" 188 | 189 | def __contains__(self, key): 190 | raise NotImplementedError 191 | 192 | def __getitem__(self, key): 193 | raise NotImplementedError 194 | 195 | def __setitem__(self, key, value): 196 | raise NotImplementedError 197 | 198 | def cleanup(self, timeout): 199 | """removes all the expired sessions""" 200 | raise NotImplementedError 201 | 202 | def encode(self, session_dict): 203 | """encodes session dict as a string""" 204 | pickled = pickle.dumps(session_dict) 205 | return base64.encodestring(pickled) 206 | 207 | def decode(self, session_data): 208 | """decodes the data to get back the session dict """ 209 | pickled = base64.decodestring(session_data) 210 | return pickle.loads(pickled) 211 | 212 | class DiskStore(Store): 213 | """ 214 | Store for saving a session on disk. 215 | 216 | >>> import tempfile 217 | >>> root = tempfile.mkdtemp() 218 | >>> s = DiskStore(root) 219 | >>> s['a'] = 'foo' 220 | >>> s['a'] 221 | 'foo' 222 | >>> time.sleep(0.01) 223 | >>> s.cleanup(0.01) 224 | >>> s['a'] 225 | Traceback (most recent call last): 226 | ... 227 | KeyError: 'a' 228 | """ 229 | def __init__(self, root): 230 | # if the storage root doesn't exists, create it. 231 | if not os.path.exists(root): 232 | os.makedirs( 233 | os.path.abspath(root) 234 | ) 235 | self.root = root 236 | 237 | def _get_path(self, key): 238 | if os.path.sep in key: 239 | raise ValueError, "Bad key: %s" % repr(key) 240 | return os.path.join(self.root, key) 241 | 242 | def __contains__(self, key): 243 | path = self._get_path(key) 244 | return os.path.exists(path) 245 | 246 | def __getitem__(self, key): 247 | path = self._get_path(key) 248 | if os.path.exists(path): 249 | pickled = open(path).read() 250 | return self.decode(pickled) 251 | else: 252 | raise KeyError, key 253 | 254 | def __setitem__(self, key, value): 255 | path = self._get_path(key) 256 | pickled = self.encode(value) 257 | try: 258 | f = open(path, 'w') 259 | try: 260 | f.write(pickled) 261 | finally: 262 | f.close() 263 | except IOError: 264 | pass 265 | 266 | def __delitem__(self, key): 267 | path = self._get_path(key) 268 | if os.path.exists(path): 269 | os.remove(path) 270 | 271 | def cleanup(self, timeout): 272 | now = time.time() 273 | for f in os.listdir(self.root): 274 | path = self._get_path(f) 275 | atime = os.stat(path).st_atime 276 | if now - atime > timeout : 277 | os.remove(path) 278 | 279 | class DBStore(Store): 280 | """Store for saving a session in database 281 | Needs a table with the following columns: 282 | 283 | session_id CHAR(128) UNIQUE NOT NULL, 284 | atime DATETIME NOT NULL default current_timestamp, 285 | data TEXT 286 | """ 287 | def __init__(self, db, table_name): 288 | self.db = db 289 | self.table = table_name 290 | 291 | def __contains__(self, key): 292 | data = self.db.select(self.table, where="session_id=$key", vars=locals()) 293 | return bool(list(data)) 294 | 295 | def __getitem__(self, key): 296 | now = datetime.datetime.now() 297 | try: 298 | s = self.db.select(self.table, where="session_id=$key", vars=locals())[0] 299 | self.db.update(self.table, where="session_id=$key", atime=now, vars=locals()) 300 | except IndexError: 301 | raise KeyError 302 | else: 303 | return self.decode(s.data) 304 | 305 | def __setitem__(self, key, value): 306 | pickled = self.encode(value) 307 | now = datetime.datetime.now() 308 | if key in self: 309 | self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals()) 310 | else: 311 | self.db.insert(self.table, False, session_id=key, data=pickled ) 312 | 313 | def __delitem__(self, key): 314 | self.db.delete(self.table, where="session_id=$key", vars=locals()) 315 | 316 | def cleanup(self, timeout): 317 | timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg 318 | last_allowed_time = datetime.datetime.now() - timeout 319 | self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals()) 320 | 321 | class ShelfStore: 322 | """Store for saving session using `shelve` module. 323 | 324 | import shelve 325 | store = ShelfStore(shelve.open('session.shelf')) 326 | 327 | XXX: is shelve thread-safe? 328 | """ 329 | def __init__(self, shelf): 330 | self.shelf = shelf 331 | 332 | def __contains__(self, key): 333 | return key in self.shelf 334 | 335 | def __getitem__(self, key): 336 | atime, v = self.shelf[key] 337 | self[key] = v # update atime 338 | return v 339 | 340 | def __setitem__(self, key, value): 341 | self.shelf[key] = time.time(), value 342 | 343 | def __delitem__(self, key): 344 | try: 345 | del self.shelf[key] 346 | except KeyError: 347 | pass 348 | 349 | def cleanup(self, timeout): 350 | now = time.time() 351 | for k in self.shelf.keys(): 352 | atime, v = self.shelf[k] 353 | if now - atime > timeout : 354 | del self[k] 355 | 356 | if __name__ == '__main__' : 357 | import doctest 358 | doctest.testmod() 359 | -------------------------------------------------------------------------------- /web.py-0.37/web/test.py: -------------------------------------------------------------------------------- 1 | """test utilities 2 | (part of web.py) 3 | """ 4 | import unittest 5 | import sys, os 6 | import web 7 | 8 | TestCase = unittest.TestCase 9 | TestSuite = unittest.TestSuite 10 | 11 | def load_modules(names): 12 | return [__import__(name, None, None, "x") for name in names] 13 | 14 | def module_suite(module, classnames=None): 15 | """Makes a suite from a module.""" 16 | if classnames: 17 | return unittest.TestLoader().loadTestsFromNames(classnames, module) 18 | elif hasattr(module, 'suite'): 19 | return module.suite() 20 | else: 21 | return unittest.TestLoader().loadTestsFromModule(module) 22 | 23 | def doctest_suite(module_names): 24 | """Makes a test suite from doctests.""" 25 | import doctest 26 | suite = TestSuite() 27 | for mod in load_modules(module_names): 28 | suite.addTest(doctest.DocTestSuite(mod)) 29 | return suite 30 | 31 | def suite(module_names): 32 | """Creates a suite from multiple modules.""" 33 | suite = TestSuite() 34 | for mod in load_modules(module_names): 35 | suite.addTest(module_suite(mod)) 36 | return suite 37 | 38 | def runTests(suite): 39 | runner = unittest.TextTestRunner() 40 | return runner.run(suite) 41 | 42 | def main(suite=None): 43 | if not suite: 44 | main_module = __import__('__main__') 45 | # allow command line switches 46 | args = [a for a in sys.argv[1:] if not a.startswith('-')] 47 | suite = module_suite(main_module, args or None) 48 | 49 | result = runTests(suite) 50 | sys.exit(not result.wasSuccessful()) 51 | 52 | -------------------------------------------------------------------------------- /web.py-0.37/web/webopenid.py: -------------------------------------------------------------------------------- 1 | """openid.py: an openid library for web.py 2 | 3 | Notes: 4 | 5 | - This will create a file called .openid_secret_key in the 6 | current directory with your secret key in it. If someone 7 | has access to this file they can log in as any user. And 8 | if the app can't find this file for any reason (e.g. you 9 | moved the app somewhere else) then each currently logged 10 | in user will get logged out. 11 | 12 | - State must be maintained through the entire auth process 13 | -- this means that if you have multiple web.py processes 14 | serving one set of URLs or if you restart your app often 15 | then log ins will fail. You have to replace sessions and 16 | store for things to work. 17 | 18 | - We set cookies starting with "openid_". 19 | 20 | """ 21 | 22 | import os 23 | import random 24 | import hmac 25 | import __init__ as web 26 | import openid.consumer.consumer 27 | import openid.store.memstore 28 | 29 | sessions = {} 30 | store = openid.store.memstore.MemoryStore() 31 | 32 | def _secret(): 33 | try: 34 | secret = file('.openid_secret_key').read() 35 | except IOError: 36 | # file doesn't exist 37 | secret = os.urandom(20) 38 | file('.openid_secret_key', 'w').write(secret) 39 | return secret 40 | 41 | def _hmac(identity_url): 42 | return hmac.new(_secret(), identity_url).hexdigest() 43 | 44 | def _random_session(): 45 | n = random.random() 46 | while n in sessions: 47 | n = random.random() 48 | n = str(n) 49 | return n 50 | 51 | def status(): 52 | oid_hash = web.cookies().get('openid_identity_hash', '').split(',', 1) 53 | if len(oid_hash) > 1: 54 | oid_hash, identity_url = oid_hash 55 | if oid_hash == _hmac(identity_url): 56 | return identity_url 57 | return None 58 | 59 | def form(openid_loc): 60 | oid = status() 61 | if oid: 62 | return ''' 63 |
64 | OpenID 65 | %s 66 | 67 | 68 | 69 |
''' % (openid_loc, oid, web.ctx.fullpath) 70 | else: 71 | return ''' 72 |
73 | 75 | 76 | 77 |
''' % (openid_loc, web.ctx.fullpath) 78 | 79 | def logout(): 80 | web.setcookie('openid_identity_hash', '', expires=-1) 81 | 82 | class host: 83 | def POST(self): 84 | # unlike the usual scheme of things, the POST is actually called 85 | # first here 86 | i = web.input(return_to='/') 87 | if i.get('action') == 'logout': 88 | logout() 89 | return web.redirect(i.return_to) 90 | 91 | i = web.input('openid', return_to='/') 92 | 93 | n = _random_session() 94 | sessions[n] = {'webpy_return_to': i.return_to} 95 | 96 | c = openid.consumer.consumer.Consumer(sessions[n], store) 97 | a = c.begin(i.openid) 98 | f = a.redirectURL(web.ctx.home, web.ctx.home + web.ctx.fullpath) 99 | 100 | web.setcookie('openid_session_id', n) 101 | return web.redirect(f) 102 | 103 | def GET(self): 104 | n = web.cookies('openid_session_id').openid_session_id 105 | web.setcookie('openid_session_id', '', expires=-1) 106 | return_to = sessions[n]['webpy_return_to'] 107 | 108 | c = openid.consumer.consumer.Consumer(sessions[n], store) 109 | a = c.complete(web.input(), web.ctx.home + web.ctx.fullpath) 110 | 111 | if a.status.lower() == 'success': 112 | web.setcookie('openid_identity_hash', _hmac(a.identity_url) + ',' + a.identity_url) 113 | 114 | del sessions[n] 115 | return web.redirect(return_to) 116 | -------------------------------------------------------------------------------- /web.py-0.37/web/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI Utilities 3 | (from web.py) 4 | """ 5 | 6 | import os, sys 7 | 8 | import http 9 | import webapi as web 10 | from utils import listget 11 | from net import validaddr, validip 12 | import httpserver 13 | 14 | def runfcgi(func, addr=('localhost', 8000)): 15 | """Runs a WSGI function as a FastCGI server.""" 16 | import flup.server.fcgi as flups 17 | return flups.WSGIServer(func, multiplexed=True, bindAddress=addr, debug=False).run() 18 | 19 | def runscgi(func, addr=('localhost', 4000)): 20 | """Runs a WSGI function as an SCGI server.""" 21 | import flup.server.scgi as flups 22 | return flups.WSGIServer(func, bindAddress=addr, debug=False).run() 23 | 24 | def runwsgi(func): 25 | """ 26 | Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server, 27 | as appropriate based on context and `sys.argv`. 28 | """ 29 | 30 | if os.environ.has_key('SERVER_SOFTWARE'): # cgi 31 | os.environ['FCGI_FORCE_CGI'] = 'Y' 32 | 33 | if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi 34 | or os.environ.has_key('SERVER_SOFTWARE')): 35 | return runfcgi(func, None) 36 | 37 | if 'fcgi' in sys.argv or 'fastcgi' in sys.argv: 38 | args = sys.argv[1:] 39 | if 'fastcgi' in args: args.remove('fastcgi') 40 | elif 'fcgi' in args: args.remove('fcgi') 41 | if args: 42 | return runfcgi(func, validaddr(args[0])) 43 | else: 44 | return runfcgi(func, None) 45 | 46 | if 'scgi' in sys.argv: 47 | args = sys.argv[1:] 48 | args.remove('scgi') 49 | if args: 50 | return runscgi(func, validaddr(args[0])) 51 | else: 52 | return runscgi(func) 53 | 54 | return httpserver.runsimple(func, validip(listget(sys.argv, 1, ''))) 55 | 56 | def _is_dev_mode(): 57 | # Some embedded python interpreters won't have sys.arv 58 | # For details, see https://github.com/webpy/webpy/issues/87 59 | argv = getattr(sys, "argv", []) 60 | 61 | # quick hack to check if the program is running in dev mode. 62 | if os.environ.has_key('SERVER_SOFTWARE') \ 63 | or os.environ.has_key('PHP_FCGI_CHILDREN') \ 64 | or 'fcgi' in argv or 'fastcgi' in argv \ 65 | or 'mod_wsgi' in argv: 66 | return False 67 | return True 68 | 69 | # When running the builtin-server, enable debug mode if not already set. 70 | web.config.setdefault('debug', _is_dev_mode()) 71 | -------------------------------------------------------------------------------- /web.py-0.37/web/wsgiserver/ssl_builtin.py: -------------------------------------------------------------------------------- 1 | """A library for integrating Python's builtin ``ssl`` library with CherryPy. 2 | 3 | The ssl module must be importable for SSL functionality. 4 | 5 | To use this module, set ``CherryPyWSGIServer.ssl_adapter`` to an instance of 6 | ``BuiltinSSLAdapter``. 7 | """ 8 | 9 | try: 10 | import ssl 11 | except ImportError: 12 | ssl = None 13 | 14 | from cherrypy import wsgiserver 15 | 16 | 17 | class BuiltinSSLAdapter(wsgiserver.SSLAdapter): 18 | """A wrapper for integrating Python's builtin ssl module with CherryPy.""" 19 | 20 | certificate = None 21 | """The filename of the server SSL certificate.""" 22 | 23 | private_key = None 24 | """The filename of the server's private key file.""" 25 | 26 | def __init__(self, certificate, private_key, certificate_chain=None): 27 | if ssl is None: 28 | raise ImportError("You must install the ssl module to use HTTPS.") 29 | self.certificate = certificate 30 | self.private_key = private_key 31 | self.certificate_chain = certificate_chain 32 | 33 | def bind(self, sock): 34 | """Wrap and return the given socket.""" 35 | return sock 36 | 37 | def wrap(self, sock): 38 | """Wrap and return the given socket, plus WSGI environ entries.""" 39 | try: 40 | s = ssl.wrap_socket(sock, do_handshake_on_connect=True, 41 | server_side=True, certfile=self.certificate, 42 | keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23) 43 | except ssl.SSLError, e: 44 | if e.errno == ssl.SSL_ERROR_EOF: 45 | # This is almost certainly due to the cherrypy engine 46 | # 'pinging' the socket to assert it's connectable; 47 | # the 'ping' isn't SSL. 48 | return None, {} 49 | elif e.errno == ssl.SSL_ERROR_SSL: 50 | if e.args[1].endswith('http request'): 51 | # The client is speaking HTTP to an HTTPS server. 52 | raise wsgiserver.NoSSLError 53 | raise 54 | return s, self.get_environ(s) 55 | 56 | # TODO: fill this out more with mod ssl env 57 | def get_environ(self, sock): 58 | """Create WSGI environ entries to be merged into each request.""" 59 | cipher = sock.cipher() 60 | ssl_environ = { 61 | "wsgi.url_scheme": "https", 62 | "HTTPS": "on", 63 | 'SSL_PROTOCOL': cipher[1], 64 | 'SSL_CIPHER': cipher[0] 65 | ## SSL_VERSION_INTERFACE string The mod_ssl program version 66 | ## SSL_VERSION_LIBRARY string The OpenSSL program version 67 | } 68 | return ssl_environ 69 | 70 | def makefile(self, sock, mode='r', bufsize=-1): 71 | return wsgiserver.CP_fileobject(sock, mode, bufsize) 72 | 73 | -------------------------------------------------------------------------------- /web.py-0.37/web/wsgiserver/ssl_pyopenssl.py: -------------------------------------------------------------------------------- 1 | """A library for integrating pyOpenSSL with CherryPy. 2 | 3 | The OpenSSL module must be importable for SSL functionality. 4 | You can obtain it from http://pyopenssl.sourceforge.net/ 5 | 6 | To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of 7 | SSLAdapter. There are two ways to use SSL: 8 | 9 | Method One 10 | ---------- 11 | 12 | * ``ssl_adapter.context``: an instance of SSL.Context. 13 | 14 | If this is not None, it is assumed to be an SSL.Context instance, 15 | and will be passed to SSL.Connection on bind(). The developer is 16 | responsible for forming a valid Context object. This approach is 17 | to be preferred for more flexibility, e.g. if the cert and key are 18 | streams instead of files, or need decryption, or SSL.SSLv3_METHOD 19 | is desired instead of the default SSL.SSLv23_METHOD, etc. Consult 20 | the pyOpenSSL documentation for complete options. 21 | 22 | Method Two (shortcut) 23 | --------------------- 24 | 25 | * ``ssl_adapter.certificate``: the filename of the server SSL certificate. 26 | * ``ssl_adapter.private_key``: the filename of the server's private key file. 27 | 28 | Both are None by default. If ssl_adapter.context is None, but .private_key 29 | and .certificate are both given and valid, they will be read, and the 30 | context will be automatically created from them. 31 | """ 32 | 33 | import socket 34 | import threading 35 | import time 36 | 37 | from cherrypy import wsgiserver 38 | 39 | try: 40 | from OpenSSL import SSL 41 | from OpenSSL import crypto 42 | except ImportError: 43 | SSL = None 44 | 45 | 46 | class SSL_fileobject(wsgiserver.CP_fileobject): 47 | """SSL file object attached to a socket object.""" 48 | 49 | ssl_timeout = 3 50 | ssl_retry = .01 51 | 52 | def _safe_call(self, is_reader, call, *args, **kwargs): 53 | """Wrap the given call with SSL error-trapping. 54 | 55 | is_reader: if False EOF errors will be raised. If True, EOF errors 56 | will return "" (to emulate normal sockets). 57 | """ 58 | start = time.time() 59 | while True: 60 | try: 61 | return call(*args, **kwargs) 62 | except SSL.WantReadError: 63 | # Sleep and try again. This is dangerous, because it means 64 | # the rest of the stack has no way of differentiating 65 | # between a "new handshake" error and "client dropped". 66 | # Note this isn't an endless loop: there's a timeout below. 67 | time.sleep(self.ssl_retry) 68 | except SSL.WantWriteError: 69 | time.sleep(self.ssl_retry) 70 | except SSL.SysCallError, e: 71 | if is_reader and e.args == (-1, 'Unexpected EOF'): 72 | return "" 73 | 74 | errnum = e.args[0] 75 | if is_reader and errnum in wsgiserver.socket_errors_to_ignore: 76 | return "" 77 | raise socket.error(errnum) 78 | except SSL.Error, e: 79 | if is_reader and e.args == (-1, 'Unexpected EOF'): 80 | return "" 81 | 82 | thirdarg = None 83 | try: 84 | thirdarg = e.args[0][0][2] 85 | except IndexError: 86 | pass 87 | 88 | if thirdarg == 'http request': 89 | # The client is talking HTTP to an HTTPS server. 90 | raise wsgiserver.NoSSLError() 91 | 92 | raise wsgiserver.FatalSSLAlert(*e.args) 93 | except: 94 | raise 95 | 96 | if time.time() - start > self.ssl_timeout: 97 | raise socket.timeout("timed out") 98 | 99 | def recv(self, *args, **kwargs): 100 | buf = [] 101 | r = super(SSL_fileobject, self).recv 102 | while True: 103 | data = self._safe_call(True, r, *args, **kwargs) 104 | buf.append(data) 105 | p = self._sock.pending() 106 | if not p: 107 | return "".join(buf) 108 | 109 | def sendall(self, *args, **kwargs): 110 | return self._safe_call(False, super(SSL_fileobject, self).sendall, 111 | *args, **kwargs) 112 | 113 | def send(self, *args, **kwargs): 114 | return self._safe_call(False, super(SSL_fileobject, self).send, 115 | *args, **kwargs) 116 | 117 | 118 | class SSLConnection: 119 | """A thread-safe wrapper for an SSL.Connection. 120 | 121 | ``*args``: the arguments to create the wrapped ``SSL.Connection(*args)``. 122 | """ 123 | 124 | def __init__(self, *args): 125 | self._ssl_conn = SSL.Connection(*args) 126 | self._lock = threading.RLock() 127 | 128 | for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read', 129 | 'renegotiate', 'bind', 'listen', 'connect', 'accept', 130 | 'setblocking', 'fileno', 'close', 'get_cipher_list', 131 | 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 132 | 'makefile', 'get_app_data', 'set_app_data', 'state_string', 133 | 'sock_shutdown', 'get_peer_certificate', 'want_read', 134 | 'want_write', 'set_connect_state', 'set_accept_state', 135 | 'connect_ex', 'sendall', 'settimeout', 'gettimeout'): 136 | exec("""def %s(self, *args): 137 | self._lock.acquire() 138 | try: 139 | return self._ssl_conn.%s(*args) 140 | finally: 141 | self._lock.release() 142 | """ % (f, f)) 143 | 144 | def shutdown(self, *args): 145 | self._lock.acquire() 146 | try: 147 | # pyOpenSSL.socket.shutdown takes no args 148 | return self._ssl_conn.shutdown() 149 | finally: 150 | self._lock.release() 151 | 152 | 153 | class pyOpenSSLAdapter(wsgiserver.SSLAdapter): 154 | """A wrapper for integrating pyOpenSSL with CherryPy.""" 155 | 156 | context = None 157 | """An instance of SSL.Context.""" 158 | 159 | certificate = None 160 | """The filename of the server SSL certificate.""" 161 | 162 | private_key = None 163 | """The filename of the server's private key file.""" 164 | 165 | certificate_chain = None 166 | """Optional. The filename of CA's intermediate certificate bundle. 167 | 168 | This is needed for cheaper "chained root" SSL certificates, and should be 169 | left as None if not required.""" 170 | 171 | def __init__(self, certificate, private_key, certificate_chain=None): 172 | if SSL is None: 173 | raise ImportError("You must install pyOpenSSL to use HTTPS.") 174 | 175 | self.context = None 176 | self.certificate = certificate 177 | self.private_key = private_key 178 | self.certificate_chain = certificate_chain 179 | self._environ = None 180 | 181 | def bind(self, sock): 182 | """Wrap and return the given socket.""" 183 | if self.context is None: 184 | self.context = self.get_context() 185 | conn = SSLConnection(self.context, sock) 186 | self._environ = self.get_environ() 187 | return conn 188 | 189 | def wrap(self, sock): 190 | """Wrap and return the given socket, plus WSGI environ entries.""" 191 | return sock, self._environ.copy() 192 | 193 | def get_context(self): 194 | """Return an SSL.Context from self attributes.""" 195 | # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473 196 | c = SSL.Context(SSL.SSLv23_METHOD) 197 | c.use_privatekey_file(self.private_key) 198 | if self.certificate_chain: 199 | c.load_verify_locations(self.certificate_chain) 200 | c.use_certificate_file(self.certificate) 201 | return c 202 | 203 | def get_environ(self): 204 | """Return WSGI environ entries to be merged into each request.""" 205 | ssl_environ = { 206 | "HTTPS": "on", 207 | # pyOpenSSL doesn't provide access to any of these AFAICT 208 | ## 'SSL_PROTOCOL': 'SSLv2', 209 | ## SSL_CIPHER string The cipher specification name 210 | ## SSL_VERSION_INTERFACE string The mod_ssl program version 211 | ## SSL_VERSION_LIBRARY string The OpenSSL program version 212 | } 213 | 214 | if self.certificate: 215 | # Server certificate attributes 216 | cert = open(self.certificate, 'rb').read() 217 | cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) 218 | ssl_environ.update({ 219 | 'SSL_SERVER_M_VERSION': cert.get_version(), 220 | 'SSL_SERVER_M_SERIAL': cert.get_serial_number(), 221 | ## 'SSL_SERVER_V_START': Validity of server's certificate (start time), 222 | ## 'SSL_SERVER_V_END': Validity of server's certificate (end time), 223 | }) 224 | 225 | for prefix, dn in [("I", cert.get_issuer()), 226 | ("S", cert.get_subject())]: 227 | # X509Name objects don't seem to have a way to get the 228 | # complete DN string. Use str() and slice it instead, 229 | # because str(dn) == "" 230 | dnstr = str(dn)[18:-2] 231 | 232 | wsgikey = 'SSL_SERVER_%s_DN' % prefix 233 | ssl_environ[wsgikey] = dnstr 234 | 235 | # The DN should be of the form: /k1=v1/k2=v2, but we must allow 236 | # for any value to contain slashes itself (in a URL). 237 | while dnstr: 238 | pos = dnstr.rfind("=") 239 | dnstr, value = dnstr[:pos], dnstr[pos + 1:] 240 | pos = dnstr.rfind("/") 241 | dnstr, key = dnstr[:pos], dnstr[pos + 1:] 242 | if key and value: 243 | wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) 244 | ssl_environ[wsgikey] = value 245 | 246 | return ssl_environ 247 | 248 | def makefile(self, sock, mode='r', bufsize=-1): 249 | if SSL and isinstance(sock, SSL.ConnectionType): 250 | timeout = sock.gettimeout() 251 | f = SSL_fileobject(sock, mode, bufsize) 252 | f.ssl_timeout = timeout 253 | return f 254 | else: 255 | return wsgiserver.CP_fileobject(sock, mode, bufsize) 256 | 257 | --------------------------------------------------------------------------------