Example #1
0
def send(msg, smtpurl, port, keyfile, certfile, username, password):
	srcaddr = getaddresses(msg.get_all('From',[]))
	tos = msg.get_all('to',[])
	ccs = msg.get_all('cc',[])
	bccs = msg.get_all('bcc',[])
	resent_tos = msg.get_all('resent-to',[])
	resent_ccs = msg.get_all('resent-cc',[])
	toaddrlist = getaddresses(tos + ccs + bccs + resent_tos + resent_ccs)

	destaddr = []
	for (name, addr) in toaddrlist:
		if not (addr in destaddr):
			destaddr.append(addr)
	del msg['Bcc']

	client = smtplib.SMTP()
	#client.set_debuglevel(2)
	client.connect(smtpurl, port)
	client.ehlo()

	if keyfile and certfile:
		client.starttls(keyfile, certfile)
	else:
		client.starttls()

	client.ehlo()

	if username and password:
		client.login(username, password)
		
	client.sendmail(srcaddr[0][1], destaddr, rstrip(msg.as_string()))
	client.quit()
Example #2
0
def getAttachements(rfcmessage):
    container = []
    message = email.message_from_string(rfcmessage)
    subject = decode_subject(message["subject"])
    
    tos = message.get_all('to', [])
    ccs = message.get_all('cc', [])
    resent_tos = message.get_all('resent-to', [])
    resent_ccs = message.get_all('resent-cc', [])
    all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)

    froms = message.get_all('from', [])
    try:
        message_id = message["message-id"]
    except:
        message_id = "not_unique"
    sender = getaddresses(froms)

    recepients = []
    for to_whom_name, to_whom in all_recipients:
        try:
            recepients.append(USER_TO_URL_RE.match(to_whom).groupdict())
        except:
            pass

    counter = 0
    for msg in message.walk():
        if not msg.is_multipart() and msg.get_content_maintype() != 'message':
            ct = msg.get_content_type()
            mtype = msg.get_content_maintype().lower()
            payload = msg.get_payload()
            te = msg.get('Content-Transfer-Encoding', '8bit').lower()
            if te == 'base64':
                try: payload = email.base64MIME.decode(payload)
                except: payload = ''   # ???
            elif te == 'quoted-printable':
                try: payload = email.quopriMIME.decode(payload)
                except: payload = ''   # ???
            if mtype == 'text' or mtype is None:
                try: tc = msg.get_content_charset()
                except: tc = 'latin-1'
            else:
                pass

            if mtype in ('image', 'video', 'audio', 'text', 'application'):
                counter += 1
                name =  dict(msg.get_params() or []).get('name',
                                             'noname-%i.txt' % counter)
            container.append((ct, name, payload))
        else:
            pass
        
    return Storage(sender=sender, 
                   recepients=recepients,
                   subject=subject, 
                   message_id=message_id,
                   container=container,)
def secureSend(self, message, mto, mfrom, subject='[No Subject]',
               mcc=None, mbcc=None, subtype='plain', charset='us-ascii',
               debug=False, **kwargs):
    """Deprecated method attempting to maintain backwards
    compatibility for code depending on the SecureMailHost API."""
    # Convert all our address list inputs
    mfrom = email_list_to_string(mfrom, charset)
    mto = email_list_to_string(mto, charset)
    mcc = email_list_to_string(mcc, charset)
    mbcc = email_list_to_string(mbcc, charset)

    # Convert to a message for adding headers.  If it's already a
    # message, copy it to be safe.
    if not isinstance(message, Message):
        if isinstance(message, unicode):
            message.encode(charset)
        message = MIMEText(message, subtype, charset)
    else:
        message = deepcopy(message)

    # Add extra headers
    _addHeaders(message, Subject=Header(subject, charset),
                To=mto, Cc=mcc, From=mfrom,
                **dict((k, Header(v, charset)) for k, v in kwargs.iteritems()))

    all_recipients = [formataddr(pair) for pair in
                      getaddresses((mto, mcc, mbcc))]

    # Convert message back to string for sending
    self._send(mfrom, all_recipients, message.as_string(), immediate=True)
Example #4
0
def main():
    fileconf = os.path.expanduser("~/.mysender.conf")
    conf = ConfigParser.ConfigParser()
    conf.readfp(open(fileconf))

    msg = email.message_from_file(sys.stdin)
    srcaddr = getaddresses(msg.get_all("From", []))

    (user, account) = split(srcaddr[0][1], "@")

    host = conf.get(account, "host")
    port = conf.getint(account, "port")

    keyfile = str()
    if conf.has_option(account, "keyfile"):
        keyfile = conf.get(account, "keyfile")

    certfile = str()
    if conf.has_option(account, "certfile"):
        certfile = conf.get(account, "certfile")

    username = str()
    if conf.has_option(account, "username"):
        username = conf.get(account, "username")

    password = str()
    if conf.has_option(account, "password"):
        password = conf.get(account, "password")

    send(msg, host, port, keyfile, certfile, username, password)
Example #5
0
def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or settings.DEFAULT_CHARSET
    val = force_unicode(val)
    if '\n' in val or '\r' in val:
        raise BadHeaderError(
            "Header values can't contain newlines (got %r for header %r)" %
            (val, name))
    try:
        val = val.encode('ascii')
    except UnicodeEncodeError:
        if name.lower() in ('to', 'from', 'cc'):
            result = []
            for nm, addr in getaddresses((val, )):
                nm = str(Header(nm.encode(encoding), encoding))
                try:
                    addr = addr.encode('ascii')
                except UnicodeEncodeError:  # IDN
                    addr = str(Header(addr.encode(encoding), encoding))
                result.append(formataddr((nm, addr)))
            val = ', '.join(result)
        else:
            val = Header(val.encode(encoding), encoding)
    else:
        if name.lower() == 'subject':
            val = Header(val)
    return name, val
Example #6
0
def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or settings.DEFAULT_CHARSET
    val = force_unicode(val)
    if '\n' in val or '\r' in val:
        raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    try:
        val = val.encode('ascii')
    except UnicodeEncodeError:
        if name.lower() in ('to', 'from', 'cc'):
            result = []
            for nm, addr in getaddresses((val,)):
                nm = str(Header(nm.encode(encoding), encoding))
                try:
                    addr = addr.encode('ascii')
                except UnicodeEncodeError:  # IDN
                    addr = str(Header(addr.encode(encoding), encoding))
                result.append(formataddr((nm, addr)))
            val = ', '.join(result)
        else:
            val = Header(val.encode(encoding), encoding)
    else:
        if name.lower() == 'subject':
            val = Header(val)
    return name, val
Example #7
0
 def check_to_cc(self, arguments):
     assert not arguments, arguments
     from email.Utils import getaddresses
     count = len(getaddresses(self.message.get_all('To', [])
                              + self.message.get_all('Cc', [])))
     if count > 50:
         self.checker.reject("More than 50 recipients (%d)." % count)
Example #8
0
def secureSend(self, message, mto, mfrom, subject='[No Subject]',
               mcc=None, mbcc=None, subtype='plain', charset='us-ascii',
               debug=False, **kwargs):
    """Deprecated method attempting to maintain backwards
    compatibility for code depending on the SecureMailHost API."""
    # Convert all our address list inputs
    mfrom = email_list_to_string(mfrom, charset)
    mto = email_list_to_string(mto, charset)
    mcc = email_list_to_string(mcc, charset)
    mbcc = email_list_to_string(mbcc, charset)

    # Convert to a message for adding headers.  If it's already a
    # message, copy it to be safe.
    if not isinstance(message, Message):
        if isinstance(message, unicode):
            message.encode(charset)
        message = MIMEText(message, subtype, charset)
    else:
        message = deepcopy(message)

    # Add extra headers
    _addHeaders(message, Subject=Header(subject, charset),
                To=mto, Cc=mcc, From=mfrom,
                **dict((k, Header(v, charset)) for k, v in kwargs.iteritems()))

    all_recipients = [formataddr(pair) for pair in
                      getaddresses((mto, mcc, mbcc))]

    # Convert message back to string for sending
    self._send(mfrom, all_recipients, message.as_string(), immediate=True)
Example #9
0
def main():
	fileconf = os.path.expanduser('~/.mysender.conf')
	conf = ConfigParser.ConfigParser()
	conf.readfp(open(fileconf))

	msg = email.message_from_file(sys.stdin)
	srcaddr = getaddresses(msg.get_all('From',[]))

	(user, account) = split(srcaddr[0][1],'@')
	
	host = conf.get(account, 'host')
	port = conf.getint(account, 'port')
	
	keyfile = str()
	if conf.has_option(account, 'keyfile'):
		keyfile = conf.get(account, 'keyfile')

	certfile = str()
	if conf.has_option(account, 'certfile'):
		certfile = conf.get(account, 'certfile')

	username = str()
	if conf.has_option(account, 'username'):
		username = conf.get(account, 'username')
		
	password = str()
	if conf.has_option(account, 'password'):
		password = conf.get(account, 'password')

	send(msg, host, port, keyfile, certfile, username, password)
Example #10
0
def determine_sender(mail, action='reply'):
    """
    Inspect a given mail to reply/forward/bounce and find the most appropriate
    account to act from and construct a suitable From-Header to use.

    :param mail: the email to inspect
    :type mail: `email.message.Message`
    :param action: intended use case: one of "reply", "forward" or "bounce"
    :type action: str
    """
    assert action in ['reply', 'forward', 'bounce']
    realname = None
    address = None

    # get accounts
    my_accounts = settings.get_accounts()
    assert my_accounts, 'no accounts set!'

    # extract list of addresses to check for my address
    # X-Envelope-To and Envelope-To are used to store the recipient address
    # if not included in other fields
    candidate_addresses = getaddresses(mail.get_all('To', []) +
                                       mail.get_all('Cc', []) +
                                       mail.get_all('Delivered-To', []) +
                                       mail.get_all('X-Envelope-To', []) +
                                       mail.get_all('Envelope-To', []) +
                                       mail.get_all('From', []))

    logging.debug('candidate addresses: %s' % candidate_addresses)
    # pick the most important account that has an address in candidates
    # and use that accounts realname and the address found here
    for account in my_accounts:
        acc_addresses = account.get_addresses()
        for alias in acc_addresses:
            if realname is not None:
                break
            regex = re.compile(re.escape(alias), flags=re.IGNORECASE)
            for seen_name, seen_address in candidate_addresses:
                if regex.match(seen_address):
                    logging.debug("match!: '%s' '%s'" % (seen_address, alias))
                    if settings.get(action + '_force_realname'):
                        realname = account.realname
                    else:
                        realname = seen_name
                    if settings.get(action + '_force_address'):
                        address = account.address
                    else:
                        address = seen_address

    # revert to default account if nothing found
    if realname is None:
        account = my_accounts[0]
        realname = account.realname
        address = account.address
    logging.debug('using realname: "%s"' % realname)
    logging.debug('using address: %s' % address)

    from_value = address if realname == '' else '%s <%s>' % (realname, address)
    return from_value, account
 def getCC(self):
     res = []
     buf = getaddresses(self._msg.get_all('cc', ''))
     for i in buf:
         header = decode_header(i[0])
         data = ''.join([to_unicode(s, enc) for s, enc in header if self.codecs_lookup(enc)])
         res.append((to_entities_quote(data), i[1]))
     return res
Example #12
0
def determine_sender(mail, action='reply'):
    """
    Inspect a given mail to reply/forward/bounce and find the most appropriate
    account to act from and construct a suitable From-Header to use.

    :param mail: the email to inspect
    :type mail: `email.message.Message`
    :param action: intended use case: one of "reply", "forward" or "bounce"
    :type action: str
    """
    assert action in ['reply', 'forward', 'bounce']
    realname = None
    address = None

    # get accounts
    my_accounts = settings.get_accounts()
    assert my_accounts, 'no accounts set!'

    # extract list of addresses to check for my address
    # X-Envelope-To and Envelope-To are used to store the recipient address
    # if not included in other fields
    candidate_addresses = getaddresses(
        mail.get_all('To', []) + mail.get_all('Cc', []) +
        mail.get_all('Delivered-To', []) + mail.get_all('X-Envelope-To', []) +
        mail.get_all('Envelope-To', []) + mail.get_all('From', []))

    logging.debug('candidate addresses: %s' % candidate_addresses)
    # pick the most important account that has an address in candidates
    # and use that accounts realname and the address found here
    for account in my_accounts:
        acc_addresses = account.get_addresses()
        for alias in acc_addresses:
            if realname is not None:
                break
            regex = re.compile(re.escape(alias), flags=re.IGNORECASE)
            for seen_name, seen_address in candidate_addresses:
                if regex.match(seen_address):
                    logging.debug("match!: '%s' '%s'" % (seen_address, alias))
                    if settings.get(action + '_force_realname'):
                        realname = account.realname
                    else:
                        realname = seen_name
                    if settings.get(action + '_force_address'):
                        address = account.address
                    else:
                        address = seen_address

    # revert to default account if nothing found
    if realname is None:
        account = my_accounts[0]
        realname = account.realname
        address = account.address
    logging.debug('using realname: "%s"' % realname)
    logging.debug('using address: %s' % address)

    from_value = address if realname == '' else '%s <%s>' % (realname, address)
    return from_value, account
