def add_to_toc(self, msg, count): """Add a message to the table of contents.""" subject = msg.get('subject', _('(no subject)')) subject = oneline(subject, in_unicode=True) # Don't include the redundant subject prefix in the toc mo = re.match('(re:? *)?({0})'.format( re.escape(self._mlist.subject_prefix)), subject, re.IGNORECASE) if mo: subject = subject[:mo.start(2)] + subject[mo.end(2):] # Take only the first author we find. username = '' addresses = getaddresses( [oneline(msg.get('from', ''), in_unicode=True)]) if addresses: username = addresses[0][0] if not username: username = addresses[0][1] if username: username = '******'.format(username) lines = wrap('{0:2}. {1}'. format(count, subject), 65).split('\n') # See if the user's name can fit on the last line if len(lines[-1]) + len(username) > 70: lines.append(username) else: lines[-1] += username # Add this subject to the accumulating topics first = True for line in lines: if first: print(' ', line, file=self._toc) first = False else: print(' ', line.lstrip(), file=self._toc)
def add_to_toc(self, msg, count): """Add a message to the table of contents.""" subject = msg.get('subject', _('(no subject)')) subject = oneline(subject, in_unicode=True) # Don't include the redundant subject prefix in the toc mo = re.match( '(re:? *)?({0})'.format(re.escape(self._mlist.subject_prefix)), subject, re.IGNORECASE) if mo: subject = subject[:mo.start(2)] + subject[mo.end(2):] # Take only the first author we find. username = '' addresses = getaddresses( [oneline(msg.get('from', ''), in_unicode=True)]) if addresses: username = addresses[0][0] if not username: username = addresses[0][1] if username: username = '******'.format(username) lines = wrap('{:2}. {}'.format(count, subject), 65).split('\n') # See if the user's name can fit on the last line if len(lines[-1]) + len(username) > 70: lines.append(username) else: lines[-1] += username # Add this subject to the accumulating topics first = True for line in lines: if first: print(' ', line, file=self._toc) first = False else: print(' ', line.lstrip(), file=self._toc)
def add_message(self, msg, count): """Add the message to the digest.""" if count > 1: print(self._separator30, file=self._text) print(file=self._text) # Each message section contains a few headers. for header in config.digests.plain_digest_keep_headers.split(): if header in msg: value = oneline(msg[header], in_unicode=True) value = wrap('{}: {}'.format(header, value)) value = '\n\t'.join(value.split('\n')) print(value, file=self._text) print(file=self._text) # Add the payload. If the decoded payload is empty, this may be a # multipart message. In that case, just stringify it. payload = msg.get_payload(decode=True) if not payload: payload = msg.as_string().split('\n\n', 1)[1] if isinstance(payload, bytes): try: # Do the decoding inside the try/except so that if the charset # conversion fails, we'll just drop back to ascii. charset = msg.get_content_charset('us-ascii') payload = payload.decode(charset, 'replace') except (LookupError, TypeError): # Unknown or empty charset. payload = payload.decode('us-ascii', 'replace') print(payload, file=self._text) if not payload.endswith('\n'): print(file=self._text)
def bounce_message(mlist, msg, error=None): """Bounce the message back to the original author. :param mlist: The mailing list that the message was posted to. :type mlist: `IMailingList` :param msg: The original message. :type msg: `email.message.Message` :param error: Optional exception causing the bounce. The exception instance must have a `.message` attribute. :type error: Exception """ # Bounce a message back to the sender, with an error message if provided # in the exception argument. .sender might be None or the empty string. if not msg.sender: # We can't bounce the message if we don't know who it's supposed to go # to. return subject = msg.get('subject', _('(no subject)')) subject = oneline(subject, mlist.preferred_language.charset) if error is None: notice = _('[No bounce details are available]') else: notice = _(error.message) # Currently we always craft bounces as MIME messages. bmsg = UserNotification(msg.sender, mlist.owner_address, subject, lang=mlist.preferred_language) # BAW: Be sure you set the type before trying to attach, or you'll get # a MultipartConversionError. bmsg.set_type('multipart/mixed') txt = MIMEText(notice, _charset=mlist.preferred_language.charset) bmsg.attach(txt) bmsg.attach(MIMEMessage(msg)) bmsg.send(mlist)
def add_message(self, msg, count): """Add the message to the digest.""" if count > 1: print >> self._text, self._separator30 print >> self._text # Each message section contains a few headers. for header in config.digests.plain_digest_keep_headers.split(): if header in msg: value = oneline(msg[header], in_unicode=True) value = wrap('{0}: {1}'.format(header, value)) value = '\n\t'.join(value.split('\n')) print >> self._text, value print >> self._text # Add the payload. If the decoded payload is empty, this may be a # multipart message. In that case, just stringify it. payload = msg.get_payload(decode=True) payload = (payload if payload else msg.as_string().split('\n\n', 1)[1]) try: charset = msg.get_content_charset('us-ascii') payload = unicode(payload, charset, 'replace') except (LookupError, TypeError): # Unknown or empty charset. payload = unicode(payload, 'us-ascii', 'replace') print >> self._text, payload if not payload.endswith('\n'): print >> self._text
def add_message(self, msg, count): """Add the message to the digest.""" if count > 1: print(self._separator30, file=self._text) print(file=self._text) # Each message section contains a few headers. for header in config.digests.plain_digest_keep_headers.split(): if header in msg: value = oneline(msg[header], in_unicode=True) value = wrap('{0}: {1}'.format(header, value)) value = '\n\t'.join(value.split('\n')) print(value, file=self._text) print(file=self._text) # Add the payload. If the decoded payload is empty, this may be a # multipart message. In that case, just stringify it. payload = msg.get_payload(decode=True) if not payload: payload = msg.as_string().split('\n\n', 1)[1] if isinstance(payload, bytes): try: # Do the decoding inside the try/except so that if the charset # conversion fails, we'll just drop back to ascii. charset = msg.get_content_charset('us-ascii') payload = payload.decode(charset, 'replace') except (LookupError, TypeError): # Unknown or empty charset. payload = payload.decode('us-ascii', 'replace') print(payload, file=self._text) if not payload.endswith('\n'): print(file=self._text)
def get_file_ext(m): """ Get filename extension. Caution: some virus don't put filename in 'Content-Disposition' header. """ fext = '' filename = m.get_filename('') or m.get_param('name', '') if filename: fext = os.path.splitext(oneline(filename,'utf-8'))[1] if len(fext) > 1: fext = fext[1:] else: fext = '' return fext
def get_file_ext(m): """ Get filename extension. Caution: some virus don't put filename in 'Content-Disposition' header. """ fext = '' filename = m.get_filename('') or m.get_param('name', '') if filename: fext = os.path.splitext(oneline(filename, 'utf-8', in_unicode=True))[1] if len(fext) > 1: fext = fext[1:] else: fext = '' return fext.lower()
def process(self, mlist, msg, msgdata): """See `IHandler`.""" # Extract the sender's address and find them in the user database sender = msgdata.get('original_sender', msg.sender) member = mlist.members.get_member(sender) if member is None or not member.acknowledge_posts: # Either the sender is not a member, in which case we can't know # whether they want an acknowlegment or not, or they are a member # who definitely does not want an acknowlegment. return # Okay, they are a member that wants an acknowledgment of their post. # Give them their original subject. BAW: do we want to use the # decoded header? original_subject = msgdata.get( 'origsubj', msg.get('subject', _('(no subject)'))) # Get the user's preferred language. language_manager = getUtility(ILanguageManager) language = (language_manager[msgdata['lang']] if 'lang' in msgdata else member.preferred_language) charset = language_manager[language.code].charset # Now get the acknowledgement template. display_name = mlist.display_name text = make('postack.txt', mailing_list=mlist, language=language.code, wrap=False, subject=oneline(original_subject, charset), list_name=mlist.list_name, display_name=display_name, listinfo_url=mlist.script_url('listinfo'), optionsurl=member.options_url, ) # Craft the outgoing message, with all headers and attributes # necessary for general delivery. Then enqueue it to the outgoing # queue. subject = _('$display_name post acknowledgment') usermsg = UserNotification(sender, mlist.bounces_address, subject, text, language) usermsg.send(mlist)
def process(self, mlist, msg, msgdata): """See `IHandler`.""" # Extract the sender's address and find them in the user database sender = msgdata.get('original_sender', msg.sender) member = mlist.members.get_member(sender) if member is None or not member.acknowledge_posts: # Either the sender is not a member, in which case we can't know # whether they want an acknowlegment or not, or they are a member # who definitely does not want an acknowlegment. return # Okay, they are a member that wants an acknowledgment of their post. # Give them their original subject. BAW: do we want to use the # decoded header? original_subject = msgdata.get('origsubj', msg.get('subject', _('(no subject)'))) # Get the user's preferred language. language_manager = getUtility(ILanguageManager) language = (language_manager[msgdata['lang']] if 'lang' in msgdata else member.preferred_language) # Now get the acknowledgement template. display_name = mlist.display_name # noqa: F841 template = getUtility(ITemplateLoader).get('list:user:notice:post', mlist, language=language.code) text = expand( template, mlist, dict( subject=oneline(original_subject, in_unicode=True), # For backward compatibility. list_name=mlist.list_name, )) # Craft the outgoing message, with all headers and attributes # necessary for general delivery. Then enqueue it to the outgoing # queue. subject = _('$display_name post acknowledgment') usermsg = UserNotification(sender, mlist.bounces_address, subject, text, language) usermsg.send(mlist)
def add_message(self, msg, count): """Add the message to the digest.""" if count > 1: print(self._separator30, file=self._text) print(file=self._text) # Each message section contains a few headers. # add the Message: n header first. print('Message: {}'.format(count), file=self._text) # Then the others. for header in config.digests.plain_digest_keep_headers.split(): if header in msg: value = oneline(msg[header], in_unicode=True) value = wrap('{}: {}'.format(header, value)) value = '\n\t'.join(value.split('\n')) print(value, file=self._text) print(file=self._text) # Get the scrubbed payload. This is the original payload with all # non text/plain parts replaced by notes that they've been removed. payload = scrub(msg) # Add the payload. print(payload, file=self._text) if not payload.endswith('\n'): print(file=self._text)
def bounce_message(mlist, msg, error=None): """Bounce the message back to the original author. :param mlist: The mailing list that the message was posted to. :type mlist: `IMailingList` :param msg: The original message. :type msg: `email.message.Message` :param error: Optional exception causing the bounce. The exception instance must have a `.message` attribute. The exception *may* have a non-None `.reasons` attribute which would be a list of reasons for the rejection, and it may have a non-None `.substitutions` attribute. The latter, along with the formatted reasons will be interpolated into the message (`.reasons` gets put into the `$reasons` placeholder). :type error: RejectMessage """ # Bounce a message back to the sender, with an error message if provided # in the exception argument. .sender might be None or the empty string. if not msg.sender: # We can't bounce the message if we don't know who it's supposed to go # to. return subject = msg.get('subject', _('(no subject)')) subject = oneline(subject, mlist.preferred_language.charset) notice = (_('[No bounce details are available]') if error is None else str(error)) # Currently we always craft bounces as MIME messages. bmsg = UserNotification(msg.sender, mlist.owner_address, subject, lang=mlist.preferred_language) # BAW: Be sure you set the type before trying to attach, or you'll get # a MultipartConversionError. bmsg.set_type('multipart/mixed') txt = MIMEText(notice, _charset=mlist.preferred_language.charset) bmsg.attach(txt) bmsg.attach(MIMEMessage(msg)) bmsg.send(mlist)
def _process(self, mlist, msg, msgdata): """See `TerminalChainBase`.""" # Start by decorating the message with a header that contains a list # of all the rules that matched. These metadata could be None or an # empty list. rule_hits = msgdata.get('rule_hits') if rule_hits: msg['X-Mailman-Rule-Hits'] = SEMISPACE.join(rule_hits) rule_misses = msgdata.get('rule_misses') if rule_misses: msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) # Hold the message by adding it to the list's request database. request_id = hold_message(mlist, msg, msgdata, None) # Calculate a confirmation token to send to the author of the # message. pendable = HeldMessagePendable(id=request_id) token = getUtility(IPendings).add(pendable) # Get the language to send the response in. If the sender is a # member, then send it in the member's language, otherwise send it in # the mailing list's preferred language. member = mlist.members.get_member(msg.sender) language = (member.preferred_language if member else mlist.preferred_language) # A substitution dictionary for the email templates. charset = mlist.preferred_language.charset original_subject = msg.get('subject') if original_subject is None: original_subject = _('(no subject)') else: # This must be encoded to the mailing list's perferred charset, # ignoring incompatible characters, otherwise when creating the # notification messages, we could get a Unicode error. oneline_subject = oneline(original_subject, in_unicode=True) bytes_subject = oneline_subject.encode(charset, 'replace') original_subject = bytes_subject.decode(charset) substitutions = dict( subject=original_subject, sender_email=msg.sender, reasons=_compose_reasons(msgdata), # For backward compatibility. sender=msg.sender, ) # At this point the message is held, but now we have to craft at least # two responses. The first will go to the original author of the # message and it will contain the token allowing them to approve or # discard the message. The second one will go to the moderators of # the mailing list, if the list is so configured. # # Start by possibly sending a response to the message author. There # are several reasons why we might not go through with this. If the # message was gated from NNTP, the author may not even know about this # list, so don't spam them. If the author specifically requested that # acknowledgments not be sent, or if the message was bulk email, then # we do not send the response. It's also possible that either the # mailing list, or the author (if they are a member) have been # configured to not send such responses. if (not msgdata.get('fromusenet') and can_acknowledge(msg) and mlist.respond_to_post_requests and autorespond_to_sender(mlist, msg.sender, language)): # We can respond to the sender with a message indicating their # posting was held. subject = _( 'Your message to $mlist.fqdn_listname awaits moderator approval') send_language_code = msgdata.get('lang', language.code) template = getUtility(ITemplateLoader).get( 'list:user:notice:hold', mlist, language=send_language_code) text = wrap(expand(template, mlist, dict( language=send_language_code, **substitutions))) adminaddr = mlist.bounces_address nmsg = UserNotification( msg.sender, adminaddr, subject, text, getUtility(ILanguageManager)[send_language_code]) nmsg.send(mlist) # Now the message for the list moderators. This one should appear to # come from <list>-owner since we really don't need to do bounce # processing on it. if mlist.admin_immed_notify: # Now let's temporarily set the language context to that which the # administrators are expecting. with _.using(mlist.preferred_language.code): language = mlist.preferred_language charset = language.charset substitutions['subject'] = original_subject # We need to regenerate or re-translate a few values in the # substitution dictionary. substitutions['reasons'] = _compose_reasons(msgdata, 55) # craft the admin notification message and deliver it subject = _( '$mlist.fqdn_listname post from $msg.sender requires ' 'approval') nmsg = UserNotification(mlist.owner_address, mlist.owner_address, subject, lang=language) nmsg.set_type('multipart/mixed') template = getUtility(ITemplateLoader).get( 'list:admin:action:post', mlist) text = MIMEText(expand(template, mlist, substitutions), _charset=charset) dmsg = MIMEText(wrap(_("""\ If you reply to this message, keeping the Subject: header intact, Mailman will discard the held message. Do this if the message is spam. If you reply to this message and include an Approved: header with the list password in it, the message will be approved for posting to the list. The Approved: header can also appear in the first line of the body of the reply.""")), _charset=language.charset) dmsg['Subject'] = 'confirm ' + token dmsg['From'] = mlist.request_address dmsg['Date'] = formatdate(localtime=True) dmsg['Message-ID'] = make_msgid() nmsg.attach(text) nmsg.attach(MIMEMessage(msg)) nmsg.attach(MIMEMessage(dmsg)) nmsg.send(mlist, **dict(tomoderators=True)) # Log the held message. Log messages are not translated, so recast # the reasons in the English. with _.using('en'): reasons = msgdata.get('moderation_reasons', ['N/A']) log.info('HOLD: %s post from %s held, message-id=%s: %s', mlist.fqdn_listname, msg.sender, msg.get('message-id', 'n/a'), SEMISPACE.join(reasons)) notify(HoldEvent(mlist, msg, msgdata, self))
def parse_attachment(self, part, counter, filter_html=True): # Store name, content-type and size # Figure out the attachment type and get the decoded data decodedpayload = part.get_payload(decode=True) # BAW: mimetypes ought to handle non-standard, but commonly found types, # e.g. image/jpg (should be image/jpeg). For now we just store such # things as application/octet-streams since that seems the safest. ctype = part.get_content_type() charset = get_charset(part, default=None, guess=False) # i18n file name is encoded try: filename = oneline(part.get_filename(''), in_unicode=True) except TypeError: # Workaround for https://bugs.launchpad.net/mailman/+bug/1060951 # (accented filenames) filename = "attachment.bin" filename, fnext = os.path.splitext(filename) # For safety, we should confirm this is valid ext for content-type # but we can use fnext if we introduce fnext filtering # TODO: re-implement this #if mm_cfg.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION: # # HTML message doesn't have filename :-( # ext = fnext or guess_extension(ctype, fnext) #else: # ext = guess_extension(ctype, fnext) ext = fnext or guess_extension(ctype, fnext) if not ext: # We don't know what it is, so assume it's just a shapeless # application/octet-stream, unless the Content-Type: is # message/rfc822, in which case we know we'll coerce the type to # text/plain below. if ctype == 'message/rfc822': ext = '.txt' else: ext = '.bin' # Allow only alphanumerics, dash, underscore, and dot ext = sre.sub('', ext) # Now base the filename on what's in the attachment, uniquifying it if # necessary. if not filename: filebase = 'attachment' else: # Sanitize the filename given in the message headers parts = pre.split(filename) filename = parts[-1] # Strip off leading dots filename = dre.sub('', filename) # Allow only alphanumerics, dash, underscore, and dot # i18n filenames are not supported yet, # see https://bugs.launchpad.net/bugs/1060951 filename = sre.sub('', filename) # If the filename's extension doesn't match the type we guessed, # which one should we go with? For now, let's go with the one we # guessed so attachments can't lie about their type. Also, if the # filename /has/ no extension, then tack on the one we guessed. # The extension was removed from the name above. filebase = filename # TODO: bring back the HTML sanitizer feature if ctype == 'message/rfc822': submsg = part.get_payload() # Don't HTML-escape it, this is the frontend's job ## BAW: I'm sure we can eventually do better than this. :( #decodedpayload = websafe(str(submsg)) decodedpayload = str(submsg) return (counter, filebase + ext, ctype, charset, decodedpayload)
def _process(self, mlist, msg, msgdata): """See `TerminalChainBase`.""" # Start by decorating the message with a header that contains a list # of all the rules that matched. These metadata could be None or an # empty list. rule_hits = msgdata.get('rule_hits') if rule_hits: msg['X-Mailman-Rule-Hits'] = SEMISPACE.join(rule_hits) rule_misses = msgdata.get('rule_misses') if rule_misses: msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) # Hold the message by adding it to the list's request database. request_id = hold_message(mlist, msg, msgdata, None) # Calculate a confirmation token to send to the author of the # message. pendable = HeldMessagePendable(type=HeldMessagePendable.PEND_KEY, id=request_id) token = getUtility(IPendings).add(pendable) # Get the language to send the response in. If the sender is a # member, then send it in the member's language, otherwise send it in # the mailing list's preferred language. member = mlist.members.get_member(msg.sender) language = (member.preferred_language if member else mlist.preferred_language) # A substitution dictionary for the email templates. charset = mlist.preferred_language.charset original_subject = msg.get('subject') if original_subject is None: original_subject = _('(no subject)') else: original_subject = oneline(original_subject, in_unicode=True) substitutions = dict( listname = mlist.fqdn_listname, subject = original_subject, sender = msg.sender, reasons = _compose_reasons(msgdata), ) # At this point the message is held, but now we have to craft at least # two responses. The first will go to the original author of the # message and it will contain the token allowing them to approve or # discard the message. The second one will go to the moderators of # the mailing list, if the list is so configured. # # Start by possibly sending a response to the message author. There # are several reasons why we might not go through with this. If the # message was gated from NNTP, the author may not even know about this # list, so don't spam them. If the author specifically requested that # acknowledgments not be sent, or if the message was bulk email, then # we do not send the response. It's also possible that either the # mailing list, or the author (if they are a member) have been # configured to not send such responses. if (not msgdata.get('fromusenet') and can_acknowledge(msg) and mlist.respond_to_post_requests and autorespond_to_sender(mlist, msg.sender, language)): # We can respond to the sender with a message indicating their # posting was held. subject = _( 'Your message to $mlist.fqdn_listname awaits moderator approval') send_language_code = msgdata.get('lang', language.code) text = make('postheld.txt', mailing_list=mlist, language=send_language_code, **substitutions) adminaddr = mlist.bounces_address nmsg = UserNotification( msg.sender, adminaddr, subject, text, getUtility(ILanguageManager)[send_language_code]) nmsg.send(mlist) # Now the message for the list moderators. This one should appear to # come from <list>-owner since we really don't need to do bounce # processing on it. if mlist.admin_immed_notify: # Now let's temporarily set the language context to that which the # administrators are expecting. with _.using(mlist.preferred_language.code): language = mlist.preferred_language charset = language.charset substitutions['subject'] = original_subject # We need to regenerate or re-translate a few values in the # substitution dictionary. substitutions['reasons'] = _compose_reasons(msgdata, 55) # craft the admin notification message and deliver it subject = _( '$mlist.fqdn_listname post from $msg.sender requires ' 'approval') nmsg = UserNotification(mlist.owner_address, mlist.owner_address, subject, lang=language) nmsg.set_type('multipart/mixed') text = MIMEText(make('postauth.txt', mailing_list=mlist, wrap=False, **substitutions), _charset=charset) dmsg = MIMEText(wrap(_("""\ If you reply to this message, keeping the Subject: header intact, Mailman will discard the held message. Do this if the message is spam. If you reply to this message and include an Approved: header with the list password in it, the message will be approved for posting to the list. The Approved: header can also appear in the first line of the body of the reply.""")), _charset=language.charset) dmsg['Subject'] = 'confirm ' + token dmsg['From'] = mlist.request_address dmsg['Date'] = formatdate(localtime=True) dmsg['Message-ID'] = make_msgid() nmsg.attach(text) nmsg.attach(MIMEMessage(msg)) nmsg.attach(MIMEMessage(dmsg)) nmsg.send(mlist, **dict(tomoderators=True)) # Log the held message. Log messages are not translated, so recast # the reasons in the English. with _.using('en'): reasons = _compose_reasons(msgdata) log.info('HOLD: %s post from %s held, message-id=%s: %s', mlist.fqdn_listname, msg.sender, msg.get('message-id', 'n/a'), SEMISPACE.join(reasons)) notify(HoldEvent(mlist, msg, msgdata, self))
def parse_attachment(self, part, counter, filter_html=True): # Store name, content-type and size # Figure out the attachment type and get the decoded data decodedpayload = part.get_payload(decode=True) # BAW: mimetypes ought to handle non-standard, but commonly found types, # e.g. image/jpg (should be image/jpeg). For now we just store such # things as application/octet-streams since that seems the safest. ctype = part.get_content_type() if not isinstance(ctype, unicode): ctype = ctype.decode("ascii") charset = get_charset(part, default=None, guess=False) # i18n file name is encoded try: filename = oneline(part.get_filename(''), in_unicode=True) except (TypeError, UnicodeDecodeError): # Workaround for https://bugs.launchpad.net/mailman/+bug/1060951 # (accented filenames) filename = u"attachment.bin" filename, fnext = os.path.splitext(filename) # For safety, we should confirm this is valid ext for content-type # but we can use fnext if we introduce fnext filtering # TODO: re-implement this #if mm_cfg.SCRUBBER_USE_ATTACHMENT_FILENAME_EXTENSION: # # HTML message doesn't have filename :-( # ext = fnext or guess_extension(ctype, fnext) #else: # ext = guess_extension(ctype, fnext) ext = fnext or guess_extension(ctype, fnext) if not ext: # We don't know what it is, so assume it's just a shapeless # application/octet-stream, unless the Content-Type: is # message/rfc822, in which case we know we'll coerce the type to # text/plain below. if ctype == 'message/rfc822': ext = '.txt' else: ext = '.bin' # Allow only alphanumerics, dash, underscore, and dot ext = sre.sub('', ext) # Now base the filename on what's in the attachment, uniquifying it if # necessary. if not filename: filebase = u'attachment' else: # Sanitize the filename given in the message headers parts = pre.split(filename) filename = parts[-1] # Strip off leading dots filename = dre.sub('', filename) # Allow only alphanumerics, dash, underscore, and dot # i18n filenames are not supported yet, # see https://bugs.launchpad.net/bugs/1060951 filename = sre.sub('', filename) # If the filename's extension doesn't match the type we guessed, # which one should we go with? For now, let's go with the one we # guessed so attachments can't lie about their type. Also, if the # filename /has/ no extension, then tack on the one we guessed. # The extension was removed from the name above. filebase = filename # TODO: bring back the HTML sanitizer feature if ctype == 'message/rfc822': submsg = part.get_payload() # Don't HTML-escape it, this is the frontend's job ## BAW: I'm sure we can eventually do better than this. :( #decodedpayload = websafe(str(submsg)) decodedpayload = str(submsg) return (counter, filebase+ext, ctype, charset, decodedpayload)
def test_oneline_bogus_charset(self): self.assertEqual(string.oneline('foo', 'bogus'), 'foo')