mail (IMAP) integration basically working
git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@3537 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
90b67d1265
commit
a78ec01d5c
6 changed files with 107 additions and 36 deletions
|
@ -38,7 +38,7 @@ of the 'extcollection' type. We use an adapter for providing the attributes
|
||||||
and methods of the external collect object.
|
and methods of the external collect object.
|
||||||
|
|
||||||
>>> from loops.concept import Concept
|
>>> from loops.concept import Concept
|
||||||
>>> from loops.setup import addObject
|
>>> from loops.setup import addObject, addAndConfigureObject
|
||||||
>>> from loops.integrator.collection import ExternalCollectionAdapter
|
>>> from loops.integrator.collection import ExternalCollectionAdapter
|
||||||
>>> tExternalCollection = concepts['extcollection']
|
>>> tExternalCollection = concepts['extcollection']
|
||||||
>>> coll01 = addObject(concepts, Concept, 'coll01',
|
>>> coll01 = addObject(concepts, Concept, 'coll01',
|
||||||
|
@ -141,10 +141,10 @@ Mail Collections
|
||||||
|
|
||||||
>>> tType = concepts['type']
|
>>> tType = concepts['type']
|
||||||
>>> from loops.integrator.mail.interfaces import IMailCollection, IMailResource
|
>>> from loops.integrator.mail.interfaces import IMailCollection, IMailResource
|
||||||
>>> tMailCollection = addObject(concepts, Concept, 'mailcollection',
|
>>> tMailCollection = addAndConfigureObject(concepts, Concept, 'mailcollection',
|
||||||
... title=u'Mail Collection', conceptType=tType,
|
... title=u'Mail Collection', conceptType=tType,
|
||||||
... typeInterface=IMailCollection)
|
... typeInterface=IMailCollection)
|
||||||
>>> tMailResource = addObject(concepts, Concept, 'email',
|
>>> tMailResource = addAndConfigureObject(concepts, Concept, 'email',
|
||||||
... title=u'Mail Resource', conceptType=tType,
|
... title=u'Mail Resource', conceptType=tType,
|
||||||
... typeInterface=IMailResource)
|
... typeInterface=IMailResource)
|
||||||
|
|
||||||
|
@ -157,17 +157,28 @@ Mail Collections
|
||||||
An external collection carries a set of attributes that control the access
|
An external collection carries a set of attributes that control the access
|
||||||
to the external system:
|
to the external system:
|
||||||
|
|
||||||
>>> (aMailColl.providerName, aMailColl.baseAddress,
|
>>> aMailColl.userName = u'jim'
|
||||||
... aMailColl.address, aMailColl.pattern)
|
>>> (aMailColl.providerName, aMailColl.baseAddress, aMailColl.address,
|
||||||
(u'imap', None, None, None)
|
... aMailColl.pattern, aMailColl.userName)
|
||||||
|
(u'imap', None, None, None, u'jim')
|
||||||
|
|
||||||
>>> from loops.integrator.mail import testing
|
>>> from loops.integrator.mail import testing
|
||||||
|
|
||||||
>>> from loops.integrator.mail.imap import IMAPCollectionProvider
|
>>> from loops.integrator.mail.imap import IMAPCollectionProvider
|
||||||
>>> component.provideUtility(IMAPCollectionProvider(), name='imap')
|
>>> component.provideUtility(IMAPCollectionProvider(), name='imap')
|
||||||
|
>>> from loops.integrator.mail.resource import MailResource
|
||||||
|
>>> component.provideAdapter(MailResource, provides=IMailResource)
|
||||||
|
|
||||||
>>> aMailColl.update()
|
>>> 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
|
||||||
|
'<p><b>Blogging from ...</b><br />\n'
|
||||||
|
>>> aMail.externalAddress
|
||||||
|
u'imap://jim@merz12/20081208171745.e4ce2xm96cco80cg@cy55.de'
|
||||||
|
|
||||||
|
|
||||||
Uploading Resources with HTTP PUT Requests
|
Uploading Resources with HTTP PUT Requests
|
||||||
|
|
|
@ -84,7 +84,7 @@ class ExternalCollectionAdapter(AdapterBase):
|
||||||
# may be it would be better to return a file's hash
|
# may be it would be better to return a file's hash
|
||||||
# for checking for changes...
|
# for checking for changes...
|
||||||
oldFound.append(addr)
|
oldFound.append(addr)
|
||||||
if mdate > self.lastUpdated:
|
if mdate and mdate > self.lastUpdated:
|
||||||
# force reindexing
|
# force reindexing
|
||||||
notify(ObjectModifiedEvent(old[addr]))
|
notify(ObjectModifiedEvent(old[addr]))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -20,4 +20,15 @@
|
||||||
factory="loops.integrator.mail.imap.IMAPCollectionProvider"
|
factory="loops.integrator.mail.imap.IMAPCollectionProvider"
|
||||||
name="imap" />
|
name="imap" />
|
||||||
|
|
||||||
|
<zope:adapter factory="loops.integrator.mail.resource.MailResource"
|
||||||
|
provides="loops.integrator.mail.interfaces.IMailResource"
|
||||||
|
trusted="True" />
|
||||||
|
|
||||||
|
<zope:class class="loops.integrator.mail.resource.MailResource">
|
||||||
|
<require permission="zope.View"
|
||||||
|
interface="loops.integrator.mail.interfaces.IMailResource" />
|
||||||
|
<require permission="zope.ManageContent"
|
||||||
|
set_schema="loops.integrator.mail.interfaces.IMailResource" />
|
||||||
|
</zope:class>
|
||||||
|
|
||||||
</configure>
|
</configure>
|
||||||
|
|
|
@ -24,14 +24,18 @@ $Id$
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import email
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
from zope.app.container.interfaces import INameChooser
|
from zope.app.container.interfaces import INameChooser
|
||||||
from zope.cachedescriptors.property import Lazy
|
from zope.cachedescriptors.property import Lazy
|
||||||
from zope import component
|
from zope import component
|
||||||
from zope.component import adapts
|
from zope.component import adapts
|
||||||
from zope.event import notify
|
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.interface import implements
|
||||||
from zope.traversing.api import getName, getParent
|
from zope.traversing.api import getName, getParent
|
||||||
|
|
||||||
|
@ -51,6 +55,10 @@ class IMAPCollectionProvider(object):
|
||||||
|
|
||||||
def collect(self, client):
|
def collect(self, client):
|
||||||
client._collectedObjects = {}
|
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 = IMAP4(client.baseAddress)
|
||||||
imap.login(client.userName, client.password)
|
imap.login(client.userName, client.password)
|
||||||
mailbox = 'INBOX'
|
mailbox = 'INBOX'
|
||||||
|
@ -61,11 +69,13 @@ class IMAPCollectionProvider(object):
|
||||||
type, data = imap.search(None, 'ALL')
|
type, data = imap.search(None, 'ALL')
|
||||||
for num in data[0].split():
|
for num in data[0].split():
|
||||||
type, data = imap.fetch(num, '(RFC822)')
|
type, data = imap.fetch(num, '(RFC822)')
|
||||||
externalAddress = num
|
raw_msg = data[0][1]
|
||||||
obj = data
|
msg = email.message_from_string(raw_msg)
|
||||||
mtime = datetime.today()
|
msgId = msg['Message-ID'].replace('<', '').replace('>', '')
|
||||||
client._collectedObjects[externalAddress] = obj
|
externalAddress = '/'.join((baseId, msgId))
|
||||||
yield externalAddress, mtime
|
#mtime = datetime.today()
|
||||||
|
client._collectedObjects[externalAddress] = msg
|
||||||
|
yield externalAddress, None
|
||||||
|
|
||||||
def createExtFileObjects(self, client, addresses, extFileTypes=None):
|
def createExtFileObjects(self, client, addresses, extFileTypes=None):
|
||||||
loopsRoot = client.context.getLoopsRoot()
|
loopsRoot = client.context.getLoopsRoot()
|
||||||
|
@ -73,16 +83,43 @@ class IMAPCollectionProvider(object):
|
||||||
contentType = 'text/plain'
|
contentType = 'text/plain'
|
||||||
resourceType = loopsRoot.getConceptManager()['email']
|
resourceType = loopsRoot.getConceptManager()['email']
|
||||||
for addr in addresses:
|
for addr in addresses:
|
||||||
print '***', addr, client._collectedObjects[addr]
|
msg = client._collectedObjects[addr]
|
||||||
return []
|
title = msg['Subject']
|
||||||
|
sender = msg['From']
|
||||||
def _dummy(self):
|
receiver = msg['To']
|
||||||
name = self.generateName(container, addr)
|
raw_date = msg['Date'].rsplit(' ', 1)[0]
|
||||||
title = self.generateTitle(addr)
|
fmt = '%a, %d %b %Y %H:%M:%S'
|
||||||
obj = addAndConfigureObject(
|
date = datetime(*(time.strptime(raw_date, fmt)[0:6]))
|
||||||
container, Resource, name,
|
parts = self.getPayload(msg, {})
|
||||||
title=title,
|
if 'html' in parts:
|
||||||
resourceType=extFileType,
|
text = parts['html']
|
||||||
contentType=contentType,
|
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
|
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
|
||||||
|
|
|
@ -41,14 +41,13 @@ class IMailCollection(IExternalCollection):
|
||||||
providerName = schema.TextLine(
|
providerName = schema.TextLine(
|
||||||
title=_(u'Provider name'),
|
title=_(u'Provider name'),
|
||||||
description=_(u'The name of a utility that provides the '
|
description=_(u'The name of a utility that provides the '
|
||||||
u'external objects; default is an IMAP '
|
u'external objects; default is an IMAP collection provider.'),
|
||||||
u'collection provider.'),
|
|
||||||
default=u'imap',
|
default=u'imap',
|
||||||
required=False)
|
required=False)
|
||||||
baseAddress = schema.TextLine(
|
baseAddress = schema.TextLine(
|
||||||
title=_(u'Host name'),
|
title=_(u'Host name'),
|
||||||
description=_(u'The host name part for accessing the '
|
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)
|
required=True)
|
||||||
userName = schema.TextLine(
|
userName = schema.TextLine(
|
||||||
title=_(u'User name'),
|
title=_(u'User name'),
|
||||||
|
@ -67,9 +66,22 @@ class IMailResource(ITextDocument):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
externalAddress = schema.BytesLine(
|
externalAddress = schema.BytesLine(
|
||||||
title=_(u'External Address'),
|
title=_(u'External Address'),
|
||||||
description=_(u'The full address of the email in the external '
|
description=_(u'The full address of the email in the external '
|
||||||
u'email system.'),
|
u'email system.'),
|
||||||
default='',
|
default='',
|
||||||
missing_value='',
|
missing_value='',
|
||||||
required=False)
|
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,)
|
||||||
|
|
|
@ -22,7 +22,7 @@ Fake access to system libraries for testing.
|
||||||
$Id$
|
$Id$
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = [('1 (RFC822 {7785}', 'Return-Path: <ceo-bounces@mailman.cy55.de>\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\n<p><b>Blogging from ...</b><br />\r\n\r\n'), ')']
|
data = [('1 (RFC822 {7785}', 'Return-Path: <ceo-bounces@mailman.cy55.de>\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\n<p><b>Blogging from ...</b><br />\r\n\r\n'), ')']
|
||||||
|
|
||||||
|
|
||||||
class IMAP4(object):
|
class IMAP4(object):
|
||||||
|
|
Loading…
Add table
Reference in a new issue