[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