def send_email(prefs, report_str): recipients = prefs['ADMIN_EMAIL'].split(',') msg = dedent(""" From: %s To: %s Subject: %s Date: %s """).lstrip() % (prefs.get('SMTP_FROM'), prefs.get('ADMIN_EMAIL'), prefs.get('SMTP_SUBJECT'), time.strftime(prefs.get('SMTP_DATE_FORMAT'))) msg += report_str try: smtp = SMTP() if logging.getLogger().isEnabledFor(logging.DEBUG): smtp.set_debuglevel(1) smtp.connect(prefs.get('SMTP_HOST'), prefs.get('SMTP_PORT')) # If the server supports ESMTP and TLS, then convert the message exchange to TLS via the # STARTTLS command. if smtp.ehlo()[0] == 250: if smtp.has_extn('starttls'): (code, resp) = smtp.starttls() if code != 220: raise SMTPResponseException(code, resp) (code, resp) = smtp.ehlo() if code != 250: raise SMTPResponseException(code, resp) else: # The server does not support esmtp. # The Python library SMTP class handles executing HELO/EHLO commands inside # login/sendmail methods when neither helo()/ehlo() methods have been # previously called. Because we have already called ehlo() above, we must # manually fallback to calling helo() here. (code, resp) = smtp.helo() if not (200 <= code <= 299): raise SMTPHeloError(code, resp) username = prefs.get('SMTP_USERNAME') password = prefs.get('SMTP_PASSWORD') if username and password: smtp.login(username, password) smtp.sendmail(prefs.get('SMTP_FROM'), recipients, msg) debug("sent email to: %s" % prefs.get("ADMIN_EMAIL")) except Exception, e: print "Error sending email" print e print "Email message follows:" print msg
def test_custom_smtp_backend(backend: T, from_addr: str) -> None: try: backend.open() backend.connection.ehlo_or_helo_if_needed() (code, resp) = backend.connection.mail(from_addr, []) if code != 250: logger.warning('Error testing mail settings, code %d, resp: %s' % (code, resp)) raise SMTPResponseException(code, resp) (code, resp) = backend.connection.rcpt('*****@*****.**') if (code != 250) and (code != 251): logger.warning('Error testing mail settings, code %d, resp: %s' % (code, resp)) raise SMTPResponseException(code, resp) finally: backend.close()
def test(self, from_addr): try: self.open() self.connection.ehlo_or_helo_if_needed() (code, resp) = self.connection.mail(from_addr, []) if code != 250: logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp)) raise SMTPResponseException(code, resp) (code, resp) = self.connection.rcpt('*****@*****.**') if (code != 250) and (code != 251): logger.warn('Error testing mail settings, code %d, resp: %s' % (code, resp)) raise SMTPResponseException(code, resp) finally: self.close()
def get_response(self): """Get a status reponse from the server. Returns a tuple consisting of: - server response code (e.g. '250', or such, if all goes well) Note: returns -1 if it can't read response code. - server response string corresponding to response code (multiline responses are converted to a single, multiline string). Raises SMTPResponseException for codes > 500. """ code = -1 response = [] while True: try: line = yield from self.reader.readline() except ConnectionResetError as exc: raise SMTPServerDisconnected(exc) if not line: break if len(line) > MAX_LINE_LENGTH: raise SMTPResponseException(500, "Line too long.") code = line[:3] message = line[4:] message = message.strip(b' \t\r\n') # Strip newlines message = message.decode() # Convert to string response.append(message) try: code = int(code) except ValueError: code = -1 if line[3:4] != b"-": break message = "\n".join(response) if self.debug: logger.debug("reply: %s %s", code, message) if 500 <= code <= 599: raise SMTPResponseException(code, message) return code, message
def test_email_server(ip_address: str) -> None: """Attempt to connect to an SMTP server. Raises: OSError: if a network-related system error occurs, for example if the IP address is a v6 address but the system lacks an IPv6 route, or if the connection times out SMTPResponseException: if the server sends an invalid response SMTPServerDisconnected: if the server hangs up on us or fails to respond both correctly and quickly enough """ smtp = SMTP(timeout=5.0) if website.env.logging_level == 'debug': smtp.set_debuglevel(2) try: code = smtp.connect(ip_address)[0] if code < 0: raise SMTPResponseException( code, "first line received from server is invalid") finally: try: smtp.close() except Exception: pass
def test_smpt_error_code_500(self): # to handle this kind of error # http://docs.python.org/2.7/library/smtplib.html#smtplib.SMTPResponseException with patch("django.core.mail.EmailMultiAlternatives.send") as send_mail: send_mail.side_effect = SMTPResponseException(500, "") result_of_sending, fatal_error = self.channel.send(self.outbound_message1) self.assertFalse(result_of_sending) self.assertTrue(fatal_error)
def __exit__(self, *args): try: code, message = self.docmd("QUIT") if code != 221: raise SMTPResponseException(code, message) except SMTPServerDisconnected: pass finally: self.close()
def starttls(self, keyfile=None, certfile=None, context=None): """Puts the connection to the SMTP server into TLS mode. If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first. If the server supports TLS, this will encrypt the rest of the SMTP session. If you provide the keyfile and certfile parameters, the identity of the SMTP server and client can be checked. This, however, depends on whether the socket module really checks the certificates. This method may raise the following exceptions: SMTPHeloError The server didn't reply properly to the helo greeting. """ self.ehlo_or_helo_if_needed() if not self.has_extn("starttls"): raise SMTPNotSupportedError( "STARTTLS extension not supported by server.") (resp, reply) = self.docmd("STARTTLS") if resp == 220: if not _have_ssl: raise RuntimeError("No SSL support included in this Python") if context is not None and keyfile is not None: raise ValueError("context and keyfile arguments are mutually " "exclusive") if context is not None and certfile is not None: raise ValueError("context and certfile arguments are mutually " "exclusive") if keyfile is not None or certfile is not None: import warnings warnings.warn( "keyfile and certfile are deprecated, use a" "custom context instead", DeprecationWarning, 2) if context is None: context = ssl._create_stdlib_context(certfile=certfile, keyfile=keyfile) self.sock = context.wrap_socket(self.sock, server_hostname=self._host) self.file = None # RFC 3207: # The client MUST discard any knowledge obtained from # the server, such as the list of SMTP service extensions, # which was not obtained from the TLS negotiation itself. self.helo_resp = None self.ehlo_resp = None self.esmtp_features = {} self.does_esmtp = 0 else: # RFC 3207: # 501 Syntax error (no parameters allowed) # 454 TLS not available due to temporary reason raise SMTPResponseException(resp, reply) return (resp, reply)
def mail(self, sender: str, options: tuple = ()): """ Like `smtplib.SMTP.mail`, but raise an appropriate exception on negative SMTP server response. A code > 400 is an error here. """ code, message = super().mail(sender=sender, options=options) if code >= 400: raise SMTPResponseException(code=code, msg=message) return code, message
def test_get_session_ssl_raisesMessSendErr(get_email, mocker): """ GIVEN an incorrect password in a valid Email object WHEN Email.get_session() is called THEN assert Exception is raised """ get_ssl_mock = mocker.patch.object(Email, '_get_ssl') get_ssl_mock.return_value.login.side_effect = SMTPResponseException( code=0, msg=b'') e = get_email with pytest.raises(MessageSendError): e._get_session()
def getreply(self): """Get a reply from the server. Returns a tuple consisting of: - server response code (e.g. '250', or such, if all goes well) Note: returns -1 if it can't read response code. - server response string corresponding to response code (multiline responses are converted to a single, multiline string). Raises SMTPServerDisconnected if end-of-file is reached. """ resp = [] if self.file is None: self.file = self.sock.makefile('rb') while 1: try: line = self.file.readline(_MAXLINE + 1) except OSError as e: self.close() raise SMTPServerDisconnected( "Connection unexpectedly closed: " + str(e)) if not line: self.close() raise SMTPServerDisconnected("Connection unexpectedly closed") if self.debuglevel > 0: self._print_debug('reply:', repr(line)) if len(line) > _MAXLINE: self.close() raise SMTPResponseException(500, "Line too long.") resp.append(line[4:].strip(b' \t\r\n')) code = line[:3] # Check that the error code is syntactically correct. # Don't attempt to read a continuation line if it is broken. try: errcode = int(code) except ValueError: errcode = -1 break # Check if multiline response. if line[3:4] != b"-": break errmsg = b"\n".join(resp) if self.debuglevel > 0: self._print_debug('reply: retcode (%s); Msg: %r' % (errcode, errmsg)) return errcode, errmsg
def rcpt(self, recip: str, options: tuple = ()): """ Like `smtplib.SMTP.rcpt`, but handle negative SMTP server responses directly. """ code, message = super().rcpt(recip=recip, options=options) if code >= 500: # Address clearly invalid: issue negative result raise AddressNotDeliverableError({ self._host: SMTPMessage(command='RCPT TO', code=code, text=message.decode(errors='ignore')) }) elif code >= 400: raise SMTPResponseException(code=code, msg=message) return code, message
def connect(self, host: str = 'localhost', port: int = 0, source_address: str = None) -> Tuple[int, str]: """ Like `smtplib.SMTP.connect`, but raise appropriate exceptions on connection failure or negative SMTP server response. """ self.__command = 'connect' # Used for error messages. self._host = host # Workaround: Missing in standard smtplib! try: code, message = super().connect(host=host, port=port, source_address=source_address) except OSError as error: raise SMTPServerDisconnected(str(error)) if code >= 400: raise SMTPResponseException(code=code, msg=message) return code, message
def test_send(self): email_server = EmailServer() email_server.server = MagicMock() email = MagicMock() email.recipients = ['bob', 'betty'] email.sender = 'ben' email.as_string.return_value = 'An email' email.should_send.return_value = NoContent("Email was empty") response = email_server.send(email) self.assertIsInstance(response, NoContent) self.assertEqual(response.message, "Email was empty") email.should_send.return_value = Failure("Couldn't render email") response = email_server.send(email) self.assertIsInstance(response, Failure) self.assertEqual(response.message, "Couldn't render email") email.should_send.return_value = OK("Seems OK to send") response = email_server.send(email) self.assertIsInstance(response, OK) self.assertEqual(response.message, "Sent OK") email_server.server.sendmail.assert_called_with( 'ben', ['bob', 'betty'], 'An email') email_server.server.sendmail.reset_mock() email_server.server.sendmail.side_effect = SMTPResponseException( 500, "Fake exception") response = email_server.send(email) self.assertIsInstance(response, Failure) self.assertEqual(response.message, "Problem sending the email") email_server.server.sendmail.assert_called_with( 'ben', ['bob', 'betty'], 'An email')
def send_email(prefs, report_str): recipients = prefs['ADMIN_EMAIL'].split(',') mail_command = prefs["MAIL_COMMAND"] if mail_command and recipients: err_code = send_mail_by_command(mail_command, recipients, report_str, debug) if err_code != 0: logging.getLogger("util").warn("Command %s returned code %s " % (" ".join(mail_command), err_code)) return msg = dedent(""" From: {0} To: {1} Subject: {2} Date: {3} """).lstrip().format(prefs.get('SMTP_FROM'), prefs.get('ADMIN_EMAIL'), prefs.get('SMTP_SUBJECT'), time.strftime(prefs.get('SMTP_DATE_FORMAT'))) msg += report_str try: if is_true(prefs.get('SMTP_SSL')): smtp = SMTP_SSL() else: smtp = SMTP() if logging.getLogger().isEnabledFor(logging.DEBUG): smtp.set_debuglevel(1) smtp.connect(prefs.get('SMTP_HOST'), prefs.get('SMTP_PORT')) # If the server supports ESMTP and TLS, then convert the message exchange to TLS via the # STARTTLS command. if smtp.ehlo()[0] == 250: if smtp.has_extn('starttls'): (code, resp) = smtp.starttls() if code != 220: raise SMTPResponseException(code, resp) (code, resp) = smtp.ehlo() if code != 250: raise SMTPResponseException(code, resp) else: # The server does not support esmtp. # The Python library SMTP class handles executing HELO/EHLO commands inside # login/sendmail methods when neither helo()/ehlo() methods have been # previously called. Because we have already called ehlo() above, we must # manually fallback to calling helo() here. (code, resp) = smtp.helo() if not (200 <= code <= 299): raise SMTPHeloError(code, resp) username = prefs.get('SMTP_USERNAME') password = prefs.get('SMTP_PASSWORD') if username and password: smtp.login(username, password) smtp.sendmail(prefs.get('SMTP_FROM'), recipients, msg) debug("sent email to: %s" % prefs.get("ADMIN_EMAIL")) except Exception as e: print("Error sending email") print(e) print("Email message follows:") print(msg) try: smtp.quit() except Exception: pass
def send_email(prefs, report_str): recipients = prefs['ADMIN_EMAIL'].split(',') msg = dedent(""" From: {0} To: {1} Subject: {2} Date: {3} """).lstrip().format(prefs.get('SMTP_FROM'), prefs.get('ADMIN_EMAIL'), prefs.get('SMTP_SUBJECT'), time.strftime(prefs.get('SMTP_DATE_FORMAT'))) msg += report_str try: method = prefs.get('EMAIL_METHOD') if is_true(prefs.get('SMTP_SSL')): smtp = SMTP_SSL() else: smtp = SMTP() if logging.getLogger().isEnabledFor(logging.DEBUG): smtp.set_debuglevel(1) smtp.connect(prefs.get('SMTP_HOST'), prefs.get('SMTP_PORT')) # If the server supports ESMTP and TLS, then convert the message exchange to TLS via the # STARTTLS command. if smtp.ehlo()[0] == 250: if smtp.has_extn('starttls'): (code, resp) = smtp.starttls() if code != 220: raise SMTPResponseException(code, resp) (code, resp) = smtp.ehlo() if code != 250: raise SMTPResponseException(code, resp) else: # The server does not support esmtp. # The Python library SMTP class handles executing HELO/EHLO commands inside # login/sendmail methods when neither helo()/ehlo() methods have been # previously called. Because we have already called ehlo() above, we must # manually fallback to calling helo() here. (code, resp) = smtp.helo() if not (200 <= code <= 299): raise SMTPHeloError(code, resp) username = prefs.get('SMTP_USERNAME') password = prefs.get('SMTP_PASSWORD') if username and password: smtp.login(username, password) smtp.sendmail(prefs.get('SMTP_FROM'), recipients, msg) debug("sent email to: %s" % prefs.get("ADMIN_EMAIL")) elif method == 'SENDMAIL': msg = MIMEText(report_str) msg["From"] = prefs.get("SMTP_FROM") msg["To"] = prefs.get("ADMIN_EMAIL") msg["Subject"] = prefs.get("SMTP_SUBJECT") p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE) p.communicate(msg.as_string()) elif method == 'MAIL': p = Popen(['mail', '-s', prefs.get("SMTP_SUBJECT")] + recipients, stdin=PIPE) p.communicate(report_str) elif method == 'STDOUT': print(report_str) else: raise Exception("Unknown e-mail method: %s" % method) except Exception as e: print("Error sending email") print(e) print("Email message follows:") print(report_str) try: smtp.quit() except Exception: pass
def fail(a, b): raise SMTPResponseException(400, 'Auth denied')