def _write(self, msg): # Just like the original _write in the Generator class except # that we do not write the headers if self._headers is false. # if self._headers: Generator._write(self, msg) else: self._dispatch(msg)
def _write_headers(self, msg): sfp = StringIO() oldfp = self._fp self._fp = Tee(oldfp, sfp) try: Generator._write_headers(self, msg) finally: self._fp = oldfp self._headertxt = sfp.getvalue()
def _dispatch(self, msg): # Get the Content-Type: for the message, then try to dispatch to # self._handle_<maintype>_<subtype>(). If there's no handler for the # full MIME type, then dispatch to self._handle_<maintype>(). If # that's missing too, then dispatch to self._writeBody(). main = msg.get_content_maintype() if msg.is_multipart() and main.lower() != 'multipart': self._handle_multipart(msg) else: Generator._dispatch(self,msg)
def __handlepost(self, record, value, comment, preserve, forward, addr): # For backwards compatibility with pre 2.0beta3 ptime, sender, subject, reason, filename, msgdata = record path = os.path.join(mm_cfg.DATA_DIR, filename) # Handle message preservation if preserve: parts = os.path.split(path)[1].split(DASH) parts[0] = 'spam' spamfile = DASH.join(parts) # Preserve the message as plain text, not as a pickle try: fp = open(path) except IOError, e: if e.errno <> errno.ENOENT: raise return LOST try: msg = cPickle.load(fp) finally: fp.close() # Save the plain text to a .msg file, not a .pck file outpath = os.path.join(mm_cfg.SPAM_DIR, spamfile) head, ext = os.path.splitext(outpath) outpath = head + '.msg' outfp = open(outpath, 'w') try: g = Generator(outfp) g.flatten(msg, 1) finally: outfp.close()
def post(self, message): """Post command. message can be -a string -a list of string -a Message from email package """ import StringIO from email.Message import Message if type(message) == type(""): # tested msg = StringIO.StringIO(message) elif (type(message) == type([])) or (type(message) == type(())): # not yet tested msg = StringIO.StringIO(LF.join(message) + CRLF) elif isinstance(message, Message): # not yet tested from email.Generator import Generator msg = StringIO.StringIO() g = Generator(msg, mangle_from_=False) g.flatten(message) msg.seek(0) # NYI: test format of message instance try: r = self.__nntp.post(msg) except NNTPError, e: raise NNTPGeneralError, e.response
def flatten(self, delivered_to, received, mangle_from=False, include_from=False): '''Return a string with native EOL convention. The email module apparently doesn't always use native EOL, so we force it by writing out what we need, letting the generator write out the message, splitting it into lines, and joining them with the platform EOL. Note on mangle_from: the Python Generator class apparently only quotes "From ", not ">From " (i.e. it uses mboxo format instead of mboxrd). So we don't use its mangling, and do it by hand instead. ''' if include_from: # Mbox-style From line, not rfc822 From: header field. fromline = 'From %s %s' % (mbox_from_escape(self.sender), time.asctime()) + os.linesep else: fromline = '' # Write the Return-Path: header rpline = format_header('Return-Path', '<%s>' % self.sender) # Remove previous Return-Path: header fields. del self.__msg['Return-Path'] if delivered_to: dtline = format_header('Delivered-To', self.recipient or 'unknown') else: dtline = '' if received: content = 'from %s by %s with %s' % ( self.received_from, self.received_by, self.received_with ) if self.recipient is not None: content += ' for <%s>' % self.recipient content += '; ' + time.strftime('%d %b %Y %H:%M:%S -0000', time.gmtime()) receivedline = format_header('Received', content) else: receivedline = '' # From_ handled above, always tell the generator not to include it try: tmpf = StringIO() gen = Generator(tmpf, False, 0) gen.flatten(self.__msg, False) strmsg = tmpf.getvalue() if mangle_from: # do mboxrd-style "From " line quoting strmsg = RE_FROMLINE.sub(r'>\1', strmsg) return (fromline + rpline + dtline + receivedline + os.linesep.join(strmsg.splitlines() + [''])) except TypeError as o: # email module chokes on some badly-misformatted messages, even # late during flatten(). Hope this is fixed in Python 2.4. if self.__raw is None: # Argh -- a filter took a correctly-formatted message # and returned a badly-misformatted one? raise getmailDeliveryError('failed to parse retrieved message ' 'and could not recover (%s)' % o) self.__msg = corrupt_message(o, fromstring=self.__raw) return self.flatten(delivered_to, received, mangle_from, include_from)
def as_string(self, unixfrom = False, **k): from email.Generator import Generator fp = io.BytesIO() if 'maxheaderlen' not in k: k['maxheaderlen'] = 0 g = Generator(fp, **k) g.flatten(self, unixfrom=unixfrom) return fp.getvalue()
def as_bytes(self, unixfrom = False): """Return the entire formatted message as bytes. Optional `unixfrom' when True, means include the Unix From_ envelope header. """ from email.Generator import Generator fp = BytesIO() g = Generator(fp) g.flatten(self, unixfrom = unixfrom) return fp.getvalue()
def __init__(self, outfp, headers = [], skip = True): """ A generator that prints out only headers. If 'skip' is true, then headers in the list 'headers' are NOT included in the output. If skip is False then only headers in the list 'headers' are included in the output. The default of headers = [] and skip = True will cause all headers to be printed. NOTE: Headers are compared in a case insensitive fashion so 'bCc' and 'bCC' and 'bcc' are all the same. """ self.log = logging.getLogger("%s.%s" % (__name__, self.__class__.__name__)) Generator.__init__(self, outfp) self._headers = [x.lower() for x in headers] self._skip = skip
def test_dont_mangle_from(self): s = StringIO() g = Generator(s, mangle_from_=0) g(self.msg) self.assertEqual( s.getvalue(), """\ From: [email protected] From the desk of A.A.A.: Blah blah blah """)
def test_header_splitter(self): msg = MIMEText('') # It'd be great if we could use add_header() here, but that doesn't # guarantee an order of the parameters. msg['X-Foobar-Spoink-Defrobnit'] = ( 'wasnipoop; giraffes="very-long-necked-animals"; ' 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"') sfp = StringIO() g = Generator(sfp) g(msg) self.assertEqual(sfp.getvalue(), openfile('msg_18.txt').read())
def wrap(self, msg): """Can take either a string or an email.Message.Message. """ if isinstance(msg, basestring): msg = msg elif isinstance(msg, Message): fp = StringIO() g = Generator(fp, mangle_from_=False, maxheaderlen=0) g.flatten(msg) msg = fp.getvalue() return base64.b64encode(bz2.compress(msg))
def _getStream(self): # We write to a TemporayFile instead of a StringIO because we don't # want to keep the full file contents around in memory, and because # this approach allows us to hand off the stream iterator to the # publisher, which will serve it efficiently even after the # transaction is closed out = tempfile.TemporaryFile(mode='w+b') generator = Generator(out, mangle_from_=False) generator.flatten(self._getMessage()) self._size = out.tell() out.seek(0) return out
def as_string(self, unixfrom=0): """Return the entire formatted message as a string. Optional `unixfrom' when true, means include the Unix From_ envelope header. Overridden from email.Message in order to turn off mangle_from_. """ from email.Generator import Generator fp = StringIO() g = Generator(fp, mangle_from_=False) g.flatten(self, unixfrom=unixfrom) return fp.getvalue()
def test_message_from_string(self): fp = openfile('msg_01.txt') try: text = fp.read() finally: fp.close() msg = email.message_from_string(text) s = StringIO() # Don't wrap/continue long headers since we're trying to test # idempotency. g = Generator(s, maxheaderlen=0) g(msg) self.assertEqual(text, s.getvalue())
def get_mail_contents(msg): """split an email in a list of attachments""" attachments = [] # retrieve messages of the email bodies = search_message_bodies(msg) # reverse bodies dict parts = dict((m, k) for k, m in bodies.iteritems()) # organize the stack to handle deep first search stack = [msg, ] while stack: part = stack.pop(0) type = part.get_content_type() if type.startswith('message/'): # ('message/delivery-status', 'message/rfc822', 'message/disposition-notification'): # I don't want to explore the tree deeper here and just save source using msg.as_string() # but I don't use msg.as_string() because I want to use mangle_from_=False from email.Generator import Generator fp = StringIO.StringIO() g = Generator(fp, mangle_from_=False) g.flatten(part, unixfrom=False) payload = fp.getvalue() filename = 'mail.eml' attachments.append(Attachment(part, filename=filename, type=type, payload=payload, charset=part.get_param('charset'), description=part.get('Content-Description'))) elif part.is_multipart(): # insert new parts at the beginning of the stack (deep first search) stack[:0] = part.get_payload() else: payload = part.get_payload(decode=True) charset = part.get_param('charset') filename = get_filename(part) disposition = None if part.get_param('inline', None, 'content-disposition') == '': disposition = 'inline' elif part.get_param('attachment', None, 'content-disposition') == '': disposition = 'attachment' attachments.append(Attachment(part, filename=filename, type=type, payload=payload, charset=charset, content_id=part.get('Content-Id'), description=part.get('Content-Description'), disposition=disposition, is_body=parts.get(part))) return attachments
def flatten(self, delivered_to, received, mangle_from=False, include_from=False): '''Return a string with native EOL convention. The email module apparently doesn't always use native EOL, so we force it by writing out what we need, letting the generator write out the message, splitting it into lines, and joining them with the platform EOL. ''' f = cStringIO.StringIO() if include_from: # This needs to be written out first, so we can't rely on the # generator f.write('From %s %s' % (mbox_from_escape(self.sender), time.asctime()) + os.linesep) # Write the Return-Path: header f.write(format_header('Return-Path', '<%s>' % self.sender)) # Remove previous Return-Path: header fields. del self.__msg['Return-Path'] if delivered_to: f.write(format_header('Delivered-To', self.recipient or 'unknown')) if received: content = 'from %s by %s with %s' % ( self.received_from, self.received_by, self.received_with) if self.recipient is not None: content += ' for <%s>' % self.recipient content += '; ' + time.strftime('%d %b %Y %H:%M:%S -0000', time.gmtime()) f.write(format_header('Received', content)) gen = Generator(f, mangle_from, 0) # From_ handled above, always tell the generator not to include it try: gen.flatten(self.__msg, False) f.seek(0) return os.linesep.join(f.read().splitlines() + ['']) except TypeError, o: # email module chokes on some badly-misformatted messages, even # late during flatten(). Hope this is fixed in Python 2.4. if self.__raw == None: # Argh -- a filter took a correctly-formatted message # and returned a badly-misformatted one? raise getmailDeliveryError('failed to parse retrieved message ' 'and could not recover (%s)' % o) self.__msg = corrupt_message(o, fromstring=self.__raw) return self.flatten(delivered_to, received, mangle_from, include_from)
def __init__(self, outfp, headers=False): """ This is a special purpose message generator. We need a generator that can be used to represent the 'TEXT' fetch attribute. When used on a multipart message it does not render the headers of the message, but renders the headers of every sub-part. When used on a message that is not a multipart it just renders the body. We do this by having the 'clone()' method basically reverse whether or not we should print the headers, and the _write() method looks at that instance variable to decide if it should print the headers or not. outfp is the output file-like object for writing the message to. It must have a write() method. """ Generator.__init__(self, outfp) self._headers = headers
def as_string(self, unixfrom=False): """Return the entire formatted message as a string. Optional `unixfrom' when True, means include the Unix From_ envelope header. This is a convenience method and may not generate the message exactly as you intend. For more flexibility, use the flatten() method of a Generator instance. """ from email.Generator import Generator fp = StringIO() g = Generator(fp) g.flatten(self, unixfrom=unixfrom) return fp.getvalue()
def HoldMessage(self, msg, reason, msgdata={}): # Make a copy of msgdata so that subsequent changes won't corrupt the # request database. TBD: remove the `filebase' key since this will # not be relevant when the message is resurrected. msgdata = msgdata.copy() # assure that the database is open for writing self.__opendb() # get the next unique id id = self.__nextid() # get the message sender sender = msg.get_sender() # calculate the file name for the message text and write it to disk if mm_cfg.HOLD_MESSAGES_AS_PICKLES: ext = 'pck' else: ext = 'txt' filename = 'heldmsg-%s-%d.%s' % (self.internal_name(), id, ext) omask = os.umask(007) try: fp = open(os.path.join(mm_cfg.DATA_DIR, filename), 'w') try: if mm_cfg.HOLD_MESSAGES_AS_PICKLES: cPickle.dump(msg, fp, 1) else: g = Generator(fp) g.flatten(msg, 1) fp.flush() os.fsync(fp.fileno()) finally: fp.close() finally: os.umask(omask) # save the information to the request database. for held message # entries, each record in the database will be of the following # format: # # the time the message was received # the sender of the message # the message's subject # a string description of the problem # name of the file in $PREFIX/data containing the msg text # an additional dictionary of message metadata # msgsubject = msg.get('subject', _('(no subject)')) if not sender: sender = _('<missing>') data = time.time(), sender, msgsubject, reason, filename, msgdata self.__db[id] = (HELDMSG, data) return id
def test_no_split_long_header(self): msg = Message() msg['From'] = '*****@*****.**' refparts = [] msg['References'] = 'x' * 80 msg.set_payload('Test') sfp = StringIO() g = Generator(sfp) g(msg) self.assertEqual( sfp.getvalue(), """\ From: [email protected] References: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Test""")
def as_string(self, unixfrom=False): """Return the entire formatted message as a string. Optional `unixfrom' when True, means include the Unix From_ envelope header. This is a convenience method and may not generate the message exactly as you intend because by default it mangles lines that begin with "From ". For more flexibility, use the flatten() method of a Generator instance. """ from email.Generator import Generator from cStringIO import StringIO fp = StringIO() g = Generator(fp, maxheaderlen=0) g.flatten(self, unixfrom=unixfrom) return fp.getvalue()
def test_no_semis_header_splitter(self): msg = Message() msg['From'] = '*****@*****.**' refparts = [] for i in range(10): refparts.append('<*****@*****.**>' % i) msg['References'] = SPACE.join(refparts) msg.set_payload('Test') sfp = StringIO() g = Generator(sfp) g(msg) self.assertEqual( sfp.getvalue(), """\ From: [email protected] References: <*****@*****.**> <*****@*****.**> <*****@*****.**> <*****@*****.**> <*****@*****.**> \t<*****@*****.**> <*****@*****.**> <*****@*****.**> <*****@*****.**> <*****@*****.**> Test""")
def notifyModerators(self, moderators, article): """ Send an article to a list of group moderators to be moderated. @param moderators: A C{list} of C{str} giving RFC 2821 addresses of group moderators to notify. @param article: The article requiring moderation. @type article: L{Article} @return: A L{Deferred} which fires with the result of sending the email. """ # Moderated postings go through as long as they have an Approved # header, regardless of what the value is group = article.getHeader('Newsgroups') subject = article.getHeader('Subject') if self._sender is None: # This case should really go away. This isn't a good default. sender = 'twisted-news@' + socket.gethostname() else: sender = self._sender msg = Message() msg['Message-ID'] = smtp.messageid() msg['From'] = sender msg['To'] = ', '.join(moderators) msg['Subject'] = 'Moderate new %s message: %s' % (group, subject) msg['Content-Type'] = 'message/rfc822' payload = Message() for header, value in article.headers.values(): payload.add_header(header, value) payload.set_payload(article.body) msg.attach(payload) out = StringIO.StringIO() gen = Generator(out, False) gen.flatten(msg) msg = out.getvalue() return self.sendmail(self._mailhost, sender, moderators, msg)
def test_generate(self): # First craft the message to be encapsulated m = Message() m['Subject'] = 'An enclosed message' m.add_payload('Here is the body of the message.\n') r = MIMEMessage(m) r['Subject'] = 'The enclosing message' s = StringIO() g = Generator(s) g(r) self.assertEqual( s.getvalue(), """\ Content-Type: message/rfc822 MIME-Version: 1.0 Subject: The enclosing message Subject: An enclosed message Here is the body of the message. """)
def fetch(self, ctx): """ This method applies fetch criteria that this object represents to the message and message entry being passed in. It returns the part of the message wanted as a string ready to be sent right back to the client that asked for it. NOTE: In case you are wondering a FETCH of a message can cause it to gain a '\Seen' flag. """ self.ctx = ctx # Based on the operation figure out what subroutine does the rest # of the work. # if self.attribute == "body": result = self.body(self.ctx.msg, self.section) elif self.attribute == "bodystructure": result = self.bodystructure(self.ctx.msg) elif self.attribute == "envelope": result = self.envelope(self.ctx.msg) elif self.attribute == "flags": result = '(%s)' % ' '.join( [asimap.constants.seq_to_flag(x) for x in self.ctx.sequences]) elif self.attribute == "internaldate": result = '"%s"' % \ self.ctx.internal_date.strftime("%d-%b-%Y %H:%m:%S %z") elif self.attribute == "rfc822.size": # result = str(len(self.ctx.mailbox.mailbox.get_string( # self.ctx.msg_key))) fp = StringIO() g = Generator(fp, mangle_from_=False) g.flatten(self.ctx.msg) result = str(len(email.utils.fix_eols(fp.getvalue()))) elif self.attribute == "uid": result = str(self.ctx.uid) else: raise NotImplemented return "%s %s" % (str(self), result)
def test_epilogue(self): fp = openfile('msg_21.txt') try: text = fp.read() finally: fp.close() msg = Message() msg['From'] = '*****@*****.**' msg['To'] = '*****@*****.**' msg['Subject'] = 'Test' msg.preamble = 'MIME message\n' msg.epilogue = 'End of MIME message\n' msg1 = MIMEText('One') msg2 = MIMEText('Two') msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') msg.add_payload(msg1) msg.add_payload(msg2) sfp = StringIO() g = Generator(sfp) g(msg) self.assertEqual(sfp.getvalue(), text)
def STOR(self, msg): """Given a message, STORe it. Takes a string or an email.Message.Message object. Returns a message ID. """ # Flatten a possible Message object. # ================================== if isinstance(msg, basestring): msg = msg elif isinstance(msg, Message): fp = StringIO() g = Generator(fp, mangle_from_=False, maxheaderlen=0) g.flatten(msg) msg = fp.getvalue() # Store it and verify the hash. # ============================= c, remote_msg_id = self.hit('STOR %s' % self.wrapper.wrap(msg)) msg = self.padder.pad(msg) msg = self.crypter.encrypt(msg) msg_id = sha.new(msg).hexdigest() if remote_msg_id == msg_id: return remote_msg_id else: raise SMAPError('Message storage failed! (client)') # Decide how to respond. # ====================== if c == 1: raise AlreadyStored assert c == 0
def renderMessage(message, mangleFromHeader=False): out = StringIO() generator = Generator(out, mangle_from_=mangleFromHeader) generator.flatten(message) return out.getvalue()
def __init__(self, outfp, mangle_from_=True, maxheaderlen=78): Generator.__init__(self, outfp, mangle_from_, maxheaderlen) self._headertxt = ''
def _write_headers(self, msg): if self.write_headers: Generator._write_headers(self, msg)
def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, write_headers=True): self.write_headers = write_headers Generator.__init__(self, outfp, mangle_from_, maxheaderlen)
def body(self, msg, section): """ Fetch the appropriate part of the message and return it to the user. """ msg_text = None g = None if len(section) == 0: # They want the entire message. # # This really only ever is the case as our first invocation of the # body() method. All further recursive calls will always have at # least one element in the section list when they are called. # fp = StringIO() g = Generator(fp, mangle_from_=False) g.flatten(msg) msg_text = email.utils.fix_eols(fp.getvalue()) else: if len(section) == 1: fp = StringIO() if isinstance(section[0], int): # The want a sub-part. Get that sub-part. Watch # out for messages that are not multipart. You can # get a sub-part of these as long as it is only # sub-part #1. # g = TextGenerator(fp, headers=False) if section[0] == 1 and not msg.is_multipart(): # The logic is simpler to read this way.. if # they want section 1 and this message is NOT # a multipart message, then do the same as # 'TEXT' ie: We will fall through to the end of this # function where we will take the generator # already created above and use it. # pass else: # Otherwise, get the sub-part they are after # as the message to pass to the generator. # if section[0] != 1 and not msg.is_multipart(): raise BadSection("Trying to retrieve section %d " "and this message is not " "multipart" % section[0]) try: msg = msg.get_payload(section[0]-1) except IndexError: raise BadSection("Section %d does not exist in " "this message sub-part" % section[0]) elif (isinstance(section[0], str) and section[0].upper() == 'TEXT'): g = TextGenerator(fp, headers=False) elif isinstance(section[0], (list, tuple)): if section[0][0].upper() == "HEADER.FIELDS": g = HeaderGenerator(fp, headers=section[0][1], skip=False) elif section[0][0].upper() == "HEADER.FIELDS.NOT": g = HeaderGenerator(fp, headers=section[0][1], skip=True) else: raise BadSection("Section value must be either " "HEADER.FIELDS or HEADER.FIELDS.NOT, " "not: %s" % section[0][0]) else: g = HeaderGenerator(fp) if (isinstance(section[0], str) and section[0].upper() == 'MIME'): # XXX just use the generator as it is for MIME.. I know # this is not quite right in that it will accept more # then it should, but it will otherwise work. # pass elif (isinstance(section[0], str) and section[0].upper() == 'HEADER'): # if the content type is message/rfc822 then to # get the headers we need to use the first # sub-part of this message. # if msg.is_multipart() and \ msg.get_content_type() == 'message/rfc822': msg = msg.get_payload(0) else: self.log.warn("body: Unexpected section[0] value: %s" % repr(section)) raise BadSection("%s: Unexpected section value: %s" % (str(self), repr(section[0]))) elif isinstance(section[0], int): # We have an integer sub-section. This means that we # need to pull apart the message (it MUST be a # multi-part message) and pass to a recursive # invocation of this function the sub-part and the # section list (with the top element of the section # list removed.) # if not msg.is_multipart(): raise BadSection("Message does not contain subsection %d " "(section list: %s" % section[0], section) try: msg = msg.get_payload(section[0]-1) except TypeError: raise BadSection("Message does not contain subsection %d " "(section list: %s)" % section[0], section) return self.body(msg, section[1:]) # We should have a generator that will give us the text # that we want. If we do not then we were not able to find # an appropriate section to parse. # if g is None: raise BadSection("Section selector '%s' can not be parsed" % section) g.flatten(msg) msg_text = fp.getvalue() # We have our message text we need to return to our caller. # truncate if it we also have a 'partial' defined. # # Convert \n in to \r\n # msg_text = email.utils.fix_eols(msg_text) if self.partial: msg_text = msg_text[self.partial[0]:self.partial[1]] # We return all of these body sections as a length prefixed # IMAP string. Also run the message text through the wringer # converter it through unicode via utf8 back in to bytes # (since we are sending messages over the network we want # stuff a bytes as much as possible.) # msg_text = to_bytes(msg_text.decode('utf8', 'replace')) return "{%d}\r\n%s" % (len(msg_text), msg_text)
def _write_headers(self, msg): if not self.__skipheaders: Generator._write_headers(self, msg)
# Copyright (C) 2001 Python Software Foundation
def __init__(self, outfp, headers=[], skip=True): self.log = logging.getLogger("%s.%s" % (__name__, self.__class__.__name__)) Generator.__init__(self, outfp) self._headers = [x.lower() for x in headers] self._skip = skip
def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, skipheaders=True): Generator.__init__(self, outfp, mangle_from_=False) self.__skipheaders = skipheaders