emailsec._utils

 1import re
 2import collections
 3
 4Body = bytes
 5Header = tuple[bytes, bytes]
 6Headers = dict[str, list[Header]]
 7
 8type BodyAndHeaders = tuple[Body, Headers]
 9
10
11def body_and_headers_for_canonicalization(message: bytes) -> BodyAndHeaders:
12    lines = re.split(b"\r?\n", message)
13
14    headers_idx = collections.defaultdict(list)
15    headers = []
16    for header_line in lines[: lines.index(b"")]:
17        if (m := re.match(rb"([\x21-\x7e]+?):", header_line)) is not None:
18            header_name = m.group(1)
19            header_value = header_line[m.end() :] + b"\r\n"
20            headers.append([header_name, header_value])
21        elif header_line.startswith(b" ") or header_line.startswith(b"\t"):
22            # Unfold header values
23            headers[-1][1] += header_line + b"\r\n"
24        else:
25            raise ValueError(f"Invalid line {header_line!r}")
26
27    for header_name, header_value in headers:
28        headers_idx[header_name.decode().lower()].append((header_name, header_value))
29
30    try:
31        # Split on the first empty line and join the remaining ones with CRLF
32        can_body = b"\r\n".join(lines[lines.index(b"") + 1 :])
33    except ValueError:
34        # No body defaults to CRLF
35        can_body = b"\r\n"
36
37    return can_body, dict(headers_idx)
38
39
40def header_value(headers: Headers, header_name: str) -> str:
41    """Returns the first value matching the header name (case-insensitive lookup)."""
42    return headers[header_name][0][1].decode().strip()
Body = <class 'bytes'>
Headers = dict[str, list[tuple[bytes, bytes]]]
type BodyAndHeaders = tuple[bytes, dict[str, list[tuple[bytes, bytes]]]]
def body_and_headers_for_canonicalization(message: bytes) -> BodyAndHeaders:
12def body_and_headers_for_canonicalization(message: bytes) -> BodyAndHeaders:
13    lines = re.split(b"\r?\n", message)
14
15    headers_idx = collections.defaultdict(list)
16    headers = []
17    for header_line in lines[: lines.index(b"")]:
18        if (m := re.match(rb"([\x21-\x7e]+?):", header_line)) is not None:
19            header_name = m.group(1)
20            header_value = header_line[m.end() :] + b"\r\n"
21            headers.append([header_name, header_value])
22        elif header_line.startswith(b" ") or header_line.startswith(b"\t"):
23            # Unfold header values
24            headers[-1][1] += header_line + b"\r\n"
25        else:
26            raise ValueError(f"Invalid line {header_line!r}")
27
28    for header_name, header_value in headers:
29        headers_idx[header_name.decode().lower()].append((header_name, header_value))
30
31    try:
32        # Split on the first empty line and join the remaining ones with CRLF
33        can_body = b"\r\n".join(lines[lines.index(b"") + 1 :])
34    except ValueError:
35        # No body defaults to CRLF
36        can_body = b"\r\n"
37
38    return can_body, dict(headers_idx)
def header_value(headers: dict[str, list[tuple[bytes, bytes]]], header_name: str) -> str:
41def header_value(headers: Headers, header_name: str) -> str:
42    """Returns the first value matching the header name (case-insensitive lookup)."""
43    return headers[header_name][0][1].decode().strip()

Returns the first value matching the header name (case-insensitive lookup).