"Fossies" - the Fresh Open Source Software Archive 
Member "getmail-5.16/getmailcore/message.py" (31 Oct 2021, 7875 Bytes) of package /linux/misc/getmail-5.16.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style:
standard) with prefixed line numbers.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "message.py" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
5.15_vs_5.16.
1 #!/usr/bin/env python2
2 '''The getmail Message class.
3
4 '''
5
6 __all__ = [
7 'Message',
8 ]
9
10 import sys
11 import os
12 import time
13 import cStringIO
14 import re
15 import email
16 import email.Errors
17 import email.Utils
18 import email.Parser
19 from email.Generator import Generator
20 try:
21 from email.header import Header
22 except ImportError, o:
23 try:
24 from email.Header import Header
25 except ImportError, o:
26 # Python < 2.5
27 from email import Header
28
29 from getmailcore.exceptions import *
30 from getmailcore.utilities import mbox_from_escape, format_header, \
31 address_no_brackets
32 import getmailcore.logging
33
34 if sys.hexversion < 0x02040000:
35 # email module in Python 2.3 uses more recursion to parse messages or
36 # similar; a user reported recursion errors with a message with ~300
37 # MIME parts.
38 # Hack around it by increasing the recursion limit.
39 sys.setrecursionlimit(2000)
40
41 message_attributes = (
42 'sender',
43 'received_by',
44 'received_from',
45 'received_with',
46 'recipient'
47 )
48
49 RE_FROMLINE = re.compile(r'^(>*From )', re.MULTILINE)
50
51
52 #######################################
53 def corrupt_message(why, fromlines=None, fromstring=None):
54 log = getmailcore.logging.Logger()
55 log.error('failed to parse retrieved message; constructing container for '
56 'contents\n')
57 if fromlines == fromstring == None:
58 raise SystemExit('corrupt_message() called with wrong arguments')
59 msg = email.message_from_string('')
60 msg['From'] = '"unknown sender" <>'
61 msg['Subject'] = 'Corrupt message received'
62 msg['Date'] = email.Utils.formatdate(localtime=True)
63 body = [
64 'A badly-corrupt message was retrieved and could not be parsed',
65 'for the following reason:',
66 '',
67 ' %s' % why,
68 '',
69 'Below the following line is the original message contents.',
70 '',
71 '--------------------------------------------------------------',
72 ]
73 if fromlines:
74 body.extend([line.rstrip() for line in fromlines])
75 elif fromstring:
76 body.extend([line.rstrip() for line in fromstring.splitlines()])
77 msg.set_payload(os.linesep.join(body))
78 for attr in message_attributes:
79 setattr(msg, attr, '')
80 return msg
81
82 #######################################
83 class Message(object):
84 '''Message class for getmail. Does sanity-checking on attribute accesses
85 and provides some convenient interfaces to an underlying email.Message()
86 object.
87 '''
88 __slots__ = (
89 '__msg',
90 '__raw',
91 #'log',
92 'sender',
93 'received_by',
94 'received_from',
95 'received_with',
96 'recipient',
97 )
98 def __init__(self, fromlines=None, fromstring=None, fromfile=None):
99 #self.log = Logger()
100 self.recipient = None
101 self.received_by = None
102 self.received_from = None
103 self.received_with = None
104 self.__raw = None
105 parser = email.Parser.Parser()
106
107 # Message is instantiated with fromlines for POP3, fromstring for
108 # IMAP (both of which can be badly-corrupted or invalid, i.e. spam,
109 # MS worms, etc). It's instantiated with fromfile for the output
110 # of filters, etc, which should be saner.
111 if fromlines:
112 try:
113 self.__msg = parser.parsestr(os.linesep.join(fromlines))
114 except email.Errors.MessageError, o:
115 self.__msg = corrupt_message(o, fromlines=fromlines)
116 self.__raw = os.linesep.join(fromlines)
117 elif fromstring:
118 try:
119 self.__msg = parser.parsestr(fromstring)
120 except email.Errors.MessageError, o:
121 self.__msg = corrupt_message(o, fromstring=fromstring)
122 self.__raw = fromstring
123 elif fromfile:
124 try:
125 self.__msg = parser.parse(fromfile)
126 except email.Errors.MessageError, o:
127 # Shouldn't happen
128 self.__msg = corrupt_message(o, fromstring=fromfile.read())
129 # fromfile is only used by getmail_maildir, getmail_mbox, and
130 # from reading the output of a filter. Ignore __raw here.
131 else:
132 # Can't happen?
133 raise SystemExit('Message() called with wrong arguments')
134
135 self.sender = address_no_brackets(self.__msg['return-path']
136 or 'unknown')
137
138 def content(self):
139 return self.__msg
140
141 def copyattrs(self, othermsg):
142 for attr in message_attributes:
143 setattr(self, attr, getattr(othermsg, attr))
144
145 def flatten(self, delivered_to, received, mangle_from=False,
146 include_from=False):
147 '''Return a string with native EOL convention.
148
149 The email module apparently doesn't always use native EOL, so we force
150 it by writing out what we need, letting the generator write out the
151 message, splitting it into lines, and joining them with the platform
152 EOL.
153
154 Note on mangle_from: the Python email.Generator class apparently only
155 quotes "From ", not ">From " (i.e. it uses mboxo format instead of
156 mboxrd). So we don't use its mangling, and do it by hand instead.
157 '''
158 if include_from:
159 # Mbox-style From line, not rfc822 From: header field.
160 fromline = 'From %s %s' % (mbox_from_escape(self.sender),
161 time.asctime()) + os.linesep
162 else:
163 fromline = ''
164 # Write the Return-Path: header
165 rpline = format_header('Return-Path', '<%s>' % self.sender)
166 # Remove previous Return-Path: header fields.
167 del self.__msg['Return-Path']
168 if delivered_to:
169 dtline = format_header('Delivered-To', self.recipient or 'unknown')
170 else:
171 dtline = ''
172 if received:
173 content = 'from %s by %s with %s' % (
174 self.received_from, self.received_by, self.received_with
175 )
176 if self.recipient is not None:
177 content += ' for <%s>' % self.recipient
178 content += '; ' + time.strftime('%d %b %Y %H:%M:%S -0000',
179 time.gmtime())
180 receivedline = format_header('Received', content)
181 else:
182 receivedline = ''
183 # From_ handled above, always tell the generator not to include it
184 try:
185 tmpf = cStringIO.StringIO()
186 gen = Generator(tmpf, False, 0)
187 gen.flatten(self.__msg, False)
188 strmsg = tmpf.getvalue()
189 if mangle_from:
190 # do mboxrd-style "From " line quoting
191 strmsg = RE_FROMLINE.sub(r'>\1', strmsg)
192 return (fromline + rpline + dtline + receivedline
193 + os.linesep.join(strmsg.splitlines() + ['']))
194 except TypeError, o:
195 # email module chokes on some badly-misformatted messages, even
196 # late during flatten(). Hope this is fixed in Python 2.4.
197 if self.__raw is None:
198 # Argh -- a filter took a correctly-formatted message
199 # and returned a badly-misformatted one?
200 raise getmailDeliveryError('failed to parse retrieved message '
201 'and could not recover (%s)' % o)
202 self.__msg = corrupt_message(o, fromstring=self.__raw)
203 return self.flatten(delivered_to, received, mangle_from,
204 include_from)
205
206 def add_header(self, name, content):
207 self.__msg[name] = Header(content.rstrip(), 'utf-8')
208
209 def remove_header(self, name):
210 del self.__msg[name]
211
212 def headers(self):
213 return self.__msg._headers
214
215 def get_all(self, name, failobj=None):
216 return self.__msg.get_all(name, failobj)