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
	
	 helmutm
						helmutm