_internal.py 14 KB

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