# 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 '<![CDATA[%s]]>' % 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) 


