[CalendarServer-changes] [2771] CalendarServer/branches/users/sagen/mailgateway-implicit-2745/ twistedcaldav/mail.py

source_changes at macosforge.org source_changes at macosforge.org
Tue Aug 5 10:28:17 PDT 2008


Revision: 2771
          http://trac.macosforge.org/projects/calendarserver/changeset/2771
Author:   sagen at apple.com
Date:     2008-08-05 10:28:16 -0700 (Tue, 05 Aug 2008)
Log Message:
-----------
Refactoring so message handling code is shared between POP and IMAP

Modified Paths:
--------------
    CalendarServer/branches/users/sagen/mailgateway-implicit-2745/twistedcaldav/mail.py

Modified: CalendarServer/branches/users/sagen/mailgateway-implicit-2745/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/users/sagen/mailgateway-implicit-2745/twistedcaldav/mail.py	2008-08-05 17:03:13 UTC (rev 2770)
+++ CalendarServer/branches/users/sagen/mailgateway-implicit-2745/twistedcaldav/mail.py	2008-08-05 17:28:16 UTC (rev 2771)
@@ -50,8 +50,6 @@
 ]
 
 
-# TODO: use @inlineCallbacks
-
 #
 # Mail gateway service config
 #
@@ -282,6 +280,7 @@
             values (:1, :2, :3)
             """, token, organizer, attendee
         )
+        self._db_commit()
         return token
 
     def lookupByToken(self, token):
@@ -464,7 +463,37 @@
         self.db = MailGatewayTokensDatabase(config.DataRoot)
 
     @inlineCallbacks
-    def inbound(self, token, calendar):
+    def inbound(self, message):
+        parsedMessage = email.message_from_string(message)
+
+        # TODO: make sure this is an email message we want to handle
+
+        # extract the token from the To header
+        name, addr = email.utils.parseaddr(parsedMessage['To'])
+        if addr:
+            # addr looks like: server_address+token at example.com
+            try:
+                pre, post = addr.split('@')
+                pre, token = pre.split('+')
+            except ValueError:
+                # TODO: handle this error
+                return
+        else:
+            # TODO: handle this error
+            return
+
+        for part in parsedMessage.walk():
+            if part.get_content_type() == "text/calendar":
+                calBody = part.get_payload(decode=True)
+                break
+        else:
+            # TODO: handle this condition
+            # No icalendear attachment
+            return
+
+        self.log_debug(calBody)
+        calendar = ical.Component.fromString(calBody)
+
         # process mail messages from POP or IMAP, inject to calendar server
         result = self.db.lookupByToken(token)
         if result is None:
@@ -505,7 +534,7 @@
         toAddr = attendee
         message = message.replace("${fromaddress}", fromAddr)
         message = message.replace("${replytoaddress}", organizer)
-        
+
         if not attendee.startswith("mailto:"):
             raise ValueError("ATTENDEE address '%s' must be mailto: for iMIP operation." % (attendee,))
         attendee = attendee[7:]
@@ -519,7 +548,7 @@
         caldata = str(calendar)
         data = cStringIO.StringIO()
         writer = MimeWriter.MimeWriter(data)
-    
+
         writer.addheader("From", "${fromaddress}")
         writer.addheader("Reply-To", "${replytoaddress}")
         writer.addheader("To", "${toaddress}")
@@ -528,14 +557,14 @@
         writer.addheader("Message-ID", messageid())
         writer.addheader("Mime-Version", "1.0")
         writer.flushheaders()
-    
+
         writer.startmultipartbody("mixed")
-    
+
         # message body
         part = writer.nextpart()
         body = part.startbody("text/plain")
         body.write("Hi, You've been invited to a cool event by CalendarServer's new iMIP processor.  %s " % (self._generateCalendarSummary(calendar),))
-    
+
         part = writer.nextpart()
         encoding = "7bit"
         for i in caldata:
@@ -546,7 +575,7 @@
         part.addheader("Content-Transfer-Encoding", encoding)
         body = part.startbody("text/calendar; charset=utf-8")
         body.write(caldata.replace("\r", ""))
-    
+
         # finish
         writer.lastpart()
 
@@ -558,15 +587,15 @@
         component = calendar.masterComponent()
         if component is None:
             component = calendar.mainComponent(True)
-            
+
         organizer = component.getOrganizerProperty()
         if "CN" in organizer.params():
             organizer = "%s <%s>" % (organizer.params()["CN"][0], organizer.value(),)
         else:
             organizer = organizer.value()
-            
+
         dtinfo = self._getDateTimeInfo(component)
-        
+
         summary = component.propertyValue("SUMMARY")
         if summary is None:
             summary = ""
@@ -585,7 +614,7 @@
 """ % (organizer, summary, dtinfo, description,)
 
     def _getDateTimeInfo(self, component):
