summaryrefslogtreecommitdiff
path: root/ememo_common.py
blob: d5ebf63e6a0778b8067d2597d56c62458c757785 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# There is no shebang because this is not to be run as a program.
#
# Electronic memo protocol utility functions.
#
# Written by Andrew Yu <https://www.andrewyu.org/>.
#
# This work is in the public domain.  See the NOCOPYRIGHT file for
# details.
#

import ememo_errors
import re

re_named_address = re.compile(b"(.*) <(.*)\\@(.*)>")
re_unnamed_address = re.compile(b"^[<](.*)\\@(.*)[>]$")


def parse_address(s):  # Returns name, user, host.
    attempt_named_address = re_named_address.match(s)
    if attempt_named_address:
        return (
            attempt_named_address.group(1),
            attempt_named_address.group(2),
            attempt_named_address.group(3),
        )

    attempt_unnamed_address = re_unnamed_address.match(s)
    if attempt_unnamed_address:
        return (
            None,
            attempt_unnamed_address.group(1),
            attempt_unnamed_address.group(2),
        )

    return None  # No results, invalid address.


def parse_header_line(line):
    if b": " not in line:
        raise ememo_errors.E_HEADER_FORMAT
    return line.split(b": ", 1)


def assemble_headers(headers, use_carriage_returns_too=False):
    buffer = b""
    for k, v in headers.items():
        buffer += k + b": " + v + b"\n"
    buffer += b"\n"
    return buffer


def parse_header_and_leave_rest(mail_buffer):
    header_lines = []
    buffer = b""
    last_char = 0

    last_char = b""
    i = 0
    while i < len(mail_buffer):
        char = mail_buffer[i].to_bytes(1, "big")
        if char == b"\n" and last_char != b"\n":
            header_lines.append(buffer)
            buffer = b""
        elif char == b"\n" and last_char == b"\n":
            buffer = b""
            i += 1
            break
        elif char != b"\r":
            buffer += char
        last_char = char
        i += 1

    buffer = mail_buffer[i:]

    headers = {}
    for header_line in header_lines:
        key, value = parse_header_line(header_line)
        headers[key] = value

    return headers, buffer


def extract_attachments(buffer):
    attachments = {}
    current_size_specifier_buffer = b""
    current_filename_buffer = b""
    current_data_buffer = b""
    reading_mode = 0  # 0size, 1filename, 2data
    i = 0
    while i < len(buffer):
        char = buffer[i].to_bytes(1, "big")
        if reading_mode == 0 and char not in [b" ", b"\n"]:
            current_size_specifier_buffer += char
        elif reading_mode == 1 and char != b"\n":
            current_filename_buffer += char
        elif reading_mode == 0 and char == b" ":
            reading_mode = 1
        elif reading_mode == 0 and char == b"\n" and not current_size_specifier_buffer:
            pass
        elif reading_mode == 0 and char == b"\n" and current_size_specifier_buffer:
            if b"" in attachments.keys():
                raise ememo_errors.E_MULTIPLE_UNNAMED_ATTACHMENTS
            if current_size_specifier_buffer[-1] == b"\r":
                current_size_specifier_buffer = current_size_specifier_buffer[0:-1]
            try:
                current_size_specifier = int(current_size_specifier_buffer)
            except ValueError:
                raise ememo_errors.E_SIZE_NOT_NUMBER
            reading_mode = 2
            current_data_buffer += buffer[i + 1 : i + 1 + current_size_specifier]
            i += current_size_specifier
            attachments[b""] = current_data_buffer
            current_filename_buffer = b""
            current_data_buffer = b""
            current_size_specifier_buffer = b""
            reading_mode = 0
        elif reading_mode == 1 and char == b"\n":
            if current_filename_buffer[-1] == b"\r":
                current_filename_buffer = current_filename_buffer[0:-1]
            try:
                current_size_specifier = int(current_size_specifier_buffer)
            except ValueError:
                raise ememo_errors.E_SIZE_NOT_NUMBER
            reading_mode = 2
            print(buffer, i + 1, i + 1 + current_size_specifier)
            current_data_buffer += buffer[i + 1 : i + 1 + current_size_specifier]
            i += current_size_specifier
            attachments[current_filename_buffer] = current_data_buffer
            current_filename_buffer = b""
            current_data_buffer = b""
            current_size_specifier_buffer = b""
            reading_mode = 0
        else:
            raise ememo_errors.ServerFatal(
                "reading mode shouldn't be 2 when char-scanning"
            )
        i += 1

    return headers, attachments