1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
|
# Copyright (C) 1998 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
"""Embody incoming and outgoing messages as objects."""
import sys
import string
import time
# 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
# with Mailman.
from Mailman.pythonlib import rfc822
# Utility functions 2 of these classes use:
def AddBackNewline(str):
return str + '\n'
def RemoveNewline(str):
return str[:-1]
# 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 GetSender(self):
# Look for a Sender field.
sender = self.getheader('sender')
if sender:
realname, mail_address = self.getaddr('sender')
else:
try:
realname, mail_address = self.getaddr('from')
except:
real_name = mail_address = None
# We can't trust that any of the headers really contained an address
if mail_address and type(mail_address) == type(""):
return string.lower(mail_address)
else:
# The unix from line is all we have left...
if self.unixfrom:
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
#
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
continue
return part
return None
def GetSenderName(self):
real_name, mail_addr = self.getaddr('from')
if not real_name:
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__
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
|