[Commits] (bkirsch) Added error handling to smtp code
commits at osafoundation.org
commits at osafoundation.org
Mon Aug 23 17:16:02 PDT 2004
Commit by: bkirsch
Modified files:
chandler/parcels/osaf/mail/MailTasks.py 1.6 1.7
chandler/parcels/osaf/mail/smtp.py 1.5 1.6
Log message:
Added error handling to smtp code
ViewCVS links:
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/mail/MailTasks.py.diff?r1=text&tr1=1.6&r2=text&tr2=1.7
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/mail/smtp.py.diff?r1=text&tr1=1.5&r2=text&tr2=1.6
Index: chandler/parcels/osaf/mail/MailTasks.py
diff -u chandler/parcels/osaf/mail/MailTasks.py:1.6 chandler/parcels/osaf/mail/MailTasks.py:1.7
--- chandler/parcels/osaf/mail/MailTasks.py:1.6 Thu Aug 19 18:27:16 2004
+++ chandler/parcels/osaf/mail/MailTasks.py Mon Aug 23 17:16:00 2004
@@ -1,5 +1,5 @@
-__revision__ = "$Revision: 1.6 $"
-__date__ = "$Date: 2004/08/20 01:27:16 $"
+__revision__ = "$Revision: 1.7 $"
+__date__ = "$Date: 2004/08/24 00:16:00 $"
__copyright__ = "Copyright (c) 2004 Open Source Applications Foundation"
__license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
@@ -53,32 +53,42 @@
m = Mail.MailMessage()
ea = Mail.EmailAddress()
- ea.emailAddress = "brian at localhost"
- ea.fullName = "Brian Kirsch"
+ ea.emailAddress = "brian at abc.com"
+ #ea.fullName = "Brian Kirsch"
+
ea1 = Mail.EmailAddress()
- ea1.emailAddress = "bkirsch at osafoundation.org"
- ea1.fullName = "Brian Kirsch"
+ ea1.emailAddress = "bkiro"
+ #ea1.fullName = "Brian Kirsch"
ea2 = Mail.EmailAddress()
- ea2.emailAddress = "bkmuzic at yahoo.com"
- ea2.fullName = "Brian Kirsch"
+ ea2.emailAddress = "bbi.com"
+ #ea2.fullName = "Brian Kirsch"
+
+ ea3 = Mail.EmailAddress()
+ ea3.emailAddress = "bill at test.com"
- m.toAddress.append(ea)
- #m.toAddress.append(ea1)
+ ea4 = Mail.EmailAddress()
+ ea4.emailAddress = "brian at yahoo.com"
+ #ea.fullName = "Brian Kirsch"
+
+ m.toAddress.append(ea1)
+ m.toAddress.append(ea2)
+ m.toAddress.append(ea3)
+ m.toAddress.append(ea4)
+ # m.toAddress.append(ea1)
#m.ccAddress.append(ea2)
+ #m.bccAddress.append(ea)
- m.fromAddress = ea1
- m.replyToAddress = ea
+ m.fromAddress = ea
+ #m.replyToAddress = ea
m.subject = "This is a Test From SMTPSenderAction"
m.body = message.strToText(m, "body", "This is some body Text")
- m.inReplyTo = "TEST"
Globals.repository.commit()
- d = defer.Deferred().addBoth(self.smtpResponse)
+ smtp.SMTPSender(account, m).sendMail()
- smtp.SMTPSender(account, m, d).sendMail()
+ account = None
+ m = None
- def smtpResponse(selfi, result):
- print "SMTP Response Got: ", result
Index: chandler/parcels/osaf/mail/smtp.py
diff -u chandler/parcels/osaf/mail/smtp.py:1.5 chandler/parcels/osaf/mail/smtp.py:1.6
--- chandler/parcels/osaf/mail/smtp.py:1.5 Thu Aug 19 18:27:16 2004
+++ chandler/parcels/osaf/mail/smtp.py Mon Aug 23 17:16:00 2004
@@ -1,5 +1,5 @@
-__revision__ = "$Revision: 1.5 $"
-__date__ = "$Date: 2004/08/20 01:27:16 $"
+__revision__ = "$Revision: 1.6 $"
+__date__ = "$Date: 2004/08/24 00:16:00 $"
__copyright__ = "Copyright (c) 2004 Open Source Applications Foundation"
__license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
@@ -12,6 +12,8 @@
import email.Message as Message
import logging as logging
import common as common
+import twisted.internet.error as error
+import errors as errorCode
import message as message
import osaf.contentmodel.mail.Mail as Mail
import repository.util.UUID as UUID
@@ -22,6 +24,9 @@
except ImportError:
from StringIO import StringIO
+class SMTPConstants(object):
+ SUCCESS = 250
+
class ChandlerESMTPSender(smtp.ESMTPSender):
def tryTLS(self, code, resp, items):
@@ -46,18 +51,17 @@
class SMTPException(common.MailException):
pass
+
class SMTPSender(RepositoryView.AbstractRepositoryViewManager):
def __init__(self, account, mailMessage, deferred=None):
- #XXX: Perhaps get the first account if None
if account is None or not account.isItemOf(Mail.MailParcel.getSMTPAccountKind()):
- raise SMTPMailException("You must pass in a SMTPAccount instance")
+ raise SMTPMailException("You must pass an SMTPAccount instance")
if mailMessage is None or not isinstance(mailMessage, Mail.MailMessage):
- raise SMTPMailException("You must pass in a mailParcel instance")
+ raise SMTPMailException("You must pass a MailMessage instance")
- id = "STMPSender Mail ", DateTime.now().ticks()
- viewName = "%s_%s_%s" % (id, account.displayName, str(UUID.UUID()))
+ viewName = "SMTPSender_%s" % str(UUID.UUID())
super(SMTPSender, self).__init__(Globals.repository, viewName)
@@ -69,14 +73,12 @@
self.failure = None
self.success = None
- #in thread
def sendMail(self):
if __debug__:
self.printCurrentView("sendMail")
reactor.callFromThread(self.__sendMail)
- #IN Twisted
def __sendMail(self):
self.setViewCurrent()
@@ -97,11 +99,14 @@
port = self.account.port
useSSL = self.account.useSSL
useAuth = self.account.useAuth
+ retries = self.account.numRetries
authRequired = True
sslContext = None
+ heloFallback = False
if not useAuth:
authRequired = False
+ heloFallback = True
username = None
password = None
@@ -110,10 +115,10 @@
self.mailMessage.outgoingMessage(account=self.account)
- messageObject = message.kindToMessageObject(self.mailMessage)
- messageText = messageObject.as_string()
+ messageText = message.kindToMessageText(self.mailMessage)
+
self.mailMessage.rfc2882Message = message.strToText(self.mailMessage, "rfc2822Message", messageText)
- d = defer.Deferred().addCallbacks(self.__mailSuccess, self.__mailFailure)
+ d = defer.Deferred().addCallbacks(self.__mailSuccessCheck, self.__mailFailure)
msg = StringIO(messageText)
to_addrs = []
@@ -131,45 +136,90 @@
self.restorePreviousView()
factory = ChandlerESMTPSenderFactory(username, password, from_addr, to_addrs, msg, d,
- 0, sslContext, False, authRequired, useSSL, useSSL)
- #XXX: Is this correct
- #if useSSL:
- # reactor.connectSSL(host, port, factory, sslContext)
+ retries, sslContext, heloFallback, authRequired, useSSL, useSSL)
+
reactor.connectTCP(host, port, factory)
+ def __mailSuccessCheck(self, result):
+ """Twisted smtp.py will call the deferred callback (this method) if
+ one or more recipients are accepted by the mail server. However
+ if at least one recipent is denied by the smtp server we need to
+ treat the message as failed for .4B and possibly beyond"""
- def __mailSuccess(self, result):
self.setViewCurrent()
try:
if __debug__:
- self.printCurrentView("__mailSuccess")
+ self.printCurrentView("__mailSuccessCheck")
+
+ if result[0] == len(result[1]):
+ self.__mailSuccess(result)
+
+ else:
+ self.__mailSomeFailed(result)
+
+ finally:
+ self.restorePreviousView()
+
+ """Commit the view in a thread to prevent blocking"""
+ self.commitView(True)
- addrs = []
- for address in result[1]:
- addrs.append(address[0])
+ def __mailSuccess(self, result):
+ if __debug__:
+ self.printCurrentView("__mailSuccess")
- str = "recipients"
+ self.mailMessage.dateSent = DateTime.now()
+ self.mailMessage.dateSentString = message.dateTimeToRFC2882Date(DateTime.now())
- if len(addrs) == 1:
- str = "recipient"
+ self.mailMessage.deliveryExtension.sendSucceeded()
- info = "SMTP Message sent to %d %s [%s]" % (result[0], str, ", ".join(addrs))
- self.log.info(info)
+ addrs = []
- self.mailMessage.dateSent = DateTime.now()
- self.mailMessage.dateSentString = message.dateTimeToRFC2882Date(DateTime.now())
+ for address in result[1]:
+ addrs.append(address[0])
- self.mailMessage.deliveryExtension.sendSucceeded()
+ info = "SMTP Message sent to [%s]" % (", ".join(addrs))
+ self.log.info(info)
- self.success = result
+ self.success = result
- finally:
- self.restorePreviousView()
+ def __mailSomeFailed(self, result):
+ """
+ result: (NumOk, [(emailAddress, serverStatusCode, serverResponseString)])
+ Collect all results that do not have a 250 and form a string for .4B
+ """
+
+ if __debug__:
+ self.printCurrentView("__mailSomeFailed")
+
+ """Clear out any errors from a previous attempt"""
+ self.mailMessage.deliveryExtension.deliveryErrors = []
+
+ errorDate = DateTime.now()
+
+ for recipient in result[1]:
+ email, code, str = recipient
+ if recipient[1] != SMTPConstants.SUCCESS:
+ deliveryError = Mail.MailDeliveryError()
+ deliveryError.errorCode = code
+ deliveryError.errorString = str
+ deliveryError.errorDate = errorDate
+ self.mailMessage.deliveryExtension.deliveryErrors.append(deliveryError)
+
+ self.mailMessage.deliveryExtension.sendFailed()
+
+ #XXX: Need to revisit this logic
+ self.failure = result[1]
+
+ s = []
+ s.append("SMTP Send failed for the following recipients:")
+
+ for deliveryError in self.mailMessage.deliveryExtension.deliveryErrors:
+ s.append(deliveryError.__str__())
+
+ self.log.error("\n".join(s))
- """Commit the view in a thread to prevent blocking"""
- self.commitView(True)
def __mailFailure(self, exc):
self.setViewCurrent()
@@ -178,11 +228,15 @@
if __debug__:
self.printCurrentView("__mailFailure")
- print exc
- self.log.error("SMTP send failed: %s" % exc)
-
+ self.__recordError(exc.value)
self.mailMessage.deliveryExtension.sendFailed()
- self.failure = exc
+
+ #XXX Not sure we need this anymore
+ self.failure = exc.value
+
+ for deliveryError in self.mailMessage.deliveryExtension.deliveryErrors:
+ s = "SMTP send failed: %s" % deliveryError
+ self.log.error(s)
finally:
self.restorePreviousView()
@@ -190,6 +244,89 @@
"""Commit the view in a thread to prevent blocking"""
self.commitView(True)
+ def __recordError(self, err):
+ deliveryError = Mail.MailDeliveryError()
+
+ """Clear out any errors from a previous attempt"""
+ self.mailMessage.deliveryExtension.deliveryErrors = []
+
+ if isinstance(err, smtp.SMTPClientError):
+ """Base type for all SMTP Related Errors.
+ Capture all the errors that may return -1 as the
+ error code and record the actual error code. Those
+ SMTPClientError's that slip through will have the
+ correct error code from the smtp server.
+ """
+
+ if isinstance(err, smtp.SMTPDeliveryError):
+ if err.code == -1:
+ deliveryError.errorCode = errorCode.DELIVERY_ERROR
+
+ elif isinstance(err, smtp.SMTPConnectError):
+ if err.code == -1:
+ deliveryError.errorCode = errorCode.CONNECTION_ERROR
+
+ elif isinstance(err, smtp.SMTPProtocolError):
+ if err.code == -1:
+ deliveryError.errorCode = errorCode.PROTOCOL_ERROR
+
+ if err.code != -1:
+ deliveryError.errorCode = err.code
+
+ deliveryError.errorString = err.resp
+ deliveryError.errorDate = DateTime.now()
+
+ elif isinstance(err, error.ConnectError):
+ """ Record the error code of a ConnectionError.
+ If a ConnectionError occurs then there was
+ a problem communicating with a SMTP server
+ and no error code will be returned by theA
+ server."""
+
+ if isinstance(err, error.ConnectBindError):
+ deliveryError.errorCode = errorCode.BIND_ERROR
+
+ elif isinstance(err, error.UnknownHostError):
+ deliveryError.errorCode = errorCode.UNKNOWN_HOST_ERROR
+
+ elif isinstance(err, error.TimeoutError):
+ deliveryError.errorCode = errorCode.TIMEOUT_ERROR
+
+ elif isinstance(err, error.SSLError):
+ deliveryError.errorCode = errorCode.SSL_ERROR
+
+ elif isinstance(err, error.ConnectionRefusedError):
+ deliveryError.errorCode = errorCode.CONNECTION_REFUSED_ERROR
+
+ else:
+ deliveryError.errorCode = errorCode.UNKNOWN_ERROR
+ self.log.error("Unknown TCP Error encountered docString: ", err.__doc__)
+
+
+ deliveryError.errorString = err.__str__()
+ deliveryError.errorDate = DateTime.now()
+
+ elif isinstance(err, error.DNSLookupError):
+ deliveryError.errorCode = errorCode.DNS_LOOKUP_ERROR
+ deliveryError.errorString = err.__str__()
+ deliveryError.errorDate = DateTime.now()
+
+ elif isinstance(err, Exception):
+ deliveryError.errorCode = errorCode.UNKNOWN_ERROR
+ deliveryError.errorString = err.__str__()
+ deliveryError.errorDate = DateTime.now()
+ s = "Unknown Exception encountered docString: %s module: %s" % (err.__doc__, err.__module__)
+ self.log.error(s)
+
+ else:
+ deliveryError.errorCode = errorCode.UNKNOWN_ERROR
+ deliveryError.errorString = err.__str__() + " UNKNOWN TYPE NOT A EXCEPTION"
+ deliveryError.errorDate = DateTime.now()
+ s = "Unknown Non-Exception encountered docString: %s module: %s" % (err.__doc__, err.__module__)
+ self.log.error(s)
+
+ self.mailMessage.deliveryExtension.deliveryErrors.append(deliveryError)
+
def _viewCommitSuccess(self):
"""
@@ -199,7 +336,6 @@
@return: C{None}
"""
-
self.account.setPinned(False)
self.mailMessage.setPinned(False)
self.account = None
More information about the Commits
mailing list