Example #13
0
File: mail.py Project: algby/ietfdb
def send_smtp(msg, bcc=None):
    '''
    Send a Message via SMTP, based on the django email server settings.
    The destination list will be taken from the To:/Cc: headers in the
    Message.  The From address will be used if present or will default
    to the django setting DEFAULT_FROM_EMAIL

    If someone has set test_mode=True, then append the msg to
    the outbox.
    '''
    add_headers(msg)
    (fname, frm) = parseaddr(msg.get('From'))
    addrlist = msg.get_all('To') + msg.get_all('Cc', [])
    if bcc:
        addrlist += [bcc]
    to = [addr for name, addr in getaddresses(addrlist) if ( addr != '' and not addr.startswith('unknown-email-') )]
    if not to:
        log("No addressees for email from '%s', subject '%s'.  Nothing sent." % (frm, msg.get('Subject', '[no subject]')))
    else:
        if test_mode:
            outbox.append(msg)
        server = None
        try:
            server = smtplib.SMTP()
            #log("SMTP server: %s" % repr(server))
            #if settings.DEBUG:
            #    server.set_debuglevel(1)
            conn_code, conn_msg = server.connect(SMTP_ADDR['ip4'], SMTP_ADDR['port'])
            #log("SMTP connect: code: %s; msg: %s" % (conn_code, conn_msg))
            if settings.EMAIL_HOST_USER and settings.EMAIL_HOST_PASSWORD:
                server.ehlo()
                if 'starttls' not in server.esmtp_features:
                    raise ImproperlyConfigured('password configured but starttls not supported')
                (retval, retmsg) = server.starttls()
                if retval != 220:
                    raise ImproperlyConfigured('password configured but tls failed: %d %s' % ( retval, retmsg ))
                # Send a new EHLO, since without TLS the server might not
                # advertise the AUTH capability.
                server.ehlo()
                server.login(settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD)
            unhandled = server.sendmail(frm, to, msg.as_string())
            if unhandled != {}:
                raise SMTPSomeRefusedRecipients(message="%d addresses were refused"%len(unhandled),original_msg=msg,refusals=unhandled)
        except Exception as e:
            # need to improve log message
            log("Exception while trying to send email from '%s' to %s subject '%s'" % (frm, to, msg.get('Subject', '[no subject]')))
            if isinstance(e, smtplib.SMTPException):
                e.original_msg=msg
                raise 
            else:
                raise smtplib.SMTPException({'really': sys.exc_info()[0], 'value': sys.exc_info()[1], 'tb': traceback.format_tb(sys.exc_info()[2])})
        finally:
            try:
                server.quit()
            except smtplib.SMTPServerDisconnected:
                pass
        log("sent email from '%s' to %s subject '%s'" % (frm, to, msg.get('Subject', '[no subject]')))
Example #14
0
 def clear_my_address(self, my_addresses, value):
     """return recipient header without the addresses in my_addresses"""
     new_value = []
     for name, address in getaddresses(value):
         if address not in my_addresses:
             if name != '':
                 new_value.append('"%s" <%s>' % (name, address))
             else:
                 new_value.append(address)
     return ', '.join(new_value)
Example #15
0
 def clear_my_address(self, my_addresses, value):
     """return recipient header without the addresses in my_addresses"""
     new_value = []
     for name, address in getaddresses(value):
         if address not in my_addresses:
             if name != '':
                 new_value.append('"%s" <%s>' % (name, address))
             else:
                 new_value.append(address)
     return ', '.join(new_value)
Example #16
0
 def __init__(self, context, message):  # -> none
     """Extract the bits of interest from an RFC2822 message string.
     context should be a wiki page. This perhaps should do the isJunk
     test up front to avoid unnecessary resource usage.
     """
     DEBUG('mailin.py processing incoming message:\n%s' % message)
     self.context = context
     self.original = message
     self.msg = email.message_from_string(self.original)
     self.date = self.msg['Date']
     # flatten a multi-line subject into one line
     s = re.sub('\n', '', self.msg.get('Subject', ''))
     # convert the possibly RFC2047-encoded subject to unicode.
     # Only the first encoded part is used if there is more than one.
     # misencoded subjects are ignored.
     (s, enc) = decode_header(s)[0]
     try:
         self.subject = tounicode(s, enc or 'ascii')
     except UnicodeDecodeError:
         self.subject = ''
     self.realSubject = re.sub(r'.*?\[.*?\] ?(.*)', r'\1', self.subject)
     self.messageid = self.msg.get('Message-id', '')
     self.inreplyto = self.msg.get('In-reply-to', '')
     self.From = self.msg.get('From')
     self.FromRealName = parseaddr(self.From)[0]
     self.FromEmail = parseaddr(self.From)[1]
     self.FromUserName = (self.FromRealName
                          or re.sub(r'@.*$', r'', self.FromEmail))
     self.sender = self.msg.get('Sender')
     self.senderEmail = (self.sender and parseaddr(self.sender)[1]) or None
     tos = self.msg.get_all('to', [])
     ccs = self.msg.get_all('cc', [])
     resent_tos = self.msg.get_all('resent-to', [])
     resent_ccs = self.msg.get_all('resent-cc', [])
     self.recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
     # mailing list support
     # XXX x-beenthere is mailman-specific - need to support ezmlm & others here
     #self.xbeenthere = (self.msg.get('X-BeenThere') or
     #                   re.search(r'[^\s<]+@[^\s>]+',self.msg.get('Delivered-To')).group())
     # ..Type Error - configured ezmlm to provide beenthere instead (?)
     self.xbeenthere = self.msg.get('X-BeenThere')
     # the mailin body will be the message's first text/plain part
     # (or a null string if there is none or it's misencoded)
     try:
         firstplaintextpart = typed_subpart_iterator(
             self.msg, 'text', 'plain').next()
         # as I understand it:
         # first decoding, from the content-transfer-encoding, eg quoted-printabe
         payload = firstplaintextpart.get_payload(decode=1)
         # second decoding, from utf8 or whatever to unicode
         charset = self.msg.get_content_charset('ascii')
         payloadutf8 = payload.decode(charset).encode('utf-8')
     except (StopIteration, UnicodeDecodeError):
         payloadutf8 = ''
     self.body = cleanupBody(payloadutf8)
Example #17
0
File: mail.py Project: mcr/ietfdb
def send_smtp(msg, bcc=None):
    '''
    Send a Message via SMTP, based on the django email server settings.
    The destination list will be taken from the To:/Cc: headers in the
    Message.  The From address will be used if present or will default
    to the django setting DEFAULT_FROM_EMAIL

    If someone has set test_mode=True, then just append the msg to
    the outbox.
    '''
    add_headers(msg)
    (fname, frm) = parseaddr(msg.get('From'))
    addrlist = msg.get_all('To') + msg.get_all('Cc', [])
    if bcc:
        addrlist += [bcc]
    to = [addr for name, addr in getaddresses(addrlist)]
    if not to:
        log("No addressees for email from '%s', subject '%s'.  Nothing sent." % (frm, msg.get('Subject', '[no subject]')))
    else:
        if test_mode:
            outbox.append(msg)
            return
        server = None
        try:
            server = smtplib.SMTP()
            log("SMTP server: %s" % repr(server))
            #if settings.DEBUG:
            #    server.set_debuglevel(1)
            conn_code, conn_msg = server.connect(settings.EMAIL_HOST, settings.EMAIL_PORT)
            log("SMTP connect: code: %s; msg: %s" % (conn_code, conn_msg))
            if settings.EMAIL_HOST_USER and settings.EMAIL_HOST_PASSWORD:
                server.ehlo()
                if 'starttls' not in server.esmtp_features:
                    raise ImproperlyConfigured('password configured but starttls not supported')
                (retval, retmsg) = server.starttls()
                if retval != 220:
                    raise ImproperlyConfigured('password configured but tls failed: %d %s' % ( retval, retmsg ))
                # Send a new EHLO, since without TLS the server might not
                # advertise the AUTH capability.
                server.ehlo()
                server.login(settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD)
            server.sendmail(frm, to, msg.as_string())
            # note: should pay attention to the return code, as it may
            # indicate that someone didn't get the email.
        except:
            if server:
                server.quit()
            # need to improve log message
            log("got exception '%s' (%s) trying to send email from '%s' to %s subject '%s'" % (sys.exc_info()[0], sys.exc_info()[1], frm, to, msg.get('Subject', '[no subject]')))
            if isinstance(sys.exc_info()[0], smtplib.SMTPException):
                raise
            else:
                raise smtplib.SMTPException({'really': sys.exc_info()[0], 'value': sys.exc_info()[1], 'tb': sys.exc_info()[2]})
        server.quit()
        log("sent email from '%s' to %s subject '%s'" % (frm, to, msg.get('Subject', '[no subject]')))
Example #18
0
def source_email(msg, return_realname=False):
    """
    Search the header of an email Message instance to find the
    sender's email address.
    """
    froms = msg.get_all('from', [])
    from_tuples = getaddresses(froms)  # [(realname, email_address), ...]
    assert len(from_tuples) == 1
    if return_realname == True:
        return from_tuples[0]  # (realname, email_address)
    return from_tuples[0][1]  # email_address
Example #19
0
    def validateSingleEmailAddress(self, address):
        # Validate a single email address, see also validateEmailAddresses.
        if not isinstance(address, basestring):
            return False

        sub = EMAIL_CUTOFF_RE.match(address)
        if sub is not None:
            # Address contains two newlines (spammer attack using
            # "address\n\nSpam message")
            return False

        if len(getaddresses([address])) != 1:
            # none or more than one address
            return False

        # Validate the address
        for name, addr in getaddresses([address]):
            if not self.validateSingleNormalizedEmailAddress(addr):
                return False
        return True
Example #20
0
    def __init__(self, message, hash_value, uid):
        self.hash_value = hash_value
        self.uid =  uid 
        message = email.message_from_string(message)

        def _get_header_data(key):
            value = message.get(key, '<unknown>')
            value = decode_mail_headers(decodeUnknown(message.get_charset(), value))
            return value
        
        self.subject = _get_header_data('subject').strip()
        self.sender =  parseaddr(message.get('from'))
        
        received = None
        date_str= message.get('date') 
        if date_str:
            date_tuple= parsedate_tz(date_str)
            if date_tuple:
                received=datetime.datetime.fromtimestamp(mktime_tz(date_tuple))
        self.received = received
    
        self.recipients = getaddresses(message.get_all('to', []) + message.get_all('cc', []) + message.get_all('resent-to', []) + message.get_all('resent-cc', []))
        counter = 0
        files = []
        
        body_plain, body_html = '', ''
        
        for part in message.walk():
            if part.get_content_maintype() == 'multipart':
                continue
            name = part.get_param("name")
            if name:
                name = collapse_rfc2231_value(name)
    
            if part.get_content_maintype() == 'text' and name == None:
                if part.get_content_subtype() == 'plain':
                    body_plain = decodeUnknown(part.get_content_charset(), part.get_payload(decode=True))
                else:
                    body_html = part.get_payload(decode=True)
            else:
                if not name:
                    ext = mimetypes.guess_extension(part.get_content_type())
                    name = "part-%i%s" % (counter, ext)
    
                files.append({
                    'filename': name,
                    'content': part.get_payload(decode=True),
                    'type': part.get_content_type()},
                    )
            counter += 1
            
        self.body_plain = body_plain
        self.body_html = mark_safe(strip_empty_tags(strip_tags(body_html, ['html', 'head', 'body', 'meta'])))
        self.files = files
Example #21
0
    def validateSingleEmailAddress(self, address):
        # Validate a single email address, see also validateEmailAddresses.
        if not isinstance(address, basestring):
            return False

        sub = EMAIL_CUTOFF_RE.match(address)
        if sub is not None:
            # Address contains two newlines (spammer attack using
            # "address\n\nSpam message")
            return False

        if len(getaddresses([address])) != 1:
            # none or more than one address
            return False

        # Validate the address
        for name, addr in getaddresses([address]):
            if not self.validateSingleNormalizedEmailAddress(addr):
                return False
        return True
Example #22
0
 def getCC(self):
     res = []
     buf = getaddresses(self._msg.get_all('cc', ''))
     for i in buf:
         header = decode_header(i[0])
         data = ''.join([
             to_unicode(s, enc) for s, enc in header
             if self.codecs_lookup(enc)
         ])
         res.append((to_entities_quote(data), i[1]))
     return res