-        
+
         dtstart = component.propertyNativeValue("DTSTART")
         tzid_start = component.getProperty("DTSTART").params().get("TZID", "UTC")
 
@@ -610,19 +639,19 @@
         if dtend is not None:
             result += "Ends:        %s\n" % (self._getDateTimeText(dtend, tzid_end),)
         result += "Duration:    %s\n" % (self._getDurationText(duration),)
-        
+
         if not isinstance(dtstart, datetime.datetime):
             result += "All Day\n"
-        
+
         for property_name in ("RRULE", "RDATE", "EXRULE", "EXDATE", "RECURRENCE-ID",):
             if component.hasProperty(property_name):
                 result += "Recurring\n"
                 break
-            
+
         return result
 
     def _getDateTimeText(self, dtvalue, tzid):
-        
+
         if isinstance(dtvalue, datetime.datetime):
             timeformat = "%A, %B %e, %Y %I:%M %p"
         elif isinstance(dtvalue, datetime.date):
@@ -632,9 +661,9 @@
             tzid = " (%s)" % (tzid,)
 
         return "%s%s" % (dtvalue.strftime(timeformat), tzid,)
-        
+
     def _getDurationText(self, duration):
-        
+
         result = ""
         if duration.days > 0:
             result += "%d %s" % (
@@ -645,7 +674,7 @@
         hours = duration.seconds / 3600
         minutes = divmod(duration.seconds / 60, 60)[1]
         seconds = divmod(duration.seconds, 60)[1]
-        
+
         if hours > 0:
             if result:
                 result += ", "
@@ -653,7 +682,7 @@
                 hours,
                 self._pluralize(hours, "hour", "hours")
             )
-        
+
         if minutes > 0:
             if result:
                 result += ", "
@@ -661,7 +690,7 @@
                 minutes,
                 self._pluralize(minutes, "minute", "minutes")
             )
-        
+
         if seconds > 0:
             if result:
                 result += ", "
@@ -710,7 +739,7 @@
     allowInsecureLogin = False
 
     def serverGreeting(self, greeting):
-        self.log_info("POP servergreeting")
+        self.log_debug("POP servergreeting")
         pop3client.POP3Client.serverGreeting(self, greeting)
         login = self.login(self.factory.settings["Username"],
             self.factory.settings["Password"])
@@ -723,24 +752,24 @@
         return self.quit()
 
     def cbLoggedIn(self, result):
-        self.log_info("POP loggedin")
+        self.log_debug("POP loggedin")
         return self.listSize().addCallback(self.cbGotMessageSizes)
 
     def cbGotMessageSizes(self, sizes):
-        self.log_info("POP gotmessagesizes")
+        self.log_debug("POP gotmessagesizes")
         downloads = []
         for i in range(len(sizes)):
             downloads.append(self.retrieve(i).addCallback(self.cbDownloaded, i))
         return defer.DeferredList(downloads).addCallback(self.cbFinished)
 
     def cbDownloaded(self, lines, id):
-        self.log_info("POP downloaded message %d" % (id,))
+        self.log_debug("POP downloaded message %d" % (id,))
         self.factory.handleMessage("\r\n".join(lines))
