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
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)
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
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
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 }
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 })
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'])
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
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
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))
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
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:|>')
def add_to_toc(self, msg, count): """Add a message to the table of contents.""" subject = msg.get('subject', _('(no subject)')) subject = oneline(subject, in_unicode=True) # Don't include the redundant subject prefix in the toc mo = re.match('(re:? *)?({0})'.format( re.escape(self._mlist.subject_prefix)), subject, re.IGNORECASE) if mo: subject = subject[:mo.start(2)] + subject[mo.end(2):] # Take only the first author we find. username = '' addresses = getaddresses( [oneline(msg.get('from', ''), in_unicode=True)]) if addresses: username = addresses[0][0] if not username: username = addresses[0][1] if username: username = '******'.format(username) lines = wrap('{0:2}. {1}'. format(count, subject), 65).split('\n') # See if the user's name can fit on the last line if len(lines[-1]) + len(username) > 70: lines.append(username) else: lines[-1] += username # Add this subject to the accumulating topics first = True for line in lines: if first: print(' ', line, file=self._toc) first = False else: print(' ', line.lstrip(), file=self._toc)
def 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"))
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())
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']
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
def load(value): if not value: return None if isinstance(value, six.string_types): return getaddresses([value.strip()]) else: return value
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, ''
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
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
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
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
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}
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]
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
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
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
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())
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)
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
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
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)
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'))
def _get_to(self, parsed_data): return getaddresses(parsed_data.get_all('to'))
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)))
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
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
def emails_parsed(self): return [(n, a) for n, a in getaddresses([self.emails]) if a]
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
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'
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)
# 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":
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
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
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
def emails_formatted(self): return [ formataddr((n, a)) for n, a in getaddresses([self.emails]) if a ]
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)
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
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)
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, )
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())
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)
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
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()
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 = [
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
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')
def _filter_alias(email): email_wn = getaddresses([email])[0][1] if email_wn not in aliases: return email_wn
def _get_emails(value): "Return list of email from the comma separated list" return [e for n, e in getaddresses([value]) if e]