""" Please use MIMEPart class for opening or creating MIME messsage.
Any part of message will be in list named 'parts'.
Body will be in filed body (this is file).
Headers can be added or read with correspondingly named functions. """

import random
import cStringIO
letters="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"

# format of mime part (whole letter is treated same as part):
#   headers list, essential headers dict, body text, parts list optionally

def gen_bound():
  return '_----------=_'+''.join(map(lambda x: letters[random.randrange(0,36)], range(24)))

class Header:
  def __init__(self, src=None):
    if src:
      while src[-1] in ['\r','\n']:
        src=src[:-1]
      a=src.split(': ', 1)
      self.name=a[0]
      self.specs={}
      if len(a)>1:
	a=map(str.strip, a[1].split(";"))
        self.value=a[0]
	for b in a[1:]:
          c=b.split('=',1)
          if len(c)>1:
	    self.specs[c[0]]=c[1]
          else:
	    self.specs[c[0]]=None
      else:
	self.value=None
    else:
      self.name=None
      self.value=None
      self.specs={}

  def __str__(self):
    assert(self.name)
    r=self.name
    if self.value:
      r+=": "+self.value
    for a in self.specs.keys():
      if self.specs[a] is not None:
        r+=";\n\t"+a+"="+self.specs[a]
      else:
	r+=";\n\t"+a
    return r+"\n"

#print Header("Subject: 123")
#print Header("Content-Type: text/plain; encoding=ko8-r")

def memsaver():
  return cStringIO.StringIO()

essential=['Subject', 'Date', 'From', 'To', 'Content-Type', 
		'Content-Transfer-Encoding']

class MIMEPart:
  """ MIME message itself is considered as one MIMEPart with subparts """
  def __init__(self, src=None, saver=memsaver):
    self.saver=saver
    self.eh={} # essential headers
    self.h=[] # headers
    self.body=self.saver()
    self.parts=[]
    self._mode=0 # head reading
    self._htmp=None

    if type(src) is str: # processing lines routine
      p=0
      while 1:
        np=src.find("\n",p)
        self._do_str(src[p:np+1 or None])
        if np==-1: break
        p=np+1
      self._eof()
    elif type(src) is file:
      while 1:
        s=src.readline()
        if not s:
          self._eof()
          break
        self._do_str(s) 

  def _eof(self):
#    if self._htmp:
#      self.set_header(self._htmp)
    del self._htmp, self._mode
    if self._part:
      self.parts.append(self._part)
    del self._part

  def _do_str(self,s):
    #print "do",s,"mode",self._mode
    if self._mode==0: # header
      if s.strip(): # string is not empty
        if s[0] in [" ", "\t"]: # string is continuation
          self._htmp+=s
        else: # new header
          if self._htmp:
	    self.set_header(self._htmp) # flush old
          self._htmp=s # start new
      else:
        self.set_header(self._htmp) # flushing headers tmp
        self._mode=2 if self.is_multipart() else 1 # multipart or simple
        self._part=None

    elif self._mode==1: # single part
      self.body.write(s)

    elif self._mode==2: # multipart init
      if s.find("--"+self.boundary())==0:
        self._part=None
        self._mode=3 # multipart parts
      else:
        self.body.write(s)   
    elif self._mode==3:
      if s.find("--"+self.boundary())==0: # next part or end
        if self._part:
          self.parts.append(self._part)
	  self._part=None
      else:
        if not self._part:
	  self._part=MIMEPart(saver=self.saver)
        self._part._do_str(s)

  def is_multipart(self):
    if self.eh.has_key("Content-Type"):
      if self.eh["Content-Type"].value.find("multipart")==0:
        return True
    return False

  def boundary(self):
    if self.eh.has_key("Content-Type"):
      cth=self.eh["Content-Type"]
      if cth.value.find("multipart")==0:
        if cth.specs.has_key("boundary"):
          return cth.specs["boundary"][1:-1]
        else:
          b='"'+gen_bound()+'"'
          cth.specs["boundary"]=b
          #self.eh["Content-Type"]=cth
          return b[1:-1]
    return None

  def set_header(self, s):
    if type(s) is not Header:
      h=Header(s)
    else:
      h=s
    if h.name in essential:
      self.eh[h.name]=h
    else:
      self.h.append(h)

  def get_header(self, s):
    if s in essential:
      return self.eh[s]
    else:
      l=filter(lambda x: x.name==s, self.h)
      return l[0] if len(l)==1 else l

  def __str__(self):
    if self.is_multipart():
      bnd=self.boundary() # init if never used before for headers
    self.body.seek(0)
    s=''.join(map(str, self.h) + map(str, self.eh.values())) + \
      '\n' + self.body.read()
    if self.is_multipart() and self.parts:
      s+="\n--"+bnd+"\n"
      s+=("\n--"+bnd+"\n").join(map(str,self.parts))
      s+="\n--"+bnd+"-- "
    return s

  def __repr__(self):
    return "MIMEPart(%s)"%`str(self)`

#a=MIMEPart(file('31.msg'))
#print a.get_header("Subject")
#for p in a.parts:
#  print "----------------"
#  for h in p.eh.values():
#    print h
#  print p.h

