# Copyright (c) 2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany). # Copyright (c) 2000-2005 LOGILAB S.A. (Paris, FRANCE). # # http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """the element used to deal with email in narval's memory :version: $Revision:$ :author: Logilab :copyright: 2001-2004 LOGILAB S.A. (Paris, FRANCE) 2004 DoCoMo Euro-Labs GmbH (Munich, Germany) :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com """ __revision__ = "$Id: rfc822xml.py,v 1.3 2002/11/22 09:29:41 syt Exp $" __docformat__ = "restructuredtext en" from time import mktime, localtime, strftime from random import randint from socket import gethostname from rfc822 import parsedate from mx.DateTime import DateTimeFromTicks from narval.public import TYPE_NS, NO_NS from narval.element import NSAttribute, ALElement from narval.interfaces.base import IEmail from narval.xml_handlers import data_handler from narval.serialutils import yn_value, yn_rev_value, \ date_time_value, date_time_rev_value, attr_string_value, attr_string_rev_value def split_address(address): """split an email address has it appears in from or to field into a name and an actual address """ if address.find('<') > 0: from_name = address[:address.find('<')].strip() from_address = address[address.find('<')+1:address.find('>')].strip() else: from_name = address.split('@')[0] from_address = address return from_name, from_address class EmailElement(ALElement): """element to handle minimal email information""" __implements__ = (IEmail,) __xml_element__ = (NO_NS, 'email') message_id = NSAttribute(NO_NS, None, attr_string_value, attr_string_rev_value) references = NSAttribute(NO_NS, None, attr_string_value, attr_string_rev_value) # FIXME: list ? from_name = NSAttribute(NO_NS, None, str, str) from_address = NSAttribute(NO_NS, None, str, str) to = NSAttribute(NO_NS, None, str, str) cc = NSAttribute(NO_NS, None, str, str) subject = NSAttribute(NO_NS, None, str, str) type = NSAttribute(NO_NS, 'incoming', str, str) # incoming/outgoing content_type = NSAttribute(NO_NS, None, str, str) date = NSAttribute(NO_NS, None, date_time_value, date_time_rev_value) multipart = NSAttribute(NO_NS, False, yn_value, yn_rev_value) __child_handler__ = data_handler('body') body = '' def children_as_xml(self, encoding='UTF-8'): """return the XML representation of children in this element. :type encoding: str :param encoding: the encoding to use in the returned string :rtype: str :return: XML string representing the element """ return '' % self.body def from_rfc822(cls, msg, path=None): """class method to create a EmailElement from a email message as defined by the mailbox module from the python standard library """ # FIXME: get encoding from email headers and use unicode for stored # attributes from_name, from_address = split_address(msg.get('From')) multipart = msg.getmaintype() == 'multipart' elmt = cls(from_name=from_name, from_address=from_address, subject=msg.get('Subject'), message_id=msg.get('message-id'), to=msg.get('to'), cc=msg.get('cc'), content_type=msg.get('content-type'), multipart=multipart) if msg.get('date') : elmt.date = DateTimeFromTicks(mktime(parsedate(msg.get('date')))) if not multipart: elmt.body = msg.fp.read() ## else: ## _read_multipart(msg, path) return elmt from_rfc822 = classmethod(from_rfc822) def build_reply(self, msg, all=False): """create a reply to the sender of this email, include all recipients if all==True :type msg: unicode :var msg: the reply body :type all: bool :var all: flag indicating whether other recipients should be cced :rtype: `EmailElement` :return: an outgoing email """ elmt = EmailElement(type='outgoing') elmt.message_id = '<%s.%s@%s>' % (''.join([str(i) for i in localtime()[:6]]), randint(10000, 99999), gethostname()) elmt.references = self.references if not self.message_id in elmt.references: elmt.references += ' ' + self.message_id elmt.to = '%s <%s>' % (self.from_name, self.from_address) if all: elmt.cc = self.to if self.cc: elmt.cc += ', ' + self.cc base_subject = self.subject.lower() if base_subject.startswith('re:') or base_subject.startswith('ref:'): elmt.subject = self.subject else: elmt.subject = 'Re: %s' % self.subject elmt.date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime()) elmt.content_type = 'text/plain; charset=UTF-8' elmt.body = msg.encode('UTF-8') return elmt class ServiceElement(ALElement): """a memory element used to locate a particular service :type type: str :ivar type: the service's type (http, smtp, pop...) :type host: str :ivar host: the service's host name, default to localhost :type port: int :ivar port: the service's port """ __xml_element__ = (NO_NS, 'service') type = NSAttribute(TYPE_NS, None, str, str) host = NSAttribute(NO_NS, 'localhost', str, str) port = NSAttribute(NO_NS, None, int, str) ## import os ## import mailbox, mimetools, multifile ## from cStringIO import StringIO ## def _read_multipart(msg, b, doc, path, base_encoding): ## # look for boundary ## msg.rewindbody() ## mf = multifile.MultiFile(msg.fp) ## mf.push(msg.getparam('BOUNDARY')) ## mf.next() ## # read first part (supposedly text) ## msg_text = mimetools.Message(StringIO(mf.read())) ## msg_text.rewindbody() ## part = doc.createElementNS(MAIL_NS,'part') ## part.setAttributeNS(NO_NS,'type','text') ## part.setAttributeNS(NO_NS,'content_type','text/plain') ## part.setAttributeNS(NO_NS,'encoding','8bit') ## b.appendChild(part) ## # read other parts, *** for now discard subparts *** ## while mf.next() : ## part = mimetools.Message(StringIO(mf.read())) ## encoding = part.getencoding() ## part.rewindbody() ## if part.getparam('name') : ## fichier = part.getparam('name') ## else : ## fichier = msg.get('message-id') ## if encoding == '7bit' or encoding == '8bit' : ## data = part.fp.read() ## else : ## data_f = StringIO() ## mimetools.decode(part.fp,data_f,encoding) ## data = data_f.getvalue() ## p = doc.createElementNS(MAIL_NS,'part') ## p.setAttributeNS(NO_NS,'type','file') ## _type = part.get('content_type', 'text/plain') ## p.setAttributeNS(NO_NS,'content_type', _type) ## p.setAttributeNS(NO_NS,'encoding',encoding) ## p.setAttributeNS(NO_NS,'name',fichier) ## if path is not None: ## save_path = os.path.join(path, fichier) ## i = 0 ## while os.path.isfile(save_path): ## i += 1 ## save_path = os.path.join(path, '%s%d'%(fichier, i)) ## p.setAttributeNS(NO_NS,'path', save_path) ## saved_file = open(save_path,'w') ## saved_file.write(data) ## saved_file.close() ## p.setAttributeNS(NO_NS,'path', save_path) ## if _type == 'text/plain': ## p.appendChild(doc.createTextNode(unicode(data, base_encoding))) ## b.appendChild(p)