Пример #1
0
 def create_mail_document(self,mail):
     body = ""
     if(mail.is_multipart()):
         body = self.get_body_from_multipart_mail(mail)
     else:
         body = unicode(mail.get_payload(decode=True),self.get_charset(mail),"replace")
     from_email = parseaddr(mail.get('From'))[1]
     if(mail.get_all('Cc')):
         ccs_string = mail.get_all('Cc')
     else:
         ccs_string = ''
     if(mail.get_all('To')):
         tos_string = mail.get_all('To')
     else:
         tos_string = ''
     cc_emails = map(lambda addr:addr[1],getaddresses(ccs_string))
     to_emails = map(lambda addr:addr[1],getaddresses(tos_string))
     from_user = parseaddr(mail.get('From'))[0]
     subject = mail.get('Subject')
     message_id = mail.get('Message-Id')
     date = datetime.fromtimestamp(mktime(parsedate(mail.get('Date'))))
     body = Mail.clean_body(body)
     from_user = self.get_user_name(from_user,from_email)
     sender ,creation_status = User.objects.get_or_create(email=from_email,auto_save=False)
     if creation_status:
         sender.name = from_user
         sender.save()
     mail_document = Mail(message_id=message_id,body=body,to=to_emails,sender=sender,cc=cc_emails,subject=subject,date=date)
     return mail_document
Пример #2
0
	def from_python_message(cls, msg):
		"""Given a python :class:`email.Message` object, return a corresponding class:`kiki.message.KikiMessage` instance."""
		payload = msg.get_payload()
		if msg.is_multipart():
			# TODO: is this the correct way to determine "body" vs "attachments"?
			body = payload[0]
			attachments = payload[1:]
		else:
			body = payload
			attachments = None
		
		# For now, let later header values override earlier ones. TODO: This should be more complex.
		headers = dict([(k.lower(), v) for k, v in msg.items() if k not in ('to', 'cc', 'bcc')])
		
		to = [addr[1] for addr in getaddresses(msg.get_all('to', []))]
		cc = [addr[1] for addr in getaddresses(msg.get_all('cc', []))]
		bcc = [addr[1] for addr in getaddresses(msg.get_all('bcc', []))]
		
		kwargs = {
			'subject': headers.pop('subject', ''),
			'body': body,
			'from_email': headers.pop('from', ''),
			'to': to,
			'bcc': bcc,
			'attachments': attachments,
			'headers': headers,
			'cc': cc
		}
		return cls(**kwargs)
Пример #3
0
def _send_message(smtp_obj, msg,
                  from_addr=None, to_addrs=None,
                  mail_options=None, rcpt_options=None):
    """ Convert message to a bytestring and passes it to sendmail.

    :type smtp_obj: smtplib.SMTP
    :type msg: email.message.Message
    :type from_addr: basestring or email.header.Header
    :type to_addrs: list of basestring or list of email.header.Header
    :type mail_options: list
    :type rcpt_options: dict

    This function is adapted from the python3 implementation of
    ``smtplib.SMTP.send_message``.

    """
    mail_options = mail_options or []
    rcpt_options = rcpt_options or {}
    # if the global cereconf disable is set -- abort here!
    if _is_disabled(False):
        raise RuntimeError("email disabled!")
    # 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822
    # Section 3.6.6). In such a case, we use the 'Resent-*' fields.  However,
    # if there is more than one 'Resent-' block there's no way to
    # unambiguously determine which one is the most recent in all cases,
    # so rather than guess we raise a ValueError in that case.
    #
    # TODO implement heuristics to guess the correct Resent-* block with an
    # option allowing the user to enable the heuristics.  (It should be
    # possible to guess correctly almost all of the time.)
    resent = msg.get_all('Resent-Date')
    if resent is None:
        header_prefix = ''
    elif len(resent) == 1:
        header_prefix = 'Resent-'
    else:
        raise ValueError("message has more than one 'Resent-' header block")
    if from_addr is None:
        # Prefer the sender field per RFC 2822:3.6.2.
        from_addr = (msg[header_prefix + 'Sender']
                     if (header_prefix + 'Sender') in msg
                     else msg[header_prefix + 'From'])
        from_addr = getaddresses([from_addr])[0][1]
    if to_addrs is None:
        addr_fields = [f for f in (msg[header_prefix + 'To'],
                                   msg[header_prefix + 'Bcc'],
                                   msg[header_prefix + 'Cc'])
                       if f is not None]
        to_addrs = [a[1] for a in getaddresses(addr_fields)]
    # Make a local copy so we can delete the bcc headers.
    msg_copy = copy.copy(msg)
    del msg_copy['Bcc']
    del msg_copy['Resent-Bcc']
    flatmsg = msg_copy.as_string()
    logger.info("sending email to %r", to_addrs)
    result = smtp_obj.sendmail(from_addr, to_addrs, flatmsg, mail_options,
                               rcpt_options)
    for addr, error_tuple in result.items():
        logger.error("unable to send to %r: %r", addr, error_tuple)
    return result
Пример #4
0
def msg2tags(msg):
    values = msg.get_all('From', [])
    for (_,a) in getaddresses(map(str, values)):
        yield 'F:'+a
    values = msg.get_all('To', []) + msg.get_all('Cc', [])
    for (_,a) in getaddresses(map(str, values)):
        yield 'T:'+a
    return
Пример #5
0
 def parse(self, content):
     p = Parser()
     msgobj = p.parsestr(content)
     if msgobj['Subject'] is not None:
         decodefrag = decode_header(msgobj['Subject'])
         subj_fragments = []
         for s , enc in decodefrag:
             if enc:
                 s = unicode(s , enc).encode('utf8','replace')
             subj_fragments.append(s)
         subject = ''.join(subj_fragments)
     else:
         subject = None
     subject = subject.replace('\n\t', " ")
     attachments = []
     body = None
     html = None
     for part in msgobj.walk():
         attachment = self.parse_attachment(part)
         if attachment:
             attachments.append(attachment)
         elif part.get_content_type() == "text/plain":
             if body is None:
                 body = ""
             charset = part.get_content_charset() or 'ascii'
             body += unicode(
                 part.get_payload(decode=True),
                 charset,
                 'replace').encode('utf8','replace')
         elif part.get_content_type() == "text/html":
             if html is None:
                 html = ""
             charset = part.get_content_charset() or 'ascii'
             html += unicode(
                 part.get_payload(decode=True),
                 charset,
                 'replace').encode('utf8','replace')
     tos = getaddresses(msgobj.get_all('To', []))
     ccs = getaddresses(msgobj.get_all('Cc', []))
     resent_tos = getaddresses(msgobj.get_all('resent-to', []))
     resent_ccs = getaddresses(msgobj.get_all('resent-cc', []))
     date = self.parse_date(msgobj.get("Date"))
     return {
         'msgobj': msgobj,
         'date': date,
         'subject': subject,
         'body': body,
         'html': html,
         'from': parseaddr(msgobj.get('From')),
         'to': tos,
         'cc': ccs,
         'resent_to': resent_tos,
         'resent_cc': resent_ccs,
         'attachments': attachments
     }
Пример #6
0
    def __init__(self, body):
        # make email obj from body
        message = mail.InboundEmailMessage(body)

        # list of emails: ['*****@*****.**', ...]
        self.to = [a[1] for a in getaddresses([message.to])]
        self.cc = [a[1] for a in getaddresses([getattr(message, 'cc', '')])]

        self.recipient = None

        # check hook recipient in message.to list first
        for addr in self.to:
            if (addr.split('@')[1] == 'emailhooks.xyz'):
                self.recipient = addr.split('@')[0]
                break

        # if not in message.to, parse hook recipient from forwarding
        # details following the patterns:
        # "for <_____ @emailhooks.xyz>" or "for _____ @emailhooks.xyz"
        if (self.recipient is None):
            match = re.search('for <?(\S+)@emailhooks\.xyz', body, re.IGNORECASE)
            self.recipient = match.group(1).lower() if match else None

        self.sender = parseaddr(message.sender)[1]
        self.subject = getattr(message, 'subject', '')
        self.date = message.date

        self.html_body = ''
        for _, body in message.bodies('text/html'):
            self.html_body = body.decode()

        self.plain_body = ''
        for _, plain in message.bodies('text/plain'):
            self.plain_body = plain.decode()

        # Attachments are a list of tuples: (filename, EncodedPayload)
        # EncodedPayloads are likely to be base64
        #
        # EncodedPayload:
        # https://cloud.google.com/appengine/docs/python/refdocs/google.appengine.api.mail#google.appengine.api.mail.EncodedPayload
        #
        self.attachments = []

        for attachment in getattr(message, 'attachments', []):
            encoding = attachment[1].encoding
            payload = attachment[1].payload

            if (not encoding or encoding.lower() != 'base64'):
                payload = attachment[1].decode().encode('base64')

            self.attachments.append({
                'filename': attachment[0],
                'payload': payload
            })
Пример #7
0
    def clean(self, value):
        value = super(EmailListField, self).clean(value).strip() \
                .replace(';', ',')

        field = django.forms.EmailField()

        try:
            return getaddresses([', '.join([
                formataddr((name, field.clean(addr)))
            for name, addr in getaddresses([value])])])
        except django.forms.ValidationError:
            raise django.forms.ValidationError(self.error_messages['invalid'])
Пример #8
0
def main(args=sys.argv):
    parser = option_parser()
    opts, args = parser.parse_args(args)


    if len(args) > 1:
        if len(args) < 4:
            print ('You must specify the from address, to address and body text'
                    ' on the command line')
            return 1
        msg = compose_mail(args[1], args[2], args[3], subject=opts.subject,
                           attachment=opts.attachment)
        from_, to = args[1:3]
        efrom, eto = map(extract_email_address, (from_, to))
        eto = [eto]
    else:
        msg = sys.stdin.read()
        from email.parser import Parser
        from email.utils import getaddresses
        eml = Parser.parsestr(msg, headersonly=True)
        tos = eml.get_all('to', [])
        ccs = eml.get_all('cc', [])
        eto = getaddresses(tos + ccs)
        if not eto:
            raise ValueError('Email from STDIN does not specify any recipients')
        efrom = getaddresses(eml.get_all('from', []))
        if not efrom:
            raise ValueError('Email from STDIN does not specify a sender')
        efrom = efrom[0]


    outbox = None
    if opts.outbox is not None:
        outbox = os.path.abspath(os.path.expanduser(opts.outbox))
        from mailbox import Maildir
        outbox = Maildir(opts.outbox, factory=None)
    if opts.fork:
        if os.fork() != 0:
            return 0

    try:
        sendmail(msg, efrom, eto, localhost=opts.localhost, verbose=opts.verbose,
             timeout=opts.timeout, relay=opts.relay, username=opts.username,
             password=opts.password, port=opts.port,
             encryption=opts.encryption_method)
    except:
        if outbox is not None:
            outbox.add(msg)
            print 'Delivery failed. Message saved to', opts.outbox
        raise
    return 0
