• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import warnings
2
3from webob.compat import (
4    escape,
5    string_types,
6    text_,
7    text_type,
8    )
9
10from webob.headers import _trans_key
11
12def html_escape(s):
13    """HTML-escape a string or object
14
15    This converts any non-string objects passed into it to strings
16    (actually, using ``unicode()``).  All values returned are
17    non-unicode strings (using ``&#num;`` entities for all non-ASCII
18    characters).
19
20    None is treated specially, and returns the empty string.
21    """
22    if s is None:
23        return ''
24    __html__ = getattr(s, '__html__', None)
25    if __html__ is not None and callable(__html__):
26        return s.__html__()
27    if not isinstance(s, string_types):
28        __unicode__ = getattr(s, '__unicode__', None)
29        if __unicode__ is not None and callable(__unicode__):
30            s = s.__unicode__()
31        else:
32            s = str(s)
33    s = escape(s, True)
34    if isinstance(s, text_type):
35        s = s.encode('ascii', 'xmlcharrefreplace')
36    return text_(s)
37
38def header_docstring(header, rfc_section):
39    if header.isupper():
40        header = _trans_key(header)
41    major_section = rfc_section.split('.')[0]
42    link = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec%s.html#sec%s' % (
43        major_section, rfc_section)
44    return "Gets and sets the ``%s`` header (`HTTP spec section %s <%s>`_)." % (
45        header, rfc_section, link)
46
47
48def warn_deprecation(text, version, stacklevel):
49    # version specifies when to start raising exceptions instead of warnings
50    if version in ('1.2', '1.3', '1.4'):
51        raise DeprecationWarning(text)
52    else:
53        cls = DeprecationWarning
54    warnings.warn(text, cls, stacklevel=stacklevel+1)
55
56status_reasons = {
57    # Status Codes
58    # Informational
59    100: 'Continue',
60    101: 'Switching Protocols',
61    102: 'Processing',
62
63    # Successful
64    200: 'OK',
65    201: 'Created',
66    202: 'Accepted',
67    203: 'Non-Authoritative Information',
68    204: 'No Content',
69    205: 'Reset Content',
70    206: 'Partial Content',
71    207: 'Multi Status',
72    226: 'IM Used',
73
74    # Redirection
75    300: 'Multiple Choices',
76    301: 'Moved Permanently',
77    302: 'Found',
78    303: 'See Other',
79    304: 'Not Modified',
80    305: 'Use Proxy',
81    307: 'Temporary Redirect',
82
83    # Client Error
84    400: 'Bad Request',
85    401: 'Unauthorized',
86    402: 'Payment Required',
87    403: 'Forbidden',
88    404: 'Not Found',
89    405: 'Method Not Allowed',
90    406: 'Not Acceptable',
91    407: 'Proxy Authentication Required',
92    408: 'Request Timeout',
93    409: 'Conflict',
94    410: 'Gone',
95    411: 'Length Required',
96    412: 'Precondition Failed',
97    413: 'Request Entity Too Large',
98    414: 'Request URI Too Long',
99    415: 'Unsupported Media Type',
100    416: 'Requested Range Not Satisfiable',
101    417: 'Expectation Failed',
102    418: "I'm a teapot",
103    422: 'Unprocessable Entity',
104    423: 'Locked',
105    424: 'Failed Dependency',
106    426: 'Upgrade Required',
107    428: 'Precondition Required',
108    429: 'Too Many Requests',
109    451: 'Unavailable for Legal Reasons',
110    431: 'Request Header Fields Too Large',
111
112    # Server Error
113    500: 'Internal Server Error',
114    501: 'Not Implemented',
115    502: 'Bad Gateway',
116    503: 'Service Unavailable',
117    504: 'Gateway Timeout',
118    505: 'HTTP Version Not Supported',
119    507: 'Insufficient Storage',
120    510: 'Not Extended',
121    511: 'Network Authentication Required',
122}
123
124# generic class responses as per RFC2616
125status_generic_reasons = {
126    1: 'Continue',
127    2: 'Success',
128    3: 'Multiple Choices',
129    4: 'Unknown Client Error',
130    5: 'Unknown Server Error',
131}
132
133try:
134    # py3.3+ have native comparison support
135    from hmac import compare_digest
136except ImportError: # pragma: nocover (Python 2.7.7 backported this)
137    compare_digest = None
138
139def strings_differ(string1, string2, compare_digest=compare_digest):
140    """Check whether two strings differ while avoiding timing attacks.
141
142    This function returns True if the given strings differ and False
143    if they are equal.  It's careful not to leak information about *where*
144    they differ as a result of its running time, which can be very important
145    to avoid certain timing-related crypto attacks:
146
147        http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf
148
149    .. versionchanged:: 1.5
150       Support :func:`hmac.compare_digest` if it is available (Python 2.7.7+
151       and Python 3.3+).
152
153    """
154    len_eq = len(string1) == len(string2)
155    if len_eq:
156        invalid_bits = 0
157        left = string1
158    else:
159        invalid_bits = 1
160        left = string2
161    right = string2
162
163    if compare_digest is not None:
164        invalid_bits += not compare_digest(left, right)
165    else:
166        for a, b in zip(left, right):
167            invalid_bits += a != b
168    return invalid_bits != 0
169
170