def source_email(msg, return_realname=False):
    """
    Search the header of an email Message instance to find the
    sender's email address.
    """
    froms = msg.get_all('from', [])
    from_tuples = getaddresses(froms) # [(realname, email_address), ...]
    assert len(from_tuples) == 1
    if return_realname == True:
        return from_tuples[0] # (realname, email_address)
    return from_tuples[0][1]  # email_address
Example #24
0
def send(msg, smtpurl, port, keyfile, certfile, username, password):
	srcaddr = getaddresses(msg.get_all('From',[]))
	tos = msg.get_all('to',[])
	ccs = msg.get_all('cc',[])
	bccs = msg.get_all('bcc',[])
	resent_tos = msg.get_all('resent-to',[])
	resent_ccs = msg.get_all('resent-cc',[])
	toaddrlist = getaddresses(tos + ccs + bccs + resent_tos + resent_ccs)

	destaddr = []
	for (name, addr) in toaddrlist:
		if not (addr in destaddr):
			destaddr.append(addr)
	del msg['Bcc']

	client = smtplib.SMTP()
	client.set_debuglevel(2)
	client.connect(smtpurl, port)
	client.ehlo()

	if keyfile and certfile:
		client.starttls(keyfile, certfile)
	else:
		client.starttls()

	client.ehlo()

	if username and password:
		client.login(username, password)
		
	client.sendmail(srcaddr[0][1], destaddr, rstrip(msg.as_string()))
	client.quit()

	out = open('/home/denever/mail/logs/sender.log', "a")
	log = "\nId: "+msg['message-id']
	log += "\tTo:"+join(destaddr)
	log += "\tDate: "+msg['date']
	log += "\tSub: "+msg['subject']
	log += "\nUsing: " + smtpurl 
	out.write(log)
	out.close()
Example #25
0
def target_emails(msg):
    """
    Search the header of an email Message instance to find a
    list of recipient's email addresses.
    """
    tos = msg.get_all('to', [])
    ccs = msg.get_all('cc', [])
    bccs = msg.get_all('bcc', [])
    resent_tos = msg.get_all('resent-to', [])
    resent_ccs = msg.get_all('resent-cc', [])
    resent_bccs = msg.get_all('resent-bcc', [])
    all_recipients = getaddresses(tos + ccs + bccs + resent_tos + resent_ccs +
                                  resent_bccs)
    return [addr[1] for addr in all_recipients]
def validateSingleEmailAddress(self, address):
    """ Validate a single email address, see also validateEmailAddresses.
    """
    if not isinstance(address, basestring):
        return False
    sub = EMAIL_CUTOFF_RE.match(address)
    if sub != None:
        # Address contains two newlines (spammer attack using
        # "address\n\nSpam message")
        return False
    email = getaddresses([address])
    # #5353 check if the tuple has any entries for the address, with a bad
    # email adress it returned a list with this format [("", "")]
    # first entry should have been full name and second if email is correct
    if len(email) != 1 or email[0][1] == "":
        # none or more than one address
        return False

    # Validate the address
    for _, addr in getaddresses([address]):
        if not self.validateSingleNormalizedEmailAddress(addr):
            return False
    return True
def target_emails(msg):
    """
    Search the header of an email Message instance to find a
    list of recipient's email addresses.
    """
    tos = msg.get_all('to', [])
    ccs = msg.get_all('cc', [])
    bccs = msg.get_all('bcc', [])
    resent_tos = msg.get_all('resent-to', [])
    resent_ccs = msg.get_all('resent-cc', [])
    resent_bccs = msg.get_all('resent-bcc', [])
    all_recipients = getaddresses(tos + ccs + bccs + resent_tos
                                  + resent_ccs + resent_bccs)
    return [addr[1] for addr in all_recipients]
def validateSingleEmailAddress(self, address):
    """ Validate a single email address, see also validateEmailAddresses.
    """
    if not isinstance(address, basestring):
        return False
    sub = EMAIL_CUTOFF_RE.match(address)
    if sub != None:
        # Address contains two newlines (spammer attack using
        # "address\n\nSpam message")
        return False
    email = getaddresses([address])
    # #5353 check if the tuple has any entries for the address, with a bad
    # email adress it returned a list with this format [("", "")]
    # first entry should have been full name and second if email is correct
    if len(email) != 1 or email[0][1] == "":
        # none or more than one address
        return False

    # Validate the address
    for _, addr in getaddresses([address]):
        if not self.validateSingleNormalizedEmailAddress(addr):
            return False
    return True
Example #29
0
    def __init__(self,
                 context,
                 message,
                 ):
        """
        Extract the bits of interest from an RFC2822 message string.

        This perhaps should do the isJunk test up front to avoid
        unnecessary resource usage.
        """
        BLATHER('mailin.py processing incoming message:\n%s' % message)
        #BLATHER('mailin.py processing incoming message')
        self.context = context
        self.original = message
        self.msg = email.message_from_string(self.original)
        self.date = self.msg['Date']
        self.subject = re.sub(r'\n',r'',self.msg.get('Subject',''))
        self.realSubject = re.sub(r'.*?\[.*?\] ?(.*)',r'\1',self.subject)
        self.messageid = self.msg.get('Message-id','')
        self.inreplyto = self.msg.get('In-reply-to','')
        self.From = self.msg.get('From')
        self.FromRealName = parseaddr(self.From)[0]
        self.FromEmail    = parseaddr(self.From)[1]
        self.FromUserName = (self.FromRealName or
                             re.sub(r'@.*$',r'',self.FromEmail))
        self.sender = self.msg.get('Sender')
        self.senderEmail = (self.sender and
                            parseaddr(self.sender)[1]) or None
        tos = self.msg.get_all('to', [])
        ccs = self.msg.get_all('cc', [])
        resent_tos = self.msg.get_all('resent-to', [])
        resent_ccs = self.msg.get_all('resent-cc', [])
        self.recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)

        # mailing list support
        # XXX x-beenthere is mailman-specific - need to support ezmlm & others here
        #self.xbeenthere = (self.msg.get('X-BeenThere') or
        #                   re.search(r'[^\s<]+@[^\s>]+',self.msg.get('Delivered-To')).group())
        # ..Type Error - configured ezmlm to provide beenthere instead (?)
        self.xbeenthere = self.msg.get('X-BeenThere')

        # raises an exception if there's no text part
        try:
            plaintextpart = typed_subpart_iterator(self.msg,
                                                   'text',
                                                   'plain').next().get_payload(decode=1)
        except StopIteration:
            plaintextpart = ''
        self.body = self.cleanupBody(plaintextpart)
Example #30
0
def forbid_multi_line_headers(name, val, encoding):
	encoding = encoding or DEFAULT_CHARSET
	val = force_unicode(val)
	if '\n' in val or '\r' in val:
		raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
	try:
		val = val.encode('ascii')
	except UnicodeEncodeError:
		if name.lower() in ('to', 'from', 'cc'):
			val = ', '.join(sanitize_address(addr, encoding) for addr in getaddresses((val,)))
		else:
			val = str(Header(val, encoding))
	else:
		if name.lower() == 'subject':
			val = Header(val)
	return name, val
Example #31
0
def forbid_multi_line_headers(name, val, encoding):
    encoding = encoding or DEFAULT_CHARSET
    val = force_unicode(val)
    if "\n" in val or "\r" in val:
        raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    try:
        val = val.encode("ascii")
    except UnicodeEncodeError:
        if name.lower() in ("to", "from", "cc"):
            val = ", ".join(sanitize_address(addr, encoding) for addr in getaddresses((val,)))
        else:
            val = str(Header(val, encoding))
    else:
        if name.lower() == "subject":
            val = Header(val)
    return name, val
Example #32
0
def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or settings.DEFAULT_CHARSET
    val = force_unicode(val)
    if "\n" in val or "\r" in val:
        raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    try:
        val = val.encode("ascii")
    except UnicodeEncodeError:
        if name.lower() in ADDRESS_HEADERS:
            val = ", ".join(sanitize_address(addr, encoding) for addr in getaddresses((val,)))
        else:
            val = str(Header(val, encoding))
    else:
        if name.lower() == "subject":
            val = Header(val)
    return name, val
Example #33
0
    def validateEmailAddresses(self, addresses):
        # Validate a list of possibly several email addresses, see also
        # validateSingleEmailAddress.
        if not isinstance(addresses, basestring):
            return False

        sub = EMAIL_CUTOFF_RE.match(addresses)
        if sub is not None:
            # Addresses contains two newlines (spammer attack using
            # "To: list\n\nSpam message")
            return False

        # Validate each address
        for name, addr in getaddresses([addresses]):
            if not self.validateSingleNormalizedEmailAddress(addr):
                return False
        return True
Example #34
0
    def validateEmailAddresses(self, addresses):
        # Validate a list of possibly several email addresses, see also
        # validateSingleEmailAddress.
        if not isinstance(addresses, basestring):
            return False

        sub = EMAIL_CUTOFF_RE.match(addresses)
        if sub is not None:
            # Addresses contains two newlines (spammer attack using
            # "To: list\n\nSpam message")
            return False

        # Validate each address
        for name, addr in getaddresses([addresses]):
            if not self.validateSingleNormalizedEmailAddress(addr):
                return False
        return True
Example #35
0
def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or "utf8"
    val = unicode(val)
    if '\n' in val or '\r' in val:
        raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    try:
        val = val.encode('ascii')
    except UnicodeEncodeError:
        if name.lower() in ADDRESS_HEADERS:
            val = ', '.join(sanitize_address(addr, encoding)
                for addr in getaddresses((val,)))
        else:
            val = str(Header(val, encoding))
    else:
        if name.lower() == 'subject':
            val = Header(val)
    return name, val
Example #36
0
def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or settings.DEFAULT_CHARSET
    val = force_unicode(val)
    if '\n' in val or '\r' in val:
        raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    try:
        val = val.encode('ascii')
    except UnicodeEncodeError:
        if name.lower() in ADDRESS_HEADERS:
            val = ', '.join(sanitize_address(addr, encoding)
                for addr in getaddresses((val,)))
        else:
            val = str(Header(val, encoding))
    else:
        if name.lower() == 'subject':
            val = Header(val)
    return name, val
Example #37
0
def process(msg):
    if msg.is_multipart():
        return None
    try:
        whofrom = getaddresses([msg.get('from', '')])[0][1]
        if not whofrom:
            return None
        username, domain = whofrom.split('@', 1)
    except (IndexError, ValueError):
        return None
    if username.lower() <> 'mailer-daemon':
        return None
    parts = domain.split('.')
    parts.reverse()
    for part1, part2 in zip(parts, ('edu', 'yale')):
        if part1 <> part2:
            return None
    # Okay, we've established that the bounce came from the mailer-daemon at
    # yale.edu.  Let's look for a name, and then guess the relevant domains.
    names = {}
    body = StringIO(msg.get_payload())
    state = 0
    # simple state machine
    #     0 == init
    #     1 == intro found
    while 1:
        line = body.readline()
        if not line:
            break
        if state == 0 and scre.search(line):
            state = 1
        elif state == 1 and ecre.search(line):
            break
        elif state == 1:
            mo = acre.search(line)
            if mo:
                names[mo.group('addr')] = 1
    # Now we have a bunch of names, these are either @yale.edu or
    # @cs.yale.edu.  Add them both.
    addrs = []
    for name in names.keys():
        addrs.append(name + '@yale.edu')
        addrs.append(name + '@cs.yale.edu')
    return addrs
Example #38
0
def process(msg):
    if msg.is_multipart():
        return None
    try:
        whofrom = getaddresses([msg.get('from', '')])[0][1]
        if not whofrom:
            return None
        username, domain = whofrom.split('@', 1)
    except (IndexError, ValueError):
        return None
    if username.lower() <> 'mailer-daemon':
        return None
    parts = domain.split('.')
    parts.reverse()
    for part1, part2 in zip(parts, ('edu', 'yale')):
        if part1 <> part2:
            return None
    # Okay, we've established that the bounce came from the mailer-daemon at
    # yale.edu.  Let's look for a name, and then guess the relevant domains.
    names = {}
    body = StringIO(msg.get_payload())
    state = 0
    # simple state machine
    #     0 == init
    #     1 == intro found
    while 1:
        line = body.readline()
        if not line:
            break
        if state == 0 and scre.search(line):
            state = 1
        elif state == 1 and ecre.search(line):
            break
        elif state == 1:
            mo = acre.search(line)
            if mo:
                names[mo.group('addr')] = 1
    # Now we have a bunch of names, these are either @yale.edu or
    # @cs.yale.edu.  Add them both.
    addrs = []
    for name in names.keys():
        addrs.append(name + '@yale.edu')
        addrs.append(name + '@cs.yale.edu')
    return addrs