Пример #9
0
def main(args=sys.argv):
    parser = option_parser()
    opts, args = parser.parse_args(args)

    if len(args) > 1:
        if len(args) < 4:
            print ('You must specify the from address, to address and body text'
                    ' on the command line')
            return 1
        msg = compose_mail(args[1], args[2], args[3], subject=opts.subject,
                           attachment=opts.attachment)
        from_, to = args[1:3]
        eto = [extract_email_address(x.strip()) for x in to.split(',')]
        efrom = extract_email_address(from_)
    else:
        msg = sys.stdin.read()
        from email import message_from_string
        from email.utils import getaddresses
        eml = message_from_string(msg)
        tos = eml.get_all('to', [])
        ccs = eml.get_all('cc', []) + eml.get_all('bcc', [])
        eto = [x[1] for x in getaddresses(tos + ccs) if x[1]]
        if not eto:
            raise ValueError('Email from STDIN does not specify any recipients')
        efrom = getaddresses(eml.get_all('from', []))
        if not efrom:
            raise ValueError('Email from STDIN does not specify a sender')
        efrom = efrom[0][1]

    outbox = None
    if opts.outbox is not None:
        outbox = os.path.abspath(os.path.expanduser(opts.outbox))
        from mailbox import Maildir
        outbox = Maildir(opts.outbox, factory=None)
    if opts.fork:
        if os.fork() != 0:
            return 0

    try:
        sendmail(msg, efrom, eto, localhost=opts.localhost, verbose=opts.verbose,
             timeout=opts.timeout, relay=opts.relay, username=opts.username,
             password=opts.password, port=opts.port,
             encryption=opts.encryption_method, verify_server_cert=not opts.dont_verify_server_certificate, cafile=opts.cafile)
    except:
        if outbox is not None:
            outbox.add(msg)
            outbox.close()
            print 'Delivery failed. Message saved to', opts.outbox
        raise
    return 0
Пример #10
0
def validate_comma_separated_emails(value):
    parsed = getaddresses([value])
    for name, address in parsed:
        try:
            validate_email(address)
        except ValidationError:
            raise ValidationError(_(u'utils:validate_comma_separated_emails:invalid_error {0}').format(address))
Пример #11
0
def parseMetaData(metas):
    # new_metas = Metas()
    new_metas = copy.deepcopy(metas)
    gtlt = re.compile('[<>]')
    for cpage in metas:
        msg = metas[cpage]['msg'].single()
        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)
        for r in all_recipients:
            new_metas[cpage]["Recipient"].add(r[1])
        reply_to = msg.get_all('reply-to', [])
        if reply_to:
            new_metas[cpage]["Sender"].add(reply_to[0])
        msg_from = msg.get_all('from', []).pop()
        new_metas[cpage]["From"].add(msg_from)
        date = msg.get_all('date', []).pop()
        new_metas[cpage]["Date"].add(date)
        subject = msg.get_all('subject', []).pop()
        subject = email.Header.decode_header(subject).pop()
        if subject[1] is not None:
            subject = unicode(subject[0], subject[1])
        else:
            subject = unicode(subject[0], 'utf-8')
        new_metas[cpage]["Subject"].add(subject)
        rpath = msg.get_all('return-path', []).pop()
        rpath = gtlt.sub('', rpath)
        new_metas[cpage]["Return-Path"].add(rpath)
        msgid = msg.get_all('message-id', []).pop()
        msgid = gtlt.sub('', msgid)
        new_metas[cpage]["Message-ID"].add(msgid)
    return new_metas
Пример #12
0
 def increment_of_mailaddr(text):
     addrs = [
         a for n, a in getaddresses(
             [x for x in re.sub(r'<mailto:[^>]+>', '', text).replace(' ', '\n').splitlines() if x.find('@') > 1]
         )
     ]
     return len(''.join(addrs)) + len(addrs) * len('<mailto:|>')
Пример #13
0
 def add_to_toc(self, msg, count):
     """Add a message to the table of contents."""
     subject = msg.get('subject', _('(no subject)'))
     subject = oneline(subject, in_unicode=True)
     # Don't include the redundant subject prefix in the toc
     mo = re.match('(re:? *)?({0})'.format(
         re.escape(self._mlist.subject_prefix)),
                   subject, re.IGNORECASE)
     if mo:
         subject = subject[:mo.start(2)] + subject[mo.end(2):]
     # Take only the first author we find.
     username = ''
     addresses = getaddresses(
         [oneline(msg.get('from', ''), in_unicode=True)])
     if addresses:
         username = addresses[0][0]
         if not username:
             username = addresses[0][1]
     if username:
         username = '******'.format(username)
     lines = wrap('{0:2}. {1}'. format(count, subject), 65).split('\n')
     # See if the user's name can fit on the last line
     if len(lines[-1]) + len(username) > 70:
         lines.append(username)
     else:
         lines[-1] += username
     # Add this subject to the accumulating topics
     first = True
     for line in lines:
         if first:
             print(' ', line, file=self._toc)
             first = False
         else:
             print('     ', line.lstrip(), file=self._toc)
Пример #14
0
def properly_encode_header(value, encoder, not_email):
    """
    The only thing special (weird) about this function is that it tries
    to do a fast check to see if the header value has an email address in
    it.  Since random headers could have an email address, and email addresses
    have weird special formatting rules, we have to check for it.

    Normally this works fine, but in Librelist, we need to "obfuscate" email
    addresses by changing the '@' to '-AT-'.  This is where
    VALUE_IS_EMAIL_ADDRESS exists.  It's a simple lambda returning True/False
    to check if a header value has an email address.  If you need to make this
    check different, then change this.
    """
    try:
        return value.encode("ascii")
    except UnicodeEncodeError:
        if not_email is False and VALUE_IS_EMAIL_ADDRESS(value):
            # this could have an email address, make sure we don't screw it up
            addresses = getaddresses(value.split(","))
            return ", ".join([
                '"%s" <%s>' % (encoder.header_encode(name.encode("utf-8")), address)
                for name, address in addresses
            ])

        return encoder.header_encode(value.encode("utf-8"))
Пример #15
0
    def send(self,message,rcpt=None):
        """
            message         : email.Message instance
            rcpt            : List of recipients (normally parsed from
                              To/Cc/Bcc fields)
            Send message
        """
        # Check if connected and connect if false
        if not self.is_connected():
            self.connect()
        # Extract recipients
        if rcpt is None:
            rcpt = [ addr[1] for addr in getaddresses((message.get_all('To') or []) + 
                                                      (message.get_all('Cc') or []) + 
                                                      (message.get_all('Bcc') or [])) ]
        # Fill in message fileds if not already set
        # NOTE: this modifies the original message and in particular deletes the Bcc field
        if message['From'] is None:
            message['From'] = self.sender
        if message['Reply-To'] is None:
            message['Reply-To'] = self.sender
        if message['Date'] is None:
            message['Date'] = formatdate(time.time(),localtime=True)
        if message['Message-ID'] is None:
            message['Message-ID'] = make_msgid()
        del message['Bcc']

        # Send message
        self.session.sendmail(self.sender,rcpt,message.as_string())
Пример #16
0
	def cook_headers(self):
		"""
		Remove: DKIM
		"""
		list_addrs = set([mailing_list.full_address for mailing_list in self.mailing_lists])
		for addr in list_addrs:
			self['X-BeenThere'] = addr
		
		if 'precedence' not in self:
			self['Precedence'] = 'list'
		
		# Reply-To should be whatever it was plus whatever lists this is being sent to.
		reply_to = set([address[1] for address in getaddresses(self.get_all('reply-to', []))]) | list_addrs
		del self['reply-to']
		if reply_to:
			self['Reply-To'] = COMMASPACE.join(reply_to)
		
		# To and Cc should be able to stay the same... though we can also put things back
		# right if necessary.
		# The whole 'letting people send messages to multiple lists' thing is getting to me.
		# It causes problems. Like how do I set the list headers if it's going to
		# three different lists?
		
		# Delete DKIM headers since they will not refer to our message.
		del self['domainkey-signature']
		del self['dkim-signature']
		del self['authentication-results']
Пример #17
0
    def __init__(self, raw=None, decoded_name=None, address=None):
        super(EmailAddress, self).__init__()

        if not raw is None:
            assert decoded_name is None and address is None

            if isinstance(raw, tuple):
                raw_str = formataddr(raw)
            elif isinstance(raw, str):
                raw_str = raw
            else:
                raise Exception('')

            self._raw = raw_str
            (encoded_name, address) = getaddresses([raw_str])[0]
            self._name_chunk = EmailHeaderChunk(raw=encoded_name)
            self._address = address
        else:
            assert not address is None
            if decoded_name is None:
                decoded_name = ''

            self._name_chunk = EmailHeaderChunk(decoded=u'')
            self._name_chunk.text = decoded_name
            self._address = address
Пример #18
0
def load(value):
    if not value:
        return None
    if isinstance(value, six.string_types):
        return getaddresses([value.strip()])
    else:
        return value
Пример #19
0
def get_addresses(maildir):
    emails = {}
    for root, _dirs, files in os.walk(maildir):
        for fname in files:
            fname = os.path.join(root, fname)
            msg = HeaderParser().parse(open(fname))

            froms = msg.get_all('from', [])
            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(froms + tos + ccs + resent_tos + resent_ccs)
            for (title, addr) in all_recipients:
                emails.setdefault(addr, set()).add(title)

    for addr, titles in emails.iteritems():
        clean = set()
        for title in titles:
            if title.startswith('=?'):
                title = dheader(title)

            title = title.strip("'\"<>").replace('\n', ' ')
            if title and title != addr:
                clean.add(title)

        if clean:
            for title in clean:
                yield addr, title
        else:
            yield addr, ''
Пример #20
0
def convert_addresses(raw_header):
    result = []
    name_addr_pairs = getaddresses([raw_header])
    for name, addr in name_addr_pairs:

        result.append({"name": _header_to_unicode(name), "address": addr})
    return result
