tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cookie_helper.py (3266B)


      1 # -*- coding: utf-8 -*-
      2 
      3 # Active wptserve handler for cookie operations.
      4 #
      5 # This must support the following requests:
      6 #
      7 # - GET with the following query parameters:
      8 #   - charset: (optional) character set for response (default: utf-8)
      9 #   A cookie: request header (if present) is echoed in the body with a
     10 #   cookie= prefix followed by the urlencoded bytes from the header.
     11 #   Used to inspect the cookie jar from an HTTP request header context.
     12 # - POST with form-data in the body and the following query-or-form parameters:
     13 #   - set-cookie: (optional; repeated) echoed in the set-cookie: response
     14 #     header and also echoed in the body with a set-cookie= prefix
     15 #     followed by the urlencoded bytes from the parameter; multiple occurrences
     16 #     are CRLF-delimited.
     17 #   Used to set cookies from an HTTP response header context.
     18 #
     19 # The response has 200 status and content-type: text/plain; charset=<charset>
     20 import encodings, re
     21 
     22 from urllib.parse import parse_qs, quote
     23 
     24 from wptserve.utils import isomorphic_decode, isomorphic_encode
     25 
     26 # NOTE: These are intentionally very lax to permit testing
     27 DISALLOWED_IN_COOKIE_NAME_RE = re.compile(br'[;\0-\x1f\x7f]')
     28 DISALLOWED_IN_HEADER_RE = re.compile(br'[\0-\x1f\x7f]')
     29 
     30 # Ensure common charset names do not end up with different
     31 # capitalization or punctuation
     32 CHARSET_OVERRIDES = {
     33    encodings.codecs.lookup(charset).name: charset
     34    for charset in (u'utf-8', u'iso-8859-1',)
     35 }
     36 
     37 def quote_str(cookie_str):
     38  return isomorphic_encode(quote(isomorphic_decode(cookie_str), u'', encoding=u'iso-8859-1'))
     39 
     40 def parse_qs_str(query_str):
     41  args = parse_qs(isomorphic_decode(query_str), keep_blank_values=True, encoding=u'iso-8859-1')
     42  binary_args = {}
     43  for key, val in args.items():
     44    binary_args[isomorphic_encode(key)] = [isomorphic_encode(x) for x in val]
     45  return binary_args
     46 
     47 def main(request, response):
     48  assert request.method in (
     49      u'GET',
     50      u'POST',
     51  ), u'request method was neither GET nor POST: %r' % request.method
     52  qd = (isomorphic_encode(request.url).split(b'#')[0].split(b'?', 1) + [b''])[1]
     53  if request.method == u'POST':
     54    qd += b'&' + request.body
     55  args = parse_qs_str(qd)
     56 
     57  charset = encodings.codecs.lookup([isomorphic_decode(x) for x in args.get(b'charset', [u'utf-8'])][-1]).name
     58  charset = CHARSET_OVERRIDES.get(charset, charset)
     59  headers = [(b'content-type', b'text/plain; charset=' + isomorphic_encode(charset))]
     60  body = []
     61  if request.method == u'POST':
     62    for set_cookie in args.get(b'set-cookie', []):
     63      if b'=' in set_cookie.split(b';', 1)[0]:
     64        name, rest = set_cookie.split(b'=', 1)
     65        assert re.search(
     66            DISALLOWED_IN_COOKIE_NAME_RE,
     67            name
     68        ) is None, b'name had disallowed characters: %r' % name
     69      else:
     70        rest = set_cookie
     71      assert re.search(
     72          DISALLOWED_IN_HEADER_RE,
     73          rest
     74      ) is None, b'rest had disallowed characters: %r' % rest
     75      headers.append((b'set-cookie', set_cookie))
     76      body.append(b'set-cookie=' + quote_str(set_cookie))
     77 
     78  else:
     79    cookie = request.headers.get(b'cookie')
     80    if cookie is not None:
     81      body.append(b'cookie=' + quote_str(cookie))
     82  body = b'\r\n'.join(body)
     83  headers.append((b'content-length', len(body)))
     84  return 200, headers, body