_internal.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. # -*- coding: utf-8 -*-
  2. """
  3. werkzeug._internal
  4. ~~~~~~~~~~~~~~~~~~
  5. This module provides internally used helpers and constants.
  6. :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import re
  10. import string
  11. import inspect
  12. from weakref import WeakKeyDictionary
  13. from datetime import datetime, date
  14. from itertools import chain
  15. from werkzeug._compat import iter_bytes, text_type, BytesIO, int_to_byte, \
  16. range_type, integer_types
  17. _logger = None
  18. _empty_stream = BytesIO()
  19. _signature_cache = WeakKeyDictionary()
  20. _epoch_ord = date(1970, 1, 1).toordinal()
  21. _cookie_params = set((b'expires', b'path', b'comment',
  22. b'max-age', b'secure', b'httponly',
  23. b'version'))
  24. _legal_cookie_chars = (string.ascii_letters +
  25. string.digits +
  26. u"!#$%&'*+-.^_`|~:").encode('ascii')
  27. _cookie_quoting_map = {
  28. b',': b'\\054',
  29. b';': b'\\073',
  30. b'"': b'\\"',
  31. b'\\': b'\\\\',
  32. }
  33. for _i in chain(range_type(32), range_type(127, 256)):
  34. _cookie_quoting_map[int_to_byte(_i)] = ('\\%03o' % _i).encode('latin1')
  35. _octal_re = re.compile(b'\\\\[0-3][0-7][0-7]')
  36. _quote_re = re.compile(b'[\\\\].')
  37. _legal_cookie_chars_re = b'[\w\d!#%&\'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]'
  38. _cookie_re = re.compile(b"""(?x)
  39. (?P<key>[^=]+)
  40. \s*=\s*
  41. (?P<val>
  42. "(?:[^\\\\"]|\\\\.)*" |
  43. (?:.*?)
  44. )
  45. \s*;
  46. """)
  47. class _Missing(object):
  48. def __repr__(self):
  49. return 'no value'
  50. def __reduce__(self):
  51. return '_missing'
  52. _missing = _Missing()
  53. def _get_environ(obj):
  54. env = getattr(obj, 'environ', obj)
  55. assert isinstance(env, dict), \
  56. '%r is not a WSGI environment (has to be a dict)' % type(obj).__name__
  57. return env
  58. def _log(type, message, *args, **kwargs):
  59. """Log into the internal werkzeug logger."""
  60. global _logger
  61. if _logger is None:
  62. import logging
  63. _logger = logging.getLogger('werkzeug')
  64. # Only set up a default log handler if the
  65. # end-user application didn't set anything up.
  66. if not logging.root.handlers and _logger.level == logging.NOTSET:
  67. _logger.setLevel(logging.INFO)
  68. handler = logging.StreamHandler()
  69. _logger.addHandler(handler)
  70. getattr(_logger, type)(message.rstrip(), *args, **kwargs)
  71. def _parse_signature(func):
  72. """Return a signature object for the function."""
  73. if hasattr(func, 'im_func'):
  74. func = func.im_func
  75. # if we have a cached validator for this function, return it
  76. parse = _signature_cache.get(func)
  77. if parse is not None:
  78. return parse
  79. # inspect the function signature and collect all the information
  80. positional, vararg_var, kwarg_var, defaults = inspect.getargspec(func)
  81. defaults = defaults or ()
  82. arg_count = len(positional)
  83. arguments = []
  84. for idx, name in enumerate(positional):
  85. if isinstance(name, list):
  86. raise TypeError('cannot parse functions that unpack tuples '
  87. 'in the function signature')
  88. try:
  89. default = defaults[idx - arg_count]
  90. except IndexError:
  91. param = (name, False, None)
  92. else:
  93. param = (name, True, default)
  94. arguments.append(param)
  95. arguments = tuple(arguments)
  96. def parse(args, kwargs):
  97. new_args = []
  98. missing = []
  99. extra = {}
  100. # consume as many arguments as positional as possible
  101. for idx, (name, has_default, default) in enumerate(arguments):
  102. try:
  103. new_args.append(args[idx])
  104. except IndexError:
  105. try:
  106. new_args.append(kwargs.pop(name))
  107. except KeyError:
  108. if has_default:
  109. new_args.append(default)
  110. else:
  111. missing.append(name)
  112. else:
  113. if name in kwargs:
  114. extra[name] = kwargs.pop(name)
  115. # handle extra arguments
  116. extra_positional = args[arg_count:]
  117. if vararg_var is not None:
  118. new_args.extend(extra_positional)
  119. extra_positional = ()
  120. if kwargs and kwarg_var is None:
  121. extra.update(kwargs)
  122. kwargs = {}
  123. return new_args, kwargs, missing, extra, extra_positional, \
  124. arguments, vararg_var, kwarg_var
  125. _signature_cache[func] = parse
  126. return parse
  127. def _date_to_unix(arg):
  128. """Converts a timetuple, integer or datetime object into the seconds from
  129. epoch in utc.
  130. """
  131. if isinstance(arg, datetime):
  132. arg = arg.utctimetuple()
  133. elif isinstance(arg, integer_types + (float,)):
  134. return int(arg)
  135. year, month, day, hour, minute, second = arg[:6]
  136. days = date(year, month, 1).toordinal() - _epoch_ord + day - 1
  137. hours = days * 24 + hour
  138. minutes = hours * 60 + minute
  139. seconds = minutes * 60 + second
  140. return seconds
  141. class _DictAccessorProperty(object):
  142. """Baseclass for `environ_property` and `header_property`."""
  143. read_only = False
  144. def __init__(self, name, default=None, load_func=None, dump_func=None,
  145. read_only=None, doc=None):
  146. self.name = name
  147. self.default = default
  148. self.load_func = load_func
  149. self.dump_func = dump_func
  150. if read_only is not None:
  151. self.read_only = read_only
  152. self.__doc__ = doc
  153. def __get__(self, obj, type=None):
  154. if obj is None:
  155. return self
  156. storage = self.lookup(obj)
  157. if self.name not in storage:
  158. return self.default
  159. rv = storage[self.name]
  160. if self.load_func is not None:
  161. try:
  162. rv = self.load_func(rv)
  163. except (ValueError, TypeError):
  164. rv = self.default
  165. return rv
  166. def __set__(self, obj, value):
  167. if self.read_only:
  168. raise AttributeError('read only property')
  169. if self.dump_func is not None:
  170. value = self.dump_func(value)
  171. self.lookup(obj)[self.name] = value
  172. def __delete__(self, obj):
  173. if self.read_only:
  174. raise AttributeError('read only property')
  175. self.lookup(obj).pop(self.name, None)
  176. def __repr__(self):
  177. return '<%s %s>' % (
  178. self.__class__.__name__,
  179. self.name
  180. )
  181. def _cookie_quote(b):
  182. buf = bytearray()
  183. all_legal = True
  184. _lookup = _cookie_quoting_map.get
  185. _push = buf.extend
  186. for char in iter_bytes(b):
  187. if char not in _legal_cookie_chars:
  188. all_legal = False
  189. char = _lookup(char, char)
  190. _push(char)
  191. if all_legal:
  192. return bytes(buf)
  193. return bytes(b'"' + buf + b'"')
  194. def _cookie_unquote(b):
  195. if len(b) < 2:
  196. return b
  197. if b[:1] != b'"' or b[-1:] != b'"':
  198. return b
  199. b = b[1:-1]
  200. i = 0
  201. n = len(b)
  202. rv = bytearray()
  203. _push = rv.extend
  204. while 0 <= i < n:
  205. o_match = _octal_re.search(b, i)
  206. q_match = _quote_re.search(b, i)
  207. if not o_match and not q_match:
  208. rv.extend(b[i:])
  209. break
  210. j = k = -1
  211. if o_match:
  212. j = o_match.start(0)
  213. if q_match:
  214. k = q_match.start(0)
  215. if q_match and (not o_match or k < j):
  216. _push(b[i:k])
  217. _push(b[k + 1:k + 2])
  218. i = k + 2
  219. else:
  220. _push(b[i:j])
  221. rv.append(int(b[j + 1:j + 4], 8))
  222. i = j + 4
  223. return bytes(rv)
  224. def _cookie_parse_impl(b):
  225. """Lowlevel cookie parsing facility that operates on bytes."""
  226. i = 0
  227. n = len(b)
  228. while i < n:
  229. match = _cookie_re.search(b + b';', i)
  230. if not match:
  231. break
  232. key = match.group('key').strip()
  233. value = match.group('val')
  234. i = match.end(0)
  235. # Ignore parameters. We have no interest in them.
  236. if key.lower() not in _cookie_params:
  237. yield _cookie_unquote(key), _cookie_unquote(value)
  238. def _encode_idna(domain):
  239. # If we're given bytes, make sure they fit into ASCII
  240. if not isinstance(domain, text_type):
  241. domain.decode('ascii')
  242. return domain
  243. # Otherwise check if it's already ascii, then return
  244. try:
  245. return domain.encode('ascii')
  246. except UnicodeError:
  247. pass
  248. # Otherwise encode each part separately
  249. parts = domain.split('.')
  250. for idx, part in enumerate(parts):
  251. parts[idx] = part.encode('idna')
  252. return b'.'.join(parts)
  253. def _decode_idna(domain):
  254. # If the input is a string try to encode it to ascii to
  255. # do the idna decoding. if that fails because of an
  256. # unicode error, then we already have a decoded idna domain
  257. if isinstance(domain, text_type):
  258. try:
  259. domain = domain.encode('ascii')
  260. except UnicodeError:
  261. return domain
  262. # Decode each part separately. If a part fails, try to
  263. # decode it with ascii and silently ignore errors. This makes
  264. # most sense because the idna codec does not have error handling
  265. parts = domain.split(b'.')
  266. for idx, part in enumerate(parts):
  267. try:
  268. parts[idx] = part.decode('idna')
  269. except UnicodeError:
  270. parts[idx] = part.decode('ascii', 'ignore')
  271. return '.'.join(parts)
  272. def _make_cookie_domain(domain):
  273. if domain is None:
  274. return None
  275. domain = _encode_idna(domain)
  276. if b':' in domain:
  277. domain = domain.split(b':', 1)[0]
  278. if b'.' in domain:
  279. return domain
  280. raise ValueError(
  281. 'Setting \'domain\' for a cookie on a server running locally (ex: '
  282. 'localhost) is not supported by complying browsers. You should '
  283. 'have something like: \'127.0.0.1 localhost dev.localhost\' on '
  284. 'your hosts file and then point your server to run on '
  285. '\'dev.localhost\' and also set \'domain\' for \'dev.localhost\''
  286. )
  287. def _easteregg(app=None):
  288. """Like the name says. But who knows how it works?"""
  289. def bzzzzzzz(gyver):
  290. import base64
  291. import zlib
  292. return zlib.decompress(base64.b64decode(gyver)).decode('ascii')
  293. gyver = u'\n'.join([x + (77 - len(x)) * u' ' for x in bzzzzzzz(b'''
  294. eJyFlzuOJDkMRP06xRjymKgDJCDQStBYT8BCgK4gTwfQ2fcFs2a2FzvZk+hvlcRvRJD148efHt9m
  295. 9Xz94dRY5hGt1nrYcXx7us9qlcP9HHNh28rz8dZj+q4rynVFFPdlY4zH873NKCexrDM6zxxRymzz
  296. 4QIxzK4bth1PV7+uHn6WXZ5C4ka/+prFzx3zWLMHAVZb8RRUxtFXI5DTQ2n3Hi2sNI+HK43AOWSY
  297. jmEzE4naFp58PdzhPMdslLVWHTGUVpSxImw+pS/D+JhzLfdS1j7PzUMxij+mc2U0I9zcbZ/HcZxc
  298. q1QjvvcThMYFnp93agEx392ZdLJWXbi/Ca4Oivl4h/Y1ErEqP+lrg7Xa4qnUKu5UE9UUA4xeqLJ5
  299. jWlPKJvR2yhRI7xFPdzPuc6adXu6ovwXwRPXXnZHxlPtkSkqWHilsOrGrvcVWXgGP3daXomCj317
  300. 8P2UOw/NnA0OOikZyFf3zZ76eN9QXNwYdD8f8/LdBRFg0BO3bB+Pe/+G8er8tDJv83XTkj7WeMBJ
  301. v/rnAfdO51d6sFglfi8U7zbnr0u9tyJHhFZNXYfH8Iafv2Oa+DT6l8u9UYlajV/hcEgk1x8E8L/r
  302. XJXl2SK+GJCxtnyhVKv6GFCEB1OO3f9YWAIEbwcRWv/6RPpsEzOkXURMN37J0PoCSYeBnJQd9Giu
  303. LxYQJNlYPSo/iTQwgaihbART7Fcyem2tTSCcwNCs85MOOpJtXhXDe0E7zgZJkcxWTar/zEjdIVCk
  304. iXy87FW6j5aGZhttDBoAZ3vnmlkx4q4mMmCdLtnHkBXFMCReqthSGkQ+MDXLLCpXwBs0t+sIhsDI
  305. tjBB8MwqYQpLygZ56rRHHpw+OAVyGgaGRHWy2QfXez+ZQQTTBkmRXdV/A9LwH6XGZpEAZU8rs4pE
  306. 1R4FQ3Uwt8RKEtRc0/CrANUoes3EzM6WYcFyskGZ6UTHJWenBDS7h163Eo2bpzqxNE9aVgEM2CqI
  307. GAJe9Yra4P5qKmta27VjzYdR04Vc7KHeY4vs61C0nbywFmcSXYjzBHdiEjraS7PGG2jHHTpJUMxN
  308. Jlxr3pUuFvlBWLJGE3GcA1/1xxLcHmlO+LAXbhrXah1tD6Ze+uqFGdZa5FM+3eHcKNaEarutAQ0A
  309. QMAZHV+ve6LxAwWnXbbSXEG2DmCX5ijeLCKj5lhVFBrMm+ryOttCAeFpUdZyQLAQkA06RLs56rzG
  310. 8MID55vqr/g64Qr/wqwlE0TVxgoiZhHrbY2h1iuuyUVg1nlkpDrQ7Vm1xIkI5XRKLedN9EjzVchu
  311. jQhXcVkjVdgP2O99QShpdvXWoSwkp5uMwyjt3jiWCqWGSiaaPAzohjPanXVLbM3x0dNskJsaCEyz
  312. DTKIs+7WKJD4ZcJGfMhLFBf6hlbnNkLEePF8Cx2o2kwmYF4+MzAxa6i+6xIQkswOqGO+3x9NaZX8
  313. MrZRaFZpLeVTYI9F/djY6DDVVs340nZGmwrDqTCiiqD5luj3OzwpmQCiQhdRYowUYEA3i1WWGwL4
  314. GCtSoO4XbIPFeKGU13XPkDf5IdimLpAvi2kVDVQbzOOa4KAXMFlpi/hV8F6IDe0Y2reg3PuNKT3i
  315. RYhZqtkQZqSB2Qm0SGtjAw7RDwaM1roESC8HWiPxkoOy0lLTRFG39kvbLZbU9gFKFRvixDZBJmpi
  316. Xyq3RE5lW00EJjaqwp/v3EByMSpVZYsEIJ4APaHmVtpGSieV5CALOtNUAzTBiw81GLgC0quyzf6c
  317. NlWknzJeCsJ5fup2R4d8CYGN77mu5vnO1UqbfElZ9E6cR6zbHjgsr9ly18fXjZoPeDjPuzlWbFwS
  318. pdvPkhntFvkc13qb9094LL5NrA3NIq3r9eNnop9DizWOqCEbyRBFJTHn6Tt3CG1o8a4HevYh0XiJ
  319. sR0AVVHuGuMOIfbuQ/OKBkGRC6NJ4u7sbPX8bG/n5sNIOQ6/Y/BX3IwRlTSabtZpYLB85lYtkkgm
  320. p1qXK3Du2mnr5INXmT/78KI12n11EFBkJHHp0wJyLe9MvPNUGYsf+170maayRoy2lURGHAIapSpQ
  321. krEDuNoJCHNlZYhKpvw4mspVWxqo415n8cD62N9+EfHrAvqQnINStetek7RY2Urv8nxsnGaZfRr/
  322. nhXbJ6m/yl1LzYqscDZA9QHLNbdaSTTr+kFg3bC0iYbX/eQy0Bv3h4B50/SGYzKAXkCeOLI3bcAt
  323. mj2Z/FM1vQWgDynsRwNvrWnJHlespkrp8+vO1jNaibm+PhqXPPv30YwDZ6jApe3wUjFQobghvW9p
  324. 7f2zLkGNv8b191cD/3vs9Q833z8t''').splitlines()])
  325. def easteregged(environ, start_response):
  326. def injecting_start_response(status, headers, exc_info=None):
  327. headers.append(('X-Powered-By', 'Werkzeug'))
  328. return start_response(status, headers, exc_info)
  329. if app is not None and environ.get('QUERY_STRING') != 'macgybarchakku':
  330. return app(environ, injecting_start_response)
  331. injecting_start_response('200 OK', [('Content-Type', 'text/html')])
  332. return [(u'''
  333. <!DOCTYPE html>
  334. <html>
  335. <head>
  336. <title>About Werkzeug</title>
  337. <style type="text/css">
  338. body { font: 15px Georgia, serif; text-align: center; }
  339. a { color: #333; text-decoration: none; }
  340. h1 { font-size: 30px; margin: 20px 0 10px 0; }
  341. p { margin: 0 0 30px 0; }
  342. pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; }
  343. </style>
  344. </head>
  345. <body>
  346. <h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1>
  347. <p>the Swiss Army knife of Python web development.</p>
  348. <pre>%s\n\n\n</pre>
  349. </body>
  350. </html>''' % gyver).encode('latin1')]
  351. return easteregged