"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)