Пример #21
0
def decode_message_headers(message):
    """
        Copy headers from the given message object into a dict
        structure and normalize the values into UTF-8 strings
    """
    headers = dict(message.items())

    # split recipient headers into lists
    for h in ['From','To','Cc','Bcc']:
        if headers.has_key(h):
            headers[h] = ['%s <%s>' % (name,address) for name,address in getaddresses(message.get_all(h))]

    # decode header values into UTF-8
    for k,value in headers.iteritems():
        if isinstance(value, list):
            headers[k] = [_decode_mime_header(x) for x in value]
        else:
            headers[k] = _decode_mime_header(value)

    # replace content-type with the normalized value (good for searching)
    headers['Content-Type'] = message.get_content_type()

    # convert Date into UTC
    if headers.has_key('Date'):
        try:
            date = parse_date(headers['Date']).astimezone(tzutc())
            headers['Date'] = datetime.datetime.strftime(date, "%Y-%m-%dT%H:%M:%SZ")
        except:
            pass

    return headers
Пример #22
0
def mbox_graph():
    try:
        fh = open("unix_email.mbox", 'rb')
    except IOError:
        print("unix_email.mbox not found")
        raise

    mbox = mailbox.UnixMailbox(fh, email.message_from_file)  # 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 https://docs.python.org/2/library/email.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)

    return G
Пример #23
0
def get_address_list(values):
    values = [parse_header_field(value) for value in values]
    address_list = getaddresses(values)
    fixed = []
    for addr in address_list:
        fixed.append((parse_header_field(addr[0]), addr[1]))
    return fixed
Пример #24
0
 def extra_parameters(self):
     """Mail message extra parameters."""
     lists = self.mail.get_all("List-ID")
     lists_addr = getaddresses(lists) if lists else None
     lists_ids = [address[1] for address in lists_addr] \
         if lists_addr else []
     return {'lists': lists_ids}
Пример #25
0
def extract_addresses(mail, file_alias_url, log):
    """Extract the domain the mail was sent to.

    Mails sent to Launchpad should have an X-Launchpad-Original-To header.
    This is added by the MTA before it ends up the mailbox for Launchpad.
    """
    if ORIGINAL_TO_HEADER in mail:
        return [mail[ORIGINAL_TO_HEADER]]

    if ORIGINAL_TO_HEADER in mail.as_string():
        # Doesn't have an X-Launchpad-Original-To in the headers, but does
        # have one in the body, because of a forwarding loop or attempted
        # spam.  See <https://bugs.launchpad.net/launchpad/+bug/701976>
        log.info('Suspected spam: %s' % file_alias_url)
    else:
        # This most likely means a email configuration problem, and it should
        # log an oops.
        log.warn(
            "No X-Launchpad-Original-To header was present "
            "in email: %s" % file_alias_url)
    # Process all addresses found as a fall back.
    cc = mail.get_all('cc') or []
    to = mail.get_all('to') or []
    names_addresses = getaddresses(to + cc)
    return [addr for name, addr in names_addresses]
Пример #26
0
    def get_addr_values(self, header_value):
        '''
        :header_value - value of particular header, which can store < mailbox name > + < address >
        :return: tuple of tuples (< mail box name (utf-8)>, < address (without angle braces) >)
        '''
        addr_value = namedtuple('addr_value', 'realname address')
        name_addr_tuples = (addr_value(*pair) for pair in utils.getaddresses(header_value))
        # and we can meet here tricky stuff like this:
        # ('=?utf-8?B?0KDQodCl0JDQo9Cf?= "=?utf-8?B?0JHQtdC70J/QodCl0JDQk9CY?="', '*****@*****.**')
        temp = list()
        for realname, address in tuple(name_addr_tuples):
            if not address:
                continue
            realname = re.sub('"','',realname)
            parts = tuple(header.decode_header(p) for p in realname.split())
            temp.append((parts, address.lower()))

        pairs = list()
        for t in temp:
            realname_parts, addr = t
            
            value = u''
            for part in realname_parts:
                if len(part)==0:
                    continue

                value += self._get_unicoded_value(*(reduce(add,part)))

            pairs.append((value, addr))

        pairs = tuple((p.realname, re.sub(r'<|>','',p.address)) for p in tuple(addr_value(*pair) for pair in pairs))
        #logger.debug(str(pairs))
        return pairs
Пример #27
0
    def _get_addresses(self, address_data, retain_name=False):
        """
        Takes RFC-compliant email addresses in both terse (email only)
        and verbose (name + email) forms and returns a list of
        email address strings

        (TODO: breaking change that returns a tuple of (name, email) per string)
        """
        if retain_name:
            raise NotImplementedError(
                "Not yet implemented, but will need client-code changes too"
            )

        # We trust than an email address contains an "@" after
        # email.utils.getaddresses has done the hard work. If we wanted
        # to we could use a regex to check for greater email validity

        # NB: getaddresses expects a list, so ensure we feed it appropriately
        if type(address_data) in [str, unicode]:
            if "[" not in address_data:
                # Definitely turn these into a list
                # NB: this is pretty assumptive, but still prob OK
                address_data = [address_data]

        output = [x[1] for x in getaddresses(address_data) if "@" in x[1]]
        return output
Пример #28
0
def cleanup_message(message,
                   addr_headers=ADDR_HEADERS, param_headers=PARAM_HEADERS):
    """
    Cleanup a `Message` handling header and payload charsets.

    Headers are handled in the most sane way possible.  Address names
    are left in `ascii` if possible or encoded to `iso-8859-1` or `utf-8`
    and finally encoded according to RFC 2047 without encoding the
    address, something the `email` stdlib package doesn't do.
    Parameterized headers such as `filename` in the
    `Content-Disposition` header, have their values encoded properly
    while leaving the rest of the header to be handled without
    encoding.  Finally, all other header are left in `ascii` if
    possible or encoded to `iso-8859-1` or `utf-8` as a whole.

    The message is modified in place and is also returned in such a
    state that it can be safely encoded to ascii.
    """
    for key, value in message.items():
        if key.lower() in addr_headers:
            addrs = []
            for name, addr in utils.getaddresses([value]):
                best, encoded = best_charset(name)
                if PY_2:
                    name = encoded
                name = header.Header(
                    name, charset=best, header_name=key).encode()
                addrs.append(utils.formataddr((name, addr)))
            value = ', '.join(addrs)
            message.replace_header(key, value)
        if key.lower() in param_headers:
            for param_key, param_value in message.get_params(header=key):
                if param_value:
                    best, encoded = best_charset(param_value)
                    if PY_2:
                        param_value = encoded
                    if best == 'ascii':
                        best = None
                    message.set_param(param_key, param_value,
                                      header=key, charset=best)
        else:
            best, encoded = best_charset(value)
            if PY_2:
                value = encoded
            value = header.Header(
                value, charset=best, header_name=key).encode()
            message.replace_header(key, value)

    payload = message.get_payload()
    if payload and isinstance(payload, text_type):
        charset = message.get_charset()
        if not charset:
            charset, encoded = best_charset(payload)
            message.set_payload(payload, charset=charset)
    elif isinstance(payload, list):
        for part in payload:
            cleanup_message(part)

    return message
Пример #29
0
 def process(self, mlist, msg, msgdata):
     """See `IHandler`."""
     recips = msgdata.get("recipients")
     # Short circuit
     if not recips:
         return
     # Seed this set with addresses we don't care about dup avoiding.
     listaddrs = set((mlist.posting_address, mlist.bounces_address, mlist.owner_address, mlist.request_address))
     explicit_recips = listaddrs.copy()
     # Figure out the set of explicit recipients.
     cc_addresses = {}
     for header in ("to", "cc", "resent-to", "resent-cc"):
         addrs = getaddresses(msg.get_all(header, []))
         header_addresses = dict((addr, formataddr((name, addr))) for name, addr in addrs if addr)
         if header == "cc":
             # Yes, it's possible that an address is mentioned in multiple
             # CC headers using different names.  In that case, the last
             # real name will win, but that doesn't seem like such a big
             # deal.  Besides, how else would you chose?
             cc_addresses.update(header_addresses)
         # Ignore the list addresses for purposes of dup avoidance.
         explicit_recips |= set(header_addresses)
     # Now strip out the list addresses.
     explicit_recips -= listaddrs
     if not explicit_recips:
         # No one was explicitly addressed, so we can't do any dup
         # collapsing
         return
     newrecips = set()
     for r in recips:
         # If this recipient is explicitly addressed...
         if r in explicit_recips:
             send_duplicate = True
             # If the member wants to receive duplicates, or if the
             # recipient is not a member at all, they will get a copy.
             # header.
             member = mlist.members.get_member(r)
             if member and not member.receive_list_copy:
                 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", set()).add(r)
                 newrecips.add(r)
             elif r in cc_addresses:
                 del cc_addresses[r]
         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.add(r)
     # Set the new list of recipients.  XXX recips should always be a set.
     msgdata["recipients"] = list(newrecips)
     # RFC 2822 specifies zero or one CC header
     if cc_addresses:
         del msg["cc"]
         msg["CC"] = COMMASPACE.join(cc_addresses.values())
Пример #30
0
 def getAddrList(self, message, name):
     """ See IMailinDispatcher.
     """
     addrs = message.get_all(name)
     if addrs is None:
         return []
     addrs = map(decode_header, addrs)
     return getaddresses(addrs)