Example #39
0
def get_addresses_from_header(email_header):
    r"""Get the e-mail addresses specificed in an e-mail header.

        >>> get_addresses_from_header('*****@*****.**')
        ['*****@*****.**']
        >>> get_addresses_from_header('[email protected], [email protected]')
        ['*****@*****.**', '*****@*****.**']
        >>> get_addresses_from_header('One\n <*****@*****.**>')
        ['One <*****@*****.**>']
        >>> get_addresses_from_header('One\r\n <*****@*****.**>')
        ['One <*****@*****.**>']
        >>> get_addresses_from_header(
        ...     '"One, A" <*****@*****.**>,\n'
        ...     ' "Two, B" <*****@*****.**>')
        ['"One, A" <*****@*****.**>', '"Two, B" <*****@*****.**>']

    """
    return [
        formataddr((name, address))
        for name, address in getaddresses([email_header])]
Example #40
0
def send_message(request, message, recipient_filter=lambda x: True):
    sender = message["from"]
    recipients = set()
    for field in ["to", "cc", "bcc"]:
        values = message.get_all(field, [])
        for _, address in getaddresses(values):
            if recipient_filter(address):
                recipients.add(address)

    smtp = smtplib.SMTP()
    try:
        try:
            smtp.connect(request.cfg.mail_smarthost)
            smtp.ehlo()

            try:
                smtp.starttls()
            except smtplib.SMTPException:
                pass
            else:
                smtp.ehlo()

            try:
                smtp.sendmail(sender, recipients, message.as_string())
                for recipient in recipients:
                    logging.info(
                        "%s invited %s to wiki %s" %
                        (sender, recipients, request.cfg.interwikiname))
            except (smtplib.SMTPSenderRefused,
                    smtplib.SMTPRecipientsRefused), error:
                if not getattr(request.cfg, "mail_login", None):
                    raise error
                smtp.login(*request.cfg.mail_login.split(" ", 1))
                smtp.sendmail(sender, recipients, message.as_string())
        except Exception, exc:
            raise InviteException("Could not send the mail: %r" % exc)
    finally:
        try:
            smtp.quit()
        except Exception:
            pass
Example #41
0
def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or settings.DEFAULT_CHARSET
    val = force_unicode(val)
    if "\n" in val or "\r" in val:
        raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
    try:
        val = val.encode("ascii")
    except UnicodeEncodeError:
        if name.lower() in ("to", "from", "cc"):
            result = []
            for nm, addr in getaddresses((val,)):
                nm = str(Header(nm.encode(encoding), encoding))
                result.append(formataddr((nm, str(addr))))
            val = ", ".join(result)
        else:
            val = Header(val.encode(encoding), encoding)
    else:
        if name.lower() == "subject":
            val = Header(val)
    return name, val
Example #42
0
def get_addresses_from_header(email_header):
    r"""Get the e-mail addresses specificed in an e-mail header.

        >>> get_addresses_from_header('*****@*****.**')
        ['*****@*****.**']
        >>> get_addresses_from_header('[email protected], [email protected]')
        ['*****@*****.**', '*****@*****.**']
        >>> get_addresses_from_header('One\n <*****@*****.**>')
        ['One <*****@*****.**>']
        >>> get_addresses_from_header('One\r\n <*****@*****.**>')
        ['One <*****@*****.**>']
        >>> get_addresses_from_header(
        ...     '"One, A" <*****@*****.**>,\n'
        ...     ' "Two, B" <*****@*****.**>')
        ['"One, A" <*****@*****.**>', '"Two, B" <*****@*****.**>']

    """
    return [
        formataddr((name, address))
        for name, address in getaddresses([email_header])
    ]
Example #43
0
def condition_message(to, frm, subject, msg, cc, extra):
    if isinstance(frm, tuple):
	frm = formataddr(frm)
    if isinstance(to, list) or isinstance(to, tuple):
        to = ", ".join([isinstance(addr, tuple) and formataddr(addr) or addr for addr in to if addr])
    if isinstance(cc, list) or isinstance(cc, tuple):
        cc = ", ".join([isinstance(addr, tuple) and formataddr(addr) or addr for addr in cc if addr])
    if frm:
	msg['From'] = frm

    # The following is a hack to avoid an issue with how the email module (as of version 4.0.3)
    # breaks lines when encoding header fields with anything other than the us-ascii codec.
    # This allows the Header implementation to encode each display name as a separate chunk. 
    # The resulting encode produces a string that is us-ascii and has a good density of 
    # "higher-level syntactic breaks"
    to_hdr = Header(header_name='To')
    for name, addr in getaddresses([to]):
        if addr != '' and not addr.startswith('unknown-email-'):
            if name:
                to_hdr.append('"%s"' % name)
            to_hdr.append("<%s>," % addr)
    to_str = to_hdr.encode()
    if to_str and to_str[-1] == ',':
        to_str=to_str[:-1]
    # It's important to use this string, and not assign the Header object.
    # Code downstream from this assumes that the msg['To'] will return a string, not an instance
    msg['To'] = to_str

    if cc:
	msg['Cc'] = cc
    msg['Subject'] = subject
    msg['X-Test-IDTracker'] = (settings.SERVER_MODE == 'production') and 'no' or 'yes'
    msg['X-IETF-IDTracker'] = ietf.__version__
    msg['Auto-Submitted'] = "auto-generated"
    msg['Precedence'] = "bulk"
    if extra:
	for k, v in extra.items():
            if v:
                msg[k] = v
Example #44
0
def send_message(request, message, recipient_filter=lambda x: True):
    sender = message["from"]
    recipients = set()
    for field in ["to", "cc", "bcc"]:
        values = message.get_all(field, [])
        for _, address in getaddresses(values):
            if recipient_filter(address):
                recipients.add(address)

    smtp = smtplib.SMTP()
    try:
        try:
            smtp.connect(request.cfg.mail_smarthost)
            smtp.ehlo()

            try:
                smtp.starttls()
            except smtplib.SMTPException:
                pass
            else:
                smtp.ehlo()

            try:
                smtp.sendmail(sender, recipients, message.as_string())
                for recipient in recipients:
                    logging.info("%s invited %s to wiki %s" % (sender, recipients, request.cfg.interwikiname))
            except (smtplib.SMTPSenderRefused, smtplib.SMTPRecipientsRefused), error:
                if not getattr(request.cfg, "mail_login", None):
                    raise error
                smtp.login(*request.cfg.mail_login.split(" ", 1))
                smtp.sendmail(sender, recipients, message.as_string())
        except Exception, exc:
            raise InviteException("Could not send the mail: %r" % exc)
    finally:
        try:
            smtp.quit()
        except Exception:
            pass
Example #45
0
 def check_locals(self, map_names):
     from email.Utils import getaddresses
     pairs = getaddresses(self.message.get_all('From', [])
                          + self.message.get_all('To', [])
                          + self.message.get_all('Cc', []))
     for pair in pairs:
         if pair is not None and pair[1]:
             address = pair[1].lower()
             fields = address.split('@')
             if len(fields) != 2:
                 self.checker.reject("Invalid address `%s'." % address)
                 continue
             user, domain = fields
             if domain in self.run.heres:
                 for item in user, address:
                     for map_name in map_names:
                         value = self.checker.get_value(
                             map_name, item,
                             "Rejected domain `%s'." % domain)
                         if value is not None:
                             break
                     if value is not None:
                         break
                 else:
                     self.checker.reject("Address `%s' locally unknown."
                                         % pair[1])
     for pair in pairs:
         if pair is not None and pair[1]:
             user_domain = pair[1].split('@')
             if len(user_domain) == 2:
                 user, domain = user_domain
                 if self.progressive_lookup(user, domain, map_names):
                     break
     else:
         if not self.run.silence_locals:
             self.checker.reject("Neither origin nor destination is local.")
Example #46
0
def split_mail(msg, fail_on_empty=False):
    """Deconstruct an email.Message object into a sequence of words;
    returns an iterator for this sequence.

    If fail_on_empty is true, then ValueError is raised if no
    parseable message parts could be found.  This function can only
    handle text parts, and does not attempt to extract meaningful data
    from other content types.
    """
    coll = set()
    results = [coll]

    for tag, hdr in (('from', 'from'), ('from', 'sender'), ('rcpt', 'to'),
                     ('rcpt', 'cc'), ('rcpt', 'bcc')):
        for name, addr in getaddresses(msg.get_all(hdr, ())):
            addr = addr.split('@', 1)
            coll.add('%s:@%s' % (tag, addr[-1].lower()))
            if len(addr) > 1:
                coll.add('%s:%s' % (tag, addr[0].lower()))

    proc = compose(unfold_entities, word_split, lowercase, remove_stopwords,
                   dropwhile(lambda s: s in ('re', 'fwd')),
                   add_prefix('subj', ':'))
    results.append(proc(msg.get('subject', '')))

    found = 0
    for part in msg.walk():
        if part.get_content_maintype() != 'text':
            continue  # skip non-text parts

        enc = part.get('content-transfer-encoding')
        if enc is None:
            text = part.get_payload()
        elif enc.lower() == 'quoted-printable':
            text = quopri.decodestring(part.get_payload())
        elif enc.lower() == 'base64':
            text = base64.decodestring(part.get_payload())
        elif enc.lower() in (None, '7bit', '8bit', 'binary'):
            text = part.get_payload()
        else:
            continue  # unknown encoding method

        # Since the content may be encoded with some weird character
        # set, we'll try to get it back to Unicode so the XML parser
        # doesn't choke too hard.

        for cs in part.get_charsets():
            if not cs:
                continue

            try:
                text = text.decode(cs)
                break
            except UnicodeDecodeError:
                continue
        else:
            # If we can't decode it some other way, try ISO 8859-1,
            # which subsubmes US ASCII anyway.
            text = text.decode('latin1')

        proc = compose(unfold_entities, word_split, lowercase,
                       remove_stopwords, crush_urls, limit_length(100))
        sub = part.get_content_subtype()
        if sub == 'html':
            proc = compose(strip_html, proc)
        elif sub not in ('plain', 'enriched'):
            continue  # unknown text type

        results.append(proc(text))
        found += 1

    if found == 0 and fail_on_empty:
        raise ValueError("No parseable content found")

    return itertools.chain(*results)
