# $Id: mime.py,v 1.13 2001/05/09 13:44:19 kjetilja Exp $

## System modules
import string
import mimetools

## Local modules
import pygmymultifile


##
## Function find_all_parts ()
##
def find_all_parts(parts, new_parts=[]):
    for sm in parts:
        type = sm.gettype()
        if (type in ['multipart/alternative', 'multipart/mixed']):
            find_all_parts(sm.body, new_parts)
        else:
            new_parts.append(sm)            
    return new_parts


##
## Function decode_quoted_printable()
##
def decode_quoted_printable(txt):
    # The version in the python distribution is very slow and essentially
    # tries to mimic a regular expression parser in python which is a
    # bad idea when you can use regular expressions directly.
    import quote_pri
    return quote_pri.decode(txt)

def decode_base64(txt):
    from StringIO import StringIO
    output = StringIO()

    mimetools.decode(StringIO(txt), output, 'base64')

    return output.getvalue()

##
##
## Message class, handling multipart messages
##
##
class Message(mimetools.Message):

    # Constructor
    def __init__(self, fp, start, stop):
        mimetools.Message.__init__(self, fp)
        self.start = start
        self.stop = stop

    # Return the message's header text as a string.  If an
    # argument is specified, it is used as a filter predicate to
    # decide which headers to return (its argument is the header
    # name converted to lower case).
    def getheadertext(self, pred = None):
        if not pred:
            return string.joinfields(self.headers, '')
        headers = []
        hit = 0
        for line in self.headers:
            if line[0] not in string.whitespace:
                i = string.find(line, ':')
                if i > 0:
                    hit = pred(string.lower(line[:i]))
            if hit: headers.append(line)
        return string.joinfields(headers, '')

    # Return the message's body text as string.  This undoes a
    # Content-Transfer-Encoding, but does not interpret other MIME
    # features (e.g. multipart messages).  To suppress to
    # decoding, pass a 0 as argument
    def getbodytext(self, decode = 1):
        self.fp.seek(self.startofbody)
        encoding = self.getencoding()
        if not decode or encoding in ('', '7bit', '8bit', 'binary') or \
           encoding not in ('quoted-printable', 'base64'):
            return self.fp.read()
        if encoding == 'quoted-printable':
            # Use our own handler here
            return decode_quoted_printable(self.fp.read())
        from cStringIO import StringIO
        output = StringIO()
        try:
            mimetools.decode(self.fp, output, encoding)
        except:
            pass
        return output.getvalue()

    # Only for multipart messages: return the message's body as a
    # list of SubMessage objects.  Each submessage object behaves
    # (almost) as a Message object.
    def getbodyparts(self):
        if self.getmaintype() != 'multipart':
            raise SystemError, 'Content-Type is not multipart/*'
        bdry = self.getparam('boundary')
        if not bdry:
            raise SystemError, 'multipart/* without boundary param'
        self.fp.seek(self.startofbody)
        mf = pygmymultifile.MultiFile(self.fp, self.stop, 1)
        mf.push(bdry)
        parts = []
        while mf.next():
            parts.append( SubMessage(mf) )
        mf.pop()
        return parts

    # Return body, either a string or a list of messages
    def getbody(self):
        if self.getmaintype() == 'multipart':
            return self.getbodyparts()
        else:
            return self.getbodytext()


##
##
## SubMessage class, handle entries of a multipart message
##
##
class SubMessage(Message):
    # Constructor
    def __init__(self, fp):
        Message.__init__(self, fp, 0, 0)
        if self.getmaintype() == 'multipart':
            self.body = Message.getbodyparts(self)
        else:
            try:
                self.body = Message.getbodytext(self)
            except:
                self.body = self.bodyencoded = ''
                return
        self.bodyencoded = Message.getbodytext(self, decode=1)
        # If this is big, should remember file pointers

    def getbodytext(self, decode = 0):
        if not decode:
            return self.bodyencoded
        if type(self.body) == type(''):
            return self.body

    def getbodyparts(self):
        if type(self.body) == type([]):
            return self.body

    def getbody(self):
        return self.body
