Bug in rfc822

phil hunt philh at comuno.freeserve.co.uk
Tue Jul 10 21:09:52 EDT 2001


rOn Mon, 9 Jul 2001 21:12:17 -0400, Barry A. Warsaw <barry at digicool.com> wrote:
>
>>>>>> "ph" == phil hunt <philh at comuno.freeserve.co.uk> writes:
>
>    ph> I am currently writing a program that reads emails, modifies
>    ph> them in various ways (adding headers, encryption, etc) and
>    ph> outputs the result.
>
>This actually sound like a good fit for mimelib, which is arguably
>misnamed (perhaps it should be called maillib?).

My encryption system won't really know about mime; it'll encrypt
the whole body of the email, regardless of whether it contains mime
attachments or not. (It'll also remove and put encrypted into
the body several headers that says things about the email's
content such as Subject: and whatever headers Mime uses).

>    ph> As part of this program, I'm writing a mail module that (when
>    ph> finished) could be hacked into a replacement for rfc822.
>    ph> rfc822's functionality is to read mail headers and allow
>    ph> interrogation of their values easily. My module is
>    ph> conceptually on a higher level; containing a MailHeader class
>    ph> which can be used to write as well as read ascii
>    ph> representations of email, and a Mail class that includes the
>    ph> header and the body; again for composition as well as reading
>    ph> emails. (for now, it uses rfc822 to parse incoming headsers).
>
>mimelib uses rfc822 for some functionality, but it has its own Parser
>and Generator class (to go from plaintext -> object tree ->
>plaintext).  It uses rfc822 for some parts of the implementation, such
>as parsing addresses and realnames out of strings (so it's vulnerable
>to the RFC 2822 updates as well).
>
>    ph> When it is written, perhaps it could be considered for
>    ph> eventual inclusion in the standard library (I say eventually,
>    ph> because IMO it'll take me a few attempts to get the API
>    ph> exactly right)
>
>That's the hard part, all right!

Indeed.

>    ph> My module doesn't know anything about Mime; which is good
>    ph> because IMO there is room for a library that is higher-level
>    ph> than rfc822 but lower levelb than knowing about Mime.
>
>    ph> What is the process of getting a module in the standard
>    ph> library?
>
>Post the code. :)  I definitely want to look at what you've got, to
>see how well (or if) it fits with mimelib. 

At the moment it's just a class for reading/writing mail headers:


#---------------------------------------------------------------------

"""***
The MailHeader class stores headers for a rfc822 email message

Creation:
~~~~~~~~~
readFromFileHandle(fh)
readFromString(str)
   read the header lines from a file or string
 
Accessing:
~~~~~~~~~~
getheader(name, default)
get(name, default)
   These do the same thing: return the value of a header line.
   if no (default) is specified and the line doesn't exist, 
   returns none.

put(name, value)
   Sets the value of header-line (name) to (value).

Writing output
~~~~~~~~~~~~~~
__str__()
asString()
   These do the same thing: Output all the header-lines as a 
   string

Instance variables:
~~~~~~~~~~~~~~~~~~~
self.h = dictionary containing header lines. If there are >1
   lines with a particular header, the value will be a list of all
   the lines, in the order they appeared

self.mess = an instance of rfc822.Message, used internally to
   decode the header messages
***"""



class MailHeader:

   def __init__(self):  
      self.h = {}
   
   def readFromFileHandle(self, fh):
      # get a message object for parsing:
      self.mess = rfc822.Message(fh)
      headerNames = self.mess.keys()
      for hn in headerNames:
         hValue = self.mess.getheader(hn)
         self.putAppend(hn, hValue)
           
   def readFromString(self, str):      
      stringToFileWrapper = StringIO.StringIO(str)
      self.readFromFileHandle(stringToFileWrapper)
      stringToFileWrapper.close()
   
   def getheader(self, name, default =None):
      return self.get(name, default)  
      
   def get(self, name, default =None):
      try:
         value = self.h[name]
         return value
      except:
         # couldn't return a value, use default
         return default
   
   def put(self, name, value):
      self.h[name] = value
      
   def putAppend(self, name, value):
      if self.h.has_key(name):
         v = self.h[name]
         if type(v)==type([]):
            print "@@@ self.h[%s] was %s @@@" % (name,self.h[name])
            v.append(value)
            print "@@@ self.h[%s] is %s @@@" % (name,self.h[name])
            # not sure if we need to add anything here???
         else:
            # it's a string, change to a list:
            self.h[name] = [self.h[name], value] 
      else:
         self.h[name] = value
   
   def __str__(self):                               
      return self.asString()

   def asString(self):
      result = ""
      for name, value in self.h.items():
         if type(value)==type([]):
            for v in value:
               result = result + name + ": " \
                  + self._valueMultiline(v) + "\n"
         else:
            result = result + name + ": " \
               + self._valueMultiline(value) + "\n"
      return result
            
   def _valueMultiline(self, v):
      """*** 
      if (v) has more than one line, ensure that lines
      after the first one begin with a space
      ***"""
      sa = string.split(v, '\n')
      for i in range(len(sa)):
         if i == 0: continue
         if not utility.startsWith(sa[i], " "):
            sa[i] = " " + sa[i]
      v = string.join(sa, '\n')    
      return v
   
   
#---------------------------------------------------------------------

I'm also thinking of adding a Mail class that'll
contain 2 instance variables, (header) for the header (a MailHeader)
and (body) (a string) for the body.

There would be a Mail.__str__() method to render the whole thing to
ascii.

There would also be getBody() and setBody() methods.

This class wouldn't understand Mime, but a subclass, MimeMail, could
be written that would. Presumably MimeMail.setBody() would throw an
exception as this wouldn't be the recomended way of adding mime 
components.

Also I think that it would be nice is these classes could be used effectively
with Usenet messages, interfacing to the low level nntplib library.

> IMO, if we're going aim at
>replacing rfc822.py and all the various and sundry MIME attempts, we
>ought to do it right, and simplify the choices end-users have to make
>in order to manipulate email messages.

I agree.

-- 
## Philip Hunt ## philh at comuno.freeserve.co.uk ##







More information about the Python-list mailing list