Пример #31
0
        filePath = sys.argv[1]

    mbox = mailbox.mbox(filePath, 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(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
        pass
Пример #32
0
def parsed(raw, uid, time, flags):
    # "email.message_from_bytes" uses "email.policy.compat32" policy
    # and it's by intention, because new policies don't work well
    # with real emails which have no encodings, badly formated addreses, etc.
    orig = email.message_from_bytes(raw)
    htm, txt, files, headers, errors = parse_mime(orig, uid)
    meta = {'origin_uid': uid, 'files': [], 'errors': errors}
    if htm:
        embeds = {
            f['content-id']: f['url']
            for f in files if 'content-id' in f
        }
        htm, extra_meta = html.clean(htm, embeds)
        meta.update(extra_meta)
    elif txt:
        htm = html.from_text(txt)

    meta['preview'] = preview(htm, files)
    meta['files'] = files

    fields = (
        ('From', 1), ('Sender', 1),
        ('Reply-To', 0), ('To', 0), ('CC', 0), ('BCC', 0)
    )
    for n, one in fields:
        v = headers.get(n)
        if not v:
            continue
        v = addresses(v)
        meta[n.lower()] = v[0] if one else v

    subj = headers['Subject']
    meta['subject'] = str(subj).strip() if subj else ''

    refs = orig['references']
    refs = [i.strip().lower() for i in refs.split()] if refs else []
    parent = refs[-1] if refs else None
    in_reply_to = orig['in-reply-to'] and normalize_msgid(orig['in-reply-to'])
    if in_reply_to:
        parent = in_reply_to
        if not refs:
            refs = [in_reply_to]
    meta['parent'] = parent

    mid = orig['message-id']
    if mid is None:
        log.info('UID=%s has no "Message-ID" header', uid)
        mid = '<mailur@noid>'
    else:
        mid = normalize_msgid(mid)
    meta['msgid'] = mid

    arrived = dt.datetime.strptime(time.strip('"'), '%d-%b-%Y %H:%M:%S %z')
    meta['arrived'] = int(arrived.timestamp())

    date = orig['date']
    try:
        date = date and int(parsedate_to_datetime(date).timestamp())
    except Exception as e:
        meta['errors'].append('error on date: val=%r err=%r' % (date, e))
        log.error('UID=%s can\'t parse date: val=%r err=%r', uid, date, e)
        date = None
    meta['date'] = date or meta['arrived']

    msg = new()
    msg.add_header('X-UID', '<%s>' % uid)
    msg.add_header('Message-ID', mid)
    msg.add_header('Subject', meta['subject'])
    msg.add_header('Date', orig['Date'])

    for n, v in headers.items():
        if n in msg:
            continue
        msg.add_header(n, v)

    is_draft = '\\Draft' in flags
    if is_draft:
        draft_id = orig['X-Draft-ID'] or mid
        msg.add_header('X-Draft-ID', draft_id)
        meta['draft_id'] = draft_id
        txt = parse_draft(orig)[0]
    elif orig['X-Draft-ID']:
        msg.add_header('X-Draft-ID', orig['X-Draft-ID'])

    thrid = None
    if not is_draft:
        addrs = [msg['from'] or msg['sender'], msg['to']]
        addrs = (a for a in addrs if a)
        addrs = ','.join(sorted(
            '"%s" <%s>' % (n, a) if n else a
            for n, a in getaddresses(addrs)
        ))
        addrs_n_subj = ' '.join(i for i in (addrs, subj) if i)
        thrid = hashlib.md5(addrs_n_subj.encode()).hexdigest()
        thrid = '<*****@*****.**>' % thrid

    thrid = ' '.join(i for i in (thrid, orig['X-Thread-ID']) if i)
    if thrid:
        meta['thrid'] = thrid
        msg.add_header('X-Thread-ID', thrid)
        refs.insert(0, thrid)

    if refs:
        msg.add_header('In-Reply-To', refs[-1])
        msg.add_header('References', ' '.join(refs))

    msg.make_mixed()
    meta_txt = json.dumps(meta, sort_keys=True, ensure_ascii=False, indent=2)
    msg.attach(binary(meta_txt, 'application/json'))
    body = new()
    body.make_alternative()
    body.attach(binary(htm, 'text/html'))
    if txt:
        body.attach(binary(txt))
    msg.attach(body)

    flags = []
    if meta['errors']:
        flags.append('#err')
    return msg, flags
Пример #33
0
def header_check (db, cl, nodeid, new_values) :
    """ Check header of new messages and determine original customer
        from that header -- only if sender is the support special
        account (any account with system status).  If send_to_customer
        flag is set *and* account is not a system account, munge
        the headers and add X-ROUNDUP-TO and X-ROUNDUP-CC headers.
    """
    send_to_customer = False
    # Be sure to alway set send_to_customer to False!
    if 'send_to_customer' in new_values :
        send_to_customer = new_values ['send_to_customer']
        new_values ['send_to_customer'] = False
    newmsgs = new_values.get ('messages')
    if not newmsgs :
        return
    newmsgs = set (newmsgs)
    if nodeid :
        oldmsgs = set (cl.get (nodeid, 'messages'))
    else :
        oldmsgs = set ()
    system  = db.user_status.lookup ('system')
    cemail  = db.contact_type.lookup ('Email')
    for msgid in newmsgs.difference (oldmsgs) :
        msg    = db.msg.getnode (msgid)
        h      = None
        if msg.header :
            h = Parser ().parsestr (msg.header, headersonly = True)
        else :
            h = Message ()
        if db.user.get (msg.author, 'status') == system :
            frm  = fix_emails (h.get_all ('From'))
            subj = header_utf8 (h.get_all ('Subject') [0])
            if  (   frm
                and 'customer' not in new_values
                and 'emails' not in new_values
                ) :
                cc  = {}
                if not nodeid :
                    # use only first 'From' address (there shouldn't be more)
                    rn, mail = getaddresses (frm) [0]
                    # the *to* address in this mail is the support user we
                    # want as a from-address for future mails *to* this user
                    autad = None
                    hto = fix_emails (h.get_all ('To'))
                    if hto :
                        torn, autad = getaddresses (hto) [0]
                        if not autad.startswith ('support') :
                            autad = None
                    c = find_or_create_contact \
                        (db, mail, rn, frm = autad, subject = subj)
                    cust  = new_values ['customer'] = \
                        db.contact.get (c, 'customer')
                    new_values ['emails'] = [c]
                else :
                    supi = cl.getnode (nodeid)
                    cust = supi.customer
                    new_values ['emails']    = supi.emails
                    new_values ['cc_emails'] = supi.cc_emails
                    if supi.cc :
                        cc = dict.fromkeys \
                            (x.strip ().lower () for x in supi.cc.split (','))
                # Parse To and CC headers to find more customer email
                # addresses. Check if these contain the same domain
                # part as the From.
                ccs = h.get_all ('CC') or []
                tos = h.get_all ('To') or []
                if nodeid :
                    tos.extend (frm)
                cfrm = db.customer.get (cust, 'fromaddress')
                alltocc = dict.fromkeys (new_values ['emails'])
                if 'cc_emails' in new_values :
                    alltocc.update (dict.fromkeys (new_values ['cc_emails']))
                for addrs, field in ((tos, 'emails'), (ccs, 'cc_emails')) :
                    addrs = fix_emails (addrs)
                    for rn, mail in getaddresses (addrs) :
                        if mail == cfrm :
                            continue
                        c = find_or_create_contact \
                            (db, mail, rn, customer = cust)
                        if c :
                            if field not in new_values :
                                new_values [field] = []
                            if c not in alltocc :
                                new_values [field].append (c)
                                alltocc [c] = 1
                        elif uidFromAddress (db, (rn, mail), create = 0) :
                            # ignore internal addresses
                            pass
                        else :
                            cc [mail.lower ()] = 1
                if cc :
                    new_values ['cc'] = ', '.join (cc.keys ())
        else :
            if send_to_customer :
                mails = []
                cc = []
                if 'emails' in new_values :
                    mails = new_values ['emails']
                elif nodeid :
                    mails = cl.get (nodeid, 'emails')
                if 'cc_emails' in new_values :
                    mcc = new_values ['cc_emails']
                elif nodeid :
                    mcc = cl.get (nodeid, 'cc_emails')
                mails = mails or []
                mcc   = mcc or []
                mails = (db.contact.get (x, 'contact') for x in mails)
                mcc   = (db.contact.get (x, 'contact') for x in mcc)
                if 'cc' in new_values :
                    cc = new_values ['cc']
                elif nodeid :
                    cc = cl.get (nodeid, 'cc')
                m  = ','.join (mails)
                mc = ','.join (mcc)
                if mc :
                    if cc :
                        mc = ','.join ((mc, cc))
                else :
                    mc = cc
                if not m and not mc :
                    raise Reject, \
                        _ ("Trying to send to customer with empty CC and "
                           "without configured contact-email for customer"
                          )
                if m :
                    h.add_header ('X-ROUNDUP-TO', m)
                if mc :
                    h.add_header ('X-ROUNDUP-CC', mc)
                if 'bcc' in new_values :
                    bcc = new_values ['bcc']
                elif nodeid :
                    bcc = cl.get (nodeid, 'bcc')
                if bcc :
                    h.add_header ('X-ROUNDUP-BCC', bcc)
                # Time-Stamp of first reply to customer
                if not nodeid or not cl.get (nodeid, 'first_reply') :
                    new_values ['first_reply'] = Date ('.')
        h = h.as_string ()
        if h != '\n' and h != msg.header :
            db.msg.set (msgid, header = h)
Пример #34
0
USE_L10N = False

USE_TZ = True

DATET_FORMAT = 'd/m/Y'
DATETIME_FORMAT = 'd/m/Y H:i'


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [
    BASE_DIR / 'pes_league' / 'static',
]

CSRF_COOKIE_DOMAIN = env('CSRF_COOKIE_DOMAIN')
CSRF_COOKIE_NAME = env('CSRF_COOKIE_NAME')
SECURE_PROXY_SSL_HEADER = env('SECURE_PROXY_SSL_HEADER')

SESSION_COOKIE_NAME = env('SESSION_COOKIE_NAME')

GRAPHENE = {
    'SCHEMA': 'season.schema.schema',
}

DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'

ADMINS = getaddresses(env('ADMINS'))
Пример #35
0
 def _get_to(self, parsed_data):
     return getaddresses(parsed_data.get_all('to'))
Пример #36
0
def addresses(field, e_id, vals):
    addrs = getaddresses([vals])
    rev_field = 'Parsed-' + field
    for name, addr in addrs:
        print("{} ('{}','{}','{}','{}');".format(PREFIX_EXTRA, e_id, rev_field,
                                                 clean(name), clean(addr)))
Пример #37
0
def munged_headers(mlist, msg, msgdata):
    # This returns a list of tuples (header, content) where header is the
    # name of a header to be added to or replaced in the wrapper or message
    # for DMARC mitigation.  It sets From: to the string
    # 'original From: display name' via 'list name' <list posting address>
    # and adds the original From: to Reply-To: or Cc: per the following.
    # Our goals for this process 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.
    #
    # Be as robust as possible here.
    all_froms = getaddresses(msg.get_all('from', []))
    # Strip the nulls and bad emails.
    froms = [email for email in all_froms if '@' in email[1]]
    if len(froms) == 1:
        realname, email = original_from = froms[0]
    else:
        # No From: or multiple addresses.  Just punt and take
        # the get_sender result.
        realname = ''
        email = msgdata['original_sender']
        original_from = (realname, email)
    # If there was no display name in the email header, see if we have a
    # matching member with a display name.
    if len(realname) == 0:
        member = mlist.members.get_member(email)
        if member:
            realname = member.display_name or email
        else:
            realname = email
    # Remove the 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 member record, it should
    # be a string.  If it's from the email address, it should be an ASCII
    # string.  In any case, ensure it's an unencoded string.
    realname_bits = []
    for fragment, charset in decode_header(realname):
        if not charset:
            # Character set should be ASCII, but use iso-8859-1 anyway.
            charset = 'iso-8859-1'
        if not isinstance(fragment, str):
            realname_bits.append(str(fragment, charset, errors='replace'))
        else:
            realname_bits.append(fragment)
    # The member's display name is a string.
    realname = EMPTYSTRING.join(realname_bits)
    # Ensure the i18n context is the list's preferred_language.
    with _.using(mlist.preferred_language.code):
        via = _('$realname via $mlist.display_name')
    # Get an RFC 2047 encoded header string.
    display_name = str(Header(via, mlist.preferred_language.charset))
    value = [('From', formataddr((display_name, mlist.posting_address)))]
    # We've made the munged From:.  Now put the original in Reply-To: or Cc:
    if mlist.reply_goes_to_list is ReplyToMunging.no_munging:
        # Add original from to Reply-To:
        add_to = 'Reply-To'
    else:
        # Add original from to Cc:
        add_to = 'Cc'
    original = getaddresses(msg.get_all(add_to, []))
    if original_from[1] not in [x[1] for x in original]:
        original.append(original_from)
    value.append((add_to, COMMASPACE.join(formataddr(x) for x in original)))
    return value
Пример #38
0
    def send(cls, to='', cc='', bcc='', subject='', body='',
            attachments=None, record=None, reports=None):
        pool = Pool()
        User = pool.get('res.user')
        ActionReport = pool.get('ir.action.report')
        Attachment = pool.get('ir.attachment')
        transaction = Transaction()
        user = User(transaction.user)

        Model = pool.get(record[0])
        record = Model(record[1])

        body_html = HTML_EMAIL % {
            'subject': subject,
            'body': body,
            'signature': user.signature or '',
            }
        content = MIMEMultipart('alternative')
        if html2text:
            body_text = HTML_EMAIL % {
                'subject': subject,
                'body': body,
                'signature': '',
                }
            converter = html2text.HTML2Text()
            body_text = converter.handle(body_text)
            if user.signature:
                body_text += '\n-- \n' + converter.handle(user.signature)
            part = MIMEText(body_text, 'plain', _charset='utf-8')
            content.attach(part)
        part = MIMEText(body_html, 'html', _charset='utf-8')
        content.attach(part)
        if reports or attachments:
            msg = MIMEMultipart('mixed')
            msg.attach(content)
            if attachments is None:
                attachments = []
            else:
                attachments = list(attachments)

            for report_id in (reports or []):
                report = ActionReport(report_id)
                Report = pool.get(report.report_name, type='report')
                ext, content, _, title = Report.execute(
                    [record.id], {
                        'action_id': report.id,
                        })
                name = '%s.%s' % (title, ext)
                if isinstance(content, str):
                    content = content.encode('utf-8')
                attachments.append((name, content))

            for name, data in attachments:
                mimetype, _ = mimetypes.guess_type(name)
                if mimetype:
                    attachment = MIMENonMultipart(*mimetype.split('/'))
                    attachment.set_payload(data)
                    encode_base64(attachment)
                else:
                    attachment = MIMEApplication(data)
                attachment.add_header(
                    'Content-Disposition', 'attachment',
                    filename=('utf-8', '', name))
                msg.attach(attachment)
        else:
            msg = content
        msg['From'] = from_ = config.get('email', 'from')
        if user.email:
            if user.name:
                user_email = formataddr((user.name, user.email))
            else:
                user_email = user.email
            msg['Behalf-Of'] = user_email
            msg['Reply-To'] = user_email
        msg['To'] = ', '.join(formataddr(a) for a in getaddresses([to]))
        msg['Cc'] = ', '.join(formataddr(a) for a in getaddresses([cc]))
        msg['Subject'] = Header(subject, 'utf-8')

        to_addrs = list(filter(None, map(
                    str.strip,
                    _get_emails(to) + _get_emails(cc) + _get_emails(bcc))))
        sendmail_transactional(
            from_, to_addrs, msg, datamanager=SMTPDataManager(strict=True))

        email = cls(
            recipients=to,
            recipients_secondary=cc,
            recipients_hidden=bcc,
            addresses=[{'address': a} for a in to_addrs],
            subject=subject,
            body=body,
            resource=record)
        email.save()
        with Transaction().set_context(_check_access=False):
            attachments_ = []
            for name, data in attachments:
                attachments_.append(
                    Attachment(resource=email, name=name, data=data))
            Attachment.save(attachments_)
        return email
Пример #39
0
 def emails_parsed(self):
     return [(n, a) for n, a in getaddresses([self.emails]) if a]
Пример #40
0
  def process_headers( self, msg ):
    headers = {}
    # for now we just take todays date as the received date
    message = { "receivedDate" : datetime.datetime.utcnow().isoformat() }

    for hn in msg.keys():
      header_values = msg.get_all(hn)
      if header_values:
        header_name = hn.lower()
        # add this header to the list of available headers
        headers[header_name] = []

        # do any charset etc conversion on the values...
        header_values = [self._safe_convert_header(v) for v in header_values]

        # go through the values converting them into usable lists
        for value in header_values:
          if re.match(r"<.+>,",value):
            for v in value.split(","):
              headers[header_name].append(unquote(v.strip()))
          # multiple reference processing
          elif header_name == "references" and re.match(r"<[^<>]+>\s+",value):
            for ref in re.findall(r"<[^<>]+>",value):
              headers[header_name].append(unquote(ref.strip()))
          else:
            headers[header_name].append(unquote(value.strip()))

    for header_name in headers:
      header_values = headers[header_name]
      if header_name in ["to","cc", "bcc", "from", "replyto"]:
        message[header_name] = [{ "name" : name, "address" : address} \
                                  for name, address \
                                  in getaddresses(header_values) \
                                  if address]
      elif header_name == "received":
        dv = 0
        for v in header_values:
          date = re.match(r".*;\s*(.+)",v,re.DOTALL).group(1)
          parse = int(mktime_tz(parsedate_tz(date)))
          if parse > dv:
            dv = parse
            rd = formatdate(parse)
            message["receivedDate"] = { "original" : rd, 
                                        "utctimestamp" : parse,
                                        "utcisoformat" : datetime.datetime.fromtimestamp(parse, tzutc()).isoformat() }

      elif header_name in ["message-id"]:
        # single value header
        value = header_values[0]
        message["mid"] = value

      elif header_name in ["subject"]:
        # single value header
        value = header_values[0]
        message["subject"] = value

      elif header_name in ["date"]:
        # single value header
        value = header_values[0]
        utctimestamp = int(mktime_tz(parsedate_tz(value)))
        timestamp = datetime.datetime.fromtimestamp(utctimestamp, tzutc())
        message["date"] = { "original" : value, 
                            "utctimestamp" : utctimestamp, 
                            "utcisoformat" : timestamp.isoformat() }

    return message
Пример #41
0
TEMPLATE_CONTEXT_PROCESSORS = DEFAULT_SETTINGS.TEMPLATE_CONTEXT_PROCESSORS + (
    'django.core.context_processors.request',
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.static',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'django.core.context_processors.static',
)

#WSGI_APPLICATION = 'opensr.wsgi.application'

# Authentication

ADMINS = getaddresses([env('ADMINS')])

MANAGERS = ADMINS

# Database

DATABASES = {
    'default': env.db(),
}

# Internationalization

TIME_ZONE = 'America/New_York'

LANGUAGE_CODE = 'en-us'
Пример #42
0
 def get_identities(self, values):
     values = [v for v in ensure_list(values) if v is not None]
     for (name, email) in getaddresses(values):
         yield EmailIdentity(self.manager, name, email)
Пример #43
0
# AUTHENTICATION
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends
AUTHENTICATION_BACKENDS = (
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend",
)
AUTH_USER_MODEL = "users.User"
LOGIN_REDIRECT_URL = "/"

# ADMIN
# ------------------------------------------------------------------------------
ADMIN_URL = "admin/"
# https://docs.djangoproject.com/en/dev/ref/settings/#admins
ADMINS = getaddresses(
    [env("DJANGO_ADMINS", default="zmrenwu <*****@*****.**>")])
# https://docs.djangoproject.com/en/dev/ref/settings/#managers
MANAGERS = ADMINS

# EMAIL
# -----------------------------------------------------------------
SERVER_EMAIL = env.str("DJANGO_SERVER_EMAIL", default="*****@*****.**")
# https://docs.djangoproject.com/en/dev/ref/settings/#email-timeout
EMAIL_TIMEOUT = 5
# https://docs.djangoproject.com/en/dev/ref/settings/#email-backend
email_backend = env.str("DJANGO_EMAIL_BACKEND", default="console")
if email_backend == "console":
    EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
elif email_backend == "smtp":
    EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
elif email_backend == "file":
Пример #44
0
def main(args=sys.argv):
    parser = option_parser()
    opts, args = parser.parse_args(args)

    if len(args) > 1:
        if len(args) < 4:
            print(
                'You must specify the from address, to address and body text'
                ' on the command line')
            return 1
        msg = compose_mail(args[1],
                           args[2],
                           args[3],
                           subject=opts.subject,
                           attachment=opts.attachment)
        from_, to = args[1:3]
        eto = [extract_email_address(x.strip()) for x in to.split(',')]
        efrom = extract_email_address(from_)
    else:
        msg = sys.stdin.read()
        from email import message_from_string
        from email.utils import getaddresses
        eml = message_from_string(msg)
        tos = eml.get_all('to', [])
        ccs = eml.get_all('cc', []) + eml.get_all('bcc', [])
        eto = [x[1] for x in getaddresses(tos + ccs) if x[1]]
        if not eto:
            raise ValueError(
                'Email from STDIN does not specify any recipients')
        efrom = getaddresses(eml.get_all('from', []))
        if not efrom:
            raise ValueError('Email from STDIN does not specify a sender')
        efrom = efrom[0][1]

    outbox = None
    if opts.outbox is not None:
        outbox = os.path.abspath(os.path.expanduser(opts.outbox))
        from mailbox import Maildir
        outbox = Maildir(opts.outbox, factory=None)
    if opts.fork:
        if os.fork() != 0:
            return 0

    try:
        sendmail(msg,
                 efrom,
                 eto,
                 localhost=opts.localhost,
                 verbose=opts.verbose,
                 timeout=opts.timeout,
                 relay=opts.relay,
                 username=opts.username,
                 password=opts.password,
                 port=opts.port,
                 encryption=opts.encryption_method)
    except:
        if outbox is not None:
            outbox.add(msg)
            outbox.close()
            print 'Delivery failed. Message saved to', opts.outbox
        raise
    return 0
Пример #45
0
def create_object_from_email_message(message, ticket_id, payload, files, logger):

    ticket, previous_followup, new = None, None, False
    now = timezone.now()

    queue = payload['queue']
    sender_email = payload['sender_email']

    to_list = getaddresses(message.get_all('To', []))
    cc_list = getaddresses(message.get_all('Cc', []))

    message_id = message.get('Message-Id')
    in_reply_to = message.get('In-Reply-To')

    if in_reply_to is not None:
        try:
            queryset = FollowUp.objects.filter(message_id=in_reply_to).order_by('-date')
            if queryset.count() > 0:
                previous_followup = queryset.first()
                ticket = previous_followup.ticket
        except FollowUp.DoesNotExist:
            pass  # play along. The header may be wrong

    if previous_followup is None and ticket_id is not None:
        try:
            ticket = Ticket.objects.get(id=ticket_id)
        except Ticket.DoesNotExist:
            ticket = None
        else:
            new = False
            # Check if the ticket has been merged to another ticket
            if ticket.merged_to:
                logger.info("Ticket has been merged to %s" % ticket.merged_to.ticket)
                # Use the ticket in which it was merged to for next operations
                ticket = ticket.merged_to

    # New issue, create a new <Ticket> instance
    if ticket is None:
        if not settings.QUEUE_EMAIL_BOX_UPDATE_ONLY:
            ticket = Ticket.objects.create(
                title=payload['subject'],
                queue=queue,
                submitter_email=sender_email,
                created=now,
                description=payload['body'],
                priority=payload['priority'],
            )
            ticket.save()
            logger.debug("Created new ticket %s-%s" % (ticket.queue.slug, ticket.id))

            new = True

    # Old issue being re-opened
    elif ticket.status == Ticket.CLOSED_STATUS:
        ticket.status = Ticket.REOPENED_STATUS
        ticket.save()

    f = FollowUp(
        ticket=ticket,
        title=_('E-Mail Received from %(sender_email)s' % {'sender_email': sender_email}),
        date=now,
        public=True,
        comment=payload['body'],
        message_id=message_id
    )

    if ticket.status == Ticket.REOPENED_STATUS:
        f.new_status = Ticket.REOPENED_STATUS
        f.title = _('Ticket Re-Opened by E-Mail Received from %(sender_email)s' % {'sender_email': sender_email})

    f.save()
    logger.debug("Created new FollowUp for Ticket")

    logger.info("[%s-%s] %s" % (ticket.queue.slug, ticket.id, ticket.title,))

    attached = process_attachments(f, files)
    for att_file in attached:
        logger.info(
            "Attachment '%s' (with size %s) successfully added to ticket from email.",
            att_file[0], att_file[1].size
        )

    context = safe_template_context(ticket)

    new_ticket_ccs = []
    new_ticket_ccs.append(create_ticket_cc(ticket, to_list + cc_list))

    notifications_to_be_sent = [sender_email]

    if queue.enable_notifications_on_email_events and len(notifications_to_be_sent):

        ticket_cc_list = TicketCC.objects.filter(ticket=ticket).all().values_list('email', flat=True)

        for email_address in ticket_cc_list:
            notifications_to_be_sent.append(email_address)

    # send mail to appropriate people now depending on what objects
    # were created and who was CC'd
    if new:
        ticket.send(
            {'submitter': ('newticket_submitter', context),
             'new_ticket_cc': ('newticket_cc', context),
             'ticket_cc': ('newticket_cc', context)},
            fail_silently=True,
            extra_headers={'In-Reply-To': message_id},
        )
    else:
        context.update(comment=f.comment)
        ticket.send(
            {'submitter': ('newticket_submitter', context),
             'assigned_to': ('updated_owner', context)},
            fail_silently=True,
            extra_headers={'In-Reply-To': message_id},
        )
        if queue.enable_notifications_on_email_events:
            ticket.send(
                {'ticket_cc': ('updated_cc', context)},
                fail_silently=True,
                extra_headers={'In-Reply-To': message_id},
            )

    return ticket
Пример #46
0
 def __get_decoded_addresses(self, address, charset):
     result = []
     for name, email in getaddresses([address]):
         result.append(
             (self.__decode(name, charset), self.__decode(email, charset)))
     return result
Пример #47
0
 def emails_formatted(self):
     return [
         formataddr((n, a)) for n, a in getaddresses([self.emails]) if a
     ]
Пример #48
0
def main():
    fp = open("/tmp/mail.log", "a")
    #fp.write("The file is " + sys.argv[1] + "\n")
    try:
        with open(sys.argv[1], 'rU') as email_fp:
            msg = email.message_from_file(email_fp)
    except Exception as errMess:
        fp.write("Failed to read e-mail message: " + str(errMess) + "\n")
        sys.exit("Failed to read e-mail message")
    raw_date = msg.get('Date', msg.get('Resent-Date', None))
    addr_return_path = msg.get('Return-path', None)
    addr_reply_to = msg.get('Reply-to', None)
    addr_to = msg.get('Envelope-to', None)
    addr_from = msg.get('From', msg.get('Sender', None))
    subject = msg.get('Subject', None)
    fp.write("Message to " + str(addr_to) + "\n")
    #fp.write("From was " + str(addr_from) + "\n")
    #fp.write("Subject was " + str(subject) + "\n")
    to_recipients = list()
    for recipient in getaddresses(msg.get_all('to', []) + msg.get_all('resent-to', [])):
        to_recipients.append(dict(name=recipient[0], address=recipient[1]))
    cc_recipients = list()
    for recipient in getaddresses(msg.get_all('cc', []) + msg.get_all('resent-cc', [])):
        cc_recipients.append(dict(name=recipient[0], address=recipient[1]))
    recipients = list()
    for recipient in getaddresses(msg.get_all('to', []) + msg.get_all('cc', []) + msg.get_all('resent-to', []) + msg.get_all('resent-cc', [])):
        recipients.append(dict(name=recipient[0], address=recipient[1]))
    if addr_to is None and len(recipients):
        addr_to = recipients[0]['address']
    #fp.write("recipients are " + str(recipients) + "\n")
    if addr_to is not None:
        #fp.write("parsed envelope-to: " + str(parseaddr(addr_to)) + "\n")
        short_code = re.sub(r'@.*', '', parseaddr(addr_to)[1])
    else:
        short_code = None
    #fp.write("short code is " + str(short_code) + "\n")
    record = db.session.query(Shortener).filter_by(short=short_code).first()
    if record is None:
        fp.write("short code not found\n")
        sys.exit("short code not found")
        #fp.write("short code found\n")
    #file_number = get_new_file_number(record.uid, 'email', yaml_file_name=record.filename)
    ##fp.write("file number is " + str(file_number) + "\n")
    #saved_file_email = SavedFile(file_number, fix=True)
    if addr_from is not None:
        #fp.write("parsed from: " + str(parseaddr(addr_from)[1]) + "\n")
        addr_from = dict(name=parseaddr(addr_from)[0], address=parseaddr(addr_from)[1])
    else:
        addr_from = dict(empty=True)
    if addr_return_path is not None:
        #fp.write("parsed return_path: " + str(parseaddr(addr_return_path)[1]) + "\n")
        addr_return_path = dict(name=parseaddr(addr_return_path)[0], address=parseaddr(addr_return_path)[1])
    else:
        addr_return_path = dict(empty=True)
    #fp.write("return_path is " + str(addr_return_path) + "\n")
    if addr_reply_to is not None:
        #fp.write("parsed reply-to: " + str(parseaddr(addr_reply_to)[1]) + "\n")
        addr_reply_to = dict(name=parseaddr(addr_reply_to)[0], address=parseaddr(addr_reply_to)[1])
        #fp.write("reply-to is " + str(addr_reply_to) + "\n")
    else:
        addr_reply_to = dict(empty=True)
    #fp.write("reply-to is " + str(addr_reply_to) + "\n")
    msg_current_time = datetime.datetime.now()
    if raw_date is not None:
        msg_date = datetime.datetime.fromtimestamp(mktime(parsedate(raw_date)))
        #fp.write("msg_date is " + str(msg_date) + "\n")
    else:
        msg_date = msg_current_time
        #fp.write("msg_date set to current time\n")
    headers = list()
    for item in msg.items():
        headers.append([item[0], item[1]])
    #fp.write("headers:\n" + json.dumps(headers) + "\n")
    
    email_record = Email(short=short_code, to_addr=json.dumps(to_recipients), cc_addr=json.dumps(cc_recipients), from_addr=json.dumps(addr_from), reply_to_addr=json.dumps(addr_reply_to), return_path_addr=json.dumps(addr_return_path), subject=subject, datetime_message=msg_date, datetime_received=msg_current_time)
    db.session.add(email_record)
    db.session.commit()

    save_attachment(record.uid, record.filename, 'headers.json', email_record.id, 0, 'application/json', 'json', json.dumps(headers))
    
    counter = 1
    for part in msg.walk():
        if part.get_content_maintype() == 'multipart':
            continue
        filename = part.get_filename()
        if part.get_content_type() == 'text/plain':
            ext = '.txt'
        else:
            ext = mimetypes.guess_extension(part.get_content_type())
        if not ext:
            ext = '.bin'
        if filename:
            filename = '%03d-%s' % (counter, secure_filename(filename))
        else:
            filename = '%03d-attachment%s' % (counter, ext)
        #fp.write("Filename is " + str(filename) + "\n")
        #fp.write("Content type is " + str(part.get_content_type()) + "\n")

        real_filename = re.sub(r'[0-9][0-9][0-9]-', r'', filename)
        real_ext = re.sub(r'^\.', r'', ext)
        save_attachment(record.uid, record.filename, real_filename, email_record.id, counter, part.get_content_type(), real_ext, part.get_payload(decode=True))
        
        counter += 1
    fp.close()
    user = None
    if record.user_id is not None:
        user = db.session.query(UserModel).filter_by(id=record.user_id).first()
    if user is None:
        user_info = dict(email=None, the_user_id='t' + str(record.temp_user_id), theid=record.temp_user_id, roles=list())
    else:
        user_info = dict(email=user.email, roles=[role.name for role in user.roles], the_user_id=user.id, theid=user.id, firstname=user.first_name, lastname=user.last_name, nickname=user.nickname, country=user.country, subdivisionfirst=user.subdivisionfirst, subdivisionsecond=user.subdivisionsecond, subdivisionthird=user.subdivisionthird, organization=user.organization)
    result = docassemble.webapp.worker.background_action.delay(record.filename, user_info, record.uid, None, 'http://localhost', 'http://localhost', dict(action='incoming_email', arguments=dict(id=email_record.id)), extra=None)
Пример #49
0
BASE_DOMAIN = HOSTNAME = socket.gethostname().lower()
if 'ALLOWED_HOSTS' in os.environ and os.environ['ALLOWED_HOSTS'].strip():
    hosts = os.environ['ALLOWED_HOSTS'].split(" ")
    BASE_HOST = hosts[0]
    BASE_URL = "https://" + BASE_HOST
    ALLOWED_HOSTS = [host.strip() for host in hosts if host.strip()]

SECURE_SSL_REDIRECT = env.bool('SECURE_SSL_REDIRECT', default=False)

ADMINS = (("Administrator",
           "{domain} admin <admin@{domain}>".format(domain=BASE_DOMAIN)), )
if 'ADMINS' in os.environ:
    from email.utils import getaddresses

    admins = os.environ['ALLOWED_HOSTS'].split(";")
    addreses = getaddresses(admins)
    ADMINS = [(name, named_email)
              for ((name, email), named_email) in zip(addreses, admins)]

DEFAULT_FROM_EMAIL = env(
    'DEFAULT_FROM_EMAIL',
    default="Help <help@{domain}>".format(domain=BASE_DOMAIN))
HELP_EMAIL = env('HELP_EMAIL', default=DEFAULT_FROM_EMAIL)
ERR_EMAIL = env('ERR_EMAIL',
                default="errors@{domain}".format(domain=BASE_DOMAIN))
SERVER_EMAIL = env(
    'SERVER_EMAIL',
    default="Errors <errors@{domain}>".format(domain=BASE_DOMAIN))
EMAIL_SUBJECT_PREFIX = env('EMAIL_SUBJECT_PREFIX', default='[OPT-OUT] ')

# Database
Пример #50
0
    emails = []

    output = None
    if args.output:
        if args.output not in ['json', 'txt']:
            print(bad + " Output should be json or txt" + end)
            exit(-1)
        output = args.output

    if args.target:
        emails.append(args.target)

    if args.list:
        try:
            lines = open(args.list).readlines()
            for line in lines:
                for input in line.split(','):
                    addresses = getaddresses([input])
                    for address in addresses:
                        emails.append(str(addresses[0][1]).strip())
        except Exception as e:
            print(bad + " Can't read the file: " + str(args.list))
            exit(-1)
    try:
        main(emails, output)
    except ConnectionError:
        print(bad +
              " Can't connect to service! restart tor service and try again.")
    except Exception as e:
        print(bad + " " + e)
Пример #51
0
def post_send_handler(
    message: EmailMessage, status: AnymailStatus, esp_name: str, **_unused
):
    """
    Add sent messages to their corresponding thread
    """
    assert esp_name == "Mailgun"

    # Parse the emails
    [(from_name, from_email)] = getaddresses([message.from_email])
    [(_, recipient_email), *_] = getaddresses(message.to)

    # Prevent reset emails from being stored
    if from_email == settings.DEFAULT_FROM_EMAIL:
        return

    # Get the HTML message if it exists
    html = None
    if isinstance(message, EmailMultiAlternatives):
        # Get the html content
        for data, content_type in message.alternatives:
            if content_type == "text/html":
                html = data

    # Set the html content if there's nothing
    if html is None:
        html = f"<pre>{message.body}</pre>"

    # Extract data from the message
    sent = Message(
        type=MessageType.OUTGOING,
        sender_email=from_email,
        recipient_email=recipient_email,
        from_name=from_name,
        from_email=from_email,
        to=", ".join(message.to),
        cc=", ".join(message.cc),
        subject=message.subject,
        timestamp=timezone.now(),
        text=message.body,
        html=html,
        message_id=status.message_id,
        status=MessageStatus.PENDING,
    )

    # Save message to database
    sent.save()

    # Attempt to associate with existing thread
    associate_with_thread(
        sent,
        message.extra_headers.get("in-reply-to")
        or message.extra_headers.get("In-Reply-To"),
    )

    # Extract attachments
    for attachment in message.attachments:
        # Extract the attachment details
        # The MIME type can be trusted since it was already sniffed by the handler
        name, content, mime = attachment

        # Get only the filename, not the path
        sanitized_name = Path(name).name

        # Create a temporary file to be uploaded
        temp = TemporaryUploadedFile(name, mime, len(content), "utf-8")
        if type(content) == str:
            temp.write(content.encode())
        else:
            temp.write(content)

        # Add it to the message
        sent.attachment_set.create(
            name=sanitized_name,
            content_type=mime,
            inline=False,
            content=temp,
        )
Пример #52
0
    def _do_send(self, transport, event, message, cc_addrs, bcc_addrs):
        notify_sys = NotificationSystem(self.env)
        smtp_from = notify_sys.smtp_from
        smtp_from_name = notify_sys.smtp_from_name or self.env.project_name
        smtp_replyto = notify_sys.smtp_replyto
        if not notify_sys.use_short_addr and notify_sys.smtp_default_domain:
            if smtp_from and '@' not in smtp_from:
                smtp_from = '%s@%s' % (smtp_from,
                                       notify_sys.smtp_default_domain)
            if smtp_replyto and '@' not in smtp_replyto:
                smtp_replyto = '%s@%s' % (smtp_replyto,
                                          notify_sys.smtp_default_domain)

        headers = {}
        headers['X-Mailer'] = 'Trac %s, by Edgewall Software'\
                              % self.env.trac_version
        headers['X-Trac-Version'] = self.env.trac_version
        headers['X-Trac-Project'] = self.env.project_name
        headers['X-URL'] = self.env.project_url
        headers['X-Trac-Realm'] = event.realm
        headers['Precedence'] = 'bulk'
        headers['Auto-Submitted'] = 'auto-generated'
        if isinstance(event.target, (list, tuple)):
            targetid = ','.join(map(get_target_id, event.target))
        else:
            targetid = get_target_id(event.target)
        rootid = create_message_id(self.env,
                                   targetid,
                                   smtp_from,
                                   None,
                                   more=event.realm)
        if event.category == 'created':
            headers['Message-ID'] = rootid
        else:
            headers['Message-ID'] = create_message_id(self.env,
                                                      targetid,
                                                      smtp_from,
                                                      event.time,
                                                      more=event.realm)
            headers['In-Reply-To'] = rootid
            headers['References'] = rootid
        headers['Date'] = formatdate()
        headers['From'] = (smtp_from_name, smtp_from) \
                          if smtp_from_name else smtp_from
        headers['To'] = 'undisclosed-recipients: ;'
        if cc_addrs:
            headers['Cc'] = ', '.join(cc_addrs)
        if bcc_addrs:
            headers['Bcc'] = ', '.join(bcc_addrs)
        headers['Reply-To'] = smtp_replyto

        for k, v in headers.iteritems():
            set_header(message, k, v, self._charset)
        for decorator in self.decorators:
            decorator.decorate_message(event, message, self._charset)

        from_name, from_addr = parseaddr(str(message['From']))
        to_addrs = set()
        for name in ('To', 'Cc', 'Bcc'):
            values = map(str, message.get_all(name, ()))
            to_addrs.update(addr for name, addr in getaddresses(values)
                            if addr)
        del message['Bcc']
        notify_sys.send_email(from_addr, list(to_addrs), message.as_string())
Пример #53
0
def handle_message(mlist, id, action, comment=None, forward=None):
    message_store = getUtility(IMessageStore)
    requestdb = IListRequests(mlist)
    key, msgdata = requestdb.get_request(id)
    # Handle the action.
    rejection = None
    message_id = msgdata['_mod_message_id']
    sender = msgdata['_mod_sender']
    subject = msgdata['_mod_subject']
    keep = False
    if action in (Action.defer, Action.hold):
        # Nothing to do, but preserve the message for later.
        keep = True
    elif action is Action.discard:
        rejection = 'Discarded'
    elif action is Action.reject:
        rejection = 'Refused'
        member = mlist.members.get_member(sender)
        if member:
            language = member.preferred_language
        else:
            language = None
        send_rejection(mlist, _('Posting of your message titled "$subject"'),
                       sender, comment or _('[No reason given]'), language)
    elif action is Action.accept:
        # Start by getting the message from the message store.
        msg = message_store.get_message_by_id(message_id)
        # Delete moderation-specific entries from the message metadata.
        for key in list(msgdata):
            if key.startswith('_mod_'):
                del msgdata[key]
        # Add some metadata to indicate this message has now been approved.
        msgdata['approved'] = True
        msgdata['moderator_approved'] = True
        # Calculate a new filebase for the approved message, otherwise
        # delivery errors will cause duplicates.
        if 'filebase' in msgdata:
            del msgdata['filebase']
        # Queue the file for delivery.  Trying to deliver the message directly
        # here can lead to a huge delay in web turnaround.  Log the moderation
        # and add a header.
        msg['X-Mailman-Approved-At'] = formatdate(time.mktime(
            now().timetuple()),
                                                  localtime=True)
        vlog.info('held message approved, message-id: %s',
                  msg.get('message-id', 'n/a'))
        # Stick the message back in the incoming queue for further
        # processing.
        config.switchboards['pipeline'].enqueue(msg, _metadata=msgdata)
    else:
        raise AssertionError('Unexpected action: {0}'.format(action))
    # Forward the message.
    if forward:
        # Get a copy of the original message from the message store.
        msg = message_store.get_message_by_id(message_id)
        # It's possible the forwarding address list is a comma separated list
        # of display_name/address pairs.
        addresses = [addr[1] for addr in getaddresses(forward)]
        language = mlist.preferred_language
        if len(addresses) == 1:
            # 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.  This is better than sending a
            # separate message per recipient.
            member = mlist.members.get_member(addresses[0])
            if member:
                language = member.preferred_language
        with _.using(language.code):
            fmsg = UserNotification(addresses,
                                    mlist.bounces_address,
                                    _('Forward of moderated message'),
                                    lang=language)
        fmsg.set_type('message/rfc822')
        fmsg.attach(msg)
        fmsg.send(mlist)
    # Delete the request if it's not being kept.
    if not keep:
        requestdb.delete_request(id)
    # Log the rejection
    if rejection:
        note = """%s: %s posting:
\tFrom: %s
\tSubject: %s"""
        if comment:
            note += '\n\tReason: ' + comment
        vlog.info(note, mlist.fqdn_listname, rejection, sender, subject)
Пример #54
0
def lambda_handler(event, context, debug=None):

    print('Parsing Email at time:\n{0}'.format(time.time()))

    if not debug:
        record = event['Records'][0]
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
    else:
        # debug is in the form of (bucket, key)
        bucket = debug[0]
        key = debug[1]
    s3_client = boto3.client('s3')
    response = s3_client.get_object(Bucket=bucket, Key=key)
    msg = message_from_bytes(response['Body'].read())
    # only want the first address, even if there are multiple
    recipient = getaddresses(msg.get_all('to', []))[0][1]
    sender = parseaddr(msg['from'])
    subject = msg['subject']
    body = ''
    if msg.is_multipart():
        print('Message is MultiPart')
        for payload in msg.get_payload():
            if payload and payload.get_payload(decode=True).decode('utf8'):
                body += payload.get_payload(decode=True).decode('utf8')
    else:
        payload = msg.get_payload(decode=True)
        if payload:
            body += payload.decode()
    if body:
        email_soup = BeautifulSoup(body, 'lxml')
    else:
        return 1
    print('From: {} <{}>'.format(sender[0], sender[1]))
    print('Subject:', subject)
    print('To:', recipient)
    from_key = '{}/{}'.format(bucket, key)
    # check if this is a giveaway win notification
    if sender[
            1] == '*****@*****.**' or sender == '*****@*****.**'[
                1]:
        from_email = 'AWS Win Notification <SES VERIFIED FROM EMAIL>'
        forward_emails = ['<SES VERIFIED TO EMAIL>']
        email_client = boto3.client('ses')
        # Add the recipient email as the text so it's easier to find out who the email was originally sent to if it is not immediately clear
        send_response = send_mail(
            sender=from_email,
            recipients=forward_emails,
            title=subject,
            html=body,
            text=recipient,
        )
        to_key = 'wins/{email}/{time:.2f}'.format(email=recipient,
                                                  time=time.time())
        s3_client.copy_object(Bucket=bucket, CopySource=from_key, Key=to_key)
        s3_client.delete_object(Bucket=bucket, Key=key)
        print('Moved email from {} to {}'.format(from_key, to_key))
        if 'MessageId' in send_response:
            if len(forward_emails) == 1:
                forward_emails = forward_emails[0]
            print(
                'Sent email from {from_email} to {to_email}. Message ID: {id}'.
                format(from_email=from_email,
                       to_email=forward_emails,
                       id=send_response['MessageId']))
            return 0
        else:
            print('Error sending email')
            return 1
    otp = None
    try:
        otp = email_soup.find_all('p', class_="otp")
        assert len(otp) == 1  # should only be one of these items (as of now)
        otp = otp[0].get_text()
        print('Found OTP:', otp)
    except:
        print('Could not find OTP')
    if otp:
        to_key = 'sorted/{email}/{time:.2f}-{OTP}'.format(email=recipient,
                                                          time=time.time(),
                                                          OTP=otp)
        s3_client.copy_object(Bucket=bucket, CopySource=from_key, Key=to_key)
    elif 'Per your request, we have updated your mobile phone information' not in body:
        # Send the email if it was not spam, not an OTP, not a phone change notification, and not a giveaway win...could be something important
        from_email = 'AWS Unknown Email <SES VERIFIED FROM EMAIL>'
        forward_emails = ['<SES VERIFIED TO EMAIL>']
        send_response = send_mail(
            sender=from_email,
            recipients=forward_emails,
            title=subject,
            html=body,
            text=recipient,
        )
        to_key = 'sorted/{email}/{time:.2f}'.format(email=recipient,
                                                    time=time.time())
        s3_client.copy_object(Bucket=bucket, CopySource=from_key, Key=to_key)
    s3_client.delete_object(Bucket=bucket, Key=key)
    print('Moved email from {} to {}'.format(from_key, to_key))
    return 0
Пример #55
0
    def send(self):
        """Perform all send operations related to this email...

        These consists in:
            - send the notification email;
            - call self.filer_cmd if not None.

        REMARKS
            If the GIT_HOOKS_TESTSUITE_MODE environment variable
            is set, then a trace of the email is printed, instead
            of sending it.  This is for testing purposes.
        """
        # Force the charset being used to UTF-8. We could possibly try
        # to guess whether more primitive charsets might work such as
        # ASCII or IS0-8859-15, but UTF-8 is so close to those encodings
        # that it is not worth the extra complication.
        #
        # The one situation where it might be worth guessing the charset
        # is when the email body contains some characters which are not
        # available in UTF-8. Since UTF-8 is so widely used, we'll assume
        # for now that it's not necessary in practice to support this
        # scenario.
        e_msg_charset = Charset("UTF-8")

        # Force quoted-printable encoding for our emails.
        #
        # Using this encoding helps ensure that the email payload
        # does not exceed any of the limitations that SMTP servers
        # might have. In particular, while RFC 6152 now defines
        # the "8bit" Content-Transfer-Encoding as being a legal
        # extension, it also warns us of some limitations:
        #
        #        | Note that this extension does NOT eliminate
        #        | the possibility of an SMTP server limiting line
        #        | length; servers are free to implement this extension
        #        | but nevertheless set a line length limit no lower
        #        | than 1000 octets.
        #
        # We also prefer the quoted-printable encoding over the base64
        # one because:
        #
        #    - The output that's generally easier for humans to read';
        #    - The output is also usually smaller in size for typical
        #      text.
        e_msg_charset.body_encoding = QP

        e_msg_body = self.__email_body_with_diff

        # Handle the situation where we were manually called by a user
        # (as opposed by Git itself) who would like us to re-send the emails,
        # with a warning banner indicating that the emails were re-generated.
        # The banner is added at the beginning of the email body.
        #
        # The main reason for adding the banner is that it helps prevent users
        # from thinking the commit was pushed at the time the email was sent.
        #
        # Note that the option of allowing the user to use the banner
        # of his choice was considered. In the end, we decided against it
        # for now, because we wanted it to be very simple for the user
        # to trigger the addition of the banner during the re-send,
        # while at the same time keeping the code in the git-hooks
        # as simple as possible also (i.e. we avoided the introduction
        # of multiple environment variables, for instance).

        manual_replay_reason = os.environ.get("GIT_HOOKS_EMAIL_REPLAY_REASON")
        if manual_replay_reason is not None:
            warning_banner = EMAIL_REPLAY_WARNING_BANNER.format(
                reason=manual_replay_reason)
            e_msg_body = warning_banner + "\n" + e_msg_body

        e_msg = MIMEText(e_msg_body, _charset=e_msg_charset)

        # Create the email's header.
        e_msg["From"] = sanitized_email_address(self.email_info.email_from)
        e_msg["To"] = ", ".join(map(sanitized_email_address, self.email_to))
        if self.email_bcc:
            e_msg["Bcc"] = ", ".join(
                map(sanitized_email_address, self.email_bcc))
        e_msg["Subject"] = sanitized_email_header_field(self.email_subject)
        e_msg["X-Act-Checkin"] = self.email_info.project_name
        e_msg["X-Git-Author"] = sanitized_email_address(
            self.author or self.email_info.email_from)
        e_msg["X-Git-Refname"] = self.ref_name
        e_msg["X-Git-Oldrev"] = self.old_rev
        e_msg["X-Git-Newrev"] = self.new_rev

        # email_from = e_msg.get('From')
        email_recipients = [
            addr[1] for addr in getaddresses(
                e_msg.get_all("To", []) + e_msg.get_all("Cc", []) +
                e_msg.get_all("Bcc", []))
        ]

        sendmail(
            self.email_info.email_from,
            email_recipients,
            e_msg.as_string(),
            "localhost",
        )

        if self.filer_cmd is not None:
            self.__call_filer_cmd()
Пример #56
0
EMAIL_HOST = env("EMAIL_HOST", default="localhost")
EMAIL_PORT = env.int("EMAIL_PORT", default=25)
EMAIL_BACKEND = "djcelery_email.backends.CeleryEmailBackend"

ATOMIC_REQUESTS = True

ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])

