diff --git a/integrator/README.txt b/integrator/README.txt
index 0630095..43af8fa 100644
--- a/integrator/README.txt
+++ b/integrator/README.txt
@@ -38,7 +38,7 @@ of the 'extcollection' type. We use an adapter for providing the attributes
and methods of the external collect object.
>>> from loops.concept import Concept
- >>> from loops.setup import addObject
+ >>> from loops.setup import addObject, addAndConfigureObject
>>> from loops.integrator.collection import ExternalCollectionAdapter
>>> tExternalCollection = concepts['extcollection']
>>> coll01 = addObject(concepts, Concept, 'coll01',
@@ -141,10 +141,10 @@ Mail Collections
>>> tType = concepts['type']
>>> from loops.integrator.mail.interfaces import IMailCollection, IMailResource
- >>> tMailCollection = addObject(concepts, Concept, 'mailcollection',
+ >>> tMailCollection = addAndConfigureObject(concepts, Concept, 'mailcollection',
... title=u'Mail Collection', conceptType=tType,
... typeInterface=IMailCollection)
- >>> tMailResource = addObject(concepts, Concept, 'email',
+ >>> tMailResource = addAndConfigureObject(concepts, Concept, 'email',
... title=u'Mail Resource', conceptType=tType,
... typeInterface=IMailResource)
@@ -157,17 +157,28 @@ Mail Collections
An external collection carries a set of attributes that control the access
to the external system:
- >>> (aMailColl.providerName, aMailColl.baseAddress,
- ... aMailColl.address, aMailColl.pattern)
- (u'imap', None, None, None)
+ >>> aMailColl.userName = u'jim'
+ >>> (aMailColl.providerName, aMailColl.baseAddress, aMailColl.address,
+ ... aMailColl.pattern, aMailColl.userName)
+ (u'imap', None, None, None, u'jim')
>>> from loops.integrator.mail import testing
>>> from loops.integrator.mail.imap import IMAPCollectionProvider
>>> component.provideUtility(IMAPCollectionProvider(), name='imap')
+ >>> from loops.integrator.mail.resource import MailResource
+ >>> component.provideAdapter(MailResource, provides=IMailResource)
>>> aMailColl.update()
- *** 1 ...
+
+ >>> aMail = adapted(mailColl.getResources()[0])
+
+ >>> aMail.date, aMail.sender, aMail.receiver, aMail.title
+ (datetime.datetime(...), 'ceo@cy55.de', 'ceo@example.org', 'Blogging from Munich')
+ >>> aMail.data
+ '
Blogging from ...
\n'
+ >>> aMail.externalAddress
+ u'imap://jim@merz12/20081208171745.e4ce2xm96cco80cg@cy55.de'
Uploading Resources with HTTP PUT Requests
diff --git a/integrator/collection.py b/integrator/collection.py
index 906bbcc..4203fe7 100644
--- a/integrator/collection.py
+++ b/integrator/collection.py
@@ -84,7 +84,7 @@ class ExternalCollectionAdapter(AdapterBase):
# may be it would be better to return a file's hash
# for checking for changes...
oldFound.append(addr)
- if mdate > self.lastUpdated:
+ if mdate and mdate > self.lastUpdated:
# force reindexing
notify(ObjectModifiedEvent(old[addr]))
else:
diff --git a/integrator/mail/configure.zcml b/integrator/mail/configure.zcml
index 70b230a..2594c6d 100644
--- a/integrator/mail/configure.zcml
+++ b/integrator/mail/configure.zcml
@@ -20,4 +20,15 @@
factory="loops.integrator.mail.imap.IMAPCollectionProvider"
name="imap" />
+
+
+
+
+
+
+
diff --git a/integrator/mail/imap.py b/integrator/mail/imap.py
index 9b154fa..94ec66a 100644
--- a/integrator/mail/imap.py
+++ b/integrator/mail/imap.py
@@ -24,14 +24,18 @@ $Id$
"""
from datetime import datetime
+import email
from logging import getLogger
+import os
+import time
from zope.app.container.interfaces import INameChooser
from zope.cachedescriptors.property import Lazy
from zope import component
from zope.component import adapts
from zope.event import notify
-from zope.lifecycleevent import ObjectModifiedEvent
+from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent
+from zope.event import notify
from zope.interface import implements
from zope.traversing.api import getName, getParent
@@ -51,6 +55,10 @@ class IMAPCollectionProvider(object):
def collect(self, client):
client._collectedObjects = {}
+ hostName = client.baseAddress
+ if hostName in ('', None, 'localhost'):
+ hostName = os.uname()[1]
+ baseId = 'imap://%s@%s' % (client.userName, hostName)
imap = IMAP4(client.baseAddress)
imap.login(client.userName, client.password)
mailbox = 'INBOX'
@@ -61,11 +69,13 @@ class IMAPCollectionProvider(object):
type, data = imap.search(None, 'ALL')
for num in data[0].split():
type, data = imap.fetch(num, '(RFC822)')
- externalAddress = num
- obj = data
- mtime = datetime.today()
- client._collectedObjects[externalAddress] = obj
- yield externalAddress, mtime
+ raw_msg = data[0][1]
+ msg = email.message_from_string(raw_msg)
+ msgId = msg['Message-ID'].replace('<', '').replace('>', '')
+ externalAddress = '/'.join((baseId, msgId))
+ #mtime = datetime.today()
+ client._collectedObjects[externalAddress] = msg
+ yield externalAddress, None
def createExtFileObjects(self, client, addresses, extFileTypes=None):
loopsRoot = client.context.getLoopsRoot()
@@ -73,16 +83,43 @@ class IMAPCollectionProvider(object):
contentType = 'text/plain'
resourceType = loopsRoot.getConceptManager()['email']
for addr in addresses:
- print '***', addr, client._collectedObjects[addr]
- return []
-
- def _dummy(self):
- name = self.generateName(container, addr)
- title = self.generateTitle(addr)
- obj = addAndConfigureObject(
- container, Resource, name,
- title=title,
- resourceType=extFileType,
- contentType=contentType,
- )
+ msg = client._collectedObjects[addr]
+ title = msg['Subject']
+ sender = msg['From']
+ receiver = msg['To']
+ raw_date = msg['Date'].rsplit(' ', 1)[0]
+ fmt = '%a, %d %b %Y %H:%M:%S'
+ date = datetime(*(time.strptime(raw_date, fmt)[0:6]))
+ parts = self.getPayload(msg, {})
+ if 'html' in parts:
+ text = parts['html']
+ ct = 'text/html'
+ else:
+ text = parts.get('plain') or u'No message found.'
+ ct = 'text/plain'
+ obj = Resource(title)
+ name = INameChooser(container).chooseName(None, obj)
+ container[name] = obj
+ obj.resourceType = resourceType
+ adObj = adapted(obj)
+ adObj.externalAddress = addr
+ adObj.contentType = ct
+ adObj.sender = sender
+ adObj.receiver = receiver
+ adObj.date = date
+ adObj.data = text
+ notify(ObjectCreatedEvent(obj))
+ notify(ObjectModifiedEvent(obj))
yield obj
+
+ def getPayload(self, msg, parts):
+ if msg.is_multipart():
+ for part in msg.get_payload():
+ self.getPayload(part, parts)
+ else:
+ ct = msg['Content-Type']
+ if ct and ct.startswith('text/html'):
+ parts['html'] = msg.get_payload()
+ if ct and ct.startswith('text/plain'):
+ parts['plain'] = msg.get_payload()
+ return parts
diff --git a/integrator/mail/interfaces.py b/integrator/mail/interfaces.py
index 387e2b0..7a1935b 100644
--- a/integrator/mail/interfaces.py
+++ b/integrator/mail/interfaces.py
@@ -41,14 +41,13 @@ class IMailCollection(IExternalCollection):
providerName = schema.TextLine(
title=_(u'Provider name'),
description=_(u'The name of a utility that provides the '
- u'external objects; default is an IMAP '
- u'collection provider.'),
+ u'external objects; default is an IMAP collection provider.'),
default=u'imap',
required=False)
baseAddress = schema.TextLine(
title=_(u'Host name'),
description=_(u'The host name part for accessing the '
- u'external mail system, i.e. the mail server.'),
+ u'external mail system, i.e. the mail server.'),
required=True)
userName = schema.TextLine(
title=_(u'User name'),
@@ -67,9 +66,22 @@ class IMailResource(ITextDocument):
"""
externalAddress = schema.BytesLine(
- title=_(u'External Address'),
- description=_(u'The full address of the email in the external '
- u'email system.'),
- default='',
- missing_value='',
- required=False)
+ title=_(u'External Address'),
+ description=_(u'The full address of the email in the external '
+ u'email system.'),
+ default='',
+ missing_value='',
+ required=False)
+ sender = schema.TextLine(
+ title=_(u'Sender'),
+ description=_(u'The email address of sender of the email.'),
+ required=False)
+ receiver = schema.TextLine(
+ title=_(u'Receiver'),
+ description=_(u'One or more email addresses of the receiver(s) of '
+ u'the email message.'),
+ required=False)
+ date = schema.Date(
+ title=_(u'Date'),
+ description=_(u'The date/time the email message was sent.'),
+ required=False,)
diff --git a/integrator/mail/testing.py b/integrator/mail/testing.py
index 9c6955f..d2920a2 100644
--- a/integrator/mail/testing.py
+++ b/integrator/mail/testing.py
@@ -22,7 +22,7 @@ Fake access to system libraries for testing.
$Id$
"""
-data = [('1 (RFC822 {7785}', 'Return-Path: \r\nMessage-ID: <20081208171745.e4ce2xm96cco80cg@cy55.de>\r\nDate: Mon, 8 Dec 2008 17:17:45 +0100\r\nFrom: ceo@cy55.de\r\nTo: ceo@example.org\r\n\r\nThis message is in MIME format.\r\n\r\n--===============0371775080==\r\nContent-Type: multipart/alternative;\r\n\tboundary="=_gtkn1bgzg6g"\r\nContent-Transfer-Encoding: 7bit\r\n\r\nThis message is in MIME format.\r\n\r\n--=_gtkn1bgzg6g\r\nContent-Type: text/plain;\r\n\tcharset=ISO-8859-1;\r\n\tDelSp="Yes";\r\n\tformat="flowed"\r\nContent-Description: Plaintext Version of Message\r\nContent-Disposition: inline\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n\r\n\r\n BLOGGING FROM ...\r\n.\r\n\r\n\r\n--=_gtkn1bgzg6g\r\nContent-Type: text/html;\r\n\tcharset=ISO-8859-1\r\nContent-Description: HTML Version of Message\r\nContent-Disposition: inline\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nBlogging from ...
\r\n\r\n'), ')']
+data = [('1 (RFC822 {7785}', 'Return-Path: \r\nMessage-ID: <20081208171745.e4ce2xm96cco80cg@cy55.de>\r\nDate: Mon, 8 Dec 2008 17:17:45 +0100\r\nFrom: ceo@cy55.de\r\nTo: ceo@example.org\r\nMIME-Version: 1.0\r\nSubject: Blogging from Munich\r\nContent-Type: multipart/mixed;\r\n\tboundary="===============0371775080=="\r\n\r\nThis message is in MIME format.\r\n\r\n--===============0371775080==\r\nContent-Type: multipart/alternative;\r\n\tboundary="=_gtkn1bgzg6g"\r\nContent-Transfer-Encoding: 7bit\r\n\r\nThis message is in MIME format.\r\n\r\n--=_gtkn1bgzg6g\r\nContent-Type: text/plain;\r\n\tcharset=ISO-8859-1;\r\n\tDelSp="Yes";\r\n\tformat="flowed"\r\nContent-Description: Plaintext Version of Message\r\nContent-Disposition: inline\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n\r\n\r\n BLOGGING FROM ...\r\n.\r\n\r\n\r\n--=_gtkn1bgzg6g\r\nContent-Type: text/html;\r\n\tcharset=ISO-8859-1\r\nContent-Description: HTML Version of Message\r\nContent-Disposition: inline\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nBlogging from ...
\r\n\r\n'), ')']
class IMAP4(object):