-        self.log_info("POP deleting message %d" % (id,))
+        self.log_debug("POP deleting message %d" % (id,))
         self.delete(id)
 
     def cbFinished(self, results):
-        self.log_info("POP finished")
+        self.log_debug("POP finished")
         return self.quit()
 
 
@@ -769,13 +798,13 @@
             self.nextPoll = None
             connector.connect()
 
-        self.log_info("Scheduling next POP3 poll")
+        self.log_debug("Scheduling next POP3 poll")
         self.nextPoll = self.reactor.callLater(self.settings["PollingSeconds"],
             reconnector)
 
     def clientConnectionLost(self, connector, reason):
         self.connector = connector
-        self.log_info("POP factory connection lost")
+        self.log_debug("POP factory connection lost")
         self.retry(connector)
 
 
@@ -786,42 +815,14 @@
 
     @inlineCallbacks
     def handleMessage(self, message):
-        self.log_info("POP factory handle message")
-        self.log_info(message)
-        parsedMessage = email.message_from_string(message)
+        self.log_debug("POP factory handle message")
+        self.log_debug(message)
 
-        # TODO: make sure this is an email message we want to handle
+        yield self.mailer.inbound(message)
 
-        # extract the token from the To header
-        name, addr = email.utils.parseaddr(parsedMessage['To'])
-        if addr:
-            # addr looks like: server_address+token at example.com
-            try:
-                pre, post = addr.split('@')
-                pre, token = pre.split('+')
-            except ValueError:
-                # TODO: handle this error
-                return
-        else:
-            # TODO: handle this error
-            return
 
-        for part in parsedMessage.walk():
-            if part.get_content_type() == "text/calendar":
-                calBody = part.get_payload(decode=True)
-                break
-        else:
-            # TODO: handle this condition
-            # No icalendear attachment
-            return
 
-        self.log_info(calBody)
-        calendar = ical.Component.fromString(calBody)
-        yield self.mailer.inbound(token, calendar)
 
-
-
-
 #
 # IMAP4
 #
@@ -852,7 +853,7 @@
 class IMAP4DownloadProtocol(imap4.IMAP4Client, LoggingMixIn):
 
     def serverGreeting(self, capabilities):
-        self.log_info("IMAP servergreeting")
+        self.log_debug("IMAP servergreeting")
         return self.login(self.factory.settings["Username"],
             self.factory.settings["Password"]).addCallback(self.cbLoggedIn)
 
@@ -865,42 +866,42 @@
         return self.transport.loseConnection()
 
     def cbLoggedIn(self, result):
-        self.log_info("IMAP logged in [%s]" % (self.state,))
+        self.log_debug("IMAP logged in [%s]" % (self.state,))
         return self.select("Inbox").addCallback(self.cbInboxSelected)
 
     def cbInboxSelected(self, result):
-        self.log_info("IMAP Inbox selected [%s]" % (self.state,))
+        self.log_debug("IMAP Inbox selected [%s]" % (self.state,))
         allMessages = imap4.MessageSet(1, None)
         return self.fetchUID(allMessages, True).addCallback(self.cbGotUIDs)
 
     def cbGotUIDs(self, results):
-        self.log_info("IMAP got uids [%s]" % (self.state,))
+        self.log_debug("IMAP got uids [%s]" % (self.state,))
         self.messageUIDs = [result['UID'] for result in results.values()]
         self.messageCount = len(self.messageUIDs)
-        self.log_info("IMAP Inbox has %d messages" % (self.messageCount,))
+        self.log_debug("IMAP Inbox has %d messages" % (self.messageCount,))
         return self.fetchNextMessage()
 
     def fetchNextMessage(self):
-        self.log_info("IMAP in fetchnextmessage [%s]" % (self.state,))
+        self.log_debug("IMAP in fetchnextmessage [%s]" % (self.state,))
         if self.messageUIDs:
             nextUID = self.messageUIDs.pop(0)
             messageListToFetch = imap4.MessageSet(nextUID)