# configure internal IPS inside docker container

INTERNAL_IPS = [
    ip[:-1] + "1" for ip in socket.gethostbyname_ex(socket.gethostname())[2]
]

ADMINS = getaddresses(env.list("ADMINS", default=[]))

SESSION_COOKIE_DOMAIN = env("SESSION_COOKIE_DOMAIN", default=None)
CSRF_COOKIE_DOMAIN = env("CSRF_COOKIE_DOMAIN", default=None)
CSRF_TRUSTED_ORIGINS = env.list("CSRF_TRUSTED_ORIGINS", default=[])

ROOT_URLCONF = "conduit.config.urls"
WSGI_APPLICATION = "conduit.config.wsgi.application"

LOCAL_APPS = [
    "conduit.articles.apps.ArticlesConfig",
    "conduit.users.apps.UsersConfig",
]


INSTALLED_APPS = [
Пример #57
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']

    # 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
    # Process the headers in order of importance: if a mail was sent with
    # account X, with account Y in e.g. CC or delivered-to, make sure that
    # account X is the one selected and not account Y.
    candidate_headers = settings.get("reply_account_header_priority")
    for candidate_header in candidate_headers:
        candidate_addresses = getaddresses(mail.get_all(candidate_header, []))

        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 = [
                re.escape(unicode(a)) for a in account.get_addresses()
            ]
            if account.alias_regexp is not None:
                acc_addresses.append(account.alias_regexp)
            for alias in acc_addresses:
                regex = re.compile(u'^' + unicode(alias) + u'$',
                                   flags=re.IGNORECASE if
                                   not account.address.case_sensitive else 0)
                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

                        logging.debug('using realname: "%s"', realname)
                        logging.debug('using address: %s', address)

                        from_value = formataddr((realname, address))
                        return from_value, account

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

    from_value = formataddr((realname, address))
    return from_value, account
Пример #58
0
from .common import (DATABASES, INSTALLED_APPS, MIDDLEWARE_CLASSES,
                     REST_FRAMEWORK, TEMPLATES, env)

# SITE CONFIGURATION
# Hosts/domain names that are valid for this site.
# "*" matches anything, ".example.com" matches example.com and all subdomains
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
ALLOWED_HOSTS = ["*"]

SITE_SCHEME = env('SITE_SCHEME', default='https')

# MANAGER CONFIGURATION
# ------------------------------------------------------------------------------
# People who get code error notifications.
# In the format 'Full Name <*****@*****.**>, Full Name <*****@*****.**>'
ADMINS = getaddresses([env("DJANGO_ADMINS")])

# Not-necessarily-technical managers of the site. They get broken link
# notifications and other various emails.
MANAGERS = ADMINS

# DJANGO_SITES
# ------------------------------------------------------------------------------
# see: http://niwinz.github.io/django-sites/latest/
SITES['remote'] = {  # noqa: F405
    "domain": env('SITE_DOMAIN'),
    "scheme": SITE_SCHEME,
    "name": env('SITE_NAME'),
}
SITE_ID = env("DJANGO_SITE_ID", default='remote')
Пример #59
0
 def _filter_alias(email):
     email_wn = getaddresses([email])[0][1]
     if email_wn not in aliases:
         return email_wn
Пример #60
0
def _get_emails(value):
    "Return list of email from the comma separated list"
    return [e for n, e in getaddresses([value]) if e]