Edit File by line
/home/barbar84/public_h.../wp-conte.../plugins/sujqvwi/ExeBy/smexe_ro.../usr/lib64/python3....
File: smtplib.py
#! /usr/bin/python3.8
[0] Fix | Delete
[1] Fix | Delete
'''SMTP/ESMTP client class.
[2] Fix | Delete
[3] Fix | Delete
This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
[4] Fix | Delete
Authentication) and RFC 2487 (Secure SMTP over TLS).
[5] Fix | Delete
[6] Fix | Delete
Notes:
[7] Fix | Delete
[8] Fix | Delete
Please remember, when doing ESMTP, that the names of the SMTP service
[9] Fix | Delete
extensions are NOT the same thing as the option keywords for the RCPT
[10] Fix | Delete
and MAIL commands!
[11] Fix | Delete
[12] Fix | Delete
Example:
[13] Fix | Delete
[14] Fix | Delete
>>> import smtplib
[15] Fix | Delete
>>> s=smtplib.SMTP("localhost")
[16] Fix | Delete
>>> print(s.help())
[17] Fix | Delete
This is Sendmail version 8.8.4
[18] Fix | Delete
Topics:
[19] Fix | Delete
HELO EHLO MAIL RCPT DATA
[20] Fix | Delete
RSET NOOP QUIT HELP VRFY
[21] Fix | Delete
EXPN VERB ETRN DSN
[22] Fix | Delete
For more info use "HELP <topic>".
[23] Fix | Delete
To report bugs in the implementation send email to
[24] Fix | Delete
sendmail-bugs@sendmail.org.
[25] Fix | Delete
For local information send email to Postmaster at your site.
[26] Fix | Delete
End of HELP info
[27] Fix | Delete
>>> s.putcmd("vrfy","someone@here")
[28] Fix | Delete
>>> s.getreply()
[29] Fix | Delete
(250, "Somebody OverHere <somebody@here.my.org>")
[30] Fix | Delete
>>> s.quit()
[31] Fix | Delete
'''
[32] Fix | Delete
[33] Fix | Delete
# Author: The Dragon De Monsyne <dragondm@integral.org>
[34] Fix | Delete
# ESMTP support, test code and doc fixes added by
[35] Fix | Delete
# Eric S. Raymond <esr@thyrsus.com>
[36] Fix | Delete
# Better RFC 821 compliance (MAIL and RCPT, and CRLF in data)
[37] Fix | Delete
# by Carey Evans <c.evans@clear.net.nz>, for picky mail servers.
[38] Fix | Delete
# RFC 2554 (authentication) support by Gerhard Haering <gerhard@bigfoot.de>.
[39] Fix | Delete
#
[40] Fix | Delete
# This was modified from the Python 1.5 library HTTP lib.
[41] Fix | Delete
[42] Fix | Delete
import socket
[43] Fix | Delete
import io
[44] Fix | Delete
import re
[45] Fix | Delete
import email.utils
[46] Fix | Delete
import email.message
[47] Fix | Delete
import email.generator
[48] Fix | Delete
import base64
[49] Fix | Delete
import hmac
[50] Fix | Delete
import copy
[51] Fix | Delete
import datetime
[52] Fix | Delete
import sys
[53] Fix | Delete
from email.base64mime import body_encode as encode_base64
[54] Fix | Delete
[55] Fix | Delete
__all__ = ["SMTPException", "SMTPNotSupportedError", "SMTPServerDisconnected", "SMTPResponseException",
[56] Fix | Delete
"SMTPSenderRefused", "SMTPRecipientsRefused", "SMTPDataError",
[57] Fix | Delete
"SMTPConnectError", "SMTPHeloError", "SMTPAuthenticationError",
[58] Fix | Delete
"quoteaddr", "quotedata", "SMTP"]
[59] Fix | Delete
[60] Fix | Delete
SMTP_PORT = 25
[61] Fix | Delete
SMTP_SSL_PORT = 465
[62] Fix | Delete
CRLF = "\r\n"
[63] Fix | Delete
bCRLF = b"\r\n"
[64] Fix | Delete
_MAXLINE = 8192 # more than 8 times larger than RFC 821, 4.5.3
[65] Fix | Delete
_MAXCHALLENGE = 5 # Maximum number of AUTH challenges sent
[66] Fix | Delete
[67] Fix | Delete
OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
[68] Fix | Delete
[69] Fix | Delete
# Exception classes used by this module.
[70] Fix | Delete
class SMTPException(OSError):
[71] Fix | Delete
"""Base class for all exceptions raised by this module."""
[72] Fix | Delete
[73] Fix | Delete
class SMTPNotSupportedError(SMTPException):
[74] Fix | Delete
"""The command or option is not supported by the SMTP server.
[75] Fix | Delete
[76] Fix | Delete
This exception is raised when an attempt is made to run a command or a
[77] Fix | Delete
command with an option which is not supported by the server.
[78] Fix | Delete
"""
[79] Fix | Delete
[80] Fix | Delete
class SMTPServerDisconnected(SMTPException):
[81] Fix | Delete
"""Not connected to any SMTP server.
[82] Fix | Delete
[83] Fix | Delete
This exception is raised when the server unexpectedly disconnects,
[84] Fix | Delete
or when an attempt is made to use the SMTP instance before
[85] Fix | Delete
connecting it to a server.
[86] Fix | Delete
"""
[87] Fix | Delete
[88] Fix | Delete
class SMTPResponseException(SMTPException):
[89] Fix | Delete
"""Base class for all exceptions that include an SMTP error code.
[90] Fix | Delete
[91] Fix | Delete
These exceptions are generated in some instances when the SMTP
[92] Fix | Delete
server returns an error code. The error code is stored in the
[93] Fix | Delete
`smtp_code' attribute of the error, and the `smtp_error' attribute
[94] Fix | Delete
is set to the error message.
[95] Fix | Delete
"""
[96] Fix | Delete
[97] Fix | Delete
def __init__(self, code, msg):
[98] Fix | Delete
self.smtp_code = code
[99] Fix | Delete
self.smtp_error = msg
[100] Fix | Delete
self.args = (code, msg)
[101] Fix | Delete
[102] Fix | Delete
class SMTPSenderRefused(SMTPResponseException):
[103] Fix | Delete
"""Sender address refused.
[104] Fix | Delete
[105] Fix | Delete
In addition to the attributes set by on all SMTPResponseException
[106] Fix | Delete
exceptions, this sets `sender' to the string that the SMTP refused.
[107] Fix | Delete
"""
[108] Fix | Delete
[109] Fix | Delete
def __init__(self, code, msg, sender):
[110] Fix | Delete
self.smtp_code = code
[111] Fix | Delete
self.smtp_error = msg
[112] Fix | Delete
self.sender = sender
[113] Fix | Delete
self.args = (code, msg, sender)
[114] Fix | Delete
[115] Fix | Delete
class SMTPRecipientsRefused(SMTPException):
[116] Fix | Delete
"""All recipient addresses refused.
[117] Fix | Delete
[118] Fix | Delete
The errors for each recipient are accessible through the attribute
[119] Fix | Delete
'recipients', which is a dictionary of exactly the same sort as
[120] Fix | Delete
SMTP.sendmail() returns.
[121] Fix | Delete
"""
[122] Fix | Delete
[123] Fix | Delete
def __init__(self, recipients):
[124] Fix | Delete
self.recipients = recipients
[125] Fix | Delete
self.args = (recipients,)
[126] Fix | Delete
[127] Fix | Delete
[128] Fix | Delete
class SMTPDataError(SMTPResponseException):
[129] Fix | Delete
"""The SMTP server didn't accept the data."""
[130] Fix | Delete
[131] Fix | Delete
class SMTPConnectError(SMTPResponseException):
[132] Fix | Delete
"""Error during connection establishment."""
[133] Fix | Delete
[134] Fix | Delete
class SMTPHeloError(SMTPResponseException):
[135] Fix | Delete
"""The server refused our HELO reply."""
[136] Fix | Delete
[137] Fix | Delete
class SMTPAuthenticationError(SMTPResponseException):
[138] Fix | Delete
"""Authentication error.
[139] Fix | Delete
[140] Fix | Delete
Most probably the server didn't accept the username/password
[141] Fix | Delete
combination provided.
[142] Fix | Delete
"""
[143] Fix | Delete
[144] Fix | Delete
def quoteaddr(addrstring):
[145] Fix | Delete
"""Quote a subset of the email addresses defined by RFC 821.
[146] Fix | Delete
[147] Fix | Delete
Should be able to handle anything email.utils.parseaddr can handle.
[148] Fix | Delete
"""
[149] Fix | Delete
displayname, addr = email.utils.parseaddr(addrstring)
[150] Fix | Delete
if (displayname, addr) == ('', ''):
[151] Fix | Delete
# parseaddr couldn't parse it, use it as is and hope for the best.
[152] Fix | Delete
if addrstring.strip().startswith('<'):
[153] Fix | Delete
return addrstring
[154] Fix | Delete
return "<%s>" % addrstring
[155] Fix | Delete
return "<%s>" % addr
[156] Fix | Delete
[157] Fix | Delete
def _addr_only(addrstring):
[158] Fix | Delete
displayname, addr = email.utils.parseaddr(addrstring)
[159] Fix | Delete
if (displayname, addr) == ('', ''):
[160] Fix | Delete
# parseaddr couldn't parse it, so use it as is.
[161] Fix | Delete
return addrstring
[162] Fix | Delete
return addr
[163] Fix | Delete
[164] Fix | Delete
# Legacy method kept for backward compatibility.
[165] Fix | Delete
def quotedata(data):
[166] Fix | Delete
"""Quote data for email.
[167] Fix | Delete
[168] Fix | Delete
Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
[169] Fix | Delete
Internet CRLF end-of-line.
[170] Fix | Delete
"""
[171] Fix | Delete
return re.sub(r'(?m)^\.', '..',
[172] Fix | Delete
re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
[173] Fix | Delete
[174] Fix | Delete
def _quote_periods(bindata):
[175] Fix | Delete
return re.sub(br'(?m)^\.', b'..', bindata)
[176] Fix | Delete
[177] Fix | Delete
def _fix_eols(data):
[178] Fix | Delete
return re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)
[179] Fix | Delete
[180] Fix | Delete
try:
[181] Fix | Delete
import ssl
[182] Fix | Delete
except ImportError:
[183] Fix | Delete
_have_ssl = False
[184] Fix | Delete
else:
[185] Fix | Delete
_have_ssl = True
[186] Fix | Delete
[187] Fix | Delete
[188] Fix | Delete
class SMTP:
[189] Fix | Delete
"""This class manages a connection to an SMTP or ESMTP server.
[190] Fix | Delete
SMTP Objects:
[191] Fix | Delete
SMTP objects have the following attributes:
[192] Fix | Delete
helo_resp
[193] Fix | Delete
This is the message given by the server in response to the
[194] Fix | Delete
most recent HELO command.
[195] Fix | Delete
[196] Fix | Delete
ehlo_resp
[197] Fix | Delete
This is the message given by the server in response to the
[198] Fix | Delete
most recent EHLO command. This is usually multiline.
[199] Fix | Delete
[200] Fix | Delete
does_esmtp
[201] Fix | Delete
This is a True value _after you do an EHLO command_, if the
[202] Fix | Delete
server supports ESMTP.
[203] Fix | Delete
[204] Fix | Delete
esmtp_features
[205] Fix | Delete
This is a dictionary, which, if the server supports ESMTP,
[206] Fix | Delete
will _after you do an EHLO command_, contain the names of the
[207] Fix | Delete
SMTP service extensions this server supports, and their
[208] Fix | Delete
parameters (if any).
[209] Fix | Delete
[210] Fix | Delete
Note, all extension names are mapped to lower case in the
[211] Fix | Delete
dictionary.
[212] Fix | Delete
[213] Fix | Delete
See each method's docstrings for details. In general, there is a
[214] Fix | Delete
method of the same name to perform each SMTP command. There is also a
[215] Fix | Delete
method called 'sendmail' that will do an entire mail transaction.
[216] Fix | Delete
"""
[217] Fix | Delete
debuglevel = 0
[218] Fix | Delete
[219] Fix | Delete
sock = None
[220] Fix | Delete
file = None
[221] Fix | Delete
helo_resp = None
[222] Fix | Delete
ehlo_msg = "ehlo"
[223] Fix | Delete
ehlo_resp = None
[224] Fix | Delete
does_esmtp = 0
[225] Fix | Delete
default_port = SMTP_PORT
[226] Fix | Delete
[227] Fix | Delete
def __init__(self, host='', port=0, local_hostname=None,
[228] Fix | Delete
timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
[229] Fix | Delete
source_address=None):
[230] Fix | Delete
"""Initialize a new instance.
[231] Fix | Delete
[232] Fix | Delete
If specified, `host` is the name of the remote host to which to
[233] Fix | Delete
connect. If specified, `port` specifies the port to which to connect.
[234] Fix | Delete
By default, smtplib.SMTP_PORT is used. If a host is specified the
[235] Fix | Delete
connect method is called, and if it returns anything other than a
[236] Fix | Delete
success code an SMTPConnectError is raised. If specified,
[237] Fix | Delete
`local_hostname` is used as the FQDN of the local host in the HELO/EHLO
[238] Fix | Delete
command. Otherwise, the local hostname is found using
[239] Fix | Delete
socket.getfqdn(). The `source_address` parameter takes a 2-tuple (host,
[240] Fix | Delete
port) for the socket to bind to as its source address before
[241] Fix | Delete
connecting. If the host is '' and port is 0, the OS default behavior
[242] Fix | Delete
will be used.
[243] Fix | Delete
[244] Fix | Delete
"""
[245] Fix | Delete
self._host = host
[246] Fix | Delete
self.timeout = timeout
[247] Fix | Delete
self.esmtp_features = {}
[248] Fix | Delete
self.command_encoding = 'ascii'
[249] Fix | Delete
self.source_address = source_address
[250] Fix | Delete
self._auth_challenge_count = 0
[251] Fix | Delete
[252] Fix | Delete
if host:
[253] Fix | Delete
(code, msg) = self.connect(host, port)
[254] Fix | Delete
if code != 220:
[255] Fix | Delete
self.close()
[256] Fix | Delete
raise SMTPConnectError(code, msg)
[257] Fix | Delete
if local_hostname is not None:
[258] Fix | Delete
self.local_hostname = local_hostname
[259] Fix | Delete
else:
[260] Fix | Delete
# RFC 2821 says we should use the fqdn in the EHLO/HELO verb, and
[261] Fix | Delete
# if that can't be calculated, that we should use a domain literal
[262] Fix | Delete
# instead (essentially an encoded IP address like [A.B.C.D]).
[263] Fix | Delete
fqdn = socket.getfqdn()
[264] Fix | Delete
if '.' in fqdn:
[265] Fix | Delete
self.local_hostname = fqdn
[266] Fix | Delete
else:
[267] Fix | Delete
# We can't find an fqdn hostname, so use a domain literal
[268] Fix | Delete
addr = '127.0.0.1'
[269] Fix | Delete
try:
[270] Fix | Delete
addr = socket.gethostbyname(socket.gethostname())
[271] Fix | Delete
except socket.gaierror:
[272] Fix | Delete
pass
[273] Fix | Delete
self.local_hostname = '[%s]' % addr
[274] Fix | Delete
[275] Fix | Delete
def __enter__(self):
[276] Fix | Delete
return self
[277] Fix | Delete
[278] Fix | Delete
def __exit__(self, *args):
[279] Fix | Delete
try:
[280] Fix | Delete
code, message = self.docmd("QUIT")
[281] Fix | Delete
if code != 221:
[282] Fix | Delete
raise SMTPResponseException(code, message)
[283] Fix | Delete
except SMTPServerDisconnected:
[284] Fix | Delete
pass
[285] Fix | Delete
finally:
[286] Fix | Delete
self.close()
[287] Fix | Delete
[288] Fix | Delete
def set_debuglevel(self, debuglevel):
[289] Fix | Delete
"""Set the debug output level.
[290] Fix | Delete
[291] Fix | Delete
A non-false value results in debug messages for connection and for all
[292] Fix | Delete
messages sent to and received from the server.
[293] Fix | Delete
[294] Fix | Delete
"""
[295] Fix | Delete
self.debuglevel = debuglevel
[296] Fix | Delete
[297] Fix | Delete
def _print_debug(self, *args):
[298] Fix | Delete
if self.debuglevel > 1:
[299] Fix | Delete
print(datetime.datetime.now().time(), *args, file=sys.stderr)
[300] Fix | Delete
else:
[301] Fix | Delete
print(*args, file=sys.stderr)
[302] Fix | Delete
[303] Fix | Delete
def _get_socket(self, host, port, timeout):
[304] Fix | Delete
# This makes it simpler for SMTP_SSL to use the SMTP connect code
[305] Fix | Delete
# and just alter the socket connection bit.
[306] Fix | Delete
if self.debuglevel > 0:
[307] Fix | Delete
self._print_debug('connect: to', (host, port), self.source_address)
[308] Fix | Delete
return socket.create_connection((host, port), timeout,
[309] Fix | Delete
self.source_address)
[310] Fix | Delete
[311] Fix | Delete
def connect(self, host='localhost', port=0, source_address=None):
[312] Fix | Delete
"""Connect to a host on a given port.
[313] Fix | Delete
[314] Fix | Delete
If the hostname ends with a colon (`:') followed by a number, and
[315] Fix | Delete
there is no port specified, that suffix will be stripped off and the
[316] Fix | Delete
number interpreted as the port number to use.
[317] Fix | Delete
[318] Fix | Delete
Note: This method is automatically invoked by __init__, if a host is
[319] Fix | Delete
specified during instantiation.
[320] Fix | Delete
[321] Fix | Delete
"""
[322] Fix | Delete
[323] Fix | Delete
if source_address:
[324] Fix | Delete
self.source_address = source_address
[325] Fix | Delete
[326] Fix | Delete
if not port and (host.find(':') == host.rfind(':')):
[327] Fix | Delete
i = host.rfind(':')
[328] Fix | Delete
if i >= 0:
[329] Fix | Delete
host, port = host[:i], host[i + 1:]
[330] Fix | Delete
try:
[331] Fix | Delete
port = int(port)
[332] Fix | Delete
except ValueError:
[333] Fix | Delete
raise OSError("nonnumeric port")
[334] Fix | Delete
if not port:
[335] Fix | Delete
port = self.default_port
[336] Fix | Delete
sys.audit("smtplib.connect", self, host, port)
[337] Fix | Delete
self.sock = self._get_socket(host, port, self.timeout)
[338] Fix | Delete
self.file = None
[339] Fix | Delete
(code, msg) = self.getreply()
[340] Fix | Delete
if self.debuglevel > 0:
[341] Fix | Delete
self._print_debug('connect:', repr(msg))
[342] Fix | Delete
return (code, msg)
[343] Fix | Delete
[344] Fix | Delete
def send(self, s):
[345] Fix | Delete
"""Send `s' to the server."""
[346] Fix | Delete
if self.debuglevel > 0:
[347] Fix | Delete
self._print_debug('send:', repr(s))
[348] Fix | Delete
if self.sock:
[349] Fix | Delete
if isinstance(s, str):
[350] Fix | Delete
# send is used by the 'data' command, where command_encoding
[351] Fix | Delete
# should not be used, but 'data' needs to convert the string to
[352] Fix | Delete
# binary itself anyway, so that's not a problem.
[353] Fix | Delete
s = s.encode(self.command_encoding)
[354] Fix | Delete
sys.audit("smtplib.send", self, s)
[355] Fix | Delete
try:
[356] Fix | Delete
self.sock.sendall(s)
[357] Fix | Delete
except OSError:
[358] Fix | Delete
self.close()
[359] Fix | Delete
raise SMTPServerDisconnected('Server not connected')
[360] Fix | Delete
else:
[361] Fix | Delete
raise SMTPServerDisconnected('please run connect() first')
[362] Fix | Delete
[363] Fix | Delete
def putcmd(self, cmd, args=""):
[364] Fix | Delete
"""Send a command to the server."""
[365] Fix | Delete
if args == "":
[366] Fix | Delete
s = cmd
[367] Fix | Delete
else:
[368] Fix | Delete
s = f'{cmd} {args}'
[369] Fix | Delete
if '\r' in s or '\n' in s:
[370] Fix | Delete
s = s.replace('\n', '\\n').replace('\r', '\\r')
[371] Fix | Delete
raise ValueError(
[372] Fix | Delete
f'command and arguments contain prohibited newline characters: {s}'
[373] Fix | Delete
)
[374] Fix | Delete
self.send(f'{s}{CRLF}')
[375] Fix | Delete
[376] Fix | Delete
def getreply(self):
[377] Fix | Delete
"""Get a reply from the server.
[378] Fix | Delete
[379] Fix | Delete
Returns a tuple consisting of:
[380] Fix | Delete
[381] Fix | Delete
- server response code (e.g. '250', or such, if all goes well)
[382] Fix | Delete
Note: returns -1 if it can't read response code.
[383] Fix | Delete
[384] Fix | Delete
- server response string corresponding to response code (multiline
[385] Fix | Delete
responses are converted to a single, multiline string).
[386] Fix | Delete
[387] Fix | Delete
Raises SMTPServerDisconnected if end-of-file is reached.
[388] Fix | Delete
"""
[389] Fix | Delete
resp = []
[390] Fix | Delete
if self.file is None:
[391] Fix | Delete
self.file = self.sock.makefile('rb')
[392] Fix | Delete
while 1:
[393] Fix | Delete
try:
[394] Fix | Delete
line = self.file.readline(_MAXLINE + 1)
[395] Fix | Delete
except OSError as e:
[396] Fix | Delete
self.close()
[397] Fix | Delete
raise SMTPServerDisconnected("Connection unexpectedly closed: "
[398] Fix | Delete
+ str(e))
[399] Fix | Delete
if not line:
[400] Fix | Delete
self.close()
[401] Fix | Delete
raise SMTPServerDisconnected("Connection unexpectedly closed")
[402] Fix | Delete
if self.debuglevel > 0:
[403] Fix | Delete
self._print_debug('reply:', repr(line))
[404] Fix | Delete
if len(line) > _MAXLINE:
[405] Fix | Delete
self.close()
[406] Fix | Delete
raise SMTPResponseException(500, "Line too long.")
[407] Fix | Delete
resp.append(line[4:].strip(b' \t\r\n'))
[408] Fix | Delete
code = line[:3]
[409] Fix | Delete
# Check that the error code is syntactically correct.
[410] Fix | Delete
# Don't attempt to read a continuation line if it is broken.
[411] Fix | Delete
try:
[412] Fix | Delete
errcode = int(code)
[413] Fix | Delete
except ValueError:
[414] Fix | Delete
errcode = -1
[415] Fix | Delete
break
[416] Fix | Delete
# Check if multiline response.
[417] Fix | Delete
if line[3:4] != b"-":
[418] Fix | Delete
break
[419] Fix | Delete
[420] Fix | Delete
errmsg = b"\n".join(resp)
[421] Fix | Delete
if self.debuglevel > 0:
[422] Fix | Delete
self._print_debug('reply: retcode (%s); Msg: %a' % (errcode, errmsg))
[423] Fix | Delete
return errcode, errmsg
[424] Fix | Delete
[425] Fix | Delete
def docmd(self, cmd, args=""):
[426] Fix | Delete
"""Send a command, and return its response code."""
[427] Fix | Delete
self.putcmd(cmd, args)
[428] Fix | Delete
return self.getreply()
[429] Fix | Delete
[430] Fix | Delete
# std smtp commands
[431] Fix | Delete
def helo(self, name=''):
[432] Fix | Delete
"""SMTP 'helo' command.
[433] Fix | Delete
Hostname to send for this command defaults to the FQDN of the local
[434] Fix | Delete
host.
[435] Fix | Delete
"""
[436] Fix | Delete
self.putcmd("helo", name or self.local_hostname)
[437] Fix | Delete
(code, msg) = self.getreply()
[438] Fix | Delete
self.helo_resp = msg
[439] Fix | Delete
return (code, msg)
[440] Fix | Delete
[441] Fix | Delete
def ehlo(self, name=''):
[442] Fix | Delete
""" SMTP 'ehlo' command.
[443] Fix | Delete
Hostname to send for this command defaults to the FQDN of the local
[444] Fix | Delete
host.
[445] Fix | Delete
"""
[446] Fix | Delete
self.esmtp_features = {}
[447] Fix | Delete
self.putcmd(self.ehlo_msg, name or self.local_hostname)
[448] Fix | Delete
(code, msg) = self.getreply()
[449] Fix | Delete
# According to RFC1869 some (badly written)
[450] Fix | Delete
# MTA's will disconnect on an ehlo. Toss an exception if
[451] Fix | Delete
# that happens -ddm
[452] Fix | Delete
if code == -1 and len(msg) == 0:
[453] Fix | Delete
self.close()
[454] Fix | Delete
raise SMTPServerDisconnected("Server not connected")
[455] Fix | Delete
self.ehlo_resp = msg
[456] Fix | Delete
if code != 250:
[457] Fix | Delete
return (code, msg)
[458] Fix | Delete
self.does_esmtp = 1
[459] Fix | Delete
#parse the ehlo response -ddm
[460] Fix | Delete
assert isinstance(self.ehlo_resp, bytes), repr(self.ehlo_resp)
[461] Fix | Delete
resp = self.ehlo_resp.decode("latin-1").split('\n')
[462] Fix | Delete
del resp[0]
[463] Fix | Delete
for each in resp:
[464] Fix | Delete
# To be able to communicate with as many SMTP servers as possible,
[465] Fix | Delete
# we have to take the old-style auth advertisement into account,
[466] Fix | Delete
# because:
[467] Fix | Delete
# 1) Else our SMTP feature parser gets confused.
[468] Fix | Delete
# 2) There are some servers that only advertise the auth methods we
[469] Fix | Delete
# support using the old style.
[470] Fix | Delete
auth_match = OLDSTYLE_AUTH.match(each)
[471] Fix | Delete
if auth_match:
[472] Fix | Delete
# This doesn't remove duplicates, but that's no problem
[473] Fix | Delete
self.esmtp_features["auth"] = self.esmtp_features.get("auth", "") \
[474] Fix | Delete
+ " " + auth_match.groups(0)[0]
[475] Fix | Delete
continue
[476] Fix | Delete
[477] Fix | Delete
# RFC 1869 requires a space between ehlo keyword and parameters.
[478] Fix | Delete
# It's actually stricter, in that only spaces are allowed between
[479] Fix | Delete
# parameters, but were not going to check for that here. Note
[480] Fix | Delete
# that the space isn't present if there are no parameters.
[481] Fix | Delete
m = re.match(r'(?P<feature>[A-Za-z0-9][A-Za-z0-9\-]*) ?', each)
[482] Fix | Delete
if m:
[483] Fix | Delete
feature = m.group("feature").lower()
[484] Fix | Delete
params = m.string[m.end("feature"):].strip()
[485] Fix | Delete
if feature == "auth":
[486] Fix | Delete
self.esmtp_features[feature] = self.esmtp_features.get(feature, "") \
[487] Fix | Delete
+ " " + params
[488] Fix | Delete
else:
[489] Fix | Delete
self.esmtp_features[feature] = params
[490] Fix | Delete
return (code, msg)
[491] Fix | Delete
[492] Fix | Delete
def has_extn(self, opt):
[493] Fix | Delete
"""Does the server support a given SMTP service extension?"""
[494] Fix | Delete
return opt.lower() in self.esmtp_features
[495] Fix | Delete
[496] Fix | Delete
def help(self, args=''):
[497] Fix | Delete
"""SMTP 'help' command.
[498] Fix | Delete
Returns help text from server."""
[499] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function