-            self.log_info("Downloading message %d of %d (%s)" %
+            self.log_debug("Downloading message %d of %d (%s)" %
                 (self.messageCount - len(self.messageUIDs), self.messageCount,
                 nextUID))
             return self.fetchMessage(messageListToFetch, True).addCallback(
                 self.cbGotMessage, messageListToFetch).addErrback(self.ebLogError)
         else:
-            self.log_info("All messages downloaded")
+            self.log_debug("All messages downloaded")
             return self.close().addCallback(self.cbClosed)
 
     def cbGotMessage(self, results, messageList):
-        self.log_info("IMAP in cbGotMessage [%s]" % (self.state,))
+        self.log_debug("IMAP in cbGotMessage [%s]" % (self.state,))
         try:
             messageData = results.values()[0]['RFC822']
         except IndexError:
             # not sure what happened, but results is empty
-            self.log_info("Skipping empty results")
+            self.log_error("Skipping empty results")
             return self.fetchNextMessage()
 
         self.factory.handleMessage(messageData)
@@ -908,22 +909,22 @@
             uid=True).addCallback(self.cbMessageDeleted, messageList)
 
     def cbMessageDeleted(self, results, messageList):
-        self.log_info("IMAP in cbMessageDeleted [%s]" % (self.state,))
-        self.log_info("Deleted message")
+        self.log_debug("IMAP in cbMessageDeleted [%s]" % (self.state,))
+        self.log_debug("Deleted message")
         self.fetchNextMessage()
 
     def cbClosed(self, results):
-        self.log_info("IMAP in cbClosed [%s]" % (self.state,))
-        self.log_info("Mailbox closed")
+        self.log_debug("IMAP in cbClosed [%s]" % (self.state,))
+        self.log_debug("Mailbox closed")
         return self.logout().addCallback(
             lambda _: self.transport.loseConnection())
 
     def lineReceived(self, line):
-        self.log_info("RECEIVED: %s" % (line,))
+        self.log_debug("RECEIVED: %s" % (line,))
         imap4.IMAP4Client.lineReceived(self, line)
 
     def sendLine(self, line):
-        self.log_info("SENDING: %s" % (line,))
+        self.log_debug("SENDING: %s" % (line,))
         imap4.IMAP4Client.sendLine(self, line)
 
 
@@ -931,7 +932,7 @@
     protocol = IMAP4DownloadProtocol
 
     def __init__(self, settings, mailer, reactor=None):
-        self.log_info("Setting up IMAPFactory")
+        self.log_debug("Setting up IMAPFactory")
 
         self.settings = settings
         self.mailer = mailer
@@ -941,12 +942,12 @@
 
 
     def handleMessage(self, message):
-        self.log_info("IMAP factory handle message")
-        self.log_info(message)
-        parsedMessage = email.message_from_string(message)
-        # TODO: messages can be handed off here...
+        self.log_debug("IMAP factory handle message")
+        self.log_debug(message)
 
+        yield self.mailer.inbound(message)
 
+
     def retry(self, connector=None):
         # TODO: if connector is None:
 
@@ -961,16 +962,16 @@
             self.nextPoll = None
             connector.connect()
 
-        self.log_info("Scheduling next IMAP4 poll")
+        self.log_debug("Scheduling next IMAP4 poll")
         self.nextPoll = self.reactor.callLater(self.settings["PollingSeconds"],
             reconnector)
 
     def clientConnectionLost(self, connector, reason):
         self.connector = connector
-        self.log_info("IMAP factory connection lost")
+        self.log_debug("IMAP factory connection lost")
         self.retry(connector)
 
     def clientConnectionFailed(self, connector, reason):
         self.connector = connector
-        self.log_info("IMAP factory connection failed")
+        self.log_error("IMAP factory connection failed")
         self.retry(connector)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080805/ea8c1614/attachment-0001.html 


More information about the calendarserver-changes mailing list