Example #47
0
def process(mlist, msg, msgdata):
    # Set the "X-Ack: no" header if noack flag is set.
    if msgdata.get('noack'):
        del msg['x-ack']
        msg['X-Ack'] = 'no'
    # Because we're going to modify various important headers in the email
    # message, we want to save some of the information in the msgdata
    # dictionary for later.  Specifically, the sender header will get waxed,
    # but we need it for the Acknowledge module later.
    msgdata['original_sender'] = msg.get_sender()
    # VirginRunner sets _fasttrack for internally crafted messages.
    fasttrack = msgdata.get('_fasttrack')
    if not msgdata.get('isdigest') and not fasttrack:
        try:
            prefix_subject(mlist, msg, msgdata)
        except (UnicodeError, ValueError):
            # TK: Sometimes subject header is not MIME encoded for 8bit
            # simply abort prefixing.
            pass
    # Mark message so we know we've been here, but leave any existing
    # X-BeenThere's intact.
    msg['X-BeenThere'] = mlist.GetListEmail()
    # Add Precedence: and other useful headers.  None of these are standard
    # and finding information on some of them are fairly difficult.  Some are
    # just common practice, and we'll add more here as they become necessary.
    # Good places to look are:
    #
    # http://www.dsv.su.se/~jpalme/ietf/jp-ietf-home.html
    # http://www.faqs.org/rfcs/rfc2076.html
    #
    # None of these headers are added if they already exist.  BAW: some
    # consider the advertising of this a security breach.  I.e. if there are
    # known exploits in a particular version of Mailman and we know a site is
    # using such an old version, they may be vulnerable.  It's too easy to
    # edit the code to add a configuration variable to handle this.
    if not msg.has_key('x-mailman-version'):
        msg['X-Mailman-Version'] = mm_cfg.VERSION
    # We set "Precedence: list" because this is the recommendation from the
    # sendmail docs, the most authoritative source of this header's semantics.
    if not msg.has_key('precedence'):
        msg['Precedence'] = 'list'
    # Reply-To: munging.  Do not do this if the message is "fast tracked",
    # meaning it is internally crafted and delivered to a specific user.  BAW:
    # Yuck, I really hate this feature but I've caved under the sheer pressure
    # of the (very vocal) folks want it.  OTOH, RFC 2822 allows Reply-To: to
    # be a list of addresses, so instead of replacing the original, simply
    # augment it.  RFC 2822 allows max one Reply-To: header so collapse them
    # if we're adding a value, otherwise don't touch it.  (Should we collapse
    # in all cases?)
    if not fasttrack:
        # A convenience function, requires nested scopes.  pair is (name, addr)
        new = []
        d = {}

        def add(pair):
            lcaddr = pair[1].lower()
            if d.has_key(lcaddr):
                return
            d[lcaddr] = pair
            new.append(pair)

        # List admin wants an explicit Reply-To: added
        if mlist.reply_goes_to_list == 2:
            add(parseaddr(mlist.reply_to_address))
        # If we're not first stripping existing Reply-To: then we need to add
        # the original Reply-To:'s to the list we're building up.  In both
        # cases we'll zap the existing field because RFC 2822 says max one is
        # allowed.
        if not mlist.first_strip_reply_to:
            orig = msg.get_all('reply-to', [])
            for pair in getaddresses(orig):
                add(pair)
        # Set Reply-To: header to point back to this list.  Add this last
        # because some folks think that some MUAs make it easier to delete
        # addresses from the right than from the left.
        if mlist.reply_goes_to_list == 1:
            i18ndesc = uheader(mlist, mlist.description, 'Reply-To')
            add((str(i18ndesc), mlist.GetListEmail()))
        del msg['reply-to']
        # Don't put Reply-To: back if there's nothing to add!
        if new:
            # Preserve order
            msg['Reply-To'] = COMMASPACE.join(
                [formataddr(pair) for pair in new])
        # The To field normally contains the list posting address.  However
        # when messages are fully personalized, that header will get
        # overwritten with the address of the recipient.  We need to get the
        # posting address in one of the recipient headers or they won't be
        # able to reply back to the list.  It's possible the posting address
        # was munged into the Reply-To header, but if not, we'll add it to a
        # Cc header.  BAW: should we force it into a Reply-To header in the
        # above code?
        # Also skip Cc if this is an anonymous list as list posting address
        # is already in From and Reply-To in this case.
        if mlist.personalize == 2 and mlist.reply_goes_to_list <> 1 \
           and not mlist.anonymous_list:
            # Watch out for existing Cc headers, merge, and remove dups.  Note
            # that RFC 2822 says only zero or one Cc header is allowed.
            new = []
            d = {}
            for pair in getaddresses(msg.get_all('cc', [])):
                add(pair)
            i18ndesc = uheader(mlist, mlist.description, 'Cc')
            add((str(i18ndesc), mlist.GetListEmail()))
            del msg['Cc']
            msg['Cc'] = COMMASPACE.join([formataddr(pair) for pair in new])
    # Add list-specific headers as defined in RFC 2369 and RFC 2919, but only
    # if the message is being crafted for a specific list (e.g. not for the
    # password reminders).
    #
    # BAW: Some people really hate the List-* headers.  It seems that the free
    # version of Eudora (possibly on for some platforms) does not hide these
    # headers by default, pissing off their users.  Too bad.  Fix the MUAs.
    if msgdata.get('_nolist') or not mlist.include_rfc2369_headers:
        return
    # This will act like an email address for purposes of formataddr()
    listid = '%s.%s' % (mlist.internal_name(), mlist.host_name)
    cset = Utils.GetCharSet(mlist.preferred_language)
    if mlist.description:
        # Don't wrap the header since here we just want to get it properly RFC
        # 2047 encoded.
        i18ndesc = uheader(mlist, mlist.description, 'List-Id', maxlinelen=998)
        listid_h = formataddr((str(i18ndesc), listid))
    else:
        # without desc we need to ensure the MUST brackets
        listid_h = '<%s>' % listid
    # We always add a List-ID: header.
    del msg['list-id']
    msg['List-Id'] = listid_h
    # For internally crafted messages, we also add a (nonstandard),
    # "X-List-Administrivia: yes" header.  For all others (i.e. those coming
    # from list posts), we add a bunch of other RFC 2369 headers.
    requestaddr = mlist.GetRequestEmail()
    subfieldfmt = '<%s>, <mailto:%s?subject=%ssubscribe>'
    listinfo = mlist.GetScriptURL('listinfo', absolute=1)
    useropts = mlist.GetScriptURL('options', absolute=1)
    headers = {}
    if msgdata.get('reduced_list_headers'):
        headers['X-List-Administrivia'] = 'yes'
    else:
        headers.update({
            'List-Help':
            '<mailto:%s?subject=help>' % requestaddr,
            'List-Unsubscribe':
            subfieldfmt % (useropts, requestaddr, 'un'),
            'List-Subscribe':
            subfieldfmt % (listinfo, requestaddr, ''),
        })
        # List-Post: is controlled by a separate attribute
        if mlist.include_list_post_header:
            headers['List-Post'] = '<mailto:%s>' % mlist.GetListEmail()
        # Add this header if we're archiving
        if mlist.archive:
            archiveurl = mlist.GetBaseArchiveURL()
            headers['List-Archive'] = '<%s>' % archiveurl
    # First we delete any pre-existing headers because the RFC permits only
    # one copy of each, and we want to be sure it's ours.
    for h, v in headers.items():
        del msg[h]
        # Wrap these lines if they are too long.  78 character width probably
        # shouldn't be hardcoded, but is at least text-MUA friendly.  The
        # adding of 2 is for the colon-space separator.
        if len(h) + 2 + len(v) > 78:
            v = CONTINUATION.join(v.split(', '))
        msg[h] = v
Example #48
0
def process(msg):
    all = msg.get_all('x-failed-recipients', [])
    return [a for n, a in getaddresses(all)]
Example #49
0
    def apply(self, ui):
        # get message to forward if not given in constructor
        if not self.message:
            self.message = ui.current_buffer.get_selected_message()
        mail = self.message.get_email()

        # set body text
        name, address = self.message.get_author()
        timestamp = self.message.get_date()
        qf = settings.get_hook('reply_prefix')
        if qf:
            quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman)
        else:
            quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp)
        mailcontent = quotestring
        quotehook = settings.get_hook('text_quote')
        if quotehook:
            mailcontent += quotehook(self.message.accumulate_body())
        else:
            quote_prefix = settings.get('quote_prefix')
            for line in self.message.accumulate_body().splitlines():
                mailcontent += quote_prefix + line + '\n'

        envelope = Envelope(bodytext=mailcontent)

        # copy subject
        subject = decode_header(mail.get('Subject', ''))
        reply_subject_hook = settings.get_hook('reply_subject')
        if reply_subject_hook:
            subject = reply_subject_hook(subject)
        else:
            rsp = settings.get('reply_subject_prefix')
            if not subject.lower().startswith(('re:', rsp.lower())):
                subject = rsp + subject
        envelope.add('Subject', subject)

        # set From-header and sending account
        try:
            from_header, account = determine_sender(mail, 'reply')
        except AssertionError as e:
            ui.notify(e.message, priority='error')
            return
        envelope.add('From', from_header)

        # set To
        sender = mail['Reply-To'] or mail['From']
        my_addresses = settings.get_addresses()
        sender_address = parseaddr(sender)[1]
        cc = ''

        # check if reply is to self sent message
        if sender_address in my_addresses:
            recipients = [mail['To']]
            emsg = 'Replying to own message, set recipients to: %s' \
                % recipients
            logging.debug(emsg)
        else:
            recipients = [sender]

        if self.groupreply:
            # make sure that our own address is not included
            # if the message was self-sent, then our address is not included
            MFT = mail.get_all('Mail-Followup-To', [])
            followupto = self.clear_my_address(my_addresses, MFT)
            if followupto and settings.get('honor_followup_to'):
                logging.debug('honor followup to: %s', followupto)
                recipients = [followupto]
                # since Mail-Followup-To was set, ignore the Cc header
            else:
                if sender != mail['From']:
                    recipients.append(mail['From'])

                # append To addresses if not replying to self sent message
                if sender_address not in my_addresses:
                    cleared = self.clear_my_address(my_addresses,
                                                    mail.get_all('To', []))
                    recipients.append(cleared)

                # copy cc for group-replies
                if 'Cc' in mail:
                    cc = self.clear_my_address(my_addresses,
                                               mail.get_all('Cc', []))
                    envelope.add('Cc', decode_header(cc))

        to = ', '.join(recipients)
        logging.debug('reply to: %s' % to)
        envelope.add('To', decode_header(to))

        # if any of the recipients is a mailinglist that we are subscribed to,
        # set Mail-Followup-To header so that duplicates are avoided
        if settings.get('followup_to'):
            # to and cc are already cleared of our own address
            allrecipients = [to] + [cc]
            lists = settings.get('mailinglists')
            # check if any recipient address matches a known mailing list
            if any([addr in lists for n, addr in getaddresses(allrecipients)]):
                followupto = ', '.join(allrecipients)
                logging.debug('mail followup to: %s' % followupto)
                envelope.add('Mail-Followup-To', decode_header(followupto))

        # set In-Reply-To header
        envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id())

        # set References header
        old_references = mail.get('References', '')
        if old_references:
            old_references = old_references.split()
            references = old_references[-8:]
            if len(old_references) > 8:
                references = old_references[:1] + references
            references.append('<%s>' % self.message.get_message_id())
            envelope.add('References', ' '.join(references))
        else:
            envelope.add('References', '<%s>' % self.message.get_message_id())

        # continue to compose
        ui.apply_command(
            ComposeCommand(envelope=envelope, spawn=self.force_spawn))
