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. | ||||
| 
 | ||||
|   >>> 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 | ||||
|   '<p><b>Blogging from ...</b><br />\n' | ||||
|   >>> aMail.externalAddress | ||||
|   u'imap://jim@merz12/20081208171745.e4ce2xm96cco80cg@cy55.de' | ||||
| 
 | ||||
| 
 | ||||
| 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 | ||||
|                 # for checking for changes... | ||||
|                 oldFound.append(addr) | ||||
|                 if mdate > self.lastUpdated: | ||||
|                 if mdate and mdate > self.lastUpdated: | ||||
|                     # force reindexing | ||||
|                     notify(ObjectModifiedEvent(old[addr])) | ||||
|             else: | ||||
|  |  | |||
|  | @ -20,4 +20,15 @@ | |||
|       factory="loops.integrator.mail.imap.IMAPCollectionProvider" | ||||
|       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> | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -41,8 +41,7 @@ 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( | ||||
|  | @ -73,3 +72,16 @@ class IMailResource(ITextDocument): | |||
|             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,) | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ Fake access to system libraries for testing. | |||
| $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): | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm