File: statuscodes.py

package info (click to toggle)
python-aiosmtpd 1.4.3-1.1%2Bdeb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,060 kB
  • sloc: python: 7,850; makefile: 158; sh: 5
file content (188 lines) | stat: -rw-r--r-- 7,437 bytes parent folder | download | duplicates (2)
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# Copyright 2014-2021 The aiosmtpd Developers
# SPDX-License-Identifier: Apache-2.0

import socket
from typing import Iterable, NamedTuple


class StatusCode(NamedTuple):
    code: int
    mesg: bytes

    def __call__(self, *args: bytes) -> "StatusCode":
        nmsg = self.mesg % args
        return StatusCode(self.code, nmsg)

    def to_bytes(self, crlf: bool = False) -> bytes:
        """
        Returns code + mesg as bytes.
        WARNING: This is NOT identical to __str()__.encode()!
        """
        _crlf = b"\r\n" if crlf else b""
        return str(self.code).encode() + b" " + self.mesg + _crlf

    def to_str(self, crlf: bool = False) -> str:
        """
        Returns code + mesg as a string.
        WARNING: This is NOT identical to __str__()!
        """
        _crlf = "\r\n" if crlf else ""
        return str(self.code) + " " + self.mesg.decode() + _crlf


_COMMON_COMMANDS = [
    b"AUTH",
    b"DATA",
    b"HELP",
    b"MAIL",
    b"NOOP",
    b"QUIT",
    b"RCPT",
    b"RSET",
    b"VRFY",
]

SUPPORTED_COMMANDS_NOTLS = _COMMON_COMMANDS + [b"EHLO", b"HELO"]
SUPPORTED_COMMANDS_NOTLS.sort()

SUPPORTED_COMMANDS_TLS = SUPPORTED_COMMANDS_NOTLS + [b"STARTTLS"]
SUPPORTED_COMMANDS_TLS.sort()

SUPPORTED_COMMANDS_LMTP = _COMMON_COMMANDS + [b"LHLO"]
SUPPORTED_COMMANDS_LMTP.sort()


def _suppcmd(commands: Iterable[bytes]) -> bytes:
    return b"Supported commands: " + b" ".join(commands)