Example #50
0
def process(mlist, msg, msgdata):
    # Set the "X-Ack: no" header if noack flag is set.
    if msgdata.get('noack'):
        change_header('X-Ack', 'no', mlist, msg, msgdata)
    # Because we're going to modify various important headers in the email
    # message, we want to save some of the information in the msgdata
    # dictionary for later.  Specifically, the sender header will get waxed,
    # but we need it for the Acknowledge module later.
    # We may have already saved it; if so, don't clobber it here.
    if 'original_sender' not in msgdata:
        msgdata['original_sender'] = msg.get_sender()
    # VirginRunner sets _fasttrack for internally crafted messages.
    fasttrack = msgdata.get('_fasttrack')
    if not msgdata.get('isdigest') and not fasttrack:
        try:
            prefix_subject(mlist, msg, msgdata)
        except (UnicodeError, ValueError):
            # TK: Sometimes subject header is not MIME encoded for 8bit
            # simply abort prefixing.
            pass
    # Mark message so we know we've been here, but leave any existing
    # X-BeenThere's intact.
    change_header('X-BeenThere',
                  mlist.GetListEmail(),
                  mlist,
                  msg,
                  msgdata,
                  delete=False)
    # Add Precedence: and other useful headers.  None of these are standard
    # and finding information on some of them are fairly difficult.  Some are
    # just common practice, and we'll add more here as they become necessary.
    # Good places to look are:
    #
    # http://www.dsv.su.se/~jpalme/ietf/jp-ietf-home.html
    # http://www.faqs.org/rfcs/rfc2076.html
    #
    # None of these headers are added if they already exist.  BAW: some
    # consider the advertising of this a security breach.  I.e. if there are
    # known exploits in a particular version of Mailman and we know a site is
    # using such an old version, they may be vulnerable.  It's too easy to
    # edit the code to add a configuration variable to handle this.
    change_header('X-Mailman-Version',
                  mm_cfg.VERSION,
                  mlist,
                  msg,
                  msgdata,
                  repl=False)
    # We set "Precedence: list" because this is the recommendation from the
    # sendmail docs, the most authoritative source of this header's semantics.
    change_header('Precedence', 'list', mlist, msg, msgdata, repl=False)
    # Do we change the from so the list takes ownership of the email
    if (msgdata.get('from_is_list') or mlist.from_is_list) and not fasttrack:
        # Be as robust as possible here.
        faddrs = getaddresses(msg.get_all('from', []))
        # Strip the nulls and bad emails.
        faddrs = [x for x in faddrs if x[1].find('@') > 0]
        if len(faddrs) == 1:
            realname, email = o_from = faddrs[0]
        else:
            # No From: or multiple addresses.  Just punt and take
            # the get_sender result.
            realname = ''
            email = msgdata['original_sender']
            o_from = (realname, email)
        if not realname:
            if mlist.isMember(email):
                realname = mlist.getMemberName(email) or email
            else:
                realname = email
        # Remove domain from realname if it looks like an email address
        realname = re.sub(r'@([^ .]+\.)+[^ .]+$', '---', realname)
        # Make a display name and RFC 2047 encode it if necessary.  This is
        # difficult and kludgy. If the realname came from From: it should be
        # ascii or RFC 2047 encoded. If it came from the list, it should be
        # in the charset of the list's preferred language or possibly unicode.
        # if it's from the email address, it should be ascii. In any case,
        # make it a unicode.
        if isinstance(realname, unicode):
            urn = realname
        else:
            rn, cs = ch_oneline(realname)
            urn = unicode(rn, cs, errors='replace')
        # likewise, the list's real_name which should be ascii, but use the
        # charset of the list's preferred_language which should be a superset.
        lcs = Utils.GetCharSet(mlist.preferred_language)
        ulrn = unicode(mlist.real_name, lcs, errors='replace')
        # get translated 'via' with dummy replacements
        realname = '%(realname)s'
        lrn = '%(lrn)s'
        # We want the i18n context to be the list's preferred_language.  It
        # could be the poster's.
        otrans = i18n.get_translation()
        i18n.set_language(mlist.preferred_language)
        via = _('%(realname)s via %(lrn)s')
        i18n.set_translation(otrans)
        uvia = unicode(via, lcs, errors='replace')
        # Replace the dummy replacements.
        uvia = re.sub(u'%\(lrn\)s', ulrn, re.sub(u'%\(realname\)s', urn, uvia))
        # And get an RFC 2047 encoded header string.
        dn = str(Header(uvia, lcs))
        change_header('From', formataddr((dn, mlist.GetListEmail())), mlist,
                      msg, msgdata)
    else:
        # Use this as a flag
        o_from = None
    # Reply-To: munging.  Do not do this if the message is "fast tracked",
    # meaning it is internally crafted and delivered to a specific user.  BAW:
    # Yuck, I really hate this feature but I've caved under the sheer pressure
    # of the (very vocal) folks want it.  OTOH, RFC 2822 allows Reply-To: to
    # be a list of addresses, so instead of replacing the original, simply
    # augment it.  RFC 2822 allows max one Reply-To: header so collapse them
    # if we're adding a value, otherwise don't touch it.  (Should we collapse
    # in all cases?)
    # MAS: We need to do some things with the original From: if we've munged
    # it for DMARC mitigation.  We have goals for this process which are
    # not completely compatible, so we do the best we can.  Our goals are:
    # 1) as long as the list is not anonymous, the original From: address
    #    should be obviously exposed, i.e. not just in a header that MUAs
    #    don't display.
    # 2) the original From: address should not be in a comment or display
    #    name in the new From: because it is claimed that multiple domains
    #    in any fields in From: are indicative of spamminess.  This means
    #    it should be in Reply-To: or Cc:.
    # 3) the behavior of an MUA doing a 'reply' or 'reply all' should be
    #    consistent regardless of whether or not the From: is munged.
    # Goal 3) implies sometimes the original From: should be in Reply-To:
    # and sometimes in Cc:, and even so, this goal won't be achieved in
    # all cases with all MUAs.  In cases of conflict, the above ordering of
    # goals is priority order.

    if not fasttrack:
        # A convenience function, requires nested scopes.  pair is (name, addr)
        new = []
        d = {}

        def add(pair):
            lcaddr = pair[1].lower()
            if d.has_key(lcaddr):
                return
            d[lcaddr] = pair
            new.append(pair)

        # List admin wants an explicit Reply-To: added
        if mlist.reply_goes_to_list == 2:
            add(parseaddr(mlist.reply_to_address))
        # If we're not first stripping existing Reply-To: then we need to add
        # the original Reply-To:'s to the list we're building up.  In both
        # cases we'll zap the existing field because RFC 2822 says max one is
        # allowed.
        o_rt = False
        if not mlist.first_strip_reply_to:
            orig = msg.get_all('reply-to', [])
            for pair in getaddresses(orig):
                # There's an original Reply-To: and we're not removing it.
                add(pair)
                o_rt = True
        # We also need to put the old From: in Reply-To: in all cases where
        # it is not going in Cc:.  This is when reply_goes_to_list == 0 and
        # either there was no original Reply-To: or we stripped it.
        # However, if there was an original Reply-To:, unstripped, and it
        # contained the original From: address we need to flag that it's
        # there so we don't add the original From: to Cc:
        if o_from and mlist.reply_goes_to_list == 0:
            if o_rt:
                if d.has_key(o_from[1].lower()):
                    # Original From: address is in original Reply-To:.
                    # Pretend we added it.
                    o_from = None
            else:
                add(o_from)
                # Flag that we added it.
                o_from = None
        # Set Reply-To: header to point back to this list.  Add this last
        # because some folks think that some MUAs make it easier to delete
        # addresses from the right than from the left.
        if mlist.reply_goes_to_list == 1:
            i18ndesc = uheader(mlist, mlist.description, 'Reply-To')
            add((str(i18ndesc), mlist.GetListEmail()))
        # Don't put Reply-To: back if there's nothing to add!
        if new:
            # Preserve order
            change_header('Reply-To',
                          COMMASPACE.join([formataddr(pair) for pair in new]),
                          mlist, msg, msgdata)
        else:
            del msg['reply-to']
        # The To field normally contains the list posting address.  However
        # when messages are fully personalized, that header will get
        # overwritten with the address of the recipient.  We need to get the
        # posting address in one of the recipient headers or they won't be
        # able to reply back to the list.  It's possible the posting address
        # was munged into the Reply-To header, but if not, we'll add it to a
        # Cc header.  BAW: should we force it into a Reply-To header in the
        # above code?
        # Also skip Cc if this is an anonymous list as list posting address
        # is already in From and Reply-To in this case.
        # We do add the Cc in cases where From: header munging is being done
        # because even though the list address is in From:, the Reply-To:
        # poster will override it. Brain dead MUAs may then address the list
        # twice on a 'reply all', but reasonable MUAs should do the right
        # thing.  We also add the original From: to Cc: if it wasn't added
        # to Reply-To:
        add_list = (mlist.personalize == 2 and mlist.reply_goes_to_list <> 1
                    and not mlist.anonymous_list)
        if add_list or o_from:
            # Watch out for existing Cc headers, merge, and remove dups.  Note
            # that RFC 2822 says only zero or one Cc header is allowed.
            new = []
            d = {}
            # If we're adding the original From:, add it first.
            if o_from:
                add(o_from)
            # AvoidDuplicates may have set a new Cc: in msgdata.add_header,
            # so check that.
            if (msgdata.has_key('add_header')
                    and msgdata['add_header'].has_key('Cc')):
                for pair in getaddresses([msgdata['add_header']['Cc']]):
                    add(pair)
            else:
                for pair in getaddresses(msg.get_all('cc', [])):
                    add(pair)
            if add_list:
                i18ndesc = uheader(mlist, mlist.description, 'Cc')
                add((str(i18ndesc), mlist.GetListEmail()))
            change_header('Cc',
                          COMMASPACE.join([formataddr(pair) for pair in new]),
                          mlist, msg, msgdata)
    # Add list-specific headers as defined in RFC 2369 and RFC 2919, but only
    # if the message is being crafted for a specific list (e.g. not for the
    # password reminders).
    #
    # BAW: Some people really hate the List-* headers.  It seems that the free
    # version of Eudora (possibly on for some platforms) does not hide these
    # headers by default, pissing off their users.  Too bad.  Fix the MUAs.
    if msgdata.get('_nolist') or not mlist.include_rfc2369_headers:
        return
    # This will act like an email address for purposes of formataddr()
    listid = '%s.%s' % (mlist.internal_name(), mlist.host_name)
    cset = Utils.GetCharSet(mlist.preferred_language)
    if mlist.description:
        # Don't wrap the header since here we just want to get it properly RFC
        # 2047 encoded.
        i18ndesc = uheader(mlist, mlist.description, 'List-Id', maxlinelen=998)
        listid_h = formataddr((str(i18ndesc), listid))
    else:
        # without desc we need to ensure the MUST brackets
        listid_h = '<%s>' % listid
    # We always add a List-ID: header.
    change_header('List-Id', listid_h, mlist, msg, msgdata)
    # For internally crafted messages, we also add a (nonstandard),
    # "X-List-Administrivia: yes" header.  For all others (i.e. those coming
    # from list posts), we add a bunch of other RFC 2369 headers.
    requestaddr = mlist.GetRequestEmail()
    subfieldfmt = '<%s>, <mailto:%s?subject=%ssubscribe>'
    listinfo = mlist.GetScriptURL('listinfo', absolute=1)
    useropts = mlist.GetScriptURL('options', absolute=1)
    headers = {}
    if msgdata.get('reduced_list_headers'):
        headers['X-List-Administrivia'] = 'yes'
    else:
        headers.update({
            'List-Help':
            '<mailto:%s?subject=help>' % requestaddr,
            'List-Unsubscribe':
            subfieldfmt % (useropts, requestaddr, 'un'),
            'List-Subscribe':
            subfieldfmt % (listinfo, requestaddr, ''),
        })
        # List-Post: is controlled by a separate attribute
        if mlist.include_list_post_header:
            headers['List-Post'] = '<mailto:%s>' % mlist.GetListEmail()
        # Add this header if we're archiving
        if mlist.archive:
            archiveurl = mlist.GetBaseArchiveURL()
            headers['List-Archive'] = '<%s>' % archiveurl
    # First we delete any pre-existing headers because the RFC permits only
    # one copy of each, and we want to be sure it's ours.
    for h, v in headers.items():
        # Wrap these lines if they are too long.  78 character width probably
        # shouldn't be hardcoded, but is at least text-MUA friendly.  The
        # adding of 2 is for the colon-space separator.
        if len(h) + 2 + len(v) > 78:
            v = CONTINUATION.join(v.split(', '))
        change_header(h, v, mlist, msg, msgdata)
Example #51
0
def get_addrs(message, header):
    """ get a list of tuples (realname, mailaddr) from the specified header """
    dec_hdr = [decode_2044(hdr) for hdr in message.get_all(header, [])]
    return getaddresses(dec_hdr)
Example #52
0
     assert value == mm_cfg.DISCARD
     # Discarded
     rejection = 'Discarded'
 # Forward the message
 if forward and addr:
     # If we've approved the message, we need to be sure to craft a
     # completely unique second message for the forwarding operation,
     # since we don't want to share any state or information with the
     # normal delivery.
     try:
         copy = readMessage(path)
     except IOError, e:
         if e.errno <> errno.ENOENT: raise
         raise Errors.LostHeldMessage(path)
     # It's possible the addr is a comma separated list of addresses.
     addrs = getaddresses([addr])
     if len(addrs) == 1:
         realname, addr = addrs[0]
         # If the address getting the forwarded message is a member of
         # the list, we want the headers of the outer message to be
         # encoded in their language.  Otherwise it'll be the preferred
         # language of the mailing list.
         lang = self.getMemberLanguage(addr)
     else:
         # Throw away the realnames
         addr = [a for realname, a in addrs]
         # Which member language do we attempt to use?  We could use
         # the first match or the first address, but in the face of
         # ambiguity, let's just use the list's preferred language
         lang = self.preferred_language
     otrans = i18n.get_translation()
Example #53
0
def process(mlist, msg, msgdata):
    recips = msgdata['recips']
    # Short circuit
    if not recips:
        return
    # There is an issue with addresses in To: or Cc: that differ in
    # case from the MemberCPAddresses in recips.  We can't just
    # lower-case everything because we still want CP addresses in
    # the final recips list, so we lower case the keys.
    # Seed this set with addresses we don't care about dup avoiding
    explicit_recips = {}
    listaddrs = [
        mlist.GetListEmail(),
        mlist.GetBouncesEmail(),
        mlist.GetOwnerEmail(),
        mlist.GetRequestEmail()
    ]
    for addr in listaddrs:
        explicit_recips[addr.lower()] = True
    # Figure out the set of explicit recipients
    ccaddrs = {}
    for header in ('to', 'cc', 'resent-to', 'resent-cc'):
        addrs = getaddresses(msg.get_all(header, []))
        if header == 'cc':
            for name, addr in addrs:
                ccaddrs[addr.lower()] = name, addr
        for name, addr in addrs:
            if not addr:
                continue
            # Ignore the list addresses for purposes of dup avoidance
            explicit_recips[addr.lower()] = True
    # Now strip out the list addresses
    for addr in listaddrs:
        del explicit_recips[addr.lower()]
    if not explicit_recips:
        # No one was explicitly addressed, so we can't do any dup collapsing
        return
    newrecips = []
    for r in recips:
        # If this recipient is explicitly addressed...
        if explicit_recips.has_key(r.lower()):
            send_duplicate = True
            # If the member wants to receive duplicates, or if the recipient
            # is not a member at all, just flag the X-Mailman-Duplicate: yes
            # header.
            if mlist.isMember(r) and \
                   mlist.getMemberOption(r, mm_cfg.DontReceiveDuplicates):
                send_duplicate = False
            # We'll send a duplicate unless the user doesn't wish it.  If
            # personalization is enabled, the add-dupe-header flag will add a
            # X-Mailman-Duplicate: yes header for this user's message.
            if send_duplicate:
                msgdata.setdefault('add-dup-header', {})[r] = True
                newrecips.append(r)
            elif ccaddrs.has_key(r.lower()):
                del ccaddrs[r.lower()]
        else:
            # Otherwise, this is the first time they've been in the recips
            # list.  Add them to the newrecips list and flag them as having
            # received this message.
            newrecips.append(r)
    # Set the new list of recipients
    msgdata['recips'] = newrecips
    # RFC 2822 specifies zero or one CC header
    del msg['cc']
    if ccaddrs:
        msg['Cc'] = COMMASPACE.join([formataddr(i) for i in ccaddrs.values()])
