diff options
Diffstat (limited to 'Mailman/Message.py')
| -rw-r--r-- | Mailman/Message.py | 233 |
1 files changed, 33 insertions, 200 deletions
diff --git a/Mailman/Message.py b/Mailman/Message.py index 895ba712e..9f6385aa7 100644 --- a/Mailman/Message.py +++ b/Mailman/Message.py @@ -14,13 +14,19 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +"""Standard Mailman message object. -"""Embody incoming and outgoing messages as objects.""" +This is a subclass of rfc822.Message but provides an extended interface which +is more convenient for use inside Mailman. One of the important things it +provides is a writable mapping interface so that new headers can be added, or +existing headers modified. +""" import sys import string import time +from types import StringType # Python 1.5's version of rfc822.py is buggy and lacks features we # depend on -- so we always use the up-to-date version distributed @@ -28,80 +34,19 @@ import time from Mailman.pythonlib import rfc822 -# Utility functions 2 of these classes use: -def AddBackNewline(str): - return str + '\n' + +class Message(rfc822.Message): + """This class extends the standard rfc822.Message object. -def RemoveNewline(str): - return str[:-1] - + It provides some convenience functions for getting certain interesting + information out of the message. -# XXX klm - use the standard lib StringIO module instead of FakeFile. -# If we're trying to create a message object from text, we need to pass -# a file object to rfc822.Message to get it to do its magic. Well, -# to avoid writing text out to a file, and having it read back in, -# here we define a class that will fool rfc822 into thinking it's a -# non-seekable message. -# The only method rfc822.Message ever calls on a non-seekable file is -# readline. It doesn't use the optional arg to readline, either. -# In my subclasses, I use the read() method, and might just use readlines() -# someday. -# -# It might be useful to expand this into a full blown fully functional class. - -class FakeFile: - def __init__(self, text): - self.lines = map(AddBackNewline, string.split(text, '\n')) - self.curline = 0 - self.lastline = len(self.lines) - 1 - def readline(self): - if self.curline > self.lastline: - return '' - self.curline = self.curline + 1 - return self.lines[self.curline - 1] - def read(self): - startline = self.curline - self.curline = self.lastline + 1 - return string.join(self.lines[startline:], '') - def readlines(self): - startline = self.curline - self.curline = self.lastline + 1 - return self.lines[startline:] - def seek(self, pos): - if pos <> 0: - raise ValueError, "FakeFiles can only seek to the beginning." - self.curline = 0 - - - -# We know the message is gonna come in on stdin or from text for our purposes. -class IncomingMessage(rfc822.Message): - def __init__(self, text=None): - if not text: - rfc822.Message.__init__(self, sys.stdin, 0) - self.body = self.fp.read() - else: - rfc822.Message.__init__(self, FakeFile(text), 0) - self.body = self.fp.read() - self.file_count = None - - def readlines(self): - if self.file_count <> None: - x = self.file_count - self.file_count = len(self.file_data) - return self.file_data[x:] - return map(RemoveNewline, self.headers) + [''] + \ - string.split(self.body,'\n') - - def readline(self): - if self.file_count == None: - self.file_count = 0 - self.file_data = map(RemoveNewline, self.headers) + [''] + \ - string.split(self.body,'\n') - if self.file_count >= len(self.file_data): - return '' - self.file_count = self.file_count + 1 - return self.file_data[self.file_count-1] + '\n' + """ + def __init__(self, fp): + rfc822.Message.__init__(self, fp) + self.body = self.fp.read() + if self.seekable: + self.rewindbody() def GetSender(self): # Look for a Sender field. @@ -109,13 +54,9 @@ class IncomingMessage(rfc822.Message): if sender: realname, mail_address = self.getaddr('sender') else: - try: - realname, mail_address = self.getaddr('from') - except: - real_name = mail_address = None - + realname, mail_address = self.getaddr('from') # We can't trust that any of the headers really contained an address - if mail_address and type(mail_address) == type(""): + if mail_address and type(mail_address) == StringType: return string.lower(mail_address) else: # The unix from line is all we have left... @@ -123,29 +64,24 @@ class IncomingMessage(rfc822.Message): return string.lower(string.split(self.unixfrom)[1]) def GetEnvelopeSender(self): - # - # look for unix from line and attain address - # from it, return None if there is no unix from line - # this function is used to get the envelope sender - # when mail is sent to a <listname>-admin address - # + # look for unix from line and attain address from it. return None if + # there is no unix from line. this function is used to get the + # envelope sender when mail is sent to a <listname>-admin address if not self.unixfrom: return None # XXX assumes no whitespace in address parts = string.split(self.unixfrom) for part in parts: - # # perform minimal check for the address - # if string.find(part, '@') > -1: user, host = string.split(part, '@', 1) if not user: continue - if string.count(host, ".") < 1: # doesn't look qualified + if string.count(host, ".") < 1: + # doesn't look qualified continue return part return None - def GetSenderName(self): real_name, mail_addr = self.getaddr('from') @@ -153,116 +89,13 @@ class IncomingMessage(rfc822.Message): return self.GetSender() return real_name - def SetHeader(self, name, value, crush_duplicates=1): - if crush_duplicates: - self[name] = value - else: - # Only bother with the dict - self.dict[string.lower(name)] = value - -# This is a simplistic class. It could do multi-line headers etc... -# But it doesn't because I don't need that for this app. -class OutgoingMessage: - def __init__(self, headers=None, body='', sender=None): - self.cached_headers = {} - if headers: - self.SetHeaders(headers) - else: - self.headers = [] - self.body = body - self.sender = sender - - def readlines(self): - if self.file_count <> None: - x = self.file_count - self.file_count = len(self.file_data) - return self.file_data[x:] - return map(RemoveNewline, self.headers) + [''] + \ - string.split(self.body,'\n') - - def readline(self): - if self.file_count == None: - self.file_count = 0 - self.file_data = map(RemoveNewline, self.headers) + [''] + \ - string.split(self.body,'\n') - if self.file_count >= len(self.file_data): - return '' - self.file_count = self.file_count + 1 - return self.file_data[self.file_count-1] + '\n' - - def SetHeaders(self, headers): - self.headers = map(AddBackNewline, string.split(headers, '\n')) - self.CacheHeaders() - - def CacheHeaders(self): - for header in self.headers: - i = string.find(header, ':') - self.cached_headers[string.lower(string.strip(header[:i])) - ] = header[i+2:] - - def SetHeader(self, header, value, crush_duplicates=1): - if value[-1] <> '\n': - value = value + '\n' - if crush_duplicates: - # Run through the list and make sure header isn't already there. - remove_these = [] - for item in self.headers: - f = string.find(item, ':') - if string.lower(item[:f]) == string.lower(header): - remove_these.append(item) - for item in remove_these: - self.headers.remove(item) - del remove_these - self.headers.append('%s%s: %s' % (string.upper(header[0]), - string.lower(header[1:]), - value)) - self.cached_headers[string.lower(header)] = value - - def SetBody(self, body): - self.body = body - - def AppendToBody(self, text): - self.body = self.body + text - - def SetSender(self, sender, set_from=1): - self.sender = sender - if not self.getheader('from') and set_from: - self.SetHeader('from', sender) - - def GetSender(self): - return self.sender - -# Lower case the name to give it the same UI as IncomingMessage -# inherits from rfc822 - def getheader(self, str): - str = string.lower(str) - if not self.cached_headers.has_key(str): - return None - return self.cached_headers[str] - - def __delitem__(self, name): - if not self.getheader(name): - # XXX this should raise an exception - return None - newheaders = [] - name = string.lower(name) - nlen = len(name) - for h in self.headers: - if (len(h) > (nlen+1) - and h[nlen] == ":" - and string.lower(h[:nlen]) == name): - continue - newheaders.append(h) - self.headers = newheaders - self.CacheHeaders() - - # XXX should have a __setitem__ + def __str__(self): + # TBD: should this include the unixfrom? + return string.join(self.headers, '') + '\n' + self.body -class NewsMessage(IncomingMessage): - def __init__(self, mail_msg): - self.fp = mail_msg.fp - self.fp.seek(0) - rfc822.Message.__init__(self, self.fp, 0) - self.body = self.fp.read() - self.file_count = None + +class OutgoingMessage(Message): + def __init__(self): + from Mailman.pythonlib.StringIO import StringIO + Message.__init__(self, StringIO()) |