class SMTP_STATUS_CODES:
    # Enforced conventions:
    #  1. Must start with uppercase "S"
    #  2. Must have the 3-digit status code following "S"
    #  3. Must be instances of StatusCode

    S220_READY_TLS = StatusCode(220, b"Ready to start TLS")
    S221_BYE = StatusCode(221, b"Bye")
    S235_AUTH_SUCCESS = StatusCode(235, b"2.7.0 Authentication successful")

    S250_OK = StatusCode(250, b"OK")
    S250_FQDN = StatusCode(250, bytes(socket.getfqdn(), "utf-8"))

    S250_SUPPCMD_LMTP = StatusCode(250, _suppcmd(SUPPORTED_COMMANDS_LMTP))
    S250_SUPPCMD_NOTLS = StatusCode(250, _suppcmd(SUPPORTED_COMMANDS_NOTLS))
    S250_SUPPCMD_TLS = StatusCode(250, _suppcmd(SUPPORTED_COMMANDS_TLS))

    S250_SYNTAX_AUTH = StatusCode(250, b"Syntax: AUTH <mechanism>")
    S250_SYNTAX_DATA = StatusCode(250, b"Syntax: DATA")
    S250_SYNTAX_EHLO = StatusCode(250, b"Syntax: EHLO hostname")
    S250_SYNTAX_HELO = StatusCode(250, b"Syntax: HELO hostname")
    S250_SYNTAX_MAIL = StatusCode(250, b"Syntax: MAIL FROM: <address>")
    S250_SYNTAX_NOOP = StatusCode(250, b"Syntax: NOOP [ignored]")
    S250_SYNTAX_QUIT = StatusCode(250, b"Syntax: QUIT")
    S250_SYNTAX_RCPT = StatusCode(250, b"Syntax: RCPT TO: <address>")
    S250_SYNTAX_RSET = StatusCode(250, b"Syntax: RSET")
    S250_SYNTAX_STARTTLS = StatusCode(250, b"Syntax: STARTTLS")
    S250_SYNTAX_VRFY = StatusCode(250, b"Syntax: VRFY <address>")

    S250_SYNTAX_MAIL_E = StatusCode(
        250, S250_SYNTAX_MAIL.mesg + b" [SP <mail-parameters>]"
    )
    S250_SYNTAX_RCPT_E = StatusCode(
        250, S250_SYNTAX_RCPT.mesg + b" [SP <mail-parameters>]"
    )

    S252_CANNOT_VRFY = StatusCode(
        252,
        b"Cannot VRFY user, but will accept message and attempt delivery",
    )

    S334_AUTH_EMPTYPROMPT = StatusCode(334, b"")
    S334_AUTH_USERNAME = StatusCode(334, b"VXNlciBOYW1lAA==")
    S334_AUTH_PASSWORD = StatusCode(334, b"UGFzc3dvcmQA")

    S354_DATA_ENDWITH = StatusCode(354, b"End data with <CR><LF>.<CR><LF>")

    S421_TOO_MANY = StatusCode(421, b"4.7.0 %b sent too many times")

    S450_DEST_GREYLIST = StatusCode(
        450, b"4.2.0 Recipient address rejected: Greylisted"
    )
    S450_SERVICE_UNAVAIL = StatusCode(450, b"4.3.2 Service currently unavailable")
    S452_TOO_MANY_CONN = StatusCode(452, b"4.7.0 Too many connections")
    S454_TLS_NA = StatusCode(454, b"TLS not available")

    S500_BAD_SYNTAX = StatusCode(500, b"Error: bad syntax")
    S500_CMD_TOO_LONG = StatusCode(500, b"Command line too long")
    S500_DATALINE_TOO_LONG = StatusCode(500, b"Line too long (see RFC5321 4.5.3.1.6)")
    S500_STRICT_ASCII = StatusCode(500, b"Error: strict ASCII mode")

    S500_CMD_UNRECOG = StatusCode(500, b'Error: command "%b" not recognized')
    S500_AUTH_UNRECOG = StatusCode(500, b"Error: command 'AUTH' not recognized")

    S501_AUTH_ABORTED = StatusCode(501, b"5.7.0 Auth aborted")
    S501_AUTH_NOTB64 = StatusCode(501, b"5.5.2 Can't decode base64")
    S501_AUTH_CANTSPLIT = StatusCode(501, b"5.5.2 Can't split auth value")

    S501_MAIL_BODY = StatusCode(501, b"Error: BODY can only be one of 7BIT, 8BITMIME")

    S501_SMTPUTF8_DISABLED = StatusCode(501, b"Error: SMTPUTF8 disabled")
    S501_SMTPUTF8_NOARG = StatusCode(501, b"Error: SMTPUTF8 takes no arguments")

    S501_SUPPCMD_NOTLS = StatusCode(501, S250_SUPPCMD_NOTLS.mesg)

    S501_SYNTAX_DATA = StatusCode(501, S250_SYNTAX_DATA.mesg)
    S501_SYNTAX_EHLO = StatusCode(501, S250_SYNTAX_EHLO.mesg)
    S501_SYNTAX_HELO = StatusCode(501, S250_SYNTAX_HELO.mesg)
    S501_SYNTAX_MAIL = StatusCode(501, S250_SYNTAX_MAIL.mesg)
    S501_SYNTAX_MAIL_E = StatusCode(501, S250_SYNTAX_MAIL_E.mesg)
    S501_SYNTAX_QUIT = StatusCode(501, S250_SYNTAX_QUIT.mesg)
    S501_SYNTAX_RCPT = StatusCode(501, S250_SYNTAX_RCPT.mesg)
    S501_SYNTAX_RCPT_E = StatusCode(501, S250_SYNTAX_RCPT_E.mesg)
    S501_SYNTAX_RSET = StatusCode(501, S250_SYNTAX_RSET.mesg)
    S501_SYNTAX_STARTTLS = StatusCode(501, S250_SYNTAX_STARTTLS.mesg)
    S501_SYNTAX_VRFY = StatusCode(501, S250_SYNTAX_VRFY.mesg)

    S501_TOO_FEW = StatusCode(501, b"Not enough value")
    S501_TOO_MANY = StatusCode(501, b"Too many values")

    S502_EXPN_NOTIMPL = StatusCode(502, b"EXPN not implemented")
    S502_VRFY_COULDNT = StatusCode(502, b"Could not VRFY %b")
    S502_TOO_MANY_UNRECOG = StatusCode(
        502, b"5.5.1 Too many unrecognized commands, goodbye."
    )

    S503_ALREADY_AUTH = StatusCode(503, b"Already authenticated")
    S503_EHLO_FIRST = StatusCode(503, b"Error: send EHLO first")
    S503_HELO_FIRST = StatusCode(503, b"Error: send HELO first")
    S503_MAIL_NEEDED = StatusCode(503, b"Error: need MAIL command")
    S503_MAIL_NESTED = StatusCode(503, b"Error: nested MAIL command")
    S503_RCPT_NEEDED = StatusCode(503, b"Error: need RCPT command")

    S504_AUTH_UNRECOG = StatusCode(504, b"5.5.4 Unrecognized authentication type")
    S530_STARTTLS_FIRST = StatusCode(530, b"Must issue a STARTTLS command first")
    S530_AUTH_REQUIRED = StatusCode(530, b"5.7.0 Authentication required")
    S535_AUTH_INVALID = StatusCode(535, b"5.7.8 Authentication credentials invalid")
    S538_AUTH_ENCRYPTREQ = StatusCode(
        538,
        b"5.7.11 Encryption required for requested authentication mechanism",
    )

    S550_DEST_UNKNOWN = StatusCode(
        550, b"5.1.1 Recipient address rejected: User unknown"
    )
    S550_NO_RELAY = StatusCode(550, b"5.7.1 Unable to relay")
    S552_EXCEED_SIZE = StatusCode(
        552, b"Error: message size exceeds fixed maximum message size"
    )
    S552_DATA_TOO_MUCH = StatusCode(552, b"Error: Too much mail data")
    S553_MALFORMED = StatusCode(553, b"5.1.3 Error: malformed address")
    S554_LACK_SECURITY = StatusCode(554, b"Command refused due to lack of security")

    S555_MAIL_PARAMS_UNRECOG = StatusCode(
        555,
        b"MAIL FROM parameters not recognized or not implemented",
    )
    S555_RCPT_PARAMS_UNRECOG = StatusCode(
        555, b"RCPT TO parameters not recognized or not implemented"
    )