Example #54
0
    fp=open(file,"r")

    mbox = mailbox.UnixMailbox(fp, msgfactory) # parse unix mailbox

    G=nx.MultiDiGraph() # create empty graph

    # parse each messages and build graph 
    for msg in mbox: # msg is python email.Message.Message object
        (source_name,source_addr) = parseaddr(msg['From']) # sender
        # get all recipients
        # see http://www.python.org/doc/current/lib/module-email.Utils.html
        tos = msg.get_all('to', [])
        ccs = msg.get_all('cc', [])
        resent_tos = msg.get_all('resent-to', [])
        resent_ccs = msg.get_all('resent-cc', [])
        all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)
        # now add the edges for this mail message
        for (target_name,target_addr) in all_recipients:
            G.add_edge(source_addr,target_addr,message=msg)  

    # print edges with message subject
    for (u,v,d) in G.edges_iter(data=True):
        print "From: %s To: %s Subject: %s"%(u,v,d['message']["Subject"])
    

    try: # draw
        pos=nx.spring_layout(G,iterations=10)
        nx.draw(G,pos,node_size=0,alpha=0.4,edge_color='r',font_size=16)
        plt.savefig("unix_email.png")
        plt.show()
    except: # matplotlib not available
Example #55
0
    def _process(self, kw):
        # sort out what encoding we're going to use
        encoding = kw.get('encoding',
                          self.getProperty('encoding', default_encoding))
        text = self.__class__.__bases__[1].__call__(self, **kw)
        # ZPT adds newline at the end, but it breaks backward compatibility.
        # So I remove it.
        if text.endswith('\n'):
            text = text[:-1]
        if not self.html() and isinstance(text, unicode):
            text = text.encode(encoding, 'replace')
        # now turn the result into a MIMEText object
        msg = MIMEText(text.replace('\r', ''),
                       self.content_type.split('/')[1], encoding)
        # sort out what headers and addresses we're going to use
        headers = {}
        values = {}
        # headers from the headers property
        for header in getattr(self, 'headers', ()):
            name, value = header.split(':', 1)
            headers[name] = value
        # headers from the headers parameter
        headers_param = kw.get('headers', {})
        headers.update(headers_param)
        # values and some specific headers
        for key, header in (('mfrom', 'From'), ('mto', 'To'), ('mcc', 'Cc'),
                            ('mbcc', 'Bcc'), ('subject', 'Subject')):
            value = kw.get(
                key,
                headers_param.get(header,
                                  getattr(self, key, headers.get(header))))
            if value is not None:
                values[key] = value

                if key == 'subject':
                    try:
                        # Try to keep header non encoded
                        value = Header(value)
                    except UnicodeDecodeError:
                        value = Header(value, "UTF-8")

                else:
                    dest_list = []
                    for name, email in getaddresses(
                        (value, ) if isinstance(value, basestring) else value):
                        try:
                            name = Header(name)
                        except UnicodeDecodeError:
                            name = Header(name, "UTF-8")
                        dest_list.append(formataddr((name.encode(), email)))
                    value = ", ".join(dest_list)

                headers[header] = value
        # check required values have been supplied
        errors = []
        for param in ('mfrom', 'mto'):
            if not values.get(param):
                errors.append(param)
        if errors:
            raise TypeError(
                'The following parameters were required by not specified: ' +
                (', '.join(errors)))
        # add date header
        headers['Date'] = DateTime().rfc822()
        # do not let the MTA to generate the Message-ID:
        # we want to have it stored in ERP5, for mail threading
        headers['Message-ID'] = make_msgid()
        # turn headers into an ordered list for predictable header order
        keys = headers.keys()
        keys.sort()
        return msg, values, [(key, headers[key]) for key in keys]
Example #56
0
    def apply(self, ui):
        # get message to forward if not given in constructor
        if not self.message:
            self.message = ui.current_buffer.get_selected_message()
        mail = self.message.get_email()

        # set body text
        name, address = self.message.get_author()
        timestamp = self.message.get_date()
        qf = settings.get_hook('reply_prefix')
        if qf:
            quotestring = qf(name, address, timestamp, ui=ui, dbm=ui.dbman)
        else:
            quotestring = 'Quoting %s (%s)\n' % (name or address, timestamp)
        mailcontent = quotestring
        quotehook = settings.get_hook('text_quote')
        if quotehook:
            mailcontent += quotehook(self.message.accumulate_body())
        else:
            quote_prefix = settings.get('quote_prefix')
            for line in self.message.accumulate_body().splitlines():
                mailcontent += quote_prefix + line + '\n'

        envelope = Envelope(bodytext=mailcontent)

        # copy subject
        subject = decode_header(mail.get('Subject', ''))
        reply_subject_hook = settings.get_hook('reply_subject')
        if reply_subject_hook:
            subject = reply_subject_hook(subject)
        else:
            rsp = settings.get('reply_subject_prefix')
            if not subject.lower().startswith(('re:', rsp.lower())):
                subject = rsp + subject
        envelope.add('Subject', subject)

        # Auto-detect ML
        auto_replyto_mailinglist = settings.get('auto_replyto_mailinglist')
        if mail['List-Id'] and self.listreply is None:
            # mail['List-Id'] is need to enable reply-to-list
            self.listreply = auto_replyto_mailinglist
        elif mail['List-Id'] and self.listreply is True:
            self.listreply = True
        elif self.listreply is False:
            # In this case we only need the sender
            self.listreply = False

        # set From-header and sending account
        try:
            from_header, account = determine_sender(mail, 'reply')
        except AssertionError as e:
            ui.notify(e.message, priority='error')
            return
        envelope.add('From', from_header)

        # set To
        sender = mail['Reply-To'] or mail['From']
        my_addresses = settings.get_addresses()
        sender_address = parseaddr(sender)[1]
        cc = ''

        # check if reply is to self sent message
        if sender_address in my_addresses:
            recipients = [mail['To']]
            emsg = 'Replying to own message, set recipients to: %s' \
                % recipients
            logging.debug(emsg)
        else:
            recipients = [sender]

        if self.groupreply:
            # make sure that our own address is not included
            # if the message was self-sent, then our address is not included
            MFT = mail.get_all('Mail-Followup-To', [])
            followupto = self.clear_my_address(my_addresses, MFT)
            if followupto and settings.get('honor_followup_to'):
                logging.debug('honor followup to: %s', followupto)
                recipients = [followupto]
                # since Mail-Followup-To was set, ignore the Cc header
            else:
                if sender != mail['From']:
                    recipients.append(mail['From'])

                # append To addresses if not replying to self sent message
                if sender_address not in my_addresses:
                    cleared = self.clear_my_address(
                        my_addresses, mail.get_all('To', []))
                    recipients.append(cleared)

                # copy cc for group-replies
                if 'Cc' in mail:
                    cc = self.clear_my_address(
                        my_addresses, mail.get_all('Cc', []))
                    envelope.add('Cc', decode_header(cc))

        to = ', '.join(recipients)
        logging.debug('reply to: %s' % to)

        if self.listreply:
            # To choose the target of the reply --list
            # Reply-To is standart reply target RFC 2822:, RFC 1036: 2.2.1
            # X-BeenThere is needed by sourceforge ML also winehq
            # X-Mailing-List is also standart and is used by git-send-mail
            to = mail['Reply-To'] or mail['X-BeenThere'] or mail['X-Mailing-List']
            # Some mail server (gmail) will not resend you own mail, so you have
            # to deal with the one in sent
            if to is None:
                to = mail['To']
            logging.debug('mail list reply to: %s' % to)
            # Cleaning the 'To' in this case
            if envelope.get('To') is not None:
                envelope.__delitem__('To')

        # Finally setup the 'To' header
        envelope.add('To', decode_header(to))

        # if any of the recipients is a mailinglist that we are subscribed to,
        # set Mail-Followup-To header so that duplicates are avoided
        if settings.get('followup_to'):
            # to and cc are already cleared of our own address
            allrecipients = [to] + [cc]
            lists = settings.get('mailinglists')
            # check if any recipient address matches a known mailing list
            if any([addr in lists for n, addr in getaddresses(allrecipients)]):
                followupto = ', '.join(allrecipients)
                logging.debug('mail followup to: %s' % followupto)
                envelope.add('Mail-Followup-To', decode_header(followupto))

        # set In-Reply-To header
        envelope.add('In-Reply-To', '<%s>' % self.message.get_message_id())

        # set References header
        old_references = mail.get('References', '')
        if old_references:
            old_references = old_references.split()
            references = old_references[-8:]
            if len(old_references) > 8:
                references = old_references[:1] + references
            references.append('<%s>' % self.message.get_message_id())
            envelope.add('References', ' '.join(references))
        else:
            envelope.add('References', '<%s>' % self.message.get_message_id())

        # continue to compose
        encrypt = mail.get_content_subtype() == 'encrypted'
        ui.apply_command(ComposeCommand(envelope=envelope,
                                        spawn=self.force_spawn,
                                        encrypt=encrypt))
Example #57
0
def getAttachements(rfcmessage):
    container = []
    message = email.message_from_string(rfcmessage)
    subject = decode_subject(message["subject"])

    tos = message.get_all('to', [])
    ccs = message.get_all('cc', [])
    resent_tos = message.get_all('resent-to', [])
    resent_ccs = message.get_all('resent-cc', [])
    all_recipients = getaddresses(tos + ccs + resent_tos + resent_ccs)

    froms = message.get_all('from', [])
    try:
        message_id = message["message-id"]
    except:
        message_id = "not_unique"
    sender = getaddresses(froms)

    recepients = []
    for to_whom_name, to_whom in all_recipients:
        try:
            recepients.append(USER_TO_URL_RE.match(to_whom).groupdict())
        except:
            pass

    counter = 0
    for msg in message.walk():
        if not msg.is_multipart() and msg.get_content_maintype() != 'message':
            ct = msg.get_content_type()
            mtype = msg.get_content_maintype().lower()
            payload = msg.get_payload()
            te = msg.get('Content-Transfer-Encoding', '8bit').lower()
            if te == 'base64':
                try:
                    payload = email.base64MIME.decode(payload)
                except:
                    payload = ''  # ???
            elif te == 'quoted-printable':
                try:
                    payload = email.quopriMIME.decode(payload)
                except:
                    payload = ''  # ???
            if mtype == 'text' or mtype is None:
                try:
                    tc = msg.get_content_charset()
                except:
                    tc = 'latin-1'
            else:
                pass

            if mtype in ('image', 'video', 'audio', 'text', 'application'):
                counter += 1
                name = dict(msg.get_params()
                            or []).get('name', 'noname-%i.txt' % counter)
            container.append((ct, name, payload))
        else:
            pass

    return Storage(
        sender=sender,
        recepients=recepients,
        subject=subject,
        message_id=message_id,
        container=container,
    )
Example #58
0
    def secureSend(self, message, mto, mfrom, subject='[No Subject]',
                   mcc=None, mbcc=None, subtype='plain', charset='us-ascii',
                   debug=False, **kwargs):
        """A more secure way to send a message

        message:
            The plain message text without any headers or an
            email.Message.Message based instance
        mto:
            To: field (string or list)
        mfrom:
            From: field
        subject:
            Message subject (default: [No Subject])
        mcc:
            Cc: (carbon copy) field (string or list)
        mbcc:
            Bcc: (blind carbon copy) field (string or list)
        subtype:
            Content subtype of the email e.g. 'plain' for text/plain (ignored
            if message is a email.Message.Message instance)
        charset:
            Charset used for the email, subject and email addresses
        kwargs:
            Additional headers
        """
        mto  = self.emailListToString(mto)
        mcc  = self.emailListToString(mcc)
        mbcc = self.emailListToString(mbcc)
        # validate email addresses
        # XXX check Return-Path
        for addr in mto, mcc, mbcc:
            if addr:
                result = self.validateEmailAddresses(addr)
                if not result:
                    raise MailHostError, 'Invalid email address: %s' % addr
        result = self.validateSingleEmailAddress(mfrom)
        if not result:
            raise MailHostError, 'Invalid email address: %s' % mfrom

        # create message
        if isinstance(message, email.Message.Message):
            # got an email message. Make a deepcopy because we don't want to
            # change the message
            msg = deepcopy(message)
        else:
            if isinstance(message, unicode):
                message = message.encode(charset)
            msg = email.MIMEText.MIMEText(message, subtype, charset)

        mfrom = encodeHeaderAddress(mfrom, charset)
        mto = encodeHeaderAddress(mto, charset)
        mcc = encodeHeaderAddress(mcc, charset)
        mbcc = encodeHeaderAddress(mbcc, charset)

        # set important headers
        self.setHeaderOf(msg, skipEmpty=True, From=mfrom, To=mto,
                 Subject=str(email.Header.Header(subject, charset)),
                 Cc=mcc, Bcc=mbcc)

        for bad in BAD_HEADERS:
            if bad in kwargs:
                raise MailHostError, 'Header %s is forbidden' % bad
        self.setHeaderOf(msg, **kwargs)

        # we have to pass *all* recipient email addresses to the
        # send method because the smtp server doesn't add CC and BCC to
        # the list of recipients
        to = msg.get_all('to', [])
        cc = msg.get_all('cc', [])
        bcc = msg.get_all('bcc', [])
        #resent_tos = msg.get_all('resent-to', [])
        #resent_ccs = msg.get_all('resent-cc', [])
        recipient_list = getaddresses(to + cc + bcc)
        all_recipients = [formataddr(pair) for pair in recipient_list]

        # finally send email
        return self._send(mfrom, all_recipients, msg, debug=debug)
