mirror of
https://github.com/postgres/pgweb.git
synced 2025-08-12 23:05:12 +00:00

Sent email can be assigned a "stagger type", for which he system will maintain a "last sent" information. When the email is sent, it will be delayed to be at least "stagger" time after the last one sent of the same type. If no email of this type has been sent before, the email is of course sent immediately.
102 lines
4.1 KiB
Python
102 lines
4.1 KiB
Python
from django.db import transaction
|
|
|
|
from datetime import datetime
|
|
from email.mime.text import MIMEText
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.nonmultipart import MIMENonMultipart
|
|
from email.utils import formatdate, formataddr
|
|
from email.utils import make_msgid
|
|
from email import encoders, charset
|
|
from email.header import Header
|
|
|
|
from .models import QueuedMail, LastSent
|
|
|
|
|
|
def _encoded_email_header(name, email):
|
|
if name:
|
|
return formataddr((str(Header(name, 'utf-8')), email))
|
|
return email
|
|
|
|
|
|
# Default for utf-8 in python is to encode subject with "shortest" and body with "base64". For our texts,
|
|
# make it always quoted printable, for easier reading and testing.
|
|
_utf8_charset = charset.Charset('utf-8')
|
|
_utf8_charset.header_encoding = charset.QP
|
|
_utf8_charset.body_encoding = charset.QP
|
|
|
|
|
|
def send_simple_mail(sender, receiver, subject, msgtxt, attachments=None, usergenerated=False, cc=None, replyto=None, sendername=None, receivername=None, messageid=None, suppress_auto_replies=True, is_auto_reply=False, htmlbody=None, headers={}, staggertype=None, stagger=None):
|
|
# attachment format, each is a tuple of (name, mimetype,contents)
|
|
# content should be *binary* and not base64 encoded, since we need to
|
|
# use the base64 routines from the email library to get a properly
|
|
# formatted output message
|
|
msg = MIMEMultipart()
|
|
msg['Subject'] = subject
|
|
msg['To'] = _encoded_email_header(receivername, receiver)
|
|
msg['From'] = _encoded_email_header(sendername, sender)
|
|
if cc:
|
|
msg['Cc'] = cc
|
|
if replyto:
|
|
msg['Reply-To'] = replyto
|
|
msg['Date'] = formatdate(localtime=True)
|
|
if messageid:
|
|
msg['Message-ID'] = messageid
|
|
else:
|
|
msg['Message-ID'] = make_msgid()
|
|
if suppress_auto_replies:
|
|
# Do our best to set some headers to indicate that auto-replies like out of office
|
|
# messages should not be sent to this email.
|
|
msg['X-Auto-Response-Suppress'] = 'All'
|
|
|
|
# Is this email auto-generated or auto-replied?
|
|
if is_auto_reply:
|
|
msg['Auto-Submitted'] = 'auto-replied'
|
|
elif not usergenerated:
|
|
msg['Auto-Submitted'] = 'auto-generated'
|
|
|
|
for h, v in headers.items():
|
|
if h in msg:
|
|
# Replace the existing header -- the one specified is supposedly overriding it
|
|
msg.replace_header(h, v)
|
|
else:
|
|
msg.add_header(h, v)
|
|
|
|
if htmlbody:
|
|
mpart = MIMEMultipart("alternative")
|
|
mpart.attach(MIMEText(msgtxt, _charset=_utf8_charset))
|
|
mpart.attach(MIMEText(htmlbody, 'html', _charset=_utf8_charset))
|
|
msg.attach(mpart)
|
|
else:
|
|
# Just a plaintext body, so append it directly
|
|
msg.attach(MIMEText(msgtxt, _charset='utf-8'))
|
|
|
|
if attachments:
|
|
for a in attachments:
|
|
main, sub = a['contenttype'].split('/')
|
|
part = MIMENonMultipart(main, sub)
|
|
part.set_payload(a['content'])
|
|
part.add_header('Content-Disposition', a.get('disposition', 'attachment; filename="%s"' % a['filename']))
|
|
if 'id' in a:
|
|
part.add_header('Content-ID', a['id'])
|
|
|
|
encoders.encode_base64(part)
|
|
msg.attach(part)
|
|
|
|
with transaction.atomic():
|
|
if staggertype and stagger:
|
|
# Don't send a second one too close after another one of this class.
|
|
ls, created = LastSent.objects.get_or_create(type=staggertype, defaults={'lastsent': datetime.now()})
|
|
|
|
sendat = ls.lastsent = ls.lastsent + stagger
|
|
ls.save(update_fields=['lastsent'])
|
|
else:
|
|
sendat = datetime.now()
|
|
|
|
# Just write it to the queue, so it will be transactionally rolled back
|
|
QueuedMail(sender=sender, receiver=receiver, fullmsg=msg.as_string(), usergenerated=usergenerated, sendat=sendat).save()
|
|
if cc:
|
|
# Write a second copy for the cc, wihch will be delivered
|
|
# directly to the recipient. (The sender doesn't parse the
|
|
# message content to extract cc fields).
|
|
QueuedMail(sender=sender, receiver=cc, fullmsg=msg.as_string(), usergenerated=usergenerated, sendat=sendat).save()
|