# $Id$

"""mime - MIME decoding Library"""

import rfc822, re
import cStringIO as StringIO


class Error(Exception):
  pass


class Entity(rfc822.Message):
  def __init__(self, msg, mime=0, eol=r"\r\n", headers=None):
    if headers is not None:
      fp = StringIO.StringIO(headers + "\n" + msg)
    else:
      fp = StringIO.StringIO(msg)
    del msg
    rfc822.Message.__init__(self, fp)
    del self.fp
    self.mime = mime or ("MIME-Version" in self)
    if not self.mime:
      self.body = fp.read()
      return
    if "Content-Type" in self:
      self.content_type = parse_content_type(self["Content-Type"])
    else:
      self.content_type = None
    if "Content-Disposition" in self:
      self.content_disposition = \
        parse_content_disposition(self["Content-Disposition"])
    else:
      self.content_disposition = None
    self.entities = []
    if self.content_type and self.content_type[0][:10] == "multipart/":
      bre = re.compile(r"(" + eol + r")?--" +
        re.escape(self.content_type[1]["boundary"]) + r"(--)?[ \t]*" +
        r"(" + eol + r")?")
      msg = fp.read()
      start = 0
      while 1:
        while 1:
          end = bre.search(msg, start)
          if not end:
            raise Error("End boundary not found in multipart")
          if end.group(1) is not None or end.start() == 0 and start == 0:
            break
        if start == 0:
          self.body = msg[start:end.start()]
        else:
          self.entities.append(Entity(msg[start:end.start()], mime=1, eol=eol))
        start = end.end()
        if end.group(2) == "--":
          break
    else:
      encoding = self.get("Content-Transfer-Encoding", "7bit").lower()
      self.body = decode(encoding, fp.read())


_tspecials = "()<>@,;:\\\"/[]?="
_whitespace = " \t\r\n"
_tokenchars = "!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
  "^_`abcdefghijklmnopqrstuvwxyz{|}~"


def parse_content_disposition(s):
  i = 0
  # skip whitespace before disposition
  while i < len(s) and s[i] in _whitespace: i+= 1
  if i >= len(s):
    raise Error("Unexpected end of string before disposition")
  # disposition
  disposition = ""
  while i < len(s) and s[i] in _tokenchars:
    disposition += s[i]
    i+= 1
  while i < len(s) and s[i] in _whitespace: i+= 1
  if i >= len(s):
    return (disposition, {})
  if s[i] != ';':
    raise Error("Unexpected character %s in disposition" % repr(s[i]))
  i+= 1
  return (disposition, parse_params(s[i:]))


def parse_content_type(s):
  i = 0
  # skip whitespace before type "/" subtype
  while i < len(s) and s[i] in _whitespace: i+= 1
  if i >= len(s):