Example #59
0
def send_i18n_digests(mlist, mboxfp):
    mbox = Mailbox(mboxfp)
    # Prepare common information (first lang/charset)
    lang = mlist.preferred_language
    lcset = Utils.GetCharSet(lang)
    lcset_out = Charset(lcset).output_charset or lcset
    # Common Information (contd)
    realname = mlist.real_name
    volume = mlist.volume
    issue = mlist.next_digest_number
    digestid = _('%(realname)s Digest, Vol %(volume)d, Issue %(issue)d')
    digestsubj = Header(digestid, lcset, header_name='Subject')
    # Set things up for the MIME digest.  Only headers not added by
    # CookHeaders need be added here.
    # Date/Message-ID should be added here also.
    mimemsg = Message.Message()
    mimemsg['Content-Type'] = 'multipart/mixed'
    mimemsg['MIME-Version'] = '1.0'
    mimemsg['From'] = mlist.GetRequestEmail()
    mimemsg['Subject'] = digestsubj
    mimemsg['To'] = mlist.GetListEmail()
    mimemsg['Reply-To'] = mlist.GetListEmail()
    mimemsg['Date'] = formatdate(localtime=1)
    mimemsg['Message-ID'] = Utils.unique_message_id(mlist)
    # Set things up for the rfc1153 digest
    plainmsg = StringIO()
    rfc1153msg = Message.Message()
    rfc1153msg['From'] = mlist.GetRequestEmail()
    rfc1153msg['Subject'] = digestsubj
    rfc1153msg['To'] = mlist.GetListEmail()
    rfc1153msg['Reply-To'] = mlist.GetListEmail()
    rfc1153msg['Date'] = formatdate(localtime=1)
    rfc1153msg['Message-ID'] = Utils.unique_message_id(mlist)
    separator70 = '-' * 70
    separator30 = '-' * 30
    # In the rfc1153 digest, the masthead contains the digest boilerplate plus
    # any digest header.  In the MIME digests, the masthead and digest header
    # are separate MIME subobjects.  In either case, it's the first thing in
    # the digest, and we can calculate it now, so go ahead and add it now.
    mastheadtxt = Utils.maketext(
        'masthead.txt', {
            'real_name': mlist.real_name,
            'got_list_email': mlist.GetListEmail(),
            'got_listinfo_url': mlist.GetScriptURL('listinfo', absolute=1),
            'got_request_email': mlist.GetRequestEmail(),
            'got_owner_email': mlist.GetOwnerEmail(),
        },
        mlist=mlist)
    # MIME
    masthead = MIMEText(mastheadtxt, _charset=lcset)
    masthead['Content-Description'] = digestid
    mimemsg.attach(masthead)
    # RFC 1153
    print >> plainmsg, mastheadtxt
    print >> plainmsg
    # Now add the optional digest header but only if more than whitespace.
    if re.sub('\s', '', mlist.digest_header):
        headertxt = decorate(mlist, mlist.digest_header, _('digest header'))
        # MIME
        header = MIMEText(headertxt, _charset=lcset)
        header['Content-Description'] = _('Digest Header')
        mimemsg.attach(header)
        # RFC 1153
        print >> plainmsg, headertxt
        print >> plainmsg
    # Now we have to cruise through all the messages accumulated in the
    # mailbox file.  We can't add these messages to the plainmsg and mimemsg
    # yet, because we first have to calculate the table of contents
    # (i.e. grok out all the Subjects).  Store the messages in a list until
    # we're ready for them.
    #
    # Meanwhile prepare things for the table of contents
    toc = StringIO()
    print >> toc, _("Today's Topics:\n")
    # Now cruise through all the messages in the mailbox of digest messages,
    # building the MIME payload and core of the RFC 1153 digest.  We'll also
    # accumulate Subject: headers and authors for the table-of-contents.
    messages = []
    msgcount = 0
    msg = mbox.next()
    while msg is not None:
        if msg == '':
            # It was an unparseable message
            msg = mbox.next()
            continue
        msgcount += 1
        messages.append(msg)
        # Get the Subject header
        msgsubj = msg.get('subject', _('(no subject)'))
        subject = Utils.oneline(msgsubj, lcset)
        # Don't include the redundant subject prefix in the toc
        mo = re.match('(re:? *)?(%s)' % re.escape(mlist.subject_prefix),
                      subject, re.IGNORECASE)
        if mo:
            subject = subject[:mo.start(2)] + subject[mo.end(2):]
        username = ''
        addresses = getaddresses([Utils.oneline(msg.get('from', ''), lcset)])
        # Take only the first author we find
        if isinstance(addresses, ListType) and addresses:
            username = addresses[0][0]
            if not username:
                username = addresses[0][1]
        if username:
            username = '******' % username
        # Put count and Wrap the toc subject line
        wrapped = Utils.wrap('%2d. %s' % (msgcount, subject), 65)
        slines = wrapped.split('\n')
        # See if the user's name can fit on the last line
        if len(slines[-1]) + len(username) > 70:
            slines.append(username)
        else:
            slines[-1] += username
        # Add this subject to the accumulating topics
        first = True
        for line in slines:
            if first:
                print >> toc, ' ', line
                first = False
            else:
                print >> toc, '     ', line.lstrip()
        # We do not want all the headers of the original message to leak
        # through in the digest messages.  For this phase, we'll leave the
        # same set of headers in both digests, i.e. those required in RFC 1153
        # plus a couple of other useful ones.  We also need to reorder the
        # headers according to RFC 1153.  Later, we'll strip out headers for
        # for the specific MIME or plain digests.
        keeper = {}
        all_keepers = {}
        for header in (mm_cfg.MIME_DIGEST_KEEP_HEADERS +
                       mm_cfg.PLAIN_DIGEST_KEEP_HEADERS):
            all_keepers[header] = True
        all_keepers = all_keepers.keys()
        for keep in all_keepers:
            keeper[keep] = msg.get_all(keep, [])
        # Now remove all unkempt headers :)
        for header in msg.keys():
            del msg[header]
        # And add back the kept header in the RFC 1153 designated order
        for keep in all_keepers:
            for field in keeper[keep]:
                msg[keep] = field
        # And a bit of extra stuff
        msg['Message'] = ` msgcount `
        # Get the next message in the digest mailbox
        msg = mbox.next()
    # Now we're finished with all the messages in the digest.  First do some
    # sanity checking and then on to adding the toc.
    if msgcount == 0:
        # Why did we even get here?
        return
    toctext = to_cset_out(toc.getvalue(), lcset)
    # MIME
    tocpart = MIMEText(toctext, _charset=lcset)
    tocpart['Content-Description'] = _(
        "Today's Topics (%(msgcount)d messages)")
    mimemsg.attach(tocpart)
    # RFC 1153
    print >> plainmsg, toctext
    print >> plainmsg
    # For RFC 1153 digests, we now need the standard separator
    print >> plainmsg, separator70
    print >> plainmsg
    # Now go through and add each message
    mimedigest = MIMEBase('multipart', 'digest')
    mimemsg.attach(mimedigest)
    first = True
    for msg in messages:
        # MIME.  Make a copy of the message object since the rfc1153
        # processing scrubs out attachments.
        mimedigest.attach(MIMEMessage(copy.deepcopy(msg)))
        # rfc1153
        if first:
            first = False
        else:
            print >> plainmsg, separator30
            print >> plainmsg
        # Use Mailman.Handlers.Scrubber.process() to get plain text
        try:
            msg = scrubber(mlist, msg)
        except Errors.DiscardMessage:
            print >> plainmsg, _('[Message discarded by content filter]')
            continue
        # Honor the default setting
        for h in mm_cfg.PLAIN_DIGEST_KEEP_HEADERS:
            if msg[h]:
                uh = Utils.wrap('%s: %s' % (h, Utils.oneline(msg[h], lcset)))
                uh = '\n\t'.join(uh.split('\n'))
                print >> plainmsg, uh
        print >> plainmsg
        # If decoded payload is empty, this may be multipart message.
        # -- just stringfy it.
        payload = msg.get_payload(decode=True) \
                  or msg.as_string().split('\n\n',1)[1]
        mcset = msg.get_content_charset('')
        if mcset and mcset <> lcset and mcset <> lcset_out:
            try:
                payload = unicode(payload, mcset,
                                  'replace').encode(lcset, 'replace')
            except (UnicodeError, LookupError):
                # TK: Message has something unknown charset.
                #     _out means charset in 'outer world'.
                payload = unicode(payload, lcset_out,
                                  'replace').encode(lcset, 'replace')
        print >> plainmsg, payload
        if not payload.endswith('\n'):
            print >> plainmsg
    # Now add the footer but only if more than whitespace.
    if re.sub('\s', '', mlist.digest_footer):
        footertxt = decorate(mlist, mlist.digest_footer, _('digest footer'))
        # MIME
        footer = MIMEText(footertxt, _charset=lcset)
        footer['Content-Description'] = _('Digest Footer')
        mimemsg.attach(footer)
        # RFC 1153
        # MAS: There is no real place for the digest_footer in an RFC 1153
        # compliant digest, so add it as an additional message with
        # Subject: Digest Footer
        print >> plainmsg, separator30
        print >> plainmsg
        print >> plainmsg, 'Subject: ' + _('Digest Footer')
        print >> plainmsg
        print >> plainmsg, footertxt
        print >> plainmsg
        print >> plainmsg, separator30
        print >> plainmsg
    # Do the last bit of stuff for each digest type
    signoff = _('End of ') + digestid
    # MIME
    # BAW: This stuff is outside the normal MIME goo, and it's what the old
    # MIME digester did.  No one seemed to complain, probably because you
    # won't see it in an MUA that can't display the raw message.  We've never
    # got complaints before, but if we do, just wax this.  It's primarily
    # included for (marginally useful) backwards compatibility.
    mimemsg.postamble = signoff
    # rfc1153
    print >> plainmsg, signoff
    print >> plainmsg, '*' * len(signoff)
    # Do our final bit of housekeeping, and then send each message to the
    # outgoing queue for delivery.
    mlist.next_digest_number += 1
    virginq = get_switchboard(mm_cfg.VIRGINQUEUE_DIR)
    # Calculate the recipients lists
    plainrecips = []
    mimerecips = []
    drecips = mlist.getDigestMemberKeys() + mlist.one_last_digest.keys()
    for user in mlist.getMemberCPAddresses(drecips):
        # user might be None if someone who toggled off digest delivery
        # subsequently unsubscribed from the mailing list.  Also, filter out
        # folks who have disabled delivery.
        if user is None or mlist.getDeliveryStatus(user) <> ENABLED:
            continue
        # Otherwise, decide whether they get MIME or RFC 1153 digests
        if mlist.getMemberOption(user, mm_cfg.DisableMime):
            plainrecips.append(user)
        else:
            mimerecips.append(user)
    # Zap this since we're now delivering the last digest to these folks.
    mlist.one_last_digest.clear()
    # MIME
    virginq.enqueue(mimemsg,
                    recips=mimerecips,
                    listname=mlist.internal_name(),
                    isdigest=True)
    # RFC 1153
    rfc1153msg.set_payload(to_cset_out(plainmsg.getvalue(), lcset), lcset)
    virginq.enqueue(rfc1153msg,
                    recips=plainrecips,
                    listname=mlist.internal_name(),
                    isdigest=True)
Example #60
0
def process(msg):
    all = msg.get_all('x-failed-recipients', [])
    return [a for n, a in getaddresses(all)]